mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 07:09:56 +01:00
ReaderImpl now maintains either one or two bytes of lookahead,
depending on the value of the first byte, so that an object's initial tag is included in the data seen by the ObjectReader. Digests and signatures can therefore be calculated over objects by their readers without any risk of ambiguity.
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
package net.sf.briar.api.serial;
|
package net.sf.briar.api.serial;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
public interface ObjectReader<T> {
|
public interface ObjectReader<T> {
|
||||||
|
|
||||||
T readObject(Reader r) throws IOException, GeneralSecurityException;
|
T readObject(Reader r) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package net.sf.briar.api.serial;
|
package net.sf.briar.api.serial;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -43,18 +42,16 @@ public interface Reader {
|
|||||||
byte[] readRaw() throws IOException;
|
byte[] readRaw() throws IOException;
|
||||||
|
|
||||||
boolean hasList() throws IOException;
|
boolean hasList() throws IOException;
|
||||||
List<Object> readList() throws IOException, GeneralSecurityException;
|
List<Object> readList() throws IOException;
|
||||||
<E> List<E> readList(Class<E> e) throws IOException,
|
<E> List<E> readList(Class<E> e) throws IOException;
|
||||||
GeneralSecurityException;
|
|
||||||
boolean hasListStart() 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;
|
||||||
Map<Object, Object> readMap() throws IOException, GeneralSecurityException;
|
Map<Object, Object> readMap() 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;
|
||||||
GeneralSecurityException;
|
|
||||||
boolean hasMapStart() throws IOException;
|
boolean hasMapStart() throws IOException;
|
||||||
void readMapStart() throws IOException;
|
void readMapStart() throws IOException;
|
||||||
boolean hasMapEnd() throws IOException;
|
boolean hasMapEnd() throws IOException;
|
||||||
@@ -63,9 +60,7 @@ public interface Reader {
|
|||||||
boolean hasNull() throws IOException;
|
boolean hasNull() throws IOException;
|
||||||
void readNull() throws IOException;
|
void readNull() throws IOException;
|
||||||
|
|
||||||
boolean hasUserDefinedTag() throws IOException;
|
boolean hasUserDefined(int tag) throws IOException;
|
||||||
int readUserDefinedTag() throws IOException;
|
<T> T readUserDefined(int tag, Class<T> t) throws IOException;
|
||||||
void readUserDefinedTag(int tag) throws IOException;
|
void readUserDefinedTag(int tag) throws IOException;
|
||||||
<T> T readUserDefinedObject(int tag, Class<T> t) throws IOException,
|
|
||||||
GeneralSecurityException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.BatchId;
|
import net.sf.briar.api.protocol.BatchId;
|
||||||
|
import net.sf.briar.api.protocol.Tags;
|
||||||
import net.sf.briar.api.protocol.UniqueId;
|
import net.sf.briar.api.protocol.UniqueId;
|
||||||
import net.sf.briar.api.serial.FormatException;
|
import net.sf.briar.api.serial.FormatException;
|
||||||
import net.sf.briar.api.serial.ObjectReader;
|
import net.sf.briar.api.serial.ObjectReader;
|
||||||
@@ -11,6 +12,7 @@ import net.sf.briar.api.serial.Reader;
|
|||||||
public class BatchIdReader implements ObjectReader<BatchId> {
|
public class BatchIdReader implements ObjectReader<BatchId> {
|
||||||
|
|
||||||
public BatchId readObject(Reader r) throws IOException {
|
public BatchId readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(Tags.BATCH_ID);
|
||||||
byte[] b = r.readRaw();
|
byte[] b = r.readRaw();
|
||||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||||
return new BatchId(b);
|
return new BatchId(b);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package net.sf.briar.protocol;
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -25,21 +24,20 @@ public class BatchReader implements ObjectReader<Batch> {
|
|||||||
this.batchFactory = batchFactory;
|
this.batchFactory = batchFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Batch readObject(Reader reader) throws IOException,
|
public Batch readObject(Reader r) throws IOException {
|
||||||
GeneralSecurityException {
|
// Initialise the consumers
|
||||||
// Initialise the consumers - the initial tag has already been read, so
|
CountingConsumer counting = new CountingConsumer(Batch.MAX_SIZE);
|
||||||
// subtract one from the maximum size
|
|
||||||
CountingConsumer counting = new CountingConsumer(Batch.MAX_SIZE - 1);
|
|
||||||
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
||||||
messageDigest.reset();
|
messageDigest.reset();
|
||||||
// Read and digest the data
|
// Read and digest the data
|
||||||
reader.addConsumer(counting);
|
r.addConsumer(counting);
|
||||||
reader.addConsumer(digesting);
|
r.addConsumer(digesting);
|
||||||
reader.addObjectReader(Tags.MESSAGE, messageReader);
|
r.readUserDefinedTag(Tags.BATCH);
|
||||||
List<Message> messages = reader.readList(Message.class);
|
r.addObjectReader(Tags.MESSAGE, messageReader);
|
||||||
reader.removeObjectReader(Tags.MESSAGE);
|
List<Message> messages = r.readList(Message.class);
|
||||||
reader.removeConsumer(digesting);
|
r.removeObjectReader(Tags.MESSAGE);
|
||||||
reader.removeConsumer(counting);
|
r.removeConsumer(digesting);
|
||||||
|
r.removeConsumer(counting);
|
||||||
// Build and return the batch
|
// Build and return the batch
|
||||||
BatchId id = new BatchId(messageDigest.digest());
|
BatchId id = new BatchId(messageDigest.digest());
|
||||||
return batchFactory.createBatch(id, messages);
|
return batchFactory.createBatch(id, messages);
|
||||||
|
|||||||
@@ -29,9 +29,8 @@ class BundleReaderImpl implements BundleReader {
|
|||||||
|
|
||||||
public Header getHeader() throws IOException, GeneralSecurityException {
|
public Header getHeader() throws IOException, GeneralSecurityException {
|
||||||
if(state != State.START) throw new IllegalStateException();
|
if(state != State.START) throw new IllegalStateException();
|
||||||
reader.readUserDefinedTag(Tags.HEADER);
|
|
||||||
reader.addObjectReader(Tags.HEADER, headerReader);
|
reader.addObjectReader(Tags.HEADER, headerReader);
|
||||||
Header h = reader.readUserDefinedObject(Tags.HEADER, Header.class);
|
Header h = reader.readUserDefined(Tags.HEADER, Header.class);
|
||||||
reader.removeObjectReader(Tags.HEADER);
|
reader.removeObjectReader(Tags.HEADER);
|
||||||
// Expect a list of batches
|
// Expect a list of batches
|
||||||
reader.readListStart();
|
reader.readListStart();
|
||||||
@@ -50,8 +49,7 @@ class BundleReaderImpl implements BundleReader {
|
|||||||
state = State.END;
|
state = State.END;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
reader.readUserDefinedTag(Tags.BATCH);
|
return reader.readUserDefined(Tags.BATCH, Batch.class);
|
||||||
return reader.readUserDefinedObject(Tags.BATCH, Batch.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish() throws IOException {
|
public void finish() throws IOException {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import net.sf.briar.api.protocol.GroupId;
|
import net.sf.briar.api.protocol.GroupId;
|
||||||
|
import net.sf.briar.api.protocol.Tags;
|
||||||
import net.sf.briar.api.protocol.UniqueId;
|
import net.sf.briar.api.protocol.UniqueId;
|
||||||
import net.sf.briar.api.serial.FormatException;
|
import net.sf.briar.api.serial.FormatException;
|
||||||
import net.sf.briar.api.serial.ObjectReader;
|
import net.sf.briar.api.serial.ObjectReader;
|
||||||
@@ -11,6 +12,7 @@ import net.sf.briar.api.serial.Reader;
|
|||||||
public class GroupIdReader implements ObjectReader<GroupId> {
|
public class GroupIdReader implements ObjectReader<GroupId> {
|
||||||
|
|
||||||
public GroupId readObject(Reader r) throws IOException {
|
public GroupId readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(Tags.GROUP_ID);
|
||||||
byte[] b = r.readRaw();
|
byte[] b = r.readRaw();
|
||||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||||
return new GroupId(b);
|
return new GroupId(b);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package net.sf.briar.protocol;
|
package net.sf.briar.protocol;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -21,28 +20,27 @@ class HeaderReader implements ObjectReader<Header> {
|
|||||||
this.headerFactory = headerFactory;
|
this.headerFactory = headerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Header readObject(Reader reader) throws IOException,
|
public Header readObject(Reader r) throws IOException {
|
||||||
GeneralSecurityException {
|
// Initialise and add the consumer
|
||||||
// Initialise and add the consumer - the initial tag has already been
|
CountingConsumer counting = new CountingConsumer(Header.MAX_SIZE);
|
||||||
// read, so subtract one from the maximum size
|
r.addConsumer(counting);
|
||||||
CountingConsumer counting = new CountingConsumer(Header.MAX_SIZE - 1);
|
r.readUserDefinedTag(Tags.HEADER);
|
||||||
reader.addConsumer(counting);
|
|
||||||
// Acks
|
// Acks
|
||||||
reader.addObjectReader(Tags.BATCH_ID, new BatchIdReader());
|
r.addObjectReader(Tags.BATCH_ID, new BatchIdReader());
|
||||||
Collection<BatchId> acks = reader.readList(BatchId.class);
|
Collection<BatchId> acks = r.readList(BatchId.class);
|
||||||
reader.removeObjectReader(Tags.BATCH_ID);
|
r.removeObjectReader(Tags.BATCH_ID);
|
||||||
// Subs
|
// Subs
|
||||||
reader.addObjectReader(Tags.GROUP_ID, new GroupIdReader());
|
r.addObjectReader(Tags.GROUP_ID, new GroupIdReader());
|
||||||
Collection<GroupId> subs = reader.readList(GroupId.class);
|
Collection<GroupId> subs = r.readList(GroupId.class);
|
||||||
reader.removeObjectReader(Tags.GROUP_ID);
|
r.removeObjectReader(Tags.GROUP_ID);
|
||||||
// Transports
|
// Transports
|
||||||
Map<String, String> transports =
|
Map<String, String> transports =
|
||||||
reader.readMap(String.class, String.class);
|
r.readMap(String.class, String.class);
|
||||||
// Timestamp
|
// Timestamp
|
||||||
long timestamp = reader.readInt64();
|
long timestamp = r.readInt64();
|
||||||
if(timestamp < 0L) throw new FormatException();
|
if(timestamp < 0L) throw new FormatException();
|
||||||
// Remove the consumer
|
// Remove the consumer
|
||||||
reader.removeConsumer(counting);
|
r.removeConsumer(counting);
|
||||||
// Build and return the header
|
// Build and return the header
|
||||||
return headerFactory.createHeader(acks, subs, transports, timestamp);
|
return headerFactory.createHeader(acks, subs, transports, timestamp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,40 +32,42 @@ class MessageReader implements ObjectReader<Message> {
|
|||||||
this.messageDigest = messageDigest;
|
this.messageDigest = messageDigest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message readObject(Reader reader) throws IOException {
|
public Message readObject(Reader r) throws IOException {
|
||||||
CopyingConsumer copying = new CopyingConsumer();
|
CopyingConsumer copying = new CopyingConsumer();
|
||||||
CountingConsumer counting = new CountingConsumer(Message.MAX_SIZE);
|
CountingConsumer counting = new CountingConsumer(Message.MAX_SIZE);
|
||||||
reader.addConsumer(copying);
|
r.addConsumer(copying);
|
||||||
reader.addConsumer(counting);
|
r.addConsumer(counting);
|
||||||
|
// Read the initial tag
|
||||||
|
r.readUserDefinedTag(Tags.MESSAGE);
|
||||||
// Read the parent's message ID
|
// Read the parent's message ID
|
||||||
reader.readUserDefinedTag(Tags.MESSAGE_ID);
|
r.readUserDefinedTag(Tags.MESSAGE_ID);
|
||||||
byte[] b = reader.readRaw();
|
byte[] b = r.readRaw();
|
||||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||||
MessageId parent = new MessageId(b);
|
MessageId parent = new MessageId(b);
|
||||||
// Read the group ID
|
// Read the group ID
|
||||||
reader.readUserDefinedTag(Tags.GROUP_ID);
|
r.readUserDefinedTag(Tags.GROUP_ID);
|
||||||
b = reader.readRaw();
|
b = r.readRaw();
|
||||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||||
GroupId group = new GroupId(b);
|
GroupId group = new GroupId(b);
|
||||||
// Read the timestamp
|
// Read the timestamp
|
||||||
long timestamp = reader.readInt64();
|
long timestamp = r.readInt64();
|
||||||
if(timestamp < 0L) throw new FormatException();
|
if(timestamp < 0L) throw new FormatException();
|
||||||
// Hash the author's nick and public key to get the author ID
|
// Hash the author's nick and public key to get the author ID
|
||||||
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
||||||
messageDigest.reset();
|
messageDigest.reset();
|
||||||
reader.addConsumer(digesting);
|
r.addConsumer(digesting);
|
||||||
reader.readString();
|
r.readString();
|
||||||
byte[] encodedKey = reader.readRaw();
|
byte[] encodedKey = r.readRaw();
|
||||||
reader.removeConsumer(digesting);
|
r.removeConsumer(digesting);
|
||||||
AuthorId author = new AuthorId(messageDigest.digest());
|
AuthorId author = new AuthorId(messageDigest.digest());
|
||||||
// Skip the message body
|
// Skip the message body
|
||||||
reader.readRaw();
|
r.readRaw();
|
||||||
// Record the length of the signed data
|
// Record the length of the signed data
|
||||||
int messageLength = (int) counting.getCount();
|
int messageLength = (int) counting.getCount();
|
||||||
// Read the signature
|
// Read the signature
|
||||||
byte[] sig = reader.readRaw();
|
byte[] sig = r.readRaw();
|
||||||
reader.removeConsumer(counting);
|
r.removeConsumer(counting);
|
||||||
reader.removeConsumer(copying);
|
r.removeConsumer(copying);
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
PublicKey publicKey;
|
PublicKey publicKey;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package net.sf.briar.serial;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -20,12 +19,11 @@ class ReaderImpl implements Reader {
|
|||||||
private static final byte[] EMPTY_BUFFER = new byte[] {};
|
private static final byte[] EMPTY_BUFFER = new byte[] {};
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private final Map<Integer, ObjectReader<?>> objectReaders =
|
|
||||||
new HashMap<Integer, ObjectReader<?>>();
|
|
||||||
|
|
||||||
private Consumer[] consumers = new Consumer[] {};
|
private Consumer[] consumers = new Consumer[] {};
|
||||||
|
private ObjectReader<?>[] objectReaders = new ObjectReader<?>[] {};
|
||||||
private boolean started = false, eof = false;
|
private boolean started = false, eof = false;
|
||||||
private byte next;
|
private byte next, nextNext;
|
||||||
private byte[] buf = null;
|
private byte[] buf = null;
|
||||||
|
|
||||||
ReaderImpl(InputStream in) {
|
ReaderImpl(InputStream in) {
|
||||||
@@ -39,16 +37,32 @@ class ReaderImpl implements Reader {
|
|||||||
|
|
||||||
private byte readNext(boolean eofAcceptable) throws IOException {
|
private byte readNext(boolean eofAcceptable) throws IOException {
|
||||||
assert !eof;
|
assert !eof;
|
||||||
if(started) for(Consumer c : consumers) c.write(next);
|
if(started) {
|
||||||
|
for(Consumer c : consumers) {
|
||||||
|
c.write(next);
|
||||||
|
if(next == Tag.USER) c.write(nextNext);
|
||||||
|
}
|
||||||
|
}
|
||||||
started = true;
|
started = true;
|
||||||
|
readLookahead(eofAcceptable);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readLookahead(boolean eofAcceptable) throws IOException {
|
||||||
|
assert started;
|
||||||
|
// Read the lookahead byte
|
||||||
int i = in.read();
|
int i = in.read();
|
||||||
if(i == -1) {
|
if(i == -1) {
|
||||||
eof = true;
|
|
||||||
if(!eofAcceptable) throw new FormatException();
|
if(!eofAcceptable) throw new FormatException();
|
||||||
|
eof = true;
|
||||||
}
|
}
|
||||||
if(i > 127) i -= 256;
|
|
||||||
next = (byte) i;
|
next = (byte) i;
|
||||||
return next;
|
// If necessary, read another lookahead byte
|
||||||
|
if(next == Tag.USER) {
|
||||||
|
i = in.read();
|
||||||
|
if(i == -1) throw new FormatException();
|
||||||
|
nextNext = (byte) i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@@ -78,11 +92,20 @@ class ReaderImpl implements Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addObjectReader(int tag, ObjectReader<?> o) {
|
public void addObjectReader(int tag, ObjectReader<?> o) {
|
||||||
objectReaders.put(tag, o);
|
if(tag < 0 || tag > 255) throw new IllegalArgumentException();
|
||||||
|
if(objectReaders.length < tag + 1) {
|
||||||
|
ObjectReader<?>[] newObjectReaders = new ObjectReader<?>[tag + 1];
|
||||||
|
System.arraycopy(objectReaders, 0, newObjectReaders, 0,
|
||||||
|
objectReaders.length);
|
||||||
|
objectReaders = newObjectReaders;
|
||||||
|
}
|
||||||
|
objectReaders[tag] = o;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeObjectReader(int tag) {
|
public void removeObjectReader(int tag) {
|
||||||
objectReaders.remove(tag);
|
if(tag < 0 || tag > objectReaders.length)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
objectReaders[tag] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasBoolean() throws IOException {
|
public boolean hasBoolean() throws IOException {
|
||||||
@@ -165,19 +188,22 @@ class ReaderImpl implements Reader {
|
|||||||
private void readIntoBuffer(byte[] b, int length) throws IOException {
|
private void readIntoBuffer(byte[] b, int length) throws IOException {
|
||||||
b[0] = next;
|
b[0] = next;
|
||||||
int offset = 1;
|
int offset = 1;
|
||||||
|
if(next == Tag.USER) {
|
||||||
|
b[1] = nextNext;
|
||||||
|
offset = 2;
|
||||||
|
}
|
||||||
while(offset < length) {
|
while(offset < length) {
|
||||||
int read = in.read(b, offset, length - offset);
|
int read = in.read(b, offset, length - offset);
|
||||||
if(read == -1) break;
|
if(read == -1) {
|
||||||
|
eof = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
if(offset < length) throw new FormatException();
|
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);
|
||||||
// Read the lookahead byte
|
readLookahead(true);
|
||||||
int i = in.read();
|
|
||||||
if(i == -1) eof = true;
|
|
||||||
if(i > 127) i -= 256;
|
|
||||||
next = (byte) i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasInt64() throws IOException {
|
public boolean hasInt64() throws IOException {
|
||||||
@@ -316,13 +342,11 @@ class ReaderImpl implements Reader {
|
|||||||
|| (next & Tag.SHORT_MASK) == Tag.SHORT_LIST;
|
|| (next & Tag.SHORT_MASK) == Tag.SHORT_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> readList() throws IOException,
|
public List<Object> readList() throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
return readList(Object.class);
|
return readList(Object.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <E> List<E> readList(Class<E> e) throws IOException,
|
public <E> List<E> readList(Class<E> e) throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
if(!hasList()) throw new FormatException();
|
if(!hasList()) throw new FormatException();
|
||||||
if(next == Tag.LIST) {
|
if(next == Tag.LIST) {
|
||||||
readNext(false);
|
readNext(false);
|
||||||
@@ -340,8 +364,7 @@ class ReaderImpl implements Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <E> List<E> readList(Class<E> e, int length) throws IOException,
|
private <E> List<E> readList(Class<E> e, int length) throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
assert length >= 0;
|
assert length >= 0;
|
||||||
List<E> list = new ArrayList<E>();
|
List<E> list = new ArrayList<E>();
|
||||||
for(int i = 0; i < length; i++) list.add(readObject(e));
|
for(int i = 0; i < length; i++) list.add(readObject(e));
|
||||||
@@ -360,13 +383,9 @@ class ReaderImpl implements Reader {
|
|||||||
readNext(true);
|
readNext(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object readObject() throws IOException, GeneralSecurityException {
|
private Object readObject() throws IOException {
|
||||||
if(!started) throw new IllegalStateException();
|
if(!started) throw new IllegalStateException();
|
||||||
if(hasUserDefinedTag()) {
|
if(hasUserDefined()) return readUserDefined();
|
||||||
ObjectReader<?> o = objectReaders.get(readUserDefinedTag());
|
|
||||||
if(o == null) throw new FormatException();
|
|
||||||
return o.readObject(this);
|
|
||||||
}
|
|
||||||
if(hasBoolean()) return Boolean.valueOf(readBoolean());
|
if(hasBoolean()) return Boolean.valueOf(readBoolean());
|
||||||
if(hasUint7()) return Byte.valueOf(readUint7());
|
if(hasUint7()) return Byte.valueOf(readUint7());
|
||||||
if(hasInt8()) return Byte.valueOf(readInt8());
|
if(hasInt8()) return Byte.valueOf(readInt8());
|
||||||
@@ -386,8 +405,23 @@ class ReaderImpl implements Reader {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T readObject(Class<T> t) throws IOException,
|
private boolean hasUserDefined() throws IOException {
|
||||||
GeneralSecurityException {
|
if(!started) readNext(true);
|
||||||
|
if(eof) return false;
|
||||||
|
if(next == Tag.USER) return true;
|
||||||
|
if((next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readUserDefined() throws IOException {
|
||||||
|
if(!hasUserDefined()) throw new FormatException();
|
||||||
|
int tag;
|
||||||
|
if(next == Tag.USER) tag = 0xFF & nextNext;
|
||||||
|
else tag = 0xFF & next ^ Tag.SHORT_USER;
|
||||||
|
return readUserDefined(tag, Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T readObject(Class<T> t) throws IOException {
|
||||||
try {
|
try {
|
||||||
return t.cast(readObject());
|
return t.cast(readObject());
|
||||||
} catch(ClassCastException e) {
|
} catch(ClassCastException e) {
|
||||||
@@ -421,13 +455,11 @@ class ReaderImpl implements Reader {
|
|||||||
|| (next & Tag.SHORT_MASK) == Tag.SHORT_MAP;
|
|| (next & Tag.SHORT_MASK) == Tag.SHORT_MAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Object, Object> readMap() throws IOException,
|
public Map<Object, Object> readMap() throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
return readMap(Object.class, Object.class);
|
return readMap(Object.class, Object.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException,
|
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
if(!hasMap()) throw new FormatException();
|
if(!hasMap()) throw new FormatException();
|
||||||
if(next == Tag.MAP) {
|
if(next == Tag.MAP) {
|
||||||
readNext(false);
|
readNext(false);
|
||||||
@@ -446,7 +478,7 @@ class ReaderImpl implements Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private <K, V> Map<K, V> readMap(Class<K> k, Class<V> v, int size)
|
private <K, V> Map<K, V> readMap(Class<K> k, Class<V> v, int size)
|
||||||
throws IOException, GeneralSecurityException {
|
throws IOException {
|
||||||
assert size >= 0;
|
assert size >= 0;
|
||||||
Map<K, V> m = new HashMap<K, V>();
|
Map<K, V> m = new HashMap<K, V>();
|
||||||
for(int i = 0; i < size; i++) m.put(readObject(k), readObject(v));
|
for(int i = 0; i < size; i++) m.put(readObject(k), readObject(v));
|
||||||
@@ -483,32 +515,21 @@ class ReaderImpl implements Reader {
|
|||||||
readNext(true);
|
readNext(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasUserDefinedTag() throws IOException {
|
public boolean hasUserDefined(int tag) throws IOException {
|
||||||
|
if(tag < 0 || tag > 255) throw new IllegalArgumentException();
|
||||||
if(!started) readNext(true);
|
if(!started) readNext(true);
|
||||||
if(eof) return false;
|
if(eof) return false;
|
||||||
return next == Tag.USER ||
|
if(next == Tag.USER)
|
||||||
(next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER;
|
return tag == (0xFF & nextNext);
|
||||||
|
else if((next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER)
|
||||||
|
return tag == (0xFF & next ^ Tag.SHORT_USER);
|
||||||
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int readUserDefinedTag() throws IOException {
|
public <T> T readUserDefined(int tag, Class<T> t) throws IOException {
|
||||||
if(!hasUserDefinedTag()) throw new FormatException();
|
if(!hasUserDefined(tag)) throw new FormatException();
|
||||||
if(next == Tag.USER) {
|
if(tag >= objectReaders.length) throw new FormatException();
|
||||||
readNext(false);
|
ObjectReader<?> o = objectReaders[tag];
|
||||||
return readLength();
|
|
||||||
} else {
|
|
||||||
int tag = 0xFF & next ^ Tag.SHORT_USER;
|
|
||||||
readNext(true);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readUserDefinedTag(int tag) throws IOException {
|
|
||||||
if(readUserDefinedTag() != tag) throw new FormatException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T readUserDefinedObject(int tag, Class<T> t) throws IOException,
|
|
||||||
GeneralSecurityException {
|
|
||||||
ObjectReader<?> o = objectReaders.get(tag);
|
|
||||||
if(o == null) throw new FormatException();
|
if(o == null) throw new FormatException();
|
||||||
try {
|
try {
|
||||||
return t.cast(o.readObject(this));
|
return t.cast(o.readObject(this));
|
||||||
@@ -516,4 +537,9 @@ class ReaderImpl implements Reader {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void readUserDefinedTag(int tag) throws IOException {
|
||||||
|
if(!hasUserDefined(tag)) throw new FormatException();
|
||||||
|
readNext(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class WriterImpl implements Writer {
|
|||||||
|
|
||||||
public void writeString(String s) throws IOException {
|
public void writeString(String s) throws IOException {
|
||||||
byte[] b = s.getBytes("UTF-8");
|
byte[] b = s.getBytes("UTF-8");
|
||||||
if(b.length < 16) out.write(intToByte(Tag.SHORT_STRING | b.length));
|
if(b.length < 16) out.write((byte) (Tag.SHORT_STRING | b.length));
|
||||||
else {
|
else {
|
||||||
out.write(Tag.STRING);
|
out.write(Tag.STRING);
|
||||||
writeLength(b.length);
|
writeLength(b.length);
|
||||||
@@ -120,12 +120,6 @@ class WriterImpl implements Writer {
|
|||||||
bytesWritten += b.length + 1;
|
bytesWritten += b.length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte intToByte(int i) {
|
|
||||||
assert i >= 0;
|
|
||||||
assert i <= 255;
|
|
||||||
return (byte) (i > 127 ? i - 256 : i);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLength(int i) throws IOException {
|
private void writeLength(int i) throws IOException {
|
||||||
assert i >= 0;
|
assert i >= 0;
|
||||||
// Fun fact: it's never worth writing a length as an int8
|
// Fun fact: it's never worth writing a length as an int8
|
||||||
@@ -135,7 +129,7 @@ class WriterImpl implements Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeRaw(byte[] b) throws IOException {
|
public void writeRaw(byte[] b) throws IOException {
|
||||||
if(b.length < 16) out.write(intToByte(Tag.SHORT_RAW | b.length));
|
if(b.length < 16) out.write((byte) (Tag.SHORT_RAW | b.length));
|
||||||
else {
|
else {
|
||||||
out.write(Tag.RAW);
|
out.write(Tag.RAW);
|
||||||
writeLength(b.length);
|
writeLength(b.length);
|
||||||
@@ -150,7 +144,7 @@ class WriterImpl implements Writer {
|
|||||||
|
|
||||||
public void writeList(Collection<?> c) throws IOException {
|
public void writeList(Collection<?> c) throws IOException {
|
||||||
int length = c.size();
|
int length = c.size();
|
||||||
if(length < 16) out.write(intToByte(Tag.SHORT_LIST | length));
|
if(length < 16) out.write((byte) (Tag.SHORT_LIST | length));
|
||||||
else {
|
else {
|
||||||
out.write(Tag.LIST);
|
out.write(Tag.LIST);
|
||||||
writeLength(length);
|
writeLength(length);
|
||||||
@@ -188,7 +182,7 @@ class WriterImpl implements Writer {
|
|||||||
|
|
||||||
public void writeMap(Map<?, ?> m) throws IOException {
|
public void writeMap(Map<?, ?> m) throws IOException {
|
||||||
int length = m.size();
|
int length = m.size();
|
||||||
if(length < 16) out.write(intToByte(Tag.SHORT_MAP | length));
|
if(length < 16) out.write((byte) (Tag.SHORT_MAP | length));
|
||||||
else {
|
else {
|
||||||
out.write(Tag.MAP);
|
out.write(Tag.MAP);
|
||||||
writeLength(length);
|
writeLength(length);
|
||||||
@@ -216,12 +210,14 @@ class WriterImpl implements Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeUserDefinedTag(int tag) throws IOException {
|
public void writeUserDefinedTag(int tag) throws IOException {
|
||||||
if(tag < 0) throw new IllegalArgumentException();
|
if(tag < 0 || tag > 255) throw new IllegalArgumentException();
|
||||||
if(tag < 32) out.write((byte) (Tag.SHORT_USER | tag));
|
if(tag < 32) {
|
||||||
else {
|
out.write((byte) (Tag.SHORT_USER | tag));
|
||||||
|
bytesWritten++;
|
||||||
|
} else {
|
||||||
out.write(Tag.USER);
|
out.write(Tag.USER);
|
||||||
writeLength(tag);
|
out.write((byte) tag);
|
||||||
|
bytesWritten += 2;
|
||||||
}
|
}
|
||||||
bytesWritten++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package net.sf.briar.protocol;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@@ -65,9 +64,8 @@ public class BatchReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.BATCH, batchReader);
|
reader.addObjectReader(Tags.BATCH, batchReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.BATCH);
|
|
||||||
try {
|
try {
|
||||||
reader.readUserDefinedObject(Tags.BATCH, Batch.class);
|
reader.readUserDefined(Tags.BATCH, Batch.class);
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch(FormatException expected) {}
|
} catch(FormatException expected) {}
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
@@ -91,17 +89,15 @@ public class BatchReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.BATCH, batchReader);
|
reader.addObjectReader(Tags.BATCH, batchReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.BATCH);
|
assertEquals(batch, reader.readUserDefined(Tags.BATCH, Batch.class));
|
||||||
assertEquals(batch, reader.readUserDefinedObject(Tags.BATCH,
|
|
||||||
Batch.class));
|
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBatchId() throws Exception {
|
public void testBatchId() throws Exception {
|
||||||
byte[] b = createBatch(Batch.MAX_SIZE);
|
byte[] b = createBatch(Batch.MAX_SIZE);
|
||||||
// Calculate the expected batch ID, skipping the initial tag
|
// Calculate the expected batch ID
|
||||||
messageDigest.update(b, 1, b.length - 1);
|
messageDigest.update(b);
|
||||||
final BatchId id = new BatchId(messageDigest.digest());
|
final BatchId id = new BatchId(messageDigest.digest());
|
||||||
messageDigest.reset();
|
messageDigest.reset();
|
||||||
|
|
||||||
@@ -121,9 +117,7 @@ public class BatchReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.BATCH, batchReader);
|
reader.addObjectReader(Tags.BATCH, batchReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.BATCH);
|
assertEquals(batch, reader.readUserDefined(Tags.BATCH, Batch.class));
|
||||||
assertEquals(batch, reader.readUserDefinedObject(Tags.BATCH,
|
|
||||||
Batch.class));
|
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,9 +139,7 @@ public class BatchReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.BATCH, batchReader);
|
reader.addObjectReader(Tags.BATCH, batchReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.BATCH);
|
assertEquals(batch, reader.readUserDefined(Tags.BATCH, Batch.class));
|
||||||
assertEquals(batch, reader.readUserDefinedObject(Tags.BATCH,
|
|
||||||
Batch.class));
|
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,8 +148,8 @@ public class BatchReaderTest extends TestCase {
|
|||||||
Writer w = writerFactory.createWriter(out);
|
Writer w = writerFactory.createWriter(out);
|
||||||
w.writeUserDefinedTag(Tags.BATCH);
|
w.writeUserDefinedTag(Tags.BATCH);
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
w.writeUserDefinedTag(Tags.MESSAGE);
|
|
||||||
// We're using a fake message reader, so it's OK to use a fake message
|
// We're using a fake message reader, so it's OK to use a fake message
|
||||||
|
w.writeUserDefinedTag(Tags.MESSAGE);
|
||||||
w.writeRaw(new byte[size - 10]);
|
w.writeRaw(new byte[size - 10]);
|
||||||
w.writeListEnd();
|
w.writeListEnd();
|
||||||
w.close();
|
w.close();
|
||||||
@@ -178,8 +170,8 @@ public class BatchReaderTest extends TestCase {
|
|||||||
|
|
||||||
private class TestMessageReader implements ObjectReader<Message> {
|
private class TestMessageReader implements ObjectReader<Message> {
|
||||||
|
|
||||||
public Message readObject(Reader r) throws IOException,
|
public Message readObject(Reader r) throws IOException {
|
||||||
GeneralSecurityException {
|
r.readUserDefinedTag(Tags.MESSAGE);
|
||||||
r.readRaw();
|
r.readRaw();
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package net.sf.briar.protocol;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
@@ -217,8 +216,7 @@ public class BundleReaderImplTest extends TestCase {
|
|||||||
|
|
||||||
private class TestHeaderReader implements ObjectReader<Header> {
|
private class TestHeaderReader implements ObjectReader<Header> {
|
||||||
|
|
||||||
public Header readObject(Reader r) throws IOException,
|
public Header readObject(Reader r) throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
r.readList();
|
r.readList();
|
||||||
r.readList();
|
r.readList();
|
||||||
r.readMap();
|
r.readMap();
|
||||||
@@ -229,8 +227,7 @@ public class BundleReaderImplTest extends TestCase {
|
|||||||
|
|
||||||
private class TestBatchReader implements ObjectReader<Batch> {
|
private class TestBatchReader implements ObjectReader<Batch> {
|
||||||
|
|
||||||
public Batch readObject(Reader r) throws IOException,
|
public Batch readObject(Reader r) throws IOException {
|
||||||
GeneralSecurityException {
|
|
||||||
r.readList();
|
r.readList();
|
||||||
return context.mock(Batch.class);
|
return context.mock(Batch.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,8 @@ public class HeaderReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.HEADER, headerReader);
|
reader.addObjectReader(Tags.HEADER, headerReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.HEADER);
|
|
||||||
try {
|
try {
|
||||||
reader.readUserDefinedObject(Tags.HEADER, Header.class);
|
reader.readUserDefined(Tags.HEADER, Header.class);
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch(FormatException expected) {}
|
} catch(FormatException expected) {}
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
@@ -78,9 +77,7 @@ public class HeaderReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.HEADER, headerReader);
|
reader.addObjectReader(Tags.HEADER, headerReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.HEADER);
|
assertEquals(header, reader.readUserDefined(Tags.HEADER, Header.class));
|
||||||
assertEquals(header, reader.readUserDefinedObject(Tags.HEADER,
|
|
||||||
Header.class));
|
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,26 +100,23 @@ public class HeaderReaderTest extends TestCase {
|
|||||||
Reader reader = readerFactory.createReader(in);
|
Reader reader = readerFactory.createReader(in);
|
||||||
reader.addObjectReader(Tags.HEADER, headerReader);
|
reader.addObjectReader(Tags.HEADER, headerReader);
|
||||||
|
|
||||||
reader.readUserDefinedTag(Tags.HEADER);
|
assertEquals(header, reader.readUserDefined(Tags.HEADER, Header.class));
|
||||||
assertEquals(header, reader.readUserDefinedObject(Tags.HEADER,
|
|
||||||
Header.class));
|
|
||||||
context.assertIsSatisfied();
|
context.assertIsSatisfied();
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createHeader(int size) throws Exception {
|
private byte[] createHeader(int size) throws Exception {
|
||||||
Random random = new Random();
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
|
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
|
||||||
Writer w = writerFactory.createWriter(out);
|
Writer w = writerFactory.createWriter(out);
|
||||||
w.writeUserDefinedTag(Tags.HEADER);
|
w.writeUserDefinedTag(Tags.HEADER);
|
||||||
// Acks
|
// No acks
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
w.writeListEnd();
|
w.writeListEnd();
|
||||||
// Subs
|
// Fill most of the header with subs
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
// Fill most of the header with subscriptions
|
byte[] b = new byte[UniqueId.LENGTH];
|
||||||
while(w.getBytesWritten() < size - 45) {
|
Random random = new Random();
|
||||||
|
while(out.size() < size - 60) {
|
||||||
w.writeUserDefinedTag(Tags.GROUP_ID);
|
w.writeUserDefinedTag(Tags.GROUP_ID);
|
||||||
byte[] b = new byte[UniqueId.LENGTH];
|
|
||||||
random.nextBytes(b);
|
random.nextBytes(b);
|
||||||
w.writeRaw(b);
|
w.writeRaw(b);
|
||||||
}
|
}
|
||||||
@@ -131,7 +125,8 @@ public class HeaderReaderTest extends TestCase {
|
|||||||
w.writeMapStart();
|
w.writeMapStart();
|
||||||
w.writeString("foo");
|
w.writeString("foo");
|
||||||
// Build a string that will bring the header up to the expected size
|
// Build a string that will bring the header up to the expected size
|
||||||
int length = (int) (size - w.getBytesWritten() - 12);
|
int length = size - out.size() - 12;
|
||||||
|
assertTrue(length > 0);
|
||||||
StringBuilder s = new StringBuilder();
|
StringBuilder s = new StringBuilder();
|
||||||
for(int i = 0; i < length; i++) s.append((char) ('0' + i % 10));
|
for(int i = 0; i < length; i++) s.append((char) ('0' + i % 10));
|
||||||
w.writeString(s.toString());
|
w.writeString(s.toString());
|
||||||
@@ -139,9 +134,8 @@ public class HeaderReaderTest extends TestCase {
|
|||||||
// Timestamp
|
// Timestamp
|
||||||
w.writeInt64(System.currentTimeMillis());
|
w.writeInt64(System.currentTimeMillis());
|
||||||
w.close();
|
w.close();
|
||||||
byte[] b = out.toByteArray();
|
assertEquals(size, out.size());
|
||||||
assertEquals(size, b.length);
|
return out.toByteArray();
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createEmptyHeader() throws Exception {
|
private byte[] createEmptyHeader() throws Exception {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.sf.briar.serial;
|
package net.sf.briar.serial;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -8,6 +9,7 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.briar.api.serial.Consumer;
|
||||||
import net.sf.briar.api.serial.FormatException;
|
import net.sf.briar.api.serial.FormatException;
|
||||||
import net.sf.briar.api.serial.ObjectReader;
|
import net.sf.briar.api.serial.ObjectReader;
|
||||||
import net.sf.briar.api.serial.Raw;
|
import net.sf.briar.api.serial.Raw;
|
||||||
@@ -331,35 +333,77 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadUserDefinedTag() throws Exception {
|
public void testReadUserDefined() throws Exception {
|
||||||
setContents("C0" + "DF" + "EF" + "20" + "EF" + "FB7FFFFFFF");
|
setContents("C0" + "83666F6F" + "EF" + "FF" + "83666F6F");
|
||||||
assertEquals(0, r.readUserDefinedTag());
|
// Add object readers for two user-defined types
|
||||||
assertEquals(31, r.readUserDefinedTag());
|
|
||||||
assertEquals(32, r.readUserDefinedTag());
|
|
||||||
assertEquals(Integer.MAX_VALUE, r.readUserDefinedTag());
|
|
||||||
assertTrue(r.eof());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadUserDefinedObject() throws Exception {
|
|
||||||
setContents("C0" + "83666F6F");
|
|
||||||
// Add an object reader for a user-defined type
|
|
||||||
r.addObjectReader(0, new ObjectReader<Foo>() {
|
r.addObjectReader(0, new ObjectReader<Foo>() {
|
||||||
public Foo readObject(Reader r) throws IOException {
|
public Foo readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(0);
|
||||||
return new Foo(r.readString());
|
return new Foo(r.readString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertEquals(0, r.readUserDefinedTag());
|
r.addObjectReader(255, new ObjectReader<Bar>() {
|
||||||
assertEquals("foo", r.readUserDefinedObject(0, Foo.class).s);
|
public Bar readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(255);
|
||||||
|
return new Bar(r.readString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Test both tag formats, short and long
|
||||||
|
assertTrue(r.hasUserDefined(0));
|
||||||
|
assertEquals("foo", r.readUserDefined(0, Foo.class).s);
|
||||||
|
assertTrue(r.hasUserDefined(255));
|
||||||
|
assertEquals("foo", r.readUserDefined(255, Bar.class).s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadUserDefinedWithConsumer() throws Exception {
|
||||||
|
setContents("C0" + "83666F6F" + "EF" + "FF" + "83666F6F");
|
||||||
|
// Add object readers for two user-defined types
|
||||||
|
r.addObjectReader(0, new ObjectReader<Foo>() {
|
||||||
|
public Foo readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(0);
|
||||||
|
return new Foo(r.readString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
r.addObjectReader(255, new ObjectReader<Bar>() {
|
||||||
|
public Bar readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(255);
|
||||||
|
return new Bar(r.readString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Add a consumer
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
r.addConsumer(new Consumer() {
|
||||||
|
|
||||||
|
public void write(byte b) throws IOException {
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(byte[] b) throws IOException {
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
out.write(b, off, len);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Test both tag formats, short and long
|
||||||
|
assertTrue(r.hasUserDefined(0));
|
||||||
|
assertEquals("foo", r.readUserDefined(0, Foo.class).s);
|
||||||
|
assertTrue(r.hasUserDefined(255));
|
||||||
|
assertEquals("foo", r.readUserDefined(255, Bar.class).s);
|
||||||
|
// Check that everything was passed to the consumer
|
||||||
|
assertEquals("C0" + "83666F6F" + "EF" + "FF" + "83666F6F",
|
||||||
|
StringUtils.toHexString(out.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnknownTagThrowsFormatException() throws Exception {
|
public void testUnknownTagThrowsFormatException() throws Exception {
|
||||||
setContents("C0" + "83666F6F");
|
setContents("C0" + "83666F6F");
|
||||||
|
assertTrue(r.hasUserDefined(0));
|
||||||
// No object reader has been added for tag 0
|
// No object reader has been added for tag 0
|
||||||
assertEquals(0, r.readUserDefinedTag());
|
|
||||||
try {
|
try {
|
||||||
r.readUserDefinedObject(0, Foo.class);
|
r.readUserDefined(0, Foo.class);
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch(FormatException expected) {}
|
} catch(FormatException expected) {}
|
||||||
}
|
}
|
||||||
@@ -370,13 +414,14 @@ public class ReaderImplTest extends TestCase {
|
|||||||
// Add an object reader for tag 0, class Foo
|
// Add an object reader for tag 0, class Foo
|
||||||
r.addObjectReader(0, new ObjectReader<Foo>() {
|
r.addObjectReader(0, new ObjectReader<Foo>() {
|
||||||
public Foo readObject(Reader r) throws IOException {
|
public Foo readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(0);
|
||||||
return new Foo(r.readString());
|
return new Foo(r.readString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertEquals(0, r.readUserDefinedTag());
|
assertTrue(r.hasUserDefined(0));
|
||||||
// Trying to read the object as class Bar should throw a FormatException
|
// Trying to read the object as class Bar should throw a FormatException
|
||||||
try {
|
try {
|
||||||
r.readUserDefinedObject(0, Bar.class);
|
r.readUserDefined(0, Bar.class);
|
||||||
assertTrue(false);
|
assertTrue(false);
|
||||||
} catch(FormatException expected) {}
|
} catch(FormatException expected) {}
|
||||||
}
|
}
|
||||||
@@ -387,6 +432,7 @@ public class ReaderImplTest extends TestCase {
|
|||||||
// Add an object reader for a user-defined type
|
// Add an object reader for a user-defined type
|
||||||
r.addObjectReader(0, new ObjectReader<Foo>() {
|
r.addObjectReader(0, new ObjectReader<Foo>() {
|
||||||
public Foo readObject(Reader r) throws IOException {
|
public Foo readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(0);
|
||||||
return new Foo(r.readString());
|
return new Foo(r.readString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -402,11 +448,13 @@ public class ReaderImplTest extends TestCase {
|
|||||||
// Add object readers for two user-defined types
|
// Add object readers for two user-defined types
|
||||||
r.addObjectReader(0, new ObjectReader<Foo>() {
|
r.addObjectReader(0, new ObjectReader<Foo>() {
|
||||||
public Foo readObject(Reader r) throws IOException {
|
public Foo readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(0);
|
||||||
return new Foo(r.readString());
|
return new Foo(r.readString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
r.addObjectReader(1, new ObjectReader<Bar>() {
|
r.addObjectReader(1, new ObjectReader<Bar>() {
|
||||||
public Bar readObject(Reader r) throws IOException {
|
public Bar readObject(Reader r) throws IOException {
|
||||||
|
r.readUserDefinedTag(1);
|
||||||
return new Bar(r.readString());
|
return new Bar(r.readString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -298,9 +298,9 @@ public class WriterImplTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWriteUserDefinedTag() throws IOException {
|
public void testWriteUserDefinedTag() throws IOException {
|
||||||
w.writeUserDefinedTag(32);
|
w.writeUserDefinedTag(32);
|
||||||
w.writeUserDefinedTag(Integer.MAX_VALUE);
|
w.writeUserDefinedTag(255);
|
||||||
// USER tag, 32 as uint7, USER tag, 2147483647 as int32
|
// USER tag, 32 as uint8, USER tag, 255 as uint8
|
||||||
checkContents("EF" + "20" + "EF" + "FB7FFFFFFF");
|
checkContents("EF" + "20" + "EF" + "FF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -323,6 +323,5 @@ public class WriterImplTest extends TestCase {
|
|||||||
byte[] expected = StringUtils.fromHexString(hex);
|
byte[] expected = StringUtils.fromHexString(hex);
|
||||||
assertTrue(StringUtils.toHexString(out.toByteArray()),
|
assertTrue(StringUtils.toHexString(out.toByteArray()),
|
||||||
Arrays.equals(expected, out.toByteArray()));
|
Arrays.equals(expected, out.toByteArray()));
|
||||||
assertEquals(expected.length, w.getBytesWritten());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ public class StringUtils {
|
|||||||
for(int i = 0, j = 0; i < len; i += 2, j++) {
|
for(int i = 0, j = 0; i < len; i += 2, j++) {
|
||||||
int high = hexDigitToInt(hex.charAt(i));
|
int high = hexDigitToInt(hex.charAt(i));
|
||||||
int low = hexDigitToInt(hex.charAt(i + 1));
|
int low = hexDigitToInt(hex.charAt(i + 1));
|
||||||
int b = (high << 4) + low;
|
bytes[j] = (byte) ((high << 4) + low);
|
||||||
if(b > 127) b -= 256;
|
|
||||||
bytes[j] = (byte) b;
|
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user