blob: c89c66c49640c3d670de0991a57306bb926caa57 [file] [log] [blame]
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.mime4j.util;
import java.util.ArrayList;
import java.util.Optional;
/**
* This is a small utility class, whose main functionality is to allow
* simple reuse of raw byte/char buffers. It is usually used through
* <code>ThreadLocal</code> member of the owning class pointing to
* instance of this class through a <code>SoftReference</code>. The
* end result is a low-overhead GC-cleanable recycling: hopefully
* ideal for use by stream readers.
*<p>
* Rewritten in 2.10 to be thread-safe (see [jackson-core#479] for details),
* to not rely on {@code ThreadLocal} access.
*/
public class BufferRecycler {
static final boolean ENABLED = Optional.ofNullable(System.getProperty("james.mime4j.buffer.recycling.enabled"))
.map(Boolean::parseBoolean)
.orElse(true);
protected final ArrayList<byte[]>[] _byteBuffers;
protected final ArrayList<char[]>[] _charBuffers;
protected final ArrayList<int[]> _intBuffers;
/**
* Default constructor used for creating instances of this default
* implementation.
*/
public BufferRecycler() {
this(4, 4);
}
/**
* Alternate constructor to be used by sub-classes, to allow customization
* of number of low-level buffers in use.
*
* @param bbCount Number of {@code byte[]} buffers to allocate
* @param cbCount Number of {@code char[]} buffers to allocate
*
* @since 2.4
*/
protected BufferRecycler(int bbCount, int cbCount) {
_byteBuffers = new ArrayList[bbCount];
for (int i = 0; i < bbCount; i++) {
_byteBuffers[i] = new ArrayList<>();
}
_charBuffers = new ArrayList[cbCount];
for (int i = 0; i < cbCount; i++) {
_charBuffers[i] = new ArrayList<>();
}
_intBuffers = new ArrayList<>();
}
/**
* @param ix One of <code>READ_IO_BUFFER</code> constants.
*
* @return Buffer allocated (possibly recycled)
*/
public final byte[] allocByteBuffer(int ix) {
return allocByteBuffer(ix, 0);
}
public final int[] allocintBuffer(int minSize) {
final int DEF_SIZE = 256;
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
final ArrayList<int[]> buffers = _intBuffers;
int[] buffer = null;
if (buffers.size() > 0) {
buffer = buffers.remove(buffers.size() -1);
}
if (buffer == null || buffer.length < minSize) {
buffer = new int[minSize];
}
return buffer;
}
public byte[] allocByteBuffer(int ix, int minSize) {
final int DEF_SIZE = 4000;
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
final ArrayList<byte[]> buffers = _byteBuffers[ix];
byte[] buffer = null;
if (buffers.size() > 0) {
buffer = buffers.remove(buffers.size() -1);
}
if (buffer == null || buffer.length < minSize) {
buffer = balloc(minSize);
}
return buffer;
}
public void releaseByteBuffer(int ix, byte[] buffer) {
if (buffer == null) {
return;
}
_byteBuffers[ix].add(buffer);
}
public void releaseIntBuffer(int[] buffer) {
if (buffer == null) {
return;
}
if (ENABLED) {
_intBuffers.add(buffer);
}
}
public final char[] allocCharBuffer(int ix) {
return allocCharBuffer(ix, 0);
}
public char[] allocCharBuffer(int ix, int minSize) {
final int DEF_SIZE = 4000;
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
final ArrayList<char[]> buffers = _charBuffers[ix];
char[] buffer = null;
if (buffers.size() > 0) {
buffer = buffers.remove(buffers.size() -1);
}
if (buffer == null || buffer.length < minSize) {
buffer = calloc(minSize);
}
return buffer;
}
public void releaseCharBuffer(int ix, char[] buffer) {
if (ENABLED) {
_charBuffers[ix].add(buffer);
}
}
protected byte[] balloc(int size) {
return new byte[size];
}
protected char[] calloc(int size) {
return new char[size];
}
}