Delimited structs - this will allow us to skip unrecognised structs.

This commit is contained in:
akwizgran
2013-11-19 18:05:44 +00:00
parent ab5389ce1f
commit 6764ade475
20 changed files with 310 additions and 224 deletions

View File

@@ -40,30 +40,28 @@ public interface Reader {
double readFloat64() throws IOException; double readFloat64() throws IOException;
boolean hasString() throws IOException; boolean hasString() throws IOException;
String readString() throws IOException;
String readString(int maxLength) throws IOException; String readString(int maxLength) throws IOException;
boolean hasBytes() throws IOException; boolean hasBytes() throws IOException;
byte[] readBytes() throws IOException;
byte[] readBytes(int maxLength) throws IOException; byte[] readBytes(int maxLength) throws IOException;
boolean hasList() throws IOException; boolean hasList() throws IOException;
<E> List<E> readList(Class<E> e) throws IOException; <E> List<E> readList(Class<E> e) throws IOException;
boolean hasListStart() throws IOException;
void readListStart() throws IOException; void readListStart() throws IOException;
boolean hasListEnd() throws IOException; boolean hasListEnd() throws IOException;
void readListEnd() throws IOException; void readListEnd() throws IOException;
boolean hasMap() throws IOException; boolean hasMap() throws IOException;
<K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException; <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException;
boolean hasMapStart() throws IOException;
void readMapStart() throws IOException; void readMapStart() throws IOException;
boolean hasMapEnd() throws IOException; boolean hasMapEnd() throws IOException;
void readMapEnd() 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; boolean hasNull() throws IOException;
void readNull() throws IOException; void readNull() throws IOException;
boolean hasStruct(int id) throws IOException;
void readStructId(int id) throws IOException;
} }

View File

@@ -2,11 +2,13 @@ package net.sf.briar.api.serial;
public interface SerialComponent { public interface SerialComponent {
int getSerialisedListEndLength();
int getSerialisedListStartLength(); int getSerialisedListStartLength();
int getSerialisedStructIdLength(int id); int getSerialisedListEndLength();
int getSerialisedStructStartLength(int id);
int getSerialisedStructEndLength();
int getSerialisedUniqueIdLength(); int getSerialisedUniqueIdLength();
} }

View File

@@ -35,7 +35,8 @@ public interface Writer {
void writeMapStart() throws IOException; void writeMapStart() throws IOException;
void writeMapEnd() 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;
} }

View File

