diff --git a/Data/Hashable.hs b/Data/Hashable.hs
--- a/Data/Hashable.hs
+++ b/Data/Hashable.hs
@@ -232,11 +232,7 @@
 -- inputs to force an application into unexpectedly behaving with
 -- quadratic time complexity.
 --
--- This library uses the SipHash algorithm to hash strings. SipHash
--- was designed to be more robust against collision attacks than
--- traditional hash algorithms, while retaining good performance.
---
--- To further mitigate the risk from collision attacks, this library
+-- To mitigate the risk from collision attacks, this library
 -- provides an environment variable named @HASHABLE_SALT@ that allows
 -- the default salt used by the 'hash' function to be chosen at
 -- application startup time.
@@ -260,7 +256,7 @@
 -- call to 'hash' is made, the application will halt with an
 -- informative error message.
 --
--- (Implementation note: while SipHash is used for strings, a
--- faster&#8212;and almost certainly less secure&#8212;algorithm is
--- used for numeric types, on the assumption that strings are much
--- more likely as a hash DoS attack vector.)
+-- (Implementation note: FNV-1, the hash function used for strings,
+-- can still be susceptible to collision attacks, even if a salt
+-- unknown to the attacker is used. Future versions of the library
+-- might improve on this situation.)
diff --git a/Data/Hashable/Class.hs b/Data/Hashable/Class.hs
--- a/Data/Hashable/Class.hs
+++ b/Data/Hashable/Class.hs
@@ -50,27 +50,25 @@
 import qualified Data.ByteString.Internal as B
 import qualified Data.ByteString.Unsafe as B
 import qualified Data.ByteString.Lazy as BL
-import qualified Data.ByteString.Lazy.Internal as BL
 #if defined(__GLASGOW_HASKELL__)
 import qualified Data.Text as T
 import qualified Data.Text.Array as TA
 import qualified Data.Text.Internal as T
 import qualified Data.Text.Lazy as TL
-import qualified Data.Text.Lazy.Internal as TL
 # ifdef GENERICS
 import GHC.Generics
 # endif
 #endif
+import Foreign.C (CString)
 #if __GLASGOW_HASKELL__ >= 703
-import Foreign.C (CSize(..))
+import Foreign.C (CLong(..))
 #else
-import Foreign.C (CSize)
+import Foreign.C (CLong)
 #endif
 import Foreign.Marshal.Utils (with)
-import Foreign.Ptr (Ptr, castPtr, nullPtr)
+import Foreign.Ptr (Ptr, castPtr)
 import Foreign.Storable (alignment, peek, sizeOf)
 import System.IO.Unsafe (unsafePerformIO)
-import Foreign.Marshal.Array (advancePtr, allocaArray)
 
 -- Byte arrays and Integers.
 #if defined(__GLASGOW_HASKELL__)
@@ -264,14 +262,18 @@
 hashNative salt = fromIntegral . go . xor (fromIntegral salt) . fromIntegral
   where
 #if WORD_SIZE_IN_BITS == 32
-    go = c_wang32
+    go :: Word32 -> Word32
 #else
-    go = c_wang64
+    go :: Word64 -> Word64
 #endif
+    go = id
 
 -- | Hash a 64-bit integer.
 hash64 :: (Integral a) => Int -> a -> Int
-hash64 salt = fromIntegral . c_wang64 . xor (fromIntegral salt) . fromIntegral
+hash64 salt = fromIntegral . go . xor (fromIntegral salt) . fromIntegral
+  where
+    go :: Word64 -> Word64
+    go = id
 
 instance Hashable Integer where
 #if defined(__GLASGOW_HASKELL__) && defined(VERSION_integer_gmp)
@@ -375,7 +377,7 @@
                            hashPtrWithSalt p (fromIntegral len) salt
 
 instance Hashable BL.ByteString where
-    hashWithSalt = hashLazyByteStringWithSalt
+    hashWithSalt = BL.foldlChunks hashWithSalt
 
 #if defined(__GLASGOW_HASKELL__)
 instance Hashable T.Text where
@@ -384,7 +386,7 @@
         salt
 
 instance Hashable TL.Text where
-    hashWithSalt = hashLazyTextWithSalt
+    hashWithSalt = TL.foldlChunks hashWithSalt
 #endif
 
 
@@ -437,54 +439,11 @@
                 -> Int     -- ^ salt
                 -> IO Int  -- ^ hash value
 hashPtrWithSalt p len salt =
