{-# LANGUAGE BinaryLiterals #-} module Network.QPACK.HeaderBlock.Prefix ( -- * Prefix encodePrefix, decodePrefix, encodeRequiredInsertCount, decodeRequiredInsertCount, encodeBase, decodeBase, ) where import Network.ByteOrder import Network.HPACK.Internal import qualified UnliftIO.Exception as E import Imports import Network.QPACK.Error import Network.QPACK.Table import Network.QPACK.Types ---------------------------------------------------------------- -- | -- >>> encodeRequiredInsertCount 3 9 -- 4 -- >>> encodeRequiredInsertCount 128 1000 -- 233 encodeRequiredInsertCount :: Int -> InsertionPoint -> Int encodeRequiredInsertCount _ 0 = 0 encodeRequiredInsertCount maxEntries (InsertionPoint reqInsertCount) = (reqInsertCount `mod` (2 * maxEntries)) + 1 -- | for decoder -- -- >>> decodeRequiredInsertCount 3 10 4 -- InsertionPoint 9 -- >>> decodeRequiredInsertCount 128 990 233 -- InsertionPoint 1000 decodeRequiredInsertCount :: Int -> InsertionPoint -> Int -> InsertionPoint decodeRequiredInsertCount _ _ 0 = 0 decodeRequiredInsertCount maxEntries (InsertionPoint totalNumberOfInserts) encodedInsertCount | encodedInsertCount > fullRange = E.impureThrow IllegalInsertCount | reqInsertCount > maxValue && reqInsertCount <= fullRange = E.impureThrow IllegalInsertCount | reqInsertCount > maxValue = InsertionPoint (reqInsertCount - fullRange) | otherwise = InsertionPoint 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 :: InsertionPoint -> BasePoint -> (Bool, Int) encodeBase (InsertionPoint 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 :: InsertionPoint -> Bool -> Int -> BasePoint decodeBase (InsertionPoint reqInsCnt) False deltaBase = BasePoint (reqInsCnt + deltaBase) decodeBase (InsertionPoint 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 <- getLargestReference dyntbl -- 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 (InsertionPoint, BasePoint) 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 $ "Required" ++ show reqInsCnt ++ ", " ++ show baseIndex return (reqInsCnt, baseIndex)