package net.i2p.util;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Like ByteCache but works directly with byte arrays, not ByteArrays.
* These are designed to be small caches, so there's no cleaner task
* like there is in ByteCache. And we don't zero out the arrays here.
* Only the static methods are public here.
*
* @since 0.8.3
*/
public final class SimpleByteCache {
private static final ConcurrentHashMap<Integer, SimpleByteCache> _caches = new ConcurrentHashMap<Integer, SimpleByteCache>(8);
private static final int DEFAULT_SIZE = 64;
/** up to this, use ABQ to minimize object churn and for performance; above this, use LBQ for two locks */
private static final int MAX_FOR_ABQ = 64;
/**
* Get a cache responsible for arrays of the given size
*
* @param size how large should the objects cached be?
*/
public static SimpleByteCache getInstance(int size) {
return getInstance(DEFAULT_SIZE, size);
}
/**
* Get a cache responsible for objects of the given size
*
* @param cacheSize how large we want the cache to grow
* (number of objects, NOT memory size)
* before discarding released objects.
* @param size how large should the objects cached be?
*/
public static SimpleByteCache getInstance(int cacheSize, int size) {
Integer sz = Integer.valueOf(size);
SimpleByteCache cache = _caches.get(sz);
if (cache == null) {
cache = new SimpleByteCache(cacheSize, size);
SimpleByteCache old = _caches.putIfAbsent(sz, cache);
if (old != null)
cache = old;
}
cache.resize(cacheSize);
return cache;
}
/**
* Clear everything (memory pressure)
*/
public static void clearAll() {
for (SimpleByteCache bc : _caches.values())
bc.clear();
}
/** list of available and available entries */
private Queue<byte[]> _available;
private int _maxCached;
private final int _entrySize;
private SimpleByteCache(int maxCachedEntries, int entrySize) {
_maxCached = maxCachedEntries;
_available = createQueue();
_entrySize = entrySize;
}
private void resize(int maxCachedEntries) {
if (_maxCached >= maxCachedEntries) return;
_maxCached = maxCachedEntries;
// make a bigger one, move the cached items over
Queue<byte[]> newLBQ = createQueue();
byte[] ba;
while ((ba = _available.poll()) != null)
newLBQ.offer(ba);
_available = newLBQ;
}
/**
* @return LBQ or ABQ
* @since 0.9.2
*/
private Queue<byte[]> createQueue() {
if (_entrySize <= MAX_FOR_ABQ)
return new ArrayBlockingQueue<byte[]>(_maxCached);
return new LinkedBlockingQueue<byte[]>(_maxCached);
}
/**
* Get the next available array, either from the cache or a brand new one
*/
public static byte[] acquire(int size) {
return getInstance(size).acquire();
}
/**
* Get the next available array, either from the cache or a brand new one
*/
private byte[] acquire() {
byte[] rv = _available.poll();
if (rv == null)
rv = new byte[_entrySize];
return rv;
}
/**
* Put this array back onto the available cache for reuse
*/
public static void release(byte[] entry) {
SimpleByteCache cache = _caches.get(entry.length);
if (cache != null)
cache.releaseIt(entry);
}
/**
* Put this array back onto the available cache for reuse
*/
private void releaseIt(byte[] entry) {
if (entry == null || entry.length != _entrySize)
return;
// should be safe without this
//Arrays.fill(entry, (byte) 0);
_available.offer(entry);
}
/**
* Clear everything (memory pressure)
*/
private void clear() {
_available.clear();
}
}