mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Incoming error correction layer (untested).
This commit is contained in:
13
components/net/sf/briar/transport/ErasureDecoder.java
Normal file
13
components/net/sf/briar/transport/ErasureDecoder.java
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
60
components/net/sf/briar/transport/XorErasureDecoder.java
Normal file
60
components/net/sf/briar/transport/XorErasureDecoder.java
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user