Make ByteCursor implementations static final inner classes (#11662)

Motivation:
People might be tempted to use mocking tools like Mockito.spy() on the ByteCursors.
By returning instances where the concrete classes are final, we will be forcing integrators to use stub-like wrappers instead.
Such stubs are more well-behaved since they are implemented in terms of the real instance.
This prevents the mocked objects from (easily) producing behaviour that violates the API specification.

Modification:
All ByteCursor implementations have changed from using anonymous inner classes, to using static-final named inner classes.

Result:
The concrete ByteCursor classes can no longer be extended via byte code generation, such as from mocking tools.
This commit is contained in:
Chris Vest 2021-09-08 09:04:16 +02:00 committed by GitHub
parent 3a23094b81
commit 3cbb41a478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 303 additions and 237 deletions

View File

@ -518,54 +518,7 @@ public final class CompositeBuffer extends ResourceSupport<Buffer, CompositeBuff
int off = fromOffset - offsets[startBufferIndex]; int off = fromOffset - offsets[startBufferIndex];
Buffer startBuf = bufs[startBufferIndex]; Buffer startBuf = bufs[startBufferIndex];
ByteCursor startCursor = startBuf.openCursor(off, Math.min(startBuf.capacity() - off, length)); ByteCursor startCursor = startBuf.openCursor(off, Math.min(startBuf.capacity() - off, length));
return new ByteCursor() { return new ForwardCompositeByteCursor(bufs, fromOffset, length, startBufferIndex, startCursor);
int index = fromOffset;
final int end = fromOffset + length;
int bufferIndex = startBufferIndex;
int initOffset = startCursor.currentOffset();
ByteCursor cursor = startCursor;
byte byteValue = -1;
@Override
public boolean readByte() {
if (cursor.readByte()) {
byteValue = cursor.getByte();
return true;
}
if (bytesLeft() > 0) {
nextCursor();
cursor.readByte();
byteValue = cursor.getByte();
return true;
}
return false;
}
private void nextCursor() {
bufferIndex++;
Buffer nextBuf = bufs[bufferIndex];
cursor = nextBuf.openCursor(0, Math.min(nextBuf.capacity(), bytesLeft()));
initOffset = 0;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
int currOff = cursor.currentOffset();
index += currOff - initOffset;
initOffset = currOff;
return index;
}
@Override
public int bytesLeft() {
return end - currentOffset();
}
};
} }
@Override @Override
@ -584,56 +537,7 @@ public final class CompositeBuffer extends ResourceSupport<Buffer, CompositeBuff
int off = fromOffset - offsets[startBufferIndex]; int off = fromOffset - offsets[startBufferIndex];
Buffer startBuf = bufs[startBufferIndex]; Buffer startBuf = bufs[startBufferIndex];
ByteCursor startCursor = startBuf.openReverseCursor(off, Math.min(off + 1, length)); ByteCursor startCursor = startBuf.openReverseCursor(off, Math.min(off + 1, length));
return new ByteCursor() { return new ReverseCompositeByteCursor(bufs, fromOffset, length, startBufferIndex, startCursor);
int index = fromOffset;
final int end = fromOffset - length;
int bufferIndex = startBufferIndex;
int initOffset = startCursor.currentOffset();
ByteCursor cursor = startCursor;
byte byteValue = -1;
@Override
public boolean readByte() {
if (cursor.readByte()) {
byteValue = cursor.getByte();
return true;
}
if (bytesLeft() > 0) {
nextCursor();
cursor.readByte();
byteValue = cursor.getByte();
return true;
}
return false;
}
private void nextCursor() {
bufferIndex--;
Buffer nextBuf = bufs[bufferIndex];
int length = Math.min(nextBuf.capacity(), bytesLeft());
int offset = nextBuf.capacity() - 1;
cursor = nextBuf.openReverseCursor(offset, length);
initOffset = offset;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
int currOff = cursor.currentOffset();
index -= initOffset - currOff;
initOffset = currOff;
return index;
}
@Override
public int bytesLeft() {
return currentOffset() - end;
}
};
} }
@Override @Override
@ -1732,4 +1636,128 @@ public final class CompositeBuffer extends ResourceSupport<Buffer, CompositeBuff
} }
} }
// </editor-fold> // </editor-fold>
private static final class ForwardCompositeByteCursor implements ByteCursor {
final Buffer[] bufs;
int index;
final int end;
int bufferIndex;
int initOffset;
ByteCursor cursor;
byte byteValue;
ForwardCompositeByteCursor(Buffer[] bufs, int fromOffset, int length, int startBufferIndex,
ByteCursor startCursor) {
this.bufs = bufs;
index = fromOffset;
end = fromOffset + length;
bufferIndex = startBufferIndex;
initOffset = startCursor.currentOffset();
cursor = startCursor;
byteValue = -1;
}
@Override
public boolean readByte() {
if (cursor.readByte()) {
byteValue = cursor.getByte();
return true;
}
if (bytesLeft() > 0) {
nextCursor();
cursor.readByte();
byteValue = cursor.getByte();
return true;
}
return false;
}
private void nextCursor() {
bufferIndex++;
Buffer nextBuf = bufs[bufferIndex];
cursor = nextBuf.openCursor(0, Math.min(nextBuf.capacity(), bytesLeft()));
initOffset = 0;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
int currOff = cursor.currentOffset();
index += currOff - initOffset;
initOffset = currOff;
return index;
}
@Override
public int bytesLeft() {
return end - currentOffset();
}
}
private static final class ReverseCompositeByteCursor implements ByteCursor {
final Buffer[] bufs;
int index;
final int end;
int bufferIndex;
int initOffset;
ByteCursor cursor;
byte byteValue;
ReverseCompositeByteCursor(Buffer[] bufs, int fromOffset, int length,
int startBufferIndex, ByteCursor startCursor) {
this.bufs = bufs;
index = fromOffset;
end = fromOffset - length;
bufferIndex = startBufferIndex;
initOffset = startCursor.currentOffset();
cursor = startCursor;
byteValue = -1;
}
@Override
public boolean readByte() {
if (cursor.readByte()) {
byteValue = cursor.getByte();
return true;
}
if (bytesLeft() > 0) {
nextCursor();
cursor.readByte();
byteValue = cursor.getByte();
return true;
}
return false;
}
private void nextCursor() {
bufferIndex--;
Buffer nextBuf = bufs[bufferIndex];
int length = Math.min(nextBuf.capacity(), bytesLeft());
int offset = nextBuf.capacity() - 1;
cursor = nextBuf.openReverseCursor(offset, length);
initOffset = offset;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
int currOff = cursor.currentOffset();
index -= initOffset - currOff;
initOffset = currOff;
return index;
}
@Override
public int bytesLeft() {
return currentOffset() - end;
}
}
} }

