#ifdef GENERICS
#endif
module Data.Hashable.Class
(
Hashable(..)
#ifdef GENERICS
, GHashable(..)
#endif
, hashUsing
, hashPtr
, hashPtrWithSalt
, hashByteArray
, hashByteArrayWithSalt
) where
import Control.Exception (assert)
import Data.Bits (bitSize, shiftL, shiftR, xor)
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Unsafe as B
import Data.Int (Int8, Int16, Int32, Int64)
import Data.List (foldl')
import Data.Ratio (Ratio, denominator, numerator)
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 Data.Typeable
import Data.Word (Word, Word8, Word16, Word32, Word64)
import Foreign.C (CString)
import Foreign.Marshal.Utils (with)
import Foreign.Ptr (Ptr, castPtr)
import Foreign.Storable (alignment, peek, sizeOf)
import GHC.Base (ByteArray#)
import GHC.Conc (ThreadId(..))
import GHC.Prim (ThreadId#)
import System.IO.Unsafe (unsafePerformIO)
import System.Mem.StableName
#ifdef GENERICS
import GHC.Generics
#endif
#if __GLASGOW_HASKELL__ >= 702
import Data.Typeable.Internal(TypeRep(..))
import GHC.Fingerprint.Type(Fingerprint(..))
#endif
#if __GLASGOW_HASKELL__ >= 703
import Foreign.C (CLong(..))
import Foreign.C.Types (CInt(..))
#else
import Foreign.C (CLong)
import Foreign.C.Types (CInt)
#endif
#if !MIN_VERSION_bytestring(0,10,0)
import qualified Data.ByteString.Lazy.Internal as BL
#endif
#if MIN_VERSION_bytestring(0,10,4)
import qualified Data.ByteString.Short.Internal as BSI
#endif
#ifdef VERSION_integer_gmp
import GHC.Exts (Int(..))
import GHC.Integer.GMP.Internals (Integer(..))
#endif
#include "MachDeps.h"
infixl 0 `hashWithSalt`
defaultSalt :: Int
#if WORD_SIZE_IN_BITS == 64
defaultSalt = 0xdc36d1615b7400a4
#else
defaultSalt = 0x087fc72c
#endif
class Hashable a where
hashWithSalt :: Int -> a -> Int
hash :: a -> Int
hash = hashWithSalt defaultSalt
#ifdef GENERICS
default hashWithSalt :: (Generic a, GHashable (Rep a)) => Int -> a -> Int
hashWithSalt salt = ghashWithSalt salt . from
class GHashable f where
ghashWithSalt :: Int -> f a -> Int
#endif
defaultHashWithSalt :: Hashable a => Int -> a -> Int
defaultHashWithSalt salt x = salt `combine` hash x
hashUsing :: (Hashable b) =>
(a -> b)
-> Int
-> a
-> Int
hashUsing f salt x = hashWithSalt salt (f x)
instance Hashable Int where
hash = id
hashWithSalt = defaultHashWithSalt
instance Hashable Int8 where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Int16 where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Int32 where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Int64 where
hash n
| bitSize (undefined :: Int) == 64 = fromIntegral n
| otherwise = fromIntegral (fromIntegral n `xor`
(fromIntegral n `shiftR` 32 :: Word64))
hashWithSalt = defaultHashWithSalt
instance Hashable Word where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Word8 where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Word16 where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Word32 where
hash = fromIntegral
hashWithSalt = defaultHashWithSalt
instance Hashable Word64 where
hash n
| bitSize (undefined :: Int) == 64 = fromIntegral n
| otherwise = fromIntegral (n `xor` (n `shiftR` 32))
hashWithSalt = defaultHashWithSalt
instance Hashable () where
hash = fromEnum
hashWithSalt = defaultHashWithSalt
instance Hashable Bool where
hash = fromEnum
hashWithSalt = defaultHashWithSalt
instance Hashable Ordering where
hash = fromEnum
hashWithSalt = defaultHashWithSalt
instance Hashable Char where
hash = fromEnum
hashWithSalt = defaultHashWithSalt
instance Hashable Integer where
#if defined(VERSION_integer_gmp)
hash (S# int) = I# int
hash n@(J# size# byteArray)
| n >= minInt && n <= maxInt = fromInteger n :: Int
| otherwise = let size = I# size#
numBytes = SIZEOF_HSWORD * abs size
in hashByteArrayWithSalt byteArray 0 numBytes defaultSalt
`hashWithSalt` size
where minInt = fromIntegral (minBound :: Int)
maxInt = fromIntegral (maxBound :: Int)
hashWithSalt salt (S# n) = hashWithSalt salt (I# n)
hashWithSalt salt n@(J# size# byteArray)
| n >= minInt && n <= maxInt = hashWithSalt salt (fromInteger n :: Int)
| otherwise = let size = I# size#
numBytes = SIZEOF_HSWORD * abs size
in hashByteArrayWithSalt byteArray 0 numBytes salt
`hashWithSalt` size
where minInt = fromIntegral (minBound :: Int)
maxInt = fromIntegral (maxBound :: Int)
#else
hashWithSalt salt = foldl' hashWithSalt salt . go
where
go n | inBounds n = [fromIntegral n :: Int]
| otherwise = fromIntegral n : go (n `shiftR` WORD_SIZE_IN_BITS)
maxInt = fromIntegral (maxBound :: Int)
inBounds x = x >= fromIntegral (minBound :: Int) && x <= maxInt
#endif
instance (Integral a, Hashable a) => Hashable (Ratio a) where
hash a = hash (numerator a) `hashWithSalt` denominator a
hashWithSalt s a = s `hashWithSalt` numerator a `hashWithSalt` denominator a
instance Hashable Float where
hash x
| isIEEE x =
assert (sizeOf x >= sizeOf (0::Word32) &&
alignment x >= alignment (0::Word32)) $
hash ((unsafePerformIO $ with x $ peek . castPtr) :: Word32)
| otherwise = hash (show x)
hashWithSalt = defaultHashWithSalt
instance Hashable Double where
hash x
| isIEEE x =
assert (sizeOf x >= sizeOf (0::Word64) &&
alignment x >= alignment (0::Word64)) $
hash ((unsafePerformIO $ with x $ peek . castPtr) :: Word64)
| otherwise = hash (show x)
hashWithSalt = defaultHashWithSalt
distinguisher :: Int
distinguisher = fromIntegral $ (maxBound :: Word) `quot` 3
instance Hashable a => Hashable (Maybe a) where
hash Nothing = 0
hash (Just a) = distinguisher `hashWithSalt` a
hashWithSalt s Nothing = s `combine` 0
hashWithSalt s (Just a) = s `combine` distinguisher `hashWithSalt` a
instance (Hashable a, Hashable b) => Hashable (Either a b) where
hash (Left a) = 0 `hashWithSalt` a
hash (Right b) = distinguisher `hashWithSalt` b
hashWithSalt s (Left a) = s `combine` 0 `hashWithSalt` a
hashWithSalt s (Right b) = s `combine` distinguisher `hashWithSalt` b
instance (Hashable a1, Hashable a2) => Hashable (a1, a2) where
hash (a1, a2) = hash a1 `hashWithSalt` a2
hashWithSalt s (a1, a2) = s `hashWithSalt` a1 `hashWithSalt` a2
instance (Hashable a1, Hashable a2, Hashable a3) => Hashable (a1, a2, a3) where
hash (a1, a2, a3) = hash a1 `hashWithSalt` a2 `hashWithSalt` a3
hashWithSalt s (a1, a2, a3) = s `hashWithSalt` a1 `hashWithSalt` a2
`hashWithSalt` a3
instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4) =>
Hashable (a1, a2, a3, a4) where
hash (a1, a2, a3, a4) = hash a1 `hashWithSalt` a2
`hashWithSalt` a3 `hashWithSalt` a4
hashWithSalt s (a1, a2, a3, a4) = s `hashWithSalt` a1 `hashWithSalt` a2
`hashWithSalt` a3 `hashWithSalt` a4
instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5)
=> Hashable (a1, a2, a3, a4, a5) where
hash (a1, a2, a3, a4, a5) =
hash a1 `hashWithSalt` a2 `hashWithSalt` a3
`hashWithSalt` a4 `hashWithSalt` a5
hashWithSalt s (a1, a2, a3, a4, a5) =
s `hashWithSalt` a1 `hashWithSalt` a2 `hashWithSalt` a3
`hashWithSalt` a4 `hashWithSalt` a5
instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5,
Hashable a6) => Hashable (a1, a2, a3, a4, a5, a6) where
hash (a1, a2, a3, a4, a5, a6) =
hash a1 `hashWithSalt` a2 `hashWithSalt` a3
`hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6
hashWithSalt s (a1, a2, a3, a4, a5, a6) =
s `hashWithSalt` a1 `hashWithSalt` a2 `hashWithSalt` a3
`hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6
instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4, Hashable a5,
Hashable a6, Hashable a7) =>
Hashable (a1, a2, a3, a4, a5, a6, a7) where
hash (a1, a2, a3, a4, a5, a6, a7) =
hash a1 `hashWithSalt` a2 `hashWithSalt` a3
`hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6 `hashWithSalt` a7
hashWithSalt s (a1, a2, a3, a4, a5, a6, a7) =
s `hashWithSalt` a1 `hashWithSalt` a2 `hashWithSalt` a3
`hashWithSalt` a4 `hashWithSalt` a5 `hashWithSalt` a6 `hashWithSalt` a7
instance Hashable (StableName a) where
hash = hashStableName
hashWithSalt = defaultHashWithSalt
instance Hashable a => Hashable [a] where
hashWithSalt = foldl' hashWithSalt
instance Hashable B.ByteString where
hashWithSalt salt bs = B.inlinePerformIO $
B.unsafeUseAsCStringLen bs $ \(p, len) ->
hashPtrWithSalt p (fromIntegral len) salt
instance Hashable BL.ByteString where
hashWithSalt = BL.foldlChunks hashWithSalt
#if MIN_VERSION_bytestring(0,10,4)
instance Hashable BSI.ShortByteString where
#if MIN_VERSION_base(4,3,0)
hashWithSalt salt sbs@(BSI.SBS ba) =
#else
hashWithSalt salt sbs@(BSI.SBS ba _) =
#endif
hashByteArrayWithSalt ba 0 (BSI.length sbs) salt
#endif
instance Hashable T.Text where
hashWithSalt salt (T.Text arr off len) =
hashByteArrayWithSalt (TA.aBA arr) (off `shiftL` 1) (len `shiftL` 1)
salt
instance Hashable TL.Text where
hashWithSalt = TL.foldlChunks hashWithSalt
hashThreadId :: ThreadId -> Int
hashThreadId (ThreadId t) = hash (fromIntegral (getThreadId t) :: Int)
foreign import ccall unsafe "rts_getThreadId" getThreadId
:: ThreadId# -> CInt
instance Hashable ThreadId where
hash = hashThreadId
hashWithSalt = defaultHashWithSalt
hashTypeRep :: TypeRep -> Int
#if __GLASGOW_HASKELL__ >= 702
hashTypeRep (TypeRep (Fingerprint x _) _ _) = fromIntegral x
#elif __GLASGOW_HASKELL__ >= 606
hashTypeRep = B.inlinePerformIO . typeRepKey
#else
hashTypeRep = hash . show
#endif
instance Hashable TypeRep where
hash = hashTypeRep
hashWithSalt = defaultHashWithSalt
hashPtr :: Ptr a
-> Int
-> IO Int
hashPtr p len = hashPtrWithSalt p len defaultSalt
hashPtrWithSalt :: Ptr a
-> Int
-> Int
-> IO Int
hashPtrWithSalt p len salt =
fromIntegral `fmap` c_hashCString (castPtr p) (fromIntegral len)
(fromIntegral salt)
foreign import ccall unsafe "hashable_fnv_hash" c_hashCString
:: CString -> CLong -> CLong -> IO CLong
hashByteArray :: ByteArray#
-> Int
-> Int
-> Int
hashByteArray ba0 off len = hashByteArrayWithSalt ba0 off len defaultSalt
hashByteArrayWithSalt
:: ByteArray#
-> Int
-> Int
-> Int
-> Int
hashByteArrayWithSalt ba !off !len !h =
fromIntegral $ c_hashByteArray ba (fromIntegral off) (fromIntegral len)
(fromIntegral h)
foreign import ccall unsafe "hashable_fnv_hash_offset" c_hashByteArray
:: ByteArray# -> CLong -> CLong -> CLong -> CLong
combine :: Int -> Int -> Int
combine h1 h2 = (h1 * 16777619) `xor` h2