use std::io::Write;
use ice_code::ice as cold_path;
use num_traits::{PrimInt, Unsigned};
use crate::decimal::Coefficient;
use crate::lazy::encoder::binary::v1_1::fixed_int::{
MAX_INT_SIZE_IN_BYTES, MAX_UINT_SIZE_IN_BYTES,
};
use crate::result::IonFailure;
use crate::{IonError, IonResult, UInt};
#[derive(Debug)]
pub struct FixedUInt {
value: UInt,
size_in_bytes: usize,
}
impl FixedUInt {
fn new(size_in_bytes: usize, value: impl Into<UInt>) -> Self {
Self {
value: value.into(),
size_in_bytes,
}
}
#[inline]
pub fn read(input: &[u8], size_in_bytes: usize, offset: usize) -> IonResult<FixedUInt> {
if input.len() < size_in_bytes {
return IonResult::incomplete("reading a FixedUInt", offset);
}
if size_in_bytes > MAX_INT_SIZE_IN_BYTES {
return cold_path! {{IonResult::decoding_error("found a FixedUInt that was larger than the supported maximum")}};
}
const BUFFER_SIZE: usize = MAX_UINT_SIZE_IN_BYTES;
let mut buffer = [0u8; BUFFER_SIZE];
buffer[..size_in_bytes].copy_from_slice(input);
let value: UInt = u128::from_le_bytes(buffer).into();
Ok(FixedUInt::new(size_in_bytes, value))
}
#[inline]
pub(crate) fn write<W: Write>(output: &mut W, value: impl Into<UInt>) -> IonResult<usize> {
let le_bytes = value.into().data.to_le_bytes();
output.write_all(&le_bytes)?;
Ok(le_bytes.len())
}
pub fn value(&self) -> &UInt {
&self.value
}
pub fn size_in_bytes(&self) -> usize {
self.size_in_bytes
}
#[inline]
pub(crate) fn write_as_uint<I: PrimInt + Unsigned>(
output: &mut impl Write,
value: impl Into<UInt>,
) -> IonResult<()> {
let size_in_bytes = std::mem::size_of::<I>();
let value: u128 = u128::try_from(value.into())?;
let encoded_bytes = value.to_le_bytes();
let max_value: u128 = num_traits::cast::cast(I::max_value()).ok_or(
IonError::encoding_error("Unable to represent bounds for value as 128bit value"),
)?;
if !(0..=max_value).contains(&value) {
return IonResult::encoding_error(format!(
"provided unsigned integer value does not fit within {size_in_bytes} byte(s)"
));
}
output.write_all(&encoded_bytes[..size_in_bytes])?;
Ok(())
}
}
impl TryFrom<FixedUInt> for Coefficient {
type Error = IonError;
fn try_from(other: FixedUInt) -> Result<Self, Self::Error> {
use crate::types::integer::Int;
let as_int: Int = other.value.into();
Ok(as_int.into())
}
}
#[cfg(test)]
mod tests {
use crate::lazy::encoder::binary::v1_1::fixed_uint::FixedUInt;
use crate::{IonResult, UInt};
const FIXED_UINT_TEST_CASES: &[(u64, &[u8])] = &[
(0, &[0b00000000]),
(1, &[0b00000001]),
(2, &[0b00000010]),
(14, &[0b00001110]),
(127, &[0b01111111]),
(128, &[0b10000000]),
(255, &[0b11111111]),
(256, &[0b00000000, 0b00000001]),
(65535, &[0b11111111, 0b11111111]),
(65536, &[0b00000000, 0b00000000, 0b00000001]),
(3954261, &[0b01010101, 0b01010110, 0b00111100]),
(16777215, &[0b11111111, 0b11111111, 0b11111111]),
(16777216, &[0b00000000, 0b00000000, 0b00000000, 0b00000001]),
(
4294967295,
&[0b11111111, 0b11111111, 0b11111111, 0b11111111],
),
(
4294967296,
&[0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001],
),
(
1099511627775,
&[0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111],
),
(
1099511627776,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
],
),
(
281474976710655,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
],
),
(
281474976710656,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001,
],
),
(
5023487023698435,
&[
0b00000011, 0b11010010, 0b10010100, 0b10110111, 0b11010101, 0b11011000, 0b00010001,
],
),
(
72057594037927935,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
],
),
(
72057594037927936,
&[
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000001,
],
),
(
72624976668147840,
&[
0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010,
0b00000001,
],
),
(
9223372036854775807,
&[
0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111,
0b01111111,
],
),
];
#[test]
fn decode_fixed_uint() -> IonResult<()> {
for (expected_value, encoding) in FIXED_UINT_TEST_CASES {
let fixed_uint = FixedUInt::read(encoding, encoding.len(), 0)?;
let actual_value = fixed_uint.value();
let expected_value = &UInt::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_uint() -> IonResult<()> {
let encoding = &[];
let fixed_uint = FixedUInt::read(encoding, encoding.len(), 0)?;
let actual_value = fixed_uint.value().expect_u64()?;
assert_eq!(
actual_value, 0,
"actual value {actual_value} was != expected value 0 for encoding {encoding:x?}"
);
Ok(())
}
#[test]
fn encode_fixed_uint() -> IonResult<()> {
let mut test_cases: Vec<_> = FIXED_UINT_TEST_CASES
.iter()
.cloned()
.map(|(value, encoding)| (UInt::from(value), encoding))
.collect();
let big_uint_test_cases = FIXED_UINT_TEST_CASES
.iter()
.cloned()
.map(|(value, encoding)| (UInt::from(u128::from(value)), encoding));
test_cases.extend(big_uint_test_cases);
for (value, expected_encoding) in test_cases {
let mut buffer = Vec::new();
FixedUInt::write(&mut buffer, value.clone())?;
let encoding = buffer.as_slice();
assert_eq!(encoding, expected_encoding, "actual encoding {encoding:x?} was != expected encoding {expected_encoding:x?} for value {value}");
}
Ok(())
}
}