~arestifo/crystal-cbor

307c57879a539bca9b5d85ec627b9de3eb1af7db — Karchnu 1 year, 8 months ago 3ab21c9
Compute the number of object properties to serialize.
4 files changed, 52 insertions(+), 21 deletions(-)

M CHANGELOG.md
M README.md
M spec/cbor/serializable_spec.cr
M src/cbor/serializable.cr
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)