/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package com.db4o.internal.caching;
import java.util.*;
import com.db4o.foundation.*;
/**
* @exclude
* Full version of the algorithm taken from here:
* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2641
*/
class LRU2QXCache<K,V> implements Cache4<K,V> {
private final Map<K,V> _slots;
private final CircularBuffer4<K> _am; // 'eden': long-term lru queue
private final CircularBuffer4<K> _a1in; // 'nursery': short-term fifo queue, entry point for all new items
private final CircularBuffer4<K> _a1out; // 'backlog': fifo queue, elements may not be backed in _slots or may overlap with _am
private final int _maxSize; // invariant: |_slots| = |_am| + |_a1in| <= _maxSize
private final int _inSize;
public LRU2QXCache(int maxSize) {
_maxSize = maxSize;
_inSize = _maxSize / 4;
_slots = new HashMap<K,V>(_maxSize);
_am = new CircularBuffer4<K>(_maxSize);
_a1in = new CircularBuffer4<K>(_maxSize);
_a1out = new CircularBuffer4<K>(_maxSize / 2);
}
public V produce(K key, Function4<K,V> producer, Procedure4<V> finalizer) {
if(key == null){
throw new ArgumentNullException();
}
if(_am.remove(key)) {
_am.addFirst(key);
return _slots.get(key);
}
if(_a1out.contains(key)) {
reclaimFor(key, producer, finalizer);
_am.addFirst(key);
return _slots.get(key);
}
if(_a1in.contains(key)) {
return _slots.get(key);
}
reclaimFor(key, producer, finalizer);
_a1in.addFirst(key);
return _slots.get(key);
}
private void reclaimFor(K key, Function4<K,V> producer, Procedure4<V> finalizer) {
if(_slots.size() < _maxSize) {
_slots.put(key, producer.apply(key));
return;
}
if(_a1in.size() > _inSize) {
K lastKey = _a1in.removeLast();
discard(lastKey, finalizer);
if(_a1out.isFull()) {
_a1out.removeLast();
}
_a1out.addFirst(lastKey);
} else {
K lastKey = _am.removeLast();
discard(lastKey, finalizer);
}
_slots.put(key, producer.apply(key));
}
public Iterator iterator() {
return _slots.values().iterator();
}
public String toString() {
return "LRU2QXCache(am=" + toString(_am) + ", a1in=" + toString(_a1in) + ", a1out=" + toString(_a1out) + ")" + " - " + _slots.size();
}
private void discard(K key, Procedure4<V> finalizer) {
V removed = _slots.remove(key);
if(finalizer != null) {
finalizer.apply(removed);
}
}
private String toString(Iterable4<K> buffer) {
return Iterators.toString(buffer);
}
}