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 {