Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: client/utils/lru.py

Issue 2414543003: isolateserver: DiskCache format v2 (Closed)
Patch Set: typo and nit Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« client/isolateserver.py ('K') | « client/tests/lru_test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2013 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file.
4
5 """Defines a dictionary that can evict least recently used items."""
6
7 import collections
8 import json
9
10
11 class LRUDict(object):
12 """Dictionary that can evict least recently used items.
13
14 Implemented as a wrapper around OrderedDict object. An OrderedDict stores
15 (key, value) pairs in order they are inserted and can effectively pop oldest
16 items.
17
18 Can also store its state as *.json file on disk.
19 """
20
21 def __init__(self):
22 # Ordered key -> value mapping, newest items at the bottom.
23 self._items = collections.OrderedDict()
24 # True if was modified after loading.
25 self._dirty = True
26
27 def __nonzero__(self):
28 """False if dict is empty."""
29 return bool(self._items)
30
31 def __iter__(self):
32 """Iterate over the keys."""
33 return self._items.__iter__()
34
35 def __len__(self):
36 """Number of items in the dict."""
37 return len(self._items)
38
39 def __contains__(self, key):
40 """True if |key| is in the dict."""
41 return key in self._items
42
43 def __getitem__(self, key):
44 """Returns value for |key| or raises KeyError if not found."""
45 return self._items[key]
46
47 @classmethod
48 def load(cls, state_file):
49 """Loads previously saved state and returns LRUDict in that state.
50
51 Raises ValueError if state file is corrupted.
52 """
53 try:
54 state = json.load(open(state_file, 'r'))
55 except (IOError, ValueError) as e:
56 raise ValueError('Broken state file %s: %s' % (state_file, e))
57
58 if not isinstance(state, list):
59 raise ValueError(
60 'Broken state file %s, should be json list' % (state_file,))
61
62 # Items are stored oldest to newest. Put them back in the same order.
63 lru = cls()
64 for pair in state:
65 if not isinstance(pair, (list, tuple)) or len(pair) != 2:
66 raise ValueError(
67 'Broken state file %s, expecting pairs: %s' % (state_file, pair))
68 lru.add(pair[0], pair[1])
69
70 # Check for duplicate keys.
71 if len(lru) != len(state):
72 raise ValueError(
73 'Broken state file %s, found duplicate keys' % (state_file,))
74
75 # Now state from the file corresponds to state in the memory.
76 lru._dirty = False
77 return lru
78
79 def save(self, state_file):
80 """Saves cache state to a file if it was modified."""
81 if not self._dirty:
82 return False
83
84 with open(state_file, 'wb') as f:
85 json.dump(self._items.items(), f, separators=(',',':'))
86
87 self._dirty = False
88 return True
89
90 def add(self, key, value):
91 """Adds or replaces a |value| for |key|, marks it as most recently used."""
92 self._items.pop(key, None)
93 self._items[key] = value
94 self._dirty = True
95
96 def batch_insert_oldest(self, items):
97 """Prepends list of |items| to the dict, marks them as least recently used.
98
99 |items| is a list of (key, value) pairs to add.
100
101 It's a very slow operation that completely rebuilds the dictionary.
102 """
103 new_items = collections.OrderedDict()
104
105 # Insert |items| first, so they became oldest.
106 for key, value in items:
107 new_items[key] = value
108
109 # Insert the rest, be careful not to override keys from |items|.
110 for key, value in self._items.iteritems():
111 if key not in new_items:
112 new_items[key] = value
113
114 self._items = new_items
115 self._dirty = True
116
117 def keys_set(self):
118 """Set of keys of items in this dict."""
119 return set(self._items)
120
121 def get(self, key, default=None):
122 """Returns value for |key| or |default| if not found."""
123 return self._items.get(key, default)
124
125 def touch(self, key):
126 """Marks |key| as most recently used.
127
128 Raises KeyError if |key| is not in the dict.
129 """
130 self._items[key] = self._items.pop(key)
131 self._dirty = True
132
133 def pop(self, key):
134 """Removes item from the dict, returns its value.
135
136 Raises KeyError if |key| is not in the dict.
137 """
138 value = self._items.pop(key)
139 self._dirty = True
140 return value
141
142 def get_oldest(self):
143 """Returns oldest item as tuple (key, value).
144
145 Raises KeyError if dict is empty.
146 """
147 for i in self._items.iteritems():
148 return i
149 raise KeyError('dictionary is empty')
150
151 def pop_oldest(self):
152 """Removes oldest item from the dict and returns it as tuple (key, value).
153
154 Raises KeyError if dict is empty.
155 """
156 pair = self._items.popitem(last=False)
157 self._dirty = True
158 return pair
159
160 def itervalues(self):
161 """Iterator over stored values in arbitrary order."""
162 return self._items.itervalues()
OLDNEW
« client/isolateserver.py ('K') | « client/tests/lru_test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698