Add a Buf.compact method

Motivation:
Compaction makes more space available at the end of a buffer, by discarding bytes at the beginning that have already been processed.

Modification:
Add a copying compact method to Buf.

Result:
It is now possible to discard read bytes by calling `compact()`.
This commit is contained in:
Chris Vest 2020-12-14 17:01:39 +01:00
parent cc685c0516
commit 0f303c7971
4 changed files with 99 additions and 0 deletions

View File

@ -417,4 +417,11 @@ public interface Buf extends Rc<Buf>, BufAccessors {
* @return A new buffer with independent and exclusive ownership over the read and readable bytes from this buffer.
*/
Buf bifurcate();
/**
* Discards the read bytes, and moves the buffer contents to the beginning of the buffer.
*
* The buffer must be {@linkplain #isOwned() owned}, or an exception will be thrown.
*/
void compact();
}

View File

@ -20,6 +20,9 @@ import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Objects;
import static jdk.incubator.foreign.MemoryAccess.setByteAtOffset;
import static jdk.incubator.foreign.MemoryAccess.setLongAtOffset;
final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
/**
* The max array size is JVM implementation dependant, but most seem to settle on {@code Integer.MAX_VALUE - 8}.
@ -600,6 +603,35 @@ final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
}
}
@Override
public void compact() {
if (!isOwned()) {
throw new IllegalStateException("Buffer must be owned in order to compact.");
}
int distance = roff;
if (distance == 0) {
return;
}
int pos = 0;
var oldOrder = order;
order = ByteOrder.BIG_ENDIAN;
try {
var cursor = openCursor();
while (cursor.readLong()) {
setLong(pos, cursor.getLong());
pos += Long.BYTES;
}
while (cursor.readByte()) {
setByte(pos, cursor.getByte());
pos++;
}
} finally {
order = oldOrder;
}
readerOffset(0);
writerOffset(woff - distance);
}
// <editor-fold defaultstate="collapsed" desc="Primitive accessors.">
@Override
public byte readByte() {

View File

@ -384,6 +384,29 @@ class MemSegBuf extends RcSupport<Buf, MemSegBuf> implements Buf {
return bifurcatedBuf;
}
@Override
public void compact() {
if (!isOwned()) {
throw new IllegalStateException("Buffer must be owned in order to compact.");
}
int distance = roff;
if (distance == 0) {
return;
}
int pos = 0;
var cursor = openCursor();
while (cursor.readLong()) {
setLongAtOffset(seg, pos, ByteOrder.BIG_ENDIAN, cursor.getLong());
pos += Long.BYTES;
}
while (cursor.readByte()) {
setByteAtOffset(seg, pos, cursor.getByte());
pos++;
}
roff -= distance;
woff -= distance;
}
// <editor-fold defaultstate="collapsed" desc="Primitive accessors implementation.">
@Override
public byte readByte() {

View File

@ -2195,6 +2195,43 @@ public class BufTest {
}
}
@ParameterizedTest
@MethodSource("nonSliceAllocators")
public void compactMustDiscardReadBytes(Fixture fixture) {
try (Allocator allocator = fixture.createAllocator();
Buf buf = allocator.allocate(16, ByteOrder.BIG_ENDIAN)) {
buf.writeLong(0x0102030405060708L).writeInt(0x090A0B0C);
assertEquals(0x01020304, buf.readInt());
assertEquals(12, buf.writerOffset());
assertEquals(4, buf.readerOffset());
assertEquals(4, buf.writableBytes());
assertEquals(8, buf.readableBytes());
assertEquals(16, buf.capacity());
buf.compact();
assertEquals(8, buf.writerOffset());
assertEquals(0, buf.readerOffset());
assertEquals(8, buf.writableBytes());
assertEquals(8, buf.readableBytes());
assertEquals(16, buf.capacity());
assertEquals(0x05060708090A0B0CL, buf.readLong());
}
}
@ParameterizedTest
@MethodSource("nonSliceAllocators")
public void compactMustThrowForUnownedBuffer(Fixture fixture) {
try (Allocator allocator = fixture.createAllocator();
Buf buf = allocator.allocate(8, ByteOrder.BIG_ENDIAN)) {
buf.writeLong(0x0102030405060708L);
assertEquals((byte) 0x01, buf.readByte());
try (Buf ignore = buf.acquire()) {
assertThrows(IllegalStateException.class, () -> buf.compact());
assertEquals(1, buf.readerOffset());
}
assertEquals((byte) 0x02, buf.readByte());
}
}
// <editor-fold defaultstate="collapsed" desc="Primitive accessors tests.">
@ParameterizedTest
@MethodSource("allocators")