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