Incoming error correction layer (untested).

This commit is contained in:
akwizgran
2012-01-20 15:37:33 +00:00
parent 5a4505cb71
commit e46ef15afb
6 changed files with 147 additions and 3 deletions

View File

@@ -0,0 +1,13 @@
package net.sf.briar.transport;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment;
interface ErasureDecoder {
/**
* Decodes the given set of segments into the given frame, or returns false
* if the segments cannot be decoded. The segment set may contain nulls.
*/
public boolean decodeFrame(Frame f, Segment[] set) throws FormatException;
}

View File

@@ -6,6 +6,7 @@ import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.util.Collection;
import java.util.HashSet;
/** A frame window that allows a limited amount of reordering. */
class FrameWindowImpl implements FrameWindow {
private final Collection<Long> window;

View File

@@ -0,0 +1,68 @@
package net.sf.briar.transport;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment;
class IncomingErrorCorrectionLayerImpl implements IncomingErrorCorrectionLayer {
private final IncomingEncryptionLayer in;
private final ErasureDecoder decoder;
private final int n, k;
private final Map<Long, Integer> discardCounts;
private final Map<Long, Segment[]> segmentSets;
IncomingErrorCorrectionLayerImpl(IncomingEncryptionLayer in,
ErasureDecoder decoder, int n, int k) {
this.in = in;
this.decoder = decoder;
this.n = n;
this.k = k;
discardCounts = new HashMap<Long, Integer>();
segmentSets = new HashMap<Long, Segment[]>();
}
public boolean readFrame(Frame f, FrameWindow window) throws IOException,
InvalidDataException {
// Free any segment sets that have been removed from the window
Iterator<Long> it = segmentSets.keySet().iterator();
while(it.hasNext()) if(!window.contains(it.next())) it.remove();
// Free any discard counts that are no longer too high for the window
Iterator<Long> it1 = discardCounts.keySet().iterator();
while(it1.hasNext()) if(!window.isTooHigh(it1.next())) it1.remove();
// Allocate a segment
Segment s = new SegmentImpl();
// Read segments until a frame can be decoded
while(true) {
// Read segments until a segment in the window is returned
long frameNumber;
while(true) {
if(!in.readSegment(s)) return false;
frameNumber = s.getSegmentNumber() / n;
if(window.contains(frameNumber)) break;
if(window.isTooHigh(frameNumber)) countDiscard(frameNumber);
}
// Add the segment to its segment set, or create one if necessary
Segment[] set = segmentSets.get(frameNumber);
if(set == null) {
set = new Segment[n];
segmentSets.put(frameNumber, set);
} else {
set[(int) (frameNumber % n)] = s;
}
// Try to decode the frame
if(decoder.decodeFrame(f, set)) return true;
}
}
private void countDiscard(long frameNumber) throws FormatException {
Integer count = discardCounts.get(frameNumber);
if(count == null) discardCounts.put(frameNumber, 1);
else if(count == n - k) throw new FormatException();
else discardCounts.put(frameNumber, count + 1);
}
}

View File

@@ -2,6 +2,7 @@ package net.sf.briar.transport;
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
/** A frame window that does not allow any reordering. */
class NullFrameWindow implements FrameWindow {
private long base = 0L;

View File

@@ -14,12 +14,13 @@ class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
segment = new SegmentImpl();
}
public boolean readFrame(Frame f, FrameWindow window)
throws IOException, InvalidDataException {
public boolean readFrame(Frame f, FrameWindow window) throws IOException,
InvalidDataException {
while(true) {
if(!in.readSegment(segment)) return false;
byte[] buf = segment.getBuffer();
if(window.contains(HeaderEncoder.getFrameNumber(buf))) break;
long frameNumber = HeaderEncoder.getFrameNumber(buf);
if(window.contains(frameNumber)) break;
}
int length = segment.getLength();
// FIXME: Unnecessary copy

View File

@@ -0,0 +1,60 @@
package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.transport.Segment;
/** An erasure decoder that uses k data segments and one parity segment. */
class XorErasureDecoder implements ErasureDecoder {
private final int n;
XorErasureDecoder(int n) {
this.n = n;
}
public boolean decodeFrame(Frame f, Segment[] set) throws FormatException {
// We need at least n - 1 pieces
int pieces = 0;
for(int i = 0; i < n; i++) if(set[i] != null) pieces++;
if(pieces < n - 1) return false;
// All the pieces must have the same length - take the minimum
int length = MAX_FRAME_LENGTH;
for(int i = 0; i < n; i++) {
if(set[i] == null) {
int len = set[i].getLength();
if(len < length) length = len;
}
}
if(length * (n - 1) > MAX_FRAME_LENGTH) throw new FormatException();
// Decode the frame
byte[] dest = f.getBuffer();
int offset = 0;
if(pieces == n || set[n - 1] == null) {
// We don't need no stinkin' parity segment
for(int i = 0; i < n - 1; i++) {
byte[] src = set[i].getBuffer();
System.arraycopy(src, 0, dest, offset, length);
offset += length;
}
} else {
// Reconstruct the missing segment
byte[] parity = new byte[length];
int missingOffset = -1;
for(int i = 0; i < n; i++) {
if(set[i] == null) {
missingOffset = offset;
} else {
byte[] src = set[i].getBuffer();
System.arraycopy(src, 0, dest, offset, length);
for(int j = 0; j < length; j++) parity[j] ^= src[j];
}
offset += length;
}
assert missingOffset != -1;
System.arraycopy(parity, 0, dest, missingOffset, length);
}
f.setLength(offset);
return true;
}
}