{-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}
module Network.HPACK.Huffman.Encode (
HuffmanEncoding
, encode
, encodeHuffman
) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative ((<$>))
#endif
import Control.Exception (throwIO)
import Control.Monad (when, void)
import Data.Array.Base (unsafeAt)
import Data.Array.IArray (listArray)
import Data.Array.Unboxed (UArray)
import Data.Bits ((.|.), shiftR, shiftL)
import Data.ByteString (ByteString)
import Data.IORef
import Data.Word (Word8, Word64)
import Foreign.Ptr (plusPtr, minusPtr)
import Foreign.Storable (poke)
import Network.HPACK.Buffer
import Network.HPACK.Huffman.Params (idxEos)
import Network.HPACK.Huffman.Table
import Network.HPACK.Types (BufferOverrun(..))
huffmanLength :: UArray Int Int
huffmanLength = listArray (0,idxEos) $ map length huffmanTable
huffmanCode :: UArray Int Word64
huffmanCode = listArray (0,idxEos) huffmanTable'
type HuffmanEncoding = WorkingBuffer -> ByteString -> IO Int
encode :: HuffmanEncoding
encode dst bs = withReadBuffer bs $ enc dst
initialOffset :: Int
initialOffset = 40
shiftForWrite :: Int
shiftForWrite = 32
enc :: WorkingBuffer -> ReadBuffer -> IO Int
enc WorkingBuffer{..} rbuf = do
beg <- readIORef offset
end <- go (beg,0,initialOffset)
writeIORef offset end
let !len = end `minusPtr` beg
return len
where
go (dst,encoded,off) = do
!i <- getByte' rbuf
if i >= 0 then
copy dst (bond i) >>= go
else if off == initialOffset then
return dst
else do
let (encoded1,_) = bond idxEos
write dst encoded1
where
{-# INLINE bond #-}
bond i = (encoded', off')
where
!len = huffmanLength `unsafeAt` i
!code = huffmanCode `unsafeAt` i
!scode = code `shiftL` (off - len)
!encoded' = encoded .|. scode
!off' = off - len
{-# INLINE write #-}
write p w = do
when (p >= limit) $ throwIO BufferOverrun
let !w8 = fromIntegral (w `shiftR` shiftForWrite) :: Word8
poke p w8
let !p' = p `plusPtr` 1
return p'
{-# INLINE copy #-}
copy p (w,o)
| o > shiftForWrite = return (p,w,o)
| otherwise = do
p' <- write p w
let !w' = w `shiftL` 8
!o' = o + 8
copy p' (w',o')
encodeHuffman :: ByteString -> IO ByteString
encodeHuffman bs = withTemporaryBuffer 4096 $ \wbuf ->
void $ encode wbuf bs