@@ -41,9 +41,10 @@ class AuthorFactoryImpl implements AuthorFactory {
private AuthorId getId(String name, byte[] publicKey) throws IOException { private AuthorId getId(String name, byte[] publicKey) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(AUTHOR); w.writeStructStart(AUTHOR);
w.writeString(name); w.writeString(name);
w.writeBytes(publicKey); w.writeBytes(publicKey);
w.writeStructEnd();
MessageDigest messageDigest = crypto.getMessageDigest(); MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.update(out.toByteArray()); messageDigest.update(out.toByteArray());
return new AuthorId(messageDigest.digest()); return new AuthorId(messageDigest.digest());

View File

@@ -23,12 +23,15 @@ class AuthorReader implements StructReader<Author> {
} }
public Author readStruct(Reader r) throws IOException { public Author readStruct(Reader r) throws IOException {
// Set up the reader
DigestingConsumer digesting = new DigestingConsumer(messageDigest); DigestingConsumer digesting = new DigestingConsumer(messageDigest);
// Read and digest the data
r.addConsumer(digesting); r.addConsumer(digesting);
r.readStructId(AUTHOR); // Read and digest the data
r.readStructStart(AUTHOR);
String name = r.readString(MAX_AUTHOR_NAME_LENGTH); String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = r.readBytes(MAX_PUBLIC_KEY_LENGTH); byte[] publicKey = r.readBytes(MAX_PUBLIC_KEY_LENGTH);
r.readStructEnd();
// Reset the reader
r.removeConsumer(digesting); r.removeConsumer(digesting);
// Build and return the author // Build and return the author
AuthorId id = new AuthorId(messageDigest.digest()); AuthorId id = new AuthorId(messageDigest.digest());

View File

@@ -36,9 +36,10 @@ class GroupFactoryImpl implements GroupFactory {
public Group createGroup(String name, byte[] salt) throws IOException { public Group createGroup(String name, byte[] salt) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(GROUP); w.writeStructStart(GROUP);
w.writeString(name); w.writeString(name);
w.writeBytes(salt); w.writeBytes(salt);
w.writeStructEnd();
MessageDigest messageDigest = crypto.getMessageDigest(); MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.update(out.toByteArray()); messageDigest.update(out.toByteArray());
GroupId id = new GroupId(messageDigest.digest()); GroupId id = new GroupId(messageDigest.digest());

View File

@@ -26,11 +26,12 @@ class GroupReader implements StructReader<Group> {
DigestingConsumer digesting = new DigestingConsumer(messageDigest); DigestingConsumer digesting = new DigestingConsumer(messageDigest);
// Read and digest the data // Read and digest the data
r.addConsumer(digesting); r.addConsumer(digesting);
r.readStructId(GROUP); r.readStructStart(GROUP);
String name = r.readString(MAX_GROUP_NAME_LENGTH); String name = r.readString(MAX_GROUP_NAME_LENGTH);
byte[] publicKey = null; byte[] publicKey = null;
if(r.hasNull()) r.readNull(); if(r.hasNull()) r.readNull();
else publicKey = r.readBytes(MAX_PUBLIC_KEY_LENGTH); else publicKey = r.readBytes(MAX_PUBLIC_KEY_LENGTH);
r.readStructEnd();
r.removeConsumer(digesting); r.removeConsumer(digesting);
// Build and return the group // Build and return the group
GroupId id = new GroupId(messageDigest.digest()); GroupId id = new GroupId(messageDigest.digest());

View File

@@ -100,7 +100,7 @@ class MessageFactoryImpl implements MessageFactory {
w.addConsumer(signingConsumer); w.addConsumer(signingConsumer);
} }
// Write the message // Write the message
w.writeStructId(MESSAGE); w.writeStructStart(MESSAGE);
if(parent == null) w.writeNull(); if(parent == null) w.writeNull();
else w.writeBytes(parent.getBytes()); else w.writeBytes(parent.getBytes());
if(group == null) w.writeNull(); if(group == null) w.writeNull();
@@ -109,7 +109,7 @@ class MessageFactoryImpl implements MessageFactory {
else writeAuthor(w, author); else writeAuthor(w, author);
w.writeString(contentType); w.writeString(contentType);
long timestamp = clock.currentTimeMillis(); long timestamp = clock.currentTimeMillis();
w.writeInt64(timestamp); w.writeIntAny(timestamp);
byte[] salt = new byte[MESSAGE_SALT_LENGTH]; byte[] salt = new byte[MESSAGE_SALT_LENGTH];
random.nextBytes(salt); random.nextBytes(salt);
w.writeBytes(salt); w.writeBytes(salt);
@@ -125,6 +125,7 @@ class MessageFactoryImpl implements MessageFactory {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
w.writeBytes(sig); w.writeBytes(sig);
} }
w.writeStructEnd();
// Hash the message, including the signature, to get the message ID // Hash the message, including the signature, to get the message ID
w.removeConsumer(digestingConsumer); w.removeConsumer(digestingConsumer);
MessageId id = new MessageId(messageDigest.digest()); MessageId id = new MessageId(messageDigest.digest());
@@ -143,14 +144,16 @@ class MessageFactoryImpl implements MessageFactory {
} }
private void writeGroup(Writer w, Group g) throws IOException { private void writeGroup(Writer w, Group g) throws IOException {
w.writeStructId(GROUP); w.writeStructStart(GROUP);
w.writeString(g.getName()); w.writeString(g.getName());
w.writeBytes(g.getSalt()); w.writeBytes(g.getSalt());
w.writeStructEnd();
} }
private void writeAuthor(Writer w, Author a) throws IOException { private void writeAuthor(Writer w, Author a) throws IOException {
w.writeStructId(AUTHOR); w.writeStructStart(AUTHOR);
w.writeString(a.getName()); w.writeString(a.getName());
w.writeBytes(a.getPublicKey()); w.writeBytes(a.getPublicKey());
w.writeStructEnd();
} }
} }

View File

@@ -42,8 +42,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
CountingConsumer counting = new CountingConsumer(MAX_PACKET_LENGTH); CountingConsumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
r.addConsumer(copying); r.addConsumer(copying);
r.addConsumer(counting); r.addConsumer(counting);
// Read the initial tag // Read the start of the struct
r.readStructId(MESSAGE); r.readStructStart(MESSAGE);
// Read the parent's message ID, if there is one // Read the parent's message ID, if there is one
MessageId parent = null; MessageId parent = null;
if(r.hasNull()) { if(r.hasNull()) {
@@ -64,7 +64,7 @@ class MessageReader implements StructReader<UnverifiedMessage> {
// Read the content type // Read the content type
String contentType = r.readString(MAX_CONTENT_TYPE_LENGTH); String contentType = r.readString(MAX_CONTENT_TYPE_LENGTH);
// Read the timestamp // Read the timestamp
long timestamp = r.readInt64(); long timestamp = r.readIntAny();
if(timestamp < 0) throw new FormatException(); if(timestamp < 0) throw new FormatException();
// Read the salt // Read the salt
byte[] salt = r.readBytes(MESSAGE_SALT_LENGTH); byte[] salt = r.readBytes(MESSAGE_SALT_LENGTH);
@@ -89,6 +89,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
byte[] signature = null; byte[] signature = null;
if(author == null) r.readNull(); if(author == null) r.readNull();
else signature = r.readBytes(MAX_SIGNATURE_LENGTH); else signature = r.readBytes(MAX_SIGNATURE_LENGTH);
// Read the end of the struct
r.readStructEnd();
// The signature will be verified later // The signature will be verified later
r.removeConsumer(counting); r.removeConsumer(counting);
r.removeConsumer(copying); r.removeConsumer(copying);

View File

@@ -70,12 +70,17 @@ class PacketReaderImpl implements PacketReader {
} }
public Ack readAck() throws IOException { public Ack readAck() throws IOException {
// Set up the reader
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
r.addConsumer(counting); r.addConsumer(counting);
r.readStructId(ACK); // Read the start of the struct
r.readStructStart(ACK);
// Read the message IDs as byte arrays // Read the message IDs as byte arrays
r.setMaxBytesLength(UniqueId.LENGTH); r.setMaxBytesLength(UniqueId.LENGTH);
List<Bytes> raw = r.readList(Bytes.class); List<Bytes> raw = r.readList(Bytes.class);
// Read the end of the struct
r.readStructEnd();
// Reset the reader
r.resetMaxBytesLength(); r.resetMaxBytesLength();
r.removeConsumer(counting); r.removeConsumer(counting);
if(raw.isEmpty()) throw new FormatException(); if(raw.isEmpty()) throw new FormatException();
@@ -103,12 +108,17 @@ class PacketReaderImpl implements PacketReader {
} }
public Offer readOffer() throws IOException { public Offer readOffer() throws IOException {
// Set up the reader
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
r.addConsumer(counting); r.addConsumer(counting);
r.readStructId(OFFER); // Read the start of the struct
r.readStructStart(OFFER);
// Read the message IDs as byte arrays // Read the message IDs as byte arrays
r.setMaxBytesLength(UniqueId.LENGTH); r.setMaxBytesLength(UniqueId.LENGTH);
List<Bytes> raw = r.readList(Bytes.class); List<Bytes> raw = r.readList(Bytes.class);
// Read the end of the struct
r.readStructEnd();
// Reset the reader
r.resetMaxBytesLength(); r.resetMaxBytesLength();
r.removeConsumer(counting); r.removeConsumer(counting);
if(raw.isEmpty()) throw new FormatException(); if(raw.isEmpty()) throw new FormatException();
@@ -128,14 +138,19 @@ class PacketReaderImpl implements PacketReader {
} }
public Request readRequest() throws IOException { public Request readRequest() throws IOException {
// Set up the reader
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
r.addConsumer(counting); 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 // There may be up to 7 bits of padding at the end of the bitmap
int padding = r.readUint7(); int padding = r.readUint7();
if(padding > 7) throw new FormatException(); if(padding > 7) throw new FormatException();
// Read the bitmap // Read the bitmap
byte[] bitmap = r.readBytes(MAX_PACKET_LENGTH); byte[] bitmap = r.readBytes(MAX_PACKET_LENGTH);
// Read the end of the struct
r.readStructEnd();
// Reset the reader
r.removeConsumer(counting); r.removeConsumer(counting);
// Convert the bitmap into a BitSet // Convert the bitmap into a BitSet
int length = bitmap.length * 8 - padding; int length = bitmap.length * 8 - padding;
@@ -154,9 +169,10 @@ class PacketReaderImpl implements PacketReader {
} }
public RetentionAck readRetentionAck() throws IOException { public RetentionAck readRetentionAck() throws IOException {
r.readStructId(RETENTION_ACK); r.readStructStart(RETENTION_ACK);
long version = r.readInt64(); long version = r.readIntAny();
if(version < 0) throw new FormatException(); if(version < 0) throw new FormatException();
r.readStructEnd();
return new RetentionAck(version); return new RetentionAck(version);
} }
@@ -165,11 +181,12 @@ class PacketReaderImpl implements PacketReader {
} }
public RetentionUpdate readRetentionUpdate() throws IOException { public RetentionUpdate readRetentionUpdate() throws IOException {
r.readStructId(RETENTION_UPDATE); r.readStructStart(RETENTION_UPDATE);
long retention = r.readInt64(); long retention = r.readIntAny();
if(retention < 0) throw new FormatException(); if(retention < 0) throw new FormatException();
long version = r.readInt64(); long version = r.readIntAny();
if(version < 0) throw new FormatException(); if(version < 0) throw new FormatException();
r.readStructEnd();
return new RetentionUpdate(retention, version); return new RetentionUpdate(retention, version);
} }
@@ -178,9 +195,10 @@ class PacketReaderImpl implements PacketReader {
} }
public SubscriptionAck readSubscriptionAck() throws IOException { public SubscriptionAck readSubscriptionAck() throws IOException {
r.readStructId(SUBSCRIPTION_ACK); r.readStructStart(SUBSCRIPTION_ACK);
long version = r.readInt64(); long version = r.readIntAny();
if(version < 0) throw new FormatException(); if(version < 0) throw new FormatException();
r.readStructEnd();
return new SubscriptionAck(version); return new SubscriptionAck(version);
} }
@@ -197,11 +215,12 @@ class PacketReaderImpl implements PacketReader {
} }
public TransportAck readTransportAck() throws IOException { public TransportAck readTransportAck() throws IOException {
r.readStructId(TRANSPORT_ACK); r.readStructStart(TRANSPORT_ACK);
byte[] b = r.readBytes(UniqueId.LENGTH); byte[] b = r.readBytes(UniqueId.LENGTH);
if(b.length < UniqueId.LENGTH) throw new FormatException(); if(b.length < UniqueId.LENGTH) throw new FormatException();
long version = r.readInt64(); long version = r.readIntAny();
if(version < 0) throw new FormatException(); if(version < 0) throw new FormatException();
r.readStructEnd();
return new TransportAck(new TransportId(b), version); return new TransportAck(new TransportId(b), version);
} }
@@ -210,9 +229,11 @@ class PacketReaderImpl implements PacketReader {
} }
public TransportUpdate readTransportUpdate() throws IOException { public TransportUpdate readTransportUpdate() throws IOException {
// Set up the reader
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
r.addConsumer(counting); r.addConsumer(counting);
r.readStructId(TRANSPORT_UPDATE); // Read the start of the struct
r.readStructStart(TRANSPORT_UPDATE);
// Read the transport ID // Read the transport ID
byte[] b = r.readBytes(UniqueId.LENGTH); byte[] b = r.readBytes(UniqueId.LENGTH);
if(b.length < UniqueId.LENGTH) throw new FormatException(); if(b.length < UniqueId.LENGTH) throw new FormatException();
@@ -223,8 +244,11 @@ class PacketReaderImpl implements PacketReader {
r.resetMaxStringLength(); r.resetMaxStringLength();
if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) throw new FormatException(); if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) throw new FormatException();
// Read the version number // Read the version number
long version = r.readInt64(); long version = r.readIntAny();
if(version < 0) throw new FormatException(); if(version < 0) throw new FormatException();
// Read the end of the struct
r.readStructEnd();
// Reset the reader
r.removeConsumer(counting); r.removeConsumer(counting);
// Build and return the transport update // Build and return the transport update
return new TransportUpdate(id, new TransportProperties(m), version); return new TransportUpdate(id, new TransportProperties(m), version);

View File

@@ -50,27 +50,30 @@ class PacketWriterImpl implements PacketWriter {
public int getMaxMessagesForAck(long capacity) { public int getMaxMessagesForAck(long capacity) {
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH); int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
int overhead = serial.getSerialisedStructIdLength(ACK) int overhead = serial.getSerialisedStructStartLength(ACK)
+ serial.getSerialisedListStartLength() + serial.getSerialisedListStartLength()
+ serial.getSerialisedListEndLength(); + serial.getSerialisedListEndLength()
+ serial.getSerialisedStructEndLength();
int idLength = serial.getSerialisedUniqueIdLength(); int idLength = serial.getSerialisedUniqueIdLength();
return (packet - overhead) / idLength; return (packet - overhead) / idLength;
} }
public int getMaxMessagesForOffer(long capacity) { public int getMaxMessagesForOffer(long capacity) {
int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH); int packet = (int) Math.min(capacity, MAX_PACKET_LENGTH);
int overhead = serial.getSerialisedStructIdLength(OFFER) int overhead = serial.getSerialisedStructStartLength(OFFER)
+ serial.getSerialisedListStartLength() + serial.getSerialisedListStartLength()
+ serial.getSerialisedListEndLength(); + serial.getSerialisedListEndLength()
+ serial.getSerialisedStructEndLength();
int idLength = serial.getSerialisedUniqueIdLength(); int idLength = serial.getSerialisedUniqueIdLength();
return (packet - overhead) / idLength; return (packet - overhead) / idLength;
} }
public void writeAck(Ack a) throws IOException { public void writeAck(Ack a) throws IOException {
w.writeStructId(ACK); w.writeStructStart(ACK);
w.writeListStart(); w.writeListStart();
for(MessageId m : a.getMessageIds()) w.writeBytes(m.getBytes()); for(MessageId m : a.getMessageIds()) w.writeBytes(m.getBytes());
w.writeListEnd(); w.writeListEnd();
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
@@ -80,10 +83,11 @@ class PacketWriterImpl implements PacketWriter {
} }
public void writeOffer(Offer o) throws IOException { public void writeOffer(Offer o) throws IOException {
w.writeStructId(OFFER); w.writeStructStart(OFFER);
w.writeListStart(); w.writeListStart();
for(MessageId m : o.getMessageIds()) w.writeBytes(m.getBytes()); for(MessageId m : o.getMessageIds()) w.writeBytes(m.getBytes());
w.writeListEnd(); w.writeListEnd();
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
@@ -101,57 +105,65 @@ class PacketWriterImpl implements PacketWriter {
bitmap[offset] |= bit; bitmap[offset] |= bit;
} }
} }
w.writeStructId(REQUEST); w.writeStructStart(REQUEST);
w.writeUint7((byte) (bytes * 8 - length)); w.writeUint7((byte) (bytes * 8 - length));
w.writeBytes(bitmap); w.writeBytes(bitmap);
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeRetentionAck(RetentionAck a) throws IOException { public void writeRetentionAck(RetentionAck a) throws IOException {
w.writeStructId(RETENTION_ACK); w.writeStructStart(RETENTION_ACK);
w.writeInt64(a.getVersion()); w.writeIntAny(a.getVersion());
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeRetentionUpdate(RetentionUpdate u) throws IOException { public void writeRetentionUpdate(RetentionUpdate u) throws IOException {
w.writeStructId(RETENTION_UPDATE); w.writeStructStart(RETENTION_UPDATE);
w.writeInt64(u.getRetentionTime()); w.writeIntAny(u.getRetentionTime());
w.writeInt64(u.getVersion()); w.writeIntAny(u.getVersion());
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeSubscriptionAck(SubscriptionAck a) throws IOException { public void writeSubscriptionAck(SubscriptionAck a) throws IOException {
w.writeStructId(SUBSCRIPTION_ACK); w.writeStructStart(SUBSCRIPTION_ACK);
w.writeInt64(a.getVersion()); w.writeIntAny(a.getVersion());
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeSubscriptionUpdate(SubscriptionUpdate u) public void writeSubscriptionUpdate(SubscriptionUpdate u)
throws IOException { throws IOException {
w.writeStructId(SUBSCRIPTION_UPDATE); w.writeStructStart(SUBSCRIPTION_UPDATE);
w.writeListStart(); w.writeListStart();
for(Group g : u.getGroups()) { for(Group g : u.getGroups()) {
w.writeStructId(GROUP); w.writeStructStart(GROUP);
w.writeString(g.getName()); w.writeString(g.getName());
w.writeBytes(g.getSalt()); w.writeBytes(g.getSalt());
w.writeStructEnd();
} }
w.writeListEnd(); w.writeListEnd();
w.writeInt64(u.getVersion()); w.writeIntAny(u.getVersion());
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeTransportAck(TransportAck a) throws IOException { public void writeTransportAck(TransportAck a) throws IOException {
w.writeStructId(TRANSPORT_ACK); w.writeStructStart(TRANSPORT_ACK);
w.writeBytes(a.getId().getBytes()); w.writeBytes(a.getId().getBytes());
w.writeInt64(a.getVersion()); w.writeIntAny(a.getVersion());
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }
public void writeTransportUpdate(TransportUpdate u) throws IOException { public void writeTransportUpdate(TransportUpdate u) throws IOException {
w.writeStructId(TRANSPORT_UPDATE); w.writeStructStart(TRANSPORT_UPDATE);
w.writeBytes(u.getId().getBytes()); w.writeBytes(u.getId().getBytes());
w.writeMap(u.getProperties()); w.writeMap(u.getProperties());
w.writeInt64(u.getVersion()); w.writeIntAny(u.getVersion());
w.writeStructEnd();
if(flush) out.flush(); if(flush) out.flush();
} }

View File

@@ -26,9 +26,11 @@ class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> {
} }
public SubscriptionUpdate readStruct(Reader r) throws IOException { public SubscriptionUpdate readStruct(Reader r) throws IOException {
// Set up the reader
Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH);
r.addConsumer(counting); r.addConsumer(counting);
r.readStructId(SUBSCRIPTION_UPDATE); // Read the start of the struct
r.readStructStart(SUBSCRIPTION_UPDATE);
// Read the subscriptions // Read the subscriptions
List<Group> subs = new ArrayList<Group>(); List<Group> subs = new ArrayList<Group>();
r.readListStart(); r.readListStart();
@@ -36,8 +38,11 @@ class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> {
subs.add(groupReader.readStruct(r)); subs.add(groupReader.readStruct(r));
r.readListEnd(); r.readListEnd();
// Read the version number // Read the version number
long version = r.readInt64(); long version = r.readIntAny();
if(version < 0) throw new FormatException(); if(version < 0) throw new FormatException();
// Read the end of the struct
r.readStructEnd();
// Reset the reader
r.removeConsumer(counting); r.removeConsumer(counting);
// Build and return the subscription update // Build and return the subscription update
subs = Collections.unmodifiableList(subs); subs = Collections.unmodifiableList(subs);

View File

@@ -23,8 +23,8 @@ class ReaderImpl implements Reader {
private final Collection<Consumer> consumers = new ArrayList<Consumer>(0); private final Collection<Consumer> consumers = new ArrayList<Consumer>(0);
private boolean hasLookahead = false, eof = false; private boolean hasLookahead = false, eof = false;
private byte next, nextNext; private byte next, nextStructId;
private byte[] buf = null; private byte[] buf = new byte[8];
private int maxStringLength = Integer.MAX_VALUE; private int maxStringLength = Integer.MAX_VALUE;
private int maxBytesLength = Integer.MAX_VALUE; private int maxBytesLength = Integer.MAX_VALUE;
@@ -33,42 +33,40 @@ class ReaderImpl implements Reader {
} }
public boolean eof() throws IOException { public boolean eof() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
return eof; return eof;
} }
private byte readLookahead(boolean eofAcceptable) throws IOException { private void readLookahead() throws IOException {
assert !eof; assert !eof;
// If one or two lookahead bytes have been read, feed the consumers // If one or two lookahead bytes have been read, feed the consumers
if(hasLookahead) consumeLookahead(); if(hasLookahead) consumeLookahead();
// Read a lookahead byte // Read a lookahead byte
int i = in.read(); int i = in.read();
if(i == -1) { if(i == -1) {
if(!eofAcceptable) throw new FormatException();
eof = true; eof = true;
return;
} }
next = (byte) i; next = (byte) i;
// If necessary, read another lookahead byte // If necessary, read another lookahead byte
if(next == Tag.STRUCT) { if(next == Tag.STRUCT) {
i = in.read(); i = in.read();
if(i == -1) throw new FormatException(); if(i == -1) throw new FormatException();
nextNext = (byte) i; nextStructId = (byte) i;
} }
hasLookahead = true; hasLookahead = true;
return next;
} }
private void consumeLookahead() throws IOException { private void consumeLookahead() throws IOException {
assert hasLookahead; assert hasLookahead;
for(Consumer c : consumers) { for(Consumer c : consumers) {
c.write(next); c.write(next);
if(next == Tag.STRUCT) c.write(nextNext); if(next == Tag.STRUCT) c.write(nextStructId);
} }
hasLookahead = false; hasLookahead = false;
} }
public void close() throws IOException { public void close() throws IOException {
buf = null;
in.close(); in.close();
} }
@@ -97,7 +95,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasBoolean() throws IOException { public boolean hasBoolean() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.FALSE || next == Tag.TRUE; return next == Tag.FALSE || next == Tag.TRUE;
} }
@@ -109,7 +107,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasUint7() throws IOException { public boolean hasUint7() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next >= 0; return next >= 0;
} }
@@ -121,34 +119,37 @@ class ReaderImpl implements Reader {
} }
public boolean hasInt8() throws IOException { public boolean hasInt8() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.INT8; return next == Tag.INT8;
} }
public byte readInt8() throws IOException { public byte readInt8() throws IOException {
if(!hasInt8()) throw new FormatException(); if(!hasInt8()) throw new FormatException();
readLookahead(false);
consumeLookahead(); consumeLookahead();
return next; int i = in.read();
if(i == -1) {
eof = true;
throw new FormatException();
}
return (byte) i;
} }
public boolean hasInt16() throws IOException { public boolean hasInt16() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.INT16; return next == Tag.INT16;
} }
public short readInt16() throws IOException { public short readInt16() throws IOException {
if(!hasInt16()) throw new FormatException(); if(!hasInt16()) throw new FormatException();
byte b1 = readLookahead(false);
byte b2 = readLookahead(false);
consumeLookahead(); consumeLookahead();
return (short) (((b1 & 0xFF) << 8) | (b2 & 0xFF)); readIntoBuffer(2);
return (short) (((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF));
} }
public boolean hasInt32() throws IOException { public boolean hasInt32() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.INT32; return next == Tag.INT32;
} }
@@ -166,7 +167,7 @@ class ReaderImpl implements Reader {
} }
private void readIntoBuffer(int length) throws IOException { 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); readIntoBuffer(buf, length);
} }
@@ -177,17 +178,16 @@ class ReaderImpl implements Reader {
int read = in.read(b, offset, length - offset); int read = in.read(b, offset, length - offset);
if(read == -1) { if(read == -1) {
eof = true; eof = true;
break; throw new FormatException();
} }
offset += read; offset += read;
} }
if(offset < length) throw new FormatException();
// Feed the hungry mouths // Feed the hungry mouths
for(Consumer c : consumers) c.write(b, 0, length); for(Consumer c : consumers) c.write(b, 0, length);
} }
public boolean hasInt64() throws IOException { public boolean hasInt64() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.INT64; return next == Tag.INT64;
} }
@@ -207,7 +207,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasIntAny() throws IOException { public boolean hasIntAny() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next >= 0 || next == Tag.INT8 || next == Tag.INT16 return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|| next == Tag.INT32 || next == Tag.INT64; || next == Tag.INT32 || next == Tag.INT64;
@@ -224,7 +224,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasFloat32() throws IOException { public boolean hasFloat32() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.FLOAT32; return next == Tag.FLOAT32;
} }
@@ -236,7 +236,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasFloat64() throws IOException { public boolean hasFloat64() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.FLOAT64; return next == Tag.FLOAT64;
} }
@@ -248,15 +248,11 @@ class ReaderImpl implements Reader {
} }
public boolean hasString() throws IOException { public boolean hasString() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.STRING; return next == Tag.STRING;
} }
public String readString() throws IOException {
return readString(maxStringLength);
}
public String readString(int maxLength) throws IOException { public String readString(int maxLength) throws IOException {
if(!hasString()) throw new FormatException(); if(!hasString()) throw new FormatException();
consumeLookahead(); consumeLookahead();
@@ -269,30 +265,29 @@ class ReaderImpl implements Reader {
private int readLength() throws IOException { private int readLength() throws IOException {
if(!hasLength()) throw new FormatException(); if(!hasLength()) throw new FormatException();
if(next >= 0) return readUint7(); int length;
if(next == Tag.INT8) return readInt8(); if(next >= 0) length = readUint7();
if(next == Tag.INT16) return readInt16(); else if(next == Tag.INT8) length = readInt8();
if(next == Tag.INT32) return readInt32(); else if(next == Tag.INT16) length = readInt16();
throw new IllegalStateException(); else if(next == Tag.INT32) length = readInt32();
else throw new IllegalStateException();
if(length < 0) throw new FormatException();
return length;
} }
private boolean hasLength() throws IOException { private boolean hasLength() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next >= 0 || next == Tag.INT8 || next == Tag.INT16 return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|| next == Tag.INT32; || next == Tag.INT32;
} }
public boolean hasBytes() throws IOException { public boolean hasBytes() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.BYTES; return next == Tag.BYTES;
} }
public byte[] readBytes() throws IOException {
return readBytes(maxBytesLength);
}
public byte[] readBytes(int maxLength) throws IOException { public byte[] readBytes(int maxLength) throws IOException {
if(!hasBytes()) throw new FormatException(); if(!hasBytes()) throw new FormatException();
consumeLookahead(); consumeLookahead();
@@ -305,7 +300,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasList() throws IOException { public boolean hasList() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.LIST; return next == Tag.LIST;
} }
@@ -320,13 +315,12 @@ class ReaderImpl implements Reader {
} }
private boolean hasEnd() throws IOException { private boolean hasEnd() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.END; return next == Tag.END;
} }
private void readEnd() throws IOException { private void readEnd() throws IOException {
if(!hasLookahead) throw new IllegalStateException();
if(!hasEnd()) throw new FormatException(); if(!hasEnd()) throw new FormatException();
consumeLookahead(); consumeLookahead();
} }
@@ -340,8 +334,8 @@ class ReaderImpl implements Reader {
if(hasInt64()) return Long.valueOf(readInt64()); if(hasInt64()) return Long.valueOf(readInt64());
if(hasFloat32()) return Float.valueOf(readFloat32()); if(hasFloat32()) return Float.valueOf(readFloat32());
if(hasFloat64()) return Double.valueOf(readFloat64()); if(hasFloat64()) return Double.valueOf(readFloat64());
if(hasString()) return readString(); if(hasString()) return readString(maxStringLength);
if(hasBytes()) return new Bytes(readBytes()); if(hasBytes()) return new Bytes(readBytes(maxBytesLength));
if(hasList()) return readList(Object.class); if(hasList()) return readList(Object.class);
if(hasMap()) return readMap(Object.class, Object.class); if(hasMap()) return readMap(Object.class, Object.class);
if(hasNull()) { 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 { public void readListStart() throws IOException {
if(!hasListStart()) throw new FormatException(); if(!hasList()) throw new FormatException();
consumeLookahead(); consumeLookahead();
} }
@@ -398,7 +386,7 @@ class ReaderImpl implements Reader {
} }
public boolean hasMap() throws IOException { public boolean hasMap() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.MAP; return next == Tag.MAP;
} }
@@ -415,14 +403,8 @@ class ReaderImpl implements Reader {
return Collections.unmodifiableMap(m); 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 { public void readMapStart() throws IOException {
if(!hasMapStart()) throw new FormatException(); if(!hasMap()) throw new FormatException();
consumeLookahead(); consumeLookahead();
} }
@@ -434,8 +416,28 @@ class ReaderImpl implements Reader {
readEnd(); 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 { public boolean hasNull() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead();
if(eof) return false; if(eof) return false;
return next == Tag.NULL; return next == Tag.NULL;
} }
@@ -444,16 +446,4 @@ class ReaderImpl implements Reader {
if(!hasNull()) throw new FormatException(); if(!hasNull()) throw new FormatException();
consumeLookahead(); 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();
}
} }

