{-# LINE 1 "src/System/Posix/Types/Iovec.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# OPTIONS_GHC -Wall -fwarn-tabs #-}
----------------------------------------------------------------
--                                                    2022.05.25
-- |
-- Module      :  System.Posix.Types.Iovec
-- Copyright   :  2010--2022 wren romano
-- License     :  BSD-3-Clause
-- Maintainer  :  wren@cpan.org
-- Stability   :  experimental
-- Portability :  non-portable (POSIX.1, XPG4.2; hsc2hs, FFI)
--
-- Imports the C @struct iovec@ type and provides conversion between
-- 'CIovec's and strict 'BS.ByteString's.
----------------------------------------------------------------
module System.Posix.Types.Iovec
    (
    -- * The @struct iovec@ type
      CIovec(..)
    , unsafeByteString2CIovec
    , touchByteString
    , unsafeUseAsCIovec
    , useAsCIovec
    ) where

import           Data.Word                (Word8)
import qualified Data.ByteString          as BS
import qualified Data.ByteString.Internal as BSI
import           Foreign.Ptr              (Ptr)
import qualified Foreign.Ptr              as FFI (castPtr, plusPtr)
import qualified Foreign.ForeignPtr       as FFP
import qualified GHC.ForeignPtr           as GHC_FFP
-- #if ???
-- import qualified Foreign.ForeignPtr.Unsafe as FFP (unsafeForeignPtrToPtr)
-- #endif
import           Foreign.C.Types          (CSize)
import           Foreign.Storable         (Storable(..))

-- iovec, writev, and readv are in <sys/uio.h>, but we must include
-- <sys/types.h> and <unistd.h> for legacy reasons.




----------------------------------------------------------------

-- | Haskell type representing the C @struct iovec@ type. This is
-- exactly like 'Foreign.C.String.CStringLen' except there's actually
-- struct definition on the C side.
data CIovec = CIovec
    { CIovec -> Ptr Word8
iov_base :: {-# UNPACK #-} !(Ptr Word8) -- char* or void*
    , CIovec -> CSize
iov_len  :: {-# UNPACK #-} !CSize       -- size_t
    }

-- This traditional macro has been incorporated into hcs2hs-0.68
-- (GHC 8.0), so we cpp guard it for older compilers.
-- <https://gitlab.haskell.org/ghc/ghc/-/wikis/migration/8.0#hsc2hs-defines-an-alignment-macro>

{-# LINE 62 "src/System/Posix/Types/Iovec.hsc" #-}

instance Storable CIovec where
    alignment :: CIovec -> Int
alignment CIovec
_ = Int
8
{-# LINE 65 "src/System/Posix/Types/Iovec.hsc" #-}

    sizeOf :: CIovec -> Int
sizeOf CIovec
_    = (Int
16)
{-# LINE 67 "src/System/Posix/Types/Iovec.hsc" #-}

    peek :: Ptr CIovec -> IO CIovec
peek Ptr CIovec
ptr = do
        Ptr Word8
base <- (\Ptr CIovec
hsc_ptr -> forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr CIovec
hsc_ptr Int
0) Ptr CIovec
ptr
{-# LINE 70 "src/System/Posix/Types/Iovec.hsc" #-}
        CSize
len  <- (\Ptr CIovec
hsc_ptr -> forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr CIovec
hsc_ptr Int
8)  Ptr CIovec
ptr
{-# LINE 71 "src/System/Posix/Types/Iovec.hsc" #-}
        forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr Word8 -> CSize -> CIovec
CIovec Ptr Word8
base CSize
len)

    poke :: Ptr CIovec -> CIovec -> IO ()
poke Ptr CIovec
ptr (CIovec Ptr Word8
base CSize
len) = do
        (\Ptr CIovec
hsc_ptr -> forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr CIovec
hsc_ptr Int
0) Ptr CIovec
ptr Ptr Word8
base
{-# LINE 75 "src/System/Posix/Types/Iovec.hsc" #-}
        (\Ptr CIovec
hsc_ptr -> forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr CIovec
hsc_ptr Int
8)  Ptr CIovec
ptr CSize
len
{-# LINE 76 "src/System/Posix/Types/Iovec.hsc" #-}


-- | /O(1) construction/ Convert a @ByteString@ into an @CIovec@.
--
-- This function is /unsafe/ in two ways:
--
-- * After calling this function the @CIovec@ shares the underlying
-- byte buffer with the original @ByteString@. Thus, modifying the
-- @CIovec@ either in C or using poke will cause the contents of
-- the @ByteString@ to change, breaking referential transparency.
-- Other @ByteStrings@ created by sharing (such as those produced
-- via 'BS.take' or 'BS.drop') will also reflect these changes.
--
-- * Also, even though the @CIovec@ shares the underlying byte
-- buffer, it does so in a way that will not keep the original
-- @ByteString@ alive with respect to garbage collection. Thus, the
-- byte buffer could be collected out from under the @CIovec@. To
-- prevent this, you must use 'touchByteString' after the last point
-- where the @CIovec@ is needed.
unsafeByteString2CIovec :: BS.ByteString -> CIovec
unsafeByteString2CIovec :: ByteString -> CIovec
unsafeByteString2CIovec (BSI.PS ForeignPtr Word8
fptr Int
offset Int
len) =
    Ptr Word8 -> CSize -> CIovec
CIovec
        (forall a. ForeignPtr a -> Ptr a
GHC_FFP.unsafeForeignPtrToPtr ForeignPtr Word8
fptr forall a b. Ptr a -> Int -> Ptr b
`FFI.plusPtr` Int
offset)
        (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
{-# INLINE unsafeByteString2CIovec #-}


-- | Keep the @ByteString@ alive. See 'unsafeByteString2CIovec'.
touchByteString :: BS.ByteString -> IO ()
touchByteString :: ByteString -> IO ()
touchByteString (BSI.PS ForeignPtr Word8
fptr Int
_ Int
_) = forall a. ForeignPtr a -> IO ()
FFP.touchForeignPtr ForeignPtr Word8
fptr
{-# INLINE touchByteString #-}


-- | /O(1) construction/ Use a @ByteString@ with a function requiring
-- a @CIovec@.
--
-- This function does zero copying, and merely unwraps a @ByteString@
-- to appear as an @CIovec@. It is /unsafe/ in the same way as
-- 'unsafeByteString2CIovec'.
unsafeUseAsCIovec :: BS.ByteString -> (CIovec -> IO a) -> IO a
unsafeUseAsCIovec :: forall a. ByteString -> (CIovec -> IO a) -> IO a
unsafeUseAsCIovec (BSI.PS ForeignPtr Word8
fptr Int
offset Int
len) CIovec -> IO a
io =
    forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
FFP.withForeignPtr ForeignPtr Word8
fptr forall a b. (a -> b) -> a -> b
$ \Ptr Word8
ptr ->
        CIovec -> IO a
io (Ptr Word8 -> CSize -> CIovec
CIovec (Ptr Word8
ptr forall a b. Ptr a -> Int -> Ptr b
`FFI.plusPtr` Int
offset) (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len))
{-# INLINE unsafeUseAsCIovec #-}
-- The above version saves a case match on @s@ vs using
-- 'unsafeByteString2CIovec' and 'touchByteString'


-- | /O(n) construction/ Use a @ByteString@ with a function requiring
-- a @CIovec@.
--
-- As with 'BS.useAsCString' and 'BS.useAsCStringLen', this function
-- makes a copy of the original @ByteString@ via @memcpy(3)@. The
-- copy will be freed automatically. See 'unsafeUseAsCIovec' for a
-- zero-copying version.
useAsCIovec :: BS.ByteString -> (CIovec -> IO a) -> IO a
useAsCIovec :: forall a. ByteString -> (CIovec -> IO a) -> IO a
useAsCIovec s :: ByteString
s@(BSI.PS ForeignPtr Word8
_ Int
_ Int
len) CIovec -> IO a
io =
    forall a. ByteString -> (CString -> IO a) -> IO a
BS.useAsCString ByteString
s forall a b. (a -> b) -> a -> b
$ \CString
cstr ->
        CIovec -> IO a
io (Ptr Word8 -> CSize -> CIovec
CIovec (forall a b. Ptr a -> Ptr b
FFI.castPtr CString
cstr) (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len))
{-# INLINE useAsCIovec #-}
{-
This definition is essentially verbatim 'BS.useAsCStringLen'. We
can save two 'FFI.castPtr' and one 'fromIntegral' if we instead do
an essentially verbatim 'BS.useAsCString':

    useAsCIovec s@(BSI.PS fptr offset len) io = do
        let lenCSize = fromIntegral len
        FMA.allocaBytes (len+1) $ \buf ->
            FFP.withForeignPtr fptr $ \ptr -> do
                BSI.memcpy buf (ptr `FFI.plusPtr` offset) lenCSize
                pokeByteOff buf len (0 :: Word8) -- add null-terminator
                io (CIovec buf lenCSize)
-}

----------------------------------------------------------------
----------------------------------------------------------- fin.