@@ -6,8 +6,11 @@ mod tests;
66use core:: { fmt, ops} ;
77
88use super :: { DInt , HInt , Int , MinInt } ;
9+ use crate :: support:: Word ;
910
1011const U128_LO_MASK : u128 = u64:: MAX as u128 ;
12+ const U128_WORDS : usize = ( u128:: BITS / Word :: BITS ) as usize ;
13+ const U256_WORDS : usize = U128_WORDS * 2 ;
1114
1215/// A 256-bit unsigned integer represented as two 128-bit native-endian limbs.
1316#[ allow( non_camel_case_types) ]
@@ -31,6 +34,29 @@ impl u256 {
3134 hi : self . hi as i128 ,
3235 }
3336 }
37+
38+ /// Split into words, with the least significant word first.
39+ fn to_words ( self ) -> [ Word ; U256_WORDS ] {
40+ // The result with 64-bit words will be: [lo.lo(), lo.hi(), hi.lo(), hi.hi()].
41+ let mut ret: [ Word ; _] = [ 0 ; U256_WORDS ] ;
42+ for i in 0 ..U128_WORDS {
43+ let shift = i as u32 * Word :: BITS ;
44+ ret[ i] = ( self . lo >> shift) as Word ;
45+ ret[ i + U128_WORDS ] = ( self . hi >> shift) as Word ;
46+ }
47+ ret
48+ }
49+
50+ /// Perform the opposite of [`to_words`]
51+ fn from_words ( words : [ Word ; U256_WORDS ] ) -> Self {
52+ let mut ret = u256:: ZERO ;
53+ for i in 0 ..U128_WORDS {
54+ let shift = i as u32 * usize:: BITS ;
55+ ret. lo |= ( words[ i] as u128 ) << shift;
56+ ret. hi |= ( words[ i + U128_WORDS ] as u128 ) << shift;
57+ }
58+ ret
59+ }
3460}
3561
3662/// A 256-bit signed integer represented as two 128-bit native-endian limbs.
@@ -58,6 +84,26 @@ impl i256 {
5884 hi : self . hi as u128 ,
5985 }
6086 }
87+
88+ fn to_words ( self ) -> [ Word ; U256_WORDS ] {
89+ let mut ret: [ Word ; _] = [ 0 ; U256_WORDS ] ;
90+ for i in 0 ..U128_WORDS {
91+ let shift = i as u32 * Word :: BITS ;
92+ ret[ i] = ( self . lo >> shift) as Word ;
93+ ret[ i + U128_WORDS ] = ( self . hi >> shift) as Word ;
94+ }
95+ ret
96+ }
97+
98+ fn from_words ( words : [ Word ; U256_WORDS ] ) -> Self {
99+ let mut ret = u256:: ZERO ;
100+ for i in 0 ..U128_WORDS {
101+ let shift = i as u32 * usize:: BITS ;
102+ ret. lo |= ( words[ i] as u128 ) << shift;
103+ ret. hi |= ( words[ i + U128_WORDS ] as u128 ) << shift;
104+ }
105+ ret. signed ( )
106+ }
61107}
62108
63109impl MinInt for u256 {
@@ -130,59 +176,95 @@ macro_rules! impl_common {
130176 }
131177 }
132178
133- impl ops:: Shl <u32 > for $ty {
179+ impl ops:: Shr <u32 > for $ty {
134180 type Output = Self ;
135181
136- fn shl( mut self , rhs: u32 ) -> Self :: Output {
137- debug_assert!( rhs < Self :: BITS , "attempt to shift left with overflow" ) ;
138-
139- let half_bits = Self :: BITS / 2 ;
140- let low_mask = half_bits - 1 ;
141- let s = rhs & low_mask;
142-
143- let lo = self . lo;
144- let hi = self . hi;
182+ fn shr( self , rhs: u32 ) -> Self :: Output {
183+ debug_assert!( rhs < Self :: BITS , "attempt to shift right with overflow" ) ;
145184
146- self . lo = lo << s;
185+ // Set up an array with the input in the low half, zeros in the upper half
186+ let mut words: [ Word ; _] = [ 0 ; U256_WORDS * 2 ] ;
187+ words[ ..U256_WORDS ] . copy_from_slice( & self . to_words( ) ) ;
188+
189+ if <$ty>:: SIGNED {
190+ // For i256, branchlessly set the upper words to all ones if the input
191+ // is negative.
192+ let top_word = words[ U256_WORDS - 1 ] . cast_signed( ) >> ( Word :: BITS - 1 ) ;
193+ for x in & mut words[ U256_WORDS ..] {
194+ * x = top_word. cast_unsigned( ) ;
195+ }
196+ }
147197
148- if rhs & half_bits == 0 {
149- self . hi = ( lo >> ( low_mask ^ s) >> 1 ) as _;
150- self . hi |= hi << s;
151- } else {
152- self . hi = self . lo as _;
153- self . lo = 0 ;
198+ let shift = rhs & 255 ; // limit to 255 in cases of overflow
199+ let word_shift = ( shift / Word :: BITS ) as usize ;
200+ let bit_shift = shift % Word :: BITS ;
201+
202+ let mut ret: [ Word ; _] = [ 0 ; U256_WORDS ] ;
203+
204+ // Each output word is a coarse (word-sized) shift plus a small bit shift. Note that
205+ // these loops get unrolled.
206+ for i in 0 ..U256_WORDS {
207+ if i < ( U256_WORDS - 1 ) {
208+ let hi = words[ word_shift + i + 1 ] ;
209+ let lo = words[ word_shift + i] ;
210+
211+ ret[ i] = <Word as HInt >:: funnel_shr( hi, lo, bit_shift) ;
212+ } else if <$ty>:: SIGNED {
213+ // The upper word doesn't get any sign bits via a funnel shift, so we need
214+ // an arithmetic shift to preserve sign.
215+ let mut x = words[ word_shift + i] . cast_signed( ) ;
216+ x >>= bit_shift;
217+ ret[ i] = x. cast_unsigned( ) ;
218+ } else {
219+ ret[ i] = words[ word_shift + i] >> bit_shift;
220+ }
154221 }
155- self
222+
223+ <$ty>:: from_words( ret)
156224 }
157225 }
226+ } ;
227+ }
158228
159- impl ops:: Shr <u32 > for $ty {
160- type Output = Self ;
229+ impl ops:: Shl < u32 > for u256 {
230+ type Output = Self ;
161231
162- fn shr ( mut self , rhs: u32 ) -> Self :: Output {
163- debug_assert!( rhs < Self :: BITS , "attempt to shift right with overflow" ) ;
232+ fn shl ( self , rhs : u32 ) -> Self :: Output {
233+ debug_assert ! ( rhs < Self :: BITS , "attempt to shift left with overflow" ) ;
164234
165- let half_bits = Self :: BITS / 2 ;
166- let low_mask = half_bits - 1 ;
167- let s = rhs & low_mask ;
235+ // Set up an array with the input in the low half, zeros in the upper half
236+ let mut words : [ Word ; _ ] = [ 0 ; U256_WORDS * 2 ] ;
237+ words [ U256_WORDS .. ] . copy_from_slice ( & self . to_words ( ) ) ;
168238
169- let lo = self . lo;
170- let hi = self . hi;
239+ let shift = rhs & 255 ; // limit to 255 in cases of overflow
240+ let word_shift = U256_WORDS - ( shift / Word :: BITS ) as usize ;
241+ let bit_shift = shift % Word :: BITS ;
171242
172- self . hi = hi >> s ;
243+ let mut ret : [ Word ; _ ] = [ 0 ; U256_WORDS ] ;
173244
174- #[ allow( unused_comparisons) ]
175- if rhs & half_bits == 0 {
176- self . lo = ( hi << ( low_mask ^ s) << 1 ) as _;
177- self . lo |= lo >> s;
178- } else {
179- self . lo = self . hi as _;
180- self . hi = if hi < 0 { !0 } else { 0 } ;
181- }
182- self
245+ // Each output word is a coarse (word-sized) shift plus a small bit shift. Note that
246+ // these loops get unrolled.
247+ for i in 0 ..U256_WORDS {
248+ if i == 0 {
249+ ret[ i] = words[ word_shift + i] << bit_shift;
250+ } else {
251+ let hi = words[ word_shift + i] ;
252+ let lo = words[ word_shift + i - 1 ] ;
253+
254+ ret[ i] = <Word as HInt >:: funnel_shl ( hi, lo, bit_shift) ;
183255 }
184256 }
185- } ;
257+
258+ u256:: from_words ( ret)
259+ }
260+ }
261+
262+ impl ops:: Shl < u32 > for i256 {
263+ type Output = Self ;
264+
265+ fn shl ( self , rhs : u32 ) -> Self :: Output {
266+ ( self . unsigned ( ) << rhs) . signed ( )
267+ }
186268}
187269
188270impl_common ! ( i256) ;
0 commit comments