#pragma once #include #include #include #include #include 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)} {} }; struct ieee754_hf final { uint16_t sign : 1; uint16_t exponent : 5; uint16_t mantissa : 10; }; using cbor_floating_point = gp::fixed_variant< ieee754_hf, float, double >; struct undefined_t final {}; template using cbor_composite = gp::fixed_variant< undefined_t, cbor_number, gp::vector, bool, gp::vector, gp::vector>, gp::nullopt_t, cbor_floating_point >; class cbor_value { cbor_composite contents; gp::reference_wrapper alloc; public: cbor_value(allocator& alloc_v) : contents(cbor_composite(undefined_t{})) , alloc(alloc_v) {} cbor_value(cbor_number number, allocator& alloc_v) : contents(number) , 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& value) { contents = value; return *this; } template cbor_value& operator=(T& value) { contents = value; return *this; } template cbor_value& operator=(T&& value) { contents = gp::move(value); return *this; } auto new_array() { return gp::vector{alloc}; } auto new_object() { return gp::vector>{alloc}; } static auto encode_float(buffer dest, cbor_floating_point& value) { switch(value.type()) { case cbor_floating_point::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(); return dest.begin()+3; } case cbor_floating_point::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(); return dest.begin()+5; } case cbor_floating_point::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(); return dest.begin()+9; } default: return dest.begin(); } } static auto encode_length(buffer 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>())[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>())[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>())[0] = num; return dest.begin()+9; } } auto encode(buffer dest) { switch(contents.type()) { case cbor_composite::alt(): { 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::alt(): { auto& ref = contents.value(); return encode_length( dest, ref.is_negative() ? cbor_type::nint : cbor_type::uint, ref.value ); } case cbor_composite::alt>(): { auto& ref = contents.value>(); 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::alt(): { if(dest.size() < 1) return dest.begin(); if(contents.value()) 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::alt>(): { auto& ary = contents.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::alt>>(): { auto& ary = contents.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::alt(): { 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::alt(): { return encode_float(dest, contents.value()); } default: return dest.begin(); } } }; }