1 /**********************************************************\
2 |                                                          |
3 |                          hprose                          |
4 |                                                          |
5 | Official WebSite: http://www.hprose.com/                 |
6 |                   http://www.hprose.org/                 |
7 |                                                          |
8 \**********************************************************/
9 
10 /**********************************************************\
11  *                                                        *
12  * hprose/io/reader.d                                     *
13  *                                                        *
14  * hprose reader library for D.                           *
15  *                                                        *
16  * LastModified: Aug 3, 2016                              *
17  * Author: Ma Bingyao <andot@hprose.com>                  *
18  *                                                        *
19 \**********************************************************/
20 
21 module hprose.io.reader;
22 
23 @trusted:
24 
25 import hprose.io.bytes;
26 import hprose.io.classmanager;
27 import hprose.io.common;
28 import hprose.io.tags;
29 import std.algorithm;
30 import std.bigint;
31 import std.container;
32 import std.conv;
33 import std.datetime;
34 import std.exception;
35 import std.json;
36 import std.math;
37 import std.stdio;
38 import std.string;
39 import std.traits;
40 import std.typecons;
41 import std.utf;
42 import std.uuid;
43 import std.variant;
44 
45 private {
46     interface ReaderRefer {
47         void set(Variant value);
48         Variant read(int index);
49         void reset();
50     }
51 
52     final class FakeReaderRefer : ReaderRefer {
53         void set(Variant value) {}
54         Variant read(int index) {
55             throw new Exception("Unexcepted serialize tag '" ~ TagRef ~ "' in stream");
56         }
57         void reset() {}
58     }
59 
60     final class RealReaderRefer : ReaderRefer {
61         private {
62             Variant[] _references;
63         }
64         void set(Variant value) {
65             _references ~= value;
66         }
67         Variant read(int index) {
68             return _references[index];
69         }
70         void reset() {
71             _references = null;
72         }
73     }
74 }
75 
76 class RawReader {
77     protected {
78         BytesIO _bytes;
79     }
80     private {
81         int stoi(string str) {
82             if (str.length == 0) return 0;
83             return to!int(str);
84         }
85         void readNumberRaw(BytesIO bytes) {
86             bytes.write(_bytes.readBytes(TagSemicolon));
87         }
88         void readDateTimeRaw(BytesIO bytes) {
89             bytes.write(_bytes.readBytes(TagSemicolon, TagUTC));
90         }
91         void readUTF8CharRaw(BytesIO bytes) {
92             bytes.write(_bytes.readUTF8Char());
93         }
94         void readBytesRaw(BytesIO bytes) {
95             string len = cast(string)_bytes.readUntil(TagQuote);
96             bytes.write(len).write(TagQuote).write(_bytes.read(stoi(len))).write(TagQuote);
97             _bytes.skip(1);
98         }
99         void readStringRaw(BytesIO bytes) {
100             string len = cast(string)_bytes.readUntil(TagQuote);
101             bytes.write(len).write(TagQuote).write(_bytes.readString(stoi(len))).write(TagQuote);
102             _bytes.skip(1);
103         }
104         void readGuidRaw(BytesIO bytes) {
105             bytes.write(_bytes.read(38));
106         }
107         void readComplexRaw(BytesIO bytes) {
108             bytes.write(_bytes.readBytes(TagOpenbrace));
109             ubyte tag;
110             while ((tag = _bytes.read()) != TagClosebrace) {
111                 readRaw(bytes, tag);
112             }
113             bytes.write(tag);
114         }
115     }
116 
117     this(BytesIO bytes) {
118         _bytes = bytes;
119     }
120 
121     @property BytesIO stream() { return _bytes; }
122 
123     static Exception unexpectedTag(char tag, string expectTags = "") {
124         if ((tag != 0) && (expectTags.length != 0)) {
125             return new Exception("Tags '" ~ expectTags ~ "' expected, but '" ~ tag ~ "' found in stream");
126         }
127         else if (tag != 0) {
128             return new Exception("Unexpected serialize tag '" ~ tag ~ "' in stream");
129         }
130         else {
131             return new Exception("No byte found in stream");
132         }
133     }
134     BytesIO readRaw() {
135         BytesIO bytes = new BytesIO();
136         readRaw(bytes);
137         return bytes;
138     }
139     BytesIO readRaw(BytesIO bytes) {
140         return readRaw(bytes, _bytes.read());
141     }
142     BytesIO readRaw(BytesIO bytes, char tag) {
143         bytes.write(tag);
144         switch (tag) {
145             case '0': .. case '9': break;
146             case TagNull, TagEmpty, TagTrue, TagFalse, TagNaN: break;
147             case TagInfinity: bytes.write(_bytes.read()); break;
148             case TagInteger, TagLong, TagDouble, TagRef: readNumberRaw(bytes); break;
149             case TagDate, TagTime: readDateTimeRaw(bytes); break;
150             case TagUTF8Char: readUTF8CharRaw(bytes); break;
151             case TagBytes: readBytesRaw(bytes); break;
152             case TagString: readStringRaw(bytes); break;
153             case TagGuid: readGuidRaw(bytes); break;
154             case TagList, TagMap, TagObject: readComplexRaw(bytes); break;
155             case TagClass: readComplexRaw(bytes); readRaw(bytes); break;
156             case TagError: readRaw(bytes); break;
157             default: throw unexpectedTag(tag);
158         }
159         return bytes;
160     }
161 }
162 
163 class Reader : RawReader {
164     private {
165         bool _simple;
166         ReaderRefer _refer;
167         Tuple!(TypeInfo, string[], string)[] _classref;
168 
169         string _readStringWithoutTag() {
170             string result = _bytes.readString(_bytes.readInt(TagQuote));
171             _bytes.skip(1);
172             return result;
173         }
174         T readRef(T)() {
175             static if (is(Unqual!T == Variant)) {
176                 return cast(T)_refer.read(_bytes.readInt(TagSemicolon));
177             }
178             else {
179                 return cast(T)_refer.read(_bytes.readInt(TagSemicolon)).get!(Unqual!T);
180             }
181         }
182         T readInteger(T)(char tag) if (isIntegral!T) {
183             alias U = Unqual!T;
184             switch(tag) {
185                 case '0': .. case '9': return cast(T)(tag - '0');
186                 case TagInteger, TagLong: return readIntegerWithoutTag!T();
187                 case TagDouble: return cast(T)to!U(readDoubleWithoutTag!real());
188                 case TagNull: return cast(T)to!U(0);
189                 case TagEmpty: return cast(T)to!U(0);
190                 case TagTrue: return cast(T)to!U(1);
191                 case TagFalse: return cast(T)to!U(0);
192                 case TagUTF8Char: return cast(T)to!U(readUTF8CharWithoutTag!string());
193                 case TagString: return cast(T)to!U(readStringWithoutTag!string());
194                 case TagRef: return cast(T)to!U(readRef!string());
195                 default: throw unexpectedTag(tag);
196             }
197         }
198         T readBigInt(T)(char tag) if (is(Unqual!T == BigInt)) {
199             switch(tag) {
200                 case '0': .. case '9': return cast(T)BigInt(tag - '0');
201                 case TagInteger, TagLong: return readBigIntWithoutTag!T();
202                 case TagDouble: return cast(T)BigInt(to!long(readDoubleWithoutTag!real()));
203                 case TagNull: return cast(T)BigInt(0);
204                 case TagEmpty: return cast(T)BigInt(0);
205                 case TagTrue: return cast(T)BigInt(1);
206                 case TagFalse: return cast(T)BigInt(0);
207                 case TagUTF8Char: return cast(T)BigInt(readUTF8CharWithoutTag!string());
208                 case TagString: return cast(T)BigInt(readStringWithoutTag!string());
209                 case TagRef: return cast(T)BigInt(readRef!string());
210                 default: throw unexpectedTag(tag);
211             }
212         }
213         T readDouble(T)(char tag) if (isFloatingPoint!T) {
214             alias U = Unqual!T;
215             switch(tag) {
216                 case '0': .. case '9': return cast(T)to!U(tag - '0');
217                 case TagInteger: return cast(T)readIntegerWithoutTag!int();
218                 case TagLong, TagDouble: return readDoubleWithoutTag!T();
219                 case TagNaN: return cast(T)U.nan;
220                 case TagInfinity: return readInfinityWithoutTag!T();
221                 case TagNull: return cast(T)to!U(0);
222                 case TagEmpty: return cast(T)to!U(0);
223                 case TagTrue: return cast(T)to!U(1);
224                 case TagFalse: return cast(T)to!U(0);
225                 case TagUTF8Char: return cast(T)to!U(readUTF8CharWithoutTag!string());
226                 case TagString: return cast(T)to!U(readStringWithoutTag!string());
227                 case TagRef: return cast(T)to!U(readRef!string());
228                 default: throw unexpectedTag(tag);
229             }
230         }
231         T readBoolean(T)(char tag) if (isBoolean!T) {
232             switch(tag) {
233                 case '0': return cast(T)(false);
234                 case '1': .. case '9': return cast(T)(true);
235                 case TagInteger: return cast(T)(readIntegerWithoutTag!int() != 0);
236                 case TagLong: return cast(T)(readBigIntWithoutTag!BigInt() != BigInt(0));
237                 case TagDouble: return cast(T)(readDoubleWithoutTag!real() != 0);
238                 case TagNull: return cast(T)(false);
239                 case TagEmpty: return cast(T)(false);
240                 case TagTrue: return cast(T)(true);
241                 case TagFalse: return cast(T)(false);
242                 case TagUTF8Char: return cast(T)(countUntil("\00", readUTF8CharWithoutTag!char()) >= 0);
243                 case TagString: {
244                     auto v = readStringWithoutTag!string();
245                     return cast(T)(v != null && v != "" && v != "false");
246                 }
247                 case TagRef: {
248                     auto v = readRef!string();
249                     return cast(T)(v != null && v != "" && v != "false");
250                 }
251                 default: throw unexpectedTag(tag);
252             }
253         }
254         T readDateTime(T)(char tag) if (is(Unqual!T == Date) ||
255             is(Unqual!T == TimeOfDay) ||
256             is(Unqual!T == DateTime) ||
257             is(Unqual!T == SysTime)) {
258             switch(tag) {
259                 case TagDate: return readDateWithoutTag!T();
260                 case TagTime: return readTimeWithoutTag!T();
261                 case TagRef: return readRef!T();
262                 default: throw unexpectedTag(tag);
263             }
264         }
265         T readUTF8Char(T)(char tag) if (isSomeChar!T) {
266             alias U = Unqual!T;
267             switch(tag) {
268                 case '0': .. case '9': return cast(T)(tag - '0');
269                 case TagInteger, TagLong: return cast(T)readIntegerWithoutTag!int();
270                 case TagDouble: return cast(T)to!U(readDoubleWithoutTag!real());
271                 case TagUTF8Char: return readUTF8CharWithoutTag!T();
272                 case TagString: return cast(T)readStringWithoutTag!(U[])()[0];
273                 case TagRef: return cast(T)readRef!(U[])()[0];
274                 default: throw unexpectedTag(tag);
275             }
276         }
277         T readString(T)(char tag) if (isSomeString!T) {
278             alias U = Unqual!T;
279             switch(tag) {
280                 case '0': .. case '9': return cast(T)to!U("" ~ cast(char)tag);
281                 case TagInteger, TagLong, TagDouble: return cast(T)to!U(cast(string)_bytes.readUntil(TagSemicolon));
282                 case TagNaN: return cast(T)to!U("NAN");
283                 case TagInfinity: return cast(T)to!U(readInfinityWithoutTag!real());
284                 case TagNull: return null;
285                 case TagEmpty: return cast(T)to!U("");
286                 case TagTrue: return cast(T)to!U("true");
287                 case TagFalse: return cast(T)to!U("true");
288                 case TagUTF8Char: return readUTF8CharWithoutTag!T();
289                 case TagString: return readStringWithoutTag!T();
290                 case TagRef: return readRef!T();
291                 default: throw unexpectedTag(tag);
292             }
293         }
294         T readUUID(T)(char tag) if (Unqual!T == UUID) {
295             switch(tag) {
296                 case TagNull: return UUID.init;
297                 case TagEmpty: return UUID.init;
298                 case TagBytes: return UUID(readBytesWithoutTag!(ubyte[16])());
299                 case TagGuid: return readUUIDWithoutTag!T();
300                 case TagString: return UUID(readStringWithoutTag!string());
301                 case TagRef: return readRef!T();
302                 default: throw unexpectedTag(tag);
303             }
304         }
305         T readBytes(T)(char tag) if (isArray!T &&
306             (is(Unqual!(ForeachType!T) == ubyte) ||
307                 is(Unqual!(ForeachType!T) == byte))) {
308             switch(tag) {
309                 case TagNull: return T.init;
310                 case TagEmpty: return T.init;
311                 case TagUTF8Char: return cast(T)(readUTF8CharWithoutTag!string());
312                 case TagString: return cast(T)(readStringWithoutTag!string());
313                 case TagBytes: return readBytesWithoutTag!T();
314                 case TagList: return readArrayWithoutTag!T();
315                 case TagRef: return readRef!T();
316                 default: throw unexpectedTag(tag);
317             }
318         }
319         T readArray(T)(char tag) if (isArray!T) {
320             switch(tag) {
321                 case TagNull: return null;
322                 case TagEmpty: return T.init;
323                 case TagList: return readArrayWithoutTag!T();
324                 case TagRef: return readRef!T();
325                 default: throw unexpectedTag(tag);
326             }
327         }
328         T readObjectAsMap(T)() if (isAssociativeArray!T) {
329             auto index = _bytes.readInt!(int)(TagOpenbrace);
330             auto fields = _classref[index][1];
331             alias KT = Unqual!(KeyType!T);
332             alias VT = Unqual!(ValueType!T);
333             VT[KT] value;
334             setRef(value);
335             foreach(f; fields) {
336                 auto k = cast(KT)f;
337                 auto v = unserialize!VT();
338                 value[k] = v;
339             }
340             _bytes.skip(1);
341             return cast(T)value;
342         }
343         T readAssociativeArray(T)(char tag) if (isAssociativeArray!T) {
344             switch(tag) {
345                 case TagNull: return null;
346                 case TagEmpty: return T.init;
347                 case TagMap: return readAssociativeArrayWithoutTag!T();
348                 case TagClass: {
349                     readClass(); return readAssociativeArray!T();
350                 }
351                 case TagObject: return readObjectAsMap!T();
352                 case TagRef: return readRef!T();
353                 default: throw unexpectedTag(tag);
354             }
355         }
356         T readJSONValue(T)(char tag) if (is(Unqual!T == JSONValue)) {
357             switch(tag) {
358                 case '0': .. case '9': return cast(T)JSONValue(cast(long)(tag - '0'));
359                 case TagInteger: return cast(T)JSONValue(readIntegerWithoutTag!int());
360                 case TagLong: {
361                     BigInt bi = readBigIntWithoutTag!BigInt();
362                     if (bi > BigInt(ulong.max) || bi < BigInt(long.min)) {
363                         return cast(T)JSONValue(bi.toDecimalString());
364                     }
365                     else if (bi > BigInt(long.max)) {
366                         return cast(T)JSONValue(bi.toDecimalString().to!ulong());
367                     }
368                     else {
369                         return cast(T)JSONValue(bi.toLong());
370                     }
371                 }
372                 case TagDouble: return cast(T)JSONValue(readDoubleWithoutTag!double());
373                 case TagNaN: return cast(T)JSONValue(double.nan);
374                 case TagInfinity: return cast(T)JSONValue(readInfinityWithoutTag!double());
375                 case TagNull: return cast(T)JSONValue(null);
376                 case TagEmpty: return cast(T)JSONValue("");
377                 case TagTrue: return cast(T)JSONValue(true);
378                 case TagFalse: return cast(T)JSONValue(false);
379                 case TagUTF8Char: return cast(T)JSONValue(readUTF8CharWithoutTag!string());
380                 case TagString: return cast(T)JSONValue(readStringWithoutTag!string());
381                 case TagDate: return cast(T)JSONValue(readDateWithoutTag!SysTime().toString());
382                 case TagTime: return cast(T)JSONValue(readTimeWithoutTag!TimeOfDay().toString());
383                 case TagGuid: return cast(T)JSONValue(readUUIDWithoutTag!UUID().toString());
384                 case TagList: return cast(T)JSONValue(readArrayWithoutTag!(JSONValue[])());
385                 case TagMap: return cast(T)JSONValue(readAssociativeArrayWithoutTag!(JSONValue[string])());
386                 case TagClass: {
387                     readClass(); return cast(T)JSONValue(readAssociativeArray!(JSONValue[string])());
388                 }
389                 case TagObject: return cast(T)JSONValue(readObjectAsMap!(JSONValue[string])());
390                 default: throw unexpectedTag(tag);
391             }
392         }
393         T readObjectAsVariant(T)() if (is(Unqual!T == Variant)) {
394             auto index = _bytes.readInt!(int)(TagOpenbrace);
395             TypeInfo t = _classref[index][0];
396             string[] fields = _classref[index][1];
397             auto unserializer = ClassManager.getUnserializer(t, this);
398             if (unserializer is null) {
399                 throw new Exception("Type " ~ _classref[index][2] ~ " is not registered.");
400             }
401             unserializer.setRef();
402             foreach(f; fields) unserializer.setField(f);
403             _bytes.skip(1);
404             return cast(T)(unserializer.get());
405         }
406         T readVariant(T)(char tag) if (is(Unqual!T == Variant)) {
407             switch(tag) {
408                 case '0': .. case '9': return cast(T)Variant(cast(int)(tag - '0'));
409                 case TagInteger: return cast(T)Variant(readIntegerWithoutTag!int());
410                 case TagLong: return cast(T)Variant(readBigIntWithoutTag!BigInt());
411                 case TagDouble: return cast(T)Variant(readDoubleWithoutTag!double());
412                 case TagNaN: return cast(T)Variant(double.nan);
413                 case TagInfinity: return cast(T)Variant(readInfinityWithoutTag!double());
414                 case TagNull: return cast(T)Variant(null);
415                 case TagEmpty: return cast(T)Variant("");
416                 case TagTrue: return cast(T)Variant(true);
417                 case TagFalse: return cast(T)Variant(false);
418                 case TagUTF8Char: return cast(T)Variant(readUTF8CharWithoutTag!string());
419                 case TagString: return cast(T)Variant(readStringWithoutTag!string());
420                 case TagDate: return cast(T)Variant(readDateWithoutTag!SysTime());
421                 case TagTime: return cast(T)Variant(readTimeWithoutTag!TimeOfDay());
422                 case TagGuid: return cast(T)Variant(readUUIDWithoutTag!UUID());
423                 case TagList: return cast(T)Variant(readArrayWithoutTag!(Variant[])());
424                 case TagMap: return cast(T)Variant(readAssociativeArrayWithoutTag!(Variant[Variant])());
425                 case TagClass: {
426                     readClass(); return readVariant!T();
427                 }
428                 case TagObject: return readObjectAsVariant!T();
429                 case TagRef: return cast(T)readRef!Variant();
430                 default: throw unexpectedTag(tag);
431             }
432         }
433         void readClass() {
434             string classalias = _readStringWithoutTag();
435             TypeInfo classtype = ClassManager.getClass(classalias);
436             int count = _bytes.readInt(TagOpenbrace);
437             string[] fields;
438             for (int i = 0; i < count; ++i) {
439                 fields ~= readString!string();
440             }
441             _bytes.skip(1);
442             _classref ~= tuple(classtype, fields, classalias);
443         }
444         T readMapAsObject(T)() if (is(T == struct) || is(T == class)) {
445             alias U = Unqual!T;
446             static if (is(U == Object)) {
447                 throw new Exception("cannot convert AssociativeArray to std.Object.");
448             }
449             else {
450                 auto count = _bytes.readInt!(int)(TagOpenbrace);
451                 ClassManager.register!T(U.stringof);
452                 auto unserializer = ClassManager.getUnserializer(typeid(U), this);
453                 unserializer.setRef();
454                 foreach(int i; 0..count) {
455                     auto f = unserialize!(string)();
456                     unserializer.setField(f);
457                 }
458                 _bytes.skip(1);
459                 return cast(T)(unserializer.get().get!U);
460             }
461         }
462         T readObject(T)(char tag) if (is(T == struct) || is(T == class)) {
463             switch(tag) {
464                 static if (is(T == class)) {
465                     case TagNull: return cast(T)null;
466                 }
467                 case TagMap: return readMapAsObject!T();
468                 case TagClass: {
469                     readClass(); return readObject!T();
470                 }
471                 case TagObject: return readObjectWithoutTag!T();
472                 case TagRef: return readRef!T();
473                 default: throw unexpectedTag(tag);
474             }
475         }
476         T unserialize(T)(char tag) if (isSerializable!T) {
477             alias U = Unqual!T;
478             static if (is(U == enum)) {
479                 return cast(T)unserialize!(OriginalType!T)(tag);
480             }
481             else static if (isIntegral!T) {
482                 return readInteger!T(tag);
483             }
484             else static if (is(U == BigInt)) {
485                 return readBigInt!T(tag);
486             }
487             else static if (isFloatingPoint!T) {
488                 return readDouble!T(tag);
489             }
490             else static if (isBoolean!T) {
491                 return readBoolean!T(tag);
492             }
493             else static if (is(U == Date) || is(U == TimeOfDay) ||
494                 is(U == DateTime) || is(U == SysTime)) {
495                 return readDateTime!T(tag);
496             }
497             else static if (isSomeChar!T) {
498                 return readUTF8Char!T(tag);
499             }
500             else static if (isSomeString!T) {
501                 return readString!T(tag);
502             }
503             else static if (is(U == UUID)) {
504                 return readUUID!T(tag);
505             }
506             else static if (isArray!T) {
507                 alias ET = Unqual!(ForeachType!T);
508                 static if (is(ET == ubyte) || is(ET == byte)) {
509                     return readBytes!T(tag);
510                 }
511                 else {
512                     return readArray!T(tag);
513                 }
514             }
515             else static if (isAssociativeArray!T) {
516                 return readAssociativeArray!T(tag);
517             }
518             else static if (is(U == JSONValue)) {
519                 return readJSONValue!T(tag);
520             }
521             else static if (is(U == Variant)) {
522                 return readVariant!T(tag);
523             }
524             else {
525                 return readObject!T(tag);
526             }
527         }
528     }
529     package {
530         void setRef(T)(ref T value) {
531             if (_simple) return;
532             _refer.set(Variant(value));
533         }
534         void setRef(T)(T value) if (is(T == typeof(null))) {
535             if (_simple) return;
536             _refer.set(Variant(null));
537         }
538     }
539 
540     this(BytesIO bytes, bool simple = false) {
541         super(bytes);
542         _simple = simple;
543         if (simple) {
544             _refer = new FakeReaderRefer();
545         }
546         else {
547             _refer = new RealReaderRefer();
548         }
549     }
550     void reset() {
551         _classref = null;
552         _refer.reset();
553     }
554     T unserialize(T)() if (isSerializable!T) {
555         char tag = _bytes.read();
556         alias U = Unqual!T;
557         static if (isInstanceOf!(Nullable, U)) {
558             if (tag == TagNull) {
559                 return T.init;
560             }
561             else {
562                 return cast(T)U(unserialize!(typeof(U.init.get()))(tag));
563             }
564         }
565         else {
566             return unserialize!T(tag);
567         }
568     }
569     void unserialize(T = void)() if (is(T == void)) {
570         unserialize!Variant(tag);
571     }
572     T readIntegerWithoutTag(T)() if (isIntegral!T) {
573         return cast(T)_bytes.readInt!(Unqual!T)(TagSemicolon);
574     }
575     T readInteger(T)() if (isIntegral!T) {
576         return readInteger!T(_bytes.read());
577     }
578     T readBigIntWithoutTag(T)() if (is(Unqual!T == BigInt)) {
579         return cast(T)BigInt(cast(string)_bytes.readUntil(TagSemicolon));
580     }
581     T readBigInt(T)() if (is(Unqual!T == BigInt)) {
582         return readBigInt!T(_bytes.read());
583     }
584     T readDoubleWithoutTag(T)() if (isFloatingPoint!T) {
585         alias U = Unqual!T;
586         return cast(T)to!U(cast(string)_bytes.readUntil(TagSemicolon));
587     }
588     T readInfinityWithoutTag(T)() if (isFloatingPoint!T) {
589         alias U = Unqual!T;
590         return cast(T)((_bytes.read() == TagNeg) ? -U.infinity : U.infinity);
591     }
592     T readDouble(T)() if (isFloatingPoint!T) {
593         return readDouble!T(_bytes.read());
594     }
595     T readDateWithoutTag(T)() if (is(Unqual!T == Date) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) {
596         int year = to!int(cast(string)_bytes.read(4));
597         int month = to!int(cast(string)_bytes.read(2));
598         int day = to!int(cast(string)_bytes.read(2));
599         static if (is(Unqual!T == Date)) {
600             ubyte tag = _bytes.skipUntil(TagSemicolon, TagUTC);
601             Date result = Date(year, month, day);
602         }
603         else {
604             int hour = 0;
605             int minute = 0;
606             int second = 0;
607             int hnsecs = 0;
608             char tag = _bytes.read();
609             if (tag == TagTime) {
610                 hour = to!int(cast(string)_bytes.read(2));
611                 minute = to!int(cast(string)_bytes.read(2));
612                 second = to!int(cast(string)_bytes.read(2));
613                 tag = _bytes.read();
614                 if (tag == TagPoint) {
615                     hnsecs = to!int(cast(string)_bytes.read(3)) * 10000;
616                     tag = _bytes.read();
617                     if ((tag >= '0') && (tag <= '9')) {
618                         hnsecs += (tag - '0') * 1000 + to!int(cast(string)_bytes.read(2)) * 10;
619                         tag = _bytes.read();
620                         if ((tag >= '0') && (tag <= '9')) {
621                             hnsecs += (tag - '0');
622                             _bytes.skip(2);
623                             tag = _bytes.read();
624                         }
625                     }
626                 }
627             }
628             static if (is(Unqual!T == DateTime)) {
629                 DateTime result = DateTime(year, month, day, hour, minute, second);
630             }
631             else {
632                 SysTime result;
633                 if (tag == TagUTC) {
634                     result = SysTime(DateTime(year, month, day, hour, minute, second), std.datetime.hnsecs(hnsecs), UTC());
635                 }
636                 else {
637                     result = SysTime(DateTime(year, month, day, hour, minute, second), std.datetime.hnsecs(hnsecs), LocalTime());
638                 }
639             }
640         }
641         setRef(result);
642         return cast(T)result;
643     }
644     T readTimeWithoutTag(T)() if (is(Unqual!T == TimeOfDay) || is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) {
645         int year = 1970;
646         int month = 1;
647         int day = 1;
648         int hour = to!int(cast(string)_bytes.read(2));
649         int minute = to!int(cast(string)_bytes.read(2));
650         int second = to!int(cast(string)_bytes.read(2));
651         int hnsecs = 0;
652         char tag = _bytes.read();
653         if (tag == TagPoint) {
654             hnsecs = to!int(cast(string)_bytes.read(3)) * 10000;
655             tag = _bytes.read();
656             if ((tag >= '0') && (tag <= '9')) {
657                 hnsecs += (tag - '0') * 1000 + to!int(cast(string)_bytes.read(2)) * 10;
658                 tag = _bytes.read();
659                 if ((tag >= '0') && (tag <= '9')) {
660                     hnsecs += (tag - '0');
661                     _bytes.skip(2);
662                     tag = _bytes.read();
663                 }
664             }
665         }
666         static if (is(Unqual!T == TimeOfDay)) {
667             TimeOfDay result = TimeOfDay(hour, minute, second);
668         }
669         else static if (is(Unqual!T == DateTime)) {
670             DateTime result = DateTime(year, month, day, hour, minute, second);
671         }
672         else {
673             SysTime result;
674             if (tag == TagUTC) {
675                 result = SysTime(DateTime(year, month, day, hour, minute, second), std.datetime.hnsecs(hnsecs), UTC());
676             }
677             else {
678                 result = SysTime(DateTime(year, month, day, hour, minute, second), std.datetime.hnsecs(hnsecs), LocalTime());
679             }
680         }
681         setRef(result);
682         return cast(T)result;
683     }
684     T readDateTime(T)() if (is(Unqual!T == Date) || is(Unqual!T == TimeOfDay) ||
685         is(Unqual!T == DateTime) || is(Unqual!T == SysTime)) {
686         return readDateTime!T(_bytes.read());
687     }
688     T readUTF8CharWithoutTag(T)() if (isSomeChar!T || isSomeString!T) {
689         alias U = Unqual!T;
690         static if (is(U == string)) {
691             return _bytes.readUTF8Char();
692         }
693         else {
694             string s = _bytes.readUTF8Char();
695             static if (isSomeChar!T) {
696                 foreach(U c; s) return cast(T)c;
697             }
698             else {
699                 return cast(T)to!U(s);
700             }
701         }
702         assert(0);
703     }
704     T readUTF8Char(T)() if (isSomeChar!T) {
705         return readUTF8Char!T(_bytes.read());
706     }
707     T readStringWithoutTag(T)() if (isSomeString!T) {
708         alias U = Unqual!T;
709         static if (is(U == string)) {
710             string value = _readStringWithoutTag();
711         }
712         else {
713             U value = to!U(_readStringWithoutTag());
714         }
715         setRef(value);
716         return cast(T)value;
717     }
718     T readString(T)() if (isSomeString!T) {
719         return readString!T(_bytes.read());
720     }
721     T readUUIDWithoutTag(T)() if (is(Unqual!T == UUID)) {
722         _bytes.skip(1);
723         UUID uuid = UUID(_bytes.read(36));
724         _bytes.skip(1);
725         setRef(uuid);
726         return cast(T)uuid;
727     }
728     T readUUID(T)() if (is(Unqual!T == UUID)) {
729         return readUUID!T(_bytes.read());
730     }
731     T readBytesWithoutTag(T)() if (isArray!T &&
732         (is(Unqual!(ForeachType!T) == ubyte) ||
733             is(Unqual!(ForeachType!T) == byte))) {
734         alias ET = Unqual!(ForeachType!T);
735         int len = _bytes.readInt(TagQuote);
736         ET[] value = cast(ET[])(_bytes.read(len));
737         _bytes.skip(1);
738         setRef(value);
739         return cast(T)value;
740     }
741     T readBytes(T)() if (isArray!T &&
742         (is(Unqual!(ForeachType!T) == ubyte) ||
743             is(Unqual!(ForeachType!T) == byte))) {
744         return readBytes!T(_bytes.read());
745     }
746     T readArrayWithoutTag(T)() if (isArray!T) {
747         int len = _bytes.readInt(TagOpenbrace);
748         alias ET = Unqual!(ForeachType!T);
749         ET[] value = new ET[len];
750         setRef(value);
751         foreach (int i; 0..len) value[i] = unserialize!ET();
752         _bytes.skip(1);
753         return cast(T)value;
754     }
755     alias readArrayWithoutTag readListWithoutTag;
756     T readArray(T)() if (isArray!T) {
757         return readArray!T(_bytes.read());
758     }
759     alias readArray readList;
760     T readAssociativeArrayWithoutTag(T)() if (isAssociativeArray!T) {
761         alias KT = Unqual!(KeyType!T);
762         alias VT = Unqual!(ValueType!T);
763         VT[KT] value;
764         setRef(value);
765         foreach (int i; 0.._bytes.readInt(TagOpenbrace)) {
766             auto k = unserialize!KT();
767             auto v = unserialize!VT();
768             value[k] = v;
769         }
770         _bytes.skip(1);
771         return cast(T)value;
772     }
773     alias readAssociativeArrayWithoutTag readMapWithoutTag;
774     T readAssociativeArray(T)() if (isAssociativeArray!T) {
775         return readAssociativeArray!T(_bytes.read());
776     }
777     alias readAssociativeArray readMap;
778     T readJSONValue(T)() if (is(Unqual!T == JSONValue)) {
779         return readJSONValue!T(_bytes.read());
780     }
781     T readVariant(T)() if (is(Unqual!T == Variant)) {
782         return readVariant!T(_bytes.read());
783     }
784     T readObjectWithoutTag(T)() if (is(T == struct) || is(T == class)) {
785         alias U = Unqual!T;
786         auto index = _bytes.readInt!(int)(TagOpenbrace);
787         auto t = _classref[index][0];
788         auto fields = _classref[index][1];
789         if (t is null) {
790             static if (!is(U == Object)) {
791                 ClassManager.register!T(U.stringof);
792                 t = typeid(U);
793             }
794             else {
795                 throw new Exception("Type " ~ _classref[index][2] ~ " is not registered.");
796             }
797         }
798         auto unserializer = ClassManager.getUnserializer(t, this);
799         if (t != typeid(U)) {
800             throw new Exception("cannot convert type " ~ t.toString() ~ " to type " ~ fullyQualifiedName!T);
801         }
802         unserializer.setRef();
803         foreach(f; fields) unserializer.setField(f);
804         _bytes.skip(1);
805         return cast(T)(unserializer.get().get!U());
806     }
807     T readObject(T)() if (is(T == struct) || is(T == class)) {
808         return readObject!T(_bytes.read());
809     }
810     void readTupleWithoutTag(T...)(ref T args) {
811         int len = _bytes.readInt(TagOpenbrace);
812         int n = min(len, args.length);
813         setRef(null);
814         static if (args.length > 0) {
815             foreach (i, ref arg; args) {
816                 if (i < n) {
817                     arg = unserialize!(typeof(arg))();
818                 }
819             }
820         }
821         if (args.length < len) {
822             for (int i = args.length; i < len; ++i) {
823                 unserialize!Variant();
824             }
825         }
826         _bytes.skip(1);
827     }
828     void readTuple(T...)(ref T args) {
829         char tag = _bytes.read();
830         enforce(tag == TagList, unexpectedTag(tag));
831         readTupleWithoutTag(args);
832     }
833 }
834 
835 unittest {
836     BytesIO bytes = new BytesIO("nnnnnnnnnnnnnnnnnn");
837     Reader reader = new Reader(bytes);
838     assert(reader.unserialize!byte() == 0);
839     assert(reader.unserialize!short() == 0);
840     assert(reader.unserialize!int() == 0);
841     assert(reader.unserialize!long() == 0);
842     assert(reader.unserialize!(Nullable!char)().isNull());
843     assert(reader.unserialize!string() == null);
844     assert(reader.unserialize!bool() == false);
845     assert(reader.unserialize!float() == 0.0f);
846     assert(reader.unserialize!double() == 0.0);
847     assert(reader.unserialize!real() == 0.0);
848     assert(reader.unserialize!(Nullable!int)().isNull());
849     assert(reader.unserialize!(const byte)() == 0);
850     assert(reader.unserialize!(const Nullable!char)().isNull());
851     assert(reader.unserialize!(const BigInt)() == BigInt(0));
852     assert(reader.unserialize!(immutable real)() == 0);
853     assert(reader.unserialize!(immutable Nullable!int)().isNull());
854     assert(reader.unserialize!(int[])() == null);
855     assert(reader.unserialize!(int[string])() == null);
856 }
857 
858 unittest {
859     BytesIO bytes = new BytesIO("0000000000000000");
860     Reader reader = new Reader(bytes);
861     assert(reader.unserialize!byte() == 0);
862     assert(reader.unserialize!short() == 0);
863     assert(reader.unserialize!int() == 0);
864     assert(reader.unserialize!long() == 0);
865     assert(reader.unserialize!char() == 0);
866     assert(reader.unserialize!string() == "0");
867     assert(reader.unserialize!bool() == false);
868     assert(reader.unserialize!float() == 0.0f);
869     assert(reader.unserialize!double() == 0.0);
870     assert(reader.unserialize!real() == 0.0);
871     assert(reader.unserialize!(Nullable!int)() == 0);
872     assert(reader.unserialize!(const byte)() == 0);
873     assert(reader.unserialize!(const char)() == 0);
874     assert(reader.unserialize!(const BigInt)() == BigInt(0));
875     assert(reader.unserialize!(immutable real)() == 0);
876     assert(reader.unserialize!(immutable Nullable!int)() == 0);
877 }
878 
879 unittest {
880     BytesIO bytes = new BytesIO("1111111111111111");
881     Reader reader = new Reader(bytes);
882     assert(reader.unserialize!byte() == 1);
883     assert(reader.unserialize!short() == 1);
884     assert(reader.unserialize!int() == 1);
885     assert(reader.unserialize!long() == 1);
886     assert(reader.unserialize!char() == 1);
887     assert(reader.unserialize!string() == "1");
888     assert(reader.unserialize!bool() == true);
889     assert(reader.unserialize!float() == 1.0f);
890     assert(reader.unserialize!double() == 1.0);
891     assert(reader.unserialize!real() == 1.0);
892     assert(reader.unserialize!(Nullable!int)() == 1);
893     assert(reader.unserialize!(const byte)() == 1);
894     assert(reader.unserialize!(const char)() == 1);
895     assert(reader.unserialize!(const BigInt)() == BigInt(1));
896     assert(reader.unserialize!(immutable real)() == 1);
897     assert(reader.unserialize!(immutable Nullable!int)() == 1);
898 }
899 
900 unittest {
901     BytesIO bytes = new BytesIO("9999999999999999");
902     Reader reader = new Reader(bytes);
903     assert(reader.unserialize!byte() == 9);
904     assert(reader.unserialize!short() == 9);
905     assert(reader.unserialize!int() == 9);
906     assert(reader.unserialize!long() == 9);
907     assert(reader.unserialize!char() == 9);
908     assert(reader.unserialize!string() == "9");
909     assert(reader.unserialize!bool() == true);
910     assert(reader.unserialize!float() == 9.0f);
911     assert(reader.unserialize!double() == 9.0);
912     assert(reader.unserialize!real() == 9.0);
913     assert(reader.unserialize!(Nullable!int)() == 9);
914     assert(reader.unserialize!(const byte)() == 9);
915     assert(reader.unserialize!(const char)() == 9);
916     assert(reader.unserialize!(const BigInt)() == BigInt(9));
917     assert(reader.unserialize!(immutable real)() == 9);
918     assert(reader.unserialize!(immutable Nullable!int)() == 9);
919 }
920 
921 unittest {
922     import hprose.io.writer;
923     BytesIO bytes = new BytesIO();
924     Writer writer = new Writer(bytes);
925     for (int i = 0; i < 16; i++) writer.serialize(-1234567890);
926     Reader reader = new Reader(bytes);
927     assert(reader.unserialize!byte() == cast(byte)-1234567890);
928     assert(reader.unserialize!short() == cast(short)-1234567890);
929     assert(reader.unserialize!int() == -1234567890);
930     assert(reader.unserialize!long() == -1234567890);
931     assert(reader.unserialize!dchar() == -1234567890);
932     assert(reader.unserialize!string() == "-1234567890");
933     assert(reader.unserialize!bool() == true);
934     auto f = reader.unserialize!float();
935     assert(f == cast(float)-1234567890);
936     assert(reader.unserialize!double() == -1234567890);
937     assert(reader.unserialize!real() == -1234567890);
938     assert(reader.unserialize!(Nullable!int)() == -1234567890);
939     assert(reader.unserialize!(const uint)() == cast(uint)-1234567890);
940     assert(reader.unserialize!(const ulong)() == cast(ulong)-1234567890);
941     assert(reader.unserialize!(const BigInt)() == BigInt(-1234567890));
942     assert(reader.unserialize!(immutable real)() == -1234567890);
943     assert(reader.unserialize!(immutable Nullable!int)() == -1234567890);
944 }
945 
946 unittest {
947     import hprose.io.writer;
948     BytesIO bytes = new BytesIO();
949     Writer writer = new Writer(bytes);
950     for (int i = 0; i < 6; i++) writer.serialize(BigInt("1234567890987654321234567890987654321"));
951     Reader reader = new Reader(bytes);
952     assert(reader.unserialize!string() == "1234567890987654321234567890987654321");
953     assert(reader.unserialize!bool() == true);
954     auto f = reader.unserialize!float();
955     auto f2 = to!float("1234567890987654321234567890987654321");
956     assert(f == f2);
957     auto d = reader.unserialize!double();
958     auto d2 = to!double("1234567890987654321234567890987654321");
959     assert(d == d2);
960     auto r = reader.unserialize!real();
961     auto r2 = to!real("1234567890987654321234567890987654321");
962     assert(r == r2);
963     assert(reader.unserialize!(const BigInt)() == BigInt("1234567890987654321234567890987654321"));
964 }
965 
966 unittest {
967     import hprose.io.writer;
968     BytesIO bytes = new BytesIO();
969     Writer writer = new Writer(bytes);
970     for (int i = 0; i < 6; i++) writer.serialize(-3.1415926);
971     Reader reader = new Reader(bytes);
972     assert(reader.unserialize!byte() == -3);
973     assert(reader.unserialize!int() == -3);
974     assert(reader.unserialize!bool() == true);
975     auto f = reader.unserialize!float();
976     auto f2 = to!float("-3.1415926");
977     assert(f == f2);
978     auto d = reader.unserialize!double();
979     auto d2 = to!double("-3.1415926");
980     assert(d == d2);
981     auto r = reader.unserialize!real();
982     auto r2 = to!real("-3.1415926");
983     assert(r == r2);
984 }
985 
986 unittest {
987     import hprose.io.writer;
988     BytesIO bytes = new BytesIO();
989     Writer writer = new Writer(bytes);
990     for (int i = 0; i < 6; i++) writer.serialize("123");
991     Reader reader = new Reader(bytes);
992     assert(reader.unserialize!byte() == cast(byte)123);
993     assert(reader.unserialize!int() == 123);
994     assert(reader.unserialize!bool() == true);
995     auto f = reader.unserialize!float();
996     auto f2 = to!float("123");
997     assert(f == f2);
998     auto d = reader.unserialize!double();
999     auto d2 = to!double("123");
1000     assert(d == d2);
1001     auto r = reader.unserialize!real();
1002     auto r2 = to!real("123");
1003     assert(r == r2);
1004 }
1005 
1006 unittest {
1007     import hprose.io.writer;
1008     BytesIO bytes = new BytesIO();
1009     Writer writer = new Writer(bytes);
1010     for (int i = 0; i < 6; i++) writer.serialize([1,2,3,4,5,6,7]);
1011     Reader reader = new Reader(bytes);
1012     assert(reader.unserialize!(byte[])() == cast(byte[])[1,2,3,4,5,6,7]);
1013     assert(reader.unserialize!(ubyte[])() == cast(ubyte[])[1,2,3,4,5,6,7]);
1014     assert(reader.unserialize!(int[])() == [1,2,3,4,5,6,7]);
1015     assert(reader.unserialize!(uint[])() == [1,2,3,4,5,6,7]);
1016     assert(reader.unserialize!(const ulong[])() == cast(const ulong[])[1,2,3,4,5,6,7]);
1017     assert(reader.unserialize!(immutable(double)[])() == cast(immutable(double)[])[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
1018 }
1019 
1020 private {
1021     class MyClass {
1022         int a;
1023         static const byte b;
1024         private int c = 3;
1025         @property {
1026             int x() const { return c; }
1027             int x(int value) { return c = value; }
1028         }
1029         this() { this.a = 1; }
1030         this(int a) { this.a = a; }
1031         void hello() {}
1032     };
1033 
1034     struct MyStruct {
1035         int a;
1036         static const byte b;
1037         int c = 3;
1038         this(int a) { this.a = a; }
1039         void hello() {}
1040     }
1041 }
1042 
1043 unittest {
1044     BytesIO bytes = new BytesIO("D19801201T174854.802400;");
1045     RawReader reader = new RawReader(bytes);
1046     assert(reader.readRaw().toString() == "D19801201T174854.802400;");
1047 }
1048 
1049 unittest {
1050     import hprose.io.writer;
1051     BytesIO bytes = new BytesIO();
1052     Writer writer = new Writer(bytes);
1053     for (int i = 0; i < 6; i++) writer.serialize(["Jane": 10.0, "Jack":20, "Bob":15]);
1054     writer.serialize(hprose.io.reader.MyStruct(13));
1055     Reader reader = new Reader(bytes);
1056     assert(reader.unserialize!(int[string])() == ["Jane": 10, "Jack":20, "Bob":15]);
1057     assert(reader.unserialize!(double[string])() == cast(double[string])["Jane": 10, "Jack":20, "Bob":15]);
1058     assert(reader.unserialize!(byte[string])() == cast(byte[string])["Jane": 10, "Jack":20, "Bob":15]);
1059     assert(reader.unserialize!(ubyte[string])() == cast(ubyte[string])["Jane": 10, "Jack":20, "Bob":15]);
1060     assert(reader.unserialize!(const long[string])() == cast(const long[string])["Jane": 10, "Jack":20, "Bob":15]);
1061     assert(reader.unserialize!(immutable ulong[string])() == cast(immutable ulong[string])["Jane": 10, "Jack":20, "Bob":15]);
1062     assert(reader.unserialize!(int[string])() == ["a": 13, "c": 3]);
1063 }
1064 
1065 unittest {
1066     int i = 1234567890;
1067     float f = cast(float)i;
1068     assert(f == 1234567890);
1069     BytesIO bytes = new BytesIO("D20141221T120808.342123432;r0;");
1070     Reader reader = new Reader(bytes);
1071     auto st = reader.unserialize!(SysTime)();
1072     auto st2 = reader.unserialize!(shared SysTime)();
1073     assert(st == st2);
1074     bytes.init("i123456789;u111");
1075     reader.reset();
1076     assert(reader.unserialize!(const int)() == 123456789);
1077     assert(reader.unserialize!(const double) == 1.0);
1078     assert(reader.unserialize!(int)() == 1);
1079     assert(reader.unserialize!(Nullable!int)() == 1);
1080 }
1081 
1082 unittest {
1083     import hprose.io.writer;
1084     import std.math;
1085     BytesIO bytes = new BytesIO();
1086     Writer writer = new Writer(bytes);
1087     writer.serialize(1);
1088     writer.serialize(long.max);
1089     writer.serialize(ulong.max);
1090     writer.serialize(BigInt("1234567890987654321234567890"));
1091     writer.serialize(PI);
1092     writer.serialize("你");
1093     writer.serialize("一闪一闪亮晶晶,烧饼油条卷大葱");
1094     writer.serialize(UUID("21f7f8de-8051-5b89-8680-0195ef798b6a"));
1095     writer.serialize(SysTime(DateTime(2015, 2, 8, 23, 05, 31)));
1096     writer.serialize(TimeOfDay(12, 12, 21));
1097     writer.serialize(Date(2015, 2, 8));
1098     writer.serialize(variantArray(1, "Hello", 3.14, Date(2015, 2, 8)));
1099     writer.serialize(["name": JSONValue("张三"), "age": JSONValue(18)]);
1100 //    writer.serialize(hprose.io.reader.MyStruct(13));
1101     Reader reader = new Reader(bytes);
1102     JSONValue jv = reader.unserialize!(JSONValue)();
1103     assert(jv == JSONValue(1));
1104     jv = reader.unserialize!(JSONValue)();
1105     assert(jv == JSONValue(long.max));
1106     jv = reader.unserialize!(JSONValue)();
1107     assert(jv == JSONValue(ulong.max));
1108     jv = reader.unserialize!(JSONValue)();
1109     assert(jv.str == "1234567890987654321234567890");
1110     jv = reader.unserialize!(JSONValue)();
1111     assert(jv == JSONValue(PI));
1112     jv = reader.unserialize!(JSONValue)();
1113     assert(jv.str == "你");
1114     jv = reader.unserialize!(JSONValue)();
1115     assert(jv.str == "一闪一闪亮晶晶,烧饼油条卷大葱");
1116     jv = reader.unserialize!(JSONValue)();
1117     assert(jv.str == "21f7f8de-8051-5b89-8680-0195ef798b6a");
1118     jv = reader.unserialize!(JSONValue)();
1119     assert(jv.str == "2015-Feb-08 23:05:31");
1120     jv = reader.unserialize!(JSONValue)();
1121     assert(jv.str == "12:12:21");
1122     jv = reader.readJSONValue!(JSONValue)();
1123     assert(jv.str == "2015-Feb-08 00:00:00");
1124     jv = reader.readJSONValue!(JSONValue)();
1125     assert(jv[0] == JSONValue(1));
1126     assert(jv[1].str == "Hello");
1127     assert(jv[2] == JSONValue(3.14));
1128     assert(jv[3].str == "2015-Feb-08 00:00:00");
1129     jv = reader.readJSONValue!(JSONValue)();
1130     assert(jv["name"].str == "张三");
1131     assert(jv["age"] == JSONValue(18));
1132 //    jv = reader.readJSONValue!(JSONValue)();
1133 //    assert(jv["a"] == JSONValue(13));
1134 //    assert(jv["c"] == JSONValue(3));
1135 }
1136 
1137 unittest {
1138     import hprose.io.writer;
1139     import std.math;
1140     BytesIO bytes = new BytesIO();
1141     Writer writer = new Writer(bytes);
1142     writer.serialize(1);
1143     writer.serialize(long.max);
1144     writer.serialize(ulong.max);
1145     writer.serialize(BigInt("1234567890987654321234567890"));
1146     writer.serialize(PI);
1147     writer.serialize("你");
1148     writer.serialize("一闪一闪亮晶晶,烧饼油条卷大葱");
1149     writer.serialize(UUID("21f7f8de-8051-5b89-8680-0195ef798b6a"));
1150     writer.serialize(SysTime(DateTime(2015, 2, 8, 23, 05, 31)));
1151     writer.serialize(TimeOfDay(12, 12, 21));
1152     writer.serialize(Date(2015, 2, 8));
1153     writer.serialize(variantArray(1, "Hello", 3.14, Date(2015, 2, 8)));
1154 //    writer.serialize([JSONValue("张三"): "name", JSONValue(18):"age"]);
1155 //    writer.serialize(hprose.io.reader.MyStruct(12));
1156     Reader reader = new Reader(bytes);
1157     Variant v = reader.unserialize!(Variant)();
1158     assert(v == Variant(1));
1159     v = reader.unserialize!(Variant)();
1160     assert(v == Variant(BigInt(long.max)));
1161     v = reader.unserialize!(Variant)();
1162     assert(v == Variant(BigInt(ulong.max)));
1163     v = reader.unserialize!(Variant)();
1164     assert(v == Variant(BigInt("1234567890987654321234567890")));
1165     v = reader.unserialize!(Variant)();
1166     assert(v == Variant(double(PI)));
1167     v = reader.unserialize!(Variant)();
1168     assert(v == "你");
1169     v = reader.unserialize!(Variant)();
1170     assert(v == "一闪一闪亮晶晶,烧饼油条卷大葱");
1171     v = reader.unserialize!(Variant)();
1172     assert(v == UUID("21f7f8de-8051-5b89-8680-0195ef798b6a"));
1173     v = reader.unserialize!(Variant)();
1174     assert(v == SysTime(DateTime(2015, 2, 8, 23, 05, 31)));
1175     v = reader.unserialize!(Variant)();
1176     assert(v == TimeOfDay(12, 12, 21));
1177     v = reader.readVariant!(Variant)();
1178     assert(v == SysTime(Date(2015, 2, 8)));
1179     v = reader.readVariant!(Variant)();
1180     assert(v[0] == Variant(1));
1181     assert(v[1] == "Hello");
1182     assert(v[2] == Variant(3.14));
1183     assert(v[3] == SysTime(Date(2015, 2, 8)));
1184 //    v = reader.readVariant!(Variant)();
1185 //    assert(v.get!(Variant[Variant])[Variant("张三")] == "name");
1186 //    assert(v.get!(Variant[Variant])[Variant(18)] == "age");
1187 //    v = reader.readVariant!(Variant)();
1188 //    assert(v.get!(hprose.io.reader.MyStruct)() == hprose.io.reader.MyStruct(12));
1189 }
1190 
1191 unittest {
1192 //    BytesIO bytes = new BytesIO("c8\"MyStruct\"1{s1\"c\"}o0{5}c7\"MyClass\"1{s1\"x\"}o1{5}m2{s1\"a\"5s1\"x\"2}");
1193 //    Reader reader = new Reader(bytes);
1194 //    hprose.io.reader.MyStruct mystruct = reader.unserialize!(hprose.io.reader.MyStruct)();
1195 //    hprose.io.reader.MyClass myclass = reader.unserialize!(hprose.io.reader.MyClass)();
1196 //    assert(mystruct.a == 0);
1197 //    assert(mystruct.c == 5);
1198 //    assert(myclass.a == 1);
1199 //    assert(myclass.x == 5);
1200 //    hprose.io.reader.MyClass myclass2 = reader.unserialize!(hprose.io.reader.MyClass)();
1201 //    assert(myclass2.a == 5);
1202 //    assert(myclass2.x == 2);
1203 }
1204 
1205 unittest {
1206     BytesIO bytes = new BytesIO("a3{1s5\"Hello\"r1;}");
1207     Reader reader = new Reader(bytes);
1208     Tuple!(int, string, string, int) value;
1209     reader.readTuple(value.expand);
1210     assert(value == tuple(1, "Hello", "Hello", 0));
1211 }
1212 
1213 unittest {
1214     enum Color {
1215         Red, Blue, Green
1216     }
1217     BytesIO bytes = new BytesIO("a3{012}");
1218     Reader reader = new Reader(bytes);
1219     Tuple!(Color, Color, Color) value;
1220     reader.readTuple(value.expand);
1221     assert(value == tuple(Color.Red, Color.Blue, Color.Green));
1222 }