on 3336bb1 help sign in
301 lines 8.8 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 TemplateHaskell #-}
12{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
14module Streamly.Benchmark.FileIO.Stream
15 (
16 -- * FileIO
17 last
18 , countBytes
19 , countLines
20 , countLinesU
21 , sumBytes
22 , cat
23 , catStreamWrite
24 , copy
25 , linesUnlinesCopy
26 , wordsUnwordsCopy
27 , copyCodecChar8
28 , copyCodecUtf8
29 , chunksOf
30 , splitOn
31 , splitOnSuffix
32 , wordsBy
33 , splitOnSeq
34 , splitOnSuffixSeq
35 )
36where
38import Data.Char (ord, chr)
39import Data.Word (Word8)
40import System.IO (Handle)
41import Prelude hiding (last, length)
43import Streamly.Streams.StreamD.Type (Step(..), GroupState)
44import Streamly.Benchmark.Inspection (hinspect)
45import qualified Streamly.Memory.Array.Types as AT
47import qualified Streamly.FileSystem.Handle as FH
48import qualified Streamly.Memory.Array as A
49import qualified Streamly.Prelude.Internal as S
50import qualified Streamly.Fold as FL
51import qualified Streamly.Data.String as SS
52import qualified Streamly.Internal as Internal
53import qualified Streamly.Streams.StreamD as D
55import Test.Inspection
57-- | Get the last byte from a file bytestream.
58{-# INLINE last #-}
59last :: Handle -> IO (Maybe Word8)
60last = S.last . FH.read
62hinspect $ hasNoTypeClasses 'last
63hinspect $ 'last `hasNoType` ''Step
64hinspect $ 'last `hasNoType` ''AT.FlattenState
66-- assert that flattenArrays constructors are not present
67-- | Count the number of bytes in a file.
68{-# INLINE countBytes #-}
69countBytes :: Handle -> IO Int
70countBytes = S.length . FH.read
72hinspect $ hasNoTypeClasses 'countBytes
73hinspect $ 'countBytes `hasNoType` ''Step
74hinspect $ 'countBytes `hasNoType` ''AT.FlattenState
76-- | Count the number of lines in a file.
77{-# INLINE countLines #-}
78countLines :: Handle -> IO Int
79countLines =
80 S.length
81 . SS.foldLines FL.drain
82 . SS.decodeChar8
83 . FH.read
85hinspect $ hasNoTypeClasses 'countLines
86hinspect $ 'countLines `hasNoType` ''Step
87hinspect $ 'countLines `hasNoType` ''AT.FlattenState
89-- | Count the number of lines in a file.
90{-# INLINE countLinesU #-}
91countLinesU :: Handle -> IO Int
92countLinesU inh =
93 S.length
94 $ SS.foldLines FL.drain
95 $ SS.decodeChar8
96 $ Internal.concatMapU Internal.readU (FH.readArrays inh)
98hinspect $ hasNoTypeClasses 'countLinesU
99hinspect $ 'countLinesU `hasNoType` ''Step
100hinspect $ 'countLinesU `hasNoType` ''Either
102-- | Sum the bytes in a file.
103{-# INLINE sumBytes #-}
104sumBytes :: Handle -> IO Word8
105sumBytes = S.sum . FH.read
107hinspect $ hasNoTypeClasses 'sumBytes
108hinspect $ 'sumBytes `hasNoType` ''Step
109hinspect $ 'sumBytes `hasNoType` ''AT.FlattenState
111-- | Send the file contents to /dev/null
112{-# INLINE cat #-}
113cat :: Handle -> Handle -> IO ()
114cat devNull inh = S.runFold (FH.write devNull) $ FH.read inh
116hinspect $ hasNoTypeClasses 'cat
117hinspect $ 'cat `hasNoType` ''Step
118hinspect $ 'cat `hasNoType` ''AT.FlattenState
120-- | Send the file contents to /dev/null
121{-# INLINE catStreamWrite #-}
122catStreamWrite :: Handle -> Handle -> IO ()
123catStreamWrite devNull inh = Internal.writeS devNull $ FH.read inh
125hinspect $ hasNoTypeClasses 'catStreamWrite
126hinspect $ 'catStreamWrite `hasNoType` ''Step
127hinspect $ 'catStreamWrite `hasNoType` ''AT.FlattenState
129-- | Copy file
130{-# INLINE copy #-}
131copy :: Handle -> Handle -> IO ()
132copy inh outh = S.runFold (FH.write outh) (FH.read inh)
134hinspect $ hasNoTypeClasses 'copy
135hinspect $ 'copy `hasNoType` ''Step
136hinspect $ 'copy `hasNoType` ''AT.FlattenState
138-- | Copy file
139{-# INLINE copyCodecChar8 #-}
140copyCodecChar8 :: Handle -> Handle -> IO ()
141copyCodecChar8 inh outh =
142 S.runFold (FH.write outh)
143 $ SS.encodeChar8
144 $ SS.decodeChar8
145 $ FH.read inh
147hinspect $ hasNoTypeClasses 'copyCodecChar8
148hinspect $ 'copyCodecChar8 `hasNoType` ''Step
149hinspect $ 'copyCodecChar8 `hasNoType` ''AT.FlattenState
151-- | Copy file
152{-# INLINE copyCodecUtf8 #-}
153copyCodecUtf8 :: Handle -> Handle -> IO ()
154copyCodecUtf8 inh outh =
155 S.runFold (FH.write outh)
156 $ SS.encodeUtf8
157 $ SS.decodeUtf8
158 $ FH.read inh
160hinspect $ hasNoTypeClasses 'copyCodecUtf8
161-- hinspect $ 'copyCodecUtf8 `hasNoType` ''Step
162-- hinspect $ 'copyCodecUtf8 `hasNoType` ''AT.FlattenState
164-- | Slice in chunks of size n and get the count of chunks.
165{-# INLINE chunksOf #-}
166chunksOf :: Int -> Handle -> IO Int
167chunksOf n inh =
168 S.length $ S.chunksOf n (AT.writeNUnsafe n) (FH.read inh)
170hinspect $ hasNoTypeClasses 'chunksOf
171hinspect $ 'chunksOf `hasNoType` ''Step
172hinspect $ 'chunksOf `hasNoType` ''AT.FlattenState
173hinspect $ 'chunksOf `hasNoType` ''GroupState
175-- This is to make sure that the concatMap in FH.read, groupsOf and foldlM'
176-- together can fuse.
177--
178-- | Slice in chunks of size n and get the count of chunks.
179{-# INLINE chunksOfD #-}
180chunksOfD :: Int -> Handle -> IO Int
181chunksOfD n inh =
182 D.foldlM' (\i _ -> return $ i + 1) 0
183 $ D.groupsOf n (AT.writeNUnsafe n)
184 $ D.fromStreamK (FH.read inh)
186hinspect $ hasNoTypeClasses 'chunksOf
187hinspect $ 'chunksOf `hasNoType` ''Step
188hinspect $ 'chunksOfD `hasNoType` ''GroupState
189hinspect $ 'chunksOfD `hasNoType` ''AT.FlattenState
191-- | Lines and unlines
192{-# INLINE linesUnlinesCopy #-}
193linesUnlinesCopy :: Handle -> Handle -> IO ()
194linesUnlinesCopy inh outh =
195 S.runFold (FH.write outh)
196 $ SS.encodeChar8
197 $ SS.unlines
198 $ SS.lines
199 $ SS.decodeChar8
200 $ FH.read inh
202-- hinspect $ hasNoTypeClasses 'linesUnlinesCopy
203-- hinspect $ 'linesUnlinesCopy `hasNoType` ''Step
204-- hinspect $ 'linesUnlinesCopy `hasNoType` ''AT.FlattenState
206-- | Word, unwords and copy
207{-# INLINE wordsUnwordsCopy #-}
208wordsUnwordsCopy :: Handle -> Handle -> IO ()
209wordsUnwordsCopy inh outh =
210 S.runFold (FH.write outh)
211 $ SS.encodeChar8
212 $ SS.unwords
213 $ SS.words
214 $ SS.decodeChar8
215 $ FH.read inh
217-- hinspect $ hasNoTypeClasses 'wordsUnwordsCopy
218-- hinspect $ 'wordsUnwordsCopy `hasNoType` ''Step
219-- hinspect $ 'wordsUnwordsCopy `hasNoType` ''AT.FlattenState
221lf :: Word8
222lf = fromIntegral (ord '\n')
224toarr :: String -> A.Array Word8
225toarr = A.fromList . map (fromIntegral . ord)
227foreign import ccall unsafe "u_iswspace"
228 iswspace :: Int -> Int
230-- Code copied from base/Data.Char to INLINE it
231{-# INLINE isSpace #-}
232isSpace :: Char -> Bool
233-- isSpace includes non-breaking space
234-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
235-- at or below 0x377 have been assigned, so we shouldn't have to worry about
236-- any new spaces appearing below there. It would probably be best to
237-- use branchless ||, but currently the eqLit transformation will undo that,
238-- so we'll do it like this until there's a way around that.
239isSpace c
240 | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
241 | otherwise = iswspace (ord c) /= 0
242 where
243 uc = fromIntegral (ord c) :: Word
245isSp :: Word8 -> Bool
246isSp = isSpace . chr . fromIntegral
248-- | Split on line feed.
249{-# INLINE splitOn #-}
250splitOn :: Handle -> IO Int
251splitOn inh =
252 (S.length $ S.splitOn (== lf) FL.drain
253 $ FH.read inh) -- >>= print
255hinspect $ hasNoTypeClasses 'splitOn
256hinspect $ 'splitOn `hasNoType` ''Step
257hinspect $ 'splitOn `hasNoType` ''AT.FlattenState
259-- | Split suffix on line feed.
260{-# INLINE splitOnSuffix #-}
261splitOnSuffix :: Handle -> IO Int
262splitOnSuffix inh =
263 (S.length $ S.splitOnSuffix (== lf) FL.drain
264 $ FH.read inh) -- >>= print
266hinspect $ hasNoTypeClasses 'splitOnSuffix
267hinspect $ 'splitOnSuffix `hasNoType` ''Step
268hinspect $ 'splitOnSuffix `hasNoType` ''AT.FlattenState
270-- | Words by space
271{-# INLINE wordsBy #-}
272wordsBy :: Handle -> IO Int
273wordsBy inh =
274 (S.length $ S.wordsBy isSp FL.drain
275 $ FH.read inh) -- >>= print
277hinspect $ hasNoTypeClasses 'wordsBy
278hinspect $ 'wordsBy `hasNoType` ''Step
279hinspect $ 'wordsBy `hasNoType` ''AT.FlattenState
281-- | Split on a character sequence.
282{-# INLINE splitOnSeq #-}
283splitOnSeq :: String -> Handle -> IO Int
284splitOnSeq str inh =
285 (S.length $ Internal.splitOnSeq (toarr str) FL.drain
286 $ FH.read inh) -- >>= print
288hinspect $ hasNoTypeClasses 'splitOnSeq
289-- hinspect $ 'splitOnSeq `hasNoType` ''Step
290-- hinspect $ 'splitOnSeq `hasNoType` ''AT.FlattenState
292-- | Split on suffix sequence.
293{-# INLINE splitOnSuffixSeq #-}
294splitOnSuffixSeq :: String -> Handle -> IO Int
295splitOnSuffixSeq str inh =
296 (S.length $ Internal.splitOnSuffixSeq (toarr str) FL.drain
297 $ FH.read inh) -- >>= print
299hinspect $ hasNoTypeClasses 'splitOnSuffixSeq
300-- hinspect $ 'splitOnSuffixSeq `hasNoType` ''Step
301-- hinspect $ 'splitOnSuffixSeq `hasNoType` ''AT.FlattenState