#[inline]
fn zigzag_encode(n: i64) -> u64 {
((n << 1) ^ (n >> 63)) as u64
}
pub fn encode_signed(value: i64, buf: &mut Vec<u8>) {
let unsigned_value = zigzag_encode(value);
encode_unsigned(unsigned_value, buf);
}
pub fn encode_unsigned(value: u64, buf: &mut Vec<u8>) {
let size = unsigned_len_value(value);
if size == 1 {
buf.push(value as u8);
} else if size == 9 {
buf.push(0xFF);
buf.extend_from_slice(&value.to_be_bytes());
} else {
let extra_bytes = size - 1;
let mask = encode_extra_bytes_to_read(extra_bytes);
let first_byte_data_bits = 8 - extra_bytes - 1;
let shift = extra_bytes * 8;
let first_byte_data = ((value >> shift) & ((1 << first_byte_data_bits) - 1)) as u8;
buf.push(mask | first_byte_data);
for i in (0..extra_bytes).rev() {
buf.push(((value >> (i * 8)) & 0xFF) as u8);
}
}
}
#[inline]
pub fn signed_len(value: i64) -> usize {
let unsigned_value = zigzag_encode(value);
unsigned_len_value(unsigned_value)
}
#[inline]
pub fn unsigned_len(value: u64) -> usize {
unsigned_len_value(value)
}
#[inline]
fn unsigned_len_value(value: u64) -> usize {
let magnitude = (value | 1).leading_zeros();
((639 - (magnitude * 9)) >> 6) as usize
}
#[inline]
fn encode_extra_bytes_to_read(extra_bytes: usize) -> u8 {
if extra_bytes == 0 {
0x00
} else if extra_bytes >= 8 {
0xFF
} else {
let shift = 8 - extra_bytes;
0xFF_u8 << shift
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zigzag_encode() {
assert_eq!(zigzag_encode(0), 0);
assert_eq!(zigzag_encode(-1), 1);
assert_eq!(zigzag_encode(1), 2);
assert_eq!(zigzag_encode(-2), 3);
assert_eq!(zigzag_encode(2), 4);
assert_eq!(zigzag_encode(-3), 5);
assert_eq!(zigzag_encode(63), 126);
assert_eq!(zigzag_encode(-64), 127);
assert_eq!(zigzag_encode(64), 128);
}
#[test]
fn test_encode_signed_test_vectors() {
let test_cases = vec![
(0i64, vec![0x00]),
(1i64, vec![0x02]),
(-1i64, vec![0x01]),
(63i64, vec![0x7E]),
(-64i64, vec![0x7F]),
(64i64, vec![0x80, 0x80]),
];
for (value, expected) in test_cases {
let mut buf = Vec::new();
encode_signed(value, &mut buf);
assert_eq!(
buf, expected,
"encode_signed({}) failed: expected {:?}, got {:?}",
value, expected, buf
);
}
}
#[test]
fn test_encode_unsigned_boundaries() {
let test_cases = vec![
(0u64, vec![0x00]),
(127u64, vec![0x7F]), (128u64, vec![0x80, 0x80]), (16383u64, vec![0xBF, 0xFF]), (16384u64, vec![0xC0, 0x40, 0x00]), ];
for (value, expected) in test_cases {
let mut buf = Vec::new();
encode_unsigned(value, &mut buf);
assert_eq!(
buf, expected,
"encode_unsigned({}) failed: expected {:?}, got {:?}",
value, expected, buf
);
}
}
#[test]
fn test_signed_len() {
assert_eq!(signed_len(0), 1);
assert_eq!(signed_len(1), 1);
assert_eq!(signed_len(-1), 1);
assert_eq!(signed_len(63), 1);
assert_eq!(signed_len(-64), 1);
assert_eq!(signed_len(64), 2);
assert_eq!(signed_len(-65), 2);
assert_eq!(signed_len(127), 2);
assert_eq!(signed_len(-128), 2);
}
#[test]
fn test_unsigned_len() {
assert_eq!(unsigned_len(0), 1);
assert_eq!(unsigned_len(127), 1);
assert_eq!(unsigned_len(128), 2);
assert_eq!(unsigned_len(16383), 2);
assert_eq!(unsigned_len(16384), 3);
assert_eq!(unsigned_len(2097151), 3);
assert_eq!(unsigned_len(2097152), 4);
}
#[test]
fn test_encode_extra_bytes() {
assert_eq!(encode_extra_bytes_to_read(0), 0x00);
assert_eq!(encode_extra_bytes_to_read(1), 0x80);
assert_eq!(encode_extra_bytes_to_read(2), 0xC0);
assert_eq!(encode_extra_bytes_to_read(3), 0xE0);
assert_eq!(encode_extra_bytes_to_read(4), 0xF0);
assert_eq!(encode_extra_bytes_to_read(5), 0xF8);
assert_eq!(encode_extra_bytes_to_read(6), 0xFC);
assert_eq!(encode_extra_bytes_to_read(7), 0xFE);
assert_eq!(encode_extra_bytes_to_read(8), 0xFF);
}
#[test]
fn test_roundtrip_with_decoder() {
use crate::parser::vint::parse_vint;
let test_values = vec![
0,
1,
-1,
63,
-64,
64,
-65,
127,
-128,
255,
-255,
1000,
-1000,
32767,
-32768,
1048576,
-1048576,
i32::MAX as i64,
i32::MIN as i64,
];
for value in test_values {
let mut buf = Vec::new();
encode_signed(value, &mut buf);
let (remaining, decoded) = parse_vint(&buf).unwrap();
assert!(
remaining.is_empty(),
"Decoder should consume all bytes for value {}",
value
);
assert_eq!(
decoded, value,
"Roundtrip failed for value {}: encoded as {:?}",
value, buf
);
}
}
#[test]
fn test_large_values() {
let test_cases = vec![
(1u64 << 7, 2), (1u64 << 14, 3), (1u64 << 21, 4), (1u64 << 28, 5), (1u64 << 35, 6), (1u64 << 42, 7), (1u64 << 49, 8), (1u64 << 56, 9), ];
for (value, expected_size) in test_cases {
assert_eq!(
unsigned_len(value),
expected_size,
"Value {} should encode to {} bytes",
value,
expected_size
);
let mut buf = Vec::new();
encode_unsigned(value, &mut buf);
assert_eq!(
buf.len(),
expected_size,
"Encoded {} to {:?}, expected {} bytes",
value,
buf,
expected_size
);
}
}
#[test]
fn test_performance_target() {
use std::time::Instant;
let iterations = 10_000;
let mut buf = Vec::with_capacity(9);
let start = Instant::now();
for i in 0..iterations {
buf.clear();
encode_signed(i, &mut buf);
}
let elapsed = start.elapsed();
let avg_ns = elapsed.as_nanos() / iterations as u128;
println!("Average encode time: {} ns", avg_ns);
assert!(
avg_ns < 1000,
"Encoding is too slow: {} ns per operation",
avg_ns
);
}
#[test]
fn test_cassandra_compatibility() {
let test_cases = vec![
(0i64, vec![0x00], "Zero value"), (1i64, vec![0x02], "Single byte positive"), (63i64, vec![0x7E], "Maximum single byte positive"), (64i64, vec![0x80, 0x80], "Two byte encoding start"), (127i64, vec![0x80, 0xFE], "Two byte positive"), (-1i64, vec![0x01], "Single byte negative"), (-64i64, vec![0x7F], "Single byte negative boundary"), (-65i64, vec![0x80, 0x81], "Two byte negative"), ];
for (value, expected_bytes, description) in test_cases {
let mut buf = Vec::new();
encode_signed(value, &mut buf);
assert_eq!(
buf, expected_bytes,
"{}: encode_signed({}) failed: expected {:?}, got {:?}",
description, value, expected_bytes, buf
);
}
}
}