#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; }; 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(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(*(gp::endian_wrapper*)(src.begin().data)), src.begin()+3 }; } case cbor_oths::dword: { if(src.size()<5) ERROR; return { uint32_t(*(gp::endian_wrapper*)(src.begin().data)), src.begin()+5 }; } case cbor_oths::qword: { if(src.size()<9) ERROR; return { uint64_t(*(gp::endian_wrapper*)(src.begin().data)), src.begin()+9 }; } default: { ERROR; } } } #undef ERROR } static gp::pair::associated_iterator> decode(allocator& alloc, gp::buffer src) { #define ERROR return {cbor_value{undefined_t{}, 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: { } 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)(*(gp::endian_wrapper*)(src.begin().data))}, alloc }, src.begin()+3 }; } case cbor_oths::dword: { if(src.size()<5) ERROR; return { cbor_value{ cbor_floating_point{float(*(gp::endian_wrapper*)(src.begin().data))}, alloc }, src.begin()+5 }; } case cbor_oths::qword: { if(src.size()<9) ERROR; return { cbor_value{ cbor_floating_point{double(*(gp::endian_wrapper*)(src.begin().data))}, alloc }, src.begin()+9 }; } default: { ERROR; } } } } ERROR; #undef ERROR } 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(); } } }; }