{-# LINE 1 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
-- |
-- Module      : Streamly.Internal.FileSystem.Posix.ReadDir
-- Copyright   : (c) 2024 Composewell Technologies
--
-- License     : BSD3
-- Maintainer  : streamly@composewell.com
-- Portability : GHC

module Streamly.Internal.FileSystem.Posix.ReadDir
    (

{-# LINE 12 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
      DirStream (..)
    , openDirStream
    , openDirStreamCString
    , closeDirStream
    , readDirStreamEither
    , readEitherChunks
    , readEitherByteChunks
    , readEitherByteChunksAt
    , eitherReader
    , reader

{-# LINE 23 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
    )
where


{-# LINE 27 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
import Control.Monad.Catch (MonadCatch)
import Control.Monad.IO.Class (MonadIO(..))
import Data.Char (ord)
import Foreign (Ptr, Word8, nullPtr, peek, peekByteOff, castPtr, plusPtr)
import Foreign.C
    (resetErrno, Errno(..), getErrno, eINTR, throwErrno
    , throwErrnoIfMinus1Retry_, throwErrnoIfNullRetry, CInt(..), CString, CChar, CSize(..))
import Foreign.Storable (poke)
import Fusion.Plugin.Types (Fuse(..))
import Streamly.Internal.Data.Array (Array(..))
import Streamly.Internal.Data.MutByteArray (MutByteArray)
import Streamly.Internal.Data.Stream (Stream(..), Step(..))
import Streamly.Internal.Data.Unfold.Type (Unfold(..))
import Streamly.Internal.FileSystem.Path (Path)
import Streamly.Internal.FileSystem.Posix.Errno (throwErrnoPathIfNullRetry)
import Streamly.Internal.FileSystem.Posix.File
    (OpenMode(..), openFd, openFdAt, closeFd)
import Streamly.Internal.FileSystem.PosixPath (PosixPath(..))
import System.Posix.Types (Fd(..))

import qualified Streamly.Internal.Data.Array as Array
import qualified Streamly.Internal.Data.MutByteArray as MutByteArray
import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
import qualified Streamly.Internal.FileSystem.Path.Common as PathC
import qualified Streamly.Internal.FileSystem.PosixPath as Path



-------------------------------------------------------------------------------

data {-# CTYPE "DIR" #-} CDir
data {-# CTYPE "struct dirent" #-} CDirent

newtype DirStream = DirStream (Ptr CDir)

-------------------------------------------------------------------------------
-- readdir operations
-------------------------------------------------------------------------------

-- IMPORTANT NOTE: Use capi FFI for all readdir APIs. This is required at
-- least on macOS for correctness. We saw random directory entries when ccall
-- was used on macOS 15.3. Looks like it was picking the wrong version of
-- dirent structure. Did not see the problem in CIs on macOS 14.7.2 though.
foreign import capi unsafe "closedir"
   c_closedir :: Ptr CDir -> IO CInt

foreign import capi unsafe "dirent.h opendir"
    c_opendir :: CString  -> IO (Ptr CDir)

foreign import capi unsafe "dirent.h fdopendir"
    c_fdopendir :: CInt  -> IO (Ptr CDir)

-- XXX The "unix" package uses a wrapper over readdir __hscore_readdir (see
-- cbits/HsUnix.c in unix package) which uses readdir_r in some cases where
-- readdir is not known to be re-entrant. We are not doing that here. We are
-- assuming that readdir is re-entrant which may not be the case on some old
-- unix systems.
foreign import capi unsafe "dirent.h readdir"
    c_readdir  :: Ptr CDir -> IO (Ptr CDirent)

foreign import ccall unsafe "lstat_is_directory"
    c_lstat_is_directory  :: CString -> IO CInt

-- | The CString must be pinned.
{-# INLINE openDirStreamCString #-}
openDirStreamCString :: CString -> IO DirStream
openDirStreamCString :: CString -> IO DirStream
openDirStreamCString CString
s = do
    -- XXX we do not decode the path here, just print it as cstring
    -- XXX pass lazy concat of "openDirStream: " ++ s
    Ptr CDir
dirp <- String -> IO (Ptr CDir) -> IO (Ptr CDir)
forall a. String -> IO (Ptr a) -> IO (Ptr a)
throwErrnoIfNullRetry String
"openDirStream" (IO (Ptr CDir) -> IO (Ptr CDir)) -> IO (Ptr CDir) -> IO (Ptr CDir)
forall a b. (a -> b) -> a -> b
$ CString -> IO (Ptr CDir)
c_opendir CString
s
    DirStream -> IO DirStream
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr CDir -> DirStream
DirStream Ptr CDir
dirp)

-- XXX Path is not null terminated therefore we need to make a copy even if the
-- array is pinned.
-- {-# INLINE openDirStream #-}
openDirStream :: PosixPath -> IO DirStream
openDirStream :: PosixPath -> IO DirStream
openDirStream PosixPath
p =
    Array Word8 -> (CString -> IO DirStream) -> IO DirStream
forall a b. Array a -> (CString -> IO b) -> IO b
Array.asCStringUnsafe (PosixPath -> Array Word8
forall a. IsPath PosixPath a => a -> Array Word8
Path.toChunk PosixPath
p) ((CString -> IO DirStream) -> IO DirStream)
-> (CString -> IO DirStream) -> IO DirStream
forall a b. (a -> b) -> a -> b
$ \CString
s -> do
        -- openDirStreamCString s
        Ptr CDir
dirp <- String -> PosixPath -> IO (Ptr CDir) -> IO (Ptr CDir)
forall a. String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
throwErrnoPathIfNullRetry String
"openDirStream" PosixPath
p (IO (Ptr CDir) -> IO (Ptr CDir)) -> IO (Ptr CDir) -> IO (Ptr CDir)
forall a b. (a -> b) -> a -> b
$ CString -> IO (Ptr CDir)
c_opendir CString
s
        DirStream -> IO DirStream
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr CDir -> DirStream
DirStream Ptr CDir
dirp)

-- | Note that the supplied Fd is used by DirStream and when we close the
-- DirStream the fd will be closed.
openDirStreamAt :: Fd -> PosixPath -> IO DirStream
openDirStreamAt :: Fd -> PosixPath -> IO DirStream
openDirStreamAt Fd
fd PosixPath
p = do
    Fd
fd1 <- Maybe Fd -> PosixPath -> OpenMode -> IO Fd
openFdAt (Fd -> Maybe Fd
forall a. a -> Maybe a
Just Fd
fd) PosixPath
p OpenMode
ReadOnly
    -- liftIO $ putStrLn $ "opened: " ++ show fd1
    Ptr CDir
dirp <- String -> PosixPath -> IO (Ptr CDir) -> IO (Ptr CDir)
forall a. String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
throwErrnoPathIfNullRetry String
"openDirStreamAt" PosixPath
p
        (IO (Ptr CDir) -> IO (Ptr CDir)) -> IO (Ptr CDir) -> IO (Ptr CDir)
forall a b. (a -> b) -> a -> b
$ CInt -> IO (Ptr CDir)
c_fdopendir (Fd -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Fd
fd1)
    -- XXX can we somehow clone fd1 instead of opening again?
    DirStream -> IO DirStream
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr CDir -> DirStream
DirStream Ptr CDir
dirp)

-- | @closeDirStream dp@ calls @closedir@ to close
--   the directory stream @dp@.
closeDirStream :: DirStream -> IO ()
closeDirStream :: DirStream -> IO ()
closeDirStream (DirStream Ptr CDir
dirp) = do
  String -> IO CInt -> IO ()
forall a. (Eq a, Num a) => String -> IO a -> IO ()
throwErrnoIfMinus1Retry_ String
"closeDirStream" (Ptr CDir -> IO CInt
c_closedir Ptr CDir
dirp)

-------------------------------------------------------------------------------
-- determining filetype
-------------------------------------------------------------------------------

isMetaDir :: Ptr CChar -> IO Bool
isMetaDir :: CString -> IO Bool
isMetaDir CString
dname = do
    -- XXX Assuming an encoding that maps "." to ".", this is true for
    -- UTF8.
    -- Load as soon as possible to optimize memory accesses
    CChar
c1 <- CString -> IO CChar
forall a. Storable a => Ptr a -> IO a
peek CString
dname
    Word8
c2 :: Word8 <- CString -> Int -> IO Word8
forall b. Ptr b -> Int -> IO Word8
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff CString
dname Int
1
    if (CChar
c1 CChar -> CChar -> Bool
forall a. Eq a => a -> a -> Bool
/= Int -> CChar
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
ord Char
'.'))
    then Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
    else do
        if (Word8
c2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0)
        then Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
        else do
            if (Word8
c2 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
ord Char
'.'))
            then Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
            else do
                Word8
c3 :: Word8 <- CString -> Int -> IO Word8
forall b. Ptr b -> Int -> IO Word8
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff CString
dname Int
2
                if (Word8
c3 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0)
                then Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                else Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False

{-# NOINLINE lstatDname #-}
lstatDname :: PosixPath -> Ptr CChar -> IO (Bool, Bool)
lstatDname :: PosixPath -> CString -> IO (Bool, Bool)
lstatDname PosixPath
parent CString
dname = do
    Bool
isMeta <- IO Bool -> IO Bool
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> IO Bool) -> IO Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ CString -> IO Bool
isMetaDir CString
dname
    if Bool
isMeta
    then (Bool, Bool) -> IO (Bool, Bool)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool
True, Bool
True)
    else do
        -- XXX We can create a pinned array right here since the next call pins
        -- it anyway.
        PosixPath
path <- PosixPath -> CString -> IO PosixPath
appendCString PosixPath
parent CString
dname
        Array Word8 -> (CString -> IO (Bool, Bool)) -> IO (Bool, Bool)
forall a b. Array a -> (CString -> IO b) -> IO b
Array.asCStringUnsafe (PosixPath -> Array Word8
forall a. IsPath PosixPath a => a -> Array Word8
Path.toChunk PosixPath
path) ((CString -> IO (Bool, Bool)) -> IO (Bool, Bool))
-> (CString -> IO (Bool, Bool)) -> IO (Bool, Bool)
forall a b. (a -> b) -> a -> b
$ \CString
cStr -> do
            CInt
res <- CString -> IO CInt
c_lstat_is_directory CString
cStr
            case CInt
res of
                CInt
x | CInt
x CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
1 -> (Bool, Bool) -> IO (Bool, Bool)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool
True, Bool
False)
                CInt
x | CInt
x CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0 -> (Bool, Bool) -> IO (Bool, Bool)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool
False, Bool
False)
                -- XXX Need to check if and how we should handle some errors
                -- like EACCES.
                CInt
