2-- Module : Streamly.Internal.Data.Array.Generic
3-- Copyright : (c) 2019 Composewell Technologies
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
9module Streamly.Internal.Data.Array.Generic
52import Control.Monad (replicateM)
53import Control.Monad.IO.Class (MonadIO)
54import Data.Functor.Identity (Identity(..))
55import Data.Word (Word8)
56import GHC.Base (MutableArray#, RealWorld)
57import GHC.Exts (Addr#)
58import GHC.IO (unsafePerformIO)
59import Text.Read (readPrec)
61import Streamly.Internal.Data.Fold.Type (Fold(..))
62import Streamly.Internal.Data.Stream.Type (Stream)
63import Streamly.Internal.Data.Unfold.Type (Unfold(..))
64import Streamly.Internal.System.IO (unsafeInlineIO)
66import qualified Streamly.Internal.Data.MutArray.Generic as MArray
67import qualified Streamly.Internal.Data.Fold.Type as FL
68import qualified Streamly.Internal.Data.Producer.Type as Producer
69import qualified Streamly.Internal.Data.Producer as Producer
70import qualified Streamly.Internal.Data.Ring.Generic as RB
71import qualified Streamly.Internal.Data.Stream.Type as D
72import qualified Streamly.Internal.Data.Stream.Generate as D
73import qualified Text.ParserCombinators.ReadPrec as ReadPrec
75import Prelude hiding (Foldable(..), read)
77-------------------------------------------------------------------------------
79-------------------------------------------------------------------------------
83 { arrContents# :: MutableArray# RealWorld a
84 -- ^ The internal contents of the array representing the entire array.
86 , arrStart :: {-# UNPACK #-}!Int
87 -- ^ The starting index of this slice.
89 , arrLen :: {-# UNPACK #-}!Int
90 -- ^ The length of this slice.
93unsafeFreeze :: MArray.MutArray a -> Array a
94unsafeFreeze (MArray.MutArray cont# arrS arrL _) = Array cont# arrS arrL
96unsafeThaw :: Array a -> MArray.MutArray a
97unsafeThaw (Array cont# arrS arrL) = MArray.MutArray cont# arrS arrL arrL
101nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
103-------------------------------------------------------------------------------
104-- Construction - Folds
105-------------------------------------------------------------------------------
107{-# INLINE_NORMAL writeN #-}
108writeN :: MonadIO m => Int -> Fold m a (Array a)
109writeN = fmap unsafeFreeze <$> MArray.writeN
111{-# INLINE_NORMAL writeWith #-}
112writeWith :: MonadIO m => Int -> Fold m a (Array a)
113writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
115-- | Fold the whole input to a single array.
117-- /Caution! Do not use this on infinite streams./
120write :: MonadIO m => Fold m a (Array a)
121write = fmap unsafeFreeze MArray.write
123fromPureStream :: Stream Identity a -> Array a
125 unsafePerformIO $ fmap (unsafeFreeze) (MArray.fromPureStream x)
126-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
127-- fromPureStream = fromList . runIdentity . D.toList
129fromByteStr# :: Addr# -> Array Word8
130fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
132-------------------------------------------------------------------------------
134-------------------------------------------------------------------------------
136{-# INLINE_NORMAL chunksOf #-}
137chunksOf :: forall m a. MonadIO m
138 => Int -> Stream m a -> Stream m (Array a)
139chunksOf n strm = fmap unsafeFreeze $ MArray.chunksOf n strm
141-------------------------------------------------------------------------------
142-- Construction - from streams
143-------------------------------------------------------------------------------
145{-# INLINE fromStreamN #-}
146fromStreamN :: MonadIO m => Int -> Stream m a -> m (Array a)
147fromStreamN n = D.fold (writeN n)
149{-# INLINE fromStream #-}
150fromStream :: MonadIO m => Stream m a -> m (Array a)
151fromStream = D.fold write
153-- XXX Consider foldr/build fusion in toList/fromList
155{-# INLINABLE fromListN #-}
156fromListN :: Int -> [a] -> Array a
157fromListN n xs = unsafePerformIO $ fromStreamN n $ D.fromList xs
159{-# INLINABLE fromList #-}
160fromList :: [a] -> Array a
161fromList xs = unsafePerformIO $ fromStream $ D.fromList xs
163-------------------------------------------------------------------------------
164-- Elimination - Unfolds
165-------------------------------------------------------------------------------
168length :: Array a -> Int
171{-# INLINE_NORMAL reader #-}
172reader :: Monad m => Unfold m (Array a) a
175 $ Producer.translate unsafeThaw unsafeFreeze
176 $ MArray.producerWith (return . unsafeInlineIO)
178-------------------------------------------------------------------------------
179-- Elimination - to streams
180-------------------------------------------------------------------------------
182{-# INLINE_NORMAL toList #-}
183toList :: Array a -> [a]
189 loop i | i == len = []
190 loop i = getIndexUnsafe i arr : loop (i + 1)
192{-# INLINE_NORMAL read #-}
193read :: Monad m => Array a -> Stream m a
195 D.map (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)
197{-# INLINE_NORMAL readRev #-}
198readRev :: Monad m => Array a -> Stream m a
199readRev arr@Array{..} =
200 D.map (`getIndexUnsafe` arr)
201 $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0
203-------------------------------------------------------------------------------
204-- Elimination - using Folds
205-------------------------------------------------------------------------------
207{-# INLINE_NORMAL foldl' #-}
208foldl' :: (b -> a -> b) -> b -> Array a -> b
209foldl' f z arr = unsafePerformIO $ D.foldl' f z $ read arr
211{-# INLINE_NORMAL foldr #-}
212foldr :: (a -> b -> b) -> b -> Array a -> b
213foldr f z arr = unsafePerformIO $ D.foldr f z $ read arr
216fold :: Monad m => Fold m a b -> Array a -> m b
217fold f arr = D.fold f (read arr)
219{-# INLINE streamFold #-}
220streamFold :: Monad m => (Stream m a -> m b) -> Array a -> m b
221streamFold f arr = f (read arr)
223-------------------------------------------------------------------------------
224-- Random reads and writes
225-------------------------------------------------------------------------------
227-- | /O(1)/ Lookup the element at the given index. Index starts from 0. Does
228-- not check the bounds.
231{-# INLINE getIndexUnsafe #-}
232getIndexUnsafe :: Int -> Array a -> a
233getIndexUnsafe i arr =
234 unsafePerformIO $ MArray.getIndexUnsafe i (unsafeThaw arr)
236-- | Lookup the element at the given index. Index starts from 0.
238{-# INLINE getIndex #-}
239getIndex :: Int -> Array a -> Maybe a
240getIndex i arr@Array {..} =
241 if i >= 0 && i < arrLen
242 then Just $ getIndexUnsafe i arr
245-- >>> import qualified Streamly.Data.Stream as Stream
246-- >>> import qualified Streamly.Data.Fold as Fold
247-- >>> import qualified Streamly.Internal.Data.Array.Generic as Array
248-- >>> import Data.Function ((&))
250-- Stream.fromList [1,2,3,4,5::Int]
251-- & Stream.scan (Array.writeLastN 2)
252-- & Stream.fold Fold.toList
254-- [fromList [],fromList [1],fromList [1,2],fromList [2,3],fromList [3,4],fromList [4,5]]
256{-# INLINE writeLastN #-}
257writeLastN :: MonadIO m => Int -> Fold m a (Array a)
258writeLastN n = FL.rmapM f (RB.writeLastN n)
263 arr <- RB.copyToMutArray 0 n rb
264 return $ unsafeFreeze arr
266{-# INLINE getSliceUnsafe #-}
267getSliceUnsafe :: Int -> Int -> Array a -> Array a
268getSliceUnsafe offset len (Array cont off1 _) = Array cont (off1 + offset) len
270-- XXX This is not efficient as it copies the array. We should support array
271-- slicing so that we can just refer to the underlying array memory instead of
274-- | Truncate the array at the beginning and end as long as the predicate
275-- holds true. Returns a slice of the original array.
277strip :: (a -> Bool) -> Array a -> Array a
278strip p arr = unsafeFreeze $ unsafePerformIO $ MArray.strip p (unsafeThaw arr)
280-------------------------------------------------------------------------------
282-------------------------------------------------------------------------------
284instance Eq a => Eq (Array a) where
287 unsafeInlineIO $! unsafeThaw arr1 `MArray.eq` unsafeThaw arr2
289instance Ord a => Ord (Array a) where
290 {-# INLINE compare #-}
292 unsafeInlineIO $! unsafeThaw arr1 `MArray.cmp` unsafeThaw arr2
294 -- Default definitions defined in base do not have an INLINE on them, so we
295 -- replicate them here with an INLINE.
297 x < y = case compare x y of { LT -> True; _ -> False }
300 x <= y = case compare x y of { GT -> False; _ -> True }
303 x > y = case compare x y of { GT -> True; _ -> False }
306 x >= y = case compare x y of { LT -> False; _ -> True }
308 -- These two default methods use '<=' rather than 'compare'
309 -- because the latter is often more expensive
311 max x y = if x <= y then y else x
314 min x y = if x <= y then x else y
316instance Show a => Show (Array a) where
318 show arr = "fromList " ++ show (toList arr)
320instance Read a => Read (Array a) where
321 {-# INLINE readPrec #-}
323 fromListWord <- replicateM 9 ReadPrec.get
324 if fromListWord == "fromList "
325 then fromList <$> readPrec
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list