1{-# LANGUAGE UnboxedTuples #-}
4-- Module : Streamly.Internal.Data.MutByteArray
5-- Copyright : (c) 2023 Composewell Technologies
6-- License : BSD3-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11module Streamly.Internal.Data.MutByteArray
16 , getMutableByteArray#
29 , pinnedNewAlignedBytes
32 , sizeOfMutableByteArray
37import Control.Monad.IO.Class (MonadIO(..))
39import Debug.Trace (trace)
41import GHC.Base (IO(..))
42import System.IO.Unsafe (unsafePerformIO)
46--------------------------------------------------------------------------------
47-- The ArrayContents type
48--------------------------------------------------------------------------------
54-- XXX can use UnliftedNewtypes
56-- | A lifted mutable byte array type wrapping @MutableByteArray# RealWorld@.
57-- This is a low level array used to back high level unboxed arrays and
59data MutByteArray = MutByteArray (MutableByteArray# RealWorld)
61{-# DEPRECATED MutableByteArray "Please use MutByteArray instead" #-}
62type MutableByteArray = MutByteArray
64{-# INLINE getMutableByteArray# #-}
65getMutableByteArray# :: MutByteArray -> MutableByteArray# RealWorld
66getMutableByteArray# (MutByteArray mbarr) = mbarr
68-- | Return the size of the array in bytes.
69{-# INLINE sizeOfMutableByteArray #-}
70sizeOfMutableByteArray :: MutByteArray -> IO Int
71sizeOfMutableByteArray (MutByteArray arr) =
73 case getSizeofMutableByteArray# arr s of
74 (# s1, i #) -> (# s1, I# i #)
77touch :: MutByteArray -> IO ()
78touch (MutByteArray contents) =
79 IO $ \s -> case touch# contents s of s' -> (# s', () #)
81-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
82-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
83-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
84-- before the pointer is wrapped.
86-- From the GHC manual:
88-- GHC, since version 8.4, guarantees that garbage collection will never occur
89-- during an unsafe call, even in the bytecode interpreter, and further
90-- guarantees that unsafe calls will be performed in the calling thread. Making
91-- it safe to pass heap-allocated objects to unsafe functions.
93-- | Use a @MutByteArray@ as @Ptr a@. This is useful when we want to pass
94-- an array as a pointer to some operating system call or to a "safe" FFI call.
96-- If the array is not pinned it is copied to pinned memory before passing it
97-- to the monadic action.
99-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised
100-- that the programmer keeps this in mind and creates a pinned array
101-- opportunistically before this operation occurs, to avoid the cost of a copy
104-- /Unsafe/ because of direct pointer operations. The user must ensure that
105-- they are writing within the legal bounds of the array.
109{-# INLINE asPtrUnsafe #-}
110asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
111asPtrUnsafe arr f = do
112 contents <- liftIO $ pin arr
113 let !ptr = Ptr (byteArrayContents#
114 (unsafeCoerce# (getMutableByteArray# contents)))
116 liftIO $ touch contents
119--------------------------------------------------------------------------------
121--------------------------------------------------------------------------------
125nil = unsafePerformIO $ newByteArray 0
127{-# INLINE newByteArray #-}
128newByteArray :: Int -> IO MutByteArray
129newByteArray nbytes | nbytes < 0 =
130 errorWithoutStackTrace "newByteArray: size must be >= 0"
131newByteArray (I# nbytes) = IO $ \s ->
132 case newByteArray# nbytes s of
134 let c = MutByteArray mbarr#
137{-# INLINE pinnedNewByteArray #-}
138pinnedNewByteArray :: Int -> IO MutByteArray
139pinnedNewByteArray nbytes | nbytes < 0 =
140 errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"
141pinnedNewByteArray (I# nbytes) = IO $ \s ->
142 case newPinnedByteArray# nbytes s of
144 let c = MutByteArray mbarr#
147{-# INLINE pinnedNewAlignedBytes #-}
148pinnedNewAlignedBytes :: Int -> Int -> IO MutByteArray
149pinnedNewAlignedBytes nbytes _align | nbytes < 0 =
150 errorWithoutStackTrace "pinnedNewAlignedBytes: size must be >= 0"
151pinnedNewAlignedBytes (I# nbytes) (I# align) = IO $ \s ->
152 case newAlignedPinnedByteArray# nbytes align s of
154 let c = MutByteArray mbarr#
157{-# INLINE newBytesAs #-}
158newBytesAs :: PinnedState -> Int -> IO MutByteArray
159newBytesAs Unpinned = newByteArray
160newBytesAs Pinned = pinnedNewByteArray
162-------------------------------------------------------------------------------
164-------------------------------------------------------------------------------
166-- | Put a sub range of a source array into a subrange of a destination array.
167-- This is not safe as it does not check the bounds.
168{-# INLINE putSliceUnsafe #-}
177putSliceUnsafe src srcStartBytes dst dstStartBytes lenBytes = liftIO $ do
179 srcLen <- sizeOfMutableByteArray src
180 dstLen <- sizeOfMutableByteArray src
181 when (srcLen - srcStartBytes < lenBytes) $ error "SRC: Insufficient length."
182 when (dstLen - dstStartBytes < lenBytes) $ error "DST: Insufficient length."
184 let !(I# srcStartBytes#) = srcStartBytes
185 !(I# dstStartBytes#) = dstStartBytes
186 !(I# lenBytes#) = lenBytes
187 let arrS# = getMutableByteArray# src
188 arrD# = getMutableByteArray# dst
189 IO $ \s# -> (# copyMutableByteArray#
190 arrS# srcStartBytes# arrD# dstStartBytes# lenBytes# s#
193-------------------------------------------------------------------------------
194-- Pinning & Unpinning
195-------------------------------------------------------------------------------
197{-# INLINE isPinned #-}
198isPinned :: MutByteArray -> Bool
199isPinned (MutByteArray arr#) =
200 let pinnedInt = I# (isMutableByteArrayPinned# arr#)
204{-# INLINE cloneMutableArrayWith# #-}
205cloneMutableArrayWith#
206 :: (Int# -> State# RealWorld -> (# State# RealWorld
207 , MutableByteArray# RealWorld #))
208 -> MutableByteArray# RealWorld
210 -> (# State# RealWorld, MutableByteArray# RealWorld #)
211cloneMutableArrayWith# alloc# arr# s# =
212 case getSizeofMutableByteArray# arr# s# of
214 case alloc# i# s1# of
216 case copyMutableByteArray# arr# 0# arr1# 0# i# s2# of
217 s3# -> (# s3#, arr1# #)
220pin :: MutByteArray -> IO MutByteArray
221pin arr@(MutByteArray marr#) =
227 -- XXX dump stack trace
228 trace ("pin: Copying array") (return ())
232 case cloneMutableArrayWith# newPinnedByteArray# marr# s# of
233 (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
236unpin :: MutByteArray -> IO MutByteArray
237unpin arr@(MutByteArray marr#) =
238 if not (isPinned arr)
243 -- XXX dump stack trace
244 trace ("unpin: Copying array") (return ())
248 case cloneMutableArrayWith# newByteArray# marr# s# of
249 (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list