on 006fc5f help sign in
streamly/src/Streamly/Memory/ArrayStream.hs
203 lines 6.7 kB history raw
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE MagicHash #-}
4{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE UnboxedTuples #-}
6{-# LANGUAGE ScopedTypeVariables #-}
8#include "inline.hs"
10-- |
11-- Module : Streamly.Memory.ArrayStream
12-- Copyright : (c) 2019 Composewell Technologies
13--
14-- License : BSD3
15-- Maintainer : harendra.kumar@gmail.com
16-- Stability : experimental
17-- Portability : GHC
18--
19-- Combinators to efficiently manipulate streams of arrays.
20--
21module Streamly.Memory.ArrayStream
22 (
23 -- * Creation
24 arraysOf
26 -- * Flattening to elements
27 , concat
28 , concatRev
29 , interposeSuffix
30 , intercalateSuffix
32 -- * Transformation
33 , splitOn
34 , compact -- compact
36 -- * Elimination
37 , toArray
38 )
39where
41import Control.Monad.IO.Class (MonadIO(..))
42-- import Data.Functor.Identity (Identity)
43import Data.Word (Word8)
44import Foreign.ForeignPtr (withForeignPtr)
45import Foreign.Ptr (minusPtr, plusPtr, castPtr)
46import Foreign.Storable (Storable(..))
47import Prelude hiding (length, null, last, map, (!!), read, concat)
49import Streamly.Memory.Array.Types (Array(..), length)
50import Streamly.Streams.Serial (SerialT)
51import Streamly.Streams.StreamK.Type (IsStream)
53import qualified Streamly.Unfold as UF
54import qualified Streamly.Memory.Array.Types as A
55import qualified Streamly.Prelude.Internal as S
56import qualified Streamly.Streams.StreamD as D
57import qualified Streamly.Streams.Prelude as P
59-- XXX efficiently compare two streams of arrays. Two streams can have chunks
60-- of different sizes, we can handle that in the stream comparison abstraction.
61-- This could be useful e.g. to fast compare whether two files differ.
63-- | Convert a stream of arrays into a stream of their elements.
64--
65-- Same as the following but more efficient:
66--
67-- > concat = S.concatMap A.read
68--
69-- @since 0.7.0
70{-# INLINE concat #-}
71concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
72-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
73-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
74concat m = D.fromStreamD $ D.concatMapU A.readU (D.toStreamD m)
76-- XXX should we have a reverseArrays API to reverse the stream of arrays
77-- instead?
78--
79-- | Convert a stream of arrays into a stream of their elements reversing the
80-- contents of each array before flattening.
81--
82-- @since 0.7.0
83{-# INLINE concatRev #-}
84concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
85concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
87{-# INLINE intercalateSuffix #-}
88intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
89 => Array a -> t m (Array a) -> t m a
90intercalateSuffix arr = S.intercalateSuffix A.readU arr A.readU
92-- | Flatten a stream of arrays appending the given element after each
93-- array.
94--
95-- @since 0.7.0
96{-# INLINE interposeSuffix #-}
97interposeSuffix :: (MonadIO m, IsStream t, Storable a)
98 => a -> t m (Array a) -> t m a
99-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
100interposeSuffix x = S.intercalateSuffix UF.singleton x A.readU
102-- | Split a stream of arrays on a given separator byte, dropping the separator
103-- and coalescing all the arrays between two separators into a single array.
104--
105-- @since 0.7.0
106{-# INLINE splitOn #-}
107splitOn
108 :: (IsStream t, MonadIO m)
109 => Word8
110 -> t m (Array Word8)
111 -> t m (Array Word8)
112splitOn byte s = D.fromStreamD $ A.splitOn byte $ D.toStreamD s
114-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
115-- maximum specified size in bytes.
116--
117-- @since 0.7.0
118{-# INLINE compact #-}
119compact :: (MonadIO m, Storable a)
120 => Int -> SerialT m (Array a) -> SerialT m (Array a)
121compact n xs = D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
123-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
124-- @n@ elements each.
125--
126-- Same as the following but more efficient:
127--
128-- > arraysOf n = S.chunksOf n (A.writeN n)
129--
130-- @since 0.7.0
131{-# INLINE arraysOf #-}
132arraysOf :: (IsStream t, MonadIO m, Storable a)
133 => Int -> t m a -> t m (Array a)
134arraysOf n str =
135 D.fromStreamD $ A.fromStreamDArraysOf n (D.toStreamD str)
137-- XXX Both of these implementations of splicing seem to perform equally well.
138-- We need to perform benchmarks over a range of sizes though.
140-- CAUTION! length must more than equal to lengths of all the arrays in the
141-- stream.
142{-# INLINE spliceArraysLenUnsafe #-}
143spliceArraysLenUnsafe :: (MonadIO m, Storable a)
144 => Int -> SerialT m (Array a) -> m (Array a)
145spliceArraysLenUnsafe len buffered = do
146 arr <- liftIO $ A.newArray len
147 end <- S.foldlM' writeArr (aEnd arr) buffered
148 return $ arr {aEnd = end}
150 where
152 writeArr dst Array{..} =
153 liftIO $ withForeignPtr aStart $ \src -> do
154 let count = aEnd `minusPtr` src
155 A.memcpy (castPtr dst) (castPtr src) count
156 return $ dst `plusPtr` count
158{-# INLINE _spliceArraysBuffered #-}
159_spliceArraysBuffered :: (MonadIO m, Storable a)
160 => SerialT m (Array a) -> m (Array a)
161_spliceArraysBuffered s = do
162 buffered <- P.foldr S.cons S.nil s
163 len <- S.sum (S.map length buffered)
164 spliceArraysLenUnsafe len s
166{-# INLINE spliceArraysRealloced #-}
167spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
168 => SerialT m (Array a) -> m (Array a)
169spliceArraysRealloced s = do
170 idst <- liftIO $ A.newArray (A.bytesToElemCount (undefined :: a)
171 (A.mkChunkSizeKB 4))
173 arr <- S.foldlM' A.spliceWithDoubling idst s
174 liftIO $ A.shrinkToFit arr
176-- | Given a stream of arrays, splice them all together to generate a single
177-- array. The stream must be /finite/.
178--
179-- @since 0.7.0
180{-# INLINE toArray #-}
181toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
182toArray = spliceArraysRealloced
183-- spliceArrays = _spliceArraysBuffered
185-- exponentially increasing sizes of the chunks upto the max limit.
186-- XXX this will be easier to implement with parsers/terminating folds
187-- With this we should be able to reduce the number of chunks/allocations.
188-- The reallocation/copy based toArray can also be implemented using this.
189--
190{-
191{-# INLINE toArraysInRange #-}
192toArraysInRange :: (IsStream t, MonadIO m, Storable a)
193 => Int -> Int -> Fold m (Array a) b -> Fold m a b
194toArraysInRange low high (Fold step initial extract) =
195-}
197{-
198-- | Fold the input to a pure buffered stream (List) of arrays.
199{-# INLINE _toArraysOf #-}
200_toArraysOf :: (MonadIO m, Storable a)
201 => Int -> Fold m a (SerialT Identity (Array a))
202_toArraysOf n = FL.lchunksOf n (A.writeNF n) FL.toStream
203-}