Skip to content

Commit

Permalink
Store as parts
Browse files Browse the repository at this point in the history
  • Loading branch information
tustvold committed Sep 27, 2022
1 parent dad5d16 commit 6a8b52b
Showing 1 changed file with 60 additions and 63 deletions.
123 changes: 60 additions & 63 deletions arrow-buffer/src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ use std::cmp::Ordering;
/// A signed 256-bit integer
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Hash)]
pub struct i256([u8; 32]);
pub struct i256 {
low: u128,
high: i128,
}

impl std::fmt::Debug for i256 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand All @@ -31,7 +34,7 @@ impl std::fmt::Debug for i256 {

impl std::fmt::Display for i256 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", BigInt::from_signed_bytes_le(&self.0))
write!(f, "{}", BigInt::from_signed_bytes_le(&self.to_le_bytes()))
}
}

Expand All @@ -45,43 +48,47 @@ impl Ord for i256 {
fn cmp(&self, other: &Self) -> Ordering {
// This is 25x faster than using a variable length encoding such
// as BigInt as it avoids allocation and branching
let (left_low, left_high) = self.as_parts();
let (right_low, right_high) = other.as_parts();
left_high.cmp(&right_high).then(left_low.cmp(&right_low))
self.high.cmp(&other.high).then(self.low.cmp(&other.low))
}
}

impl i256 {
/// The additive identity for this integer type, i.e. `0`.
pub const ZERO: Self = i256 { low: 0, high: 0 };

/// The multiplicative identity for this integer type, i.e. `1`.
pub const ONE: Self = i256 { low: 1, high: 0 };

/// The multiplicative inverse for this integer type, i.e. `-1`.
pub const MINUS_ONE: Self = i256 {
low: u128::MAX,
high: -1,
};

/// Create an integer value from its representation as a byte array in little-endian.
#[inline]
pub fn from_le_bytes(b: [u8; 32]) -> Self {
Self(b)
}

/// Return the memory representation of this integer as a byte array in little-endian byte order.
#[inline]
pub fn to_le_bytes(self) -> [u8; 32] {
self.0
Self {
high: i128::from_le_bytes(b[16..32].try_into().unwrap()),
low: u128::from_le_bytes(b[0..16].try_into().unwrap()),
}
}

/// Returns this i256 as a low u128 and high i128
/// Create an i256 from the provided low u128 and high i128
#[inline]
fn as_parts(self) -> (u128, i128) {
(
u128::from_le_bytes(self.0[0..16].try_into().unwrap()),
i128::from_le_bytes(self.0[16..32].try_into().unwrap()),
)
fn from_parts(low: u128, high: i128) -> Self {
Self { low, high }
}

/// Create an i256 from the provided low u128 and high i128
/// Return the memory representation of this integer as a byte array in little-endian byte order.
#[inline]
fn from_parts(low: u128, high: i128) -> Self {
pub fn to_le_bytes(self) -> [u8; 32] {
let mut t = [0; 32];
let t_low: &mut [u8; 16] = (&mut t[0..16]).try_into().unwrap();
*t_low = low.to_le_bytes();
*t_low = self.low.to_le_bytes();
let t_high: &mut [u8; 16] = (&mut t[16..32]).try_into().unwrap();
*t_high = high.to_le_bytes();
Self(t)
*t_high = self.high.to_le_bytes();
t
}

/// Create an i256 from the provided [`BigInt`] returning a bool indicating
Expand All @@ -96,108 +103,98 @@ impl i256 {
[0; 32]
};
bytes[0..v_bytes.len()].copy_from_slice(&v_bytes[..v_bytes.len()]);
(Self(bytes), false)
(Self::from_le_bytes(bytes), false)
}
Ordering::Equal => (Self::from_le_bytes(v_bytes.try_into().unwrap()), false),
Ordering::Greater => {
(Self::from_le_bytes(v_bytes[..32].try_into().unwrap()), true)
}
Ordering::Equal => (Self(v_bytes.try_into().unwrap()), false),
Ordering::Greater => (Self(v_bytes[..32].try_into().unwrap()), true),
}
}

/// Performs wrapping addition
#[inline]
pub fn wrapping_add(self, other: Self) -> Self {
let (left_low, left_high) = self.as_parts();
let (right_low, right_high) = other.as_parts();

let (low, carry) = left_low.overflowing_add(right_low);
let high = left_high.wrapping_add(right_high).wrapping_add(carry as _);
Self::from_parts(low, high)
let (low, carry) = self.low.overflowing_add(other.low);
let high = self.high.wrapping_add(other.high).wrapping_add(carry as _);
Self { low, high }
}

