on master help sign in
Add write and fromStream Data.Array apis and corresponding tests.

- Remove length field from Data.Array.

commitae2dce8db17074fc7e100c050ae3fcfa09a1b361 authorPranay Sashank <pranaysashank@gmail.com> dateTue 3 Dec 2019, 11:39 (7 years ago) parent175b0b6
3 files changed+170−91
src/Streamly/Data/Array.hs+13−8 src/Streamly/Internal/Data/Array.hs+99−17 test/Streamly/Test/Data/Array.hs+58−66
src/Streamly/Data/Array.hs+13−8
@@ -20,12 +20,16 @@ module Streamly.Data.Array
2020 ( Array
2121
2222 -- * Construction
23+ , A.fromListN
24+ , A.fromList
2325
2426 -- Stream Folds
2527 , fromStreamN
28+ , fromStream
2629
2730 -- MonadicAPIs
2831 , A.writeN
32+ , A.write
2933
3034 -- * Elimination
3135
@@ -34,11 +38,12 @@ module Streamly.Data.Array
3438 , read
3539
3640 -- * Random Access
37− , length
3841
3942 -- * Folding Arrays
4043 , streamFold
4144 , fold
45+
46+ , A.length
4247 )
4348where
4449
@@ -64,6 +69,10 @@ fromStreamN n m = do
6469 when (n < 0) $ error "fromStreamN: negative write count specified"
6570 A.fromStreamDN n $ D.toStreamD m
6671
72+{-# INLINE fromStream #-}
73+fromStream :: MonadIO m => SerialT m a -> m (Array a)
74+fromStream m = A.fromStreamD $ D.toStreamD m
75+
6776{-# INLINE_EARLY toStream #-}
6877toStream :: (Monad m, IsStream t) => Array a -> t m a
6978toStream = D.fromStreamD . A.toStreamD
@@ -72,10 +81,6 @@ toStream = D.fromStreamD . A.toStreamD
7281toStreamRev :: (Monad m, IsStream t) => Array a -> t m a
7382toStreamRev = D.fromStreamD . A.toStreamDRev
7483
75−{-# INLINE length #-}
76−length :: Array a -> Int
77−length Array {..} = aLen
78−
7984{-# INLINE fold #-}
8085fold :: forall m a b . Monad m => Fold m a b -> Array a -> m b
8186fold f arr = P.runFold f (toStream arr :: SerialT m a)
@@ -89,9 +94,9 @@ read :: Monad m => Unfold m (Array a) a
8994read = Unfold step inject
9095 where
9196 inject arr = return (arr, 0)
92− step (Array {..}, i)
93− | i == aLen = return D.Stop
97+ step (arr, i)
98+ | i == A.length arr = return D.Stop
9499 step (arr, (I# i)) =
95100 return $
96− case Exts.indexArray# (aA arr) i of
101+ case Exts.indexArray# (array# arr) i of
97102 (# x #) -> D.Yield x (arr, I# i + 1)
src/Streamly/Internal/Data/Array.hs+99−17
@@ -1,6 +1,5 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE MagicHash #-}
3−{-# LANGUAGE RecordWildCards #-}
43{-# LANGUAGE UnboxedTuples #-}
54
65#include "inline.hs"
@@ -20,18 +19,27 @@ module Streamly.Internal.Data.Array
2019 , foldl'
2120 , foldr
2221
22+ , length
23+
2324 , writeN
25+ , write
2426
2527 , toStreamD
2628 , toStreamDRev
29+
30+ , fromListN
31+ , fromList
2732 , fromStreamDN
33+ , fromStreamD
2834 )
2935where
3036
31−import Prelude hiding (foldr)
37+import Prelude hiding (foldr, length)
38+import Control.DeepSeq (NFData(..))
3239import Control.Monad.ST (stToIO, RealWorld)
3340import Control.Monad.IO.Class (liftIO, MonadIO)
3441import GHC.ST (ST(..))
42+import GHC.IO (unsafePerformIO)
3543import GHC.Base (Int(..))
3644import Data.Functor.Identity (runIdentity)
3745import qualified GHC.Exts as Exts
@@ -42,8 +50,7 @@ import qualified Streamly.Streams.StreamD as D
4250
4351data Array a =
4452 Array
45− { aLen :: {-# UNPACK #-} !Int -- Length of the array
46− , aA :: Exts.Array# a
53+ { array# :: Exts.Array# a
4754 }
4855
4956{-# NOINLINE bottomElement #-}
@@ -75,7 +82,30 @@ freezeArray :: MutableArray s a -> Int -> Int -> ST s (Array a)
7582freezeArray marr (I# start#) (I# len#) =
7683 ST (\s# ->
7784 case Exts.freezeArray# (marray# marr) start# len# s# of
78− (# s'#, arr# #) -> (# s'#, Array (I# len#) arr# #))
85+ (# s'#, arr# #) -> (# s'#, Array arr# #))
86+
87+{-# INLINE copyMutableArray #-}
88+copyMutableArray :: MutableArray s a
89+ -- ^ The source array
90+ -> Int
91+ -- ^ Offset into the source array
92+ -> MutableArray s a
93+ -- ^ Destination array to copy to
94+ -> Int
95+ -- ^ Offset into the destination array
96+ -> Int
97+ -- ^ Number of elements to copy
98+ -> ST s ()
99+copyMutableArray sourceMarr (I# sourceOffset#) destMarr (I# destOffset#) (I# numElements#) =
100+ ST (\s# ->
101+ case Exts.copyMutableArray#
102+ (marray# sourceMarr)
103+ sourceOffset#
104+ (marray# destMarr)
105+ destOffset#
106+ numElements#
107+ s# of
108+ s'# -> (# s'#, () #))
79109
80110{-# INLINE newArrayIO #-}
81111newArrayIO :: Int -> a -> IO (MutableArray RealWorld a)
@@ -89,28 +119,44 @@ writeArrayIO marr i x = stToIO $ writeArray marr i x
89119freezeArrayIO :: MutableArray RealWorld a -> Int -> Int -> IO (Array a)
90120freezeArrayIO marr i len = stToIO $ freezeArray marr i len
91121
122+{-# INLINE length #-}
123+length :: Array a -> Int
124+length arr = I# (Exts.sizeofArray# (array# arr))
125+
126+{-# INLINE copyMutableArrayIO #-}
127+copyMutableArrayIO ::
128+ MutableArray RealWorld a
129+ -> Int
130+ -> MutableArray RealWorld a
131+ -> Int
132+ -> Int
133+ -> IO ()
134+copyMutableArrayIO sourceMarr sourceOffset destMarr destOffset numElements =
135+ stToIO $
136+ copyMutableArray sourceMarr sourceOffset destMarr destOffset numElements
137+
92138{-# INLINE_NORMAL toStreamD #-}
93139toStreamD :: Monad m => Array a -> D.Stream m a
94−toStreamD Array {..} = D.Stream step 0
140+toStreamD arr = D.Stream step 0
95141 where
96142 {-# INLINE_LATE step #-}
97143 step _ i
98− | i == aLen = return D.Stop
144+ | i == length arr = return D.Stop
99145 step _ (I# i) =
100146 return $
101− case Exts.indexArray# aA i of
147+ case Exts.indexArray# (array# arr) i of
102148 (# x #) -> D.Yield x ((I# i) + 1)
103149
104150{-# INLINE_NORMAL toStreamDRev #-}
105151toStreamDRev :: Monad m => Array a -> D.Stream m a
106−toStreamDRev Array {..} = D.Stream step (aLen - 1)
152+toStreamDRev arr = D.Stream step (length arr - 1)
107153 where
108154 {-# INLINE_LATE step #-}
109155 step _ i
110156 | i < 0 = return D.Stop
111157 step _ (I# i) =
112158 return $
113− case Exts.indexArray# aA i of
159+ case Exts.indexArray# (array# arr) i of
114160 (# x #) -> D.Yield x ((I# i) - 1)
115161
116162{-# INLINE_NORMAL foldl' #-}
@@ -127,13 +173,33 @@ writeN :: MonadIO m => Int -> Fold m a (Array a)
127173writeN limit = Fold step initial extract
128174 where
129175 initial = do
130− marr <- liftIO $ newArrayIO limit bottomElement
131− return (marr, 0)
132− step (marr,i) x | i == limit = return (marr, i)
133− | otherwise = do
134− liftIO $ writeArrayIO marr i x
135− return (marr, i+1)
136− extract (marr,len) = liftIO $ freezeArrayIO marr 0 len
176+ marr <- liftIO $ newArrayIO limit bottomElement
177+ return (marr, 0)
178+ step (marr, i) x
179+ | i == limit = return (marr, i)
180+ | otherwise = do
181+ liftIO $ writeArrayIO marr i x
182+ return (marr, i + 1)
183+ extract (marr, len) = liftIO $ freezeArrayIO marr 0 len
184+
185+{-# INLINE_NORMAL write #-}
186+write :: MonadIO m => Fold m a (Array a)
187+write = Fold step initial extract
188+ where
189+ initial = do
190+ marr <- liftIO $ newArrayIO 0 bottomElement
191+ return (marr, 0, 0)
192+ step (marr, i, capacity) x
193+ | i == capacity =
194+ let newCapacity = max (capacity * 2) 1
195+ in do newMarr <- liftIO $ newArrayIO newCapacity bottomElement
196+ liftIO $ copyMutableArrayIO marr 0 newMarr 0 i
197+ liftIO $ writeArrayIO newMarr i x
198+ return (newMarr, i + 1, newCapacity)
199+ | otherwise = do
200+ liftIO $ writeArrayIO marr i x
201+ return (marr, i + 1, capacity)
202+ extract (marr, len, _) = liftIO $ freezeArrayIO marr 0 len
137203
138204{-# INLINE_NORMAL fromStreamDN #-}
139205fromStreamDN :: MonadIO m => Int -> D.Stream m a -> m (Array a)
@@ -145,3 +211,19 @@ fromStreamDN limit str = do
145211 0 $
146212 D.take limit str
147213 liftIO $ freezeArrayIO marr 0 i
214+
215+{-# INLINE fromStreamD #-}
216+fromStreamD :: MonadIO m => D.Stream m a -> m (Array a)
217+fromStreamD str = D.runFold write str
218+
219+{-# INLINABLE fromListN #-}
220+fromListN :: Int -> [a] -> Array a
221+fromListN n xs = unsafePerformIO $ fromStreamDN n $ D.fromList xs
222+
223+{-# INLINABLE fromList #-}
224+fromList :: [a] -> Array a
225+fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
226+
227+instance NFData a => NFData (Array a) where
228+ {-# INLINE rnf #-}
229+ rnf = foldl' (\_ x -> rnf x) ()
test/Streamly/Test/Data/Array.hs+58−66
@@ -15,10 +15,13 @@ import Foreign.Storable (Storable(..))
1515
1616import Test.Hspec.QuickCheck
1717import Test.QuickCheck (Property, forAll, Gen, vectorOf, arbitrary, choose)
18−import Test.QuickCheck.Monadic (monadicIO, assert)
18+import Test.QuickCheck.Monadic (monadicIO, assert, run)
1919
2020import Test.Hspec as H
2121
22+import Streamly (SerialT)
23+import Streamly.Data.Array (Array)
24+
2225import qualified Streamly.Data.Array as A
2326import qualified Streamly.Prelude as S
2427
@@ -44,78 +47,64 @@ defaultChunkSize = 32 * k - allocOverhead
4447maxArrLen :: Int
4548maxArrLen = defaultChunkSize * 8
4649
47−testLength :: Property
48−testLength =
50+genericTestFrom ::
51+ (Int -> SerialT IO Int -> IO (Array Int))
52+ -> Property
53+genericTestFrom arrFold =
4954 forAll (choose (0, maxArrLen)) $ \len ->
5055 forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
5156 monadicIO $ do
52− arr <- S.fold (A.writeN len)
53− $ S.fromList list
57+ arr <- run $ arrFold len $ S.fromList list
5458 assert (A.length arr == len)
5559
60+testLength :: Property
61+testLength = genericTestFrom (\n -> S.fold (A.writeN n))
62+
5663testLengthFromStreamN :: Property
57−testLengthFromStreamN =
58− forAll (choose (0, maxArrLen)) $ \len ->
59− forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
60− monadicIO $ do
61− arr <- A.fromStreamN len
62− $ S.fromList list
63− assert (A.length arr == len)
64+testLengthFromStreamN = genericTestFrom A.fromStreamN
6465
65−testFoldUnfold :: Property
66−testFoldUnfold =
67− forAll (choose (0, maxArrLen)) $ \len ->
68− forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
69− monadicIO $ do
70− arr <- S.fold (A.writeN len)
71− $ S.fromList list
72− xs <- S.toList
73− $ S.unfold A.read arr
74− assert (xs == list)
75−
76−testFoldToStream :: Property
77−testFoldToStream =
78− forAll (choose (0, maxArrLen)) $ \len ->
79− forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
80− monadicIO $ do
81− arr <- S.fold (A.writeN len)
82− $ S.fromList list
83− xs <- S.toList
84− $ A.toStream arr
85− assert (xs == list)
86−
87−testFoldToStreamRev :: Property
88−testFoldToStreamRev =
89− forAll (choose (0, maxArrLen)) $ \len ->
90− forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
91− monadicIO $ do
92− arr <- S.fold (A.writeN len)
93− $ S.fromList list
94− xs <- S.toList
95− $ A.toStreamRev arr
96− assert (xs == reverse list)
97−
98−testFromStreamUnfold :: Property
99−testFromStreamUnfold =
66+testLengthFromStream :: Property
67+testLengthFromStream = genericTestFrom (const A.fromStream)
68+
69+genericTestFromTo ::
70+ (Int -> SerialT IO Int -> IO (Array Int))
71+ -> (Array Int -> SerialT IO Int)
72+ -> ([Int] -> [Int] -> Bool)
73+ -> Property
74+genericTestFromTo arrFold arrUnfold listEq =
10075 forAll (choose (0, maxArrLen)) $ \len ->
10176 forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
10277 monadicIO $ do
103− arr <- A.fromStreamN len
104− $ S.fromList list
105− xs <- S.toList
106− $ S.unfold A.read arr
107− assert (xs == list)
78+ arr <- run $ arrFold len $ S.fromList list
79+ xs <- run $ S.toList $ arrUnfold arr
80+ assert (listEq xs list)
81+
82+testFoldNUnfold :: Property
83+testFoldNUnfold =
84+ genericTestFromTo (\n -> S.fold (A.writeN n)) (S.unfold A.read) (==)
85+
86+testFoldNToStream :: Property
87+testFoldNToStream =
88+ genericTestFromTo (\n -> S.fold (A.writeN n)) A.toStream (==)
89+
90+testFoldNToStreamRev :: Property
91+testFoldNToStreamRev =
92+ genericTestFromTo
93+ (\n -> S.fold (A.writeN n))
94+ A.toStreamRev
95+ (\xs list -> xs == reverse list)
96+
97+testFromStreamNUnfold :: Property
98+testFromStreamNUnfold = genericTestFromTo A.fromStreamN (S.unfold A.read) (==)
99+
100+testFromStreamNToStream :: Property
101+testFromStreamNToStream = genericTestFromTo A.fromStreamN A.toStream (==)
108102
109103testFromStreamToStream :: Property
110−testFromStreamToStream =
111− forAll (choose (0, maxArrLen)) $ \len ->
112− forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
113− monadicIO $ do
114− arr <- A.fromStreamN len
115− $ S.fromList list
116− xs <- S.toList
117− $ A.toStream arr
118− assert (xs == list)
104+testFromStreamToStream = genericTestFromTo (const A.fromStream) A.toStream (==)
105+
106+testFoldUnfold :: Property
107+testFoldUnfold = genericTestFromTo (const (S.fold A.write)) (S.unfold A.read) (==)
119108
120109main :: IO ()
121110main =
@@ -125,8 +114,11 @@ main =
125114 describe "Construction" $ do
126115 prop "length . writeN n === n" testLength
127116 prop "length . fromStreamN n === n" testLengthFromStreamN
128− prop "read . writeN === id " testFoldUnfold
129− prop "toStream . writeN === id" testFoldToStream
130− prop "toStreamRev . writeN === reverse" testFoldToStreamRev
131− prop "read . fromStreamN === id" testFromStreamUnfold
132− prop "toStream . fromStreamN === reverse" testFromStreamToStream
117+ prop "length . fromStream === n" testLengthFromStream
118+ prop "read . writeN === id " testFoldNUnfold
119+ prop "toStream . writeN === id" testFoldNToStream
120+ prop "toStreamRev . writeN === reverse" testFoldNToStreamRev
121+ prop "read . fromStreamN === id" testFromStreamNUnfold
122+ prop "toStream . fromStreamN === id" testFromStreamNToStream
123+ prop "toStream . fromStream === id" testFromStreamToStream
124+ prop "read . write === id" testFoldUnfold