module Data.Hashable
(
Hashable(..)
, hashPtr
, hashPtrWithSalt
#if defined(__GLASGOW_HASKELL__)
, hashByteArray
, hashByteArrayWithSalt
#endif
, combine
) where
import Data.Bits (bitSize, shiftL, shiftR, xor)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word, Word8, Word16, Word32, Word64)
import Data.List (foldl')
import qualified Data.ByteString as B
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
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 LT
import Foreign.C (CLong, CString)
import Foreign.Ptr (Ptr, castPtr)
#if defined(__GLASGOW_HASKELL__)
import Foreign.C.Types (CInt)
import GHC.Base (ByteArray#)
import GHC.Conc (ThreadId(..))
import GHC.Prim (ThreadId#)
#else
import Control.Concurrent (ThreadId)
#endif
class Hashable a where
hash :: a -> Int
instance Hashable () where hash _ = 0
instance Hashable Bool where hash x = case x of { True -> 1; False -> 0 }
instance Hashable Int where hash = id
instance Hashable Int8 where hash = fromIntegral
instance Hashable Int16 where hash = fromIntegral
instance Hashable Int32 where hash = fromIntegral
instance Hashable Int64 where
hash n
| bitSize n == 64 = fromIntegral n
| otherwise = fromIntegral (fromIntegral n `xor`
(fromIntegral n `shiftR` 32 :: Word64))
instance Hashable Word where hash = fromIntegral
instance Hashable Word8 where hash = fromIntegral
instance Hashable Word16 where hash = fromIntegral
instance Hashable Word32 where hash = fromIntegral
instance Hashable Word64 where
hash n
| bitSize n == 64 = fromIntegral n
| otherwise = fromIntegral (n `xor` (n `shiftR` 32))
instance Hashable Char where hash = fromEnum
instance Hashable a => Hashable (Maybe a) where
hash Nothing = 0
hash (Just a) = 1 `combine` hash a
instance (Hashable a, Hashable b) => Hashable (Either a b) where
hash (Left a) = 0 `combine` hash a
hash (Right b) = 1 `combine` hash b
instance (Hashable a1, Hashable a2) => Hashable (a1, a2) where
hash (a1, a2) = hash a1 `combine` hash a2
instance (Hashable a1, Hashable a2, Hashable a3) => Hashable (a1, a2, a3) where
hash (a1, a2, a3) = hash a1 `combine` hash a2 `combine` hash a3
instance (Hashable a1, Hashable a2, Hashable a3, Hashable a4) =>
Hashable (a1, a2, a3, a4) where
hash (a1, a2, a3, a4) = hash a1 `combine` hash a2 `combine` hash a3
`combine` hash 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 `combine` hash a2 `combine` hash a3 `combine` hash a4 `combine`
hash 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 `combine` hash a2 `combine` hash a3 `combine` hash a4 `combine`
hash a5 `combine` hash 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 `combine` hash a2 `combine` hash a3 `combine` hash a4 `combine`
hash a5 `combine` hash a6 `combine` hash a7
instance Hashable a => Hashable [a] where
hash = foldl' hashAndCombine 0
hashThreadId :: ThreadId -> Int
#if defined(__GLASGOW_HASKELL__)
hashThreadId (ThreadId t) = hash (fromIntegral (getThreadId t) :: Int)
foreign import ccall unsafe "rts_getThreadId" getThreadId :: ThreadId# -> CInt
#else
hashThreadId = hash . show
#endif
instance Hashable ThreadId where
hash = hashThreadId
hashAndCombine :: Hashable h => Int -> h -> Int
hashAndCombine acc h = acc `combine` hash h
instance Hashable B.ByteString where
hash bs = B.inlinePerformIO $
B.unsafeUseAsCStringLen bs $ \(p, len) ->
hashPtr p (fromIntegral len)
hashByteStringWithSalt :: Int
-> B.ByteString
-> Int
hashByteStringWithSalt salt bs =
B.inlinePerformIO $ B.unsafeUseAsCStringLen bs $ \(p, len) ->
hashPtrWithSalt p (fromIntegral len) salt
instance Hashable BL.ByteString where
hash = BL.foldlChunks hashByteStringWithSalt 0
instance Hashable T.Text where
hash (T.Text arr off len) = hashByteArray (TA.aBA arr)
(off `shiftL` 1) (len `shiftL` 1)
hashTextWithSalt :: Int
-> T.Text
-> Int
hashTextWithSalt salt (T.Text arr off len) =
hashByteArrayWithSalt (TA.aBA arr) (off `shiftL` 1) (len `shiftL` 1) salt
instance Hashable LT.Text where
hash = LT.foldlChunks hashTextWithSalt 0
hashPtr :: Ptr a
-> Int
-> IO Int
hashPtr p len = hashPtrWithSalt p len 0
hashPtrWithSalt :: Ptr a
-> Int
-> Int
-> IO Int
hashPtrWithSalt p len salt =
fromIntegral `fmap` hashCString (castPtr p) (fromIntegral len)
(fromIntegral salt)
foreign import ccall unsafe "djb_hash" hashCString
:: CString -> CLong -> CLong -> IO CLong
#if defined(__GLASGOW_HASKELL__)
hashByteArray :: ByteArray#
-> Int
-> Int
-> Int
hashByteArray ba0 off len = hashByteArrayWithSalt ba0 off len 0
hashByteArrayWithSalt
:: ByteArray#
-> Int
-> Int
-> Int
-> Int
hashByteArrayWithSalt ba !off !len !h0 =
fromIntegral $ c_hashByteArray ba (fromIntegral off) (fromIntegral len)
(fromIntegral h0)
foreign import ccall unsafe "djb_hash_offset" c_hashByteArray
:: ByteArray# -> CLong -> CLong -> CLong -> CLong
#endif
combine :: Int -> Int -> Int
combine h1 h2 = (h1 + h1 `shiftL` 5) `xor` h2