use crate::result::IonResult;
use std::io::Write;
use std::mem;
type VarIntStorage = i64;
const LOWER_6_BITMASK: u8 = 0b0011_1111;
const LOWER_7_BITMASK: u8 = 0b0111_1111;
const BITS_PER_BYTE: usize = 8;
const BITS_PER_U64: usize = mem::size_of::<u64>() * BITS_PER_BYTE;
const VARINT_NEGATIVE_ZERO: u8 = 0xC0;
#[derive(Debug)]
pub struct VarInt {
size_in_bytes: usize,
value: VarIntStorage,
is_negative: bool,
}
const MAGNITUDE_BITS_IN_FINAL_BYTE: usize = 6;
impl VarInt {
pub(crate) fn new(value: i64, is_negative: bool, size_in_bytes: usize) -> Self {
VarInt {
size_in_bytes,
value,
is_negative,
}
}
pub fn write_i64<W: Write>(sink: &mut W, value: i64) -> IonResult<usize> {
const VAR_INT_BUFFER_SIZE: usize = 10;
#[rustfmt::skip]
let mut buffer: [u8; VAR_INT_BUFFER_SIZE] = [
0, 0, 0, 0, 0,
0, 0, 0, 0, 0b1000_0000
];
let mut magnitude: u64 = value.unsigned_abs();
let occupied_bits = BITS_PER_U64 - magnitude.leading_zeros() as usize;
let mut bytes_required: usize = 1;
let remaining_bits = occupied_bits.saturating_sub(MAGNITUDE_BITS_IN_FINAL_BYTE);
bytes_required += f64::ceil(remaining_bits as f64 / 7.0) as usize;
let mut bytes_remaining = bytes_required;
for buffer_byte in buffer[VAR_INT_BUFFER_SIZE - bytes_required..]
.iter_mut()
.rev()
{
bytes_remaining -= 1;
if bytes_remaining > 0 {
*buffer_byte |= magnitude as u8 & LOWER_7_BITMASK;
magnitude >>= 7;
} else {
*buffer_byte |= magnitude as u8 & LOWER_6_BITMASK;
if value < 0 {
*buffer_byte |= 0b0100_0000;
}
}
}
let encoded_bytes = &buffer[VAR_INT_BUFFER_SIZE - bytes_required..];
sink.write_all(encoded_bytes)?;
Ok(encoded_bytes.len())
}
pub fn write_negative_zero<W: Write>(sink: &mut W) -> IonResult<usize> {
sink.write_all(&[VARINT_NEGATIVE_ZERO])?;
Ok(1)
}
pub fn is_negative_zero(&self) -> bool {
self.value == 0 && self.is_negative
}
#[inline(always)]
pub fn value(&self) -> VarIntStorage {
self.value
}
#[inline(always)]
pub fn size_in_bytes(&self) -> usize {
self.size_in_bytes
}
}
#[cfg(test)]
mod tests {
use super::VarInt;
use crate::result::IonResult;
fn var_int_encoding_test(value: i64, expected_encoding: &[u8]) -> IonResult<()> {
let mut buffer = vec![];
VarInt::write_i64(&mut buffer, value)?;
assert_eq!(buffer.as_slice(), expected_encoding);
Ok(())
}
#[test]
fn test_write_var_uint_zero() -> IonResult<()> {
var_int_encoding_test(0, &[0b1000_0000])?;
Ok(())
}
#[test]
fn test_write_var_int_single_byte_values() -> IonResult<()> {
var_int_encoding_test(17, &[0b1001_0001])?;
var_int_encoding_test(-17, &[0b1101_0001])?;
Ok(())
}
#[test]
fn test_write_var_int_two_byte_values() -> IonResult<()> {
var_int_encoding_test(555, &[0b0000_0100, 0b1010_1011])?;
var_int_encoding_test(-555, &[0b0100_0100, 0b1010_1011])?;
Ok(())
}
#[test]
fn test_write_var_int_three_byte_values() -> IonResult<()> {
var_int_encoding_test(400_600, &[0b0001_1000, 0b0011_1001, 0b1101_1000])?;
var_int_encoding_test(-400_600, &[0b0101_1000, 0b0011_1001, 0b1101_1000])?;
Ok(())
}
}