From: Karchnu <karchnu@karchnu.fr>
---
I added the `use_cbor_discriminator`.
This is a slow version, I read
the whole buffer twice. Still, I think this can be useful.
It can be
easily improved:
- if we can assume that the discriminator is the first
attribute in the buffer,
we could just read it and reset the decoder
- if we read and decode the whole content (which is currently the case),
we should be able to cast the result into the final object.
I don't
know how to do that in Crystal, but it sounds like the right thing to
do.
src/cbor/decoder.cr | 5 +++++src/cbor/from_cbor.cr | 4 ++++src/cbor/lexer.cr | 5 +++++src/cbor/serializable.cr | 29 +++++++++++++++++++++++++++++
4 files changed, 43 insertions(+)
diff --git a/src/cbor/decoder.cr b/src/cbor/decoder.cr
index 66ba0f7..ae567df 100644
--- a/src/cbor/decoder.cr+++ b/src/cbor/decoder.cr
@@ -2,6 +2,11 @@ class CBOR::Decoder
@lexer : Lexer
getter current_token : Token::T?
+ def reset+ @lexer.reset+ @current_token = @lexer.next_token+ end+ def initialize(input)
@lexer = Lexer.new(input)
@current_token = @lexer.next_token
diff --git a/src/cbor/from_cbor.cr b/src/cbor/from_cbor.cr
index e2610e7..0374c1f 100644
--- a/src/cbor/from_cbor.cr+++ b/src/cbor/from_cbor.cr
@@ -3,6 +3,10 @@ def Object.from_cbor(string_or_io)
new(parser)
end
+def Object.from_cbor(parser : CBOR::Decoder)+ new(parser)+end+def String.new(decoder : CBOR::Decoder)
decoder.read_string
end
diff --git a/src/cbor/lexer.cr b/src/cbor/lexer.cr
index 8ba2a11..cc34491 100644
--- a/src/cbor/lexer.cr+++ b/src/cbor/lexer.cr
@@ -8,6 +8,11 @@ class CBOR::Lexer
def initialize(@io : IO)
end
+ def reset+ @io.seek 0+ @eof = false+ end+ def next_token : Token::T?
return nil if @eof
diff --git a/src/cbor/serializable.cr b/src/cbor/serializable.cr
index b1fe894..b55258a 100644
--- a/src/cbor/serializable.cr+++ b/src/cbor/serializable.cr
@@ -346,6 +346,35 @@ module CBOR
end
end
+ macro use_cbor_discriminator(field, mapping)+ {% unless mapping.is_a?(HashLiteral) || mapping.is_a?(NamedTupleLiteral) %}+ {% mapping.raise "mapping argument must be a HashLiteral or a NamedTupleLiteral, not #{mapping.class_name.id}" %}+ {% end %}++ # SLOW. Read everything, get the type, read everything again.+ def self.new(decoder : ::CBOR::Decoder)+ if v = decoder.read_value+ decoder.reset+ case v+ when Hash(CBOR::Type, CBOR::Type)+ discriminator_value = v[{{field.id.stringify}}]?+ case discriminator_value+ {% for key, value in mapping %}+ when {{key.id.stringify}}+ return {{value.id}}.from_cbor(decoder)+ {% end %}+ else+ raise "Unknown '{{field.id}}' discriminator value: #{discriminator_value.inspect}"+ end+ else+ raise "cannot get cbor discriminator #{ {{ field.id.stringify }} }"+ end+ else+ raise "cannot decode cbor value"+ end+ end+ end+ # Tells this class to decode CBOR by using a field as a discriminator.
#
# - *field* must be the field name to use as a discriminator
--
2.26.2