{-| Module : Z.Data.Array.Checked Description : Bounded checked boxed and unboxed arrays Copyright : (c) Dong Han, 2017-2019 License : BSD Maintainer : winterland1989@gmail.com Stability : experimental Portability : non-portable Unified unboxed and boxed array operations using type family. This module re-export "Z.Data.Array.Base" module, but add check when @check-array-bound@ flag is set. To debug array algorithms just add @Z-Data: -f+check-array-bound@ to your local @cabal.project@ file. otherwise, none of the operations are bound checked. Some mnemonics: * 'newArr' and 'newArrWith' return mutable array. 'readArr' and 'writeArr' perform read and write actions on mutable arrays. 'setArr' fills the elements with offset and length. 'indexArr' only works on immutable Array, use 'indexArr'' to avoid thunks building up in the heap. * 'freezeArr' and 'thawArr' make a copy thus need slicing params. 'unsafeFreezeArr' and 'unsafeThawArr' DO NOT COPY, use with care. * The order of arguements of 'copyArr', 'copyMutableArr' and 'moveArr' are always target and its offset come first, and source and source offset follow, copying length comes last. -} module Z.Data.Array ( -- * Arr typeclass re-export Arr, MArr , A.emptyArr, A.singletonArr, A.doubletonArr , modifyIndexArr, insertIndexArr, deleteIndexArr , RealWorld -- * Boxed array type , A.Array(..) , A.MutableArray(..) , A.SmallArray(..) , A.SmallMutableArray(..) , A.uninitialized -- * Primitive array type , A.PrimArray(..) , A.MutablePrimArray(..) , Prim(..) -- * Bound checked array operations , newArr , newArrWith , readArr , writeArr , setArr , indexArr , indexArr' , indexArrM , freezeArr , thawArr , copyArr , copyMutableArr , moveArr , cloneArr , cloneMutableArr , resizeMutableArr , shrinkMutableArr -- * No bound checked operations , A.unsafeFreezeArr , A.unsafeThawArr , A.sameMutableArr , A.sizeofArr , A.sizeofMutableArr , A.sameArr -- * Bound checked primitive array operations , newPinnedPrimArray, newAlignedPinnedPrimArray , copyPrimArrayToPtr, copyMutablePrimArrayToPtr, copyPtrToMutablePrimArray -- * No bound checked primitive array operations , A.primArrayContents, A.mutablePrimArrayContents, A.withPrimArrayContents, A.withMutablePrimArrayContents , A.isPrimArrayPinned, A.isMutablePrimArrayPinned -- * Unlifted array type , A.UnliftedArray(..) , A.MutableUnliftedArray(..) , A.PrimUnlifted(..) -- * The 'ArrayException' type , ArrayException(..) -- * Cast between primitive arrays , A.Cast , A.castArray , A.castMutableArray -- * Re-export , sizeOf ) where import Control.Exception (ArrayException (..), throw) import Control.Monad.Primitive import Data.Primitive.Types import GHC.Stack import Z.Data.Array.Base (Arr, MArr) import qualified Z.Data.Array.Base as A #ifdef CHECK_ARRAY_BOUND import Control.Monad import Control.Monad.ST #endif #ifdef CHECK_ARRAY_BOUND check :: HasCallStack => Bool -> a -> a {-# INLINE check #-} check True x = x check False _ = throw (IndexOutOfBounds $ show callStack) #endif -- | Make a new array with a given size. -- -- For boxed arrays, all elements are 'uninitialized' , which shall not be accessed. -- For primitive arrays, elements are just random garbage. newArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => Int -> m (MArr arr s a) newArr n = #ifdef CHECK_ARRAY_BOUND check (n>=0) (A.newArr n) #else A.newArr n #endif {-# INLINE newArr #-} -- | Make a new array and fill it with an initial value. newArrWith :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => Int -> a -> m (MArr arr s a) newArrWith n x = #ifdef CHECK_ARRAY_BOUND check (n>=0) (A.newArrWith n x) #else (A.newArrWith n x) #endif {-# INLINE newArrWith #-} -- | Read from specified index of mutable array in a primitive monad. readArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> m a readArr marr i = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (i>=0 && i MArr arr s a -> Int -> a -> m () writeArr marr i x = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (i>=0 && i MArr arr s a -> Int -> Int -> a -> m () setArr marr s l x = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (s>=0 && l>=0 && (s+l)<=siz) (A.setArr marr s l x) #else (A.setArr marr s l x) #endif {-# INLINE setArr #-} -- | Read from the specified index of an immutable array. It's pure and often -- results in an indexing thunk for lifted arrays, use 'indexArr\'' or 'indexArrM' to avoid this. indexArr :: (Arr arr a, HasCallStack) => arr a -> Int -> a indexArr arr i = #ifdef CHECK_ARRAY_BOUND check (i>=0 && i arr a -> Int -> (# a #) indexArr' arr i = #ifdef CHECK_ARRAY_BOUND if (i>=0 && i copy marr arr ... = do ... -- > writeArray marr i (indexArray arr i) ... -- > ... -- -- But since primitive arrays are lazy, the calls to 'indexArray' will not be -- evaluated. Rather, @marr@ will be filled with thunks each of which would -- retain a reference to @arr@. This is definitely not what we want! -- -- With 'indexArrayM', we can instead write -- -- > copy marr arr ... = do ... -- > x <- indexArrayM arr i -- > writeArray marr i x -- > ... -- -- Now, indexing is executed immediately although the returned element is -- still not evaluated. -- -- /Note:/ this function does not do bounds checking. indexArrM :: (Arr arr a, Monad m, HasCallStack) => arr a -> Int -> m a indexArrM arr i = #ifdef CHECK_ARRAY_BOUND check (i>=0 && i MArr arr s a -> Int -> Int -> m (arr a) freezeArr marr s l = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (s>=0 && l>=0 && (s+l)<=siz) (A.freezeArr marr s l) #else (A.freezeArr marr s l) #endif {-# INLINE freezeArr #-} -- | Create a mutable array from a slice of an immutable array. -- This operation makes a copy of the specified slice, so it is safe to use the immutable array afterward. thawArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => arr a -> Int -> Int -> m (MArr arr s a) thawArr arr s l = #ifdef CHECK_ARRAY_BOUND check (s>=0 && l>=0 && (s+l)<=A.sizeofArr arr) (A.thawArr arr s l) #else (A.thawArr arr s l) #endif {-# INLINE thawArr #-} -- | Copy a slice of an immutable array to a mutable array at given offset. copyArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> arr a -> Int -> Int -> m () copyArr marr s1 arr s2 l = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (s1>=0 && s2>=0 && l>=0 && (s2+l)<=A.sizeofArr arr && (s1+l)<=siz) (A.copyArr marr s1 arr s2 l) #else (A.copyArr marr s1 arr s2 l) #endif {-# INLINE copyArr #-} -- | Copy a slice of a mutable array to another mutable array at given offset. -- The two mutable arrays must not be the same. copyMutableArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> MArr arr s a -> Int -> Int -> m () copyMutableArr marr1 s1 marr2 s2 l = do #ifdef CHECK_ARRAY_BOUND siz1 <- A.sizeofMutableArr marr1 siz2 <- A.sizeofMutableArr marr2 check (s1>=0 && s2>=0 && l>=0 && (s2+l)<=siz2 && (s1+l)<=siz1) (A.copyMutableArr marr1 s1 marr2 s2 l) #else (A.copyMutableArr marr1 s1 marr2 s2 l) #endif {-# INLINE copyMutableArr #-} -- | Copy a slice of a mutable array to a mutable array at given offset. -- The two mutable arrays can be the same. moveArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> MArr arr s a -> Int -> Int -> m () moveArr marr1 s1 marr2 s2 l = do #ifdef CHECK_ARRAY_BOUND siz1 <- A.sizeofMutableArr marr1 siz2 <- A.sizeofMutableArr marr2 check (s1>=0 && s2>=0 && l>=0 && (s2+l)<=siz2 && (s1+l)<=siz1) (A.moveArr marr1 s1 marr2 s2 l) #else (A.moveArr marr1 s1 marr2 s2 l) #endif {-# INLINE moveArr #-} -- | Create an immutable copy with the given subrange of the original array. cloneArr :: (Arr arr a, HasCallStack) => arr a -> Int -> Int -> arr a cloneArr arr s l = #ifdef CHECK_ARRAY_BOUND check (s>=0 && l>=0 && (s+l)<=A.sizeofArr arr) (A.cloneArr arr s l) #else (A.cloneArr arr s l) #endif {-# INLINE cloneArr #-} -- | Create a mutable copy the given subrange of the original array. cloneMutableArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> Int -> m (MArr arr s a) cloneMutableArr marr s l = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (s>=0 && l>=0 && (s+l)<=siz) (A.cloneMutableArr marr s l) #else (A.cloneMutableArr marr s l) #endif {-# INLINE cloneMutableArr #-} -- | Resize a mutable array to the given size. resizeMutableArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> m (MArr arr s a) resizeMutableArr marr n = #ifdef CHECK_ARRAY_BOUND check (n>=0) (A.resizeMutableArr marr n) #else (A.resizeMutableArr marr n) #endif {-# INLINE resizeMutableArr #-} -- | Shrink a mutable array to the given size. This operation only works on primitive arrays. -- For some array types, this is a no-op, e.g. 'sizeOfMutableArr' will not change. -- -- New size should be >= 0, and <= original size. shrinkMutableArr :: (Arr arr a, PrimMonad m, PrimState m ~ s, HasCallStack) => MArr arr s a -> Int -> m () shrinkMutableArr marr n = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (n>=0 && n<=siz) (A.shrinkMutableArr marr n) #else (A.shrinkMutableArr marr n) #endif {-# INLINE shrinkMutableArr #-} -------------------------------------------------------------------------------- -- | Create a /pinned/ byte array of the specified size, -- The garbage collector is guaranteed not to move it. newPinnedPrimArray :: (PrimMonad m, Prim a, HasCallStack) => Int -> m (A.MutablePrimArray (PrimState m) a) {-# INLINE newPinnedPrimArray #-} newPinnedPrimArray n = #ifdef CHECK_ARRAY_BOUND check (n>=0) (A.newPinnedPrimArray n) #else (A.newPinnedPrimArray n) #endif -- | Create a /pinned/ primitive array of the specified size and respect given primitive type's -- alignment. The garbage collector is guaranteed not to move it. -- newAlignedPinnedPrimArray :: (PrimMonad m, Prim a, HasCallStack) => Int -> m (A.MutablePrimArray (PrimState m) a) {-# INLINE newAlignedPinnedPrimArray #-} newAlignedPinnedPrimArray n = #ifdef CHECK_ARRAY_BOUND check (n>=0) (A.newAlignedPinnedPrimArray n) #else (A.newAlignedPinnedPrimArray n) #endif copyPrimArrayToPtr :: (PrimMonad m, Prim a, HasCallStack) => Ptr a -> A.PrimArray a -> Int -> Int -> m () {-# INLINE copyPrimArrayToPtr #-} copyPrimArrayToPtr ptr arr s l = #ifdef CHECK_ARRAY_BOUND check (s>=0 && l>=0 && (s+l)<=A.sizeofArr arr) (A.copyPrimArrayToPtr ptr arr s l) #else (A.copyPrimArrayToPtr ptr arr s l) #endif copyMutablePrimArrayToPtr :: (PrimMonad m, Prim a, HasCallStack) => Ptr a -> A.MutablePrimArray (PrimState m) a -> Int -> Int -> m () {-# INLINE copyMutablePrimArrayToPtr #-} copyMutablePrimArrayToPtr ptr marr s l = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (s>=0 && l>=0 && (s+l)<=siz) (A.copyMutablePrimArrayToPtr ptr marr s l) #else (A.copyMutablePrimArrayToPtr ptr marr s l) #endif copyPtrToMutablePrimArray :: (PrimMonad m, Prim a, HasCallStack) => A.MutablePrimArray (PrimState m) a -> Int -> Ptr a -> Int -> m () {-# INLINE copyPtrToMutablePrimArray #-} copyPtrToMutablePrimArray marr s ptr l = do #ifdef CHECK_ARRAY_BOUND siz <- A.sizeofMutableArr marr check (s>=0 && l>=0 && (s+l)<=siz) (A.copyPtrToMutablePrimArray marr s ptr l) #else (A.copyPtrToMutablePrimArray marr s ptr l) #endif -------------------------------------------------------------------------------- modifyIndexArr :: (Arr arr a, HasCallStack) => arr a -> Int -- ^ offset -> Int -- ^ length -> Int -- ^ index in new array -> (a -> a) -- ^ modify function -> arr a {-# INLINE modifyIndexArr #-} modifyIndexArr arr off len ix f = #ifdef CHECK_ARRAY_BOUND runST $ do marr <- A.unsafeThawArr (cloneArr arr off len) !v <- f <$> readArr marr ix writeArr marr ix v A.unsafeFreezeArr marr #else A.modifyIndexArr arr off len ix f #endif -- | Insert an immutable array's element at given index to produce a new array. insertIndexArr :: Arr arr a => arr a -> Int -- ^ offset -> Int -- ^ length -> Int -- ^ insert index in new array -> a -- ^ element to be inserted -> arr a {-# INLINE insertIndexArr #-} insertIndexArr arr s l i x = #ifdef CHECK_ARRAY_BOUND runST $ do marr <- newArrWith (l+1) x when (i>0) $ copyArr marr 0 arr s i when (i arr a -> Int -- ^ offset -> Int -- ^ length -> Int -- ^ drop index in new array -> arr a {-# INLINE deleteIndexArr #-} deleteIndexArr arr s l i = #ifdef CHECK_ARRAY_BOUND runST $ do marr <- newArr (l-1) when (i>0) $ copyArr marr 0 arr s i let i' = i+1 when (i'