{-# LANGUAGE BangPatterns, OverloadedStrings #-}
module Network.Wai.Handler.Warp.Buffer (
bufferSize
, allocateBuffer
, freeBuffer
, mallocBS
, newBufferPool
, withBufferPool
, toBuilderBuffer
, copy
, bufferIO
) where
import qualified Data.ByteString as BS
import Data.ByteString.Internal (ByteString(..), memcpy)
import Data.ByteString.Unsafe (unsafeTake, unsafeDrop)
import Data.IORef (newIORef, readIORef, writeIORef)
import qualified Data.Streaming.ByteString.Builder.Buffer as B (Buffer (..))
import Foreign.ForeignPtr
import Foreign.Marshal.Alloc (mallocBytes, free, finalizerFree)
import Foreign.Ptr (castPtr, plusPtr)
import Network.Wai.Handler.Warp.Types
bufferSize :: BufSize
bufferSize = 16384
allocateBuffer :: Int -> IO Buffer
allocateBuffer = mallocBytes
freeBuffer :: Buffer -> IO ()
freeBuffer = free
largeBufferSize :: Int
largeBufferSize = 16384
minBufferSize :: Int
minBufferSize = 2048
newBufferPool :: IO BufferPool
newBufferPool = newIORef BS.empty
mallocBS :: Int -> IO ByteString
mallocBS size = do
ptr <- allocateBuffer size
fptr <- newForeignPtr finalizerFree ptr
return $! PS fptr 0 size
{-# INLINE mallocBS #-}
usefulBuffer :: ByteString -> Bool
usefulBuffer buffer = BS.length buffer >= minBufferSize
{-# INLINE usefulBuffer #-}
getBuffer :: BufferPool -> IO ByteString
getBuffer pool = do
buffer <- readIORef pool
if usefulBuffer buffer then return buffer else mallocBS largeBufferSize
{-# INLINE getBuffer #-}
putBuffer :: BufferPool -> ByteString -> IO ()
putBuffer pool buffer = writeIORef pool buffer
{-# INLINE putBuffer #-}
withForeignBuffer :: ByteString -> ((Buffer, BufSize) -> IO Int) -> IO Int
withForeignBuffer (PS ps s l) f = withForeignPtr ps $ \p -> f (castPtr p `plusPtr` s, l)
{-# INLINE withForeignBuffer #-}
withBufferPool :: BufferPool -> ((Buffer, BufSize) -> IO Int) -> IO ByteString
withBufferPool pool f = do
buffer <- getBuffer pool
consumed <- withForeignBuffer buffer f
putBuffer pool $! unsafeDrop consumed buffer
return $! unsafeTake consumed buffer
{-# INLINE withBufferPool #-}
toBuilderBuffer :: Buffer -> BufSize -> IO B.Buffer
toBuilderBuffer ptr size = do
fptr <- newForeignPtr_ ptr
return $ B.Buffer fptr ptr ptr (ptr `plusPtr` size)
copy :: Buffer -> ByteString -> IO Buffer
copy !ptr (PS fp o l) = withForeignPtr fp $ \p -> do
memcpy ptr (p `plusPtr` o) (fromIntegral l)
return $! ptr `plusPtr` l
{-# INLINE copy #-}
bufferIO :: Buffer -> Int -> (ByteString -> IO ()) -> IO ()
bufferIO ptr siz io = do
fptr <- newForeignPtr_ ptr
io $ PS fptr 0 siz