mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
A more efficient encoding for short strings, raws, lists and maps. Now we can encode a list of three small integers in 4 bytes like MessagePack does, should that ever turn out to be useful.
This commit is contained in:
@@ -15,6 +15,8 @@ import net.sf.briar.api.serial.Tag;
|
|||||||
|
|
||||||
class ReaderImpl implements Reader {
|
class ReaderImpl implements Reader {
|
||||||
|
|
||||||
|
private static final byte[] EMPTY_BUFFER = new byte[] {};
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private Consumer[] consumers = new Consumer[] {};
|
private Consumer[] consumers = new Consumer[] {};
|
||||||
private boolean started = false, eof = false;
|
private boolean started = false, eof = false;
|
||||||
@@ -143,18 +145,21 @@ class ReaderImpl implements Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readIntoBuffer(int length) throws IOException {
|
private void readIntoBuffer(int length) throws IOException {
|
||||||
assert length > 0;
|
|
||||||
if(buf == null || buf.length < length) buf = new byte[length];
|
if(buf == null || buf.length < length) buf = new byte[length];
|
||||||
buf[0] = next;
|
readIntoBuffer(buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readIntoBuffer(byte[] b, int length) throws IOException {
|
||||||
|
b[0] = next;
|
||||||
int offset = 1;
|
int offset = 1;
|
||||||
while(offset < length) {
|
while(offset < length) {
|
||||||
int read = in.read(buf, offset, length - offset);
|
int read = in.read(b, offset, length - offset);
|
||||||
if(read == -1) break;
|
if(read == -1) break;
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
if(offset < length) throw new FormatException();
|
if(offset < length) throw new FormatException();
|
||||||
// Feed the hungry mouths
|
// Feed the hungry mouths
|
||||||
for(Consumer c : consumers) c.write(buf, 0, length);
|
for(Consumer c : consumers) c.write(b, 0, length);
|
||||||
// Read the lookahead byte
|
// Read the lookahead byte
|
||||||
int i = in.read();
|
int i = in.read();
|
||||||
if(i == -1) eof = true;
|
if(i == -1) eof = true;
|
||||||
@@ -226,24 +231,27 @@ class ReaderImpl implements Reader {
|
|||||||
public boolean hasString() throws IOException {
|
public boolean hasString() throws IOException {
|
||||||
if(!started) readNext(true);
|
if(!started) readNext(true);
|
||||||
if(eof) return false;
|
if(eof) return false;
|
||||||
return next == Tag.STRING;
|
return next == Tag.STRING
|
||||||
|
|| (next & Tag.SHORT_MASK) == Tag.SHORT_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String readString() throws IOException {
|
public String readString() throws IOException {
|
||||||
if(!hasString()) throw new FormatException();
|
if(!hasString()) throw new FormatException();
|
||||||
readNext(false);
|
if(next == Tag.STRING) {
|
||||||
int length = readLength();
|
readNext(false);
|
||||||
if(length == 0) return "";
|
return readString(readLength());
|
||||||
checkLimit(length);
|
} else {
|
||||||
readIntoBuffer(length);
|
int length = 0xFF & next ^ Tag.SHORT_STRING;
|
||||||
return new String(buf, 0, length, "UTF-8");
|
readNext(length == 0);
|
||||||
|
return readString(length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasLength() throws IOException {
|
private String readString(int length) throws IOException {
|
||||||
if(!started) readNext(true);
|
assert length >= 0;
|
||||||
if(eof) return false;
|
if(length == 0) return "";
|
||||||
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|
readIntoBuffer(length);
|
||||||
|| next == Tag.INT32;
|
return new String(buf, 0, length, "UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readLength() throws IOException {
|
private int readLength() throws IOException {
|
||||||
@@ -255,32 +263,44 @@ class ReaderImpl implements Reader {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkLimit(long bytes) throws FormatException {
|
private boolean hasLength() throws IOException {
|
||||||
// FIXME
|
if(!started) readNext(true);
|
||||||
|
if(eof) return false;
|
||||||
|
return next >= 0 || next == Tag.INT8 || next == Tag.INT16
|
||||||
|
|| next == Tag.INT32;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRaw() throws IOException {
|
public boolean hasRaw() throws IOException {
|
||||||
if(!started) readNext(true);
|
if(!started) readNext(true);
|
||||||
if(eof) return false;
|
if(eof) return false;
|
||||||
return next == Tag.RAW;
|
return next == Tag.RAW || (next & Tag.SHORT_MASK) == Tag.SHORT_RAW;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] readRaw() throws IOException {
|
public byte[] readRaw() throws IOException {
|
||||||
if(!hasRaw()) throw new FormatException();
|
if(!hasRaw()) throw new FormatException();
|
||||||
readNext(false);
|
if(next == Tag.RAW) {
|
||||||
int length = readLength();
|
readNext(false);
|
||||||
if(length == 0) return new byte[] {};
|
return readRaw(readLength());
|
||||||
checkLimit(length);
|
} else {
|
||||||
readIntoBuffer(length);
|
int length = 0xFF & next ^ Tag.SHORT_RAW;
|
||||||
byte[] b = buf;
|
readNext(length == 0);
|
||||||
buf = null;
|
return readRaw(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readRaw(int length) throws IOException {
|
||||||
|
assert length >= 0;
|
||||||
|
if(length == 0) return EMPTY_BUFFER;
|
||||||
|
byte[] b = new byte[length];
|
||||||
|
readIntoBuffer(b, length);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasList() throws IOException {
|
public boolean hasList() throws IOException {
|
||||||
if(!started) readNext(true);
|
if(!started) readNext(true);
|
||||||
if(eof) return false;
|
if(eof) return false;
|
||||||
return next == Tag.LIST || next == Tag.LIST_START;
|
return next == Tag.LIST || next == Tag.LIST_START
|
||||||
|
|| (next & Tag.SHORT_MASK) == Tag.SHORT_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> readList() throws IOException {
|
public List<Object> readList() throws IOException {
|
||||||
@@ -289,16 +309,26 @@ class ReaderImpl implements Reader {
|
|||||||
|
|
||||||
public <E> List<E> readList(Class<E> e) throws IOException {
|
public <E> List<E> readList(Class<E> e) throws IOException {
|
||||||
if(!hasList()) throw new FormatException();
|
if(!hasList()) throw new FormatException();
|
||||||
boolean definite = next == Tag.LIST;
|
if(next == Tag.LIST) {
|
||||||
readNext(false);
|
readNext(false);
|
||||||
List<E> list = new ArrayList<E>();
|
return readList(e, readLength());
|
||||||
if(definite) {
|
} else if(next == Tag.LIST_START) {
|
||||||
int length = readLength();
|
readNext(false);
|
||||||
for(int i = 0; i < length; i++) list.add(readObject(e));
|
List<E> list = new ArrayList<E>();
|
||||||
} else {
|
|
||||||
while(!hasEnd()) list.add(readObject(e));
|
while(!hasEnd()) list.add(readObject(e));
|
||||||
readEnd();
|
readEnd();
|
||||||
|
return list;
|
||||||
|
} else {
|
||||||
|
int length = 0xFF & next ^ Tag.SHORT_LIST;
|
||||||
|
readNext(length == 0);
|
||||||
|
return readList(e, length);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +345,6 @@ class ReaderImpl implements Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object readObject() throws IOException {
|
private Object readObject() throws IOException {
|
||||||
// FIXME: Use a switch statement
|
|
||||||
if(!started) throw new IllegalStateException();
|
if(!started) throw new IllegalStateException();
|
||||||
if(hasBoolean()) return Boolean.valueOf(readBoolean());
|
if(hasBoolean()) return Boolean.valueOf(readBoolean());
|
||||||
if(hasUint7()) return Byte.valueOf(readUint7());
|
if(hasUint7()) return Byte.valueOf(readUint7());
|
||||||
@@ -367,7 +396,8 @@ class ReaderImpl implements Reader {
|
|||||||
public boolean hasMap() throws IOException {
|
public boolean hasMap() throws IOException {
|
||||||
if(!started) readNext(true);
|
if(!started) readNext(true);
|
||||||
if(eof) return false;
|
if(eof) return false;
|
||||||
return next == Tag.MAP || next == Tag.MAP_START;
|
return next == Tag.MAP || next == Tag.MAP_START
|
||||||
|
|| (next & Tag.SHORT_MASK) == Tag.SHORT_MAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Object, Object> readMap() throws IOException {
|
public Map<Object, Object> readMap() throws IOException {
|
||||||
@@ -376,16 +406,27 @@ class ReaderImpl implements Reader {
|
|||||||
|
|
||||||
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException {
|
public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException {
|
||||||
if(!hasMap()) throw new FormatException();
|
if(!hasMap()) throw new FormatException();
|
||||||
boolean definite = next == Tag.MAP;
|
if(next == Tag.MAP) {
|
||||||
readNext(false);
|
readNext(false);
|
||||||
Map<K, V> m = new HashMap<K, V>();
|
return readMap(k, v, readLength());
|
||||||
if(definite) {
|
} else if(next == Tag.MAP_START) {
|
||||||
int length = readLength();
|
readNext(false);
|
||||||
for(int i = 0; i < length; i++) m.put(readObject(k), readObject(v));
|
Map<K, V> m = new HashMap<K, V>();
|
||||||
} else {
|
|
||||||
while(!hasEnd()) m.put(readObject(k), readObject(v));
|
while(!hasEnd()) m.put(readObject(k), readObject(v));
|
||||||
readEnd();
|
readEnd();
|
||||||
|
return m;
|
||||||
|
} else {
|
||||||
|
int size = 0xFF & next ^ Tag.SHORT_MAP;
|
||||||
|
readNext(size == 0);
|
||||||
|
return readMap(k, v, size);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <K, V> Map<K, V> readMap(Class<K> k, Class<V> v, int size)
|
||||||
|
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));
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,16 +108,38 @@ class WriterImpl implements Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeString(String s) throws IOException {
|
public void writeString(String s) throws IOException {
|
||||||
out.write(Tag.STRING);
|
|
||||||
byte[] b = s.getBytes("UTF-8");
|
byte[] b = s.getBytes("UTF-8");
|
||||||
writeIntAny(b.length);
|
if(b.length < 16) out.write(intToByte(Tag.SHORT_STRING | b.length));
|
||||||
|
else {
|
||||||
|
out.write(Tag.STRING);
|
||||||
|
writeLength(b.length);
|
||||||
|
}
|
||||||
out.write(b);
|
out.write(b);
|
||||||
bytesWritten += b.length + 1;
|
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 {
|
||||||
|
if(i >= 0 && i <= Byte.MAX_VALUE)
|
||||||
|
writeUint7((byte) i);
|
||||||
|
else if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE)
|
||||||
|
writeInt8((byte) i);
|
||||||
|
else if(i >= Short.MIN_VALUE && i <= Short.MAX_VALUE)
|
||||||
|
writeInt16((short) i);
|
||||||
|
else writeInt32(i);
|
||||||
|
}
|
||||||
|
|
||||||
public void writeRaw(byte[] b) throws IOException {
|
public void writeRaw(byte[] b) throws IOException {
|
||||||
out.write(Tag.RAW);
|
if(b.length < 16) out.write(intToByte(Tag.SHORT_RAW | b.length));
|
||||||
writeIntAny(b.length);
|
else {
|
||||||
|
out.write(Tag.RAW);
|
||||||
|
writeLength(b.length);
|
||||||
|
}
|
||||||
out.write(b);
|
out.write(b);
|
||||||
bytesWritten += b.length + 1;
|
bytesWritten += b.length + 1;
|
||||||
}
|
}
|
||||||
@@ -127,10 +149,14 @@ class WriterImpl implements Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeList(List<?> l) throws IOException {
|
public void writeList(List<?> l) throws IOException {
|
||||||
out.write(Tag.LIST);
|
int length = l.size();
|
||||||
bytesWritten++;
|
if(length < 16) out.write(intToByte(Tag.SHORT_LIST | length));
|
||||||
writeIntAny(l.size());
|
else {
|
||||||
|
out.write(Tag.LIST);
|
||||||
|
writeLength(length);
|
||||||
|
}
|
||||||
for(Object o : l) writeObject(o);
|
for(Object o : l) writeObject(o);
|
||||||
|
bytesWritten++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeObject(Object o) throws IOException {
|
private void writeObject(Object o) throws IOException {
|
||||||
@@ -160,13 +186,17 @@ class WriterImpl implements Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeMap(Map<?, ?> m) throws IOException {
|
public void writeMap(Map<?, ?> m) throws IOException {
|
||||||
out.write(Tag.MAP);
|
int length = m.size();
|
||||||
bytesWritten++;
|
if(length < 16) out.write(intToByte(Tag.SHORT_MAP | length));
|
||||||
writeIntAny(m.size());
|
else {
|
||||||
|
out.write(Tag.MAP);
|
||||||
|
writeLength(length);
|
||||||
|
}
|
||||||
for(Entry<?, ?> e : m.entrySet()) {
|
for(Entry<?, ?> e : m.entrySet()) {
|
||||||
writeObject(e.getKey());
|
writeObject(e.getKey());
|
||||||
writeObject(e.getValue());
|
writeObject(e.getValue());
|
||||||
}
|
}
|
||||||
|
bytesWritten++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeMapStart() throws IOException {
|
public void writeMapStart() throws IOException {
|
||||||
|
|||||||
@@ -120,25 +120,27 @@ public class ReaderImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadString() throws IOException {
|
public void testReadString() throws IOException {
|
||||||
setContents("F703666F6F" + "F703666F6F" + "F700");
|
setContents("F703666F6F" + "83666F6F" + "F700" + "80");
|
||||||
assertEquals("foo", r.readString());
|
assertEquals("foo", r.readString());
|
||||||
assertEquals("foo", r.readString());
|
assertEquals("foo", r.readString());
|
||||||
assertEquals("", r.readString());
|
assertEquals("", r.readString());
|
||||||
|
assertEquals("", r.readString());
|
||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadRaw() throws IOException {
|
public void testReadRaw() throws IOException {
|
||||||
setContents("F603010203" + "F603010203" + "F600");
|
setContents("F603010203" + "93010203" + "F600" + "90");
|
||||||
assertTrue(Arrays.equals(new byte[] {1, 2, 3}, r.readRaw()));
|
assertTrue(Arrays.equals(new byte[] {1, 2, 3}, r.readRaw()));
|
||||||
assertTrue(Arrays.equals(new byte[] {1, 2, 3}, r.readRaw()));
|
assertTrue(Arrays.equals(new byte[] {1, 2, 3}, r.readRaw()));
|
||||||
assertTrue(Arrays.equals(new byte[] {}, r.readRaw()));
|
assertTrue(Arrays.equals(new byte[] {}, r.readRaw()));
|
||||||
|
assertTrue(Arrays.equals(new byte[] {}, r.readRaw()));
|
||||||
assertTrue(r.eof());
|
assertTrue(r.eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDefiniteList() throws IOException {
|
public void testReadShortList() throws IOException {
|
||||||
setContents("F5" + "03" + "01" + "F703666F6F" + "FC0080");
|
setContents("A" + "3" + "01" + "83666F6F" + "FC0080");
|
||||||
List<Object> l = r.readList(Object.class);
|
List<Object> l = r.readList(Object.class);
|
||||||
assertNotNull(l);
|
assertNotNull(l);
|
||||||
assertEquals(3, l.size());
|
assertEquals(3, l.size());
|
||||||
@@ -149,8 +151,20 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDefiniteListTypeSafe() throws IOException {
|
public void testReadList() throws IOException {
|
||||||
setContents("F5" + "03" + "01" + "02" + "03");
|
setContents("F5" + "03" + "01" + "83666F6F" + "FC0080");
|
||||||
|
List<Object> l = r.readList(Object.class);
|
||||||
|
assertNotNull(l);
|
||||||
|
assertEquals(3, l.size());
|
||||||
|
assertEquals((byte) 1, l.get(0));
|
||||||
|
assertEquals("foo", l.get(1));
|
||||||
|
assertEquals((short) 128, l.get(2));
|
||||||
|
assertTrue(r.eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadListTypeSafe() throws IOException {
|
||||||
|
setContents("A" + "3" + "01" + "02" + "03");
|
||||||
List<Byte> l = r.readList(Byte.class);
|
List<Byte> l = r.readList(Byte.class);
|
||||||
assertNotNull(l);
|
assertNotNull(l);
|
||||||
assertEquals(3, l.size());
|
assertEquals(3, l.size());
|
||||||
@@ -161,8 +175,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDefiniteMap() throws IOException {
|
public void testReadShortMap() throws IOException {
|
||||||
setContents("F4" + "02" + "F703666F6F" + "7B" + "F600" + "F0");
|
setContents("B" + "2" + "83666F6F" + "7B" + "90" + "F0");
|
||||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||||
assertNotNull(m);
|
assertNotNull(m);
|
||||||
assertEquals(2, m.size());
|
assertEquals(2, m.size());
|
||||||
@@ -174,8 +188,21 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadDefiniteMapTypeSafe() throws IOException {
|
public void testReadMap() throws IOException {
|
||||||
setContents("F4" + "02" + "F703666F6F" + "7B" + "F700" + "F0");
|
setContents("F4" + "02" + "83666F6F" + "7B" + "90" + "F0");
|
||||||
|
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||||
|
assertNotNull(m);
|
||||||
|
assertEquals(2, m.size());
|
||||||
|
assertEquals((byte) 123, m.get("foo"));
|
||||||
|
Raw raw = new RawByteArray(new byte[] {});
|
||||||
|
assertTrue(m.containsKey(raw));
|
||||||
|
assertNull(m.get(raw));
|
||||||
|
assertTrue(r.eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadMapTypeSafe() throws IOException {
|
||||||
|
setContents("B" + "2" + "83666F6F" + "7B" + "80" + "F0");
|
||||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||||
assertNotNull(m);
|
assertNotNull(m);
|
||||||
assertEquals(2, m.size());
|
assertEquals(2, m.size());
|
||||||
@@ -186,8 +213,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadIndefiniteList() throws IOException {
|
public void testReadDelimitedList() throws IOException {
|
||||||
setContents("F3" + "01" + "F703666F6F" + "FC0080" + "F1");
|
setContents("F3" + "01" + "83666F6F" + "FC0080" + "F1");
|
||||||
List<Object> l = r.readList(Object.class);
|
List<Object> l = r.readList(Object.class);
|
||||||
assertNotNull(l);
|
assertNotNull(l);
|
||||||
assertEquals(3, l.size());
|
assertEquals(3, l.size());
|
||||||
@@ -198,8 +225,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadIndfiniteListElements() throws IOException {
|
public void testReadDelimitedListElements() throws IOException {
|
||||||
setContents("F3" + "01" + "F703666F6F" + "FC0080" + "F1");
|
setContents("F3" + "01" + "83666F6F" + "FC0080" + "F1");
|
||||||
assertTrue(r.hasListStart());
|
assertTrue(r.hasListStart());
|
||||||
r.readListStart();
|
r.readListStart();
|
||||||
assertFalse(r.hasListEnd());
|
assertFalse(r.hasListEnd());
|
||||||
@@ -214,7 +241,7 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadIndefiniteListTypeSafe() throws IOException {
|
public void testReadDelimitedListTypeSafe() throws IOException {
|
||||||
setContents("F3" + "01" + "02" + "03" + "F1");
|
setContents("F3" + "01" + "02" + "03" + "F1");
|
||||||
List<Byte> l = r.readList(Byte.class);
|
List<Byte> l = r.readList(Byte.class);
|
||||||
assertNotNull(l);
|
assertNotNull(l);
|
||||||
@@ -226,8 +253,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadIndefiniteMap() throws IOException {
|
public void testReadDelimitedMap() throws IOException {
|
||||||
setContents("F2" + "F703666F6F" + "7B" + "F600" + "F0" + "F1");
|
setContents("F2" + "83666F6F" + "7B" + "90" + "F0" + "F1");
|
||||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||||
assertNotNull(m);
|
assertNotNull(m);
|
||||||
assertEquals(2, m.size());
|
assertEquals(2, m.size());
|
||||||
@@ -239,8 +266,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadIndefiniteMapEntries() throws IOException {
|
public void testReadDelimitedMapEntries() throws IOException {
|
||||||
setContents("F2" + "F703666F6F" + "7B" + "F600" + "F0" + "F1");
|
setContents("F2" + "83666F6F" + "7B" + "90" + "F0" + "F1");
|
||||||
assertTrue(r.hasMapStart());
|
assertTrue(r.hasMapStart());
|
||||||
r.readMapStart();
|
r.readMapStart();
|
||||||
assertFalse(r.hasMapEnd());
|
assertFalse(r.hasMapEnd());
|
||||||
@@ -258,8 +285,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadIndefiniteMapTypeSafe() throws IOException {
|
public void testReadDelimitedMapTypeSafe() throws IOException {
|
||||||
setContents("F2" + "F703666F6F" + "7B" + "F700" + "F0" + "F1");
|
setContents("F2" + "83666F6F" + "7B" + "80" + "F0" + "F1");
|
||||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||||
assertNotNull(m);
|
assertNotNull(m);
|
||||||
assertEquals(2, m.size());
|
assertEquals(2, m.size());
|
||||||
@@ -272,8 +299,8 @@ public class ReaderImplTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void testReadNestedMapsAndLists() throws IOException {
|
public void testReadNestedMapsAndLists() throws IOException {
|
||||||
setContents("F4" + "01" + "F4" + "01" + "F703666F6F" + "7B" +
|
setContents("B" + "1" + "B" + "1" + "83666F6F" + "7B" +
|
||||||
"F5" + "01" + "01");
|
"A" + "1" + "01");
|
||||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||||
assertNotNull(m);
|
assertNotNull(m);
|
||||||
assertEquals(1, m.size());
|
assertEquals(1, m.size());
|
||||||
|
|||||||
@@ -30,13 +30,15 @@ public class WriterImplTest extends TestCase {
|
|||||||
public void testWriteBoolean() throws IOException {
|
public void testWriteBoolean() throws IOException {
|
||||||
w.writeBoolean(true);
|
w.writeBoolean(true);
|
||||||
w.writeBoolean(false);
|
w.writeBoolean(false);
|
||||||
checkContents("FEFF");
|
// TRUE tag, FALSE tag
|
||||||
|
checkContents("FE" + "FF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint7() throws IOException {
|
public void testWriteUint7() throws IOException {
|
||||||
w.writeUint7((byte) 0);
|
w.writeUint7((byte) 0);
|
||||||
w.writeUint7((byte) 127);
|
w.writeUint7((byte) 127);
|
||||||
|
// 0, 127
|
||||||
checkContents("00" + "7F");
|
checkContents("00" + "7F");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +48,8 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeInt8((byte) -1);
|
w.writeInt8((byte) -1);
|
||||||
w.writeInt8((byte) -128);
|
w.writeInt8((byte) -128);
|
||||||
w.writeInt8((byte) 127);
|
w.writeInt8((byte) 127);
|
||||||
checkContents("FD00" + "FDFF" + "FD80" + "FD7F");
|
// INT8 tag, 0, INT8 tag, -1, INT8 tag, -128, INT8 tag, 127
|
||||||
|
checkContents("FD" + "00" + "FD" + "FF" + "FD" + "80" + "FD" + "7F");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -55,7 +58,9 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeInt16((short) -1);
|
w.writeInt16((short) -1);
|
||||||
w.writeInt16((short) -32768);
|
w.writeInt16((short) -32768);
|
||||||
w.writeInt16((short) 32767);
|
w.writeInt16((short) 32767);
|
||||||
checkContents("FC0000" + "FCFFFF" + "FC8000" + "FC7FFF");
|
// INT16 tag, 0, INT16 tag, -1, INT16 tag, -32768, INT16 tag, 32767
|
||||||
|
checkContents("FC" + "0000" + "FC" + "FFFF" + "FC" + "8000"
|
||||||
|
+ "FC" + "7FFF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -64,8 +69,9 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeInt32(-1);
|
w.writeInt32(-1);
|
||||||
w.writeInt32(-2147483648);
|
w.writeInt32(-2147483648);
|
||||||
w.writeInt32(2147483647);
|
w.writeInt32(2147483647);
|
||||||
checkContents("FB00000000" + "FBFFFFFFFF" +
|
// INT32 tag, 0, INT32 tag, -1, etc
|
||||||
"FB80000000" + "FB7FFFFFFF");
|
checkContents("FB" + "00000000" + "FB" + "FFFFFFFF" + "FB" + "80000000"
|
||||||
|
+ "FB" + "7FFFFFFF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -74,8 +80,9 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeInt64(-1L);
|
w.writeInt64(-1L);
|
||||||
w.writeInt64(-9223372036854775808L);
|
w.writeInt64(-9223372036854775808L);
|
||||||
w.writeInt64(9223372036854775807L);
|
w.writeInt64(9223372036854775807L);
|
||||||
checkContents("FA0000000000000000" + "FAFFFFFFFFFFFFFFFF" +
|
// INT64 tag, 0, INT64 tag, -1, etc
|
||||||
"FA8000000000000000" + "FA7FFFFFFFFFFFFFFF");
|
checkContents("FA" + "0000000000000000" + "FA" + "FFFFFFFFFFFFFFFF"
|
||||||
|
+ "FA" + "8000000000000000" + "FA" + "7FFFFFFFFFFFFFFF");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,8 +95,8 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeIntAny(32768L); // int32
|
w.writeIntAny(32768L); // int32
|
||||||
w.writeIntAny(2147483647L); // int32
|
w.writeIntAny(2147483647L); // int32
|
||||||
w.writeIntAny(2147483648L); // int64
|
w.writeIntAny(2147483648L); // int64
|
||||||
checkContents("00" + "7F" + "FDFF" + "FC0080" + "FC7FFF" +
|
checkContents("00" + "7F" + "FDFF" + "FC0080" + "FC7FFF"
|
||||||
"FB00008000" + "FB7FFFFFFF" + "FA0000000080000000");
|
+ "FB00008000" + "FB7FFFFFFF" + "FA0000000080000000");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -105,9 +112,9 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeFloat32(Float.NEGATIVE_INFINITY); // 1 255 0 -> 0xFF800000
|
w.writeFloat32(Float.NEGATIVE_INFINITY); // 1 255 0 -> 0xFF800000
|
||||||
w.writeFloat32(Float.POSITIVE_INFINITY); // 0 255 0 -> 0x7F800000
|
w.writeFloat32(Float.POSITIVE_INFINITY); // 0 255 0 -> 0x7F800000
|
||||||
w.writeFloat32(Float.NaN); // 0 255 1 -> 0x7FC00000
|
w.writeFloat32(Float.NaN); // 0 255 1 -> 0x7FC00000
|
||||||
checkContents("F900000000" + "F93F800000" + "F940000000" +
|
checkContents("F9" + "00000000" + "F9" + "3F800000" + "F9" + "40000000"
|
||||||
"F9BF800000" + "F980000000" + "F9FF800000" +
|
+ "F9" + "BF800000" + "F9" + "80000000" + "F9" + "FF800000"
|
||||||
"F97F800000" + "F97FC00000");
|
+ "F9" + "7F800000" + "F9" + "7FC00000");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -121,70 +128,138 @@ public class WriterImplTest extends TestCase {
|
|||||||
w.writeFloat64(Double.NEGATIVE_INFINITY); // 1 2047 0 -> 0xFFF00000...
|
w.writeFloat64(Double.NEGATIVE_INFINITY); // 1 2047 0 -> 0xFFF00000...
|
||||||
w.writeFloat64(Double.POSITIVE_INFINITY); // 0 2047 0 -> 0x7FF00000...
|
w.writeFloat64(Double.POSITIVE_INFINITY); // 0 2047 0 -> 0x7FF00000...
|
||||||
w.writeFloat64(Double.NaN); // 0 2047 1 -> 0x7FF8000000000000
|
w.writeFloat64(Double.NaN); // 0 2047 1 -> 0x7FF8000000000000
|
||||||
checkContents("F80000000000000000" + "F83FF0000000000000" +
|
checkContents("F8" + "0000000000000000" + "F8" + "3FF0000000000000"
|
||||||
"F84000000000000000" + "F8BFF0000000000000" +
|
+ "F8" + "4000000000000000" + "F8" + "BFF0000000000000"
|
||||||
"F88000000000000000" + "F8FFF0000000000000" +
|
+ "F8" + "8000000000000000" + "F8" + "FFF0000000000000"
|
||||||
"F87FF0000000000000" + "F87FF8000000000000");
|
+ "F8" + "7FF0000000000000" + "F8" + "7FF8000000000000");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUtf8() throws IOException {
|
public void testWriteShortString() throws IOException {
|
||||||
w.writeString("foo");
|
w.writeString("foo bar baz bam");
|
||||||
// UTF-8 tag, length as uint7, UTF-8 bytes
|
// SHORT_STRING tag, length 15, UTF-8 bytes
|
||||||
checkContents("F7" + "03" + "666F6F");
|
checkContents("8" + "F" + "666F6F206261722062617A2062616D");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteString() throws IOException {
|
||||||
|
w.writeString("foo bar baz bam ");
|
||||||
|
// STRING tag, length 16 as uint7, UTF-8 bytes
|
||||||
|
checkContents("F7" + "10" + "666F6F206261722062617A2062616D20");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteShortRawBytes() throws IOException {
|
||||||
|
w.writeRaw(new byte[] {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||||
|
});
|
||||||
|
// SHORT_RAW tag, length 15, raw bytes
|
||||||
|
checkContents("9" + "F" + "000102030405060708090A0B0C0D0E");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteShortRawObject() throws IOException {
|
||||||
|
w.writeRaw(new RawByteArray(new byte[] {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||||
|
}));
|
||||||
|
// SHORT_RAW tag, length 15, raw bytes
|
||||||
|
checkContents("9" + "F" + "000102030405060708090A0B0C0D0E");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteRawBytes() throws IOException {
|
public void testWriteRawBytes() throws IOException {
|
||||||
w.writeRaw(new byte[] {0, 1, -1, 127, -128});
|
w.writeRaw(new byte[] {
|
||||||
checkContents("F6" + "05" + "0001FF7F80");
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||||
|
});
|
||||||
|
// RAW tag, length 16 as uint7, raw bytes
|
||||||
|
checkContents("F6" + "10" + "000102030405060708090A0B0C0D0E0F");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteRawObject() throws IOException {
|
public void testWriteRawObject() throws IOException {
|
||||||
w.writeRaw(new RawByteArray(new byte[] {0, 1, -1, 127, -128}));
|
w.writeRaw(new RawByteArray(new byte[] {
|
||||||
checkContents("F6" + "05" + "0001FF7F80");
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||||
|
}));
|
||||||
|
// RAW tag, length 16 as uint7, raw bytes
|
||||||
|
checkContents("F6" + "10" + "000102030405060708090A0B0C0D0E0F");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteDefiniteList() throws IOException {
|
public void testWriteShortList() throws IOException {
|
||||||
List<Object> l = new ArrayList<Object>();
|
List<Object> l = new ArrayList<Object>();
|
||||||
l.add(Byte.valueOf((byte) 1)); // Written as a uint7
|
for(int i = 0; i < 15; i++) l.add(i);
|
||||||
l.add("foo");
|
|
||||||
l.add(Long.valueOf(128L)); // Written as an int16
|
|
||||||
w.writeList(l);
|
w.writeList(l);
|
||||||
checkContents("F5" + "03" + "01" + "F703666F6F" + "FC0080");
|
// SHORT_LIST tag, length, elements as uint7
|
||||||
|
checkContents("A" + "F" + "000102030405060708090A0B0C0D0E");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteDefiniteMap() throws IOException {
|
public void testWriteList() throws IOException {
|
||||||
|
List<Object> l = new ArrayList<Object>();
|
||||||
|
for(int i = 0; i < 16; i++) l.add(i);
|
||||||
|
w.writeList(l);
|
||||||
|
// LIST tag, length as uint7, elements as uint7
|
||||||
|
checkContents("F5" + "10" + "000102030405060708090A0B0C0D0E0F");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListCanContainNull() throws IOException {
|
||||||
|
List<Object> l = new ArrayList<Object>();
|
||||||
|
l.add(1);
|
||||||
|
l.add(null);
|
||||||
|
l.add(2);
|
||||||
|
w.writeList(l);
|
||||||
|
// SHORT_LIST tag, length, 1 as uint7, null, 2 as uint7
|
||||||
|
checkContents("A" + "3" + "01" + "F0" + "02");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteShortMap() throws IOException {
|
||||||
// Use LinkedHashMap to get predictable iteration order
|
// Use LinkedHashMap to get predictable iteration order
|
||||||
Map<Object, Object> m = new LinkedHashMap<Object, Object>();
|
Map<Object, Object> m = new LinkedHashMap<Object, Object>();
|
||||||
m.put("foo", Integer.valueOf(123)); // Written as a uint7
|
for(int i = 0; i < 15; i++) m.put(i, i + 1);
|
||||||
m.put(new RawByteArray(new byte[] {}), null); // Empty array != null
|
|
||||||
w.writeMap(m);
|
w.writeMap(m);
|
||||||
checkContents("F4" + "02" + "F703666F6F" + "7B" + "F600" + "F0");
|
// SHORT_MAP tag, size, entries as uint7
|
||||||
|
checkContents("B" + "F" + "0001" + "0102" + "0203" + "0304" + "0405"
|
||||||
|
+ "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C"
|
||||||
|
+ "0C0D" + "0D0E" + "0E0F");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteIndefiniteList() throws IOException {
|
public void testWriteMap() throws IOException {
|
||||||
|
// Use LinkedHashMap to get predictable iteration order
|
||||||
|
Map<Object, Object> m = new LinkedHashMap<Object, Object>();
|
||||||
|
for(int i = 0; i < 16; i++) m.put(i, i + 1);
|
||||||
|
w.writeMap(m);
|
||||||
|
// MAP tag, size as uint7, entries as uint7
|
||||||
|
checkContents("F4" + "10" + "0001" + "0102" + "0203" + "0304" + "0405"
|
||||||
|
+ "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C"
|
||||||
|
+ "0C0D" + "0D0E" + "0E0F" + "0F10");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteDelimitedList() throws IOException {
|
||||||
w.writeListStart();
|
w.writeListStart();
|
||||||
w.writeIntAny((byte) 1); // Written as uint7
|
w.writeIntAny((byte) 1); // Written as uint7
|
||||||
w.writeString("foo");
|
w.writeString("foo"); // Written as short string
|
||||||
w.writeIntAny(128L); // Written as an int16
|
w.writeIntAny(128L); // Written as an int16
|
||||||
w.writeListEnd();
|
w.writeListEnd();
|
||||||
checkContents("F3" + "01" + "F703666F6F" + "FC0080" + "F1");
|
// LIST_START tag, 1 as uint7, "foo" as short string, 128 as int16,
|
||||||
|
// END tag
|
||||||
|
checkContents("F3" + "01" + "83666F6F" + "FC0080" + "F1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteIndefiniteMap() throws IOException {
|
public void testWriteDelimitedMap() throws IOException {
|
||||||
w.writeMapStart();
|
w.writeMapStart();
|
||||||
w.writeString("foo");
|
w.writeString("foo"); // Written as short string
|
||||||
w.writeIntAny(123); // Written as a uint7
|
w.writeIntAny(123); // Written as a uint7
|
||||||
w.writeRaw(new byte[] {});
|
w.writeRaw(new byte[] {}); // Written as short raw
|
||||||
w.writeNull();
|
w.writeNull();
|
||||||
w.writeMapEnd();
|
w.writeMapEnd();
|
||||||
checkContents("F2" + "F703666F6F" + "7B" + "F600" + "F0" + "F1");
|
// MAP_START tag, "foo" as short string, 123 as uint7,
|
||||||
|
// byte[] {} as short raw, NULL tag, END tag
|
||||||
|
checkContents("F2" + "83666F6F" + "7B" + "90" + "F0" + "F1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -196,8 +271,10 @@ public class WriterImplTest extends TestCase {
|
|||||||
Map<Object, Object> m1 = new LinkedHashMap<Object, Object>();
|
Map<Object, Object> m1 = new LinkedHashMap<Object, Object>();
|
||||||
m1.put(m, l);
|
m1.put(m, l);
|
||||||
w.writeMap(m1);
|
w.writeMap(m1);
|
||||||
checkContents("F4" + "01" + "F4" + "01" + "F703666F6F" + "7B" +
|
// SHORT_MAP tag, length 1, SHORT_MAP tag, length 1,
|
||||||
"F5" + "01" + "01");
|
// "foo" as short string, 123 as uint7, SHORT_LIST tag, length 1,
|
||||||
|
// 1 as uint7
|
||||||
|
checkContents("B" + "1" + "B" + "1" + "83666F6F" + "7B" + "A1" + "01");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user