From 9f1e3dea2165f9078324049502da07f461f3526f Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Sat, 21 Jan 2012 18:48:24 +0000 Subject: [PATCH] Incoming reliability layer with support for reordering (untested). --- .../ConnectionReaderFactoryImpl.java | 6 +- .../briar/transport/ConnectionReaderImpl.java | 17 +++-- components/net/sf/briar/transport/Frame.java | 5 ++ .../transport/IncomingReliabilityLayer.java | 6 +- .../IncomingReliabilityLayerImpl.java | 62 ++++++++++++++++--- .../NullIncomingReliabilityLayer.java | 27 ++++++++ .../transport/ConnectionReaderImplTest.java | 3 +- .../briar/transport/FrameReadWriteTest.java | 3 +- 8 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 components/net/sf/briar/transport/NullIncomingReliabilityLayer.java diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java index a2725ef2d9..c4fdf88a3f 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 1859eda8dc..efebd6b301 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 921e5ff23e..6d1aae7cbd 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 162a228c2d..01525c0e4b 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 a5943a50ab..5635d0313c 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 0000000000..194df4f4bf --- /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 3559501c4d..feaddea5ce 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 98f50444db..5ca1908782 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(); -- GitLab