use nom::IResult;
pub const MAX_VINT_SIZE: usize = 9;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VIntError {
Truncated,
}
#[inline]
pub fn decode_unsigned(input: &[u8]) -> Result<(u64, usize), VIntError> {
let first = *input.first().ok_or(VIntError::Truncated)?;
let extra = first.leading_ones() as usize;
let total = extra + 1;
if input.len() < total {
return Err(VIntError::Truncated);
}
if extra == 0 {
return Ok((first as u64, 1));
}
let (_, rest) = input.split_at(1);
let mut be = [0u8; 8];
be[8 - extra..].copy_from_slice(&rest[..extra]);
let tail = u64::from_be_bytes(be);
let value = if extra == 8 {
tail
} else {
let data_bits_first = 7 - extra;
let mask = (1u64 << data_bits_first) - 1;
((first as u64 & mask) << (extra * 8)) | tail
};
Ok((value, total))
}
#[inline]
pub fn decode_signed(input: &[u8]) -> Result<(i64, usize), VIntError> {
let (unsigned, consumed) = decode_unsigned(input)?;
Ok((zigzag_decode(unsigned), consumed))
}
pub const MAX_VINT_LENGTH: i64 = 1024 * 1024 * 1024;
pub fn parse_vint(input: &[u8]) -> IResult<&[u8], i64> {
match decode_signed(input) {
Ok((value, consumed)) => Ok((&input[consumed..], value)),
Err(_) => Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Eof,
))),
}
}
#[allow(dead_code)]
fn encode_cassandra_vint(value: i64) -> Vec<u8> {
let _unsigned_value = if value >= 0 {
value as u64
} else {
value as u64 };
let bytes_needed = if value == 0 {
1
} else if (-63..=63).contains(&value) {
1 } else if (-8192..=8191).contains(&value) {
2 } else if (-1048576..=1048575).contains(&value) {
3 } else if (-134217728..=134217727).contains(&value) {
4 } else {
let abs_value = value.unsigned_abs();
if abs_value <= 0xFF {
2
} else if abs_value <= 0xFFFF {
3
} else if abs_value <= 0xFFFFFF {
4
} else if abs_value <= 0xFFFFFFFF {
5
} else {
8 }
};
match bytes_needed {
1 => {
if (0..=63).contains(&value) {
vec![0x80 | (value as u8)]
} else if value == -1 {
vec![0xFF]
} else if (-63..0).contains(&value) {
vec![0xC0 | ((-value) as u8)]
} else {
encode_cassandra_vint_multi_byte(value, 2)
}
}
2 => encode_cassandra_vint_multi_byte(value, 2),
3 => encode_cassandra_vint_multi_byte(value, 3),
4 => encode_cassandra_vint_multi_byte(value, 4),
_ => encode_cassandra_vint_multi_byte(value, bytes_needed),
}
}
#[allow(dead_code)]
fn encode_cassandra_vint_multi_byte(value: i64, num_bytes: usize) -> Vec<u8> {
let mut result = vec![0u8; num_bytes];
let leading_ones = num_bytes - 1;
let first_byte_mask = (0xFF << (8 - leading_ones)) & 0xFF;
let value_bytes = if value >= 0 {
value.to_be_bytes()
} else {
(value as u64).to_be_bytes() };
let data_bits = (num_bytes * 8) - leading_ones - 1; let data_bytes = data_bits.div_ceil(8);
let start_idx = 8 - data_bytes;
for (i, &byte) in value_bytes[start_idx..].iter().enumerate() {
if i == 0 {
let data_mask = (1u8 << (8 - leading_ones - 1)) - 1;
result[0] = first_byte_mask as u8 | (byte & data_mask);
} else {
result[i] = byte;
}
}
result
}
#[allow(dead_code)]
fn encode_zigzag_vint(value: i64) -> Vec<u8> {
let unsigned_value = zigzag_encode(value);
if unsigned_value <= 0x7F {
vec![unsigned_value as u8]
} else if unsigned_value <= 0x3FFF {
let high = ((unsigned_value >> 8) & 0x3F) | 0x80;
let low = unsigned_value & 0xFF;
vec![high as u8, low as u8]
} else if unsigned_value <= 0x1FFFFF {
let high = ((unsigned_value >> 16) & 0x1F) | 0xC0;
let mid = (unsigned_value >> 8) & 0xFF;
let low = unsigned_value & 0xFF;
vec![high as u8, mid as u8, low as u8]
} else {
let bytes = unsigned_value.to_be_bytes();
let mut result = vec![0xF0];
let start = bytes.iter().position(|&b| b != 0).unwrap_or(7);
result.extend_from_slice(&bytes[start..]);
result
}
}
#[allow(dead_code)]
pub fn zigzag_encode(value: i64) -> u64 {
((value << 1) ^ (value >> 63)) as u64
}
#[allow(dead_code)]
pub fn zigzag_decode(value: u64) -> i64 {
((value >> 1) ^ ((!0u64).wrapping_mul(value & 1))) as i64
}
#[allow(dead_code)]
fn vint_size(value: u64) -> usize {
if value == 0 {
return 1;
}
if value <= 127 {
1
} else if value <= 16383 {
2
} else if value <= 2097151 {
3
} else if value <= 268435455 {
4
} else if value <= 34359738367 {
5
} else if value <= 4398046511103 {
6
} else if value <= 562949953421311 {
7
} else if value <= 72057594037927935 {
8
} else {
9 }
}
pub fn encode_vint(value: i64) -> Vec<u8> {
encode_vint_zigzag(value)
}
pub fn encode_vint_zigzag(value: i64) -> Vec<u8> {
let unsigned_value = zigzag_encode(value);
if unsigned_value <= 0x7F {
vec![unsigned_value as u8]
} else if unsigned_value <= 0x3FFF {
let byte0 = 0x80 | ((unsigned_value >> 8) & 0x3F) as u8;
let byte1 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1]
} else if unsigned_value <= 0x1FFFFF {
let byte0 = 0xC0 | ((unsigned_value >> 16) & 0x1F) as u8;
let byte1 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte2 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1, byte2]
} else if unsigned_value <= 0xFFFFFFF {
let byte0 = 0xE0 | ((unsigned_value >> 24) & 0x0F) as u8;
let byte1 = ((unsigned_value >> 16) & 0xFF) as u8;
let byte2 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte3 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3]
} else if unsigned_value <= 0x7FFFFFFFF {
let byte0 = 0xF0 | ((unsigned_value >> 32) & 0x07) as u8;
let byte1 = ((unsigned_value >> 24) & 0xFF) as u8;
let byte2 = ((unsigned_value >> 16) & 0xFF) as u8;
let byte3 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte4 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4]
} else if unsigned_value <= 0x3FFFFFFFFFF {
let byte0 = 0xF8 | ((unsigned_value >> 40) & 0x03) as u8;
let byte1 = ((unsigned_value >> 32) & 0xFF) as u8;
let byte2 = ((unsigned_value >> 24) & 0xFF) as u8;
let byte3 = ((unsigned_value >> 16) & 0xFF) as u8;
let byte4 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte5 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4, byte5]
} else if unsigned_value <= 0x1FFFFFFFFFFFF {
let byte0 = 0xFC | ((unsigned_value >> 48) & 0x01) as u8;
let byte1 = ((unsigned_value >> 40) & 0xFF) as u8;
let byte2 = ((unsigned_value >> 32) & 0xFF) as u8;
let byte3 = ((unsigned_value >> 24) & 0xFF) as u8;
let byte4 = ((unsigned_value >> 16) & 0xFF) as u8;
let byte5 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte6 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6]
} else if unsigned_value <= 0xFFFFFFFFFFFFFF {
let byte0 = 0xFE;
let byte1 = ((unsigned_value >> 48) & 0xFF) as u8;
let byte2 = ((unsigned_value >> 40) & 0xFF) as u8;
let byte3 = ((unsigned_value >> 32) & 0xFF) as u8;
let byte4 = ((unsigned_value >> 24) & 0xFF) as u8;
let byte5 = ((unsigned_value >> 16) & 0xFF) as u8;
let byte6 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte7 = (unsigned_value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7]
} else {
let byte0 = 0xFF;
let byte1 = ((unsigned_value >> 56) & 0xFF) as u8;
let byte2 = ((unsigned_value >> 48) & 0xFF) as u8;
let byte3 = ((unsigned_value >> 40) & 0xFF) as u8;
let byte4 = ((unsigned_value >> 32) & 0xFF) as u8;
let byte5 = ((unsigned_value >> 24) & 0xFF) as u8;
let byte6 = ((unsigned_value >> 16) & 0xFF) as u8;
let byte7 = ((unsigned_value >> 8) & 0xFF) as u8;
let byte8 = (unsigned_value & 0xFF) as u8;
vec![
byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8,
]
}
}
pub fn parse_vuint(input: &[u8]) -> IResult<&[u8], u64> {
match decode_unsigned(input) {
Ok((value, consumed)) => Ok((&input[consumed..], value)),
Err(_) => Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Eof,
))),
}
}
pub fn encode_vuint(value: u64) -> Vec<u8> {
if value <= 0x7F {
vec![value as u8]
} else if value <= 0x3FFF {
let byte0 = 0x80 | ((value >> 8) & 0x3F) as u8;
let byte1 = (value & 0xFF) as u8;
vec![byte0, byte1]
} else if value <= 0x1FFFFF {
let byte0 = 0xC0 | ((value >> 16) & 0x1F) as u8;
let byte1 = ((value >> 8) & 0xFF) as u8;
let byte2 = (value & 0xFF) as u8;
vec![byte0, byte1, byte2]
} else if value <= 0xFFFFFFF {
let byte0 = 0xE0 | ((value >> 24) & 0x0F) as u8;
let byte1 = ((value >> 16) & 0xFF) as u8;
let byte2 = ((value >> 8) & 0xFF) as u8;
let byte3 = (value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3]
} else if value <= 0x7FFFFFFFF {
let byte0 = 0xF0 | ((value >> 32) & 0x07) as u8;
let byte1 = ((value >> 24) & 0xFF) as u8;
let byte2 = ((value >> 16) & 0xFF) as u8;
let byte3 = ((value >> 8) & 0xFF) as u8;
let byte4 = (value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4]
} else if value <= 0x3FFFFFFFFFF {
let byte0 = 0xF8 | ((value >> 40) & 0x03) as u8;
let byte1 = ((value >> 32) & 0xFF) as u8;
let byte2 = ((value >> 24) & 0xFF) as u8;
let byte3 = ((value >> 16) & 0xFF) as u8;
let byte4 = ((value >> 8) & 0xFF) as u8;
let byte5 = (value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4, byte5]
} else if value <= 0x1FFFFFFFFFFFF {
let byte0 = 0xFC | ((value >> 48) & 0x01) as u8;
let byte1 = ((value >> 40) & 0xFF) as u8;
let byte2 = ((value >> 32) & 0xFF) as u8;
let byte3 = ((value >> 24) & 0xFF) as u8;
let byte4 = ((value >> 16) & 0xFF) as u8;
let byte5 = ((value >> 8) & 0xFF) as u8;
let byte6 = (value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6]
} else if value <= 0xFFFFFFFFFFFFFF {
let byte0 = 0xFE;
let byte1 = ((value >> 48) & 0xFF) as u8;
let byte2 = ((value >> 40) & 0xFF) as u8;
let byte3 = ((value >> 32) & 0xFF) as u8;
let byte4 = ((value >> 24) & 0xFF) as u8;
let byte5 = ((value >> 16) & 0xFF) as u8;
let byte6 = ((value >> 8) & 0xFF) as u8;
let byte7 = (value & 0xFF) as u8;
vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7]
} else {
let byte0 = 0xFF;
let byte1 = ((value >> 56) & 0xFF) as u8;
let byte2 = ((value >> 48) & 0xFF) as u8;
let byte3 = ((value >> 40) & 0xFF) as u8;
let byte4 = ((value >> 32) & 0xFF) as u8;
let byte5 = ((value >> 24) & 0xFF) as u8;
let byte6 = ((value >> 16) & 0xFF) as u8;
let byte7 = ((value >> 8) & 0xFF) as u8;
let byte8 = (value & 0xFF) as u8;
vec![
byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8,
]
}
}
pub fn parse_vint_length(input: &[u8]) -> IResult<&[u8], usize> {
let (remaining, value) = parse_vuint(input)?;
if value > MAX_VINT_LENGTH as u64 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TooLarge,
)));
}
Ok((remaining, value as usize))
}
pub fn parse_vint_length_signed(input: &[u8]) -> IResult<&[u8], usize> {
let (remaining, value) = parse_vint(input)?;
if value < 0 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Verify,
)));
}
if value > MAX_VINT_LENGTH {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::TooLarge,
)));
}
Ok((remaining, value as usize))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zigzag_encoding() {
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(i64::MAX), u64::MAX - 1);
assert_eq!(zigzag_encode(i64::MIN), u64::MAX);
}
#[test]
fn test_zigzag_roundtrip() {
let test_values = vec![0, 1, -1, 127, -128, 32767, -32768, i64::MAX, i64::MIN];
for value in test_values {
let encoded = zigzag_encode(value);
let decoded = zigzag_decode(encoded);
assert_eq!(decoded, value, "ZigZag roundtrip failed for {}", value);
}
}
#[test]
fn test_vint_size_calculation() {
assert_eq!(vint_size(0), 1);
assert_eq!(vint_size(0x7F), 1); assert_eq!(vint_size(0x80), 2); assert_eq!(vint_size(0x3FFF), 2); assert_eq!(vint_size(0x4000), 3); }
#[test]
fn test_vint_single_byte_encoding() {
for i in 0..=63 {
let encoded = encode_vint(i);
assert_eq!(encoded.len(), 1, "Value {} should encode to 1 byte", i);
assert_eq!(encoded[0] & 0x80, 0, "Single byte should have leading 0");
let (_, decoded) = parse_vint(&encoded).unwrap();
assert_eq!(decoded, i, "Roundtrip failed for {}", i);
}
for i in -63..=0 {
let encoded = encode_vint(i);
assert_eq!(encoded.len(), 1, "Value {} should encode to 1 byte", i);
let (_, decoded) = parse_vint(&encoded).unwrap();
assert_eq!(decoded, i, "Roundtrip failed for {}", i);
}
}
#[test]
fn test_vint_multi_byte_encoding() {
let value = 128;
let encoded = encode_vint(value);
assert_eq!(encoded.len(), 2, "Value {} should encode to 2 bytes", value);
assert_eq!(
encoded[0] & 0x80,
0x80,
"Two-byte encoding should start with 10"
);
assert_eq!(
encoded[0] & 0x40,
0,
"Two-byte encoding should start with 10"
);
let (_, decoded) = parse_vint(&encoded).unwrap();
assert_eq!(decoded, value);
let value = 16384; let encoded = encode_vint(value);
assert_eq!(encoded.len(), 3, "Value {} should encode to 3 bytes", value);
assert_eq!(
encoded[0] & 0xE0,
0xC0,
"Three-byte encoding should start with 110"
);
let (_, decoded) = parse_vint(&encoded).unwrap();
assert_eq!(decoded, value);
}
#[test]
fn test_vint_comprehensive_roundtrip() {
let test_values = vec![
0,
1,
-1,
63,
-63,
64,
-64,
127,
-127,
128,
-128,
255,
-255,
256,
-256,
1023,
-1023,
1024,
-1024,
2047,
-2047,
2048,
-2048,
32767,
-32768,
65535,
-65535,
1000000,
-1000000,
i32::MAX as i64,
i32::MIN as i64,
i64::MAX / 2,
i64::MIN / 2,
];
for value in test_values {
let encoded = encode_vint(value);
assert!(
encoded.len() <= MAX_VINT_SIZE,
"Encoded length {} exceeds maximum {} for value {}",
encoded.len(),
MAX_VINT_SIZE,
value
);
let (remaining, decoded) = parse_vint(&encoded).unwrap();
assert!(remaining.is_empty(), "Parsing should consume all bytes");
assert_eq!(decoded, value, "Roundtrip failed for value {}", value);
}
}
#[test]
fn test_vint_format_compliance() {
let encoded = encode_vint(0);
assert_eq!(encoded, vec![0x00]);
let encoded = encode_vint(1);
assert_eq!(encoded, vec![0x02]);
let encoded = encode_vint(-1);
assert_eq!(encoded, vec![0x01]);
let encoded = encode_vint(64);
assert_eq!(encoded.len(), 2);
assert_eq!(encoded[0] & 0xC0, 0x80);
let (_, decoded) = parse_vint(&encoded).unwrap();
assert_eq!(decoded, 64);
}
#[test]
fn test_vuint_positive() {
let value = 1000u64;
let encoded = encode_vuint(value);
let (_, decoded) = parse_vuint(&encoded).unwrap();
assert_eq!(decoded, value);
}
#[test]
fn test_vint_length() {
let bytes = encode_vuint(42);
let (_, length) = parse_vint_length(&bytes).unwrap();
assert_eq!(length, 42);
}
#[test]
fn test_parse_vint_length_unsigned_single_byte_ac() {
assert_eq!(parse_vint_length(&[0x05]).unwrap().1, 5);
}
#[test]
fn test_parse_vint_length_unsigned_bit_patterns() {
assert_eq!(parse_vint_length(&[0x04]).unwrap().1, 4);
assert_eq!(parse_vint_length(&[0x7F]).unwrap().1, 127);
assert_eq!(parse_vint_length(&[0x00]).unwrap().1, 0);
assert_eq!(parse_vint_length(&[0x80, 0x80]).unwrap().1, 128);
}
#[test]
fn test_parse_vint_length_unsigned_roundtrip() {
for value in [
0u64, 1, 2, 3, 4, 5, 63, 64, 127, 128, 255, 256, 16383, 16384, 100_000,
] {
let bytes = encode_vuint(value);
let (_, decoded) = parse_vint_length(&bytes)
.unwrap_or_else(|_| panic!("unsigned length decode failed for {value}"));
assert_eq!(decoded as u64, value, "roundtrip failed for {value}");
}
}
#[test]
fn test_parse_vint_length_signed_zigzag_roundtrip() {
for value in [0i64, 1, 5, 13, 42, 63, 64, 127, 1000] {
let bytes = encode_vint(value); let (_, decoded) = parse_vint_length_signed(&bytes)
.unwrap_or_else(|_| panic!("signed length decode failed for {value}"));
assert_eq!(decoded as i64, value, "signed roundtrip failed for {value}");
}
assert!(parse_vint_length_signed(&encode_vint(-10)).is_err());
}
#[test]
fn test_collection_vint_debug() {
let encoded_4 = encode_vint(4);
println!("encode_vint(4) = {:?}", encoded_4);
let (_, decoded_4) = parse_vint(&encoded_4).unwrap();
println!("parse_vint({:?}) = {}", encoded_4, decoded_4);
let test_10 = [10u8];
let (_, decoded_10) = parse_vint(&test_10).unwrap();
println!("parse_vint([10]) = {}", decoded_10);
for i in 0..20 {
let encoded = encode_vint(i);
if encoded == vec![10] {
println!("Value {} encodes to [10]", i);
}
}
assert_eq!(decoded_4, 4, "Roundtrip test for 4");
let long_string = "this is a longer string";
let encoded_23 = encode_vint(long_string.len() as i64);
println!("encode_vint(23) = {:?}", encoded_23);
println!(
"String length: {}, bytes: {:?}",
long_string.len(),
long_string.as_bytes()
);
match parse_vint(&encoded_23) {
Ok((_, decoded)) => println!("parse_vint({:?}) = {}", encoded_23, decoded),
Err(e) => println!("parse_vint({:?}) failed: {:?}", encoded_23, e),
}
println!("\n🔍 Debug failing VInt cases:");
let failing_values = vec![256, 1048576, 64];
for value in failing_values {
let encoded = encode_vint(value);
println!(
"Value {}: encoded={:?}, hex={:02X?}, len={}",
value,
encoded,
encoded,
encoded.len()
);
if !encoded.is_empty() {
let first_byte = encoded[0];
println!(
" First byte: 0x{:02X} ({:08b}), leading_ones: {}",
first_byte,
first_byte,
first_byte.leading_ones()
);
if encoded.len() > 1 {
println!(
" Expected leading ones: {}, got: {}",
encoded.len() - 1,
first_byte.leading_ones()
);
}
}
}
println!("\n🔍 Testing Cassandra format:");
let cassandra_bytes = vec![0xE0, 0x01, 0x00]; match parse_vint(&cassandra_bytes) {
Ok((_, decoded)) => println!("Cassandra bytes {:?} -> {}", cassandra_bytes, decoded),
Err(e) => println!(
"Failed to parse Cassandra bytes {:?}: {:?}",
cassandra_bytes, e
),
}
}
#[test]
fn test_vint_errors() {
assert!(parse_vint(&[]).is_err());
assert!(parse_vint_length_signed(&encode_vint(-10)).is_err());
assert!(parse_vint_length(&encode_vuint(2_000_000_000u64)).is_err());
assert!(parse_vint(&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_ok());
assert!(parse_vint(&[0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_ok()); assert!(parse_vint(&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).is_err());
assert!(parse_vint(&[0x80, 0x00]).is_ok()); assert!(parse_vint(&[0xC0, 0x00, 0x00]).is_ok());
let _corrupted_data = b"data"; }
#[test]
fn test_parse_vint_truncated_lead_byte_is_err_1624() {
assert!(
parse_vint(&[0xC0]).is_err(),
"bare 0xC0 is a truncated 3-byte vint, must be Err"
);
assert!(
parse_vint(&[0x80]).is_err(),
"bare 0x80 is a truncated 2-byte vint, must be Err"
);
}
#[test]
fn test_parse_vint_framing_agreement_1624() {
let (rem_a, val_a) = parse_vint(&[0x80, 0x80]).expect("complete 2-byte vint");
let (rem_b, val_b) =
parse_vint(&[0x80, 0x80, 0xAA, 0xBB]).expect("2-byte vint + trailing junk");
assert_eq!(val_a, val_b, "decoded value must not depend on framing");
assert!(rem_a.is_empty(), "first form consumes all bytes");
assert_eq!(rem_b, &[0xAA, 0xBB], "second form leaves exactly the junk");
assert!(parse_vint(&[0x80]).is_err());
}
#[test]
fn test_vint_edge_case_patterns() {
let max_single = 63;
let encoded = encode_vint(max_single);
assert_eq!(encoded.len(), 1);
assert_eq!(encoded[0] & 0x80, 0);
let min_double = 64;
let encoded = encode_vint(min_double);
assert_eq!(encoded.len(), 2);
assert_eq!(encoded[0] & 0xC0, 0x80); }
#[test]
fn test_parse_vint_extended_formats() {
let bytes = [0xF0, 0x00, 0x00, 0x00, 0x10];
let _ = parse_vint(&bytes).expect("0xF0 5-byte vint parses");
let bytes = [0xFF, 0x00, 0x00, 0x00, 0x05];
assert!(
parse_vint(&bytes).is_err(),
"truncated 0xFF vint (5 bytes, needs 9) must be Err"
);
}
#[test]
fn test_vint_overflow_protection() {
let max = MAX_VINT_LENGTH as u64;
let large_value = encode_vuint(2_000_000_000u64); let result = parse_vint_length(&large_value);
assert!(
result.is_err(),
"Should reject values > 1GB for length fields"
);
let safe_value = encode_vuint(max - 1);
let result = parse_vint_length(&safe_value);
assert!(result.is_ok(), "Should accept values < 1GB");
let (_, length) = result.unwrap();
assert_eq!(length as u64, max - 1);
let at_limit = encode_vuint(max);
assert!(
parse_vint_length(&at_limit).is_ok(),
"Should accept exactly MAX_VINT_LENGTH"
);
let over_limit = encode_vuint(max + 1);
assert!(
parse_vint_length(&over_limit).is_err(),
"Should reject values > MAX_VINT_LENGTH"
);
let zero_value = encode_vuint(0u64);
let result = parse_vint_length(&zero_value);
assert!(result.is_ok(), "Should accept zero");
let (_, length) = result.unwrap();
assert_eq!(length, 0);
let sixteen_mb = encode_vuint(16 * 1024 * 1024u64);
let result = parse_vint_length(&sixteen_mb);
assert!(result.is_ok(), "Should accept 16MB (common limit)");
}
}