retainSlice() unwrap ByteBuf

Motivation:
retainSlice() currently does not unwrap the ByteBuf when creating the ByteBuf wrapper. This effectivley forms a linked list of ByteBuf when it is only necessary to maintain a reference to the unwrapped ByteBuf.

Modifications:
- retainSlice() and retainDuplicate() variants should only maintain a reference to the unwrapped ByteBuf
- create new unit tests which generally verify the retainSlice() behavior
- Remove unecessary generic arguments from AbstractPooledDerivedByteBuf
- Remove unecessary int length member variable from the unpooled sliced ByteBuf implementation
- Rename the unpooled sliced/derived ByteBuf to include Unpooled in their name to be more consistent with the Pooled variants

Result:
Fixes https://github.com/netty/netty/issues/5582
This commit is contained in:
Scott Mitchell 2016-07-26 12:50:56 -07:00
parent d92c5f5f5b
commit 82b617dfe9
16 changed files with 664 additions and 522 deletions

View File

@ -1165,7 +1165,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
@Override
public ByteBuf duplicate() {
return new DuplicatedAbstractByteBuf(this);
return new UnpooledDuplicatedByteBuf(this);
}
@Override
@ -1185,7 +1185,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
@Override
public ByteBuf slice(int index, int length) {
return new SlicedAbstractByteBuf(this, index, length);
return new UnpooledSlicedByteBuf(this, index, length);
}
@Override

View File

