module Network.HTTP2.Priority.PSQ (
PriorityQueue
, empty
, isEmpty
, enqueue
, dequeue
, delete
, clear
) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative ((<$>))
#endif
import Data.Array (Array, listArray, (!))
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as I
import Data.IntPSQ (IntPSQ)
import qualified Data.IntPSQ as P
import Data.Word (Word64)
type Key = Int
type Weight = Int
type Deficit = Word64
type Heap a = IntPSQ Deficit (Weight, a)
data PriorityQueue a = PriorityQueue {
baseDeficit :: !Deficit
, deficitMap :: IntMap Deficit
, queue :: Heap a
}
deficitSteps :: Int
deficitSteps = 65536
deficitList :: [Deficit]
deficitList = map calc idxs
where
idxs = [1..256] :: [Double]
calc w = round (fromIntegral deficitSteps / w)
deficitTable :: Array Int Deficit
deficitTable = listArray (1,256) deficitList
weightToDeficit :: Weight -> Deficit
weightToDeficit w = deficitTable ! w
empty :: PriorityQueue a
empty = PriorityQueue 0 I.empty P.empty
isEmpty :: PriorityQueue a -> Bool
isEmpty PriorityQueue{..} = P.null queue
enqueue :: Key -> Weight -> a -> PriorityQueue a -> PriorityQueue a
enqueue k w x PriorityQueue{..} =
PriorityQueue baseDeficit deficitMap' queue'
where
!d = weightToDeficit w
!forNew = baseDeficit + d
f _ _ old = old + d
(!mold, !deficitMap') = I.insertLookupWithKey f k forNew deficitMap
!deficit' = case mold of
Nothing -> forNew
Just old -> old + d
!queue' = P.insert k deficit' (w,x) queue
dequeue :: PriorityQueue a -> Maybe (Key, Weight, a, PriorityQueue a)
dequeue PriorityQueue{..} = case P.minView queue of
Nothing -> Nothing
Just (k, deficit, (w,x), queue')
| P.null queue' -> Just (k, w, x, empty)
| otherwise -> Just (k, w, x, PriorityQueue deficit deficitMap queue')
delete :: Key -> PriorityQueue a -> (Maybe a, PriorityQueue a)
delete k PriorityQueue{..} = case P.findMin queue' of
Nothing -> (mx, empty)
Just (_,deficit,_) -> let !deficitMap' = I.delete k deficitMap
in (mx, PriorityQueue deficit deficitMap' queue')
where
(!mx,!queue') = P.alter f k queue
f Nothing = (Nothing, Nothing)
f (Just (_, (_,x))) = (Just x, Nothing)
clear :: Key -> PriorityQueue a -> PriorityQueue a
clear k PriorityQueue{..} = PriorityQueue baseDeficit deficitMap' queue'
where
!deficitMap' = I.delete k deficitMap
!queue' = P.delete k queue