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

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


The following commit(s) were added to refs/heads/main by this push:
     new 58bdc7d557 arrow-buffer: i256: Implement num_traits wrapping shift 
(#9418)
58bdc7d557 is described below

commit 58bdc7d557df323b5c15b5779f8ea23b05bf0ccf
Author: theirix <[email protected]>
AuthorDate: Wed Jun 3 14:18:47 2026 +0100

    arrow-buffer: i256: Implement num_traits wrapping shift (#9418)
    
    # Which issue does this PR close?
    
    - Closes #9417
    
    # Rationale for this change
    
    A follow-up to #8976
    
    Implement some missing traits -
    
[WrappingShl](https://docs.rs/num-traits/latest/num_traits/ops/wrapping/trait.WrappingShl.html)
    and
    
[WrappingShr](https://docs.rs/num-traits/latest/num_traits/ops/wrapping/trait.WrappingShl.html)
    
    # What changes are included in this PR?
    
    - num_traits' WrappingShl implementation  for `usize`
    - `Shl` and `Shr` trait implementation for all scalar numeric types, not
    only for `u8`
    
    # Are these changes tested?
    
    - Unit tests
    
    # Are there any user-facing changes?
    
    <!--
    If there are user-facing changes then we may require documentation to be
    updated before approving the PR.
    
    If there are any breaking changes to public APIs, please call them out.
    -->
---
 arrow-buffer/src/bigint/mod.rs | 71 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 2 deletions(-)

diff --git a/arrow-buffer/src/bigint/mod.rs b/arrow-buffer/src/bigint/mod.rs
index 15faed43a1..396597d7c6 100644
--- a/arrow-buffer/src/bigint/mod.rs
+++ b/arrow-buffer/src/bigint/mod.rs
@@ -20,8 +20,8 @@ use crate::bigint::div::div_rem;
 use num_bigint::BigInt;
 use num_traits::{
     Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, 
CheckedSub, FromPrimitive,
-    Num, One, Signed, ToPrimitive, WrappingAdd, WrappingMul, WrappingNeg, 
WrappingSub, Zero,
-    cast::AsPrimitive,
+    Num, One, Signed, ToPrimitive, WrappingAdd, WrappingMul, WrappingNeg, 
WrappingShl, WrappingShr,
+    WrappingSub, Zero, cast::AsPrimitive,
 };
 use std::cmp::Ordering;
 use std::num::ParseIntError;
@@ -807,6 +807,55 @@ impl Shr<u8> for i256 {
     }
 }
 
+impl WrappingShl for i256 {
+    #[inline]
+    fn wrapping_shl(&self, rhs: u32) -> i256 {
+        // Limit shift to 256 (max valid shift for i256)
+        (*self).shl(rhs as u8)
+    }
+}
+
+impl WrappingShr for i256 {
+    #[inline]
+    fn wrapping_shr(&self, rhs: u32) -> i256 {
+        // Limit shift to 256 (max valid shift for i256)
+        (*self).shr(rhs as u8)
+    }
+}
+
+// Define Shl<T> and Shr<T> for specified integer types using
+// an existing Shl<u8> and Shr<u8> implementation
+macro_rules! define_standard_shift {
+    // Handle multiple types
+    ($trait_name:ident, $method:ident, [$($t:ty),+]) => {
+        $(define_standard_shift!($trait_name, $method, $t);)+
+    };
+    // Handle single type
+    ($trait_name:ident, $method:ident, $t:ty) => {
+        impl $trait_name<$t> for i256 {
+            type Output = i256;
+
+            #[inline]
+            fn $method(self, rhs: $t) -> Self::Output {
+                let rhs = u8::try_from(rhs).expect("rhs overflow for shift");
+                // Other possible overflows are handled by Shl<u8> 
implementation
+                self.$method(rhs)
+            }
+        }
+    };
+}
+
+define_standard_shift!(
+    Shl,
+    shl,
+    [u16, u32, u64, u128, usize, i16, i32, i64, i128, isize]
+);
+define_standard_shift!(
+    Shr,
+    shr,
+    [u16, u32, u64, u128, usize, i16, i32, i64, i128, isize]
+);
+
 macro_rules! define_as_primitive {
     ($native_ty:ty) => {
         impl AsPrimitive<i256> for $native_ty {
@@ -1190,9 +1239,19 @@ mod tests {
             let (expected, _) = i256::from_bigint_with_overflow(bl.clone() << 
shift);
             assert_eq!(actual.to_string(), expected.to_string());
 
+            let wrapping_actual = <i256 as WrappingShl>::wrapping_shl(&il, 
shift as u32);
+            assert_eq!(wrapping_actual.to_string(), expected.to_string());
+
             let actual = il >> shift;
             let (expected, _) = i256::from_bigint_with_overflow(bl.clone() >> 
shift);
             assert_eq!(actual.to_string(), expected.to_string());
+
+            let wrapping_actual = <i256 as WrappingShr>::wrapping_shr(&il, 
shift as u32);
+            assert_eq!(wrapping_actual.to_string(), expected.to_string());
+
+            // Check wrapping of the shift argument
+            let wrapping_actual = <i256 as WrappingShr>::wrapping_shr(&il, 512 
+ shift as u32);
+            assert_eq!(wrapping_actual.to_string(), expected.to_string());
         }
     }
 
@@ -1535,6 +1594,14 @@ mod tests {
         assert_eq!(<i256 as Bounded>::max_value(), i256::MAX);
     }
 
+    #[should_panic]
+    #[test]
+    fn test_shl_panic_on_arg_overflow() {
+        let value = i256::from(123);
+        let rhs = std::hint::black_box(500);
+        let _ = value << rhs;
+    }
+
     #[test]
     fn test_numtraits_from_str_radix() {
         assert_eq!(

Reply via email to