View File

@@ -5,31 +5,35 @@ import net.sf.briar.api.serial.SerialComponent;
class SerialComponentImpl implements SerialComponent { class SerialComponentImpl implements SerialComponent {
public int getSerialisedListEndLength() {
// END tag
return 1;
}
public int getSerialisedListStartLength() { public int getSerialisedListStartLength() {
// LIST tag // LIST tag
return 1; return 1;
} }
public int getSerialisedStructIdLength(int id) { public int getSerialisedListEndLength() {
if(id < 0 || id > 255) throw new IllegalArgumentException(); // END tag
return id < 32 ? 1 : 2; return 1;
}
public int getSerialisedStructStartLength(int id) {
// STRUCT tag, ID
return 2;
}
public int getSerialisedStructEndLength() {
// END tag
return 1;
} }
public int getSerialisedUniqueIdLength() { public int getSerialisedUniqueIdLength() {
// BYTES tag, length spec, bytes // BYTES tag, length spec, bytes
return 1 + getSerialisedLengthSpecLength(UniqueId.LENGTH) return 1 + getSerialisedLengthSpecLength(UniqueId.LENGTH)
+ UniqueId.LENGTH; + UniqueId.LENGTH;
} }
private int getSerialisedLengthSpecLength(int length) { private int getSerialisedLengthSpecLength(int length) {
if(length < 0) throw new IllegalArgumentException(); if(length < 0) throw new IllegalArgumentException();
if(length < 128) return 1; // Uint7 // Uint7, int16 or int32
if(length < Short.MAX_VALUE) return 3; // Int16 return length <= Byte.MAX_VALUE ? 1 : length <= Short.MAX_VALUE ? 3 : 5;
return 5; // Int32
} }
} }

