|
|
- #pragma once
-
- #include <gp/algorithm/repeat.hpp>
- #include <gp/bitops.hpp>
- #include <gp/optional.hpp>
- #include <gp/pair.hpp>
- #include <gp/variant.hpp>
- #include <gp/vector.hpp>
-
-
- namespace gp {
- enum class cbor_type : uint8_t {
- uint = 0,
- nint = 1,
- bstr = 2,
- tstr = 3,
- list = 4,
- hmap = 5,
- tags = 6,
- oths = 7
- };
-
- enum class cbor_oths : uint8_t {
- value_false = 20,
- value_true = 21,
- value_null = 22,
- value_undefined = 23,
- byte = 24,
- word = 25,
- dword = 26,
- qword = 27,
- terminator = 31
- };
-
- enum class cbor_tags {
- datetime = 0,
- unix_time = 1,
- ubignum = 2,
- nbignum = 3,
- decimal = 4,
- bigfloat = 5,
- cose_encrypt0 = 16,
- cose_mac0 = 17,
- cose_sign1 = 18,
- expected_base64url = 21,
- expected_base64 = 22,
- expected_base16 = 23,
- encoded_cbor = 24,
- url = 32,
- base64url = 33,
- base64 = 34,
- regexp = 35,
- mime = 36,
- cose_encrypt = 96,
- cose_mac = 97,
- cose_sign = 98,
- signature = 55799
- };
-
- struct cbor_number final {
- bool sign;
- uint64_t value;
-
- bool is_negative() {
- return sign;
- }
-
- cbor_number(int64_t v)
- : sign{v < 0}
- , value{uint64_t((sign ? -1 : 1) * v)}
- {}
-
- cbor_number(bool s, uint64_t v)
- : sign{s}
- , value{v}
- {}
- };
-
- struct ieee754_hf final {
- 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;
- }
- };
-
- 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<ieee754_hf>();
- }
-
- operator float() {
- switch(contents.type()) {
- case backing::alt<ieee754_hf>():
- return contents.value<ieee754_hf>();
- case backing::alt<float>():
- return contents.value<float>();
- default:
- gp_config::assertion(false,"this code should never be reached");
- return std::numeric_limits<float>::quiet_NaN();
- }
- }
-
- operator double() {
- switch(contents.type()) {
- case backing::alt<ieee754_hf>():
- return contents.value<ieee754_hf>();
- case backing::alt<float>():
- return contents.value<float>();
- case backing::alt<double>():
- return contents.value<double>();
- default:
- gp_config::assertion(false,"this code should never be reached");
- return std::numeric_limits<double>::quiet_NaN();
- }
- }
- };
-
- struct undefined_t final {};
-
- template<typename T>
- using cbor_composite = gp::fixed_variant<
- gp::undefined_t,
- cbor_number,
- gp::vector<std::byte>,
- bool,
- gp::vector<T>,
- gp::vector<gp::pair<T, T>>,
- gp::nullopt_t,
- cbor_floating_point
- >;
-
- class cbor_value {
-
- cbor_composite<cbor_value> contents;
- gp::reference_wrapper<allocator> alloc;
- public:
-
- cbor_value(allocator& alloc_v)
- : contents()
- , alloc(alloc_v)
- {}
-
- cbor_value(cbor_number number, allocator& alloc_v)
- : contents(number)
- , alloc(alloc_v)
- {}
-
- cbor_value(cbor_composite<cbor_value> val, allocator& alloc_v)
- : contents(val)
- , alloc(alloc_v)
- {}
-
- cbor_value(const cbor_value& oth)
- : contents(oth.contents)
- , alloc(oth.alloc)
- {}
-
- cbor_value(cbor_value&& oth)
- : contents(gp::move(oth.contents))
- , alloc(gp::move(oth.alloc))
- {}
-
- cbor_value& operator=(cbor_value& value) {
- contents = value.contents;
- alloc = value.alloc;
- return *this;
- }
-
- cbor_value& operator=(cbor_value&& value) {
- gp::swap(contents, value.contents);
- gp::swap(alloc, value.alloc);
- return *this;
- }
-
- cbor_value& operator=(cbor_composite<cbor_value>& value) {
- contents = value;
- return *this;
- }
-
- template<typename T>
- cbor_value& operator=(T& value) {
- contents = value;
- return *this;
- }
-
- template<typename T>
- cbor_value& operator=(T&& value) {
- contents = gp::move(value);
- return *this;
- }
-
- auto new_array() {
- return gp::vector<cbor_value>{alloc};
- }
-
- auto new_object() {
- return gp::vector<gp::pair<cbor_value, cbor_value>>{alloc};
- }
-
- static gp::pair<uint64_t, gp::buffer<std::byte>::associated_iterator> decode_integer(gp::buffer<std::byte> src) {
- #define ERROR return {0, src.begin()}
- auto local = (uint8_t)0b00011111 & (uint8_t)src[0];
- if(local <= 23) {
- return {local, src.begin()+1};
- } else {
- switch((cbor_oths)local) {
- case cbor_oths::byte: {
- if(src.size()<2) ERROR;
- return {
- (uint8_t)*(src.begin()+1),
- src.begin()+2
- };
- }
- case cbor_oths::word: {
- if(src.size()<3) ERROR;
- return {
- uint16_t(*(src.slice_start(3).slice_end(2).cast<gp::endian_wrapper<uint16_t, endian::big>>().begin().data)),
- src.begin()+3
- };
- }
- case cbor_oths::dword: {
- if(src.size()<5) ERROR;
- return {
- uint32_t(*(src.slice_start(5).slice_end(4).cast<gp::endian_wrapper<uint32_t, endian::big>>().begin().data)),
- src.begin()+5
- };
- }
- case cbor_oths::qword: {
- if(src.size()<9) ERROR;
- return {
- uint64_t(*(src.slice_start(9).slice_end(8).cast<gp::endian_wrapper<uint64_t, endian::big>>().begin().data)),
- src.begin()+9
- };
- }
- default: {
- ERROR;
- }
- }
- }
- #undef ERROR
- }
-
- template<typename U>
- bool is_a() {
- if constexpr (
- std::is_same_v<U, uint8_t>
- || std::is_same_v<U, uint16_t>
- || std::is_same_v<U, uint32_t>
- || std::is_same_v<U, uint64_t>
- || std::is_same_v<U, int8_t>
- || std::is_same_v<U, int16_t>
- || std::is_same_v<U, int32_t>
- || std::is_same_v<U, int64_t>
- ) {
- if(contents.is_a<cbor_number>())
- {
- auto& v = contents.value<cbor_number>();
- if(!std::is_signed_v<U> && v.is_negative()) return false;
- if(
- std::numeric_limits<int64_t>::max()<v.value
- && (
- !std::is_same_v<U, uint64_t>
- || v.is_negative()
- )
- ) return false;
- int64_t signed_v = (v.is_negative() ? -1 : 1 ) * v.value;
- if(
- std::numeric_limits<U>::min() <= signed_v
- && std::numeric_limits<U>::max() >= signed_v
- ) return true;
- return false;
- } else return false;
- } else if constexpr (
- std::is_same_v<U, ieee754_hf>
- || std::is_same_v<U, float>
- || std::is_same_v<U, double>
- ) {
- auto& v = contents.value<cbor_floating_point>();
- switch(v.contents.type()) {
- case cbor_floating_point::backing::alt<ieee754_hf>():
- return std::is_same_v<U, ieee754_hf>;
- case cbor_floating_point::backing::alt<float>():
- return std::is_same_v<U, float> || std::is_same_v<U, ieee754_hf>;
- case cbor_floating_point::backing::alt<double>():
- return std::is_same_v<U, double> || std::is_same_v<U, float> || std::is_same_v<U, ieee754_hf>;
- default:
- return false;
- }
- } else {
- return contents.is_a<U>();
- }
- }
-
- template<typename U>
- U get_value() {
- gp_config::assertion(is_a<U>(), "can't convert value legally");
- if constexpr (
- std::is_same_v<U, uint8_t>
- || std::is_same_v<U, uint16_t>
- || std::is_same_v<U, uint32_t>
- || std::is_same_v<U, uint64_t>
- || std::is_same_v<U, int8_t>
- || std::is_same_v<U, int16_t>
- || std::is_same_v<U, int32_t>
- || std::is_same_v<U, int64_t>
- ) {
- auto& v = contents.value<cbor_number>();
- return (v.is_negative() ? -1 : 1 ) * v.value;
- } else if constexpr (
- std::is_same_v<U, ieee754_hf>
- || std::is_same_v<U, float>
- || std::is_same_v<U, double>
- ) {
- auto& v = contents.value<cbor_floating_point>();
- return v;
- } else {
- return contents.value<U>();
- }
- }
-
- static gp::pair<cbor_value, gp::buffer<std::byte>::associated_iterator> decode(allocator& alloc, gp::buffer<std::byte> src) {
- #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);
- switch(discriminant) {
- case cbor_type::uint:
- case cbor_type::nint: {
- auto nb = decode_integer(src);
- return {cbor_value{cbor_number{(bool)discriminant, nb.first}, alloc}, nb.second};
- }
- case cbor_type::bstr: {
- gp::vector<std::byte> str{alloc};
- if(local == 31) {
- auto sub = src.slice_end(src.size() - 1);
- if(!sub.size()) ERROR;
- while((uint8_t)sub[0] && 0b00011111 != 31) {
- auto len = decode_integer(sub);
- if(len.second == sub.begin()) ERROR;
- str.reserve(str.size() + len.first);
- auto end = (len.second + len.first);
- if(len.first + (len.second - src.begin()) > src.size()) ERROR;
- for(auto it = len.second; it != end; it++) {
- str.push_back(*it);
- }
- sub = sub.slice_end(sub.size() - (len.first + (len.second - sub.begin())));
- }
- return {cbor_value{str, alloc}, sub.begin()+1};
- } else if(auto len = decode_integer(src); len.second != src.begin()) {
- str.reserve(len.first);
- auto end = (len.second + len.first);
- if(len.first + (len.second - src.begin()) > src.size()) ERROR;
- for(auto it = len.second; it != end; it++) {
- str.push_back(*it);
- }
- return {cbor_value{str, alloc}, end};
- }
- ERROR;
- }
- case cbor_type::tstr: {
- ERROR;
- }
- case cbor_type::list: {
- gp::vector<cbor_value> list{alloc};
- if(local == 31) {
- // TODO: Add the indefinite length version
- } else if(auto len = decode_integer(src); len.second != src.begin()) {
- auto sub = src.slice_end(src.size()-(len.second - src.begin()));
- while(len.first--) {
- auto tmp = decode(alloc, sub);
- if(tmp.second == sub.begin()) ERROR;
- list.push_back(tmp.first);
- sub = sub.slice_end(sub.size() - (tmp.second - sub.begin()));
- }
- return {cbor_value{list, alloc}, sub.begin()};
- }
- ERROR;
- }
- case cbor_type::hmap: {
- gp::vector<gp::pair<cbor_value, cbor_value>> list{alloc};
- if(local == 31) {
- // TODO: Add the indefinite length version
- } else if(auto len = decode_integer(src); len.second != src.begin()) {
- auto sub = src.slice_end(src.size()-(len.second - src.begin()));
- while(len.first--) {
- auto tmpl = decode(alloc, sub);
- if(tmpl.second == sub.begin()) ERROR;
- sub = sub.slice_end(sub.size() - (tmpl.second - sub.begin()));
- auto tmpr = decode(alloc, sub);
- if(tmpr.second == sub.begin()) ERROR;
- list.push_back(gp::make_pair(
- tmpl.first,
- tmpr.first
- ));
- sub = sub.slice_end(sub.size() - (tmpr.second - sub.begin()));
- }
- return {cbor_value{list, alloc}, sub.begin()};
- }
- ERROR;
- }
- case cbor_type::tags: {
- // TODO: add tag decoding methods
- }
- case cbor_type::oths: {
- switch((cbor_oths)local) {
- case cbor_oths::value_false: {
- return {
- cbor_value{
- cbor_composite<cbor_value>(false),
- alloc
- },
- src.begin()+1
- };
- }
- case cbor_oths::value_true: {
- return {
- cbor_value{
- cbor_composite<cbor_value>(true),
- alloc
- },
- src.begin()+1
- };
- }
- case cbor_oths::value_null: {
- return {
- cbor_value{
- cbor_composite<cbor_value>(nullopt),
- alloc
- },
- src.begin()+1
- };
- }
- case cbor_oths::value_undefined: {
- return {
- cbor_value{
- cbor_composite<cbor_value>(undefined_t{}),
- alloc
- },
- src.begin()+1
- };
- }
- case cbor_oths::word: {
- if(src.size()<3) ERROR;
- return {
- cbor_value{
- cbor_floating_point{ieee754_hf(*(src.slice_start(3).slice_end(2).cast<gp::endian_wrapper<ieee754_hf, endian::big>>().begin().data))},
- alloc
- },
- src.begin()+3
- };
- }
- case cbor_oths::dword: {
- if(src.size()<5) ERROR;
- return {
- cbor_value{
- cbor_floating_point{float(*(src.slice_start(5).slice_end(4).cast<gp::endian_wrapper<float, endian::big>>().begin().data))},
- alloc
- },
- src.begin()+5
- };
- }
- case cbor_oths::qword: {
- if(src.size()<9) ERROR;
- return {
- cbor_value{
- cbor_floating_point{double(*(src.slice_start(9).slice_end(8).cast<gp::endian_wrapper<double, endian::big>>().begin().data))},
- alloc
- },
- src.begin()+9
- };
- }
- default: {
- ERROR;
- }
- }
- }
- }
- ERROR;
- #undef ERROR
- }
-
- static auto encode_float(buffer<std::byte> dest, cbor_floating_point& value) {
- switch(value.contents.type()) {
- case cbor_floating_point::backing::alt<ieee754_hf>():{
- 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<gp::endian_wrapper<ieee754_hf, gp::endian::big>>())[0] = value.contents.value<ieee754_hf>();
- return dest.begin()+3;
- }
- case cbor_floating_point::backing::alt<float>():{
- 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<gp::endian_wrapper<float, gp::endian::big>>())[0] = value.contents.value<float>();
- return dest.begin()+5;
- }
- case cbor_floating_point::backing::alt<double>():{
- 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<gp::endian_wrapper<double, gp::endian::big>>())[0] = value.contents.value<double>();
- return dest.begin()+9;
- }
- default: return dest.begin();
- }
- }
-
- static auto encode_length(buffer<std::byte> dest, cbor_type major, uint64_t value) {
- auto num = value;
- if(value <= 23) {
- if(dest.size() < 1) return dest.begin();
- dest[0] = std::byte(((uint8_t)major << 5u) + value);
- return dest.begin()+1;
- } else if(value <= 255) {
- if(dest.size() < 2) return dest.begin();
- dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::byte);
- dest[1] = std::byte(value);
- return dest.begin() + 2;
- } else if(value <= 65535) {
- if(dest.size() < 3) return dest.begin();
- dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::word);
- (dest.slice_start(3).slice_end(2).cast<gp::endian_wrapper<uint16_t, gp::endian::big>>())[0] = num;
- return dest.begin()+3;
- } else if(value <= 4294967295) {
- if(dest.size() < 5) return dest.begin();
- dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::dword);
- (dest.slice_start(5).slice_end(4).cast<gp::endian_wrapper<uint32_t, gp::endian::big>>())[0] = num;
- return dest.begin()+5;
- } else {
- if(dest.size() < 9) return dest.begin();
- dest[0] = std::byte(((uint8_t)major << 5u) + (uint8_t)cbor_oths::qword);
- (dest.slice_start(9).slice_end(8).cast<gp::endian_wrapper<uint64_t, gp::endian::big>>())[0] = num;
- return dest.begin()+9;
- }
- }
-
- auto encode(buffer<std::byte> dest) {
- switch(contents.type()) {
- case cbor_composite<cbor_value>::alt<undefined_t>(): {
- if(dest.size() < 1) return dest.begin();
- dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_undefined);
- return dest.begin()+1;
- }
- case cbor_composite<cbor_value>::alt<cbor_number>(): {
- auto& ref = contents.value<cbor_number>();
- return encode_length(
- dest,
- ref.is_negative() ? cbor_type::nint : cbor_type::uint,
- ref.value
- );
- }
- case cbor_composite<cbor_value>::alt<gp::vector<std::byte>>(): {
- auto& ref = contents.value<gp::vector<std::byte>>();
- auto it = encode_length(
- dest,
- cbor_type::bstr,
- ref.size()
- );
- if(it == dest.begin()) return it;
-
- for(auto a : ref) {
- *(it++) = a;
- }
- return it;
- }
- case cbor_composite<cbor_value>::alt<bool>(): {
- if(dest.size() < 1) return dest.begin();
- if(contents.value<bool>())
- dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_true);
- else
- dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_false);
- return dest.begin()+1;
- }
- case cbor_composite<cbor_value>::alt<gp::vector<cbor_value>>(): {
- auto& ary = contents.value<gp::vector<cbor_value>>();
- auto it_begin = encode_length(dest, cbor_type::list, ary.size());
- if(it_begin == dest.begin()) return dest.begin();
- for(auto& elem : ary) {
- auto slice = dest.slice_end(dest.size() - (it_begin - dest.begin()));
- auto it = elem.encode(slice);
- if(it == it_begin)
- return dest.begin();
- it_begin = it;
- }
- return it_begin;
- }
- case cbor_composite<cbor_value>::alt<gp::vector<gp::pair<cbor_value, cbor_value>>>(): {
- auto& ary = contents.value<gp::vector<gp::pair<cbor_value,cbor_value>>>();
- auto it_begin = encode_length(dest, cbor_type::hmap, ary.size());
- if(it_begin == dest.begin()) return dest.begin();
-
- for(auto& elem : ary) {
- auto slice = dest.slice_end(dest.size() - (it_begin - dest.begin()));
- auto it = elem.first.encode(slice);
- if(it == it_begin) return dest.begin();
- it_begin = it;
-
- slice = dest.slice_end(dest.size() - (it_begin - dest.begin()));
- it = elem.second.encode(slice);
- if(it == it_begin) return dest.begin();
- it_begin = it;
- }
- return it_begin;
- }
- case cbor_composite<cbor_value>::alt<gp::nullopt_t>(): {
- if(dest.size() < 1) return dest.begin();
- dest[0] = std::byte(((uint8_t)cbor_type::oths << 5u) + (uint8_t)cbor_oths::value_null);
- return dest.begin()+1;
- }
- case cbor_composite<cbor_value>::alt<cbor_floating_point>(): {
- return encode_float(dest, contents.value<cbor_floating_point>());
- }
- default: return dest.begin();
- }
- }
- };
- }
|