mercurial/util.py
changeset 27371 45d996a566d7
parent 27363 c7ab2087d436
child 27391 4eeef1b2d689
equal deleted inserted replaced
27368:409a20314c64 27371:45d996a566d7
   508             yield k, self[k]
   508             yield k, self[k]
   509     def insert(self, index, key, val):
   509     def insert(self, index, key, val):
   510         self._list.insert(index, key)
   510         self._list.insert(index, key)
   511         dict.__setitem__(self, key, val)
   511         dict.__setitem__(self, key, val)
   512 
   512 
       
   513 class _lrucachenode(object):
       
   514     """A node in a doubly linked list.
       
   515 
       
   516     Holds a reference to nodes on either side as well as a key-value
       
   517     pair for the dictionary entry.
       
   518     """
       
   519     __slots__ = ('next', 'prev', 'key', 'value')
       
   520 
       
   521     def __init__(self):
       
   522         self.next = None
       
   523         self.prev = None
       
   524 
       
   525         self.key = _notset
       
   526         self.value = None
       
   527 
       
   528     def markempty(self):
       
   529         """Mark the node as emptied."""
       
   530         self.key = _notset
       
   531 
   513 class lrucachedict(object):
   532 class lrucachedict(object):
   514     '''cache most recent gets from or sets to this dictionary'''
   533     """Dict that caches most recent accesses and sets.
   515     def __init__(self, maxsize):
   534 
       
   535     The dict consists of an actual backing dict - indexed by original
       
   536     key - and a doubly linked circular list defining the order of entries in
       
   537     the cache.
       
   538 
       
   539     The head node is the newest entry in the cache. If the cache is full,
       
   540     we recycle head.prev and make it the new head. Cache accesses result in
       
   541     the node being moved to before the existing head and being marked as the
       
   542     new head node.
       
   543 
       
   544     NOTE: construction of this class doesn't scale well if the cache size
       
   545     is in the thousands. Avoid creating hundreds or thousands of instances
       
   546     with large capacities.
       
   547     """
       
   548     def __init__(self, max):
   516         self._cache = {}
   549         self._cache = {}
   517         self._maxsize = maxsize
   550 
   518         self._order = collections.deque()
   551         self._head = head = _lrucachenode()
   519 
   552         head.prev = head
   520     def __getitem__(self, key):
   553         head.next = head
   521         value = self._cache[key]
   554         self._size = 1
   522         self._order.remove(key)
   555         self._capacity = max
   523         self._order.append(key)
   556 
   524         return value
   557     def __len__(self):
   525 
   558         return len(self._cache)
   526     def __setitem__(self, key, value):
   559 
   527         if key not in self._cache:
   560     def __contains__(self, k):
   528             if len(self._cache) >= self._maxsize:
   561         return k in self._cache
   529                 del self._cache[self._order.popleft()]
   562 
       
   563     def __iter__(self):
       
   564         # We don't have to iterate in cache order, but why not.
       
   565         n = self._head
       
   566         for i in range(len(self._cache)):
       
   567             yield n.key
       
   568             n = n.next
       
   569 
       
   570     def __getitem__(self, k):
       
   571         node = self._cache[k]
       
   572         self._movetohead(node)
       
   573         return node.value
       
   574 
       
   575     def __setitem__(self, k, v):
       
   576         node = self._cache.get(k)
       
   577         # Replace existing value and mark as newest.
       
   578         if node is not None:
       
   579             node.value = v
       
   580             self._movetohead(node)
       
   581             return
       
   582 
       
   583         if self._size < self._capacity:
       
   584             node = self._addcapacity()
   530         else:
   585         else:
   531             self._order.remove(key)
   586             # Grab the last/oldest item.
   532         self._cache[key] = value
   587             node = self._head.prev
   533         self._order.append(key)
   588 
   534 
   589         # At capacity. Kill the old entry.
   535     def __contains__(self, key):
   590         if node.key is not _notset:
   536         return key in self._cache
   591             del self._cache[node.key]
       
   592 
       
   593         node.key = k
       
   594         node.value = v
       
   595         self._cache[k] = node
       
   596         # And mark it as newest entry. No need to adjust order since it
       
   597         # is already self._head.prev.
       
   598         self._head = node
       
   599 
       
   600     def __delitem__(self, k):
       
   601         node = self._cache.pop(k)
       
   602         node.markempty()
       
   603 
       
   604         # Temporarily mark as newest item before re-adjusting head to make
       
   605         # this node the oldest item.
       
   606         self._movetohead(node)
       
   607         self._head = node.next
       
   608 
       
   609     # Additional dict methods.
       
   610 
       
   611     def get(self, k, default=None):
       
   612         try:
       
   613             return self._cache[k]
       
   614         except KeyError:
       
   615             return default
   537 
   616 
   538     def clear(self):
   617     def clear(self):
       
   618         n = self._head
       
   619         while n.key is not _notset:
       
   620             n.markempty()
       
   621             n = n.next
       
   622 
   539         self._cache.clear()
   623         self._cache.clear()
   540         self._order = collections.deque()
   624 
       
   625     def _movetohead(self, node):
       
   626         """Mark a node as the newest, making it the new head.
       
   627 
       
   628         When a node is accessed, it becomes the freshest entry in the LRU
       
   629         list, which is denoted by self._head.
       
   630 
       
   631         Visually, let's make ``N`` the new head node (* denotes head):
       
   632 
       
   633             previous/oldest <-> head <-> next/next newest
       
   634 
       
   635             ----<->--- A* ---<->-----
       
   636             |                       |
       
   637             E <-> D <-> N <-> C <-> B
       
   638 
       
   639         To:
       
   640 
       
   641             ----<->--- N* ---<->-----
       
   642             |                       |
       
   643             E <-> D <-> C <-> B <-> A
       
   644 
       
   645         This requires the following moves:
       
   646 
       
   647            C.next = D  (node.prev.next = node.next)
       
   648            D.prev = C  (node.next.prev = node.prev)
       
   649            E.next = N  (head.prev.next = node)
       
   650            N.prev = E  (node.prev = head.prev)
       
   651            N.next = A  (node.next = head)
       
   652            A.prev = N  (head.prev = node)
       
   653         """
       
   654         head = self._head
       
   655         # C.next = D
       
   656         node.prev.next = node.next
       
   657         # D.prev = C
       
   658         node.next.prev = node.prev
       
   659         # N.prev = E
       
   660         node.prev = head.prev
       
   661         # N.next = A
       
   662         # It is tempting to do just "head" here, however if node is
       
   663         # adjacent to head, this will do bad things.
       
   664         node.next = head.prev.next
       
   665         # E.next = N
       
   666         node.next.prev = node
       
   667         # A.prev = N
       
   668         node.prev.next = node
       
   669 
       
   670         self._head = node
       
   671 
       
   672     def _addcapacity(self):
       
   673         """Add a node to the circular linked list.
       
   674 
       
   675         The new node is inserted before the head node.
       
   676         """
       
   677         head = self._head
       
   678         node = _lrucachenode()
       
   679         head.prev.next = node
       
   680         node.prev = head.prev
       
   681         node.next = head
       
   682         head.prev = node
       
   683         self._size += 1
       
   684         return node
   541 
   685 
   542 def lrucachefunc(func):
   686 def lrucachefunc(func):
   543     '''cache most recent results of function calls'''
   687     '''cache most recent results of function calls'''
   544     cache = {}
   688     cache = {}
   545     order = collections.deque()
   689     order = collections.deque()