#pragma once #include #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)} {} 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(); } 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 {}; template using cbor_composite = gp::fixed_variant< gp::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() , alloc(alloc_v) {} cbor_value(cbor_number number, allocator& alloc_v) : contents(number) , alloc(alloc_v) {} cbor_value(cbor_composite 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& 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 gp::pair::associated_iterator> decode_integer(gp::buffer 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>().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>().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>().begin().data)), src.begin()+9 }; } default: { ERROR; } } } #undef ERROR } 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{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 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 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> 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(false), alloc }, src.begin()+1 }; } case cbor_oths::value_true: { return { cbor_value{ cbor_composite(true), alloc }, src.begin()+1 }; } case cbor_oths::value_null: { return { cbor_value{ cbor_composite(nullopt), alloc }, src.begin()+1 }; } case cbor_oths::value_undefined: { return { cbor_value{ cbor_composite(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>().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>().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>().begin().data))}, alloc }, src.begin()+9 }; } default: { ERROR; } } } } ERROR; #undef ERROR } static auto encode_float(buffer dest, cbor_floating_point& value) { 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.contents.value(); return dest.begin()+3; } 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.contents.value(); return dest.begin()+5; } 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.contents.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(); } } }; }