packages feed

unix-bytestring (empty) → 0.3.2

raw patch · 6 files changed

+899/−0 lines, 6 filesdep +basedep +bytestringbuild-type:Customsetup-changed

Dependencies added: base, bytestring

Files

+ LICENSE view
@@ -0,0 +1,33 @@+Copyright (c) 2010, 2011, wren ng thornton.+ALL RIGHTS RESERVED.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of the copyright holders nor the names of+      other contributors may be used to endorse or promote products+      derived from this software without specific prior written+      permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.+
+ Setup.hs view
@@ -0,0 +1,45 @@+#!/usr/bin/env runhaskell+-- Cf. <http://www.mail-archive.com/haskell-cafe@haskell.org/msg59984.html>++{-# OPTIONS_GHC -Wall -fwarn-tabs -fno-warn-missing-signatures #-}+module Main (main) where+import Distribution.Simple+import Distribution.Simple.LocalBuildInfo (withPrograms, buildDir)+import Distribution.Simple.Program        (userSpecifyArgs)+import System.Directory+import System.FilePath+import Control.Monad (forM_)+----------------------------------------------------------------++-- | The list of generated files to remove. /N.B./, watch your file+-- extensions.+--+-- TODO: this should be discovered dynamically.+generatedFiles :: [FilePath]+generatedFiles =+    [ "System/Posix/Types/Iovec.hs"+    ]+++-- | Define __HADDOCK__ when building documentation.+main :: IO ()+main = defaultMainWithHooks+    $ simpleUserHooks `modify_haddockHook` \oldHH pkg lbi hooks flags -> do+            +        -- Horrible hack to force re-processing of the @.hsc@ files.+        -- Otherwise the __HADDOCK__ macro doesn't end up being defined+        -- because Cabal will reuse the processed file from before.+        forM_ generatedFiles $ \file ->+            removeFile (buildDir lbi </> file) `catch` \_ -> return ()+        +        -- Call the old haddockHook with a modified LocalBuildInfo+        (\lbi' -> oldHH pkg lbi' hooks flags)+            $ lbi `modify_withPrograms` \oldWP ->+                userSpecifyArgs "hsc2hs" ["-D__HADDOCK__"] oldWP+++modify_haddockHook  hooks f = hooks { haddockHook  = f (haddockHook  hooks) }+modify_withPrograms lbi   f = lbi   { withPrograms = f (withPrograms lbi)   }++----------------------------------------------------------------+----------------------------------------------------------- fin.
+ src/System/Posix/IO/ByteString.hsc view
@@ -0,0 +1,484 @@+{-+/N.B./, There's a bug when trying to use Cabal-style MIN_VERSION_foo(1,2,3)+macros in combination with hsc2hs. We don't need full hsc2hs support+in this file, but if we use CPP instead we get a strange error on+OSX 10.5.8 about "architecture not supported" (even though the+headers work fine with hsc2hs). It turns out that we don't /need/+to combine Cabal-style macros and hsc2hs\/cpp since we can remove+our dependency on the @unix@ package. But this issue is worth making+a note of.+-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# OPTIONS_GHC -Wall -fwarn-tabs #-}+----------------------------------------------------------------+--                                                    2011.03.17+-- |+-- Module      :  System.Posix.IO.ByteString+-- Copyright   :  Copyright (c) 2010--2011 wren ng thornton+-- License     :  BSD+-- Maintainer  :  wren@community.haskell.org+-- Stability   :  experimental+-- Portability :  non-portable (POSIX.1, XPG4.2; hsc2hs, FFI)+--+-- Provides a strict-'BS.ByteString' file-descriptor based I\/O+-- API, designed loosely after the @String@ file-descriptor based+-- I\/O API in "System.Posix.IO". The functions here wrap standard+-- C implementations of the functions specified by the ISO\/IEC+-- 9945-1:1990 (``POSIX.1'') and X\/Open Portability Guide Issue+-- 4, Version 2 (``XPG4.2'') specifications.+----------------------------------------------------------------+module System.Posix.IO.ByteString+    (+    -- * I\/O with file descriptors+    -- ** Reading+    -- *** The POSIX.1 @read(2)@ syscall+      fdRead+    , fdReadBuf+    , fdReads+    -- *** The XPG4.2 @readv(2)@ syscall+    -- , fdReadv+    , fdReadvBuf+    -- *** The XPG4.2 @pread(2)@ syscall+    , fdPread+    , fdPreadBuf+    , fdPreads+    +    -- ** Writing+    -- *** The POSIX.1 @write(2)@ syscall+    , fdWrite+    , fdWriteBuf+    , fdWrites+    -- *** The XPG4.2 @writev(2)@ syscall+    , fdWritev+    , fdWritevBuf+    -- *** The XPG4.2 @pwrite(2)@ syscall+    , fdPwrite+    , fdPwriteBuf+    ) where++import           Data.Word                (Word8)+import qualified Data.ByteString          as BS+import qualified Data.ByteString.Internal as BSI+import qualified Data.ByteString.Unsafe   as BSU++import qualified System.IO.Error          as IOE+{-+-- /N.B./, hsc2hs doesn't like this...+#if (MIN_VERSION_unix(2,4,0))+import           System.Posix.IO          (fdReadBuf, fdWriteBuf)+#endif+-}+import           System.Posix.Types.Iovec+import           System.Posix.Types       (Fd, ByteCount, FileOffset+                                          , CSsize, COff)+import           Foreign.C.Types          (CInt, CSize, CChar)+import qualified Foreign.C.Error          as FFI (throwErrnoIfMinus1Retry)+import           Foreign.Ptr              (Ptr)+import qualified Foreign.Ptr              as FFI (castPtr, plusPtr)+import qualified Foreign.Marshal.Array    as FMA (withArrayLen)++-- iovec, writev, and readv are in <sys/uio.h>, but we must include+-- <sys/types.h> and <unistd.h> for legacy reasons.+#include <sys/types.h>+#include <sys/uio.h>+#include <unistd.h>++----------------------------------------------------------------+-- | Throw an 'IOE.IOError' for EOF.+ioErrorEOF :: String -> IO a+ioErrorEOF fun =+    IOE.ioError+        (IOE.ioeSetErrorString+            (IOE.mkIOError IOE.eofErrorType fun Nothing Nothing)+            "EOF")+++----------------------------------------------------------------+-- /N.B./, hsc2hs doesn't like this...+-- #if ! (MIN_VERSION_unix(2,4,0))+-- This version was copied from unix-2.4.2.0+-- | Read data from an 'Fd' into memory. This is exactly equivalent+-- to the POSIX.1 @read(2)@ system call.+--+-- TODO: better documentation.+fdReadBuf+    :: Fd+    -> Ptr Word8    -- ^ Memory in which to put the data.+    -> ByteCount    -- ^ How many bytes to try to read.+    -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).+fdReadBuf _  _   0      = return 0+fdReadBuf fd buf nbytes = +    fmap fromIntegral+        $ FFI.throwErrnoIfMinus1Retry+            "System.Posix.IO.ByteString.fdReadBuf"+            $ c_safe_read+                (fromIntegral fd)+                (FFI.castPtr  buf)+                (fromIntegral nbytes)++foreign import ccall safe "read"+    -- ssize_t read(int fildes, void *buf, size_t nbyte);+    c_safe_read :: CInt -> Ptr CChar -> CSize -> IO CSsize+-- #endif+++----------------------------------------------------------------+-- | Read data from an 'Fd' and convert it to a 'BS.ByteString'.+-- Throws an exception if this is an invalid descriptor, or EOF has+-- been reached.+--+-- This is essentially equivalent to the POSIX.1 @read(2)@ system+-- call; the differences are that we allocate a byte buffer for the+-- @ByteString@ (and then pass its underlying @Ptr Word8@ and+-- @ByteCount@ components to 'fdReadBuf'), and that we detect EOF+-- and throw an 'IOE.IOError'.+fdRead+    :: Fd+    -> ByteCount        -- ^ How many bytes to try to read.+    -> IO BS.ByteString -- ^ The bytes read.+fdRead _  0 = return BS.empty+fdRead fd n =+    BSI.createAndTrim (fromIntegral n) $ \buf -> do+        rc <- fdReadBuf fd buf n+        if 0 == rc+            then ioErrorEOF "System.Posix.IO.ByteString.fdRead"+            else return (fromIntegral rc)+++----------------------------------------------------------------+-- | Read data from an 'Fd' and convert it to a 'BS.ByteString'.+-- Throws an exception if this is an invalid descriptor, or EOF has+-- been reached.+--+-- This version takes a kind of stateful predicate for whether and+-- how long to keep retrying. Assume the function is called as+-- @fdReads f z0 fd n0@. We will attempt to read @n0@ bytes from+-- @fd@. If we fall short, then we will call @f len z@ where @len@+-- is the total number of bytes read so far and @z@ is the current+-- state (initially @z0@). If it returns @Nothing@ then we will+-- give up and return the current buffer; otherwise we will retry+-- with the new state, continuing from where we left off.+--+-- For example, to define a function that tries up to @n@ times,+-- we can use:+--+-- > fdReadUptoNTimes :: Int -> Fd -> ByteCount -> IO ByteString+-- > fdReadUptoNTimes n0+-- >     | n0 <= 0   = \_ _ -> return empty+-- >     | otherwise = fdReads retry n0+-- >     where+-- >     retry _ 0 = Nothing+-- >     retry _ n = Just $! n-1+--+-- The benefit of doing this instead of the naive approach of calling+-- 'fdRead' repeatedly is that we only need to allocate one byte+-- buffer, and trim it once at the end--- whereas the naive approach+-- would allocate a buffer, trim it to the number of bytes read,+-- and then concatenate with the previous one (another allocation,+-- plus copying everything over) for each time around the loop.+fdReads+    :: (ByteCount -> a -> Maybe a) -- ^ A stateful predicate for retrying.+    -> a                           -- ^ An initial state for the predicate.+    -> Fd+    -> ByteCount                   -- ^ How many bytes to try to read.+    -> IO BS.ByteString            -- ^ The bytes read.+fdReads _ _  _  0  = return BS.empty+fdReads f z0 fd n0 = BSI.createAndTrim (fromIntegral n0) (go z0 0 n0)+    where+    go _ len n buf | len `seq` n `seq` buf `seq` False = undefined+    go z len n buf = do+        rc <- fdReadBuf fd buf n+        let len' = len + rc+        case rc of+          _ | rc == 0 -> ioErrorEOF "System.Posix.IO.ByteString.fdReads"+            | rc == n -> return (fromIntegral len') -- Finished.+            | otherwise ->+                case f len' z of+                Nothing -> return (fromIntegral len') -- Gave up.+                Just z' ->+                    go z' len' (n - rc) (buf `FFI.plusPtr` fromIntegral rc)+++----------------------------------------------------------------+-- | Read data from an 'Fd' and scatter it into memory. This is+-- exactly equivalent to the XPG4.2 @readv(2)@ system call.+--+-- TODO: better documentation.+fdReadvBuf+    :: Fd+    -> Ptr CIovec   -- ^ A C-style array of buffers to fill.+    -> Int          -- ^ How many buffers there are.+    -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).+fdReadvBuf _  _    0   = return 0+fdReadvBuf fd bufs len =+    fmap fromIntegral+        $ FFI.throwErrnoIfMinus1Retry+            "System.Posix.IO.ByteString.fdReadvBuf"+            $ c_safe_readv (fromIntegral fd) bufs (fromIntegral len)++foreign import ccall safe "readv"+    -- ssize_t readv(int fildes, const struct iovec *iov, int iovcnt);+    c_safe_readv :: CInt -> Ptr CIovec -> CInt -> IO CSsize+++-- TODO: What's a reasonable wrapper for fdReadvBuf to make it Haskellish?++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' into memory,+-- without altering the position stored in the @Fd@. This is exactly+-- equivalent to the XPG4.2 @pread(2)@ system call.+--+-- TODO: better documentation.+fdPreadBuf+    :: Fd+    -> Ptr Word8    -- ^ Memory in which to put the data.+    -> ByteCount    -- ^ How many bytes to try to read.+    -> FileOffset   -- ^ Where to read the data from.+    -> IO ByteCount -- ^ How many bytes were actually read (zero for EOF).+fdPreadBuf _  _   0      _      = return 0+fdPreadBuf fd buf nbytes offset =+    fmap fromIntegral+        $ FFI.throwErrnoIfMinus1Retry+            "System.Posix.IO.ByteString.fdPreadBuf"+            $ c_safe_pread+                (fromIntegral fd)+                (FFI.castPtr  buf)+                (fromIntegral nbytes)+                (fromIntegral offset)++foreign import ccall safe "pread"+    -- ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);+    c_safe_pread :: CInt -> Ptr Word8 -> CSize -> COff -> IO CSsize+++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' and convert+-- it to a 'BS.ByteString', without altering the position stored+-- in the @Fd@. Throws an exception if this is an invalid descriptor,+-- or EOF has been reached.+--+-- This is essentially equivalent to the XPG4.2 @pread(2)@ system+-- call; the differences are that we allocate a byte buffer for the+-- @ByteString@ (and then pass its underlying @Ptr Word8@ and+-- @ByteCount@ components to 'fdPreadBuf'), and that we detect EOF+-- and throw an 'IOE.IOError'.+fdPread+    :: Fd+    -> ByteCount        -- ^ How many bytes to try to read.+    -> FileOffset       -- ^ Where to read the data from.+    -> IO BS.ByteString -- ^ The bytes read.+fdPread _  0 _      = return BS.empty+fdPread fd n offset =+    BSI.createAndTrim (fromIntegral n) $ \buf -> do+        rc <- fdPreadBuf fd buf n offset+        if 0 == rc+            then ioErrorEOF "System.Posix.IO.ByteString.fdPread"+            else return (fromIntegral rc)++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' and convert+-- it to a 'BS.ByteString', without altering the position stored+-- in the @Fd@. Throws an exception if this is an invalid descriptor,+-- or EOF has been reached. This is a @pread(2)@ based version of+-- 'fdReads'; see that function for more details.+fdPreads+    :: (ByteCount -> a -> Maybe a) -- ^ A stateful predicate for retrying.+    -> a                           -- ^ An initial state for the predicate.+    -> Fd+    -> ByteCount                   -- ^ How many bytes to try to read.+    -> FileOffset                  -- ^ Where to read the data from.+    -> IO BS.ByteString            -- ^ The bytes read.+fdPreads _ _  _  0  _      = return BS.empty+fdPreads f z0 fd n0 offset = BSI.createAndTrim (fromIntegral n0) (go z0 0 n0)+    where+    go _ len n buf | len `seq` n `seq` buf `seq` False = undefined+    go z len n buf = do+        rc <- fdPreadBuf fd buf n (offset + fromIntegral len)+        let len' = len + rc+        case rc of+          _ | rc == 0 -> ioErrorEOF "System.Posix.IO.ByteString.fdPreads"+            | rc == n -> return (fromIntegral len') -- Finished.+            | otherwise ->+                case f len' z of+                Nothing -> return (fromIntegral len') -- Gave up.+                Just z' ->+                    go z' len' (n - rc) (buf `FFI.plusPtr` fromIntegral rc)++----------------------------------------------------------------+----------------------------------------------------------------+-- /N.B./, hsc2hs doesn't like this...+-- #if ! (MIN_VERSION_unix(2,4,0))+-- This version was copied from unix-2.4.2.0+-- | Write data from memory to an 'Fd'. This is exactly equivalent+-- to the POSIX.1 @write(2)@ system call.+--+-- TODO: better documentation.+fdWriteBuf+    :: Fd+    -> Ptr Word8    -- ^ Memory containing the data to write.+    -> ByteCount    -- ^ How many bytes to try to write.+    -> IO ByteCount -- ^ How many bytes were actually written.+fdWriteBuf fd buf nbytes =+    fmap fromIntegral+        $ FFI.throwErrnoIfMinus1Retry+            "System.Posix.IO.ByteString.fdWriteBuf"+            $ c_safe_write+                (fromIntegral fd)+                (FFI.castPtr  buf)+                (fromIntegral nbytes)++foreign import ccall safe "write" +    -- ssize_t write(int fildes, const void *buf, size_t nbyte);+    c_safe_write :: CInt -> Ptr CChar -> CSize -> IO CSsize+-- #endif+++----------------------------------------------------------------+-- | Write a 'BS.ByteString' to an 'Fd'. The return value is the+-- total number of bytes actually written. This is exactly equivalent+-- to the POSIX.1 @write(2)@ system call; we just convert the+-- @ByteString@ into its underlying @Ptr Word8@ and @ByteCount@+-- components for passing to 'fdWriteBuf'.+fdWrite+    :: Fd+    -> BS.ByteString -- ^ The string to write.+    -> IO ByteCount  -- ^ How many bytes were actually written.+fdWrite fd s =+    -- N.B., BSU.unsafeUseAsCStringLen does zero copying. Use+    -- BS.useAsCStringLen if there's any chance fdWriteBuf might+    -- alter the buffer.+    BSU.unsafeUseAsCStringLen s $ \(buf,len) -> do+        fdWriteBuf fd (FFI.castPtr buf) (fromIntegral len)+++----------------------------------------------------------------+-- | Write a sequence of 'BS.ByteString's to an 'Fd'. The return+-- value is a triple of: the total number of bytes written, the+-- number of bytes written from the first of the remaining strings,+-- and the remaining (unwritten) strings. We return this triple+-- instead of a pair adjusting the head of the remaining strings+-- (i.e., removing the bytes already written) in case there is some+-- semantic significance to the way the input is split into chunks.+--+-- This version consumes the list lazily and will call the @write(2)@+-- system call once for each @ByteString@. This laziness allows the+-- early parts of the list to be garbage collected and prevents+-- needing to hold the whole list of @ByteString@s in memory at+-- once. Compare against 'fdWritev'.+fdWrites+    :: Fd+    -> [BS.ByteString]+        -- ^ The strings to write.+    -> IO (ByteCount, ByteCount, [BS.ByteString])+        -- ^ The total number of bytes written, the number of bytes+        -- written from the first of the remaining strings, the+        -- remaining (unwritten) strings.+fdWrites fd = go 0+    where+    -- We want to do a left fold in order to avoid stack overflows,+    -- but we need to have an early exit for incomplete writes+    -- (which normally requires a right fold). Hence this recursion.+    go acc []         = return (acc, 0, [])+    go acc ccs@(c:cs) = do+        rc <- fdWrite fd c+        let acc' = acc+rc in acc' `seq` do+        if rc == fromIntegral (BS.length c)+            then go acc' cs+            else return (acc', rc, ccs)+++----------------------------------------------------------------+-- | Write data from memory to an 'Fd'. This is exactly equivalent+-- to the XPG4.2 @writev(2)@ system call.+--+-- TODO: better documentation.+fdWritevBuf+    :: Fd+    -> Ptr CIovec   -- ^ A C-style array of buffers to write.+    -> Int          -- ^ How many buffers there are.+    -> IO ByteCount -- ^ How many bytes were actually written.+fdWritevBuf _  _    0   = return 0+fdWritevBuf fd bufs len =+    fmap fromIntegral+        $ FFI.throwErrnoIfMinus1Retry+            "System.Posix.IO.ByteString.fdWritevBuf"+            $ c_safe_writev (fromIntegral fd) bufs (fromIntegral len)++foreign import ccall safe "writev"+    -- ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);+    c_safe_writev :: CInt -> Ptr CIovec -> CInt -> IO CSsize+++----------------------------------------------------------------+-- | Write a sequence of 'BS.ByteString's to an 'Fd'. The return+-- value is the total number of bytes written. Unfortunately the+-- @writev(2)@ system call does not provide enough information to+-- return the triple that 'fdWrites' does.+--+-- This version will force the spine of the list, convert each+-- @ByteString@ into an @iovec@, and then call the @writev(2)@+-- system call. This means we only make one system call, which+-- reduces the overhead of performing context switches. But it also+-- means that we must store the whole list of @ByteString@s in+-- memory at once, and that we must perform some allocation and+-- conversion. Compare against 'fdWrites'.+fdWritev+    :: Fd+    -> [BS.ByteString] -- ^ The strings to write.+    -> IO ByteCount    -- ^ How many bytes were actually written.+fdWritev fd cs = do+    rc <- FMA.withArrayLen (map unsafeByteString2CIovec cs) $ \len iovs ->+        fdWritevBuf fd iovs (fromIntegral len)+    -- BUG: is this enough to actually hold onto them?+    mapM_ touchByteString cs+    return rc+++----------------------------------------------------------------+-- | Write data from memory to a specified position in the 'Fd',+-- but without altering the position stored in the @Fd@. This is+-- exactly equivalent to the XPG4.2 @pwrite(2)@ system call.+--+-- TODO: better documentation.+fdPwriteBuf+    :: Fd+    -> Ptr Word8    -- ^ Memory containing the data to write.+    -> ByteCount    -- ^ How many bytes to try to write.+    -> FileOffset   -- ^ Where to write the data to.+    -> IO ByteCount -- ^ How many bytes were actually written.+fdPwriteBuf _  _   0      _      = return 0+fdPwriteBuf fd buf nbytes offset =+    fmap fromIntegral+        $ FFI.throwErrnoIfMinus1Retry+            "System.Posix.IO.ByteString.fdPwriteBuf"+            $ c_safe_pwrite+                (fromIntegral fd)+                (FFI.castPtr  buf)+                (fromIntegral nbytes)+                (fromIntegral offset)++foreign import ccall safe "pwrite"+    -- ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset);+    c_safe_pwrite :: CInt -> Ptr Word8 -> CSize -> COff -> IO CSsize+++----------------------------------------------------------------+-- | Write data from memory to a specified position in the 'Fd',+-- but without altering the position stored in the @Fd@. This is+-- exactly equivalent to the XPG4.2 @pwrite(2)@ system call; we+-- just convert the @ByteString@ into its underlying @Ptr Word8@+-- and @ByteCount@ components for passing to 'fdPwriteBuf'.+fdPwrite+    :: Fd+    -> BS.ByteString -- ^ The string to write.+    -> FileOffset    -- ^ Where to write the data to.+    -> IO ByteCount  -- ^ How many bytes were actually written.+fdPwrite fd s offset =+    -- N.B., BSU.unsafeUseAsCStringLen does zero copying. Use+    -- BS.useAsCStringLen if there's any chance fdPwriteBuf might+    -- alter the buffer.+    BSU.unsafeUseAsCStringLen s $ \(buf,len) -> do+        fdPwriteBuf fd (FFI.castPtr buf) (fromIntegral len) offset++----------------------------------------------------------------+----------------------------------------------------------- fin.
+ src/System/Posix/IO/ByteString/Lazy.hs view
@@ -0,0 +1,133 @@+{-# OPTIONS_GHC -Wall -fwarn-tabs #-}+----------------------------------------------------------------+--                                                    2011.03.17+-- |+-- Module      :  System.Posix.IO.ByteString.Lazy+-- Copyright   :  Copyright (c) 2010--2011 wren ng thornton+-- License     :  BSD+-- Maintainer  :  wren@community.haskell.org+-- Stability   :  experimental+-- Portability :  non-portable (requires POSIX.1, XPG4.2)+--+-- Provides a lazy-'BL.ByteString' file-descriptor based I\/O+-- API, designed loosely after the @String@ file-descriptor based+-- I\/O API in "System.Posix.IO". The functions here wrap standard+-- C implementations of the functions specified by the ISO\/IEC+-- 9945-1:1990 (``POSIX.1'') and X\/Open Portability Guide Issue+-- 4, Version 2 (``XPG4.2'') specifications.+--+-- These functions are provided mainly as a convenience to avoid+-- boilerplate code converting between lazy 'BL.ByteString' and+-- strict @['BS.ByteString']@. It may be depricated in the future.+----------------------------------------------------------------+module System.Posix.IO.ByteString.Lazy+    (+    -- * I\/O with file descriptors+    -- ** Reading+      fdRead+    , fdPread+    -- ** Writing+    , fdWrites+    , fdWritev+    ) where++import qualified Data.ByteString               as BS+import qualified Data.ByteString.Unsafe        as BSU+import qualified System.Posix.IO.ByteString    as PosixBS+import qualified Data.ByteString.Lazy          as BL+import qualified Data.ByteString.Lazy.Internal as BLI+import           System.Posix.Types            (Fd, ByteCount, FileOffset)++----------------------------------------------------------------+-- | Read data from an 'Fd' and convert it to a 'BL.ByteString'.+-- Throws an exception if this is an invalid descriptor, or EOF has+-- been reached. This is a thin wrapper around 'PosixBS.fdRead'.+fdRead+    :: Fd+    -> ByteCount        -- ^ How many bytes to try to read.+    -> IO BL.ByteString -- ^ The bytes read.+fdRead _  0 = return BL.empty+fdRead fd n = do+    s <- PosixBS.fdRead fd n+    return (BLI.chunk s BL.empty)++----------------------------------------------------------------+-- | Read data from a specified position in the 'Fd' and convert+-- it to a 'BS.ByteString', without altering the position stored+-- in the @Fd@. Throws an exception if this is an invalid descriptor,+-- or EOF has been reached. This is a thin wrapper around+-- 'PosixBS.fdPread'.+fdPread+    :: Fd+    -> ByteCount        -- ^ How many bytes to try to read.+    -> FileOffset       -- ^ Where to read the data from.+    -> IO BL.ByteString -- ^ The bytes read.+fdPread _  0 _      = return BL.empty+fdPread fd n offset = do+    s <- PosixBS.fdPread fd n offset+    return (BLI.chunk s BL.empty)+++----------------------------------------------------------------+-- | Write a 'BL.ByteString' to an 'Fd'. This function makes one+-- @write(2)@ system call per chunk, as per 'PosixBS.fdWrites'.+fdWrites+    :: Fd+    -> BL.ByteString+        -- ^ The string to write.+    -> IO (ByteCount, BL.ByteString)+        -- ^ How many bytes were actually written, and the remaining+        -- (unwritten) string.+fdWrites fd = go 0+    where+    -- We want to do a left fold in order to avoid stack overflows,+    -- but we need to have an early exit for incomplete writes+    -- (which normally requires a right fold). Hence this recursion.+    go acc BLI.Empty        = return (acc, BL.empty)+    go acc (BLI.Chunk c cs) = do+        rc <- PosixBS.fdWrite fd c+        let acc'  = acc+rc          in acc'  `seq` do+        let rcInt = fromIntegral rc in rcInt `seq` do+        if rcInt == BS.length c+            then go acc' cs+            else return (acc', BLI.Chunk (BSU.unsafeDrop rcInt c) cs)+{-+Using 'BSU.unsafeDrop' above is safe, assuming that+'System.Posix.IO.fdWriteBuf' never returns (rc < 0 || rc > BS.length c).+If we are paranoid about that then we should do the following instead:++    go acc ccs =+        case ccs of+        BLI.Empty      -> return (acc, ccs)+        BLI.Chunk c cs -> do+            rc <- PosixBS.fdWrite fd c+            let acc'  = acc+rc          in acc'  `seq` do+            let rcInt = fromIntegral rc in rcInt `seq` do+            case BS.length c of+                len | rcInt == len -> go acc' cs+                    | rcInt >  len -> error _impossibleByteCount+                    | rcInt <  0   -> error _negtiveByteCount+                    | rcInt == 0   -> return (acc', ccs) -- trivial optimizing+                    | otherwise    -> return (acc', BLI.Chunk (BSU.unsafeDrop rcInt c) cs)++_negtiveByteCount =+    "System.Posix.IO.fdWriteBuf: returned a negative byte count"+_impossibleByteCount =+    "System.Posix.IO.fdWriteBuf: returned a byte count greater than the length it was given"+-}+++----------------------------------------------------------------+-- | Write a 'BL.ByteString' to an 'Fd'. This function makes a+-- single @writev(2)@ system call, as per 'PosixBS.fdWritev'.+fdWritev+    :: Fd+    -> BL.ByteString -- ^ The string to write.+    -> IO ByteCount  -- ^ How many bytes were actually written.+fdWritev fd s = PosixBS.fdWritev fd (BL.toChunks s)+{-# INLINE fdWritev #-}+-- Hopefully the intermediate list can be fused away...+++----------------------------------------------------------------+----------------------------------------------------------- fin.
+ src/System/Posix/Types/Iovec.hsc view
@@ -0,0 +1,148 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# OPTIONS_GHC -Wall -fwarn-tabs #-}+----------------------------------------------------------------+--                                                    2011.03.17+-- |+-- Module      :  System.Posix.Types.Iovec+-- Copyright   :  Copyright (c) 2010--2011 wren ng thornton+-- License     :  BSD+-- Maintainer  :  wren@community.haskell.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           Foreign.C.Types          (CSize)+import           Foreign.Storable         (Storable(..))++-- N.B., we need a Custom cabal build-type in order for this to+-- work.+#ifdef __HADDOCK__+import Foreign.C.String (CStringLen)+#endif++-- iovec, writev, and readv are in <sys/uio.h>, but we must include+-- <sys/types.h> and <unistd.h> for legacy reasons.+#include <sys/types.h>+#include <sys/uio.h>+#include <unistd.h>++----------------------------------------------------------------++-- | Haskell type representing the C @struct iovec@ type. This is+-- exactly like @'CStringLen'@ except there's actually struct+-- definition on the C side.+data CIovec = CIovec+    { iov_base :: {-# UNPACK #-} !(Ptr Word8) -- char* or void*+    , iov_len  :: {-# UNPACK #-} !CSize       -- size_t+    }++#let alignment t = \+    "%lu", (unsigned long) offsetof(struct {char x__; t (y__); }, y__)++instance Storable CIovec where+    alignment _ = #{alignment struct iovec}+    +    sizeOf _    = #{size struct iovec}+    +    peek ptr = do+        base <- #{peek struct iovec, iov_base} ptr+        len  <- #{peek struct iovec, iov_len}  ptr+        return (CIovec base len)+    +    poke ptr (CIovec base len) = do+        #{poke struct iovec, iov_base} ptr base+        #{poke struct iovec, iov_len}  ptr len+++-- | /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 (BSI.PS fptr offset len) =+    CIovec+        (FFP.unsafeForeignPtrToPtr fptr `FFI.plusPtr` offset)+        (fromIntegral len)+{-# INLINE unsafeByteString2CIovec #-}+++-- | Keep the @ByteString@ alive. See 'unsafeByteString2CIovec'.+touchByteString :: BS.ByteString -> IO ()+touchByteString (BSI.PS fptr _ _) = FFP.touchForeignPtr 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 (BSI.PS fptr offset len) io =+    FFP.withForeignPtr fptr $ \ptr ->+        io (CIovec (ptr `FFI.plusPtr` offset) (fromIntegral 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 s@(BSI.PS _ _ len) io =+    BS.useAsCString s $ \cstr ->+        io (CIovec (FFI.castPtr cstr) (fromIntegral 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.
+ unix-bytestring.cabal view
@@ -0,0 +1,56 @@+----------------------------------------------------------------+-- wren ng thornton <wren@community.haskell.org>    ~ 2011.03.17+----------------------------------------------------------------++Name:           unix-bytestring+Version:        0.3.2+-- Source-Repository requires version 1.6+Cabal-Version:  >= 1.6+-- We need a custom build in order to define __HADDOCK__+Build-Type:     Custom+Stability:      experimental+Copyright:      Copyright (c) 2010--2011 wren ng thornton+License:        BSD3+License-File:   LICENSE+Author:         wren ng thornton+Maintainer:     wren@community.haskell.org+Homepage:       http://code.haskell.org/~wren/+Category:       Data+Synopsis:       Unix/Posix-specific functions for ByteStrings.+Description:    Unix\/Posix-specific functions for ByteStrings.+    .+    Provides @ByteString@ file-descriptor based I\/O API, designed+    loosely after the @String@ file-descriptor based I\/O API in+    "System.Posix.IO". The functions here wrap standard C implementations+    of the functions specified by the ISO\/IEC 9945-1:1990 (``POSIX.1'')+    and X\/Open Portability Guide Issue 4, Version 2 (``XPG4.2'')+    specifications.+    .+    Note that this package doesn't require the @unix@ package as a+    dependency. But you'll need it in order to get your hands on+    an @Fd@, so we're not offering a complete replacement.++Tested-With:    GHC == 6.12.1, GHC == 6.12.3+Source-Repository head+    Type:     darcs+    Location: http://community.haskell.org/~wren/unix-bytestring++----------------------------------------------------------------+Library+    Hs-Source-Dirs:  src+    Exposed-Modules: System.Posix.IO.ByteString+                   , System.Posix.IO.ByteString.Lazy+                   , System.Posix.Types.Iovec++    -- We require base>=4.1 for Foreign.C.Error.throwErrnoIfMinus1Retry.+    --+    -- We would require unix>=2.4 for System.Posix.IO.fdReadBuf/fdWriteBuf+    -- (and unix-2.4.0.0 requires base>=4.1 too), except we define+    -- them on our own for better backwards compatibility.+    --+    -- Not sure what the real minbound is on bytestring...+    Build-Depends: base       >= 4.1 && < 5+                 , bytestring >= 0.9.1.5++----------------------------------------------------------------+----------------------------------------------------------- fin.