http3-0.1.0: Network/QPACK/HeaderBlock/Prefix.hs
{-# LANGUAGE BinaryLiterals #-}
module Network.QPACK.HeaderBlock.Prefix (
-- * Prefix
encodePrefix,
decodePrefix,
encodeRequiredInsertCount,
decodeRequiredInsertCount,
encodeBase,
decodeBase,
) where
import qualified Control.Exception as E
import Network.ByteOrder
import Network.HPACK.Internal (decodeI, encodeI)
import Imports
import Network.QPACK.Error
import Network.QPACK.Table
import Network.QPACK.Types
----------------------------------------------------------------
-- |
-- >>> encodeRequiredInsertCount 3 9
-- 4
-- >>> encodeRequiredInsertCount 128 1000
-- 233
encodeRequiredInsertCount :: Int -> RequiredInsertCount -> Int
encodeRequiredInsertCount _ 0 = 0
encodeRequiredInsertCount maxEntries (RequiredInsertCount reqInsertCount) =
(reqInsertCount `mod` (2 * maxEntries)) + 1
-- | for decoder
--
-- >>> decodeRequiredInsertCount 3 10 4
-- RequiredInsertCount 9
-- >>> decodeRequiredInsertCount 128 990 233
-- RequiredInsertCount 1000
decodeRequiredInsertCount
:: Int -> InsertionPoint -> Int -> RequiredInsertCount
decodeRequiredInsertCount _ _ 0 = 0
decodeRequiredInsertCount 0 _ n = RequiredInsertCount (n - 1)
decodeRequiredInsertCount maxEntries (InsertionPoint totalNumberOfInserts) encodedInsertCount
| encodedInsertCount > fullRange = E.throw IllegalInsertCount
| reqInsertCount > maxValue && reqInsertCount <= fullRange =
E.throw IllegalInsertCount
| reqInsertCount > maxValue = RequiredInsertCount (reqInsertCount - fullRange)
| otherwise = RequiredInsertCount reqInsertCount
where
fullRange = 2 * maxEntries
maxValue = totalNumberOfInserts + maxEntries
maxWrapped = (maxValue `div` fullRange) * fullRange
reqInsertCount = maxWrapped + encodedInsertCount - 1
----------------------------------------------------------------
-- |
-- >>> encodeBase 6 9
-- (False,3)
-- >>> encodeBase 9 6
-- (True,2)
encodeBase :: RequiredInsertCount -> BasePoint -> (Bool, Int)
encodeBase (RequiredInsertCount reqInsCnt) (BasePoint base)
| diff >= 0 = (False, diff) -- base - reqInsCnt
| otherwise = (True, negate diff - 1) -- reqInsCnt - base - 1
where
diff = base - reqInsCnt
-- |
-- >>> decodeBase 6 False 3
-- BasePoint 9
-- >>> decodeBase 9 True 2
-- BasePoint 6
decodeBase :: RequiredInsertCount -> Bool -> Int -> BasePoint
decodeBase (RequiredInsertCount reqInsCnt) False deltaBase = BasePoint (reqInsCnt + deltaBase)
decodeBase (RequiredInsertCount reqInsCnt) True deltaBase = BasePoint (reqInsCnt - deltaBase - 1)
----------------------------------------------------------------
-- | Encoding the prefix part of header block.
-- This should be used after 'encodeTokenHeader'.
encodePrefix :: WriteBuffer -> DynamicTable -> IO ()
encodePrefix wbuf dyntbl = do
clearWriteBuffer wbuf
maxEntries <- getMaxNumOfEntries dyntbl
baseIndex <- getBasePoint dyntbl
reqInsCnt <- getRequiredInsertCount dyntbl
qpackDebug dyntbl $ print reqInsCnt
-- Required Insert Count
let ric = encodeRequiredInsertCount maxEntries reqInsCnt
encodeI wbuf set0 8 ric
-- Sign bit + Delta Base (7+)
let (s, base) = encodeBase reqInsCnt baseIndex
set
| s = set1
| otherwise = set0
encodeI wbuf set 7 base
-- | Decoding the prefix part of header block.
decodePrefix
:: ReadBuffer -> DynamicTable -> IO (RequiredInsertCount, BasePoint, Bool)
decodePrefix rbuf dyntbl = do
maxEntries <- getMaxNumOfEntries dyntbl
totalNumberOfInserts <- getInsertionPoint dyntbl
w8 <- read8 rbuf
ric <- decodeI 8 w8 rbuf
let reqInsCnt = decodeRequiredInsertCount maxEntries totalNumberOfInserts ric
w8' <- read8 rbuf
let s = w8' `testBit` 7
w8'' = w8' .&. 0b01111111
delta <- decodeI 7 w8'' rbuf
let baseIndex = decodeBase reqInsCnt s delta
qpackDebug dyntbl $
putStrLn $
show reqInsCnt
++ ", "
++ show baseIndex
++ ", "
++ show totalNumberOfInserts
++ ", maxN "
++ show maxEntries
let needAck = reqInsCnt /= 0
return (reqInsCnt, baseIndex, needAck)