Skip to content

Commit 14c3a28

Browse files
mmhathasufell
authored andcommitted
Added readDirStreamWithPtr
This version of `readDirStreamWith` takes a pre-allocated pointer as an additional argument that is used to store the pointer to the dirent. It is useful when you want to read a lot of entries and want to safe a few allocations since you can reuse the pointer for each call to `readDirStreamWithPtr`.
1 parent d8fd147 commit 14c3a28

File tree

2 files changed

+48
-20
lines changed

2 files changed

+48
-20
lines changed

System/Posix/Directory.hsc

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ module System.Posix.Directory (
3434
readDirStream,
3535
readDirStreamMaybe,
3636
readDirStreamWith,
37+
readDirStreamWithPtr,
3738
rewindDirStream,
3839
closeDirStream,
3940
DirStreamOffset,
@@ -114,26 +115,38 @@ readDirStreamMaybe = readDirStreamWith
114115
-- invocation of the callback and it will be freed automatically after. Do not
115116
-- pass it to the outside world!
116117
readDirStreamWith :: (DirEnt -> IO a) -> DirStream -> IO (Maybe a)
117-
readDirStreamWith f (DirStream dirp) =
118-
alloca $ \ptr_dEnt -> loop ptr_dEnt
119-
where
120-
loop ptr_dEnt = do
121-
resetErrno
122-
r <- c_readdir dirp ptr_dEnt
123-
if (r == 0)
124-
then do dEnt <- peek ptr_dEnt
125-
if (dEnt == nullPtr)
126-
then return Nothing
127-
else do
128-
res <- f (DirEnt dEnt)
129-
c_freeDirEnt dEnt
130-
return (Just res)
131-
else do errno <- getErrno
132-
if (errno == eINTR) then loop ptr_dEnt else do
133-
let (Errno eo) = errno
134-
if (eo == 0)
135-
then return Nothing
136-
else throwErrno "readDirStream"
118+
readDirStreamWith f dstream = alloca
119+
(\ptr_dEnt -> readDirStreamWithPtr ptr_dEnt f dstream)
120+
121+
-- | A version of 'readDirStreamWith' that takes a pre-allocated pointer in
122+
-- addition to the other arguments. This pointer is used to store the pointer
123+
-- to the next directory entry, if there is any. This function is intended for
124+
-- usecases where you need to read a lot of directory entries and want to
125+
-- reuse the pointer for each of them. Using for example 'readDirStream' or
126+
-- 'readDirStreamWith' in this scenario would allocate a new pointer for each
127+
-- call of these functions.
128+
--
129+
-- __NOTE__: You are responsible for releasing the pointer after you are done.
130+
readDirStreamWithPtr :: Ptr DirEnt -> (DirEnt -> IO a) -> DirStream -> IO (Maybe a)
131+
readDirStreamWithPtr ptr_dEnt f dstream@(DirStream dirp) = do
132+
resetErrno
133+
r <- c_readdir dirp (castPtr ptr_dEnt)
134+
if (r == 0)
135+
then do dEnt@(DirEnt dEntPtr) <- peek ptr_dEnt
136+
if (dEntPtr == nullPtr)
137+
then return Nothing
138+
else do
139+
res <- f dEnt
140+
c_freeDirEnt dEntPtr
141+
return (Just res)
142+
else do errno <- getErrno
143+
if (errno == eINTR)
144+
then readDirStreamWithPtr ptr_dEnt f dstream
145+
else do
146+
let (Errno eo) = errno
147+
if (eo == 0)
148+
then return Nothing
149+
else throwErrno "readDirStream"
137150

138151
-- traversing directories
139152
foreign import ccall unsafe "__hscore_readdir"

System/Posix/Directory/Common.hsc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ newtype DirStream = DirStream (Ptr CDir)
4545

4646
newtype DirEnt = DirEnt (Ptr CDirent)
4747

48+
-- We provide a hand-written instance here since GeneralizedNewtypeDeriving and
49+
-- DerivingVia are not allowed in Safe Haskell.
50+
instance Storable DirEnt where
51+
sizeOf _ = sizeOf (undefined :: Ptr CDirent)
52+
{-# INLINE sizeOf #-}
53+
54+
alignment _ = alignment (undefined :: Ptr CDirent)
55+
{-# INLINE alignment #-}
56+
57+
peek ptr = DirEnt <$> peek (castPtr ptr)
58+
{-# INLINE peek #-}
59+
60+
poke ptr (DirEnt dEnt) = poke (castPtr ptr) dEnt
61+
{-# INLINE poke#-}
62+
4863
data {-# CTYPE "DIR" #-} CDir
4964
data {-# CTYPE "struct dirent" #-} CDirent
5065

0 commit comments

Comments
 (0)