diff --git a/components/net/sf/briar/transport/Frame.java b/components/net/sf/briar/transport/Frame.java
index f9d3ca2d1..64c8eeda9 100644
--- a/components/net/sf/briar/transport/Frame.java
+++ b/components/net/sf/briar/transport/Frame.java
@@ -4,10 +4,18 @@ import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
class Frame {
- private final byte[] buf = new byte[MAX_FRAME_LENGTH];
+ private final byte[] buf;
private int length = -1;
+ Frame() {
+ this(MAX_FRAME_LENGTH);
+ }
+
+ Frame(int length) {
+ buf = new byte[length];
+ }
+
public byte[] getBuffer() {
return buf;
}
diff --git a/components/net/sf/briar/transport/XorErasureDecoder.java b/components/net/sf/briar/transport/XorErasureDecoder.java
index 47093034e..685aab252 100644
--- a/components/net/sf/briar/transport/XorErasureDecoder.java
+++ b/components/net/sf/briar/transport/XorErasureDecoder.java
@@ -36,27 +36,31 @@ class XorErasureDecoder implements ErasureDecoder {
// 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);
+ int copyLength = Math.min(length, dest.length - offset);
+ System.arraycopy(src, 0, dest, offset, copyLength);
offset += length;
}
} else {
// Reconstruct the missing segment
byte[] parity = new byte[length];
int missingOffset = -1;
- for(int i = 0; i < n; i++) {
+ for(int i = 0; i < n - 1; 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];
+ int copyLength = Math.min(length, dest.length - offset);
+ System.arraycopy(src, 0, dest, offset, copyLength);
}
offset += length;
}
+ byte[] src = set[n - 1].getBuffer();
+ for(int i = 0; i < length; i++) parity[i] ^= src[i];
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
int payload = HeaderEncoder.getPayloadLength(dest);
int padding = HeaderEncoder.getPaddingLength(dest);
diff --git a/components/net/sf/briar/transport/XorErasureEncoder.java b/components/net/sf/briar/transport/XorErasureEncoder.java
index b0a529b64..220f679e3 100644
--- a/components/net/sf/briar/transport/XorErasureEncoder.java
+++ b/components/net/sf/briar/transport/XorErasureEncoder.java
@@ -21,8 +21,9 @@ class XorErasureEncoder implements ErasureEncoder {
byte[] src = f.getBuffer(), parity = set[n - 1].getBuffer();
int offset = 0;
for(int i = 0; i < n - 1; i++) {
- System.arraycopy(src, offset, set[i].getBuffer(), 0, length);
- for(int j = 0; j < length; j++) parity[j] ^= src[offset + j];
+ int copyLength = Math.min(length, src.length - offset);
+ System.arraycopy(src, offset, set[i].getBuffer(), 0, copyLength);
+ for(int j = 0; j < copyLength; j++) parity[j] ^= src[offset + j];
offset += length;
}
return set;
diff --git a/test/build.xml b/test/build.xml
index 2f41b5e64..c2b2dd641 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -61,6 +61,7 @@
+
diff --git a/test/net/sf/briar/transport/XorErasureCodeTest.java b/test/net/sf/briar/transport/XorErasureCodeTest.java
new file mode 100644
index 000000000..b361cc2d8
--- /dev/null
+++ b/test/net/sf/briar/transport/XorErasureCodeTest.java
@@ -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());
+ }
+ }
+}
diff --git a/test/net/sf/briar/transport/XorErasureDecoderTest.java b/test/net/sf/briar/transport/XorErasureDecoderTest.java
index 3b78f755b..944610fcb 100644
--- a/test/net/sf/briar/transport/XorErasureDecoderTest.java
+++ b/test/net/sf/briar/transport/XorErasureDecoderTest.java
@@ -14,24 +14,15 @@ public class XorErasureDecoderTest extends BriarTestCase {
@Test
public void testMaximumLength() throws Exception {
+ XorErasureDecoder d = new XorErasureDecoder(5);
// A frame of the maximum length should be decoded successfully
Segment[] set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4, 5);
- XorErasureDecoder d = new XorErasureDecoder(5);
Frame f = new Frame();
assertTrue(d.decodeFrame(f, set));
- // Check the header
- 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]);
- }
+ checkFrame(f, MAX_FRAME_LENGTH);
// A frame larger than the maximum length should not be decoded
set = encodeEmptyFrame(MAX_FRAME_LENGTH / 4 + 1, 5);
+ f = new Frame();
try {
d.decodeFrame(f, set);
} catch(FormatException expected) {}
@@ -47,12 +38,35 @@ public class XorErasureDecoderTest extends BriarTestCase {
set[1].setLength(251);
// The frame should be decoded successfully
XorErasureDecoder d = new XorErasureDecoder(4);
- Frame f = new Frame();
+ Frame f = new Frame(750);
assertTrue(d.decodeFrame(f, set));
// The minimum of the segments' lengths should have been used
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) {
Segment[] set = new Segment[n];
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);
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]);
+ }
+ }
}