From ba6357a6ec99b971d0437cf51d72f0a61ba944d6 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Tue, 27 Sep 2016 11:11:24 +0200 Subject: [PATCH] Specified chunk size should include header (#3) --- README.md | 32 +++++-------------- .../java/org/saltyrtc/chunkedDc/Chunker.java | 12 +++---- .../saltyrtc/chunkedDc/tests/ChunkTest.java | 4 +-- .../saltyrtc/chunkedDc/tests/ChunkerTest.java | 22 +++++++++---- 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 3321b63..427bea0 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ This allows you to send the chunks to the receiver in any order. While the library was written for use with WebRTC DataChannels, it can also be used outside of that scope. +The full specification for the chunking format can be found +[here](https://github.com/saltyrtc/saltyrtc-meta/blob/master/Chunking.md). + ## Installing The package is available [on Bintray](https://bintray.com/saltyrtc/maven/chunked-dc) @@ -46,8 +49,8 @@ For each message that you want to split into chunks, pass it to a `Chunker`. ```java long messageId = 1337; -ByteBuffer message = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5 }); -int chunkSize = 2; +ByteBuffer message = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }); +int chunkSize = 12; Chunker chunker = new Chunker(messageId, message, chunkSize); ``` @@ -60,7 +63,7 @@ while (chunker.hasNext()) { } ``` -The example above will return 3 chunks: `[1, 2], [3, 4], [5]`. +The example above will return 3 chunks: `[1, 2, 3], [4, 5, 6], [7, 8]`. ### Unchunking @@ -110,27 +113,8 @@ All classes exposed by this library should be thread safe. ## Format -A chunker instance splits up a ByteBuffer into multiple chunks. - -A header is added to each chunk: - - |O|IIII|SSSS| - - - O: Options bitfield (1 byte) - - I: Id (4 bytes) - - S: Serial number (4 bytes) - -The options bitfield looks as follows: - - |000000E| - ^---- End-of-message - -The Id can be any number, but it's recommended to start at 0 and -increment the counter for each message. - -The Serial must start at 0 and be incremented after every message. - -No chunk may contain more bytes than the first one. +The chunking format is described +[in the specification](https://github.com/saltyrtc/saltyrtc-meta/blob/master/Chunking.md). ## Testing diff --git a/src/main/java/org/saltyrtc/chunkedDc/Chunker.java b/src/main/java/org/saltyrtc/chunkedDc/Chunker.java index 75f34eb..ad8edbe 100644 --- a/src/main/java/org/saltyrtc/chunkedDc/Chunker.java +++ b/src/main/java/org/saltyrtc/chunkedDc/Chunker.java @@ -20,7 +20,7 @@ public class Chunker { private final long id; private final ByteBuffer buf; - private final int chunkSize; + private final int chunkDataSize; private int chunkId; /** @@ -28,7 +28,7 @@ public class Chunker { * * @param id An identifier for the message. Must be between 0 and 2**32-1. * @param buf The ByteBuffer containing the data that should be chunked. - * @param chunkSize The chunk size *excluding* header data. + * @param chunkSize The chunk size *including* header data. * @throws IllegalArgumentException if message id is negative * @throws IllegalArgumentException if chunk size is less than 1 * @throws IllegalArgumentException if buffer is empty @@ -37,15 +37,15 @@ public Chunker(long id, ByteBuffer buf, int chunkSize) { if (id < 0) { throw new IllegalArgumentException("Message id may not be negative"); } - if (chunkSize < 1) { - throw new IllegalArgumentException("Chunk size must be at least 1"); + if (chunkSize < (Common.HEADER_LENGTH + 1)) { + throw new IllegalArgumentException("Chunk size must be at least " + (Common.HEADER_LENGTH + 1)); } if (!buf.hasRemaining()) { throw new IllegalArgumentException("Buffer may not be empty"); } this.id = id; this.buf = buf; - this.chunkSize = chunkSize; + this.chunkDataSize = chunkSize - Common.HEADER_LENGTH; this.chunkId = 0; } @@ -65,7 +65,7 @@ public ByteBuffer next() { } // Allocate chunk buffer final int remaining = this.buf.remaining(); - final int chunkBytes = remaining < this.chunkSize ? remaining : this.chunkSize; + final int chunkBytes = remaining < this.chunkDataSize ? remaining : this.chunkDataSize; final ByteBuffer chunk = ByteBuffer.allocate(chunkBytes + Common.HEADER_LENGTH); // Create header diff --git a/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkTest.java b/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkTest.java index fc801be..1436110 100644 --- a/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkTest.java +++ b/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkTest.java @@ -10,14 +10,12 @@ import org.junit.Test; import org.saltyrtc.chunkedDc.Chunk; -import org.saltyrtc.chunkedDc.Chunker; import java.nio.ByteBuffer; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class ChunkTest { @@ -63,7 +61,7 @@ public void testChunkNoData() { @Test(expected = IllegalArgumentException.class) public void testInvalidChunk() { final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2, 3 }); - final Chunk chunk = new Chunk(buf); + new Chunk(buf); } @Test diff --git a/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkerTest.java b/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkerTest.java index 0c4ef99..25e1d1d 100644 --- a/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkerTest.java +++ b/src/test/java/org/saltyrtc/chunkedDc/tests/ChunkerTest.java @@ -10,6 +10,7 @@ import org.junit.Test; import org.saltyrtc.chunkedDc.Chunker; +import org.saltyrtc.chunkedDc.Common; import java.nio.ByteBuffer; @@ -32,7 +33,7 @@ public class ChunkerTest { @Test public void testChunkingMultiples() { final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6}); - final Chunker chunker = new Chunker(ID, buf, 2); + final Chunker chunker = new Chunker(ID, buf, Common.HEADER_LENGTH + 2); assertTrue(chunker.hasNext()); ByteBuffer firstBuf = chunker.next(); assertEquals(11, firstBuf.remaining()); @@ -60,7 +61,7 @@ public void testChunkingMultiples() { @Test public void testChunkingNonMultiples() { final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2, 3, 4, 5, 6}); - final Chunker chunker = new Chunker(ID, buf, 4); + final Chunker chunker = new Chunker(ID, buf, Common.HEADER_LENGTH + 4); assertTrue(chunker.hasNext()); assertArrayEquals( new byte[] { MORE, /*Id*/0,0,0,ID, /*Serial*/0,0,0,0, /*Data*/1,2,3,4 }, @@ -81,7 +82,7 @@ public void testChunkingNonMultiples() { @Test public void testChunkingSmallData() { final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2 }); - final Chunker chunker = new Chunker(ID, buf, 99); + final Chunker chunker = new Chunker(ID, buf, Common.HEADER_LENGTH + 99); assertTrue(chunker.hasNext()); assertArrayEquals( new byte[] { END, /*Id*/0,0,0,ID, /*Serial*/0,0,0,0, /*Data*/1,2 }, @@ -97,7 +98,7 @@ public void testChunkingSmallData() { @Test public void testChunkSize1() { final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2 }); - final Chunker chunker = new Chunker(ID, buf, 1); + final Chunker chunker = new Chunker(ID, buf, Common.HEADER_LENGTH + 1); assertArrayEquals( new byte[] { MORE, /*Id*/0,0,0,ID, /*Serial*/0,0,0,0, /*Data*/1 }, chunker.next().array() @@ -117,6 +118,15 @@ public void testChunkSize0() { new Chunker(ID, buf, 0); } + /** + * Does not allow chunk size of 9. + */ + @Test(expected = IllegalArgumentException.class) + public void testChunkSize9() { + final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2 }); + new Chunker(ID, buf, Common.HEADER_LENGTH); + } + /** * Does not allow negative chunk size. */ @@ -132,7 +142,7 @@ public void testChunkSizeNegative() { @Test(expected = IllegalArgumentException.class) public void testChunkEmpty() { final ByteBuffer buf = ByteBuffer.allocate(0); - new Chunker(ID, buf, 2); + new Chunker(ID, buf, Common.HEADER_LENGTH + 2); } /** @@ -141,7 +151,7 @@ public void testChunkEmpty() { @Test(expected = IllegalArgumentException.class) public void testNegativeId() { final ByteBuffer buf = ByteBuffer.wrap(new byte[] { 1, 2 }); - new Chunker(-1, buf, 2); + new Chunker(-1, buf, Common.HEADER_LENGTH + 2); } }