use crate::wire_format::{MAX_VARINT_BYTES, VARINT_CONTINUATION_BIT, VARINT_PAYLOAD_MASK};
use crate::{ProtobufError, Result};
use ::std::io::Write;
mod read;
#[allow(unused_imports)]
pub use read::{
DecodeOutcome, DecodeState, IteratorExtVarint, ReadExtVarint, TryIteratorExtVarint,
VarintIterator,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Varint([u8; 8]);
impl Varint {
pub fn new(bytes: [u8; 8]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 8] {
&self.0
}
pub fn from_uint64(value: u64) -> Self {
let bytes = value.to_le_bytes();
Self(bytes)
}
pub fn from_uint32(value: u32) -> Self {
let bytes = (value as u64).to_le_bytes();
Self(bytes)
}
pub fn from_sint64(value: i64) -> Self {
let zigzag_value = if value < 0 {
((-value) as u64) * 2 - 1
} else {
(value as u64) * 2
};
let bytes = zigzag_value.to_le_bytes();
Self(bytes)
}
pub fn from_sint32(value: i32) -> Self {
Self::from_sint64(value as i64)
}
pub fn from_int64(value: i64) -> Self {
let bytes = (value as u64).to_le_bytes();
Self(bytes)
}
pub fn from_int32(value: i32) -> Self {
let bytes = (value as u64).to_le_bytes();
Self(bytes)
}
pub fn from_bool(value: bool) -> Self {
let bytes = (if value { 1u64 } else { 0u64 }).to_le_bytes();
Self(bytes)
}
pub fn to_uint64(&self) -> u64 {
u64::from_le_bytes(self.0)
}
pub fn try_to_uint32(&self) -> Result<u32> {
let value = self.to_uint64();
u32::try_from(value).map_err(|_| ProtobufError::VarintDowncastOutOfRange {
value,
target_type: "u32",
})
}
pub fn to_sint64(&self) -> i64 {
let value = self.to_uint64();
((value >> 1) as i64) ^ (-((value & 1) as i64))
}
pub fn try_to_sint32(&self) -> Result<i32> {
let sint64_value = self.to_sint64();
i32::try_from(sint64_value).map_err(|_| ProtobufError::VarintDowncastOutOfRange {
value: sint64_value as u64,
target_type: "i32",
})
}
pub fn to_int64(&self) -> i64 {
i64::from_le_bytes(self.0)
}
pub fn try_to_int32(&self) -> Result<i32> {
let value = self.to_int64();
i32::try_from(value).map_err(|_| ProtobufError::VarintDowncastOutOfRange {
value: value as u64,
target_type: "i32",
})
}
pub fn to_bool(&self) -> bool {
self.to_uint64() != 0
}
pub fn varint_size(&self) -> usize {
let value = self.to_uint64();
if value == 0 {
1
} else {
(64 - value.leading_zeros() as usize).div_ceil(7)
}
}
pub fn encode(&self) -> ([u8; MAX_VARINT_BYTES], usize) {
let value = self.to_uint64();
let mut bytes = [0u8; MAX_VARINT_BYTES];
let mut bytes_written = 0;
let mut remaining_value = value;
for byte in bytes.iter_mut() {
*byte = (remaining_value & VARINT_PAYLOAD_MASK as u64) as u8;
remaining_value >>= 7;
bytes_written += 1;
if remaining_value == 0 {
break;
} else {
*byte |= VARINT_CONTINUATION_BIT; }
}
(bytes, bytes_written)
}
}
pub trait WriteExtVarint {
fn write_varint(&mut self, value: &Varint) -> ::std::io::Result<usize>;
}
impl<W> WriteExtVarint for W
where
W: Write,
{
fn write_varint(&mut self, value: &Varint) -> ::std::io::Result<usize> {
let (bytes, count) = value.encode();
self.write_all(&bytes[..count])?;
Ok(count)
}
}
#[cfg(test)]
mod tests {
use super::{Varint, WriteExtVarint};
use crate::wire_format::MAX_VARINT_BYTES;
#[test]
fn test_varint_value_creation() {
let bytes = [0x96, 0x01, 0, 0, 0, 0, 0, 0];
let varint = Varint::new(bytes);
assert_eq!(varint.as_bytes(), &bytes);
}
#[test]
fn test_varint_conversions() {
let bytes = [0x96, 0x01, 0, 0, 0, 0, 0, 0];
let varint = Varint::new(bytes);
assert_eq!(varint.to_uint64(), 406);
match varint.try_to_uint32() {
Ok(value) => assert_eq!(value, 406),
Err(e) => panic!("Expected Ok(406), got error: {:?}", e),
}
let varint = Varint::new(bytes);
assert_eq!(varint.to_sint64(), 203);
let varint = Varint::new(bytes);
match varint.try_to_sint32() {
Ok(value) => assert_eq!(value, 203),
Err(e) => panic!("Expected Ok(203), got error: {:?}", e),
}
let varint = Varint::new(bytes);
assert_eq!(varint.to_bool(), true);
}
#[test]
fn test_signed_integer_conversions() {
let bytes = [0x01, 0, 0, 0, 0, 0, 0, 0];
let varint = Varint::new(bytes);
assert_eq!(varint.to_sint64(), -1);
let varint = Varint::new(bytes);
match varint.try_to_sint32() {
Ok(value) => assert_eq!(value, -1),
Err(e) => panic!("Expected Ok(-1), got error: {:?}", e),
}
}
#[test]
fn test_from_traits() {
let varint = Varint::from_uint64(150);
assert_eq!(varint.to_uint64(), 150);
let varint = Varint::from_uint32(150);
assert_eq!(varint.to_uint64(), 150);
let varint = Varint::from_sint64(150);
assert_eq!(varint.to_sint64(), 150);
let varint = Varint::from_sint64(-1);
assert_eq!(varint.to_sint64(), -1);
let varint = Varint::from_bool(true);
assert_eq!(varint.to_bool(), true);
let varint = Varint::from_int32(150);
assert_eq!(varint.to_int64(), 150);
let varint = Varint::from_int64(150);
assert_eq!(varint.to_int64(), 150);
}
#[test]
fn test_to_methods() {
let bytes = [150, 0, 0, 0, 0, 0, 0, 0];
let varint = Varint::new(bytes);
assert_eq!(varint.try_to_uint32().unwrap(), 150);
assert_eq!(varint.try_to_sint32().unwrap(), 75);
assert_eq!(varint.to_sint64(), 75);
assert_eq!(varint.to_bool(), true);
assert_eq!(varint.try_to_int32().unwrap(), 150);
assert_eq!(varint.to_int64(), 150);
}
#[test]
fn test_roundtrip_conversions() {
let original = 150u64;
let varint = Varint::from_uint64(original);
assert_eq!(varint.to_uint64(), original);
let original = 150u32;
let varint = Varint::from_uint32(original);
let converted = varint.try_to_uint32().unwrap();
assert_eq!(converted, original);
let original = -1i64;
let varint = Varint::from_sint64(original);
let converted = varint.to_sint64();
assert_eq!(converted, original);
let original = 150i64;
let varint = Varint::from_int64(original);
let converted = varint.to_int64();
assert_eq!(converted, original);
let original = 150i32;
let varint = Varint::from_int32(original);
let converted = varint.try_to_int32().unwrap();
assert_eq!(converted, original);
let original = true;
let varint = Varint::from_bool(original);
let converted = varint.to_bool();
assert_eq!(converted, original);
}
#[test]
fn test_encode_varint() {
let varint = Varint::from_uint64(150);
let (bytes, count) = varint.encode();
assert_eq!(count, 2);
assert_eq!(&bytes[..count], &[0x96, 0x01]);
let varint = Varint::from_uint64(127);
let (bytes, count) = varint.encode();
assert_eq!(count, 1);
assert_eq!(&bytes[..count], &[0x7F]);
let varint = Varint::from_uint64(0);
let (bytes, count) = varint.encode();
assert_eq!(count, 1);
assert_eq!(&bytes[..count], &[0x00]);
let varint = Varint::from_uint64(0x7FFFFFFFFFFFFFFF);
let (bytes, count) = varint.encode();
assert_eq!(count, 9);
assert_eq!(
&bytes[..count],
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]
);
let varint = Varint::from_uint64(0xFFFFFFFFFFFFFFFF);
let (bytes, count) = varint.encode();
assert_eq!(count, MAX_VARINT_BYTES);
assert_eq!(
&bytes[..count],
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01]
);
}
#[test]
fn test_write_varint() {
let varint = Varint::from_uint64(150);
let mut buffer = Vec::new();
let bytes_written = buffer.write_varint(&varint).unwrap();
assert_eq!(bytes_written, 2);
assert_eq!(buffer, vec![0x96, 0x01]);
let varint = Varint::from_uint64(127);
let mut buffer = Vec::new();
let bytes_written = buffer.write_varint(&varint).unwrap();
assert_eq!(bytes_written, 1);
assert_eq!(buffer, vec![0x7F]);
let varint = Varint::from_uint64(0);
let mut buffer = Vec::new();
let bytes_written = buffer.write_varint(&varint).unwrap();
assert_eq!(bytes_written, 1);
assert_eq!(buffer, vec![0x00]);
let varint = Varint::from_uint64(0x7FFFFFFFFFFFFFFF);
let mut buffer = Vec::new();
let bytes_written = buffer.write_varint(&varint).unwrap();
assert_eq!(bytes_written, 9);
let varint = Varint::from_uint64(0xFFFFFFFFFFFFFFFF);
let mut buffer = Vec::new();
let bytes_written = buffer.write_varint(&varint).unwrap();
assert_eq!(bytes_written, MAX_VARINT_BYTES);
}
#[test]
fn test_all_encoding_methods_consistency() {
let test_values = vec![0, 1, 127, 128, 150, 255, 256, 65535, 0x7FFFFFFF];
for &value in &test_values {
let varint = Varint::from_uint64(value);
let (array_bytes, array_count) = varint.encode();
let varint2 = Varint::from_uint64(value);
let mut vec_buffer = Vec::new();
let vec_count = vec_buffer.write_varint(&varint2).unwrap();
assert_eq!(array_count, vec_count);
assert_eq!(&array_bytes[..array_count], &vec_buffer[..]);
}
}
}