on master help sign in
Move writeLastN to the Ring module
0bd3d4eHarendra Kumar
core/src/Streamly/Internal/Data/Array/Generic.hs+5−27
@@ -45,14 +45,13 @@ where
45#include "inline.hs"
45#include "inline.hs"
46
46
47import Control.Monad (replicateM)
47import Control.Monad (replicateM)
48−import Control.Monad.IO.Class (liftIO, MonadIO)
48+import Control.Monad.IO.Class (MonadIO)
49import GHC.Base (MutableArray#, RealWorld)
49import GHC.Base (MutableArray#, RealWorld)
50import GHC.IO (unsafePerformIO)
50import GHC.IO (unsafePerformIO)
51import Text.Read (readPrec)
51import Text.Read (readPrec)
52
52
53import Streamly.Internal.Data.Fold.Type (Fold(..))
53import Streamly.Internal.Data.Fold.Type (Fold(..))
54import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
54import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
55−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
56import Streamly.Internal.Data.Unfold.Type (Unfold(..))
55import Streamly.Internal.Data.Unfold.Type (Unfold(..))
57import Streamly.Internal.System.IO (unsafeInlineIO)
56import Streamly.Internal.System.IO (unsafeInlineIO)
58
57
@@ -65,7 +64,6 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
65import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
64import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
66import qualified Text.ParserCombinators.ReadPrec as ReadPrec
65import qualified Text.ParserCombinators.ReadPrec as ReadPrec
67
66
68−import Data.IORef
69import Prelude hiding (foldr, length, read)
67import Prelude hiding (foldr, length, read)
70
68
71-------------------------------------------------------------------------------
69-------------------------------------------------------------------------------
@@ -211,33 +209,13 @@ getIndexUnsafe i arr =
211
209
212{-# INLINE writeLastN #-}
210{-# INLINE writeLastN #-}
213writeLastN :: MonadIO m => Int -> Fold m a (Array a)
211writeLastN :: MonadIO m => Int -> Fold m a (Array a)
214−writeLastN n
212+writeLastN n = FL.rmapM f (RB.writeLastN n)
215− | n <= 0 = fmap (const nil) FL.drain
216− | otherwise = Fold step initial done
217
213
218 where
214 where
219
215
220− initial = do
216+ f rb = do
221− rb <- liftIO $ RB.createRing n
217+ arr <- RB.toMutArray 0 n rb
222− return $ FL.Partial $ Tuple' rb (0 :: Int)
223−
224− step (Tuple' rb cnt) x = do
225− liftIO $ RB.unsafeInsertRing rb x
226− return $ FL.Partial $ Tuple' rb (cnt + 1)
227−
228− done (Tuple' rb cnt) = do
229− let len = min cnt n
230− arr <- liftIO $ MArray.new len
231− arr1 <- MArray.uninit arr len
232− -- XXX Should use an API for retrieving the ring head
233− rhead <- liftIO $ readIORef $ RB.ringHead rb
234− if cnt <= n
235− then do
236− MArray.putSliceUnsafe (RB.ringArr rb) 0 arr1 0 cnt
237− else do
238− MArray.putSliceUnsafe (RB.ringArr rb) rhead arr1 0 (n - rhead)
239− MArray.putSliceUnsafe (RB.ringArr rb) 0 arr1 (n - rhead) rhead
240− return $ unsafeFreeze arr1
218+ return $ unsafeFreeze arr
241
219
242sliceUnsafe :: Int -> Int -> Array a -> Array a
220sliceUnsafe :: Int -> Int -> Array a -> Array a
243sliceUnsafe offset len (Array cont off1 _) = Array cont (off1 + offset) len
221sliceUnsafe offset len (Array cont off1 _) = Array cont (off1 + offset) len
core/src/Streamly/Internal/Data/Ring.hs+127−21
@@ -9,50 +9,156 @@
9
9
10module Streamly.Internal.Data.Ring
10module Streamly.Internal.Data.Ring
11 ( Ring(..)
11 ( Ring(..)
12+
13+ -- * Generation
12 , createRing
14 , createRing
13− , unsafeInsertRing
15+ , writeLastN
16+
17+ -- * Modification
18+ , seek
19+ , unsafeInsertRingWith
20+
21+ -- * Conversion
22+ , toMutArray
23+ , toStreamWith
14 ) where
24 ) where
15
25
26+#include "assert.hs"
27+
16−import Control.Monad (when)
28+import Control.Monad.IO.Class (liftIO, MonadIO)
17−import Data.IORef (newIORef, readIORef, writeIORef, IORef)
29+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
30+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
31+import Streamly.Internal.Data.Fold.Type (Fold(..))
18import Streamly.Internal.Data.Array.Generic.Mut.Type
32import Streamly.Internal.Data.Array.Generic.Mut.Type
19 ( MutArray(..)
33 ( MutArray(..)
20 , new
34 , new
21 , uninit
35 , uninit
22 , putIndexUnsafe
36 , putIndexUnsafe
37+ , putSliceUnsafe
23 )
38 )
39+-- import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
40+import qualified Streamly.Internal.Data.Fold.Type as Fold
24
41
42+-- XXX Use MutableArray rather than keeping a MutArray here.
25data Ring a = Ring
43data Ring a = Ring
26 { ringArr :: MutArray a
44 { ringArr :: MutArray a
27− -- XXX This could be an unboxed IO ref
45+ -- XXX We can keep the current fill amount, Or we can keep a count of total
28− -- If we want to rely on an external Int count using mod ringMax as index
46+ -- elements inserted and compute ring head as well using mod on that,
29− -- into the ring then we do not need to maintain this, assuming the counter
47+ -- assuming it won't overflow. But mod could be expensive.
30− -- would not overflow.
31− , ringHead :: IORef Int -- current index to be over-written
48+ , ringHead :: !Int -- current index to be over-written
32− , ringMax :: !Int -- first index beyond allocated memory
49+ , ringMax :: !Int -- first index beyond allocated memory
33 }
50 }
34
51
52+-------------------------------------------------------------------------------
53+-- Generation
54+-------------------------------------------------------------------------------
55+
35-- XXX If we align the ringMax to nearest power of two then computation of the
56-- XXX If we align the ringMax to nearest power of two then computation of the
36-- index to write could be cheaper.
57-- index to write could be cheaper.
37{-# INLINE createRing #-}
58{-# INLINE createRing #-}
38−createRing :: Int -> IO (Ring a)
59+createRing :: MonadIO m => Int -> m (Ring a)
39−createRing count = do
60+createRing count = liftIO $ do
40 arr <- new count
61 arr <- new count
41 arr1 <- uninit arr count
62 arr1 <- uninit arr count
42− ref <- newIORef 0
43 return (Ring
63 return (Ring
44 { ringArr = arr1
64 { ringArr = arr1
45− , ringHead = ref
65+ , ringHead = 0
46 , ringMax = count
66 , ringMax = count
47 })
67 })
48
68
69+
70+{-# INLINE writeLastN #-}
71+writeLastN :: MonadIO m => Int -> Fold m a (Ring a)
72+writeLastN n = Fold step initial extract
73+
74+ where
75+
76+ initial = do
77+ if n <= 0
78+ then Fold.Done <$> createRing 0
79+ else do
80+ rb <- createRing n
81+ return $ Fold.Partial $ Tuple' rb (0 :: Int)
82+
83+ step (Tuple' rb cnt) x = do
84+ rh1 <- liftIO $ unsafeInsertRingWith rb x
85+ return $ Fold.Partial $ Tuple' (rb {ringHead = rh1}) (cnt + 1)
86+
87+ extract (Tuple' rb@Ring{..} cnt) =
88+ return $
89+ if cnt < ringMax
90+ then Ring ringArr 0 ringHead
91+ else rb
92+
93+-------------------------------------------------------------------------------
94+-- Modification
95+-------------------------------------------------------------------------------
96+
49-- XXX This is safe
97-- XXX This is safe
50−{-# INLINE unsafeInsertRing #-}
98+-- Take the ring head and return the new ring head.
51−unsafeInsertRing :: Ring a -> a -> IO ()
99+{-# INLINE unsafeInsertRingWith #-}
52−unsafeInsertRing Ring{..} x = do
100+unsafeInsertRingWith :: Ring a -> a -> IO Int
101+unsafeInsertRingWith Ring{..} x = do
102+ assertM(ringMax >= 1)
103+ assertM(ringHead < ringMax)
104+ putIndexUnsafe ringHead ringArr x
105+ let rh1 = ringHead + 1
106+ next = if rh1 == ringMax then 0 else rh1
107+ return next
108+
109+-- | Move the ring head clockwise (+ve adj) or counter clockwise (-ve adj) by
110+-- the given amount.
111+{-# INLINE seek #-}
112+seek :: MonadIO m => Int -> Ring a -> m (Ring a)
113+seek adj rng@Ring{..}
114+ | ringMax > 0 = liftIO $ do
115+ -- XXX try avoiding mod when in bounds
116+ let idx1 = ringHead + adj
117+ next = mod idx1 ringMax
118+ return $ Ring ringArr next ringMax
119+ | otherwise = pure rng
120+
121+-------------------------------------------------------------------------------
122+-- Conversion
123+-------------------------------------------------------------------------------
124+
125+-- | @toMutArray rignHeadAdjustment lengthToRead ring@.
126+-- Convert the ring into a boxed mutable array. Note that the returned MutArray
127+-- may share the same underlying memory as the Ring.
128+{-# INLINE toMutArray #-}
129+toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)
130+toMutArray adj n Ring{..} = do
53− when (ringMax > 0) $ do
131+ let len = min ringMax n
132+ let idx = mod (ringHead + adj) ringMax
133+ end = idx + len
134+ if end <= ringMax
135+ then
136+ -- putSliceUnsafe ringArr idx arr1 0 len
137+ return $ ringArr { arrStart = idx, arrLen = len }
138+ else do
139+ -- XXX Just swap the elements in the existing ring and return the
140+ -- same array without reallocation.
141+ arr <- liftIO $ new len
142+ arr1 <- uninit arr len
143+ putSliceUnsafe ringArr idx arr1 0 (ringMax - idx)
144+ putSliceUnsafe ringArr 0 arr1 (ringMax - idx) (end - ringMax)
145+ return arr1
146+
147+-- This would be theoretically slower than toMutArray because of a branch
148+-- introduced for each element in the second half of the ring.
149+
150+-- | Seek by n and then read the entire ring. Use 'take' on the stream to
151+-- restrict the reads.
152+toStreamWith :: Int -> Ring a -> Stream m a
153+toStreamWith = undefined
154+{-
155+toStreamWith n Ring{..}
156+ | ringMax > 0 = concatEffect $ liftIO $ do
54 idx <- readIORef ringHead
157 idx <- readIORef ringHead
55− putIndexUnsafe idx ringArr x
56− let idx1 = idx + 1
158+ let idx1 = idx + adj
57− next = if idx1 == ringMax then 0 else idx1
159+ next = mod idx1 ringMax
58− writeIORef ringHead next
160+ s1 = undefined -- stream initial slice
161+ s2 = undefined -- stream next slice
162+ return (s1 `Stream.append` s2)
163+ | otherwise = Stream.nil
164+-}