1 /++
2 +/
3 module mir.deser.low_level;
4
5 import mir.appender: scopedBuffer, ScopedBuffer;
6 import mir.bignum.decimal: Decimal;
7 import mir.bignum.integer: BigInt;
8 import mir.ion.exception;
9 import mir.ion.internal.basic_types;
10 import mir.ion.type_code;
11 import mir.ion.value;
12 import mir.rc.array: RCArray;
13 import mir.small_array;
14 import mir.small_string;
15 import mir.timestamp;
16 import mir.utility: _expect;
17 import mir.serde: serdeGetFinalProxy;
18 import mir.lob : Clob, Blob;
19
20 import std.traits:
21 isArray,
22 isAggregateType,
23 ForeachType,
24 hasUDA,
25 isFloatingPoint,
26 isIntegral,
27 isSigned,
28 isSomeChar,
29 isUnsigned,
30 Unqual;
31
32 template isFirstOrderSerdeType(T)
33 {
34 import mir.serde: serdeGetFinalProxy, serdeLikeStruct, serdeLikeList;
35
36 static if (isAggregateType!T)
37 {
38 static if (is(T == Clob) || is(T == Blob))
39 enum isFirstOrderSerdeType = true;
40 else
41 static if (isBigInt!T)
42 enum isFirstOrderSerdeType = true;
43 else
44 static if (isDecimal!T)
45 enum isFirstOrderSerdeType = true;
46 else
47 static if (isTimestamp!T || is(typeof(Timestamp(T.init))) && __traits(getAliasThis, T).length == 0)
48 enum isFirstOrderSerdeType = true;
49 else
50 static if (is(T : SmallString!maxLength, size_t maxLength))
51 enum isFirstOrderSerdeType = false;
52 else
53 static if (is(T : SmallArray!(E, maxLength), E, size_t maxLength))
54 enum isFirstOrderSerdeType = isFirstOrderSerdeType!E;
55 else
56 static if (is(T : RCArray!E, E))
57 enum isFirstOrderSerdeType = isFirstOrderSerdeType!E;
58 else
59 static if (hasUDA!(T, serdeLikeStruct) || hasUDA!(T, serdeLikeList))
60 enum isFirstOrderSerdeType = false;
61 else
62 static if (is(T == serdeGetFinalProxy!T))
63 enum isFirstOrderSerdeType = false;
64 else
65 enum isFirstOrderSerdeType = isFirstOrderSerdeType!(serdeGetFinalProxy!T);
66 }
67 else
68 static if (isArray!T)
69 enum isFirstOrderSerdeType = .isFirstOrderSerdeType!(Unqual!(ForeachType!T));
70 else
71 static if (is(T == V[K], K, V))
72 enum isFirstOrderSerdeType = false;
73 else
74 static if (is(T == enum))
75 enum isFirstOrderSerdeType = false;
76 else
77 static if (isSomeChar!T)
78 enum isFirstOrderSerdeType = false;
79 else
80 static if (is(T == serdeGetFinalProxy!T))
81 enum isFirstOrderSerdeType = true;
82 else
83 enum isFirstOrderSerdeType = isFirstOrderSerdeType!(serdeGetFinalProxy!T, false);
84 }
85
86 version(mir_ion_test)
87 unittest
88 {
89 import std.datetime.date;
90 import std.datetime.systime;
91 static assert(isFirstOrderSerdeType!Date);
92 static assert(isFirstOrderSerdeType!DateTime);
93 static assert(isFirstOrderSerdeType!SysTime);
94 }
95
96 version(mir_ion_test)
97 unittest
98 {
99 import mir.date;
100 static assert(isFirstOrderSerdeType!Date);
101 }
102
103 /++
104 Deserialize `null` value
105 +/
106 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
107 pure @safe nothrow @nogc
108 if (is(T == typeof(null)))
109 {
110 version (LDC) pragma(inline, true);
111 return data == null ? IonErrorCode.none : IonErrorCode.expectedNullValue;
112 }
113
114 ///
115 version(mir_ion_test) unittest
116 {
117 import mir.ion.value;
118 import mir.ion.exception;
119
120 auto data = IonValue([0x1F]).describe; // null.bool
121 typeof(null) value;
122 assert(deserializeValueImpl!(typeof(null))(data, value) == IonErrorCode.none);
123 }
124
125 /++
126 Deserialize boolean value
127 +/
128 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
129 pure @safe nothrow @nogc
130 if (is(T == bool))
131 {
132 IonErrorCode error;
133 value = data.get!bool(error);
134 return error;
135 }
136
137 ///
138 pure version(mir_ion_test) unittest
139 {
140 import mir.ion.value;
141 import mir.ion.exception;
142
143 auto data = IonValue([0x11]).describe;
144 bool value;
145 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
146 assert(value);
147 }
148
149 /++
150 Deserialize integral value.
151 +/
152 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
153 pure @safe nothrow @nogc
154 if (isIntegral!T && !is(T == enum))
155 {
156 static if (__traits(isUnsigned, T))
157 {
158 IonErrorCode error;
159 auto ionValue = data.get!IonUInt(error);
160 if (error)
161 return error;
162 return ionValue.get!T(value);
163 }
164 else
165 {
166 IonErrorCode error;
167 auto ionValue = data.get!IonInt(error);
168 if (error)
169 return error;
170 return ionValue.get!T(value);
171 }
172 }
173
174 ///
175 version(mir_ion_test) unittest
176 {
177 import mir.ion.value;
178 import mir.ion.exception;
179
180 auto data = IonValue([0x21, 0x07]).describe;
181 int valueS;
182 uint valueU;
183
184 assert(deserializeValueImpl(data, valueS) == IonErrorCode.none);
185 assert(valueS == 7);
186
187 assert(deserializeValueImpl(data, valueU) == IonErrorCode.none);
188 assert(valueU == 7);
189
190 data = IonValue([0x31, 0x07]).describe;
191
192 assert(deserializeValueImpl(data, valueS) == IonErrorCode.none);
193 assert(valueS == -7);
194 }
195
196 /++
197 Deserialize big integer value.
198 +/
199 IonErrorCode deserializeValueImpl(T : BigInt!maxSize64, size_t maxSize64)(scope IonDescribedValue data, scope ref T value)
200 pure @safe nothrow @nogc
201 {
202 IonErrorCode error;
203 auto ionValue = data.get!IonInt(error);
204 if (error)
205 return error;
206 if (!value.copyFromBigEndian(ionValue.data, ionValue.sign))
207 return IonErrorCode.integerOverflow;
208 return IonErrorCode.none;
209 }
210
211 ///
212 version(mir_ion_test) unittest
213 {
214 import mir.ion.value;
215 import mir.ion.exception;
216 import mir.bignum.integer;
217
218 auto data = IonValue([0x31, 0x07]).describe;
219 BigInt!128 value = void; // 256x64
220
221 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
222 assert(value.sign);
223 assert(value.view.unsigned == 7);
224 }
225
226 /++
227 Deserialize Blob value.
228 +/
229 IonErrorCode deserializeValueImpl()(scope IonDescribedValue data, ref Blob value)
230 pure @safe nothrow
231 {
232 IonErrorCode error;
233 auto ionValue = data.get!Blob(error);
234 if (error)
235 return error;
236 value = ionValue.data.dup.Blob;
237 return IonErrorCode.none;
238 }
239
240 ///
241 version(mir_ion_test) unittest
242 {
243 import mir.deser.ion : deserializeIon;
244 import mir.lob : Blob;
245 import mir.ser.ion : serializeIon;
246
247 static struct BlobWrapper { Blob blob; }
248 auto blob = BlobWrapper(Blob([0xF0, 0x00, 0xBA, 0x50]));
249 auto serdeBlob = serializeIon(blob).deserializeIon!BlobWrapper;
250 assert(serdeBlob == blob);
251 }
252
253 /++
254 Deserialize Clob value.
255 +/
256 IonErrorCode deserializeValueImpl()(scope IonDescribedValue data, ref Clob value)
257 pure @safe nothrow
258 {
259 IonErrorCode error;
260 auto ionValue = data.get!Clob(error);
261 if (error)
262 return error;
263 value = ionValue.data.dup.Clob;
264 return IonErrorCode.none;
265 }
266
267 ///
268 version(mir_ion_test) unittest
269 {
270 import mir.deser.ion : deserializeIon;
271 import mir.lob : Clob;
272 import mir.ser.ion : serializeIon;
273
274 static struct ClobWrapper { Clob clob; }
275 auto clob = ClobWrapper(Clob("abcd"));
276 auto serdeClob = serializeIon(clob).deserializeIon!ClobWrapper;
277 assert(serdeClob == clob);
278 }
279
280 /++
281 Deserialize floating point value.
282
283 Special_deserialisation_symbol_values:
284
285 $(TABLE
286 $(TR $(TD `nan"`))
287 $(TR $(TD `+inf"`))
288 $(TR $(TD `-inf`))
289 )
290 +/
291 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
292 pure @safe nothrow @nogc
293 if (isFloatingPoint!T)
294 {
295 if (_expect(data != null, true))
296 {
297 if (data.descriptor.type == IonTypeCode.float_)
298 {
299 return data.trustedGet!IonFloat.get!T(value);
300 }
301 else
302 if (data.descriptor.type == IonTypeCode.decimal)
303 {
304 return data.trustedGet!IonDecimal.get!T(value);
305 }
306 else
307 if (data.descriptor.type == IonTypeCode.uInt || data.descriptor.type == IonTypeCode.nInt)
308 {
309 IonErrorCode error;
310 auto ionValue = data.get!IonInt(error);
311 if (error)
312 return error;
313 import mir.bignum.integer;
314 BigInt!128 integer = void;
315 if(!integer.copyFromBigEndian(ionValue.data, ionValue.sign))
316 return IonErrorCode.overflowInIntegerValue;
317 value = cast(T) integer;
318 return IonErrorCode.none;
319 }
320 else
321 {
322 IonErrorCode error;
323 auto ionValue = data.get!(const(char)[])(error);
324 if (error)
325 return error;
326
327 import mir.bignum.decimal;
328 Decimal!128 decimal = void;
329 DecimalExponentKey exponentKey;
330
331 enum bool allowSpecialValues = true;
332 enum bool allowDotOnBounds = true;
333 enum bool allowDExponent = true;
334 enum bool allowStartingPlus = true;
335 enum bool allowUnderscores = false;
336 enum bool allowLeadingZeros = true;
337 enum bool allowExponent = true;
338 enum bool checkEmpty = false;
339
340 if (!decimal.fromStringImpl!(
341 char,
342 allowSpecialValues,
343 allowDotOnBounds,
344 allowDExponent,
345 allowStartingPlus,
346 allowUnderscores,
347 allowLeadingZeros,
348 allowExponent,
349 checkEmpty,
350 )(ionValue, exponentKey))
351 return IonErrorCode.expectedFloatingValue;
352
353 value = cast(T) decimal;
354 return IonErrorCode.none;
355 }
356 }
357 return IonErrorCode.expectedFloatingValue;
358 }
359
360 ///
361 version(mir_ion_test) unittest
362 {
363 import mir.ion.value;
364 import mir.ion.exception;
365 // from ion float
366 auto data = IonValue([0x44, 0x42, 0xAA, 0x40, 0x00]).describe;
367 double value;
368
369 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
370 assert(value == 85.125);
371
372 // from ion double
373 data = IonValue([0x48, 0x40, 0x55, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00]).describe;
374
375 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
376 assert(value == 85.125);
377
378 // from ion decimal
379 data = IonValue([0x56, 0x00, 0xcb, 0x80, 0xbc, 0x2d, 0x86]).describe;
380
381 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
382 assert(value == -12332422e75);
383 }
384
385 /++
386 Deserialize decimal value.
387 +/
388 IonErrorCode deserializeValueImpl(T : Decimal!maxW64bitSize, size_t maxW64bitSize)(scope IonDescribedValue data, scope ref T value)
389 pure @safe nothrow @nogc
390 {
391 IonErrorCode error;
392 auto ionValue = data.get!IonDecimal(error);
393 if (error)
394 return error;
395 auto ionDescribedDecimal = ionValue.get!IonDescribedDecimal(error);
396 if (error)
397 return error;
398 return ionDescribedDecimal.get(value);
399 }
400
401 ///
402 version(mir_ion_test) unittest
403 {
404 import mir.ion.value;
405 import mir.ion.exception;
406 import mir.bignum.decimal;
407
408 Decimal!128 value; // 256x64 bits
409
410 // from ion decimal
411 auto data = IonValue([0x56, 0x00, 0xcb, 0x80, 0xbc, 0x2d, 0x86]).describe;
412
413 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
414 assert(cast(double)value == -12332422e75);
415 }
416
417 /++
418 Deserialize timestamp value.
419 +/
420 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
421 pure @safe nothrow @nogc
422 if (is(T == Timestamp))
423 {
424 if (_expect(data != null, true))
425 {
426 if (data.descriptor.type == IonTypeCode.timestamp)
427 {
428 return data.trustedGet!IonTimestamp.get!T(value);
429 }
430 else
431 {
432 IonErrorCode error;
433 auto ionValue = data.get!(const(char)[])(error);
434 if (!error && Timestamp.fromString(ionValue, value))
435 return IonErrorCode.none;
436 }
437 }
438 return IonErrorCode.expectedTimestampValue;
439 }
440
441 ///ditto
442 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
443 // pure @safe nothrow @nogc
444 if (!(hasProxy!T && !hasLikeStruct!T && isFirstOrderSerdeType!T) && is(typeof(Timestamp.init.opCast!T)))
445 {
446 Timestamp temporal;
447 if (auto error = .deserializeValueImpl(data, temporal))
448 return error;
449 value = temporal.opCast!T;
450 return IonErrorCode.none;
451 }
452
453 ///
454 version(mir_ion_test) unittest
455 {
456 import mir.ion.value;
457 import mir.ion.exception;
458 import mir.bignum.decimal;
459
460 Decimal!128 value; // 256x64 bits
461
462 // from ion decimal
463 auto data = IonValue([0x56, 0x00, 0xcb, 0x80, 0xbc, 0x2d, 0x86]).describe;
464
465 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
466 assert(cast(double)value == -12332422e75);
467 }
468
469 package template hasProxy(T)
470 {
471 import mir.serde: serdeProxy;
472 static if (is(T == enum) || isAggregateType!T)
473 enum hasProxy = hasUDA!(T, serdeProxy);
474 else
475 enum hasProxy = false;
476 }
477
478 package template hasLikeStruct(T)
479 {
480 import mir.serde: serdeLikeStruct;
481 static if (is(T == enum) || isAggregateType!T)
482 enum hasLikeStruct = hasUDA!(T, serdeLikeStruct);
483 else
484 enum hasLikeStruct = false;
485 }
486
487 package template hasDiscriminatedField(T)
488 {
489 import mir.serde: serdeDiscriminatedField;
490 static if (is(T == enum) || isAggregateType!T)
491 enum hasDiscriminatedField = hasUDA!(T, serdeDiscriminatedField);
492 else
493 enum hasDiscriminatedField = false;
494 }
495
496 package template hasFallbackStruct(T)
497 {
498 import mir.serde: serdeFallbackStruct;
499 static if (is(T == enum) || isAggregateType!T)
500 enum hasFallbackStruct = hasUDA!(T, serdeFallbackStruct);
501 else
502 enum hasFallbackStruct = false;
503 }
504
505 /++
506 Deserialize struct/class value with proxy.
507 +/
508 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
509 if (hasProxy!T && !hasLikeStruct!T && isFirstOrderSerdeType!T)
510 {
511 import std.traits: Select;
512 import mir.serde: serdeGetProxy, serdeScoped, serdeScoped;
513 import mir.conv: to;
514
515 serdeGetProxy!T proxy;
516 if (auto error = deserializeValueImpl(data, proxy))
517 return error;
518 value = proxy.to!T;
519 return IonErrorCode.none;
520 }
521
522 /++
523 Deserialize enum value.
524 +/
525 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
526 if (is(T == enum) && !hasProxy!T)
527 {
528 import mir.serde: serdeParseEnum;
529 IonErrorCode error;
530 auto ionValue = data.get!(const(char)[])(error);
531 if (error)
532 return error;
533 if (serdeParseEnum(ionValue, value))
534 return IonErrorCode.none;
535 return IonErrorCode.expectedEnumValue;
536 }
537
538 ///
539 version(mir_ion_test) unittest
540 {
541 import mir.ion.value;
542 import mir.ion.exception;
543 enum E {a, b, c}
544
545 // from ion string
546 auto data = IonValue([0x81, 'b']).describe;
547 E value;
548
549 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
550 assert(value == E.b);
551 }
552
553
554 /++
555 Deserialize ascii value from ion string.
556 +/
557 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
558 pure @safe nothrow
559 if (is(T == char))
560 {
561 IonErrorCode error;
562 auto ionValue = data.get!(const(char)[])(error);
563 if (error)
564 return error;
565 if (_expect(ionValue.length != 1, false))
566 return IonErrorCode.expectedCharValue;
567 value = ionValue[0];
568 return IonErrorCode.none;
569 }
570
571 ///
572 version(mir_ion_test) unittest
573 {
574 import mir.ion.value;
575 import mir.ion.exception;
576
577 auto data = IonValue([0x81, 'b']).describe;
578 char value;
579
580 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
581 assert(value == 'b');
582 }
583
584 private IonErrorCode deserializeListToScopedBuffer(Buffer)(scope IonDescribedValue data, ref Buffer buffer)
585 {
586 auto ionValue = data.trustedGet!IonList;
587 foreach (IonErrorCode error, scope IonDescribedValue ionElem; ionValue)
588 {
589 import std.traits: Unqual;
590 if (_expect(error, false))
591 return error;
592 Unqual!(typeof(buffer.data[0])) value;
593 error = deserializeValueImpl(ionElem, value);
594 if (_expect(error, false))
595 return error;
596 import core.lifetime: move;
597 buffer.put(move(value));
598 }
599 return IonErrorCode.none;
600 }
601
602
603 ///
604 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
605 if (is(T == E[], E) && !isSomeChar!E)
606 {
607 alias E = Unqual!(ForeachType!T);
608 if (data.descriptor.type == IonTypeCode.list)
609 {
610 import std.array: std_appender = appender;
611 auto buffer = std_appender!(E[]);
612 if (auto error = deserializeListToScopedBuffer(data, buffer))
613 return error;
614 value = buffer.data;
615 return IonErrorCode.none;
616 }
617 else
618 if (data.descriptor.type == IonTypeCode.null_)
619 {
620 value = null;
621 return IonErrorCode.none;
622 }
623 return IonErrorCode.expectedListValue;
624 }
625
626 ///
627 @safe pure
628 version(mir_ion_test) unittest
629 {
630 import mir.ion.value;
631 import mir.ion.exception;
632
633 auto data = IonValue([
634 0xbe, 0x91, 0x00, 0x00, 0x21, 0x0c,
635 0x00, 0x00, 0x48, 0x43, 0x0c, 0x6b,
636 0xf5, 0x26, 0x34, 0x00, 0x00, 0x00,
637 0x00]).describe;
638
639 double[] value;
640 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
641 assert(value == [12, 100e13]);
642 }
643
644 ///
645 IonErrorCode deserializeValueImpl(T)(scope IonDescribedValue data, scope ref T value)
646 if (is(T == RCArray!E, E) && !isSomeChar!E)
647 {
648 alias E = Unqual!(ForeachType!T);
649 if (data.descriptor.type == IonTypeCode.list)
650 {
651 import core.lifetime: move;
652 import std.traits: TemplateArgsOf;
653 auto buffer = scopedBuffer!E;
654 if (auto error = deserializeListToScopedBuffer(data, buffer))
655 return error;
656 auto ar = RCArray!E(buffer.length, false);
657 () @trusted {
658 buffer.moveDataAndEmplaceTo(ar[]);
659 } ();
660 static if (__traits(compiles, value = move(ar)))
661 value = move(ar);
662 else () @trusted {
663 value = ar.opCast!T;
664 } ();
665 return IonErrorCode.none;
666 }
667 else
668 if (data.descriptor.type == IonTypeCode.null_)
669 {
670 value = null;
671 return IonErrorCode.none;
672 }
673 return IonErrorCode.expectedListValue;
674 }
675
676 ///
677 @safe pure
678 version(mir_ion_test) unittest
679 {
680 import mir.ion.exception;
681 import mir.ion.value;
682 import mir.rc.array: RCArray;
683
684 auto data = IonValue([
685 0xbe, 0x91, 0x00, 0x00, 0x21, 0x0c,
686 0x00, 0x00, 0x48, 0x43, 0x0c, 0x6b,
687 0xf5, 0x26, 0x34, 0x00, 0x00, 0x00,
688 0x00]).describe;
689
690 RCArray!(const double) value;
691 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
692 assert(value[] == [12, 100e13]);
693 }
694
695 ///
696 IonErrorCode deserializeValueImpl(T : SmallArray!(E, maxLength), E, size_t maxLength)(scope IonDescribedValue data, out T value)
697 {
698 if (data.descriptor.type == IonTypeCode.list)
699 {
700 foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
701 {
702 if (_expect(error, false))
703 return error;
704 if (value._length == maxLength)
705 return IonErrorCode.smallArrayOverflow;
706 E elem;
707 error = .deserializeValueImpl(ionElem, elem);
708 if (_expect(error, false))
709 return error;
710 import core.lifetime: move;
711 value.trustedAppend(move(elem));
712 }
713 return IonErrorCode.none;
714 }
715 else
716 if (data.descriptor.type == IonTypeCode.null_)
717 {
718 return IonErrorCode.none;
719 }
720 return IonErrorCode.expectedListValue;
721 }
722
723 ///
724 IonErrorCode deserializeValueImpl(T : E[N], E, size_t N)(scope IonDescribedValue data, out T value)
725 {
726 if (data.descriptor.type == IonTypeCode.list)
727 {
728 size_t i;
729 foreach (IonErrorCode error, scope IonDescribedValue ionElem; data.trustedGet!IonList)
730 {
731 if (_expect(error, false))
732 return error;
733 if (i >= N)
734 return IonErrorCode.tooManyElementsForStaticArray;
735 error = .deserializeValueImpl(ionElem, value[i++]);
736 if (_expect(error, false))
737 return error;
738 }
739 if (i < N)
740 return IonErrorCode.notEnoughElementsForStaticArray;
741 return IonErrorCode.none;
742 }
743 return IonErrorCode.expectedListValue;
744 }
745
746 ///
747 @safe pure
748 version(mir_ion_test) unittest
749 {
750 import mir.ion.value;
751 import mir.ion.exception;
752 import mir.small_array;
753
754 auto data = IonValue([
755 0xbe, 0x91, 0x00, 0x00, 0x21, 0x0c,
756 0x00, 0x00, 0x48, 0x43, 0x0c, 0x6b,
757 0xf5, 0x26, 0x34, 0x00, 0x00, 0x00,
758 0x00]).describe;
759
760 SmallArray!(double, 3) value;
761 assert(deserializeValueImpl(data, value) == IonErrorCode.none);
762 assert(value == [12, 100e13]);
763 }