class Aws::Cbor::Decoder

Pure Ruby implementation of CBOR Decoder

Constants

FIVE_BIT_MASK
TAG_TYPE_BIGDEC
TAG_TYPE_BIGNUM
TAG_TYPE_EPOCH
TAG_TYPE_NEG_BIGNUM

Public Class Methods

new(bytes) click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 7
def initialize(bytes)
  @buffer = bytes
  @pos = 0
end

Public Instance Methods

decode() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 12
def decode
  return nil if @buffer.nil? || @buffer.empty?

  val = decode_item
  return val unless @pos != @buffer.size

  raise ExtraBytesError.new(@pos, @buffer.size)
end

Private Instance Methods

decode_item() click to toggle source

high level, generic decode. Based on the next type. Consumes and returns the next item as a ruby object.

# File lib/aws-sdk-core/cbor/decoder.rb, line 31
def decode_item
  case (next_type = peek_type)
  when :array
    read_array.times.map { decode_item }
  when :map
    read_map.times.map { [read_string, decode_item] }.to_h
  when :indefinite_array
    read_start_indefinite_array
    value = []
    value << decode_item until peek_type == :break_stop_code
    read_end_indefinite_collection
    value
  when :indefinite_map
    read_start_indefinite_map
    value = {}
    value[read_string] = decode_item until peek_type == :break_stop_code
    read_end_indefinite_collection
    value
  when :indefinite_binary_string
    read_info
    value = String.new
    value << read_binary_string until peek_type == :break_stop_code
    read_end_indefinite_collection
    value
  when :indefinite_string
    read_info
    value = String.new
    value << read_string until peek_type == :break_stop_code
    read_end_indefinite_collection
    value.force_encoding(Encoding::UTF_8)
  when :tag
    case (tag = read_tag)
    when TAG_TYPE_EPOCH
      type = peek_type
      item = decode_item
      item /= 1000.0 if type == :integer
      Time.at(item)
    when TAG_TYPE_BIGNUM, TAG_TYPE_NEG_BIGNUM
      read_bignum(tag)
    when TAG_TYPE_BIGDEC
      read_big_decimal
    else
      Tagged.new(tag, decode_item)
    end
  when :break_stop_code
    raise UnexpectedBreakCodeError
  else
    send("read_#{next_type}")
  end
end
peek(n_bytes) click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 303
def peek(n_bytes)
  return @buffer[@pos, n_bytes] if (@pos + n_bytes) <= @buffer.bytesize

  raise OutOfBytesError.new(n_bytes, @buffer.bytesize - @pos)
end
peek_type() click to toggle source

low level streaming interface

# File lib/aws-sdk-core/cbor/decoder.rb, line 83
def peek_type
  ib = peek(1).ord
  add_info = ib & FIVE_BIT_MASK
  major_type = ib >> 5
  case major_type
  when 0, 1 then :integer
  when 2
    add_info == 31 ? :indefinite_binary_string : :binary_string
  when 3
    add_info == 31 ? :indefinite_string : :string
  when 4
    add_info == 31 ? :indefinite_array : :array
  when 5
    add_info == 31 ? :indefinite_map : :map
  when 6 then :tag
  when 7 # simple or float
    case add_info
    when 20, 21 then :boolean
    when 22 then :nil
    when 23 then :undefined # for smithy, this should be parsed as nil
    when 25 then :half
    when 26 then :float
    when 27 then :double
    when 31 then :break_stop_code
    else
      :reserved_undefined
    end
  end
end
read_array() click to toggle source

returns only the length of the array, caller must read the correct number of values after this

# File lib/aws-sdk-core/cbor/decoder.rb, line 142
def read_array
  _major_type, add_info = read_info
  read_count(add_info)
end
read_big_decimal() click to toggle source

A decimal fraction or a bigfloat is represented as a tagged array that contains exactly two integer numbers: an exponent e and a mantissa m See: www.rfc-editor.org/rfc/rfc8949.html#name-decimal-fractions-and-bigfl

# File lib/aws-sdk-core/cbor/decoder.rb, line 265
def read_big_decimal
  unless (s = read_array) == 2
    raise Error, "Expected array of length 2 but length is: #{s}"
  end

  e = read_integer
  m = read_integer
  BigDecimal(m) * (BigDecimal(10)**BigDecimal(e))
end
read_bignum(tag_value) click to toggle source

tag type 2 or 3

# File lib/aws-sdk-core/cbor/decoder.rb, line 244
def read_bignum(tag_value)
  _major_type, add_info = read_info
  bstr = take(read_count(add_info))
  v = bstr.bytes.inject(0) do |sum, b|
    sum <<= 8
    sum + b
  end
  case tag_value
  when 2 then v
  when 3 then -1 - v
  else
    raise Error,
          'Invalid Tag value for BigNum, ' \
          "expected 2 or 3, got: #{tag_value}"
  end