_ -> String -> IO (Bool, Bool)
forall a. String -> IO a
throwErrno String
"checkIfDirectory"

-- | Checks if dname is a directory and additionaly returns if dname is a meta
-- directory.
{-# INLINE checkDirStatus #-}
checkDirStatus
    :: PosixPath -> Ptr CChar -> Word8 -> IO (Bool, Bool)
{-# LINE 175 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}

{-# LINE 178 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
checkDirStatus :: PosixPath -> CString -> Word8 -> IO (Bool, Bool)
checkDirStatus PosixPath
parent CString
dname Word8
dtype =
    if Word8
dtype Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== (Word8
4)
{-# LINE 180 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
    then do
        Bool
isMeta <- IO Bool -> IO Bool
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> IO Bool) -> IO Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ CString -> IO Bool
isMetaDir CString
dname
        (Bool, Bool) -> IO (Bool, Bool)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool
True, Bool
isMeta)
    else if Word8
dtype Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
0
{-# LINE 184 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
         then (Bool, Bool) -> IO (Bool, Bool)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool
False, Bool
False)
         else PosixPath -> CString -> IO (Bool, Bool)
lstatDname PosixPath
parent CString
dname

{-# LINE 187 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}

-------------------------------------------------------------------------------
-- streaming reads
-------------------------------------------------------------------------------

-- XXX We can use getdents64 directly so that we can use array slices from the
-- same buffer that we passed to the OS. That way we can also avoid any
-- overhead of bracket.
-- XXX Make this as Unfold to avoid returning Maybe
-- XXX Or NOINLINE some parts and inline the rest to fuse it
-- {-# INLINE readDirStreamEither #-}
readDirStreamEither ::
    -- DirStream -> IO (Either (Rel (Dir Path)) (Rel (File Path)))
    (PosixPath, DirStream) -> IO (Maybe (Either PosixPath PosixPath))
readDirStreamEither :: (PosixPath, DirStream) -> IO (Maybe (Either PosixPath PosixPath))
readDirStreamEither (PosixPath
curdir, (DirStream Ptr CDir
dirp)) = IO (Maybe (Either PosixPath PosixPath))
loop

  where

  -- mkPath :: IsPath (Rel (a Path)) => Array Word8 -> Rel (a Path)
  -- {-# INLINE mkPath #-}
  mkPath :: Array Word8 -> PosixPath
  mkPath :: Array Word8 -> PosixPath
mkPath = Array Word8 -> PosixPath
forall a. IsPath PosixPath a => Array Word8 -> a
Path.unsafeFromChunk

  loop :: IO (Maybe (Either PosixPath PosixPath))
loop = do
    IO ()
resetErrno
    Ptr CDirent
ptr <- Ptr CDir -> IO (Ptr CDirent)
c_readdir Ptr CDir
dirp
    if (Ptr CDirent
ptr Ptr CDirent -> Ptr CDirent -> Bool
forall a. Eq a => a -> a -> Bool
/= Ptr CDirent
forall a. Ptr a
nullPtr)
    then do
        let dname :: Ptr b
dname = (\Ptr CDirent
hsc_ptr -> Ptr CDirent
hsc_ptr Ptr CDirent -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
19) Ptr CDirent
ptr
{-# LINE 216 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
        Word8
dtype :: Word8 <- (\Ptr CDirent
hsc_ptr -> Ptr CDirent -> Int -> IO Word8
forall b. Ptr b -> Int -> IO Word8
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr CDirent
hsc_ptr Int
18) Ptr CDirent
ptr
{-# LINE 217 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
        -- dreclen :: #{type unsigned short} <- #{peek struct dirent, d_reclen} ptr
        -- It is possible to find the name length using dreclen and then use
        -- fromPtrN, but it is not straightforward because the reclen is
        -- padded to 8-byte boundary.
        Array Word8
name <- Ptr Word8 -> IO (Array Word8)
forall (m :: * -> *). MonadIO m => Ptr Word8 -> m (Array Word8)
Array.fromCString (Ptr Any -> Ptr Word8
forall a b. Ptr a -> Ptr b
castPtr Ptr Any
forall a. Ptr a
dname)
        (Bool
isDir, Bool
isMeta) <- PosixPath -> CString -> Word8 -> IO (Bool, Bool)
checkDirStatus PosixPath
curdir CString
forall a. Ptr a
dname Word8
dtype
        if Bool
isDir
        then do
            if Bool
isMeta
            then IO (Maybe (Either PosixPath PosixPath))
loop
            else Maybe (Either PosixPath PosixPath)
-> IO (Maybe (Either PosixPath PosixPath))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PosixPath PosixPath -> Maybe (Either PosixPath PosixPath)
forall a. a -> Maybe a
Just (PosixPath -> Either PosixPath PosixPath
forall a b. a -> Either a b
Left (Array Word8 -> PosixPath
mkPath Array Word8
name)))
        else Maybe (Either PosixPath PosixPath)
-> IO (Maybe (Either PosixPath PosixPath))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either PosixPath PosixPath -> Maybe (Either PosixPath PosixPath)
forall a. a -> Maybe a
Just (PosixPath -> Either PosixPath PosixPath
forall a b. b -> Either a b
Right (Array Word8 -> PosixPath
mkPath Array Word8
name)))
    else do
        Errno
errno <- IO Errno
getErrno
        if (Errno
errno Errno -> Errno -> Bool
forall a. Eq a => a -> a -> Bool
== Errno
eINTR)
        then IO (Maybe (Either PosixPath PosixPath))
loop
        else do
            let (Errno CInt
n) = Errno
errno
            if (CInt
n CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0)
            -- then return (Left (mkPath (Array.fromList [46])))
            then Maybe (Either PosixPath PosixPath)
-> IO (Maybe (Either PosixPath PosixPath))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (Either PosixPath PosixPath)
forall a. Maybe a
Nothing
            else String -> IO (Maybe (Either PosixPath PosixPath))
forall a. String -> IO a
throwErrno String
"readDirStreamEither"

-- XXX We can make this code common with windows, the path argument would be
-- redundant for windows case though.
{-# INLINE streamEitherReader #-}
streamEitherReader :: MonadIO m =>
    Unfold m (PosixPath, DirStream) (Either Path Path)
streamEitherReader :: forall (m :: * -> *).
MonadIO m =>
Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
streamEitherReader = ((PosixPath, DirStream)
 -> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath)))
-> ((PosixPath, DirStream) -> m (PosixPath, DirStream))
-> Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
forall (m :: * -> *) a b s.
(s -> m (Step s b)) -> (a -> m s) -> Unfold m a b
Unfold (PosixPath, DirStream)
-> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath))
forall {m :: * -> *}.
MonadIO m =>
(PosixPath, DirStream)
-> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath))
step (PosixPath, DirStream) -> m (PosixPath, DirStream)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return
    where

    step :: (PosixPath, DirStream)
-> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath))
step (PosixPath, DirStream)
s = do
        Maybe (Either PosixPath PosixPath)
r <- IO (Maybe (Either PosixPath PosixPath))
-> m (Maybe (Either PosixPath PosixPath))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe (Either PosixPath PosixPath))
 -> m (Maybe (Either PosixPath PosixPath)))
-> IO (Maybe (Either PosixPath PosixPath))
-> m (Maybe (Either PosixPath PosixPath))
forall a b. (a -> b) -> a -> b
$ (PosixPath, DirStream) -> IO (Maybe (Either PosixPath PosixPath))
readDirStreamEither (PosixPath, DirStream)
s
        case Maybe (Either PosixPath PosixPath)
r of
            Maybe (Either PosixPath PosixPath)
Nothing -> Step (PosixPath, DirStream) (Either PosixPath PosixPath)
-> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Step (PosixPath, DirStream) (Either PosixPath PosixPath)
forall s a. Step s a
Stop
            Just Either PosixPath PosixPath
x -> Step (PosixPath, DirStream) (Either PosixPath PosixPath)
-> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step (PosixPath, DirStream) (Either PosixPath PosixPath)
 -> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath)))
-> Step (PosixPath, DirStream) (Either PosixPath PosixPath)
-> m (Step (PosixPath, DirStream) (Either PosixPath PosixPath))
forall a b. (a -> b) -> a -> b
$ Either PosixPath PosixPath
-> (PosixPath, DirStream)
-> Step (PosixPath, DirStream) (Either PosixPath PosixPath)
forall s a. a -> s -> Step s a
Yield Either PosixPath PosixPath
x (PosixPath, DirStream)
s

