1 module mir.ion.internal.stage3; 2 3 import core.stdc.string: memcpy, memmove; 4 import mir.internal.memory: malloc, realloc, free; 5 import mir.bitop; 6 import mir.ion.exception; 7 import mir.ion.symbol_table; 8 import mir.ion.tape; 9 import mir.ion.type_code; 10 import mir.primitives; 11 import mir.utility: _expect; 12 import std.meta: AliasSeq, aliasSeqOf; 13 import std.traits; 14 15 /// 16 struct IonErrorInfo 17 { 18 /// 19 IonErrorCode code; 20 /// 21 size_t location; 22 /// refers tape or text 23 const(char)[] key; 24 } 25 26 // version = MirDecimalJson; 27 28 alias Stage3Handle = void delegate(IonErrorInfo, scope const ubyte[]) @safe pure nothrow @nogc; 29 30 @trusted pure nothrow @nogc 31 bool stage12( 32 scope const(char)[] text, 33 scope ulong[2]* pairedMask1, 34 scope ulong[2]* pairedMask2, 35 ) 36 { 37 pragma(inline, false); 38 import core.stdc.string: memcpy; 39 import mir.ion.internal.stage1; 40 import mir.ion.internal.stage2; 41 42 // assume 32KB L1 data cache 43 // 32 * 2 / 5 > 12.5 44 enum nMax = 1024 * 12 + 512; 45 enum k = nMax / 64; 46 47 bool backwardEscapeBit; 48 align(64) ubyte[64][k] vector = void; 49 50 while (text.length >= nMax) 51 { 52 memcpy(vector.ptr, text.ptr, nMax); 53 stage1(k, cast(const) vector.ptr, pairedMask1, backwardEscapeBit); 54 stage2(k, cast(const) vector.ptr, pairedMask2); 55 text = text[nMax .. $]; 56 pairedMask1 += k; 57 pairedMask2 += k; 58 } 59 60 if (text.length) 61 { 62 auto y = text.length / 64; 63 if (auto tail = text.length % 64) 64 vector[y++] = ' '; 65 memcpy(vector.ptr, text.ptr, text.length); 66 stage1(y, cast(const) vector.ptr, pairedMask1, backwardEscapeBit); 67 stage2(y, cast(const) vector.ptr, pairedMask2); 68 } 69 70 return backwardEscapeBit; 71 } 72 73 extern(C) 74 struct Stage3Result 75 { 76 ubyte[] tape; 77 IonErrorInfo info; 78 } 79 80 @trusted pure nothrow @nogc 81 void mir_json2ion( 82 scope const(char)[] text, 83 scope Stage3Handle handle, 84 ) 85 { 86 version (LDC) pragma(inline, false); 87 with(mir_json2ion(text)) 88 { 89 version (measure) StopWatch swh; 90 version (measure) assumePure(&swh.start)(); 91 scope(exit) 92 tape.ptr.free; 93 handle(info, tape); 94 version (measure) assumePure(&swh.stop)(); 95 version (measure) assumePure(&printSw)(swh); 96 } 97 } 98 99 extern(C) 100 @trusted pure nothrow @nogc 101 Stage3Result mir_json2ion( 102 scope const(char)[] text, 103 ) 104 { 105 version (LDC) pragma(inline, false); 106 107 import core.stdc.string: memcpy; 108 import mir.utility: _expect, min, max; 109 110 IonSymbolTableSequental symbolTable = void; 111 symbolTable.initialize; 112 113 ubyte[] tape; 114 size_t currentTapePosition; 115 ulong[2]* pairedMask1 = void; 116 ulong[2]* pairedMask2 = void; 117 // const(char)[] key; // Last key, it is the reference to the tape 118 // size_t location; 119 IonErrorCode errorCode; 120 121 // vector[$ - 1] = ' '; 122 // pairedMask1[$ - 1] = [0UL, 0UL]; 123 // pairedMask2[$ - 1] = [0UL, ulong.max]; 124 125 version (measure) StopWatch swm; 126 version (measure) assumePure(&swm.start)(); 127 128 size_t[1024] stack = void; 129 sizediff_t stackPos = stack.length; 130 131 bool skipSpaces() 132 { 133 version(LDC) pragma(inline, true); 134 while (text.length) 135 { 136 auto index = text.length - 1; 137 auto indexG = index >> 6; 138 auto indexL = index & 0x3F; 139 auto spacesMask = pairedMask2[indexG][1] << (63 - indexL); 140 if (spacesMask != 0) 141 { 142 assert(ctlz(spacesMask) < text.length); 143 text = text[0 .. $ - cast(size_t)ctlz(spacesMask)]; 144 return false; 145 } 146 text = text[0 .. index & ~0x3FUL]; 147 } 148 return true; 149 } 150 151 int readUnicode()(ref dchar d, scope const(char)* ptr) 152 { 153 version(LDC) pragma(inline, true); 154 155 uint e = 0; 156 size_t i = 4; 157 do 158 { 159 int c = uniFlags[*ptr++]; 160 assert(c < 16); 161 if (c == -1) 162 return -1; 163 assert(c >= 0); 164 e <<= 4; 165 e ^= c; 166 } 167 while(--i); 168 d = e; 169 return 0; 170 } 171 172 size_t maskLength = text.length / 64 + (text.length % 64 != 0); 173 currentTapePosition = text.length + 1024u + maskLength * (ulong[2]).sizeof; 174 tape = (cast(ubyte*) malloc(currentTapePosition))[0 .. currentTapePosition]; 175 176 pairedMask1 = cast(ulong[2]*) tape.ptr; 177 pairedMask2 = pairedMask1 + maskLength; 178 179 if (stage12(text, pairedMask1, pairedMask2)) 180 goto unexpectedEnd; 181 182 183 for (;;) 184 { 185 if (skipSpaces) 186 { 187 if (stackPos == stack.length) 188 break; 189 else 190 goto unexpectedEnd; 191 } 192 Next: 193 auto startC = text[$ - 1]; 194 195 switch(startC) 196 { 197 case '"': 198 { 199 text = text[0 .. $ - 1]; 200 size_t oldTapePosition = currentTapePosition; 201 for (;;) 202 { 203 char[64] _textBuffer = void; 204 if (_expect(text.length < _textBuffer.length, false)) 205 { 206 memcpy(_textBuffer.ptr + _textBuffer.length - text.length, text.ptr, text.length); 207 text = _textBuffer[$ - text.length .. $]; 208 if (text.length == 0) 209 goto unexpectedEnd; 210 } 211 size_t index = text.length - 1; 212 auto indexG = index >> 6; 213 auto indexL = index & 0x3F; 214 auto quoteMask = pairedMask1[indexG][0] << (63 - indexL); 215 auto escapeMask = pairedMask1[indexG][1] << (63 - indexL); 216 auto mask = quoteMask | escapeMask; 217 memcpy(tape.ptr + currentTapePosition - 64, text.ptr + text.length - 64, 64); 218 if (mask != 0) 219 { 220 assert(text.length > ctlz(mask)); 221 auto shift = cast(size_t)ctlz(mask); 222 text = text[0 .. $ - shift]; 223 currentTapePosition -= shift; 224 if (_expect(quoteMask > escapeMask, true)) 225 { 226 assert(text[$ - 1] == '"', text); 227 currentTapePosition -= ionPutEndR(tape.ptr + currentTapePosition, IonTypeCode..string, oldTapePosition - currentTapePosition); 228 text = text[0 .. $ - 1]; 229 break; 230 } 231 else 232 { 233 assert(text.length >= 2); 234 auto c = text[$ - 1]; 235 assert(text[$ - 2] == '\\', text[$ - min($, 40u) .. $]); 236 text = text[0 .. $ - 2]; 237 --currentTapePosition; 238 239 switch (c) 240 { 241 case '/' : 242 case '\"': 243 case '\\': tape[currentTapePosition] = c ; continue; 244 case 'b' : tape[currentTapePosition] = '\b'; continue; 245 case 'f' : tape[currentTapePosition] = '\f'; continue; 246 case 'n' : tape[currentTapePosition] = '\n'; continue; 247 case 'r' : tape[currentTapePosition] = '\r'; continue; 248 case 't' : tape[currentTapePosition] = '\t'; continue; 249 case 'u' : 250 dchar d; 251 if (oldTapePosition - currentTapePosition < 4) 252 goto unexpected_escape_unicode_value; //unexpected \u 253 currentTapePosition += 4; 254 if (auto r = readUnicode(d, text.ptr + text.length + 2)) 255 goto unexpected_escape_unicode_value; //unexpected \u 256 if (_expect(0xD800 <= d && d <= 0xDFFF, false)) 257 { 258 if (d < 0xDC00) 259 goto invalid_utf_value; 260 if (text.length < 6 || text[$ - 6 .. $ - 4] != `\u`) 261 goto invalid_utf_value; 262 dchar trailing = d; 263 if (auto r = readUnicode(d, text.ptr + text.length - 4)) 264 goto unexpected_escape_unicode_value; //unexpected \u 265 if (!(0xD800 <= d && d <= 0xDFFF)) 266 goto invalid_trail_surrogate; 267 text = text[0 .. $ - 6]; 268 d &= 0x3FF; 269 trailing &= 0x3FF; 270 d <<= 10; 271 d |= trailing; 272 d += 0x10000; 273 } 274 if (d < 0x80) 275 { 276 tape[currentTapePosition] = cast(ubyte) (d); 277 continue; 278 } 279 if (d < 0x800) 280 { 281 tape[currentTapePosition - 1] = cast(ubyte) (0xC0 | (d >> 6)); 282 tape[currentTapePosition - 0] = cast(ubyte) (0x80 | (d & 0x3F)); 283 currentTapePosition -= 1; 284 continue; 285 } 286 if (!(d < 0xD800 || (d > 0xDFFF && d <= 0x10FFFF))) 287 goto invalid_trail_surrogate; 288 if (d < 0x10000) 289 { 290 tape[currentTapePosition - 2] = cast(ubyte) (0xE0 | (d >> 12)); 291 tape[currentTapePosition - 1] = cast(ubyte) (0x80 | ((d >> 6) & 0x3F)); 292 tape[currentTapePosition - 0] = cast(ubyte) (0x80 | (d & 0x3F)); 293 currentTapePosition -= 2; 294 continue; 295 } 296 // assert(d < 0x200000); 297 tape[currentTapePosition - 3] = cast(ubyte) (0xF0 | (d >> 18)); 298 tape[currentTapePosition - 2] = cast(ubyte) (0x80 | ((d >> 12) & 0x3F)); 299 tape[currentTapePosition - 1] = cast(ubyte) (0x80 | ((d >> 6) & 0x3F)); 300 tape[currentTapePosition - 0] = cast(ubyte) (0x80 | (d & 0x3F)); 301 currentTapePosition -= 3; 302 continue; 303 default: 304 goto unexpected_escape_value; // unexpected escape 305 } 306 } 307 } 308 size_t newTextLength = index & ~size_t(0x3F); 309 currentTapePosition -= text.length - newTextLength; 310 text = text[0 .. newTextLength]; 311 } 312 } 313 break; 314 case '0': .. case '9': 315 { 316 size_t stringStart = text.length; 317 for (;;) 318 { 319 // FFFFFFFFFFFFFFD1 320 // {"a":3} 321 auto index = stringStart - 1; //5 322 auto indexG = index >> 6; 323 auto indexL = index & 0x3F; 324 auto spacesMask = pairedMask2[indexG][0] << (63 - indexL); 325 if (spacesMask != 0) 326 { 327 assert(stringStart >= ctlz(spacesMask), text); 328 stringStart -= ctlz(spacesMask); 329 break; 330 } 331 stringStart = index & ~0x3FUL; 332 if (stringStart == 0) 333 break; 334 } 335 336 auto str = text[stringStart .. $]; 337 text = text[0 .. stringStart]; 338 339 import mir.bignum.internal.parse: parseJsonNumberImpl; 340 auto result = str.parseJsonNumberImpl; 341 if (!result.success) 342 goto unexpected_decimal_value; 343 344 if (!result.key) // integer 345 { 346 currentTapePosition -= ionPutR(tape.ptr + currentTapePosition, result.coefficient, result.coefficient && result.sign); 347 } 348 else 349 version(MirDecimalJson) 350 { 351 currentTapePosition -= ionPutDecimalR(tape.ptr + currentTapePosition, result.sign, result.coefficient, result.exponent); 352 } 353 else 354 { 355 import mir.bignum.internal.dec2float: decimalToFloatImpl; 356 auto fp = decimalToFloatImpl!double(result.coefficient, result.exponent); 357 if (result.sign) 358 fp = -fp; 359 // sciencific 360 currentTapePosition -= ionPutR(tape.ptr + currentTapePosition, fp); 361 } 362 } 363 break; 364 case '}': 365 { 366 text = text[0 .. $ - 1]; 367 assert(stackPos <= stack.length); 368 369 if (skipSpaces) 370 goto unexpectedEnd; 371 372 if (text[$ - 1] != '{') 373 { 374 if (--stackPos < 0) 375 goto stack_overflow; 376 stack[stackPos] = (currentTapePosition << 1) | 1; 377 goto Next; 378 } 379 text = text[0 .. $ - 1]; 380 tape[--currentTapePosition] = IonTypeCode.struct_ << 4; 381 } 382 break; 383 case ']': 384 { 385 text = text[0 .. $ - 1]; 386 assert(stackPos <= stack.length); 387 388 if (skipSpaces) 389 goto unexpectedEnd; 390 391 if (text[$ - 1] != '[') 392 { 393 if (--stackPos < 0) 394 goto stack_overflow; 395 stack[stackPos] = (currentTapePosition << 1) | 0; 396 goto Next; 397 } 398 text = text[0 .. $ - 1]; 399 tape[--currentTapePosition] = IonTypeCode.list << 4; 400 } 401 break; 402 case 'e': 403 currentTapePosition--; 404 if (text.length >= 4 && text[$ - 4 .. $] == "true") 405 { 406 ionPut(tape.ptr + currentTapePosition, true); 407 text = text[0 .. $ - 4]; 408 break; 409 } 410 else 411 if (text.length >= 5 && text[$ - 5 .. $ - 1] == "fals") 412 { 413 ionPut(tape.ptr + currentTapePosition, false); 414 text = text[0 .. $ - 5]; 415 break; 416 } 417 else goto default; 418 case 'l': 419 currentTapePosition--; 420 ionPut(tape.ptr + currentTapePosition, null); 421 if (text.length >= 4 && text[$ - 4 .. $] == "null") 422 { 423 text = text[0 .. $ - 4]; 424 break; 425 } 426 else goto default; 427 default: 428 goto value_unexpectedStart; 429 } 430 431 for(;;) 432 { 433 if (stackPos == stack.length) 434 break; 435 436 // put key 437 if (stack[stackPos] & 1) 438 { 439 if (skipSpaces) 440 goto unexpectedEnd; 441 if (text[$ - 1] != ':') 442 goto object_after_key_is_missing; 443 text = text[0 .. $ - 1]; 444 if (skipSpaces) 445 goto unexpectedEnd; 446 447 if (text[$ - 1] != '"') 448 goto object_key_start_unexpectedValue; 449 assert(text[$ - 1] == '"', "Internal Mir Ion logic error. Please report an issue."); 450 text = text[0 .. $ - 1]; 451 452 size_t stringStart = text.length; 453 for (;;) 454 { 455 auto index = stringStart - 1; 456 auto indexG = index >> 6; 457 auto indexL = index & 0x3F; 458 auto spacesMask = pairedMask1[indexG][0] << (63 - indexL); 459 if (spacesMask != 0) 460 { 461 stringStart -= ctlz(spacesMask); 462 break; 463 } 464 stringStart = index & ~0x3FUL; 465 } 466 467 assert(text[stringStart - 1] == '"'); 468 auto str = text[stringStart .. $]; 469 text = text[0 .. stringStart - 1]; 470 auto id = symbolTable.insert(str); 471 // TODO find id using the key 472 currentTapePosition -= ionPutVarUIntR(tape.ptr + currentTapePosition, id); 473 } 474 475 // next 476 if (skipSpaces) 477 goto unexpectedEnd; 478 479 assert(stackPos >= 0); 480 assert(stackPos < stack.length); 481 482 const v = text[$ - 1]; 483 text = text[0 .. $ - 1]; 484 if (v == ',') 485 { 486 break; 487 } 488 else 489 if (stack[stackPos] & 1) 490 { 491 if (v != '{') 492 goto unexpectedValue; 493 currentTapePosition -= ionPutEndR(tape.ptr + currentTapePosition, IonTypeCode.struct_, (stack[stackPos++] >> 1) - currentTapePosition); 494 continue; 495 } 496 else 497 { 498 if (v != '[') 499 goto unexpectedValue; 500 currentTapePosition -= ionPutEndR(tape.ptr + currentTapePosition, IonTypeCode.list, (stack[stackPos++] >> 1) - currentTapePosition); 501 continue; 502 } 503 } 504 } 505 506 ret_final: 507 { 508 symbolTable.finalize; 509 510 import mir.ion.internal.data_holder: ionPrefix; 511 auto extendLength = symbolTable.serializer.data.length + ionPrefix.length; 512 if (_expect(currentTapePosition < extendLength, false)) 513 tape = (cast(ubyte*) realloc(tape.ptr, tape.length + extendLength - currentTapePosition))[0 .. tape.length + extendLength - currentTapePosition]; 514 memmove(tape.ptr + extendLength, tape.ptr + currentTapePosition, tape.length - currentTapePosition); 515 memcpy(tape.ptr, ionPrefix.ptr, ionPrefix.length); 516 memcpy(tape.ptr + ionPrefix.length, symbolTable.serializer.data.ptr, symbolTable.serializer.data.length); 517 tape = tape[0 .. $ + extendLength - currentTapePosition]; 518 // tape = (cast(ubyte*)tape.ptr.realloc(tape.length))[0 .. tape.length]; 519 version (measure) assumePure(&swm.stop)(); 520 version (measure) assumePure(&printSw)(swm); 521 return Stage3Result(tape, IonErrorInfo(errorCode, text.length, /+key+/null)); 522 } 523 524 errorReadingFile: 525 errorCode = IonErrorCode.errorReadingFile; 526 goto ret_final; 527 cant_insert_key: 528 errorCode = IonErrorCode.symbolTableCantInsertKey; 529 goto ret_final; 530 unexpected_comma: 531 errorCode = IonErrorCode.unexpectedComma; 532 goto ret_final; 533 unexpectedEnd: 534 errorCode = IonErrorCode.jsonUnexpectedEnd; 535 goto ret_final; 536 unexpectedValue: 537 errorCode = IonErrorCode.jsonUnexpectedValue; 538 goto ret_final; 539 integerOverflow: 540 errorCode = IonErrorCode.integerOverflow; 541 goto ret_final; 542 unexpected_decimal_value: 543 // _lastError = "unexpected decimal value"; 544 goto unexpectedValue; 545 unexpected_escape_unicode_value: 546 // _lastError = "unexpected escape unicode value"; 547 goto unexpectedValue; 548 unexpected_escape_value: 549 // _lastError = "unexpected escape value"; 550 goto unexpectedValue; 551 object_after_key_is_missing: 552 // _lastError = "expected ':' after key"; 553 goto unexpectedValue; 554 object_key_start_unexpectedValue: 555 // _lastError = "expected '\"' when start parsing object key"; 556 goto unexpectedValue; 557 key_is_to_large: 558 // _lastError = "key length is limited to 255 characters"; 559 goto unexpectedValue; 560 next_unexpectedEnd: 561 assert(stackPos >= 0); 562 assert(stackPos < stack.length); 563 goto unexpectedEnd; 564 next_unexpectedValue: 565 assert(stackPos >= 0); 566 assert(stackPos < stack.length); 567 goto unexpectedValue; 568 value_unexpectedStart: 569 // _lastError = "unexpected character when start parsing JSON value"; 570 goto unexpectedEnd; 571 value_unexpectedEnd: 572 // _lastError = "unexpected end when start parsing JSON value"; 573 goto unexpectedEnd; 574 number_length_unexpectedValue: 575 // _lastError = "number length is limited to 255 characters"; 576 goto unexpectedValue; 577 object_first_value_start_unexpectedEnd: 578 // _lastError = "unexpected end of input data after '{'"; 579 goto unexpectedEnd; 580 array_first_value_start_unexpectedEnd: 581 // _lastError = "unexpected end of input data after '['"; 582 goto unexpectedEnd; 583 false_unexpectedEnd: 584 // _lastError = "unexpected end when parsing 'false'"; 585 goto unexpectedEnd; 586 false_unexpectedValue: 587 // _lastError = "unexpected character when parsing 'false'"; 588 goto unexpectedValue; 589 null_unexpectedEnd: 590 // _lastError = "unexpected end when parsing 'null'"; 591 goto unexpectedEnd; 592 null_unexpectedValue: 593 // _lastError = "unexpected character when parsing 'null'"; 594 goto unexpectedValue; 595 true_unexpectedEnd: 596 // _lastError = "unexpected end when parsing 'true'"; 597 goto unexpectedEnd; 598 true_unexpectedValue: 599 // _lastError = "unexpected character when parsing 'true'"; 600 goto unexpectedValue; 601 string_unexpectedEnd: 602 // _lastError = "unexpected end when parsing string"; 603 goto unexpectedEnd; 604 string_unexpectedValue: 605 // _lastError = "unexpected character when parsing string"; 606 goto unexpectedValue; 607 failed_to_read_after_key: 608 // _lastError = "unexpected end after object key"; 609 goto unexpectedEnd; 610 unexpected_character_after_key: 611 // _lastError = "unexpected character after key"; 612 goto unexpectedValue; 613 string_length_is_too_large: 614 // _lastError = "string size is limited to 2^32-1"; 615 goto unexpectedValue; 616 invalid_trail_surrogate: 617 // _lastError = "invalid UTF-16 trail surrogate"; 618 goto unexpectedValue; 619 invalid_utf_value: 620 // _lastError = "invalid UTF value"; 621 goto unexpectedValue; 622 stack_overflow: 623 // _lastError = "overflow of internal stack"; 624 goto unexpectedValue; 625 } 626 627 private __gshared immutable byte[256] uniFlags = [ 628 // 0 1 2 3 4 5 6 7 8 9 A B C D E F 629 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 0 630 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 1 631 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 2 632 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 3 633 634 -1,10,11,12,13,14,15,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 4 635 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 5 636 -1,10,11,12,13,14,15,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 6 637 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 7 638 639 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 640 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 641 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 642 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 643 644 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 645 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 646 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 647 -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 648 ]; 649 650 @trusted pure nothrow 651 void mir_json2ion( 652 scope const(char)[] text, 653 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe pure nothrow handle, 654 ) 655 { 656 mir_json2ion(text, cast(Stage3Handle) handle); 657 } 658 659 @trusted pure @nogc 660 void mir_json2ion( 661 scope const(char)[] text, 662 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe pure @nogc handle, 663 ) 664 { 665 mir_json2ion(text, cast(Stage3Handle) handle); 666 } 667 668 669 @trusted pure 670 void mir_json2ion( 671 scope const(char)[] text, 672 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe pure handle, 673 ) 674 { 675 mir_json2ion(text, cast(Stage3Handle) handle); 676 } 677 678 679 @trusted nothrow @nogc 680 void mir_json2ion( 681 scope const(char)[] text, 682 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe nothrow @nogc handle, 683 ) 684 { 685 mir_json2ion(text, cast(Stage3Handle) handle); 686 } 687 688 689 @trusted nothrow 690 void mir_json2ion( 691 scope const(char)[] text, 692 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe nothrow handle, 693 ) 694 { 695 mir_json2ion(text, cast(Stage3Handle) handle); 696 } 697 698 @trusted @nogc 699 void mir_json2ion( 700 scope const(char)[] text, 701 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe @nogc handle, 702 ) 703 { 704 mir_json2ion(text, cast(Stage3Handle) handle); 705 } 706 707 708 @trusted 709 void mir_json2ion( 710 scope const(char)[] text, 711 scope void delegate(IonErrorInfo, scope const ubyte[]) @safe handle, 712 ) 713 { 714 mir_json2ion(text, cast(Stage3Handle) handle); 715 } 716 717 718 @system pure nothrow @nogc 719 void mir_json2ion( 720 scope const(char)[] text, 721 scope void delegate(IonErrorInfo, scope const ubyte[]) @system pure nothrow @nogc handle, 722 ) 723 { 724 mir_json2ion(text, cast(Stage3Handle) handle); 725 } 726 727 @system pure nothrow 728 void mir_json2ion( 729 scope const(char)[] text, 730 scope void delegate(IonErrorInfo, scope const ubyte[]) @system pure nothrow handle, 731 ) 732 { 733 mir_json2ion(text, cast(Stage3Handle) handle); 734 } 735 736 @system pure @nogc 737 void mir_json2ion( 738 scope const(char)[] text, 739 scope void delegate(IonErrorInfo, scope const ubyte[]) @system pure @nogc handle, 740 ) 741 { 742 mir_json2ion(text, cast(Stage3Handle) handle); 743 } 744 745 746 @system pure 747 void mir_json2ion( 748 scope const(char)[] text, 749 scope void delegate(IonErrorInfo, scope const ubyte[]) @system pure handle, 750 ) 751 { 752 mir_json2ion(text, cast(Stage3Handle) handle); 753 } 754 755 756 @system nothrow @nogc 757 void mir_json2ion( 758 scope const(char)[] text, 759 scope void delegate(IonErrorInfo, scope const ubyte[]) @system nothrow @nogc handle, 760 ) 761 { 762 mir_json2ion(text, cast(Stage3Handle) handle); 763 } 764 765 766 @system nothrow 767 void mir_json2ion( 768 scope const(char)[] text, 769 scope void delegate(IonErrorInfo, scope const ubyte[]) @system nothrow handle, 770 ) 771 { 772 mir_json2ion(text, cast(Stage3Handle) handle); 773 } 774 775 @system @nogc 776 void mir_json2ion( 777 scope const(char)[] text, 778 scope void delegate(IonErrorInfo, scope const ubyte[]) @system @nogc handle, 779 ) 780 { 781 mir_json2ion(text, cast(Stage3Handle) handle); 782 } 783 784 785 @system 786 void mir_json2ion( 787 scope const(char)[] text, 788 scope void delegate(IonErrorInfo, scope const ubyte[]) @system handle, 789 ) 790 { 791 mir_json2ion(text, cast(Stage3Handle) handle); 792 } 793 794 795 // version = measure; 796 797 version (measure) 798 { 799 import std.datetime.stopwatch; 800 import std.traits; 801 auto assumePure(T)(T t) 802 if (isFunctionPointer!T || isDelegate!T) 803 { 804 pragma(inline, false); 805 enum attrs = functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.nogc | FunctionAttribute.nothrow_; 806 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 807 } 808 809 void printSw(StopWatch sw) 810 { 811 import mir.stdio; 812 tout << sw.peek << endl; 813 } 814 } 815 816 /// 817 version(none) unittest 818 { 819 static ubyte[] jsonToIonTest(scope const(char)[] text) 820 @trusted pure 821 { 822 import mir.serde: SerdeMirException; 823 import mir.ion.exception: ionErrorMsg; 824 import mir.ion.internal.data_holder; 825 import mir.ion.symbol_table; 826 827 enum sizediff_t nMax = 128; 828 829 IonTapeHolder!(nMax * 4) tapeHolder = void; 830 tapeHolder.initialize; 831 832 auto errorInfo = mir_json2ion!nMax(tapeHolder, text); 833 if (errorInfo.code) 834 throw new SerdeMirException(errorInfo.code.ionErrorMsg, ". location = ", errorInfo.location, ", last input key = ", errorInfo.key); 835 836 return tapeHolder.data.dup; 837 } 838 839 import mir.ion.value; 840 import mir.ion.type_code; 841 842 assert(jsonToIonTest("1 2 3") == [0x21, 1, 0x21, 2, 0x21, 3]); 843 assert(IonValue(jsonToIonTest("12345")).describe.get!IonUInt.get!ulong == 12345); 844 assert(IonValue(jsonToIonTest("-12345")).describe.get!IonNInt.get!long == -12345); 845 // assert(IonValue(jsonToIonTest("-12.345")).describe.get!IonDecimal.get!double == -12.345); 846 version (MirDecimalJson) 847 { 848 assert(IonValue(jsonToIonTest("\t \r\n-12345e-3 \t\r\n")).describe.get!IonDecimal.get!double == -12.345); 849 assert(IonValue(jsonToIonTest(" -12345e-3 ")).describe.get!IonDecimal.get!double == -12.345); 850 } 851 else 852 { 853 assert(IonValue(jsonToIonTest("\t \r\n-12345e-3 \t\r\n")).describe.get!IonFloat.get!double == -12.345); 854 assert(IonValue(jsonToIonTest(" -12345e-3 ")).describe.get!IonFloat.get!double == -12.345); 855 } 856 assert(IonValue(jsonToIonTest(" null")).describe.get!IonNull == IonNull(IonTypeCode.null_)); 857 assert(IonValue(jsonToIonTest("true ")).describe.get!bool == true); 858 assert(IonValue(jsonToIonTest(" false")).describe.get!bool == false); 859 assert(IonValue(jsonToIonTest(` "string"`)).describe.get!(const(char)[]) == "string"); 860 861 enum str = "iwfpwqocbpwoewouivhqpeobvnqeon wlekdnfw;lefqoeifhq[woifhdq[owifhq[owiehfq[woiehf[ oiehwfoqwewefiqweopurefhqweoifhqweofihqeporifhq3eufh38hfoidf"; 862 auto data = jsonToIonTest(`"` ~ str ~ `"`); 863 assert(IonValue(jsonToIonTest(`"` ~ str ~ `"`)).describe.get!(const(char)[]) == str); 864 865 assert(IonValue(jsonToIonTest(`"hey \uD801\uDC37tee"`)).describe.get!(const(char)[]) == "hey 𐐷tee"); 866 assert(IonValue(jsonToIonTest(`[]`)).describe.get!IonList.data.length == 0); 867 assert(IonValue(jsonToIonTest(`{}`)).describe.get!IonStruct.data.length == 0); 868 869 // assert(jsonToIonTest(" [ {}, true , \t\r\nfalse, null, \"string\", 12.3 ]") == 870 // cast(ubyte[])"\xbe\x8e\xd0\x11\x10\x0f\x86\x73\x74\x72\x69\x6e\x67\x52\xc1\x7b"); 871 872 data = jsonToIonTest(` { "a": "b", "key": ["array", {"a": "c" } ] } `); 873 assert(data == cast(ubyte[])"\xde\x8f\x8a\x81b\x8b\xba\x85array\xd3\x8a\x81c"); 874 875 data = jsonToIonTest( 876 `{ 877 "tags":[ 878 "russian", 879 "novel", 880 "19th century" 881 ] 882 }`); 883 884 }