net.minestom.server.utils.binary.BinaryReader Maven / Gradle / Ivy
Show all versions of minestom-snapshots Show documentation
package net.minestom.server.utils.binary;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.coordinate.Point;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.Either;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import static net.minestom.server.network.NetworkBuffer.*;
/**
* Class used to read from a byte array.
*
* WARNING: not thread-safe.
*/
public class BinaryReader extends InputStream {
private final NetworkBuffer buffer;
public BinaryReader(@NotNull NetworkBuffer buffer) {
this.buffer = buffer;
}
public BinaryReader(@NotNull ByteBuffer buffer) {
this.buffer = new NetworkBuffer(buffer);
}
public BinaryReader(byte[] bytes) {
this(ByteBuffer.wrap(bytes));
}
public int readVarInt() {
return buffer.read(VAR_INT);
}
public long readVarLong() {
return buffer.read(VAR_LONG);
}
public boolean readBoolean() {
return buffer.read(BOOLEAN);
}
public byte readByte() {
return buffer.read(BYTE);
}
public short readShort() {
return buffer.read(SHORT);
}
public int readUnsignedShort() {
return buffer.read(SHORT) & 0xFFFF;
}
/**
* Same as readInt
*/
public int readInteger() {
return buffer.read(INT);
}
/**
* Same as readInteger, created for parity with BinaryWriter
*/
public int readInt() {
return buffer.read(INT);
}
public long readLong() {
return buffer.read(LONG);
}
public float readFloat() {
return buffer.read(FLOAT);
}
public double readDouble() {
return buffer.read(DOUBLE);
}
/**
* Reads a string size by a var-int.
*
* If the string length is higher than {@code maxLength},
* the code throws an exception and the string bytes are not read.
*
* @param maxLength the max length of the string
* @return the string
* @throws IllegalStateException if the string length is invalid or higher than {@code maxLength}
*/
public String readSizedString(int maxLength) {
final int length = readVarInt();
byte[] bytes = new byte[length];
try {
for (int i = 0; i < length; i++) {
bytes[i] = readByte();
}
} catch (BufferUnderflowException e) {
throw new RuntimeException("Could not read " + length + ", " + buffer.readableBytes() + " remaining.");
}
final String str = new String(bytes, StandardCharsets.UTF_8);
Check.stateCondition(str.length() > maxLength,
"String length ({0}) was higher than the max length of {1}", length, maxLength);
return str;
}
public String readSizedString() {
return buffer.read(STRING);
}
public byte[] readBytes(int length) {
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) {
bytes[i] = readByte();
}
return bytes;
}
public byte[] readByteArray() {
return readBytes(readVarInt());
}
public String[] readSizedStringArray(int maxLength) {
final int size = readVarInt();
String[] strings = new String[size];
for (int i = 0; i < size; i++) {
strings[i] = readSizedString(maxLength);
}
return strings;
}
public String[] readSizedStringArray() {
return readSizedStringArray(Integer.MAX_VALUE);
}
public int[] readVarIntArray() {
final int size = readVarInt();
int[] array = new int[size];
for (int i = 0; i < size; i++) {
array[i] = readVarInt();
}
return array;
}
public long[] readVarLongArray() {
final int size = readVarInt();
long[] array = new long[size];
for (int i = 0; i < size; i++) {
array[i] = readVarLong();
}
return array;
}
public long[] readLongArray() {
final int size = readVarInt();
long[] array = new long[size];
for (int i = 0; i < size; i++) {
array[i] = readLong();
}
return array;
}
public byte[] readRemainingBytes() {
return buffer.read(RAW_BYTES);
}
public Point readBlockPosition() {
return buffer.read(BLOCK_POSITION);
}
public UUID readUuid() {
return buffer.read(UUID);
}
public ItemStack readItemStack() {
return buffer.read(ItemStack.NETWORK_TYPE);
}
public Component readComponent(int maxLength) {
final String jsonObject = readSizedString(maxLength);
return GsonComponentSerializer.gson().deserialize(jsonObject);
}
public Component readComponent() {
return buffer.read(COMPONENT);
}
/**
* Creates a new object from the given supplier and calls its {@link Readable#read(BinaryReader)} method with this reader.
*
* @param supplier supplier to create new instances of your object
* @param the readable object type
* @return the read object
*/
public T read(@NotNull Supplier<@NotNull T> supplier) {
T result = supplier.get();
result.read(this);
return result;
}
/**
* Reads the length of the array to read as a varint, creates the array to contain the readable objects and call
* their respective {@link Readable#read(BinaryReader)} methods.
*
* @param supplier supplier to create new instances of your object
* @param the readable object type
* @return the read objects
*/
public @NotNull T[] readArray(@NotNull Supplier<@NotNull T> supplier) {
Readable[] result = new Readable[readVarInt()];
for (int i = 0; i < result.length; i++) {
result[i] = supplier.get();
result[i].read(this);
}
return (T[]) result;
}
public List readVarIntList(@NotNull Function supplier) {
return readList(readVarInt(), supplier);
}
public List readByteList(@NotNull Function supplier) {
return readList(readByte(), supplier);
}
public Either readEither(Function leftReader, Function rightReader) {
if (readBoolean()) {
return Either.left(leftReader.apply(this));
} else {
return Either.right(rightReader.apply(this));
}
}
private List readList(int length, @NotNull Function supplier) {
List list = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
list.add(supplier.apply(this));
}
return list;
}
@Override
public int read() {
return readByte() & 0xFF;
}
@Override
public int available() {
return buffer.readableBytes();
}
public BinaryTag readTag() {
return buffer.read(NBT);
}
/**
* Records the current position, runs the given Runnable, and then returns the bytes between the position before
* running the runnable and the position after.
* Can be used to extract a subsection of this reader's buffer with complex data
*
* @param extractor the extraction code, simply call the reader's read* methods here.
*/
public byte[] extractBytes(Runnable extractor) {
int startingPosition = buffer.readIndex();
extractor.run();
int endingPosition = buffer.readIndex();
byte[] output = new byte[endingPosition - startingPosition];
buffer.copyTo(buffer.readIndex(), output, 0, output.length);
//buffer.get(startingPosition, output);
return output;
}
}