{-# LANGUAGE BangPatterns, OverloadedStrings #-}
module Network.HPACK.HeaderBlock.Integer (
    encode
  , encodeInteger
  , decode
  , decodeInteger
  ) where
import Data.Array (Array, listArray)
import Data.Array.Base (unsafeAt)
import Network.ByteOrder
import Imports
powerArray :: Array Int Int
powerArray = listArray (1,8) [1,3,7,15,31,63,127,255]
encodeInteger :: Int -> Int -> IO ByteString
encodeInteger n i = withWriteBuffer 4096 $ \wbuf -> encode wbuf id n i
{-# INLINABLE encode #-}
encode :: WriteBuffer -> (Word8 -> Word8) -> Int -> Int -> IO ()
encode wbuf set n i
  | i < p     = write8 wbuf $ set $ fromIntegral i
  | otherwise = do
        write8 wbuf $ set $ fromIntegral p
        encode' (i - p)
  where
    !p = powerArray `unsafeAt` (n - 1)
    encode' :: Int -> IO ()
    encode' j
      | j < 128   = write8 wbuf $ fromIntegral j
      | otherwise = do
          let !q = j `shiftR` 7
              !r = j .&. 0x7f
          write8 wbuf $ fromIntegral (r + 128)
          encode' q
decodeInteger :: Int -> Word8 -> ByteString -> IO Int
decodeInteger n w bs = withReadBuffer bs $ \rbuf -> decode n w rbuf
{-# INLINABLE decode #-}
decode :: Int -> Word8 -> ReadBuffer -> IO Int
decode n w rbuf
  | i < p     = return i
  | otherwise = decode' 0 i
  where
    !p = powerArray `unsafeAt` (n - 1)
    !i = fromIntegral w
    decode' :: Int -> Int -> IO Int
    decode' m j = do
        !b <- readInt8 rbuf
        let !j' = j + (b .&. 0x7f) * 2 ^ m
            !m' = m + 7
            !cont = b `testBit` 7
        if cont then decode' m' j' else return j'