pub mod var_u32 {
use bytes::{BufMut, BytesMut};
use crate::serde::DeserializeError;
const MAX_LEN_CODE: u8 = 4;
const FIRST_BYTE_DATA_BITS: u32 = 5;
const FIRST_BYTE_DATA_MASK: u8 = 0x1F;
const THRESHOLDS: [u64; 5] = [
1 << 5, 1 << 13, 1 << 21, 1 << 29, 1 << 37, ];
#[inline]
fn length_code(x: u32) -> u8 {
let x = x as u64;
for (i, &threshold) in THRESHOLDS.iter().enumerate() {
if x < threshold {
return i as u8;
}
}
MAX_LEN_CODE
}
pub fn serialize(value: u32, buf: &mut BytesMut) {
let len_code = length_code(value);
let total_bytes = len_code as usize + 1;
let mut bytes = [0u8; 5];
let shift = 8 * (total_bytes - 1);
let first_data_bits = ((value as u64) >> shift) as u8 & FIRST_BYTE_DATA_MASK;
bytes[0] = (len_code << FIRST_BYTE_DATA_BITS) | first_data_bits;
#[allow(clippy::needless_range_loop)]
for i in 1..total_bytes {
bytes[i] = ((value >> (8 * (total_bytes - 1 - i))) & 0xFF) as u8;
}
buf.put_slice(&bytes[..total_bytes]);
}
pub fn deserialize(buf: &mut &[u8]) -> Result<u32, DeserializeError> {
if buf.is_empty() {
return Err(DeserializeError {
message: "unexpected end of input: missing var_u32 first byte".to_string(),
});
}
let first_byte = buf[0];
let len_code = first_byte >> FIRST_BYTE_DATA_BITS;
if len_code > MAX_LEN_CODE {
return Err(DeserializeError {
message: format!(
"invalid var_u32 length code: {} (expected 0..{})",
len_code, MAX_LEN_CODE
),
});
}
let total_bytes = len_code as usize + 1;
if buf.len() < total_bytes {
return Err(DeserializeError {
message: format!(
"unexpected end of input: expected {} bytes, got {}",
total_bytes,
buf.len()
),
});
}
let mut value = (first_byte & FIRST_BYTE_DATA_MASK) as u64;
for i in 1..total_bytes {
value = (value << 8) | (buf[i] as u64);
}
if value > u32::MAX as u64 {
return Err(DeserializeError {
message: format!("var_u32 value {} exceeds u32::MAX", value),
});
}
*buf = &buf[total_bytes..];
Ok(value as u32)
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
proptest! {
#[test]
fn should_roundtrip_any_value(value: u32) {
let mut buf = BytesMut::new();
serialize(value, &mut buf);
let mut slice = buf.as_ref();
let decoded = deserialize(&mut slice).unwrap();
prop_assert_eq!(decoded, value);
prop_assert!(slice.is_empty());
}
#[test]
fn should_preserve_ordering(a: u32, b: u32) {
let mut buf_a = BytesMut::new();
let mut buf_b = BytesMut::new();
serialize(a, &mut buf_a);
serialize(b, &mut buf_b);
prop_assert_eq!(
a.cmp(&b),
buf_a.as_ref().cmp(buf_b.as_ref()),
"ordering mismatch: a={}, b={}, enc_a={:?}, enc_b={:?}",
a,
b,
buf_a.as_ref(),
buf_b.as_ref()
);
}
}
#[test]
fn should_encode_boundary_values_correctly() {
let cases: &[(u32, &[u8])] = &[
(0, &[0x00]),
(31, &[0x1F]),
(32, &[0x20, 0x20]),
(8191, &[0x3F, 0xFF]),
(8192, &[0x40, 0x20, 0x00]),
(2097151, &[0x5F, 0xFF, 0xFF]),
(2097152, &[0x60, 0x20, 0x00, 0x00]),
(536870911, &[0x7F, 0xFF, 0xFF, 0xFF]),
(536870912, &[0x80, 0x20, 0x00, 0x00, 0x00]),
(u32::MAX, &[0x80, 0xFF, 0xFF, 0xFF, 0xFF]),
];
for &(value, expected) in cases {
let mut buf = BytesMut::new();
serialize(value, &mut buf);
assert_eq!(buf.as_ref(), expected, "encoding mismatch for {value:#x}");
}
}
#[test]
fn should_fail_deserialize_empty_buffer() {
let mut slice: &[u8] = &[];
let result = deserialize(&mut slice);
assert!(result.is_err());
}
#[test]
fn should_fail_deserialize_invalid_length_code() {
for len_code in 5..=7u8 {
let data = &[len_code << 5, 0x00, 0x00, 0x00, 0x00, 0x00];
let mut slice = &data[..];
assert!(deserialize(&mut slice).is_err());
}
}
#[test]
fn should_fail_deserialize_truncated_payload() {
let data = &[0x20];
let mut slice = &data[..];
assert!(deserialize(&mut slice).is_err());
}
#[test]
fn should_advance_buffer_past_consumed_bytes() {
let data = &[0x05, 0xDE, 0xAD];
let mut slice = &data[..];
let decoded = deserialize(&mut slice).unwrap();
assert_eq!(decoded, 5);
assert_eq!(slice, &[0xDE, 0xAD]);
}
}
}
pub mod var_u64 {
use bytes::{BufMut, BytesMut};
use crate::serde::DeserializeError;
const MAX_LEN_CODE: u8 = 8;
const THRESHOLDS: [u64; 9] = [
1 << 4, 1 << 12, 1 << 20, 1 << 28, 1 << 36, 1 << 44, 1 << 52, 1 << 60, u64::MAX, ];
#[inline]
fn length_code(x: u64) -> u8 {
for (i, &threshold) in THRESHOLDS.iter().enumerate() {
if x < threshold {
return i as u8;
}
}
MAX_LEN_CODE
}
pub fn serialize(value: u64, buf: &mut BytesMut) {
let len_code = length_code(value);
let total_bytes = len_code as usize + 1;
let mut bytes = [0u8; 9];
let shift = 8 * (total_bytes - 1);
let first_data_bits = if shift >= 64 {
0
} else {
(value >> shift) as u8 & 0x0F
};
bytes[0] = (len_code << 4) | first_data_bits;
#[allow(clippy::needless_range_loop)]
for i in 1..total_bytes {
bytes[i] = ((value >> (8 * (total_bytes - 1 - i))) & 0xFF) as u8;
}
buf.put_slice(&bytes[..total_bytes]);
}
pub fn deserialize(buf: &mut &[u8]) -> Result<u64, DeserializeError> {
if buf.is_empty() {
return Err(DeserializeError {
message: "unexpected end of input: missing var_u64 first byte".to_string(),
});
}
let first_byte = buf[0];
let len_code = first_byte >> 4;
if len_code > MAX_LEN_CODE {
return Err(DeserializeError {
message: format!(
"invalid var_u64 length code: {} (expected 0..{})",
len_code, MAX_LEN_CODE
),
});
}
let total_bytes = len_code as usize + 1;
if buf.len() < total_bytes {
return Err(DeserializeError {
message: format!(
"unexpected end of input: expected {} bytes, got {}",
total_bytes,
buf.len()
),
});
}
let mut value = (first_byte & 0x0F) as u64;
for i in 1..total_bytes {
value = (value << 8) | (buf[i] as u64);
}
*buf = &buf[total_bytes..];
Ok(value)
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
proptest! {
#[test]
fn should_roundtrip_any_value(value: u64) {
let mut buf = BytesMut::new();
serialize(value, &mut buf);
let mut slice = buf.as_ref();
let decoded = deserialize(&mut slice).unwrap();
prop_assert_eq!(decoded, value);
prop_assert!(slice.is_empty());
}
#[test]
fn should_preserve_ordering(a: u64, b: u64) {
let mut buf_a = BytesMut::new();
let mut buf_b = BytesMut::new();
serialize(a, &mut buf_a);
serialize(b, &mut buf_b);
prop_assert_eq!(
a.cmp(&b),
buf_a.as_ref().cmp(buf_b.as_ref()),
"ordering mismatch: a={}, b={}, enc_a={:?}, enc_b={:?}",
a,
b,
buf_a.as_ref(),
buf_b.as_ref()
);
}
}
#[test]
fn should_encode_boundary_values_correctly() {
let cases: &[(u64, &[u8])] = &[
(0, &[0x00]),
(15, &[0x0F]),
(16, &[0x10, 0x10]),
(4095, &[0x1F, 0xFF]),
(4096, &[0x20, 0x10, 0x00]),
(1048575, &[0x2F, 0xFF, 0xFF]),
(1048576, &[0x30, 0x10, 0x00, 0x00]),
(268435455, &[0x3F, 0xFF, 0xFF, 0xFF]),
(268435456, &[0x40, 0x10, 0x00, 0x00, 0x00]),
(68719476735, &[0x4F, 0xFF, 0xFF, 0xFF, 0xFF]),
(68719476736, &[0x50, 0x10, 0x00, 0x00, 0x00, 0x00]),
(17592186044415, &[0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
(17592186044416, &[0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00]),
(
4503599627370495,
&[0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
(
4503599627370496,
&[0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
),
(
1152921504606846975,
&[0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
(
1152921504606846976, &[0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
),
(
u64::MAX,
&[0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
];
for &(value, expected) in cases {
let mut buf = BytesMut::new();
serialize(value, &mut buf);
assert_eq!(buf.as_ref(), expected, "encoding mismatch for {value:#x}");
}
}
#[test]
fn should_fail_deserialize_empty_buffer() {
let mut slice: &[u8] = &[];
let result = deserialize(&mut slice);
assert!(result.is_err());
}
#[test]
fn should_fail_deserialize_invalid_length_code() {
for len_code in 9..=15u8 {
let data = &[
len_code << 4,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
];
let mut slice = &data[..];
assert!(deserialize(&mut slice).is_err());
}
}
#[test]
fn should_fail_deserialize_truncated_payload() {
let data = &[0x10];
let mut slice = &data[..];
assert!(deserialize(&mut slice).is_err());
}
#[test]
fn should_advance_buffer_past_consumed_bytes() {
let data = &[0x05, 0xDE, 0xAD];
let mut slice = &data[..];
let decoded = deserialize(&mut slice).unwrap();
assert_eq!(decoded, 5);
assert_eq!(slice, &[0xDE, 0xAD]);
}
}
}