    -- |
    -- Module      : Basement.Block.Mutable
    -- License     : BSD-style
    -- Maintainer  : Haskell Foundation
    --
    -- A block of memory that contains elements of a type,
    -- very similar to an unboxed array but with the key difference:
    --
    -- * It doesn't have slicing capability (no cheap take or drop)
    -- * It consume less memory: 1 Offset, 1 CountOf, 1 Pinning status trimmed
    -- * It's unpackable in any constructor
    -- * It uses unpinned memory by default
    --
    -- It should be rarely needed in high level API, but
    -- in lowlevel API or some data structure containing lots
    -- of unboxed array that will benefit from optimisation.
    --
    -- Because it's unpinned, the blocks are compactable / movable,
    -- at the expense of making them less friendly to interop with the C layer
    -- as address.
    --
    -- Note that sadly the bytearray primitive type automatically create
    -- a pinned bytearray if the size is bigger than a certain threshold
    --
    -- GHC Documentation associated:
    --
    -- includes/rts/storage/Block.h
    --   * LARGE_OBJECT_THRESHOLD ((uint32_t)(BLOCK_SIZE * 8 / 10))
    --   * BLOCK_SIZE   (1<<BLOCK_SHIFT)
    --
    -- includes/rts/Constant.h
    --   * BLOCK_SHIFT  12
    --
    {-# LANGUAGE MagicHash           #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE UnboxedTuples       #-}
    module Basement.Block.Mutable
        ( Block(..)
        , MutableBlock(..)
        , mutableLengthSize
        , mutableLength
        , mutableLengthBytes
        , mutableWithPtr
        , withMutablePtr
        , withMutablePtrHint
        , new
        , newPinned
        , mutableEmpty
        , iterSet
        , read
        , write
        , unsafeNew
        , unsafeWrite
        , unsafeRead
        , unsafeFreeze
        , unsafeThaw
        , unsafeCopyElements
        , unsafeCopyElementsRO
        , unsafeCopyBytes
        , unsafeCopyBytesRO
        , unsafeCopyBytesPtr
        -- * Foreign
        , copyFromPtr
        , copyToPtr
        ) where

    import           GHC.Prim
    import           GHC.Types
    import           Basement.Compat.Base
    import           Data.Proxy
    import           Basement.Exception
    import           Basement.Types.OffsetSize
    import           Basement.Monad
    import           Basement.Numerical.Additive
    import           Basement.PrimType
    import           Basement.Block.Base

    -- | Set all mutable block element to a value
    iterSet :: (PrimType ty, PrimMonad prim)
            => (Offset ty -> ty)
            -> MutableBlock ty (PrimState prim)
            -> prim ()
    iterSet :: forall ty (prim :: * -> *).
    (PrimType ty, PrimMonad prim) =>
    (Offset ty -> ty) -> MutableBlock ty (PrimState prim) -> prim ()
    iterSet Offset ty -> ty
    f MutableBlock ty (PrimState prim)
    ma = Offset ty -> prim ()
    loop Offset ty
    0
      where
        !sz :: CountOf ty
    sz = MutableBlock ty (PrimState prim) -> CountOf ty
    forall ty st. PrimType ty => MutableBlock ty st -> CountOf ty
    mutableLength MutableBlock ty (PrimState prim)
    ma
        loop :: Offset ty -> prim ()
    loop Offset ty
    i
            | Offset ty
    i Offset ty -> CountOf ty -> Bool
    forall ty. Offset ty -> CountOf ty -> Bool
    .==# CountOf ty
    sz = () -> prim ()
    forall a. a -> prim a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ()
            | Bool
    otherwise = MutableBlock ty (PrimState prim) -> Offset ty -> ty -> prim ()
    forall (prim :: * -> *) ty.
    (PrimMonad prim, PrimType ty) =>
    MutableBlock ty (PrimState prim) -> Offset ty -> ty -> prim ()
    unsafeWrite MutableBlock ty (PrimState prim)
    ma Offset ty
    i (Offset ty -> ty
    f Offset ty
    i) prim () -> prim () -> prim ()
    forall a b. prim a -> prim b -> prim b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Offset ty -> prim ()
    loop (Offset ty
    iOffset ty -> Offset ty -> Offset ty
    forall a. Additive a => a -> a -> a
    +Offset ty
    1)
        {-# INLINE loop #-}

    mutableLengthSize :: PrimType ty => MutableBlock ty st -> CountOf ty
    mutableLengthSize :: forall ty st. PrimType ty => MutableBlock ty st -> CountOf ty
    mutableLengthSize = MutableBlock ty st -> CountOf ty
    forall ty st. PrimType ty => MutableBlock ty st -> CountOf ty
    mutableLength
    {-# DEPRECATED mutableLengthSize "use mutableLength" #-}

    -- | read a cell in a mutable array.
    --
    -- If the index is out of bounds, an error is raised.
    read :: (PrimMonad prim, PrimType ty) => MutableBlock ty (PrimState prim) -> Offset ty -> prim ty
    read :: forall (prim :: * -> *) ty.
    (PrimMonad prim, PrimType ty) =>
    MutableBlock ty (PrimState prim) -> Offset ty -> prim ty
    read MutableBlock ty (PrimState prim)
    array Offset ty
    n
        | Offset ty -> CountOf ty -> Bool
    forall ty. Offset ty -> CountOf ty -> Bool
    isOutOfBound Offset ty
    n CountOf ty
    len = OutOfBoundOperation -> Offset ty -> CountOf ty -> prim ty
    forall (prim :: * -> *) ty a.
    PrimMonad prim =>
    OutOfBoundOperation -> Offset ty -> CountOf ty -> prim a
    primOutOfBound OutOfBoundOperation
    OOB_Read Offset ty
    n CountOf ty
    len
        | Bool
    otherwise          = MutableBlock ty (PrimState prim) -> Offset ty -> prim ty
    forall (prim :: * -> *) ty.
    (PrimMonad prim, PrimType ty) =>
    MutableBlock ty (PrimState prim) -> Offset ty -> prim ty
    unsafeRead MutableBlock ty (PrimState prim)
    array Offset ty
    n
      where len :: CountOf ty
    len = MutableBlock ty (PrimState prim) -> CountOf ty
    forall ty st. PrimType ty => MutableBlock ty st -> CountOf ty
    mutableLength MutableBlock ty (PrimState prim)
    array
    {-# INLINE read #-}

    -- | Write to a cell in a mutable array.
    --
    -- If the index is out of bounds, an error is raised.
    write :: (PrimMonad prim, PrimType ty) => MutableBlock ty (PrimState prim) -> Offset ty -> ty -> prim ()
    write :: forall (prim :: * -> *) ty.
    (PrimMonad prim, PrimType ty) =>
    MutableBlock ty (PrimState prim) -> Offset ty -> ty -> prim ()
    write MutableBlock ty (PrimState prim)
    array Offset ty
    n ty
    val
        | Offset ty -> CountOf ty -> Bool
    forall ty. Offset ty -> CountOf ty -> Bool
    isOutOfBound Offset ty
    n CountOf ty
    len = OutOfBoundOperation -> Offset ty -> CountOf ty -> prim ()
    forall (prim :: * -> *) ty a.
    PrimMonad prim =>
    OutOfBoundOperation -> Offset ty -> CountOf ty -> prim a
    primOutOfBound OutOfBoundOperation
    OOB_Write Offset ty
    n CountOf ty
    len
        | Bool
    otherwise          = MutableBlock ty (PrimState prim) -> Offset ty -> ty -> prim ()
    forall (prim :: * -> *) ty.
    (PrimMonad prim, PrimType ty) =>
    MutableBlock ty (PrimState prim) -> Offset ty -> ty -> prim ()
    unsafeWrite MutableBlock ty (PrimState prim)
    array Offset ty
    n ty
    val
      where
        len :: CountOf ty
    len = MutableBlock ty (PrimState prim) -> CountOf ty
    forall ty st. PrimType ty => MutableBlock ty st -> CountOf ty
    mutableLengthSize MutableBlock ty (PrimState prim)
    array
    {-# INLINE write #-}

    -- | Copy from a pointer, @count@ elements, into the Mutable Block at a starting offset @ofs@
    --
    -- if the source pointer is invalid (size or bad allocation), bad things will happen
    --
    copyFromPtr :: forall prim ty . (PrimMonad prim, PrimType ty)
                => Ptr ty                           -- ^ Source Ptr of 'ty' to start of memory
                -> MutableBlock ty (PrimState prim) -- ^ Destination mutable block
                -> Offset ty                        -- ^ Start offset in the destination mutable block
                -> CountOf ty                       -- ^ Number of 'ty' elements
                -> prim ()
    copyFromPtr :: forall (prim :: * -> *) ty.
    (PrimMonad prim, PrimType ty) =>
    Ptr ty
    -> MutableBlock ty (PrimState prim)
    -> Offset ty
    -> CountOf ty
    -> prim ()
    copyFromPtr src :: Ptr ty
    src@(Ptr Addr#
    src#) mb :: MutableBlock ty (PrimState prim)
    mb@(MutableBlock MutableByteArray# (PrimState prim)
    mba) Offset ty
    ofs CountOf ty
    count
        | Offset Word8
    end Offset Word8 -> Offset Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    > CountOf Word8 -> Offset Word8
    forall a. CountOf a -> Offset a
    sizeAsOffset CountOf Word8
    arrSz = OutOfBoundOperation -> Offset Word8 -> CountOf Word8 -> prim ()
    forall (prim :: * -> *) ty a.
    PrimMonad prim =>
    OutOfBoundOperation -> Offset ty -> CountOf ty -> prim a
    primOutOfBound OutOfBoundOperation
    OOB_MemCopy Offset Word8
    end CountOf Word8
    arrSz
        | Bool
    otherwise                = (State# (PrimState prim) -> (# State# (PrimState prim), () #))
    -> prim ()
    forall a.
    (State# (PrimState prim) -> (# State# (PrimState prim), a #))
    -> prim a
    forall (m :: * -> *) a.
    PrimMonad m =>
    (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a
    primitive ((State# (PrimState prim) -> (# State# (PrimState prim), () #))
     -> prim ())
    -> (State# (PrimState prim) -> (# State# (PrimState prim), () #))
    -> prim ()
    forall a b. (a -> b) -> a -> b
    $ \State# (PrimState prim)
    st -> (# Addr#
    -> MutableByteArray# (PrimState prim)
    -> Int#
    -> Int#
    -> State# (PrimState prim)
    -> State# (PrimState prim)
    forall d.
    Addr#
    -> MutableByteArray# d -> Int# -> Int# -> State# d -> State# d
    copyAddrToByteArray# Addr#
    src# MutableByteArray# (PrimState prim)
    mba Int#
    od# Int#
    bytes# State# (PrimState prim)
    st, () #)
      where
        end :: Offset Word8
    end = Offset Word8
    od Offset Word8 -> CountOf Word8 -> Offset Word8
    forall ty. Offset ty -> CountOf ty -> Offset ty
    `offsetPlusE` CountOf Word8
    arrSz

        sz :: CountOf Word8
    sz = Proxy ty -> CountOf Word8
    forall ty. PrimType ty => Proxy ty -> CountOf Word8
    primSizeInBytes (Proxy ty
    forall {k} (t :: k). Proxy t
    Proxy :: Proxy ty)
        !arrSz :: CountOf Word8
    arrSz@(CountOf (I# Int#
    bytes#)) = CountOf Word8 -> CountOf ty -> CountOf Word8
    forall ty. CountOf Word8 -> CountOf ty -> CountOf Word8
    sizeOfE CountOf Word8
    sz CountOf ty
    count
        !od :: Offset Word8
    od@(Offset (I# Int#
    od#)) = CountOf Word8 -> Offset ty -> Offset Word8
    forall ty. CountOf Word8 -> Offset ty -> Offset Word8
    offsetOfE CountOf Word8
    sz Offset ty
    ofs

    -- | Copy all the block content to the memory starting at the destination address
    --
    -- If the destination pointer is invalid (size or bad allocation), bad things will happen
    copyToPtr :: forall ty prim . (PrimType ty, PrimMonad prim)
              => MutableBlock ty (PrimState prim) -- ^ The source mutable block to copy
              -> Offset ty                        -- ^ The source offset in the mutable block
              -> Ptr ty                           -- ^ The destination address where the copy is going to start
              -> CountOf ty                       -- ^ The number of bytes
              -> prim ()
    copyToPtr :: forall ty (prim :: * -> *).
    (PrimType ty, PrimMonad prim) =>
    MutableBlock ty (PrimState prim)
    -> Offset ty -> Ptr ty -> CountOf ty -> prim ()
    copyToPtr mb :: MutableBlock ty (PrimState prim)
    mb@(MutableBlock MutableByteArray# (PrimState prim)
    mba) Offset ty
    ofs dst :: Ptr ty
    dst@(Ptr Addr#
    dst#) CountOf ty
    count
        | Offset Word8
    srcEnd Offset Word8 -> Offset Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    > CountOf Word8 -> Offset Word8
    forall a. CountOf a -> Offset a
    sizeAsOffset CountOf Word8
    arrSz = OutOfBoundOperation -> Offset Word8 -> CountOf Word8 -> prim ()
    forall (prim :: * -> *) ty a.
    PrimMonad prim =>
    OutOfBoundOperation -> Offset ty -> CountOf ty -> prim a
    primOutOfBound OutOfBoundOperation
    OOB_MemCopy Offset Word8
    srcEnd CountOf Word8
    arrSz
        | Bool
    otherwise                = do
            blk <- MutableBlock ty (PrimState prim) -> prim (Block ty)
    forall (prim :: * -> *) ty.
    PrimMonad prim =>
    MutableBlock ty (PrimState prim) -> prim (Block ty)
    unsafeFreeze MutableBlock ty (PrimState prim)
    mb
            let !(Block ba) = blk
            primitive $ \State# (PrimState prim)
    s1 -> (# ByteArray#
    -> Int#
    -> Addr#
    -> Int#
    -> State# (PrimState prim)
    -> State# (PrimState prim)
    forall d.
    ByteArray# -> Int# -> Addr# -> Int# -> State# d -> State# d
    copyByteArrayToAddr# ByteArray#
    ba Int#
    os# Addr#
    dst# Int#
    szBytes# State# (PrimState prim)
    s1, () #)
      where
        srcEnd :: Offset Word8
    srcEnd = Offset Word8
    os Offset Word8 -> CountOf Word8 -> Offset Word8
    forall ty. Offset ty -> CountOf ty -> Offset ty
    `offsetPlusE` CountOf Word8
    arrSz
        !os :: Offset Word8
    os@(Offset (I# Int#
    os#)) = Offset ty -> Offset Word8
    forall a. PrimType a => Offset a -> Offset Word8
    offsetInBytes Offset ty
    ofs
        !arrSz :: CountOf Word8
    arrSz@(CountOf (I# Int#
    szBytes#)) = MutableBlock ty (PrimState prim) -> CountOf Word8
    forall ty st. MutableBlock ty st -> CountOf Word8
    mutableLengthBytes MutableBlock ty (PrimState prim)
    mb