http2-0.1.2: Network/HPACK/Context/ReferenceSet.hs
module Network.HPACK.Context.ReferenceSet (
-- * Type
ReferenceSet
, emptyReferenceSet
-- * Index and reference set
, isMember
, addIndex
, removeIndex
, removeIndices
-- * Managing reference set
, getNotEmittedIndices
, adjustReferenceSet
, renewForEncoding
, renewForDecoding
, Sequence(..)
, lookupAndUpdate
, getCommon
) where
import Data.List (foldl')
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Network.HPACK.Table
----------------------------------------------------------------
data Status = Old -- E: Initial state for encoding
| NotEmitted -- E&D: Initial state for decoding
| Emitted -- E&D:
deriving (Eq,Show)
-- | Type for the reference set.
newtype ReferenceSet = ReferenceSet (Map Index Status) deriving Show
-- Must not use IntMap because its mapKeysMonotonic is slow
-- | Empty reference set.
emptyReferenceSet :: ReferenceSet
emptyReferenceSet = ReferenceSet M.empty
----------------------------------------------------------------
-- | Is 'Index' a member of 'ReferenceSet'?
isMember :: Index -> ReferenceSet -> Bool
isMember idx (ReferenceSet m) = idx `M.member` m
-- | Adding 'Index' to 'ReferenceSet'.
addIndex :: Index -> ReferenceSet -> ReferenceSet
addIndex idx (ReferenceSet m) = ReferenceSet $ M.insert idx Emitted m
-- | Removing 'Index' from 'ReferenceSet'.
removeIndex :: Index -> ReferenceSet -> ReferenceSet
removeIndex idx (ReferenceSet m) = ReferenceSet $ M.delete idx m
-- | Removing a set of 'Index' from 'ReferenceSet'.
removeIndices :: [Index] -> ReferenceSet -> ReferenceSet
removeIndices idcs (ReferenceSet m) = ReferenceSet $ foldl' (flip M.delete) m idcs
----------------------------------------------------------------
-- | Get all not emitted 'Index' from 'ReferenceSet'.
getNotEmittedIndices :: ReferenceSet -> [Index]
getNotEmittedIndices (ReferenceSet m) = M.keys $ M.filter (== NotEmitted) m
-- | Renewing 'ReferenceSet' for the next encoding step.
renewForEncoding :: ReferenceSet -> ([Index],ReferenceSet)
renewForEncoding (ReferenceSet m) = (removedIndces, ReferenceSet m')
where
(oldm,newm) = M.partition (== Old) m
removedIndces = M.keys oldm
m' = M.map (const Old) newm
-- | Renewing 'ReferenceSet' for the next decoding step.
renewForDecoding :: ReferenceSet -> ReferenceSet
renewForDecoding (ReferenceSet m) = ReferenceSet m'
where
m' = M.map (const NotEmitted) m
-- | Incrementing all 'Index' by one.
adjustReferenceSet :: ReferenceSet -> ReferenceSet
adjustReferenceSet (ReferenceSet m) = ReferenceSet $ M.mapKeysMonotonic (+1) m
data Sequence = Z | E0 | E2 | E4
lookupAndUpdate :: Index -> ReferenceSet -> (Sequence,ReferenceSet)
lookupAndUpdate idx rs@(ReferenceSet m) = case M.lookup idx m of
Nothing -> (Z, rs)
Just Old -> (E0, ReferenceSet $ M.adjust (const NotEmitted) idx m)
Just NotEmitted -> (E4, ReferenceSet $ M.adjust (const Emitted) idx m)
Just Emitted -> (E2, rs)
getCommon :: [Index] -> ReferenceSet -> [Index]
getCommon is (ReferenceSet m) = go is []
where
go [] ret = ret
go (n:ns) ret = case M.lookup n m of
Just NotEmitted -> go ns (n:ret)
_ -> go ns ret