2-- Module : Streamly.Benchmark.FileIO.Stream
3-- Copyright : (c) 2019 Composewell Technologies
6-- Maintainer : harendra.kumar@gmail.com
7-- Stability : experimental
12#ifdef __HADDOCK_VERSION__
17{-# LANGUAGE TemplateHaskell #-}
18{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
21module Streamly.Benchmark.FileIO.Stream
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
62import Streamly.Streams.StreamD.Type (Step(..), GroupState)
66-- | Get the last byte from a file bytestream.
68last :: Handle -> IO (Maybe Word8)
69last = S.last . FH.read
72inspect $ hasNoTypeClasses 'last
73inspect $ 'last `hasNoType` ''Step
74inspect $ 'last `hasNoType` ''AT.FlattenState
75inspect $ 'last `hasNoType` ''D.ConcatMapUState
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
85inspect $ hasNoTypeClasses 'countBytes
86inspect $ 'countBytes `hasNoType` ''Step
87inspect $ 'countBytes `hasNoType` ''AT.FlattenState
88inspect $ 'countBytes `hasNoType` ''D.ConcatMapUState
91-- | Count the number of lines in a file.
92{-# INLINE countLines #-}
93countLines :: Handle -> IO Int
96 . SS.foldLines FL.drain
101inspect $ hasNoTypeClasses 'countLines
102inspect $ 'countLines `hasNoType` ''Step
103inspect $ 'countLines `hasNoType` ''AT.FlattenState
104inspect $ 'countLines `hasNoType` ''D.ConcatMapUState
107-- | Count the number of lines in a file.
108{-# INLINE countLinesU #-}
109countLinesU :: Handle -> IO Int
112 $ SS.foldLines FL.drain
114 $ Internal.concatMapU Internal.readU (FH.readArrays inh)
117inspect $ hasNoTypeClasses 'countLinesU
118inspect $ 'countLinesU `hasNoType` ''Step
119inspect $ 'countLinesU `hasNoType` ''D.ConcatMapUState
122-- | Sum the bytes in a file.
123{-# INLINE sumBytes #-}
124sumBytes :: Handle -> IO Word8
125sumBytes = S.sum . FH.read
128inspect $ hasNoTypeClasses 'sumBytes
129inspect $ 'sumBytes `hasNoType` ''Step
130inspect $ 'sumBytes `hasNoType` ''AT.FlattenState
131inspect $ 'sumBytes `hasNoType` ''D.ConcatMapUState
134-- | Send the file contents to /dev/null
136cat :: Handle -> Handle -> IO ()
137cat devNull inh = S.runFold (FH.write devNull) $ FH.read inh
140inspect $ hasNoTypeClasses 'cat
141inspect $ 'cat `hasNoType` ''Step
142inspect $ 'cat `hasNoType` ''AT.FlattenState
143inspect $ 'cat `hasNoType` ''D.ConcatMapUState
146-- | Send the file contents to /dev/null
147{-# INLINE catStreamWrite #-}
148catStreamWrite :: Handle -> Handle -> IO ()
149catStreamWrite devNull inh = FH.writeS devNull $ FH.read inh
152inspect $ hasNoTypeClasses 'catStreamWrite
153inspect $ 'catStreamWrite `hasNoType` ''Step
154inspect $ 'catStreamWrite `hasNoType` ''AT.FlattenState
155inspect $ 'catStreamWrite `hasNoType` ''D.ConcatMapUState
160copy :: Handle -> Handle -> IO ()
161copy inh outh = S.runFold (FH.write outh) (FH.read inh)
164inspect $ hasNoTypeClasses 'copy
165inspect $ 'copy `hasNoType` ''Step
166inspect $ 'copy `hasNoType` ''AT.FlattenState
167inspect $ 'copy `hasNoType` ''D.ConcatMapUState
171{-# INLINE copyCodecChar8 #-}
172copyCodecChar8 :: Handle -> Handle -> IO ()
173copyCodecChar8 inh outh =
174 S.runFold (FH.write outh)
180inspect $ hasNoTypeClasses 'copyCodecChar8
181inspect $ 'copyCodecChar8 `hasNoType` ''Step
182inspect $ 'copyCodecChar8 `hasNoType` ''AT.FlattenState
183inspect $ 'copyCodecChar8 `hasNoType` ''D.ConcatMapUState
187{-# INLINE copyCodecUtf8 #-}
188copyCodecUtf8 :: Handle -> Handle -> IO ()
189copyCodecUtf8 inh outh =
190 S.runFold (FH.write outh)
196inspect $ hasNoTypeClasses 'copyCodecUtf8
197-- inspect $ 'copyCodecUtf8 `hasNoType` ''Step
198-- inspect $ 'copyCodecUtf8 `hasNoType` ''AT.FlattenState
199-- inspect $ 'copyCodecUtf8 `hasNoType` ''D.ConcatMapUState
202-- | Slice in chunks of size n and get the count of chunks.
203{-# INLINE chunksOf #-}
204chunksOf :: Int -> Handle -> IO Int
206 -- writeNUnsafe gives 2.5x boost here over writeN.
207 S.length $ S.chunksOf n (AT.writeNUnsafe n) (FH.read inh)
210inspect $ hasNoTypeClasses 'chunksOf
211inspect $ 'chunksOf `hasNoType` ''Step
212inspect $ 'chunksOf `hasNoType` ''AT.FlattenState
213inspect $ 'chunksOf `hasNoType` ''D.ConcatMapUState
214inspect $ 'chunksOf `hasNoType` ''GroupState
217-- This is to make sure that the concatMap in FH.read, groupsOf and foldlM'
220-- | Slice in chunks of size n and get the count of chunks.
221{-# INLINE chunksOfD #-}
222chunksOfD :: Int -> Handle -> IO Int
224 D.foldlM' (\i _ -> return $ i + 1) 0
225 $ D.groupsOf n (AT.writeNUnsafe n)
226 $ D.fromStreamK (FH.read inh)
229inspect $ hasNoTypeClasses 'chunksOf
230inspect $ 'chunksOf `hasNoType` ''Step
231inspect $ 'chunksOfD `hasNoType` ''GroupState
232inspect $ 'chunksOfD `hasNoType` ''AT.FlattenState
233inspect $ 'chunksOfD `hasNoType` ''D.ConcatMapUState
236-- | Lines and unlines
237{-# INLINE linesUnlinesCopy #-}
238linesUnlinesCopy :: Handle -> Handle -> IO ()
239linesUnlinesCopy inh outh =
240 S.runFold (FH.write outh)
248-- inspect $ hasNoTypeClasses 'linesUnlinesCopy
249-- inspect $ 'linesUnlinesCopy `hasNoType` ''Step
250-- inspect $ 'linesUnlinesCopy `hasNoType` ''AT.FlattenState
251-- inspect $ 'linesUnlinesCopy `hasNoType` ''D.ConcatMapUState
254-- | Word, unwords and copy
255{-# INLINE wordsUnwordsCopy #-}
256wordsUnwordsCopy :: Handle -> Handle -> IO ()
257wordsUnwordsCopy inh outh =
258 S.runFold (FH.write outh)
266-- inspect $ hasNoTypeClasses 'wordsUnwordsCopy
267-- inspect $ 'wordsUnwordsCopy `hasNoType` ''Step
268-- inspect $ 'wordsUnwordsCopy `hasNoType` ''AT.FlattenState
269-- inspect $ 'wordsUnwordsCopy `hasNoType` ''D.ConcatMapUState
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.
291 | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
292 | otherwise = iswspace (ord c) /= 0
294 uc = fromIntegral (ord c) :: Word
297isSp = isSpace . chr . fromIntegral
299-- | Split on line feed.
300{-# INLINE splitOn #-}
301splitOn :: Handle -> IO Int
303 (S.length $ S.splitOn (== lf) FL.drain
304 $ FH.read inh) -- >>= print
307inspect $ hasNoTypeClasses 'splitOn
308inspect $ 'splitOn `hasNoType` ''Step
309inspect $ 'splitOn `hasNoType` ''AT.FlattenState
310inspect $ 'splitOn `hasNoType` ''D.ConcatMapUState
313-- | Split suffix on line feed.
314{-# INLINE splitOnSuffix #-}
315splitOnSuffix :: Handle -> IO Int
317 (S.length $ S.splitOnSuffix (== lf) FL.drain
318 $ FH.read inh) -- >>= print
321inspect $ hasNoTypeClasses 'splitOnSuffix
322inspect $ 'splitOnSuffix `hasNoType` ''Step
323inspect $ 'splitOnSuffix `hasNoType` ''AT.FlattenState
324inspect $ 'splitOnSuffix `hasNoType` ''D.ConcatMapUState
328{-# INLINE wordsBy #-}
329wordsBy :: Handle -> IO Int
331 (S.length $ S.wordsBy isSp FL.drain
332 $ FH.read inh) -- >>= print
335inspect $ hasNoTypeClasses 'wordsBy
336inspect $ 'wordsBy `hasNoType` ''Step
337inspect $ 'wordsBy `hasNoType` ''AT.FlattenState
338inspect $ 'wordsBy `hasNoType` ''D.ConcatMapUState
341-- | Split on a character sequence.
342{-# INLINE splitOnSeq #-}
343splitOnSeq :: String -> Handle -> IO Int
345 (S.length $ Internal.splitOnSeq (toarr str) FL.drain
346 $ FH.read inh) -- >>= print
349inspect $ hasNoTypeClasses 'splitOnSeq
350-- inspect $ 'splitOnSeq `hasNoType` ''Step
351-- inspect $ 'splitOnSeq `hasNoType` ''AT.FlattenState
352-- inspect $ 'splitOnSeq `hasNoType` ''D.ConcatMapUState
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
363inspect $ hasNoTypeClasses 'splitOnSuffixSeq
364-- inspect $ 'splitOnSuffixSeq `hasNoType` ''Step
365-- inspect $ 'splitOnSuffixSeq `hasNoType` ''AT.FlattenState
366-- inspect $ 'splitOnSuffixSeq `hasNoType` ''D.ConcatMapUState
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