Turn ByteIterator into ByteCursor

Motivation:
Cursors are better than iterators in that they only need to check boundary conditions once per iteration, when processed in a loop.
This should make them easier for the compiler to optimise.

Modification:
Change the ByteIterator to a ByteCursor. The API is almost the same, but with a few subtle differences in semantics.
The primary difference is that the cursor movement and boundary condition checking and position movement happen at the same time, and do not need to occur when the values are fetched out of the cursor.
An iterator, on the other hand, needs to throw an exception if "next" is called too many times.

Result:
Simpler code, and hopefully faster code as well.
This commit is contained in:
Chris Vest 2020-12-02 14:29:40 +01:00
parent 3aeebdd058
commit 6cc49c1c62
6 changed files with 386 additions and 366 deletions

View File

@ -302,9 +302,9 @@ public interface Buf extends Rc<Buf>, BufAccessors {
* {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while
* the iteration takes place. Otherwise unpredictable behaviour might result.
*
* @return A {@link ByteIterator} for the readable bytes of this buffer.
* @return A {@link ByteCursor} for the readable bytes of this buffer.
*/
default ByteIterator iterate() {
default ByteCursor iterate() {
return iterate(readerOffset(), readableBytes());
}
@ -320,11 +320,11 @@ public interface Buf extends Rc<Buf>, BufAccessors {
* @param fromOffset The offset into the buffer where iteration should start.
* The first byte read from the iterator will be the byte at this offset.
* @param length The number of bytes to iterate.
* @return A {@link ByteIterator} for the given stretch of bytes of this buffer.
* @return A {@link ByteCursor} for the given stretch of bytes of this buffer.
* @throws IllegalArgumentException if the length is negative, or if the region given by the {@code fromOffset} and
* the {@code length} reaches outside of the bounds of this buffer.
*/
ByteIterator iterate(int fromOffset, int length);
ByteCursor iterate(int fromOffset, int length);
/**
* Iterate the readable bytes of this buffer, in reverse. The {@linkplain #readerOffset() reader offset} and
@ -334,9 +334,9 @@ public interface Buf extends Rc<Buf>, BufAccessors {
* {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while
* the iteration takes place. Otherwise unpredictable behaviour might result.
*
* @return A {@link ByteIterator} for the readable bytes of this buffer.
* @return A {@link ByteCursor} for the readable bytes of this buffer.
*/
default ByteIterator iterateReverse() {
default ByteCursor iterateReverse() {
int woff = writerOffset();
return iterateReverse(woff == 0? 0 : woff - 1, readableBytes());
}
@ -353,11 +353,11 @@ public interface Buf extends Rc<Buf>, BufAccessors {
* @param fromOffset The offset into the buffer where iteration should start.
* The first byte read from the iterator will be the byte at this offset.
* @param length The number of bytes to iterate.
* @return A {@link ByteIterator} for the given stretch of bytes of this buffer.
* @return A {@link ByteCursor} for the given stretch of bytes of this buffer.
* @throws IllegalArgumentException if the length is negative, or if the region given by the {@code fromOffset} and
* the {@code length} reaches outside of the bounds of this buffer.
*/
ByteIterator iterateReverse(int fromOffset, int length);
ByteCursor iterateReverse(int fromOffset, int length);
/**
* Ensure that this buffer has {@linkplain #writableBytes() available space for writing} the given number of

View File

@ -17,48 +17,55 @@ package io.netty.buffer.api;
import io.netty.util.ByteProcessor;
/**
* The ByteIterator scans through a sequence of bytes.
* The ByteCursor scans through a sequence of bytes.
* This is similar to {@link ByteProcessor}, but for external iteration rather than internal iteration.
* The external iteration allows the callers to control the pace of the iteration.
* The API includes methods for reading {@code long}s as a batch of 8 bytes.
* The long values are always in big-endian format, so that the highest-order byte in the long value, contain the byte
* that would otherwise have been returned by the next call to {@link #nextByte()}.
* that would otherwise have been returned by the next call to {@link #getByte()}.
*/
public interface ByteIterator {
public interface ByteCursor {
/**
* Check if the iterator has at least 8 bytes left.
* Note that when this method returns {@code false}, the {@link #hasNextByte()} can still return {@code true}.
* Check if the iterator has at least 8 bytes left, and if so, read those 8 bytes and move the cursor forward.
* The bytes are packed as a {@code long} value in big-endian format, such that the highest-order byte
* in the long, is the byte that would otherwise have been returned by the next call to {@link #getByte()},
* after a call to {@link #nextByte()}.
* The bytes (as a {@code long}) will then be available through the {@link #getLong()} method.
* <p>
* Note that when this method returns {@code false}, the {@link #nextByte()} can still return {@code true}.
* It is recommended to have any long-processing loop be followed by a byte-processing loop for the 7 or fewer
* bytes that might form a tail in the iterator.
* bytes that might form a tail in the cursor.
* <p>
* Also note that this method will not influence what is returned the {@link #getByte()} method.
*
* @return {@code true} if a call to {@link #nextLong()} would succeed, otherwise {@code false}.
* @return {@code true} if the cursor read 8 bytes and moved forward, otherwise {@code false}.
*/
boolean hasNextLong();
boolean nextLong();
/**
* Read and return the next 8 bytes, and move the iterator position forward by 8 bytes.
* The bytes are packed and return as a {@code long} value in big-endian format, such that the highest-order byte
* in the long, is the byte that would otherwise have been returned by the next call to {@link #nextByte()}.
* Return the last 8 bytes read by {@link #nextLong()}.
*
* @return The next 8 bytes in big-endian format.
* @throws java.util.NoSuchElementException If the iterator has fewer than 8 bytes left.
* @return The 8 bytes, in big-endian format, that was read by the most recent successful call to
* {@link #nextLong()}.
*/
long nextLong();
long getLong();
/**
* Check if the iterator has at least one byte left.
* Check if the iterator has at least one byte left, and if so, read that byte and move the cursor forward.
* The byte will then be available through the {@link #getByte()}.
* <p>
* Note that this method will not influence what is returned from the {@link #getLong()} method.
*
* @return {@code true} if the next call to {@link #nextByte()} would succeed, otherwise {@code false}.
* @return {@code true} if the cursor read a byte and moved forward, otherwise {@code false}.
*/
boolean hasNextByte();
boolean nextByte();
/**
* Read and return the next byte, and move the iterator position] forward by one byte.
* Return the last byte that was read by {@link #nextByte()}.
*
* @return The next byte.
* @throws java.util.NoSuchElementException If the iterator has no more bytes left.
* @return The next byte that was read by the most recent successful call to {@link #nextByte()}.
*/
byte nextByte();
byte getByte();
/**
* The current position of this iterator into the underlying sequence of bytes.
@ -85,14 +92,10 @@ public interface ByteIterator {
*/
default int process(ByteProcessor processor) {
boolean requestMore = true;
int index = currentOffset();
if (hasNextByte()) {
byte val = nextByte();
while ((requestMore = processor.process(val)) && hasNextByte()) {
val = nextByte();
index++;
}
int count = 0;
while (nextByte() && (requestMore = processor.process(getByte()))) {
count++;
}
return requestMore? -1 : index;
return requestMore? -1 : count;
}
}

View File

@ -293,18 +293,17 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
// Iterate in reverse to account for src and dest buffer overlap.
// todo optimise by delegating to constituent buffers.
var itr = iterateReverse(srcPos + length - 1, length);
var cursor = iterateReverse(srcPos + length - 1, length);
ByteOrder prevOrder = dest.order();
// We read longs in BE, in reverse, so they need to be flipped for writing.
dest.order(ByteOrder.LITTLE_ENDIAN);
try {
while (itr.hasNextLong()) {
long val = itr.nextLong();
while (cursor.nextLong()) {
length -= Long.BYTES;
dest.setLong(destPos + length, val);
dest.setLong(destPos + length, cursor.getLong());
}
while (itr.hasNextByte()) {
dest.setByte(destPos + --length, itr.nextByte());
while (cursor.nextByte()) {
dest.setByte(destPos + --length, cursor.getByte());
}
} finally {
dest.order(prevOrder);
@ -312,7 +311,7 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
}
@Override
public ByteIterator iterate(int fromOffset, int length) {
public ByteCursor iterate(int fromOffset, int length) {
if (fromOffset < 0) {
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
}
@ -326,61 +325,63 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
int startBufferIndex = searchOffsets(fromOffset);
int off = fromOffset - offsets[startBufferIndex];
Buf startBuf = bufs[startBufferIndex];
ByteIterator startIterator = startBuf.iterate(off, Math.min(startBuf.capacity() - off, length));
return new ByteIterator() {
ByteCursor startCursor = startBuf.iterate(off, Math.min(startBuf.capacity() - off, length));
return new ByteCursor() {
int index = fromOffset;
final int end = fromOffset + length;
int bufferIndex = startBufferIndex;
ByteIterator itr = startIterator;
ByteCursor cursor = startCursor;
long longValue = -1;
byte byteValue = -1;
@Override
public boolean hasNextLong() {
return bytesLeft() >= Long.BYTES;
}
@Override
public long nextLong() {
if (itr.hasNextLong()) {
long val = itr.nextLong();
index += Long.BYTES;
return val;
public boolean nextLong() {
if (bytesLeft() >= Long.BYTES) {
if (cursor.nextLong()) {
longValue = cursor.getLong();
index += Long.BYTES;
} else {
longValue = nextLongFromBytes(); // Leave index increments to 'nextByte'
}
return true;
}
if (!hasNextLong()) {
throw new NoSuchElementException();
}
return nextLongFromBytes(); // Leave index increments to 'nextByte'
return false;
}
private long nextLongFromBytes() {
long val = 0;
for (int i = 0; i < 8; i++) {
nextByte();
val <<= 8;
val |= nextByte();
val |= getByte();
}
return val;
}
@Override
public boolean hasNextByte() {
return index < end;
public long getLong() {
return longValue;
}
@Override
public byte nextByte() {
if (itr.hasNextByte()) {
byte val = itr.nextByte();
public boolean nextByte() {
if (index < end) {
if (!cursor.nextByte()) {
bufferIndex++;
Buf nextBuf = bufs[bufferIndex];
cursor = nextBuf.iterate(0, Math.min(nextBuf.capacity(), bytesLeft()));
cursor.nextByte();
}
byteValue = cursor.getByte();
index++;
return val;
return true;
}
if (!hasNextByte()) {
throw new NoSuchElementException();
}
bufferIndex++;
Buf nextBuf = bufs[bufferIndex];
itr = nextBuf.iterate(0, Math.min(nextBuf.capacity(), bytesLeft()));
byte val = itr.nextByte();
index++;
return val;
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
@ -396,7 +397,7 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
}
@Override
public ByteIterator iterateReverse(int fromOffset, int length) {
public ByteCursor iterateReverse(int fromOffset, int length) {
if (fromOffset < 0) {
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
}
@ -410,60 +411,64 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
int startBufferIndex = searchOffsets(fromOffset);
int off = fromOffset - offsets[startBufferIndex];
Buf startBuf = bufs[startBufferIndex];
ByteIterator startIterator = startBuf.iterateReverse(off, Math.min(off + 1, length));
return new ByteIterator() {
ByteCursor startCursor = startBuf.iterateReverse(off, Math.min(off + 1, length));
return new ByteCursor() {
int index = fromOffset;
final int end = fromOffset - length;
int bufferIndex = startBufferIndex;
ByteIterator itr = startIterator;
ByteCursor cursor = startCursor;
long longValue = -1;
byte byteValue = -1;
@Override
public boolean hasNextLong() {
return bytesLeft() >= Long.BYTES;
}
@Override
public long nextLong() {
if (itr.hasNextLong()) {
index -= Long.BYTES;
return itr.nextLong();
public boolean nextLong() {
if (bytesLeft() >= Long.BYTES) {
if (cursor.nextLong()) {
index -= Long.BYTES;
longValue = cursor.getLong();
} else {
longValue = nextLongFromBytes(); // Leave index increments to 'nextByte'
}
return true;
}
if (!hasNextLong()) {
throw new NoSuchElementException();
}
return nextLongFromBytes(); // Leave index increments to 'nextByte'
return false;
}
private long nextLongFromBytes() {
long val = 0;
for (int i = 0; i < 8; i++) {
nextByte();
val <<= 8;
val |= nextByte();
val |= getByte();
}
return val;
}
@Override
public boolean hasNextByte() {
return index > end;
public long getLong() {
return longValue;
}
@Override
public byte nextByte() {
if (itr.hasNextByte()) {
byte val = itr.nextByte();
public boolean nextByte() {
if (index > end) {
if (!cursor.nextByte()) {
bufferIndex--;
Buf nextBuf = bufs[bufferIndex];
int length = Math.min(nextBuf.capacity(), bytesLeft());
cursor = nextBuf.iterateReverse(nextBuf.capacity() - 1, length);
cursor.nextByte();
}
byteValue = cursor.getByte();
index--;
return val;
return true;
}
if (!hasNextByte()) {
throw new NoSuchElementException();
}
bufferIndex--;
Buf nextBuf = bufs[bufferIndex];
itr = nextBuf.iterateReverse(nextBuf.capacity() - 1, Math.min(nextBuf.capacity(), bytesLeft()));
byte val = itr.nextByte();
index--;
return val;
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override

View File

@ -18,7 +18,7 @@ package io.netty.buffer.api.memseg;
import io.netty.buffer.api.Allocator;
import io.netty.buffer.api.AllocatorControl;
import io.netty.buffer.api.Buf;
import io.netty.buffer.api.ByteIterator;
import io.netty.buffer.api.ByteCursor;
import io.netty.buffer.api.Drop;
import io.netty.buffer.api.Owned;
import io.netty.buffer.api.RcSupport;
@ -163,13 +163,13 @@ class MemSegBuf extends RcSupport<Buf, MemSegBuf> implements Buf {
// We read longs in BE, in reverse, so they need to be flipped for writing.
dest.order(ByteOrder.LITTLE_ENDIAN);
try {
while (itr.hasNextLong()) {
long val = itr.nextLong();
while (itr.nextLong()) {
long val = itr.getLong();
length -= Long.BYTES;
dest.setLong(destPos + length, val);
}
while (itr.hasNextByte()) {
dest.setByte(destPos + --length, itr.nextByte());
while (itr.nextByte()) {
dest.setByte(destPos + --length, itr.getByte());
}
} finally {
dest.order(prevOrder);
@ -177,7 +177,7 @@ class MemSegBuf extends RcSupport<Buf, MemSegBuf> implements Buf {
}
@Override
public ByteIterator iterate(int fromOffset, int length) {
public ByteCursor iterate(int fromOffset, int length) {
if (fromOffset < 0) {
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
}
@ -188,39 +188,41 @@ class MemSegBuf extends RcSupport<Buf, MemSegBuf> implements Buf {
throw new IllegalArgumentException("The fromOffset+length is beyond the end of the buffer: " +
"fromOffset=" + fromOffset + ", length=" + length + '.');
}
return new ByteIterator() {
return new ByteCursor() {
final MemorySegment segment = seg;
int index = fromOffset;
final int end = index + length;
long longValue = -1;
byte byteValue = -1;
@Override
public boolean hasNextLong() {
return index + Long.BYTES <= end;
}
@Override
public long nextLong() {
if (!hasNextLong()) {
throw new NoSuchElementException("No 'long' value at offet " + currentOffset() + '.');
public boolean nextLong() {
if (index + Long.BYTES <= end) {
longValue = getLongAtOffset(segment, index, ByteOrder.BIG_ENDIAN);
index += Long.BYTES;
return true;
}
long val = getLongAtOffset(segment, index, ByteOrder.BIG_ENDIAN);
index += Long.BYTES;
return val;
return false;
}
@Override
public boolean hasNextByte() {
return index < end;
public long getLong() {
return longValue;
}
@Override
public byte nextByte() {
if (!hasNextByte()) {
throw new NoSuchElementException("No 'byte' value at offet " + currentOffset() + '.');
public boolean nextByte() {
if (index < end) {
byteValue = getByteAtOffset(segment, index);
index++;
return true;
}
byte val = getByteAtOffset(segment, index);
index++;
return val;
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override
@ -236,7 +238,7 @@ class MemSegBuf extends RcSupport<Buf, MemSegBuf> implements Buf {
}
@Override
public ByteIterator iterateReverse(int fromOffset, int length) {
public ByteCursor iterateReverse(int fromOffset, int length) {
if (fromOffset < 0) {
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
}
@ -250,40 +252,42 @@ class MemSegBuf extends RcSupport<Buf, MemSegBuf> implements Buf {
throw new IllegalArgumentException("The fromOffset-length would underflow the buffer: " +
"fromOffset=" + fromOffset + ", length=" + length + '.');
}
return new ByteIterator() {
return new ByteCursor() {
final MemorySegment segment = seg;
int index = fromOffset;
final int end = index - length;
long longValue = -1;
byte byteValue = -1;
@Override
public boolean hasNextLong() {
return index - Long.BYTES >= end;
}
@Override
public long nextLong() {
if (!hasNextLong()) {
throw new NoSuchElementException("No 'long' value at offet " + currentOffset() + '.');
public boolean nextLong() {
if (index - Long.BYTES >= end) {
index -= 7;
longValue = getLongAtOffset(segment, index, ByteOrder.LITTLE_ENDIAN);
index--;
return true;
}
index -= 7;
long val = getLongAtOffset(segment, index, ByteOrder.LITTLE_ENDIAN);
index--;
return val;
return false;
}
@Override
public boolean hasNextByte() {
return index > end;
public long getLong() {
return longValue;
}
@Override
public byte nextByte() {
if (!hasNextByte()) {
throw new NoSuchElementException("No 'byte' value at offet " + currentOffset() + '.');
public boolean nextByte() {
if (index > end) {
byteValue = getByteAtOffset(segment, index);
index--;
return true;
}
byte val = getByteAtOffset(segment, index);
index--;
return val;
return false;
}
@Override
public byte getByte() {
return byteValue;
}
@Override

View File

@ -1051,59 +1051,62 @@ public class BufTest {
}
private static void checkByteIteration(Buf buf) {
var itr = buf.iterate();
assertFalse(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
var cursor = buf.iterate();
assertFalse(cursor.nextByte());
assertFalse(cursor.nextLong());
assertEquals(0, cursor.bytesLeft());
assertEquals((byte) -1, cursor.getByte());
assertEquals(-1L, cursor.getLong());
for (int i = 0; i < 0x27; i++) {
buf.writeByte((byte) (i + 1));
}
int roff = buf.readerOffset();
int woff = buf.writerOffset();
itr = buf.iterate();
assertEquals(0x27, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertTrue(itr.hasNextLong());
assertEquals(0x0102030405060708L, itr.nextLong());
assertEquals(0x1F, itr.bytesLeft());
assertTrue(itr.hasNextLong());
assertEquals(0x090A0B0C0D0E0F10L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x17, itr.bytesLeft());
assertEquals(0x1112131415161718L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x0F, itr.bytesLeft());
assertEquals(0x191A1B1C1D1E1F20L, itr.nextLong());
assertFalse(itr.hasNextLong());
assertEquals(7, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertTrue(itr.hasNextByte());
assertEquals((byte) 0x21, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(6, itr.bytesLeft());
assertEquals((byte) 0x22, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(5, itr.bytesLeft());
assertEquals((byte) 0x23, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(4, itr.bytesLeft());
assertEquals((byte) 0x24, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(3, itr.bytesLeft());
assertEquals((byte) 0x25, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(2, itr.bytesLeft());
assertEquals((byte) 0x26, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(1, itr.bytesLeft());
assertEquals((byte) 0x27, itr.nextByte());
assertFalse(itr.hasNextByte());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
cursor = buf.iterate();
assertEquals(0x27, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x01, cursor.getByte());
assertEquals((byte) 0x01, cursor.getByte());
assertTrue(cursor.nextLong());
assertEquals(0x0203040506070809L, cursor.getLong());
assertEquals(0x0203040506070809L, cursor.getLong());
assertEquals(0x1E, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x0A0B0C0D0E0F1011L, cursor.getLong());
assertEquals(0x16, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x1213141516171819L, cursor.getLong());
assertEquals(0x0E, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x1A1B1C1D1E1F2021L, cursor.getLong());
assertEquals(6, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertEquals(6, cursor.bytesLeft());
assertEquals(0x1A1B1C1D1E1F2021L, cursor.getLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x22, cursor.getByte());
assertEquals((byte) 0x22, cursor.getByte());
assertEquals(5, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x23, cursor.getByte());
assertEquals(4, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x24, cursor.getByte());
assertEquals(3, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x25, cursor.getByte());
assertEquals(2, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x26, cursor.getByte());
assertEquals(1, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x27, cursor.getByte());
assertEquals(0, cursor.bytesLeft());
assertFalse(cursor.nextByte());
assertEquals((byte) 0x27, cursor.getByte());
assertEquals((byte) 0x27, cursor.getByte());
assertFalse(cursor.nextLong());
assertEquals(roff, buf.readerOffset());
assertEquals(woff, buf.writerOffset());
}
@ -1115,66 +1118,67 @@ public class BufTest {
assertThrows(IllegalArgumentException.class, () -> buf.iterate(buf.capacity() - 1, 2));
assertThrows(IllegalArgumentException.class, () -> buf.iterate(buf.capacity() - 2, 3));
var itr = buf.iterate(1, 0);
assertFalse(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
var cursor = buf.iterate(1, 0);
assertFalse(cursor.nextByte());
assertFalse(cursor.nextLong());
assertEquals(0, cursor.bytesLeft());
assertEquals((byte) -1, cursor.getByte());
assertEquals(-1L, cursor.getLong());
for (int i = 0; i < 0x27; i++) {
buf.writeByte((byte) (i + 1));
}
int roff = buf.readerOffset();
int woff = buf.writerOffset();
itr = buf.iterate(buf.readerOffset() + 1, buf.readableBytes() - 2);
assertEquals(0x25, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertTrue(itr.hasNextLong());
assertEquals(0x0203040506070809L, itr.nextLong());
assertEquals(0x1D, itr.bytesLeft());
assertTrue(itr.hasNextLong());
assertEquals(0x0A0B0C0D0E0F1011L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x15, itr.bytesLeft());
assertEquals(0x1213141516171819L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x0D, itr.bytesLeft());
assertEquals(0x1A1B1C1D1E1F2021L, itr.nextLong());
assertFalse(itr.hasNextLong());
assertEquals(5, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertTrue(itr.hasNextByte());
assertEquals((byte) 0x22, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(4, itr.bytesLeft());
assertEquals((byte) 0x23, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(3, itr.bytesLeft());
assertEquals((byte) 0x24, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(2, itr.bytesLeft());
assertEquals((byte) 0x25, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(1, itr.bytesLeft());
assertEquals((byte) 0x26, itr.nextByte());
assertFalse(itr.hasNextByte());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
cursor = buf.iterate(buf.readerOffset() + 1, buf.readableBytes() - 2);
assertEquals(0x25, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x02, cursor.getByte());
assertEquals((byte) 0x02, cursor.getByte());
assertTrue(cursor.nextLong());
assertEquals(0x030405060708090AL, cursor.getLong());
assertEquals(0x1C, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x0B0C0D0E0F101112L, cursor.getLong());
assertEquals(0x14, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x131415161718191AL, cursor.getLong());
assertEquals(0x0C, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x1B1C1D1E1F202122L, cursor.getLong());
assertEquals(4, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertEquals(4, cursor.bytesLeft());
assertEquals(0x1B1C1D1E1F202122L, cursor.getLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x23, cursor.getByte());
assertEquals(3, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x24, cursor.getByte());
assertEquals(2, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x25, cursor.getByte());
assertEquals(1, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x26, cursor.getByte());
assertEquals(0, cursor.bytesLeft());
assertFalse(cursor.nextByte());
assertEquals(0, cursor.bytesLeft());
assertEquals(0x1B1C1D1E1F202122L, cursor.getLong());
assertEquals((byte) 0x26, cursor.getByte());
itr = buf.iterate(buf.readerOffset() + 1, 2);
assertEquals(2, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals((byte) 0x02, itr.nextByte());
assertEquals(1, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals((byte) 0x03, itr.nextByte());
assertEquals(0, itr.bytesLeft());
assertFalse(itr.hasNextByte());
assertFalse(itr.hasNextLong());
cursor = buf.iterate(buf.readerOffset() + 1, 2);
assertEquals(2, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x02, cursor.getByte());
assertEquals(1, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x03, cursor.getByte());
assertEquals(0, cursor.bytesLeft());
assertFalse(cursor.nextByte());
assertFalse(cursor.nextLong());
assertEquals(roff, buf.readerOffset());
assertEquals(woff, buf.writerOffset());
}
@ -1204,59 +1208,61 @@ public class BufTest {
}
private static void checkReverseByteIteration(Buf buf) {
var itr = buf.iterateReverse();
assertFalse(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
var cursor = buf.iterateReverse();
assertFalse(cursor.nextByte());
assertFalse(cursor.nextLong());
assertEquals(0, cursor.bytesLeft());
assertEquals((byte) -1, cursor.getByte());
assertEquals(-1L, cursor.getLong());
for (int i = 0; i < 0x27; i++) {
buf.writeByte((byte) (i + 1));
}
int roff = buf.readerOffset();
int woff = buf.writerOffset();
itr = buf.iterateReverse();
assertEquals(0x27, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertTrue(itr.hasNextLong());
assertEquals(0x2726252423222120L, itr.nextLong());
assertEquals(0x1F, itr.bytesLeft());
assertTrue(itr.hasNextLong());
assertEquals(0x1F1E1D1C1B1A1918L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x17, itr.bytesLeft());
assertEquals(0x1716151413121110L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x0F, itr.bytesLeft());
assertEquals(0x0F0E0D0C0B0A0908L, itr.nextLong());
assertFalse(itr.hasNextLong());
assertEquals(7, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertTrue(itr.hasNextByte());
assertEquals((byte) 0x07, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(6, itr.bytesLeft());
assertEquals((byte) 0x06, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(5, itr.bytesLeft());
assertEquals((byte) 0x05, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(4, itr.bytesLeft());
assertEquals((byte) 0x04, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(3, itr.bytesLeft());
assertEquals((byte) 0x03, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(2, itr.bytesLeft());
assertEquals((byte) 0x02, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(1, itr.bytesLeft());
assertEquals((byte) 0x01, itr.nextByte());
assertFalse(itr.hasNextByte());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
cursor = buf.iterateReverse();
assertEquals(0x27, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x27, cursor.getByte());
assertEquals((byte) 0x27, cursor.getByte());
assertTrue(cursor.nextLong());
assertEquals(0x262524232221201FL, cursor.getLong());
assertEquals(0x1E, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x1E1D1C1B1A191817L, cursor.getLong());
assertEquals(0x16, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x161514131211100FL, cursor.getLong());
assertEquals(0x0E, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x0E0D0C0B0A090807L, cursor.getLong());
assertEquals(6, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertEquals(6, cursor.bytesLeft());
assertEquals(0x0E0D0C0B0A090807L, cursor.getLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x06, cursor.getByte());
assertEquals(5, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x05, cursor.getByte());
assertEquals(4, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x04, cursor.getByte());
assertEquals(3, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x03, cursor.getByte());
assertEquals(2, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x02, cursor.getByte());
assertEquals(1, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x01, cursor.getByte());
assertEquals(0, cursor.bytesLeft());
assertFalse(cursor.nextByte());
assertEquals((byte) 0x01, cursor.getByte());
assertFalse(cursor.nextByte());
assertEquals(0, cursor.bytesLeft());
assertEquals(0x0E0D0C0B0A090807L, cursor.getLong());
assertEquals(roff, buf.readerOffset());
assertEquals(woff, buf.writerOffset());
}
@ -1268,66 +1274,68 @@ public class BufTest {
assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(1, 3));
assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(buf.capacity(), 0));
var itr = buf.iterateReverse(1, 0);
assertFalse(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
var cursor = buf.iterateReverse(1, 0);
assertFalse(cursor.nextByte());
assertFalse(cursor.nextLong());
assertEquals(0, cursor.bytesLeft());
assertEquals((byte) -1, cursor.getByte());
assertEquals(-1L, cursor.getLong());
for (int i = 0; i < 0x27; i++) {
buf.writeByte((byte) (i + 1));
}
int roff = buf.readerOffset();
int woff = buf.writerOffset();
itr = buf.iterateReverse(buf.writerOffset() - 2, buf.readableBytes() - 2);
assertEquals(0x25, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertTrue(itr.hasNextLong());
assertEquals(0x262524232221201FL, itr.nextLong());
assertEquals(0x1D, itr.bytesLeft());
assertTrue(itr.hasNextLong());
assertEquals(0x1E1D1C1B1A191817L, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x15, itr.bytesLeft());
assertEquals(0x161514131211100FL, itr.nextLong());
assertTrue(itr.hasNextLong());
assertEquals(0x0D, itr.bytesLeft());
assertEquals(0x0E0D0C0B0A090807L, itr.nextLong());
assertFalse(itr.hasNextLong());
assertEquals(5, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertTrue(itr.hasNextByte());
assertEquals((byte) 0x06, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(4, itr.bytesLeft());
assertEquals((byte) 0x05, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(3, itr.bytesLeft());
assertEquals((byte) 0x04, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(2, itr.bytesLeft());
assertEquals((byte) 0x03, itr.nextByte());
assertTrue(itr.hasNextByte());
assertEquals(1, itr.bytesLeft());
assertEquals((byte) 0x02, itr.nextByte());
assertFalse(itr.hasNextByte());
assertEquals(0, itr.bytesLeft());
assertThrows(NoSuchElementException.class, itr::nextLong);
assertThrows(NoSuchElementException.class, itr::nextByte);
cursor = buf.iterateReverse(buf.writerOffset() - 2, buf.readableBytes() - 2);
assertEquals(0x25, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x26, cursor.getByte());
assertEquals((byte) 0x26, cursor.getByte());
assertTrue(cursor.nextLong());
assertEquals(0x2524232221201F1EL, cursor.getLong());
assertEquals(0x1C, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x1D1C1B1A19181716L, cursor.getLong());
assertEquals(0x14, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x1514131211100F0EL, cursor.getLong());
assertEquals(0x0C, cursor.bytesLeft());
assertTrue(cursor.nextLong());
assertEquals(0x0D0C0B0A09080706L, cursor.getLong());
assertEquals(4, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertEquals(4, cursor.bytesLeft());
assertEquals(0x0D0C0B0A09080706L, cursor.getLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x05, cursor.getByte());
assertEquals(3, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x04, cursor.getByte());
assertEquals(2, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x03, cursor.getByte());
assertEquals(1, cursor.bytesLeft());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x02, cursor.getByte());
assertEquals(0, cursor.bytesLeft());
assertFalse(cursor.nextByte());
assertEquals((byte) 0x02, cursor.getByte());
assertFalse(cursor.nextByte());
assertEquals(0, cursor.bytesLeft());
assertEquals(0x0D0C0B0A09080706L, cursor.getLong());
itr = buf.iterateReverse(buf.readerOffset() + 2, 2);
assertEquals(2, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals((byte) 0x03, itr.nextByte());
assertEquals(1, itr.bytesLeft());
assertTrue(itr.hasNextByte());
assertFalse(itr.hasNextLong());
assertEquals((byte) 0x02, itr.nextByte());
assertEquals(0, itr.bytesLeft());
assertFalse(itr.hasNextByte());
assertFalse(itr.hasNextLong());
cursor = buf.iterateReverse(buf.readerOffset() + 2, 2);
assertEquals(2, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x03, cursor.getByte());
assertEquals(1, cursor.bytesLeft());
assertFalse(cursor.nextLong());
assertTrue(cursor.nextByte());
assertEquals((byte) 0x02, cursor.getByte());
assertEquals(0, cursor.bytesLeft());
assertFalse(cursor.nextByte());
assertFalse(cursor.nextLong());
assertEquals(roff, buf.readerOffset());
assertEquals(woff, buf.writerOffset());
}

View File

@ -94,11 +94,11 @@ public class ByteIterationBenchmark {
public long sum() {
var itr = buf.iterate();
long sum = 0;
while (itr.hasNextLong()) {
sum += itr.nextLong();
while (itr.nextLong()) {
sum += itr.getLong();
}
while (itr.hasNextByte()) {
sum += itr.nextByte();
while (itr.nextByte()) {
sum += itr.getByte();
}
return sum;
}
@ -107,11 +107,11 @@ public class ByteIterationBenchmark {
public long sumReverse() {
var itr = buf.iterateReverse();
long sum = 0;
while (itr.hasNextLong()) {
sum += itr.nextLong();
while (itr.nextLong()) {
sum += itr.getLong();
}
while (itr.hasNextByte()) {
sum += itr.nextByte();
while (itr.nextByte()) {
sum += itr.getByte();
}
return sum;
}