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.Memory.Array.Types (Array(..), length)
45import Streamly.Internal.Data.Stream.Serial (SerialT)
46import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
48import qualified Streamly.Internal.Memory.Array as A
49import qualified Streamly.Internal.Memory.Array.Types as A
50import qualified Streamly.Internal.Memory.Mutable.Array.Types as MA
51import qualified Streamly.Internal.Prelude as S
52import qualified Streamly.Internal.Data.Stream.StreamD as D
53import qualified Streamly.Internal.Data.Stream.Prelude as P
55-- XXX efficiently compare two streams of arrays. Two streams can have chunks
56-- of different sizes, we can handle that in the stream comparison abstraction.
57-- This could be useful e.g. to fast compare whether two files differ.
59-- | Convert a stream of arrays into a stream of their elements.
61-- Same as the following but more efficient:
63-- > concat = S.concatMap A.read
67concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
68-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
69-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
70concat m = D.fromStreamD $ D.concatMapU A.read (D.toStreamD m)
72-- XXX should we have a reverseArrays API to reverse the stream of arrays
75-- | Convert a stream of arrays into a stream of their elements reversing the
76-- contents of each array before flattening.
79{-# INLINE concatRev #-}
80concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
81concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
83-- | Flatten a stream of arrays after inserting the given element between
87{-# INLINE interpose #-}
88interpose :: (MonadIO m, IsStream t, Storable a) => a -> t m (Array a) -> t m a
89interpose x = S.interpose x A.read
91{-# INLINE intercalateSuffix #-}
92intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
93 => Array a -> t m (Array a) -> t m a
94intercalateSuffix arr = S.intercalateSuffix arr A.read
96-- | Flatten a stream of arrays appending the given element after each
100{-# INLINE interposeSuffix #-}
101interposeSuffix :: (MonadIO m, IsStream t, Storable a)
102 => a -> t m (Array a) -> t m a
103-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
104interposeSuffix x = S.interposeSuffix x A.read
106-- | Split a stream of arrays on a given separator byte, dropping the separator
107-- and coalescing all the arrays between two separators into a single array.
110{-# INLINE splitOn #-}
112 :: (IsStream t, MonadIO m)
117 D.fromStreamD $ D.splitInnerBy (A.breakOn byte) A.spliceTwo $ D.toStreamD s
119{-# INLINE splitOnSuffix #-}
121 :: (IsStream t, MonadIO m)
125-- splitOn byte s = D.fromStreamD $ A.splitOn byte $ D.toStreamD s
126splitOnSuffix byte s =
127 D.fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.spliceTwo $ D.toStreamD s
129-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
130-- maximum specified size in bytes.
133{-# INLINE compact #-}
134compact :: (MonadIO m, Storable a)
135 => Int -> SerialT m (Array a) -> SerialT m (Array a)
136compact n xs = D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
138-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
141-- Same as the following but more efficient:
143-- > arraysOf n = S.chunksOf n (A.writeN n)
146{-# INLINE arraysOf #-}
147arraysOf :: (IsStream t, MonadIO m, Storable a)
148 => Int -> t m a -> t m (Array a)
150 D.fromStreamD $ A.fromStreamDArraysOf n (D.toStreamD str)
152-- XXX Both of these implementations of splicing seem to perform equally well.
153-- We need to perform benchmarks over a range of sizes though.
155-- CAUTION! length must more than equal to lengths of all the arrays in the
157{-# INLINE spliceArraysLenUnsafe #-}
158spliceArraysLenUnsafe :: (MonadIO m, Storable a)
159 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
160spliceArraysLenUnsafe len buffered = do
161 arr <- liftIO $ MA.newArray len
162 end <- S.foldlM' writeArr (return $ MA.aEnd arr) buffered
163 return $ arr {MA.aEnd = end}
167 writeArr dst (MA.Array as ae _) =
168 liftIO $ withForeignPtr as $ \src -> do
169 let count = ae `minusPtr` src
170 A.memcpy (castPtr dst) (castPtr src) count
171 return $ dst `plusPtr` count
173{-# INLINE _spliceArrays #-}
174_spliceArrays :: (MonadIO m, Storable a)
175 => SerialT m (Array a) -> m (Array a)
177 buffered <- P.foldr S.cons S.nil s
178 len <- S.sum (S.map length buffered)
179 arr <- liftIO $ MA.newArray len
180 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
181 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
185 writeArr dst (Array as ae) =
186 liftIO $ withForeignPtr as $ \src -> do
187 let count = ae `minusPtr` src
188 A.memcpy (castPtr dst) (castPtr src) count
189 return $ dst `plusPtr` count
191{-# INLINE _spliceArraysBuffered #-}
192_spliceArraysBuffered :: (MonadIO m, Storable a)
193 => SerialT m (Array a) -> m (Array a)
194_spliceArraysBuffered s = do
195 buffered <- P.foldr S.cons S.nil s
196 len <- S.sum (S.map length buffered)
197 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
199{-# INLINE spliceArraysRealloced #-}
200spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
201 => SerialT m (Array a) -> m (Array a)
202spliceArraysRealloced s = do
203 let idst = liftIO $ MA.newArray (A.bytesToElemCount (undefined :: a)
206 arr <- S.foldlM' MA.spliceWithDoubling idst (S.map A.unsafeThaw s)
207 liftIO $ A.unsafeFreeze <$> MA.shrinkToFit arr
209-- | Given a stream of arrays, splice them all together to generate a single
210-- array. The stream must be /finite/.
213{-# INLINE toArray #-}
214toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
215toArray = spliceArraysRealloced
216-- spliceArrays = _spliceArraysBuffered
218-- exponentially increasing sizes of the chunks upto the max limit.
219-- XXX this will be easier to implement with parsers/terminating folds
220-- With this we should be able to reduce the number of chunks/allocations.
221-- The reallocation/copy based toArray can also be implemented using this.
224{-# INLINE toArraysInRange #-}
225toArraysInRange :: (IsStream t, MonadIO m, Storable a)
226 => Int -> Int -> Fold m (Array a) b -> Fold m a b
227toArraysInRange low high (Fold step initial extract) =
231-- | Fold the input to a pure buffered stream (List) of arrays.
232{-# INLINE _toArraysOf #-}
233_toArraysOf :: (MonadIO m, Storable a)
234 => Int -> Fold m a (SerialT Identity (Array a))
235_toArraysOf n = FL.lchunksOf 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