package com.limegroup.gnutella.io;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
public class HeapByteBufferCache {
private final TreeMap /* Integer -> List<ByteBuffer> */ CACHE = new TreeMap();
private final Map /* ByteBuffer -> ByteBuffer */ SLICED = new IdentityHashMap();
public ByteBuffer get() {
return get(8192);
}
public synchronized ByteBuffer get(int size) {
// trivial case - cache is empty
if (CACHE.isEmpty()) {
ByteBuffer buf = ByteBuffer.allocate(size);
return buf;
}
// if not, see if we have a buffer of the exact size
Integer key = new Integer(size);
List l = (List) CACHE.get(key);
// if yes, return it.
if (l != null) {
if (l.isEmpty())
return ByteBuffer.allocate(size);
return (ByteBuffer) l.remove(l.size() - 1);
}
// if not, try to get the next largest buffer
// are there any larger buffers than this one at all?
// if not, just allocate a new one
Integer largest = (Integer) CACHE.lastKey();
if (largest.compareTo(key) < 0)
return ByteBuffer.allocate(size);
ByteBuffer larger = null;
// otherwise find the next largest
// note: we could shorten the iteration using a tailMap, but its unclear if
// creating a new object is justified.
for (Iterator iter = CACHE.entrySet().iterator();iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
if (key.compareTo((Integer)entry.getKey()) > 0)
continue;
l = (List) entry.getValue();
if (!l.isEmpty()) {
larger = (ByteBuffer) l.remove(l.size() - 1);
break;
}
}
// still no larger buffer?
if (larger == null)
return ByteBuffer.allocate(size);
// slice!
larger.limit(size);
ByteBuffer ret = larger.slice();
SLICED.put(ret, larger);
return ret;
}
public synchronized void put(ByteBuffer buf) {
// see if this buffer was sliced off of a bigger one
ByteBuffer toReturn = (ByteBuffer) SLICED.remove(buf);
if (toReturn == null)
toReturn = buf;
toReturn.clear();
Integer size = new Integer(toReturn.capacity());
List l = (List) CACHE.get(size);
if (l == null) {
l = new ArrayList(1);
CACHE.put(size, l);
}
l.add(toReturn);
}
public synchronized void clear() {
CACHE.clear();
// keep the SCLICED mappings around to allow returning of sliced buffers.
}
}