-    fromIntegral `fmap` c_siphash24 k0 (fromSalt salt) (castPtr p)
-                        (fromIntegral len)
-
-k0 :: Word64
-k0 = 0x56e2b8a0aee1721a
-{-# INLINE k0 #-}
-
-hashLazyByteStringWithSalt :: Int -> BL.ByteString -> Int
-hashLazyByteStringWithSalt salt cs0 = unsafePerformIO . allocaArray 5 $ \v -> do
-  c_siphash_init k0 (fromSalt salt) v
-  let go !buffered !totallen (BL.Chunk c cs) =
-        B.unsafeUseAsCStringLen c $ \(ptr, len) -> do
-          let len' = fromIntegral len
-          buffered' <- c_siphash24_chunk buffered v (castPtr ptr) len' (-1)
-          go buffered' (totallen + len') cs
-      go buffered totallen _ = do
-        _ <- c_siphash24_chunk buffered v nullPtr 0 totallen
-        fromIntegral `fmap` peek (v `advancePtr` 4)
-  go 0 0 cs0
-
-#if defined(__GLASGOW_HASKELL__)
-hashLazyTextWithSalt :: Int -> TL.Text -> Int
-hashLazyTextWithSalt salt cs0 = unsafePerformIO . allocaArray 5 $ \v -> do
-  c_siphash_init k0 (fromSalt salt) v
-  let go !buffered !totallen (TL.Chunk (T.Text arr off len) cs) = do
-        let len' = fromIntegral (len `shiftL` 1)
-        buffered' <- c_siphash24_chunk_offset buffered v (TA.aBA arr)
-                     (fromIntegral (off `shiftL` 1)) len' (-1)
-        go buffered' (totallen + len') cs
-      go buffered totallen _ = do
-        _ <- c_siphash24_chunk buffered v nullPtr 0 totallen
-        fromIntegral `fmap` peek (v `advancePtr` 4)
-  go 0 0 cs0
-#endif
-
-fromSalt :: Int -> Word64
-#if WORD_SIZE_IN_BITS == 64
-fromSalt = fromIntegral
-#else
-fromSalt v = fromIntegral v `xor` k1
-
-k1 :: Word64
-k1 = 0x7654954208bdfef9
-{-# INLINE k1 #-}
-#endif
+    fromIntegral `fmap` c_hashCString (castPtr p) (fromIntegral len)
+    (fromIntegral salt)
 
-foreign import ccall unsafe "hashable_siphash24" c_siphash24
-    :: Word64 -> Word64 -> Ptr Word8 -> CSize -> IO Word64
+foreign import ccall unsafe "hashable_fnv_hash" c_hashCString
+    :: CString -> CLong -> CLong -> IO CLong
 
 #if defined(__GLASGOW_HASKELL__)
 -- | Compute a hash value for the content of this 'ByteArray#',
@@ -512,27 +471,9 @@
     -> Int         -- ^ salt
     -> Int         -- ^ hash value
 hashByteArrayWithSalt ba !off !len !h =
-    fromIntegral $
-    c_siphash24_offset k0 (fromSalt h) ba (fromIntegral off) (fromIntegral len)
-
-foreign import ccall unsafe "hashable_siphash24_offset" c_siphash24_offset
-    :: Word64 -> Word64 -> ByteArray# -> CSize -> CSize -> Word64
-
-foreign import ccall unsafe "hashable_siphash24_chunk_offset"
-        c_siphash24_chunk_offset
-    :: CInt -> Ptr Word64 -> ByteArray# -> CSize -> CSize -> CSize -> IO CInt
-#endif
+    fromIntegral $ c_hashByteArray ba (fromIntegral off) (fromIntegral len)
+    (fromIntegral h)
 
-#if WORD_SIZE_IN_BITS == 32
-foreign import ccall unsafe "hashable_wang_32" c_wang32
-    :: Word32 -> Word32
+foreign import ccall unsafe "hashable_fnv_hash_offset" c_hashByteArray
+    :: ByteArray# -> CLong -> CLong -> CLong -> CLong
 #endif
-
-foreign import ccall unsafe "hashable_wang_64" c_wang64
-    :: Word64 -> Word64
-
-foreign import ccall unsafe "hashable_siphash_init" c_siphash_init
-    :: Word64 -> Word64 -> Ptr Word64 -> IO ()
-
-foreign import ccall unsafe "hashable_siphash24_chunk" c_siphash24_chunk
-    :: CInt -> Ptr Word64 -> Ptr Word8 -> CSize -> CSize -> IO CInt
diff --git a/benchmarks/cbits/fnv.c b/benchmarks/cbits/fnv.c
deleted file mode 100644
--- a/benchmarks/cbits/fnv.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-Copyright Johan Tibell 2011
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-
-    * Neither the name of Johan Tibell nor the names of other
-      contributors may be used to endorse or promote products derived
-      from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/* FNV-1 hash
- *
- * The FNV-1 hash description: http://isthe.com/chongo/tech/comp/fnv/
- * The FNV-1 hash is public domain: http://isthe.com/chongo/tech/comp/fnv/#public_domain
- */
-long hashable_fnv_hash(const unsigned char* str, long len, long hash) {
-
-  while (len--) {
-    hash = (hash * 16777619) ^ *str++;
-  }
-
-  return hash;
-}
-
-/* Used for ByteArray#s. We can't treat them like pointers in
-   native Haskell, but we can in unsafe FFI calls.
- */
-long hashable_fnv_hash_offset(const unsigned char* str, long offset, long len, long hash) {
-  return hashable_fnv_hash(str + offset, len, hash);
-}
diff --git a/benchmarks/cbits/siphash-sse2.c b/benchmarks/cbits/siphash-sse2.c
new file mode 100644
--- /dev/null
+++ b/benchmarks/cbits/siphash-sse2.c
@@ -0,0 +1,129 @@
+/*
+ * The original code was developed by Samuel Neves, and has been
+ * only lightly modified.
+ *
+ * Used with permission.
+ */
+#pragma GCC target("sse2")
+
+#include <emmintrin.h>
+#include "siphash.h"
+
+#define _mm_roti_epi64(x, c) ((16 == (c)) ? _mm_shufflelo_epi16((x), _MM_SHUFFLE(2,1,0,3)) : _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c))))
+
+u64 hashable_siphash24_sse2(u64 ik0, u64 ik1, const u8 *m, size_t n)
+{
+	__m128i v0, v1, v2, v3;
+	__m128i k0, k1;
+	__m128i mi, mask, len;
+	size_t i, k;
+	union { u64 gpr; __m128i xmm; } hash;
+	const u8 *p;
+
+	/* We used to use the _mm_seti_epi32 intrinsic to initialize
+	   SSE2 registers. This compiles to a movdqa instruction,
+	   which requires 16-byte alignment. On 32-bit Windows, it
+	   looks like ghc's runtime linker doesn't align ".rdata"
+	   sections as requested, so we got segfaults for our trouble.
+
+	   Now we use an intrinsic that cares less about alignment
+	   (_mm_loadu_si128, aka movdqu) instead, and all seems
+	   happy. */
+
+	static const u32 const iv[6][4] = {
+		{ 0x70736575, 0x736f6d65, 0, 0 },
+		{ 0x6e646f6d, 0x646f7261, 0, 0 },
+		{ 0x6e657261, 0x6c796765, 0, 0 },
+		{ 0x79746573, 0x74656462, 0, 0 },
+		{ -1, -1, 0, 0 },
+		{ 255, 0, 0, 0 },
+	};
+
+	k0 = _mm_loadl_epi64((__m128i*)(&ik0));
+	k1 = _mm_loadl_epi64((__m128i*)(&ik1));
+
+	v0 = _mm_xor_si128(k0, _mm_loadu_si128((__m128i*) &iv[0]));
+	v1 = _mm_xor_si128(k1, _mm_loadu_si128((__m128i*) &iv[1]));
+	v2 = _mm_xor_si128(k0, _mm_loadu_si128((__m128i*) &iv[2]));
+	v3 = _mm_xor_si128(k1, _mm_loadu_si128((__m128i*) &iv[3]));
+
+#define HALF_ROUND(a,b,c,d,s,t) \
+	do \
+	{ \
+		a = _mm_add_epi64(a, b);  c = _mm_add_epi64(c, d); \
+		b = _mm_roti_epi64(b, s); d = _mm_roti_epi64(d, t); \
+		b = _mm_xor_si128(b, a);  d = _mm_xor_si128(d, c); \
+	} while(0)
+
+#define COMPRESS(v0,v1,v2,v3) \
+	do \
+	{ \
+		HALF_ROUND(v0,v1,v2,v3,13,16); \
+		v0 = _mm_shufflelo_epi16(v0, _MM_SHUFFLE(1,0,3,2)); \
+		HALF_ROUND(v2,v1,v0,v3,17,21); \
+		v2 = _mm_shufflelo_epi16(v2, _MM_SHUFFLE(1,0,3,2)); \
+	} while(0)
+
+	for(i = 0; i < (n-n%8); i += 8)
+	{
+		mi = _mm_loadl_epi64((__m128i*)(m + i));
+		v3 = _mm_xor_si128(v3, mi);
+		if (SIPHASH_ROUNDS == 2) {
+			COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
+		} else {
+			for (k = 0; k < SIPHASH_ROUNDS; ++k)
+				COMPRESS(v0,v1,v2,v3);
+		}
+		v0 = _mm_xor_si128(v0, mi);
+	}
+
+	p = m + n;
+
+	/* We must be careful to not trigger a segfault by reading an
+	   unmapped page. So where is the end of our input? */
+
+	if (((uintptr_t) p & 4095) == 0)
+		/* Exactly at a page boundary: do not read past the end. */
+		mi = _mm_setzero_si128();
+	else if (((uintptr_t) p & 4095) <= 4088)
+		/* Inside a page: safe to read past the end, as we'll
+		   mask out any bits we shouldn't have looked at below. */
+		mi = _mm_loadl_epi64((__m128i*)(m + i));
+	else
+		/* Within 8 bytes of the end of a page: ensure that
+		   our final read re-reads some bytes so that we do
+		   not cross the page boundary, then shift our result
+		   right so that the re-read bytes vanish. */
+		mi = _mm_srli_epi64(_mm_loadl_epi64((__m128i*)(((uintptr_t) m + i) & ~7)),
+				    8 * (((uintptr_t) m + i) % 8));
+
+	len = _mm_set_epi32(0, 0, (n&0xff) << 24, 0);
+	mask = _mm_srli_epi64(_mm_loadu_si128((__m128i*) &iv[4]), 8*(8-n%8));
+	mi = _mm_xor_si128(_mm_and_si128(mi, mask), len);
+
+	v3 = _mm_xor_si128(v3, mi);
+	if (SIPHASH_ROUNDS == 2) {
+		COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
+	} else {
+		for (k = 0; k < SIPHASH_ROUNDS; ++k)
+			COMPRESS(v0,v1,v2,v3);
+	}
+	v0 = _mm_xor_si128(v0, mi);
+
+	v2 = _mm_xor_si128(v2, _mm_loadu_si128((__m128i*) &iv[5]));
+	if (SIPHASH_FINALROUNDS == 4) {
+		COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
+		COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
+	} else {
+		for (k = 0; k < SIPHASH_FINALROUNDS; ++k)
+			COMPRESS(v0,v1,v2,v3);
+	}
+
+	v0 = _mm_xor_si128(_mm_xor_si128(v0, v1), _mm_xor_si128(v2, v3));
+	hash.xmm = v0;
+
+#undef COMPRESS
+#undef HALF_ROUND
+	//return _mm_extract_epi32(v0, 0) | (((u64)_mm_extract_epi32(v0, 1)) << 32);
+	return hash.gpr;
+}
diff --git a/benchmarks/cbits/siphash-sse41.c b/benchmarks/cbits/siphash-sse41.c
new file mode 100644
--- /dev/null
+++ b/benchmarks/cbits/siphash-sse41.c
@@ -0,0 +1,86 @@
+/*
+ * The original code was developed by Samuel Neves, and has been
+ * only lightly modified.
+ *
+ * Used with permission.
+ */
+#pragma GCC target("sse4.1")
+
+#include <smmintrin.h>
+#include "siphash.h"
+
+// Specialized for siphash, do not reuse
+#define rotate16(x) _mm_shufflehi_epi16((x), _MM_SHUFFLE(2,1,0,3))
+
+#define _mm_roti_epi64(x, c) (((c) == 16) ? rotate16((x)) : _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c))))
+//#define _mm_roti_epi64(x, c)  _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c)))
+
+
+u64 hashable_siphash24_sse41(u64 _k0, u64 _k1, const unsigned char *m, size_t n)
+{
+	__m128i v0, v1, v02, v13;
+	__m128i k0;
+	__m128i mi, mask, len, h;
+	const __m128i zero = _mm_setzero_si128();
+	size_t i, k;
+	union { u64 gpr; __m128i xmm; } hash;
+	unsigned char key[16];
+
+	((u64 *)key)[0] = _k0;
+	((u64 *)key)[1] = _k1;
+
+	k0 = _mm_loadu_si128((__m128i*)(key + 0));
+
+	v0 = _mm_xor_si128(k0, _mm_set_epi32(0x646f7261, 0x6e646f6d, 0x736f6d65, 0x70736575));
+	v1 = _mm_xor_si128(k0, _mm_set_epi32(0x74656462, 0x79746573, 0x6c796765, 0x6e657261));
+
+	v02 = _mm_unpacklo_epi64(v0, v1);
+	v13 = _mm_unpackhi_epi64(v0, v1);
+
+#define HALF_ROUND(a,b,s,t) \
+do \
+{ \
+	__m128i b1,b2; \
+	a = _mm_add_epi64(a, b);  \
+	b1 = _mm_roti_epi64(b, s); b2 = _mm_roti_epi64(b, t); b = _mm_blend_epi16(b1, b2, 0xF0); \
+	b = _mm_xor_si128(b, a);  \
+} while(0)
+
+#define COMPRESS(v02,v13) \
+	do \
+	{ \
+		HALF_ROUND(v02,v13,13,16); \
+		v02 = _mm_shuffle_epi32(v02, _MM_SHUFFLE(0,1,3,2)); \
+		HALF_ROUND(v02,v13,17,21); \
+		v02 = _mm_shuffle_epi32(v02, _MM_SHUFFLE(0,1,3,2)); \
+	} while(0)
+
+	for(i = 0; i < (n-n%8); i += 8)
+	{
+		mi = _mm_loadl_epi64((__m128i*)(m + i));
+		v13 = _mm_xor_si128(v13, _mm_unpacklo_epi64(zero, mi));
+		for(k = 0; k < SIPHASH_ROUNDS; ++k) COMPRESS(v02,v13);
+		v02 = _mm_xor_si128(v02, mi);
+	}
+
+	mi = _mm_loadl_epi64((__m128i*)(m + i));
+	len = _mm_set_epi32(0, 0, (n&0xff) << 24, 0);
+	mask = _mm_srli_epi64(_mm_set_epi32(0, 0, 0xffffffff, 0xffffffff), 8*(8-n%8));
+	mi = _mm_xor_si128(_mm_and_si128(mi, mask), len);
+
+	v13 = _mm_xor_si128(v13, _mm_unpacklo_epi64(zero, mi));
+	for(k = 0; k < SIPHASH_ROUNDS; ++k) COMPRESS(v02,v13);
+	v02 = _mm_xor_si128(v02, mi);
+
+	v02 = _mm_xor_si128(v02, _mm_set_epi32(0, 0xff, 0, 0));
+	for(k = 0; k < SIPHASH_FINALROUNDS; ++k) COMPRESS(v02,v13);
+
+	v0 = _mm_xor_si128(v02, v13);
+	v0 = _mm_xor_si128(v0, _mm_castps_si128(_mm_movehl_ps(_mm_castsi128_ps(zero), _mm_castsi128_ps(v0))));
+	hash.xmm = v0;
+
+#undef COMPRESS
+#undef HALF_ROUND
+	//return _mm_extract_epi32(v0, 0) | (((u64)_mm_extract_epi32(v0, 1)) << 32);
+	return hash.gpr;
+}
diff --git a/benchmarks/cbits/siphash.c b/benchmarks/cbits/siphash.c
new file mode 100644
--- /dev/null
+++ b/benchmarks/cbits/siphash.c
@@ -0,0 +1,262 @@
+/* Almost a verbatim copy of the reference implementation. */
+
+#include <stddef.h>
+#include "siphash.h"
+
+#define ROTL(x,b) (u64)(((x) << (b)) | ((x) >> (64 - (b))))
+
+#define SIPROUND \
+    do { \
+	v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+	v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
+	v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
+	v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+    } while(0)
+
+#if defined(__i386)
+# define _siphash24 plain_siphash24
+#endif
+
+static inline u64 odd_read(const u8 *p, int count, u64 val, int shift)
+{
+    switch (count) {
+    case 7: val |= ((u64)p[6]) << (shift + 48);
+    case 6: val |= ((u64)p[5]) << (shift + 40);
+    case 5: val |= ((u64)p[4]) << (shift + 32);
+    case 4: val |= ((u64)p[3]) << (shift + 24);
+    case 3: val |= ((u64)p[2]) << (shift + 16);
+    case 2: val |= ((u64)p[1]) << (shift + 8);
+    case 1: val |= ((u64)p[0]) << shift;
+    }
+    return val;
+}
+
+static inline u64 _siphash(int c, int d, u64 k0, u64 k1,
+			   const u8 *str, size_t len)
+{
+    u64 v0 = 0x736f6d6570736575ull ^ k0;
+    u64 v1 = 0x646f72616e646f6dull ^ k1;
+    u64 v2 = 0x6c7967656e657261ull ^ k0;
+    u64 v3 = 0x7465646279746573ull ^ k1;
+    const u8 *end, *p;
+    u64 b;
+    int i;
+
+    for (p = str, end = str + (len & ~7); p < end; p += 8) {
+	u64 m = peek_u64le((u64 *) p);
+	v3 ^= m;
+	if (c == 2) {
+	    SIPROUND;
+	    SIPROUND;
+	} else {
+	    for (i = 0; i < c; i++)
+		SIPROUND;
+	}
+	v0 ^= m;
+    }
+
+    b = odd_read(p, len & 7, ((u64) len) << 56, 0);
+
+    v3 ^= b;
+    if (c == 2) {
+	SIPROUND;
+	SIPROUND;
+    } else {
+	for (i = 0; i < c; i++)
+	    SIPROUND;
+    }
+    v0 ^= b;
+
+    v2 ^= 0xff;
+    if (d == 4) {
+	SIPROUND;
+	SIPROUND;
+	SIPROUND;
+	SIPROUND;
+    } else {
+	for (i = 0; i < d; i++)
+	    SIPROUND;
+    }
+    b = v0 ^ v1 ^ v2  ^ v3;
+    return b;
+}
+
+
+static inline u64 _siphash24(u64 k0, u64 k1, const u8 *str, size_t len)
+{
+    return _siphash(2, 4, k0, k1, str, len);
+}
+
+#if defined(__i386)
+# undef _siphash24
+
+static u64 (*_siphash24)(u64 k0, u64 k1, const u8 *, size_t);
+
+static void maybe_use_sse()
+    __attribute__((constructor));
+
+static void maybe_use_sse()
+{
+    uint32_t eax = 1, ebx, ecx, edx;
+
+    __asm volatile
+	("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
+	 "cpuid;"
+	 "mov %%ebx, %%esi;"
+	 "mov %%edi, %%ebx;"
+	 :"+a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx)
+	 : :"edi");
+
+#if defined(HAVE_SSE2)
+    if (edx & (1 << 26))
+	_siphash24 = hashable_siphash24_sse2;
+#if defined(HAVE_SSE41)
+    else if (ecx & (1 << 19))
+	_siphash24 = hashable_siphash24_sse41;
+#endif
+    else
+#endif
+	_siphash24 = plain_siphash24;
+}
+
+#endif
+
+/* ghci's linker fails to call static initializers. */
+static inline void ensure_sse_init()
+{
+#if defined(__i386)
+    if (_siphash24 == NULL)
+	maybe_use_sse();
+#endif
+}
+
+u64 hashable_siphash(int c, int d, u64 k0, u64 k1, const u8 *str, size_t len)
+{
+    return _siphash(c, d, k0, k1, str, len);
+}
+
+u64 hashable_siphash24(u64 k0, u64 k1, const u8 *str, size_t len)
+{
+    ensure_sse_init();
+    return _siphash24(k0, k1, str, len);
+}
+
+/* Used for ByteArray#s. We can't treat them like pointers in
+   native Haskell, but we can in unsafe FFI calls.
+ */
+u64 hashable_siphash24_offset(u64 k0, u64 k1,
+			      const u8 *str, size_t off, size_t len)
+{
+    ensure_sse_init();
+    return _siphash24(k0, k1, str + off, len);
+}
+
+static int _siphash_chunk(int c, int d, int buffered, u64 v[5],
+			  const u8 *str, size_t len, size_t totallen)
+{
+    u64 v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3], m, b;
+    const u8 *p, *end;
+    u64 carry = 0;
+    int i;
+
+    if (buffered > 0) {
+	int unbuffered = 8 - buffered;
+	int tobuffer = unbuffered > len ? len : unbuffered;
+	int shift = buffered << 3;
+
+	m = odd_read(str, tobuffer, v[4], shift);
+	str += tobuffer;
+	buffered += tobuffer;
+	len -= tobuffer;
+
+	if (buffered < 8)
+	    carry = m;
+	else {
+	    v3 ^= m;
+	    if (c == 2) {
+		SIPROUND;
+		SIPROUND;
+	    } else {
+		for (i = 0; i < c; i++)
+		    SIPROUND;
+	    }
+	    v0 ^= m;
+	    buffered = 0;
+	    m = 0;
+	}
+    }
+
+    for (p = str, end = str + (len & ~7); p < end; p += 8) {
+	m = peek_u64le((u64 *) p);
+	v3 ^= m;
+	if (c == 2) {
+	    SIPROUND;
+	    SIPROUND;
+	} else {
+	    for (i = 0; i < c; i++)
+		SIPROUND;
+	}
+	v0 ^= m;
+    }
+
+    b = odd_read(p, len & 7, 0, 0);
+
+    if (totallen == -1) {
+	v[0] = v0;
+	v[1] = v1;
+	v[2] = v2;
+	v[3] = v3;
+	v[4] = b | carry;
+
+	return buffered + (len & 7);
+    }
+
+    b |= ((u64) totallen) << 56;
+
+    v3 ^= b;
+    if (c == 2) {
+	SIPROUND;
+	SIPROUND;
+    } else {
+	for (i = 0; i < c; i++)
+	    SIPROUND;
+    }
+    v0 ^= b;
+
+    v2 ^= 0xff;
+    if (d == 4) {
+	SIPROUND;
+	SIPROUND;
+	SIPROUND;
+	SIPROUND;
+    } else {
+	for (i = 0; i < d; i++)
+	    SIPROUND;
+    }
+    v[4] = v0 ^ v1 ^ v2  ^ v3;
+    return 0;
+}
+
+void hashable_siphash_init(u64 k0, u64 k1, u64 *v)
+{
+    v[0] = 0x736f6d6570736575ull ^ k0;
+    v[1] = 0x646f72616e646f6dull ^ k1;
+    v[2] = 0x6c7967656e657261ull ^ k0;
+    v[3] = 0x7465646279746573ull ^ k1;
+    v[4] = 0;
+}
+
+int hashable_siphash24_chunk(int buffered, u64 v[5], const u8 *str,
+			     size_t len, size_t totallen)
+{
+    return _siphash_chunk(2, 4, buffered, v, str, len, totallen);
+}
+
+/*
+ * Used for ByteArray#.
+ */
+int hashable_siphash24_chunk_offset(int buffered, u64 v[5], const u8 *str,
+				    size_t off, size_t len, size_t totallen)
+{
+    return _siphash_chunk(2, 4, buffered, v, str + off, len, totallen);
+}
diff --git a/benchmarks/cbits/wang.c b/benchmarks/cbits/wang.c
new file mode 100644
--- /dev/null
+++ b/benchmarks/cbits/wang.c
@@ -0,0 +1,29 @@
+/*
+ * These hash functions were developed by Thomas Wang.
+ *
+ * http://www.concentric.net/~ttwang/tech/inthash.htm
+ */
+
+#include <stdint.h>
+
+uint32_t hashable_wang_32(uint32_t a)
+{
+    a = (a ^ 61) ^ (a >> 16);
+    a = a + (a << 3);
+    a = a ^ (a >> 4);
+    a = a * 0x27d4eb2d;
+    a = a ^ (a >> 15);
+    return a;
+}
+
+uint64_t hashable_wang_64(uint64_t key)
+{
+    key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+    key = key ^ ((key >> 24) | (key << 40));
+    key = (key + (key << 3)) + (key << 8); // key * 265
+    key = key ^ ((key >> 14) | (key << 50));
+    key = (key + (key << 2)) + (key << 4); // key * 21
+    key = key ^ ((key >> 28) | (key << 36));
+    key = key + (key << 31);
+    return key;
+}
diff --git a/cbits/fnv.c b/cbits/fnv.c
new file mode 100644
--- /dev/null
+++ b/cbits/fnv.c
@@ -0,0 +1,53 @@
+/*
+Copyright Johan Tibell 2011
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Johan Tibell nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* FNV-1 hash
+ *
+ * The FNV-1 hash description: http://isthe.com/chongo/tech/comp/fnv/
+ * The FNV-1 hash is public domain: http://isthe.com/chongo/tech/comp/fnv/#public_domain
+ */
+long hashable_fnv_hash(const unsigned char* str, long len, long hash) {
+
+  while (len--) {
+    hash = (hash * 16777619) ^ *str++;
+  }
+
+  return hash;
+}
+
+/* Used for ByteArray#s. We can't treat them like pointers in
+   native Haskell, but we can in unsafe FFI calls.
+ */
+long hashable_fnv_hash_offset(const unsigned char* str, long offset, long len, long hash) {
+  return hashable_fnv_hash(str + offset, len, hash);
+}
diff --git a/cbits/inthash.c b/cbits/inthash.c
deleted file mode 100644
--- a/cbits/inthash.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * These hash functions were developed by Thomas Wang.
- *
- * http://www.concentric.net/~ttwang/tech/inthash.htm
- */
-
-#include <stdint.h>
-
-uint32_t hashable_wang_32(uint32_t a)
-{
-    a = (a ^ 61) ^ (a >> 16);
-    a = a + (a << 3);
-    a = a ^ (a >> 4);
-    a = a * 0x27d4eb2d;
-    a = a ^ (a >> 15);
-    return a;
-}
-
-uint64_t hashable_wang_64(uint64_t key)
-{
-    key = (~key) + (key << 21); // key = (key << 21) - key - 1;
-    key = key ^ ((key >> 24) | (key << 40));
-    key = (key + (key << 3)) + (key << 8); // key * 265
-    key = key ^ ((key >> 14) | (key << 50));
-    key = (key + (key << 2)) + (key << 4); // key * 21
-    key = key ^ ((key >> 28) | (key << 36));
-    key = key + (key << 31);
-    return key;
-}
diff --git a/cbits/siphash-sse2.c b/cbits/siphash-sse2.c
deleted file mode 100644
--- a/cbits/siphash-sse2.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * The original code was developed by Samuel Neves, and has been
- * only lightly modified.
- *
- * Used with permission.
- */
-#pragma GCC target("sse2")
-
-#include <emmintrin.h>
-#include "siphash.h"
-
-#define _mm_roti_epi64(x, c) ((16 == (c)) ? _mm_shufflelo_epi16((x), _MM_SHUFFLE(2,1,0,3)) : _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c))))
-
-u64 hashable_siphash24_sse2(u64 ik0, u64 ik1, const u8 *m, size_t n)
-{
-	__m128i v0, v1, v2, v3;
-	__m128i k0, k1;
-	__m128i mi, mask, len;
-	size_t i, k;
-	union { u64 gpr; __m128i xmm; } hash;
-	const u8 *p;
-
-	/* We used to use the _mm_seti_epi32 intrinsic to initialize
-	   SSE2 registers. This compiles to a movdqa instruction,
-	   which requires 16-byte alignment. On 32-bit Windows, it
-	   looks like ghc's runtime linker doesn't align ".rdata"
-	   sections as requested, so we got segfaults for our trouble.
-
-	   Now we use an intrinsic that cares less about alignment
-	   (_mm_loadu_si128, aka movdqu) instead, and all seems
-	   happy. */
-
-	static const u32 const iv[6][4] = {
-		{ 0x70736575, 0x736f6d65, 0, 0 },
-		{ 0x6e646f6d, 0x646f7261, 0, 0 },
-		{ 0x6e657261, 0x6c796765, 0, 0 },
-		{ 0x79746573, 0x74656462, 0, 0 },
-		{ -1, -1, 0, 0 },
-		{ 255, 0, 0, 0 },
-	};
-
-	k0 = _mm_loadl_epi64((__m128i*)(&ik0));
-	k1 = _mm_loadl_epi64((__m128i*)(&ik1));
-
-	v0 = _mm_xor_si128(k0, _mm_loadu_si128((__m128i*) &iv[0]));
-	v1 = _mm_xor_si128(k1, _mm_loadu_si128((__m128i*) &iv[1]));
-	v2 = _mm_xor_si128(k0, _mm_loadu_si128((__m128i*) &iv[2]));
-	v3 = _mm_xor_si128(k1, _mm_loadu_si128((__m128i*) &iv[3]));
-
-#define HALF_ROUND(a,b,c,d,s,t) \
-	do \
-	{ \
-		a = _mm_add_epi64(a, b);  c = _mm_add_epi64(c, d); \
-		b = _mm_roti_epi64(b, s); d = _mm_roti_epi64(d, t); \
-		b = _mm_xor_si128(b, a);  d = _mm_xor_si128(d, c); \
-	} while(0)
-
-#define COMPRESS(v0,v1,v2,v3) \
-	do \
-	{ \
-		HALF_ROUND(v0,v1,v2,v3,13,16); \
-		v0 = _mm_shufflelo_epi16(v0, _MM_SHUFFLE(1,0,3,2)); \
-		HALF_ROUND(v2,v1,v0,v3,17,21); \
-		v2 = _mm_shufflelo_epi16(v2, _MM_SHUFFLE(1,0,3,2)); \
-	} while(0)
-
-	for(i = 0; i < (n-n%8); i += 8)
-	{
-		mi = _mm_loadl_epi64((__m128i*)(m + i));
-		v3 = _mm_xor_si128(v3, mi);
-		if (SIPHASH_ROUNDS == 2) {
-			COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
-		} else {
-			for (k = 0; k < SIPHASH_ROUNDS; ++k)
-				COMPRESS(v0,v1,v2,v3);
-		}
-		v0 = _mm_xor_si128(v0, mi);
-	}
-
-	p = m + n;
-
-	/* We must be careful to not trigger a segfault by reading an
-	   unmapped page. So where is the end of our input? */
-
-	if (((uintptr_t) p & 4095) == 0)
-		/* Exactly at a page boundary: do not read past the end. */
-		mi = _mm_setzero_si128();
-	else if (((uintptr_t) p & 4095) <= 4088)
-		/* Inside a page: safe to read past the end, as we'll
-		   mask out any bits we shouldn't have looked at below. */
-		mi = _mm_loadl_epi64((__m128i*)(m + i));
-	else
-		/* Within 8 bytes of the end of a page: ensure that
-		   our final read re-reads some bytes so that we do
-		   not cross the page boundary, then shift our result
-		   right so that the re-read bytes vanish. */
-		mi = _mm_srli_epi64(_mm_loadl_epi64((__m128i*)(((uintptr_t) m + i) & ~7)),
-				    8 * (((uintptr_t) m + i) % 8));
-
-	len = _mm_set_epi32(0, 0, (n&0xff) << 24, 0);
-	mask = _mm_srli_epi64(_mm_loadu_si128((__m128i*) &iv[4]), 8*(8-n%8));
-	mi = _mm_xor_si128(_mm_and_si128(mi, mask), len);
-
-	v3 = _mm_xor_si128(v3, mi);
-	if (SIPHASH_ROUNDS == 2) {
-		COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
-	} else {
-		for (k = 0; k < SIPHASH_ROUNDS; ++k)
-			COMPRESS(v0,v1,v2,v3);
-	}
-	v0 = _mm_xor_si128(v0, mi);
-
-	v2 = _mm_xor_si128(v2, _mm_loadu_si128((__m128i*) &iv[5]));
-	if (SIPHASH_FINALROUNDS == 4) {
-		COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
-		COMPRESS(v0,v1,v2,v3); COMPRESS(v0,v1,v2,v3);
-	} else {
-		for (k = 0; k < SIPHASH_FINALROUNDS; ++k)
-			COMPRESS(v0,v1,v2,v3);
-	}
-
-	v0 = _mm_xor_si128(_mm_xor_si128(v0, v1), _mm_xor_si128(v2, v3));
-	hash.xmm = v0;
-
-#undef COMPRESS
-#undef HALF_ROUND
-	//return _mm_extract_epi32(v0, 0) | (((u64)_mm_extract_epi32(v0, 1)) << 32);
-	return hash.gpr;
-}
diff --git a/cbits/siphash-sse41.c b/cbits/siphash-sse41.c
deleted file mode 100644
--- a/cbits/siphash-sse41.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * The original code was developed by Samuel Neves, and has been
- * only lightly modified.
- *
- * Used with permission.
- */
-#pragma GCC target("sse4.1")
-
-#include <smmintrin.h>
-#include "siphash.h"
-
-// Specialized for siphash, do not reuse
-#define rotate16(x) _mm_shufflehi_epi16((x), _MM_SHUFFLE(2,1,0,3))
-
-#define _mm_roti_epi64(x, c) (((c) == 16) ? rotate16((x)) : _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c))))
-//#define _mm_roti_epi64(x, c)  _mm_xor_si128(_mm_slli_epi64((x), (c)), _mm_srli_epi64((x), 64-(c)))
-
-
-u64 hashable_siphash24_sse41(u64 _k0, u64 _k1, const unsigned char *m, size_t n)
-{
-	__m128i v0, v1, v02, v13;
-	__m128i k0;
-	__m128i mi, mask, len, h;
-	const __m128i zero = _mm_setzero_si128();
-	size_t i, k;
-	union { u64 gpr; __m128i xmm; } hash;
-	unsigned char key[16];
-
-	((u64 *)key)[0] = _k0;
-	((u64 *)key)[1] = _k1;
-
-	k0 = _mm_loadu_si128((__m128i*)(key + 0));
-
-	v0 = _mm_xor_si128(k0, _mm_set_epi32(0x646f7261, 0x6e646f6d, 0x736f6d65, 0x70736575));
-	v1 = _mm_xor_si128(k0, _mm_set_epi32(0x74656462, 0x79746573, 0x6c796765, 0x6e657261));
-
-	v02 = _mm_unpacklo_epi64(v0, v1);
-	v13 = _mm_unpackhi_epi64(v0, v1);
-
-#define HALF_ROUND(a,b,s,t) \
-do \
-{ \
-	__m128i b1,b2; \
-	a = _mm_add_epi64(a, b);  \
-	b1 = _mm_roti_epi64(b, s); b2 = _mm_roti_epi64(b, t); b = _mm_blend_epi16(b1, b2, 0xF0); \
-	b = _mm_xor_si128(b, a);  \
-} while(0)
-
-#define COMPRESS(v02,v13) \
-	do \
-	{ \
-		HALF_ROUND(v02,v13,13,16); \
-		v02 = _mm_shuffle_epi32(v02, _MM_SHUFFLE(0,1,3,2)); \
-		HALF_ROUND(v02,v13,17,21); \
-		v02 = _mm_shuffle_epi32(v02, _MM_SHUFFLE(0,1,3,2)); \
-	} while(0)
-
-	for(i = 0; i < (n-n%8); i += 8)
-	{
-		mi = _mm_loadl_epi64((__m128i*)(m + i));
-		v13 = _mm_xor_si128(v13, _mm_unpacklo_epi64(zero, mi));
-		for(k = 0; k < SIPHASH_ROUNDS; ++k) COMPRESS(v02,v13);
-		v02 = _mm_xor_si128(v02, mi);
-	}
-
-	mi = _mm_loadl_epi64((__m128i*)(m + i));
-	len = _mm_set_epi32(0, 0, (n&0xff) << 24, 0);
-	mask = _mm_srli_epi64(_mm_set_epi32(0, 0, 0xffffffff, 0xffffffff), 8*(8-n%8));
-	mi = _mm_xor_si128(_mm_and_si128(mi, mask), len);
-
-	v13 = _mm_xor_si128(v13, _mm_unpacklo_epi64(zero, mi));
-	for(k = 0; k < SIPHASH_ROUNDS; ++k) COMPRESS(v02,v13);
-	v02 = _mm_xor_si128(v02, mi);
-
-	v02 = _mm_xor_si128(v02, _mm_set_epi32(0, 0xff, 0, 0));
-	for(k = 0; k < SIPHASH_FINALROUNDS; ++k) COMPRESS(v02,v13);
-
-	v0 = _mm_xor_si128(v02, v13);
-	v0 = _mm_xor_si128(v0, _mm_castps_si128(_mm_movehl_ps(_mm_castsi128_ps(zero), _mm_castsi128_ps(v0))));
-	hash.xmm = v0;
-
-#undef COMPRESS
-#undef HALF_ROUND
-	//return _mm_extract_epi32(v0, 0) | (((u64)_mm_extract_epi32(v0, 1)) << 32);
-	return hash.gpr;
-}
diff --git a/cbits/siphash.c b/cbits/siphash.c
deleted file mode 100644
--- a/cbits/siphash.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/* Almost a verbatim copy of the reference implementation. */
-
-#include <stddef.h>
-#include "siphash.h"
-
-#define ROTL(x,b) (u64)(((x) << (b)) | ((x) >> (64 - (b))))
-
-#define SIPROUND \
-    do { \
-	v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
-	v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
-	v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
-	v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
-    } while(0)
-
-#if defined(__i386)
-# define _siphash24 plain_siphash24
-#endif
-
-static inline u64 odd_read(const u8 *p, int count, u64 val, int shift)
-{
-    switch (count) {
-    case 7: val |= ((u64)p[6]) << (shift + 48);
-    case 6: val |= ((u64)p[5]) << (shift + 40);
-    case 5: val |= ((u64)p[4]) << (shift + 32);
-    case 4: val |= ((u64)p[3]) << (shift + 24);
-    case 3: val |= ((u64)p[2]) << (shift + 16);
-    case 2: val |= ((u64)p[1]) << (shift + 8);
-    case 1: val |= ((u64)p[0]) << shift;
-    }
-    return val;
-}
-
-static inline u64 _siphash(int c, int d, u64 k0, u64 k1,
-			   const u8 *str, size_t len)
-{
-    u64 v0 = 0x736f6d6570736575ull ^ k0;
-    u64 v1 = 0x646f72616e646f6dull ^ k1;
-    u64 v2 = 0x6c7967656e657261ull ^ k0;
-    u64 v3 = 0x7465646279746573ull ^ k1;
-    const u8 *end, *p;
-    u64 b;
-    int i;
-
-    for (p = str, end = str + (len & ~7); p < end; p += 8) {
-	u64 m = peek_u64le((u64 *) p);
-	v3 ^= m;
-	if (c == 2) {
-	    SIPROUND;
-	    SIPROUND;
-	} else {
-	    for (i = 0; i < c; i++)
-		SIPROUND;
-	}
-	v0 ^= m;
-    }
-
-    b = odd_read(p, len & 7, ((u64) len) << 56, 0);
-
-    v3 ^= b;
-    if (c == 2) {
-	SIPROUND;
-	SIPROUND;
-    } else {
-	for (i = 0; i < c; i++)
-	    SIPROUND;
-    }
-    v0 ^= b;
-
-    v2 ^= 0xff;
-    if (d == 4) {
-	SIPROUND;
-	SIPROUND;
-	SIPROUND;
-	SIPROUND;
-    } else {
-	for (i = 0; i < d; i++)
-	    SIPROUND;
-    }
-    b = v0 ^ v1 ^ v2  ^ v3;
-    return b;
-}
-
-
-static inline u64 _siphash24(u64 k0, u64 k1, const u8 *str, size_t len)
-{
-    return _siphash(2, 4, k0, k1, str, len);
-}
-
-#if defined(__i386)
-# undef _siphash24
-
-static u64 (*_siphash24)(u64 k0, u64 k1, const u8 *, size_t);
-
-static void maybe_use_sse()
-    __attribute__((constructor));
-
-static void maybe_use_sse()
-{
-    uint32_t eax = 1, ebx, ecx, edx;
-
-    __asm volatile
-	("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
-	 "cpuid;"
-	 "mov %%ebx, %%esi;"
-	 "mov %%edi, %%ebx;"
-	 :"+a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx)
-	 : :"edi");
-
-#if defined(HAVE_SSE2)
-    if (edx & (1 << 26))
-	_siphash24 = hashable_siphash24_sse2;
-#if defined(HAVE_SSE41)
-    else if (ecx & (1 << 19))
-	_siphash24 = hashable_siphash24_sse41;
-#endif
-    else
-#endif
-	_siphash24 = plain_siphash24;
-}
-
-#endif
-
-/* ghci's linker fails to call static initializers. */
-static inline void ensure_sse_init()
-{
-#if defined(__i386)
-    if (_siphash24 == NULL)
-	maybe_use_sse();
-#endif
-}
-
-u64 hashable_siphash(int c, int d, u64 k0, u64 k1, const u8 *str, size_t len)
-{
-    return _siphash(c, d, k0, k1, str, len);
-}
-
-u64 hashable_siphash24(u64 k0, u64 k1, const u8 *str, size_t len)
-{
-    ensure_sse_init();
-    return _siphash24(k0, k1, str, len);
-}
-
-/* Used for ByteArray#s. We can't treat them like pointers in
-   native Haskell, but we can in unsafe FFI calls.
- */
-u64 hashable_siphash24_offset(u64 k0, u64 k1,
-			      const u8 *str, size_t off, size_t len)
-{
-    ensure_sse_init();
-    return _siphash24(k0, k1, str + off, len);
-}
-
-static int _siphash_chunk(int c, int d, int buffered, u64 v[5],
-			  const u8 *str, size_t len, size_t totallen)
-{
-    u64 v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3], m, b;
-    const u8 *p, *end;
-    u64 carry = 0;
-    int i;
-
-    if (buffered > 0) {
-	int unbuffered = 8 - buffered;
-	int tobuffer = unbuffered > len ? len : unbuffered;
-	int shift = buffered << 3;
-
-	m = odd_read(str, tobuffer, v[4], shift);
-	str += tobuffer;
-	buffered += tobuffer;
-	len -= tobuffer;
-
-	if (buffered < 8)
-	    carry = m;
-	else {
-	    v3 ^= m;
-	    if (c == 2) {
-		SIPROUND;
-		SIPROUND;
-	    } else {
-		for (i = 0; i < c; i++)
-		    SIPROUND;
-	    }
-	    v0 ^= m;
-	    buffered = 0;
-	    m = 0;
-	}
-    }
-
-    for (p = str, end = str + (len & ~7); p < end; p += 8) {
-	m = peek_u64le((u64 *) p);
-	v3 ^= m;
-	if (c == 2) {
-	    SIPROUND;
-	    SIPROUND;
-	} else {
-	    for (i = 0; i < c; i++)
-		SIPROUND;
-	}
-	v0 ^= m;
-    }
-
-    b = odd_read(p, len & 7, 0, 0);
-
-    if (totallen == -1) {
-	v[0] = v0;
-	v[1] = v1;
-	v[2] = v2;
-	v[3] = v3;
-	v[4] = b | carry;
-
-	return buffered + (len & 7);
-    }
-
-    b |= ((u64) totallen) << 56;
-
-    v3 ^= b;
-    if (c == 2) {
-	SIPROUND;
-	SIPROUND;
-    } else {
-	for (i = 0; i < c; i++)
-	    SIPROUND;
-    }
-    v0 ^= b;
-
-    v2 ^= 0xff;
-    if (d == 4) {
-	SIPROUND;
-	SIPROUND;
-	SIPROUND;
-	SIPROUND;
-    } else {
-	for (i = 0; i < d; i++)
-	    SIPROUND;
-    }
-    v[4] = v0 ^ v1 ^ v2  ^ v3;
-    return 0;
-}
-
-void hashable_siphash_init(u64 k0, u64 k1, u64 *v)
-{
-    v[0] = 0x736f6d6570736575ull ^ k0;
-    v[1] = 0x646f72616e646f6dull ^ k1;
-    v[2] = 0x6c7967656e657261ull ^ k0;
-    v[3] = 0x7465646279746573ull ^ k1;
-    v[4] = 0;
-}
-
-int hashable_siphash24_chunk(int buffered, u64 v[5], const u8 *str,
-			     size_t len, size_t totallen)
-{
-    return _siphash_chunk(2, 4, buffered, v, str, len, totallen);
-}
-
-/*
- * Used for ByteArray#.
- */
-int hashable_siphash24_chunk_offset(int buffered, u64 v[5], const u8 *str,
-				    size_t off, size_t len, size_t totallen)
-{
-    return _siphash_chunk(2, 4, buffered, v, str + off, len, totallen);
-}
diff --git a/cbits/siphash.h b/cbits/siphash.h
deleted file mode 100644
--- a/cbits/siphash.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef _hashable_siphash_h
-#define _hashable_siphash_h
-
-#include <stdint.h>
-
-typedef uint64_t u64;
-typedef uint32_t u32;
-typedef uint16_t u16;
-typedef uint8_t u8;
-
-#define SIPHASH_ROUNDS 2
-#define SIPHASH_FINALROUNDS 4
-
-u64 hashable_siphash(int, int, u64, u64, const u8 *, size_t);
-u64 hashable_siphash24(u64, u64, const u8 *, size_t);
-
-#if defined(__i386)
-
-/* To use SSE instructions, we have to adjust the stack from its
-   default of 4-byte alignment to use 16-byte alignment. */
-
-# define ALIGNED_STACK __attribute__((force_align_arg_pointer))
-
-u64 hashable_siphash24_sse2(u64, u64, const u8 *, size_t) ALIGNED_STACK;
-u64 hashable_siphash24_sse41(u64, u64, const u8 *, size_t) ALIGNED_STACK;
-#endif
-
-#if defined(_WIN32)
-# define __LITTLE_ENDIAN 1234
-# define __BIG_ENDIAN 4321
-# define __BYTE_ORDER __LITTLE_ENDIAN
-
-#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || defined(__OpenBSD__) || defined(__NetBSD__)
-# include <sys/endian.h>
-# define __BIG_ENDIAN BIG_ENDIAN
-# define __LITTLE_ENDIAN LITTLE_ENDIAN
-# define __BYTE_ORDER BYTE_ORDER
-
-#elif (defined(BSD) && (BSD >= 199103)) || defined(__APPLE__)
-# include <machine/endian.h>
-# define __BIG_ENDIAN BIG_ENDIAN
-# define __LITTLE_ENDIAN LITTLE_ENDIAN
-# define __BYTE_ORDER BYTE_ORDER
-
-#elif defined(__linux__)
-# include <endian.h>
-#endif
-
-static inline u64 peek_u64le(const u64 *p)
-{
-    u64 x = *p;
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-    x = ((x & 0xff00000000000000ull) >> 56) |
-	((x & 0x00ff000000000000ull) >> 40) |
-	((x & 0x0000ff0000000000ull) >> 24) |
-	((x & 0x000000ff00000000ull) >> 8) |
-	((x & 0x00000000ff000000ull) << 8) |
-	((x & 0x0000000000ff0000ull) << 24) |
-	((x & 0x000000000000ff00ull) << 40) |
-	((x & 0x00000000000000ffull) << 56);
-#endif
-
-    return x;
-}
-
-#endif /* _hashable_siphash_h */
diff --git a/hashable.cabal b/hashable.cabal
--- a/hashable.cabal
+++ b/hashable.cabal
@@ -1,5 +1,5 @@
 Name:                hashable
