2021-08-06 14:21:11 +02:00
|
|
|
package net.minestom.server.utils.binary;
|
|
|
|
|
|
|
|
import org.jetbrains.annotations.ApiStatus;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.jglrxavpok.hephaistos.nbt.NBTReader;
|
|
|
|
import org.jglrxavpok.hephaistos.nbt.NBTWriter;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.channels.ReadableByteChannel;
|
|
|
|
import java.nio.channels.WritableByteChannel;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manages off-heap memory.
|
|
|
|
* Not thread-safe.
|
|
|
|
*/
|
2021-08-08 17:01:44 +02:00
|
|
|
@ApiStatus.Internal
|
2021-08-06 14:21:11 +02:00
|
|
|
public final class BinaryBuffer {
|
|
|
|
private ByteBuffer nioBuffer; // To become a `MemorySegment` once released
|
2021-08-06 17:30:39 +02:00
|
|
|
private NBTReader nbtReader;
|
|
|
|
private NBTWriter nbtWriter;
|
2021-08-06 14:21:11 +02:00
|
|
|
|
|
|
|
private final int capacity;
|
|
|
|
private int readerOffset, writerOffset;
|
|
|
|
|
|
|
|
private BinaryBuffer(ByteBuffer buffer) {
|
|
|
|
this.nioBuffer = buffer;
|
|
|
|
this.capacity = buffer.capacity();
|
|
|
|
}
|
|
|
|
|
|
|
|
@ApiStatus.Internal
|
|
|
|
public static BinaryBuffer ofSize(int size) {
|
|
|
|
return new BinaryBuffer(ByteBuffer.allocateDirect(size));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static BinaryBuffer copy(BinaryBuffer buffer) {
|
|
|
|
final int size = buffer.readableBytes();
|
|
|
|
final var temp = ByteBuffer.allocateDirect(size)
|
|
|
|
.put(buffer.asByteBuffer(0, size));
|
|
|
|
return new BinaryBuffer(temp);
|
|
|
|
}
|
|
|
|
|
2021-11-02 03:08:36 +01:00
|
|
|
public void write(ByteBuffer buffer, int index, int length) {
|
|
|
|
this.nioBuffer.put(writerOffset, buffer, index, length);
|
|
|
|
this.writerOffset += length;
|
|
|
|
}
|
|
|
|
|
2021-08-06 14:21:11 +02:00
|
|
|
public void write(ByteBuffer buffer) {
|
2021-11-02 03:08:36 +01:00
|
|
|
write(buffer, buffer.position(), buffer.remaining());
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void write(BinaryBuffer buffer) {
|
|
|
|
write(buffer.asByteBuffer(buffer.readerOffset, buffer.writerOffset));
|
|
|
|
}
|
|
|
|
|
|
|
|
public int readVarInt() {
|
|
|
|
int value = 0;
|
2021-08-06 14:30:55 +02:00
|
|
|
for (int i = 0; i < 5; i++) {
|
2021-08-06 14:21:11 +02:00
|
|
|
final int offset = readerOffset + i;
|
|
|
|
final byte k = nioBuffer.get(offset);
|
|
|
|
value |= (k & 0x7F) << i * 7;
|
|
|
|
if ((k & 0x80) != 128) {
|
|
|
|
this.readerOffset = offset + 1;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new RuntimeException("VarInt is too big");
|
|
|
|
}
|
|
|
|
|
|
|
|
public @NotNull Marker mark() {
|
|
|
|
return new Marker(readerOffset, writerOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void reset(int readerOffset, int writerOffset) {
|
|
|
|
this.readerOffset = readerOffset;
|
|
|
|
this.writerOffset = writerOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void reset(@NotNull Marker marker) {
|
|
|
|
reset(marker.readerOffset(), marker.writerOffset());
|
|
|
|
}
|
|
|
|
|
2021-09-11 03:39:59 +02:00
|
|
|
public boolean canRead(int size) {
|
2021-09-21 10:17:33 +02:00
|
|
|
return readerOffset + size <= writerOffset;
|
2021-09-11 03:39:59 +02:00
|
|
|
}
|
|
|
|
|
2021-08-06 14:21:11 +02:00
|
|
|
public boolean canWrite(int size) {
|
2021-08-09 01:45:00 +02:00
|
|
|
return writerOffset + size < capacity;
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public int capacity() {
|
|
|
|
return capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int readerOffset() {
|
|
|
|
return readerOffset;
|
|
|
|
}
|
|
|
|
|
2021-09-11 03:39:59 +02:00
|
|
|
public void readerOffset(int offset) {
|
|
|
|
this.readerOffset = offset;
|
|
|
|
}
|
|
|
|
|
2021-08-06 14:21:11 +02:00
|
|
|
public int writerOffset() {
|
|
|
|
return writerOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int readableBytes() {
|
|
|
|
return writerOffset - readerOffset;
|
|
|
|
}
|
|
|
|
|
2021-08-08 19:02:36 +02:00
|
|
|
public void writeBytes(byte[] bytes) {
|
2021-10-22 01:55:55 +02:00
|
|
|
this.nioBuffer.put(writerOffset, bytes);
|
2021-08-08 19:02:36 +02:00
|
|
|
this.writerOffset += bytes.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] readBytes(int length) {
|
|
|
|
byte[] bytes = new byte[length];
|
2021-10-22 01:55:55 +02:00
|
|
|
this.nioBuffer.get(readerOffset, bytes);
|
2021-08-08 19:02:36 +02:00
|
|
|
this.readerOffset += length;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] readRemainingBytes() {
|
|
|
|
return readBytes(readableBytes());
|
|
|
|
}
|
|
|
|
|
2021-09-11 03:39:59 +02:00
|
|
|
public BinaryBuffer clear() {
|
2021-08-06 14:21:11 +02:00
|
|
|
this.readerOffset = 0;
|
|
|
|
this.writerOffset = 0;
|
2021-10-15 11:09:05 +02:00
|
|
|
this.nioBuffer.limit(capacity);
|
2021-09-11 03:39:59 +02:00
|
|
|
return this;
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public ByteBuffer asByteBuffer(int reader, int writer) {
|
2021-10-22 01:55:55 +02:00
|
|
|
return nioBuffer.slice(reader, writer);
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
|
|
|
|
2021-10-15 11:09:05 +02:00
|
|
|
@ApiStatus.Internal
|
2021-11-02 03:08:36 +01:00
|
|
|
public ByteBuffer asByteBuffer() {
|
|
|
|
return nioBuffer;
|
2021-10-15 11:09:05 +02:00
|
|
|
}
|
|
|
|
|
2021-09-02 15:44:36 +02:00
|
|
|
public boolean writeChannel(WritableByteChannel channel) throws IOException {
|
2021-10-22 01:55:55 +02:00
|
|
|
var writeBuffer = nioBuffer.slice(readerOffset, writerOffset - readerOffset);
|
2021-09-02 15:44:36 +02:00
|
|
|
final int count = channel.write(writeBuffer);
|
|
|
|
if (count == -1) {
|
|
|
|
// EOS
|
|
|
|
throw new IOException("Disconnected");
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
2021-09-02 15:44:36 +02:00
|
|
|
this.readerOffset += count;
|
|
|
|
return writeBuffer.limit() == writeBuffer.position();
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void readChannel(ReadableByteChannel channel) throws IOException {
|
2021-10-22 01:55:55 +02:00
|
|
|
final int count = channel.read(nioBuffer.slice(readerOffset, capacity - readerOffset));
|
2021-08-06 14:21:11 +02:00
|
|
|
if (count == -1) {
|
|
|
|
// EOS
|
|
|
|
throw new IOException("Disconnected");
|
|
|
|
}
|
|
|
|
this.writerOffset += count;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "BinaryBuffer{" +
|
|
|
|
"readerOffset=" + readerOffset +
|
|
|
|
", writerOffset=" + writerOffset +
|
|
|
|
", capacity=" + capacity +
|
|
|
|
'}';
|
|
|
|
}
|
|
|
|
|
2021-10-22 01:55:55 +02:00
|
|
|
public record Marker(int readerOffset, int writerOffset) {
|
2021-08-06 14:21:11 +02:00
|
|
|
}
|
|
|
|
}
|