{-# LINE 1 "src/System/SendFile/Linux.hsc" #-}
{-# LANGUAGE CPP                      #-}
{-# LINE 2 "src/System/SendFile/Linux.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}

-- | Linux system-dependent code for 'sendfile'.
module System.SendFile.Linux (sendFile) where

import Data.Int
import Foreign.C.Error (eAGAIN, getErrno, throwErrno)

{-# LINE 10 "src/System/SendFile/Linux.hsc" #-}
import Foreign.C.Types (CSize(..), CInt(..))

{-# LINE 14 "src/System/SendFile/Linux.hsc" #-}
import Foreign.Marshal (alloca)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (poke)

{-# LINE 18 "src/System/SendFile/Linux.hsc" #-}
import System.Posix.Types (Fd(..), COff(..), CSsize(..))

{-# LINE 22 "src/System/SendFile/Linux.hsc" #-}

sendFile :: IO () -> Fd -> Fd -> Int64 -> Int64 -> IO Int64
sendFile onBlock out_fd in_fd off count
  | count == 0 = return 0
  | off == 0   = do
        sbytes <- sendfile onBlock out_fd in_fd nullPtr bytes
        return $ fromIntegral sbytes
  | otherwise  = alloca $ \poff -> do
        poke poff (fromIntegral off)
        sbytes <- sendfile onBlock out_fd in_fd poff bytes
        return $ fromIntegral sbytes
    where
      bytes = min (fromIntegral count) maxBytes

sendfile :: IO () -> Fd -> Fd -> Ptr COff -> CSize -> IO CSsize
sendfile onBlock out_fd in_fd poff bytes = do
    nsent <- c_sendfile out_fd in_fd poff bytes
    if nsent <= -1
      then do errno <- getErrno
              if errno == eAGAIN
                then onBlock >> sendfile onBlock out_fd in_fd poff bytes
                else throwErrno "System.SendFile.Linux"
      else return nsent

-- max num of bytes in one send
maxBytes :: CSize
maxBytes = maxBound :: CSize

-- sendfile64 gives LFS support
foreign import ccall unsafe "sys/sendfile.h sendfile64" c_sendfile
    :: Fd -> Fd -> Ptr COff -> CSize -> IO CSsize