Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions gems/smithy-cbor/lib/smithy-cbor/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def build(shape, data)
private

def build_shape(shape, value)
case shape.target
target = shape.target
case target
when BlobShape then blob(value)
when ListShape then list(shape, value)
when MapShape then map(shape, value)
Expand All @@ -38,48 +39,54 @@ def blob(value)
def list(shape, values)
return if values.nil?

member = shape.target.member
values.collect do |value|
build_shape(shape.target.member, value)
build_shape(member, value)
end
end

def map(shape, values)
return if values.nil?

value_member = shape.target.value
values.each.with_object({}) do |(key, value), data|
data[key] = build_shape(shape.target.value, value)
data[key] = build_shape(value_member, value)
end
end

def structure(shape, values)
return if values.nil?

members = shape.target.members
index = @extension.member_index(shape.target)
values.each_pair.with_object({}) do |(member_name, value), data|
next if value.nil?

member_shape = members[member_name]
next unless member_shape
entry = index[member_name]
next unless entry

data[@extension.wire_name(member_shape)] = build_shape(member_shape, value)
wire_name, member_shape = entry
data[wire_name] = build_shape(member_shape, value)
end
end

def union(shape, values) # rubocop:disable Metrics/AbcSize
def union(shape, values)
return if values.nil?

data = {}
if values.is_a?(Schema::Union)
_name, member_shape = shape.target.member_by_type(values.class)
data[@extension.wire_name(member_shape)] = build_shape(member_shape, values.value)
else
key, value = values.first
if shape.target.member?(key)
member_shape = shape.target.member(key)
data[@extension.wire_name(member_shape)] = build_shape(member_shape, value)
target = shape.target
key, value =
if values.is_a?(Schema::Union)
values.active_member_value
else
values.first
end
end
data

return {} unless key

entry = @extension.member_index(target)[key]
return {} unless entry

wire_name, member_shape = entry
{ wire_name => build_shape(member_shape, value) }
end
end
end
Expand Down
7 changes: 4 additions & 3 deletions gems/smithy-cbor/lib/smithy-cbor/codec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,23 @@ module Cbor
class Codec
# @param [Hash] options
def initialize(options = {})
@options = options
@builder = Builder.new(options)
@parser = Parser.new(options)
end

# @param [Shape] shape
# @param [Object] data
# @return [String, nil]
def build(shape, data)
Builder.new(@options).build(shape, data)
@builder.build(shape, data)
end

# @param [Shape] shape
# @param [String] bytes
# @param [Object, nil] result (nil)
# @return [Object, nil]
def parse(shape, bytes, result = nil)
Parser.new(@options).parse(shape, bytes, result)
@parser.parse(shape, bytes, result)
end
end
end
Expand Down
47 changes: 38 additions & 9 deletions gems/smithy-cbor/lib/smithy-cbor/decoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Cbor
# @api private
class Decoder # rubocop:disable Metrics/ClassLength
FIVE_BIT_MASK = 0x1F
BREAK_STOP_BYTE = 0xFF
TAG_TYPE_EPOCH = 1
TAG_TYPE_BIGNUM = 2
TAG_TYPE_NEG_BIGNUM = 3
Expand Down Expand Up @@ -47,6 +48,16 @@ def decode_item # rubocop:disable Metrics
when :indefinite_string then process_indefinite_string
when :tag then process_tag
when :break_stop_code then raise ParseError, 'Unexpected break code'
when :integer then read_integer
when :binary_string then read_binary_string
when :string then read_string
when :boolean then read_boolean
when :nil then read_nil
when :undefined then read_undefined
when :reserved_undefined then read_reserved_undefined
when :half then read_half
when :float then read_float
when :double then read_double
else send("read_#{next_type}")
end
ensure
Expand All @@ -62,7 +73,7 @@ def peek(n_bytes)

# low level streaming interface
def peek_type # rubocop:disable Metrics
ib = peek(1).ord
ib = peek_byte
add_info = ib & FIVE_BIT_MASK
major_type = ib >> 5
case major_type
Expand Down Expand Up @@ -97,31 +108,31 @@ def process_major_type_simple(add_info) # rubocop:disable Metrics
def process_indefinite_array
read_start_indefinite_array
value = []
value << decode_item until peek_type == :break_stop_code
value << decode_item until peek_byte == BREAK_STOP_BYTE
read_end_indefinite_collection
value
end

def process_indefinite_binary
read_info
value = String.new
value << read_binary_string until peek_type == :break_stop_code
value << read_binary_string until peek_byte == BREAK_STOP_BYTE
read_end_indefinite_collection
value
end

def process_indefinite_map
read_start_indefinite_map
value = {}
value[read_string] = decode_item until peek_type == :break_stop_code
value[read_string] = decode_item until peek_byte == BREAK_STOP_BYTE
read_end_indefinite_collection
value
end

def process_indefinite_string
read_info
value = String.new
value << read_string until peek_type == :break_stop_code
value << read_string until peek_byte == BREAK_STOP_BYTE
read_end_indefinite_collection
value.force_encoding(Encoding::UTF_8)
end
Expand Down Expand Up @@ -191,8 +202,8 @@ def read_binary_string
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 24 then take_byte
when 25 then take_u16
when 26 then take(4).unpack1('N')
when 27 then take(8).unpack1('Q>')
else raise ParseError, "Unexpected additional information: #{add_info}"
Expand Down Expand Up @@ -222,7 +233,7 @@ def read_float
# precision - 10 bits
def read_half
read_info
b16 = take(2).unpack1('n')
b16 = take_u16
exp = (b16 >> 10) & 0x1f
mant = b16 & 0x3ff
val =
Expand All @@ -245,7 +256,7 @@ def read_half

