mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Changed the root package from net.sf.briar to org.briarproject.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.briarproject.api.serial.Reader;
|
||||
import org.briarproject.api.serial.ReaderFactory;
|
||||
|
||||
class ReaderFactoryImpl implements ReaderFactory {
|
||||
|
||||
public Reader createReader(InputStream in) {
|
||||
return new ReaderImpl(in);
|
||||
}
|
||||
}
|
||||
498
briar-core/src/org/briarproject/serial/ReaderImpl.java
Normal file
498
briar-core/src/org/briarproject/serial/ReaderImpl.java
Normal file
@@ -0,0 +1,498 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.serial.Consumer;
|
||||
import org.briarproject.api.serial.Reader;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ReaderImpl implements Reader {
|
||||
|
||||
private static final byte[] EMPTY_BUFFER = new byte[] {};
|
||||
|
||||
private final InputStream in;
|
||||
private final Collection<Consumer> consumers = new ArrayList<Consumer>(0);
|
||||
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next, nextStructId;
|
||||
private byte[] buf = new byte[8];
|
||||
|
||||
ReaderImpl(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public boolean eof() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
return eof;
|
||||
}
|
||||
|
||||
private void readLookahead() throws IOException {
|
||||
assert !eof;
|
||||
// If one or two lookahead bytes have been read, feed the consumers
|
||||
if(hasLookahead) consumeLookahead();
|
||||
// Read a lookahead byte
|
||||
int i = in.read();
|
||||
if(i == -1) {
|
||||
eof = true;
|
||||
return;
|
||||
}
|
||||
next = (byte) i;
|
||||
// If necessary, read another lookahead byte
|
||||
if(next == Tag.STRUCT) {
|
||||
i = in.read();
|
||||
if(i == -1) throw new FormatException();
|
||||
nextStructId = (byte) i;
|
||||
}
|
||||
hasLookahead = true;
|
||||
}
|
||||
|
||||
private void consumeLookahead() throws IOException {
|
||||
assert hasLookahead;
|
||||
for(Consumer c : consumers) {
|
||||
c.write(next);
|
||||
if(next == Tag.STRUCT) c.write(nextStructId);
|
||||
}
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
public void addConsumer(Consumer c) {
|
||||
consumers.add(c);
|
||||
}
|
||||
|
||||
public void removeConsumer(Consumer c) {
|
||||
if(!consumers.remove(c)) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
public boolean hasBoolean() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.FALSE || next == Tag.TRUE;
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
if(!hasBoolean()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return next == Tag.TRUE;
|
||||
}
|
||||
|
||||
public void skipBoolean() throws IOException {
|
||||
if(!hasBoolean()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasUint7() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next >= 0;
|
||||
}
|
||||
|
||||
public byte readUint7() throws IOException {
|
||||
if(!hasUint7()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return next;
|
||||
}
|
||||
|
||||
public void skipUint7() throws IOException {
|
||||
if(!hasUint7()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasInt8() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT8;
|
||||
}
|
||||
|
||||
public byte readInt8() throws IOException {
|
||||
if(!hasInt8()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
int i = in.read();
|
||||
if(i == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
byte b = (byte) i;
|
||||
// Feed the hungry mouths
|
||||
for(Consumer c : consumers) c.write(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public void skipInt8() throws IOException {
|
||||
if(!hasInt8()) throw new FormatException();
|
||||
if(in.read() == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasInt16() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT16;
|
||||
}
|
||||
|
||||
public short readInt16() throws IOException {
|
||||
if(!hasInt16()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
readIntoBuffer(2);
|
||||
return (short) (((buf[0] & 0xFF) << 8) | (buf[1] & 0xFF));
|
||||
}
|
||||
|
||||
private void readIntoBuffer(int length) throws IOException {
|
||||
if(buf.length < length) buf = new byte[length];
|
||||
readIntoBuffer(buf, length);
|
||||
}
|
||||
|
||||
private void readIntoBuffer(byte[] b, int length) throws IOException {
|
||||
assert !hasLookahead;
|
||||
int offset = 0;
|
||||
while(offset < length) {
|
||||
int read = in.read(b, offset, length - offset);
|
||||
if(read == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
offset += read;
|
||||
}
|
||||
// Feed the hungry mouths
|
||||
for(Consumer c : consumers) c.write(b, 0, length);
|
||||
}
|
||||
|
||||
public void skipInt16() throws IOException {
|
||||
if(!hasInt16()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(2);
|
||||
}
|
||||
|
||||
private void skip(int length) throws IOException {
|
||||
while(length > 0) {
|
||||
int read = in.read(buf, 0, Math.min(length, buf.length));
|
||||
if(read == -1) {
|
||||
eof = true;
|
||||
throw new FormatException();
|
||||
}
|
||||
length -= read;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasInt32() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT32;
|
||||
}
|
||||
|
||||
public int readInt32() throws IOException {
|
||||
if(!hasInt32()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return readInt32Bits();
|
||||
}
|
||||
|
||||
private int readInt32Bits() throws IOException {
|
||||
readIntoBuffer(4);
|
||||
return ((buf[0] & 0xFF) << 24) | ((buf[1] & 0xFF) << 16) |
|
||||
((buf[2] & 0xFF) << 8) | (buf[3] & 0xFF);
|
||||
}
|
||||
|
||||
public void skipInt32() throws IOException {
|
||||
if(!hasInt32()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public boolean hasInt64() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.INT64;
|
||||
}
|
||||
|
||||
public long readInt64() throws IOException {
|
||||
if(!hasInt64()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return readInt64Bits();
|
||||
}
|
||||
|
||||
private long readInt64Bits() throws IOException {
|
||||
readIntoBuffer(8);
|
||||
return ((buf[0] & 0xFFL) << 56) | ((buf[1] & 0xFFL) << 48) |
|
||||
((buf[2] & 0xFFL) << 40) | ((buf[3] & 0xFFL) << 32) |
|
||||
((buf[4] & 0xFFL) << 24) | ((buf[5] & 0xFFL) << 16) |
|
||||
((buf[6] & 0xFFL) << 8) | (buf[7] & 0xFFL);
|
||||
}
|
||||
|
||||
public void skipInt64() throws IOException {
|
||||
if(!hasInt64()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(8);
|
||||
}
|
||||
|
||||
public boolean hasIntAny() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|
||||
|| next == Tag.INT32 || next == Tag.INT64;
|
||||
}
|
||||
|
||||
public long readIntAny() throws IOException {
|
||||
if(!hasIntAny()) throw new FormatException();
|
||||
if(next >= 0) return readUint7();
|
||||
if(next == Tag.INT8) return readInt8();
|
||||
if(next == Tag.INT16) return readInt16();
|
||||
if(next == Tag.INT32) return readInt32();
|
||||
if(next == Tag.INT64) return readInt64();
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public void skipIntAny() throws IOException {
|
||||
if(!hasIntAny()) throw new FormatException();
|
||||
if(next >= 0) skipUint7();
|
||||
else if(next == Tag.INT8) skipInt8();
|
||||
else if(next == Tag.INT16) skipInt16();
|
||||
else if(next == Tag.INT32) skipInt32();
|
||||
else if(next == Tag.INT64) skipInt64();
|
||||
else throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public boolean hasFloat32() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.FLOAT32;
|
||||
}
|
||||
|
||||
public float readFloat32() throws IOException {
|
||||
if(!hasFloat32()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return Float.intBitsToFloat(readInt32Bits());
|
||||
}
|
||||
|
||||
public void skipFloat32() throws IOException {
|
||||
if(!hasFloat32()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(4);
|
||||
}
|
||||
|
||||
public boolean hasFloat64() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.FLOAT64;
|
||||
}
|
||||
|
||||
public double readFloat64() throws IOException {
|
||||
if(!hasFloat64()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
return Double.longBitsToDouble(readInt64Bits());
|
||||
}
|
||||
|
||||
public void skipFloat64() throws IOException {
|
||||
if(!hasFloat64()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(8);
|
||||
}
|
||||
|
||||
public boolean hasString() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.STRING;
|
||||
}
|
||||
|
||||
public String readString(int maxLength) throws IOException {
|
||||
if(!hasString()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
int length = readLength();
|
||||
if(length > maxLength) throw new FormatException();
|
||||
if(length == 0) return "";
|
||||
readIntoBuffer(length);
|
||||
return new String(buf, 0, length, "UTF-8");
|
||||
}
|
||||
|
||||
private int readLength() throws IOException {
|
||||
if(!hasLength()) throw new FormatException();
|
||||
int length;
|
||||
if(next >= 0) length = readUint7();
|
||||
else if(next == Tag.INT8) length = readInt8();
|
||||
else if(next == Tag.INT16) length = readInt16();
|
||||
else if(next == Tag.INT32) length = readInt32();
|
||||
else throw new IllegalStateException();
|
||||
if(length < 0) throw new FormatException();
|
||||
return length;
|
||||
}
|
||||
|
||||
private boolean hasLength() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|
||||
|| next == Tag.INT32;
|
||||
}
|
||||
|
||||
public void skipString(int maxLength) throws IOException {
|
||||
if(!hasString()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
int length = readLength();
|
||||
if(length > maxLength) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(length);
|
||||
}
|
||||
|
||||
public boolean hasBytes() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.BYTES;
|
||||
}
|
||||
|
||||
public byte[] readBytes(int maxLength) throws IOException {
|
||||
if(!hasBytes()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
int length = readLength();
|
||||
if(length > maxLength) throw new FormatException();
|
||||
if(length == 0) return EMPTY_BUFFER;
|
||||
byte[] b = new byte[length];
|
||||
readIntoBuffer(b, length);
|
||||
return b;
|
||||
}
|
||||
|
||||
public void skipBytes(int maxLength) throws IOException {
|
||||
if(!hasBytes()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
int length = readLength();
|
||||
if(length > maxLength) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
skip(length);
|
||||
}
|
||||
|
||||
public boolean hasList() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.LIST;
|
||||
}
|
||||
|
||||
public void readListStart() throws IOException {
|
||||
if(!hasList()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public boolean hasListEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.END;
|
||||
}
|
||||
|
||||
public void readListEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if(!hasEnd()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public void skipList() throws IOException {
|
||||
if(!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while(!hasListEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
private void skipObject() throws IOException {
|
||||
if(hasBoolean()) skipBoolean();
|
||||
else if(hasIntAny()) skipIntAny();
|
||||
else if(hasFloat32()) skipFloat32();
|
||||
else if(hasFloat64()) skipFloat64();
|
||||
else if(hasString()) skipString(Integer.MAX_VALUE);
|
||||
else if(hasBytes()) skipBytes(Integer.MAX_VALUE);
|
||||
else if(hasList()) skipList();
|
||||
else if(hasMap()) skipMap();
|
||||
else if(hasStruct()) skipStruct();
|
||||
else if(hasNull()) skipNull();
|
||||
else throw new FormatException();
|
||||
}
|
||||
|
||||
public boolean hasMap() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.MAP;
|
||||
}
|
||||
|
||||
public void readMapStart() throws IOException {
|
||||
if(!hasMap()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public boolean hasMapEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
public void readMapEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
public void skipMap() throws IOException {
|
||||
if(!hasMap()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while(!hasMapEnd()) {
|
||||
skipObject();
|
||||
skipObject();
|
||||
}
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasStruct() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.STRUCT;
|
||||
}
|
||||
|
||||
public boolean hasStruct(int id) throws IOException {
|
||||
if(id < 0 || id > 255) throw new IllegalArgumentException();
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.STRUCT && (nextStructId & 0xFF) == id;
|
||||
}
|
||||
|
||||
public void readStructStart(int id) throws IOException {
|
||||
if(!hasStruct(id)) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public boolean hasStructEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
public void readStructEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
public void skipStruct() throws IOException {
|
||||
if(!hasStruct()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while(!hasStructEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
public boolean hasNull() throws IOException {
|
||||
if(!hasLookahead) readLookahead();
|
||||
if(eof) return false;
|
||||
return next == Tag.NULL;
|
||||
}
|
||||
|
||||
public void readNull() throws IOException {
|
||||
if(!hasNull()) throw new FormatException();
|
||||
consumeLookahead();
|
||||
}
|
||||
|
||||
public void skipNull() throws IOException {
|
||||
if(!hasNull()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import org.briarproject.api.UniqueId;
|
||||
import org.briarproject.api.serial.SerialComponent;
|
||||
|
||||
class SerialComponentImpl implements SerialComponent {
|
||||
|
||||
public int getSerialisedListStartLength() {
|
||||
// LIST tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedListEndLength() {
|
||||
// END tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedStructStartLength(int id) {
|
||||
// STRUCT tag, ID
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getSerialisedStructEndLength() {
|
||||
// END tag
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getSerialisedUniqueIdLength() {
|
||||
// BYTES tag, length spec, bytes
|
||||
return 1 + getSerialisedLengthSpecLength(UniqueId.LENGTH)
|
||||
+ UniqueId.LENGTH;
|
||||
}
|
||||
|
||||
private int getSerialisedLengthSpecLength(int length) {
|
||||
if(length < 0) throw new IllegalArgumentException();
|
||||
// Uint7, int16 or int32
|
||||
return length <= Byte.MAX_VALUE ? 1 : length <= Short.MAX_VALUE ? 3 : 5;
|
||||
}
|
||||
}
|
||||
19
briar-core/src/org/briarproject/serial/SerialModule.java
Normal file
19
briar-core/src/org/briarproject/serial/SerialModule.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.briarproject.api.serial.ReaderFactory;
|
||||
import org.briarproject.api.serial.SerialComponent;
|
||||
import org.briarproject.api.serial.WriterFactory;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class SerialModule extends AbstractModule {
|
||||
|
||||
protected void configure() {
|
||||
bind(ReaderFactory.class).to(ReaderFactoryImpl.class);
|
||||
bind(SerialComponent.class).to(
|
||||
SerialComponentImpl.class).in(Singleton.class);
|
||||
bind(WriterFactory.class).to(WriterFactoryImpl.class);
|
||||
}
|
||||
}
|
||||
20
briar-core/src/org/briarproject/serial/Tag.java
Normal file
20
briar-core/src/org/briarproject/serial/Tag.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
interface Tag {
|
||||
|
||||
byte FALSE = (byte) 0xFF;
|
||||
byte TRUE = (byte) 0xFE;
|
||||
byte INT8 = (byte) 0xFD;
|
||||
byte INT16 = (byte) 0xFC;
|
||||
byte INT32 = (byte) 0xFB;
|
||||
byte INT64 = (byte) 0xFA;
|
||||
byte FLOAT32 = (byte) 0xF9;
|
||||
byte FLOAT64 = (byte) 0xF8;
|
||||
byte STRING = (byte) 0xF7;
|
||||
byte BYTES = (byte) 0xF6;
|
||||
byte LIST = (byte) 0xF5;
|
||||
byte MAP = (byte) 0xF4;
|
||||
byte STRUCT = (byte) 0xF3;
|
||||
byte END = (byte) 0xF2;
|
||||
byte NULL = (byte) 0xF1;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.briarproject.api.serial.Writer;
|
||||
import org.briarproject.api.serial.WriterFactory;
|
||||
|
||||
class WriterFactoryImpl implements WriterFactory {
|
||||
|
||||
public Writer createWriter(OutputStream out) {
|
||||
return new WriterImpl(out);
|
||||
}
|
||||
}
|
||||
203
briar-core/src/org/briarproject/serial/WriterImpl.java
Normal file
203
briar-core/src/org/briarproject/serial/WriterImpl.java
Normal file
@@ -0,0 +1,203 @@
|
||||
package org.briarproject.serial;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.briarproject.api.Bytes;
|
||||
import org.briarproject.api.serial.Consumer;
|
||||
import org.briarproject.api.serial.Writer;
|
||||
|
||||
// This class is not thread-safe
|
||||
class WriterImpl implements Writer {
|
||||
|
||||
private final OutputStream out;
|
||||
private final Collection<Consumer> consumers = new ArrayList<Consumer>(0);
|
||||
|
||||
WriterImpl(OutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
public void addConsumer(Consumer c) {
|
||||
consumers.add(c);
|
||||
}
|
||||
|
||||
public void removeConsumer(Consumer c) {
|
||||
if(!consumers.remove(c)) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
public void writeBoolean(boolean b) throws IOException {
|
||||
if(b) write(Tag.TRUE);
|
||||
else write(Tag.FALSE);
|
||||
}
|
||||
|
||||
public void writeUint7(byte b) throws IOException {
|
||||
if(b < 0) throw new IllegalArgumentException();
|
||||
write(b);
|
||||
}
|
||||
|
||||
public void writeInt8(byte b) throws IOException {
|
||||
write(Tag.INT8);
|
||||
write(b);
|
||||
}
|
||||
|
||||
public void writeInt16(short s) throws IOException {
|
||||
write(Tag.INT16);
|
||||
write((byte) (s >> 8));
|
||||
write((byte) ((s << 8) >> 8));
|
||||
}
|
||||
|
||||
public void writeInt32(int i) throws IOException {
|
||||
write(Tag.INT32);
|
||||
writeInt32Bits(i);
|
||||
}
|
||||
|
||||
private void writeInt32Bits(int i) throws IOException {
|
||||
write((byte) (i >> 24));
|
||||
write((byte) ((i << 8) >> 24));
|
||||
write((byte) ((i << 16) >> 24));
|
||||
write((byte) ((i << 24) >> 24));
|
||||
}
|
||||
|
||||
public void writeInt64(long l) throws IOException {
|
||||
write(Tag.INT64);
|
||||
writeInt64Bits(l);
|
||||
}
|
||||
|
||||
private void writeInt64Bits(long l) throws IOException {
|
||||
write((byte) (l >> 56));
|
||||
write((byte) ((l << 8) >> 56));
|
||||
write((byte) ((l << 16) >> 56));
|
||||
write((byte) ((l << 24) >> 56));
|
||||
write((byte) ((l << 32) >> 56));
|
||||
write((byte) ((l << 40) >> 56));
|
||||
write((byte) ((l << 48) >> 56));
|
||||
write((byte) ((l << 56) >> 56));
|
||||
}
|
||||
|
||||
public void writeIntAny(long l) throws IOException {
|
||||
if(l >= 0 && l <= Byte.MAX_VALUE)
|
||||
writeUint7((byte) l);
|
||||
else if(l >= Byte.MIN_VALUE && l <= Byte.MAX_VALUE)
|
||||
writeInt8((byte) l);
|
||||
else if(l >= Short.MIN_VALUE && l <= Short.MAX_VALUE)
|
||||
writeInt16((short) l);
|
||||
else if(l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE)
|
||||
writeInt32((int) l);
|
||||
else writeInt64(l);
|
||||
}
|
||||
|
||||
public void writeFloat32(float f) throws IOException {
|
||||
write(Tag.FLOAT32);
|
||||
writeInt32Bits(Float.floatToRawIntBits(f));
|
||||
}
|
||||
|
||||
public void writeFloat64(double d) throws IOException {
|
||||
write(Tag.FLOAT64);
|
||||
writeInt64Bits(Double.doubleToRawLongBits(d));
|
||||
}
|
||||
|
||||
public void writeString(String s) throws IOException {
|
||||
byte[] b = s.getBytes("UTF-8");
|
||||
write(Tag.STRING);
|
||||
writeLength(b.length);
|
||||
write(b);
|
||||
}
|
||||
|
||||
private void writeLength(int i) throws IOException {
|
||||
assert i >= 0;
|
||||
// Fun fact: it's never worth writing a length as an int8
|
||||
if(i <= Byte.MAX_VALUE) writeUint7((byte) i);
|
||||
else if(i <= Short.MAX_VALUE) writeInt16((short) i);
|
||||
else writeInt32(i);
|
||||
}
|
||||
|
||||
public void writeBytes(byte[] b) throws IOException {
|
||||
write(Tag.BYTES);
|
||||
writeLength(b.length);
|
||||
write(b);
|
||||
}
|
||||
|
||||
public void writeList(Collection<?> c) throws IOException {
|
||||
write(Tag.LIST);
|
||||
for(Object o : c) writeObject(o);
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
private void writeObject(Object o) throws IOException {
|
||||
if(o instanceof Boolean) writeBoolean((Boolean) o);
|
||||
else if(o instanceof Byte) writeIntAny((Byte) o);
|
||||
else if(o instanceof Short) writeIntAny((Short) o);
|
||||
else if(o instanceof Integer) writeIntAny((Integer) o);
|
||||
else if(o instanceof Long) writeIntAny((Long) o);
|
||||
else if(o instanceof Float) writeFloat32((Float) o);
|
||||
else if(o instanceof Double) writeFloat64((Double) o);
|
||||
else if(o instanceof String) writeString((String) o);
|
||||
else if(o instanceof Bytes) writeBytes(((Bytes) o).getBytes());
|
||||
else if(o instanceof List<?>) writeList((List<?>) o);
|
||||
else if(o instanceof Map<?, ?>) writeMap((Map<?, ?>) o);
|
||||
else if(o == null) writeNull();
|
||||
else throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public void writeListStart() throws IOException {
|
||||
write(Tag.LIST);
|
||||
}
|
||||
|
||||
public void writeListEnd() throws IOException {
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
public void writeMap(Map<?, ?> m) throws IOException {
|
||||
write(Tag.MAP);
|
||||
for(Entry<?, ?> e : m.entrySet()) {
|
||||
writeObject(e.getKey());
|
||||
writeObject(e.getValue());
|
||||
}
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
public void writeMapStart() throws IOException {
|
||||
write(Tag.MAP);
|
||||
}
|
||||
|
||||
public void writeMapEnd() throws IOException {
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
public void writeStructStart(int id) throws IOException {
|
||||
if(id < 0 || id > 255) throw new IllegalArgumentException();
|
||||
write(Tag.STRUCT);
|
||||
write((byte) id);
|
||||
}
|
||||
|
||||
public void writeStructEnd() throws IOException {
|
||||
write(Tag.END);
|
||||
}
|
||||
|
||||
public void writeNull() throws IOException {
|
||||
write(Tag.NULL);
|
||||
}
|
||||
|
||||
private void write(byte b) throws IOException {
|
||||
out.write(b);
|
||||
for(Consumer c : consumers) c.write(b);
|
||||
}
|
||||
|
||||
private void write(byte[] b) throws IOException {
|
||||
out.write(b);
|
||||
for(Consumer c : consumers) c.write(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user