{-# INLINE streamReader #-}
streamReader :: MonadIO m => Unfold m (PosixPath, DirStream) Path
streamReader :: forall (m :: * -> *).
MonadIO m =>
Unfold m (PosixPath, DirStream) PosixPath
streamReader = (Either PosixPath PosixPath -> PosixPath)
-> Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
-> Unfold m (PosixPath, DirStream) PosixPath
forall a b.
(a -> b)
-> Unfold m (PosixPath, DirStream) a
-> Unfold m (PosixPath, DirStream) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((PosixPath -> PosixPath)
-> (PosixPath -> PosixPath)
-> Either PosixPath PosixPath
-> PosixPath
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either PosixPath -> PosixPath
forall a. a -> a
id PosixPath -> PosixPath
forall a. a -> a
id) Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
forall (m :: * -> *).
MonadIO m =>
Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
streamEitherReader

{-# INLINE before #-}
before :: PosixPath -> IO (PosixPath, DirStream)
before :: PosixPath -> IO (PosixPath, DirStream)
before PosixPath
parent = (PosixPath
parent,) (DirStream -> (PosixPath, DirStream))
-> IO DirStream -> IO (PosixPath, DirStream)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> PosixPath -> IO DirStream
openDirStream PosixPath
parent

{-# INLINE after #-}
after :: (PosixPath, DirStream) -> IO ()
after :: (PosixPath, DirStream) -> IO ()
after (PosixPath
_, DirStream
dirStream) = DirStream -> IO ()
closeDirStream DirStream
dirStream

--  | Read a directory emitting a stream with names of the children. Filter out
--  "." and ".." entries.
--
--  /Internal/
--
{-# INLINE reader #-}
reader :: (MonadIO m, MonadCatch m) => Unfold m Path Path
reader :: forall (m :: * -> *).
(MonadIO m, MonadCatch m) =>
Unfold m PosixPath PosixPath
reader =
    -- XXX Instead of using bracketIO for each iteration of the loop we should
    -- instead yield a buffer of dir entries in each iteration and then use an
    -- unfold and concat to flatten those entries. That should improve the
    -- performance.
    (PosixPath -> IO (PosixPath, DirStream))
-> ((PosixPath, DirStream) -> IO ())
-> Unfold m (PosixPath, DirStream) PosixPath
-> Unfold m PosixPath PosixPath
forall (m :: * -> *) a c d b.
(MonadIO m, MonadCatch m) =>
(a -> IO c) -> (c -> IO d) -> Unfold m c b -> Unfold m a b
UF.bracketIO PosixPath -> IO (PosixPath, DirStream)
before (PosixPath, DirStream) -> IO ()
after Unfold m (PosixPath, DirStream) PosixPath
forall (m :: * -> *).
MonadIO m =>
Unfold m (PosixPath, DirStream) PosixPath
streamReader

-- | Read directories as Left and files as Right. Filter out "." and ".."
-- entries.
--
--  /Internal/
--
{-# INLINE eitherReader #-}
eitherReader :: (MonadIO m, MonadCatch m) =>
    Unfold m Path (Either Path Path)
eitherReader :: forall (m :: * -> *).
(MonadIO m, MonadCatch m) =>
Unfold m PosixPath (Either PosixPath PosixPath)
eitherReader =
    -- XXX The measured overhead of bracketIO is not noticeable, if it turns
    -- out to be a problem for small filenames we can use getdents64 to use
    -- chunked read to avoid the overhead.
    (PosixPath -> IO (PosixPath, DirStream))
-> ((PosixPath, DirStream) -> IO ())
-> Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
-> Unfold m PosixPath (Either PosixPath PosixPath)
forall (m :: * -> *) a c d b.
(MonadIO m, MonadCatch m) =>
(a -> IO c) -> (c -> IO d) -> Unfold m c b -> Unfold m a b
UF.bracketIO PosixPath -> IO (PosixPath, DirStream)
before (PosixPath, DirStream) -> IO ()
after Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
forall (m :: * -> *).
MonadIO m =>
Unfold m (PosixPath, DirStream) (Either PosixPath PosixPath)
streamEitherReader

{-# INLINE appendCString #-}
appendCString :: PosixPath -> CString -> IO PosixPath
appendCString :: PosixPath -> CString -> IO PosixPath
appendCString (PosixPath Array Word8
a) CString
b = do
    Array Word8
arr <- OS -> Array Word8 -> CString -> IO (Array Word8)
PathC.appendCString OS
PathC.Posix Array Word8
a CString
b
    PosixPath -> IO PosixPath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (PosixPath -> IO PosixPath) -> PosixPath -> IO PosixPath
forall a b. (a -> b) -> a -> b
$ Array Word8 -> PosixPath
PosixPath Array Word8
arr

{-# ANN type ChunkStreamState Fuse #-}
data ChunkStreamState =
      ChunkStreamInit [PosixPath] [PosixPath] Int [PosixPath] Int
    | ChunkStreamLoop
        PosixPath -- current dir path
        [PosixPath]  -- remaining dirs
        (Ptr CDir) -- current dir
        [PosixPath] -- dirs buffered
        Int    -- dir count
        [PosixPath] -- files buffered
        Int -- file count

-- XXX We can use a fold for collecting files and dirs.
-- XXX We can write a two fold scan to buffer and yield whichever fills first
-- like foldMany, it would be foldEither.
{-# INLINE readEitherChunks #-}
readEitherChunks :: MonadIO m => [PosixPath] -> Stream m (Either [PosixPath] [PosixPath])
readEitherChunks :: forall (m :: * -> *).
MonadIO m =>
[PosixPath] -> Stream m (Either [PosixPath] [PosixPath])
readEitherChunks [PosixPath]
alldirs =
    (State StreamK m (Either [PosixPath] [PosixPath])
 -> ChunkStreamState
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> ChunkStreamState -> Stream m (Either [PosixPath] [PosixPath])
forall (m :: * -> *) a s.
(State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
Stream State StreamK m (Either [PosixPath] [PosixPath])
-> ChunkStreamState
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall {m :: * -> *} {p}.
MonadIO m =>
p
-> ChunkStreamState
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
step ([PosixPath]
-> [PosixPath] -> Int -> [PosixPath] -> Int -> ChunkStreamState
ChunkStreamInit [PosixPath]
alldirs [] Int
0 [] Int
0)

    where

    -- We want to keep the dir batching as low as possible for better
    -- concurrency esp when the number of dirs is low.
    dirMax :: Int
dirMax = Int
4
    fileMax :: Int
fileMax = Int
1000

    step :: p
-> ChunkStreamState
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
step p
_ (ChunkStreamInit (PosixPath
x:[PosixPath]
xs) [PosixPath]
dirs Int
ndirs [PosixPath]
files Int
nfiles) = do
        DirStream Ptr CDir
dirp <- IO DirStream -> m DirStream
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO DirStream -> m DirStream) -> IO DirStream -> m DirStream
forall a b. (a -> b) -> a -> b
$ PosixPath -> IO DirStream
openDirStream PosixPath
x
        Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. s -> Step s a
Skip (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> [PosixPath]
-> Int
-> ChunkStreamState
ChunkStreamLoop PosixPath
x [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs [PosixPath]
files Int
nfiles)

    step p
_ (ChunkStreamInit [] [] Int
_ [] Int
_) =
        Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. Step s a
Stop

    step p
_ (ChunkStreamInit [] [] Int
_ [PosixPath]
files Int
_) =
        Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] [PosixPath]
-> ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] [PosixPath]
forall a b. b -> Either a b
Right [PosixPath]
files) ([PosixPath]
-> [PosixPath] -> Int -> [PosixPath] -> Int -> ChunkStreamState
ChunkStreamInit [] [] Int
0 [] Int
0)

    step p
_ (ChunkStreamInit [] [PosixPath]
dirs Int
_ [PosixPath]
files Int
_) =
        Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] [PosixPath]
-> ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] [PosixPath]
forall a b. a -> Either a b
Left [PosixPath]
dirs) ([PosixPath]
-> [PosixPath] -> Int -> [PosixPath] -> Int -> ChunkStreamState
ChunkStreamInit [] [] Int
0 [PosixPath]
files Int
0)

    step p
_ st :: ChunkStreamState
st@(ChunkStreamLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs [PosixPath]
files Int
nfiles) = do
        IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
resetErrno
        Ptr CDirent
dentPtr <- IO (Ptr CDirent) -> m (Ptr CDirent)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Ptr CDirent) -> m (Ptr CDirent))
-> IO (Ptr CDirent) -> m (Ptr CDirent)
forall a b. (a -> b) -> a -> b
$ Ptr CDir -> IO (Ptr CDirent)
c_readdir Ptr CDir
dirp
        if (Ptr CDirent
dentPtr Ptr CDirent -> Ptr CDirent -> Bool
forall a. Eq a => a -> a -> Bool
/= Ptr CDirent
forall a. Ptr a
nullPtr)
        then do
            let dname :: Ptr b
dname = (\Ptr CDirent
hsc_ptr -> Ptr CDirent
hsc_ptr Ptr CDirent -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
19) Ptr CDirent
dentPtr
{-# LINE 346 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
            Word8
dtype :: Word8 <-
{-# LINE 347 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
                IO Word8 -> m Word8
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Word8 -> m Word8) -> IO Word8 -> m Word8
forall a b. (a -> b) -> a -> b
$ (\Ptr CDirent
hsc_ptr -> Ptr CDirent -> Int -> IO Word8
forall b. Ptr b -> Int -> IO Word8
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr CDirent
hsc_ptr Int
18) Ptr CDirent
dentPtr
{-# LINE 348 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}

            (Bool
isDir, Bool
isMeta) <- IO (Bool, Bool) -> m (Bool, Bool)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Bool, Bool) -> m (Bool, Bool))
-> IO (Bool, Bool) -> m (Bool, Bool)
forall a b. (a -> b) -> a -> b
$ PosixPath -> CString -> Word8 -> IO (Bool, Bool)
checkDirStatus PosixPath
curdir CString
forall a. Ptr a
dname Word8
dtype
            if Bool
isDir
            then do
                if Bool
isMeta
                then Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. s -> Step s a
Skip ChunkStreamState
st
                else do
                     PosixPath
path <- IO PosixPath -> m PosixPath
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO PosixPath -> m PosixPath) -> IO PosixPath -> m PosixPath
forall a b. (a -> b) -> a -> b
$ PosixPath -> CString -> IO PosixPath
appendCString PosixPath
curdir CString
forall a. Ptr a
dname
                     let dirs1 :: [PosixPath]
dirs1 = PosixPath
path PosixPath -> [PosixPath] -> [PosixPath]
forall a. a -> [a] -> [a]
: [PosixPath]
dirs
                         ndirs1 :: Int
ndirs1 = Int
ndirs Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
                      in if Int
ndirs1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
dirMax
                         then Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] [PosixPath]
-> ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] [PosixPath]
forall a b. a -> Either a b
Left [PosixPath]
dirs1)
                            (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> [PosixPath]
-> Int
-> ChunkStreamState
ChunkStreamLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [] Int
0 [PosixPath]
files Int
nfiles)
                         else Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. s -> Step s a
Skip
                            (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> [PosixPath]
-> Int
-> ChunkStreamState
ChunkStreamLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs1 Int
ndirs1 [PosixPath]
files Int
nfiles)
            else do
                 PosixPath
path <- IO PosixPath -> m PosixPath
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO PosixPath -> m PosixPath) -> IO PosixPath -> m PosixPath
forall a b. (a -> b) -> a -> b
$ PosixPath -> CString -> IO PosixPath
appendCString PosixPath
curdir CString
forall a. Ptr a
dname
                 let files1 :: [PosixPath]
files1 = PosixPath
path PosixPath -> [PosixPath] -> [PosixPath]
forall a. a -> [a] -> [a]
: [PosixPath]
files
                     nfiles1 :: Int
nfiles1 = Int
nfiles Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
                  in if Int
nfiles1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
fileMax
                     then Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] [PosixPath]
-> ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] [PosixPath]
forall a b. b -> Either a b
Right [PosixPath]
files1)
                        (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> [PosixPath]
-> Int
-> ChunkStreamState
ChunkStreamLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs [] Int
0)
                     else Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. s -> Step s a
Skip
                        (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> [PosixPath]
-> Int
-> ChunkStreamState
ChunkStreamLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs [PosixPath]
files1 Int
nfiles1)
        else do
            Errno
errno <- IO Errno -> m Errno
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Errno
getErrno
            if (Errno
errno Errno -> Errno -> Bool
forall a. Eq a => a -> a -> Bool
== Errno
eINTR)
            then Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. s -> Step s a
Skip ChunkStreamState
st
            else do
                let (Errno CInt
n) = Errno
errno
                -- XXX Exception safety
                IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ DirStream -> IO ()
