Modifying Protocol Buffers (or Thrift, or MessagePack, or any of the free ASN.1 implementations I could find) to support length constraints was more work than writing a custom serialisation format, so I wrote a custom format.

This commit is contained in:
akwizgran
2011-07-10 14:44:15 +01:00
parent 4deb52478d
commit 1f5e52c31b
21 changed files with 1212 additions and 1142 deletions

View File

@@ -7,14 +7,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.invitation.InvitationCallback;
import net.sf.briar.api.invitation.InvitationParameters;
import net.sf.briar.api.protocol.Transport.TransportDetails;
import net.sf.briar.api.protocol.Transport.TransportDetails.TransportDetail;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
import net.sf.briar.util.FileUtils;
class InvitationWorker implements Runnable {
@@ -22,13 +21,16 @@ class InvitationWorker implements Runnable {
private final InvitationCallback callback;
private final InvitationParameters parameters;
private final DatabaseComponent databaseComponent;
private final WriterFactory writerFactory;
InvitationWorker(final InvitationCallback callback,
InvitationParameters parameters,
DatabaseComponent databaseComponent) {
DatabaseComponent databaseComponent,
WriterFactory writerFactory) {
this.callback = callback;
this.parameters = parameters;
this.databaseComponent = databaseComponent;
this.writerFactory = writerFactory;
}
public void run() {
@@ -65,6 +67,7 @@ class InvitationWorker implements Runnable {
private File createInvitationDat(File dir) throws IOException {
char[] password = parameters.getPassword();
assert password != null;
Arrays.fill(password, (char) 0);
File invitationDat = new File(dir, "invitation.dat");
callback.encryptingFile(invitationDat);
// FIXME: Create a real invitation
@@ -74,19 +77,11 @@ class InvitationWorker implements Runnable {
} catch(DbException e) {
throw new IOException(e);
}
TransportDetails.Builder b = TransportDetails.newBuilder();
for(Entry<String, String> e : transports.entrySet()) {
TransportDetail.Builder b1 = TransportDetail.newBuilder();
b1.setKey(e.getKey());
b1.setValue(e.getValue());
b.addDetails(b1.build());
}
TransportDetails t = b.build();
FileOutputStream out = new FileOutputStream(invitationDat);
t.writeTo(out);
Writer w = writerFactory.createWriter(out);
w.writeMap(transports);
out.flush();
out.close();
Arrays.fill(password, (char) 0);
return invitationDat;
}

View File

@@ -4,20 +4,25 @@ import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.invitation.InvitationCallback;
import net.sf.briar.api.invitation.InvitationParameters;
import net.sf.briar.api.invitation.InvitationWorkerFactory;
import net.sf.briar.api.serial.WriterFactory;
import com.google.inject.Inject;
class InvitationWorkerFactoryImpl implements InvitationWorkerFactory {
private final DatabaseComponent databaseComponent;
private final WriterFactory writerFactory;
@Inject
InvitationWorkerFactoryImpl(DatabaseComponent databaseComponent) {
InvitationWorkerFactoryImpl(DatabaseComponent databaseComponent,
WriterFactory writerFactory) {
this.databaseComponent = databaseComponent;
this.writerFactory = writerFactory;
}
public Runnable createWorker(InvitationCallback callback,
InvitationParameters parameters) {
return new InvitationWorker(callback, parameters, databaseComponent);
return new InvitationWorker(callback, parameters, databaseComponent,
writerFactory);
}
}

View File

@@ -0,0 +1,29 @@
package net.sf.briar.serial;
import java.util.Arrays;
import net.sf.briar.api.serial.Raw;
class RawImpl implements Raw {
private final byte[] bytes;
RawImpl(byte[] bytes) {
this.bytes = bytes;
}
public byte[] getBytes() {
return bytes;
}
@Override
public int hashCode() {
return Arrays.hashCode(bytes);
}
@Override
public boolean equals(Object o) {
if(o instanceof Raw) return Arrays.equals(bytes, ((Raw) o).getBytes());
return false;
}
}

View File

@@ -0,0 +1,13 @@
package net.sf.briar.serial;
import java.io.InputStream;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.ReaderFactory;
public class ReaderFactoryImpl implements ReaderFactory {
public Reader createReader(InputStream in) {
return new ReaderImpl(in);
}
}

View File

@@ -0,0 +1,360 @@
package net.sf.briar.serial;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.briar.api.serial.FormatException;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.Tag;
public class ReaderImpl implements Reader {
private static final int TOO_LARGE_TO_KEEP = 4096;
private final InputStream in;
private boolean started = false, eof = false;
private byte next;
private byte[] stringBuffer = null;
public ReaderImpl(InputStream in) {
this.in = in;
}
private byte readNext(boolean eofAcceptable) throws IOException {
started = true;
int i = in.read();
if(i == -1) {
eof = true;
if(!eofAcceptable) throw new FormatException();
}
if(i > 127) i -= 256;
next = (byte) i;
return next;
}
public boolean eof() throws IOException {
if(!started) readNext(true);
return eof;
}
public boolean hasBoolean() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.FALSE || next == Tag.TRUE;
}
public boolean readBoolean() throws IOException {
if(!hasBoolean()) throw new FormatException();
int i = next;
readNext(true);
return i == Tag.TRUE;
}
public boolean hasUint7() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next >= 0;
}
public byte readUint7() throws IOException {
if(!hasUint7()) throw new FormatException();
byte b = next;
readNext(true);
return b;
}
public boolean hasInt8() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.INT8;
}
public byte readInt8() throws IOException {
if(!hasInt8()) throw new FormatException();
byte b = readNext(false);
readNext(true);
return b;
}
public boolean hasInt16() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.INT16;
}
public short readInt16() throws IOException {
if(!hasInt16()) throw new FormatException();
byte b1 = readNext(false);
byte b2 = readNext(false);
readNext(true);
int i = ((b1 & 0xFF) << 8) | (b2 & 0xFF);
return (short) i;
}
public boolean hasInt32() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.INT32;
}
public int readInt32() throws IOException {
if(!hasInt32()) throw new FormatException();
return readInt32Bits();
}
private int readInt32Bits() throws IOException {
byte b1 = readNext(false);
byte b2 = readNext(false);
byte b3 = readNext(false);
byte b4 = readNext(false);
readNext(true);
return ((b1 & 0xFF) << 24) | ((b2 & 0xFF) << 16) |
((b3 & 0xFF) << 8) | (b4 & 0xFF);
}
public boolean hasInt64() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.INT64;
}
public long readInt64() throws IOException {
if(!hasInt64()) throw new FormatException();
return readInt64Bits();
}
private long readInt64Bits() throws IOException {
byte b1 = readNext(false);
byte b2 = readNext(false);
byte b3 = readNext(false);
byte b4 = readNext(false);
byte b5 = readNext(false);
byte b6 = readNext(false);
byte b7 = readNext(false);
byte b8 = readNext(false);
readNext(true);
return ((b1 & 0xFFL) << 56) | ((b2 & 0xFFL) << 48) |
((b3 & 0xFFL) << 40) | ((b4 & 0xFFL) << 32) |
((b5 & 0xFFL) << 24) | ((b6 & 0xFFL) << 16) |
((b7 & 0xFFL) << 8) | (b8 & 0xFFL);
}
public boolean hasIntAny() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|| next == Tag.INT32 || next == Tag.INT64;
}
public long readIntAny() throws IOException {
if(!hasIntAny()) throw new FormatException();
if(next >= 0) return readUint7();
if(next == Tag.INT8) return readInt8();
if(next == Tag.INT16) return readInt16();
if(next == Tag.INT32) return readInt32();
if(next == Tag.INT64) return readInt64();
throw new IllegalStateException();
}
public boolean hasFloat32() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.FLOAT32;
}
public float readFloat32() throws IOException {
if(!hasFloat32()) throw new FormatException();
return Float.intBitsToFloat(readInt32Bits());
}
public boolean hasFloat64() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.FLOAT64;
}
public double readFloat64() throws IOException {
if(!hasFloat64()) throw new FormatException();
return Double.longBitsToDouble(readInt64Bits());
}
public boolean hasUtf8() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.UTF8;
}
public String readUtf8() throws IOException {
return readUtf8(Integer.MAX_VALUE);
}
public String readUtf8(int maxLength) throws IOException {
if(!hasUtf8()) throw new FormatException();
readNext(false);
long l = readIntAny();
if(l < 0 || l > maxLength) throw new FormatException();
int length = (int) l;
if(length == 0) return "";
if(stringBuffer == null || stringBuffer.length < length)
stringBuffer = new byte[length];
stringBuffer[0] = next;
int offset = 1, read = 0;
while(offset < length && read != -1) {
read = in.read(stringBuffer, offset, length - offset);
if(read != -1) offset += read;
}
if(offset < length) throw new FormatException();
String s = new String(stringBuffer, 0, length, "UTF-8");
if(length >= TOO_LARGE_TO_KEEP) stringBuffer = null;
readNext(true);
return s;
}
public boolean hasRaw() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.RAW;
}
public byte[] readRaw() throws IOException {
return readRaw(Integer.MAX_VALUE);
}
public byte[] readRaw(int maxLength) throws IOException {
if(!hasRaw()) throw new FormatException();
readNext(false);
long l = readIntAny();
if(l < 0 || l > maxLength) throw new FormatException();
int length = (int) l;
if(length == 0) return new byte[] {};
byte[] b = new byte[length];
b[0] = next;
int offset = 1, read = 0;
while(offset < length && read != -1) {
read = in.read(b, offset, length - offset);
if(read != -1) offset += read;
}
if(offset < length) throw new FormatException();
readNext(true);
return b;
}
public boolean hasList(boolean definite) throws IOException {
if(!started) readNext(true);
if(eof) return false;
if(definite) return next == Tag.LIST_DEF;
else return next == Tag.LIST_INDEF;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<?> readList(boolean definite) throws IOException {
if(!hasList(definite)) throw new FormatException();
readNext(false);
List list = new ArrayList();
if(definite) {
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
int length = (int) l;
for(int i = 0; i < length; i++) list.add(readObject());
} else {
while(!hasEnd()) list.add(readObject());
readEnd();
}
return list;
}
private boolean hasEnd() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.END;
}
private void readEnd() throws IOException {
if(!hasEnd()) throw new FormatException();
readNext(true);
}
private Object readObject() throws IOException {
if(!started) throw new IllegalStateException();
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(hasUtf8()) return readUtf8();
if(hasRaw()) return new RawImpl(readRaw());
if(hasList()) return readList();
if(hasMap()) return readMap();
if(hasNull()) {
readNull();
return null;
}
throw new IllegalStateException();
}
public boolean hasList() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.LIST_DEF || next == Tag.LIST_INDEF;
}
public List<?> readList() throws IOException {
if(hasList(true)) return readList(true);
if(hasList(false)) return readList(false);
throw new FormatException();
}
public boolean hasMap(boolean definite) throws IOException {
if(!started) readNext(true);
if(eof) return false;
if(definite) return next == Tag.MAP_DEF;
else return next == Tag.MAP_INDEF;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Map<?, ?> readMap(boolean definite) throws IOException {
if(!hasMap(definite)) throw new FormatException();
readNext(false);
Map m = new HashMap();
if(definite) {
long l = readIntAny();
if(l < 0 || l > Integer.MAX_VALUE) throw new FormatException();
int length = (int) l;
for(int i = 0; i < length; i++) m.put(readObject(), readObject());
} else {
while(!hasEnd()) m.put(readObject(), readObject());
readEnd();
}
return m;
}
public boolean hasMap() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.MAP_DEF || next == Tag.MAP_INDEF;
}
public Map<?, ?> readMap() throws IOException {
if(hasMap(true)) return readMap(true);
if(hasMap(false)) return readMap(false);
throw new FormatException();
}
public boolean hasNull() throws IOException {
if(!started) readNext(true);
if(eof) return false;
return next == Tag.NULL;
}
public void readNull() throws IOException {
if(!hasNull()) throw new FormatException();
readNext(true);
}
}

