diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for the hashes package
+
+## 0.1.0.0 -- 2021-09-29
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2021, Lars Kuhtz <lakuhtz@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+Haskell implementation of various non-cryptographic hash functions.
+
+The current version includes
+
+*  SipHash
+*  FNV1 (64 bit and 32 bit)
+*  FNV1a (64 bit and 32 bit)
+
diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,98 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+-- |
+-- Module: Main
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Main
+( main
+) where
+
+import Criterion
+import Criterion.Main
+
+import qualified Data.ByteArray as BA
+import qualified Data.ByteArray.Hash as BA
+import qualified Data.ByteString as B
+import Data.Word
+
+import GHC.Ptr
+
+import Test.QuickCheck
+
+-- internal modules
+
+import qualified Data.Hash.SipHash as H
+import qualified Data.Hash.FNV1 as H
+
+-- -------------------------------------------------------------------------- --
+-- Main
+
+main :: IO ()
+main = do
+    putStrLn "prop_sip"
+    quickCheck prop_sip
+    putStrLn "prop_fnv1a"
+    quickCheck prop_fnv1a
+    putStrLn "prop_fnv1aPrimitive"
+    quickCheck prop_fnv1aPrimitive
+    defaultMain
+        [ bgroup "sipHash"
+            [ sipBench "memory" (memorySipHash 17 17)
+            , sipBench "internal" (H.hashByteString $ H.sipHash24 17 17)
+            ]
+        , bgroup "fnv1aHash"
+            [ fnv1aBench "memory" memoryFnv1a
+            , fnv1aBench "internal" (H.hashByteString H.fnv1a_64)
+            , fnv1aBench "primitive" primitiveFnv1a
+            ]
+        ]
+
+-- -------------------------------------------------------------------------- --
+-- SipHash
+
+memorySipHash :: BA.ByteArrayAccess p => Word64 -> Word64 -> p -> Word64
+memorySipHash w0 w1 x = let BA.SipHash r = BA.sipHash (BA.SipKey w0 w1) x in r
+{-# INLINE memorySipHash #-}
+
+prop_sip :: Word64 -> Word64 -> [Word8] -> Property
+prop_sip w0 w1 b =
+    memorySipHash w0 w1 (B.pack b) === H.hashByteString (H.sipHash24 w0 w1) (B.pack b)
+
+sipBench :: String -> (B.ByteString -> Word64) -> Benchmark
+sipBench l f = bgroup l $ go <$> benchStrings
+  where
+    go i = bench (show (B.length i)) $ whnf f i
+
+benchStrings :: [B.ByteString]
+benchStrings = str <$> [0, 1, 5, 67, 300, 2000, 20000]
+  where
+    str (i :: Int) = B.pack $ fromIntegral <$> [0..i-1]
+
+-- -------------------------------------------------------------------------- --
+-- Fvn1Hash
+
+memoryFnv1a :: B.ByteString -> Word64
+memoryFnv1a b = let BA.FnvHash64 h = BA.fnv1a_64Hash b in h
+{-# INLINE memoryFnv1a #-}
+
+primitiveFnv1a :: B.ByteString -> Word64
+primitiveFnv1a = H.hashByteString $ \(Ptr addr) n ->
+    fromIntegral <$> H.fnv1a addr n
+{-# INLINE primitiveFnv1a #-}
+
+prop_fnv1a :: [Word8] -> Property
+prop_fnv1a b = memoryFnv1a (B.pack b) === H.hashByteString H.fnv1a_64 (B.pack b)
+
+prop_fnv1aPrimitive :: [Word8] -> Property
+prop_fnv1aPrimitive b = primitiveFnv1a (B.pack b) === H.hashByteString H.fnv1a_64 (B.pack b)
+
+fnv1aBench :: String -> (B.ByteString -> Word64) -> Benchmark
+fnv1aBench l f = bgroup l $ go <$> benchStrings
+  where
+    go i = bench (show (B.length i)) $ whnf f i
+
diff --git a/hashes.cabal b/hashes.cabal
new file mode 100644
--- /dev/null
+++ b/hashes.cabal
@@ -0,0 +1,83 @@
+cabal-version: 2.4
+name: hashes
+version: 0.1.0.0
+synopsis: Hash functions
+Description: Efficient implementation of non-cryptographic hash functions
+homepage: https://github.com/larskuhtz/hs-hashes
+bug-reports: https://github.com/larskuhtz/hs-hashes/issues
+license: MIT
+license-file: LICENSE
+author: Lars Kuhtz
+maintainer: lakuhtz@gmail.com
+copyright: Copyright (c) 2019-2021 Lars Kuhtz <lakuhtz@gmail.com>
+category: Data
+tested-with:
+    GHC==9.0.1
+    GHC==8.10.7
+    GHC==8.8.4
+extra-source-files:
+    README.md
+    CHANGELOG.md
+
+source-repository head
+    type: git
+    location: https://github.com/larskuhtz/hs-hashes.git
+
+library
+    hs-source-dirs: src
+    default-language: Haskell2010
+    exposed-modules:
+        Data.Hash.SipHash
+        Data.Hash.FNV1
+        Data.Hash.Utils
+    ghc-options:
+        -Wall
+    build-depends:
+        , base >=4.11 && <5
+        , bytestring >=0.10
+
+test-suite tests
+    type: exitcode-stdio-1.0
+    hs-source-dirs: test
+    default-language: Haskell2010
+    ghc-options:
+        -Wall
+        -rtsopts
+        -threaded
+        -with-rtsopts=-N
+    main-is: Main.hs
+    other-modules:
+        Test.Data.Hash.SipHash
+        Test.Data.Hash.FNV1
+        Test.Data.Hash.Utils
+    build-depends:
+        -- internal
+        , hashes
+        -- external
+        , QuickCheck >= 2.13
+        , base >=4.11 && <5
+        , bytestring >=0.10
+
+benchmark benchmarks
+    type: exitcode-stdio-1.0
+    hs-source-dirs: bench
+    main-is: Main.hs
+    default-language: Haskell2010
+    ghc-options:
+        -Wall
+        -rtsopts
+        -threaded
+        -with-rtsopts=-N
+        -mbmi2
+        -msse4.2
+    build-depends:
+        -- internal
+        , hashes
+
+        -- external
+        , QuickCheck >= 2.13
+        , base >=4.10 && <5
+        , bytestring >=0.10
+        , criterion >= 1.5
+        , memory >=0.14
+
diff --git a/src/Data/Hash/FNV1.hs b/src/Data/Hash/FNV1.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Hash/FNV1.hs
@@ -0,0 +1,239 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+-- |
+-- Module: Data.Hash.FNV1
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+-- The primitive versions are usually not more efficient than the version with
+-- explicit word sizes for the respective host architecture.
+--
+module Data.Hash.FNV1
+(
+-- * IO API (64 bit)
+
+  fnv1_64
+, fnv1_64_
+, fnv1a_64
+, fnv1a_64_
+
+-- * 32 bit versions
+, fnv1_32
+, fnv1_32_
+, fnv1a_32
+, fnv1a_32_
+
+-- * Primitive (host word size)
+, fnv1
+, fnv1_
+, fnv1Primitive
+, fnv1Primitive_
+
+, fnv1a
+, fnv1a_
+, fnv1aPrimitive
+, fnv1aPrimitive_
+
+-- * Utils
+, module Data.Hash.Utils
+
+-- * Constants
+, fnvPrime
+, fnvPrime32
+, fnvPrime64
+
+, fnvOffsetBasis
+, fnvOffsetBasis32
+, fnvOffsetBasis64
+
+) where
+
+import Data.Bits
+import Data.Word
+
+import Foreign.Ptr
+import Foreign.Storable
+
+import GHC.Exts
+
+import GHC.IO
+
+-- internal modules
+
+import Data.Hash.Utils
+
+-- -------------------------------------------------------------------------- --
+-- Constants
+
+fnvPrime32 :: Word32
+fnvPrime32 = 0x01000193
+
+fnvPrime64 :: Word64
+fnvPrime64 = 0x100000001b3
+
+fnvOffsetBasis32 :: Word32
+fnvOffsetBasis32 = 0x811c9dc5
+
+fnvOffsetBasis64 :: Word64
+fnvOffsetBasis64 = 0xcbf29ce484222325
+
+fnvPrime :: Word
+#if defined(x86_64_HOST_ARCH)
+fnvPrime = fromIntegral fnvPrime64
+#elif defined(i386_HOST_ARCH)
+fnvPrime = fromIntegral fvnPrime32
+#else
+fnvPrime = error "fnvPrime: unsupported hardware platform"
+#endif
+
+fnvOffsetBasis :: Word
+#if defined(x86_64_HOST_ARCH)
+fnvOffsetBasis = fromIntegral fnvOffsetBasis64
+#elif defined(i386_HOST_ARCH)
+fnvOffsetBasis = fromIntegral fnvOffsetBasis32
+#else
+fnvOffsetBasis = error "fnvOffsetBasis: unsupported hardware platform"
+#endif
+
+-- -------------------------------------------------------------------------- --
+-- FNV1 64 bit
+
+fnv1_64 :: Ptr Word8 -> Int -> IO Word64
+fnv1_64 !ptr !n = fnv1_64_ ptr n fnvOffsetBasis64
+{-# INLINE fnv1_64 #-}
+
+fnv1_64_ :: Ptr Word8 -> Int -> Word64 -> IO Word64
+fnv1_64_ !ptr !n !a = loop a 0
+  where
+    loop !acc !i
+        | i == n = return acc
+        | otherwise = do
+            !x <- peekByteOff @Word8 ptr i
+            loop ((fnvPrime64 * acc) `xor` fromIntegral x) (i + 1)
+{-# INLINE fnv1_64_ #-}
+
+-- -------------------------------------------------------------------------- --
+-- FNV1a 64 bit
+
+fnv1a_64 :: Ptr Word8 -> Int -> IO Word64
+fnv1a_64 !ptr !n = fnv1a_64_ ptr n fnvOffsetBasis64
+{-# INLINE fnv1a_64 #-}
+
+fnv1a_64_ :: Ptr Word8 -> Int -> Word64 -> IO Word64
+fnv1a_64_ !ptr !n !a = loop a 0
+  where
+    loop !acc !i
+        | i == n = return acc
+        | otherwise = do
+            !x <- peekByteOff @Word8 ptr i
+            loop (fnvPrime64 * (acc `xor` fromIntegral x)) (i + 1)
+{-# INLINE fnv1a_64_ #-}
+
+-- -------------------------------------------------------------------------- --
+-- FNV1 32 bit
+
+fnv1_32 :: Ptr Word8 -> Int -> IO Word32
+fnv1_32 !ptr !n = fnv1_32_ ptr n fnvOffsetBasis32
+{-# INLINE fnv1_32 #-}
+
+fnv1_32_ :: Ptr Word8 -> Int -> Word32 -> IO Word32
+fnv1_32_ !ptr !n !a = loop a 0
+  where
+    loop !acc !i
+        | i == n = return acc
+        | otherwise = do
+            !x <- peekByteOff @Word8 ptr i
+            loop ((fnvPrime32 * acc) `xor` fromIntegral x) (i + 1)
+{-# INLINE fnv1_32_ #-}
+
+-- FNV1a 32 bit
+
+fnv1a_32 :: Ptr Word8 -> Int -> IO Word32
+fnv1a_32 !ptr !n = fnv1a_32_ ptr n fnvOffsetBasis32
+{-# INLINE fnv1a_32 #-}
+
+fnv1a_32_ :: Ptr Word8 -> Int -> Word32 -> IO Word32
+fnv1a_32_ !ptr !n a = loop a 0
+  where
+    loop !acc !i
+        | i == n = return acc
+        | otherwise = do
+            !x <- peekByteOff @Word8 ptr i
+            loop (fnvPrime32 * (acc `xor` fromIntegral x)) (i + 1)
+{-# INLINE fnv1a_32_ #-}
+
+-- -------------------------------------------------------------------------- --
+-- Primitive (host architecture words)
+
+-- FNV1
+
+fnv1 :: Addr# -> Int -> IO Word
+fnv1 addr (I# n) = IO $ \s -> case fnv1Primitive addr n s of
+    (# s1, w #) -> (# s1, W# w #)
+{-# INlINE fnv1 #-}
+
+fnv1_ :: Addr# -> Int -> Word -> IO Word
+fnv1_ addr (I# n) (W# a) = IO $ \s -> case fnv1Primitive_ addr n a s of
+    (# s1, w #) -> (# s1, W# w #)
+{-# INlINE fnv1_ #-}
+
+fnv1Primitive :: Addr# -> Int# -> State# tok -> (# State# tok, Word# #)
+fnv1Primitive !addr !n !tok = fnv1Primitive_ addr n o tok
+  where
+    !(W# o) = fnvOffsetBasis
+{-# INLINE fnv1Primitive #-}
+
+fnv1Primitive_ :: Addr# -> Int# -> Word# -> State# tok -> (# State# tok, Word# #)
+fnv1Primitive_ !addr !n !a tok = case loop a 0# tok of
+    (# tok1, w #) -> (# tok1, w #)
+  where
+    loop !acc !i !s = case i ==# n of
+        1# -> (# s, acc #)
+        _ -> case readWord8OffAddr# addr i s of
+            (# s1, w #) -> loop
+                ((p `timesWord#` acc) `xor#` w)
+                (i +# 1#)
+                s1
+
+    !(W# p) = fnvPrime
+{-# INLINE fnv1Primitive_ #-}
+
+-- FNV1a
+
+fnv1a :: Addr# -> Int -> IO Word
+fnv1a addr (I# n) = IO $ \s -> case fnv1aPrimitive addr n s of
+    (# s1, w #) -> (# s1, W# w #)
+{-# INlINE fnv1a #-}
+
+fnv1a_ :: Addr# -> Int -> Word -> IO Word
+fnv1a_ addr (I# n) (W# a) = IO $ \s -> case fnv1aPrimitive_ addr n a s of
+    (# s1, w #) -> (# s1, W# w #)
+{-# INlINE fnv1a_ #-}
+
+fnv1aPrimitive :: Addr# -> Int# -> State# tok -> (# State# tok, Word# #)
+fnv1aPrimitive !addr !n !tok = fnv1aPrimitive_ addr n o tok
+  where
+    !(W# o) = fnvOffsetBasis
+{-# INLINE fnv1aPrimitive #-}
+
+fnv1aPrimitive_ :: Addr# -> Int# -> Word# -> State# tok -> (# State# tok, Word# #)
+fnv1aPrimitive_ !addr !n !a tok = case loop a 0# tok of
+    (# tok1, w #) -> (# tok1, w #)
+  where
+    loop !acc !i !s = case i ==# n of
+        1# -> (# s, acc #)
+        _ -> case readWord8OffAddr# addr i s of
+            (# s1, w #) -> loop
+                (p `timesWord#` (acc `xor#` w))
+                (i +# 1#)
+                s1
+
+    !(W# p) = fnvPrime
+{-# INLINE fnv1aPrimitive_ #-}
+
diff --git a/src/Data/Hash/SipHash.hs b/src/Data/Hash/SipHash.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Hash/SipHash.hs
@@ -0,0 +1,245 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+-- |
+-- Module: Data.Hash.SipHash
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Data.Hash.SipHash
+( sipHash
+, sipHash13
+, sipHash24
+, sipHash48
+, sipHashCD
+
+-- * Utils
+, module Data.Hash.Utils
+) where
+
+import Control.Monad
+
+import Data.Bits
+import Data.Function
+import Data.Word
+
+import Foreign.Marshal.Utils
+import Foreign.Storable
+
+import GHC.Ptr
+
+import Prelude hiding (drop, length, null, splitAt, take)
+
+-- internal modules
+
+import Data.Hash.Utils
+
+-- -------------------------------------------------------------------------- --
+-- SipHash
+
+-- | SipHash, with recommended default parameters of c=2 and c=4.
+--
+-- The first and second argument is the 128 bit key, represented as two 64 bit
+-- words.
+--
+sipHash
+    :: Word64
+    -> Word64
+    -> Ptr Word8
+    -> Int
+    -> IO Word64
+sipHash = sipHash24
+{-# INLINE sipHash #-}
+
+-- | SipHash-2-4
+--
+-- The first and second argument is the 128 bit key, represented as two 64 bit
+-- words.
+--
+sipHash24
+    :: Word64
+    -> Word64
+    -> Ptr Word8
+    -> Int
+    -> IO Word64
+sipHash24 = sipHashInternal rounds2 rounds4
+{-# INLINE sipHash24 #-}
+
+-- | SipHash-1-3
+--
+-- The first and second argument is the 128 bit key, represented as two 64 bit
+-- words.
+--
+sipHash13
+    :: Word64
+    -> Word64
+    -> Ptr Word8
+    -> Int
+    -> IO Word64
+sipHash13 = sipHashInternal rounds1 rounds3
+{-# INLINE sipHash13 #-}
+
+-- | SipHash-4-8
+--
+-- The first and second argument is the 128 bit key, represented as two 64 bit
+-- words.
+--
+sipHash48
+    :: Word64
+    -> Word64
+    -> Ptr Word8
+    -> Int
+    -> IO Word64
+sipHash48 = sipHashInternal rounds4 rounds8
+{-# INLINE sipHash48 #-}
+
+-- | Generic SipHash with c rounds per block and d finalization rounds.
+--
+-- The first and second argument is the 128 bit key, represented as two 64 bit
+-- words.
+--
+sipHashCD
+    :: Int
+    -> Int
+    -> Word64
+    -> Word64
+    -> Ptr Word8
+    -> Int
+    -> IO Word64
+sipHashCD c d = sipHashInternal (rounds c) (rounds d)
+{-# INLINE sipHashCD #-}
+
+-- -------------------------------------------------------------------------- --
+-- Generic SipHash
+
+data S = S
+    {-# UNPACK #-} !Word64
+    {-# UNPACK #-} !Word64
+    {-# UNPACK #-} !Word64
+    {-# UNPACK #-} !Word64
+
+type Round = Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+
+sipHashInternal
+    :: Round
+    -> Round
+    -> Word64
+    -> Word64
+    -> Ptr Word8
+    -> Int
+    -> IO Word64
+sipHashInternal cRound dRound !k0 !k1 !ptr !len = do
+
+    -- loop
+    (S !v0 !v1 !v2 !v3) <- loop i0 i1 i2 i3 (castPtr ptr) len
+
+    -- end
+    let (!off, !r) = quotRem len 8
+    w <- ptrToWord64 (plusPtr ptr (off * 8)) r
+    let !b = shiftL (fromIntegral len) 56 .|. w
+        (# !v0', !v1', !v2', !v3' #) = cRound v0 v1 v2 (v3 `xor` b)
+        (# !v0'', !v1'', !v2'', !v3'' #) = dRound (v0' `xor` b) v1' (v2' `xor` 0xff) v3'
+    return $! v0'' `xor` v1'' `xor` v2'' `xor` v3''
+
+  where
+
+    loop !v0 !v1 !v2 !v3 !p !l
+        | l < 8 = return $ S v0 v1 v2 v3
+        | otherwise = do
+            !m <- peek p
+            let (# v0', v1', v2', v3' #) = cRound v0 v1 v2 (v3 `xor` m)
+            loop (v0' `xor` m) v1' v2' v3' (plusPtr p 8) (l - 8)
+
+    !i0 = 0x736f6d6570736575 `xor` k0
+    !i1 = 0x646f72616e646f6d `xor` k1
+    !i2 = 0x6c7967656e657261 `xor` k0
+    !i3 = 0x7465646279746573 `xor` k1
+    {-# INLINE i0 #-}
+    {-# INLINE i1 #-}
+    {-# INLINE i2 #-}
+    {-# INLINE i3 #-}
+{-# INLINE sipHashInternal #-}
+
+ptrToWord64 :: Ptr Word64 -> Int -> IO Word64
+ptrToWord64 _ 0 = pure 0
+ptrToWord64 !p 1 = fromIntegral <$!> peek @Word8 (castPtr p)
+ptrToWord64 !p 2 = fromIntegral <$!> peek @Word16 (castPtr p)
+ptrToWord64 !p 4 = fromIntegral <$!> peek @Word32 (castPtr p)
+ptrToWord64 !p !i = with @Word64 0 $ \p' -> do
+        -- using 'with' within unsafeDupablePerformIO is probably safe because
+        -- with uses 'alloca', which guarantees that the memory is released
+        -- when computation is abondended before being terminated.
+    copyBytes p' p i
+    peek p'
+{-# INLINE ptrToWord64 #-}
+
+rounds1 :: Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+rounds1 !v0 !v1 !v2 !v3 = sipRound v0 v1 v2 v3
+{-# INLINE rounds1 #-}
+
+rounds2 :: Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+rounds2 !v0 !v1 !v2 !v3 =
+    let (# !v0', !v1', !v2', !v3' #) = sipRound v0 v1 v2 v3
+    in sipRound v0' v1' v2' v3'
+{-# INLINE rounds2 #-}
+
+rounds3 :: Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+rounds3 !v0 !v1 !v2 !v3 =
+    let (# !v0', !v1', !v2', !v3' #) = sipRound v0 v1 v2 v3
+        (# !v0'', !v1'', !v2'', !v3'' #) = sipRound v0' v1' v2' v3'
+    in sipRound v0'' v1'' v2'' v3''
+{-# INLINE rounds3 #-}
+
+rounds4 :: Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+rounds4 !v0 !v1 !v2 !v3 =
+    let (# !v0', !v1', !v2', !v3' #) = sipRound v0 v1 v2 v3
+        (# !v0'', !v1'', !v2'', !v3'' #) = sipRound v0' v1' v2' v3'
+        (# !v0''', !v1''', !v2''', !v3''' #) = sipRound v0'' v1'' v2'' v3''
+    in sipRound v0''' v1''' v2''' v3'''
+{-# INLINE rounds4 #-}
+
+rounds8 :: Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+rounds8 !v0 !v1 !v2 !v3 =
+    let (# !v0', !v1', !v2', !v3' #) = sipRound v0 v1 v2 v3
+        (# !v0'', !v1'', !v2'', !v3'' #) = sipRound v0' v1' v2' v3'
+        (# !v0''', !v1''', !v2''', !v3''' #) = sipRound v0'' v1'' v2'' v3''
+        (# !v0'''', !v1'''', !v2'''', !v3'''' #) = sipRound v0''' v1''' v2''' v3'''
+        (# !v0''''', !v1''''', !v2''''', !v3''''' #) = sipRound v0'''' v1'''' v2'''' v3''''
+        (# !v0'''''', !v1'''''', !v2'''''', !v3'''''' #) = sipRound v0''''' v1''''' v2''''' v3'''''
+        (# !v0''''''', !v1''''''', !v2''''''', !v3''''''' #) = sipRound v0'''''' v1'''''' v2'''''' v3''''''
+    in sipRound v0''''''' v1''''''' v2''''''' v3'''''''
+{-# INLINE rounds8 #-}
+
+rounds :: Int -> Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+rounds 1 !v0 !v1 !v2 !v3 = rounds1 v0 v1 v2 v3
+rounds 2 !v0 !v1 !v2 !v3 = rounds2 v0 v1 v2 v3
+rounds 3 !v0 !v1 !v2 !v3 = rounds3 v0 v1 v2 v3
+rounds 4 !v0 !v1 !v2 !v3 = rounds4 v0 v1 v2 v3
+rounds 8 !v0 !v1 !v2 !v3 = rounds8 v0 v1 v2 v3
+rounds !c !v0 !v1 !v2 !v3 = case sipRound v0 v1 v2 v3 of
+    (# v0', v1', v2', v3' #) -> rounds (c - 1) v0' v1' v2' v3'
+{-# INLINE rounds #-}
+
+sipRound :: Word64 -> Word64 -> Word64 -> Word64 -> (# Word64, Word64, Word64, Word64 #)
+sipRound !v0 !v1 !v2 !v3 = (# v0''', v1'''', v2''', v3'''' #)
+    where
+    !v0' = v0 + v1
+    !v2' = v2 + v3
+    !v1' = v1 `rotateL` 13
+    !v3' = v3 `rotateL` 16
+    !v1'' = v1' `xor` v0'
+    !v3'' = v3' `xor` v2'
+    !v0'' = v0' `rotateL` 32
+    !v2'' = v2' + v1''
+    !v0''' = v0'' + v3''
+    !v1''' = v1'' `rotateL` 17
+    !v3''' = v3'' `rotateL` 21
+    !v1'''' = v1''' `xor` v2''
+    !v3'''' = v3''' `xor` v0'''
+    !v2''' = v2'' `rotateL` 32
+{-# INLINE sipRound #-}
+
diff --git a/src/Data/Hash/Utils.hs b/src/Data/Hash/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Hash/Utils.hs
@@ -0,0 +1,144 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+-- |
+-- Module: Data.Hash.Utils
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Data.Hash.Utils
+(
+-- * Pure API
+  hashStorable
+, hashStorable_
+, hashByteString
+, hashByteString_
+, hashByteArray
+, hashByteArray_
+, hashPtr
+, hashPtr_
+
+-- * IO API
+, hashStorableIO
+, hashStorableIO_
+, hashByteStringIO
+, hashByteStringIO_
+, hashByteArrayIO
+, hashByteArrayIO_
+) where
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Unsafe as B
+import Data.Word
+
+import Foreign.Marshal.Utils
+import Foreign.Ptr
+import Foreign.Storable
+
+import GHC.Exts
+import GHC.IO
+import Foreign.Marshal.Alloc
+
+-- -------------------------------------------------------------------------- --
+-- Pure API
+
+-- Storable
+
+hashStorable :: Storable a => (Ptr Word8 -> Int -> IO b) -> a -> b
+hashStorable f = unsafeDupablePerformIO . hashStorableIO f
+{-# INLINE hashStorable #-}
+
+hashStorable_ :: Storable a => (Ptr Word8 -> Int -> b -> IO b) -> a -> b -> b
+hashStorable_ f a = unsafeDupablePerformIO . hashStorableIO_ f a
+{-# INLINE hashStorable_ #-}
+
+-- ByteString
+
+hashByteString :: (Ptr Word8 -> Int -> IO b) -> B.ByteString -> b
+hashByteString f = unsafeDupablePerformIO . hashByteStringIO f
+{-# INLINE hashByteString #-}
+
+hashByteString_ :: (Ptr Word8 -> Int -> b -> IO b) -> B.ByteString -> b -> b
+hashByteString_ f a = unsafeDupablePerformIO . hashByteStringIO_ f a
+{-# INLINE hashByteString_ #-}
+
+-- ByteArray
+
+hashByteArray :: (Ptr Word8 -> Int -> IO b) -> ByteArray# -> b
+hashByteArray f a# = unsafeDupablePerformIO $! hashByteArrayIO f a#
+{-# INLINE hashByteArray #-}
+
+hashByteArray_ :: (Ptr Word8 -> Int -> b -> IO b) -> ByteArray# -> b -> b
+hashByteArray_ f a# = unsafeDupablePerformIO . hashByteArrayIO_ f a#
+{-# INLINE hashByteArray_ #-}
+
+-- Ptr
+
+hashPtr :: (Ptr Word8 -> Int -> IO b) -> Ptr Word8 -> Int -> b
+hashPtr f ptr = unsafeDupablePerformIO . f ptr
+{-# INLINE hashPtr #-}
+
+hashPtr_ :: (Ptr Word8 -> Int -> b -> IO b) -> Ptr Word8 -> Int -> b -> b
+hashPtr_ f ptr l = unsafeDupablePerformIO . f ptr l
+{-# INLINE hashPtr_ #-}
+
+-- -------------------------------------------------------------------------- --
+-- IO API
+
+-- Storable
+
+hashStorableIO :: Storable a => (Ptr Word8 -> Int -> IO b) -> a -> IO b
+hashStorableIO f a = with a $ \ptr -> f (castPtr ptr) (sizeOf a)
+{-# INLINE hashStorableIO #-}
+
+hashStorableIO_ :: Storable a => (Ptr Word8 -> Int -> b -> IO b) -> a -> b -> IO b
+hashStorableIO_ f a b = with a $ \ptr -> f (castPtr ptr) (sizeOf a) b
+{-# INLINE hashStorableIO_ #-}
+
+-- ByteString
+
+hashByteStringIO :: (Ptr Word8 -> Int -> IO b) -> B.ByteString -> IO b
+hashByteStringIO f a = B.unsafeUseAsCStringLen a $ \(!p, !l) -> f (castPtr p) l
+{-# INLINE hashByteStringIO #-}
+
+hashByteStringIO_ :: (Ptr Word8 -> Int -> b -> IO b) -> B.ByteString -> b -> IO b
+hashByteStringIO_ f a b = B.unsafeUseAsCStringLen a $ \(!p, !l) -> f (castPtr p) l b
+{-# INLINE hashByteStringIO_ #-}
+
+-- ByteArray
+
+hashByteArrayIO :: (Ptr Word8 -> Int -> IO b) -> ByteArray# -> IO b
+hashByteArrayIO f a# = case isByteArrayPinned# a# of
+    -- Pinned ByteArray
+    1# -> f (Ptr (byteArrayContents# a#)) (I# size#)
+
+    -- Unpinned ByteArray, copy content to newly allocated pinned ByteArray
+    _ -> allocaBytes (I# size#) $ \ptr@(Ptr addr#) -> IO $ \s0 ->
+        case copyByteArrayToAddr# a# 0# addr# size# s0 of
+            s1 -> case f ptr (I# size#) of
+                IO run -> run s1
+  where
+    size# = sizeofByteArray# a#
+{-# INLINE hashByteArrayIO #-}
+
+
+hashByteArrayIO_ :: (Ptr Word8 -> Int -> b -> IO b) -> ByteArray# -> b -> IO b
+hashByteArrayIO_ f a# b = case isByteArrayPinned# a# of
+    -- Pinned ByteArray
+    1# -> f (Ptr (byteArrayContents# a#)) (I# size#) b
+
+    -- Unpinned ByteArray, copy content to newly allocated pinned ByteArray
+    _ -> allocaBytes (I# size#) $ \ptr@(Ptr addr#) -> IO $ \s0 ->
+        case copyByteArrayToAddr# a# 0# addr# size# s0 of
+            s1 -> case f ptr (I# size#) b of
+                IO run -> run s1
+  where
+    size# = sizeofByteArray# a#
+{-# INLINE hashByteArrayIO_ #-}
+
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,22 @@
+
+-- |
+-- Module: Main
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Main
+( main
+) where
+
+import qualified Test.Data.Hash.FNV1
+import qualified Test.Data.Hash.SipHash
+import qualified Test.Data.Hash.Utils
+
+main :: IO ()
+main = do
+    putStrLn "Test.Data.Hash.FNV1.tests: " >> Test.Data.Hash.FNV1.tests
+    putStrLn "Test.Data.Hash.SipHash.tests: " >> Test.Data.Hash.SipHash.tests
+    putStrLn "Test.Data.Hash.Utils:" >> Test.Data.Hash.Utils.tests
+
diff --git a/test/Test/Data/Hash/FNV1.hs b/test/Test/Data/Hash/FNV1.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Data/Hash/FNV1.hs
@@ -0,0 +1,463 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+-- |
+-- Module: Test.Data.Hash.FNV1
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Test.Data.Hash.FNV1
+( tests
+) where
+
+import Data.Bifunctor
+import qualified Data.ByteString as B
+import Data.Word
+
+import GHC.Ptr
+
+import Test.QuickCheck
+
+-- internal modules
+
+import Data.Hash.FNV1
+
+-- -------------------------------------------------------------------------- --
+-- All tests
+
+tests :: IO ()
+tests = quickCheck $ tests64
+    && tests32
+    && tests64a
+    && tests32a
+    && testsPrim
+    && testsPrima
+
+-- -------------------------------------------------------------------------- --
+-- 64 bit FNV1
+
+tests64 :: Bool
+tests64 = all test64 testVectors64
+    && all testZero64 zeros64
+
+test64 :: (B.ByteString, Word64) -> Bool
+test64 (b, r) = hashByteString fnv1_64 b == r
+
+testZero64 :: B.ByteString -> Bool
+testZero64 b = hashByteString fnv1_64 b == 0
+
+testVectors64 :: [(B.ByteString, Word64)]
+testVectors64 = []
+
+-- | All FNV1 64 bit inputs that result in a hash of 0 up to a length of nine
+-- bytes.
+--
+-- (cf. http://www.isthe.com/chongo/tech/comp/fnv/)
+--
+zeros64 :: [B.ByteString]
+zeros64 = B.pack <$>
+    [ [0x92, 0x06, 0x77, 0x4c, 0xe0, 0x2f, 0x89, 0x2a, 0xd2]
+    , [0xfb, 0x6c, 0x4f, 0xdb, 0x00, 0x41, 0xdb, 0xc0, 0xfe]
+    , [0x9f, 0x72, 0x72, 0x35, 0x80, 0x4b, 0x8d, 0x6b, 0x06]
+    , [0x3d, 0x82, 0x76, 0x00, 0x80, 0x7c, 0x52, 0x62, 0x1a]
+    , [0x58, 0x21, 0xf5, 0xa2, 0xe1, 0x01, 0x9d, 0x80, 0xe0]
+    , [0x52, 0xa1, 0xeb, 0x10, 0xa0, 0xe9, 0x45, 0x96, 0x05]
+    , [0xbe, 0x64, 0x8a, 0x14, 0xe1, 0x46, 0x17, 0x18, 0xff]
+    , [0x9e, 0x50, 0x1d, 0xf2, 0x41, 0x75, 0x55, 0xac, 0x05]
+    , [0xb3, 0x92, 0xa9, 0x6f, 0x80, 0xe4, 0x29, 0xa4, 0x63]
+    , [0x82, 0xba, 0xb3, 0x41, 0x81, 0x0f, 0xdb, 0x83, 0x15]
+    , [0x92, 0xf4, 0x6e, 0x0e, 0xe1, 0xb9, 0xe5, 0x45, 0x2d]
+    , [0x3c, 0xea, 0x57, 0x50, 0x81, 0x67, 0x67, 0x9b, 0xe0]
+    , [0x36, 0xe3, 0x96, 0x34, 0xe2, 0x1d, 0x36, 0x00, 0x61]
+    , [0xaf, 0xfa, 0xff, 0xee, 0x61, 0xb0, 0x5e, 0xc4, 0x04]
+    , [0xd5, 0xc9, 0x88, 0x15, 0x02, 0x2f, 0xd3, 0x38, 0xc2]
+    , [0xbc, 0xa7, 0x38, 0x51, 0xe2, 0x2f, 0xb1, 0x1b, 0x22]
+    , [0x70, 0xf2, 0x10, 0xba, 0x02, 0x45, 0x37, 0xb3, 0xe8]
+    , [0x5b, 0xa3, 0xa0, 0x4e, 0x81, 0xfc, 0x4f, 0x32, 0x5b]
+    , [0xcd, 0x50, 0x99, 0x68, 0x22, 0xd4, 0x78, 0x2f, 0xb0]
+    , [0xfa, 0xff, 0x0d, 0xfa, 0xe2, 0xf0, 0x43, 0xb4, 0x9a]
+    , [0x16, 0x3f, 0xba, 0x47, 0x43, 0x94, 0x44, 0xfe, 0x86]
+    , [0x46, 0xe0, 0x03, 0x71, 0xc2, 0x57, 0xf0, 0xbc, 0xda]
+    , [0x60, 0x74, 0x4a, 0x52, 0xe3, 0x06, 0x42, 0x36, 0xf2]
+    , [0x97, 0x20, 0x3a, 0xf8, 0xa2, 0xf6, 0x8a, 0x62, 0xa6]
+    , [0x98, 0xf5, 0xcc, 0xd5, 0x03, 0x32, 0x22, 0x1a, 0xe2]
+    , [0x5d, 0xd5, 0x14, 0xe8, 0xe3, 0x43, 0x8b, 0x5a, 0x9e]
+    , [0x95, 0xb8, 0x11, 0x62, 0x03, 0x5a, 0xed, 0xd2, 0xcb]
+    , [0xf9, 0xcb, 0x23, 0x1d, 0x03, 0x71, 0xc5, 0xca, 0x5c]
+    , [0xc3, 0x04, 0x1c, 0x59, 0x82, 0xa5, 0xaa, 0x9b, 0x55]
+    , [0x68, 0x10, 0x80, 0x61, 0xa3, 0x5e, 0x72, 0x31, 0x48]
+    , [0x43, 0x0a, 0xeb, 0x89, 0xc2, 0xc1, 0x9d, 0xd8, 0x2a]
+    , [0x5a, 0xa3, 0xe3, 0x40, 0x23, 0xce, 0x90, 0xbc, 0xa2]
+    , [0x27, 0xe1, 0x3f, 0xaa, 0xa3, 0x8f, 0xaf, 0x6a, 0x51]
+    , [0x0c, 0x5b, 0xac, 0x36, 0x24, 0x22, 0xa2, 0x7c, 0x24]
+    , [0xea, 0xbc, 0xd7, 0x7c, 0x24, 0x49, 0x58, 0x66, 0x57]
+    , [0x5a, 0x30, 0x62, 0x51, 0x83, 0x9c, 0x97, 0xc3, 0x75]
+    , [0x7a, 0x18, 0x75, 0xd3, 0xc3, 0x88, 0x65, 0x0b, 0x3a]
+    , [0x02, 0x38, 0x25, 0x95, 0x25, 0x0b, 0x3b, 0x0c, 0xa8]
+    , [0x33, 0x1c, 0xae, 0xdd, 0x83, 0xfc, 0x55, 0xf4, 0x63]
+    , [0xcb, 0x87, 0x3d, 0xb9, 0xe5, 0x38, 0x0b, 0xbd, 0xca]
+    , [0x3a, 0x25, 0xba, 0x67, 0xa4, 0xc7, 0x49, 0x80, 0xe9]
+    , [0x49, 0x14, 0x3b, 0x94, 0x05, 0x63, 0xb5, 0xde, 0xd6]
+    , [0x45, 0xfb, 0x0d, 0x28, 0x45, 0x19, 0x66, 0x63, 0x27]
+    , [0x92, 0x7a, 0xe5, 0xa6, 0xe5, 0x88, 0xba, 0x55, 0x68]
+    , [0xc3, 0x93, 0x7c, 0x11, 0xc4, 0x1a, 0x2f, 0xe0, 0x8b]
+    , [0xa1, 0xd8, 0x5e, 0x71, 0xa5, 0x6a, 0x96, 0x5e, 0x64]
+    , [0x77, 0x7b, 0xd7, 0x7d, 0x05, 0xdc, 0x73, 0x64, 0xc3]
+    , [0x3d, 0x3f, 0xd4, 0xd6, 0xa6, 0x71, 0xb2, 0x9d, 0x65]
+    , [0x8d, 0x45, 0x9e, 0xd0, 0x27, 0x66, 0xb4, 0x46, 0x34]
+    , [0xf1, 0xda, 0x16, 0xa1, 0x85, 0xfe, 0x43, 0xa7, 0x30]
+    , [0x97, 0x6b, 0x02, 0xe3, 0x66, 0x10, 0x3d, 0x6e, 0xab]
+    , [0x84, 0x2b, 0xd9, 0x13, 0xa7, 0x25, 0x5a, 0x8a, 0x22]
+    , [0x0c, 0x2b, 0x2f, 0xd2, 0x28, 0x3a, 0x91, 0xdf, 0x95]
+    , [0x1c, 0x01, 0xed, 0xaa, 0x47, 0x2f, 0x64, 0x6a, 0x8b]
+    , [0x71, 0x66, 0x6f, 0x98, 0x47, 0x74, 0x48, 0xeb, 0x39]
+    , [0x2b, 0xb3, 0x61, 0xde, 0xe8, 0x6c, 0x33, 0xfd, 0x32]
+    , [0xc5, 0x33, 0x7e, 0xc3, 0x87, 0x9c, 0x0c, 0x24, 0x4b]
+    , [0xb7, 0x44, 0xea, 0x2c, 0xa8, 0x59, 0xbc, 0xfc, 0xc5]
+    , [0xb2, 0x8c, 0x40, 0x98, 0x67, 0x46, 0x6f, 0xce, 0x35]
+    , [0x04, 0x34, 0x61, 0xb3, 0x87, 0xd4, 0xe5, 0x1a, 0x7f]
+    , [0x20, 0xba, 0x15, 0x17, 0xe8, 0xdd, 0xd9, 0xf6, 0x19]
+    , [0x36, 0xc4, 0xd3, 0xef, 0x88, 0x53, 0x6e, 0x86, 0x56]
+    , [0x32, 0x55, 0xce, 0xf0, 0x48, 0x73, 0xaf, 0x8d, 0x9d]
+    , [0x18, 0x3c, 0xd8, 0x70, 0x08, 0x71, 0x6a, 0x47, 0xa3]
+    , [0xa8, 0xad, 0xfb, 0x29, 0xa8, 0xe4, 0x8e, 0x82, 0xc4]
+    , [0x47, 0x47, 0xbf, 0x58, 0x08, 0x7f, 0xab, 0xbc, 0x34]
+    , [0x8f, 0x68, 0x71, 0x3a, 0xe9, 0x56, 0x8f, 0xd0, 0x0f]
+    , [0x64, 0x5e, 0xdc, 0x14, 0x88, 0x93, 0x8d, 0xb4, 0x35]
+    , [0xb4, 0x33, 0xd0, 0x6d, 0x48, 0xbb, 0x3a, 0x5d, 0x3d]
+    , [0x22, 0xe4, 0x4d, 0x0a, 0x48, 0xbd, 0x95, 0x1e, 0x8e]
+    , [0x78, 0x6e, 0x06, 0x7d, 0x48, 0xce, 0xdb, 0x12, 0x33]
+    , [0x7e, 0x1f, 0x4d, 0x02, 0x48, 0xf8, 0xd2, 0xa5, 0xc2]
+    , [0x90, 0x6b, 0x26, 0x4e, 0xc7, 0xef, 0x12, 0xf2, 0x20]
+    , [0x90, 0x10, 0xa8, 0x21, 0x09, 0x16, 0x22, 0x57, 0xbc]
+    , [0x5b, 0x1a, 0x8c, 0xbc, 0x09, 0x2c, 0xa4, 0x2d, 0x9c]
+    , [0x2c, 0x1a, 0x6e, 0x44, 0xa9, 0x7d, 0x98, 0xe0, 0x39]
+    , [0x8a, 0x2b, 0x35, 0xab, 0x49, 0x97, 0xd5, 0xf7, 0xbc]
+    , [0x29, 0xbc, 0x62, 0x5e, 0x0a, 0x4e, 0x9e, 0x34, 0x40]
+    , [0xe7, 0xe6, 0xc7, 0x00, 0xaa, 0xd3, 0x26, 0x5a, 0x4a]
+    , [0x7e, 0x65, 0xe5, 0x51, 0x2b, 0x66, 0xe8, 0xb5, 0xb2]
+    , [0x2b, 0xd7, 0xcf, 0x6a, 0x6a, 0x6f, 0xb4, 0x56, 0xc9]
+    , [0x33, 0x51, 0x09, 0xaf, 0xab, 0x42, 0x80, 0x8b, 0x07]
+    , [0xd1, 0x78, 0x30, 0x6b, 0xc9, 0x9c, 0xaf, 0x1f, 0xd4]
+    , [0xc6, 0x00, 0xf9, 0xbf, 0x4b, 0x38, 0xf0, 0x00, 0xc6]
+    , [0xa1, 0x81, 0x99, 0x9a, 0xc9, 0xf2, 0x8e, 0x4c, 0x6f]
+    , [0xb3, 0xd1, 0x6c, 0x57, 0x2c, 0x62, 0x3c, 0xb0, 0x5c]
+    , [0xc7, 0x50, 0xc1, 0x4d, 0xec, 0xde, 0x91, 0xe8, 0xfd]
+    , [0x54, 0x51, 0xfe, 0xaa, 0xec, 0xe9, 0x6b, 0xf2, 0xa2]
+    , [0x5d, 0xdc, 0x27, 0xf3, 0x4b, 0xc8, 0xb0, 0x9a, 0xc9]
+    , [0xd2, 0x4d, 0xe1, 0x77, 0x8c, 0x0a, 0x8f, 0x2d, 0x44]
+    , [0xce, 0xe4, 0xe5, 0x80, 0xca, 0x8a, 0x07, 0xdc, 0x67]
+    , [0x0f, 0xd0, 0x8f, 0x67, 0xca, 0x9f, 0x94, 0xa5, 0x86]
+    , [0x89, 0xe9, 0xed, 0xdd, 0xed, 0x84, 0x84, 0x50, 0x26]
+    , [0x76, 0xf5, 0xbe, 0x03, 0xac, 0x82, 0x5a, 0x09, 0x26]
+    , [0xf8, 0x71, 0x02, 0xca, 0x6c, 0x6d, 0xa5, 0xae, 0xfc]
+    , [0xe8, 0xcb, 0x80, 0x56, 0xad, 0x00, 0xa0, 0xd1, 0xd2]
+    , [0x88, 0x89, 0x0c, 0xdd, 0x0c, 0xcf, 0xb2, 0x5d, 0xd7]
+    , [0x69, 0xad, 0x8f, 0xdb, 0x0d, 0x03, 0x8a, 0xca, 0x37]
+    , [0x96, 0x58, 0x24, 0x93, 0xcb, 0xbf, 0xd7, 0xf1, 0xfe]
+    , [0xee, 0xb1, 0xf6, 0xd2, 0x6d, 0x9b, 0x46, 0x7d, 0x3f]
+    , [0xde, 0x64, 0x18, 0xd6, 0x2e, 0x86, 0x5b, 0x3c, 0x0c]
+    , [0x24, 0x05, 0x66, 0xe4, 0x0d, 0xf1, 0xc8, 0xb1, 0xd3]
+    , [0x77, 0xd7, 0x5f, 0x5f, 0xef, 0x81, 0x2d, 0xb3, 0x37]
+    , [0x58, 0x53, 0xf5, 0xb5, 0x2e, 0xec, 0x51, 0xfe, 0x25]
+    , [0xba, 0x65, 0x2a, 0x3b, 0x8e, 0xc6, 0xbb, 0xa8, 0xb6]
+    , [0xe5, 0x81, 0x4f, 0x09, 0xf0, 0x0c, 0xec, 0xf9, 0xfa]
+    , [0x50, 0x5b, 0xe8, 0xf9, 0xf0, 0x6a, 0x47, 0xb4, 0x64]
+    , [0xd3, 0x0e, 0xf4, 0xc7, 0x6e, 0xd9, 0x60, 0x31, 0xb5]
+    , [0x58, 0xe4, 0x4a, 0x3c, 0xcd, 0x97, 0xd6, 0xfd, 0x84]
+    , [0x6f, 0x82, 0xaa, 0xca, 0x2f, 0xe7, 0x1a, 0x8d, 0xb1]
+    , [0x8f, 0x50, 0xec, 0x2f, 0x2f, 0xe7, 0x30, 0x4c, 0xdb]
+    , [0xf0, 0x3b, 0x01, 0xdb, 0x6f, 0x33, 0xdb, 0x82, 0x41]
+    , [0x13, 0x3c, 0x71, 0xe4, 0x8f, 0x75, 0x46, 0x48, 0x8f]
+    , [0x2f, 0xa5, 0xa5, 0x41, 0xcd, 0xf5, 0xb4, 0xc6, 0x45]
+    , [0x8b, 0x1a, 0x4c, 0x12, 0x30, 0x38, 0xc3, 0x61, 0x08]
+    , [0x6f, 0x9c, 0x5c, 0x3f, 0x8f, 0xd6, 0x47, 0x70, 0xf7]
+    , [0xc1, 0x0e, 0x16, 0x18, 0x50, 0x1b, 0x47, 0x48, 0xf8]
+    , [0x2b, 0x94, 0x3f, 0xad, 0x30, 0xad, 0x5e, 0xc4, 0x85]
+    , [0x5f, 0x55, 0x92, 0xc6, 0xce, 0xb9, 0xf5, 0x05, 0x08]
+    , [0x42, 0x8e, 0x3a, 0xbb, 0xf1, 0xbd, 0x08, 0x09, 0x25]
+    , [0x28, 0x84, 0xf5, 0x2b, 0xce, 0xf1, 0x3e, 0x47, 0x41]
+    , [0x25, 0xd1, 0x2c, 0xbf, 0x90, 0x5c, 0x71, 0x24, 0x85]
+    , [0xd0, 0x25, 0x3d, 0xf3, 0x90, 0x64, 0x67, 0xae, 0xf9]
+    , [0xc5, 0x1d, 0xe5, 0x36, 0x11, 0xa9, 0x7b, 0x4d, 0xe0]
+    , [0x88, 0xc2, 0x24, 0xda, 0x50, 0xf9, 0x55, 0x03, 0xdc]
+    , [0xff, 0x54, 0x4f, 0x75, 0xcf, 0x6d, 0xbb, 0xc4, 0x4b]
+    , [0x9e, 0x49, 0xc2, 0x63, 0x11, 0xd7, 0xb0, 0x74, 0x25]
+    , [0xac, 0xb1, 0x42, 0x65, 0xcf, 0x7d, 0x84, 0x0e, 0x37]
+    , [0x05, 0xa5, 0x28, 0x18, 0x11, 0xe3, 0x0d, 0x6a, 0xaa]
+    , [0x03, 0x8d, 0x11, 0xce, 0xf2, 0x6f, 0x0e, 0x0a, 0xcf]
+    , [0xbf, 0xd6, 0x88, 0x3d, 0x51, 0x75, 0x18, 0x67, 0x0e]
+    , [0x69, 0xa2, 0xdd, 0x69, 0xcf, 0xe4, 0x94, 0x5e, 0xe3]
+    , [0xe4, 0x62, 0x0b, 0x95, 0xb1, 0x58, 0xa1, 0xd4, 0x25]
+    , [0xc0, 0x75, 0xc6, 0xec, 0x51, 0x9e, 0xd4, 0xc7, 0x4c]
+    , [0x83, 0x9d, 0xb3, 0x97, 0x32, 0x54, 0x9b, 0x04, 0xec]
+    , [0x9a, 0x7f, 0x71, 0x94, 0x32, 0xcf, 0xbb, 0x97, 0x38]
+    , [0x7a, 0x4f, 0xe4, 0x60, 0x13, 0x93, 0xfd, 0x7d, 0x50]
+    , [0x76, 0x48, 0x71, 0xf5, 0x52, 0x2f, 0xcb, 0xf7, 0xe2]
+    , [0xda, 0x0e, 0x88, 0x1f, 0x13, 0xba, 0xd5, 0xa7, 0x2f]
+    , [0x2a, 0xbe, 0xca, 0xe7, 0x33, 0x12, 0x4e, 0x33, 0x26]
+    , [0x79, 0xce, 0x5e, 0xee, 0x92, 0x27, 0x5d, 0xc7, 0x17]
+    , [0x36, 0x91, 0x77, 0xd3, 0x72, 0x5a, 0xde, 0x9f, 0x0b]
+    , [0x3a, 0x33, 0xcd, 0x8d, 0x52, 0x7a, 0xb8, 0x2a, 0xd8]
+    , [0x25, 0x66, 0xec, 0xc3, 0x52, 0x8b, 0xd0, 0x13, 0x4f]
+    , [0xde, 0xd7, 0xa1, 0x1f, 0xb2, 0x58, 0x65, 0xd8, 0xd7]
+    , [0x8a, 0xe5, 0xcb, 0x54, 0x33, 0xb0, 0xde, 0x92, 0x42]
+    , [0x71, 0x49, 0x0a, 0xbb, 0x33, 0xd8, 0x35, 0xb3, 0x59]
+    , [0x03, 0x36, 0x3b, 0x64, 0xf4, 0x98, 0x4b, 0x21, 0x99]
+    , [0x8c, 0xf5, 0x50, 0x02, 0xd2, 0x10, 0xdb, 0x86, 0x1d]
+    , [0xc3, 0x4f, 0xcf, 0x6e, 0x93, 0x4e, 0xb5, 0xd8, 0xb4]
+    , [0x2a, 0xfb, 0x95, 0x75, 0x73, 0x8c, 0x85, 0x76, 0x82]
+    , [0x35, 0xc0, 0x2a, 0x8b, 0xf5, 0x56, 0x45, 0x66, 0x45]
+    , [0x68, 0x2a, 0x76, 0x44, 0x73, 0xcf, 0x31, 0x3a, 0x8e]
+    , [0x98, 0x4c, 0xa0, 0x05, 0x93, 0x9a, 0xcf, 0x65, 0xa1]
+    , [0xa5, 0x3e, 0x3b, 0xf8, 0x94, 0x0e, 0x6e, 0x74, 0x73]
+    , [0x4b, 0x75, 0x16, 0xe9, 0x35, 0x47, 0xe2, 0xaf, 0x21]
+    , [0x3c, 0x84, 0xfb, 0xee, 0xf6, 0x10, 0x6c, 0x09, 0x3f]
+    , [0x45, 0x52, 0x87, 0x53, 0x74, 0x7c, 0x1b, 0x2c, 0xa1]
+    , [0xa0, 0x8b, 0x28, 0xe0, 0xd3, 0x34, 0xbe, 0x90, 0xcb]
+    , [0x67, 0x57, 0x50, 0xd0, 0x16, 0x3c, 0x23, 0xaf, 0xe1]
+    , [0xa8, 0xa6, 0x4d, 0x44, 0x16, 0x52, 0x57, 0xba, 0x95]
+    , [0x9e, 0x0c, 0xbe, 0x6a, 0x75, 0x1d, 0x3f, 0x6c, 0x20]
+    , [0x14, 0x1b, 0x43, 0x7a, 0xb5, 0x4e, 0x40, 0x88, 0xb0]
+    , [0x68, 0x3b, 0xbd, 0xd6, 0xf7, 0xae, 0xd7, 0xf9, 0x28]
+    , [0xab, 0xc0, 0x61, 0xa3, 0x76, 0x20, 0xb8, 0xf7, 0x3b]
+    , [0x05, 0xe8, 0x00, 0x91, 0xb5, 0xdd, 0xbe, 0x7e, 0xaf]
+    , [0x14, 0xdd, 0x60, 0xa4, 0x96, 0x19, 0x04, 0x05, 0x16]
+    , [0x7e, 0xe5, 0x14, 0xcd, 0x76, 0x87, 0x03, 0x4c, 0x2d]
+    , [0x07, 0xf0, 0x09, 0x29, 0x18, 0x7d, 0x7d, 0xfe, 0x8a]
+    , [0xb9, 0xb4, 0xa5, 0xc6, 0x37, 0x99, 0xfd, 0x49, 0xff]
+    , [0xd2, 0xdd, 0x5a, 0x63, 0x37, 0xe4, 0x08, 0x8e, 0xd4]
+    , [0xa2, 0x0b, 0xf2, 0x5e, 0x76, 0xfd, 0x65, 0x2c, 0x80]
+    , [0xc8, 0xd7, 0xdf, 0xba, 0x37, 0xe8, 0xd9, 0x2b, 0x0c]
+    , [0x6e, 0x40, 0x03, 0xeb, 0x37, 0xea, 0xeb, 0xa6, 0x93]
+    , [0x27, 0xf9, 0x53, 0x0f, 0x37, 0xfc, 0x0e, 0x28, 0x60]
+    , [0xc2, 0x72, 0x49, 0x23, 0xd5, 0xce, 0x7e, 0xbb, 0xef]
+    , [0x3e, 0x11, 0xd8, 0x6b, 0xb6, 0xe9, 0x30, 0x2f, 0xab]
+    , [0x0e, 0xdd, 0xb7, 0xd7, 0x19, 0x45, 0x86, 0xd7, 0x79]
+    , [0xa3, 0xf7, 0x21, 0x85, 0x97, 0x07, 0x7d, 0xe1, 0x4b]
+    , [0x4e, 0x76, 0x21, 0xee, 0x19, 0x67, 0x54, 0x55, 0x23]
+    , [0x0f, 0x19, 0x18, 0xda, 0x38, 0x60, 0x1c, 0x0b, 0x9c]
+    , [0xa4, 0x91, 0x5d, 0x2c, 0x77, 0x7f, 0xb4, 0xcb, 0xc6]
+    , [0xc1, 0xb7, 0xee, 0x41, 0x19, 0x7a, 0x60, 0x4d, 0x7e]
+    , [0x88, 0x47, 0x7b, 0x0c, 0xd6, 0x4c, 0xe0, 0xc1, 0x20]
+    , [0x73, 0x88, 0xeb, 0x4f, 0xd6, 0x53, 0x4f, 0x7c, 0x6a]
+    , [0xf4, 0x5a, 0xe5, 0xb3, 0xf9, 0xcf, 0x96, 0x88, 0x8f]
+    , [0xba, 0x5c, 0xb1, 0x28, 0x58, 0x2f, 0x33, 0x16, 0xcc]
+    , [0x04, 0xcd, 0x1f, 0xc8, 0x1a, 0x7a, 0x20, 0xf1, 0xd0]
+    , [0x5f, 0x2c, 0x1c, 0x63, 0xb8, 0x48, 0x83, 0x82, 0xa4]
+    , [0x22, 0xf5, 0xed, 0xa6, 0x98, 0x51, 0xeb, 0xa3, 0xf8]
+    , [0xf8, 0x6b, 0x6d, 0x33, 0xfa, 0x65, 0x24, 0xbd, 0x5c]
+    , [0x10, 0x3b, 0xb6, 0xae, 0xd7, 0x86, 0x73, 0x25, 0xd3]
+    , [0x52, 0x31, 0x6c, 0x5a, 0x79, 0x03, 0xbe, 0x55, 0x95]
+    , [0xaf, 0xc9, 0xab, 0x5e, 0x1b, 0x54, 0xde, 0x1d, 0x84]
+    , [0xc2, 0xed, 0xf4, 0x36, 0x79, 0x77, 0x81, 0x0d, 0xee]
+    , [0x36, 0xf5, 0xdb, 0xeb, 0xd8, 0x30, 0xd9, 0x69, 0xf6]
+    , [0x43, 0x84, 0x92, 0xb9, 0x79, 0x90, 0x66, 0xa4, 0xe6]
+    , [0x7d, 0xe9, 0x4d, 0x0e, 0x1b, 0xda, 0x69, 0xeb, 0x57]
+    , [0x2d, 0x03, 0x20, 0x62, 0xfb, 0x3b, 0x6e, 0x88, 0x65]
+    , [0xe5, 0x69, 0xec, 0xd2, 0x79, 0xbe, 0xd8, 0xaa, 0x8e]
+    , [0xac, 0x26, 0x0a, 0xdd, 0xfb, 0x68, 0x43, 0x1f, 0x6d]
+    , [0x3c, 0x96, 0xc1, 0xbe, 0x3a, 0xbe, 0x22, 0xae, 0x76]
+    , [0xde, 0x73, 0x09, 0xf1, 0x1c, 0x39, 0x67, 0x8a, 0x0c]
+    , [0x56, 0x2e, 0x5f, 0x2c, 0x3a, 0xfd, 0x30, 0x40, 0x7d]
+    , [0x68, 0x57, 0xce, 0x1c, 0x59, 0xe0, 0xe0, 0xeb, 0x04]
+    , [0xdf, 0xf7, 0xe6, 0x75, 0x1c, 0x66, 0x88, 0x86, 0x90]
+    , [0xfd, 0xaf, 0xf5, 0x8b, 0x5a, 0x06, 0xae, 0x0b, 0x40]
+    , [0x4e, 0xcf, 0xa7, 0xe9, 0x1c, 0x93, 0xaf, 0x36, 0xa8]
+    , [0x56, 0xdc, 0x13, 0xcf, 0xfc, 0x12, 0x88, 0xbb, 0xb8]
+    , [0x1f, 0x5f, 0xc2, 0xb2, 0x3b, 0x75, 0xfb, 0x7c, 0x92]
+    , [0x4e, 0x9c, 0xbb, 0xba, 0x5a, 0x74, 0xe3, 0xa4, 0xe1]
+    , [0x0b, 0xa0, 0xc8, 0xc5, 0x1d, 0x99, 0x06, 0x87, 0x5e]
+    , [0xff, 0xef, 0xb0, 0x25, 0x1d, 0xf4, 0x9d, 0xdf, 0xdb]
+    , [0x09, 0x67, 0xcb, 0x8f, 0x7c, 0x13, 0x3c, 0x75, 0x97]
+    , [0x05, 0xbf, 0xfe, 0x4d, 0xfd, 0xd2, 0x6e, 0x61, 0x64]
+    , [0xa4, 0xd7, 0x4b, 0x6d, 0x3d, 0x2a, 0xbb, 0xee, 0x4c]
+    , [0xb9, 0x8c, 0x01, 0xcb, 0x5b, 0xf2, 0x50, 0x15, 0xf0]
+    , [0x31, 0x45, 0x17, 0x91, 0x1e, 0xa3, 0x7f, 0x25, 0x52]
+    , [0x10, 0x6c, 0x0c, 0x98, 0xdb, 0x0f, 0x9b, 0x1e, 0x88]
+    , [0x06, 0xec, 0xcc, 0xb6, 0x9b, 0xda, 0xa2, 0x3a, 0x16]
+    , [0x23, 0x66, 0x38, 0x19, 0x7c, 0xac, 0x80, 0xd3, 0x78]
+    , [0xb8, 0xf3, 0xd7, 0x98, 0xbc, 0x11, 0x60, 0x0f, 0xbf]
+    , [0x27, 0x24, 0x2d, 0xa8, 0xdb, 0xc2, 0x99, 0x28, 0x65]
+    , [0x64, 0x61, 0x39, 0xc2, 0xbc, 0x79, 0xb5, 0xb1, 0xa6]
+    , [0xdf, 0xff, 0x2a, 0x12, 0x5c, 0xfb, 0x42, 0x6d, 0x01]
+    , [0xaf, 0x1d, 0xea, 0x4e, 0xff, 0x0d, 0x46, 0x95, 0x5e]
+    , [0x2a, 0xb0, 0xc6, 0x54, 0x9c, 0xe9, 0x98, 0x43, 0xe1]
+    , [0x7c, 0xe5, 0x9e, 0x74, 0x7d, 0xc8, 0x44, 0xef, 0x9e]
+    , [0x56, 0x14, 0xd1, 0x31, 0xdc, 0xce, 0x77, 0x17, 0xc3]
+    , [0xed, 0x6b, 0x65, 0x68, 0x3f, 0x05, 0x36, 0x41, 0x9b]
+    , [0x2f, 0xd2, 0x24, 0x7e, 0xdd, 0x19, 0x4f, 0x91, 0x72]
+    , [0xc8, 0x96, 0xf0, 0x54, 0x9d, 0x92, 0x5b, 0xcc, 0xb1]
+    , [0x47, 0x5d, 0x76, 0x63, 0xbe, 0x17, 0x24, 0xad, 0xd4]
+    , [0x4f, 0x88, 0x0b, 0x03, 0x9e, 0x4a, 0x48, 0xee, 0x14]
+    , [0x26, 0xec, 0x1b, 0x56, 0x5e, 0xb8, 0x5d, 0x5e, 0x39]
+    , [0xc3, 0xa8, 0x2d, 0x1a, 0x5e, 0xfc, 0xb7, 0x81, 0x49]
+    , [0x14, 0x3a, 0x3b, 0x0b, 0xbe, 0xe7, 0xb2, 0x4d, 0x75]
+    , [0x27, 0xa0, 0xf4, 0x22, 0xdf, 0xb5, 0xb6, 0x5e, 0x4a]
+    ]
+
+-- -------------------------------------------------------------------------- --
+-- 64 bit FNV1a
+
+tests64a :: Bool
+tests64a = all test64a testVectors64a
+    && all testZero64a zeros64a
+
+test64a :: (B.ByteString, Word64) -> Bool
+test64a (b, r) = hashByteString fnv1a_64 b == r
+
+testZero64a :: B.ByteString -> Bool
+testZero64a b = hashByteString fnv1a_64 b == 0
+
+testVectors64a :: [(B.ByteString, Word64)]
+testVectors64a = []
+
+-- | All FNV1a 64 bit inputs that result in a hash of 0 up to a length of eight
+-- bytes.
+--
+-- (cf. http://www.isthe.com/chongo/tech/comp/fnv/)
+--
+zeros64a :: [B.ByteString]
+zeros64a = B.pack <$>
+    [ [0xd5, 0x6b, 0xb9, 0x53, 0x42, 0x87, 0x08, 0x36]
+    ]
+
+-- -------------------------------------------------------------------------- --
+-- 32 bit FNV1
+
+tests32 :: Bool
+tests32 = all test32 testVectors32
+    && all testZero32 zeros32
+    && testZero32 zero32ff
+
+test32 :: (B.ByteString, Word32) -> Bool
+test32 (b, r) = hashByteString fnv1_32 b == r
+
+testVectors32 :: [(B.ByteString, Word32)]
+testVectors32 = []
+
+testZero32 :: B.ByteString -> Bool
+testZero32 b = hashByteString fnv1_32 b == 0
+
+-- | Two out of 254 inputs of up to 5 bytes length that result in a
+-- fnv1 32 bit hash of 0.
+--
+-- (cf. http://www.isthe.com/chongo/tech/comp/fnv/)
+--
+zeros32 :: [B.ByteString]
+zeros32 = B.pack <$>
+    [ [0x01, 0x47, 0x6c, 0x10, 0xf3]
+    , [0xfd, 0x45, 0x41, 0x08, 0xa0]
+    ]
+
+-- | The shortest set of consecutive 0xff octets for which the 32-bit FNV-1 hash
+-- is 0.
+--
+-- (cf. http://www.isthe.com/chongo/tech/comp/fnv/)
+--
+zero32ff :: B.ByteString
+zero32ff = B.replicate 428876705 0xff
+
+-- -------------------------------------------------------------------------- --
+-- 32 bit FNV1a
+
+tests32a :: Bool
+tests32a = all test32a testVectors32a
+    && all testZero32a zeros32a
+    && testZero32a zero32ffa
+
+test32a :: (B.ByteString, Word32) -> Bool
+test32a (b, r) = hashByteString fnv1a_32 b == r
+
+testVectors32a :: [(B.ByteString, Word32)]
+testVectors32a = []
+
+testZero32a :: B.ByteString -> Bool
+testZero32a b = hashByteString fnv1a_32 b == 0
+
+-- | All FNV1a 32 bit inputs that result in a hash of 0 up to a length of four
+-- bytes.
+--
+-- (cf. http://www.isthe.com/chongo/tech/comp/fnv/)
+--
+zeros32a :: [B.ByteString]
+zeros32a = B.pack <$>
+    [ [0xcc, 0x24, 0x31, 0xc4]
+    , [0xe0, 0x4d, 0x9f, 0xcb]
+    ]
+
+zero32ffa :: B.ByteString
+zero32ffa = B.replicate 3039744951 0xff
+
+-- -------------------------------------------------------------------------- --
+-- Primitive FNV1
+
+primitiveFnv1 :: B.ByteString -> Word
+primitiveFnv1 = hashByteString $ \(Ptr addr) n ->
+    fromIntegral <$> fnv1 addr n
+{-# INLINE primitiveFnv1 #-}
+
+testsPrim :: Bool
+testsPrim = all testPrim testVectorsPrim
+    && all testZeroPrim zerosPrim
+
+testPrim :: (B.ByteString, Word) -> Bool
+testPrim (b, r) = primitiveFnv1 b == r
+
+testZeroPrim :: B.ByteString -> Bool
+testZeroPrim b = primitiveFnv1 b == 0
+
+testVectorsPrim :: [(B.ByteString, Word)]
+#if defined(x86_64_HOST_ARCH)
+testVectorsPrim = second fromIntegral <$> testVectors64
+#elif defined(i386_HOST_ARCH)
+testVectorsPrim = second fromIntegral <$> testVectors32
+#else
+testVectorsPrim = error "testVectorsPrim: unsupported hardware platform"
+#endif
+
+zerosPrim :: [B.ByteString]
+#if defined(x86_64_HOST_ARCH)
+zerosPrim = zeros64
+#elif defined(i386_HOST_ARCH)
+zerosPrim = zeros32
+#else
+zerosPrim = error "zerosPrim: unsupported hardware platform"
+#endif
+
+-- -------------------------------------------------------------------------- --
+-- Primitive FNV1a
+
+primitiveFnv1a :: B.ByteString -> Word
+primitiveFnv1a = hashByteString $ \(Ptr addr) n ->
+    fromIntegral <$> fnv1a addr n
+{-# INLINE primitiveFnv1a #-}
+
+testsPrima :: Bool
+testsPrima = all testPrima testVectorsPrima
+    && all testZeroPrima zerosPrima
+
+testPrima :: (B.ByteString, Word) -> Bool
+testPrima (b, r) = primitiveFnv1a b == r
+
+testZeroPrima :: B.ByteString -> Bool
+testZeroPrima b = primitiveFnv1a b == 0
+
+testVectorsPrima :: [(B.ByteString, Word)]
+#if defined(x86_64_HOST_ARCH)
+testVectorsPrima = second fromIntegral <$> testVectors64a
+#elif defined(i386_HOST_ARCH)
+testVectorsPrima = second fromIntegral <$> testVectors32a
+#endif
+
+zerosPrima :: [B.ByteString]
+#if defined(x86_64_HOST_ARCH)
+zerosPrima = zeros64a
+#elif defined(i386_HOST_ARCH)
+zerosPrima = zeros32a
+#endif
+
diff --git a/test/Test/Data/Hash/SipHash.hs b/test/Test/Data/Hash/SipHash.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Data/Hash/SipHash.hs
@@ -0,0 +1,151 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
+
+-- |
+-- Module: Test.Data.Hash.SipHash
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Test.Data.Hash.SipHash
+( tests
+) where
+
+import qualified Data.ByteString as B
+
+import Foreign
+
+import System.IO.Unsafe
+
+import Test.QuickCheck
+
+-- internal modules
+
+import Data.Hash.SipHash
+
+-- -------------------------------------------------------------------------- --
+-- Utils
+
+bytesToWord64:: [Word8] -> Word64
+bytesToWord64 l = unsafePerformIO $
+    B.useAsCStringLen (B.pack l) $ \(b, _) -> peek @Word64 (castPtr b)
+
+-- -------------------------------------------------------------------------- --
+-- Tests
+
+-- internal sip hash implementation
+tests :: IO ()
+tests = quickCheck $ all test [0..63]
+
+test :: Int -> Bool
+test i = hashByteString (uncurry sipHash24 testKey) (testInput i) == (testVectors !! i)
+
+-- -------------------------------------------------------------------------- --
+-- Test Vectors from https://svn.grid.pub.ro/svn/bhyve-save-restore/trunk/sys/crypto/siphash/siphash_test.c
+
+{-
+/*-
+ * Test Vectors from the SipHash reference C implementation:
+ *
+ * Written in 2012 by
+ * Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
+ * Daniel J. Bernstein <djb@cr.yp.to>
+ *
+ * Adjusted by Andre Oppermann <andre@freebsd.org> to use function calls in
+ * line with other hash implementations.
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ *
+ * SipHash-2-4 output with
+ * k = 00 01 02 ...
+ * and
+ * in = (empty string)
+ * in = 00 (1 byte)
+ * in = 00 01 (2 bytes)
+ * in = 00 01 02 (3 bytes)
+ * ...
+ * in = 00 01 02 ... 3e (63 bytes)
+ */
+-}
+
+testKey :: (Word64, Word64)
+testKey = (bytesToWord64 [0..7], bytesToWord64 [8..15])
+
+testInput :: Int -> B.ByteString
+testInput i = B.pack $ fromIntegral <$> [0..i-1]
+
+testVectors :: [Word64]
+testVectors = bytesToWord64 <$>
+    [ [ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72 ]
+    , [ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74 ]
+    , [ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d ]
+    , [ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85 ]
+    , [ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf ]
+    , [ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18 ]
+    , [ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb ]
+    , [ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab ]
+    , [ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93 ]
+    , [ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e ]
+    , [ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a ]
+    , [ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4 ]
+    , [ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75 ]
+    , [ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14 ]
+    , [ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7 ]
+    , [ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1 ]
+    , [ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f ]
+    , [ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69 ]
+    , [ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b ]
+    , [ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb ]
+    , [ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe ]
+    , [ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0 ]
+    , [ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93 ]
+    , [ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8 ]
+    , [ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8 ]
+    , [ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc ]
+    , [ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17 ]
+    , [ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f ]
+    , [ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde ]
+    , [ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6 ]
+    , [ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad ]
+    , [ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32 ]
+    , [ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71 ]
+    , [ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7 ]
+    , [ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12 ]
+    , [ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15 ]
+    , [ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31 ]
+    , [ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02 ]
+    , [ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca ]
+    , [ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a ]
+    , [ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e ]
+    , [ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad ]
+    , [ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18 ]
+    , [ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4 ]
+    , [ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9 ]
+    , [ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9 ]
+    , [ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb ]
+    , [ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0 ]
+    , [ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6 ]
+    , [ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7 ]
+    , [ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee ]
+    , [ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1 ]
+    , [ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a ]
+    , [ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81 ]
+    , [ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f ]
+    , [ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24 ]
+    , [ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7 ]
+    , [ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea ]
+    , [ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60 ]
+    , [ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66 ]
+    , [ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c ]
+    , [ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f ]
+    , [ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5 ]
+    , [ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95 ]
+    ]
diff --git a/test/Test/Data/Hash/Utils.hs b/test/Test/Data/Hash/Utils.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Data/Hash/Utils.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+-- |
+-- Module: Test.Data.Hash.Utils
+-- Copyright: Copyright © 2021 Lars Kuhtz <lakuhtz@gmail.com>
+-- License: MIT
+-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
+-- Stability: experimental
+--
+module Test.Data.Hash.Utils
+( tests
+) where
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Unsafe as B
+
+import Foreign.Marshal
+import Foreign.Ptr
+import Foreign.Storable
+
+import GHC.Exts
+import GHC.IO
+import GHC.Word
+
+import Test.QuickCheck
+
+-- internal modules
+
+import Data.Hash.Utils
+
+-- -------------------------------------------------------------------------- --
+--
+
+tests :: IO ()
+tests = do
+    putStrLn "prop_hashByteString"
+    quickCheck prop_hashByteString
+    putStrLn "prop_hashStorable"
+    quickCheck prop_hashStorable
+    putStrLn "prop_hashPtr"
+    quickCheck prop_hashPtr
+    putStrLn "prop_hashByteArray"
+    quickCheck prop_hashByteArray
+
+ptrToList :: Ptr Word8 -> Int -> IO [Word8]
+ptrToList = flip peekArray
+
+prop_hashStorable :: Word64 -> Property
+prop_hashStorable b = hashStorable (\ptr _ -> peek (castPtr ptr)) b === b
+
+prop_hashPtr :: [Word8] -> Property
+prop_hashPtr b = unsafeDupablePerformIO $
+    B.unsafeUseAsCStringLen (B.pack b) $ \(ptr, len) -> do
+        return $ hashPtr ptrToList (castPtr ptr) len === b
+
+prop_hashByteString :: [Word8] -> Property
+prop_hashByteString b = hashByteString ptrToList (B.pack b) === b
+
+prop_hashByteArray :: [Word8] -> Property
+prop_hashByteArray bytes = unsafeDupablePerformIO $ IO $ \s0 ->
+    case newPinnedByteArray# size s0 of
+        (# s1, a# #) ->
+            case copyToArray 0# bytes a# s1 of
+                s2 -> case unsafeFreezeByteArray# a# s2 of
+                    (# s3, b# #) ->
+                        let r = hashByteArray ptrToList b# === bytes
+                        in (# s3, r #)
+  where
+    !(I# size) = length bytes
+
+    copyToArray _ [] _ s = s
+    copyToArray i ((W8# h):t) a s = case writeWord8Array# a i h s of
+        s' -> copyToArray (i +# 1#) t a s'
