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 }