From 0504a2d6fd6851abe78ff9e2f9011c1848ee8ee2 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Fri, 12 Aug 2011 21:55:22 +0200
Subject: [PATCH] Implemented PacketReader, renamed
 Packet{Reader,Writer}Factory in the protocol component to
 Protocol{Reader,Writer}Factory.

---
 .../writers/ProtocolReaderFactory.java        |  26 ++++
 ...actory.java => ProtocolWriterFactory.java} |   2 +-
 .../briar/api/transport/ConnectionWindow.java |   4 +-
 .../api/transport/PacketWriterFactory.java    |   4 +-
 ...pl.java => ProtocolWriterFactoryImpl.java} |   6 +-
 .../briar/protocol/writers/WritersModule.java |   4 +-
 .../transport/ConnectionRecogniserImpl.java   |  15 +--
 .../briar/transport/ConnectionWindowImpl.java |  20 +--
 .../sf/briar/transport/PacketReaderImpl.java  | 126 ++++++++++++++++++
 .../transport/PacketWriterFactoryImpl.java    |  10 +-
 .../sf/briar/transport/PacketWriterImpl.java  |  31 ++---
 .../net/sf/briar/transport/TagDecoder.java    |  33 +++++
 .../net/sf/briar/transport/TagEncoder.java    |  12 +-
 test/build.xml                                |   1 +
 .../sf/briar/protocol/FileReadWriteTest.java  |  18 +--
 .../sf/briar/transport/TagDecoderTest.java    |  30 +++++
 .../sf/briar/transport/TagEncoderTest.java    |   4 +-
 17 files changed, 281 insertions(+), 65 deletions(-)
 create mode 100644 api/net/sf/briar/api/protocol/writers/ProtocolReaderFactory.java
 rename api/net/sf/briar/api/protocol/writers/{PacketWriterFactory.java => ProtocolWriterFactory.java} (90%)
 rename components/net/sf/briar/protocol/writers/{PacketWriterFactoryImpl.java => ProtocolWriterFactoryImpl.java} (89%)
 create mode 100644 components/net/sf/briar/transport/PacketReaderImpl.java
 create mode 100644 components/net/sf/briar/transport/TagDecoder.java
 create mode 100644 test/net/sf/briar/transport/TagDecoderTest.java

diff --git a/api/net/sf/briar/api/protocol/writers/ProtocolReaderFactory.java b/api/net/sf/briar/api/protocol/writers/ProtocolReaderFactory.java
new file mode 100644
index 0000000000..5a0609c180
--- /dev/null
+++ b/api/net/sf/briar/api/protocol/writers/ProtocolReaderFactory.java
@@ -0,0 +1,26 @@
+package net.sf.briar.api.protocol.writers;
+
+import java.io.InputStream;
+
+import net.sf.briar.api.protocol.Ack;
+import net.sf.briar.api.protocol.Batch;
+import net.sf.briar.api.protocol.Offer;
+import net.sf.briar.api.protocol.Request;
+import net.sf.briar.api.protocol.SubscriptionUpdate;
+import net.sf.briar.api.protocol.TransportUpdate;
+import net.sf.briar.api.serial.ObjectReader;
+
+public interface ProtocolReaderFactory {
+
+	ObjectReader<Ack> createAckReader(InputStream in);
+
+	ObjectReader<Batch> createBatchReader(InputStream in);
+
+	ObjectReader<Offer> createOfferReader(InputStream in);
+
+	ObjectReader<Request> createRequestReader(InputStream in);
+
+	ObjectReader<SubscriptionUpdate> createSubscriptionReader(InputStream in);
+
+	ObjectReader<TransportUpdate> createTransportReader(InputStream in);
+}
diff --git a/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java b/api/net/sf/briar/api/protocol/writers/ProtocolWriterFactory.java
similarity index 90%
rename from api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
rename to api/net/sf/briar/api/protocol/writers/ProtocolWriterFactory.java
index 71699b8ad3..480ac550b4 100644
--- a/api/net/sf/briar/api/protocol/writers/PacketWriterFactory.java
+++ b/api/net/sf/briar/api/protocol/writers/ProtocolWriterFactory.java
@@ -2,7 +2,7 @@ package net.sf.briar.api.protocol.writers;
 
 import java.io.OutputStream;
 
-public interface PacketWriterFactory {
+public interface ProtocolWriterFactory {
 
 	AckWriter createAckWriter(OutputStream out);
 
diff --git a/api/net/sf/briar/api/transport/ConnectionWindow.java b/api/net/sf/briar/api/transport/ConnectionWindow.java
index b8e39cb8e9..a7e8eb98b2 100644
--- a/api/net/sf/briar/api/transport/ConnectionWindow.java
+++ b/api/net/sf/briar/api/transport/ConnectionWindow.java
@@ -8,9 +8,9 @@ public interface ConnectionWindow {
 
 	int getBitmap();
 
-	boolean isSeen(long connectionNumber);
+	boolean isSeen(long connection);
 
-	void setSeen(long connectionNumber);
+	void setSeen(long connection);
 
 	Collection<Long> getUnseenConnectionNumbers();
 }
diff --git a/api/net/sf/briar/api/transport/PacketWriterFactory.java b/api/net/sf/briar/api/transport/PacketWriterFactory.java
index fdb79b0ebd..f820576b04 100644
--- a/api/net/sf/briar/api/transport/PacketWriterFactory.java
+++ b/api/net/sf/briar/api/transport/PacketWriterFactory.java
@@ -6,7 +6,7 @@ import javax.crypto.SecretKey;
 
 public interface PacketWriterFactory {
 
-	PacketWriter createPacketWriter(OutputStream out, int transportIdentifier,
-			long connectionNumber, SecretKey macKey, SecretKey tagKey,
+	PacketWriter createPacketWriter(OutputStream out, int transportId,
+			long connection, SecretKey macKey, SecretKey tagKey,
 			SecretKey packetKey);
 }
diff --git a/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java b/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
similarity index 89%
rename from components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
rename to components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
index 5daaf2361a..2599940367 100644
--- a/components/net/sf/briar/protocol/writers/PacketWriterFactoryImpl.java
+++ b/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
@@ -7,7 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.protocol.writers.OfferWriter;
-import net.sf.briar.api.protocol.writers.PacketWriterFactory;
+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;
@@ -15,13 +15,13 @@ import net.sf.briar.api.serial.WriterFactory;
 
 import com.google.inject.Inject;
 
-class PacketWriterFactoryImpl implements PacketWriterFactory {
+class ProtocolWriterFactoryImpl implements ProtocolWriterFactory {
 
 	private final MessageDigest messageDigest;
 	private final WriterFactory writerFactory;
 
 	@Inject
-	PacketWriterFactoryImpl(CryptoComponent crypto,
+	ProtocolWriterFactoryImpl(CryptoComponent crypto,
 			WriterFactory writerFactory) {
 		messageDigest = crypto.getMessageDigest();
 		this.writerFactory = writerFactory;
diff --git a/components/net/sf/briar/protocol/writers/WritersModule.java b/components/net/sf/briar/protocol/writers/WritersModule.java
index a8a51fd9df..af8546ec6b 100644
--- a/components/net/sf/briar/protocol/writers/WritersModule.java
+++ b/components/net/sf/briar/protocol/writers/WritersModule.java
@@ -1,6 +1,6 @@
 package net.sf.briar.protocol.writers;
 
-import net.sf.briar.api.protocol.writers.PacketWriterFactory;
+import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
 
 import com.google.inject.AbstractModule;
 
@@ -8,6 +8,6 @@ public class WritersModule extends AbstractModule {
 
 	@Override
 	protected void configure() {
-		bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
+		bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class);
 	}
 }
diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
index 605a0ffbe1..4fcf5deff1 100644
--- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
+++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
@@ -76,9 +76,8 @@ DatabaseListener {
 		initialised = true;
 	}
 
-	private synchronized byte[] calculateTag(ContactId c,
-			long connectionNumber) {
-		byte[] tag = TagEncoder.encodeTag(transportId, connectionNumber, 0L);
+	private synchronized byte[] calculateTag(ContactId c, long connection) {
+		byte[] tag = TagEncoder.encodeTag(transportId, connection, 0L);
 		Cipher cipher = contactToCipher.get(c);
 		assert cipher != null;
 		try {
@@ -97,25 +96,25 @@ DatabaseListener {
 		if(!initialised) initialise();
 		Bytes b = new Bytes(tag);
 		ContactId contactId = tagToContact.remove(b);
-		Long connectionNumber = tagToConnectionNumber.remove(b);
-		assert (contactId == null) == (connectionNumber == null);
+		Long connection = tagToConnectionNumber.remove(b);
+		assert (contactId == null) == (connection == null);
 		if(contactId == null) return null;
 		// The tag was expected - update and save the connection window
 		ConnectionWindow w = contactToWindow.get(contactId);
 		assert w != null;
-		w.setSeen(connectionNumber);
+		w.setSeen(connection);
 		db.setConnectionWindow(contactId, transportId, w);
 		// Update the set of expected tags
 		Map<Long, Bytes> oldTags = contactToTags.remove(contactId);
 		assert oldTags != null;
-		assert oldTags.containsKey(connectionNumber);
+		assert oldTags.containsKey(connection);
 		Map<Long, Bytes> newTags = new HashMap<Long, Bytes>();
 		for(Long unseen : w.getUnseenConnectionNumbers()) {
 			Bytes expectedTag = oldTags.get(unseen);
 			if(expectedTag == null) {
 				expectedTag = new Bytes(calculateTag(contactId, unseen));
 				tagToContact.put(expectedTag, contactId);
-				tagToConnectionNumber.put(expectedTag, connectionNumber);
+				tagToConnectionNumber.put(expectedTag, connection);
 			}
 			newTags.put(unseen, expectedTag);
 		}
diff --git a/components/net/sf/briar/transport/ConnectionWindowImpl.java b/components/net/sf/briar/transport/ConnectionWindowImpl.java
index af2c294870..6db2b6c41a 100644
--- a/components/net/sf/briar/transport/ConnectionWindowImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWindowImpl.java
@@ -23,29 +23,29 @@ class ConnectionWindowImpl implements ConnectionWindow {
 		return bitmap;
 	}
 
-	public boolean isSeen(long connectionNumber) {
-		int offset = getOffset(connectionNumber);
+	public boolean isSeen(long connection) {
+		int offset = getOffset(connection);
 		int mask = 0x80000000 >>> offset;
 		return (bitmap & mask) != 0;
 	}
 
-	private int getOffset(long connectionNumber) {
-		if(connectionNumber < 0L) throw new IllegalArgumentException();
-		if(connectionNumber > Constants.MAX_32_BIT_UNSIGNED)
+	private int getOffset(long connection) {
+		if(connection < 0L) throw new IllegalArgumentException();
+		if(connection > Constants.MAX_32_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
-		int offset = (int) (connectionNumber - centre) + 16;
+		int offset = (int) (connection - centre) + 16;
 		if(offset < 0 || offset > 31) throw new IllegalArgumentException();
 		return offset;
 	}
 
-	public void setSeen(long connectionNumber) {
-		int offset = getOffset(connectionNumber);
+	public void setSeen(long connection) {
+		int offset = getOffset(connection);
 		int mask = 0x80000000 >>> offset;
 		if((bitmap & mask) != 0) throw new IllegalArgumentException();
 		bitmap |= mask;
 		// If the new connection number is above the centre, slide the window
-		if(connectionNumber >= centre) {
-			centre = connectionNumber + 1;
+		if(connection >= centre) {
+			centre = connection + 1;
 			bitmap <<= offset - 16 + 1;
 		}
 	}
diff --git a/components/net/sf/briar/transport/PacketReaderImpl.java b/components/net/sf/briar/transport/PacketReaderImpl.java
new file mode 100644
index 0000000000..3ad2eb0f11
--- /dev/null
+++ b/components/net/sf/briar/transport/PacketReaderImpl.java
@@ -0,0 +1,126 @@
+package net.sf.briar.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.crypto.Mac;
+
+import net.sf.briar.api.protocol.Ack;
+import net.sf.briar.api.protocol.Batch;
+import net.sf.briar.api.protocol.Offer;
+import net.sf.briar.api.protocol.Request;
+import net.sf.briar.api.protocol.SubscriptionUpdate;
+import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.TransportUpdate;
+import net.sf.briar.api.protocol.writers.ProtocolReaderFactory;
+import net.sf.briar.api.serial.Reader;
+import net.sf.briar.api.serial.ReaderFactory;
+import net.sf.briar.api.transport.PacketReader;
+
+class PacketReaderImpl implements PacketReader {
+
+	private final Reader reader;
+	private final PacketDecrypter decrypter;
+	private final Mac mac;
+	private final int transportId;
+	private final long connection;
+
+	private long packet = 0L;
+	private boolean betweenPackets = true;
+
+	PacketReaderImpl(byte[] firstTag, ReaderFactory readerFactory,
+			ProtocolReaderFactory protocol, PacketDecrypter decrypter, Mac mac,
+			int transportId, long connection) {
+		InputStream in = decrypter.getInputStream();
+		reader = readerFactory.createReader(in);
+		reader.addObjectReader(Tags.ACK, protocol.createAckReader(in));
+		reader.addObjectReader(Tags.BATCH, protocol.createBatchReader(in));
+		reader.addObjectReader(Tags.OFFER, protocol.createOfferReader(in));
+		reader.addObjectReader(Tags.REQUEST, protocol.createRequestReader(in));
+		reader.addObjectReader(Tags.SUBSCRIPTIONS,
+				protocol.createSubscriptionReader(in));
+		reader.addObjectReader(Tags.TRANSPORTS,
+				protocol.createTransportReader(in));
+		this.decrypter = decrypter;
+		this.mac = mac;
+		this.transportId = transportId;
+		this.connection = connection;
+	}
+
+	public boolean eof() throws IOException {
+		return reader.eof();
+	}
+
+	public boolean hasAck() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.hasUserDefined(Tags.ACK);
+	}
+
+	private void readTag() throws IOException {
+		assert betweenPackets;
+		if(packet > Constants.MAX_32_BIT_UNSIGNED)
+			throw new IllegalStateException();
+		byte[] tag = decrypter.readTag();
+		if(!TagDecoder.decodeTag(tag, transportId, connection, packet))
+			throw new IOException();
+		mac.update(tag);
+		packet++;
+		betweenPackets = false;
+	}
+
+	public Ack readAck() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.readUserDefined(Tags.ACK, Ack.class);
+	}
+
+	public boolean hasBatch() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.hasUserDefined(Tags.BATCH);
+	}
+
+	public Batch readBatch() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.readUserDefined(Tags.BATCH, Batch.class);
+	}
+
+	public boolean hasOffer() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.hasUserDefined(Tags.OFFER);
+	}
+
+	public Offer readOffer() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.readUserDefined(Tags.OFFER, Offer.class);
+	}
+
+	public boolean hasRequest() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.hasUserDefined(Tags.REQUEST);
+	}
+
+	public Request readRequest() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.readUserDefined(Tags.REQUEST, Request.class);
+	}
+
+	public boolean hasSubscriptionUpdate() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.hasUserDefined(Tags.SUBSCRIPTIONS);
+	}
+
+	public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.readUserDefined(Tags.SUBSCRIPTIONS,
+				SubscriptionUpdate.class);
+	}
+
+	public boolean hasTransportUpdate() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.hasUserDefined(Tags.TRANSPORTS);
+	}
+
+	public TransportUpdate readTransportUpdate() throws IOException {
+		if(betweenPackets) readTag();
+		return reader.readUserDefined(Tags.TRANSPORTS, TransportUpdate.class);
+	}
+}
diff --git a/components/net/sf/briar/transport/PacketWriterFactoryImpl.java b/components/net/sf/briar/transport/PacketWriterFactoryImpl.java
index 92ea6ded20..2c4a48a025 100644
--- a/components/net/sf/briar/transport/PacketWriterFactoryImpl.java
+++ b/components/net/sf/briar/transport/PacketWriterFactoryImpl.java
@@ -21,9 +21,9 @@ class PacketWriterFactoryImpl implements PacketWriterFactory {
 		this.crypto = crypto;
 	}
 
-	public PacketWriter createPacketWriter(OutputStream out,
-			int transportIdentifier, long connectionNumber, SecretKey macKey,
-			SecretKey tagKey, SecretKey packetKey) {
+	public PacketWriter createPacketWriter(OutputStream out, int transportId,
+			long connection, SecretKey macKey, SecretKey tagKey,
+			SecretKey packetKey) {
 		Mac mac = crypto.getMac();
 		try {
 			mac.init(macKey);
@@ -32,7 +32,7 @@ class PacketWriterFactoryImpl implements PacketWriterFactory {
 		}
 		PacketEncrypter e = new PacketEncrypterImpl(out, crypto.getTagCipher(),
 				crypto.getPacketCipher(), tagKey, packetKey);
-		return new PacketWriterImpl(e, mac, transportIdentifier,
-				connectionNumber);
+		return new PacketWriterImpl(e, mac, transportId,
+				connection);
 	}
 }
diff --git a/components/net/sf/briar/transport/PacketWriterImpl.java b/components/net/sf/briar/transport/PacketWriterImpl.java
index 97c9626aa7..16c9ea8b79 100644
--- a/components/net/sf/briar/transport/PacketWriterImpl.java
+++ b/components/net/sf/briar/transport/PacketWriterImpl.java
@@ -12,25 +12,25 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
 
 	private final PacketEncrypter encrypter;
 	private final Mac mac;
-	private final int transportIdentifier;
-	private final long connectionNumber;
+	private final int transportId;
+	private final long connection;
 
-	private long packetNumber = 0L;
+	private long packet = 0L;
 	private boolean betweenPackets = true;
 
-	PacketWriterImpl(PacketEncrypter encrypter, Mac mac,
-			int transportIdentifier, long connectionNumber) {
+	PacketWriterImpl(PacketEncrypter encrypter, Mac mac, int transportId,
+			long connection) {
 		super(encrypter.getOutputStream());
 		this.encrypter = encrypter;
 		this.mac = mac;
-		if(transportIdentifier < 0) throw new IllegalArgumentException();
-		if(transportIdentifier > Constants.MAX_16_BIT_UNSIGNED)
+		if(transportId < 0) throw new IllegalArgumentException();
+		if(transportId > Constants.MAX_16_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
-		this.transportIdentifier = transportIdentifier;
-		if(connectionNumber < 0L) throw new IllegalArgumentException();
-		if(connectionNumber > Constants.MAX_32_BIT_UNSIGNED)
+		this.transportId = transportId;
+		if(connection < 0L) throw new IllegalArgumentException();
+		if(connection > Constants.MAX_32_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
-		this.connectionNumber = connectionNumber;
+		this.connection = connection;
 	}
 
 	public OutputStream getOutputStream() {
@@ -69,14 +69,15 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
 	}
 
 	private void writeTag() throws IOException {
-		if(packetNumber > Constants.MAX_32_BIT_UNSIGNED)
+		assert betweenPackets;
+		if(packet > Constants.MAX_32_BIT_UNSIGNED)
 			throw new IllegalStateException();
-		byte[] tag = TagEncoder.encodeTag(transportIdentifier, connectionNumber,
-				packetNumber);
+		byte[] tag = TagEncoder.encodeTag(transportId, connection,
+				packet);
 		// Write the tag to the encrypter and start calculating the MAC
 		encrypter.writeTag(tag);
 		mac.update(tag);
-		packetNumber++;
+		packet++;
 		betweenPackets = false;
 	}
 }
diff --git a/components/net/sf/briar/transport/TagDecoder.java b/components/net/sf/briar/transport/TagDecoder.java
new file mode 100644
index 0000000000..01b59b6191
--- /dev/null
+++ b/components/net/sf/briar/transport/TagDecoder.java
@@ -0,0 +1,33 @@
+package net.sf.briar.transport;
+
+class TagDecoder {
+
+	static boolean decodeTag(byte[] tag, int transportId, long connection,
+			long packet) {
+		if(tag.length != Constants.TAG_BYTES) return false;
+		// First 16 bits must be zero
+		if(readUint16(tag, 0) != 0) return false;
+		// Transport identifier is encoded as an unsigned 16-bit integer
+		if(readUint16(tag, 2) != transportId) return false;
+		// Connection number is encoded as an unsigned 32-bit integer
+		if(readUint32(tag, 4) != connection) return false;
+		// Packet number is encoded as an unsigned 32-bit integer
+		if(readUint32(tag, 8) != packet) return false;
+		// Last 32 bits must be zero
+		if(readUint32(tag, 12) != 0L) return false;
+		return true;
+	}
+
+	// Package access for testing
+	static int readUint16(byte[] b, int offset) {
+		assert b.length >= offset + 2;
+		return ((b[offset] & 0xFF) << 8) | (b[offset + 1] & 0xFF);
+	}
+
+	// Package access for testing
+	static long readUint32(byte[] b, int offset) {
+		assert b.length >= offset + 4;
+		return ((b[offset] & 0xFFL) << 24) | ((b[offset + 1] & 0xFFL) << 16)
+		| ((b[offset + 2] & 0xFFL) << 8) | (b[offset + 3] & 0xFFL);
+	}
+}
diff --git a/components/net/sf/briar/transport/TagEncoder.java b/components/net/sf/briar/transport/TagEncoder.java
index 98c0b1fe87..8160f76212 100644
--- a/components/net/sf/briar/transport/TagEncoder.java
+++ b/components/net/sf/briar/transport/TagEncoder.java
@@ -1,16 +1,16 @@
 package net.sf.briar.transport;
 
-public class TagEncoder {
+class TagEncoder {
 
-	static byte[] encodeTag(int transportIdentifier, long connectionNumber,
-			long packetNumber) {
+	static byte[] encodeTag(int transportId, long connection,
+			long packet) {
 		byte[] tag = new byte[Constants.TAG_BYTES];
 		// Encode the transport identifier as an unsigned 16-bit integer
-		writeUint16(transportIdentifier, tag, 2);
+		writeUint16(transportId, tag, 2);
 		// Encode the connection number as an unsigned 32-bit integer
-		writeUint32(connectionNumber, tag, 4);
+		writeUint32(connection, tag, 4);
 		// Encode the packet number as an unsigned 32-bit integer
-		writeUint32(packetNumber, tag, 8);
+		writeUint32(packet, tag, 8);
 		return tag;
 	}
 
diff --git a/test/build.xml b/test/build.xml
index 90ccb67ce2..708e057603 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -37,6 +37,7 @@
 			<test name='net.sf.briar.transport.PacketDecrypterImplTest'/>
 			<test name='net.sf.briar.transport.PacketEncrypterImplTest'/>
 			<test name='net.sf.briar.transport.PacketWriterImplTest'/>
+			<test name='net.sf.briar.transport.TagDecoderTest'/>
 			<test name='net.sf.briar.transport.TagEncoderTest'/>
 			<test name='net.sf.briar.util.FileUtilsTest'/>
 			<test name='net.sf.briar.util.StringUtilsTest'/>
diff --git a/test/net/sf/briar/protocol/FileReadWriteTest.java b/test/net/sf/briar/protocol/FileReadWriteTest.java
index a0ec39fd31..7762655319 100644
--- a/test/net/sf/briar/protocol/FileReadWriteTest.java
+++ b/test/net/sf/briar/protocol/FileReadWriteTest.java
@@ -34,7 +34,7 @@ import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.protocol.writers.OfferWriter;
-import net.sf.briar.api.protocol.writers.PacketWriterFactory;
+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;
@@ -60,7 +60,7 @@ public class FileReadWriteTest extends TestCase {
 	private final long start = System.currentTimeMillis();
 
 	private final ReaderFactory readerFactory;
-	private final PacketWriterFactory packetWriterFactory;
+	private final ProtocolWriterFactory protocolWriterFactory;
 	private final CryptoComponent crypto;
 	private final AckReader ackReader;
 	private final BatchReader batchReader;
@@ -81,7 +81,7 @@ public class FileReadWriteTest extends TestCase {
 				new ProtocolModule(), new SerialModule(),
 				new WritersModule());
 		readerFactory = i.getInstance(ReaderFactory.class);
-		packetWriterFactory = i.getInstance(PacketWriterFactory.class);
+		protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
 		crypto = i.getInstance(CryptoComponent.class);
 		assertEquals(crypto.getMessageDigest().getDigestLength(),
 				UniqueId.LENGTH);
@@ -126,39 +126,39 @@ public class FileReadWriteTest extends TestCase {
 	public void testWriteFile() throws Exception {
 		FileOutputStream out = new FileOutputStream(file);
 
-		AckWriter a = packetWriterFactory.createAckWriter(out);
+		AckWriter a = protocolWriterFactory.createAckWriter(out);
 		assertTrue(a.writeBatchId(ack));
 		a.finish();
 
-		BatchWriter b = packetWriterFactory.createBatchWriter(out);
+		BatchWriter b = protocolWriterFactory.createBatchWriter(out);
 		assertTrue(b.writeMessage(message.getBytes()));
 		assertTrue(b.writeMessage(message1.getBytes()));
 		assertTrue(b.writeMessage(message2.getBytes()));
 		assertTrue(b.writeMessage(message3.getBytes()));
 		b.finish();
 
-		OfferWriter o = packetWriterFactory.createOfferWriter(out);
+		OfferWriter o = protocolWriterFactory.createOfferWriter(out);
 		assertTrue(o.writeMessageId(message.getId()));
 		assertTrue(o.writeMessageId(message1.getId()));
 		assertTrue(o.writeMessageId(message2.getId()));
 		assertTrue(o.writeMessageId(message3.getId()));
 		o.finish();
 
-		RequestWriter r = packetWriterFactory.createRequestWriter(out);
+		RequestWriter r = protocolWriterFactory.createRequestWriter(out);
 		BitSet requested = new BitSet(4);
 		requested.set(1);
 		requested.set(3);
 		r.writeBitmap(requested, 4);
 
 		SubscriptionWriter s =
-			packetWriterFactory.createSubscriptionWriter(out);
+			protocolWriterFactory.createSubscriptionWriter(out);
 		// Use a LinkedHashMap for predictable iteration order
 		Map<Group, Long> subs = new LinkedHashMap<Group, Long>();
 		subs.put(group, 0L);
 		subs.put(group1, 0L);
 		s.writeSubscriptions(subs);
 
-		TransportWriter t = packetWriterFactory.createTransportWriter(out);
+		TransportWriter t = protocolWriterFactory.createTransportWriter(out);
 		t.writeTransports(transports);
 
 		out.close();
diff --git a/test/net/sf/briar/transport/TagDecoderTest.java b/test/net/sf/briar/transport/TagDecoderTest.java
new file mode 100644
index 0000000000..d99e0d480c
--- /dev/null
+++ b/test/net/sf/briar/transport/TagDecoderTest.java
@@ -0,0 +1,30 @@
+package net.sf.briar.transport;
+
+import junit.framework.TestCase;
+
+import net.sf.briar.util.StringUtils;
+
+import org.junit.Test;
+
+public class TagDecoderTest extends TestCase {
+
+	@Test
+	public void testReadUint16() {
+		byte[] b = StringUtils.fromHexString("000000");
+		assertEquals(0, TagDecoder.readUint16(b, 1));
+		b = StringUtils.fromHexString("000001");
+		assertEquals(1, TagDecoder.readUint16(b, 1));
+		b = StringUtils.fromHexString("00FFFF");
+		assertEquals(65535, TagDecoder.readUint16(b, 1));
+	}
+
+	@Test
+	public void testReadUint32() {
+		byte[] b = StringUtils.fromHexString("0000000000");
+		assertEquals(0L, TagDecoder.readUint32(b, 1));
+		b = StringUtils.fromHexString("0000000001");
+		assertEquals(1L, TagDecoder.readUint32(b, 1));
+		b = StringUtils.fromHexString("00FFFFFFFF");
+		assertEquals(4294967295L, TagDecoder.readUint32(b, 1));
+	}
+}
diff --git a/test/net/sf/briar/transport/TagEncoderTest.java b/test/net/sf/briar/transport/TagEncoderTest.java
index 0f81d84a1a..c521b05236 100644
--- a/test/net/sf/briar/transport/TagEncoderTest.java
+++ b/test/net/sf/briar/transport/TagEncoderTest.java
@@ -9,7 +9,7 @@ import junit.framework.TestCase;
 public class TagEncoderTest extends TestCase {
 
 	@Test
-	public void testWriteUint16() throws Exception {
+	public void testWriteUint16() {
 		byte[] b = new byte[3];
 		TagEncoder.writeUint16(0, b, 1);
 		assertEquals("000000", StringUtils.toHexString(b));
@@ -20,7 +20,7 @@ public class TagEncoderTest extends TestCase {
 	}
 
 	@Test
-	public void testWriteUint32() throws Exception {
+	public void testWriteUint32() {
 		byte[] b = new byte[5];
 		TagEncoder.writeUint32(0L, b, 1);
 		assertEquals("0000000000", StringUtils.toHexString(b));
-- 
GitLab