closeDirStream (Ptr CDir -> DirStream
DirStream Ptr CDir
dirp)
                if (CInt
n CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0)
                then Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamState (Either [PosixPath] [PosixPath])
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ ChunkStreamState
-> Step ChunkStreamState (Either [PosixPath] [PosixPath])
forall s a. s -> Step s a
Skip ([PosixPath]
-> [PosixPath] -> Int -> [PosixPath] -> Int -> ChunkStreamState
ChunkStreamInit [PosixPath]
xs [PosixPath]
dirs Int
ndirs [PosixPath]
files Int
nfiles)
                else IO (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
 -> m (Step ChunkStreamState (Either [PosixPath] [PosixPath])))
-> IO (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
-> m (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a b. (a -> b) -> a -> b
$ String
-> IO (Step ChunkStreamState (Either [PosixPath] [PosixPath]))
forall a. String -> IO a
throwErrno String
"readEitherChunks"

foreign import ccall unsafe "string.h memcpy" c_memcpy
    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)

-- See also cstringLength# in GHC.CString in ghc-prim
foreign import ccall unsafe "string.h strlen" c_strlen
    :: Ptr CChar -> IO CSize

{-# ANN type ChunkStreamByteState Fuse #-}
data ChunkStreamByteState =
      ChunkStreamByteInit0
    | ChunkStreamByteInit [PosixPath] [PosixPath] Int MutByteArray Int
    | ChunkStreamByteLoop
        PosixPath -- current dir path
        [PosixPath]  -- remaining dirs
        (Ptr CDir) -- current dir
        [PosixPath] -- dirs buffered
        Int    -- dir count
        MutByteArray
        Int
    | ChunkStreamByteLoopPending
        (Ptr CChar) -- pending item
        PosixPath -- current dir path
        [PosixPath]  -- remaining dirs
        (Ptr CDir) -- current dir
        MutByteArray
        Int

-- XXX Add follow-symlinks option.
-- XXX Detect cycles.

-- XXX We can also emit both files and directories together this will be
-- especially useful when we are emitting chunks.
--
-- Since we are separating paths by newlines, it cannot support newlines in
-- paths. Or we can return null separated paths as well. Provide a Mut array
-- API to replace the nulls with newlines in-place.
--
-- We can pass a fold to make this modular, but if we are passing readdir
-- managed memory then we will have to consume it immediately. Otherwise we can
-- use getdents64 directly and use GHC managed memory instead.

-- | Left is directories. Right is a buffer containing directories and files
-- separated by newlines.
{-# INLINE readEitherByteChunks #-}
readEitherByteChunks :: MonadIO m =>
    [PosixPath] -> Stream m (Either [PosixPath] (Array Word8))
readEitherByteChunks :: forall (m :: * -> *).
MonadIO m =>
[PosixPath] -> Stream m (Either [PosixPath] (Array Word8))
readEitherByteChunks [PosixPath]
alldirs =
    (State StreamK m (Either [PosixPath] (Array Word8))
 -> ChunkStreamByteState
 -> m (Step
         ChunkStreamByteState (Either [PosixPath] (Array Word8))))
-> ChunkStreamByteState
-> Stream m (Either [PosixPath] (Array Word8))
forall (m :: * -> *) a s.
(State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
Stream State StreamK m (Either [PosixPath] (Array Word8))
-> ChunkStreamByteState
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array Word8)))
forall {m :: * -> *} {p} {a}.
MonadIO m =>
p
-> ChunkStreamByteState
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
step (ChunkStreamByteState
ChunkStreamByteInit0)

    where

    -- XXX A single worker may not have enough directories to list at once to
    -- fill up a large buffer. We need to change the concurrency model such
    -- that a worker should be able to pick up another dir from the queue
    -- without emitting an output until the buffer fills.
    --
    -- XXX A worker can also pick up multiple work items in one go. However, we
    -- also need to keep in mind that any kind of batching might have
    -- pathological cases where concurrency may be reduced.
    --
    -- XXX Alternatively, we can distribute the dir stream over multiple
    -- concurrent folds and return (monadic output) a stream of arrays created
    -- from the output channel, then consume that stream by using a monad bind.
    bufSize :: Int
bufSize = Int
4000

    copyToBuf :: MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
dstArr Int
pos PosixPath
dirPath CString
name = do
        Int
nameLen <- (CSize -> Int) -> m CSize -> m Int
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (IO CSize -> m CSize
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO CSize -> m CSize) -> IO CSize -> m CSize
forall a b. (a -> b) -> a -> b
$ CString -> IO CSize
c_strlen CString
name)
        let PosixPath (Array MutByteArray
dirArr Int
start Int
end) = PosixPath
dirPath
            dirLen :: Int
dirLen = Int
end Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
start
            -- XXX We may need to decode and encode the path if the
            -- output encoding differs from fs encoding.
            --
            -- Account for separator and newline bytes.
            byteCount :: Int
byteCount = Int
dirLen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
nameLen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2
        if Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
byteCount Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
bufSize
        then do
            -- XXX append a path separator to a dir path
            -- We know it is already pinned.
            MutByteArray -> (Ptr Any -> IO ()) -> m ()
