package sh.calaba.org.codehaus.jackson.map.util; import java.lang.reflect.Array; import java.util.List; /** * Helper class to use for constructing Object arrays by appending entries * to create arrays of various lengths (length that is not known a priori). */ public final class ObjectBuffer { // // // Config constants /** * Let's start with small chunks; typical usage is for small arrays anyway. */ final static int INITIAL_CHUNK_SIZE = 12; /** * Also: let's expand by doubling up until 64k chunks (which is 16k entries for * 32-bit machines) */ final static int SMALL_CHUNK_SIZE = (1 << 14); /** * Let's limit maximum size of chunks we use; helps avoid excessive allocation * overhead for huge data sets. * For now, let's limit to quarter million entries, 1 meg chunks for 32-bit * machines. */ final static int MAX_CHUNK_SIZE = (1 << 18); // // // Data storage private Node _bufferHead; private Node _bufferTail; /** * Number of total buffered entries in this buffer, counting all instances * within linked list formed by following {@link #_bufferHead}. */ private int _bufferedEntryCount; // // // Simple reuse /** * Reusable Object array, stored here after buffer has been released having * been used previously. */ private Object[] _freeBuffer; /* /********************************************************** /* Construction /********************************************************** */ public ObjectBuffer() { } /* /********************************************************** /* Public API /********************************************************** */ /** * Method called to start buffering process. Will ensure that the buffer * is empty, and then return an object array to start chunking content on */ public Object[] resetAndStart() { _reset(); if (_freeBuffer == null) { return new Object[INITIAL_CHUNK_SIZE]; } return _freeBuffer; } /** * Method called to add a full Object array as a chunk buffered within * this buffer, and to obtain a new array to fill. Caller is not to use * the array it gives; but to use the returned array for continued * buffering. * * @param fullChunk Completed chunk that the caller is requesting * to append to this buffer. It is generally chunk that was * returned by an earlier call to {@link #resetAndStart} or * {@link #appendCompletedChunk} (although this is not required or * enforced) * * @return New chunk buffer for caller to fill */ public Object[] appendCompletedChunk(Object[] fullChunk) { Node next = new Node(fullChunk); if (_bufferHead == null) { // first chunk _bufferHead = _bufferTail = next; } else { // have something already _bufferTail.linkNext(next); _bufferTail = next; } int len = fullChunk.length; _bufferedEntryCount += len; // double the size for small chunks if (len < SMALL_CHUNK_SIZE) { len += len; } else { // but by +25% for larger (to limit overhead) len += (len >> 2); } return new Object[len]; } /** * Method called to indicate that the buffering process is now * complete; and to construct a combined exactly-sized result * array. Additionally the buffer itself will be reset to * reduce memory retention. *
* Resulting array will be of generic Object[]
type:
* if a typed array is needed, use the method with additional
* type argument.
*/
public Object[] completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries)
{
int totalSize = lastChunkEntries + _bufferedEntryCount;
Object[] result = new Object[totalSize];
_copyTo(result, totalSize, lastChunk, lastChunkEntries);
return result;
}
/**
* Type-safe alternative to
* {@link #completeAndClearBuffer(Object[], int)}, to allow
* for constructing explicitly typed result array.
*
* @param componentType Type of elements included in the buffer. Will be
* used for constructing the result array.
*/
public