Expose serialisation overhead without breaking encapsulation.

This commit is contained in:
akwizgran
2011-09-21 13:16:58 +01:00
parent 95c3fb4fed
commit 3e60233ae0
13 changed files with 130 additions and 83 deletions

View File

@@ -5,9 +5,6 @@ import java.util.Collection;
/** A packet acknowledging receipt of one or more batches. */
public interface Ack {
/** The maximum number of batch IDs per ack. */
static final int MAX_IDS_PER_ACK = 29959;
/** Returns the IDs of the acknowledged batches. */
Collection<BatchId> getBatchIds();
}

View File

@@ -5,9 +5,6 @@ import java.util.Collection;
/** A packet offering the recipient some messages. */
public interface Offer {
/** The maximum number of message IDs per offer. */
static final int MAX_IDS_PER_OFFER = 29959;
/** Returns the message IDs contained in the offer. */
Collection<MessageId> getMessageIds();
}

View File

@@ -6,7 +6,7 @@ import java.util.Map;
public interface SubscriptionUpdate {
/** The maximum number of subscriptions per update. */
static final int MAX_SUBS_PER_UPDATE = 6393;
static final int MAX_SUBS_PER_UPDATE = 6000;
/** Returns the subscriptions contained in the update. */
Map<Group, Long> getSubscriptions();

View File

@@ -0,0 +1,12 @@
package net.sf.briar.api.serial;
public interface SerialComponent {
int getSerialisedListEndLength();
int getSerialisedListStartLength();
int getSerialisedUniqueIdLength(int id);
int getSerialisedUserDefinedIdLength(int id);
}

View File

@@ -3,7 +3,6 @@ package net.sf.briar.protocol;
import java.io.IOException;
import java.util.Collection;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.ProtocolConstants;
@@ -31,7 +30,6 @@ class AckReader implements ObjectReader<Ack> {
r.readUserDefinedId(Types.ACK);
r.addObjectReader(Types.BATCH_ID, batchIdReader);
Collection<BatchId> batches = r.readList(BatchId.class);
if(batches.size() > Ack.MAX_IDS_PER_ACK) throw new FormatException();
r.removeObjectReader(Types.BATCH_ID);
r.removeConsumer(counting);
// Build and return the ack

View File

@@ -3,7 +3,6 @@ package net.sf.briar.protocol;
import java.io.IOException;
import java.util.Collection;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolConstants;
@@ -32,8 +31,6 @@ class OfferReader implements ObjectReader<Offer> {
r.readUserDefinedId(Types.OFFER);
r.addObjectReader(Types.MESSAGE_ID, messageIdReader);
Collection<MessageId> messages = r.readList(MessageId.class);
if(messages.size() > Offer.MAX_IDS_PER_OFFER)
throw new FormatException();
r.removeObjectReader(Types.MESSAGE_ID);
r.removeConsumer(counting);
// Build and return the offer

View File

@@ -3,46 +3,53 @@ package net.sf.briar.protocol.writers;
import java.io.IOException;
import java.io.OutputStream;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.ProtocolConstants;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
class AckWriterImpl implements AckWriter {
private final OutputStream out;
private final SerialComponent serial;
private final Writer w;
private boolean started = false;
private int idsWritten = 0;
private int capacity = ProtocolConstants.MAX_PACKET_LENGTH; // FIXME
AckWriterImpl(OutputStream out, WriterFactory writerFactory) {
AckWriterImpl(OutputStream out, SerialComponent serial,
WriterFactory writerFactory) {
this.out = out;
this.serial = serial;
w = writerFactory.createWriter(out);
}
public boolean writeBatchId(BatchId b) throws IOException {
if(!started) {
w.writeUserDefinedTag(Types.ACK);
w.writeListStart();
started = true;
}
if(idsWritten >= Ack.MAX_IDS_PER_ACK) return false;
if(!started) start();
int length = serial.getSerialisedUniqueIdLength(Types.BATCH_ID);
if(capacity < length + serial.getSerialisedListEndLength())
return false;
b.writeTo(w);
idsWritten++;
capacity -= length;
return true;
}
public void finish() throws IOException {
if(!started) {
w.writeUserDefinedTag(Types.ACK);
w.writeListStart();
started = true;
}
if(!started) start();
w.writeListEnd();
out.flush();
capacity = ProtocolConstants.MAX_PACKET_LENGTH; // FIXME
started = false;
}
private void start() throws IOException {
w.writeUserDefinedTag(Types.ACK);
capacity -= serial.getSerialisedUserDefinedIdLength(Types.ACK);
w.writeListStart();
capacity -= serial.getSerialisedListStartLength();
started = true;
}
}

View File

@@ -9,50 +9,53 @@ import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.ProtocolConstants;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.writers.BatchWriter;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
class BatchWriterImpl implements BatchWriter {
private final DigestOutputStream out;
private final SerialComponent serial;
private final Writer w;
private final MessageDigest messageDigest;
private boolean started = false;
private int capacity = ProtocolConstants.MAX_PACKET_LENGTH; // FIXME
BatchWriterImpl(OutputStream out, WriterFactory writerFactory,
MessageDigest messageDigest) {
BatchWriterImpl(OutputStream out, SerialComponent serial,
WriterFactory writerFactory, MessageDigest messageDigest) {
this.out = new DigestOutputStream(out, messageDigest);
this.serial = serial;
w = writerFactory.createWriter(this.out);
this.messageDigest = messageDigest;
}
public boolean writeMessage(byte[] message) throws IOException {
if(!started) {
messageDigest.reset();
w.writeUserDefinedTag(Types.BATCH);
w.writeListStart();
started = true;
}
// Allow one byte for the list end tag
int capacity = ProtocolConstants.MAX_PACKET_LENGTH
- (int) w.getBytesWritten() - 1;
if(capacity < message.length) return false;
// Bypass the writer and write each raw message directly
if(!started) start();
if(capacity < message.length + serial.getSerialisedListEndLength())
return false;
// Bypass the writer and write the raw message directly
out.write(message);
capacity -= message.length;
return true;
}
public BatchId finish() throws IOException {
if(!started) {
messageDigest.reset();
w.writeUserDefinedTag(Types.BATCH);
w.writeListStart();
started = true;
}
if(!started) start();
w.writeListEnd();
out.flush();
capacity = ProtocolConstants.MAX_PACKET_LENGTH; // FIXME
started = false;
return new BatchId(messageDigest.digest());
}
private void start() throws IOException {
messageDigest.reset();
w.writeUserDefinedTag(Types.BATCH);
capacity -= serial.getSerialisedUserDefinedIdLength(Types.BATCH);
w.writeListStart();
capacity -= serial.getSerialisedListStartLength();
started = true;
}
}

View File

@@ -4,45 +4,50 @@ import java.io.IOException;
import java.io.OutputStream;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolConstants;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.writers.OfferWriter;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
class OfferWriterImpl implements OfferWriter {
private final OutputStream out;
private final SerialComponent serial;
private final Writer w;
private boolean started = false;
private int idsWritten = 0;
private int capacity = ProtocolConstants.MAX_PACKET_LENGTH; // FIXME
OfferWriterImpl(OutputStream out, WriterFactory writerFactory) {
OfferWriterImpl(OutputStream out, SerialComponent serial,
WriterFactory writerFactory) {
this.out = out;
this.serial = serial;
w = writerFactory.createWriter(out);
}
public boolean writeMessageId(MessageId m) throws IOException {
if(!started) {
w.writeUserDefinedTag(Types.OFFER);
w.writeListStart();
started = true;
}
if(idsWritten >= Offer.MAX_IDS_PER_OFFER) return false;
if(!started) start();
int length = serial.getSerialisedUniqueIdLength(Types.MESSAGE_ID);
if(capacity < length + serial.getSerialisedListEndLength())
return false;
m.writeTo(w);
idsWritten++;
capacity -= length;
return true;
}
public void finish() throws IOException {
if(!started) {
w.writeUserDefinedTag(Types.OFFER);
w.writeListStart();
started = true;
}
if(!started) start();
w.writeListEnd();
out.flush();
capacity = ProtocolConstants.MAX_PACKET_LENGTH; // FIXME
started = false;
}
private void start() throws IOException {
w.writeUserDefinedTag(Types.OFFER);
w.writeListStart();
started = true;
}
}

View File

@@ -11,6 +11,7 @@ import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.protocol.writers.RequestWriter;
import net.sf.briar.api.protocol.writers.SubscriptionWriter;
import net.sf.briar.api.protocol.writers.TransportWriter;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.WriterFactory;
import com.google.inject.Inject;
@@ -18,25 +19,27 @@ import com.google.inject.Inject;
class ProtocolWriterFactoryImpl implements ProtocolWriterFactory {
private final MessageDigest messageDigest;
private final SerialComponent serial;
private final WriterFactory writerFactory;
@Inject
ProtocolWriterFactoryImpl(CryptoComponent crypto,
WriterFactory writerFactory) {
SerialComponent serial, WriterFactory writerFactory) {
messageDigest = crypto.getMessageDigest();
this.serial = serial;
this.writerFactory = writerFactory;
}
public AckWriter createAckWriter(OutputStream out) {
return new AckWriterImpl(out, writerFactory);
return new AckWriterImpl(out, serial, writerFactory);
}
public BatchWriter createBatchWriter(OutputStream out) {
return new BatchWriterImpl(out, writerFactory, messageDigest);
return new BatchWriterImpl(out, serial, writerFactory, messageDigest);
}
public OfferWriter createOfferWriter(OutputStream out) {
return new OfferWriterImpl(out, writerFactory);
return new OfferWriterImpl(out, serial, writerFactory);
}
public RequestWriter createRequestWriter(OutputStream out) {

View File

@@ -0,0 +1,34 @@
package net.sf.briar.serial;
import net.sf.briar.api.protocol.UniqueId;
import net.sf.briar.api.serial.SerialComponent;
class SerialComponentImpl implements SerialComponent {
public int getSerialisedListEndLength() {
return 1;
}
public int getSerialisedListStartLength() {
return 1;
}
public int getSerialisedUniqueIdLength(int id) {
// User-defined ID, BYTES tag, length spec, bytes
return getSerialisedUserDefinedIdLength(id) + 1 +
getSerialisedLengthSpecLength(UniqueId.LENGTH) +
UniqueId.LENGTH;
}
private int getSerialisedLengthSpecLength(int length) {
assert length >= 0;
if(length < 128) return 1; // Uint7
if(length < Short.MAX_VALUE) return 3; // Int16
return 5; // Int32
}
public int getSerialisedUserDefinedIdLength(int id) {
assert id >= 0 && id <= 255;
return id < 32 ? 1 : 2;
}
}

View File

@@ -1,15 +1,19 @@
package net.sf.briar.serial;
import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.WriterFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
public class SerialModule extends AbstractModule {
@Override
protected void configure() {
bind(ReaderFactory.class).to(ReaderFactoryImpl.class);
bind(SerialComponent.class).to(SerialComponentImpl.class).in(
Singleton.class);
bind(WriterFactory.class).to(WriterFactoryImpl.class);
}
}

View File

@@ -8,7 +8,6 @@ import java.util.Map;
import junit.framework.TestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.BatchId;
@@ -17,7 +16,6 @@ import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageEncoder;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolConstants;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate;
@@ -27,6 +25,7 @@ import net.sf.briar.api.protocol.writers.BatchWriter;
import net.sf.briar.api.protocol.writers.OfferWriter;
import net.sf.briar.api.protocol.writers.SubscriptionWriter;
import net.sf.briar.api.protocol.writers.TransportWriter;
import net.sf.briar.api.serial.SerialComponent;
import net.sf.briar.api.serial.WriterFactory;
import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.protocol.ProtocolModule;
@@ -41,6 +40,7 @@ public class ConstantsTest extends TestCase {
private final WriterFactory writerFactory;
private final CryptoComponent crypto;
private final SerialComponent serial;
private final GroupFactory groupFactory;
private final AuthorFactory authorFactory;
private final MessageEncoder messageEncoder;
@@ -51,6 +51,7 @@ public class ConstantsTest extends TestCase {
new ProtocolModule(), new SerialModule());
writerFactory = i.getInstance(WriterFactory.class);
crypto = i.getInstance(CryptoComponent.class);
serial = i.getInstance(SerialComponent.class);
groupFactory = i.getInstance(GroupFactory.class);
authorFactory = i.getInstance(AuthorFactory.class);
messageEncoder = i.getInstance(MessageEncoder.class);
@@ -61,15 +62,10 @@ public class ConstantsTest extends TestCase {
// Create an ack with the maximum number of batch IDs
ByteArrayOutputStream out = new ByteArrayOutputStream(
ProtocolConstants.MAX_PACKET_LENGTH);
AckWriter a = new AckWriterImpl(out, writerFactory);
for(int i = 0; i < Ack.MAX_IDS_PER_ACK; i++) {
assertTrue(a.writeBatchId(new BatchId(TestUtils.getRandomId())));
}
// Check that no more batch IDs can be written
assertFalse(a.writeBatchId(new BatchId(TestUtils.getRandomId())));
AckWriter a = new AckWriterImpl(out, serial, writerFactory);
while(a.writeBatchId(new BatchId(TestUtils.getRandomId())));
a.finish();
// Check the size of the serialised ack
assertTrue(out.size() > UniqueId.LENGTH * Ack.MAX_IDS_PER_ACK);
assertTrue(out.size() <= ProtocolConstants.MAX_PACKET_LENGTH);
}
@@ -92,7 +88,7 @@ public class ConstantsTest extends TestCase {
// Add the message to a batch
ByteArrayOutputStream out = new ByteArrayOutputStream(
ProtocolConstants.MAX_PACKET_LENGTH);
BatchWriter b = new BatchWriterImpl(out, writerFactory,
BatchWriter b = new BatchWriterImpl(out, serial, writerFactory,
crypto.getMessageDigest());
b.writeMessage(message.getBytes());
b.finish();
@@ -108,16 +104,10 @@ public class ConstantsTest extends TestCase {
// Create an offer with the maximum number of message IDs
ByteArrayOutputStream out = new ByteArrayOutputStream(
ProtocolConstants.MAX_PACKET_LENGTH);
OfferWriter o = new OfferWriterImpl(out, writerFactory);
for(int i = 0; i < Offer.MAX_IDS_PER_OFFER; i++) {
assertTrue(o.writeMessageId(new MessageId(
TestUtils.getRandomId())));
}
// Check that no more message IDs can be written
assertFalse(o.writeMessageId(new MessageId(TestUtils.getRandomId())));
OfferWriter o = new OfferWriterImpl(out, serial, writerFactory);
while(o.writeMessageId(new MessageId(TestUtils.getRandomId())));
o.finish();
// Check the size of the serialised offer
assertTrue(out.size() > UniqueId.LENGTH * Offer.MAX_IDS_PER_OFFER);
assertTrue(out.size() <= ProtocolConstants.MAX_PACKET_LENGTH);
}