posix-api-0.3.4.0: src/Posix/File.hs
{-# language BangPatterns #-}
{-# language BinaryLiterals #-}
{-# language MagicHash #-}
{-# language UnliftedFFITypes #-}
module Posix.File
( -- * Functions
uninterruptibleGetDescriptorFlags
, uninterruptibleGetStatusFlags
, uninterruptibleWriteByteArray
, writeByteArray
-- * Types
, DescriptorFlags(..)
, StatusFlags(..)
-- * File Descriptor Flags
, Types.nonblocking
, Types.append
, isReadOnly
, isWriteOnly
, isReadWrite
) where
import Assertion (assertByteArrayPinned)
import Data.Bits ((.&.))
import Posix.File.Types (DescriptorFlags(..),StatusFlags(..))
import System.Posix.Types (Fd(..),CSsize(..))
import Foreign.C.Error (Errno,getErrno)
import Foreign.C.Types (CInt(..),CSize(..))
import Data.Primitive (ByteArray(..))
import GHC.Exts (ByteArray#)
import qualified Posix.File.Types as Types
-- | Get file descriptor flags. This uses the unsafe FFI to
-- perform @fcntl(fd,F_GETFD)@.
uninterruptibleGetDescriptorFlags :: Fd -> IO (Either Errno DescriptorFlags)
uninterruptibleGetDescriptorFlags !fd = c_getFdFlags fd >>= errorsFromDescriptorFlags
-- | Get file status flags. This uses the unsafe FFI to
-- perform @fcntl(fd,F_GETFL)@.
uninterruptibleGetStatusFlags :: Fd -> IO (Either Errno StatusFlags)
uninterruptibleGetStatusFlags !fd = c_getFlFlags fd >>= errorsFromStatusFlags
foreign import ccall unsafe "HaskellPosix.h hs_get_fd_flags"
c_getFdFlags :: Fd -> IO DescriptorFlags
foreign import ccall unsafe "HaskellPosix.h hs_get_fl_flags"
c_getFlFlags :: Fd -> IO StatusFlags
foreign import ccall unsafe "HaskellPosix.h write_offset"
c_unsafe_bytearray_write :: Fd -> ByteArray# -> Int -> CSize -> IO CSsize
foreign import ccall safe "HaskellPosix.h write_offset"
c_safe_bytearray_write :: Fd -> ByteArray# -> Int -> CSize -> IO CSsize
errorsFromDescriptorFlags :: DescriptorFlags -> IO (Either Errno DescriptorFlags)
errorsFromDescriptorFlags r@(DescriptorFlags x) = if x > (-1)
then pure (Right r)
else fmap Left getErrno
errorsFromStatusFlags :: StatusFlags -> IO (Either Errno StatusFlags)
errorsFromStatusFlags r@(StatusFlags x) = if x > (-1)
then pure (Right r)
else fmap Left getErrno
-- | Wrapper for @write(2)@ that takes a byte array and an offset.
-- The byte array does not need to be pinned.
uninterruptibleWriteByteArray ::
Fd -- ^ Socket
-> ByteArray -- ^ Source byte array
-> Int -- ^ Offset into source array
-> CSize -- ^ Length in bytes
-> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer
uninterruptibleWriteByteArray !fd (ByteArray buf) !off !len =
c_unsafe_bytearray_write fd buf off len >>= errorsFromSize
-- | Wrapper for @write(2)@ that takes a byte array and an offset. Uses @safe@ FFI. The byte array must be pinned.
writeByteArray ::
Fd -- ^ Socket
-> ByteArray -- ^ Source byte array
-> Int -- ^ Offset into source array
-> CSize -- ^ Length in bytes
-> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer
writeByteArray !fd !buf0 !off !len =
let !(ByteArray buf1) = assertByteArrayPinned buf0
in c_safe_bytearray_write fd buf1 off len >>= errorsFromSize
errorsFromSize :: CSsize -> IO (Either Errno CSize)
errorsFromSize r = if r > (-1)
then pure (Right (cssizeToCSize r))
else fmap Left getErrno
-- only call this when it is known that the argument is non-negative
cssizeToCSize :: CSsize -> CSize
cssizeToCSize = fromIntegral
isReadOnly :: StatusFlags -> Bool
isReadOnly (StatusFlags x) = x .&. 0b11 == 0
isWriteOnly :: StatusFlags -> Bool
isWriteOnly (StatusFlags x) = x .&. 0b11 == 1
isReadWrite :: StatusFlags -> Bool
isReadWrite (StatusFlags x) = x .&. 0b11 == 2