2-- Module : Streamly.Internal.Data.Array.Generic
3-- Copyright : (c) 2019 Composewell Technologies
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
7-- Stability : pre-release
10module Streamly.Internal.Data.Array.Generic
48import Control.Monad (replicateM)
49import Control.Monad.IO.Class (MonadIO)
50import GHC.Base (MutableArray#, RealWorld)
51import GHC.IO (unsafePerformIO)
52import Text.Read (readPrec)
54import Streamly.Internal.Data.Fold.Type (Fold(..))
55import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
56import Streamly.Internal.Data.Unfold.Type (Unfold(..))
57import Streamly.Internal.System.IO (unsafeInlineIO)
59import qualified Streamly.Internal.Data.Array.Generic.Mut.Type as MArray
60import qualified Streamly.Internal.Data.Fold.Type as FL
61import qualified Streamly.Internal.Data.Producer.Type as Producer
62import qualified Streamly.Internal.Data.Producer as Producer
63import qualified Streamly.Internal.Data.Ring as RB
64import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
65import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
66import qualified Text.ParserCombinators.ReadPrec as ReadPrec
68import Prelude hiding (foldr, length, read)
70-------------------------------------------------------------------------------
72-------------------------------------------------------------------------------
76 { arrContents# :: MutableArray# RealWorld a
77 -- ^ The internal contents of the array representing the entire array.
79 , arrStart :: {-# UNPACK #-}!Int
80 -- ^ The starting index of this slice.
82 , arrLen :: {-# UNPACK #-}!Int
83 -- ^ The length of this slice.
86unsafeFreeze :: MArray.MutArray a -> Array a
87unsafeFreeze (MArray.MutArray cont# arrS arrL _) = Array cont# arrS arrL
89unsafeThaw :: Array a -> MArray.MutArray a
90unsafeThaw (Array cont# arrS arrL) = MArray.MutArray cont# arrS arrL arrL
94nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
96-------------------------------------------------------------------------------
97-- Construction - Folds
98-------------------------------------------------------------------------------
100{-# INLINE_NORMAL writeN #-}
101writeN :: MonadIO m => Int -> Fold m a (Array a)
102writeN = fmap unsafeFreeze <$> MArray.writeN
104{-# INLINE_NORMAL writeWith #-}
105writeWith :: MonadIO m => Int -> Fold m a (Array a)
106writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
108-- | Fold the whole input to a single array.
110-- /Caution! Do not use this on infinite streams./
113write :: MonadIO m => Fold m a (Array a)
114write = fmap unsafeFreeze MArray.write
116-------------------------------------------------------------------------------
117-- Construction - from streams
118-------------------------------------------------------------------------------
120{-# INLINE fromStreamN #-}
121fromStreamN :: MonadIO m => Int -> Stream m a -> m (Array a)
122fromStreamN n = D.fold (writeN n)
124{-# INLINE fromStream #-}
125fromStream :: MonadIO m => Stream m a -> m (Array a)
126fromStream = D.fold write
128-- XXX Consider foldr/build fusion in toList/fromList
130{-# INLINABLE fromListN #-}
131fromListN :: Int -> [a] -> Array a
132fromListN n xs = unsafePerformIO $ fromStreamN n $ D.fromList xs
134{-# INLINABLE fromList #-}
135fromList :: [a] -> Array a
136fromList xs = unsafePerformIO $ fromStream $ D.fromList xs
138-------------------------------------------------------------------------------
139-- Elimination - Unfolds
140-------------------------------------------------------------------------------
143length :: Array a -> Int
146{-# INLINE_NORMAL reader #-}
147reader :: Monad m => Unfold m (Array a) a
150 $ Producer.translate unsafeThaw unsafeFreeze
151 $ MArray.producerWith (return . unsafeInlineIO)
153-------------------------------------------------------------------------------
154-- Elimination - to streams
155-------------------------------------------------------------------------------
157{-# INLINE_NORMAL toList #-}
158toList :: Array a -> [a]
164 loop i | i == len = []
165 loop i = getIndexUnsafe i arr : loop (i + 1)
167{-# INLINE_NORMAL read #-}
168read :: Monad m => Array a -> Stream m a
170 D.map (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)
172{-# INLINE_NORMAL readRev #-}
173readRev :: Monad m => Array a -> Stream m a
174readRev arr@Array{..} =
175 D.map (`getIndexUnsafe` arr)
176 $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0
178-------------------------------------------------------------------------------
179-- Elimination - using Folds
180-------------------------------------------------------------------------------
182{-# INLINE_NORMAL foldl' #-}
183foldl' :: (b -> a -> b) -> b -> Array a -> b
184foldl' f z arr = unsafePerformIO $ D.foldl' f z $ read arr
186{-# INLINE_NORMAL foldr #-}
187foldr :: (a -> b -> b) -> b -> Array a -> b
188foldr f z arr = unsafePerformIO $ D.foldr f z $ read arr
191fold :: Monad m => Fold m a b -> Array a -> m b
192fold f arr = D.fold f (read arr)
194{-# INLINE streamFold #-}
195streamFold :: Monad m => (Stream m a -> m b) -> Array a -> m b
196streamFold f arr = f (read arr)
198-------------------------------------------------------------------------------
199-- Random reads and writes
200-------------------------------------------------------------------------------
202-- | /O(1)/ Lookup the element at the given index. Index starts from 0. Does
203-- not check the bounds.
206{-# INLINE getIndexUnsafe #-}
207getIndexUnsafe :: Int -> Array a -> a
208getIndexUnsafe i arr =
209 unsafePerformIO $ MArray.getIndexUnsafe i (unsafeThaw arr)
211{-# INLINE writeLastN #-}
212writeLastN :: MonadIO m => Int -> Fold m a (Array a)
213writeLastN n = FL.rmapM f (RB.writeLastN n)
218 arr <- RB.toMutArray 0 n rb
219 return $ unsafeFreeze arr
221{-# INLINE getSliceUnsafe #-}
222getSliceUnsafe :: Int -> Int -> Array a -> Array a
223getSliceUnsafe offset len (Array cont off1 _) = Array cont (off1 + offset) len
225-- XXX This is not efficient as it copies the array. We should support array
226-- slicing so that we can just refer to the underlying array memory instead of
229-- | Truncate the array at the beginning and end as long as the predicate
230-- holds true. Returns a slice of the original array.
232strip :: (a -> Bool) -> Array a -> Array a
233strip p arr = unsafeFreeze $ unsafePerformIO $ MArray.strip p (unsafeThaw arr)
235-------------------------------------------------------------------------------
237-------------------------------------------------------------------------------
239instance Eq a => Eq (Array a) where
242 unsafeInlineIO $! unsafeThaw arr1 `MArray.eq` unsafeThaw arr2
244instance Ord a => Ord (Array a) where
245 {-# INLINE compare #-}
247 unsafeInlineIO $! unsafeThaw arr1 `MArray.cmp` unsafeThaw arr2
249 -- Default definitions defined in base do not have an INLINE on them, so we
250 -- replicate them here with an INLINE.
252 x < y = case compare x y of { LT -> True; _ -> False }
255 x <= y = case compare x y of { GT -> False; _ -> True }
258 x > y = case compare x y of { GT -> True; _ -> False }
261 x >= y = case compare x y of { LT -> False; _ -> True }
263 -- These two default methods use '<=' rather than 'compare'
264 -- because the latter is often more expensive
266 max x y = if x <= y then y else x
269 min x y = if x <= y then x else y
271instance Show a => Show (Array a) where
273 show arr = "fromList " ++ show (toList arr)
275instance Read a => Read (Array a) where
276 {-# INLINE readPrec #-}
278 fromListWord <- replicateM 9 ReadPrec.get
279 if fromListWord == "fromList "
280 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