--- /dev/null
+package org.simantics.databoard.streaming;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.util.binary.Endian;
+import org.simantics.databoard.util.binary.UTF8;
+
+/**
+ * This is an utility class that encapsulates the databoard value decoding scheme.
+ * It can be used for streaming value reading.
+ */
+public class DataReader {
+ private static final Serializer DATATYPE_SERIALIZER = Bindings.getSerializerUnchecked(Bindings.getBindingUnchecked( Datatype.class ));
+
+ private final DataInput in;
+
+ public DataReader(DataInput in) {
+ this.in = in;
+ }
+
+ public DataReader(InputStream stream) {
+ this.in = new DataInputStream(stream);
+ }
+
+ public boolean readBoolean() throws IOException {
+ return in.readByte() != 0;
+ }
+
+ public byte readByte() throws IOException {
+ return in.readByte();
+ }
+
+ public int readInteger() throws IOException {
+ return in.readInt();
+ }
+
+ public long readLong() throws IOException {
+ return in.readLong();
+ }
+
+ public float readFloat() throws IOException {
+ return in.readFloat();
+ }
+
+ public double readDouble() throws IOException {
+ return in.readDouble();
+ }
+
+ public int readStringLength() throws IOException {
+ return Endian.readDynamicUInt32(in);
+ }
+
+ public String readStringContent(int length) throws IOException {
+ return UTF8.readModifiedUTF(in, length);
+ }
+
+ public String readString() throws IOException {
+ int length = readStringLength();
+ return readStringContent(length);
+ }
+
+ public Datatype readDatatype() throws IOException {
+ return (Datatype)DATATYPE_SERIALIZER.deserialize(in);
+ }
+
+ /**
+ * A variable length array is started with the length
+ * followed by the serialization of all elements.
+ */
+ public int beginVariableLengthArray() throws IOException {
+ return in.readInt();
+ }
+
+ /**
+ * A map is started with its size followed by
+ * serialization of interleaved keys and values.
+ */
+ public int beginMap() throws IOException {
+ return in.readInt();
+ }
+
+ /**
+ * Starts reading optional value. False is returned,
+ * if the optional is null.
+ */
+ public boolean beginOptional() throws IOException {
+ return in.readByte() != 0;
+ }
+
+ /**
+ * Selects the constructor of the union type.
+ * It is written as a variable length integer,
+ * so the total number of tags is required.
+ */
+ public int readUnionTag(int tagCount) throws IOException {
+ return Endian.getUInt(in, tagCount-1);
+ }
+
+ /**
+ * Reads a record reference. Value 0 indicates that a new record is encountered.
+ * In this case reading the reference again returns the id of the new record.
+ */
+ public int readReferableRecordReference() throws IOException {
+ return in.readInt();
+ }
+}