M CHANGELOG.md => CHANGELOG.md +4 -0
@@ 7,6 7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Changed
+- The length of objects is now pre-computed, reducing the size of the encoded
+ object and speeding up the decoing process. Thanks to Karchnu for his contribution.
+
## [0.2.1] - 2020-09-29
### Fixed
M README.md => README.md +3 -3
@@ 54,7 54,7 @@ house = House.from_cbor(cbor)
house.address # => "Crystal Road 1234"
house.location # => #<Location:0x10cd93d80 @latitude=12.3, @longitude=34.5>
bytes = house.to_cbor # => Bytes[...]
-CBOR::Diagnostic.to_s(bytes) # => {_ "address": "Crystal Road 1234", "location": {_ "lat": 12.3, "lng": 34.5}}
+CBOR::Diagnostic.to_s(bytes) # => {"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}
data_array = [data]
cbor_array = data_array.to_cbor # => Bytes[...]
@@ 63,7 63,7 @@ CBOR::Diagnostic.to_s(cbor) # => [{"address": "Crystal Road 1234", "location
houses = Array(House).from_cbor(cbor_array)
houses.size # => 1
bytes = houses.to_cbor # => Bytes[...]
-CBOR::Diagnostic.to_s(bytes) # => [{_ "address": "Crystal Road 1234", "location": {_ "lat": 12.3, "lng": 34.5}}]
+CBOR::Diagnostic.to_s(bytes) # => [{"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}]
```
## Installation
@@ 158,7 158,7 @@ end
a = A.from_cbor({"a" => 1, "b" => 2}.to_cbor) # => A(@cbor_unmapped={"b" => 2}, @a=1)
bytes = a.to_cbor # => Bytes[...]
-CBOR::Diagnostic.to_s(bytes) # => {_ "a": 1, "b": 2}
+CBOR::Diagnostic.to_s(bytes) # => {"a": 1, "b": 2}
```
### Class annotation `CBOR::Serializable::Options`
M spec/cbor/serializable_spec.cr => spec/cbor/serializable_spec.cr +6 -6
@@ 66,7 66,7 @@ end
describe CBOR::Serializable do
describe "rfc examples" do
- describe %(example {_ "a": 1, "b": [_ 2, 3]}) do
+ describe %(example {"a": 1, "b": [2, 3]}) do
it "decodes from cbor" do
result = ExampleA.from_cbor(Bytes[0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff])
@@ 75,7 75,7 @@ describe CBOR::Serializable do
end
end
- describe %(example {_ "Fun": true, "Amt": -2}) do
+ describe %(example {"Fun": true, "Amt": -2}) do
it "decodes from cbor" do
result = ExampleB.from_cbor(Bytes[0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff])
@@ 84,7 84,7 @@ describe CBOR::Serializable do
end
end
- describe %(example ["a", {_ "b": "c"}]) do
+ describe %(example ["a", {"b": "c"}]) do
it "decodes from cbor" do
result = Array(String | ExampleC).from_cbor(Bytes[0x82, 0x61, 0x61, 0xbf, 0x61, 0x62, 0x61, 0x63, 0xff])
@@ 137,7 137,7 @@ describe CBOR::Serializable do
it "encodes to CBOR" do
cbor = House.from_cbor(bytes).to_cbor
- CBOR::Diagnostic.to_s(cbor).should eq(%({_ "address": "Crystal Road 1234", "location": {_ "lat": 12.3, "lng": 34.5}}))
+ CBOR::Diagnostic.to_s(cbor).should eq(%({"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}))
end
end
@@ 168,7 168,7 @@ describe CBOR::Serializable do
it "encodes to CBOR" do
cbor = Array(House).from_cbor(bytes).to_cbor
- CBOR::Diagnostic.to_s(cbor).should eq(%([{_ "address": "Crystal Road 1234", "location": {_ "lat": 12.3, "lng": 34.5}}]))
+ CBOR::Diagnostic.to_s(cbor).should eq(%([{"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}]))
end
end
@@ 185,7 185,7 @@ describe CBOR::Serializable do
res.a.should eq(1)
res.cbor_unmapped.should eq({"b" => 2})
- CBOR::Diagnostic.to_s(res.to_cbor).should eq(%({_ "a": 1, "b": 2}))
+ CBOR::Diagnostic.to_s(res.to_cbor).should eq(%({"a": 1, "b": 2}))
end
end
end
M src/cbor/serializable.cr => src/cbor/serializable.cr +39 -12
@@ 243,6 243,10 @@ module CBOR
raise ::CBOR::SerializationError.new("Unknown CBOR attribute: #{key}", self.class.to_s, nil)
end
+ protected def get_cbor_unmapped
+ {} of String => ::CBOR::Type
+ end
+
protected def on_to_cbor(cbor : ::CBOR::Encoder)
end
@@ 268,9 272,28 @@ module CBOR
{% end %}
{% end %}
- cbor.object do
- {% for name, value in properties %}
+ # Compute the size of the final list of properties to serialize.
+ # This allows a more compact encoding, and a faster decoding.
+ nb_properties_to_serialize = 0
+ {% for name, value in properties %}
_{{name}} = @{{name}}
+ {% unless value[:emit_null] %}
+ unless _{{name}}.nil?
+ nb_properties_to_serialize += 1
+ end
+ {% else %}
+ nb_properties_to_serialize += 1
+ {% end %}
+ {% end %}
+
+ nb_properties_to_serialize += get_cbor_unmapped.size
+
+
+ {% if properties.size > 0 %}
+ cbor.write_object_start nb_properties_to_serialize
+
+ {% for name, value in properties %}
+ _{{name}} = @{{name}}
{% unless value[:emit_null] %}
unless _{{name}}.nil?
@@ 279,22 302,22 @@ module CBOR
# Write the key of the map
cbor.write({{value[:key]}})
- {% if value[:converter] %}
- if _{{name}}
- {{ value[:converter] }}.to_cbor(_{{name}}, cbor)
- else
- cbor.write(nil, use_undefined: value[:nil_as_undefined])
- end
- {% else %}
- _{{name}}.to_cbor(cbor)
- {% end %}
+ {% if value[:converter] %}
+ if _{{name}}
+ {{ value[:converter] }}.to_cbor(_{{name}}, cbor)
+ else
+ cbor.write(nil, use_undefined: value[:nil_as_undefined])
+ end
+ {% else %}
+ _{{name}}.to_cbor(cbor)
+ {% end %}
{% unless value[:emit_null] %}
end
{% end %}
{% end %}
on_to_cbor(cbor)
- end
+ {% end %}
{% end %}
end
@@ 310,6 333,10 @@ module CBOR
end
end
+ protected def get_cbor_unmapped
+ cbor_unmapped
+ end
+
protected def on_to_cbor(cbor : ::CBOR::Encoder)
cbor_unmapped.each do |key, value|
cbor.write(key)