on 5d096df help sign in
947 lines 29 kB history raw
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE UnboxedTuples #-}
3-- |
4-- Module : Streamly.Internal.Data.MutArray.Generic
5-- Copyright : (c) 2020 Composewell Technologies
6-- License : BSD3-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
9-- Portability : GHC
10--
11module Streamly.Internal.Data.MutArray.Generic
13 -- * Type
14 -- $arrayNotes
15 MutArray (..)
17 -- * Constructing and Writing
18 -- ** Construction
19 , nil
21 -- *** Uninitialized Arrays
22 , emptyOf
23 -- , newArrayWith
25 -- *** From streams
26 , unsafeCreateOf
27 , createOf
28 , createWith -- createOfMin/createMin/createGE?
29 , create
30 , fromStreamN
31 , fromStream
32 , fromPureStream
34 -- , writeRevN
35 -- , writeRev
37 -- ** From containers
38 , fromListN
39 , fromList
41 -- * Random writes
42 , putIndex
43 , putIndexUnsafe
44 , putIndices
45 -- , putFromThenTo
46 -- , putFrom -- start writing at the given position
47 -- , putUpto -- write from beginning up to the given position
48 -- , putFromTo
49 -- , putFromRev
50 -- , putUptoRev
51 , modifyIndexUnsafe
52 , modifyIndex
53 -- , modifyIndices
54 -- , modify
55 -- , swapIndices
57 -- * Growing and Shrinking
58 -- Arrays grow only at the end, though it is possible to grow on both sides
59 -- and therefore have a cons as well as snoc. But that will require two
60 -- bounds in the array representation.
62 -- ** Reallocation
63 , realloc
64 , uninit
66 -- ** Appending elements
67 , snocWith
68 , snoc
69 -- , snocLinear
70 -- , snocMay
71 , snocUnsafe
73 -- ** Appending streams
74 -- , writeAppendNUnsafe
75 -- , writeAppendN
76 -- , writeAppendWith
77 -- , writeAppend
79 -- ** Truncation
80 -- These are not the same as slicing the array at the beginning, they may
81 -- reduce the length as well as the capacity of the array.
82 -- , truncateWith
83 -- , truncate
84 -- , truncateExp
86 -- * Eliminating and Reading
88 -- ** Unfolds
89 , reader
90 -- , readerRev
91 , producerWith -- experimental
92 , producer -- experimental
94 -- ** To containers
95 , read
96 , readRev
97 , toStreamK
98 -- , toStreamKRev
99 , toList
101 -- ** Random reads
102 , getIndex
103 , getIndexUnsafe
104 , getIndexUnsafeWith
105 -- , getIndices
106 -- , getFromThenTo
107 -- , getIndexRev
109 -- * Size
110 , length
112 -- * In-place Mutation Algorithms
113 , strip
114 -- , reverse
115 -- , permute
116 -- , partitionBy
117 -- , shuffleBy
118 -- , divideBy
119 -- , mergeBy
121 -- * Folding
122 -- , foldl'
123 -- , foldr
124 , cmp
125 , eq
127 -- * Arrays of arrays
128 -- We can add dimensionality parameter to the array type to get
129 -- multidimensional arrays. Multidimensional arrays would just be a
130 -- convenience wrapper on top of single dimensional arrays.
132 -- | Operations dealing with multiple arrays, streams of arrays or
133 -- multidimensional array representations.
135 -- ** Construct from streams
136 , chunksOf
137 -- , arrayStreamKFromStreamD
138 -- , writeChunks
140 -- ** Eliminate to streams
141 -- , flattenArrays
142 -- , flattenArraysRev
143 -- , fromArrayStreamK
145 -- ** Construct from arrays
146 -- get chunks without copying
147 , getSliceUnsafe
148 , getSlice
149 -- , getSlicesFromLenN
150 -- , splitAt -- XXX should be able to express using getSlice
151 -- , breakOn
153 -- ** Appending arrays
154 -- , spliceCopy
155 -- , spliceWith
156 -- , splice
157 -- , spliceExp
158 , putSliceUnsafe
159 -- , appendSlice
160 -- , appendSliceFrom
162 , clone
164 -- * Deprecated
165 , new
166 , writeNUnsafe
167 , writeN
168 , writeWith
169 , write
170 )
171where
173#include "inline.hs"
174#include "assert.hs"
176import Control.Monad (when)
177import Control.Monad.IO.Class (MonadIO(..))
178import Data.Functor.Identity (Identity(..))
179import GHC.Base
180 ( MutableArray#
181 , RealWorld
182 , copyMutableArray#
183 , newArray#
184 , readArray#
185 , writeArray#
186 )
187import GHC.IO (IO(..))
188import GHC.Int (Int(..))
189import Streamly.Internal.Data.Fold.Type (Fold(..))
190import Streamly.Internal.Data.Producer.Type (Producer (..))
191import Streamly.Internal.Data.Unfold.Type (Unfold(..))
192import Streamly.Internal.Data.Stream.Type (Stream)
193import Streamly.Internal.Data.SVar.Type (adaptState)
195import qualified Streamly.Internal.Data.Fold.Type as FL
196import qualified Streamly.Internal.Data.Producer as Producer
197import qualified Streamly.Internal.Data.Stream.Type as D
198import qualified Streamly.Internal.Data.Stream.Generate as D
199import qualified Streamly.Internal.Data.Stream.Lift as D
200import qualified Streamly.Internal.Data.StreamK.Type as K
202import Prelude hiding (read, length)
204#include "DocTestDataMutArrayGeneric.hs"
206-------------------------------------------------------------------------------
207-- MutArray Data Type
208-------------------------------------------------------------------------------
210data MutArray a =
211 MutArray
212 { arrContents# :: MutableArray# RealWorld a
213 -- ^ The internal contents of the array representing the entire array.
215 , arrStart :: {-# UNPACK #-}!Int
216 -- ^ The starting index of this slice.
218 , arrLen :: {-# UNPACK #-}!Int
219 -- ^ The length of this slice.
221 , arrTrueLen :: {-# UNPACK #-}!Int
222 -- ^ This is the true length of the array. Coincidentally, this also
223 -- represents the first index beyond the maximum acceptable index of
224 -- the array. This is specific to the array contents itself and not
225 -- dependent on the slice. This value should not change and is shared
226 -- across all the slices.
227 }
229{-# INLINE bottomElement #-}
230bottomElement :: a
231bottomElement =
232 error
233 $ unwords
234 [ funcName
235 , "This is the bottom element of the array."
236 , "This is a place holder and should never be reached!"
237 ]
239 where
241 funcName = "Streamly.Internal.Data.MutArray.Generic.bottomElement:"
243-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
244-- that we do not have to write undefined or error in the whole array.
246-- | @emptyOf count@ allocates a zero length array that can be extended to hold
247-- up to 'count' items without reallocating.
248--
249-- /Pre-release/
250{-# INLINE emptyOf #-}
251emptyOf :: MonadIO m => Int -> m (MutArray a)
252emptyOf n@(I# n#) =
253 liftIO
254 $ IO
255 $ \s# ->
256 case newArray# n# bottomElement s# of
257 (# s1#, arr# #) ->
258 let ma = MutArray arr# 0 0 n
259 in (# s1#, ma #)
261-- XXX Deprecate in major
262-- {-# DEPRECATED new "Please use emptyOf instead." #-}
263{-# INLINE new #-}
264new :: MonadIO m => Int -> m (MutArray a)
265new = emptyOf
267-- XXX This could be pure?
269-- |
270-- Definition:
271--
272-- >>> nil = MutArray.new 0
273{-# INLINE nil #-}
274nil :: MonadIO m => m (MutArray a)
275nil = new 0
277-------------------------------------------------------------------------------
278-- Random writes
279-------------------------------------------------------------------------------
281-- | Write the given element to the given index of the 'MutableArray#'. Does not
282-- check if the index is out of bounds of the array.
283--
284-- /Pre-release/
285{-# INLINE putIndexUnsafeWith #-}
286putIndexUnsafeWith :: MonadIO m => Int -> MutableArray# RealWorld a -> a -> m ()
287putIndexUnsafeWith n _arrContents# x =
288 liftIO
289 $ IO
290 $ \s# ->
291 case n of
292 I# n# ->
293 let s1# = writeArray# _arrContents# n# x s#
294 in (# s1#, () #)
296-- | Write the given element to the given index of the array. Does not check if
297-- the index is out of bounds of the array.
298--
299-- /Pre-release/
300{-# INLINE putIndexUnsafe #-}
301putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()
302putIndexUnsafe i MutArray {..} x =
303 assert (i >= 0 && i < arrLen)
304 putIndexUnsafeWith (i + arrStart) arrContents# x
306invalidIndex :: String -> Int -> a
307invalidIndex label i =
308 error $ label ++ ": invalid array index " ++ show i
310-- | /O(1)/ Write the given element at the given index in the array.
311-- Performs in-place mutation of the array.
312--
313-- >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))
314--
315-- /Pre-release/
316{-# INLINE putIndex #-}
317putIndex :: MonadIO m => Int -> MutArray a -> a -> m ()
318putIndex i arr@MutArray {..} x =
319 if i >= 0 && i < arrLen
320 then putIndexUnsafe i arr x
321 else invalidIndex "putIndex" i
323-- | Write an input stream of (index, value) pairs to an array. Throws an
324-- error if any index is out of bounds.
325--
326-- /Pre-release/
327{-# INLINE putIndices #-}
328putIndices :: MonadIO m
329 => MutArray a -> Fold m (Int, a) ()
330putIndices arr = FL.foldlM' step (return ())
332 where
334 step () (i, x) = putIndex i arr x
336-- | Modify a given index of an array using a modifier function without checking
337-- the bounds.
338--
339-- Unsafe because it does not check the bounds of the array.
340--
341-- /Pre-release/
342modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
343modifyIndexUnsafe i MutArray {..} f = do
344 liftIO
345 $ IO
346 $ \s# ->
347 case i + arrStart of
348 I# n# ->
349 case readArray# arrContents# n# s# of
350 (# s1#, a #) ->
351 let (a1, b) = f a
352 s2# = writeArray# arrContents# n# a1 s1#
353 in (# s2#, b #)
355-- | Modify a given index of an array using a modifier function.
356--
357-- /Pre-release/
358modifyIndex :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
359modifyIndex i arr@MutArray {..} f = do
360 if i >= 0 && i < arrLen
361 then modifyIndexUnsafe i arr f
362 else invalidIndex "modifyIndex" i
364-------------------------------------------------------------------------------
365-- Resizing
366-------------------------------------------------------------------------------
368-- | Reallocates the array according to the new size. This is a safe function
369-- that always creates a new array and copies the old array into the new one.
370-- If the reallocated size is less than the original array it results in a
371-- truncated version of the original array.
372--
373realloc :: MonadIO m => Int -> MutArray a -> m (MutArray a)
374realloc n arr = do
375 arr1 <- new n
376 let !newLen@(I# newLen#) = min n (arrLen arr)
377 !(I# arrS#) = arrStart arr
378 !(I# arr1S#) = arrStart arr1
379 arrC# = arrContents# arr
380 arr1C# = arrContents# arr1
381 liftIO
382 $ IO
383 $ \s# ->
384 let s1# = copyMutableArray# arrC# arrS# arr1C# arr1S# newLen# s#
385 in (# s1#, arr1 {arrLen = newLen, arrTrueLen = n} #)
387reallocWith ::
388 MonadIO m => String -> (Int -> Int) -> Int -> MutArray a -> m (MutArray a)
389reallocWith label sizer reqSize arr = do
390 let oldSize = arrLen arr
391 newSize = sizer oldSize
392 safeSize = max newSize (oldSize + reqSize)
393 assert (newSize >= oldSize + reqSize || error badSize) (return ())
394 realloc safeSize arr
396 where
398 badSize = concat
399 [ label
400 , ": new array size is less than required size "
401 , show reqSize
402 , ". Please check the sizing function passed."
403 ]
405-------------------------------------------------------------------------------
406-- Snoc
407-------------------------------------------------------------------------------
409-- XXX Not sure of the behavior of writeArray# if we specify an index which is
410-- out of bounds. This comment should be rewritten based on that.
411-- | Really really unsafe, appends the element into the first array, may
412-- cause silent data corruption or if you are lucky a segfault if the index
413-- is out of bounds.
414--
415-- /Internal/
416{-# INLINE snocUnsafe #-}
417snocUnsafe :: MonadIO m => MutArray a -> a -> m (MutArray a)
418snocUnsafe arr@MutArray {..} a = do
419 assert (arrStart + arrLen < arrTrueLen) (return ())
420 let arr1 = arr {arrLen = arrLen + 1}
421 putIndexUnsafe arrLen arr1 a
422 return arr1
424-- NOINLINE to move it out of the way and not pollute the instruction cache.
425{-# NOINLINE snocWithRealloc #-}
426snocWithRealloc :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)
427snocWithRealloc sizer arr x = do
428 arr1 <- reallocWith "snocWithRealloc" sizer 1 arr
429 snocUnsafe arr1 x
431-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
432-- the array increases by 1.
433--
434-- If there is no reserved space available in @arr@ it is reallocated to a size
435-- in bytes determined by the @sizer oldSize@ function, where @oldSize@ is the
436-- original size of the array.
437--
438-- Note that the returned array may be a mutated version of the original array.
439--
440-- /Pre-release/
441{-# INLINE snocWith #-}
442snocWith :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)
443snocWith sizer arr@MutArray {..} x = do
444 if arrStart + arrLen < arrTrueLen
445 then snocUnsafe arr x
446 else snocWithRealloc sizer arr x
448-- XXX round it to next power of 2.
450-- | The array is mutated to append an additional element to it. If there is no
451-- reserved space available in the array then it is reallocated to double the
452-- original size.
453--
454-- This is useful to reduce allocations when appending unknown number of
455-- elements.
456--
457-- Note that the returned array may be a mutated version of the original array.
458--
459-- >>> snoc = MutArray.snocWith (* 2)
460--
461-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.
462--
463-- /Pre-release/
464{-# INLINE snoc #-}
465snoc :: MonadIO m => MutArray a -> a -> m (MutArray a)
466snoc = snocWith (* 2)
468-- | Make the uninitialized memory in the array available for use extending it
469-- by the supplied length beyond the current length of the array. The array may
470-- be reallocated.
471--
472{-# INLINE uninit #-}
473uninit :: MonadIO m => MutArray a -> Int -> m (MutArray a)
474uninit arr@MutArray{..} len =
475 if arrStart + arrLen + len <= arrTrueLen
476 then return $ arr {arrLen = arrLen + len}
477 else realloc (arrLen + len) arr
479-------------------------------------------------------------------------------
480-- Random reads
481-------------------------------------------------------------------------------
483-- | Return the element at the specified index without checking the bounds from
484-- a @MutableArray# RealWorld@.
485--
486-- Unsafe because it does not check the bounds of the array.
487{-# INLINE getIndexUnsafeWith #-}
488getIndexUnsafeWith :: MonadIO m => MutableArray# RealWorld a -> Int -> m a
489getIndexUnsafeWith _arrContents# n =
490 liftIO
491 $ IO
492 $ \s# ->
493 let !(I# i#) = n
494 in readArray# _arrContents# i# s#
496-- | Return the element at the specified index without checking the bounds.
497--
498-- Unsafe because it does not check the bounds of the array.
499{-# INLINE_NORMAL getIndexUnsafe #-}
500getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a
501getIndexUnsafe n MutArray {..} = getIndexUnsafeWith arrContents# (n + arrStart)
503-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
504--
505{-# INLINE getIndex #-}
506getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)
507getIndex i arr@MutArray {..} =
508 if i >= 0 && i < arrLen
509 then Just <$> getIndexUnsafe i arr
510 else return Nothing
512-------------------------------------------------------------------------------
513-- Subarrays
514-------------------------------------------------------------------------------
516-- XXX We can also get immutable slices.
518-- | /O(1)/ Slice an array in constant time.
519--
520-- Unsafe: The bounds of the slice are not checked.
521--
522-- /Unsafe/
523--
524-- /Pre-release/
525{-# INLINE getSliceUnsafe #-}
526getSliceUnsafe
527 :: Int -- ^ from index
528 -> Int -- ^ length of the slice
529 -> MutArray a
530 -> MutArray a
531getSliceUnsafe index len arr@MutArray {..} =
532 assert (index >= 0 && len >= 0 && index + len <= arrLen)
533 $ arr {arrStart = arrStart + index, arrLen = len}
535-- | /O(1)/ Slice an array in constant time. Throws an error if the slice
536-- extends out of the array bounds.
537--
538-- /Pre-release/
539{-# INLINE getSlice #-}
540getSlice
541 :: Int -- ^ from index
542 -> Int -- ^ length of the slice
543 -> MutArray a
544 -> MutArray a
545getSlice index len arr@MutArray{..} =
546 if index >= 0 && len >= 0 && index + len <= arrLen
547 then arr {arrStart = arrStart + index, arrLen = len}
548 else error
549 $ "getSlice: invalid slice, index "
550 ++ show index ++ " length " ++ show len
552-------------------------------------------------------------------------------
553-- to Lists and streams
554-------------------------------------------------------------------------------
556-- XXX Maybe faster to create a list explicitly instead of mapM, if list fusion
557-- does not work well.
559-- | Convert an 'Array' into a list.
560--
561-- /Pre-release/
562{-# INLINE toList #-}
563toList :: MonadIO m => MutArray a -> m [a]
564toList arr@MutArray{..} = mapM (`getIndexUnsafe` arr) [0 .. (arrLen - 1)]
566-- | Generates a stream from the elements of a @MutArray@.
567--
568-- >>> read = Stream.unfold MutArray.reader
569--
570{-# INLINE_NORMAL read #-}
571read :: MonadIO m => MutArray a -> D.Stream m a
572read arr@MutArray{..} =
573 D.mapM (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)
575-- Check equivalence with StreamK.fromStream . toStreamD and remove
576{-# INLINE toStreamK #-}
577toStreamK :: MonadIO m => MutArray a -> K.StreamK m a
578toStreamK arr@MutArray{..} = K.unfoldrM step 0
580 where
582 step i
583 | i == arrLen = return Nothing
584 | otherwise = do
585 x <- getIndexUnsafe i arr
586 return $ Just (x, i + 1)
588{-# INLINE_NORMAL readRev #-}
589readRev :: MonadIO m => MutArray a -> D.Stream m a
590readRev arr@MutArray{..} =
591 D.mapM (`getIndexUnsafe` arr)
592 $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0
594-------------------------------------------------------------------------------
595-- Folds
596-------------------------------------------------------------------------------
598-- XXX deduplicate this across unboxed array and this module?
600-- | The default chunk size by which the array creation routines increase the
601-- size of the array when the array is grown linearly.
602arrayChunkSize :: Int
603arrayChunkSize = 1024
605-- | Like 'createOf' but does not check the array bounds when writing. The fold
606-- driver must not call the step function more than 'n' times otherwise it will
607-- corrupt the memory and crash. This function exists mainly because any
608-- conditional in the step function blocks fusion causing 10x performance
609-- slowdown.
610--
611-- /Pre-release/
612{-# INLINE_NORMAL unsafeCreateOf #-}
613unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
614unsafeCreateOf n = Fold step initial return return
616 where
618 initial = FL.Partial <$> new (max n 0)
620 step arr x = FL.Partial <$> snocUnsafe arr x
622{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
623{-# INLINE writeNUnsafe #-}
624writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
625writeNUnsafe = unsafeCreateOf
627-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
628-- 'Array'.
629--
630-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
631--
632-- /Pre-release/
633{-# INLINE_NORMAL createOf #-}
634createOf :: MonadIO m => Int -> Fold m a (MutArray a)
635createOf n = FL.take n $ unsafeCreateOf n
637-- XXX Deprecate in major
638-- {-# DEPRECATED writeN "Please use createOf instead." #-}
639{-# INLINE writeN #-}
640writeN :: MonadIO m => Int -> Fold m a (MutArray a)
641writeN = createOf
643-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
644-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
645-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
647-- | @createWith minCount@ folds the whole input to a single array. The array
648-- starts at a size big enough to hold minCount elements, the size is doubled
649-- every time the array needs to be grown.
650--
651-- /Caution! Do not use this on infinite streams./
652--
653-- /Pre-release/
654{-# INLINE_NORMAL createWith #-}
655createWith :: MonadIO m => Int -> Fold m a (MutArray a)
656-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
657createWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
659 where
661 initial = do
662 when (elemCount < 0) $ error "createWith: elemCount is negative"
663 new elemCount
665 step arr@(MutArray _ start end bound) x
666 | end == bound = do
667 let oldSize = end - start
668 newSize = max (oldSize * 2) 1
669 arr1 <- realloc newSize arr
670 snocUnsafe arr1 x
671 step arr x = snocUnsafe arr x
673 -- extract = rightSize
674 extract = return
676{-# DEPRECATED writeWith "Please use createWith instead." #-}
677{-# INLINE writeWith #-}
678writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
679writeWith = createWith
681-- | Fold the whole input to a single array.
682--
683-- Same as 'createWith' using an initial array size of 'arrayChunkSize' bytes
684-- rounded up to the element size.
685--
686-- /Caution! Do not use this on infinite streams./
687--
688{-# INLINE create #-}
689create :: MonadIO m => Fold m a (MutArray a)
690create = writeWith arrayChunkSize
692-- XXX Deprecate in major
693-- {-# DEPRECATED write "Please use create instead." #-}
694{-# INLINE write #-}
695write :: MonadIO m => Fold m a (MutArray a)
696write = create
698-- | Create a 'MutArray' from the first @n@ elements of a stream. The
699-- array is allocated to size @n@, if the stream terminates before @n@
700-- elements then the array may hold less than @n@ elements.
701--
702{-# INLINE fromStreamN #-}
703fromStreamN :: MonadIO m => Int -> Stream m a -> m (MutArray a)
704fromStreamN n = D.fold (writeN n)
706{-# INLINE fromStream #-}
707fromStream :: MonadIO m => Stream m a -> m (MutArray a)
708fromStream = D.fold write
710{-# INLINABLE fromListN #-}
711fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)
712fromListN n xs = fromStreamN n $ D.fromList xs
714{-# INLINABLE fromList #-}
715fromList :: MonadIO m => [a] -> m (MutArray a)
716fromList xs = fromStream $ D.fromList xs
718{-# INLINABLE fromPureStream #-}
719fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
720fromPureStream xs =
721 D.fold write $ D.morphInner (return . runIdentity) xs
723-------------------------------------------------------------------------------
724-- Chunking
725-------------------------------------------------------------------------------
727data GroupState s a start end bound
728 = GroupStart s
729 | GroupBuffer s (MutableArray# RealWorld a) start end bound
730 | GroupYield
731 (MutableArray# RealWorld a)
732 start
733 end
734 bound
735 (GroupState s a start end bound)
736 | GroupFinish
738-- | @chunksOf n stream@ groups the input stream into a stream of
739-- arrays of size n.
740--
741-- @chunksOf n = foldMany (MutArray.writeN n)@
742--
743-- /Pre-release/
744{-# INLINE_NORMAL chunksOf #-}
745chunksOf :: forall m a. MonadIO m
746 => Int -> D.Stream m a -> D.Stream m (MutArray a)
747-- XXX the idiomatic implementation leads to large regression in the D.reverse'
748-- benchmark. It seems it has difficulty producing optimized code when
749-- converting to StreamK. Investigate GHC optimizations.
750-- chunksOf n = D.foldMany (writeN n)
751chunksOf n (D.Stream step state) =
752 D.Stream step' (GroupStart state)
754 where
756 -- start is always 0
757 -- end and len are always equal
759 {-# INLINE_LATE step' #-}
760 step' _ (GroupStart st) = do
761 when (n <= 0) $
762 -- XXX we can pass the module string from the higher level API
763 error $ "Streamly.Internal.Data.Array.Generic.Mut.Type.chunksOf: "
764 ++ "the size of arrays [" ++ show n
765 ++ "] must be a natural number"
766 (MutArray contents start end bound :: MutArray a) <- new n
767 return $ D.Skip (GroupBuffer st contents start end bound)
769 step' gst (GroupBuffer st contents start end bound) = do
770 r <- step (adaptState gst) st
771 case r of
772 D.Yield x s -> do
773 putIndexUnsafeWith end contents x
774 let end1 = end + 1
775 return $
776 if end1 >= bound
777 then D.Skip
778 (GroupYield
779 contents start end1 bound (GroupStart s))
780 else D.Skip (GroupBuffer s contents start end1 bound)
781 D.Skip s ->
782 return $ D.Skip (GroupBuffer s contents start end bound)
783 D.Stop ->
784 return
785 $ D.Skip (GroupYield contents start end bound GroupFinish)
787 step' _ (GroupYield contents start end bound next) =
788 return $ D.Yield (MutArray contents start end bound) next
790 step' _ GroupFinish = return D.Stop
792-------------------------------------------------------------------------------
793-- Unfolds
794-------------------------------------------------------------------------------
796-- | Resumable unfold of an array.
797--
798{-# INLINE_NORMAL producerWith #-}
799producerWith :: Monad m => (forall b. IO b -> m b) -> Producer m (MutArray a) a
800producerWith liftio = Producer step inject extract
802 where
804 {-# INLINE inject #-}
805 inject arr = return (arr, 0)
807 {-# INLINE extract #-}
808 extract (arr, i) =
809 return $ arr {arrStart = arrStart arr + i, arrLen = arrLen arr - i}
811 {-# INLINE_LATE step #-}
812 step (arr, i)
813 | assert (arrLen arr >= 0) (i == arrLen arr) = return D.Stop
814 step (arr, i) = do
815 x <- liftio $ getIndexUnsafe i arr
816 return $ D.Yield x (arr, i + 1)
818-- | Resumable unfold of an array.
819--
820{-# INLINE_NORMAL producer #-}
821producer :: MonadIO m => Producer m (MutArray a) a
822producer = producerWith liftIO
824-- | Unfold an array into a stream.
825--
826{-# INLINE_NORMAL reader #-}
827reader :: MonadIO m => Unfold m (MutArray a) a
828reader = Producer.simplify producer
830--------------------------------------------------------------------------------
831-- Appending arrays
832--------------------------------------------------------------------------------
834-- | Put a sub range of a source array into a subrange of a destination array.
835-- This is not safe as it does not check the bounds.
836{-# INLINE putSliceUnsafe #-}
837putSliceUnsafe :: MonadIO m =>
838 MutArray a -> Int -> MutArray a -> Int -> Int -> m ()
839putSliceUnsafe src srcStart dst dstStart len = liftIO $ do
840 assertM(len <= arrLen dst)
841 assertM(len <= arrLen src)
842 let !(I# srcStart#) = srcStart + arrStart src
843 !(I# dstStart#) = dstStart + arrStart dst
844 !(I# len#) = len
845 let arrS# = arrContents# src
846 arrD# = arrContents# dst
847 IO $ \s# -> (# copyMutableArray#
848 arrS# srcStart# arrD# dstStart# len# s#
849 , () #)
851{-# INLINE clone #-}
852clone :: MonadIO m => MutArray a -> m (MutArray a)
853clone src = do
854 let len = arrLen src
855 dst <- new len
856 putSliceUnsafe src 0 dst 0 len
857 return dst
859-------------------------------------------------------------------------------
860-- Size
861-------------------------------------------------------------------------------
863{-# INLINE length #-}
864length :: MutArray a -> Int
865length = arrLen
867-------------------------------------------------------------------------------
868-- Equality
869-------------------------------------------------------------------------------
871-- | Compare the length of the arrays. If the length is equal, compare the
872-- lexicographical ordering of two underlying byte arrays otherwise return the
873-- result of length comparison.
874--
875-- /Pre-release/
876{-# INLINE cmp #-}
877cmp :: (MonadIO m, Ord a) => MutArray a -> MutArray a -> m Ordering
878cmp a1 a2 =
879 case compare lenA1 lenA2 of
880 EQ -> loop (lenA1 - 1)
881 x -> return x
883 where
885 lenA1 = length a1
886 lenA2 = length a2
888 loop i
889 | i < 0 = return EQ
890 | otherwise = do
891 v1 <- getIndexUnsafe i a1
892 v2 <- getIndexUnsafe i a2
893 case compare v1 v2 of
894 EQ -> loop (i - 1)
895 x -> return x
897{-# INLINE eq #-}
898eq :: (MonadIO m, Eq a) => MutArray a -> MutArray a -> m Bool
899eq a1 a2 =
900 if lenA1 == lenA2
901 then loop (lenA1 - 1)
902 else return False
904 where
906 lenA1 = length a1
907 lenA2 = length a2
909 loop i
910 | i < 0 = return True
911 | otherwise = do
912 v1 <- getIndexUnsafe i a1
913 v2 <- getIndexUnsafe i a2
914 if v1 == v2
915 then loop (i - 1)
916 else return False
918{-# INLINE strip #-}
919strip :: MonadIO m => (a -> Bool) -> MutArray a -> m (MutArray a)
920strip p arr = liftIO $ do
921 let lastIndex = length arr - 1
922 indexR <- getIndexR lastIndex -- last predicate failing index
923 if indexR < 0
924 then nil
925 else do
926 indexL <- getIndexL 0 -- first predicate failing index
927 if indexL == 0 && indexR == lastIndex
928 then return arr
929 else
930 let newLen = indexR - indexL + 1
931 in return $ getSliceUnsafe indexL newLen arr
933 where
935 getIndexR idx
936 | idx < 0 = return idx
937 | otherwise = do
938 r <- getIndexUnsafe idx arr
939 if p r
940 then getIndexR (idx - 1)
941 else return idx
943 getIndexL idx = do
944 r <- getIndexUnsafe idx arr
945 if p r
946 then getIndexL (idx + 1)
947 else return idx