{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}

module Network.HPACK.Table.Dynamic (
    DynamicTable (..),
    newDynamicTableForEncoding,
    newDynamicTableForDecoding,
    renewDynamicTable,
    huffmanDecoder,
    printDynamicTable,
    isDynamicTableEmpty,
    isSuitableSize,
    TableSizeAction (..),
    needChangeTableSize,
    setLimitForEncoding,
    resetLimitForEncoding,
    insertEntry,
    toDynamicEntry,
    CodeInfo (..),
    withDynamicTableForEncoding,
    withDynamicTableForDecoding,
    toIndexedEntry,
    fromHIndexToIndex,
    getRevIndex,
) where

import Control.Exception (throwIO)
import Data.Array.Base (unsafeRead, unsafeWrite)
import Data.Array.IO (IOArray, newArray)
import qualified Data.ByteString.Char8 as BS
import Data.IORef

import Imports
import Network.HPACK.Huffman
import Network.HPACK.Table.Entry
import Network.HPACK.Table.RevIndex
import Network.HPACK.Table.Static
import Network.HPACK.Types

----------------------------------------------------------------

-- For decoder
{-# INLINE toIndexedEntry #-}
toIndexedEntry :: DynamicTable -> Index -> IO Entry
toIndexedEntry :: DynamicTable -> Size -> IO Entry
toIndexedEntry DynamicTable
dyntbl Size
idx
    | Size
idx Size -> Size -> Bool
forall a. Ord a => a -> a -> Bool
<= Size
0 = DecodeError -> IO Entry
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO (DecodeError -> IO Entry) -> DecodeError -> IO Entry
forall a b. (a -> b) -> a -> b
$ Size -> DecodeError
IndexOverrun Size
idx
    | Size
idx Size -> Size -> Bool
forall a. Ord a => a -> a -> Bool
<= Size
staticTableSize = Entry -> IO Entry
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Entry -> IO Entry) -> Entry -> IO Entry
forall a b. (a -> b) -> a -> b
$ Size -> Entry
toStaticEntry Size
idx
    | Bool
otherwise = DynamicTable -> Size -> IO Entry
toDynamicEntry DynamicTable
dyntbl Size
idx

