diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index a2725ef2d..c4fdf88a3 100644
--- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -55,8 +55,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
new IncomingAuthenticationLayerImpl(correction, mac, macKey);
// No reordering or retransmission
IncomingReliabilityLayer reliability =
- new IncomingReliabilityLayerImpl(authentication,
- new NullFrameWindow());
+ new NullIncomingReliabilityLayer(authentication);
// Create the reader - don't tolerate errors
return new ConnectionReaderImpl(reliability, false);
}
@@ -93,8 +92,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
new IncomingAuthenticationLayerImpl(correction, mac, macKey);
// No reordering or retransmission
IncomingReliabilityLayer reliability =
- new IncomingReliabilityLayerImpl(authentication,
- new NullFrameWindow());
+ new NullIncomingReliabilityLayer(authentication);
// Create the reader - don't tolerate errors
return new ConnectionReaderImpl(reliability, false);
}
diff --git a/components/net/sf/briar/transport/ConnectionReaderImpl.java b/components/net/sf/briar/transport/ConnectionReaderImpl.java
index 1859eda8d..efebd6b30 100644
--- a/components/net/sf/briar/transport/ConnectionReaderImpl.java
+++ b/components/net/sf/briar/transport/ConnectionReaderImpl.java
@@ -12,14 +12,13 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
private final IncomingReliabilityLayer in;
private final boolean tolerateErrors;
- private final Frame frame;
+ private Frame frame;
private int offset = 0, length = 0;
ConnectionReaderImpl(IncomingReliabilityLayer in, boolean tolerateErrors) {
this.in = in;
this.tolerateErrors = tolerateErrors;
- frame = new Frame(in.getMaxFrameLength());
}
public InputStream getInputStream() {
@@ -28,7 +27,8 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
@Override
public int read() throws IOException {
- while(length == 0) if(!readValidFrame()) return -1;
+ if(length == -1) return -1;
+ while(length == 0) if(!readFrame()) return -1;
int b = frame.getBuffer()[offset] & 0xff;
offset++;
length--;
@@ -42,7 +42,8 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
@Override
public int read(byte[] b, int off, int len) throws IOException {
- while(length == 0) if(!readValidFrame()) return -1;
+ if(length == -1) return -1;
+ while(length == 0) if(!readFrame()) return -1;
len = Math.min(len, length);
System.arraycopy(frame.getBuffer(), offset, b, off, len);
offset += len;
@@ -50,11 +51,15 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
return len;
}
- private boolean readValidFrame() throws IOException {
+ private boolean readFrame() throws IOException {
assert length == 0;
while(true) {
try {
- if(!in.readFrame(frame)) return false;
+ frame = in.readFrame(frame);
+ if(frame == null) {
+ length = -1;
+ return false;
+ }
offset = FRAME_HEADER_LENGTH;
length = HeaderEncoder.getPayloadLength(frame.getBuffer());
return true;
diff --git a/components/net/sf/briar/transport/Frame.java b/components/net/sf/briar/transport/Frame.java
index 921e5ff23..6d1aae7cb 100644
--- a/components/net/sf/briar/transport/Frame.java
+++ b/components/net/sf/briar/transport/Frame.java
@@ -26,6 +26,11 @@ class Frame {
return buf;
}
+ public long getFrameNumber() {
+ if(length == -1) throw new IllegalStateException();
+ return HeaderEncoder.getFrameNumber(buf);
+ }
+
public int getLength() {
if(length == -1) throw new IllegalStateException();
return length;
diff --git a/components/net/sf/briar/transport/IncomingReliabilityLayer.java b/components/net/sf/briar/transport/IncomingReliabilityLayer.java
index 162a228c2..01525c0e4 100644
--- a/components/net/sf/briar/transport/IncomingReliabilityLayer.java
+++ b/components/net/sf/briar/transport/IncomingReliabilityLayer.java
@@ -5,14 +5,14 @@ import java.io.IOException;
interface IncomingReliabilityLayer {
/**
- * Reads a frame into the given buffer. Returns false if no more frames
- * can be read from the connection.
+ * Reads and returns a frame, possibly using the given buffer. Returns null
+ * if no more frames can be read from the connection.
* @throws IOException if an unrecoverable error occurs and the connection
* must be closed.
* @throws InvalidDataException if a recoverable error occurs. The caller
* may choose whether to retry the read or close the connection.
*/
- boolean readFrame(Frame f) throws IOException, InvalidDataException;
+ Frame readFrame(Frame f) throws IOException, InvalidDataException;
/** Returns the maximum length in bytes of the frames this layer returns. */
int getMaxFrameLength();
diff --git a/components/net/sf/briar/transport/IncomingReliabilityLayerImpl.java b/components/net/sf/briar/transport/IncomingReliabilityLayerImpl.java
index a5943a50a..5635d0313 100644
--- a/components/net/sf/briar/transport/IncomingReliabilityLayerImpl.java
+++ b/components/net/sf/briar/transport/IncomingReliabilityLayerImpl.java
@@ -1,25 +1,69 @@
package net.sf.briar.transport;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.ListIterator;
class IncomingReliabilityLayerImpl implements IncomingReliabilityLayer {
private final IncomingAuthenticationLayer in;
- private final FrameWindow window;
private final int maxFrameLength;
+ private final FrameWindow window;
+ private final LinkedList frames;
+ private final ArrayList freeFrames;
- IncomingReliabilityLayerImpl(IncomingAuthenticationLayer in,
- FrameWindow window) {
+ private long nextFrameNumber = 0L;
+
+ IncomingReliabilityLayerImpl(IncomingAuthenticationLayer in) {
this.in = in;
- this.window = window;
maxFrameLength = in.getMaxFrameLength();
+ window = new FrameWindowImpl();
+ frames = new LinkedList();
+ freeFrames = new ArrayList();
}
- public boolean readFrame(Frame f) throws IOException, InvalidDataException {
- if(!in.readFrame(f, window)) return false;
- long frameNumber = HeaderEncoder.getFrameNumber(f.getBuffer());
- if(!window.remove(frameNumber)) throw new IllegalStateException();
- return true;
+ public Frame readFrame(Frame f) throws IOException,
+ InvalidDataException {
+ freeFrames.add(f);
+ // Read frames until there's an in-order frame to return
+ Frame next = frames.peek();
+ while(next == null || next.getFrameNumber() > nextFrameNumber) {
+ // Grab a free frame, or allocate one if necessary
+ int free = freeFrames.size();
+ if(free == 0) f = new Frame(maxFrameLength);
+ else f = freeFrames.remove(free - 1);
+ // Read a frame
+ if(!in.readFrame(f, window)) return null;
+ // If the frame is in order, return it
+ long frameNumber = f.getFrameNumber();
+ if(frameNumber == nextFrameNumber) {
+ nextFrameNumber++;
+ return f;
+ }
+ // Insert the frame into the list
+ if(next == null || next.getFrameNumber() > frameNumber) {
+ frames.push(f);
+ } else {
+ boolean inserted = false;
+ ListIterator it = frames.listIterator();
+ while(it.hasNext()) {
+ if(it.next().getFrameNumber() > frameNumber) {
+ // Insert the frame before the one just examined
+ it.previous();
+ it.add(f);
+ inserted = true;
+ break;
+ }
+ }
+ if(!inserted) frames.add(f);
+ }
+ next = frames.peek();
+ }
+ assert next != null && next.getFrameNumber() == nextFrameNumber;
+ frames.poll();
+ nextFrameNumber++;
+ return next;
}
public int getMaxFrameLength() {
diff --git a/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java b/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java
new file mode 100644
index 000000000..194df4f4b
--- /dev/null
+++ b/components/net/sf/briar/transport/NullIncomingReliabilityLayer.java
@@ -0,0 +1,27 @@
+package net.sf.briar.transport;
+
+import java.io.IOException;
+
+class NullIncomingReliabilityLayer implements IncomingReliabilityLayer {
+
+ private final IncomingAuthenticationLayer in;
+ private final int maxFrameLength;
+ private final FrameWindow window;
+
+ NullIncomingReliabilityLayer(IncomingAuthenticationLayer in) {
+ this.in = in;
+ maxFrameLength = in.getMaxFrameLength();
+ window = new NullFrameWindow();
+ }
+
+ public Frame readFrame(Frame f) throws IOException, InvalidDataException {
+ if(!in.readFrame(f, window)) return null;
+ if(!window.remove(f.getFrameNumber()))
+ throw new IllegalStateException();
+ return f;
+ }
+
+ public int getMaxFrameLength() {
+ return maxFrameLength;
+ }
+}
diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
index 3559501c4..feaddea5c 100644
--- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java
@@ -224,8 +224,7 @@ public class ConnectionReaderImplTest extends TransportTest {
IncomingAuthenticationLayer authentication =
new IncomingAuthenticationLayerImpl(correction, mac, macKey);
IncomingReliabilityLayer reliability =
- new IncomingReliabilityLayerImpl(authentication,
- new NullFrameWindow());
+ new NullIncomingReliabilityLayer(authentication);
return new ConnectionReaderImpl(reliability, false);
}
}
diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java
index 98f50444d..5ca190878 100644
--- a/test/net/sf/briar/transport/FrameReadWriteTest.java
+++ b/test/net/sf/briar/transport/FrameReadWriteTest.java
@@ -103,8 +103,7 @@ public class FrameReadWriteTest extends BriarTestCase {
IncomingAuthenticationLayer authenticationIn =
new IncomingAuthenticationLayerImpl(correctionIn, mac, macKey);
IncomingReliabilityLayer reliabilityIn =
- new IncomingReliabilityLayerImpl(authenticationIn,
- new NullFrameWindow());
+ new NullIncomingReliabilityLayer(authenticationIn);
ConnectionReader reader = new ConnectionReaderImpl(reliabilityIn,
false);
InputStream in1 = reader.getInputStream();