forall (m :: * -> *) a b.
MonadIO m =>
MutByteArray -> (Ptr a -> IO b) -> m b
MutByteArray.unsafeAsPtr MutByteArray
dstArr (\Ptr Any
ptr -> IO () -> IO ()
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
                MutByteArray -> Int -> MutByteArray -> Int -> Int -> IO ()
forall (m :: * -> *).
MonadIO m =>
MutByteArray -> Int -> MutByteArray -> Int -> Int -> m ()
MutByteArray.unsafePutSlice  MutByteArray
dirArr Int
start MutByteArray
dstArr Int
pos Int
dirLen
                let ptr1 :: Ptr b
ptr1 = Ptr Any
ptr Ptr Any -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` (Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
dirLen)
                    separator :: Word8
separator = Word8
47 :: Word8
                Ptr Word8 -> Word8 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr Word8
forall a. Ptr a
ptr1 Word8
separator
                let ptr2 :: Ptr b
ptr2 = Ptr Any
forall a. Ptr a
ptr1 Ptr Any -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
1
                Ptr Word8
_ <- Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
c_memcpy Ptr Word8
forall a. Ptr a
ptr2 (CString -> Ptr Word8
forall a b. Ptr a -> Ptr b
castPtr CString
name) (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
nameLen)
                let ptr3 :: Ptr b
ptr3 = Ptr Any
forall a. Ptr a
ptr2 Ptr Any -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
nameLen
                    newline :: Word8
newline = Word8
10 :: Word8
                Ptr Word8 -> Word8 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr Word8
forall a. Ptr a
ptr3 Word8
newline
                )
            Maybe Int -> m (Maybe Int)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
byteCount))
        else Maybe Int -> m (Maybe Int)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Int
forall a. Maybe a
Nothing

    step :: p
-> ChunkStreamByteState
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
step p
_ ChunkStreamByteState
ChunkStreamByteInit0 = do
        MutByteArray
mbarr <- IO MutByteArray -> m MutByteArray
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO MutByteArray -> m MutByteArray)
-> IO MutByteArray -> m MutByteArray
forall a b. (a -> b) -> a -> b
$ Int -> IO MutByteArray
MutByteArray.new' Int
bufSize
        Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip ([PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteInit [PosixPath]
alldirs [] Int
0 MutByteArray
mbarr Int
0)

    step p
_ (ChunkStreamByteInit (PosixPath
x:[PosixPath]
xs) [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos) = do
        DirStream Ptr CDir
dirp <- IO DirStream -> m DirStream
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO DirStream -> m DirStream) -> IO DirStream -> m DirStream
forall a b. (a -> b) -> a -> b
$ PosixPath -> IO DirStream
openDirStream PosixPath
x
        Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoop PosixPath
x [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos)

    step p
_ (ChunkStreamByteInit [] [] Int
_ MutByteArray
_ Int
pos) | Int
pos Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
        Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. Step s a
Stop

    step p
_ (ChunkStreamByteInit [] [] Int
_ MutByteArray
mbarr Int
pos) =
        Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] (Array a)
-> ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. a -> s -> Step s a
Yield (Array a -> Either [PosixPath] (Array a)
forall a b. b -> Either a b
Right (MutByteArray -> Int -> Int -> Array a
forall a. MutByteArray -> Int -> Int -> Array a
Array MutByteArray
mbarr Int
0 Int
pos)) ([PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteInit [] [] Int
0 MutByteArray
mbarr Int
0)

    step p
_ (ChunkStreamByteInit [] [PosixPath]
dirs Int
_ MutByteArray
mbarr Int
pos) =
        Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] (Array a)
-> ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] (Array a)
forall a b. a -> Either a b
Left [PosixPath]
dirs) ([PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteInit [] [] Int
0 MutByteArray
mbarr Int
pos)

    step p
_ (ChunkStreamByteLoopPending CString
pending PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp MutByteArray
mbarr Int
pos) = do
        MutByteArray
mbarr1 <- IO MutByteArray -> m MutByteArray
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO MutByteArray -> m MutByteArray)
-> IO MutByteArray -> m MutByteArray
forall a b. (a -> b) -> a -> b
$ Int -> IO MutByteArray
MutByteArray.new' Int
bufSize
        Maybe Int
r1 <- MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
forall {m :: * -> *}.
MonadIO m =>
MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
mbarr1 Int
0 PosixPath
curdir CString
pending
        case Maybe Int
r1 of
            Just Int
pos2 ->
                Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] (Array a)
-> ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. a -> s -> Step s a
Yield (Array a -> Either [PosixPath] (Array a)
forall a b. b -> Either a b
Right (MutByteArray -> Int -> Int -> Array a
forall a. MutByteArray -> Int -> Int -> Array a
Array MutByteArray
mbarr Int
0 Int
pos))
                    -- When we come in this state we have emitted dirs
                    (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [] Int
0 MutByteArray
mbarr1 Int
pos2)
            Maybe Int
Nothing -> String
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. HasCallStack => String -> a
error String
"Dirname too big for bufSize"

    step p
_ st :: ChunkStreamByteState
st@(ChunkStreamByteLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos) = do
        IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
resetErrno
        Ptr CDirent
dentPtr <- IO (Ptr CDirent) -> m (Ptr CDirent)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Ptr CDirent) -> m (Ptr CDirent))
-> IO (Ptr CDirent) -> m (Ptr CDirent)
forall a b. (a -> b) -> a -> b
$ Ptr CDir -> IO (Ptr CDirent)
c_readdir Ptr CDir
dirp
        if (Ptr CDirent
dentPtr Ptr CDirent -> Ptr CDirent -> Bool
forall a. Eq a => a -> a -> Bool
/= Ptr CDirent
forall a. Ptr a
nullPtr)
        then do
            let dname :: Ptr b
dname = (\Ptr CDirent
hsc_ptr -> Ptr CDirent
hsc_ptr Ptr CDirent -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
19) Ptr CDirent
dentPtr
{-# LINE 509 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
            Word8
dtype :: Word8 <-
{-# LINE 510 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
                IO Word8 -> m Word8
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Word8 -> m Word8) -> IO Word8 -> m Word8
forall a b. (a -> b) -> a -> b
$ (\Ptr CDirent
hsc_ptr -> Ptr CDirent -> Int -> IO Word8
forall b. Ptr b -> Int -> IO Word8
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr CDirent
hsc_ptr Int
18) Ptr CDirent
dentPtr
{-# LINE 511 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}

            -- XXX Skips come around the entire loop, does that impact perf
            -- because it has a StreamK in the middle.
            -- Keep the file check first as it is more likely
            (Bool
isDir, Bool
isMeta) <- IO (Bool, Bool) -> m (Bool, Bool)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Bool, Bool) -> m (Bool, Bool))
-> IO (Bool, Bool) -> m (Bool, Bool)
forall a b. (a -> b) -> a -> b
$ PosixPath -> CString -> Word8 -> IO (Bool, Bool)
checkDirStatus PosixPath
curdir CString
forall a. Ptr a
dname Word8
dtype
            if Bool -> Bool
not Bool
isDir
            then do
                    Maybe Int
r <- MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
forall {m :: * -> *}.
MonadIO m =>
MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
mbarr Int
pos PosixPath
curdir CString
forall a. Ptr a
dname
                    case Maybe Int
r of
                        Just Int
pos1 ->
                            Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip
                                (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos1)
                        Maybe Int
Nothing -> do
                            -- XXX we do not need to yield the out dirs here
                            -- XXX But we should yield if the number of dirs
                            -- become more than a threshold.
                            if Int
ndirs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
                            then
                                Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] (Array a)
-> ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] (Array a)
forall a b. a -> Either a b
Left [PosixPath]
dirs)
                                    (CString
-> PosixPath
-> [PosixPath]
-> Ptr CDir
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoopPending CString
forall a. Ptr a
dname PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp MutByteArray
mbarr Int
pos)
                            else
                                Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip
                                    (CString
-> PosixPath
-> [PosixPath]
-> Ptr CDir
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoopPending CString
forall a. Ptr a
dname PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp MutByteArray
mbarr Int
pos)
            else do
                if Bool
isMeta
                then Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip ChunkStreamByteState
st
                else do
                    PosixPath
path <- IO PosixPath -> m PosixPath
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO PosixPath -> m PosixPath) -> IO PosixPath -> m PosixPath
forall a b. (a -> b) -> a -> b
$ PosixPath -> CString -> IO PosixPath
appendCString PosixPath
curdir CString
forall a. Ptr a
dname
                    let dirs1 :: [PosixPath]
dirs1 = PosixPath
path PosixPath -> [PosixPath] -> [PosixPath]
forall a. a -> [a] -> [a]
: [PosixPath]
dirs
                        ndirs1 :: Int
ndirs1 = Int
ndirs Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
                    Maybe Int
r <- MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
forall {m :: * -> *}.
MonadIO m =>
MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
mbarr Int
pos PosixPath
curdir CString
forall a. Ptr a
dname
                    case Maybe Int
r of
                        Just Int
pos1 ->
                            Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip
                                (PosixPath
-> [PosixPath]
-> Ptr CDir
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoop PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp [PosixPath]
dirs1 Int
ndirs1 MutByteArray
mbarr Int
pos1)
                        Maybe Int
Nothing -> do
                            -- We know dirs1 in not empty here
                            -- XXX Yield only if dirs are more than a threshold
                            -- otherwise skip.
                            Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ Either [PosixPath] (Array a)
-> ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. a -> s -> Step s a
Yield ([PosixPath] -> Either [PosixPath] (Array a)
forall a b. a -> Either a b
Left [PosixPath]
dirs1)
                                (CString
-> PosixPath
-> [PosixPath]
-> Ptr CDir
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteLoopPending CString
forall a. Ptr a
dname PosixPath
curdir [PosixPath]
xs Ptr CDir
dirp MutByteArray
mbarr Int
pos)
        else do
            Errno
errno <- IO Errno -> m Errno
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Errno
getErrno
            if (Errno
errno Errno -> Errno -> Bool
forall a. Eq a => a -> a -> Bool
== Errno
eINTR)
            then Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip ChunkStreamByteState
st
            else do
                let (Errno CInt
n) = Errno
errno
                -- XXX Exception safety
                IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ DirStream -> IO ()
closeDirStream (Ptr CDir -> DirStream
DirStream Ptr CDir
dirp)
                if (CInt
n CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0)
                then Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ChunkStreamByteState (Either [PosixPath] (Array a))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ ChunkStreamByteState
-> Step ChunkStreamByteState (Either [PosixPath] (Array a))
forall s a. s -> Step s a
Skip ([PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ChunkStreamByteState
ChunkStreamByteInit [PosixPath]
xs [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos)
                else IO (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
 -> m (Step ChunkStreamByteState (Either [PosixPath] (Array a))))
-> IO (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
-> m (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a b. (a -> b) -> a -> b
$ String
-> IO (Step ChunkStreamByteState (Either [PosixPath] (Array a)))
forall a. String -> IO a
throwErrno String
"readEitherByteChunks"

{-# ANN type ByteChunksAt Fuse #-}
data ByteChunksAt =
      ByteChunksAtInit0
    | ByteChunksAtInit
        Fd
        [PosixPath] -- input dirs
        -- (Handle, [PosixPath]) -- output dirs
        -- Int -- count of output dirs
        MutByteArray -- output files and dirs
        Int -- position in MutByteArray
    | ByteChunksAtLoop
        Fd
        (Ptr CDir) -- current dir stream
        PosixPath -- current dir path
        [PosixPath]  -- remaining dirs
        [PosixPath] -- output dirs
        Int    -- output dir count
        MutByteArray
        Int
    | ByteChunksAtRealloc
        (Ptr CChar) -- pending item
        Fd
        (Ptr CDir) -- current dir stream
        PosixPath -- current dir path
        [PosixPath]  -- remaining dirs
        [PosixPath] -- output dirs
        Int    -- output dir count
        MutByteArray
        Int

-- The advantage of readEitherByteChunks over readEitherByteChunksAt is that we
-- do not need to open the dir handles and thus requires less open fd.
{-# INLINE readEitherByteChunksAt #-}
readEitherByteChunksAt :: MonadIO m =>
       -- (parent dir path, child dir paths rel to parent)
       (PosixPath, [PosixPath])
    -> Stream m (Either (PosixPath, [PosixPath]) (Array Word8))
readEitherByteChunksAt :: forall (m :: * -> *).
MonadIO m =>
(PosixPath, [PosixPath])
-> Stream m (Either (PosixPath, [PosixPath]) (Array Word8))
readEitherByteChunksAt (PosixPath
ppath, [PosixPath]
alldirs) =
    (State StreamK m (Either (PosixPath, [PosixPath]) (Array Word8))
 -> ByteChunksAt
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array Word8))))
-> ByteChunksAt
-> Stream m (Either (PosixPath, [PosixPath]) (Array Word8))
forall (m :: * -> *) a s.
(State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
Stream State StreamK m (Either (PosixPath, [PosixPath]) (Array Word8))
-> ByteChunksAt
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array Word8)))
forall {m :: * -> *} {p} {a}.
MonadIO m =>
p
-> ByteChunksAt
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
step (ByteChunksAt
ByteChunksAtInit0)

    where

    bufSize :: Int
bufSize = Int
4000

    copyToBuf :: MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
dstArr Int
pos PosixPath
dirPath CString
name = do
        Int
nameLen <- (CSize -> Int) -> m CSize -> m Int
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CSize -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (IO CSize -> m CSize
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO CSize -> m CSize) -> IO CSize -> m CSize
forall a b. (a -> b) -> a -> b
$ CString -> IO CSize
c_strlen CString
name)
        -- XXX prepend ppath to dirPath
        let PosixPath (Array MutByteArray
dirArr Int
start Int
end) = PosixPath
dirPath
            dirLen :: Int
dirLen = Int
end Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
start
            -- XXX We may need to decode and encode the path if the
            -- output encoding differs from fs encoding.
            --
            -- Account for separator and newline bytes.
            byteCount :: Int
byteCount = Int
dirLen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
nameLen Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2
        if Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
byteCount Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
bufSize
        then do
            -- XXX append a path separator to a dir path
            -- We know it is already pinned.
            MutByteArray -> (Ptr Any -> IO ()) -> m ()
forall (m :: * -> *) a b.
MonadIO m =>
MutByteArray -> (Ptr a -> IO b) -> m b
MutByteArray.unsafeAsPtr MutByteArray
dstArr (\Ptr Any
ptr -> IO () -> IO ()
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
                MutByteArray -> Int -> MutByteArray -> Int -> Int -> IO ()
forall (m :: * -> *).
MonadIO m =>
MutByteArray -> Int -> MutByteArray -> Int -> Int -> m ()
MutByteArray.unsafePutSlice  MutByteArray
dirArr Int
start MutByteArray
dstArr Int
pos Int
dirLen
                let ptr1 :: Ptr b
ptr1 = Ptr Any
ptr Ptr Any -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` (Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
dirLen)
                    separator :: Word8
separator = Word8
47 :: Word8
                Ptr Word8 -> Word8 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr Word8
forall a. Ptr a
ptr1 Word8
separator
                let ptr2 :: Ptr b
ptr2 = Ptr Any
forall a. Ptr a
ptr1 Ptr Any -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
1
                Ptr Word8
_ <- Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
c_memcpy Ptr Word8
forall a. Ptr a
ptr2 (CString -> Ptr Word8
forall a b. Ptr a -> Ptr b
castPtr CString
name) (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
nameLen)
                let ptr3 :: Ptr b
ptr3 = Ptr Any
forall a. Ptr a
ptr2 Ptr Any -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
nameLen
                    newline :: Word8
newline = Word8
10 :: Word8
                Ptr Word8 -> Word8 -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr Word8
forall a. Ptr a
ptr3 Word8
newline
                )
            Maybe Int -> m (Maybe Int)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int
