use std::io::Write;
use crate::decimal::Coefficient;
use crate::result::IonFailure;
use crate::{Int, IonResult};
#[derive(Debug)]
pub struct FixedInt {
value: Int,
size_in_bytes: usize,
}
pub(crate) const MAX_INT_SIZE_IN_BYTES: usize = size_of::<i128>();
pub(crate) const MAX_UINT_SIZE_IN_BYTES: usize = size_of::<u128>();
impl FixedInt {
fn new(size_in_bytes: usize, value: impl Into<Int>) -> Self {
Self::from_int(size_in_bytes, value.into())
}
pub(crate) const fn from_int(size_in_bytes: usize, value: Int) -> Self {
Self {
value,
size_in_bytes,
}
}
#[inline(always)]
pub fn read(input: &[u8], size_in_bytes: usize, offset: usize) -> IonResult<FixedInt> {
if input.len() < size_in_bytes {
return IonResult::incomplete("reading a FixedInt", offset);
}
let fixed_int = match size_in_bytes {
0 => FixedInt::from_int(0, Int::ZERO),
1 => Self::read_const::<1>(input),
2 => Self::read_const::<2>(input),
3 => Self::read_const::<3>(input),
4 => Self::read_const::<4>(input),
5 => Self::read_const::<5>(input),
6 => Self::read_const::<6>(input),
n @ 7..=MAX_INT_SIZE_IN_BYTES => Self::read_general_case(input, n),
_ => {
return IonResult::decoding_error(
"found a FixedInt that was larger than the supported maximum",
)
}
};
Ok(fixed_int)
}
#[inline]
pub(crate) fn read_const<const N: usize>(input_slice: &[u8]) -> FixedInt {
let input: [u8; N] = input_slice.try_into().unwrap();
let mut buffer = [0u8; MAX_INT_SIZE_IN_BYTES];
*buffer.last_chunk_mut::<N>().unwrap() = input;
let value = i128::from_le_bytes(buffer)
.checked_shr(128 - (N as u32 * 8))
.unwrap_or(0i128);
FixedInt::new(N, value)
}
fn read_general_case(input: &[u8], size_in_bytes: usize) -> FixedInt {
const BUFFER_SIZE: usize = MAX_INT_SIZE_IN_BYTES;
let mut buffer = [0u8; BUFFER_SIZE];
let first_occupied_byte_index = BUFFER_SIZE - size_in_bytes;
buffer[first_occupied_byte_index..].copy_from_slice(input);
let value: Int = i128::from_le_bytes(buffer)
.checked_shr(128 - (size_in_bytes as u32 * 8))
.unwrap_or(0)
.into();
FixedInt::new(size_in_bytes, value)
}
#[inline]
fn write_i128<W: Write>(output: &mut W, value: i128) -> IonResult<usize> {
let num_encoded_bytes = Self::encoded_size(value);
let le_bytes = value.to_le_bytes();
let encoded_bytes = &le_bytes[..num_encoded_bytes];
output.write_all(encoded_bytes)?;
Ok(num_encoded_bytes)
}
pub fn write(output: &mut impl Write, value: &Int) -> IonResult<usize> {
Self::write_i128(output, value.expect_i128()?)
}
#[inline]
pub fn encoded_size(value: impl Into<Int>) -> usize {
value.into().data.byte_len()
}
pub fn value(&self) -> &Int {
&self.value
}
pub fn size_in_bytes(&self) -> usize {
self.size_in_bytes
}
}
impl From<FixedInt> for Int {
fn from(other: FixedInt) -> Self {
other.value
}
}
impl From<FixedInt> for Coefficient {
fn from(other: FixedInt) -> Self {
other.value.into()
}
}
impl From<i64> for FixedInt {
fn from(other: i64) -> Self {
let encoded_size = FixedInt::encoded_size(other);
FixedInt::new(encoded_size, other)
}
}
#[cfg(test)]
mod tests {
use crate::lazy::encoder::binary::v1_1::fixed_int::FixedInt;
use crate::{Int, IonResult};
const FIXED_INT_TEST_CASES: &[(i64, &[u8])] = &[
(0, &[0b00000000]),
(1, &[0b00000001]),
(2, &[0b00000010]),
(14, &[0b00001110]),
(127, &[0b01111111]),
(128, &[0b10000000, 0b00000000]),
(32767, &[0b11111111, 0b01111111]),
(32768, &[0b00000000, 0b10000000, 0b00000000]),
(3954261, &[0b01010101, 0b01010110, 0b00111100]),
(8388607, &[0b11111111, 0b11111111, 0b01111111]),
(8388608, &[0b00000000, 0b00000000, 0b10000000, 0b00000000]),
(
2147483647,
&[0b11111111, 0b11111111, 0b11111111, 0b01111111],
),
(
2147483648,
&[0b00000000, 0b00000000, 0b00000000, 0b10000000, 0b00000000],
),
(
549755813887,
&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b01111111],
),
(
549755813888,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000, 0b00000000,
],
),
(
140737488355327,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b01111111,
],
),
(
140737488355328,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000, 0b00000000,
],
),
(
36028797018963967,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b01111111,
],
),
(
36028797018963968,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000,
0b00000000,
],
),
(
72624976668147840,
&[
0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010,
0b00000001,
],
),
(
9223372036854775807,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
0b01111111,
],
),
(-1, &[0b11111111]),
(-2, &[0b11111110]),
(-14, &[0b11110010]),
(-128, &[0b10000000]),
(-129, &[0b01111111, 0b11111111]),
(-32768, &[0b00000000, 0b10000000]),
(-32769, &[0b11111111, 0b01111111, 0b11111111]),
(-3954261, &[0b10101011, 0b10101001, 0b11000011]),
(-8388608, &[0b00000000, 0b00000000, 0b10000000]),
(-8388609, &[0b11111111, 0b11111111, 0b01111111, 0b11111111]),
(
-2147483648,
&[0b00000000, 0b00000000, 0b00000000, 0b10000000],
),
(
-2147483649,
&[0b11111111, 0b11111111, 0b11111111, 0b01111111, 0b11111111],
),
(
-549755813888,
&[0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000],
),
(
-549755813889,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b01111111, 0b11111111,
],
),
(
-140737488355328,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000,
],
),
(
-140737488355329,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b01111111, 0b11111111,
],
),
(
-36028797018963968,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000,
],
),
(
-36028797018963969,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b01111111,
0b11111111,
],
),
(
-72624976668147841,
&[
0b01111111, 0b10111111, 0b11011111, 0b11101111, 0b11110111, 0b11111011, 0b11111101,
0b11111110,
],
),
(
-9223372036854775808,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b10000000,
],
),
];
#[test]
fn decode_fixed_int() -> IonResult<()> {
for (expected_value, encoding) in FIXED_INT_TEST_CASES {
let fixed_int = FixedInt::read(encoding, encoding.len(), 0)?;
let actual_value = fixed_int.value();
let expected_value = &Int::from(*expected_value);
assert_eq!(actual_value, expected_value, "actual value {actual_value} was != expected value {expected_value} for encoding {encoding:x?}")
}
Ok(())
}
#[test]
fn decode_zero_length_fixed_int() -> IonResult<()> {
let encoding = &[];
let fixed_int = FixedInt::read(encoding, encoding.len(), 0)?;
let actual_value = fixed_int.value().expect_i64()?;
assert_eq!(
actual_value, 0,
"actual value {actual_value} was != expected value 0 for encoding {encoding:x?}"
);
Ok(())
}
#[test]
fn encode_fixed_int() -> IonResult<()> {
let mut test_cases: Vec<_> = FIXED_INT_TEST_CASES
.iter()
.cloned()
.map(|(value, encoding)| (Int::from(value), encoding))
.collect();
let big_uint_test_cases = FIXED_INT_TEST_CASES
.iter()
.cloned()
.map(|(value, encoding)| (Int::from(i128::from(value)), encoding));
test_cases.extend(big_uint_test_cases);
for (value, expected_encoding) in test_cases {
let mut buffer = Vec::new();
FixedInt::write(&mut buffer, &value)?;
let encoding = buffer.as_slice();
assert_eq!(encoding, expected_encoding, "actual encoding {encoding:x?} was != expected encoding {expected_encoding:x?} for value {value}");
}
Ok(())
}
}