package org.infinispan.server.hotrod.transport;
import java.util.Optional;
import org.infinispan.commons.io.SignedNumeric;
import org.infinispan.server.core.transport.VInt;
import org.infinispan.server.core.transport.VLong;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;
public class ExtendedByteBuf {
public static ByteBuf wrappedBuffer(byte[]... arrays) {
return Unpooled.wrappedBuffer(arrays);
}
public static ByteBuf buffer(int capacity) {
return Unpooled.buffer(capacity);
}
public static ByteBuf dynamicBuffer() {
return Unpooled.buffer();
}
public static int readUnsignedShort(ByteBuf bf) {
return bf.readUnsignedShort();
}
public static int readUnsignedInt(ByteBuf bf) {
return VInt.read(bf);
}
public static long readUnsignedLong(ByteBuf bf) {
return VLong.read(bf);
}
public static byte[] readRangedBytes(ByteBuf bf) {
int length = readUnsignedInt(bf);
return readRangedBytes(bf, length);
}
public static byte[] readRangedBytes(ByteBuf bf, int length) {
if (length > 0) {
byte[] array = new byte[length];
bf.readBytes(array);
return array;
} else {
return new byte[0];
}
}
/**
* Reads optional range of bytes. Negative lengths are translated to None, 0 length represents empty Array
*/
public static Optional<byte[]> readOptRangedBytes(ByteBuf bf) {
int length = SignedNumeric.decode(readUnsignedInt(bf));
return length < 0 ? Optional.empty() : Optional.of(readRangedBytes(bf, length));
}
/**
* Reads an optional String. 0 length is an empty string, negative length is translated to None.
*/
public static Optional<String> readOptString(ByteBuf bf) {
Optional<byte[]> bytes = readOptRangedBytes(bf);
return bytes.map(b -> new String(b, CharsetUtil.UTF_8));
}
/**
* Reads length of String and then returns an UTF-8 formatted String of such length. If the length is 0, an empty
* String is returned.
*/
public static String readString(ByteBuf bf) {
byte[] bytes = readRangedBytes(bf);
return bytes.length > 0 ? new String(bytes, CharsetUtil.UTF_8) : "";
}
/**
* Reads a byte if possible. If not present the reader index is reset to the last mark.
*
* @param bf
* @return
*/
public static Optional<Byte> readMaybeByte(ByteBuf bf) {
if (bf.readableBytes() >= 1) {
return Optional.of(bf.readByte());
} else {
bf.resetReaderIndex();
return Optional.empty();
}
}
public static Optional<Long> readMaybeLong(ByteBuf bf) {
if (bf.readableBytes() < 8) {
bf.resetReaderIndex();
return Optional.empty();
} else {
return Optional.of(bf.readLong());
}
}
/**
* Reads a variable long if possible. If not present the reader index is reset to the last mark.
*
* @param bf
* @return
*/
public static Optional<Long> readMaybeVLong(ByteBuf bf) {
if (bf.readableBytes() >= 1) {
byte b = bf.readByte();
return read(bf, b, 7, (long) b & 0x7F, 1);
} else {
bf.resetReaderIndex();
return Optional.empty();
}
}
private static Optional<Long> read(ByteBuf buf, byte b, int shift, long i, int count) {
if ((b & 0x80) == 0) return Optional.of(i);
else {
if (count > 9)
throw new IllegalStateException(
"Stream corrupted. A variable length long cannot be longer than 9 bytes.");
if (buf.readableBytes() >= 1) {
byte bb = buf.readByte();
return read(buf, bb, shift + 7, i | (bb & 0x7FL) << shift, count + 1);
} else {
buf.resetReaderIndex();
return Optional.empty();
}
}
}
/**
* Reads a variable size int if possible. If not present the reader index is reset to the last mark.
*
* @param bf
* @return
*/
public static Optional<Integer> readMaybeVInt(ByteBuf bf) {
if (bf.readableBytes() >= 1) {
byte b = bf.readByte();
return read(bf, b, 7, b & 0x7F, 1);
} else {
bf.resetReaderIndex();
return Optional.empty();
}
}
private static Optional<Integer> read(ByteBuf buf, byte b, int shift, int i, int count) {
if ((b & 0x80) == 0) return Optional.of(i);
else {
if (count > 5)
throw new IllegalStateException(
"Stream corrupted. A variable length integer cannot be longer than 5 bytes.");
if (buf.readableBytes() >= 1) {
byte bb = buf.readByte();
return read(buf, bb, shift + 7, i | (int) ((bb & 0x7FL) << shift), count + 1);
} else {
buf.resetReaderIndex();
return Optional.empty();
}
}
}
/**
* Reads a range of bytes if possible. If not present the reader index is reset to the last mark.
*
* @param bf
* @return
*/
public static Optional<byte[]> readMaybeRangedBytes(ByteBuf bf) {
Optional<Integer> length = readMaybeVInt(bf);
if (length.isPresent()) {
int l = length.get();
if (bf.readableBytes() >= l) {
if (l > 0) {
byte[] array = new byte[l];
bf.readBytes(array);
return Optional.of(array);
} else {
return Optional.of(new byte[0]);
}
} else {
bf.resetReaderIndex();
return Optional.empty();
}
} else return Optional.empty();
}
public static Optional<byte[]> readMaybeRangedBytes(ByteBuf bf, int length) {
if (bf.readableBytes() < length) {
bf.resetReaderIndex();
return Optional.empty();
} else {
byte[] bytes = new byte[length];
bf.readBytes(bytes);
return Optional.of(bytes);
}
}
public static Optional<Integer> readMaybeSignedInt(ByteBuf bf) {
return readMaybeVInt(bf).map(SignedNumeric::decode);
}
/**
* Read a range of bytes prefixed by its length (encoded as a signed VInt).
*
* @return {@code Optional(Optional(byte[])} if it could read the range,
* {@code Optional(Optional.empty())} if the length was negative,
* or {@code Optional.empty()} if the input buffer didn't contain the entire range.
*/
public static Optional<Optional<byte[]>> readMaybeOptRangedBytes(ByteBuf bf) {
Optional<Integer> l = readMaybeSignedInt(bf);
if (l.isPresent()) {
int length = l.get();
if (length < 0) {
return Optional.of(Optional.empty());
} else {
Optional<byte[]> rb = readMaybeRangedBytes(bf, length);
if (rb.isPresent()) {
return Optional.of(rb);
} else {
return Optional.empty();
}
}
} else {
return Optional.empty();
}
}
/**
* Reads a string if possible. If not present the reader index is reset to the last mark.
*
* @param bf
* @return
*/
public static Optional<String> readMaybeString(ByteBuf bf) {
Optional<byte[]> bytes = readMaybeRangedBytes(bf);
return bytes.map(b -> {
if (b.length == 0) return "";
else return new String(b, CharsetUtil.UTF_8);
});
}
public static Optional<Optional<String>> readMaybeOptString(ByteBuf bf) {
return readMaybeOptRangedBytes(bf).map(optionalBytes -> optionalBytes.map(
bytes -> {
if (bytes.length == 0) return "";
else return new String(bytes, CharsetUtil.UTF_8);
}));
}
public static void writeUnsignedShort(int i, ByteBuf bf) {
bf.writeShort(i);
}
public static void writeUnsignedInt(int i, ByteBuf bf) {
VInt.write(bf, i);
}
public static void writeUnsignedLong(long l, ByteBuf bf) {
VLong.write(bf, l);
}
public static void writeRangedBytes(byte[] src, ByteBuf bf) {
writeUnsignedInt(src.length, bf);
if (src.length > 0)
bf.writeBytes(src);
}
public static void writeRangedBytes(byte[] src, int offset, ByteBuf bf) {
int l = src.length - offset;
writeUnsignedInt(l, bf);
if (l > 0)
bf.writeBytes(src);
}
public static void writeString(String msg, ByteBuf bf) {
writeRangedBytes(msg.getBytes(CharsetUtil.UTF_8), bf);
}
public static void writeString(Optional<String> msg, ByteBuf bf) {
writeRangedBytes(msg.map(m -> m.getBytes(CharsetUtil.UTF_8)).orElse(new byte[0]), bf);
}
}