# return a tuple of major_type, add_info
def read_info
ib = take(1).ord
ib = take_byte
[ib >> 5, ib & FIVE_BIT_MASK]
end

Expand Down Expand Up @@ -313,6 +324,24 @@ def take(n_bytes)
left = @buffer.bytesize - @pos
raise ParseError, "Out of bytes. Trying to read #{n_bytes} bytes but buffer contains only #{left}"
end

def peek_byte
return @buffer.getbyte(@pos) if @pos < @buffer.bytesize

left = @buffer.bytesize - @pos
raise ParseError, "Out of bytes. Trying to read 1 bytes but buffer contains only #{left}"
end

def take_byte
return @buffer.getbyte(@pos).tap { @pos += 1 } if @pos < @buffer.bytesize

left = @buffer.bytesize - @pos
raise ParseError, "Out of bytes. Trying to read 1 bytes but buffer contains only #{left}"
end

def take_u16
(take_byte << 8) | take_byte
end
end
end
end
40 changes: 22 additions & 18 deletions gems/smithy-cbor/lib/smithy-cbor/encoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def add_big_decimal(value)
end

def add_boolean(value)
value ? head(MAJOR_TYPE_SIMPLE, 21) : head(MAJOR_TYPE_SIMPLE, 20)
@buffer << (value ? 0xf5 : 0xf4)
end

# Encoding MUST already be Encoding::BINARY
Expand All @@ -141,11 +141,16 @@ def add_hash(value)
end

def add_nil
head(MAJOR_TYPE_SIMPLE, 22)
@buffer << 0xf6
end

def add_string(value)
value = value.encode(Encoding::UTF_8).force_encoding(Encoding::BINARY)
value =
if value.encoding == Encoding::UTF_8
value.b
else
value.encode(Encoding::UTF_8).force_encoding(Encoding::BINARY)
end
head(MAJOR_TYPE_STR, value.bytesize)
@buffer << value
end
Expand All @@ -170,21 +175,20 @@ def bignum_to_bytes(value)
end

def head(major_type, value)
@buffer <<
case value
when 0...24
[major_type + value].pack('C') # 8-bit unsigned
when 0...256
[major_type + 24, value].pack('CC')
when 0...65_536
[major_type + 25, value].pack('Cn')
when 0...4_294_967_296
[major_type + 26, value].pack('CN')
when 0...MAX_INTEGER
[major_type + 27, value].pack('CQ>')
else
raise BuildError, "Value is too large to encode: #{value}"
end
case value
when 0...24
@buffer << (major_type + value) # 8-bit unsigned
when 0...256
@buffer << (major_type + 24) << value
when 0...65_536
@buffer << [major_type + 25, value].pack('Cn')
when 0...4_294_967_296
@buffer << [major_type + 26, value].pack('CN')
when 0...MAX_INTEGER
@buffer << [major_type + 27, value].pack('CQ>')
else
raise BuildError, "Value is too large to encode: #{value}"
end
end

def process_string(value)
Expand Down
35 changes: 20 additions & 15 deletions gems/smithy-cbor/lib/smithy-cbor/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def parse(shape, bytes, result = nil)
def parse_shape(shape, value, result = nil)
return nil if value.nil?

case shape.target
target = shape.target
case target
when ListShape then list(shape, value, result)
when MapShape then map(shape, value, result)
when StructureShape then structure(shape, value, result)
Expand All @@ -33,28 +34,35 @@ def parse_shape(shape, value, result = nil)
end

def list(shape, values, result = nil)
target = shape.target
sparse = Smithy::Schema::Extension.sparse?(target)
list_member = target.member
result = [] if result.nil?
values.each do |value|
next if value.nil? && !sparse?(shape.target)
next if value.nil? && !sparse

result << parse_shape(shape.target.member, value)
result << parse_shape(list_member, value)
end
result
end

def map(shape, values, result = nil)
target = shape.target
sparse = Smithy::Schema::Extension.sparse?(target)
value_member = target.value
result = {} if result.nil?
values.each do |key, value|
next if value.nil? && !sparse?(shape.target)
next if value.nil? && !sparse

result[key] = parse_shape(shape.target.value, value)
result[key] = parse_shape(value_member, value)
end
result
end

def structure(shape, values, result = nil)
result = shape.target.type.new if result.nil?
index = @extension.member_index(shape.target)
target = shape.target
result = target.type.new if result.nil?
index = @extension.wire_index(target)
values.each do |wire_name, value|
next if value.nil?

Expand All @@ -67,26 +75,23 @@ def structure(shape, values, result = nil)
result
end

def union(shape, values, result = nil) # rubocop:disable Metrics/AbcSize
index = @extension.member_index(shape.target)
def union(shape, values, result = nil)
target = shape.target
index = @extension.wire_index(target)
values.each do |wire_name, value|
next if value.nil?

entry = index[wire_name]
next unless entry

member_name, member_shape = entry
result = shape.target.member_type(member_name) if result.nil?
result = target.member_type(member_name) if result.nil?
return result.new(member_name => parse_shape(member_shape, value))
end

values.delete('__type')
key, value = values.first
shape.target.member_type(:unknown).new(unknown: { key => value })
end

def sparse?(shape)
@extension.sparse?(shape)
target.member_type(:unknown).new(unknown: { key => value })
end
end
end
Expand Down
1 change: 1 addition & 0 deletions gems/smithy-client/lib/smithy-client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
require_relative 'smithy-client/http/headers'
require_relative 'smithy-client/http/response'
require_relative 'smithy-client/http/request'
require_relative 'smithy-client/http_binding'
require_relative 'smithy-client/stream'
require_relative 'smithy-client/transport'
require_relative 'smithy-client/net_http/connection_pool'
Expand Down
Loading