on master help sign in
implement fromStreamDArraysOf more efficiently
dbaaceaHarendra Kumar
benchmark/FileIO.hs+12−6
@@ -135,14 +135,14 @@ main = do
135 FH.write outh (FH.read inh)
135 FH.write outh (FH.read inh)
136 ]
136 ]
137 -- This needs an ascii file, as decode just errors out.
137 -- This needs an ascii file, as decode just errors out.
138− , bgroup "decode/encode"
138+ , bgroup "decode-encode"
139− [ mkBench "encodeChar8/decodeChar8" href $ do
139+ [ mkBench "encodeChar8-decodeChar8" href $ do
140 Handles inh outh <- readIORef href
140 Handles inh outh <- readIORef href
141 FH.write outh
141 FH.write outh
142 $ SS.encodeChar8
142 $ SS.encodeChar8
143 $ SS.decodeChar8
143 $ SS.decodeChar8
144 $ FH.read inh
144 $ FH.read inh
145− , mkBench "encodeUtf8/decodeUtf8" href $ do
145+ , mkBench "encodeUtf8-decodeUtf8" href $ do
146 Handles inh outh <- readIORef href
146 Handles inh outh <- readIORef href
147 FH.write outh
147 FH.write outh
148 $ SS.encodeUtf8
148 $ SS.encodeUtf8
@@ -165,8 +165,8 @@ main = do
165 Handles inh _ <- readIORef href
165 Handles inh _ <- readIORef href
166 S.length $ FL.chunksOf 1000 FL.drain (FH.read inh)
166 S.length $ FL.chunksOf 1000 FL.drain (FH.read inh)
167 ]
167 ]
168− , bgroup "group/ungroup"
168+ , bgroup "group-ungroup"
169− [ mkBench "lines/unlines" href $ do
169+ [ mkBench "lines-unlines" href $ do
170 Handles inh outh <- readIORef href
170 Handles inh outh <- readIORef href
171 FH.write outh
171 FH.write outh
172 $ SS.encodeChar8
172 $ SS.encodeChar8
@@ -174,7 +174,13 @@ main = do
174 $ SS.lines
174 $ SS.lines
175 $ SS.decodeChar8
175 $ SS.decodeChar8
176 $ FH.read inh
176 $ FH.read inh
177+ , mkBench "lines-unlines-arrays" href $ do
178+ Handles inh outh <- readIORef href
179+ FH.writeArraysPackedUpto (1024*1024) outh
180+ $ S.insertAfterEach (return $ A.fromList [10])
181+ $ A.splitArraysOn 10
182+ $ FH.readArraysOfUpto (1024*1024) inh
177− , mkBench "words/unwords" href $ do
183+ , mkBench "words-unwords" href $ do
178 Handles inh outh <- readIORef href
184 Handles inh outh <- readIORef href
179 FH.write outh
185 FH.write outh
180 $ SS.encodeChar8
186 $ SS.encodeChar8
examples/FileIO.hs+3−3
@@ -7,13 +7,13 @@ import Data.Char (ord)
7import System.Environment (getArgs)
7import System.Environment (getArgs)
8
8
9cat :: FilePath -> IO ()
9cat :: FilePath -> IO ()
10−cat src = File.writeArrays "/dev/stdout" $ File.readArraysUpto (256*1024) src
10+cat src = File.writeArrays "/dev/stdout" $ File.readArraysOfUpto (256*1024) src
11
11
12cp :: FilePath -> FilePath -> IO ()
12cp :: FilePath -> FilePath -> IO ()
13−cp src dst = File.writeArrays dst $ File.readArraysUpto (256*1024) src
13+cp src dst = File.writeArrays dst $ File.readArraysOfUpto (256*1024) src
14
14
15append :: FilePath -> FilePath -> IO ()
15append :: FilePath -> FilePath -> IO ()
16−append src dst = File.appendArrays dst $ File.readArraysUpto (256*1024) src
16+append src dst = File.appendArrays dst $ File.readArraysOfUpto (256*1024) src
17
17
18ord' :: Num a => Char -> a
18ord' :: Num a => Char -> a
19ord' = (fromIntegral . ord)
19ord' = (fromIntegral . ord)
examples/HandleIO.hs+9
@@ -5,6 +5,7 @@ import qualified Streamly.FileSystem.Handle as FH
5import qualified System.IO as FH
5import qualified System.IO as FH
6-- import qualified Streamly.FileSystem.FD as FH
6-- import qualified Streamly.FileSystem.FD as FH
7import qualified Streamly.String as SS
7import qualified Streamly.String as SS
8+import Data.Word (Word8)
8
9
9import Data.Char (ord)
10import Data.Char (ord)
10import System.Environment (getArgs)
11import System.Environment (getArgs)
@@ -13,6 +14,7 @@ import System.IO (IOMode(..), hSeek, SeekMode(..))
13cat :: FH.Handle -> IO ()
14cat :: FH.Handle -> IO ()
14cat src = FH.writeArrays FH.stdout $ FH.readArraysOfUpto (256*1024) src
15cat src = FH.writeArrays FH.stdout $ FH.readArraysOfUpto (256*1024) src
15{-
16{-
17+-- byte stream version
16cat src =
18cat src =
17 FH.writeByChunksOf (1024*1024) FH.stdout
19 FH.writeByChunksOf (1024*1024) FH.stdout
18 $ FH.readByChunksUpto (16*1024) src
20 $ FH.readByChunksUpto (16*1024) src
@@ -21,6 +23,7 @@ cat src =
21cp :: FH.Handle -> FH.Handle -> IO ()
23cp :: FH.Handle -> FH.Handle -> IO ()
22cp src dst = FH.writeArrays dst $ FH.readArraysOfUpto (256*1024) src
24cp src dst = FH.writeArrays dst $ FH.readArraysOfUpto (256*1024) src
23{-
25{-
26+-- byte stream version
24cp src dst =
27cp src dst =
25 FH.writeByChunksOf (1024*1024) dst
28 FH.writeByChunksOf (1024*1024) dst
26 $ FH.readByChunksUpto (16*1024) src
29 $ FH.readByChunksUpto (16*1024) src
@@ -30,10 +33,16 @@ ord' :: Num a => Char -> a
30ord' = (fromIntegral . ord)
33ord' = (fromIntegral . ord)
31
34
32wcl :: FH.Handle -> IO ()
35wcl :: FH.Handle -> IO ()
36+wcl src = print =<< (S.length
37+ $ A.splitArraysOn 10
38+ $ FH.readArrays src)
39+{-
40+-- Char stream version
33wcl src = print =<< (S.length
41wcl src = print =<< (S.length
34 $ flip SS.foldLines FL.drain
42 $ flip SS.foldLines FL.drain
35 $ SS.decodeChar8
43 $ SS.decodeChar8
36 $ FH.read src)
44 $ FH.read src)
45+-}
37
46
38grepc :: String -> FH.Handle -> IO ()
47grepc :: String -> FH.Handle -> IO ()
39grepc pat src = print . (subtract 1) =<< (S.length
48grepc pat src = print . (subtract 1) =<< (S.length
src/Streamly/FileSystem/FD.hs+1−1
@@ -358,7 +358,7 @@ writev h m = S.mapM_ (liftIO . writeIOVec h) m
358{-# INLINE writeArraysOfUpto #-}
358{-# INLINE writeArraysOfUpto #-}
359writeArraysOfUpto :: (MonadIO m, Storable a)
359writeArraysOfUpto :: (MonadIO m, Storable a)
360 => Int -> Handle -> SerialT m (Array a) -> m ()
360 => Int -> Handle -> SerialT m (Array a) -> m ()
361−writeArraysOfUpto n h xs = writeArrays h $ A.coalesceChunksOf n xs
361+writeArraysOfUpto n h xs = writeArrays h $ A.packArraysChunksOf n xs
362
362
363-- | Write a stream of arrays to a handle after grouping them in 'IOVec' arrays
363-- | Write a stream of arrays to a handle after grouping them in 'IOVec' arrays
364-- of up to a maximum total size. Writes are performed using gather IO via
364-- of up to a maximum total size. Writes are performed using gather IO via
src/Streamly/FileSystem/Handle.hs+16−4
@@ -73,6 +73,7 @@ module Streamly.FileSystem.Handle
73 -- -- * Array Write
73 -- -- * Array Write
74 , writeArray
74 , writeArray
75 , writeArrays
75 , writeArrays
76+ , writeArraysPackedUpto
76
77
77 -- -- * Random Access (Seek)
78 -- -- * Random Access (Seek)
78 -- -- | Unlike the streaming APIs listed above, these APIs apply to devices or
79 -- -- | Unlike the streaming APIs listed above, these APIs apply to devices or
@@ -108,11 +109,11 @@ import Prelude hiding (read)
108import Streamly.Mem.Array.Types (Array(..))
109import Streamly.Mem.Array.Types (Array(..))
109import Streamly.Streams.Serial (SerialT)
110import Streamly.Streams.Serial (SerialT)
110import Streamly.Streams.StreamK.Type (IsStream, mkStream)
111import Streamly.Streams.StreamK.Type (IsStream, mkStream)
112+import Streamly.Mem.Array.Types (defaultChunkSize, shrinkToFit)
111-- import Streamly.Fold (Fold)
113-- import Streamly.Fold (Fold)
112-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
114-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
113
115
114import qualified Streamly.Mem.Array as A
116import qualified Streamly.Mem.Array as A
115−import qualified Streamly.Mem.Array.Types as A hiding (flattenArrays)
116import qualified Streamly.Prelude as S
117import qualified Streamly.Prelude as S
117import qualified Streamly.Streams.StreamD.Type as D
118import qualified Streamly.Streams.StreamD.Type as D
118
119
@@ -150,7 +151,7 @@ readArrayUpto size h = do
150 , aBound = p `plusPtr` size
151 , aBound = p `plusPtr` size
151 }
152 }
152 -- XXX shrink only if the diff is significant
153 -- XXX shrink only if the diff is significant
153− A.shrinkToFit v
154+ shrinkToFit v
154
155
155-------------------------------------------------------------------------------
156-------------------------------------------------------------------------------
156-- Array IO (output)
157-- Array IO (output)
@@ -211,7 +212,7 @@ readArraysOfUpto size h = D.fromStreamD (D.Stream step ())
211-- @since 0.7.0
212-- @since 0.7.0
212{-# INLINE readArrays #-}
213{-# INLINE readArrays #-}
213readArrays :: (IsStream t, MonadIO m) => Handle -> t m (Array Word8)
214readArrays :: (IsStream t, MonadIO m) => Handle -> t m (Array Word8)
214−readArrays = readArraysOfUpto A.defaultChunkSize
215+readArrays = readArraysOfUpto defaultChunkSize
215
216
216-------------------------------------------------------------------------------
217-------------------------------------------------------------------------------
217-- Read File to Stream
218-- Read File to Stream
@@ -252,6 +253,17 @@ read = A.flattenArrays . readArrays
252writeArrays :: (MonadIO m, Storable a) => Handle -> SerialT m (Array a) -> m ()
253writeArrays :: (MonadIO m, Storable a) => Handle -> SerialT m (Array a) -> m ()
253writeArrays h m = S.mapM_ (liftIO . writeArray h) m
254writeArrays h m = S.mapM_ (liftIO . writeArray h) m
254
255
256+-- | Write a stream of arrays to a handle after coalescing them in chunks of
257+-- specified size. The chunk size is only a maximum and the actual writes could
258+-- be smaller than that as we do not split the arrays to fit them to the
259+-- specified size.
260+--
261+-- @since 0.7.0
262+{-# INLINE writeArraysPackedUpto #-}
263+writeArraysPackedUpto :: (MonadIO m, Storable a)
264+ => Int -> Handle -> SerialT m (Array a) -> m ()
265+writeArraysPackedUpto n h xs = writeArrays h $ A.packArraysChunksOf n xs
266+
255-- GHC buffer size dEFAULT_FD_BUFFER_SIZE=8192 bytes.
267-- GHC buffer size dEFAULT_FD_BUFFER_SIZE=8192 bytes.
256--
268--
257-- XXX test this
269-- XXX test this
@@ -278,7 +290,7 @@ writeByChunksOf n h m = writeArrays h $ A.arraysOf n m
278-- @since 0.7.0
290-- @since 0.7.0
279{-# INLINE write #-}
291{-# INLINE write #-}
280write :: MonadIO m => Handle -> SerialT m Word8 -> m ()
292write :: MonadIO m => Handle -> SerialT m Word8 -> m ()
281−write = writeByChunksOf A.defaultChunkSize
293+write = writeByChunksOf defaultChunkSize
282
294
283{-
295{-
284{-# INLINE write #-}
296{-# INLINE write #-}
src/Streamly/Mem/Array.hs+9−8
@@ -115,8 +115,8 @@ module Streamly.Mem.Array
115 -- Streams of arrays
115 -- Streams of arrays
116 , flattenArrays
116 , flattenArrays
117 -- , flattenArraysRev
117 -- , flattenArraysRev
118− , coalesceArrays
118+ , packArrays
119− , coalesceChunksOf
119+ , packArraysChunksOf
120 , unlinesArraysBy
120 , unlinesArraysBy
121 , splitArraysOn
121 , splitArraysOn
122
122
@@ -500,10 +500,11 @@ splitArraysOn byte s = D.fromStreamD $ A.splitOn byte $ D.toStreamD s
500-- maximum specified size.
500-- maximum specified size.
501--
501--
502-- @since 0.7.0
502-- @since 0.7.0
503−{-# INLINE coalesceChunksOf #-}
503+{-# INLINE packArraysChunksOf #-}
504−coalesceChunksOf :: (MonadIO m, Storable a)
504+packArraysChunksOf :: (MonadIO m, Storable a)
505 => Int -> SerialT m (Array a) -> SerialT m (Array a)
505 => Int -> SerialT m (Array a) -> SerialT m (Array a)
506+packArraysChunksOf n xs =
506−coalesceChunksOf n xs = D.fromStreamD $ A.coalesceChunksOf n (D.toStreamD xs)
507+ D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
507
508
508-- |
509-- |
509-- > arraysOf n = FL.groupsOf n (FL.toArrayN n)
510-- > arraysOf n = FL.groupsOf n (FL.toArrayN n)
@@ -560,9 +561,9 @@ spliceArraysRealloced s = do
560-- array. The stream must be /finite/.
561-- array. The stream must be /finite/.
561--
562--
562-- @since 0.7.0
563-- @since 0.7.0
563−{-# INLINE coalesceArrays #-}
564+{-# INLINE packArrays #-}
564−coalesceArrays :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
565+packArrays :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
565−coalesceArrays = spliceArraysRealloced
566+packArrays = spliceArraysRealloced
566-- spliceArrays = _spliceArraysBuffered
567-- spliceArrays = _spliceArraysBuffered
567
568
568-- | Create an 'Array' from a stream. This is useful when we want to create a
569-- | Create an 'Array' from a stream. This is useful when we want to create a
src/Streamly/Mem/Array/Types.hs+58−7
@@ -38,7 +38,7 @@ module Streamly.Mem.Array.Types
38 , fromStreamDArraysOf
38 , fromStreamDArraysOf
39 , flattenArrays
39 , flattenArrays
40 , flattenArraysRev
40 , flattenArraysRev
41− , coalesceChunksOf
41+ , packArraysChunksOf
42 , groupIOVecsOf
42 , groupIOVecsOf
43 , splitOn
43 , splitOn
44
44
@@ -485,12 +485,49 @@ fromStreamDN limit str = do
485 liftIO $ poke ptr x
485 liftIO $ poke ptr x
486 return $ ptr `plusPtr` sizeOf (undefined :: a)
486 return $ ptr `plusPtr` sizeOf (undefined :: a)
487
487
488+data GroupState s start end bound
489+ = GroupStart s
490+ | GroupBuffer s start end bound
491+ | GroupYield start end bound (GroupState s start end bound)
492+ | GroupFinish
493+
488-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
494-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
489-- arrays of size n.
495-- arrays of size n.
490−{-# INLINE fromStreamDArraysOf #-}
496+{-# INLINE_NORMAL fromStreamDArraysOf #-}
491fromStreamDArraysOf :: (MonadIO m, Storable a)
497fromStreamDArraysOf :: (MonadIO m, Storable a)
492 => Int -> D.Stream m a -> D.Stream m (Array a)
498 => Int -> D.Stream m a -> D.Stream m (Array a)
493−fromStreamDArraysOf n str = D.groupsOf n (toArrayN n) str
499+-- fromStreamDArraysOf n str = D.groupsOf n (toArrayN n) str
500+fromStreamDArraysOf n (D.Stream step state) =
501+ D.Stream step' (GroupStart state)
502+
503+ where
504+
505+ {-# INLINE_LATE step' #-}
506+ step' _ (GroupStart st) = do
507+ when (n <= 0) $
508+ -- XXX we can pass the module string from the higher level API
509+ error $ "Streamly.Mem.Array.Types.fromStreamDArraysOf: the size of "
510+ ++ "arrays [" ++ show n ++ "] must be a natural number"
511+ Array start end bound <- liftIO $ newArray n
512+ return $ D.Skip (GroupBuffer st start end bound)
513+
514+ step' gst (GroupBuffer st start end bound) = do
515+ r <- step (adaptState gst) st
516+ case r of
517+ D.Yield x s -> do
518+ liftIO $ poke end x
519+ let end' = end `plusPtr` 1
520+ return $
521+ if end' >= bound
522+ then D.Skip (GroupYield start end' bound (GroupStart s))
523+ else D.Skip (GroupBuffer s start end' bound)
524+ D.Skip s -> return $ D.Skip (GroupBuffer s start end bound)
525+ D.Stop -> return $ D.Skip (GroupYield start end bound GroupFinish)
526+
527+ step' _ (GroupYield start end bound next) =
528+ return $ D.Yield (Array start end bound) next
529+
530+ step' _ GroupFinish = return D.Stop
494
531
495-- XXX concatMap does not seem to have the best possible performance so we have
532-- XXX concatMap does not seem to have the best possible performance so we have
496-- a custom way to concat arrays.
533-- a custom way to concat arrays.
@@ -833,15 +870,20 @@ data SpliceState s arr
833-- maximum specified size.
870-- maximum specified size.
834--
871--
835-- @since 0.7.0
872-- @since 0.7.0
836−{-# INLINE_NORMAL coalesceChunksOf #-}
873+{-# INLINE_NORMAL packArraysChunksOf #-}
837−coalesceChunksOf :: (MonadIO m, Storable a)
874+packArraysChunksOf :: (MonadIO m, Storable a)
838 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
875 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
876+packArraysChunksOf n (D.Stream step state) =
839−coalesceChunksOf n (D.Stream step state) = D.Stream step' (SpliceInitial state)
877+ D.Stream step' (SpliceInitial state)
840
878
841 where
879 where
842
880
843 {-# INLINE_LATE step' #-}
881 {-# INLINE_LATE step' #-}
844 step' gst (SpliceInitial st) = do
882 step' gst (SpliceInitial st) = do
883+ when (n <= 0) $
884+ -- XXX we can pass the module string from the higher level API
885+ error $ "Streamly.Mem.Array.Types.packArraysChunksOf: the size of "
886+ ++ "arrays [" ++ show n ++ "] must be a natural number"
845 r <- step gst st
887 r <- step gst st
846 case r of
888 case r of
847 D.Yield arr s -> return $
889 D.Yield arr s -> return $
@@ -858,7 +900,8 @@ coalesceChunksOf n (D.Stream step state) = D.Stream step' (SpliceInitial state)
858 D.Yield arr s -> do
900 D.Yield arr s -> do
859 let len = byteLength buf + byteLength arr
901 let len = byteLength buf + byteLength arr
860 if len > n
902 if len > n
903+ then return $
861− then return $ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
904+ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
862 else do
905 else do
863 buf' <- if byteCapacity buf < n
906 buf' <- if byteCapacity buf < n
864 then liftIO $ realloc n buf
907 then liftIO $ realloc n buf
@@ -893,6 +936,14 @@ groupIOVecsOf n maxIOVLen (D.Stream step state) =
893
936
894 {-# INLINE_LATE step' #-}
937 {-# INLINE_LATE step' #-}
895 step' gst (GatherInitial st) = do
938 step' gst (GatherInitial st) = do
939+ when (n <= 0) $
940+ -- XXX we can pass the module string from the higher level API
941+ error $ "Streamly.Mem.Array.Types.groupIOVecsOf: the size of "
942+ ++ "groups [" ++ show n ++ "] must be a natural number"
943+ when (maxIOVLen <= 0) $
944+ -- XXX we can pass the module string from the higher level API
945+ error $ "Streamly.Mem.Array.Types.groupIOVecsOf: the number of "
946+ ++ "IOVec entries [" ++ show n ++ "] must be a natural number"
896 r <- step (adaptState gst) st
947 r <- step (adaptState gst) st
897 case r of
948 case r of
898 D.Yield arr s -> do
949 D.Yield arr s -> do
src/Streamly/Prelude.hs+5
@@ -370,6 +370,7 @@ module Streamly.Prelude
370 , insertBy
370 , insertBy
371 , intersperseM
371 , intersperseM
372 , intersperse
372 , intersperse
373+ , insertAfterEach
373 -- , intersperseBySpan
374 -- , intersperseBySpan
374 , intersperseByTime
375 , intersperseByTime
375
376
@@ -1949,6 +1950,10 @@ intersperseM m = fromStreamS . S.intersperseM m . toStreamS
1949intersperse :: (IsStream t, MonadAsync m) => a -> t m a -> t m a
1950intersperse :: (IsStream t, MonadAsync m) => a -> t m a -> t m a
1950intersperse a = fromStreamS . S.intersperse a . toStreamS
1951intersperse a = fromStreamS . S.intersperse a . toStreamS
1951
1952
1953+{-# INLINE insertAfterEach #-}
1954+insertAfterEach :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1955+insertAfterEach m = fromStreamS . S.insertAfterEach m . toStreamS
1956+
1952{-
1957{-
1953-- | Intersperse a monadic action into the input stream after every @n@
1958-- | Intersperse a monadic action into the input stream after every @n@
1954-- elements.
1959-- elements.
src/Streamly/Streams/StreamD.hs+23
@@ -205,6 +205,7 @@ module Streamly.Streams.StreamD
205 -- * Inserting
205 -- * Inserting
206 , intersperseM
206 , intersperseM
207 , intersperse
207 , intersperse
208+ , insertAfterEach
208 , insertBy
209 , insertBy
209
210
210 -- * Deleting
211 -- * Deleting
@@ -2204,6 +2205,28 @@ intersperseM m (Stream step state) = Stream step' (FirstYield state)
2204
2205
2205 step' _ (YieldAndCarry x st) = return $ Yield x (InterspersingYield st)
2206 step' _ (YieldAndCarry x st) = return $ Yield x (InterspersingYield st)
2206
2207
2208+data SuffixState s a
2209+ = SuffixElem s
2210+ | SuffixSuffix s
2211+ | SuffixYield a (SuffixState s a)
2212+
2213+{-# INLINE_NORMAL insertAfterEach #-}
2214+insertAfterEach :: forall m a. Monad m => m a -> Stream m a -> Stream m a
2215+insertAfterEach action (Stream step state) = Stream step' (SuffixElem state)
2216+ where
2217+ {-# INLINE_LATE step' #-}
2218+ step' gst (SuffixElem st) = do
2219+ r <- step gst st
2220+ return $ case r of
2221+ Yield x s -> Skip (SuffixYield x (SuffixSuffix s))
2222+ Skip s -> Skip (SuffixElem s)
2223+ Stop -> Stop
2224+
2225+ step' _ (SuffixSuffix st) = do
2226+ action >>= \r -> return $ Skip (SuffixYield r (SuffixElem st))
2227+
2228+ step' _ (SuffixYield x next) = return $ Yield x next
2229+
2207{-# INLINE intersperse #-}
2230{-# INLINE intersperse #-}
2208intersperse :: Monad m => a -> Stream m a -> Stream m a
2231intersperse :: Monad m => a -> Stream m a -> Stream m a
2209intersperse a = intersperseM (return a)
2232intersperse a = intersperseM (return a)
src/Streamly/Streams/StreamD/Type.hs+8−1
@@ -65,7 +65,7 @@ module Streamly.Streams.StreamD.Type
65where
65where
66
66
67import Control.Applicative (liftA2)
67import Control.Applicative (liftA2)
68−import Control.Monad (ap)
68+import Control.Monad (ap, when)
69import Control.Monad.Trans (lift, MonadTrans)
69import Control.Monad.Trans (lift, MonadTrans)
70import GHC.Types (SPEC(..))
70import GHC.Types (SPEC(..))
71import Prelude hiding (map, mapM, foldr, take, concatMap)
71import Prelude hiding (map, mapM, foldr, take, concatMap)
@@ -520,6 +520,13 @@ groupsOf n f (Stream step state) =
520
520
521 {-# INLINE_LATE stepOuter #-}
521 {-# INLINE_LATE stepOuter #-}
522 stepOuter gst (Just st) = do
522 stepOuter gst (Just st) = do
523+ -- XXX shall we use the Natural type instead? Need to check performance
524+ -- implications.
525+ when (n <= 0) $
526+ -- XXX we can pass the module string from the higher level API
527+ error $ "Streamly.Streams.StreamD.Type.groupsOf: the size of "
528+ ++ "groups [" ++ show n ++ "] must be a natural number"
529+
523 -- We retrieve the first element of the stream before we start to fold
530 -- We retrieve the first element of the stream before we start to fold
524 -- a chunk so that we do not return an empty chunk in case the stream
531 -- a chunk so that we do not return an empty chunk in case the stream
525 -- is empty.
532 -- is empty.
streamly.cabal+2
@@ -738,6 +738,7 @@ benchmark base
738 , Streamly.Streams.StreamD.Type
738 , Streamly.Streams.StreamD.Type
739 , Streamly.Streams.StreamD
739 , Streamly.Streams.StreamD
740 , Streamly.Streams.Prelude
740 , Streamly.Streams.Prelude
741+ , Streamly.FileSystem.IOVec
741
742
742 , StreamDOps
743 , StreamDOps
743 , StreamKOps
744 , StreamKOps
@@ -806,6 +807,7 @@ executable nano-bench
806 , Streamly.Streams.StreamK.Type
807 , Streamly.Streams.StreamK.Type
807 , Streamly.Streams.StreamK
808 , Streamly.Streams.StreamK
808 , Streamly.Streams.StreamD.Type
809 , Streamly.Streams.StreamD.Type
810+ , Streamly.FileSystem.IOVec
809 , Streamly.Streams.StreamD
811 , Streamly.Streams.StreamD
810 default-language: Haskell2010
812 default-language: Haskell2010
811 ghc-options: -O2 -Wall -with-rtsopts "-T"
813 ghc-options: -O2 -Wall -with-rtsopts "-T"
test/String.hs+23−3
@@ -3,9 +3,12 @@
3
3
4module Main (main) where
4module Main (main) where
5
5
6+import Data.Char (ord, chr)
7+import Data.Word (Word8)
6−import Test.Hspec.QuickCheck
8+import Test.Hspec.QuickCheck
9+import Test.QuickCheck
7−import Test.QuickCheck (Property, forAll, Gen, listOf, arbitraryUnicodeChar)
10+ (Property, forAll, Gen, listOf, arbitraryUnicodeChar, arbitrary)
8−import Test.QuickCheck.Monadic (run, monadicIO, assert)
11+import Test.QuickCheck.Monadic (run, monadicIO, assert)
9
12
10import Test.Hspec as H
13import Test.Hspec as H
11
14
@@ -14,17 +17,22 @@ import qualified Streamly.Prelude as S
14import qualified Streamly.String as SS
17import qualified Streamly.String as SS
15
18
16-- Coverage build takes too long with default number of tests
19-- Coverage build takes too long with default number of tests
20+{-
17maxTestCount :: Int
21maxTestCount :: Int
18#ifdef DEVBUILD
22#ifdef DEVBUILD
19maxTestCount = 100
23maxTestCount = 100
20#else
24#else
21maxTestCount = 10
25maxTestCount = 10
22#endif
26#endif
27+-}
23
28
24-- Use quickcheck-unicode instead?
29-- Use quickcheck-unicode instead?
25genUnicode :: Gen String
30genUnicode :: Gen String
26genUnicode = listOf arbitraryUnicodeChar
31genUnicode = listOf arbitraryUnicodeChar
27
32
33+genWord8 :: Gen [Word8]
34+genWord8 = listOf arbitrary
35+
28propDecodeEncodeId :: Property
36propDecodeEncodeId :: Property
29propDecodeEncodeId =
37propDecodeEncodeId =
30 forAll genUnicode $ \list ->
38 forAll genUnicode $ \list ->
@@ -43,6 +51,17 @@ testLines =
43 $ S.fromList list
51 $ S.fromList list
44 assert (xs == lines list)
52 assert (xs == lines list)
45
53
54+testLinesArray :: Property
55+testLinesArray =
56+ forAll genWord8 $ \list ->
57+ monadicIO $ do
58+ xs <- S.toList
59+ $ S.map A.toList
60+ $ A.splitArraysOn 10
61+ $ S.yield (A.fromList list)
62+ assert (xs == map (map (fromIntegral . ord))
63+ (lines (map (chr . fromIntegral) list)))
64+
46testWords :: Property
65testWords :: Property
47testWords =
66testWords =
48 forAll genUnicode $ \list ->
67 forAll genUnicode $ \list ->
@@ -82,6 +101,7 @@ main = hspec
82 describe "UTF8 - Encoding / Decoding" $ do
101 describe "UTF8 - Encoding / Decoding" $ do
83 prop "decodeUtf8 . encodeUtf8 == id" $ propDecodeEncodeId
102 prop "decodeUtf8 . encodeUtf8 == id" $ propDecodeEncodeId
84 prop "Streamly.String.lines == Prelude.lines" $ testLines
103 prop "Streamly.String.lines == Prelude.lines" $ testLines
104+ prop "Arrays Streamly.String.lines == Prelude.lines" $ testLinesArray
85 prop "Streamly.String.words == Prelude.words" $ testWords
105 prop "Streamly.String.words == Prelude.words" $ testWords
86 prop
106 prop
87 "Streamly.String.unlines . Streamly.String.lines == unlines . lines"
107 "Streamly.String.unlines . Streamly.String.lines == unlines . lines"