View File

@@ -2,19 +2,19 @@ package net.sf.briar.serial;
interface Tag { interface Tag {
byte FALSE = (byte) 0xFF; // 1111 1111 byte FALSE = (byte) 0xFF;
byte TRUE = (byte) 0xFE; // 1111 1110 byte TRUE = (byte) 0xFE;
byte INT8 = (byte) 0xFD; // 1111 1101 byte INT8 = (byte) 0xFD;
byte INT16 = (byte) 0xFC; // 1111 1100 byte INT16 = (byte) 0xFC;
byte INT32 = (byte) 0xFB; // 1111 1011 byte INT32 = (byte) 0xFB;
byte INT64 = (byte) 0xFA; // 1111 1010 byte INT64 = (byte) 0xFA;
byte FLOAT32 = (byte) 0xF9; // 1111 1001 byte FLOAT32 = (byte) 0xF9;
byte FLOAT64 = (byte) 0xF8; // 1111 1000 byte FLOAT64 = (byte) 0xF8;
byte STRING = (byte) 0xF7; // 1111 0111 byte STRING = (byte) 0xF7;
byte BYTES = (byte) 0xF6; // 1111 0110 byte BYTES = (byte) 0xF6;
byte LIST = (byte) 0xF5; // 1111 0111 byte LIST = (byte) 0xF5;
byte MAP = (byte) 0xF4; // 1111 0100 byte MAP = (byte) 0xF4;
byte END = (byte) 0xF3; // 1111 0011 byte STRUCT = (byte) 0xF3;
byte NULL = (byte) 0xF2; // 1111 0010 byte END = (byte) 0xF2;
byte STRUCT = (byte) 0xF1; // 1111 0001 byte NULL = (byte) 0xF1;
} }

