packages feed

primitive-checked-0.7.2.0: src/Data/Primitive/ByteArray.hs

{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Data.Primitive.ByteArray
  ( -- * Types
    ByteArray(..)
  , MutableByteArray(..)
  , A.ByteArray#
  , A.MutableByteArray#
    -- * Allocation
  , newByteArray
  , newPinnedByteArray
  , newAlignedPinnedByteArray
  , resizeMutableByteArray
  , shrinkMutableByteArray
    -- * Element access
  , readByteArray
  , writeByteArray
  , indexByteArray
    -- * Constructing
  , A.byteArrayFromList
  , A.byteArrayFromListN
    -- * Folding
  , A.foldrByteArray
    -- * Comparing
  , compareByteArrays
    -- * Freezing and thawing
  , freezeByteArray
  , thawByteArray
  , A.unsafeFreezeByteArray
  , A.unsafeThawByteArray
    -- * Block operations
  , copyByteArray
  , copyMutableByteArray
  , copyByteArrayToPtr
  , copyMutableByteArrayToPtr
  , copyByteArrayToAddr
  , copyMutableByteArrayToAddr
  , moveByteArray
  , setByteArray
  , fillByteArray
  , cloneByteArray
  , cloneMutableByteArray
  -- * Information
  , A.sizeofByteArray
  , A.sizeofMutableByteArray
  , A.getSizeofMutableByteArray
  , A.sameMutableByteArray
  , A.isByteArrayPinned
  , A.isMutableByteArrayPinned
  , A.byteArrayContents
  , A.mutableByteArrayContents
  ) where

import Control.Monad.Primitive (PrimMonad,PrimState)
import Control.Exception (throw, ArrayException(..))
import Data.Primitive.Types (Prim, Ptr, sizeOf)
import Data.Proxy (Proxy(..))
import Data.Word (Word8)
import "primitive" Data.Primitive.ByteArray (ByteArray, MutableByteArray)
import qualified "primitive" Data.Primitive.ByteArray as A
import qualified Data.List as L
import GHC.Stack

check :: HasCallStack => String -> Bool -> a -> a
check _      True  x = x
check errMsg False _ = throw (IndexOutOfBounds $ "Data.Primitive.ByteArray." ++ errMsg ++ "\n" ++ prettyCallStack callStack)

elementSizeofByteArray :: forall a. Prim a => Proxy a -> ByteArray -> Int
elementSizeofByteArray _ arr = A.sizeofByteArray arr `div` sizeOf (undefined :: a)

getElementSizeofMutableByteArray :: forall m a. (PrimMonad m, Prim a)
  => Proxy a -> MutableByteArray (PrimState m) -> m Int
getElementSizeofMutableByteArray _ arr = do
  sz <- A.getSizeofMutableByteArray arr
  return (sz `div` sizeOf (undefined :: a))

newByteArray :: (HasCallStack, PrimMonad m) => Int -> m (MutableByteArray (PrimState m))
newByteArray n =
    check "newByteArray: negative size" (n >= 0)
  $ check ("newByteArray: requested " ++ show n ++ " bytes") (n < 1024*1024*1024)
  $ A.newByteArray n

newPinnedByteArray :: (HasCallStack, PrimMonad m) => Int -> m (MutableByteArray (PrimState m))
newPinnedByteArray n = check "newPinnedByteArray: negative size" (n >= 0) (A.newPinnedByteArray n)

newAlignedPinnedByteArray :: (HasCallStack, PrimMonad m) => Int -> Int -> m (MutableByteArray (PrimState m))
newAlignedPinnedByteArray n k = check "newAlignedPinnedByteArray: negative size" (n >= 0) (A.newAlignedPinnedByteArray n k)

resizeMutableByteArray :: PrimMonad m => MutableByteArray (PrimState m) -> Int -> m (MutableByteArray (PrimState m))
resizeMutableByteArray a n = check "resizeMutableByteArray: negative size" (n >= 0) (A.resizeMutableByteArray a n)

shrinkMutableByteArray :: (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m)
  -> Int -- ^ new size
  -> m ()
shrinkMutableByteArray marr n = do
  old <- A.getSizeofMutableByteArray marr
  check "shrinkMutableByteArray: illegal new size" (n >= 0 && n <= old) (A.shrinkMutableByteArray marr n)

readByteArray :: forall m a. (HasCallStack, Prim a, PrimMonad m) => MutableByteArray (PrimState m) -> Int -> m a
readByteArray marr i = do
  siz <- getElementSizeofMutableByteArray (Proxy :: Proxy a) marr
  check "readByteArray: index out of bounds" (i >= 0 && i < siz) (A.readByteArray marr i)

writeByteArray :: forall m a. (HasCallStack, Prim a, PrimMonad m) => MutableByteArray (PrimState m) -> Int -> a -> m ()
writeByteArray marr i x = do
  siz <- getElementSizeofMutableByteArray (Proxy :: Proxy a) marr
  let explain = L.concat
        [ "[size: "
        , show siz
        , ", index: "
        , show i
        , ", elem_sz: "
        , show (sizeOf (undefined :: a))
        , "]"
        ]
  check ("writeByteArray: index out of bounds " ++ explain)
    (i >= 0 && i < siz)
    (A.writeByteArray marr i x)

-- This one is a little special. We allow users to index past the
-- end of the byte array as long as the content grabbed is within
-- the last machine word of the byte array.
indexByteArray :: forall a. (HasCallStack, Prim a) => ByteArray -> Int -> a
indexByteArray arr i = check "indexByteArray: index out of bounds"
  (i >= 0 && i < elementSizeofByteArray (Proxy :: Proxy a) arr)
  (A.indexByteArray arr i)

compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering
compareByteArrays arr1 off1 arr2 off2 len = check "compareByteArrays: index range out of bounds"
  (off1 >= 0 && off2 >= 0 && off1 + len <= A.sizeofByteArray arr1 && off2 + len <= A.sizeofByteArray arr2)
  (A.compareByteArrays arr1 off1 arr2 off2 len)

freezeByteArray
  :: (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ source
  -> Int                            -- ^ offset
  -> Int                            -- ^ length
  -> m ByteArray
freezeByteArray marr s l = check "freezeByteArray: index range of out bounds"
  (s >= 0 && l >= 0 && s + l <= A.sizeofMutableByteArray marr)
  (A.freezeByteArray marr s l)

thawByteArray
  :: (HasCallStack, PrimMonad m)
  => ByteArray -- ^ source
  -> Int       -- ^ offset
  -> Int       -- ^ length
  -> m (MutableByteArray (PrimState m))
thawByteArray arr s l = check "thawByteArray: index range of out bounds"
  (s >= 0 && l >= 0 && s + l <= A.sizeofByteArray arr)
  (A.thawByteArray arr s l)

copyByteArray :: forall m. (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ destination array
  -> Int -- ^ offset into destination array
  -> ByteArray -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of elements to copy
  -> m ()
copyByteArray marr s1 arr s2 l = do
  let siz = A.sizeofMutableByteArray marr
  check "copyByteArray: index range of out bounds"
    (s1 >= 0 && s2 >= 0 && l >= 0 && s1 + l <= siz && s2 + l <= A.sizeofByteArray arr)
    (A.copyByteArray marr s1 arr s2 l)

copyMutableByteArray :: forall m. (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ destination array
  -> Int -- ^ offset into destination array
  -> MutableByteArray (PrimState m) -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of elements to copy
  -> m ()
copyMutableByteArray marr1 s1 marr2 s2 l = do
  let siz1 = A.sizeofMutableByteArray marr1
  let siz2 = A.sizeofMutableByteArray marr2
  check "copyMutableByteArray: index range of out bounds"
    (s1 >= 0 && s2 >= 0 && l >= 0 && s1 + l <= siz1 && s2 + l <= siz2)
    (A.copyMutableByteArray marr1 s1 marr2 s2 l)

copyByteArrayToPtr :: forall m a. (HasCallStack, PrimMonad m, Prim a)
  => Ptr a -- ^ destination pointer
  -> ByteArray -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of elements to copy
  -> m ()
copyByteArrayToPtr ptr arr s l = do
  let srcSz = elementSizeofByteArray (Proxy :: Proxy a) arr
  let explain = L.concat
        [ "[src_sz: "
        , show srcSz
        , ", src_off: "
        , show s
        , ", len: "
        , show l
        , "]"
        ]
  check ("copyByteArrayToPtr: index range of out bounds " ++ explain)
    (s >= 0 && l >= 0 && s + l <= srcSz)
    (A.copyByteArrayToPtr ptr arr s l)

copyMutableByteArrayToPtr :: forall m a. (HasCallStack, PrimMonad m, Prim a)
  => Ptr a -- ^ destination pointer
  -> MutableByteArray (PrimState m) -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of elements to copy
  -> m ()
copyMutableByteArrayToPtr ptr marr s l = do
  srcSz <- getElementSizeofMutableByteArray (Proxy :: Proxy a) marr
  let explain = L.concat
        [ "[src_sz: "
        , show srcSz
        , ", src_off: "
        , show s
        , ", len: "
        , show l
        , "]"
        ]
  check ("copyMutableByteArrayToPtr: index range of out bounds " ++ explain)
    (s >= 0 && l >= 0 && s + l <= srcSz)
    (A.copyMutableByteArrayToPtr ptr marr s l)

copyByteArrayToAddr :: (HasCallStack, PrimMonad m)
  => Ptr Word8 -- ^ destination pointer
  -> ByteArray -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of bytes to copy
  -> m ()
copyByteArrayToAddr ptr arr s l = do
  let srcSz = A.sizeofByteArray arr
  let explain = L.concat
        [ "[src_sz: "
        , show srcSz
        , ", src_off: "
        , show s
        , ", len: "
        , show l
        , "]"
        ]
  check ("copyByteArrayToAddr: index range of out bounds " ++ explain)
    (s >= 0 && l >= 0 && s + l <= srcSz)
    (A.copyByteArrayToAddr ptr arr s l)

copyMutableByteArrayToAddr :: (HasCallStack, PrimMonad m)
  => Ptr Word8 -- ^ destination pointer
  -> MutableByteArray (PrimState m) -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of bytes to copy
  -> m ()
copyMutableByteArrayToAddr ptr marr s l = do
  srcSz <- A.getSizeofMutableByteArray marr
  let explain = L.concat
        [ "[src_sz: "
        , show srcSz
        , ", src_off: "
        , show s
        , ", len: "
        , show l
        , "]"
        ]
  check ("copyMutableByteArrayToAddr: index range of out bounds " ++ explain)
    (s >= 0 && l >= 0 && s + l <= srcSz)
    (A.copyMutableByteArrayToAddr ptr marr s l)

moveByteArray :: forall m. (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ destination array
  -> Int -- ^ offset into destination array
  -> MutableByteArray (PrimState m) -- ^ source array
  -> Int -- ^ offset into source array
  -> Int -- ^ number of bytes to copy
  -> m ()
moveByteArray marr1 s1 marr2 s2 l = do
  let siz1 = A.sizeofMutableByteArray marr1
  let siz2 = A.sizeofMutableByteArray marr2
  check "moveByteArray: index range of out bounds"
    (s1 >= 0 && s2 >= 0 && l >= 0 && s1 + l <= siz1 && s2 + l <= siz2)
    (A.moveByteArray marr1 s1 marr2 s2 l)

fillByteArray :: (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ array to fill
  -> Int -- ^ offset into array
  -> Int -- ^ number of bytes to fill
  -> Word8 -- ^ byte to fill with
  -> m ()
fillByteArray = setByteArray

setByteArray :: forall m a. (HasCallStack, Prim a, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ array to fill
  -> Int -- ^ offset into array
  -> Int -- ^ number of values to fill
  -> a -- ^ value to fill with
  -> m ()
setByteArray dst doff sz x = do
  siz <- getElementSizeofMutableByteArray (Proxy :: Proxy a) dst
  check "setByteArray: index range of out bounds"
    (doff >= 0 && doff + sz <= siz)
    (A.setByteArray dst doff sz x)

cloneByteArray :: HasCallStack
  => ByteArray -- ^ source array
  -> Int       -- ^ offset into source array
  -> Int       -- ^ number of bytes to copy
  -> ByteArray
cloneByteArray arr s l = check "cloneByteArray: index range of out bounds"
  (s >= 0 && l >= 0 && s + l <= A.sizeofByteArray arr)
  (A.cloneByteArray arr s l)

cloneMutableByteArray :: (HasCallStack, PrimMonad m)
  => MutableByteArray (PrimState m) -- ^ source array
  -> Int                            -- ^ offset into source array
  -> Int                            -- ^ number of bytes to copy
  -> m (MutableByteArray (PrimState m))
cloneMutableByteArray marr s l = check "cloneMutableByteArray: index range of out bounds"
  (s >= 0 && l >= 0 && s + l <= A.sizeofMutableByteArray marr)
  (A.cloneMutableByteArray marr s l)