package com.fasterxml.jackson.databind.util; /** * Base class for specialized primitive array builders. */ public abstract class PrimitiveArrayBuilder<T> { /** * 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 T _freeBuffer; Node<T> _bufferHead; Node<T> _bufferTail; /** * Number of total buffered entries in this buffer, counting all instances * within linked list formed by following {@link #_bufferHead}. */ int _bufferedEntryCount; // // // Recycled instances of sub-classes // // // Life-cycle protected PrimitiveArrayBuilder() { } /* //////////////////////////////////////////////////////////////////////// // Public API //////////////////////////////////////////////////////////////////////// */ public T resetAndStart() { _reset(); return (_freeBuffer == null) ? _constructArray(INITIAL_CHUNK_SIZE) : _freeBuffer; } /** * @return Length of the next chunk to allocate */ public final T appendCompletedChunk(T fullChunk, int fullChunkLength) { Node<T> next = new Node<T>(fullChunk, fullChunkLength); if (_bufferHead == null) { // first chunk _bufferHead = _bufferTail = next; } else { // have something already _bufferTail.linkNext(next); _bufferTail = next; } _bufferedEntryCount += fullChunkLength; int nextLen = fullChunkLength; // start with last chunk size // double the size for small chunks if (nextLen < SMALL_CHUNK_SIZE) { nextLen += nextLen; } else { // but by +25% for larger (to limit overhead) nextLen += (nextLen >> 2); } return _constructArray(nextLen); } public T completeAndClearBuffer(T lastChunk, int lastChunkEntries) { int totalSize = lastChunkEntries + _bufferedEntryCount; T resultArray = _constructArray(totalSize); int ptr = 0; for (Node<T> n = _bufferHead; n != null; n = n.next()) { ptr = n.copyData(resultArray, ptr); } System.arraycopy(lastChunk, 0, resultArray, ptr, lastChunkEntries); ptr += lastChunkEntries; // sanity check (could have failed earlier due to out-of-bounds, too) if (ptr != totalSize) { throw new IllegalStateException("Should have gotten "+totalSize+" entries, got "+ptr); } return resultArray; } /* //////////////////////////////////////////////////////////////////////// // Abstract methods for sub-classes to implement //////////////////////////////////////////////////////////////////////// */ protected abstract T _constructArray(int len); /* //////////////////////////////////////////////////////////////////////// // Internal methods //////////////////////////////////////////////////////////////////////// */ protected void _reset() { // can we reuse the last (and thereby biggest) array for next time? if (_bufferTail != null) { _freeBuffer = _bufferTail.getData(); } // either way, must discard current contents _bufferHead = _bufferTail = null; _bufferedEntryCount = 0; } /* //////////////////////////////////////////////////////////////////////// // Helper classes //////////////////////////////////////////////////////////////////////// */ /** * For actual buffering beyond the current buffer, we can actually * use shared class which only deals with opaque "untyped" chunks. * This works because {@link java.lang.System#arraycopy} does not * take type; hence we can implement some aspects of primitive data * handling in generic fashion. */ final static class Node<T> { /** * Data stored in this node. */ final T _data; /** * Number entries in the (untyped) array. Offset is assumed to be 0. */ final int _dataLength; Node<T> _next; public Node(T data, int dataLen) { _data = data; _dataLength = dataLen; } public T getData() { return _data; } public int copyData(T dst, int ptr) { System.arraycopy(_data, 0, dst, ptr, _dataLength); ptr += _dataLength; return ptr; } public Node<T> next() { return _next; } public void linkNext(Node<T> next) { if (_next != null) { // sanity check throw new IllegalStateException(); } _next = next; } } }