mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Added the ability to skip serialised objects.
This commit is contained in:
@@ -1,67 +1,79 @@
|
||||
package net.sf.briar.api.serial;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface Reader {
|
||||
|
||||
boolean eof() throws IOException;
|
||||
void close() throws IOException;
|
||||
|
||||
void setMaxStringLength(int length);
|
||||
void resetMaxStringLength();
|
||||
|
||||
void setMaxBytesLength(int length);
|
||||
void resetMaxBytesLength();
|
||||
|
||||
void addConsumer(Consumer c);
|
||||
void removeConsumer(Consumer c);
|
||||
|
||||
boolean hasBoolean() throws IOException;
|
||||
boolean readBoolean() throws IOException;
|
||||
void skipBoolean() throws IOException;
|
||||
|
||||
boolean hasUint7() throws IOException;
|
||||
byte readUint7() throws IOException;
|
||||
void skipUint7() throws IOException;
|
||||
|
||||
boolean hasInt8() throws IOException;
|
||||
byte readInt8() throws IOException;
|
||||
void skipInt8() throws IOException;
|
||||
|
||||
boolean hasInt16() throws IOException;
|
||||
short readInt16() throws IOException;
|
||||
void skipInt16() throws IOException;
|
||||
|
||||
boolean hasInt32() throws IOException;
|
||||
int readInt32() throws IOException;
|
||||
void skipInt32() throws IOException;
|
||||
|
||||
boolean hasInt64() throws IOException;
|
||||
long readInt64() throws IOException;
|
||||
void skipInt64() throws IOException;
|
||||
|
||||
boolean hasIntAny() throws IOException;
|
||||
long readIntAny() throws IOException;
|
||||
void skipIntAny() throws IOException;
|
||||
|
||||
boolean hasFloat32() throws IOException;
|
||||
float readFloat32() throws IOException;
|
||||
void skipFloat32() throws IOException;
|
||||
|
||||
boolean hasFloat64() throws IOException;
|
||||
double readFloat64() throws IOException;
|
||||
void skipFloat64() throws IOException;
|
||||
|
||||
boolean hasString() throws IOException;
|
||||
String readString(int maxLength) throws IOException;
|
||||
void skipString(int maxLength) throws IOException;
|
||||
|
||||
boolean hasBytes() throws IOException;
|
||||
byte[] readBytes(int maxLength) throws IOException;
|
||||
void skipBytes(int maxLength) throws IOException;
|
||||
|
||||
boolean hasList() throws IOException;
|
||||
<E> List<E> readList(Class<E> e) throws IOException;
|
||||
void readListStart() throws IOException;
|
||||
boolean hasListEnd() throws IOException;
|
||||
void readListEnd() throws IOException;
|
||||
void skipList() throws IOException;
|
||||
|
||||
boolean hasMap() throws IOException;
|
||||
<K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException;
|
||||
void readMapStart() throws IOException;
|
||||
boolean hasMapEnd() throws IOException;
|
||||
void readMapEnd() throws IOException;
|
||||
void skipMap() throws IOException;
|
||||
|
||||
boolean hasStruct() throws IOException;
|
||||
boolean hasStruct(int id) throws IOException;
|
||||
void readStructStart(int id) throws IOException;
|
||||
boolean hasStructEnd() throws IOException;
|
||||
void readStructEnd() throws IOException;
|
||||
void skipStruct() throws IOException;
|
||||
|
||||
boolean hasNull() throws IOException;
|
||||
void readNull() throws IOException;
|
||||
void skipNull() throws IOException;
|
||||
}
|
||||
|
||||
@@ -244,11 +244,16 @@ abstract class Connector extends Thread {
|
||||
byte[] b = r.readBytes(UniqueId.LENGTH);
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
TransportId id = new TransportId(b);
|
||||
r.setMaxStringLength(MAX_PROPERTY_LENGTH);
|
||||
Map<String, String> p = r.readMap(String.class, String.class);
|
||||
r.resetMaxStringLength();
|
||||
if(p.size() > MAX_PROPERTIES_PER_TRANSPORT)
|
||||
throw new FormatException();
|
||||
Map<String, String> p = new HashMap<String, String>();
|
||||
r.readMapStart();
|
||||
for(int i = 0; !r.hasMapEnd(); i++) {
|
||||
if(i == MAX_PROPERTIES_PER_TRANSPORT)
|
||||
throw new FormatException();
|
||||
String key = r.readString(MAX_PROPERTY_LENGTH);
|
||||
String value = r.readString(MAX_PROPERTY_LENGTH);
|
||||
p.put(key, value);
|
||||
}
|
||||
r.readMapEnd();
|
||||
remoteProps.put(id, new TransportProperties(p));
|
||||
}
|
||||
r.readListEnd();
|
||||
|
||||
@@ -19,10 +19,10 @@ import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.TransportId;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
@@ -75,22 +75,21 @@ class PacketReaderImpl implements PacketReader {
|
||||
r.addConsumer(counting);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(ACK);
|
||||
// Read the message IDs as byte arrays
|
||||
r.setMaxBytesLength(UniqueId.LENGTH);
|
||||
List<Bytes> raw = r.readList(Bytes.class);
|
||||
// Read the message IDs
|
||||
List<MessageId> acked = new ArrayList<MessageId>();
|
||||
r.readListStart();
|
||||
while(!r.hasListEnd()) {
|
||||
byte[] b = r.readBytes(UniqueId.LENGTH);
|
||||
if(b.length != UniqueId.LENGTH)
|
||||
throw new FormatException();
|
||||
acked.add(new MessageId(b));
|
||||
}
|
||||
if(acked.isEmpty()) throw new FormatException();
|
||||
r.readListEnd();
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.resetMaxBytesLength();
|
||||
r.removeConsumer(counting);
|
||||
if(raw.isEmpty()) throw new FormatException();
|
||||
// Convert the byte arrays to message IDs
|
||||
List<MessageId> acked = new ArrayList<MessageId>();
|
||||
for(Bytes b : raw) {
|
||||
if(b.getBytes().length != UniqueId.LENGTH)
|
||||
throw new FormatException();
|
||||
acked.add(new MessageId(b.getBytes()));
|
||||
}
|
||||
// Build and return the ack
|
||||
return new Ack(Collections.unmodifiableList(acked));
|
||||
}
|
||||
@@ -113,22 +112,21 @@ class PacketReaderImpl implements PacketReader {
|
||||
r.addConsumer(counting);
|
||||
// Read the start of the struct
|
||||
r.readStructStart(OFFER);
|
||||
// Read the message IDs as byte arrays
|
||||
r.setMaxBytesLength(UniqueId.LENGTH);
|
||||
List<Bytes> raw = r.readList(Bytes.class);
|
||||
// Read the message IDs
|
||||
List<MessageId> messages = new ArrayList<MessageId>();
|
||||
r.readListStart();
|
||||
while(!r.hasListEnd()) {
|
||||
byte[] b = r.readBytes(UniqueId.LENGTH);
|
||||
if(b.length != UniqueId.LENGTH)
|
||||
throw new FormatException();
|
||||
messages.add(new MessageId(b));
|
||||
}
|
||||
if(messages.isEmpty()) throw new FormatException();
|
||||
r.readListEnd();
|
||||
// Read the end of the struct
|
||||
r.readStructEnd();
|
||||
// Reset the reader
|
||||
r.resetMaxBytesLength();
|
||||
r.removeConsumer(counting);
|
||||
if(raw.isEmpty()) throw new FormatException();
|
||||
// Convert the byte arrays to message IDs
|
||||
List<MessageId> messages = new ArrayList<MessageId>();
|
||||
for(Bytes b : raw) {
|
||||
if(b.getBytes().length != UniqueId.LENGTH)
|
||||
throw new FormatException();
|
||||
messages.add(new MessageId(b.getBytes()));
|
||||
}
|
||||
// Build and return the offer
|
||||
return new Offer(Collections.unmodifiableList(messages));
|
||||
}
|
||||
@@ -239,10 +237,16 @@ class PacketReaderImpl implements PacketReader {
|
||||
if(b.length < UniqueId.LENGTH) throw new FormatException();
|
||||
TransportId id = new TransportId(b);
|
||||
// Read the transport properties
|
||||
r.setMaxStringLength(MAX_PROPERTY_LENGTH);
|
||||
Map<String, String> m = r.readMap(String.class, String.class);
|
||||
r.resetMaxStringLength();
|
||||
if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) throw new FormatException();
|
||||
Map<String, String> p = new HashMap<String, String>();
|
||||
r.readMapStart();
|
||||
for(int i = 0; !r.hasMapEnd(); i++) {
|
||||
if(i == MAX_PROPERTIES_PER_TRANSPORT)
|
||||
throw new FormatException();
|
||||
String key = r.readString(MAX_PROPERTY_LENGTH);
|
||||
String value = r.readString(MAX_PROPERTY_LENGTH);
|
||||
p.put(key, value);
|
||||
}
|
||||
r.readMapEnd();
|
||||
// Read the version number
|
||||
long version = r.readIntAny();
|
||||
if(version < 0) throw new FormatException();
|
||||
@@ -251,6 +255,6 @@ class PacketReaderImpl implements PacketReader {
|
||||
// Reset the reader
|
||||
r.removeConsumer(counting);
|
||||
// Build and return the transport update
|
||||
return new TransportUpdate(id, new TransportProperties(m), version);
|
||||
return new TransportUpdate(id, new TransportProperties(p), version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.serial.Consumer;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
@@ -25,8 +20,6 @@ class ReaderImpl implements Reader {
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next, nextStructId;
|
||||
private byte[] buf = new byte[8];
|
||||
private int maxStringLength = Integer.MAX_VALUE;
|
||||
private int maxBytesLength = Integer.MAX_VALUE;
|
||||
|
||||
ReaderImpl(InputStream in) {
|
||||
this.in = in;
|
||||
@@ -70,22 +63,6 @@ class ReaderImpl implements Reader {
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void setMaxStringLength(int length) {
|
||||
maxStringLength = length;
|
||||
}
|
||||
|
||||
public void resetMaxStringLength() {
|
||||
maxStringLength = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public void setMaxBytesLength(int length) {
|
||||
maxBytesLength = length;
|
||||
}
|
||||
|
||||
public void resetMaxBytesLength() {
|
||||
maxBytesLength = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public void addConsumer(Consumer c) {
|
||||
consumers.add(c);
|
||||
}
|
||||
@@ -106,6 +83,11 @@ class ReaderImpl implements Reader {
|
||||
return next == Tag.TRUE;
|
||||
}
|
||||
|
||||
public void skipBoolean() throws IOException {
|
||||
if(!hasBoolean()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasUint7() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -118,6 +100,11 @@ class ReaderImpl implements Reader {
|
||||
return next;
|
||||
}
|
||||
|
||||
public void skipUint7() throws IOException {
|
||||
if(!hasUint7()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasInt8() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -132,7 +119,19 @@ class ReaderImpl implements Reader {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
return (byte) i;
|
||||
byte b = (byte) i;
|
||||
// Feed the hungry mouths
|
||||
for(Consumer c : consumers) c.write(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public void skipInt8() throws IOException {
|
||||
if(!hasInt8()) throw new FormatException();
|
||||
if(in.read() == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasInt16() throws IOException {
|
||||
@@ -148,24 +147,6 @@ class ReaderImpl implements Reader {
|
||||
return (short) (((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF));
|
||||
}
|
||||
|
||||
public boolean hasInt32() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT32;
|
||||
}
|
||||
|
||||
public int readInt32() throws IOException {
|
||||
if(!hasInt32()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return readInt32Bits();
|
||||
}
|
||||
|
||||
private int readInt32Bits() throws IOException {
|
||||
readIntoBuffer(4);
|
||||
return ((buf[0] & 0xFF) << 24) | ((buf[1] & 0xFF) << 16) |
|
||||
((buf[2] & 0xFF) << 8) | (buf[3] & 0xFF);
|
||||
}
|
||||
|
||||
private void readIntoBuffer(int length) throws IOException {
|
||||
if(buf.length < length) buf = new byte[length];
|
||||
readIntoBuffer(buf, length);
|
||||
@@ -186,6 +167,47 @@ class ReaderImpl implements Reader {
|
||||
for(Consumer c : consumers) c.write(b, 0, length);
|
||||
}
|
||||
|
||||
public void skipInt16() throws IOException {
|
||||
if(!hasInt16()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(2);
|
||||
}
|
||||
|
||||
private void skip(int length) throws IOException {
|
||||
while(length > 0) {
|
||||
int read = in.read(buf, 0, Math.min(length, buf.length));
|
||||
if(read == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
length -= read;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasInt32() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT32;
|
||||
}
|
||||
|
||||
public int readInt32() throws IOException {
|
||||
if(!hasInt32()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return readInt32Bits();
|
||||
}
|
||||
|
||||
private int readInt32Bits() throws IOException {
|
||||
readIntoBuffer(4);
|
||||
return ((buf[0] & 0xFF) << 24) | ((buf[1] & 0xFF) << 16) |
|
||||
((buf[2] & 0xFF) << 8) | (buf[3] & 0xFF);
|
||||
}
|
||||
|
||||
public void skipInt32() throws IOException {
|
||||
if(!hasInt32()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public boolean hasInt64() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -206,6 +228,12 @@ class ReaderImpl implements Reader {
|
||||
((buf[6] & 0xFFL) << 8) | (buf[7] & 0xFFL);
|
||||
}
|
||||
|
||||
public void skipInt64() throws IOException {
|
||||
if(!hasInt64()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(8);
|
||||
}
|
||||
|
||||
public boolean hasIntAny() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -223,6 +251,16 @@ class ReaderImpl implements Reader {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public void skipIntAny() throws IOException {
|
||||
if(!hasIntAny()) throw new FormatException();
|
||||
if(next >= 0) skipUint7();
|
||||
else if(next == Tag.INT8) skipInt8();
|
||||
else if(next == Tag.INT16) skipInt16();
|
||||
else if(next == Tag.INT32) skipInt32();
|
||||
else if(next == Tag.INT64) skipInt64();
|
||||
else throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public boolean hasFloat32() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -235,6 +273,12 @@ class ReaderImpl implements Reader {
|
||||
return Float.intBitsToFloat(readInt32Bits());
|
||||
}
|
||||
|
||||
public void skipFloat32() throws IOException {
|
||||
if(!hasFloat32()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public boolean hasFloat64() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -247,6 +291,12 @@ class ReaderImpl implements Reader {
|
||||
return Double.longBitsToDouble(readInt64Bits());
|
||||
}
|
||||
|
||||
public void skipFloat64() throws IOException {
|
||||
if(!hasFloat64()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(8);
|
||||
}
|
||||
|
||||
public boolean hasString() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -282,6 +332,15 @@ class ReaderImpl implements Reader {
|
||||
|| next == Tag.INT32;
|
||||
}
|
||||
|
||||
public void skipString(int maxLength) throws IOException {
|
||||
if(!hasString()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
int length = readLength();
|
||||
if(length > maxLength) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(length);
|
||||
}
|
||||
|
||||
public boolean hasBytes() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -299,79 +358,21 @@ class ReaderImpl implements Reader {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void skipBytes(int maxLength) throws IOException {
|
||||
if(!hasBytes()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
int length = readLength();
|
||||
if(length > maxLength) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(length);
|
||||
}
|
||||
|
||||
public boolean hasList() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.LIST;
|
||||
}
|
||||
|
||||
public <E> List<E> readList(Class<E> e) throws IOException {
|
||||
if(!hasList()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
List<E> list = new ArrayList<E>();
|
||||
while(!hasEnd()) list.add(readObject(e));
|
||||
readEnd();
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.END;
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if(!hasEnd()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
private Object readObject() throws IOException {
|
||||
if(hasBoolean()) return Boolean.valueOf(readBoolean());
|
||||
if(hasUint7()) return Byte.valueOf(readUint7());
|
||||
if(hasInt8()) return Byte.valueOf(readInt8());
|
||||
if(hasInt16()) return Short.valueOf(readInt16());
|
||||
if(hasInt32()) return Integer.valueOf(readInt32());
|
||||
if(hasInt64()) return Long.valueOf(readInt64());
|
||||
if(hasFloat32()) return Float.valueOf(readFloat32());
|
||||
if(hasFloat64()) return Double.valueOf(readFloat64());
|
||||
if(hasString()) return readString(maxStringLength);
|
||||
if(hasBytes()) return new Bytes(readBytes(maxBytesLength));
|
||||
if(hasList()) return readList(Object.class);
|
||||
if(hasMap()) return readMap(Object.class, Object.class);
|
||||
if(hasNull()) {
|
||||
readNull();
|
||||
return null;
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
private <T> T readObject(Class<T> t) throws IOException {
|
||||
try {
|
||||
Object o = readObject();
|
||||
// If this is a small integer type and we're expecting a larger
|
||||
// integer type, promote before casting
|
||||
if(o instanceof Byte) {
|
||||
if(Short.class.isAssignableFrom(t))
|
||||
return t.cast(Short.valueOf((Byte) o));
|
||||
if(Integer.class.isAssignableFrom(t))
|
||||
return t.cast(Integer.valueOf((Byte) o));
|
||||
if(Long.class.isAssignableFrom(t))
|
||||
return t.cast(Long.valueOf((Byte) o));
|
||||
} else if(o instanceof Short) {
|
||||
if(Integer.class.isAssignableFrom(t))
|
||||
return t.cast(Integer.valueOf((Short) o));
|
||||
if(Long.class.isAssignableFrom(t))
|
||||
return t.cast(Long.valueOf((Short) o));
|
||||
} else if(o instanceof Integer) {
|
||||
if(Long.class.isAssignableFrom(t))
|
||||
return t.cast(Long.valueOf((Integer) o));
|
||||
}
|
||||
return t.cast(o);
|
||||
} catch(ClassCastException e) {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public void readListStart() throws IOException {
|
||||
if(!hasList()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
@@ -381,28 +382,48 @@ class ReaderImpl implements Reader {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.END;
|
||||
}
|
||||
|
||||
public void readListEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if(!hasEnd()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public void skipList() throws IOException {
|
||||
if(!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while(!hasListEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
private void skipObject() throws IOException {
|
||||
if(hasBoolean()) skipBoolean();
|
||||
else if(hasIntAny()) skipIntAny();
|
||||
else if(hasFloat32()) skipFloat32();
|
||||
else if(hasFloat64()) skipFloat64();
|
||||
else if(hasString()) skipString(Integer.MAX_VALUE);
|
||||
else if(hasBytes()) skipBytes(Integer.MAX_VALUE);
|
||||
else if(hasList()) skipList();
|
||||
else if(hasMap()) skipMap();
|
||||
else if(hasStruct()) skipStruct();
|
||||
else if(hasNull()) skipNull();
|
||||
else throw new FormatException();
|
||||
}
|
||||
|
||||
public boolean hasMap() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.MAP;
|
||||
}
|
||||
|
||||
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException {
|
||||
if(!hasMap()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
Map<K, V> m = new HashMap<K, V>();
|
||||
while(!hasEnd()) {
|
||||
if(m.put(readObject(k), readObject(v)) != null)
|
||||
throw new FormatException(); // Duplicate key
|
||||
}
|
||||
readEnd();
|
||||
return Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
public void readMapStart() throws IOException {
|
||||
if(!hasMap()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
@@ -416,11 +437,27 @@ class ReaderImpl implements Reader {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
public void skipMap() throws IOException {
|
||||
if(!hasMap()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while(!hasMapEnd()) {
|
||||
skipObject();
|
||||
skipObject();
|
||||
}
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasStruct() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.STRUCT;
|
||||
}
|
||||
|
||||
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;
|
||||
return next == Tag.STRUCT && (nextStructId & 0xFF) == id;
|
||||
}
|
||||
|
||||
public void readStructStart(int id) throws IOException {
|
||||
@@ -436,6 +473,13 @@ class ReaderImpl implements Reader {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
public void skipStruct() throws IOException {
|
||||
if(!hasStruct()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while(!hasStructEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasNull() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
@@ -446,4 +490,9 @@ class ReaderImpl implements Reader {
|
||||
if(!hasNull()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public void skipNull() throws IOException {
|
||||
if(!hasNull()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,8 @@ package net.sf.briar.serial;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
@@ -22,12 +17,20 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadBoolean() throws Exception {
|
||||
setContents("FFFE");
|
||||
setContents("FF" + "FE");
|
||||
assertFalse(r.readBoolean());
|
||||
assertTrue(r.readBoolean());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipBoolean() throws Exception {
|
||||
setContents("FF" + "FE");
|
||||
r.skipBoolean();
|
||||
r.skipBoolean();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt8() throws Exception {
|
||||
setContents("FD00" + "FDFF" + "FD7F" + "FD80");
|
||||
@@ -38,6 +41,13 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipInt8() throws Exception {
|
||||
setContents("FD00");
|
||||
r.skipInt8();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt16() throws Exception {
|
||||
setContents("FC0000" + "FCFFFF" + "FC7FFF" + "FC8000");
|
||||
@@ -48,6 +58,13 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipInt16() throws Exception {
|
||||
setContents("FC0000");
|
||||
r.skipInt16();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt32() throws Exception {
|
||||
setContents("FB00000000" + "FBFFFFFFFF" + "FB7FFFFFFF" + "FB80000000");
|
||||
@@ -58,6 +75,13 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipInt32() throws Exception {
|
||||
setContents("FB00000000");
|
||||
r.skipInt32();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt64() throws Exception {
|
||||
setContents("FA0000000000000000" + "FAFFFFFFFFFFFFFFFF"
|
||||
@@ -69,6 +93,13 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipInt64() throws Exception {
|
||||
setContents("FA0000000000000000");
|
||||
r.skipInt64();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIntAny() throws Exception {
|
||||
setContents("00" + "7F" + "FD80" + "FDFF" + "FC0080" + "FC7FFF"
|
||||
@@ -85,6 +116,18 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipIntAny() throws Exception {
|
||||
setContents("00" + "FD00" + "FC0000" + "FB00000000"
|
||||
+ "FA0000000000000000");
|
||||
r.skipIntAny();
|
||||
r.skipIntAny();
|
||||
r.skipIntAny();
|
||||
r.skipIntAny();
|
||||
r.skipIntAny();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFloat32() throws Exception {
|
||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||
@@ -102,6 +145,13 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipFloat32() throws Exception {
|
||||
setContents("F900000000");
|
||||
r.skipFloat32();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFloat64() throws Exception {
|
||||
setContents("F80000000000000000" + "F83FF0000000000000"
|
||||
@@ -119,11 +169,18 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipFloat64() throws Exception {
|
||||
setContents("F80000000000000000");
|
||||
r.skipFloat64();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadString() throws Exception {
|
||||
setContents("F703666F6F" + "F700");
|
||||
assertEquals("foo", r.readString(1000));
|
||||
assertEquals("", r.readString(1000));
|
||||
assertEquals("foo", r.readString(Integer.MAX_VALUE));
|
||||
assertEquals("", r.readString(Integer.MAX_VALUE));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@@ -137,11 +194,29 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipString() throws Exception {
|
||||
setContents("F703666F6F" + "F700");
|
||||
r.skipString(Integer.MAX_VALUE);
|
||||
r.skipString(Integer.MAX_VALUE);
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipStringMaxLength() throws Exception {
|
||||
setContents("F703666F6F" + "F703666F6F");
|
||||
r.skipString(3);
|
||||
try {
|
||||
r.skipString(2);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBytes() throws Exception {
|
||||
setContents("F603010203" + "F600");
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes(1000));
|
||||
assertArrayEquals(new byte[] {}, r.readBytes(1000));
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes(Integer.MAX_VALUE));
|
||||
assertArrayEquals(new byte[] {}, r.readBytes(Integer.MAX_VALUE));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@@ -155,87 +230,27 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipBytes() throws Exception {
|
||||
setContents("F603010203" + "F600");
|
||||
r.skipBytes(Integer.MAX_VALUE);
|
||||
r.skipBytes(Integer.MAX_VALUE);
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipBytesMaxLength() throws Exception {
|
||||
setContents("F603010203" + "F603010203");
|
||||
r.skipBytes(3);
|
||||
try {
|
||||
r.skipBytes(2);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadList() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
|
||||
List<Object> l = r.readList(Object.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals((byte) 1, l.get(0));
|
||||
assertEquals("foo", l.get(1));
|
||||
assertEquals((short) 128, l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListTypeSafe() throws Exception {
|
||||
setContents("F5" + "01" + "02" + "03" + "F2");
|
||||
List<Byte> l = r.readList(Byte.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals(Byte.valueOf((byte) 1), l.get(0));
|
||||
assertEquals(Byte.valueOf((byte) 2), l.get(1));
|
||||
assertEquals(Byte.valueOf((byte) 3), l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListTypeSafeThrowsFormatException() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "03" + "F2");
|
||||
// Trying to read a mixed list as a list of bytes should throw a
|
||||
// FormatException
|
||||
try {
|
||||
r.readList(Byte.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals((byte) 123, m.get("foo"));
|
||||
Bytes b = new Bytes(new byte[] {});
|
||||
assertTrue(m.containsKey(b));
|
||||
assertNull(m.get(b));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadMapTypeSafe() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F700" + "F1" + "F2");
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals(Byte.valueOf((byte) 123), m.get("foo"));
|
||||
assertTrue(m.containsKey(""));
|
||||
assertNull(m.get(""));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeysMustBeUnique() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "01" + "F703626172" + "02" + "F2"
|
||||
+ "F4" + "F703666F6F" + "01" + "F703666F6F" + "02" + "F2");
|
||||
// The first map has unique keys
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals(Byte.valueOf((byte) 1), m.get("foo"));
|
||||
assertEquals(Byte.valueOf((byte) 2), m.get("bar"));
|
||||
// The second map has a duplicate key
|
||||
try {
|
||||
r.readMap(String.class, Byte.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedListElements() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
|
||||
assertTrue(r.hasList());
|
||||
r.readListStart();
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals((byte) 1, r.readIntAny());
|
||||
@@ -249,21 +264,15 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedListTypeSafe() throws Exception {
|
||||
setContents("F5" + "01" + "02" + "03" + "F2");
|
||||
List<Byte> l = r.readList(Byte.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals(Byte.valueOf((byte) 1), l.get(0));
|
||||
assertEquals(Byte.valueOf((byte) 2), l.get(1));
|
||||
assertEquals(Byte.valueOf((byte) 3), l.get(2));
|
||||
public void testSkipList() throws Exception {
|
||||
setContents("F5" + "01" + "F703666F6F" + "FC0080" + "F2");
|
||||
r.skipList();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMapEntries() throws Exception {
|
||||
public void testReadMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
|
||||
assertTrue(r.hasMap());
|
||||
r.readMapStart();
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertEquals("foo", r.readString(1000));
|
||||
@@ -280,87 +289,54 @@ public class ReaderImplTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMapTypeSafe() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F700" + "F1" + "F2");
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals(Byte.valueOf((byte) 123), m.get("foo"));
|
||||
assertTrue(m.containsKey(""));
|
||||
assertNull(m.get(""));
|
||||
public void testSkipMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "7B" + "F600" + "F1" + "F2");
|
||||
r.skipMap();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testReadNestedMapsAndLists() throws Exception {
|
||||
setContents("F4" + "F4" + "F703666F6F" + "7B" + "F2"
|
||||
+ "F5" + "01" + "F2" + "F2");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(1, m.size());
|
||||
Entry<Object, Object> e = m.entrySet().iterator().next();
|
||||
Map<Object, Object> m1 = (Map<Object, Object>) e.getKey();
|
||||
assertNotNull(m1);
|
||||
assertEquals(1, m1.size());
|
||||
assertEquals((byte) 123, m1.get("foo"));
|
||||
List<Object> l = (List<Object>) e.getValue();
|
||||
assertNotNull(l);
|
||||
assertEquals(1, l.size());
|
||||
assertEquals((byte) 1, l.get(0));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMaxStringLengthAppliesInsideMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxStringLength(3);
|
||||
Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
|
||||
assertTrue(r.eof());
|
||||
String key = "foo";
|
||||
Bytes value = new Bytes(new byte[] {1, 2, 3});
|
||||
assertEquals(Collections.singletonMap(key, value), m);
|
||||
// The max string length should be applied inside the map
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxStringLength(2);
|
||||
try {
|
||||
r.readMap(String.class, Bytes.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxBytesLengthAppliesInsideMap() throws Exception {
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxBytesLength(3);
|
||||
Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
|
||||
assertTrue(r.eof());
|
||||
String key = "foo";
|
||||
Bytes value = new Bytes(new byte[] {1, 2, 3});
|
||||
assertEquals(Collections.singletonMap(key, value), m);
|
||||
// The max bytes length should be applied inside the map
|
||||
setContents("F4" + "F703666F6F" + "F603010203" + "F2");
|
||||
r.setMaxBytesLength(2);
|
||||
try {
|
||||
r.readMap(String.class, Bytes.class);
|
||||
fail();
|
||||
} 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");
|
||||
// Two empty structs with IDs 0 and 255
|
||||
setContents("F300" + "F2" + "F3FF" + "F2");
|
||||
r.readStructStart(0);
|
||||
assertEquals(1, r.readUint7());
|
||||
r.readStructEnd();
|
||||
r.readStructStart(255);
|
||||
assertEquals(1, r.readUint7());
|
||||
r.readStructEnd();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipStruct() throws Exception {
|
||||
// Two empty structs with IDs 0 and 255
|
||||
setContents("F300" + "F2" + "F3FF" + "F2");
|
||||
r.skipStruct();
|
||||
r.skipStruct();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipNestedStructMapAndList() throws Exception {
|
||||
// A struct containing a map containing two empty lists
|
||||
setContents("F300" + "F4" + "F5" + "F2" + "F5" + "F2" + "F2" + "F2");
|
||||
r.skipStruct();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadNull() throws Exception {
|
||||
setContents("F1");
|
||||
r.readNull();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipNull() throws Exception {
|
||||
setContents("F1");
|
||||
r.skipNull();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadEmptyInput() throws Exception {
|
||||
setContents("");
|
||||
|
||||
Reference in New Issue
Block a user