View File

@@ -177,16 +177,20 @@ class WriterImpl implements Writer {
write(Tag.END); write(Tag.END);
} }
public void writeNull() throws IOException { public void writeStructStart(int id) throws IOException {
write(Tag.NULL);
}
public void writeStructId(int id) throws IOException {
if(id < 0 || id > 255) throw new IllegalArgumentException(); if(id < 0 || id > 255) throw new IllegalArgumentException();
write(Tag.STRUCT); write(Tag.STRUCT);
write((byte) id); 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 { private void write(byte b) throws IOException {
out.write(b); out.write(b);
for(Consumer c : consumers) c.write(b); for(Consumer c : consumers) c.write(b);

View File

@@ -165,14 +165,17 @@ public class PacketReaderImplTest extends BriarTestCase {
private byte[] createAck(boolean tooBig) throws Exception { private byte[] createAck(boolean tooBig) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(ACK); w.writeStructStart(ACK);
w.writeListStart(); w.writeListStart();
while(out.size() + serial.getSerialisedUniqueIdLength() while(out.size() + serial.getSerialisedUniqueIdLength()
+ serial.getSerialisedListEndLength()
+ serial.getSerialisedStructEndLength()
< MAX_PACKET_LENGTH) { < MAX_PACKET_LENGTH) {
w.writeBytes(TestUtils.getRandomId()); w.writeBytes(TestUtils.getRandomId());
} }
if(tooBig) w.writeBytes(TestUtils.getRandomId()); if(tooBig) w.writeBytes(TestUtils.getRandomId());
w.writeListEnd(); w.writeListEnd();
w.writeStructEnd();
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH); assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
return out.toByteArray(); return out.toByteArray();
} }
@@ -180,23 +183,27 @@ public class PacketReaderImplTest extends BriarTestCase {
private byte[] createEmptyAck() throws Exception { private byte[] createEmptyAck() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(ACK); w.writeStructStart(ACK);
w.writeListStart(); w.writeListStart();
w.writeListEnd(); w.writeListEnd();
w.writeStructEnd();
return out.toByteArray(); return out.toByteArray();
} }
private byte[] createOffer(boolean tooBig) throws Exception { private byte[] createOffer(boolean tooBig) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(OFFER); w.writeStructStart(OFFER);
w.writeListStart(); w.writeListStart();
while(out.size() + serial.getSerialisedUniqueIdLength() while(out.size() + serial.getSerialisedUniqueIdLength()
+ serial.getSerialisedListEndLength()
+ serial.getSerialisedStructEndLength()
< MAX_PACKET_LENGTH) { < MAX_PACKET_LENGTH) {
w.writeBytes(TestUtils.getRandomId()); w.writeBytes(TestUtils.getRandomId());
} }
if(tooBig) w.writeBytes(TestUtils.getRandomId()); if(tooBig) w.writeBytes(TestUtils.getRandomId());
w.writeListEnd(); w.writeListEnd();
w.writeStructEnd();
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH); assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
return out.toByteArray(); return out.toByteArray();
} }
@@ -204,24 +211,27 @@ public class PacketReaderImplTest extends BriarTestCase {
private byte[] createEmptyOffer() throws Exception { private byte[] createEmptyOffer() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(OFFER); w.writeStructStart(OFFER);
w.writeListStart(); w.writeListStart();
w.writeListEnd(); w.writeListEnd();
w.writeStructEnd();
return out.toByteArray(); return out.toByteArray();
} }
private byte[] createRequest(boolean tooBig) throws Exception { private byte[] createRequest(boolean tooBig) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(REQUEST); w.writeStructStart(REQUEST);
// Allow one byte for the STRUCT tag, one byte for the REQUEST tag, // 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 // 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 // tag, five bytes for the length of the byte array as an int32, and
int size = MAX_PACKET_LENGTH - 9; // one byte for the END tag
int size = MAX_PACKET_LENGTH - 10;
if(tooBig) size++; if(tooBig) size++;
assertTrue(size > Short.MAX_VALUE); assertTrue(size > Short.MAX_VALUE);
w.writeUint7((byte) 0); w.writeUint7((byte) 0);
w.writeBytes(new byte[size]); w.writeBytes(new byte[size]);
w.writeStructEnd();
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH); assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
return out.toByteArray(); return out.toByteArray();
} }
@@ -229,9 +239,10 @@ public class PacketReaderImplTest extends BriarTestCase {
private byte[] createRequest(byte[] bitmap) throws Exception { private byte[] createRequest(byte[] bitmap) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeStructId(REQUEST); w.writeStructStart(REQUEST);
w.writeUint7((byte) 0); w.writeUint7((byte) 0);
w.writeBytes(bitmap); w.writeBytes(bitmap);
w.writeStructEnd();
return out.toByteArray(); return out.toByteArray();
} }
} }

