mod big_small;
mod int_data;
use crate::ion_data::{IonDataHash, IonDataOrd, IonEq};
use crate::result::IonFailure;
use crate::types::CountDecimalDigits;
use crate::{IonError, IonResult};
use big_small::AsBigOrSmallValue;
pub(crate) use int_data::{IntData, UIntData};
use num_bigint::BigInt;
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::{Add, Neg};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UInt {
pub(crate) data: UIntData,
}
impl UInt {
pub const ZERO: UInt = UInt {
data: UIntData::ZERO,
};
#[inline]
pub(crate) fn new(data: impl Into<u128>) -> Self {
Self {
data: UIntData::from(data.into()),
}
}
pub(crate) fn from_str_radix(s: &str, radix: u32) -> IonResult<Self> {
let data = UIntData::from_str_radix(s, radix)?;
Ok(Self { data })
}
pub(crate) fn from_be_bytes(bytes: &[u8]) -> UInt {
let data = UIntData::from_be_bytes(bytes);
Self { data }
}
pub fn as_usize(&self) -> Option<usize> {
usize::try_from(self).ok()
}
pub fn as_u64(&self) -> Option<u64> {
u64::try_from(self).ok()
}
pub fn as_u128(&self) -> Option<u128> {
u128::try_from(self).ok()
}
pub fn expect_usize(&self) -> IonResult<usize> {
usize::try_from(self)
.map_err(|_| IonError::decoding_error("UInt was too large to convert to a usize"))
}
pub fn expect_u64(&self) -> IonResult<u64> {
u64::try_from(self)
.map_err(|_| IonError::decoding_error("UInt was too large to convert to a u64"))
}
pub fn expect_u128(&self) -> IonResult<u128> {
u128::try_from(self)
.map_err(|_| IonError::decoding_error("UInt was too large to convert to a u128"))
}
pub fn number_of_decimal_digits(&self) -> u32 {
self.data.count_decimal_digits()
}
pub fn from_le_bytes(bytes: &[u8]) -> UInt {
UInt {
data: UIntData::from_le_bytes(bytes),
}
}
pub fn to_le_bytes(&self) -> Vec<u8> {
self.data.to_le_bytes()
}
pub fn is_zero(&self) -> bool {
self.data == UIntData::ZERO
}
}
impl From<UIntData> for UInt {
fn from(value: UIntData) -> Self {
UInt { data: value }
}
}
macro_rules! impl_uint_from_unsigned_int_types {
($($t:ty),*) => ($(
impl From<$t> for UInt {
#[inline]
fn from(value: $t) -> UInt {
UInt::new(value)
}
}
)*)
}
impl_uint_from_unsigned_int_types!(u8, u16, u32, u64, u128);
impl From<usize> for UInt {
#[inline]
fn from(value: usize) -> Self {
debug_assert!(
mem::size_of::<usize>() <= mem::size_of::<u128>(),
"usize cannot be cast to u128 safely on this platform"
);
UInt::new(value as u128)
}
}
macro_rules! impl_uint_try_from_signed_int_types {
($($t:ty),*) => ($(
impl TryFrom<$t> for UInt {
type Error = IonError;
fn try_from(value: $t) -> Result<Self, Self::Error> {
if value.is_negative() {
return IonResult::decoding_error("cannot convert a negative number to a UInt");
}
Ok(UInt::from(value.unsigned_abs()))
}
}
)*)
}
impl_uint_try_from_signed_int_types!(i8, i16, i32, i64, i128, isize);
macro_rules! impl_int_types_try_from_uint {
($($t:ty),*) => ($(
impl TryFrom<&UInt> for $t {
type Error = IonError;
fn try_from(value: &UInt) -> Result<Self, Self::Error> {
<$t>::try_from(value.clone().data).map_err(|_| {
IonError::decoding_error(
concat!("UInt was too large to fit in a ", stringify!($t))
)
})
}
}
)*)
}
impl_int_types_try_from_uint!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
impl TryFrom<Int> for UInt {
type Error = IonError;
fn try_from(value: Int) -> Result<Self, Self::Error> {
if value.data.is_negative() {
return IonResult::decoding_error("cannot convert negative Int to a UInt");
}
Ok(value.data.unsigned_abs().into())
}
}
impl TryFrom<&Int> for UInt {
type Error = IonError;
fn try_from(value: &Int) -> Result<Self, Self::Error> {
value.clone().try_into()
}
}
impl From<&UInt> for UInt {
fn from(value: &UInt) -> Self {
value.clone()
}
}
impl From<&Int> for Int {
fn from(value: &Int) -> Self {
value.clone()
}
}
macro_rules! impl_small_int_try_from_int {
($($t:ty),*) => ($(
impl TryFrom<Int> for $t {
type Error = IonError;
fn try_from(value: Int) -> Result<Self, Self::Error> {
<$t>::try_from(value.data).map_err(|_| {
IonError::decoding_error(concat!("Int was outside the range of a(n) ", stringify!($t)))
})
}
}
)*)
}
impl_small_int_try_from_int!(i8, i16, i32, i64, i128, isize);
impl_small_int_try_from_int!(u8, u16, u32, u64, u128, usize);
macro_rules! impl_small_unsigned_int_try_from_uint {
($($t:ty),*) => ($(
impl TryFrom<UInt> for $t {
type Error = IonError;
fn try_from(value: UInt) -> Result<Self, Self::Error> {
<$t>::try_from(value.data).map_err(|_| {
IonError::decoding_error(concat!("UInt was outside the range of a(n) ", stringify!($t)))
})
}
}
)*)
}
impl_small_unsigned_int_try_from_uint!(u8, u16, u32, u64, u128, usize);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Int {
pub(crate) data: IntData,
}
impl Int {
pub const ZERO: Int = Int {
data: IntData::ZERO,
};
#[allow(unused)]
pub(crate) fn new(data: impl Into<i128>) -> Self {
Self {
data: IntData::from(data.into()),
}
}
pub fn unsigned_abs(&self) -> UInt {
self.data.unsigned_abs().into()
}
pub fn is_negative(&self) -> bool {
self.data.is_negative()
}
#[inline]
pub fn expect_i64(&self) -> IonResult<i64> {
self.as_i64().ok_or_else(
#[inline(never)]
|| IonError::decoding_error(format!("Int {self} was not in the range of an i64.")),
)
}
#[inline(always)]
pub fn as_u32(&self) -> Option<u32> {
u32::try_from(&self.data).ok()
}
#[inline]
pub fn expect_u32(&self) -> IonResult<u32> {
self.as_u32().ok_or_else(
#[inline(never)]
|| IonError::decoding_error(format!("Int {self} was not in the range of a u32.")),
)
}
#[inline(always)]
pub fn as_u64(&self) -> Option<u64> {
u64::try_from(&self.data).ok()
}
#[inline]
pub fn expect_u64(&self) -> IonResult<u64> {
self.as_u64().ok_or_else(
#[inline(never)]
|| IonError::decoding_error(format!("Int {self} was not in the range of a u64.")),
)
}
#[inline(always)]
pub fn as_usize(&self) -> Option<usize> {
usize::try_from(&self.data).ok()
}
#[inline]
pub fn expect_usize(&self) -> IonResult<usize> {
self.as_usize().ok_or_else(
#[inline(never)]
|| IonError::decoding_error(format!("Int {self} was not in the range of a usize.")),
)
}
pub fn expect_i128(&self) -> IonResult<i128> {
self.as_i128().ok_or_else(|| {
IonError::decoding_error(format!("Int {self} was not in the range of an i128."))
})
}
pub fn as_i64(&self) -> Option<i64> {
i64::try_from(&self.data).ok()
}
pub fn as_i128(&self) -> Option<i128> {
i128::try_from(&self.data).ok()
}
pub fn from_le_signed_bytes(bytes: &[u8]) -> Int {
Int {
data: IntData::from_le_signed_bytes(bytes),
}
}
pub fn to_le_signed_bytes(&self) -> Vec<u8> {
self.data.to_le_bytes()
}
#[cfg_attr(not(feature = "bigdecimal"), allow(dead_code))]
pub(crate) fn to_bigint(&self) -> BigInt {
self.data.as_big_value().into_owned()
}
pub fn is_zero(&self) -> bool {
self.data == IntData::ZERO
}
#[allow(clippy::should_implement_trait)]
pub fn neg(self) -> Self {
self.data.neg().into()
}
pub(crate) fn add(self, rhs: Self) -> Self {
self.data.add(rhs.data).into()
}
}
impl IonEq for Int {
fn ion_eq(&self, other: &Self) -> bool {
self == other
}
}
impl IonDataOrd for Int {
fn ion_cmp(&self, other: &Self) -> Ordering {
self.cmp(other)
}
}
impl IonDataHash for Int {
fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
self.hash(state)
}
}
impl CountDecimalDigits for Int {
fn count_decimal_digits(self) -> u32 {
self.data.count_decimal_digits()
}
}
impl CountDecimalDigits for UInt {
fn count_decimal_digits(self) -> u32 {
self.data.count_decimal_digits()
}
}
impl Display for UInt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.data)
}
}
impl<T> From<T> for Int
where
T: Into<IntData>,
{
fn from(value: T) -> Self {
Self { data: value.into() }
}
}
impl From<UInt> for IntData {
fn from(value: UInt) -> Self {
value.data.into()
}
}
impl From<&UInt> for Int {
fn from(value: &UInt) -> Self {
IntData::from(value.data.clone()).into()
}
}
impl Display for Int {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.data)
}
}
#[cfg(test)]
mod integer_tests {
use std::io::Write;
use super::*;
use crate::types::UInt;
use rstest::*;
use std::cmp::Ordering;
#[test]
fn is_zero() {
assert!(Int::from(0).is_zero());
assert!(Int::from(0i128).is_zero());
assert!(!Int::from(55).is_zero());
assert!(!Int::from(55i128).is_zero());
assert!(!Int::from(-55).is_zero());
assert!(!Int::from(-55i128).is_zero());
}
#[test]
fn zero() {
assert!(Int::ZERO.is_zero());
}
#[rstest]
#[case::i64(5.into(), 4.into(), Ordering::Greater)]
#[case::i64_equal(Int::from(-5), Int::from(-5), Ordering::Equal)]
#[case::i64_gt_big_int(Int::from(4), Int::from(3i128), Ordering::Greater)]
#[case::i64_eq_big_int(Int::from(3), Int::from(3i128), Ordering::Equal)]
#[case::i64_lt_big_int(Int::from(-3), Int::from(5i128), Ordering::Less)]
#[case::big_int(
Int::from(1100i128),
Int::from(-1005i128),
Ordering::Greater
)]
#[case::big_int(Int::from(1100i128), Int::from(1100i128), Ordering::Equal)]
#[case::big_int_lt_i64(Int::from(-9223372036854775809i128), Int::from(0), Ordering::Less)]
#[case::big_int_gt_i64(Int::from(9223372036854775809i128), Int::from(0), Ordering::Greater)]
#[case::i64_gt_big_int_i128(Int::from(0), Int::from(9223372036854775809i128), Ordering::Less)]
#[case::i64_lt_big_int_i128(Int::from(0), Int::from(-9223372036854775809i128), Ordering::Greater)]
fn integer_ordering_tests(#[case] this: Int, #[case] other: Int, #[case] expected: Ordering) {
assert_eq!(this.cmp(&other), expected)
}
#[rstest]
#[case::u64(UInt::from(5u64), UInt::from(4u64), Ordering::Greater)]
#[case::u64_equal(UInt::from(5u64), UInt::from(5u64), Ordering::Equal)]
#[case::u64_gt_big_uint(UInt::from(4u64), UInt::from(3u128), Ordering::Greater)]
#[case::u64_lt_big_uint(UInt::from(3u64), UInt::from(5u128), Ordering::Less)]
#[case::u64_eq_big_uint(UInt::from(3u64), UInt::from(3u128), Ordering::Equal)]
#[case::big_uint(UInt::from(1100u128), UInt::from(1005u128), Ordering::Greater)]
#[case::big_uint(UInt::from(1005u128), UInt::from(1005u128), Ordering::Equal)]
fn unsigned_integer_ordering_tests(
#[case] this: UInt,
#[case] other: UInt,
#[case] expected: Ordering,
) {
assert_eq!(this.cmp(&other), expected)
}
#[rstest]
#[case(UInt::from(1u64), 1)] #[case(UInt::from(0u128), 1)]
#[case(UInt::from(1u128), 1)]
#[case(UInt::from(10u128), 2)]
#[case(UInt::from(3117u128), 4)]
fn uint_decimal_digits_test(#[case] uint: UInt, #[case] expected: u32) {
assert_eq!(uint.number_of_decimal_digits(), expected)
}
#[rstest]
#[case(Int::from(5), "5")]
#[case(Int::from(-5), "-5")]
#[case(Int::from(0), "0")]
#[case(Int::from(1100i128), "1100")]
#[case(Int::from(-1100i128), "-1100")]
fn int_display_test(#[case] value: Int, #[case] expect: String) {
let mut buf = Vec::new();
write!(&mut buf, "{value}").unwrap();
assert_eq!(expect, String::from_utf8(buf).unwrap());
}
#[rstest]
#[case(UInt::from(5u64), "5")]
#[case(UInt::from(0u64), "0")]
#[case(UInt::from(0u128), "0")]
#[case(UInt::from(1100u128), "1100")]
fn uint_display_test(#[case] value: UInt, #[case] expect: String) {
let mut buf = Vec::new();
write!(&mut buf, "{value}").unwrap();
assert_eq!(expect, String::from_utf8(buf).unwrap());
}
#[test]
fn u8_from_uint() {
assert_eq!(u8::try_from(UInt::from(0u64)), Ok(0u8));
assert_eq!(u8::try_from(UInt::from(21u64)), Ok(21u8));
assert_eq!(u8::try_from(UInt::from(255u64)), Ok(255u8));
assert_eq!(u8::try_from(UInt::from(255u128)), Ok(255u8));
assert!(u8::try_from(UInt::from(999u64)).is_err())
}
#[test]
fn u16_from_uint() {
assert_eq!(u16::try_from(UInt::from(0u64)), Ok(0u16));
assert_eq!(u16::try_from(UInt::from(999u64)), Ok(999u16));
assert_eq!(u16::try_from(UInt::from(16500u64)), Ok(16500u16));
assert_eq!(u16::try_from(UInt::from(16500u128)), Ok(16500u16));
assert!(u16::try_from(UInt::from(128_000u64)).is_err())
}
#[test]
fn u32_from_uint() {
assert_eq!(u32::try_from(UInt::from(0u64)), Ok(0u32));
assert_eq!(u32::try_from(UInt::from(16500u64)), Ok(16500u32));
assert_eq!(u32::try_from(UInt::from(128_000u64)), Ok(128_000u32));
assert_eq!(u32::try_from(UInt::from(128_000u128)), Ok(128_000u32));
assert!(u32::try_from(UInt::from(5_000_000_000u64)).is_err())
}
#[test]
fn u64_from_uint() {
assert_eq!(u64::try_from(UInt::from(0u64)), Ok(0u64));
assert_eq!(u64::try_from(UInt::from(128_000u64)), Ok(128_000u64));
assert_eq!(
u64::try_from(UInt::from(5_000_000_000u64)),
Ok(5_000_000_000u64)
);
assert!(u64::try_from(UInt::from(u128::MAX)).is_err())
}
#[test]
fn usize_from_uint() {
assert_eq!(usize::try_from(UInt::from(0u64)), Ok(0usize));
assert_eq!(usize::try_from(UInt::from(16500u64)), Ok(16500usize));
assert_eq!(usize::try_from(UInt::from(128_000u64)), Ok(128_000usize));
assert_eq!(usize::try_from(UInt::from(128_000u128)), Ok(128_000usize));
assert!(usize::try_from(UInt::from(u128::MAX)).is_err())
}
#[test]
fn as_usize() {
assert_eq!(UInt::from(128_000u64).as_usize(), Some(128_000usize));
assert_eq!(UInt::from(128_000u128).as_usize(), Some(128_000usize));
assert!(UInt::from(u128::MAX).as_usize().is_none())
}
#[test]
fn expect_usize() {
assert_eq!(UInt::from(128_000u64).expect_usize(), Ok(128_000usize));
assert_eq!(UInt::from(128_000u128).expect_usize(), Ok(128_000usize));
assert!(UInt::from(u128::MAX).expect_usize().is_err())
}
#[test]
fn as_u64() {
assert_eq!(UInt::from(128_000u64).as_u64(), Some(128_000u64));
assert_eq!(UInt::from(128_000u128).as_u64(), Some(128_000u64));
assert!(UInt::from(u128::MAX).as_u64().is_none())
}
#[test]
fn expect_u64() {
assert_eq!(UInt::from(128_000u64).expect_u64(), Ok(128_000u64));
assert_eq!(UInt::from(128_000u128).expect_u64(), Ok(128_000u64));
assert!(UInt::from(u128::MAX).expect_u64().is_err())
}
#[test]
fn int_as_u64() {
assert_eq!(Int::from(128_000i64).as_u64(), Some(128_000u64));
assert_eq!(Int::from(0i64).as_u64(), Some(0u64));
assert!(Int::from(-1i64).as_u64().is_none());
assert!(Int::from(i128::MAX).as_u64().is_none());
}
#[test]
fn int_expect_u64() {
assert_eq!(Int::from(128_000i64).expect_u64(), Ok(128_000u64));
assert_eq!(Int::from(0i64).expect_u64(), Ok(0u64));
assert!(Int::from(-1i64).expect_u64().is_err());
assert!(Int::from(i128::MAX).expect_u64().is_err());
}
#[test]
fn int_from_signed_bytes_le_big() {
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let big = Int::from_le_signed_bytes(&bytes);
assert!(big.as_i128().is_none());
assert!(!big.is_negative());
assert!(!big.is_zero());
assert_eq!(big.to_string(), "340282366920938463463374607431768211456");
}
#[test]
fn uint_from_bytes_le_big() {
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let big = UInt::from_le_bytes(&bytes);
assert!(big.as_u128().is_none());
assert!(!big.is_zero());
assert_eq!(big.to_string(), "340282366920938463463374607431768211456");
}
#[test]
fn int_neg_big() {
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let big = Int::from_le_signed_bytes(&bytes);
let neg = big.neg();
assert!(neg.is_negative());
assert_eq!(neg.to_string(), "-340282366920938463463374607431768211456");
}
#[test]
fn int_from_bytes_roundtrip() {
for v in [0i128, 1, -1, 42, -42, i128::MAX, i128::MIN] {
let int = Int::from(v);
let bytes = int.data.to_le_bytes();
let roundtripped = Int::from_le_signed_bytes(&bytes);
assert_eq!(int, roundtripped, "roundtrip failed for {v}");
}
}
#[test]
fn int_cross_repr_eq() {
let inline = Int::from(42i64);
let also_inline = Int::from(42i64);
assert_eq!(inline, also_inline);
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let big1 = Int::from_le_signed_bytes(&bytes);
let big2 = Int::from_le_signed_bytes(&bytes);
assert_eq!(big1, big2);
assert_ne!(inline, big1);
}
#[test]
fn int_cross_repr_ord() {
let small = Int::from(42i64);
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let big = Int::from_le_signed_bytes(&bytes);
assert!(small < big);
assert!(big > small);
let neg_big = Int::from_le_signed_bytes(&bytes).neg();
assert!(neg_big < small);
assert!(small > neg_big);
}
#[test]
fn int_cross_repr_hash_consistent() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let hash = |v: &Int| {
let mut h = DefaultHasher::new();
v.hash(&mut h);
h.finish()
};
let a = Int::from(42i64);
let b = Int::from(42i64);
assert_eq!(hash(&a), hash(&b));
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let c = Int::from_le_signed_bytes(&bytes);
let d = Int::from_le_signed_bytes(&bytes);
assert_eq!(hash(&c), hash(&d));
}
#[test]
fn int_ion_eq_and_ion_data_hash() {
use crate::ion_data::{IonDataHash, IonEq};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
let a = Int::from(42i64);
let b = Int::from(42i64);
assert!(a.ion_eq(&b));
let ion_hash = |v: &Int| {
let mut h = DefaultHasher::new();
v.ion_data_hash(&mut h);
h.finish()
};
assert_eq!(ion_hash(&a), ion_hash(&b));
}
#[test]
fn uint_cross_repr_ord() {
let small = UInt::from(42u64);
let mut bytes = vec![0u8; 17];
bytes[16] = 1;
let big = UInt::from_le_bytes(&bytes);
assert!(small < big);
assert!(big > small);
}
}