mirror of
https://gitlab.com/artofrev/smallint.git
synced 2024-12-04 17:11:38 -05:00
ver. 0.2.0: add unsigned integer type; private raw fields
This commit is contained in:
parent
209cd7aa83
commit
5eb006e4c7
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "smallint"
|
name = "smallint"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["artofrev"]
|
authors = ["artofrev"]
|
||||||
description = "A library for optimized arbitrary precision integers."
|
description = "A library for optimized arbitrary precision integers."
|
||||||
|
|
199
src/lib.rs
199
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,
|
//! 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.
|
//! 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")]
|
#[cfg(feature="num-bigint")]
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
|
|
||||||
|
#[cfg(feature="num-bigint")]
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
|
||||||
#[cfg(feature="num-bigint")]
|
#[cfg(feature="num-bigint")]
|
||||||
use num_bigint::Sign;
|
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
|
/// An integer-like type that will store small integers up to `i128` inline. Larger integers are
|
||||||
/// represented as `BigInt` types, which are stored on the heap.
|
/// represented as a slice to a sequence of base 2<sup>32</sup> digits represented as a `*mut u32`.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub enum SmallInt {
|
pub struct SmallInt(SmallIntType);
|
||||||
/// An integer stored inline.
|
|
||||||
|
|
||||||
|
/// 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 2<sup>32</sup> digits represented as a `*mut u32`.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct SmallUint(SmallUintType);
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
enum SmallIntType {
|
||||||
Inline(i128),
|
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))
|
Heap((*mut u32, isize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
enum SmallUintType {
|
||||||
|
Inline(u128),
|
||||||
|
Heap((*mut u32, usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Drop for SmallInt {
|
impl Drop for SmallInt {
|
||||||
fn drop(&mut self) {
|
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 size = usize::try_from(s.abs()).unwrap();
|
||||||
let slice = unsafe { core::slice::from_raw_parts_mut(*r, size) };
|
let slice = unsafe { core::slice::from_raw_parts_mut(*r, size) };
|
||||||
unsafe { std::mem::drop(Box::from_raw(slice)) }
|
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 {
|
macro_rules! int_impl {
|
||||||
($itype:ty) => {
|
($itype:ty, $rt:tt, $rtt:tt, $n:tt) => {
|
||||||
impl From<$itype> for SmallInt {
|
impl From<$itype> for $rt {
|
||||||
fn from(a: $itype) -> Self {
|
fn from(a: $itype) -> Self {
|
||||||
SmallInt::Inline(i128::from(a))
|
Self(<$rtt>::Inline($n::from(a)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<SmallInt> for $itype {
|
impl TryFrom<$rt> for $itype {
|
||||||
|
|
||||||
type Error = SmallIntError;
|
type Error = SmallIntError;
|
||||||
|
|
||||||
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
|
fn try_from(s: $rt) -> Result<Self, Self::Error> {
|
||||||
match s {
|
match s.0 {
|
||||||
SmallInt::Inline(i) => <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError),
|
$rtt::Inline(i) => <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError),
|
||||||
SmallInt::Heap((_, _)) => Err(SmallIntError::ConversionError),
|
$rtt::Heap((_, _)) => Err(SmallIntError::ConversionError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int_impl!(u8);
|
int_impl!(u8, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(u16);
|
int_impl!(u16, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(u32);
|
int_impl!(u32, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(u64);
|
int_impl!(u64, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(i8);
|
int_impl!(i8, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(i16);
|
int_impl!(i16, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(i32);
|
int_impl!(i32, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(i64);
|
int_impl!(i64, SmallInt, SmallIntType, i128);
|
||||||
int_impl!(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<Self, Self::Error> {
|
||||||
|
Ok(Self(SmallUintType::Inline(u128::try_from(a).map_err(|_| SmallIntError::ConversionError)?)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SmallUint> for $itype {
|
||||||
|
|
||||||
|
type Error = SmallIntError;
|
||||||
|
|
||||||
|
fn try_from(s: SmallUint) -> Result<Self, Self::Error> {
|
||||||
|
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<u128> for SmallInt {
|
impl From<u128> for SmallInt {
|
||||||
fn from(a: u128) -> Self {
|
fn from(a: u128) -> Self {
|
||||||
match i128::try_from(a) {
|
match i128::try_from(a) {
|
||||||
Ok(i) => Self::Inline(i),
|
Ok(i) => Self(SmallIntType::Inline(i)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut v = a;
|
let mut v = a;
|
||||||
let mut vec = Vec::with_capacity(4);
|
let mut vec = Vec::with_capacity(4);
|
||||||
|
@ -101,7 +167,7 @@ impl From<u128> for SmallInt {
|
||||||
v >>= 32;
|
v >>= 32;
|
||||||
}
|
}
|
||||||
let mut slice = ManuallyDrop::new(vec.into_boxed_slice());
|
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<SmallInt> for u128 {
|
||||||
type Error = SmallIntError;
|
type Error = SmallIntError;
|
||||||
|
|
||||||
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
|
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
|
||||||
match s {
|
match s.0 {
|
||||||
SmallInt::Inline(i) => u128::try_from(i).map_err(|_| SmallIntError::ConversionError),
|
SmallIntType::Inline(i) => u128::try_from(i).map_err(|_| SmallIntError::ConversionError),
|
||||||
SmallInt::Heap((r, s)) => {
|
SmallIntType::Heap((r, s)) => {
|
||||||
let mut ret: u128 = 0;
|
let mut ret: u128 = 0;
|
||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
let size = usize::try_from(s.abs()).unwrap();
|
let size = usize::try_from(s.abs()).unwrap();
|
||||||
|
@ -133,12 +199,42 @@ impl TryFrom<SmallInt> for u128 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SmallUint> 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<SmallInt> for SmallUint {
|
||||||
|
|
||||||
|
type Error = SmallIntError;
|
||||||
|
|
||||||
|
fn try_from(value: SmallInt) -> Result<Self, Self::Error> {
|
||||||
|
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")]
|
#[cfg(feature="num-bigint")]
|
||||||
impl From<BigInt> for SmallInt {
|
impl From<BigInt> for SmallInt {
|
||||||
fn from(b: BigInt) -> Self {
|
fn from(b: BigInt) -> Self {
|
||||||
match (&b).try_into() {
|
match (&b).try_into() {
|
||||||
Ok(i) => SmallInt::Inline(i),
|
Ok(i) => Self(SmallIntType::Inline(i)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let (sign, vec) = b.to_u32_digits();
|
let (sign, vec) = b.to_u32_digits();
|
||||||
let mut slice = ManuallyDrop::new(vec.into_boxed_slice());
|
let mut slice = ManuallyDrop::new(vec.into_boxed_slice());
|
||||||
|
@ -147,7 +243,7 @@ impl From<BigInt> for SmallInt {
|
||||||
Sign::NoSign => panic!("Shouldn't happen; BigInts which store zero should convert to inline."),
|
Sign::NoSign => panic!("Shouldn't happen; BigInts which store zero should convert to inline."),
|
||||||
Sign::Plus => isize::try_from(slice.len()).unwrap()
|
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<BigInt> for SmallInt {
|
||||||
#[cfg(feature="num-bigint")]
|
#[cfg(feature="num-bigint")]
|
||||||
impl From<SmallInt> for BigInt {
|
impl From<SmallInt> for BigInt {
|
||||||
fn from(s: SmallInt) -> Self {
|
fn from(s: SmallInt) -> Self {
|
||||||
match s {
|
match s.0 {
|
||||||
SmallInt::Inline(i) => Self::from(i),
|
SmallIntType::Inline(i) => Self::from(i),
|
||||||
SmallInt::Heap((r, s)) => {
|
SmallIntType::Heap((r, s)) => {
|
||||||
let size = usize::try_from(s.abs()).unwrap();
|
let size = usize::try_from(s.abs()).unwrap();
|
||||||
let sign = s.signum();
|
let sign = s.signum();
|
||||||
let slice = unsafe { core::slice::from_raw_parts(r, size) };
|
let slice = unsafe { core::slice::from_raw_parts(r, size) };
|
||||||
|
@ -174,6 +270,35 @@ impl From<SmallInt> for BigInt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="num-bigint")]
|
||||||
|
impl From<BigUint> 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<SmallUint> 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)]
|
#[cfg(test)]
|
||||||
mod conversion_tests {
|
mod conversion_tests {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user