From 114ca203d84f93ba5a2fc859d310cee8072fcf2f Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Fri, 20 Jan 2012 19:09:27 +0000 Subject: [PATCH] Tests and bugfixes for XOR encoder and decoder. --- components/net/sf/briar/transport/Frame.java | 10 +++- .../sf/briar/transport/XorErasureDecoder.java | 14 +++-- .../sf/briar/transport/XorErasureEncoder.java | 5 +- test/build.xml | 1 + .../briar/transport/XorErasureCodeTest.java | 50 +++++++++++++++++ .../transport/XorErasureDecoderTest.java | 53 ++++++++++++++----- 6 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 test/net/sf/briar/transport/XorErasureCodeTest.java diff --git a/components/net/sf/briar/transport/Frame.java b/components/net/sf/briar/transport/Frame.java index f9d3ca2d1b..64c8eeda9a 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 47093034ed..685aab252e 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 b0a529b647..220f679e38 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 2f41b5e64e..c2b2dd641e 100644 --- a/test/build.xml +++ b/test/build.xml @@ -61,6 +61,7 @@ <test name='net.sf.briar.transport.IncomingSegmentedEncryptionLayerTest'/> <test name='net.sf.briar.transport.OutgoingEncryptionLayerImplTest'/> <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.XorErasureEncoderTest'/> <test name='net.sf.briar.util.ByteUtilsTest'/> diff --git a/test/net/sf/briar/transport/XorErasureCodeTest.java b/test/net/sf/briar/transport/XorErasureCodeTest.java new file mode 100644 index 0000000000..b361cc2d83 --- /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 3b78f755b4..944610fcbe 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]); + } + } } -- GitLab