From dd2c587b3537bd076427ac492c9727f5568247de Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 15 Jun 2024 14:08:25 +0200 Subject: [PATCH 1/3] perf: rewrite bit shift implementations --- src/bits.rs | 86 ++++++++++++--------------------------------------- src/macros.rs | 2 ++ 2 files changed, 21 insertions(+), 67 deletions(-) diff --git a/src/bits.rs b/src/bits.rs index 119804a..dde145b 100644 --- a/src/bits.rs +++ b/src/bits.rs @@ -254,53 +254,22 @@ impl Uint { /// the shift is larger than `BITS` (which is IMHO not very useful). #[inline] #[must_use] - pub fn overflowing_shl(mut self, rhs: usize) -> (Self, bool) { + pub fn overflowing_shl(self, rhs: usize) -> (Self, bool) { let (limbs, bits) = (rhs / 64, rhs % 64); if limbs >= LIMBS { return (Self::ZERO, self != Self::ZERO); } - if bits == 0 { - // Check for overflow - let mut overflow = false; - for i in (LIMBS - limbs)..LIMBS { - overflow |= self.limbs[i] != 0; - } - if self.limbs[LIMBS - limbs - 1] > Self::MASK { - overflow = true; - } - - // Shift - for i in (limbs..LIMBS).rev() { - assume!(i >= limbs && i - limbs < LIMBS); - self.limbs[i] = self.limbs[i - limbs]; - } - self.limbs[..limbs].fill(0); - self.limbs[LIMBS - 1] &= Self::MASK; - return (self, overflow); - } - // Check for overflow - let mut overflow = false; - for i in (LIMBS - limbs)..LIMBS { - overflow |= self.limbs[i] != 0; - } - if self.limbs[LIMBS - limbs - 1] >> (64 - bits) != 0 { - overflow = true; + let word_bits = 64; + let mut r = Self::ZERO; + let mut carry = 0; + for i in 0..Self::LIMBS - limbs { + let x = self.limbs[i]; + r.limbs[i + limbs] = (x << bits) | carry; + carry = (x >> (word_bits - bits - 1)) >> 1; } - if self.limbs[LIMBS - limbs - 1] << bits > Self::MASK { - overflow = true; - } - - // Shift - for i in (limbs + 1..LIMBS).rev() { - assume!(i - limbs < LIMBS && i - limbs - 1 < LIMBS); - self.limbs[i] = self.limbs[i - limbs] << bits; - self.limbs[i] |= self.limbs[i - limbs - 1] >> (64 - bits); - } - self.limbs[limbs] = self.limbs[0] << bits; - self.limbs[..limbs].fill(0); - self.limbs[LIMBS - 1] &= Self::MASK; - (self, overflow) + r.limbs[LIMBS - 1] &= Self::MASK; + (r, carry != 0) } /// Left shift by `rhs` bits. @@ -349,38 +318,21 @@ impl Uint { /// the shift is larger than `BITS` (which is IMHO not very useful). #[inline] #[must_use] - pub fn overflowing_shr(mut self, rhs: usize) -> (Self, bool) { + pub fn overflowing_shr(self, rhs: usize) -> (Self, bool) { let (limbs, bits) = (rhs / 64, rhs % 64); if limbs >= LIMBS { return (Self::ZERO, self != Self::ZERO); } - if bits == 0 { - // Check for overflow - let mut overflow = false; - for i in 0..limbs { - overflow |= self.limbs[i] != 0; - } - - // Shift - for i in 0..(LIMBS - limbs) { - self.limbs[i] = self.limbs[i + limbs]; - } - self.limbs[LIMBS - limbs..].fill(0); - return (self, overflow); - } - - // Check for overflow - let overflow = self.limbs[LIMBS - limbs - 1] >> (bits - 1) & 1 != 0; - // Shift - for i in 0..(LIMBS - limbs - 1) { - assume!(i + limbs < LIMBS && i + limbs + 1 < LIMBS); - self.limbs[i] = self.limbs[i + limbs] >> bits; - self.limbs[i] |= self.limbs[i + limbs + 1] << (64 - bits); + let word_bits = 64; + let mut r = Self::ZERO; + let mut carry = 0; + for i in 0..LIMBS - limbs { + let x = self.limbs[LIMBS - 1 - i]; + r.limbs[LIMBS - 1 - i - limbs] = (x >> bits) | carry; + carry = (x << (word_bits - bits - 1)) << 1; } - self.limbs[LIMBS - limbs - 1] = self.limbs[LIMBS - 1] >> bits; - self.limbs[LIMBS - limbs..].fill(0); - (self, overflow) + (r, carry != 0) } /// Right shift by `rhs` bits. diff --git a/src/macros.rs b/src/macros.rs index 8c38daf..92898c2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -75,6 +75,7 @@ macro_rules! impl_bin_op { }; } +#[allow(unused)] macro_rules! assume { ($e:expr $(,)?) => { if !$e { @@ -89,6 +90,7 @@ macro_rules! assume { }; } +#[allow(unused)] macro_rules! debug_unreachable { ($($t:tt)*) => { if cfg!(debug_assertions) { From a7a000a404d88ed07310407d446c7161cacf0af6 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 15 Jun 2024 14:13:02 +0200 Subject: [PATCH 2/3] chore: allow Unicode-3.0 in deny.toml --- deny.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deny.toml b/deny.toml index caba8a0..fb3c29e 100644 --- a/deny.toml +++ b/deny.toml @@ -26,7 +26,8 @@ allow = [ "Unicode-DFS-2016", "Unlicense", "MPL-2.0", - "CC0-1.0" + "CC0-1.0", + "Unicode-3.0", ] [sources] From 38573902a3b0021e5e2f5e62719dafed9d01867f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 15 Jun 2024 15:15:59 +0200 Subject: [PATCH 3/3] perf: simplify serde serialize implementation --- src/support/serde.rs | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/support/serde.rs b/src/support/serde.rs index cd6ba83..c06160c 100644 --- a/src/support/serde.rs +++ b/src/support/serde.rs @@ -39,29 +39,11 @@ impl Uint { } fn serialize_human_minimal(&self, s: S) -> Result { - if BITS == 0 { + if self.is_zero() { return s.serialize_str(ZERO_STR); } - let le_bytes = self.as_le_bytes(); - let mut bytes = le_bytes.iter().rev().skip_while(|b| **b == 0); - - // We avoid String allocation if there is no non-0 byte - // If there is a first byte, we allocate a string, and write the prefix - // and first byte to it - let mut result = match bytes.next() { - Some(b) => { - let mut result = String::with_capacity(2 + nbytes(BITS) * 2); - write!(result, "0x{b:x}").unwrap(); - result - } - None => return s.serialize_str(ZERO_STR), - }; - bytes - .try_for_each(|byte| write!(result, "{byte:02x}")) - .unwrap(); - - s.serialize_str(&result) + s.serialize_str(&format!("{self:#x}")) } fn serialize_binary(&self, s: S) -> Result { @@ -83,7 +65,8 @@ impl Serialize for Uint { } } -/// Deserialize human readable hex strings or byte arrays into hashes. +/// Deserialize human readable hex strings or byte arrays into [`Uint`]. +/// /// Hex strings can be upper/lower/mixed case, have an optional `0x` prefix, and /// can be any length. They are interpreted big-endian. impl<'de, const BITS: usize, const LIMBS: usize> Deserialize<'de> for Uint {