diff --git a/spec/rfc_spec.cr b/spec/rfc_spec.cr index b8a09ec..6cf00be 100644 --- a/spec/rfc_spec.cr +++ b/spec/rfc_spec.cr @@ -77,11 +77,11 @@ tests = [ { %([1, 2, 3]), "83 01 02 03" }, { %([1, [2, 3], [4, 5]]), "83 01 82 02 03 82 04 05" }, { %([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]), "98 19 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 18 18 19" }, - # { %({}), "a0" }, - # { %({1: 2, 3: 4}), "a2 01 02 03 04" }, - # { %({"a": 1, "b": [2, 3]}), "a2 61 61 01 61 62 82 02 03" }, - # { %(["a", {"b": "c"}]), "82 61 61 a1 61 62 61 63" }, - # { %({"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}), "a5 61 61 61 41 61 62 61 42 61 63 61 43 61 64 61 44 61 65 61 45" }, + { %({}), "a0" }, + { %({1: 2, 3: 4}), "a2 01 02 03 04" }, + { %({"a": 1, "b": [2, 3]}), "a2 61 61 01 61 62 82 02 03" }, + { %(["a", {"b": "c"}]), "82 61 61 a1 61 62 61 63" }, + { %({"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}), "a5 61 61 61 41 61 62 61 42 61 63 61 43 61 64 61 44 61 65 61 45" }, { %((_ h'0102', h'030405')), "5f 42 01 02 43 03 04 05 ff" }, { %((_ "strea", "ming")), "7f 65 73 74 72 65 61 64 6d 69 6e 67 ff" }, { %([_ ]), "9f ff" }, @@ -90,9 +90,9 @@ tests = [ { %([1, [2, 3], [_ 4, 5]]), "83 01 82 02 03 9f 04 05 ff" }, { %([1, [_ 2, 3], [4, 5]]), "83 01 9f 02 03 ff 82 04 05" }, { %([_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]), "9f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 18 18 19 ff" }, - # { %({_ "a": 1, "b": [_ 2, 3]}), "bf 61 61 01 61 62 9f 02 03 ff ff" }, - # { %(["a", {_ "b": "c"}]), "82 61 61 bf 61 62 61 63 ff" }, - # { %({_ "Fun": true, "Amt": -2}), "bf 63 46 75 6e f5 63 41 6d 74 21 ff" }, + { %({_ "a": 1, "b": [_ 2, 3]}), "bf 61 61 01 61 62 9f 02 03 ff ff" }, + { %(["a", {_ "b": "c"}]), "82 61 61 bf 61 62 61 63 ff" }, + { %({_ "Fun": true, "Amt": -2}), "bf 63 46 75 6e f5 63 41 6d 74 21 ff" }, ] describe "Examples from RFC7049 Appendix A" do diff --git a/src/cbor/diagnostic.cr b/src/cbor/diagnostic.cr index 53c6a45..97042f2 100644 --- a/src/cbor/diagnostic.cr +++ b/src/cbor/diagnostic.cr @@ -46,6 +46,13 @@ class CBOR::Diagnostic arr = read_array(token.size) return "[#{arr.join(", ")}]" if token.size "[_ #{arr.join(", ")}]" + when Token::MapT + hash_body = read_hash(token.size).join(", ") + return "{#{hash_body}}" if token.size + "{_ #{hash_body}}" + when Token::BoolT + return "true" if token.value + "false" else token.inspect end @@ -67,6 +74,24 @@ class CBOR::Diagnostic arr end + # Reads the hash, returning an array of key-pairs strings already + # correctly formatted in the diagnostic notation + private def read_hash(size : Int32?) : Array(String) + key_pairs = Array(String).new + + if size + size.times { key_pairs << key_value(*@lexer.next_pair) } + else + @lexer.pairs_until_break { |pairs| key_pairs << key_value(*pairs) } + end + + key_pairs + end + + private def key_value(key : Token::T, value : Token::T) : String + "#{to_diagnostic(key)}: #{to_diagnostic(value)}" + end + private def chunks(value : Bytes, chunks : Array(Int32)) : Array(Bytes) res = Array(Bytes).new bytes = value.to_a diff --git a/src/cbor/lexer.cr b/src/cbor/lexer.cr index cb8ccad..591f62c 100644 --- a/src/cbor/lexer.cr +++ b/src/cbor/lexer.cr @@ -20,6 +20,17 @@ class CBOR::Lexer decode(byte) end + # Read the next pair of tokens, useful for maps. + # Raises an exception if there are no two pairs left. + def next_pair : Tuple(Token::T, Token::T) + pairs = Array(Token::T).new(2) do + token = next_token + raise ParseError.new("Unexpected EOF while reading next pair") unless token + token + end + Tuple.new(pairs[0], pairs[1]) + end + def until_break(&block : Token::T ->) loop do byte = next_byte @@ -29,6 +40,23 @@ class CBOR::Lexer end end + # Read a pair of values until a break is reached + def pairs_until_break(&block : Tuple(Token::T, Token::T) ->) + loop do + key_byte = next_byte + raise ParseError.new("Unexpected EOF while searching for break") unless key_byte + break if key_byte == BREAK + + key = decode(key_byte) + raise ParseError.new("Unexpected EOF while reading key in pairs") unless key + + value = next_token + raise ParseError.new("Unexpected EOF while reading value in pairs") unless value + + yield Tuple.new(key, value) + end + end + private def decode(byte : UInt8) : Token::T case byte when 0x00..0x1b @@ -47,6 +75,15 @@ class CBOR::Lexer array_start(read_size(byte - 0x80)) when 0x9f Token::ArrayT.new + when 0xa0..0xbb + map_start(read_size(byte - 0xa0)) + when 0xbf + Token::MapT.new + ################## + when 0xf4 + Token::BoolT.new(value: false) + when 0xf5 + Token::BoolT.new(value: true) else raise ParseError.new("Unexpected first byte 0x#{byte.to_s(16)}") end @@ -128,6 +165,11 @@ class CBOR::Lexer Token::ArrayT.new(size: size.to_i32) end + private def map_start(size) + raise ParseError.new("Maximum size for array exeeded") if size > Int32::MAX + Token::MapT.new(size: size.to_i32) + end + # Creates a method overloaded for each UInt sizes to convert the UInt into # the respective Int capable of containing the value