diff -r bd9d3a89f07b -r ee087f0d7db5 mercurial/util.py --- a/mercurial/util.py Wed Sep 05 23:15:20 2018 -0700 +++ b/mercurial/util.py Fri Sep 07 12:14:42 2018 -0700 @@ -1209,7 +1209,7 @@ Holds a reference to nodes on either side as well as a key-value pair for the dictionary entry. """ - __slots__ = (u'next', u'prev', u'key', u'value') + __slots__ = (u'next', u'prev', u'key', u'value', u'cost') def __init__(self): self.next = None @@ -1217,10 +1217,13 @@ self.key = _notset self.value = None + self.cost = 0 def markempty(self): """Mark the node as emptied.""" self.key = _notset + self.value = None + self.cost = 0 class lrucachedict(object): """Dict that caches most recent accesses and sets. @@ -1233,6 +1236,10 @@ we recycle head.prev and make it the new head. Cache accesses result in the node being moved to before the existing head and being marked as the new head node. + + Items in the cache can be inserted with an optional "cost" value. This is + simply an integer that is specified by the caller. The cache can be queried + for the total cost of all items presently in the cache. """ def __init__(self, max): self._cache = {} @@ -1242,6 +1249,7 @@ head.next = head self._size = 1 self.capacity = max + self.totalcost = 0 def __len__(self): return len(self._cache) @@ -1261,11 +1269,15 @@ self._movetohead(node) return node.value - def __setitem__(self, k, v): + def insert(self, k, v, cost=0): + """Insert a new item in the cache with optional cost value.""" node = self._cache.get(k) # Replace existing value and mark as newest. if node is not None: + self.totalcost -= node.cost node.value = v + node.cost = cost + self.totalcost += cost self._movetohead(node) return @@ -1277,17 +1289,24 @@ # At capacity. Kill the old entry. if node.key is not _notset: + self.totalcost -= node.cost del self._cache[node.key] node.key = k node.value = v + node.cost = cost + self.totalcost += cost self._cache[k] = node # And mark it as newest entry. No need to adjust order since it # is already self._head.prev. self._head = node + def __setitem__(self, k, v): + self.insert(k, v) + def __delitem__(self, k): node = self._cache.pop(k) + self.totalcost -= node.cost node.markempty() # Temporarily mark as newest item before re-adjusting head to make @@ -1306,6 +1325,7 @@ def clear(self): n = self._head while n.key is not _notset: + self.totalcost -= n.cost n.markempty() n = n.next @@ -1336,7 +1356,7 @@ # We could potentially skip the first N items when decreasing capacity. # But let's keep it simple unless it is a performance problem. for i in range(len(self._cache)): - result[n.key] = n.value + result.insert(n.key, n.value, cost=n.cost) n = n.prev return result @@ -1359,6 +1379,7 @@ # And remove it from the cache and mark it as empty. del self._cache[n.key] + self.totalcost -= n.cost n.markempty() return key, value