package org.limewire.collection; import java.util.Stack; /** * A cache of byte[]. Sets a limit to the number of byte arrays that can be * created. <code>ByteArrayCache</code> has methods to create and release the * byte arrays, as well as check the size (total size, remaining size, and byte * array size). * <p> * When the maximum number of byte arrays are created ({@link #getCreated()} == * {@link #getMaxSize()}), attempts to get a new byte array ({@link #get()}) * block until a byte array is freed ({@link #release(byte[])}). <pre> ByteArrayCache bac = new ByteArrayCache(3, 2); byte[] b1 = bac.getQuick(); byte[] b2 = bac.getQuick(); for(int i = 0; i < b1.length; i++) b1[i] = 1; System.arraycopy(b1, 0, b2, 0, b1.length); for(int i = 0; i < b2.length; i++) System.out.println("b2[" + i + "]: " + b2[i]); Output: b2[0]: 1 b2[1]: 1 </pre> * */ public class ByteArrayCache { /** Default # of byte[]'s to hold. */ private static final int DEFAULT_SIZE = 512; /** Default length of returned bytes. */ private static final int DEFAULT_LENGTH = 1024; /** Holds cached byte[]'s for future re-use. */ private final Stack<byte[]> CACHE = new Stack<byte[]>(); /** The total size of the bytes stored in the cache. */ private int _totalSize; /** The number of byte[]'s this cache has created that haven't been erased. */ private int _numCreated; /** The number of byte[]'s to create before blocking when the next one is gotten. */ private final int _maxSize; /** The length of buffers to create. */ private final int _length; /** Constructs a new ByteArraCache using the default size of 512 & length of 1024. */ public ByteArrayCache() { this(DEFAULT_SIZE, DEFAULT_LENGTH); } /** Constructs a new ByteArrayCache using the given maxSize & length. */ public ByteArrayCache(int maxSize, int length) { _maxSize = maxSize; _length = length; assert _length > 0 : "constructing cache of emtpy arrays "+_length; } /** Returns the size of all cached bytes. */ public synchronized int getCacheSize() { return _totalSize; } /** * Attempts to retrieve a new byte[]. * If the cache is empty and getCreated() is >= getMaxSize(), this will * block until a byte[] is returned to the cache. */ public synchronized byte[] get() throws InterruptedException { while(true) { if (!CACHE.isEmpty()) { byte[] b = CACHE.pop(); _totalSize -= b.length; return b; } else if (_numCreated < _maxSize) { _numCreated++; return new byte[_length]; } else { wait(); } } } /** * Attempts to retrieve a new byte[]. If the cache is empty and getCreated() * is >= getMaxSize(), this will return null. */ public synchronized byte[] getQuick() { if (!CACHE.isEmpty()) { byte[] b = CACHE.pop(); _totalSize -= b.length; return b; } else if (_numCreated < _maxSize) { _numCreated++; return new byte[_length]; } else { return null; } } /** * Returns a byte[] to this cache. * The byte[] MUST HAVE BEEN A byte[] RETURNED FROM get(). */ public synchronized void release(byte[] data) { assert data.length == _length : "illegal length: " + data.length; _totalSize += data.length; CACHE.ensureCapacity(_maxSize); CACHE.push(data); notifyAll(); } /** Clears all items in the cache. */ public synchronized void clear() { _numCreated -= CACHE.size(); _totalSize = 0; CACHE.clear(); notifyAll(); } /** Determines if there's space in the cache for another byte[]. */ public synchronized boolean isBufferAvailable() { return !CACHE.isEmpty() || _numCreated < _maxSize; } /** Returns the number of byte[]'s this cache has created. */ public synchronized int getCreated() { return _numCreated; } /** Returns the length of the byte[]'s this creates. */ public int getLength() { return _length; } /** Returns the maximum number of byte[]'s this will create. */ public int getMaxSize() { return _maxSize; } }