Remove unused code

This commit is contained in:
Kovid Goyal 2019-03-25 14:28:09 +05:30
parent ba70f43118
commit 7fd70a28e8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -1,265 +0,0 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import (unicode_literals, division, absolute_import,
print_function)
# Based on https://github.com/jlhutch/pylru/blob/master/pylru.py (which is
# licensed GPL v2+)
class DoublyLinkedNode(object):
__slots__ = 'empty prev next key value'.split()
def __init__(self):
self.empty = True
self.prev = self.next = self.key = self.value = None
class lru_cache(object):
'''
An LRU cache with constant time lookup. Uses a doubly linked list that is
pre-allocated based on the cache size. Infinite caches are not supported.
'''
def __init__(self, size=1000, callback=None):
''' callback is called with the key and value when the item is either replaced or removed from the cache. '''
self.callback = callback
# Create an empty hash table.
self.table = {}
# Initialize the doubly linked list with one empty node. This is an
# invariant. The cache size must always be greater than zero. Each
# node has a 'prev' and 'next' variable to hold the node that comes
# before it and after it respectively. Initially the two variables
# each point to the head node itself, creating a circular doubly
# linked list of size one. Then the size property is used to adjust
# the list to the desired size.
self.head = DoublyLinkedNode()
self.head.next = self.head
self.head.prev = self.head
self._size = 1
# Adjust the size
self.size = size
def __len__(self):
return len(self.table)
def clear(self):
for node in self:
node.empty = True
node.key = None
node.value = None
self.table.clear()
def __contains__(self, key):
return key in self.table
def peek(self, key):
' Looks up a value in the cache without affecting cache order. '
return self.table[key].value
def __getitem__(self, key):
# Look up the node
node = self.table[key]
# Update the list ordering. Move this node so that is directly
# proceeds the head node. Then set the 'head' variable to it. This
# makes it the new head of the list.
self.move_to_front(node)
self.head = node
# Return the value.
return node.value
def get(self, key, default=None):
"""Get an item - return default (None) if not present"""
try:
return self[key]
except KeyError:
return default
def set(self, key, value):
self[key] = value
def __setitem__(self, key, value):
# First, see if any value is stored under 'key' in the cache already.
# If so we are going to replace that value with the new one.
node = self.table.get(key)
if node is not None:
# Replace the value.
node.value = value
# Update the list ordering.
self.move_to_front(node)
self.head = node
return
# Ok, no value is currently stored under 'key' in the cache. We need
# to choose a node to place the new item in. There are two cases. If
# the cache is full some item will have to be pushed out of the
# cache. We want to choose the node with the least recently used
# item. This is the node at the tail of the list. If the cache is not
# full we want to choose a node that is empty. Because of the way the
# list is managed, the empty nodes are always together at the tail
# end of the list. Thus, in either case, by chooseing the node at the
# tail of the list our conditions are satisfied.
# Since the list is circular, the tail node directly preceeds the
# 'head' node.
node = self.head.prev
# If the node already contains something we need to remove the old
# key from the dictionary.
if not node.empty:
if self.callback is not None:
self.callback(node.key, node.value)
del self.table[node.key]
# Place the new key and value in the node
node.empty = False
node.key = key
node.value = value
# Add the node to the dictionary under the new key.
self.table[key] = node
# We need to move the node to the head of the list. The node is the
# tail node, so it directly preceeds the head node due to the list
# being circular. Therefore, the ordering is already correct, we just
# need to adjust the 'head' variable.
self.head = node
def __delitem__(self, key):
# Lookup the node, then remove it from the hash table.
node = self.table[key]
del self.table[key]
node.empty = True
# Not strictly necessary.
node.key = None
node.value = None
# Because this node is now empty we want to reuse it before any
# non-empty node. To do that we want to move it to the tail of the
# list. We move it so that it directly precedes the 'head' node. This
# makes it the tail node. The 'head' is then adjusted. This
# adjustment ensures correctness even for the case where the 'node'
# is the 'head' node.
self.move_to_front(node)
self.head = node.next
def __iter__(self):
''' Return an iterator that returns the keys in the cache in order from
the most recently to least recently used. Does not modify the cache
order. '''
node = self.head
left = len(self.table)
while left > 0:
left -= 1
yield node
node = node.next
def items(self):
''' Return an iterator that returns the (key, value) pairs in the cache
in order from the most recently to least recently used. Does not
modify the cache order. '''
for node in self:
yield node.key, node.value
iteritems = items
def keys(self):
''' Return an iterator that returns the keys in the cache in order from
the most recently to least recently used. Does not modify the cache
order. '''
for node in self:
yield node.key
iterkeys = keys
def values(self):
''' Return an iterator that returns the values in the cache in order
from the most recently to least recently used. Does not modify the
cache order. '''
for node in self:
yield node.value
itervalues = values
@property
def size(self):
' The current cache size '
return self._size
@size.setter
def size(self, size):
' Change the current cache size, the value is auto-clamped to [1, *] '
size = max(1, size)
if size > self._size:
self.expand(size - self._size)
elif size < self._size:
self.shrink(self._size - size)
# Internal API {{{
def expand(self, n):
' Increases the size of the cache by inserting n empty nodes at the tail of the list '
n = max(0, n)
left = n
while left > 0:
left -= 1
node = DoublyLinkedNode()
node.next = self.head
node.prev = self.head.prev
self.head.prev.next = node
self.head.prev = node
self._size += n
def shrink(self, n):
' Decreases the size of the cache by removing n nodes from the tail of the list '
n = max(0, min(n, self._size - 1))
left = n
while left > 0:
left -= 1
node = self.head.prev
if not node.empty:
if self.callback is not None:
self.callback(node.key, node.value)
del self.table[node.key]
# Splice the tail node out of the list
self.head.prev = node.prev
node.prev.next = self.head
# The next four lines are not strictly necessary.
node.prev = None
node.next = None
node.key = None
node.value = None
self._size -= n
def move_to_front(self, node):
''' This method adjusts the ordering of the doubly linked list so that
'node' directly precedes the 'head' node. Because of the order of
operations, if 'node' already directly precedes the 'head' node or if
'node' is the 'head' node the order of the list will be unchanged. '''
node.prev.next = node.next
node.next.prev = node.prev
node.prev = self.head.prev
node.next = self.head.prev.next
node.next.prev = node
node.prev.next = node
# }}}