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
50import Control.Monad (replicateM)
51import Control.Monad.IO.Class (MonadIO)
52import Data.Functor.Identity (Identity(..))
53import Data.Word (Word8)
54import GHC.Base (MutableArray#, RealWorld)
55import GHC.Exts (Addr#)
56import GHC.IO (unsafePerformIO)
57import Text.Read (readPrec)
59import Streamly.Internal.Data.Fold.Type (Fold(..))
60import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
61import Streamly.Internal.Data.Unfold.Type (Unfold(..))
62import Streamly.Internal.System.IO (unsafeInlineIO)
64import qualified Streamly.Internal.Data.MutArray.Generic as MArray
65import qualified Streamly.Internal.Data.Fold.Type as FL
66import qualified Streamly.Internal.Data.Producer.Type as Producer
67import qualified Streamly.Internal.Data.Producer as Producer
68import qualified Streamly.Internal.Data.Ring as RB
69import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
70import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
71import qualified Text.ParserCombinators.ReadPrec as ReadPrec
73import Prelude hiding (Foldable(..), read)
75-------------------------------------------------------------------------------
77-------------------------------------------------------------------------------
81 { arrContents# :: MutableArray# RealWorld a
82 -- ^ The internal contents of the array representing the entire array.
84 , arrStart :: {-# UNPACK #-}!Int
85 -- ^ The starting index of this slice.
87 , arrLen :: {-# UNPACK #-}!Int
88 -- ^ The length of this slice.
91unsafeFreeze :: MArray.MutArray a -> Array a
92unsafeFreeze (MArray.MutArray cont# arrS arrL _) = Array cont# arrS arrL
94unsafeThaw :: Array a -> MArray.MutArray a
95unsafeThaw (Array cont# arrS arrL) = MArray.MutArray cont# arrS arrL arrL
99nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
101-------------------------------------------------------------------------------
102-- Construction - Folds
103-------------------------------------------------------------------------------
105{-# INLINE_NORMAL writeN #-}
106writeN :: MonadIO m => Int -> Fold m a (Array a)
107writeN = fmap unsafeFreeze <$> MArray.writeN
109{-# INLINE_NORMAL writeWith #-}
110writeWith :: MonadIO m => Int -> Fold m a (Array a)
111writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
113-- | Fold the whole input to a single array.
115-- /Caution! Do not use this on infinite streams./
118write :: MonadIO m => Fold m a (Array a)
119write = fmap unsafeFreeze MArray.write
121fromPureStream :: Stream Identity a -> Array a
123 unsafePerformIO $ fmap (unsafeFreeze) (MArray.fromPureStream x)
124-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
125-- fromPureStream = fromList . runIdentity . D.toList
127fromByteStr# :: Addr# -> Array Word8
128fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
130-------------------------------------------------------------------------------
131-- Construction - from streams
132-------------------------------------------------------------------------------
134{-# INLINE fromStreamN #-}
135fromStreamN :: MonadIO m => Int -> Stream m a -> m (Array a)
136fromStreamN n = D.fold (writeN n)
138{-# INLINE fromStream #-}
139fromStream :: MonadIO m => Stream m a -> m (Array a)
140fromStream = D.fold write
142-- XXX Consider foldr/build fusion in toList/fromList
144{-# INLINABLE fromListN #-}
145fromListN :: Int -> [a] -> Array a
146fromListN n xs = unsafePerformIO $ fromStreamN n $ D.fromList xs
148{-# INLINABLE fromList #-}
149fromList :: [a] -> Array a
150fromList xs = unsafePerformIO $ fromStream $ D.fromList xs
152-------------------------------------------------------------------------------
153-- Elimination - Unfolds
154-------------------------------------------------------------------------------
157length :: Array a -> Int
160{-# INLINE_NORMAL reader #-}
161reader :: Monad m => Unfold m (Array a) a
164 $ Producer.translate unsafeThaw unsafeFreeze
165 $ MArray.producerWith (return . unsafeInlineIO)
167-------------------------------------------------------------------------------
168-- Elimination - to streams
169-------------------------------------------------------------------------------
171{-# INLINE_NORMAL toList #-}
172toList :: Array a -> [a]
178 loop i | i == len = []
179 loop i = getIndexUnsafe i arr : loop (i + 1)
181{-# INLINE_NORMAL read #-}
182read :: Monad m => Array a -> Stream m a
184 D.map (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)
186{-# INLINE_NORMAL readRev #-}
187readRev :: Monad m => Array a -> Stream m a
188readRev arr@Array{..} =
189 D.map (`getIndexUnsafe` arr)
190 $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0
192-------------------------------------------------------------------------------
193-- Elimination - using Folds
194-------------------------------------------------------------------------------
196{-# INLINE_NORMAL foldl' #-}
197foldl' :: (b -> a -> b) -> b -> Array a -> b
198foldl' f z arr = unsafePerformIO $ D.foldl' f z $ read arr
200{-# INLINE_NORMAL foldr #-}
201foldr :: (a -> b -> b) -> b -> Array a -> b
202foldr f z arr = unsafePerformIO $ D.foldr f z $ read arr
205fold :: Monad m => Fold m a b -> Array a -> m b
206fold f arr = D.fold f (read arr)
208{-# INLINE streamFold #-}
209streamFold :: Monad m => (Stream m a -> m b) -> Array a -> m b
210streamFold f arr = f (read arr)
212-------------------------------------------------------------------------------
213-- Random reads and writes
214-------------------------------------------------------------------------------
216-- | /O(1)/ Lookup the element at the given index. Index starts from 0. Does
217-- not check the bounds.
220{-# INLINE getIndexUnsafe #-}
221getIndexUnsafe :: Int -> Array a -> a
222getIndexUnsafe i arr =
223 unsafePerformIO $ MArray.getIndexUnsafe i (unsafeThaw arr)
225-- | Lookup the element at the given index. Index starts from 0.
227{-# INLINE getIndex #-}
228getIndex :: Int -> Array a -> Maybe a
229getIndex i arr@Array {..} =
230 if i >= 0 && i < arrLen
231 then Just $ getIndexUnsafe i arr
234{-# INLINE writeLastN #-}
235writeLastN :: MonadIO m => Int -> Fold m a (Array a)
236writeLastN n = FL.rmapM f (RB.writeLastN n)
241 arr <- RB.toMutArray 0 n rb
242 return $ unsafeFreeze arr
244{-# INLINE getSliceUnsafe #-}
245getSliceUnsafe :: Int -> Int -> Array a -> Array a
246getSliceUnsafe offset len (Array cont off1 _) = Array cont (off1 + offset) len
248-- XXX This is not efficient as it copies the array. We should support array
249-- slicing so that we can just refer to the underlying array memory instead of
252-- | Truncate the array at the beginning and end as long as the predicate
253-- holds true. Returns a slice of the original array.
255strip :: (a -> Bool) -> Array a -> Array a
256strip p arr = unsafeFreeze $ unsafePerformIO $ MArray.strip p (unsafeThaw arr)
258-------------------------------------------------------------------------------
260-------------------------------------------------------------------------------
262instance Eq a => Eq (Array a) where
265 unsafeInlineIO $! unsafeThaw arr1 `MArray.eq` unsafeThaw arr2
267instance Ord a => Ord (Array a) where
268 {-# INLINE compare #-}
270 unsafeInlineIO $! unsafeThaw arr1 `MArray.cmp` unsafeThaw arr2
272 -- Default definitions defined in base do not have an INLINE on them, so we
273 -- replicate them here with an INLINE.
275 x < y = case compare x y of { LT -> True; _ -> False }
278 x <= y = case compare x y of { GT -> False; _ -> True }
281 x > y = case compare x y of { GT -> True; _ -> False }
284 x >= y = case compare x y of { LT -> False; _ -> True }
286 -- These two default methods use '<=' rather than 'compare'
287 -- because the latter is often more expensive
289 max x y = if x <= y then y else x
292 min x y = if x <= y then x else y
294instance Show a => Show (Array a) where
296 show arr = "fromList " ++ show (toList arr)
298instance Read a => Read (Array a) where
299 {-# INLINE readPrec #-}
301 fromListWord <- replicateM 9 ReadPrec.get
302 if fromListWord == "fromList "
303 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