sendfile 0.2 → 0.3
raw patch · 10 files changed
+181/−195 lines, 10 filesdep +Win32dep ~base
Dependencies added: Win32
Dependency ranges changed: base
Files
- csrc/sendfile_linux.c +4/−24
- csrc/sendfile_win32.c +11/−34
- sendfile.cabal +16/−13
- src/Network/Socket/SendFile.hs +28/−0
- src/Network/Socket/SendFile/Internal.hs +39/−0
- src/Network/Socket/SendFile/Linux.hsc +27/−0
- src/Network/Socket/SendFile/Portable.hs +13/−0
- src/Network/Socket/SendFile/Win32.hsc +43/−0
- src/SendFile.hs +0/−33
- src/SendFile/Internal.hsc +0/−91
csrc/sendfile_linux.c view
@@ -1,29 +1,9 @@ /* LINUX */-#include <errno.h>-#include <fcntl.h>-#include <unistd.h>+#include <stdlib.h> #include <sys/sendfile.h>-#include <sys/stat.h> -/* return value is an errno code or 0 if there is no error */-int c_sendfile_linux(const int out_fd, const char* in_fp, const long _offset, const long _count) {- off_t offset = _offset;- size_t count = _count;- int in_fd = open(in_fp, O_RDONLY);- - if(in_fd == -1) {- return errno;- }- - if(sendfile(out_fd, in_fd, &offset, count) == -1) {- return errno;- }-- if(close(in_fd) == -1) {- return errno;- }- - /* return 0 to indicate no errors */- return 0;+ssize_t c_sendfile_linux(const int out_fd, const int in_fd, const size_t count) {+ return sendfile(out_fd, in_fd, NULL, count); }+
csrc/sendfile_win32.c view
@@ -2,39 +2,16 @@ /* WIN32 */ #include <windows.h> #include <mswsock.h>+#include <io.h> -/* return value is 0 if there is no error otherwise it is a system error code defined here:- * http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx- */-int c_sendfile_win32(const int out_fd, const char* in_fp, const long _offset, const long _count) {- LONG offset = _offset;- DWORD count = _count;- HANDLE in_hdl = CreateFile(- in_fp,- GENERIC_READ,- FILE_SHARE_READ,- NULL,- OPEN_EXISTING,- FILE_FLAG_SEQUENTIAL_SCAN,- NULL- );- - if(in_hdl == INVALID_HANDLE_VALUE) {- return GetLastError();- }- - if(SetFilePointer(in_hdl, offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {- return GetLastError();- }- - if(! TransmitFile(out_fd, in_hdl, count, 0, NULL, NULL, TF_USE_KERNEL_APC)) {- return WSAGetLastError();- }- - if(! CloseHandle(in_hdl)) {- return GetLastError();- }- - /* return 0 to indicate no errors */- return 0;+BOOL c_TransmitFile(SOCKET out_fd, HANDLE in_hdl, DWORD count) {+ /* http://msdn.microsoft.com/en-us/library/ms740565(VS.85).aspx */+ return TransmitFile(out_fd, in_hdl, count, 0, NULL, NULL, TF_USE_KERNEL_APC); }++long c_get_osfhandle(int fd) {+ /* http://support.microsoft.com/kb/99173 - MAY BE IMPORTANT */+ /* http://msdn.microsoft.com/en-us/library/ks2530z6.aspx */+ return _get_osfhandle(fd);+}+
sendfile.cabal view
@@ -1,13 +1,10 @@ name: sendfile-version: 0.2+version: 0.3 stability: experimental synopsis: A portable sendfile library description: A library which exposes zero-copy sendfile functionality in a portable way. If a platform does not support sendfile, a fallback implementation in haskell is provided. .- Currently supported platforms:- * Windows 2000+ (Native)- * Linux 2.6+ (Native)- * Everything else (Haskell)+ Currently supported platforms: Windows 2000+ (Native), Linux 2.6+ (Native), Everything else (Haskell). license: BSD3 license-file: LICENSE@@ -25,23 +22,29 @@ library hs-source-dirs: src - exposed-modules: SendFile+ exposed-modules: Network.Socket.SendFile - other-modules: SendFile.Internal+ other-modules: Network.Socket.SendFile.Internal - build-depends: base >= 3 && < 5,+ build-depends: base >= 4 && < 5, network >= 2 && < 3- ++ ghc-options: -Wall+ if os(windows) && !flag(portable)- cc-options: -DWIN32_SENDFILE+ cpp-options: -DWIN32_SENDFILE+ build-depends: Win32 c-sources: csrc/sendfile_win32.c- extra-libraries: kernel32, mswsock+ extra-libraries: mswsock+ other-modules: Network.Socket.SendFile.Win32 else if os(linux) && !flag(portable)- cc-options: -DLINUX_SENDFILE+ cpp-options: -DLINUX_SENDFILE c-sources: csrc/sendfile_linux.c+ other-modules: Network.Socket.SendFile.Linux else- cc-options: -DPORTABLE_SENDFILE+ cpp-options: -DPORTABLE_SENDFILE+ other-modules: Network.Socket.SendFile.Portable build-depends: bytestring >= 0.9.1.4 && < 0.10 source-repository head
+ src/Network/Socket/SendFile.hs view
@@ -0,0 +1,28 @@+-- | A cross-platform wrapper for sendfile -- this implements an available operating-system call if supported, otherwise it falls back to a portable haskell implementation. SendFile will flush the output Handle before transmission.+--+-- Keep in mind that the input should only be a `Handle` derived from a network socket (by using socketToHandle). Using something other than a network socket for the input Handle will result in undefined behavior. Furthermore, for consistent read/write behavior, both the output and input handles should be opened in Binary mode rather than Text mode (especially if you are using hSeek).+module Network.Socket.SendFile (+ sendFile,+ sendFile',+ sendFileMode+ ) where+ +import qualified Network.Socket.SendFile.Internal (sendFile, sendFile', sendFileMode)+import System.IO (Handle)++-- | The simplest interface. Simply give it an output `Handle` and the `FilePath` to the input file.+sendFile :: Handle -- ^ The output handle+ -> FilePath -- ^ The path where the input file resides+ -> IO ()+sendFile = Network.Socket.SendFile.Internal.sendFile++-- | A more powerful interface than sendFile, sendFile' accepts a `Handle` for the input file instead of a `FilePath` and the number of bytes you would like to read. This unlocks the full potential `Handle`(s). For instance, if you wanted to start reading from a particular offset, you could utilize `hSeek`. If you need the file size you can use 'hFileSize'.+sendFile' :: Handle -- ^ The output handle+ -> Handle -- ^ The input handle+ -> Integer -- ^ The number of bytes to read+ -> IO ()+sendFile' = Network.Socket.SendFile.Internal.sendFile'++-- | Returns the mode that sendfile was compiled with. Mainly for debugging use. Possible values are 'WIN32_SENDFILE', 'LINUX_SENDFILE', and 'PORTABLE_SENDFILE'.+sendFileMode :: String -- ^ The mode that sendfile was compiled with+sendFileMode = Network.Socket.SendFile.Internal.sendFileMode
+ src/Network/Socket/SendFile/Internal.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE CPP #-}+module Network.Socket.SendFile.Internal (+ sendFile,+ sendFile',+ sendFileMode+ ) where++import System.IO (+ Handle,+ IOMode(..),+ hFileSize,+ withBinaryFile+ )++#if defined(WIN32_SENDFILE)+import Network.Socket.SendFile.Win32 (sendFile')++sendFileMode :: String+sendFileMode = "WIN32_SENDFILE"+#endif++#if defined(LINUX_SENDFILE)+import Network.Socket.SendFile.Linux (sendFile')++sendFileMode :: String+sendFileMode = "LINUX_SENDFILE"+#endif++#if defined(PORTABLE_SENDFILE)+import Network.Socket.SendFile.Portable (sendFile')++sendFileMode :: String+sendFileMode = "PORTABLE_SENDFILE"+#endif++sendFile :: Handle -> FilePath -> IO ()+sendFile outp infp = withBinaryFile infp ReadMode $ \inp -> do+ count <- hFileSize inp+ sendFile' outp inp count
+ src/Network/Socket/SendFile/Linux.hsc view
@@ -0,0 +1,27 @@+{-# LANGUAGE ForeignFunctionInterface #-} +-- | Linux system-dependent code for 'sendfile'. +module Network.Socket.SendFile.Linux (sendFile') where+import Data.Int+import Data.Word +import Foreign.C.Error (throwErrno) +import GHC.IOBase (FD, haFD) +import GHC.Handle (withHandle_)+import System.IO (Handle, hFlush) ++#include <sys/sendfile.h>+ +sendFile' :: Handle -> Handle -> Integer -> IO () +sendFile' outp inp count = do + -- flush outp before sending + hFlush outp + withHandle_ "Network.Socket.SendFile.Linux.sendFile'" outp $ \outp' -> do + withHandle_ "Network.Socket.SendFile.Linux.sendFile'" inp $ \inp' -> do + let out_fd = haFD outp'+ let in_fd = haFD inp' + res <- c_sendfile_linux out_fd in_fd (fromIntegral count) + if res == -1 + then throwErrno "Network.Socket.SendFile.Linux.sendFile'" + else return () + +foreign import ccall + c_sendfile_linux :: FD -> FD -> (#type size_t) -> IO (#type ssize_t)
+ src/Network/Socket/SendFile/Portable.hs view
@@ -0,0 +1,13 @@+-- | Portable implementation of sendfile. +module Network.Socket.SendFile.Portable (sendFile') where + +import Data.ByteString.Char8 +import Prelude hiding (readFile) +import System.IO (Handle, hFlush) + + +-- FIXME: possibly immature / inefficient implementation +sendFile' :: Handle -> Handle -> Integer -> IO () +sendFile' outp inp count = do + hPutStr outp =<< hGet inp (fromIntegral count) + hFlush outp -- match the behavior that all data is "flushed to the os" of native implementations
+ src/Network/Socket/SendFile/Win32.hsc view
@@ -0,0 +1,43 @@+{-# LANGUAGE ForeignFunctionInterface #-} +-- | Win32 system-dependent code for 'TransmitFile'. +module Network.Socket.SendFile.Win32 (sendFile') where +import Data.Int +import Data.Word +import Foreign.C.Error (throwErrno) +import Foreign.Ptr (IntPtr, intPtrToPtr) +import GHC.Handle (withHandle_) +import GHC.IOBase (FD, haFD) +import System.IO (Handle, hFlush) +import System.Win32.Types + +#include <windows.h> + +sendFile' :: Handle -> Handle -> Integer -> IO () +sendFile' outp inp count = do + hFlush outp -- flush the buffer before invoking transmitFile + withHandle_ "Network.Socket.SendFile.Win32.sendFile'" outp $ \outp' -> do + withHandle_ "Network.Socket.SendFile.Win32.sendFile'" inp $ \inp' -> do + let out_fd = haFD outp' + in_hdl <- get_osfhandle (haFD inp') + transmitFile (fromIntegral out_fd) in_hdl (fromIntegral count) + +get_osfhandle :: FD -- ^ User file descriptor. + -> IO HANDLE -- ^ The operating-system file handle. +get_osfhandle fd = do + res <- c_get_osfhandle fd + if res == (#const INVALID_HANDLE_VALUE) + then throwErrno "Network.Socket.SendFile.Win32.get_osfhandle" + else return (intPtrToPtr res) + +transmitFile :: FD -- ^ A handle to a connected socket. + -> HANDLE -- ^ A handle to the open file that the TransmitFile function transmits. + -> DWORD -- ^ The number of bytes in the file to transmit. + -> IO () +transmitFile out_fd in_hdl count = + failIf_ (== 0) "Network.Socket.SendFile.Win32.transmitFile" (c_TransmitFile out_fd in_hdl count) + +foreign import ccall + c_get_osfhandle :: FD -> IO IntPtr + +foreign import ccall + c_TransmitFile :: FD -> HANDLE -> DWORD -> IO (#type BOOL)
− src/SendFile.hs
@@ -1,33 +0,0 @@-module SendFile (- fileSize,- sendFile,- sendFile',- sendFileMode- ) where- -import qualified SendFile.Internal-import System.IO (Handle(..))---- | Get the size of a file.-fileSize :: FilePath -- ^ The path of the file- -> IO Int -- ^ The size of the file (in bytes)-fileSize = SendFile.Internal.fileSize---- | A cross-platform wrapper for sendfile -- this implements an available operating-system call if supported, otherwise it falls back to a portable haskell implementation. It will flush the output Handle before handing it to the operating system for transmission.-sendFile :: Handle -- ^ The output Handle- -> FilePath -- ^ The path where the input file resides- -> IO ()-sendFile = SendFile.Internal.sendFile---- | Like sendFile except that it additionally allows you to specify an offset and count. The behavior of reading beyond the end of the file is undefined.-sendFile' :: Handle -- ^ The output Handle- -> FilePath -- ^ The path where the input file resides- -> Int -- ^ The starting offset of the file (in bytes)- -> Int -- ^ The number of bytes to read- -> IO ()-sendFile' = SendFile.Internal.sendFile'---- | Returns the mode that sendfile was compiled with. Mainly for debugging use. Possible values are 'WIN32_SENDFILE', 'LINUX_SENDFILE', and 'PORTABLE_SENDFILE'.-sendFileMode :: String -- ^ The mode that sendfile was compiled with-sendFileMode = SendFile.Internal.sendFileMode-
− src/SendFile/Internal.hsc
@@ -1,91 +0,0 @@-{-# LANGUAGE CPP, ForeignFunctionInterface #-}-module SendFile.Internal (- fileSize,- sendFile,- sendFile',- sendFileMode- ) where--import System.IO (- Handle(..),- IOMode(..),- SeekMode(..),- hFileSize,- hFlush,- hSeek,- withBinaryFile- )--#if defined(WIN32_SENDFILE)-import Foreign.C-import GHC.IOBase (haFD)-import GHC.Handle (withHandle_)--sendFileMode :: String-sendFileMode = "WIN32_SENDFILE"--sendFile' :: Handle -> FilePath -> Int -> Int -> IO ()-sendFile' outh infp offset count = do- -- flush outh before handing it sendFile- hFlush outh- withHandle_ "sendFile" outh $ \outh' -> do - withCString infp $ \in_fp -> do- let out_fd = haFD outh'- err <- c_sendfile_win32 out_fd in_fp (fromIntegral offset) (fromIntegral count)- if err == 0- then return ()- else fail ("system error " ++ show err)- -foreign import ccall- c_sendfile_win32 :: CInt -> CString -> CLong -> CLong -> IO Int-#endif--#if defined(LINUX_SENDFILE)-import Foreign.C-import GHC.IOBase (haFD)-import GHC.Handle (withHandle_)--sendFileMode :: String-sendFileMode = "LINUX_SENDFILE"--sendFile' :: Handle -> FilePath -> Int -> Int -> IO ()-sendFile' outh infp offset count = do- -- flush outh before handing it sendFile- hFlush outh- withHandle_ "sendFile" outh $ \outh' -> do - withCString infp $ \in_fp -> do- let out_fd = haFD outh'- err <- c_sendfile_linux out_fd in_fp (fromIntegral offset) (fromIntegral count)- if err == 0- then return ()- else fail ("errno " ++ show err)- -foreign import ccall- c_sendfile_linux :: CInt -> CString -> CLong -> CLong -> IO Int-#endif--#if defined(PORTABLE_SENDFILE)-import Prelude hiding (readFile)-import Data.ByteString.Char8--sendFileMode :: String-sendFileMode = "PORTABLE_SENDFILE"---- FIXME: possibly immature / inefficient implementation-sendFile' :: Handle -> FilePath -> Int -> Int -> IO ()-sendFile' outh infp offset count =- withBinaryFile infp ReadMode $ \inh -> do- hSeek inh AbsoluteSeek (fromIntegral offset)- hPutStr outh =<< hGet inh count- hFlush outh - return ()-#endif--fileSize :: FilePath -> IO Int-fileSize fp = fmap fromIntegral (withBinaryFile fp ReadMode hFileSize)--sendFile :: Handle -> FilePath -> IO ()-sendFile outh infp = do- count <- fileSize infp- sendFile' outh infp 0 count-