    {-# LANGUAGE BangPatterns #-}
    {-# LANGUAGE CPP          #-}
    {-# LANGUAGE MagicHash    #-}

    module Data.HashTable.Internal.IntArray
      ( IntArray
      , Elem
      , elemMask
      , primWordToElem
      , elemToInt
      , elemToInt#
      , newArray
      , readArray
      , writeArray
      , length
      , toPtr
      ) where

    ------------------------------------------------------------------------------
    import           Control.Monad.ST
    import           Data.Bits
    import qualified Data.Primitive.ByteArray as A
    #if !MIN_VERSION_primitive(0,7,0)
    import           Data.Primitive.Types     (Addr (..))
    #endif
    import           GHC.Exts
    import           GHC.Word
    import           Prelude                  hiding (length)
    ------------------------------------------------------------------------------

    #ifdef BOUNDS_CHECKING
    #define BOUNDS_MSG(sz,i) concat [ "[", __FILE__, ":",                         \
                                      show (__LINE__ :: Int),                     \
                                      "] bounds check exceeded: ",                \
                                      "size was ", show (sz), " i was ", show (i) ]

    #define BOUNDS_CHECK(arr,i) let sz = (A.sizeofMutableByteArray (arr)          \
                                          `div` wordSizeInBytes) in               \
                                if (i) < 0 || (i) >= sz                           \
                                  then error (BOUNDS_MSG(sz,(i)))                 \
                                  else return ()
    #else
    #define BOUNDS_CHECK(arr,i)
    #endif

    ------------------------------------------------------------------------------
    newtype IntArray s = IA (A.MutableByteArray s)
    type Elem = Word16

    ------------------------------------------------------------------------------
    primWordToElem :: Word# -> Elem
    primWordToElem :: Word# -> Elem
    primWordToElem Word#
    w# = Word16# -> Elem
    W16# (Word# -> Word16#
    wordToWord16Compat# Word#
    w#)

    ------------------------------------------------------------------------------
    elemToInt :: Elem -> Int
    elemToInt :: Elem -> Int
    elemToInt Elem
    e = let !i# :: Int#
    i# = Elem -> Int#
    elemToInt# Elem
    e
                  in (Int# -> Int
    I# Int#
    i#)

    ------------------------------------------------------------------------------
    elemToInt# :: Elem -> Int#
    elemToInt# :: Elem -> Int#
    elemToInt# (W16# Word16#
    w#) = Word# -> Int#
    word2Int# (Word16# -> Word#
    word16ToWordCompat# Word16#
    w#)

    ------------------------------------------------------------------------------
    elemMask :: Int
    elemMask :: Int
    elemMask = Int
    0xffff

    ------------------------------------------------------------------------------
    wordSizeInBytes :: Int
    wordSizeInBytes :: Int
    wordSizeInBytes = Elem -> Int
    forall b. FiniteBits b => b -> Int
    finiteBitSize (Elem
    0::Elem) Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `div` Int
    8

    ------------------------------------------------------------------------------
    -- | Cache line size, in bytes
    cacheLineSize :: Int
    cacheLineSize :: Int
    cacheLineSize = Int
    64

    ------------------------------------------------------------------------------
    newArray :: Int -> ST s (IntArray s)
    newArray :: forall s. Int -> ST s (IntArray s)
    newArray Int
    n = do
        let !sz :: Int
    sz = Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    wordSizeInBytes
        !arr <- Int -> Int -> ST s (MutableByteArray (PrimState (ST s)))
    forall (m :: * -> *).
    PrimMonad m =>
    Int -> Int -> m (MutableByteArray (PrimState m))
    A.newAlignedPinnedByteArray Int
    sz Int
    cacheLineSize
        A.fillByteArray arr 0 sz 0
        return $! IA arr

    ------------------------------------------------------------------------------
    readArray :: IntArray s -> Int -> ST s Elem
    readArray :: forall s. IntArray s -> Int -> ST s Elem
    readArray (IA MutableByteArray s
    a) Int
    idx = do
        BOUNDS_CHECK(a,idx)
        MutableByteArray (PrimState (ST s)) -> Int -> ST s Elem
    forall a (m :: * -> *).
    (Prim a, PrimMonad m) =>
    MutableByteArray (PrimState m) -> Int -> m a
    A.readByteArray MutableByteArray s
    MutableByteArray (PrimState (ST s))
    a Int
    idx

    ------------------------------------------------------------------------------
    writeArray :: IntArray s -> Int -> Elem -> ST s ()
    writeArray :: forall s. IntArray s -> Int -> Elem -> ST s ()
    writeArray (IA MutableByteArray s
    a) Int
    idx Elem
    val = do
        BOUNDS_CHECK(a,idx)
        MutableByteArray (PrimState (ST s)) -> Int -> Elem -> ST s ()
    forall a (m :: * -> *).
    (Prim a, PrimMonad m) =>
    MutableByteArray (PrimState m) -> Int -> a -> m ()
    A.writeByteArray MutableByteArray s
    MutableByteArray (PrimState (ST s))
    a Int
    idx Elem
    val

    ------------------------------------------------------------------------------
    length :: IntArray s -> ST s Int
    length :: forall s. IntArray s -> ST s Int
    length (IA MutableByteArray s
    a) = (Int -> Int -> Int
    forall a. Integral a => a -> a -> a
    `div` Int
    wordSizeInBytes) (Int -> Int) -> ST s Int -> ST s Int
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> MutableByteArray (PrimState (ST s)) -> ST s Int
    forall (m :: * -> *).
    PrimMonad m =>
    MutableByteArray (PrimState m) -> m Int
    A.getSizeofMutableByteArray MutableByteArray s
    MutableByteArray (PrimState (ST s))
    a

    ------------------------------------------------------------------------------
    toPtr :: IntArray s -> Ptr a
    toPtr :: forall s a. IntArray s -> Ptr a
    toPtr (IA MutableByteArray s
    a) = Addr# -> Ptr a
    forall a. Addr# -> Ptr a
    Ptr Addr#
    a#
      where
    #if MIN_VERSION_primitive(0,7,0)
        !(Ptr !Addr#
    a#) = MutableByteArray s -> Ptr Word8
    forall s. MutableByteArray s -> Ptr Word8
    A.mutableByteArrayContents MutableByteArray s
    a
    #else
        !(Addr !a#) = A.mutableByteArrayContents a
    #endif

    #if MIN_VERSION_base(4,16,0)
    word16ToWordCompat# :: Word16# -> Word#
    word16ToWordCompat# :: Word16# -> Word#
    word16ToWordCompat# = Word16# -> Word#
    word16ToWord#

    wordToWord16Compat# :: Word# -> Word16#
    wordToWord16Compat# :: Word# -> Word16#
    wordToWord16Compat# = Word# -> Word16#
    wordToWord16#
    #else
    word16ToWordCompat# :: Word# -> Word#
    word16ToWordCompat# x = x

    wordToWord16Compat# :: Word# -> Word#
    wordToWord16Compat# x = x
    #endif