#pragma once #include #include #include #include #include #include // TODO: Rewrite in a more ORM-like way // TODO: Implement some bignum for support 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; } }; template vector& push_as_cbor(vector&, T); template vector& push_as_cbor(vector&, T&); inline vector& push_integer_with_header_as_cbor(vector& src, uint8_t header, uint64_t value) { auto norm_v = (value<0) ? -value : value; if(norm_v <= 23) { src.push_back(header+norm_v); } else if(norm_v < (1ll<<8ll)) { src.push_back(header+24); src.push_back(norm_v); } else if(norm_v < (1ll<<16ll)) { endian_wrapper wrapper = norm_v; src.push_back(header+25); for(auto byte : wrapper.bytes) { src.push_back(byte); } } else if(norm_v < (1ll<<32ll)) { endian_wrapper wrapper = norm_v; src.push_back(header+26); for(auto byte : wrapper.bytes) { src.push_back(byte); } } else { endian_wrapper wrapper = norm_v; src.push_back(header+27); for(auto byte : wrapper.bytes) { src.push_back(byte); } } return src; } /** * @brief Pushes an integer as CBOR on the vector * * @param src the vector on which the push happens * @param value the value to push, can be signed * @return vector& the same reference that was received for the source */ template<> inline vector& push_as_cbor(vector& src, int64_t& value) { uint8_t sign = (value<0) ? 0b00100000 : 0; auto norm_v = (value<0) ? -value : value; return push_integer_with_header_as_cbor(src, sign, norm_v); } /** * @brief Pushes an unsigned integer as CBOR on the vector * * @param src the vector on which the push happens * @param value the value to push, cannot be signed * @return vector& the same reference that was received for the source */ template<> inline vector& push_as_cbor(vector& src, uint64_t& value) { return push_integer_with_header_as_cbor(src, 0, value); } template<> inline vector& push_as_cbor(vector& src, std::nullptr_t) { src.push_back(0b11110110); return src; } struct cbor_undefined{}; template<> inline vector& push_as_cbor(vector& src, cbor_undefined) { src.push_back(0b11110111); return src; } template<> inline vector& push_as_cbor(vector& src, bool value) { src.push_back(0b11110100+(value ? 1 : 0)); return src; } template<> inline vector& push_as_cbor>(vector& src, gp::buffer value) { push_integer_with_header_as_cbor(src, (uint8_t)0b01000000, value.size()); for(auto byte : value) { src.push_back(byte); } return src; } struct cbor_array_initiator { size_t size; }; struct cbor_associative_array_initiator { size_t size; }; template<> inline vector& push_as_cbor(vector& src, cbor_array_initiator value) { return push_integer_with_header_as_cbor(src, (uint8_t)0b10000000, value.size); } template<> inline vector& push_as_cbor(vector& src, cbor_associative_array_initiator value) { return push_integer_with_header_as_cbor(src, (uint8_t)0b10100000, value.size); } template inline vector& push_as_cbor(vector& src, gp::pair& value) { push_as_cbor(src,value.first); return push_as_cbor(src,value.second); } struct cbor_tag_initiator { union { size_t as_integer; cbor_tags tag; }; }; template<> inline vector& push_as_cbor(vector& src, cbor_tag_initiator value) { return push_integer_with_header_as_cbor(src, (uint8_t)0b11000000, value.as_integer); } using parsing_state = gp::buffer; template gp::pair, parsing_state> read_cbor(parsing_state state); inline gp::pair, parsing_state> pull_arbitrary_integer_from_cbor(parsing_state state) { auto local = (uint8_t)0b00011111 & (uint8_t)*state.begin(); if(local <= 23) { return {*state.begin(), {state.begin()+1, state.end()}}; } else { switch((cbor_oths)local) { case cbor_oths::byte: { if(state.size() < 2) return {nullopt, state}; return {*(state.begin()+1), {state.begin()+2, state.end()}}; } case cbor_oths::word: { if(state.size() < 3) return {nullopt, state}; return { uint16_t(*(state.slice_start(3).slice_end(2).cast>().begin().data)), {state.begin()+3, state.end()} }; } case cbor_oths::dword: { if(state.size() < 5) return {nullopt, state}; return { uint32_t(*(state.slice_start(5).slice_end(4).cast>().begin().data)), {state.begin()+5, state.end()} }; } case cbor_oths::qword: { if(state.size() < 9) return {nullopt, state}; return { uint64_t(*(state.slice_start(9).slice_end(8).cast>().begin().data)), {state.begin()+9, state.end()} }; } default: { return {nullopt, state}; } } } } template<> inline gp::pair, parsing_state> read_cbor(parsing_state state) { if(state.size()) return {nullopt, state}; auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5); switch(type) { case cbor_type::uint: { auto[value, new_state] = pull_arbitrary_integer_from_cbor(state); if(value.has_value()) return {value.value(), new_state}; break; } case cbor_type::nint: { auto[value, new_state] = pull_arbitrary_integer_from_cbor(state); if(value.has_value()) return {-value.value(), new_state}; break; } default: break; } return {nullopt, state}; } template<> inline gp::pair, parsing_state> read_cbor(parsing_state state) { if(state.size()) return {nullopt, state}; auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5); switch(type) { case cbor_type::uint: { auto[value, new_state] = pull_arbitrary_integer_from_cbor(state); if(value.has_value()) return {value.value(), new_state}; break; } case cbor_type::nint: { auto[value, new_state] = pull_arbitrary_integer_from_cbor(state); if( value.has_value() && value.value() < (uint64_t)std::numeric_limits::max() ) { return {-value.value(), new_state}; } break; } default: break; } return {nullopt, state}; } }