The OpenD Programming Language

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 }