package com.fasterxml.jackson.databind.util;

Base class for specialized primitive array builders.
/** * 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.
/** * 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)
/** * 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.
/** * 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 protected T _freeBuffer; protected Node<T> _bufferHead; protected Node<T> _bufferTail;
Number of total buffered entries in this buffer, counting all instances within linked list formed by following PrimitiveArrayBuilder<T>._bufferHead.
/** * Number of total buffered entries in this buffer, counting all instances * within linked list formed by following {@link #_bufferHead}. */
protected int _bufferedEntryCount; // // // Recycled instances of sub-classes // // // Life-cycle protected PrimitiveArrayBuilder() { } /* /********************************************************** /* Public API /********************************************************** */ public int bufferedSize() { return _bufferedEntryCount; } public T resetAndStart() { _reset(); return (_freeBuffer == null) ? _constructArray(INITIAL_CHUNK_SIZE) : _freeBuffer; }
Returns:Length of the next chunk to allocate
/** * @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 System.arraycopy does not take type; hence we can implement some aspects of primitive data handling in generic fashion.
/** * 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.
/** * Data stored in this node. */
final T _data;
Number entries in the (untyped) array. Offset is assumed to be 0.
/** * 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; } } }