on 4e34693 help sign in
824 lines 25 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 , new
23 -- , newArrayWith
25 -- *** From streams
26 , writeNUnsafe
27 , writeN
28 , writeWith
29 , write
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 -- , getIndices
105 -- , getFromThenTo
106 -- , getIndexRev
108 -- * Size
109 , length
111 -- * In-place Mutation Algorithms
112 , strip
113 -- , reverse
114 -- , permute
115 -- , partitionBy
116 -- , shuffleBy
117 -- , divideBy
118 -- , mergeBy
120 -- * Folding
121 -- , foldl'
122 -- , foldr
123 , cmp
124 , eq
126 -- * Arrays of arrays
127 -- We can add dimensionality parameter to the array type to get
128 -- multidimensional arrays. Multidimensional arrays would just be a
129 -- convenience wrapper on top of single dimensional arrays.
131 -- | Operations dealing with multiple arrays, streams of arrays or
132 -- multidimensional array representations.
134 -- ** Construct from streams
135 -- , chunksOf
136 -- , arrayStreamKFromStreamD
137 -- , writeChunks
139 -- ** Eliminate to streams
140 -- , flattenArrays
141 -- , flattenArraysRev
142 -- , fromArrayStreamK
144 -- ** Construct from arrays
145 -- get chunks without copying
146 , getSliceUnsafe
147 , getSlice
148 -- , getSlicesFromLenN
149 -- , splitAt -- XXX should be able to express using getSlice
150 -- , breakOn
152 -- ** Appending arrays
153 -- , spliceCopy
154 -- , spliceWith
155 -- , splice
156 -- , spliceExp
157 , putSliceUnsafe
158 -- , appendSlice
159 -- , appendSliceFrom
161 , clone
162 )
163where
165#include "inline.hs"
166#include "assert.hs"
168import Control.Monad (when)
169import Control.Monad.IO.Class (MonadIO(..))
170import Data.Functor.Identity (Identity(..))
171import GHC.Base
172 ( MutableArray#
173 , RealWorld
174 , copyMutableArray#
175 , newArray#
176 , readArray#
177 , writeArray#
178 )
179import GHC.IO (IO(..))
180import GHC.Int (Int(..))
181import Streamly.Internal.Data.Fold.Type (Fold(..))
182import Streamly.Internal.Data.Producer.Type (Producer (..))
183import Streamly.Internal.Data.Unfold.Type (Unfold(..))
184import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
186import qualified Streamly.Internal.Data.Fold.Type as FL
187import qualified Streamly.Internal.Data.Producer as Producer
188import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
189import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
190import qualified Streamly.Internal.Data.Stream.StreamD.Lift as D
191import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
193import Prelude hiding (read, length)
195#include "DocTestDataMutArrayGeneric.hs"
197-------------------------------------------------------------------------------
198-- MutArray Data Type
199-------------------------------------------------------------------------------
201data MutArray a =
202 MutArray
203 { arrContents# :: MutableArray# RealWorld a
204 -- ^ The internal contents of the array representing the entire array.
206 , arrStart :: {-# UNPACK #-}!Int
207 -- ^ The starting index of this slice.
209 , arrLen :: {-# UNPACK #-}!Int
210 -- ^ The length of this slice.
212 , arrTrueLen :: {-# UNPACK #-}!Int
213 -- ^ This is the true length of the array. Coincidentally, this also
214 -- represents the first index beyond the maximum acceptable index of
215 -- the array. This is specific to the array contents itself and not
216 -- dependent on the slice. This value should not change and is shared
217 -- across all the slices.
218 }
220{-# INLINE bottomElement #-}
221bottomElement :: a
222bottomElement =
223 error
224 $ unwords
225 [ funcName
226 , "This is the bottom element of the array."
227 , "This is a place holder and should never be reached!"
228 ]
230 where
232 funcName = "Streamly.Internal.Data.MutArray.Generic.bottomElement:"
234-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
235-- that we do not have to write undefined or error in the whole array.
237-- | @new count@ allocates a zero length array that can be extended to hold
238-- up to 'count' items without reallocating.
239--
240-- /Pre-release/
241{-# INLINE new #-}
242new :: MonadIO m => Int -> m (MutArray a)
243new n@(I# n#) =
244 liftIO
245 $ IO
246 $ \s# ->
247 case newArray# n# bottomElement s# of
248 (# s1#, arr# #) ->
249 let ma = MutArray arr# 0 0 n
250 in (# s1#, ma #)
252-- XXX This could be pure?
254-- |
255-- Definition:
256--
257-- >>> nil = MutArray.new 0
258{-# INLINE nil #-}
259nil :: MonadIO m => m (MutArray a)
260nil = new 0
262-------------------------------------------------------------------------------
263-- Random writes
264-------------------------------------------------------------------------------
266-- | Write the given element to the given index of the array. Does not check if
267-- the index is out of bounds of the array.
268--
269-- /Pre-release/
270{-# INLINE putIndexUnsafe #-}
271putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()
272putIndexUnsafe i MutArray {..} x =
273 assert (i >= 0 && i < arrLen)
274 (liftIO
275 $ IO
276 $ \s# ->
277 case i + arrStart of
278 I# n# ->
279 let s1# = writeArray# arrContents# n# x s#
280 in (# s1#, () #))
282invalidIndex :: String -> Int -> a
283invalidIndex label i =
284 error $ label ++ ": invalid array index " ++ show i
286-- | /O(1)/ Write the given element at the given index in the array.
287-- Performs in-place mutation of the array.
288--
289-- >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))
290--
291-- /Pre-release/
292{-# INLINE putIndex #-}
293putIndex :: MonadIO m => Int -> MutArray a -> a -> m ()
294putIndex i arr@MutArray {..} x =
295 if i >= 0 && i < arrLen
296 then putIndexUnsafe i arr x
297 else invalidIndex "putIndex" i
299-- | Write an input stream of (index, value) pairs to an array. Throws an
300-- error if any index is out of bounds.
301--
302-- /Pre-release/
303{-# INLINE putIndices #-}
304putIndices :: MonadIO m
305 => MutArray a -> Fold m (Int, a) ()
306putIndices arr = FL.foldlM' step (return ())
308 where
310 step () (i, x) = liftIO (putIndex i arr x)
312-- | Modify a given index of an array using a modifier function without checking
313-- the bounds.
314--
315-- Unsafe because it does not check the bounds of the array.
316--
317-- /Pre-release/
318modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
319modifyIndexUnsafe i MutArray {..} f = do
320 liftIO
321 $ IO
322 $ \s# ->
323 case i + arrStart of
324 I# n# ->
325 case readArray# arrContents# n# s# of
326 (# s1#, a #) ->
327 let (a1, b) = f a
328 s2# = writeArray# arrContents# n# a1 s1#
329 in (# s2#, b #)
331-- | Modify a given index of an array using a modifier function.
332--
333-- /Pre-release/
334modifyIndex :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
335modifyIndex i arr@MutArray {..} f = do
336 if i >= 0 && i < arrLen
337 then modifyIndexUnsafe i arr f
338 else invalidIndex "modifyIndex" i
340-------------------------------------------------------------------------------
341-- Resizing
342-------------------------------------------------------------------------------
344-- | Reallocates the array according to the new size. This is a safe function
345-- that always creates a new array and copies the old array into the new one.
346-- If the reallocated size is less than the original array it results in a
347-- truncated version of the original array.
348--
349realloc :: MonadIO m => Int -> MutArray a -> m (MutArray a)
350realloc n arr = do
351 arr1 <- new n
352 let !newLen@(I# newLen#) = min n (arrLen arr)
353 !(I# arrS#) = arrStart arr
354 !(I# arr1S#) = arrStart arr1
355 arrC# = arrContents# arr
356 arr1C# = arrContents# arr1
357 liftIO
358 $ IO
359 $ \s# ->
360 let s1# = copyMutableArray# arrC# arrS# arr1C# arr1S# newLen# s#
361 in (# s1#, arr1 {arrLen = newLen, arrTrueLen = n} #)
363reallocWith ::
364 MonadIO m => String -> (Int -> Int) -> Int -> MutArray a -> m (MutArray a)
365reallocWith label sizer reqSize arr = do
366 let oldSize = arrLen arr
367 newSize = sizer oldSize
368 safeSize = max newSize (oldSize + reqSize)
369 assert (newSize >= oldSize + reqSize || error badSize) (return ())
370 realloc safeSize arr
372 where
374 badSize = concat
375 [ label
376 , ": new array size is less than required size "
377 , show reqSize
378 , ". Please check the sizing function passed."
379 ]
381-------------------------------------------------------------------------------
382-- Snoc
383-------------------------------------------------------------------------------
385-- XXX Not sure of the behavior of writeArray# if we specify an index which is
386-- out of bounds. This comment should be rewritten based on that.
387-- | Really really unsafe, appends the element into the first array, may
388-- cause silent data corruption or if you are lucky a segfault if the index
389-- is out of bounds.
390--
391-- /Internal/
392{-# INLINE snocUnsafe #-}
393snocUnsafe :: MonadIO m => MutArray a -> a -> m (MutArray a)
394snocUnsafe arr@MutArray {..} a = do
395 assert (arrStart + arrLen < arrTrueLen) (return ())
396 let arr1 = arr {arrLen = arrLen + 1}
397 putIndexUnsafe arrLen arr1 a
398 return arr1
400-- NOINLINE to move it out of the way and not pollute the instruction cache.
401{-# NOINLINE snocWithRealloc #-}
402snocWithRealloc :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)
403snocWithRealloc sizer arr x = do
404 arr1 <- reallocWith "snocWithRealloc" sizer 1 arr
405 snocUnsafe arr1 x
407-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
408-- the array increases by 1.
409--
410-- If there is no reserved space available in @arr@ it is reallocated to a size
411-- in bytes determined by the @sizer oldSize@ function, where @oldSize@ is the
412-- original size of the array.
413--
414-- Note that the returned array may be a mutated version of the original array.
415--
416-- /Pre-release/
417{-# INLINE snocWith #-}
418snocWith :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)
419snocWith sizer arr@MutArray {..} x = do
420 if arrStart + arrLen < arrTrueLen
421 then snocUnsafe arr x
422 else snocWithRealloc sizer arr x
424-- XXX round it to next power of 2.
426-- | The array is mutated to append an additional element to it. If there is no
427-- reserved space available in the array then it is reallocated to double the
428-- original size.
429--
430-- This is useful to reduce allocations when appending unknown number of
431-- elements.
432--
433-- Note that the returned array may be a mutated version of the original array.
434--
435-- >>> snoc = MutArray.snocWith (* 2)
436--
437-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.
438--
439-- /Pre-release/
440{-# INLINE snoc #-}
441snoc :: MonadIO m => MutArray a -> a -> m (MutArray a)
442snoc = snocWith (* 2)
444-- | Make the uninitialized memory in the array available for use extending it
445-- by the supplied length beyond the current length of the array. The array may
446-- be reallocated.
447--
448{-# INLINE uninit #-}
449uninit :: MonadIO m => MutArray a -> Int -> m (MutArray a)
450uninit arr@MutArray{..} len =
451 if arrStart + arrLen + len <= arrTrueLen
452 then return $ arr {arrLen = arrLen + len}
453 else realloc (arrLen + len) arr
455-------------------------------------------------------------------------------
456-- Random reads
457-------------------------------------------------------------------------------
459-- | Return the element at the specified index without checking the bounds.
460--
461-- Unsafe because it does not check the bounds of the array.
462{-# INLINE_NORMAL getIndexUnsafe #-}
463getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a
464getIndexUnsafe n MutArray {..} =
465 liftIO
466 $ IO
467 $ \s# ->
468 let !(I# i#) = arrStart + n
469 in readArray# arrContents# i# s#
471-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
472--
473{-# INLINE getIndex #-}
474getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)
475getIndex i arr@MutArray {..} =
476 if i >= 0 && i < arrLen
477 then Just <$> getIndexUnsafe i arr
478 else return Nothing
480-------------------------------------------------------------------------------
481-- Subarrays
482-------------------------------------------------------------------------------
484-- XXX We can also get immutable slices.
486-- | /O(1)/ Slice an array in constant time.
487--
488-- Unsafe: The bounds of the slice are not checked.
489--
490-- /Unsafe/
491--
492-- /Pre-release/
493{-# INLINE getSliceUnsafe #-}
494getSliceUnsafe
495 :: Int -- ^ from index
496 -> Int -- ^ length of the slice
497 -> MutArray a
498 -> MutArray a
499getSliceUnsafe index len arr@MutArray {..} =
500 assert (index >= 0 && len >= 0 && index + len <= arrLen)
501 $ arr {arrStart = arrStart + index, arrLen = len}
503-- | /O(1)/ Slice an array in constant time. Throws an error if the slice
504-- extends out of the array bounds.
505--
506-- /Pre-release/
507{-# INLINE getSlice #-}
508getSlice
509 :: Int -- ^ from index
510 -> Int -- ^ length of the slice
511 -> MutArray a
512 -> MutArray a
513getSlice index len arr@MutArray{..} =
514 if index >= 0 && len >= 0 && index + len <= arrLen
515 then arr {arrStart = arrStart + index, arrLen = len}
516 else error
517 $ "getSlice: invalid slice, index "
518 ++ show index ++ " length " ++ show len
520-------------------------------------------------------------------------------
521-- to Lists and streams
522-------------------------------------------------------------------------------
524-- XXX Maybe faster to create a list explicitly instead of mapM, if list fusion
525-- does not work well.
527-- | Convert an 'Array' into a list.
528--
529-- /Pre-release/
530{-# INLINE toList #-}
531toList :: MonadIO m => MutArray a -> m [a]
532toList arr@MutArray{..} = mapM (`getIndexUnsafe` arr) [0 .. (arrLen - 1)]
534-- | Generates a stream from the elements of a @MutArray@.
535--
536-- >>> read = Stream.unfold MutArray.reader
537--
538{-# INLINE_NORMAL read #-}
539read :: MonadIO m => MutArray a -> D.Stream m a
540read arr@MutArray{..} =
541 D.mapM (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)
543-- Check equivalence with StreamK.fromStream . toStreamD and remove
544{-# INLINE toStreamK #-}
545toStreamK :: MonadIO m => MutArray a -> K.StreamK m a
546toStreamK arr@MutArray{..} = K.unfoldrM step 0
548 where
550 step i
551 | i == arrLen = return Nothing
552 | otherwise = do
553 x <- getIndexUnsafe i arr
554 return $ Just (x, i + 1)
556{-# INLINE_NORMAL readRev #-}
557readRev :: MonadIO m => MutArray a -> D.Stream m a
558readRev arr@MutArray{..} =
559 D.mapM (`getIndexUnsafe` arr)
560 $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0
562-------------------------------------------------------------------------------
563-- Folds
564-------------------------------------------------------------------------------
566-- XXX deduplicate this across unboxed array and this module?
568-- | The default chunk size by which the array creation routines increase the
569-- size of the array when the array is grown linearly.
570arrayChunkSize :: Int
571arrayChunkSize = 1024
573-- | Like 'writeN' but does not check the array bounds when writing. The fold
574-- driver must not call the step function more than 'n' times otherwise it will
575-- corrupt the memory and crash. This function exists mainly because any
576-- conditional in the step function blocks fusion causing 10x performance
577-- slowdown.
578--
579-- /Pre-release/
580{-# INLINE_NORMAL writeNUnsafe #-}
581writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
582writeNUnsafe n = Fold step initial return
584 where
586 initial = FL.Partial <$> new (max n 0)
588 step arr x = FL.Partial <$> snocUnsafe arr x
590-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
591-- 'Array'.
592--
593-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
594--
595-- /Pre-release/
596{-# INLINE_NORMAL writeN #-}
597writeN :: MonadIO m => Int -> Fold m a (MutArray a)
598writeN n = FL.take n $ writeNUnsafe n
600-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
601-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
602-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
604-- | @writeWith minCount@ folds the whole input to a single array. The array
605-- starts at a size big enough to hold minCount elements, the size is doubled
606-- every time the array needs to be grown.
607--
608-- /Caution! Do not use this on infinite streams./
609--
610-- /Pre-release/
611{-# INLINE_NORMAL writeWith #-}
612writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
613-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
614writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
616 where
618 initial = do
619 when (elemCount < 0) $ error "writeWith: elemCount is negative"
620 liftIO $ new elemCount
622 step arr@(MutArray _ start end bound) x
623 | end == bound = do
624 let oldSize = end - start
625 newSize = max (oldSize * 2) 1
626 arr1 <- liftIO $ realloc newSize arr
627 snocUnsafe arr1 x
628 step arr x = snocUnsafe arr x
630 -- extract = liftIO . rightSize
631 extract = return
633-- | Fold the whole input to a single array.
634--
635-- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes
636-- rounded up to the element size.
637--
638-- /Caution! Do not use this on infinite streams./
639--
640{-# INLINE write #-}
641write :: MonadIO m => Fold m a (MutArray a)
642write = writeWith arrayChunkSize
644-- | Create a 'MutArray' from the first @n@ elements of a stream. The
645-- array is allocated to size @n@, if the stream terminates before @n@
646-- elements then the array may hold less than @n@ elements.
647--
648{-# INLINE fromStreamN #-}
649fromStreamN :: MonadIO m => Int -> Stream m a -> m (MutArray a)
650fromStreamN n = D.fold (writeN n)
652{-# INLINE fromStream #-}
653fromStream :: MonadIO m => Stream m a -> m (MutArray a)
654fromStream = D.fold write
656{-# INLINABLE fromListN #-}
657fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)
658fromListN n xs = fromStreamN n $ D.fromList xs
660{-# INLINABLE fromList #-}
661fromList :: MonadIO m => [a] -> m (MutArray a)
662fromList xs = fromStream $ D.fromList xs
664{-# INLINABLE fromPureStream #-}
665fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
666fromPureStream xs =
667 liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
669-------------------------------------------------------------------------------
670-- Unfolds
671-------------------------------------------------------------------------------
673-- | Resumable unfold of an array.
674--
675{-# INLINE_NORMAL producerWith #-}
676producerWith :: Monad m => (forall b. IO b -> m b) -> Producer m (MutArray a) a
677producerWith liftio = Producer step inject extract
679 where
681 {-# INLINE inject #-}
682 inject arr = return (arr, 0)
684 {-# INLINE extract #-}
685 extract (arr, i) =
686 return $ arr {arrStart = arrStart arr + i, arrLen = arrLen arr - i}
688 {-# INLINE_LATE step #-}
689 step (arr, i)
690 | assert (arrLen arr >= 0) (i == arrLen arr) = return D.Stop
691 step (arr, i) = do
692 x <- liftio $ getIndexUnsafe i arr
693 return $ D.Yield x (arr, i + 1)
695-- | Resumable unfold of an array.
696--
697{-# INLINE_NORMAL producer #-}
698producer :: MonadIO m => Producer m (MutArray a) a
699producer = producerWith liftIO
701-- | Unfold an array into a stream.
702--
703{-# INLINE_NORMAL reader #-}
704reader :: MonadIO m => Unfold m (MutArray a) a
705reader = Producer.simplify producer
707--------------------------------------------------------------------------------
708-- Appending arrays
709--------------------------------------------------------------------------------
711-- | Put a sub range of a source array into a subrange of a destination array.
712-- This is not safe as it does not check the bounds.
713{-# INLINE putSliceUnsafe #-}
714putSliceUnsafe :: MonadIO m =>
715 MutArray a -> Int -> MutArray a -> Int -> Int -> m ()
716putSliceUnsafe src srcStart dst dstStart len = liftIO $ do
717 assertM(len <= arrLen dst)
718 assertM(len <= arrLen src)
719 let !(I# srcStart#) = srcStart + arrStart src
720 !(I# dstStart#) = dstStart + arrStart dst
721 !(I# len#) = len
722 let arrS# = arrContents# src
723 arrD# = arrContents# dst
724 IO $ \s# -> (# copyMutableArray#
725 arrS# srcStart# arrD# dstStart# len# s#
726 , () #)
728{-# INLINE clone #-}
729clone :: MonadIO m => MutArray a -> m (MutArray a)
730clone src = liftIO $ do
731 let len = arrLen src
732 dst <- new len
733 putSliceUnsafe src 0 dst 0 len
734 return dst
736-------------------------------------------------------------------------------
737-- Size
738-------------------------------------------------------------------------------
740{-# INLINE length #-}
741length :: MutArray a -> Int
742length = arrLen
744-------------------------------------------------------------------------------
745-- Equality
746-------------------------------------------------------------------------------
748-- | Compare the length of the arrays. If the length is equal, compare the
749-- lexicographical ordering of two underlying byte arrays otherwise return the
750-- result of length comparison.
751--
752-- /Pre-release/
753{-# INLINE cmp #-}
754cmp :: (MonadIO m, Ord a) => MutArray a -> MutArray a -> m Ordering
755cmp a1 a2 =
756 case compare lenA1 lenA2 of
757 EQ -> loop (lenA1 - 1)
758 x -> return x
760 where
762 lenA1 = length a1
763 lenA2 = length a2
765 loop i
766 | i < 0 = return EQ
767 | otherwise = do
768 v1 <- getIndexUnsafe i a1
769 v2 <- getIndexUnsafe i a2
770 case compare v1 v2 of
771 EQ -> loop (i - 1)
772 x -> return x
774{-# INLINE eq #-}
775eq :: (MonadIO m, Eq a) => MutArray a -> MutArray a -> m Bool
776eq a1 a2 =
777 if lenA1 == lenA2
778 then loop (lenA1 - 1)
779 else return False
781 where
783 lenA1 = length a1
784 lenA2 = length a2
786 loop i
787 | i < 0 = return True
788 | otherwise = do
789 v1 <- getIndexUnsafe i a1
790 v2 <- getIndexUnsafe i a2
791 if v1 == v2
792 then loop (i - 1)
793 else return False
795{-# INLINE strip #-}
796strip :: MonadIO m => (a -> Bool) -> MutArray a -> m (MutArray a)
797strip p arr = liftIO $ do
798 let lastIndex = length arr - 1
799 indexR <- getIndexR lastIndex -- last predicate failing index
800 if indexR < 0
801 then nil
802 else do
803 indexL <- getIndexL 0 -- first predicate failing index
804 if indexL == 0 && indexR == lastIndex
805 then return arr
806 else
807 let newLen = indexR - indexL + 1
808 in return $ getSliceUnsafe indexL newLen arr
810 where
812 getIndexR idx
813 | idx < 0 = return idx
814 | otherwise = do
815 r <- getIndexUnsafe idx arr
816 if p r
817 then getIndexR (idx - 1)
818 else return idx
820 getIndexL idx = do
821 r <- getIndexUnsafe idx arr
822 if p r
823 then getIndexL (idx + 1)
824 else return idx