mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-11-24 15:25:01 -05:00
70 lines
1.9 KiB
Python
70 lines
1.9 KiB
Python
"""LRU caching class and decorator"""
|
|
import threading
|
|
|
|
_marker = object()
|
|
|
|
class LRUCache(object):
|
|
def __init__(self, size):
|
|
""" Implements a psueudo-LRU algorithm (CLOCK) """
|
|
if size < 1:
|
|
raise ValueError('size must be >1')
|
|
self.clock = []
|
|
for i in xrange(0, size):
|
|
self.clock.append({'key':_marker, 'ref':False})
|
|
self.size = size
|
|
self.maxpos = size - 1
|
|
self.hand = 0
|
|
self.data = {}
|
|
self.lock = threading.Lock()
|
|
|
|
def __contains__(self, key):
|
|
return key in self.data
|
|
|
|
def __getitem__(self, key, default=None):
|
|
try:
|
|
datum = self.data[key]
|
|
except KeyError:
|
|
return default
|
|
pos, val = datum
|
|
self.clock[pos]['ref'] = True
|
|
hand = pos + 1
|
|
if hand > self.maxpos:
|
|
hand = 0
|
|
self.hand = hand
|
|
return val
|
|
|
|
def __setitem__(self, key, val, _marker=_marker):
|
|
hand = self.hand
|
|
maxpos = self.maxpos
|
|
clock = self.clock
|
|
data = self.data
|
|
lock = self.lock
|
|
|
|
end = hand - 1
|
|
if end < 0:
|
|
end = maxpos
|
|
|
|
while 1:
|
|
current = clock[hand]
|
|
ref = current['ref']
|
|
if ref is True:
|
|
current['ref'] = False
|
|
hand = hand + 1
|
|
if hand > maxpos:
|
|
hand = 0
|
|
elif ref is False or hand == end:
|
|
lock.acquire()
|
|
try:
|
|
oldkey = current['key']
|
|
if oldkey in data:
|
|
del data[oldkey]
|
|
current['key'] = key
|
|
current['ref'] = True
|
|
data[key] = (hand, val)
|
|
hand += 1
|
|
if hand > maxpos:
|
|
hand = 0
|
|
self.hand = hand
|
|
finally:
|
|
lock.release()
|
|
break |