mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Delimited structs - this will allow us to skip unrecognised structs.
This commit is contained in:
@@ -40,30 +40,28 @@ public interface Reader {
|
||||
double readFloat64() throws IOException;
|
||||
|
||||
boolean hasString() throws IOException;
|
||||
String readString() throws IOException;
|
||||
String readString(int maxLength) throws IOException;
|
||||
|
||||
boolean hasBytes() throws IOException;
|
||||
byte[] readBytes() throws IOException;
|
||||
byte[] readBytes(int maxLength) throws IOException;
|
||||
|
||||
boolean hasList() throws IOException;
|
||||
<E> List<E> readList(Class<E> e) throws IOException;
|
||||
boolean hasListStart() throws IOException;
|
||||
void readListStart() throws IOException;
|
||||
boolean hasListEnd() throws IOException;
|
||||
void readListEnd() throws IOException;
|
||||
|
||||
boolean hasMap() throws IOException;
|
||||
<K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException;
|
||||
boolean hasMapStart() throws IOException;
|
||||
void readMapStart() throws IOException;
|
||||
boolean hasMapEnd() throws IOException;
|
||||
void readMapEnd() throws IOException;
|
||||
|
||||
boolean hasStruct(int id) throws IOException;
|
||||
void readStructStart(int id) throws IOException;
|
||||
boolean hasStructEnd() throws IOException;
|
||||
void readStructEnd() throws IOException;
|
||||
|
||||
boolean hasNull() throws IOException;
|
||||
void readNull() throws IOException;
|
||||
|
||||
boolean hasStruct(int id) throws IOException;
|
||||
void readStructId(int id) throws IOException;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package net.sf.briar.api.serial;
|
||||
|
||||
public interface SerialComponent {
|
||||
|
||||
int getSerialisedListEndLength();
|
||||
|
||||
int getSerialisedListStartLength();
|
||||
|
||||
int getSerialisedStructIdLength(int id);
|
||||
int getSerialisedListEndLength();
|
||||
|
||||
int getSerialisedStructStartLength(int id);
|
||||
|
||||
int getSerialisedStructEndLength();
|
||||
|
||||
int getSerialisedUniqueIdLength();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ public interface Writer {
|
||||
void writeMapStart() throws IOException;
|
||||
void writeMapEnd() throws IOException;
|
||||
|
||||
void writeNull() throws IOException;
|
||||
void writeStructStart(int id) throws IOException;
|
||||
void writeStructEnd() throws IOException;
|
||||
|
||||
void writeStructId(int id) throws IOException;
|
||||
void writeNull() throws IOException;
|
||||
}
|
||||
|
||||
@@ -41,9 +41,10 @@ class AuthorFactoryImpl implements AuthorFactory {
|
||||
private AuthorId getId(String name, byte[] publicKey) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(AUTHOR);
|
||||
w.writeStructStart(AUTHOR);
|
||||
w.writeString(name);
|
||||
w.writeBytes(publicKey);
|
||||
w.writeStructEnd();
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
messageDigest.update(out.toByteArray());
|
||||
return new AuthorId(messageDigest.digest());
|
||||
|
||||
@@ -23,12 +23,15 @@ class AuthorReader implements StructReader<Author> {
|
||||
}
|
||||
|
||||
public Author readStruct(Reader r) throws IOException {
|
||||
// Set up the reader
|
||||
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
||||
// Read and digest the data
|
||||
r.addConsumer(digesting);
|
||||
r.readStructId(AUTHOR);
|
||||
// Read and digest the data
|
||||
r.readStructStart(AUTHOR);
|
||||
String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] publicKey = r.readBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.removeConsumer(digesting);
|
||||
// Build and return the author
|
||||
AuthorId id = new AuthorId(messageDigest.digest());
|
||||
|
||||
@@ -36,9 +36,10 @@ class GroupFactoryImpl implements GroupFactory {
|
||||
public Group createGroup(String name, byte[] salt) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(GROUP);
|
||||
w.writeStructStart(GROUP);
|
||||
w.writeString(name);
|
||||
w.writeBytes(salt);
|
||||
w.writeStructEnd();
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
messageDigest.update(out.toByteArray());
|
||||
GroupId id = new GroupId(messageDigest.digest());
|
||||
|
||||
@@ -26,11 +26,12 @@ class GroupReader implements StructReader<Group> {
|
||||
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
||||
// Read and digest the data
|
||||
r.addConsumer(digesting);
|
||||
r.readStructId(GROUP);
|
||||
r.readStructStart(GROUP);
|
||||
String name = r.readString(MAX_GROUP_NAME_LENGTH);
|
||||
byte[] publicKey = null;
|
||||
if(r.hasNull()) r.readNull();
|
||||
else publicKey = r.readBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
r.readStructEnd();
|
||||
r.removeConsumer(digesting);
|
||||
// Build and return the group
|
||||
GroupId id = new GroupId(messageDigest.digest());
|
||||
|
||||
@@ -100,7 +100,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
w.addConsumer(signingConsumer);
|
||||
}
|
||||
// Write the message
|
||||
w.writeStructId(MESSAGE);
|
||||
w.writeStructStart(MESSAGE);
|
||||
if(parent == null) w.writeNull();
|
||||
else w.writeBytes(parent.getBytes());
|
||||
if(group == null) w.writeNull();
|
||||
@@ -109,7 +109,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
else writeAuthor(w, author);
|
||||
w.writeString(contentType);
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
w.writeInt64(timestamp);
|
||||
w.writeIntAny(timestamp);
|
||||
byte[] salt = new byte[MESSAGE_SALT_LENGTH];
|
||||
random.nextBytes(salt);
|
||||
w.writeBytes(salt);
|
||||
@@ -125,6 +125,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
throw new IllegalArgumentException();
|
||||
w.writeBytes(sig);
|
||||
}
|
||||
w.writeStructEnd();
|
||||
// Hash the message, including the signature, to get the message ID
|
||||
w.removeConsumer(digestingConsumer);
|
||||
MessageId id = new MessageId(messageDigest.digest());
|
||||
@@ -143,14 +144,16 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
}
|
||||
|
||||
private void writeGroup(Writer w, Group g) throws IOException {
|
||||
w.writeStructId(GROUP);
|
||||
w.writeStructStart(GROUP);
|
||||
w.writeString(g.getName());
|
||||
w.writeBytes(g.getSalt());
|
||||
w.writeStructEnd();
|
||||
}
|
||||
|
||||
private void writeAuthor(Writer w, Author a) throws IOException {
|
||||
w.writeStructId(AUTHOR);
|
||||
w.writeStructStart(AUTHOR);
|
||||
w.writeString(a.getName());
|
||||
w.writeBytes(a.getPublicKey());
|
||||
w.writeStructEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
|
||||
CountingConsumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
|
||||
r.addConsumer(copying);
|
||||
r.addConsumer(counting);
|
||||
// Read the initial tag
|
||||
r.readStructId(MESSAGE);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(MESSAGE);
|
||||
// Read the parent's message ID, if there is one
|
||||
MessageId parent = null;
|
||||
if(r.hasNull()) {
|
||||
@@ -64,7 +64,7 @@ class MessageReader implements StructReader<UnverifiedMessage> {
|
||||
// Read the content type
|
||||
String contentType = r.readString(MAX_CONTENT_TYPE_LENGTH);
|
||||
// Read the timestamp
|
||||
long timestamp = r.readInt64();
|
||||
long timestamp = r.readIntAny();
|
||||
if(timestamp < 0) throw new FormatException();
|
||||
// Read the salt
|
||||
byte[] salt = r.readBytes(MESSAGE_SALT_LENGTH);
|
||||
@@ -89,6 +89,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
|
||||
byte[] signature = null;
|
||||
if(author == null) r.readNull();
|
||||
else signature = r.readBytes(MAX_SIGNATURE_LENGTH);
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// The signature will be verified later
|
||||
r.removeConsumer(counting);
|
||||
r.removeConsumer(copying);
|
||||
|
||||
@@ -70,12 +70,17 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public Ack readAck() throws IOException {
|
||||
// Set up the reader
|
||||
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
|
||||
r.addConsumer(counting);
|
||||
r.readStructId(ACK);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(ACK);
|
||||
// Read the message IDs as byte arrays
|
||||
r.setMaxBytesLength(UniqueId.LENGTH);
|
||||
List<Bytes> raw = r.readList(Bytes.class);
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.resetMaxBytesLength();
|
||||
r.removeConsumer(counting);
|
||||
if(raw.isEmpty()) throw new FormatException();
|
||||
@@ -103,12 +108,17 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public Offer readOffer() throws IOException {
|
||||
// Set up the reader
|
||||
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
|
||||
r.addConsumer(counting);
|
||||
r.readStructId(OFFER);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(OFFER);
|
||||
// Read the message IDs as byte arrays
|
||||
r.setMaxBytesLength(UniqueId.LENGTH);
|
||||
List<Bytes> raw = r.readList(Bytes.class);
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.resetMaxBytesLength();
|
||||
r.removeConsumer(counting);
|
||||
if(raw.isEmpty()) throw new FormatException();
|
||||
@@ -128,14 +138,19 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public Request readRequest() throws IOException {
|
||||
// Set up the reader
|
||||
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
|
||||
r.addConsumer(counting);
|
||||
r.readStructId(REQUEST);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(REQUEST);
|
||||
// There may be up to 7 bits of padding at the end of the bitmap
|
||||
int padding = r.readUint7();
|
||||
if(padding > 7) throw new FormatException();
|
||||
// Read the bitmap
|
||||
byte[] bitmap = r.readBytes(MAX_PACKET_LENGTH);
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.removeConsumer(counting);
|
||||
// Convert the bitmap into a BitSet
|
||||
int length = bitmap.length * 8 - padding;
|
||||
@@ -154,9 +169,10 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public RetentionAck readRetentionAck() throws IOException {
|
||||
r.readStructId(RETENTION_ACK);
|
||||
long version = r.readInt64();
|
||||
r.readStructStart(RETENTION_ACK);
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
r.readStructEnd();
|
||||
return new RetentionAck(version);
|
||||
}
|
||||
|
||||
@@ -165,11 +181,12 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public RetentionUpdate readRetentionUpdate() throws IOException {
|
||||
r.readStructId(RETENTION_UPDATE);
|
||||
long retention = r.readInt64();
|
||||
r.readStructStart(RETENTION_UPDATE);
|
||||
long retention = r.readIntAny();
|
||||
if(retention < 0) throw new FormatException();
|
||||
long version = r.readInt64();
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
r.readStructEnd();
|
||||
return new RetentionUpdate(retention, version);
|
||||
}
|
||||
|
||||
@@ -178,9 +195,10 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public SubscriptionAck readSubscriptionAck() throws IOException {
|
||||
r.readStructId(SUBSCRIPTION_ACK);
|
||||
long version = r.readInt64();
|
||||
r.readStructStart(SUBSCRIPTION_ACK);
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
r.readStructEnd();
|
||||
return new SubscriptionAck(version);
|
||||
}
|
||||
|
||||
@@ -197,11 +215,12 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public TransportAck readTransportAck() throws IOException {
|
||||
r.readStructId(TRANSPORT_ACK);
|
||||
r.readStructStart(TRANSPORT_ACK);
|
||||
byte[] b = r.readBytes(UniqueId.LENGTH);
|
||||
if(b.length < UniqueId.LENGTH) throw new FormatException();
|
||||
long version = r.readInt64();
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
r.readStructEnd();
|
||||
return new TransportAck(new TransportId(b), version);
|
||||
}
|
||||
|
||||
@@ -210,9 +229,11 @@ class PacketReaderImpl implements PacketReader {
|
||||
}
|
||||
|
||||
public TransportUpdate readTransportUpdate() throws IOException {
|
||||
// Set up the reader
|
||||
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
|
||||
r.addConsumer(counting);
|
||||
r.readStructId(TRANSPORT_UPDATE);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(TRANSPORT_UPDATE);
|
||||
// Read the transport ID
|
||||
byte[] b = r.readBytes(UniqueId.LENGTH);
|
||||
if(b.length < UniqueId.LENGTH) throw new FormatException();
|
||||
@@ -223,8 +244,11 @@ class PacketReaderImpl implements PacketReader {
|
||||
r.resetMaxStringLength();
|
||||
if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) throw new FormatException();
|
||||
// Read the version number
|
||||
long version = r.readInt64();
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.removeConsumer(counting);
|
||||
// Build and return the transport update
|
||||
return new TransportUpdate(id, new TransportProperties(m), version);
|
||||
|
||||
@@ -50,27 +50,30 @@ class PacketWriterImpl implements PacketWriter {
|
||||
|
||||
public int getMaxMessagesForAck(long capacity) {
|
||||
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
|
||||
int overhead = serial.getSerialisedStructIdLength(ACK)
|
||||
int overhead = serial.getSerialisedStructStartLength(ACK)
|
||||
+ serial.getSerialisedListStartLength()
|
||||
+ serial.getSerialisedListEndLength();
|
||||
+ serial.getSerialisedListEndLength()
|
||||
+ serial.getSerialisedStructEndLength();
|
||||
int idLength = serial.getSerialisedUniqueIdLength();
|
||||
return (packet - overhead) / idLength;
|
||||
}
|
||||
|
||||
public int getMaxMessagesForOffer(long capacity) {
|
||||
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
|
||||
int overhead = serial.getSerialisedStructIdLength(OFFER)
|
||||
int overhead = serial.getSerialisedStructStartLength(OFFER)
|
||||
+ serial.getSerialisedListStartLength()
|
||||
+ serial.getSerialisedListEndLength();
|
||||
+ serial.getSerialisedListEndLength()
|
||||
+ serial.getSerialisedStructEndLength();
|
||||
int idLength = serial.getSerialisedUniqueIdLength();
|
||||
return (packet - overhead) / idLength;
|
||||
}
|
||||
|
||||
public void writeAck(Ack a) throws IOException {
|
||||
w.writeStructId(ACK);
|
||||
w.writeStructStart(ACK);
|
||||
w.writeListStart();
|
||||
for(MessageId m : a.getMessageIds()) w.writeBytes(m.getBytes());
|
||||
w.writeListEnd();
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
@@ -80,10 +83,11 @@ class PacketWriterImpl implements PacketWriter {
|
||||
}
|
||||
|
||||
public void writeOffer(Offer o) throws IOException {
|
||||
w.writeStructId(OFFER);
|
||||
w.writeStructStart(OFFER);
|
||||
w.writeListStart();
|
||||
for(MessageId m : o.getMessageIds()) w.writeBytes(m.getBytes());
|
||||
w.writeListEnd();
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
@@ -101,57 +105,65 @@ class PacketWriterImpl implements PacketWriter {
|
||||
bitmap[offset] |= bit;
|
||||
}
|
||||
}
|
||||
w.writeStructId(REQUEST);
|
||||
w.writeStructStart(REQUEST);
|
||||
w.writeUint7((byte) (bytes * 8 - length));
|
||||
w.writeBytes(bitmap);
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeRetentionAck(RetentionAck a) throws IOException {
|
||||
w.writeStructId(RETENTION_ACK);
|
||||
w.writeInt64(a.getVersion());
|
||||
w.writeStructStart(RETENTION_ACK);
|
||||
w.writeIntAny(a.getVersion());
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeRetentionUpdate(RetentionUpdate u) throws IOException {
|
||||
w.writeStructId(RETENTION_UPDATE);
|
||||
w.writeInt64(u.getRetentionTime());
|
||||
w.writeInt64(u.getVersion());
|
||||
w.writeStructStart(RETENTION_UPDATE);
|
||||
w.writeIntAny(u.getRetentionTime());
|
||||
w.writeIntAny(u.getVersion());
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeSubscriptionAck(SubscriptionAck a) throws IOException {
|
||||
w.writeStructId(SUBSCRIPTION_ACK);
|
||||
w.writeInt64(a.getVersion());
|
||||
w.writeStructStart(SUBSCRIPTION_ACK);
|
||||
w.writeIntAny(a.getVersion());
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeSubscriptionUpdate(SubscriptionUpdate u)
|
||||
throws IOException {
|
||||
w.writeStructId(SUBSCRIPTION_UPDATE);
|
||||
w.writeStructStart(SUBSCRIPTION_UPDATE);
|
||||
w.writeListStart();
|
||||
for(Group g : u.getGroups()) {
|
||||
w.writeStructId(GROUP);
|
||||
w.writeStructStart(GROUP);
|
||||
w.writeString(g.getName());
|
||||
w.writeBytes(g.getSalt());
|
||||
w.writeStructEnd();
|
||||
}
|
||||
w.writeListEnd();
|
||||
w.writeInt64(u.getVersion());
|
||||
w.writeIntAny(u.getVersion());
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeTransportAck(TransportAck a) throws IOException {
|
||||
w.writeStructId(TRANSPORT_ACK);
|
||||
w.writeStructStart(TRANSPORT_ACK);
|
||||
w.writeBytes(a.getId().getBytes());
|
||||
w.writeInt64(a.getVersion());
|
||||
w.writeIntAny(a.getVersion());
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
public void writeTransportUpdate(TransportUpdate u) throws IOException {
|
||||
w.writeStructId(TRANSPORT_UPDATE);
|
||||
w.writeStructStart(TRANSPORT_UPDATE);
|
||||
w.writeBytes(u.getId().getBytes());
|
||||
w.writeMap(u.getProperties());
|
||||
w.writeInt64(u.getVersion());
|
||||
w.writeIntAny(u.getVersion());
|
||||
w.writeStructEnd();
|
||||
if(flush) out.flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,11 @@ class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> {
|
||||
}
|
||||
|
||||
public SubscriptionUpdate readStruct(Reader r) throws IOException {
|
||||
// Set up the reader
|
||||
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
|
||||
r.addConsumer(counting);
|
||||
r.readStructId(SUBSCRIPTION_UPDATE);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(SUBSCRIPTION_UPDATE);
|
||||
// Read the subscriptions
|
||||
List<Group> subs = new ArrayList<Group>();
|
||||
r.readListStart();
|
||||
@@ -36,8 +38,11 @@ class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> {
|
||||
subs.add(groupReader.readStruct(r));
|
||||
r.readListEnd();
|
||||
// Read the version number
|
||||
long version = r.readInt64();
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.removeConsumer(counting);
|
||||
// Build and return the subscription update
|
||||
subs = Collections.unmodifiableList(subs);
|
||||
|
||||
@@ -23,8 +23,8 @@ class ReaderImpl implements Reader {
|
||||
private final Collection<Consumer> consumers = new ArrayList<Consumer>(0);
|
||||
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next, nextNext;
|
||||
private byte[] buf = null;
|
||||
private byte next, nextStructId;
|
||||
private byte[] buf = new byte[8];
|
||||
private int maxStringLength = Integer.MAX_VALUE;
|
||||
private int maxBytesLength = Integer.MAX_VALUE;
|
||||
|
||||
@@ -33,42 +33,40 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean eof() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
return eof;
|
||||
}
|
||||
|
||||
private byte readLookahead(boolean eofAcceptable) throws IOException {
|
||||
private void readLookahead() throws IOException {
|
||||
assert !eof;
|
||||
// If one or two lookahead bytes have been read, feed the consumers
|
||||
if(hasLookahead) consumeLookahead();
|
||||
// Read a lookahead byte
|
||||
int i = in.read();
|
||||
if(i == -1) {
|
||||
if(!eofAcceptable) throw new FormatException();
|
||||
eof = true;
|
||||
return;
|
||||
}
|
||||
next = (byte) i;
|
||||
// If necessary, read another lookahead byte
|
||||
if(next == Tag.STRUCT) {
|
||||
i = in.read();
|
||||
if(i == -1) throw new FormatException();
|
||||
nextNext = (byte) i;
|
||||
nextStructId = (byte) i;
|
||||
}
|
||||
hasLookahead = true;
|
||||
return next;
|
||||
}
|
||||
|
||||
private void consumeLookahead() throws IOException {
|
||||
assert hasLookahead;
|
||||
for(Consumer c : consumers) {
|
||||
c.write(next);
|
||||
if(next == Tag.STRUCT) c.write(nextNext);
|
||||
if(next == Tag.STRUCT) c.write(nextStructId);
|
||||
}
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
buf = null;
|
||||
in.close();
|
||||
}
|
||||
|
||||
@@ -97,7 +95,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasBoolean() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.FALSE || next == Tag.TRUE;
|
||||
}
|
||||
@@ -109,7 +107,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasUint7() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next >= 0;
|
||||
}
|
||||
@@ -121,34 +119,37 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasInt8() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT8;
|
||||
}
|
||||
|
||||
public byte readInt8() throws IOException {
|
||||
if(!hasInt8()) throw new FormatException();
|
||||
readLookahead(false);
|
||||
consumeLookahead();
|
||||
return next;
|
||||
int i = in.read();
|
||||
if(i == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
return (byte) i;
|
||||
}
|
||||
|
||||
public boolean hasInt16() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT16;
|
||||
}
|
||||
|
||||
public short readInt16() throws IOException {
|
||||
if(!hasInt16()) throw new FormatException();
|
||||
byte b1 = readLookahead(false);
|
||||
byte b2 = readLookahead(false);
|
||||
consumeLookahead();
|
||||
return (short) (((b1 & 0xFF) << 8) | (b2 & 0xFF));
|
||||
readIntoBuffer(2);
|
||||
return (short) (((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF));
|
||||
}
|
||||
|
||||
public boolean hasInt32() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT32;
|
||||
}
|
||||
@@ -166,7 +167,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
private void readIntoBuffer(int length) throws IOException {
|
||||
if(buf == null || buf.length < length) buf = new byte[length];
|
||||
if(buf.length < length) buf = new byte[length];
|
||||
readIntoBuffer(buf, length);
|
||||
}
|
||||
|
||||
@@ -177,17 +178,16 @@ class ReaderImpl implements Reader {
|
||||
int read = in.read(b, offset, length - offset);
|
||||
if(read == -1) {
|
||||
eof = true;
|
||||
break;
|
||||
throw new FormatException();
|
||||
}
|
||||
offset += read;
|
||||
}
|
||||
if(offset < length) throw new FormatException();
|
||||
// Feed the hungry mouths
|
||||
for(Consumer c : consumers) c.write(b, 0, length);
|
||||
}
|
||||
|
||||
public boolean hasInt64() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT64;
|
||||
}
|
||||
@@ -207,7 +207,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasIntAny() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|
||||
|| next == Tag.INT32 || next == Tag.INT64;
|
||||
@@ -224,7 +224,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasFloat32() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.FLOAT32;
|
||||
}
|
||||
@@ -236,7 +236,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasFloat64() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.FLOAT64;
|
||||
}
|
||||
@@ -248,15 +248,11 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasString() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.STRING;
|
||||
}
|
||||
|
||||
public String readString() throws IOException {
|
||||
return readString(maxStringLength);
|
||||
}
|
||||
|
||||
public String readString(int maxLength) throws IOException {
|
||||
if(!hasString()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
@@ -269,30 +265,29 @@ class ReaderImpl implements Reader {
|
||||
|
||||
private int readLength() throws IOException {
|
||||
if(!hasLength()) throw new FormatException();
|
||||
if(next >= 0) return readUint7();
|
||||
if(next == Tag.INT8) return readInt8();
|
||||
if(next == Tag.INT16) return readInt16();
|
||||
if(next == Tag.INT32) return readInt32();
|
||||
throw new IllegalStateException();
|
||||
int length;
|
||||
if(next >= 0) length = readUint7();
|
||||
else if(next == Tag.INT8) length = readInt8();
|
||||
else if(next == Tag.INT16) length = readInt16();
|
||||
else if(next == Tag.INT32) length = readInt32();
|
||||
else throw new IllegalStateException();
|
||||
if(length < 0) throw new FormatException();
|
||||
return length;
|
||||
}
|
||||
|
||||
private boolean hasLength() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|
||||
|| next == Tag.INT32;
|
||||
}
|
||||
|
||||
public boolean hasBytes() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.BYTES;
|
||||
}
|
||||
|
||||
public byte[] readBytes() throws IOException {
|
||||
return readBytes(maxBytesLength);
|
||||
}
|
||||
|
||||
public byte[] readBytes(int maxLength) throws IOException {
|
||||
if(!hasBytes()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
@@ -305,7 +300,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasList() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.LIST;
|
||||
}
|
||||
@@ -320,13 +315,12 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.END;
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if(!hasLookahead) throw new IllegalStateException();
|
||||
if(!hasEnd()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
@@ -340,8 +334,8 @@ class ReaderImpl implements Reader {
|
||||
if(hasInt64()) return Long.valueOf(readInt64());
|
||||
if(hasFloat32()) return Float.valueOf(readFloat32());
|
||||
if(hasFloat64()) return Double.valueOf(readFloat64());
|
||||
if(hasString()) return readString();
|
||||
if(hasBytes()) return new Bytes(readBytes());
|
||||
if(hasString()) return readString(maxStringLength);
|
||||
if(hasBytes()) return new Bytes(readBytes(maxBytesLength));
|
||||
if(hasList()) return readList(Object.class);
|
||||
if(hasMap()) return readMap(Object.class, Object.class);
|
||||
if(hasNull()) {
|
||||
@@ -378,14 +372,8 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasListStart() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(eof) return false;
|
||||
return next == Tag.LIST;
|
||||
}
|
||||
|
||||
public void readListStart() throws IOException {
|
||||
if(!hasListStart()) throw new FormatException();
|
||||
if(!hasList()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
@@ -398,7 +386,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public boolean hasMap() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.MAP;
|
||||
}
|
||||
@@ -415,14 +403,8 @@ class ReaderImpl implements Reader {
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
public boolean hasMapStart() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(eof) return false;
|
||||
return next == Tag.MAP;
|
||||
}
|
||||
|
||||
public void readMapStart() throws IOException {
|
||||
if(!hasMapStart()) throw new FormatException();
|
||||
if(!hasMap()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
@@ -434,8 +416,28 @@ class ReaderImpl implements Reader {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
public boolean hasStruct(int id) throws IOException {
|
||||
if(id < 0 || id > 255) throw new IllegalArgumentException();
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return (nextStructId & 0xFF) == id;
|
||||
}
|
||||
|
||||
public void readStructStart(int id) throws IOException {
|
||||
if(!hasStruct(id)) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public boolean hasStructEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
public void readStructEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
public boolean hasNull() throws IOException {
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.NULL;
|
||||
}
|
||||
@@ -444,16 +446,4 @@ class ReaderImpl implements Reader {
|
||||
if(!hasNull()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public boolean hasStruct(int id) throws IOException {
|
||||
if(id < 0 || id > 255) throw new IllegalArgumentException();
|
||||
if(!hasLookahead) readLookahead(true);
|
||||
if(eof) return false;
|
||||
return id == (0xFF & nextNext);
|
||||
}
|
||||
|
||||
public void readStructId(int id) throws IOException {
|
||||
if(!hasStruct(id)) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,31 +5,35 @@ import net.sf.briar.api.serial.SerialComponent;
|
||||
|
||||
class SerialComponentImpl implements SerialComponent {
|
||||
|
||||
public int getSerialisedListEndLength() {
|
||||
// END tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedListStartLength() {
|
||||
// LIST tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedStructIdLength(int id) {
|
||||
if(id < 0 || id > 255) throw new IllegalArgumentException();
|
||||
return id < 32 ? 1 : 2;
|
||||
public int getSerialisedListEndLength() {
|
||||
// END tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedStructStartLength(int id) {
|
||||
// STRUCT tag, ID
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getSerialisedStructEndLength() {
|
||||
// END tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedUniqueIdLength() {
|
||||
// BYTES tag, length spec, bytes
|
||||
return 1 + getSerialisedLengthSpecLength(UniqueId.LENGTH)
|
||||
+ UniqueId.LENGTH;
|
||||
+ UniqueId.LENGTH;
|
||||
}
|
||||
|
||||
private int getSerialisedLengthSpecLength(int length) {
|
||||
if(length < 0) throw new IllegalArgumentException();
|
||||
if(length < 128) return 1; // Uint7
|
||||
if(length < Short.MAX_VALUE) return 3; // Int16
|
||||
return 5; // Int32
|
||||
// Uint7, int16 or int32
|
||||
return length <= Byte.MAX_VALUE ? 1 : length <= Short.MAX_VALUE ? 3 : 5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,19 @@ package net.sf.briar.serial;
|
||||
|
||||
interface Tag {
|
||||
|
||||
byte FALSE = (byte) 0xFF; // 1111 1111
|
||||
byte TRUE = (byte) 0xFE; // 1111 1110
|
||||
byte INT8 = (byte) 0xFD; // 1111 1101
|
||||
byte INT16 = (byte) 0xFC; // 1111 1100
|
||||
byte INT32 = (byte) 0xFB; // 1111 1011
|
||||
byte INT64 = (byte) 0xFA; // 1111 1010
|
||||
byte FLOAT32 = (byte) 0xF9; // 1111 1001
|
||||
byte FLOAT64 = (byte) 0xF8; // 1111 1000
|
||||
byte STRING = (byte) 0xF7; // 1111 0111
|
||||
byte BYTES = (byte) 0xF6; // 1111 0110
|
||||
byte LIST = (byte) 0xF5; // 1111 0111
|
||||
byte MAP = (byte) 0xF4; // 1111 0100
|
||||
byte END = (byte) 0xF3; // 1111 0011
|
||||
byte NULL = (byte) 0xF2; // 1111 0010
|
||||
byte STRUCT = (byte) 0xF1; // 1111 0001
|
||||
byte FALSE = (byte) 0xFF;
|
||||
byte TRUE = (byte) 0xFE;
|
||||
byte INT8 = (byte) 0xFD;
|
||||
byte INT16 = (byte) 0xFC;
|
||||
byte INT32 = (byte) 0xFB;
|
||||
byte INT64 = (byte) 0xFA;
|
||||
byte FLOAT32 = (byte) 0xF9;
|
||||
byte FLOAT64 = (byte) 0xF8;
|
||||
byte STRING = (byte) 0xF7;
|
||||
byte BYTES = (byte) 0xF6;
|
||||
byte LIST = (byte) 0xF5;
|
||||
byte MAP = (byte) 0xF4;
|
||||
byte STRUCT = (byte) 0xF3;
|
||||
byte END = (byte) 0xF2;
|
||||
byte NULL = (byte) 0xF1;
|
||||
}
|
||||
|
||||
@@ -177,16 +177,20 @@ class WriterImpl implements Writer {
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
public void writeNull() throws IOException {
|
||||
write(Tag.NULL);
|
||||
}
|
||||
|
||||
public void writeStructId(int id) throws IOException {
|
||||
public void writeStructStart(int id) throws IOException {
|
||||
if(id < 0 || id > 255) throw new IllegalArgumentException();
|
||||
write(Tag.STRUCT);
|
||||
write((byte) id);
|
||||
}
|
||||
|
||||
public void writeStructEnd() throws IOException {
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
public void writeNull() throws IOException {
|
||||
write(Tag.NULL);
|
||||
}
|
||||
|
||||
private void write(byte b) throws IOException {
|
||||
out.write(b);
|
||||
for(Consumer c : consumers) c.write(b);
|
||||
|
||||
@@ -165,14 +165,17 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
private byte[] createAck(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(ACK);
|
||||
w.writeStructStart(ACK);
|
||||
w.writeListStart();
|
||||
while(out.size() + serial.getSerialisedUniqueIdLength()
|
||||
+ serial.getSerialisedListEndLength()
|
||||
+ serial.getSerialisedStructEndLength()
|
||||
< MAX_PACKET_LENGTH) {
|
||||
w.writeBytes(TestUtils.getRandomId());
|
||||
}
|
||||
if(tooBig) w.writeBytes(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
w.writeStructEnd();
|
||||
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
|
||||
return out.toByteArray();
|
||||
}
|
||||
@@ -180,23 +183,27 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
private byte[] createEmptyAck() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(ACK);
|
||||
w.writeStructStart(ACK);
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
w.writeStructEnd();
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] createOffer(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(OFFER);
|
||||
w.writeStructStart(OFFER);
|
||||
w.writeListStart();
|
||||
while(out.size() + serial.getSerialisedUniqueIdLength()
|
||||
+ serial.getSerialisedListEndLength()
|
||||
+ serial.getSerialisedStructEndLength()
|
||||
< MAX_PACKET_LENGTH) {
|
||||
w.writeBytes(TestUtils.getRandomId());
|
||||
}
|
||||
if(tooBig) w.writeBytes(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
w.writeStructEnd();
|
||||
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
|
||||
return out.toByteArray();
|
||||
}
|
||||
@@ -204,24 +211,27 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
private byte[] createEmptyOffer() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(OFFER);
|
||||
w.writeStructStart(OFFER);
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
w.writeStructEnd();
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] createRequest(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(REQUEST);
|
||||
// Allow one byte for the STRUCT tag, one byte for the REQUEST tag,
|
||||
w.writeStructStart(REQUEST);
|
||||
// Allow one byte for the STRUCT tag, one byte for the struct ID,
|
||||
// one byte for the padding length as a uint7, one byte for the BYTES
|
||||
// tag, and five bytes for the length of the byte array as an int32
|
||||
int size = MAX_PACKET_LENGTH - 9;
|
||||
// tag, five bytes for the length of the byte array as an int32, and
|
||||
// one byte for the END tag
|
||||
int size = MAX_PACKET_LENGTH - 10;
|
||||
if(tooBig) size++;
|
||||
assertTrue(size > Short.MAX_VALUE);
|
||||
w.writeUint7((byte) 0);
|
||||
w.writeBytes(new byte[size]);
|
||||
w.writeStructEnd();
|
||||
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
|
||||
return out.toByteArray();
|
||||
}
|
||||
@@ -229,9 +239,10 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
private byte[] createRequest(byte[] bitmap) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(REQUEST);
|
||||
w.writeStructStart(REQUEST);
|
||||
w.writeUint7((byte) 0);
|
||||
w.writeBytes(bitmap);
|
||||
w.writeStructEnd();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ public class PacketWriterImplTest extends BriarTestCase {
|
||||
b.set(15);
|
||||
w.writeRequest(new Request(b, 16));
|
||||
// STRUCT tag, struct ID 5, 0 as uint7, BYTES tag, length 2 as uint7,
|
||||
// 0xD959
|
||||
// bitmap 0xD959, END tag
|
||||
byte[] output = out.toByteArray();
|
||||
assertEquals("F1" + "05" + "00" + "F6" + "02" + "D959",
|
||||
assertEquals("F3" + "05" + "00" + "F6" + "02" + "D959" + "F2",
|
||||
StringUtils.toHexString(output));
|
||||
}
|
||||
|
||||
@@ -85,9 +85,9 @@ public class PacketWriterImplTest extends BriarTestCase {
|
||||
b.set(12);
|
||||
w.writeRequest(new Request(b, 13));
|
||||
// STRUCT tag, struct ID 5, 3 as uint7, BYTES tag, length 2 as uint7,
|
||||
// 0xD959
|
||||
// bitmap 0x59D8, END tag
|
||||
byte[] output = out.toByteArray();
|
||||
assertEquals("F1" + "05" + "03" + "F6" + "02" + "59D8",
|
||||
assertEquals("F3" + "05" + "03" + "F6" + "02" + "59D8" + "F2",
|
||||
StringUtils.toHexString(output));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,8 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
@Test
|
||||
public void testReadString() throws Exception {
|
||||
setContents("F703666F6F" + "F700");
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("", r.readString());
|
||||
assertEquals("foo", r.readString(1000));
|
||||
assertEquals("", r.readString(1000));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@@ -140,8 +140,8 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
@Test
|
||||
public void testReadBytes() throws Exception {
|
||||
setContents("F603010203" + "F600");
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes());
|
||||
assertArrayEquals(new byte[] {}, r.readBytes());
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes(1000));
|
||||
assertArrayEquals(new byte[] {}, r.readBytes(1000));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadList() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F3");
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
|
||||
List<Object> l = r.readList(Object.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
@@ -169,7 +169,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadListTypeSafe() throws Exception {
|
||||
setContents("F5" + "01" + "02" + "03" + "F3");
|
||||
setContents("F5" + "01" + "02" + "03" + "F2");
|
||||
List<Byte> l = r.readList(Byte.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
@@ -181,7 +181,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadListTypeSafeThrowsFormatException() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "03" + "F3");
|
||||
setContents("F5" + "01" + "F703666F6F" + "03" + "F2");
|
||||
// Trying to read a mixed list as a list of bytes should throw a
|
||||
// FormatException
|
||||
try {
|
||||
@@ -192,7 +192,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F2" + "F3");
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
@@ -205,7 +205,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadMapTypeSafe() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F700" + "F2" + "F3");
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F700" + "F1" + "F2");
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
@@ -217,8 +217,8 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testMapKeysMustBeUnique() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "01" + "F703626172" + "02" + "F3"
|
||||
+ "F4" + "F703666F6F" + "01" + "F703666F6F" + "02" + "F3");
|
||||
setContents("F4" + "F703666F6F" + "01" + "F703626172" + "02" + "F2"
|
||||
+ "F4" + "F703666F6F" + "01" + "F703666F6F" + "02" + "F2");
|
||||
// The first map has unique keys
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
@@ -234,13 +234,13 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedListElements() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F3");
|
||||
assertTrue(r.hasListStart());
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
|
||||
assertTrue(r.hasList());
|
||||
r.readListStart();
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals((byte) 1, r.readIntAny());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("foo", r.readString(1000));
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals((short) 128, r.readIntAny());
|
||||
assertTrue(r.hasListEnd());
|
||||
@@ -250,7 +250,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedListTypeSafe() throws Exception {
|
||||
setContents("F5" + "01" + "02" + "03" + "F3");
|
||||
setContents("F5" + "01" + "02" + "03" + "F2");
|
||||
List<Byte> l = r.readList(Byte.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
@@ -262,15 +262,15 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMapEntries() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F2" + "F3");
|
||||
assertTrue(r.hasMapStart());
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
|
||||
assertTrue(r.hasMap());
|
||||
r.readMapStart();
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("foo", r.readString(1000));
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertEquals((byte) 123, r.readIntAny());
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertArrayEquals(new byte[] {}, r.readBytes());
|
||||
assertArrayEquals(new byte[] {}, r.readBytes(1000));
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertTrue(r.hasNull());
|
||||
r.readNull();
|
||||
@@ -281,7 +281,7 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMapTypeSafe() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F700" + "F2" + "F3");
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F700" + "F1" + "F2");
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
@@ -294,8 +294,8 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testReadNestedMapsAndLists() throws Exception {
|
||||
setContents("F4" + "F4" + "F703666F6F" + "7B" + "F3"
|
||||
+ "F5" + "01" + "F3" + "F3");
|
||||
setContents("F4" + "F4" + "F703666F6F" + "7B" + "F2"
|
||||
+ "F5" + "01" + "F2" + "F2");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(1, m.size());
|
||||
@@ -313,23 +313,34 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void testMaxLengthAppliesInsideMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F3");
|
||||
public void testMaxStringLengthAppliesInsideMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxStringLength(3);
|
||||
r.setMaxBytesLength(3);
|
||||
Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
|
||||
assertTrue(r.eof());
|
||||
String key = "foo";
|
||||
Bytes value = new Bytes(new byte[] {1, 2, 3});
|
||||
assertEquals(Collections.singletonMap(key, value), m);
|
||||
// The max string length should be applied inside the map
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F3");
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxStringLength(2);
|
||||
try {
|
||||
r.readMap(String.class, Bytes.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxBytesLengthAppliesInsideMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxBytesLength(3);
|
||||
Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
|
||||
assertTrue(r.eof());
|
||||
String key = "foo";
|
||||
Bytes value = new Bytes(new byte[] {1, 2, 3});
|
||||
assertEquals(Collections.singletonMap(key, value), m);
|
||||
// The max bytes length should be applied inside the map
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F3");
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxBytesLength(2);
|
||||
try {
|
||||
r.readMap(String.class, Bytes.class);
|
||||
@@ -337,6 +348,19 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStruct() throws Exception {
|
||||
// Two structs with IDs 0 and 255, each containing 1 as a uint7
|
||||
setContents("F300" + "01" + "F2" + "F3FF" + "01" + "F2");
|
||||
r.readStructStart(0);
|
||||
assertEquals(1, r.readUint7());
|
||||
r.readStructEnd();
|
||||
r.readStructStart(255);
|
||||
assertEquals(1, r.readUint7());
|
||||
r.readStructEnd();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadEmptyInput() throws Exception {
|
||||
setContents("");
|
||||
|
||||
@@ -155,7 +155,7 @@ public class WriterImplTest extends BriarTestCase {
|
||||
for(int i = 0; i < 16; i++) l.add(i);
|
||||
w.writeList(l);
|
||||
// LIST tag, elements as uint7, END tag
|
||||
checkContents("F5" + "000102030405060708090A0B0C0D0E0F" + "F3");
|
||||
checkContents("F5" + "000102030405060708090A0B0C0D0E0F" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -166,7 +166,7 @@ public class WriterImplTest extends BriarTestCase {
|
||||
l.add(2);
|
||||
w.writeList(l);
|
||||
// LIST tag, 1 as uint7, null, 2 as uint7, END tag
|
||||
checkContents("F5" + "01" + "F2" + "02" + "F3");
|
||||
checkContents("F5" + "01" + "F1" + "02" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,7 +178,7 @@ public class WriterImplTest extends BriarTestCase {
|
||||
// MAP tag, entries as uint7, END tag
|
||||
checkContents("F4" + "0001" + "0102" + "0203" + "0304" + "0405"
|
||||
+ "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C"
|
||||
+ "0C0D" + "0D0E" + "0E0F" + "0F10" + "F3");
|
||||
+ "0C0D" + "0D0E" + "0E0F" + "0F10" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -189,7 +189,7 @@ public class WriterImplTest extends BriarTestCase {
|
||||
w.writeIntAny(128L); // Written as int16
|
||||
w.writeListEnd();
|
||||
// LIST tag, 1 as uint7, "foo" as string, 128 as int16, END tag
|
||||
checkContents("F5" + "01" + "F703666F6F" + "FC0080" + "F3");
|
||||
checkContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -202,7 +202,7 @@ public class WriterImplTest extends BriarTestCase {
|
||||
w.writeMapEnd();
|
||||
// MAP tag, "foo" as string, 123 as uint7, byte[0] as bytes,
|
||||
// NULL tag, END tag
|
||||
checkContents("F4" + "F703666F6F" + "7B" + "F600" + "F2" + "F3");
|
||||
checkContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -216,22 +216,22 @@ public class WriterImplTest extends BriarTestCase {
|
||||
w.writeMap(m1);
|
||||
// MAP tag, MAP tag, "foo" as string, 123 as uint7, END tag,
|
||||
// LIST tag, 1 as uint7, END tag, END tag
|
||||
checkContents("F4" + "F4" + "F703666F6F" + "7B" + "F3"
|
||||
+ "F5" + "01" + "F3" + "F3");
|
||||
checkContents("F4" + "F4" + "F703666F6F" + "7B" + "F2"
|
||||
+ "F5" + "01" + "F2" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteStruct() throws IOException {
|
||||
w.writeStructStart(123);
|
||||
w.writeStructEnd();
|
||||
// STRUCT tag, 123 as struct ID, END tag
|
||||
checkContents("F3" + "7B" + "F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteNull() throws IOException {
|
||||
w.writeNull();
|
||||
checkContents("F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteStructId() throws IOException {
|
||||
w.writeStructId(32);
|
||||
w.writeStructId(255);
|
||||
// STRUCT tag, 32 as uint8, STRUCT tag, 255 as uint8
|
||||
checkContents("F1" + "20" + "F1" + "FF");
|
||||
checkContents("F1");
|
||||
}
|
||||
|
||||
private void checkContents(String hex) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user