View File

@ -253,38 +253,7 @@ class NioBuffer extends AdaptableBuffer<NioBuffer> implements ReadableComponent,
throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " + throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " +
"fromOffset = " + fromOffset + ", length = " + length + '.'); "fromOffset = " + fromOffset + ", length = " + length + '.');
} }
return new ByteCursor() { return new ForwardNioByteCursor(rmem, fromOffset, length);
// Duplicate source buffer to keep our own byte order state.
final ByteBuffer buffer = rmem.duplicate().order(ByteOrder.BIG_ENDIAN);
int index = fromOffset;
final int end = index + length;
byte byteValue = -1;
@Override
public boolean readByte() {
if (index < end) {
byteValue = buffer.get(index);
index++;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return end - index;
}
};
} }
@Override @Override
@ -305,37 +274,7 @@ class NioBuffer extends AdaptableBuffer<NioBuffer> implements ReadableComponent,
throw new IllegalArgumentException("The fromOffset - length would underflow the buffer: " + throw new IllegalArgumentException("The fromOffset - length would underflow the buffer: " +
"fromOffset = " + fromOffset + ", length = " + length + '.'); "fromOffset = " + fromOffset + ", length = " + length + '.');
} }
return new ByteCursor() { return new ReverseNioByteCursor(rmem, fromOffset, length);
final ByteBuffer buffer = rmem.duplicate().order(ByteOrder.LITTLE_ENDIAN);
int index = fromOffset;
final int end = index - length;
byte byteValue = -1;
@Override
public boolean readByte() {
if (index > end) {
byteValue = buffer.get(index);
index--;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return index - end;
}
};
} }
@Override @Override
@ -1104,4 +1043,83 @@ class NioBuffer extends AdaptableBuffer<NioBuffer> implements ReadableComponent,
ByteBuffer recoverable() { ByteBuffer recoverable() {
return base; return base;
} }
private static final class ForwardNioByteCursor implements ByteCursor {
// Duplicate source buffer to keep our own byte order state.
final ByteBuffer buffer;
int index;
final int end;
byte byteValue;
ForwardNioByteCursor(ByteBuffer rmem, int fromOffset, int length) {
buffer = rmem.duplicate().order(ByteOrder.BIG_ENDIAN);
index = fromOffset;
end = index + length;
byteValue = -1;
}
@Override
public boolean readByte() {
if (index < end) {
byteValue = buffer.get(index);
index++;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return end - index;
}
}
private static final class ReverseNioByteCursor implements ByteCursor {
final ByteBuffer buffer;
int index;
final int end;
byte byteValue;
ReverseNioByteCursor(ByteBuffer rmem, int fromOffset, int length) {
buffer = rmem.duplicate().order(ByteOrder.LITTLE_ENDIAN);
index = fromOffset;
end = index - length;
byteValue = -1;
}
@Override
public boolean readByte() {
if (index > end) {
byteValue = buffer.get(index);
index--;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return index - end;
}
}
} }

