The OpenD Programming Language

1 /++
2 Macros:
3 AlgorithmREF = $(GREF_ALTTEXT mir-algorithm, $(TT $2), $2, mir, $1)$(NBSP)
4 +/
5 module mir.ion.value;
6 
7 import mir.bignum.decimal: Decimal;
8 import mir.ion.exception;
9 import mir.ion.type_code;
10 import mir.lob;
11 import mir.utility: _expect;
12 import std.traits: isMutable, isIntegral, isSigned, isUnsigned, Unsigned, Signed, isFloatingPoint, ParameterTypeTuple;
13 
14 public import mir.ion.type_code;
15 public import mir.ion.exception;
16 
17 /++
18 Ion Version Marker
19 +/
20 struct IonVersionMarker
21 {
22     /// Major Version
23     ubyte major = 1;
24     /// Minor Version
25     ubyte minor = 0;
26 }
27 
28 package IonErrorCode parseVersion(ref const(ubyte)[] data, scope ref IonVersionMarker versionMarker)
29     @safe pure nothrow @nogc
30 {
31     version (LDC) pragma(inline, true);
32     if (data.length < 4 || data[0] != 0xE0 || data[3] != 0xEA)
33         return IonErrorCode.cantParseValueStream;
34     versionMarker = IonVersionMarker(data[1], data[2]);
35     data = data[4 .. $];
36     return IonErrorCode.none;
37 }
38 
39 /// Aliases the $(SUBREF type_code, IonTypeCode) to the corresponding Ion Typed Value type.
40 alias IonType(IonTypeCode code : IonTypeCode.null_) = IonNull;
41 /// ditto
42 alias IonType(IonTypeCode code : IonTypeCode.bool_) = bool;
43 /// ditto
44 alias IonType(IonTypeCode code : IonTypeCode.uInt) = IonUInt;
45 /// ditto
46 alias IonType(IonTypeCode code : IonTypeCode.nInt) = IonNInt;
47 /// ditto
48 alias IonType(IonTypeCode code : IonTypeCode.float_) = IonFloat;
49 /// ditto
50 alias IonType(IonTypeCode code : IonTypeCode.decimal) = IonDecimal;
51 /// ditto
52 alias IonType(IonTypeCode code : IonTypeCode.timestamp) = IonTimestamp;
53 /// ditto
54 alias IonType(IonTypeCode code : IonTypeCode.symbol) = IonSymbolID;
55 /// ditto
56 alias IonType(IonTypeCode code : IonTypeCode..string) = const(char)[];
57 /// ditto
58 alias IonType(IonTypeCode code : IonTypeCode.clob) = Clob;
59 /// ditto
60 alias IonType(IonTypeCode code : IonTypeCode.blob) = Blob;
61 /// ditto
62 alias IonType(IonTypeCode code : IonTypeCode.list) = IonList;
63 /// ditto
64 alias IonType(IonTypeCode code : IonTypeCode.sexp) = IonSexp;
65 /// ditto
66 alias IonType(IonTypeCode code : IonTypeCode.struct_) = IonStruct;
67 /// ditto
68 alias IonType(IonTypeCode code : IonTypeCode.annotations) = IonAnnotationWrapper;
69 
70 /// Aliases the type to the corresponding $(SUBREF type_code, IonTypeCode).
71 alias IonTypeCodeOf(T : IonNull) = IonTypeCode.null_;
72 /// ditto
73 alias IonTypeCodeOf(T : bool) = IonTypeCode.bool_;
74 /// ditto
75 alias IonTypeCodeOf(T : IonUInt) = IonTypeCode.uInt;
76 /// ditto
77 alias IonTypeCodeOf(T : IonNInt) = IonTypeCode.nInt;
78 /// ditto
79 alias IonTypeCodeOf(T : IonFloat) = IonTypeCode.float_;
80 /// ditto
81 alias IonTypeCodeOf(T : IonDecimal) = IonTypeCode.decimal;
82 /// ditto
83 alias IonTypeCodeOf(T : IonTimestamp) = IonTypeCode.timestamp;
84 /// ditto
85 alias IonTypeCodeOf(T : IonSymbolID) = IonTypeCode.symbol;
86 /// ditto
87 alias IonTypeCodeOf(T : const(char)[]) = IonTypeCode..string;
88 /// ditto
89 alias IonTypeCodeOf(T : Clob) = IonTypeCode.clob;
90 /// ditto
91 alias IonTypeCodeOf(T : Blob) = IonTypeCode.blob;
92 /// ditto
93 alias IonTypeCodeOf(T : IonList) = IonTypeCode.list;
94 /// ditto
95 alias IonTypeCodeOf(T : IonSexp) = IonTypeCode.sexp;
96 /// ditto
97 alias IonTypeCodeOf(T : IonStruct) = IonTypeCode.struct_;
98 /// ditto
99 alias IonTypeCodeOf(T : IonAnnotationWrapper) = IonTypeCode.annotations;
100 
101 /++
102 A template to check if the type is one of Ion Typed Value types.
103 See_also: $(SUBREF type_code, IonTypeCode)
104 +/
105 enum isIonType(T) = false;
106 /// ditto
107 enum isIonType(T : IonNull) = true;
108 /// ditto
109 enum isIonType(T : bool) = true;
110 /// ditto
111 enum isIonType(T : IonUInt) = true;
112 /// ditto
113 enum isIonType(T : IonNInt) = true;
114 /// ditto
115 enum isIonType(T : IonFloat) = true;
116 /// ditto
117 enum isIonType(T : IonDecimal) = true;
118 /// ditto
119 enum isIonType(T : IonTimestamp) = true;
120 /// ditto
121 enum isIonType(T : IonSymbolID) = true;
122 /// ditto
123 enum isIonType(T : const(char)[]) = true;
124 /// ditto
125 enum isIonType(T : Clob) = true;
126 /// ditto
127 enum isIonType(T : Blob) = true;
128 /// ditto
129 enum isIonType(T : IonList) = true;
130 /// ditto
131 enum isIonType(T : IonSexp) = true;
132 /// ditto
133 enum isIonType(T : IonStruct) = true;
134 /// ditto
135 enum isIonType(T : IonAnnotationWrapper) = true;
136 
137 /// Ion null value.
138 struct IonNull
139 {
140     ///
141     IonTypeCode code;
142 
143     /++
144     Params:
145         serializer = serializer
146     +/
147     void serialize(S)(scope ref S serializer) scope const
148     {
149         serializer.putNull(code);
150     }
151 
152     string toString() @safe pure nothrow @nogc const
153     {
154         return code.nullStringOf;
155     }
156 }
157 
158 /++
159 Ion Value
160 
161 The type descriptor octet has two subfields: a four-bit type code T, and a four-bit length L.
162 
163 ----------
164        7       4 3       0
165       +---------+---------+
166 value |    T    |    L    |
167       +---------+---------+======+
168       :     length [VarUInt]     :
169       +==========================+
170       :      representation      :
171       +==========================+
172 ----------
173 +/
174 struct IonValue
175 {
176     const(ubyte)[] data;
177 
178     /++
179     Describes value (nothrow version).
180     Params:
181         value = (out) $(LREF IonDescribedValue)
182     Returns: $(SUBREF exception, IonErrorCode)
183     +/
184     deprecated("")
185     IonErrorCode describe()(scope ref IonDescribedValue value)
186         @safe pure nothrow @nogc const
187     {
188         auto d = data[];
189         if (auto error = parseValue(d, value))
190             return error;
191         if (_expect(d.length, false))
192             return IonErrorCode.illegalBinaryData;
193         return IonErrorCode.none;
194     }
195 
196     IonDescribedValue describe()(scope out IonErrorCode error) return scope
197         @safe pure nothrow @nogc const
198     {
199         auto d = data[];
200         auto value = parseValue(d, error);
201         if (error)
202             return IonDescribedValue.init;
203         if (_expect(d.length, false))
204         {
205             error = IonErrorCode.illegalBinaryData;
206             return IonDescribedValue.init;
207         }
208         return value;
209     }
210 
211     version (D_Exceptions)
212     {
213         /++
214         Describes value.
215         Returns: $(LREF IonDescribedValue)
216         +/
217         IonDescribedValue describe()() return scope
218             @safe pure @nogc const
219         {
220             IonErrorCode error;
221             auto ret = describe(error);
222             if (error)
223                 throw error.ionException;
224             return ret;
225         }
226     }
227 
228     /++
229     Params:
230         serializer = serializer
231     +/
232     void serialize(S)(scope ref S serializer) scope const
233     {
234         describe.serialize(serializer);
235     }
236 
237     ///
238     pure @safe
239     unittest
240     {
241         import mir.ion.stream;
242         import mir.ser.json;
243         assert(IonValueStream([0x11]).serializeJson == "true");
244     }
245 }
246 
247 ///
248 @safe pure
249 version(mir_ion_test) unittest
250 {
251     import mir.lob;
252     import mir.ion.type_code;
253     import mir.ion.value;
254     // null.string
255     assert(IonValue([0x9F]).describe.get!IonNull == IonNull(IonTypeCode.clob));
256     // empty string
257     assert(IonValue([0x90]).describe.get!Clob.data == "");
258 
259     assert(IonValue([0x95, 0x63, 0x6f, 0x76, 0x69, 0x64]).describe.get!Clob.data == "covid");
260 }
261 
262 ///
263 @safe pure
264 version(mir_ion_test) unittest
265 {
266     import mir.lob;
267     import mir.ion.type_code;
268     import mir.ion.value;
269     // null.string
270     assert(IonValue([0xAF]).describe.get!IonNull == IonNull(IonTypeCode.blob));
271     // empty string
272     assert(IonValue([0xA0]).describe.get!Blob.data == "");
273 
274     assert(IonValue([0xA5, 0x63, 0x6f, 0x76, 0x69, 0x64]).describe.get!Blob.data == "covid");
275 }
276 
277 
278 /++
279 Ion Type Descriptor
280 +/
281 struct IonDescriptor
282 {
283     /++
284     The type descriptor octet has two subfields: a four-bit type code T, and a four-bit length L.
285     +/
286     this(scope const(ubyte)* reference)
287         @safe pure nothrow @nogc
288     {
289         assert(reference);
290         this(*reference);
291     }
292 
293     /// ditto
294     this(ubyte value)
295         @safe pure nothrow @nogc
296     {
297         this.type = cast(IonTypeCode)(value >> 4);
298         assert(type <= IonTypeCode.max);
299         this.L = cast(uint)(value & 0xF);
300     }
301 
302     /// T
303     IonTypeCode type;
304 
305     /// L
306     uint L;
307 }
308 
309 /++
310 Ion Described Value stores type descriptor and rerpresentation.
311 +/
312 struct IonDescribedValue
313 {
314     /// Type Descriptor
315     IonDescriptor descriptor;
316     /// Rerpresentation
317     const(ubyte)[] data;
318 
319     /++
320     Returns: true if the value is any Ion `null`.
321     +/
322     bool opEquals()(typeof(null))
323         @safe pure nothrow @nogc const
324     {
325         return descriptor.L == 0xF;
326     }
327 
328     /++
329     Returns: true if the values have the same binary representation.
330     +/
331     bool opEquals()(scope IonDescribedValue rhs)
332         @safe pure nothrow @nogc const
333     {
334         return this.descriptor == rhs.descriptor && this.data == rhs.data;
335     }
336 
337     /++
338     Gets typed value (nothrow version).
339     Params:
340         value = (out) Ion Typed Value
341     Returns: $(SUBREF exception, IonErrorCode)
342     +/
343     deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
344     IonErrorCode get(T)(scope ref T value)
345         @safe pure nothrow @nogc const
346         if (isIonType!T || is(T == IonInt))
347     {
348         static if (is(T == IonNull))
349         {
350             if (_expect(descriptor.L != 0xF, false))
351                 return IonErrorCode.unexpectedIonType;
352         }
353         else
354         static if (is(T == IonInt))
355         {
356             if (_expect(descriptor.L == 0xF || (descriptor.type | 1) != IonTypeCodeOf!IonNInt, false))
357                 return IonErrorCode.unexpectedIonType;
358         }
359         else
360         {
361             if (_expect(descriptor.L == 0xF || descriptor.type != IonTypeCodeOf!T, false))
362                 return IonErrorCode.unexpectedIonType;
363         }
364         value = trustedGet!T;
365         return IonErrorCode.none;
366     }
367 
368     /++
369     Gets typed value (nothrow version).
370     Params:
371         value = (out) Ion Typed Value
372     Returns: $(SUBREF exception, IonErrorCode)
373     +/
374     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
375     T get(T)(scope out IonErrorCode error) return scope
376         @safe pure nothrow @nogc const
377         if (isIonType!T || is(T == IonInt))
378     {
379 
380         static if (is(T == IonNull))
381         {
382             if (_expect(descriptor.L != 0xF, false))
383             {
384                 error = IonErrorCode.unexpectedIonType;
385                 return T.init;
386             }
387             return trustedGet!T;
388         }
389         else
390         static if (is(T == IonInt))
391         {
392             if (_expect(descriptor.L == 0xF || (descriptor.type | 1) != IonTypeCodeOf!IonNInt, false))
393             {
394                 error = IonErrorCode.unexpectedIonType;
395                 return T.init;
396             }
397             return trustedGet!T;
398         }
399         else
400         {
401             if (_expect(descriptor.L == 0xF || descriptor.type != IonTypeCodeOf!T, false))
402             {
403                 error = IonErrorCode.unexpectedIonType;
404                 return T.init;
405             }
406 
407             static if (is(T == IonAnnotationWrapper))
408             {
409                 size_t length;
410                 const(ubyte)[] d = data;
411                 error = parseVarUInt(d, length);
412                 if (error)
413                     return T.init;
414                 if (_expect(length == 0, false))
415                 {
416                     error = IonErrorCode.zeroAnnotations;
417                     return T.init;
418                 }
419                 if (_expect(length >= d.length, false))
420                 {
421                     error = IonErrorCode.unexpectedEndOfData;
422                     return T.init;
423                 }
424                 auto idv = IonValue(d[length .. $]).describe(error);
425                 if (error)
426                     return T.init;
427                 if (_expect(idv.descriptor.type == IonTypeCode.annotations, false))
428                 {
429                     error = IonErrorCode.nestedAnnotationsNotAllowed;
430                     return T.init;
431                 }
432                 return T(IonAnnotations(d[0 .. length]), idv);
433             }
434             else
435             {
436                 return trustedGet!T;
437             }
438         }
439     }
440 
441     /++
442     Gets typed value.
443     Returns: Ion Typed Value
444     +/
445     T get(T)() return scope
446         @safe pure @nogc const
447         if (isIonType!T || is(T == IonInt))
448     {
449         static if (is(T == IonAnnotationWrapper))
450         {
451             IonErrorCode error;
452             auto ret = get!IonAnnotationWrapper(error);
453             if (error)
454                 throw error.ionException;
455             return ret;
456         }
457         else
458         static if (is(T == IonNull))
459         {
460             if (_expect(descriptor.L != 0xF, false))
461                 throw IonErrorCode.unexpectedIonType.ionException;
462             return trustedGet!T;
463         }
464         else
465         static if (is(T == IonInt))
466         {
467             if (_expect(descriptor.type == IonTypeCode.null_ || (descriptor.type | 1) != IonTypeCodeOf!IonNInt, false))
468                 throw IonErrorCode.unexpectedIonType.ionException;
469             return trustedGet!T;
470         }
471         else
472         {
473             if (_expect(descriptor.type == IonTypeCode.null_ || descriptor.type != IonTypeCodeOf!T, false))
474                 throw IonErrorCode.unexpectedIonType.ionException;
475             return trustedGet!T;
476         }
477     }
478 
479     /++
480     Gets typed value (nothrow internal version).
481     Returns:
482         Ion Typed Value
483     Note:
484         This function doesn't check the encoded value type.
485     +/
486     T trustedGet(T)() return scope
487         @safe pure nothrow @nogc const
488         if ((isIonType!T || is(T == IonInt)) && !is(T == IonAnnotationWrapper))
489     {
490         static if (is(T == IonInt))
491         {
492             assert(descriptor.type == IonTypeCode.null_ || (descriptor.type == IonTypeCodeOf!IonNInt || descriptor.type == IonTypeCodeOf!IonUInt), T.stringof);
493         }
494         else
495         static if (is(T == IonNull))
496         {
497             assert(descriptor.L == 0xF, T.stringof);
498         }
499         else
500         {
501             assert(descriptor.type == IonTypeCode.null_ ||  descriptor.type == IonTypeCodeOf!T, T.stringof);
502         }
503 
504         static if (is(T == IonNull))
505         {
506             return T(descriptor.type);
507         }
508         else
509         static if (is(T == bool))
510         {
511             return descriptor.L & 1;
512         }
513         else
514         static if (is(T == IonStruct))
515         {
516             return T(descriptor, data);
517         }
518         else
519         static if (is(T == const(char)[]))
520         {
521             return cast(const(char)[])data;
522         }
523         else
524         static if (is(T == Clob))
525         {
526             return T(cast(const(char)[])data);
527         }
528         else
529         static if (is(T == IonUInt) || is(T == IonNInt) || is(T == IonSymbolID))
530         {
531             return T(data);
532         }
533         else
534         static if (is(T == IonInt))
535         {
536             return T(descriptor.type & 1, data);
537         }
538         else
539         {
540             return T(data);
541         }
542     }
543 
544     // Issue 21681 workaround
545     private void serializeDummy(S)(scope ref S serializer) scope const @safe
546     {
547         final switch (descriptor.type) with (IonTypeCode)
548         {
549             case IonTypeCode.null_:
550                 trustedGet!IonNull.serialize(serializer);
551                 break;
552             case IonTypeCode.bool_:
553                 serializer.putValue(trustedGet!bool);
554                 break;
555             case IonTypeCode.uInt:
556             case IonTypeCode.nInt:
557                 trustedGet!IonInt.serialize(serializer);
558                 break;
559             case IonTypeCode.float_:
560                 trustedGet!IonFloat.serialize(serializer);
561                 break;
562             case IonTypeCode.decimal:
563                 trustedGet!IonDecimal.serialize(serializer);
564                 break;
565             case IonTypeCode.timestamp:
566                 trustedGet!IonTimestamp.serialize(serializer);
567                 break;
568             case IonTypeCode.symbol:
569                 trustedGet!IonSymbolID.serialize(serializer);
570                 break;
571             case IonTypeCode..string:
572                 serializer.putValue(trustedGet!(const(char)[]));
573                 break;
574             case IonTypeCode.clob:
575                 serializer.putValue(trustedGet!Clob);
576                 break;
577             case IonTypeCode.blob:
578                 serializer.putValue(trustedGet!Blob);
579                 break;
580             case IonTypeCode.list:
581                 break;
582             case IonTypeCode.sexp:
583                 break;
584             case IonTypeCode.struct_:
585                 break;
586             case IonTypeCode.annotations:
587                 break;
588         }
589     }
590 
591     // Issue 21681 workaround
592     private void serializeImpl(S)(scope ref S serializer) scope const @safe pure nothrow @nogc
593     {
594         return invokeAssumingAllAttributes(()
595         {
596             if (this == null)
597             {
598                 trustedGet!IonNull.serialize(serializer);
599             }
600             else
601             {
602                 final switch (descriptor.type) with (IonTypeCode)
603                 {
604                     case IonTypeCode.null_:
605                         assert(0);
606                     case IonTypeCode.bool_:
607                         serializer.putValue(trustedGet!bool);
608                         break;
609                     case IonTypeCode.uInt:
610                     case IonTypeCode.nInt:
611                         trustedGet!IonInt.serialize(serializer);
612                         break;
613                     case IonTypeCode.float_:
614                         trustedGet!IonFloat.serialize(serializer);
615                         break;
616                     case IonTypeCode.decimal:
617                         trustedGet!IonDecimal.serialize(serializer);
618                         break;
619                     case IonTypeCode.timestamp:
620                         trustedGet!IonTimestamp.serialize(serializer);
621                         break;
622                     case IonTypeCode.symbol:
623                         trustedGet!IonSymbolID.serialize(serializer);
624                         break;
625                     case IonTypeCode..string:
626                         serializer.putValue(trustedGet!(const(char)[]));
627                         break;
628                     case IonTypeCode.clob:
629                         serializer.putValue(trustedGet!Clob);
630                         break;
631                     case IonTypeCode.blob:
632                         serializer.putValue(trustedGet!Blob);
633                         break;
634                     case IonTypeCode.list:
635                         trustedGet!IonList.serialize(serializer);
636                         break;
637                     case IonTypeCode.sexp:
638                         trustedGet!IonSexp.serialize(serializer);
639                         break;
640                     case IonTypeCode.struct_:
641                         trustedGet!IonStruct.serialize(serializer);
642                         break;
643                     case IonTypeCode.annotations:
644                         get!IonAnnotationWrapper.serialize(serializer);
645                         break;
646                 }
647             }
648         });
649     }
650 
651     /++
652     Params:
653         serializer = serializer
654     +/
655     void serialize(S)(scope ref S serializer) scope const @safe
656     {
657         // Issue 21681 workaround
658         serializeImpl(serializer);
659         if (false)
660             serializeDummy(serializer);
661     }
662 }
663 
664 private auto invokeAssumingAllAttributes(T)(scope T t) @trusted pure nothrow @nogc
665 {
666     import std.traits: functionAttributes, functionLinkage, FunctionAttribute, SetFunctionAttributes;
667     enum attrs = functionAttributes!T  & ~FunctionAttribute.system  & ~FunctionAttribute.trusted | FunctionAttribute.pure_  | FunctionAttribute.nothrow_ | FunctionAttribute.nogc | FunctionAttribute.safe;
668     return (cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t)();
669 }
670 
671 /++
672 Ion integer field.
673 +/
674 struct IonIntField
675 {
676     ///
677     const(ubyte)[] data;
678 
679     /++
680     Params:
681         value = (out) signed integer
682     Returns: $(SUBREF exception, IonErrorCode)
683     +/
684     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
685     IonErrorCode get(T)(scope ref T value)
686         @safe pure nothrow @nogc scope const
687         if (isSigned!T)
688     {
689         auto d = cast()data;
690         size_t i;
691         T f;
692         bool s;
693         if (d.length == 0)
694             goto R;
695         f = d[0] & 0x7F;
696         s = d[0] >> 7;
697         for(;;)
698         {
699             d = d[1 .. $];
700             if (d.length == 0)
701             {
702                 if (f < 0)
703                     break;
704                 if (s)
705                     f = cast(T)(0-f);
706             R:
707                 value = f;
708                 return IonErrorCode.none;
709             }
710             i += cast(bool)f;
711             f <<= 8;
712             f |= d[0];
713             if (i >= T.sizeof)
714                 break;
715         }
716         return IonErrorCode.overflowInIntegerValue;
717     }
718 
719     version (D_Exceptions)
720     {
721         /++
722         Returns: signed integer
723         Precondition: `this != null`.
724         +/
725         T get(T)()
726             @safe pure @nogc const
727             if (isSigned!T)
728         {
729             T ret;
730             if (auto error = get(ret))
731                 throw error.ionException;
732             return ret;
733         }
734     }
735 
736     /++
737     Returns: $(SUBREF exception, IonErrorCode)
738     Precondition: `this != null`.
739     +/
740     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
741     IonErrorCode getErrorCode(T)()
742         @trusted pure nothrow @nogc const
743         if (isSigned!T)
744     {
745         T value;
746         return get!T(value);
747     }
748 }
749 
750 ///
751 @safe pure
752 version(mir_ion_test) unittest
753 {
754     assert(IonValue([0x1F]).describe.get!IonNull.code == IonTypeCode.bool_);
755     assert(IonValue([0x10]).describe.get!bool == false);
756     assert(IonValue([0x11]).describe.get!bool == true);
757 }
758 
759 /++
760 Ion non-negative integer number.
761 +/
762 struct IonUInt
763 {
764     ///
765     const(ubyte)[] data;
766 
767 
768     // /++
769     // Returns: true if the integer isn't `null.int` and equals to `rhs`.
770     // +/
771     // bool opEquals(ulong rhs)
772     //     @safe pure nothrow @nogc const
773     // {
774     //     return field == rhs;
775     // }
776 
777     /++
778     Params:
779         value = (out) unsigned or signed integer
780     Returns: $(SUBREF exception, IonErrorCode)
781     +/
782     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
783     IonErrorCode get(T)(scope ref T value)
784         @trusted pure nothrow @nogc scope const
785         if (isIntegral!T && T.sizeof >= 4)
786     {
787         static if (isUnsigned!T)
788         {
789             return data.IonSymbolID.get(value);
790         }
791         else
792         {
793             Unsigned!T uvalue;
794             if (auto error = this.get(uvalue))
795                 return error;
796             value = uvalue;
797             if (_expect(value < 0, false))
798                 return IonErrorCode.overflowInIntegerValue;
799             return IonErrorCode.none;
800         }
801     }
802 
803     /// ditto
804     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
805     IonErrorCode get(T)(scope ref T value)
806         @trusted pure nothrow @nogc scope const
807         if (isIntegral!T && T.sizeof < 4)
808     {
809         static if (isUnsigned!T)
810             uint ext;
811         else
812             int ext;
813         if (auto error = this.get(ext))
814             return error;
815         if (cast(T)ext != ext)
816             return IonErrorCode.overflowInIntegerValue;
817         value = cast(T)ext;
818         return IonErrorCode.none;
819     }
820 
821     version (D_Exceptions)
822     {
823         /++
824         Returns: unsigned or signed integer
825         +/
826         T get(T)()
827             @safe pure @nogc const
828             if (isIntegral!T)
829         {
830             T ret;
831             if (auto error = get(ret))
832                 throw error.ionException;
833             return ret;
834         }
835     }
836 
837     /++
838     Returns: $(SUBREF exception, IonErrorCode)
839     +/
840     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
841     IonErrorCode getErrorCode(T)()
842         @trusted pure nothrow @nogc const
843         if (isIntegral!T)
844     {
845         T value;
846         return get!T(value);
847     }
848 }
849 
850 ///
851 @safe pure
852 version(mir_ion_test) unittest
853 {
854     import mir.test;
855     assert(IonValue([0x2F]).describe.get!IonNull == IonNull(IonTypeCode.uInt));
856     assert(IonValue([0x21, 0x07]).describe.get!IonUInt.get!int == 7);
857 
858     int v;
859     assert(IonValue([0x22, 0x01, 0x04]).describe.get!IonUInt.get(v) == IonErrorCode.none);
860     v.should == 260;
861 }
862 
863 @safe pure
864 version(mir_ion_test) unittest
865 {
866     import mir.test: should;
867     alias AliasSeq(T...) = T;
868     foreach (T; AliasSeq!(byte, short, int, long, ubyte, ushort, uint, ulong))
869     {
870         IonValue([0x20]).describe.get!IonUInt.getErrorCode!T.should == 0;
871         IonValue([0x21, 0x00]).describe.get!IonUInt.getErrorCode!T.should == 0;
872 
873         IonValue([0x21, 0x07]).describe.get!IonUInt.get!T.should == 7;
874         IonValue([0x2E, 0x81, 0x07]).describe.get!IonUInt.get!T.should == 7;
875         IonValue([0x2A, 0,0,0, 0,0,0, 0,0,0, 0x07]).describe.get!IonUInt.get!T.should == 7;
876     }
877 
878     IonValue([0x21, 0x7F]).describe.get!IonUInt.get!byte.should == byte.max;
879     IonValue([0x22, 0x7F, 0xFF]).describe.get!IonUInt.get!short.should == short.max;
880     IonValue([0x24, 0x7F, 0xFF,0xFF,0xFF]).describe.get!IonUInt.get!int.should == int.max;
881     IonValue([0x28, 0x7F, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF]).describe.get!IonUInt.get!long.should == long.max;
882     IonValue([0x2A, 0,0, 0x7F, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF]).describe.get!IonUInt.get!long.should == long.max;
883 
884     IonValue([0x21, 0xFF]).describe.get!IonUInt.get!ubyte.should == ubyte.max;
885     IonValue([0x22, 0xFF, 0xFF]).describe.get!IonUInt.get!ushort.should == ushort.max;
886     IonValue([0x24, 0xFF, 0xFF,0xFF,0xFF]).describe.get!IonUInt.get!uint.should == uint.max;
887     IonValue([0x28, 0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF]).describe.get!IonUInt.get!ulong.should == ulong.max;
888     IonValue([0x2A, 0,0, 0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF]).describe.get!IonUInt.get!ulong.should == ulong.max;
889 
890     IonValue([0x21, 0x80]).describe.get!IonUInt.getErrorCode!byte.should == IonErrorCode.overflowInIntegerValue;
891     IonValue([0x22, 0x80, 0]).describe.get!IonUInt.getErrorCode!short.should == IonErrorCode.overflowInIntegerValue;
892     IonValue([0x24, 0x80, 0,0,0]).describe.get!IonUInt.getErrorCode!int.should == IonErrorCode.overflowInIntegerValue;
893 
894     IonValue([0x22, 1, 0]).describe.get!IonUInt.getErrorCode!ubyte.should == IonErrorCode.overflowInIntegerValue;
895     IonValue([0x23, 1, 0,0]).describe.get!IonUInt.getErrorCode!ushort.should == IonErrorCode.overflowInIntegerValue;
896     IonValue([0x25, 1, 0,0,0,0]).describe.get!IonUInt.getErrorCode!uint.should == IonErrorCode.overflowInIntegerValue;
897     IonValue([0x29, 1, 0,0,0,0,0,0,0,0]).describe.get!IonUInt.getErrorCode!ulong.should == IonErrorCode.overflowInIntegerValue;
898 }
899 
900 /++
901 Ion negative integer number.
902 +/
903 struct IonNInt
904 {
905     ///
906     const(ubyte)[] data;
907 
908     // /++
909     // Returns: true if the integer isn't `null.int` and equals to `rhs`.
910     // +/
911     // bool opEquals(long rhs)
912     //     @safe pure nothrow @nogc const
913     // {
914     //     if (rhs >= 0)
915     //         return false;
916     //     return IonUInt(data) == -rhs;
917     // }
918 
919     /++
920     Params:
921         value = (out) signed or unsigned integer
922     Returns: $(SUBREF exception, IonErrorCode)
923     +/
924     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
925     IonErrorCode get(T)(scope ref T value)
926         @trusted pure nothrow @nogc scope const
927         if (isIntegral!T && T.sizeof >= 4)
928     {
929         static if (isUnsigned!T)
930         {
931             return IonErrorCode.overflowInIntegerValue;
932         }
933         else
934         {
935             Unsigned!T uvalue;
936             if (auto overflow = data.IonUInt.get(uvalue))
937                 return IonErrorCode.overflowInIntegerValue;
938             value = -uvalue;
939             if (_expect(value >= 0, false))
940                 return IonErrorCode.overflowInIntegerValue;
941             return IonErrorCode.none;
942         }
943     }
944 
945     /// ditto
946     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
947     IonErrorCode get(T)(scope ref T value)
948         @trusted pure nothrow @nogc scope const
949         if (isIntegral!T && T.sizeof < 4)
950     {
951         static if (isUnsigned!T)
952             uint ext;
953         else
954             int ext;
955         if (auto error = this.get(ext))
956             return error;
957         if (cast(T)ext != ext)
958             return IonErrorCode.overflowInIntegerValue;
959         value = cast(T)ext;
960         return IonErrorCode.none;
961     }
962 
963     version (D_Exceptions)
964     {
965         /++
966         Returns: unsigned or signed integer
967         +/
968         T get(T)()
969             @safe pure @nogc const
970             if (isIntegral!T)
971         {
972             T ret;
973             if (auto error = get(ret))
974                 throw error.ionException;
975             return ret;
976         }
977     }
978 
979     /++
980     Returns: $(SUBREF exception, IonErrorCode)
981     +/
982     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
983     IonErrorCode getErrorCode(T)()
984         @trusted pure nothrow @nogc const
985         if (isIntegral!T)
986     {
987         T value;
988         return get!T(value);
989     }
990 }
991 
992 ///
993 @safe pure
994 version(mir_ion_test) unittest
995 {
996     import mir.test;
997     assert(IonValue([0x3F]).describe.get!IonNull == IonNull(IonTypeCode.nInt));
998     assert(IonValue([0x31, 0x07]).describe.get!IonNInt.get!int == -7);
999 
1000     long v;
1001     assert(IonValue([0x32, 0x01, 0x04]).describe.get!IonNInt.get(v) == IonErrorCode.none);
1002     v.should == -260;
1003 
1004     // IonNInt can't store zero according to the Ion Binary format specification.
1005     IonErrorCode error;
1006     IonValue([0x30]).describe(error);
1007     assert(error == IonErrorCode.negativeIntegerZero);
1008     IonValue([0x31, 0x00]).describe(error);
1009     assert(error == IonErrorCode.negativeIntegerZero);
1010     IonValue([0x3E, 0x80]).describe(error);
1011     assert(error == IonErrorCode.negativeIntegerZero);
1012 }
1013 
1014 @safe pure
1015 version(mir_ion_test) unittest
1016 {
1017     alias AliasSeq(T...) = T;
1018     foreach (T; AliasSeq!(byte, short, int, long, ubyte, ushort, uint, ulong))
1019     {
1020         static if (!__traits(isUnsigned, T))
1021         {   // signed
1022             assert(IonValue([0x31, 0x07]).describe.get!IonNInt.get!T == -7);
1023             assert(IonValue([0x3E, 0x81, 0x07]).describe.get!IonNInt.get!T == -7);
1024             assert(IonValue([0x3A, 0,0,0, 0,0,0, 0,0,0, 0x07]).describe.get!IonNInt.get!T == -7);
1025         }
1026         else
1027         {   // unsigned integers can't represent negative numbers
1028             assert(IonValue([0x31, 0x07]).describe.get!IonNInt.getErrorCode!T == IonErrorCode.overflowInIntegerValue);
1029             assert(IonValue([0x3E, 0x81, 0x07]).describe.get!IonNInt.getErrorCode!T == IonErrorCode.overflowInIntegerValue);
1030             assert(IonValue([0x3A, 0,0,0, 0,0,0, 0,0,0, 0x07]).describe.get!IonNInt.getErrorCode!T == IonErrorCode.overflowInIntegerValue);
1031         }
1032     }
1033 
1034     assert(IonValue([0x31, 0x80]).describe.get!IonNInt.get!byte == byte.min);
1035     assert(IonValue([0x32, 0x80, 0]).describe.get!IonNInt.get!short == short.min);
1036     assert(IonValue([0x34, 0x80, 0,0,0]).describe.get!IonNInt.get!int == int.min);
1037     assert(IonValue([0x38, 0x80, 0,0,0, 0,0,0,0]).describe.get!IonNInt.get!long == long.min);
1038 
1039     assert(IonValue([0x31, 0x81]).describe.get!IonNInt.getErrorCode!byte == IonErrorCode.overflowInIntegerValue);
1040     assert(IonValue([0x32, 0x80, 1]).describe.get!IonNInt.getErrorCode!short == IonErrorCode.overflowInIntegerValue);
1041     assert(IonValue([0x34, 0x80, 0,0,1]).describe.get!IonNInt.getErrorCode!int == IonErrorCode.overflowInIntegerValue);
1042     assert(IonValue([0x38, 0x80, 0,0,0, 0,0,0,1]).describe.get!IonNInt.getErrorCode!long == IonErrorCode.overflowInIntegerValue);
1043 }
1044 
1045 /++
1046 Ion signed integer number.
1047 +/
1048 struct IonInt
1049 {
1050     ///
1051     bool sign;
1052     ///
1053     const(ubyte)[] data;
1054 
1055     /++
1056     Params:
1057         value = (out) signed or unsigned integer
1058     Returns: $(SUBREF exception, IonErrorCode)
1059     +/
1060     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1061     IonErrorCode get(T)(scope ref T value)
1062         @trusted pure nothrow @nogc scope const
1063         if (isIntegral!T && T.sizeof >= 4)
1064     {
1065         static if (isUnsigned!T)
1066         {
1067             if (sign)
1068                 return IonErrorCode.overflowInIntegerValue;
1069         }
1070 
1071         Unsigned!T uvalue;
1072         if (auto overflow = data.IonUInt.get(uvalue))
1073             return IonErrorCode.overflowInIntegerValue;
1074         value = uvalue;
1075         static if (isSigned!T)
1076         {
1077             if (sign)
1078             {
1079                 value = -value;
1080                 if (value >= 0)
1081                     return IonErrorCode.overflowInIntegerValue;
1082             }
1083             else
1084             {
1085                 if (value < 0)
1086                     return IonErrorCode.overflowInIntegerValue;
1087             }
1088         }
1089 
1090         return IonErrorCode.none;
1091     }
1092 
1093     /// ditto
1094     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1095     IonErrorCode get(T)(scope ref T value)
1096         @trusted pure nothrow @nogc scope const
1097         if (isIntegral!T && T.sizeof < 4)
1098     {
1099         static if (isUnsigned!T)
1100             uint ext;
1101         else
1102             int ext;
1103         if (auto error = this.get(ext))
1104             return error;
1105         if (cast(T)ext != ext)
1106             return IonErrorCode.overflowInIntegerValue;
1107         value = cast(T)ext;
1108         return IonErrorCode.none;
1109     }
1110 
1111     version (D_Exceptions)
1112     {
1113         /++
1114         Returns: unsigned or signed integer
1115         +/
1116         T get(T)()
1117             @safe pure @nogc const
1118             if (isIntegral!T)
1119         {
1120             T ret;
1121             if (auto error = get(ret))
1122                 throw error.ionException;
1123             return ret;
1124         }
1125     }
1126 
1127     /++
1128     Returns: $(SUBREF exception, IonErrorCode)
1129     +/
1130     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1131     IonErrorCode getErrorCode(T)()
1132         @trusted pure nothrow @nogc const
1133         if (isIntegral!T)
1134     {
1135         T value;
1136         return get!T(value);
1137     }
1138 
1139     /++
1140     Params:
1141         serializer = serializer
1142     +/
1143     void serialize(S)(scope ref S serializer) scope const
1144     {
1145         import mir.bignum.integer: BigInt;
1146         auto f = BigInt!128.fromBigEndian(data, sign);
1147         serializer.putValue(f);
1148     }
1149 }
1150 
1151 /// test with $(LREF IonUInt)s
1152 @safe pure
1153 version(mir_ion_test) unittest
1154 {
1155     import mir.ion.exception;
1156     import mir.test;
1157 
1158     assert(IonValue([0x2F]).describe.get!IonNull == IonNull(IonTypeCode.uInt));
1159     assert(IonValue([0x21, 0x07]).describe.get!IonInt.get!int == 7);
1160     assert(IonValue([0x20]).describe.get!IonInt.get!int == 0);
1161 
1162     int v;
1163     assert(IonValue([0x22, 0x01, 0x04]).describe.get!IonInt.get(v) == IonErrorCode.none);
1164     v.should == 260;
1165 }
1166 
1167 /// test with $(LREF IonNInt)s
1168 @safe pure
1169 version(mir_ion_test) unittest
1170 {
1171     import mir.ion.exception;
1172     import mir.test;
1173 
1174     assert(IonValue([0x3F]).describe.get!IonNull == IonNull(IonTypeCode.nInt));
1175     assert(IonValue([0x31, 0x07]).describe.get!IonInt.get!int == -7);
1176 
1177     long v;
1178     assert(IonValue([0x32, 0x01, 0x04]).describe.get!IonInt.get(v) == IonErrorCode.none);
1179     v.should == -260;
1180 }
1181 
1182 /++
1183 Ion floating point number.
1184 +/
1185 struct IonFloat
1186 {
1187     ///
1188     const(ubyte)[] data;
1189 
1190     /++
1191     Params:
1192         value = (out) `float`, `double`, or `real`
1193     Returns: $(SUBREF exception, IonErrorCode)
1194     +/
1195     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1196     IonErrorCode get(T)(scope ref T value)
1197         @safe pure nothrow @nogc scope const
1198         if (isFloatingPoint!T)
1199     {
1200         if (data.length == 8)
1201         {
1202             value = parseFloating!double(data);
1203             return IonErrorCode.none;
1204         }
1205         if (data.length == 4)
1206         {
1207             value = parseFloating!float(data);
1208             return IonErrorCode.none;
1209         }
1210         if (_expect(data.length, false))
1211         {
1212             return IonErrorCode.wrongFloatDescriptor;
1213         }
1214         value = 0;
1215         return IonErrorCode.none;
1216     }
1217 
1218     version(D_Exceptions)
1219     {
1220         /++
1221         Returns: floating point number
1222         +/
1223         T get(T)()
1224             @safe pure @nogc const
1225             if (isFloatingPoint!T)
1226         {
1227             T ret;
1228             if (auto error = get(ret))
1229                 throw error.ionException;
1230             return ret;
1231         }
1232     }
1233 
1234     /++
1235     Returns: $(SUBREF exception, IonErrorCode)
1236     +/
1237     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1238     IonErrorCode getErrorCode(T)()
1239         @trusted pure nothrow @nogc const
1240         if (isFloatingPoint!T)
1241     {
1242         T value;
1243         return get!T(value);
1244     }
1245 
1246     /++
1247     Params:
1248         serializer = serializer
1249     +/
1250     void serialize(S)(scope ref S serializer) scope const
1251     {
1252         if (data.length == 8)
1253         {
1254             auto value = parseFloating!double(data);
1255             serializer.putValue(value);
1256             return;
1257         }
1258         if (data.length == 4)
1259         {
1260             auto value = parseFloating!float(data);
1261             serializer.putValue(value);
1262             return;
1263         }
1264         if (_expect(data.length, false))
1265         {
1266             throw IonErrorCode.wrongFloatDescriptor.ionException;
1267         }
1268         serializer.putValue(0f);
1269     }
1270 }
1271 
1272 ///
1273 @safe pure
1274 version(mir_ion_test) unittest
1275 {
1276     // null
1277     assert(IonValue([0x4F]).describe.get!IonNull == IonNull(IonTypeCode.float_));
1278 
1279     // zero
1280     auto ionFloat = IonValue([0x40]).describe.get!IonFloat;
1281     assert(ionFloat.get!float == 0);
1282     assert(ionFloat.get!double == 0);
1283     assert(ionFloat.get!real == 0);
1284 
1285     // single
1286     ionFloat = IonValue([0x44, 0x42, 0xAA, 0x40, 0x00]).describe.get!IonFloat;
1287     assert(ionFloat.get!float == 85.125);
1288     assert(ionFloat.get!double == 85.125);
1289     assert(ionFloat.get!real == 85.125);
1290 
1291     // double
1292     ionFloat = IonValue([0x48, 0x40, 0x55, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00]).describe.get!IonFloat;
1293     assert(ionFloat.get!float == 85.125);
1294     assert(ionFloat.get!double == 85.125);
1295     assert(ionFloat.get!real == 85.125);
1296 }
1297 
1298 /++
1299 +/
1300 struct IonDescribedDecimal
1301 {
1302     ///
1303     int exponent;
1304     ///
1305     IonIntField coefficient;
1306 
1307     ///
1308     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1309     IonErrorCode get(size_t maxW64bitSize)(scope ref Decimal!maxW64bitSize value)
1310         @safe pure nothrow @nogc scope const
1311     {
1312         enum maxLength = maxW64bitSize * 8;
1313         if (!value.coefficient.copyFromBigEndian(coefficient.data))
1314             return IonErrorCode.overflowInDecimalValue;
1315         value.exponent = exponent;
1316         if (value.coefficient.length == 0)
1317             return IonErrorCode.none;
1318         value.coefficient.sign = coefficient.data[0] >> 7;
1319         if (value.coefficient.sign)
1320         {
1321             import mir.ndslice.topology: bitwise;
1322             assert(value
1323                 .coefficient
1324                 .coefficients
1325                 .bitwise[coefficient.data.length * 8 - 1]);
1326             value
1327                 .coefficient
1328                 .coefficients
1329                 .bitwise[coefficient.data.length * 8 - 1] = false;
1330             assert(!value
1331                 .coefficient
1332                 .coefficients
1333                 .bitwise[coefficient.data.length * 8 - 1]);
1334             if (value.coefficient.coefficients[$ - 1] == 0)
1335                 value.coefficient.length--;
1336         }
1337         return IonErrorCode.none;
1338     }
1339 
1340     /++
1341     Params:
1342         value = (out) floating point number
1343     Returns: $(SUBREF exception, IonErrorCode)
1344     +/
1345     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1346     IonErrorCode get(T)(scope ref T value)
1347         @safe pure nothrow @nogc scope const
1348         if (isFloatingPoint!T && isMutable!T)
1349     {
1350         Decimal!128 decimal = void;
1351         if (auto ret = this.get(decimal))
1352             return ret;
1353         value = cast(T) decimal;
1354         return IonErrorCode.none;
1355     }
1356 
1357     version(D_Exceptions)
1358     {
1359         /++
1360         Returns: floating point number
1361         +/
1362         T get(T = double)()
1363             @safe pure @nogc const
1364             if (isFloatingPoint!T)
1365         {
1366             T ret;
1367             if (auto error = get(ret))
1368                 throw error.ionException;
1369             return ret;
1370         }
1371     }
1372 
1373     /++
1374     Returns: $(SUBREF exception, IonErrorCode)
1375     +/
1376     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1377     IonErrorCode getErrorCode()()
1378         @trusted pure nothrow @nogc const
1379         if (isFloatingPoint!T)
1380     {
1381         Decimal!128 decimal = void;
1382         return get!T(decimal);
1383     }
1384 
1385     /++
1386     Params:
1387         serializer = serializer
1388     +/
1389     void serialize(S)(scope ref S serializer) scope const @safe
1390     {
1391         Decimal!128 decimal = void;
1392         if (auto error = this.get(decimal))
1393             throw error.ionException;
1394         serializer.putValue(decimal);
1395     }
1396 }
1397 
1398 /++
1399 Ion described decimal number.
1400 +/
1401 struct IonDecimal
1402 {
1403     ///
1404     const(ubyte)[] data;
1405 
1406     /++
1407     Describes decimal (nothrow version).
1408     Params:
1409         value = (out) $(LREF IonDescribedDecimal)
1410     Returns: $(SUBREF exception, IonErrorCode)
1411     +/
1412     deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1413     IonErrorCode get(T : IonDescribedDecimal)(scope ref T value)
1414         @safe pure nothrow @nogc const
1415     {
1416         const(ubyte)[] d = data;
1417         if (data.length)
1418         {
1419             if (auto error = parseVarInt(d, value.exponent))
1420                 return error;
1421             value.coefficient = IonIntField(d);
1422         }
1423         else
1424         {
1425             value = T.init;
1426         }
1427         return IonErrorCode.none;
1428     }
1429 
1430     IonDescribedDecimal get(T : IonDescribedDecimal)(scope out IonErrorCode error)
1431         @safe pure nothrow @nogc const return scope
1432     {
1433         const(ubyte)[] d = data;
1434         if (d.length)
1435         {
1436             IonDescribedDecimal value;
1437             error = parseVarInt(d, value.exponent);
1438             if (error)
1439                 return IonDescribedDecimal.init;
1440             value.coefficient = IonIntField(d);
1441             return value;
1442         }
1443         else
1444         {
1445             return IonDescribedDecimal.init;
1446         }
1447     }
1448 
1449     /++
1450     Params:
1451         value = (out) floating point number
1452     Returns: $(SUBREF exception, IonErrorCode)
1453     +/
1454     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1455     IonErrorCode get(T)(scope ref T value)
1456         @safe pure nothrow @nogc scope const
1457         if (isFloatingPoint!T)
1458     {
1459         IonErrorCode error;
1460         auto decimal = get!IonDescribedDecimal(error);
1461         if (error)
1462             return error;
1463         return decimal.get(value);
1464     }
1465 
1466     version (D_Exceptions)
1467     {
1468         /++
1469         Describes decimal.
1470         Returns: $(LREF IonDescribedDecimal)
1471         +/
1472         T get(T = IonDescribedDecimal)() return scope
1473             @safe pure @nogc const
1474         {
1475             static if (isFloatingPoint!T)
1476             {
1477                 T ret;
1478                 if (auto error = get(ret))
1479                     throw error.ionException;
1480                 return ret;
1481             }
1482             else
1483             {
1484                 IonErrorCode error;
1485                 auto ret = get!T(error);
1486                 if (error)
1487                     throw error.ionException;
1488                 return ret;
1489             }
1490         }
1491     }
1492 
1493     /++
1494     Returns: $(SUBREF exception, IonErrorCode)
1495     +/
1496     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1497     IonErrorCode getErrorCode(T = IonDescribedDecimal)()
1498         @trusted pure nothrow @nogc const
1499     {
1500         T value;
1501         return get!T(value);
1502     }
1503 
1504     /++
1505     Params:
1506         serializer = serializer
1507     +/
1508     void serialize(S)(scope ref S serializer) scope const @safe
1509     {
1510         this.get!IonDescribedDecimal.serialize(serializer);
1511     }
1512 }
1513 
1514 ///
1515 @safe pure
1516 version(mir_ion_test) unittest
1517 {
1518     import mir.test;
1519     // null.decimal
1520     assert(IonValue([0x5F]).describe.get!IonNull == IonNull(IonTypeCode.decimal));
1521 
1522     auto describedDecimal = IonValue([0x56, 0x50, 0xcb, 0x80, 0xbc, 0x2d, 0x86]).describe.get!IonDecimal.get;
1523     assert(describedDecimal.exponent == -2123);
1524     assert(describedDecimal.coefficient.get!int == -12332422);
1525 
1526     describedDecimal = IonValue([0x56, 0x00, 0xcb, 0x80, 0xbc, 0x2d, 0x86]).describe.get!IonDecimal.get;
1527     describedDecimal.get!double.should == -12332422e75;
1528 
1529     assert(IonValue([0x50]).describe.get!IonDecimal.get!double == 0);
1530     assert(IonValue([0x51, 0x83]).describe.get!IonDecimal.get!double == 0);
1531     assert(IonValue([0x53, 0xc3, 0xb0, 0x39]).describe.get!IonDecimal.get!double == -12.345);
1532 }
1533 
1534 /++
1535 Ion Timestamp
1536 
1537 Timestamp representations have 7 components, where 5 of these components are optional depending on the precision of the timestamp.
1538 The 2 non-optional components are offset and year.
1539 The 5 optional components are (from least precise to most precise): `month`, `day`, `hour` and `minute`, `second`, `fraction_exponent` and `fraction_coefficient`.
1540 All of these 7 components are in Universal Coordinated Time (UTC).
1541 +/
1542 struct IonTimestamp
1543 {
1544     import mir.timestamp;
1545 
1546     ///
1547     const(ubyte)[] data;
1548 
1549     /++
1550     Describes decimal (nothrow version).
1551     Params:
1552         value = (out) $(AlgorithmREF timestamp, Timestamp)
1553     Returns: $(SUBREF exception, IonErrorCode)
1554     +/
1555     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1556     IonErrorCode get(T : Timestamp)(scope ref T value)
1557         @safe pure nothrow @nogc scope const
1558     {
1559         pragma(inline, false);
1560         auto d = data[];
1561         Timestamp v;
1562         bool offsetSign;
1563         ushort offset;
1564         if (auto error = parseVarInt(d, offset, offsetSign))
1565             return error;
1566         if (offset || !offsetSign)
1567             v.offset = offsetSign ? cast(short)(0-cast(short)offset) : offset;
1568         ushort year;
1569         if (auto error = parseVarUInt(d, year))
1570             return error;
1571         v.year = year;
1572 
1573         if (d.length == 0)
1574             goto R;
1575         if (auto error = parseVarUInt(d, v.month))
1576             return error;
1577         v.precision = v.precision.month;
1578 
1579         if (d.length == 0)
1580             goto R;
1581         if (auto error = parseVarUInt(d, v.day))
1582             return error;
1583         v.precision = v.precision.day;
1584 
1585         if (d.length == 0)
1586             goto R;
1587         if (auto error = parseVarUInt(d, v.hour))
1588             return error;
1589         if (v.hour >= 24)
1590             return IonErrorCode.illegalTimeStamp;
1591         {            
1592             typeof(v.minute) minute;
1593             if (auto error = parseVarUInt(d, minute))
1594                 return error;
1595             if (v.minute >= 60)
1596                 return IonErrorCode.illegalTimeStamp;
1597             v.minute = minute;
1598         }
1599         v.precision = v.precision.minute;
1600 
1601         if (d.length == 0)
1602             goto R;
1603         {
1604             typeof(v.second) second;
1605             if (auto error = parseVarUInt(d, second))
1606                 return error;
1607             if (v.second >= 60)
1608                 return IonErrorCode.illegalTimeStamp;
1609             v.second = second;
1610         }
1611         v.precision = v.precision.second;
1612 
1613         if (d.length == 0)
1614             goto R;
1615         {
1616             typeof(v.fractionExponent) fractionExponent;
1617             long fractionCoefficient;
1618             if (auto error = parseVarInt(d, fractionExponent))
1619                 return error;
1620             if (auto error = IonIntField(d).get(fractionCoefficient))
1621                 return error;
1622             if (fractionCoefficient == 0 && fractionExponent >= 0)
1623                 goto R;
1624             static immutable exps = [
1625                 1L,
1626                 10L,
1627                 100L,
1628                 1_000L,
1629                 10_000L,
1630                 100_000L,
1631                 1_000_000L,
1632                 10_000_000L,
1633                 100_000_000L,
1634                 1_000_000_000L,
1635                 10_000_000_000L,
1636                 100_000_000_000L,
1637                 1_000_000_000_000L,
1638             ];
1639             if (fractionExponent < -12
1640              || fractionExponent > 0
1641              || fractionCoefficient < 0
1642              || fractionCoefficient >= exps[0-fractionExponent])
1643                 return IonErrorCode.illegalTimeStamp;
1644             v.fractionExponent = fractionExponent;
1645             v.fractionCoefficient = fractionCoefficient;
1646         }
1647         v.precision = v.precision.fraction;
1648     R:
1649         import mir.date: maxDay;
1650 
1651         // duration
1652         if (v.day == 88 || v.day == 99)
1653         {
1654         }
1655         else
1656         // time of day
1657         if (v.precision > v.Precision.day && v.day == 0)
1658         {
1659             if (v.year || v.month)
1660                 return IonErrorCode.illegalTimeStamp;
1661         }
1662         else
1663         if (!v.year)
1664         {
1665             return IonErrorCode.illegalTimeStamp;
1666         }
1667         else
1668         if (v.precision >= Timestamp.Precision.month)
1669         {
1670             if (v.month == 0 || v.month > 12)
1671                 return IonErrorCode.illegalTimeStamp;
1672             if (v.precision > Timestamp.Precision.month && (v.day == 0 || v.day > maxDay(v.year, v.month)))
1673                 return IonErrorCode.illegalTimeStamp;
1674         }
1675 
1676         value = v;
1677         return IonErrorCode.none;
1678     }
1679 
1680     version (D_Exceptions)
1681     {
1682         /++
1683         Describes decimal.
1684         Returns: $(LREF Timestamp)
1685         +/
1686         Timestamp get(T = Timestamp)()
1687             @safe pure @nogc const
1688         {
1689             T ret;
1690             if (auto error = get(ret))
1691                 throw error.ionException;
1692             return ret;
1693         }
1694     }
1695 
1696     /++
1697     Returns: $(SUBREF exception, IonErrorCode)
1698     +/
1699     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1700     IonErrorCode getErrorCode(T = Timestamp)()
1701         @trusted pure nothrow @nogc const
1702     {
1703         T value;
1704         return get!T(value);
1705     }
1706 
1707     /++
1708     Params:
1709         serializer = serializer
1710     +/
1711     void serialize(S)(scope ref S serializer) scope const
1712     {
1713         serializer.putValue(this.get!Timestamp);
1714     }
1715 }
1716 
1717 ///
1718 @safe pure
1719 version(mir_ion_test) unittest
1720 {
1721     import mir.timestamp;
1722 
1723     // null.timestamp
1724     assert(IonValue([0x6F]).describe.get!IonNull == IonNull(IonTypeCode.timestamp));
1725 
1726     ubyte[][] set = [
1727         [0x68, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84,         ], // 2000-07-08T02:03:04Z with no fractional seconds
1728         [0x69, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0x80,   ], // The same instant with 0d0 fractional seconds and implicit zero coefficient
1729         [0x6A, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0x80, 00], // The same instant with 0d0 fractional seconds and explicit zero coefficient
1730         [0x69, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0xC0,   ], // The same instant with 0d-0 fractional seconds
1731         [0x69, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0x81,   ], // The same instant with 0d1 fractional seconds
1732     ];
1733 
1734     auto r = Timestamp(2000, 7, 8, 2, 3, 4).withOffset(0);
1735 
1736     foreach(data; set)
1737     {
1738         assert(IonValue(data).describe.get!IonTimestamp.get == r);
1739     }
1740 
1741     assert(IonValue([0x69, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0xC2])
1742         .describe
1743         .get!IonTimestamp
1744         .get ==
1745             Timestamp(2000, 7, 8, 2, 3, 4, -2, 0).withOffset(0));
1746 
1747     assert(IonValue([0x6A, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0xC3, 0x10])
1748         .describe
1749         .get!IonTimestamp
1750         .get ==
1751             Timestamp(2000, 7, 8, 2, 3, 4, -3, 16).withOffset(0));
1752 }
1753 
1754 /++
1755 Ion Symbol Id
1756 
1757 In the binary encoding, all Ion symbols are stored as integer symbol IDs whose text values are provided by a symbol table.
1758 If L is zero then the symbol ID is zero and the length and symbol ID fields are omitted.
1759 +/
1760 struct IonSymbolID
1761 {
1762     ///
1763     const(ubyte)[] data;
1764 
1765     /++
1766     Params:
1767         value = (out) symbol id
1768     Returns: $(SUBREF exception, IonErrorCode)
1769     +/
1770  
1771     // IonErrorCode get(T)(scope ref T value)
1772     //     @trusted pure nothrow @nogc const
1773     //     if (isUnsigned!T)
1774     // {
1775     //     pragma(inline, true);
1776 
1777     //     auto d = data[];
1778 
1779     //     value = 0;
1780     //     while (d.length)
1781     //     {
1782     //         enum c = T.max >> 8;
1783     //         if (value > c)
1784     //             return IonErrorCode.overflowInIntegerValue;
1785     //         value <<= 8;
1786     //         value |= d[0];
1787     //         d = d[1 .. $];
1788     //     }
1789     //     return IonErrorCode.none;
1790     // }
1791 
1792      IonErrorCode get(T)(scope ref T value)
1793         @safe pure nothrow @nogc scope const
1794         if (isUnsigned!T && T.sizeof >= 4)
1795     {
1796         auto d = data[];
1797 
1798         while (_expect(d.length && d[0] == 0, false))
1799             d = d[1 .. $];
1800 
1801         if (_expect(d.length > value.sizeof, false))
1802             return IonErrorCode.overflowInIntegerValue;
1803 
1804         value = 0;
1805 
1806         while (d.length)
1807         {
1808             value <<= 8;
1809             value |= d[0];
1810             d = d[1 .. $];
1811         }
1812 
1813         return IonErrorCode.none;
1814     }
1815 
1816     /// ditto
1817     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1818     IonErrorCode get(T)(scope ref T value)
1819         @trusted pure nothrow @nogc scope const
1820         if (isUnsigned!T && T.sizeof < 4)
1821     {
1822         uint ext;
1823         if (auto error = this.get(ext))
1824             return error;
1825         if (cast(T)ext != ext)
1826             return IonErrorCode.overflowInIntegerValue;
1827         value = cast(T)ext;
1828         return IonErrorCode.none;
1829     }
1830 
1831     /++
1832     Returns: unsigned or signed integer
1833     +/
1834     T get(T = size_t)()
1835         @safe pure @nogc const
1836         if (isUnsigned!T)
1837     {
1838         T ret;
1839         if (auto error = get(ret))
1840             throw error.ionException;
1841         return ret;
1842     }
1843 
1844     /++
1845     Returns: $(SUBREF exception, IonErrorCode)
1846     +/
1847     // deprecated("Use inout(T) get(T)(scope out IonErrorCode)")
1848     IonErrorCode getErrorCode(T = size_t)()
1849         @trusted pure nothrow @nogc const
1850         if (isUnsigned!T)
1851     {
1852         T value;
1853         return get!T(value);
1854     }
1855 
1856     /++
1857     Serializes SymbolId as Ion value.
1858     Note: This serialization shouldn't be used for `struct` keys or `annotation` list.
1859     Params:
1860         serializer = serializer with `putSymbolId` primitive.
1861     +/
1862     void serialize(S)(scope ref S serializer) scope const
1863     {
1864         size_t id;
1865         if (auto err = get(id))
1866             throw err.ionException;
1867         serializer.putSymbolId(id);
1868     }
1869 }
1870 
1871 ///
1872 @safe pure
1873 version(mir_ion_test) unittest
1874 {
1875     import mir.test;
1876     assert(IonValue([0x7F]).describe.get!IonNull == IonNull(IonTypeCode.symbol));
1877     assert(IonValue([0x71, 0x07]).describe.get!IonSymbolID.get == 7);
1878 
1879     size_t v;
1880     assert(IonValue([0x72, 0x01, 0x04]).describe.get!IonSymbolID.get(v) == IonErrorCode.none);
1881     v.should == 260;
1882 }
1883 
1884 @safe pure
1885 version(mir_ion_test) unittest
1886 {
1887     assert(IonValue([0x70]).describe.get!IonSymbolID.getErrorCode == 0);
1888     assert(IonValue([0x71, 0x00]).describe.get!IonSymbolID.getErrorCode == 0);
1889 
1890     assert(IonValue([0x71, 0x07]).describe.get!IonSymbolID.get == 7);
1891     assert(IonValue([0x7E, 0x81, 0x07]).describe.get!IonSymbolID.get == 7);
1892     assert(IonValue([0x7A, 0,0,0, 0,0,0, 0,0,0, 0x07]).describe.get!IonSymbolID.get == 7);
1893 
1894     assert(IonValue([0x71, 0xFF]).describe.get!IonSymbolID.get!ubyte == ubyte.max);
1895     assert(IonValue([0x72, 0xFF, 0xFF]).describe.get!IonSymbolID.get!ushort == ushort.max);
1896     assert(IonValue([0x74, 0xFF, 0xFF,0xFF,0xFF]).describe.get!IonSymbolID.get!uint == uint.max);
1897     assert(IonValue([0x78, 0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF]).describe.get!IonSymbolID.get!ulong == ulong.max);
1898     assert(IonValue([0x7A, 0,0, 0xFF, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF]).describe.get!IonSymbolID.get!ulong == ulong.max);
1899 
1900     assert(IonValue([0x72, 1, 0]).describe.get!IonSymbolID.getErrorCode!ubyte == IonErrorCode.overflowInIntegerValue);
1901     assert(IonValue([0x73, 1, 0,0]).describe.get!IonSymbolID.getErrorCode!ushort == IonErrorCode.overflowInIntegerValue);
1902     assert(IonValue([0x75, 1, 0,0,0,0]).describe.get!IonSymbolID.getErrorCode!uint == IonErrorCode.overflowInIntegerValue);
1903     assert(IonValue([0x79, 1, 0,0,0,0,0,0,0,0]).describe.get!IonSymbolID.getErrorCode!ulong == IonErrorCode.overflowInIntegerValue);
1904 }
1905 
1906 
1907 ///
1908 @safe pure
1909 version(mir_ion_test) unittest
1910 {
1911     // null.string
1912     assert(IonValue([0x8F]).describe.get!IonNull == IonNull(IonTypeCode..string));
1913     // empty string
1914     assert(IonValue([0x80]).describe.get!(const(char)[]) !is null);
1915     assert(IonValue([0x80]).describe.get!(const(char)[]) == "");
1916 
1917     assert(IonValue([0x85, 0x63, 0x6f, 0x76, 0x69, 0x64]).describe.get!(const(char)[]) == "covid");
1918 }
1919 
1920 /++
1921 Ion List (array)
1922 +/
1923 struct IonList
1924 {
1925     ///
1926     const(ubyte)[] data;
1927     private alias DG = int delegate(IonErrorCode error, scope IonDescribedValue value) @safe pure nothrow @nogc;
1928     private alias EDG = int delegate(scope IonDescribedValue value) @safe pure @nogc;
1929 
1930     /++
1931     Returns: true if the sexp is `null.sexp`, `null`, or `()`.
1932     Note: a NOP padding makes in the struct non-empty.
1933     +/
1934     bool empty()
1935         scope @safe pure nothrow @nogc const @property
1936     {
1937         return data.length == 0;
1938     }
1939 
1940 const:
1941 
1942     version (D_Exceptions)
1943     {
1944         /++
1945         +/
1946         @safe pure @nogc
1947         scope int opApply(scope int delegate(scope IonDescribedValue value) @safe pure @nogc dg)
1948         {
1949             return opApply((IonErrorCode error, scope IonDescribedValue value) {
1950                 if (_expect(error, false))
1951                     throw error.ionException;
1952                 return dg(value);
1953             });
1954         }
1955 
1956         /// ditto
1957         @trusted @nogc
1958         scope int opApply(scope int delegate(scope IonDescribedValue value)
1959         @safe @nogc dg) { return opApply(cast(EDG) dg); }
1960 
1961         /// ditto
1962         @trusted pure
1963         scope int opApply(scope int delegate(scope IonDescribedValue value)
1964         @safe pure dg) { return opApply(cast(EDG) dg); }
1965 
1966         /// ditto
1967         @trusted
1968         scope int opApply(scope int delegate(scope IonDescribedValue value)
1969         @safe dg) { return opApply(cast(EDG) dg); }
1970 
1971         /// ditto
1972         @system pure @nogc
1973         scope int opApply(scope int delegate(scope IonDescribedValue value)
1974         @system pure @nogc dg) { return opApply(cast(EDG) dg); }
1975 
1976         /// ditto
1977         @system @nogc
1978         scope int opApply(scope int delegate(scope IonDescribedValue value)
1979         @system @nogc dg) { return opApply(cast(EDG) dg); }
1980 
1981         /// ditto
1982         @system pure
1983         scope int opApply(scope int delegate(scope IonDescribedValue value)
1984         @system pure dg) { return opApply(cast(EDG) dg); }
1985 
1986         /// ditto
1987         @system
1988         scope int opApply(scope int delegate(scope IonDescribedValue value)
1989         @system dg) { return opApply(cast(EDG) dg); }
1990     }
1991 
1992     /++
1993     +/
1994     @safe pure nothrow @nogc
1995     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value) @safe pure nothrow @nogc dg)
1996     {
1997         auto d = data[];
1998         while (d.length)
1999         {
2000             IonErrorCode error;
2001             auto describedValue = parseValue(d, error);
2002             if (error == IonErrorCode.nop)
2003                 continue;
2004             if (auto ret = dg(error, describedValue))
2005                 return ret;
2006             assert(!error, "User provided delegate MUST break the iteration when error has non-zero value.");
2007         }
2008         return 0;
2009     }
2010 
2011     /// ditto
2012     @trusted nothrow @nogc
2013     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2014     @safe nothrow @nogc dg) { return opApply(cast(DG) dg); }
2015 
2016     /// ditto
2017     @trusted pure @nogc
2018     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2019     @safe pure @nogc dg) { return opApply(cast(DG) dg); }
2020 
2021     /// ditto
2022     @trusted pure nothrow
2023     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2024     @safe pure nothrow dg) { return opApply(cast(DG) dg); }
2025 
2026     /// ditto
2027     @trusted @nogc
2028     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2029     @safe @nogc dg) { return opApply(cast(DG) dg); }
2030 
2031     /// ditto
2032     @trusted pure
2033     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2034     @safe pure dg) { return opApply(cast(DG) dg); }
2035 
2036     /// ditto
2037     @trusted nothrow
2038     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2039     @safe nothrow dg) { return opApply(cast(DG) dg); }
2040 
2041     /// ditto
2042     @trusted
2043     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2044     @safe dg) { return opApply(cast(DG) dg); }
2045 
2046     /// ditto
2047     @system pure nothrow @nogc
2048     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2049     @system pure nothrow @nogc dg) { return opApply(cast(DG) dg); }
2050 
2051     /// ditto
2052     @system nothrow @nogc
2053     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2054     @system nothrow @nogc dg) { return opApply(cast(DG) dg); }
2055 
2056     /// ditto
2057     @system pure @nogc
2058     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2059     @system pure @nogc dg) { return opApply(cast(DG) dg); }
2060 
2061     /// ditto
2062     @system pure nothrow
2063     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2064     @system pure nothrow dg) { return opApply(cast(DG) dg); }
2065 
2066     /// ditto
2067     @system @nogc
2068     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2069     @system @nogc dg) { return opApply(cast(DG) dg); }
2070 
2071     /// ditto
2072     @system pure
2073     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2074     @system pure dg) { return opApply(cast(DG) dg); }
2075 
2076     /// ditto
2077     @system nothrow
2078     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2079     @system nothrow dg) { return opApply(cast(DG) dg); }
2080 
2081     /// ditto
2082     @system
2083     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2084     @system dg) { return opApply(cast(DG) dg); }
2085 
2086     /++
2087     For iteration with $(LREF IonDescribedValue) elements.
2088     Throws: `IonException`
2089     +/
2090     version(GenericOpApply)
2091     scope int opApply(Dg)(scope Dg dg)
2092         if (ParameterTypeTuple!Dg.length == 1)
2093     {
2094         foreach (IonErrorCode error, scope IonDescribedValue value; this)
2095         {
2096             if (_expect(error, false))
2097                 throw error.ionException;
2098             if (auto ret = dg(value))
2099                 return ret;
2100         }
2101         return 0;
2102     }
2103 
2104     /++
2105     For iteration with `IonErrorCode` and $(LREF IonDescribedValue) pairs.
2106     +/
2107     version(GenericOpApply)
2108     scope int opApply(Dg)(scope Dg dg)
2109         if (ParameterTypeTuple!Dg.length == 2)
2110     {
2111         version(LDC)
2112             pragma(inline, true);
2113         pragma (msg, ParameterTypeTuple!Dg); // foreach with 2 parameters
2114         auto d = data[];
2115         while (d.length)
2116         {
2117             IonDescribedValue describedValue;
2118             auto error = parseValue(d, describedValue);
2119             if (error == IonErrorCode.nop)
2120                 continue;
2121             if (auto ret = dg(error, describedValue))
2122                 return ret;
2123             assert(!error, "User provided delegate MUST break the iteration when error has non-zero value.");
2124         }
2125         return 0;
2126     }
2127 
2128     /++
2129     Params:
2130         serializer = serializer
2131     +/
2132     void serialize(S)(scope ref S serializer) scope const
2133     {
2134         import mir.ser: beginList;
2135         auto state = serializer.beginList(this);
2136         foreach (scope IonDescribedValue value; this)
2137         {
2138             serializer.elemBegin;
2139             value.serializeImpl(serializer);
2140             if (false)
2141                 value.serializeDummy(serializer);
2142         }
2143         serializer.listEnd(state);
2144     }
2145 
2146     ///
2147     size_t walkLength() scope const @property @safe pure @nogc
2148     {
2149         size_t length;
2150         IonErrorCode firstError;
2151         foreach (IonErrorCode error, scope IonDescribedValue value; this)
2152         {
2153             length++;
2154             if (error)
2155             {
2156                 firstError = error;
2157                 break;
2158             }
2159         }
2160         if (firstError)
2161             throw firstError.ionException;
2162         return length;
2163     }
2164 }
2165 
2166 ///
2167 version(mir_ion_test) unittest
2168 {
2169     // check parsing with NOP padding:
2170     // [NOP, int, NOP, double, NOP]
2171     auto list = IonValue([0xbe, 0x91, 0x00, 0x00, 0x21, 0x0c, 0x00, 0x00, 0x48, 0x43, 0x0c, 0x6b, 0xf5, 0x26, 0x34, 0x00, 0x00, 0x00, 0x00])
2172         .describe.get!IonList;
2173     size_t i;
2174     foreach (elem; list)
2175     {
2176         if (i == 0)
2177             assert(elem.get!IonUInt.get!int == 12);
2178         if (i == 1)
2179             assert(elem.get!IonFloat.get!double == 100e13);
2180         i++;
2181     }
2182     assert(i == 2);
2183 }
2184 
2185 /++
2186 Ion Sexp (symbol expression, array)
2187 +/
2188 struct IonSexp
2189 {
2190     /// data view.
2191     const(ubyte)[] data;
2192 
2193     private alias DG = IonList.DG;
2194     private alias EDG = IonList.EDG;
2195 
2196     /++
2197     Returns: true if the sexp is `null.sexp`, `null`, or `()`.
2198     Note: a NOP padding makes in the struct makes it non-empty.
2199     +/
2200     bool empty()
2201         scope @safe pure nothrow @nogc const @property
2202     {
2203         return data.length == 0;
2204     }
2205 
2206 const:
2207 
2208     version (D_Exceptions)
2209     {
2210         /++
2211         +/
2212         @safe pure @nogc
2213         scope int opApply(scope int delegate(scope IonDescribedValue value) @safe pure @nogc dg)
2214         {
2215             return IonList(data).opApply(dg);
2216         }
2217 
2218         /// ditto
2219         @trusted @nogc
2220         scope int opApply(scope int delegate(scope IonDescribedValue value)
2221         @safe @nogc dg) { return opApply(cast(EDG) dg); }
2222 
2223         /// ditto
2224         @trusted pure
2225         scope int opApply(scope int delegate(scope IonDescribedValue value)
2226         @safe pure dg) { return opApply(cast(EDG) dg); }
2227 
2228         /// ditto
2229         @trusted
2230         scope int opApply(scope int delegate(scope IonDescribedValue value)
2231         @safe dg) { return opApply(cast(EDG) dg); }
2232 
2233         /// ditto
2234         @system pure @nogc
2235         scope int opApply(scope int delegate(scope IonDescribedValue value)
2236         @system pure @nogc dg) { return opApply(cast(EDG) dg); }
2237 
2238         /// ditto
2239         @system @nogc
2240         scope int opApply(scope int delegate(scope IonDescribedValue value)
2241         @system @nogc dg) { return opApply(cast(EDG) dg); }
2242 
2243         /// ditto
2244         @system pure
2245         scope int opApply(scope int delegate(scope IonDescribedValue value)
2246         @system pure dg) { return opApply(cast(EDG) dg); }
2247 
2248         /// ditto
2249         @system
2250         scope int opApply(scope int delegate(scope IonDescribedValue value)
2251         @system dg) { return opApply(cast(EDG) dg); }
2252     }
2253 
2254     /++
2255     +/
2256     @safe pure nothrow @nogc
2257     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value) @safe pure nothrow @nogc dg)
2258     {
2259         return IonList(data).opApply(dg);
2260     }
2261 
2262     /// ditto
2263     @trusted nothrow @nogc
2264     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2265     @safe nothrow @nogc dg) { return opApply(cast(DG) dg); }
2266 
2267     /// ditto
2268     @trusted pure @nogc
2269     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2270     @safe pure @nogc dg) { return opApply(cast(DG) dg); }
2271 
2272     /// ditto
2273     @trusted pure nothrow
2274     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2275     @safe pure nothrow dg) { return opApply(cast(DG) dg); }
2276 
2277     /// ditto
2278     @trusted @nogc
2279     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2280     @safe @nogc dg) { return opApply(cast(DG) dg); }
2281 
2282     /// ditto
2283     @trusted pure
2284     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2285     @safe pure dg) { return opApply(cast(DG) dg); }
2286 
2287     /// ditto
2288     @trusted nothrow
2289     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2290     @safe nothrow dg) { return opApply(cast(DG) dg); }
2291 
2292     /// ditto
2293     @trusted
2294     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2295     @safe dg) { return opApply(cast(DG) dg); }
2296 
2297     /// ditto
2298     @system pure nothrow @nogc
2299     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2300     @system pure nothrow @nogc dg) { return opApply(cast(DG) dg); }
2301 
2302     /// ditto
2303     @system nothrow @nogc
2304     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2305     @system nothrow @nogc dg) { return opApply(cast(DG) dg); }
2306 
2307     /// ditto
2308     @system pure @nogc
2309     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2310     @system pure @nogc dg) { return opApply(cast(DG) dg); }
2311 
2312     /// ditto
2313     @system pure nothrow
2314     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2315     @system pure nothrow dg) { return opApply(cast(DG) dg); }
2316 
2317     /// ditto
2318     @system @nogc
2319     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2320     @system @nogc dg) { return opApply(cast(DG) dg); }
2321 
2322     /// ditto
2323     @system pure
2324     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2325     @system pure dg) { return opApply(cast(DG) dg); }
2326 
2327     /// ditto
2328     @system nothrow
2329     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2330     @system nothrow dg) { return opApply(cast(DG) dg); }
2331 
2332     /// ditto
2333     @system
2334     scope int opApply(scope int delegate(IonErrorCode error, scope IonDescribedValue value)
2335     @system dg) { return opApply(cast(DG) dg); }
2336 
2337     /++
2338     For iteration with $(LREF IonDescribedValue) elements.
2339     Throws: `IonException`
2340     +/
2341     version(GenericOpApply)
2342     scope int opApply(Dg)(scope Dg dg)
2343         if (ParameterTypeTuple!Dg.length == 1)
2344     {
2345         foreach (IonErrorCode error, scope IonDescribedValue value; this)
2346         {
2347             if (_expect(error, false))
2348                 throw error.ionException;
2349             if (auto ret = dg(value))
2350                 return ret;
2351         }
2352         return 0;
2353     }
2354 
2355     /++
2356     For iteration with `IonErrorCode` and $(LREF IonDescribedValue) pairs.
2357     +/
2358     version(GenericOpApply)
2359     scope int opApply(Dg)(scope Dg dg)
2360         if (ParameterTypeTuple!Dg.length == 2)
2361     {
2362         version(LDC)
2363             pragma(inline, true);
2364         pragma (msg, ParameterTypeTuple!Dg); // foreach with 2 parameters
2365         auto d = data[];
2366         while (d.length)
2367         {
2368             IonDescribedValue describedValue;
2369             auto error = parseValue(d, describedValue);
2370             if (error == IonErrorCode.nop)
2371                 continue;
2372             if (auto ret = dg(error, describedValue))
2373                 return ret;
2374             assert(!error, "User provided delegate MUST break the iteration when error has non-zero value.");
2375         }
2376         return 0;
2377     }
2378 
2379     /++
2380     Params:
2381         serializer = serializer
2382     +/
2383     void serialize(S)(scope ref S serializer) scope const
2384     {
2385         import mir.ser: beginSexp;
2386         auto state = serializer.beginSexp(this);
2387         foreach (scope IonDescribedValue value; this)
2388         {
2389             serializer.sexpElemBegin;
2390             value.serializeImpl(serializer);
2391             if (false)
2392                 value.serializeDummy(serializer);
2393         }
2394         serializer.sexpEnd(state);
2395     }
2396 
2397     ///
2398     size_t walkLength() scope const @property @safe pure @nogc
2399     {
2400         size_t length;
2401         IonErrorCode firstError;
2402         foreach (IonErrorCode error, scope IonDescribedValue value; this)
2403         {
2404             length++;
2405             if (error)
2406             {
2407                 firstError = error;
2408                 break;
2409             }
2410         }
2411         if (firstError)
2412             throw firstError.ionException;
2413         return length;
2414     }
2415 }
2416 
2417 ///
2418 version(mir_ion_test) unittest
2419 {
2420     // check parsing with NOP padding:
2421     // (NOP int NOP double NOP)
2422     auto list = IonValue([0xce, 0x91, 0x00, 0x00, 0x21, 0x0c, 0x00, 0x00, 0x48, 0x43, 0x0c, 0x6b, 0xf5, 0x26, 0x34, 0x00, 0x00, 0x00, 0x00])
2423         .describe.get!IonSexp;
2424     size_t i;
2425     foreach (elem; list)
2426     {
2427         if (i == 0)
2428             assert(elem.get!IonUInt.get!int == 12);
2429         if (i == 1)
2430             assert(elem.get!IonFloat.get!double == 100e13);
2431         i++;
2432     }
2433     assert(i == 2);
2434 }
2435 
2436 /++
2437 $(LERF IonDescribedValue), $(SUBREF exception, IonErrorCode) and symbol id triplet used in the $(LREF IonList) and $(LREF IonSexp).
2438 +/
2439 struct IonElementWithId
2440 {
2441     /// Ion described value
2442     IonDescribedValue value;
2443     /// Error code
2444     IonErrorCode error;
2445     /// Symbol ID
2446     size_t id;
2447 }
2448 
2449 /++
2450 $(LERF IonDescribedValue) and $(SUBREF exception, IonErrorCode) pair used in the $(LREF IonList) and $(LREF IonSexp)/
2451 +/
2452 struct IonElement
2453 {
2454     /// Ion described value
2455     IonDescribedValue value;
2456     /// Error code
2457     IonErrorCode error;
2458 }
2459 
2460 /++
2461 Ion struct (object)
2462 +/
2463 struct IonStruct
2464 {
2465     ///
2466     IonDescriptor descriptor;
2467     ///
2468     const(ubyte)[] data;
2469 
2470     private alias DG = int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value) @safe pure nothrow @nogc;
2471     private alias EDG = int delegate(size_t symbolID, scope IonDescribedValue value) @safe pure @nogc;
2472 
2473     ///
2474     bool sorted()
2475         @safe pure nothrow @nogc const @property
2476     {
2477         return descriptor.L == 1;
2478     }
2479 
2480     /++
2481     Returns: true if the struct is `null.struct`, `null`, or `()`.
2482     Note: a NOP padding makes in the struct makes it non-empty.
2483     +/
2484     bool empty()
2485         scope @safe pure nothrow @nogc const @property
2486     {
2487         return data.length == 0;
2488     }
2489 
2490 const:
2491 
2492     version (D_Exceptions)
2493     {
2494         /++
2495         +/
2496         @safe pure @nogc
2497         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value) @safe pure @nogc dg)
2498         {
2499             return opApply((IonErrorCode error, size_t symbolID, scope IonDescribedValue value) {
2500                 if (_expect(error, false))
2501                     throw error.ionException;
2502                 return dg(symbolID, value);
2503             });
2504         }
2505 
2506         /// ditto
2507         @trusted @nogc
2508         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2509         @safe @nogc dg) { return opApply(cast(EDG) dg); }
2510 
2511         /// ditto
2512         @trusted pure
2513         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2514         @safe pure dg) { return opApply(cast(EDG) dg); }
2515 
2516         /// ditto
2517         @trusted
2518         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2519         @safe dg) { return opApply(cast(EDG) dg); }
2520 
2521         /// ditto
2522         @system pure @nogc
2523         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2524         @system pure @nogc dg) { return opApply(cast(EDG) dg); }
2525 
2526         /// ditto
2527         @system @nogc
2528         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2529         @system @nogc dg) { return opApply(cast(EDG) dg); }
2530 
2531         /// ditto
2532         @system pure
2533         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2534         @system pure dg) { return opApply(cast(EDG) dg); }
2535 
2536         /// ditto
2537         @system
2538         scope int opApply(scope int delegate(size_t symbolID, scope IonDescribedValue value)
2539         @system dg) { return opApply(cast(EDG) dg); }
2540     }
2541 
2542     /++
2543     +/
2544     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value) @safe pure nothrow @nogc dg)
2545         @safe pure nothrow @nogc
2546     {
2547         auto d = data[];
2548         while (d.length)
2549         {
2550             size_t symbolID;
2551             IonDescribedValue describedValue;
2552             auto error = parseVarUInt(d, symbolID);
2553             if (!error)
2554             {
2555                 describedValue = parseValue(d, error);
2556                 if (error == IonErrorCode.nop)
2557                     continue;
2558             }
2559             if (auto ret = dg(error, symbolID, describedValue))
2560                 return ret;
2561             assert(!error, "User provided delegate MUST break the iteration when error has non-zero value.");
2562         }
2563         return 0;
2564     }
2565 
2566     /// ditto
2567     @trusted nothrow @nogc
2568     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2569     @safe nothrow @nogc dg) { return opApply(cast(DG) dg); }
2570 
2571     /// ditto
2572     @trusted pure @nogc
2573     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2574     @safe pure @nogc dg) { return opApply(cast(DG) dg); }
2575 
2576     /// ditto
2577     @trusted pure nothrow
2578     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2579     @safe pure nothrow dg) { return opApply(cast(DG) dg); }
2580 
2581     /// ditto
2582     @trusted @nogc
2583     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2584     @safe @nogc dg) { return opApply(cast(DG) dg); }
2585 
2586     /// ditto
2587     @trusted pure
2588     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2589     @safe pure dg) { return opApply(cast(DG) dg); }
2590 
2591     /// ditto
2592     @trusted nothrow
2593     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2594     @safe nothrow dg) { return opApply(cast(DG) dg); }
2595 
2596     /// ditto
2597     @trusted
2598     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2599     @safe dg) { return opApply(cast(DG) dg); }
2600 
2601     /// ditto
2602     @system pure nothrow @nogc
2603     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2604     @system pure nothrow @nogc dg) { return opApply(cast(DG) dg); }
2605 
2606     /// ditto
2607     @system nothrow @nogc
2608     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2609     @system nothrow @nogc dg) { return opApply(cast(DG) dg); }
2610 
2611     /// ditto
2612     @system pure @nogc
2613     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2614     @system pure @nogc dg) { return opApply(cast(DG) dg); }
2615 
2616     /// ditto
2617     @system pure nothrow
2618     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2619     @system pure nothrow dg) { return opApply(cast(DG) dg); }
2620 
2621     /// ditto
2622     @system @nogc
2623     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2624     @system @nogc dg) { return opApply(cast(DG) dg); }
2625 
2626     /// ditto
2627     @system pure
2628     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2629     @system pure dg) { return opApply(cast(DG) dg); }
2630 
2631     /// ditto
2632     @system nothrow
2633     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2634     @system nothrow dg) { return opApply(cast(DG) dg); }
2635 
2636     /// ditto
2637     @system
2638     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID, scope IonDescribedValue value)
2639     @system dg) { return opApply(cast(DG) dg); }
2640 
2641     /++
2642     For iteration with $(LREF IonDescribedValue) pairs.
2643     Throws: `IonException`
2644     +/
2645     version(GenericOpApply)
2646     scope int opApply(Dg)(scope Dg dg)
2647         if (ParameterTypeTuple!Dg.length == 2)
2648     {
2649         foreach (IonErrorCode error, size_t symbolID, scope IonDescribedValue value; this)
2650         {
2651             if (_expect(error, false))
2652                 throw error.ionException;
2653             if (auto ret = dg(symbolID, value))
2654                 return ret;
2655         }
2656         return 0;
2657     }
2658 
2659     /++
2660     For iteration with `size_t` (symbol ID), `IonErrorCode`, and $(LREF IonDescribedValue) triplets.
2661     +/
2662     version(GenericOpApply)
2663     scope int opApply(Dg)(scope Dg dg)
2664         if (ParameterTypeTuple!Dg.length == 3)
2665     {
2666         version(LDC)
2667             pragma(inline, true);
2668         pragma (msg, ParameterTypeTuple!Dg); // foreach with 2 parameters
2669         auto d = data[];
2670         while (d.length)
2671         {
2672             size_t symbolID;
2673             IonDescribedValue describedValue;
2674             auto error = parseVarUInt(d, symbolID);
2675             if (!error)
2676             {
2677                 error = parseValue(d, describedValue);
2678                 if (error == IonErrorCode.nop)
2679                     continue;
2680             }
2681             import core.lifetime: move;
2682             if (auto ret = dg(move(error), move(symbolID), move(describedValue)))
2683                 return ret;
2684             assert(!error, "User provided delegate MUST break the iteration when error has non-zero value.");
2685         }
2686         return 0;
2687     }
2688 
2689     /++
2690     Params:
2691         serializer = serializer
2692     +/
2693     void serialize(S)(scope ref S serializer) scope const
2694     {
2695         import mir.ser: beginStruct;
2696         auto state = serializer.beginStruct(this);
2697         foreach (size_t symbolID, scope IonDescribedValue value; this)
2698         {
2699             serializer.putKeyId(symbolID);
2700             value.serializeImpl(serializer);
2701             if (false)
2702                 value.serializeDummy(serializer);
2703         }
2704         serializer.structEnd(state);
2705     }
2706 
2707     ///
2708     @safe pure nothrow @nogc
2709     IonStructWithSymbols withSymbols(return scope const(char[])[] symbolTable) return scope
2710     {
2711         return IonStructWithSymbols(this, symbolTable);
2712     }
2713 
2714     ///
2715     size_t walkLength(out IonErrorCode firstError) scope const @property @safe pure @nogc
2716     {
2717         size_t length;
2718         foreach (IonErrorCode error, size_t symbolID, scope IonDescribedValue value; this)
2719         {
2720             length++;
2721             if (error)
2722             {
2723                 firstError = error;
2724                 break;
2725             }
2726         }
2727         return length;
2728     }
2729 
2730     ///
2731     size_t walkLength() scope const @property @safe pure @nogc
2732     {
2733         IonErrorCode firstError;
2734         auto length = this.walkLength(firstError);
2735         if (firstError)
2736             throw firstError.ionException;
2737         return length;
2738     }
2739 }
2740 
2741 ///
2742 @safe pure
2743 version(mir_ion_test) unittest
2744 {
2745     // null.struct
2746     assert(IonValue([0xDF]).describe.get!IonNull == IonNull(IonTypeCode.struct_));
2747 
2748     // empty struct
2749     auto ionStruct = IonValue([0xD0]).describe.get!IonStruct;
2750     size_t i;
2751     foreach (symbolID, elem; ionStruct)
2752         i++;
2753     assert(i == 0);
2754 
2755     // added two 2-bytes NOP padings 0x8F 0x00
2756     ionStruct = IonValue([0xDE, 0x91, 0x8F, 0x00, 0x8A, 0x21, 0x0C, 0x8B, 0x48, 0x43, 0x0C, 0x6B, 0xF5, 0x26, 0x34, 0x00, 0x00, 0x8F, 0x00])
2757         .describe
2758         .get!IonStruct;
2759 
2760     foreach (symbolID, elem; ionStruct)
2761     {
2762         if (i == 0)
2763         {
2764             assert(symbolID == 10);
2765             assert(elem.get!IonUInt.get!int == 12);
2766         }
2767         if (i == 1)
2768         {
2769             assert(symbolID == 11);
2770             assert(elem.get!IonFloat.get!double == 100e13);
2771         }
2772         i++;
2773     }
2774     assert(i == 2);
2775 }
2776 
2777 
2778 /++
2779 Ion struct (object) with a symbol table
2780 +/
2781 struct IonStructWithSymbols
2782 {
2783     ///
2784     IonStruct ionStruct;
2785     ///
2786     const(char[])[] symbolTable;
2787 
2788     private alias DG = int delegate(IonErrorCode error, scope const(char)[], scope IonDescribedValue value) @safe pure nothrow @nogc;
2789     private alias EDG = int delegate(scope const(char)[], scope IonDescribedValue value) @safe pure @nogc;
2790 
2791     ///
2792     bool sorted()
2793         @safe pure nothrow @nogc const @property
2794     {
2795         return ionStruct.sorted;
2796     }
2797 
2798     /++
2799     Returns: true if the struct is `null.struct`, `null`, or `()`.
2800     Note: a NOP padding makes in the struct makes it non-empty.
2801     +/
2802     bool empty()
2803         scope @safe pure nothrow @nogc const @property
2804     {
2805         return ionStruct.empty;
2806     }
2807 
2808 const:
2809 
2810     version (D_Exceptions)
2811     {
2812         /++
2813         +/
2814         @safe pure @nogc
2815         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value) @safe pure @nogc dg)
2816         {
2817             return opApply((IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value) {
2818                 if (_expect(error, false))
2819                     throw error.ionException;
2820                 return dg(symbol, value);
2821             });
2822         }
2823 
2824         /// ditto
2825         @trusted @nogc
2826         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2827         @safe @nogc dg) { return opApply(cast(EDG) dg); }
2828 
2829         /// ditto
2830         @trusted pure
2831         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2832         @safe pure dg) { return opApply(cast(EDG) dg); }
2833 
2834         /// ditto
2835         @trusted
2836         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2837         @safe dg) { return opApply(cast(EDG) dg); }
2838 
2839         /// ditto
2840         @system pure @nogc
2841         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2842         @system pure @nogc dg) { return opApply(cast(EDG) dg); }
2843 
2844         /// ditto
2845         @system @nogc
2846         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2847         @system @nogc dg) { return opApply(cast(EDG) dg); }
2848 
2849         /// ditto
2850         @system pure
2851         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2852         @system pure dg) { return opApply(cast(EDG) dg); }
2853 
2854         /// ditto
2855         @system
2856         scope int opApply(scope int delegate(scope const(char)[] symbol, scope IonDescribedValue value)
2857         @system dg) { return opApply(cast(EDG) dg); }
2858     }
2859 
2860     /++
2861     +/
2862     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value) @safe pure nothrow @nogc dg)
2863         @safe pure nothrow @nogc
2864     {
2865         return ionStruct.opApply((IonErrorCode error, size_t symbolId, scope IonDescribedValue value) {
2866             scope const(char)[] symbol;
2867             if (!error)
2868             {
2869                 if (symbolId < symbolTable.length)
2870                 {
2871                     symbol = symbolTable[symbolId];
2872                 }
2873                 else
2874                 {
2875                     error = IonErrorCode.symbolIdIsTooLargeForTheCurrentSymbolTable;
2876                 }
2877             }
2878             return dg(error, symbol, value);
2879         });
2880     }
2881 
2882     /// ditto
2883     @trusted nothrow @nogc
2884     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2885     @safe nothrow @nogc dg) { return opApply(cast(DG) dg); }
2886 
2887     /// ditto
2888     @trusted pure @nogc
2889     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2890     @safe pure @nogc dg) { return opApply(cast(DG) dg); }
2891 
2892     /// ditto
2893     @trusted pure nothrow
2894     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2895     @safe pure nothrow dg) { return opApply(cast(DG) dg); }
2896 
2897     /// ditto
2898     @trusted @nogc
2899     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2900     @safe @nogc dg) { return opApply(cast(DG) dg); }
2901 
2902     /// ditto
2903     @trusted pure
2904     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2905     @safe pure dg) { return opApply(cast(DG) dg); }
2906 
2907     /// ditto
2908     @trusted nothrow
2909     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2910     @safe nothrow dg) { return opApply(cast(DG) dg); }
2911 
2912     /// ditto
2913     @trusted
2914     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2915     @safe dg) { return opApply(cast(DG) dg); }
2916 
2917     /// ditto
2918     @system pure nothrow @nogc
2919     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2920     @system pure nothrow @nogc dg) { return opApply(cast(DG) dg); }
2921 
2922     /// ditto
2923     @system nothrow @nogc
2924     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2925     @system nothrow @nogc dg) { return opApply(cast(DG) dg); }
2926 
2927     /// ditto
2928     @system pure @nogc
2929     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2930     @system pure @nogc dg) { return opApply(cast(DG) dg); }
2931 
2932     /// ditto
2933     @system pure nothrow
2934     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2935     @system pure nothrow dg) { return opApply(cast(DG) dg); }
2936 
2937     /// ditto
2938     @system @nogc
2939     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2940     @system @nogc dg) { return opApply(cast(DG) dg); }
2941 
2942     /// ditto
2943     @system pure
2944     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2945     @system pure dg) { return opApply(cast(DG) dg); }
2946 
2947     /// ditto
2948     @system nothrow
2949     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2950     @system nothrow dg) { return opApply(cast(DG) dg); }
2951 
2952     /// ditto
2953     @system
2954     scope int opApply(scope int delegate(IonErrorCode error, scope const(char)[] symbol, scope IonDescribedValue value)
2955     @system dg) { return opApply(cast(DG) dg); }
2956 
2957     /++
2958     Returns: $(LREF IonDescribedValue)
2959     +/
2960     auto opIndex(scope const(char)[] symbol) const @safe pure // @nogc DIP1008
2961     {
2962         foreach (key, scope IonDescribedValue value; this)
2963         {
2964             if (key == symbol)
2965             {
2966                 return value;
2967             }
2968         }
2969         import mir.serde: SerdeMirException;
2970         throw new SerdeMirException("Ion struct doesn't contain member ", symbol);
2971     }
2972 
2973     import mir.algebraic: Nullable;
2974 
2975     /++
2976     +/
2977     Nullable!IonDescribedValue opBinaryRight(string op : "in")(scope const(char)[] symbol) const @safe pure @nogc
2978     {
2979         foreach (key, value; this)
2980         {
2981             if (key == symbol)
2982             {
2983                 return typeof(return)(value);
2984             }
2985         }
2986         return typeof(return).init;
2987     }
2988 
2989     /++
2990     Params:
2991         serializer = serializer
2992     +/
2993     void serialize(S)(scope ref S serializer) scope const
2994     {
2995         ionStruct.serialize(serializer);
2996     }
2997 }
2998 
2999 /++
3000 Ion Annotation Wrapper
3001 +/
3002 struct IonAnnotationWrapper
3003 {
3004     ///
3005     IonAnnotations annotations;
3006     ///
3007     IonDescribedValue value;
3008 
3009     /++
3010     Params:
3011         serializer = serializer
3012     +/
3013     void serialize(S)(scope ref S serializer) scope const
3014     {
3015         auto state = serializer.annotationWrapperBegin;
3016         foreach(symbolID; annotations)
3017         {
3018             serializer.putAnnotationId(symbolID);
3019         }
3020         auto annotationsState = serializer.annotationsEnd(state);
3021 
3022         value.serializeImpl(serializer);
3023         if (false)
3024             value.serializeDummy(serializer);
3025 
3026         serializer.annotationWrapperEnd(annotationsState, state);
3027     }
3028 }
3029 
3030 ///
3031 @safe pure
3032 version(mir_ion_test) unittest
3033 {
3034     // null.struct
3035 
3036     auto uw = IonValue([0xE7, 0x82, 0x8A, 0x8B, 0x53, 0xC3, 0x04, 0x65])
3037         .describe
3038         .get!IonAnnotationWrapper;
3039     assert(uw.value.get!IonDecimal.get!double == 1.125);
3040 
3041     size_t i;
3042     foreach (symbolID; uw.annotations)
3043     {
3044         if (i == 0)
3045         {
3046             assert(symbolID == 10);
3047         }
3048         if (i == 1)
3049         {
3050             assert(symbolID == 11);
3051         }
3052         i++;
3053     }
3054     assert(i == 2);
3055 }
3056 
3057 /++
3058 List of annotations represented as symbol IDs.
3059 +/
3060 struct IonAnnotations
3061 {
3062     ///
3063     const(ubyte)[] data;
3064     private alias DG = int delegate(IonErrorCode error, size_t symbolID) @safe pure nothrow @nogc;
3065     private alias EDG = int delegate(size_t symbolID) @safe pure @nogc;
3066 
3067     /++
3068     Returns: true if no annotations provided.
3069     +/
3070     bool empty()
3071         scope @safe pure nothrow @nogc const @property
3072     {
3073         return data.length == 0;
3074     }
3075 
3076     ///
3077     IonErrorCode pick(scope ref size_t symbolID)
3078         @safe pure nothrow @nogc scope
3079     {
3080         assert(!empty);
3081         return parseVarUInt(data, symbolID);
3082     }
3083 
3084 const:
3085 
3086     version (D_Exceptions)
3087     {
3088         /++
3089         +/
3090         @safe pure @nogc
3091         scope int opApply(scope int delegate(size_t symbolID) @safe pure @nogc dg)
3092         {
3093             return opApply((IonErrorCode error, size_t symbolID) {
3094                 if (_expect(error, false))
3095                     throw error.ionException;
3096                 return dg(symbolID);
3097             });
3098         }
3099 
3100         /// ditto
3101         @trusted @nogc
3102         scope int opApply(scope int delegate(size_t symbolID)
3103         @safe @nogc dg) { return opApply(cast(EDG) dg); }
3104 
3105         /// ditto
3106         @trusted pure
3107         scope int opApply(scope int delegate(size_t symbolID)
3108         @safe pure dg) { return opApply(cast(EDG) dg); }
3109 
3110         /// ditto
3111         @trusted
3112         scope int opApply(scope int delegate(size_t symbolID)
3113         @safe dg) { return opApply(cast(EDG) dg); }
3114 
3115         /// ditto
3116         @system pure @nogc
3117         scope int opApply(scope int delegate(size_t symbolID)
3118         @system pure @nogc dg) { return opApply(cast(EDG) dg); }
3119 
3120         /// ditto
3121         @system @nogc
3122         scope int opApply(scope int delegate(size_t symbolID)
3123         @system @nogc dg) { return opApply(cast(EDG) dg); }
3124 
3125         /// ditto
3126         @system pure
3127         scope int opApply(scope int delegate(size_t symbolID)
3128         @system pure dg) { return opApply(cast(EDG) dg); }
3129 
3130         /// ditto
3131         @system
3132         scope int opApply(scope int delegate(size_t symbolID)
3133         @system dg) { return opApply(cast(EDG) dg); }
3134     }
3135 
3136     /++
3137     +/
3138     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID) @safe pure nothrow @nogc dg)
3139         @safe pure nothrow @nogc
3140     {
3141         auto d = data[];
3142         while (d.length)
3143         {
3144             size_t symbolID;
3145             auto error = parseVarUInt(d, symbolID);
3146             if (auto ret = dg(error, symbolID))
3147                 return ret;
3148             assert(!error, "User provided delegate MUST break the iteration when error has non-zero value.");
3149         }
3150         return 0;
3151     }
3152 
3153     /// ditto
3154     @trusted nothrow @nogc
3155     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3156     @safe nothrow @nogc dg) { return opApply(cast(DG) dg); }
3157 
3158     /// ditto
3159     @trusted pure @nogc
3160     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3161     @safe pure @nogc dg) { return opApply(cast(DG) dg); }
3162 
3163     /// ditto
3164     @trusted pure nothrow
3165     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3166     @safe pure nothrow dg) { return opApply(cast(DG) dg); }
3167 
3168     /// ditto
3169     @trusted @nogc
3170     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3171     @safe @nogc dg) { return opApply(cast(DG) dg); }
3172 
3173     /// ditto
3174     @trusted pure
3175     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3176     @safe pure dg) { return opApply(cast(DG) dg); }
3177 
3178     /// ditto
3179     @trusted nothrow
3180     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3181     @safe nothrow dg) { return opApply(cast(DG) dg); }
3182 
3183     /// ditto
3184     @trusted
3185     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3186     @safe dg) { return opApply(cast(DG) dg); }
3187 
3188     /// ditto
3189     @system pure nothrow @nogc
3190     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3191     @system pure nothrow @nogc dg) { return opApply(cast(DG) dg); }
3192 
3193     /// ditto
3194     @system nothrow @nogc
3195     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3196     @system nothrow @nogc dg) { return opApply(cast(DG) dg); }
3197 
3198     /// ditto
3199     @system pure @nogc
3200     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3201     @system pure @nogc dg) { return opApply(cast(DG) dg); }
3202 
3203     /// ditto
3204     @system pure nothrow
3205     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3206     @system pure nothrow dg) { return opApply(cast(DG) dg); }
3207 
3208     /// ditto
3209     @system @nogc
3210     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3211     @system @nogc dg) { return opApply(cast(DG) dg); }
3212 
3213     /// ditto
3214     @system pure
3215     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3216     @system pure dg) { return opApply(cast(DG) dg); }
3217 
3218     /// ditto
3219     @system nothrow
3220     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3221     @system nothrow dg) { return opApply(cast(DG) dg); }
3222 
3223     /// ditto
3224     @system
3225     scope int opApply(scope int delegate(IonErrorCode error, size_t symbolID)
3226     @system dg) { return opApply(cast(DG) dg); }
3227 }
3228 
3229 package IonErrorCode parseVarUInt(bool checkInput = true, U)(scope ref const(ubyte)[] data, scope out U result)
3230     @safe pure nothrow @nogc
3231     if (is(U == ubyte) || is(U == ushort) || is(U == uint) || is(U == ulong))
3232 {
3233     version (LDC) pragma(inline, true);
3234     enum mLength = U(1) << (U.sizeof * 8 / 7 * 7);
3235     for(;;)
3236     {
3237         static if (checkInput)
3238         {
3239             if (_expect(data.length == 0, false))
3240                 return IonErrorCode.unexpectedEndOfData;
3241         }
3242         else
3243         {
3244             assert(data.length);
3245         }
3246         ubyte b = data[0];
3247         data = data[1 .. $];
3248         result <<= 7;
3249         result |= b & 0x7F;
3250         if (cast(byte)b < 0)
3251             return IonErrorCode.none;
3252         static if (checkInput)
3253         {
3254             if (_expect(result >= mLength, false))
3255                 return IonErrorCode.overflowInParseVarUInt;
3256         }
3257         else
3258         {
3259             assert(result < mLength);
3260         }
3261     }
3262 }
3263 
3264 private IonErrorCode parseVarInt(S)(scope ref const(ubyte)[] data, scope out S result)
3265     @safe pure nothrow @nogc
3266     if (is(S == byte) || is(S == short) || is(S == int) || is(S == long))
3267 {
3268     bool neg;
3269     Unsigned!S unsigned;
3270     if (auto error = parseVarInt(data, unsigned, neg))
3271         return error;
3272     result = neg ? cast(S)(0-cast(S)unsigned) : unsigned;
3273     return IonErrorCode.none;
3274 }
3275 
3276 private IonErrorCode parseVarInt(U)(scope ref const(ubyte)[] data, scope out U result, scope out bool neg)
3277     @safe pure nothrow @nogc
3278     if (is(U == ubyte) || is(U == ushort) || is(U == uint) || is(U == ulong))
3279 {
3280     version (LDC) pragma(inline, true);
3281     enum mLength = U(1) << (U.sizeof * 8 / 7 * 7 - 1);
3282     U length;
3283     if (_expect(data.length == 0, false))
3284         return IonErrorCode.unexpectedEndOfData;
3285     ubyte b = data[0];
3286     data = data[1 .. $];
3287     if (b & 0x40)
3288     {
3289         neg = true;
3290         b ^= 0x40;
3291     }
3292     length =  b & 0x7F;
3293     goto L;
3294     for(;;)
3295     {
3296         if (_expect(data.length == 0, false))
3297             return IonErrorCode.unexpectedEndOfData;
3298         b = data[0];
3299         data = data[1 .. $];
3300         length <<= 7;
3301         length |= b & 0x7F;
3302     L:
3303         if (cast(byte)b < 0)
3304         {
3305             result = length;
3306             return IonErrorCode.none;
3307         }
3308         if (_expect(length >= mLength, false))
3309             return IonErrorCode.overflowInParseVarInt;
3310     }
3311 }
3312 
3313 package IonDescribedValue parseValue()(ref return scope const(ubyte)[] data, out IonErrorCode error)
3314     @safe pure nothrow @nogc
3315 {
3316     version (LDC) pragma(inline, true);
3317 
3318     if (_expect(data.length == 0, false))
3319     {
3320         error = IonErrorCode.unexpectedEndOfData;
3321         return IonDescribedValue.init;
3322     }
3323     auto descriptorPtr = &data[0];
3324     data = data[1 .. $];
3325     ubyte descriptorData = *descriptorPtr;
3326 
3327     if (_expect(descriptorData > 0xEE, false))
3328     {
3329         error = IonErrorCode.illegalTypeDescriptor;
3330         return IonDescribedValue.init;
3331     }
3332 
3333     auto describedValue = IonDescribedValue(IonDescriptor(descriptorPtr));
3334 
3335     const L = describedValue.descriptor.L;
3336     const type = describedValue.descriptor.type;
3337     // if null
3338     if (L == 0xF)
3339         return describedValue;
3340     // if bool
3341     if (type == IonTypeCode.bool_)
3342     {
3343         if (_expect(L > 1, false))
3344             error = IonErrorCode.illegalTypeDescriptor;
3345         return describedValue;
3346     }
3347     size_t length = L;
3348     // if large
3349     if (length == 0xE)
3350     {
3351         error = parseVarUInt(data, length);
3352         if (error)
3353             return IonDescribedValue.init;
3354     }
3355     else
3356     if (descriptorData == 0xD1)
3357     {
3358         error = parseVarUInt(data, length);
3359         if (error)
3360             return IonDescribedValue.init;
3361         if (length == 0)
3362         {
3363             error = IonErrorCode.emptyOrderedStruct;
3364             return IonDescribedValue.init;
3365         }
3366     }
3367     if (_expect(length > data.length, false))
3368     {
3369         error = IonErrorCode.unexpectedEndOfData;
3370         return IonDescribedValue.init;
3371     }
3372 
3373     if (_expect(type == IonTypeCode.nInt, false))
3374     {
3375         if (length) do
3376         {
3377             if (data[0])
3378                 goto R;
3379             data = data[1 .. $];
3380         }
3381         while(--length);
3382         error = IonErrorCode.negativeIntegerZero;
3383         return IonDescribedValue.init;
3384     }
3385 R:
3386     describedValue.data = data[0 .. length];
3387     data = data[length .. $];
3388     // NOP Padding
3389     error = type == IonTypeCode.null_ ? IonErrorCode.nop : IonErrorCode.none;
3390     return describedValue;
3391 }
3392 
3393 private F parseFloating(F)(scope const(ubyte)[] data)
3394     @trusted pure nothrow @nogc
3395     if (isFloatingPoint!F)
3396 {
3397     version (LDC) pragma(inline, true);
3398 
3399     enum n = F.sizeof;
3400     static if (n == 4)
3401         alias U = uint;
3402     else
3403     static if (n == 8)
3404         alias U = ulong;
3405     else
3406     static if (n == 16)
3407         alias U = ucent;
3408     else static assert(0);
3409 
3410     assert(data.length == n);
3411 
3412     U num;
3413     if (__ctfe)
3414     {
3415         static foreach_reverse (i; 0 .. n)
3416         {
3417             num <<= 8;
3418             num |= data.ptr[i];
3419         }
3420     }
3421     else
3422     {
3423         num = (cast(U[1])cast(ubyte[n])data.ptr[0 .. n])[0];
3424     }
3425     version (LittleEndian)
3426     {
3427         import core.bitop : bswap;
3428         num = bswap(num);
3429     }
3430     return num.unsignedDataToFloating;
3431 }
3432 
3433 private auto unsignedDataToFloating(T)(const T data)
3434     @trusted pure nothrow @nogc
3435     if (__traits(isUnsigned, T) && T.sizeof >= 4)
3436 {
3437     static if (T.sizeof == 4)
3438         alias F = float;
3439     else
3440     static if (T.sizeof == 8)
3441         alias F = double;
3442     else
3443         alias F = quadruple;
3444 
3445     version(all)
3446     {
3447         return *cast(F*)&data;
3448     }
3449     else
3450     {        
3451         T num = data;
3452         bool sign = cast(bool)(num >> (T.sizeof * 8 - 1));
3453         num &= num.max >> 1;
3454         if (num == 0)
3455             return F(sign ? -0.0f : 0.0f);
3456         int exp = cast(int) (num >> (F.mant_dig - 1));
3457         num &= (T(1) << (F.mant_dig - 1)) - 1;
3458         if (exp)
3459         {
3460             if (exp == (T(1) << (T.sizeof * 8 - F.mant_dig)) - 1)
3461             {
3462                 F ret = num == 0 ? F.infinity : F.nan;
3463                 if (sign)
3464                     ret = -ret;
3465                 return ret;
3466             }
3467             exp -= 1;
3468             num |= T(1) << (F.mant_dig - 1);
3469         }
3470 
3471         exp -= F.mant_dig - F.min_exp;
3472         F ret = num;
3473         import mir.math.ieee: ldexp;
3474         // ret = ldexp(ret, exp);
3475         ret *= 2.0 ^^ exp;
3476         if (sign)
3477             ret = -ret;
3478         assert(data == cast(ulong)*cast(T*) &ret);
3479         return ret;
3480     }
3481 }
3482 
3483 @safe pure nothrow @nogc
3484 unittest
3485 {
3486     assert(unsignedDataToFloating(1UL) == double.min_normal * double.epsilon);
3487     assert(unsignedDataToFloating(1U) == float.min_normal * float.epsilon);
3488 
3489     assert(unsignedDataToFloating(0xFFF0000000000000U) == -double.infinity);
3490     assert(unsignedDataToFloating(0x4008000000000000U) == 3.0);
3491     assert(unsignedDataToFloating(0x4028000000000000U) == 12.0);
3492     assert(unsignedDataToFloating(0x430c6bf526340000U) == 1e15);
3493     assert(unsignedDataToFloating(1UL) == double.min_normal * double.epsilon);
3494     assert(unsignedDataToFloating(1U) == float.min_normal * float.epsilon);
3495 
3496     static assert(unsignedDataToFloating(0xFFF0000000000000U) == -double.infinity);
3497     static assert(unsignedDataToFloating(0x4008000000000000U) == 3.0, unsignedDataToFloating(0x4008000000000000U));
3498     static assert(unsignedDataToFloating(0x4028000000000000U) == 12.0, unsignedDataToFloating(0x4028000000000000U));
3499     static assert(unsignedDataToFloating(0x430c6bf526340000U) == 1e15, unsignedDataToFloating(0x430c6bf526340000U));
3500     static assert(unsignedDataToFloating(1UL) == double.min_normal * double.epsilon);
3501     static assert(unsignedDataToFloating(1U) == float.min_normal * float.epsilon);
3502 }