pos Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
byteCount))
        else Maybe Int -> m (Maybe Int)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Int
forall a. Maybe a
Nothing

    step :: p
-> ByteChunksAt
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
step p
_ ByteChunksAt
ByteChunksAtInit0 = do
        Fd
pfd <- IO Fd -> m Fd
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Fd -> m Fd) -> IO Fd -> m Fd
forall a b. (a -> b) -> a -> b
$ PosixPath -> OpenMode -> IO Fd
openFd PosixPath
ppath OpenMode
ReadOnly
        MutByteArray
mbarr <- IO MutByteArray -> m MutByteArray
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO MutByteArray -> m MutByteArray)
-> IO MutByteArray -> m MutByteArray
forall a b. (a -> b) -> a -> b
$ Int -> IO MutByteArray
MutByteArray.new' Int
bufSize
        Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip (Fd -> [PosixPath] -> MutByteArray -> Int -> ByteChunksAt
ByteChunksAtInit Fd
pfd [PosixPath]
alldirs MutByteArray
mbarr Int
0)

    step p
_ (ByteChunksAtInit Fd
ph (PosixPath
x:[PosixPath]
xs) MutByteArray
mbarr Int
pos) = do
        (DirStream Ptr CDir
dirp) <- IO DirStream -> m DirStream
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO DirStream -> m DirStream) -> IO DirStream -> m DirStream
forall a b. (a -> b) -> a -> b
$ Fd -> PosixPath -> IO DirStream
openDirStreamAt Fd
ph PosixPath
x
        Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip (Fd
-> Ptr CDir
-> PosixPath
-> [PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ByteChunksAt
ByteChunksAtLoop Fd
ph Ptr CDir
dirp PosixPath
x [PosixPath]
xs [] Int
0 MutByteArray
mbarr Int
pos)

    step p
_ (ByteChunksAtInit Fd
pfd [] MutByteArray
_ Int
0) = do
        IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Fd -> IO ()
closeFd (Fd
pfd)
        Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. Step s a
Stop

    step p
_ (ByteChunksAtInit Fd
pfd [] MutByteArray
mbarr Int
pos) = do
        Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return
            (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ Either (PosixPath, [PosixPath]) (Array a)
-> ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. a -> s -> Step s a
Yield
                (Array a -> Either (PosixPath, [PosixPath]) (Array a)
forall a b. b -> Either a b
Right (MutByteArray -> Int -> Int -> Array a
forall a. MutByteArray -> Int -> Int -> Array a
Array MutByteArray
mbarr Int
0 Int
pos))
                (Fd -> [PosixPath] -> MutByteArray -> Int -> ByteChunksAt
ByteChunksAtInit Fd
pfd [] MutByteArray
mbarr Int
0)

    step p
_ (ByteChunksAtRealloc CString
pending Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos) = do
        MutByteArray
mbarr1 <- IO MutByteArray -> m MutByteArray
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO MutByteArray -> m MutByteArray)
-> IO MutByteArray -> m MutByteArray
forall a b. (a -> b) -> a -> b
$ Int -> IO MutByteArray
MutByteArray.new' Int
bufSize
        Maybe Int
r1 <- MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
forall {m :: * -> *}.
MonadIO m =>
MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
mbarr1 Int
0 PosixPath
curdir CString
pending
        case Maybe Int
r1 of
            Just Int
pos2 ->
                Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ Either (PosixPath, [PosixPath]) (Array a)
-> ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. a -> s -> Step s a
Yield (Array a -> Either (PosixPath, [PosixPath]) (Array a)
forall a b. b -> Either a b
Right (MutByteArray -> Int -> Int -> Array a
forall a. MutByteArray -> Int -> Int -> Array a
Array MutByteArray
mbarr Int
0 Int
pos))
                    (Fd
-> Ptr CDir
-> PosixPath
-> [PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ByteChunksAt
ByteChunksAtLoop Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs Int
ndirs MutByteArray
mbarr1 Int
pos2)
            Maybe Int
Nothing -> String
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. HasCallStack => String -> a
error String
"Dirname too big for bufSize"

    step p
_ st :: ByteChunksAt
st@(ByteChunksAtLoop Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos) = do
        IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
resetErrno
        Ptr CDirent
dentPtr <- IO (Ptr CDirent) -> m (Ptr CDirent)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Ptr CDirent) -> m (Ptr CDirent))
-> IO (Ptr CDirent) -> m (Ptr CDirent)
forall a b. (a -> b) -> a -> b
$ Ptr CDir -> IO (Ptr CDirent)
c_readdir Ptr CDir
dirp
        if (Ptr CDirent
dentPtr Ptr CDirent -> Ptr CDirent -> Bool
forall a. Eq a => a -> a -> Bool
/= Ptr CDirent
forall a. Ptr a
nullPtr)
        then do
            let dname :: Ptr b
