mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 04:39:54 +01:00
ReaderImpl now maintains either one or two bytes of lookahead,
depending on the value of the first byte, so that an object's initial tag is included in the data seen by the ObjectReader. Digests and signatures can therefore be calculated over objects by their readers without any risk of ambiguity.
This commit is contained in:
@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Tags;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.ObjectReader;
|
||||
@@ -11,6 +12,7 @@ import net.sf.briar.api.serial.Reader;
|
||||
public class BatchIdReader implements ObjectReader<BatchId> {
|
||||
|
||||
public BatchId readObject(Reader r) throws IOException {
|
||||
r.readUserDefinedTag(Tags.BATCH_ID);
|
||||
byte[] b = r.readRaw();
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
return new BatchId(b);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,21 +24,20 @@ public class BatchReader implements ObjectReader<Batch> {
|
||||
this.batchFactory = batchFactory;
|
||||
}
|
||||
|
||||
public Batch readObject(Reader reader) throws IOException,
|
||||
GeneralSecurityException {
|
||||
// Initialise the consumers - the initial tag has already been read, so
|
||||
// subtract one from the maximum size
|
||||
CountingConsumer counting = new CountingConsumer(Batch.MAX_SIZE - 1);
|
||||
public Batch readObject(Reader r) throws IOException {
|
||||
// Initialise the consumers
|
||||
CountingConsumer counting = new CountingConsumer(Batch.MAX_SIZE);
|
||||
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
||||
messageDigest.reset();
|
||||
// Read and digest the data
|
||||
reader.addConsumer(counting);
|
||||
reader.addConsumer(digesting);
|
||||
reader.addObjectReader(Tags.MESSAGE, messageReader);
|
||||
List<Message> messages = reader.readList(Message.class);
|
||||
reader.removeObjectReader(Tags.MESSAGE);
|
||||
reader.removeConsumer(digesting);
|
||||
reader.removeConsumer(counting);
|
||||
r.addConsumer(counting);
|
||||
r.addConsumer(digesting);
|
||||
r.readUserDefinedTag(Tags.BATCH);
|
||||
r.addObjectReader(Tags.MESSAGE, messageReader);
|
||||
List<Message> messages = r.readList(Message.class);
|
||||
r.removeObjectReader(Tags.MESSAGE);
|
||||
r.removeConsumer(digesting);
|
||||
r.removeConsumer(counting);
|
||||
// Build and return the batch
|
||||
BatchId id = new BatchId(messageDigest.digest());
|
||||
return batchFactory.createBatch(id, messages);
|
||||
|
||||
@@ -29,9 +29,8 @@ class BundleReaderImpl implements BundleReader {
|
||||
|
||||
public Header getHeader() throws IOException, GeneralSecurityException {
|
||||
if(state != State.START) throw new IllegalStateException();
|
||||
reader.readUserDefinedTag(Tags.HEADER);
|
||||
reader.addObjectReader(Tags.HEADER, headerReader);
|
||||
Header h = reader.readUserDefinedObject(Tags.HEADER, Header.class);
|
||||
Header h = reader.readUserDefined(Tags.HEADER, Header.class);
|
||||
reader.removeObjectReader(Tags.HEADER);
|
||||
// Expect a list of batches
|
||||
reader.readListStart();
|
||||
@@ -50,8 +49,7 @@ class BundleReaderImpl implements BundleReader {
|
||||
state = State.END;
|
||||
return null;
|
||||
}
|
||||
reader.readUserDefinedTag(Tags.BATCH);
|
||||
return reader.readUserDefinedObject(Tags.BATCH, Batch.class);
|
||||
return reader.readUserDefined(Tags.BATCH, Batch.class);
|
||||
}
|
||||
|
||||
public void finish() throws IOException {
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Tags;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.ObjectReader;
|
||||
@@ -11,6 +12,7 @@ import net.sf.briar.api.serial.Reader;
|
||||
public class GroupIdReader implements ObjectReader<GroupId> {
|
||||
|
||||
public GroupId readObject(Reader r) throws IOException {
|
||||
r.readUserDefinedTag(Tags.GROUP_ID);
|
||||
byte[] b = r.readRaw();
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
return new GroupId(b);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -21,28 +20,27 @@ class HeaderReader implements ObjectReader<Header> {
|
||||
this.headerFactory = headerFactory;
|
||||
}
|
||||
|
||||
public Header readObject(Reader reader) throws IOException,
|
||||
GeneralSecurityException {
|
||||
// Initialise and add the consumer - the initial tag has already been
|
||||
// read, so subtract one from the maximum size
|
||||
CountingConsumer counting = new CountingConsumer(Header.MAX_SIZE - 1);
|
||||
reader.addConsumer(counting);
|
||||
public Header readObject(Reader r) throws IOException {
|
||||
// Initialise and add the consumer
|
||||
CountingConsumer counting = new CountingConsumer(Header.MAX_SIZE);
|
||||
r.addConsumer(counting);
|
||||
r.readUserDefinedTag(Tags.HEADER);
|
||||
// Acks
|
||||
reader.addObjectReader(Tags.BATCH_ID, new BatchIdReader());
|
||||
Collection<BatchId> acks = reader.readList(BatchId.class);
|
||||
reader.removeObjectReader(Tags.BATCH_ID);
|
||||
r.addObjectReader(Tags.BATCH_ID, new BatchIdReader());
|
||||
Collection<BatchId> acks = r.readList(BatchId.class);
|
||||
r.removeObjectReader(Tags.BATCH_ID);
|
||||
// Subs
|
||||
reader.addObjectReader(Tags.GROUP_ID, new GroupIdReader());
|
||||
Collection<GroupId> subs = reader.readList(GroupId.class);
|
||||
reader.removeObjectReader(Tags.GROUP_ID);
|
||||
r.addObjectReader(Tags.GROUP_ID, new GroupIdReader());
|
||||
Collection<GroupId> subs = r.readList(GroupId.class);
|
||||
r.removeObjectReader(Tags.GROUP_ID);
|
||||
// Transports
|
||||
Map<String, String> transports =
|
||||
reader.readMap(String.class, String.class);
|
||||
r.readMap(String.class, String.class);
|
||||
// Timestamp
|
||||
long timestamp = reader.readInt64();
|
||||
long timestamp = r.readInt64();
|
||||
if(timestamp < 0L) throw new FormatException();
|
||||
// Remove the consumer
|
||||
reader.removeConsumer(counting);
|
||||
r.removeConsumer(counting);
|
||||
// Build and return the header
|
||||
return headerFactory.createHeader(acks, subs, transports, timestamp);
|
||||
}
|
||||
|
||||
@@ -32,40 +32,42 @@ class MessageReader implements ObjectReader<Message> {
|
||||
this.messageDigest = messageDigest;
|
||||
}
|
||||
|
||||
public Message readObject(Reader reader) throws IOException {
|
||||
public Message readObject(Reader r) throws IOException {
|
||||
CopyingConsumer copying = new CopyingConsumer();
|
||||
CountingConsumer counting = new CountingConsumer(Message.MAX_SIZE);
|
||||
reader.addConsumer(copying);
|
||||
reader.addConsumer(counting);
|
||||
r.addConsumer(copying);
|
||||
r.addConsumer(counting);
|
||||
// Read the initial tag
|
||||
r.readUserDefinedTag(Tags.MESSAGE);
|
||||
// Read the parent's message ID
|
||||
reader.readUserDefinedTag(Tags.MESSAGE_ID);
|
||||
byte[] b = reader.readRaw();
|
||||
r.readUserDefinedTag(Tags.MESSAGE_ID);
|
||||
byte[] b = r.readRaw();
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
MessageId parent = new MessageId(b);
|
||||
// Read the group ID
|
||||
reader.readUserDefinedTag(Tags.GROUP_ID);
|
||||
b = reader.readRaw();
|
||||
r.readUserDefinedTag(Tags.GROUP_ID);
|
||||
b = r.readRaw();
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
GroupId group = new GroupId(b);
|
||||
// Read the timestamp
|
||||
long timestamp = reader.readInt64();
|
||||
long timestamp = r.readInt64();
|
||||
if(timestamp < 0L) throw new FormatException();
|
||||
// Hash the author's nick and public key to get the author ID
|
||||
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
|
||||
messageDigest.reset();
|
||||
reader.addConsumer(digesting);
|
||||
reader.readString();
|
||||
byte[] encodedKey = reader.readRaw();
|
||||
reader.removeConsumer(digesting);
|
||||
r.addConsumer(digesting);
|
||||
r.readString();
|
||||
byte[] encodedKey = r.readRaw();
|
||||
r.removeConsumer(digesting);
|
||||
AuthorId author = new AuthorId(messageDigest.digest());
|
||||
// Skip the message body
|
||||
reader.readRaw();
|
||||
r.readRaw();
|
||||
// Record the length of the signed data
|
||||
int messageLength = (int) counting.getCount();
|
||||
// Read the signature
|
||||
byte[] sig = reader.readRaw();
|
||||
reader.removeConsumer(counting);
|
||||
reader.removeConsumer(copying);
|
||||
byte[] sig = r.readRaw();
|
||||
r.removeConsumer(counting);
|
||||
r.removeConsumer(copying);
|
||||
// Verify the signature
|
||||
PublicKey publicKey;
|
||||
try {
|
||||
|
||||
@@ -2,7 +2,6 @@ package net.sf.briar.serial;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -20,12 +19,11 @@ class ReaderImpl implements Reader {
|
||||
private static final byte[] EMPTY_BUFFER = new byte[] {};
|
||||
|
||||
private final InputStream in;
|
||||
private final Map<Integer, ObjectReader<?>> objectReaders =
|
||||
new HashMap<Integer, ObjectReader<?>>();
|
||||
|
||||
private Consumer[] consumers = new Consumer[] {};
|
||||
private ObjectReader<?>[] objectReaders = new ObjectReader<?>[] {};
|
||||
private boolean started = false, eof = false;
|
||||
private byte next;
|
||||
private byte next, nextNext;
|
||||
private byte[] buf = null;
|
||||
|
||||
ReaderImpl(InputStream in) {
|
||||
@@ -39,16 +37,32 @@ class ReaderImpl implements Reader {
|
||||
|
||||
private byte readNext(boolean eofAcceptable) throws IOException {
|
||||
assert !eof;
|
||||
if(started) for(Consumer c : consumers) c.write(next);
|
||||
if(started) {
|
||||
for(Consumer c : consumers) {
|
||||
c.write(next);
|
||||
if(next == Tag.USER) c.write(nextNext);
|
||||
}
|
||||
}
|
||||
started = true;
|
||||
readLookahead(eofAcceptable);
|
||||
return next;
|
||||
}
|
||||
|
||||
private void readLookahead(boolean eofAcceptable) throws IOException {
|
||||
assert started;
|
||||
// Read the lookahead byte
|
||||
int i = in.read();
|
||||
if(i == -1) {
|
||||
eof = true;
|
||||
if(!eofAcceptable) throw new FormatException();
|
||||
eof = true;
|
||||
}
|
||||
if(i > 127) i -= 256;
|
||||
next = (byte) i;
|
||||
return next;
|
||||
// If necessary, read another lookahead byte
|
||||
if(next == Tag.USER) {
|
||||
i = in.read();
|
||||
if(i == -1) throw new FormatException();
|
||||
nextNext = (byte) i;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
@@ -78,11 +92,20 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
public void addObjectReader(int tag, ObjectReader<?> o) {
|
||||
objectReaders.put(tag, o);
|
||||
if(tag < 0 || tag > 255) throw new IllegalArgumentException();
|
||||
if(objectReaders.length < tag + 1) {
|
||||
ObjectReader<?>[] newObjectReaders = new ObjectReader<?>[tag + 1];
|
||||
System.arraycopy(objectReaders, 0, newObjectReaders, 0,
|
||||
objectReaders.length);
|
||||
objectReaders = newObjectReaders;
|
||||
}
|
||||
objectReaders[tag] = o;
|
||||
}
|
||||
|
||||
public void removeObjectReader(int tag) {
|
||||
objectReaders.remove(tag);
|
||||
if(tag < 0 || tag > objectReaders.length)
|
||||
throw new IllegalArgumentException();
|
||||
objectReaders[tag] = null;
|
||||
}
|
||||
|
||||
public boolean hasBoolean() throws IOException {
|
||||
@@ -165,19 +188,22 @@ class ReaderImpl implements Reader {
|
||||
private void readIntoBuffer(byte[] b, int length) throws IOException {
|
||||
b[0] = next;
|
||||
int offset = 1;
|
||||
if(next == Tag.USER) {
|
||||
b[1] = nextNext;
|
||||
offset = 2;
|
||||
}
|
||||
while(offset < length) {
|
||||
int read = in.read(b, offset, length - offset);
|
||||
if(read == -1) break;
|
||||
if(read == -1) {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
offset += read;
|
||||
}
|
||||
if(offset < length) throw new FormatException();
|
||||
// Feed the hungry mouths
|
||||
for(Consumer c : consumers) c.write(b, 0, length);
|
||||
// Read the lookahead byte
|
||||
int i = in.read();
|
||||
if(i == -1) eof = true;
|
||||
if(i > 127) i -= 256;
|
||||
next = (byte) i;
|
||||
readLookahead(true);
|
||||
}
|
||||
|
||||
public boolean hasInt64() throws IOException {
|
||||
@@ -316,13 +342,11 @@ class ReaderImpl implements Reader {
|
||||
|| (next & Tag.SHORT_MASK) == Tag.SHORT_LIST;
|
||||
}
|
||||
|
||||
public List<Object> readList() throws IOException,
|
||||
GeneralSecurityException {
|
||||
public List<Object> readList() throws IOException {
|
||||
return readList(Object.class);
|
||||
}
|
||||
|
||||
public <E> List<E> readList(Class<E> e) throws IOException,
|
||||
GeneralSecurityException {
|
||||
public <E> List<E> readList(Class<E> e) throws IOException {
|
||||
if(!hasList()) throw new FormatException();
|
||||
if(next == Tag.LIST) {
|
||||
readNext(false);
|
||||
@@ -340,8 +364,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
}
|
||||
|
||||
private <E> List<E> readList(Class<E> e, int length) throws IOException,
|
||||
GeneralSecurityException {
|
||||
private <E> List<E> readList(Class<E> e, int length) throws IOException {
|
||||
assert length >= 0;
|
||||
List<E> list = new ArrayList<E>();
|
||||
for(int i = 0; i < length; i++) list.add(readObject(e));
|
||||
@@ -360,13 +383,9 @@ class ReaderImpl implements Reader {
|
||||
readNext(true);
|
||||
}
|
||||
|
||||
private Object readObject() throws IOException, GeneralSecurityException {
|
||||
private Object readObject() throws IOException {
|
||||
if(!started) throw new IllegalStateException();
|
||||
if(hasUserDefinedTag()) {
|
||||
ObjectReader<?> o = objectReaders.get(readUserDefinedTag());
|
||||
if(o == null) throw new FormatException();
|
||||
return o.readObject(this);
|
||||
}
|
||||
if(hasUserDefined()) return readUserDefined();
|
||||
if(hasBoolean()) return Boolean.valueOf(readBoolean());
|
||||
if(hasUint7()) return Byte.valueOf(readUint7());
|
||||
if(hasInt8()) return Byte.valueOf(readInt8());
|
||||
@@ -386,8 +405,23 @@ class ReaderImpl implements Reader {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
private <T> T readObject(Class<T> t) throws IOException,
|
||||
GeneralSecurityException {
|
||||
private boolean hasUserDefined() throws IOException {
|
||||
if(!started) readNext(true);
|
||||
if(eof) return false;
|
||||
if(next == Tag.USER) return true;
|
||||
if((next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object readUserDefined() throws IOException {
|
||||
if(!hasUserDefined()) throw new FormatException();
|
||||
int tag;
|
||||
if(next == Tag.USER) tag = 0xFF & nextNext;
|
||||
else tag = 0xFF & next ^ Tag.SHORT_USER;
|
||||
return readUserDefined(tag, Object.class);
|
||||
}
|
||||
|
||||
private <T> T readObject(Class<T> t) throws IOException {
|
||||
try {
|
||||
return t.cast(readObject());
|
||||
} catch(ClassCastException e) {
|
||||
@@ -421,13 +455,11 @@ class ReaderImpl implements Reader {
|
||||
|| (next & Tag.SHORT_MASK) == Tag.SHORT_MAP;
|
||||
}
|
||||
|
||||
public Map<Object, Object> readMap() throws IOException,
|
||||
GeneralSecurityException {
|
||||
public Map<Object, Object> readMap() throws IOException {
|
||||
return readMap(Object.class, Object.class);
|
||||
}
|
||||
|
||||
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException,
|
||||
GeneralSecurityException {
|
||||
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException {
|
||||
if(!hasMap()) throw new FormatException();
|
||||
if(next == Tag.MAP) {
|
||||
readNext(false);
|
||||
@@ -446,7 +478,7 @@ class ReaderImpl implements Reader {
|
||||
}
|
||||
|
||||
private <K, V> Map<K, V> readMap(Class<K> k, Class<V> v, int size)
|
||||
throws IOException, GeneralSecurityException {
|
||||
throws IOException {
|
||||
assert size >= 0;
|
||||
Map<K, V> m = new HashMap<K, V>();
|
||||
for(int i = 0; i < size; i++) m.put(readObject(k), readObject(v));
|
||||
@@ -483,32 +515,21 @@ class ReaderImpl implements Reader {
|
||||
readNext(true);
|
||||
}
|
||||
|
||||
public boolean hasUserDefinedTag() throws IOException {
|
||||
public boolean hasUserDefined(int tag) throws IOException {
|
||||
if(tag < 0 || tag > 255) throw new IllegalArgumentException();
|
||||
if(!started) readNext(true);
|
||||
if(eof) return false;
|
||||
return next == Tag.USER ||
|
||||
(next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER;
|
||||
if(next == Tag.USER)
|
||||
return tag == (0xFF & nextNext);
|
||||
else if((next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER)
|
||||
return tag == (0xFF & next ^ Tag.SHORT_USER);
|
||||
else return false;
|
||||
}
|
||||
|
||||
public int readUserDefinedTag() throws IOException {
|
||||
if(!hasUserDefinedTag()) throw new FormatException();
|
||||
if(next == Tag.USER) {
|
||||
readNext(false);
|
||||
return readLength();
|
||||
} else {
|
||||
int tag = 0xFF & next ^ Tag.SHORT_USER;
|
||||
readNext(true);
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
public void readUserDefinedTag(int tag) throws IOException {
|
||||
if(readUserDefinedTag() != tag) throw new FormatException();
|
||||
}
|
||||
|
||||
public <T> T readUserDefinedObject(int tag, Class<T> t) throws IOException,
|
||||
GeneralSecurityException {
|
||||
ObjectReader<?> o = objectReaders.get(tag);
|
||||
public <T> T readUserDefined(int tag, Class<T> t) throws IOException {
|
||||
if(!hasUserDefined(tag)) throw new FormatException();
|
||||
if(tag >= objectReaders.length) throw new FormatException();
|
||||
ObjectReader<?> o = objectReaders[tag];
|
||||
if(o == null) throw new FormatException();
|
||||
try {
|
||||
return t.cast(o.readObject(this));
|
||||
@@ -516,4 +537,9 @@ class ReaderImpl implements Reader {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public void readUserDefinedTag(int tag) throws IOException {
|
||||
if(!hasUserDefined(tag)) throw new FormatException();
|
||||
readNext(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class WriterImpl implements Writer {
|
||||
|
||||
public void writeString(String s) throws IOException {
|
||||
byte[] b = s.getBytes("UTF-8");
|
||||
if(b.length < 16) out.write(intToByte(Tag.SHORT_STRING | b.length));
|
||||
if(b.length < 16) out.write((byte) (Tag.SHORT_STRING | b.length));
|
||||
else {
|
||||
out.write(Tag.STRING);
|
||||
writeLength(b.length);
|
||||
@@ -120,12 +120,6 @@ class WriterImpl implements Writer {
|
||||
bytesWritten += b.length + 1;
|
||||
}
|
||||
|
||||
private byte intToByte(int i) {
|
||||
assert i >= 0;
|
||||
assert i <= 255;
|
||||
return (byte) (i > 127 ? i - 256 : i);
|
||||
}
|
||||
|
||||
private void writeLength(int i) throws IOException {
|
||||
assert i >= 0;
|
||||
// Fun fact: it's never worth writing a length as an int8
|
||||
@@ -135,7 +129,7 @@ class WriterImpl implements Writer {
|
||||
}
|
||||
|
||||
public void writeRaw(byte[] b) throws IOException {
|
||||
if(b.length < 16) out.write(intToByte(Tag.SHORT_RAW | b.length));
|
||||
if(b.length < 16) out.write((byte) (Tag.SHORT_RAW | b.length));
|
||||
else {
|
||||
out.write(Tag.RAW);
|
||||
writeLength(b.length);
|
||||
@@ -150,7 +144,7 @@ class WriterImpl implements Writer {
|
||||
|
||||
public void writeList(Collection<?> c) throws IOException {
|
||||
int length = c.size();
|
||||
if(length < 16) out.write(intToByte(Tag.SHORT_LIST | length));
|
||||
if(length < 16) out.write((byte) (Tag.SHORT_LIST | length));
|
||||
else {
|
||||
out.write(Tag.LIST);
|
||||
writeLength(length);
|
||||
@@ -188,7 +182,7 @@ class WriterImpl implements Writer {
|
||||
|
||||
public void writeMap(Map<?, ?> m) throws IOException {
|
||||
int length = m.size();
|
||||
if(length < 16) out.write(intToByte(Tag.SHORT_MAP | length));
|
||||
if(length < 16) out.write((byte) (Tag.SHORT_MAP | length));
|
||||
else {
|
||||
out.write(Tag.MAP);
|
||||
writeLength(length);
|
||||
@@ -216,12 +210,14 @@ class WriterImpl implements Writer {
|
||||
}
|
||||
|
||||
public void writeUserDefinedTag(int tag) throws IOException {
|
||||
if(tag < 0) throw new IllegalArgumentException();
|
||||
if(tag < 32) out.write((byte) (Tag.SHORT_USER | tag));
|
||||
else {
|
||||
if(tag < 0 || tag > 255) throw new IllegalArgumentException();
|
||||
if(tag < 32) {
|
||||
out.write((byte) (Tag.SHORT_USER | tag));
|
||||
bytesWritten++;
|
||||
} else {
|
||||
out.write(Tag.USER);
|
||||
writeLength(tag);
|
||||
out.write((byte) tag);
|
||||
bytesWritten += 2;
|
||||
}
|
||||
bytesWritten++;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user