4-- Module : Streamly.Internal.Memory.ArrayStream
5-- Copyright : (c) 2020 Composewell Technologies
7-- License : BSD3-3-Clause
8-- Maintainer : streamly@composewell.com
9-- Stability : experimental
12-- Combinators to efficiently manipulate streams of arrays.
14module Streamly.Internal.Memory.ArrayStream
19 -- * Flattening to elements
36import Control.Monad.IO.Class (MonadIO(..))
37-- import Data.Functor.Identity (Identity)
38import Data.Word (Word8)
39import Foreign.ForeignPtr (withForeignPtr)
40import Foreign.Ptr (minusPtr, plusPtr, castPtr)
41import Foreign.Storable (Storable(..))
42import Prelude hiding (length, null, last, map, (!!), read, concat)
44import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..), length)
45import Streamly.Internal.Data.Stream.Serial (SerialT)
46import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
48import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
49import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as A
50import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
51import qualified Streamly.Internal.Data.Stream.IsStream as S
52import qualified Streamly.Internal.Data.Stream.StreamD as D
54-- XXX efficiently compare two streams of arrays. Two streams can have chunks
55-- of different sizes, we can handle that in the stream comparison abstraction.
56-- This could be useful e.g. to fast compare whether two files differ.
58-- | Convert a stream of arrays into a stream of their elements.
60-- Same as the following but more efficient:
62-- > concat = S.concatMap A.read
66concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
67-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
68-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
69concat m = D.fromStreamD $ D.concatMapU A.read (D.toStreamD m)
71-- XXX should we have a reverseArrays API to reverse the stream of arrays
74-- | Convert a stream of arrays into a stream of their elements reversing the
75-- contents of each array before flattening.
78{-# INLINE concatRev #-}
79concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
80concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
82-- | Flatten a stream of arrays after inserting the given element between
86{-# INLINE interpose #-}
87interpose :: (MonadIO m, IsStream t, Storable a) => a -> t m (Array a) -> t m a
88interpose x = S.interpose x A.read
90{-# INLINE intercalateSuffix #-}
91intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
92 => Array a -> t m (Array a) -> t m a
93intercalateSuffix arr = S.intercalateSuffix arr A.read
95-- | Flatten a stream of arrays appending the given element after each
99{-# INLINE interposeSuffix #-}
100interposeSuffix :: (MonadIO m, IsStream t, Storable a)
101 => a -> t m (Array a) -> t m a
102-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
103interposeSuffix x = S.interposeSuffix x A.read
105-- | Split a stream of arrays on a given separator byte, dropping the separator
106-- and coalescing all the arrays between two separators into a single array.
109{-# INLINE splitOn #-}
111 :: (IsStream t, MonadIO m)
116 D.fromStreamD $ D.splitInnerBy (A.breakOn byte) A.spliceTwo $ D.toStreamD s
118{-# INLINE splitOnSuffix #-}
120 :: (IsStream t, MonadIO m)
124-- splitOn byte s = D.fromStreamD $ A.splitOn byte $ D.toStreamD s
125splitOnSuffix byte s =
126 D.fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.spliceTwo $ D.toStreamD s
128-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
129-- maximum specified size in bytes.
132{-# INLINE compact #-}
133compact :: (MonadIO m, Storable a)
134 => Int -> SerialT m (Array a) -> SerialT m (Array a)
135compact n xs = D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
137-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
140-- Same as the following but more efficient:
142-- > arraysOf n = S.chunksOf n (A.writeN n)
145{-# INLINE arraysOf #-}
146arraysOf :: (IsStream t, MonadIO m, Storable a)
147 => Int -> t m a -> t m (Array a)
149 D.fromStreamD $ A.fromStreamDArraysOf n (D.toStreamD str)
151-- XXX Both of these implementations of splicing seem to perform equally well.
152-- We need to perform benchmarks over a range of sizes though.
154-- CAUTION! length must more than equal to lengths of all the arrays in the
156{-# INLINE spliceArraysLenUnsafe #-}
157spliceArraysLenUnsafe :: (MonadIO m, Storable a)
158 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
159spliceArraysLenUnsafe len buffered = do
160 arr <- liftIO $ MA.newArray len
161 end <- S.foldlM' writeArr (return $ MA.aEnd arr) buffered
162 return $ arr {MA.aEnd = end}
166 writeArr dst (MA.Array as ae _) =
167 liftIO $ withForeignPtr as $ \src -> do
168 let count = ae `minusPtr` src
169 A.memcpy (castPtr dst) (castPtr src) count
170 return $ dst `plusPtr` count
172{-# INLINE _spliceArrays #-}
173_spliceArrays :: (MonadIO m, Storable a)
174 => SerialT m (Array a) -> m (Array a)
176 buffered <- S.foldr S.cons S.nil s
177 len <- S.sum (S.map length buffered)
178 arr <- liftIO $ MA.newArray len
179 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
180 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
184 writeArr dst (Array as ae) =
185 liftIO $ withForeignPtr as $ \src -> do
186 let count = ae `minusPtr` src
187 A.memcpy (castPtr dst) (castPtr src) count
188 return $ dst `plusPtr` count
190{-# INLINE _spliceArraysBuffered #-}
191_spliceArraysBuffered :: (MonadIO m, Storable a)
192 => SerialT m (Array a) -> m (Array a)
193_spliceArraysBuffered s = do
194 buffered <- S.foldr S.cons S.nil s
195 len <- S.sum (S.map length buffered)
196 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
198{-# INLINE spliceArraysRealloced #-}
199spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
200 => SerialT m (Array a) -> m (Array a)
201spliceArraysRealloced s = do
202 let idst = liftIO $ MA.newArray (A.bytesToElemCount (undefined :: a)
205 arr <- S.foldlM' MA.spliceWithDoubling idst (S.map A.unsafeThaw s)
206 liftIO $ A.unsafeFreeze <$> MA.shrinkToFit arr
208-- | Given a stream of arrays, splice them all together to generate a single
209-- array. The stream must be /finite/.
212{-# INLINE toArray #-}
213toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
214toArray = spliceArraysRealloced
215-- spliceArrays = _spliceArraysBuffered
217-- exponentially increasing sizes of the chunks upto the max limit.
218-- XXX this will be easier to implement with parsers/terminating folds
219-- With this we should be able to reduce the number of chunks/allocations.
220-- The reallocation/copy based toArray can also be implemented using this.
223{-# INLINE toArraysInRange #-}
224toArraysInRange :: (IsStream t, MonadIO m, Storable a)
225 => Int -> Int -> Fold m (Array a) b -> Fold m a b
226toArraysInRange low high (Fold step initial extract) =
230-- | Fold the input to a pure buffered stream (List) of arrays.
231{-# INLINE _toArraysOf #-}
232_toArraysOf :: (MonadIO m, Storable a)
233 => Int -> Fold m a (SerialT Identity (Array a))
234_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
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