Skip to content

Commit

Permalink
Specified chunk size should include header (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrgn committed Sep 27, 2016
1 parent 2abfb12 commit ba6357a
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 39 deletions.
32 changes: 8 additions & 24 deletions README.md
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
```

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down
12 changes: 6 additions & 6 deletions src/main/java/org/saltyrtc/chunkedDc/Chunker.java
Expand Up @@ -20,15 +20,15 @@ public class Chunker {

private final long id;
private final ByteBuffer buf;
private final int chunkSize;
private final int chunkDataSize;
private int chunkId;

/**
* Create a Chunker instance.
*
* @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
Expand All @@ -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;
}

Expand All @@ -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
Expand Down
4 changes: 1 addition & 3 deletions src/test/java/org/saltyrtc/chunkedDc/tests/ChunkTest.java
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
22 changes: 16 additions & 6 deletions src/test/java/org/saltyrtc/chunkedDc/tests/ChunkerTest.java
Expand Up @@ -10,6 +10,7 @@

import org.junit.Test;
import org.saltyrtc.chunkedDc.Chunker;
import org.saltyrtc.chunkedDc.Common;

import java.nio.ByteBuffer;

Expand All @@ -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());
Expand Down Expand Up @@ -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 },
Expand All @@ -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 },
Expand All @@ -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()
Expand All @@ -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.
*/
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

}

0 comments on commit ba6357a

Please sign in to comment.