1{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE MagicHash #-}
4{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE UnboxedTuples #-}
6{-# LANGUAGE ScopedTypeVariables #-}
11-- Module : Streamly.Internal.Memory.ArrayStream
12-- Copyright : (c) 2019 Composewell Technologies
15-- Maintainer : harendra.kumar@gmail.com
16-- Stability : experimental
19-- Combinators to efficiently manipulate streams of arrays.
21module Streamly.Internal.Memory.ArrayStream
26 -- * Flattening to elements
42import Control.Monad.IO.Class (MonadIO(..))
43-- import Data.Functor.Identity (Identity)
44import Data.Word (Word8)
45import Foreign.ForeignPtr (withForeignPtr)
46import Foreign.Ptr (minusPtr, plusPtr, castPtr)
47import Foreign.Storable (Storable(..))
48import Prelude hiding (length, null, last, map, (!!), read, concat)
50import Streamly.Internal.Memory.Array.Types (Array(..), length)
51import Streamly.Streams.Serial (SerialT)
52import Streamly.Streams.StreamK.Type (IsStream)
54import qualified Streamly.Internal.Data.Unfold as UF
55import qualified Streamly.Internal.Memory.Array as A
56import qualified Streamly.Internal.Memory.Array.Types as A
57import qualified Streamly.Internal.Prelude as S
58import qualified Streamly.Streams.StreamD as D
59import qualified Streamly.Streams.Prelude as P
61-- XXX efficiently compare two streams of arrays. Two streams can have chunks
62-- of different sizes, we can handle that in the stream comparison abstraction.
63-- This could be useful e.g. to fast compare whether two files differ.
65-- | Convert a stream of arrays into a stream of their elements.
67-- Same as the following but more efficient:
69-- > concat = S.concatMap A.read
73concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
74-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
75-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
76concat m = D.fromStreamD $ D.concatMapU A.read (D.toStreamD m)
78-- XXX should we have a reverseArrays API to reverse the stream of arrays
81-- | Convert a stream of arrays into a stream of their elements reversing the
82-- contents of each array before flattening.
85{-# INLINE concatRev #-}
86concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
87concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
89{-# INLINE intercalateSuffix #-}
90intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
91 => Array a -> t m (Array a) -> t m a
92intercalateSuffix arr = S.intercalateSuffix A.read arr A.read
94-- | Flatten a stream of arrays appending the given element after each
98{-# INLINE interposeSuffix #-}
99interposeSuffix :: (MonadIO m, IsStream t, Storable a)
100 => a -> t m (Array a) -> t m a
101-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
102interposeSuffix x = S.intercalateSuffix UF.identity x A.read
104-- | Split a stream of arrays on a given separator byte, dropping the separator
105-- and coalescing all the arrays between two separators into a single array.
108{-# INLINE splitOn #-}
110 :: (IsStream t, MonadIO m)
115 D.fromStreamD $ D.splitInnerBy (A.breakOn byte) A.spliceTwo $ D.toStreamD s
117{-# INLINE splitOnSuffix #-}
119 :: (IsStream t, MonadIO m)
123-- splitOn byte s = D.fromStreamD $ A.splitOn byte $ D.toStreamD s
124splitOnSuffix byte s =
125 D.fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.spliceTwo $ D.toStreamD s
127-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
128-- maximum specified size in bytes.
131{-# INLINE compact #-}
132compact :: (MonadIO m, Storable a)
133 => Int -> SerialT m (Array a) -> SerialT m (Array a)
134compact n xs = D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
136-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
139-- Same as the following but more efficient:
141-- > arraysOf n = S.chunksOf n (A.writeN n)
144{-# INLINE arraysOf #-}
145arraysOf :: (IsStream t, MonadIO m, Storable a)
146 => Int -> t m a -> t m (Array a)
148 D.fromStreamD $ A.fromStreamDArraysOf n (D.toStreamD str)
150-- XXX Both of these implementations of splicing seem to perform equally well.
151-- We need to perform benchmarks over a range of sizes though.
153-- CAUTION! length must more than equal to lengths of all the arrays in the
155{-# INLINE spliceArraysLenUnsafe #-}
156spliceArraysLenUnsafe :: (MonadIO m, Storable a)
157 => Int -> SerialT m (Array a) -> m (Array a)
158spliceArraysLenUnsafe len buffered = do
159 arr <- liftIO $ A.newArray len
160 end <- S.foldlM' writeArr (aEnd arr) buffered
161 return $ arr {aEnd = end}
165 writeArr dst Array{..} =
166 liftIO $ withForeignPtr aStart $ \src -> do
167 let count = aEnd `minusPtr` src
168 A.memcpy (castPtr dst) (castPtr src) count
169 return $ dst `plusPtr` count
171{-# INLINE _spliceArraysBuffered #-}
172_spliceArraysBuffered :: (MonadIO m, Storable a)
173 => SerialT m (Array a) -> m (Array a)
174_spliceArraysBuffered s = do
175 buffered <- P.foldr S.cons S.nil s
176 len <- S.sum (S.map length buffered)
177 spliceArraysLenUnsafe len s
179{-# INLINE spliceArraysRealloced #-}
180spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
181 => SerialT m (Array a) -> m (Array a)
182spliceArraysRealloced s = do
183 idst <- liftIO $ A.newArray (A.bytesToElemCount (undefined :: a)
186 arr <- S.foldlM' A.spliceWithDoubling idst s
187 liftIO $ A.shrinkToFit arr
189-- | Given a stream of arrays, splice them all together to generate a single
190-- array. The stream must be /finite/.
193{-# INLINE toArray #-}
194toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
195toArray = spliceArraysRealloced
196-- spliceArrays = _spliceArraysBuffered
198-- exponentially increasing sizes of the chunks upto the max limit.
199-- XXX this will be easier to implement with parsers/terminating folds
200-- With this we should be able to reduce the number of chunks/allocations.
201-- The reallocation/copy based toArray can also be implemented using this.
204{-# INLINE toArraysInRange #-}
205toArraysInRange :: (IsStream t, MonadIO m, Storable a)
206 => Int -> Int -> Fold m (Array a) b -> Fold m a b
207toArraysInRange low high (Fold step initial extract) =
211-- | Fold the input to a pure buffered stream (List) of arrays.
212{-# INLINE _toArraysOf #-}
213_toArraysOf :: (MonadIO m, Storable a)
214 => Int -> Fold m a (SerialT Identity (Array a))
215_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