mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Remove unused code
This commit is contained in:
parent
ba70f43118
commit
7fd70a28e8
@ -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
|
|
||||||
|
|
||||||
# }}}
|
|
Loading…
x
Reference in New Issue
Block a user