Avoid allocations when wrapping byte[] and ByteBuffer arrays as ByteBuf (#8420)
Motivation: Unpooled.wrap(byte[]...) and Unpooled.wrap(ByteBuffer...) currently allocate/copy an intermediate ByteBuf ArrayList and array, which can be avoided. Modifications: - Define new internal ByteWrapper interface and add a CompositeByteBuf constructor which takes a ByteWrapper with an array of the type that it wraps, and modify the appropriate Unpooled.wrap(...) methods to take advantage of it - Tidy up other constructors in CompositeByteBuf to remove duplication and misleading len arg (which is really an end offset into provided array) Result: Less allocation/copying when wrapping byte[] and ByteBuffer arrays, tidier code.
This commit is contained in:
parent
52699bd6dd
commit
44cca1a26f
@ -53,65 +53,85 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
|
|||||||
|
|
||||||
private boolean freed;
|
private boolean freed;
|
||||||
|
|
||||||
public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) {
|
private CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, int initSize) {
|
||||||
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
|
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
|
||||||
if (alloc == null) {
|
if (alloc == null) {
|
||||||
throw new NullPointerException("alloc");
|
throw new NullPointerException("alloc");
|
||||||
}
|
}
|
||||||
|
if (maxNumComponents < 2) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
|
||||||
|
}
|
||||||
this.alloc = alloc;
|
this.alloc = alloc;
|
||||||
this.direct = direct;
|
this.direct = direct;
|
||||||
this.maxNumComponents = maxNumComponents;
|
this.maxNumComponents = maxNumComponents;
|
||||||
components = newList(0, maxNumComponents);
|
components = newList(initSize, maxNumComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) {
|
||||||
|
this(alloc, direct, maxNumComponents, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) {
|
public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) {
|
||||||
this(alloc, direct, maxNumComponents, buffers, 0, buffers.length);
|
this(alloc, direct, maxNumComponents, buffers, 0, buffers.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositeByteBuf(
|
CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents,
|
||||||
ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf[] buffers, int offset, int len) {
|
ByteBuf[] buffers, int offset, int endOffset) {
|
||||||
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
|
this(alloc, direct, maxNumComponents, endOffset - offset);
|
||||||
if (alloc == null) {
|
|
||||||
throw new NullPointerException("alloc");
|
|
||||||
}
|
|
||||||
if (maxNumComponents < 2) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.alloc = alloc;
|
addComponents0(false, 0, buffers, offset, endOffset);
|
||||||
this.direct = direct;
|
|
||||||
this.maxNumComponents = maxNumComponents;
|
|
||||||
components = newList(len, maxNumComponents);
|
|
||||||
|
|
||||||
addComponents0(false, 0, buffers, offset, len);
|
|
||||||
consolidateIfNeeded();
|
consolidateIfNeeded();
|
||||||
setIndex(0, capacity());
|
setIndex(0, capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompositeByteBuf(
|
public CompositeByteBuf(
|
||||||
ByteBufAllocator alloc, boolean direct, int maxNumComponents, Iterable<ByteBuf> buffers) {
|
ByteBufAllocator alloc, boolean direct, int maxNumComponents, Iterable<ByteBuf> buffers) {
|
||||||
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
|
this(alloc, direct, maxNumComponents,
|
||||||
if (alloc == null) {
|
buffers instanceof Collection ? ((Collection<ByteBuf>) buffers).size() : 0);
|
||||||
throw new NullPointerException("alloc");
|
|
||||||
}
|
|
||||||
if (maxNumComponents < 2) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = buffers instanceof Collection ? ((Collection<ByteBuf>) buffers).size() : 0;
|
|
||||||
|
|
||||||
this.alloc = alloc;
|
|
||||||
this.direct = direct;
|
|
||||||
this.maxNumComponents = maxNumComponents;
|
|
||||||
components = newList(len, maxNumComponents);
|
|
||||||
|
|
||||||
addComponents0(false, 0, buffers);
|
addComponents0(false, 0, buffers);
|
||||||
consolidateIfNeeded();
|
consolidateIfNeeded();
|
||||||
setIndex(0, capacity());
|
setIndex(0, capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// support passing arrays of other types instead of having to copy to a ByteBuf[] first
|
||||||
|
interface ByteWrapper<T> {
|
||||||
|
ByteBuf wrap(T bytes);
|
||||||
|
boolean isEmpty(T bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final ByteWrapper<byte[]> BYTE_ARRAY_WRAPPER = new ByteWrapper<byte[]>() {
|
||||||
|
@Override
|
||||||
|
public ByteBuf wrap(byte[] bytes) {
|
||||||
|
return Unpooled.wrappedBuffer(bytes);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty(byte[] bytes) {
|
||||||
|
return bytes.length == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static final ByteWrapper<ByteBuffer> BYTE_BUFFER_WRAPPER = new ByteWrapper<ByteBuffer>() {
|
||||||
|
@Override
|
||||||
|
public ByteBuf wrap(ByteBuffer bytes) {
|
||||||
|
return Unpooled.wrappedBuffer(bytes);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty(ByteBuffer bytes) {
|
||||||
|
return !bytes.hasRemaining();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
<T> CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents,
|
||||||
|
ByteWrapper<T> wrapper, T[] buffers, int offset) {
|
||||||
|
this(alloc, direct, maxNumComponents, buffers.length - offset);
|
||||||
|
|
||||||
|
addComponents0(false, 0, wrapper, buffers, offset);
|
||||||
|
consolidateIfNeeded();
|
||||||
|
setIndex(0, capacity());
|
||||||
|
}
|
||||||
|
|
||||||
private static ComponentList newList(int initComponents, int maxNumComponents) {
|
private static ComponentList newList(int initComponents, int maxNumComponents) {
|
||||||
int capacityGuess = Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents);
|
int capacityGuess = Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents);
|
||||||
return new ComponentList(Math.max(initComponents, capacityGuess));
|
return new ComponentList(Math.max(initComponents, capacityGuess));
|
||||||
@ -301,14 +321,15 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] buffers, int offset, int len) {
|
private int addComponents0(boolean increaseWriterIndex, int cIndex,
|
||||||
|
ByteBuf[] buffers, int offset, int endOffset) {
|
||||||
checkNotNull(buffers, "buffers");
|
checkNotNull(buffers, "buffers");
|
||||||
int i = offset;
|
int i = offset;
|
||||||
try {
|
try {
|
||||||
checkComponentIndex(cIndex);
|
checkComponentIndex(cIndex);
|
||||||
|
|
||||||
// No need for consolidation
|
// No need for consolidation
|
||||||
while (i < len) {
|
while (i < endOffset) {
|
||||||
// Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0
|
// Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0
|
||||||
// will release if an exception occurs, and we also release in the finally block here).
|
// will release if an exception occurs, and we also release in the finally block here).
|
||||||
ByteBuf b = buffers[i++];
|
ByteBuf b = buffers[i++];
|
||||||
@ -323,7 +344,7 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
|
|||||||
}
|
}
|
||||||
return cIndex;
|
return cIndex;
|
||||||
} finally {
|
} finally {
|
||||||
for (; i < len; ++i) {
|
for (; i < endOffset; ++i) {
|
||||||
ByteBuf b = buffers[i];
|
ByteBuf b = buffers[i];
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
try {
|
try {
|
||||||
@ -336,6 +357,28 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> int addComponents0(boolean increaseWriterIndex, int cIndex,
|
||||||
|
ByteWrapper<T> wrapper, T[] buffers, int offset) {
|
||||||
|
checkNotNull(buffers, "buffers");
|
||||||
|
checkComponentIndex(cIndex);
|
||||||
|
|
||||||
|
// No need for consolidation
|
||||||
|
for (int i = offset, len = buffers.length; i < len; i++) {
|
||||||
|
T b = buffers[i];
|
||||||
|
if (b == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!wrapper.isEmpty(b)) {
|
||||||
|
cIndex = addComponent0(increaseWriterIndex, cIndex, wrapper.wrap(b)) + 1;
|
||||||
|
int size = components.size();
|
||||||
|
if (cIndex > size) {
|
||||||
|
cIndex = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cIndex;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given {@link ByteBuf}s on the specific index
|
* Add the given {@link ByteBuf}s on the specific index
|
||||||
*
|
*
|
||||||
|
@ -15,15 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.buffer;
|
package io.netty.buffer;
|
||||||
|
|
||||||
|
import io.netty.buffer.CompositeByteBuf.ByteWrapper;
|
||||||
import io.netty.util.internal.PlatformDependent;
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,38 +261,37 @@ public final class Unpooled {
|
|||||||
return wrappedBuffer(buffers.length, buffers);
|
return wrappedBuffer(buffers.length, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <T> ByteBuf wrappedBuffer(int maxNumComponents, ByteWrapper<T> wrapper, T[] array) {
|
||||||
|
switch (array.length) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (!wrapper.isEmpty(array[0])) {
|
||||||
|
return wrapper.wrap(array[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (int i = 0, len = array.length; i < len; i++) {
|
||||||
|
T bytes = array[i];
|
||||||
|
if (bytes == null) {
|
||||||
|
return EMPTY_BUFFER;
|
||||||
|
}
|
||||||
|
if (!wrapper.isEmpty(bytes)) {
|
||||||
|
return new CompositeByteBuf(ALLOC, false, maxNumComponents, wrapper, array, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EMPTY_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new big-endian composite buffer which wraps the specified
|
* Creates a new big-endian composite buffer which wraps the specified
|
||||||
* arrays without copying them. A modification on the specified arrays'
|
* arrays without copying them. A modification on the specified arrays'
|
||||||
* content will be visible to the returned buffer.
|
* content will be visible to the returned buffer.
|
||||||
*/
|
*/
|
||||||
public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) {
|
public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) {
|
||||||
switch (arrays.length) {
|
return wrappedBuffer(maxNumComponents, CompositeByteBuf.BYTE_ARRAY_WRAPPER, arrays);
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (arrays[0].length != 0) {
|
|
||||||
return wrappedBuffer(arrays[0]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Get the list of the component, while guessing the byte order.
|
|
||||||
final List<ByteBuf> components = new ArrayList<ByteBuf>(arrays.length);
|
|
||||||
for (byte[] a: arrays) {
|
|
||||||
if (a == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (a.length > 0) {
|
|
||||||
components.add(wrappedBuffer(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!components.isEmpty()) {
|
|
||||||
return new CompositeByteBuf(ALLOC, false, maxNumComponents, components);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EMPTY_BUFFER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -336,32 +334,7 @@ public final class Unpooled {
|
|||||||
* specified buffers will be visible to the returned buffer.
|
* specified buffers will be visible to the returned buffer.
|
||||||
*/
|
*/
|
||||||
public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuffer... buffers) {
|
public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuffer... buffers) {
|
||||||
switch (buffers.length) {
|
return wrappedBuffer(maxNumComponents, CompositeByteBuf.BYTE_BUFFER_WRAPPER, buffers);
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (buffers[0].hasRemaining()) {
|
|
||||||
return wrappedBuffer(buffers[0].order(BIG_ENDIAN));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Get the list of the component, while guessing the byte order.
|
|
||||||
final List<ByteBuf> components = new ArrayList<ByteBuf>(buffers.length);
|
|
||||||
for (ByteBuffer b: buffers) {
|
|
||||||
if (b == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (b.remaining() > 0) {
|
|
||||||
components.add(wrappedBuffer(b.order(BIG_ENDIAN)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!components.isEmpty()) {
|
|
||||||
return new CompositeByteBuf(ALLOC, false, maxNumComponents, components);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EMPTY_BUFFER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user