@ -24,15 +24,15 @@ import java.nio.ByteOrder;
/**
* Abstract base class for derived {@link ByteBuf} implementations.
*/
abstract class AbstractPooledDerivedByteBuf<T> extends AbstractReferenceCountedByteBuf {
abstract class AbstractPooledDerivedByteBuf extends AbstractReferenceCountedByteBuf {
private final Handle<AbstractPooledDerivedByteBuf<T>> recyclerHandle;
private final Handle<AbstractPooledDerivedByteBuf> recyclerHandle;
private AbstractByteBuf buffer;
@SuppressWarnings("unchecked")
AbstractPooledDerivedByteBuf(Handle<? extends AbstractPooledDerivedByteBuf<T>> recyclerHandle) {
AbstractPooledDerivedByteBuf(Handle<? extends AbstractPooledDerivedByteBuf> recyclerHandle) {
super(0);
this.recyclerHandle = (Handle<AbstractPooledDerivedByteBuf<T>>) recyclerHandle;
this.recyclerHandle = (Handle<AbstractPooledDerivedByteBuf>) recyclerHandle;
}
@Override
@ -40,26 +40,25 @@ abstract class AbstractPooledDerivedByteBuf<T> extends AbstractReferenceCountedB
return buffer;
}
final <U extends AbstractPooledDerivedByteBuf<T>> U init(
AbstractByteBuf buffer, int readerIndex, int writerIndex, int maxCapacity) {
final <U extends AbstractPooledDerivedByteBuf> U init(
AbstractByteBuf unwrapped, ByteBuf wrapped, int readerIndex, int writerIndex, int maxCapacity) {
buffer.retain();
this.buffer = buffer;
wrapped.retain(); // Retain up front to ensure the wrapped buffer is accessible before doing more work.
this.buffer = unwrapped;
boolean success = false;
try {
maxCapacity(maxCapacity);
setIndex(readerIndex, writerIndex);
setIndex0(readerIndex, writerIndex); // It is assumed the bounds checking is done by the caller.
setRefCnt(1);
@SuppressWarnings("unchecked")
final U castThis = (U) this;
success = true;
wrapped = null;
return castThis;
} finally {
if (!success) {
if (wrapped != null) {
this.buffer = null;
buffer.release();
wrapped.release();
}
}
}
@ -120,19 +119,9 @@ abstract class AbstractPooledDerivedByteBuf<T> extends AbstractReferenceCountedB
return nioBuffer(index, length);
}
@Override
public final ByteBuf retainedDuplicate() {
return PooledDuplicatedByteBuf.newInstance(this, readerIndex(), writerIndex());
}
@Override
public final ByteBuf retainedSlice() {
final int index = readerIndex();
return retainedSlice(index, writerIndex() - index);
}
@Override
public final ByteBuf retainedSlice(int index, int length) {
return PooledSlicedByteBuf.newInstance(this, index, length, index);
}
}

View File

@ -0,0 +1,497 @@
/*
* Copyright 2016 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.ByteProcessor;
import io.netty.util.CharsetUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
abstract class AbstractUnpooledSlicedByteBuf extends AbstractDerivedByteBuf {
private final ByteBuf buffer;
private final int adjustment;
AbstractUnpooledSlicedByteBuf(ByteBuf buffer, int index, int length) {
super(length);
checkSliceOutOfBounds(index, length, buffer);
if (buffer instanceof AbstractUnpooledSlicedByteBuf) {
this.buffer = ((AbstractUnpooledSlicedByteBuf) buffer).buffer;
adjustment = ((AbstractUnpooledSlicedByteBuf) buffer).adjustment + index;
} else if (buffer instanceof DuplicatedByteBuf) {
this.buffer = buffer.unwrap();
adjustment = index;
} else {
this.buffer = buffer;
adjustment = index;
}
initLength(length);
writerIndex(length);
}
/**
* Called by the constructor before {@link #writerIndex(int)}.
* @param length the {@code length} argument from the constructor.
*/
void initLength(int length) {
}
int length() {
return capacity();
}
@Override
public ByteBuf unwrap() {
return buffer;
}
@Override
public ByteBufAllocator alloc() {
return unwrap().alloc();
}
@Override
@Deprecated
public ByteOrder order() {
return unwrap().order();
}
@Override
public boolean isDirect() {
return unwrap().isDirect();
}
@Override
public ByteBuf capacity(int newCapacity) {
throw new UnsupportedOperationException("sliced buffer");
}
@Override
public boolean hasArray() {
return unwrap().hasArray();
}
@Override
public byte[] array() {
return unwrap().array();
}
@Override
public int arrayOffset() {
return idx(unwrap().arrayOffset());
}
@Override
public boolean hasMemoryAddress() {
return unwrap().hasMemoryAddress();
}
@Override
public long memoryAddress() {
return unwrap().memoryAddress() + adjustment;
}
@Override
public byte getByte(int index) {
checkIndex0(index, 1);
return unwrap().getByte(idx(index));
}
@Override
protected byte _getByte(int index) {
return unwrap().getByte(idx(index));
}
@Override
public short getShort(int index) {
checkIndex0(index, 2);
return unwrap().getShort(idx(index));
}
@Override
protected short _getShort(int index) {
return unwrap().getShort(idx(index));
}
@Override
public short getShortLE(int index) {
checkIndex0(index, 2);
return unwrap().getShortLE(idx(index));
}
@Override
protected short _getShortLE(int index) {
return unwrap().getShortLE(idx(index));
}
@Override
public int getUnsignedMedium(int index) {
checkIndex0(index, 3);
return unwrap().getUnsignedMedium(idx(index));
}
@Override
protected int _getUnsignedMedium(int index) {
return unwrap().getUnsignedMedium(idx(index));
}
@Override
public int getUnsignedMediumLE(int index) {
checkIndex0(index, 3);
return unwrap().getUnsignedMediumLE(idx(index));
}
@Override
protected int _getUnsignedMediumLE(int index) {
return unwrap().getUnsignedMediumLE(idx(index));
}
@Override
public int getInt(int index) {
checkIndex0(index, 4);
return unwrap().getInt(idx(index));
}
@Override
protected int _getInt(int index) {
return unwrap().getInt(idx(index));
}
@Override
public int getIntLE(int index) {
checkIndex0(index, 4);
return unwrap().getIntLE(idx(index));
}
@Override
protected int _getIntLE(int index) {
return unwrap().getIntLE(idx(index));
}
@Override
public long getLong(int index) {
checkIndex0(index, 8);
return unwrap().getLong(idx(index));
}
@Override
protected long _getLong(int index) {
return unwrap().getLong(idx(index));
}
@Override
public long getLongLE(int index) {
checkIndex0(index, 8);
return unwrap().getLongLE(idx(index));
}
@Override
protected long _getLongLE(int index) {
return unwrap().getLongLE(idx(index));
}
@Override
public ByteBuf duplicate() {
final ByteBuf duplicate = unwrap().slice(adjustment, length());
duplicate.setIndex(readerIndex(), writerIndex());
return duplicate;
}
@Override
public ByteBuf copy(int index, int length) {
checkIndex0(index, length);
return unwrap().copy(idx(index), length);
}
@Override
public ByteBuf slice(int index, int length) {
checkIndex0(index, length);
return unwrap().slice(idx(index), length);
}
@Override
public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
checkIndex0(index, length);
unwrap().getBytes(idx(index), dst, dstIndex, length);
return this;
}
@Override
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
checkIndex0(index, length);
unwrap().getBytes(idx(index), dst, dstIndex, length);
return this;
}
@Override
public ByteBuf getBytes(int index, ByteBuffer dst) {
checkIndex0(index, dst.remaining());
unwrap().getBytes(idx(index), dst);
return this;
}
@Override
public ByteBuf setByte(int index, int value) {
checkIndex0(index, 1);
unwrap().setByte(idx(index), value);
return this;
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
checkIndex0(index, length);
return buffer.getCharSequence(idx(index), length, charset);
}
@Override
protected void _setByte(int index, int value) {
unwrap().setByte(idx(index), value);
}
@Override
public ByteBuf setShort(int index, int value) {
checkIndex0(index, 2);
unwrap().setShort(idx(index), value);
return this;
}
@Override
protected void _setShort(int index, int value) {
unwrap().setShort(idx(index), value);
}
@Override
public ByteBuf setShortLE(int index, int value) {
checkIndex0(index, 2);
unwrap().setShortLE(idx(index), value);
return this;
}
@Override
protected void _setShortLE(int index, int value) {
unwrap().setShortLE(idx(index), value);
}
@Override
public ByteBuf setMedium(int index, int value) {
checkIndex0(index, 3);
unwrap().setMedium(idx(index), value);
return this;
}
@Override
protected void _setMedium(int index, int value) {
unwrap().setMedium(idx(index), value);
}
@Override
public ByteBuf setMediumLE(int index, int value) {
checkIndex0(index, 3);
unwrap().setMediumLE(idx(index), value);
return this;
}
@Override
protected void _setMediumLE(int index, int value) {
unwrap().setMediumLE(idx(index), value);
}
@Override
public ByteBuf setInt(int index, int value) {
checkIndex0(index, 4);
unwrap().setInt(idx(index), value);
return this;
}
@Override
protected void _setInt(int index, int value) {
unwrap().setInt(idx(index), value);
}
@Override
public ByteBuf setIntLE(int index, int value) {
checkIndex0(index, 4);
unwrap().setIntLE(idx(index), value);
return this;
}
@Override
protected void _setIntLE(int index, int value) {
unwrap().setIntLE(idx(index), value);
}
@Override
public ByteBuf setLong(int index, long value) {
checkIndex0(index, 8);
unwrap().setLong(idx(index), value);
return this;
}
@Override
protected void _setLong(int index, long value) {
unwrap().setLong(idx(index), value);
}
@Override
public ByteBuf setLongLE(int index, long value) {
checkIndex0(index, 8);
unwrap().setLongLE(idx(index), value);
return this;
}
@Override
protected void _setLongLE(int index, long value) {
unwrap().setLongLE(idx(index), value);
}
@Override
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
checkIndex0(index, length);
unwrap().setBytes(idx(index), src, srcIndex, length);
return this;
}
@Override
public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
checkIndex0(index, length);
unwrap().setBytes(idx(index), src, srcIndex, length);
return this;
}
@Override
public ByteBuf setBytes(int index, ByteBuffer src) {
checkIndex0(index, src.remaining());
unwrap().setBytes(idx(index), src);
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
if (charset.equals(CharsetUtil.UTF_8)) {
checkIndex0(index, ByteBufUtil.utf8MaxBytes(sequence));
return ByteBufUtil.writeUtf8(this, idx(index), sequence, sequence.length());
}
if (charset.equals(CharsetUtil.US_ASCII)) {
int len = sequence.length();
checkIndex0(index, len);
return ByteBufUtil.writeAscii(this, idx(index), sequence, len);
}
byte[] bytes = sequence.toString().getBytes(charset);
checkIndex0(index, bytes.length);
buffer.setBytes(idx(index), bytes);
return bytes.length;
}
@Override
public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
checkIndex0(index, length);
unwrap().getBytes(idx(index), out, length);
return this;
}
@Override
public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
checkIndex0(index, length);
return unwrap().getBytes(idx(index), out, length);
}
@Override
public int getBytes(int index, FileChannel out, long position, int length) throws IOException {
checkIndex0(index, length);
return unwrap().getBytes(idx(index), out, position, length);
}
@Override
public int setBytes(int index, InputStream in, int length) throws IOException {
checkIndex0(index, length);
return unwrap().setBytes(idx(index), in, length);
}
@Override
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
checkIndex0(index, length);
return unwrap().setBytes(idx(index), in, length);
}
@Override
public int setBytes(int index, FileChannel in, long position, int length) throws IOException {
checkIndex0(index, length);
return unwrap().setBytes(idx(index), in, position, length);
}
@Override
public int nioBufferCount() {
return unwrap().nioBufferCount();
}
@Override
public ByteBuffer nioBuffer(int index, int length) {
checkIndex0(index, length);
return unwrap().nioBuffer(idx(index), length);
}
@Override
public ByteBuffer[] nioBuffers(int index, int length) {
checkIndex0(index, length);
return unwrap().nioBuffers(idx(index), length);
}
@Override
public int forEachByte(int index, int length, ByteProcessor processor) {
checkIndex0(index, length);
int ret = unwrap().forEachByte(idx(index), length, processor);
if (ret >= adjustment) {
return ret - adjustment;
} else {
return -1;
}
}
@Override
public int forEachByteDesc(int index, int length, ByteProcessor processor) {
checkIndex0(index, length);
int ret = unwrap().forEachByteDesc(idx(index), length, processor);
if (ret >= adjustment) {
return ret - adjustment;
} else {
return -1;
}
}
/**
* Returns the index with the needed adjustment.
*/
final int idx(int index) {
return index + adjustment;
}
static void checkSliceOutOfBounds(int index, int length, ByteBuf buffer) {
if (isOutOfBounds(index, length, buffer.capacity())) {
throw new IndexOutOfBoundsException(buffer + ".slice(" + index + ", " + length + ')');
}
}
}

