diff --git a/BioD/contrib/msgpack-d/.gitignore b/BioD/contrib/msgpack-d/.gitignore
new file mode 100644
index 0000000..14f1428
--- /dev/null
+++ b/BioD/contrib/msgpack-d/.gitignore
@@ -0,0 +1,12 @@
+dub.selections.json
+.dub
+tests/*/tests
+__test__*__
+*.obj
+*.[oa]
+*.so
+*.lib
+*.dll
+*.sublime-project
+*.sublime-workspace
+msgpack-d-test-unittest
diff --git a/BioD/contrib/msgpack-d/LICENSE_1_0.txt b/BioD/contrib/msgpack-d/LICENSE_1_0.txt
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/BioD/contrib/msgpack-d/LICENSE_1_0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/BioD/contrib/msgpack-d/README.markdown b/BioD/contrib/msgpack-d/README.markdown
new file mode 100644
index 0000000..e4c43e5
--- /dev/null
+++ b/BioD/contrib/msgpack-d/README.markdown
@@ -0,0 +1,199 @@
+[![CI](https://github.com/msgpack/msgpack-d/actions/workflows/d.yml/badge.svg)](https://github.com/msgpack/msgpack-d/actions/workflows/d.yml)
+
+# MessagePack for D
+
+MessagePack is a binary-based JSON-like serialization library.
+
+MessagePack for D is a pure D implementation of MessagePack.
+
+# Features
+
+* Small size and High performance
+* Zero copy serialization / deserialization
+* Streaming deserializer for non-contiguous IO situation
+* Supports D features (Ranges, Tuples, real type)
+
+Note: The `real` type is only supported in D.
+Don't use the `real` type when communicating with other programming languages.
+Note that `Unpacker` will raise an exception if a loss of precision occurs.
+
+## Current Limitations
+
+* No circular references support
+* If you want to use the LDC compiler, you need at least version 0.15.2 beta2
+
+# Install
+
+Use dub to add it as a dependency:
+
+```sh
+% dub install msgpack-d
+```
+
+# Usage
+
+Example code can be found in the `example` directory.
+
+The documentation can be found [here](http://msgpack.github.io/msgpack-d/)
+
+## pack / unpack
+
+msgpack-d is very simple to use. Use `pack` for serialization, and `unpack` for deserialization:
+
+```D
+import std.file;
+import msgpack;
+
+struct S { int x; float y; string z; }
+
+void main()
+{
+ S input = S(10, 25.5, "message");
+
+ // serialize data
+ ubyte[] inData = pack(input);
+
+ // write data to a file
+ write("file.dat", inData);
+
+ // read data from a file
+ ubyte[] outData = cast(ubyte[])read("file.dat");
+
+ // unserialize the data
+ S target = outData.unpack!S();
+
+ // verify data is the same
+ assert(target.x == input.x);
+ assert(target.y == input.y);
+ assert(target.z == input.z);
+}
+```
+
+### Feature: Skip serialization/deserialization of a specific field.
+
+Use the `@nonPacked` attribute:
+
+```d
+struct User
+{
+ string name;
+ @nonPacked int level; // pack / unpack will ignore the 'level' field
+}
+```
+
+### Feature: Use your own serialization/deserialization routines for custom class and struct types.
+
+msgpack-d provides the functions `registerPackHandler` / `registerUnpackHandler` to allow you
+to use custom routines during the serialization or deserialization of user-defined class and struct types.
+This feature is especially useful when serializing a derived class object when that object is statically
+typed as a base class object.
+
+For example:
+
+```d
+class Document { }
+class XmlDocument : Document
+{
+ this() { }
+ this(string name) { this.name = name; }
+ string name;
+}
+
+void xmlPackHandler(ref Packer p, ref XmlDocument xml)
+{
+ p.pack(xml.name);
+}
+
+void xmlUnpackHandler(ref Unpacker u, ref XmlDocument xml)
+{
+ u.unpack(xml.name);
+}
+
+void main()
+{
+ /// Register the 'xmlPackHandler' and 'xmlUnpackHandler' routines for
+ /// XmlDocument object instances.
+ registerPackHandler!(XmlDocument, xmlPackHandler);
+ registerUnpackHandler!(XmlDocument, xmlUnpackHandler);
+
+ /// Now we can serialize/deserialize XmlDocument object instances via a
+ /// base class reference.
+ Document doc = new XmlDocument("test.xml");
+ auto data = pack(doc);
+ XmlDocument xml = unpack!XmlDocument(data);
+ assert(xml.name == "test.xml"); // xml.name is "test.xml"
+}
+```
+
+In addition, here is also a method using `@serializedAs` attribute:
+
+```d
+import std.datetime: Clock, SysTime;
+static struct SysTimePackProxy
+{
+ static void serialize(ref Packer p, ref in SysTime tim)
+ {
+ p.pack(tim.toISOExtString());
+ }
+
+ static void deserialize(ref Unpacker u, ref SysTime tim)
+ {
+ string tmp;
+ u.unpack(tmp);
+ tim = SysTime.fromISOExtString(tmp);
+ }
+}
+static struct LogData
+{
+ string msg;
+ string file;
+ ulong line;
+ @serializedAs!SysTimePackProxy SysTime timestamp;
+
+ this(string message, string file = __FILE__, ulong line = __LINE__)
+ {
+ this.msg = message;
+ this.file = file;
+ this.line = line;
+ this.timestamp = Clock.currTime();
+ }
+}
+
+void main()
+{
+ /// Now we can serialize/deserialize LogData
+ LogData[] logs;
+ logs ~= LogData("MessagePack is nice!");
+ auto data = pack(logs);
+ LogData[] datas = unpack!(LogData[])(data);
+ assert(datas[0].timestamp.toString() == datas[0].timestamp.toString());
+}
+```
+
+## The PackerImpl / Unpacker / StreamingUnpacker types
+
+These types are used by the `pack` and `unpack` functions.
+
+See the documentation of [PackerImpl](http://msgpack.github.io/msgpack-d/#PackerImpl), [Unpacker](http://msgpack.github.io/msgpack-d/#Unpacker) and [StreamingUnpacker](http://msgpack.github.io/msgpack-d/#StreamingUnpacker) for more details.
+
+# Links
+
+* [The MessagePack Project](http://msgpack.org/)
+
+ The official MessagePack protocol website.
+
+* [msgpack-d's issue tracker](https://github.com/msgpack/msgpack-d/issues)
+
+ Use this issue tracker to review and file bugs in msgpack-d.
+
+* [MessagePack's Github](http://github.com/msgpack/)
+
+ Other language bindings and implementations of the msgpack protocol can be found here.
+
+# Copyright
+
+ Copyright (c) 2010- Masahiro Nakagawa
+
+# License
+
+Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/users/license.html).
diff --git a/BioD/contrib/msgpack-d/dub.json b/BioD/contrib/msgpack-d/dub.json
new file mode 100644
index 0000000..01c85d0
--- /dev/null
+++ b/BioD/contrib/msgpack-d/dub.json
@@ -0,0 +1,19 @@
+{
+ "name": "msgpack-d",
+ "description": "MessagePack for D.",
+ "authors": ["Masahiro Nakagawa"],
+ "homepage": "https://github.com/msgpack/msgpack-d",
+ "license": "Boost Software License, Version 1.0",
+ "copyright": "Copyright (c) 2010- Masahiro Nakagawa",
+
+ "configurations": [
+ {
+ "name": "default",
+ "targetType": "library"
+ },
+ {
+ "name": "unittest",
+ "dflags": ["-dip25", "-dip1000"]
+ }
+ ]
+}
diff --git a/BioD/contrib/msgpack-d/example/array_test.d b/BioD/contrib/msgpack-d/example/array_test.d
new file mode 100644
index 0000000..b2d19ac
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/array_test.d
@@ -0,0 +1,39 @@
+import std.conv;
+import std.stdio;
+import std.datetime;
+import msgpack;
+
+struct A {
+ int x;
+}
+
+struct Foo
+{
+ A[] a;
+}
+
+void main()
+{
+ Foo foo;
+ foreach (a; 'a' .. 'z')
+ foreach (b; 'a' .. 'z')
+ foreach (c; 'a' .. 'z')
+ foo.a ~= A();
+
+ auto sw = StopWatch(AutoStart.yes);
+ ubyte[] data = msgpack.pack(foo);
+ writeln(sw.peek.usecs);
+
+ auto sw2 = StopWatch(AutoStart.yes);
+ Foo foo1 = msgpack.unpack(data).as!(typeof(foo));
+ writeln(sw2.peek.usecs);
+
+ assert(foo == foo1);
+
+ Foo foo2;
+ auto sw3 = StopWatch(AutoStart.yes);
+ msgpack.unpack(data, foo2);
+ writeln(sw3.peek.usecs);
+
+ assert(foo == foo2);
+}
diff --git a/BioD/contrib/msgpack-d/example/attribute.d b/BioD/contrib/msgpack-d/example/attribute.d
new file mode 100644
index 0000000..b5179aa
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/attribute.d
@@ -0,0 +1,24 @@
+// Written in the D programming language.
+
+/**
+ * Attribute usage
+ */
+
+import msgpack;
+
+struct Hoge
+{
+ string f1;
+ @nonPacked int f2;
+}
+
+void main()
+{
+ Hoge hoge = Hoge("hoge", 10);
+ Hoge fuga;
+
+ unpack(pack(hoge), fuga);
+ assert(hoge.f1 == fuga.f1);
+ assert(hoge.f2 != fuga.f2);
+ assert(fuga.f2 == int.init);
+}
diff --git a/BioD/contrib/msgpack-d/example/compare_direct_stream.d b/BioD/contrib/msgpack-d/example/compare_direct_stream.d
new file mode 100644
index 0000000..1718943
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/compare_direct_stream.d
@@ -0,0 +1,55 @@
+// Written in the D programming language.
+
+/**
+ * Compares direct conversion with stream.
+ */
+
+import std.datetime;
+import std.stdio;
+import std.typecons;
+
+import msgpack;
+
+
+void main()
+{
+ // tuple
+ auto test1 = tuple(new int[](100), "MessagePack!", [1:2.0, 3:4.0, 5:6.0, 7:8.0]);
+ auto data1 = pack(test1);
+
+ // stream
+ void s1()
+ {
+ auto result = unpack(data1).as!(typeof(test1));
+ }
+
+ // direct conversion
+ void d1()
+ {
+ typeof(test1) result;
+ unpack(data1, result);
+ }
+
+ // array
+ auto test2 = new int[](1000);
+ auto data2 = pack(test2);
+
+ // stream
+ void s2()
+ {
+ auto result = unpack(data2).as!(typeof(test2));
+ }
+
+ // direct conversion
+ void d2()
+ {
+ typeof(test2) result;
+ unpack(data2, result);
+ }
+
+ auto times = benchmark!(s1, d1, s2, d2)(1000);
+ writeln("Stream(Tuple):", times[0].msecs);
+ writeln("Direct(Tuple):", times[1].msecs);
+ writeln("Stream(Array):", times[2].msecs);
+ writeln("Direct(Array):", times[3].msecs);
+}
diff --git a/BioD/contrib/msgpack-d/example/compare_json.d b/BioD/contrib/msgpack-d/example/compare_json.d
new file mode 100644
index 0000000..a02d704
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/compare_json.d
@@ -0,0 +1,33 @@
+// Written in the D programming language.
+
+/**
+ * Compares std.json
+ */
+
+import std.datetime;
+import std.json;
+import std.stdio;
+
+import msgpack;
+
+
+void main()
+{
+ JSONValue jsonObj = parseJSON(`[12, "foo", true, 0.23, {"1":1}, [1, 2]]`);
+
+ void f1()
+ {
+ parseJSON(toJSON(jsonObj));
+ }
+
+ Value mpObj = unpack(pack(12, "foo", true, 0.23, ["1":1], [1, 2]));
+
+ void f2()
+ {
+ unpack(pack(mpObj));
+ }
+
+ auto times = benchmark!(f1, f2)(10000);
+ writeln("JSON: ", times[0].msecs);
+ writeln("Msgpack: ", times[1].msecs);
+}
diff --git a/BioD/contrib/msgpack-d/example/convert_json.d b/BioD/contrib/msgpack-d/example/convert_json.d
new file mode 100644
index 0000000..77ec3fa
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/convert_json.d
@@ -0,0 +1,38 @@
+// Written in the D programming language.
+
+/**
+ * Converting to/from JSON usage
+ */
+
+import std.stdio;
+
+import msgpack;
+import std.json;
+
+
+void main()
+{
+ struct Simple
+ {
+ int a = 5;
+ string b = "hello";
+ double c = 3.14;
+ char d = '!';
+ @nonPacked string e = "world";
+ bool f = true;
+ }
+ auto simple = Simple();
+ Value val = simple.pack().unpack();
+ writeln(val.toJSONValue());
+ val = simple.pack!true().unpack();
+ writeln(val.toJSONValue());
+
+ string jsonString = `[30, 30.5, true, "hello", "40"]`;
+ val = parseJSON(jsonString).fromJSONValue();
+ assert(val.pack() !is null);
+ assert(val.type == Value.Type.array);
+ foreach (v; val.via.array)
+ {
+ writeln(v.type);
+ }
+}
diff --git a/BioD/contrib/msgpack-d/example/custom.d b/BioD/contrib/msgpack-d/example/custom.d
new file mode 100644
index 0000000..f24a9fc
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/custom.d
@@ -0,0 +1,514 @@
+// Written in the D programming language.
+
+/**
+ * User-defined class sample
+ */
+
+import std.stdio, std.math;
+
+import msgpack;
+
+
+enum Attr : ubyte
+{
+ A, B
+}
+
+
+struct User
+{
+ string name;
+ uint age;
+ Attr attr;
+}
+
+
+void main()
+{
+ User user = User("Foo", 20, Attr.B), other;
+
+ unpack(pack(user), other);
+
+ writeln("name: ", other.name, "(", other.age, ", ", other.attr, ")");
+
+ // Complex data-structure
+
+ auto btree = new BTree!(int, string);
+ int[] keys = [3, 6, 8, 10, 1, 5, 20];
+ string[] values = ["Foo", "Baz", "Bar", "Hoge", "Fuga", "Piyo", "ham"];
+
+ foreach (i, key; keys)
+ btree.insert(key, values[i]);
+ btree.print();
+
+ auto result = new BTree!(int, string);
+
+ unpack(pack(btree), result);
+
+ result.print();
+}
+
+
+/**
+ * Simple B-Tree.
+ */
+class BTree(Key, Data)
+{
+ private:
+ immutable uint MAX_CHILD; // sub-tree size(m-tree)
+ immutable uint HALF_CHILD; // min number((m + 1) / 2)
+
+ enum NodeType
+ {
+ Internal,
+ Leaf
+ }
+
+ static class Node
+ {
+ NodeType type;
+
+ union
+ {
+ struct // internal
+ {
+ uint childs; // child number
+ Key[] low; // min element of each sub-tree
+ Node[] child; // sub-tree size(m)
+ }
+
+ struct // leaf
+ {
+ Key key;
+ Data data;
+ }
+ }
+
+ this(in uint num) { low.length = child.length = num; }
+
+ void toMsgpack(Packer)(ref Packer packer) const
+ {
+ if (type == NodeType.Internal)
+ packer.packArray(childs, low.length, low, child);
+ else
+ packer.packArray(key, data);
+ }
+
+ void fromMsgpack(ref Unpacker unpacker)
+ {
+ if (unpacker.beginArray() == 4) {
+ uint max;
+
+ unpacker.unpack(childs, max, low);
+ child.length = unpacker.beginArray();
+ foreach (i; 0..child.length)
+ unpacker.unpack(child[i], max);
+ low.length = child.length = max;
+ } else {
+ type = NodeType.Leaf;
+ unpacker.unpack(key, data);
+ }
+ }
+ }
+
+ Node root;
+
+
+ public:
+ /**
+ * Params:
+ * num = node size.
+ */
+ this(in uint num = 5)
+ in
+ {
+ assert(num >= 2, "The prerequisite of m-tree(size must be larger than 2)");
+ }
+ body
+ {
+ MAX_CHILD = num;
+ HALF_CHILD = cast(uint)ceil(cast(real)MAX_CHILD / 2);
+ }
+
+ /**
+ * Params:
+ * key = key that represents a data to insert.
+ * data = data to insert.
+ *
+ * Returns:
+ * node that inserted data.
+ */
+ Node insert(in Key key, in Data data)
+ {
+ /*
+ * Params:
+ * n = node to insert element
+ * nNode = new node(null if not create).
+ * lowest = min element in $(D_PARAM nNode)
+ *
+ * Returns:
+ * node that element was inserted.
+ */
+ Node _insert(ref Node n, out Node nNode, out Key lowest)
+ {
+ Node node = n; // for updating data
+
+ if (node.type == NodeType.Leaf) {
+ if (node.key == key) {
+ return null;
+ } else {
+ Node elem = allocNode();
+ elem.type = NodeType.Leaf;
+ elem.key = key;
+ elem.data = data;
+
+ if (elem.key < node.key) {
+ n = elem;
+ nNode = node;
+ lowest = node.key;
+ } else {
+ nNode = elem;
+ lowest = elem.key;
+ }
+
+ return elem;
+ }
+ } else {
+ int i, j, pos; // pos = position to insert.
+ Key xLowest; // lowest for recursion.
+ Node xNode, ret; // nNode for recursion.
+
+ pos = locateSubTree(node, key);
+ ret = _insert(node.child[pos], xNode, xLowest);
+
+ // Doesn't create and patition.
+ if (xNode is null)
+ return ret;
+
+ if (node.childs < MAX_CHILD) {
+ for (i = node.childs - 1; i > pos; i--) {
+ node.child[i+1] = node.child[i];
+ node.low[i+1] = node.low[i];
+ }
+ node.child[pos+1] = xNode;
+ node.low[pos+1] = xLowest;
+ node.childs++;
+ return ret;
+ } else {
+ Node elem = allocNode();
+ elem.type = NodeType.Internal;
+
+ // insert to node side or elem side?
+ if (pos < HALF_CHILD - 1) {
+ for (i = HALF_CHILD - 1, j = 0; i < MAX_CHILD; i++, j++) {
+ elem.child[j] = node.child[i];
+ elem.low[j] = node.low[i];
+ }
+
+ for (i = HALF_CHILD - 2; i > pos; i--) {
+ node.child[i+1] = node.child[i];
+ node.low[i+1] = node.low[i];
+ }
+ node.child[pos+1] = xNode;
+ node.low[pos+1] = xLowest;
+ } else {
+ for (i = MAX_CHILD - 1, j = MAX_CHILD - HALF_CHILD; i >= HALF_CHILD; i--) {
+ if (i == pos) {
+ elem.child[j] = xNode;
+ elem.low[j--] = xLowest;
+ }
+ elem.child[j] = node.child[i];
+ elem.low[j--] = node.low[i];
+ }
+
+ if (pos < HALF_CHILD) {
+ elem.child[0] = xNode;
+ elem.low[0] = xLowest;
+ }
+ }
+
+ node.childs = HALF_CHILD;
+ elem.childs = MAX_CHILD+1 - HALF_CHILD;
+
+ nNode = elem;
+ lowest = elem.low[0];
+
+ return ret;
+ }
+ }
+ }
+
+ if (root is null) {
+ root = allocNode();
+ root.type = NodeType.Leaf;
+ root.key = key;
+ root.data = data;
+ return root;
+ } else {
+ Key lowest;
+ Node ret, newNode;
+
+ ret = _insert(root, newNode, lowest);
+
+ // new node and growl height if patitioned.
+ if (newNode !is null) {
+ Node elem = allocNode();
+ elem.type = NodeType.Internal;
+ elem.childs = 2;
+ elem.child[0] = root;
+ elem.child[1] = newNode;
+ elem.low[1] = lowest;
+ root = elem;
+ }
+
+ return ret;
+ }
+ }
+
+ /**
+ * Params:
+ * key = key to delete.
+ *
+ * Returns:
+ * true if deleted.
+ */
+ bool remove(in Key key)
+ {
+ enum State
+ {
+ Nothing,
+ Removed,
+ Reconfig
+ }
+
+ /*
+ * Params:
+ * n = node to remove.
+ * result = node change because of an remove.
+ *
+ * Returns:
+ * true if removed.
+ */
+ bool _remove(ref Node n, out State result)
+ {
+ if (n.type == NodeType.Leaf) {
+ if (n.key == key) {
+ result = State.Removed;
+ delete n;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ int pos, sub; // sub-tree position to remove, for restructure.
+ bool ret, merged; // delete?, merge sub-tree?
+ State state; // sub-tree state.
+
+ pos = locateSubTree(n, key);
+ ret = _remove(n.child[pos], state);
+
+ if (state == State.Nothing)
+ return ret;
+
+ if (state == State.Reconfig) {
+ sub = pos == 0 ? 0 : pos - 1;
+ merged = revisionNodes(n, sub);
+
+ if (merged)
+ pos = sub+1;
+ }
+
+ if (state == State.Removed || merged) {
+ // sub-tree compaction
+ for (int i = pos; i < n.childs - 1; i++) {
+ n.child[i] = n.child[i+1];
+ n.low[i] = n.low[i+1];
+ }
+
+ if (--n.childs < HALF_CHILD)
+ result = State.Reconfig;
+ }
+
+ return ret;
+ }
+ }
+
+ if (root is null) {
+ return false;
+ } else {
+ State result;
+ bool ret = _remove(root, result);
+
+ if (result == State.Removed) {
+ root = null;
+ } else if (result == State.Reconfig && root.childs == 1) {
+ Node n = root;
+ root = root.child[0];
+ delete n;
+ }
+
+ return ret;
+ }
+ }
+
+ /**
+ * Params:
+ * key = key to search.
+ *
+ * Returns:
+ * finded node.
+ */
+ Node search(in Key key)
+ {
+ if (root is null) {
+ return null;
+ } else {
+ int i;
+ Node n = root;
+
+ // searches internal node until find leaf.
+ while (n.type == NodeType.Internal) {
+ i = locateSubTree(n, key);
+ n = n.child[i];
+ }
+
+ if (key == n.key)
+ return n;
+ else
+ return null;
+ }
+ }
+
+ void print()
+ {
+ void _print(ref Node n)
+ {
+ if (n.type == NodeType.Leaf) {
+ writefln("[%x] Leaf : %s Data : %s", &n, n.key, n.data);
+ } else {
+ writef("[%x] Childs %d [%x], ", &n, n.childs, &n.child[0]);
+
+ foreach (i; 0..MAX_CHILD)
+ writef("%d[%x] ", n.low[i], &n.child[i]);
+ writeln();
+
+ foreach (i; 0..n.childs)
+ _print(n.child[i]);
+ }
+ }
+
+ if (root is null)
+ writefln("Element is nothing");
+ else
+ _print(root);
+ }
+
+ // for MessagePack
+
+ void toMsgpack(Packer)(ref Packer packer) const
+ {
+ packer.pack(root);
+ }
+
+ void fromMsgpack(ref Unpacker unpacker)
+ {
+ unpacker.unpack(root, MAX_CHILD);
+ }
+
+
+ private:
+ /*
+ * Returns:
+ * new node.
+ */
+ Node allocNode()
+ {
+ return new Node(MAX_CHILD);
+ }
+
+ /*
+ * searches $(D_PARAM key) element in sub-tree of $(D_PARAM node).
+ *
+ * Params:
+ * node = node to search.
+ * key = key to search in $(D_PARAM node).
+ *
+ * Returns:
+ * finded position.
+ */
+ int locateSubTree(ref Node node, in Key key) const
+ {
+ for (int i = node.childs - 1; i > 0; i--)
+ if (key >= node.low[i])
+ return i;
+ return 0;
+ }
+
+ /*
+ * Params:
+ * n = revision node.
+ * x = position to sub-tree of revision node.
+ *
+ * Returns:
+ * true if merged.
+ */
+ bool revisionNodes(ref Node n, in uint x)
+ {
+ int i;
+ Node a, b; // each sub-tree.
+ uint an, bn; // child number of each sub-tree.
+
+ a = n.child[x];
+ b = n.child[x+1];
+ an = a.childs;
+ bn = b.childs;
+ b.low[0] = n.low[x+1];
+
+ if (an + bn <= MAX_CHILD) { // merge
+ for (i = 0; i < bn; i++) {
+ a.child[an+i] = b.child[i];
+ a.low[an+i] = b.low[i];
+ }
+
+ a.childs += bn;
+ delete b;
+
+ return true;
+ } else { // partition
+ uint pivot = (an + bn) / 2; // pivot to patition.
+ uint move; // element number to copy.
+
+ if (an > pivot) {
+ move = an - pivot;
+
+ for (i = bn - 1; i >= 0; i--) {
+ b.child[move+i] = b.child[i];
+ b.low[move+i] = b.low[i];
+ }
+ // copy element a to b.
+ for (i = 0; i < move; i++) {
+ b.child[i] = a.child[pivot+i];
+ b.low[i] = a.low[pivot+i];
+ }
+ } else {
+ move = pivot - an;
+
+ // copy element b to a.
+ for (i = 0; i < move; i++) {
+ a.child[an+i] = b.child[i];
+ a.low[an+i] = b.low[i];
+ }
+ for (i = 0; i < bn - move; i++) {
+ b.child[i] = b.child[move+i];
+ b.low[i] = b.low[move+i];
+ }
+ }
+
+ a.childs = pivot;
+ b.childs = an + bn - pivot;
+ n.low[x+1] = b.low[0];
+
+ return false;
+ }
+ }
+}
diff --git a/BioD/contrib/msgpack-d/example/custom_handler.d b/BioD/contrib/msgpack-d/example/custom_handler.d
new file mode 100644
index 0000000..e7d6337
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/custom_handler.d
@@ -0,0 +1,65 @@
+import msgpack;
+import std.array;
+import std.stdio;
+import std.variant;
+
+class A { }
+class C : A
+{
+ int num;
+ this(int n) { num = n; }
+}
+
+void cPackHandler(ref Packer p, ref C c)
+{
+ writeln("Pack C: ", c.num);
+ p.pack(c.num);
+}
+
+void cUnpackHandler(ref Unpacker u, ref C c)
+{
+ writeln("Unpack C: ", c.num);
+ u.unpack(c.num);
+}
+
+void vPackHandler(ref Packer p, ref Variant v)
+{
+ writeln("pack Variant: ", v);
+ p.pack(v.get!bool);
+}
+
+void vUnpackHandler(ref Unpacker u, ref Variant v)
+{
+ writeln("unpack Variant: ", v);
+ bool b;
+ u.unpack(b);
+ v = b;
+}
+
+void main()
+{
+ registerPackHandler!(C, cPackHandler);
+ registerUnpackHandler!(C, cUnpackHandler);
+ registerPackHandler!(Variant, vPackHandler);
+ registerUnpackHandler!(Variant, vUnpackHandler);
+
+ {
+ Packer p;
+ A c = new C(1000);
+ p.pack(c);
+
+ A c2 = new C(5);
+ unpack(p.stream.data, c2);
+ assert(1000 == (cast(C)c2).num);
+ }
+ {
+ Packer p;
+
+ Variant v = true;
+ p.pack(v);
+
+ Variant v2 = 10;
+ unpack(p.stream.data, v2);
+ assert(v2 == true);
+ }
+}
diff --git a/BioD/contrib/msgpack-d/example/map_test.d b/BioD/contrib/msgpack-d/example/map_test.d
new file mode 100644
index 0000000..6107cf6
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/map_test.d
@@ -0,0 +1,39 @@
+import std.conv;
+import std.stdio;
+import std.datetime;
+import msgpack;
+
+struct A {
+ int x;
+}
+
+struct Foo
+{
+ A[string] a;
+}
+
+void main()
+{
+ Foo foo;
+ foreach (a; 'a' .. 'z')
+ foreach (b; 'a' .. 'z')
+ foreach (c; 'a' .. 'z')
+ foo.a[to!string(a) ~ to!string(b) ~ to!string(c)] = A();
+
+ auto sw = StopWatch(AutoStart.yes);
+ ubyte[] data = msgpack.pack(foo);
+ writeln(sw.peek.usecs);
+
+ auto sw2 = StopWatch(AutoStart.yes);
+ Foo foo1 = msgpack.unpack(data).as!(typeof(foo));
+ writeln(sw2.peek.usecs);
+
+ assert(foo == foo1);
+
+ Foo foo2;
+ auto sw3 = StopWatch(AutoStart.yes);
+ msgpack.unpack(data, foo2);
+ writeln(sw3.peek.usecs);
+
+ assert(foo == foo2);
+}
diff --git a/BioD/contrib/msgpack-d/example/register.d b/BioD/contrib/msgpack-d/example/register.d
new file mode 100644
index 0000000..89149a9
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/register.d
@@ -0,0 +1,37 @@
+import msgpack;
+import std.stdio;
+
+class A
+{
+ string str = "foo";
+}
+
+class C : A
+{
+ int num;
+ this(int n) { num = n; }
+}
+
+void main()
+{
+ registerClass!(C);
+
+ {
+ Packer p;
+ A c = new C(1000);
+ p.pack(c);
+
+ A c2 = new C(5);
+ unpack(p.stream.data, c2);
+ assert(1000 == (cast(C)c2).num);
+ }
+ {
+ Packer p;
+ C c = new C(1000);
+ p.pack(c);
+
+ C c2 = new C(5);
+ unpack(p.stream.data, c2);
+ assert(1000 == (cast(C)c2).num);
+ }
+}
diff --git a/BioD/contrib/msgpack-d/example/simple.d b/BioD/contrib/msgpack-d/example/simple.d
new file mode 100644
index 0000000..464fdd8
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/simple.d
@@ -0,0 +1,27 @@
+// Written in the D programming language.
+
+/**
+ * Serializer and Stream Deserializer usage
+ */
+
+import std.array;
+import std.stdio;
+
+import msgpack;
+
+
+void main()
+{
+ auto packer = packer(appender!(ubyte[])());
+
+ packer.packArray(null, true, "Hi!", -1, [1, 2], '!');
+
+ auto unpacker = StreamingUnpacker(packer.stream.data);
+
+ if (unpacker.execute()) {
+ foreach (obj; unpacker.purge())
+ writeln(obj.type);
+ } else {
+ writeln("Serialized object is too large!");
+ }
+}
diff --git a/BioD/contrib/msgpack-d/example/stream.d b/BioD/contrib/msgpack-d/example/stream.d
new file mode 100644
index 0000000..bcd0568
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/stream.d
@@ -0,0 +1,57 @@
+// Written in the D programming language.
+
+/**
+ * Stream deserialization usage
+ */
+
+import std.array;
+import std.concurrency;
+import std.exception;
+import std.stdio;
+
+import msgpack;
+
+
+void deserializer()
+{
+ auto unpacker = StreamingUnpacker(cast(ubyte[])null);
+ bool endLoop;
+
+ while (true) {
+ receive((immutable(ubyte)[] data) { unpacker.feed(data); },
+ (bool end) { endLoop = end; });
+
+ if (endLoop)
+ break;
+
+ while (unpacker.execute()) {
+ auto unpacked = unpacker.purge();
+ writeln("Type: ", unpacked.type);
+ writeln("Value: ", unpacked.as!(string));
+ }
+
+ if (unpacker.size >= 100)
+ throw new Exception("Too large!");
+ }
+}
+
+
+void main()
+{
+ string message = "Hell";
+ foreach (i; 0..93) // Throws Exception if 94
+ message ~= 'o';
+
+ auto packed = pack(message);
+ auto data = packed.assumeUnique();
+ auto tid = spawn(&deserializer);
+
+ while (!data.empty) {
+ auto limit = data.length >= 10 ? 10 : data.length;
+
+ tid.send(data[0..limit]);
+ data = data[limit..$];
+ }
+
+ tid.send(true);
+}
diff --git a/BioD/contrib/msgpack-d/example/unpacker_foreach.d b/BioD/contrib/msgpack-d/example/unpacker_foreach.d
new file mode 100644
index 0000000..f89d032
--- /dev/null
+++ b/BioD/contrib/msgpack-d/example/unpacker_foreach.d
@@ -0,0 +1,43 @@
+// Written in the D programming language.
+
+/**
+ * Stream Deserializer with foreach.
+ */
+
+import std.stdio;
+
+import msgpack;
+
+
+void main()
+{
+ // create 3 MessagePack objects([1, 0.1L], true, "foobarbaz")
+ auto test1 = pack(1, 0.1L) ~ pack(true);
+ auto test2 = pack("foobarbaz");
+
+ // split data to deserialize test
+ test1 ~= test2[0..2];
+ test2 = test2[2..$];
+
+ auto unpacker = StreamingUnpacker(test1);
+
+ foreach (unpacked; unpacker) {
+ if (unpacked.type == Value.Type.array) {
+ foreach (obj; unpacked) {
+ switch (obj.type) {
+ case Value.Type.unsigned: writeln(obj.as!(uint)); break;
+ case Value.Type.floating: writeln(obj.as!(real)); break;
+ default:
+ throw new Exception("Unknown type");
+ }
+ }
+ } else {
+ writeln(unpacked.as!(bool));
+ }
+ }
+
+ unpacker.feed(test2);
+
+ foreach (unpacked; unpacker)
+ writeln(unpacked.as!(string));
+}
diff --git a/BioD/contrib/msgpack-d/html/candydoc/candy.ddoc b/BioD/contrib/msgpack-d/html/candydoc/candy.ddoc
new file mode 100644
index 0000000..a40ba4c
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/candy.ddoc
@@ -0,0 +1,52 @@
+D = $(B $0)
+
+DDOC =
+
+
+
+$(TITLE)
+
+
+
+
+
+
+
+
+$(ADD_MODULES)
+
+
+
+DDOC_DECL =
+
+$(DT $0)
+
+
+
+DDOC_PSYMBOL =
+$0
+
+
+
+DDOC_MEMBERS =
+
+$(DL $0)
+
+
+
+DDOC_PARAM_ID =
+$0 |
+
+
+DDOC_PARAM =$0
+ADD_MODULES =
+MODULE =explorer.packageExplorer.addModule("$0");
diff --git a/BioD/contrib/msgpack-d/html/candydoc/explorer.js b/BioD/contrib/msgpack-d/html/candydoc/explorer.js
new file mode 100644
index 0000000..61f3b53
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/explorer.js
@@ -0,0 +1,305 @@
+/* This file is a part of CanDyDOC fileset.
+ File is written by Victor Nakoryakov and placed into the public domain.
+
+ This file is javascript with classes that represents explorer window.
+ And things related to navigation. */
+
+var explorer = new Explorer();
+
+///////////////////////////////////////////////////////////////////////////////
+// Current symbol marker class constructor
+///////////////////////////////////////////////////////////////////////////////
+function Marker()
+{
+ this.top = document.createElement("div");
+ this.middle = document.createElement("div");
+ this.bottom = document.createElement("div");
+ this.container = document.createElement("div");
+
+ this.setTo = function(term)
+ {
+ // find definition related to `term`
+ var def = term.nextSibling;
+ while (def && def.nodeName != "DD")
+ def = def.nextSibling;
+
+ var defHeight = 0;
+ var childrenHeight = 0; // children of current declaration
+ if (def)
+ {
+ defHeight = def.offsetHeight;
+ var child = def.firstChild;
+
+ // traverse until DL tag, until children definition
+ while (child && child.nodeName != "DL")
+ child = child.nextSibling;
+
+ if (child)
+ childrenHeight = child.offsetHeight;
+ }
+
+ this.top.style.height = term.offsetHeight;
+ this.middle.style.height = defHeight - childrenHeight;
+ this.bottom.style.height = childrenHeight;
+
+ if (childrenHeight == 0)
+ this.bottom.style.display = "none";
+ else
+ this.bottom.style.display = "";
+
+ this.container.style.left = getLeft(term) - 8;
+ this.container.style.top = getTop(term);
+ this.container.style.display = "";
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ this.container.style.position = "absolute";
+ this.container.style.display = "none";
+
+ this.top.className = "markertop";
+ this.middle.className = "markermiddle";
+ this.bottom.className = "markerbottom";
+
+ this.container.appendChild(this.top);
+ this.container.appendChild(this.middle);
+ this.container.appendChild(this.bottom);
+
+ //document.body.appendChild( this.container );
+
+ // Workaround bug in IE 5/6. We can not append anything to document body until
+ // full page load.
+ window.marker = this;
+ if (window.addEventListener)
+ window.addEventListener("load", new Function("document.body.appendChild( window.marker.container );"), false);
+ else if (window.attachEvent)
+ window.attachEvent("onload", new Function("document.body.appendChild( window.marker.container );"));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Outline class constructor
+///////////////////////////////////////////////////////////////////////////////
+function Outline()
+{
+ this.tree = new TreeView();
+ this.mountPoint = null;
+ this.writeEnabled = false;
+ this.marker = new Marker();
+ this.classRegExp = new RegExp;
+ this.structRegExp = new RegExp;
+ this.enumRegExp = new RegExp;
+ this.templateRegExp = new RegExp;
+ this.aliasRegExp = new RegExp;
+ this.funcRegExp = new RegExp;
+
+ this.incSymbolLevel = function()
+ {
+ if (this.mountPoint == null)
+ this.mountPoint = this.tree.children[ 0 ];
+ else
+ this.mountPoint = this.mountPoint.lastChild();
+ }
+
+ this.decSymbolLevel = function()
+ {
+ // place icons near items according to extracted below type
+ for (var i = 0; i < this.mountPoint.children.length; ++i)
+ {
+ child = this.mountPoint.children[i];
+ var term = child.termRef;
+
+ // find first span node
+ var n = term.firstChild;
+ while (n && n.nodeName != "SPAN")
+ n = n.nextSibling;
+
+ if (!n) // shouldn't happen
+ continue;
+
+ var iconSrc;
+ if (n.firstChild.nodeName == "#text")
+ {
+ var text = n.firstChild.data; // text before declaration
+
+ if ( this.classRegExp.test(text) )
+ iconSrc = "candydoc/img/outline/class.gif";
+ else if ( this.structRegExp.test(text) )
+ iconSrc = "candydoc/img/outline/struct.gif";
+ else if ( this.enumRegExp.test(text) )
+ iconSrc = "candydoc/img/outline/enum.gif";
+ else if ( this.templateRegExp.test(text) )
+ iconSrc = "candydoc/img/outline/template.gif";
+ else if ( this.aliasRegExp.test(text) )
+ iconSrc = "candydoc/img/outline/alias.gif";
+ else // function or variable? check whether '(' ')' exists on the right
+ {
+ var np = n.firstChild;
+ while (np && np.nodeName != "SCRIPT") // find our script "onDecl"
+ np = np.nextSibling;
+
+ if (np && np.nextSibling && np.nextSibling.nodeName == "#text" &&
+ this.funcRegExp.test(np.nextSibling.data))
+ {
+ iconSrc = "candydoc/img/outline/func.gif";
+ }
+ else
+ iconSrc = "candydoc/img/outline/var.gif";
+ }
+ }
+ else // enum member ?
+ iconSrc = "candydoc/img/outline/var.gif";
+
+ child.icon.src = iconSrc;
+ child.icon.width = 16;
+ child.icon.height = 16;
+ }
+
+ this.mountPoint = this.mountPoint.parentNode;
+ }
+
+ this.addDecl = function(decl)
+ {
+ function getLastLeaf(elem)
+ {
+ if (elem.childNodes.length > 0)
+ return getLastLeaf(elem.lastChild);
+ else
+ return elem;
+ }
+
+ function getCurrentTerm()
+ {
+ var ret = getLastLeaf( document.getElementById("content") );
+ while (ret && ret.nodeName != "DT")
+ ret = ret.parentNode;
+
+ return ret;
+ }
+
+ if (this.writeEnabled)
+ {
+ var node = this.mountPoint.createChild(decl);
+ node.termRef = getCurrentTerm();
+ node.setOnclick( new Function("explorer.outline.mark(this.termRef);") );
+ }
+ }
+
+ this.mark = function(term)
+ {
+ this.marker.setTo(term);
+ window.scrollTo(0, getTop(term) - getWindowHeight() / 6);
+ }
+
+
+ this.classRegExp.compile("(.*\b)?class(\b.*)?");
+ this.structRegExp.compile("(.*\b)?struct(\b.*)?");
+ this.enumRegExp.compile("(.*\b)?enum(\b.*)?");
+ this.templateRegExp.compile("(.*\b)?template(\b.*)?");
+ this.aliasRegExp.compile("(.*\b)?alias(\b.*)?");
+ this.funcRegExp.compile(/.*\(.*/);
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Package explorer class constructor
+///////////////////////////////////////////////////////////////////////////////
+function PackageExplorer()
+{
+ this.tree = new TreeView(true);
+
+ this.addModule = function(mod)
+ {
+ var moduleIco = "candydoc/img/outline/module.gif";
+ var packageIco = "candydoc/img/outline/package.gif";
+
+ var path = mod.split("\.");
+ var node = this.tree.branch(path[0]);
+ if ( !node )
+ node = this.tree.createBranch(path[0], (path.length == 1) ? moduleIco : packageIco);
+
+ for (var i = 1; i < path.length; ++i)
+ {
+ var prev = node;
+ node = node.child(path[i]);
+ if (!node)
+ node = prev.createChild(path[i], (path.length == i + 1) ? moduleIco : packageIco);
+
+ if (path.length == i + 1)
+ node.setRef(path[i] + ".html");
+ }
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Explorer class constructor
+///////////////////////////////////////////////////////////////////////////////
+function Explorer()
+{
+ this.outline = new Outline();
+ this.packageExplorer = new PackageExplorer();
+ this.tabs = new Array();
+ this.tabCount = 0;
+
+ this.initialize = function(moduleName)
+ {
+ this.tabArea = document.getElementById("tabarea");
+ this.clientArea = document.getElementById("explorerclient");
+
+ // prevent text selection
+ this.tabArea.onmousedown = new Function("return false;");
+ this.tabArea.onclick = new Function("return true;");
+ this.tabArea.onselectstart = new Function("return false;");
+ this.clientArea.onmousedown = new Function("return false;");
+ this.clientArea.onclick = new Function("return true;");
+ this.clientArea.onselectstart = new Function("return false;");
+
+ this.outline.tree.createBranch( moduleName, "candydoc/img/outline/module.gif" );
+
+ // create tabs
+ this.createTab("Outline", this.outline.tree.domEntry);
+ this.createTab("Package", this.packageExplorer.tree.domEntry);
+ }
+
+ this.createTab = function(name, domEntry)
+ {
+ var tab = new Object();
+ this.tabs[name] = tab;
+ this.tabCount++;
+
+ tab.domEntry = domEntry;
+ tab.labelSpan = document.createElement("span");
+
+ if (this.tabCount > 1)
+ {
+ tab.labelSpan.className = "inactivetab";
+ tab.domEntry.style.display = "none";
+ }
+ else
+ {
+ tab.labelSpan.className = "activetab";
+ tab.domEntry.style.display = "";
+ }
+
+ tab.labelSpan.appendChild( document.createTextNode(name) );
+ tab.labelSpan.owner = this;
+ tab.labelSpan.onclick = new Function("this.owner.setSelection('" + name + "');");
+
+ this.tabArea.appendChild( tab.labelSpan );
+ this.clientArea.appendChild( domEntry );
+ }
+
+ this.setSelection = function(tabName)
+ {
+ for (name in this.tabs)
+ {
+ this.tabs[name].labelSpan.className = "inactivetab";
+ this.tabs[name].domEntry.style.display = "none";
+ }
+
+ this.tabs[tabName].labelSpan.className = "activetab";
+ this.tabs[tabName].domEntry.style.display = "";
+ }
+}
diff --git a/BioD/contrib/msgpack-d/html/candydoc/ie56hack.css b/BioD/contrib/msgpack-d/html/candydoc/ie56hack.css
new file mode 100644
index 0000000..97a3de9
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/ie56hack.css
@@ -0,0 +1,21 @@
+/* This file is a part of CanDyDOC fileset.
+ File is written by Victor Nakoryakov and placed into the public domain.
+
+ This file is CSS to work around IE6 and earlier bugs. It's included just
+ in these browsers. */
+
+
+/* Some magic to emulate unsupported "position: fixed" style. */
+#tabarea
+{
+ _position: absolute;
+ _top: expression(eval(document.body.scrollTop+8));
+}
+
+/* ditto */
+#explorerclient
+{
+ _position: absolute;
+ _top: expression(eval(document.body.scrollTop+24));
+ _height: expression(eval(document.body.clientHeight-48));
+}
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/bg.gif b/BioD/contrib/msgpack-d/html/candydoc/img/bg.gif
new file mode 100644
index 0000000..457c74e
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/bg.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/candydoc.gif b/BioD/contrib/msgpack-d/html/candydoc/img/candydoc.gif
new file mode 100644
index 0000000..c1747c0
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/candydoc.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/alias.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/alias.gif
new file mode 100644
index 0000000..3b04bf8
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/alias.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/bg.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/bg.gif
new file mode 100644
index 0000000..5edf835
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/bg.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/class.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/class.gif
new file mode 100644
index 0000000..a4a1486
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/class.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/enum.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/enum.gif
new file mode 100644
index 0000000..4a77d8c
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/enum.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/func.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/func.gif
new file mode 100644
index 0000000..18103b1
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/func.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/module.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/module.gif
new file mode 100644
index 0000000..24813ad
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/module.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/package.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/package.gif
new file mode 100644
index 0000000..231dc70
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/package.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/struct.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/struct.gif
new file mode 100644
index 0000000..040197f
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/struct.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/template.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/template.gif
new file mode 100644
index 0000000..34bcaf7
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/template.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/outline/var.gif b/BioD/contrib/msgpack-d/html/candydoc/img/outline/var.gif
new file mode 100644
index 0000000..630b286
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/outline/var.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/package/bg.gif b/BioD/contrib/msgpack-d/html/candydoc/img/package/bg.gif
new file mode 100644
index 0000000..45ad4f0
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/package/bg.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/shim.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/shim.gif
new file mode 100644
index 0000000..45b7594
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/shim.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/tb.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tb.gif
new file mode 100644
index 0000000..efecd28
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tb.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbr.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbr.gif
new file mode 100644
index 0000000..e5da126
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbr.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrm.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrm.gif
new file mode 100644
index 0000000..e6a2147
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrm.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrp.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrp.gif
new file mode 100644
index 0000000..b2f254e
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrp.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/tr.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tr.gif
new file mode 100644
index 0000000..b30a067
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/tr.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/trm.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/trm.gif
new file mode 100644
index 0000000..b37912d
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/trm.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/img/tree/trp.gif b/BioD/contrib/msgpack-d/html/candydoc/img/tree/trp.gif
new file mode 100644
index 0000000..337b338
Binary files /dev/null and b/BioD/contrib/msgpack-d/html/candydoc/img/tree/trp.gif differ
diff --git a/BioD/contrib/msgpack-d/html/candydoc/modules.ddoc b/BioD/contrib/msgpack-d/html/candydoc/modules.ddoc
new file mode 100644
index 0000000..f36692a
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/modules.ddoc
@@ -0,0 +1,8 @@
+MODULES =
+$(MODULE msgpack.buffer)
+$(MODULE msgpack.common)
+$(MODULE msgpack.msgpack)
+$(MODULE msgpack.object)
+$(MODULE msgpack.packer)
+$(MODULE msgpack.unpacker)
+$(MODULE msgpack.util)
diff --git a/BioD/contrib/msgpack-d/html/candydoc/style.css b/BioD/contrib/msgpack-d/html/candydoc/style.css
new file mode 100644
index 0000000..831c899
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/style.css
@@ -0,0 +1,169 @@
+/* This file is a part of CanDyDOC fileset.
+ File is written by Victor Nakoryakov and placed into the public domain.
+
+ This file is main CSS file of CanDyDOC. You may adjust some part of
+ parameters to control how result documentation would looks like. See
+ further documentation for details. */
+
+
+
+/* This controls how background would looks like and
+ sets some document-scope defaults. */
+body
+{
+ /* These parameters control default font. */
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 10pt;
+ color: #666666;
+
+ /* These control look of background. Note that you have to use
+ fixed background to keep documentation good-looking in
+ IE6 and earlier. Otherwise whole *explorer* will jerk while
+ scrolling. If you do not want to use background at all use
+ some invalid url, e.g. url(foo). */
+ background-color: #e6fcea;
+ background: url(img/bg.gif) fixed;
+
+ /* Don't touch. Necessary for IE6 and earlier. */
+ height: 100%;
+}
+
+
+
+/* Style applied to all tables. Actualy there are two: one table is
+ that contains contant and footer with CanDyDOC logo, and others
+ are that contains functions' parameters description. */
+table
+{
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 10pt;
+ color: #666666;
+ text-align: justify;
+}
+
+
+/* Style used for all hyperlinks. */
+a:link { color: #009900; text-decoration: none }
+a:visited { color: #009999; text-decoration: none }
+a:hover { color: #0033cc; text-decoration: none }
+a:active { color: #0033cc; text-decoration: none }
+
+/*
+table.matrix
+{
+ border-left: double 3px #666666;
+ border-right: double 3px #666666;
+ margin-left: 3em;
+}
+*/
+
+/* Style appled to declarations. E.g. 'void foo(int a, float b);' */
+span.decl { font-size: 10pt; font-weight: bold; color: #000000; text-align: left }
+/* Style appled to current declaration's symbol. E.g. 'foo' in 'void foo(int a, float b);' */
+span.currsymbol { font-size: 12pt; color: #009900 }
+/* Style appled to function's parameters. E.g. 'a' and 'b' in 'void foo(int a, float b);' */
+span.funcparam { font-style: italic; font-weight: normal; color: #331200 }
+
+/* Style for div that actualy contains documenation. */
+#content
+{
+ padding-right: 8px;
+ position: absolute;
+ left: 245px;
+ top: 8px;
+ text-align: justify;
+}
+
+/* Style for table that is inside div considered above. Contains documentaton
+ itself and footer with CanDyDOC logo. */
+table.content
+{
+ margin-bottom: 8px;
+ border-spacing: 0px;
+ border-collapse: collapse;
+ background-color: #ffffff;
+}
+
+/* Style for cell of above considered table that contains documentation itself. */
+#docbody
+{
+ padding: 8px 20px 8px 20px;
+ border: solid 1px #009900;
+}
+
+/* Style for cell that contains CanDyDOC logo. */
+#docfooter
+{
+ height: 16px;
+ background-color: #ddeedd;
+ padding: 0px 8px 0px 8px;
+ border: solid 1px #009900;
+}
+
+/* Style applied to currently active tab of explorer window. */
+span.activetab
+{
+ background-color: #0033cc;
+ border-top: solid 2px #009900;
+ color: #ffffff;
+ font-weight: bold;
+ padding-left: 4px;
+ padding-right: 4px;
+ padding-top: 1px;
+ margin-right: 1px;
+}
+
+/* Style applied to currently inactive tab of explorer window. */
+span.inactivetab
+{
+ background-color: #000066;
+ color: #cccccc;
+ font-weight: normal;
+ padding-left: 4px;
+ padding-right: 4px;
+ padding-top: 0px;
+ margin-right: 1px;
+}
+
+/* Style applied to div that contains tabs of explorer. Note that if
+ you want to change it's position you have to change position of
+ #explorerclient, #content and corresponding values in ie56hack.css */
+#tabarea
+{
+ position: fixed;
+ top: 8px;
+ width: 205px;
+ height: 16px;
+ cursor: default;
+}
+
+
+/* Style applied to div that contains tree in explorer. Note that if
+ you want to change it's position you have to change position of
+ #tabarea, #content and corresponding values in ie56hack.css */
+#explorerclient
+{
+ position: fixed;
+ top: 24px;
+ bottom: 8px;
+ width: 205px;
+ overflow: auto;
+ background-color: #fcfffc;
+ border: solid 2px #0033cc;
+ padding: 4px;
+ cursor: default;
+ color: Black;
+}
+
+/* Following 3 styles control appearance of marker that appears
+ if you click some entity in outline window. */
+div.markertop { border-left: solid 2px #0033cc;}
+div.markermiddle{ border-left: dotted 2px #0033cc;}
+div.markerbottom{ border-left: dotted 2px #66cc66;}
+
+/* Style applied to preformated text used to show examples. */
+pre.d_code
+{
+ border: dotted 1px #9c9;
+ background-color: #eeffee;
+}
\ No newline at end of file
diff --git a/BioD/contrib/msgpack-d/html/candydoc/tree.js b/BioD/contrib/msgpack-d/html/candydoc/tree.js
new file mode 100644
index 0000000..97996c0
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/tree.js
@@ -0,0 +1,374 @@
+/* This file is a part of CanDyDOC fileset.
+ File is written by Victor Nakoryakov and placed into the public domain.
+
+ This file is javascript with classes that represents native style tree control. */
+
+var pmNone = 0;
+var pmPlus = 1;
+var pmMinus = 2;
+
+var hlNone = 0;
+var hlGrey = 1;
+var hlSelected = 2;
+
+function TreeView(hrefMode)
+{
+ this.domEntry = document.createElement("div");
+ this.children = new Array();
+ this.selection = null;
+ this.hrefMode = hrefMode;
+
+ this.createBranch = function(text, iconSrc)
+ {
+ var root = new TreeNode(text, iconSrc, this.hrefMode);
+ root.owner = this;
+ this.children[ this.children.length ] = root;
+ this.domEntry.appendChild( root.domEntry );
+ return root;
+ }
+
+ this.branch = function(text)
+ {
+ var ret = null;
+ for (var i = 0; i < this.children.length; ++i)
+ if (this.children[i].textElement.data == text)
+ {
+ ret = this.children[i];
+ break;
+ }
+
+ return ret;
+ }
+
+ this.domEntry.style.fontSize = "10px";
+ this.domEntry.style.cursor = "default";
+ this.domEntry.style.whiteSpace = "nowrap";
+}
+
+var idCounter = 0;
+function TreeNode(text, iconSrc, hrefMode)
+{
+ this.id = idCounter++;
+ this.parentNode = null;
+ this.children = new Array();
+ this.domEntry = document.createElement("div");
+ this.icon = document.createElement("img");
+ this.textElement = document.createTextNode(text);
+ this.textSpan = document.createElement("span");
+ this.lineDiv = document.createElement("div");
+ this.hierarchyImgs = new Array();
+ this.onclick = null;
+
+ function createIcon()
+ {
+ var img = document.createElement("img");
+ img.style.verticalAlign = "middle";
+ img.style.position = "relative";
+ img.style.top = "-1px";
+ img.width = 16;
+ img.height = 16;
+ return img;
+ }
+
+ function createHierarchyImage()
+ {
+ var img = createIcon();
+ img.pointsTop = false;
+ img.pointsBottom = false;
+ img.pointsRight = false;
+ img.pmState = pmNone;
+ return img;
+ }
+
+ function genHierarchyImageSrc(hierarchyImg)
+ {
+ var name = "";
+ if (hierarchyImg.pointsTop)
+ name += "t";
+
+ if (hierarchyImg.pointsBottom)
+ name += "b";
+
+ if (hierarchyImg.pointsRight)
+ name += "r";
+
+ if (hierarchyImg.pmState == pmPlus)
+ name += "p";
+ else if (hierarchyImg.pmState == pmMinus)
+ name += "m";
+
+ if (name == "")
+ name = "shim";
+
+ return "candydoc/img/tree/" + name + ".gif";
+ }
+
+ function setSrc(icon, src)
+ {
+ icon.src = src;
+ // After src change width and height are reseted in IE.
+ // Bug workaround:
+ icon.width = 16;
+ icon.height = 16;
+ }
+
+ this.createChild = function(text, iconSrc)
+ {
+ var child = new TreeNode(text, iconSrc, this.owner.hrefMode);
+ this.children[ this.children.length ] = child;
+ this.domEntry.appendChild( child.domEntry );
+ child.parentNode = this;
+ child.owner = this.owner;
+
+ // insert hierarchy images according to deepness level
+ // of created child.
+
+ if (this.children.length > 1)
+ {
+ // there were already added child before. So copy `level-1`
+ // hierarchy images from it.
+
+ var prevAddedChild = this.children[ this.children.length - 2 ];
+
+ for (var i = 0; i < prevAddedChild.hierarchyImgs.length - 1; ++i)
+ {
+ var prevAddedChildImg = prevAddedChild.hierarchyImgs[i];
+ var img = createHierarchyImage();
+ setSrc(img, prevAddedChildImg.src);
+ img.pointsTop = prevAddedChildImg.pointsTop;
+ img.pointsBottom = prevAddedChildImg.pointsBottom;
+ img.pointsRight = prevAddedChildImg.pointsRight;
+ img.pmState = prevAddedChildImg.pmState;
+
+ child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
+ child.lineDiv.insertBefore(img, child.icon);
+ }
+
+ // change last hierarchy image of prevAddedChild from |_ to |-
+ var lastHierarchyImg = prevAddedChild.hierarchyImgs[ prevAddedChild.hierarchyImgs.length - 1 ];
+ lastHierarchyImg.pointsBottom = true;
+ setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
+
+ // change hierarchy images of prevAddedChild's children on it's last
+ // level to |
+ prevAddedChild.addHierarchyTBLine(prevAddedChild.hierarchyImgs.length - 1);
+ }
+ else
+ {
+ // this is a first child. So copy `level-2`
+ // hierarchy images from parent, i.e. this.
+
+ for (var i = 0; i < this.hierarchyImgs.length - 1; ++i)
+ {
+ var parentImg = this.hierarchyImgs[i];
+ var img = createHierarchyImage();
+ setSrc(img, parentImg.src);
+ img.pointsTop = parentImg.pointsTop;
+ img.pointsBottom = parentImg.pointsBottom;
+ img.pointsRight = parentImg.pointsRight;
+ img.pmState = parentImg.pmState;
+
+ child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
+ child.lineDiv.insertBefore(img, child.icon);
+ }
+
+ if (this.hierarchyImgs.length > 0) // we are not root
+ {
+ // change last hierarchy image of parent (i.e. this): add minus to it
+ var lastHierarchyImg = this.hierarchyImgs[ this.hierarchyImgs.length - 1];
+ lastHierarchyImg.pmState = pmMinus;
+ setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
+ lastHierarchyImg.owner = this;
+ lastHierarchyImg.onclick = new Function("e", "this.owner.processPMClick(e);");
+
+ // make decision on image on `level-1`. It depends on parent's (ie this)
+ // image on same level.
+ var parentL1HierarchyImg = lastHierarchyImg;
+ var l1HierarchyImg = createHierarchyImage();
+ if (parentL1HierarchyImg.pointsBottom)
+ {
+ l1HierarchyImg.pointsTop = true;
+ l1HierarchyImg.pointsBottom = true;
+ }
+ setSrc(l1HierarchyImg, genHierarchyImageSrc(l1HierarchyImg));
+ child.hierarchyImgs[ child.hierarchyImgs.length ] = l1HierarchyImg;
+ child.lineDiv.insertBefore(l1HierarchyImg, child.icon);
+ }
+ }
+
+ // in any case on last level our child will have icon |_
+ var img = createHierarchyImage();
+ img.pointsTop = true;
+ img.pointsRight = true;
+ setSrc(img, genHierarchyImageSrc(img));
+
+ child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
+ child.lineDiv.insertBefore(img, child.icon);
+
+ return child;
+ }
+
+ this.lastChild = function()
+ {
+ return this.children[ this.children.length - 1 ];
+ }
+
+ this.child = function(text)
+ {
+ var ret = null;
+ for (var i = 0; i < this.children.length; ++i)
+ if (this.children[i].textElement.data == text)
+ {
+ ret = this.children[i];
+ break;
+ }
+
+ return ret;
+ }
+
+ this.addHierarchyTBLine = function(level)
+ {
+ for (var i = 0; i < this.children.length; ++i)
+ {
+ var img = this.children[i].hierarchyImgs[level];
+ img.pointsTop = true;
+ img.pointsBottom = true;
+ setSrc(img, genHierarchyImageSrc(img));
+ this.children[i].addHierarchyTBLine(level);
+ }
+ }
+
+ this.expand = function()
+ {
+ var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
+
+ if (img.pmState == pmPlus)
+ {
+ img.pmState = pmMinus;
+ setSrc(img, genHierarchyImageSrc(img));
+
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i].domEntry.style.display = "";
+ }
+ }
+
+ this.collapse = function()
+ {
+ var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
+
+ if (img.pmState == pmMinus)
+ {
+ img.pmState = pmPlus;
+ setSrc(img, genHierarchyImageSrc(img));
+
+ for (var i = 0; i < this.children.length; ++i)
+ this.children[i].domEntry.style.display = "none";
+ }
+ }
+
+ this.toggle = function()
+ {
+ var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
+ if (img.pmState == pmMinus)
+ this.collapse();
+ else
+ this.expand();
+ }
+
+ this.select = function()
+ {
+ if (this.owner.selection != this)
+ {
+ if (this.owner.selection)
+ this.owner.selection.setHighlight(hlNone);
+
+ this.owner.selection = this;
+ this.setHighlight(hlSelected);
+ }
+ }
+
+ this.setHighlight = function(mode)
+ {
+ if (mode == hlNone)
+ {
+ this.textSpan.style.backgroundColor = "";
+ this.textSpan.style.color = "";
+ this.textSpan.style.border = "";
+ }
+ else if (mode == hlGrey)
+ {
+ this.textSpan.style.backgroundColor = "#aaaaaa";
+ this.textSpan.style.color = "";
+ this.textSpan.style.border = "";
+ }
+ else if (mode == hlSelected)
+ {
+ this.textSpan.style.backgroundColor = "3399cc";
+ this.textSpan.style.color = "white";
+ this.textSpan.style.border = "dotted 1px red";
+ }
+ }
+
+ this.setOnclick = function(proc)
+ {
+ this.onclick = proc;
+ }
+
+ this.setRef = function(url)
+ {
+ if (this.anchor)
+ this.anchor.href = url;
+ }
+
+ this.processPMClick = function(e)
+ {
+ this.toggle();
+
+ // prevent this line selection, stop bubbling
+ if (e)
+ e.stopPropagation(); // Mozilla way
+ if (window.event)
+ window.event.cancelBubble = true; // IE way
+ }
+
+ this.processOnclick = function()
+ {
+ this.select();
+ if (this.onclick instanceof Function)
+ this.onclick();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ if (iconSrc)
+ this.icon.src = iconSrc;
+ else
+ {
+ this.icon.width = 0;
+ this.icon.height = 0;
+ }
+
+ this.icon.style.verticalAlign = "middle";
+ this.icon.style.position = "relative";
+ this.icon.style.top = "-1px";
+ this.icon.style.paddingRight = "2px";
+
+ if (!hrefMode)
+ {
+ this.textSpan.appendChild( this.textElement );
+ }
+ else
+ {
+ this.anchor = document.createElement("a");
+ this.anchor.appendChild( this.textElement );
+ this.textSpan.appendChild( this.anchor );
+ }
+
+ this.lineDiv.appendChild( this.icon );
+ this.lineDiv.appendChild( this.textSpan );
+ this.domEntry.appendChild( this.lineDiv );
+
+ this.lineDiv.owner = this;
+
+ if (!hrefMode)
+ this.lineDiv.onclick = new Function("this.owner.processOnclick();");
+}
diff --git a/BioD/contrib/msgpack-d/html/candydoc/util.js b/BioD/contrib/msgpack-d/html/candydoc/util.js
new file mode 100644
index 0000000..edb7165
--- /dev/null
+++ b/BioD/contrib/msgpack-d/html/candydoc/util.js
@@ -0,0 +1,41 @@
+/* This file is a part of CanDyDOC fileset.
+ File is written by Victor Nakoryakov and placed into the public domain.
+
+ This file is javascript with cross-browser utility functions. */
+
+function getLeft(elem)
+{
+ var ret = 0;
+ while (elem.offsetParent)
+ {
+ ret += elem.offsetLeft;
+ elem = elem.offsetParent;
+ }
+
+ return ret;
+}
+
+function getTop(elem)
+{
+ var ret = 0;
+ while (elem.offsetParent)
+ {
+ ret += elem.offsetTop;
+ elem = elem.offsetParent;
+ }
+
+ return ret;
+}
+
+function getWindowHeight()
+{
+ var ret = 0;
+ if (typeof(window.innerHeight) == "number")
+ ret = window.innerHeight;
+ else if (document.documentElement && document.documentElement.clientHeight)
+ ret = document.documentElement.clientHeight;
+ else if (document.body && document.body.clientHeight)
+ ret = document.body.clientHeight;
+
+ return ret;
+}
diff --git a/BioD/contrib/msgpack-d/meson.build b/BioD/contrib/msgpack-d/meson.build
new file mode 100644
index 0000000..295de55
--- /dev/null
+++ b/BioD/contrib/msgpack-d/meson.build
@@ -0,0 +1,73 @@
+ project('msgpack-d', 'd',
+ meson_version: '>=0.47',
+ license: 'BSL-1.0',
+ version: '1.0.4'
+)
+
+project_soversion = '1'
+
+pkgc = import('pkgconfig')
+
+#
+# Sources
+#
+msgpack_src = [
+ 'src/msgpack/attribute.d',
+ 'src/msgpack/buffer.d',
+ 'src/msgpack/common.d',
+ 'src/msgpack/exception.d',
+ 'src/msgpack/package.d',
+ 'src/msgpack/packer.d',
+ 'src/msgpack/register.d',
+ 'src/msgpack/streaming_unpacker.d',
+ 'src/msgpack/unpacker.d',
+ 'src/msgpack/value.d',
+]
+
+src_dir = include_directories('src/')
+
+#
+# Targets
+#
+msgpack_lib = library('msgpack-d',
+ [msgpack_src],
+ include_directories: [src_dir],
+ install: true,
+ version: meson.project_version(),
+ soversion: project_soversion,
+)
+
+pkgc.generate(name: 'msgpack-d',
+ libraries: [msgpack_lib],
+ subdirs: 'd/msgpack',
+ version: meson.project_version(),
+ description: 'Library for lexing and parsing D source code.'
+)
+
+# for use by others which embed this as subproject
+msgpack_dep = declare_dependency(
+ link_with: [msgpack_lib],
+ include_directories: [src_dir]
+)
+
+#
+# Tests
+#
+if meson.get_compiler('d').get_id() == 'llvm'
+ extra_args = ['-main', '-link-defaultlib-shared']
+else
+ extra_args = ['-main']
+endif
+
+msgpack_test_exe = executable('test_msgpack',
+ [msgpack_src],
+ include_directories: [src_dir],
+ d_unittest: true,
+ link_args: extra_args,
+)
+test('test_msgpack', msgpack_test_exe)
+
+#
+# Install
+#
+install_subdir('src/msgpack/', install_dir: 'include/d/msgpack/')
diff --git a/BioD/contrib/msgpack-d/posix.mak b/BioD/contrib/msgpack-d/posix.mak
new file mode 100644
index 0000000..f5f5eb8
--- /dev/null
+++ b/BioD/contrib/msgpack-d/posix.mak
@@ -0,0 +1,50 @@
+# build mode: 32bit or 64bit
+MODEL ?= $(shell getconf LONG_BIT)
+
+ifeq (,$(DMD))
+ DMD := dmd
+endif
+
+LIB = libmsgpack-d.a
+DFLAGS = -Isrc -m$(MODEL) -d -w -dip25 -dip1000
+
+ifeq (true, $(EnableReal))
+ DFLAGS += -version=EnableReal
+endif
+
+ifeq ($(BUILD),debug)
+ DFLAGS += -g -debug
+else
+ DFLAGS += -O -release -nofloat -inline -noboundscheck
+endif
+
+NAMES = attribute common package register unpacker buffer exception packer streaming_unpacker value
+FILES = $(addsuffix .d, $(NAMES))
+SRCS = $(addprefix src/msgpack/, $(FILES))
+
+# DDoc
+DOCS = $(addsuffix .html, $(NAMES))
+DOCDIR = html
+CANDYDOC = $(addprefix html/candydoc/, candy.ddoc modules.ddoc)
+DDOCFLAGS = -Dd$(DOCDIR) -c -o- -Isrc $(CANDYDOC)
+
+target: doc $(LIB)
+
+$(LIB):
+ $(DMD) $(DFLAGS) -lib -of$(LIB) $(SRCS)
+
+doc:
+ $(DMD) $(DDOCFLAGS) $(SRCS)
+
+clean:
+ rm $(addprefix $(DOCDIR)/, $(DOCS)) $(LIB)
+
+MAIN_FILE = "empty_msgpack_unittest.d"
+
+unittest:
+ echo 'import msgpack; void main(){}' > $(MAIN_FILE)
+ $(DMD) $(DFLAGS) -unittest -of$(LIB) $(SRCS) -run $(MAIN_FILE)
+ rm $(MAIN_FILE)
+
+run_examples:
+ echo example/* | xargs -n 1 $(DMD) src/msgpack/*.d $(DFLAGS) -Isrc -run
diff --git a/BioD/contrib/msgpack-d/src/msgpack/attribute.d b/BioD/contrib/msgpack-d/src/msgpack/attribute.d
new file mode 100644
index 0000000..d2e9659
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/attribute.d
@@ -0,0 +1,95 @@
+module msgpack.attribute;
+
+import std.typetuple; // will use std.meta
+import std.traits;
+
+
+/**
+ * Attribute for specifying non pack/unpack field.
+ * This is an alternative approach of MessagePackable mixin.
+ *
+ * Example:
+ * -----
+ * struct S
+ * {
+ * int num;
+ * // Packer/Unpacker ignores this field;
+ * @nonPacked string str;
+ * }
+ * -----
+ */
+struct nonPacked {}
+
+
+package template isPackedField(alias field)
+{
+ enum isPackedField = (staticIndexOf!(nonPacked, __traits(getAttributes, field)) == -1) && (!isSomeFunction!(typeof(field)));
+}
+
+
+/**
+ * Attribute for specifying serialize/deserialize proxy for pack/unpack field.
+ * This is an alternative approach of registerPackHandler/registerUnpackHandler.
+ *
+ * Example:
+ * -----
+ * struct Proxy
+ * {
+ * import std.conv;
+ * static void serialize(ref Packer p, ref int val) { p.pack(to!string(val)); }
+ * static void deserialize(ref Unpacker u, ref int val) { string tmp; u.unpack(tmp); val = to!int(tmp); }
+ * }
+ * struct S
+ * {
+ * // The Packer/Unpacker proxy handler is applied this field.
+ * @serializedAs!Proxy int num;
+ * string str;
+ * }
+ * -----
+ */
+struct serializedAs(T){}
+
+package enum bool isSerializedAs(A) = is(A : serializedAs!T, T);
+package alias getSerializedAs(T : serializedAs!Proxy, Proxy) = Proxy;
+package alias ProxyList(alias value) = staticMap!(getSerializedAs, Filter!(isSerializedAs, __traits(getAttributes, value)));
+package template isSerializedAs(alias value)
+{
+ static if ( __traits(compiles, __traits(getAttributes, value)) ) {
+ enum bool isSerializedAs = ProxyList!value.length > 0;
+ } else {
+ enum bool isSerializedAs = false;
+ }
+}
+package template getSerializedAs(alias value)
+{
+ private alias _list = ProxyList!value;
+ static assert(_list.length <= 1, `Only single serialization proxy is allowed`);
+ alias getSerializedAs = _list[0];
+}
+package template hasSerializedAs(alias value)
+{
+ private enum _listLength = ProxyList!value.length;
+ static assert(_listLength <= 1, `Only single serialization proxy is allowed`);
+ enum bool hasSerializedAs = _listLength == 1;
+}
+
+unittest
+{
+ import msgpack.packer, msgpack.unpacker;
+ struct Proxy
+ {
+ static void serialize(ref Packer p, ref int value) {}
+ static void deserialize(ref Unpacker u, ref int value) {}
+ }
+ struct A
+ {
+ @serializedAs!Proxy int a;
+ @(42) int b;
+ @(42) @serializedAs!Proxy int c;
+ }
+ static assert(is(getSerializedAs!(A.a) == Proxy));
+ static assert(isSerializedAs!(__traits(getAttributes, A.a)[0]));
+ static assert(hasSerializedAs!(A.a));
+ static assert(!hasSerializedAs!(A.b));
+ static assert(hasSerializedAs!(A.c));
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/buffer.d b/BioD/contrib/msgpack-d/src/msgpack/buffer.d
new file mode 100644
index 0000000..eb2a374
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/buffer.d
@@ -0,0 +1,216 @@
+// Written in the D programming language.
+
+module msgpack.buffer;
+
+//import std.traits;
+import std.range;
+
+
+version(Posix)
+{
+ import core.sys.posix.sys.uio : iovec;
+}
+else
+{
+ /**
+ * from core.sys.posix.sys.uio.iovec for compatibility with posix.
+ */
+ struct iovec
+ {
+ void* iov_base;
+ size_t iov_len;
+ }
+}
+
+
+/**
+ * $(D RefBuffer) is a reference stored buffer for more efficient serialization
+ *
+ * Example:
+ * -----
+ * auto packer = packer(RefBuffer(16)); // threshold is 16
+ *
+ * // packs data
+ *
+ * writev(fd, cast(void*)packer.buffer.vector.ptr, packer.buffer.vector.length);
+ * -----
+ */
+struct RefBuffer
+{
+ private:
+ static struct Chunk
+ {
+ ubyte[] data; // storing serialized value
+ size_t used; // used size of data
+ }
+
+ immutable size_t Threshold;
+ immutable size_t ChunkSize;
+
+ // for putCopy
+ Chunk[] chunks_; // memory chunk for buffer
+ size_t index_; // index for cunrrent chunk
+
+ // for putRef
+ iovec[] vecList_; // reference to large data or copied data.
+
+
+ public:
+ /**
+ * Constructs a buffer.
+ *
+ * Params:
+ * threshold = the threshold of writing value or stores reference.
+ * chunkSize = the default size of chunk for allocation.
+ */
+ @safe
+ this(in size_t threshold, in size_t chunkSize = 8192)
+ {
+ Threshold = threshold;
+ ChunkSize = chunkSize;
+
+ chunks_.length = 1;
+ chunks_[index_].data.length = chunkSize;
+ }
+
+
+ /**
+ * Returns the buffer contents that excluding references.
+ *
+ * Returns:
+ * the non-contiguous copied contents.
+ */
+ @property @safe
+ nothrow ubyte[] data()
+ {
+ ubyte[] result;
+
+ foreach (ref chunk; chunks_)
+ result ~= chunk.data[0..chunk.used];
+
+ return result;
+ }
+
+
+ /**
+ * Forwards to all buffer contents.
+ *
+ * Returns:
+ * the array of iovec struct that stores references.
+ */
+ @property @safe
+ nothrow ref iovec[] vector() return
+ {
+ return vecList_;
+ }
+
+
+ /**
+ * Writes the argument to buffer and stores the reference of writed content
+ * if the argument size is smaller than threshold,
+ * otherwise stores the reference of argument directly.
+ *
+ * Params:
+ * value = the content to write.
+ */
+ @safe
+ void put(in ubyte value)
+ {
+ ubyte[1] values = [value];
+ putCopy(values);
+ }
+
+
+ /// ditto
+ @safe
+ void put(in ubyte[] value)
+ {
+ if (value.length < Threshold)
+ putCopy(value);
+ else
+ putRef(value);
+ }
+
+
+ private:
+ /*
+ * Stores the reference of $(D_PARAM value).
+ *
+ * Params:
+ * value = the content to write.
+ */
+ @trusted
+ void putRef(in ubyte[] value)
+ {
+ vecList_.length += 1;
+ vecList_[$ - 1] = iovec(cast(void*)value.ptr, value.length);
+ }
+
+
+ /*
+ * Writes $(D_PARAM value) to buffer and appends to its reference.
+ *
+ * Params:
+ * value = the contents to write.
+ */
+ @trusted
+ void putCopy(const scope ubyte[] value)
+ {
+ /*
+ * Helper for expanding new space.
+ */
+ void expand(in size_t size)
+ {
+ const newSize = size < ChunkSize ? ChunkSize : size;
+
+ index_++;
+ chunks_.length = 1;
+ chunks_[index_].data.length = newSize;
+ }
+
+ const size = value.length;
+
+ // lacks current chunk?
+ if (chunks_[index_].data.length - chunks_[index_].used < size)
+ expand(size);
+
+ const base = chunks_[index_].used; // start index
+ auto data = chunks_[index_].data[base..base + size]; // chunk to write
+
+ data[] = value[];
+ chunks_[index_].used += size;
+
+ // Optimization for avoiding iovec allocation.
+ if (vecList_.length && data.ptr == (vecList_[$ - 1].iov_base +
+ vecList_[$ - 1].iov_len))
+ vecList_[$ - 1].iov_len += size;
+ else
+ putRef(data);
+ }
+}
+
+
+unittest
+{
+ static assert(isOutputRange!(RefBuffer, ubyte) &&
+ isOutputRange!(RefBuffer, ubyte[]));
+
+ auto buffer = RefBuffer(2, 4);
+
+ ubyte[] tests = [1, 2];
+ foreach (v; tests)
+ buffer.put(v);
+ buffer.put(tests);
+
+ assert(buffer.data == tests, "putCopy failed");
+
+ iovec[] vector = buffer.vector;
+ ubyte[] result;
+
+ assert(vector.length == 2, "Optimization failed");
+
+ foreach (v; vector)
+ result ~= (cast(ubyte*)v.iov_base)[0..v.iov_len];
+
+ assert(result == tests ~ tests);
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/common.d b/BioD/contrib/msgpack-d/src/msgpack/common.d
new file mode 100644
index 0000000..6f723d9
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/common.d
@@ -0,0 +1,633 @@
+// Written in the D programming language.
+
+module msgpack.common;
+
+import msgpack.attribute;
+
+import std.typetuple; // will use std.meta
+import std.traits;
+
+
+// for Converting Endian using ntohs and ntohl;
+version(Windows)
+{
+ package import core.sys.windows.winsock2;
+}
+else
+{
+ package import core.sys.posix.arpa.inet;
+}
+
+
+version(EnableReal)
+{
+ package enum EnableReal = true;
+}
+else
+{
+ package enum EnableReal = false;
+}
+
+
+static if (real.sizeof == double.sizeof) {
+ // for 80bit real inter-operation on non-x86 CPU
+ version = NonX86;
+
+ package import std.numeric;
+}
+
+
+@trusted:
+public:
+
+
+/**
+ * $(D ExtValue) is a $(D MessagePack) Extended value representation.
+ * The application is responsible for correctly interpreting $(D data) according
+ * to the type described by $(D type).
+ */
+struct ExtValue
+{
+ byte type; /// An integer 0-127 with application-defined meaning
+ ubyte[] data; /// The raw bytes
+}
+
+
+/**
+ * MessagePack type-information format
+ *
+ * See_Also:
+ * $(LINK2 http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec, MessagePack Specificaton)
+ */
+enum Format : ubyte
+{
+ // unsinged integer
+ UINT8 = 0xcc, // ubyte
+ UINT16 = 0xcd, // ushort
+ UINT32 = 0xce, // uint
+ UINT64 = 0xcf, // ulong
+
+ // signed integer
+ INT8 = 0xd0, // byte
+ INT16 = 0xd1, // short
+ INT32 = 0xd2, // int
+ INT64 = 0xd3, // long
+
+ // floating point
+ FLOAT = 0xca, // float
+ DOUBLE = 0xcb, // double
+
+ // raw byte
+ RAW = 0xa0,
+ RAW16 = 0xda,
+ RAW32 = 0xdb,
+
+ // bin type
+ BIN8 = 0xc4,
+ BIN16 = 0xc5,
+ BIN32 = 0xc6,
+
+ // ext type
+ EXT = 0xd4, // fixext 1/2/4/8/16
+ EXT8 = 0xc7,
+ EXT16 = 0xc8,
+ EXT32 = 0xc9,
+
+ // str type
+ STR8 = 0xd9,
+ //STR16 = 0xda,
+ //STR32 = 0xdb,
+
+ // array
+ ARRAY = 0x90,
+ ARRAY16 = 0xdc,
+ ARRAY32 = 0xdd,
+
+ // map
+ MAP = 0x80,
+ MAP16 = 0xde,
+ MAP32 = 0xdf,
+
+ // other
+ NIL = 0xc0, // null
+ TRUE = 0xc3,
+ FALSE = 0xc2,
+
+ // real (This format is D only!)
+ REAL = 0xd4
+}
+
+
+package:
+
+
+/**
+ * For float type serialization / deserialization
+ */
+union _f
+{
+ float f;
+ uint i;
+}
+
+
+/**
+ * For double type serialization / deserialization
+ */
+union _d
+{
+ double f;
+ ulong i;
+}
+
+
+/**
+ * For real type serialization / deserialization
+ *
+ * 80-bit real is padded to 12 bytes(Linux) and 16 bytes(Mac).
+ * http://lists.puremagic.com/pipermail/digitalmars-d/2010-June/077394.html
+ */
+union _r
+{
+ real f;
+
+ struct
+ {
+ ulong fraction;
+ ushort exponent; // includes sign
+ }
+}
+
+enum RealSize = 10; // Real size is 80bit
+
+
+/**
+ * Detects whether $(D_PARAM T) is a built-in byte type.
+ */
+template isByte(T)
+{
+ enum isByte = staticIndexOf!(Unqual!T, byte, ubyte) >= 0;
+}
+
+
+unittest
+{
+ static assert(isByte!(byte));
+ static assert(isByte!(const(byte)));
+ static assert(isByte!(ubyte));
+ static assert(isByte!(immutable(ubyte)));
+ static assert(!isByte!(short));
+ static assert(!isByte!(char));
+ static assert(!isByte!(string));
+}
+
+
+/**
+ * Gets asterisk string from pointer type
+ */
+template AsteriskOf(T)
+{
+ static if (is(T P == U*, U))
+ enum AsteriskOf = "*" ~ AsteriskOf!U;
+ else
+ enum AsteriskOf = "";
+}
+
+
+/**
+ * Get the number of member to serialize.
+ */
+template SerializingMemberNumbers(Classes...)
+{
+ static if (Classes.length == 0)
+ enum SerializingMemberNumbers = 0;
+ else
+ enum SerializingMemberNumbers = Filter!(isPackedField, Classes[0].tupleof).length + SerializingMemberNumbers!(Classes[1..$]);
+}
+
+
+/**
+ * Get derived classes with serialization-order
+ */
+template SerializingClasses(T)
+{
+ // There is no information in Object type. Currently disable Object serialization.
+ static if (is(T == Object))
+ static assert(false, "Object type serialization doesn't support yet. Please define toMsgpack/fromMsgpack and use cast");
+ else
+ alias TypeTuple!(Reverse!(Erase!(Object, BaseClassesTuple!(T))), T) SerializingClasses;
+}
+
+
+/**
+ * Get a field name of class or struct.
+ */
+template getFieldName(Type, size_t i)
+{
+ import std.conv : text;
+
+ static assert((is(Unqual!Type == class) || is(Unqual!Type == struct)), "Type must be class or struct: type = " ~ Type.stringof);
+ static assert(i < Type.tupleof.length, text(Type.stringof, " has ", Type.tupleof.length, " attributes: given index = ", i));
+
+ enum getFieldName = __traits(identifier, Type.tupleof[i]);
+}
+
+
+version (LittleEndian)
+{
+ /*
+ * Converts $(value) to different Endian.
+ *
+ * Params:
+ * value = the LittleEndian value to convert.
+ *
+ * Returns:
+ * the converted value.
+ */
+ @trusted
+ ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16)
+ {
+ return ntohs(cast(ushort)value);
+ }
+
+
+ // ditto
+ @trusted
+ uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32)
+ {
+ return ntohl(cast(uint)value);
+ }
+
+
+ // ditto
+ @trusted
+ ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64)
+ {
+ // dmd has convert function?
+ return ((((cast(ulong)value) << 56) & 0xff00000000000000UL) |
+ (((cast(ulong)value) << 40) & 0x00ff000000000000UL) |
+ (((cast(ulong)value) << 24) & 0x0000ff0000000000UL) |
+ (((cast(ulong)value) << 8) & 0x000000ff00000000UL) |
+ (((cast(ulong)value) >> 8) & 0x00000000ff000000UL) |
+ (((cast(ulong)value) >> 24) & 0x0000000000ff0000UL) |
+ (((cast(ulong)value) >> 40) & 0x000000000000ff00UL) |
+ (((cast(ulong)value) >> 56) & 0x00000000000000ffUL));
+ }
+
+
+ unittest
+ {
+ assert(convertEndianTo!16(0x0123) == 0x2301);
+ assert(convertEndianTo!32(0x01234567) == 0x67452301);
+ assert(convertEndianTo!64(0x0123456789abcdef) == 0xefcdab8967452301);
+ }
+
+
+ /*
+ * Comapatible for BigEndian environment.
+ */
+ ubyte take8from(size_t bit = 8, T)(T value)
+ {
+ static if (bit == 8 || bit == 16 || bit == 32 || bit == 64)
+ return (cast(ubyte*)&value)[0];
+ else
+ static assert(false, bit.stringof ~ " is not support bit width.");
+ }
+
+
+ unittest
+ {
+ foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) {
+ assert(take8from!8 (cast(Integer)0x01) == 0x01);
+ assert(take8from!16(cast(Integer)0x0123) == 0x23);
+ assert(take8from!32(cast(Integer)0x01234567) == 0x67);
+ assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef);
+ }
+ }
+}
+else
+{
+ /*
+ * Comapatible for LittleEndian environment.
+ */
+ @safe
+ ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16)
+ {
+ return cast(ushort)value;
+ }
+
+
+ // ditto
+ @safe
+ uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32)
+ {
+ return cast(uint)value;
+ }
+
+
+ // ditto
+ @safe
+ ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64)
+ {
+ return cast(ulong)value;
+ }
+
+
+ unittest
+ {
+ assert(convertEndianTo!16(0x0123) == 0x0123);
+ assert(convertEndianTo!32(0x01234567) == 0x01234567);
+ assert(convertEndianTo!64(0x0123456789) == 0x0123456789);
+ }
+
+
+ /*
+ * Takes 8bit from $(D_PARAM value)
+ *
+ * Params:
+ * value = the content to take.
+ *
+ * Returns:
+ * the 8bit value corresponding $(D_PARAM bit) width.
+ */
+ ubyte take8from(size_t bit = 8, T)(T value)
+ {
+ static if (bit == 8)
+ return (cast(ubyte*)&value)[0];
+ else static if (bit == 16)
+ return (cast(ubyte*)&value)[1];
+ else static if (bit == 32)
+ return (cast(ubyte*)&value)[3];
+ else static if (bit == 64)
+ return (cast(ubyte*)&value)[7];
+ else
+ static assert(false, bit.stringof ~ " is not support bit width.");
+ }
+
+
+ unittest
+ {
+ foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) {
+ assert(take8from!8 (cast(Integer)0x01) == 0x01);
+ assert(take8from!16(cast(Integer)0x0123) == 0x23);
+ assert(take8from!32(cast(Integer)0x01234567) == 0x67);
+ assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef);
+ }
+ }
+}
+
+
+/*
+ * Loads $(D_PARAM T) type value from $(D_PARAM buffer).
+ *
+ * Params:
+ * buffer = the serialized contents.
+ *
+ * Returns:
+ * the Endian-converted value.
+ */
+T load16To(T)(ubyte[] buffer)
+{
+ return cast(T)(convertEndianTo!16(*cast(ushort*)buffer.ptr));
+}
+
+
+// ditto
+T load32To(T)(ubyte[] buffer)
+{
+ return cast(T)(convertEndianTo!32(*cast(uint*)buffer.ptr));
+}
+
+
+// ditto
+T load64To(T)(ubyte[] buffer)
+{
+ return cast(T)(convertEndianTo!64(*cast(ulong*)buffer.ptr));
+}
+
+
+version (D_Ddoc)
+{
+ /**
+ * Internal buffer and related operations for Unpacker
+ *
+ * Following Unpackers mixin this template. So, Unpacker can use following methods.
+ *
+ * -----
+ * //buffer image:
+ * +-------------------------------------------+
+ * | [object] | [obj | unparsed... | unused... |
+ * +-------------------------------------------+
+ * ^ offset
+ * ^ current
+ * ^ used
+ * ^ buffer.length
+ * -----
+ *
+ * This mixin template is a private.
+ */
+ mixin template InternalBuffer()
+ {
+ private:
+ ubyte[] buffer_; // internal buffer
+ size_t used_; // index that buffer cosumed
+ size_t offset_; // index that buffer parsed
+ size_t parsed_; // total size of parsed message
+ bool hasRaw_; // indicates whether Raw object has been deserialized
+
+
+ public:
+ /**
+ * Forwards to internal buffer.
+ *
+ * Returns:
+ * the reference of internal buffer.
+ */
+ @property @safe
+ nothrow ubyte[] buffer();
+
+
+ /**
+ * Fills internal buffer with $(D_PARAM target).
+ *
+ * Params:
+ * target = new serialized buffer to deserialize.
+ */
+ @safe void feed(in ubyte[] target);
+
+
+ /**
+ * Consumes buffer. This method is helper for buffer property.
+ * You must use this method if you write bytes to buffer directly.
+ *
+ * Params:
+ * size = the number of consuming.
+ */
+ @safe
+ nothrow void bufferConsumed(in size_t size);
+
+
+ /**
+ * Removes unparsed buffer.
+ */
+ @safe
+ nothrow void removeUnparsed();
+
+
+ /**
+ * Returns:
+ * the total size including unparsed buffer size.
+ */
+ @property @safe
+ nothrow size_t size() const;
+
+
+ /**
+ * Returns:
+ * the parsed size of buffer.
+ */
+ @property @safe
+ nothrow size_t parsedSize() const;
+
+
+ /**
+ * Returns:
+ * the unparsed size of buffer.
+ */
+ @property @safe
+ nothrow size_t unparsedSize() const;
+
+
+ private:
+ @safe
+ void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192);
+ }
+}
+else
+{
+ mixin template InternalBuffer()
+ {
+ private:
+ ubyte[] buffer_; // internal buffer
+ size_t used_; // index that buffer cosumed
+ size_t offset_; // index that buffer parsed
+ size_t parsed_; // total size of parsed message
+ bool hasRaw_; // indicates whether Raw object has been deserialized
+
+
+ public:
+ @property @safe
+ nothrow ubyte[] buffer()
+ {
+ return buffer_;
+ }
+
+
+ @safe
+ void feed(in ubyte[] target)
+ in
+ {
+ assert(target.length);
+ }
+ do
+ {
+ /*
+ * Expands internal buffer.
+ *
+ * Params:
+ * size = new buffer size to append.
+ */
+ void expandBuffer(in size_t size)
+ {
+ // rewinds buffer(completed deserialization)
+ if (used_ == offset_ && !hasRaw_) {
+ used_ = offset_ = 0;
+
+ if (buffer_.length < size)
+ buffer_.length = size;
+
+ return;
+ }
+
+ // deserializing state is mid-flow(buffer has non-parsed data yet)
+ auto unparsed = buffer_[offset_..used_];
+ auto restSize = buffer_.length - used_ + offset_;
+ auto newSize = size > restSize ? unparsedSize + size : buffer_.length;
+
+ if (hasRaw_) {
+ hasRaw_ = false;
+ buffer_ = new ubyte[](newSize);
+ } else {
+ buffer_.length = newSize;
+
+ // avoids overlapping copy
+ auto area = buffer_[0..unparsedSize];
+ unparsed = area.overlap(unparsed) ? unparsed.dup : unparsed;
+ }
+
+ buffer_[0..unparsedSize] = unparsed[];
+ used_ = unparsedSize;
+ offset_ = 0;
+ }
+
+ const size = target.length;
+
+ // lacks current buffer?
+ if (buffer_.length - used_ < size)
+ expandBuffer(size);
+
+ buffer_[used_..used_ + size] = target[];
+ used_ += size;
+ }
+
+
+ @safe
+ nothrow void bufferConsumed(in size_t size)
+ {
+ if (used_ + size > buffer_.length)
+ used_ = buffer_.length;
+ else
+ used_ += size;
+ }
+
+
+ @safe
+ nothrow void removeUnparsed()
+ {
+ used_ = offset_;
+ }
+
+
+ @property @safe
+ nothrow size_t size() const
+ {
+ return parsed_ - offset_ + used_;
+ }
+
+
+ @property @safe
+ nothrow size_t parsedSize() const
+ {
+ return parsed_;
+ }
+
+
+ @property @safe
+ nothrow size_t unparsedSize() const
+ {
+ return used_ - offset_;
+ }
+
+
+ private:
+ @safe
+ nothrow void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192)
+ {
+ const size = target.length;
+
+ buffer_ = new ubyte[](size > bufferSize ? size : bufferSize);
+ used_ = size;
+ buffer_[0..size] = target[];
+ }
+ }
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/exception.d b/BioD/contrib/msgpack-d/src/msgpack/exception.d
new file mode 100644
index 0000000..489c270
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/exception.d
@@ -0,0 +1,27 @@
+module msgpack.exception;
+
+@trusted:
+
+/**
+ * $(D MessagePackException) is a root Exception for MessagePack related operation.
+ */
+class MessagePackException : Exception
+{
+ pure this(string message)
+ {
+ super(message);
+ }
+}
+
+
+/**
+ * $(D UnpackException) is thrown on deserialization failure
+ */
+class UnpackException : MessagePackException
+{
+ this(string message)
+ {
+ super(message);
+ }
+}
+
diff --git a/BioD/contrib/msgpack-d/src/msgpack/package.d b/BioD/contrib/msgpack-d/src/msgpack/package.d
new file mode 100644
index 0000000..d922400
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/package.d
@@ -0,0 +1,522 @@
+// Written in the D programming language.
+
+/**
+ * MessagePack serializer and deserializer implementation.
+ *
+ * MessagePack is a binary-based serialization specification.
+ *
+ * Example:
+ * -----
+ * auto data = tuple("MessagePack!", [1, 2], true);
+ *
+ * auto serialized = pack(data);
+ *
+ * // ...
+ *
+ * typeof(data) deserialized;
+ *
+ * unpack(serialized, deserialized);
+ *
+ * assert(data == deserialized);
+ * -----
+ *
+ * See_Also:
+ * $(LINK2 http://msgpack.org/, The MessagePack Project)$(BR)
+ * $(LINK2 https://github.com/msgpack/msgpack/blob/master/spec.md, MessagePack data format)
+ *
+ * Copyright: Copyright Masahiro Nakagawa 2010-.
+ * License: Boost License 1.0.
+ * Authors: Masahiro Nakagawa
+ */
+module msgpack;
+
+public:
+
+import msgpack.common;
+import msgpack.attribute;
+import msgpack.buffer;
+import msgpack.exception;
+import msgpack.packer;
+import msgpack.unpacker;
+import msgpack.streaming_unpacker;
+import msgpack.register;
+import msgpack.value;
+
+version(Windows) {
+ pragma(lib, "WS2_32");
+}
+
+@trusted:
+
+
+/**
+ * Serializes $(D_PARAM args).
+ *
+ * Assumes single object if the length of $(D_PARAM args) == 1,
+ * otherwise array object.
+ *
+ * Params:
+ * args = the contents to serialize.
+ *
+ * Returns:
+ * a serialized data.
+ */
+ubyte[] pack(bool withFieldName = false, Args...)(in Args args)
+{
+ auto packer = Packer(withFieldName);
+
+ static if (Args.length == 1)
+ packer.pack(args[0]);
+ else
+ packer.packArray(args);
+
+ return packer.stream.data;
+}
+
+
+/**
+ * Deserializes $(D_PARAM buffer) using stream deserializer.
+ *
+ * Params:
+ * buffer = the buffer to deserialize.
+ *
+ * Returns:
+ * a $(D Unpacked) contains deserialized object.
+ *
+ * Throws:
+ * UnpackException if deserialization doesn't succeed.
+ */
+Unpacked unpack(in ubyte[] buffer)
+{
+ auto unpacker = StreamingUnpacker(buffer);
+
+ if (!unpacker.execute())
+ throw new UnpackException("Deserialization failure");
+
+ return unpacker.unpacked;
+}
+
+
+/**
+ * Deserializes $(D_PARAM buffer) using direct-conversion deserializer.
+ *
+ * Assumes single object if the length of $(D_PARAM args) == 1,
+ * otherwise array object.
+ *
+ * Params:
+ * buffer = the buffer to deserialize.
+ * args = the references of values to assign.
+ */
+void unpack(bool withFieldName = false, Args...)(in ubyte[] buffer, ref Args args)
+{
+ auto unpacker = Unpacker(buffer, buffer.length, withFieldName);
+
+ static if (Args.length == 1)
+ unpacker.unpack(args[0]);
+ else
+ unpacker.unpackArray(args);
+}
+
+
+/**
+ * Return value version
+ */
+Type unpack(Type, bool withFieldName = false)(in ubyte[] buffer)
+{
+ auto unpacker = Unpacker(buffer, buffer.length, withFieldName);
+
+ Type result;
+ unpacker.unpack(result);
+ return result;
+}
+
+
+unittest
+{
+ auto serialized = pack(false);
+
+ assert(serialized[0] == Format.FALSE);
+
+ auto deserialized = unpack(pack(1, true, "Foo"));
+
+ assert(deserialized.type == Value.Type.array);
+ assert(deserialized.via.array[0].type == Value.Type.unsigned);
+ assert(deserialized.via.array[1].type == Value.Type.boolean);
+ assert(deserialized.via.array[2].type == Value.Type.raw);
+}
+
+
+unittest
+{
+ import std.typecons;
+
+ { // stream
+ auto result = unpack(pack(false));
+
+ assert(result.via.boolean == false);
+ }
+ { // direct conversion
+ Tuple!(uint, string) result;
+ Tuple!(uint, string) test = tuple(1, "Hi!");
+
+ unpack(pack(test), result);
+ assert(result == test);
+
+ test.field[0] = 2;
+ test.field[1] = "Hey!";
+ unpack(pack(test.field[0], test.field[1]), result.field[0], result.field[1]);
+ assert(result == test);
+ }
+ { // return value direct conversion
+ Tuple!(uint, string) test = tuple(1, "Hi!");
+
+ auto data = pack(test);
+ assert(data.unpack!(Tuple!(uint, string)) == test);
+ }
+ { // serialize object as a Map
+ static class C
+ {
+ int num;
+
+ this(int num) { this.num = num; }
+ }
+
+ auto test = new C(10);
+ auto result = new C(100);
+
+ unpack!(true)(pack!(true)(test), result);
+ assert(result.num == 10, "Unpacking with field names failed");
+ }
+}
+
+
+unittest
+{
+ import std.typetuple;
+
+ // unittest for https://github.com/msgpack/msgpack-d/issues/8
+ foreach (Type; TypeTuple!(byte, short, int, long)) {
+ foreach (i; [-33, -20, -1, 0, 1, 20, 33]) {
+ Type a = cast(Type)i;
+ Type b;
+ unpack(pack(a), b);
+ assert(a == b);
+ }
+ }
+}
+
+
+unittest
+{
+ import std.typetuple;
+
+ // char types
+ foreach (Type; TypeTuple!(char, wchar, dchar)) {
+ foreach (i; [Type.init, Type.min, Type.max, cast(Type)'j']) {
+ Type a = i;
+ Type b;
+ unpack(pack(a), b);
+ assert(a == b);
+ }
+ }
+}
+
+unittest
+{
+ // ext type
+ auto result = unpack(pack(ExtValue(7, [1,2,3,4])));
+ assert(result == ExtValue(7, [1,2,3,4]));
+}
+
+unittest {
+ import std.exception: assertThrown;
+
+ struct Version {
+ int major= -1;
+ int minor = -1;
+ }
+
+ struct SubscriptionTopic {
+ string[] topicComponents;
+ }
+
+ struct SubscriptionSender
+ {
+ string hostName;
+ string biosName;
+ }
+
+ struct PubSubMessage {
+
+ enum Type {
+ publication,
+ subscribe,
+ unsubscribe,
+ }
+
+ Version version_;
+ Type type;
+ SubscriptionSender sender;
+ SubscriptionTopic topic;
+ string value;
+ }
+
+ ubyte[] bytes = [149, 146, 255, 255, 0, 146, 164, 104,
+ 111, 115, 116, 164, 98, 105, 111, 115,
+ 145, 221, 171, 105, 110, 116, 101, 114,
+ 101, 115, 116, 105, 110, 103, 165, 116,
+ 111, 112, 105, 99, 167, 112, 97, 121,
+ 108, 111, 97, 100, 158, 142, 210, 31,
+ 127, 81, 149, 125, 183, 108, 86, 17,
+ 100, 35, 168];
+
+ // should not throw OutOfMemoryError
+ assertThrown!MessagePackException(unpack!PubSubMessage(bytes));
+}
+
+
+/**
+ * Handy helper for creating MessagePackable object.
+ *
+ * toMsgpack / fromMsgpack are special methods for serialization / deserialization.
+ * This template provides those methods to struct/class.
+ *
+ * Example:
+ * -----
+ * struct S
+ * {
+ * int num; string str;
+ *
+ * // http://d.puremagic.com/issues/show_bug.cgi?id = 1099
+ * mixin MessagePackable; // all members
+ * // mixin MessagePackable!("num"); // num only
+ * }
+ * -----
+ *
+ * Defines those methods manually if you treat complex data-structure.
+ */
+mixin template MessagePackable(Members...)
+{
+ static if (Members.length == 0) {
+ /**
+ * Serializes members using $(D_PARAM packer).
+ *
+ * Params:
+ * packer = the serializer to pack.
+ */
+ void toMsgpack(Packer)(ref Packer packer, bool withFieldName = false) const
+ {
+ if (withFieldName) {
+ packer.beginMap(this.tupleof.length);
+ foreach (i, member; this.tupleof) {
+ packer.pack(getFieldName!(typeof(this), i));
+ packer.pack(member);
+ }
+ } else {
+ packer.beginArray(this.tupleof.length);
+ foreach (member; this.tupleof)
+ packer.pack(member);
+ }
+ }
+
+
+ /**
+ * Deserializes $(D MessagePack) object to members using Value.
+ *
+ * Params:
+ * value = the MessagePack value to unpack.
+ *
+ * Throws:
+ * MessagePackException if $(D_PARAM value) is not an Array type.
+ */
+ void fromMsgpack(Value value)
+ {
+ // enables if std.contracts.enforce is moved to object_.d
+ // enforceEx!MessagePackException(value.type == Value.Type.array, "Value must be Array type");
+ if (value.type != Value.Type.array)
+ throw new MessagePackException("Value must be an Array type");
+ if (value.via.array.length != this.tupleof.length)
+ throw new MessagePackException("The size of deserialized value is mismatched");
+
+ foreach (i, member; this.tupleof)
+ this.tupleof[i] = value.via.array[i].as!(typeof(member));
+ }
+
+
+ /**
+ * Deserializes $(D MessagePack) object to members using direct-conversion deserializer.
+ *
+ * Params:
+ * value = the reference to direct-conversion deserializer.
+ *
+ * Throws:
+ * MessagePackException if the size of deserialized value is mismatched.
+ */
+ void fromMsgpack(ref Unpacker unpacker)
+ {
+ auto length = unpacker.beginArray();
+ if (length != this.tupleof.length)
+ throw new MessagePackException("The size of deserialized value is mismatched");
+
+ foreach (i, member; this.tupleof)
+ unpacker.unpack(this.tupleof[i]);
+ }
+ } else {
+ /**
+ * Member selecting version of toMsgpack.
+ */
+ void toMsgpack(Packer)(ref Packer packer, bool withFieldName = false) const
+ {
+ if (withFieldName) {
+ packer.beginMap(Members.length);
+ foreach (member; Members) {
+ packer.pack(member);
+ packer.pack(mixin(member));
+ }
+ } else {
+ packer.beginArray(Members.length);
+ foreach (member; Members)
+ packer.pack(mixin(member));
+ }
+ }
+
+
+ /**
+ * Member selecting version of fromMsgpack for Value.
+ */
+ void fromMsgpack(Value value)
+ {
+ if (value.type != Value.Type.array)
+ throw new MessagePackException("Value must be an Array type");
+ if (value.via.array.length != Members.length)
+ throw new MessagePackException("The size of deserialized value is mismatched");
+
+ foreach (i, member; Members)
+ mixin(member ~ "= value.via.array[i].as!(typeof(" ~ member ~ "));");
+ }
+
+
+ /**
+ * Member selecting version of fromMsgpack for direct-converion deserializer.
+ */
+ void fromMsgpack(ref Unpacker unpacker)
+ {
+ auto length = unpacker.beginArray();
+ if (length != Members.length)
+ throw new MessagePackException("The size of deserialized value is mismatched");
+
+ foreach (member; Members)
+ unpacker.unpack(mixin(member));
+ }
+ }
+}
+
+
+unittest
+{
+ { // all members
+ /*
+ * Comment out because "src/msgpack.d(4048): Error: struct msgpack.__unittest16.S no size yet for forward reference" occurs
+ */
+ static struct S
+ {
+ uint num; string str;
+ mixin MessagePackable;
+ }
+
+ mixin DefinePacker;
+
+ S orig = S(10, "Hi!"); orig.toMsgpack(packer);
+
+ { // stream
+ auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
+
+ S result; result.fromMsgpack(unpacker.unpacked);
+
+ assert(result.num == 10);
+ assert(result.str == "Hi!");
+ }
+ { // direct conversion
+ auto unpacker = Unpacker(packer.stream.data);
+
+ S result; unpacker.unpack(result);
+
+ assert(result.num == 10);
+ assert(result.str == "Hi!");
+ }
+ }
+ { // member select
+ static class C
+ {
+ uint num; string str;
+
+ this() {}
+ this(uint n, string s) { num = n; str = s; }
+
+ mixin MessagePackable!("num");
+ }
+
+ mixin DefinePacker;
+
+ C orig = new C(10, "Hi!"); orig.toMsgpack(packer);
+
+ { // stream
+ auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
+
+ C result = new C; result.fromMsgpack(unpacker.unpacked);
+
+ assert(result.num == 10);
+ }
+ { // direct conversion
+ auto unpacker = Unpacker(packer.stream.data);
+
+ C result; unpacker.unpack(result);
+
+ assert(result.num == 10);
+ }
+ }
+}
+
+
+unittest
+{
+ import std.datetime: Clock, SysTime;
+ import msgpack.packer, msgpack.unpacker;
+
+ static struct SysTimePackProxy
+ {
+ static void serialize(ref Packer p, ref in SysTime tim)
+ {
+ p.pack(tim.toISOExtString());
+ }
+
+ static void deserialize(ref Unpacker u, ref SysTime tim)
+ {
+ string tmp;
+ u.unpack(tmp);
+ tim = SysTime.fromISOExtString(tmp);
+ }
+ }
+ static struct LogData
+ {
+ string msg;
+ string file;
+ ulong line;
+ @serializedAs!SysTimePackProxy SysTime timestamp;
+
+ this(string message, string file = __FILE__, ulong line = __LINE__)
+ {
+ this.msg = message;
+ this.file = file;
+ this.line = line;
+ this.timestamp = Clock.currTime();
+ }
+ }
+
+ /// Now we can serialize/deserialize LogData
+ LogData[] logs;
+ logs ~= LogData("MessagePack is nice!");
+ auto data = pack(logs);
+ LogData[] datas = unpack!(LogData[])(data);
+ assert(datas[0].timestamp.toString() == datas[0].timestamp.toString());
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/packer.d b/BioD/contrib/msgpack-d/src/msgpack/packer.d
new file mode 100644
index 0000000..1898818
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/packer.d
@@ -0,0 +1,1396 @@
+module msgpack.packer;
+
+import msgpack.common;
+import msgpack.attribute;
+import msgpack.exception;
+
+import std.array;
+import std.exception;
+import std.range;
+import std.stdio;
+import std.traits;
+import std.typecons;
+import std.typetuple;
+import std.container;
+
+
+/**
+ * $(D Packer) is a $(D MessagePack) serializer
+ *
+ * Example:
+ * -----
+ * auto packer = packer(Appender!(ubyte[])());
+ *
+ * packer.packArray(false, 100, 1e-10, null);
+ *
+ * stdout.rawWrite(packer.stream.data);
+ * -----
+ *
+ * NOTE:
+ * Current implementation can't deal with a circular reference.
+ * If you try to serialize a object that has circular reference, runtime raises 'Stack Overflow'.
+ */
+struct PackerImpl(Stream) if (isOutputRange!(Stream, ubyte) && isOutputRange!(Stream, ubyte[]))
+{
+ private:
+ static @system
+ {
+ alias void delegate(ref PackerImpl, void*) PackHandler;
+ PackHandler[TypeInfo] packHandlers;
+
+ public void registerHandler(T, alias Handler)()
+ {
+ packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) {
+ Handler(packer, *cast(T*)obj);
+ };
+ }
+
+ public void register(T)()
+ {
+ packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) {
+ packer.packObject(*cast(T*)obj);
+ };
+ }
+ }
+
+ enum size_t Offset = 1; // type-information offset
+
+ Stream stream_; // the stream to write
+ ubyte[Offset + RealSize] store_; // stores serialized value
+ bool withFieldName_;
+
+
+ public:
+ /**
+ * Constructs a packer with $(D_PARAM stream).
+ *
+ * Params:
+ * stream = the stream to write.
+ * withFieldName = serialize class / struct with field name
+ */
+ this(Stream stream, bool withFieldName = false)
+ {
+ stream_ = stream;
+ withFieldName_ = withFieldName;
+ }
+
+
+ /**
+ * Constructs a packer with $(D_PARAM withFieldName).
+ *
+ * Params:
+ * withFieldName = serialize class / struct with field name
+ */
+ this(bool withFieldName)
+ {
+ withFieldName_ = withFieldName;
+ }
+
+
+ /**
+ * Forwards to stream.
+ *
+ * Returns:
+ * the stream.
+ */
+ @property @safe
+ nothrow ref Stream stream()
+ {
+ return stream_;
+ }
+
+
+ /**
+ * Serializes argument and writes to stream.
+ *
+ * If the argument is the pointer type, dereferences the pointer and serializes pointed value.
+ * -----
+ * int a = 10;
+ * int* b = &b;
+ *
+ * packer.pack(b); // serializes 10, not address of a
+ * -----
+ * Serializes nil if the argument of nullable type is null.
+ *
+ * NOTE:
+ * MessagePack doesn't define $(D_KEYWORD real) type format.
+ * Don't serialize $(D_KEYWORD real) if you communicate with other languages.
+ * Transfer $(D_KEYWORD double) serialization if $(D_KEYWORD real) on your environment equals $(D_KEYWORD double).
+ *
+ * Params:
+ * value = the content to serialize.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ */
+ ref PackerImpl pack(T)(in T value) if (is(Unqual!T == bool))
+ {
+ if (value)
+ stream_.put(Format.TRUE);
+ else
+ stream_.put(Format.FALSE);
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T value) if (isUnsigned!T && !is(Unqual!T == enum))
+ {
+ // ulong < ulong is slower than uint < uint
+ static if (!is(Unqual!T == ulong)) {
+ enum Bits = T.sizeof * 8;
+
+ if (value < (1 << 8)) {
+ if (value < (1 << 7)) {
+ // fixnum
+ stream_.put(take8from!Bits(value));
+ } else {
+ // uint 8
+ store_[0] = Format.UINT8;
+ store_[1] = take8from!Bits(value);
+ stream_.put(store_[0..Offset + ubyte.sizeof]);
+ }
+ } else {
+ if (value < (1 << 16)) {
+ // uint 16
+ const temp = convertEndianTo!16(value);
+
+ store_[0] = Format.UINT16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ } else {
+ // uint 32
+ const temp = convertEndianTo!32(value);
+
+ store_[0] = Format.UINT32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ }
+ }
+ } else {
+ if (value < (1UL << 8)) {
+ if (value < (1UL << 7)) {
+ // fixnum
+ stream_.put(take8from!64(value));
+ } else {
+ // uint 8
+ store_[0] = Format.UINT8;
+ store_[1] = take8from!64(value);
+ stream_.put(store_[0..Offset + ubyte.sizeof]);
+ }
+ } else {
+ if (value < (1UL << 16)) {
+ // uint 16
+ const temp = convertEndianTo!16(value);
+
+ store_[0] = Format.UINT16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ } else if (value < (1UL << 32)){
+ // uint 32
+ const temp = convertEndianTo!32(value);
+
+ store_[0] = Format.UINT32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ } else {
+ // uint 64
+ const temp = convertEndianTo!64(value);
+
+ store_[0] = Format.UINT64;
+ *cast(ulong*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ulong.sizeof]);
+ }
+ }
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum))
+ {
+ // long < long is slower than int < int
+ static if (!is(Unqual!T == long)) {
+ enum Bits = T.sizeof * 8;
+
+ if (value < -(1 << 5)) {
+ if (value < -(1 << 15)) {
+ // int 32
+ const temp = convertEndianTo!32(value);
+
+ store_[0] = Format.INT32;
+ *cast(int*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + int.sizeof]);
+ } else if (value < -(1 << 7)) {
+ // int 16
+ const temp = convertEndianTo!16(value);
+
+ store_[0] = Format.INT16;
+ *cast(short*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + short.sizeof]);
+ } else {
+ // int 8
+ store_[0] = Format.INT8;
+ store_[1] = take8from!Bits(value);
+ stream_.put(store_[0..Offset + byte.sizeof]);
+ }
+ } else if (value < (1 << 7)) {
+ // fixnum
+ stream_.put(take8from!Bits(value));
+ } else {
+ if (value < (1 << 8)) {
+ // uint 8
+ store_[0] = Format.UINT8;
+ store_[1] = take8from!Bits(value);
+ stream_.put(store_[0..Offset + ubyte.sizeof]);
+ } else if (value < (1 << 16)) {
+ // uint 16
+ const temp = convertEndianTo!16(value);
+
+ store_[0] = Format.UINT16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ } else {
+ // uint 32
+ const temp = convertEndianTo!32(value);
+
+ store_[0] = Format.UINT32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ }
+ }
+ } else {
+ if (value < -(1L << 5)) {
+ if (value < -(1L << 15)) {
+ if (value < -(1L << 31)) {
+ // int 64
+ const temp = convertEndianTo!64(value);
+
+ store_[0] = Format.INT64;
+ *cast(long*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + long.sizeof]);
+ } else {
+ // int 32
+ const temp = convertEndianTo!32(value);
+
+ store_[0] = Format.INT32;
+ *cast(int*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + int.sizeof]);
+ }
+ } else {
+ if (value < -(1L << 7)) {
+ // int 16
+ const temp = convertEndianTo!16(value);
+
+ store_[0] = Format.INT16;
+ *cast(short*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + short.sizeof]);
+ } else {
+ // int 8
+ store_[0] = Format.INT8;
+ store_[1] = take8from!64(value);
+ stream_.put(store_[0..Offset + byte.sizeof]);
+ }
+ }
+ } else if (value < (1L << 7)) {
+ // fixnum
+ stream_.put(take8from!64(value));
+ } else {
+ if (value < (1L << 16)) {
+ if (value < (1L << 8)) {
+ // uint 8
+ store_[0] = Format.UINT8;
+ store_[1] = take8from!64(value);
+ stream_.put(store_[0..Offset + ubyte.sizeof]);
+ } else {
+ // uint 16
+ const temp = convertEndianTo!16(value);
+
+ store_[0] = Format.UINT16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ }
+ } else {
+ if (value < (1L << 32)) {
+ // uint 32
+ const temp = convertEndianTo!32(value);
+
+ store_[0] = Format.UINT32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ } else {
+ // uint 64
+ const temp = convertEndianTo!64(value);
+
+ store_[0] = Format.UINT64;
+ *cast(ulong*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ulong.sizeof]);
+ }
+ }
+ }
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T value) if (isSomeChar!T && !is(Unqual!T == enum))
+ {
+ static if (is(Unqual!T == char)) {
+ return pack(cast(ubyte)(value));
+ } else static if (is(Unqual!T == wchar)) {
+ return pack(cast(ushort)(value));
+ } else static if (is(Unqual!T == dchar)) {
+ return pack(cast(uint)(value));
+ }
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T value) if (isFloatingPoint!T && !is(Unqual!T == enum))
+ {
+ static if (is(Unqual!T == float)) {
+ const temp = convertEndianTo!32(_f(value).i);
+
+ store_[0] = Format.FLOAT;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ } else static if (is(Unqual!T == double)) {
+ const temp = convertEndianTo!64(_d(value).i);
+
+ store_[0] = Format.DOUBLE;
+ *cast(ulong*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ulong.sizeof]);
+ } else {
+ static if ((real.sizeof > double.sizeof) && EnableReal) {
+ store_[0] = Format.REAL;
+ const temp = _r(value);
+ const fraction = convertEndianTo!64(temp.fraction);
+ const exponent = convertEndianTo!16(temp.exponent);
+
+ *cast(Unqual!(typeof(fraction))*)&store_[Offset] = fraction;
+ *cast(Unqual!(typeof(exponent))*)&store_[Offset + fraction.sizeof] = exponent;
+ stream_.put(store_[0..$]);
+ } else { // Non-x86 CPUs, real type equals double type.
+ pack(cast(double)value);
+ }
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T value) if (is(Unqual!T == enum))
+ {
+ pack(cast(OriginalType!T)value);
+
+ return this;
+ }
+
+
+ /// Overload for pack(null) for 2.057 or later
+ static if (!is(typeof(null) == void*))
+ {
+ ref PackerImpl pack(T)(in T value) if (is(Unqual!T == typeof(null)))
+ {
+ return packNil();
+ }
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T value) if (isPointer!T)
+ {
+ static if (is(Unqual!T == void*)) { // for pack(null) for 2.056 or earlier
+ enforce(value is null, "Can't serialize void type");
+ stream_.put(Format.NIL);
+ } else {
+ if (value is null)
+ stream_.put(Format.NIL);
+ else
+ pack(mixin(AsteriskOf!T ~ "value"));
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T array) if ((isArray!T || isInstanceOf!(Array, T)) && !is(Unqual!T == enum))
+ {
+ alias typeof(T.init[0]) U;
+
+ if (array.empty)
+ return packNil();
+
+ // Raw bytes
+ static if (isByte!(U) || isSomeChar!(U)) {
+ ubyte[] raw = cast(ubyte[])array;
+
+ beginRaw(raw.length);
+ stream_.put(raw);
+ } else {
+ beginArray(array.length);
+ foreach (elem; array)
+ pack(elem);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(T)(in T array) if (isAssociativeArray!T)
+ {
+ if (array is null)
+ return packNil();
+
+ beginMap(array.length);
+ foreach (key, value; array) {
+ pack(key);
+ pack(value);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl pack(Types...)(auto ref const Types objects) if (Types.length > 1)
+ {
+ foreach (i, T; Types)
+ pack(objects[i]);
+
+ return this;
+ }
+
+
+ /**
+ * Serializes $(D_PARAM object) and writes to stream.
+ *
+ * Calling $(D toMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D toMsgpack) method. $(D toMsgpack) signature is:
+ * -----
+ * void toMsgpack(Packer)(ref Packer packer) const
+ * -----
+ * This method serializes all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D toMsgpack).
+ *
+ * An object that doesn't implement $(D toMsgpack) is serialized to Array type.
+ * -----
+ * packer.pack(tuple(true, 1, "Hi!")) // -> '[true, 1, "Hi!"]', not 'ture, 1, "Hi!"'
+ *
+ * struct Foo
+ * {
+ * int num = 10;
+ * string msg = "D!";
+ * }
+ * packer.pack(Foo()); // -> '[10, "D!"]'
+ *
+ * class Base
+ * {
+ * bool flag = true;
+ * }
+ * class Derived : Base
+ * {
+ * double = 0.5f;
+ * }
+ * packer.pack(new Derived()); // -> '[true, 0.5f]'
+ * -----
+ *
+ * Params:
+ * object = the content to serialize.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ */
+ ref PackerImpl pack(T)(in T object) if (is(Unqual!T == class))
+ {
+ if (object is null)
+ return packNil();
+
+ static if (hasMember!(T, "toMsgpack"))
+ {
+ static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) {
+ object.toMsgpack(this, withFieldName_);
+ } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible
+ object.toMsgpack(this);
+ } else {
+ static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
+ }
+ } else {
+ if (auto handler = object.classinfo in packHandlers) {
+ (*handler)(this, cast(void*)&object);
+ return this;
+ }
+ if (T.classinfo !is object.classinfo) {
+ throw new MessagePackException("Can't pack derived class through reference to base class.");
+ }
+
+ packObject!(T)(object);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ @trusted
+ ref PackerImpl pack(T)(auto ref T object) if (is(Unqual!T == struct) &&
+ !isInstanceOf!(Array, T) &&
+ !is(Unqual!T == ExtValue))
+ {
+ static if (hasMember!(T, "toMsgpack"))
+ {
+ static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) {
+ object.toMsgpack(this, withFieldName_);
+ } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible
+ object.toMsgpack(this);
+ } else {
+ static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
+ }
+ } else static if (isTuple!T) {
+ beginArray(object.field.length);
+ foreach (f; object.field)
+ pack(f);
+ } else { // simple struct
+ if (auto handler = typeid(Unqual!T) in packHandlers) {
+ (*handler)(this, cast(void*)&object);
+ return this;
+ }
+
+ immutable memberNum = SerializingMemberNumbers!(T);
+ if (withFieldName_)
+ beginMap(memberNum);
+ else
+ beginArray(memberNum);
+
+ if (withFieldName_) {
+ foreach (i, f; object.tupleof) {
+ static if (isPackedField!(T.tupleof[i])) {
+ pack(getFieldName!(T, i));
+ static if (hasSerializedAs!(T.tupleof[i])) {
+ alias Proxy = getSerializedAs!(T.tupleof[i]);
+ Proxy.serialize(this, f);
+ } else static if (__traits(compiles, { pack(f); }))
+ pack(f);
+ }
+ }
+ } else {
+ foreach (i, f; object.tupleof) {
+ static if (isPackedField!(T.tupleof[i])) {
+ static if (hasSerializedAs!(T.tupleof[i])) {
+ alias Proxy = getSerializedAs!(T.tupleof[i]);
+ Proxy.serialize(this, f);
+ } else static if (__traits(compiles, { pack(f); }))
+ pack(f);
+ }
+ }
+ }
+ }
+
+ return this;
+ }
+
+
+ void packObject(T)(in T object) if (is(Unqual!T == class))
+ {
+ alias SerializingClasses!(T) Classes;
+
+ immutable memberNum = SerializingMemberNumbers!(Classes);
+ if (withFieldName_)
+ beginMap(memberNum);
+ else
+ beginArray(memberNum);
+
+ foreach (Class; Classes) {
+ Class obj = cast(Class)object;
+ if (withFieldName_) {
+ foreach (i, f ; obj.tupleof) {
+ static if (isPackedField!(Class.tupleof[i])) {
+ pack(getFieldName!(Class, i));
+ static if (hasSerializedAs!(T.tupleof[i])) {
+ alias Proxy = getSerializedAs!(T.tupleof[i]);
+ Proxy.serialize(this, f);
+ } else {
+ pack(f);
+ }
+ }
+ }
+ } else {
+ foreach (i, f ; obj.tupleof) {
+ static if (isPackedField!(Class.tupleof[i])) {
+ static if (hasSerializedAs!(T.tupleof[i])) {
+ alias Proxy = getSerializedAs!(T.tupleof[i]);
+ Proxy.serialize(this, f);
+ } else {
+ pack(f);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Serializes the arguments as container to stream.
+ *
+ * -----
+ * packer.packArray(true, 1); // -> [true, 1]
+ * packer.packMap("Hi", 100); // -> ["Hi":100]
+ * -----
+ *
+ * In packMap, the number of arguments must be even.
+ *
+ * Params:
+ * objects = the contents to serialize.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ */
+ ref PackerImpl packArray(Types...)(auto ref const Types objects)
+ {
+ beginArray(Types.length);
+ foreach (i, T; Types)
+ pack(objects[i]);
+ //pack(objects); // slow :(
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl packMap(Types...)(auto ref const Types objects)
+ {
+ static assert(Types.length % 2 == 0, "The number of arguments must be even");
+
+ beginMap(Types.length / 2);
+ foreach (i, T; Types)
+ pack(objects[i]);
+
+ return this;
+ }
+
+ /**
+ * Packs $(D data) as an extended value of $(D type).
+ *
+ * ----
+ * packer.packExt(3, bytes);
+ * ----
+ *
+ * $(D type) must be a signed byte 0-127.
+ *
+ * Params:
+ * type = the application-defined type for the data
+ * data = an array of bytes
+ *
+ * Returns:
+ * seld, i.e. for method chaining.
+ */
+ ref PackerImpl pack(T)(auto ref const T data) if (is(Unqual!T == ExtValue))
+ {
+ packExt(data.type, data.data);
+ return this;
+ }
+
+ /**
+ * Packs $(D data) as an extended value of $(D type).
+ *
+ * ----
+ * packer.packExt(3, bytes);
+ * ----
+ *
+ * $(D type) must be a signed byte 0-127.
+ *
+ * Params:
+ * type = the application-defined type for the data
+ * data = an array of bytes
+ *
+ * Returns:
+ * seld, i.e. for method chaining.
+ */
+ ref PackerImpl packExt(in byte type, const ubyte[] data) return
+ {
+ ref PackerImpl packExtFixed(int fmt)
+ {
+ store_[0] = cast(ubyte)fmt;
+ store_[1] = type;
+ stream_.put(store_[0 .. 2]);
+ stream_.put(data);
+ return this;
+ }
+
+ // Try packing to a fixed-length type
+ if (data.length == 1)
+ return packExtFixed(Format.EXT + 0);
+ else if (data.length == 2)
+ return packExtFixed(Format.EXT + 1);
+ else if (data.length == 4)
+ return packExtFixed(Format.EXT + 2);
+ else if (data.length == 8)
+ return packExtFixed(Format.EXT + 3);
+ else if (data.length == 16)
+ return packExtFixed(Format.EXT + 4);
+
+ int typeByte = void;
+ if (data.length <= (2^^8)-1)
+ {
+ store_[0] = Format.EXT8;
+ store_[1] = cast(ubyte)data.length;
+ typeByte = 2;
+
+ } else if (data.length <= (2^^16)-1) {
+ store_[0] = Format.EXT16;
+ const temp = convertEndianTo!16(data.length);
+ *cast(ushort*)&store_[Offset] = temp;
+ typeByte = 3;
+ } else if (data.length <= (2^^32)-1) {
+ store_[0] = Format.EXT32;
+ const temp = convertEndianTo!32(data.length);
+ *cast(uint*)&store_[Offset] = temp;
+ typeByte = 5;
+ } else
+ throw new MessagePackException("Data too large to pack as EXT");
+
+ store_[typeByte] = type;
+ stream_.put(store_[0..typeByte+1]);
+ stream_.put(data);
+
+ return this;
+ }
+
+ /*
+ * Serializes raw type-information to stream for binary type.
+ */
+ void beginRaw(in size_t length)
+ {
+ import std.conv : text;
+
+ if (length < 32) {
+ const ubyte temp = Format.RAW | cast(ubyte)length;
+ stream_.put(take8from(temp));
+ } else if (length < 65536) {
+ const temp = convertEndianTo!16(length);
+
+ store_[0] = Format.RAW16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ } else {
+ if (length > 0xffffffff)
+ throw new MessagePackException(text("size of raw is too long to pack: ", length, " bytes should be <= ", 0xffffffff));
+
+ const temp = convertEndianTo!32(length);
+
+ store_[0] = Format.RAW32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ }
+ }
+
+ /**
+ * Serializes the type-information to stream.
+ *
+ * These methods don't serialize contents.
+ * You need to call pack method to serialize contents at your own risk.
+ * -----
+ * packer.beginArray(3).pack(true, 1); // -> [true, 1,
+ *
+ * // other operation
+ *
+ * packer.pack("Hi!"); // -> [true, 1, "Hi!"]
+ * -----
+ *
+ * Params:
+ * length = the length of container.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ */
+ ref PackerImpl beginArray(in size_t length) return
+ {
+ if (length < 16) {
+ const ubyte temp = Format.ARRAY | cast(ubyte)length;
+ stream_.put(take8from(temp));
+ } else if (length < 65536) {
+ const temp = convertEndianTo!16(length);
+
+ store_[0] = Format.ARRAY16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ } else {
+ const temp = convertEndianTo!32(length);
+
+ store_[0] = Format.ARRAY32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref PackerImpl beginMap(in size_t length) return
+ {
+ if (length < 16) {
+ const ubyte temp = Format.MAP | cast(ubyte)length;
+ stream_.put(take8from(temp));
+ } else if (length < 65536) {
+ const temp = convertEndianTo!16(length);
+
+ store_[0] = Format.MAP16;
+ *cast(ushort*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + ushort.sizeof]);
+ } else {
+ const temp = convertEndianTo!32(length);
+
+ store_[0] = Format.MAP32;
+ *cast(uint*)&store_[Offset] = temp;
+ stream_.put(store_[0..Offset + uint.sizeof]);
+ }
+
+ return this;
+ }
+
+
+ private:
+ /*
+ * Serializes the nil value.
+ */
+ ref PackerImpl packNil() return
+ {
+ stream_.put(Format.NIL);
+ return this;
+ }
+}
+
+
+/// Default serializer
+alias PackerImpl!(Appender!(ubyte[])) Packer; // should be pure struct?
+
+
+/**
+ * Helper for $(D Packer) construction.
+ *
+ * Params:
+ * stream = the stream to write.
+ * withFieldName = serialize class / struct with field name
+ *
+ * Returns:
+ * a $(D Packer) object instantiated and initialized according to the arguments.
+ */
+PackerImpl!(Stream) packer(Stream)(Stream stream, bool withFieldName = false)
+{
+ return typeof(return)(stream, withFieldName);
+}
+
+
+version(unittest)
+{
+ package import std.file, core.stdc.string;
+
+ package mixin template DefinePacker()
+ {
+ Packer packer;
+ }
+
+ package mixin template DefineDictionalPacker()
+ {
+ Packer packer = Packer(false);
+ }
+}
+
+
+unittest
+{
+ { // unique value
+ mixin DefinePacker;
+
+ ubyte[] result = [Format.NIL, Format.TRUE, Format.FALSE];
+
+ packer.pack(null, true, false);
+ foreach (i, value; packer.stream.data)
+ assert(value == result[i]);
+ }
+ { // uint *
+ static struct UTest { ubyte format; ulong value; }
+
+ enum : ulong { A = ubyte.max, B = ushort.max, C = uint.max, D = ulong.max }
+
+ static UTest[][] utests = [
+ [{Format.UINT8, A}],
+ [{Format.UINT8, A}, {Format.UINT16, B}],
+ [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}],
+ [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}, {Format.UINT64, D}],
+ ];
+
+ foreach (I, T; TypeTuple!(ubyte, ushort, uint, ulong)) {
+ foreach (i, test; utests[I]) {
+ mixin DefinePacker;
+
+ packer.pack(cast(T)test.value);
+ assert(packer.stream.data[0] == test.format);
+
+ switch (i) {
+ case 0:
+ auto answer = take8from!(T.sizeof * 8)(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, ubyte.sizeof) == 0);
+ break;
+ case 1:
+ auto answer = convertEndianTo!16(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0);
+ break;
+ case 2:
+ auto answer = convertEndianTo!32(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0);
+ break;
+ default:
+ auto answer = convertEndianTo!64(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0);
+ }
+ }
+ }
+ }
+ { // int *
+ static struct STest { ubyte format; long value; }
+
+ enum : long { A = byte.min, B = short.min, C = int.min, D = long.min }
+
+ static STest[][] stests = [
+ [{Format.INT8, A}],
+ [{Format.INT8, A}, {Format.INT16, B}],
+ [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}],
+ [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}, {Format.INT64, D}],
+ ];
+
+ foreach (I, T; TypeTuple!(byte, short, int, long)) {
+ foreach (i, test; stests[I]) {
+ mixin DefinePacker;
+
+ packer.pack(cast(T)test.value);
+ assert(packer.stream.data[0] == test.format);
+
+ switch (i) {
+ case 0:
+ auto answer = take8from!(T.sizeof * 8)(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, byte.sizeof) == 0);
+ break;
+ case 1:
+ auto answer = convertEndianTo!16(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, short.sizeof) == 0);
+ break;
+ case 2:
+ auto answer = convertEndianTo!32(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, int.sizeof) == 0);
+ break;
+ default:
+ auto answer = convertEndianTo!64(test.value);
+ assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0);
+ }
+ }
+ }
+ }
+ { // fload, double
+ static if ((real.sizeof == double.sizeof) || !EnableReal)
+ {
+ alias TypeTuple!(float, double, double) FloatingTypes;
+ static struct FTest { ubyte format; double value; }
+
+ static FTest[] ftests = [
+ {Format.FLOAT, float.min_normal},
+ {Format.DOUBLE, double.max},
+ {Format.DOUBLE, double.max},
+ ];
+ }
+ else
+ {
+ alias TypeTuple!(float, double, real) FloatingTypes;
+ static struct FTest { ubyte format; real value; }
+
+ static FTest[] ftests = [
+ {Format.FLOAT, float.min_normal},
+ {Format.DOUBLE, double.max},
+ {Format.REAL, real.max},
+ ];
+ }
+
+ foreach (I, T; FloatingTypes) {
+ mixin DefinePacker;
+
+ packer.pack(cast(T)ftests[I].value);
+ assert(packer.stream.data[0] == ftests[I].format);
+
+ switch (I) {
+ case 0:
+ const answer = convertEndianTo!32(_f(cast(T)ftests[I].value).i);
+ assert(memcmp(&packer.stream.data[1], &answer, float.sizeof) == 0);
+ break;
+ case 1:
+ const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i);
+ assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
+ break;
+ default:
+ static if (EnableReal)
+ {
+ const t = _r(cast(T)ftests[I].value);
+ const f = convertEndianTo!64(t.fraction);
+ const e = convertEndianTo!16(t.exponent);
+ assert(memcmp(&packer.stream.data[1], &f, f.sizeof) == 0);
+ assert(memcmp(&packer.stream.data[1 + f.sizeof], &e, e.sizeof) == 0);
+ }
+ else
+ {
+ const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i);
+ assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
+ }
+ }
+ }
+ }
+ { // pointer
+ static struct PTest
+ {
+ ubyte format;
+
+ union
+ {
+ ulong* p0;
+ long* p1;
+ double* p2;
+ }
+ }
+
+ PTest[] ptests = [PTest(Format.UINT64), PTest(Format.INT64), PTest(Format.DOUBLE)];
+
+ ulong v0 = ulong.max;
+ long v1 = long.min;
+ double v2 = double.max;
+
+ foreach (I, Index; TypeTuple!("0", "1", "2")) {
+ mixin DefinePacker;
+
+ mixin("ptests[I].p" ~ Index ~ " = &v" ~ Index ~ ";");
+
+ packer.pack(mixin("ptests[I].p" ~ Index));
+ assert(packer.stream.data[0] == ptests[I].format);
+
+ switch (I) {
+ case 0:
+ auto answer = convertEndianTo!64(*ptests[I].p0);
+ assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0);
+ break;
+ case 1:
+ auto answer = convertEndianTo!64(*ptests[I].p1);
+ assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0);
+ break;
+ default:
+ const answer = convertEndianTo!64(_d(*ptests[I].p2).i);
+ assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
+ }
+ }
+ }
+ { // enum
+ enum E : ubyte { A = ubyte.max }
+
+ mixin DefinePacker; E e = E.A;
+
+ packer.pack(e);
+ assert(packer.stream.data[0] == Format.UINT8);
+
+ auto answer = E.A;
+ assert(memcmp(&packer.stream.data[1], &answer, (OriginalType!E).sizeof) == 0);
+ }
+ { // enum with string
+ enum E2 : string { A = "test" }
+
+ mixin DefinePacker; E2 e = E2.A;
+
+ packer.pack(e);
+ assert(packer.stream.data[0] == (Format.RAW | 0x04));
+ }
+ { // container
+ static struct CTest { ubyte format; size_t value; }
+
+ enum : ulong { A = 16 / 2, B = ushort.max, C = uint.max }
+
+ static CTest[][] ctests = [
+ [{Format.ARRAY | A, Format.ARRAY | A}, {Format.ARRAY16, B}, {Format.ARRAY32, C}],
+ [{Format.MAP | A, Format.MAP | A}, {Format.MAP16, B}, {Format.MAP32, C}],
+ [{Format.RAW | A, Format.RAW | A}, {Format.RAW16, B}, {Format.RAW32, C}],
+ ];
+
+ foreach (I, Name; TypeTuple!("Array", "Map", "Raw")) {
+ auto test = ctests[I];
+
+ foreach (i, T; TypeTuple!(ubyte, ushort, uint)) {
+ mixin DefinePacker;
+ mixin("packer.begin" ~ Name ~ "(i ? test[i].value : A);");
+
+ assert(packer.stream.data[0] == test[i].format);
+
+ switch (i) {
+ case 0:
+ auto answer = take8from(test[i].value);
+ assert(memcmp(&packer.stream.data[0], &answer, ubyte.sizeof) == 0);
+ break;
+ case 1:
+ auto answer = convertEndianTo!16(test[i].value);
+ assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0);
+ break;
+ default:
+ auto answer = convertEndianTo!32(test[i].value);
+ assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0);
+ }
+ }
+ }
+ }
+
+ version (X86_64) // can't create a long enough array to trigger this on x86
+ { // larger spec size for string / binary
+ mixin DefinePacker;
+
+ try {
+ // using malloc because - hopefully - this means we don't
+ // actually physically allocate such a huge amount of memory
+ import core.stdc.stdlib;
+ auto len = 0xffffffffUL + 1;
+ auto bins = (cast(byte*)malloc(len))[0 .. len];
+ assert(bins);
+ scope(exit) free(bins.ptr);
+ packer.pack(bins);
+ assert(false); //check it wasn't allowed
+ } catch (MessagePackException e) {
+ }
+ }
+ { // user defined
+ {
+ static struct S
+ {
+ uint num = uint.max;
+
+ void toMsgpack(P)(ref P p) const { p.packArray(num); }
+ }
+
+ mixin DefinePacker; S test;
+
+ packer.pack(test);
+
+ assert(packer.stream.data[0] == (Format.ARRAY | 1));
+ assert(packer.stream.data[1] == Format.UINT32);
+ assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0);
+ }
+ {
+ mixin DefinePacker; auto test = tuple(true, false, uint.max);
+
+ packer.pack(test);
+
+ assert(packer.stream.data[0] == (Format.ARRAY | 3));
+ assert(packer.stream.data[1] == Format.TRUE);
+ assert(packer.stream.data[2] == Format.FALSE);
+ assert(packer.stream.data[3] == Format.UINT32);
+ assert(memcmp(&packer.stream.data[4], &test.field[2], uint.sizeof) == 0);
+ }
+ {
+ static class C
+ {
+ uint num;
+
+ this(uint n) { num = n; }
+
+ void toMsgpack(P)(ref P p) const { p.packArray(num); }
+ }
+
+ mixin DefinePacker; C test = new C(ushort.max);
+
+ packer.pack(test);
+
+ assert(packer.stream.data[0] == (Format.ARRAY | 1));
+ assert(packer.stream.data[1] == Format.UINT16);
+ assert(memcmp(&packer.stream.data[2], &test.num, ushort.sizeof) == 0);
+ }
+ }
+ { // simple struct and class
+ {
+ static struct Simple
+ {
+ uint num = uint.max;
+ }
+
+ static struct SimpleWithNonPacked1
+ {
+ uint num = uint.max;
+ @nonPacked string str = "ignored";
+ }
+
+ static struct SimpleWithNonPacked2
+ {
+ @nonPacked string str = "ignored";
+ uint num = uint.max;
+ }
+
+ static struct SimpleWithSkippedTypes
+ {
+ int function(int) fn;
+ int delegate(int) dg;
+ uint num = uint.max;
+ }
+
+ foreach (Type; TypeTuple!(Simple, SimpleWithNonPacked1, SimpleWithNonPacked2, SimpleWithSkippedTypes)) {
+ mixin DefinePacker;
+
+ Type test;
+ packer.pack(test);
+
+ assert(packer.stream.data[0] == (Format.ARRAY | 1));
+ assert(packer.stream.data[1] == Format.UINT32);
+ assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0);
+ }
+ }
+
+ static class SimpleA
+ {
+ bool flag = true;
+ }
+
+ static class SimpleB : SimpleA
+ {
+ ubyte type = 100;
+ }
+
+ static class SimpleC : SimpleB
+ {
+ uint num = uint.max;
+ }
+
+ static class SimpleCWithNonPacked1 : SimpleB
+ {
+ uint num = uint.max;
+ @nonPacked string str = "ignored";
+ }
+
+ static class SimpleCWithNonPacked2 : SimpleB
+ {
+ @nonPacked string str = "ignored";
+ uint num = uint.max;
+ }
+
+ static class SimpleCWithSkippedTypes : SimpleB
+ {
+ uint num = uint.max;
+ int function(int) fn;
+ int delegate(int) dg;
+ }
+
+ { // from derived class
+ foreach (Type; TypeTuple!(SimpleC, SimpleCWithNonPacked1, SimpleCWithNonPacked2, SimpleCWithSkippedTypes)) {
+ mixin DefinePacker;
+
+ Type test = new Type();
+ packer.pack(test);
+
+ assert(packer.stream.data[0] == (Format.ARRAY | 3));
+ assert(packer.stream.data[1] == Format.TRUE);
+ assert(packer.stream.data[2] == 100);
+ assert(packer.stream.data[3] == Format.UINT32);
+ assert(memcmp(&packer.stream.data[4], &test.num, uint.sizeof) == 0);
+ }
+ }
+ { // from base class
+ mixin DefinePacker; SimpleB test = new SimpleC();
+
+ try {
+ packer.pack(test);
+ assert(false);
+ } catch (Exception e) { }
+ }
+ }
+
+ // ext types
+ {
+ byte type = 7; // an arbitrary type value
+
+ // fixexts
+ {
+ ubyte[16] data;
+ data[] = 1;
+ foreach (L; TypeTuple!(1, 2, 4, 8, 16))
+ {
+ mixin DefinePacker;
+ packer.pack(ExtValue(type, data[0 .. L]));
+
+ // format, type, data
+ assert(packer.stream.data.length == 2 + L);
+ const l = 2 ^^ (packer.stream.data[0] - Format.EXT);
+ assert(l == L);
+ assert(packer.stream.data[1] == type);
+ assert(packer.stream.data[2 .. 2+l] == data[0 .. L]);
+ }
+ }
+
+ // ext8
+ {
+ foreach (L; TypeTuple!(3, 7, 255))
+ {
+ ubyte[] data = new ubyte[](L);
+ data[] = 1;
+
+ mixin DefinePacker;
+ packer.pack(ExtValue(type, data[0 .. L]));
+
+ // format, length, type, data
+ assert(packer.stream.data.length == 3 + L);
+ assert(packer.stream.data[0] == Format.EXT8);
+ assert(packer.stream.data[1] == L);
+ assert(packer.stream.data[2] == type);
+ assert(packer.stream.data[3 .. 3 + L] == data);
+ }
+ }
+
+ // ext16
+ {
+ foreach (L; TypeTuple!(256, (2^^16)-1))
+ {
+ ubyte[] data = new ubyte[](L);
+ data[] = 1;
+
+ mixin DefinePacker;
+ packer.pack(ExtValue(type, data[0 .. L]));
+
+ // format, length, type, data
+ import std.conv : text;
+ assert(packer.stream.data.length == 4 + L, text(packer.stream.data.length));
+ assert(packer.stream.data[0] == Format.EXT16);
+
+ ushort l = convertEndianTo!16(L);
+ assert(memcmp(&packer.stream.data[1], &l, ushort.sizeof) == 0);
+ assert(packer.stream.data[3] == type);
+ assert(packer.stream.data[4 .. 4 + L] == data);
+ }
+ }
+
+ // ext32
+ {
+ foreach (L; TypeTuple!(2^^16, 2^^17))
+ {
+ ubyte[] data = new ubyte[](L);
+ data[] = 1;
+
+ mixin DefinePacker;
+ packer.pack(ExtValue(type, data[0 .. L]));
+
+ // format, length, type, data
+ import std.conv : text;
+ assert(packer.stream.data.length == 6 + L, text(packer.stream.data.length));
+ assert(packer.stream.data[0] == Format.EXT32);
+
+ uint l = convertEndianTo!32(L);
+ assert(memcmp(&packer.stream.data[1], &l, uint.sizeof) == 0);
+ assert(packer.stream.data[5] == type);
+ assert(packer.stream.data[6 .. 6 + L] == data);
+ }
+ }
+ }
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/register.d b/BioD/contrib/msgpack-d/src/msgpack/register.d
new file mode 100644
index 0000000..f976ae0
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/register.d
@@ -0,0 +1,49 @@
+module msgpack.register;
+
+import msgpack.packer;
+import msgpack.unpacker;
+
+import std.array;
+
+
+/**
+ * Register a serialization handler for $(D_PARAM T) type
+ *
+ * Example:
+ * -----
+ * registerPackHandler!(Foo, fooPackHandler);
+ * -----
+ */
+void registerPackHandler(T, alias Handler, Stream = Appender!(ubyte[]))()
+{
+ PackerImpl!(Stream).registerHandler!(T, Handler);
+}
+
+
+/**
+ * Register a deserialization handler for $(D_PARAM T) type
+ *
+ * Example:
+ * -----
+ * registerUnackHandler!(Foo, fooUnackHandler);
+ * -----
+ */
+void registerUnpackHandler(T, alias Handler)()
+{
+ Unpacker.registerHandler!(T, Handler);
+}
+
+
+/**
+ * Register derived class for (de)serialization
+ *
+ * Example:
+ * -----
+ * registerClass!(DerivedClass);
+ * -----
+ */
+void registerClass(T, Stream = Appender!(ubyte[]))()
+{
+ PackerImpl!(Stream).register!(T);
+ Unpacker.register!(T);
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/streaming_unpacker.d b/BioD/contrib/msgpack-d/src/msgpack/streaming_unpacker.d
new file mode 100644
index 0000000..2743a66
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/streaming_unpacker.d
@@ -0,0 +1,914 @@
+module msgpack.streaming_unpacker;
+
+import msgpack.common;
+import msgpack.attribute;
+import msgpack.exception;
+import msgpack.value;
+
+import std.array;
+import std.exception;
+import std.range;
+import std.stdio;
+import std.traits;
+import std.typecons;
+import std.typetuple;
+import std.container;
+
+
+/**
+ * $(D Unpacked) is a $(D Range) wrapper for stream deserialization result
+ */
+struct Unpacked
+{
+ import std.conv : text;
+
+ Value value; /// deserialized value
+
+ alias value this;
+
+
+ /**
+ * Constructs a $(D Unpacked) with argument.
+ *
+ * Params:
+ * value = a deserialized value.
+ */
+ @safe
+ this(ref Value value)
+ {
+ this.value = value;
+ }
+
+
+ /**
+ * InputRange primitive operation that checks iteration state.
+ *
+ * Returns:
+ * true if there are no more elements to be iterated.
+ */
+ @property @trusted
+ nothrow bool empty() const // std.array.empty isn't nothrow function
+ {
+ return (value.type == Value.Type.array) && !value.via.array.length;
+ }
+
+
+ /**
+ * Range primitive operation that returns the length of the range.
+ *
+ * Returns:
+ * the number of values.
+ */
+ @property @trusted
+ size_t length()
+ {
+ debug enforce(value.type == Value.Type.array, "lenght is called with non array object. type = " ~ text(value.type));
+ return value.via.array.length;
+ }
+
+
+ /**
+ * InputRange primitive operation that returns the currently iterated element.
+ *
+ * Returns:
+ * the deserialized $(D Value).
+ */
+ @property @trusted
+ ref Value front()
+ {
+ debug enforce(value.type == Value.Type.array, "front is called with non array object. type = " ~ text(value.type));
+ return value.via.array.front;
+ }
+
+
+ /**
+ * InputRange primitive operation that advances the range to its next element.
+ */
+ @trusted
+ void popFront()
+ {
+ debug enforce(value.type == Value.Type.array, "popFront is called with non array object. type = " ~ text(value.type));
+ value.via.array.popFront();
+ }
+
+ /**
+ * RandomAccessRange primitive operation.
+ *
+ * Returns:
+ * the deserialized $(D Value) at $(D_PARAM n) position.
+ */
+ @trusted
+ ref Value opIndex(size_t n)
+ {
+ debug enforce(value.type == Value.Type.array, "opIndex is called with non array object. type = " ~ text(value.type));
+ return value.via.array[n];
+ }
+
+ /**
+ * Returns a slice of the range.
+ *
+ * Paramas:
+ * from = the start point of slicing.
+ * to = the end point of slicing.
+ *
+ * Returns:
+ * the slice of Values.
+ */
+ @trusted
+ Value[] opSlice(size_t from, size_t to)
+ {
+ debug enforce(value.type == Value.Type.array, "opSlice is called with non array object. type = " ~ text(value.type));
+ return value.via.array[from..to];
+ }
+
+ /**
+ * Range primitive operation that returns the snapshot.
+ *
+ * Returns:
+ * the snapshot of this Value.
+ */
+ @property @safe
+ Unpacked save()
+ {
+ return Unpacked(value);
+ }
+}
+
+
+unittest
+{
+ static assert(isForwardRange!Unpacked);
+ static assert(hasLength!Unpacked);
+}
+
+
+/**
+ * This $(D StreamingUnpacker) is a $(D MessagePack) streaming deserializer
+ *
+ * This implementation enables you to load multiple objects from a stream(like network).
+ *
+ * Example:
+ * -----
+ * ...
+ * auto unpacker = StreamingUnpacker(serializedData);
+ * ...
+ *
+ * // appends new data to buffer if pre execute() call didn't finish deserialization.
+ * unpacker.feed(newSerializedData);
+ *
+ * while (unpacker.execute()) {
+ * foreach (obj; unpacker.purge()) {
+ * // do stuff (obj is a Value)
+ * }
+ * }
+ *
+ * if (unpacker.size)
+ * throw new Exception("Message is too large");
+ * -----
+ */
+struct StreamingUnpacker
+{
+ private:
+ /*
+ * Context state of deserialization
+ */
+ enum State
+ {
+ HEADER = 0x00,
+
+ BIN8 = 0x04,
+ BIN16,
+ BIN32,
+
+ // Floating point, Unsigned, Signed interger (== header & 0x03)
+ FLOAT = 0x0a,
+ DOUBLE,
+ UINT8,
+ UINT16,
+ UINT32,
+ UINT64,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+
+ // Container (== header & 0x01)
+ STR8 = 0x19,
+ RAW16 = 0x1a,
+ RAW32,
+ ARRAY16,
+ ARRAY36,
+ MAP16,
+ MAP32,
+ RAW,
+
+ // EXT family
+ EXT8,
+ EXT16,
+ EXT32,
+ EXT_DATA,
+
+ // D-specific type
+ REAL
+ }
+
+
+ /*
+ * Element type of container
+ */
+ enum ContainerElement
+ {
+ ARRAY_ITEM,
+ MAP_KEY,
+ MAP_VALUE
+ }
+
+
+ /*
+ * Internal stack context
+ */
+ static struct Context
+ {
+ static struct Container
+ {
+ ContainerElement type; // value container type
+ Value value; // current value
+ Value key; // for map value
+ size_t count; // container length
+ }
+
+ State state; // current state of deserialization
+ size_t trail; // current deserializing size
+ size_t top; // current index of stack
+ Container[] stack; // storing values
+ }
+
+ Context context_; // stack environment for streaming deserialization
+
+ mixin InternalBuffer;
+
+
+ public:
+ /**
+ * Constructs a $(D StreamingUnpacker).
+ *
+ * Params:
+ * target = byte buffer to deserialize
+ * bufferSize = size limit of buffer size
+ */
+ @safe
+ this(in ubyte[] target, in size_t bufferSize = 8192)
+ {
+ initializeBuffer(target, bufferSize);
+ initializeContext();
+ }
+
+
+ /**
+ * Forwards to deserialized object.
+ *
+ * Returns:
+ * the $(D Unpacked) object contains deserialized value.
+ */
+ @property @safe
+ Unpacked unpacked()
+ {
+ return Unpacked(context_.stack[0].value);
+ }
+
+
+ /**
+ * Clears some states for next deserialization.
+ */
+ @safe
+ nothrow void clear()
+ {
+ initializeContext();
+
+ parsed_ = 0;
+ }
+
+
+ /**
+ * Convenient method for unpacking and clearing states.
+ *
+ * Example:
+ * -----
+ * foreach (obj; unpacker.purge()) {
+ * // do stuff
+ * }
+ * -----
+ * is equivalent to
+ * -----
+ * foreach (obj; unpacker.unpacked) {
+ * // do stuff
+ * }
+ * unpacker.clear();
+ * -----
+ *
+ * Returns:
+ * the $(D Unpacked) object contains deserialized value.
+ */
+ @safe
+ Unpacked purge()
+ {
+ auto result = Unpacked(context_.stack[0].value);
+
+ clear();
+
+ return result;
+ }
+
+
+ /**
+ * Executes deserialization.
+ *
+ * Returns:
+ * true if deserialization has been completed, otherwise false.
+ *
+ * Throws:
+ * $(D UnpackException) when parse error occurs.
+ */
+ bool execute()
+ {
+ /*
+ * Current implementation is very dirty(goto! goto!! goto!!!).
+ * This Complexity for performance(avoid function call).
+ */
+
+ bool ret;
+ size_t cur = offset_;
+ Value obj;
+
+ // restores before state
+ auto state = context_.state;
+ auto trail = context_.trail;
+ auto top = context_.top;
+ auto stack = &context_.stack;
+
+ /*
+ * Helper for container deserialization
+ */
+ void startContainer(string Type)(ContainerElement type, size_t length)
+ {
+ mixin("callback" ~ Type ~ "((*stack)[top].value, length);");
+
+ (*stack)[top].type = type;
+ (*stack)[top].count = length;
+ (*stack).length = ++top + 1;
+ }
+
+ // non-deserialized data is nothing
+ if (used_ - offset_ == 0)
+ goto Labort;
+
+ do {
+ Lstart:
+ if (state == State.HEADER) {
+ const header = buffer_[cur];
+
+ if (0x00 <= header && header <= 0x7f) { // positive
+ callbackUInt(obj, header);
+ goto Lpush;
+ } else if (0xe0 <= header && header <= 0xff) { // negative
+ callbackInt(obj, cast(byte)header);
+ goto Lpush;
+ } else if (0xa0 <= header && header <= 0xbf) { // fix raw
+ trail = header & 0x1f;
+ state = State.RAW;
+ cur++;
+ continue;
+ } else if (0xd4 <= header && header <= 0xd8) { // fix ext
+ trail = 2 ^^ (header - 0xd4) + 1;
+ state = State.EXT_DATA;
+ cur++;
+ continue;
+ } else if (0x90 <= header && header <= 0x9f) { // fix array
+ size_t length = header & 0x0f;
+ if (length == 0) {
+ callbackArray(obj, 0);
+ goto Lpush;
+ } else {
+ startContainer!"Array"(ContainerElement.ARRAY_ITEM, length);
+ cur++;
+ continue;
+ }
+ } else if (0x80 <= header && header <= 0x8f) { // fix map
+ size_t length = header & 0x0f;
+ if (length == 0) {
+ callbackMap(obj, 0);
+ goto Lpush;
+ } else {
+ startContainer!"Map"(ContainerElement.MAP_KEY, length);
+ cur++;
+ continue;
+ }
+ } else {
+ switch (header) {
+ case Format.UINT8, Format.UINT16, Format.UINT32, Format.UINT64,
+ Format.INT8, Format.INT16, Format.INT32, Format.INT64,
+ Format.FLOAT, Format.DOUBLE:
+ trail = 1 << (header & 0x03); // computes object size
+ state = cast(State)(header & 0x1f);
+ break;
+ case Format.REAL:
+ trail = RealSize;
+ state = State.REAL;
+ break;
+ case Format.ARRAY16, Format.ARRAY32,
+ Format.MAP16, Format.MAP32:
+ trail = 2 << (header & 0x01); // computes container size
+ state = cast(State)(header & 0x1f);
+ break;
+ // raw will become str format in new spec
+ case Format.STR8:
+ case Format.RAW16: // will be STR16
+ case Format.RAW32: // will be STR32
+ trail = 1 << ((header & 0x03) - 1); // computes container size
+ state = cast(State)(header & 0x1f);
+ break;
+ case Format.BIN8, Format.BIN16, Format.BIN32:
+ trail = 1 << (header & 0x03); // computes container size
+ state = cast(State)(header & 0x1f);
+ break;
+ case Format.EXT8:
+ trail = 1;
+ state = State.EXT8;
+ break;
+ case Format.EXT16:
+ trail = 2;
+ state = State.EXT16;
+ break;
+ case Format.EXT32:
+ trail = 4;
+ state = State.EXT32;
+ break;
+ case Format.NIL:
+ callbackNil(obj);
+ goto Lpush;
+ case Format.TRUE:
+ callbackBool(obj, true);
+ goto Lpush;
+ case Format.FALSE:
+ callbackBool(obj, false);
+ goto Lpush;
+ default:
+ throw new UnpackException("Unknown type");
+ }
+
+ cur++;
+ goto Lstart;
+ }
+ } else {
+ // data lack for deserialization
+ if (used_ - cur < trail)
+ goto Labort;
+
+ const base = cur; cur += trail - 1; // fix current position
+
+ final switch (state) {
+ case State.FLOAT:
+ _f temp;
+
+ temp.i = load32To!uint(buffer_[base..base + trail]);
+ callbackFloat(obj, temp.f);
+ goto Lpush;
+ case State.DOUBLE:
+ _d temp;
+
+ temp.i = load64To!ulong(buffer_[base..base + trail]);
+ callbackFloat(obj, temp.f);
+ goto Lpush;
+ case State.REAL:
+ const expb = base + ulong.sizeof;
+
+ version (NonX86)
+ {
+ CustomFloat!80 temp;
+
+ const frac = load64To!ulong (buffer_[base..expb]);
+ const exp = load16To!ushort(buffer_[expb..expb + ushort.sizeof]);
+
+ temp.significand = frac;
+ temp.exponent = exp & 0x7fff;
+ temp.sign = exp & 0x8000 ? true : false;
+
+ // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max.
+ callbackFloat(obj, temp.get!real);
+ }
+ else
+ {
+ _r temp;
+
+ temp.fraction = load64To!(typeof(temp.fraction))(buffer_[base..expb]);
+ temp.exponent = load16To!(typeof(temp.exponent))(buffer_[expb..expb + temp.exponent.sizeof]);
+
+ callbackFloat(obj, temp.f);
+ }
+
+ goto Lpush;
+ case State.UINT8:
+ callbackUInt(obj, buffer_[base]);
+ goto Lpush;
+ case State.UINT16:
+ callbackUInt(obj, load16To!ushort(buffer_[base..base + trail]));
+ goto Lpush;
+ case State.UINT32:
+ callbackUInt(obj, load32To!uint(buffer_[base..base + trail]));
+ goto Lpush;
+ case State.UINT64:
+ callbackUInt(obj, load64To!ulong(buffer_[base..base + trail]));
+ goto Lpush;
+ case State.INT8:
+ callbackInt(obj, cast(byte)buffer_[base]);
+ goto Lpush;
+ case State.INT16:
+ callbackInt(obj, load16To!short(buffer_[base..base + trail]));
+ goto Lpush;
+ case State.INT32:
+ callbackInt(obj, load32To!int(buffer_[base..base + trail]));
+ goto Lpush;
+ case State.INT64:
+ callbackInt(obj, load64To!long(buffer_[base..base + trail]));
+ goto Lpush;
+ case State.RAW: Lraw:
+ hasRaw_ = true;
+ callbackRaw(obj, buffer_[base..base + trail]);
+ goto Lpush;
+
+ case State.EXT_DATA: Lext:
+ hasRaw_ = true;
+ obj.via.ext.type = buffer_[base];
+ callbackExt(obj, buffer_[base+1..base+trail]);
+ goto Lpush;
+ case State.EXT8:
+ trail = buffer_[base] + 1;
+ if (trail == 0)
+ goto Lext;
+ state = State.EXT_DATA;
+ cur++;
+ goto Lstart;
+ case State.EXT16:
+ trail = load16To!size_t(buffer_[base..base+trail]) + 1;
+ if (trail == 0)
+ goto Lext;
+ state = State.EXT_DATA;
+ cur++;
+ goto Lstart;
+ case State.EXT32:
+ trail = load32To!size_t(buffer_[base..base+trail]) + 1;
+ if (trail == 0)
+ goto Lext;
+ state = State.EXT_DATA;
+ cur++;
+ goto Lstart;
+
+ case State.STR8, State.BIN8:
+ trail = buffer_[base];
+ if (trail == 0)
+ goto Lraw;
+ state = State.RAW;
+ cur++;
+ goto Lstart;
+ case State.RAW16, State.BIN16:
+ trail = load16To!size_t(buffer_[base..base + trail]);
+ if (trail == 0)
+ goto Lraw;
+ state = State.RAW;
+ cur++;
+ goto Lstart;
+ case State.RAW32, State.BIN32:
+ trail = load32To!size_t(buffer_[base..base + trail]);
+ if (trail == 0)
+ goto Lraw;
+ state = State.RAW;
+ cur++;
+ goto Lstart;
+ case State.ARRAY16:
+ size_t length = load16To!size_t(buffer_[base..base + trail]);
+ if (length == 0) {
+ callbackArray(obj, 0);
+ goto Lpush;
+ } else {
+ startContainer!"Array"(ContainerElement.ARRAY_ITEM, length);
+ state = State.HEADER;
+ cur++;
+ continue;
+ }
+ case State.ARRAY36:
+ size_t length = load32To!size_t(buffer_[base..base + trail]);
+ if (length == 0) {
+ callbackArray(obj, 0);
+ goto Lpush;
+ } else {
+ startContainer!"Array"(ContainerElement.ARRAY_ITEM, length);
+ state = State.HEADER;
+ cur++;
+ continue;
+ }
+ case State.MAP16:
+ size_t length = load16To!size_t(buffer_[base..base + trail]);
+ if (length == 0) {
+ callbackMap(obj, 0);
+ goto Lpush;
+ } else {
+ startContainer!"Map"(ContainerElement.MAP_KEY, length);
+ state = State.HEADER;
+ cur++;
+ continue;
+ }
+ case State.MAP32:
+ size_t length = load32To!size_t(buffer_[base..base + trail]);
+ if (length == 0) {
+ callbackMap(obj, 0);
+ goto Lpush;
+ } else {
+ startContainer!"Map"(ContainerElement.MAP_KEY, length);
+ state = State.HEADER;
+ cur++;
+ continue;
+ }
+ case State.HEADER:
+ break;
+ }
+ }
+
+ Lpush:
+ if (top == 0)
+ goto Lfinish;
+
+ auto container = &(*stack)[top - 1];
+
+ final switch (container.type) {
+ case ContainerElement.ARRAY_ITEM:
+ container.value.via.array ~= obj;
+ if (--container.count == 0) {
+ obj = container.value;
+ top--;
+ goto Lpush;
+ }
+ break;
+ case ContainerElement.MAP_KEY:
+ container.key = obj;
+ container.type = ContainerElement.MAP_VALUE;
+ break;
+ case ContainerElement.MAP_VALUE:
+ container.value.via.map[container.key] = obj;
+ if (--container.count == 0) {
+ obj = container.value;
+ top--;
+ goto Lpush;
+ }
+ container.type = ContainerElement.MAP_KEY;
+ }
+
+ state = State.HEADER;
+ cur++;
+ } while (cur < used_);
+
+ goto Labort;
+
+ Lfinish:
+ (*stack)[0].value = obj;
+ ret = true;
+ cur++;
+ goto Lend;
+
+ Labort:
+ ret = false;
+
+ Lend:
+ context_.state = state;
+ context_.trail = trail;
+ context_.top = top;
+ parsed_ += cur - offset_;
+ offset_ = cur;
+
+ return ret;
+ }
+
+
+ /**
+ * supports foreach. One loop provides $(D Unpacked) object contains execute() result.
+ * This is convenient in case that $(D MessagePack) values are continuous.
+ */
+ int opApply(scope int delegate(ref Unpacked) dg)
+ {
+ int result;
+
+ while (execute()) {
+ auto unpackedResult = Unpacked(context_.stack[0].value);
+ result = dg(unpackedResult);
+ if (result)
+ break;
+
+ clear();
+ }
+
+ return result;
+ }
+
+
+ private:
+ /*
+ * initializes internal stack environment.
+ */
+ @safe
+ nothrow void initializeContext()
+ {
+ context_.state = State.HEADER;
+ context_.trail = 0;
+ context_.top = 0;
+ context_.stack.length = 1;
+ }
+}
+
+
+unittest
+{
+ import msgpack.packer;
+
+ {
+ // serialize
+ mixin DefinePacker;
+
+ packer.packArray(null, true, 1, -2, "Hi!", [1], [1:1], double.max, ExtValue(7, [1,2,3,4]));
+
+ // deserialize
+ auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
+ auto unpacked = unpacker.purge();
+
+ // Range test
+ foreach (unused; 0..2) {
+ uint i;
+
+ foreach (obj; unpacked)
+ i++;
+
+ assert(i == unpacked.via.array.length);
+ }
+
+ auto result = unpacked.via.array;
+
+ assert(result[0].type == Value.Type.nil);
+ assert(result[1].via.boolean == true);
+ assert(result[2].via.uinteger == 1);
+ assert(result[3].via.integer == -2);
+ assert(result[4].via.raw == [72, 105, 33]);
+ assert(result[5].as!(int[]) == [1]);
+ assert(result[6].as!(int[int]) == [1:1]);
+ assert(result[7].as!(double) == double.max);
+ assert(result[8].as!(ExtValue) == ExtValue(7, [1,2,3,4]));
+ }
+
+ // Test many combinations of EXT
+ {
+ mixin DefinePacker;
+
+ alias Lengths = TypeTuple!(0, 1, 2, 3, 4, 5, 8, 15, 16, 31,
+ 255, 256, 2^^16, 2^^32);
+
+ // Initialize a bunch of ExtValues and pack them
+ ExtValue[Lengths.length] values;
+ foreach (I, L; Lengths)
+ values[I] = ExtValue(7, new ubyte[](L));
+ packer.pack(values);
+
+ auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
+ auto unpacked = unpacker.purge();
+
+ // Compare unpacked values to originals
+ size_t i = 0;
+ foreach (deserialized; unpacked)
+ assert(deserialized == values[i++]);
+ }
+}
+
+
+private:
+@trusted:
+
+
+/**
+ * Sets value type and value.
+ *
+ * Params:
+ * value = the value to set
+ * number = the content to set
+ */
+void callbackUInt(ref Value value, ulong number)
+{
+ value.type = Value.Type.unsigned;
+ value.via.uinteger = number;
+}
+
+
+/// ditto
+void callbackInt(ref Value value, long number)
+{
+ value.type = Value.Type.signed;
+ value.via.integer = number;
+}
+
+
+/// ditto
+void callbackFloat(ref Value value, real number)
+{
+ value.type = Value.Type.floating;
+ value.via.floating = number;
+}
+
+
+/// ditto
+void callbackRaw(ref Value value, ubyte[] raw)
+{
+ value.type = Value.Type.raw;
+ value.via.raw = raw;
+}
+
+/// ditto
+void callbackExt(ref Value value, ubyte[] raw)
+{
+ value.type = Value.Type.ext;
+ value.via.ext.data = raw;
+}
+
+/// ditto
+void callbackArray(ref Value value, size_t length)
+{
+ value.type = Value.Type.array;
+ value.via.array.length = 0;
+ value.via.array.reserve(length);
+}
+
+
+/// ditto
+void callbackMap(ref Value value, lazy size_t length)
+{
+ value.type = Value.Type.map;
+ value.via.map = null; // clears previous result avoiding 'Access Violation'
+}
+
+
+/// ditto
+void callbackNil(ref Value value)
+{
+ value.type = Value.Type.nil;
+}
+
+
+/// ditto
+void callbackBool(ref Value value, bool boolean)
+{
+ value.type = Value.Type.boolean;
+ value.via.boolean = boolean;
+}
+
+
+unittest
+{
+ Value value;
+
+ // Unsigned integer
+ callbackUInt(value, uint.max);
+ assert(value.type == Value.Type.unsigned);
+ assert(value.via.uinteger == uint.max);
+
+ // Signed integer
+ callbackInt(value, int.min);
+ assert(value.type == Value.Type.signed);
+ assert(value.via.integer == int.min);
+
+ // Floating point
+ callbackFloat(value, real.max);
+ assert(value.type == Value.Type.floating);
+ assert(value.via.floating == real.max);
+
+ // Raw
+ callbackRaw(value, cast(ubyte[])[1]);
+ assert(value.type == Value.Type.raw);
+ assert(value.via.raw == cast(ubyte[])[1]);
+
+ // Array
+ Value[] array; array.reserve(16);
+
+ callbackArray(value, 16);
+ assert(value.type == Value.Type.array);
+ assert(value.via.array.capacity == array.capacity);
+
+ // Map
+ Value[Value] map;
+
+ callbackMap(value, 16);
+ assert(value.type == Value.Type.map);
+ assert(value.via.map == null);
+
+ // NIL
+ callbackNil(value);
+ assert(value.type == Value.Type.nil);
+
+ // Bool
+ callbackBool(value, true);
+ assert(value.type == Value.Type.boolean);
+ assert(value.via.boolean == true);
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/unpacker.d b/BioD/contrib/msgpack-d/src/msgpack/unpacker.d
new file mode 100644
index 0000000..fb29795
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/unpacker.d
@@ -0,0 +1,1543 @@
+module msgpack.unpacker;
+
+import msgpack.common;
+import msgpack.attribute;
+import msgpack.exception;
+
+import std.array;
+import std.exception;
+import std.range;
+import std.stdio;
+import std.traits;
+import std.typecons;
+import std.typetuple;
+import std.container;
+
+
+// for unpack without calling constructor
+private extern(C) Object _d_newclass(const ClassInfo);
+
+
+/**
+ * This $(D Unpacker) is a $(D MessagePack) direct-conversion deserializer
+ *
+ * This implementation is suitable for fixed data.
+ *
+ * Example:
+ * -----
+ * // serializedData is [10, 0.1, false]
+ * auto unpacker = Unpacker(serializedData);
+ *
+ * uint n;
+ * double d;
+ * bool b;
+ *
+ * unpacker.unpackArray(n, d, b);
+ *
+ * // using Tuple
+ * Tuple!(uint, double, bool) record;
+ * unpacker.unpack(record); // record is [10, 0.1, false]
+ * -----
+ *
+ * NOTE:
+ * Unpacker becomes template struct if Phobos supports truly IO module.
+ */
+struct Unpacker
+{
+ private:
+ static @system
+ {
+ alias void delegate(ref Unpacker, void*) UnpackHandler;
+ UnpackHandler[TypeInfo] unpackHandlers;
+
+ public void registerHandler(T, alias Handler)()
+ {
+ unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) {
+ Handler(unpacker, *cast(T*)obj);
+ };
+ }
+
+ public void register(T)()
+ {
+ unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) {
+ unpacker.unpackObject(*cast(T*)obj);
+ };
+ }
+
+ }
+
+ enum Offset = 1;
+
+ mixin InternalBuffer;
+
+ bool withFieldName_;
+
+
+ public:
+ /**
+ * Constructs a $(D Unpacker).
+ *
+ * Params:
+ * target = byte buffer to deserialize
+ * bufferSize = size limit of buffer size
+ */
+ this(in ubyte[] target, in size_t bufferSize = 8192, bool withFieldName = false)
+ {
+ initializeBuffer(target, bufferSize);
+ withFieldName_ = withFieldName;
+ }
+
+
+ /**
+ * Clears states for next deserialization.
+ */
+ @safe
+ nothrow void clear()
+ {
+ used_ = offset_ = parsed_ = 0;
+ hasRaw_ = false;
+ }
+
+
+ /**
+ * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM value).
+ *
+ * If the argument is pointer, dereferences pointer and assigns deserialized value.
+ * -----
+ * int* a;
+ * unpacker.unpack(a) // enforce throws Exception because a is null or
+ * // no throw if deserialized value is nil
+ *
+ * int b; a = &b;
+ * unpacker.unpack(b) // b is deserialized value or
+ * // assigns null if deserialized value is nil
+ * -----
+ *
+ * Params:
+ * value = the reference of value to assign.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ *
+ * Throws:
+ * UnpackException when doesn't read from buffer or precision loss occurs and
+ * MessagePackException when $(D_PARAM T) type doesn't match serialized type.
+ */
+ ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == bool))
+ {
+ canRead(Offset, 0);
+ const header = read();
+
+ switch (header) {
+ case Format.TRUE:
+ value = true;
+ break;
+ case Format.FALSE:
+ value = false;
+ break;
+ default:
+ rollback(0, "bool", cast(Format)header);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T value) if (isUnsigned!T && !is(Unqual!T == enum))
+ {
+ canRead(Offset, 0);
+ const header = read();
+
+ if (0x00 <= header && header <= 0x7f) {
+ value = header;
+ } else {
+ switch (header) {
+ case Format.UINT8:
+ canRead(ubyte.sizeof);
+ value = read();
+ break;
+ case Format.UINT16:
+ canRead(ushort.sizeof);
+ auto us = load16To!ushort(read(ushort.sizeof));
+ if (us > T.max)
+ rollback(ushort.sizeof, T.stringof, Format.UINT16);
+ value = cast(T)us;
+ break;
+ case Format.UINT32:
+ canRead(uint.sizeof);
+ auto ui = load32To!uint(read(uint.sizeof));
+ if (ui > T.max)
+ rollback(uint.sizeof, T.stringof, Format.UINT32);
+ value = cast(T)ui;
+ break;
+ case Format.UINT64:
+ canRead(ulong.sizeof);
+ auto ul = load64To!ulong(read(ulong.sizeof));
+ if (ul > T.max)
+ rollback(ulong.sizeof, T.stringof, Format.UINT64);
+ value = cast(T)ul;
+ break;
+ default:
+ rollback(0, T.stringof, cast(Format)header);
+ }
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum))
+ {
+ canRead(Offset, 0);
+ const header = read();
+
+ if (0x00 <= header && header <= 0x7f) {
+ value = cast(T)header;
+ } else if (0xe0 <= header && header <= 0xff) {
+ auto b = cast(T)(cast(ubyte)(-int(header)));
+ static if(T.sizeof < int.sizeof)
+ value = cast(T)(-int(b));
+ else
+ value = -b;
+ } else {
+ switch (header) {
+ case Format.UINT8:
+ canRead(ubyte.sizeof);
+ auto ub = read();
+ if (ub > T.max)
+ rollback(ubyte.sizeof, T.stringof, Format.UINT8);
+ value = cast(T)ub;
+ break;
+ case Format.UINT16:
+ canRead(ushort.sizeof);
+ auto us = load16To!ushort(read(ushort.sizeof));
+ if (us > T.max)
+ rollback(ushort.sizeof, T.stringof, Format.UINT16);
+ value = cast(T)us;
+ break;
+ case Format.UINT32:
+ canRead(uint.sizeof);
+ auto ui = load32To!uint(read(uint.sizeof));
+ if (ui > T.max)
+ rollback(uint.sizeof, T.stringof, Format.UINT32);
+ value = cast(T)ui;
+ break;
+ case Format.UINT64:
+ canRead(ulong.sizeof);
+ auto ul = load64To!ulong(read(ulong.sizeof));
+ if (ul > T.max)
+ rollback(ulong.sizeof, T.stringof, Format.UINT64);
+ value = cast(T)ul;
+ break;
+ case Format.INT8:
+ canRead(byte.sizeof);
+ value = cast(byte)read();
+ break;
+ case Format.INT16:
+ canRead(short.sizeof);
+ auto s = load16To!short(read(short.sizeof));
+ if (s < T.min || T.max < s)
+ rollback(short.sizeof, T.stringof, Format.INT16);
+ value = cast(T)s;
+ break;
+ case Format.INT32:
+ canRead(int.sizeof);
+ auto i = load32To!int(read(int.sizeof));
+ if (i < T.min || T.max < i)
+ rollback(int.sizeof, T.stringof, Format.INT32);
+ value = cast(T)i;
+ break;
+ case Format.INT64:
+ canRead(long.sizeof);
+ auto l = load64To!long(read(long.sizeof));
+ if (l < T.min || T.max < l)
+ rollback(long.sizeof, T.stringof, Format.INT64);
+ value = cast(T)l;
+ break;
+ default:
+ rollback(0, T.stringof, cast(Format)header);
+ }
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T value) if (isSomeChar!T && !is(Unqual!T == enum))
+ {
+ static if (is(Unqual!T == char)) {
+ ubyte tmp;
+ } else static if (is(Unqual!T == wchar)) {
+ ushort tmp;
+ } else static if (is(Unqual!T == dchar)) {
+ uint tmp;
+ }
+ unpack(tmp);
+ value = cast(T)(tmp);
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T value) if (isFloatingPoint!T && !is(Unqual!T == enum))
+ {
+ canRead(Offset, 0);
+ const header = read();
+
+ switch (header) {
+ case Format.FLOAT:
+ _f temp;
+
+ canRead(uint.sizeof);
+ temp.i = load32To!uint(read(uint.sizeof));
+ value = temp.f;
+ break;
+ case Format.DOUBLE:
+ // check precision loss
+ static if (is(Unqual!T == float))
+ rollback(0, T.stringof, Format.DOUBLE);
+
+ _d temp;
+
+ canRead(ulong.sizeof);
+ temp.i = load64To!ulong(read(ulong.sizeof));
+ value = temp.f;
+ break;
+ case Format.REAL:
+ static if (!EnableReal)
+ {
+ rollback(0, "real is disabled", Format.REAL);
+ }
+ else
+ {
+ // check precision loss
+ static if (is(Unqual!T == float) || is(Unqual!T == double))
+ rollback(0, T.stringof, Format.REAL);
+
+ canRead(RealSize);
+
+ version (NonX86)
+ {
+ CustomFloat!80 temp;
+
+ const frac = load64To!ulong (read(ulong.sizeof));
+ const exp = load16To!ushort(read(ushort.sizeof));
+
+ temp.significand = frac;
+ temp.exponent = exp & 0x7fff;
+ temp.sign = exp & 0x8000 ? true : false;
+
+ // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max.
+ value = temp.get!real;
+ }
+ else
+ {
+ _r temp;
+
+ temp.fraction = load64To!(typeof(temp.fraction))(read(temp.fraction.sizeof));
+ temp.exponent = load16To!(typeof(temp.exponent))(read(temp.exponent.sizeof));
+
+ value = temp.f;
+ }
+ }
+
+ break;
+ default:
+ rollback(0, T.stringof, cast(Format)header);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == enum))
+ {
+ OriginalType!T temp;
+
+ unpack(temp);
+
+ value = cast(T)temp;
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(T value) if (isPointer!T)
+ {
+ static if (is(Unqual!T == void*)) {
+ enforce(value !is null, "Can't deserialize void type");
+ unpackNil(value);
+ } else {
+ if (checkNil())
+ unpackNil(value);
+ else
+ enforce(value !is null, T.stringof ~ " is null pointer");
+
+ unpack(mixin(AsteriskOf!T ~ "value"));
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T value) if (is(T == ExtValue))
+ {
+ canRead(Offset, 0);
+ const header = read();
+
+ // Fixed
+ if (header >= Format.EXT && header <= Format.EXT + 4)
+ {
+ const length = 2^^(header - Format.EXT);
+ canRead(1 + length);
+
+ value.type = read();
+ value.data = read(length);
+ return this;
+ }
+
+ // Dynamic length
+ uint length;
+ switch (header) with (Format)
+ {
+ case EXT8:
+ canRead(1);
+ length = read();
+ break;
+ case EXT16:
+ canRead(2);
+ length = load16To!ushort(read(2));
+ break;
+ case EXT32:
+ canRead(4);
+ length = load32To!uint(read(4));
+ break;
+ default:
+ rollback(0, T.stringof, cast(Format)header);
+ }
+
+ canRead(1 + length);
+ value.type = read();
+ value.data = read(length);
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(Types...)(ref Types objects) if (Types.length > 1)
+ {
+ foreach (i, T; Types)
+ unpack!(T)(objects[i]);
+
+ return this;
+ }
+
+
+ /**
+ * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM array).
+ *
+ * This is convenient method for array deserialization.
+ * Rollback will be completely successful if you deserialize raw type((u)byte[] or string types).
+ * But, Rollback will be one element(e.g. int) if you deserialize other types(e.g. int[], int[int])
+ *
+ * No assign if the length of deserialized object is 0.
+ *
+ * In a static array, this method checks the length. Do rollback and throw exception
+ * if length of $(D_PARAM array) is different from length of deserialized object.
+ *
+ * Params:
+ * array = the reference of array to assign.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ *
+ * Throws:
+ * UnpackException when doesn't read from buffer or precision loss occurs and
+ * MessagePackException when $(D_PARAM T) type doesn't match serialized type.
+ */
+ ref Unpacker unpack(T)(ref T array) if ((isArray!T ||
+ isInstanceOf!(Array, T)) &&
+ !is(Unqual!T == enum))
+ {
+ alias typeof(T.init[0]) U;
+
+ /*
+ * Deserializes type-information of raw type.
+ */
+ @safe
+ size_t beginRaw()
+ {
+ canRead(Offset, 0);
+ const header = read();
+ size_t length;
+
+ if (0xa0 <= header && header <= 0xbf) {
+ length = header & 0x1f;
+ } else {
+ switch (header) {
+ case Format.BIN8, Format.STR8:
+ canRead(ubyte.sizeof);
+ length = read();
+ break;
+ case Format.BIN16, Format.RAW16:
+ canRead(ushort.sizeof);
+ length = load16To!size_t(read(ushort.sizeof));
+ break;
+ case Format.BIN32, Format.RAW32:
+ canRead(uint.sizeof);
+ length = load32To!size_t(read(uint.sizeof));
+ break;
+ case Format.NIL:
+ break;
+ default:
+ rollback(0, T.stringof, cast(Format)header);
+ }
+ }
+
+ return length;
+ }
+
+
+ if (checkNil()) {
+ static if (isStaticArray!T) {
+ onInvalidType("static array", Format.NIL);
+ } else {
+ return unpackNil(array);
+ }
+ }
+
+ // Raw bytes
+ static if (isByte!U || isSomeChar!U)
+ auto length = beginRaw();
+ else
+ auto length = beginArray();
+
+ if(length > buffer_.length) {
+ import std.conv: text;
+ throw new MessagePackException(text("Invalid array size in byte stream: Length (", length,
+ ") is larger than internal buffer size (", buffer_.length, ")"));
+ }
+
+ // Raw bytes
+ static if (isByte!U || isSomeChar!U) {
+ auto offset = calculateSize!(true)(length);
+ if (length == 0)
+ return this;
+
+ static if (isStaticArray!T) {
+ if (length != array.length)
+ rollback(offset, "static array was given but the length is mismatched");
+ }
+
+ canRead(length, offset + Offset);
+ static if (isStaticArray!T) {
+ array[] = (cast(U[])read(length))[0 .. T.length];
+ } else {
+ array = cast(T)read(length);
+ }
+
+ static if (isDynamicArray!T)
+ hasRaw_ = true;
+ } else {
+ if (length == 0)
+ return this;
+
+ static if (isStaticArray!T) {
+ if (length != array.length)
+ rollback(calculateSize(length), "static array was given but the length is mismatched");
+ } else {
+ array.length = length;
+ }
+
+ foreach (i; 0..length)
+ unpack(array[i]);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T array) if (isAssociativeArray!T)
+ {
+ alias typeof(T.init.keys[0]) K;
+ alias typeof(T.init.values[0]) V;
+
+ if (checkNil())
+ return unpackNil(array);
+
+ auto length = beginMap();
+ if (length == 0)
+ return this;
+
+ foreach (i; 0..length) {
+ K k; unpack(k);
+ V v; unpack(v);
+ array[k] = v;
+ }
+
+ return this;
+ }
+
+ /**
+ * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM object).
+ *
+ * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is:
+ * -----
+ * void fromMsgpack(ref Unpacker unpacker)
+ * -----
+ * Assumes $(D std.typecons.Tuple) or simple struct if $(D_KEYWORD struct) doesn't implement $(D fromMsgpack).
+ * Checks length if $(D_PARAM T) is a $(D std.typecons.Tuple) or simple struct.
+ *
+ * Params:
+ * object = the reference of object to assign.
+ * args = the arguments to class constructor(class only).
+ * This is used at new statement if $(D_PARAM object) is $(D_KEYWORD null).
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ */
+ ref Unpacker unpack(T, Args...)(ref T object, auto ref Args args) if (is(Unqual!T == class))
+ {
+ if (checkNil())
+ return unpackNil(object);
+
+ if (object is null) {
+ static if (Args.length == 0) {
+ static if (__traits(compiles, { new T(); }))
+ object = new T();
+ else
+ object = cast(T)_d_newclass(T.classinfo);
+ } else static if (__traits(compiles, { new T(args); })) {
+ object = new T(args);
+ } else {
+ throw new MessagePackException("Don't know how to construct class type '" ~ Unqual!T.stringof ~ "' with argument types '" ~ Args.stringof ~ "'.");
+ }
+ }
+
+ static if (hasMember!(T, "fromMsgpack"))
+ {
+ static if (__traits(compiles, { object.fromMsgpack(this, withFieldName_); })) {
+ object.fromMsgpack(this, withFieldName_);
+ } else static if (__traits(compiles, { object.fromMsgpack(this); })) { // backward compatible
+ object.fromMsgpack(this);
+ } else {
+ static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
+ }
+ } else {
+ if (auto handler = object.classinfo in unpackHandlers) {
+ (*handler)(this, cast(void*)&object);
+ return this;
+ }
+ if (T.classinfo !is object.classinfo) {
+ throw new MessagePackException("Can't unpack derived class through reference to base class.");
+ }
+
+ unpackObject(object);
+ }
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpack(T)(ref T object) if (is(Unqual!T == struct) &&
+ !is(Unqual!T == ExtValue))
+ {
+ static if (hasMember!(T, "fromMsgpack"))
+ {
+ static if (__traits(compiles, { object.fromMsgpack(this); })) {
+ object.fromMsgpack(this);
+ } else {
+ static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
+ }
+ } else {
+ if (auto handler = typeid(T) in unpackHandlers) {
+ (*handler)(this, cast(void*)&object);
+ return this;
+ }
+
+ size_t length = withFieldName_ ? beginMap() : beginArray();
+ if (length == 0)
+ return this;
+
+ static if (isTuple!T) {
+ if (length != T.Types.length)
+ rollback(calculateSize(length), "the number of tuple fields is mismatched");
+
+ foreach (i, Type; T.Types)
+ unpack(object.field[i]);
+ } else { // simple struct
+ //if (length != object.tupleof.length)
+ if (length != SerializingMemberNumbers!(T))
+ rollback(calculateSize(length), "the number of struct fields is mismatched");
+
+ if (withFieldName_) {
+ foreach (i, member; object.tupleof) {
+ static if (isPackedField!(T.tupleof[i]))
+ {
+ string fieldName;
+ unpack(fieldName);
+
+ if (fieldName == getFieldName!(T, i)) {
+ static if (hasSerializedAs!(T.tupleof[i])) {
+ alias Proxy = getSerializedAs!(T.tupleof[i]);
+ Proxy.deserialize(this, object.tupleof[i]);
+ } else {
+ unpack(object.tupleof[i]);
+ }
+ } else {
+ assert(false, "Invalid field name: '" ~ fieldName ~ "', expect '" ~ getFieldName!(T, i) ~ "'");
+ }
+ }
+ }
+ } else {
+ foreach (i, member; object.tupleof) {
+ static if (isPackedField!(T.tupleof[i])) {
+ static if (hasSerializedAs!(T.tupleof[i])) {
+ alias Proxy = getSerializedAs!(T.tupleof[i]);
+ Proxy.deserialize(this, object.tupleof[i]);
+ } else {
+ unpack(object.tupleof[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return this;
+ }
+
+
+ void unpackObject(T)(ref T object) if (is(Unqual!T == class))
+ {
+ alias SerializingClasses!(T) Classes;
+
+ size_t length = withFieldName_ ? beginMap() : beginArray();
+ if (length == 0)
+ return;
+
+ if (length != SerializingMemberNumbers!(Classes))
+ rollback(calculateSize(length), "the number of class fields is mismatched");
+
+ if (withFieldName_) {
+ foreach (_; 0..length) {
+ string fieldName;
+ unpack(fieldName);
+
+ foreach (Class; Classes) {
+ Class obj = cast(Class)object;
+
+ foreach (i, member; obj.tupleof) {
+ static if (isPackedField!(Class.tupleof[i]))
+ {
+ if (fieldName == getFieldName!(Class, i)) {
+ static if (hasSerializedAs!(Class.tupleof[i])) {
+ alias Proxy = getSerializedAs!(Class.tupleof[i]);
+ Proxy.deserialize(this, obj.tupleof[i]);
+ } else {
+ unpack(obj.tupleof[i]);
+ }
+ goto endLoop;
+ }
+ }
+ }
+ }
+ assert(false, "Invalid field name: '" ~ fieldName~"' ");
+
+ endLoop:
+ continue;
+ }
+ } else {
+ foreach (Class; Classes) {
+ Class obj = cast(Class)object;
+
+ foreach (i, member; obj.tupleof) {
+ static if (isPackedField!(Class.tupleof[i])) {
+ static if (hasSerializedAs!(Class.tupleof[i])) {
+ alias Proxy = getSerializedAs!(Class.tupleof[i]);
+ Proxy.deserialize(this, obj.tupleof[i]);
+ } else {
+ unpack(obj.tupleof[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Deserializes the container object and assigns to each argument.
+ *
+ * These methods check the length. Do rollback if
+ * the length of arguments is different from length of deserialized object.
+ *
+ * In unpackMap, the number of arguments must be even.
+ *
+ * Params:
+ * objects = the references of object to assign.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ */
+ ref Unpacker unpackArray(Types...)(ref Types objects)
+ {
+ auto length = beginArray();
+ if (length != Types.length)
+ rollback(calculateSize(length), "the number of deserialized objects is mismatched");
+
+ foreach (i, T; Types)
+ unpack(objects[i]);
+ // unpack(objects); // slow :(
+
+ return this;
+ }
+
+
+ /// ditto
+ ref Unpacker unpackMap(Types...)(ref Types objects)
+ {
+ static assert(Types.length % 2 == 0, "The number of arguments must be even");
+
+ auto length = beginMap();
+ if (length != Types.length / 2)
+ rollback(calculateSize(length), "the number of deserialized objects is mismatched");
+
+ foreach (i, T; Types)
+ unpack(objects[i]);
+
+ return this;
+ }
+
+
+ /**
+ * Deserializes the type-information of container.
+ *
+ * These methods don't deserialize contents.
+ * You need to call unpack method to deserialize contents at your own risk.
+ * -----
+ * // serialized data is [1, "Hi!"];
+ * int num;
+ * unpacker.beginArray(2).unpack(num); // num is 1
+ *
+ * // other operation
+ *
+ * string str;
+ * unpacker.unpack(str); // str is "Hi!"
+ * -----
+ *
+ * Returns:
+ * the container size.
+ */
+ @safe
+ size_t beginArray()
+ {
+ canRead(Offset, 0);
+ const header = read();
+ size_t length;
+
+ if (0x90 <= header && header <= 0x9f) {
+ length = header & 0x0f;
+ } else {
+ switch (header) {
+ case Format.ARRAY16:
+ canRead(ushort.sizeof);
+ length = load16To!size_t(read(ushort.sizeof));
+ break;
+ case Format.ARRAY32:
+ canRead(uint.sizeof);
+ length = load32To!size_t(read(uint.sizeof));
+ break;
+ case Format.NIL:
+ break;
+ default:
+ rollback(0, "array", cast(Format)header);
+ }
+ }
+
+ return length;
+ }
+
+
+ /// ditto
+ @safe
+ size_t beginMap()
+ {
+ canRead(Offset, 0);
+ const header = read();
+ size_t length;
+
+ if (0x80 <= header && header <= 0x8f) {
+ length = header & 0x0f;
+ } else {
+ switch (header) {
+ case Format.MAP16:
+ canRead(ushort.sizeof);
+ length = load16To!size_t(read(ushort.sizeof));
+ break;
+ case Format.MAP32:
+ canRead(uint.sizeof);
+ length = load32To!size_t(read(uint.sizeof));
+ break;
+ case Format.NIL:
+ break;
+ default:
+ rollback(0, "map", cast(Format)header);
+ }
+ }
+
+ return length;
+ }
+
+
+ /**
+ * Unpacks an EXT value into $(D type) and $(D data).
+ * $(D type) is checked and a $(D MessagePackException) is thrown if it does
+ * not match. The length of $(D data) is checked and a $(D MessagePackException)
+ * is thrown if the lengths do not match. If $(D data) is null, a new slice
+ * is returned.
+ */
+ ref Unpacker unpackExt(ref byte type, ref ubyte[] data) return
+ {
+ import std.conv : text;
+
+ canRead(Offset, 0);
+ const header = read();
+
+ uint length;
+ uint rollbackLength = 0;
+ if (header >= Format.EXT && header <= Format.EXT + 4)
+ {
+ // Fixed
+ length = 2^^(header - Format.EXT);
+
+ } else {
+ // Dynamic length
+ switch (header) with (Format)
+ {
+ case EXT8:
+ canRead(1);
+ length = read();
+ rollbackLength = 1;
+ break;
+ case EXT16:
+ canRead(2);
+ length = load16To!ushort(read(2));
+ rollbackLength = 2;
+ break;
+ case EXT32:
+ canRead(4);
+ length = load32To!uint(read(4));
+ rollbackLength = 4;
+ break;
+ default:
+ rollback(0, "ext", cast(Format)header);
+ }
+
+ }
+
+ canRead(1 + length);
+
+ // Read and check the type
+ byte type_ = read();
+ rollbackLength += 1;
+ if (type_ != type)
+ rollback(rollbackLength, text("Cannot unpack EXT of type ", type_, " into type ", type));
+
+ // Read and check data
+ if (data is null)
+ data = new ubyte[](length);
+ else if (data.length != length) {
+ rollback(rollbackLength, text("Length mismatch while unpacking EXT: ", data.length, " was given, actual length is ", length));
+ }
+ data[] = read(length);
+ return this;
+ }
+
+ /**
+ * Scans an entire buffer and converts each objects.
+ *
+ * This method is used for unpacking record-like objects.
+ *
+ * Example:
+ * -----
+ * // serialized data is "[1, 2][3, 4][5, 6][...".
+ * auto unpacker = Unpacker(serializedData);
+ * foreach (n, d; &unpacker.scan!(int, int)) // == "foreach (int n, int d; unpacker)"
+ * writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"...
+ * -----
+ */
+ int scan(Types...)(scope int delegate(ref Types) dg)
+ {
+ return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); });
+ }
+
+
+ /// ditto
+ int opApply(Types...)(scope int delegate(ref Types) dg)
+ {
+ int result;
+
+ while (used_ - offset_) {
+ auto length = beginArray();
+ if (length != Types.length)
+ rollback(calculateSize(length), "the number of deserialized objects is mismatched");
+
+ Types objects;
+ foreach (i, T; Types)
+ unpack(objects[i]);
+
+ result = dg(objects);
+ if (result)
+ return result;
+ }
+
+ return result;
+ }
+
+
+ private:
+ /*
+ * Deserializes nil object and assigns to $(D_PARAM value).
+ *
+ * Params:
+ * value = the reference of value to assign.
+ *
+ * Returns:
+ * self, i.e. for method chaining.
+ *
+ * Throws:
+ * UnpackException when doesn't read from buffer or precision loss occurs and
+ * MessagePackException when $(D_PARAM T) type doesn't match serialized type.
+ */
+ @safe
+ ref Unpacker unpackNil(T)(ref T value)
+ {
+ canRead(Offset, 0);
+ const header = read();
+
+ if (header == Format.NIL)
+ value = null;
+ else
+ rollback(0, "nil", cast(Format)header);
+
+ return this;
+ }
+
+
+ /*
+ * Next object is nil?
+ *
+ * Returns:
+ * true if next object is nil.
+ */
+ @safe
+ bool checkNil()
+ {
+ canRead(Offset, 0);
+
+ return buffer_[offset_] == Format.NIL;
+ }
+
+
+ /*
+ * Calculates the format size of container length.
+ */
+ size_t calculateSize(bool rawType = false)(in size_t length)
+ {
+ static if (rawType)
+ return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
+ else
+ return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
+ }
+
+
+ /*
+ * Reading test.
+ *
+ * Params:
+ * size = the size to read.
+ * offset = the offset to subtract when doesn't read from buffer.
+ *
+ * Throws:
+ * UnpackException when doesn't read from buffer.
+ */
+ @safe
+ void canRead(in size_t size, in size_t offset = Offset)
+ {
+ if (used_ - offset_ < size) {
+ if (offset)
+ offset_ -= offset;
+
+ throw new UnpackException("Insufficient buffer");
+ }
+ }
+
+
+ /*
+ * Reads value from buffer and advances offset.
+ */
+ @safe
+ nothrow ubyte read()
+ {
+ return buffer_[offset_++];
+ }
+
+
+ /*
+ * Reads value from buffer and advances offset.
+ */
+ @safe
+ nothrow ubyte[] read(in size_t size)
+ {
+ auto result = buffer_[offset_..offset_ + size];
+
+ offset_ += size;
+
+ return result;
+ }
+
+
+ /*
+ * Do rollback and throws exception.
+ */
+ @safe
+ void rollback(in size_t size, in string reason)
+ {
+ offset_ -= size + Offset;
+ onInvalidType(reason);
+ }
+
+ @safe
+ void rollback(in size_t size, in string expected, in Format actual)
+ {
+ offset_ -= size + Offset;
+ onInvalidType(expected, actual);
+ }
+}
+
+
+private:
+
+
+/*
+ * A callback for type-mismatched error in deserialization process.
+ */
+@safe
+pure void onInvalidType(in string reason)
+{
+ throw new MessagePackException("Attempt to unpack with non-compatible type: reason = " ~ reason);
+}
+
+@safe
+pure void onInvalidType(in string expected, in Format actual)
+{
+ import std.conv: text;
+ throw new MessagePackException(text("Attempt to unpack with non-compatible type: expected = ", expected, ", actual = ", actual));
+}
+
+
+unittest
+{
+ import msgpack.packer;
+
+ { // unique
+ mixin DefinePacker;
+
+ Tuple!(bool, bool) result;
+ Tuple!(bool, bool) test = tuple(true, false);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ unpacker.unpack(result);
+ assert(test == result);
+ }
+ { // uint *
+ mixin DefinePacker;
+
+ Tuple!(ubyte, ushort, uint, ulong) result;
+ Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max,
+ cast(uint)uint.max, cast(ulong)ulong.max);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ unpacker.unpack(result);
+ assert(test == result);
+ }
+ { // int *
+ mixin DefinePacker;
+
+ Tuple!(byte, short, int, long) result;
+ Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min,
+ cast(int)int.min, cast(long)long.min);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ unpacker.unpack(result);
+ assert(test == result);
+ }
+ { // floating point
+ mixin DefinePacker;
+
+ static if (real.sizeof == double.sizeof || !EnableReal)
+ {
+ Tuple!(float, double, double) result;
+ Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal);
+ }
+ else
+ {
+ Tuple!(float, double, real) result;
+ Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal);
+ }
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ unpacker.unpack(result);
+ assert(test == result);
+ }
+ { // pointer
+ mixin DefinePacker;
+
+ Tuple!(ulong, long, double) origin;
+ Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal);
+ Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]);
+ Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ unpacker.unpack(result);
+ foreach (i, v; test.field)
+ assert(*v == *result.field[i]);
+ assert(origin == values);
+ }
+ { // enum
+ enum : float { D = 0.5 }
+ enum E : ulong { U = 100 }
+
+ mixin DefinePacker;
+
+ float f = D, resultF;
+ E e = E.U, resultE;
+
+ packer.pack(D, e);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ unpacker.unpack(resultF, resultE);
+ assert(f == resultF);
+ assert(e == resultE);
+ }
+ { // container
+ mixin DefinePacker;
+
+ Tuple!(ulong[], double[uint], string, bool[2], char[2]) test
+ = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!");
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+ Tuple!(ulong[], double[uint], string, bool[2], char[2]) result;
+
+ unpacker.unpack(result);
+ assert(test == result);
+ }
+ { // ext
+
+ // Try a variety of lengths, making sure to hit all the fixexts
+ foreach (L; TypeTuple!(1, 2, 3, 4, 5, 8, 9, 16, 32, 512, 2^^16))
+ {
+ mixin DefinePacker;
+
+ ubyte[] data = new ubyte[](L);
+ ExtValue ext = ExtValue(7, data);
+ packer.pack(ext);
+
+ auto unpacker1 = Unpacker(packer.stream.data);
+ ExtValue witness;
+
+ unpacker1.unpack(witness);
+ assert(ext == witness);
+
+ // And try unpackExt
+ auto unpacker2 = Unpacker(packer.stream.data);
+ byte type = 1;
+ ubyte[] deserializedData = new ubyte[](7);
+
+ // This should be a type mismatch (1 != 7)
+ assertThrown!MessagePackException(
+ unpacker2.unpackExt(type, deserializedData));
+ type = 7;
+
+ // A data size mismatch
+ assertThrown!MessagePackException(
+ unpacker2.unpackExt(type, deserializedData));
+ deserializedData = new ubyte[](L);
+
+ // And this should succeed
+ unpacker2.unpackExt(type, deserializedData);
+ assert(deserializedData == data);
+ }
+ }
+ { // user defined
+ {
+ static struct S
+ {
+ uint num;
+
+ void toMsgpack(P)(ref P p) const { p.packArray(num); }
+ void fromMsgpack(ref Unpacker u)
+ {
+ assert(u.beginArray() == 1);
+ u.unpack(num);
+ }
+ }
+
+ mixin DefinePacker; S result, test = S(uint.max);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+ unpacker.unpack(result);
+
+ assert(test.num == result.num);
+ }
+ {
+ static class C
+ {
+ uint num;
+
+ this(uint n) { num = n; }
+
+ void toMsgpack(P)(ref P p) const { p.packArray(num - 1); }
+ void fromMsgpack(ref Unpacker u)
+ {
+ assert(u.beginArray() == 1);
+ u.unpack(num);
+ }
+ }
+
+ mixin DefinePacker; C result, test = new C(ushort.max);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+ unpacker.unpack(result, ushort.max);
+
+ assert(test.num == result.num + 1);
+ }
+ }
+ { // simple struct and class
+ {
+ static struct Simple
+ {
+ uint num;
+ @nonPacked string str;
+ }
+
+ static struct Simple2
+ {
+ @nonPacked string str;
+ uint num;
+ }
+
+ foreach (Type; TypeTuple!(Simple, Simple2)) {
+ mixin DefinePacker;
+ Type result, test;
+ test.num = uint.max;
+ test.str = "ignored";
+
+ packer.pack(test);
+ auto unpacker = Unpacker(packer.stream.data);
+ unpacker.unpack(result);
+
+ assert(test.num == result.num);
+ assert(test.str != result.str);
+ }
+ }
+
+ {
+ static struct SimpleProxy1
+ {
+ import std.conv;
+ static void serialize(ref Packer p, ref string val) { p.pack(to!uint(val)); }
+ static void deserialize(ref Unpacker u, ref string val) { uint tmp; u.unpack(tmp); val = to!string(tmp); }
+ }
+ static struct SimpleWithProxied1
+ {
+ @serializedAs!SimpleProxy1 string data;
+ enum string defaultValue = "10";
+ }
+
+ // https://github.com/msgpack/msgpack-d/issues/83
+ static struct SimpleProxy2
+ {
+ import std.datetime;
+ static void serialize(ref Packer p, ref SysTime val) { p.pack(val.toISOExtString()); }
+ static void deserialize(ref Unpacker u, ref SysTime val) { string tmp; u.unpack(tmp); val = SysTime.fromISOExtString(tmp); }
+ }
+ static struct SimpleWithProxied2
+ {
+ import std.datetime;
+ @serializedAs!SimpleProxy2 SysTime data;
+ static SysTime defaultValue() @property { return SysTime(DateTime(2019,1,1,0,0,0)); }
+ }
+
+ foreach (Type; TypeTuple!(SimpleWithProxied1, SimpleWithProxied2)) {
+ mixin DefinePacker;
+ Type result, test;
+ test.data = Type.defaultValue;
+
+ packer.pack(test);
+ auto unpacker = Unpacker(packer.stream.data);
+ unpacker.unpack(result);
+ assert(test.data == result.data);
+ }
+ }
+
+ static class SimpleA
+ {
+ bool flag = true;
+ }
+
+ static class SimpleB : SimpleA
+ {
+ ubyte type = 100;
+ }
+
+ static class SimpleC : SimpleB
+ {
+ uint num = uint.max;
+ @nonPacked string str;
+ }
+
+ static class SimpleC2 : SimpleB
+ {
+ @nonPacked string str;
+ uint num = uint.max;
+ }
+
+ static class SimpleD
+ {
+ static struct Proxy
+ {
+ import std.conv;
+ static void serialize(ref Packer p, ref bool val) { p.pack(to!string(val)); }
+ static void serialize(ref Packer p, ref uint val) { p.pack(to!string(val)); }
+ static void serialize(ref Packer p, ref ubyte val) { p.pack(to!string(val)); }
+ static void deserialize(ref Unpacker u, ref bool val) { string tmp; u.unpack(tmp); val = to!bool(tmp); }
+ static void deserialize(ref Unpacker u, ref uint val) { string tmp; u.unpack(tmp); val = to!uint(tmp); }
+ static void deserialize(ref Unpacker u, ref ubyte val) { string tmp; u.unpack(tmp); val = to!ubyte(tmp); }
+ }
+ @serializedAs!Proxy bool flag = true;
+ @serializedAs!Proxy ubyte type = 100;
+ @serializedAs!Proxy uint num = uint.max;
+ @nonPacked string str;
+ }
+
+ { // from derived class
+ foreach (Type; TypeTuple!(SimpleC, SimpleC2, SimpleD)) {
+ mixin DefinePacker;
+ Type result, test = new Type();
+ test.flag = false;
+ test.type = 99;
+ test.num = uint.max / 2;
+ test.str = "ignored";
+
+ packer.pack(test);
+ auto unpacker = Unpacker(packer.stream.data);
+ unpacker.unpack(result);
+
+ assert(test.flag == result.flag);
+ assert(test.type == result.type);
+ assert(test.num == result.num);
+ assert(test.str != result.str);
+ }
+ }
+ { // from base class
+ mixin DefinePacker; SimpleC test = new SimpleC();
+
+ packer.pack(test);
+
+ SimpleB result = new SimpleC();
+ auto unpacker = Unpacker(packer.stream.data);
+
+ try {
+ unpacker.unpack(result);
+ assert(false);
+ } catch (Exception e) { }
+ }
+ { // https://github.com/msgpack/msgpack-d/issues/16
+ mixin DefinePacker;
+
+ static class Issue16
+ {
+ int i;
+ this(int i) { this.i = i; }
+ }
+
+ Issue16 c1 = new Issue16(10);
+
+ // change behaviour to accept null with new object without constructor
+ Issue16 c2 = null;
+ packer.pack(c1);
+ auto unpacker1 = Unpacker(packer.stream.data);
+ unpacker1.unpack(c2);
+ //unpack(pack(c1), c2);
+ assert(c2.i == c1.i);
+
+ Issue16 c3 = new Issue16(20);
+ packer.stream.clear();
+ packer.pack(c1);
+ auto unpacker2 = Unpacker(packer.stream.data);
+ unpacker2.unpack(c3);
+ //unpack(pack(c1), c3);
+ assert(c3.i == c1.i);
+ }
+ }
+ { // variadic
+ mixin DefinePacker;
+
+ Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max);
+
+ packer.pack(test);
+
+ auto unpacker = Unpacker(packer.stream.data);
+
+ uint u; long l; double d;
+
+ unpacker.unpackArray(u, l, d);
+ assert(test == tuple(u, l, d));
+ }
+ { // scan / opApply
+ ubyte[] data;
+ mixin DefinePacker;
+
+ foreach (i; 0..2)
+ packer.pack(tuple(1, 0.5, "Hi!"));
+
+ foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) {
+ assert(n == 1);
+ assert(d == 0.5);
+ assert(s == "Hi!");
+ }
+ }
+}
diff --git a/BioD/contrib/msgpack-d/src/msgpack/value.d b/BioD/contrib/msgpack-d/src/msgpack/value.d
new file mode 100644
index 0000000..546cc88
--- /dev/null
+++ b/BioD/contrib/msgpack-d/src/msgpack/value.d
@@ -0,0 +1,1022 @@
+module msgpack.value;
+
+import msgpack.common;
+import msgpack.attribute;
+import msgpack.exception;
+
+import std.json;
+import std.container : Array;
+import std.traits;
+import std.typecons : Tuple, isTuple;
+
+
+/**
+ * $(D Value) is a $(D MessagePack) value representation
+ *
+ * Example:
+ * -----
+ * auto unpacker = StreamingUnpacker(pack(1, 0.1L) ~ pack(true) ~ pack("foobarbaz"));
+ *
+ * foreach (unpacked; unpacker) {
+ * if (unpacked.type == Value.Type.array) {
+ * foreach (obj; unpacked) {
+ * switch (obj.type) {
+ * case Value.Type.unsigned: writeln(obj.as!(uint)); break;
+ * case Value.Type.floating: writeln(obj.as!(real)); break;
+ * defalut:
+ * throw new Exception("Unknown type");
+ * }
+ * }
+ * } else {
+ * if (unpacked.type == Value.Type.boolean)
+ * writeln(unpacked.as!(bool));
+ * else
+ * writeln("Message: ", unpacked.as!(string));
+ * }
+ * }
+ * -----
+ */
+struct Value
+{
+ /**
+ * $(D MessagePack) value type
+ */
+ static enum Type
+ {
+ nil, /// nil(null in D)
+ boolean, /// true, false
+ unsigned, /// positive fixnum, uint 8, uint 16, uint 32, uint 64
+ signed, /// negative fixnum, int 8, int 16, int 32, int 64
+ floating, /// float, double, real
+ array, /// fix array, array 16, array 32
+ map, /// fix map, map 16, map 32
+ raw, /// fix raw, raw 16, raw 32
+ ext /// fix ext, ext8, ext16, ext32
+ }
+
+
+ /**
+ * msgpack value representation
+ */
+ static union Via
+ {
+ bool boolean; /// corresponding to Type.boolean
+ ulong uinteger; /// corresponding to Type.unsigned
+ long integer; /// corresponding to Type.signed
+ real floating; /// corresponding to Type.floating
+ Value[] array; /// corresponding to Type.array
+ Value[Value] map; /// corresponding to Type.map
+ ubyte[] raw; /// corresponding to Type.raw
+ ExtValue ext; /// corresponding to Type.ext
+ }
+
+
+ Type type; /// represents value type
+ Via via; /// represents real value
+
+
+ /**
+ * Constructs a $(D Value) with arguments.
+ *
+ * Params:
+ * value = the real content.
+ * type = the type of value.
+ */
+ @safe
+ this(Type type)
+ {
+ this.type = type;
+ }
+
+ @safe
+ this(typeof(null))
+ {
+ this(Type.nil);
+ }
+
+ /// ditto
+ @trusted
+ this(bool value, Type type = Type.boolean)
+ {
+ this(type);
+ via.boolean = value;
+ }
+
+
+ /// ditto
+ @trusted
+ this(ulong value, Type type = Type.unsigned)
+ {
+ this(type);
+ via.uinteger = value;
+ }
+
+
+ /// ditto
+ @trusted
+ this(long value, Type type = Type.signed)
+ {
+ this(type);
+ via.integer = value;
+ }
+
+
+ /// ditto
+ @trusted
+ this(real value, Type type = Type.floating)
+ {
+ this(type);
+ via.floating = value;
+ }
+
+
+ /// ditto
+ @trusted
+ this(Value[] value, Type type = Type.array)
+ {
+ this(type);
+ via.array = value;
+ }
+
+
+ /// ditto
+ @trusted
+ this(Value[Value] value, Type type = Type.map)
+ {
+ this(type);
+ via.map = value;
+ }
+
+
+ /// ditto
+ @trusted
+ this(ubyte[] value, Type type = Type.raw)
+ {
+ this(type);
+ via.raw = value;
+ }
+
+ /// This is unsafe overload because using cast internally.
+ @trusted
+ this(string value, Type type = Type.raw)
+ {
+ this(type);
+ via.raw = cast(ubyte[])value;
+ }
+
+ /**
+ * Constructs a $(D Value) with arguments.
+ *
+ * Params:
+ * value = the real content.
+ * type = the type of value.
+ */
+ @trusted
+ this(ExtValue value, Type type = Type.ext)
+ {
+ this(type);
+ via.ext = value;
+ }
+
+ /**
+ * Converts value to $(D_PARAM T) type.
+ *
+ * Returns:
+ * converted value.
+ *
+ * Throws:
+ * MessagePackException if type is mismatched.
+ *
+ * NOTE:
+ * Current implementation uses cast.
+ */
+ @property @trusted
+ T as(T)() if (is(Unqual!T == bool))
+ {
+ if (type != Type.boolean)
+ onCastError();
+
+ return via.boolean;
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if (isIntegral!T && !is(Unqual!T == enum))
+ {
+ if (type == Type.unsigned)
+ return cast(T)via.uinteger;
+
+ if (type == Type.signed)
+ return cast(T)via.integer;
+
+ onCastError();
+
+ assert(false);
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if (isFloatingPoint!T && !is(Unqual!T == enum))
+ {
+ if (type != Type.floating)
+ onCastError();
+
+ return cast(T)via.floating;
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if (is(Unqual!T == enum))
+ {
+ return cast(T)as!(OriginalType!T);
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if (is(Unqual!T == ExtValue))
+ {
+ if (type != Type.ext)
+ onCastError();
+
+ return cast(T)via.ext;
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if ((isArray!T ||
+ isInstanceOf!(Array, T)) &&
+ !is(Unqual!T == enum))
+ {
+ alias typeof(T.init[0]) V;
+
+ if (type == Type.nil) {
+ static if (isDynamicArray!T) {
+ return null;
+ } else {
+ return T.init;
+ }
+ }
+
+ static if (isByte!V || isSomeChar!V) {
+ if (type != Type.raw)
+ onCastError();
+
+ static if (isDynamicArray!T) {
+ return cast(T)via.raw;
+ } else {
+ if (via.raw.length != T.length)
+ onCastError();
+
+ return cast(T)(via.raw[0 .. T.length]);
+ }
+ } else {
+ if (type != Type.array)
+ onCastError();
+
+ V[] array;
+
+ foreach (elem; via.array)
+ array ~= elem.as!(V);
+
+ return array;
+ }
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if (isAssociativeArray!T)
+ {
+ alias typeof(T.init.keys[0]) K;
+ alias typeof(T.init.values[0]) V;
+
+ if (type == Type.nil)
+ return null;
+
+ if (type != Type.map)
+ onCastError();
+
+ V[K] map;
+
+ foreach (key, value; via.map)
+ map[key.as!(K)] = value.as!(V);
+
+ return map;
+ }
+
+
+ /**
+ * Converts to $(D_PARAM T) type.
+ *
+ * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is:
+ * -----
+ * void fromMsgpack(Value value)
+ * -----
+ * This method assigns converted values to all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D fromMsgpack).
+ *
+ * Params:
+ * args = arguments to class constructor(class only).
+ *
+ * Returns:
+ * converted value.
+ */
+ @property @trusted
+ T as(T, Args...)(Args args) if (is(Unqual!T == class))
+ {
+ if (type == Type.nil)
+ return null;
+
+ T object = new T(args);
+
+ static if (hasMember!(T, "fromMsgpack"))
+ {
+ static if (__traits(compiles, { object.fromMsgpack(this); })) {
+ object.fromMsgpack(this);
+ } else {
+ static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
+ }
+ } else {
+ alias SerializingClasses!(T) Classes;
+
+ if (via.array.length != SerializingMemberNumbers!(Classes))
+ throw new MessagePackException("The number of deserialized object member is mismatched");
+
+ size_t offset;
+ foreach (Class; Classes) {
+ Class obj = cast(Class)object;
+ foreach (i, member; obj.tupleof) {
+ static if (isPackedField!(Class.tupleof[i]))
+ obj.tupleof[i] = via.array[offset++].as!(typeof(member));
+ }
+ }
+ }
+
+ return object;
+ }
+
+
+ /// ditto
+ @property @trusted
+ T as(T)() if (is(Unqual!T == struct) && !is(Unqual!T == ExtValue))
+ {
+ T obj;
+
+ static if (hasMember!(T, "fromMsgpack"))
+ {
+ static if (__traits(compiles, { obj.fromMsgpack(this); })) {
+ obj.fromMsgpack(this);
+ } else {
+ static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
+ }
+ } else {
+ static if (isTuple!T) {
+ if (via.array.length != T.Types.length)
+ throw new MessagePackException("The number of deserialized Tuple element is mismatched");
+
+ foreach (i, Type; T.Types)
+ obj.field[i] = via.array[i].as!(Type);
+ } else { // simple struct
+ if (via.array.length != SerializingMemberNumbers!T)
+ throw new MessagePackException("The number of deserialized struct member is mismatched");
+
+ size_t offset;
+ foreach (i, member; obj.tupleof) {
+ static if (isPackedField!(T.tupleof[i]))
+ obj.tupleof[i] = via.array[offset++].as!(typeof(member));
+ }
+ }
+ }
+
+ return obj;
+ }
+
+
+ /**
+ * Special method called by $(D Packer).
+ *
+ * Params:
+ * packer = a MessagePack serializer.
+ */
+ void toMsgpack(Packer)(ref Packer packer) const
+ {
+ final switch (type) {
+ case Type.nil:
+ packer.pack(null);
+ break;
+ case Type.boolean:
+ packer.pack(via.boolean);
+ break;
+ case Type.unsigned:
+ packer.pack(via.uinteger);
+ break;
+ case Type.signed:
+ packer.pack(via.integer);
+ break;
+ case Type.floating:
+ packer.pack(via.floating);
+ break;
+ case Type.raw:
+ packer.pack(via.raw);
+ break;
+ case Type.ext:
+ packer.packExt(via.ext.type, via.ext.data);
+ break;
+ case Type.array:
+ packer.beginArray(via.array.length);
+ foreach (elem; via.array)
+ elem.toMsgpack(packer);
+ break;
+ case Type.map:
+ packer.beginMap(via.map.length);
+ foreach (key, value; via.map) {
+ key.toMsgpack(packer);
+ value.toMsgpack(packer);
+ }
+ break;
+ }
+ }
+
+
+ /**
+ * Comparison for equality. @trusted for union.
+ */
+ @trusted
+ bool opEquals(Tdummy = void)(ref const Value other) const
+ {
+ if (type != other.type)
+ return false;
+
+ final switch (other.type) {
+ case Type.nil: return true;
+ case Type.boolean: return opEquals(other.via.boolean);
+ case Type.unsigned: return opEquals(other.via.uinteger);
+ case Type.signed: return opEquals(other.via.integer);
+ case Type.floating: return opEquals(other.via.floating);
+ case Type.raw: return opEquals(other.via.raw);
+ case Type.ext: return opEquals(other.via.ext);
+ case Type.array: return opEquals(other.via.array);
+ case Type.map: return opEquals(other.via.map);
+ }
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : bool)(in T other) const
+ {
+ if (type != Type.boolean)
+ return false;
+
+ return via.boolean == other;
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : ulong)(in T other) const
+ {
+ static if (__traits(isUnsigned, T)) {
+ if (type != Type.unsigned)
+ return false;
+
+ return via.uinteger == other;
+ } else {
+ if (type != Type.signed)
+ return false;
+
+ return via.integer == other;
+ }
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : real)(in T other) const
+ {
+ if (type != Type.floating)
+ return false;
+
+ return via.floating == other;
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : const Value[])(in T other) const
+ {
+ if (type != Type.array)
+ return false;
+
+ return via.array == other;
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : const Value[Value])(in T other) const
+ {
+ if (type != Type.map)
+ return false;
+
+ // This comparison is instead of default comparison because 'via.map == other' raises "Access Violation".
+ foreach (key, value; via.map) {
+ if (key in other) {
+ if (other[key] != value)
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : const(ubyte)[])(in T other) const
+ {
+ if (type != Type.raw)
+ return false;
+
+ return via.raw == other;
+ }
+
+
+ /// ditto
+ @trusted
+ bool opEquals(T : string)(in T other) const
+ {
+ if (type != Type.raw)
+ return false;
+
+ return via.raw == cast(ubyte[])other;
+ }
+
+
+ //
+ @trusted
+ bool opEquals(T : ExtValue)(in T other) const
+ {
+ if (type != Type.ext)
+ return false;
+
+ return via.ext.type == other.type && via.ext.data == other.data;
+ }
+
+
+ @trusted
+ hash_t toHash() const nothrow
+ {
+ static hash_t getHash(T)(T* v) @safe nothrow
+ {
+ return typeid(T).getHash(v);
+ }
+
+ final switch (type) {
+ case Type.nil: return 0;
+ case Type.boolean: return getHash(&via.boolean);
+ case Type.unsigned: return getHash(&via.uinteger);
+ case Type.signed: return getHash(&via.integer);
+ case Type.floating: return getHash(&via.floating);
+ case Type.raw: return getHash(&via.raw);
+ case Type.ext: return getHash(&via.ext);
+ case Type.array:
+ hash_t ret;
+ foreach (elem; via.array)
+ ret ^= elem.toHash();
+ return ret;
+ case Type.map:
+ try {
+ hash_t ret;
+ foreach (key, value; via.map) {
+ ret ^= key.toHash();
+ ret ^= value.toHash();
+ }
+ return ret;
+ } catch(Throwable) assert(0);
+ }
+ }
+}
+
+
+unittest
+{
+ import std.array;
+
+ // nil
+ Value value = Value(null);
+ Value other = Value();
+
+ assert(value == other);
+ assert(value.type == Value.Type.nil);
+
+ // boolean
+ value = Value(true);
+ other = Value(false);
+
+ assert(value != other);
+ assert(value.type == Value.Type.boolean);
+ assert(value.as!(bool) == true);
+ assert(other == false);
+
+ try {
+ auto b = value.as!(uint);
+ assert(false);
+ } catch (MessagePackException e) { }
+
+ // unsigned integer
+ value = Value(10UL);
+ other = Value(10UL);
+
+ assert(value == other);
+ assert(value.type == Value.Type.unsigned);
+ assert(value.as!(uint) == 10);
+ assert(other == 10UL);
+
+ // signed integer
+ value = Value(-20L);
+ other = Value(-10L);
+
+ assert(value != other);
+ assert(value.type == Value.Type.signed);
+ assert(value.as!(int) == -20);
+ assert(other == -10L);
+
+ // enum
+ enum E : int { F = -20 }
+
+ E e = value.as!(E);
+ assert(e == E.F);
+
+ // floating point
+ value = Value(0.1e-10L);
+ other = Value(0.1e-20L);
+
+ assert(value != other);
+ assert(value.type == Value.Type.floating);
+ assert(value.as!(real) == 0.1e-10L);
+ assert(other == 0.1e-20L);
+
+ // raw
+ value = Value(cast(ubyte[])[72, 105, 33]);
+ other = Value(cast(ubyte[])[72, 105, 33]);
+
+ assert(value == other);
+ assert(value.type == Value.Type.raw);
+ assert(value.as!(string) == "Hi!");
+ assert(value.as!(ubyte[3]) == [72, 105, 33]);
+ assert(other == cast(ubyte[])[72, 105, 33]);
+
+ // raw with string
+ value = Value("hello");
+ other = Value("hello");
+
+ assert(value == other);
+ assert(value.type == Value.Type.raw);
+ assert(value.as!(string) == "hello");
+
+ // enum : string
+ enum EStr : string { elem = "hello" }
+
+ assert(value.as!(EStr) == EStr.elem);
+
+ // ext
+ auto ext = ExtValue(7, [1,2,3]);
+ value = Value(ExtValue(7, [1,2,3]));
+ assert(value.as!ExtValue == ext);
+
+ // array
+ auto t = Value(cast(ubyte[])[72, 105, 33]);
+ value = Value([t]);
+ other = Value([t]);
+
+ assert(value == other);
+ assert(value.type == Value.Type.array);
+ assert(value.as!(string[]) == ["Hi!"]);
+ assert(other == [t]);
+
+ // map
+ value = Value([Value(1L):Value(2L)]);
+ other = Value([Value(1L):Value(1L)]);
+
+ assert(value != other);
+ assert(value.type == Value.Type.map);
+ assert(value.as!(int[int]) == [1:2]);
+ assert(other == [Value(1L):Value(1L)]);
+
+ value = Value(10UL);
+
+ // struct
+ static struct S
+ {
+ ulong num;
+
+ void fromMsgpack(Value value) { num = value.via.uinteger; }
+ }
+
+ S s = value.as!(S);
+ assert(s.num == 10);
+
+ value = Value([Value(0.5f), Value(cast(ubyte[])[72, 105, 33])]);
+
+ // struct
+ static struct Simple
+ {
+ @nonPacked int era;
+ double num;
+ string msg;
+ }
+
+ Simple simple = value.as!(Simple);
+ assert(simple.era == int.init);
+ assert(simple.num == 0.5f);
+ assert(simple.msg == "Hi!");
+
+ value = Value(10UL);
+
+ // class
+ static class C
+ {
+ ulong num;
+
+ void fromMsgpack(Value value) { num = value.via.uinteger; }
+ }
+
+ C c = value.as!(C);
+ assert(c.num == 10);
+
+ static class SimpleA
+ {
+ bool flag = true;
+ }
+
+ static class SimpleB : SimpleA
+ {
+ ubyte type = 100;
+ }
+
+ static class SimpleC : SimpleB
+ {
+ @nonPacked string str;
+ uint num = uint.max;
+ }
+
+ value = Value([Value(false), Value(99UL), Value(cast(ulong)(uint.max / 2u))]);
+
+ SimpleC sc = value.as!(SimpleC);
+ assert(sc.flag == false);
+ assert(sc.type == 99);
+ assert(sc.num == uint.max / 2);
+ assert(sc.str.empty);
+
+ // std.typecons.Tuple
+ value = Value([Value(true), Value(1UL), Value(cast(ubyte[])"Hi!")]);
+
+ auto tuple = value.as!(Tuple!(bool, uint, string));
+ assert(tuple.field[0] == true);
+ assert(tuple.field[1] == 1u);
+ assert(tuple.field[2] == "Hi!");
+
+ /*
+ * non-MessagePackable object is stopped by static assert
+ * static struct NonMessagePackable {}
+ * auto nonMessagePackable = value.as!(NonMessagePackable);
+ */
+}
+
+
+/**
+ * Converts $(D Value) to $(D JSONValue).
+ *
+ * Params:
+ * val = $(D Value) to convert.
+ *
+ * Returns:
+ * a $(D JSONValue).
+ */
+@trusted
+JSONValue toJSONValue(in Value val)
+{
+ final switch (val.type)
+ {
+ case Value.Type.nil: return JSONValue(null);
+ case Value.Type.boolean: return JSONValue(val.via.boolean);
+ case Value.Type.unsigned: return JSONValue(val.via.uinteger);
+ case Value.Type.signed: return JSONValue(val.via.integer);
+ case Value.Type.floating: return JSONValue(val.via.floating);
+ case Value.Type.raw: return JSONValue(cast(string)(val.via.raw.idup));
+ case Value.Type.ext: throw new MessagePackException("Unable to convert ext to json");
+ case Value.Type.array: {
+ JSONValue[] vals;
+ foreach (elem; val.via.array)
+ vals ~= elem.toJSONValue();
+ return JSONValue(vals);
+ }
+ case Value.Type.map: {
+ JSONValue[string] vals;
+ foreach (key, value; val.via.map) {
+ if (key.type != Value.Type.raw)
+ {
+ throw new MessagePackException("JSON-object key must be a raw type");
+ }
+ vals[key.as!string] = value.toJSONValue();
+ }
+ return JSONValue(vals);
+ }
+ }
+}
+
+/**
+ * Converts $(D JSONValue) to $(D Value).
+ *
+ * Params:
+ * val = $(D JSONValue) to convert.
+ *
+ * Returns:
+ * a $(D Value).
+ */
+@trusted
+Value fromJSONValue(in JSONValue val)
+{
+ final switch (val.type())
+ {
+ case JSONType.null_: return Value(null);
+ case JSONType.true_: return Value(true);
+ case JSONType.false_: return Value(false);
+ case JSONType.uinteger: return Value(val.uinteger);
+ case JSONType.integer: return Value(val.integer);
+ case JSONType.float_: return Value(val.floating);
+ case JSONType.string: return Value(cast(ubyte[])(val.str));
+ case JSONType.array: {
+ Value[] vals;
+ foreach (elem; val.array)
+ vals ~= elem.fromJSONValue();
+ return Value(vals);
+ }
+ case JSONType.object: {
+ Value[Value] vals;
+ foreach (key, value; val.object) {
+ vals[Value(cast(ubyte[])key)] = value.fromJSONValue();
+ }
+ return Value(vals);
+ }
+ }
+}
+
+unittest
+{
+ import std.array : array;
+ import std.algorithm : equal, map;
+ import std.conv;
+ import std.math : isClose;
+ import std.range;
+ import msgpack;
+
+ // nil
+ Value value = Value(null);
+
+ assert(toJSONValue(value).type() == JSONType.null_);
+
+ // boolean
+ value = Value(true);
+ auto other = Value(false);
+
+ assert(toJSONValue(value).type() == JSONType.true_);
+ assert(toJSONValue(other).type() == JSONType.false_);
+
+ // unsigned integer
+ value = Value(10UL);
+
+ assert(value.toJSONValue().type == JSONType.uinteger);
+ assert(value.toJSONValue().uinteger == value.as!uint);
+ assert(value.toJSONValue().uinteger == 10UL);
+
+ // signed integer
+ value = Value(-20L);
+
+ assert(value.toJSONValue().type == JSONType.integer);
+ assert(value.toJSONValue().integer == value.as!int);
+
+ // enum
+ enum E : int { F = -20 }
+ value = Value(cast(long)(E.F));
+
+ assert(value.toJSONValue().type == JSONType.integer);
+ assert(value.toJSONValue().integer == E.F);
+
+ // floating point
+ value = Value(0.1e-10L);
+ other = Value(0.1e-20L);
+
+ assert(value.toJSONValue().type == JSONType.float_);
+ assert(other.toJSONValue().type == JSONType.float_);
+
+ assert(isClose(value.toJSONValue().floating, 0.1e-10L));
+ assert(isClose(other.toJSONValue().floating, 0.1e-20L));
+
+ // raw
+ long[] arr = [72, 105, 33];
+ value = Value(to!(ubyte[])(arr));
+
+ assert(value.toJSONValue().type == JSONType.string);
+ assert(equal(value.toJSONValue().str, arr));
+
+ // raw with string
+ value = Value("hello");
+ assert(value.toJSONValue().type == JSONType.string);
+ assert(value.toJSONValue().str == "hello");
+
+ // array
+ auto t = Value(to!(ubyte[])(arr));
+ value = Value([t]);
+ other = Value(array(map!(a => Value(a))(arr)));
+
+ assert(value.toJSONValue().type == JSONType.array);
+ assert(value.toJSONValue().array.length == 1);
+ assert(value.toJSONValue().array.front().type == JSONType.string);
+ assert(equal(value.toJSONValue().array.front().str, arr));
+ assert(other.toJSONValue().type == JSONType.array);
+ assert(array(map!(a => a.integer)(other.toJSONValue().array)) == arr);
+
+ // map
+ value = Value([Value("key"):Value(2L)]);
+
+ assert(value.toJSONValue().type == JSONType.object);
+ assert("key" in value.toJSONValue().object);
+ assert(value.toJSONValue().object["key"].type == JSONType.integer);
+ assert(value.toJSONValue().object["key"].integer == 2L);
+
+ // struct
+ static struct Simple
+ {
+ @nonPacked int era;
+ double num;
+ string msg;
+ }
+
+ Simple simple;
+ simple.era = 5;
+ simple.num = 13.5;
+ simple.msg = "helloworld";
+ value = simple.pack().unpack().value;
+
+ assert(value.toJSONValue().type == JSONType.array);
+ assert(value.toJSONValue().array.length == 2);
+ assert(value.toJSONValue().array[0].type == JSONType.float_);
+ assert(isClose(value.toJSONValue().array[0].floating, simple.num));
+ assert(value.toJSONValue().array[1].type == JSONType.string);
+ assert(value.toJSONValue().array[1].str == simple.msg);
+
+ // class
+ static class SimpleA
+ {
+ bool flag = true;
+ }
+
+ static class SimpleB : SimpleA
+ {
+ ubyte type = 100;
+ }
+
+ static class SimpleC : SimpleB
+ {
+ @nonPacked string str;
+ uint num = uint.max;
+ }
+
+ SimpleC sc = new SimpleC;
+ value = sc.pack!true().unpack().value;
+
+ assert(value.toJSONValue().type == JSONType.object);
+ assert(value.toJSONValue().object.length == 3);
+ assert("flag" in value.toJSONValue().object);
+ assert(value.toJSONValue().object["flag"].type == (sc.flag ? JSONType.true_ : JSONType.false_));
+ assert("type" in value.toJSONValue().object);
+ assert(value.toJSONValue().object["type"].type == JSONType.uinteger);
+ assert(value.toJSONValue().object["type"].uinteger == sc.type);
+ assert("num" in value.toJSONValue().object);
+ assert(value.toJSONValue().object["num"].type == JSONType.uinteger);
+ assert(value.toJSONValue().object["num"].uinteger == sc.num);
+
+ other = value.toJSONValue().fromJSONValue();
+ assert(value == other);
+}
+
+
+private:
+
+
+/**
+ * A callback for type-mismatched error in cast conversion.
+ */
+@safe
+pure void onCastError()
+{
+ throw new MessagePackException("Attempt to cast with another type");
+}
diff --git a/BioD/contrib/msgpack-d/win32.mak b/BioD/contrib/msgpack-d/win32.mak
new file mode 100644
index 0000000..2146acf
--- /dev/null
+++ b/BioD/contrib/msgpack-d/win32.mak
@@ -0,0 +1,24 @@
+DMD = dmd
+LIB = msgpack.lib
+DFLAGS = -O -release -inline -nofloat -w -d -Isrc
+UDFLAGS = -w -g -debug -unittest
+
+SRCS = src\msgpack.d
+
+# DDoc
+DOCDIR = html
+CANDYDOC = html\candydoc\candy.ddoc html\candydoc\modules.ddoc
+DDOCFLAGS = -Dd$(DOCDIR) -c -o- -Isrc $(CANDYDOC)
+
+DOCS = $(DOCDIR)\msgpack.html
+
+target: doc $(LIB)
+
+$(LIB):
+ $(DMD) $(DFLAGS) -lib -of$(LIB) $(SRCS)
+
+doc:
+ $(DMD) $(DDOCFLAGS) $(SRCS)
+
+clean:
+ rm $(DOCS) $(LIB)