From 5eb006e4c7c42f673222291e340575785d07da34 Mon Sep 17 00:00:00 2001 From: EvilMuffinHa Date: Tue, 2 Aug 2022 23:33:31 -0400 Subject: [PATCH] ver. 0.2.0: add unsigned integer type; private raw fields --- Cargo.toml | 2 +- src/lib.rs | 199 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 163 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 469d270..1a8c81e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smallint" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["artofrev"] description = "A library for optimized arbitrary precision integers." diff --git a/src/lib.rs b/src/lib.rs index a439ae5..1e20fa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,9 +5,17 @@ //! inline an integer and store it on the stack if that integer is small. However, for larger values, //! this will be instead stored on the heap as a pointer to a `u32` slice, a length, and a sign. + +// Invariant: If a small integer is within the bounds of an inline value, it must be inline. +// Invariant: If a small integer is on the heap, the size is the minimum digits required to +// represent it. + #[cfg(feature="num-bigint")] use num_bigint::BigInt; +#[cfg(feature="num-bigint")] +use num_bigint::BigUint; + #[cfg(feature="num-bigint")] use num_bigint::Sign; @@ -30,23 +38,34 @@ impl core::fmt::Display for SmallIntError { } -/// An integer-like type that will store small integers up to `i64` inline. Larger integers are -/// represented as `BigInt` types, which are stored on the heap. +/// An integer-like type that will store small integers up to `i128` inline. Larger integers are +/// represented as a slice to a sequence of base 232 digits represented as a `*mut u32`. #[derive(Clone, PartialEq, Eq)] -pub enum SmallInt { - /// An integer stored inline. +pub struct SmallInt(SmallIntType); + + +/// An integer-like type that will store small integers up to `u128` inline. Larger integers are +/// represented as a slice to a sequence of base 232 digits represented as a `*mut u32`. +#[derive(Clone, PartialEq, Eq)] +pub struct SmallUint(SmallUintType); + + +#[derive(Clone, PartialEq, Eq)] +enum SmallIntType { Inline(i128), - /// A larger integer stored on the heap. This value is represented in base 232 and - /// ordered least significant digit first. Also stored with the size and sign of the integer. - /// - /// It is assumed that values stored on the heap are larger than the maximum value of inline - /// integers. Heap((*mut u32, isize)) } +#[derive(Clone, PartialEq, Eq)] +enum SmallUintType { + Inline(u128), + Heap((*mut u32, usize)) +} + + impl Drop for SmallInt { fn drop(&mut self) { - if let Self::Heap((r, s)) = self { + if let Self(SmallIntType::Heap((r, s))) = self { let size = usize::try_from(s.abs()).unwrap(); let slice = unsafe { core::slice::from_raw_parts_mut(*r, size) }; unsafe { std::mem::drop(Box::from_raw(slice)) } @@ -55,43 +74,90 @@ impl Drop for SmallInt { } +impl Drop for SmallUint { + fn drop(&mut self) { + if let Self(SmallUintType::Heap((r, s))) = self { + let slice = unsafe { core::slice::from_raw_parts_mut(*r, *s) }; + unsafe { std::mem::drop(Box::from_raw(slice)) } + } + } + +} + macro_rules! int_impl { - ($itype:ty) => { - impl From<$itype> for SmallInt { + ($itype:ty, $rt:tt, $rtt:tt, $n:tt) => { + impl From<$itype> for $rt { fn from(a: $itype) -> Self { - SmallInt::Inline(i128::from(a)) + Self(<$rtt>::Inline($n::from(a))) } } - impl TryFrom for $itype { + impl TryFrom<$rt> for $itype { type Error = SmallIntError; - fn try_from(s: SmallInt) -> Result { - match s { - SmallInt::Inline(i) => <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError), - SmallInt::Heap((_, _)) => Err(SmallIntError::ConversionError), + fn try_from(s: $rt) -> Result { + match s.0 { + $rtt::Inline(i) => <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError), + $rtt::Heap((_, _)) => Err(SmallIntError::ConversionError), } } } } } -int_impl!(u8); -int_impl!(u16); -int_impl!(u32); -int_impl!(u64); -int_impl!(i8); -int_impl!(i16); -int_impl!(i32); -int_impl!(i64); -int_impl!(i128); +int_impl!(u8, SmallInt, SmallIntType, i128); +int_impl!(u16, SmallInt, SmallIntType, i128); +int_impl!(u32, SmallInt, SmallIntType, i128); +int_impl!(u64, SmallInt, SmallIntType, i128); +int_impl!(i8, SmallInt, SmallIntType, i128); +int_impl!(i16, SmallInt, SmallIntType, i128); +int_impl!(i32, SmallInt, SmallIntType, i128); +int_impl!(i64, SmallInt, SmallIntType, i128); +int_impl!(i128, SmallInt, SmallIntType, i128); + +int_impl!(u8, SmallUint, SmallUintType, u128); +int_impl!(u16, SmallUint, SmallUintType, u128); +int_impl!(u32, SmallUint, SmallUintType, u128); +int_impl!(u64, SmallUint, SmallUintType, u128); +int_impl!(u128, SmallUint, SmallUintType, u128); + +macro_rules! try_from_itou { + ($itype:ty) => { + impl TryFrom<$itype> for SmallUint { + + type Error = SmallIntError; + + fn try_from(a: $itype) -> Result { + Ok(Self(SmallUintType::Inline(u128::try_from(a).map_err(|_| SmallIntError::ConversionError)?))) + } + } + + impl TryFrom for $itype { + + type Error = SmallIntError; + + fn try_from(s: SmallUint) -> Result { + match s.0 { + SmallUintType::Inline(i) => <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError), + SmallUintType::Heap((_, _)) => Err(SmallIntError::ConversionError), + } + } + } + } +} + +try_from_itou!(i8); +try_from_itou!(i16); +try_from_itou!(i32); +try_from_itou!(i64); +try_from_itou!(i128); impl From for SmallInt { fn from(a: u128) -> Self { match i128::try_from(a) { - Ok(i) => Self::Inline(i), + Ok(i) => Self(SmallIntType::Inline(i)), Err(_) => { let mut v = a; let mut vec = Vec::with_capacity(4); @@ -101,7 +167,7 @@ impl From for SmallInt { v >>= 32; } let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); - SmallInt::Heap((slice.as_mut_ptr(), isize::try_from(slice.len()).unwrap())) + Self(SmallIntType::Heap((slice.as_mut_ptr(), isize::try_from(slice.len()).unwrap()))) } } } @@ -112,9 +178,9 @@ impl TryFrom for u128 { type Error = SmallIntError; fn try_from(s: SmallInt) -> Result { - match s { - SmallInt::Inline(i) => u128::try_from(i).map_err(|_| SmallIntError::ConversionError), - SmallInt::Heap((r, s)) => { + match s.0 { + SmallIntType::Inline(i) => u128::try_from(i).map_err(|_| SmallIntError::ConversionError), + SmallIntType::Heap((r, s)) => { let mut ret: u128 = 0; let mut bits = 0; let size = usize::try_from(s.abs()).unwrap(); @@ -133,12 +199,42 @@ impl TryFrom for u128 { } } +impl From for SmallInt { + fn from(s: SmallUint) -> Self { + match s.0 { + SmallUintType::Inline(i) => SmallInt::from(i), + SmallUintType::Heap((r, s)) => SmallInt(SmallIntType::Heap((r, isize::try_from(s).unwrap()))) + } + + } +} + +impl TryFrom for SmallUint { + + type Error = SmallIntError; + + fn try_from(value: SmallInt) -> Result { + match value.0 { + SmallIntType::Inline(i) => Self::try_from(i), + SmallIntType::Heap((r, s)) => { + let size = usize::try_from(s).map_err(|_| SmallIntError::ConversionError)?; + if size > 4 { + Ok(Self(SmallUintType::Heap((r, size)))) + } else { + Ok(Self(SmallUintType::Inline(u128::try_from(value)?))) + } + } + } + } + +} + #[cfg(feature="num-bigint")] impl From for SmallInt { fn from(b: BigInt) -> Self { match (&b).try_into() { - Ok(i) => SmallInt::Inline(i), + Ok(i) => Self(SmallIntType::Inline(i)), Err(_) => { let (sign, vec) = b.to_u32_digits(); let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); @@ -147,7 +243,7 @@ impl From for SmallInt { Sign::NoSign => panic!("Shouldn't happen; BigInts which store zero should convert to inline."), Sign::Plus => isize::try_from(slice.len()).unwrap() }; - SmallInt::Heap((slice.as_mut_ptr(), size)) } + Self(SmallIntType::Heap((slice.as_mut_ptr(), size))) } } } } @@ -155,9 +251,9 @@ impl From for SmallInt { #[cfg(feature="num-bigint")] impl From for BigInt { fn from(s: SmallInt) -> Self { - match s { - SmallInt::Inline(i) => Self::from(i), - SmallInt::Heap((r, s)) => { + match s.0 { + SmallIntType::Inline(i) => Self::from(i), + SmallIntType::Heap((r, s)) => { let size = usize::try_from(s.abs()).unwrap(); let sign = s.signum(); let slice = unsafe { core::slice::from_raw_parts(r, size) }; @@ -174,6 +270,35 @@ impl From for BigInt { } } +#[cfg(feature="num-bigint")] +impl From for SmallUint { + fn from(b: BigUint) -> Self { + match (&b).try_into() { + Ok(i) => Self(SmallUintType::Inline(i)), + Err(_) => { + let vec = b.to_u32_digits(); + let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); + let size = slice.len(); + Self(SmallUintType::Heap((slice.as_mut_ptr(), size))) } + } + } +} + +#[cfg(feature="num-bigint")] +impl From for BigUint { + fn from(s: SmallUint) -> Self { + match s.0 { + SmallUintType::Inline(i) => Self::from(i), + SmallUintType::Heap((r, s)) => { + let slice = unsafe { core::slice::from_raw_parts(r, s) }; + + BigUint::new(slice.to_vec()) + } + } + } +} + + #[cfg(test)] mod conversion_tests {