module Cache where import Utils import Data.Bits import Data.HashTable (HashTable) import qualified Data.HashTable as HashTable import Data.Int import Data.IORef import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe (isJust, fromJust) import Data.Unique import Control.Monad (when) import System.IO.Unsafe import System.Mem.StableName class Hashable a where hash :: a -> Int32 instance Hashable String where hash s = HashTable.hashString s instance Hashable Int where hash i = HashTable.hashInt i instance Hashable Unique where hash u = hash (hashUnique u) instance Hashable (StableName a) where hash n = hash (hashStableName n) instance (Hashable a, Hashable b) => Hashable (a,b) where hash (x,y) = hash x `xor` HashTable.hashInt (fromIntegral $ hash y) type LinkedList a = IORef (LinkedNode a) data LinkedNode a = LinkedNode { lnPrev :: LinkedList a, lnValue :: IORef a, lnNext :: LinkedList a } | End { lnPrev :: LinkedList a, lnNext :: LinkedList a } isEnd (LinkedNode _ _ _) = False isEnd (End _ _) = True newList :: IO (LinkedList a) newList = mdo let end = End p n p <- newIORef end; n <- newIORef end; list <- newIORef end return list newNode :: a -> IO (LinkedNode a) newNode x = do let err = error "Cache: access to not-yet-linked node" p <- newIORef err; val <- newIORef x; n <- newIORef err return (LinkedNode p val n) appendNode :: LinkedNode a -> LinkedList a -> IO () appendNode node list = do n <- readIORef list; p <- readIORef (lnPrev n) writeIORef (lnNext p) node; writeIORef (lnPrev n) node writeIORef (lnPrev node) p; writeIORef (lnNext node) n removeFirst :: LinkedList a -> IO a removeFirst list = do l <- readIORef list; node <- readIORef (lnNext l) removeNode node readIORef (lnValue node) removeNode :: LinkedNode a -> IO () removeNode node = do when (isEnd node) $ error "Cache: remove from empty list" p <- readIORef (lnPrev node); n <- readIORef (lnNext node) let err = error "Cache: access to unlinked node" writeIORef (lnPrev node) err; writeIORef (lnNext node) err writeIORef (lnNext p) n; writeIORef (lnPrev n) p access :: LinkedList a -> LinkedNode a -> IO () access list node = do removeNode node; appendNode node list add :: a -> LinkedList a -> IO (LinkedNode a) add x list = do node <- newNode x; appendNode node list; return node byAddress :: a -> StableName a byAddress = unsafePerformIO . makeStableName type Cache key value = (IORef Int, Int, HashTable key (value, LinkedNode key), LinkedList key) newCache :: (Eq key, Hashable key) => Int -> Cache key value newCache maxsize = unsafePerformIO $ do ht <- HashTable.new (==) hash lru <- newList; size <- newIORef 0 return (size, maxsize, ht, lru) cached :: (Eq k, Hashable k) => k -> Cache k v -> v -> v cached key (sizeRef, maxsize, cache, lru) val = unsafePerformIO $ do mval' <- HashTable.lookup cache key if isJust mval' then do let (val', node) = fromJust mval' access lru node --putStrLn "Cache access" return val' else do size <- readIORef sizeRef --putStrLn ("Cache add, former size " ++ show size) if size < maxsize then writeIORef sizeRef (size+1) else do dropped <- removeFirst lru HashTable.delete cache dropped node <- add key lru HashTable.insert cache key (val, node) return val