packages feed

unix-bytestring 0.3.7.8 → 0.4.0.4

raw patch · 7 files changed

Files

CHANGELOG view
@@ -1,3 +1,15 @@+0.4.0.4 (2026-02-26):+    - Updating version bounds for GHC 9.14+0.4.0.3 (2025-02-11):+    - Updating version bounds for GHC 9.12+0.4.0.2 (2024-08-28):+    - Updating version bounds for GHC 9.10+0.4.0.1 (2024-02-19):+    - Updating version bounds for GHC 9.8+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):
README.md view
@@ -1,8 +1,9 @@ unix-bytestring ===============+[![CI Status](https://github.com/wrengr/unix-bytestring/actions/workflows/ci.yml/badge.svg)](https://github.com/wrengr/unix-bytestring/actions?query=workflow%3Aci+-event%3Apull_request) [![Hackage version](https://img.shields.io/hackage/v/unix-bytestring.svg?style=flat)](https://hackage.haskell.org/package/unix-bytestring) -[![Build Status](https://github.com/wrengr/unix-bytestring/workflows/ci/badge.svg)](https://github.com/wrengr/unix-bytestring/actions?query=workflow%3Aci)-[![Dependencies](https://img.shields.io/hackage-deps/v/unix-bytestring.svg?style=flat)](http://packdeps.haskellers.com/specific?package=unix-bytestring)+[![Stackage LTS version](https://stackage.org/package/unix-bytestring/badge/lts)](https://stackage.org/lts/package/unix-bytestring)+[![Stackage Nightly version](https://stackage.org/package/unix-bytestring/badge/nightly)](https://stackage.org/nightly/package/unix-bytestring)  Unix/Posix-specific functions for `ByteString`s. 
− 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>                ~ 2026-02-26 ----------------------------------------------------------------  Name:           unix-bytestring-Version:        0.3.7.8+Version:        0.4.0.4 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–2025 wren romano -- Cabal-2.2 requires us to say "BSD-3-Clause" not "BSD3" License:        BSD-3-Clause License-File:   LICENSE@@ -51,7 +51,12 @@     GHC ==8.10.3,     GHC ==9.0.1,     GHC ==9.2.4,-    GHC ==9.4.1+    GHC ==9.4.8,+    GHC ==9.6.5,+    GHC ==9.8.2,+    GHC ==9.10.1,+    GHC ==9.12.1,+    GHC ==9.14.1  Source-Repository head     Type:     git@@ -62,8 +67,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,9 +77,9 @@     -- (and unix-2.4.0.0 requires base>=4.1 too), except we define     -- them on our own for better backwards compatibility.     ---    -- Not sure what the real minbound is on bytestring...-    Build-Depends: base       >= 4.1     && < 4.18-                 , bytestring >= 0.9.1.5 && < 0.12+    -- FIXME: unix-2.8.0.0 introduces some additional conflicts+    Build-Depends: base       >= 4.9    && < 4.23+                 , bytestring >= 0.10.8 && < 0.13  ---------------------------------------------------------------- ----------------------------------------------------------- fin.