mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
2 Commits
block-inpu
...
variable-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56c1fef4db | ||
|
|
c2f96580b8 |
@@ -208,24 +208,6 @@ public interface DatabaseComponent {
|
|||||||
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
||||||
int maxLength, int maxLatency) throws DbException;
|
int maxLength, int maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of blocks in the given message.
|
|
||||||
* <p>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
int getBlockCount(Transaction txn, MessageId m) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the given block of the given message.
|
|
||||||
* <p>
|
|
||||||
* Read-only.
|
|
||||||
*
|
|
||||||
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
|
|
||||||
* to the number of blocks in the message
|
|
||||||
*/
|
|
||||||
byte[] getBlock(Transaction txn, MessageId m, int blockNumber)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given ID.
|
* Returns the contact with the given ID.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.db;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when a database operation is attempted for a block that is not in
|
|
||||||
* the database. This exception may occur due to concurrent updates and does
|
|
||||||
* not indicate a database error.
|
|
||||||
*/
|
|
||||||
public class NoSuchBlockException extends DbException {
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.db.NoSuchBlockException;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
public interface BlockSource {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of blocks in the given message.
|
|
||||||
*/
|
|
||||||
int getBlockCount(MessageId m) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the given block of the given message.
|
|
||||||
*
|
|
||||||
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
|
|
||||||
* to the number of blocks in the message
|
|
||||||
*/
|
|
||||||
byte[] getBlock(MessageId m, int blockNumber) throws DbException;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface MessageInputStreamFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an {@link InputStream} for reading the given message from the
|
|
||||||
* database. This method returns immediately. If the message is not in the
|
|
||||||
* database or cannot be read, reading from the stream will throw an
|
|
||||||
* {@link IOException};
|
|
||||||
*/
|
|
||||||
InputStream getMessageInputStream(MessageId m);
|
|
||||||
}
|
|
||||||
@@ -35,9 +35,4 @@ public interface SyncConstants {
|
|||||||
* The maximum number of message IDs in an ack, offer or request record.
|
* The maximum number of message IDs in an ack, offer or request record.
|
||||||
*/
|
*/
|
||||||
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
|
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum length of a message block in bytes.
|
|
||||||
*/
|
|
||||||
int MAX_BLOCK_LENGTH = 32 * 2014; // 32 KiB
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.bramble.util;
|
package org.briarproject.bramble.util;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
|
||||||
public class ByteUtils {
|
public class ByteUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,15 +14,26 @@ public class ByteUtils {
|
|||||||
*/
|
*/
|
||||||
public static final long MAX_32_BIT_UNSIGNED = 4294967295L; // 2^32 - 1
|
public static final long MAX_32_BIT_UNSIGNED = 4294967295L; // 2^32 - 1
|
||||||
|
|
||||||
/** The number of bytes needed to encode a 16-bit integer. */
|
/**
|
||||||
|
* The number of bytes needed to encode a 16-bit integer.
|
||||||
|
*/
|
||||||
public static final int INT_16_BYTES = 2;
|
public static final int INT_16_BYTES = 2;
|
||||||
|
|
||||||
/** The number of bytes needed to encode a 32-bit integer. */
|
/**
|
||||||
|
* The number of bytes needed to encode a 32-bit integer.
|
||||||
|
*/
|
||||||
public static final int INT_32_BYTES = 4;
|
public static final int INT_32_BYTES = 4;
|
||||||
|
|
||||||
/** The number of bytes needed to encode a 64-bit integer. */
|
/**
|
||||||
|
* The number of bytes needed to encode a 64-bit integer.
|
||||||
|
*/
|
||||||
public static final int INT_64_BYTES = 8;
|
public static final int INT_64_BYTES = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of bytes needed to encode a variable-length integer.
|
||||||
|
*/
|
||||||
|
public static final int MAX_VARINT_BYTES = 9;
|
||||||
|
|
||||||
public static void writeUint16(int src, byte[] dest, int offset) {
|
public static void writeUint16(int src, byte[] dest, int offset) {
|
||||||
if (src < 0) throw new IllegalArgumentException();
|
if (src < 0) throw new IllegalArgumentException();
|
||||||
if (src > MAX_16_BIT_UNSIGNED) throw new IllegalArgumentException();
|
if (src > MAX_16_BIT_UNSIGNED) throw new IllegalArgumentException();
|
||||||
@@ -55,6 +68,42 @@ public class ByteUtils {
|
|||||||
dest[offset + 7] = (byte) (src & 0xFF);
|
dest[offset + 7] = (byte) (src & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes needed to represent 'src' as a
|
||||||
|
* variable-length integer.
|
||||||
|
* <p>
|
||||||
|
* 'src' must not be negative.
|
||||||
|
*/
|
||||||
|
public static int getVarIntBytes(long src) {
|
||||||
|
if (src < 0) throw new IllegalArgumentException();
|
||||||
|
int len = 1;
|
||||||
|
while ((src >>= 7) > 0) len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 'src' to 'dest' as a variable-length integer, starting at
|
||||||
|
* 'offset', and returns the number of bytes written.
|
||||||
|
* <p>
|
||||||
|
* `src` must not be negative.
|
||||||
|
*/
|
||||||
|
public static int writeVarInt(long src, byte[] dest, int offset) {
|
||||||
|
if (src < 0) throw new IllegalArgumentException();
|
||||||
|
int len = getVarIntBytes(src);
|
||||||
|
if (dest.length < offset + len) throw new IllegalArgumentException();
|
||||||
|
// Work backwards from the end
|
||||||
|
int end = offset + len - 1;
|
||||||
|
for (int i = end; i >= offset; i--) {
|
||||||
|
// Encode 7 bits
|
||||||
|
dest[i] = (byte) (src & 0x7F);
|
||||||
|
// Raise the continuation flag, except for the last byte
|
||||||
|
if (i < end) dest[i] |= (byte) 0x80;
|
||||||
|
// Shift out the bits that were encoded
|
||||||
|
src >>= 7;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
public static int readUint16(byte[] src, int offset) {
|
public static int readUint16(byte[] src, int offset) {
|
||||||
if (src.length < offset + INT_16_BYTES)
|
if (src.length < offset + INT_16_BYTES)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
@@ -83,14 +132,46 @@ public class ByteUtils {
|
|||||||
| (src[offset + 7] & 0xFFL);
|
| (src[offset + 7] & 0xFFL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int readUint(byte[] src, int bits) {
|
/**
|
||||||
if (src.length << 3 < bits) throw new IllegalArgumentException();
|
* Returns the length in bytes of a variable-length integer encoded in
|
||||||
int dest = 0;
|
* 'src' starting at 'offset'.
|
||||||
for (int i = 0; i < bits; i++) {
|
*
|
||||||
if ((src[i >> 3] & 128 >> (i & 7)) != 0) dest |= 1 << bits - i - 1;
|
* @throws FormatException if there is not a valid variable-length integer
|
||||||
|
* at the specified position.
|
||||||
|
*/
|
||||||
|
public static int getVarIntBytes(byte[] src, int offset)
|
||||||
|
throws FormatException {
|
||||||
|
if (src.length < offset) throw new IllegalArgumentException();
|
||||||
|
for (int i = 0; i < MAX_VARINT_BYTES && offset + i < src.length; i++) {
|
||||||
|
// If the continuation flag is lowered, this is the last byte
|
||||||
|
if ((src[offset + i] & 0x80) == 0) return i + 1;
|
||||||
}
|
}
|
||||||
if (dest < 0) throw new AssertionError();
|
// We've read 9 bytes or reached the end of the input without finding
|
||||||
if (dest >= 1 << bits) throw new AssertionError();
|
// the last byte
|
||||||
return dest;
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a variable-length integer from 'src' starting at 'offset' and
|
||||||
|
* returns it.
|
||||||
|
*
|
||||||
|
* @throws FormatException if there is not a valid variable-length integer
|
||||||
|
* at the specified position.
|
||||||
|
*/
|
||||||
|
public static long readVarInt(byte[] src, int offset)
|
||||||
|
throws FormatException {
|
||||||
|
if (src.length < offset) throw new IllegalArgumentException();
|
||||||
|
long dest = 0;
|
||||||
|
for (int i = 0; i < MAX_VARINT_BYTES && offset + i < src.length; i++) {
|
||||||
|
// Decode 7 bits
|
||||||
|
dest |= src[offset + i] & 0x7F;
|
||||||
|
// If the continuation flag is lowered, this is the last byte
|
||||||
|
if ((src[offset + i] & 0x80) == 0) return dest;
|
||||||
|
// Make room for the next 7 bits
|
||||||
|
dest <<= 7;
|
||||||
|
}
|
||||||
|
// We've read 9 bytes or reached the end of the input without finding
|
||||||
|
// the last byte
|
||||||
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
@@ -37,7 +36,6 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IoModule.class,
|
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.briarproject.bramble.db;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
class BlockSourceImpl implements BlockSource {
|
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
BlockSourceImpl(DatabaseComponent db) {
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockCount(MessageId m) throws DbException {
|
|
||||||
return db.transactionWithResult(true, txn -> db.getBlockCount(txn, m));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getBlock(MessageId m, int blockNumber) throws DbException {
|
|
||||||
return db.transactionWithResult(true, txn ->
|
|
||||||
db.getBlock(txn, m, blockNumber));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.DbRunnable;
|
import org.briarproject.bramble.api.db.DbRunnable;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
import org.briarproject.bramble.api.db.MigrationListener;
|
||||||
import org.briarproject.bramble.api.db.NoSuchBlockException;
|
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
||||||
@@ -421,26 +420,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockCount(Transaction transaction, MessageId m)
|
|
||||||
throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsMessage(txn, m))
|
|
||||||
throw new NoSuchMessageException();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getBlock(Transaction transaction, MessageId m,
|
|
||||||
int blockNumber) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsMessage(txn, m))
|
|
||||||
throw new NoSuchMessageException();
|
|
||||||
if (blockNumber != 0)
|
|
||||||
throw new NoSuchBlockException();
|
|
||||||
return db.getMessage(txn, m).getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Contact getContact(Transaction transaction, ContactId c)
|
public Contact getContact(Transaction transaction, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
@@ -32,9 +31,4 @@ public class DatabaseModule {
|
|||||||
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
BlockSource provideBlockSource(BlockSourceImpl blockSource) {
|
|
||||||
return blockSource;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
|
||||||
import static java.lang.Thread.currentThread;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link InputStream} that asynchronously fetches blocks of data on demand.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class BlockInputStream extends InputStream {
|
|
||||||
|
|
||||||
private final int minBufferBytes;
|
|
||||||
private final BlockingQueue<Buffer> queue = new ArrayBlockingQueue<>(1);
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Buffer buffer = null;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private int offset = 0;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean fetchingBlock = false;
|
|
||||||
|
|
||||||
abstract void fetchBlockAsync(int blockNumber);
|
|
||||||
|
|
||||||
BlockInputStream(int minBufferBytes) {
|
|
||||||
this.minBufferBytes = minBufferBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!prepareRead()) return -1;
|
|
||||||
if (buffer == null) throw new AssertionError();
|
|
||||||
return buffer.data[offset++] & 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b) throws IOException {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
if (off < 0 || len < 0 || off + len > b.length)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!prepareRead()) return -1;
|
|
||||||
if (buffer == null) throw new AssertionError();
|
|
||||||
len = Math.min(len, buffer.length - offset);
|
|
||||||
if (len < 0) throw new AssertionError();
|
|
||||||
arraycopy(buffer.data, offset, b, off, len);
|
|
||||||
offset += len;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean prepareRead() throws IOException {
|
|
||||||
throwExceptionIfNecessary();
|
|
||||||
if (isEndOfStream()) return false;
|
|
||||||
if (shouldFetchBlock()) fetchBlockAsync();
|
|
||||||
waitForBlock();
|
|
||||||
if (buffer == null) throw new AssertionError();
|
|
||||||
return offset < buffer.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private void throwExceptionIfNecessary() throws IOException {
|
|
||||||
if (buffer != null && buffer.exception != null)
|
|
||||||
throw new IOException(buffer.exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean isEndOfStream() {
|
|
||||||
return buffer != null && offset == buffer.length && !fetchingBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean shouldFetchBlock() {
|
|
||||||
if (fetchingBlock) return false;
|
|
||||||
if (buffer == null) return true;
|
|
||||||
if (buffer.length == 0) return false;
|
|
||||||
return buffer.length - offset < minBufferBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private void fetchBlockAsync() {
|
|
||||||
if (buffer == null) fetchBlockAsync(0);
|
|
||||||
else fetchBlockAsync(buffer.blockNumber + 1);
|
|
||||||
fetchingBlock = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private void waitForBlock() throws IOException {
|
|
||||||
if (buffer != null && offset < buffer.length) return;
|
|
||||||
try {
|
|
||||||
buffer = queue.take();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
currentThread().interrupt();
|
|
||||||
throw new InterruptedIOException();
|
|
||||||
}
|
|
||||||
fetchingBlock = false;
|
|
||||||
offset = 0;
|
|
||||||
throwExceptionIfNecessary();
|
|
||||||
}
|
|
||||||
|
|
||||||
void fetchSucceeded(int blockNumber, byte[] data, int length) {
|
|
||||||
queue.add(new Buffer(blockNumber, data, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fetchFailed(int blockNumber, Exception exception) {
|
|
||||||
queue.add(new Buffer(blockNumber, exception));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Buffer {
|
|
||||||
|
|
||||||
private final int blockNumber;
|
|
||||||
private final byte[] data;
|
|
||||||
private final int length;
|
|
||||||
@Nullable
|
|
||||||
private final Exception exception;
|
|
||||||
|
|
||||||
private Buffer(int blockNumber, byte[] data, int length) {
|
|
||||||
if (length < 0 || length > data.length)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
this.blockNumber = blockNumber;
|
|
||||||
this.data = data;
|
|
||||||
this.length = length;
|
|
||||||
exception = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Buffer(int blockNumber, Exception exception) {
|
|
||||||
this.blockNumber = blockNumber;
|
|
||||||
this.exception = exception;
|
|
||||||
data = new byte[0];
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link BlockInputStream} that fetches data from a {@link BlockSource}.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class BlockSourceInputStream extends BlockInputStream {
|
|
||||||
|
|
||||||
private final Executor executor;
|
|
||||||
private final BlockSource blockSource;
|
|
||||||
private final MessageId messageId;
|
|
||||||
|
|
||||||
private volatile int blockCount = -1;
|
|
||||||
|
|
||||||
BlockSourceInputStream(int minBufferBytes, Executor executor,
|
|
||||||
BlockSource blockSource, MessageId messageId) {
|
|
||||||
super(minBufferBytes);
|
|
||||||
this.executor = executor;
|
|
||||||
this.blockSource = blockSource;
|
|
||||||
this.messageId = messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void fetchBlockAsync(int blockNumber) {
|
|
||||||
executor.execute(() -> {
|
|
||||||
try {
|
|
||||||
if (blockCount == -1) {
|
|
||||||
blockCount = blockSource.getBlockCount(messageId);
|
|
||||||
}
|
|
||||||
if (blockNumber > blockCount) {
|
|
||||||
fetchFailed(blockNumber, new IllegalArgumentException());
|
|
||||||
} else if (blockNumber == blockCount) {
|
|
||||||
fetchSucceeded(blockNumber, new byte[0], 0); // EOF
|
|
||||||
} else {
|
|
||||||
byte[] block = blockSource.getBlock(messageId, blockNumber);
|
|
||||||
fetchSucceeded(blockNumber, block, block.length);
|
|
||||||
}
|
|
||||||
} catch (DbException e) {
|
|
||||||
fetchFailed(blockNumber, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class IoModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
MessageInputStreamFactory provideMessageInputStreamFactory(
|
|
||||||
MessageInputStreamFactoryImpl messageInputStreamFactory) {
|
|
||||||
return messageInputStreamFactory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
|
||||||
|
|
||||||
class MessageInputStreamFactoryImpl implements MessageInputStreamFactory {
|
|
||||||
|
|
||||||
private final Executor dbExecutor;
|
|
||||||
private final BlockSource blockSource;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MessageInputStreamFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
|
||||||
BlockSource blockSource) {
|
|
||||||
this.dbExecutor = dbExecutor;
|
|
||||||
this.blockSource = blockSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getMessageInputStream(MessageId m) {
|
|
||||||
return new BlockSourceInputStream(MAX_BLOCK_LENGTH, dbExecutor,
|
|
||||||
blockSource, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.jmock.lib.concurrent.Synchroniser;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.spongycastle.util.Arrays.copyOfRange;
|
|
||||||
|
|
||||||
public class BlockSourceInputStreamTest extends BrambleMockTestCase {
|
|
||||||
|
|
||||||
private static final int MAX_DATA_BYTES = 1_000_000;
|
|
||||||
private static final int READ_BUFFER_BYTES = 4 * 1024;
|
|
||||||
|
|
||||||
private final BlockSource blockSource;
|
|
||||||
|
|
||||||
private final Random random = new Random();
|
|
||||||
private final Executor executor = newSingleThreadExecutor();
|
|
||||||
private final MessageId messageId = new MessageId(getRandomId());
|
|
||||||
|
|
||||||
public BlockSourceInputStreamTest() {
|
|
||||||
context.setThreadingPolicy(new Synchroniser());
|
|
||||||
blockSource = context.mock(BlockSource.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadSingleBytes() throws IOException {
|
|
||||||
byte[] data = createRandomData();
|
|
||||||
BlockSource source = new ByteArrayBlockSource(data, MAX_BLOCK_LENGTH);
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
source, messageId);
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
//noinspection ForLoopReplaceableByForEach
|
|
||||||
for (int i = 0; i < data.length; i++) {
|
|
||||||
int read = in.read();
|
|
||||||
assertNotEquals(-1, read);
|
|
||||||
out.write(read);
|
|
||||||
}
|
|
||||||
assertEquals(-1, in.read());
|
|
||||||
in.close();
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertArrayEquals(data, out.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadByteArrays() throws IOException {
|
|
||||||
byte[] data = createRandomData();
|
|
||||||
BlockSource source = new ByteArrayBlockSource(data, MAX_BLOCK_LENGTH);
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
source, messageId);
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
byte[] buf = new byte[READ_BUFFER_BYTES];
|
|
||||||
int dataOffset = 0;
|
|
||||||
while (dataOffset < data.length) {
|
|
||||||
int length = Math.min(random.nextInt(buf.length) + 1,
|
|
||||||
data.length - dataOffset);
|
|
||||||
int bufOffset = 0;
|
|
||||||
if (length < buf.length)
|
|
||||||
bufOffset = random.nextInt(buf.length - length);
|
|
||||||
int read = in.read(buf, bufOffset, length);
|
|
||||||
assertNotEquals(-1, read);
|
|
||||||
out.write(buf, bufOffset, read);
|
|
||||||
dataOffset += read;
|
|
||||||
}
|
|
||||||
assertEquals(-1, in.read(buf, 0, 0));
|
|
||||||
in.close();
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertArrayEquals(data, out.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IOException.class)
|
|
||||||
public void testDbExceptionFromGetBlockCountIsRethrown() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(blockSource).getBlockCount(messageId);
|
|
||||||
will(throwException(new DbException()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
blockSource, messageId);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
in.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IOException.class)
|
|
||||||
public void testDbExceptionFromGetBlockIsRethrown() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(blockSource).getBlockCount(messageId);
|
|
||||||
will(returnValue(1));
|
|
||||||
oneOf(blockSource).getBlock(messageId, 0);
|
|
||||||
will(throwException(new DbException()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
blockSource, messageId);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
in.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadFullBlockAtEndOfMessage() throws Exception {
|
|
||||||
testReadBlockAtEndOfMessage(MAX_BLOCK_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadPartialBlockAtEndOfMessage() throws Exception {
|
|
||||||
testReadBlockAtEndOfMessage(MAX_BLOCK_LENGTH - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testReadBlockAtEndOfMessage(int blockLength) throws Exception {
|
|
||||||
byte[] block = new byte[blockLength];
|
|
||||||
random.nextBytes(block);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(blockSource).getBlockCount(messageId);
|
|
||||||
will(returnValue(1));
|
|
||||||
oneOf(blockSource).getBlock(messageId, 0);
|
|
||||||
will(returnValue(block));
|
|
||||||
}});
|
|
||||||
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
blockSource, messageId);
|
|
||||||
byte[] buf = new byte[MAX_BLOCK_LENGTH * 2];
|
|
||||||
assertEquals(block.length, in.read(buf, 0, buf.length));
|
|
||||||
assertArrayEquals(block, copyOfRange(buf, 0, block.length));
|
|
||||||
assertEquals(-1, in.read(buf, 0, buf.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createRandomData() {
|
|
||||||
int length = random.nextInt(MAX_DATA_BYTES) + 1;
|
|
||||||
byte[] data = new byte[length];
|
|
||||||
random.nextBytes(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
|
||||||
|
|
||||||
class ByteArrayBlockSource implements BlockSource {
|
|
||||||
|
|
||||||
private final byte[] data;
|
|
||||||
private final int blockBytes;
|
|
||||||
|
|
||||||
ByteArrayBlockSource(byte[] data, int blockBytes) {
|
|
||||||
this.data = data;
|
|
||||||
this.blockBytes = blockBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockCount(MessageId m) {
|
|
||||||
return (data.length + blockBytes - 1) / blockBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getBlock(MessageId m, int blockNumber) {
|
|
||||||
int offset = blockNumber * blockBytes;
|
|
||||||
if (offset >= data.length) throw new IllegalArgumentException();
|
|
||||||
int length = Math.min(blockBytes, data.length - offset);
|
|
||||||
byte[] block = new byte[length];
|
|
||||||
arraycopy(data, offset, block, 0, length);
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +1,135 @@
|
|||||||
package org.briarproject.bramble.util;
|
package org.briarproject.bramble.util;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static java.util.Arrays.fill;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.MAX_VARINT_BYTES;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.getVarIntBytes;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.readUint16;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.readUint32;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.readUint64;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.readVarInt;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.writeUint16;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.writeUint32;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.writeUint64;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.writeVarInt;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public class ByteUtilsTest extends BrambleTestCase {
|
public class ByteUtilsTest extends BrambleTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadUint16() {
|
public void testReadUint16() {
|
||||||
byte[] b = StringUtils.fromHexString("00000000");
|
byte[] b = fromHexString("00000000");
|
||||||
assertEquals(0, ByteUtils.readUint16(b, 1));
|
assertEquals(0, readUint16(b, 1));
|
||||||
b = StringUtils.fromHexString("00000100");
|
b = fromHexString("00000100");
|
||||||
assertEquals(1, ByteUtils.readUint16(b, 1));
|
assertEquals(1, readUint16(b, 1));
|
||||||
b = StringUtils.fromHexString("007FFF00");
|
b = fromHexString("007FFF00");
|
||||||
assertEquals(Short.MAX_VALUE, ByteUtils.readUint16(b, 1));
|
assertEquals(Short.MAX_VALUE, readUint16(b, 1));
|
||||||
b = StringUtils.fromHexString("00FFFF00");
|
b = fromHexString("00FFFF00");
|
||||||
assertEquals(65535, ByteUtils.readUint16(b, 1));
|
assertEquals(65535, readUint16(b, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testReadUint16ValidatesArguments1() {
|
public void testReadUint16ValidatesArguments1() {
|
||||||
ByteUtils.readUint16(new byte[1], 0);
|
readUint16(new byte[1], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testReadUint16ValidatesArguments2() {
|
public void testReadUint16ValidatesArguments2() {
|
||||||
ByteUtils.readUint16(new byte[2], 1);
|
readUint16(new byte[2], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadUint32() {
|
public void testReadUint32() {
|
||||||
byte[] b = StringUtils.fromHexString("000000000000");
|
byte[] b = fromHexString("000000000000");
|
||||||
assertEquals(0, ByteUtils.readUint32(b, 1));
|
assertEquals(0, readUint32(b, 1));
|
||||||
b = StringUtils.fromHexString("000000000100");
|
b = fromHexString("000000000100");
|
||||||
assertEquals(1, ByteUtils.readUint32(b, 1));
|
assertEquals(1, readUint32(b, 1));
|
||||||
b = StringUtils.fromHexString("007FFFFFFF00");
|
b = fromHexString("007FFFFFFF00");
|
||||||
assertEquals(Integer.MAX_VALUE, ByteUtils.readUint32(b, 1));
|
assertEquals(Integer.MAX_VALUE, readUint32(b, 1));
|
||||||
b = StringUtils.fromHexString("00FFFFFFFF00");
|
b = fromHexString("00FFFFFFFF00");
|
||||||
assertEquals(4294967295L, ByteUtils.readUint32(b, 1));
|
assertEquals(4294967295L, readUint32(b, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testReadUint32ValidatesArguments1() {
|
public void testReadUint32ValidatesArguments1() {
|
||||||
ByteUtils.readUint32(new byte[3], 0);
|
readUint32(new byte[3], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testReadUint32ValidatesArguments2() {
|
public void testReadUint32ValidatesArguments2() {
|
||||||
ByteUtils.readUint32(new byte[4], 1);
|
readUint32(new byte[4], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadUint64() {
|
public void testReadUint64() {
|
||||||
byte[] b = StringUtils.fromHexString("00000000000000000000");
|
byte[] b = fromHexString("00000000000000000000");
|
||||||
assertEquals(0L, ByteUtils.readUint64(b, 1));
|
assertEquals(0L, readUint64(b, 1));
|
||||||
b = StringUtils.fromHexString("00000000000000000100");
|
b = fromHexString("00000000000000000100");
|
||||||
assertEquals(1L, ByteUtils.readUint64(b, 1));
|
assertEquals(1L, readUint64(b, 1));
|
||||||
b = StringUtils.fromHexString("007FFFFFFFFFFFFFFF00");
|
b = fromHexString("007FFFFFFFFFFFFFFF00");
|
||||||
assertEquals(Long.MAX_VALUE, ByteUtils.readUint64(b, 1));
|
assertEquals(Long.MAX_VALUE, readUint64(b, 1));
|
||||||
b = StringUtils.fromHexString("00800000000000000000");
|
b = fromHexString("00800000000000000000");
|
||||||
assertEquals(Long.MIN_VALUE, ByteUtils.readUint64(b, 1));
|
assertEquals(Long.MIN_VALUE, readUint64(b, 1));
|
||||||
b = StringUtils.fromHexString("00FFFFFFFFFFFFFFFF00");
|
b = fromHexString("00FFFFFFFFFFFFFFFF00");
|
||||||
assertEquals(-1L, ByteUtils.readUint64(b, 1));
|
assertEquals(-1L, readUint64(b, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testReadUint64ValidatesArguments1() {
|
public void testReadUint64ValidatesArguments1() {
|
||||||
ByteUtils.readUint64(new byte[7], 0);
|
readUint64(new byte[7], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testReadUint64ValidatesArguments2() {
|
public void testReadUint64ValidatesArguments2() {
|
||||||
ByteUtils.readUint64(new byte[8], 1);
|
readUint64(new byte[8], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint16() {
|
public void testWriteUint16() {
|
||||||
byte[] b = new byte[4];
|
byte[] b = new byte[4];
|
||||||
ByteUtils.writeUint16(0, b, 1);
|
writeUint16(0, b, 1);
|
||||||
assertEquals("00000000", StringUtils.toHexString(b));
|
assertEquals("00000000", toHexString(b));
|
||||||
ByteUtils.writeUint16(1, b, 1);
|
writeUint16(1, b, 1);
|
||||||
assertEquals("00000100", StringUtils.toHexString(b));
|
assertEquals("00000100", toHexString(b));
|
||||||
ByteUtils.writeUint16(Short.MAX_VALUE, b, 1);
|
writeUint16(Short.MAX_VALUE, b, 1);
|
||||||
assertEquals("007FFF00", StringUtils.toHexString(b));
|
assertEquals("007FFF00", toHexString(b));
|
||||||
ByteUtils.writeUint16(MAX_16_BIT_UNSIGNED, b, 1);
|
writeUint16(MAX_16_BIT_UNSIGNED, b, 1);
|
||||||
assertEquals("00FFFF00", StringUtils.toHexString(b));
|
assertEquals("00FFFF00", toHexString(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint16ValidatesArguments() {
|
public void testWriteUint16ValidatesArguments() {
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint16(0, new byte[1], 0);
|
writeUint16(0, new byte[1], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint16(0, new byte[2], 1);
|
writeUint16(0, new byte[2], 1);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint16(-1, new byte[2], 0);
|
writeUint16(-1, new byte[2], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint16(MAX_16_BIT_UNSIGNED + 1, new byte[2], 0);
|
writeUint16(MAX_16_BIT_UNSIGNED + 1, new byte[2], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -122,38 +139,38 @@ public class ByteUtilsTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWriteUint32() {
|
public void testWriteUint32() {
|
||||||
byte[] b = new byte[6];
|
byte[] b = new byte[6];
|
||||||
ByteUtils.writeUint32(0, b, 1);
|
writeUint32(0, b, 1);
|
||||||
assertEquals("000000000000", StringUtils.toHexString(b));
|
assertEquals("000000000000", toHexString(b));
|
||||||
ByteUtils.writeUint32(1, b, 1);
|
writeUint32(1, b, 1);
|
||||||
assertEquals("000000000100", StringUtils.toHexString(b));
|
assertEquals("000000000100", toHexString(b));
|
||||||
ByteUtils.writeUint32(Integer.MAX_VALUE, b, 1);
|
writeUint32(Integer.MAX_VALUE, b, 1);
|
||||||
assertEquals("007FFFFFFF00", StringUtils.toHexString(b));
|
assertEquals("007FFFFFFF00", toHexString(b));
|
||||||
ByteUtils.writeUint32(MAX_32_BIT_UNSIGNED, b, 1);
|
writeUint32(MAX_32_BIT_UNSIGNED, b, 1);
|
||||||
assertEquals("00FFFFFFFF00", StringUtils.toHexString(b));
|
assertEquals("00FFFFFFFF00", toHexString(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint32ValidatesArguments() {
|
public void testWriteUint32ValidatesArguments() {
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint32(0, new byte[3], 0);
|
writeUint32(0, new byte[3], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint32(0, new byte[4], 1);
|
writeUint32(0, new byte[4], 1);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint32(-1, new byte[4], 0);
|
writeUint32(-1, new byte[4], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint32(MAX_32_BIT_UNSIGNED + 1, new byte[4], 0);
|
writeUint32(MAX_32_BIT_UNSIGNED + 1, new byte[4], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -163,30 +180,30 @@ public class ByteUtilsTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWriteUint64() {
|
public void testWriteUint64() {
|
||||||
byte[] b = new byte[10];
|
byte[] b = new byte[10];
|
||||||
ByteUtils.writeUint64(0, b, 1);
|
writeUint64(0, b, 1);
|
||||||
assertEquals("00000000000000000000", StringUtils.toHexString(b));
|
assertEquals("00000000000000000000", toHexString(b));
|
||||||
ByteUtils.writeUint64(1, b, 1);
|
writeUint64(1, b, 1);
|
||||||
assertEquals("00000000000000000100", StringUtils.toHexString(b));
|
assertEquals("00000000000000000100", toHexString(b));
|
||||||
ByteUtils.writeUint64(Long.MAX_VALUE, b, 1);
|
writeUint64(Long.MAX_VALUE, b, 1);
|
||||||
assertEquals("007FFFFFFFFFFFFFFF00", StringUtils.toHexString(b));
|
assertEquals("007FFFFFFFFFFFFFFF00", toHexString(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint64ValidatesArguments() {
|
public void testWriteUint64ValidatesArguments() {
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint64(0, new byte[7], 0);
|
writeUint64(0, new byte[7], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint64(0, new byte[8], 1);
|
writeUint64(0, new byte[8], 1);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ByteUtils.writeUint64(-1, new byte[8], 0);
|
writeUint64(-1, new byte[8], 0);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -194,17 +211,170 @@ public class ByteUtilsTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadUint() {
|
public void testGetVarIntBytesToWrite() {
|
||||||
byte[] b = new byte[1];
|
assertEquals(1, getVarIntBytes(0));
|
||||||
b[0] = (byte) 128;
|
assertEquals(1, getVarIntBytes(0x7F)); // Max 7-bit int
|
||||||
for (int i = 0; i < 8; i++) {
|
assertEquals(2, getVarIntBytes(0x7F + 1));
|
||||||
assertEquals(1 << i, ByteUtils.readUint(b, i + 1));
|
assertEquals(2, getVarIntBytes(0x3FFF)); // Max 14-bit int
|
||||||
}
|
assertEquals(3, getVarIntBytes(0x3FFF + 1));
|
||||||
b = new byte[2];
|
assertEquals(3, getVarIntBytes(0x1FFFFF)); // Max 21-bit int
|
||||||
for (int i = 0; i < 65535; i++) {
|
assertEquals(4, getVarIntBytes(0x1FFFFF + 1));
|
||||||
ByteUtils.writeUint16(i, b, 0);
|
assertEquals(4, getVarIntBytes(0xFFFFFFF)); // Max 28-bit int
|
||||||
assertEquals(i, ByteUtils.readUint(b, 16));
|
assertEquals(5, getVarIntBytes(0xFFFFFFF + 1));
|
||||||
assertEquals(i >> 1, ByteUtils.readUint(b, 15));
|
assertEquals(5, getVarIntBytes(0x7FFFFFFFFL)); // Max 35-bit int
|
||||||
|
assertEquals(6, getVarIntBytes(0x7FFFFFFFFL + 1));
|
||||||
|
assertEquals(6, getVarIntBytes(0x3FFFFFFFFFFL)); // Max 42-bit int
|
||||||
|
assertEquals(7, getVarIntBytes(0x3FFFFFFFFFFL + 1));
|
||||||
|
assertEquals(7, getVarIntBytes(0x1FFFFFFFFFFFFL)); // Max 49-bit int
|
||||||
|
assertEquals(8, getVarIntBytes(0x1FFFFFFFFFFFFL + 1));
|
||||||
|
assertEquals(8, getVarIntBytes(0xFFFFFFFFFFFFFFL)); // Max 56-bit int
|
||||||
|
assertEquals(9, getVarIntBytes(0xFFFFFFFFFFFFFFL + 1));
|
||||||
|
assertEquals(9, getVarIntBytes(0x7FFFFFFFFFFFFFFFL)); // Max 63-bit int
|
||||||
|
assertEquals(MAX_VARINT_BYTES, getVarIntBytes(Long.MAX_VALUE)); // Same
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteVarInt() {
|
||||||
|
testWriteVarInt(0, 1, "00");
|
||||||
|
testWriteVarInt(1, 1, "01");
|
||||||
|
testWriteVarInt(0x7F, 1, "7F"); // Max 7-bit int
|
||||||
|
testWriteVarInt(0x7F + 1, 2, "8100");
|
||||||
|
testWriteVarInt(0x3FFF, 2, "FF7F"); // Max 14-bit int
|
||||||
|
testWriteVarInt(0x3FFF + 1, 3, "818000");
|
||||||
|
testWriteVarInt(0x1FFFFF, 3, "FFFF7F"); // Max 21-bit int
|
||||||
|
testWriteVarInt(0x1FFFFF + 1, 4, "81808000");
|
||||||
|
testWriteVarInt(0xFFFFFFF, 4, "FFFFFF7F"); // Max 28-bit int
|
||||||
|
testWriteVarInt(0xFFFFFFF + 1, 5, "8180808000");
|
||||||
|
testWriteVarInt(0x7FFFFFFFFL, 5, "FFFFFFFF7F"); // Max 35-bit int
|
||||||
|
testWriteVarInt(0x7FFFFFFFFL + 1, 6, "818080808000");
|
||||||
|
testWriteVarInt(0x3FFFFFFFFFFL, 6, "FFFFFFFFFF7F"); // Max 42-bit int
|
||||||
|
testWriteVarInt(0x3FFFFFFFFFFL + 1, 7, "81808080808000");
|
||||||
|
testWriteVarInt(0x1FFFFFFFFFFFFL, 7, "FFFFFFFFFFFF7F"); // Max 49
|
||||||
|
testWriteVarInt(0x1FFFFFFFFFFFFL + 1, 8, "8180808080808000");
|
||||||
|
testWriteVarInt(0xFFFFFFFFFFFFFFL, 8, "FFFFFFFFFFFFFF7F"); // Max 56
|
||||||
|
testWriteVarInt(0xFFFFFFFFFFFFFFL + 1, 9, "818080808080808000");
|
||||||
|
testWriteVarInt(0x7FFFFFFFFFFFFFFFL, 9, "FFFFFFFFFFFFFFFF7F"); // Max 63
|
||||||
|
testWriteVarInt(Long.MAX_VALUE, MAX_VARINT_BYTES, "FFFFFFFFFFFFFFFF7F");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testWriteVarInt(long src, int len, String destHex) {
|
||||||
|
byte[] dest = new byte[9];
|
||||||
|
assertEquals(len, writeVarInt(src, dest, 0));
|
||||||
|
assertEquals(destHex, toHexString(dest).substring(0, len * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetVarIntBytesToRead() throws FormatException {
|
||||||
|
testGetVarIntBytesToRead(1, "00", 0);
|
||||||
|
testGetVarIntBytesToRead(1, "01", 0);
|
||||||
|
testGetVarIntBytesToRead(1, "7F", 0); // Max 7-bit int
|
||||||
|
testGetVarIntBytesToRead(2, "8100", 0);
|
||||||
|
testGetVarIntBytesToRead(2, "FF7F", 0); // Max 14-bit int
|
||||||
|
testGetVarIntBytesToRead(3, "818000", 0);
|
||||||
|
testGetVarIntBytesToRead(3, "FFFF7F", 0); // Max 21-bit int
|
||||||
|
testGetVarIntBytesToRead(4, "81808000", 0);
|
||||||
|
testGetVarIntBytesToRead(4, "FFFFFF7F", 0); // Max 28-bit int
|
||||||
|
testGetVarIntBytesToRead(5, "8180808000", 0);
|
||||||
|
testGetVarIntBytesToRead(5, "FFFFFFFF7F", 0); // Max 35-bit int
|
||||||
|
testGetVarIntBytesToRead(6, "818080808000", 0);
|
||||||
|
testGetVarIntBytesToRead(6, "FFFFFFFFFF7F", 0); // Max 42-bit int
|
||||||
|
testGetVarIntBytesToRead(7, "81808080808000", 0);
|
||||||
|
testGetVarIntBytesToRead(7, "FFFFFFFFFFFF7F", 0); // Max 49-bit int
|
||||||
|
testGetVarIntBytesToRead(8, "8180808080808000", 0);
|
||||||
|
testGetVarIntBytesToRead(8, "FFFFFFFFFFFFFF7F", 0); // Max 56-bit int
|
||||||
|
testGetVarIntBytesToRead(9, "818080808080808000", 0);
|
||||||
|
testGetVarIntBytesToRead(9, "FFFFFFFFFFFFFFFF7F", 0); // Max 63-bit int
|
||||||
|
// Start at offset, ignore trailing data
|
||||||
|
testGetVarIntBytesToRead(1, "FF0000", 1);
|
||||||
|
testGetVarIntBytesToRead(9, "00FFFFFFFFFFFFFFFF7F00", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGetVarIntBytesToRead(int len, String srcHex, int offset)
|
||||||
|
throws FormatException {
|
||||||
|
assertEquals(len, getVarIntBytes(fromHexString(srcHex), offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testGetVarIntBytesToReadThrowsExceptionAtEndOfInput()
|
||||||
|
throws FormatException {
|
||||||
|
byte[] src = new byte[MAX_VARINT_BYTES - 1];
|
||||||
|
fill(src, (byte) 0xFF);
|
||||||
|
// Reaches end of input without finding lowered continuation flag
|
||||||
|
getVarIntBytes(src, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testGetVarIntBytesToReadThrowsExceptionAfterNineBytes()
|
||||||
|
throws FormatException {
|
||||||
|
byte[] src = new byte[MAX_VARINT_BYTES];
|
||||||
|
fill(src, (byte) 0xFF);
|
||||||
|
// Reaches max length without finding lowered continuation flag
|
||||||
|
getVarIntBytes(src, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadVarInt() throws FormatException {
|
||||||
|
testReadVarInt(0, "00", 0);
|
||||||
|
testReadVarInt(1, "01", 0);
|
||||||
|
testReadVarInt(0x7F, "7F", 0); // Max 7-bit int
|
||||||
|
testReadVarInt(0x7F + 1, "8100", 0);
|
||||||
|
testReadVarInt(0x3FFF, "FF7F", 0); // Max 14-bit int
|
||||||
|
testReadVarInt(0x3FFF + 1, "818000", 0);
|
||||||
|
testReadVarInt(0x1FFFFF, "FFFF7F", 0); // Max 21-bit int
|
||||||
|
testReadVarInt(0x1FFFFF + 1, "81808000", 0);
|
||||||
|
testReadVarInt(0xFFFFFFF, "FFFFFF7F", 0); // Max 28-bit int
|
||||||
|
testReadVarInt(0xFFFFFFF + 1, "8180808000", 0);
|
||||||
|
testReadVarInt(0x7FFFFFFFFL, "FFFFFFFF7F", 0); // Max 35-bit int
|
||||||
|
testReadVarInt(0x7FFFFFFFFL + 1, "818080808000", 0);
|
||||||
|
testReadVarInt(0x3FFFFFFFFFFL, "FFFFFFFFFF7F", 0); // Max 42-bit int
|
||||||
|
testReadVarInt(0x3FFFFFFFFFFL + 1, "81808080808000", 0);
|
||||||
|
testReadVarInt(0x1FFFFFFFFFFFFL, "FFFFFFFFFFFF7F", 0); // Max 49-bit int
|
||||||
|
testReadVarInt(0x1FFFFFFFFFFFFL + 1, "8180808080808000", 0);
|
||||||
|
testReadVarInt(0xFFFFFFFFFFFFFFL, "FFFFFFFFFFFFFF7F", 0); // Max 56
|
||||||
|
testReadVarInt(0xFFFFFFFFFFFFFFL + 1, "818080808080808000", 0);
|
||||||
|
testReadVarInt(0x7FFFFFFFFFFFFFFFL, "FFFFFFFFFFFFFFFF7F", 0); // Max 63
|
||||||
|
testReadVarInt(Long.MAX_VALUE, "FFFFFFFFFFFFFFFF7F", 0);
|
||||||
|
// Start at offset, ignore trailing data
|
||||||
|
testReadVarInt(0, "FF0000", 1);
|
||||||
|
testReadVarInt(Long.MAX_VALUE, "00FFFFFFFFFFFFFFFF7F00", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testReadVarInt(long dest, String srcHex, int offset)
|
||||||
|
throws FormatException {
|
||||||
|
assertEquals(dest, readVarInt(fromHexString(srcHex), offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadVarIntThrowsExceptionAtEndOfInput()
|
||||||
|
throws FormatException {
|
||||||
|
byte[] src = new byte[MAX_VARINT_BYTES - 1];
|
||||||
|
fill(src, (byte) 0xFF);
|
||||||
|
// Reaches end of input without finding lowered continuation flag
|
||||||
|
readVarInt(src, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testReadVarIntThrowsExceptionAfterNineBytes()
|
||||||
|
throws FormatException {
|
||||||
|
byte[] src = new byte[MAX_VARINT_BYTES];
|
||||||
|
fill(src, (byte) 0xFF);
|
||||||
|
// Reaches max length without finding lowered continuation flag
|
||||||
|
readVarInt(src, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteAndReadVarInt() throws FormatException {
|
||||||
|
Random random = new Random();
|
||||||
|
int padding = 10;
|
||||||
|
byte[] buf = new byte[MAX_VARINT_BYTES + padding];
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
long src = random.nextLong() & 0x7FFFFFFFFFFFFFFFL; // Non-negative
|
||||||
|
int offset = random.nextInt(padding);
|
||||||
|
int len = getVarIntBytes(src);
|
||||||
|
assertEquals(len, writeVarInt(src, buf, offset));
|
||||||
|
assertEquals(len, getVarIntBytes(buf, offset));
|
||||||
|
assertEquals(src, readVarInt(buf, offset));
|
||||||
|
fill(buf, (byte) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import org.briarproject.briar.api.messaging.PrivateMessage;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class ThreadedMessage extends PrivateMessage {
|
public abstract class ThreadedMessage extends PrivateMessage {
|
||||||
@@ -21,7 +19,7 @@ public abstract class ThreadedMessage extends PrivateMessage {
|
|||||||
|
|
||||||
public ThreadedMessage(Message message, @Nullable MessageId parent,
|
public ThreadedMessage(Message message, @Nullable MessageId parent,
|
||||||
Author author) {
|
Author author) {
|
||||||
super(message, emptyList());
|
super(message);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.author = author;
|
this.author = author;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package org.briarproject.briar.api.messaging;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -12,18 +10,13 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
public class PrivateMessage {
|
public class PrivateMessage {
|
||||||
|
|
||||||
private final Message message;
|
private final Message message;
|
||||||
private final List<AttachmentHeader> attachments;
|
|
||||||
|
|
||||||
public PrivateMessage(Message message, List<AttachmentHeader> attachments) {
|
public PrivateMessage(Message message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.attachments = attachments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message getMessage() {
|
public Message getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AttachmentHeader> getAttachmentHeaders() {
|
|
||||||
return attachments;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,13 @@ import org.briarproject.bramble.api.data.BdfList;
|
|||||||
import org.briarproject.bramble.api.data.MetadataParser;
|
import org.briarproject.bramble.api.data.MetadataParser;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Client;
|
import org.briarproject.bramble.api.sync.Client;
|
||||||
import org.briarproject.bramble.api.sync.Group;
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
import org.briarproject.bramble.api.sync.Group.Visibility;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
@@ -35,17 +32,16 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
|||||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
|
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
|
||||||
import org.briarproject.briar.client.ConversationClientImpl;
|
import org.briarproject.briar.client.ConversationClientImpl;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
|
||||||
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -55,21 +51,15 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
private final ClientVersioningManager clientVersioningManager;
|
private final ClientVersioningManager clientVersioningManager;
|
||||||
private final ContactGroupFactory contactGroupFactory;
|
private final ContactGroupFactory contactGroupFactory;
|
||||||
private final MessageFactory messageFactory;
|
|
||||||
private final MessageInputStreamFactory messageInputStreamFactory;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||||
ClientVersioningManager clientVersioningManager,
|
ClientVersioningManager clientVersioningManager,
|
||||||
MetadataParser metadataParser, MessageTracker messageTracker,
|
MetadataParser metadataParser, MessageTracker messageTracker,
|
||||||
ContactGroupFactory contactGroupFactory,
|
ContactGroupFactory contactGroupFactory) {
|
||||||
MessageFactory messageFactory,
|
|
||||||
MessageInputStreamFactory messageInputStreamFactory) {
|
|
||||||
super(db, clientHelper, metadataParser, messageTracker);
|
super(db, clientHelper, metadataParser, messageTracker);
|
||||||
this.clientVersioningManager = clientVersioningManager;
|
this.clientVersioningManager = clientVersioningManager;
|
||||||
this.contactGroupFactory = contactGroupFactory;
|
this.contactGroupFactory = contactGroupFactory;
|
||||||
this.messageFactory = messageFactory;
|
|
||||||
this.messageInputStreamFactory = messageInputStreamFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,11 +142,9 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
meta.put("read", true);
|
meta.put("read", true);
|
||||||
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
|
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
|
||||||
messageTracker.trackOutgoingMessage(txn, m.getMessage());
|
messageTracker.trackOutgoingMessage(txn, m.getMessage());
|
||||||
for (AttachmentHeader h : m.getAttachmentHeaders())
|
|
||||||
db.setMessageShared(txn, h.getMessageId());
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new AssertionError(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
@@ -164,16 +152,11 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp,
|
public AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp,
|
||||||
String contentType, ByteBuffer data) throws DbException {
|
String contentType, ByteBuffer data) {
|
||||||
// TODO: Remove this restriction when large messages are supported
|
// TODO add real implementation
|
||||||
byte[] body = data.array();
|
byte[] b = new byte[MessageId.LENGTH];
|
||||||
if (body.length > MAX_MESSAGE_LENGTH) throw new DbException();
|
new Random().nextBytes(b);
|
||||||
// TODO: Store message type and content type
|
return new AttachmentHeader(new MessageId(b), "image/png");
|
||||||
Message m = messageFactory.createMessage(groupId, timestamp, body);
|
|
||||||
Metadata meta = new Metadata();
|
|
||||||
// Attachment will be shared when private message is added
|
|
||||||
db.transaction(false, txn -> db.addLocalMessage(txn, m, meta, false));
|
|
||||||
return new AttachmentHeader(m.getId(), contentType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContactId getContactId(Transaction txn, GroupId g)
|
private ContactId getContactId(Transaction txn, GroupId g)
|
||||||
@@ -253,9 +236,8 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attachment getAttachment(MessageId m) {
|
public Attachment getAttachment(MessageId m) {
|
||||||
InputStream in = messageInputStreamFactory.getMessageInputStream(m);
|
// TODO add real implementation
|
||||||
// TODO: Read message type and content type
|
throw new IllegalStateException("Not yet implemented");
|
||||||
return new Attachment(in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ class PrivateMessageFactoryImpl implements PrivateMessageFactory {
|
|||||||
// Serialise the message
|
// Serialise the message
|
||||||
BdfList message = BdfList.of(text);
|
BdfList message = BdfList.of(text);
|
||||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||||
return new PrivateMessage(m, attachments);
|
return new PrivateMessage(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.briarproject.bramble.data.DataModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
import org.briarproject.bramble.record.RecordModule;
|
||||||
@@ -51,7 +50,6 @@ import dagger.Component;
|
|||||||
GroupInvitationModule.class,
|
GroupInvitationModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IntroductionModule.class,
|
IntroductionModule.class,
|
||||||
IoModule.class,
|
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
MessagingModule.class,
|
MessagingModule.class,
|
||||||
PrivateGroupModule.class,
|
PrivateGroupModule.class,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.briarproject.bramble.data.DataModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.sync.SyncModule;
|
import org.briarproject.bramble.sync.SyncModule;
|
||||||
import org.briarproject.bramble.sync.validation.ValidationModule;
|
import org.briarproject.bramble.sync.validation.ValidationModule;
|
||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
@@ -41,7 +40,6 @@ import dagger.Component;
|
|||||||
EventModule.class,
|
EventModule.class,
|
||||||
ForumModule.class,
|
ForumModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IoModule.class,
|
|
||||||
MessagingModule.class,
|
MessagingModule.class,
|
||||||
SyncModule.class,
|
SyncModule.class,
|
||||||
SystemModule.class,
|
SystemModule.class,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import org.briarproject.bramble.data.DataModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
import org.briarproject.bramble.record.RecordModule;
|
||||||
import org.briarproject.bramble.sync.SyncModule;
|
import org.briarproject.bramble.sync.SyncModule;
|
||||||
@@ -49,7 +48,6 @@ import dagger.Component;
|
|||||||
DatabaseModule.class,
|
DatabaseModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IoModule.class,
|
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
MessagingModule.class,
|
MessagingModule.class,
|
||||||
RecordModule.class,
|
RecordModule.class,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import org.briarproject.bramble.data.DataModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
import org.briarproject.bramble.record.RecordModule;
|
||||||
@@ -69,7 +68,6 @@ import dagger.Component;
|
|||||||
GroupInvitationModule.class,
|
GroupInvitationModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IntroductionModule.class,
|
IntroductionModule.class,
|
||||||
IoModule.class,
|
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
MessagingModule.class,
|
MessagingModule.class,
|
||||||
PrivateGroupModule.class,
|
PrivateGroupModule.class,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
emptyList()
|
emptyList()
|
||||||
)
|
)
|
||||||
private val sessionId = SessionId(getRandomId())
|
private val sessionId = SessionId(getRandomId())
|
||||||
private val privateMessage = PrivateMessage(message, emptyList())
|
private val privateMessage = PrivateMessage(message)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun list() {
|
fun list() {
|
||||||
|
|||||||
Reference in New Issue
Block a user