-Version:             1.2.0.8
+Version:             1.2.0.9
 Synopsis:            A class for types that can be converted to a hash value
 Description:         This package defines a class, 'Hashable', for types that
                      can be converted to a hash value.  This class
@@ -20,8 +20,7 @@
 -- tests/Properties.hs shouldn't have to go here, but the source files
 -- for the test-suite stanzas don't get picked up by `cabal sdist`.
 Extra-source-files:
-  CHANGES, README.md, tests/Properties.hs, benchmarks/Benchmarks.hs,
-  cbits/siphash.h
+  CHANGES, README.md, tests/Properties.hs, benchmarks/Benchmarks.hs
 
 Flag integer-gmp
   Description: Are we using integer-gmp to provide fast Integer instances?
@@ -55,15 +54,7 @@
     Other-modules:   Data.Hashable.Generic
 
   C-sources:
-                     cbits/inthash.c
-                     cbits/siphash.c
-  if arch(i386) && flag(sse2)
-    CPP-Options:   -DHAVE_SSE2
-    C-sources:       cbits/siphash-sse2.c
-
-    if flag(sse41)
-      CPP-Options:   -DHAVE_SSE41
-      C-sources:     cbits/siphash-sse41.c
+                     cbits/fnv.c
 
   Ghc-options:       -Wall
   if impl(ghc >= 6.8)
@@ -129,20 +120,20 @@
     Build-depends:   integer-gmp >= 0.2
 
   c-sources:
-    cbits/inthash.c
-    cbits/siphash.c
-    benchmarks/cbits/fnv.c
     benchmarks/cbits/inthash.c
+    benchmarks/cbits/siphash.c
+    benchmarks/cbits/wang.c
+    cbits/fnv.c
 
   if (arch(i386) || arch(x86_64)) && flag(sse2)
     cpp-options: -DHAVE_SSE2
     c-sources:
-      cbits/siphash-sse2.c
+      benchmarks/cbits/siphash-sse2.c
 
     if flag(sse41)
       cpp-options: -DHAVE_SSE41
       c-sources:
-        cbits/siphash-sse41.c
+        benchmarks/cbits/siphash-sse41.c
 
   Ghc-options:       -Wall -O2
   if impl(ghc >= 6.8)
