Overgram/lib/src/main/java/io/github/lonamiwebs/overgram/utils/BinaryWriter.java

164 lines
4.5 KiB
Java

package io.github.lonamiwebs.overgram.utils;
import io.github.lonamiwebs.overgram.tl.TLObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
public class BinaryWriter {
private static final ByteOrder ORDER = ByteOrder.LITTLE_ENDIAN;
private final ByteArrayOutputStream buffer;
public BinaryWriter() {
buffer = new ByteArrayOutputStream(128);
}
public BinaryWriter(final int capacity) {
buffer = new ByteArrayOutputStream(capacity);
}
public void write(final byte value) {
buffer.write(value);
}
public void write(final int value) {
try {
buffer.write(ByteBuffer.allocate(4).order(ORDER).putInt(value).array());
} catch (IOException ignored) {
throw new RuntimeException();
}
}
public void write(final long value) {
try {
buffer.write(ByteBuffer.allocate(8).order(ORDER).putLong(value).array());
} catch (IOException ignored) {
throw new RuntimeException();
}
}
public void write(final BigInteger value, final int size) {
final byte[] bytes = value.toByteArray();
final byte[] result;
if (bytes.length == size) {
result = bytes;
} else {
result = new byte[size];
final byte fill = value.signum() > 0 ? 0 : (byte) 0xff;
System.arraycopy(bytes, 0, result, size - bytes.length, bytes.length);
for (int i = size - bytes.length; i-- != 0; ) {
result[i] = fill;
}
}
buffer.write(ORDER == ByteOrder.BIG_ENDIAN ? result : Utils.reversed(result), 0, size);
}
public void write(final double value) {
try {
buffer.write(ByteBuffer.allocate(8).order(ORDER).putDouble(value).array());
} catch (IOException ignored) {
throw new RuntimeException();
}
}
public void write(final boolean value) {
write(value ? 0x997275b5 : 0xbc799737);
}
public void write(final byte[] bytes) {
try {
int padding;
if (bytes.length < 0xfe) {
padding = (bytes.length + 1) % 4;
buffer.write(bytes.length);
buffer.write(bytes);
} else {
padding = bytes.length % 4;
write(bytes.length << 8 | 0xfe);
buffer.write(bytes);
}
if (padding != 0) {
for (padding = 4 - padding; padding-- != 0; ) {
buffer.write(0);
}
}
} catch (IOException ignored) {
throw new RuntimeException();
}
}
public void writeRaw(final byte[] bytes) {
buffer.write(bytes, 0, bytes.length);
}
public void writeRaw(final byte[] bytes, final int start, final int length) {
buffer.write(bytes, start, length);
}
public void write(final String string) {
write(StandardCharsets.UTF_8.encode(string).array());
}
// TODO Handle boxed vs unboxed types (and vector<>)
public void write(final TLObject object) {
object.serialize(this);
}
@SuppressWarnings("unchecked")
public void write(final List<?> objects) {
write(0x1cb5c415);
write(objects.size());
if (objects.isEmpty()) {
return;
}
final Object first = objects.get(0);
if (first instanceof TLObject) {
for (final TLObject obj : (List<TLObject>) objects) {
write(obj);
}
} else if (first instanceof Integer) {
for (final Integer obj : (List<Integer>) objects) {
write(obj);
}
} else if (first instanceof Long) {
for (final Long obj : (List<Long>) objects) {
write(obj);
}
} else if (first instanceof byte[]) {
for (final byte[] obj : (List<byte[]>) objects) {
write(obj);
}
} else {
throw new UnsupportedOperationException();
}
}
public void clear() {
buffer.reset();
}
public int size() {
return buffer.size();
}
public byte[] toBytes() {
return buffer.toByteArray();
}
@Override
public String toString() {
return Arrays.toString(toBytes());
}
}