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) + + + + + + +
+
+ + + +

$(TITLE)

$(BODY)
+ Page was generated with + + on $(DATETIME) +
+
+$(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)