module Network.Wai.Middleware.Push.Referer.LRU (
Cache(..)
, Priority
, empty
, insert
, lookup
) where
import Data.OrdPSQ (OrdPSQ)
import qualified Data.OrdPSQ as PSQ
import Data.Int (Int64)
import Prelude hiding (lookup)
import Network.Wai.Middleware.Push.Referer.Multi (Multi)
import qualified Network.Wai.Middleware.Push.Referer.Multi as M
type Priority = Int64
data Cache k v = Cache {
cCapacity :: Int
, cSize :: Int
, cValLimit :: Int
, cTick :: Priority
, cQueue :: OrdPSQ k Priority (Multi v)
} deriving (Eq, Show)
empty :: Int -> Int -> Cache k v
empty capacity valLimit
| capacity < 1 = error "Cache.empty: capacity < 1"
| otherwise = Cache {
cCapacity = capacity
, cSize = 0
, cValLimit = valLimit
, cTick = 0
, cQueue = PSQ.empty
}
trim :: Ord k => Cache k v -> Cache k v
trim c
| cTick c == maxBound = empty (cCapacity c) (cValLimit c)
| cSize c > cCapacity c = c {
cSize = cSize c - 1
, cQueue = PSQ.deleteMin (cQueue c)
}
| otherwise = c
insert :: (Ord k, Ord v) => k -> v -> Cache k v -> Cache k v
insert k v c = case PSQ.alter lookupAndBump k (cQueue c) of
(True, q) -> trim $ c { cTick = cTick c + 1, cQueue = q, cSize = cSize c + 1}
(False, q) -> trim $ c { cTick = cTick c + 1, cQueue = q }
where
lookupAndBump Nothing = (True, Just (cTick c, M.singleton (cValLimit c) v))
lookupAndBump (Just (_, x)) = (False, Just (cTick c, M.insert v x))
lookup :: Ord k => k -> Cache k v -> (Cache k v, [v])
lookup k c = case PSQ.alter lookupAndBump k (cQueue c) of
(Nothing, _) -> (c, [])
(Just x, q) -> let c' = trim $ c { cTick = cTick c + 1, cQueue = q }
xs = M.list x
in (c', xs)
where
lookupAndBump Nothing = (Nothing, Nothing)
lookupAndBump (Just (_, x)) = (Just x, Just (cTick c, x))