unix-bytestring 0.3.7.8 → 0.4.0
raw patch · 6 files changed
+966/−937 lines, 6 filesdep ~basedep ~bytestring
Dependency ranges changed: base, bytestring
Files
- CHANGELOG +4/−0
- src/System/Posix/IO/ByteString.hsc +0/−792
- src/System/Posix/IO/ByteString/Ext.hsc +816/−0
- src/System/Posix/IO/ByteString/Ext/Lazy.hs +136/−0
- src/System/Posix/IO/ByteString/Lazy.hs +0/−138
- unix-bytestring.cabal +10/−7
CHANGELOG view
@@ -1,3 +1,7 @@+0.4.0 (2023-03-19):+ - Renaming modules to avoid conflicts with unix-2.8.0.0+ - Updating version bounds for GHC 9.6+ 0.3.7.8 (2022-08-28): - Updated version bounds for GHC 9.4 0.3.7.7 (2022-05-25):
− src/System/Posix/IO/ByteString.hsc
@@ -1,792 +0,0 @@-{--/N.B./, There's a bug when trying to use Cabal-style MIN_VERSION_foo(1,2,3)-macros in combination with hsc2hs. We don't need full hsc2hs support-in this file, but if we use CPP instead we get a strange error on-OSX 10.5.8 about "architecture not supported" (even though the-headers work fine with hsc2hs). It turns out that we don't /need/-to combine Cabal-style macros and hsc2hs\/cpp since we can remove-our dependency on the @unix@ package. But this issue is worth making-a note of.--}---- GHC 7.6 changed the semantics of the FFI so that we must have--- the data constructors in scope in order to import functions using--- the given types. However, those data constructors[1] are not exported--- in earlier versions, so having @(..)@ will raise warnings on old--- systems. However, Cabal-style MIN_VERSION_foo(1,2,3) macros don't--- play nicely with hsc2hs; and we need hsc2hs in lieu of CPP for--- OS X. So we disable -Wall rather than trying to CPP this problem--- away. There doesn't appear to be a -fno-warn-foo for this--- particular issue.------ [1] CSsize(..), COff(..), CInt(..), CSize(..), CChar(..)--{-# LANGUAGE ForeignFunctionInterface #-}-{-# OPTIONS_GHC -fwarn-tabs #-}-------------------------------------------------------------------- 2021.10.17--- |--- Module : System.Posix.IO.ByteString--- Copyright : 2010--2022 wren romano--- License : BSD-3-Clause--- Maintainer : wren@cpan.org--- Stability : experimental--- Portability : non-portable (POSIX.1, XPG4.2; hsc2hs, FFI)------ Provides a strict-'BS.ByteString' file-descriptor based I\/O--- API, designed loosely after the @String@ file-descriptor based--- I\/O API in "System.Posix.IO". The functions here wrap standard--- C implementations of the functions specified by the ISO\/IEC--- 9945-1:1990 (``POSIX.1'') and X\/Open Portability Guide Issue--- 4, Version 2 (``XPG4.2'') specifications.------------------------------------------------------------------module System.Posix.IO.ByteString- (- -- * I\/O with file descriptors- -- ** Reading- -- *** The POSIX.1 @read(2)@ syscall- fdRead- , fdReadBuf- , tryFdReadBuf- , fdReads- -- *** The XPG4.2 @readv(2)@ syscall- -- , fdReadv- , fdReadvBuf- , tryFdReadvBuf- -- *** The XPG4.2 @pread(2)@ syscall- , fdPread- , fdPreadBuf- , tryFdPreadBuf- , fdPreads-- -- ** Writing- -- *** The POSIX.1 @write(2)@ syscall- , fdWrite- , fdWriteBuf- , tryFdWriteBuf- , fdWrites- -- *** The XPG4.2 @writev(2)@ syscall- , fdWritev- , fdWritevBuf- , tryFdWritevBuf- -- *** The XPG4.2 @pwrite(2)@ syscall- , fdPwrite- , fdPwriteBuf- , tryFdPwriteBuf-- -- ** Seeking- -- | These functions are not 'BS.ByteString' related, but are- -- provided here for API completeness.-- -- *** The POSIX.1 @lseek(2)@ syscall- , fdSeek- , tryFdSeek- ) where--import Data.Word (Word8)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Internal as BSI-import qualified Data.ByteString.Unsafe as BSU--import System.IO (SeekMode(..))-import qualified System.IO.Error as IOE-import qualified System.Posix.Internals as Base-import System.Posix.Types.Iovec-import System.Posix.Types ( Fd, ByteCount, FileOffset- , CSsize(..), COff(..))-import Foreign.C.Types (CInt(..), CSize(..), CChar(..))--import qualified Foreign.C.Error as C-import Foreign.C.Error.Safe-import Foreign.Ptr (Ptr, castPtr, plusPtr)-import qualified Foreign.Marshal.Array as FMA (withArrayLen)---- For the functor instance of 'Either', aka 'right' for ArrowChoice(->)-import Control.Arrow (ArrowChoice(..))---- iovec, writev, and readv are in <sys/uio.h>, but we must include--- <sys/types.h> and <unistd.h> for legacy reasons.-#include <sys/types.h>-#include <sys/uio.h>-#include <unistd.h>--------------------------------------------------------------------- | Throw an 'IOE.IOError' for EOF.-ioErrorEOF :: String -> IO a-ioErrorEOF fun =- IOE.ioError- (IOE.ioeSetErrorString- (IOE.mkIOError IOE.eofErrorType fun Nothing Nothing)- "EOF")--------------------------------------------------------------------foreign import ccall safe "read"- -- ssize_t read(int fildes, void *buf, size_t nbyte);- c_safe_read :: CInt -> Ptr CChar -> CSize -> IO CSsize----- | Read data from an 'Fd' into memory. This is exactly equivalent--- to the POSIX.1 @read(2)@ system call, except that we return 0--- bytes read if the @ByteCount@ argument is less than or equal to--- zero (instead of throwing an errno exception). /N.B./, this--- behavior is different from the version in @unix-2.4.2.0@ which--- only checks for equality to zero. If there are any errors, then--- they are thrown as 'IOE.IOError' exceptions.------ /Since: 0.3.0/-fdReadBuf- :: Fd- -> Ptr Word8 -- ^ Memory in which to put the data.- -> ByteCount -- ^ How many bytes to try to read.- -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).-fdReadBuf fd buf nbytes- | nbytes <= 0 = return 0- | otherwise =- fmap fromIntegral- $ C.throwErrnoIfMinus1Retry _fdReadBuf- $ c_safe_read- (fromIntegral fd)- (castPtr buf)- (fromIntegral nbytes)--_fdReadBuf :: String-_fdReadBuf = "System.Posix.IO.ByteString.fdReadBuf"-{-# NOINLINE _fdReadBuf #-}----- | Read data from an 'Fd' into memory. This is a variation of--- 'fdReadBuf' which returns errors with an 'Either' instead of--- throwing exceptions.------ /Since: 0.3.3/-tryFdReadBuf- :: Fd- -> Ptr Word8 -- ^ Memory in which to put the data.- -> ByteCount -- ^ How many bytes to try to read.- -> IO (Either C.Errno ByteCount)- -- ^ How many bytes were actually read (zero for EOF).-tryFdReadBuf fd buf nbytes- | nbytes <= 0 = return (Right 0)- | otherwise =- fmap (right fromIntegral)- $ eitherErrnoIfMinus1Retry- $ c_safe_read- (fromIntegral fd)- (castPtr buf)- (fromIntegral nbytes)---------------------------------------------------------------------- | Read data from an 'Fd' and convert it to a 'BS.ByteString'.--- Throws an exception if this is an invalid descriptor, or EOF has--- been reached. This is essentially equivalent to 'fdReadBuf'; the--- differences are that we allocate a byte buffer for the @ByteString@,--- and that we detect EOF and throw an 'IOE.IOError'.-fdRead- :: Fd- -> ByteCount -- ^ How many bytes to try to read.- -> IO BS.ByteString -- ^ The bytes read.-fdRead fd nbytes- | nbytes <= 0 = return BS.empty- | otherwise =- BSI.createAndTrim (fromIntegral nbytes) $ \buf -> do- rc <- fdReadBuf fd buf nbytes- if 0 == rc- then ioErrorEOF _fdRead- else return (fromIntegral rc)--_fdRead :: String-_fdRead = "System.Posix.IO.ByteString.fdRead"-{-# NOINLINE _fdRead #-}---------------------------------------------------------------------- | Read data from an 'Fd' and convert it to a 'BS.ByteString'.--- Throws an exception if this is an invalid descriptor, or EOF has--- been reached.------ This version takes a kind of stateful predicate for whether and--- how long to keep retrying. Assume the function is called as--- @fdReads f z0 fd n0@. We will attempt to read @n0@ bytes from--- @fd@. If we fall short, then we will call @f len z@ where @len@--- is the total number of bytes read so far and @z@ is the current--- state (initially @z0@). If it returns @Nothing@ then we will--- give up and return the current buffer; otherwise we will retry--- with the new state, continuing from where we left off.------ For example, to define a function that tries up to @n@ times,--- we can use:------ > fdReadUptoNTimes :: Int -> Fd -> ByteCount -> IO ByteString--- > fdReadUptoNTimes n0 = fdReads retry n0--- > where--- > retry _ 0 = Nothing--- > retry _ n = Just $! n-1------ The benefit of doing this instead of the naive approach of calling--- 'fdRead' repeatedly is that we only need to allocate one byte--- buffer, and trim it once at the end--- whereas the naive approach--- would allocate a buffer, trim it to the number of bytes read,--- and then concatenate with the previous one (another allocation,--- plus copying everything over) for each time around the loop.------ /Since: 0.2.1/-fdReads- :: (ByteCount -> a -> Maybe a) -- ^ A stateful predicate for retrying.- -> a -- ^ An initial state for the predicate.- -> Fd- -> ByteCount -- ^ How many bytes to try to read.- -> IO BS.ByteString -- ^ The bytes read.-fdReads f z0 fd n0- | n0 <= 0 = return BS.empty- | otherwise = BSI.createAndTrim (fromIntegral n0) (go z0 0 n0)- where- go _ len n buf | len `seq` n `seq` buf `seq` False = undefined- go z len n buf = do- rc <- fdReadBuf fd buf n- let len' = len + rc- case rc of- _ | rc == 0 -> ioErrorEOF _fdReads- | rc == n -> return (fromIntegral len') -- Finished.- | otherwise ->- case f len' z of- Nothing -> return (fromIntegral len') -- Gave up.- Just z' ->- go z' len' (n - rc) (buf `plusPtr` fromIntegral rc)--_fdReads :: String-_fdReads = "System.Posix.IO.ByteString.fdReads"-{-# NOINLINE _fdReads #-}--------------------------------------------------------------------foreign import ccall safe "readv"- -- ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);- c_safe_readv :: CInt -> Ptr CIovec -> CInt -> IO CSsize-{---- N.B., c_safe_readv will throw errno=EINVAL--- if iovcnt <= 0 || > 16,--- if one of the iov_len values in the iov array was negative,--- if the sum of the iov_len values in the iov array overflowed a 32-bit integer.--fdReadvBufSafe :: Fd -> Ptr CIovec -> Int -> IO ByteCount-fdReadvBufSafe fd = go 0- where- go rc bufs len- | len <= 0 = return rc- | otherwise = do- m <- checkIovecs bufs (min 16 len)- case m of- Nothing -> error- Just (bufs', l, nbytes) -> do- rc' <- fdReadvBuf fd bufs l- if rc' == nbytes- then go (rc+rc') bufs' (len-l)- else return (rc+rc')--checkIovecs :: Ptr CIovec -> Int -> IO (Maybe (Ptr CIovec, Int, ByteCount))-checkIovecs = go (0 :: Int32) 0- where- go nbytes n p len- | nbytes `seq` n `seq` p `seq` len `seq` False = undefined- | len == 0 = return (Just (p, n, fromIntegral nbytes)- | otherwise = do- l <- iov_len <$> peek p- if l < 0- then return Nothing- else do- let nbytes' = nbytes+l- if nbytes' < 0- then return (Just (p, n, fromIntegral nbytes)- else go nbytes' (n+1) (p++) (len-1)--}----- | Read data from an 'Fd' and scatter it into memory. This is--- exactly equivalent to the XPG4.2 @readv(2)@ system call, except--- that we return 0 bytes read if the @Int@ argument is less than--- or equal to zero (instead of throwing an 'C.eINVAL' exception).--- If there are any errors, then they are thrown as 'IOE.IOError'--- exceptions.------ TODO: better documentation.------ /Since: 0.3.0/-fdReadvBuf- :: Fd- -> Ptr CIovec -- ^ A C-style array of buffers to fill.- -> Int -- ^ How many buffers there are.- -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).-fdReadvBuf fd bufs len- | len <= 0 = return 0- | otherwise =- fmap fromIntegral- $ C.throwErrnoIfMinus1Retry _fdReadvBuf- $ c_safe_readv (fromIntegral fd) bufs (fromIntegral len)--_fdReadvBuf :: String-_fdReadvBuf = "System.Posix.IO.ByteString.fdReadvBuf"-{-# NOINLINE _fdReadvBuf #-}----- | Read data from an 'Fd' and scatter it into memory. This is a--- variation of 'fdReadvBuf' which returns errors with an 'Either'--- instead of throwing exceptions.------ /Since: 0.3.3/-tryFdReadvBuf- :: Fd- -> Ptr CIovec -- ^ A C-style array of buffers to fill.- -> Int -- ^ How many buffers there are.- -> IO (Either C.Errno ByteCount)- -- ^ How many bytes were actually read (zero for EOF).-tryFdReadvBuf fd bufs len- | len <= 0 = return (Right 0)- | otherwise =- fmap (right fromIntegral)- $ eitherErrnoIfMinus1Retry- $ c_safe_readv (fromIntegral fd) bufs (fromIntegral len)----- TODO: What's a reasonable wrapper for fdReadvBuf to make it Haskellish?-------------------------------------------------------------------foreign import ccall safe "pread"- -- ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);- c_safe_pread :: CInt -> Ptr Word8 -> CSize -> COff -> IO CSsize----- | Read data from a specified position in the 'Fd' into memory,--- without altering the position stored in the @Fd@. This is exactly--- equivalent to the XPG4.2 @pread(2)@ system call, except that we--- return 0 bytes read if the @Int@ argument is less than or equal--- to zero (instead of throwing an errno exception). If there are--- any errors, then they are thrown as 'IOE.IOError' exceptions.------ /Since: 0.3.0/-fdPreadBuf- :: Fd- -> Ptr Word8 -- ^ Memory in which to put the data.- -> ByteCount -- ^ How many bytes to try to read.- -> FileOffset -- ^ Where to read the data from.- -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).-fdPreadBuf fd buf nbytes offset- | nbytes <= 0 = return 0- | otherwise =- fmap fromIntegral- $ C.throwErrnoIfMinus1Retry _fdPreadBuf- $ c_safe_pread- (fromIntegral fd)- buf- (fromIntegral nbytes)- (fromIntegral offset)--_fdPreadBuf :: String-_fdPreadBuf = "System.Posix.IO.ByteString.fdPreadBuf"-{-# NOINLINE _fdPreadBuf #-}----- | Read data from a specified position in the 'Fd' into memory,--- without altering the position stored in the @Fd@. This is a--- variation of 'fdPreadBuf' which returns errors with an 'Either'--- instead of throwing exceptions.------ /Since: 0.3.3/-tryFdPreadBuf- :: Fd- -> Ptr Word8 -- ^ Memory in which to put the data.- -> ByteCount -- ^ How many bytes to try to read.- -> FileOffset -- ^ Where to read the data from.- -> IO (Either C.Errno ByteCount)- -- ^ How many bytes were actually read (zero for EOF).-tryFdPreadBuf fd buf nbytes offset- | nbytes <= 0 = return (Right 0)- | otherwise =- fmap (right fromIntegral)- $ eitherErrnoIfMinus1Retry- $ c_safe_pread- (fromIntegral fd)- buf- (fromIntegral nbytes)- (fromIntegral offset)--------------------------------------------------------------------- | Read data from a specified position in the 'Fd' and convert--- it to a 'BS.ByteString', without altering the position stored--- in the @Fd@. Throws an exception if this is an invalid descriptor,--- or EOF has been reached. This is essentially equivalent to--- 'fdPreadBuf'; the differences are that we allocate a byte buffer--- for the @ByteString@, and that we detect EOF and throw an--- 'IOE.IOError'.------ /Since: 0.3.0/-fdPread- :: Fd- -> ByteCount -- ^ How many bytes to try to read.- -> FileOffset -- ^ Where to read the data from.- -> IO BS.ByteString -- ^ The bytes read.-fdPread fd nbytes offset- | nbytes <= 0 = return BS.empty- | otherwise =- BSI.createAndTrim (fromIntegral nbytes) $ \buf -> do- rc <- fdPreadBuf fd buf nbytes offset- if 0 == rc- then ioErrorEOF _fdPread- else return (fromIntegral rc)--_fdPread :: String-_fdPread = "System.Posix.IO.ByteString.fdPread"-{-# NOINLINE _fdPread #-}--------------------------------------------------------------------- | Read data from a specified position in the 'Fd' and convert--- it to a 'BS.ByteString', without altering the position stored--- in the @Fd@. Throws an exception if this is an invalid descriptor,--- or EOF has been reached. This is a 'fdPreadBuf' based version--- of 'fdReads'; see those functions for more details.------ /Since: 0.3.1/-fdPreads- :: (ByteCount -> a -> Maybe a) -- ^ A stateful predicate for retrying.- -> a -- ^ An initial state for the predicate.- -> Fd- -> ByteCount -- ^ How many bytes to try to read.- -> FileOffset -- ^ Where to read the data from.- -> IO BS.ByteString -- ^ The bytes read.-fdPreads f z0 fd n0 offset- | n0 <= 0 = return BS.empty- | otherwise = BSI.createAndTrim (fromIntegral n0) (go z0 0 n0)- where- go _ len n buf | len `seq` n `seq` buf `seq` False = undefined- go z len n buf = do- rc <- fdPreadBuf fd buf n (offset + fromIntegral len)- let len' = len + rc- case rc of- _ | rc == 0 -> ioErrorEOF _fdPreads- | rc == n -> return (fromIntegral len') -- Finished.- | otherwise ->- case f len' z of- Nothing -> return (fromIntegral len') -- Gave up.- Just z' ->- go z' len' (n - rc) (buf `plusPtr` fromIntegral rc)--_fdPreads :: String-_fdPreads = "System.Posix.IO.ByteString.fdPreads"-{-# NOINLINE _fdPreads #-}------------------------------------------------------------------------------------------------------------------------------------foreign import ccall safe "write"- -- ssize_t write(int fildes, const void *buf, size_t nbyte);- c_safe_write :: CInt -> Ptr CChar -> CSize -> IO CSsize----- | Write data from memory to an 'Fd'. This is exactly equivalent--- to the POSIX.1 @write(2)@ system call, except that we return 0--- bytes written if the @ByteCount@ argument is less than or equal--- to zero (instead of throwing an errno exception). /N.B./, this--- behavior is different from the version in @unix-2.4.2.0@ which--- doesn't check the byte count. If there are any errors, then they--- are thrown as 'IOE.IOError' exceptions.------ /Since: 0.3.0/-fdWriteBuf- :: Fd- -> Ptr Word8 -- ^ Memory containing the data to write.- -> ByteCount -- ^ How many bytes to try to write.- -> IO ByteCount -- ^ How many bytes were actually written.-fdWriteBuf fd buf nbytes- | nbytes <= 0 = return 0- | otherwise =- fmap fromIntegral- $ C.throwErrnoIfMinus1Retry _fdWriteBuf- $ c_safe_write- (fromIntegral fd)- (castPtr buf)- (fromIntegral nbytes)--_fdWriteBuf :: String-_fdWriteBuf = "System.Posix.IO.ByteString.fdWriteBuf"-{-# NOINLINE _fdWriteBuf #-}----- | Write data from memory to an 'Fd'. This is a variation of--- 'fdWriteBuf' which returns errors with an 'Either' instead of--- throwing exceptions.------ /Since: 0.3.3/-tryFdWriteBuf- :: Fd- -> Ptr Word8 -- ^ Memory containing the data to write.- -> ByteCount -- ^ How many bytes to try to write.- -> IO (Either C.Errno ByteCount)- -- ^ How many bytes were actually read (zero for EOF).-tryFdWriteBuf fd buf nbytes- | nbytes <= 0 = return (Right 0)- | otherwise =- fmap (right fromIntegral)- $ eitherErrnoIfMinus1Retry- $ c_safe_write- (fromIntegral fd)- (castPtr buf)- (fromIntegral nbytes)--------------------------------------------------------------------- | Write a 'BS.ByteString' to an 'Fd'. The return value is the--- total number of bytes actually written. This is exactly equivalent--- to 'fdWriteBuf'; we just convert the @ByteString@ into its--- underlying @Ptr Word8@ and @ByteCount@ components for passing--- to 'fdWriteBuf'.-fdWrite- :: Fd- -> BS.ByteString -- ^ The string to write.- -> IO ByteCount -- ^ How many bytes were actually written.-fdWrite fd s =- -- N.B., BSU.unsafeUseAsCStringLen does zero copying. Use- -- BS.useAsCStringLen if there's any chance fdWriteBuf might- -- alter the buffer.- BSU.unsafeUseAsCStringLen s $ \(buf,len) -> do- fdWriteBuf fd (castPtr buf) (fromIntegral len)---------------------------------------------------------------------- | Write a sequence of 'BS.ByteString's to an 'Fd'. The return--- value is a triple of: the total number of bytes written, the--- number of bytes written from the first of the remaining strings,--- and the remaining (unwritten) strings. We return this triple--- instead of a pair adjusting the head of the remaining strings--- (i.e., removing the bytes already written) in case there is some--- semantic significance to the way the input is split into chunks.------ This version consumes the list lazily and will call 'fdWrite'--- once for each @ByteString@, thus making /O(n)/ system calls.--- This laziness allows the early parts of the list to be garbage--- collected and prevents needing to hold the whole list of--- @ByteString@s in memory at once. Compare against 'fdWritev'.-fdWrites- :: Fd- -> [BS.ByteString]- -- ^ The strings to write.- -> IO (ByteCount, ByteCount, [BS.ByteString])- -- ^ The total number of bytes written, the number of bytes- -- written from the first of the remaining strings, the- -- remaining (unwritten) strings.-fdWrites fd = go 0- where- -- We want to do a left fold in order to avoid stack overflows,- -- but we need to have an early exit for incomplete writes- -- (which normally requires a right fold). Hence this recursion.- go acc [] = return (acc, 0, [])- go acc ccs@(c:cs) =- fdWrite fd c >>= \rc ->- let acc' = acc+rc in acc' `seq`- if rc == fromIntegral (BS.length c)- then go acc' cs- else return (acc', rc, ccs)--------------------------------------------------------------------foreign import ccall safe "writev"- -- ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);- c_safe_writev :: CInt -> Ptr CIovec -> CInt -> IO CSsize-{---- N.B., c_safe_readv will throw errno=EINVAL--- if iovcnt is less than or equal to 0, or greater than UIO_MAXIOV. (BUG: I have no idea where UIO_MAXIOV is defined! The web says it's in <linux/uio.h>, and some suggest using <limits.h>IOV_MAX or <limits.h>_XOPEN_IOV_MAX instead.)--- <http://www.mail-archive.com/freebsd-current@freebsd.org/msg27878.html>--- <http://www.mail-archive.com/naviserver-devel@lists.sourceforge.net/msg02237.html>--- -- That last link says that glibc might transparently chop up larger values before sending to the kernel.--- if one of the iov_len values in the iov array is negative.--- if the sum of the iov_len values in the iov array overflows a 32-bit integer.--}----- | Write data from memory to an 'Fd'. This is exactly equivalent--- to the XPG4.2 @writev(2)@ system call, except that we return 0--- bytes written if the @Int@ argument is less than or equal to--- zero (instead of throwing an 'C.eINVAL' exception). If there are--- any errors, then they are thrown as 'IOE.IOError' exceptions.------ TODO: better documentation.------ /Since: 0.3.0/-fdWritevBuf- :: Fd- -> Ptr CIovec -- ^ A C-style array of buffers to write.- -> Int -- ^ How many buffers there are.- -> IO ByteCount -- ^ How many bytes were actually written.-fdWritevBuf fd bufs len- | len <= 0 = return 0- | otherwise =- fmap fromIntegral- $ C.throwErrnoIfMinus1Retry _fdWritevBuf- $ c_safe_writev (fromIntegral fd) bufs (fromIntegral len)--_fdWritevBuf :: String-_fdWritevBuf = "System.Posix.IO.ByteString.fdWritevBuf"-{-# NOINLINE _fdWritevBuf #-}----- | Write data from memory to an 'Fd'. This is a variation of--- 'fdWritevBuf' which returns errors with an 'Either' instead of--- throwing exceptions.------ /Since: 0.3.3/-tryFdWritevBuf- :: Fd- -> Ptr CIovec -- ^ A C-style array of buffers to write.- -> Int -- ^ How many buffers there are.- -> IO (Either C.Errno ByteCount)- -- ^ How many bytes were actually read (zero for EOF).-tryFdWritevBuf fd bufs len- | len <= 0 = return (Right 0)- | otherwise =- fmap (right fromIntegral)- $ eitherErrnoIfMinus1Retry- $ c_safe_writev (fromIntegral fd) bufs (fromIntegral len)---------------------------------------------------------------------- | Write a sequence of 'BS.ByteString's to an 'Fd'. The return--- value is the total number of bytes written. Unfortunately the--- @writev(2)@ system call does not provide enough information to--- return the triple that 'fdWrites' does.------ This version will force the spine of the list, converting each--- @ByteString@ into an @iovec@ (see 'CIovec'), and then call--- 'fdWritevBuf'. This means we only make one system call, which--- reduces the overhead of performing context switches. But it also--- means that we must store the whole list of @ByteString@s in--- memory at once, and that we must perform some allocation and--- conversion. Compare against 'fdWrites'.-fdWritev- :: Fd- -> [BS.ByteString] -- ^ The strings to write.- -> IO ByteCount -- ^ How many bytes were actually written.-fdWritev fd cs = do- rc <- FMA.withArrayLen (map unsafeByteString2CIovec cs) $ \len iovs ->- fdWritevBuf fd iovs (fromIntegral len)- -- BUG: is this enough to actually hold onto them?- mapM_ touchByteString cs- return rc--------------------------------------------------------------------foreign import ccall safe "pwrite"- -- ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset);- c_safe_pwrite :: CInt -> Ptr Word8 -> CSize -> COff -> IO CSsize----- | Write data from memory to a specified position in the 'Fd',--- but without altering the position stored in the @Fd@. This is--- exactly equivalent to the XPG4.2 @pwrite(2)@ system call, except--- that we return 0 bytes written if the @ByteCount@ argument is--- less than or equal to zero (instead of throwing an errno exception).--- If there are any errors, then they are thrown as 'IOE.IOError'--- exceptions.------ /Since: 0.3.0/-fdPwriteBuf- :: Fd- -> Ptr Word8 -- ^ Memory containing the data to write.- -> ByteCount -- ^ How many bytes to try to write.- -> FileOffset -- ^ Where to write the data to.- -> IO ByteCount -- ^ How many bytes were actually written.-fdPwriteBuf fd buf nbytes offset- | nbytes <= 0 = return 0- | otherwise =- fmap fromIntegral- $ C.throwErrnoIfMinus1Retry _fdPwriteBuf- $ c_safe_pwrite- (fromIntegral fd)- (castPtr buf)- (fromIntegral nbytes)- (fromIntegral offset)--_fdPwriteBuf :: String-_fdPwriteBuf = "System.Posix.IO.ByteString.fdPwriteBuf"-{-# NOINLINE _fdPwriteBuf #-}----- | Write data from memory to a specified position in the 'Fd',--- but without altering the position stored in the @Fd@. This is a--- variation of 'fdPwriteBuf' which returns errors with an 'Either'--- instead of throwing exceptions.------ /Since: 0.3.3/-tryFdPwriteBuf- :: Fd- -> Ptr Word8 -- ^ Memory containing the data to write.- -> ByteCount -- ^ How many bytes to try to write.- -> FileOffset -- ^ Where to write the data to.- -> IO (Either C.Errno ByteCount)- -- ^ How many bytes were actually written.-tryFdPwriteBuf fd buf nbytes offset- | nbytes <= 0 = return (Right 0)- | otherwise =- fmap (right fromIntegral)- $ eitherErrnoIfMinus1Retry- $ c_safe_pwrite- (fromIntegral fd)- (castPtr buf)- (fromIntegral nbytes)- (fromIntegral offset)---------------------------------------------------------------------- | Write data from memory to a specified position in the 'Fd',--- but without altering the position stored in the @Fd@. This is--- exactly equivalent to 'fdPwriteBuf'; we just convert the--- @ByteString@ into its underlying @Ptr Word8@ and @ByteCount@--- components for passing to 'fdPwriteBuf'.------ /Since: 0.3.0/-fdPwrite- :: Fd- -> BS.ByteString -- ^ The string to write.- -> FileOffset -- ^ Where to write the data to.- -> IO ByteCount -- ^ How many bytes were actually written.-fdPwrite fd s offset =- -- N.B., BSU.unsafeUseAsCStringLen does zero copying. Use- -- BS.useAsCStringLen if there's any chance fdPwriteBuf might- -- alter the buffer.- BSU.unsafeUseAsCStringLen s $ \(buf,len) -> do- fdPwriteBuf fd (castPtr buf) (fromIntegral len) offset---------------------------------------------------------------------mode2Int :: SeekMode -> CInt-mode2Int AbsoluteSeek = (#const SEEK_SET)-mode2Int RelativeSeek = (#const SEEK_CUR)-mode2Int SeekFromEnd = (#const SEEK_END)----- | Repositions the offset of the file descriptor according to the--- offset and the seeking mode. This is exactly equivalent to the--- POSIX.1 @lseek(2)@ system call. If there are any errors, then--- they are thrown as 'IOE.IOError' exceptions.------ This is the same as 'System.Posix.IO.fdSeek' in @unix-2.6.0.1@,--- but provided here for consistency.------ /Since: 0.3.5/-fdSeek :: Fd -> SeekMode -> FileOffset -> IO FileOffset-fdSeek fd mode off =- C.throwErrnoIfMinus1 "fdSeek"- $ Base.c_lseek (fromIntegral fd) off (mode2Int mode)----- | Repositions the offset of the file descriptor according to the--- offset and the seeking mode. This is a variation of 'fdSeek'--- which returns errors with an @Either@ instead of throwing--- exceptions.------ /Since: 0.3.5/-tryFdSeek :: Fd -> SeekMode -> FileOffset -> IO (Either C.Errno FileOffset)-tryFdSeek fd mode off =- eitherErrnoIfMinus1- $ Base.c_lseek (fromIntegral fd) off (mode2Int mode)------------------------------------------------------------------------------------------------------------------------------ fin.
+ src/System/Posix/IO/ByteString/Ext.hsc view
@@ -0,0 +1,816 @@+{-+/N.B./, There's a bug when trying to use Cabal-style MIN_VERSION_foo(1,2,3)+macros in combination with hsc2hs. We don't need full hsc2hs support+in this file, but if we use CPP instead we get a strange error on+OSX 10.5.8 about "architecture not supported" (even though the+headers work fine with hsc2hs). It turns out that we don't /need/+to combine Cabal-style macros and hsc2hs\/cpp since we can remove+our dependency on the @unix@ package. But this issue is worth making+a note of.+-}++-- GHC 7.6 changed the semantics of the FFI so that we must have+-- the data constructors in scope in order to import functions using+-- the given types. However, those data constructors[1] are not exported+-- in earlier versions, so having @(..)@ will raise warnings on old+-- systems. However, Cabal-style MIN_VERSION_foo(1,2,3) macros don't+-- play nicely with hsc2hs; and we need hsc2hs in lieu of CPP for+-- OS X. So we disable -Wall rather than trying to CPP this problem+-- away. There doesn't appear to be a -fno-warn-foo for this+-- particular issue.+--+-- [1] CSsize(..), COff(..), CInt(..), CSize(..), CChar(..)++{-# LANGUAGE ForeignFunctionInterface #-}+{-# OPTIONS_GHC -fwarn-tabs #-}+----------------------------------------------------------------+-- 2022.08.28+-- |+-- Module : System.Posix.IO.ByteString.Ext+-- Copyright : 2010--2022 wren romano+-- License : BSD-3-Clause+-- Maintainer : wren@cpan.org+-- Stability : experimental+-- Portability : non-portable (POSIX.1, XPG4.2; hsc2hs, FFI)+--+-- Provides a strict-'BS.ByteString' file-descriptor based I\/O+-- API wrapping the standard C implementations of the functions+-- specified by the ISO\/IEC 9945-1:1990 (``POSIX.1'') and X\/Open+-- Portability Guide Issue 4, Version 2 (``XPG4.2'') specifications.+--+-- The API was originally designed loosely after the @String@ based+-- API in "System.Posix.IO", but significantly extending that API.+-- The `unix-2.8.0.0` package added a subset of our original API+-- in "System.Posix.IO.ByteString", so as of version 0.4.0 this+-- module has been renamed in order to avoid conflicts.+----------------------------------------------------------------+module System.Posix.IO.ByteString.Ext+ (+ -- * I\/O with file descriptors+ -- ** Reading+ -- *** The POSIX.1 @read(2)@ syscall+ fdRead+ , fdReadBuf+ , tryFdReadBuf+ , fdReads+ -- *** The XPG4.2 @readv(2)@ syscall+ -- , fdReadv+ , fdReadvBuf+ , tryFdReadvBuf+ -- *** The XPG4.2 @pread(2)@ syscall+ , fdPread+ , fdPreadBuf+ , tryFdPreadBuf+ , fdPreads++ -- ** Writing+ -- *** The POSIX.1 @write(2)@ syscall+ , fdWrite+ , fdWriteBuf+ , tryFdWriteBuf+ , fdWrites+ -- *** The XPG4.2 @writev(2)@ syscall+ , fdWritev+ , fdWritevBuf+ , tryFdWritevBuf+ -- *** The XPG4.2 @pwrite(2)@ syscall+ , fdPwrite+ , fdPwriteBuf+ , tryFdPwriteBuf++ -- ** Seeking+ -- | These functions are not 'BS.ByteString' related, but are+ -- provided here for API completeness.++ -- *** The POSIX.1 @lseek(2)@ syscall+ , fdSeek+ , tryFdSeek+ ) where++import Data.Word (Word8)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Internal as BSI+import qualified Data.ByteString.Unsafe as BSU++import System.IO (SeekMode(..))+import qualified System.IO.Error as IOE+import qualified System.Posix.Internals as Base+import System.Posix.Types.Iovec+import System.Posix.Types ( Fd, ByteCount, FileOffset+ , CSsize(..), COff(..))+import Foreign.C.Types (CInt(..), CSize(..), CChar(..))++import qualified Foreign.C.Error as C+import Foreign.C.Error.Safe+import Foreign.Ptr (Ptr, castPtr, plusPtr)+import qualified Foreign.Marshal.Array as FMA (withArrayLen)++-- For the functor instance of 'Either', aka 'right' for ArrowChoice(->)+import Control.Arrow (ArrowChoice(..))++-- iovec, writev, and readv are in <sys/uio.h>, but we must include+-- <sys/types.h> and <unistd.h> for legacy reasons.+#include <sys/types.h>+#include <sys/uio.h>+#include <unistd.h>++----------------------------------------------------------------+-- | Throw an 'IOE.IOError' for EOF.+ioErrorEOF :: String -> IO a+ioErrorEOF fun =+ IOE.ioError+ (IOE.ioeSetErrorString+ (IOE.mkIOError IOE.eofErrorType fun Nothing Nothing)+ "EOF")+++----------------------------------------------------------------+-- @ssize_t read(int fildes, void *buf, size_t nbyte)@+-- TODO: Portability: why using `CChar` instead of `Word8` or something else for `void*`?+foreign import ccall safe "read"+ c_safe_read :: CInt -> Ptr CChar -> CSize -> IO CSsize+++-- TODO: reexport `unix-2.8.0.0` version if available...+-- FIXME: what is the behavior for @unix-2.8.0.0@?+--+-- | Read data from an 'Fd' into memory. This is exactly equivalent+-- to the POSIX.1 @read(2)@ system call, except that we return 0+-- bytes read if the @ByteCount@ argument is less than or equal to+-- zero (instead of throwing an errno exception).+--+-- /N.B./, this behavior is different from the version in @unix-2.4.2.0@+-- which only checks for equality to zero. If there are any errors,+-- then they are thrown as 'IOE.IOError' exceptions.+--+-- /Since: 0.3.0/+fdReadBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory in which to put the data.+ -> ByteCount -- ^ How many bytes to try to read.+ -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).+fdReadBuf fd buf nbytes+ | nbytes <= 0 = return 0+ | otherwise =+ fmap fromIntegral+ $ C.throwErrnoIfMinus1Retry _fdReadBuf+ $ c_safe_read+ (fromIntegral fd)+ (castPtr buf)+ (fromIntegral nbytes)++_fdReadBuf :: String+_fdReadBuf = "System.Posix.IO.ByteString.fdReadBuf"+{-# NOINLINE _fdReadBuf #-}+++-- | Read data from an 'Fd' into memory. This is a variation of+-- 'fdReadBuf' which returns errors with an 'Either' instead of+-- throwing exceptions.+--+-- /Since: 0.3.3/+tryFdReadBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory in which to put the data.+ -> ByteCount -- ^ How many bytes to try to read.+ -> IO (Either C.Errno ByteCount)+ -- ^ How many bytes were actually read (zero for EOF).+tryFdReadBuf fd buf nbytes+ | nbytes <= 0 = return (Right 0)+ | otherwise =+ fmap (right fromIntegral)+ $ eitherErrnoIfMinus1Retry+ $ c_safe_read+ (fromIntegral fd)+ (castPtr buf)+ (fromIntegral nbytes)+++----------------------------------------------------------------+-- TODO: reexport `unix-2.8.0.0` version if available...+-- FIXME: what is the behavior for @unix-2.8.0.0@?+--+-- | Read data from an 'Fd' and convert it to a 'BS.ByteString'.+-- Throws an exception if this is an invalid descriptor, or EOF has+-- been reached. This is essentially equivalent to 'fdReadBuf'; the+-- differences are that we allocate a byte buffer for the @ByteString@,+-- and that we detect EOF and throw an 'IOE.IOError'.+fdRead+ :: Fd+ -> ByteCount -- ^ How many bytes to try to read.+ -> IO BS.ByteString -- ^ The bytes read.+fdRead fd nbytes+ | nbytes <= 0 = return BS.empty+ | otherwise =+ BSI.createAndTrim (fromIntegral nbytes) $ \buf -> do+ rc <- fdReadBuf fd buf nbytes+ if 0 == rc+ then ioErrorEOF _fdRead+ else return (fromIntegral rc)++_fdRead :: String+_fdRead = "System.Posix.IO.ByteString.fdRead"+{-# NOINLINE _fdRead #-}+++----------------------------------------------------------------+-- | Read data from an 'Fd' and convert it to a 'BS.ByteString'.+-- Throws an exception if this is an invalid descriptor, or EOF has+-- been reached.+--+-- This version takes a kind of stateful predicate for whether and+-- how long to keep retrying. Assume the function is called as+-- @fdReads f z0 fd n0@. We will attempt to read @n0@ bytes from+-- @fd@. If we fall short, then we will call @f len z@ where @len@+-- is the total number of bytes read so far and @z@ is the current+-- state (initially @z0@). If it returns @Nothing@ then we will+-- give up and return the current buffer; otherwise we will retry+-- with the new state, continuing from where we left off.+--+-- For example, to define a function that tries up to @n@ times,+-- we can use:+--+-- > fdReadUptoNTimes :: Int -> Fd -> ByteCount -> IO ByteString+-- > fdReadUptoNTimes n0 = fdReads retry n0+-- > where+-- > retry _ 0 = Nothing+-- > retry _ n = Just $! n-1+--+-- The benefit of doing this instead of the naive approach of calling+-- 'fdRead' repeatedly is that we only need to allocate one byte+-- buffer, and trim it once at the end--- whereas the naive approach+-- would allocate a buffer, trim it to the number of bytes read,+-- and then concatenate with the previous one (another allocation,+-- plus copying everything over) for each time around the loop.+--+-- /Since: 0.2.1/+fdReads+ :: (ByteCount -> a -> Maybe a) -- ^ A stateful predicate for retrying.+ -> a -- ^ An initial state for the predicate.+ -> Fd+ -> ByteCount -- ^ How many bytes to try to read.+ -> IO BS.ByteString -- ^ The bytes read.+fdReads f z0 fd n0+ | n0 <= 0 = return BS.empty+ | otherwise = BSI.createAndTrim (fromIntegral n0) (go z0 0 n0)+ where+ go _ len n buf | len `seq` n `seq` buf `seq` False = undefined+ go z len n buf = do+ rc <- fdReadBuf fd buf n+ let len' = len + rc+ case rc of+ _ | rc == 0 -> ioErrorEOF _fdReads+ | rc == n -> return (fromIntegral len') -- Finished.+ | otherwise ->+ case f len' z of+ Nothing -> return (fromIntegral len') -- Gave up.+ Just z' ->+ go z' len' (n - rc) (buf `plusPtr` fromIntegral rc)++_fdReads :: String+_fdReads = "System.Posix.IO.ByteString.fdReads"+{-# NOINLINE _fdReads #-}+++----------------------------------------------------------------+-- @ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)@+foreign import ccall safe "readv"+ c_safe_readv :: CInt -> Ptr CIovec -> CInt -> IO CSsize+{-+-- N.B., c_safe_readv will throw errno=EINVAL+-- if iovcnt <= 0 || > 16,+-- if one of the iov_len values in the iov array was negative,+-- if the sum of the iov_len values in the iov array overflowed a 32-bit integer.++fdReadvBufSafe :: Fd -> Ptr CIovec -> Int -> IO ByteCount+fdReadvBufSafe fd = go 0+ where+ go rc bufs len+ | len <= 0 = return rc+ | otherwise = do+ m <- checkIovecs bufs (min 16 len)+ case m of+ Nothing -> error+ Just (bufs', l, nbytes) -> do+ rc' <- fdReadvBuf fd bufs l+ if rc' == nbytes+ then go (rc+rc') bufs' (len-l)+ else return (rc+rc')++checkIovecs :: Ptr CIovec -> Int -> IO (Maybe (Ptr CIovec, Int, ByteCount))+checkIovecs = go (0 :: Int32) 0+ where+ go nbytes n p len+ | nbytes `seq` n `seq` p `seq` len `seq` False = undefined+ | len == 0 = return (Just (p, n, fromIntegral nbytes)+ | otherwise = do+ l <- iov_len <$> peek p+ if l < 0+ then return Nothing+ else do+ let nbytes' = nbytes+l+ if nbytes' < 0+ then return (Just (p, n, fromIntegral nbytes)+ else go nbytes' (n+1) (p++) (len-1)+-}+++-- | Read data from an 'Fd' and scatter it into memory. This is+-- exactly equivalent to the XPG4.2 @readv(2)@ system call, except+-- that we return 0 bytes read if the @Int@ argument is less than+-- or equal to zero (instead of throwing an 'C.eINVAL' exception).+-- If there are any errors, then they are thrown as 'IOE.IOError'+-- exceptions.+--+-- TODO: better documentation.+--+-- /Since: 0.3.0/+fdReadvBuf+ :: Fd+ -> Ptr CIovec -- ^ A C-style array of buffers to fill.+ -> Int -- ^ How many buffers there are.+ -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).+fdReadvBuf fd bufs len+ | len <= 0 = return 0+ | otherwise =+ fmap fromIntegral+ $ C.throwErrnoIfMinus1Retry _fdReadvBuf+ $ c_safe_readv (fromIntegral fd) bufs (fromIntegral len)++_fdReadvBuf :: String+_fdReadvBuf = "System.Posix.IO.ByteString.fdReadvBuf"+{-# NOINLINE _fdReadvBuf #-}+++-- | Read data from an 'Fd' and scatter it into memory. This is a+-- variation of 'fdReadvBuf' which returns errors with an 'Either'+-- instead of throwing exceptions.+--+-- /Since: 0.3.3/+tryFdReadvBuf+ :: Fd+ -> Ptr CIovec -- ^ A C-style array of buffers to fill.+ -> Int -- ^ How many buffers there are.+ -> IO (Either C.Errno ByteCount)+ -- ^ How many bytes were actually read (zero for EOF).+tryFdReadvBuf fd bufs len+ | len <= 0 = return (Right 0)+ | otherwise =+ fmap (right fromIntegral)+ $ eitherErrnoIfMinus1Retry+ $ c_safe_readv (fromIntegral fd) bufs (fromIntegral len)+++-- TODO: What's a reasonable wrapper for fdReadvBuf to make it Haskellish?++----------------------------------------------------------------+-- @ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset)@+-- TODO: Portability: why using `Word8` instead of something else for `void*`?+foreign import ccall safe "pread"+ c_safe_pread :: CInt -> Ptr Word8 -> CSize -> COff -> IO CSsize+++-- | Read data from a specified position in the 'Fd' into memory,+-- without altering the position stored in the @Fd@. This is exactly+-- equivalent to the XPG4.2 @pread(2)@ system call, except that we+-- return 0 bytes read if the @Int@ argument is less than or equal+-- to zero (instead of throwing an errno exception). If there are+-- any errors, then they are thrown as 'IOE.IOError' exceptions.+--+-- /Since: 0.3.0/+fdPreadBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory in which to put the data.+ -> ByteCount -- ^ How many bytes to try to read.+ -> FileOffset -- ^ Where to read the data from.+ -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).+fdPreadBuf fd buf nbytes offset+ | nbytes <= 0 = return 0+ | otherwise =+ fmap fromIntegral+ $ C.throwErrnoIfMinus1Retry _fdPreadBuf+ $ c_safe_pread+ (fromIntegral fd)+ buf+ (fromIntegral nbytes)+ (fromIntegral offset)++_fdPreadBuf :: String+_fdPreadBuf = "System.Posix.IO.ByteString.fdPreadBuf"+{-# NOINLINE _fdPreadBuf #-}+++-- | Read data from a specified position in the 'Fd' into memory,+-- without altering the position stored in the @Fd@. This is a+-- variation of 'fdPreadBuf' which returns errors with an 'Either'+-- instead of throwing exceptions.+--+-- /Since: 0.3.3/+tryFdPreadBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory in which to put the data.+ -> ByteCount -- ^ How many bytes to try to read.+ -> FileOffset -- ^ Where to read the data from.+ -> IO (Either C.Errno ByteCount)+ -- ^ How many bytes were actually read (zero for EOF).+tryFdPreadBuf fd buf nbytes offset+ | nbytes <= 0 = return (Right 0)+ | otherwise =+ fmap (right fromIntegral)+ $ eitherErrnoIfMinus1Retry+ $ c_safe_pread+ (fromIntegral fd)+ buf+ (fromIntegral nbytes)+ (fromIntegral offset)++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' and convert+-- it to a 'BS.ByteString', without altering the position stored+-- in the @Fd@. Throws an exception if this is an invalid descriptor,+-- or EOF has been reached. This is essentially equivalent to+-- 'fdPreadBuf'; the differences are that we allocate a byte buffer+-- for the @ByteString@, and that we detect EOF and throw an+-- 'IOE.IOError'.+--+-- /Since: 0.3.0/+fdPread+ :: Fd+ -> ByteCount -- ^ How many bytes to try to read.+ -> FileOffset -- ^ Where to read the data from.+ -> IO BS.ByteString -- ^ The bytes read.+fdPread fd nbytes offset+ | nbytes <= 0 = return BS.empty+ | otherwise =+ BSI.createAndTrim (fromIntegral nbytes) $ \buf -> do+ rc <- fdPreadBuf fd buf nbytes offset+ if 0 == rc+ then ioErrorEOF _fdPread+ else return (fromIntegral rc)++_fdPread :: String+_fdPread = "System.Posix.IO.ByteString.fdPread"+{-# NOINLINE _fdPread #-}++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' and convert+-- it to a 'BS.ByteString', without altering the position stored+-- in the @Fd@. Throws an exception if this is an invalid descriptor,+-- or EOF has been reached. This is a 'fdPreadBuf' based version+-- of 'fdReads'; see those functions for more details.+--+-- /Since: 0.3.1/+fdPreads+ :: (ByteCount -> a -> Maybe a) -- ^ A stateful predicate for retrying.+ -> a -- ^ An initial state for the predicate.+ -> Fd+ -> ByteCount -- ^ How many bytes to try to read.+ -> FileOffset -- ^ Where to read the data from.+ -> IO BS.ByteString -- ^ The bytes read.+fdPreads f z0 fd n0 offset+ | n0 <= 0 = return BS.empty+ | otherwise = BSI.createAndTrim (fromIntegral n0) (go z0 0 n0)+ where+ go _ len n buf | len `seq` n `seq` buf `seq` False = undefined+ go z len n buf = do+ rc <- fdPreadBuf fd buf n (offset + fromIntegral len)+ let len' = len + rc+ case rc of+ _ | rc == 0 -> ioErrorEOF _fdPreads+ | rc == n -> return (fromIntegral len') -- Finished.+ | otherwise ->+ case f len' z of+ Nothing -> return (fromIntegral len') -- Gave up.+ Just z' ->+ go z' len' (n - rc) (buf `plusPtr` fromIntegral rc)++_fdPreads :: String+_fdPreads = "System.Posix.IO.ByteString.fdPreads"+{-# NOINLINE _fdPreads #-}++----------------------------------------------------------------+----------------------------------------------------------------+-- @ssize_t write(int fildes, const void *buf, size_t nbyte)@+-- TODO: Portability: why using `CChar` instead of `Word8` or something else for `void*`?+foreign import ccall safe "write"+ c_safe_write :: CInt -> Ptr CChar -> CSize -> IO CSsize+++-- TODO: reexport `unix-2.8.0.0` version if available...+-- FIXME: what is the behavior for @unix-2.8.0.0@?+--+-- | Write data from memory to an 'Fd'. This is exactly equivalent+-- to the POSIX.1 @write(2)@ system call, except that we return 0+-- bytes written if the @ByteCount@ argument is less than or equal+-- to zero (instead of throwing an errno exception). /N.B./, this+-- behavior is different from the version in @unix-2.4.2.0@ which+-- doesn't check the byte count. If there are any errors, then they+-- are thrown as 'IOE.IOError' exceptions.+--+-- /Since: 0.3.0/+fdWriteBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory containing the data to write.+ -> ByteCount -- ^ How many bytes to try to write.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdWriteBuf fd buf nbytes+ | nbytes <= 0 = return 0+ | otherwise =+ fmap fromIntegral+ $ C.throwErrnoIfMinus1Retry _fdWriteBuf+ $ c_safe_write+ (fromIntegral fd)+ (castPtr buf)+ (fromIntegral nbytes)++_fdWriteBuf :: String+_fdWriteBuf = "System.Posix.IO.ByteString.fdWriteBuf"+{-# NOINLINE _fdWriteBuf #-}+++-- | Write data from memory to an 'Fd'. This is a variation of+-- 'fdWriteBuf' which returns errors with an 'Either' instead of+-- throwing exceptions.+--+-- /Since: 0.3.3/+tryFdWriteBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory containing the data to write.+ -> ByteCount -- ^ How many bytes to try to write.+ -> IO (Either C.Errno ByteCount)+ -- ^ How many bytes were actually read (zero for EOF).+tryFdWriteBuf fd buf nbytes+ | nbytes <= 0 = return (Right 0)+ | otherwise =+ fmap (right fromIntegral)+ $ eitherErrnoIfMinus1Retry+ $ c_safe_write+ (fromIntegral fd)+ (castPtr buf)+ (fromIntegral nbytes)++----------------------------------------------------------------+-- TODO: reexport `unix-2.8.0.0` version if available...+-- FIXME: what is the behavior for @unix-2.8.0.0@?+--+-- | Write a 'BS.ByteString' to an 'Fd'. The return value is the+-- total number of bytes actually written. This is exactly equivalent+-- to 'fdWriteBuf'; we just convert the @ByteString@ into its+-- underlying @Ptr Word8@ and @ByteCount@ components for passing+-- to 'fdWriteBuf'.+fdWrite+ :: Fd+ -> BS.ByteString -- ^ The string to write.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdWrite fd s =+ -- N.B., BSU.unsafeUseAsCStringLen does zero copying. Use+ -- BS.useAsCStringLen if there's any chance fdWriteBuf might+ -- alter the buffer.+ BSU.unsafeUseAsCStringLen s $ \(buf,len) -> do+ fdWriteBuf fd (castPtr buf) (fromIntegral len)+++----------------------------------------------------------------+-- | Write a sequence of 'BS.ByteString's to an 'Fd'. The return+-- value is a triple of: the total number of bytes written, the+-- number of bytes written from the first of the remaining strings,+-- and the remaining (unwritten) strings. We return this triple+-- instead of a pair adjusting the head of the remaining strings+-- (i.e., removing the bytes already written) in case there is some+-- semantic significance to the way the input is split into chunks.+--+-- This version consumes the list lazily and will call 'fdWrite'+-- once for each @ByteString@, thus making /O(n)/ system calls.+-- This laziness allows the early parts of the list to be garbage+-- collected and prevents needing to hold the whole list of+-- @ByteString@s in memory at once. Compare against 'fdWritev'.+fdWrites+ :: Fd+ -> [BS.ByteString]+ -- ^ The strings to write.+ -> IO (ByteCount, ByteCount, [BS.ByteString])+ -- ^ The total number of bytes written, the number of bytes+ -- written from the first of the remaining strings, the+ -- remaining (unwritten) strings.+fdWrites fd = go 0+ where+ -- We want to do a left fold in order to avoid stack overflows,+ -- but we need to have an early exit for incomplete writes+ -- (which normally requires a right fold). Hence this recursion.+ go acc [] = return (acc, 0, [])+ go acc ccs@(c:cs) =+ fdWrite fd c >>= \rc ->+ let acc' = acc+rc in acc' `seq`+ if rc == fromIntegral (BS.length c)+ then go acc' cs+ else return (acc', rc, ccs)+++----------------------------------------------------------------+-- @ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)@+foreign import ccall safe "writev"+ c_safe_writev :: CInt -> Ptr CIovec -> CInt -> IO CSsize+{-+-- N.B., c_safe_readv will throw errno=EINVAL+-- if iovcnt is less than or equal to 0, or greater than UIO_MAXIOV. (BUG: I have no idea where UIO_MAXIOV is defined! The web says it's in <linux/uio.h>, and some suggest using <limits.h>IOV_MAX or <limits.h>_XOPEN_IOV_MAX instead.)+-- <http://www.mail-archive.com/freebsd-current@freebsd.org/msg27878.html>+-- <http://www.mail-archive.com/naviserver-devel@lists.sourceforge.net/msg02237.html>+-- -- That last link says that glibc might transparently chop up larger values before sending to the kernel.+-- if one of the iov_len values in the iov array is negative.+-- if the sum of the iov_len values in the iov array overflows a 32-bit integer.+-}+++-- | Write data from memory to an 'Fd'. This is exactly equivalent+-- to the XPG4.2 @writev(2)@ system call, except that we return 0+-- bytes written if the @Int@ argument is less than or equal to+-- zero (instead of throwing an 'C.eINVAL' exception). If there are+-- any errors, then they are thrown as 'IOE.IOError' exceptions.+--+-- TODO: better documentation.+--+-- /Since: 0.3.0/+fdWritevBuf+ :: Fd+ -> Ptr CIovec -- ^ A C-style array of buffers to write.+ -> Int -- ^ How many buffers there are.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdWritevBuf fd bufs len+ | len <= 0 = return 0+ | otherwise =+ fmap fromIntegral+ $ C.throwErrnoIfMinus1Retry _fdWritevBuf+ $ c_safe_writev (fromIntegral fd) bufs (fromIntegral len)++_fdWritevBuf :: String+_fdWritevBuf = "System.Posix.IO.ByteString.fdWritevBuf"+{-# NOINLINE _fdWritevBuf #-}+++-- | Write data from memory to an 'Fd'. This is a variation of+-- 'fdWritevBuf' which returns errors with an 'Either' instead of+-- throwing exceptions.+--+-- /Since: 0.3.3/+tryFdWritevBuf+ :: Fd+ -> Ptr CIovec -- ^ A C-style array of buffers to write.+ -> Int -- ^ How many buffers there are.+ -> IO (Either C.Errno ByteCount)+ -- ^ How many bytes were actually read (zero for EOF).+tryFdWritevBuf fd bufs len+ | len <= 0 = return (Right 0)+ | otherwise =+ fmap (right fromIntegral)+ $ eitherErrnoIfMinus1Retry+ $ c_safe_writev (fromIntegral fd) bufs (fromIntegral len)+++----------------------------------------------------------------+-- | Write a sequence of 'BS.ByteString's to an 'Fd'. The return+-- value is the total number of bytes written. Unfortunately the+-- @writev(2)@ system call does not provide enough information to+-- return the triple that 'fdWrites' does.+--+-- This version will force the spine of the list, converting each+-- @ByteString@ into an @iovec@ (see 'CIovec'), and then call+-- 'fdWritevBuf'. This means we only make one system call, which+-- reduces the overhead of performing context switches. But it also+-- means that we must store the whole list of @ByteString@s in+-- memory at once, and that we must perform some allocation and+-- conversion. Compare against 'fdWrites'.+fdWritev+ :: Fd+ -> [BS.ByteString] -- ^ The strings to write.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdWritev fd cs = do+ rc <- FMA.withArrayLen (map unsafeByteString2CIovec cs) $ \len iovs ->+ fdWritevBuf fd iovs (fromIntegral len)+ -- BUG: is this enough to actually hold onto them?+ mapM_ touchByteString cs+ return rc+++----------------------------------------------------------------+-- @ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)@+-- TODO: Portability: why using `Word8` instead of something else for `void*`?+foreign import ccall safe "pwrite"+ c_safe_pwrite :: CInt -> Ptr Word8 -> CSize -> COff -> IO CSsize+++-- | Write data from memory to a specified position in the 'Fd',+-- but without altering the position stored in the @Fd@. This is+-- exactly equivalent to the XPG4.2 @pwrite(2)@ system call, except+-- that we return 0 bytes written if the @ByteCount@ argument is+-- less than or equal to zero (instead of throwing an errno exception).+-- If there are any errors, then they are thrown as 'IOE.IOError'+-- exceptions.+--+-- /Since: 0.3.0/+fdPwriteBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory containing the data to write.+ -> ByteCount -- ^ How many bytes to try to write.+ -> FileOffset -- ^ Where to write the data to.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdPwriteBuf fd buf nbytes offset+ | nbytes <= 0 = return 0+ | otherwise =+ fmap fromIntegral+ $ C.throwErrnoIfMinus1Retry _fdPwriteBuf+ $ c_safe_pwrite+ (fromIntegral fd)+ (castPtr buf)+ (fromIntegral nbytes)+ (fromIntegral offset)++_fdPwriteBuf :: String+_fdPwriteBuf = "System.Posix.IO.ByteString.fdPwriteBuf"+{-# NOINLINE _fdPwriteBuf #-}+++-- | Write data from memory to a specified position in the 'Fd',+-- but without altering the position stored in the @Fd@. This is a+-- variation of 'fdPwriteBuf' which returns errors with an 'Either'+-- instead of throwing exceptions.+--+-- /Since: 0.3.3/+tryFdPwriteBuf+ :: Fd+ -> Ptr Word8 -- ^ Memory containing the data to write.+ -> ByteCount -- ^ How many bytes to try to write.+ -> FileOffset -- ^ Where to write the data to.+ -> IO (Either C.Errno ByteCount)+ -- ^ How many bytes were actually written.+tryFdPwriteBuf fd buf nbytes offset+ | nbytes <= 0 = return (Right 0)+ | otherwise =+ fmap (right fromIntegral)+ $ eitherErrnoIfMinus1Retry+ $ c_safe_pwrite+ (fromIntegral fd)+ (castPtr buf)+ (fromIntegral nbytes)+ (fromIntegral offset)+++----------------------------------------------------------------+-- | Write data from memory to a specified position in the 'Fd',+-- but without altering the position stored in the @Fd@. This is+-- exactly equivalent to 'fdPwriteBuf'; we just convert the+-- @ByteString@ into its underlying @Ptr Word8@ and @ByteCount@+-- components for passing to 'fdPwriteBuf'.+--+-- /Since: 0.3.0/+fdPwrite+ :: Fd+ -> BS.ByteString -- ^ The string to write.+ -> FileOffset -- ^ Where to write the data to.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdPwrite fd s offset =+ -- N.B., BSU.unsafeUseAsCStringLen does zero copying. Use+ -- BS.useAsCStringLen if there's any chance fdPwriteBuf might+ -- alter the buffer.+ BSU.unsafeUseAsCStringLen s $ \(buf,len) -> do+ fdPwriteBuf fd (castPtr buf) (fromIntegral len) offset+++----------------------------------------------------------------++mode2Int :: SeekMode -> CInt+mode2Int AbsoluteSeek = (#const SEEK_SET)+mode2Int RelativeSeek = (#const SEEK_CUR)+mode2Int SeekFromEnd = (#const SEEK_END)+++-- TODO: reexport `unix-2.8.0.0` version if available...+-- FIXME: what is the behavior for @unix-2.8.0.0@?+--+-- | Repositions the offset of the file descriptor according to the+-- offset and the seeking mode. This is exactly equivalent to the+-- POSIX.1 @lseek(2)@ system call. If there are any errors, then+-- they are thrown as 'IOE.IOError' exceptions.+--+-- This is the same as 'System.Posix.IO.fdSeek' in @unix-2.6.0.1@,+-- but provided here for consistency.+--+-- /Since: 0.3.5/+fdSeek :: Fd -> SeekMode -> FileOffset -> IO FileOffset+fdSeek fd mode off =+ C.throwErrnoIfMinus1 "fdSeek"+ $ Base.c_lseek (fromIntegral fd) off (mode2Int mode)+++-- | Repositions the offset of the file descriptor according to the+-- offset and the seeking mode. This is a variation of 'fdSeek'+-- which returns errors with an @Either@ instead of throwing+-- exceptions.+--+-- /Since: 0.3.5/+tryFdSeek :: Fd -> SeekMode -> FileOffset -> IO (Either C.Errno FileOffset)+tryFdSeek fd mode off =+ eitherErrnoIfMinus1+ $ Base.c_lseek (fromIntegral fd) off (mode2Int mode)++----------------------------------------------------------------+----------------------------------------------------------- fin.
+ src/System/Posix/IO/ByteString/Ext/Lazy.hs view
@@ -0,0 +1,136 @@+{-# OPTIONS_GHC -Wall -fwarn-tabs #-}+-- TODO: cf <http://hpaste.org/76873>+----------------------------------------------------------------+-- 2022.08.28+-- |+-- Module : System.Posix.IO.ByteString.Ext.Lazy+-- Copyright : 2010--2022 wren romano+-- License : BSD-3-Clause+-- Maintainer : wren@cpan.org+-- Stability : experimental+-- Portability : non-portable (requires POSIX.1, XPG4.2)+--+-- Provides a lazy-'BL.ByteString' variant of "System.Posix.IO.ByteString.Ext",+-- to avoid boilerplate code for converting between lazy-'BL.ByteString'+-- and strict @['BS.ByteString']@. This module was renamed in+-- version 0.4.0 to mirror the renaming of the strict module.+-- See the documentation there for the reason why.+----------------------------------------------------------------+module System.Posix.IO.ByteString.Ext.Lazy+ (+ -- * I\/O with file descriptors+ -- ** Reading+ fdRead+ , fdPread+ -- ** Writing+ , fdWrites+ , fdWritev+ ) where++import qualified Data.ByteString as BS+import qualified Data.ByteString.Unsafe as BSU+import qualified System.Posix.IO.ByteString.Ext as PosixBS+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Internal as BLI+import System.Posix.Types (Fd, ByteCount, FileOffset)++----------------------------------------------------------------+-- | Read data from an 'Fd' and convert it to a 'BL.ByteString'.+-- Throws an exception if this is an invalid descriptor, or EOF has+-- been reached. This is a thin wrapper around 'PosixBS.fdRead'.+fdRead+ :: Fd+ -> ByteCount -- ^ How many bytes to try to read.+ -> IO BL.ByteString -- ^ The bytes read.+fdRead fd nbytes+ | nbytes <= 0 = return BL.empty+ | otherwise = do+ s <- PosixBS.fdRead fd nbytes+ return (BLI.chunk s BL.empty)++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' and convert+-- it to a 'BS.ByteString', without altering the position stored+-- in the @Fd@. Throws an exception if this is an invalid descriptor,+-- or EOF has been reached. This is a thin wrapper around+-- 'PosixBS.fdPread'.+--+-- /Since: 0.3.1/+fdPread+ :: Fd+ -> ByteCount -- ^ How many bytes to try to read.+ -> FileOffset -- ^ Where to read the data from.+ -> IO BL.ByteString -- ^ The bytes read.+fdPread fd nbytes offset+ | nbytes <= 0 = return BL.empty+ | otherwise = do+ s <- PosixBS.fdPread fd nbytes offset+ return (BLI.chunk s BL.empty)+++----------------------------------------------------------------+-- | Write a 'BL.ByteString' to an 'Fd'. This function makes one+-- @write(2)@ system call per chunk, as per 'PosixBS.fdWrites'.+fdWrites+ :: Fd+ -> BL.ByteString+ -- ^ The string to write.+ -> IO (ByteCount, BL.ByteString)+ -- ^ How many bytes were actually written, and the remaining+ -- (unwritten) string.+fdWrites fd = go 0+ where+ -- We want to do a left fold in order to avoid stack overflows,+ -- but we need to have an early exit for incomplete writes+ -- (which normally requires a right fold). Hence this recursion.+ go acc BLI.Empty = return (acc, BL.empty)+ go acc (BLI.Chunk c cs) =+ PosixBS.fdWrite fd c >>= \rc ->+ let acc' = acc+rc in acc' `seq`+ let rcInt = fromIntegral rc in rcInt `seq`+ if rcInt == BS.length c+ then go acc' cs+ else return (acc', BLI.Chunk (BSU.unsafeDrop rcInt c) cs)+{-+Using 'BSU.unsafeDrop' above is safe, assuming that+'System.Posix.IO.fdWriteBuf' never returns (rc < 0 || rc > BS.length c).+If we are paranoid about that then we should do the following instead:++ go acc ccs =+ case ccs of+ BLI.Empty -> return (acc, ccs)+ BLI.Chunk c cs -> do+ rc <- PosixBS.fdWrite fd c+ let acc' = acc+rc in acc' `seq` do+ let rcInt = fromIntegral rc in rcInt `seq` do+ case BS.length c of+ len | rcInt == len -> go acc' cs+ | rcInt > len -> error _impossibleByteCount+ | rcInt < 0 -> error _negtiveByteCount+ | rcInt == 0 -> return (acc', ccs) -- trivial optimizing+ | otherwise -> return (acc', BLI.Chunk (BSU.unsafeDrop rcInt c) cs)++_negtiveByteCount =+ "System.Posix.IO.fdWriteBuf: returned a negative byte count"+_impossibleByteCount =+ "System.Posix.IO.fdWriteBuf: returned a byte count greater than the length it was given"+-}+++----------------------------------------------------------------+-- | Write a 'BL.ByteString' to an 'Fd'. This function makes a+-- single @writev(2)@ system call, as per 'PosixBS.fdWritev'.+fdWritev+ :: Fd+ -> BL.ByteString -- ^ The string to write.+ -> IO ByteCount -- ^ How many bytes were actually written.+fdWritev fd s = PosixBS.fdWritev fd (BL.toChunks s)+{-# INLINE fdWritev #-}+-- Hopefully the intermediate list can be fused away...++-- TODO: a variant of `fdWritev` with a return type matching+-- `fdWrites` yet only making the one @writev(2)@ syscall.+++----------------------------------------------------------------+----------------------------------------------------------- fin.
− src/System/Posix/IO/ByteString/Lazy.hs
@@ -1,138 +0,0 @@-{-# OPTIONS_GHC -Wall -fwarn-tabs #-}--- TODO: cf <http://hpaste.org/76873>-------------------------------------------------------------------- 2021.10.17--- |--- Module : System.Posix.IO.ByteString.Lazy--- Copyright : 2010--2022 wren romano--- License : BSD-3-Clause--- Maintainer : wren@cpan.org--- Stability : experimental--- Portability : non-portable (requires POSIX.1, XPG4.2)------ Provides a lazy-'BL.ByteString' file-descriptor based I\/O--- API, designed loosely after the @String@ file-descriptor based--- I\/O API in "System.Posix.IO". The functions here wrap standard--- C implementations of the functions specified by the ISO\/IEC--- 9945-1:1990 (``POSIX.1'') and X\/Open Portability Guide Issue--- 4, Version 2 (``XPG4.2'') specifications.------ These functions are provided mainly as a convenience to avoid--- boilerplate code converting between lazy 'BL.ByteString' and--- strict @['BS.ByteString']@. It may be depricated in the future.------------------------------------------------------------------module System.Posix.IO.ByteString.Lazy- (- -- * I\/O with file descriptors- -- ** Reading- fdRead- , fdPread- -- ** Writing- , fdWrites- , fdWritev- ) where--import qualified Data.ByteString as BS-import qualified Data.ByteString.Unsafe as BSU-import qualified System.Posix.IO.ByteString as PosixBS-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Lazy.Internal as BLI-import System.Posix.Types (Fd, ByteCount, FileOffset)--------------------------------------------------------------------- | Read data from an 'Fd' and convert it to a 'BL.ByteString'.--- Throws an exception if this is an invalid descriptor, or EOF has--- been reached. This is a thin wrapper around 'PosixBS.fdRead'.-fdRead- :: Fd- -> ByteCount -- ^ How many bytes to try to read.- -> IO BL.ByteString -- ^ The bytes read.-fdRead fd nbytes- | nbytes <= 0 = return BL.empty- | otherwise = do- s <- PosixBS.fdRead fd nbytes- return (BLI.chunk s BL.empty)--------------------------------------------------------------------- | Read data from a specified position in the 'Fd' and convert--- it to a 'BS.ByteString', without altering the position stored--- in the @Fd@. Throws an exception if this is an invalid descriptor,--- or EOF has been reached. This is a thin wrapper around--- 'PosixBS.fdPread'.------ /Since: 0.3.1/-fdPread- :: Fd- -> ByteCount -- ^ How many bytes to try to read.- -> FileOffset -- ^ Where to read the data from.- -> IO BL.ByteString -- ^ The bytes read.-fdPread fd nbytes offset- | nbytes <= 0 = return BL.empty- | otherwise = do- s <- PosixBS.fdPread fd nbytes offset- return (BLI.chunk s BL.empty)---------------------------------------------------------------------- | Write a 'BL.ByteString' to an 'Fd'. This function makes one--- @write(2)@ system call per chunk, as per 'PosixBS.fdWrites'.-fdWrites- :: Fd- -> BL.ByteString- -- ^ The string to write.- -> IO (ByteCount, BL.ByteString)- -- ^ How many bytes were actually written, and the remaining- -- (unwritten) string.-fdWrites fd = go 0- where- -- We want to do a left fold in order to avoid stack overflows,- -- but we need to have an early exit for incomplete writes- -- (which normally requires a right fold). Hence this recursion.- go acc BLI.Empty = return (acc, BL.empty)- go acc (BLI.Chunk c cs) =- PosixBS.fdWrite fd c >>= \rc ->- let acc' = acc+rc in acc' `seq`- let rcInt = fromIntegral rc in rcInt `seq`- if rcInt == BS.length c- then go acc' cs- else return (acc', BLI.Chunk (BSU.unsafeDrop rcInt c) cs)-{--Using 'BSU.unsafeDrop' above is safe, assuming that-'System.Posix.IO.fdWriteBuf' never returns (rc < 0 || rc > BS.length c).-If we are paranoid about that then we should do the following instead:-- go acc ccs =- case ccs of- BLI.Empty -> return (acc, ccs)- BLI.Chunk c cs -> do- rc <- PosixBS.fdWrite fd c- let acc' = acc+rc in acc' `seq` do- let rcInt = fromIntegral rc in rcInt `seq` do- case BS.length c of- len | rcInt == len -> go acc' cs- | rcInt > len -> error _impossibleByteCount- | rcInt < 0 -> error _negtiveByteCount- | rcInt == 0 -> return (acc', ccs) -- trivial optimizing- | otherwise -> return (acc', BLI.Chunk (BSU.unsafeDrop rcInt c) cs)--_negtiveByteCount =- "System.Posix.IO.fdWriteBuf: returned a negative byte count"-_impossibleByteCount =- "System.Posix.IO.fdWriteBuf: returned a byte count greater than the length it was given"--}---------------------------------------------------------------------- | Write a 'BL.ByteString' to an 'Fd'. This function makes a--- single @writev(2)@ system call, as per 'PosixBS.fdWritev'.-fdWritev- :: Fd- -> BL.ByteString -- ^ The string to write.- -> IO ByteCount -- ^ How many bytes were actually written.-fdWritev fd s = PosixBS.fdWritev fd (BL.toChunks s)-{-# INLINE fdWritev #-}--- Hopefully the intermediate list can be fused away...------------------------------------------------------------------------------------------------------------------------------- fin.
unix-bytestring.cabal view
@@ -6,18 +6,18 @@ -- <https://github.com/haskell/cabal/issues/4899> ------------------------------------------------------------------- wren gayle romano <wren@cpan.org> ~ 2022.08.28+-- wren gayle romano <wren@cpan.org> ~ 2023.03.19 ---------------------------------------------------------------- Name: unix-bytestring-Version: 0.3.7.8+Version: 0.4.0 Build-Type: Simple Stability: provisional Homepage: https://wrengr.org/software/hackage.html Bug-Reports: https://github.com/wrengr/unix-bytestring/issues Author: wren gayle romano Maintainer: wren@cpan.org-Copyright: 2010–2022 wren romano+Copyright: 2010–2023 wren romano -- Cabal-2.2 requires us to say "BSD-3-Clause" not "BSD3" License: BSD-3-Clause License-File: LICENSE@@ -51,7 +51,8 @@ GHC ==8.10.3, GHC ==9.0.1, GHC ==9.2.4,- GHC ==9.4.1+ GHC ==9.4.4,+ GHC ==9.6.1 Source-Repository head Type: git@@ -62,8 +63,8 @@ Default-Language: Haskell2010 Hs-Source-Dirs: src Exposed-Modules: Foreign.C.Error.Safe- , System.Posix.IO.ByteString- , System.Posix.IO.ByteString.Lazy+ , System.Posix.IO.ByteString.Ext+ , System.Posix.IO.ByteString.Ext.Lazy , System.Posix.Types.Iovec -- We require base>=4.1 for Foreign.C.Error.throwErrnoIfMinus1Retry.@@ -72,8 +73,10 @@ -- (and unix-2.4.0.0 requires base>=4.1 too), except we define -- them on our own for better backwards compatibility. --+ -- FIXME: unix-2.8.0.0 introduces some additional conflicts+ -- -- Not sure what the real minbound is on bytestring...- Build-Depends: base >= 4.1 && < 4.18+ Build-Depends: base >= 4.1 && < 4.19 , bytestring >= 0.9.1.5 && < 0.12 ----------------------------------------------------------------