/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.lanternpowered.server.network.buffer;
import static com.google.common.base.Preconditions.checkNotNull;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import org.lanternpowered.server.data.persistence.nbt.NbtDataContainerInputStream;
import org.lanternpowered.server.data.persistence.nbt.NbtStreamUtils;
import org.lanternpowered.server.network.buffer.objects.Type;
import org.lanternpowered.server.util.LimitInputStream;
import org.spongepowered.api.data.DataView;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import javax.annotation.Nullable;
public class LanternByteBuffer implements ByteBuffer {
private final ByteBuf buf;
@Nullable
private LanternByteBuffer opposite;
LanternByteBuffer(ByteBuf buf) {
this.buf = buf;
}
public ByteBuf getDelegate() {
return this.buf;
}
@Override
public int getCapacity() {
return this.buf.capacity();
}
@Override
public int available() {
return this.buf.readableBytes();
}
// TODO: Deprecate in the api
@SuppressWarnings("deprecation")
@Deprecated
@Override
public int refCnt() {
return this.buf.refCnt();
}
@Override
public ByteBuffer retain() {
this.buf.retain();
return this;
}
@Override
public ByteBuffer retain(int increment) {
this.buf.retain(increment);
return this;
}
@Override
public ByteBuffer touch() {
this.buf.touch();
return this;
}
@Override
public ByteBuffer touch(Object hint) {
this.buf.touch(hint);
return this;
}
@Override
public LanternByteBuffer order(ByteOrder order) {
if (this.buf.order().equals(order)) {
return this;
} else {
if (this.opposite == null) {
this.opposite = new LanternByteBuffer(this.buf.order(order));
this.opposite.opposite = this;
}
return this.opposite;
}
}
@SuppressWarnings("deprecation")
@Deprecated
@Override
public ByteOrder getByteOrder() {
return this.buf.order();
}
@Override
public int readerIndex() {
return this.buf.readerIndex();
}
@Override
public LanternByteBuffer setReadIndex(int index) {
this.buf.readerIndex(index);
return this;
}
@Override
public int writerIndex() {
return this.buf.writerIndex();
}
@Override
public LanternByteBuffer setWriteIndex(int index) {
this.buf.writerIndex(index);
return this;
}
@Override
public LanternByteBuffer setIndex(int readIndex, int writeIndex) {
this.buf.setIndex(readIndex, writeIndex);
return this;
}
@Override
public LanternByteBuffer clear() {
this.buf.clear();
return this;
}
@Override
public LanternByteBuffer markRead() {
this.buf.markReaderIndex();
return this;
}
@Override
public LanternByteBuffer markWrite() {
this.buf.markWriterIndex();
return this;
}
@Override
public LanternByteBuffer resetRead() {
this.buf.resetReaderIndex();
return this;
}
@Override
public LanternByteBuffer resetWrite() {
this.buf.resetWriterIndex();
return this;
}
@Override
public LanternByteBuffer slice() {
return new LanternByteBuffer(this.buf.slice());
}
@Override
public LanternByteBuffer slice(int index, int length) {
return new LanternByteBuffer(this.buf.slice(index, length));
}
@Override
public byte[] array() {
return this.buf.array();
}
@Override
public LanternByteBuffer writeBoolean(boolean data) {
this.buf.writeBoolean(data);
return this;
}
@Override
public LanternByteBuffer setBoolean(int index, boolean data) {
this.buf.setBoolean(index, data);
return this;
}
@Override
public boolean readBoolean() {
return this.buf.readBoolean();
}
@Override
public boolean getBoolean(int index) {
return this.buf.getBoolean(index);
}
@Override
public LanternByteBuffer writeByte(byte data) {
this.buf.writeByte(data);
return this;
}
@Override
public LanternByteBuffer setByte(int index, byte data) {
this.buf.setByte(index, data);
return this;
}
@Override
public byte readByte() {
return this.buf.readByte();
}
@Override
public byte getByte(int index) {
return this.buf.getByte(index);
}
@Override
public LanternByteBuffer writeByteArray(byte[] data) {
this.writeVarInt(data.length);
this.writeBytes(data);
return this;
}
@Override
public LanternByteBuffer writeByteArray(byte[] data, int start, int length) {
this.writeVarInt(length);
this.writeBytes(data, start, length);
return this;
}
@Override
public LanternByteBuffer setByteArray(int index, byte[] data) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeByteArray(data);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public LanternByteBuffer setByteArray(int index, byte[] data, int start, int length) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeVarInt(length);
this.writeBytes(data, start, length);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public byte[] readLimitedByteArray(int maxLength) throws DecoderException {
int length = this.readVarInt();
if (length < 0) {
throw new DecoderException("Byte array length may not be negative.");
}
if (length > maxLength) {
throw new DecoderException("Exceeded the maximum allowed length, got " + length + " which is greater then " + maxLength);
}
byte[] bytes = new byte[length];
this.buf.readBytes(bytes);
return bytes;
}
@Override
public byte[] readByteArray() {
return this.readLimitedByteArray(Integer.MAX_VALUE);
}
@Override
public byte[] readByteArray(int index) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
byte[] data = this.readByteArray();
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public LanternByteBuffer writeBytes(byte[] data) {
this.buf.writeBytes(data);
return this;
}
@Override
public LanternByteBuffer writeBytes(byte[] data, int start, int length) {
this.buf.writeBytes(data, start, length);
return this;
}
@Override
public LanternByteBuffer setBytes(int index, byte[] data) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.buf.writeBytes(data);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public LanternByteBuffer setBytes(int index, byte[] data, int start, int length) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.buf.writeBytes(data, start, length);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public byte[] readBytes(int length) {
byte[] data = new byte[length];
this.buf.readBytes(data);
return data;
}
@Override
public byte[] readBytes(int index, int length) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
byte[] data = new byte[length];
this.buf.readBytes(data);
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public LanternByteBuffer writeShort(short data) {
this.buf.writeShort(data);
return this;
}
@Override
public LanternByteBuffer setShort(int index, short data) {
this.buf.setShort(index, data);
return this;
}
@Override
public short readShort() {
return this.buf.readShort();
}
@Override
public short getShort(int index) {
return this.buf.getShort(index);
}
@Override
public LanternByteBuffer writeChar(char data) {
this.buf.writeChar(data);
return this;
}
@Override
public LanternByteBuffer setChar(int index, char data) {
this.buf.setChar(index, data);
return this;
}
@Override
public char readChar() {
return this.buf.readChar();
}
@Override
public char getChar(int index) {
return this.buf.getChar(index);
}
@Override
public LanternByteBuffer writeInteger(int data) {
this.buf.writeInt(data);
return this;
}
@Override
public LanternByteBuffer setInteger(int index, int data) {
this.buf.setInt(index, data);
return this;
}
@Override
public int readInteger() {
return this.buf.readInt();
}
@Override
public int getInteger(int index) {
return this.buf.getInt(index);
}
@Override
public LanternByteBuffer writeLong(long data) {
this.buf.writeLong(data);
return this;
}
@Override
public LanternByteBuffer setLong(int index, long data) {
this.buf.setLong(index, data);
return this;
}
@Override
public long readLong() {
return this.buf.readLong();
}
@Override
public long getLong(int index) {
return this.buf.getLong(index);
}
@Override
public LanternByteBuffer writeFloat(float data) {
this.buf.writeFloat(data);
return this;
}
@Override
public LanternByteBuffer setFloat(int index, float data) {
this.buf.setFloat(index, data);
return this;
}
@Override
public float readFloat() {
return this.buf.readFloat();
}
@Override
public float getFloat(int index) {
return this.buf.getFloat(index);
}
@Override
public LanternByteBuffer writeDouble(double data) {
this.buf.writeDouble(data);
return this;
}
@Override
public LanternByteBuffer setDouble(int index, double data) {
this.buf.setDouble(index, data);
return this;
}
@Override
public double readDouble() {
return this.buf.readDouble();
}
@Override
public double getDouble(int index) {
return this.buf.getDouble(index);
}
public static void writeVarInt(ByteBuf byteBuf, int value) {
while ((value & 0xFFFFFF80) != 0L) {
byteBuf.writeByte((value & 0x7F) | 0x80);
value >>>= 7;
}
byteBuf.writeByte(value & 0x7F);
}
public static int readVarInt(ByteBuf byteBuf) {
int value = 0;
int i = 0;
int b;
while (((b = byteBuf.readByte()) & 0x80) != 0) {
value |= (b & 0x7F) << i;
i += 7;
if (i > 35) {
throw new DecoderException("Variable length is too long!");
}
}
return value | (b << i);
}
@Override
public LanternByteBuffer writeVarInt(int value) {
writeVarInt(this.buf, value);
return this;
}
@Override
public LanternByteBuffer setVarInt(int index, int value) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeVarInt(value);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public int readVarInt() {
return readVarInt(this.buf);
}
@Override
public int getVarInt(int index) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
int data = this.readVarInt();
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public LanternByteBuffer writeString(String data) {
this.writeByteArray(data.getBytes(StandardCharsets.UTF_8));
return this;
}
@Override
public LanternByteBuffer setString(int index, String data) {
this.setByteArray(index, data.getBytes(StandardCharsets.UTF_8));
return this;
}
@Override
public String readLimitedString(int maxLength) throws DecoderException {
return new String(this.readLimitedByteArray(maxLength * 4), StandardCharsets.UTF_8);
}
@Override
public String readString() {
return this.readLimitedString(Short.MAX_VALUE);
}
@Override
public String getString(int index) {
return new String(this.readByteArray(index), StandardCharsets.UTF_8);
}
@Override
public LanternByteBuffer writeUTF(String data) {
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
if (bytes.length > 32767) {
throw new EncoderException("String too big (was " + data.length() + " bytes encoded, max " + 32767 + ")");
}
this.buf.writeShort(bytes.length);
this.buf.writeBytes(bytes);
return this;
}
@Override
public LanternByteBuffer setUTF(int index, String data) {
checkNotNull(data, "data");
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeUTF(data);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public String readUTF() {
int length = this.readShort();
return new String(this.buf.readBytes(length).array(), StandardCharsets.UTF_8);
}
@Override
public String getUTF(int index) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
int length = this.readShort();
String data = new String(this.buf.readBytes(length).array(), StandardCharsets.UTF_8);
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public LanternByteBuffer writeUniqueId(UUID data) {
this.buf.writeLong(data.getMostSignificantBits());
this.buf.writeLong(data.getLeastSignificantBits());
return this;
}
@Override
public LanternByteBuffer setUniqueId(int index, UUID data) {
final int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeUniqueId(data);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public UUID readUniqueId() {
final long most = this.buf.readLong();
final long least = this.buf.readLong();
return new UUID(most, least);
}
@Override
public UUID getUniqueId(int index) {
final int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
final UUID data = this.readUniqueId();
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public LanternByteBuffer writeDataView(@Nullable DataView data) {
if (data == null) {
this.buf.writeByte(0);
return this;
}
try {
NbtStreamUtils.write(data, new ByteBufOutputStream(this.buf), false);
} catch (IOException e) {
throw new CodecException(e);
}
return this;
}
@Override
public LanternByteBuffer setDataView(int index, @Nullable DataView data) {
final int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeDataView(data);
this.buf.writerIndex(oldIndex);
return this;
}
@Nullable
@Override
public DataView readLimitedDataView(int maximumDepth, int maxBytes) {
final int index = this.buf.readerIndex();
if (this.buf.readByte() == 0) {
return null;
}
this.buf.readerIndex(index);
try {
try (NbtDataContainerInputStream input = new NbtDataContainerInputStream(
new LimitInputStream(new ByteBufInputStream(this.buf), maxBytes), false, maximumDepth)) {
return input.read();
}
} catch (IOException e) {
throw new CodecException(e);
}
}
@Nullable
@Override
public DataView readDataView() {
return this.readLimitedDataView(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
@Nullable
@Override
public DataView getDataView(int index) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
DataView data = this.readDataView();
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public ByteBuffer writeBytes(ByteBuffer byteBuffer) {
this.buf.writeBytes(((LanternByteBuffer) byteBuffer).getDelegate());
return this;
}
@Override
public ByteBuffer readBytes(byte[] byteArray) {
this.buf.readBytes(byteArray);
return this;
}
@Override
public ByteBuffer readBytes(byte[] dst, int dstIndex, int length) {
this.buf.readBytes(dst, dstIndex, length);
return this;
}
@Override
public ByteBuffer readBytes(ByteBuffer byteBuffer) {
this.buf.readBytes(((LanternByteBuffer) byteBuffer).getDelegate());
return this;
}
@Override
public ByteBuffer readBytes(ByteBuffer dst, int dstIndex, int length) {
this.buf.readBytes(((LanternByteBuffer) dst).getDelegate(), dstIndex, length);
return this;
}
@Override
public LanternByteBuffer writeVarLong(long value) {
while ((value & 0xFFFFFFFFFFFFFF80L) != 0L) {
this.buf.writeByte(((int) value & 0x7F) | 0x80);
value >>>= 7;
}
this.buf.writeByte((int) value & 0x7F);
return this;
}
@Override
public LanternByteBuffer setVarLong(int index, long value) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.writeVarLong(value);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public long readVarLong() {
long value = 0L;
int i = 0;
long b;
while (((b = this.buf.readByte()) & 0x80L) != 0) {
value |= (b & 0x7F) << i;
i += 7;
if (i > 63) {
throw new DecoderException("Variable length is too long!");
}
}
return value | (b << i);
}
@Override
public long getVarLong(int index) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
long data = this.readVarLong();
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public <V> LanternByteBuffer write(Type<V> type, V value) {
type.getSerializer().write(this, value);
return this;
}
@Override
public <V> LanternByteBuffer set(int index, Type<V> type, V value) {
int oldIndex = this.buf.writerIndex();
this.buf.writerIndex(index);
this.write(type, value);
this.buf.writerIndex(oldIndex);
return this;
}
@Override
public <V> V read(Type<V> type) {
return type.getSerializer().read(this);
}
@Override
public <V> V get(int index, Type<V> type) {
int oldIndex = this.buf.readerIndex();
this.buf.readerIndex(index);
V data = this.read(type);
this.buf.readerIndex(oldIndex);
return data;
}
@Override
public LanternByteBuffer ensureWritable(int minWritableBytes) {
this.buf.ensureWritable(minWritableBytes);
return this;
}
@Override
public boolean release() {
return this.buf.release();
}
@Override
public boolean release(int decrement) {
return this.buf.release(decrement);
}
@Override
public LanternByteBuffer copy() {
return new LanternByteBuffer(this.buf.copy());
}
}