class CBOR::Buffer

Constants

HALF_NAN_BYTES
MT_NAMES
MT_TO_ENCODING

Attributes

buffer[R]

Public Class Methods

new(s = String.new) click to toggle source
# File lib/cbor-pure.rb, line 99
def initialize(s = String.new)
  @buffer = s
  @pos = 0
end

Public Instance Methods

add(d) click to toggle source
# File lib/cbor-pure.rb, line 150
def add(d)
  case d
  when Integer
    ib = if d < 0
           d = -1-d
           0x20
         else
           0x00
         end
    head(ib, d) {             # block is called if things do not fit
      s = bignum_to_bytes(d)
      head(0xc0, TAG_BIGNUM_BASE + (ib >> 5))
      head(0x40, s.bytesize)
      s
    }
  when Numeric; addfloat(d)
  when Symbol; add(d.to_s)    # hack: this should really be tagged
  when Simple; head(0xe0, d.value)
  when false; head(0xe0, 20)
  when true; head(0xe0, 21)
  when nil; head(0xe0, 22)
  when Tagged                 # we don't handle :simple here
    head(0xc0, d.tag)
    add(d.value)
  when String
    lengths = d.cbor_stream?
    e = d
    ib = if d.encoding == Encoding::BINARY
           0x40
         else
           d = d.encode(Encoding::UTF_8).force_encoding(Encoding::BINARY)
           0x60
         end
    if lengths
      @buffer << (ib + 31)
      pos = 0
      lengths.each do |r|
        add(e[pos, r])
        pos += r
      end
      @buffer << 0xff
    else
      head(ib, d.bytesize)
      @buffer << d
    end
  when Array
    if d.cbor_stream?
      @buffer << 0x9f
      d.each {|di| add(di)}
      @buffer << 0xff
    else
      head(0x80, d.size)
      d.each {|di| add(di)}
    end
  when Hash
    if d.cbor_stream?
      @buffer << 0xbf
      d.each {|k, v| add(k); add(v)}
      @buffer << 0xff
    else
      head(0xa0, d.size)
      d.each {|k, v| add(k); add(v)}
    end
  else
    raise("Don't know how to encode #{d.inspect}")
  end
  self
end
addfloat(fv) click to toggle source
# File lib/cbor-pure.rb, line 124
def addfloat(fv)
  if fv.nan?
    @buffer << HALF_NAN_BYTES
  else
    ss = [fv].pack("g")         # single-precision
    if ss.unpack("g").first == fv
      if hs = Half.encode_from_single(fv, ss)
        @buffer << 0xf9 << hs
      else
        @buffer << 0xfa << ss
      end
    else
      @buffer << [0xfb, fv].pack("CG") # double-precision
    end
  end
end
atleast(n) click to toggle source
# File lib/cbor-pure.rb, line 219
def atleast(n)
  left = @buffer.bytesize - @pos
  raise OutOfBytesError.new(n - left) if n > left
end
bignum_to_bytes(d) click to toggle source
# File lib/cbor-pure.rb, line 141
def bignum_to_bytes(d)
  s = String.new
  while (d != 0)
    s << (d & 0xFF)
    d >>= 8
  end
  s.reverse!
end
decode_item(breakable = false) click to toggle source
# File lib/cbor-pure.rb, line 263
def decode_item(breakable = false)
  ib = take(1).ord
  ai = ib & 0x1F
  val = case ai
        when 0...24; ai
        when 24; take(1).ord
        when 25; take(2).unpack("n").first
        when 26; (s = take(4)).unpack("N").first
        when 27; (s = take(8)).unpack("Q>").first
        when 31; return decode_item_streaming(ib, breakable)
        else raise "unknown additional information #{ai} in ib #{ib}"
        end
  case ib >>= 5
  when 0; val
  when 1; -1-val
  when 7
    case ai
    when 20; false
    when 21; true
    when 22; nil
    # when 23; Simple.new(23)   # Ruby does not have Undefined
    when 24;
      raise "two-byte simple is #{val} but must be between 32 and 255" unless val >= 32;
      Simple.new(val)
    when 25; Half.decode(val)
    when 26; s.unpack("g").first # cannot go directly from val
    when 27; s.unpack("G").first #   in Ruby
    else
      Simple.new(val)
    end
  when 6
    di = decode_item
    if String === di && (val & ~1) == TAG_BIGNUM_BASE
      (TAG_BIGNUM_BASE - val) ^ di.bytes.inject(0) {|sum, b| sum <<= 8; sum += b }
    else
      Tagged.new(val, di)
    end
  when 2; take(val).force_encoding(Encoding::BINARY)
  when 3; take(val).force_encoding(Encoding::UTF_8)
  when 4; atleast(val); Array.new(val) { decode_item }
  when 5; atleast(val<<1); Hash[Array.new(val) {[decode_item, decode_item]}]
  end
