Created
May 5, 2025 16:04
-
-
Save lucianodasilva/e68453d034e943042b99e268605964b2 to your computer and use it in GitHub Desktop.
varint load experiment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template < typename type_t > | |
concept s_nbyte_integral = | |
std::signed_integral <type_t> && | |
(sizeof(type_t) > 1); | |
template < typename type_t > | |
concept u_nbyte_integral = | |
std::unsigned_integral <type_t> && | |
(sizeof(type_t) > 1); | |
std::size_t varint_store (u_nbyte_integral auto value, std::span < uint8_t > dest) { | |
auto cursor = dest.begin (); | |
while (value != 0) { | |
if (cursor == dest.end()) { | |
throw std::runtime_error ("varint dest buffer overflow"); | |
} | |
// get and remove 7 bit fragment from value | |
auto frag = value & 0x7F; | |
value >>= 7; | |
// mark fragment as continuation if there are more bits to process | |
if (value != 0) { | |
frag |= 0x80; | |
} | |
// push out the fragment | |
*cursor = frag; | |
++cursor; | |
} | |
return std::distance(dest.begin(), cursor); | |
} | |
template < s_nbyte_integral type_t> | |
std::size_t varint_store (type_t value, std::span < uint8_t > dest) { | |
// zigzag encode signed value | |
// convert to unsigned | |
using utype_t = std::make_unsigned_t < type_t >; | |
auto const u_value = static_cast < utype_t > (value << 1) ^ static_cast < utype_t > (value >> (sizeof (type_t) * 8 - 1)); | |
// store as varint | |
return varint_store (u_value, dest); | |
} | |
template < std::integral type_t > requires (sizeof (type_t) == 1) | |
std::size_t varint_store (type_t value, std::span < uint8_t > dest) { | |
if (dest.empty()) { | |
throw std::runtime_error ("varint dest buffer overflow"); | |
} | |
std::copy_n (value, 1, dest.begin()); | |
return 1; | |
} | |
std::size_t varint_load (std::span < uint8_t > const src, u_nbyte_integral auto & value) { | |
constexpr uint8_t bit_size = sizeof (value) * 8; | |
auto cursor = src.begin (); | |
// clear data from dest address | |
value = {}; | |
std::uint8_t shift = 0; | |
while (true) { | |
if (cursor == src.end()) { | |
throw std::runtime_error ("varint source buffer overflow"); | |
} | |
// pop fragment | |
uint8_t const frag = *cursor; | |
++cursor; | |
// check for overflow or truncation | |
{ | |
auto const rem = bit_size - shift; | |
if (rem < 0) { | |
throw std::runtime_error ("varint source buffer overflow"); | |
} | |
// bits above rem are not allowed to be set otherwise | |
// the value would be truncated | |
auto const rem_value_mask = ~((1u << rem) - 1u); | |
if (rem < 7 && (frag & rem_value_mask) != 0) { | |
throw std::runtime_error ("varint truncation"); | |
} | |
} | |
// add 7 bit fragment to value | |
value |= (frag & 0x7F) << shift; | |
shift += 7; | |
// check if done | |
if ((frag & 0x80) == 0) { | |
break; | |
} | |
} | |
return std::distance(src.begin(), cursor); | |
} | |
template < s_nbyte_integral type_t > | |
std::size_t varint_load (std::span < uint8_t > const src, type_t & value) { | |
// zigzag decode signed value | |
// load as unsigned | |
using u_type_t = std::make_unsigned_t < type_t >; | |
u_type_t u_value = 0; | |
auto const size = varint_load < u_type_t > (src, u_value); | |
// convert to signed | |
value = static_cast < type_t > ((u_value >> 1) ^ (-static_cast < type_t > (u_value & 1))); | |
return size; | |
} | |
template < std::integral type_t > requires (sizeof (type_t) == 1) | |
std::size_t varint_load (std::span < uint8_t > const src, type_t & value) { | |
if (src.empty()) { | |
throw std::runtime_error ("varint source buffer overflow"); | |
} | |
// copy the byte | |
std::copy_n (src.begin(), 1, &value); | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment