From 254da2da275ef3e7045a4e0a7e65090863f13d15 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Sun, 14 Aug 2011 11:41:56 +0200
Subject: [PATCH] Removed the restriction that transport updates have to be
 written in delimited form.

---
 api/net/sf/briar/api/protocol/Tags.java       |  5 +-
 api/net/sf/briar/api/serial/Reader.java       |  6 +++
 .../sf/briar/protocol/ProtocolReaderImpl.java | 12 ++---
 .../sf/briar/protocol/SubscriptionReader.java |  2 +-
 .../sf/briar/protocol/TransportReader.java    | 49 +++++++++++++------
 .../writers/SubscriptionWriterImpl.java       |  2 +-
 .../protocol/writers/TransportWriterImpl.java | 15 ++----
 .../net/sf/briar/serial/ReaderImpl.java       | 48 ++++++++++++++----
 test/net/sf/briar/serial/ReaderImplTest.java  | 26 ++++++++++
 9 files changed, 119 insertions(+), 46 deletions(-)

diff --git a/api/net/sf/briar/api/protocol/Tags.java b/api/net/sf/briar/api/protocol/Tags.java
index 101b1e6577..7013d9c665 100644
--- a/api/net/sf/briar/api/protocol/Tags.java
+++ b/api/net/sf/briar/api/protocol/Tags.java
@@ -19,6 +19,7 @@ public interface Tags {
 	static final int OFFER = 9;
 	static final int OFFER_ID = 10;
 	static final int REQUEST = 11;
-	static final int SUBSCRIPTIONS = 12;
-	static final int TRANSPORTS = 13;
+	static final int SUBSCRIPTION_UPDATE = 12;
+	static final int TRANSPORT_PROPERTIES = 13;
+	static final int TRANSPORT_UPDATE = 14;
 }
diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java
index 9b8960229c..c00acfe934 100644
--- a/api/net/sf/briar/api/serial/Reader.java
+++ b/api/net/sf/briar/api/serial/Reader.java
@@ -9,6 +9,12 @@ 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);
 
diff --git a/components/net/sf/briar/protocol/ProtocolReaderImpl.java b/components/net/sf/briar/protocol/ProtocolReaderImpl.java
index 5797d89b3d..d79d8a4e40 100644
--- a/components/net/sf/briar/protocol/ProtocolReaderImpl.java
+++ b/components/net/sf/briar/protocol/ProtocolReaderImpl.java
@@ -30,8 +30,8 @@ class ProtocolReaderImpl implements ProtocolReader {
 		reader.addObjectReader(Tags.BATCH, batchReader);
 		reader.addObjectReader(Tags.OFFER, offerReader);
 		reader.addObjectReader(Tags.REQUEST, requestReader);
-		reader.addObjectReader(Tags.SUBSCRIPTIONS, subscriptionReader);
-		reader.addObjectReader(Tags.TRANSPORTS, transportReader);
+		reader.addObjectReader(Tags.SUBSCRIPTION_UPDATE, subscriptionReader);
+		reader.addObjectReader(Tags.TRANSPORT_UPDATE, transportReader);
 	}
 
 	public boolean hasAck() throws IOException {
@@ -67,19 +67,19 @@ class ProtocolReaderImpl implements ProtocolReader {
 	}
 
 	public boolean hasSubscriptionUpdate() throws IOException {
-		return reader.hasUserDefined(Tags.SUBSCRIPTIONS);
+		return reader.hasUserDefined(Tags.SUBSCRIPTION_UPDATE);
 	}
 
 	public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
-		return reader.readUserDefined(Tags.SUBSCRIPTIONS,
+		return reader.readUserDefined(Tags.SUBSCRIPTION_UPDATE,
 				SubscriptionUpdate.class);
 	}
 
 	public boolean hasTransportUpdate() throws IOException {
-		return reader.hasUserDefined(Tags.TRANSPORTS);
+		return reader.hasUserDefined(Tags.TRANSPORT_UPDATE);
 	}
 
 	public TransportUpdate readTransportUpdate() throws IOException {
-		return reader.readUserDefined(Tags.TRANSPORTS, TransportUpdate.class);
+		return reader.readUserDefined(Tags.TRANSPORT_UPDATE, TransportUpdate.class);
 	}
 }
diff --git a/components/net/sf/briar/protocol/SubscriptionReader.java b/components/net/sf/briar/protocol/SubscriptionReader.java
index 8faaf647e1..7ea4cacb35 100644
--- a/components/net/sf/briar/protocol/SubscriptionReader.java
+++ b/components/net/sf/briar/protocol/SubscriptionReader.java
@@ -26,7 +26,7 @@ class SubscriptionReader implements ObjectReader<SubscriptionUpdate> {
 		Consumer counting = new CountingConsumer(SubscriptionUpdate.MAX_SIZE);
 		// Read the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.SUBSCRIPTIONS);
+		r.readUserDefinedTag(Tags.SUBSCRIPTION_UPDATE);
 		r.addObjectReader(Tags.GROUP, groupReader);
 		Map<Group, Long> subs = r.readMap(Group.class, Long.class);
 		r.removeObjectReader(Tags.GROUP);
diff --git a/components/net/sf/briar/protocol/TransportReader.java b/components/net/sf/briar/protocol/TransportReader.java
index 2ea5e61b3b..fe729f3f0d 100644
--- a/components/net/sf/briar/protocol/TransportReader.java
+++ b/components/net/sf/briar/protocol/TransportReader.java
@@ -1,6 +1,7 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
@@ -13,9 +14,11 @@ import net.sf.briar.api.serial.Reader;
 class TransportReader implements ObjectReader<TransportUpdate> {
 
 	private final TransportFactory transportFactory;
+	private final ObjectReader<TransportProperties> propertiesReader;
 
 	TransportReader(TransportFactory transportFactory) {
 		this.transportFactory = transportFactory;
+		propertiesReader = new TransportPropertiesReader();
 	}
 
 	public TransportUpdate readObject(Reader r) throws IOException {
@@ -23,27 +26,41 @@ class TransportReader implements ObjectReader<TransportUpdate> {
 		Consumer counting = new CountingConsumer(TransportUpdate.MAX_SIZE);
 		// Read the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.TRANSPORTS);
-		// Transport maps are always written in delimited form
+		r.readUserDefinedTag(Tags.TRANSPORT_UPDATE);
+		r.addObjectReader(Tags.TRANSPORT_PROPERTIES, propertiesReader);
+		r.setMaxStringLength(TransportUpdate.MAX_SIZE);
+		List<TransportProperties> l = r.readList(TransportProperties.class);
+		r.resetMaxStringLength();
+		r.removeObjectReader(Tags.TRANSPORT_PROPERTIES);
 		Map<String, Map<String, String>> transports =
 			new TreeMap<String, Map<String, String>>();
-		r.readMapStart();
-		while(!r.hasMapEnd()) {
-			String name = r.readString(TransportUpdate.MAX_SIZE);
-			Map<String, String> properties = new TreeMap<String, String>();
-			r.readMapStart();
-			while(!r.hasMapEnd()) {
-				String key = r.readString(TransportUpdate.MAX_SIZE);
-				String value = r.readString(TransportUpdate.MAX_SIZE);
-				properties.put(key, value);
-			}
-			r.readMapEnd();
-			transports.put(name, properties);
-		}
-		r.readMapEnd();
+		for(TransportProperties t : l) transports.put(t.name, t.properties);
 		long timestamp = r.readInt64();
 		r.removeConsumer(counting);
 		// Build and return the transport update
 		return transportFactory.createTransports(transports, timestamp);
 	}
+
+	private static class TransportProperties {
+
+		private final String name;
+		private final Map<String, String> properties;
+
+		TransportProperties(String name, Map<String, String> properties) {
+			this.name = name;
+			this.properties = properties;
+		}
+	}
+
+	private static class TransportPropertiesReader
+	implements ObjectReader<TransportProperties> {
+
+		public TransportProperties readObject(Reader r) throws IOException {
+			r.readUserDefinedTag(Tags.TRANSPORT_PROPERTIES);
+			String name = r.readString();
+			Map<String, String> properties =
+				r.readMap(String.class, String.class);
+			return new TransportProperties(name, properties);
+		}
+	}
 }
diff --git a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
index 3b591f408b..b8f080e27b 100644
--- a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
@@ -21,7 +21,7 @@ class SubscriptionWriterImpl implements SubscriptionWriter {
 	}
 
 	public void writeSubscriptions(Map<Group, Long> subs) throws IOException {
-		w.writeUserDefinedTag(Tags.SUBSCRIPTIONS);
+		w.writeUserDefinedTag(Tags.SUBSCRIPTION_UPDATE);
 		w.writeMap(subs);
 		w.writeInt64(System.currentTimeMillis());
 		out.flush();
diff --git a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
index 89dc7ac0d3..8381f4d064 100644
--- a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
@@ -22,19 +22,14 @@ class TransportWriterImpl implements TransportWriter {
 
 	public void writeTransports(Map<String, Map<String, String>> transports)
 	throws IOException {
-		w.writeUserDefinedTag(Tags.TRANSPORTS);
-		// Transport maps are always written in delimited form
-		w.writeMapStart();
+		w.writeUserDefinedTag(Tags.TRANSPORT_UPDATE);
+		w.writeListStart();
 		for(Entry<String, Map<String, String>> e : transports.entrySet()) {
+			w.writeUserDefinedTag(Tags.TRANSPORT_PROPERTIES);
 			w.writeString(e.getKey());
-			w.writeMapStart();
-			for(Entry<String, String> e1 : e.getValue().entrySet()) {
-				w.writeString(e1.getKey());
-				w.writeString(e1.getValue());
-			}
-			w.writeMapEnd();
+			w.writeMap(e.getValue());
 		}
-		w.writeMapEnd();
+		w.writeListEnd();
 		w.writeInt64(System.currentTimeMillis());
 		out.flush();
 	}
diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java
index 92cb7a8652..63b9e4ef0c 100644
--- a/components/net/sf/briar/serial/ReaderImpl.java
+++ b/components/net/sf/briar/serial/ReaderImpl.java
@@ -26,6 +26,8 @@ class ReaderImpl implements Reader {
 	private boolean hasLookahead = false, eof = false;
 	private byte next, nextNext;
 	private byte[] buf = null;
+	private int maxStringLength = Integer.MAX_VALUE;
+	private int maxBytesLength = Integer.MAX_VALUE;
 
 	ReaderImpl(InputStream in) {
 		this.in = in;
@@ -71,6 +73,22 @@ 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) {
 		Consumer[] newConsumers = new Consumer[consumers.length + 1];
 		System.arraycopy(consumers, 0, newConsumers, 0, consumers.length);
@@ -268,21 +286,26 @@ class ReaderImpl implements Reader {
 	}
 
 	public String readString() throws IOException {
-		return readString(Integer.MAX_VALUE);
-	}
-
-	public String readString(int maxLength) throws IOException {
 		if(!hasString()) throw new FormatException();
 		consumeLookahead();
 		int length;
 		if(next == Tag.STRING) length = readLength();
 		else length = 0xFF & next ^ Tag.SHORT_STRING;
-		if(length > maxLength) throw new FormatException();
+		if(length > maxStringLength) throw new FormatException();
 		if(length == 0) return "";
 		readIntoBuffer(length);
 		return new String(buf, 0, length, "UTF-8");
 	}
 
+	public String readString(int maxLength) throws IOException {
+		setMaxStringLength(maxLength);
+		try {
+			return readString();
+		} finally {
+			resetMaxStringLength();
+		}
+	}
+
 	private int readLength() throws IOException {
 		if(!hasLength()) throw new FormatException();
 		if(next >= 0) return readUint7();
@@ -306,22 +329,27 @@ class ReaderImpl implements Reader {
 	}
 
 	public byte[] readBytes() throws IOException {
-		return readBytes(Integer.MAX_VALUE);
-	}
-
-	public byte[] readBytes(int maxLength) throws IOException {
 		if(!hasBytes()) throw new FormatException();
 		consumeLookahead();
 		int length;
 		if(next == Tag.BYTES) length = readLength();
 		else length = 0xFF & next ^ Tag.SHORT_BYTES;
-		if(length > maxLength) throw new FormatException();
+		if(length > maxBytesLength) throw new FormatException();
 		if(length == 0) return EMPTY_BUFFER;
 		byte[] b = new byte[length];
 		readIntoBuffer(b, length);
 		return b;
 	}
 
+	public byte[] readBytes(int maxLength) throws IOException {
+		setMaxBytesLength(maxLength);
+		try {
+			return readBytes();
+		} finally {
+			resetMaxBytesLength();
+		}
+	}
+
 	public boolean hasList() throws IOException {
 		if(!hasLookahead) readLookahead(true);
 		if(eof) return false;
diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java
index 2187e67bf7..28670b8b91 100644
--- a/test/net/sf/briar/serial/ReaderImplTest.java
+++ b/test/net/sf/briar/serial/ReaderImplTest.java
@@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -485,6 +486,31 @@ public class ReaderImplTest extends TestCase {
 		assertEquals("bar", e.getValue().s);
 	}
 
+	@Test
+	public void testMaxLengthAppliesInsideMap() throws Exception {
+		setContents("B" + "1" + "83666F6F" + "93010203");
+		r.setMaxStringLength(3);
+		r.setMaxBytesLength(3);
+		Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
+		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("B" + "1" + "83666F6F" + "93010203");
+		r.setMaxStringLength(2);
+		try {
+			r.readMap(String.class, Bytes.class);
+			fail();
+		} catch(FormatException expected) {}
+		// The max bytes length should be applied inside the map
+		setContents("B" + "1" + "83666F6F" + "93010203");
+		r.setMaxBytesLength(2);
+		try {
+			r.readMap(String.class, Bytes.class);
+			fail();
+		} catch(FormatException expected) {}
+	}
+
 	@Test
 	public void testReadEmptyInput() throws Exception {
 		setContents("");
-- 
GitLab