end
decode_item_final() click to toggle source
# File lib/cbor-pure.rb, line 307
def decode_item_final
  val = decode_item
  raise "extra bytes follow after a deserialized object of #@pos bytes" if @pos != @buffer.size
  val
end
decode_item_streaming(ib, breakable) click to toggle source
# File lib/cbor-pure.rb, line 233
def decode_item_streaming(ib, breakable)
  case ib >>= 5
  when 2, 3
    want_encoding = MT_TO_ENCODING[ib]
    subs = []
    while (element = decode_item(true)) != BREAK
      raise "non-string (#{element.inspect}) in streaming string" unless String === element
      raise "bytes/text mismatch (#{element.encoding} != #{want_encoding}) in streaming string" unless element.encoding == want_encoding
      subs << element
    end
    result = subs.join.cbor_stream!(subs.map(&:length)).force_encoding(want_encoding)
  when 4
    result = Array.new;
    while (element = decode_item(true)) != BREAK
      result << element
    end
    result
  when 5
    result = Hash.new
    while (key = decode_item(true)) != BREAK
      result[key] = decode_item
    end
    result
  when 7
    raise "break stop code outside indefinite length item" unless breakable
    BREAK
  else raise "unknown ib #{ib} for additional information 31"
  end
end
decode_item_with_rest() click to toggle source
# File lib/cbor-pure.rb, line 313
def decode_item_with_rest
  val = decode_item
  [val, @buffer[@pos..-1]]
end
decode_items() click to toggle source
# File lib/cbor-pure.rb, line 318
def decode_items
  ret = []
  while @pos != buffer.size
    ret << decode_item
  end
  ret
end
empty?() click to toggle source
# File lib/cbor-pure.rb, line 326
def empty?
  @pos == buffer.size
end
head(ib, n) { || ... } click to toggle source
# File lib/cbor-pure.rb, line 104
def head(ib, n)
  @buffer <<
    case n
    when 0...24
      [ib + n].pack("C")
    when 0...256
      [ib + 24, n].pack("CC")
    when 0...65536
      [ib + 25, n].pack("Cn")
    when 0...4294967296
      [ib + 26, n].pack("CN")
    when 0...18446744073709551616
      [ib + 27, n].pack("CQ>")
    else
      yield                   # throw back to caller
    end
end
pretty_item() click to toggle source
# File lib/cbor-pretty.rb, line 54
def pretty_item
  ib = take_and_print(1, '   ' * @indent).ord
  ai = ib & 0x1F
  val = case ai
        when 0...24; ai
        when 24; take_and_print(1, ' ').ord
        when 25; take_and_print(2, ' ').unpack("n").first
        when 26; (s = take_and_print(4, ' ')).unpack("N").first
        when 27; (s = take_and_print(8, ' ')).unpack("Q>").first
        when 31; return pretty_item_streaming(ib)
        else raise "unknown additional information #{ai} in ib #{ib}"
        end
  @out << " # #{MT_NAMES[ib >> 5]}(#{val})\n"
  @indent += 1
  case ib >>= 5
  when 6
    pretty_item
  when 2, 3
    @out << '   ' * (@indent)
    s = take_and_print(val)
    @out << " # #{s.inspect}"
    @out << "\n"
  when 4; val.times { pretty_item }
  when 5; val.times { pretty_item; pretty_item}
  end
  @indent -= 1
  nil
end
pretty_item_final(indent = 0, max_target = 40, seq = false) click to toggle source
# File lib/cbor-pretty.rb, line 83
def pretty_item_final(indent = 0, max_target = 40, seq = false)
  @out = ''
  @indent = indent
  pretty_item
  unless seq
    raise if @pos != @buffer.size
  end
  target = [@out.each_line.map {|ln| ln =~ /#/ || 0}.max, max_target].min
  @out.each_line.map {|ln|
    col = ln =~ /#/
    if col && col < target
      ln[col, 0] = ' ' * (target - col)
    end
    ln
  }.join
end
pretty_item_streaming(ib) click to toggle source
# File lib/cbor-pretty.rb, line 37
def pretty_item_streaming(ib)
  res = nil
  @out << " # #{MT_NAMES[ib >> 5]}(*)\n"
  @indent += 1
  case ib >>= 5
  when 2, 3, 4, 5
    while (element = pretty_item) != BREAK
    end
  when 7; res = BREAK
  else raise "unknown ib #{ib} for additional information 31"
  end
  @indent -= 1
  res
end
take(n) click to toggle source
# File lib/cbor-pure.rb, line 224
def take(n)
  opos = @pos
  @pos += n
  raise OutOfBytesError.new(@pos - @buffer.bytesize) if @pos > @buffer.bytesize
  @buffer[opos, n]
end
take_and_print(n, prefix = '') click to toggle source
# File lib/cbor-pretty.rb, line 30
def take_and_print(n, prefix = '')
  s = take(n)
  @out << prefix
  @out << s.hexbytes
  s
end