Skip to content

Commit

Permalink
Fixes #35: fd{Read,Write} with ByteString payload
Browse files Browse the repository at this point in the history
  • Loading branch information
mmhat authored and Bodigrim committed Jul 16, 2022
1 parent c83bcdf commit e0f4580
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 31 deletions.
35 changes: 32 additions & 3 deletions System/Posix/IO.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ module System.Posix.IO (

) where

import Foreign ( allocaBytes, castPtr )
import Foreign.C ( peekCStringLen, withCStringLen )

import GHC.IO.Exception ( IOErrorType(EOF) )

import System.IO.Error ( ioeSetErrorString, mkIOError )

import System.Posix.Types
import System.Posix.Error
import System.Posix.IO.Common
Expand All @@ -88,9 +95,9 @@ openFdAt :: Maybe Fd -- ^ Optional directory file descriptor
-> OpenFileFlags -- ^ Append, exclusive, truncate, etc.
-> IO Fd
openFdAt fdMay name how flags =
withFilePath name $ \str ->
throwErrnoPathIfMinus1Retry "openFdAt" name $
openat_ fdMay str how flags
withFilePath name $ \str ->
throwErrnoPathIfMinus1Retry "openFdAt" name $
openat_ fdMay str how flags

-- |Create and open this file in WriteOnly mode. A special case of
-- 'openFd'. See 'System.Posix.Files' for information on how to use
Expand All @@ -111,3 +118,25 @@ createFileAt :: Maybe Fd -- ^ Optional directory file descriptor
-> IO Fd
createFileAt fdMay name mode
= openFdAt fdMay name WriteOnly defaultFileFlags{ trunc=True, creat=(Just mode) }

-- | Read data from an 'Fd' and convert it to a 'String' using the locale encoding.
-- Throws an exception if this is an invalid descriptor, or EOF has been
-- reached.
fdRead :: Fd
-> ByteCount -- ^How many bytes to read
-> IO (String, ByteCount) -- ^The bytes read, how many bytes were read.
fdRead _fd 0 = return ("", 0)
fdRead fd nbytes =
allocaBytes (fromIntegral nbytes) $ \ buf -> do
rc <- fdReadBuf fd buf nbytes
case rc of
0 -> ioError (ioeSetErrorString (mkIOError EOF "fdRead" Nothing Nothing) "EOF")
n -> do
s <- peekCStringLen (castPtr buf, fromIntegral n)
return (s, n)

-- | Write a 'String' to an 'Fd' using the locale encoding.
fdWrite :: Fd -> String -> IO ByteCount
fdWrite fd str =
withCStringLen str $ \ (buf,len) ->
fdWriteBuf fd (castPtr buf) (fromIntegral len)
38 changes: 34 additions & 4 deletions System/Posix/IO/ByteString.hsc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{-# LANGUAGE Safe #-}
{-# LANGUAGE Trustworthy #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Posix.IO.ByteString
Expand Down Expand Up @@ -63,6 +63,16 @@ module System.Posix.IO.ByteString (

) where

import Data.ByteString ( ByteString, empty )
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Unsafe as BU

import Foreign ( castPtr )

import GHC.IO.Exception ( IOErrorType(EOF) )

import System.IO.Error ( ioeSetErrorString, mkIOError )

import System.Posix.Types
import System.Posix.IO.Common

Expand All @@ -88,9 +98,9 @@ openFdAt :: Maybe Fd -- ^ Optional directory file descriptor
-> OpenFileFlags -- ^ Append, exclusive, truncate, etc.
-> IO Fd
openFdAt fdMay name how flags =
withFilePath name $ \str ->
throwErrnoPathIfMinus1Retry "openFdAt" name $
openat_ fdMay str how flags
withFilePath name $ \str ->
throwErrnoPathIfMinus1Retry "openFdAt" name $
openat_ fdMay str how flags

-- |Create and open this file in WriteOnly mode. A special case of
-- 'openFd'. See 'System.Posix.Files' for information on how to use
Expand All @@ -111,3 +121,23 @@ createFileAt :: Maybe Fd -- ^ Optional directory file descriptor
-> IO Fd
createFileAt fdMay name mode
= openFdAt fdMay name WriteOnly defaultFileFlags{ trunc=True, creat=(Just mode) }

-- | Read data from an 'Fd' and return it as a 'ByteString'.
-- Throws an exception if this is an invalid descriptor, or EOF has been
-- reached.
fdRead :: Fd
-> ByteCount -- ^How many bytes to read
-> IO ByteString -- ^The bytes read
fdRead _fd 0 = return empty
fdRead fd nbytes =
BI.createUptoN (fromIntegral nbytes) $ \ buf -> do
rc <- fdReadBuf fd buf nbytes
case rc of
0 -> ioError (ioeSetErrorString (mkIOError EOF "fdRead" Nothing Nothing) "EOF")
n -> return (fromIntegral n)

-- | Write a 'ByteString' to an 'Fd'.
fdWrite :: Fd -> ByteString -> IO ByteCount
fdWrite fd bs =
BU.unsafeUseAsCStringLen bs $ \ (buf,len) ->
fdWriteBuf fd (castPtr buf) (fromIntegral len)
25 changes: 1 addition & 24 deletions System/Posix/IO/Common.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ module System.Posix.IO.Common (
-- |Programmers using the 'fdRead' and 'fdWrite' API should be aware that
-- EAGAIN exceptions may occur for non-blocking IO!

fdRead, fdWrite,
fdReadBuf, fdWriteBuf,

-- ** Seeking
Expand Down Expand Up @@ -447,23 +446,7 @@ waitToSetLock (Fd fd) lock = do
#endif // HAVE_F_GETLK

-- -----------------------------------------------------------------------------
-- fd{Read,Write}

-- | Read data from an 'Fd' and convert it to a 'String' using the locale encoding.
-- Throws an exception if this is an invalid descriptor, or EOF has been
-- reached.
fdRead :: Fd
-> ByteCount -- ^How many bytes to read
-> IO (String, ByteCount) -- ^The bytes read, how many bytes were read.
fdRead _fd 0 = return ("", 0)
fdRead fd nbytes = do
allocaBytes (fromIntegral nbytes) $ \ buf -> do
rc <- fdReadBuf fd buf nbytes
case rc of
0 -> ioError (ioeSetErrorString (mkIOError EOF "fdRead" Nothing Nothing) "EOF")
n -> do
s <- peekCStringLen (castPtr buf, fromIntegral n)
return (s, n)
-- fd{Read,Write}Buf

-- | Read data from an 'Fd' into memory. This is exactly equivalent
-- to the POSIX @read@ function.
Expand All @@ -480,12 +463,6 @@ fdReadBuf fd buf nbytes =
foreign import ccall safe "read"
c_safe_read :: CInt -> Ptr CChar -> CSize -> IO CSsize

-- | Write a 'String' to an 'Fd' using the locale encoding.
fdWrite :: Fd -> String -> IO ByteCount
fdWrite fd str =
withCStringLen str $ \ (buf,len) ->
fdWriteBuf fd (castPtr buf) (fromIntegral len)

-- | Write data from memory to an 'Fd'. This is exactly equivalent
-- to the POSIX @write@ function.
fdWriteBuf :: Fd
Expand Down

0 comments on commit e0f4580

Please sign in to comment.