From 2d6fedf74978cf15069f8e674139a47697cc4729 Mon Sep 17 00:00:00 2001 From: Alberto Restifo Date: Tue, 2 Jun 2020 12:54:16 +0200 Subject: [PATCH] Fix encoding of CBOR tags --- spec/cbor/to_cbor_spec.cr | 61 +++++++++++++++++++++++++++++++++++++++ src/cbor/encoder.cr | 6 ++-- src/cbor/to_cbor.cr | 28 ++++++++++++++++-- 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 spec/cbor/to_cbor_spec.cr diff --git a/spec/cbor/to_cbor_spec.cr b/spec/cbor/to_cbor_spec.cr new file mode 100644 index 0000000..ab91e57 --- /dev/null +++ b/spec/cbor/to_cbor_spec.cr @@ -0,0 +1,61 @@ +require "../spec_helper" + +describe "to_cbor" do + describe "rfc examples" do + tests = [ + {String, Bytes[0x61, 0x61], "a"}, + {UInt8, Bytes[0x18, 0x18], 24}, + {UInt16, Bytes[0x19, 0x03, 0xe8], 1000}, + {UInt32, Bytes[0x1a, 0x00, 0x0f, 0x42, 0x40], 1000000}, + {UInt64, Bytes[0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00], 1000000000000}, + {Int8, Bytes[0x29], -10}, + {Bool, Bytes[0xf4], false}, + {Bool, Bytes[0xf5], true}, + {Bytes, Bytes[0x44, 0x01, 0x02, 0x03, 0x04], Bytes[0x01, 0x02, 0x03, 0x04]}, + {Time, Bytes[0xc0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30, 0x33, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x34, 0x3a, 0x30, 0x30, 0x5a], Time::Format::RFC_3339.parse("2013-03-21T20:04:00Z")}, + # {Time, Bytes[0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0], Time.unix(1363896240)}, + # {Time, Bytes[0xc1, 0xfb, 0x41, 0xd4, 0x52, 0xd9, 0xec, 0x20, 0x00, 0x00], Time.unix_ms((BigFloat.new(1363896240.5) * 1000).to_u64)}, + {Nil, Bytes[0xf6], nil}, + {Float32, Bytes[0xfa, 0x3f, 0x8c, 0xcc, 0xcd], 1.1_f32}, + {Float64, Bytes[0xfb, 0x40, 0xf8, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00], 100000.0_f64}, + {Set(Int8), Bytes[0x83, 0x01, 0x02, 0x03], Set(Int8){1, 2, 3}}, + {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[0x80], [] of UInt8}, + {Array(UInt8), Bytes[0x81, 0x01], [1_u8] of UInt8}, + {Array(Int32), Bytes[0x98, 0x19, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19], [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]}, + {Array(Array(Int8) | Int8), Bytes[0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05], [1_i8, [2_i8, 3_i8], [4_i8, 5_i8]]}, + {Hash(UInt8, UInt8), Bytes[0xa0], {} of UInt8 => UInt8}, + {Hash(UInt8, UInt8), Bytes[0xa2, 0x01, 0x02, 0x03, 0x04], Hash(UInt8, UInt8){1 => 2, 3 => 4}}, + {TestEnum, Bytes[0x01], TestEnum::Foo}, + {Tuple(Int8, Int8), Bytes[0x82, 0x01, 0x02], {1_i8, 2_i8}}, + {NamedTuple(a: UInt8, b: Array(UInt8)), Bytes[0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03], {a: 1_u8, b: [2_u8, 3_u8]}}, + # {BigInt, Bytes[0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], BigInt.new("18446744073709551616")}, + # {BigDecimal, Bytes[0xc4, 0x82, 0x21, 0x19, 0x6a, 0xb3], BigDecimal.new(273.15)}, + # {BigDecimal, Bytes[0xc5, 0x82, 0x20, 0x03], BigDecimal.new(1.5)}, + {Hash(String, Int32 | Array(Int32)), Bytes[0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03], {"a" => 1, "b" => [2, 3]}}, + {Array(String | Hash(String, String)), Bytes[0x82, 0x61, 0x61, 0xa1, 0x61, 0x62, 0x61, 0x63], ["a", {"b" => "c"}]}, + {Hash(String, Bool | Int32), Bytes[0xa2, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21], {"Fun" => true, "Amt" => -2}}, + ] + + tests.each do |tt| + type, bytes, value = tt + + it "encodes #{value.inspect} of type #{type.to_s}" do + res = value.to_cbor + res.hexdump.should eq(bytes.hexdump) + end + end + end + + describe Time::EpochConverter do + it "encodes to_cbor" do + time = Time.unix(1363896240) + + encoder = CBOR::Encoder.new + Time::EpochConverter.to_cbor(time, encoder) + + encoder.to_slice.hexdump.should eq(Bytes[0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0].hexdump) + end + end +end diff --git a/src/cbor/encoder.cr b/src/cbor/encoder.cr index e2dfba2..aaca8de 100644 --- a/src/cbor/encoder.cr +++ b/src/cbor/encoder.cr @@ -93,9 +93,9 @@ class CBOR::Encoder value.each { |item| write(item) } end - def write(value : Tag) - write_size(0xc0, value) - write_value(value) + def write(tag : Tag) + compressed = compress(tag.value) + write(compressed, 0xc0) end def write_array_start(size) diff --git a/src/cbor/to_cbor.cr b/src/cbor/to_cbor.cr index b785d8c..25a96ef 100644 --- a/src/cbor/to_cbor.cr +++ b/src/cbor/to_cbor.cr @@ -74,7 +74,7 @@ module Time::Format::RFC_3339 # [RFC 7049 section 2.4.1](https://tools.ietf.org/html/rfc7049#section-2.4.1). def self.to_cbor(value : Time, encoder : CBOR::Encoder) encoder.write(CBOR::Tag::RFC3339Time) - value.format(value, fraction_digits: 0).to_cbor(encoder) + format(value, fraction_digits: 0).to_cbor(encoder) end end @@ -103,6 +103,30 @@ struct Time # ``` def to_cbor(encoder : CBOR::Encoder) encoder.write(CBOR::Tag::RFC3339Time) - self.format(self, fraction_digits: 0).to_cbor(encoder) + encoder.write(to_rfc3339) end end + +# struct BigInt +# # Encodes the value a bytes arrya tagged with the CBOR tag 2 or 3, as specified +# # in [RFC 7049 Section 2.4.2](https://tools.ietf.org/html/rfc7049#section-2.4.2). +# def to_cbor(encoder : CBOR::Encoder) +# encoded_value = BigInt.new(self) +# if encoded_value >= 0 +# encoder.write(CBOR::Tag::PositiveBigNum) +# else +# encoder.write(CBOR::Tag::NegativeBigNum) +# encoded_value *= -1 +# encoded_value += 1 +# end + +# io = IO::Memory.new +# encoded_value.to_io(io, IO::ByteFormat::NetworkEndian) +# encoder.write(io.to_slice) +# end +# end + +# struct BigDecimal +# def to_cbor(encoder : CBOR::Encoder) +# end +# end