mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Reuse BdfReader/Writer for MetadataParser/Encoder.
This commit is contained in:
@@ -1,186 +1,71 @@
|
||||
package org.briarproject.data;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.briarproject.api.Bytes;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfWriter;
|
||||
import org.briarproject.api.data.BdfWriterFactory;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.data.Types.DICTIONARY;
|
||||
import static org.briarproject.data.Types.END;
|
||||
import static org.briarproject.data.Types.FALSE;
|
||||
import static org.briarproject.data.Types.FLOAT_64;
|
||||
import static org.briarproject.data.Types.INT_16;
|
||||
import static org.briarproject.data.Types.INT_32;
|
||||
import static org.briarproject.data.Types.INT_64;
|
||||
import static org.briarproject.data.Types.INT_8;
|
||||
import static org.briarproject.data.Types.LIST;
|
||||
import static org.briarproject.data.Types.NULL;
|
||||
import static org.briarproject.data.Types.RAW_16;
|
||||
import static org.briarproject.data.Types.RAW_32;
|
||||
import static org.briarproject.data.Types.RAW_8;
|
||||
import static org.briarproject.data.Types.STRING_16;
|
||||
import static org.briarproject.data.Types.STRING_32;
|
||||
import static org.briarproject.data.Types.STRING_8;
|
||||
import static org.briarproject.data.Types.TRUE;
|
||||
|
||||
class MetadataEncoderImpl implements MetadataEncoder {
|
||||
|
||||
private final BdfWriterFactory bdfWriterFactory;
|
||||
|
||||
@Inject
|
||||
MetadataEncoderImpl(BdfWriterFactory bdfWriterFactory) {
|
||||
this.bdfWriterFactory = bdfWriterFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metadata encode(BdfDictionary d) throws FormatException {
|
||||
Metadata m = new Metadata();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
for (Entry<String, Object> e : d.entrySet()) {
|
||||
if (e.getValue() == NULL_VALUE) {
|
||||
// Special case: if the value is null, the key is being removed
|
||||
m.put(e.getKey(), REMOVE);
|
||||
} else {
|
||||
encodeObject(out, e.getValue());
|
||||
m.put(e.getKey(), out.toByteArray());
|
||||
out.reset();
|
||||
BdfWriter writer = bdfWriterFactory.createWriter(out);
|
||||
try {
|
||||
for (Entry<String, Object> e : d.entrySet()) {
|
||||
if (e.getValue() == NULL_VALUE) {
|
||||
// Special case: if value is null, key is being removed
|
||||
m.put(e.getKey(), REMOVE);
|
||||
} else {
|
||||
encodeObject(writer, e.getValue());
|
||||
m.put(e.getKey(), out.toByteArray());
|
||||
out.reset();
|
||||
}
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private void encodeObject(ByteArrayOutputStream out, Object o)
|
||||
throws FormatException {
|
||||
if (o == NULL_VALUE) out.write(NULL);
|
||||
else if (o instanceof Boolean) out.write((Boolean) o ? TRUE : FALSE);
|
||||
else if (o instanceof Byte) encodeInteger(out, (Byte) o);
|
||||
else if (o instanceof Short) encodeInteger(out, (Short) o);
|
||||
else if (o instanceof Integer) encodeInteger(out, (Integer) o);
|
||||
else if (o instanceof Long) encodeInteger(out, (Long) o);
|
||||
else if (o instanceof Float) encodeFloat(out, (Float) o);
|
||||
else if (o instanceof Double) encodeFloat(out, (Double) o);
|
||||
else if (o instanceof String) encodeString(out, (String) o);
|
||||
else if (o instanceof byte[]) encodeRaw(out, (byte[]) o);
|
||||
else if (o instanceof Bytes) encodeRaw(out, ((Bytes) o).getBytes());
|
||||
else if (o instanceof List) encodeList(out, (List) o);
|
||||
else if (o instanceof Map) encodeDictionary(out, (Map) o);
|
||||
private void encodeObject(BdfWriter writer, Object o)
|
||||
throws IOException {
|
||||
if (o instanceof Boolean) writer.writeBoolean((Boolean) o);
|
||||
else if (o instanceof Byte) writer.writeLong((Byte) o);
|
||||
else if (o instanceof Short) writer.writeLong((Short) o);
|
||||
else if (o instanceof Integer) writer.writeLong((Integer) o);
|
||||
else if (o instanceof Long) writer.writeLong((Long) o);
|
||||
else if (o instanceof Float) writer.writeDouble((Float) o);
|
||||
else if (o instanceof Double) writer.writeDouble((Double) o);
|
||||
else if (o instanceof String) writer.writeString((String) o);
|
||||
else if (o instanceof byte[]) writer.writeRaw((byte[]) o);
|
||||
else if (o instanceof Bytes) writer.writeRaw(((Bytes) o).getBytes());
|
||||
else if (o instanceof List) writer.writeList((List) o);
|
||||
else if (o instanceof Map) writer.writeDictionary((Map) o);
|
||||
else throw new FormatException();
|
||||
}
|
||||
|
||||
private void encodeInteger(ByteArrayOutputStream out, byte i) {
|
||||
out.write(INT_8);
|
||||
encodeInt8(out, i);
|
||||
}
|
||||
|
||||
private void encodeInteger(ByteArrayOutputStream out, short i) {
|
||||
if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
|
||||
encodeInteger(out, (byte) i);
|
||||
} else {
|
||||
out.write(INT_16);
|
||||
encodeInt16(out, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeInteger(ByteArrayOutputStream out, int i) {
|
||||
if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
|
||||
encodeInteger(out, (short) i);
|
||||
} else {
|
||||
out.write(INT_32);
|
||||
encodeInt32(out, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeInteger(ByteArrayOutputStream out, long i) {
|
||||
if (i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE) {
|
||||
encodeInteger(out, (int) i);
|
||||
} else {
|
||||
out.write(INT_64);
|
||||
encodeInt64(out, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeInt8(ByteArrayOutputStream out, byte i) {
|
||||
out.write(i);
|
||||
}
|
||||
|
||||
private void encodeInt16(ByteArrayOutputStream out, short i) {
|
||||
out.write((byte) (i >> 8));
|
||||
out.write((byte) ((i << 8) >> 8));
|
||||
}
|
||||
|
||||
private void encodeInt32(ByteArrayOutputStream out, int i) {
|
||||
out.write((byte) (i >> 24));
|
||||
out.write((byte) ((i << 8) >> 24));
|
||||
out.write((byte) ((i << 16) >> 24));
|
||||
out.write((byte) ((i << 24) >> 24));
|
||||
}
|
||||
|
||||
private void encodeInt64(ByteArrayOutputStream out, long i) {
|
||||
out.write((byte) (i >> 56));
|
||||
out.write((byte) ((i << 8) >> 56));
|
||||
out.write((byte) ((i << 16) >> 56));
|
||||
out.write((byte) ((i << 24) >> 56));
|
||||
out.write((byte) ((i << 32) >> 56));
|
||||
out.write((byte) ((i << 40) >> 56));
|
||||
out.write((byte) ((i << 48) >> 56));
|
||||
out.write((byte) ((i << 56) >> 56));
|
||||
}
|
||||
|
||||
private void encodeFloat(ByteArrayOutputStream out, float f) {
|
||||
encodeFloat(out, (double) f);
|
||||
}
|
||||
|
||||
private void encodeFloat(ByteArrayOutputStream out, double d) {
|
||||
out.write(FLOAT_64);
|
||||
encodeInt64(out, Double.doubleToLongBits(d));
|
||||
}
|
||||
|
||||
private void encodeString(ByteArrayOutputStream out, String s) {
|
||||
byte[] b = StringUtils.toUtf8(s);
|
||||
if (b.length <= Byte.MAX_VALUE) {
|
||||
out.write(STRING_8);
|
||||
encodeInt8(out, (byte) b.length);
|
||||
} else if (b.length <= Short.MAX_VALUE) {
|
||||
out.write(STRING_16);
|
||||
encodeInt16(out, (short) b.length);
|
||||
} else {
|
||||
out.write(STRING_32);
|
||||
encodeInt32(out, b.length);
|
||||
}
|
||||
out.write(b, 0, b.length);
|
||||
}
|
||||
|
||||
private void encodeRaw(ByteArrayOutputStream out, byte[] b) {
|
||||
if (b.length <= Byte.MAX_VALUE) {
|
||||
out.write(RAW_8);
|
||||
encodeInt8(out, (byte) b.length);
|
||||
} else if (b.length <= Short.MAX_VALUE) {
|
||||
out.write(RAW_16);
|
||||
encodeInt16(out, (short) b.length);
|
||||
} else {
|
||||
out.write(RAW_32);
|
||||
encodeInt32(out, b.length);
|
||||
}
|
||||
out.write(b, 0, b.length);
|
||||
}
|
||||
|
||||
private void encodeList(ByteArrayOutputStream out, List list)
|
||||
throws FormatException {
|
||||
out.write(LIST);
|
||||
for (Object o : list) encodeObject(out, o);
|
||||
out.write(END);
|
||||
}
|
||||
|
||||
private void encodeDictionary(ByteArrayOutputStream out, Map<?, ?> map)
|
||||
throws FormatException {
|
||||
out.write(DICTIONARY);
|
||||
for (Entry<?, ?> e : map.entrySet()) {
|
||||
if (!(e.getKey() instanceof String)) throw new FormatException();
|
||||
encodeString(out, (String) e.getKey());
|
||||
encodeObject(out, e.getValue());
|
||||
}
|
||||
out.write(END);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,168 +1,64 @@
|
||||
package org.briarproject.data;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.BdfReader;
|
||||
import org.briarproject.api.data.BdfReaderFactory;
|
||||
import org.briarproject.api.data.MetadataParser;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.data.Types.DICTIONARY;
|
||||
import static org.briarproject.data.Types.END;
|
||||
import static org.briarproject.data.Types.FALSE;
|
||||
import static org.briarproject.data.Types.FLOAT_64;
|
||||
import static org.briarproject.data.Types.INT_16;
|
||||
import static org.briarproject.data.Types.INT_32;
|
||||
import static org.briarproject.data.Types.INT_64;
|
||||
import static org.briarproject.data.Types.INT_8;
|
||||
import static org.briarproject.data.Types.LIST;
|
||||
import static org.briarproject.data.Types.NULL;
|
||||
import static org.briarproject.data.Types.RAW_16;
|
||||
import static org.briarproject.data.Types.RAW_32;
|
||||
import static org.briarproject.data.Types.RAW_8;
|
||||
import static org.briarproject.data.Types.STRING_16;
|
||||
import static org.briarproject.data.Types.STRING_32;
|
||||
import static org.briarproject.data.Types.STRING_8;
|
||||
import static org.briarproject.data.Types.TRUE;
|
||||
|
||||
class MetadataParserImpl implements MetadataParser {
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
|
||||
@Inject
|
||||
MetadataParserImpl(BdfReaderFactory bdfReaderFactory) {
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfDictionary parse(Metadata m) throws FormatException {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
for (Entry<String, byte[]> e : m.entrySet())
|
||||
d.put(e.getKey(), parseValue(e.getValue()));
|
||||
try {
|
||||
for (Entry<String, byte[]> e : m.entrySet()) {
|
||||
// Special case: if key is being removed, value is null
|
||||
if (e.getValue() == REMOVE) d.put(e.getKey(), NULL_VALUE);
|
||||
else d.put(e.getKey(), parseValue(e.getValue()));
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
private Object parseValue(byte[] b) throws FormatException {
|
||||
if (b == REMOVE) return NULL_VALUE;
|
||||
private Object parseValue(byte[] b) throws IOException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Object o = parseObject(in);
|
||||
if (in.available() > 0) throw new FormatException();
|
||||
BdfReader reader = bdfReaderFactory.createReader(in);
|
||||
Object o = parseObject(reader);
|
||||
if (!reader.eof()) throw new FormatException();
|
||||
return o;
|
||||
}
|
||||
|
||||
private Object parseObject(ByteArrayInputStream in) throws FormatException {
|
||||
switch(in.read()) {
|
||||
case NULL:
|
||||
return NULL_VALUE;
|
||||
case TRUE:
|
||||
return Boolean.TRUE;
|
||||
case FALSE:
|
||||
return Boolean.FALSE;
|
||||
case INT_8:
|
||||
return (long) parseInt8(in);
|
||||
case INT_16:
|
||||
return (long) parseInt16(in);
|
||||
case INT_32:
|
||||
return (long) parseInt32(in);
|
||||
case INT_64:
|
||||
return parseInt64(in);
|
||||
case FLOAT_64:
|
||||
return Double.longBitsToDouble(parseInt64(in));
|
||||
case STRING_8:
|
||||
return parseString(in, parseInt8(in));
|
||||
case STRING_16:
|
||||
return parseString(in, parseInt16(in));
|
||||
case STRING_32:
|
||||
return parseString(in, parseInt32(in));
|
||||
case RAW_8:
|
||||
return parseRaw(in, parseInt8(in));
|
||||
case RAW_16:
|
||||
return parseRaw(in, parseInt16(in));
|
||||
case RAW_32:
|
||||
return parseRaw(in, parseInt32(in));
|
||||
case LIST:
|
||||
return parseList(in);
|
||||
case DICTIONARY:
|
||||
return parseDictionary(in);
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
private String parseString(ByteArrayInputStream in) throws FormatException {
|
||||
switch(in.read()) {
|
||||
case STRING_8:
|
||||
return parseString(in, parseInt8(in));
|
||||
case STRING_16:
|
||||
return parseString(in, parseInt16(in));
|
||||
case STRING_32:
|
||||
return parseString(in, parseInt32(in));
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
private byte parseInt8(ByteArrayInputStream in) throws FormatException {
|
||||
if (in.available() < 1) throw new FormatException();
|
||||
return (byte) in.read();
|
||||
}
|
||||
|
||||
private short parseInt16(ByteArrayInputStream in) throws FormatException {
|
||||
if (in.available() < 2) throw new FormatException();
|
||||
return (short) (((in.read() & 0xFF) << 8) + (in.read() & 0xFF));
|
||||
}
|
||||
|
||||
private int parseInt32(ByteArrayInputStream in) throws FormatException {
|
||||
if (in.available() < 4) throw new FormatException();
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
value |= (in.read() & 0xFF) << (24 - i * 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
private long parseInt64(ByteArrayInputStream in) throws FormatException {
|
||||
if (in.available() < 8) throw new FormatException();
|
||||
long value = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
value |= (in.read() & 0xFFL) << (56 - i * 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
private String parseString(ByteArrayInputStream in, int len)
|
||||
throws FormatException {
|
||||
if (len < 0) throw new FormatException();
|
||||
byte[] b = new byte[len];
|
||||
if (in.read(b, 0, len) != len) throw new FormatException();
|
||||
return StringUtils.fromUtf8(b, 0, len);
|
||||
}
|
||||
|
||||
private byte[] parseRaw(ByteArrayInputStream in, int len)
|
||||
throws FormatException {
|
||||
if (len < 0) throw new FormatException();
|
||||
byte[] b = new byte[len];
|
||||
if (in.read(b, 0, len) != len) throw new FormatException();
|
||||
return b;
|
||||
}
|
||||
|
||||
private BdfList parseList(ByteArrayInputStream in) throws FormatException {
|
||||
BdfList list = new BdfList();
|
||||
for (int b = peek(in); b != -1 && b != (END & 0xFF); b = peek(in))
|
||||
list.add(parseObject(in));
|
||||
if (in.read() != (END & 0xFF)) throw new FormatException();
|
||||
return list;
|
||||
}
|
||||
|
||||
private BdfDictionary parseDictionary(ByteArrayInputStream in)
|
||||
throws FormatException {
|
||||
BdfDictionary dict = new BdfDictionary();
|
||||
for (int b = peek(in); b != -1 && b != (END & 0xFF); b = peek(in))
|
||||
dict.put(parseString(in), parseObject(in));
|
||||
if (in.read() != (END & 0xFF)) throw new FormatException();
|
||||
return dict;
|
||||
}
|
||||
|
||||
private int peek(ByteArrayInputStream in) {
|
||||
in.mark(1);
|
||||
int next = in.read();
|
||||
in.reset();
|
||||
return next;
|
||||
private Object parseObject(BdfReader reader) throws IOException {
|
||||
if (reader.hasNull()) return NULL_VALUE;
|
||||
if (reader.hasBoolean()) return reader.readBoolean();
|
||||
if (reader.hasLong()) return reader.readLong();
|
||||
if (reader.hasDouble()) return reader.readDouble();
|
||||
if (reader.hasString()) return reader.readString(Integer.MAX_VALUE);
|
||||
if (reader.hasRaw()) return reader.readRaw(Integer.MAX_VALUE);
|
||||
if (reader.hasList()) return reader.readList();
|
||||
if (reader.hasDictionary()) return reader.readDictionary();
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.BriarTestCase;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -16,9 +17,16 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class MetadataEncoderParserImplTest extends BriarTestCase {
|
||||
|
||||
MetadataEncoderImpl e = new MetadataEncoderImpl();
|
||||
MetadataParserImpl p = new MetadataParserImpl();
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
MetadataEncoderImpl e;
|
||||
MetadataParserImpl p;
|
||||
BdfDictionary d;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
e = new MetadataEncoderImpl(new BdfWriterFactoryImpl());
|
||||
p = new MetadataParserImpl(new BdfReaderFactoryImpl());
|
||||
d = new BdfDictionary();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoolean() throws FormatException {
|
||||
|
||||
Reference in New Issue
Block a user