/// Performs checked addition
#[inline]
pub fn checked_add(self, other: Self) -> Option<Self> {
let (left_low, left_high) = self.as_parts();
let (right_low, right_high) = other.as_parts();

let (low, carry) = left_low.overflowing_add(right_low);
let high = left_high.checked_add(right_high)?.checked_add(carry as _)?;
Some(Self::from_parts(low, high))
let (low, carry) = self.low.overflowing_add(other.low);
let high = self.high.checked_add(other.high)?.checked_add(carry as _)?;
Some(Self { low, high })
}

/// Performs wrapping subtraction
#[inline]
pub fn wrapping_sub(self, other: Self) -> Self {
let (left_low, left_high) = self.as_parts();
let (right_low, right_high) = other.as_parts();

let (low, carry) = left_low.overflowing_sub(right_low);
let high = left_high.wrapping_sub(right_high).wrapping_sub(carry as _);
Self::from_parts(low, high)
let (low, carry) = self.low.overflowing_sub(other.low);
let high = self.high.wrapping_sub(other.high).wrapping_sub(carry as _);
Self { low, high }
}

/// Performs checked subtraction
#[inline]
pub fn checked_sub(self, other: Self) -> Option<Self> {
let (left_low, left_high) = self.as_parts();
let (right_low, right_high) = other.as_parts();

let (low, carry) = left_low.overflowing_sub(right_low);
let high = left_high.checked_sub(right_high)?.checked_sub(carry as _)?;
Some(Self::from_parts(low, high))
let (low, carry) = self.low.overflowing_sub(other.low);
let high = self.high.checked_sub(other.high)?.checked_sub(carry as _)?;
Some(Self { low, high })
}

/// Performs wrapping multiplication
#[inline]
pub fn wrapping_mul(self, other: Self) -> Self {
let l = BigInt::from_signed_bytes_le(&self.0);
let r = BigInt::from_signed_bytes_le(&other.0);
let l = BigInt::from_signed_bytes_le(&self.to_le_bytes());
let r = BigInt::from_signed_bytes_le(&other.to_le_bytes());
Self::from_bigint_with_overflow(l * r).0
}

/// Performs checked multiplication
#[inline]
pub fn checked_mul(self, other: Self) -> Option<Self> {
let l = BigInt::from_signed_bytes_le(&self.0);
let r = BigInt::from_signed_bytes_le(&other.0);
let l = BigInt::from_signed_bytes_le(&self.to_le_bytes());
let r = BigInt::from_signed_bytes_le(&other.to_le_bytes());
let (val, overflow) = Self::from_bigint_with_overflow(l * r);
(!overflow).then(|| val)
}

/// Performs wrapping division
#[inline]
pub fn wrapping_div(self, other: Self) -> Self {
let l = BigInt::from_signed_bytes_le(&self.0);
let r = BigInt::from_signed_bytes_le(&other.0);
let l = BigInt::from_signed_bytes_le(&self.to_le_bytes());
let r = BigInt::from_signed_bytes_le(&other.to_le_bytes());
Self::from_bigint_with_overflow(l / r).0
}

/// Performs checked division
#[inline]
pub fn checked_div(self, other: Self) -> Option<Self> {
let l = BigInt::from_signed_bytes_le(&self.0);
let r = BigInt::from_signed_bytes_le(&other.0);
let l = BigInt::from_signed_bytes_le(&self.to_le_bytes());
let r = BigInt::from_signed_bytes_le(&other.to_le_bytes());
let (val, overflow) = Self::from_bigint_with_overflow(l / r);
(!overflow).then(|| val)
}

/// Performs wrapping remainder
#[inline]
pub fn wrapping_rem(self, other: Self) -> Self {
let l = BigInt::from_signed_bytes_le(&self.0);
let r = BigInt::from_signed_bytes_le(&other.0);
let l = BigInt::from_signed_bytes_le(&self.to_le_bytes());
let r = BigInt::from_signed_bytes_le(&other.to_le_bytes());
Self::from_bigint_with_overflow(l % r).0
}

/// Performs checked remainder
#[inline]
pub fn checked_rem(self, other: Self) -> Option<Self> {
if other.0 == [0; 32] {
if other == Self::ZERO {
return None;
}

let l = BigInt::from_signed_bytes_le(&self.0);
let r = BigInt::from_signed_bytes_le(&other.0);
let l = BigInt::from_signed_bytes_le(&self.to_le_bytes());
let r = BigInt::from_signed_bytes_le(&other.to_le_bytes());
let (val, overflow) = Self::from_bigint_with_overflow(l % r);
(!overflow).then(|| val)
}
Expand Down

0 comments on commit 6a8b52b

Please sign in to comment.