mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 19:59:05 +01:00
Compact encodings for integers, strings and byte arrays.
This adds complexity but will save a lot of bandwidth, as most of the strings and byte arrays we want to send are less than 128 bytes. The extra complexity isn't exposed outside of the serial component.
This commit is contained in:
@@ -1,14 +1,21 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import static org.briarproject.serial.Tag.BYTES;
|
||||
import static org.briarproject.serial.Tag.BYTES_16;
|
||||
import static org.briarproject.serial.Tag.BYTES_32;
|
||||
import static org.briarproject.serial.Tag.BYTES_8;
|
||||
import static org.briarproject.serial.Tag.END;
|
||||
import static org.briarproject.serial.Tag.FALSE;
|
||||
import static org.briarproject.serial.Tag.FLOAT;
|
||||
import static org.briarproject.serial.Tag.INTEGER;
|
||||
import static org.briarproject.serial.Tag.INTEGER_16;
|
||||
import static org.briarproject.serial.Tag.INTEGER_32;
|
||||
import static org.briarproject.serial.Tag.INTEGER_64;
|
||||
import static org.briarproject.serial.Tag.INTEGER_8;
|
||||
import static org.briarproject.serial.Tag.LIST;
|
||||
import static org.briarproject.serial.Tag.MAP;
|
||||
import static org.briarproject.serial.Tag.NULL;
|
||||
import static org.briarproject.serial.Tag.STRING;
|
||||
import static org.briarproject.serial.Tag.STRING_16;
|
||||
import static org.briarproject.serial.Tag.STRING_32;
|
||||
import static org.briarproject.serial.Tag.STRING_8;
|
||||
import static org.briarproject.serial.Tag.STRUCT;
|
||||
import static org.briarproject.serial.Tag.TRUE;
|
||||
|
||||
@@ -82,20 +89,6 @@ class ReaderImpl implements Reader {
|
||||
readIntoBuffer(buf, length, consume);
|
||||
}
|
||||
|
||||
private int readInt32(boolean consume) throws IOException {
|
||||
readIntoBuffer(4, consume);
|
||||
int value = 0;
|
||||
for(int i = 0; i < 4; i++) value |= (buf[i] & 0xFF) << (24 - i * 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
private long readInt64(boolean consume) throws IOException {
|
||||
readIntoBuffer(8, consume);
|
||||
long value = 0;
|
||||
for(int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
private void skip(int length) throws IOException {
|
||||
while(length > 0) {
|
||||
int read = in.read(buf, 0, Math.min(length, buf.length));
|
||||
@@ -154,18 +147,56 @@ class ReaderImpl implements Reader {
|
||||
public boolean hasInteger() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == INTEGER;
|
||||
return next == INTEGER_8 || next == INTEGER_16 || next == INTEGER_32 ||
|
||||
next == INTEGER_64;
|
||||
}
|
||||
|
||||
public long readInteger() throws IOException {
|
||||
if(!hasInteger()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
if(next == INTEGER_8) return readInt8(true);
|
||||
if(next == INTEGER_16) return readInt16(true);
|
||||
if(next == INTEGER_32) return readInt32(true);
|
||||
return readInt64(true);
|
||||
}
|
||||
|
||||
private int readInt8(boolean consume) throws IOException {
|
||||
readIntoBuffer(1, consume);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
private short readInt16(boolean consume) throws IOException {
|
||||
readIntoBuffer(2, consume);
|
||||
short value = (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
||||
if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
|
||||
throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
private int readInt32(boolean consume) throws IOException {
|
||||
readIntoBuffer(4, consume);
|
||||
int value = 0;
|
||||
for(int i = 0; i < 4; i++) value |= (buf[i] & 0xFF) << (24 - i * 8);
|
||||
if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
|
||||
throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
private long readInt64(boolean consume) throws IOException {
|
||||
readIntoBuffer(8, consume);
|
||||
long value = 0;
|
||||
for(int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
||||
if(value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE)
|
||||
throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
public void skipInteger() throws IOException {
|
||||
if(!hasInteger()) throw new FormatException();
|
||||
skip(8);
|
||||
if(next == INTEGER_8) skip(1);
|
||||
else if(next == INTEGER_16) skip(2);
|
||||
else if(next == INTEGER_32) skip(4);
|
||||
else skip(8);
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@@ -178,7 +209,10 @@ class ReaderImpl implements Reader {
|
||||
public double readFloat() throws IOException {
|
||||
if(!hasFloat()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return Double.longBitsToDouble(readInt64(true));
|
||||
readIntoBuffer(8, true);
|
||||
long value = 0;
|
||||
for(int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
||||
return Double.longBitsToDouble(value);
|
||||
}
|
||||
|
||||
public void skipFloat() throws IOException {
|
||||
@@ -190,22 +224,29 @@ class ReaderImpl implements Reader {
|
||||
public boolean hasString() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == STRING;
|
||||
return next == STRING_8 || next == STRING_16 || next == STRING_32;
|
||||
}
|
||||
|
||||
public String readString(int maxLength) throws IOException {
|
||||
if(!hasString()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
int length = readInt32(true);
|
||||
int length = readStringLength(true);
|
||||
if(length < 0 || length > maxLength) throw new FormatException();
|
||||
if(length == 0) return "";
|
||||
readIntoBuffer(length, true);
|
||||
return new String(buf, 0, length, "UTF-8");
|
||||
}
|
||||
|
||||
private int readStringLength(boolean consume) throws IOException {
|
||||
if(next == STRING_8) return readInt8(consume);
|
||||
if(next == STRING_16) return readInt16(consume);
|
||||
if(next == STRING_32) return readInt32(consume);
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public void skipString(int maxLength) throws IOException {
|
||||
if(!hasString()) throw new FormatException();
|
||||
int length = readInt32(false);
|
||||
int length = readStringLength(false);
|
||||
if(length < 0 || length > maxLength) throw new FormatException();
|
||||
skip(length);
|
||||
hasLookahead = false;
|
||||
@@ -214,13 +255,13 @@ class ReaderImpl implements Reader {
|
||||
public boolean hasBytes() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == BYTES;
|
||||
return next == BYTES_8 || next == BYTES_16 || next == BYTES_32;
|
||||
}
|
||||
|
||||
public byte[] readBytes(int maxLength) throws IOException {
|
||||
if(!hasBytes()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
int length = readInt32(true);
|
||||
int length = readBytesLength(true);
|
||||
if(length < 0 || length > maxLength) throw new FormatException();
|
||||
if(length == 0) return EMPTY_BUFFER;
|
||||
byte[] b = new byte[length];
|
||||
@@ -228,9 +269,16 @@ class ReaderImpl implements Reader {
|
||||
return b;
|
||||
}
|
||||
|
||||
private int readBytesLength(boolean consume) throws IOException {
|
||||
if(next == BYTES_8) return readInt8(consume);
|
||||
if(next == BYTES_16) return readInt16(consume);
|
||||
if(next == BYTES_32) return readInt32(consume);
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public void skipBytes(int maxLength) throws IOException {
|
||||
if(!hasBytes()) throw new FormatException();
|
||||
int length = readInt32(false);
|
||||
int length = readBytesLength(false);
|
||||
if(length < 0 || length > maxLength) throw new FormatException();
|
||||
skip(length);
|
||||
hasLookahead = false;
|
||||
|
||||
@@ -26,7 +26,13 @@ class SerialComponentImpl implements SerialComponent {
|
||||
}
|
||||
|
||||
public int getSerialisedUniqueIdLength() {
|
||||
// BYTES tag, 32-bit length, bytes
|
||||
return 5 + UniqueId.LENGTH;
|
||||
// BYTES_8, BYTES_16 or BYTES_32 tag, length, bytes
|
||||
return 1 + getLengthBytes(UniqueId.LENGTH) + UniqueId.LENGTH;
|
||||
}
|
||||
|
||||
private int getLengthBytes(int length) {
|
||||
if(length <= Byte.MAX_VALUE) return 1;
|
||||
if(length <= Short.MAX_VALUE) return 2;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,22 @@ package org.briarproject.serial;
|
||||
|
||||
interface Tag {
|
||||
|
||||
byte FALSE = 0;
|
||||
byte TRUE = 1;
|
||||
byte INTEGER = 2;
|
||||
byte FLOAT = 3;
|
||||
byte STRING = 4;
|
||||
byte BYTES = 5;
|
||||
byte LIST = 6;
|
||||
byte MAP = 7;
|
||||
byte STRUCT = 8;
|
||||
byte END = 9;
|
||||
byte NULL = 10;
|
||||
byte FALSE = 0x00;
|
||||
byte TRUE = 0x01;
|
||||
byte INTEGER_8 = 0x02;
|
||||
byte INTEGER_16 = 0x03;
|
||||
byte INTEGER_32 = 0x04;
|
||||
byte INTEGER_64 = 0x05;
|
||||
byte FLOAT = 0x06;
|
||||
byte STRING_8 = 0x07;
|
||||
byte STRING_16 = 0x08;
|
||||
byte STRING_32 = 0x09;
|
||||
byte BYTES_8 = 0x0A;
|
||||
byte BYTES_16 = 0x0B;
|
||||
byte BYTES_32 = 0x0C;
|
||||
byte LIST = 0x0D;
|
||||
byte MAP = 0x0E;
|
||||
byte STRUCT = 0x0F;
|
||||
byte END = 0x10;
|
||||
byte NULL = 0x11;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import static org.briarproject.serial.Tag.BYTES_16;
|
||||
import static org.briarproject.serial.Tag.BYTES_32;
|
||||
import static org.briarproject.serial.Tag.BYTES_8;
|
||||
import static org.briarproject.serial.Tag.FALSE;
|
||||
import static org.briarproject.serial.Tag.FLOAT;
|
||||
import static org.briarproject.serial.Tag.INTEGER;
|
||||
import static org.briarproject.serial.Tag.STRING;
|
||||
import static org.briarproject.serial.Tag.INTEGER_16;
|
||||
import static org.briarproject.serial.Tag.INTEGER_32;
|
||||
import static org.briarproject.serial.Tag.INTEGER_64;
|
||||
import static org.briarproject.serial.Tag.INTEGER_8;
|
||||
import static org.briarproject.serial.Tag.STRING_16;
|
||||
import static org.briarproject.serial.Tag.STRING_32;
|
||||
import static org.briarproject.serial.Tag.STRING_8;
|
||||
import static org.briarproject.serial.Tag.TRUE;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -49,20 +57,43 @@ class WriterImpl implements Writer {
|
||||
else write(FALSE);
|
||||
}
|
||||
|
||||
public void writeInteger(long l) throws IOException {
|
||||
write(INTEGER);
|
||||
writeInt64(l);
|
||||
public void writeInteger(long i) throws IOException {
|
||||
if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
|
||||
write(INTEGER_8);
|
||||
write((byte) i);
|
||||
} else if(i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
|
||||
write(INTEGER_16);
|
||||
writeInt16((short) i);
|
||||
} else if(i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE) {
|
||||
write(INTEGER_32);
|
||||
writeInt32((int) i);
|
||||
} else {
|
||||
write(INTEGER_64);
|
||||
writeInt64(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInt64(long l) throws IOException {
|
||||
write((byte) (l >> 56));
|
||||
write((byte) ((l << 8) >> 56));
|
||||
write((byte) ((l << 16) >> 56));
|
||||
write((byte) ((l << 24) >> 56));
|
||||
write((byte) ((l << 32) >> 56));
|
||||
write((byte) ((l << 40) >> 56));
|
||||
write((byte) ((l << 48) >> 56));
|
||||
write((byte) ((l << 56) >> 56));
|
||||
private void writeInt16(short i) throws IOException {
|
||||
write((byte) (i >> 8));
|
||||
write((byte) ((i << 8) >> 8));
|
||||
}
|
||||
|
||||
private void writeInt32(int i) throws IOException {
|
||||
write((byte) (i >> 24));
|
||||
write((byte) ((i << 8) >> 24));
|
||||
write((byte) ((i << 16) >> 24));
|
||||
write((byte) ((i << 24) >> 24));
|
||||
}
|
||||
|
||||
private void writeInt64(long i) throws IOException {
|
||||
write((byte) (i >> 56));
|
||||
write((byte) ((i << 8) >> 56));
|
||||
write((byte) ((i << 16) >> 56));
|
||||
write((byte) ((i << 24) >> 56));
|
||||
write((byte) ((i << 32) >> 56));
|
||||
write((byte) ((i << 40) >> 56));
|
||||
write((byte) ((i << 48) >> 56));
|
||||
write((byte) ((i << 56) >> 56));
|
||||
}
|
||||
|
||||
public void writeFloat(double d) throws IOException {
|
||||
@@ -72,22 +103,30 @@ class WriterImpl implements Writer {
|
||||
|
||||
public void writeString(String s) throws IOException {
|
||||
byte[] b = s.getBytes("UTF-8");
|
||||
write(STRING);
|
||||
writeLength(b.length);
|
||||
if(b.length <= Byte.MAX_VALUE) {
|
||||
write(STRING_8);
|
||||
write((byte) b.length);
|
||||
} else if(b.length <= Short.MAX_VALUE) {
|
||||
write(STRING_16);
|
||||
writeInt16((short) b.length);
|
||||
} else {
|
||||
write(STRING_32);
|
||||
writeInt32(b.length);
|
||||
}
|
||||
write(b);
|
||||
}
|
||||
|
||||
private void writeLength(int i) throws IOException {
|
||||
assert i >= 0;
|
||||
write((byte) (i >> 24));
|
||||
write((byte) ((i << 8) >> 24));
|
||||
write((byte) ((i << 16) >> 24));
|
||||
write((byte) ((i << 24) >> 24));
|
||||
}
|
||||
|
||||
public void writeBytes(byte[] b) throws IOException {
|
||||
write(Tag.BYTES);
|
||||
writeLength(b.length);
|
||||
if(b.length <= Byte.MAX_VALUE) {
|
||||
write(BYTES_8);
|
||||
write((byte) b.length);
|
||||
} else if(b.length <= Short.MAX_VALUE) {
|
||||
write(BYTES_16);
|
||||
writeInt16((short) b.length);
|
||||
} else {
|
||||
write(BYTES_32);
|
||||
writeInt32(b.length);
|
||||
}
|
||||
write(b);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user