View File

@@ -0,0 +1,13 @@
package net.sf.briar.serial;
import java.io.OutputStream;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
public class WriterFactoryImpl implements WriterFactory {
public Writer createWriter(OutputStream out) {
return new WriterImpl(out);
}
}

View File

@@ -0,0 +1,166 @@
package net.sf.briar.serial;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.sf.briar.api.serial.Raw;
import net.sf.briar.api.serial.Tag;
import net.sf.briar.api.serial.Writer;
public class WriterImpl implements Writer {
private final OutputStream out;
public WriterImpl(OutputStream out) {
this.out = out;
}
public void writeBoolean(boolean b) throws IOException {
if(b) out.write(Tag.TRUE);
else out.write(Tag.FALSE);
}
public void writeUint7(byte b) throws IOException {
if(b < 0) throw new IllegalArgumentException();
out.write(b);
}
public void writeInt8(byte b) throws IOException {
out.write(Tag.INT8);
out.write(b);
}
public void writeInt16(short s) throws IOException {
out.write(Tag.INT16);
out.write((byte) (s >> 8));
out.write((byte) ((s << 8) >> 8));
}
public void writeInt32(int i) throws IOException {
out.write(Tag.INT32);
writeInt32Bits(i);
}
private void writeInt32Bits(int i) throws IOException {
out.write((byte) (i >> 24));
out.write((byte) ((i << 8) >> 24));
out.write((byte) ((i << 16) >> 24));
out.write((byte) ((i << 24) >> 24));
}
public void writeInt64(long l) throws IOException {
out.write(Tag.INT64);
writeInt64Bits(l);
}
private void writeInt64Bits(long l) throws IOException {
out.write((byte) (l >> 56));
out.write((byte) ((l << 8) >> 56));
out.write((byte) ((l << 16) >> 56));
out.write((byte) ((l << 24) >> 56));
out.write((byte) ((l << 32) >> 56));
out.write((byte) ((l << 40) >> 56));
out.write((byte) ((l << 48) >> 56));
out.write((byte) ((l << 56) >> 56));
}
public void writeIntAny(long l) throws IOException {
if(l >= 0 && l <= Byte.MAX_VALUE)
writeUint7((byte) l);
else if(l >= Byte.MIN_VALUE && l <= Byte.MAX_VALUE)
writeInt8((byte) l);
else if(l >= Short.MIN_VALUE && l <= Short.MAX_VALUE)
writeInt16((short) l);
else if(l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE)
writeInt32((int) l);
else writeInt64(l);
}
public void writeFloat32(float f) throws IOException {
out.write(Tag.FLOAT32);
writeInt32Bits(Float.floatToRawIntBits(f));
}
public void writeFloat64(double d) throws IOException {
out.write(Tag.FLOAT64);
writeInt64Bits(Double.doubleToRawLongBits(d));
}
public void writeUtf8(String s) throws IOException {
out.write(Tag.UTF8);
byte[] b = s.getBytes("UTF-8");
writeIntAny(b.length);
out.write(b);
}
public void writeRaw(byte[] b) throws IOException {
out.write(Tag.RAW);
writeIntAny(b.length);
out.write(b);
}
public void writeRaw(Raw r) throws IOException {
writeRaw(r.getBytes());
}
public void writeList(List<?> l, boolean definite) throws IOException {
if(definite) {
out.write(Tag.LIST_DEF);
writeIntAny(l.size());
for(Object o : l) writeObject(o);
} else {
out.write(Tag.LIST_INDEF);
for(Object o : l) writeObject(o);
out.write(Tag.END);
}
}
private void writeObject(Object o) throws IOException {
if(o instanceof Boolean) writeBoolean((Boolean) o);
else if(o instanceof Byte) writeIntAny((Byte) o);
else if(o instanceof Short) writeIntAny((Short) o);
else if(o instanceof Integer) writeIntAny((Integer) o);
else if(o instanceof Long) writeIntAny((Long) o);
else if(o instanceof Float) writeFloat32((Float) o);
else if(o instanceof Double) writeFloat64((Double) o);
else if(o instanceof String) writeUtf8((String) o);
else if(o instanceof Raw) writeRaw((Raw) o);
else if(o instanceof List) writeList((List<?>) o);
else if(o instanceof Map) writeMap((Map<?, ?>) o);
else if(o == null) writeNull();
else throw new IllegalStateException();
}
public void writeList(List<?> l) throws IOException {
writeList(l, true);
}
public void writeMap(Map<?, ?> m, boolean definite) throws IOException {
if(definite) {
out.write(Tag.MAP_DEF);
writeIntAny(m.size());
for(Entry<?, ?> e : m.entrySet()) {
writeObject(e.getKey());
writeObject(e.getValue());
}
} else {
out.write(Tag.MAP_INDEF);
for(Entry<?, ?> e : m.entrySet()) {
writeObject(e.getKey());
writeObject(e.getValue());
}
out.write(Tag.END);
}
}
public void writeMap(Map<?, ?> m) throws IOException {
writeMap(m, true);
}
public void writeNull() throws IOException {
out.write(Tag.NULL);
}
}