Incoming reliability layer with support for reordering (untested).

This commit is contained in:
akwizgran
2012-01-21 18:48:24 +00:00
parent 48ceaaea2a
commit 9f1e3dea21
8 changed files with 103 additions and 26 deletions

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
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<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() {

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();