mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 05:09:53 +01:00
Tests and bugfixes for XOR encoder and decoder.
This commit is contained in:
@@ -4,10 +4,18 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
|||||||
|
|
||||||
class Frame {
|
class Frame {
|
||||||
|
|
||||||
private final byte[] buf = new byte[MAX_FRAME_LENGTH];
|
private final byte[] buf;
|
||||||
|
|
||||||
private int length = -1;
|
private int length = -1;
|
||||||
|
|
||||||
|
Frame() {
|
||||||
|
this(MAX_FRAME_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame(int length) {
|
||||||
|
buf = new byte[length];
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getBuffer() {
|
public byte[] getBuffer() {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,27 +36,31 @@ class XorErasureDecoder implements ErasureDecoder {
|
|||||||
// We don't need no stinkin' parity segment
|
// We don't need no stinkin' parity segment
|
||||||
for(int i = 0; i < n - 1; i++) {
|
for(int i = 0; i < n - 1; i++) {
|
||||||
byte[] src = set[i].getBuffer();
|
byte[] src = set[i].getBuffer();
|
||||||
System.arraycopy(src, 0, dest, offset, length);
|
int copyLength = Math.min(length, dest.length - offset);
|
||||||
|
System.arraycopy(src, 0, dest, offset, copyLength);
|
||||||
offset += length;
|
offset += length;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Reconstruct the missing segment
|
// Reconstruct the missing segment
|
||||||
byte[] parity = new byte[length];
|
byte[] parity = new byte[length];
|
||||||
int missingOffset = -1;
|
int missingOffset = -1;
|
||||||
for(int i = 0; i < n; i++) {
|
for(int i = 0; i < n - 1; i++) {
|
||||||
if(set[i] == null) {
|
if(set[i] == null) {
|
||||||
missingOffset = offset;
|
missingOffset = offset;
|
||||||
} else {
|
} else {
|
||||||
byte[] src = set[i].getBuffer();
|
byte[] src = set[i].getBuffer();
|
||||||
System.arraycopy(src, 0, dest, offset, length);
|
|
||||||
for(int j = 0; j < length; j++) parity[j] ^= src[j];
|
for(int j = 0; j < length; j++) parity[j] ^= src[j];
|
||||||
|
int copyLength = Math.min(length, dest.length - offset);
|
||||||
|
System.arraycopy(src, 0, dest, offset, copyLength);
|
||||||
}
|
}
|
||||||
offset += length;
|
offset += length;
|
||||||
}
|
}
|
||||||
|
byte[] src = set[n - 1].getBuffer();
|
||||||
|
for(int i = 0; i < length; i++) parity[i] ^= src[i];
|
||||||
assert missingOffset != -1;
|
assert missingOffset != -1;
|
||||||
System.arraycopy(parity, 0, dest, missingOffset, length);
|
int copyLength = Math.min(length, dest.length - missingOffset);
|
||||||
|
System.arraycopy(parity, 0, dest, missingOffset, copyLength);
|
||||||
}
|
}
|
||||||
assert offset == length * (n - 1);
|
|
||||||
// The frame length may not be an exact multiple of the segment length
|
// The frame length may not be an exact multiple of the segment length
|
||||||
int payload = HeaderEncoder.getPayloadLength(dest);
|
int payload = HeaderEncoder.getPayloadLength(dest);
|
||||||
int padding = HeaderEncoder.getPaddingLength(dest);
|
int padding = HeaderEncoder.getPaddingLength(dest);
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ class XorErasureEncoder implements ErasureEncoder {
|
|||||||
byte[] src = f.getBuffer(), parity = set[n - 1].getBuffer();
|
byte[] src = f.getBuffer(), parity = set[n - 1].getBuffer();
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for(int i = 0; i < n - 1; i++) {
|
for(int i = 0; i < n - 1; i++) {
|
||||||
System.arraycopy(src, offset, set[i].getBuffer(), 0, length);
|
int copyLength = Math.min(length, src.length - offset);
|
||||||
for(int j = 0; j < length; j++) parity[j] ^= src[offset + j];
|
System.arraycopy(src, offset, set[i].getBuffer(), 0, copyLength);
|
||||||
|
for(int j = 0; j < copyLength; j++) parity[j] ^= src[offset + j];
|
||||||
offset += length;
|
offset += length;
|
||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<test name='net.sf.briar.transport.IncomingSegmentedEncryptionLayerTest'/>
|
<test name='net.sf.briar.transport.IncomingSegmentedEncryptionLayerTest'/>
|
||||||
<test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/>
|
<test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/>
|
||||||
<test name='net.sf.briar.transport.OutgoingSegmentedEncryptionLayerTest'/>
|
<test name='net.sf.briar.transport.OutgoingSegmentedEncryptionLayerTest'/>
|
||||||
|
<test name='net.sf.briar.transport.XorErasureCodeTest'/>
|
||||||
<test name='net.sf.briar.transport.XorErasureDecoderTest'/>
|
<test name='net.sf.briar.transport.XorErasureDecoderTest'/>
|
||||||
<test name='net.sf.briar.transport.XorErasureEncoderTest'/>
|
<test name='net.sf.briar.transport.XorErasureEncoderTest'/>
|
||||||
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
||||||
|
|||||||
50
test/net/sf/briar/transport/XorErasureCodeTest.java
Normal file
50
test/net/sf/briar/transport/XorErasureCodeTest.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import static net.sf.briar.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||||
|
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import net.sf.briar.BriarTestCase;
|
||||||
|
import net.sf.briar.api.transport.Segment;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class XorErasureCodeTest extends BriarTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodingAndDecodingWithAllSegments() throws Exception {
|
||||||
|
XorErasureEncoder e = new XorErasureEncoder(5);
|
||||||
|
XorErasureDecoder d = new XorErasureDecoder(5);
|
||||||
|
Frame f = new Frame(1234);
|
||||||
|
new Random().nextBytes(f.getBuffer());
|
||||||
|
int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
||||||
|
HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
|
||||||
|
f.setLength(1234);
|
||||||
|
Segment[] set = e.encodeFrame(f);
|
||||||
|
assertEquals(5, set.length);
|
||||||
|
Frame f1 = new Frame(1234);
|
||||||
|
assertTrue(d.decodeFrame(f1, set));
|
||||||
|
assertArrayEquals(f.getBuffer(), f1.getBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodingAndDecodingWithMissingSegment() throws Exception {
|
||||||
|
XorErasureEncoder e = new XorErasureEncoder(5);
|
||||||
|
XorErasureDecoder d = new XorErasureDecoder(5);
|
||||||
|
Frame f = new Frame(1234);
|
||||||
|
new Random().nextBytes(f.getBuffer());
|
||||||
|
int payload = 1234 - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
||||||
|
HeaderEncoder.encodeHeader(f.getBuffer(), 0L, payload, 0);
|
||||||
|
f.setLength(1234);
|
||||||
|
for(int i = 0; i < 5; i++) {
|
||||||
|
Segment[] set = e.encodeFrame(f);
|
||||||
|
assertEquals(5, set.length);
|
||||||
|
set[i] = null;
|
||||||
|
Frame f1 = new Frame(1234);
|
||||||
|
assertTrue(d.decodeFrame(f1, set));
|
||||||
|
assertArrayEquals(f.getBuffer(), f1.getBuffer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,24 +14,15 @@ public class XorErasureDecoderTest extends BriarTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMaximumLength() throws Exception {
|
public void testMaximumLength() throws Exception {
|
||||||
|
XorErasureDecoder d = new XorErasureDecoder(5);
|
||||||
// A frame of the maximum length should be decoded successfully
|
// A frame of the maximum length should be decoded successfully
|
||||||
Segment[] set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4, 5);
|
Segment[] set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4, 5);
|
||||||
XorErasureDecoder d = new XorErasureDecoder(5);
|
|
||||||
Frame f = new Frame();
|
Frame f = new Frame();
|
||||||
assertTrue(d.decodeFrame(f, set));
|
assertTrue(d.decodeFrame(f, set));
|
||||||
// Check the header
|
checkFrame(f, MAX_FRAME_LENGTH);
|
||||||
byte[] b = f.getBuffer();
|
|
||||||
assertEquals(0L, HeaderEncoder.getFrameNumber(b));
|
|
||||||
int payload = MAX_FRAME_LENGTH - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
|
||||||
assertEquals(payload, HeaderEncoder.getPayloadLength(b));
|
|
||||||
assertEquals(0, HeaderEncoder.getPaddingLength(b));
|
|
||||||
// Check the body
|
|
||||||
assertEquals(MAX_FRAME_LENGTH, f.getLength());
|
|
||||||
for(int i = FRAME_HEADER_LENGTH; i < MAX_FRAME_LENGTH; i++) {
|
|
||||||
assertEquals(0, b[i]);
|
|
||||||
}
|
|
||||||
// A frame larger than the maximum length should not be decoded
|
// A frame larger than the maximum length should not be decoded
|
||||||
set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4 + 1, 5);
|
set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4 + 1, 5);
|
||||||
|
f = new Frame();
|
||||||
try {
|
try {
|
||||||
d.decodeFrame(f, set);
|
d.decodeFrame(f, set);
|
||||||
} catch(FormatException expected) {}
|
} catch(FormatException expected) {}
|
||||||
@@ -47,12 +38,35 @@ public class XorErasureDecoderTest extends BriarTestCase {
|
|||||||
set[1].setLength(251);
|
set[1].setLength(251);
|
||||||
// The frame should be decoded successfully
|
// The frame should be decoded successfully
|
||||||
XorErasureDecoder d = new XorErasureDecoder(4);
|
XorErasureDecoder d = new XorErasureDecoder(4);
|
||||||
Frame f = new Frame();
|
Frame f = new Frame(750);
|
||||||
assertTrue(d.decodeFrame(f, set));
|
assertTrue(d.decodeFrame(f, set));
|
||||||
// The minimum of the segments' lengths should have been used
|
// The minimum of the segments' lengths should have been used
|
||||||
assertEquals(750, f.getLength());
|
assertEquals(750, f.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodingWithMissingSegment() throws Exception {
|
||||||
|
XorErasureDecoder d = new XorErasureDecoder(4);
|
||||||
|
for(int i = 0; i < 4; i++) {
|
||||||
|
Segment[] set = encodeEmptyFrame(250, 4);
|
||||||
|
set[i] = null;
|
||||||
|
// The frame should be decoded successfully
|
||||||
|
Frame f = new Frame(750);
|
||||||
|
assertTrue(d.decodeFrame(f, set));
|
||||||
|
checkFrame(f, 750);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodingWithTwoMissingSegments() throws Exception {
|
||||||
|
XorErasureDecoder d = new XorErasureDecoder(4);
|
||||||
|
Segment[] set = encodeEmptyFrame(250, 4);
|
||||||
|
set[0] = null;
|
||||||
|
set[1] = null;
|
||||||
|
Frame f = new Frame(750);
|
||||||
|
assertFalse(d.decodeFrame(f, set));
|
||||||
|
}
|
||||||
|
|
||||||
private Segment[] encodeEmptyFrame(int length, int n) {
|
private Segment[] encodeEmptyFrame(int length, int n) {
|
||||||
Segment[] set = new Segment[n];
|
Segment[] set = new Segment[n];
|
||||||
for(int i = 0; i < n; i++) {
|
for(int i = 0; i < n; i++) {
|
||||||
@@ -64,4 +78,17 @@ public class XorErasureDecoderTest extends BriarTestCase {
|
|||||||
HeaderEncoder.encodeHeader(set[n - 1].getBuffer(), 0L, payload, 0);
|
HeaderEncoder.encodeHeader(set[n - 1].getBuffer(), 0L, payload, 0);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkFrame(Frame f, int length) {
|
||||||
|
byte[] b = f.getBuffer();
|
||||||
|
assertEquals(0L, HeaderEncoder.getFrameNumber(b));
|
||||||
|
int payload = length - FRAME_HEADER_LENGTH - MAC_LENGTH;
|
||||||
|
assertEquals(payload, HeaderEncoder.getPayloadLength(b));
|
||||||
|
assertEquals(0, HeaderEncoder.getPaddingLength(b));
|
||||||
|
// Check the body
|
||||||
|
assertEquals(length, f.getLength());
|
||||||
|
for(int i = FRAME_HEADER_LENGTH; i < length; i++) {
|
||||||
|
assertEquals("" + i, 0, b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user