View File

@ -47,6 +47,8 @@ public class DuplicatedByteBuf extends AbstractDerivedByteBuf {
if (buffer instanceof DuplicatedByteBuf) {
this.buffer = ((DuplicatedByteBuf) buffer).buffer;
} else if (buffer instanceof AbstractPooledDerivedByteBuf) {
this.buffer = buffer.unwrap();
} else {
this.buffer = buffer;
}

View File

@ -138,7 +138,7 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
@Override
public final ByteBuf retainedDuplicate() {
return PooledDuplicatedByteBuf.newInstance(this, readerIndex(), writerIndex());
return PooledDuplicatedByteBuf.newInstance(this, this, readerIndex(), writerIndex());
}
@Override
@ -149,7 +149,7 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
@Override
public final ByteBuf retainedSlice(int index, int length) {
return PooledSlicedByteBuf.newInstance(this, index, length, index);
return PooledSlicedByteBuf.newInstance(this, this, index, length);
}
protected final ByteBuffer internalNioBuffer() {

View File

@ -28,7 +28,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
final class PooledDuplicatedByteBuf extends AbstractPooledDerivedByteBuf<PooledDuplicatedByteBuf> {
final class PooledDuplicatedByteBuf extends AbstractPooledDerivedByteBuf {
private static final Recycler<PooledDuplicatedByteBuf> RECYCLER = new Recycler<PooledDuplicatedByteBuf>() {
@Override
@ -37,9 +37,10 @@ final class PooledDuplicatedByteBuf extends AbstractPooledDerivedByteBuf<PooledD
}
};
static PooledDuplicatedByteBuf newInstance(AbstractByteBuf buffer, int readerIndex, int writerIndex) {
static PooledDuplicatedByteBuf newInstance(AbstractByteBuf unwrapped, ByteBuf wrapped,
int readerIndex, int writerIndex) {
final PooledDuplicatedByteBuf duplicate = RECYCLER.get();
duplicate.init(buffer, readerIndex, writerIndex, buffer.maxCapacity());
duplicate.init(unwrapped, wrapped, readerIndex, writerIndex, wrapped.maxCapacity());
duplicate.markReaderIndex();
duplicate.markWriterIndex();
@ -86,6 +87,21 @@ final class PooledDuplicatedByteBuf extends AbstractPooledDerivedByteBuf<PooledD
return unwrap().copy(index, length);
}
@Override
public ByteBuf slice(int index, int length) {
return unwrap().slice(index, length);
}
@Override
public ByteBuf retainedSlice(int index, int length) {
return PooledSlicedByteBuf.newInstance(unwrap(), this, index, length);
}
@Override
public ByteBuf retainedDuplicate() {
return PooledDuplicatedByteBuf.newInstance(unwrap(), this, readerIndex(), writerIndex());
}
@Override
public byte getByte(int index) {
return unwrap().getByte(index);

View File

@ -28,9 +28,9 @@ import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
import static io.netty.buffer.AbstractUnpooledSlicedByteBuf.checkSliceOutOfBounds;
final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf<PooledSlicedByteBuf> {
final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf {
private static final Recycler<PooledSlicedByteBuf> RECYCLER = new Recycler<PooledSlicedByteBuf>() {
@Override
@ -39,20 +39,23 @@ final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf<PooledSlice
}
};
static PooledSlicedByteBuf newInstance(AbstractByteBuf buffer, int index, int length, int adjustment) {
if (isOutOfBounds(index, length, buffer.capacity())) {
throw new IndexOutOfBoundsException(buffer + ".slice(" + index + ", " + length + ')');
}
static PooledSlicedByteBuf newInstance(AbstractByteBuf unwrapped, ByteBuf wrapped,
int index, int length) {
checkSliceOutOfBounds(index, length, unwrapped);
return newInstance0(unwrapped, wrapped, index, length);
}
private static PooledSlicedByteBuf newInstance0(AbstractByteBuf unwrapped, ByteBuf wrapped,
int adjustment, int length) {
final PooledSlicedByteBuf slice = RECYCLER.get();
slice.init(buffer, 0, length, length);
slice.init(unwrapped, wrapped, 0, length, length);
slice.discardMarks();
slice.adjustment = adjustment;
return slice;
}
private int adjustment;
int adjustment;
private PooledSlicedByteBuf(Handle<PooledSlicedByteBuf> handle) {
super(handle);
@ -65,7 +68,7 @@ final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf<PooledSlice
@Override
public ByteBuf capacity(int newCapacity) {
return reject();
throw new UnsupportedOperationException("sliced buffer");
}
@Override
@ -96,6 +99,34 @@ final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf<PooledSlice
return unwrap().copy(idx(index), length);
}
@Override
public ByteBuf slice(int index, int length) {
checkIndex0(index, length);
return unwrap().slice(idx(index), length);
}
@Override
public ByteBuf retainedSlice(int index, int length) {
checkIndex0(index, length);
return PooledSlicedByteBuf.newInstance0(unwrap(), this, idx(index), length);
}
@Override
public ByteBuf duplicate() {
// Capacity is not allowed to change for a sliced ByteBuf, so length == capacity()
final ByteBuf duplicate = unwrap().slice(adjustment, capacity());
duplicate.setIndex(readerIndex(), writerIndex());
return duplicate;
}
@Override
public ByteBuf retainedDuplicate() {
// Capacity is not allowed to change for a sliced ByteBuf, so length == capacity()
final ByteBuf duplicate = retainedSlice(0, capacity());
duplicate.setIndex(readerIndex(), writerIndex());
return duplicate;
}
@Override
public byte getByte(int index) {
checkIndex0(index, 1);
@ -411,8 +442,4 @@ final class PooledSlicedByteBuf extends AbstractPooledDerivedByteBuf<PooledSlice
private int idx(int index) {
return index + adjustment;
}
private static ByteBuf reject() {
throw new UnsupportedOperationException("sliced buffer");
}
}

View File

@ -15,21 +15,6 @@
*/
package io.netty.buffer;
import io.netty.util.ByteProcessor;
import io.netty.util.CharsetUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
/**
* A derived buffer which exposes its parent's sub-region only. It is
* recommended to use {@link ByteBuf#slice()} and
@ -39,464 +24,26 @@ import static io.netty.util.internal.MathUtil.isOutOfBounds;
* @deprecated Do not use.
*/
@Deprecated
public class SlicedByteBuf extends AbstractDerivedByteBuf {
public class SlicedByteBuf extends AbstractUnpooledSlicedByteBuf {
private final ByteBuf buffer;
private final int adjustment;
private final int length;
private int length;
public SlicedByteBuf(ByteBuf buffer, int index, int length) {
super(length);
if (isOutOfBounds(index, length, buffer.capacity())) {
throw new IndexOutOfBoundsException(buffer + ".slice(" + index + ", " + length + ')');
}
super(buffer, index, length);
}
if (buffer instanceof SlicedByteBuf) {
this.buffer = ((SlicedByteBuf) buffer).buffer;
adjustment = ((SlicedByteBuf) buffer).adjustment + index;
} else if (buffer instanceof DuplicatedByteBuf) {
this.buffer = buffer.unwrap();
adjustment = index;
} else {
this.buffer = buffer;
adjustment = index;
}
@Override
final void initLength(int length) {
this.length = length;
writerIndex(length);
}
final int adjustment() {
return adjustment;
}
@Override
public ByteBuf unwrap() {
return buffer;
}
@Override
public ByteBufAllocator alloc() {
return unwrap().alloc();
}
@Override
@Deprecated
public ByteOrder order() {
return unwrap().order();
}
@Override
public boolean isDirect() {
return unwrap().isDirect();
final int length() {
return length;
}
@Override
public int capacity() {
return length;
}
@Override
public ByteBuf capacity(int newCapacity) {
throw new UnsupportedOperationException("sliced buffer");
}
@Override
public boolean hasArray() {
return unwrap().hasArray();
}
@Override
public byte[] array() {
return unwrap().array();
}
@Override
public int arrayOffset() {
return idx(unwrap().arrayOffset());
}
@Override
public boolean hasMemoryAddress() {
return unwrap().hasMemoryAddress();
}
@Override
public long memoryAddress() {
return unwrap().memoryAddress() + adjustment;
}
@Override
public byte getByte(int index) {
checkIndex0(index, 1);
return unwrap().getByte(idx(index));
}
@Override
protected byte _getByte(int index) {
return unwrap().getByte(idx(index));
}
@Override
public short getShort(int index) {
checkIndex0(index, 2);
return unwrap().getShort(idx(index));
}
@Override
protected short _getShort(int index) {
return unwrap().getShort(idx(index));
}
@Override
public short getShortLE(int index) {
checkIndex0(index, 2);
return unwrap().getShortLE(idx(index));
}
@Override
protected short _getShortLE(int index) {
return unwrap().getShortLE(idx(index));
}
@Override
public int getUnsignedMedium(int index) {
checkIndex0(index, 3);
return unwrap().getUnsignedMedium(idx(index));
}
@Override
protected int _getUnsignedMedium(int index) {
return unwrap().getUnsignedMedium(idx(index));
}
@Override
public int getUnsignedMediumLE(int index) {
checkIndex0(index, 3);
return unwrap().getUnsignedMediumLE(idx(index));
}
@Override
protected int _getUnsignedMediumLE(int index) {
return unwrap().getUnsignedMediumLE(idx(index));
}
@Override
public int getInt(int index) {
checkIndex0(index, 4);
return unwrap().getInt(idx(index));
}
@Override
protected int _getInt(int index) {
return unwrap().getInt(idx(index));
}
@Override
public int getIntLE(int index) {
checkIndex0(index, 4);
return unwrap().getIntLE(idx(index));
}
@Override
protected int _getIntLE(int index) {
return unwrap().getIntLE(idx(index));
}
@Override
public long getLong(int index) {
checkIndex0(index, 8);
return unwrap().getLong(idx(index));
}
@Override
protected long _getLong(int index) {
return unwrap().getLong(idx(index));
}
@Override
public long getLongLE(int index) {
checkIndex0(index, 8);
return unwrap().getLongLE(idx(index));
}
@Override
protected long _getLongLE(int index) {
return unwrap().getLongLE(idx(index));
}
@Override
public ByteBuf duplicate() {
final ByteBuf duplicate = unwrap().slice(adjustment, length);
duplicate.setIndex(readerIndex(), writerIndex());
return duplicate;
}
@Override
public ByteBuf copy(int index, int length) {
checkIndex0(index, length);
return unwrap().copy(idx(index), length);
}
@Override
public ByteBuf slice(int index, int length) {
checkIndex0(index, length);
return unwrap().slice(idx(index), length);
}
@Override
public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
checkIndex0(index, length);
unwrap().getBytes(idx(index), dst, dstIndex, length);
return this;
}
@Override
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
checkIndex0(index, length);
unwrap().getBytes(idx(index), dst, dstIndex, length);
return this;
}
@Override
public ByteBuf getBytes(int index, ByteBuffer dst) {
checkIndex0(index, dst.remaining());
unwrap().getBytes(idx(index), dst);
return this;
}
@Override
public ByteBuf setByte(int index, int value) {
checkIndex0(index, 1);
unwrap().setByte(idx(index), value);
return this;
}
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
checkIndex0(index, length);
return buffer.getCharSequence(idx(index), length, charset);
}
@Override
protected void _setByte(int index, int value) {
unwrap().setByte(idx(index), value);
}
@Override
public ByteBuf setShort(int index, int value) {
checkIndex0(index, 2);
unwrap().setShort(idx(index), value);
return this;
}
@Override
protected void _setShort(int index, int value) {
unwrap().setShort(idx(index), value);
}
@Override
public ByteBuf setShortLE(int index, int value) {
checkIndex0(index, 2);
unwrap().setShortLE(idx(index), value);
return this;
}
@Override
protected void _setShortLE(int index, int value) {
unwrap().setShortLE(idx(index), value);
}
@Override
public ByteBuf setMedium(int index, int value) {
checkIndex0(index, 3);
unwrap().setMedium(idx(index), value);
return this;
}
@Override
protected void _setMedium(int index, int value) {
unwrap().setMedium(idx(index), value);
}
@Override
public ByteBuf setMediumLE(int index, int value) {
checkIndex0(index, 3);
unwrap().setMediumLE(idx(index), value);
return this;
}
@Override
protected void _setMediumLE(int index, int value) {
unwrap().setMediumLE(idx(index), value);
}
@Override
public ByteBuf setInt(int index, int value) {
checkIndex0(index, 4);
unwrap().setInt(idx(index), value);
return this;
}
@Override
protected void _setInt(int index, int value) {
unwrap().setInt(idx(index), value);
}
@Override
public ByteBuf setIntLE(int index, int value) {
checkIndex0(index, 4);
unwrap().setIntLE(idx(index), value);
return this;
}
@Override
protected void _setIntLE(int index, int value) {
unwrap().setIntLE(idx(index), value);
}
@Override
public ByteBuf setLong(int index, long value) {
checkIndex0(index, 8);
unwrap().setLong(idx(index), value);
return this;
}
@Override
protected void _setLong(int index, long value) {
unwrap().setLong(idx(index), value);
}
@Override
public ByteBuf setLongLE(int index, long value) {
checkIndex0(index, 8);
unwrap().setLongLE(idx(index), value);
return this;
}
@Override
protected void _setLongLE(int index, long value) {
unwrap().setLongLE(idx(index), value);
}
@Override
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
checkIndex0(index, length);
unwrap().setBytes(idx(index), src, srcIndex, length);
return this;
}
@Override
public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
checkIndex0(index, length);
unwrap().setBytes(idx(index), src, srcIndex, length);
return this;
}
@Override
public ByteBuf setBytes(int index, ByteBuffer src) {
checkIndex0(index, src.remaining());
unwrap().setBytes(idx(index), src);
return this;
}
@Override
public int setCharSequence(int index, CharSequence sequence, Charset charset) {
if (charset.equals(CharsetUtil.UTF_8)) {
checkIndex0(index, ByteBufUtil.utf8MaxBytes(sequence));
return ByteBufUtil.writeUtf8(this, idx(index), sequence, sequence.length());
}
if (charset.equals(CharsetUtil.US_ASCII)) {
int len = sequence.length();
checkIndex0(index, len);
return ByteBufUtil.writeAscii(this, idx(index), sequence, len);
}
byte[] bytes = sequence.toString().getBytes(charset);
checkIndex0(index, bytes.length);
buffer.setBytes(idx(index), bytes);
return bytes.length;
}
@Override
public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
checkIndex0(index, length);
unwrap().getBytes(idx(index), out, length);
return this;
}
@Override
public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
checkIndex0(index, length);
return unwrap().getBytes(idx(index), out, length);
}
@Override
public int getBytes(int index, FileChannel out, long position, int length) throws IOException {
checkIndex0(index, length);
return unwrap().getBytes(idx(index), out, position, length);
}
@Override
public int setBytes(int index, InputStream in, int length) throws IOException {
checkIndex0(index, length);
return unwrap().setBytes(idx(index), in, length);
}
@Override
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
checkIndex0(index, length);
return unwrap().setBytes(idx(index), in, length);
}
@Override
public int setBytes(int index, FileChannel in, long position, int length) throws IOException {
checkIndex0(index, length);
return unwrap().setBytes(idx(index), in, position, length);
}
@Override
public int nioBufferCount() {
return unwrap().nioBufferCount();
}
@Override
public ByteBuffer nioBuffer(int index, int length) {
checkIndex0(index, length);
return unwrap().nioBuffer(idx(index), length);
}
@Override
public ByteBuffer[] nioBuffers(int index, int length) {
checkIndex0(index, length);
return unwrap().nioBuffers(idx(index), length);
}
@Override
public int forEachByte(int index, int length, ByteProcessor processor) {
checkIndex0(index, length);
int ret = unwrap().forEachByte(idx(index), length, processor);
if (ret >= adjustment) {
return ret - adjustment;
} else {
return -1;
}
}
@Override
public int forEachByteDesc(int index, int length, ByteProcessor processor) {
checkIndex0(index, length);
int ret = unwrap().forEachByteDesc(idx(index), length, processor);
if (ret >= adjustment) {
return ret - adjustment;
} else {
return -1;
}
}
/**
* Returns the index with the needed adjustment.
*/
final int idx(int index) {
return index + adjustment;
}
}

View File

@ -19,8 +19,8 @@ package io.netty.buffer;
* {@link DuplicatedByteBuf} implementation that can do optimizations because it knows the duplicated buffer
* is of type {@link AbstractByteBuf}.
*/
final class DuplicatedAbstractByteBuf extends DuplicatedByteBuf {
DuplicatedAbstractByteBuf(AbstractByteBuf buffer) {
final class UnpooledDuplicatedByteBuf extends DuplicatedByteBuf {
UnpooledDuplicatedByteBuf(AbstractByteBuf buffer) {
super(buffer);
}

View File

@ -16,15 +16,20 @@
package io.netty.buffer;
/**
* A special {@link SlicedByteBuf} that can make optimizations because it knows the sliced buffer is of type
* {@link AbstractByteBuf}.
* A special {@link AbstractUnpooledSlicedByteBuf} that can make optimizations because it knows the sliced buffer is of
* type {@link AbstractByteBuf}.
*/
final class SlicedAbstractByteBuf extends SlicedByteBuf {
final class UnpooledSlicedByteBuf extends AbstractUnpooledSlicedByteBuf {
SlicedAbstractByteBuf(AbstractByteBuf buffer, int index, int length) {
UnpooledSlicedByteBuf(AbstractByteBuf buffer, int index, int length) {
super(buffer, index, length);
}
@Override
public int capacity() {
return maxCapacity();
}
@Override
public AbstractByteBuf unwrap() {
return (AbstractByteBuf) super.unwrap();

View File

@ -2918,6 +2918,54 @@ public abstract class AbstractByteBufTest {
testDuplicateContents(false);
}
@Test
public void testDuplicateCapacityChange() {
testDuplicateCapacityChange(false);
}
@Test
public void testRetainedDuplicateCapacityChange() {
testDuplicateCapacityChange(true);
}
@Test(expected = UnsupportedOperationException.class)
public void testSliceCapacityChange() {
testSliceCapacityChange(false);
}
@Test(expected = UnsupportedOperationException.class)
public void testRetainedSliceCapacityChange() {
testSliceCapacityChange(true);
}
private void testDuplicateCapacityChange(boolean retainedDuplicate) {
ByteBuf buf = releaseLater(newBuffer(8));
ByteBuf dup = retainedDuplicate ? buf.retainedDuplicate() : buf.duplicate();
try {
dup.capacity(10);
assertEquals(buf.capacity(), dup.capacity());
dup.capacity(5);
assertEquals(buf.capacity(), dup.capacity());
} finally {
if (retainedDuplicate) {
dup.release();
}
}
}
private void testSliceCapacityChange(boolean retainedSlice) {
ByteBuf buf = releaseLater(newBuffer(8));
ByteBuf slice = retainedSlice ? buf.retainedSlice(buf.readerIndex() + 1, 3)
: buf.slice(buf.readerIndex() + 1, 3);
try {
slice.capacity(10);
} finally {
if (retainedSlice) {
slice.release();
}
}
}
private void testSliceOutOfBounds(boolean initRetainedSlice, boolean finalRetainedSlice, boolean indexOutOfBounds) {
ByteBuf buf = releaseLater(newBuffer(8));
ByteBuf slice = initRetainedSlice ? buf.retainedSlice(buf.readerIndex() + 1, 2)

View File

@ -457,7 +457,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
public void testComponentMustBeSlice() {
CompositeByteBuf buf = releaseLater(compositeBuffer());
buf.addComponent(buffer(4).setIndex(1, 3));
assertThat(buf.component(0), is(instanceOf(SlicedByteBuf.class)));
assertThat(buf.component(0), is(instanceOf(AbstractUnpooledSlicedByteBuf.class)));
assertThat(buf.component(0).capacity(), is(2));
assertThat(buf.component(0).maxCapacity(), is(2));
}

View File

@ -34,7 +34,7 @@ public class ByteBufDerivationTest {
ByteBuf buf = Unpooled.buffer(8).setIndex(1, 7);
ByteBuf slice = buf.slice(1, 7);
assertThat(slice, instanceOf(SlicedByteBuf.class));
assertThat(slice, instanceOf(AbstractUnpooledSlicedByteBuf.class));
assertThat(slice.unwrap(), sameInstance(buf));
assertThat(slice.readerIndex(), is(0));
assertThat(slice.writerIndex(), is(7));
@ -53,7 +53,7 @@ public class ByteBufDerivationTest {
ByteBuf slice2 = slice.slice(0, 6);
assertThat(slice2, not(sameInstance(slice)));
assertThat(slice2, instanceOf(SlicedByteBuf.class));
assertThat(slice2, instanceOf(AbstractUnpooledSlicedByteBuf.class));
assertThat(slice2.unwrap(), sameInstance(buf));
assertThat(slice2.writerIndex(), is(6));
assertThat(slice2.capacity(), is(6));

View File

@ -126,6 +126,18 @@ public class SlicedByteBufTest extends AbstractByteBufTest {
// ignore for SlicedByteBuf
}
@Test(expected = UnsupportedOperationException.class)
@Override
public void testDuplicateCapacityChange() {
super.testDuplicateCapacityChange();
}
@Test(expected = UnsupportedOperationException.class)
@Override
public void testRetainedDuplicateCapacityChange() {
super.testRetainedDuplicateCapacityChange();
}
@Test
public void testReaderIndexAndMarks() {
ByteBuf wrapped = Unpooled.buffer(16);
@ -134,7 +146,7 @@ public class SlicedByteBufTest extends AbstractByteBufTest {
wrapped.readerIndex(2);
wrapped.markWriterIndex();
wrapped.markReaderIndex();
ByteBuf slice = new SlicedByteBuf(wrapped, 4, 4);
ByteBuf slice = wrapped.slice(4, 4);
assertEquals(0, slice.readerIndex());
assertEquals(4, slice.writerIndex());

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.SlicedByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpContent;
@ -37,6 +36,7 @@ import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TRANSFER_ENCODING;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/** {@link HttpPostRequestEncoder} test case. */
public class HttpPostRequestEncoderTest {
@ -225,11 +225,11 @@ public class HttpPostRequestEncoderTest {
encoder.finalizeRequest();
while (! encoder.isEndOfInput()) {
HttpContent httpContent = encoder.readChunk((ByteBufAllocator) null);
if (httpContent.content() instanceof SlicedByteBuf) {
assertEquals(2, httpContent.content().refCnt());
} else {
assertEquals(1, httpContent.content().refCnt());
}
ByteBuf content = httpContent.content();
int refCnt = content.refCnt();
assertTrue("content: " + content + " content.unwrap(): " + content.unwrap() + " refCnt: " + refCnt,
(content.unwrap() == content || content.unwrap() == null) && refCnt == 1 ||
content.unwrap() != content && refCnt == 2);
httpContent.release();
}
encoder.cleanFiles();

View File

@ -17,7 +17,6 @@ package io.netty.microbench.buffer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.SlicedByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.microbench.util.AbstractMicrobenchmark;
import org.openjdk.jmh.annotations.Benchmark;
@ -42,7 +41,7 @@ public class SlicedByteBufBenchmark extends AbstractMicrobenchmark {
public void setup() {
// Use buffer sizes that will also allow to write UTF-8 without grow the buffer
ByteBuf buffer = Unpooled.buffer(512).retain();
slicedByteBuf = new SlicedByteBuf(buffer, 0, 256);
slicedByteBuf = buffer.slice(0, 256);
slicedAbstractByteBuf = buffer.slice(0, 256);
if (slicedByteBuf.getClass() == slicedAbstractByteBuf.getClass()) {