WIP: Array

dev
Alberto Restifo 2020-04-24 23:43:57 +02:00
parent 839a41dd84
commit 74c71a57c3
3 changed files with 81 additions and 1 deletions

View File

@ -21,6 +21,14 @@ describe "CBOR helpers on basic types" do
{Nil, Bytes[0xf7], nil},
{Float32, Bytes[0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a], 1.1_f32},
{Float64, Bytes[0xfa, 0x47, 0xc3, 0x50, 0x00], 100000.0_f64},
{Array(Int8), Bytes[0x83, 0x01, 0x02, 0x03], [1_i8, 2_i8, 3_i8]},
{Array(Array(Int8) | Int8),
Bytes[0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05],
[1_i8, [2_i8, 3_i8], [4_i8, 5_i8]]},
{Array(UInt8), Bytes[0x9f, 0xff], [] of UInt8},
# {Array(Array(Int8) | Int8),
# Bytes[0x9f, 0x01, 0x82, 0x02, 0x03, 0x9f, 0x04, 0x05, 0xff, 0xff],
# [1_i8, [2_i8, 3_i8], [4_i8, 5_i8]]},
]
tests.each do |tt|

View File

@ -1,6 +1,6 @@
class CBOR::Decoder
@lexer : Lexer
@current_token : Token::T?
getter current_token : Token::T?
def initialize(input)
@lexer = Lexer.new(input)
@ -70,10 +70,27 @@ class CBOR::Decoder
end
end
def consume_array(&block)
read_type(Token::ArrayT, finish_token: false) do |token|
read(token.size) { yield }
end
end
private def finish_token!
@current_token = @lexer.next_token
end
def read(size : Int32?, &block)
if size
size.times { yield }
else
@lexer.until_break do |token|
@current_token = token
yield
end
end
end
private macro read_type(type, finish_token = true, ignore_tag = true, &block)
# Skip the tag unless the token we want to read is a tag
{% if ignore_tag %}

View File

@ -39,6 +39,12 @@ def Slice.new(decoder : CBOR::Decoder)
decoder.read_bytes.to_slice
end
def Array.new(decoder : CBOR::Decoder)
arr = new
decoder.consume_array { arr << T.new(decoder) }
arr
end
# Reads the CBOR values as a time. The value must be surrounded by a time tag as
# specified by [Section 2.4.1 of RFC 7049][1].
#
@ -58,3 +64,52 @@ def Time.new(decoder : CBOR::Decoder)
raise CBOR::ParseError.new("Expected tag to have value 0 or 1, got #{tag.value.to_s}")
end
end
def Union.new(decoder : CBOR::Decoder)
token = decoder.current_token
# Optimization: use fast path for primitive types
{% begin %}
# Here we store types that are not primitive types
{% non_primitives = [] of Nil %}
{% for type, index in T %}
{% if type == Nil %}
return decoder.read_nil if token.is_a?(CBOR::Token::SimpleValueT)
{% elsif type == Bool %}
return decoder.read_bool if token.is_a?(CBOR::Token::SimpleValueT)
{% elsif type == String %}
return decoder.read_string if token.is_a?(CBOR::Token::StringT)
{% elsif type == Int8 || type == Int16 || type == Int32 || type == Int64 ||
type == UInt8 || type == UInt16 || type == UInt32 || type == UInt64 %}
return {{type}}.new(decoder) if token.is_a?(CBOR::Token::IntT)
{% elsif type == Float32 || type == Float64 %}
return {{type}}.new(decoder) if token.is_a?(CBOR::Token::FloatT)
{% unless T.any? { |t| t < Int } %}
return {{type}}.new(decoder) if token.is_a?(CBOR::Token::IntT)
{% end %}
{% else %}
{% non_primitives << type %}
{% end %}
{% end %}
# If after traversing all the types we are left with just one
# non-primitive type, we can parse it directly (no need to use `read_raw`)
{% if non_primitives.size == 1 %}
return {{non_primitives[0]}}.new(decoder)
{% else %}
raise "What is this?"
# node = decoder.read_node
# {% for type in non_primitives %}
# unpacker = CBOR::NodeUnpacker.new(node)
# begin
# return {{type}}.new(unpacker)
# rescue e : CBOR::TypeCastError
# # ignore
# end
# {% end %}
# {% end %}
{% end %}
raise CBOR::ParseError.new("Couldn't parse data as " + {{T.stringify}})
end