use crate::data_source::IonDataSource;
use crate::result::{decoding_error, IonResult};
use std::io::Write;
use std::mem;
const BITS_PER_ENCODED_BYTE: usize = 7;
const STORAGE_SIZE_IN_BITS: usize = mem::size_of::<usize>() * 8;
const MAX_ENCODED_SIZE_IN_BYTES: usize = STORAGE_SIZE_IN_BITS / BITS_PER_ENCODED_BYTE;
const LOWER_7_BITMASK: u8 = 0b0111_1111;
const HIGHEST_BIT_VALUE: u8 = 0b1000_0000;
#[derive(Debug)]
pub struct VarUInt {
value: usize,
size_in_bytes: usize,
}
impl VarUInt {
pub(crate) fn new(value: usize, size_in_bytes: usize) -> Self {
VarUInt {
value,
size_in_bytes,
}
}
pub fn read<R: IonDataSource>(data_source: &mut R) -> IonResult<VarUInt> {
let mut magnitude: usize = 0;
let mut byte_processor = |byte: u8| {
let lower_seven = (LOWER_7_BITMASK & byte) as usize;
magnitude <<= 7; magnitude |= lower_seven;
byte < HIGHEST_BIT_VALUE };
let encoded_size_in_bytes = data_source.read_next_byte_while(&mut byte_processor)?;
if encoded_size_in_bytes > MAX_ENCODED_SIZE_IN_BYTES {
return decoding_error(format!(
"Found a {}-byte VarUInt. Max supported size is {} bytes.",
encoded_size_in_bytes, MAX_ENCODED_SIZE_IN_BYTES
));
}
Ok(VarUInt {
size_in_bytes: encoded_size_in_bytes,
value: magnitude,
})
}
pub fn write_u64<W: Write>(sink: &mut W, mut magnitude: u64) -> IonResult<usize> {
const VAR_UINT_BUFFER_SIZE: usize = 9;
#[rustfmt::skip]
let mut buffer: [u8; VAR_UINT_BUFFER_SIZE] = [
0, 0, 0, 0, 0, 0, 0, 0, 0b1000_0000
];
if magnitude == 0 {
sink.write_all(&[0b1000_0000])?;
return Ok(1);
}
let mut first_byte = VAR_UINT_BUFFER_SIZE as u64;
for buffer_byte in buffer.iter_mut().rev() {
first_byte -= 1;
*buffer_byte |= magnitude as u8 & LOWER_7_BITMASK;
magnitude >>= BITS_PER_ENCODED_BYTE;
if magnitude == 0 {
break;
}
}
let encoded_bytes = &buffer[(first_byte as usize)..];
sink.write_all(encoded_bytes)?;
Ok(encoded_bytes.len())
}
#[inline(always)]
pub fn value(&self) -> usize {
self.value
}
#[inline(always)]
pub fn size_in_bytes(&self) -> usize {
self.size_in_bytes
}
}
#[cfg(test)]
mod tests {
use super::VarUInt;
use crate::result::IonResult;
use std::io::{BufReader, Cursor};
const ERROR_MESSAGE: &str = "Failed to read a VarUInt from the provided data.";
#[test]
fn test_read_var_uint() {
let var_uint = VarUInt::read(&mut Cursor::new(&[0b0111_1001, 0b0000_1111, 0b1000_0001]))
.expect(ERROR_MESSAGE);
assert_eq!(3, var_uint.size_in_bytes());
assert_eq!(1_984_385, var_uint.value());
}
#[test]
fn test_read_var_uint_small_buffer() {
let var_uint = VarUInt::read(
&mut BufReader::with_capacity(1, Cursor::new(&[0b0111_1001, 0b0000_1111, 0b1000_0001])),
)
.expect(ERROR_MESSAGE);
assert_eq!(var_uint.size_in_bytes(), 3);
assert_eq!(var_uint.value(), 1_984_385);
}
#[test]
fn test_read_var_uint_zero() {
let var_uint = VarUInt::read(&mut Cursor::new(&[0b1000_0000])).expect(ERROR_MESSAGE);
assert_eq!(var_uint.size_in_bytes(), 1);
assert_eq!(var_uint.value(), 0);
}
#[test]
fn test_read_var_uint_two_bytes_max_value() {
let var_uint =
VarUInt::read(&mut Cursor::new(&[0b0111_1111, 0b1111_1111])).expect(ERROR_MESSAGE);
assert_eq!(var_uint.size_in_bytes(), 2);
assert_eq!(var_uint.value(), 16_383);
}
#[test]
fn test_read_var_uint_overflow_detection() {
let _var_uint = VarUInt::read(&mut Cursor::new(&[
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b0111_1111,
0b1111_1111,
]))
.expect_err("This should have failed due to overflow.");
}
fn var_uint_encoding_test(value: u64, expected_encoding: &[u8]) -> IonResult<()> {
let mut buffer = vec![];
VarUInt::write_u64(&mut buffer, value)?;
assert_eq!(buffer.as_slice(), expected_encoding);
Ok(())
}
#[test]
fn test_write_var_uint_zero() -> IonResult<()> {
var_uint_encoding_test(0, &[0b1000_0000])?;
Ok(())
}
#[test]
fn test_write_var_uint_single_byte_values() -> IonResult<()> {
var_uint_encoding_test(6, &[0b1000_0110])?;
var_uint_encoding_test(17, &[0b1001_0001])?;
var_uint_encoding_test(41, &[0b1010_1001])?;
Ok(())
}
#[test]
fn test_write_var_uint_two_byte_values() -> IonResult<()> {
var_uint_encoding_test(279, &[0b0000_0010, 0b1001_0111])?;
var_uint_encoding_test(555, &[0b0000_0100, 0b1010_1011])?;
var_uint_encoding_test(999, &[0b0000_0111, 0b1110_0111])?;
Ok(())
}
#[test]
fn test_write_var_uint_three_byte_values() -> IonResult<()> {
var_uint_encoding_test(81_991, &[0b0000_0101, 0b0000_0000, 0b1100_0111])?;
var_uint_encoding_test(400_600, &[0b0001_1000, 0b0011_1001, 0b1101_1000])?;
Ok(())
}
}