From 6cc49c1c620d172154d6c8fee273d659827a51f0 Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Wed, 2 Dec 2020 14:29:40 +0100 Subject: [PATCH 1/4] 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. --- src/main/java/io/netty/buffer/api/Buf.java | 16 +- .../{ByteIterator.java => ByteCursor.java} | 61 +-- .../io/netty/buffer/api/CompositeBuf.java | 151 +++---- .../io/netty/buffer/api/memseg/MemSegBuf.java | 100 ++--- .../java/io/netty/buffer/api/BufTest.java | 408 +++++++++--------- .../benchmarks/ByteIterationBenchmark.java | 16 +- 6 files changed, 386 insertions(+), 366 deletions(-) rename src/main/java/io/netty/buffer/api/{ByteIterator.java => ByteCursor.java} (57%) diff --git a/src/main/java/io/netty/buffer/api/Buf.java b/src/main/java/io/netty/buffer/api/Buf.java index f95d0d7..6359c6f 100644 --- a/src/main/java/io/netty/buffer/api/Buf.java +++ b/src/main/java/io/netty/buffer/api/Buf.java @@ -302,9 +302,9 @@ public interface Buf extends Rc, 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, 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, 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, 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 diff --git a/src/main/java/io/netty/buffer/api/ByteIterator.java b/src/main/java/io/netty/buffer/api/ByteCursor.java similarity index 57% rename from src/main/java/io/netty/buffer/api/ByteIterator.java rename to src/main/java/io/netty/buffer/api/ByteCursor.java index dbf40c0..cc8277d 100644 --- a/src/main/java/io/netty/buffer/api/ByteIterator.java +++ b/src/main/java/io/netty/buffer/api/ByteCursor.java @@ -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. + *

+ * 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. + *

+ * 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()}. + *

+ * 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; } } diff --git a/src/main/java/io/netty/buffer/api/CompositeBuf.java b/src/main/java/io/netty/buffer/api/CompositeBuf.java index 3492b0e..8aa68d5 100644 --- a/src/main/java/io/netty/buffer/api/CompositeBuf.java +++ b/src/main/java/io/netty/buffer/api/CompositeBuf.java @@ -293,18 +293,17 @@ final class CompositeBuf extends RcSupport 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 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 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 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 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 diff --git a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java index 3367c57..8383f53 100644 --- a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java +++ b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java @@ -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 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 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 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 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 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 diff --git a/src/test/java/io/netty/buffer/api/BufTest.java b/src/test/java/io/netty/buffer/api/BufTest.java index 70d027f..9bcf334 100644 --- a/src/test/java/io/netty/buffer/api/BufTest.java +++ b/src/test/java/io/netty/buffer/api/BufTest.java @@ -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()); } diff --git a/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java b/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java index a0bfe37..65896c7 100644 --- a/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java +++ b/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java @@ -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; } From b2d0231f2756ec2ee768f82a53b26ae5349b0479 Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Mon, 7 Dec 2020 14:30:40 +0100 Subject: [PATCH 2/4] Specify the behaviour of ByteCursor.getByte and getLong, when relevant next* methods haven't been called --- src/main/java/io/netty/buffer/api/ByteCursor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/netty/buffer/api/ByteCursor.java b/src/main/java/io/netty/buffer/api/ByteCursor.java index cc8277d..66a5ee8 100644 --- a/src/main/java/io/netty/buffer/api/ByteCursor.java +++ b/src/main/java/io/netty/buffer/api/ByteCursor.java @@ -44,6 +44,7 @@ public interface ByteCursor { /** * Return the last 8 bytes read by {@link #nextLong()}. + * If {@link #nextLong()} has not been called on this cursor before, then {@code -1} is returned. * * @return The 8 bytes, in big-endian format, that was read by the most recent successful call to * {@link #nextLong()}. @@ -62,6 +63,7 @@ public interface ByteCursor { /** * Return the last byte that was read by {@link #nextByte()}. + * If {@link #nextByte()} has not been called on this cursor before, then {@code -1} is returned. * * @return The next byte that was read by the most recent successful call to {@link #nextByte()}. */ From a7701c04b58f737964310eaaaff29748d50fa32a Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Mon, 7 Dec 2020 14:56:03 +0100 Subject: [PATCH 3/4] Update ByteCursor method names and related javadocs --- src/main/java/io/netty/buffer/api/Buf.java | 55 +++--- .../java/io/netty/buffer/api/ByteCursor.java | 22 +-- .../io/netty/buffer/api/CompositeBuf.java | 47 +++-- .../io/netty/buffer/api/memseg/MemSegBuf.java | 19 +- .../java/io/netty/buffer/api/BufTest.java | 183 +++++++++--------- .../benchmarks/ByteIterationBenchmark.java | 12 +- 6 files changed, 168 insertions(+), 170 deletions(-) diff --git a/src/main/java/io/netty/buffer/api/Buf.java b/src/main/java/io/netty/buffer/api/Buf.java index 6359c6f..946f473 100644 --- a/src/main/java/io/netty/buffer/api/Buf.java +++ b/src/main/java/io/netty/buffer/api/Buf.java @@ -295,27 +295,27 @@ public interface Buf extends Rc, BufAccessors { } /** - * Iterate the readable bytes of this buffer. The {@linkplain #readerOffset() reader offset} and - * {@linkplain #writerOffset() witer offset} are not modified by the iterator. + * Open a cursor to iterate the readable bytes of this buffer. The {@linkplain #readerOffset() reader offset} and + * {@linkplain #writerOffset() witer offset} are not modified by the cursor. *

- * Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the - * {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while - * the iteration takes place. Otherwise unpredictable behaviour might result. + * Care should be taken to ensure that the buffers lifetime extends beyond the cursor and the iteration, and that + * the {@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 ByteCursor} for the readable bytes of this buffer. + * @return A {@link ByteCursor} for iterating the readable bytes of this buffer. */ - default ByteCursor iterate() { - return iterate(readerOffset(), readableBytes()); + default ByteCursor openCursor() { + return openCursor(readerOffset(), readableBytes()); } /** - * Iterate the given number bytes of this buffer, starting at the given offset. + * Open a cursor to iterate the given number bytes of this buffer, starting at the given offset. * The {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() witer offset} are not modified by - * the iterator. + * the cursor. *

- * Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the - * {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while - * the iteration takes place. Otherwise unpredictable behaviour might result. + * Care should be taken to ensure that the buffers lifetime extends beyond the cursor and the iteration, and that + * the {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified + * while the iteration takes place. Otherwise unpredictable behaviour might result. * * @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. @@ -324,31 +324,32 @@ public interface Buf extends Rc, BufAccessors { * @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. */ - ByteCursor iterate(int fromOffset, int length); + ByteCursor openCursor(int fromOffset, int length); /** - * Iterate the readable bytes of this buffer, in reverse. The {@linkplain #readerOffset() reader offset} and - * {@linkplain #writerOffset() witer offset} are not modified by the iterator. + * Open a cursor to iterate the readable bytes of this buffer, in reverse. + * The {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() witer offset} are not modified by + * the cursor. *

- * Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the - * {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while - * the iteration takes place. Otherwise unpredictable behaviour might result. + * Care should be taken to ensure that the buffers lifetime extends beyond the cursor and the iteration, and that + * the {@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 ByteCursor} for the readable bytes of this buffer. */ - default ByteCursor iterateReverse() { + default ByteCursor openReverseCursor() { int woff = writerOffset(); - return iterateReverse(woff == 0? 0 : woff - 1, readableBytes()); + return openReverseCursor(woff == 0? 0 : woff - 1, readableBytes()); } /** - * Iterate the given number bytes of this buffer, in reverse, starting at the given offset. + * Open a cursor to iterate the given number bytes of this buffer, in reverse, starting at the given offset. * The {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() witer offset} are not modified by - * the iterator. + * the cursor. *

- * Care should be taken to ensure that the buffers lifetime extends beyond the iteration, and the - * {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified while - * the iteration takes place. Otherwise unpredictable behaviour might result. + * Care should be taken to ensure that the buffers lifetime extends beyond the cursor and the iteration, and that + * the {@linkplain #readerOffset() reader offset} and {@linkplain #writerOffset() writer offset} are not modified + * while the iteration takes place. Otherwise unpredictable behaviour might result. * * @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. @@ -357,7 +358,7 @@ public interface Buf extends Rc, BufAccessors { * @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. */ - ByteCursor iterateReverse(int fromOffset, int length); + ByteCursor openReverseCursor(int fromOffset, int length); /** * Ensure that this buffer has {@linkplain #writableBytes() available space for writing} the given number of diff --git a/src/main/java/io/netty/buffer/api/ByteCursor.java b/src/main/java/io/netty/buffer/api/ByteCursor.java index 66a5ee8..f94bdeb 100644 --- a/src/main/java/io/netty/buffer/api/ByteCursor.java +++ b/src/main/java/io/netty/buffer/api/ByteCursor.java @@ -29,10 +29,10 @@ public interface ByteCursor { * 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()}. + * after a call to {@link #readByte()}. * The bytes (as a {@code long}) will then be available through the {@link #getLong()} method. *

- * Note that when this method returns {@code false}, the {@link #nextByte()} can still return {@code true}. + * Note that when this method returns {@code false}, the {@link #readByte()} 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 cursor. *

@@ -40,14 +40,14 @@ public interface ByteCursor { * * @return {@code true} if the cursor read 8 bytes and moved forward, otherwise {@code false}. */ - boolean nextLong(); + boolean readLong(); /** - * Return the last 8 bytes read by {@link #nextLong()}. - * If {@link #nextLong()} has not been called on this cursor before, then {@code -1} is returned. + * Return the last 8 bytes read by {@link #readLong()}. + * If {@link #readLong()} has not been called on this cursor before, then {@code -1} is returned. * * @return The 8 bytes, in big-endian format, that was read by the most recent successful call to - * {@link #nextLong()}. + * {@link #readLong()}. */ long getLong(); @@ -59,13 +59,13 @@ public interface ByteCursor { * * @return {@code true} if the cursor read a byte and moved forward, otherwise {@code false}. */ - boolean nextByte(); + boolean readByte(); /** - * Return the last byte that was read by {@link #nextByte()}. - * If {@link #nextByte()} has not been called on this cursor before, then {@code -1} is returned. + * Return the last byte that was read by {@link #readByte()}. + * If {@link #readByte()} has not been called on this cursor before, then {@code -1} is returned. * - * @return The next byte that was read by the most recent successful call to {@link #nextByte()}. + * @return The next byte that was read by the most recent successful call to {@link #readByte()}. */ byte getByte(); @@ -95,7 +95,7 @@ public interface ByteCursor { default int process(ByteProcessor processor) { boolean requestMore = true; int count = 0; - while (nextByte() && (requestMore = processor.process(getByte()))) { + while (readByte() && (requestMore = processor.process(getByte()))) { count++; } return requestMore? -1 : count; diff --git a/src/main/java/io/netty/buffer/api/CompositeBuf.java b/src/main/java/io/netty/buffer/api/CompositeBuf.java index 8aa68d5..d030d9a 100644 --- a/src/main/java/io/netty/buffer/api/CompositeBuf.java +++ b/src/main/java/io/netty/buffer/api/CompositeBuf.java @@ -18,7 +18,6 @@ package io.netty.buffer.api; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; -import java.util.NoSuchElementException; import java.util.Objects; final class CompositeBuf extends RcSupport implements Buf { @@ -293,16 +292,16 @@ final class CompositeBuf extends RcSupport implements Buf { // Iterate in reverse to account for src and dest buffer overlap. // todo optimise by delegating to constituent buffers. - var cursor = iterateReverse(srcPos + length - 1, length); + var cursor = openReverseCursor(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 (cursor.nextLong()) { + while (cursor.readLong()) { length -= Long.BYTES; dest.setLong(destPos + length, cursor.getLong()); } - while (cursor.nextByte()) { + while (cursor.readByte()) { dest.setByte(destPos + --length, cursor.getByte()); } } finally { @@ -311,7 +310,7 @@ final class CompositeBuf extends RcSupport implements Buf { } @Override - public ByteCursor iterate(int fromOffset, int length) { + public ByteCursor openCursor(int fromOffset, int length) { if (fromOffset < 0) { throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.'); } @@ -325,7 +324,7 @@ final class CompositeBuf extends RcSupport implements Buf { int startBufferIndex = searchOffsets(fromOffset); int off = fromOffset - offsets[startBufferIndex]; Buf startBuf = bufs[startBufferIndex]; - ByteCursor startCursor = startBuf.iterate(off, Math.min(startBuf.capacity() - off, length)); + ByteCursor startCursor = startBuf.openCursor(off, Math.min(startBuf.capacity() - off, length)); return new ByteCursor() { int index = fromOffset; final int end = fromOffset + length; @@ -335,13 +334,13 @@ final class CompositeBuf extends RcSupport implements Buf { byte byteValue = -1; @Override - public boolean nextLong() { + public boolean readLong() { if (bytesLeft() >= Long.BYTES) { - if (cursor.nextLong()) { + if (cursor.readLong()) { longValue = cursor.getLong(); index += Long.BYTES; } else { - longValue = nextLongFromBytes(); // Leave index increments to 'nextByte' + longValue = nextLongFromBytes(); // Leave index increments to 'readByte' } return true; } @@ -351,7 +350,7 @@ final class CompositeBuf extends RcSupport implements Buf { private long nextLongFromBytes() { long val = 0; for (int i = 0; i < 8; i++) { - nextByte(); + readByte(); val <<= 8; val |= getByte(); } @@ -364,13 +363,13 @@ final class CompositeBuf extends RcSupport implements Buf { } @Override - public boolean nextByte() { + public boolean readByte() { if (index < end) { - if (!cursor.nextByte()) { + if (!cursor.readByte()) { bufferIndex++; Buf nextBuf = bufs[bufferIndex]; - cursor = nextBuf.iterate(0, Math.min(nextBuf.capacity(), bytesLeft())); - cursor.nextByte(); + cursor = nextBuf.openCursor(0, Math.min(nextBuf.capacity(), bytesLeft())); + cursor.readByte(); } byteValue = cursor.getByte(); index++; @@ -397,7 +396,7 @@ final class CompositeBuf extends RcSupport implements Buf { } @Override - public ByteCursor iterateReverse(int fromOffset, int length) { + public ByteCursor openReverseCursor(int fromOffset, int length) { if (fromOffset < 0) { throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.'); } @@ -411,7 +410,7 @@ final class CompositeBuf extends RcSupport implements Buf { int startBufferIndex = searchOffsets(fromOffset); int off = fromOffset - offsets[startBufferIndex]; Buf startBuf = bufs[startBufferIndex]; - ByteCursor startCursor = startBuf.iterateReverse(off, Math.min(off + 1, length)); + ByteCursor startCursor = startBuf.openReverseCursor(off, Math.min(off + 1, length)); return new ByteCursor() { int index = fromOffset; final int end = fromOffset - length; @@ -421,13 +420,13 @@ final class CompositeBuf extends RcSupport implements Buf { byte byteValue = -1; @Override - public boolean nextLong() { + public boolean readLong() { if (bytesLeft() >= Long.BYTES) { - if (cursor.nextLong()) { + if (cursor.readLong()) { index -= Long.BYTES; longValue = cursor.getLong(); } else { - longValue = nextLongFromBytes(); // Leave index increments to 'nextByte' + longValue = nextLongFromBytes(); // Leave index increments to 'readByte' } return true; } @@ -437,7 +436,7 @@ final class CompositeBuf extends RcSupport implements Buf { private long nextLongFromBytes() { long val = 0; for (int i = 0; i < 8; i++) { - nextByte(); + readByte(); val <<= 8; val |= getByte(); } @@ -450,14 +449,14 @@ final class CompositeBuf extends RcSupport implements Buf { } @Override - public boolean nextByte() { + public boolean readByte() { if (index > end) { - if (!cursor.nextByte()) { + if (!cursor.readByte()) { bufferIndex--; Buf nextBuf = bufs[bufferIndex]; int length = Math.min(nextBuf.capacity(), bytesLeft()); - cursor = nextBuf.iterateReverse(nextBuf.capacity() - 1, length); - cursor.nextByte(); + cursor = nextBuf.openReverseCursor(nextBuf.capacity() - 1, length); + cursor.readByte(); } byteValue = cursor.getByte(); index--; diff --git a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java index 8383f53..cc5f2fb 100644 --- a/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java +++ b/src/main/java/io/netty/buffer/api/memseg/MemSegBuf.java @@ -26,7 +26,6 @@ import jdk.incubator.foreign.MemorySegment; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.NoSuchElementException; import static jdk.incubator.foreign.MemoryAccess.getByteAtOffset; import static jdk.incubator.foreign.MemoryAccess.getCharAtOffset; @@ -158,17 +157,17 @@ class MemSegBuf extends RcSupport implements Buf { public void copyInto(int srcPos, Buf dest, int destPos, int length) { // todo optimise: specialise for MemSegBuf. // Iterate in reverse to account for src and dest buffer overlap. - var itr = iterateReverse(srcPos + length - 1, length); + var itr = openReverseCursor(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.nextLong()) { + while (itr.readLong()) { long val = itr.getLong(); length -= Long.BYTES; dest.setLong(destPos + length, val); } - while (itr.nextByte()) { + while (itr.readByte()) { dest.setByte(destPos + --length, itr.getByte()); } } finally { @@ -177,7 +176,7 @@ class MemSegBuf extends RcSupport implements Buf { } @Override - public ByteCursor iterate(int fromOffset, int length) { + public ByteCursor openCursor(int fromOffset, int length) { if (fromOffset < 0) { throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.'); } @@ -196,7 +195,7 @@ class MemSegBuf extends RcSupport implements Buf { byte byteValue = -1; @Override - public boolean nextLong() { + public boolean readLong() { if (index + Long.BYTES <= end) { longValue = getLongAtOffset(segment, index, ByteOrder.BIG_ENDIAN); index += Long.BYTES; @@ -211,7 +210,7 @@ class MemSegBuf extends RcSupport implements Buf { } @Override - public boolean nextByte() { + public boolean readByte() { if (index < end) { byteValue = getByteAtOffset(segment, index); index++; @@ -238,7 +237,7 @@ class MemSegBuf extends RcSupport implements Buf { } @Override - public ByteCursor iterateReverse(int fromOffset, int length) { + public ByteCursor openReverseCursor(int fromOffset, int length) { if (fromOffset < 0) { throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.'); } @@ -260,7 +259,7 @@ class MemSegBuf extends RcSupport implements Buf { byte byteValue = -1; @Override - public boolean nextLong() { + public boolean readLong() { if (index - Long.BYTES >= end) { index -= 7; longValue = getLongAtOffset(segment, index, ByteOrder.LITTLE_ENDIAN); @@ -276,7 +275,7 @@ class MemSegBuf extends RcSupport implements Buf { } @Override - public boolean nextByte() { + public boolean readByte() { if (index > end) { byteValue = getByteAtOffset(segment, index); index--; diff --git a/src/test/java/io/netty/buffer/api/BufTest.java b/src/test/java/io/netty/buffer/api/BufTest.java index 9bcf334..0e4dbba 100644 --- a/src/test/java/io/netty/buffer/api/BufTest.java +++ b/src/test/java/io/netty/buffer/api/BufTest.java @@ -32,7 +32,6 @@ import java.nio.ByteOrder; import java.text.ParseException; import java.util.Arrays; import java.util.List; -import java.util.NoSuchElementException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -1051,9 +1050,9 @@ public class BufTest { } private static void checkByteIteration(Buf buf) { - var cursor = buf.iterate(); - assertFalse(cursor.nextByte()); - assertFalse(cursor.nextLong()); + var cursor = buf.openCursor(); + assertFalse(cursor.readByte()); + assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); @@ -1063,64 +1062,64 @@ public class BufTest { } int roff = buf.readerOffset(); int woff = buf.writerOffset(); - cursor = buf.iterate(); + cursor = buf.openCursor(); assertEquals(0x27, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x01, cursor.getByte()); assertEquals((byte) 0x01, cursor.getByte()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x0203040506070809L, cursor.getLong()); assertEquals(0x0203040506070809L, cursor.getLong()); assertEquals(0x1E, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x0A0B0C0D0E0F1011L, cursor.getLong()); assertEquals(0x16, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x1213141516171819L, cursor.getLong()); assertEquals(0x0E, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x1A1B1C1D1E1F2021L, cursor.getLong()); assertEquals(6, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readLong()); assertEquals(6, cursor.bytesLeft()); assertEquals(0x1A1B1C1D1E1F2021L, cursor.getLong()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x22, cursor.getByte()); assertEquals((byte) 0x22, cursor.getByte()); assertEquals(5, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x23, cursor.getByte()); assertEquals(4, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x24, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x25, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x26, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x27, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); - assertFalse(cursor.nextByte()); + assertFalse(cursor.readByte()); assertEquals((byte) 0x27, cursor.getByte()); assertEquals((byte) 0x27, cursor.getByte()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } private static void checkByteIterationOfRegion(Buf buf) { - assertThrows(IllegalArgumentException.class, () -> buf.iterate(-1, 1)); - assertThrows(IllegalArgumentException.class, () -> buf.iterate(1, -1)); - assertThrows(IllegalArgumentException.class, () -> buf.iterate(buf.capacity(), 1)); - assertThrows(IllegalArgumentException.class, () -> buf.iterate(buf.capacity() - 1, 2)); - assertThrows(IllegalArgumentException.class, () -> buf.iterate(buf.capacity() - 2, 3)); + assertThrows(IllegalArgumentException.class, () -> buf.openCursor(-1, 1)); + assertThrows(IllegalArgumentException.class, () -> buf.openCursor(1, -1)); + assertThrows(IllegalArgumentException.class, () -> buf.openCursor(buf.capacity(), 1)); + assertThrows(IllegalArgumentException.class, () -> buf.openCursor(buf.capacity() - 1, 2)); + assertThrows(IllegalArgumentException.class, () -> buf.openCursor(buf.capacity() - 2, 3)); - var cursor = buf.iterate(1, 0); - assertFalse(cursor.nextByte()); - assertFalse(cursor.nextLong()); + var cursor = buf.openCursor(1, 0); + assertFalse(cursor.readByte()); + assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); @@ -1130,55 +1129,55 @@ public class BufTest { } int roff = buf.readerOffset(); int woff = buf.writerOffset(); - cursor = buf.iterate(buf.readerOffset() + 1, buf.readableBytes() - 2); + cursor = buf.openCursor(buf.readerOffset() + 1, buf.readableBytes() - 2); assertEquals(0x25, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals((byte) 0x02, cursor.getByte()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x030405060708090AL, cursor.getLong()); assertEquals(0x1C, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x0B0C0D0E0F101112L, cursor.getLong()); assertEquals(0x14, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x131415161718191AL, cursor.getLong()); assertEquals(0x0C, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x1B1C1D1E1F202122L, cursor.getLong()); assertEquals(4, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readLong()); assertEquals(4, cursor.bytesLeft()); assertEquals(0x1B1C1D1E1F202122L, cursor.getLong()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x23, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x24, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x25, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x26, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); - assertFalse(cursor.nextByte()); + assertFalse(cursor.readByte()); assertEquals(0, cursor.bytesLeft()); assertEquals(0x1B1C1D1E1F202122L, cursor.getLong()); assertEquals((byte) 0x26, cursor.getByte()); - cursor = buf.iterate(buf.readerOffset() + 1, 2); + cursor = buf.openCursor(buf.readerOffset() + 1, 2); assertEquals(2, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); - assertTrue(cursor.nextByte()); + assertFalse(cursor.readLong()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); - assertTrue(cursor.nextByte()); + assertFalse(cursor.readLong()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); - assertFalse(cursor.nextByte()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readByte()); + assertFalse(cursor.readLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } @@ -1208,9 +1207,9 @@ public class BufTest { } private static void checkReverseByteIteration(Buf buf) { - var cursor = buf.iterateReverse(); - assertFalse(cursor.nextByte()); - assertFalse(cursor.nextLong()); + var cursor = buf.openReverseCursor(); + assertFalse(cursor.readByte()); + assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); @@ -1220,47 +1219,47 @@ public class BufTest { } int roff = buf.readerOffset(); int woff = buf.writerOffset(); - cursor = buf.iterateReverse(); + cursor = buf.openReverseCursor(); assertEquals(0x27, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x27, cursor.getByte()); assertEquals((byte) 0x27, cursor.getByte()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x262524232221201FL, cursor.getLong()); assertEquals(0x1E, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x1E1D1C1B1A191817L, cursor.getLong()); assertEquals(0x16, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x161514131211100FL, cursor.getLong()); assertEquals(0x0E, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x0E0D0C0B0A090807L, cursor.getLong()); assertEquals(6, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readLong()); assertEquals(6, cursor.bytesLeft()); assertEquals(0x0E0D0C0B0A090807L, cursor.getLong()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x06, cursor.getByte()); assertEquals(5, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x05, cursor.getByte()); assertEquals(4, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x04, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x01, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); - assertFalse(cursor.nextByte()); + assertFalse(cursor.readByte()); assertEquals((byte) 0x01, cursor.getByte()); - assertFalse(cursor.nextByte()); + assertFalse(cursor.readByte()); assertEquals(0, cursor.bytesLeft()); assertEquals(0x0E0D0C0B0A090807L, cursor.getLong()); assertEquals(roff, buf.readerOffset()); @@ -1268,15 +1267,15 @@ public class BufTest { } private static void checkReverseByteIterationOfRegion(Buf buf) { - assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(-1, 0)); - assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(0, -1)); - assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(0, 2)); - assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(1, 3)); - assertThrows(IllegalArgumentException.class, () -> buf.iterateReverse(buf.capacity(), 0)); + assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(-1, 0)); + assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(0, -1)); + assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(0, 2)); + assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(1, 3)); + assertThrows(IllegalArgumentException.class, () -> buf.openReverseCursor(buf.capacity(), 0)); - var cursor = buf.iterateReverse(1, 0); - assertFalse(cursor.nextByte()); - assertFalse(cursor.nextLong()); + var cursor = buf.openReverseCursor(1, 0); + assertFalse(cursor.readByte()); + assertFalse(cursor.readLong()); assertEquals(0, cursor.bytesLeft()); assertEquals((byte) -1, cursor.getByte()); assertEquals(-1L, cursor.getLong()); @@ -1286,56 +1285,56 @@ public class BufTest { } int roff = buf.readerOffset(); int woff = buf.writerOffset(); - cursor = buf.iterateReverse(buf.writerOffset() - 2, buf.readableBytes() - 2); + cursor = buf.openReverseCursor(buf.writerOffset() - 2, buf.readableBytes() - 2); assertEquals(0x25, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x26, cursor.getByte()); assertEquals((byte) 0x26, cursor.getByte()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x2524232221201F1EL, cursor.getLong()); assertEquals(0x1C, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x1D1C1B1A19181716L, cursor.getLong()); assertEquals(0x14, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x1514131211100F0EL, cursor.getLong()); assertEquals(0x0C, cursor.bytesLeft()); - assertTrue(cursor.nextLong()); + assertTrue(cursor.readLong()); assertEquals(0x0D0C0B0A09080706L, cursor.getLong()); assertEquals(4, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readLong()); assertEquals(4, cursor.bytesLeft()); assertEquals(0x0D0C0B0A09080706L, cursor.getLong()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x05, cursor.getByte()); assertEquals(3, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x04, cursor.getByte()); assertEquals(2, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); - assertTrue(cursor.nextByte()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); - assertFalse(cursor.nextByte()); + assertFalse(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); - assertFalse(cursor.nextByte()); + assertFalse(cursor.readByte()); assertEquals(0, cursor.bytesLeft()); assertEquals(0x0D0C0B0A09080706L, cursor.getLong()); - cursor = buf.iterateReverse(buf.readerOffset() + 2, 2); + cursor = buf.openReverseCursor(buf.readerOffset() + 2, 2); assertEquals(2, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); - assertTrue(cursor.nextByte()); + assertFalse(cursor.readLong()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x03, cursor.getByte()); assertEquals(1, cursor.bytesLeft()); - assertFalse(cursor.nextLong()); - assertTrue(cursor.nextByte()); + assertFalse(cursor.readLong()); + assertTrue(cursor.readByte()); assertEquals((byte) 0x02, cursor.getByte()); assertEquals(0, cursor.bytesLeft()); - assertFalse(cursor.nextByte()); - assertFalse(cursor.nextLong()); + assertFalse(cursor.readByte()); + assertFalse(cursor.readLong()); assertEquals(roff, buf.readerOffset()); assertEquals(woff, buf.writerOffset()); } diff --git a/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java b/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java index 65896c7..ba3a38f 100644 --- a/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java +++ b/src/test/java/io/netty/buffer/api/benchmarks/ByteIterationBenchmark.java @@ -92,12 +92,12 @@ public class ByteIterationBenchmark { @Benchmark public long sum() { - var itr = buf.iterate(); + var itr = buf.openCursor(); long sum = 0; - while (itr.nextLong()) { + while (itr.readLong()) { sum += itr.getLong(); } - while (itr.nextByte()) { + while (itr.readByte()) { sum += itr.getByte(); } return sum; @@ -105,12 +105,12 @@ public class ByteIterationBenchmark { @Benchmark public long sumReverse() { - var itr = buf.iterateReverse(); + var itr = buf.openReverseCursor(); long sum = 0; - while (itr.nextLong()) { + while (itr.readLong()) { sum += itr.getLong(); } - while (itr.nextByte()) { + while (itr.readByte()) { sum += itr.getByte(); } return sum; From 4960e8b3e75f1fe339d009891264b88a02178047 Mon Sep 17 00:00:00 2001 From: Chris Vest Date: Mon, 7 Dec 2020 16:00:45 +0100 Subject: [PATCH 4/4] Make ByteCursors from CompositeBuf slightly faster --- .../io/netty/buffer/api/CompositeBuf.java | 93 ++++++++++++------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/src/main/java/io/netty/buffer/api/CompositeBuf.java b/src/main/java/io/netty/buffer/api/CompositeBuf.java index d030d9a..0f1346e 100644 --- a/src/main/java/io/netty/buffer/api/CompositeBuf.java +++ b/src/main/java/io/netty/buffer/api/CompositeBuf.java @@ -329,25 +329,31 @@ final class CompositeBuf extends RcSupport implements Buf { int index = fromOffset; final int end = fromOffset + length; int bufferIndex = startBufferIndex; + int initOffset = startCursor.currentOffset(); ByteCursor cursor = startCursor; long longValue = -1; byte byteValue = -1; @Override public boolean readLong() { + if (cursor.readLong()) { + longValue = cursor.getLong(); + return true; + } if (bytesLeft() >= Long.BYTES) { - if (cursor.readLong()) { - longValue = cursor.getLong(); - index += Long.BYTES; - } else { - longValue = nextLongFromBytes(); // Leave index increments to 'readByte' - } + longValue = nextLongFromBytes(); return true; } return false; } private long nextLongFromBytes() { + if (cursor.bytesLeft() == 0) { + nextCursor(); + if (cursor.readLong()) { + return cursor.getLong(); + } + } long val = 0; for (int i = 0; i < 8; i++) { readByte(); @@ -364,20 +370,26 @@ final class CompositeBuf extends RcSupport implements Buf { @Override public boolean readByte() { - if (index < end) { - if (!cursor.readByte()) { - bufferIndex++; - Buf nextBuf = bufs[bufferIndex]; - cursor = nextBuf.openCursor(0, Math.min(nextBuf.capacity(), bytesLeft())); - cursor.readByte(); - } + if (cursor.readByte()) { + byteValue = cursor.getByte(); + return true; + } + if (bytesLeft() > 0) { + nextCursor(); + cursor.readByte(); byteValue = cursor.getByte(); - index++; return true; } return false; } + private void nextCursor() { + bufferIndex++; + Buf nextBuf = bufs[bufferIndex]; + cursor = nextBuf.openCursor(0, Math.min(nextBuf.capacity(), bytesLeft())); + initOffset = 0; + } + @Override public byte getByte() { return byteValue; @@ -385,12 +397,15 @@ final class CompositeBuf extends RcSupport implements Buf { @Override public int currentOffset() { + int currOff = cursor.currentOffset(); + index += currOff - initOffset; + initOffset = currOff; return index; } @Override public int bytesLeft() { - return end - index; + return end - currentOffset(); } }; } @@ -415,25 +430,31 @@ final class CompositeBuf extends RcSupport implements Buf { int index = fromOffset; final int end = fromOffset - length; int bufferIndex = startBufferIndex; + int initOffset = startCursor.currentOffset(); ByteCursor cursor = startCursor; long longValue = -1; byte byteValue = -1; @Override public boolean readLong() { + if (cursor.readLong()) { + longValue = cursor.getLong(); + return true; + } if (bytesLeft() >= Long.BYTES) { - if (cursor.readLong()) { - index -= Long.BYTES; - longValue = cursor.getLong(); - } else { - longValue = nextLongFromBytes(); // Leave index increments to 'readByte' - } + longValue = nextLongFromBytes(); return true; } return false; } private long nextLongFromBytes() { + if (cursor.bytesLeft() == 0) { + nextCursor(); + if (cursor.readLong()) { + return cursor.getLong(); + } + } long val = 0; for (int i = 0; i < 8; i++) { readByte(); @@ -450,21 +471,28 @@ final class CompositeBuf extends RcSupport implements Buf { @Override public boolean readByte() { - if (index > end) { - if (!cursor.readByte()) { - bufferIndex--; - Buf nextBuf = bufs[bufferIndex]; - int length = Math.min(nextBuf.capacity(), bytesLeft()); - cursor = nextBuf.openReverseCursor(nextBuf.capacity() - 1, length); - cursor.readByte(); - } + if (cursor.readByte()) { + byteValue = cursor.getByte(); + return true; + } + if (bytesLeft() > 0) { + nextCursor(); + cursor.readByte(); byteValue = cursor.getByte(); - index--; return true; } return false; } + private void nextCursor() { + bufferIndex--; + Buf 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; @@ -472,12 +500,15 @@ final class CompositeBuf extends RcSupport implements Buf { @Override public int currentOffset() { + int currOff = cursor.currentOffset(); + index -= initOffset - currOff; + initOffset = currOff; return index; } @Override public int bytesLeft() { - return index - end; + return currentOffset() - end; } }; }