mirror of
https://gitlab.com/artofrev/smallint.git
synced 2024-12-04 17:11:38 -05:00
conversions
This commit is contained in:
commit
c5e952d7d7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "smallint"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
num-bigint = "0.4.3"
|
188
src/lib.rs
Normal file
188
src/lib.rs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
//! A crate for small integer optimization. Provides the [`SmallInt`] type. When possible this will
|
||||||
|
//! 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.
|
||||||
|
|
||||||
|
use num_bigint::BigInt;
|
||||||
|
use num_bigint::Sign;
|
||||||
|
use core::mem::ManuallyDrop;
|
||||||
|
|
||||||
|
/// An error that occurred when processing a SmallInt.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum SmallIntError {
|
||||||
|
/// Conversion error when converting from SmallInt to other integer types.
|
||||||
|
ConversionError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for SmallIntError {}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SmallIntError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let Self::ConversionError = self;
|
||||||
|
write!(f, "Error converting integer to SmallInt.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub enum SmallInt {
|
||||||
|
/// An integer stored inline.
|
||||||
|
Inline(i128),
|
||||||
|
/// A larger integer stored on the heap. This value is represented in base 2<sup>32</sup> 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! int_impl {
|
||||||
|
($itype:ty) => {
|
||||||
|
impl From<$itype> for SmallInt {
|
||||||
|
fn from(a: $itype) -> Self {
|
||||||
|
SmallInt::Inline(i128::from(a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SmallInt> for $itype {
|
||||||
|
|
||||||
|
type Error = SmallIntError;
|
||||||
|
|
||||||
|
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
|
||||||
|
match s {
|
||||||
|
SmallInt::Inline(i) => <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError),
|
||||||
|
SmallInt::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);
|
||||||
|
|
||||||
|
|
||||||
|
impl From<u128> for SmallInt {
|
||||||
|
fn from(a: u128) -> Self {
|
||||||
|
match i128::try_from(a) {
|
||||||
|
Ok(i) => Self::Inline(i),
|
||||||
|
Err(_) => {
|
||||||
|
let mut v = a;
|
||||||
|
let mut vec = Vec::with_capacity(4);
|
||||||
|
while v != 0 {
|
||||||
|
vec.push(v as u32);
|
||||||
|
|
||||||
|
v = (v >> 1) >> (32 - 1);
|
||||||
|
}
|
||||||
|
let mut slice = ManuallyDrop::new(vec.into_boxed_slice());
|
||||||
|
SmallInt::Heap((slice.as_mut_ptr(), isize::try_from(slice.len()).unwrap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SmallInt> for u128 {
|
||||||
|
|
||||||
|
type Error = SmallIntError;
|
||||||
|
|
||||||
|
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
|
||||||
|
match s {
|
||||||
|
SmallInt::Inline(i) => u128::try_from(i).map_err(|_| SmallIntError::ConversionError),
|
||||||
|
SmallInt::Heap((r, s)) => {
|
||||||
|
let mut ret: u128 = 0;
|
||||||
|
let mut bits = 0;
|
||||||
|
let size = usize::try_from(s.abs()).unwrap();
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts(r, size) };
|
||||||
|
for i in slice {
|
||||||
|
if bits >= 128 {
|
||||||
|
return Err(SmallIntError::ConversionError);
|
||||||
|
}
|
||||||
|
ret |= u128::from(*i) << bits;
|
||||||
|
bits += 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl From<BigInt> for SmallInt {
|
||||||
|
fn from(b: BigInt) -> Self {
|
||||||
|
match (&b).try_into() {
|
||||||
|
Ok(i) => SmallInt::Inline(i),
|
||||||
|
Err(_) => {
|
||||||
|
let (sign, vec) = b.to_u32_digits();
|
||||||
|
let mut slice = ManuallyDrop::new(vec.into_boxed_slice());
|
||||||
|
let size = match sign {
|
||||||
|
Sign::Minus => -isize::try_from(slice.len()).unwrap(),
|
||||||
|
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)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SmallInt> for BigInt {
|
||||||
|
|
||||||
|
|
||||||
|
fn from(s: SmallInt) -> Self {
|
||||||
|
match s {
|
||||||
|
SmallInt::Inline(i) => Self::from(i),
|
||||||
|
SmallInt::Heap((r, s)) => {
|
||||||
|
let size = usize::try_from(s.abs()).unwrap();
|
||||||
|
let sign = s.signum();
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts(r, size) };
|
||||||
|
let bs = match sign {
|
||||||
|
0 => Sign::NoSign,
|
||||||
|
-1 => Sign::Minus,
|
||||||
|
1 => Sign::Plus,
|
||||||
|
_ => panic!("This shouldn't happen; There are only 3 signums."),
|
||||||
|
};
|
||||||
|
|
||||||
|
BigInt::new(bs, slice.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod conversion_tests {
|
||||||
|
|
||||||
|
use crate::SmallInt;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_u8() {
|
||||||
|
let i = u8::MAX;
|
||||||
|
let s = SmallInt::from(i);
|
||||||
|
assert_eq!(u8::try_from(s).unwrap(), i);
|
||||||
|
|
||||||
|
let i = u8::MIN;
|
||||||
|
let s = SmallInt::from(i);
|
||||||
|
assert_eq!(u8::try_from(s).unwrap(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_i8() {
|
||||||
|
let i = i8::MIN;
|
||||||
|
let s = SmallInt::from(i);
|
||||||
|
assert_eq!(i8::try_from(s).unwrap(), i);
|
||||||
|
|
||||||
|
let i = i8::MAX;
|
||||||
|
let s = SmallInt::from(i);
|
||||||
|
assert_eq!(i8::try_from(s).unwrap(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user