add LRU cache

This commit is contained in:
Marko Andjelic 2026-07-05 01:45:47 +01:00
commit da70b3ccf2
Signed by: marko
GPG key ID: 9C5E99C8C682FB59
3 changed files with 31 additions and 3 deletions

View file

@ -19,5 +19,7 @@ initialState title path =
stZoom = 1.0,
stTitle = title,
stBookPath = path,
stGen = 0
stGen = 0,
stUse = M.empty,
stTick = 0,
}

View file

@ -99,7 +99,9 @@ data AppState = AppState
stZoom :: Double,
stTitle :: T.Text,
stBookPath :: FilePath,
stGen :: Int
stGen :: Int,
stUse :: M.Map ChapterIndex Int,
stTick :: Int,
}
-- | A user request, produced by the keyboard / sidebar and handled centrally.

View file

@ -51,6 +51,10 @@ data Ctx = Ctx
ctxRender :: TVar (ChapterRef -> IO (Either String Chapter))
}
-- | The cache will start evicting after exceeding this number using LRU.
cacheCap :: Int
cacheCap = 7
--------------------------------------------------------------------------------
-- Widget tree
--------------------------------------------------------------------------------
@ -257,12 +261,32 @@ goto ctx target frag = do
case rendered of
Left err -> putStrLn ("render failed: " ++ err)
Right chapter -> do
atomically $ modifyTVar' (ctxState ctx) $ \s -> s {stCache = M.insert idx chapter (stCache s)}
atomically $ modifyTVar' (ctxState ctx) $ \s -> insertLRU idx chapter s
postGtk $ do
cur <- stIndex <$> readTVarIO (ctxState ctx)
when (cur == idx) (display ctx chapter frag)
preload ctx
-- | Bump the tick of an index to track frequency for cache eviction.
touch :: ChapterIndex -> AppState -> AppState
touch idx s =
let t = stTick s + 1
in s { stTick = t, stUse = M.insert idx t (stUse s) }
-- | Insert a rendered chapter as MRU or evict the LRU entry.
insertLRU :: ChapterIndex -> Chapter -> AppState -> AppState
insertLRU idx chapter s0 =
let s = touch idx (s0 {stCache = M.insert idx chapter (stCache s0) })
in if M.size (stCache s) <= cacheCap then s else evictLRU s
evictLRU :: AppState -> AppState
evictLRU s =
case M.toList (stUse s) of
[] -> s
xs ->
let victim = fst (minimumBy (comparing snd) xs)
in s {stCache = M.delete victim (stCache s), stUse = M.delete victim (stUse s)}
-- | Render the chapters on either side of the current one into the cache.
preload :: Ctx -> IO ()
preload ctx = do