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
51import Control.Monad (when, replicateM)
52import Control.Monad.IO.Class (liftIO, MonadIO)
53import GHC.Base (MutableArray#, RealWorld)
54import GHC.IO (unsafePerformIO)
55import Text.Read (readPrec)
57import Streamly.Internal.Data.Fold.Type (Fold(..))
58import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
59import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
60import Streamly.Internal.Data.Unfold.Type (Unfold(..))
61import Streamly.Internal.System.IO (unsafeInlineIO)
63import qualified Streamly.Internal.Data.Array.Generic.Mut.Type as MArray
64import qualified Streamly.Internal.Data.Fold.Type as FL
65import qualified Streamly.Internal.Data.Producer.Type as Producer
66import qualified Streamly.Internal.Data.Producer as Producer
67import qualified Streamly.Internal.Data.Ring as RB
68import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
69import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
70import qualified Text.ParserCombinators.ReadPrec as ReadPrec
73import Prelude hiding (foldr, length, 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 $ do
101 return $ unsafeFreeze marr
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
123-------------------------------------------------------------------------------
124-- Construction - from streams
125-------------------------------------------------------------------------------
127{-# INLINE_NORMAL fromStreamDN #-}
128fromStreamDN :: MonadIO m => Int -> D.Stream m a -> m (Array a)
129fromStreamDN limit str = do
130 marr <- liftIO $ MArray.new (max limit 0)
133 (\i x -> i `seq` liftIO $ MArray.putIndexUnsafe i marr x >> return (i + 1))
136 return $ unsafeFreeze $ marr { MArray.arrLen = i }
138{-# INLINE fromStreamD #-}
139fromStreamD :: MonadIO m => D.Stream m a -> m (Array a)
140fromStreamD = D.fold write
142{-# INLINE fromStreamN #-}
143fromStreamN :: MonadIO m => Int -> Stream m a -> m (Array a)
145 when (n < 0) $ error "fromStreamN: negative write count specified"
148{-# INLINE fromStream #-}
149fromStream :: MonadIO m => Stream m a -> m (Array a)
150fromStream = fromStreamD
152{-# INLINABLE fromListN #-}
153fromListN :: Int -> [a] -> Array a
154fromListN n xs = unsafePerformIO $ fromStreamDN n $ D.fromList xs
156{-# INLINABLE fromList #-}
157fromList :: [a] -> Array a
158fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
160-------------------------------------------------------------------------------
161-- Elimination - Unfolds
162-------------------------------------------------------------------------------
165length :: Array a -> Int
168{-# INLINE_NORMAL reader #-}
169reader :: Monad m => Unfold m (Array a) a
172 $ Producer.translate unsafeThaw unsafeFreeze
173 $ MArray.producerWith (return . unsafeInlineIO)
175-------------------------------------------------------------------------------
176-- Elimination - to streams
177-------------------------------------------------------------------------------
179{-# INLINE_NORMAL toList #-}
180toList :: Array a -> [a]
186 loop i | i == len = []
187 loop i = getIndexUnsafe arr i : loop (i + 1)
189{-# INLINE_NORMAL readStreamD #-}
190readStreamD :: MonadIO m => Array a -> D.Stream m a
191readStreamD = MArray.toStreamD . unsafeThaw
193{-# INLINE_NORMAL readRevStreamD #-}
194readRevStreamD :: Monad m => Array a -> D.Stream m a
195readRevStreamD arr@Array{..} =
196 D.map (getIndexUnsafe arr)
197 $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0
199{-# INLINE_EARLY read #-}
200read :: MonadIO m => Array a -> Stream m a
203{-# INLINE_EARLY readRev #-}
204readRev :: Monad m => Array a -> Stream m a
205readRev = readRevStreamD
207-------------------------------------------------------------------------------
208-- Elimination - using Folds
209-------------------------------------------------------------------------------
211{-# INLINE_NORMAL foldl' #-}
212foldl' :: (b -> a -> b) -> b -> Array a -> b
213foldl' f z arr = unsafePerformIO $ D.foldl' f z $ readStreamD arr
215{-# INLINE_NORMAL foldr #-}
216foldr :: (a -> b -> b) -> b -> Array a -> b
217foldr f z arr = unsafePerformIO $ D.foldr f z $ readStreamD arr
220fold :: MonadIO m => Fold m a b -> Array a -> m b
221fold f arr = D.fold f (readStreamD arr)
223{-# INLINE streamFold #-}
224streamFold :: MonadIO m => (Stream m a -> m b) -> Array a -> m b
225streamFold f arr = f (read arr)
227-------------------------------------------------------------------------------
228-- Random reads and writes
229-------------------------------------------------------------------------------
231-- | /O(1)/ Lookup the element at the given index. Index starts from 0. Does
232-- not check the bounds.
235{-# INLINE getIndexUnsafe #-}
236getIndexUnsafe :: Array a -> Int -> a
237getIndexUnsafe arr i =
238 unsafePerformIO $ MArray.getIndexUnsafe i (unsafeThaw arr)
240{-# INLINE writeLastN #-}
241writeLastN :: MonadIO m => Int -> Fold m a (Array a)
243 | n <= 0 = fmap (const nil) FL.drain
244 | otherwise = Fold step initial done
249 rb <- liftIO $ RB.createRing n
250 return $ FL.Partial $ Tuple' rb (0 :: Int)
252 step (Tuple' rb cnt) x = do
253 liftIO $ RB.unsafeInsertRing rb x
254 return $ FL.Partial $ Tuple' rb (cnt + 1)
256 done (Tuple' rb cnt) = do
258 arr <- liftIO $ MArray.new len
259 arr1 <- MArray.uninit arr len
260 -- XXX Should use an API for retrieving the ring head
261 rhead <- liftIO $ readIORef $ RB.ringHead rb
264 MArray.putSliceUnsafe (RB.ringArr rb) 0 arr1 0 cnt
266 MArray.putSliceUnsafe (RB.ringArr rb) rhead arr1 0 (n - rhead)
267 MArray.putSliceUnsafe (RB.ringArr rb) 0 arr1 (n - rhead) rhead
268 return $ unsafeFreeze arr1
270sliceUnsafe :: Int -> Int -> Array a -> Array a
271sliceUnsafe offset len (Array cont off1 _) = Array cont (off1 + offset) len
273-- XXX This is not efficient as it copies the array. We should support array
274-- slicing so that we can just refer to the underlying array memory instead of
277-- | Truncate the array at the beginning and end as long as the predicate
279strip :: (a -> Bool) -> Array a -> Array a
281 let lastIndex = length arr - 1
282 indexR = getIndexR lastIndex -- last predicate failing index
286 let indexL = getIndexL 0 -- first predicate failing index
287 in if indexL == 0 && indexR == lastIndex
289 else unsafeFreeze $ unsafePerformIO $ do
290 let newLen = indexR - indexL + 1
291 arrThawed = unsafeThaw (sliceUnsafe indexL newLen arr)
292 MArray.clone arrThawed
299 if p (getIndexUnsafe arr idx) then getIndexR (idx - 1) else idx
301 getIndexL idx = if p (getIndexUnsafe arr idx)
302 then getIndexL (idx + 1)
305-------------------------------------------------------------------------------
307-------------------------------------------------------------------------------
309instance Eq a => Eq (Array a) where
310 a1 == a2 = lenA1 == lenA2 && loop (lenA1 - 1)
320 let v1 = getIndexUnsafe a1 i
321 v2 = getIndexUnsafe a2 i
322 in v1 == v2 && loop (i - 1)
324instance Ord a => Ord (Array a) where
326 case compare lenA1 lenA2 of
339 let v1 = getIndexUnsafe a1 i
340 v2 = getIndexUnsafe a2 i
341 in case compare v1 v2 of
346instance Show a => Show (Array a) where
347 show arr = "fromList " ++ show (toList arr)
349instance Read a => Read (Array a) where
351 fromListWord <- replicateM 9 ReadPrec.get
352 if fromListWord == "fromList "
353 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