on b9e99db help sign in
367 lines 10 kB history raw
1-- |
2-- Module : Streamly.Benchmark.FileIO.Stream
3-- Copyright : (c) 2019 Composewell Technologies
4--
5-- License : BSD3
6-- Maintainer : harendra.kumar@gmail.com
7-- Stability : experimental
8-- Portability : GHC
10{-# LANGUAGE CPP #-}
12#ifdef __HADDOCK_VERSION__
13#undef INSPECTION
14#endif
16#ifdef INSPECTION
17{-# LANGUAGE TemplateHaskell #-}
18{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
19#endif
21module Streamly.Benchmark.FileIO.Stream
22 (
23 -- * FileIO
24 last
25 , countBytes
26 , countLines
27 , countLinesU
28 , sumBytes
29 , cat
30 , catStreamWrite
31 , copy
32 , linesUnlinesCopy
33 , wordsUnwordsCopy
34 , copyCodecChar8
35 , copyCodecUtf8
36 , chunksOf
37 , chunksOfD
38 , splitOn
39 , splitOnSuffix
40 , wordsBy
41 , splitOnSeq
42 , splitOnSuffixSeq
43 )
44where
46import Data.Char (ord, chr)
47import Data.Word (Word8)
48import System.IO (Handle)
49import Prelude hiding (last, length)
51import qualified Streamly.FileSystem.Handle as FH
52import qualified Streamly.FileSystem.Handle.Internal as FH
53import qualified Streamly.Memory.Array as A
54import qualified Streamly.Memory.Array.Types as AT
55import qualified Streamly.Prelude.Internal as S
56import qualified Streamly.Fold as FL
57import qualified Streamly.Data.String as SS
58import qualified Streamly.Internal as Internal
59import qualified Streamly.Streams.StreamD as D
61#ifdef INSPECTION
62import Streamly.Streams.StreamD.Type (Step(..), GroupState)
63import Test.Inspection
64#endif
66-- | Get the last byte from a file bytestream.
67{-# INLINE last #-}
68last :: Handle -> IO (Maybe Word8)
69last = S.last . FH.read
71#ifdef INSPECTION
72inspect $ hasNoTypeClasses 'last
73inspect $ 'last `hasNoType` ''Step
74inspect $ 'last `hasNoType` ''AT.FlattenState
75inspect $ 'last `hasNoType` ''D.ConcatMapUState
76#endif
78-- assert that flattenArrays constructors are not present
79-- | Count the number of bytes in a file.
80{-# INLINE countBytes #-}
81countBytes :: Handle -> IO Int
82countBytes = S.length . FH.read
84#ifdef INSPECTION
85inspect $ hasNoTypeClasses 'countBytes
86inspect $ 'countBytes `hasNoType` ''Step
87inspect $ 'countBytes `hasNoType` ''AT.FlattenState
88inspect $ 'countBytes `hasNoType` ''D.ConcatMapUState
89#endif
91-- | Count the number of lines in a file.
92{-# INLINE countLines #-}
93countLines :: Handle -> IO Int
94countLines =
95 S.length
96 . SS.foldLines FL.drain
97 . SS.decodeChar8
98 . FH.read
100#ifdef INSPECTION
101inspect $ hasNoTypeClasses 'countLines
102inspect $ 'countLines `hasNoType` ''Step
103inspect $ 'countLines `hasNoType` ''AT.FlattenState
104inspect $ 'countLines `hasNoType` ''D.ConcatMapUState
105#endif
107-- | Count the number of lines in a file.
108{-# INLINE countLinesU #-}
109countLinesU :: Handle -> IO Int
110countLinesU inh =
111 S.length
112 $ SS.foldLines FL.drain
113 $ SS.decodeChar8
114 $ Internal.concatMapU Internal.readU (FH.readArrays inh)
116#ifdef INSPECTION
117inspect $ hasNoTypeClasses 'countLinesU
118inspect $ 'countLinesU `hasNoType` ''Step
119inspect $ 'countLinesU `hasNoType` ''D.ConcatMapUState
120#endif
122-- | Sum the bytes in a file.
123{-# INLINE sumBytes #-}
124sumBytes :: Handle -> IO Word8
125sumBytes = S.sum . FH.read
127#ifdef INSPECTION
128inspect $ hasNoTypeClasses 'sumBytes
129inspect $ 'sumBytes `hasNoType` ''Step
130inspect $ 'sumBytes `hasNoType` ''AT.FlattenState
131inspect $ 'sumBytes `hasNoType` ''D.ConcatMapUState
132#endif
134-- | Send the file contents to /dev/null
135{-# INLINE cat #-}
136cat :: Handle -> Handle -> IO ()
137cat devNull inh = S.runFold (FH.write devNull) $ FH.read inh
139#ifdef INSPECTION
140inspect $ hasNoTypeClasses 'cat
141inspect $ 'cat `hasNoType` ''Step
142inspect $ 'cat `hasNoType` ''AT.FlattenState
143inspect $ 'cat `hasNoType` ''D.ConcatMapUState
144#endif
146-- | Send the file contents to /dev/null
147{-# INLINE catStreamWrite #-}
148catStreamWrite :: Handle -> Handle -> IO ()
149catStreamWrite devNull inh = FH.writeS devNull $ FH.read inh
151#ifdef INSPECTION
152inspect $ hasNoTypeClasses 'catStreamWrite
153inspect $ 'catStreamWrite `hasNoType` ''Step
154inspect $ 'catStreamWrite `hasNoType` ''AT.FlattenState
155inspect $ 'catStreamWrite `hasNoType` ''D.ConcatMapUState
156#endif
158-- | Copy file
159{-# INLINE copy #-}
160copy :: Handle -> Handle -> IO ()
161copy inh outh = S.runFold (FH.write outh) (FH.read inh)
163#ifdef INSPECTION
164inspect $ hasNoTypeClasses 'copy
165inspect $ 'copy `hasNoType` ''Step
166inspect $ 'copy `hasNoType` ''AT.FlattenState
167inspect $ 'copy `hasNoType` ''D.ConcatMapUState
168#endif
170-- | Copy file
171{-# INLINE copyCodecChar8 #-}
172copyCodecChar8 :: Handle -> Handle -> IO ()
173copyCodecChar8 inh outh =
174 S.runFold (FH.write outh)
175 $ SS.encodeChar8
176 $ SS.decodeChar8
177 $ FH.read inh
179#ifdef INSPECTION
180inspect $ hasNoTypeClasses 'copyCodecChar8
181inspect $ 'copyCodecChar8 `hasNoType` ''Step
182inspect $ 'copyCodecChar8 `hasNoType` ''AT.FlattenState
183inspect $ 'copyCodecChar8 `hasNoType` ''D.ConcatMapUState
184#endif
186-- | Copy file
187{-# INLINE copyCodecUtf8 #-}
188copyCodecUtf8 :: Handle -> Handle -> IO ()
189copyCodecUtf8 inh outh =
190 S.runFold (FH.write outh)
191 $ SS.encodeUtf8
192 $ SS.decodeUtf8
193 $ FH.read inh
195#ifdef INSPECTION
196inspect $ hasNoTypeClasses 'copyCodecUtf8
197-- inspect $ 'copyCodecUtf8 `hasNoType` ''Step
198-- inspect $ 'copyCodecUtf8 `hasNoType` ''AT.FlattenState
199-- inspect $ 'copyCodecUtf8 `hasNoType` ''D.ConcatMapUState
200#endif
202-- | Slice in chunks of size n and get the count of chunks.
203{-# INLINE chunksOf #-}
204chunksOf :: Int -> Handle -> IO Int
205chunksOf n inh =
206 -- writeNUnsafe gives 2.5x boost here over writeN.
207 S.length $ S.chunksOf n (AT.writeNUnsafe n) (FH.read inh)
209#ifdef INSPECTION
210inspect $ hasNoTypeClasses 'chunksOf
211inspect $ 'chunksOf `hasNoType` ''Step
212inspect $ 'chunksOf `hasNoType` ''AT.FlattenState
213inspect $ 'chunksOf `hasNoType` ''D.ConcatMapUState
214inspect $ 'chunksOf `hasNoType` ''GroupState
215#endif
217-- This is to make sure that the concatMap in FH.read, groupsOf and foldlM'
218-- together can fuse.
219--
220-- | Slice in chunks of size n and get the count of chunks.
221{-# INLINE chunksOfD #-}
222chunksOfD :: Int -> Handle -> IO Int
223chunksOfD n inh =
224 D.foldlM' (\i _ -> return $ i + 1) 0
225 $ D.groupsOf n (AT.writeNUnsafe n)
226 $ D.fromStreamK (FH.read inh)
228#ifdef INSPECTION
229inspect $ hasNoTypeClasses 'chunksOf
230inspect $ 'chunksOf `hasNoType` ''Step
231inspect $ 'chunksOfD `hasNoType` ''GroupState
232inspect $ 'chunksOfD `hasNoType` ''AT.FlattenState
233inspect $ 'chunksOfD `hasNoType` ''D.ConcatMapUState
234#endif
236-- | Lines and unlines
237{-# INLINE linesUnlinesCopy #-}
238linesUnlinesCopy :: Handle -> Handle -> IO ()
239linesUnlinesCopy inh outh =
240 S.runFold (FH.write outh)
241 $ SS.encodeChar8
242 $ SS.unlines
243 $ SS.lines
244 $ SS.decodeChar8
245 $ FH.read inh
247#ifdef INSPECTION
248-- inspect $ hasNoTypeClasses 'linesUnlinesCopy
249-- inspect $ 'linesUnlinesCopy `hasNoType` ''Step
250-- inspect $ 'linesUnlinesCopy `hasNoType` ''AT.FlattenState
251-- inspect $ 'linesUnlinesCopy `hasNoType` ''D.ConcatMapUState
252#endif
254-- | Word, unwords and copy
255{-# INLINE wordsUnwordsCopy #-}
256wordsUnwordsCopy :: Handle -> Handle -> IO ()
257wordsUnwordsCopy inh outh =
258 S.runFold (FH.write outh)
259 $ SS.encodeChar8
260 $ SS.unwords
261 $ SS.words
262 $ SS.decodeChar8
263 $ FH.read inh
265#ifdef INSPECTION
266-- inspect $ hasNoTypeClasses 'wordsUnwordsCopy
267-- inspect $ 'wordsUnwordsCopy `hasNoType` ''Step
268-- inspect $ 'wordsUnwordsCopy `hasNoType` ''AT.FlattenState
269-- inspect $ 'wordsUnwordsCopy `hasNoType` ''D.ConcatMapUState
270#endif
272lf :: Word8
273lf = fromIntegral (ord '\n')
275toarr :: String -> A.Array Word8
276toarr = A.fromList . map (fromIntegral . ord)
278foreign import ccall unsafe "u_iswspace"
279 iswspace :: Int -> Int
281-- Code copied from base/Data.Char to INLINE it
282{-# INLINE isSpace #-}
283isSpace :: Char -> Bool
284-- isSpace includes non-breaking space
285-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
286-- at or below 0x377 have been assigned, so we shouldn't have to worry about
287-- any new spaces appearing below there. It would probably be best to
288-- use branchless ||, but currently the eqLit transformation will undo that,
289-- so we'll do it like this until there's a way around that.
290isSpace c
291 | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
292 | otherwise = iswspace (ord c) /= 0
293 where
294 uc = fromIntegral (ord c) :: Word
296isSp :: Word8 -> Bool
297isSp = isSpace . chr . fromIntegral
299-- | Split on line feed.
300{-# INLINE splitOn #-}
301splitOn :: Handle -> IO Int
302splitOn inh =
303 (S.length $ S.splitOn (== lf) FL.drain
304 $ FH.read inh) -- >>= print
306#ifdef INSPECTION
307inspect $ hasNoTypeClasses 'splitOn
308inspect $ 'splitOn `hasNoType` ''Step
309inspect $ 'splitOn `hasNoType` ''AT.FlattenState
310inspect $ 'splitOn `hasNoType` ''D.ConcatMapUState
311#endif
313-- | Split suffix on line feed.
314{-# INLINE splitOnSuffix #-}
315splitOnSuffix :: Handle -> IO Int
316splitOnSuffix inh =
317 (S.length $ S.splitOnSuffix (== lf) FL.drain
318 $ FH.read inh) -- >>= print
320#ifdef INSPECTION
321inspect $ hasNoTypeClasses 'splitOnSuffix
322inspect $ 'splitOnSuffix `hasNoType` ''Step
323inspect $ 'splitOnSuffix `hasNoType` ''AT.FlattenState
324inspect $ 'splitOnSuffix `hasNoType` ''D.ConcatMapUState
325#endif
327-- | Words by space
328{-# INLINE wordsBy #-}
329wordsBy :: Handle -> IO Int
330wordsBy inh =
331 (S.length $ S.wordsBy isSp FL.drain
332 $ FH.read inh) -- >>= print
334#ifdef INSPECTION
335inspect $ hasNoTypeClasses 'wordsBy
336inspect $ 'wordsBy `hasNoType` ''Step
337inspect $ 'wordsBy `hasNoType` ''AT.FlattenState
338inspect $ 'wordsBy `hasNoType` ''D.ConcatMapUState
339#endif
341-- | Split on a character sequence.
342{-# INLINE splitOnSeq #-}
343splitOnSeq :: String -> Handle -> IO Int
344splitOnSeq str inh =
345 (S.length $ Internal.splitOnSeq (toarr str) FL.drain
346 $ FH.read inh) -- >>= print
348#ifdef INSPECTION
349inspect $ hasNoTypeClasses 'splitOnSeq
350-- inspect $ 'splitOnSeq `hasNoType` ''Step
351-- inspect $ 'splitOnSeq `hasNoType` ''AT.FlattenState
352-- inspect $ 'splitOnSeq `hasNoType` ''D.ConcatMapUState
353#endif
355-- | Split on suffix sequence.
356{-# INLINE splitOnSuffixSeq #-}
357splitOnSuffixSeq :: String -> Handle -> IO Int
358splitOnSuffixSeq str inh =
359 (S.length $ Internal.splitOnSuffixSeq (toarr str) FL.drain
360 $ FH.read inh) -- >>= print
362#ifdef INSPECTION
363inspect $ hasNoTypeClasses 'splitOnSuffixSeq
364-- inspect $ 'splitOnSuffixSeq `hasNoType` ''Step
365-- inspect $ 'splitOnSuffixSeq `hasNoType` ''AT.FlattenState
366-- inspect $ 'splitOnSuffixSeq `hasNoType` ''D.ConcatMapUState
367#endif