Fix numerous bugs in the ByteBufAdaptor

This commit is contained in:
Chris Vest 2021-03-09 12:04:57 +01:00
parent 45074e4749
commit da70f29ff4
3 changed files with 125 additions and 41 deletions

View File

@ -38,10 +38,12 @@ import java.util.concurrent.atomic.AtomicReference;
public final class ByteBufAdaptor extends ByteBuf { public final class ByteBufAdaptor extends ByteBuf {
private final ByteBufAllocatorAdaptor alloc; private final ByteBufAllocatorAdaptor alloc;
private final Buffer buffer; private final Buffer buffer;
private final boolean hasMemoryAddress;
public ByteBufAdaptor(ByteBufAllocatorAdaptor alloc, Buffer buffer) { public ByteBufAdaptor(ByteBufAllocatorAdaptor alloc, Buffer buffer) {
this.alloc = alloc; this.alloc = alloc;
this.buffer = buffer; this.buffer = buffer;
hasMemoryAddress = buffer.nativeAddress() != 0;
} }
/** /**
@ -70,7 +72,14 @@ public final class ByteBufAdaptor extends ByteBuf {
public ByteBuf capacity(int newCapacity) { public ByteBuf capacity(int newCapacity) {
int diff = newCapacity - capacity() - buffer.writableBytes(); int diff = newCapacity - capacity() - buffer.writableBytes();
if (diff > 0) { if (diff > 0) {
buffer.ensureWritable(diff); try {
buffer.ensureWritable(diff);
} catch (IllegalStateException e) {
if (!buffer.isOwned()) {
throw new UnsupportedOperationException(e);
}
throw e;
}
} }
return this; return this;
} }
@ -103,7 +112,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public boolean isDirect() { public boolean isDirect() {
return buffer.nativeAddress() != 0; return hasMemoryAddress;
} }
@Override @Override
@ -186,6 +195,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf discardReadBytes() { public ByteBuf discardReadBytes() {
checkAccess();
buffer.compact(); buffer.compact();
return this; return this;
} }
@ -197,9 +207,10 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf ensureWritable(int minWritableBytes) { public ByteBuf ensureWritable(int minWritableBytes) {
try { checkAccess();
if (writableBytes() < minWritableBytes) { if (writableBytes() < minWritableBytes) {
int borrows = buffer.countBorrows(); int borrows = buffer.countBorrows();
try {
if (borrows == 0) { if (borrows == 0) {
// Good place. // Good place.
buffer.ensureWritable(minWritableBytes); buffer.ensureWritable(minWritableBytes);
@ -212,9 +223,9 @@ public final class ByteBufAdaptor extends ByteBuf {
retain(borrows); retain(borrows);
} }
} }
} catch (IllegalArgumentException e) {
throw new IndexOutOfBoundsException(e.getMessage());
} }
} catch (IllegalStateException e) {
throw new IllegalReferenceCountException(e);
} }
return this; return this;
} }
@ -453,6 +464,9 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
if (index < 0 || capacity() < index + length) {
throw new IndexOutOfBoundsException();
}
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
dst[dstIndex + i] = getByte(index + i); dst[dstIndex + i] = getByte(index + i);
} }
@ -636,9 +650,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf setBytes(int index, ByteBuf src) { public ByteBuf setBytes(int index, ByteBuf src) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
while (src.isReadable() && index < capacity()) { while (src.isReadable() && index < capacity()) {
setByte(index++, src.readByte()); setByte(index++, src.readByte());
} }
@ -647,6 +659,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf setBytes(int index, ByteBuf src, int length) { public ByteBuf setBytes(int index, ByteBuf src, int length) {
checkAccess();
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
setByte(index + i, src.readByte()); setByte(index + i, src.readByte());
} }
@ -685,6 +698,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int setBytes(int index, InputStream in, int length) throws IOException { public int setBytes(int index, InputStream in, int length) throws IOException {
checkAccess();
byte[] bytes = in.readNBytes(length); byte[] bytes = in.readNBytes(length);
setBytes(index, bytes, 0, length); setBytes(index, bytes, 0, length);
return bytes.length; return bytes.length;
@ -692,6 +706,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
checkAccess();
ByteBuffer transfer = ByteBuffer.allocate(length); ByteBuffer transfer = ByteBuffer.allocate(length);
int bytes = in.read(transfer); int bytes = in.read(transfer);
transfer.flip(); transfer.flip();
@ -701,6 +716,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int setBytes(int index, FileChannel in, long position, int length) throws IOException { public int setBytes(int index, FileChannel in, long position, int length) throws IOException {
checkAccess();
ByteBuffer transfer = ByteBuffer.allocate(length); ByteBuffer transfer = ByteBuffer.allocate(length);
int bytes = in.read(transfer, position); int bytes = in.read(transfer, position);
transfer.flip(); transfer.flip();
@ -925,8 +941,9 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf readBytes(int length) { public ByteBuf readBytes(int length) {
Buffer copy = preferredBufferAllocator().allocate(length); Buffer copy = preferredBufferAllocator().allocate(length);
buffer.copyInto(0, copy, 0, length); buffer.copyInto(0, copy, readerIndex(), length);
return wrap(copy); readerIndex(readerIndex() + length);
return wrap(copy).writerIndex(length);
} }
@Override @Override
@ -1262,6 +1279,9 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf writeZero(int length) { public ByteBuf writeZero(int length) {
if (length < 0) {
throw new IllegalArgumentException();
}
ensureWritable(length); ensureWritable(length);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
writeByte(0); writeByte(0);
@ -1278,7 +1298,32 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int indexOf(int fromIndex, int toIndex, byte value) { public int indexOf(int fromIndex, int toIndex, byte value) {
for (int i = fromIndex; i < toIndex; i++) { if (!buffer.isAccessible()) {
return -1;
}
int inc, start, end;
if (fromIndex <= toIndex) {
inc = 1;
start = fromIndex;
end = toIndex - 1;
if (start < 0) {
start = 0; // Required to pass regression tests.
}
if (capacity() <= end) {
throw new IndexOutOfBoundsException();
}
} else {
inc = -1;
start = fromIndex - 1;
end = toIndex;
if (capacity() <= start) {
start = capacity() - 1; // Required to pass regression tests.
}
if (end < 0) {
throw new IndexOutOfBoundsException();
}
}
for (int i = start; i != end; i += inc) {
if (getByte(i) == value) { if (getByte(i) == value) {
return i; return i;
} }
@ -1307,9 +1352,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int forEachByte(ByteProcessor processor) { public int forEachByte(ByteProcessor processor) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
int index = readerIndex(); int index = readerIndex();
int bytes = buffer.openCursor().process(processor); int bytes = buffer.openCursor().process(processor);
return bytes == -1 ? -1 : index + bytes; return bytes == -1 ? -1 : index + bytes;
@ -1317,18 +1360,14 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int forEachByte(int index, int length, ByteProcessor processor) { public int forEachByte(int index, int length, ByteProcessor processor) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
int bytes = buffer.openCursor(index, length).process(processor); int bytes = buffer.openCursor(index, length).process(processor);
return bytes == -1 ? -1 : index + bytes; return bytes == -1 ? -1 : index + bytes;
} }
@Override @Override
public int forEachByteDesc(ByteProcessor processor) { public int forEachByteDesc(ByteProcessor processor) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
int index = readerIndex(); int index = readerIndex();
int bytes = buffer.openReverseCursor().process(processor); int bytes = buffer.openReverseCursor().process(processor);
return bytes == -1 ? -1 : index - bytes; return bytes == -1 ? -1 : index - bytes;
@ -1336,9 +1375,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public int forEachByteDesc(int index, int length, ByteProcessor processor) { public int forEachByteDesc(int index, int length, ByteProcessor processor) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
int bytes = buffer.openReverseCursor(index, length).process(processor); int bytes = buffer.openReverseCursor(index, length).process(processor);
return bytes == -1 ? -1 : index - bytes; return bytes == -1 ? -1 : index - bytes;
} }
@ -1350,9 +1387,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf copy(int index, int length) { public ByteBuf copy(int index, int length) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
try { try {
BufferAllocator allocator = preferredBufferAllocator(); BufferAllocator allocator = preferredBufferAllocator();
Buffer copy = allocator.allocate(length); Buffer copy = allocator.allocate(length);
@ -1374,9 +1409,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf retainedSlice() { public ByteBuf retainedSlice() {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
return wrap(buffer.slice()); return wrap(buffer.slice());
} }
@ -1389,6 +1422,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf retainedSlice(int index, int length) { public ByteBuf retainedSlice(int index, int length) {
checkAccess();
try { try {
return wrap(buffer.slice(index, length)); return wrap(buffer.slice(index, length));
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
@ -1405,7 +1439,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuf retainedDuplicate() { public ByteBuf retainedDuplicate() {
return retainedSlice(0, capacity()); return retainedSlice(0, capacity()).setIndex(readerIndex(), writerIndex());
} }
@Override @Override
@ -1420,9 +1454,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuffer nioBuffer(int index, int length) { public ByteBuffer nioBuffer(int index, int length) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
ByteBuffer copy = isDirect() ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length); ByteBuffer copy = isDirect() ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length);
while (index < length) { while (index < length) {
copy.put(getByte(index++)); copy.put(getByte(index++));
@ -1432,9 +1464,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public ByteBuffer internalNioBuffer(int index, int length) { public ByteBuffer internalNioBuffer(int index, int length) {
if (!buffer.isAccessible()) { checkAccess();
throw new IllegalReferenceCountException();
}
if (readerIndex() <= index && index < writerIndex() && length <= readableBytes()) { if (readerIndex() <= index && index < writerIndex() && length <= readableBytes()) {
// We wish to read from the internal buffer. // We wish to read from the internal buffer.
if (buffer.countReadableComponents() != 1) { if (buffer.countReadableComponents() != 1) {
@ -1504,7 +1534,7 @@ public final class ByteBufAdaptor extends ByteBuf {
@Override @Override
public boolean hasMemoryAddress() { public boolean hasMemoryAddress() {
return buffer.nativeAddress() != 0; return hasMemoryAddress;
} }
@Override @Override
@ -1612,6 +1642,12 @@ public final class ByteBufAdaptor extends ByteBuf {
return !buffer.isAccessible(); return !buffer.isAccessible();
} }
private void checkAccess() {
if (!buffer.isAccessible()) {
throw new IllegalReferenceCountException();
}
}
private ByteBufAdaptor wrap(Buffer copy) { private ByteBufAdaptor wrap(Buffer copy) {
return new ByteBufAdaptor(alloc, copy); return new ByteBufAdaptor(alloc, copy);
} }

View File

@ -20,6 +20,8 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.api.BufferAllocator; import io.netty.buffer.api.BufferAllocator;
import static java.nio.ByteOrder.BIG_ENDIAN;
public class ByteBufAllocatorAdaptor implements ByteBufAllocator, AutoCloseable { public class ByteBufAllocatorAdaptor implements ByteBufAllocator, AutoCloseable {
private final BufferAllocator onheap; private final BufferAllocator onheap;
private final BufferAllocator offheap; private final BufferAllocator offheap;
@ -53,7 +55,7 @@ public class ByteBufAllocatorAdaptor implements ByteBufAllocator, AutoCloseable
@Override @Override
public ByteBuf buffer(int initialCapacity) { public ByteBuf buffer(int initialCapacity) {
return new ByteBufAdaptor(this, onheap.allocate(initialCapacity)); return new ByteBufAdaptor(this, onheap.allocate(initialCapacity).order(BIG_ENDIAN));
} }
@Override @Override
@ -98,7 +100,7 @@ public class ByteBufAllocatorAdaptor implements ByteBufAllocator, AutoCloseable
@Override @Override
public ByteBuf directBuffer(int initialCapacity) { public ByteBuf directBuffer(int initialCapacity) {
return new ByteBufAdaptor(this, offheap.allocate(initialCapacity)); return new ByteBufAdaptor(this, offheap.allocate(initialCapacity).order(BIG_ENDIAN));
} }
@Override @Override

View File

@ -1,9 +1,25 @@
/*
* Copyright 2021 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:
*
* https://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.api.adaptor; package io.netty.buffer.api.adaptor;
import io.netty.buffer.AbstractByteBufTest; import io.netty.buffer.AbstractByteBufTest;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
public class ByteBufAdaptorTest extends AbstractByteBufTest { public class ByteBufAdaptorTest extends AbstractByteBufTest {
static ByteBufAllocatorAdaptor alloc; static ByteBufAllocatorAdaptor alloc;
@ -22,4 +38,34 @@ public class ByteBufAdaptorTest extends AbstractByteBufTest {
protected ByteBuf newBuffer(int capacity, int maxCapacity) { protected ByteBuf newBuffer(int capacity, int maxCapacity) {
return alloc.buffer(capacity, capacity); return alloc.buffer(capacity, capacity);
} }
@Ignore("new buffers not thread-safe like this")
@Override
public void testSliceReadGatheringByteChannelMultipleThreads() throws Exception {
}
@Ignore("new buffers not thread-safe like this")
@Override
public void testDuplicateReadGatheringByteChannelMultipleThreads() throws Exception {
}
@Ignore("new buffers not thread-safe like this")
@Override
public void testSliceReadOutputStreamMultipleThreads() throws Exception {
}
@Ignore("new buffers not thread-safe like this")
@Override
public void testDuplicateReadOutputStreamMultipleThreads() throws Exception {
}
@Ignore("new buffers not thread-safe like this")
@Override
public void testSliceBytesInArrayMultipleThreads() throws Exception {
}
@Ignore("new buffers not thread-safe like this")
@Override
public void testDuplicateBytesInArrayMultipleThreads() throws Exception {
}
} }