1 /++
2 +/
3 // TODO: tape building for Annotations
4 module mir.ion.tape;
5 
6 import core.stdc.string: memmove, memcpy;
7 import mir.bignum.low_level_view;
8 import mir.bitop;
9 import mir.date;
10 import mir.lob;
11 import mir.timestamp: Timestamp;
12 import mir.ion.type_code;
13 import mir.utility: _expect;
14 import std.traits;
15 
16 version(LDC) import ldc.attributes: optStrategy;
17 else private struct optStrategy { string opt; }
18 
19 /++
20 +/
21 size_t ionPutVarUInt(T)(scope ubyte* ptr, const T num)
22     if (isUnsigned!T)
23 {
24     T value = num;
25     enum s = T.sizeof * 8 / 7 + 1;
26     uint len;
27     do ptr[s - 1 - len++] = value & 0x7F;
28     while (value >>>= 7);
29     ptr[s - 1] |= 0x80;
30     if (!__ctfe)
31     {
32         auto arr = *cast(ubyte[s-1]*)(ptr + s - len);
33         *cast(ubyte[s-1]*)ptr = arr;
34     }
35     else
36     {
37         foreach(i; 0 .. len)
38             ptr[i] = ptr[i + s - len];
39     }
40     return len;
41 }
42 
43 ///
44 @system pure nothrow @nogc
45 version(mir_ion_test) unittest
46 {
47     ubyte[19] data = void;
48 
49     alias AliasSeq(T...) = T;
50 
51     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
52     {
53         data[] = 0;
54         assert(ionPutVarUInt!T(data.ptr, 0) == 1);
55         assert(data[0] == 0x80);
56 
57         data[] = 0;
58         assert(ionPutVarUInt!T(data.ptr, 1) == 1);
59         assert(data[0] == 0x81);
60 
61         data[] = 0;
62         assert(ionPutVarUInt!T(data.ptr, 0x7F) == 1);
63         assert(data[0] == 0xFF);
64 
65         data[] = 0;
66         assert(ionPutVarUInt!T(data.ptr, 0xFF) == 2);
67         assert(data[0] == 0x01);
68         assert(data[1] == 0xFF);
69     }
70 
71     foreach(T; AliasSeq!(ushort, uint, ulong))
72     {
73 
74         data[] = 0;
75         assert(ionPutVarUInt!T(data.ptr, 0x3FFF) == 2);
76         assert(data[0] == 0x7F);
77         assert(data[1] == 0xFF);
78 
79         data[] = 0;
80         assert(ionPutVarUInt!T(data.ptr, 0x7FFF) == 3);
81         assert(data[0] == 0x01);
82         assert(data[1] == 0x7F);
83         assert(data[2] == 0xFF);
84 
85         data[] = 0;
86         assert(ionPutVarUInt!T(data.ptr, 0xFFEE) == 3);
87         assert(data[0] == 0x03);
88         assert(data[1] == 0x7F);
89         assert(data[2] == 0xEE);
90     }
91 
92     data[] = 0;
93     assert(ionPutVarUInt(data.ptr, uint.max) == 5);
94     assert(data[0] == 0x0F);
95     assert(data[1] == 0x7F);
96     assert(data[2] == 0x7F);
97     assert(data[3] == 0x7F);
98     assert(data[4] == 0xFF);
99 
100     data[] = 0;
101     assert(ionPutVarUInt!ulong(data.ptr, ulong.max >> 1) == 9);
102     assert(data[0] == 0x7F);
103     assert(data[1] == 0x7F);
104     assert(data[2] == 0x7F);
105     assert(data[3] == 0x7F);
106     assert(data[4] == 0x7F);
107     assert(data[5] == 0x7F);
108     assert(data[6] == 0x7F);
109     assert(data[7] == 0x7F);
110     assert(data[8] == 0xFF);
111 
112     data[] = 0;
113     assert(ionPutVarUInt(data.ptr, ulong.max) == 10);
114     assert(data[0] == 0x01);
115     assert(data[1] == 0x7F);
116     assert(data[2] == 0x7F);
117     assert(data[3] == 0x7F);
118     assert(data[4] == 0x7F);
119     assert(data[5] == 0x7F);
120     assert(data[6] == 0x7F);
121     assert(data[7] == 0x7F);
122     assert(data[8] == 0x7F);
123     assert(data[9] == 0xFF);
124 }
125 
126 /++
127 +/
128 size_t ionPutVarInt(T)(scope ubyte* ptr, const T num)
129     if (isSigned!T)
130 {
131     return .ionPutVarInt!(Unsigned!T)(ptr, num < 0 ? cast(Unsigned!T)(0-num) : num, num < 0);
132 }
133 
134 /++
135 +/
136 size_t ionPutVarInt(T)(scope ubyte* ptr, const T num, bool sign)
137     if (isUnsigned!T)
138 {
139     T value = num; 
140     if (_expect(value < 64, true))
141     {
142         *ptr = cast(ubyte)(value | 0x80 | (sign << 6));
143         return 1;
144     }
145     enum s = T.sizeof * 8 / 7 + 1;
146     size_t len;
147     do ptr[s - 1 - len++] = value & 0x7F;
148     while (value >>>= 7);
149     auto sb = ptr[s - len] >>> 6;
150     auto r = ptr[s - len] & ~(~sb + 1);
151     len += sb;
152     ptr[s - len] = cast(ubyte)r | (cast(ubyte)sign << 6);
153     ptr[s - 1] |= 0x80;
154     auto arr = *cast(ubyte[s-1]*)(ptr + s - len);
155     *cast(ubyte[s-1]*)ptr = arr;
156     return len;
157 }
158 
159 ///
160 @system pure nothrow @nogc
161 version(mir_ion_test) unittest
162 {
163     ubyte[19] data = void;
164 
165     alias AliasSeq(T...) = T;
166 
167     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
168     {
169         data[] = 0;
170         assert(ionPutVarInt!T(data.ptr, 0, false) == 1);
171         assert(data[0] == 0x80);
172 
173         data[] = 0;
174         assert(ionPutVarInt!T(data.ptr, 1, false) == 1);
175         assert(data[0] == 0x81);
176 
177         data[] = 0;
178         assert(ionPutVarInt!T(data.ptr, 1, true) == 1);
179         assert(data[0] == 0xC1);
180 
181         data[] = 0;
182         assert(ionPutVarInt!T(data.ptr, 0x3F, false) == 1);
183         assert(data[0] == 0xBF);
184 
185         data[] = 0;
186         assert(ionPutVarInt!T(data.ptr, 0x3F, true) == 1);
187         assert(data[0] == 0xFF);
188 
189         data[] = 0;
190         assert(ionPutVarInt!T(data.ptr, 0x7F, false) == 2);
191         assert(data[0] == 0x00);
192         assert(data[1] == 0xFF);
193 
194         data[] = 0;
195         assert(ionPutVarInt!T(data.ptr, 128, true) == 2);
196         assert(data[0] == 0x41);
197         assert(data[1] == 0x80);
198 
199         data[] = 0;
200         assert(ionPutVarInt!T(data.ptr, 127, true) == 2);
201         assert(data[0] == 0x40);
202         assert(data[1] == 0xFF);
203 
204 
205         data[] = 0;
206         assert(ionPutVarInt!T(data.ptr, 3, true) == 1);
207         assert(data[0] == 0xC3);
208 
209         data[] = 0;
210         assert(ionPutVarInt!T(data.ptr, 127, true) == 2);
211         assert(data[0] == 0x40);
212         assert(data[1] == 0xFF);
213 
214         data[] = 0;
215         assert(ionPutVarInt!T(data.ptr, 63, true) == 1);
216         assert(data[0] == 0xFF);
217     }
218 
219     data[] = 0;
220     assert(ionPutVarInt!uint(data.ptr, int.max, false) == 5);
221     assert(data[0] == 0x07);
222     assert(data[1] == 0x7F);
223     assert(data[2] == 0x7F);
224     assert(data[3] == 0x7F);
225     assert(data[4] == 0xFF);
226 
227     data[] = 0;
228     assert(ionPutVarInt!uint(data.ptr, int.max, true) == 5);
229     assert(data[0] == 0x47);
230     assert(data[1] == 0x7F);
231     assert(data[2] == 0x7F);
232     assert(data[3] == 0x7F);
233     assert(data[4] == 0xFF);
234 
235     data[] = 0;
236     assert(ionPutVarInt!uint(data.ptr, int.max + 1, true) == 5);
237     assert(data[0] == 0x48);
238     assert(data[1] == 0x00);
239     assert(data[2] == 0x00);
240     assert(data[3] == 0x00);
241     assert(data[4] == 0x80);
242 
243     data[] = 0;
244     assert(ionPutVarInt!ulong(data.ptr, long.max >> 1, false) == 9);
245     assert(data[0] == 0x3F);
246     assert(data[1] == 0x7F);
247     assert(data[2] == 0x7F);
248     assert(data[3] == 0x7F);
249     assert(data[4] == 0x7F);
250     assert(data[5] == 0x7F);
251     assert(data[6] == 0x7F);
252     assert(data[7] == 0x7F);
253     assert(data[8] == 0xFF);
254 
255     data[] = 0;
256     assert(ionPutVarInt!ulong(data.ptr, long.max, false) == 10);
257     assert(data[0] == 0x00);
258     assert(data[1] == 0x7F);
259     assert(data[2] == 0x7F);
260     assert(data[3] == 0x7F);
261     assert(data[4] == 0x7F);
262     assert(data[5] == 0x7F);
263     assert(data[6] == 0x7F);
264     assert(data[7] == 0x7F);
265     assert(data[8] == 0x7F);
266     assert(data[9] == 0xFF);
267 
268     data[] = 0;
269     assert(ionPutVarInt!ulong(data.ptr, long.max, true) == 10);
270     assert(data[0] == 0x40);
271     assert(data[1] == 0x7F);
272     assert(data[2] == 0x7F);
273     assert(data[3] == 0x7F);
274     assert(data[4] == 0x7F);
275     assert(data[5] == 0x7F);
276     assert(data[6] == 0x7F);
277     assert(data[7] == 0x7F);
278     assert(data[8] == 0x7F);
279     assert(data[9] == 0xFF);
280 
281     data[] = 0;
282     assert(ionPutVarInt!ulong(data.ptr, -long.min, true) == 10);
283     assert(data[0] == 0x41);
284     assert(data[1] == 0x00);
285     assert(data[2] == 0x00);
286     assert(data[3] == 0x00);
287     assert(data[4] == 0x00);
288     assert(data[5] == 0x00);
289     assert(data[6] == 0x00);
290     assert(data[7] == 0x00);
291     assert(data[8] == 0x00);
292     assert(data[9] == 0x80);
293 
294     data[] = 0;
295     assert(ionPutVarInt(data.ptr, ulong.max, true) == 10);
296     assert(data[0] == 0x41);
297     assert(data[1] == 0x7F);
298     assert(data[2] == 0x7F);
299     assert(data[3] == 0x7F);
300     assert(data[4] == 0x7F);
301     assert(data[5] == 0x7F);
302     assert(data[6] == 0x7F);
303     assert(data[7] == 0x7F);
304     assert(data[8] == 0x7F);
305     assert(data[9] == 0xFF);
306 
307     data[] = 0;
308     assert(ionPutVarInt(data.ptr, ulong.max, false) == 10);
309     assert(data[0] == 0x01);
310     assert(data[1] == 0x7F);
311     assert(data[2] == 0x7F);
312     assert(data[3] == 0x7F);
313     assert(data[4] == 0x7F);
314     assert(data[5] == 0x7F);
315     assert(data[6] == 0x7F);
316     assert(data[7] == 0x7F);
317     assert(data[8] == 0x7F);
318     assert(data[9] == 0xFF);
319 }
320 
321 /++
322 +/
323 size_t ionPutVarIntR(T)(scope ubyte* ptr, const T num)
324     if (isSigned!T)
325 {
326     return .ionPutVarIntR!(Unsigned!T)(ptr, num < 0 ? cast(Unsigned!T)(0-num) : num, num < 0);
327 }
328 
329 /++
330 +/
331 size_t ionPutVarIntR(T)(scope ubyte* ptr, const T num, bool sign)
332     if (isUnsigned!T)
333 {
334     T value = num; 
335     if (_expect(value < 64, true))
336     {
337         *(ptr - 1) = cast(ubyte)(value | 0x80 | (sign << 6));
338         return 1;
339     }
340     enum s = T.sizeof * 8 / 7 + 1;
341     size_t len;
342     do *(ptr - ++len) = value & 0x7F;
343     while (value >>>= 7);
344     auto sb = *(ptr - len) >>> 6;
345     auto r = *(ptr - len) & ~(~sb + 1);
346     len += sb;
347     *(ptr - len) = cast(ubyte)r | (cast(ubyte)sign << 6);
348     *(ptr - 1) |= 0x80;
349     return len;
350 }
351 
352 ///
353 @system pure nothrow @nogc
354 version(mir_ion_test) unittest
355 {
356     ubyte[10] data = void;
357 
358     alias AliasSeq(T...) = T;
359 
360 
361     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
362     {
363         data[] = 0;
364         assert(ionPutVarIntR!T(data.ptr + 10, 0, false) == 1);
365         assert(data[$ - 1] == 0x80);
366 
367         data[] = 0;
368         assert(ionPutVarIntR!T(data.ptr + 10, 1, false) == 1);
369         assert(data[$ - 1] == 0x81);
370 
371         data[] = 0;
372         assert(ionPutVarIntR!T(data.ptr + 10, 1, true) == 1);
373         assert(data[$ - 1] == 0xC1);
374 
375         data[] = 0;
376         assert(ionPutVarIntR!T(data.ptr + 10, 0x3F, false) == 1);
377         assert(data[$ - 1] == 0xBF);
378 
379         data[] = 0;
380         assert(ionPutVarIntR!T(data.ptr + 10, 0x3F, true) == 1);
381         assert(data[$ - 1] == 0xFF);
382 
383         data[] = 0;
384         assert(ionPutVarIntR!T(data.ptr + 10, 0x7F, false) == 2);
385         assert(data[$ - 2] == 0x00);
386         assert(data[$ - 1] == 0xFF);
387 
388         data[] = 0;
389         assert(ionPutVarIntR!T(data.ptr + 10, 128, true) == 2);
390         assert(data[$ - 2] == 0x41);
391         assert(data[$ - 1] == 0x80);
392 
393         data[] = 0;
394         assert(ionPutVarIntR!T(data.ptr + 10, 127, true) == 2);
395         assert(data[$ - 2] == 0x40);
396         assert(data[$ - 1] == 0xFF);
397 
398 
399         data[] = 0;
400         assert(ionPutVarIntR!T(data.ptr + 10, 3, true) == 1);
401         assert(data[$ - 1] == 0xC3);
402 
403         data[] = 0;
404         assert(ionPutVarIntR!T(data.ptr + 10, 127, true) == 2);
405         assert(data[$ - 2] == 0x40);
406         assert(data[$ - 1] == 0xFF);
407 
408         data[] = 0;
409         assert(ionPutVarIntR!T(data.ptr + 10, 63, true) == 1);
410         assert(data[$ - 1] == 0xFF);
411     }
412 
413     data[] = 0;
414     assert(ionPutVarIntR!uint(data.ptr + 10, int.max, false) == 5);
415     assert(data[5 + 0] == 0x07);
416     assert(data[5 + 1] == 0x7F);
417     assert(data[5 + 2] == 0x7F);
418     assert(data[5 + 3] == 0x7F);
419     assert(data[5 + 4] == 0xFF);
420 
421     data[] = 0;
422     assert(ionPutVarIntR!uint(data.ptr + 10, int.max, true) == 5);
423     assert(data[5 + 0] == 0x47);
424     assert(data[5 + 1] == 0x7F);
425     assert(data[5 + 2] == 0x7F);
426     assert(data[5 + 3] == 0x7F);
427     assert(data[5 + 4] == 0xFF);
428 
429     data[] = 0;
430     assert(ionPutVarIntR!uint(data.ptr + 10, int.max + 1, true) == 5);
431     assert(data[5 + 0] == 0x48);
432     assert(data[5 + 1] == 0x00);
433     assert(data[5 + 2] == 0x00);
434     assert(data[5 + 3] == 0x00);
435     assert(data[5 + 4] == 0x80);
436 
437     data[] = 0;
438     assert(ionPutVarIntR!ulong(data.ptr + 10, long.max >> 1, false) == 9);
439     assert(data[1 + 0] == 0x3F);
440     assert(data[1 + 1] == 0x7F);
441     assert(data[1 + 2] == 0x7F);
442     assert(data[1 + 3] == 0x7F);
443     assert(data[1 + 4] == 0x7F);
444     assert(data[1 + 5] == 0x7F);
445     assert(data[1 + 6] == 0x7F);
446     assert(data[1 + 7] == 0x7F);
447     assert(data[1 + 8] == 0xFF);
448 
449     data[] = 0;
450     assert(ionPutVarIntR!ulong(data.ptr + 10, long.max, false) == 10);
451     assert(data[0] == 0x00);
452     assert(data[1] == 0x7F);
453     assert(data[2] == 0x7F);
454     assert(data[3] == 0x7F);
455     assert(data[4] == 0x7F);
456     assert(data[5] == 0x7F);
457     assert(data[6] == 0x7F);
458     assert(data[7] == 0x7F);
459     assert(data[8] == 0x7F);
460     assert(data[9] == 0xFF);
461 
462     data[] = 0;
463     assert(ionPutVarIntR!ulong(data.ptr + 10, long.max, true) == 10);
464     assert(data[0] == 0x40);
465     assert(data[1] == 0x7F);
466     assert(data[2] == 0x7F);
467     assert(data[3] == 0x7F);
468     assert(data[4] == 0x7F);
469     assert(data[5] == 0x7F);
470     assert(data[6] == 0x7F);
471     assert(data[7] == 0x7F);
472     assert(data[8] == 0x7F);
473     assert(data[9] == 0xFF);
474 
475     data[] = 0;
476     assert(ionPutVarIntR!ulong(data.ptr + 10, -long.min, true) == 10);
477     assert(data[0] == 0x41);
478     assert(data[1] == 0x00);
479     assert(data[2] == 0x00);
480     assert(data[3] == 0x00);
481     assert(data[4] == 0x00);
482     assert(data[5] == 0x00);
483     assert(data[6] == 0x00);
484     assert(data[7] == 0x00);
485     assert(data[8] == 0x00);
486     assert(data[9] == 0x80);
487 
488     data[] = 0;
489     assert(ionPutVarIntR(data.ptr + 10, ulong.max, true) == 10);
490     assert(data[0] == 0x41);
491     assert(data[1] == 0x7F);
492     assert(data[2] == 0x7F);
493     assert(data[3] == 0x7F);
494     assert(data[4] == 0x7F);
495     assert(data[5] == 0x7F);
496     assert(data[6] == 0x7F);
497     assert(data[7] == 0x7F);
498     assert(data[8] == 0x7F);
499     assert(data[9] == 0xFF);
500 
501     data[] = 0;
502     assert(ionPutVarIntR(data.ptr + 10, ulong.max, false) == 10);
503     assert(data[0] == 0x01);
504     assert(data[1] == 0x7F);
505     assert(data[2] == 0x7F);
506     assert(data[3] == 0x7F);
507     assert(data[4] == 0x7F);
508     assert(data[5] == 0x7F);
509     assert(data[6] == 0x7F);
510     assert(data[7] == 0x7F);
511     assert(data[8] == 0x7F);
512     assert(data[9] == 0xFF);
513 }
514 
515 /++
516 +/
517 @optStrategy("optsize")
518 size_t ionPutUIntField()(
519     scope ubyte* ptr,
520     BigUIntView!(const size_t) value,
521     )
522 {
523     auto data = value.coefficients;
524     size_t ret;
525     static if (size_t.sizeof > 1)
526     {
527         if (data.length)
528         {
529             ret = .ionPutUIntField(ptr, data[$ - 1]);
530             data = data[0 .. $ - 1];
531         }
532     }
533     foreach_reverse (d; data)
534     {
535         *cast(ubyte[size_t.sizeof]*)(ptr + ret) = byteData(d);
536         ret += size_t.sizeof;
537     }
538     return ret;
539 }
540 
541 /++
542 +/
543 size_t ionPutUIntField(T)(scope ubyte* ptr, const T num)
544     if (isUnsigned!T && T.sizeof >= 4)
545 {
546     T value = num;
547     auto c = cast(size_t)ctlzp(value);
548     enum ubyte cm = 0xF8 & (value.sizeof * 8 - 1);
549     value <<= c & cm;
550     c >>>= 3;
551     *cast(ubyte[T.sizeof]*)ptr = byteData(value);
552     return T.sizeof - c;
553 }
554 
555 /++
556 +/
557 size_t ionPutUIntField(T)(scope ubyte* ptr, const T num)
558     if (is(T == ubyte))
559 {
560     *ptr = num;
561     return num != 0;
562 }
563 
564 /++
565 +/
566 size_t ionPutUIntField(T)(scope ubyte* ptr, const T num)
567     if (is(T == ushort))
568 {
569     return ionPutUIntField!uint(ptr, num);
570 }
571 
572 ///
573 @system pure nothrow @nogc
574 version(mir_ion_test) unittest
575 {
576     ubyte[8] data;
577 
578     alias AliasSeq(T...) = T;
579 
580     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
581     {
582         data[] = 0;
583         assert(ionPutUIntField!T(data.ptr, 0) == 0);
584 
585         data[] = 0;
586         assert(ionPutUIntField!T(data.ptr, 1) == 1);
587         assert(data[0] == 0x01);
588 
589         data[] = 0;
590         assert(ionPutUIntField!T(data.ptr, 0x3F) == 1);
591         assert(data[0] == 0x3F);
592 
593         data[] = 0;
594         assert(ionPutUIntField!T(data.ptr, 0xFF) == 1);
595         assert(data[0] == 0xFF);
596 
597         data[] = 0;
598         assert(ionPutUIntField!T(data.ptr, 0x80) == 1);
599         assert(data[0] == 0x80);
600     }
601 
602     data[] = 0;
603     assert(ionPutUIntField!uint(data.ptr, int.max) == 4);
604     assert(data[0] == 0x7F);
605     assert(data[1] == 0xFF);
606     assert(data[2] == 0xFF);
607     assert(data[3] == 0xFF);
608 
609     data[] = 0;
610     assert(ionPutUIntField!uint(data.ptr, int.max + 1) == 4);
611     assert(data[0] == 0x80);
612     assert(data[1] == 0x00);
613     assert(data[2] == 0x00);
614     assert(data[3] == 0x00);
615 
616     data[] = 0;
617     assert(ionPutUIntField!ulong(data.ptr, long.max >> 1) == 8);
618     assert(data[0] == 0x3F);
619     assert(data[1] == 0xFF);
620     assert(data[2] == 0xFF);
621     assert(data[3] == 0xFF);
622     assert(data[4] == 0xFF);
623     assert(data[5] == 0xFF);
624     assert(data[6] == 0xFF);
625     assert(data[7] == 0xFF);
626 
627     data[] = 0;
628     assert(ionPutUIntField!ulong(data.ptr, long.max) == 8);
629     assert(data[0] == 0x7F);
630     assert(data[1] == 0xFF);
631     assert(data[2] == 0xFF);
632     assert(data[3] == 0xFF);
633     assert(data[4] == 0xFF);
634     assert(data[5] == 0xFF);
635     assert(data[6] == 0xFF);
636     assert(data[7] == 0xFF);
637 
638     data[] = 0;
639     assert(ionPutUIntField!ulong(data.ptr, long.max + 1) == 8);
640     assert(data[0] == 0x80);
641     assert(data[1] == 0x00);
642     assert(data[2] == 0x00);
643     assert(data[3] == 0x00);
644     assert(data[4] == 0x00);
645     assert(data[5] == 0x00);
646     assert(data[6] == 0x00);
647     assert(data[7] == 0x00);
648 
649     data[] = 0;
650     assert(ionPutUIntField(data.ptr, ulong.max) == 8);
651     assert(data[0] == 0xFF);
652     assert(data[1] == 0xFF);
653     assert(data[2] == 0xFF);
654     assert(data[3] == 0xFF);
655     assert(data[4] == 0xFF);
656     assert(data[5] == 0xFF);
657     assert(data[6] == 0xFF);
658     assert(data[7] == 0xFF);
659 }
660 
661 
662 /++
663 +/
664 size_t ionPutUIntFieldR(T)(scope ubyte* ptr, const T num)
665     if (isUnsigned!T && T.sizeof >= 4)
666 {
667     T value = num;
668     auto c = cast(size_t)ctlzp(value);
669     enum ubyte cm = 0xF8 & (value.sizeof * 8 - 1);
670     c >>>= 3;
671     *cast(ubyte[T.sizeof]*)(ptr - T.sizeof) = byteData(value);
672     return T.sizeof - c;
673 }
674 
675 /++
676 +/
677 size_t ionPutUIntFieldR(T)(scope ubyte* ptr, const T num)
678     if (is(T == ubyte))
679 {
680     *(ptr - 1) = num;
681     return num != 0;
682 }
683 
684 /++
685 +/
686 size_t ionPutUIntFieldR(T)(scope ubyte* ptr, const T num)
687     if (is(T == ushort))
688 {
689     return ionPutUIntFieldR!uint(ptr, num);
690 }
691 
692 
693 ///
694 @system pure nothrow @nogc
695 version(mir_ion_test) unittest
696 {
697     ubyte[8] data;
698 
699     alias AliasSeq(T...) = T;
700 
701     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
702     {
703         data[] = 0;
704         assert(ionPutUIntFieldR!T(data.ptr + data.length, 0) == 0);
705 
706         data[] = 0;
707         assert(ionPutUIntFieldR!T(data.ptr + data.length, 1) == 1);
708         assert(data[$ - 1] == 0x01);
709 
710         data[] = 0;
711         assert(ionPutUIntFieldR!T(data.ptr + data.length, 0x3F) == 1);
712         assert(data[$ - 1] == 0x3F);
713 
714         data[] = 0;
715         assert(ionPutUIntFieldR!T(data.ptr + data.length, 0xFF) == 1);
716         assert(data[$ - 1] == 0xFF);
717 
718         data[] = 0;
719         assert(ionPutUIntFieldR!T(data.ptr + data.length, 0x80) == 1);
720         assert(data[$ - 1] == 0x80);
721     }
722 
723     data[] = 0;
724     assert(ionPutUIntFieldR!uint(data.ptr + data.length, int.max) == 4);
725     assert(data[4 + 0] == 0x7F);
726     assert(data[4 + 1] == 0xFF);
727     assert(data[4 + 2] == 0xFF);
728     assert(data[4 + 3] == 0xFF);
729 
730     data[] = 0;
731     assert(ionPutUIntFieldR!uint(data.ptr + data.length, int.max + 1) == 4);
732     assert(data[4 + 0] == 0x80);
733     assert(data[4 + 1] == 0x00);
734     assert(data[4 + 2] == 0x00);
735     assert(data[4 + 3] == 0x00);
736 
737     data[] = 0;
738     assert(ionPutUIntFieldR!ulong(data.ptr + data.length, long.max >> 1) == 8);
739     assert(data[0] == 0x3F);
740     assert(data[1] == 0xFF);
741     assert(data[2] == 0xFF);
742     assert(data[3] == 0xFF);
743     assert(data[4] == 0xFF);
744     assert(data[5] == 0xFF);
745     assert(data[6] == 0xFF);
746     assert(data[7] == 0xFF);
747 
748     data[] = 0;
749     assert(ionPutUIntFieldR!ulong(data.ptr + data.length, long.max) == 8);
750     assert(data[0] == 0x7F);
751     assert(data[1] == 0xFF);
752     assert(data[2] == 0xFF);
753     assert(data[3] == 0xFF);
754     assert(data[4] == 0xFF);
755     assert(data[5] == 0xFF);
756     assert(data[6] == 0xFF);
757     assert(data[7] == 0xFF);
758 
759     data[] = 0;
760     assert(ionPutUIntFieldR!ulong(data.ptr + data.length, long.max + 1) == 8);
761     assert(data[0] == 0x80);
762     assert(data[1] == 0x00);
763     assert(data[2] == 0x00);
764     assert(data[3] == 0x00);
765     assert(data[4] == 0x00);
766     assert(data[5] == 0x00);
767     assert(data[6] == 0x00);
768     assert(data[7] == 0x00);
769 
770     data[] = 0;
771     assert(ionPutUIntFieldR(data.ptr + data.length, ulong.max) == 8);
772     assert(data[0] == 0xFF);
773     assert(data[1] == 0xFF);
774     assert(data[2] == 0xFF);
775     assert(data[3] == 0xFF);
776     assert(data[4] == 0xFF);
777     assert(data[5] == 0xFF);
778     assert(data[6] == 0xFF);
779     assert(data[7] == 0xFF);
780 }
781 
782 /++
783 +/
784 @optStrategy("optsize")
785 size_t ionPutIntField()(
786     scope ubyte* ptr,
787     BigIntView!(const size_t) value,
788     )
789 {
790     auto data = value.unsigned.coefficients;
791     if (data.length == 0)
792     {
793         *ptr = value.sign << 7;
794         return value.sign;
795     }
796     size_t ret = .ionPutIntField(ptr, data[$ - 1], value.sign);
797     data = data[0 .. $ - 1];
798     foreach_reverse (d; data)
799     {
800         *cast(ubyte[size_t.sizeof]*)(ptr + ret) = byteData(d);
801         ret += size_t.sizeof;
802     }
803     return ret;
804 }
805 
806 /++
807 +/
808 size_t ionPutIntField(T)(scope ubyte* ptr, const T num)
809     if (isSigned!T && isIntegral!T)
810 {
811     T value = num;
812     bool sign = value < 0;
813     if (sign)
814         value = cast(T)(0-value);
815     return ionPutIntField!(Unsigned!T)(ptr, value, sign);
816 }
817 
818 /++
819 +/
820 size_t ionPutIntField(T)(scope ubyte* ptr, const T num, bool sign)
821     if (isUnsigned!T)
822 {
823     T value = num;
824     static if (T.sizeof >= 4)
825     {
826         auto c = cast(uint)ctlzp(value);
827         bool s = (c & 0x7) == 0;
828         *ptr = sign << 7;
829         ptr += s;
830         enum ubyte cm = 0xF8 & (value.sizeof * 8 - 1);
831         value <<= c & cm;
832         c >>>= 3;
833         value |= T(sign) << (T.sizeof * 8 - 1);
834         c = uint(T.sizeof) - c + s - (value == 0);
835         *cast(ubyte[T.sizeof]*)ptr = byteData(value);
836         return c;
837     }
838     else
839     {
840         return ionPutIntField!uint(ptr, value, sign);
841     }
842 }
843 
844 ///
845 @system pure nothrow @nogc
846 version(mir_ion_test) unittest
847 {
848     ubyte[9] data;
849 
850     alias AliasSeq(T...) = T;
851 
852     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
853     {
854         data[] = 0;
855         assert(ionPutIntField!T(data.ptr, 0, false) == 0, T.stringof);
856 
857         data[] = 0;
858         assert(ionPutIntField!T(data.ptr, 0, true) == 1);
859         assert(data[0] == 0x80);
860 
861         data[] = 0;
862         assert(ionPutIntField!T(data.ptr, 1, false) == 1);
863         assert(data[0] == 0x01);
864 
865         data[] = 0;
866         assert(ionPutIntField!T(data.ptr, 1, true) == 1);
867         assert(data[0] == 0x81);
868 
869         data[] = 0;
870         assert(ionPutIntField!T(data.ptr, 0x3F, true) == 1);
871         assert(data[0] == 0xBF);
872 
873         data[] = 0;
874         assert(ionPutIntField!T(data.ptr, 0xFF, false) == 2);
875         assert(data[0] == 0x00);
876         assert(data[1] == 0xFF);
877 
878         data[] = 0;
879         assert(ionPutIntField!T(data.ptr, 0xFF, true) == 2);
880         assert(data[0] == 0x80);
881         assert(data[1] == 0xFF);
882 
883         data[] = 0;
884         assert(ionPutIntField!T(data.ptr, 0x80, true) == 2);
885         assert(data[0] == 0x80);
886         assert(data[1] == 0x80);
887     }
888 
889     data[] = 0;
890     assert(ionPutIntField(data.ptr, int.max) == 4);
891     assert(data[0] == 0x7F);
892     assert(data[1] == 0xFF);
893     assert(data[2] == 0xFF);
894     assert(data[3] == 0xFF);
895 
896     data[] = 0;
897     assert(ionPutIntField(data.ptr, int.min) == 5);
898     assert(data[0] == 0x80);
899     assert(data[1] == 0x80);
900     assert(data[2] == 0x00);
901     assert(data[3] == 0x00);
902     assert(data[4] == 0x00);
903 
904     data[] = 0;
905     assert(ionPutIntField(data.ptr, long.max >> 1) == 8);
906     assert(data[0] == 0x3F);
907     assert(data[1] == 0xFF);
908     assert(data[2] == 0xFF);
909     assert(data[3] == 0xFF);
910     assert(data[4] == 0xFF);
911     assert(data[5] == 0xFF);
912     assert(data[6] == 0xFF);
913     assert(data[7] == 0xFF);
914 
915     data[] = 0;
916     assert(ionPutIntField(data.ptr, ulong(long.max >> 1), true) == 8);
917     assert(data[0] == 0xBF);
918     assert(data[1] == 0xFF);
919     assert(data[2] == 0xFF);
920     assert(data[3] == 0xFF);
921     assert(data[4] == 0xFF);
922     assert(data[5] == 0xFF);
923     assert(data[6] == 0xFF);
924     assert(data[7] == 0xFF);
925 
926     data[] = 0;
927     assert(ionPutIntField(data.ptr, long.max) == 8);
928     assert(data[0] == 0x7F);
929     assert(data[1] == 0xFF);
930     assert(data[2] == 0xFF);
931     assert(data[3] == 0xFF);
932     assert(data[4] == 0xFF);
933     assert(data[5] == 0xFF);
934     assert(data[6] == 0xFF);
935     assert(data[7] == 0xFF);
936 
937     data[] = 0;
938     assert(ionPutIntField(data.ptr, ulong(long.max), true) == 8);
939     assert(data[0] == 0xFF);
940     assert(data[1] == 0xFF);
941     assert(data[2] == 0xFF);
942     assert(data[3] == 0xFF);
943     assert(data[4] == 0xFF);
944     assert(data[5] == 0xFF);
945     assert(data[6] == 0xFF);
946     assert(data[7] == 0xFF);
947 
948     data[] = 0;
949     assert(ionPutIntField!ulong(data.ptr, long.max + 1, false) == 9);
950     assert(data[0] == 0x00);
951     assert(data[1] == 0x80);
952     assert(data[2] == 0x00);
953     assert(data[3] == 0x00);
954     assert(data[4] == 0x00);
955     assert(data[5] == 0x00);
956     assert(data[6] == 0x00);
957     assert(data[7] == 0x00);
958     assert(data[8] == 0x00);
959 
960     data[] = 0;
961     assert(ionPutIntField(data.ptr, ulong.max, true) == 9);
962     assert(data[0] == 0x80);
963     assert(data[1] == 0xFF);
964     assert(data[2] == 0xFF);
965     assert(data[3] == 0xFF);
966     assert(data[4] == 0xFF);
967     assert(data[5] == 0xFF);
968     assert(data[6] == 0xFF);
969     assert(data[7] == 0xFF);
970     assert(data[8] == 0xFF);
971 }
972 
973 /++
974 +/
975 size_t ionPutIntFieldR(T)(scope ubyte* ptr, const T num)
976     if (isSigned!T && isIntegral!T)
977 {
978     T value = num;
979     bool sign = value < 0;
980     if (sign)
981         value = cast(T)(0-value);
982     return ionPutIntFieldR!(Unsigned!T)(ptr, value, sign);
983 }
984 
985 /++
986 +/
987 size_t ionPutIntFieldR(T)(scope ubyte* ptr, const T num, bool sign)
988     if (isUnsigned!T)
989 {
990     T value = num;
991     static if (T.sizeof >= 4)
992     {
993         auto c = cast(uint)ctlzp(value);
994         bool s = (c & 0x7) == 0;
995         enum ubyte cm = 0xF8 & (value.sizeof * 8 - 1);
996         c >>>= 3;
997         c = uint(T.sizeof) - c + s - ((value | sign) == 0);
998         value |= T(sign) << ((c - (c > T.sizeof)) * 8 - 1);
999         *cast(ubyte[T.sizeof]*)(ptr - T.sizeof) = byteData(value);
1000         *(ptr - T.sizeof - 1) = sign << 7;
1001         return c;
1002     }
1003     else
1004     {
1005         return ionPutIntFieldR!uint(ptr, value, sign);
1006     }
1007 }
1008 
1009 ///
1010 @system pure nothrow @nogc
1011 version(mir_ion_test) unittest
1012 {
1013     ubyte[9] data;
1014 
1015     alias AliasSeq(T...) = T;
1016 
1017     foreach(T; AliasSeq!(ubyte, ushort, uint, ulong))
1018     {
1019         data[] = 0;
1020         assert(ionPutIntFieldR!T(data.ptr + 9, 0, false) == 0, T.stringof);
1021 
1022         data[] = 0;
1023         assert(ionPutIntFieldR!T(data.ptr + 9, 0, true) == 1);
1024         assert(data[$ - 1] == 0x80);
1025 
1026         data[] = 0;
1027         assert(ionPutIntFieldR!T(data.ptr + 9, 1, false) == 1);
1028         assert(data[$ - 1] == 0x01);
1029 
1030         data[] = 0;
1031         assert(ionPutIntFieldR!T(data.ptr + 9, 1, true) == 1);
1032         assert(data[$ - 1] == 0x81);
1033 
1034         data[] = 0;
1035         assert(ionPutIntFieldR!T(data.ptr + 9, 0x3F, true) == 1);
1036         assert(data[$ - 1] == 0xBF);
1037 
1038         data[] = 0;
1039         assert(ionPutIntFieldR!T(data.ptr + 9, 0xFF, false) == 2);
1040         assert(data[$ - 2] == 0x00);
1041         assert(data[$ - 1] == 0xFF);
1042 
1043         data[] = 0;
1044         assert(ionPutIntFieldR!T(data.ptr + 9, 0xFF, true) == 2);
1045         assert(data[$ - 2] == 0x80);
1046         assert(data[$ - 1] == 0xFF);
1047 
1048         data[] = 0;
1049         assert(ionPutIntFieldR!T(data.ptr + 9, 0x80, true) == 2);
1050         assert(data[$ - 2] == 0x80);
1051         assert(data[$ - 1] == 0x80);
1052     }
1053 
1054     data[] = 0;
1055     assert(ionPutIntFieldR(data.ptr + 9, int.max) == 4);
1056     assert(data[5 + 0] == 0x7F);
1057     assert(data[5 + 1] == 0xFF);
1058     assert(data[5 + 2] == 0xFF);
1059     assert(data[5 + 3] == 0xFF);
1060 
1061     data[] = 0;
1062     assert(ionPutIntFieldR(data.ptr + 9, int.min) == 5);
1063     assert(data[4 + 0] == 0x80);
1064     assert(data[4 + 1] == 0x80);
1065     assert(data[4 + 2] == 0x00);
1066     assert(data[4 + 3] == 0x00);
1067     assert(data[4 + 4] == 0x00);
1068 
1069     data[] = 0;
1070     assert(ionPutIntFieldR(data.ptr + 9, long.max >> 1) == 8);
1071     assert(data[1 + 0] == 0x3F);
1072     assert(data[1 + 1] == 0xFF);
1073     assert(data[1 + 2] == 0xFF);
1074     assert(data[1 + 3] == 0xFF);
1075     assert(data[1 + 4] == 0xFF);
1076     assert(data[1 + 5] == 0xFF);
1077     assert(data[1 + 6] == 0xFF);
1078     assert(data[1 + 7] == 0xFF);
1079 
1080     data[] = 0;
1081     assert(ionPutIntFieldR(data.ptr + 9, long.max) == 8);
1082     assert(data[1 + 0] == 0x7F);
1083     assert(data[1 + 1] == 0xFF);
1084     assert(data[1 + 2] == 0xFF);
1085     assert(data[1 + 3] == 0xFF);
1086     assert(data[1 + 4] == 0xFF);
1087     assert(data[1 + 5] == 0xFF);
1088     assert(data[1 + 6] == 0xFF);
1089     assert(data[1 + 7] == 0xFF);
1090 
1091     data[] = 0xFF;
1092     assert(ionPutIntFieldR!ulong(data.ptr + 9, long.max + 1, false) == 9);
1093     assert(data[0] == 0x00);
1094     assert(data[1] == 0x80);
1095     assert(data[2] == 0x00);
1096     assert(data[3] == 0x00);
1097     assert(data[4] == 0x00);
1098     assert(data[5] == 0x00);
1099     assert(data[6] == 0x00);
1100     assert(data[7] == 0x00);
1101     assert(data[8] == 0x00);
1102 
1103     data[] = 0;
1104     assert(ionPutIntFieldR(data.ptr + 9, ulong.max, true) == 9);
1105     assert(data[0] == 0x80);
1106     assert(data[1] == 0xFF);
1107     assert(data[2] == 0xFF);
1108     assert(data[3] == 0xFF);
1109     assert(data[4] == 0xFF);
1110     assert(data[5] == 0xFF);
1111     assert(data[6] == 0xFF);
1112     assert(data[7] == 0xFF);
1113     assert(data[8] == 0xFF);
1114 }
1115 
1116 /++
1117 +/
1118 size_t ionPut(scope ubyte* ptr, typeof(null), IonTypeCode typeCode = IonTypeCode.null_) pure nothrow @nogc
1119 {
1120     *ptr++ = cast(ubyte) (0x0F | (typeCode << 4));
1121     return 1;
1122 }
1123 
1124 ///
1125 // @system pure nothrow @nogc
1126 version(mir_ion_test) unittest
1127 {
1128     ubyte[1] data;
1129     assert(ionPut(data.ptr, null) == 1);
1130     import mir.conv;
1131     assert(data[0] == 0x0F, data[0].to!string);
1132 }
1133 
1134 /++
1135 +/
1136 size_t ionPut(T : bool)(scope ubyte* ptr, const T value)
1137 {
1138     *ptr++ = 0x10 | value;
1139     return 1;
1140 }
1141 
1142 ///
1143 @system pure nothrow @nogc
1144 version(mir_ion_test) unittest
1145 {
1146     ubyte[1] data;
1147     assert(ionPut(data.ptr, true) == 1);
1148     assert(data[0] == 0x11);
1149     assert(ionPut(data.ptr, false) == 1);
1150     assert(data[0] == 0x10);
1151 }
1152 
1153 /++
1154 +/
1155 size_t ionPut(T)(scope ubyte* ptr, const T value, bool sign = false)
1156     if (isUnsigned!T)
1157 {
1158     auto L = ionPutUIntField!T(ptr + 1, value);
1159     static if (T.sizeof <= 8)
1160     {
1161         *ptr = cast(ubyte) (0x20 | (sign << 4) | L);
1162         return L + 1;
1163     }
1164     else
1165     {
1166         static assert(0, "cent and ucent types not supported by mir.ion for now");
1167     }
1168 }
1169 
1170 ///
1171 @system pure nothrow @nogc
1172 version(mir_ion_test) unittest
1173 {
1174     ubyte[19] data = void;
1175     assert(ionPut(data.ptr, 0u) == 1);
1176     assert(data[0] == 0x20);
1177     assert(ionPut(data.ptr, 0u, true) == 1);
1178     assert(data[0] == 0x30);
1179     assert(ionPut(data.ptr, 0xFFu) == 2);
1180     assert(data[0] == 0x21);
1181     assert(data[1] == 0xFF);
1182     assert(ionPut(data.ptr, 0xFFu, true) == 2);
1183     assert(data[0] == 0x31);
1184     assert(data[1] == 0xFF);
1185 
1186     assert(ionPut(data.ptr, ulong.max, true) == 9);
1187     assert(data[0] == 0x38);
1188     assert(data[1] == 0xFF);
1189     assert(data[2] == 0xFF);
1190     assert(data[3] == 0xFF);
1191     assert(data[4] == 0xFF);
1192     assert(data[5] == 0xFF);
1193     assert(data[6] == 0xFF);
1194     assert(data[7] == 0xFF);
1195     assert(data[8] == 0xFF);
1196 }
1197 
1198 /++
1199 +/
1200 size_t ionPutR(T)(scope ubyte* ptr, const T value, bool sign = false)
1201     if (isUnsigned!T)
1202 {
1203     auto L = ionPutUIntFieldR!T(ptr, value);
1204     static if (T.sizeof <= 8)
1205     {
1206         *(ptr - (L + 1)) = cast(ubyte) (0x20 | (sign << 4) | L);
1207         return L + 1;
1208     }
1209     else
1210     {
1211         static assert(0, "cent and ucent types not supported by mir.ion for now");
1212     }
1213 }
1214 
1215 ///
1216 @system pure nothrow @nogc
1217 version(mir_ion_test) unittest
1218 {
1219     ubyte[19] data = void;
1220     assert(ionPutR(data.ptr + 1, 0u) == 1);
1221     assert(data[0] == 0x20);
1222     assert(ionPutR(data.ptr + 1, 0u, true) == 1);
1223     assert(data[0] == 0x30);
1224     assert(ionPutR(data.ptr + 2, 0xFFu) == 2);
1225     assert(data[0] == 0x21);
1226     assert(data[1] == 0xFF);
1227     assert(ionPutR(data.ptr + 2, 0xFFu, true) == 2);
1228     assert(data[0] == 0x31);
1229     assert(data[1] == 0xFF);
1230 
1231     assert(ionPutR(data.ptr + 9, ulong.max, true) == 9);
1232     assert(data[0] == 0x38);
1233     assert(data[1] == 0xFF);
1234     assert(data[2] == 0xFF);
1235     assert(data[3] == 0xFF);
1236     assert(data[4] == 0xFF);
1237     assert(data[5] == 0xFF);
1238     assert(data[6] == 0xFF);
1239     assert(data[7] == 0xFF);
1240     assert(data[8] == 0xFF);
1241 }
1242 
1243 /++
1244 +/
1245 size_t ionPut(T)(scope ubyte* ptr, const T value)
1246     if (isSigned!T && isIntegral!T)
1247 {
1248     bool sign = value < 0;
1249     T num = value;
1250     if (sign)
1251         num = cast(T)(0-num);
1252     return ionPut!(Unsigned!T)(ptr, num, sign);
1253 }
1254 
1255 ///
1256 @("ionPutInteger")
1257 @system pure nothrow @nogc
1258 version(mir_ion_test) unittest
1259 {
1260     ubyte[19] data = void;
1261     assert(ionPut(data.ptr, -16) == 2);
1262     assert(data[0] == 0x31);
1263     assert(data[1] == 0x10);
1264 
1265     assert(ionPut(data.ptr, 258) == 3);
1266     assert(data[0] == 0x22);
1267     assert(data[1] == 0x01);
1268     assert(data[2] == 0x02);
1269 }
1270 
1271 private auto byteData(T)(const T value)
1272     if (__traits(isUnsigned, T))
1273 {
1274     static if (T.sizeof == 1)
1275     {
1276         T num = value;
1277     }
1278     else
1279     version (LittleEndian)
1280     {
1281         import core.bitop: bswap;
1282         T num = bswap(value);
1283     }
1284     else
1285     {
1286         T num = value;
1287     }
1288 
1289     ubyte[T.sizeof] data;
1290 
1291     if (__ctfe)
1292     {
1293         foreach (ref d; data)
1294         {
1295             d = num & 0xFF;
1296             num >>= 8;
1297         }
1298     }
1299     else
1300     {
1301         data = cast(ubyte[T.sizeof])cast(T[1])[num];
1302     }
1303 
1304     return data;
1305 }
1306 
1307 /++
1308 +/
1309 size_t ionPut(T)(scope ubyte* ptr, const T value)
1310     if (is(T == float))
1311 {
1312     auto num = *cast(uint*)&value;
1313     auto s = (num != 0) << 2;
1314     *ptr = cast(ubyte)(0x40 + s);
1315     *cast(ubyte[4]*)(ptr + 1) = byteData(num);
1316     return 1 + s;
1317 }
1318 
1319 ///
1320 @system pure nothrow @nogc
1321 version(mir_ion_test) unittest
1322 {
1323     ubyte[5] data;
1324     assert(ionPut(data.ptr, -16f) == 5);
1325     assert(data[0] == 0x44);
1326     assert(data[1] == 0xC1);
1327     assert(data[2] == 0x80);
1328     assert(data[3] == 0x00);
1329     assert(data[4] == 0x00);
1330 
1331     assert(ionPut(data.ptr, 0f) == 1);
1332     assert(data[0] == 0x40);
1333 
1334     assert(ionPut(data.ptr, -0f) == 5);
1335     assert(data[0] == 0x44);
1336     assert(data[1] == 0x80);
1337     assert(data[2] == 0x00);
1338     assert(data[3] == 0x00);
1339     assert(data[4] == 0x00);
1340 }
1341 
1342 /++
1343 +/
1344 size_t ionPut(T)(scope ubyte* ptr, const T value)
1345     if (is(T == double))
1346 {
1347     auto num = *cast(ulong*)&value;
1348     auto s = (num != 0) << 3;
1349     *ptr = cast(ubyte)(0x40 + s);
1350     *cast(ubyte[8]*)(ptr + 1) = byteData(num);
1351     return 1 + s;
1352 }
1353 
1354 ///
1355 @system pure nothrow @nogc
1356 version(mir_ion_test) unittest
1357 {
1358     ubyte[9] data;
1359     assert(ionPut(data.ptr, -16.0) == 9);
1360     assert(data[0] == 0x48);
1361     assert(data[1] == 0xC0);
1362     assert(data[2] == 0x30);
1363     assert(data[3] == 0x00);
1364     assert(data[4] == 0x00);
1365     assert(data[5] == 0x00);
1366     assert(data[6] == 0x00);
1367     assert(data[7] == 0x00);
1368     assert(data[8] == 0x00);
1369 
1370     assert(ionPut(data.ptr, 0.0) == 1);
1371     assert(data[0] == 0x40);
1372 
1373     assert(ionPut(data.ptr, -0.0) == 9);
1374     assert(data[0] == 0x48);
1375     assert(data[1] == 0x80);
1376     assert(data[2] == 0x00);
1377     assert(data[3] == 0x00);
1378     assert(data[4] == 0x00);
1379     assert(data[5] == 0x00);
1380     assert(data[6] == 0x00);
1381     assert(data[7] == 0x00);
1382     assert(data[8] == 0x00);
1383 }
1384 
1385 /++
1386 +/
1387 size_t ionPutR(T)(scope ubyte* ptr, const T value)
1388     if (is(T == double))
1389 {
1390     auto num = *cast(ulong*)&value;
1391     auto s = (num != 0) << 3;
1392     *cast(ubyte[8]*)(ptr - double.sizeof) = byteData(num);
1393     *(ptr - (s + 1)) = cast(ubyte)(0x40 + s);
1394     return s + 1;
1395 }
1396 
1397 ///
1398 @system pure nothrow @nogc
1399 version(mir_ion_test) unittest
1400 {
1401     ubyte[9] data;
1402     assert(ionPutR(data.ptr + data.length, -16.0) == 9);
1403     assert(data[0] == 0x48);
1404     assert(data[1] == 0xC0);
1405     assert(data[2] == 0x30);
1406     assert(data[3] == 0x00);
1407     assert(data[4] == 0x00);
1408     assert(data[5] == 0x00);
1409     assert(data[6] == 0x00);
1410     assert(data[7] == 0x00);
1411     assert(data[8] == 0x00);
1412 
1413     assert(ionPutR(data.ptr + data.length, 0.0) == 1);
1414     assert(data[$ - 1] == 0x40);
1415 
1416     assert(ionPutR(data.ptr + data.length, -0.0) == 9);
1417     assert(data[0] == 0x48);
1418     assert(data[1] == 0x80);
1419     assert(data[2] == 0x00);
1420     assert(data[3] == 0x00);
1421     assert(data[4] == 0x00);
1422     assert(data[5] == 0x00);
1423     assert(data[6] == 0x00);
1424     assert(data[7] == 0x00);
1425     assert(data[8] == 0x00);
1426 }
1427 
1428 /++
1429 +/
1430 size_t ionPut(T)(scope ubyte* ptr, const T value)
1431     if (is(T == real))
1432 {
1433     return ionPut!double(ptr, value);
1434 }
1435 
1436 ///
1437 @system pure nothrow @nogc
1438 version(mir_ion_test) unittest
1439 {
1440     ubyte[9] data;
1441     assert(ionPut(data.ptr, -16.0L) == 9);
1442     assert(data[0] == 0x48);
1443     assert(data[1] == 0xC0);
1444     assert(data[2] == 0x30);
1445     assert(data[3] == 0x00);
1446     assert(data[4] == 0x00);
1447     assert(data[5] == 0x00);
1448     assert(data[6] == 0x00);
1449     assert(data[7] == 0x00);
1450     assert(data[8] == 0x00);
1451 
1452     assert(ionPut(data.ptr, 0.0L) == 1);
1453     assert(data[0] == 0x40);
1454 
1455     assert(ionPut(data.ptr, -0.0L) == 9);
1456     assert(data[0] == 0x48);
1457     assert(data[1] == 0x80);
1458     assert(data[2] == 0x00);
1459     assert(data[3] == 0x00);
1460     assert(data[4] == 0x00);
1461     assert(data[5] == 0x00);
1462     assert(data[6] == 0x00);
1463     assert(data[7] == 0x00);
1464     assert(data[8] == 0x00);
1465 }
1466 
1467 /++
1468 +/
1469 size_t ionPut()(
1470     scope ubyte* ptr,
1471     BigUIntView!(const size_t) value,
1472     )
1473 {
1474     return ionPut(ptr, value.signed);
1475 }
1476 
1477 pure
1478 version(mir_ion_test) unittest
1479 {
1480     ubyte[32] data;
1481     // big unsigned integer
1482     assert(ionPut(data.ptr, BigUIntView!size_t.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219").lightConst) == 32);
1483     assert(data[0] == 0x2E);
1484     assert(data[1] == 0x9E);
1485     // assert(data[2 .. 32] == BigUIntView!(ubyte, WordEndian.big).fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219").coefficients);
1486 }
1487 
1488 /++
1489 +/
1490 size_t ionPut()(
1491     scope ubyte* ptr,
1492     BigIntView!(const size_t) value,
1493     )
1494 {
1495     auto length = ionPutUIntField(ptr + 1, value.unsigned);
1496     auto q = 0x20 | (value.sign << 4);
1497     if (_expect(length < 0xE, true))
1498     {
1499         *ptr = cast(ubyte)(q | length);
1500         return length + 1;
1501     }
1502     else
1503     {
1504         *ptr = cast(ubyte)(q | 0xE);
1505         ubyte[19] lengthPayload = void;
1506         auto lengthLength = ionPutVarUInt(lengthPayload.ptr, length);
1507 
1508         if (__ctfe)
1509         {
1510             foreach_reverse (i; 0 .. length)
1511                 ptr[i + 1 + lengthLength] = ptr[i + 1];
1512             ptr[1 .. lengthLength + 1] = lengthPayload[0 .. lengthLength];
1513         }
1514         else
1515         {
1516             memmove(ptr + 1 + lengthLength, ptr + 1, length);
1517             memcpy(ptr + 1, lengthPayload.ptr, lengthLength);
1518         }
1519         return length + 1 + lengthLength;
1520     }
1521 }
1522 
1523 pure
1524 version(mir_ion_test) unittest
1525 {
1526     ubyte[9] data;
1527     // big unsigned integer
1528     assert(ionPut(data.ptr, -BigUIntView!size_t.fromHexString("45be").lightConst) == 3);
1529     assert(data[0] == 0x32);
1530     assert(data[1] == 0x45);
1531     assert(data[2] == 0xbe);
1532 }
1533 
1534 /++
1535 +/
1536 size_t ionPut()(
1537     scope ubyte* ptr,
1538     DecimalView!(const size_t) value,
1539     )
1540 {
1541     size_t length;
1542     if (value.coefficient.coefficients.length == 0 && value.sign == false && value.exponent == 0)
1543         goto L;
1544     length = ionPutVarInt(ptr + 1, value.exponent);
1545     length += ionPutIntField(ptr + 1 + length, value.signedCoefficient);
1546     if (_expect(length < 0xE, true))
1547     {
1548     L:
1549         *ptr = cast(ubyte)(0x50 | length);
1550         return length + 1;
1551     }
1552     else
1553     {
1554         *ptr = 0x5E;
1555         ubyte[19] lengthPayload = void;
1556         auto lengthLength = ionPutVarUInt(lengthPayload.ptr, length);
1557         if (__ctfe)
1558         {
1559             foreach_reverse (i; 0 .. length)
1560                 ptr[i + 1 + lengthLength] = ptr[i + 1];
1561             ptr[1 .. lengthLength + 1] = lengthPayload[0 .. lengthLength];
1562         }
1563         else
1564         {
1565             memmove(ptr + 1 + lengthLength, ptr + 1, length);
1566             memcpy(ptr + 1, lengthPayload.ptr, lengthLength);
1567         }
1568         return length + 1 + lengthLength;
1569     }
1570 }
1571 
1572 pure
1573 version(mir_ion_test) unittest
1574 {
1575     ubyte[34] data;
1576     // 0.6
1577     assert(ionPut(data.ptr, DecimalView!size_t(false, -1, BigUIntView!size_t.fromHexString("06")).lightConst) == 3);
1578     assert(data[0] == 0x52);
1579     assert(data[1] == 0xC1);
1580     assert(data[2] == 0x06);
1581 
1582     // -0.6
1583     assert(ionPut(data.ptr, DecimalView!size_t(true, -1, BigUIntView!size_t.fromHexString("06")).lightConst) == 3);
1584     assert(data[0] == 0x52);
1585     assert(data[1] == 0xC1);
1586     assert(data[2] == 0x86);
1587 
1588 
1589     // 0e-3
1590     assert(ionPut(data.ptr, DecimalView!size_t(false, 3, BigUIntView!size_t([0])).lightConst) == 2);
1591     assert(data[0] == 0x51);
1592     assert(data[1] == 0x83);
1593 
1594     // -0e+0
1595     assert(ionPut(data.ptr, DecimalView!size_t(true, 0, BigUIntView!size_t([0])).lightConst) == 3);
1596     assert(data[0] == 0x52);
1597     assert(data[1] == 0x80);
1598     assert(data[2] == 0x80);
1599 
1600     // 0e+0
1601     assert(ionPut(data.ptr, DecimalView!size_t(false, 0, BigUIntView!size_t([0])).lightConst) == 2);
1602     assert(data[0] == 0x51);
1603     assert(data[1] == 0x80);
1604 
1605     // 0e+0 (minimal)
1606     assert(ionPut(data.ptr, DecimalView!size_t(false, 0, BigUIntView!size_t.init).lightConst) == 1);
1607     assert(data[0] == 0x50);
1608 
1609     // big decimal
1610     assert(ionPut(data.ptr, DecimalView!size_t(false, -9, BigUIntView!size_t.fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219")).lightConst) == 34);
1611     assert(data[0] == 0x5E);
1612     assert(data[1] == 0xA0);
1613     assert(data[2] == 0xC9);
1614     assert(data[3] == 0x00);
1615     // assert(data[4 .. 34] == BigUIntView!(ubyte, WordEndian.big).fromHexString("88BF4748507FB9900ADB624CCFF8D78897DC900FB0460327D4D86D327219").coefficients);
1616 
1617     // -12.345
1618     // assert( >=0);
1619     assert(ionPut(data.ptr, DecimalView!size_t(true, -3, BigUIntView!size_t.fromHexString("3039")).lightConst) == 4);
1620     assert(data[0] == 0x53);
1621     assert(data[1] == 0xC3);
1622     assert(data[2] == 0xB0);
1623     assert(data[3] == 0x39);
1624     // assert(data[0] == 0x50);
1625 }
1626 
1627 ///
1628 size_t ionPutDecimal()(scope ubyte* ptr, bool sign, ulong coefficient, long exponent)
1629 {
1630     version(LDC)
1631         pragma(inline, true);
1632     size_t length;
1633     // if (coefficient == 0)
1634     //     goto L;
1635     length = ionPutVarInt(ptr + 1, exponent);
1636     length += ionPutIntField(ptr + 1 + length, coefficient, sign);
1637     if (_expect(length < 0xE, true))
1638     {
1639     L:
1640         *ptr = cast(ubyte)(0x50 | length);
1641         return length + 1;
1642     }
1643     else
1644     {
1645         memmove(ptr + 2, ptr + 1, 16);
1646         *ptr = 0x5E;
1647         assert(length < 0x80);
1648         ptr[1] = cast(ubyte) (length ^ 0x80);
1649         return length + 2;
1650     }
1651 }
1652 
1653 ///
1654 size_t ionPutDecimalR()(scope ubyte* ptr, bool sign, ulong coefficient, long exponent)
1655 {
1656     version(LDC)
1657         pragma(inline, true);
1658     size_t length;
1659     // if (coefficient == 0)
1660     //     goto L;
1661     length = ionPutIntFieldR(ptr, coefficient, sign);
1662     length += ionPutVarIntR(ptr - length, exponent);
1663     ptr -= length;
1664     if (_expect(length < 0xE, true))
1665     {
1666     L:
1667         *--ptr = cast(ubyte)(0x50 | length);
1668         return length + 1;
1669     }
1670     else
1671     {
1672         *--ptr = cast(ubyte)(0x80 | length);
1673         *--ptr = 0x5E;
1674         return length + 2;
1675     }
1676 }
1677 
1678 /++
1679 +/
1680 size_t ionPut(T)(scope ubyte* ptr, const T value)
1681     if (is(T == Timestamp))
1682 {
1683     size_t ret = 1;
1684     ret += value.isLocalTime ?
1685         ionPutVarInt(ptr + ret, ubyte.init, true):
1686         ionPutVarInt(ptr + ret, value.offset);
1687     ret += ionPutVarUInt(ptr + ret, cast(ushort)value.year);
1688     if (value.precision >= Timestamp.precision.month)
1689     {
1690         ptr[ret++] = cast(ubyte) (0x80 | value.month);
1691         if (value.precision >= Timestamp.precision.day)
1692         {
1693             ptr[ret++] = cast(ubyte) (0x80 | value.day);
1694             if (value.precision >= Timestamp.precision.minute)
1695             {
1696                 ptr[ret++] = cast(ubyte) (0x80 | value.hour);
1697                 ptr[ret++] = cast(ubyte) (0x80 | value.minute);
1698                 if (value.precision >= Timestamp.precision.second)
1699                 {
1700                     ptr[ret++] = cast(ubyte) (0x80 | value.second);
1701                     if (value.precision > Timestamp.precision.second) //fraction
1702                     {
1703                         ret += ionPutVarInt(ptr + ret, value.fractionExponent);
1704                         ret += ionPutIntField(ptr + ret, long(value.fractionCoefficient));
1705                     }
1706                 }
1707             }
1708         }
1709     }
1710     auto length = ret - 1;
1711     if (_expect(ret < 0xF, true))
1712     {
1713         *ptr = cast(ubyte) (0x60 | length);
1714         return ret;
1715     }
1716     else
1717     {
1718         if (__ctfe)
1719             foreach_reverse (i; 0 .. length)
1720                 ptr[i + 2] = ptr[i + 1];
1721         else
1722             memmove(ptr + 2, ptr + 1, length);
1723         *ptr = 0x6E;
1724         ptr[1] = cast(ubyte) (0x80 | length);
1725         return ret + 1;
1726     }
1727 }
1728 
1729 ///
1730 version(mir_ion_test) unittest
1731 {
1732     import mir.timestamp;
1733 
1734     ubyte[20] data;
1735 
1736     ubyte[] result = [0x68, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84];
1737     auto ts = Timestamp(2000, 7, 8, 2, 3, 4).withOffset(0);
1738     assert(data[0 .. ionPut(data.ptr, ts)] == result);
1739 
1740     result = [0x69, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0xC2];
1741     ts = Timestamp(2000, 7, 8, 2, 3, 4, -2, 0).withOffset(0);
1742     assert(data[0 .. ionPut(data.ptr, ts)] == result);
1743 
1744     result = [0x6A, 0x80, 0x0F, 0xD0, 0x87, 0x88, 0x82, 0x83, 0x84, 0xC3, 0x10];
1745     ts = Timestamp(2000, 7, 8, 2, 3, 4, -3, 16).withOffset(0);
1746     assert(data[0 .. ionPut(data.ptr, ts)] == result);
1747 }
1748 
1749 /++
1750 +/
1751 size_t ionPut(T)(scope ubyte* ptr, const T value)
1752     if (is(T == Date))
1753 {
1754     size_t ret = 1;
1755     auto ymd = value.yearMonthDay;
1756     ptr[ret++] = 0x80;
1757     ret += ionPutVarUInt(ptr + ret, cast(ushort)value.year);
1758     ptr[ret++] = cast(ubyte) (0x80 | value.month);
1759     ptr[ret++] = cast(ubyte) (0x80 | value.day);
1760     auto length = ret - 1;
1761     *ptr = cast(ubyte) (0x60 | length);
1762     return ret;
1763 }
1764 
1765 ///
1766 version(mir_ion_test) unittest
1767 {
1768     import mir.date;
1769 
1770     ubyte[13] data;
1771 
1772     ubyte[] result = [0x65, 0x80, 0x0F, 0xD0, 0x87, 0x88];
1773     auto ts = Date(2000, 7, 8);
1774     assert(data[0 .. ionPut(data.ptr, ts)] == result);
1775 }
1776 
1777 /++
1778 +/
1779 size_t ionPutSymbolId(T)(scope ubyte* ptr, const T value)
1780     if (isUnsigned!T)
1781 {
1782     auto length = ionPutUIntField(ptr + 1, value);
1783     *ptr = cast(ubyte)(0x70 | length);
1784     return length + 1;
1785 }
1786 
1787 ///
1788 version(mir_ion_test) unittest
1789 {
1790     ubyte[8] data;
1791 
1792     ubyte[] result = [0x72, 0x01, 0xFF];
1793     auto id = 0x1FFu;
1794     assert(data[0 .. ionPutSymbolId(data.ptr, id)] == result);
1795 }
1796 
1797 /++
1798 +/
1799 size_t ionPutSymbolId(T)(scope ubyte* ptr, BigUIntView!T value)
1800 {
1801     auto length = ionPutUIntField(ptr + 1, value);
1802     assert(length < 10);
1803     *ptr = cast(ubyte)(0x70 | length);
1804     return length + 1;
1805 }
1806 
1807 ///
1808 version(mir_ion_test) unittest
1809 {
1810     import mir.bignum.low_level_view: BigUIntView;
1811 
1812     ubyte[8] data;
1813 
1814     ubyte[] result = [0x72, 0x01, 0xFF];
1815     // auto id = BigUIntView!(ubyte, WordEndian.big).fromHexString("1FF");
1816     // assert(data[0 .. ionPutSymbolId(data.ptr, id)] == result);
1817 }
1818 
1819 /++
1820 +/
1821 size_t ionPut()(scope ubyte* ptr, scope const(char)[] value)
1822 {
1823     size_t ret = 1;
1824     if (value.length < 0xE)
1825     {
1826         *ptr = cast(ubyte) (0x80 | value.length);
1827     }
1828     else
1829     {
1830         *ptr = 0x8E;
1831         ret += ionPutVarUInt(ptr + 1, value.length);
1832     }
1833     if (__ctfe)
1834         ptr[ret .. ret + value.length] = cast(const(ubyte)[])value;
1835     else
1836         memcpy(ptr + ret, value.ptr, value.length);
1837     return ret + value.length;
1838 }
1839 
1840 ///
1841 version(mir_ion_test) unittest
1842 {
1843     ubyte[20] data;
1844 
1845     ubyte[] result = [0x85, 'v', 'a', 'l', 'u', 'e'];
1846     auto str = "value";
1847     assert(data[0 .. ionPut(data.ptr, str)] == result);
1848 
1849     result = [ubyte(0x8E), ubyte(0x90)] ~ cast(ubyte[])"hexadecimal23456";
1850     str = "hexadecimal23456";
1851     assert(data[0 .. ionPut(data.ptr, str)] == result);
1852 }
1853 
1854 /++
1855 +/
1856 size_t ionPut(T)(scope ubyte* ptr, const T value)
1857     if (is(T == Clob))
1858 {
1859     size_t ret = 1;
1860     if (value.data.length < 0xE)
1861     {
1862         *ptr = cast(ubyte) (0x90 | value.data.length);
1863     }
1864     else
1865     {
1866         *ptr = 0x9E;
1867         ret += ionPutVarUInt(ptr + 1, value.data.length);
1868     }
1869     memcpy(ptr + ret, value.data.ptr, value.data.length);
1870     return ret + value.data.length;
1871 }
1872 
1873 ///
1874 version(mir_ion_test) unittest
1875 {
1876     import mir.lob;
1877 
1878     ubyte[20] data;
1879 
1880     ubyte[] result = [0x95, 'v', 'a', 'l', 'u', 'e'];
1881     auto str = Clob("value");
1882     assert(data[0 .. ionPut(data.ptr, str)] == result);
1883 
1884     result = [ubyte(0x9E), ubyte(0x90)] ~ cast(ubyte[])"hexadecimal23456";
1885     str = Clob("hexadecimal23456");
1886     assert(data[0 .. ionPut(data.ptr, str)] == result);
1887 }
1888 
1889 /++
1890 +/
1891 size_t ionPut(T)(scope ubyte* ptr, const T value)
1892     if (is(T == Blob))
1893 {
1894     size_t ret = 1;
1895     if (value.data.length < 0xE)
1896     {
1897         *ptr = cast(ubyte) (0xA0 | value.data.length);
1898     }
1899     else
1900     {
1901         *ptr = 0xAE;
1902         ret += ionPutVarUInt(ptr + 1, value.data.length);
1903     }
1904     memcpy(ptr + ret, value.data.ptr, value.data.length);
1905     return ret + value.data.length;
1906 }
1907 
1908 ///
1909 version(mir_ion_test) unittest
1910 {
1911     import mir.lob;
1912 
1913     ubyte[20] data;
1914 
1915     ubyte[] result = [0xA5, 'v', 'a', 'l', 'u', 'e'];
1916     auto payload = Blob(cast(ubyte[])"value");
1917     assert(data[0 .. ionPut(data.ptr, payload)] == result);
1918 
1919     result = [ubyte(0xAE), ubyte(0x90)] ~ cast(ubyte[])"hexadecimal23456";
1920     payload = Blob(cast(ubyte[])"hexadecimal23456");
1921     assert(data[0 .. ionPut(data.ptr, payload)] == result);
1922 }
1923 
1924 /++
1925 +/
1926 size_t ionPutStartLength()()
1927 {
1928     return 3;
1929 }
1930 
1931 /++
1932 +/
1933 size_t ionPutEmpty()(ubyte* startPtr, IonTypeCode tc)
1934 {
1935     version(LDC) pragma(inline, true);
1936     assert (tc == IonTypeCode..string || tc == IonTypeCode.list || tc == IonTypeCode.sexp || tc == IonTypeCode.struct_ || tc == IonTypeCode.annotations);
1937     auto tck = tc << 4;
1938     *startPtr = cast(ubyte) tck;
1939     return 1;
1940 }
1941 
1942 /++
1943 +/
1944 size_t ionPutEnd()(ubyte* startPtr, IonTypeCode tc, size_t totalElementLength)
1945 {
1946     version(LDC) pragma(inline, true);
1947     assert (tc == IonTypeCode..string || tc == IonTypeCode.list || tc == IonTypeCode.sexp || tc == IonTypeCode.struct_ || tc == IonTypeCode.annotations);
1948     auto tck = tc << 4;
1949     if (totalElementLength < 0xE)
1950     {
1951         *startPtr = cast(ubyte) (tck | totalElementLength);
1952         if (__ctfe)
1953             foreach (i; 0 .. totalElementLength)
1954                 startPtr[i + 1] = startPtr[i + 3];
1955         else
1956             memmove(startPtr + 1, startPtr + 3, 16);
1957         debug {
1958             startPtr[totalElementLength + 1] = 0xFF;
1959             startPtr[totalElementLength + 2] = 0xFF;
1960         }
1961         return 1 + totalElementLength;
1962     }
1963     *startPtr = cast(ubyte)(tck | 0xE);
1964     if (totalElementLength < 0x80)
1965     {
1966         startPtr[1] = cast(ubyte) (0x80 | totalElementLength);
1967         if (__ctfe)
1968             foreach (i; 0 .. totalElementLength)
1969                 startPtr[i + 2] = startPtr[i + 3];
1970         else
1971             memmove(startPtr + 2, startPtr + 3, 128);
1972         debug {
1973             startPtr[totalElementLength + 2] = 0xFF;
1974         }
1975         return 2 + totalElementLength;
1976     }
1977     if (totalElementLength < 0x4000)
1978     {
1979         startPtr[1] = cast(ubyte) (totalElementLength >> 7);
1980         startPtr[2] = cast(ubyte) (totalElementLength | 0x80);
1981         return 3 + totalElementLength;
1982     }
1983     ubyte[19] lengthPayload  = void;
1984     auto lengthLength = ionPutVarUInt(lengthPayload.ptr, totalElementLength);
1985     if (__ctfe)
1986     {
1987         foreach_reverse (i; 0 .. totalElementLength)
1988             startPtr[i + 1 + lengthLength] = startPtr[i + 3];
1989         startPtr[1 .. lengthLength + 1] = lengthPayload[0 .. lengthLength];
1990     }
1991     else
1992     {
1993         memmove(startPtr + 1 + lengthLength, startPtr + 3, totalElementLength);
1994         memcpy(startPtr + 1, lengthPayload.ptr, lengthLength);
1995     }
1996     return totalElementLength + 1 + lengthLength;
1997 }
1998 
1999 ///
2000 version(mir_ion_test) unittest
2001 {
2002     import mir.ion.type_code;
2003 
2004     ubyte[1024] data;
2005     auto pos = ionPutStartLength;
2006 
2007     ubyte[] result = [0xB0];
2008     assert(data[0 .. ionPutEnd(data.ptr, IonTypeCode.list, 0)] == result);
2009 
2010     result = [ubyte(0xB6), ubyte(0x85)] ~ cast(ubyte[])"hello";
2011     auto len = ionPut(data.ptr + pos, "hello");
2012     assert(data[0 .. ionPutEnd(data.ptr, IonTypeCode.list, len)] == result);
2013 
2014     result = [0xCE, 0x90, 0x8E, 0x8E];
2015     result ~= cast(ubyte[])"hello world!!!";
2016     len = ionPut(data.ptr + pos, "hello world!!!");
2017     assert(data[0 .. ionPutEnd(data.ptr, IonTypeCode.sexp, len)] == result);
2018 
2019     auto bm = `
2020 Generating test runner configuration 'mir-ion-test-library' for 'library' (library).
2021 Performing "unittest" build using /Users/9il/dlang/ldc2/bin/ldc2 for x86_64.
2022 mir-core 1.1.7: target for configuration "library" is up to date.
2023 mir-algorithm 3.9.2: target for configuration "default" is up to date.
2024 mir-cpuid 1.2.6: target for configuration "library" is up to date.
2025 mir-ion 0.5.7+commit.70.g7dcac11: building configuration "mir-ion-test-library"...
2026 Linking...
2027 To force a rebuild of up-to-date targets, run again with --force.
2028 Running ./mir-ion-test-library`;
2029 
2030     result = [0xBE, 0x04, 0xB0, 0x8E, 0x04, 0xAD];
2031     result ~= cast(ubyte[])bm;
2032     len = ionPut(data.ptr + pos, bm);
2033     assert(data[0 .. ionPutEnd(data.ptr, IonTypeCode.list, len)] == result);
2034 }
2035 
2036 /++
2037 +/
2038 size_t ionPutAnnotationsListStartLength()()
2039 {
2040     return 1;
2041 }
2042 
2043 /++
2044 +/
2045 size_t ionPutAnnotationsListEnd()(ubyte* startPtr, size_t totalElementLength)
2046 {
2047     if (_expect(totalElementLength < 0x80, true))
2048     {
2049         startPtr[0] = cast(ubyte) (0x80 | totalElementLength);
2050         return 1 + totalElementLength;
2051     }
2052     else
2053     {
2054         ubyte[19] lengthPayload  = void;
2055         auto lengthLength = ionPutVarUInt(lengthPayload.ptr, totalElementLength);
2056         if (__ctfe)
2057         {
2058             foreach_reverse (i; 0 .. totalElementLength)
2059                 startPtr[lengthLength] = startPtr[i + 1];
2060             startPtr[0 .. lengthLength] = lengthPayload[0 .. lengthLength];
2061         }
2062         else
2063         {
2064             memmove(startPtr + lengthLength, startPtr + 1, totalElementLength);
2065             memcpy(startPtr, lengthPayload.ptr, lengthLength);
2066         }
2067         return totalElementLength + lengthLength;
2068     }
2069 }
2070 
2071 ///
2072 size_t ionPutEndR(ubyte* ptr, IonTypeCode type, size_t length)
2073     pure nothrow @nogc
2074 {
2075     if (length < 0xE)
2076     {
2077         *--ptr = cast(ubyte) ((type << 4) | length);
2078         return 1;
2079     }
2080     else
2081     {
2082         length = ionPutVarUIntR(ptr, length);
2083         *(ptr - (length + 1)) = cast(ubyte) ((type << 4) | 0xE);
2084         return length + 1;
2085     }
2086 }
2087 
2088 ///
2089 size_t ionPutVarUIntR(ubyte* ptr, size_t length)
2090     pure nothrow @nogc
2091 {
2092     auto safe = ptr;
2093     do *--ptr = length & 0x7F;
2094     while (length >>>= 7);
2095     *(safe - 1) |= 0x80;
2096     return safe - ptr;
2097 }