Skip to content

Instantly share code, notes, and snippets.

@lucianodasilva
Created May 5, 2025 16:04
Show Gist options
  • Save lucianodasilva/e68453d034e943042b99e268605964b2 to your computer and use it in GitHub Desktop.
Save lucianodasilva/e68453d034e943042b99e268605964b2 to your computer and use it in GitHub Desktop.
varint load experiment
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