66-- Maintainer : streamly@composewell.com
77-- Portability : GHC
88
9+ {-# LANGUAGE UnliftedFFITypes #-}
10+
911module Streamly.Internal.Syscall.Windows.ReadDir
1012 (
1113#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
@@ -29,6 +31,7 @@ import Control.Monad.Catch (MonadCatch)
2931import Control.Monad.IO.Class (MonadIO (.. ))
3032import Data.Char (ord , isSpace )
3133import Data.IORef (IORef , newIORef , readIORef , writeIORef )
34+ import GHC.Base (Addr ##)
3235import Foreign.C
3336 ( CInt (.. ), CSize (.. ), CWchar (.. ), Errno (.. )
3437 , errnoToIOError , peekCWString
@@ -44,8 +47,10 @@ import Streamly.Internal.FileSystem.WindowsPath (WindowsPath(..))
4447import System.IO.Error (ioeSetErrorString )
4548
4649import qualified Streamly.Internal.Data.Array as Array
50+ import qualified Streamly.Internal.Data.MutArray as MutArray
4751import qualified Streamly.Internal.Data.MutByteArray as MutByteArray
4852import qualified Streamly.Internal.Data.Unfold as UF (bracketIO )
53+ import qualified Streamly.Internal.FileSystem.Path.Common as PathC
4954import qualified Streamly.Internal.FileSystem.WindowsPath as Path
5055import qualified System.Win32 as Win32 (failWith )
5156
@@ -95,9 +100,12 @@ foreign import ccall unsafe "windows.h LocalFree"
95100 localFree :: Ptr a -> IO (Ptr a )
96101
97102------------------------------------------------------------------------------
98- -- Haskell C APIs
103+ -- FFI imports/ Haskell C APIs
99104------------------------------------------------------------------------------
100105
106+ foreign import ccall unsafe " string.h memcpy" c_memcpy
107+ :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8 )
108+
101109foreign import ccall unsafe " maperrno_func" -- in base/cbits/Win32Utils.c
102110 c_maperrno_func :: ErrCode -> IO Errno
103111
@@ -144,6 +152,32 @@ failIf p wh act = do
144152iNVALID_HANDLE_VALUE :: HANDLE
145153iNVALID_HANDLE_VALUE = castUINTPtrToPtr maxBound
146154
155+ ------------------------------------------------------------------------------
156+ -- Path string manipulation
157+ ------------------------------------------------------------------------------
158+
159+ foreign import ccall unsafe " wchar.h wcslen" c_wcslen
160+ :: Ptr CWchar -> IO CSize
161+
162+ foreign import ccall unsafe " wchar.h wcslen" c_wcslen_pinned
163+ :: Addr ## -> IO CSize
164+
165+ -- This is defined here and not in Path module because wcslen is a platform
166+ -- specific function and uses 32-bit wide chars on posix and 16-bit wide chars
167+ -- on Windows. We cannot have it in WindowsPath module because that module is
168+ -- plaform agnostic and works on Posix as well.
169+ --
170+ {-# INLINE appendW16CString #-}
171+ appendW16CString :: WindowsPath -> Ptr CWchar -> IO WindowsPath
172+ appendW16CString (WindowsPath arr) str =
173+ fmap WindowsPath
174+ $ PathC. appendCStringWith
175+ MutArray. emptyOf
176+ c_wcslen_pinned
177+ PathC. Windows
178+ arr
179+ (castPtr str)
180+
147181------------------------------------------------------------------------------
148182-- Dir stream implementation
149183------------------------------------------------------------------------------
@@ -358,11 +392,8 @@ readEitherChunks _confMod alldirs =
358392 if isMeta
359393 then return $ Skip st
360394 else do
361- arr <- liftIO $ Array. fromW16CString dname
362- let path =
363- Path. unsafeJoin curdir
364- (Path. unsafeFromArray arr)
365- dirs1 = path : dirs
395+ path <- liftIO $ appendW16CString curdir dname
396+ let dirs1 = path : dirs
366397 ndirs1 = ndirs + 1
367398 if ndirs1 >= dirMax
368399 then return $ Yield (Left dirs1)
@@ -371,11 +402,8 @@ readEitherChunks _confMod alldirs =
371402 (ChunkStreamLoop
372403 curdir xs ds dirs1 ndirs1 files nfiles)
373404 else do
374- arr <- liftIO $ Array. fromW16CString dname
375- let path =
376- Path. unsafeJoin curdir
377- (Path. unsafeFromArray arr)
378- files1 = path : files
405+ path <- liftIO $ appendW16CString curdir dname
406+ let files1 = path : files
379407 nfiles1 = nfiles + 1
380408 if nfiles1 >= fileMax
381409 then return $ Yield (Right files1)
@@ -392,12 +420,6 @@ readEitherChunks _confMod alldirs =
392420-- Chunked byte-buffered reads
393421------------------------------------------------------------------------------
394422
395- foreign import ccall unsafe " string.h memcpy" c_memcpy
396- :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8 )
397-
398- foreign import ccall unsafe " wchar.h wcslen" c_wcslen
399- :: Ptr CWchar -> IO CSize
400-
401423-- Split a list in half.
402424splitHalf :: [a ] -> ([a ], [a ])
403425splitHalf xxs = split xxs xxs
@@ -430,8 +452,6 @@ data ChunkStreamByteState =
430452 MutByteArray
431453 Int
432454
433- -- TODO: instead of unsafeJoin use appendCWString
434- --
435455-- NOTE: Unlike posix on Windows the file attribute to determine whether it is
436456-- a directory or not is always available so we do not need the code to handle
437457-- the case when they are not available, on Posix we need to use stat
@@ -597,10 +617,8 @@ readEitherByteChunks _confMod alldirs =
597617
598618 {-# INLINE handleDirEnt #-}
599619 handleDirEnt pos dname = do
600- arr <- liftIO $ Array. fromW16CString dname
601- let path =
602- Path. unsafeJoin curdir (Path. unsafeFromArray arr)
603- dirs1 = path : dirs
620+ path <- liftIO $ appendW16CString curdir dname
621+ let dirs1 = path : dirs
604622 r <- copyToBuf mbarr pos curdir dname
605623 case r of
606624 Just pos1 -> goInner dirs1 pos1
0 commit comments