-- For encoder
{-# INLINE fromHIndexToIndex #-}
fromHIndexToIndex :: DynamicTable -> HIndex -> IO Index
fromHIndexToIndex :: DynamicTable -> HIndex -> IO Size
fromHIndexToIndex DynamicTable
_ (SIndex Size
idx) = Size -> IO Size
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Size
idx
fromHIndexToIndex DynamicTable{IORef Size
IORef Table
CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
..} (DIndex Size
didx) = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    x <- adj maxN (didx - off)
    return $ x + staticTableSize

----------------------------------------------------------------

type Table = IOArray Index Entry

{-
        offset
        v
   +-+-+-+-+-+-+-+-+
   | | | |z|y|x| | |
   +-+-+-+-+-+-+-+-+
          1 2 3      (numOfEntries = 3)

After insertion:

      offset
      v
   +-+-+-+-+-+-+-+-+
   | | |w|z|y|x| | |
   +-+-+-+-+-+-+-+-+
        1 2 3 4      (numOfEntries = 4)
-}

data CodeInfo = CIE EncodeInfo | CID DecodeInfo
data EncodeInfo
    = EncodeInfo
        RevIndex -- Reverse index
        -- The value informed by SETTINGS_HEADER_TABLE_SIZE.
        -- If 'Nothing', dynamic table size update is not necessary.
        -- Otherwise, dynamic table size update is sent
        -- and this value should be set to 'Nothing'.
        (IORef (Maybe Size))
data DecodeInfo
    = DecodeInfo
        HuffmanDecoder
        (IORef Size) -- The limit size

toEncodeInfo :: CodeInfo -> EncodeInfo
toEncodeInfo :: CodeInfo -> EncodeInfo
toEncodeInfo (CIE EncodeInfo
x) = EncodeInfo
x
toEncodeInfo CodeInfo
_ = [Char] -> EncodeInfo
forall a. HasCallStack => [Char] -> a
error [Char]
"toEncodeInfo"

toDecodeInfo :: CodeInfo -> DecodeInfo
toDecodeInfo :: CodeInfo -> DecodeInfo
toDecodeInfo (CID DecodeInfo
x) = DecodeInfo
x
toDecodeInfo CodeInfo
_ = [Char] -> DecodeInfo
forall a. HasCallStack => [Char] -> a
error [Char]
"toDecodeInfo"

-- | Type for dynamic table.
data DynamicTable = DynamicTable
    { DynamicTable -> CodeInfo
codeInfo :: CodeInfo
    , DynamicTable -> IORef Table
circularTable :: IORef Table
    -- ^ An array
    , DynamicTable -> IORef Size
offset :: IORef Index
    -- ^ Start point
    , DynamicTable -> IORef Size
numOfEntries :: IORef Int
    -- ^ The current number of entries
    , DynamicTable -> IORef Size
maxNumOfEntries :: IORef Int
    -- ^ The size of the array
    , DynamicTable -> IORef Size
dynamicTableSize :: IORef Size
    -- ^ The current dynamic table size (defined in HPACK)
    , DynamicTable -> IORef Size
maxDynamicTableSize :: IORef Size
    -- ^ The max dynamic table size (defined in HPACK)
    }

{-# INLINE adj #-}
adj :: Int -> Int -> IO Int
adj :: Size -> Size -> IO Size
adj Size
maxN Size
x
    | Size
maxN Size -> Size -> Bool
forall a. Eq a => a -> a -> Bool
== Size
0 = DecodeError -> IO Size
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO DecodeError
TooSmallTableSize
    | Bool
otherwise =
        let ret :: Size
ret = (Size
x Size -> Size -> Size
forall a. Num a => a -> a -> a
+ Size
maxN) Size -> Size -> Size
forall a. Integral a => a -> a -> a
`mod` Size
maxN
         in Size -> IO Size
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Size
ret

huffmanDecoder :: DynamicTable -> HuffmanDecoder
huffmanDecoder :: DynamicTable -> HuffmanDecoder
huffmanDecoder DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = HuffmanDecoder
dec
  where
    DecodeInfo HuffmanDecoder
dec IORef Size
_ = CodeInfo -> DecodeInfo
toDecodeInfo CodeInfo
codeInfo

----------------------------------------------------------------

-- | Printing 'DynamicTable'.
printDynamicTable :: DynamicTable -> IO ()
printDynamicTable :: DynamicTable -> IO ()
printDynamicTable DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    n <- readIORef numOfEntries
    let beg = Size
off Size -> Size -> Size
forall a. Num a => a -> a -> a
+ Size
1
        end = Size
off Size -> Size -> Size
forall a. Num a => a -> a -> a
+ Size
n
    tbl <- readIORef circularTable
    es <- mapM (adj maxN >=> unsafeRead tbl) [beg .. end]
    let ts = [Size] -> [Entry] -> [(Size, Entry)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Size
1 ..] [Entry]
es
    mapM_ printEntry ts
    dsize <- readIORef dynamicTableSize
    maxdsize <- readIORef maxDynamicTableSize
    putStrLn $ "      Table size: " ++ show dsize ++ "/" ++ show maxdsize

printEntry :: (Index, Entry) -> IO ()
printEntry :: (Size, Entry) -> IO ()
printEntry (Size
i, Entry
e) = do
    [Char] -> IO ()
putStr [Char]
"[ "
    [Char] -> IO ()
putStr ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ Size -> [Char]
forall a. Show a => a -> [Char]
show Size
i
    [Char] -> IO ()
putStr [Char]
"] (s = "
    [Char] -> IO ()
putStr ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ Size -> [Char]
forall a. Show a => a -> [Char]
show (Size -> [Char]) -> Size -> [Char]
forall a b. (a -> b) -> a -> b
$ Entry -> Size
entrySize Entry
e
    [Char] -> IO ()
putStr [Char]
") "
    ByteString -> IO ()
BS.putStr (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ CI ByteString -> ByteString
forall s. CI s -> s
original (CI ByteString -> ByteString) -> CI ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Entry -> CI ByteString
entryHeaderName Entry
e
    [Char] -> IO ()
putStr [Char]
": "
    ByteString -> IO ()
BS.putStrLn (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ Entry -> ByteString
entryFieldValue Entry
e

----------------------------------------------------------------

isDynamicTableEmpty :: DynamicTable -> IO Bool
isDynamicTableEmpty :: DynamicTable -> IO Bool
isDynamicTableEmpty DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    n <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
numOfEntries
    return $ n == 0

isSuitableSize :: Size -> DynamicTable -> IO Bool
isSuitableSize :: Size -> DynamicTable -> IO Bool
isSuitableSize Size
siz DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    let DecodeInfo HuffmanDecoder
_ IORef Size
limref = CodeInfo -> DecodeInfo
toDecodeInfo CodeInfo
codeInfo
    lim <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
limref
    return $ siz <= lim

data TableSizeAction = Keep | Change Size | Ignore Size

needChangeTableSize :: DynamicTable -> IO TableSizeAction
needChangeTableSize :: DynamicTable -> IO TableSizeAction
needChangeTableSize DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    let EncodeInfo RevIndex
_ IORef (Maybe Size)
limref = CodeInfo -> EncodeInfo
toEncodeInfo CodeInfo
codeInfo
    mlim <- IORef (Maybe Size) -> IO (Maybe Size)
forall a. IORef a -> IO a
readIORef IORef (Maybe Size)
limref
    maxsiz <- readIORef maxDynamicTableSize
    return $ case mlim of
        Maybe Size
Nothing -> TableSizeAction
Keep
        Just Size
lim
            | Size
lim Size -> Size -> Bool
forall a. Ord a => a -> a -> Bool
< Size
maxsiz -> Size -> TableSizeAction
Change Size
lim
            | Bool
otherwise -> Size -> TableSizeAction
Ignore Size
maxsiz

-- | When SETTINGS_HEADER_TABLE_SIZE is received from a peer,
--   its value should be set by this function.
setLimitForEncoding :: Size -> DynamicTable -> IO ()
setLimitForEncoding :: Size -> DynamicTable -> IO ()
setLimitForEncoding Size
siz DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    let EncodeInfo RevIndex
_ IORef (Maybe Size)
limref = CodeInfo -> EncodeInfo
toEncodeInfo CodeInfo
codeInfo
    IORef (Maybe Size) -> Maybe Size -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (Maybe Size)
limref (Maybe Size -> IO ()) -> Maybe Size -> IO ()
forall a b. (a -> b) -> a -> b
$ Size -> Maybe Size
forall a. a -> Maybe a
Just Size
siz

resetLimitForEncoding :: DynamicTable -> IO ()
resetLimitForEncoding :: DynamicTable -> IO ()
resetLimitForEncoding DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    let EncodeInfo RevIndex
_ IORef (Maybe Size)
limref = CodeInfo -> EncodeInfo
toEncodeInfo CodeInfo
codeInfo
    IORef (Maybe Size) -> Maybe Size -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef (Maybe Size)
limref Maybe Size
forall a. Maybe a
Nothing

----------------------------------------------------------------

-- | Creating 'DynamicTable' for encoding.
newDynamicTableForEncoding
    :: Size
    -- ^ The dynamic table size
    -> IO DynamicTable
newDynamicTableForEncoding :: Size -> IO DynamicTable
newDynamicTableForEncoding Size
maxsiz = do
    rev <- IO RevIndex
newRevIndex
    lim <- newIORef Nothing
    let info = EncodeInfo -> CodeInfo
CIE (EncodeInfo -> CodeInfo) -> EncodeInfo -> CodeInfo
forall a b. (a -> b) -> a -> b
$ RevIndex -> IORef (Maybe Size) -> EncodeInfo
EncodeInfo RevIndex
rev IORef (Maybe Size)
lim
    newDynamicTable maxsiz info

-- | Creating 'DynamicTable' for decoding.
newDynamicTableForDecoding
    :: Size
    -- ^ The dynamic table size
    -> Size
    -- ^ The size of temporary buffer for Huffman decoding
    -> IO DynamicTable
newDynamicTableForDecoding :: Size -> Size -> IO DynamicTable
newDynamicTableForDecoding Size
maxsiz Size
huftmpsiz = do
    lim <- Size -> IO (IORef Size)
forall a. a -> IO (IORef a)
newIORef Size
maxsiz
    buf <- mallocPlainForeignPtrBytes huftmpsiz
    let decoder = ForeignPtr Word8 -> Size -> HuffmanDecoder
decodeH ForeignPtr Word8
buf Size
huftmpsiz
        info = DecodeInfo -> CodeInfo
CID (DecodeInfo -> CodeInfo) -> DecodeInfo -> CodeInfo
forall a b. (a -> b) -> a -> b
$ HuffmanDecoder -> IORef Size -> DecodeInfo
DecodeInfo HuffmanDecoder
decoder IORef Size
lim
    newDynamicTable maxsiz info

newDynamicTable :: Size -> CodeInfo -> IO DynamicTable
newDynamicTable :: Size -> CodeInfo -> IO DynamicTable
newDynamicTable Size
maxsiz CodeInfo
info = do
    tbl <- (Size, Size) -> Entry -> IO Table
forall i. Ix i => (i, i) -> Entry -> IO (IOArray i Entry)
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> e -> m (a i e)
newArray (Size
0, Size
end) Entry
dummyEntry
    DynamicTable info
        <$> newIORef tbl -- circularTable
        <*> newIORef end -- offset
        <*> newIORef 0 -- numOfEntries
        <*> newIORef maxN -- maxNumOfEntries
        <*> newIORef 0 -- dynamicTableSize
        <*> newIORef maxsiz -- maxDynamicTableSize
  where
    maxN :: Size
maxN = Size -> Size
maxNumbers Size
maxsiz
    end :: Size
end = Size
maxN Size -> Size -> Size
forall a. Num a => a -> a -> a
- Size
1

-- | Renewing 'DynamicTable' with necessary entries copied.
renewDynamicTable :: Size -> DynamicTable -> IO ()
renewDynamicTable :: Size -> DynamicTable -> IO ()
renewDynamicTable Size
maxsiz dyntbl :: DynamicTable
dyntbl@DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    renew <- DynamicTable -> Size -> IO Bool
shouldRenew DynamicTable
dyntbl Size
maxsiz
    when renew $ do
        entries <- getEntries dyntbl
        let maxN = Size -> Size
maxNumbers Size
maxsiz
            end = Size
maxN Size -> Size -> Size
forall a. Num a => a -> a -> a
- Size
1
        newtbl <- newArray (0, end) dummyEntry
        writeIORef circularTable newtbl
        writeIORef offset end
        writeIORef numOfEntries 0
        writeIORef maxNumOfEntries maxN
        writeIORef dynamicTableSize 0
        writeIORef maxDynamicTableSize maxsiz
        case codeInfo of
            CIE (EncodeInfo RevIndex
rev IORef (Maybe Size)
_) -> RevIndex -> IO ()
renewRevIndex RevIndex
rev
            CodeInfo
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        copyEntries dyntbl entries

getEntries :: DynamicTable -> IO [Entry]
getEntries :: DynamicTable -> IO [Entry]
getEntries DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    n <- readIORef numOfEntries
    table <- readIORef circularTable
    let readTable Size
i = Size -> Size -> IO Size
adj Size
maxN (Size
off Size -> Size -> Size
forall a. Num a => a -> a -> a
+ Size
i) IO Size -> (Size -> IO Entry) -> IO Entry
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Table -> Size -> IO Entry
forall i. Ix i => IOArray i Entry -> Size -> IO Entry
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> Size -> m e
unsafeRead Table
table
    forM [1 .. n] readTable

copyEntries :: DynamicTable -> [Entry] -> IO ()
copyEntries :: DynamicTable -> [Entry] -> IO ()
copyEntries DynamicTable
_ [] = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
copyEntries dyntbl :: DynamicTable
dyntbl@DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} (Entry
e : [Entry]
es) = do
    dsize <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
dynamicTableSize
    maxdsize <- readIORef maxDynamicTableSize
    when (dsize + entrySize e <= maxdsize) $ do
        insertEnd e dyntbl
        copyEntries dyntbl es

-- | Is the size of 'DynamicTable' really changed?
shouldRenew :: DynamicTable -> Size -> IO Bool
shouldRenew :: DynamicTable -> Size -> IO Bool
shouldRenew DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} Size
maxsiz = do
    maxdsize <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxDynamicTableSize
    return $ maxdsize /= maxsiz

----------------------------------------------------------------

-- | Creating 'DynamicTable' for encoding,
--   performing the action and
--   clearing the 'DynamicTable'.
withDynamicTableForEncoding
    :: Size
    -- ^ The dynamic table size
    -> (DynamicTable -> IO a)
    -> IO a
withDynamicTableForEncoding :: forall a. Size -> (DynamicTable -> IO a) -> IO a
withDynamicTableForEncoding Size
maxsiz DynamicTable -> IO a
action =
    Size -> IO DynamicTable
newDynamicTableForEncoding Size
maxsiz IO DynamicTable -> (DynamicTable -> IO a) -> IO a
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= DynamicTable -> IO a
action

-- | Creating 'DynamicTable' for decoding,
--   performing the action and
--   clearing the 'DynamicTable'.
withDynamicTableForDecoding
    :: Size
    -- ^ The dynamic table size
    -> Size
    -- ^ The size of temporary buffer for Huffman
    -> (DynamicTable -> IO a)
    -> IO a
withDynamicTableForDecoding :: forall a. Size -> Size -> (DynamicTable -> IO a) -> IO a
withDynamicTableForDecoding Size
maxsiz Size
huftmpsiz DynamicTable -> IO a
action =
    Size -> Size -> IO DynamicTable
newDynamicTableForDecoding Size
maxsiz Size
huftmpsiz IO DynamicTable -> (DynamicTable -> IO a) -> IO a
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= DynamicTable -> IO a
action

----------------------------------------------------------------

-- | Inserting 'Entry' to 'DynamicTable'.
--   New 'DynamicTable', the largest new 'Index'
--   and a set of dropped OLD 'Index'
--   are returned.
insertEntry :: Entry -> DynamicTable -> IO ()
insertEntry :: Entry -> DynamicTable -> IO ()
insertEntry Entry
e dyntbl :: DynamicTable
dyntbl@DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    Entry -> DynamicTable -> IO ()
insertFront Entry
e DynamicTable
dyntbl
    es <- DynamicTable -> IO [Entry]
adjustTableSize DynamicTable
dyntbl
    case codeInfo of
        CIE (EncodeInfo RevIndex
rev IORef (Maybe Size)
_) -> [Entry] -> RevIndex -> IO ()
deleteRevIndexList [Entry]
es RevIndex
rev
        CodeInfo
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

insertFront :: Entry -> DynamicTable -> IO ()
insertFront :: Entry -> DynamicTable -> IO ()
insertFront Entry
e DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    n <- readIORef numOfEntries
    dsize <- readIORef dynamicTableSize
    table <- readIORef circularTable
    let i = Size
off
        dsize' = Size
dsize Size -> Size -> Size
forall a. Num a => a -> a -> a
+ Entry -> Size
entrySize Entry
e
    if maxN == 0
        then return ()
        else do
            off' <- adj maxN (off - 1)
            unsafeWrite table i e
            writeIORef offset off'
            writeIORef numOfEntries $ n + 1
            writeIORef dynamicTableSize dsize'
            case codeInfo of
                CIE (EncodeInfo RevIndex
rev IORef (Maybe Size)
_) -> Entry -> HIndex -> RevIndex -> IO ()
insertRevIndex Entry
e (Size -> HIndex
DIndex Size
i) RevIndex
rev
                CodeInfo
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

adjustTableSize :: DynamicTable -> IO [Entry]
adjustTableSize :: DynamicTable -> IO [Entry]
adjustTableSize dyntbl :: DynamicTable
dyntbl@DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = [Entry] -> IO [Entry]
adjust []
  where
    adjust :: [Entry] -> IO [Entry]
    adjust :: [Entry] -> IO [Entry]
adjust [Entry]
es = do
        dsize <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
dynamicTableSize
        maxdsize <- readIORef maxDynamicTableSize
        if dsize <= maxdsize
            then return es
            else do
                e <- removeEnd dyntbl
                adjust (e : es)

----------------------------------------------------------------

insertEnd :: Entry -> DynamicTable -> IO ()
insertEnd :: Entry -> DynamicTable -> IO ()
insertEnd Entry
e DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    n <- readIORef numOfEntries
    dsize <- readIORef dynamicTableSize
    table <- readIORef circularTable
    i <- adj maxN (off + n + 1)
    let dsize' = Size
dsize Size -> Size -> Size
forall a. Num a => a -> a -> a
+ Entry -> Size
entrySize Entry
e
    unsafeWrite table i e
    writeIORef numOfEntries $ n + 1
    writeIORef dynamicTableSize dsize'
    case codeInfo of
        CIE (EncodeInfo RevIndex
rev IORef (Maybe Size)
_) -> Entry -> HIndex -> RevIndex -> IO ()
insertRevIndex Entry
e (Size -> HIndex
DIndex Size
i) RevIndex
rev
        CodeInfo
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

----------------------------------------------------------------

removeEnd :: DynamicTable -> IO Entry
removeEnd :: DynamicTable -> IO Entry
removeEnd DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    n <- readIORef numOfEntries
    i <- adj maxN (off + n)
    table <- readIORef circularTable
    e <- unsafeRead table i
    unsafeWrite table i dummyEntry -- let the entry GCed
    dsize <- readIORef dynamicTableSize
    let dsize' = Size
dsize Size -> Size -> Size
forall a. Num a => a -> a -> a
- Entry -> Size
entrySize Entry
e
    writeIORef numOfEntries (n - 1)
    writeIORef dynamicTableSize dsize'
    return e

----------------------------------------------------------------

{-# INLINE toDynamicEntry #-}
toDynamicEntry :: DynamicTable -> Index -> IO Entry
toDynamicEntry :: DynamicTable -> Size -> IO Entry
toDynamicEntry DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} Size
idx = do
    maxN <- IORef Size -> IO Size
forall a. IORef a -> IO a
readIORef IORef Size
maxNumOfEntries
    off <- readIORef offset
    n <- readIORef numOfEntries
    when (idx > n + staticTableSize) $ throwIO $ IndexOverrun idx
    didx <- adj maxN (idx + off - staticTableSize)
    table <- readIORef circularTable
    unsafeRead table didx

----------------------------------------------------------------

{-# INLINE getRevIndex #-}
getRevIndex :: DynamicTable -> RevIndex
getRevIndex :: DynamicTable -> RevIndex
getRevIndex DynamicTable{IORef Size
IORef Table
CodeInfo
maxDynamicTableSize :: DynamicTable -> IORef Size
dynamicTableSize :: DynamicTable -> IORef Size
maxNumOfEntries :: DynamicTable -> IORef Size
numOfEntries :: DynamicTable -> IORef Size
offset :: DynamicTable -> IORef Size
circularTable :: DynamicTable -> IORef Table
codeInfo :: DynamicTable -> CodeInfo
codeInfo :: CodeInfo
circularTable :: IORef Table
offset :: IORef Size
numOfEntries :: IORef Size
maxNumOfEntries :: IORef Size
dynamicTableSize :: IORef Size
maxDynamicTableSize :: IORef Size
..} = RevIndex
rev
  where
    EncodeInfo RevIndex
rev IORef (Maybe Size)
_ = CodeInfo -> EncodeInfo
toEncodeInfo CodeInfo
codeInfo