on defafa6 help sign in
993 lines 35 kB history raw
1{-# LANGUAGE CPP #-}
2-- |
3-- Module : Streamly.Internal.Data.Array
4-- Copyright : (c) 2019 Composewell Technologies
5-- License : BSD3
6-- Maintainer : streamly@composewell.com
7-- Stability : experimental
8-- Portability : GHC
9--
10module Streamly.Internal.Data.Array
11 (
12 -- * Setup
13 -- $setup
15 -- * Design Notes
16 -- $design
18 -- * The Array Type
19 module Streamly.Internal.Data.Array.Type
21 -- * Construction
22 -- Monadic Folds
23 , writeLastN
25 -- * Random Access
26 -- , (!!)
27 , getIndex
28 , getIndexRev
29 , last -- XXX getLastIndex?
30 -- , getIndicesFrom -- read from a given position to the end of file
31 -- , getIndicesUpto -- read from beginning up to the given position
32 -- , getIndicesFromTo
33 -- , getIndicesFromRev -- read from a given position to the beginning of file
34 -- , getIndicesUptoRev -- read from end to the given position in file
35 , indexReader
36 , indexReaderFromThenTo
38 -- * Size
39 , null
41 -- * Search
42 , binarySearch
43 , findIndicesOf
44 -- getIndicesOf
45 , indexFinder -- see splitOn
46 -- , findIndexOf
47 -- , find
49 -- * Casting
50 , cast
51 , asBytes
52 , castUnsafe
53 , asCStringUnsafe
55 -- * Subarrays
56 , getSliceUnsafe
57 -- , getSlice
58 , sliceIndexerFromLen
59 , slicerFromLen
60 , splitOn
62 -- * Streaming Operations
63 , streamTransform
65 -- * Folding
66 , streamFold
67 , fold
69 -- * Stream of Arrays
71 -- XXX these are probably not very useful to have in this module as we can
72 -- express these idiomatically using streams.
73 , interpose
74 , interposeSuffix
75 , intercalateSuffix
77 , compactOnByte
78 , compactOnByteSuffix
80 , foldBreakChunks
81 , foldChunks
82 , foldBreakChunksK
83 , parseBreakChunksK
85 -- * Serialization
86 , encodeAs
87 , serialize
88 , pinnedSerialize
89 , deserialize
91 -- * Deprecated
92 , genSlicesFromLen
93 , getSlicesFromLen
94 , getIndices
95 )
96where
98#include "assert.hs"
99#include "inline.hs"
100#include "ArrayMacros.h"
102import Control.Monad.IO.Class (MonadIO(..))
103-- import Data.Bifunctor (first)
104-- import Data.Either (fromRight)
105import Data.Functor.Identity (Identity)
106import Data.Proxy (Proxy(..))
107import Data.Word (Word8)
108import Foreign.C.String (CString)
109import Foreign.Ptr (castPtr)
110import Foreign.Storable (Storable)
111import GHC.Types (SPEC(..))
112import Streamly.Internal.Data.Unbox (Unbox(..))
113import Prelude hiding (length, null, last, map, (!!), read, concat)
115import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..), MutByteArray)
116import Streamly.Internal.Data.Serialize.Type (Serialize)
117import Streamly.Internal.Data.Fold.Type (Fold(..))
118import Streamly.Internal.Data.Parser (Parser(..), Initial(..), ParseError(..))
119import Streamly.Internal.Data.Stream (Stream(..))
120import Streamly.Internal.Data.StreamK (StreamK)
121import Streamly.Internal.Data.SVar.Type (adaptState, defState)
122import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3Fused'(..))
123import Streamly.Internal.Data.Unfold.Type (Unfold(..))
124import Streamly.Internal.System.IO (unsafeInlineIO)
126import qualified Streamly.Internal.Data.Fold.Type as Fold
127import qualified Streamly.Internal.Data.Serialize.Type as Serialize
128import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
129import qualified Streamly.Internal.Data.MutArray as MA
130import qualified Streamly.Internal.Data.Fold as FL
131import qualified Streamly.Internal.Data.Ring as RB
132import qualified Streamly.Internal.Data.Parser as Parser
133-- import qualified Streamly.Internal.Data.ParserK as ParserK
134import qualified Streamly.Internal.Data.Stream as D
135import qualified Streamly.Internal.Data.Stream as Stream
136import qualified Streamly.Internal.Data.StreamK as StreamK
137import qualified Streamly.Internal.Data.Unfold as Unfold
138import qualified Prelude
140import Streamly.Internal.Data.Array.Type
142#include "DocTestDataArray.hs"
144-- $design
145--
146-- To summarize:
147--
148-- * Arrays are finite and fixed in size
149-- * provide /O(1)/ access to elements
150-- * store only data and not functions
151-- * provide efficient IO interfacing
152--
153-- 'Foldable' instance is not provided because the implementation would be much
154-- less efficient compared to folding via streams. 'Semigroup' and 'Monoid'
155-- instances should be used with care; concatenating arrays using binary
156-- operations can be highly inefficient. Instead, use
157-- 'Streamly.Internal.Data.Stream.Chunked.toArray' to concatenate N
158-- arrays at once.
159--
160-- Each array is one pointer visible to the GC. Too many small arrays (e.g.
161-- single byte) are only as good as holding those elements in a Haskell list.
162-- However, small arrays can be compacted into large ones to reduce the
163-- overhead. To hold 32GB memory in 32k sized buffers we need 1 million arrays
164-- if we use one array for each chunk. This is still significant to add
165-- pressure to GC.
167-------------------------------------------------------------------------------
168-- Elimination
169-------------------------------------------------------------------------------
171-- |
172--
173-- >>> null arr = Array.byteLength arr == 0
174--
175-- /Pre-release/
176{-# INLINE null #-}
177null :: Array a -> Bool
178null arr = byteLength arr == 0
180-- | Like 'getIndex' but indexes the array in reverse from the end.
181--
182-- /Pre-release/
183{-# INLINE getIndexRev #-}
184getIndexRev :: forall a. Unbox a => Int -> Array a -> Maybe a
185getIndexRev i arr =
186 unsafeInlineIO
187 $ do
188 let elemPtr = RINDEX_OF(arrEnd arr, i, a)
189 if i >= 0 && elemPtr >= arrStart arr
190 then Just <$> peekAt elemPtr (arrContents arr)
191 else return Nothing
193-- |
194--
195-- >>> last arr = Array.getIndexRev arr 0
196--
197-- /Pre-release/
198{-# INLINE last #-}
199last :: Unbox a => Array a -> Maybe a
200last = getIndexRev 0
202-------------------------------------------------------------------------------
203-- Folds with Array as the container
204-------------------------------------------------------------------------------
206-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
207-- stream to an 'Array'.
208--
209{-# INLINE writeLastN #-}
210writeLastN ::
211 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
212writeLastN n
213 | n <= 0 = fmap (const mempty) FL.drain
214 | otherwise = unsafeFreeze <$> Fold step initial done done
216 where
218 step (Tuple3Fused' rb rh i) a = do
219 rh1 <- liftIO $ RB.unsafeInsert rb rh a
220 return $ FL.Partial $ Tuple3Fused' rb rh1 (i + 1)
222 initial =
223 let f (a, b) = FL.Partial $ Tuple3Fused' a b (0 :: Int)
224 in fmap f $ liftIO $ RB.new n
226 done (Tuple3Fused' rb rh i) = do
227 arr <- MA.new n
228 -- XXX We should write a read unfold for ring.
229 foldFunc i rh MA.snocUnsafe arr rb
231 foldFunc i
232 | i < n = RB.unsafeFoldRingM
233 | otherwise = RB.unsafeFoldRingFullM
235-------------------------------------------------------------------------------
236-- Random Access
237-------------------------------------------------------------------------------
239-------------------------------------------------------------------------------
240-- Searching
241-------------------------------------------------------------------------------
243-- | Given a sorted array, perform a binary search to find the given element.
244-- Returns the index of the element if found.
245--
246-- /Unimplemented/
247{-# INLINE binarySearch #-}
248binarySearch :: a -> Array a -> Maybe Int
249binarySearch = undefined
251-- find/findIndex etc can potentially be implemented more efficiently on arrays
252-- compared to streams by using SIMD instructions.
253-- We can also return a bit array instead.
255-- Can use SIMD.
257-- | Perform a linear search to find all the indices where a given element is
258-- present in an array.
259--
260-- /Unimplemented/
261indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
262indexFinder = undefined
264-- |
265-- /Unimplemented/
266findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
267findIndicesOf p = Stream.unfold (indexFinder p)
269{-
270findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
271findIndexOf p = Unfold.fold Fold.one . Stream.unfold (indexFinder p)
273find :: (a -> Bool) -> Array a -> Bool
274find = Unfold.fold Fold.null . Stream.unfold (indexFinder p)
275-}
277-------------------------------------------------------------------------------
278-- Folds
279-------------------------------------------------------------------------------
281-- XXX We can potentially use SIMD instructions on arrays to fold faster.
283-------------------------------------------------------------------------------
284-- Slice
285-------------------------------------------------------------------------------
287-- | /O(1)/ Slice an array in constant time.
288--
289-- Caution: The bounds of the slice are not checked.
290--
291-- /Unsafe/
292--
293-- /Pre-release/
294{-# INLINE getSliceUnsafe #-}
295getSliceUnsafe ::
296 forall a. Unbox a
297 => Int -- ^ starting index
298 -> Int -- ^ length of the slice
299 -> Array a
300 -> Array a
301getSliceUnsafe index len (Array contents start e) =
302 let size = SIZE_OF(a)
303 start1 = start + (index * size)
304 end1 = start1 + (len * size)
305 in assert (end1 <= e) (Array contents start1 end1)
307-- | Split the array into a stream of slices using a predicate. The element
308-- matching the predicate is dropped.
309--
310-- /Pre-release/
311{-# INLINE splitOn #-}
312splitOn :: (Monad m, Unbox a) =>
313 (a -> Bool) -> Array a -> Stream m (Array a)
314splitOn predicate arr =
315 fmap (\(i, len) -> getSliceUnsafe i len arr)
316 $ D.indexOnSuffix predicate (read arr)
318{-# INLINE sliceIndexerFromLen #-}
319sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
320 => Int -- ^ from index
321 -> Int -- ^ length of the slice
322 -> Unfold m (Array a) (Int, Int)
323sliceIndexerFromLen from len =
324 Unfold.lmap unsafeThaw (MA.sliceIndexerFromLen from len)
326{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
327genSlicesFromLen :: forall m a. (Monad m, Unbox a)
328 => Int -- ^ from index
329 -> Int -- ^ length of the slice
330 -> Unfold m (Array a) (Int, Int)
331genSlicesFromLen = sliceIndexerFromLen
333-- | Generate a stream of slices of specified length from an array, starting
334-- from the supplied array index. The last slice may be shorter than the
335-- requested length.
336--
337-- /Pre-release//
338{-# INLINE slicerFromLen #-}
339slicerFromLen :: forall m a. (Monad m, Unbox a)
340 => Int -- ^ from index
341 -> Int -- ^ length of the slice
342 -> Unfold m (Array a) (Array a)
343slicerFromLen from len =
344 fmap unsafeFreeze
345 $ Unfold.lmap unsafeThaw (MA.slicerFromLen from len)
347{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
348getSlicesFromLen :: forall m a. (Monad m, Unbox a)
349 => Int -- ^ from index
350 -> Int -- ^ length of the slice
351 -> Unfold m (Array a) (Array a)
352getSlicesFromLen = slicerFromLen
354-------------------------------------------------------------------------------
355-- Random reads and writes
356-------------------------------------------------------------------------------
358-- XXX Change this to a partial function instead of a Maybe type? And use
359-- MA.getIndex instead.
360--
361-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
362--
363{-# INLINE getIndex #-}
364getIndex :: forall a. Unbox a => Int -> Array a -> Maybe a
365getIndex i arr =
366 unsafeInlineIO
367 $ do
368 let elemPtr = INDEX_OF(arrStart arr, i, a)
369 if i >= 0 && INDEX_VALID(elemPtr, arrEnd arr, a)
370 then Just <$> peekAt elemPtr (arrContents arr)
371 else return Nothing
373-- | Given a stream of array indices, read the elements on those indices from
374-- the supplied Array. An exception is thrown if an index is out of bounds.
375--
376-- This is the most general operation. We can implement other operations in
377-- terms of this:
378--
379-- @
380-- read =
381-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
382-- in Unfold.lmap f (indexReader arr)
383--
384-- readRev =
385-- let i = length arr - 1
386-- in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0)
387-- @
388--
389-- /Pre-release/
390{-# INLINE indexReader #-}
391indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
392indexReader m =
393 let unf = MA.indexReaderWith (return . unsafeInlineIO) m
394 in Unfold.lmap unsafeThaw unf
396-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
397{-# DEPRECATED getIndices "Please use getIndices instead." #-}
398getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
399getIndices = indexReader
401-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
402-- element is the array value from the index @from@ and the successive elements
403-- are from the indices in increments of @then@ up to @to@. Index enumeration
404-- can occur downwards or upwards depending on whether @then@ comes before or
405-- after @from@.
406--
407-- @
408-- getIndicesFromThenTo =
409-- let f (from, next, to, arr) =
410-- (Stream.enumerateFromThenTo from next to, arr)
411-- in Unfold.lmap f getIndices
412-- @
413--
414-- /Unimplemented/
415{-# INLINE indexReaderFromThenTo #-}
416indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
417indexReaderFromThenTo = undefined
419-------------------------------------------------------------------------------
420-- Transform via stream operations
421-------------------------------------------------------------------------------
423-- for non-length changing operations we can use the original length for
424-- allocation. If we can predict the length then we can use the prediction for
425-- new allocation. Otherwise we can use a hint and adjust dynamically.
427{-
428-- | Transform an array into another array using a pipe transformation
429-- operation.
430--
431{-# INLINE runPipe #-}
432runPipe :: (MonadIO m, Unbox a, Unbox b)
433 => Pipe m a b -> Array a -> m (Array b)
434runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
435-}
437-- XXX For transformations that cannot change the number of elements e.g. "map"
438-- we can use a predetermined array length.
439--
440-- | Transform an array into another array using a stream transformation
441-- operation.
442--
443-- /Pre-release/
444{-# INLINE streamTransform #-}
445streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
446 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
447streamTransform f arr =
448 Stream.fold (writeWith (length arr)) $ f (read arr)
450-------------------------------------------------------------------------------
451-- Casts
452-------------------------------------------------------------------------------
454-- | Cast an array having elements of type @a@ into an array having elements of
455-- type @b@. The array size must be a multiple of the size of type @b@
456-- otherwise accessing the last element of the array may result into a crash or
457-- a random value.
458--
459-- /Pre-release/
460--
461castUnsafe ::
462#ifdef DEVBUILD
463 Unbox b =>
464#endif
465 Array a -> Array b
466castUnsafe (Array contents start end) =
467 Array contents start end
469-- | Cast an @Array a@ into an @Array Word8@.
470--
471--
472asBytes :: Array a -> Array Word8
473asBytes = castUnsafe
475-- | Cast an array having elements of type @a@ into an array having elements of
476-- type @b@. The length of the array should be a multiple of the size of the
477-- target element otherwise 'Nothing' is returned.
478--
479--
480cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
481cast arr =
482 let len = byteLength arr
483 r = len `mod` SIZE_OF(b)
484 in if r /= 0
485 then Nothing
486 else Just $ castUnsafe arr
488-- | Convert an array of any type into a null terminated CString Ptr. If the
489-- array is unpinned it is first converted to a pinned array which requires a
490-- copy.
491--
492-- /Unsafe/
493--
494-- /O(n) Time: (creates a copy of the array)/
495--
496-- /Pre-release/
497--
498asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
499asCStringUnsafe arr act = do
500 let arr1 = asBytes arr <> fromList [0]
501 -- asPtrUnsafe makes sure the array is pinned
502 asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
504-------------------------------------------------------------------------------
505-- Folds
506-------------------------------------------------------------------------------
508-- XXX We can directly use toStreamD and D.fold here.
510-- | Fold an array using a 'Fold'.
511--
512-- /Pre-release/
513{-# INLINE fold #-}
514fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
515fold f arr = Stream.fold f (read arr)
517-- | Fold an array using a stream fold operation.
518--
519-- /Pre-release/
520{-# INLINE streamFold #-}
521streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
522streamFold f arr = f (read arr)
524--------------------------------------------------------------------------------
525-- Serialization
526--------------------------------------------------------------------------------
528{-# INLINE encodeAs #-}
529encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8
530encodeAs ps a =
531 unsafeInlineIO $ do
532 let len = Serialize.addSizeTo 0 a
533 mbarr <- MBA.newBytesAs ps len
534 off <- Serialize.serializeAt 0 mbarr a
535 assertM(len == off)
536 pure $ Array mbarr 0 off
538-- |
539-- Properties:
540-- 1. Identity: @deserialize . serialize == id@
541-- 2. Encoded equivalence: @serialize a == serialize a@
542{-# INLINE serialize #-}
543serialize :: Serialize a => a -> Array Word8
544serialize = encodeAs Unpinned
546-- | Serialize a Haskell type to a pinned byte array. The array is allocated
547-- using pinned memory so that it can be used directly in OS APIs for writing
548-- to file or sending over the network.
549--
550-- Properties:
551-- 1. Identity: @deserialize . pinnedSerialize == id@
552-- 2. Encoded equivalence: @pinnedSerialize a == pinnedSerialize a@
553{-# INLINE pinnedSerialize #-}
554pinnedSerialize :: Serialize a => a -> Array Word8
555pinnedSerialize = encodeAs Pinned
557-- | Decode a Haskell type from a byte array containing its serialized
558-- representation.
559{-# INLINE deserialize #-}
560deserialize :: Serialize a => Array Word8 -> a
561deserialize arr@(Array {..}) = unsafeInlineIO $ do
562 let lenArr = length arr
563 (off, val) <-
564 Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)
565 assertM(off == arrStart + lenArr)
566 pure val
568-------------------------------------------------------------------------------
569-- Streams of Arrays
570-------------------------------------------------------------------------------
572-- TODO: efficiently compare two streams of arrays. Two streams can have chunks
573-- of different sizes, we can handle that in the stream comparison abstraction.
574-- This could be useful e.g. to fast compare whether two files differ.
576-- | Insert the given element between arrays and flatten.
577--
578-- >>> interpose x = Stream.interpose x Array.reader
579--
580{-# INLINE interpose #-}
581interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
582interpose x = D.interpose x reader
584data FlattenState s =
585 OuterLoop s
586 | InnerLoop s !MutByteArray !Int !Int
588-- | Insert the given element after each array and flatten. This is similar to
589-- unlines.
590--
591-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader
592--
593{-# INLINE_NORMAL interposeSuffix #-}
594interposeSuffix :: forall m a. (MonadIO m, Unbox a)
595 => a -> Stream m (Array a) -> Stream m a
596-- This does not require MonadIO constraint.
597-- interposeSuffix x = D.interposeSuffix x reader
598interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)
600 where
602 {-# INLINE_LATE step' #-}
603 step' gst (OuterLoop st) = do
604 r <- step (adaptState gst) st
605 return $ case r of
606 D.Yield Array{..} s ->
607 D.Skip (InnerLoop s arrContents arrStart arrEnd)
608 D.Skip s -> D.Skip (OuterLoop s)
609 D.Stop -> D.Stop
611 step' _ (InnerLoop st _ p end) | p == end =
612 return $ D.Yield sep $ OuterLoop st
614 step' _ (InnerLoop st contents p end) = do
615 x <- liftIO $ peekAt p contents
616 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
618-- | Insert the given array after each array and flatten.
619--
620-- >>> intercalateSuffix = Stream.intercalateSuffix Array.reader
621--
622{-# INLINE intercalateSuffix #-}
623intercalateSuffix :: (Monad m, Unbox a)
624 => Array a -> Stream m (Array a) -> Stream m a
625intercalateSuffix = D.intercalateSuffix reader
627-- | Split a stream of arrays on a given separator byte, dropping the separator
628-- and coalescing all the arrays between two separators into a single array.
629--
630{-# INLINE compactOnByte #-}
631compactOnByte
632 :: (MonadIO m)
633 => Word8
634 -> Stream m (Array Word8)
635 -> Stream m (Array Word8)
636compactOnByte byte =
637 fmap unsafeFreeze . MA.compactOnByte byte . fmap unsafeThaw
639-- | Like 'compactOnByte' considers the separator in suffix position instead of
640-- infix position.
641{-# INLINE compactOnByteSuffix #-}
642compactOnByteSuffix
643 :: (MonadIO m)
644 => Word8
645 -> Stream m (Array Word8)
646 -> Stream m (Array Word8)
647compactOnByteSuffix byte =
648 fmap unsafeFreeze . MA.compactOnByteSuffix byte . fmap unsafeThaw
650-------------------------------------------------------------------------------
651-- Folding Streams of Arrays
652-------------------------------------------------------------------------------
654-- XXX This should not be used for breaking a stream as the D.cons used in
655-- reconstructing the stream could be very bad for performance. This can only
656-- be useful in folding without breaking.
657{-# INLINE_NORMAL foldBreakChunks #-}
658foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) =>
659 Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
660foldBreakChunks (Fold fstep initial _ final) stream@(Stream step state) = do
661 res <- initial
662 case res of
663 Fold.Partial fs -> go SPEC state fs
664 Fold.Done fb -> return $! (fb, stream)
666 where
668 {-# INLINE go #-}
669 go !_ st !fs = do
670 r <- step defState st
671 case r of
672 Stream.Yield (Array contents start end) s ->
673 let fp = Tuple' end contents
674 in goArray SPEC s fp start fs
675 Stream.Skip s -> go SPEC s fs
676 Stream.Stop -> do
677 b <- final fs
678 return (b, D.nil)
680 goArray !_ s (Tuple' end _) !cur !fs
681 | cur == end = do
682 go SPEC s fs
683 goArray !_ st fp@(Tuple' end contents) !cur !fs = do
684 x <- liftIO $ peekAt cur contents
685 res <- fstep fs x
686 let next = INDEX_NEXT(cur,a)
687 case res of
688 Fold.Done b -> do
689 let arr = Array contents next end
690 return $! (b, D.cons arr (D.Stream step st))
691 Fold.Partial fs1 -> goArray SPEC st fp next fs1
693-- This may be more robust wrt fusion compared to unfoldMany?
695-- | Fold a stream of arrays using a 'Fold'. This is equivalent to the
696-- following:
697--
698-- >>> foldChunks f = Stream.fold f . Stream.unfoldMany Array.reader
699--
700foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
701foldChunks f s = fmap fst (foldBreakChunks f s)
702-- foldStream f = Stream.fold f . Stream.unfoldMany reader
704-- | Fold a stream of arrays using a 'Fold' and return the remaining stream.
705--
706-- The following alternative to this function allows composing the fold using
707-- the parser Monad:
708--
709-- @
710-- foldBreakStreamK f s =
711-- fmap (first (fromRight undefined))
712-- $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
713-- @
714--
715-- We can compare perf and remove this one or define it in terms of that.
716--
717foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) =>
718 Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
719{-
720foldBreakChunksK f s =
721 fmap (first (fromRight undefined))
722 $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
723-}
724foldBreakChunksK (Fold fstep initial _ final) stream = do
725 res <- initial
726 case res of
727 Fold.Partial fs -> go fs stream
728 Fold.Done fb -> return (fb, stream)
730 where
732 {-# INLINE go #-}
733 go !fs st = do
734 let stop = (, StreamK.nil) <$> final fs
735 single a = yieldk a StreamK.nil
736 yieldk (Array contents start end) r =
737 let fp = Tuple' end contents
738 in goArray fs r fp start
739 in StreamK.foldStream defState yieldk single stop st
741 goArray !fs st (Tuple' end _) !cur
742 | cur == end = do
743 go fs st
744 goArray !fs st fp@(Tuple' end contents) !cur = do
745 x <- liftIO $ peekAt cur contents
746 res <- fstep fs x
747 let next = INDEX_NEXT(cur,a)
748 case res of
749 Fold.Done b -> do
750 let arr = Array contents next end
751 return $! (b, StreamK.cons arr st)
752 Fold.Partial fs1 -> goArray fs1 st fp next
754{-
755-- This can be generalized to any type provided it can be unfolded to a stream
756-- and it can be combined using a semigroup operation.
757--
758{-# INLINE_NORMAL parseBreakD #-}
759parseBreakD ::
760 forall m a b. (MonadIO m, MonadThrow m, Unbox a)
761 => PRD.Parser a m b
762 -> D.Stream m (Array.Array a)
763 -> m (b, D.Stream m (Array.Array a))
764parseBreakD
765 (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
767 res <- initial
768 case res of
769 PRD.IPartial s -> go SPEC state (List []) s
770 PRD.IDone b -> return (b, stream)
771 PRD.IError err -> throwM $ ParseError err
773 where
775 -- "backBuf" contains last few items in the stream that we may have to
776 -- backtrack to.
777 --
778 -- XXX currently we are using a dumb list based approach for backtracking
779 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
780 -- That will allow us more efficient random back and forth movement.
781 go !_ st backBuf !pst = do
782 r <- step defState st
783 case r of
784 D.Yield (Array contents start end) s ->
785 gobuf SPEC s backBuf
786 (Tuple' end contents) start pst
787 D.Skip s -> go SPEC s backBuf pst
788 D.Stop -> do
789 b <- extract pst
790 return (b, D.nil)
792 -- Use strictness on "cur" to keep it unboxed
793 gobuf !_ s backBuf (Tuple' end _) !cur !pst
794 | cur == end = do
795 go SPEC s backBuf pst
796 gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
797 x <- liftIO $ peekByteIndex contents cur
798 pRes <- pstep pst x
799 let next = INDEX_NEXT(cur,a)
800 case pRes of
801 PR.Partial 0 pst1 ->
802 gobuf SPEC s (List []) fp next pst1
803 PR.Partial n pst1 -> do
804 assert (n <= Prelude.length (x:getList backBuf)) (return ())
805 let src0 = Prelude.take n (x:getList backBuf)
806 arr0 = A.fromListN n (Prelude.reverse src0)
807 arr1 = Array contents next end
808 src = arr0 <> arr1
809 let !(Array cont1 start end1) = src
810 fp1 = Tuple' end1 cont1
811 gobuf SPEC s (List []) fp1 start pst1
812 PR.Continue 0 pst1 ->
813 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
814 PR.Continue n pst1 -> do
815 assert (n <= Prelude.length (x:getList backBuf)) (return ())
816 let (src0, buf1) = splitAt n (x:getList backBuf)
817 arr0 = A.fromListN n (Prelude.reverse src0)
818 arr1 = Array contents next end
819 src = arr0 <> arr1
820 let !(Array cont1 start end1) = src
821 fp1 = Tuple' end1 cont1
822 gobuf SPEC s (List buf1) fp1 start pst1
823 PR.Done 0 b -> do
824 let arr = Array contents next end
825 return (b, D.cons arr (D.Stream step s))
826 PR.Done n b -> do
827 assert (n <= Prelude.length (x:getList backBuf)) (return ())
828 let src0 = Prelude.take n (x:getList backBuf)
829 -- XXX create the array in reverse instead
830 arr0 = A.fromListN n (Prelude.reverse src0)
831 arr1 = Array contents next end
832 -- XXX Use StreamK to avoid adding arbitrary layers of
833 -- constructors every time.
834 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
835 return (b, str)
836 PR.Error err -> throwM $ ParseError err
837-}
839-- | Parse an array stream using the supplied 'Parser'. Returns the parse
840-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
841--
842-- The following alternative to this function allows composing the parser using
843-- the parser Monad:
844--
845-- >>> parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
846--
847-- We can compare perf and remove this one or define it in terms of that.
848--
849-- /Internal/
850--
851{-# INLINE_NORMAL parseBreakChunksK #-}
852parseBreakChunksK ::
853 forall m a b. (MonadIO m, Unbox a)
854 => Parser a m b
855 -> StreamK m (Array a)
856 -> m (Either ParseError b, StreamK m (Array a))
857-- parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
858parseBreakChunksK (Parser pstep initial extract) stream = do
859 res <- initial
860 case res of
861 IPartial s -> go s stream []
862 IDone b -> return (Right b, stream)
863 IError err -> return (Left (ParseError err), stream)
865 where
867 -- "backBuf" contains last few items in the stream that we may have to
868 -- backtrack to.
869 --
870 -- XXX currently we are using a dumb list based approach for backtracking
871 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
872 -- That will allow us more efficient random back and forth movement.
873 go !pst st backBuf = do
874 let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
875 single a = yieldk a StreamK.nil
876 yieldk arr r = goArray pst backBuf r arr
877 in StreamK.foldStream defState yieldk single stop st
879 -- Use strictness on "cur" to keep it unboxed
880 goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
881 goArray !pst backBuf st (Array contents cur end) = do
882 x <- liftIO $ peekAt cur contents
883 pRes <- pstep pst x
884 let next = INDEX_NEXT(cur,a)
885 case pRes of
886 Parser.Partial 0 s ->
887 goArray s [] st (Array contents next end)
888 Parser.Partial n s -> do
889 assert (n <= Prelude.length (x:backBuf)) (return ())
890 let src0 = Prelude.take n (x:backBuf)
891 arr0 = fromListN n (Prelude.reverse src0)
892 arr1 = Array contents next end
893 src = arr0 <> arr1
894 goArray s [] st src
895 Parser.Continue 0 s ->
896 goArray s (x:backBuf) st (Array contents next end)
897 Parser.Continue n s -> do
898 assert (n <= Prelude.length (x:backBuf)) (return ())
899 let (src0, buf1) = Prelude.splitAt n (x:backBuf)
900 arr0 = fromListN n (Prelude.reverse src0)
901 arr1 = Array contents next end
902 src = arr0 <> arr1
903 goArray s buf1 st src
904 Parser.Done 0 b -> do
905 let arr = Array contents next end
906 return (Right b, StreamK.cons arr st)
907 Parser.Done n b -> do
908 assert (n <= Prelude.length (x:backBuf)) (return ())
909 let src0 = Prelude.take n (x:backBuf)
910 -- XXX Use fromListRevN once implemented
911 -- arr0 = A.fromListRevN n src0
912 arr0 = fromListN n (Prelude.reverse src0)
913 arr1 = Array contents next end
914 str = StreamK.cons arr0 (StreamK.cons arr1 st)
915 return (Right b, str)
916 Parser.Error err -> do
917 let n = Prelude.length backBuf
918 arr0 = fromListN n (Prelude.reverse backBuf)
919 arr1 = Array contents cur end
920 str = StreamK.cons arr0 (StreamK.cons arr1 stream)
921 return (Left (ParseError err), str)
923 -- This is a simplified goArray
924 goExtract !pst backBuf (Array _ cur end)
925 | cur == end = goStop pst backBuf
926 goExtract !pst backBuf (Array contents cur end) = do
927 x <- liftIO $ peekAt cur contents
928 pRes <- pstep pst x
929 let next = INDEX_NEXT(cur,a)
930 case pRes of
931 Parser.Partial 0 s ->
932 goExtract s [] (Array contents next end)
933 Parser.Partial n s -> do
934 assert (n <= Prelude.length (x:backBuf)) (return ())
935 let src0 = Prelude.take n (x:backBuf)
936 arr0 = fromListN n (Prelude.reverse src0)
937 arr1 = Array contents next end
938 src = arr0 <> arr1
939 goExtract s [] src
940 Parser.Continue 0 s ->
941 goExtract s backBuf (Array contents next end)
942 Parser.Continue n s -> do
943 assert (n <= Prelude.length (x:backBuf)) (return ())
944 let (src0, buf1) = Prelude.splitAt n (x:backBuf)
945 arr0 = fromListN n (Prelude.reverse src0)
946 arr1 = Array contents next end
947 src = arr0 <> arr1
948 goExtract s buf1 src
949 Parser.Done 0 b -> do
950 let arr = Array contents next end
951 return (Right b, StreamK.fromPure arr)
952 Parser.Done n b -> do
953 assert (n <= Prelude.length backBuf) (return ())
954 let src0 = Prelude.take n backBuf
955 -- XXX Use fromListRevN once implemented
956 -- arr0 = A.fromListRevN n src0
957 arr0 = fromListN n (Prelude.reverse src0)
958 arr1 = Array contents next end
959 str = StreamK.cons arr0 (StreamK.fromPure arr1)
960 return (Right b, str)
961 Parser.Error err -> do
962 let n = Prelude.length backBuf
963 arr0 = fromListN n (Prelude.reverse backBuf)
964 arr1 = Array contents cur end
965 str = StreamK.cons arr0 (StreamK.cons arr1 stream)
966 return (Left (ParseError err), str)
968 -- This is a simplified goExtract
969 {-# INLINE goStop #-}
970 goStop !pst backBuf = do
971 pRes <- extract pst
972 case pRes of
973 Parser.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
974 Parser.Continue 0 s ->
975 goStop s backBuf
976 Parser.Continue n s -> do
977 assert (n <= Prelude.length backBuf) (return ())
978 let (src0, buf1) = Prelude.splitAt n backBuf
979 arr = fromListN n (Prelude.reverse src0)
980 goExtract s buf1 arr
981 Parser.Done 0 b ->
982 return (Right b, StreamK.nil)
983 Parser.Done n b -> do
984 assert (n <= Prelude.length backBuf) (return ())
985 let src0 = Prelude.take n backBuf
986 -- XXX Use fromListRevN once implemented
987 -- arr0 = A.fromListRevN n src0
988 arr0 = fromListN n (Prelude.reverse src0)
989 return (Right b, StreamK.fromPure arr0)
990 Parser.Error err -> do
991 let n = Prelude.length backBuf
992 arr0 = fromListN n (Prelude.reverse backBuf)
993 return (Left (ParseError err), StreamK.fromPure arr0)