@@ -23,29 +23,34 @@ module Streamly.Mem.Array.Types
23 Array (..)
23 Array (..)
25 -- * Construction
25 -- * Construction
27 , withNewArray
26 , withNewArray
28 , newArray
27 , newArray
29 , unsafeSnoc
28 , unsafeSnoc
31− , shrinkToFit
30+ , spliceWithDoubling
35 , fromList
32 , fromList
36 , fromListN
33 , fromListN
37 , fromStreamDN
34 , fromStreamDN
38 -- , fromStreamD
35 -- , fromStreamD
37+ -- * Streams of arrays
39 , fromStreamDArraysOf
38 , fromStreamDArraysOf
40 , flattenArrays
39 , flattenArrays
41 , flattenArraysRev
40 , flattenArraysRev
43 -- * Elimination
45 -- * Elimination
44 , unsafeIndexIO
46 , unsafeIndexIO
45 , unsafeIndex
47 , unsafeIndex
50 , toStreamD
55 , toStreamD
51 , toStreamDRev
56 , toStreamDRev
@@ -59,7 +64,11 @@ module Streamly.Mem.Array.Types
59 , defaultChunkSize
64 , defaultChunkSize
60 , mkChunkSize
65 , mkChunkSize
61 , mkChunkSizeKB
66 , mkChunkSizeKB
62− , reallocDouble
67+ , unsafeInlineIO
@@ -74,11 +83,11 @@ import Data.Word (Word8)
74import Foreign.C.String (CString)
83import Foreign.C.String (CString)
75import Foreign.C.Types (CSize(..), CInt(..))
84import Foreign.C.Types (CSize(..), CInt(..))
76import Foreign.ForeignPtr
85import Foreign.ForeignPtr
77− (ForeignPtr, withForeignPtr, touchForeignPtr)
86+ (ForeignPtr, withForeignPtr, touchForeignPtr, plusForeignPtr)
78import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
87import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
79−import Foreign.Ptr (plusPtr, minusPtr, castPtr)
88+import Foreign.Ptr (plusPtr, minusPtr, castPtr, nullPtr)
80import Foreign.Storable (Storable(..))
89import Foreign.Storable (Storable(..))
81−import Prelude hiding (length, foldr, read, unlines, unwords)
90+import Prelude hiding (length, foldr, read, unlines, splitAt)
82import Text.Read (readPrec, readListPrec, readListPrecDefault)
91import Text.Read (readPrec, readListPrec, readListPrecDefault)
84import GHC.Base (Addr#, realWorld#)
93import GHC.Base (Addr#, realWorld#)
@@ -89,6 +98,7 @@ import GHC.Ptr (Ptr(..))
90import Streamly.Fold.Types (Fold(..))
99import Streamly.Fold.Types (Fold(..))
91import Streamly.SVar (adaptState)
100import Streamly.SVar (adaptState)
101+import Streamly.FileSystem.FDIO (IOVec(..))
93import qualified Streamly.Streams.StreamD.Type as D
103import qualified Streamly.Streams.StreamD.Type as D
94import qualified Streamly.Streams.StreamK as K
104import qualified Streamly.Streams.StreamK as K
@@ -165,6 +175,9 @@ foreign import ccall unsafe "string.h memcpy" c_memcpy
165foreign import ccall unsafe "string.h strlen" c_strlen
175foreign import ccall unsafe "string.h strlen" c_strlen
166 :: CString -> IO CSize
176 :: CString -> IO CSize
178+foreign import ccall unsafe "string.h memchr" c_memchr
179+ :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)
168-- XXX we are converting Int to CSize
181-- XXX we are converting Int to CSize
169memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
182memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
170memcpy dst src len = c_memcpy dst src (fromIntegral len) >> return ()
183memcpy dst src len = c_memcpy dst src (fromIntegral len) >> return ()
@@ -222,14 +235,14 @@ withNewArray count f = do
222unsafeSnoc :: forall a. Storable a => Array a -> a -> IO (Array a)
235unsafeSnoc :: forall a. Storable a => Array a -> a -> IO (Array a)
223unsafeSnoc arr@Array{..} x = do
236unsafeSnoc arr@Array{..} x = do
224 when (aEnd == aBound) $
237 when (aEnd == aBound) $
225− error "BUG: unsafeAppend: writing beyond array bounds"
238+ error "BUG: unsafeSnoc: writing beyond array bounds"
226 poke aEnd x
239 poke aEnd x
227 touchForeignPtr aStart
240 touchForeignPtr aStart
228 return $ arr {aEnd = aEnd `plusPtr` (sizeOf (undefined :: a))}
241 return $ arr {aEnd = aEnd `plusPtr` (sizeOf (undefined :: a))}
230{-# INLINE snoc #-}
243{-# INLINE snoc #-}
231−snoc :: forall a. Storable a => Array a -> a -> Array a
244+snoc :: forall a. Storable a => Array a -> a -> IO (Array a)
232−snoc arr@Array {..} x = unsafePerformIO $
245+snoc arr@Array {..} x = do
233 if (aEnd == aBound)
246 if (aEnd == aBound)
235 let oldStart = unsafeForeignPtrToPtr aStart
248 let oldStart = unsafeForeignPtrToPtr aStart
@@ -250,37 +263,14 @@ snoc arr@Array {..} x = unsafePerformIO $
250 touchForeignPtr aStart
263 touchForeignPtr aStart
251 return $ arr {aEnd = aEnd `plusPtr` (sizeOf (undefined :: a))}
264 return $ arr {aEnd = aEnd `plusPtr` (sizeOf (undefined :: a))}
253−
266+-- | Reallocate the array to the specified size in bytes. If the size is less
254−-- | Remove the free space from an Array.
267+-- than the original array the array gets truncated.
255−shrinkToFit :: forall a. Storable a => Array a -> IO (Array a)
256−shrinkToFit arr@Array{..} = do
257− assert (aEnd <= aBound) (return ())
260− let oldStart = unsafeForeignPtrToPtr aStart
261− let size = aEnd `minusPtr` oldStart
262− newPtr <- mallocPlainForeignPtrAlignedBytes
263− size (alignment (undefined :: a))
264− withForeignPtr newPtr $ \pNew -> do
265− memcpy (castPtr pNew) (castPtr oldStart) size
266− touchForeignPtr aStart
267− let end = pNew `plusPtr` size
275−-- | Expand the free space in the array doubling the size with a minimum
276−-- exapnsion of the specified amount of bytes.
277−{-# NOINLINE reallocDouble #-}
268+{-# NOINLINE realloc #-}
278−reallocDouble :: forall a. Storable a => Int -> Array a -> IO (Array a)
269+realloc :: forall a. Storable a => Int -> Array a -> IO (Array a)
279−reallocDouble minIncrease Array{..} = do
270+realloc newSize Array{..} = do
280 assert (aEnd <= aBound) (return ())
271 assert (aEnd <= aBound) (return ())
281 let oldStart = unsafeForeignPtrToPtr aStart
272 let oldStart = unsafeForeignPtrToPtr aStart
282 let size = aEnd `minusPtr` oldStart
273 let size = aEnd `minusPtr` oldStart
283− newSize = max (size * 2) (size + minIncrease)
284 newPtr <- mallocPlainForeignPtrAlignedBytes
274 newPtr <- mallocPlainForeignPtrAlignedBytes
285 newSize (alignment (undefined :: a))
275 newSize (alignment (undefined :: a))
286 withForeignPtr newPtr $ \pNew -> do
276 withForeignPtr newPtr $ \pNew -> do
@@ -292,6 +282,17 @@ reallocDouble minIncrease Array{..} = do
292 , aBound = pNew `plusPtr` newSize
282 , aBound = pNew `plusPtr` newSize
285+-- | Remove the free space from an Array.
286+shrinkToFit :: forall a. Storable a => Array a -> IO (Array a)
287+shrinkToFit arr@Array{..} = do
288+ assert (aEnd <= aBound) (return ())
291+ let oldStart = unsafeForeignPtrToPtr aStart
292+ let size = aEnd `minusPtr` oldStart
295-- XXX when converting an array of Word8 from a literal string we can simply
296-- XXX when converting an array of Word8 from a literal string we can simply
296-- refer to the literal string. Is it possible to write rules such that
297-- refer to the literal string. Is it possible to write rules such that
297-- fromList Word8 can be rewritten so that GHC does not first convert the
298-- fromList Word8 can be rewritten so that GHC does not first convert the
@@ -340,15 +341,30 @@ unsafeIndexIO Array {..} i =
340unsafeIndex :: forall a. Storable a => Array a -> Int -> a
341unsafeIndex :: forall a. Storable a => Array a -> Int -> a
341unsafeIndex arr i = let !r = unsafeInlineIO $ unsafeIndexIO arr i in r
342unsafeIndex arr i = let !r = unsafeInlineIO $ unsafeIndexIO arr i in r
344+-- | /O(1)/ Get the byte length of the array.
347+{-# INLINE byteLength #-}
348+byteLength :: Array a -> Int
349+byteLength Array{..} =
350+ let p = unsafeForeignPtrToPtr aStart
351+ len = aEnd `minusPtr` p
352+ in assert (len >= 0) len
343−-- | /O(1)/ Get the length of the array.
354+-- | /O(1)/ Get the length of the array i.e. the number of elements in the
345-- @since 0.7.0
357-- @since 0.7.0
346{-# INLINE length #-}
358{-# INLINE length #-}
347length :: forall a. Storable a => Array a -> Int
359length :: forall a. Storable a => Array a -> Int
348−length Array{..} =
360+length arr = byteLength arr `div` sizeOf (undefined :: a)
362+{-# INLINE byteCapacity #-}
363+byteCapacity :: Array a -> Int
364+byteCapacity Array{..} =
349 let p = unsafeForeignPtrToPtr aStart
365 let p = unsafeForeignPtrToPtr aStart
350− aLen = aEnd `minusPtr` p
366+ len = aBound `minusPtr` p
351− in assert (aLen >= 0) (aLen `div` sizeOf (undefined :: a))
367+ in assert (len >= 0) len
353{-# INLINE_NORMAL toStreamD #-}
369{-# INLINE_NORMAL toStreamD #-}
354toStreamD :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
370toStreamD :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
@@ -729,8 +745,9 @@ defaultChunkSize :: Int
729defaultChunkSize = mkChunkSizeKB 32
745defaultChunkSize = mkChunkSizeKB 32
731{-# INLINE_NORMAL unlines #-}
747{-# INLINE_NORMAL unlines #-}
748+unlines :: forall m a. (MonadIO m, Storable a)
732−unlines :: MonadIO m => D.Stream m (Array Char) -> D.Stream m Char
749+ => a -> D.Stream m (Array a) -> D.Stream m a
733−unlines (D.Stream step state) = D.Stream step' (OuterLoop state)
750+unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
735 {-# INLINE_LATE step' #-}
752 {-# INLINE_LATE step' #-}
736 step' gst (OuterLoop st) = do
753 step' gst (OuterLoop st) = do
@@ -743,7 +760,7 @@ unlines (D.Stream step state) = D.Stream step' (OuterLoop state)
743 D.Stop -> D.Stop
760 D.Stop -> D.Stop
745 step' _ (InnerLoop st _ p end) | p == end =
762 step' _ (InnerLoop st _ p end) | p == end =
746− return $ D.Yield '\n' $ OuterLoop st
763+ return $ D.Yield sep $ OuterLoop st
748 step' _ (InnerLoop st startf p end) = do
765 step' _ (InnerLoop st startf p end) = do
749 x <- liftIO $ do
766 x <- liftIO $ do
@@ -751,4 +768,272 @@ unlines (D.Stream step state) = D.Stream step' (OuterLoop state)
751 touchForeignPtr startf
768 touchForeignPtr startf
753 return $ D.Yield x (InnerLoop st startf
770 return $ D.Yield x (InnerLoop st startf
754− (p `plusPtr` (sizeOf (undefined :: Char))) end)
771+ (p `plusPtr` (sizeOf (undefined :: a))) end)
773+{-# INLINE spliceTwo #-}
774+spliceTwo :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
775+spliceTwo arr1 arr2 = do
776+ let src1 = unsafeForeignPtrToPtr (aStart arr1)
777+ src2 = unsafeForeignPtrToPtr (aStart arr2)
778+ len1 = aEnd arr1 `minusPtr` src1
779+ len2 = aEnd arr2 `minusPtr` src2
781+ arr <- liftIO $ newArray (len1 + len2)
782+ let dst = unsafeForeignPtrToPtr (aStart arr)
785+ memcpy (castPtr dst) (castPtr src1) len1
786+ touchForeignPtr (aStart arr1)
787+ memcpy (castPtr (dst `plusPtr` len1)) (castPtr src2) len2
788+ touchForeignPtr (aStart arr2)
789+ return arr { aEnd = dst `plusPtr` (len1 + len2) }
791+-- Splice a new array into a pre-reserved array. The user must ensure that
792+-- there is enough space in the array.
793+{-# INLINE spliceWith #-}
794+spliceWith :: (MonadIO m) => Array a -> Array a -> m (Array a)
795+spliceWith dst@(Array _ end bound) src = liftIO $ do
796+ let srcLen = byteLength src
797+ if end `plusPtr` srcLen > bound
798+ then error "Bug: spliceIntoUnsafe: Not enough space in the target array"
800+ withForeignPtr (aStart dst) $ \_ -> do
801+ withForeignPtr (aStart src) $ \psrc -> do
803+ memcpy (castPtr pdst) (castPtr psrc) srcLen
804+ return $ dst { aEnd = pdst `plusPtr` srcLen }
806+-- Splice a new array into a preallocated array, doubling the space if there is
807+-- no space in the target array.
808+{-# INLINE spliceWithDoubling #-}
809+spliceWithDoubling :: (MonadIO m, Storable a)
810+ => Array a -> Array a -> m (Array a)
811+spliceWithDoubling dst@(Array start end bound) src = do
812+ assert (end <= bound) (return ())
813+ let srcLen = aEnd src `minusPtr` unsafeForeignPtrToPtr (aStart src)
816+ if end `plusPtr` srcLen >= bound
818+ let oldStart = unsafeForeignPtrToPtr start
819+ oldSize = end `minusPtr` oldStart
820+ newSize = max (oldSize * 2) (oldSize + srcLen)
821+ liftIO $ realloc newSize dst
825+data SpliceState s arr
827+ | SpliceBuffering s arr
828+ | SpliceYielding arr (SpliceState s arr)
831+-- XXX can use general grouping combinators to achieve this?
832+-- | Coalesce adajcent arrays in incoming stream to form bigger arrays of a
833+-- maximum specified size.
836+{-# INLINE_NORMAL coalesceChunksOf #-}
837+coalesceChunksOf :: (MonadIO m, Storable a)
838+ => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
839+coalesceChunksOf n (D.Stream step state) = D.Stream step' (SpliceInitial state)
843+ {-# INLINE_LATE step' #-}
844+ step' gst (SpliceInitial st) = do
847+ D.Yield arr s -> return $
848+ let len = byteLength arr
850+ then D.Skip (SpliceYielding arr (SpliceInitial s))
851+ else D.Skip (SpliceBuffering s arr)
852+ D.Skip s -> return $ D.Skip (SpliceInitial s)
853+ D.Stop -> return $ D.Stop
855+ step' gst (SpliceBuffering st buf) = do
859+ let len = byteLength buf + byteLength arr
861+ then return $ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
863+ buf' <- if byteCapacity buf < n
864+ then liftIO $ realloc n buf
866+ buf'' <- spliceWith buf' arr
867+ return $ D.Skip (SpliceBuffering s buf'')
868+ D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
869+ D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
871+ step' _ SpliceFinish = return D.Stop
873+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
875+data GatherState s arr
877+ | GatherBuffering s arr Int
878+ | GatherYielding arr (GatherState s arr)
881+-- | @groupIOVecsOf maxBytes maxEntries@ groups arrays in the incoming stream
882+-- to create a stream of 'IOVec' arrays with a maximum of @maxBytes@ bytes in
883+-- each array and a maximum of @maxEntries@ entries in each array.
886+{-# INLINE_NORMAL groupIOVecsOf #-}
887+groupIOVecsOf :: MonadIO m
888+ => Int -> Int -> D.Stream m (Array a) -> D.Stream m (Array IOVec)
889+groupIOVecsOf n maxIOVLen (D.Stream step state) =
890+ D.Stream step' (GatherInitial state)
894+ {-# INLINE_LATE step' #-}
895+ step' gst (GatherInitial st) = do
896+ r <- step (adaptState gst) st
899+ let p = unsafeForeignPtrToPtr (aStart arr)
900+ len = byteLength arr
901+ iov <- liftIO $ newArray maxIOVLen
902+ iov' <- liftIO $ unsafeSnoc iov (IOVec (castPtr p)
905+ then return $ D.Skip (GatherYielding iov' (GatherInitial s))
906+ else return $ D.Skip (GatherBuffering s iov' len)
907+ D.Skip s -> return $ D.Skip (GatherInitial s)
908+ D.Stop -> return $ D.Stop
910+ step' gst (GatherBuffering st iov len) = do
911+ r <- step (adaptState gst) st
914+ let p = unsafeForeignPtrToPtr (aStart arr)
915+ alen = byteLength arr
917+ if len' > n || length iov >= maxIOVLen
919+ iov' <- liftIO $ newArray maxIOVLen
920+ iov'' <- liftIO $ unsafeSnoc iov' (IOVec (castPtr p)
921+ (fromIntegral alen))
922+ return $ D.Skip (GatherYielding iov
923+ (GatherBuffering s iov'' alen))
925+ iov' <- liftIO $ unsafeSnoc iov (IOVec (castPtr p)
926+ (fromIntegral alen))
927+ return $ D.Skip (GatherBuffering s iov' len')
928+ D.Skip s -> return $ D.Skip (GatherBuffering s iov len)
929+ D.Stop -> return $ D.Skip (GatherYielding iov GatherFinish)
931+ step' _ GatherFinish = return D.Stop
933+ step' _ (GatherYielding iov next) = return $ D.Yield iov next
935+-- | Create two slices of an array without copying the original array. The
936+-- specified index @i@ is the first index of the second slice.
939+splitAt :: forall a. Storable a => Int -> Array a -> (Array a, Array a)
940+splitAt i arr@Array{..} =
941+ let maxIndex = length arr - 1
943+ then error "sliceAt: negative array index"
944+ else if i > maxIndex
945+ then error $ "sliceAt: specified array index " ++ show i
946+ ++ " is beyond the maximum index " ++ show maxIndex
947+ else let off = i * sizeOf (undefined :: a)
948+ p = unsafeForeignPtrToPtr aStart `plusPtr` off
955+ { aStart = aStart `plusForeignPtr` off
961+-- Drops the separator byte
962+{-# INLINE breakOn #-}
964+ => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
965+breakOn sep arr@Array{..} = liftIO $ do
966+ let p = unsafeForeignPtrToPtr aStart
967+ loc <- c_memchr p sep (fromIntegral $ aEnd `minusPtr` p)
978+ { aStart = aStart `plusForeignPtr` (loc `minusPtr` p + 1)
984+data SplitState s arr
988+ | Yielding arr (SplitState s arr)
991+-- | Split a stream of arrays on a given separator byte, dropping the separator
992+-- and coalescing all the arrays between two separators into a single array.
995+{-# INLINE_NORMAL splitOn #-}
999+ -> D.Stream m (Array Word8)
1000+ -> D.Stream m (Array Word8)
1001+splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
1005+ {-# INLINE_LATE step' #-}
1006+ step' gst (Initial st) = do
1009+ D.Yield arr s -> do
1010+ (arr1, marr2) <- breakOn byte arr
1011+ return $ case marr2 of
1012+ Nothing -> D.Skip (Buffering s arr1)
1013+ Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
1014+ D.Skip s -> return $ D.Skip (Initial s)
1015+ D.Stop -> return $ D.Stop
1017+ step' gst (Buffering st buf) = do
1020+ D.Yield arr s -> do
1021+ (arr1, marr2) <- breakOn byte arr
1022+ buf' <- spliceTwo buf arr1
1023+ return $ case marr2 of
1024+ Nothing -> D.Skip (Buffering s buf')
1025+ Just x -> D.Skip (Yielding buf' (Splitting s x))
1026+ D.Skip s -> return $ D.Skip (Buffering s buf)
1028+ if byteLength buf == 0
1030+ else D.Skip (Yielding buf Finishing)
1032+ step' _ (Splitting st buf) = do
1033+ (arr1, marr2) <- breakOn byte buf
1034+ return $ case marr2 of
1035+ Nothing -> D.Skip $ Buffering st arr1
1036+ Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
1038+ step' _ (Yielding arr next) = return $ D.Yield arr next
1039+ step' _ Finishing = return $ D.Stop