{-# LANGUAGE BangPatterns, RecordWildCards, CPP #-}
module Network.HPACK.Buffer (
Buffer
, BufferSize
, WorkingBuffer(..)
, newWorkingBuffer
, wind
, readWord8
, writeWord8
, shiftLastN
, returnLength
, toByteString
, copyByteString
, withTemporaryBuffer
, currentOffset
, ReadBuffer
, withReadBuffer
, hasOneByte
, hasMoreBytes
, rewindOneByte
, getByte
, getByte'
, extractByteString
) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative ((<$>))
#endif
import Control.Exception (bracket, throwIO)
import Data.ByteString.Internal (ByteString(..), create, memcpy)
import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef')
import Data.Word (Word8)
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Marshal.Alloc
import Foreign.Ptr (plusPtr, minusPtr)
import Foreign.Storable (peek, poke)
import Network.HPACK.Types (Buffer, BufferSize, BufferOverrun(..))
data WorkingBuffer = WorkingBuffer {
start :: !Buffer
, limit :: !Buffer
, offset :: !(IORef Buffer)
}
newWorkingBuffer :: Buffer -> BufferSize -> IO WorkingBuffer
newWorkingBuffer buf siz = WorkingBuffer buf (buf `plusPtr` siz) <$> newIORef buf
{-# INLINE wind #-}
wind :: WorkingBuffer -> Int -> IO ()
wind WorkingBuffer{..} n = do
ptr <- readIORef offset
let !ptr' = ptr `plusPtr` n
writeIORef offset ptr'
{-# INLINE readWord8 #-}
readWord8 :: WorkingBuffer -> IO Word8
readWord8 WorkingBuffer{..} = readIORef offset >>= peek
{-# INLINE writeWord8 #-}
writeWord8 :: WorkingBuffer -> Word8 -> IO ()
writeWord8 WorkingBuffer{..} w = do
ptr <- readIORef offset
if ptr >= limit then
throwIO BufferOverrun
else do
poke ptr w
let !ptr' = ptr `plusPtr` 1
writeIORef offset ptr'
{-# INLINE shiftLastN #-}
shiftLastN :: WorkingBuffer -> Int -> Int -> IO ()
shiftLastN WorkingBuffer{..} 0 _ = return ()
shiftLastN WorkingBuffer{..} i len = do
ptr <- readIORef offset
let !ptr' = ptr `plusPtr` i
if ptr' >= limit then
throwIO BufferOverrun
else if i < 0 then do
let !src = ptr `plusPtr` negate len
!dst = src `plusPtr` i
shiftLeft dst src len
writeIORef offset ptr'
else do
let !src = ptr `plusPtr` (-1)
!dst = ptr' `plusPtr` (-1)
shiftRight dst src len
writeIORef offset ptr'
where
shiftLeft :: Buffer -> Buffer -> Int -> IO ()
shiftLeft _ _ 0 = return ()
shiftLeft !dst !src n = do
peek src >>= poke dst
shiftLeft (dst `plusPtr` 1) (src `plusPtr` 1) (n - 1)
shiftRight :: Buffer -> Buffer -> Int -> IO ()
shiftRight _ _ 0 = return ()
shiftRight !dst !src n = do
peek src >>= poke dst
shiftRight (dst `plusPtr` (-1)) (src `plusPtr` (-1)) (n - 1)
{-# INLINE copyByteString #-}
copyByteString :: WorkingBuffer -> ByteString -> IO ()
copyByteString WorkingBuffer{..} (PS fptr off len) = withForeignPtr fptr $ \ptr -> do
let src = ptr `plusPtr` off
dst <- readIORef offset
let !dst' = dst `plusPtr` len
if dst' >= limit then
throwIO BufferOverrun
else do
memcpy dst src len
writeIORef offset dst'
toByteString :: WorkingBuffer -> IO ByteString
toByteString WorkingBuffer{..} = do
ptr <- readIORef offset
let !len = ptr `minusPtr` start
create len $ \p -> memcpy p start len
{-# INLINE returnLength #-}
returnLength :: WorkingBuffer -> IO () -> IO Int
returnLength WorkingBuffer{..} body = do
beg <- readIORef offset
body
end <- readIORef offset
return $ end `minusPtr` beg
withTemporaryBuffer :: Int -> (WorkingBuffer -> IO ()) -> IO ByteString
withTemporaryBuffer siz action = bracket (mallocBytes siz) free $ \buf -> do
wbuf <- newWorkingBuffer buf 4096
action wbuf
toByteString wbuf
currentOffset :: WorkingBuffer -> IO Buffer
currentOffset WorkingBuffer{..} = readIORef offset
data ReadBuffer = ReadBuffer {
beg :: !Buffer
, end :: !Buffer
, cur :: !(IORef Buffer)
}
withReadBuffer :: ByteString -> (ReadBuffer -> IO a) -> IO a
withReadBuffer (PS fp off len) action = withForeignPtr fp $ \ptr -> do
let !bg = ptr `plusPtr` off
!ed = bg `plusPtr` len
nsrc <- ReadBuffer bg ed <$> newIORef bg
action nsrc
{-# INLINE hasOneByte #-}
hasOneByte :: ReadBuffer -> IO Bool
hasOneByte ReadBuffer{..} = do
ptr <- readIORef cur
return $! ptr < end
{-# INLINE hasMoreBytes #-}
hasMoreBytes :: ReadBuffer -> Int -> IO Bool
hasMoreBytes ReadBuffer{..} n = do
ptr <- readIORef cur
return $! (end `minusPtr` ptr) >= n
{-# INLINE rewindOneByte #-}
rewindOneByte :: ReadBuffer -> IO ()
rewindOneByte ReadBuffer{..} = modifyIORef' cur (`plusPtr` (-1))
{-# INLINE getByte #-}
getByte :: ReadBuffer -> IO Word8
getByte ReadBuffer{..} = do
ptr <- readIORef cur
w <- peek ptr
writeIORef cur $! ptr `plusPtr` 1
return w
{-# INLINE getByte' #-}
getByte' :: ReadBuffer -> IO Int
getByte' ReadBuffer{..} = do
ptr <- readIORef cur
if ptr < end then do
w <- peek ptr
writeIORef cur $! ptr `plusPtr` 1
let !i = fromIntegral w
return i
else
return (-1)
extractByteString :: ReadBuffer -> Int -> IO ByteString
extractByteString ReadBuffer{..} len = do
src <- readIORef cur
bs <- create len $ \dst -> memcpy dst src len
writeIORef cur $! src `plusPtr` len
return bs