diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java index a2725ef2d9a0205230211a54fbac230742fe5cbb..c4fdf88a3fc204272d0d8fc9d8df61b94779ca71 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 1859eda8dca6aec5f2987257af8b3544310fb6d5..efebd6b301e1a1b1db18576e093e1e6a02d5279c 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 921e5ff23e6d1d553b06f09c4bdd67208a2d827b..6d1aae7cbdda6904942f0d43d5fc980cd63066e5 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 162a228c2dc99dd79f6cb3a5d0932a19ae213ab8..01525c0e4bed2714e727769d8513860fb36dc382 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 a5943a50abe1dfad150cdfaa1e872fad59b5d7f6..5635d0313cac6ee32c25f209a8872c04f98b6013 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<Frame> frames; + private final ArrayList<Frame> freeFrames; + + private long nextFrameNumber = 0L; - IncomingReliabilityLayerImpl(IncomingAuthenticationLayer in, - FrameWindow window) { + IncomingReliabilityLayerImpl(IncomingAuthenticationLayer in) { this.in = in; - this.window = window; maxFrameLength = in.getMaxFrameLength(); + window = new FrameWindowImpl(); + frames = new LinkedList<Frame>(); + freeFrames = new ArrayList<Frame>(); } - 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<Frame> 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 0000000000000000000000000000000000000000..194df4f4bf1517fb41870ab0afde64cde7a25450 --- /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 3559501c4d093a09b0b508896da53da268f9d814..feaddea5ce670569c7afed45d770f8f404dcf1c0 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 98f50444db1624539188e9c96269351b6963142c..5ca19087826e3778623d9121ee9096b789350435 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();