From 61a173670117ee3e4d9093c501887d83373bf573 Mon Sep 17 00:00:00 2001 From: Pjotr Prins Date: Wed, 16 Aug 2023 09:22:43 +0200 Subject: [PATCH] Adding msg-pack --- BioD/contrib/msgpack-d/.gitignore | 12 + BioD/contrib/msgpack-d/LICENSE_1_0.txt | 23 + BioD/contrib/msgpack-d/README.markdown | 199 +++ BioD/contrib/msgpack-d/dub.json | 19 + BioD/contrib/msgpack-d/example/array_test.d | 39 + BioD/contrib/msgpack-d/example/attribute.d | 24 + .../msgpack-d/example/compare_direct_stream.d | 55 + BioD/contrib/msgpack-d/example/compare_json.d | 33 + BioD/contrib/msgpack-d/example/convert_json.d | 38 + BioD/contrib/msgpack-d/example/custom.d | 514 ++++++ .../msgpack-d/example/custom_handler.d | 65 + BioD/contrib/msgpack-d/example/map_test.d | 39 + BioD/contrib/msgpack-d/example/register.d | 37 + BioD/contrib/msgpack-d/example/simple.d | 27 + BioD/contrib/msgpack-d/example/stream.d | 57 + .../msgpack-d/example/unpacker_foreach.d | 43 + .../msgpack-d/html/candydoc/candy.ddoc | 52 + .../msgpack-d/html/candydoc/explorer.js | 305 ++++ .../msgpack-d/html/candydoc/ie56hack.css | 21 + .../msgpack-d/html/candydoc/img/bg.gif | Bin 0 -> 37503 bytes .../msgpack-d/html/candydoc/img/candydoc.gif | Bin 0 -> 706 bytes .../html/candydoc/img/outline/alias.gif | Bin 0 -> 92 bytes .../html/candydoc/img/outline/bg.gif | Bin 0 -> 36708 bytes .../html/candydoc/img/outline/class.gif | Bin 0 -> 160 bytes .../html/candydoc/img/outline/enum.gif | Bin 0 -> 174 bytes .../html/candydoc/img/outline/func.gif | Bin 0 -> 97 bytes .../html/candydoc/img/outline/module.gif | Bin 0 -> 347 bytes .../html/candydoc/img/outline/package.gif | Bin 0 -> 229 bytes .../html/candydoc/img/outline/struct.gif | Bin 0 -> 169 bytes .../html/candydoc/img/outline/template.gif | Bin 0 -> 159 bytes .../html/candydoc/img/outline/var.gif | Bin 0 -> 92 bytes .../html/candydoc/img/package/bg.gif | Bin 0 -> 16167 bytes .../msgpack-d/html/candydoc/img/tree/shim.gif | Bin 0 -> 55 bytes .../msgpack-d/html/candydoc/img/tree/tb.gif | Bin 0 -> 63 bytes .../msgpack-d/html/candydoc/img/tree/tbr.gif | Bin 0 -> 65 bytes .../msgpack-d/html/candydoc/img/tree/tbrm.gif | Bin 0 -> 313 bytes .../msgpack-d/html/candydoc/img/tree/tbrp.gif | Bin 0 -> 314 bytes .../msgpack-d/html/candydoc/img/tree/tr.gif | Bin 0 -> 63 bytes .../msgpack-d/html/candydoc/img/tree/trm.gif | Bin 0 -> 312 bytes .../msgpack-d/html/candydoc/img/tree/trp.gif | Bin 0 -> 313 bytes .../msgpack-d/html/candydoc/modules.ddoc | 8 + .../contrib/msgpack-d/html/candydoc/style.css | 169 ++ BioD/contrib/msgpack-d/html/candydoc/tree.js | 374 ++++ BioD/contrib/msgpack-d/html/candydoc/util.js | 41 + BioD/contrib/msgpack-d/meson.build | 73 + BioD/contrib/msgpack-d/posix.mak | 50 + .../contrib/msgpack-d/src/msgpack/attribute.d | 95 + BioD/contrib/msgpack-d/src/msgpack/buffer.d | 216 +++ BioD/contrib/msgpack-d/src/msgpack/common.d | 633 +++++++ .../contrib/msgpack-d/src/msgpack/exception.d | 27 + BioD/contrib/msgpack-d/src/msgpack/package.d | 522 ++++++ BioD/contrib/msgpack-d/src/msgpack/packer.d | 1396 +++++++++++++++ BioD/contrib/msgpack-d/src/msgpack/register.d | 49 + .../src/msgpack/streaming_unpacker.d | 914 ++++++++++ BioD/contrib/msgpack-d/src/msgpack/unpacker.d | 1543 +++++++++++++++++ BioD/contrib/msgpack-d/src/msgpack/value.d | 1022 +++++++++++ BioD/contrib/msgpack-d/win32.mak | 24 + 57 files changed, 8758 insertions(+) create mode 100644 BioD/contrib/msgpack-d/.gitignore create mode 100644 BioD/contrib/msgpack-d/LICENSE_1_0.txt create mode 100644 BioD/contrib/msgpack-d/README.markdown create mode 100644 BioD/contrib/msgpack-d/dub.json create mode 100644 BioD/contrib/msgpack-d/example/array_test.d create mode 100644 BioD/contrib/msgpack-d/example/attribute.d create mode 100644 BioD/contrib/msgpack-d/example/compare_direct_stream.d create mode 100644 BioD/contrib/msgpack-d/example/compare_json.d create mode 100644 BioD/contrib/msgpack-d/example/convert_json.d create mode 100644 BioD/contrib/msgpack-d/example/custom.d create mode 100644 BioD/contrib/msgpack-d/example/custom_handler.d create mode 100644 BioD/contrib/msgpack-d/example/map_test.d create mode 100644 BioD/contrib/msgpack-d/example/register.d create mode 100644 BioD/contrib/msgpack-d/example/simple.d create mode 100644 BioD/contrib/msgpack-d/example/stream.d create mode 100644 BioD/contrib/msgpack-d/example/unpacker_foreach.d create mode 100644 BioD/contrib/msgpack-d/html/candydoc/candy.ddoc create mode 100644 BioD/contrib/msgpack-d/html/candydoc/explorer.js create mode 100644 BioD/contrib/msgpack-d/html/candydoc/ie56hack.css create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/bg.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/candydoc.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/alias.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/bg.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/class.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/enum.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/func.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/module.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/package.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/struct.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/template.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/outline/var.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/package/bg.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/shim.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/tb.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/tbr.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrm.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/tbrp.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/tr.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/trm.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/img/tree/trp.gif create mode 100644 BioD/contrib/msgpack-d/html/candydoc/modules.ddoc create mode 100644 BioD/contrib/msgpack-d/html/candydoc/style.css create mode 100644 BioD/contrib/msgpack-d/html/candydoc/tree.js create mode 100644 BioD/contrib/msgpack-d/html/candydoc/util.js create mode 100644 BioD/contrib/msgpack-d/meson.build create mode 100644 BioD/contrib/msgpack-d/posix.mak create mode 100644 BioD/contrib/msgpack-d/src/msgpack/attribute.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/buffer.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/common.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/exception.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/package.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/packer.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/register.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/streaming_unpacker.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/unpacker.d create mode 100644 BioD/contrib/msgpack-d/src/msgpack/value.d create mode 100644 BioD/contrib/msgpack-d/win32.mak 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 0000000000000000000000000000000000000000..457c74e45f63a9723cc602a30ad048979470c423 GIT binary patch literal 37503 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3b04bf80c76334e875b8a3b14599515fdf57f44d GIT binary patch literal 92 zcmZ?wbhEHb6krfwSj50k?%|PaV{`k+k<}$7|G|I(3>1H|FfuT(GU$MKAhit4iV?f+ m{4+S^xsbu(jJ4YZK?8vn_0A=doQ~gf4@RFY$@S)Bum%8Z5gtbX literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5edf8359f9d007751147c4eac1d0bff1eb3e02cd GIT binary patch literal 36708 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a4a14864987d7d660a75c7596e6ba5b1107b4720 GIT binary patch literal 160 zcmZ?wbhEHb6krfw*v!CSKfm6-KE8hceEa$K_VM=n@9(eQAHRS9{QLL!{|5sGRG|2i zg^_`Qi$Mn@3^Id(#W&!j=jy!?3lCTxx}(rFN3=)7Q}%g+$_7Wd%Rj`LC-fY=BF5>E w4 z14FDVVN%VoCP%%j11JS5p&$lBVfB&>1S`d%MGycc$E0IAY(9-h-?HVL@REmMwWyK# crj8o{pfmQEWN3;cU;qY~7+{~v_#Y7fJ21jW6#xJL literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..18103b11fc7ea74276adbc2cad5252b670854661 GIT binary patch literal 97 zcmZ?wbhEHb6krfwSj50!UvF<8Z(lz@e*XUW|Ns9pz<}aU7DfgJ76u)V07xwZvzo`Q sJO2z$oorwg?y+^caz~+p|3vSEoeNVnHu!(omm+e+Z^fkBHw2V@M$PYi4o4pR#}bfo%EJXBkGienxVXUGK2EHQ?Lg$-A;x?~)b z3p6@T&QW3&;NUs)mT_r@p$uDpr)<7rk`_xNAD=L5n~=A&yb2eW0ADPdx~l@0B99n9NRgReI$JLIy*900{}PseK`OC literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..231dc70baa1a0159ac468500c560fcf40f3b3717 GIT binary patch literal 229 zcmZ?wbhEHb6krfwIKsg2^yI>M1rcvAE<3lndqJe)!7APBJEmXXIiu3wq0mNra;W_A z1x>g2&-?jt_x>is{iRylb2S!4DlLssI#{lKwApBPq1L)&)s+d#|NsAIzy=h5vM@3* zNHFMtBtdpEuqG*}^`&Iadog3ljNm=3OyYi)h71NQJM5CM zz{#Kkk^z~)z!GL~(sT7*i@AcoEFuhe+$`C`6sEXFbG_Iyl_g17O{3SXQJ{5JrUB=M zx}{4Lrx{$zIU%%QMU9x3&%B4?E-~&#lNOd8ex;%{?KFdTCa+9dsxU*xIY}QD25SIC C4>!{Q literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..34bcaf71351ceb9008750d283a3c20f943b60c9e GIT binary patch literal 159 zcmZ?wbhEHb6krfw*v!CiY)L|Lg!8u(`L7P-+}oCMX>H2d36TrhL%*(w`L?Ox*Om3A zrl$Y@{|Ax~p!k!8k%57SL5BedKxQzo_)IwIxq9!6l`U045|dP%ES083tW&efbrL+Y zS+VA_1`opq8HPi;hK?IpxEW3#HP|%i^drGtNw31LJnU`>o#o*AZsO!!d^4x|y0e^1 IQDU$L0P*}koB#j- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..630b286c3bda32fa480e0e01949bad7409098b66 GIT binary patch literal 92 zcmZ?wbhEHb6krfwSj50!UvF<8Z@>Tk{`vdk|APSo7%2W^VPs%nWzYffKx!G76(e@t m`Dbv-b0LGn8Edx-f(8OD>YYm@IUT>}9*jO)lIzXMU=09r*&bs6 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..45ad4f077239295f4370e9aa277b28cbafe46e81 GIT binary patch literal 16167 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..45b7594365a818b73be8f519439f500c252da4d2 GIT binary patch literal 55 zcmZ?wbhEHb6krfwXkcLY|NlP&1B2pE7Dgb&paUX6G7L<7E&VG`zvW*%XUnbb&33E| F)&OmJ4$S}n literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..efecd28b62a270145609391759d3b8419bac4979 GIT binary patch literal 63 zcmZ?wbhEHb6krfwXkcVmvts7||Nj+#vM@3*Ff!;c00Bsbfk~`~clqhFFBjPq+$!I^ Q>enZ`+60w8H%10)09E%CumAu6 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e5da126570cd6862a7e950d2a1b2a3442b32367b GIT binary patch literal 65 zcmZ?wbhEHb6krfwXkcVmvts7||Nj+#vM@3*Ff!;c00Bsbfk~o=clqhFFBjPq+$!Jf SW%FfK;6k>(piDVd25SI!WfYJA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e6a21475389475a90a7c8392cc98cef54581f694 GIT binary patch literal 313 zcmZ?wbhEHb6krfwxT?%hF=OkR6*Cza7&aWb{P*wggFBXg|NiCApWolVfBp9D%ZCA!iCw9Mj`S|I>TQ@GB{QmvR;a#f^Y+v^M+vjiJzC3<#^U1@Tm(J|JbL0H| z+m~)%JM;DH$G2~vfBN+9LWcC7gS|3AEcf!yZuQ`2wCV6Wka%-V mFN3(G03$aKZ@n{v1eXE}i?C3qQ$TQFsNYO?pINgU8LR<03U*Ea literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b30a0673d343091ee1779a9d05daf54c703cf741 GIT binary patch literal 63 zcmZ?wbhEHb6krfwXkcVmvts7||Nj+#vM@3*Ff!;c00Bsbfk~`~clqhFFBjPq+$!Jf QW%FfK>m!e8UW^RZ08$GSx&QzG literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b37912db0d564b95bfb14f267a6deea9cbb67532 GIT binary patch literal 312 zcmZ?wbhEHb6krfwxT?%hF=HzO1H+mXGdCQ%{P*wggFBXg|NiCApWolVfBp9D%Z``-TB#tc1#vp ri+WU!1V}T=C~(P%NC?+CGf0VXv$Bc{b~ptD2Zs91m^sVSk--`OA)R@3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..337b338fb950ed3de4a5285c8d1436318c964b05 GIT binary patch literal 313 zcmZ?wbhEHb6krfwxT?rdF=HzO1H+mXGdCQ%d~nC|@87@v`Sbhl-`^iTy!rFz*Rg%; zu3bEK>CA!iCw9Mj`S|I>TQ@GB{QmvR;a#f^Y+v^M+vjiJzC3<#^U1@Tm(J|JbL0H| z+m~)%JM;DH$G2~vfBN+9LWcC7gS|3AEcf!yZuQ`2wCV6Wka%-V lFN3(G03$aKZ@n{v1eXE}i?C3qQ$TQFsNc+4vppRdtO3J2b_4(b literal 0 HcmV?d00001 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)