module Data.Digest.Murmur32
( Hash32, asWord32,
Hashable32(..),
hash32AddWord32, hash32AddInt, hash32, hash32WithSeed
)
where
import Data.Word
import Numeric ( showHex )
import Data.Bits
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Char ( ord )
import Data.Foldable
import Data.List ( unfoldr )
newtype Hash32 = Hash32 Word32
deriving (Eq, Ord, Bounded)
instance Show Hash32 where
showsPrec _ (Hash32 w) = showString "Hash32 0x" . showHex w
asWord32 :: Hash32 -> Word32
asWord32 (Hash32 w) = w
class Hashable32 a where
hash32Add :: a -> Hash32 -> Hash32
murmur_m :: Word32
murmur_m = 0x5bd1e995
murmur_r :: Int
murmur_r = 24
hash32AddWord32 :: Word32 -> Hash32 -> Hash32
hash32AddWord32 k (Hash32 h) =
let k1 = k * murmur_m
k2 = k1 `xor` (k1 `shiftR` murmur_r)
k3 = k2 * murmur_m
h1 = h * murmur_m
h2 = h1 `xor` k3
in Hash32 h2
hash32AddInt :: Int -> Hash32 -> Hash32
hash32AddInt !k0
| bitSize (undefined :: Int) <= 32
= hash32AddWord32 (fromIntegral k0)
| otherwise
= hash32AddWord32 (fromIntegral k0) `combine`
hash32AddWord32 (fromIntegral (k0 `shiftR` 32))
hash32AddFoldable :: (Hashable32 a, Foldable c) => c a -> Hash32 -> Hash32
hash32AddFoldable c !h0 = foldl' f h0 c
where f h a = hash32Add a h
hash32WithSeed :: Hashable32 a => Word32 -> a -> Hash32
hash32WithSeed seed a = hash32End (hash32Add a (Hash32 seed))
hash32 :: Hashable32 a => a -> Hash32
hash32 = hash32WithSeed defaultSeed
combine :: (Hash32 -> Hash32) -> (Hash32 -> Hash32) -> (Hash32 -> Hash32)
combine x y = y . x
hash32End :: Hash32 -> Hash32
hash32End (Hash32 h) =
let h1 = h `xor` (h `shiftR` 13)
h2 = h1 * murmur_m
h3 = h2 `xor` (h2 `shiftR` 15)
in Hash32 h3
defaultSeed :: Word32
defaultSeed = 0xdeadbeef
instance Hashable32 Char where
hash32Add c = hash32AddInt (ord c)
instance Hashable32 Int where
hash32Add = hash32AddInt
instance Hashable32 Word32 where
hash32Add = hash32AddWord32
instance Hashable32 a => Hashable32 [a] where
hash32Add = hash32AddFoldable
instance Hashable32 Integer where
hash32Add i0
| i0 >= fromIntegral (minBound :: Int) &&
i0 <= fromIntegral (maxBound :: Int)
= hash32AddInt (fromIntegral i0)
| otherwise
= hash32Add (signum i0 > 0) `combine`
hash32AddFoldable (unfoldr f (abs i0) :: [Word32])
where
f i | i == 0 = Nothing
f i =
let (i', a) = quotRem i maxWord in
Just (fromIntegral a, i')
maxWord = fromIntegral (maxBound :: Word32) + 1 :: Integer
instance Hashable32 Bool where
hash32Add False = hash32AddWord32 1
hash32Add True = hash32AddWord32 2
instance Hashable32 a => Hashable32 (Maybe a) where
hash32Add Nothing = hash32AddWord32 3
hash32Add (Just a) = hash32AddWord32 4 `combine` hash32Add a
instance (Hashable32 a, Hashable32 b) => Hashable32 (Either a b) where
hash32Add (Left a) = hash32AddWord32 5 `combine` hash32Add a
hash32Add (Right b) = hash32AddWord32 6 `combine` hash32Add b
instance Hashable32 () where
hash32Add () = hash32AddWord32 7
instance (Hashable32 a, Hashable32 b) => Hashable32 (a, b) where
hash32Add (a, b) = hash32Add a `combine` hash32Add b
instance (Hashable32 a, Hashable32 b, Hashable32 c)
=> Hashable32 (a, b, c) where
hash32Add (a, b, c) =
hash32Add a `combine` hash32Add b `combine` hash32Add c
instance (Hashable32 a, Hashable32 b, Hashable32 c, Hashable32 d)
=> Hashable32 (a, b, c, d) where
hash32Add (a, b, c, d) =
hash32Add a `combine` hash32Add b `combine`
hash32Add c `combine` hash32Add d
instance Hashable32 B.ByteString where
hash32Add = B.foldl go (hash32AddWord32 8)
where go acc b = acc `combine` hash32AddWord32 (fromIntegral b)
instance Hashable32 L.ByteString where
hash32Add = L.foldl go (hash32AddWord32 9)
where go acc b = acc `combine` hash32AddWord32 (fromIntegral b)