├── .github
└── workflows
│ └── d.yml
├── .gitignore
├── LICENSE_1_0.txt
├── README.markdown
├── dub.json
├── example
├── array_test.d
├── attribute.d
├── compare_direct_stream.d
├── compare_json.d
├── convert_json.d
├── custom.d
├── custom_handler.d
├── map_test.d
├── register.d
├── simple.d
├── stream.d
└── unpacker_foreach.d
├── html
└── candydoc
│ ├── candy.ddoc
│ ├── explorer.js
│ ├── ie56hack.css
│ ├── img
│ ├── bg.gif
│ ├── candydoc.gif
│ ├── outline
│ │ ├── alias.gif
│ │ ├── bg.gif
│ │ ├── class.gif
│ │ ├── enum.gif
│ │ ├── func.gif
│ │ ├── module.gif
│ │ ├── package.gif
│ │ ├── struct.gif
│ │ ├── template.gif
│ │ └── var.gif
│ ├── package
│ │ └── bg.gif
│ └── tree
│ │ ├── shim.gif
│ │ ├── tb.gif
│ │ ├── tbr.gif
│ │ ├── tbrm.gif
│ │ ├── tbrp.gif
│ │ ├── tr.gif
│ │ ├── trm.gif
│ │ └── trp.gif
│ ├── modules.ddoc
│ ├── style.css
│ ├── tree.js
│ └── util.js
├── meson.build
├── posix.mak
├── src
└── msgpack
│ ├── attribute.d
│ ├── buffer.d
│ ├── common.d
│ ├── exception.d
│ ├── package.d
│ ├── packer.d
│ ├── register.d
│ ├── streaming_unpacker.d
│ ├── unpacker.d
│ └── value.d
└── win32.mak
/.github/workflows/d.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | test:
6 | strategy:
7 | fail-fast: false
8 | matrix:
9 | os: [ ubuntu-latest, macOS-latest, windows-latest ]
10 | dc: [ dmd-latest, ldc-latest ]
11 | runs-on: ${{ matrix.os }}
12 | steps:
13 | - name: Checkout repository
14 | uses: actions/checkout@v3
15 | - name: Install compiler
16 | uses: dlang-community/setup-dlang@v1
17 | with:
18 | compiler: ${{ matrix.dc }}
19 | - name: Test
20 | run: |
21 | dub test
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dub.selections.json
2 | .dub
3 | tests/*/tests
4 | __test__*__
5 | *.obj
6 | *.[oa]
7 | *.so
8 | *.lib
9 | *.dll
10 | *.sublime-project
11 | *.sublime-workspace
12 | msgpack-d-test-unittest
13 |
--------------------------------------------------------------------------------
/LICENSE_1_0.txt:
--------------------------------------------------------------------------------
1 | Boost Software License - Version 1.0 - August 17th, 2003
2 |
3 | Permission is hereby granted, free of charge, to any person or organization
4 | obtaining a copy of the software and accompanying documentation covered by
5 | this license (the "Software") to use, reproduce, display, distribute,
6 | execute, and transmit the Software, and to prepare derivative works of the
7 | Software, and to permit third-parties to whom the Software is furnished to
8 | do so, all subject to the following:
9 |
10 | The copyright notices in the Software and this entire statement, including
11 | the above license grant, this restriction and the following disclaimer,
12 | must be included in all copies of the Software, in whole or in part, and
13 | all derivative works of the Software, unless such copies or derivative
14 | works are solely in the form of machine-executable object code generated by
15 | a source language processor.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | [](https://github.com/msgpack/msgpack-d/actions/workflows/d.yml)
2 |
3 | # MessagePack for D
4 |
5 | MessagePack is a binary-based JSON-like serialization library.
6 |
7 | MessagePack for D is a pure D implementation of MessagePack.
8 |
9 | # Features
10 |
11 | * Small size and High performance
12 | * Zero copy serialization / deserialization
13 | * Streaming deserializer for non-contiguous IO situation
14 | * Supports D features (Ranges, Tuples, real type)
15 |
16 | Note: The `real` type is only supported in D.
17 | Don't use the `real` type when communicating with other programming languages.
18 | Note that `Unpacker` will raise an exception if a loss of precision occurs.
19 |
20 | ## Current Limitations
21 |
22 | * No circular references support
23 | * If you want to use the LDC compiler, you need at least version 0.15.2 beta2
24 |
25 | # Install
26 |
27 | Use dub to add it as a dependency:
28 |
29 | ```sh
30 | % dub install msgpack-d
31 | ```
32 |
33 | # Usage
34 |
35 | Example code can be found in the `example` directory.
36 |
37 | The documentation can be found [here](http://msgpack.github.io/msgpack-d/)
38 |
39 | ## pack / unpack
40 |
41 | msgpack-d is very simple to use. Use `pack` for serialization, and `unpack` for deserialization:
42 |
43 | ```D
44 | import std.file;
45 | import msgpack;
46 |
47 | struct S { int x; float y; string z; }
48 |
49 | void main()
50 | {
51 | S input = S(10, 25.5, "message");
52 |
53 | // serialize data
54 | ubyte[] inData = pack(input);
55 |
56 | // write data to a file
57 | write("file.dat", inData);
58 |
59 | // read data from a file
60 | ubyte[] outData = cast(ubyte[])read("file.dat");
61 |
62 | // unserialize the data
63 | S target = outData.unpack!S();
64 |
65 | // verify data is the same
66 | assert(target.x == input.x);
67 | assert(target.y == input.y);
68 | assert(target.z == input.z);
69 | }
70 | ```
71 |
72 | ### Feature: Skip serialization/deserialization of a specific field.
73 |
74 | Use the `@nonPacked` attribute:
75 |
76 | ```d
77 | struct User
78 | {
79 | string name;
80 | @nonPacked int level; // pack / unpack will ignore the 'level' field
81 | }
82 | ```
83 |
84 | ### Feature: Use your own serialization/deserialization routines for custom class and struct types.
85 |
86 | msgpack-d provides the functions `registerPackHandler` / `registerUnpackHandler` to allow you
87 | to use custom routines during the serialization or deserialization of user-defined class and struct types.
88 | This feature is especially useful when serializing a derived class object when that object is statically
89 | typed as a base class object.
90 |
91 | For example:
92 |
93 | ```d
94 | class Document { }
95 | class XmlDocument : Document
96 | {
97 | this() { }
98 | this(string name) { this.name = name; }
99 | string name;
100 | }
101 |
102 | void xmlPackHandler(ref Packer p, ref XmlDocument xml)
103 | {
104 | p.pack(xml.name);
105 | }
106 |
107 | void xmlUnpackHandler(ref Unpacker u, ref XmlDocument xml)
108 | {
109 | u.unpack(xml.name);
110 | }
111 |
112 | void main()
113 | {
114 | /// Register the 'xmlPackHandler' and 'xmlUnpackHandler' routines for
115 | /// XmlDocument object instances.
116 | registerPackHandler!(XmlDocument, xmlPackHandler);
117 | registerUnpackHandler!(XmlDocument, xmlUnpackHandler);
118 |
119 | /// Now we can serialize/deserialize XmlDocument object instances via a
120 | /// base class reference.
121 | Document doc = new XmlDocument("test.xml");
122 | auto data = pack(doc);
123 | XmlDocument xml = unpack!XmlDocument(data);
124 | assert(xml.name == "test.xml"); // xml.name is "test.xml"
125 | }
126 | ```
127 |
128 | In addition, here is also a method using `@serializedAs` attribute:
129 |
130 | ```d
131 | import std.datetime: Clock, SysTime;
132 | static struct SysTimePackProxy
133 | {
134 | static void serialize(ref Packer p, ref in SysTime tim)
135 | {
136 | p.pack(tim.toISOExtString());
137 | }
138 |
139 | static void deserialize(ref Unpacker u, ref SysTime tim)
140 | {
141 | string tmp;
142 | u.unpack(tmp);
143 | tim = SysTime.fromISOExtString(tmp);
144 | }
145 | }
146 | static struct LogData
147 | {
148 | string msg;
149 | string file;
150 | ulong line;
151 | @serializedAs!SysTimePackProxy SysTime timestamp;
152 |
153 | this(string message, string file = __FILE__, ulong line = __LINE__)
154 | {
155 | this.msg = message;
156 | this.file = file;
157 | this.line = line;
158 | this.timestamp = Clock.currTime();
159 | }
160 | }
161 |
162 | void main()
163 | {
164 | /// Now we can serialize/deserialize LogData
165 | LogData[] logs;
166 | logs ~= LogData("MessagePack is nice!");
167 | auto data = pack(logs);
168 | LogData[] datas = unpack!(LogData[])(data);
169 | assert(datas[0].timestamp.toString() == datas[0].timestamp.toString());
170 | }
171 | ```
172 |
173 | ## The PackerImpl / Unpacker / StreamingUnpacker types
174 |
175 | These types are used by the `pack` and `unpack` functions.
176 |
177 | 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.
178 |
179 | # Links
180 |
181 | * [The MessagePack Project](http://msgpack.org/)
182 |
183 | The official MessagePack protocol website.
184 |
185 | * [msgpack-d's issue tracker](https://github.com/msgpack/msgpack-d/issues)
186 |
187 | Use this issue tracker to review and file bugs in msgpack-d.
188 |
189 | * [MessagePack's Github](http://github.com/msgpack/)
190 |
191 | Other language bindings and implementations of the msgpack protocol can be found here.
192 |
193 | # Copyright
194 |
195 | Copyright (c) 2010- Masahiro Nakagawa
196 |
197 | # License
198 |
199 | Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/users/license.html).
200 |
--------------------------------------------------------------------------------
/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "msgpack-d",
3 | "description": "MessagePack for D.",
4 | "authors": ["Masahiro Nakagawa"],
5 | "homepage": "https://github.com/msgpack/msgpack-d",
6 | "license": "Boost Software License, Version 1.0",
7 | "copyright": "Copyright (c) 2010- Masahiro Nakagawa",
8 |
9 | "configurations": [
10 | {
11 | "name": "default",
12 | "targetType": "library"
13 | },
14 | {
15 | "name": "unittest",
16 | "dflags": ["-dip25", "-dip1000"]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/example/array_test.d:
--------------------------------------------------------------------------------
1 | import std.conv;
2 | import std.stdio;
3 | import std.datetime;
4 | import msgpack;
5 |
6 | struct A {
7 | int x;
8 | }
9 |
10 | struct Foo
11 | {
12 | A[] a;
13 | }
14 |
15 | void main()
16 | {
17 | Foo foo;
18 | foreach (a; 'a' .. 'z')
19 | foreach (b; 'a' .. 'z')
20 | foreach (c; 'a' .. 'z')
21 | foo.a ~= A();
22 |
23 | auto sw = StopWatch(AutoStart.yes);
24 | ubyte[] data = msgpack.pack(foo);
25 | writeln(sw.peek.usecs);
26 |
27 | auto sw2 = StopWatch(AutoStart.yes);
28 | Foo foo1 = msgpack.unpack(data).as!(typeof(foo));
29 | writeln(sw2.peek.usecs);
30 |
31 | assert(foo == foo1);
32 |
33 | Foo foo2;
34 | auto sw3 = StopWatch(AutoStart.yes);
35 | msgpack.unpack(data, foo2);
36 | writeln(sw3.peek.usecs);
37 |
38 | assert(foo == foo2);
39 | }
40 |
--------------------------------------------------------------------------------
/example/attribute.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Attribute usage
5 | */
6 |
7 | import msgpack;
8 |
9 | struct Hoge
10 | {
11 | string f1;
12 | @nonPacked int f2;
13 | }
14 |
15 | void main()
16 | {
17 | Hoge hoge = Hoge("hoge", 10);
18 | Hoge fuga;
19 |
20 | unpack(pack(hoge), fuga);
21 | assert(hoge.f1 == fuga.f1);
22 | assert(hoge.f2 != fuga.f2);
23 | assert(fuga.f2 == int.init);
24 | }
25 |
--------------------------------------------------------------------------------
/example/compare_direct_stream.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Compares direct conversion with stream.
5 | */
6 |
7 | import std.datetime;
8 | import std.stdio;
9 | import std.typecons;
10 |
11 | import msgpack;
12 |
13 |
14 | void main()
15 | {
16 | // tuple
17 | auto test1 = tuple(new int[](100), "MessagePack!", [1:2.0, 3:4.0, 5:6.0, 7:8.0]);
18 | auto data1 = pack(test1);
19 |
20 | // stream
21 | void s1()
22 | {
23 | auto result = unpack(data1).as!(typeof(test1));
24 | }
25 |
26 | // direct conversion
27 | void d1()
28 | {
29 | typeof(test1) result;
30 | unpack(data1, result);
31 | }
32 |
33 | // array
34 | auto test2 = new int[](1000);
35 | auto data2 = pack(test2);
36 |
37 | // stream
38 | void s2()
39 | {
40 | auto result = unpack(data2).as!(typeof(test2));
41 | }
42 |
43 | // direct conversion
44 | void d2()
45 | {
46 | typeof(test2) result;
47 | unpack(data2, result);
48 | }
49 |
50 | auto times = benchmark!(s1, d1, s2, d2)(1000);
51 | writeln("Stream(Tuple):", times[0].msecs);
52 | writeln("Direct(Tuple):", times[1].msecs);
53 | writeln("Stream(Array):", times[2].msecs);
54 | writeln("Direct(Array):", times[3].msecs);
55 | }
56 |
--------------------------------------------------------------------------------
/example/compare_json.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Compares std.json
5 | */
6 |
7 | import std.datetime;
8 | import std.json;
9 | import std.stdio;
10 |
11 | import msgpack;
12 |
13 |
14 | void main()
15 | {
16 | JSONValue jsonObj = parseJSON(`[12, "foo", true, 0.23, {"1":1}, [1, 2]]`);
17 |
18 | void f1()
19 | {
20 | parseJSON(toJSON(jsonObj));
21 | }
22 |
23 | Value mpObj = unpack(pack(12, "foo", true, 0.23, ["1":1], [1, 2]));
24 |
25 | void f2()
26 | {
27 | unpack(pack(mpObj));
28 | }
29 |
30 | auto times = benchmark!(f1, f2)(10000);
31 | writeln("JSON: ", times[0].msecs);
32 | writeln("Msgpack: ", times[1].msecs);
33 | }
34 |
--------------------------------------------------------------------------------
/example/convert_json.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Converting to/from JSON usage
5 | */
6 |
7 | import std.stdio;
8 |
9 | import msgpack;
10 | import std.json;
11 |
12 |
13 | void main()
14 | {
15 | struct Simple
16 | {
17 | int a = 5;
18 | string b = "hello";
19 | double c = 3.14;
20 | char d = '!';
21 | @nonPacked string e = "world";
22 | bool f = true;
23 | }
24 | auto simple = Simple();
25 | Value val = simple.pack().unpack();
26 | writeln(val.toJSONValue());
27 | val = simple.pack!true().unpack();
28 | writeln(val.toJSONValue());
29 |
30 | string jsonString = `[30, 30.5, true, "hello", "40"]`;
31 | val = parseJSON(jsonString).fromJSONValue();
32 | assert(val.pack() !is null);
33 | assert(val.type == Value.Type.array);
34 | foreach (v; val.via.array)
35 | {
36 | writeln(v.type);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/custom.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * User-defined class sample
5 | */
6 |
7 | import std.stdio, std.math;
8 |
9 | import msgpack;
10 |
11 |
12 | enum Attr : ubyte
13 | {
14 | A, B
15 | }
16 |
17 |
18 | struct User
19 | {
20 | string name;
21 | uint age;
22 | Attr attr;
23 | }
24 |
25 |
26 | void main()
27 | {
28 | User user = User("Foo", 20, Attr.B), other;
29 |
30 | unpack(pack(user), other);
31 |
32 | writeln("name: ", other.name, "(", other.age, ", ", other.attr, ")");
33 |
34 | // Complex data-structure
35 |
36 | auto btree = new BTree!(int, string);
37 | int[] keys = [3, 6, 8, 10, 1, 5, 20];
38 | string[] values = ["Foo", "Baz", "Bar", "Hoge", "Fuga", "Piyo", "ham"];
39 |
40 | foreach (i, key; keys)
41 | btree.insert(key, values[i]);
42 | btree.print();
43 |
44 | auto result = new BTree!(int, string);
45 |
46 | unpack(pack(btree), result);
47 |
48 | result.print();
49 | }
50 |
51 |
52 | /**
53 | * Simple B-Tree.
54 | */
55 | class BTree(Key, Data)
56 | {
57 | private:
58 | immutable uint MAX_CHILD; // sub-tree size(m-tree)
59 | immutable uint HALF_CHILD; // min number((m + 1) / 2)
60 |
61 | enum NodeType
62 | {
63 | Internal,
64 | Leaf
65 | }
66 |
67 | static class Node
68 | {
69 | NodeType type;
70 |
71 | union
72 | {
73 | struct // internal
74 | {
75 | uint childs; // child number
76 | Key[] low; // min element of each sub-tree
77 | Node[] child; // sub-tree size(m)
78 | }
79 |
80 | struct // leaf
81 | {
82 | Key key;
83 | Data data;
84 | }
85 | }
86 |
87 | this(in uint num) { low.length = child.length = num; }
88 |
89 | void toMsgpack(Packer)(ref Packer packer) const
90 | {
91 | if (type == NodeType.Internal)
92 | packer.packArray(childs, low.length, low, child);
93 | else
94 | packer.packArray(key, data);
95 | }
96 |
97 | void fromMsgpack(ref Unpacker unpacker)
98 | {
99 | if (unpacker.beginArray() == 4) {
100 | uint max;
101 |
102 | unpacker.unpack(childs, max, low);
103 | child.length = unpacker.beginArray();
104 | foreach (i; 0..child.length)
105 | unpacker.unpack(child[i], max);
106 | low.length = child.length = max;
107 | } else {
108 | type = NodeType.Leaf;
109 | unpacker.unpack(key, data);
110 | }
111 | }
112 | }
113 |
114 | Node root;
115 |
116 |
117 | public:
118 | /**
119 | * Params:
120 | * num = node size.
121 | */
122 | this(in uint num = 5)
123 | in
124 | {
125 | assert(num >= 2, "The prerequisite of m-tree(size must be larger than 2)");
126 | }
127 | body
128 | {
129 | MAX_CHILD = num;
130 | HALF_CHILD = cast(uint)ceil(cast(real)MAX_CHILD / 2);
131 | }
132 |
133 | /**
134 | * Params:
135 | * key = key that represents a data to insert.
136 | * data = data to insert.
137 | *
138 | * Returns:
139 | * node that inserted data.
140 | */
141 | Node insert(in Key key, in Data data)
142 | {
143 | /*
144 | * Params:
145 | * n = node to insert element
146 | * nNode = new node(null if not create).
147 | * lowest = min element in $(D_PARAM nNode)
148 | *
149 | * Returns:
150 | * node that element was inserted.
151 | */
152 | Node _insert(ref Node n, out Node nNode, out Key lowest)
153 | {
154 | Node node = n; // for updating data
155 |
156 | if (node.type == NodeType.Leaf) {
157 | if (node.key == key) {
158 | return null;
159 | } else {
160 | Node elem = allocNode();
161 | elem.type = NodeType.Leaf;
162 | elem.key = key;
163 | elem.data = data;
164 |
165 | if (elem.key < node.key) {
166 | n = elem;
167 | nNode = node;
168 | lowest = node.key;
169 | } else {
170 | nNode = elem;
171 | lowest = elem.key;
172 | }
173 |
174 | return elem;
175 | }
176 | } else {
177 | int i, j, pos; // pos = position to insert.
178 | Key xLowest; // lowest for recursion.
179 | Node xNode, ret; // nNode for recursion.
180 |
181 | pos = locateSubTree(node, key);
182 | ret = _insert(node.child[pos], xNode, xLowest);
183 |
184 | // Doesn't create and patition.
185 | if (xNode is null)
186 | return ret;
187 |
188 | if (node.childs < MAX_CHILD) {
189 | for (i = node.childs - 1; i > pos; i--) {
190 | node.child[i+1] = node.child[i];
191 | node.low[i+1] = node.low[i];
192 | }
193 | node.child[pos+1] = xNode;
194 | node.low[pos+1] = xLowest;
195 | node.childs++;
196 | return ret;
197 | } else {
198 | Node elem = allocNode();
199 | elem.type = NodeType.Internal;
200 |
201 | // insert to node side or elem side?
202 | if (pos < HALF_CHILD - 1) {
203 | for (i = HALF_CHILD - 1, j = 0; i < MAX_CHILD; i++, j++) {
204 | elem.child[j] = node.child[i];
205 | elem.low[j] = node.low[i];
206 | }
207 |
208 | for (i = HALF_CHILD - 2; i > pos; i--) {
209 | node.child[i+1] = node.child[i];
210 | node.low[i+1] = node.low[i];
211 | }
212 | node.child[pos+1] = xNode;
213 | node.low[pos+1] = xLowest;
214 | } else {
215 | for (i = MAX_CHILD - 1, j = MAX_CHILD - HALF_CHILD; i >= HALF_CHILD; i--) {
216 | if (i == pos) {
217 | elem.child[j] = xNode;
218 | elem.low[j--] = xLowest;
219 | }
220 | elem.child[j] = node.child[i];
221 | elem.low[j--] = node.low[i];
222 | }
223 |
224 | if (pos < HALF_CHILD) {
225 | elem.child[0] = xNode;
226 | elem.low[0] = xLowest;
227 | }
228 | }
229 |
230 | node.childs = HALF_CHILD;
231 | elem.childs = MAX_CHILD+1 - HALF_CHILD;
232 |
233 | nNode = elem;
234 | lowest = elem.low[0];
235 |
236 | return ret;
237 | }
238 | }
239 | }
240 |
241 | if (root is null) {
242 | root = allocNode();
243 | root.type = NodeType.Leaf;
244 | root.key = key;
245 | root.data = data;
246 | return root;
247 | } else {
248 | Key lowest;
249 | Node ret, newNode;
250 |
251 | ret = _insert(root, newNode, lowest);
252 |
253 | // new node and growl height if patitioned.
254 | if (newNode !is null) {
255 | Node elem = allocNode();
256 | elem.type = NodeType.Internal;
257 | elem.childs = 2;
258 | elem.child[0] = root;
259 | elem.child[1] = newNode;
260 | elem.low[1] = lowest;
261 | root = elem;
262 | }
263 |
264 | return ret;
265 | }
266 | }
267 |
268 | /**
269 | * Params:
270 | * key = key to delete.
271 | *
272 | * Returns:
273 | * true if deleted.
274 | */
275 | bool remove(in Key key)
276 | {
277 | enum State
278 | {
279 | Nothing,
280 | Removed,
281 | Reconfig
282 | }
283 |
284 | /*
285 | * Params:
286 | * n = node to remove.
287 | * result = node change because of an remove.
288 | *
289 | * Returns:
290 | * true if removed.
291 | */
292 | bool _remove(ref Node n, out State result)
293 | {
294 | if (n.type == NodeType.Leaf) {
295 | if (n.key == key) {
296 | result = State.Removed;
297 | delete n;
298 | return true;
299 | } else {
300 | return false;
301 | }
302 | } else {
303 | int pos, sub; // sub-tree position to remove, for restructure.
304 | bool ret, merged; // delete?, merge sub-tree?
305 | State state; // sub-tree state.
306 |
307 | pos = locateSubTree(n, key);
308 | ret = _remove(n.child[pos], state);
309 |
310 | if (state == State.Nothing)
311 | return ret;
312 |
313 | if (state == State.Reconfig) {
314 | sub = pos == 0 ? 0 : pos - 1;
315 | merged = revisionNodes(n, sub);
316 |
317 | if (merged)
318 | pos = sub+1;
319 | }
320 |
321 | if (state == State.Removed || merged) {
322 | // sub-tree compaction
323 | for (int i = pos; i < n.childs - 1; i++) {
324 | n.child[i] = n.child[i+1];
325 | n.low[i] = n.low[i+1];
326 | }
327 |
328 | if (--n.childs < HALF_CHILD)
329 | result = State.Reconfig;
330 | }
331 |
332 | return ret;
333 | }
334 | }
335 |
336 | if (root is null) {
337 | return false;
338 | } else {
339 | State result;
340 | bool ret = _remove(root, result);
341 |
342 | if (result == State.Removed) {
343 | root = null;
344 | } else if (result == State.Reconfig && root.childs == 1) {
345 | Node n = root;
346 | root = root.child[0];
347 | delete n;
348 | }
349 |
350 | return ret;
351 | }
352 | }
353 |
354 | /**
355 | * Params:
356 | * key = key to search.
357 | *
358 | * Returns:
359 | * finded node.
360 | */
361 | Node search(in Key key)
362 | {
363 | if (root is null) {
364 | return null;
365 | } else {
366 | int i;
367 | Node n = root;
368 |
369 | // searches internal node until find leaf.
370 | while (n.type == NodeType.Internal) {
371 | i = locateSubTree(n, key);
372 | n = n.child[i];
373 | }
374 |
375 | if (key == n.key)
376 | return n;
377 | else
378 | return null;
379 | }
380 | }
381 |
382 | void print()
383 | {
384 | void _print(ref Node n)
385 | {
386 | if (n.type == NodeType.Leaf) {
387 | writefln("[%x] Leaf : %s Data : %s", &n, n.key, n.data);
388 | } else {
389 | writef("[%x] Childs %d [%x], ", &n, n.childs, &n.child[0]);
390 |
391 | foreach (i; 0..MAX_CHILD)
392 | writef("%d[%x] ", n.low[i], &n.child[i]);
393 | writeln();
394 |
395 | foreach (i; 0..n.childs)
396 | _print(n.child[i]);
397 | }
398 | }
399 |
400 | if (root is null)
401 | writefln("Element is nothing");
402 | else
403 | _print(root);
404 | }
405 |
406 | // for MessagePack
407 |
408 | void toMsgpack(Packer)(ref Packer packer) const
409 | {
410 | packer.pack(root);
411 | }
412 |
413 | void fromMsgpack(ref Unpacker unpacker)
414 | {
415 | unpacker.unpack(root, MAX_CHILD);
416 | }
417 |
418 |
419 | private:
420 | /*
421 | * Returns:
422 | * new node.
423 | */
424 | Node allocNode()
425 | {
426 | return new Node(MAX_CHILD);
427 | }
428 |
429 | /*
430 | * searches $(D_PARAM key) element in sub-tree of $(D_PARAM node).
431 | *
432 | * Params:
433 | * node = node to search.
434 | * key = key to search in $(D_PARAM node).
435 | *
436 | * Returns:
437 | * finded position.
438 | */
439 | int locateSubTree(ref Node node, in Key key) const
440 | {
441 | for (int i = node.childs - 1; i > 0; i--)
442 | if (key >= node.low[i])
443 | return i;
444 | return 0;
445 | }
446 |
447 | /*
448 | * Params:
449 | * n = revision node.
450 | * x = position to sub-tree of revision node.
451 | *
452 | * Returns:
453 | * true if merged.
454 | */
455 | bool revisionNodes(ref Node n, in uint x)
456 | {
457 | int i;
458 | Node a, b; // each sub-tree.
459 | uint an, bn; // child number of each sub-tree.
460 |
461 | a = n.child[x];
462 | b = n.child[x+1];
463 | an = a.childs;
464 | bn = b.childs;
465 | b.low[0] = n.low[x+1];
466 |
467 | if (an + bn <= MAX_CHILD) { // merge
468 | for (i = 0; i < bn; i++) {
469 | a.child[an+i] = b.child[i];
470 | a.low[an+i] = b.low[i];
471 | }
472 |
473 | a.childs += bn;
474 | delete b;
475 |
476 | return true;
477 | } else { // partition
478 | uint pivot = (an + bn) / 2; // pivot to patition.
479 | uint move; // element number to copy.
480 |
481 | if (an > pivot) {
482 | move = an - pivot;
483 |
484 | for (i = bn - 1; i >= 0; i--) {
485 | b.child[move+i] = b.child[i];
486 | b.low[move+i] = b.low[i];
487 | }
488 | // copy element a to b.
489 | for (i = 0; i < move; i++) {
490 | b.child[i] = a.child[pivot+i];
491 | b.low[i] = a.low[pivot+i];
492 | }
493 | } else {
494 | move = pivot - an;
495 |
496 | // copy element b to a.
497 | for (i = 0; i < move; i++) {
498 | a.child[an+i] = b.child[i];
499 | a.low[an+i] = b.low[i];
500 | }
501 | for (i = 0; i < bn - move; i++) {
502 | b.child[i] = b.child[move+i];
503 | b.low[i] = b.low[move+i];
504 | }
505 | }
506 |
507 | a.childs = pivot;
508 | b.childs = an + bn - pivot;
509 | n.low[x+1] = b.low[0];
510 |
511 | return false;
512 | }
513 | }
514 | }
515 |
--------------------------------------------------------------------------------
/example/custom_handler.d:
--------------------------------------------------------------------------------
1 | import msgpack;
2 | import std.array;
3 | import std.stdio;
4 | import std.variant;
5 |
6 | class A { }
7 | class C : A
8 | {
9 | int num;
10 | this(int n) { num = n; }
11 | }
12 |
13 | void cPackHandler(ref Packer p, ref C c)
14 | {
15 | writeln("Pack C: ", c.num);
16 | p.pack(c.num);
17 | }
18 |
19 | void cUnpackHandler(ref Unpacker u, ref C c)
20 | {
21 | writeln("Unpack C: ", c.num);
22 | u.unpack(c.num);
23 | }
24 |
25 | void vPackHandler(ref Packer p, ref Variant v)
26 | {
27 | writeln("pack Variant: ", v);
28 | p.pack(v.get!bool);
29 | }
30 |
31 | void vUnpackHandler(ref Unpacker u, ref Variant v)
32 | {
33 | writeln("unpack Variant: ", v);
34 | bool b;
35 | u.unpack(b);
36 | v = b;
37 | }
38 |
39 | void main()
40 | {
41 | registerPackHandler!(C, cPackHandler);
42 | registerUnpackHandler!(C, cUnpackHandler);
43 | registerPackHandler!(Variant, vPackHandler);
44 | registerUnpackHandler!(Variant, vUnpackHandler);
45 |
46 | {
47 | Packer p;
48 | A c = new C(1000);
49 | p.pack(c);
50 |
51 | A c2 = new C(5);
52 | unpack(p.stream.data, c2);
53 | assert(1000 == (cast(C)c2).num);
54 | }
55 | {
56 | Packer p;
57 |
58 | Variant v = true;
59 | p.pack(v);
60 |
61 | Variant v2 = 10;
62 | unpack(p.stream.data, v2);
63 | assert(v2 == true);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/example/map_test.d:
--------------------------------------------------------------------------------
1 | import std.conv;
2 | import std.stdio;
3 | import std.datetime;
4 | import msgpack;
5 |
6 | struct A {
7 | int x;
8 | }
9 |
10 | struct Foo
11 | {
12 | A[string] a;
13 | }
14 |
15 | void main()
16 | {
17 | Foo foo;
18 | foreach (a; 'a' .. 'z')
19 | foreach (b; 'a' .. 'z')
20 | foreach (c; 'a' .. 'z')
21 | foo.a[to!string(a) ~ to!string(b) ~ to!string(c)] = A();
22 |
23 | auto sw = StopWatch(AutoStart.yes);
24 | ubyte[] data = msgpack.pack(foo);
25 | writeln(sw.peek.usecs);
26 |
27 | auto sw2 = StopWatch(AutoStart.yes);
28 | Foo foo1 = msgpack.unpack(data).as!(typeof(foo));
29 | writeln(sw2.peek.usecs);
30 |
31 | assert(foo == foo1);
32 |
33 | Foo foo2;
34 | auto sw3 = StopWatch(AutoStart.yes);
35 | msgpack.unpack(data, foo2);
36 | writeln(sw3.peek.usecs);
37 |
38 | assert(foo == foo2);
39 | }
40 |
--------------------------------------------------------------------------------
/example/register.d:
--------------------------------------------------------------------------------
1 | import msgpack;
2 | import std.stdio;
3 |
4 | class A
5 | {
6 | string str = "foo";
7 | }
8 |
9 | class C : A
10 | {
11 | int num;
12 | this(int n) { num = n; }
13 | }
14 |
15 | void main()
16 | {
17 | registerClass!(C);
18 |
19 | {
20 | Packer p;
21 | A c = new C(1000);
22 | p.pack(c);
23 |
24 | A c2 = new C(5);
25 | unpack(p.stream.data, c2);
26 | assert(1000 == (cast(C)c2).num);
27 | }
28 | {
29 | Packer p;
30 | C c = new C(1000);
31 | p.pack(c);
32 |
33 | C c2 = new C(5);
34 | unpack(p.stream.data, c2);
35 | assert(1000 == (cast(C)c2).num);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/example/simple.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Serializer and Stream Deserializer usage
5 | */
6 |
7 | import std.array;
8 | import std.stdio;
9 |
10 | import msgpack;
11 |
12 |
13 | void main()
14 | {
15 | auto packer = packer(appender!(ubyte[])());
16 |
17 | packer.packArray(null, true, "Hi!", -1, [1, 2], '!');
18 |
19 | auto unpacker = StreamingUnpacker(packer.stream.data);
20 |
21 | if (unpacker.execute()) {
22 | foreach (obj; unpacker.purge())
23 | writeln(obj.type);
24 | } else {
25 | writeln("Serialized object is too large!");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example/stream.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Stream deserialization usage
5 | */
6 |
7 | import std.array;
8 | import std.concurrency;
9 | import std.exception;
10 | import std.stdio;
11 |
12 | import msgpack;
13 |
14 |
15 | void deserializer()
16 | {
17 | auto unpacker = StreamingUnpacker(cast(ubyte[])null);
18 | bool endLoop;
19 |
20 | while (true) {
21 | receive((immutable(ubyte)[] data) { unpacker.feed(data); },
22 | (bool end) { endLoop = end; });
23 |
24 | if (endLoop)
25 | break;
26 |
27 | while (unpacker.execute()) {
28 | auto unpacked = unpacker.purge();
29 | writeln("Type: ", unpacked.type);
30 | writeln("Value: ", unpacked.as!(string));
31 | }
32 |
33 | if (unpacker.size >= 100)
34 | throw new Exception("Too large!");
35 | }
36 | }
37 |
38 |
39 | void main()
40 | {
41 | string message = "Hell";
42 | foreach (i; 0..93) // Throws Exception if 94
43 | message ~= 'o';
44 |
45 | auto packed = pack(message);
46 | auto data = packed.assumeUnique();
47 | auto tid = spawn(&deserializer);
48 |
49 | while (!data.empty) {
50 | auto limit = data.length >= 10 ? 10 : data.length;
51 |
52 | tid.send(data[0..limit]);
53 | data = data[limit..$];
54 | }
55 |
56 | tid.send(true);
57 | }
58 |
--------------------------------------------------------------------------------
/example/unpacker_foreach.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * Stream Deserializer with foreach.
5 | */
6 |
7 | import std.stdio;
8 |
9 | import msgpack;
10 |
11 |
12 | void main()
13 | {
14 | // create 3 MessagePack objects([1, 0.1L], true, "foobarbaz")
15 | auto test1 = pack(1, 0.1L) ~ pack(true);
16 | auto test2 = pack("foobarbaz");
17 |
18 | // split data to deserialize test
19 | test1 ~= test2[0..2];
20 | test2 = test2[2..$];
21 |
22 | auto unpacker = StreamingUnpacker(test1);
23 |
24 | foreach (unpacked; unpacker) {
25 | if (unpacked.type == Value.Type.array) {
26 | foreach (obj; unpacked) {
27 | switch (obj.type) {
28 | case Value.Type.unsigned: writeln(obj.as!(uint)); break;
29 | case Value.Type.floating: writeln(obj.as!(real)); break;
30 | default:
31 | throw new Exception("Unknown type");
32 | }
33 | }
34 | } else {
35 | writeln(unpacked.as!(bool));
36 | }
37 | }
38 |
39 | unpacker.feed(test2);
40 |
41 | foreach (unpacked; unpacker)
42 | writeln(unpacked.as!(string));
43 | }
44 |
--------------------------------------------------------------------------------
/html/candydoc/candy.ddoc:
--------------------------------------------------------------------------------
1 | D = $(B $0)
2 |
3 | DDOC =
4 |
5 |
6 |
7 | $(TITLE)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | $(TITLE)$(BODY) |
18 |
23 |
24 |
25 | $(ADD_MODULES)
26 |
27 |
28 |
29 | DDOC_DECL =
30 |
31 | $(DT $0)
32 |
33 |
34 |
35 | DDOC_PSYMBOL =
36 | $0
37 |
38 |
39 |
40 | DDOC_MEMBERS =
41 |
42 | $(DL $0)
43 |
44 |
45 |
46 | DDOC_PARAM_ID =
47 | $0 |
48 |
49 |
50 | DDOC_PARAM =$0
51 | ADD_MODULES =
52 | MODULE =explorer.packageExplorer.addModule("$0");
53 |
--------------------------------------------------------------------------------
/html/candydoc/explorer.js:
--------------------------------------------------------------------------------
1 | /* This file is a part of CanDyDOC fileset.
2 | File is written by Victor Nakoryakov and placed into the public domain.
3 |
4 | This file is javascript with classes that represents explorer window.
5 | And things related to navigation. */
6 |
7 | var explorer = new Explorer();
8 |
9 | ///////////////////////////////////////////////////////////////////////////////
10 | // Current symbol marker class constructor
11 | ///////////////////////////////////////////////////////////////////////////////
12 | function Marker()
13 | {
14 | this.top = document.createElement("div");
15 | this.middle = document.createElement("div");
16 | this.bottom = document.createElement("div");
17 | this.container = document.createElement("div");
18 |
19 | this.setTo = function(term)
20 | {
21 | // find definition related to `term`
22 | var def = term.nextSibling;
23 | while (def && def.nodeName != "DD")
24 | def = def.nextSibling;
25 |
26 | var defHeight = 0;
27 | var childrenHeight = 0; // children of current declaration
28 | if (def)
29 | {
30 | defHeight = def.offsetHeight;
31 | var child = def.firstChild;
32 |
33 | // traverse until DL tag, until children definition
34 | while (child && child.nodeName != "DL")
35 | child = child.nextSibling;
36 |
37 | if (child)
38 | childrenHeight = child.offsetHeight;
39 | }
40 |
41 | this.top.style.height = term.offsetHeight;
42 | this.middle.style.height = defHeight - childrenHeight;
43 | this.bottom.style.height = childrenHeight;
44 |
45 | if (childrenHeight == 0)
46 | this.bottom.style.display = "none";
47 | else
48 | this.bottom.style.display = "";
49 |
50 | this.container.style.left = getLeft(term) - 8;
51 | this.container.style.top = getTop(term);
52 | this.container.style.display = "";
53 | }
54 |
55 | ///////////////////////////////////////////////////////////////////////////
56 | this.container.style.position = "absolute";
57 | this.container.style.display = "none";
58 |
59 | this.top.className = "markertop";
60 | this.middle.className = "markermiddle";
61 | this.bottom.className = "markerbottom";
62 |
63 | this.container.appendChild(this.top);
64 | this.container.appendChild(this.middle);
65 | this.container.appendChild(this.bottom);
66 |
67 | //document.body.appendChild( this.container );
68 |
69 | // Workaround bug in IE 5/6. We can not append anything to document body until
70 | // full page load.
71 | window.marker = this;
72 | if (window.addEventListener)
73 | window.addEventListener("load", new Function("document.body.appendChild( window.marker.container );"), false);
74 | else if (window.attachEvent)
75 | window.attachEvent("onload", new Function("document.body.appendChild( window.marker.container );"));
76 | }
77 |
78 | ///////////////////////////////////////////////////////////////////////////////
79 | // Outline class constructor
80 | ///////////////////////////////////////////////////////////////////////////////
81 | function Outline()
82 | {
83 | this.tree = new TreeView();
84 | this.mountPoint = null;
85 | this.writeEnabled = false;
86 | this.marker = new Marker();
87 | this.classRegExp = new RegExp;
88 | this.structRegExp = new RegExp;
89 | this.enumRegExp = new RegExp;
90 | this.templateRegExp = new RegExp;
91 | this.aliasRegExp = new RegExp;
92 | this.funcRegExp = new RegExp;
93 |
94 | this.incSymbolLevel = function()
95 | {
96 | if (this.mountPoint == null)
97 | this.mountPoint = this.tree.children[ 0 ];
98 | else
99 | this.mountPoint = this.mountPoint.lastChild();
100 | }
101 |
102 | this.decSymbolLevel = function()
103 | {
104 | // place icons near items according to extracted below type
105 | for (var i = 0; i < this.mountPoint.children.length; ++i)
106 | {
107 | child = this.mountPoint.children[i];
108 | var term = child.termRef;
109 |
110 | // find first span node
111 | var n = term.firstChild;
112 | while (n && n.nodeName != "SPAN")
113 | n = n.nextSibling;
114 |
115 | if (!n) // shouldn't happen
116 | continue;
117 |
118 | var iconSrc;
119 | if (n.firstChild.nodeName == "#text")
120 | {
121 | var text = n.firstChild.data; // text before declaration
122 |
123 | if ( this.classRegExp.test(text) )
124 | iconSrc = "candydoc/img/outline/class.gif";
125 | else if ( this.structRegExp.test(text) )
126 | iconSrc = "candydoc/img/outline/struct.gif";
127 | else if ( this.enumRegExp.test(text) )
128 | iconSrc = "candydoc/img/outline/enum.gif";
129 | else if ( this.templateRegExp.test(text) )
130 | iconSrc = "candydoc/img/outline/template.gif";
131 | else if ( this.aliasRegExp.test(text) )
132 | iconSrc = "candydoc/img/outline/alias.gif";
133 | else // function or variable? check whether '(' ')' exists on the right
134 | {
135 | var np = n.firstChild;
136 | while (np && np.nodeName != "SCRIPT") // find our script "onDecl"
137 | np = np.nextSibling;
138 |
139 | if (np && np.nextSibling && np.nextSibling.nodeName == "#text" &&
140 | this.funcRegExp.test(np.nextSibling.data))
141 | {
142 | iconSrc = "candydoc/img/outline/func.gif";
143 | }
144 | else
145 | iconSrc = "candydoc/img/outline/var.gif";
146 | }
147 | }
148 | else // enum member ?
149 | iconSrc = "candydoc/img/outline/var.gif";
150 |
151 | child.icon.src = iconSrc;
152 | child.icon.width = 16;
153 | child.icon.height = 16;
154 | }
155 |
156 | this.mountPoint = this.mountPoint.parentNode;
157 | }
158 |
159 | this.addDecl = function(decl)
160 | {
161 | function getLastLeaf(elem)
162 | {
163 | if (elem.childNodes.length > 0)
164 | return getLastLeaf(elem.lastChild);
165 | else
166 | return elem;
167 | }
168 |
169 | function getCurrentTerm()
170 | {
171 | var ret = getLastLeaf( document.getElementById("content") );
172 | while (ret && ret.nodeName != "DT")
173 | ret = ret.parentNode;
174 |
175 | return ret;
176 | }
177 |
178 | if (this.writeEnabled)
179 | {
180 | var node = this.mountPoint.createChild(decl);
181 | node.termRef = getCurrentTerm();
182 | node.setOnclick( new Function("explorer.outline.mark(this.termRef);") );
183 | }
184 | }
185 |
186 | this.mark = function(term)
187 | {
188 | this.marker.setTo(term);
189 | window.scrollTo(0, getTop(term) - getWindowHeight() / 6);
190 | }
191 |
192 |
193 | this.classRegExp.compile("(.*\b)?class(\b.*)?");
194 | this.structRegExp.compile("(.*\b)?struct(\b.*)?");
195 | this.enumRegExp.compile("(.*\b)?enum(\b.*)?");
196 | this.templateRegExp.compile("(.*\b)?template(\b.*)?");
197 | this.aliasRegExp.compile("(.*\b)?alias(\b.*)?");
198 | this.funcRegExp.compile(/.*\(.*/);
199 | }
200 |
201 |
202 |
203 |
204 | ///////////////////////////////////////////////////////////////////////////////
205 | // Package explorer class constructor
206 | ///////////////////////////////////////////////////////////////////////////////
207 | function PackageExplorer()
208 | {
209 | this.tree = new TreeView(true);
210 |
211 | this.addModule = function(mod)
212 | {
213 | var moduleIco = "candydoc/img/outline/module.gif";
214 | var packageIco = "candydoc/img/outline/package.gif";
215 |
216 | var path = mod.split("\.");
217 | var node = this.tree.branch(path[0]);
218 | if ( !node )
219 | node = this.tree.createBranch(path[0], (path.length == 1) ? moduleIco : packageIco);
220 |
221 | for (var i = 1; i < path.length; ++i)
222 | {
223 | var prev = node;
224 | node = node.child(path[i]);
225 | if (!node)
226 | node = prev.createChild(path[i], (path.length == i + 1) ? moduleIco : packageIco);
227 |
228 | if (path.length == i + 1)
229 | node.setRef(path[i] + ".html");
230 | }
231 | }
232 | }
233 |
234 |
235 |
236 | ///////////////////////////////////////////////////////////////////////////////
237 | // Explorer class constructor
238 | ///////////////////////////////////////////////////////////////////////////////
239 | function Explorer()
240 | {
241 | this.outline = new Outline();
242 | this.packageExplorer = new PackageExplorer();
243 | this.tabs = new Array();
244 | this.tabCount = 0;
245 |
246 | this.initialize = function(moduleName)
247 | {
248 | this.tabArea = document.getElementById("tabarea");
249 | this.clientArea = document.getElementById("explorerclient");
250 |
251 | // prevent text selection
252 | this.tabArea.onmousedown = new Function("return false;");
253 | this.tabArea.onclick = new Function("return true;");
254 | this.tabArea.onselectstart = new Function("return false;");
255 | this.clientArea.onmousedown = new Function("return false;");
256 | this.clientArea.onclick = new Function("return true;");
257 | this.clientArea.onselectstart = new Function("return false;");
258 |
259 | this.outline.tree.createBranch( moduleName, "candydoc/img/outline/module.gif" );
260 |
261 | // create tabs
262 | this.createTab("Outline", this.outline.tree.domEntry);
263 | this.createTab("Package", this.packageExplorer.tree.domEntry);
264 | }
265 |
266 | this.createTab = function(name, domEntry)
267 | {
268 | var tab = new Object();
269 | this.tabs[name] = tab;
270 | this.tabCount++;
271 |
272 | tab.domEntry = domEntry;
273 | tab.labelSpan = document.createElement("span");
274 |
275 | if (this.tabCount > 1)
276 | {
277 | tab.labelSpan.className = "inactivetab";
278 | tab.domEntry.style.display = "none";
279 | }
280 | else
281 | {
282 | tab.labelSpan.className = "activetab";
283 | tab.domEntry.style.display = "";
284 | }
285 |
286 | tab.labelSpan.appendChild( document.createTextNode(name) );
287 | tab.labelSpan.owner = this;
288 | tab.labelSpan.onclick = new Function("this.owner.setSelection('" + name + "');");
289 |
290 | this.tabArea.appendChild( tab.labelSpan );
291 | this.clientArea.appendChild( domEntry );
292 | }
293 |
294 | this.setSelection = function(tabName)
295 | {
296 | for (name in this.tabs)
297 | {
298 | this.tabs[name].labelSpan.className = "inactivetab";
299 | this.tabs[name].domEntry.style.display = "none";
300 | }
301 |
302 | this.tabs[tabName].labelSpan.className = "activetab";
303 | this.tabs[tabName].domEntry.style.display = "";
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/html/candydoc/ie56hack.css:
--------------------------------------------------------------------------------
1 | /* This file is a part of CanDyDOC fileset.
2 | File is written by Victor Nakoryakov and placed into the public domain.
3 |
4 | This file is CSS to work around IE6 and earlier bugs. It's included just
5 | in these browsers. */
6 |
7 |
8 | /* Some magic to emulate unsupported "position: fixed" style. */
9 | #tabarea
10 | {
11 | _position: absolute;
12 | _top: expression(eval(document.body.scrollTop+8));
13 | }
14 |
15 | /* ditto */
16 | #explorerclient
17 | {
18 | _position: absolute;
19 | _top: expression(eval(document.body.scrollTop+24));
20 | _height: expression(eval(document.body.clientHeight-48));
21 | }
22 |
--------------------------------------------------------------------------------
/html/candydoc/img/bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/bg.gif
--------------------------------------------------------------------------------
/html/candydoc/img/candydoc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/candydoc.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/alias.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/alias.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/bg.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/class.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/class.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/enum.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/enum.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/func.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/func.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/module.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/module.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/package.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/package.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/struct.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/struct.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/template.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/template.gif
--------------------------------------------------------------------------------
/html/candydoc/img/outline/var.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/outline/var.gif
--------------------------------------------------------------------------------
/html/candydoc/img/package/bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/package/bg.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/shim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/shim.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/tb.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/tb.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/tbr.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/tbr.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/tbrm.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/tbrm.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/tbrp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/tbrp.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/tr.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/tr.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/trm.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/trm.gif
--------------------------------------------------------------------------------
/html/candydoc/img/tree/trp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msgpack/msgpack-d/26ef07e16023483ad93e3f86374b19d0e541c924/html/candydoc/img/tree/trp.gif
--------------------------------------------------------------------------------
/html/candydoc/modules.ddoc:
--------------------------------------------------------------------------------
1 | MODULES =
2 | $(MODULE msgpack.buffer)
3 | $(MODULE msgpack.common)
4 | $(MODULE msgpack.msgpack)
5 | $(MODULE msgpack.object)
6 | $(MODULE msgpack.packer)
7 | $(MODULE msgpack.unpacker)
8 | $(MODULE msgpack.util)
9 |
--------------------------------------------------------------------------------
/html/candydoc/style.css:
--------------------------------------------------------------------------------
1 | /* This file is a part of CanDyDOC fileset.
2 | File is written by Victor Nakoryakov and placed into the public domain.
3 |
4 | This file is main CSS file of CanDyDOC. You may adjust some part of
5 | parameters to control how result documentation would looks like. See
6 | further documentation for details. */
7 |
8 |
9 |
10 | /* This controls how background would looks like and
11 | sets some document-scope defaults. */
12 | body
13 | {
14 | /* These parameters control default font. */
15 | font-family: Verdana, Arial, Helvetica, sans-serif;
16 | font-size: 10pt;
17 | color: #666666;
18 |
19 | /* These control look of background. Note that you have to use
20 | fixed background to keep documentation good-looking in
21 | IE6 and earlier. Otherwise whole *explorer* will jerk while
22 | scrolling. If you do not want to use background at all use
23 | some invalid url, e.g. url(foo). */
24 | background-color: #e6fcea;
25 | background: url(img/bg.gif) fixed;
26 |
27 | /* Don't touch. Necessary for IE6 and earlier. */
28 | height: 100%;
29 | }
30 |
31 |
32 |
33 | /* Style applied to all tables. Actualy there are two: one table is
34 | that contains contant and footer with CanDyDOC logo, and others
35 | are that contains functions' parameters description. */
36 | table
37 | {
38 | font-family: Verdana, Arial, Helvetica, sans-serif;
39 | font-size: 10pt;
40 | color: #666666;
41 | text-align: justify;
42 | }
43 |
44 |
45 | /* Style used for all hyperlinks. */
46 | a:link { color: #009900; text-decoration: none }
47 | a:visited { color: #009999; text-decoration: none }
48 | a:hover { color: #0033cc; text-decoration: none }
49 | a:active { color: #0033cc; text-decoration: none }
50 |
51 | /*
52 | table.matrix
53 | {
54 | border-left: double 3px #666666;
55 | border-right: double 3px #666666;
56 | margin-left: 3em;
57 | }
58 | */
59 |
60 | /* Style appled to declarations. E.g. 'void foo(int a, float b);' */
61 | span.decl { font-size: 10pt; font-weight: bold; color: #000000; text-align: left }
62 | /* Style appled to current declaration's symbol. E.g. 'foo' in 'void foo(int a, float b);' */
63 | span.currsymbol { font-size: 12pt; color: #009900 }
64 | /* Style appled to function's parameters. E.g. 'a' and 'b' in 'void foo(int a, float b);' */
65 | span.funcparam { font-style: italic; font-weight: normal; color: #331200 }
66 |
67 | /* Style for div that actualy contains documenation. */
68 | #content
69 | {
70 | padding-right: 8px;
71 | position: absolute;
72 | left: 245px;
73 | top: 8px;
74 | text-align: justify;
75 | }
76 |
77 | /* Style for table that is inside div considered above. Contains documentaton
78 | itself and footer with CanDyDOC logo. */
79 | table.content
80 | {
81 | margin-bottom: 8px;
82 | border-spacing: 0px;
83 | border-collapse: collapse;
84 | background-color: #ffffff;
85 | }
86 |
87 | /* Style for cell of above considered table that contains documentation itself. */
88 | #docbody
89 | {
90 | padding: 8px 20px 8px 20px;
91 | border: solid 1px #009900;
92 | }
93 |
94 | /* Style for cell that contains CanDyDOC logo. */
95 | #docfooter
96 | {
97 | height: 16px;
98 | background-color: #ddeedd;
99 | padding: 0px 8px 0px 8px;
100 | border: solid 1px #009900;
101 | }
102 |
103 | /* Style applied to currently active tab of explorer window. */
104 | span.activetab
105 | {
106 | background-color: #0033cc;
107 | border-top: solid 2px #009900;
108 | color: #ffffff;
109 | font-weight: bold;
110 | padding-left: 4px;
111 | padding-right: 4px;
112 | padding-top: 1px;
113 | margin-right: 1px;
114 | }
115 |
116 | /* Style applied to currently inactive tab of explorer window. */
117 | span.inactivetab
118 | {
119 | background-color: #000066;
120 | color: #cccccc;
121 | font-weight: normal;
122 | padding-left: 4px;
123 | padding-right: 4px;
124 | padding-top: 0px;
125 | margin-right: 1px;
126 | }
127 |
128 | /* Style applied to div that contains tabs of explorer. Note that if
129 | you want to change it's position you have to change position of
130 | #explorerclient, #content and corresponding values in ie56hack.css */
131 | #tabarea
132 | {
133 | position: fixed;
134 | top: 8px;
135 | width: 205px;
136 | height: 16px;
137 | cursor: default;
138 | }
139 |
140 |
141 | /* Style applied to div that contains tree in explorer. Note that if
142 | you want to change it's position you have to change position of
143 | #tabarea, #content and corresponding values in ie56hack.css */
144 | #explorerclient
145 | {
146 | position: fixed;
147 | top: 24px;
148 | bottom: 8px;
149 | width: 205px;
150 | overflow: auto;
151 | background-color: #fcfffc;
152 | border: solid 2px #0033cc;
153 | padding: 4px;
154 | cursor: default;
155 | color: Black;
156 | }
157 |
158 | /* Following 3 styles control appearance of marker that appears
159 | if you click some entity in outline window. */
160 | div.markertop { border-left: solid 2px #0033cc;}
161 | div.markermiddle{ border-left: dotted 2px #0033cc;}
162 | div.markerbottom{ border-left: dotted 2px #66cc66;}
163 |
164 | /* Style applied to preformated text used to show examples. */
165 | pre.d_code
166 | {
167 | border: dotted 1px #9c9;
168 | background-color: #eeffee;
169 | }
--------------------------------------------------------------------------------
/html/candydoc/tree.js:
--------------------------------------------------------------------------------
1 | /* This file is a part of CanDyDOC fileset.
2 | File is written by Victor Nakoryakov and placed into the public domain.
3 |
4 | This file is javascript with classes that represents native style tree control. */
5 |
6 | var pmNone = 0;
7 | var pmPlus = 1;
8 | var pmMinus = 2;
9 |
10 | var hlNone = 0;
11 | var hlGrey = 1;
12 | var hlSelected = 2;
13 |
14 | function TreeView(hrefMode)
15 | {
16 | this.domEntry = document.createElement("div");
17 | this.children = new Array();
18 | this.selection = null;
19 | this.hrefMode = hrefMode;
20 |
21 | this.createBranch = function(text, iconSrc)
22 | {
23 | var root = new TreeNode(text, iconSrc, this.hrefMode);
24 | root.owner = this;
25 | this.children[ this.children.length ] = root;
26 | this.domEntry.appendChild( root.domEntry );
27 | return root;
28 | }
29 |
30 | this.branch = function(text)
31 | {
32 | var ret = null;
33 | for (var i = 0; i < this.children.length; ++i)
34 | if (this.children[i].textElement.data == text)
35 | {
36 | ret = this.children[i];
37 | break;
38 | }
39 |
40 | return ret;
41 | }
42 |
43 | this.domEntry.style.fontSize = "10px";
44 | this.domEntry.style.cursor = "default";
45 | this.domEntry.style.whiteSpace = "nowrap";
46 | }
47 |
48 | var idCounter = 0;
49 | function TreeNode(text, iconSrc, hrefMode)
50 | {
51 | this.id = idCounter++;
52 | this.parentNode = null;
53 | this.children = new Array();
54 | this.domEntry = document.createElement("div");
55 | this.icon = document.createElement("img");
56 | this.textElement = document.createTextNode(text);
57 | this.textSpan = document.createElement("span");
58 | this.lineDiv = document.createElement("div");
59 | this.hierarchyImgs = new Array();
60 | this.onclick = null;
61 |
62 | function createIcon()
63 | {
64 | var img = document.createElement("img");
65 | img.style.verticalAlign = "middle";
66 | img.style.position = "relative";
67 | img.style.top = "-1px";
68 | img.width = 16;
69 | img.height = 16;
70 | return img;
71 | }
72 |
73 | function createHierarchyImage()
74 | {
75 | var img = createIcon();
76 | img.pointsTop = false;
77 | img.pointsBottom = false;
78 | img.pointsRight = false;
79 | img.pmState = pmNone;
80 | return img;
81 | }
82 |
83 | function genHierarchyImageSrc(hierarchyImg)
84 | {
85 | var name = "";
86 | if (hierarchyImg.pointsTop)
87 | name += "t";
88 |
89 | if (hierarchyImg.pointsBottom)
90 | name += "b";
91 |
92 | if (hierarchyImg.pointsRight)
93 | name += "r";
94 |
95 | if (hierarchyImg.pmState == pmPlus)
96 | name += "p";
97 | else if (hierarchyImg.pmState == pmMinus)
98 | name += "m";
99 |
100 | if (name == "")
101 | name = "shim";
102 |
103 | return "candydoc/img/tree/" + name + ".gif";
104 | }
105 |
106 | function setSrc(icon, src)
107 | {
108 | icon.src = src;
109 | // After src change width and height are reseted in IE.
110 | // Bug workaround:
111 | icon.width = 16;
112 | icon.height = 16;
113 | }
114 |
115 | this.createChild = function(text, iconSrc)
116 | {
117 | var child = new TreeNode(text, iconSrc, this.owner.hrefMode);
118 | this.children[ this.children.length ] = child;
119 | this.domEntry.appendChild( child.domEntry );
120 | child.parentNode = this;
121 | child.owner = this.owner;
122 |
123 | // insert hierarchy images according to deepness level
124 | // of created child.
125 |
126 | if (this.children.length > 1)
127 | {
128 | // there were already added child before. So copy `level-1`
129 | // hierarchy images from it.
130 |
131 | var prevAddedChild = this.children[ this.children.length - 2 ];
132 |
133 | for (var i = 0; i < prevAddedChild.hierarchyImgs.length - 1; ++i)
134 | {
135 | var prevAddedChildImg = prevAddedChild.hierarchyImgs[i];
136 | var img = createHierarchyImage();
137 | setSrc(img, prevAddedChildImg.src);
138 | img.pointsTop = prevAddedChildImg.pointsTop;
139 | img.pointsBottom = prevAddedChildImg.pointsBottom;
140 | img.pointsRight = prevAddedChildImg.pointsRight;
141 | img.pmState = prevAddedChildImg.pmState;
142 |
143 | child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
144 | child.lineDiv.insertBefore(img, child.icon);
145 | }
146 |
147 | // change last hierarchy image of prevAddedChild from |_ to |-
148 | var lastHierarchyImg = prevAddedChild.hierarchyImgs[ prevAddedChild.hierarchyImgs.length - 1 ];
149 | lastHierarchyImg.pointsBottom = true;
150 | setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
151 |
152 | // change hierarchy images of prevAddedChild's children on it's last
153 | // level to |
154 | prevAddedChild.addHierarchyTBLine(prevAddedChild.hierarchyImgs.length - 1);
155 | }
156 | else
157 | {
158 | // this is a first child. So copy `level-2`
159 | // hierarchy images from parent, i.e. this.
160 |
161 | for (var i = 0; i < this.hierarchyImgs.length - 1; ++i)
162 | {
163 | var parentImg = this.hierarchyImgs[i];
164 | var img = createHierarchyImage();
165 | setSrc(img, parentImg.src);
166 | img.pointsTop = parentImg.pointsTop;
167 | img.pointsBottom = parentImg.pointsBottom;
168 | img.pointsRight = parentImg.pointsRight;
169 | img.pmState = parentImg.pmState;
170 |
171 | child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
172 | child.lineDiv.insertBefore(img, child.icon);
173 | }
174 |
175 | if (this.hierarchyImgs.length > 0) // we are not root
176 | {
177 | // change last hierarchy image of parent (i.e. this): add minus to it
178 | var lastHierarchyImg = this.hierarchyImgs[ this.hierarchyImgs.length - 1];
179 | lastHierarchyImg.pmState = pmMinus;
180 | setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
181 | lastHierarchyImg.owner = this;
182 | lastHierarchyImg.onclick = new Function("e", "this.owner.processPMClick(e);");
183 |
184 | // make decision on image on `level-1`. It depends on parent's (ie this)
185 | // image on same level.
186 | var parentL1HierarchyImg = lastHierarchyImg;
187 | var l1HierarchyImg = createHierarchyImage();
188 | if (parentL1HierarchyImg.pointsBottom)
189 | {
190 | l1HierarchyImg.pointsTop = true;
191 | l1HierarchyImg.pointsBottom = true;
192 | }
193 | setSrc(l1HierarchyImg, genHierarchyImageSrc(l1HierarchyImg));
194 | child.hierarchyImgs[ child.hierarchyImgs.length ] = l1HierarchyImg;
195 | child.lineDiv.insertBefore(l1HierarchyImg, child.icon);
196 | }
197 | }
198 |
199 | // in any case on last level our child will have icon |_
200 | var img = createHierarchyImage();
201 | img.pointsTop = true;
202 | img.pointsRight = true;
203 | setSrc(img, genHierarchyImageSrc(img));
204 |
205 | child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
206 | child.lineDiv.insertBefore(img, child.icon);
207 |
208 | return child;
209 | }
210 |
211 | this.lastChild = function()
212 | {
213 | return this.children[ this.children.length - 1 ];
214 | }
215 |
216 | this.child = function(text)
217 | {
218 | var ret = null;
219 | for (var i = 0; i < this.children.length; ++i)
220 | if (this.children[i].textElement.data == text)
221 | {
222 | ret = this.children[i];
223 | break;
224 | }
225 |
226 | return ret;
227 | }
228 |
229 | this.addHierarchyTBLine = function(level)
230 | {
231 | for (var i = 0; i < this.children.length; ++i)
232 | {
233 | var img = this.children[i].hierarchyImgs[level];
234 | img.pointsTop = true;
235 | img.pointsBottom = true;
236 | setSrc(img, genHierarchyImageSrc(img));
237 | this.children[i].addHierarchyTBLine(level);
238 | }
239 | }
240 |
241 | this.expand = function()
242 | {
243 | var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
244 |
245 | if (img.pmState == pmPlus)
246 | {
247 | img.pmState = pmMinus;
248 | setSrc(img, genHierarchyImageSrc(img));
249 |
250 | for (var i = 0; i < this.children.length; ++i)
251 | this.children[i].domEntry.style.display = "";
252 | }
253 | }
254 |
255 | this.collapse = function()
256 | {
257 | var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
258 |
259 | if (img.pmState == pmMinus)
260 | {
261 | img.pmState = pmPlus;
262 | setSrc(img, genHierarchyImageSrc(img));
263 |
264 | for (var i = 0; i < this.children.length; ++i)
265 | this.children[i].domEntry.style.display = "none";
266 | }
267 | }
268 |
269 | this.toggle = function()
270 | {
271 | var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
272 | if (img.pmState == pmMinus)
273 | this.collapse();
274 | else
275 | this.expand();
276 | }
277 |
278 | this.select = function()
279 | {
280 | if (this.owner.selection != this)
281 | {
282 | if (this.owner.selection)
283 | this.owner.selection.setHighlight(hlNone);
284 |
285 | this.owner.selection = this;
286 | this.setHighlight(hlSelected);
287 | }
288 | }
289 |
290 | this.setHighlight = function(mode)
291 | {
292 | if (mode == hlNone)
293 | {
294 | this.textSpan.style.backgroundColor = "";
295 | this.textSpan.style.color = "";
296 | this.textSpan.style.border = "";
297 | }
298 | else if (mode == hlGrey)
299 | {
300 | this.textSpan.style.backgroundColor = "#aaaaaa";
301 | this.textSpan.style.color = "";
302 | this.textSpan.style.border = "";
303 | }
304 | else if (mode == hlSelected)
305 | {
306 | this.textSpan.style.backgroundColor = "3399cc";
307 | this.textSpan.style.color = "white";
308 | this.textSpan.style.border = "dotted 1px red";
309 | }
310 | }
311 |
312 | this.setOnclick = function(proc)
313 | {
314 | this.onclick = proc;
315 | }
316 |
317 | this.setRef = function(url)
318 | {
319 | if (this.anchor)
320 | this.anchor.href = url;
321 | }
322 |
323 | this.processPMClick = function(e)
324 | {
325 | this.toggle();
326 |
327 | // prevent this line selection, stop bubbling
328 | if (e)
329 | e.stopPropagation(); // Mozilla way
330 | if (window.event)
331 | window.event.cancelBubble = true; // IE way
332 | }
333 |
334 | this.processOnclick = function()
335 | {
336 | this.select();
337 | if (this.onclick instanceof Function)
338 | this.onclick();
339 | }
340 |
341 | ///////////////////////////////////////////////////////////////////////////
342 | if (iconSrc)
343 | this.icon.src = iconSrc;
344 | else
345 | {
346 | this.icon.width = 0;
347 | this.icon.height = 0;
348 | }
349 |
350 | this.icon.style.verticalAlign = "middle";
351 | this.icon.style.position = "relative";
352 | this.icon.style.top = "-1px";
353 | this.icon.style.paddingRight = "2px";
354 |
355 | if (!hrefMode)
356 | {
357 | this.textSpan.appendChild( this.textElement );
358 | }
359 | else
360 | {
361 | this.anchor = document.createElement("a");
362 | this.anchor.appendChild( this.textElement );
363 | this.textSpan.appendChild( this.anchor );
364 | }
365 |
366 | this.lineDiv.appendChild( this.icon );
367 | this.lineDiv.appendChild( this.textSpan );
368 | this.domEntry.appendChild( this.lineDiv );
369 |
370 | this.lineDiv.owner = this;
371 |
372 | if (!hrefMode)
373 | this.lineDiv.onclick = new Function("this.owner.processOnclick();");
374 | }
375 |
--------------------------------------------------------------------------------
/html/candydoc/util.js:
--------------------------------------------------------------------------------
1 | /* This file is a part of CanDyDOC fileset.
2 | File is written by Victor Nakoryakov and placed into the public domain.
3 |
4 | This file is javascript with cross-browser utility functions. */
5 |
6 | function getLeft(elem)
7 | {
8 | var ret = 0;
9 | while (elem.offsetParent)
10 | {
11 | ret += elem.offsetLeft;
12 | elem = elem.offsetParent;
13 | }
14 |
15 | return ret;
16 | }
17 |
18 | function getTop(elem)
19 | {
20 | var ret = 0;
21 | while (elem.offsetParent)
22 | {
23 | ret += elem.offsetTop;
24 | elem = elem.offsetParent;
25 | }
26 |
27 | return ret;
28 | }
29 |
30 | function getWindowHeight()
31 | {
32 | var ret = 0;
33 | if (typeof(window.innerHeight) == "number")
34 | ret = window.innerHeight;
35 | else if (document.documentElement && document.documentElement.clientHeight)
36 | ret = document.documentElement.clientHeight;
37 | else if (document.body && document.body.clientHeight)
38 | ret = document.body.clientHeight;
39 |
40 | return ret;
41 | }
42 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('msgpack-d', 'd',
2 | meson_version: '>=0.47',
3 | license: 'BSL-1.0',
4 | version: '1.0.4'
5 | )
6 |
7 | project_soversion = '1'
8 |
9 | pkgc = import('pkgconfig')
10 |
11 | #
12 | # Sources
13 | #
14 | msgpack_src = [
15 | 'src/msgpack/attribute.d',
16 | 'src/msgpack/buffer.d',
17 | 'src/msgpack/common.d',
18 | 'src/msgpack/exception.d',
19 | 'src/msgpack/package.d',
20 | 'src/msgpack/packer.d',
21 | 'src/msgpack/register.d',
22 | 'src/msgpack/streaming_unpacker.d',
23 | 'src/msgpack/unpacker.d',
24 | 'src/msgpack/value.d',
25 | ]
26 |
27 | src_dir = include_directories('src/')
28 |
29 | #
30 | # Targets
31 | #
32 | msgpack_lib = library('msgpack-d',
33 | [msgpack_src],
34 | include_directories: [src_dir],
35 | install: true,
36 | version: meson.project_version(),
37 | soversion: project_soversion,
38 | )
39 |
40 | pkgc.generate(name: 'msgpack-d',
41 | libraries: [msgpack_lib],
42 | subdirs: 'd/msgpack',
43 | version: meson.project_version(),
44 | description: 'Library for lexing and parsing D source code.'
45 | )
46 |
47 | # for use by others which embed this as subproject
48 | msgpack_dep = declare_dependency(
49 | link_with: [msgpack_lib],
50 | include_directories: [src_dir]
51 | )
52 |
53 | #
54 | # Tests
55 | #
56 | if meson.get_compiler('d').get_id() == 'llvm'
57 | extra_args = ['-main', '-link-defaultlib-shared']
58 | else
59 | extra_args = ['-main']
60 | endif
61 |
62 | msgpack_test_exe = executable('test_msgpack',
63 | [msgpack_src],
64 | include_directories: [src_dir],
65 | d_unittest: true,
66 | link_args: extra_args,
67 | )
68 | test('test_msgpack', msgpack_test_exe)
69 |
70 | #
71 | # Install
72 | #
73 | install_subdir('src/msgpack/', install_dir: 'include/d/msgpack/')
74 |
--------------------------------------------------------------------------------
/posix.mak:
--------------------------------------------------------------------------------
1 | # build mode: 32bit or 64bit
2 | MODEL ?= $(shell getconf LONG_BIT)
3 |
4 | ifeq (,$(DMD))
5 | DMD := dmd
6 | endif
7 |
8 | LIB = libmsgpack-d.a
9 | DFLAGS = -Isrc -m$(MODEL) -d -w -dip25 -dip1000
10 |
11 | ifeq (true, $(EnableReal))
12 | DFLAGS += -version=EnableReal
13 | endif
14 |
15 | ifeq ($(BUILD),debug)
16 | DFLAGS += -g -debug
17 | else
18 | DFLAGS += -O -release -nofloat -inline -noboundscheck
19 | endif
20 |
21 | NAMES = attribute common package register unpacker buffer exception packer streaming_unpacker value
22 | FILES = $(addsuffix .d, $(NAMES))
23 | SRCS = $(addprefix src/msgpack/, $(FILES))
24 |
25 | # DDoc
26 | DOCS = $(addsuffix .html, $(NAMES))
27 | DOCDIR = html
28 | CANDYDOC = $(addprefix html/candydoc/, candy.ddoc modules.ddoc)
29 | DDOCFLAGS = -Dd$(DOCDIR) -c -o- -Isrc $(CANDYDOC)
30 |
31 | target: doc $(LIB)
32 |
33 | $(LIB):
34 | $(DMD) $(DFLAGS) -lib -of$(LIB) $(SRCS)
35 |
36 | doc:
37 | $(DMD) $(DDOCFLAGS) $(SRCS)
38 |
39 | clean:
40 | rm $(addprefix $(DOCDIR)/, $(DOCS)) $(LIB)
41 |
42 | MAIN_FILE = "empty_msgpack_unittest.d"
43 |
44 | unittest:
45 | echo 'import msgpack; void main(){}' > $(MAIN_FILE)
46 | $(DMD) $(DFLAGS) -unittest -of$(LIB) $(SRCS) -run $(MAIN_FILE)
47 | rm $(MAIN_FILE)
48 |
49 | run_examples:
50 | echo example/* | xargs -n 1 $(DMD) src/msgpack/*.d $(DFLAGS) -Isrc -run
51 |
--------------------------------------------------------------------------------
/src/msgpack/attribute.d:
--------------------------------------------------------------------------------
1 | module msgpack.attribute;
2 |
3 | import std.typetuple; // will use std.meta
4 | import std.traits;
5 |
6 |
7 | /**
8 | * Attribute for specifying non pack/unpack field.
9 | * This is an alternative approach of MessagePackable mixin.
10 | *
11 | * Example:
12 | * -----
13 | * struct S
14 | * {
15 | * int num;
16 | * // Packer/Unpacker ignores this field;
17 | * @nonPacked string str;
18 | * }
19 | * -----
20 | */
21 | struct nonPacked {}
22 |
23 |
24 | package template isPackedField(alias field)
25 | {
26 | enum isPackedField = (staticIndexOf!(nonPacked, __traits(getAttributes, field)) == -1) && (!isSomeFunction!(typeof(field)));
27 | }
28 |
29 |
30 | /**
31 | * Attribute for specifying serialize/deserialize proxy for pack/unpack field.
32 | * This is an alternative approach of registerPackHandler/registerUnpackHandler.
33 | *
34 | * Example:
35 | * -----
36 | * struct Proxy
37 | * {
38 | * import std.conv;
39 | * static void serialize(ref Packer p, ref int val) { p.pack(to!string(val)); }
40 | * static void deserialize(ref Unpacker u, ref int val) { string tmp; u.unpack(tmp); val = to!int(tmp); }
41 | * }
42 | * struct S
43 | * {
44 | * // The Packer/Unpacker proxy handler is applied this field.
45 | * @serializedAs!Proxy int num;
46 | * string str;
47 | * }
48 | * -----
49 | */
50 | struct serializedAs(T){}
51 |
52 | package enum bool isSerializedAs(A) = is(A : serializedAs!T, T);
53 | package alias getSerializedAs(T : serializedAs!Proxy, Proxy) = Proxy;
54 | package alias ProxyList(alias value) = staticMap!(getSerializedAs, Filter!(isSerializedAs, __traits(getAttributes, value)));
55 | package template isSerializedAs(alias value)
56 | {
57 | static if ( __traits(compiles, __traits(getAttributes, value)) ) {
58 | enum bool isSerializedAs = ProxyList!value.length > 0;
59 | } else {
60 | enum bool isSerializedAs = false;
61 | }
62 | }
63 | package template getSerializedAs(alias value)
64 | {
65 | private alias _list = ProxyList!value;
66 | static assert(_list.length <= 1, `Only single serialization proxy is allowed`);
67 | alias getSerializedAs = _list[0];
68 | }
69 | package template hasSerializedAs(alias value)
70 | {
71 | private enum _listLength = ProxyList!value.length;
72 | static assert(_listLength <= 1, `Only single serialization proxy is allowed`);
73 | enum bool hasSerializedAs = _listLength == 1;
74 | }
75 |
76 | unittest
77 | {
78 | import msgpack.packer, msgpack.unpacker;
79 | struct Proxy
80 | {
81 | static void serialize(ref Packer p, ref int value) {}
82 | static void deserialize(ref Unpacker u, ref int value) {}
83 | }
84 | struct A
85 | {
86 | @serializedAs!Proxy int a;
87 | @(42) int b;
88 | @(42) @serializedAs!Proxy int c;
89 | }
90 | static assert(is(getSerializedAs!(A.a) == Proxy));
91 | static assert(isSerializedAs!(__traits(getAttributes, A.a)[0]));
92 | static assert(hasSerializedAs!(A.a));
93 | static assert(!hasSerializedAs!(A.b));
94 | static assert(hasSerializedAs!(A.c));
95 | }
96 |
--------------------------------------------------------------------------------
/src/msgpack/buffer.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | module msgpack.buffer;
4 |
5 | //import std.traits;
6 | import std.range;
7 |
8 |
9 | version(Posix)
10 | {
11 | import core.sys.posix.sys.uio : iovec;
12 | }
13 | else
14 | {
15 | /**
16 | * from core.sys.posix.sys.uio.iovec for compatibility with posix.
17 | */
18 | struct iovec
19 | {
20 | void* iov_base;
21 | size_t iov_len;
22 | }
23 | }
24 |
25 |
26 | /**
27 | * $(D RefBuffer) is a reference stored buffer for more efficient serialization
28 | *
29 | * Example:
30 | * -----
31 | * auto packer = packer(RefBuffer(16)); // threshold is 16
32 | *
33 | * // packs data
34 | *
35 | * writev(fd, cast(void*)packer.buffer.vector.ptr, packer.buffer.vector.length);
36 | * -----
37 | */
38 | struct RefBuffer
39 | {
40 | private:
41 | static struct Chunk
42 | {
43 | ubyte[] data; // storing serialized value
44 | size_t used; // used size of data
45 | }
46 |
47 | immutable size_t Threshold;
48 | immutable size_t ChunkSize;
49 |
50 | // for putCopy
51 | Chunk[] chunks_; // memory chunk for buffer
52 | size_t index_; // index for cunrrent chunk
53 |
54 | // for putRef
55 | iovec[] vecList_; // reference to large data or copied data.
56 |
57 |
58 | public:
59 | /**
60 | * Constructs a buffer.
61 | *
62 | * Params:
63 | * threshold = the threshold of writing value or stores reference.
64 | * chunkSize = the default size of chunk for allocation.
65 | */
66 | @safe
67 | this(in size_t threshold, in size_t chunkSize = 8192)
68 | {
69 | Threshold = threshold;
70 | ChunkSize = chunkSize;
71 |
72 | chunks_.length = 1;
73 | chunks_[index_].data.length = chunkSize;
74 | }
75 |
76 |
77 | /**
78 | * Returns the buffer contents that excluding references.
79 | *
80 | * Returns:
81 | * the non-contiguous copied contents.
82 | */
83 | @property @safe
84 | nothrow ubyte[] data()
85 | {
86 | ubyte[] result;
87 |
88 | foreach (ref chunk; chunks_)
89 | result ~= chunk.data[0..chunk.used];
90 |
91 | return result;
92 | }
93 |
94 |
95 | /**
96 | * Forwards to all buffer contents.
97 | *
98 | * Returns:
99 | * the array of iovec struct that stores references.
100 | */
101 | @property @safe
102 | nothrow ref iovec[] vector() return
103 | {
104 | return vecList_;
105 | }
106 |
107 |
108 | /**
109 | * Writes the argument to buffer and stores the reference of writed content
110 | * if the argument size is smaller than threshold,
111 | * otherwise stores the reference of argument directly.
112 | *
113 | * Params:
114 | * value = the content to write.
115 | */
116 | @safe
117 | void put(in ubyte value)
118 | {
119 | ubyte[1] values = [value];
120 | putCopy(values);
121 | }
122 |
123 |
124 | /// ditto
125 | @safe
126 | void put(in ubyte[] value)
127 | {
128 | if (value.length < Threshold)
129 | putCopy(value);
130 | else
131 | putRef(value);
132 | }
133 |
134 |
135 | private:
136 | /*
137 | * Stores the reference of $(D_PARAM value).
138 | *
139 | * Params:
140 | * value = the content to write.
141 | */
142 | @trusted
143 | void putRef(in ubyte[] value)
144 | {
145 | vecList_.length += 1;
146 | vecList_[$ - 1] = iovec(cast(void*)value.ptr, value.length);
147 | }
148 |
149 |
150 | /*
151 | * Writes $(D_PARAM value) to buffer and appends to its reference.
152 | *
153 | * Params:
154 | * value = the contents to write.
155 | */
156 | @trusted
157 | void putCopy(const scope ubyte[] value)
158 | {
159 | /*
160 | * Helper for expanding new space.
161 | */
162 | void expand(in size_t size)
163 | {
164 | const newSize = size < ChunkSize ? ChunkSize : size;
165 |
166 | index_++;
167 | chunks_.length = 1;
168 | chunks_[index_].data.length = newSize;
169 | }
170 |
171 | const size = value.length;
172 |
173 | // lacks current chunk?
174 | if (chunks_[index_].data.length - chunks_[index_].used < size)
175 | expand(size);
176 |
177 | const base = chunks_[index_].used; // start index
178 | auto data = chunks_[index_].data[base..base + size]; // chunk to write
179 |
180 | data[] = value[];
181 | chunks_[index_].used += size;
182 |
183 | // Optimization for avoiding iovec allocation.
184 | if (vecList_.length && data.ptr == (vecList_[$ - 1].iov_base +
185 | vecList_[$ - 1].iov_len))
186 | vecList_[$ - 1].iov_len += size;
187 | else
188 | putRef(data);
189 | }
190 | }
191 |
192 |
193 | unittest
194 | {
195 | static assert(isOutputRange!(RefBuffer, ubyte) &&
196 | isOutputRange!(RefBuffer, ubyte[]));
197 |
198 | auto buffer = RefBuffer(2, 4);
199 |
200 | ubyte[] tests = [1, 2];
201 | foreach (v; tests)
202 | buffer.put(v);
203 | buffer.put(tests);
204 |
205 | assert(buffer.data == tests, "putCopy failed");
206 |
207 | iovec[] vector = buffer.vector;
208 | ubyte[] result;
209 |
210 | assert(vector.length == 2, "Optimization failed");
211 |
212 | foreach (v; vector)
213 | result ~= (cast(ubyte*)v.iov_base)[0..v.iov_len];
214 |
215 | assert(result == tests ~ tests);
216 | }
217 |
--------------------------------------------------------------------------------
/src/msgpack/common.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | module msgpack.common;
4 |
5 | import msgpack.attribute;
6 |
7 | import std.typetuple; // will use std.meta
8 | import std.traits;
9 |
10 |
11 | // for Converting Endian using ntohs and ntohl;
12 | version(Windows)
13 | {
14 | package import core.sys.windows.winsock2;
15 | }
16 | else
17 | {
18 | package import core.sys.posix.arpa.inet;
19 | }
20 |
21 |
22 | version(EnableReal)
23 | {
24 | package enum EnableReal = true;
25 | }
26 | else
27 | {
28 | package enum EnableReal = false;
29 | }
30 |
31 |
32 | static if (real.sizeof == double.sizeof) {
33 | // for 80bit real inter-operation on non-x86 CPU
34 | version = NonX86;
35 |
36 | package import std.numeric;
37 | }
38 |
39 |
40 | @trusted:
41 | public:
42 |
43 |
44 | /**
45 | * $(D ExtValue) is a $(D MessagePack) Extended value representation.
46 | * The application is responsible for correctly interpreting $(D data) according
47 | * to the type described by $(D type).
48 | */
49 | struct ExtValue
50 | {
51 | byte type; /// An integer 0-127 with application-defined meaning
52 | ubyte[] data; /// The raw bytes
53 | }
54 |
55 |
56 | /**
57 | * MessagePack type-information format
58 | *
59 | * See_Also:
60 | * $(LINK2 http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec, MessagePack Specificaton)
61 | */
62 | enum Format : ubyte
63 | {
64 | // unsinged integer
65 | UINT8 = 0xcc, // ubyte
66 | UINT16 = 0xcd, // ushort
67 | UINT32 = 0xce, // uint
68 | UINT64 = 0xcf, // ulong
69 |
70 | // signed integer
71 | INT8 = 0xd0, // byte
72 | INT16 = 0xd1, // short
73 | INT32 = 0xd2, // int
74 | INT64 = 0xd3, // long
75 |
76 | // floating point
77 | FLOAT = 0xca, // float
78 | DOUBLE = 0xcb, // double
79 |
80 | // raw byte
81 | RAW = 0xa0,
82 | RAW16 = 0xda,
83 | RAW32 = 0xdb,
84 |
85 | // bin type
86 | BIN8 = 0xc4,
87 | BIN16 = 0xc5,
88 | BIN32 = 0xc6,
89 |
90 | // ext type
91 | EXT = 0xd4, // fixext 1/2/4/8/16
92 | EXT8 = 0xc7,
93 | EXT16 = 0xc8,
94 | EXT32 = 0xc9,
95 |
96 | // str type
97 | STR8 = 0xd9,
98 | //STR16 = 0xda,
99 | //STR32 = 0xdb,
100 |
101 | // array
102 | ARRAY = 0x90,
103 | ARRAY16 = 0xdc,
104 | ARRAY32 = 0xdd,
105 |
106 | // map
107 | MAP = 0x80,
108 | MAP16 = 0xde,
109 | MAP32 = 0xdf,
110 |
111 | // other
112 | NIL = 0xc0, // null
113 | TRUE = 0xc3,
114 | FALSE = 0xc2,
115 |
116 | // real (This format is D only!)
117 | REAL = 0xd4
118 | }
119 |
120 |
121 | package:
122 |
123 |
124 | /**
125 | * For float type serialization / deserialization
126 | */
127 | union _f
128 | {
129 | float f;
130 | uint i;
131 | }
132 |
133 |
134 | /**
135 | * For double type serialization / deserialization
136 | */
137 | union _d
138 | {
139 | double f;
140 | ulong i;
141 | }
142 |
143 |
144 | /**
145 | * For real type serialization / deserialization
146 | *
147 | * 80-bit real is padded to 12 bytes(Linux) and 16 bytes(Mac).
148 | * http://lists.puremagic.com/pipermail/digitalmars-d/2010-June/077394.html
149 | */
150 | union _r
151 | {
152 | real f;
153 |
154 | struct
155 | {
156 | ulong fraction;
157 | ushort exponent; // includes sign
158 | }
159 | }
160 |
161 | enum RealSize = 10; // Real size is 80bit
162 |
163 |
164 | /**
165 | * Detects whether $(D_PARAM T) is a built-in byte type.
166 | */
167 | template isByte(T)
168 | {
169 | enum isByte = staticIndexOf!(Unqual!T, byte, ubyte) >= 0;
170 | }
171 |
172 |
173 | unittest
174 | {
175 | static assert(isByte!(byte));
176 | static assert(isByte!(const(byte)));
177 | static assert(isByte!(ubyte));
178 | static assert(isByte!(immutable(ubyte)));
179 | static assert(!isByte!(short));
180 | static assert(!isByte!(char));
181 | static assert(!isByte!(string));
182 | }
183 |
184 |
185 | /**
186 | * Gets asterisk string from pointer type
187 | */
188 | template AsteriskOf(T)
189 | {
190 | static if (is(T P == U*, U))
191 | enum AsteriskOf = "*" ~ AsteriskOf!U;
192 | else
193 | enum AsteriskOf = "";
194 | }
195 |
196 |
197 | /**
198 | * Get the number of member to serialize.
199 | */
200 | template SerializingMemberNumbers(Classes...)
201 | {
202 | static if (Classes.length == 0)
203 | enum SerializingMemberNumbers = 0;
204 | else
205 | enum SerializingMemberNumbers = Filter!(isPackedField, Classes[0].tupleof).length + SerializingMemberNumbers!(Classes[1..$]);
206 | }
207 |
208 |
209 | /**
210 | * Get derived classes with serialization-order
211 | */
212 | template SerializingClasses(T)
213 | {
214 | // There is no information in Object type. Currently disable Object serialization.
215 | static if (is(T == Object))
216 | static assert(false, "Object type serialization doesn't support yet. Please define toMsgpack/fromMsgpack and use cast");
217 | else
218 | alias TypeTuple!(Reverse!(Erase!(Object, BaseClassesTuple!(T))), T) SerializingClasses;
219 | }
220 |
221 |
222 | /**
223 | * Get a field name of class or struct.
224 | */
225 | template getFieldName(Type, size_t i)
226 | {
227 | import std.conv : text;
228 |
229 | static assert((is(Unqual!Type == class) || is(Unqual!Type == struct)), "Type must be class or struct: type = " ~ Type.stringof);
230 | static assert(i < Type.tupleof.length, text(Type.stringof, " has ", Type.tupleof.length, " attributes: given index = ", i));
231 |
232 | enum getFieldName = __traits(identifier, Type.tupleof[i]);
233 | }
234 |
235 |
236 | version (LittleEndian)
237 | {
238 | /*
239 | * Converts $(value) to different Endian.
240 | *
241 | * Params:
242 | * value = the LittleEndian value to convert.
243 | *
244 | * Returns:
245 | * the converted value.
246 | */
247 | @trusted
248 | ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16)
249 | {
250 | return ntohs(cast(ushort)value);
251 | }
252 |
253 |
254 | // ditto
255 | @trusted
256 | uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32)
257 | {
258 | return ntohl(cast(uint)value);
259 | }
260 |
261 |
262 | // ditto
263 | @trusted
264 | ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64)
265 | {
266 | // dmd has convert function?
267 | return ((((cast(ulong)value) << 56) & 0xff00000000000000UL) |
268 | (((cast(ulong)value) << 40) & 0x00ff000000000000UL) |
269 | (((cast(ulong)value) << 24) & 0x0000ff0000000000UL) |
270 | (((cast(ulong)value) << 8) & 0x000000ff00000000UL) |
271 | (((cast(ulong)value) >> 8) & 0x00000000ff000000UL) |
272 | (((cast(ulong)value) >> 24) & 0x0000000000ff0000UL) |
273 | (((cast(ulong)value) >> 40) & 0x000000000000ff00UL) |
274 | (((cast(ulong)value) >> 56) & 0x00000000000000ffUL));
275 | }
276 |
277 |
278 | unittest
279 | {
280 | assert(convertEndianTo!16(0x0123) == 0x2301);
281 | assert(convertEndianTo!32(0x01234567) == 0x67452301);
282 | assert(convertEndianTo!64(0x0123456789abcdef) == 0xefcdab8967452301);
283 | }
284 |
285 |
286 | /*
287 | * Comapatible for BigEndian environment.
288 | */
289 | ubyte take8from(size_t bit = 8, T)(T value)
290 | {
291 | static if (bit == 8 || bit == 16 || bit == 32 || bit == 64)
292 | return (cast(ubyte*)&value)[0];
293 | else
294 | static assert(false, bit.stringof ~ " is not support bit width.");
295 | }
296 |
297 |
298 | unittest
299 | {
300 | foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) {
301 | assert(take8from!8 (cast(Integer)0x01) == 0x01);
302 | assert(take8from!16(cast(Integer)0x0123) == 0x23);
303 | assert(take8from!32(cast(Integer)0x01234567) == 0x67);
304 | assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef);
305 | }
306 | }
307 | }
308 | else
309 | {
310 | /*
311 | * Comapatible for LittleEndian environment.
312 | */
313 | @safe
314 | ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16)
315 | {
316 | return cast(ushort)value;
317 | }
318 |
319 |
320 | // ditto
321 | @safe
322 | uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32)
323 | {
324 | return cast(uint)value;
325 | }
326 |
327 |
328 | // ditto
329 | @safe
330 | ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64)
331 | {
332 | return cast(ulong)value;
333 | }
334 |
335 |
336 | unittest
337 | {
338 | assert(convertEndianTo!16(0x0123) == 0x0123);
339 | assert(convertEndianTo!32(0x01234567) == 0x01234567);
340 | assert(convertEndianTo!64(0x0123456789) == 0x0123456789);
341 | }
342 |
343 |
344 | /*
345 | * Takes 8bit from $(D_PARAM value)
346 | *
347 | * Params:
348 | * value = the content to take.
349 | *
350 | * Returns:
351 | * the 8bit value corresponding $(D_PARAM bit) width.
352 | */
353 | ubyte take8from(size_t bit = 8, T)(T value)
354 | {
355 | static if (bit == 8)
356 | return (cast(ubyte*)&value)[0];
357 | else static if (bit == 16)
358 | return (cast(ubyte*)&value)[1];
359 | else static if (bit == 32)
360 | return (cast(ubyte*)&value)[3];
361 | else static if (bit == 64)
362 | return (cast(ubyte*)&value)[7];
363 | else
364 | static assert(false, bit.stringof ~ " is not support bit width.");
365 | }
366 |
367 |
368 | unittest
369 | {
370 | foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) {
371 | assert(take8from!8 (cast(Integer)0x01) == 0x01);
372 | assert(take8from!16(cast(Integer)0x0123) == 0x23);
373 | assert(take8from!32(cast(Integer)0x01234567) == 0x67);
374 | assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef);
375 | }
376 | }
377 | }
378 |
379 |
380 | /*
381 | * Loads $(D_PARAM T) type value from $(D_PARAM buffer).
382 | *
383 | * Params:
384 | * buffer = the serialized contents.
385 | *
386 | * Returns:
387 | * the Endian-converted value.
388 | */
389 | T load16To(T)(ubyte[] buffer)
390 | {
391 | return cast(T)(convertEndianTo!16(*cast(ushort*)buffer.ptr));
392 | }
393 |
394 |
395 | // ditto
396 | T load32To(T)(ubyte[] buffer)
397 | {
398 | return cast(T)(convertEndianTo!32(*cast(uint*)buffer.ptr));
399 | }
400 |
401 |
402 | // ditto
403 | T load64To(T)(ubyte[] buffer)
404 | {
405 | return cast(T)(convertEndianTo!64(*cast(ulong*)buffer.ptr));
406 | }
407 |
408 |
409 | version (D_Ddoc)
410 | {
411 | /**
412 | * Internal buffer and related operations for Unpacker
413 | *
414 | * Following Unpackers mixin this template. So, Unpacker can use following methods.
415 | *
416 | * -----
417 | * //buffer image:
418 | * +-------------------------------------------+
419 | * | [object] | [obj | unparsed... | unused... |
420 | * +-------------------------------------------+
421 | * ^ offset
422 | * ^ current
423 | * ^ used
424 | * ^ buffer.length
425 | * -----
426 | *
427 | * This mixin template is a private.
428 | */
429 | mixin template InternalBuffer()
430 | {
431 | private:
432 | ubyte[] buffer_; // internal buffer
433 | size_t used_; // index that buffer cosumed
434 | size_t offset_; // index that buffer parsed
435 | size_t parsed_; // total size of parsed message
436 | bool hasRaw_; // indicates whether Raw object has been deserialized
437 |
438 |
439 | public:
440 | /**
441 | * Forwards to internal buffer.
442 | *
443 | * Returns:
444 | * the reference of internal buffer.
445 | */
446 | @property @safe
447 | nothrow ubyte[] buffer();
448 |
449 |
450 | /**
451 | * Fills internal buffer with $(D_PARAM target).
452 | *
453 | * Params:
454 | * target = new serialized buffer to deserialize.
455 | */
456 | @safe void feed(in ubyte[] target);
457 |
458 |
459 | /**
460 | * Consumes buffer. This method is helper for buffer property.
461 | * You must use this method if you write bytes to buffer directly.
462 | *
463 | * Params:
464 | * size = the number of consuming.
465 | */
466 | @safe
467 | nothrow void bufferConsumed(in size_t size);
468 |
469 |
470 | /**
471 | * Removes unparsed buffer.
472 | */
473 | @safe
474 | nothrow void removeUnparsed();
475 |
476 |
477 | /**
478 | * Returns:
479 | * the total size including unparsed buffer size.
480 | */
481 | @property @safe
482 | nothrow size_t size() const;
483 |
484 |
485 | /**
486 | * Returns:
487 | * the parsed size of buffer.
488 | */
489 | @property @safe
490 | nothrow size_t parsedSize() const;
491 |
492 |
493 | /**
494 | * Returns:
495 | * the unparsed size of buffer.
496 | */
497 | @property @safe
498 | nothrow size_t unparsedSize() const;
499 |
500 |
501 | private:
502 | @safe
503 | void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192);
504 | }
505 | }
506 | else
507 | {
508 | mixin template InternalBuffer()
509 | {
510 | private:
511 | ubyte[] buffer_; // internal buffer
512 | size_t used_; // index that buffer cosumed
513 | size_t offset_; // index that buffer parsed
514 | size_t parsed_; // total size of parsed message
515 | bool hasRaw_; // indicates whether Raw object has been deserialized
516 |
517 |
518 | public:
519 | @property @safe
520 | nothrow ubyte[] buffer()
521 | {
522 | return buffer_;
523 | }
524 |
525 |
526 | @safe
527 | void feed(in ubyte[] target)
528 | in
529 | {
530 | assert(target.length);
531 | }
532 | do
533 | {
534 | /*
535 | * Expands internal buffer.
536 | *
537 | * Params:
538 | * size = new buffer size to append.
539 | */
540 | void expandBuffer(in size_t size)
541 | {
542 | // rewinds buffer(completed deserialization)
543 | if (used_ == offset_ && !hasRaw_) {
544 | used_ = offset_ = 0;
545 |
546 | if (buffer_.length < size)
547 | buffer_.length = size;
548 |
549 | return;
550 | }
551 |
552 | // deserializing state is mid-flow(buffer has non-parsed data yet)
553 | auto unparsed = buffer_[offset_..used_];
554 | auto restSize = buffer_.length - used_ + offset_;
555 | auto newSize = size > restSize ? unparsedSize + size : buffer_.length;
556 |
557 | if (hasRaw_) {
558 | hasRaw_ = false;
559 | buffer_ = new ubyte[](newSize);
560 | } else {
561 | buffer_.length = newSize;
562 |
563 | // avoids overlapping copy
564 | auto area = buffer_[0..unparsedSize];
565 | unparsed = area.overlap(unparsed) ? unparsed.dup : unparsed;
566 | }
567 |
568 | buffer_[0..unparsedSize] = unparsed[];
569 | used_ = unparsedSize;
570 | offset_ = 0;
571 | }
572 |
573 | const size = target.length;
574 |
575 | // lacks current buffer?
576 | if (buffer_.length - used_ < size)
577 | expandBuffer(size);
578 |
579 | buffer_[used_..used_ + size] = target[];
580 | used_ += size;
581 | }
582 |
583 |
584 | @safe
585 | nothrow void bufferConsumed(in size_t size)
586 | {
587 | if (used_ + size > buffer_.length)
588 | used_ = buffer_.length;
589 | else
590 | used_ += size;
591 | }
592 |
593 |
594 | @safe
595 | nothrow void removeUnparsed()
596 | {
597 | used_ = offset_;
598 | }
599 |
600 |
601 | @property @safe
602 | nothrow size_t size() const
603 | {
604 | return parsed_ - offset_ + used_;
605 | }
606 |
607 |
608 | @property @safe
609 | nothrow size_t parsedSize() const
610 | {
611 | return parsed_;
612 | }
613 |
614 |
615 | @property @safe
616 | nothrow size_t unparsedSize() const
617 | {
618 | return used_ - offset_;
619 | }
620 |
621 |
622 | private:
623 | @safe
624 | nothrow void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192)
625 | {
626 | const size = target.length;
627 |
628 | buffer_ = new ubyte[](size > bufferSize ? size : bufferSize);
629 | used_ = size;
630 | buffer_[0..size] = target[];
631 | }
632 | }
633 | }
634 |
--------------------------------------------------------------------------------
/src/msgpack/exception.d:
--------------------------------------------------------------------------------
1 | module msgpack.exception;
2 |
3 | @trusted:
4 |
5 | /**
6 | * $(D MessagePackException) is a root Exception for MessagePack related operation.
7 | */
8 | class MessagePackException : Exception
9 | {
10 | pure this(string message)
11 | {
12 | super(message);
13 | }
14 | }
15 |
16 |
17 | /**
18 | * $(D UnpackException) is thrown on deserialization failure
19 | */
20 | class UnpackException : MessagePackException
21 | {
22 | this(string message)
23 | {
24 | super(message);
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/src/msgpack/package.d:
--------------------------------------------------------------------------------
1 | // Written in the D programming language.
2 |
3 | /**
4 | * MessagePack serializer and deserializer implementation.
5 | *
6 | * MessagePack is a binary-based serialization specification.
7 | *
8 | * Example:
9 | * -----
10 | * auto data = tuple("MessagePack!", [1, 2], true);
11 | *
12 | * auto serialized = pack(data);
13 | *
14 | * // ...
15 | *
16 | * typeof(data) deserialized;
17 | *
18 | * unpack(serialized, deserialized);
19 | *
20 | * assert(data == deserialized);
21 | * -----
22 | *
23 | * See_Also:
24 | * $(LINK2 http://msgpack.org/, The MessagePack Project)$(BR)
25 | * $(LINK2 https://github.com/msgpack/msgpack/blob/master/spec.md, MessagePack data format)
26 | *
27 | * Copyright: Copyright Masahiro Nakagawa 2010-.
28 | * License: Boost License 1.0.
29 | * Authors: Masahiro Nakagawa
30 | */
31 | module msgpack;
32 |
33 | public:
34 |
35 | import msgpack.common;
36 | import msgpack.attribute;
37 | import msgpack.buffer;
38 | import msgpack.exception;
39 | import msgpack.packer;
40 | import msgpack.unpacker;
41 | import msgpack.streaming_unpacker;
42 | import msgpack.register;
43 | import msgpack.value;
44 |
45 | version(Windows) {
46 | pragma(lib, "WS2_32");
47 | }
48 |
49 | @trusted:
50 |
51 |
52 | /**
53 | * Serializes $(D_PARAM args).
54 | *
55 | * Assumes single object if the length of $(D_PARAM args) == 1,
56 | * otherwise array object.
57 | *
58 | * Params:
59 | * args = the contents to serialize.
60 | *
61 | * Returns:
62 | * a serialized data.
63 | */
64 | ubyte[] pack(bool withFieldName = false, Args...)(in Args args)
65 | {
66 | auto packer = Packer(withFieldName);
67 |
68 | static if (Args.length == 1)
69 | packer.pack(args[0]);
70 | else
71 | packer.packArray(args);
72 |
73 | return packer.stream.data;
74 | }
75 |
76 |
77 | /**
78 | * Deserializes $(D_PARAM buffer) using stream deserializer.
79 | *
80 | * Params:
81 | * buffer = the buffer to deserialize.
82 | *
83 | * Returns:
84 | * a $(D Unpacked) contains deserialized object.
85 | *
86 | * Throws:
87 | * UnpackException if deserialization doesn't succeed.
88 | */
89 | Unpacked unpack(in ubyte[] buffer)
90 | {
91 | auto unpacker = StreamingUnpacker(buffer);
92 |
93 | if (!unpacker.execute())
94 | throw new UnpackException("Deserialization failure");
95 |
96 | return unpacker.unpacked;
97 | }
98 |
99 |
100 | /**
101 | * Deserializes $(D_PARAM buffer) using direct-conversion deserializer.
102 | *
103 | * Assumes single object if the length of $(D_PARAM args) == 1,
104 | * otherwise array object.
105 | *
106 | * Params:
107 | * buffer = the buffer to deserialize.
108 | * args = the references of values to assign.
109 | */
110 | void unpack(bool withFieldName = false, Args...)(in ubyte[] buffer, ref Args args)
111 | {
112 | auto unpacker = Unpacker(buffer, buffer.length, withFieldName);
113 |
114 | static if (Args.length == 1)
115 | unpacker.unpack(args[0]);
116 | else
117 | unpacker.unpackArray(args);
118 | }
119 |
120 |
121 | /**
122 | * Return value version
123 | */
124 | Type unpack(Type, bool withFieldName = false)(in ubyte[] buffer)
125 | {
126 | auto unpacker = Unpacker(buffer, buffer.length, withFieldName);
127 |
128 | Type result;
129 | unpacker.unpack(result);
130 | return result;
131 | }
132 |
133 |
134 | unittest
135 | {
136 | auto serialized = pack(false);
137 |
138 | assert(serialized[0] == Format.FALSE);
139 |
140 | auto deserialized = unpack(pack(1, true, "Foo"));
141 |
142 | assert(deserialized.type == Value.Type.array);
143 | assert(deserialized.via.array[0].type == Value.Type.unsigned);
144 | assert(deserialized.via.array[1].type == Value.Type.boolean);
145 | assert(deserialized.via.array[2].type == Value.Type.raw);
146 | }
147 |
148 |
149 | unittest
150 | {
151 | import std.typecons;
152 |
153 | { // stream
154 | auto result = unpack(pack(false));
155 |
156 | assert(result.via.boolean == false);
157 | }
158 | { // direct conversion
159 | Tuple!(uint, string) result;
160 | Tuple!(uint, string) test = tuple(1, "Hi!");
161 |
162 | unpack(pack(test), result);
163 | assert(result == test);
164 |
165 | test.field[0] = 2;
166 | test.field[1] = "Hey!";
167 | unpack(pack(test.field[0], test.field[1]), result.field[0], result.field[1]);
168 | assert(result == test);
169 | }
170 | { // return value direct conversion
171 | Tuple!(uint, string) test = tuple(1, "Hi!");
172 |
173 | auto data = pack(test);
174 | assert(data.unpack!(Tuple!(uint, string)) == test);
175 | }
176 | { // serialize object as a Map
177 | static class C
178 | {
179 | int num;
180 |
181 | this(int num) { this.num = num; }
182 | }
183 |
184 | auto test = new C(10);
185 | auto result = new C(100);
186 |
187 | unpack!(true)(pack!(true)(test), result);
188 | assert(result.num == 10, "Unpacking with field names failed");
189 | }
190 | }
191 |
192 |
193 | unittest
194 | {
195 | import std.typetuple;
196 |
197 | // unittest for https://github.com/msgpack/msgpack-d/issues/8
198 | foreach (Type; TypeTuple!(byte, short, int, long)) {
199 | foreach (i; [-33, -20, -1, 0, 1, 20, 33]) {
200 | Type a = cast(Type)i;
201 | Type b;
202 | unpack(pack(a), b);
203 | assert(a == b);
204 | }
205 | }
206 | }
207 |
208 |
209 | unittest
210 | {
211 | import std.typetuple;
212 |
213 | // char types
214 | foreach (Type; TypeTuple!(char, wchar, dchar)) {
215 | foreach (i; [Type.init, Type.min, Type.max, cast(Type)'j']) {
216 | Type a = i;
217 | Type b;
218 | unpack(pack(a), b);
219 | assert(a == b);
220 | }
221 | }
222 | }
223 |
224 | unittest
225 | {
226 | // ext type
227 | auto result = unpack(pack(ExtValue(7, [1,2,3,4])));
228 | assert(result == ExtValue(7, [1,2,3,4]));
229 | }
230 |
231 | unittest {
232 | import std.exception: assertThrown;
233 |
234 | struct Version {
235 | int major= -1;
236 | int minor = -1;
237 | }
238 |
239 | struct SubscriptionTopic {
240 | string[] topicComponents;
241 | }
242 |
243 | struct SubscriptionSender
244 | {
245 | string hostName;
246 | string biosName;
247 | }
248 |
249 | struct PubSubMessage {
250 |
251 | enum Type {
252 | publication,
253 | subscribe,
254 | unsubscribe,
255 | }
256 |
257 | Version version_;
258 | Type type;
259 | SubscriptionSender sender;
260 | SubscriptionTopic topic;
261 | string value;
262 | }
263 |
264 | ubyte[] bytes = [149, 146, 255, 255, 0, 146, 164, 104,
265 | 111, 115, 116, 164, 98, 105, 111, 115,
266 | 145, 221, 171, 105, 110, 116, 101, 114,
267 | 101, 115, 116, 105, 110, 103, 165, 116,
268 | 111, 112, 105, 99, 167, 112, 97, 121,
269 | 108, 111, 97, 100, 158, 142, 210, 31,
270 | 127, 81, 149, 125, 183, 108, 86, 17,
271 | 100, 35, 168];
272 |
273 | // should not throw OutOfMemoryError
274 | assertThrown!MessagePackException(unpack!PubSubMessage(bytes));
275 | }
276 |
277 |
278 | /**
279 | * Handy helper for creating MessagePackable object.
280 | *
281 | * toMsgpack / fromMsgpack are special methods for serialization / deserialization.
282 | * This template provides those methods to struct/class.
283 | *
284 | * Example:
285 | * -----
286 | * struct S
287 | * {
288 | * int num; string str;
289 | *
290 | * // http://d.puremagic.com/issues/show_bug.cgi?id = 1099
291 | * mixin MessagePackable; // all members
292 | * // mixin MessagePackable!("num"); // num only
293 | * }
294 | * -----
295 | *
296 | * Defines those methods manually if you treat complex data-structure.
297 | */
298 | mixin template MessagePackable(Members...)
299 | {
300 | static if (Members.length == 0) {
301 | /**
302 | * Serializes members using $(D_PARAM packer).
303 | *
304 | * Params:
305 | * packer = the serializer to pack.
306 | */
307 | void toMsgpack(Packer)(ref Packer packer, bool withFieldName = false) const
308 | {
309 | if (withFieldName) {
310 | packer.beginMap(this.tupleof.length);
311 | foreach (i, member; this.tupleof) {
312 | packer.pack(getFieldName!(typeof(this), i));
313 | packer.pack(member);
314 | }
315 | } else {
316 | packer.beginArray(this.tupleof.length);
317 | foreach (member; this.tupleof)
318 | packer.pack(member);
319 | }
320 | }
321 |
322 |
323 | /**
324 | * Deserializes $(D MessagePack) object to members using Value.
325 | *
326 | * Params:
327 | * value = the MessagePack value to unpack.
328 | *
329 | * Throws:
330 | * MessagePackException if $(D_PARAM value) is not an Array type.
331 | */
332 | void fromMsgpack(Value value)
333 | {
334 | // enables if std.contracts.enforce is moved to object_.d
335 | // enforceEx!MessagePackException(value.type == Value.Type.array, "Value must be Array type");
336 | if (value.type != Value.Type.array)
337 | throw new MessagePackException("Value must be an Array type");
338 | if (value.via.array.length != this.tupleof.length)
339 | throw new MessagePackException("The size of deserialized value is mismatched");
340 |
341 | foreach (i, member; this.tupleof)
342 | this.tupleof[i] = value.via.array[i].as!(typeof(member));
343 | }
344 |
345 |
346 | /**
347 | * Deserializes $(D MessagePack) object to members using direct-conversion deserializer.
348 | *
349 | * Params:
350 | * value = the reference to direct-conversion deserializer.
351 | *
352 | * Throws:
353 | * MessagePackException if the size of deserialized value is mismatched.
354 | */
355 | void fromMsgpack(ref Unpacker unpacker)
356 | {
357 | auto length = unpacker.beginArray();
358 | if (length != this.tupleof.length)
359 | throw new MessagePackException("The size of deserialized value is mismatched");
360 |
361 | foreach (i, member; this.tupleof)
362 | unpacker.unpack(this.tupleof[i]);
363 | }
364 | } else {
365 | /**
366 | * Member selecting version of toMsgpack.
367 | */
368 | void toMsgpack(Packer)(ref Packer packer, bool withFieldName = false) const
369 | {
370 | if (withFieldName) {
371 | packer.beginMap(Members.length);
372 | foreach (member; Members) {
373 | packer.pack(member);
374 | packer.pack(mixin(member));
375 | }
376 | } else {
377 | packer.beginArray(Members.length);
378 | foreach (member; Members)
379 | packer.pack(mixin(member));
380 | }
381 | }
382 |
383 |
384 | /**
385 | * Member selecting version of fromMsgpack for Value.
386 | */
387 | void fromMsgpack(Value value)
388 | {
389 | if (value.type != Value.Type.array)
390 | throw new MessagePackException("Value must be an Array type");
391 | if (value.via.array.length != Members.length)
392 | throw new MessagePackException("The size of deserialized value is mismatched");
393 |
394 | foreach (i, member; Members)
395 | mixin(member ~ "= value.via.array[i].as!(typeof(" ~ member ~ "));");
396 | }
397 |
398 |
399 | /**
400 | * Member selecting version of fromMsgpack for direct-converion deserializer.
401 | */
402 | void fromMsgpack(ref Unpacker unpacker)
403 | {
404 | auto length = unpacker.beginArray();
405 | if (length != Members.length)
406 | throw new MessagePackException("The size of deserialized value is mismatched");
407 |
408 | foreach (member; Members)
409 | unpacker.unpack(mixin(member));
410 | }
411 | }
412 | }
413 |
414 |
415 | unittest
416 | {
417 | { // all members
418 | /*
419 | * Comment out because "src/msgpack.d(4048): Error: struct msgpack.__unittest16.S no size yet for forward reference" occurs
420 | */
421 | static struct S
422 | {
423 | uint num; string str;
424 | mixin MessagePackable;
425 | }
426 |
427 | mixin DefinePacker;
428 |
429 | S orig = S(10, "Hi!"); orig.toMsgpack(packer);
430 |
431 | { // stream
432 | auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
433 |
434 | S result; result.fromMsgpack(unpacker.unpacked);
435 |
436 | assert(result.num == 10);
437 | assert(result.str == "Hi!");
438 | }
439 | { // direct conversion
440 | auto unpacker = Unpacker(packer.stream.data);
441 |
442 | S result; unpacker.unpack(result);
443 |
444 | assert(result.num == 10);
445 | assert(result.str == "Hi!");
446 | }
447 | }
448 | { // member select
449 | static class C
450 | {
451 | uint num; string str;
452 |
453 | this() {}
454 | this(uint n, string s) { num = n; str = s; }
455 |
456 | mixin MessagePackable!("num");
457 | }
458 |
459 | mixin DefinePacker;
460 |
461 | C orig = new C(10, "Hi!"); orig.toMsgpack(packer);
462 |
463 | { // stream
464 | auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
465 |
466 | C result = new C; result.fromMsgpack(unpacker.unpacked);
467 |
468 | assert(result.num == 10);
469 | }
470 | { // direct conversion
471 | auto unpacker = Unpacker(packer.stream.data);
472 |
473 | C result; unpacker.unpack(result);
474 |
475 | assert(result.num == 10);
476 | }
477 | }
478 | }
479 |
480 |
481 | unittest
482 | {
483 | import std.datetime: Clock, SysTime;
484 | import msgpack.packer, msgpack.unpacker;
485 |
486 | static struct SysTimePackProxy
487 | {
488 | static void serialize(ref Packer p, ref in SysTime tim)
489 | {
490 | p.pack(tim.toISOExtString());
491 | }
492 |
493 | static void deserialize(ref Unpacker u, ref SysTime tim)
494 | {
495 | string tmp;
496 | u.unpack(tmp);
497 | tim = SysTime.fromISOExtString(tmp);
498 | }
499 | }
500 | static struct LogData
501 | {
502 | string msg;
503 | string file;
504 | ulong line;
505 | @serializedAs!SysTimePackProxy SysTime timestamp;
506 |
507 | this(string message, string file = __FILE__, ulong line = __LINE__)
508 | {
509 | this.msg = message;
510 | this.file = file;
511 | this.line = line;
512 | this.timestamp = Clock.currTime();
513 | }
514 | }
515 |
516 | /// Now we can serialize/deserialize LogData
517 | LogData[] logs;
518 | logs ~= LogData("MessagePack is nice!");
519 | auto data = pack(logs);
520 | LogData[] datas = unpack!(LogData[])(data);
521 | assert(datas[0].timestamp.toString() == datas[0].timestamp.toString());
522 | }
523 |
--------------------------------------------------------------------------------
/src/msgpack/register.d:
--------------------------------------------------------------------------------
1 | module msgpack.register;
2 |
3 | import msgpack.packer;
4 | import msgpack.unpacker;
5 |
6 | import std.array;
7 |
8 |
9 | /**
10 | * Register a serialization handler for $(D_PARAM T) type
11 | *
12 | * Example:
13 | * -----
14 | * registerPackHandler!(Foo, fooPackHandler);
15 | * -----
16 | */
17 | void registerPackHandler(T, alias Handler, Stream = Appender!(ubyte[]))()
18 | {
19 | PackerImpl!(Stream).registerHandler!(T, Handler);
20 | }
21 |
22 |
23 | /**
24 | * Register a deserialization handler for $(D_PARAM T) type
25 | *
26 | * Example:
27 | * -----
28 | * registerUnackHandler!(Foo, fooUnackHandler);
29 | * -----
30 | */
31 | void registerUnpackHandler(T, alias Handler)()
32 | {
33 | Unpacker.registerHandler!(T, Handler);
34 | }
35 |
36 |
37 | /**
38 | * Register derived class for (de)serialization
39 | *
40 | * Example:
41 | * -----
42 | * registerClass!(DerivedClass);
43 | * -----
44 | */
45 | void registerClass(T, Stream = Appender!(ubyte[]))()
46 | {
47 | PackerImpl!(Stream).register!(T);
48 | Unpacker.register!(T);
49 | }
50 |
--------------------------------------------------------------------------------
/src/msgpack/streaming_unpacker.d:
--------------------------------------------------------------------------------
1 | module msgpack.streaming_unpacker;
2 |
3 | import msgpack.common;
4 | import msgpack.attribute;
5 | import msgpack.exception;
6 | import msgpack.value;
7 |
8 | import std.array;
9 | import std.exception;
10 | import std.range;
11 | import std.stdio;
12 | import std.traits;
13 | import std.typecons;
14 | import std.typetuple;
15 | import std.container;
16 |
17 |
18 | /**
19 | * $(D Unpacked) is a $(D Range) wrapper for stream deserialization result
20 | */
21 | struct Unpacked
22 | {
23 | import std.conv : text;
24 |
25 | Value value; /// deserialized value
26 |
27 | alias value this;
28 |
29 |
30 | /**
31 | * Constructs a $(D Unpacked) with argument.
32 | *
33 | * Params:
34 | * value = a deserialized value.
35 | */
36 | @safe
37 | this(ref Value value)
38 | {
39 | this.value = value;
40 | }
41 |
42 |
43 | /**
44 | * InputRange primitive operation that checks iteration state.
45 | *
46 | * Returns:
47 | * true if there are no more elements to be iterated.
48 | */
49 | @property @trusted
50 | nothrow bool empty() const // std.array.empty isn't nothrow function
51 | {
52 | return (value.type == Value.Type.array) && !value.via.array.length;
53 | }
54 |
55 |
56 | /**
57 | * Range primitive operation that returns the length of the range.
58 | *
59 | * Returns:
60 | * the number of values.
61 | */
62 | @property @trusted
63 | size_t length()
64 | {
65 | debug enforce(value.type == Value.Type.array, "lenght is called with non array object. type = " ~ text(value.type));
66 | return value.via.array.length;
67 | }
68 |
69 |
70 | /**
71 | * InputRange primitive operation that returns the currently iterated element.
72 | *
73 | * Returns:
74 | * the deserialized $(D Value).
75 | */
76 | @property @trusted
77 | ref Value front()
78 | {
79 | debug enforce(value.type == Value.Type.array, "front is called with non array object. type = " ~ text(value.type));
80 | return value.via.array.front;
81 | }
82 |
83 |
84 | /**
85 | * InputRange primitive operation that advances the range to its next element.
86 | */
87 | @trusted
88 | void popFront()
89 | {
90 | debug enforce(value.type == Value.Type.array, "popFront is called with non array object. type = " ~ text(value.type));
91 | value.via.array.popFront();
92 | }
93 |
94 | /**
95 | * RandomAccessRange primitive operation.
96 | *
97 | * Returns:
98 | * the deserialized $(D Value) at $(D_PARAM n) position.
99 | */
100 | @trusted
101 | ref Value opIndex(size_t n)
102 | {
103 | debug enforce(value.type == Value.Type.array, "opIndex is called with non array object. type = " ~ text(value.type));
104 | return value.via.array[n];
105 | }
106 |
107 | /**
108 | * Returns a slice of the range.
109 | *
110 | * Paramas:
111 | * from = the start point of slicing.
112 | * to = the end point of slicing.
113 | *
114 | * Returns:
115 | * the slice of Values.
116 | */
117 | @trusted
118 | Value[] opSlice(size_t from, size_t to)
119 | {
120 | debug enforce(value.type == Value.Type.array, "opSlice is called with non array object. type = " ~ text(value.type));
121 | return value.via.array[from..to];
122 | }
123 |
124 | /**
125 | * Range primitive operation that returns the snapshot.
126 | *
127 | * Returns:
128 | * the snapshot of this Value.
129 | */
130 | @property @safe
131 | Unpacked save()
132 | {
133 | return Unpacked(value);
134 | }
135 | }
136 |
137 |
138 | unittest
139 | {
140 | static assert(isForwardRange!Unpacked);
141 | static assert(hasLength!Unpacked);
142 | }
143 |
144 |
145 | /**
146 | * This $(D StreamingUnpacker) is a $(D MessagePack) streaming deserializer
147 | *
148 | * This implementation enables you to load multiple objects from a stream(like network).
149 | *
150 | * Example:
151 | * -----
152 | * ...
153 | * auto unpacker = StreamingUnpacker(serializedData);
154 | * ...
155 | *
156 | * // appends new data to buffer if pre execute() call didn't finish deserialization.
157 | * unpacker.feed(newSerializedData);
158 | *
159 | * while (unpacker.execute()) {
160 | * foreach (obj; unpacker.purge()) {
161 | * // do stuff (obj is a Value)
162 | * }
163 | * }
164 | *
165 | * if (unpacker.size)
166 | * throw new Exception("Message is too large");
167 | * -----
168 | */
169 | struct StreamingUnpacker
170 | {
171 | private:
172 | /*
173 | * Context state of deserialization
174 | */
175 | enum State
176 | {
177 | HEADER = 0x00,
178 |
179 | BIN8 = 0x04,
180 | BIN16,
181 | BIN32,
182 |
183 | // Floating point, Unsigned, Signed interger (== header & 0x03)
184 | FLOAT = 0x0a,
185 | DOUBLE,
186 | UINT8,
187 | UINT16,
188 | UINT32,
189 | UINT64,
190 | INT8,
191 | INT16,
192 | INT32,
193 | INT64,
194 |
195 | // Container (== header & 0x01)
196 | STR8 = 0x19,
197 | RAW16 = 0x1a,
198 | RAW32,
199 | ARRAY16,
200 | ARRAY36,
201 | MAP16,
202 | MAP32,
203 | RAW,
204 |
205 | // EXT family
206 | EXT8,
207 | EXT16,
208 | EXT32,
209 | EXT_DATA,
210 |
211 | // D-specific type
212 | REAL
213 | }
214 |
215 |
216 | /*
217 | * Element type of container
218 | */
219 | enum ContainerElement
220 | {
221 | ARRAY_ITEM,
222 | MAP_KEY,
223 | MAP_VALUE
224 | }
225 |
226 |
227 | /*
228 | * Internal stack context
229 | */
230 | static struct Context
231 | {
232 | static struct Container
233 | {
234 | ContainerElement type; // value container type
235 | Value value; // current value
236 | Value key; // for map value
237 | size_t count; // container length
238 | }
239 |
240 | State state; // current state of deserialization
241 | size_t trail; // current deserializing size
242 | size_t top; // current index of stack
243 | Container[] stack; // storing values
244 | }
245 |
246 | Context context_; // stack environment for streaming deserialization
247 |
248 | mixin InternalBuffer;
249 |
250 |
251 | public:
252 | /**
253 | * Constructs a $(D StreamingUnpacker).
254 | *
255 | * Params:
256 | * target = byte buffer to deserialize
257 | * bufferSize = size limit of buffer size
258 | */
259 | @safe
260 | this(in ubyte[] target, in size_t bufferSize = 8192)
261 | {
262 | initializeBuffer(target, bufferSize);
263 | initializeContext();
264 | }
265 |
266 |
267 | /**
268 | * Forwards to deserialized object.
269 | *
270 | * Returns:
271 | * the $(D Unpacked) object contains deserialized value.
272 | */
273 | @property @safe
274 | Unpacked unpacked()
275 | {
276 | return Unpacked(context_.stack[0].value);
277 | }
278 |
279 |
280 | /**
281 | * Clears some states for next deserialization.
282 | */
283 | @safe
284 | nothrow void clear()
285 | {
286 | initializeContext();
287 |
288 | parsed_ = 0;
289 | }
290 |
291 |
292 | /**
293 | * Convenient method for unpacking and clearing states.
294 | *
295 | * Example:
296 | * -----
297 | * foreach (obj; unpacker.purge()) {
298 | * // do stuff
299 | * }
300 | * -----
301 | * is equivalent to
302 | * -----
303 | * foreach (obj; unpacker.unpacked) {
304 | * // do stuff
305 | * }
306 | * unpacker.clear();
307 | * -----
308 | *
309 | * Returns:
310 | * the $(D Unpacked) object contains deserialized value.
311 | */
312 | @safe
313 | Unpacked purge()
314 | {
315 | auto result = Unpacked(context_.stack[0].value);
316 |
317 | clear();
318 |
319 | return result;
320 | }
321 |
322 |
323 | /**
324 | * Executes deserialization.
325 | *
326 | * Returns:
327 | * true if deserialization has been completed, otherwise false.
328 | *
329 | * Throws:
330 | * $(D UnpackException) when parse error occurs.
331 | */
332 | bool execute()
333 | {
334 | /*
335 | * Current implementation is very dirty(goto! goto!! goto!!!).
336 | * This Complexity for performance(avoid function call).
337 | */
338 |
339 | bool ret;
340 | size_t cur = offset_;
341 | Value obj;
342 |
343 | // restores before state
344 | auto state = context_.state;
345 | auto trail = context_.trail;
346 | auto top = context_.top;
347 | auto stack = &context_.stack;
348 |
349 | /*
350 | * Helper for container deserialization
351 | */
352 | void startContainer(string Type)(ContainerElement type, size_t length)
353 | {
354 | mixin("callback" ~ Type ~ "((*stack)[top].value, length);");
355 |
356 | (*stack)[top].type = type;
357 | (*stack)[top].count = length;
358 | (*stack).length = ++top + 1;
359 | }
360 |
361 | // non-deserialized data is nothing
362 | if (used_ - offset_ == 0)
363 | goto Labort;
364 |
365 | do {
366 | Lstart:
367 | if (state == State.HEADER) {
368 | const header = buffer_[cur];
369 |
370 | if (0x00 <= header && header <= 0x7f) { // positive
371 | callbackUInt(obj, header);
372 | goto Lpush;
373 | } else if (0xe0 <= header && header <= 0xff) { // negative
374 | callbackInt(obj, cast(byte)header);
375 | goto Lpush;
376 | } else if (0xa0 <= header && header <= 0xbf) { // fix raw
377 | trail = header & 0x1f;
378 | state = State.RAW;
379 | cur++;
380 | continue;
381 | } else if (0xd4 <= header && header <= 0xd8) { // fix ext
382 | trail = 2 ^^ (header - 0xd4) + 1;
383 | state = State.EXT_DATA;
384 | cur++;
385 | continue;
386 | } else if (0x90 <= header && header <= 0x9f) { // fix array
387 | size_t length = header & 0x0f;
388 | if (length == 0) {
389 | callbackArray(obj, 0);
390 | goto Lpush;
391 | } else {
392 | startContainer!"Array"(ContainerElement.ARRAY_ITEM, length);
393 | cur++;
394 | continue;
395 | }
396 | } else if (0x80 <= header && header <= 0x8f) { // fix map
397 | size_t length = header & 0x0f;
398 | if (length == 0) {
399 | callbackMap(obj, 0);
400 | goto Lpush;
401 | } else {
402 | startContainer!"Map"(ContainerElement.MAP_KEY, length);
403 | cur++;
404 | continue;
405 | }
406 | } else {
407 | switch (header) {
408 | case Format.UINT8, Format.UINT16, Format.UINT32, Format.UINT64,
409 | Format.INT8, Format.INT16, Format.INT32, Format.INT64,
410 | Format.FLOAT, Format.DOUBLE:
411 | trail = 1 << (header & 0x03); // computes object size
412 | state = cast(State)(header & 0x1f);
413 | break;
414 | case Format.REAL:
415 | trail = RealSize;
416 | state = State.REAL;
417 | break;
418 | case Format.ARRAY16, Format.ARRAY32,
419 | Format.MAP16, Format.MAP32:
420 | trail = 2 << (header & 0x01); // computes container size
421 | state = cast(State)(header & 0x1f);
422 | break;
423 | // raw will become str format in new spec
424 | case Format.STR8:
425 | case Format.RAW16: // will be STR16
426 | case Format.RAW32: // will be STR32
427 | trail = 1 << ((header & 0x03) - 1); // computes container size
428 | state = cast(State)(header & 0x1f);
429 | break;
430 | case Format.BIN8, Format.BIN16, Format.BIN32:
431 | trail = 1 << (header & 0x03); // computes container size
432 | state = cast(State)(header & 0x1f);
433 | break;
434 | case Format.EXT8:
435 | trail = 1;
436 | state = State.EXT8;
437 | break;
438 | case Format.EXT16:
439 | trail = 2;
440 | state = State.EXT16;
441 | break;
442 | case Format.EXT32:
443 | trail = 4;
444 | state = State.EXT32;
445 | break;
446 | case Format.NIL:
447 | callbackNil(obj);
448 | goto Lpush;
449 | case Format.TRUE:
450 | callbackBool(obj, true);
451 | goto Lpush;
452 | case Format.FALSE:
453 | callbackBool(obj, false);
454 | goto Lpush;
455 | default:
456 | throw new UnpackException("Unknown type");
457 | }
458 |
459 | cur++;
460 | goto Lstart;
461 | }
462 | } else {
463 | // data lack for deserialization
464 | if (used_ - cur < trail)
465 | goto Labort;
466 |
467 | const base = cur; cur += trail - 1; // fix current position
468 |
469 | final switch (state) {
470 | case State.FLOAT:
471 | _f temp;
472 |
473 | temp.i = load32To!uint(buffer_[base..base + trail]);
474 | callbackFloat(obj, temp.f);
475 | goto Lpush;
476 | case State.DOUBLE:
477 | _d temp;
478 |
479 | temp.i = load64To!ulong(buffer_[base..base + trail]);
480 | callbackFloat(obj, temp.f);
481 | goto Lpush;
482 | case State.REAL:
483 | const expb = base + ulong.sizeof;
484 |
485 | version (NonX86)
486 | {
487 | CustomFloat!80 temp;
488 |
489 | const frac = load64To!ulong (buffer_[base..expb]);
490 | const exp = load16To!ushort(buffer_[expb..expb + ushort.sizeof]);
491 |
492 | temp.significand = frac;
493 | temp.exponent = exp & 0x7fff;
494 | temp.sign = exp & 0x8000 ? true : false;
495 |
496 | // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max.
497 | callbackFloat(obj, temp.get!real);
498 | }
499 | else
500 | {
501 | _r temp;
502 |
503 | temp.fraction = load64To!(typeof(temp.fraction))(buffer_[base..expb]);
504 | temp.exponent = load16To!(typeof(temp.exponent))(buffer_[expb..expb + temp.exponent.sizeof]);
505 |
506 | callbackFloat(obj, temp.f);
507 | }
508 |
509 | goto Lpush;
510 | case State.UINT8:
511 | callbackUInt(obj, buffer_[base]);
512 | goto Lpush;
513 | case State.UINT16:
514 | callbackUInt(obj, load16To!ushort(buffer_[base..base + trail]));
515 | goto Lpush;
516 | case State.UINT32:
517 | callbackUInt(obj, load32To!uint(buffer_[base..base + trail]));
518 | goto Lpush;
519 | case State.UINT64:
520 | callbackUInt(obj, load64To!ulong(buffer_[base..base + trail]));
521 | goto Lpush;
522 | case State.INT8:
523 | callbackInt(obj, cast(byte)buffer_[base]);
524 | goto Lpush;
525 | case State.INT16:
526 | callbackInt(obj, load16To!short(buffer_[base..base + trail]));
527 | goto Lpush;
528 | case State.INT32:
529 | callbackInt(obj, load32To!int(buffer_[base..base + trail]));
530 | goto Lpush;
531 | case State.INT64:
532 | callbackInt(obj, load64To!long(buffer_[base..base + trail]));
533 | goto Lpush;
534 | case State.RAW: Lraw:
535 | hasRaw_ = true;
536 | callbackRaw(obj, buffer_[base..base + trail]);
537 | goto Lpush;
538 |
539 | case State.EXT_DATA: Lext:
540 | hasRaw_ = true;
541 | obj.via.ext.type = buffer_[base];
542 | callbackExt(obj, buffer_[base+1..base+trail]);
543 | goto Lpush;
544 | case State.EXT8:
545 | trail = buffer_[base] + 1;
546 | if (trail == 0)
547 | goto Lext;
548 | state = State.EXT_DATA;
549 | cur++;
550 | goto Lstart;
551 | case State.EXT16:
552 | trail = load16To!size_t(buffer_[base..base+trail]) + 1;
553 | if (trail == 0)
554 | goto Lext;
555 | state = State.EXT_DATA;
556 | cur++;
557 | goto Lstart;
558 | case State.EXT32:
559 | trail = load32To!size_t(buffer_[base..base+trail]) + 1;
560 | if (trail == 0)
561 | goto Lext;
562 | state = State.EXT_DATA;
563 | cur++;
564 | goto Lstart;
565 |
566 | case State.STR8, State.BIN8:
567 | trail = buffer_[base];
568 | if (trail == 0)
569 | goto Lraw;
570 | state = State.RAW;
571 | cur++;
572 | goto Lstart;
573 | case State.RAW16, State.BIN16:
574 | trail = load16To!size_t(buffer_[base..base + trail]);
575 | if (trail == 0)
576 | goto Lraw;
577 | state = State.RAW;
578 | cur++;
579 | goto Lstart;
580 | case State.RAW32, State.BIN32:
581 | trail = load32To!size_t(buffer_[base..base + trail]);
582 | if (trail == 0)
583 | goto Lraw;
584 | state = State.RAW;
585 | cur++;
586 | goto Lstart;
587 | case State.ARRAY16:
588 | size_t length = load16To!size_t(buffer_[base..base + trail]);
589 | if (length == 0) {
590 | callbackArray(obj, 0);
591 | goto Lpush;
592 | } else {
593 | startContainer!"Array"(ContainerElement.ARRAY_ITEM, length);
594 | state = State.HEADER;
595 | cur++;
596 | continue;
597 | }
598 | case State.ARRAY36:
599 | size_t length = load32To!size_t(buffer_[base..base + trail]);
600 | if (length == 0) {
601 | callbackArray(obj, 0);
602 | goto Lpush;
603 | } else {
604 | startContainer!"Array"(ContainerElement.ARRAY_ITEM, length);
605 | state = State.HEADER;
606 | cur++;
607 | continue;
608 | }
609 | case State.MAP16:
610 | size_t length = load16To!size_t(buffer_[base..base + trail]);
611 | if (length == 0) {
612 | callbackMap(obj, 0);
613 | goto Lpush;
614 | } else {
615 | startContainer!"Map"(ContainerElement.MAP_KEY, length);
616 | state = State.HEADER;
617 | cur++;
618 | continue;
619 | }
620 | case State.MAP32:
621 | size_t length = load32To!size_t(buffer_[base..base + trail]);
622 | if (length == 0) {
623 | callbackMap(obj, 0);
624 | goto Lpush;
625 | } else {
626 | startContainer!"Map"(ContainerElement.MAP_KEY, length);
627 | state = State.HEADER;
628 | cur++;
629 | continue;
630 | }
631 | case State.HEADER:
632 | break;
633 | }
634 | }
635 |
636 | Lpush:
637 | if (top == 0)
638 | goto Lfinish;
639 |
640 | auto container = &(*stack)[top - 1];
641 |
642 | final switch (container.type) {
643 | case ContainerElement.ARRAY_ITEM:
644 | container.value.via.array ~= obj;
645 | if (--container.count == 0) {
646 | obj = container.value;
647 | top--;
648 | goto Lpush;
649 | }
650 | break;
651 | case ContainerElement.MAP_KEY:
652 | container.key = obj;
653 | container.type = ContainerElement.MAP_VALUE;
654 | break;
655 | case ContainerElement.MAP_VALUE:
656 | container.value.via.map[container.key] = obj;
657 | if (--container.count == 0) {
658 | obj = container.value;
659 | top--;
660 | goto Lpush;
661 | }
662 | container.type = ContainerElement.MAP_KEY;
663 | }
664 |
665 | state = State.HEADER;
666 | cur++;
667 | } while (cur < used_);
668 |
669 | goto Labort;
670 |
671 | Lfinish:
672 | (*stack)[0].value = obj;
673 | ret = true;
674 | cur++;
675 | goto Lend;
676 |
677 | Labort:
678 | ret = false;
679 |
680 | Lend:
681 | context_.state = state;
682 | context_.trail = trail;
683 | context_.top = top;
684 | parsed_ += cur - offset_;
685 | offset_ = cur;
686 |
687 | return ret;
688 | }
689 |
690 |
691 | /**
692 | * supports foreach. One loop provides $(D Unpacked) object contains execute() result.
693 | * This is convenient in case that $(D MessagePack) values are continuous.
694 | */
695 | int opApply(scope int delegate(ref Unpacked) dg)
696 | {
697 | int result;
698 |
699 | while (execute()) {
700 | auto unpackedResult = Unpacked(context_.stack[0].value);
701 | result = dg(unpackedResult);
702 | if (result)
703 | break;
704 |
705 | clear();
706 | }
707 |
708 | return result;
709 | }
710 |
711 |
712 | private:
713 | /*
714 | * initializes internal stack environment.
715 | */
716 | @safe
717 | nothrow void initializeContext()
718 | {
719 | context_.state = State.HEADER;
720 | context_.trail = 0;
721 | context_.top = 0;
722 | context_.stack.length = 1;
723 | }
724 | }
725 |
726 |
727 | unittest
728 | {
729 | import msgpack.packer;
730 |
731 | {
732 | // serialize
733 | mixin DefinePacker;
734 |
735 | packer.packArray(null, true, 1, -2, "Hi!", [1], [1:1], double.max, ExtValue(7, [1,2,3,4]));
736 |
737 | // deserialize
738 | auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
739 | auto unpacked = unpacker.purge();
740 |
741 | // Range test
742 | foreach (unused; 0..2) {
743 | uint i;
744 |
745 | foreach (obj; unpacked)
746 | i++;
747 |
748 | assert(i == unpacked.via.array.length);
749 | }
750 |
751 | auto result = unpacked.via.array;
752 |
753 | assert(result[0].type == Value.Type.nil);
754 | assert(result[1].via.boolean == true);
755 | assert(result[2].via.uinteger == 1);
756 | assert(result[3].via.integer == -2);
757 | assert(result[4].via.raw == [72, 105, 33]);
758 | assert(result[5].as!(int[]) == [1]);
759 | assert(result[6].as!(int[int]) == [1:1]);
760 | assert(result[7].as!(double) == double.max);
761 | assert(result[8].as!(ExtValue) == ExtValue(7, [1,2,3,4]));
762 | }
763 |
764 | // Test many combinations of EXT
765 | {
766 | mixin DefinePacker;
767 |
768 | alias Lengths = TypeTuple!(0, 1, 2, 3, 4, 5, 8, 15, 16, 31,
769 | 255, 256, 2^^16, 2^^32);
770 |
771 | // Initialize a bunch of ExtValues and pack them
772 | ExtValue[Lengths.length] values;
773 | foreach (I, L; Lengths)
774 | values[I] = ExtValue(7, new ubyte[](L));
775 | packer.pack(values);
776 |
777 | auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute();
778 | auto unpacked = unpacker.purge();
779 |
780 | // Compare unpacked values to originals
781 | size_t i = 0;
782 | foreach (deserialized; unpacked)
783 | assert(deserialized == values[i++]);
784 | }
785 | }
786 |
787 |
788 | private:
789 | @trusted:
790 |
791 |
792 | /**
793 | * Sets value type and value.
794 | *
795 | * Params:
796 | * value = the value to set
797 | * number = the content to set
798 | */
799 | void callbackUInt(ref Value value, ulong number)
800 | {
801 | value.type = Value.Type.unsigned;
802 | value.via.uinteger = number;
803 | }
804 |
805 |
806 | /// ditto
807 | void callbackInt(ref Value value, long number)
808 | {
809 | value.type = Value.Type.signed;
810 | value.via.integer = number;
811 | }
812 |
813 |
814 | /// ditto
815 | void callbackFloat(ref Value value, real number)
816 | {
817 | value.type = Value.Type.floating;
818 | value.via.floating = number;
819 | }
820 |
821 |
822 | /// ditto
823 | void callbackRaw(ref Value value, ubyte[] raw)
824 | {
825 | value.type = Value.Type.raw;
826 | value.via.raw = raw;
827 | }
828 |
829 | /// ditto
830 | void callbackExt(ref Value value, ubyte[] raw)
831 | {
832 | value.type = Value.Type.ext;
833 | value.via.ext.data = raw;
834 | }
835 |
836 | /// ditto
837 | void callbackArray(ref Value value, size_t length)
838 | {
839 | value.type = Value.Type.array;
840 | value.via.array.length = 0;
841 | value.via.array.reserve(length);
842 | }
843 |
844 |
845 | /// ditto
846 | void callbackMap(ref Value value, lazy size_t length)
847 | {
848 | value.type = Value.Type.map;
849 | value.via.map = null; // clears previous result avoiding 'Access Violation'
850 | }
851 |
852 |
853 | /// ditto
854 | void callbackNil(ref Value value)
855 | {
856 | value.type = Value.Type.nil;
857 | }
858 |
859 |
860 | /// ditto
861 | void callbackBool(ref Value value, bool boolean)
862 | {
863 | value.type = Value.Type.boolean;
864 | value.via.boolean = boolean;
865 | }
866 |
867 |
868 | unittest
869 | {
870 | Value value;
871 |
872 | // Unsigned integer
873 | callbackUInt(value, uint.max);
874 | assert(value.type == Value.Type.unsigned);
875 | assert(value.via.uinteger == uint.max);
876 |
877 | // Signed integer
878 | callbackInt(value, int.min);
879 | assert(value.type == Value.Type.signed);
880 | assert(value.via.integer == int.min);
881 |
882 | // Floating point
883 | callbackFloat(value, real.max);
884 | assert(value.type == Value.Type.floating);
885 | assert(value.via.floating == real.max);
886 |
887 | // Raw
888 | callbackRaw(value, cast(ubyte[])[1]);
889 | assert(value.type == Value.Type.raw);
890 | assert(value.via.raw == cast(ubyte[])[1]);
891 |
892 | // Array
893 | Value[] array; array.reserve(16);
894 |
895 | callbackArray(value, 16);
896 | assert(value.type == Value.Type.array);
897 | assert(value.via.array.capacity == array.capacity);
898 |
899 | // Map
900 | Value[Value] map;
901 |
902 | callbackMap(value, 16);
903 | assert(value.type == Value.Type.map);
904 | assert(value.via.map == null);
905 |
906 | // NIL
907 | callbackNil(value);
908 | assert(value.type == Value.Type.nil);
909 |
910 | // Bool
911 | callbackBool(value, true);
912 | assert(value.type == Value.Type.boolean);
913 | assert(value.via.boolean == true);
914 | }
915 |
--------------------------------------------------------------------------------
/src/msgpack/value.d:
--------------------------------------------------------------------------------
1 | module msgpack.value;
2 |
3 | import msgpack.common;
4 | import msgpack.attribute;
5 | import msgpack.exception;
6 |
7 | import std.json;
8 | import std.container : Array;
9 | import std.traits;
10 | import std.typecons : Tuple, isTuple;
11 |
12 |
13 | /**
14 | * $(D Value) is a $(D MessagePack) value representation
15 | *
16 | * Example:
17 | * -----
18 | * auto unpacker = StreamingUnpacker(pack(1, 0.1L) ~ pack(true) ~ pack("foobarbaz"));
19 | *
20 | * foreach (unpacked; unpacker) {
21 | * if (unpacked.type == Value.Type.array) {
22 | * foreach (obj; unpacked) {
23 | * switch (obj.type) {
24 | * case Value.Type.unsigned: writeln(obj.as!(uint)); break;
25 | * case Value.Type.floating: writeln(obj.as!(real)); break;
26 | * defalut:
27 | * throw new Exception("Unknown type");
28 | * }
29 | * }
30 | * } else {
31 | * if (unpacked.type == Value.Type.boolean)
32 | * writeln(unpacked.as!(bool));
33 | * else
34 | * writeln("Message: ", unpacked.as!(string));
35 | * }
36 | * }
37 | * -----
38 | */
39 | struct Value
40 | {
41 | /**
42 | * $(D MessagePack) value type
43 | */
44 | static enum Type
45 | {
46 | nil, /// nil(null in D)
47 | boolean, /// true, false
48 | unsigned, /// positive fixnum, uint 8, uint 16, uint 32, uint 64
49 | signed, /// negative fixnum, int 8, int 16, int 32, int 64
50 | floating, /// float, double, real
51 | array, /// fix array, array 16, array 32
52 | map, /// fix map, map 16, map 32
53 | raw, /// fix raw, raw 16, raw 32
54 | ext /// fix ext, ext8, ext16, ext32
55 | }
56 |
57 |
58 | /**
59 | * msgpack value representation
60 | */
61 | static union Via
62 | {
63 | bool boolean; /// corresponding to Type.boolean
64 | ulong uinteger; /// corresponding to Type.unsigned
65 | long integer; /// corresponding to Type.signed
66 | real floating; /// corresponding to Type.floating
67 | Value[] array; /// corresponding to Type.array
68 | Value[Value] map; /// corresponding to Type.map
69 | ubyte[] raw; /// corresponding to Type.raw
70 | ExtValue ext; /// corresponding to Type.ext
71 | }
72 |
73 |
74 | Type type; /// represents value type
75 | Via via; /// represents real value
76 |
77 |
78 | /**
79 | * Constructs a $(D Value) with arguments.
80 | *
81 | * Params:
82 | * value = the real content.
83 | * type = the type of value.
84 | */
85 | @safe
86 | this(Type type)
87 | {
88 | this.type = type;
89 | }
90 |
91 | @safe
92 | this(typeof(null))
93 | {
94 | this(Type.nil);
95 | }
96 |
97 | /// ditto
98 | @trusted
99 | this(bool value, Type type = Type.boolean)
100 | {
101 | this(type);
102 | via.boolean = value;
103 | }
104 |
105 |
106 | /// ditto
107 | @trusted
108 | this(ulong value, Type type = Type.unsigned)
109 | {
110 | this(type);
111 | via.uinteger = value;
112 | }
113 |
114 |
115 | /// ditto
116 | @trusted
117 | this(long value, Type type = Type.signed)
118 | {
119 | this(type);
120 | via.integer = value;
121 | }
122 |
123 |
124 | /// ditto
125 | @trusted
126 | this(real value, Type type = Type.floating)
127 | {
128 | this(type);
129 | via.floating = value;
130 | }
131 |
132 |
133 | /// ditto
134 | @trusted
135 | this(Value[] value, Type type = Type.array)
136 | {
137 | this(type);
138 | via.array = value;
139 | }
140 |
141 |
142 | /// ditto
143 | @trusted
144 | this(Value[Value] value, Type type = Type.map)
145 | {
146 | this(type);
147 | via.map = value;
148 | }
149 |
150 |
151 | /// ditto
152 | @trusted
153 | this(ubyte[] value, Type type = Type.raw)
154 | {
155 | this(type);
156 | via.raw = value;
157 | }
158 |
159 | /// This is unsafe overload because using cast internally.
160 | @trusted
161 | this(string value, Type type = Type.raw)
162 | {
163 | this(type);
164 | via.raw = cast(ubyte[])value;
165 | }
166 |
167 | /**
168 | * Constructs a $(D Value) with arguments.
169 | *
170 | * Params:
171 | * value = the real content.
172 | * type = the type of value.
173 | */
174 | @trusted
175 | this(ExtValue value, Type type = Type.ext)
176 | {
177 | this(type);
178 | via.ext = value;
179 | }
180 |
181 | /**
182 | * Converts value to $(D_PARAM T) type.
183 | *
184 | * Returns:
185 | * converted value.
186 | *
187 | * Throws:
188 | * MessagePackException if type is mismatched.
189 | *
190 | * NOTE:
191 | * Current implementation uses cast.
192 | */
193 | @property @trusted
194 | T as(T)() if (is(Unqual!T == bool))
195 | {
196 | if (type != Type.boolean)
197 | onCastError();
198 |
199 | return via.boolean;
200 | }
201 |
202 |
203 | /// ditto
204 | @property @trusted
205 | T as(T)() if (isIntegral!T && !is(Unqual!T == enum))
206 | {
207 | if (type == Type.unsigned)
208 | return cast(T)via.uinteger;
209 |
210 | if (type == Type.signed)
211 | return cast(T)via.integer;
212 |
213 | onCastError();
214 |
215 | assert(false);
216 | }
217 |
218 |
219 | /// ditto
220 | @property @trusted
221 | T as(T)() if (isFloatingPoint!T && !is(Unqual!T == enum))
222 | {
223 | if (type != Type.floating)
224 | onCastError();
225 |
226 | return cast(T)via.floating;
227 | }
228 |
229 |
230 | /// ditto
231 | @property @trusted
232 | T as(T)() if (is(Unqual!T == enum))
233 | {
234 | return cast(T)as!(OriginalType!T);
235 | }
236 |
237 |
238 | /// ditto
239 | @property @trusted
240 | T as(T)() if (is(Unqual!T == ExtValue))
241 | {
242 | if (type != Type.ext)
243 | onCastError();
244 |
245 | return cast(T)via.ext;
246 | }
247 |
248 |
249 | /// ditto
250 | @property @trusted
251 | T as(T)() if ((isArray!T ||
252 | isInstanceOf!(Array, T)) &&
253 | !is(Unqual!T == enum))
254 | {
255 | alias typeof(T.init[0]) V;
256 |
257 | if (type == Type.nil) {
258 | static if (isDynamicArray!T) {
259 | return null;
260 | } else {
261 | return T.init;
262 | }
263 | }
264 |
265 | static if (isByte!V || isSomeChar!V) {
266 | if (type != Type.raw)
267 | onCastError();
268 |
269 | static if (isDynamicArray!T) {
270 | return cast(T)via.raw;
271 | } else {
272 | if (via.raw.length != T.length)
273 | onCastError();
274 |
275 | return cast(T)(via.raw[0 .. T.length]);
276 | }
277 | } else {
278 | if (type != Type.array)
279 | onCastError();
280 |
281 | V[] array;
282 |
283 | foreach (elem; via.array)
284 | array ~= elem.as!(V);
285 |
286 | return array;
287 | }
288 | }
289 |
290 |
291 | /// ditto
292 | @property @trusted
293 | T as(T)() if (isAssociativeArray!T)
294 | {
295 | alias typeof(T.init.keys[0]) K;
296 | alias typeof(T.init.values[0]) V;
297 |
298 | if (type == Type.nil)
299 | return null;
300 |
301 | if (type != Type.map)
302 | onCastError();
303 |
304 | V[K] map;
305 |
306 | foreach (key, value; via.map)
307 | map[key.as!(K)] = value.as!(V);
308 |
309 | return map;
310 | }
311 |
312 |
313 | /**
314 | * Converts to $(D_PARAM T) type.
315 | *
316 | * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is:
317 | * -----
318 | * void fromMsgpack(Value value)
319 | * -----
320 | * This method assigns converted values to all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D fromMsgpack).
321 | *
322 | * Params:
323 | * args = arguments to class constructor(class only).
324 | *
325 | * Returns:
326 | * converted value.
327 | */
328 | @property @trusted
329 | T as(T, Args...)(Args args) if (is(Unqual!T == class))
330 | {
331 | if (type == Type.nil)
332 | return null;
333 |
334 | T object = new T(args);
335 |
336 | static if (hasMember!(T, "fromMsgpack"))
337 | {
338 | static if (__traits(compiles, { object.fromMsgpack(this); })) {
339 | object.fromMsgpack(this);
340 | } else {
341 | static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
342 | }
343 | } else {
344 | alias SerializingClasses!(T) Classes;
345 |
346 | if (via.array.length != SerializingMemberNumbers!(Classes))
347 | throw new MessagePackException("The number of deserialized object member is mismatched");
348 |
349 | size_t offset;
350 | foreach (Class; Classes) {
351 | Class obj = cast(Class)object;
352 | foreach (i, member; obj.tupleof) {
353 | static if (isPackedField!(Class.tupleof[i]))
354 | obj.tupleof[i] = via.array[offset++].as!(typeof(member));
355 | }
356 | }
357 | }
358 |
359 | return object;
360 | }
361 |
362 |
363 | /// ditto
364 | @property @trusted
365 | T as(T)() if (is(Unqual!T == struct) && !is(Unqual!T == ExtValue))
366 | {
367 | T obj;
368 |
369 | static if (hasMember!(T, "fromMsgpack"))
370 | {
371 | static if (__traits(compiles, { obj.fromMsgpack(this); })) {
372 | obj.fromMsgpack(this);
373 | } else {
374 | static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
375 | }
376 | } else {
377 | static if (isTuple!T) {
378 | if (via.array.length != T.Types.length)
379 | throw new MessagePackException("The number of deserialized Tuple element is mismatched");
380 |
381 | foreach (i, Type; T.Types)
382 | obj.field[i] = via.array[i].as!(Type);
383 | } else { // simple struct
384 | if (via.array.length != SerializingMemberNumbers!T)
385 | throw new MessagePackException("The number of deserialized struct member is mismatched");
386 |
387 | size_t offset;
388 | foreach (i, member; obj.tupleof) {
389 | static if (isPackedField!(T.tupleof[i]))
390 | obj.tupleof[i] = via.array[offset++].as!(typeof(member));
391 | }
392 | }
393 | }
394 |
395 | return obj;
396 | }
397 |
398 |
399 | /**
400 | * Special method called by $(D Packer).
401 | *
402 | * Params:
403 | * packer = a MessagePack serializer.
404 | */
405 | void toMsgpack(Packer)(ref Packer packer) const
406 | {
407 | final switch (type) {
408 | case Type.nil:
409 | packer.pack(null);
410 | break;
411 | case Type.boolean:
412 | packer.pack(via.boolean);
413 | break;
414 | case Type.unsigned:
415 | packer.pack(via.uinteger);
416 | break;
417 | case Type.signed:
418 | packer.pack(via.integer);
419 | break;
420 | case Type.floating:
421 | packer.pack(via.floating);
422 | break;
423 | case Type.raw:
424 | packer.pack(via.raw);
425 | break;
426 | case Type.ext:
427 | packer.packExt(via.ext.type, via.ext.data);
428 | break;
429 | case Type.array:
430 | packer.beginArray(via.array.length);
431 | foreach (elem; via.array)
432 | elem.toMsgpack(packer);
433 | break;
434 | case Type.map:
435 | packer.beginMap(via.map.length);
436 | foreach (key, value; via.map) {
437 | key.toMsgpack(packer);
438 | value.toMsgpack(packer);
439 | }
440 | break;
441 | }
442 | }
443 |
444 |
445 | /**
446 | * Comparison for equality. @trusted for union.
447 | */
448 | @trusted
449 | bool opEquals(Tdummy = void)(ref const Value other) const
450 | {
451 | if (type != other.type)
452 | return false;
453 |
454 | final switch (other.type) {
455 | case Type.nil: return true;
456 | case Type.boolean: return opEquals(other.via.boolean);
457 | case Type.unsigned: return opEquals(other.via.uinteger);
458 | case Type.signed: return opEquals(other.via.integer);
459 | case Type.floating: return opEquals(other.via.floating);
460 | case Type.raw: return opEquals(other.via.raw);
461 | case Type.ext: return opEquals(other.via.ext);
462 | case Type.array: return opEquals(other.via.array);
463 | case Type.map: return opEquals(other.via.map);
464 | }
465 | }
466 |
467 |
468 | /// ditto
469 | @trusted
470 | bool opEquals(T : bool)(in T other) const
471 | {
472 | if (type != Type.boolean)
473 | return false;
474 |
475 | return via.boolean == other;
476 | }
477 |
478 |
479 | /// ditto
480 | @trusted
481 | bool opEquals(T : ulong)(in T other) const
482 | {
483 | static if (__traits(isUnsigned, T)) {
484 | if (type != Type.unsigned)
485 | return false;
486 |
487 | return via.uinteger == other;
488 | } else {
489 | if (type != Type.signed)
490 | return false;
491 |
492 | return via.integer == other;
493 | }
494 | }
495 |
496 |
497 | /// ditto
498 | @trusted
499 | bool opEquals(T : real)(in T other) const
500 | {
501 | if (type != Type.floating)
502 | return false;
503 |
504 | return via.floating == other;
505 | }
506 |
507 |
508 | /// ditto
509 | @trusted
510 | bool opEquals(T : const Value[])(in T other) const
511 | {
512 | if (type != Type.array)
513 | return false;
514 |
515 | return via.array == other;
516 | }
517 |
518 |
519 | /// ditto
520 | @trusted
521 | bool opEquals(T : const Value[Value])(in T other) const
522 | {
523 | if (type != Type.map)
524 | return false;
525 |
526 | // This comparison is instead of default comparison because 'via.map == other' raises "Access Violation".
527 | foreach (key, value; via.map) {
528 | if (key in other) {
529 | if (other[key] != value)
530 | return false;
531 | } else {
532 | return false;
533 | }
534 | }
535 |
536 | return true;
537 | }
538 |
539 |
540 | /// ditto
541 | @trusted
542 | bool opEquals(T : const(ubyte)[])(in T other) const
543 | {
544 | if (type != Type.raw)
545 | return false;
546 |
547 | return via.raw == other;
548 | }
549 |
550 |
551 | /// ditto
552 | @trusted
553 | bool opEquals(T : string)(in T other) const
554 | {
555 | if (type != Type.raw)
556 | return false;
557 |
558 | return via.raw == cast(ubyte[])other;
559 | }
560 |
561 |
562 | //
563 | @trusted
564 | bool opEquals(T : ExtValue)(in T other) const
565 | {
566 | if (type != Type.ext)
567 | return false;
568 |
569 | return via.ext.type == other.type && via.ext.data == other.data;
570 | }
571 |
572 |
573 | @trusted
574 | hash_t toHash() const nothrow
575 | {
576 | static hash_t getHash(T)(T* v) @safe nothrow
577 | {
578 | return typeid(T).getHash(v);
579 | }
580 |
581 | final switch (type) {
582 | case Type.nil: return 0;
583 | case Type.boolean: return getHash(&via.boolean);
584 | case Type.unsigned: return getHash(&via.uinteger);
585 | case Type.signed: return getHash(&via.integer);
586 | case Type.floating: return getHash(&via.floating);
587 | case Type.raw: return getHash(&via.raw);
588 | case Type.ext: return getHash(&via.ext);
589 | case Type.array:
590 | hash_t ret;
591 | foreach (elem; via.array)
592 | ret ^= elem.toHash();
593 | return ret;
594 | case Type.map:
595 | try {
596 | hash_t ret;
597 | foreach (key, value; via.map) {
598 | ret ^= key.toHash();
599 | ret ^= value.toHash();
600 | }
601 | return ret;
602 | } catch(Throwable) assert(0);
603 | }
604 | }
605 | }
606 |
607 |
608 | unittest
609 | {
610 | import std.array;
611 |
612 | // nil
613 | Value value = Value(null);
614 | Value other = Value();
615 |
616 | assert(value == other);
617 | assert(value.type == Value.Type.nil);
618 |
619 | // boolean
620 | value = Value(true);
621 | other = Value(false);
622 |
623 | assert(value != other);
624 | assert(value.type == Value.Type.boolean);
625 | assert(value.as!(bool) == true);
626 | assert(other == false);
627 |
628 | try {
629 | auto b = value.as!(uint);
630 | assert(false);
631 | } catch (MessagePackException e) { }
632 |
633 | // unsigned integer
634 | value = Value(10UL);
635 | other = Value(10UL);
636 |
637 | assert(value == other);
638 | assert(value.type == Value.Type.unsigned);
639 | assert(value.as!(uint) == 10);
640 | assert(other == 10UL);
641 |
642 | // signed integer
643 | value = Value(-20L);
644 | other = Value(-10L);
645 |
646 | assert(value != other);
647 | assert(value.type == Value.Type.signed);
648 | assert(value.as!(int) == -20);
649 | assert(other == -10L);
650 |
651 | // enum
652 | enum E : int { F = -20 }
653 |
654 | E e = value.as!(E);
655 | assert(e == E.F);
656 |
657 | // floating point
658 | value = Value(0.1e-10L);
659 | other = Value(0.1e-20L);
660 |
661 | assert(value != other);
662 | assert(value.type == Value.Type.floating);
663 | assert(value.as!(real) == 0.1e-10L);
664 | assert(other == 0.1e-20L);
665 |
666 | // raw
667 | value = Value(cast(ubyte[])[72, 105, 33]);
668 | other = Value(cast(ubyte[])[72, 105, 33]);
669 |
670 | assert(value == other);
671 | assert(value.type == Value.Type.raw);
672 | assert(value.as!(string) == "Hi!");
673 | assert(value.as!(ubyte[3]) == [72, 105, 33]);
674 | assert(other == cast(ubyte[])[72, 105, 33]);
675 |
676 | // raw with string
677 | value = Value("hello");
678 | other = Value("hello");
679 |
680 | assert(value == other);
681 | assert(value.type == Value.Type.raw);
682 | assert(value.as!(string) == "hello");
683 |
684 | // enum : string
685 | enum EStr : string { elem = "hello" }
686 |
687 | assert(value.as!(EStr) == EStr.elem);
688 |
689 | // ext
690 | auto ext = ExtValue(7, [1,2,3]);
691 | value = Value(ExtValue(7, [1,2,3]));
692 | assert(value.as!ExtValue == ext);
693 |
694 | // array
695 | auto t = Value(cast(ubyte[])[72, 105, 33]);
696 | value = Value([t]);
697 | other = Value([t]);
698 |
699 | assert(value == other);
700 | assert(value.type == Value.Type.array);
701 | assert(value.as!(string[]) == ["Hi!"]);
702 | assert(other == [t]);
703 |
704 | // map
705 | value = Value([Value(1L):Value(2L)]);
706 | other = Value([Value(1L):Value(1L)]);
707 |
708 | assert(value != other);
709 | assert(value.type == Value.Type.map);
710 | assert(value.as!(int[int]) == [1:2]);
711 | assert(other == [Value(1L):Value(1L)]);
712 |
713 | value = Value(10UL);
714 |
715 | // struct
716 | static struct S
717 | {
718 | ulong num;
719 |
720 | void fromMsgpack(Value value) { num = value.via.uinteger; }
721 | }
722 |
723 | S s = value.as!(S);
724 | assert(s.num == 10);
725 |
726 | value = Value([Value(0.5f), Value(cast(ubyte[])[72, 105, 33])]);
727 |
728 | // struct
729 | static struct Simple
730 | {
731 | @nonPacked int era;
732 | double num;
733 | string msg;
734 | }
735 |
736 | Simple simple = value.as!(Simple);
737 | assert(simple.era == int.init);
738 | assert(simple.num == 0.5f);
739 | assert(simple.msg == "Hi!");
740 |
741 | value = Value(10UL);
742 |
743 | // class
744 | static class C
745 | {
746 | ulong num;
747 |
748 | void fromMsgpack(Value value) { num = value.via.uinteger; }
749 | }
750 |
751 | C c = value.as!(C);
752 | assert(c.num == 10);
753 |
754 | static class SimpleA
755 | {
756 | bool flag = true;
757 | }
758 |
759 | static class SimpleB : SimpleA
760 | {
761 | ubyte type = 100;
762 | }
763 |
764 | static class SimpleC : SimpleB
765 | {
766 | @nonPacked string str;
767 | uint num = uint.max;
768 | }
769 |
770 | value = Value([Value(false), Value(99UL), Value(cast(ulong)(uint.max / 2u))]);
771 |
772 | SimpleC sc = value.as!(SimpleC);
773 | assert(sc.flag == false);
774 | assert(sc.type == 99);
775 | assert(sc.num == uint.max / 2);
776 | assert(sc.str.empty);
777 |
778 | // std.typecons.Tuple
779 | value = Value([Value(true), Value(1UL), Value(cast(ubyte[])"Hi!")]);
780 |
781 | auto tuple = value.as!(Tuple!(bool, uint, string));
782 | assert(tuple.field[0] == true);
783 | assert(tuple.field[1] == 1u);
784 | assert(tuple.field[2] == "Hi!");
785 |
786 | /*
787 | * non-MessagePackable object is stopped by static assert
788 | * static struct NonMessagePackable {}
789 | * auto nonMessagePackable = value.as!(NonMessagePackable);
790 | */
791 | }
792 |
793 |
794 | /**
795 | * Converts $(D Value) to $(D JSONValue).
796 | *
797 | * Params:
798 | * val = $(D Value) to convert.
799 | *
800 | * Returns:
801 | * a $(D JSONValue).
802 | */
803 | @trusted
804 | JSONValue toJSONValue(in Value val)
805 | {
806 | final switch (val.type)
807 | {
808 | case Value.Type.nil: return JSONValue(null);
809 | case Value.Type.boolean: return JSONValue(val.via.boolean);
810 | case Value.Type.unsigned: return JSONValue(val.via.uinteger);
811 | case Value.Type.signed: return JSONValue(val.via.integer);
812 | case Value.Type.floating: return JSONValue(val.via.floating);
813 | case Value.Type.raw: return JSONValue(cast(string)(val.via.raw.idup));
814 | case Value.Type.ext: throw new MessagePackException("Unable to convert ext to json");
815 | case Value.Type.array: {
816 | JSONValue[] vals;
817 | foreach (elem; val.via.array)
818 | vals ~= elem.toJSONValue();
819 | return JSONValue(vals);
820 | }
821 | case Value.Type.map: {
822 | JSONValue[string] vals;
823 | foreach (key, value; val.via.map) {
824 | if (key.type != Value.Type.raw)
825 | {
826 | throw new MessagePackException("JSON-object key must be a raw type");
827 | }
828 | vals[key.as!string] = value.toJSONValue();
829 | }
830 | return JSONValue(vals);
831 | }
832 | }
833 | }
834 |
835 | /**
836 | * Converts $(D JSONValue) to $(D Value).
837 | *
838 | * Params:
839 | * val = $(D JSONValue) to convert.
840 | *
841 | * Returns:
842 | * a $(D Value).
843 | */
844 | @trusted
845 | Value fromJSONValue(in JSONValue val)
846 | {
847 | final switch (val.type())
848 | {
849 | case JSONType.null_: return Value(null);
850 | case JSONType.true_: return Value(true);
851 | case JSONType.false_: return Value(false);
852 | case JSONType.uinteger: return Value(val.uinteger);
853 | case JSONType.integer: return Value(val.integer);
854 | case JSONType.float_: return Value(val.floating);
855 | case JSONType.string: return Value(cast(ubyte[])(val.str));
856 | case JSONType.array: {
857 | Value[] vals;
858 | foreach (elem; val.array)
859 | vals ~= elem.fromJSONValue();
860 | return Value(vals);
861 | }
862 | case JSONType.object: {
863 | Value[Value] vals;
864 | foreach (key, value; val.object) {
865 | vals[Value(cast(ubyte[])key)] = value.fromJSONValue();
866 | }
867 | return Value(vals);
868 | }
869 | }
870 | }
871 |
872 | unittest
873 | {
874 | import std.array : array;
875 | import std.algorithm : equal, map;
876 | import std.conv;
877 | import std.math : isClose;
878 | import std.range;
879 | import msgpack;
880 |
881 | // nil
882 | Value value = Value(null);
883 |
884 | assert(toJSONValue(value).type() == JSONType.null_);
885 |
886 | // boolean
887 | value = Value(true);
888 | auto other = Value(false);
889 |
890 | assert(toJSONValue(value).type() == JSONType.true_);
891 | assert(toJSONValue(other).type() == JSONType.false_);
892 |
893 | // unsigned integer
894 | value = Value(10UL);
895 |
896 | assert(value.toJSONValue().type == JSONType.uinteger);
897 | assert(value.toJSONValue().uinteger == value.as!uint);
898 | assert(value.toJSONValue().uinteger == 10UL);
899 |
900 | // signed integer
901 | value = Value(-20L);
902 |
903 | assert(value.toJSONValue().type == JSONType.integer);
904 | assert(value.toJSONValue().integer == value.as!int);
905 |
906 | // enum
907 | enum E : int { F = -20 }
908 | value = Value(cast(long)(E.F));
909 |
910 | assert(value.toJSONValue().type == JSONType.integer);
911 | assert(value.toJSONValue().integer == E.F);
912 |
913 | // floating point
914 | value = Value(0.1e-10L);
915 | other = Value(0.1e-20L);
916 |
917 | assert(value.toJSONValue().type == JSONType.float_);
918 | assert(other.toJSONValue().type == JSONType.float_);
919 |
920 | assert(isClose(value.toJSONValue().floating, 0.1e-10L));
921 | assert(isClose(other.toJSONValue().floating, 0.1e-20L));
922 |
923 | // raw
924 | long[] arr = [72, 105, 33];
925 | value = Value(to!(ubyte[])(arr));
926 |
927 | assert(value.toJSONValue().type == JSONType.string);
928 | assert(equal(value.toJSONValue().str, arr));
929 |
930 | // raw with string
931 | value = Value("hello");
932 | assert(value.toJSONValue().type == JSONType.string);
933 | assert(value.toJSONValue().str == "hello");
934 |
935 | // array
936 | auto t = Value(to!(ubyte[])(arr));
937 | value = Value([t]);
938 | other = Value(array(map!(a => Value(a))(arr)));
939 |
940 | assert(value.toJSONValue().type == JSONType.array);
941 | assert(value.toJSONValue().array.length == 1);
942 | assert(value.toJSONValue().array.front().type == JSONType.string);
943 | assert(equal(value.toJSONValue().array.front().str, arr));
944 | assert(other.toJSONValue().type == JSONType.array);
945 | assert(array(map!(a => a.integer)(other.toJSONValue().array)) == arr);
946 |
947 | // map
948 | value = Value([Value("key"):Value(2L)]);
949 |
950 | assert(value.toJSONValue().type == JSONType.object);
951 | assert("key" in value.toJSONValue().object);
952 | assert(value.toJSONValue().object["key"].type == JSONType.integer);
953 | assert(value.toJSONValue().object["key"].integer == 2L);
954 |
955 | // struct
956 | static struct Simple
957 | {
958 | @nonPacked int era;
959 | double num;
960 | string msg;
961 | }
962 |
963 | Simple simple;
964 | simple.era = 5;
965 | simple.num = 13.5;
966 | simple.msg = "helloworld";
967 | value = simple.pack().unpack().value;
968 |
969 | assert(value.toJSONValue().type == JSONType.array);
970 | assert(value.toJSONValue().array.length == 2);
971 | assert(value.toJSONValue().array[0].type == JSONType.float_);
972 | assert(isClose(value.toJSONValue().array[0].floating, simple.num));
973 | assert(value.toJSONValue().array[1].type == JSONType.string);
974 | assert(value.toJSONValue().array[1].str == simple.msg);
975 |
976 | // class
977 | static class SimpleA
978 | {
979 | bool flag = true;
980 | }
981 |
982 | static class SimpleB : SimpleA
983 | {
984 | ubyte type = 100;
985 | }
986 |
987 | static class SimpleC : SimpleB
988 | {
989 | @nonPacked string str;
990 | uint num = uint.max;
991 | }
992 |
993 | SimpleC sc = new SimpleC;
994 | value = sc.pack!true().unpack().value;
995 |
996 | assert(value.toJSONValue().type == JSONType.object);
997 | assert(value.toJSONValue().object.length == 3);
998 | assert("flag" in value.toJSONValue().object);
999 | assert(value.toJSONValue().object["flag"].type == (sc.flag ? JSONType.true_ : JSONType.false_));
1000 | assert("type" in value.toJSONValue().object);
1001 | assert(value.toJSONValue().object["type"].type == JSONType.uinteger);
1002 | assert(value.toJSONValue().object["type"].uinteger == sc.type);
1003 | assert("num" in value.toJSONValue().object);
1004 | assert(value.toJSONValue().object["num"].type == JSONType.uinteger);
1005 | assert(value.toJSONValue().object["num"].uinteger == sc.num);
1006 |
1007 | other = value.toJSONValue().fromJSONValue();
1008 | assert(value == other);
1009 | }
1010 |
1011 |
1012 | private:
1013 |
1014 |
1015 | /**
1016 | * A callback for type-mismatched error in cast conversion.
1017 | */
1018 | @safe
1019 | pure void onCastError()
1020 | {
1021 | throw new MessagePackException("Attempt to cast with another type");
1022 | }
1023 |
--------------------------------------------------------------------------------
/win32.mak:
--------------------------------------------------------------------------------
1 | DMD = dmd
2 | LIB = msgpack.lib
3 | DFLAGS = -O -release -inline -nofloat -w -d -Isrc
4 | UDFLAGS = -w -g -debug -unittest
5 |
6 | SRCS = src\msgpack.d
7 |
8 | # DDoc
9 | DOCDIR = html
10 | CANDYDOC = html\candydoc\candy.ddoc html\candydoc\modules.ddoc
11 | DDOCFLAGS = -Dd$(DOCDIR) -c -o- -Isrc $(CANDYDOC)
12 |
13 | DOCS = $(DOCDIR)\msgpack.html
14 |
15 | target: doc $(LIB)
16 |
17 | $(LIB):
18 | $(DMD) $(DFLAGS) -lib -of$(LIB) $(SRCS)
19 |
20 | doc:
21 | $(DMD) $(DDOCFLAGS) $(SRCS)
22 |
23 | clean:
24 | rm $(DOCS) $(LIB)
25 |
--------------------------------------------------------------------------------