use crate::{encode, decode, encode_zigzag, decode_zigzag, Error};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VarintValue {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
}
const TYPE_BITS_UNSIGNED: u8 = 0b000_00000;
const TYPE_BITS_SIGNED: u8 = 0b001_00000;
const SIZE_BITS_8: u8 = 0b000_00000;
const SIZE_BITS_16: u8 = 0b000_00001;
const SIZE_BITS_32: u8 = 0b000_00010;
const SIZE_BITS_64: u8 = 0b000_00011;
const SIZE_BITS_128: u8 = 0b000_00100;
macro_rules! for_all_types {
($value:expr, $unsigned_op:expr, $signed_op:expr) => {
match $value {
VarintValue::U8(val) => ($unsigned_op)(*val, TYPE_BITS_UNSIGNED | SIZE_BITS_8),
VarintValue::U16(val) => ($unsigned_op)(*val, TYPE_BITS_UNSIGNED | SIZE_BITS_16),
VarintValue::U32(val) => ($unsigned_op)(*val, TYPE_BITS_UNSIGNED | SIZE_BITS_32),
VarintValue::U64(val) => ($unsigned_op)(*val, TYPE_BITS_UNSIGNED | SIZE_BITS_64),
VarintValue::U128(val) => ($unsigned_op)(*val, TYPE_BITS_UNSIGNED | SIZE_BITS_128),
VarintValue::I8(val) => ($signed_op)(*val, TYPE_BITS_SIGNED | SIZE_BITS_8),
VarintValue::I16(val) => ($signed_op)(*val, TYPE_BITS_SIGNED | SIZE_BITS_16),
VarintValue::I32(val) => ($signed_op)(*val, TYPE_BITS_SIGNED | SIZE_BITS_32),
VarintValue::I64(val) => ($signed_op)(*val, TYPE_BITS_SIGNED | SIZE_BITS_64),
VarintValue::I128(val) => ($signed_op)(*val, TYPE_BITS_SIGNED | SIZE_BITS_128),
}
};
}
impl VarintValue {
#[inline]
pub fn get_type_id(&self) -> u8 {
for_all_types!(self,
|_, type_id| type_id,
|_, type_id| type_id
)
}
#[inline]
fn direct_size_calculation(&self) -> usize {
let type_byte_size = 1;
match self {
VarintValue::U8(0) | VarintValue::U16(0) | VarintValue::U32(0) |
VarintValue::U64(0) | VarintValue::U128(0) | VarintValue::I8(0) |
VarintValue::I16(0) | VarintValue::I32(0) | VarintValue::I64(0) |
VarintValue::I128(0) => return type_byte_size,
_ => {}
}
let value_size = match self {
VarintValue::U8(val) => {
if *val == 0 { 1 } else {
let bits = 8 - val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::U16(val) => {
if *val == 0 { 1 } else {
let bits = 16 - val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::U32(val) => {
if *val == 0 { 1 } else {
let bits = 32 - val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::U64(val) => {
if *val == 0 { 1 } else {
let bits = 64 - val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::U128(val) => {
if *val == 0 { 1 } else {
let bits = 128 - val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::I8(val) => {
let zigzag_val = ((val << 1) ^ (val >> 7)) as u8;
if zigzag_val == 0 { 1 } else {
let bits = 8 - zigzag_val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::I16(val) => {
let zigzag_val = ((val << 1) ^ (val >> 15)) as u16;
if zigzag_val == 0 { 1 } else {
let bits = 16 - zigzag_val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::I32(val) => {
let zigzag_val = ((val << 1) ^ (val >> 31)) as u32;
if zigzag_val == 0 { 1 } else {
let bits = 32 - zigzag_val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::I64(val) => {
let zigzag_val = ((val << 1) ^ (val >> 63)) as u64;
if zigzag_val == 0 { 1 } else {
let bits = 64 - zigzag_val.leading_zeros() as usize;
(bits + 6) / 7
}
},
VarintValue::I128(val) => {
let zigzag_val = ((val << 1) ^ (val >> 127)) as u128;
if zigzag_val == 0 { 1 } else {
let bits = 128 - zigzag_val.leading_zeros() as usize;
(bits + 6) / 7
}
},
};
type_byte_size + value_size
}
#[inline]
pub fn serialized_size(&self) -> usize {
self.direct_size_calculation()
}
#[inline]
pub fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.is_empty() {
return Err(Error::BufferTooSmall {
needed: 1,
actual: 0
});
}
match self {
VarintValue::U8(0) | VarintValue::U16(0) | VarintValue::U32(0) |
VarintValue::U64(0) | VarintValue::U128(0) | VarintValue::I8(0) |
VarintValue::I16(0) | VarintValue::I32(0) | VarintValue::I64(0) |
VarintValue::I128(0) => {
buffer[0] = self.get_type_id();
return Ok(1);
},
_ => { }
}
buffer[0] = self.get_type_id();
match self {
VarintValue::U8(val) => {
let result = encode(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1), Err(e) => Err(e),
}
},
VarintValue::U16(val) => {
let result = encode(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::U32(val) => {
let result = encode(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::U64(val) => {
let result = encode(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::U128(val) => {
let result = encode(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::I8(val) => {
let result = encode_zigzag(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::I16(val) => {
let result = encode_zigzag(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::I32(val) => {
let result = encode_zigzag(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::I64(val) => {
let result = encode_zigzag(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
VarintValue::I128(val) => {
let result = encode_zigzag(*val, &mut buffer[1..]);
match result {
Ok(bytes_written) => Ok(bytes_written + 1),
Err(e) => Err(e),
}
},
}
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> Result<(Self, usize), Error> {
if bytes.is_empty() {
return Err(Error::InputTooShort);
}
let type_byte = bytes[0];
let type_bits = type_byte & 0b111_00000; let size_bits = type_byte & 0b000_11111;
let data = &bytes[1..];
if data.is_empty() && (type_bits == TYPE_BITS_UNSIGNED || type_bits == TYPE_BITS_SIGNED) {
match (type_bits, size_bits) {
(TYPE_BITS_UNSIGNED, SIZE_BITS_8) => return Ok((VarintValue::U8(0), 1)),
(TYPE_BITS_UNSIGNED, SIZE_BITS_16) => return Ok((VarintValue::U16(0), 1)),
(TYPE_BITS_UNSIGNED, SIZE_BITS_32) => return Ok((VarintValue::U32(0), 1)),
(TYPE_BITS_UNSIGNED, SIZE_BITS_64) => return Ok((VarintValue::U64(0), 1)),
(TYPE_BITS_UNSIGNED, SIZE_BITS_128) => return Ok((VarintValue::U128(0), 1)),
(TYPE_BITS_SIGNED, SIZE_BITS_8) => return Ok((VarintValue::I8(0), 1)),
(TYPE_BITS_SIGNED, SIZE_BITS_16) => return Ok((VarintValue::I16(0), 1)),
(TYPE_BITS_SIGNED, SIZE_BITS_32) => return Ok((VarintValue::I32(0), 1)),
(TYPE_BITS_SIGNED, SIZE_BITS_64) => return Ok((VarintValue::I64(0), 1)),
(TYPE_BITS_SIGNED, SIZE_BITS_128) => return Ok((VarintValue::I128(0), 1)),
_ => return Err(Error::InvalidEncoding),
}
}
match (type_bits, size_bits) {
(TYPE_BITS_UNSIGNED, SIZE_BITS_8) => {
let (val, bytes_read) = decode::<u8>(data)?;
Ok((VarintValue::U8(val), bytes_read + 1))
},
(TYPE_BITS_UNSIGNED, SIZE_BITS_16) => {
let (val, bytes_read) = decode::<u16>(data)?;
Ok((VarintValue::U16(val), bytes_read + 1))
},
(TYPE_BITS_UNSIGNED, SIZE_BITS_32) => {
let (val, bytes_read) = decode::<u32>(data)?;
Ok((VarintValue::U32(val), bytes_read + 1))
},
(TYPE_BITS_UNSIGNED, SIZE_BITS_64) => {
let (val, bytes_read) = decode::<u64>(data)?;
Ok((VarintValue::U64(val), bytes_read + 1))
},
(TYPE_BITS_UNSIGNED, SIZE_BITS_128) => {
let (val, bytes_read) = decode::<u128>(data)?;
Ok((VarintValue::U128(val), bytes_read + 1))
},
(TYPE_BITS_SIGNED, SIZE_BITS_8) => {
let (val, bytes_read) = decode_zigzag::<i8>(data)?;
Ok((VarintValue::I8(val), bytes_read + 1))
},
(TYPE_BITS_SIGNED, SIZE_BITS_16) => {
let (val, bytes_read) = decode_zigzag::<i16>(data)?;
Ok((VarintValue::I16(val), bytes_read + 1))
},
(TYPE_BITS_SIGNED, SIZE_BITS_32) => {
let (val, bytes_read) = decode_zigzag::<i32>(data)?;
Ok((VarintValue::I32(val), bytes_read + 1))
},
(TYPE_BITS_SIGNED, SIZE_BITS_64) => {
let (val, bytes_read) = decode_zigzag::<i64>(data)?;
Ok((VarintValue::I64(val), bytes_read + 1))
},
(TYPE_BITS_SIGNED, SIZE_BITS_128) => {
let (val, bytes_read) = decode_zigzag::<i128>(data)?;
Ok((VarintValue::I128(val), bytes_read + 1))
},
_ => Err(Error::InvalidEncoding),
}
}
}
#[macro_export]
macro_rules! varint {
(u8: $val:expr) => { $crate::VarintValue::U8($val) };
(u16: $val:expr) => { $crate::VarintValue::U16($val) };
(u32: $val:expr) => { $crate::VarintValue::U32($val) };
(u64: $val:expr) => { $crate::VarintValue::U64($val) };
(u128: $val:expr) => { $crate::VarintValue::U128($val) };
(i8: $val:expr) => { $crate::VarintValue::I8($val) };
(i16: $val:expr) => { $crate::VarintValue::I16($val) };
(i32: $val:expr) => { $crate::VarintValue::I32($val) };
(i64: $val:expr) => { $crate::VarintValue::I64($val) };
(i128: $val:expr) => { $crate::VarintValue::I128($val) };
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_id_encoding() {
let values = [
VarintValue::U8(42),
VarintValue::U16(1000),
VarintValue::U32(100000),
VarintValue::I8(-42),
VarintValue::I16(-1000),
VarintValue::I32(-100000),
];
for value in &values {
let type_id = value.get_type_id();
let mut dummy_buffer = [type_id, 0, 0, 0, 0]; let (decoded, _) = VarintValue::from_bytes(&dummy_buffer).unwrap_err_or_else(|_| {
match value {
VarintValue::U8(_) => (VarintValue::U8(0), 1),
VarintValue::U16(_) => (VarintValue::U16(0), 1),
VarintValue::U32(_) => (VarintValue::U32(0), 1),
VarintValue::U64(_) => (VarintValue::U64(0), 1),
VarintValue::U128(_) => (VarintValue::U128(0), 1),
VarintValue::I8(_) => (VarintValue::I8(0), 1),
VarintValue::I16(_) => (VarintValue::I16(0), 1),
VarintValue::I32(_) => (VarintValue::I32(0), 1),
VarintValue::I64(_) => (VarintValue::I64(0), 1),
VarintValue::I128(_) => (VarintValue::I128(0), 1),
}
});
match (value, decoded) {
(VarintValue::U8(_), VarintValue::U8(_)) => {},
(VarintValue::U16(_), VarintValue::U16(_)) => {},
(VarintValue::U32(_), VarintValue::U32(_)) => {},
(VarintValue::U64(_), VarintValue::U64(_)) => {},
(VarintValue::U128(_), VarintValue::U128(_)) => {},
(VarintValue::I8(_), VarintValue::I8(_)) => {},
(VarintValue::I16(_), VarintValue::I16(_)) => {},
(VarintValue::I32(_), VarintValue::I32(_)) => {},
(VarintValue::I64(_), VarintValue::I64(_)) => {},
(VarintValue::I128(_), VarintValue::I128(_)) => {},
_ => panic!("Type mismatch: original {:?}, decoded {:?}", value, decoded),
}
}
}
#[test]
fn test_varint_value_serialization() {
let values = [
VarintValue::U8(42),
VarintValue::U16(1000),
VarintValue::U32(1000000),
VarintValue::U64(1000000000),
VarintValue::U128(u128::MAX / 2),
];
for value in &values {
let mut buffer = [0u8; 30];
let bytes_written = value.to_bytes(&mut buffer).unwrap();
let (decoded, bytes_read) = VarintValue::from_bytes(&buffer[..bytes_written]).unwrap();
assert_eq!(*value, decoded);
assert_eq!(bytes_written, bytes_read);
}
let values = [
VarintValue::I8(-42),
VarintValue::I16(-1000),
VarintValue::I32(-1000000),
VarintValue::I64(-1000000000),
VarintValue::I128(i128::MIN / 2),
];
for value in &values {
let mut buffer = [0u8; 30];
let bytes_written = value.to_bytes(&mut buffer).unwrap();
let (decoded, bytes_read) = VarintValue::from_bytes(&buffer[..bytes_written]).unwrap();
assert_eq!(*value, decoded);
assert_eq!(bytes_written, bytes_read);
}
}
#[test]
fn test_zero_optimization() {
let zero_values = [
VarintValue::U8(0),
VarintValue::U16(0),
VarintValue::U32(0),
VarintValue::U64(0),
VarintValue::U128(0),
VarintValue::I8(0),
VarintValue::I16(0),
VarintValue::I32(0),
VarintValue::I64(0),
VarintValue::I128(0),
];
for value in &zero_values {
let mut buffer = [0u8; 30];
let bytes_written = value.to_bytes(&mut buffer).unwrap();
assert_eq!(bytes_written, 1, "Zero value {:?} should be encoded in 1 byte", value);
let (decoded, bytes_read) = VarintValue::from_bytes(&buffer[..bytes_written]).unwrap();
assert_eq!(*value, decoded);
assert_eq!(bytes_written, bytes_read);
}
}
#[test]
fn test_varint_macro() {
assert_eq!(varint!(u8: 42), VarintValue::U8(42));
assert_eq!(varint!(i16: -1000), VarintValue::I16(-1000));
assert_eq!(varint!(u32: 1000000), VarintValue::U32(1000000));
assert_eq!(varint!(i64: -1000000000), VarintValue::I64(-1000000000));
}
#[test]
fn test_serialized_size() {
let value = VarintValue::U64(128);
assert_eq!(value.serialized_size(), 3);
let value = VarintValue::I32(-1);
assert_eq!(value.serialized_size(), 2);
let value = VarintValue::U32(0);
assert_eq!(value.serialized_size(), 1); }
#[test]
fn test_error_handling() {
let value = VarintValue::U64(1000000);
let mut small_buffer = [0u8; 2];
assert!(value.to_bytes(&mut small_buffer).is_err());
let empty: [u8; 0] = [];
assert!(VarintValue::from_bytes(&empty).is_err());
let invalid = [0xFF, 0x00];
assert!(VarintValue::from_bytes(&invalid).is_err());
}
}
#[cfg(test)]
trait ResultExt<T, E> {
fn unwrap_err_or_else<F>(self, f: F) -> T
where
F: FnOnce(&E) -> T;
}
#[cfg(test)]
impl<T, E> ResultExt<T, E> for Result<T, E> {
fn unwrap_err_or_else<F>(self, f: F) -> T
where
F: FnOnce(&E) -> T,
{
match self {
Ok(t) => t,
Err(ref e) => f(e),
}
}
}