module Foundation.Array.Unboxed.Mutable
( MUArray(..)
, sizeInMutableBytesOfContent
, mutableLength
, mutableLengthSize
, mutableSame
, new
, newPinned
, newNative
, mutableForeignMem
, copyAt
, copyFromPtr
, copyToPtr
, sub
, unsafeWrite
, unsafeRead
, write
, read
, withMutablePtr
) where
import GHC.Prim
import GHC.Types
import GHC.Ptr
import Foundation.Internal.Base
import qualified Foundation.Primitive.Runtime as Runtime
import Foundation.Internal.Primitive
import Foundation.Internal.Proxy
import Foundation.Primitive.Types.OffsetSize
import Foundation.Primitive.Monad
import Foundation.Primitive.Types
import Foundation.Primitive.FinalPtr
import Foundation.Primitive.Exception
import Foundation.Numerical
import Foreign.Marshal.Utils (copyBytes)
data MUArray ty st =
MUVecMA !(Offset ty)
!(CountOf ty)
!PinnedStatus
(MutableByteArray# st)
| MUVecAddr !(Offset ty)
!(CountOf ty)
!(FinalPtr ty)
mutableArrayProxyTy :: MUArray ty st -> Proxy ty
mutableArrayProxyTy _ = Proxy
sizeInMutableBytesOfContent :: PrimType ty => MUArray ty s -> Size8
sizeInMutableBytesOfContent = primSizeInBytes . mutableArrayProxyTy
mvectorProxyTy :: MUArray ty s -> Proxy ty
mvectorProxyTy _ = Proxy
read :: (PrimMonad prim, PrimType ty) => MUArray ty (PrimState prim) -> Offset ty -> prim ty
read array n
| isOutOfBound n len = primOutOfBound OOB_Read n len
| otherwise = unsafeRead array n
where len = mutableLengthSize array
unsafeRead :: (PrimMonad prim, PrimType ty) => MUArray ty (PrimState prim) -> Offset ty -> prim ty
unsafeRead (MUVecMA start _ _ mba) i = primMbaRead mba (start + i)
unsafeRead (MUVecAddr start _ fptr) i = withFinalPtr fptr $ \(Ptr addr) -> primAddrRead addr (start + i)
write :: (PrimMonad prim, PrimType ty) => MUArray ty (PrimState prim) -> Offset ty -> ty -> prim ()
write array n val
| isOutOfBound n len = primOutOfBound OOB_Write n len
| otherwise = unsafeWrite array n val
where
len = mutableLengthSize array
unsafeWrite :: (PrimMonad prim, PrimType ty) => MUArray ty (PrimState prim) -> Offset ty -> ty -> prim ()
unsafeWrite (MUVecMA start _ _ mba) i v = primMbaWrite mba (start+i) v
unsafeWrite (MUVecAddr start _ fptr) i v = withFinalPtr fptr $ \(Ptr addr) -> primAddrWrite addr (start+i) v
newPinned :: (PrimMonad prim, PrimType ty) => CountOf ty -> prim (MUArray ty (PrimState prim))
newPinned n = newFake n Proxy
where newFake :: (PrimMonad prim, PrimType ty) => CountOf ty -> Proxy ty -> prim (MUArray ty (PrimState prim))
newFake sz ty = primitive $ \s1 ->
case newAlignedPinnedByteArray# bytes 8# s1 of
(# s2, mba #) -> (# s2, MUVecMA (Offset 0) sz pinned mba #)
where
!(CountOf (I# bytes)) = sizeOfE (primSizeInBytes ty) sz
newUnpinned :: (PrimMonad prim, PrimType ty) => CountOf ty -> prim (MUArray ty (PrimState prim))
newUnpinned n = newFake n Proxy
where newFake :: (PrimMonad prim, PrimType ty) => CountOf ty -> Proxy ty -> prim (MUArray ty (PrimState prim))
newFake sz ty = primitive $ \s1 ->
case newByteArray# bytes s1 of
(# s2, mba #) -> (# s2, MUVecMA (Offset 0) sz unpinned mba #)
where
!(CountOf (I# bytes)) = sizeOfE (primSizeInBytes ty) sz
empty :: PrimMonad prim => prim (MUArray ty (PrimState prim))
empty = primitive $ \s1 -> case newByteArray# 0# s1 of { (# s2, mba #) -> (# s2, MUVecMA 0 0 unpinned mba #) }
new :: (PrimMonad prim, PrimType ty) => CountOf ty -> prim (MUArray ty (PrimState prim))
new sz
| sizeRecast sz <= maxSizeUnpinned = newUnpinned sz
| otherwise = newPinned sz
where
maxSizeUnpinned = Runtime.unsafeUArrayUnpinnedMaxSize
mutableSame :: MUArray ty st -> MUArray ty st -> Bool
mutableSame (MUVecMA sa ea _ ma) (MUVecMA sb eb _ mb) = and [ sa == sb, ea == eb, bool# (sameMutableByteArray# ma mb)]
mutableSame (MUVecAddr s1 e1 f1) (MUVecAddr s2 e2 f2) = and [ s1 == s2, e1 == e2, finalPtrSameMemory f1 f2 ]
mutableSame (MUVecMA {}) (MUVecAddr {}) = False
mutableSame (MUVecAddr {}) (MUVecMA {}) = False
newNative :: (PrimMonad prim, PrimType ty) => CountOf ty -> (MutableByteArray# (PrimState prim) -> prim ()) -> prim (MUArray ty (PrimState prim))
newNative n f = do
muvec <- new n
case muvec of
(MUVecMA _ _ _ mba) -> f mba >> return muvec
(MUVecAddr {}) -> error "internal error: unboxed new only supposed to allocate natively"
mutableForeignMem :: (PrimMonad prim, PrimType ty)
=> FinalPtr ty
-> Int
-> prim (MUArray ty (PrimState prim))
mutableForeignMem fptr nb = return $ MUVecAddr (Offset 0) (CountOf nb) fptr
copyAt :: (PrimMonad prim, PrimType ty)
=> MUArray ty (PrimState prim)
-> Offset ty
-> MUArray ty (PrimState prim)
-> Offset ty
-> CountOf ty
-> prim ()
copyAt (MUVecMA dstStart _ _ dstMba) ed uvec@(MUVecMA srcStart _ _ srcBa) es n =
primitive $ \st -> (# copyMutableByteArray# srcBa os dstMba od nBytes st, () #)
where
!sz = primSizeInBytes (mutableArrayProxyTy uvec)
!(Offset (I# os)) = offsetOfE sz (srcStart + es)
!(Offset (I# od)) = offsetOfE sz (dstStart + ed)
!(CountOf (I# nBytes)) = sizeOfE sz n
copyAt (MUVecMA dstStart _ _ dstMba) ed muvec@(MUVecAddr srcStart _ srcFptr) es n =
withFinalPtr srcFptr $ \srcPtr ->
let !(Ptr srcAddr) = srcPtr `plusPtr` os
in primitive $ \s -> (# compatCopyAddrToByteArray# srcAddr dstMba od nBytes s, () #)
where
!sz = primSizeInBytes (mutableArrayProxyTy muvec)
!(Offset os) = offsetOfE sz (srcStart + es)
!(Offset (I# od)) = offsetOfE sz (dstStart + ed)
!(CountOf (I# nBytes)) = sizeOfE sz n
copyAt dst od src os n = loop od os
where
!endIndex = os `offsetPlusE` n
loop !d !i
| i == endIndex = return ()
| otherwise = unsafeRead src i >>= unsafeWrite dst d >> loop (d+1) (i+1)
sub :: (PrimMonad prim, PrimType ty)
=> MUArray ty (PrimState prim)
-> Int
-> Int
-> prim (MUArray ty (PrimState prim))
sub (MUVecMA start sz pstatus mba) dropElems' takeElems
| takeElems <= 0 = empty
| resultEmpty = empty
| otherwise = return $ MUVecMA (start `offsetPlusE` dropElems) (min (CountOf takeElems) (sz dropElems)) pstatus mba
where
dropElems = max 0 (CountOf dropElems')
resultEmpty = dropElems >= sz
sub (MUVecAddr start sz addr) dropElems' takeElems
| takeElems <= 0 = empty
| resultEmpty = empty
| otherwise = return $ MUVecAddr (start `offsetPlusE` dropElems) (min (CountOf takeElems) (sz dropElems)) addr
where
dropElems = max 0 (CountOf dropElems')
resultEmpty = dropElems >= sz
mutableLength :: PrimType ty => MUArray ty st -> Int
mutableLength (MUVecMA _ (CountOf end) _ _) = end
mutableLength (MUVecAddr _ (CountOf end) _) = end
mutableLengthSize :: PrimType ty => MUArray ty st -> CountOf ty
mutableLengthSize (MUVecMA _ end _ _) = end
mutableLengthSize (MUVecAddr _ end _) = end
withMutablePtrHint :: (PrimMonad prim, PrimType ty)
=> Bool
-> Bool
-> MUArray ty (PrimState prim)
-> (Ptr ty -> prim a)
-> prim a
withMutablePtrHint _ _ vec@(MUVecAddr start _ fptr) f =
withFinalPtr fptr (\ptr -> f (ptr `plusPtr` os))
where
sz = primSizeInBytes (mvectorProxyTy vec)
!(Offset os) = offsetOfE sz start
withMutablePtrHint skipCopy skipCopyBack vec@(MUVecMA start vecSz pstatus a) f
| isPinned pstatus = mutableByteArrayContent a >>= \ptr -> f (ptr `plusPtr` os)
| otherwise = do
trampoline <- newPinned vecSz
if not skipCopy
then copyAt trampoline 0 vec 0 vecSz
else pure ()
r <- withMutablePtr trampoline f
if not skipCopyBack
then copyAt vec 0 trampoline 0 vecSz
else pure ()
pure r
where
!(Offset os) = offsetOfE sz start
sz = primSizeInBytes (mvectorProxyTy vec)
mutableByteArrayContent :: PrimMonad prim => MutableByteArray# (PrimState prim) -> prim (Ptr ty)
mutableByteArrayContent mba = primitive $ \s1 ->
case unsafeFreezeByteArray# mba s1 of
(# s2, ba #) -> (# s2, Ptr (byteArrayContents# ba) #)
withMutablePtr :: (PrimMonad prim, PrimType ty)
=> MUArray ty (PrimState prim)
-> (Ptr ty -> prim a)
-> prim a
withMutablePtr = withMutablePtrHint False False
copyFromPtr :: forall prim ty . (PrimMonad prim, PrimType ty)
=> Ptr ty -> CountOf ty -> MUArray ty (PrimState prim) -> prim ()
copyFromPtr (Ptr p) count (MUVecMA ofs arrSz _ mba)
| count > arrSz = primOutOfBound OOB_MemCopy (sizeAsOffset count) arrSz
| otherwise = primitive $ \st -> (# copyAddrToByteArray# p mba od countBytes st, () #)
where
!sz = primSizeInBytes (Proxy :: Proxy ty)
!(CountOf (I# countBytes)) = sizeOfE sz count
!(Offset (I# od)) = offsetOfE sz ofs
copyFromPtr p count (MUVecAddr ofs arrSz fptr)
| count > arrSz = primOutOfBound OOB_MemCopy (sizeAsOffset count) arrSz
| otherwise = withFinalPtr fptr $ \dstPtr ->
unsafePrimFromIO $ copyBytes (dstPtr `plusPtr` os) p bytes
where
sz = primSizeInBytes (Proxy :: Proxy ty)
!(CountOf bytes) = sizeOfE sz count
!(Offset os) = offsetOfE sz ofs
copyToPtr :: forall ty prim . (PrimType ty, PrimMonad prim)
=> MUArray ty (PrimState prim)
-> Ptr ty
-> prim ()
copyToPtr (MUVecMA start sz _ ma) (Ptr p) = primitive $ \s1 ->
case unsafeFreezeByteArray# ma s1 of
(# s2, ba #) -> (# compatCopyByteArrayToAddr# ba offset p szBytes s2, () #)
where
!(Offset (I# offset)) = offsetInBytes start
!(CountOf (I# szBytes)) = sizeInBytes sz
copyToPtr (MUVecAddr start sz fptr) dst =
unsafePrimFromIO $ withFinalPtr fptr $ \ptr -> copyBytes dst (ptr `plusPtr` os) szBytes
where
!(Offset os) = offsetInBytes start
!(CountOf szBytes) = sizeInBytes sz