dname = (\Ptr CDirent
hsc_ptr -> Ptr CDirent
hsc_ptr Ptr CDirent -> Int -> Ptr b
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
19) Ptr CDirent
dentPtr
{-# LINE 670 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
            Word8
dtype :: Word8 <-
{-# LINE 671 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}
                IO Word8 -> m Word8
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Word8 -> m Word8) -> IO Word8 -> m Word8
forall a b. (a -> b) -> a -> b
$ (\Ptr CDirent
hsc_ptr -> Ptr CDirent -> Int -> IO Word8
forall b. Ptr b -> Int -> IO Word8
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr CDirent
hsc_ptr Int
18) Ptr CDirent
dentPtr
{-# LINE 672 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}

            -- Keep the file check first as it is more likely
            (Bool
isDir, Bool
isMeta) <- IO (Bool, Bool) -> m (Bool, Bool)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Bool, Bool) -> m (Bool, Bool))
-> IO (Bool, Bool) -> m (Bool, Bool)
forall a b. (a -> b) -> a -> b
$ PosixPath -> CString -> Word8 -> IO (Bool, Bool)
checkDirStatus PosixPath
curdir CString
forall a. Ptr a
dname Word8
dtype
            if Bool -> Bool
not Bool
isDir
            then do
                    Maybe Int
r <- MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
forall {m :: * -> *}.
MonadIO m =>
MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
mbarr Int
pos PosixPath
curdir CString
forall a. Ptr a
dname
                    case Maybe Int
r of
                        Just Int
pos1 ->
                            Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip
                                (Fd
-> Ptr CDir
-> PosixPath
-> [PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ByteChunksAt
ByteChunksAtLoop
                                    Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos1)
                        Maybe Int
Nothing ->
                            Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip
                                (CString
-> Fd
-> Ptr CDir
-> PosixPath
-> [PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ByteChunksAt
ByteChunksAtRealloc
                                    CString
forall a. Ptr a
dname Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs Int
ndirs MutByteArray
mbarr Int
pos)
            else do
                if Bool
isMeta
                then Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip ByteChunksAt
st
                else do
                    Array Word8
arr <- Ptr Word8 -> m (Array Word8)
forall (m :: * -> *). MonadIO m => Ptr Word8 -> m (Array Word8)
Array.fromCString (Ptr Any -> Ptr Word8
forall a b. Ptr a -> Ptr b
castPtr Ptr Any
forall a. Ptr a
dname)
                    let path :: PosixPath
path = Array Word8 -> PosixPath
forall a. IsPath PosixPath a => Array Word8 -> a
Path.unsafeFromChunk Array Word8
arr
                    let dirs1 :: [PosixPath]
dirs1 = PosixPath
path PosixPath -> [PosixPath] -> [PosixPath]
forall a. a -> [a] -> [a]
: [PosixPath]
dirs
                        ndirs1 :: Int
ndirs1 = Int
ndirs Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
                    Maybe Int
r <- MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
forall {m :: * -> *}.
MonadIO m =>
MutByteArray -> Int -> PosixPath -> CString -> m (Maybe Int)
copyToBuf MutByteArray
mbarr Int
pos PosixPath
curdir CString
forall a. Ptr a
dname
                    case Maybe Int
r of
                        Just Int
pos1 ->
                            -- XXX When there is less parallelization at the
                            -- top of the tree, we should use smaller chunks.
                            {-
                            if ndirs > 64
                            then do
                                let fpath = Path.unsafeAppend ppath curdir
                                return $ Yield
                                    (Left (fpath, dirs1))
                                    (ByteChunksAtLoop pfd dirp curdir xs [] 0 mbarr pos1)
                            else
                            -}
                                Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip
                                    (Fd
-> Ptr CDir
-> PosixPath
-> [PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ByteChunksAt
ByteChunksAtLoop
                                        Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs1 Int
ndirs1 MutByteArray
mbarr Int
pos1)
                        Maybe Int
Nothing -> do
                            Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip
                                (CString
-> Fd
-> Ptr CDir
-> PosixPath
-> [PosixPath]
-> [PosixPath]
-> Int
-> MutByteArray
-> Int
-> ByteChunksAt
ByteChunksAtRealloc
                                    CString
forall a. Ptr a
dname Fd
pfd Ptr CDir
dirp PosixPath
curdir [PosixPath]
xs [PosixPath]
dirs1 Int
ndirs1 MutByteArray
mbarr Int
pos)
        else do
            Errno
errno <- IO Errno -> m Errno
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Errno
getErrno
            if (Errno
errno Errno -> Errno -> Bool
forall a. Eq a => a -> a -> Bool
== Errno
eINTR)
            then Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip ByteChunksAt
st
            else do
                let (Errno CInt
n) = Errno
errno
                -- XXX What if an exception occurs in the code before this?
                -- Should we attach a weak IORef to close the fd on GC.
                IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ DirStream -> IO ()
closeDirStream (Ptr CDir -> DirStream
DirStream Ptr CDir
dirp)
                if (CInt
n CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
== CInt
0)
                then
                    -- XXX Yielding on each dir completion may hurt perf when
                    -- there are many small directories. However, it may also
                    -- help parallelize more in IO bound case.
                    if Int
ndirs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0
                    then do
                        let fpath :: PosixPath
fpath = PosixPath -> PosixPath -> PosixPath
Path.unsafeAppend PosixPath
ppath PosixPath
curdir
                        Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ Either (PosixPath, [PosixPath]) (Array a)
-> ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. a -> s -> Step s a
Yield
                            ((PosixPath, [PosixPath])
-> Either (PosixPath, [PosixPath]) (Array a)
forall a b. a -> Either a b
Left (PosixPath
fpath, [PosixPath]
dirs))
                            (Fd -> [PosixPath] -> MutByteArray -> Int -> ByteChunksAt
ByteChunksAtInit Fd
pfd [PosixPath]
xs MutByteArray
mbarr Int
pos)
                    else Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ ByteChunksAt
-> Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))
forall s a. s -> Step s a
Skip (Fd -> [PosixPath] -> MutByteArray -> Int -> ByteChunksAt
ByteChunksAtInit Fd
pfd [PosixPath]
xs MutByteArray
mbarr Int
pos)
                else IO (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
 -> m (Step
         ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a))))
-> IO
     (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
-> m (Step
        ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a b. (a -> b) -> a -> b
$ String
-> IO
     (Step ByteChunksAt (Either (PosixPath, [PosixPath]) (Array a)))
forall a. String -> IO a
throwErrno String
"readEitherByteChunks"

{-# LINE 739 "src/Streamly/Internal/FileSystem/Posix/ReadDir.hsc" #-}