Generate complete and tested Buf API

Motivation:
 The Buf API is wide, taking into account signedness, endian-ness, and so on, for all primitive
 types.

Modification:
 A code generator has been added which add API, implementation, and tests, to the Buf, BBuf,
 and BBufTest files respectively.

Result:
 We have a complete and fully tested accessor API in the Buf interface, which is still somewhat
 easy to bulk-modify using the code generator.
This commit is contained in:
Chris Vest 2020-09-10 18:05:36 +02:00
parent 94e3a00fd4
commit 0055837b75
5 changed files with 5913 additions and 194 deletions

View File

@ -15,52 +15,52 @@
*/
package io.netty.buffer.b2;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemorySegment;
import static io.netty.buffer.b2.Statics.*;
import static jdk.incubator.foreign.MemoryAccess.*;
class BBuf extends RcSupport<Buf, BBuf> implements Buf {
static final Drop<BBuf> SEGMENT_CLOSE = buf -> buf.segment.close();
static final Drop<BBuf> SEGMENT_CLOSE = buf -> buf.seg.close();
static final Drop<BBuf> SEGMENT_CLOSE_NATIVE = buf -> {
buf.segment.close();
MEM_USAGE_NATIVE.add(-buf.segment.byteSize());
buf.seg.close();
MEM_USAGE_NATIVE.add(-buf.seg.byteSize());
};
final MemorySegment segment;
private int read;
private int write;
final MemorySegment seg;
private int roff;
private int woff;
BBuf(MemorySegment segment, Drop<BBuf> drop) {
super(drop);
this.segment = segment;
seg = segment;
}
@Override
public int capacity() {
return (int) segment.byteSize();
return (int) seg.byteSize();
}
@Override
public int readerIndex() {
return read;
return roff;
}
@Override
public BBuf readerIndex(int index) {
checkIndexBounds(index);
read = index;
checkRead(index, 0);
roff = index;
return this;
}
@Override
public int writerIndex() {
return write;
return woff;
}
@Override
public BBuf writerIndex(int index) {
checkIndexBounds(index);
write = index;
checkWrite(index, 0);
woff = index;
return this;
}
@ -76,149 +76,693 @@ class BBuf extends RcSupport<Buf, BBuf> implements Buf {
@Override
public Buf fill(byte value) {
segment.fill(value);
seg.fill(value);
return this;
}
@Override
public byte[] copy() {
return segment.toByteArray();
return seg.toByteArray();
}
@Override
public long getNativeAddress() {
try {
return segment.address().toRawLongValue();
return seg.address().toRawLongValue();
} catch (UnsupportedOperationException e) {
return 0; // This is a heap segment.
}
}
// ### CODEGEN START primitive accessors implementation
@Override
public byte readByte() {
byte value = MemoryAccess.getByteAtOffset(segment, read);
read += 1;
checkRead(roff, Byte.BYTES);
byte value = getByteAtOffset_BE(seg, roff);
roff += Byte.BYTES;
return value;
}
@Override
public byte readByte(int index) {
return MemoryAccess.getByteAtOffset(segment, index);
public byte readByte(int roff) {
checkRead(roff, Byte.BYTES);
return getByteAtOffset_BE(seg, roff);
}
@Override
public int readUnsignedByte() {
checkRead(roff, Byte.BYTES);
int value = getByteAtOffset_BE(seg, roff) & 0xFF;
roff += Byte.BYTES;
return value;
}
@Override
public int readUnsignedByte(int roff) {
checkRead(roff, Byte.BYTES);
return getByteAtOffset_BE(seg, roff) & 0xFF;
}
@Override
public Buf writeByte(byte value) {
MemoryAccess.setByteAtOffset(segment, write, value);
write += 1;
setByteAtOffset_BE(seg, woff, value);
woff += Byte.BYTES;
return this;
}
@Override
public Buf writeByte(int index, byte value) {
MemoryAccess.setByteAtOffset(segment, index, value);
public Buf writeByte(int woff, byte value) {
setByteAtOffset_BE(seg, woff, value);
return this;
}
@Override
public long readLong() {
long value = MemoryAccess.getLongAtOffset(segment, read);
read += Long.BYTES;
public Buf writeUnsignedByte(int value) {
setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));
woff += Byte.BYTES;
return this;
}
@Override
public Buf writeUnsignedByte(int woff, int value) {
setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));
return this;
}
@Override
public char readChar() {
checkRead(roff, 2);
char value = getCharAtOffset_BE(seg, roff);
roff += 2;
return value;
}
@Override
public long readLong(int offset) {
return MemoryAccess.getLongAtOffset(segment, offset);
public char readChar(int roff) {
checkRead(roff, 2);
return getCharAtOffset_BE(seg, roff);
}
@Override
public Buf writeLong(long value) {
MemoryAccess.setLongAtOffset(segment, write, value);
write += Long.BYTES;
return this;
}
@Override
public Buf writeLong(int offset, long value) {
MemoryAccess.setLongAtOffset(segment, offset, value);
return this;
}
@Override
public int readInt() {
int value = MemoryAccess.getIntAtOffset(segment, read);
read += Integer.BYTES;
public char readCharLE() {
checkRead(roff, 2);
char value = getCharAtOffset_LE(seg, roff);
roff += 2;
return value;
}
@Override
public int readInt(int offset) {
return MemoryAccess.getIntAtOffset(segment, offset);
public char readCharLE(int roff) {
checkRead(roff, 2);
return getCharAtOffset_LE(seg, roff);
}
@Override
public Buf writeInt(int value) {
MemoryAccess.setIntAtOffset(segment, write, value);
write += Integer.BYTES;
public Buf writeChar(char value) {
setCharAtOffset_BE(seg, woff, value);
woff += 2;
return this;
}
@Override
public Buf writeInt(int offset, int value) {
MemoryAccess.setIntAtOffset(segment, offset, value);
public Buf writeChar(int woff, char value) {
setCharAtOffset_BE(seg, woff, value);
return this;
}
@Override
public Buf writeCharLE(char value) {
setCharAtOffset_LE(seg, woff, value);
woff += 2;
return this;
}
@Override
public Buf writeCharLE(int woff, char value) {
setCharAtOffset_LE(seg, woff, value);
return this;
}
@Override
public short readShort() {
short value = MemoryAccess.getShortAtOffset(segment, read);
read += Short.BYTES;
checkRead(roff, Short.BYTES);
short value = getShortAtOffset_BE(seg, roff);
roff += Short.BYTES;
return value;
}
@Override
public short readShort(int offset) {
return MemoryAccess.getShortAtOffset(segment, offset);
public short readShort(int roff) {
checkRead(roff, Short.BYTES);
return getShortAtOffset_BE(seg, roff);
}
@Override
public short readShortLE() {
checkRead(roff, Short.BYTES);
short value = getShortAtOffset_LE(seg, roff);
roff += Short.BYTES;
return value;
}
@Override
public short readShortLE(int roff) {
checkRead(roff, Short.BYTES);
return getShortAtOffset_LE(seg, roff);
}
@Override
public int readUnsignedShort() {
checkRead(roff, Short.BYTES);
int value = getShortAtOffset_BE(seg, roff) & 0xFFFF;
roff += Short.BYTES;
return value;
}
@Override
public int readUnsignedShort(int roff) {
checkRead(roff, Short.BYTES);
return getShortAtOffset_BE(seg, roff) & 0xFFFF;
}
@Override
public int readUnsignedShortLE() {
checkRead(roff, Short.BYTES);
int value = getShortAtOffset_LE(seg, roff) & 0xFFFF;
roff += Short.BYTES;
return value;
}
@Override
public int readUnsignedShortLE(int roff) {
checkRead(roff, Short.BYTES);
return getShortAtOffset_LE(seg, roff) & 0xFFFF;
}
@Override
public Buf writeShort(short value) {
MemoryAccess.setShortAtOffset(segment, write, value);
write += Short.BYTES;
setShortAtOffset_BE(seg, woff, value);
woff += Short.BYTES;
return this;
}
@Override
public Buf writeShort(int offset, short value) {
MemoryAccess.setShortAtOffset(segment, offset, value);
public Buf writeShort(int woff, short value) {
setShortAtOffset_BE(seg, woff, value);
return this;
}
@Override
public Buf writeShortLE(short value) {
setShortAtOffset_LE(seg, woff, value);
woff += Short.BYTES;
return this;
}
@Override
public Buf writeShortLE(int woff, short value) {
setShortAtOffset_LE(seg, woff, value);
return this;
}
@Override
public Buf writeUnsignedShort(int value) {
setShortAtOffset_BE(seg, woff, (short) (value & 0xFFFF));
woff += Short.BYTES;
return this;
}
@Override
public Buf writeUnsignedShort(int woff, int value) {
setShortAtOffset_BE(seg, woff, (short) (value & 0xFFFF));
return this;
}
@Override
public Buf writeUnsignedShortLE(int value) {
setShortAtOffset_LE(seg, woff, (short) (value & 0xFFFF));
woff += Short.BYTES;
return this;
}
@Override
public Buf writeUnsignedShortLE(int woff, int value) {
setShortAtOffset_LE(seg, woff, (short) (value & 0xFFFF));
return this;
}
@Override
public int readMedium() {
checkRead(roff, 3);
int value = getByteAtOffset_BE(seg, roff) << 16 |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) & 0xFF;
roff += 3;
return value;
}
@Override
public int readMedium(int roff) {
checkRead(roff, 3);
return getByteAtOffset_BE(seg, roff) << 16 |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) & 0xFF;
}
@Override
public int readMediumLE() {
checkRead(roff, 3);
int value = getByteAtOffset_BE(seg, roff) & 0xFF |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) << 16;
roff += 3;
return value;
}
@Override
public int readMediumLE(int roff) {
checkRead(roff, 3);
return getByteAtOffset_BE(seg, roff) & 0xFF |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) << 16;
}
@Override
public int readUnsignedMedium() {
checkRead(roff, 3);
int value = (getByteAtOffset_BE(seg, roff) << 16 |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) & 0xFF) & 0xFFFFFF;
roff += 3;
return value;
}
@Override
public int readUnsignedMedium(int roff) {
checkRead(roff, 3);
return (getByteAtOffset_BE(seg, roff) << 16 |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) & 0xFF) & 0xFFFFFF;
}
@Override
public int readUnsignedMediumLE() {
checkRead(roff, 3);
int value = (getByteAtOffset_BE(seg, roff) & 0xFF |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) << 16) & 0xFFFFFF;
roff += 3;
return value;
}
@Override
public int readUnsignedMediumLE(int roff) {
checkRead(roff, 3);
return (getByteAtOffset_BE(seg, roff) & 0xFF |
(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |
getByteAtOffset_BE(seg, roff + 2) << 16) & 0xFFFFFF;
}
@Override
public Buf writeMedium(int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value >> 16));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value & 0xFF));
woff += 3;
return this;
}
@Override
public Buf writeMedium(int woff, int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value >> 16));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value & 0xFF));
return this;
}
@Override
public Buf writeMediumLE(int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value >> 16 & 0xFF));
woff += 3;
return this;
}
@Override
public Buf writeMediumLE(int woff, int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value >> 16 & 0xFF));
return this;
}
@Override
public Buf writeUnsignedMedium(int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value >> 16));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value & 0xFF));
woff += 3;
return this;
}
@Override
public Buf writeUnsignedMedium(int woff, int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value >> 16));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value & 0xFF));
return this;
}
@Override
public Buf writeUnsignedMediumLE(int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value >> 16 & 0xFF));
woff += 3;
return this;
}
@Override
public Buf writeUnsignedMediumLE(int woff, int value) {
checkWrite(woff, 3);
setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));
setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));
setByteAtOffset_BE(seg, woff + 2, (byte) (value >> 16 & 0xFF));
return this;
}
@Override
public int readInt() {
checkRead(roff, Integer.BYTES);
int value = getIntAtOffset_BE(seg, roff);
roff += Integer.BYTES;
return value;
}
@Override
public int readInt(int roff) {
checkRead(roff, Integer.BYTES);
return getIntAtOffset_BE(seg, roff);
}
@Override
public int readIntLE() {
checkRead(roff, Integer.BYTES);
int value = getIntAtOffset_LE(seg, roff);
roff += Integer.BYTES;
return value;
}
@Override
public int readIntLE(int roff) {
checkRead(roff, Integer.BYTES);
return getIntAtOffset_LE(seg, roff);
}
@Override
public long readUnsignedInt() {
checkRead(roff, Integer.BYTES);
long value = getIntAtOffset_BE(seg, roff) & 0xFFFFFFFFL;
roff += Integer.BYTES;
return value;
}
@Override
public long readUnsignedInt(int roff) {
checkRead(roff, Integer.BYTES);
return getIntAtOffset_BE(seg, roff) & 0xFFFFFFFFL;
}
@Override
public long readUnsignedIntLE() {
checkRead(roff, Integer.BYTES);
long value = getIntAtOffset_LE(seg, roff) & 0xFFFFFFFFL;
roff += Integer.BYTES;
return value;
}
@Override
public long readUnsignedIntLE(int roff) {
checkRead(roff, Integer.BYTES);
return getIntAtOffset_LE(seg, roff) & 0xFFFFFFFFL;
}
@Override
public Buf writeInt(int value) {
setIntAtOffset_BE(seg, woff, value);
woff += Integer.BYTES;
return this;
}
@Override
public Buf writeInt(int woff, int value) {
setIntAtOffset_BE(seg, woff, value);
return this;
}
@Override
public Buf writeIntLE(int value) {
setIntAtOffset_LE(seg, woff, value);
woff += Integer.BYTES;
return this;
}
@Override
public Buf writeIntLE(int woff, int value) {
setIntAtOffset_LE(seg, woff, value);
return this;
}
@Override
public Buf writeUnsignedInt(long value) {
setIntAtOffset_BE(seg, woff, (int) (value & 0xFFFFFFFFL));
woff += Integer.BYTES;
return this;
}
@Override
public Buf writeUnsignedInt(int woff, long value) {
setIntAtOffset_BE(seg, woff, (int) (value & 0xFFFFFFFFL));
return this;
}
@Override
public Buf writeUnsignedIntLE(long value) {
setIntAtOffset_LE(seg, woff, (int) (value & 0xFFFFFFFFL));
woff += Integer.BYTES;
return this;
}
@Override
public Buf writeUnsignedIntLE(int woff, long value) {
setIntAtOffset_LE(seg, woff, (int) (value & 0xFFFFFFFFL));
return this;
}
@Override
public float readFloat() {
checkRead(roff, Float.BYTES);
float value = getFloatAtOffset_BE(seg, roff);
roff += Float.BYTES;
return value;
}
@Override
public float readFloat(int roff) {
checkRead(roff, Float.BYTES);
return getFloatAtOffset_BE(seg, roff);
}
@Override
public float readFloatLE() {
checkRead(roff, Float.BYTES);
float value = getFloatAtOffset_LE(seg, roff);
roff += Float.BYTES;
return value;
}
@Override
public float readFloatLE(int roff) {
checkRead(roff, Float.BYTES);
return getFloatAtOffset_LE(seg, roff);
}
@Override
public Buf writeFloat(float value) {
setFloatAtOffset_BE(seg, woff, value);
woff += Float.BYTES;
return this;
}
@Override
public Buf writeFloat(int woff, float value) {
setFloatAtOffset_BE(seg, woff, value);
return this;
}
@Override
public Buf writeFloatLE(float value) {
setFloatAtOffset_LE(seg, woff, value);
woff += Float.BYTES;
return this;
}
@Override
public Buf writeFloatLE(int woff, float value) {
setFloatAtOffset_LE(seg, woff, value);
return this;
}
@Override
public long readLong() {
checkRead(roff, Long.BYTES);
long value = getLongAtOffset_BE(seg, roff);
roff += Long.BYTES;
return value;
}
@Override
public long readLong(int roff) {
checkRead(roff, Long.BYTES);
return getLongAtOffset_BE(seg, roff);
}
@Override
public long readLongLE() {
checkRead(roff, Long.BYTES);
long value = getLongAtOffset_LE(seg, roff);
roff += Long.BYTES;
return value;
}
@Override
public long readLongLE(int roff) {
checkRead(roff, Long.BYTES);
return getLongAtOffset_LE(seg, roff);
}
@Override
public Buf writeLong(long value) {
setLongAtOffset_BE(seg, woff, value);
woff += Long.BYTES;
return this;
}
@Override
public Buf writeLong(int woff, long value) {
setLongAtOffset_BE(seg, woff, value);
return this;
}
@Override
public Buf writeLongLE(long value) {
setLongAtOffset_LE(seg, woff, value);
woff += Long.BYTES;
return this;
}
@Override
public Buf writeLongLE(int woff, long value) {
setLongAtOffset_LE(seg, woff, value);
return this;
}
@Override
public double readDouble() {
checkRead(roff, Double.BYTES);
double value = getDoubleAtOffset_BE(seg, roff);
roff += Double.BYTES;
return value;
}
@Override
public double readDouble(int roff) {
checkRead(roff, Double.BYTES);
return getDoubleAtOffset_BE(seg, roff);
}
@Override
public double readDoubleLE() {
checkRead(roff, Double.BYTES);
double value = getDoubleAtOffset_LE(seg, roff);
roff += Double.BYTES;
return value;
}
@Override
public double readDoubleLE(int roff) {
checkRead(roff, Double.BYTES);
return getDoubleAtOffset_LE(seg, roff);
}
@Override
public Buf writeDouble(double value) {
setDoubleAtOffset_BE(seg, woff, value);
woff += Double.BYTES;
return this;
}
@Override
public Buf writeDouble(int woff, double value) {
setDoubleAtOffset_BE(seg, woff, value);
return this;
}
@Override
public Buf writeDoubleLE(double value) {
setDoubleAtOffset_LE(seg, woff, value);
woff += Double.BYTES;
return this;
}
@Override
public Buf writeDoubleLE(int woff, double value) {
setDoubleAtOffset_LE(seg, woff, value);
return this;
}
// ### CODEGEN END primitive accessors implementation
@Override
protected Owned<BBuf> prepareSend() {
BBuf outer = this;
boolean isConfined = segment.ownerThread() == null;
MemorySegment transferSegment = isConfined? segment : segment.withOwnerThread(null);
boolean isConfined = seg.ownerThread() == null;
MemorySegment transferSegment = isConfined? seg : seg.withOwnerThread(null);
return new Owned<BBuf>() {
@Override
public BBuf transferOwnership(Thread recipient, Drop<BBuf> drop) {
var newSegment = isConfined? transferSegment.withOwnerThread(recipient) : transferSegment;
BBuf copy = new BBuf(newSegment, drop);
copy.read = outer.read;
copy.write = outer.write;
copy.roff = outer.roff;
copy.woff = outer.woff;
return copy;
}
};
}
private void checkIndexBounds(int index) {
if (index < 0 || segment.byteSize() <= index) {
private void checkRead(int index, int size) {
if (index < 0 || woff < index + size) {
throw indexOutOfBounds(index);
}
}
private void checkWrite(int index, int size) {
if (index < 0 || seg.byteSize() < index + size) {
throw indexOutOfBounds(index);
}
}
private IndexOutOfBoundsException indexOutOfBounds(int index) {
return new IndexOutOfBoundsException(
"Index " + index + " is out of bounds: [0 to " + segment.byteSize() + "].");
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
(seg.byteSize() - 1) + "].");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ class NativeMemoryCleanerDrop implements Drop<BBuf> {
@Override
public void accept(BBuf buf) {
drop(null); // Unregister old cleanable, if any, to avoid uncontrolled build-up.
var segment = buf.segment;
var segment = buf.seg;
cleanable = CLEANER.register(this, () -> {
if (segment.isAlive()) {
// TODO return segment to pool, or call out to external drop, instead of closing it directly.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,593 @@
package io.netty.buffer.b2;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.Stream.Builder;
import static java.nio.charset.StandardCharsets.*;
import static java.nio.file.StandardOpenOption.*;
public final class Codegen {
private static final Pattern ALL_DIGITS = Pattern.compile("^\\d+$");
private static final Map<String, Supplier<Stream<String>>> REGION_GENERATORS = Map.of(
"primitive accessors interface", Codegen::primitiveAccessorsInterface,
"primitive accessors implementation", Codegen::primitiveAccessorsImplementation,
"primitive accessors tests", Codegen::primitiveAccessorsTests);
enum Type {
BYTE("byte", "Byte", "Byte.BYTES", Byte.BYTES, false, "two's complement 8-bit", "int"),
CHAR("char", "Char", "2", 2, true, "2-byte UTF-16", null),
SHORT("short", "Short", "Short.BYTES", Short.BYTES, true, "two's complement 16-bit", "int"),
MED("int", "Medium", "3", 3, true, "two's complement 24-bit", "int") {
@Override
public String load(boolean le, boolean unsigned) {
String indent = " ";
String tailPart = unsigned? ") & 0x" + "FF".repeat(actualSize) : "";
if (le) {
return (unsigned? "(" : "") +
"getByteAtOffset_BE(seg, roff) & 0xFF |\n" +
indent +
"(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |\n" +
indent +
"getByteAtOffset_BE(seg, roff + 2) << 16" +
tailPart;
} else {
return (unsigned? "(" : "") +
"getByteAtOffset_BE(seg, roff) << 16 |\n" +
indent +
"(getByteAtOffset_BE(seg, roff + 1) & 0xFF) << 8 |\n" +
indent +
"getByteAtOffset_BE(seg, roff + 2) & 0xFF" +
tailPart;
}
}
@Override
public String store(boolean le, boolean unsigned) {
String indent = " ";
if (le) {
return "setByteAtOffset_BE(seg, woff, (byte) (value & 0xFF));\n" +
indent +
"setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));\n" +
indent +
"setByteAtOffset_BE(seg, woff + 2, (byte) (value >> 16 & 0xFF))";
} else {
return "setByteAtOffset_BE(seg, woff, (byte) (value >> 16));\n" +
indent +
"setByteAtOffset_BE(seg, woff + 1, (byte) (value >> 8 & 0xFF));\n" +
indent +
"setByteAtOffset_BE(seg, woff + 2, (byte) (value & 0xFF))";
}
}
},
INT("int", "Int", "Integer.BYTES", Integer.BYTES, true, "two's complement 32-bit", "long"),
FLOAT("float", "Float", "Float.BYTES", Float.BYTES, true, "32-bit IEEE floating point", null),
LONG("long", "Long", "Long.BYTES", Long.BYTES, true, "two's complement 64-bit", null),
DOUBLE("double", "Double", "Double.BYTES", Double.BYTES, true, "64-bit IEEE floating point", null),
;
protected final String type;
protected final String title;
protected final String size;
protected final int actualSize;
protected final boolean includeLE;
protected final String extra;
protected final String unsignedCarrier;
Type(String type, String title, String size, int actualSize, boolean includeLE, String extra, String unsignedCarrier) {
this.type = type;
this.title = title;
this.size = size;
this.actualSize = actualSize;
this.includeLE = includeLE;
this.extra = extra;
this.unsignedCarrier = unsignedCarrier;
}
public String title(boolean le, boolean unsigned) {
return (unsigned? "Unsigned" + title : title) + (le? "LE" : "");
}
public String extraRead(boolean le, boolean unsigned) {
return getExtra("read", le, unsigned);
}
public String extraWrite(boolean le, boolean unsigned) {
return getExtra("written", le, unsigned);
}
private String getExtra(String op, boolean le, boolean unsigned) {
return "The value is " + op + " using " +
(unsigned? "an unsigned " : "a ") +
extra +
" encoding, with " +
(le? "little" : "big") +
"-endian byte order.";
}
public String type(boolean unsigned) {
return unsigned? unsignedCarrier : type;
}
public String load(boolean le, boolean unsigned) {
boolean longCarrier = "long".equals(unsignedCarrier);
boolean intCarrier = "int".equals(unsignedCarrier);
return (unsigned && !longCarrier && !intCarrier? '(' + unsignedCarrier + ") (" : "") +
"get" +
title +
"AtOffset_" +
(le? "LE" : "BE") +
"(seg, roff)" +
(unsigned? " & 0x" + "FF".repeat(actualSize) +
(longCarrier? 'L' : intCarrier? "" : ')') : "");
}
public String store(boolean le, boolean unsigned) {
boolean longCarrier = "long".equals(unsignedCarrier);
return "set" +
title +
"AtOffset_" +
(le? "LE" : "BE") +
"(seg, woff, " +
(unsigned? '(' + type + ") (value & 0x" + "FF".repeat(actualSize) +
(longCarrier? "L)" : ")") : "value") +
')';
}
public String realType(boolean unsigned) {
return unsigned? "unsigned " + type : type;
}
}
enum Template {
INTERFACE {
@Override
public String relativeRead(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" /**\n" +
" * Get the %8$s value at the current {@link Buf#readerIndex()},\n" +
" * and increases the reader offset by %3$s.\n" +
" * %4$s\n" +
" *\n" +
" * @return The %8$s value at the current reader offset.\n" +
" * @throws IndexOutOfBoundsException If {@link Buf#readableBytes} is less than %3$s.\n" +
" */\n" +
" %1$s read%2$s();";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String offsetRead(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" /**\n" +
" * Get the %8$s value at the given reader offset.\n" +
" * The {@link Buf#readerIndex()} is not modified.\n" +
" * %4$s\n" +
" *\n" +
" * @param roff The read offset, an absolute index into this buffer, to read from.\n" +
" * @return The %8$s value at the given offset.\n" +
" * @throws IndexOutOfBoundsException if the given index is out of bounds of the buffer, that is, less than 0 or\n" +
" * greater than or equal to {@link Buf#capacity()} minus %3$s.\n" +
" */\n" +
" %1$s read%2$s(int roff);";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String relativeWrite(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" /**\n" +
" * Set the given %8$s value at the current {@link Buf#writerIndex()},\n" +
" * and increase the writer offset by %3$s.\n" +
" * %4$s\n" +
" *\n" +
" * @param value The %1$s value to write.\n" +
" * @return This Buf.\n" +
" * @throws IndexOutOfBoundsException If {@link Buf#writableBytes} is less than %3$s.\n" +
" */\n" +
" Buf write%2$s(%1$s value);";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String offsetWrite(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" /**\n" +
" * Set the given %8$s value at the given write offset. The {@link Buf#writerIndex()} is not modified.\n" +
" * %4$s\n" +
" *\n" +
" * @param woff The write offset, an absolute index into this buffer to write to.\n" +
" * @param value The %1$s value to write.\n" +
" * @return This Buf.\n" +
" * @throws IndexOutOfBoundsException if the given offset is out of bounds of the buffer, that is, less than 0 or\n" +
" * greater than or equal to {@link Buf#capacity()} minus %3$s.\n" +
" */\n" +
" Buf write%2$s(int woff, %1$s value);";
return format(tmpl, type, le, unsigned, read);
}
},
IMPLEMENTATION {
@Override
public String relativeRead(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" @Override\n" +
" public %1$s read%2$s() {\n" +
" checkRead(roff, %5$s);\n" +
" %1$s value = %6$s;\n" +
" roff += %5$s;\n" +
" return value;\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String offsetRead(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" @Override\n" +
" public %1$s read%2$s(int roff) {\n" +
" checkRead(roff, %5$s);\n" +
" return %6$s;\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String relativeWrite(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" @Override\n" +
" public Buf write%2$s(%1$s value) {\n" +
(type == Type.MED? " checkWrite(woff, %5$s);\n" : "") +
" %7$s;\n" +
" woff += %5$s;\n" +
" return this;\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String offsetWrite(Type type, boolean le, boolean unsigned, boolean read) {
var tmpl = '\n' +
" @Override\n" +
" public Buf write%2$s(int woff, %1$s value) {\n" +
(type == Type.MED? " checkWrite(woff, %5$s);\n" : "") +
" %7$s;\n" +
" return this;\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
},
TESTS {
String testValue;
String testValueByteOrder;
int bytesAvailAfter;
@Override
public String relativeRead(Type type, boolean le, boolean unsigned, boolean read) {
prepare(type);
var tmpl = '\n' +
" @Test\n" +
" public void relativeReadOf%2$sMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset() {\n" +
" assertEquals(0, buf.readableBytes());\n" +
" assertEquals(Long.BYTES, buf.writableBytes());\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" assertEquals(" + type.actualSize + ", buf.readableBytes());\n" +
" assertEquals(" + bytesAvailAfter + ", buf.writableBytes());\n" +
" assertEquals(value, buf.read%2$s());\n" +
" assertEquals(0, buf.readableBytes());\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void relativeReadOf%2$sMustReadWith" + (le? "Little" : "Big") + "EndianByteOrder() {\n" +
" assertEquals(0, buf.readableBytes());\n" +
" assertEquals(Long.BYTES, buf.writableBytes());\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" buf.writeByte(" + (le? type.actualSize - 1 : 0) + ", (byte) 0x10);\n" +
" assertEquals(" + type.actualSize + ", buf.readableBytes());\n" +
" assertEquals(" + bytesAvailAfter + ", buf.writableBytes());\n" +
" assertEquals(" + testValueByteOrder + ", buf.read%2$s());\n" +
" assertEquals(0, buf.readableBytes());\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void relativeReadOf%2$sMustBoundsCheckWhenReadOffsetAndSizeIsBeyondWriteOffset() {\n" +
" assertEquals(0, buf.readableBytes());\n" +
" assertEquals(Long.BYTES, buf.writableBytes());\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" buf.readerIndex(1);\n" +
" assertEquals(" + (type.actualSize - 1) + ", buf.readableBytes());\n" +
" assertEquals(" + bytesAvailAfter + ", buf.writableBytes());\n" +
" try {\n" +
" buf.read%2$s();\n" +
" fail(\"Expected a bounds check.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" assertEquals(" + (type.actualSize - 1) + ", buf.readableBytes());\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String offsetRead(Type type, boolean le, boolean unsigned, boolean read) {
prepare(type);
var tmpl = '\n' +
" @Test\n" +
" public void offsettedReadOf%2$sMustBoundsCheckOnNegativeOffset() {\n" +
" try {\n" +
" buf.read%2$s(-1);\n" +
" fail(\"Expected a bounds check.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void offsettedReadOf%2$sMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset() {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" assertEquals(value, buf.read%2$s(0));\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void offsettedReadOf%2$sMustReadWith" + (le? "Little" : "Big") + "EndianByteOrder() {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" buf.writeByte(" + (le? type.actualSize - 1 : 0) + ", (byte) 0x10);\n" +
" assertEquals(" + testValueByteOrder + ", buf.read%2$s(0));\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void offsettedReadOf%2$sMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset() {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" try {\n" +
" buf.read%2$s(1);\n" +
" fail(\"Expected a bounds check.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void offsettedReadOf%2$sMustBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset() {\n" +
" try {\n" +
" buf.read%2$s(0);\n" +
" fail(\"Expected a bounds check.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String relativeWrite(Type type, boolean le, boolean unsigned, boolean read) {
prepare(type);
int size = type.actualSize;
int r = le? size : 1;
var tmpl = '\n' +
" @Test\n" +
" public void relativeWriteOf%2$sMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity() {\n" +
" assertEquals(Long.BYTES, buf.capacity());\n" +
" buf.writerIndex(" + (Long.BYTES + 1 - type.actualSize) + ");\n" +
" try {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" fail(\"Should have bounds checked.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" buf.writerIndex(Long.BYTES);\n" +
" // Verify contents are unchanged.\n" +
" assertEquals(0, buf.readLong());\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void relativeWriteOf%2$sMustHave" + (le? "Little" : "Big") + "EndianByteOrder() {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(value);\n" +
" buf.writerIndex(Long.BYTES);\n" +
" assertEquals((byte) 0x0" + (le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 2? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 3? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 4? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 5? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 6? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 7? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 8? 0 : r) + ", buf.readByte());\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
@Override
public String offsetWrite(Type type, boolean le, boolean unsigned, boolean read) {
prepare(type);
int size = type.actualSize;
int r = le? size : 1;
var tmpl = '\n' +
" @Test\n" +
" public void offsettedWriteOf%2$sMustBoundsCheckWhenWriteOffsetIsNegative() {\n" +
" assertEquals(Long.BYTES, buf.capacity());\n" +
" try {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(-1, value);\n" +
" fail(\"Should have bounds checked.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" buf.writerIndex(Long.BYTES);\n" +
" // Verify contents are unchanged.\n" +
" assertEquals(0, buf.readLong());\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void offsettedWriteOf%2$sMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity() {\n" +
" assertEquals(Long.BYTES, buf.capacity());\n" +
" try {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(" + (Long.BYTES + 1 - type.actualSize) + ", value);\n" +
" fail(\"Should have bounds checked.\");\n" +
" } catch (IndexOutOfBoundsException ignore) {\n" +
" // Good.\n" +
" }\n" +
" buf.writerIndex(Long.BYTES);\n" +
" // Verify contents are unchanged.\n" +
" assertEquals(0, buf.readLong());\n" +
" }\n" +
'\n' +
" @Test\n" +
" public void offsettedWriteOf%2$sMustHave" + (le? "Little" : "Big") + "EndianByteOrder() {\n" +
" %1$s value = " + testValue + ";\n" +
" buf.write%2$s(0, value);\n" +
" buf.writerIndex(Long.BYTES);\n" +
" assertEquals((byte) 0x0" + (le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 2? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 3? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 4? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 5? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 6? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 7? 0 : le? r-- : r++) + ", buf.readByte());\n" +
" assertEquals((byte) 0x0" + (size < 8? 0 : r) + ", buf.readByte());\n" +
" }";
return format(tmpl, type, le, unsigned, read);
}
private void prepare(Type type) {
testValue = "0x0102030405060708L";
if (type.actualSize < 8) {
testValue = testValue.substring(0, 2 + type.actualSize * 2);
}
testValueByteOrder = testValue.substring(4);
testValueByteOrder = "0x10" + testValueByteOrder;
if (type == Type.DOUBLE) {
testValue = "Double.longBitsToDouble(" + testValue + ')';
testValueByteOrder = "Double.longBitsToDouble(" + testValueByteOrder + ')';
} else if (type == Type.FLOAT) {
testValue = "Float.intBitsToFloat(" + testValue + ')';
testValueByteOrder = "Float.intBitsToFloat(" + testValueByteOrder + ')';
}
bytesAvailAfter = Long.BYTES - type.actualSize;
}
};
public abstract String relativeRead(Type type, boolean le, boolean unsigned, boolean read);
public abstract String offsetRead(Type type, boolean le, boolean unsigned, boolean read);
public abstract String relativeWrite(Type type, boolean le, boolean unsigned, boolean read);
public abstract String offsetWrite(Type type, boolean le, boolean unsigned, boolean read);
}
public static void main(String[] args) throws Exception {
generateCodeInline(Path.of("buffer/src/main/java/io/netty/buffer/b2/Buf.java"));
generateCodeInline(Path.of("buffer/src/main/java/io/netty/buffer/b2/BBuf.java"));
generateCodeInline(Path.of("buffer/src/test/java/io/netty/buffer/b2/BBufTest.java"));
}
private static void generateCodeInline(Path path) throws IOException {
String result;
try (Stream<String> lines = Files.lines(path)) {
result = lines.flatMap(processLines()).collect(Collectors.joining("\n"));
}
Files.writeString(path, result, UTF_8, TRUNCATE_EXISTING, WRITE);
}
private static Function<String, Stream<String>> processLines() {
return new Function<String, Stream<String>>() {
final Pattern codegenStart = Pattern.compile("^\\s*// ### CODEGEN START (.*)$");
final Pattern codegenEnd = Pattern.compile("^\\s*// ### CODEGEN END (.*)$");
boolean inCodeGenRegion;
@Override
public Stream<String> apply(String line) {
if (inCodeGenRegion) {
if (codegenEnd.matcher(line).find()) {
inCodeGenRegion = false;
return Stream.of(line);
}
return Stream.empty();
}
var matcher = codegenStart.matcher(line);
Stream<String> generator = Stream.empty();
if (matcher.find()) {
String region = matcher.group(1);
var generatorSupplier = REGION_GENERATORS.get(region);
if (generatorSupplier != null) {
generator = generatorSupplier.get();
inCodeGenRegion = true;
}
}
return Stream.concat(Stream.of(line), generator);
}
};
}
private static Stream<String> primitiveAccessorsInterface() {
return Arrays.stream(Type.values()).flatMap(type -> generateAccessors(Template.INTERFACE, type));
}
private static Stream<String> primitiveAccessorsImplementation() {
return Arrays.stream(Type.values()).flatMap(type -> generateAccessors(Template.IMPLEMENTATION, type));
}
private static Stream<String> primitiveAccessorsTests() {
return Arrays.stream(Type.values()).flatMap(type -> generateAccessors(Template.TESTS, type));
}
private static Stream<String> generateAccessors(Template template, Type type) {
Builder<String> builder = Stream.builder();
builder.add(template.relativeRead(type, false, false, true));
builder.add(template.offsetRead(type, false, false, true));
if (type.includeLE) {
builder.add(template.relativeRead(type, true, false, true));
builder.add(template.offsetRead(type, true, false, true));
}
if (type.unsignedCarrier != null) {
builder.add(template.relativeRead(type, false, true, true));
builder.add(template.offsetRead(type, false, true, true));
if (type.includeLE) {
builder.add(template.relativeRead(type, true, true, true));
builder.add(template.offsetRead(type, true, true, true));
}
}
builder.add(template.relativeWrite(type, false, false, false));
builder.add(template.offsetWrite(type, false, false, false));
if (type.includeLE) {
builder.add(template.relativeWrite(type, true, false, false));
builder.add(template.offsetWrite(type, true, false, false));
}
if (type.unsignedCarrier != null) {
builder.add(template.relativeWrite(type, false, true, false));
builder.add(template.offsetWrite(type, false, true, false));
if (type.includeLE) {
builder.add(template.relativeWrite(type, true, true, false));
builder.add(template.offsetWrite(type, true, true, false));
}
}
return builder.build();
}
private static String format(String format, Type type, boolean le, boolean unsigned, boolean read) {
var carrier = type.type(unsigned);
var title = type.title(le, unsigned);
var size = ALL_DIGITS.matcher(type.size).matches()? type.size : "{@link " + type.size.replace('.', '#') + '}';
var extra = read? type.extraRead(le, unsigned) : type.extraWrite(le, unsigned);
var realSize = type.size;
return String.format(format, carrier, title, size, extra, realSize,
type.load(le, unsigned), type.store(le, unsigned),
type.realType(unsigned));
}
}