You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by tu...@apache.org on 2022/11/05 21:38:52 UTC

[arrow-rs] branch master updated: Make various i256 methods const (#3026)

This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 53b7f64d7 Make various i256 methods const (#3026)
53b7f64d7 is described below

commit 53b7f64d7c78d3173055fbf870c67294b80f81ef
Author: Raphael Taylor-Davies <17...@users.noreply.github.com>
AuthorDate: Sun Nov 6 10:38:47 2022 +1300

    Make various i256 methods const (#3026)
---
 arrow-buffer/src/bigint.rs | 76 ++++++++++++++++++++++++++++++----------------
 1 file changed, 50 insertions(+), 26 deletions(-)

diff --git a/arrow-buffer/src/bigint.rs b/arrow-buffer/src/bigint.rs
index 463c63729..e87c05826 100644
--- a/arrow-buffer/src/bigint.rs
+++ b/arrow-buffer/src/bigint.rs
@@ -80,40 +80,36 @@ impl i256 {
 
     /// Create an integer value from its representation as a byte array in little-endian.
     #[inline]
-    pub fn from_le_bytes(b: [u8; 32]) -> Self {
+    pub const fn from_le_bytes(b: [u8; 32]) -> Self {
+        let (low, high) = split_array(b);
         Self {
-            high: i128::from_le_bytes(b[16..32].try_into().unwrap()),
-            low: u128::from_le_bytes(b[0..16].try_into().unwrap()),
+            high: i128::from_le_bytes(high),
+            low: u128::from_le_bytes(low),
         }
     }
 
     /// Create an integer value from its representation as a byte array in little-endian.
     #[inline]
-    pub fn from_be_bytes(b: [u8; 32]) -> Self {
+    pub const fn from_be_bytes(b: [u8; 32]) -> Self {
+        let (high, low) = split_array(b);
         Self {
-            high: i128::from_be_bytes(b[0..16].try_into().unwrap()),
-            low: u128::from_be_bytes(b[16..32].try_into().unwrap()),
+            high: i128::from_be_bytes(high),
+            low: u128::from_be_bytes(low),
         }
     }
 
-    pub fn from_i128(v: i128) -> Self {
-        let mut bytes = if num::Signed::is_negative(&v) {
-            [255_u8; 32]
-        } else {
-            [0; 32]
-        };
-        bytes[0..16].copy_from_slice(&v.to_le_bytes());
-        Self::from_le_bytes(bytes)
+    pub const fn from_i128(v: i128) -> Self {
+        Self::from_parts(v as u128, v >> 127)
     }
 
     /// Create an i256 from the provided low u128 and high i128
     #[inline]
-    pub fn from_parts(low: u128, high: i128) -> Self {
+    pub const fn from_parts(low: u128, high: i128) -> Self {
         Self { low, high }
     }
 
     /// Returns this `i256` as a low u128 and high i128
-    pub fn to_parts(self) -> (u128, i128) {
+    pub const fn to_parts(self) -> (u128, i128) {
         (self.low, self.high)
     }
 
@@ -131,23 +127,31 @@ impl i256 {
 
     /// 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] {
+    pub const fn to_le_bytes(self) -> [u8; 32] {
+        let low = self.low.to_le_bytes();
+        let high = self.high.to_le_bytes();
         let mut t = [0; 32];
-        let t_low: &mut [u8; 16] = (&mut t[0..16]).try_into().unwrap();
-        *t_low = self.low.to_le_bytes();
-        let t_high: &mut [u8; 16] = (&mut t[16..32]).try_into().unwrap();
-        *t_high = self.high.to_le_bytes();
+        let mut i = 0;
+        while i != 16 {
+            t[i] = low[i];
+            t[i + 16] = high[i];
+            i += 1;
+        }
         t
     }
 
     /// Return the memory representation of this integer as a byte array in big-endian byte order.
     #[inline]
-    pub fn to_be_bytes(self) -> [u8; 32] {
+    pub const fn to_be_bytes(self) -> [u8; 32] {
+        let low = self.low.to_be_bytes();
+        let high = self.high.to_be_bytes();
         let mut t = [0; 32];
-        let t_low: &mut [u8; 16] = (&mut t[0..16]).try_into().unwrap();
-        *t_low = self.high.to_be_bytes();
-        let t_high: &mut [u8; 16] = (&mut t[16..32]).try_into().unwrap();
-        *t_high = self.low.to_be_bytes();
+        let mut i = 0;
+        while i != 16 {
+            t[i] = high[i];
+            t[i + 16] = low[i];
+            i += 1;
+        }
         t
     }
 
@@ -369,6 +373,20 @@ impl i256 {
     }
 }
 
+/// Temporary workaround due to lack of stable const array slicing
+/// See <https://github.com/rust-lang/rust/issues/90091>
+const fn split_array(vals: [u8; 32]) -> ([u8; 16], [u8; 16]) {
+    let mut a = [0; 16];
+    let mut b = [0; 16];
+    let mut i = 0;
+    while i != 16 {
+        a[i] = vals[i];
+        b[i] = vals[i + 16];
+        i += 1;
+    }
+    (a, b)
+}
+
 /// Performs an unsigned multiplication of `a * b` returning a tuple of
 /// `(low, high)` where `low` contains the lower 128-bits of the result
 /// and `high` the higher 128-bits
@@ -490,6 +508,12 @@ mod tests {
         // Comparison
         assert_eq!(il.cmp(&ir), bl.cmp(&br), "{} cmp {}", bl, br);
 
+        // Conversions
+        assert_eq!(i256::from_le_bytes(il.to_le_bytes()), il);
+        assert_eq!(i256::from_be_bytes(il.to_be_bytes()), il);
+        assert_eq!(i256::from_le_bytes(ir.to_le_bytes()), ir);
+        assert_eq!(i256::from_be_bytes(ir.to_be_bytes()), ir);
+
         // To i128
         assert_eq!(il.to_i128(), bl.to_i128(), "{}", bl);
         assert_eq!(ir.to_i128(), br.to_i128(), "{}", br);