View File

@@ -61,9 +61,9 @@ public class PacketWriterImplTest extends BriarTestCase {
b.set(15); b.set(15);
w.writeRequest(new Request(b, 16)); w.writeRequest(new Request(b, 16));
// STRUCT tag, struct ID 5, 0 as uint7, BYTES tag, length 2 as uint7, // STRUCT tag, struct ID 5, 0 as uint7, BYTES tag, length 2 as uint7,
// 0xD959 // bitmap 0xD959, END tag
byte[] output = out.toByteArray(); byte[] output = out.toByteArray();
assertEquals("F1" + "05" + "00" + "F6" + "02" + "D959", assertEquals("F3" + "05" + "00" + "F6" + "02" + "D959" + "F2",
StringUtils.toHexString(output)); StringUtils.toHexString(output));
} }
@@ -85,9 +85,9 @@ public class PacketWriterImplTest extends BriarTestCase {
b.set(12); b.set(12);
w.writeRequest(new Request(b, 13)); w.writeRequest(new Request(b, 13));
// STRUCT tag, struct ID 5, 3 as uint7, BYTES tag, length 2 as uint7, // STRUCT tag, struct ID 5, 3 as uint7, BYTES tag, length 2 as uint7,
// 0xD959 // bitmap 0x59D8, END tag
byte[] output = out.toByteArray(); byte[] output = out.toByteArray();
assertEquals("F1" + "05" + "03" + "F6" + "02" + "59D8", assertEquals("F3" + "05" + "03" + "F6" + "02" + "59D8" + "F2",
StringUtils.toHexString(output)); StringUtils.toHexString(output));
} }
} }

