]> git.lizzy.rs Git - rust.git/commitdiff
optimized SipHash implementation
authorSean McArthur <sean.monstar@gmail.com>
Mon, 14 Apr 2014 21:43:38 +0000 (14:43 -0700)
committerSean McArthur <sean.monstar@gmail.com>
Tue, 15 Apr 2014 22:57:04 +0000 (15:57 -0700)
work started from @gereeter's PR: https://github.com/mozilla/rust/pull/13114
but adjusted bits

src/libstd/hash/sip.rs

index 63844fcd414b4744a9f56ed1df6e6537f340aece..9afb7124c4bf273ff17b771f060c4f7e380c523e 100644 (file)
 use clone::Clone;
 use container::Container;
 use default::Default;
+use int;
 use io::{IoResult, Writer};
 use iter::Iterator;
 use result::Ok;
 use slice::ImmutableVector;
+use uint;
 
 use super::{Hash, Hasher};
 
@@ -43,7 +45,7 @@ pub struct SipState {
     v1: u64,
     v2: u64,
     v3: u64,
-    tail: [u8, ..8], // unprocessed bytes
+    tail: u64, // unprocessed bytes le
     ntail: uint,  // how many bytes in tail are valid
 }
 
@@ -60,7 +62,17 @@ macro_rules! u8to64_le (
      $buf[4+$i] as u64 << 32 |
      $buf[5+$i] as u64 << 40 |
      $buf[6+$i] as u64 << 48 |
-     $buf[7+$i] as u64 << 56)
+     $buf[7+$i] as u64 << 56);
+    ($buf:expr, $i:expr, $len:expr) =>
+    ({
+        let mut t = 0;
+        let mut out = 0u64;
+        while t < $len {
+            out |= $buf[t+$i] as u64 << t*8;
+            t += 1;
+        }
+        out
+    });
 )
 
 macro_rules! rotl (
@@ -98,7 +110,7 @@ pub fn new_with_keys(key0: u64, key1: u64) -> SipState {
             v1: 0,
             v2: 0,
             v3: 0,
-            tail: [ 0, 0, 0, 0, 0, 0, 0, 0 ],
+            tail: 0,
             ntail: 0,
         };
         state.reset();
@@ -124,15 +136,7 @@ pub fn result(&self) -> u64 {
         let mut v2 = self.v2;
         let mut v3 = self.v3;
 
-        let mut b : u64 = (self.length as u64 & 0xff) << 56;
-
-        if self.ntail > 0 { b |= self.tail[0] as u64 <<  0; }
-        if self.ntail > 1 { b |= self.tail[1] as u64 <<  8; }
-        if self.ntail > 2 { b |= self.tail[2] as u64 << 16; }
-        if self.ntail > 3 { b |= self.tail[3] as u64 << 24; }
-        if self.ntail > 4 { b |= self.tail[4] as u64 << 32; }
-        if self.ntail > 5 { b |= self.tail[5] as u64 << 40; }
-        if self.ntail > 6 { b |= self.tail[6] as u64 << 48; }
+        let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;
 
         v3 ^= b;
         compress!(v0, v1, v2, v3);
@@ -147,8 +151,38 @@ pub fn result(&self) -> u64 {
 
         v0 ^ v1 ^ v2 ^ v3
     }
+
+    #[inline]
+    fn write_le(&mut self, n: u64, size: uint) {
+        self.tail |= n << 8*self.ntail;
+        self.ntail += size;
+
+        if self.ntail >= 8 {
+            let m = self.tail;
+
+            self.v3 ^= m;
+            compress!(self.v0, self.v1, self.v2, self.v3);
+            compress!(self.v0, self.v1, self.v2, self.v3);
+            self.v0 ^= m;
+
+            self.ntail -= 8;
+            if self.ntail == 0 {
+                self.tail = 0;
+            } else {
+                self.tail = n >> 64 - 8*self.ntail;
+            }
+        }
+    }
 }
 
+macro_rules! make_write_le(
+    ($this:expr, $n:expr, $size:expr) => ({
+          $this.write_le($n as u64, $size);
+          $this.length += $size;
+          Ok(())
+    })
+)
+
 impl Writer for SipState {
     #[inline]
     fn write(&mut self, msg: &[u8]) -> IoResult<()> {
@@ -159,24 +193,13 @@ fn write(&mut self, msg: &[u8]) -> IoResult<()> {
 
         if self.ntail != 0 {
             needed = 8 - self.ntail;
-
             if length < needed {
-                let mut t = 0;
-                while t < length {
-                    self.tail[self.ntail+t] = msg[t];
-                    t += 1;
-                }
+                self.tail |= u8to64_le!(msg, 0, length) << 8*self.ntail;
                 self.ntail += length;
                 return Ok(());
             }
 
-            let mut t = 0;
-            while t < needed {
-                self.tail[self.ntail+t] = msg[t];
-                t += 1;
-            }
-
-            let m = u8to64_le!(self.tail, 0);
+            let m = self.tail | u8to64_le!(msg, 0, needed) << 8*self.ntail;
 
             self.v3 ^= m;
             compress!(self.v0, self.v1, self.v2, self.v3);
@@ -203,15 +226,62 @@ fn write(&mut self, msg: &[u8]) -> IoResult<()> {
             i += 8;
         }
 
-        let mut t = 0u;
-        while t < left {
-            self.tail[t] = msg[i+t];
-            t += 1
-        }
+        self.tail = u8to64_le!(msg, i, left);
         self.ntail = left;
 
         Ok(())
     }
+
+    #[inline]
+    fn write_u8(&mut self, n: u8) -> IoResult<()> {
+        make_write_le!(self, n, 1)
+    }
+
+    #[inline]
+    fn write_le_u16(&mut self, n: u16) -> IoResult<()> {
+        make_write_le!(self, n, 2)
+    }
+
+    #[inline]
+    fn write_le_u32(&mut self, n: u32) -> IoResult<()> {
+        make_write_le!(self, n, 4)
+    }
+
+    #[inline]
+    fn write_le_u64(&mut self, n: u64) -> IoResult<()> {
+        make_write_le!(self, n, 8)
+    }
+
+    #[inline]
+    fn write_le_uint(&mut self, n: uint) -> IoResult<()> {
+        make_write_le!(self, n, uint::BYTES)
+    }
+
+    #[inline]
+    fn write_i8(&mut self, n: i8) -> IoResult<()> {
+        make_write_le!(self, n, 1)
+    }
+
+    #[inline]
+    fn write_le_i16(&mut self, n: i16) -> IoResult<()> {
+        make_write_le!(self, n, 2)
+    }
+
+    #[inline]
+    fn write_le_i32(&mut self, n: i32) -> IoResult<()> {
+        make_write_le!(self, n, 4)
+    }
+
+    #[inline]
+    fn write_le_i64(&mut self, n: i64) -> IoResult<()> {
+        make_write_le!(self, n, 8)
+    }
+
+    #[inline]
+    fn write_le_int(&mut self, n: int) -> IoResult<()> {
+        make_write_le!(self, n, int::BYTES)
+    }
+
 }
 
 impl Clone for SipState {
@@ -284,6 +354,8 @@ pub fn hash_with_keys<T: Hash<SipState>>(k0: u64, k1: u64, value: &T) -> u64 {
     state.result()
 }
 
+
+
 #[cfg(test)]
 mod tests {
     extern crate test;
@@ -517,28 +589,57 @@ fn test_hash_no_concat_alias() {
     }
 
     #[bench]
-    fn bench_str(b: &mut Bencher) {
+    fn bench_str_under_8_bytes(b: &mut Bencher) {
         let s = "foo";
         b.iter(|| {
             assert_eq!(hash(&s), 16262950014981195938);
         })
     }
 
+    #[bench]
+    fn bench_str_of_8_bytes(b: &mut Bencher) {
+        let s = "foobar78";
+        b.iter(|| {
+            assert_eq!(hash(&s), 4898293253460910787);
+        })
+    }
+
+    #[bench]
+    fn bench_str_over_8_bytes(b: &mut Bencher) {
+        let s = "foobarbaz0";
+        b.iter(|| {
+            assert_eq!(hash(&s), 10581415515220175264);
+        })
+    }
+
+    #[bench]
+    fn bench_long_str(b: &mut Bencher) {
+        let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor \
+incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \
+exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute \
+irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \
+pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \
+officia deserunt mollit anim id est laborum.";
+        b.iter(|| {
+            assert_eq!(hash(&s), 17717065544121360093);
+        })
+    }
+
+    #[bench]
+    fn bench_u64(b: &mut Bencher) {
+        let u = 16262950014981195938u64;
+        b.iter(|| {
+            assert_eq!(hash(&u), 5254097107239593357);
+        })
+    }
+
+    #[deriving(Hash)]
     struct Compound {
         x: u8,
-        y: u16,
+        y: u64,
         z: ~str,
     }
 
-    impl<S: Writer> Hash<S> for Compound {
-        #[inline]
-        fn hash(&self, state: &mut S) {
-            self.x.hash(state);
-            self.y.hash(state);
-            self.z.hash(state);
-        }
-    }
-
     #[bench]
     fn bench_compound_1(b: &mut Bencher) {
         let compound = Compound {
@@ -547,7 +648,7 @@ fn bench_compound_1(b: &mut Bencher) {
             z: ~"foobarbaz",
         };
         b.iter(|| {
-            assert_eq!(hash(&compound), 3581836382593270478);
+            assert_eq!(hash(&compound), 15783192367317361799);
         })
     }
 }