1 /++
2 ASDF Representation
3 
4 Copyright: Tamedia Digital, 2016
5 
6 Authors: Ilia Ki
7 
8 License: MIT
9 
10 Macros:
11 SUBMODULE = $(LINK2 asdf_$1.html, asdf.$1)
12 SUBREF = $(LINK2 asdf_$1.html#.$2, $(TT $2))$(NBSP)
13 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
14 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
15 +/
16 module asdf.asdf;
17 
18 import std.exception;
19 import std.range.primitives;
20 import std.typecons;
21 import std.traits;
22 
23 import asdf.jsonbuffer;
24 import asdf.jsonparser: assumePure;
25 
26 version(X86_64)
27     version = X86_Any;
28 else
29 version(X86)
30     version = X86_Any;
31 
32 version (D_Exceptions)
33 {
34     import mir.serde: SerdeException;
35     /++
36     Serde Exception
37     +/
38     class AsdfSerdeException : SerdeException
39     {
40         /// zero based faulty location
41         size_t location;
42 
43         ///
44         this(
45             string msg,
46             size_t location,
47             string file = __FILE__,
48             size_t line = __LINE__,
49             ) pure nothrow @nogc @safe 
50         {
51             this.location = location;
52             super(msg, file, line);
53         }
54 
55         ///
56         this(
57             string msg,
58             string file = __FILE__,
59             size_t line = __LINE__,
60             Throwable next = null) pure nothrow @nogc @safe 
61         {
62             super(msg, file, line, next);
63         }
64 
65         ///
66         this(
67             string msg,
68             Throwable next,
69             string file = __FILE__,
70             size_t line = __LINE__,
71             ) pure nothrow @nogc @safe 
72         {
73             this(msg, file, line, next);
74         }
75 
76         override AsdfSerdeException toMutable() @trusted pure nothrow @nogc const
77         {
78             return cast() this;
79         }
80 
81         alias toMutable this;
82     }
83 }
84 
85 deprecated("use mir.serde: SerdeException instead")
86 alias AsdfException = SerdeException;
87 
88 ///
89 class InvalidAsdfException: SerdeException
90 {
91     ///
92     this(
93         uint kind,
94         string file = __FILE__,
95         size_t line = __LINE__,
96         Throwable next = null) pure nothrow @safe 
97     {
98         import mir.format: text;
99         super(text("ASDF values is invalid for kind = ", kind), file, line, next);
100     }
101 
102     ///
103     this(
104         uint kind,
105         Throwable next,
106         string file = __FILE__,
107         size_t line = __LINE__,
108         ) pure nothrow @safe 
109     {
110         this(kind, file, line, next);
111     }
112 }
113 
114 private void enforceValidAsdf(
115         bool condition,
116         uint kind,
117         string file = __FILE__,
118         size_t line = __LINE__) @safe pure
119 {
120     if(!condition)
121         throw new InvalidAsdfException(kind, file, line);
122 }
123 
124 ///
125 class EmptyAsdfException: SerdeException
126 {
127     ///
128     this(
129         string msg = "ASDF value is empty",
130         string file = __FILE__,
131         size_t line = __LINE__,
132         Throwable next = null) pure nothrow @nogc @safe 
133     {
134         super(msg, file, line, next);
135     }
136 }
137 
138 /++
139 The structure for ASDF manipulation.
140 +/
141 struct Asdf
142 {
143     ///
144     enum Kind : ubyte
145     {
146         ///
147         null_  = 0x00,
148         ///
149         true_  = 0x01,
150         ///
151         false_ = 0x02,
152         ///
153         number = 0x03,
154         ///
155         string = 0x05,
156         ///
157         array  = 0x09,
158         ///
159         object = 0x0A,
160     }
161 
162     /// Returns ASDF Kind
163     ubyte kind() const pure @safe @nogc
164     {
165         if (!data.length)
166         {
167             static immutable exc = new EmptyAsdfException;
168             throw exc;
169         }
170         return data[0];
171     }
172 
173     /++
174     Plain ASDF data.
175     +/
176     ubyte[] data;
177 
178     /// Creates ASDF using already allocated data
179     this(ubyte[] data) pure @safe nothrow @nogc
180     {
181         this.data = data;
182     }
183 
184     /// Creates ASDF from a string
185     this(in char[] str) pure @safe
186     {
187         data = new ubyte[str.length + 5];
188         data[0] = Kind..string;
189         length4 = str.length;
190         data[5 .. $] = cast(const(ubyte)[])str;
191     }
192 
193     ///
194     unittest
195     {
196         assert(Asdf("string") == "string");
197         assert(Asdf("string") != "String");
198     }
199 
200     // \uXXXX character support
201     unittest
202     {
203         import mir.conv: to;
204         import asdf.jsonparser;
205         assert(Asdf("begin\u000bend").to!string == `"begin\u000Bend"`);
206         assert("begin\u000bend" == cast(string) `"begin\u000Bend"`.parseJson, to!string(cast(ubyte[]) cast(string)( `"begin\u000Bend"`.parseJson)));
207     }
208 
209     /// Sets deleted bit on
210     void remove() pure @safe nothrow @nogc
211     {
212         if(data.length)
213             data[0] |= 0x80;
214     }
215 
216     ///
217     unittest
218     {
219         import mir.conv: to;
220         import asdf.jsonparser;
221         auto asdfData = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`.parseJson;
222         asdfData["inner", "d"].remove;
223         assert(asdfData.to!string == `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","e":{}}}`);
224     }
225 
226     ///
227     void toString(Dg)(scope Dg sink) const
228     {
229         scope buffer = JsonBuffer!Dg(sink);
230         toStringImpl(buffer);
231         buffer.flush;
232     }
233 
234     /+
235     Internal recursive toString implementation.
236     Params:
237         sink = output range that accepts `char`, `in char[]` and compile time string `(string str)()`
238     +/
239     private void toStringImpl(Dg)(ref JsonBuffer!Dg sink) const
240     {
241         if (!data.length)
242         {
243             static immutable exc = new EmptyAsdfException("Data buffer is empty");
244             throw exc;
245         }
246         auto t = data[0];
247         switch(t)
248         {
249             case Kind.null_:
250                 enforceValidAsdf(data.length == 1, t);
251                 sink.put!"null";
252                 break;
253             case Kind.true_:
254                 enforceValidAsdf(data.length == 1, t);
255                 sink.put!"true";
256                 break;
257             case Kind.false_:
258                 enforceValidAsdf(data.length == 1, t);
259                 sink.put!"false";
260                 break;
261             case Kind.number:
262                 enforceValidAsdf(data.length > 1, t);
263                 size_t length = data[1];
264                 enforceValidAsdf(data.length == length + 2, t);
265                 sink.putSmallEscaped(cast(const(char)[]) data[2 .. $]);
266                 break;
267             case Kind..string:
268                 enforceValidAsdf(data.length >= 5, Kind.object);
269                 enforceValidAsdf(data.length == length4 + 5, t);
270                 sink.put('"');
271                 sink.put(cast(const(char)[]) data[5 .. $]);
272                 sink.put('"');
273                 break;
274             case Kind.array:
275                 auto elems = Asdf(cast(ubyte[])data).byElement;
276                 if(elems.empty)
277                 {
278                     sink.put!"[]";
279                     break;
280                 }
281                 sink.put('[');
282                 elems.front.toStringImpl(sink);
283                 elems.popFront;
284                 foreach(e; elems)
285                 {
286                     sink.put(',');
287                     e.toStringImpl(sink);
288                 }
289                 sink.put(']');
290                 break;
291             case Kind.object:
292                 auto pairs = Asdf(cast(ubyte[])data).byKeyValue;
293                 if(pairs.empty)
294                 {
295                     sink.put!"{}";
296                     break;
297                 }
298                 sink.put!"{\"";
299                 sink.put(pairs.front.key);
300                 sink.put!"\":";
301                 pairs.front.value.toStringImpl(sink);
302                 pairs.popFront;
303                 foreach(e; pairs)
304                 {
305                     sink.put!",\"";
306                     sink.put(e.key);
307                     sink.put!"\":";
308                     e.value.toStringImpl(sink);
309                 }
310                 sink.put('}');
311                 break;
312             default:
313                 enforceValidAsdf(0, t);
314         }
315     }
316 
317     ///
318     unittest
319     {
320         import mir.conv: to;
321         import asdf.jsonparser;
322         auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`;
323         const asdfData = text.parseJson;
324         assert(asdfData.to!string == text);
325     }
326 
327     /++
328     `==` operator overloads for `null`
329     +/
330     bool opEquals(in Asdf rhs) const @safe pure nothrow @nogc
331     {
332         return data == rhs.data;
333     }
334 
335     ///
336     unittest
337     {
338         import asdf.jsonparser;
339         auto asdfData = `null`.parseJson;
340         assert(asdfData == asdfData);
341     }
342 
343     /++
344     `==` operator overloads for `null`
345     +/
346     bool opEquals(typeof(null)) const pure @safe nothrow
347     {
348         return data.length == 1 && data[0] == 0;
349     }
350 
351     ///
352     unittest
353     {
354         import asdf.jsonparser;
355         auto asdfData = `null`.parseJson;
356         assert(asdfData == null);
357     }
358 
359     /++
360     `==` operator overloads for `bool`
361     +/
362     bool opEquals(bool boolean) const pure @safe nothrow
363     {
364         return data.length == 1 && (data[0] == Kind.true_ && boolean || data[0] == Kind.false_ && !boolean);
365     }
366 
367     ///
368     unittest
369     {
370         import asdf.jsonparser;
371         auto asdfData = `true`.parseJson;
372         assert(asdfData == true);
373         assert(asdfData != false);
374     }
375 
376     /++
377     `==` operator overloads for `string`
378     +/
379     bool opEquals(in char[] str) const pure @trusted nothrow
380     {
381         return data.length >= 5 && data[0] == Kind..string && data[5 .. 5 + length4] == cast(const(ubyte)[]) str;
382     }
383 
384     ///
385     unittest
386     {
387         import asdf.jsonparser;
388         auto asdfData = `"str"`.parseJson;
389         assert(asdfData == "str");
390         assert(asdfData != "stR");
391     }
392 
393     /++
394     Returns:
395         input range composed of elements of an array.
396     +/
397     auto byElement() pure
398     {
399         static struct Range
400         {
401             private ubyte[] _data;
402             private Asdf _front;
403 
404             auto save()() pure @property
405             {
406                 return this;
407             }
408 
409             void popFront() pure
410             {
411                 while(!_data.empty)
412                 {
413                     uint t = cast(ubyte) _data.front;
414                     switch(t)
415                     {
416                         case Kind.null_:
417                         case Kind.true_:
418                         case Kind.false_:
419                             _front = Asdf(_data[0 .. 1]);
420                             _data.popFront;
421                             return;
422                         case Kind.number:
423                             enforceValidAsdf(_data.length >= 2, t);
424                             size_t len = _data[1] + 2;
425                             enforceValidAsdf(_data.length >= len, t);
426                             _front = Asdf(_data[0 .. len]);
427                             _data = _data[len .. $];
428                             return;
429                         case Kind..string:
430                         case Kind.array:
431                         case Kind.object:
432                             enforceValidAsdf(_data.length >= 5, t);
433                             size_t len = Asdf(_data).length4 + 5;
434                             enforceValidAsdf(_data.length >= len, t);
435                             _front = Asdf(_data[0 .. len]);
436                             _data = _data[len .. $];
437                             return;
438                         case 0x80 | Kind.null_:
439                         case 0x80 | Kind.true_:
440                         case 0x80 | Kind.false_:
441                             _data.popFront;
442                             continue;
443                         case 0x80 | Kind.number:
444                             enforceValidAsdf(_data.length >= 2, t);
445                             _data.popFrontExactly(_data[1] + 2);
446                             continue;
447                         case 0x80 | Kind..string:
448                         case 0x80 | Kind.array:
449                         case 0x80 | Kind.object:
450                             enforceValidAsdf(_data.length >= 5, t);
451                             size_t len = Asdf(_data).length4 + 5;
452                             _data.popFrontExactly(len);
453                             continue;
454                         default:
455                             enforceValidAsdf(0, t);
456                     }
457                 }
458                 _front = Asdf.init;
459             }
460 
461             auto front() pure @property
462             {
463                 assert(!empty);
464                 return _front;
465             }
466 
467             bool empty() pure @property
468             {
469                 return _front.data.length == 0;
470             }
471         }
472         if(data.empty || data[0] != Kind.array)
473             return Range.init;
474         enforceValidAsdf(data.length >= 5, Kind.array);
475         enforceValidAsdf(length4 == data.length - 5, Kind.array);
476         auto ret = Range(data[5 .. $]);
477         if(ret._data.length)
478             ret.popFront;
479         return ret;
480     }
481 
482     /++
483     Returns:
484         Input range composed of key-value pairs of an object.
485         Elements are type of `Tuple!(const(char)[], "key", Asdf, "value")`.
486     +/
487     auto byKeyValue() pure
488     {
489         static struct Range
490         {
491             private ubyte[] _data;
492             private Tuple!(const(char)[], "key", Asdf, "value") _front;
493 
494             auto save() pure @property
495             {
496                 return this;
497             }
498 
499             void popFront() pure
500             {
501                 while(!_data.empty)
502                 {
503                     enforceValidAsdf(_data.length > 1, Kind.object);
504                     size_t l = cast(ubyte) _data[0];
505                     _data.popFront;
506                     enforceValidAsdf(_data.length >= l, Kind.object);
507                     _front.key = cast(const(char)[])_data[0 .. l];
508                     _data.popFrontExactly(l);
509                     uint t = cast(ubyte) _data.front;
510                     switch(t)
511                     {
512                         case Kind.null_:
513                         case Kind.true_:
514                         case Kind.false_:
515                             _front.value = Asdf(_data[0 .. 1]);
516                             _data.popFront;
517                             return;
518                         case Kind.number:
519                             enforceValidAsdf(_data.length >= 2, t);
520                             size_t len = _data[1] + 2;
521                             enforceValidAsdf(_data.length >= len, t);
522                             _front.value = Asdf(_data[0 .. len]);
523                             _data = _data[len .. $];
524                             return;
525                         case Kind..string:
526                         case Kind.array:
527                         case Kind.object:
528                             enforceValidAsdf(_data.length >= 5, t);
529                             size_t len = Asdf(_data).length4 + 5;
530                             enforceValidAsdf(_data.length >= len, t);
531                             _front.value = Asdf(_data[0 .. len]);
532                             _data = _data[len .. $];
533                             return;
534                         case 0x80 | Kind.null_:
535                         case 0x80 | Kind.true_:
536                         case 0x80 | Kind.false_:
537                             _data.popFront;
538                             continue;
539                         case 0x80 | Kind.number:
540                             enforceValidAsdf(_data.length >= 2, t);
541                             _data.popFrontExactly(_data[1] + 2);
542                             continue;
543                         case 0x80 | Kind..string:
544                         case 0x80 | Kind.array:
545                         case 0x80 | Kind.object:
546                             enforceValidAsdf(_data.length >= 5, t);
547                             size_t len = Asdf(_data).length4 + 5;
548                             _data.popFrontExactly(len);
549                             continue;
550                         default:
551                             enforceValidAsdf(0, t);
552                     }
553                 }
554                 _front = _front.init;
555             }
556 
557             auto front() pure @property
558             {
559                 assert(!empty);
560                 return _front;
561             }
562 
563             bool empty() pure @property
564             {
565                 return _front.value.data.length == 0;
566             }
567         }
568         if(data.empty || data[0] != Kind.object)
569             return Range.init;
570         enforceValidAsdf(data.length >= 5, Kind.object);
571         enforceValidAsdf(length4 == data.length - 5, Kind.object);
572         auto ret = Range(data[5 .. $]);
573         if(ret._data.length)
574             ret.popFront;
575         return ret;
576     }
577 
578     /// returns 4-byte length
579     private size_t length4() const @property pure nothrow @nogc @trusted
580     {
581         assert(data.length >= 5);
582         version(X86_Any)
583         {
584             return (cast(uint*)(data.ptr + 1))[0];
585         }
586         else
587         {
588             align(4) auto ret = *cast(ubyte[4]*)(data.ptr + 1);
589             return (cast(uint[1])ret)[0];
590         }
591     }
592 
593     /// ditto
594     private void length4(size_t len) const @property pure nothrow @nogc @trusted
595     {
596         assert(data.length >= 5);
597         assert(len <= uint.max);
598         version(X86_Any)
599         {
600             *(cast(uint*)(data.ptr + 1)) = cast(uint) len;
601         }
602         else
603         {
604             *(cast(ubyte[4]*)(data.ptr + 1)) = cast(ubyte[4]) cast(uint[1]) [cast(uint) len];
605         }
606     }
607 
608     /++
609     Searches for a value recursively in an ASDF object.
610 
611     Params:
612         keys = list of keys keys
613     Returns
614         ASDF value if it was found (first win) or ASDF with empty plain data.
615     +/
616     Asdf opIndex(in char[][] keys...) pure
617     {
618         auto asdf = this;
619         if(asdf.data.empty)
620             return Asdf.init;
621         L: foreach(key; keys)
622         {
623             if(asdf.data[0] != Asdf.Kind.object)
624                 return Asdf.init;
625             foreach(e; asdf.byKeyValue)
626             {
627                 if(e.key == key)
628                 {
629                     asdf = e.value;
630                     continue L;
631                 }
632             }
633             return Asdf.init;
634         }
635         return asdf;
636     }
637 
638     ///
639     unittest
640     {
641         import asdf.jsonparser;
642         auto asdfData = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`.parseJson;
643         assert(asdfData["inner", "a"] == true);
644         assert(asdfData["inner", "b"] == false);
645         assert(asdfData["inner", "c"] == "32323");
646         assert(asdfData["inner", "d"] == null);
647         assert(asdfData["no", "such", "keys"] == Asdf.init);
648     }
649 
650     /++
651     Params:
652         def = default value. It is used when ASDF value equals `Asdf.init`.
653     Returns:
654         `cast(T) this` if `this != Asdf.init` and `def` otherwise.
655     +/
656     T get(T)(T def)
657     {
658         if(data.length)
659         {
660             return cast(T) this;
661         }
662         return def;
663     }
664 
665     ///
666     unittest
667     {
668         import asdf.jsonparser;
669         auto asdfData = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`.parseJson;
670         assert(asdfData["inner", "a"].get(false) == true);
671         assert(asdfData["inner", "b"].get(true) == false);
672         assert(asdfData["inner", "c"].get(100) == 32323);
673         assert(asdfData["no", "such", "keys"].get(100) == 100);
674     }
675 
676     /++
677     `cast` operator overloading.
678     +/
679     T opCast(T)()
680     {
681         import std.datetime: SysTime, DateTime, usecs, UTC;
682         import std.traits: isNumeric;
683         import mir.conv: to;
684         import std.conv: ConvException;
685         import std.format: format;
686         import std.math: trunc;
687         import asdf.serialization;
688         auto k = kind;
689         with(Kind) switch(kind)
690         {
691             case null_ :
692                 static if (isNumeric!T
693                         || is(T == interface)
694                         || is(T == class)
695                         || is(T == E[], E)
696                         || is(T == E[K], E, K)
697                         || is(T == bool))
698                     return T.init;
699                 else goto default;
700             case true_ :
701                 static if(__traits(compiles, true.to!T))
702                     return true.to!T;
703                 else goto default;
704             case false_:
705                 static if(__traits(compiles, false.to!T))
706                     return false.to!T;
707                 else goto default;
708             case number:
709             {
710                 auto str = cast(const(char)[]) data[2 .. $];
711                 static if(is(T == bool))
712                     return assumePure(() => str.to!double)() != 0;
713                 else
714                 static if(is(T == SysTime) || is(T == DateTime))
715                 {
716                     auto unixTime = assumePure(() => str.to!real)();
717                     auto secsR = assumePure(() => unixTime.trunc)();
718                     auto rem = unixTime - secsR;
719                     auto st = SysTime.fromUnixTime(cast(long)(secsR), UTC());
720                     assumePure((ref SysTime st) => st.fracSecs = usecs(cast(long)(rem * 1_000_000)))(st);
721                     return assumePure(() => st.to!T)();
722                 }
723                 else
724                 static if(__traits(compiles, assumePure(() => str.to!T)()))
725                 {
726                     static if (isFloatingPoint!T)
727                     {
728                         import mir.bignum.internal.dec2float: decimalToFloatImpl;
729                         import mir.bignum.internal.parse: parseJsonNumberImpl;
730                         auto result = str.parseJsonNumberImpl;
731                         if (!result.success)
732                             throw new Exception("Failed to deserialize number");
733 
734                         auto fp = decimalToFloatImpl!(Unqual!T)(result.coefficient, result.exponent);
735                         if (result.sign)
736                             fp = -fp;
737                         return fp;
738                     }
739                     else
740                     {
741                         return assumePure(() => str.to!T)();
742                     }
743                 }
744                 else goto default;
745             }
746             case string:
747             {
748                 auto str = cast(const(char)[]) data[5 .. $];
749                 static if(is(T == bool))
750                     return str != "0" && str != "false" && str != "";
751                 else
752                 static if(__traits(compiles, str.to!T))
753                     return str.to!T;
754                 else goto default;
755             }
756             static if (isAggregateType!T || isArray!T)
757             {
758             case array :
759             case object:
760                 static if(__traits(compiles, {T t = deserialize!T(this);}))
761                     return deserialize!T(this);
762                 else goto default;
763             }
764             default:
765                 throw new ConvException(format("Cannot convert kind %s(\\x%02X) to %s", cast(Kind) k, k, T.stringof));
766         }
767     }
768 
769     /// null
770     unittest
771     {
772         import std.math;
773         import asdf.serialization;
774         auto null_ = serializeToAsdf(null);
775         interface I {}
776         class C {}
777         assert(cast(uint[]) null_ is null);
778         assert(cast(uint[uint]) null_ is null);
779         assert(cast(I) null_ is null);
780         assert(cast(C) null_ is null);
781         assert(isNaN(cast(double) null_));
782         assert(! cast(bool) null_);
783     }
784 
785     /// boolean
786     unittest
787     {
788         import std.math;
789         import asdf.serialization;
790         auto true_ = serializeToAsdf(true);
791         auto false_ = serializeToAsdf(false);
792         static struct C {
793             this(bool){}
794         }
795         auto a = cast(C) true_;
796         auto b = cast(C) false_;
797         assert(cast(bool) true_ == true);
798         assert(cast(bool) false_ == false);
799         assert(cast(uint) true_ == 1);
800         assert(cast(uint) false_ == 0);
801         assert(cast(double) true_ == 1);
802         assert(cast(double) false_ == 0);
803     }
804 
805     /// numbers
806     unittest
807     {
808         import std.bigint;
809         import asdf.serialization;
810         auto number = serializeToAsdf(1234);
811         auto zero = serializeToAsdf(0);
812         static struct C
813         {
814             this(in char[] numberString)
815             {
816                 assert(numberString == "1234");
817             }
818         }
819         auto a = cast(C) number;
820         assert(cast(bool) number == true);
821         assert(cast(bool) zero == false);
822         assert(cast(uint) number == 1234);
823         assert(cast(double) number == 1234);
824         assert(cast(BigInt) number == 1234);
825         assert(cast(uint) zero == 0);
826         assert(cast(double) zero == 0);
827         assert(cast(BigInt) zero == 0);
828     }
829 
830     /// string
831     unittest
832     {
833         import std.bigint;
834         import asdf.serialization;
835         auto number = serializeToAsdf("1234");
836         auto false_ = serializeToAsdf("false");
837         auto bar = serializeToAsdf("bar");
838         auto zero = serializeToAsdf("0");
839         static struct C
840         {
841             this(in char[] str)
842             {
843                 assert(str == "1234");
844             }
845         }
846         auto a = cast(C) number;
847         assert(cast(string) number == "1234");
848         assert(cast(bool) number == true);
849         assert(cast(bool) bar == true);
850         assert(cast(bool) zero == false);
851         assert(cast(bool) false_ == false);
852         assert(cast(uint) number == 1234);
853         assert(cast(double) number == 1234);
854         assert(cast(BigInt) number == 1234);
855         assert(cast(uint) zero == 0);
856         assert(cast(double) zero == 0);
857         assert(cast(BigInt) zero == 0);
858     }
859 
860     /++
861     For ASDF arrays and objects `cast(T)` just returns `this.deserialize!T`.
862     +/
863     unittest
864     {
865         import std.bigint;
866         import asdf.serialization;
867         assert(cast(int[]) serializeToAsdf([100, 20]) == [100, 20]);
868     }
869 
870     /// UNIX Time
871     unittest
872     {
873         import std.datetime;
874         import asdf.serialization;
875 
876         auto num = serializeToAsdf(0.123456789); // rounding up to usecs
877         assert(cast(DateTime) num == DateTime(1970, 1, 1));
878         assert(cast(SysTime) num == SysTime(DateTime(1970, 1, 1), usecs(123456), UTC())); // UTC time zone is used.
879     }
880 }