/*
* Copyright 2012 The Netty Project
*
* The Netty Project 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 io.netty.buffer;
import io.netty.util.
ResourceLeakDetector;
import io.netty.util.
ResourceLeakTracker;
import io.netty.util.internal.
PlatformDependent;
import io.netty.util.internal.
StringUtil;
/**
* Skeletal {@link ByteBufAllocator} implementation to extend.
*/
public abstract class
AbstractByteBufAllocator implements
ByteBufAllocator {
static final int
DEFAULT_INITIAL_CAPACITY = 256;
static final int
DEFAULT_MAX_CAPACITY =
Integer.
MAX_VALUE;
static final int
DEFAULT_MAX_COMPONENTS = 16;
static final int
CALCULATE_THRESHOLD = 1048576 * 4; // 4 MiB page
static {
ResourceLeakDetector.
addExclusions(
AbstractByteBufAllocator.class, "toLeakAwareBuffer");
}
protected static
ByteBuf toLeakAwareBuffer(
ByteBuf buf) {
ResourceLeakTracker<
ByteBuf>
leak;
switch (
ResourceLeakDetector.
getLevel()) {
case
SIMPLE:
leak =
AbstractByteBuf.
leakDetector.
track(
buf);
if (
leak != null) {
buf = new
SimpleLeakAwareByteBuf(
buf,
leak);
}
break;
case
ADVANCED:
case
PARANOID:
leak =
AbstractByteBuf.
leakDetector.
track(
buf);
if (
leak != null) {
buf = new
AdvancedLeakAwareByteBuf(
buf,
leak);
}
break;
default:
break;
}
return
buf;
}
protected static
CompositeByteBuf toLeakAwareBuffer(
CompositeByteBuf buf) {
ResourceLeakTracker<
ByteBuf>
leak;
switch (
ResourceLeakDetector.
getLevel()) {
case
SIMPLE:
leak =
AbstractByteBuf.
leakDetector.
track(
buf);
if (
leak != null) {
buf = new
SimpleLeakAwareCompositeByteBuf(
buf,
leak);
}
break;
case
ADVANCED:
case
PARANOID:
leak =
AbstractByteBuf.
leakDetector.
track(
buf);
if (
leak != null) {
buf = new
AdvancedLeakAwareCompositeByteBuf(
buf,
leak);
}
break;
default:
break;
}
return
buf;
}
private final boolean
directByDefault;
private final
ByteBuf emptyBuf;
/**
* Instance use heap buffers by default
*/
protected
AbstractByteBufAllocator() {
this(false);
}
/**
* Create new instance
*
* @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
* a heap buffer
*/
protected
AbstractByteBufAllocator(boolean
preferDirect) {
directByDefault =
preferDirect &&
PlatformDependent.
hasUnsafe();
emptyBuf = new
EmptyByteBuf(this);
}
@
Override
public
ByteBuf buffer() {
if (
directByDefault) {
return
directBuffer();
}
return
heapBuffer();
}
@
Override
public
ByteBuf buffer(int
initialCapacity) {
if (
directByDefault) {
return
directBuffer(
initialCapacity);
}
return
heapBuffer(
initialCapacity);
}
@
Override
public
ByteBuf buffer(int
initialCapacity, int
maxCapacity) {
if (
directByDefault) {
return
directBuffer(
initialCapacity,
maxCapacity);
}
return
heapBuffer(
initialCapacity,
maxCapacity);
}
@
Override
public
ByteBuf ioBuffer() {
if (
PlatformDependent.
hasUnsafe()) {
return
directBuffer(
DEFAULT_INITIAL_CAPACITY);
}
return
heapBuffer(
DEFAULT_INITIAL_CAPACITY);
}
@
Override
public
ByteBuf ioBuffer(int
initialCapacity) {
if (
PlatformDependent.
hasUnsafe()) {
return
directBuffer(
initialCapacity);
}
return
heapBuffer(
initialCapacity);
}
@
Override
public
ByteBuf ioBuffer(int
initialCapacity, int
maxCapacity) {
if (
PlatformDependent.
hasUnsafe()) {
return
directBuffer(
initialCapacity,
maxCapacity);
}
return
heapBuffer(
initialCapacity,
maxCapacity);
}
@
Override
public
ByteBuf heapBuffer() {
return
heapBuffer(
DEFAULT_INITIAL_CAPACITY,
DEFAULT_MAX_CAPACITY);
}
@
Override
public
ByteBuf heapBuffer(int
initialCapacity) {
return
heapBuffer(
initialCapacity,
DEFAULT_MAX_CAPACITY);
}
@
Override
public
ByteBuf heapBuffer(int
initialCapacity, int
maxCapacity) {
if (
initialCapacity == 0 &&
maxCapacity == 0) {
return
emptyBuf;
}
validate(
initialCapacity,
maxCapacity);
return
newHeapBuffer(
initialCapacity,
maxCapacity);
}
@
Override
public
ByteBuf directBuffer() {
return
directBuffer(
DEFAULT_INITIAL_CAPACITY,
DEFAULT_MAX_CAPACITY);
}
@
Override
public
ByteBuf directBuffer(int
initialCapacity) {
return
directBuffer(
initialCapacity,
DEFAULT_MAX_CAPACITY);
}
@
Override
public
ByteBuf directBuffer(int
initialCapacity, int
maxCapacity) {
if (
initialCapacity == 0 &&
maxCapacity == 0) {
return
emptyBuf;
}
validate(
initialCapacity,
maxCapacity);
return
newDirectBuffer(
initialCapacity,
maxCapacity);
}
@
Override
public
CompositeByteBuf compositeBuffer() {
if (
directByDefault) {
return
compositeDirectBuffer();
}
return
compositeHeapBuffer();
}
@
Override
public
CompositeByteBuf compositeBuffer(int
maxNumComponents) {
if (
directByDefault) {
return
compositeDirectBuffer(
maxNumComponents);
}
return
compositeHeapBuffer(
maxNumComponents);
}
@
Override
public
CompositeByteBuf compositeHeapBuffer() {
return
compositeHeapBuffer(
DEFAULT_MAX_COMPONENTS);
}
@
Override
public
CompositeByteBuf compositeHeapBuffer(int
maxNumComponents) {
return
toLeakAwareBuffer(new
CompositeByteBuf(this, false,
maxNumComponents));
}
@
Override
public
CompositeByteBuf compositeDirectBuffer() {
return
compositeDirectBuffer(
DEFAULT_MAX_COMPONENTS);
}
@
Override
public
CompositeByteBuf compositeDirectBuffer(int
maxNumComponents) {
return
toLeakAwareBuffer(new
CompositeByteBuf(this, true,
maxNumComponents));
}
private static void
validate(int
initialCapacity, int
maxCapacity) {
if (
initialCapacity < 0) {
throw new
IllegalArgumentException("initialCapacity: " +
initialCapacity + " (expected: 0+)");
}
if (
initialCapacity >
maxCapacity) {
throw new
IllegalArgumentException(
String.
format(
"initialCapacity: %d (expected: not greater than maxCapacity(%d)",
initialCapacity,
maxCapacity));
}
}
/**
* Create a heap {@link ByteBuf} with the given initialCapacity and maxCapacity.
*/
protected abstract
ByteBuf newHeapBuffer(int
initialCapacity, int
maxCapacity);
/**
* Create a direct {@link ByteBuf} with the given initialCapacity and maxCapacity.
*/
protected abstract
ByteBuf newDirectBuffer(int
initialCapacity, int
maxCapacity);
@
Override
public
String toString() {
return
StringUtil.
simpleClassName(this) + "(directByDefault: " +
directByDefault + ')';
}
@
Override
public int
calculateNewCapacity(int
minNewCapacity, int
maxCapacity) {
if (
minNewCapacity < 0) {
throw new
IllegalArgumentException("minNewCapacity: " +
minNewCapacity + " (expected: 0+)");
}
if (
minNewCapacity >
maxCapacity) {
throw new
IllegalArgumentException(
String.
format(
"minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
minNewCapacity,
maxCapacity));
}
final int
threshold =
CALCULATE_THRESHOLD; // 4 MiB page
if (
minNewCapacity ==
threshold) {
return
threshold;
}
// If over threshold, do not double but just increase by threshold.
if (
minNewCapacity >
threshold) {
int
newCapacity =
minNewCapacity /
threshold *
threshold;
if (
newCapacity >
maxCapacity -
threshold) {
newCapacity =
maxCapacity;
} else {
newCapacity +=
threshold;
}
return
newCapacity;
}
// Not over threshold. Double up to 4 MiB, starting from 64.
int
newCapacity = 64;
while (
newCapacity <
minNewCapacity) {
newCapacity <<= 1;
}
return
Math.
min(
newCapacity,
maxCapacity);
}
}