View File

@ -269,43 +269,7 @@ class UnsafeBuffer extends AdaptableBuffer<UnsafeBuffer> implements ReadableComp
throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " + throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " +
"fromOffset = " + fromOffset + ", length = " + length + '.'); "fromOffset = " + fromOffset + ", length = " + length + '.');
} }
return new ByteCursor() { return new ForwardUnsafeByteCursor(memory, base, address, fromOffset, length);
final UnsafeMemory memory = UnsafeBuffer.this.memory; // Keep memory alive.
final Object baseObj = base;
final long baseAddress = address;
int index = fromOffset;
final int end = index + length;
byte byteValue = -1;
@Override
public boolean readByte() {
if (index < end) {
try {
byteValue = PlatformDependent.getByte(baseObj, baseAddress + index);
} finally {
Reference.reachabilityFence(memory);
}
index++;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return end - index;
}
};
} }
@Override @Override
@ -326,43 +290,7 @@ class UnsafeBuffer extends AdaptableBuffer<UnsafeBuffer> implements ReadableComp
throw new IllegalArgumentException("The fromOffset - length would underflow the buffer: " + throw new IllegalArgumentException("The fromOffset - length would underflow the buffer: " +
"fromOffset = " + fromOffset + ", length = " + length + '.'); "fromOffset = " + fromOffset + ", length = " + length + '.');
} }
return new ByteCursor() { return new ReverseUnsafeByteCursor(memory, base, address, fromOffset, length);
final UnsafeMemory memory = UnsafeBuffer.this.memory; // Keep memory alive.
final Object baseObj = base;
final long baseAddress = address;
int index = fromOffset;
final int end = index - length;
byte byteValue = -1;
@Override
public boolean readByte() {
if (index > end) {
try {
byteValue = PlatformDependent.getByte(baseObj, baseAddress + index);
} finally {
Reference.reachabilityFence(memory);
}
index--;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return index - end;
}
};
} }
@Override @Override
@ -1511,4 +1439,98 @@ class UnsafeBuffer extends AdaptableBuffer<UnsafeBuffer> implements ReadableComp
Object recover() { Object recover() {
return memory; return memory;
} }
private static final class ForwardUnsafeByteCursor implements ByteCursor {
final UnsafeMemory memory; // Keep memory alive.
final Object baseObj;
final long baseAddress;
int index;
final int end;
byte byteValue;
ForwardUnsafeByteCursor(UnsafeMemory memory, Object base, long address, int fromOffset, int length) {
this.memory = memory;
baseObj = base;
baseAddress = address;
index = fromOffset;
end = index + length;
byteValue = -1;
}
@Override
public boolean readByte() {
if (index < end) {
try {
byteValue = PlatformDependent.getByte(baseObj, baseAddress + index);
} finally {
Reference.reachabilityFence(memory);
}
index++;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return end - index;
}
}
private static final class ReverseUnsafeByteCursor implements ByteCursor {
final UnsafeMemory memory; // Keep memory alive.
final Object baseObj;
final long baseAddress;
int index;
final int end;
byte byteValue;
ReverseUnsafeByteCursor(UnsafeMemory memory, Object base, long address, int fromOffset, int length) {
this.memory = memory;
baseObj = base;
baseAddress = address;
index = fromOffset;
end = index - length;
byteValue = -1;
}
@Override
public boolean readByte() {
if (index > end) {
try {
byteValue = PlatformDependent.getByte(baseObj, baseAddress + index);
} finally {
Reference.reachabilityFence(memory);
}
index--;
return true;
}
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
public int currentOffset() {
return index;
}
@Override
public int bytesLeft() {
return index - end;
}
}
} }

View File

@ -95,7 +95,6 @@ public abstract class AbstractByteBufAllocatorTest<T extends AbstractByteBufAllo
assertTrue(clazz.isInstance(buffer instanceof SimpleLeakAwareByteBuf ? buffer.unwrap() : buffer)); assertTrue(clazz.isInstance(buffer instanceof SimpleLeakAwareByteBuf ? buffer.unwrap() : buffer));
} }
@SuppressWarnings("unchecked")
@Test @Test
public void testUsedDirectMemory() { public void testUsedDirectMemory() {
T allocator = newAllocator(true); T allocator = newAllocator(true);
@ -114,7 +113,6 @@ public abstract class AbstractByteBufAllocatorTest<T extends AbstractByteBufAllo
assertEquals(expectedUsedMemoryAfterRelease(allocator, capacity), metric.usedDirectMemory()); assertEquals(expectedUsedMemoryAfterRelease(allocator, capacity), metric.usedDirectMemory());
} }
@SuppressWarnings("unchecked")
@Test @Test
public void testUsedHeapMemory() { public void testUsedHeapMemory() {
T allocator = newAllocator(true); T allocator = newAllocator(true);