Compute the number of object properties to serialize.

remotes/dev/pr-use-cbor-discriminator
Karchnu 2020-11-10 20:14:50 +01:00 committed by Alberto Restifo
parent 3ab21c9616
commit 307c57879a
4 changed files with 52 additions and 21 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [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 ## [0.2.1] - 2020-09-29
### Fixed ### Fixed

View File

@ -54,7 +54,7 @@ house = House.from_cbor(cbor)
house.address # => "Crystal Road 1234" house.address # => "Crystal Road 1234"
house.location # => #<Location:0x10cd93d80 @latitude=12.3, @longitude=34.5> house.location # => #<Location:0x10cd93d80 @latitude=12.3, @longitude=34.5>
bytes = house.to_cbor # => Bytes[...] 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] data_array = [data]
cbor_array = data_array.to_cbor # => Bytes[...] 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 = Array(House).from_cbor(cbor_array)
houses.size # => 1 houses.size # => 1
bytes = houses.to_cbor # => Bytes[...] 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 ## Installation
@ -158,7 +158,7 @@ end
a = A.from_cbor({"a" => 1, "b" => 2}.to_cbor) # => A(@cbor_unmapped={"b" => 2}, @a=1) a = A.from_cbor({"a" => 1, "b" => 2}.to_cbor) # => A(@cbor_unmapped={"b" => 2}, @a=1)
bytes = a.to_cbor # => Bytes[...] 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` ### Class annotation `CBOR::Serializable::Options`

View File

@ -66,7 +66,7 @@ end
describe CBOR::Serializable do describe CBOR::Serializable do
describe "rfc examples" 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 it "decodes from cbor" do
result = ExampleA.from_cbor(Bytes[0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff]) 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
end end
describe %(example {_ "Fun": true, "Amt": -2}) do describe %(example {"Fun": true, "Amt": -2}) do
it "decodes from cbor" do it "decodes from cbor" do
result = ExampleB.from_cbor(Bytes[0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff]) 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
end end
describe %(example ["a", {_ "b": "c"}]) do describe %(example ["a", {"b": "c"}]) do
it "decodes from cbor" do it "decodes from cbor" do
result = Array(String | ExampleC).from_cbor(Bytes[0x82, 0x61, 0x61, 0xbf, 0x61, 0x62, 0x61, 0x63, 0xff]) 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 it "encodes to CBOR" do
cbor = House.from_cbor(bytes).to_cbor 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
end end
@ -168,7 +168,7 @@ describe CBOR::Serializable do
it "encodes to CBOR" do it "encodes to CBOR" do
cbor = Array(House).from_cbor(bytes).to_cbor 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
end end
@ -185,7 +185,7 @@ describe CBOR::Serializable do
res.a.should eq(1) res.a.should eq(1)
res.cbor_unmapped.should eq({"b" => 2}) 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 end
end end

View File

@ -243,6 +243,10 @@ module CBOR
raise ::CBOR::SerializationError.new("Unknown CBOR attribute: #{key}", self.class.to_s, nil) raise ::CBOR::SerializationError.new("Unknown CBOR attribute: #{key}", self.class.to_s, nil)
end end
protected def get_cbor_unmapped
{} of String => ::CBOR::Type
end
protected def on_to_cbor(cbor : ::CBOR::Encoder) protected def on_to_cbor(cbor : ::CBOR::Encoder)
end end
@ -268,9 +272,28 @@ module CBOR
{% end %} {% end %}
{% end %} {% end %}
cbor.object do # Compute the size of the final list of properties to serialize.
{% for name, value in properties %} # This allows a more compact encoding, and a faster decoding.
nb_properties_to_serialize = 0
{% for name, value in properties %}
_{{name}} = @{{name}} _{{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 value[:emit_null] %}
unless _{{name}}.nil? unless _{{name}}.nil?
@ -279,22 +302,22 @@ module CBOR
# Write the key of the map # Write the key of the map
cbor.write({{value[:key]}}) cbor.write({{value[:key]}})
{% if value[:converter] %} {% if value[:converter] %}
if _{{name}} if _{{name}}
{{ value[:converter] }}.to_cbor(_{{name}}, cbor) {{ value[:converter] }}.to_cbor(_{{name}}, cbor)
else else
cbor.write(nil, use_undefined: value[:nil_as_undefined]) cbor.write(nil, use_undefined: value[:nil_as_undefined])
end end
{% else %} {% else %}
_{{name}}.to_cbor(cbor) _{{name}}.to_cbor(cbor)
{% end %} {% end %}
{% unless value[:emit_null] %} {% unless value[:emit_null] %}
end end
{% end %} {% end %}
{% end %} {% end %}
on_to_cbor(cbor) on_to_cbor(cbor)
end {% end %}
{% end %} {% end %}
end end
@ -310,6 +333,10 @@ module CBOR
end end
end end
protected def get_cbor_unmapped
cbor_unmapped
end
protected def on_to_cbor(cbor : ::CBOR::Encoder) protected def on_to_cbor(cbor : ::CBOR::Encoder)
cbor_unmapped.each do |key, value| cbor_unmapped.each do |key, value|
cbor.write(key) cbor.write(key)