end
read_binary_string() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 131
def read_binary_string
  _major_type, add_info = read_info
  take(read_count(add_info)).force_encoding(Encoding::BINARY)
end
read_boolean() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 182
def read_boolean
  _major_type, add_info = read_info
  case add_info
  when 20 then false
  when 21 then true
  else
    raise Error,
          'Invalid Boolean simple type, expected add_info of 20 or 21, ' \
           "got: #{add_info}"
  end
end
read_break_stop_code() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 113
def read_break_stop_code
  read_info
  :break_stop_code
end
read_count(add_info) click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 281
def read_count(add_info)
  case add_info
  when 0..23 then add_info
  when 24 then take(1).ord
  when 25 then take(2).unpack1('n')
  when 26 then take(4).unpack1('N')
  when 27 then take(8).unpack1('Q>')
  when 28 then take(16).unpack1('Q>')
  when 29 then take(32).unpack1('Q>')
  else raise UnexpectedAdditionalInformationError, add_info
  end
end
read_double() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 238
def read_double
  read_info
  take(8).unpack1('G')
end
read_end_indefinite_collection() click to toggle source

returns nothing but consumes and checks the type/info.

# File lib/aws-sdk-core/cbor/decoder.rb, line 160
def read_end_indefinite_collection
  read_info
end
read_float() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 233
def read_float
  read_info
  take(4).unpack1('g')
end
read_half() click to toggle source

16 bit IEEE 754 half-precision floats Support decoding only format: sign - 1 bit exponent - 5 bits precision - 10 bits

# File lib/aws-sdk-core/cbor/decoder.rb, line 210
def read_half
  read_info
  b16 = take(2).unpack1('n')
  exp = (b16 >> 10) & 0x1f
  mant = b16 & 0x3ff
  val =
    case exp
    when 0
      Math.ldexp(mant, -24)
    when 31
      mant.zero? ? Float::INFINITY : Float::NAN
    else
      # exp bias is 15, but to use ldexp we divide by 1024 (2^10) to get
      # exp-15-10
      Math.ldexp(1024 + mant, exp - 25)
    end
  if (b16[15]).zero?
    val
  else
    -val
  end
end
read_info() click to toggle source

return a tuple of major_type, add_info

# File lib/aws-sdk-core/cbor/decoder.rb, line 276
def read_info
  ib = take(1).ord
  [ib >> 5, ib & FIVE_BIT_MASK]
end
read_integer() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 118
def read_integer
  major_type, add_info = read_info

  val = read_count(add_info)
  case major_type
  when 0 then val
  when 1 then -1 - val
  else
    raise Error,
          "Expected Integer (0,1) got major type: #{major_type}"
  end
end
read_map() click to toggle source

returns only the length of the array, caller must read the correct number of key value pairs after this

# File lib/aws-sdk-core/cbor/decoder.rb, line 165
def read_map
  _major_type, add_info = read_info
  read_count(add_info)
end
read_nil() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 194
def read_nil
  read_info
  nil
end
read_reserved_undefined() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 176
def read_reserved_undefined
  _major_type, add_info = read_info
  raise Error,
    "Undefined reserved additional information: #{add_info}"
end
read_start_indefinite_array() click to toggle source

returns nothing but consumes and checks the type/info. Caller must keep reading until encountering the stop sequence

# File lib/aws-sdk-core/cbor/decoder.rb, line 149
def read_start_indefinite_array
  read_info
end
read_start_indefinite_map() click to toggle source

returns nothing but consumes and checks the type/info. Caller must keep reading until encountering the stop sequence

# File lib/aws-sdk-core/cbor/decoder.rb, line 155
def read_start_indefinite_map
  read_info
end
read_string() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 136
def read_string
  _major_type, add_info = read_info
  take(read_count(add_info)).force_encoding(Encoding::UTF_8)
end
read_tag() click to toggle source

returns only the tag, caller must interpret the tag and read another value as appropriate

# File lib/aws-sdk-core/cbor/decoder.rb, line 171
def read_tag
  _major_type, add_info = read_info
  read_count(add_info)
end
read_undefined() click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 199
def read_undefined
  read_info
  :undefined
end
take(n_bytes) click to toggle source
# File lib/aws-sdk-core/cbor/decoder.rb, line 294
def take(n_bytes)
  opos = @pos
  @pos += n_bytes

  return @buffer[opos, n_bytes] if @pos <= @buffer.bytesize

  raise OutOfBytesError.new(n_bytes, @buffer.bytesize - @pos)
end