@@ -4,7 +4,6 @@ use std::cmp;
44use std:: hash:: Hasher ;
55use std:: mem;
66use std:: ptr;
7- use std:: slice;
87
98#[ cfg( test) ]
109mod tests;
@@ -52,46 +51,17 @@ macro_rules! compress {
5251 } } ;
5352}
5453
55- /// Loads an integer of the desired type from a byte stream, in LE order. Uses
56- /// `copy_nonoverlapping` to let the compiler generate the most efficient way
57- /// to load it from a possibly unaligned address.
58- ///
59- /// Unsafe because: unchecked indexing at i..i+size_of(int_ty)
60- macro_rules! load_int_le {
61- ( $buf: expr, $i: expr, $int_ty: ident) => { {
62- debug_assert!( $i + mem:: size_of:: <$int_ty>( ) <= $buf. len( ) ) ;
63- let mut data = 0 as $int_ty;
64- ptr:: copy_nonoverlapping(
65- $buf. get_unchecked( $i) ,
66- & mut data as * mut _ as * mut u8 ,
67- mem:: size_of:: <$int_ty>( ) ,
68- ) ;
69- data. to_le( )
70- } } ;
71- }
72-
73- /// Loads an u64 using up to 7 bytes of a byte slice.
74- ///
75- /// Unsafe because: unchecked indexing at start..start+len
54+ /// Loads up to 8 bytes from a byte-slice into a little-endian u64.
7655#[ inline]
77- unsafe fn u8to64_le ( buf : & [ u8 ] , start : usize , len : usize ) -> u64 {
78- debug_assert ! ( len < 8 ) ;
79- let mut i = 0 ; // current byte index (from LSB) in the output u64
80- let mut out = 0 ;
81- if i + 3 < len {
82- out = u64:: from ( load_int_le ! ( buf, start + i, u32 ) ) ;
83- i += 4 ;
84- }
85- if i + 1 < len {
86- out |= u64:: from ( load_int_le ! ( buf, start + i, u16 ) ) << ( i * 8 ) ;
87- i += 2
88- }
89- if i < len {
90- out |= u64:: from ( * buf. get_unchecked ( start + i) ) << ( i * 8 ) ;
91- i += 1 ;
56+ fn u8to64_le ( buf : & [ u8 ] , start : usize , len : usize ) -> u64 {
57+ assert ! ( len <= 8 && start + len <= buf. len( ) ) ;
58+
59+ let mut out = 0u64 ;
60+ unsafe {
61+ let out_ptr = & mut out as * mut _ as * mut u8 ;
62+ ptr:: copy_nonoverlapping ( buf. as_ptr ( ) . offset ( start as isize ) , out_ptr, len) ;
9263 }
93- debug_assert_eq ! ( i, len) ;
94- out
64+ out. to_le ( )
9565}
9666
9767impl SipHasher128 {
@@ -122,42 +92,76 @@ impl SipHasher128 {
12292 self . state . v1 ^= 0xee ;
12393 }
12494
125- // Specialized write function that is only valid for buffers with len <= 8.
126- // It's used to force inlining of write_u8 and write_usize, those would normally be inlined
127- // except for composite types (that includes slices and str hashing because of delimiter).
128- // Without this extra push the compiler is very reluctant to inline delimiter writes,
129- // degrading performance substantially for the most common use cases.
95+ // A specialized write function for values with size <= 8.
96+ //
97+ // The hashing of multi-byte integers depends on endianness. E.g.:
98+ // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
99+ // - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
100+ //
101+ // This function does the right thing for little-endian hardware. On
102+ // big-endian hardware `x` must be byte-swapped first to give the right
103+ // behaviour. After any byte-swapping, the input must be zero-extended to
104+ // 64-bits. The caller is responsible for the byte-swapping and
105+ // zero-extension.
130106 #[ inline]
131- fn short_write ( & mut self , msg : & [ u8 ] ) {
132- debug_assert ! ( msg. len( ) <= 8 ) ;
133- let length = msg. len ( ) ;
134- self . length += length;
107+ fn short_write < T > ( & mut self , _x : T , x : u64 ) {
108+ let size = mem:: size_of :: < T > ( ) ;
109+ self . length += size;
110+
111+ // The original number must be zero-extended, not sign-extended.
112+ debug_assert ! ( if size < 8 { x >> ( 8 * size) == 0 } else { true } ) ;
135113
114+ // The number of bytes needed to fill `self.tail`.
136115 let needed = 8 - self . ntail ;
137- let fill = cmp:: min ( length, needed) ;
138- if fill == 8 {
139- self . tail = unsafe { load_int_le ! ( msg, 0 , u64 ) } ;
140- } else {
141- self . tail |= unsafe { u8to64_le ( msg, 0 , fill) } << ( 8 * self . ntail ) ;
142- if length < needed {
143- self . ntail += length;
144- return ;
145- }
116+
117+ // SipHash parses the input stream as 8-byte little-endian integers.
118+ // Inputs are put into `self.tail` until 8 bytes of data have been
119+ // collected, and then that word is processed.
120+ //
121+ // For example, imagine that `self.tail` is 0x0000_00EE_DDCC_BBAA,
122+ // `self.ntail` is 5 (because 5 bytes have been put into `self.tail`),
123+ // and `needed` is therefore 3.
124+ //
125+ // - Scenario 1, `self.write_u8(0xFF)`: we have already zero-extended
126+ // the input to 0x0000_0000_0000_00FF. We now left-shift it five
127+ // bytes, giving 0x0000_FF00_0000_0000. We then bitwise-OR that value
128+ // into `self.tail`, resulting in 0x0000_FFEE_DDCC_BBAA.
129+ // (Zero-extension of the original input is critical in this scenario
130+ // because we don't want the high two bytes of `self.tail` to be
131+ // touched by the bitwise-OR.) `self.tail` is not yet full, so we
132+ // return early, after updating `self.ntail` to 6.
133+ //
134+ // - Scenario 2, `self.write_u32(0xIIHH_GGFF)`: we have already
135+ // zero-extended the input to 0x0000_0000_IIHH_GGFF. We now
136+ // left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then
137+ // bitwise-OR that value into `self.tail`, resulting in
138+ // 0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it
139+ // to update `self.state`. (As mentioned above, this assumes a
140+ // little-endian machine; on a big-endian machine we would have
141+ // byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we
142+ // would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into
143+ // `self.tail`).
144+ //
145+ self . tail |= x << ( 8 * self . ntail ) ;
146+ if size < needed {
147+ self . ntail += size;
148+ return ;
146149 }
150+
151+ // `self.tail` is full, process it.
147152 self . state . v3 ^= self . tail ;
148153 Sip24Rounds :: c_rounds ( & mut self . state ) ;
149154 self . state . v0 ^= self . tail ;
150155
151- // Buffered tail is now flushed, process new input.
152- self . ntail = length - needed;
153- self . tail = unsafe { u8to64_le ( msg, needed, self . ntail ) } ;
154- }
155-
156- #[ inline( always) ]
157- fn short_write_gen < T > ( & mut self , x : T ) {
158- let bytes =
159- unsafe { slice:: from_raw_parts ( & x as * const T as * const u8 , mem:: size_of :: < T > ( ) ) } ;
160- self . short_write ( bytes) ;
156+ // Continuing scenario 2: we have one byte left over from the input. We
157+ // set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >>
158+ // 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine
159+ // the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.)
160+ //
161+ // The `if` is needed to avoid shifting by 64 bits, which Rust
162+ // complains about.
163+ self . ntail = size - needed;
164+ self . tail = if needed < 8 { x >> ( 8 * needed) } else { 0 } ;
161165 }
162166
163167 #[ inline]
@@ -182,52 +186,52 @@ impl SipHasher128 {
182186impl Hasher for SipHasher128 {
183187 #[ inline]
184188 fn write_u8 ( & mut self , i : u8 ) {
185- self . short_write_gen ( i ) ;
189+ self . short_write ( i , i as u64 ) ;
186190 }
187191
188192 #[ inline]
189193 fn write_u16 ( & mut self , i : u16 ) {
190- self . short_write_gen ( i ) ;
194+ self . short_write ( i , i . to_le ( ) as u64 ) ;
191195 }
192196
193197 #[ inline]
194198 fn write_u32 ( & mut self , i : u32 ) {
195- self . short_write_gen ( i ) ;
199+ self . short_write ( i , i . to_le ( ) as u64 ) ;
196200 }
197201
198202 #[ inline]
199203 fn write_u64 ( & mut self , i : u64 ) {
200- self . short_write_gen ( i ) ;
204+ self . short_write ( i , i . to_le ( ) as u64 ) ;
201205 }
202206
203207 #[ inline]
204208 fn write_usize ( & mut self , i : usize ) {
205- self . short_write_gen ( i ) ;
209+ self . short_write ( i , i . to_le ( ) as u64 ) ;
206210 }
207211
208212 #[ inline]
209213 fn write_i8 ( & mut self , i : i8 ) {
210- self . short_write_gen ( i ) ;
214+ self . short_write ( i , i as u8 as u64 ) ;
211215 }
212216
213217 #[ inline]
214218 fn write_i16 ( & mut self , i : i16 ) {
215- self . short_write_gen ( i ) ;
219+ self . short_write ( i , ( i as u16 ) . to_le ( ) as u64 ) ;
216220 }
217221
218222 #[ inline]
219223 fn write_i32 ( & mut self , i : i32 ) {
220- self . short_write_gen ( i ) ;
224+ self . short_write ( i , ( i as u32 ) . to_le ( ) as u64 ) ;
221225 }
222226
223227 #[ inline]
224228 fn write_i64 ( & mut self , i : i64 ) {
225- self . short_write_gen ( i ) ;
229+ self . short_write ( i , ( i as u64 ) . to_le ( ) as u64 ) ;
226230 }
227231
228232 #[ inline]
229233 fn write_isize ( & mut self , i : isize ) {
230- self . short_write_gen ( i ) ;
234+ self . short_write ( i , ( i as usize ) . to_le ( ) as u64 ) ;
231235 }
232236
233237 #[ inline]
@@ -239,7 +243,7 @@ impl Hasher for SipHasher128 {
239243
240244 if self . ntail != 0 {
241245 needed = 8 - self . ntail ;
242- self . tail |= unsafe { u8to64_le ( msg, 0 , cmp:: min ( length, needed) ) } << ( 8 * self . ntail ) ;
246+ self . tail |= u8to64_le ( msg, 0 , cmp:: min ( length, needed) ) << ( 8 * self . ntail ) ;
243247 if length < needed {
244248 self . ntail += length;
245249 return ;
@@ -257,7 +261,7 @@ impl Hasher for SipHasher128 {
257261
258262 let mut i = needed;
259263 while i < len - left {
260- let mi = unsafe { load_int_le ! ( msg, i, u64 ) } ;
264+ let mi = u8to64_le ( msg, i, 8 ) ;
261265
262266 self . state . v3 ^= mi;
263267 Sip24Rounds :: c_rounds ( & mut self . state ) ;
@@ -266,7 +270,7 @@ impl Hasher for SipHasher128 {
266270 i += 8 ;
267271 }
268272
269- self . tail = unsafe { u8to64_le ( msg, i, left) } ;
273+ self . tail = u8to64_le ( msg, i, left) ;
270274 self . ntail = left;
271275 }
272276
0 commit comments