packages feed

simple-sendfile-0.2.33: Network/Sendfile/IOVec.hsc

{- Original: Network/Socket/ByteString/* -}

-- | Support module for the POSIX writev system call.
module Network.Sendfile.IOVec (
    IOVec(..)
  , SfHdtr(..)
  , withSfHdtr
  , remainingChunks
  ) where

import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Foreign.C.Types (CChar, CInt, CSize)
import Foreign.Marshal (alloca)
import Foreign.Marshal.Array (allocaArray)
import Foreign.Ptr (Ptr, nullPtr, plusPtr)
import Foreign.Storable (Storable(..))

#include <limits.h>
#include <sys/uio.h>
#include <sys/socket.h>

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

data IOVec = IOVec {
    iovBase :: Ptr CChar
  , iovLen  :: CSize
  }

instance Storable IOVec where
  sizeOf _    = (#size struct iovec)
  alignment _ = (#alignment struct iovec)

  peek p = do
      base <- (#peek struct iovec, iov_base) p
      len  <- (#peek struct iovec, iov_len)  p
      return $ IOVec base len

  poke p iov = do
      (#poke struct iovec, iov_base) p (iovBase iov)
      (#poke struct iovec, iov_len)  p (iovLen  iov)

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

data SfHdtr = SfHdtr {
    sfhdtrHdr    :: Ptr IOVec
  , sfhdtrHdrLen :: CInt
  }

instance Storable SfHdtr where
  sizeOf _    = (#size struct sf_hdtr)
  alignment _ = (#alignment struct sf_hdtr)

  peek p = do
      hdr  <- (#peek struct sf_hdtr, headers) p
      hlen <- (#peek struct sf_hdtr, hdr_cnt)  p
      return $ SfHdtr hdr hlen

  poke p sfhdtr = do
      (#poke struct sf_hdtr, headers)  p (sfhdtrHdr sfhdtr)
      (#poke struct sf_hdtr, hdr_cnt)  p (sfhdtrHdrLen sfhdtr)
      (#poke struct sf_hdtr, trailers) p nullPtr
      (#poke struct sf_hdtr, trl_cnt)  p (0 :: CInt)

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

-- The buffers stay valid while 'f' runs since the calls are nested.
withIOVec :: [ByteString] -> ((Ptr IOVec, Int) -> IO a) -> IO a
withIOVec cs f =
    allocaArray csLen $ \aPtr -> go aPtr aPtr cs
  where
    csLen = length cs
    go aPtr _ [] = f (aPtr, csLen)
    go aPtr ptr (s:ss) =
        unsafeUseAsCStringLen s $ \(sPtr, sLen) -> do
            poke ptr $ IOVec sPtr (fromIntegral sLen)
            go aPtr (ptr `plusPtr` sizeOf (undefined :: IOVec)) ss

-- Too many chunks cause EINVAL, so they are concatenated.
withSfHdtr :: [ByteString] -> (Ptr SfHdtr -> IO a) -> IO a
withSfHdtr cs0 f = withIOVec cs $ \(iovecp,len) ->
    alloca $ \sfhdtrp -> do
        poke sfhdtrp $ SfHdtr iovecp (fromIntegral len)
        f sfhdtrp
  where
    cs | length cs0 > (#const IOV_MAX) = [BS.concat cs0]
       | otherwise                     = cs0

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

remainingChunks :: Int -> [ByteString] -> [ByteString]
remainingChunks _ [] = []
remainingChunks i (x:xs)
    | i < len        = BS.drop i x : xs
    | otherwise      = let i' = i - len in i' `seq` remainingChunks i' xs
  where
    len = BS.length x