diff --git a/include/gp/enveloppe/cbor.hpp b/include/gp/enveloppe/cbor.hpp index 1a530f8..61e9f34 100644 --- a/include/gp/enveloppe/cbor.hpp +++ b/include/gp/enveloppe/cbor.hpp @@ -80,13 +80,68 @@ namespace gp { uint16_t sign : 1; uint16_t exponent : 5; uint16_t mantissa : 10; + + // TODO: support for denormalized values and NaNs + operator float() { + auto a = (uint32_t)((sign<<16) | ((exponent+0x1C000)<<13) | (mantissa<<13)); + return *(float*)&a; + } + + operator double() { + return (float)*this; + } }; - using cbor_floating_point = gp::fixed_variant< - ieee754_hf, - float, - double - >; + struct cbor_floating_point final { + using backing = gp::fixed_variant< + ieee754_hf, + float, + double + >; + backing contents; + + cbor_floating_point(backing& p) + : contents{p}{} + + cbor_floating_point(ieee754_hf p) + : contents{p}{} + + cbor_floating_point(float p) + : contents{p}{} + + cbor_floating_point(double p) + : contents{p}{} + + operator ieee754_hf() { + return contents.value(); + } + + operator float() { + switch(contents.type()) { + case backing::alt(): + return contents.value(); + case backing::alt(): + return contents.value(); + default: + gp_config::assertion(false,"this code should never be reached"); + return std::numeric_limits::quiet_NaN(); + } + } + + operator double() { + switch(contents.type()) { + case backing::alt(): + return contents.value(); + case backing::alt(): + return contents.value(); + case backing::alt(): + return contents.value(); + default: + gp_config::assertion(false,"this code should never be reached"); + return std::numeric_limits::quiet_NaN(); + } + } + }; struct undefined_t final {}; @@ -109,7 +164,7 @@ namespace gp { public: cbor_value(allocator& alloc_v) - : contents(cbor_composite(undefined_t{})) + : contents() , alloc(alloc_v) {} @@ -213,8 +268,91 @@ namespace gp { #undef ERROR } + template + U& value() { + + } + + template + bool is_a() { + if constexpr ( + std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + ) { + if(contents.is_a()) + { + auto& v = contents.value(); + if(!std::is_signed_v && v.is_negative()) return false; + if( + std::numeric_limits::max() + || v.is_negative() + ) + ) return false; + int64_t signed_v = (v.is_negative() ? -1 : 1 ) * v.value; + if( + std::numeric_limits::min() <= signed_v + && std::numeric_limits::max() >= signed_v + ) return true; + return false; + } else return false; + } else if constexpr ( + std::is_same_v + || std::is_same_v + || std::is_same_v + ) { + auto& v = contents.value(); + switch(v.contents.type()) { + case cbor_floating_point::backing::alt(): + return std::is_same_v; + case cbor_floating_point::backing::alt(): + return std::is_same_v || std::is_same_v; + case cbor_floating_point::backing::alt(): + return std::is_same_v || std::is_same_v || std::is_same_v; + default: + return false; + } + } else { + return contents.is_a(); + } + } + + template + U get_value() { + gp_config::assertion(is_a(), "can't convert value legally"); + if constexpr ( + std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + ) { + auto& v = contents.value(); + return (v.is_negative() ? -1 : 1 ) * v.value; + } else if constexpr ( + std::is_same_v + || std::is_same_v + || std::is_same_v + ) { + auto& v = contents.value(); + return v; + } else { + return contents.value(); + } + } + static gp::pair::associated_iterator> decode(allocator& alloc, gp::buffer src) { - #define ERROR return {cbor_value{undefined_t{}, alloc}, src.begin()} + #define ERROR return {cbor_value{alloc}, src.begin()} if(src.size()==0) ERROR; auto discriminant = (cbor_type)(((uint8_t)*src.begin()) >> 5); auto local = uint8_t(((uint8_t)*src.begin()) & 0b00011111); @@ -375,23 +513,23 @@ namespace gp { } static auto encode_float(buffer dest, cbor_floating_point& value) { - switch(value.type()) { - case cbor_floating_point::alt():{ + switch(value.contents.type()) { + case cbor_floating_point::backing::alt():{ if(dest.size() < 3) return dest.begin(); dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::word); - (dest.slice_start(3).slice_end(2).cast>())[0] = value.value(); + (dest.slice_start(3).slice_end(2).cast>())[0] = value.contents.value(); return dest.begin()+3; } - case cbor_floating_point::alt():{ + case cbor_floating_point::backing::alt():{ if(dest.size() < 5) return dest.begin(); dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::dword); - (dest.slice_start(5).slice_end(4).cast>())[0] = value.value(); + (dest.slice_start(5).slice_end(4).cast>())[0] = value.contents.value(); return dest.begin()+5; } - case cbor_floating_point::alt():{ + case cbor_floating_point::backing::alt():{ if(dest.size() < 9) return dest.begin(); dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::qword); - (dest.slice_start(9).slice_end(8).cast>())[0] = value.value(); + (dest.slice_start(9).slice_end(8).cast>())[0] = value.contents.value(); return dest.begin()+9; } default: return dest.begin(); diff --git a/tests/cbor_test.cpp b/tests/cbor_test.cpp index c7c1961..b82ea84 100644 --- a/tests/cbor_test.cpp +++ b/tests/cbor_test.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -10,8 +11,8 @@ struct cbor_test : public test_scaffold { } virtual int run() { - gp::array store; - gp::arena alloc{&*store.begin(), store.size()}; + auto store = std::make_unique>(); + gp::arena alloc{&*store->begin(), store->size()}; using some_int = gp::fixed_variant; { @@ -73,6 +74,14 @@ struct cbor_test : public test_scaffold { gp_config::assertion(serialized.begin() != ret_it, "could not encode"); gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); + + gp::fill(serialized,(std::byte)0); + + auto decoded = gp::cbor_value::decode(alloc, serialized_manual.as_buffer()); + ret_it = decoded.first.encode(serialized.as_buffer()); + + gp_config::assertion(serialized.begin() != ret_it, "could not encode"); + gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); } { gp::cbor_value data{alloc}; @@ -87,6 +96,14 @@ struct cbor_test : public test_scaffold { gp_config::assertion(serialized.begin() != ret_it, "could not encode"); gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); + + gp::fill(serialized,(std::byte)0); + + auto decoded = gp::cbor_value::decode(alloc, serialized_manual.as_buffer()); + ret_it = decoded.first.encode(serialized.as_buffer()); + + gp_config::assertion(serialized.begin() != ret_it, "could not encode"); + gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); } { gp::cbor_value data{alloc}; @@ -105,6 +122,14 @@ struct cbor_test : public test_scaffold { gp_config::assertion(serialized.begin() != ret_it, "could not encode"); gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); + + gp::fill(serialized,(std::byte)0); + + auto decoded = gp::cbor_value::decode(alloc, serialized_manual.as_buffer()); + ret_it = decoded.first.encode(serialized.as_buffer()); + + gp_config::assertion(serialized.begin() != ret_it, "could not encode"); + gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); } { gp::vector str{alloc}; @@ -128,6 +153,14 @@ struct cbor_test : public test_scaffold { gp_config::assertion(serialized.begin() != ret_it, "could not encode"); gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); + + gp::fill(serialized,(std::byte)0); + + auto decoded = gp::cbor_value::decode(alloc, serialized_manual.as_buffer()); + ret_it = decoded.first.encode(serialized.as_buffer()); + + gp_config::assertion(serialized.begin() != ret_it, "could not encode"); + gp_config::assertion(serialized == serialized_manual, "data did not serialize correctly"); } return 0;