4-- Module : Streamly.Internal.FileSystem.Dir
5-- Copyright : (c) 2018 Composewell Technologies
8-- Maintainer : streamly@composewell.com
9-- Stability : pre-release
12module Streamly.Internal.FileSystem.Dir
22 -- | Use the more convenient stream APIs instead of unfolds where possible.
29 , toStreamWithBufferOf
32 , readChunksWithBufferOf
34 , toChunksWithBufferOf
40 -- Byte stream write (Streams)
42 , fromStreamWithBufferOf
47 , writeChunksWithBufferOf
49 -- -- * Array stream Write
51 , fromChunksWithBufferOf
61import Control.Monad.IO.Class (MonadIO(..))
62import Data.Either (isRight, isLeft, fromLeft, fromRight)
63import Streamly.Data.Stream (Stream)
64import Streamly.Internal.Data.Unfold.Type (Unfold(..))
66import qualified Streamly.Data.Unfold as UF
67import qualified Streamly.Internal.Data.Unfold as UF (mapM2)
68import qualified Streamly.Data.Stream as S
69import qualified System.Directory as Dir
71import Prelude hiding (read)
74{-# INLINABLE readArrayUpto #-}
75readArrayUpto :: Int -> Handle -> IO (Array Word8)
76readArrayUpto size h = do
77 ptr <- mallocPlainForeignPtrBytes size
78 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
79 withForeignPtr ptr $ \p -> do
80 n <- hGetBufSome h p size
83 , arrEnd = p `plusPtr` n
84 , arrBound = p `plusPtr` size
86 -- XXX shrink only if the diff is significant
89-------------------------------------------------------------------------------
91-------------------------------------------------------------------------------
93-- | @toChunksWithBufferOf size h@ reads a stream of arrays from file handle @h@.
94-- The maximum size of a single array is specified by @size@. The actual size
95-- read may be less than or equal to @size@.
96{-# INLINE _toChunksWithBufferOf #-}
97_toChunksWithBufferOf :: MonadIO m => Int -> Handle -> Stream m (Array Word8)
98_toChunksWithBufferOf size h = go
100 -- XXX use cons/nil instead
101 go = mkStream $ \_ yld _ stp -> do
102 arr <- liftIO $ readArrayUpto size h
107-- | @toChunksWithBufferOf size handle@ reads a stream of arrays from the file
108-- handle @handle@. The maximum size of a single array is limited to @size@.
109-- The actual size read may be less than or equal to @size@.
112{-# INLINE_NORMAL toChunksWithBufferOf #-}
113toChunksWithBufferOf :: MonadIO m => Int -> Handle -> Stream m (Array Word8)
114toChunksWithBufferOf size h = D.fromStreamD (D.Stream step ())
116 {-# INLINE_LATE step #-}
118 arr <- liftIO $ readArrayUpto size h
124-- | Unfold the tuple @(bufsize, handle)@ into a stream of 'Word8' arrays.
125-- Read requests to the IO device are performed using a buffer of size
126-- @bufsize@. The size of an array in the resulting stream is always less than
127-- or equal to @bufsize@.
130{-# INLINE_NORMAL readChunksWithBufferOf #-}
131readChunksWithBufferOf :: MonadIO m => Unfold m (Int, Handle) (Array Word8)
132readChunksWithBufferOf = Unfold step return
134 {-# INLINE_LATE step #-}
136 arr <- liftIO $ readArrayUpto size h
140 _ -> D.Yield arr (size, h)
142-- XXX read 'Array a' instead of Word8
144-- | @toChunks handle@ reads a stream of arrays from the specified file
145-- handle. The maximum size of a single array is limited to
146-- @defaultChunkSize@. The actual size read may be less than or equal to
147-- @defaultChunkSize@.
149-- > toChunks = toChunksWithBufferOf defaultChunkSize
152{-# INLINE toChunks #-}
153toChunks :: MonadIO m => Handle -> Stream m (Array Word8)
154toChunks = toChunksWithBufferOf defaultChunkSize
156-- | Unfolds a handle into a stream of 'Word8' arrays. Requests to the IO
157-- device are performed using a buffer of size
158-- 'Streamly.Internal.Data.Array.Unboxed.Type.defaultChunkSize'. The
159-- size of arrays in the resulting stream are therefore less than or equal to
160-- 'Streamly.Internal.Data.Array.Unboxed.Type.defaultChunkSize'.
163{-# INLINE readChunks #-}
164readChunks :: MonadIO m => Unfold m Handle (Array Word8)
165readChunks = UF.first readChunksWithBufferOf defaultChunkSize
167-------------------------------------------------------------------------------
168-- Read File to Stream
169-------------------------------------------------------------------------------
171-- TODO for concurrent streams implement readahead IO. We can send multiple
172-- read requests at the same time. For serial case we can use async IO. We can
173-- also control the read throughput in mbps or IOPS.
175-- | Unfolds the tuple @(bufsize, handle)@ into a byte stream, read requests
176-- to the IO device are performed using buffers of @bufsize@.
179{-# INLINE readWithBufferOf #-}
180readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
181readWithBufferOf = UF.many readChunksWithBufferOf A.read
183-- | @toStreamWithBufferOf bufsize handle@ reads a byte stream from a file
184-- handle, reads are performed in chunks of up to @bufsize@.
187{-# INLINE toStreamWithBufferOf #-}
188toStreamWithBufferOf :: MonadIO m => Int -> Handle -> Stream m Word8
189toStreamWithBufferOf chunkSize h = AS.concat $ toChunksWithBufferOf chunkSize h
192-- XXX exception handling
193-- | Raw read of a directory
198reader :: MonadIO m => Unfold m String String
200 -- XXX use proper streaming read of the dir
201 UF.lmapM (liftIO . Dir.getDirectoryContents) UF.fromList
203-- XXX We can use a more general mechanism to filter the contents of a
204-- directory. We can just stat each child and pass on the stat information. We
205-- can then use that info to do a general filtering. "find" like filters can be
208-- | Read directories as Left and files as Right. Filter out "." and ".."
213{-# INLINE eitherReader #-}
214eitherReader :: MonadIO m => Unfold m String (Either String String)
217 $ UF.filter (\x -> x /= "." && x /= "..")
218 -- XXX use proper streaming read of the dir
219 $ UF.lmapM (liftIO . Dir.getDirectoryContents) UF.fromList
222 r <- liftIO $ Dir.doesDirectoryExist (dir ++ "/" ++ x)
223 return $ if r then Left x else Right x
230{-# INLINE fileReader #-}
231fileReader :: MonadIO m => Unfold m String String
232fileReader = fmap (fromRight undefined) $ UF.filter isRight eitherReader
234-- | Read directories only. Filter out "." and ".." entries.
238{-# INLINE dirReader #-}
239dirReader :: MonadIO m => Unfold m String String
240dirReader = fmap (fromLeft undefined) $ UF.filter isLeft eitherReader
242-- | Raw read of a directory.
246read :: MonadIO m => String -> Stream m String
247read = S.unfold reader
249{-# DEPRECATED toStream "Please use 'read' instead" #-}
250{-# INLINE toStream #-}
251toStream :: MonadIO m => String -> Stream m String
254-- | Read directories as Left and files as Right. Filter out "." and ".."
258{-# INLINE readEither #-}
259readEither :: MonadIO m => String -> Stream m (Either String String)
260readEither = S.unfold eitherReader
262{-# DEPRECATED toEither "Please use 'readEither' instead" #-}
263{-# INLINE toEither #-}
264toEither :: MonadIO m => String -> Stream m (Either String String)
271{-# INLINE readFiles #-}
272readFiles :: MonadIO m => String -> Stream m String
273readFiles = S.unfold fileReader
275{-# DEPRECATED toFiles "Please use 'readFiles' instead" #-}
276{-# INLINE toFiles #-}
277toFiles :: MonadIO m => String -> Stream m String
280-- | Read directories only.
284{-# INLINE readDirs #-}
285readDirs :: MonadIO m => String -> Stream m String
286readDirs = S.unfold dirReader
288{-# DEPRECATED toDirs "Please use 'readDirs' instead" #-}
290toDirs :: MonadIO m => String -> Stream m String
294-------------------------------------------------------------------------------
296-------------------------------------------------------------------------------
298-------------------------------------------------------------------------------
300-------------------------------------------------------------------------------
302-- | Write an 'Array' to a file handle.
305{-# INLINABLE writeArray #-}
306writeArray :: Storable a => Handle -> Array a -> IO ()
307writeArray _ arr | A.length arr == 0 = return ()
308writeArray h Array{..} = withForeignPtr aStart $ \p -> hPutBuf h p aLen
311 let p = unsafeForeignPtrToPtr aStart
312 in arrEnd `minusPtr` p
314-------------------------------------------------------------------------------
315-- Stream of Arrays IO
316-------------------------------------------------------------------------------
318-------------------------------------------------------------------------------
320-------------------------------------------------------------------------------
322-- | Write a stream of arrays to a handle.
325{-# INLINE fromChunks #-}
326fromChunks :: (MonadIO m, Storable a)
327 => Handle -> Stream m (Array a) -> m ()
328fromChunks h m = S.mapM_ (liftIO . writeArray h) m
330-- | @fromChunksWithBufferOf bufsize handle stream@ writes a stream of arrays
331-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
332-- The chunk size is only a maximum and the actual writes could be smaller as
333-- we do not split the arrays to fit exactly to the specified size.
336{-# INLINE fromChunksWithBufferOf #-}
337fromChunksWithBufferOf :: (MonadIO m, Storable a)
338 => Int -> Handle -> Stream m (Array a) -> m ()
339fromChunksWithBufferOf n h xs = fromChunks h $ AS.compact n xs
341-- | @fromStreamWithBufferOf bufsize handle stream@ writes @stream@ to @handle@
342-- in chunks of @bufsize@. A write is performed to the IO device as soon as we
343-- collect the required input size.
346{-# INLINE fromStreamWithBufferOf #-}
347fromStreamWithBufferOf :: MonadIO m => Int -> Handle -> Stream m Word8 -> m ()
348fromStreamWithBufferOf n h m = fromChunks h $ S.arraysOf n m
349-- fromStreamWithBufferOf n h m = fromChunks h $ AS.arraysOf n m
351-- > write = 'writeWithBufferOf' A.defaultChunkSize
353-- | Write a byte stream to a file handle. Accumulates the input in chunks of
354-- up to 'Streamly.Internal.Data.Array.Unboxed.Type.defaultChunkSize' before writing.
356-- NOTE: This may perform better than the 'write' fold, you can try this if you
357-- need some extra perf boost.
360{-# INLINE fromStream #-}
361fromStream :: MonadIO m => Handle -> Stream m Word8 -> m ()
362fromStream = fromStreamWithBufferOf defaultChunkSize
364-- | Write a stream of arrays to a handle. Each array in the stream is written
365-- to the device as a separate IO request.
368{-# INLINE writeChunks #-}
369writeChunks :: (MonadIO m, Storable a) => Handle -> Fold m (Array a) ()
370writeChunks h = FL.drainBy (liftIO . writeArray h)
372-- | @writeChunksWithBufferOf bufsize handle@ writes a stream of arrays
373-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
374-- We never split an array, if a single array is bigger than the specified size
375-- it emitted as it is. Multiple arrays are coalesed as long as the total size
376-- remains below the specified size.
379{-# INLINE writeChunksWithBufferOf #-}
380writeChunksWithBufferOf :: (MonadIO m, Storable a)
381 => Int -> Handle -> Fold m (Array a) ()
382writeChunksWithBufferOf n h = lpackArraysChunksOf n (writeChunks h)
384-- GHC buffer size dEFAULT_FD_BUFFER_SIZE=8192 bytes.
387-- Note that if you use a chunk size less than 8K (GHC's default buffer
388-- size) then you are advised to use 'NOBuffering' mode on the 'Handle' in case you
389-- do not want buffering to occur at GHC level as well. Same thing applies to
392-- | @writeWithBufferOf reqSize handle@ writes the input stream to @handle@.
393-- Bytes in the input stream are collected into a buffer until we have a chunk
394-- of @reqSize@ and then written to the IO device.
397{-# INLINE writeWithBufferOf #-}
398writeWithBufferOf :: MonadIO m => Int -> Handle -> Fold m Word8 ()
399writeWithBufferOf n h = FL.chunksOf n (writeNUnsafe n) (writeChunks h)
401-- > write = 'writeWithBufferOf' A.defaultChunkSize
403-- | Write a byte stream to a file handle. Accumulates the input in chunks of
404-- up to 'Streamly.Internal.Data.Array.Unboxed.Type.defaultChunkSize' before writing
409write :: MonadIO m => Handle -> Fold m Word8 ()
410write = writeWithBufferOf defaultChunkSize
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