@@ -221,35 +221,76 @@ impl<S: Sip> Hasher<S> {
221221 self . ntail = 0 ;
222222 }
223223
224- // Specialized write function that is only valid for buffers with len <= 8.
225- // It's used to force inlining of write_u8 and write_usize, those would normally be inlined
226- // except for composite types (that includes slices and str hashing because of delimiter).
227- // Without this extra push the compiler is very reluctant to inline delimiter writes,
228- // degrading performance substantially for the most common use cases.
224+ // A specialized write function for values with size <= 8.
225+ //
226+ // The hashing of multi-byte integers depends on endianness. E.g.:
227+ // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])`
228+ // - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])`
229+ //
230+ // This function does the right thing for little-endian hardware. On
231+ // big-endian hardware `x` must be byte-swapped first to give the right
232+ // behaviour. After any byte-swapping, the input must be zero-extended to
233+ // 64-bits. The caller is responsible for the byte-swapping and
234+ // zero-extension.
229235 #[ inline]
230- fn short_write ( & mut self , msg : & [ u8 ] ) {
231- debug_assert ! ( msg. len( ) <= 8 ) ;
232- let length = msg. len ( ) ;
233- self . length += length;
236+ fn short_write < T > ( & mut self , _x : T , x : u64 ) {
237+ let size = mem:: size_of :: < T > ( ) ;
238+ self . length += size;
234239
240+ // The original number must be zero-extended, not sign-extended.
241+ debug_assert ! ( if size < 8 { x >> ( 8 * size) == 0 } else { true } ) ;
242+
243+ // The number of bytes needed to fill `self.tail`.
235244 let needed = 8 - self . ntail ;
236- let fill = cmp:: min ( length, needed) ;
237- if fill == 8 {
238- self . tail = unsafe { load_int_le ! ( msg, 0 , u64 ) } ;
239- } else {
240- self . tail |= unsafe { u8to64_le ( msg, 0 , fill) } << ( 8 * self . ntail ) ;
241- if length < needed {
242- self . ntail += length;
243- return ;
244- }
245+
246+ // SipHash parses the input stream as 8-byte little-endian integers.
247+ // Inputs are put into `self.tail` until 8 bytes of data have been
248+ // collected, and then that word is processed.
249+ //
250+ // For example, imagine that `self.tail` is 0x0000_00EE_DDCC_BBAA,
251+ // `self.ntail` is 5 (because 5 bytes have been put into `self.tail`),
252+ // and `needed` is therefore 3.
253+ //
254+ // - Scenario 1, `self.write_u8(0xFF)`: we have already zero-extended
255+ // the input to 0x0000_0000_0000_00FF. We now left-shift it five
256+ // bytes, giving 0x0000_FF00_0000_0000. We then bitwise-OR that value
257+ // into `self.tail`, resulting in 0x0000_FFEE_DDCC_BBAA.
258+ // (Zero-extension of the original input is critical in this scenario
259+ // because we don't want the high two bytes of `self.tail` to be
260+ // touched by the bitwise-OR.) `self.tail` is not yet full, so we
261+ // return early, after updating `self.ntail` to 6.
262+ //
263+ // - Scenario 2, `self.write_u32(0xIIHH_GGFF)`: we have already
264+ // zero-extended the input to 0x0000_0000_IIHH_GGFF. We now
265+ // left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then
266+ // bitwise-OR that value into `self.tail`, resulting in
267+ // 0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it
268+ // to update `self.state`. (As mentioned above, this assumes a
269+ // little-endian machine; on a big-endian machine we would have
270+ // byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we
271+ // would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into
272+ // `self.tail`).
273+ //
274+ self . tail |= x << ( 8 * self . ntail ) ;
275+ if size < needed {
276+ self . ntail += size;
277+ return ;
245278 }
279+
280+ // `self.tail` is full, process it.
246281 self . state . v3 ^= self . tail ;
247282 S :: c_rounds ( & mut self . state ) ;
248283 self . state . v0 ^= self . tail ;
249284
250- // Buffered tail is now flushed, process new input.
251- self . ntail = length - needed;
252- self . tail = unsafe { u8to64_le ( msg, needed, self . ntail ) } ;
285+ // Continuing scenario 2: we have one byte left over from the input. We
286+ // set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >>
287+ // 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine
288+ // the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.)
289+ //
290+ // The `if` is needed to avoid shifting by 64 bits, which Rust
291+ // complains about.
292+ self . ntail = size - needed;
293+ self . tail = if needed < 8 { x >> ( 8 * needed) } else { 0 } ;
253294 }
254295}
255296
@@ -280,19 +321,14 @@ impl super::Hasher for SipHasher13 {
280321}
281322
282323impl < S : Sip > super :: Hasher for Hasher < S > {
283- // see short_write comment for explanation
284324 #[ inline]
285- fn write_usize ( & mut self , i : usize ) {
286- let bytes = unsafe {
287- crate :: slice:: from_raw_parts ( & i as * const usize as * const u8 , mem:: size_of :: < usize > ( ) )
288- } ;
289- self . short_write ( bytes) ;
325+ fn write_u8 ( & mut self , i : u8 ) {
326+ self . short_write ( i, i as u64 ) ;
290327 }
291328
292- // see short_write comment for explanation
293329 #[ inline]
294- fn write_u8 ( & mut self , i : u8 ) {
295- self . short_write ( & [ i ] ) ;
330+ fn write_usize ( & mut self , i : usize ) {
331+ self . short_write ( i , i . to_le ( ) as u64 ) ;
296332 }
297333
298334 #[ inline]
0 commit comments