From 7e0a91cef9960901145459f8b4f6e1c4fcea8eba Mon Sep 17 00:00:00 2001 From: Sven Gestegard Robertz <sven.robertz@cs.lth.se> Date: Fri, 7 Feb 2014 10:16:46 +0100 Subject: [PATCH] started creating labcomm2006 for legacy interfaces --- .../se/lth/control/labcomm2006/LabComm.java | 32 ++++ .../control/labcomm2006/LabCommDecoder.java | 19 +++ .../labcomm2006/LabCommDecoderChannel.java | 152 ++++++++++++++++++ .../labcomm2006/LabCommDecoderRegistry.java | 144 +++++++++++++++++ .../labcomm2006/LabCommDispatcher.java | 15 ++ .../control/labcomm2006/LabCommEncoder.java | 20 +++ .../labcomm2006/LabCommEncoderChannel.java | 117 ++++++++++++++ .../labcomm2006/LabCommEncoderRegistry.java | 55 +++++++ .../control/labcomm2006/LabCommHandler.java | 4 + .../control/labcomm2006/LabCommReader.java | 7 + .../control/labcomm2006/LabCommSample.java | 5 + .../lth/control/labcomm2006/LabCommType.java | 5 + .../control/labcomm2006/LabCommWriter.java | 9 ++ .../control/labcomm2006/WriterWrapper.java | 17 ++ 14 files changed, 601 insertions(+) create mode 100644 lib/java/se/lth/control/labcomm2006/LabComm.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDecoder.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommEncoder.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommHandler.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommReader.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommSample.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommType.java create mode 100644 lib/java/se/lth/control/labcomm2006/LabCommWriter.java create mode 100644 lib/java/se/lth/control/labcomm2006/WriterWrapper.java diff --git a/lib/java/se/lth/control/labcomm2006/LabComm.java b/lib/java/se/lth/control/labcomm2006/LabComm.java new file mode 100644 index 0000000..16b5abd --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabComm.java @@ -0,0 +1,32 @@ +package se.lth.control.labcomm2006; + +public class LabComm { + + public static final String VERSION = "LabComm2006"; + + /* + * Predeclared aggregate type indices + */ + public static final int TYPEDEF = 0x01; + public static final int SAMPLE = 0x02; + public static final int ARRAY = 0x10; + public static final int STRUCT = 0x11; + + /* + * Predeclared primitive type indices + */ + public static final int BOOLEAN = 0x20; + public static final int BYTE = 0x21; + public static final int SHORT = 0x22; + public static final int INT = 0x23; + public static final int LONG = 0x24; + public static final int FLOAT = 0x25; + public static final int DOUBLE = 0x26; + public static final int STRING = 0x27; + + /* + * Start of user declared types + */ + public static final int FIRST_USER_INDEX = 0x40; + +} diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDecoder.java b/lib/java/se/lth/control/labcomm2006/LabCommDecoder.java new file mode 100644 index 0000000..864e43e --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommDecoder.java @@ -0,0 +1,19 @@ +package se.lth.control.labcomm2006; + +import java.io.IOException; + +public interface LabCommDecoder { + + public void register(LabCommDispatcher dispatcher, + LabCommHandler handler) throws IOException; + public boolean decodeBoolean() throws IOException; + public byte decodeByte() throws IOException; + public short decodeShort() throws IOException; + public int decodeInt() throws IOException; + public long decodeLong() throws IOException; + public float decodeFloat() throws IOException; + public double decodeDouble() throws IOException; + public String decodeString() throws IOException; + public int decodePacked32() throws IOException; + +} diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java b/lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java new file mode 100644 index 0000000..f12fffc --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommDecoderChannel.java @@ -0,0 +1,152 @@ +package se.lth.control.labcomm2006; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.IOException; + +public class LabCommDecoderChannel implements LabCommDecoder { + + private DataInputStream in; + private LabCommDecoderRegistry registry; + + public LabCommDecoderChannel(InputStream in) throws IOException { + this.in = new DataInputStream(in); + registry = new LabCommDecoderRegistry(); + } + + public void runOne() throws Exception { + boolean done = false; + while (!done) { + int tag = decodePacked32(); + switch (tag) { + case LabComm.TYPEDEF: + case LabComm.SAMPLE: { + int index = decodePacked32(); + String name = decodeString(); + ByteArrayOutputStream signature = new ByteArrayOutputStream(); + collectFlatSignature(new LabCommEncoderChannel(signature, false)); + registry.add(index, name, signature.toByteArray()); + } break; + default: { + LabCommDecoderRegistry.Entry e = registry.get(tag); + if (e == null) { + throw new IOException("Unhandled tag " + tag); + } + LabCommDispatcher d = e.getDispatcher(); + if (d == null) { + throw new IOException("No dispatcher for '" + e.getName() + "'"); + } + LabCommHandler h = e.getHandler(); + if (h == null) { + throw new IOException("No handler for '" + e.getName() +"'"); + } + d.decodeAndHandle(this, h); + done = true; + } + } + } + } + + public void run() throws Exception { + while (true) { + runOne(); + } + } + + private void collectFlatSignature(LabCommEncoder out) throws IOException { + int type = decodePacked32(); + out.encodePacked32(type); + switch (type) { + case LabComm.ARRAY: { + int dimensions = decodePacked32(); + out.encodePacked32(dimensions); + for (int i = 0 ; i < dimensions ; i++) { + out.encodePacked32(decodePacked32()); + } + collectFlatSignature(out); + } break; + case LabComm.STRUCT: { + int fields = decodePacked32(); + out.encodePacked32(fields); + for (int i = 0 ; i < fields ; i++) { + out.encodeString(decodeString()); + collectFlatSignature(out); + } + } break; + case LabComm.BOOLEAN: + case LabComm.BYTE: + case LabComm.SHORT: + case LabComm.INT: + case LabComm.LONG: + case LabComm.FLOAT: + case LabComm.DOUBLE: + case LabComm.STRING: { + } break; + default: { + throw new IOException("Unimplemented type=" + type); + } + } + out.end(null); + } + + public void register(LabCommDispatcher dispatcher, + LabCommHandler handler) throws IOException { + registry.add(dispatcher, handler); + } + + public boolean decodeBoolean() throws IOException { + return in.readBoolean(); + } + + public byte decodeByte() throws IOException { + return in.readByte(); + } + + public short decodeShort() throws IOException { + return in.readShort(); + } + + public int decodeInt() throws IOException { + return in.readInt(); + } + + public long decodeLong() throws IOException { + return in.readLong(); + } + + public float decodeFloat() throws IOException { + return in.readFloat(); + } + + public double decodeDouble() throws IOException { + return in.readDouble(); + } + + public String decodeString() throws IOException { + //in.readShort(); // HACK + //return in.readUTF(); + int len = decodePacked32() & 0xffffffff; + byte[] chars = new byte[len]; + for(int i=0; i<len; i++) { + chars[i] = in.readByte(); + } + return new String(chars); + } + + public int decodePacked32() throws IOException { + long res=0; + byte i=0; + boolean cont=true; + + do { + byte c = in.readByte(); + res = (res << 7) | (c & 0x7f); + cont = (c & 0x80) != 0; + i++; + } while(cont); + + return (int) (res & 0xffffffff); + } +} + diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java b/lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java new file mode 100644 index 0000000..a94bc6e --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommDecoderRegistry.java @@ -0,0 +1,144 @@ +package se.lth.control.labcomm2006; + +import java.io.IOException; +import java.util.HashMap; + +public class LabCommDecoderRegistry { + + public static class Entry { + + private LabCommDispatcher dispatcher; + private LabCommHandler handler; + private int index; + private String name; + private byte[] signature; + + public Entry(LabCommDispatcher dispatcher, + LabCommHandler handler) { + this.dispatcher = dispatcher; + this.name = dispatcher.getName(); + this.signature = dispatcher.getSignature(); + this.handler = handler; + } + + public Entry(int index, String name, byte[] signature) { + this.index = index; + this.name = name; + this.signature = signature; + } + + public LabCommDispatcher getDispatcher() { + return dispatcher; + } + + public void setDispatcher(LabCommDispatcher dispatcher) { + this.dispatcher = dispatcher; + } + + public LabCommHandler getHandler() { + return handler; + } + + public void setHandler(LabCommHandler handler) { + this.handler = handler; + } + + public String getName() { + return name; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) throws IOException { + if (this.index != 0 && this.index != index) { + throw new IOException("Index mismatch " + + this.index + " != " + index); + } + this.index = index; + } + + public boolean matchName(String name) { + return this.name.equals(name); + } + + public boolean matchSignature(byte[] signature) { + boolean result = this.signature.length == signature.length; + for (int i = 0 ; result && i < signature.length ; i++) { + result = this.signature[i] == signature[i]; + } + return result; + } + + public boolean match(String name, byte[] signature) { + return matchName(name) && matchSignature(signature); + } + + public void check(String name, byte[] signature) throws IOException { + if (!matchName(name)) { + throw new IOException("Name mismatch '" + + this.name + "' != '" + name + "'"); + } + if (!matchSignature(signature)) { + throw new IOException("Signature mismatch"); + } + } + } + + private HashMap<Class, Entry> byClass; + private HashMap<Integer, Entry> byIndex; + + public LabCommDecoderRegistry() { + byClass = new HashMap<Class, Entry>(); + byIndex = new HashMap<Integer, Entry>(); + } + + public synchronized void add(LabCommDispatcher dispatcher, + LabCommHandler handler) throws IOException{ + Entry e = byClass.get(dispatcher.getSampleClass()); + if (e != null) { + e.check(dispatcher.getName(), dispatcher.getSignature()); + e.setHandler(handler); + } else { + for (Entry e2 : byIndex.values()) { + if (e2.match(dispatcher.getName(), dispatcher.getSignature())) { + e2.setDispatcher(dispatcher); + e2.setHandler(handler); + e = e2; + break; + } + } + if (e == null) { + e = new Entry(dispatcher, handler); + byClass.put(dispatcher.getSampleClass(), e); + } + } + } + + public synchronized void add(int index, + String name, + byte[] signature) throws IOException { + Entry e = byIndex.get(Integer.valueOf(index)); + if (e != null) { + e.check(name, signature); + } else { + for (Entry e2 : byClass.values()) { + if (e2.match(name, signature)) { + e2.setIndex(index); + e = e2; + break; + } + } + if (e == null) { + e = new Entry(index, name, signature); + } + byIndex.put(Integer.valueOf(index), e); + } + } + + public synchronized Entry get(int index) { + return byIndex.get(Integer.valueOf(index)); + } + +} \ No newline at end of file diff --git a/lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java b/lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java new file mode 100644 index 0000000..469627e --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommDispatcher.java @@ -0,0 +1,15 @@ +package se.lth.control.labcomm2006; + +public interface LabCommDispatcher { + + public Class getSampleClass(); + + public String getName(); + + public byte[] getSignature(); + + public void decodeAndHandle(LabCommDecoder decoder, + LabCommHandler handler) throws Exception; + +} + diff --git a/lib/java/se/lth/control/labcomm2006/LabCommEncoder.java b/lib/java/se/lth/control/labcomm2006/LabCommEncoder.java new file mode 100644 index 0000000..51c2223 --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommEncoder.java @@ -0,0 +1,20 @@ +package se.lth.control.labcomm2006; + +import java.io.IOException; + +public interface LabCommEncoder { + + public void register(LabCommDispatcher dispatcher) throws IOException; + public void begin(Class<? extends LabCommSample> c) throws IOException; + public void end(Class<? extends LabCommSample> c) throws IOException; + public void encodeBoolean(boolean value) throws IOException; + public void encodeByte(byte value) throws IOException; + public void encodeShort(short value) throws IOException; + public void encodeInt(int value) throws IOException; + public void encodeLong(long value) throws IOException; + public void encodeFloat(float value) throws IOException; + public void encodeDouble(double value) throws IOException; + public void encodeString(String value) throws IOException; + public void encodePacked32(long value) throws IOException; + +} diff --git a/lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java b/lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java new file mode 100644 index 0000000..c38c852 --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommEncoderChannel.java @@ -0,0 +1,117 @@ +package se.lth.control.labcomm2006; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class LabCommEncoderChannel implements LabCommEncoder { + + private LabCommWriter writer; + private ByteArrayOutputStream bytes; + private DataOutputStream data; + private LabCommEncoderRegistry registry; + + public LabCommEncoderChannel(LabCommWriter writer, + boolean emitVersion) throws IOException { + this.writer = writer; + bytes = new ByteArrayOutputStream(); + data = new DataOutputStream(bytes); + registry = new LabCommEncoderRegistry(); + if (emitVersion) { + throw new RuntimeError("Labcomm 2006 does not support emitVersion"); + } + } + + public LabCommEncoderChannel(LabCommWriter writer) throws IOException { + this(writer, true); + } + + public LabCommEncoderChannel(OutputStream writer, + boolean emitVersion) throws IOException { + this(new WriterWrapper(writer), emitVersion); + } + + public LabCommEncoderChannel(OutputStream writer) throws IOException { + this(new WriterWrapper(writer), true); + } + + public void register(LabCommDispatcher dispatcher) throws IOException { + int index = registry.add(dispatcher); + encodePacked32(LabComm.SAMPLE); + encodePacked32(index); + encodeString(dispatcher.getName()); + byte[] signature = dispatcher.getSignature(); + for (int i = 0 ; i < signature.length ; i++) { + encodeByte(signature[i]); + } + end(null); + } + + public void begin(Class<? extends LabCommSample> c) throws IOException { + encodePacked32(registry.getTag(c)); + } + + public void end(Class<? extends LabCommSample> c) throws IOException { + data.flush(); + //XXX when writer was a stream, it was probably a bit more GC efficient: + //bytes.writeTo(writer); + writer.write(bytes.toByteArray()); + bytes.reset(); + } + + public void encodeBoolean(boolean value) throws IOException{ + data.writeBoolean(value); + } + + public void encodeByte(byte value) throws IOException { + data.writeByte(value); + } + + public void encodeShort(short value) throws IOException { + data.writeShort(value); + } + + public void encodeInt(int value) throws IOException { + data.writeInt(value); + } + + public void encodeLong(long value) throws IOException { + data.writeLong(value); + } + + public void encodeFloat(float value) throws IOException { + data.writeFloat(value); + } + + public void encodeDouble(double value) throws IOException { + data.writeDouble(value); + } + + public void encodeString(String value) throws IOException { + data.writeShort(0); // HACK... + data.writeUTF(value); + + //kludge, to replace above hack with packed length + ByteArrayOutputStream tmpb = new ByteArrayOutputStream(); + DataOutputStream tmps = new DataOutputStream(tmpb); + + tmps.writeUTF(value); + tmps.flush(); + byte[] tmp = tmpb.toByteArray(); + + encodePacked32(tmp.length-2); + for (int i = 2 ; i < tmp.length ; i++) { + encodeByte(tmp[i]); + } + } + + /** + method for API harmonization with labcomm2013. + Labcomm2006 encodes lengths etc as 32 bit ints. + */ + public inline void encodePacked32(long value) throws IOException { + encodeInt(value); + } +} + diff --git a/lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java b/lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java new file mode 100644 index 0000000..2a5564e --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommEncoderRegistry.java @@ -0,0 +1,55 @@ +package se.lth.control.labcomm2006; + +import java.io.IOException; +import java.util.HashMap; + +public class LabCommEncoderRegistry { + + public static class Entry { + + private LabCommDispatcher dispatcher; + private int index; + + public Entry(LabCommDispatcher dispatcher, int index) { + this.dispatcher = dispatcher; + this.index = index; + } + + public LabCommDispatcher getDispatcher() { + return dispatcher; + } + + public int getIndex() { + return index; + } + + } + + private int userIndex = LabComm.FIRST_USER_INDEX; + private HashMap<Class, Entry> byClass; + + public LabCommEncoderRegistry() { + byClass = new HashMap<Class, Entry>(); + } + + public synchronized int add(LabCommDispatcher dispatcher) { + Entry e = byClass.get(dispatcher.getSampleClass()); + if (e == null) { + e = new Entry(dispatcher, userIndex); + byClass.put(dispatcher.getSampleClass(), e); + userIndex++; + } + return e.getIndex(); + } + + public int getTag(Class<? extends LabCommSample> sample) throws IOException { + Entry e = byClass.get(sample); + if (e == null) { + throw new IOException("'" + + sample.getSimpleName() + + "' is not registered"); + } + return e.index; + } + +} \ No newline at end of file diff --git a/lib/java/se/lth/control/labcomm2006/LabCommHandler.java b/lib/java/se/lth/control/labcomm2006/LabCommHandler.java new file mode 100644 index 0000000..2880c19 --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommHandler.java @@ -0,0 +1,4 @@ +package se.lth.control.labcomm2006; + +public interface LabCommHandler { +} \ No newline at end of file diff --git a/lib/java/se/lth/control/labcomm2006/LabCommReader.java b/lib/java/se/lth/control/labcomm2006/LabCommReader.java new file mode 100644 index 0000000..1c2561d --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommReader.java @@ -0,0 +1,7 @@ +package se.lth.control.labcomm2006; + +public interface LabCommReader { + + public void handle(byte[] data, int begin, int end); + +} diff --git a/lib/java/se/lth/control/labcomm2006/LabCommSample.java b/lib/java/se/lth/control/labcomm2006/LabCommSample.java new file mode 100644 index 0000000..d1dc1ff --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommSample.java @@ -0,0 +1,5 @@ +package se.lth.control.labcomm2006; + +public interface LabCommSample { + +} \ No newline at end of file diff --git a/lib/java/se/lth/control/labcomm2006/LabCommType.java b/lib/java/se/lth/control/labcomm2006/LabCommType.java new file mode 100644 index 0000000..0722e6d --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommType.java @@ -0,0 +1,5 @@ +package se.lth.control.labcomm2006; + +public interface LabCommType { + +} \ No newline at end of file diff --git a/lib/java/se/lth/control/labcomm2006/LabCommWriter.java b/lib/java/se/lth/control/labcomm2006/LabCommWriter.java new file mode 100644 index 0000000..e70e142 --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/LabCommWriter.java @@ -0,0 +1,9 @@ +package se.lth.control.labcomm2006; + +import java.io.IOException; + +public interface LabCommWriter { + + public void write(byte[] data) throws IOException; + +} diff --git a/lib/java/se/lth/control/labcomm2006/WriterWrapper.java b/lib/java/se/lth/control/labcomm2006/WriterWrapper.java new file mode 100644 index 0000000..4c96d88 --- /dev/null +++ b/lib/java/se/lth/control/labcomm2006/WriterWrapper.java @@ -0,0 +1,17 @@ +package se.lth.control.labcomm2006; + +import java.io.OutputStream; +import java.io.IOException; + +class WriterWrapper implements LabCommWriter{ + + private OutputStream os; + + public WriterWrapper(OutputStream os) { + this.os = os; + } + + public void write(byte[] data) throws IOException { + os.write(data); + } +} -- GitLab