    {-# LANGUAGE CPP, BangPatterns, MagicHash, CApiFFI, UnliftedFFITypes #-}
    {-# LANGUAGE Trustworthy #-}
    -- | A module containing low-level hash primitives.
    module Data.Hashable.LowLevel (
        Salt,
        defaultSalt,
        hashInt,
        hashInt64,
        hashWord64,
        hashPtrWithSalt,
        hashByteArrayWithSalt,
    ) where

    #include "MachDeps.h"

    import Data.Array.Byte (ByteArray (..))
    import Foreign.Ptr (Ptr, castPtr)
    import GHC.Base (ByteArray#)

    #ifdef HASHABLE_RANDOM_SEED
    import System.IO.Unsafe (unsafePerformIO)
    #endif

    import Data.Hashable.Imports
    import Data.Hashable.Mix
    import Data.Hashable.XXH3

    -------------------------------------------------------------------------------
    -- Initial seed
    -------------------------------------------------------------------------------

    #ifdef HASHABLE_RANDOM_SEED
    initialSeed :: Word64
    initialSeed = unsafePerformIO initialSeedC
    {-# NOINLINE initialSeed #-}

    foreign import capi "HsHashable.h hs_hashable_init" initialSeedC :: IO Word64
    #endif

    -- | A default salt used in the implementation of 'hash'.
    defaultSalt :: Salt
    #ifdef HASHABLE_RANDOM_SEED
    defaultSalt = hashInt defaultSalt' (fromIntegral initialSeed)
    #else
    defaultSalt :: Salt
    defaultSalt = Salt
    defaultSalt'
    #endif
    {-# INLINE defaultSalt #-}

    defaultSalt' :: Salt
    #if WORD_SIZE_IN_BITS == 64
    defaultSalt' :: Salt
    defaultSalt' = -Salt
    3750763034362895579 -- 14695981039346656037 :: Int64
    #else
    defaultSalt' = -2128831035 -- 2166136261 :: Int32
    #endif
    {-# INLINE defaultSalt' #-}

    -------------------------------------------------------------------------------
    -- Hash primitives
    -------------------------------------------------------------------------------

    -- | Hash 'Int'. First argument is a salt, second argument is an 'Int'.
    -- The result is new salt / hash value.
    hashInt :: Salt -> Int -> Salt
    hashInt :: Salt -> Salt -> Salt
    hashInt !Salt
    s !Salt
    x = Word -> Salt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word -> Word -> Word
    mixHash (Salt -> Word
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Salt
    s) (Salt -> Word
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Salt
    x))

    hashInt64  :: Salt -> Int64 -> Salt
    hashWord64 :: Salt -> Word64 -> Salt

    #if WORD_SIZE_IN_BITS == 64
    hashInt64 :: Salt -> Int64 -> Salt
    hashInt64  !Salt
    s !Int64
    x = Salt -> Salt -> Salt
    hashInt Salt
    s (Int64 -> Salt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int64
    x)
    hashWord64 :: Salt -> Word64 -> Salt
    hashWord64 !Salt
    s !Word64
    x = Salt -> Salt -> Salt
    hashInt Salt
    s (Word64 -> Salt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word64
    x)
    #else
    hashInt64  !s !x = hashInt (hashInt s (fromIntegral x)) (fromIntegral (x `unsafeShiftR` 32))
    hashWord64 !s !x = hashInt (hashInt s (fromIntegral x)) (fromIntegral (x `unsafeShiftR` 32))
    #endif

    -- | Compute a hash value for the content of this pointer, using an
    -- initial salt.
    --
    -- This function can for example be used to hash non-contiguous
    -- segments of memory as if they were one contiguous segment, by using
    -- the output of one hash as the salt for the next.
    hashPtrWithSalt :: Ptr a   -- ^ pointer to the data to hash
                    -> Int     -- ^ length, in bytes
                    -> Salt    -- ^ salt
                    -> IO Salt -- ^ hash value
    hashPtrWithSalt :: forall a. Ptr a -> Salt -> Salt -> IO Salt
    hashPtrWithSalt Ptr a
    ptr Salt
    len Salt
    salt =
        Word64 -> Salt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word64 -> Salt) -> IO Word64 -> IO Salt
    forall a b. (a -> b) -> IO a -> IO b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    `fmap` Ptr Word8 -> Salt -> Word64 -> IO Word64
    xxh3_64bit_withSeed_ptr (Ptr a -> Ptr Word8
    forall a b. Ptr a -> Ptr b
    castPtr Ptr a
    ptr) Salt
    len (Salt -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Salt
    salt)

    -- | Compute a hash value for the content of this 'ByteArray#', using
    -- an initial salt.
    --
    -- This function can for example be used to hash non-contiguous
    -- segments of memory as if they were one contiguous segment, by using
    -- the output of one hash as the salt for the next.
    hashByteArrayWithSalt
        :: ByteArray#  -- ^ data to hash
        -> Int         -- ^ offset, in bytes
        -> Int         -- ^ length, in bytes
        -> Salt        -- ^ salt
        -> Salt        -- ^ hash value
    hashByteArrayWithSalt :: ByteArray# -> Salt -> Salt -> Salt -> Salt
    hashByteArrayWithSalt ByteArray#
    ba !Salt
    off !Salt
    len !Salt
    salt =
        Word64 -> Salt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (ByteArray -> Salt -> Salt -> Word64 -> Word64
    xxh3_64bit_withSeed_ba (ByteArray# -> ByteArray
    ByteArray ByteArray#
    ba) Salt
    off Salt
    len (Salt -> Word64
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Salt
    salt))