View File

@@ -122,8 +122,8 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadString() throws Exception { public void testReadString() throws Exception {
setContents("F703666F6F" + "F700"); setContents("F703666F6F" + "F700");
assertEquals("foo", r.readString()); assertEquals("foo", r.readString(1000));
assertEquals("", r.readString()); assertEquals("", r.readString(1000));
assertTrue(r.eof()); assertTrue(r.eof());
} }
@@ -140,8 +140,8 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadBytes() throws Exception { public void testReadBytes() throws Exception {
setContents("F603010203" + "F600"); setContents("F603010203" + "F600");
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes()); assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes(1000));
assertArrayEquals(new byte[] {}, r.readBytes()); assertArrayEquals(new byte[] {}, r.readBytes(1000));
assertTrue(r.eof()); assertTrue(r.eof());
} }
@@ -157,7 +157,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadList() throws Exception { public void testReadList() throws Exception {
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F3"); setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
List<Object> l = r.readList(Object.class); List<Object> l = r.readList(Object.class);
assertNotNull(l); assertNotNull(l);
assertEquals(3, l.size()); assertEquals(3, l.size());
@@ -169,7 +169,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadListTypeSafe() throws Exception { public void testReadListTypeSafe() throws Exception {
setContents("F5" + "01" + "02" + "03" + "F3"); setContents("F5" + "01" + "02" + "03" + "F2");
List<Byte> l = r.readList(Byte.class); List<Byte> l = r.readList(Byte.class);
assertNotNull(l); assertNotNull(l);
assertEquals(3, l.size()); assertEquals(3, l.size());
@@ -181,7 +181,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadListTypeSafeThrowsFormatException() throws Exception { 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 // Trying to read a mixed list as a list of bytes should throw a
// FormatException // FormatException
try { try {
@@ -192,7 +192,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadMap() throws Exception { 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); Map<Object, Object> m = r.readMap(Object.class, Object.class);
assertNotNull(m); assertNotNull(m);
assertEquals(2, m.size()); assertEquals(2, m.size());
@@ -205,7 +205,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadMapTypeSafe() throws Exception { 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); Map<String, Byte> m = r.readMap(String.class, Byte.class);
assertNotNull(m); assertNotNull(m);
assertEquals(2, m.size()); assertEquals(2, m.size());
@@ -217,8 +217,8 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testMapKeysMustBeUnique() throws Exception { public void testMapKeysMustBeUnique() throws Exception {
setContents("F4" + "F703666F6F" + "01" + "F703626172" + "02" + "F3" setContents("F4" + "F703666F6F" + "01" + "F703626172" + "02" + "F2"
+ "F4" + "F703666F6F" + "01" + "F703666F6F" + "02" + "F3"); + "F4" + "F703666F6F" + "01" + "F703666F6F" + "02" + "F2");
// The first map has unique keys // The first map has unique keys
Map<String, Byte> m = r.readMap(String.class, Byte.class); Map<String, Byte> m = r.readMap(String.class, Byte.class);
assertNotNull(m); assertNotNull(m);
@@ -234,13 +234,13 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadDelimitedListElements() throws Exception { public void testReadDelimitedListElements() throws Exception {
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F3"); setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
assertTrue(r.hasListStart()); assertTrue(r.hasList());
r.readListStart(); r.readListStart();
assertFalse(r.hasListEnd()); assertFalse(r.hasListEnd());
assertEquals((byte) 1, r.readIntAny()); assertEquals((byte) 1, r.readIntAny());
assertFalse(r.hasListEnd()); assertFalse(r.hasListEnd());
assertEquals("foo", r.readString()); assertEquals("foo", r.readString(1000));
assertFalse(r.hasListEnd()); assertFalse(r.hasListEnd());
assertEquals((short) 128, r.readIntAny()); assertEquals((short) 128, r.readIntAny());
assertTrue(r.hasListEnd()); assertTrue(r.hasListEnd());
@@ -250,7 +250,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadDelimitedListTypeSafe() throws Exception { public void testReadDelimitedListTypeSafe() throws Exception {
setContents("F5" + "01" + "02" + "03" + "F3"); setContents("F5" + "01" + "02" + "03" + "F2");
List<Byte> l = r.readList(Byte.class); List<Byte> l = r.readList(Byte.class);
assertNotNull(l); assertNotNull(l);
assertEquals(3, l.size()); assertEquals(3, l.size());
@@ -262,15 +262,15 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadDelimitedMapEntries() throws Exception { public void testReadDelimitedMapEntries() throws Exception {
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F2" + "F3"); setContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
assertTrue(r.hasMapStart()); assertTrue(r.hasMap());
r.readMapStart(); r.readMapStart();
assertFalse(r.hasMapEnd()); assertFalse(r.hasMapEnd());
assertEquals("foo", r.readString()); assertEquals("foo", r.readString(1000));
assertFalse(r.hasMapEnd()); assertFalse(r.hasMapEnd());
assertEquals((byte) 123, r.readIntAny()); assertEquals((byte) 123, r.readIntAny());
assertFalse(r.hasMapEnd()); assertFalse(r.hasMapEnd());
assertArrayEquals(new byte[] {}, r.readBytes()); assertArrayEquals(new byte[] {}, r.readBytes(1000));
assertFalse(r.hasMapEnd()); assertFalse(r.hasMapEnd());
assertTrue(r.hasNull()); assertTrue(r.hasNull());
r.readNull(); r.readNull();
@@ -281,7 +281,7 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testReadDelimitedMapTypeSafe() throws Exception { 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); Map<String, Byte> m = r.readMap(String.class, Byte.class);
assertNotNull(m); assertNotNull(m);
assertEquals(2, m.size()); assertEquals(2, m.size());
@@ -294,8 +294,8 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testReadNestedMapsAndLists() throws Exception { public void testReadNestedMapsAndLists() throws Exception {
setContents("F4" + "F4" + "F703666F6F" + "7B" + "F3" setContents("F4" + "F4" + "F703666F6F" + "7B" + "F2"
+ "F5" + "01" + "F3" + "F3"); + "F5" + "01" + "F2" + "F2");
Map<Object, Object> m = r.readMap(Object.class, Object.class); Map<Object, Object> m = r.readMap(Object.class, Object.class);
assertNotNull(m); assertNotNull(m);
assertEquals(1, m.size()); assertEquals(1, m.size());
@@ -313,23 +313,34 @@ public class ReaderImplTest extends BriarTestCase {
@Test @Test
public void testMaxLengthAppliesInsideMap() throws Exception { public void testMaxStringLengthAppliesInsideMap() throws Exception {
setContents("F4" + "F703666F6F" + "F603010203" + "F3"); setContents("F4" + "F703666F6F" + "F603010203" + "F2");
r.setMaxStringLength(3); r.setMaxStringLength(3);
r.setMaxBytesLength(3);
Map<String, Bytes> m = r.readMap(String.class, Bytes.class); Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
assertTrue(r.eof());
String key = "foo"; String key = "foo";
Bytes value = new Bytes(new byte[] {1, 2, 3}); Bytes value = new Bytes(new byte[] {1, 2, 3});
assertEquals(Collections.singletonMap(key, value), m); assertEquals(Collections.singletonMap(key, value), m);
// The max string length should be applied inside the map // The max string length should be applied inside the map
setContents("F4" + "F703666F6F" + "F603010203" + "F3"); setContents("F4" + "F703666F6F" + "F603010203" + "F2");
r.setMaxStringLength(2); r.setMaxStringLength(2);
try { try {
r.readMap(String.class, Bytes.class); r.readMap(String.class, Bytes.class);
fail(); fail();
} catch(FormatException expected) {} } 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 // The max bytes length should be applied inside the map
setContents("F4" + "F703666F6F" + "F603010203" + "F3"); setContents("F4" + "F703666F6F" + "F603010203" + "F2");
r.setMaxBytesLength(2); r.setMaxBytesLength(2);
try { try {
r.readMap(String.class, Bytes.class); r.readMap(String.class, Bytes.class);
@@ -337,6 +348,19 @@ public class ReaderImplTest extends BriarTestCase {
} catch(FormatException expected) {} } 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 @Test
public void testReadEmptyInput() throws Exception { public void testReadEmptyInput() throws Exception {
setContents(""); setContents("");

View File

@@ -155,7 +155,7 @@ public class WriterImplTest extends BriarTestCase {
for(int i = 0; i < 16; i++) l.add(i); for(int i = 0; i < 16; i++) l.add(i);
w.writeList(l); w.writeList(l);
// LIST tag, elements as uint7, END tag // LIST tag, elements as uint7, END tag
checkContents("F5" + "000102030405060708090A0B0C0D0E0F" + "F3"); checkContents("F5" + "000102030405060708090A0B0C0D0E0F" + "F2");
} }
@Test @Test
@@ -166,7 +166,7 @@ public class WriterImplTest extends BriarTestCase {
l.add(2); l.add(2);
w.writeList(l); w.writeList(l);
// LIST tag, 1 as uint7, null, 2 as uint7, END tag // LIST tag, 1 as uint7, null, 2 as uint7, END tag
checkContents("F5" + "01" + "F2" + "02" + "F3"); checkContents("F5" + "01" + "F1" + "02" + "F2");
} }
@Test @Test
@@ -178,7 +178,7 @@ public class WriterImplTest extends BriarTestCase {
// MAP tag, entries as uint7, END tag // MAP tag, entries as uint7, END tag
checkContents("F4" + "0001" + "0102" + "0203" + "0304" + "0405" checkContents("F4" + "0001" + "0102" + "0203" + "0304" + "0405"
+ "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C" + "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C"
+ "0C0D" + "0D0E" + "0E0F" + "0F10" + "F3"); + "0C0D" + "0D0E" + "0E0F" + "0F10" + "F2");
} }
@Test @Test
@@ -189,7 +189,7 @@ public class WriterImplTest extends BriarTestCase {
w.writeIntAny(128L); // Written as int16 w.writeIntAny(128L); // Written as int16
w.writeListEnd(); w.writeListEnd();
// LIST tag, 1 as uint7, "foo" as string, 128 as int16, END tag // 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 @Test
@@ -202,7 +202,7 @@ public class WriterImplTest extends BriarTestCase {
w.writeMapEnd(); w.writeMapEnd();
// MAP tag, "foo" as string, 123 as uint7, byte[0] as bytes, // MAP tag, "foo" as string, 123 as uint7, byte[0] as bytes,
// NULL tag, END tag // NULL tag, END tag
checkContents("F4" + "F703666F6F" + "7B" + "F600" + "F2" + "F3"); checkContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
} }
@Test @Test
@@ -216,22 +216,22 @@ public class WriterImplTest extends BriarTestCase {
w.writeMap(m1); w.writeMap(m1);
// MAP tag, MAP tag, "foo" as string, 123 as uint7, END tag, // MAP tag, MAP tag, "foo" as string, 123 as uint7, END tag,
// LIST tag, 1 as uint7, END tag, END tag // LIST tag, 1 as uint7, END tag, END tag
checkContents("F4" + "F4" + "F703666F6F" + "7B" + "F3" checkContents("F4" + "F4" + "F703666F6F" + "7B" + "F2"
+ "F5" + "01" + "F3" + "F3"); + "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 @Test
public void testWriteNull() throws IOException { public void testWriteNull() throws IOException {
w.writeNull(); w.writeNull();
checkContents("F2"); checkContents("F1");
}
@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");
} }
private void checkContents(String hex) throws IOException { private void checkContents(String hex) throws IOException {