1 /**
2 
3 Various @nogc alternatives. This file includes parts of `std.process`, `std.random`, `std.uuid`.
4 
5 Authors:
6   $(HTTP guillaumepiolat.fr, Guillaume Piolat)
7   $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
8   $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
9   $(HTTP thecybershadow.net, Vladimir Panteleev)
10 
11 Copyright:
12  Copyright (c) 2016, Guillaume Piolat.
13  Copyright (c) 2013, Lars Tandle Kyllingstad (std.process).
14  Copyright (c) 2013, Steven Schveighoffer (std.process).
15  Copyright (c) 2013, Vladimir Panteleev (std.process).
16 
17  License:
18    $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
19 */
20 module dplug.core.nogc;
21 
22 import core.stdc.stdarg;
23 import core.stdc.string: strdup, memcpy, strlen;
24 import core.stdc.stdlib: malloc, free, getenv;
25 import core.memory: GC;
26 import core.exception: onOutOfMemoryErrorNoGC;
27 
28 import std.conv: emplace;
29 import std.traits;
30 
31 import dplug.core.vec: Vec;
32 
33 // This module provides many utilities to deal with @nogc nothrow, in a situation with the runtime 
34 // disabled.
35 
36 //
37 // Faking @nogc
38 //
39 
40 version(Windows)
41 {
42     import core.sys.windows.winbase;
43 }
44 
45 version = useTimSort;
46 
47 auto assumeNoGC(T) (T t)
48 {
49     static if (isFunctionPointer!T || isDelegate!T)
50     {
51         enum attrs = functionAttributes!T | FunctionAttribute.nogc;
52         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
53     }
54     else
55         static assert(false);
56 }
57 
58 auto assumeNothrowNoGC(T) (T t)
59 {
60     static if (isFunctionPointer!T || isDelegate!T)
61     {
62         enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
63         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
64     }
65     else
66         static assert(false);
67 }
68 
69 unittest
70 {
71     void funcThatDoesGC()
72     {
73         int a = 4;
74         int[] b = [a, a, a];
75     }
76 
77     void anotherFunction() nothrow @nogc
78     {
79         assumeNothrowNoGC( (){ funcThatDoesGC(); } )();
80     }
81 
82     void aThirdFunction() @nogc
83     {
84         assumeNoGC( () { funcThatDoesGC(); } )();
85     }
86 }
87 
88 
89 //
90 // Optimistic .destroy, which is @nogc nothrow by breaking the type-system
91 //
92 
93 // for classes
94 void destroyNoGC(T)(T x) nothrow @nogc if (is(T == class) || is(T == interface))
95 {
96     assumeNothrowNoGC(
97         (T x)
98         {
99             return destroy(x);
100         })(x);
101 }
102 
103 // for struct
104 void destroyNoGC(T)(ref T obj) nothrow @nogc if (is(T == struct))
105 {
106     assumeNothrowNoGC(
107         (ref T x)
108         {
109             return destroy(x);
110         })(obj);
111 }
112 /*
113 void destroyNoGC(T : U[n], U, size_t n)(ref T obj) nothrow @nogc
114 {
115     assumeNothrowNoGC(
116         (T x)
117         {
118             return destroy(x);
119         })(obj);
120 }*/
121 
122 void destroyNoGC(T)(ref T obj) nothrow @nogc
123     if (!is(T == struct) && !is(T == class) && !is(T == interface))
124 {
125     assumeNothrowNoGC(
126                       (ref T x)
127                       {
128                           return destroy(x);
129                       })(obj);
130 }
131 
132 
133 
134 //
135 // Constructing and destroying without the GC.
136 //
137 
138 /// Allocates and construct a struct or class object.
139 /// Returns: Newly allocated object.
140 auto mallocNew(T, Args...)(Args args)
141 {
142     static if (is(T == class))
143         immutable size_t allocSize = __traits(classInstanceSize, T);
144     else
145         immutable size_t allocSize = T.sizeof;
146 
147     void* rawMemory = malloc(allocSize);
148     if (!rawMemory)
149         onOutOfMemoryErrorNoGC();
150 
151     static if (is(T == class))
152     {
153         T obj = emplace!T(rawMemory[0 .. allocSize], args);
154     }
155     else
156     {
157         T* obj = cast(T*)rawMemory;
158         emplace!T(obj, args);
159     }
160 
161     return obj;
162 }
163 
164 /// Destroys and frees a class object created with $(D mallocEmplace).
165 void destroyFree(T)(T p) if (is(T == class))
166 {
167     if (p !is null)
168     {
169         destroyNoGC(p);
170         free(cast(void*)p);
171     }
172 }
173 
174 /// Destroys and frees an interface object created with $(D mallocEmplace).
175 void destroyFree(T)(T p) if (is(T == interface))
176 {
177     if (p !is null)
178     {
179         void* here = cast(void*)(cast(Object)p);
180         destroyNoGC(p);
181         free(cast(void*)here);
182     }
183 }
184 
185 /// Destroys and frees a non-class object created with $(D mallocEmplace).
186 void destroyFree(T)(T* p) if (!is(T == class))
187 {
188     if (p !is null)
189     {
190         destroyNoGC(p);
191         free(cast(void*)p);
192     }
193 }
194 
195 
196 unittest
197 {
198     class A
199     {
200         int _i;
201         this(int i)
202         {
203             _i = i;
204         }
205     }
206 
207     struct B
208     {
209         int i;
210     }
211 
212     void testMallocEmplace()
213     {
214         A a = mallocNew!A(4);
215         destroyFree(a);
216 
217         B* b = mallocNew!B(5);
218         destroyFree(b);
219     }
220 
221     testMallocEmplace();
222 }
223 
224 version( D_InlineAsm_X86 )
225 {
226     version = AsmX86;
227 }
228 else version( D_InlineAsm_X86_64 )
229 {
230     version = AsmX86;
231 }
232 
233 /// Allocates a slice with `malloc`.
234 T[] mallocSlice(T)(size_t count) nothrow @nogc
235 {
236     T[] slice = mallocSliceNoInit!T(count);
237     static if (is(T == struct))
238     {
239         // we must avoid calling struct destructors with uninitialized memory
240         for(size_t i = 0; i < count; ++i)
241         {
242             T uninitialized;
243             memcpy(&slice[i], &uninitialized, T.sizeof); // memcpy OK
244         }
245     }
246     else
247         slice[0..count] = T.init;
248     return slice;
249 }
250 
251 /// Allocates a slice with `malloc`, but does not initialize the content.
252 T[] mallocSliceNoInit(T)(size_t count) nothrow @nogc
253 {
254     T* p = cast(T*) malloc(count * T.sizeof);
255     return p[0..count];
256 }
257 
258 /// Frees a slice allocated with `mallocSlice`.
259 void freeSlice(T)(const(T)[] slice) nothrow @nogc
260 {
261     free(cast(void*)(slice.ptr)); // const cast here
262 }
263 
264 /// Duplicates a slice with `malloc`. Equivalent to `.dup`
265 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`.
266 /// FUTURE: all the semantics are not well-defined with null slices,
267 ///         but it would be tricky to think about it and define it.
268 T[] mallocDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct))
269 {
270     T[] copy = mallocSliceNoInit!T(slice.length);
271     memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof);
272     return copy;
273 }
274 
275 /// Duplicates a string with `malloc`, and add a terminal zero.
276 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`.
277 char[] mallocDupZ(const(char)[] slice) nothrow @nogc
278 {
279     char[] copy = mallocSliceNoInit!char(slice.length + 1);
280     memcpy(copy.ptr, slice.ptr, slice.length * char.sizeof);
281     copy[$-1] = '\0';
282     return copy[0..$-1];
283 }
284 unittest
285 {
286     char[] s = mallocDupZ("Hello");
287     assert(*assumeZeroTerminated(s) == 'H');
288     freeSlice(s);
289 }
290 
291 /// Duplicates a slice with `malloc`. Equivalent to `.idup`
292 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`.
293 immutable(T)[] mallocIDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct))
294 {
295     return cast(immutable(T)[]) (mallocDup!T(slice));
296 }
297 
298 /// Duplicates a zero-terminated string with `malloc`, return a `char[]` with zero-terminated byte.
299 /// Has to be cleaned-up with `free(s.ptr)`.
300 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be 
301 /// converted to a C string with `.ptr`. However the zero byte is not included in slice length.
302 char[] stringDup(const(char)* cstr) nothrow @nogc
303 {
304     assert(cstr !is null);
305     size_t len = strlen(cstr);
306     char* copy = strdup(cstr);
307     return copy[0..len];
308 }
309 
310 /// Duplicates a zero-terminated string with `malloc`, return a `string`. with zero-terminated 
311 /// byte. Has to be cleaned-up with `free(s.ptr)`.
312 ///
313 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be 
314 /// converted to a C string with `.ptr`. However the zero byte is not included in slice length.
315 string stringIDup(const(char)* cstr) nothrow @nogc
316 {
317     return cast(string) stringDup(cstr);
318 }
319 
320 unittest
321 {
322     int[] slice = mallocSlice!int(4);
323     assert(slice[3] == int.init);
324     freeSlice(slice);    
325 
326     slice = mallocSliceNoInit!int(4);
327     freeSlice(slice);
328 
329     slice = mallocSliceNoInit!int(0);
330     assert(slice == []);
331     freeSlice(slice);
332 }
333 
334 /// Semantic function to check that a D string implicitely conveys a
335 /// termination byte after the slice.
336 /// (typically those comes from string literals or `stringDup`/`stringIDup`)
337 const(char)* assumeZeroTerminated(const(char)[] input) nothrow @nogc
338 {
339     if (input.ptr is null)
340         return null;
341 
342     // Check that the null character is there
343     assert(input.ptr[input.length] == '\0');
344     return input.ptr;
345 }
346 
347 //
348 // STABLE IN-PLACE SORT (implementation is at bottom of file)
349 //
350 // Here is how to use it:
351 unittest
352 {
353     {
354         int[2][] testData = [[110, 0], [5, 0], [10, 0], [3, 0], [110, 1], [5, 1], [10, 1], [3, 1]];
355         version(useTimSort)
356         {
357             Vec!(int[2]) tempBuf;
358             timSort!(int[2])(testData, tempBuf, (a, b) => (a[0] - b[0]));        
359         }
360         assert(testData == [[3, 0], [3, 1], [5, 0], [5, 1], [10, 0], [10, 1], [110, 0], [110, 1]]);
361     }    
362 }
363 
364 
365 //
366 // STABLE MERGE SORT
367 //
368 
369 /// A bit faster than a dynamic cast.
370 /// This is to avoid TypeInfo look-up.
371 T unsafeObjectCast(T)(Object obj)
372 {
373     return cast(T)(cast(void*)(obj));
374 }
375 
376 /// Outputs a debug string in either:
377 ///  - stdout on POSIX-like (visible in the command-line)
378 ///  - the Output Windows on Windows (visible withing Visual Studio or with dbgview.exe)
379 /// Warning: no end-of-line added!
380 void debugLog(const(char)* message) nothrow @nogc
381 {
382     version(Windows)
383     {
384         OutputDebugStringA(message);
385     }
386     else
387     {
388         import core.stdc.stdio;
389         printf("%s\n", message);
390     }
391 }
392 
393 ///ditto
394 extern (C) void debugLogf(const(char)* fmt, ...) nothrow @nogc
395 {
396     // This is a complete hack to be able to build in Ubuntu Focal, which distributes D front-ends 
397     // based upon DMDFE 2.090. In these compilers, va_start is not marked @nogc.
398     static if (__VERSION__ > 2090)
399     {
400         import core.stdc.stdio;
401 
402         char[256] buffer;
403         va_list args;
404         va_start (args, fmt);
405         vsnprintf (buffer.ptr, 256, fmt, args);
406         va_end (args);
407 
408         version(Windows)
409         {
410             OutputDebugStringA(buffer.ptr);
411         }
412         else
413         {        
414             printf("%s\n", buffer.ptr);
415         }
416     }
417 }
418 
419 /// Inserts a breakpoint instruction. useful to trigger the debugger.
420 void debugBreak() nothrow @nogc
421 {
422     version( AsmX86 )
423     {
424         asm nothrow @nogc
425         {
426             int 3;
427         }
428     }
429     else version( GNU )
430     {
431         // __builtin_trap() is not the same thing unfortunately
432         asm
433         {
434             "int $0x03" : : : ;
435         }
436     }
437     else version(LDC)
438     {
439     	import ldc.intrinsics;
440     	llvm_debugtrap();
441     }
442     else
443     {
444         static assert(false, "No debugBreak() for this compiler");
445     }
446 }
447 
448 
449 // Copy source into dest.
450 // dest must contain room for maxChars characters
451 // A zero-byte character is then appended.
452 void stringNCopy(char* dest, size_t maxChars, const(char)[] source) nothrow @nogc
453 {
454     if (maxChars == 0)
455         return;
456 
457     size_t max = maxChars < source.length ? maxChars - 1 : source.length;
458     for (int i = 0; i < max; ++i)
459         dest[i] = source[i];
460     dest[max] = '\0';
461 }
462 
463 
464 //
465 // Low-cost C string conversions
466 //
467 alias CString = CStringImpl!char;
468 alias CString16 = CStringImpl!wchar;
469 
470 /// Zero-terminated C string, to replace toStringz and toUTF16z
471 struct CStringImpl(CharType) if (is(CharType: char) || is(CharType: wchar))
472 {
473 public:
474 nothrow:
475 @nogc:
476 
477     const(CharType)* storage = null;
478     alias storage this;
479 
480 
481     this(const(CharType)[] s)
482     {
483         // Always copy. We can't assume anything about the input.
484         size_t len = s.length;
485         CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof);
486         buffer[0..len] = s[0..len];
487         buffer[len] = '\0';
488         storage = buffer;
489         wasAllocated = true;
490     }
491 
492     // The constructor taking immutable can safely assume that such memory
493     // has been allocated by the GC or malloc, or an allocator that align
494     // pointer on at least 4 bytes.
495     this(immutable(CharType)[] s)
496     {
497         // Same optimizations that for toStringz
498         if (s.length == 0)
499         {
500             enum emptyString = cast(CharType[])"";
501             storage = emptyString.ptr;
502             return;
503         }
504 
505         /* Peek past end of s[], if it's 0, no conversion necessary.
506         * Note that the compiler will put a 0 past the end of static
507         * strings, and the storage allocator will put a 0 past the end
508         * of newly allocated char[]'s.
509         */
510         const(CharType)* p = s.ptr + s.length;
511         // Is p dereferenceable? A simple test: if the p points to an
512         // address multiple of 4, then conservatively assume the pointer
513         // might be pointing to another block of memory, which might be
514         // unreadable. Otherwise, it's definitely pointing to valid
515         // memory.
516         if ((cast(size_t) p & 3) && *p == 0)
517         {
518             storage = s.ptr;
519             return;
520         }
521 
522         size_t len = s.length;
523         CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof);
524         buffer[0..len] = s[0..len];
525         buffer[len] = '\0';
526         storage = buffer;
527         wasAllocated = true;
528     }
529 
530     ~this()
531     {
532         if (wasAllocated)
533             free(cast(void*)storage);
534     }
535 
536     @disable this(this);
537 
538 private:
539     bool wasAllocated = false;
540 }
541 
542 
543 //
544 // Launch browser, replaces std.process.browse
545 //
546 
547 void browseNoGC(string url) nothrow @nogc
548 {
549     version(Windows)
550     {
551         import core.sys.windows.winuser;
552         import core.sys.windows.shellapi;
553         ShellExecuteA(null, CString("open").storage, CString(url).storage, null, null, 
554                       SW_SHOWNORMAL);
555     }
556 
557     version(OSX)
558     {
559         import core.sys.posix.unistd;
560         const(char)*[5] args;
561 
562         auto curl = CString(url).storage;
563         const(char)* browser = getenv("BROWSER");
564         if (browser)
565         {
566             browser = strdup(browser);
567             args[0] = browser;
568             args[1] = curl;
569             args[2] = null;
570         }
571         else
572         {
573             args[0] = "open".ptr;
574             args[1] = curl;
575             args[2] = null;
576         }
577 
578         auto childpid = core.sys.posix.unistd.fork();
579         if (childpid == 0)
580         {
581             core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr);
582             return;
583         }
584         if (browser)
585             free(cast(void*)browser);
586     }
587     version(linux)
588     {
589         import core.sys.posix.stdlib;
590         import core.stdc.stdio;
591         char[256] curl;
592         sprintf(curl.ptr, "%s %s", "xdg-open".ptr, CString(url).storage);
593         system(curl.ptr);
594     }
595 }
596 
597 //
598 // @nogc sorting.
599 //
600 
601 /// Must return -1 if a < b
602 ///              0 if a == b
603 ///              1 if a > b
604 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc;
605 
606 
607 version(useTimSort)
608 {
609 
610     public void timSort(T)(T[] dst, 
611                        ref Vec!T storeBuf, // content unimportant, this will be use a temp storage.
612                                            // it should be "grow-only"
613                        nogcComparisonFunction!T comparison) nothrow @nogc
614     {
615         const size_t size = dst.length;
616 
617         /* don't bother sorting an array of size 1 */
618         if (size <= 1) {
619             return;
620         }
621 
622         if (size < 64) 
623         {
624             // uh... all out test cases are here
625             tim_sort_binary_inversion_sort!T(dst.ptr, size, comparison);
626             return;
627         }
628 
629         // Why would it be used only there???
630         enum TIM_SORT_STACK_SIZE = 64;
631         tim_sort_run_t[TIM_SORT_STACK_SIZE] run_stack;
632         size_t stack_curr = 0;
633         size_t curr = 0;
634 
635         /* compute the minimum run length */
636         size_t minrun = tim_sort_compute_minrun(size);
637 
638         if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, 
639                                   &curr, comparison)) 
640         {
641             return;
642         }
643 
644         if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, 
645                                   &curr, comparison)) 
646         {
647             return;
648         }
649 
650         if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, 
651                                   &curr, comparison)) 
652         {
653             return;
654         }
655 
656         while (1) {
657             if (!tim_sort_check_invariant(run_stack.ptr, cast(int)stack_curr)) {
658                 stack_curr = tim_sort_collapse!T(dst.ptr, run_stack.ptr, cast(int)stack_curr, 
659                                                  storeBuf, size, comparison);
660                 continue;
661             }
662 
663             if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, 
664                                       &curr, comparison)) {
665                 return;
666             }
667         }
668     }
669 
670     private:
671 
672 
673     /* adapted from Hacker's Delight */
674     static int clzll(ulong x) pure nothrow @nogc
675     {
676         if (x == 0)
677             return 64;
678 
679         // Note: not worth optimizing further with `63 - bsr(x)`
680         // It's simply called once.
681         int n = 0;
682 
683         if (x <= 0x00000000FFFFFFFFL) 
684         {
685             n = n + 32;
686             x = x << 32;
687         }
688 
689         if (x <= 0x0000FFFFFFFFFFFFL) 
690         {
691             n = n + 16;
692             x = x << 16;
693         }
694 
695         if (x <= 0x00FFFFFFFFFFFFFFL) 
696         {
697             n = n + 8;
698             x = x << 8;
699         }
700 
701         if (x <= 0x0FFFFFFFFFFFFFFFL) 
702         {
703             n = n + 4;
704             x = x << 4;
705         }
706 
707         if (x <= 0x3FFFFFFFFFFFFFFFL) 
708         {
709             n = n + 2;
710             x = x << 2;
711         }
712 
713         if (x <= 0x7FFFFFFFFFFFFFFFL) 
714         {
715             n = n + 1;
716         }
717         return n;
718     }
719     unittest
720     {
721         assert(clzll(0) == 64);
722         assert(clzll(1) == 63);
723         assert(clzll(-1) == 0);
724     }
725 
726     static int tim_sort_compute_minrun(const ulong size) pure nothrow @nogc
727     {
728         int top_bit = 64 - clzll(size);
729         int maxbit = top_bit > 6 ? top_bit : 6;
730         int shift = maxbit - 6;
731         int minrun = cast(int)(size >> shift);
732         ulong mask = ((cast(ulong)1) << shift) - 1;
733         if (mask & size) 
734         {
735             return minrun + 1;
736         }
737         return minrun;
738     }
739 
740 
741     struct tim_sort_run_t
742     {
743         size_t start;
744         size_t length;
745     }
746 
747     /* Function used to do a binary search for binary insertion sort */
748     size_t tim_sort_binary_inversion_find(T)(T *dst, const T x, const size_t size, 
749                                              nogcComparisonFunction!T comparison) nothrow @nogc
750     {
751         size_t l, c, r;
752         T cx;
753         l = 0;
754         r = size - 1;
755         c = r >> 1;
756 
757         /* check for out of bounds at the beginning. */
758         if (comparison(x, dst[0]) < 0) 
759         {
760             return 0;
761         } else if (comparison(x, dst[r]) > 0) 
762         {
763             return r;
764         }
765 
766         cx = dst[c];
767 
768         while (1) 
769         {
770             const int val = comparison(x, cx);
771 
772             if (val < 0) 
773             {
774                 if (c - l <= 1) 
775                 {
776                     return c;
777                 }
778 
779                 r = c;
780             } else 
781             { /* allow = for stability. The binary search favors the right. */
782                 if (r - c <= 1) 
783                 {
784                     return c + 1;
785                 }
786                 l = c;
787             }
788 
789             c = l + ((r - l) >> 1);
790             cx = dst[c];
791         }
792     }
793 
794     // Binary insertion sort, but knowing that the first "start" entries are sorted.
795     static void tim_sort_binary_inversion_sort_start(T)(T *dst, 
796                                                const size_t start, 
797                                                const size_t size, 
798                                                nogcComparisonFunction!T comparison) nothrow @nogc 
799     {
800         size_t i;
801 
802         for (i = start; i < size; i++) {
803             size_t j;
804             T x;
805             size_t location;
806 
807             /* If this entry is already correct, just move along */
808             if (comparison(dst[i - 1], dst[i]) <= 0) {
809                 continue;
810             }
811 
812             /* Else we need to find the right place, shift everything over, and squeeze in */
813             x = dst[i];
814             location = tim_sort_binary_inversion_find!T(dst, x, i, comparison);
815 
816             for (j = i - 1; j >= location; j--) {
817                 dst[j + 1] = dst[j];
818 
819                 if (j == 0) { /* check edge case because j is unsigned */
820                     break;
821                 }
822             }
823 
824             dst[location] = x;
825         }
826     }
827 
828     /* Binary insertion sort */
829     static void tim_sort_binary_inversion_sort(T)(T *dst, 
830                                          const size_t size, 
831                                          nogcComparisonFunction!T comparison) 
832     {
833         /* don't bother sorting an array of size <= 1 */
834         if (size <= 1) {
835             return;
836         }
837         tim_sort_binary_inversion_sort_start!T(dst, 1, size, comparison);
838     }
839 
840     /* timsort implementation, based on timsort.txt */
841 
842     static void tim_sort_reverse_elements(T)(T *dst, size_t start, size_t end) 
843     {
844         while (1) {
845             if (start >= end) {
846                 return;
847             }
848 
849             T temp = dst[start]; // swap
850             dst[start] = dst[end];
851             dst[end] = temp;
852 
853             start++;
854             end--;
855         }
856     }
857 
858     static size_t tim_sort_count_run(T)(T *dst, const size_t start, const size_t size, nogcComparisonFunction!T comparison) 
859     {
860         size_t curr;
861 
862         if (size - start == 1) {
863             return 1;
864         }
865 
866         if (start >= size - 2) {
867             if (comparison(dst[size - 2], dst[size - 1]) > 0) 
868             {
869                 // swap
870                 T temp = dst[size - 2];
871                 dst[size - 2] = dst[size - 1];
872                 dst[size - 1] = temp;
873             }
874 
875             return 2;
876         }
877 
878         curr = start + 2;
879 
880         if (comparison(dst[start], dst[start + 1]) <= 0) {
881             /* increasing run */
882             while (1) {
883                 if (curr == size - 1) {
884                     break;
885                 }
886 
887                 if (comparison(dst[curr - 1], dst[curr]) > 0) {
888                     break;
889                 }
890 
891                 curr++;
892             }
893 
894             return curr - start;
895         } else {
896             /* decreasing run */
897             while (1) {
898                 if (curr == size - 1) {
899                     break;
900                 }
901 
902                 if (comparison(dst[curr - 1], dst[curr]) <= 0) {
903                     break;
904                 }
905 
906                 curr++;
907             }
908 
909             /* reverse in-place */
910             tim_sort_reverse_elements!T(dst, start, curr - 1);
911             return curr - start;
912         }
913     }
914 
915     static int tim_sort_check_invariant(tim_sort_run_t *stack, const int stack_curr) nothrow @nogc
916     {
917         size_t A, B, C;
918 
919         if (stack_curr < 2) {
920             return 1;
921         }
922 
923         if (stack_curr == 2) {
924             const size_t A1 = stack[stack_curr - 2].length;
925             const size_t B1 = stack[stack_curr - 1].length;
926 
927             if (A1 <= B1) {
928                 return 0;
929             }
930 
931             return 1;
932         }
933 
934         A = stack[stack_curr - 3].length;
935         B = stack[stack_curr - 2].length;
936         C = stack[stack_curr - 1].length;
937 
938         if ((A <= B + C) || (B <= C)) 
939         {
940             return 0;
941         }
942 
943         return 1;
944     }
945 
946     static void tim_sort_merge(T)(T *dst, 
947                                   const tim_sort_run_t *stack, 
948                                   const int stack_curr,
949                                   ref Vec!T storeBuf,
950                                   nogcComparisonFunction!T comparison) 
951     {
952         const size_t A = stack[stack_curr - 2].length;
953         const size_t B = stack[stack_curr - 1].length;
954         const size_t curr = stack[stack_curr - 2].start;
955         
956         size_t i, j, k;
957 
958         size_t minSize = (A < B) ? A : B;
959 
960         storeBuf.resize( minSize );
961         T* storage = storeBuf.ptr;
962 
963         /* left merge */
964         if (A < B) {
965             memcpy(storage, &dst[curr], A * T.sizeof);
966             i = 0;
967             j = curr + A;
968 
969             for (k = curr; k < curr + A + B; k++) {
970                 if ((i < A) && (j < curr + A + B)) {
971                     if (comparison(storage[i], dst[j]) <= 0) {
972                         dst[k] = storage[i++];
973                     } else {
974                         dst[k] = dst[j++];
975                     }
976                 } else if (i < A) {
977                     dst[k] = storage[i++];
978                 } else {
979                     break;
980                 }
981             }
982         } else {
983             /* right merge */
984             memcpy(storage, &dst[curr + A], B * T.sizeof);
985             i = B;
986             j = curr + A;
987             k = curr + A + B;
988 
989             while (k > curr) {
990                 k--;
991                 if ((i > 0) && (j > curr)) {
992                     if (comparison(dst[j - 1], storage[i - 1]) > 0) {
993                         dst[k] = dst[--j];
994                     } else {
995                         dst[k] = storage[--i];
996                     }
997                 } else if (i > 0) {
998                     dst[k] = storage[--i];
999                 } else {
1000                     break;
1001                 }
1002             }
1003         }
1004     }
1005 
1006     static int tim_sort_collapse(T)(T *dst, 
1007                                     tim_sort_run_t *stack, 
1008                                     int stack_curr,
1009                                     ref Vec!T storeBuf,
1010                                     const size_t size, 
1011                                     nogcComparisonFunction!T comparison) nothrow @nogc
1012     {
1013         while (1) 
1014         {
1015             size_t A, B, C, D;
1016             int ABC, BCD, CD;
1017 
1018             /* if the stack only has one thing on it, we are done with the collapse */
1019             if (stack_curr <= 1) {
1020                 break;
1021             }
1022 
1023             /* if this is the last merge, just do it */
1024             if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {
1025                 tim_sort_merge!T(dst, stack, stack_curr, storeBuf, comparison);
1026                 stack[0].length += stack[1].length;
1027                 stack_curr--;
1028                 break;
1029             }
1030             /* check if the invariant is off for a stack of 2 elements */
1031             else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {
1032                 tim_sort_merge!T(dst, stack, stack_curr, storeBuf, comparison);
1033                 stack[0].length += stack[1].length;
1034                 stack_curr--;
1035                 break;
1036             } else if (stack_curr == 2) {
1037                 break;
1038             }
1039 
1040             B = stack[stack_curr - 3].length;
1041             C = stack[stack_curr - 2].length;
1042             D = stack[stack_curr - 1].length;
1043 
1044             if (stack_curr >= 4) {
1045                 A = stack[stack_curr - 4].length;
1046                 ABC = (A <= B + C);
1047             } else {
1048                 ABC = 0;
1049             }
1050 
1051             BCD = (B <= C + D) || ABC;
1052             CD = (C <= D);
1053 
1054             /* Both invariants are good */
1055             if (!BCD && !CD) {
1056                 break;
1057             }
1058 
1059             /* left merge */
1060             if (BCD && !CD) {
1061                 tim_sort_merge!T(dst, stack, stack_curr - 1, storeBuf, comparison);
1062                 stack[stack_curr - 3].length += stack[stack_curr - 2].length;
1063                 stack[stack_curr - 2] = stack[stack_curr - 1];
1064                 stack_curr--;
1065             } else {
1066                 /* right merge */
1067                 tim_sort_merge!T(dst, stack, stack_curr, storeBuf, comparison);
1068                 stack[stack_curr - 2].length += stack[stack_curr - 1].length;
1069                 stack_curr--;
1070             }
1071         }
1072 
1073         return stack_curr;
1074     }
1075 
1076     static int tim_sort_push_next(T)(T *dst,
1077                             const size_t size,
1078                             ref Vec!T storeBuf,
1079                             const size_t minrun,
1080                             tim_sort_run_t *run_stack,
1081                             size_t *stack_curr,
1082                             size_t *curr,
1083                             nogcComparisonFunction!T comparison) 
1084     {
1085         size_t len = tim_sort_count_run!T(dst, *curr, size, comparison);
1086         size_t run = minrun;
1087 
1088         if (run > size - *curr) {
1089             run = size - *curr;
1090         }
1091 
1092         if (run > len) {
1093             tim_sort_binary_inversion_sort_start!T(&dst[*curr], len, run, comparison);
1094             len = run;
1095         }
1096 
1097         run_stack[*stack_curr].start = *curr;
1098         run_stack[*stack_curr].length = len;
1099         (*stack_curr)++;
1100         *curr += len;
1101 
1102         if (*curr == size) {
1103             /* finish up */
1104             while (*stack_curr > 1) {
1105                 tim_sort_merge!T(dst, run_stack, cast(int) *stack_curr, storeBuf, comparison);
1106                 run_stack[*stack_curr - 2].length += run_stack[*stack_curr - 1].length;
1107                 (*stack_curr)--;
1108             }
1109 
1110             return 0;
1111         }
1112 
1113         return 1;
1114     }
1115 }
1116 
1117 
1118 /**
1119 $(H1 @nogc Simple Base64 parsing)
1120 
1121 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
1122 Authors: Harrison Ford
1123 Copyright: 2021 Harrison Ford, Symmetry Investments, 2023 Guillaume Piolat
1124 */
1125 // this is from mir.base64 but a bit stripped down.
1126 
1127 private
1128 {
1129 
1130     // NOTE: I do not know if this would work on big-endian systems.
1131     // Needs further testing to figure out if it *does* work on them.
1132 
1133     // Technique borrowed from:
1134     // http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html#branchless-code-for-lookup-table
1135     ubyte lookup_encoding(ubyte i, char plusChar = '+', char slashChar = '/') 
1136         pure nothrow @nogc @safe
1137     {
1138         assert(i < 64);
1139 
1140         ubyte shift;
1141 
1142         if (i < 26)
1143         {
1144             // range A-Z
1145             shift = 'A';
1146         }
1147         else if (i >= 26 && i < 52)
1148         {
1149             // range a-z
1150             shift = 'a' - 26;
1151         }
1152         else if (i >= 52 && i < 62)
1153         {
1154             // range 0-9
1155             shift = cast(ubyte)('0' - 52);
1156         }
1157         else if (i == 62)
1158         {
1159             // character plus
1160             shift = cast(ubyte)(plusChar - 62);
1161         }
1162         else if (i == 63)
1163         {
1164             // character slash
1165             shift = cast(ubyte)(slashChar - 63);
1166         }
1167 
1168         return cast(char)(i + shift);
1169     }
1170 
1171     // Do the inverse of above (convert an ASCII value into the Base64 character set)
1172     ubyte lookup_decoding(ubyte i, char plusChar, char slashChar, bool* err)
1173         pure nothrow @nogc @safe
1174     {
1175         *err = false;
1176         // Branching bad, but this isn't performance sensitive
1177         if (i <= 'Z' && i >= 'A') {
1178             return cast(ubyte)(i - 'A');
1179         }
1180         else if (i <= 'z' && i >= 'a') {
1181             return cast(ubyte)(i - 'a' + 26); 
1182         }
1183         else if (i <= '9' && i >= '0') {
1184             return cast(ubyte)(i - '0' + 52);
1185         }
1186         else if (i == plusChar) {
1187             return 62;
1188         }
1189         else if (i == slashChar) {
1190             return 63;
1191         }
1192         // Just return 0 for padding,
1193         // as it typically means nothing.
1194         else if (i == '=') {
1195             return 0;
1196         }
1197         else 
1198         {
1199             *err = true;
1200             return 0;
1201         }
1202     }
1203 }
1204 
1205 /// Decode a Base64 encoded value, returning a buffer to be freed with free().
1206 /// `null` in case of error or zero size.
1207 ubyte[] decodeBase64(scope const(ubyte)[] data, char plusChar = '+', char slashChar = '/') 
1208     nothrow @nogc @system
1209 {
1210     Vec!ubyte outBuffer;
1211     bool err;
1212     decodeBase64(data, outBuffer, plusChar, slashChar, &err);
1213     if (err)
1214         return null;
1215     return outBuffer.releaseData;
1216 }
1217 ///ditto
1218 ubyte[] decodeBase64(scope const(char)[] data, char plusChar = '+', char slashChar = '/') 
1219     nothrow @nogc @system
1220 {
1221     return decodeBase64(cast(const(ubyte)[])data, plusChar, slashChar);
1222 }
1223 
1224 /// Decode a Base64 encoded value, appending the result onto a `Vec!ubyte`.
1225 /// Reusing the same `Vec!ubyte` allows you to avoid reallocations.
1226 /// Note: `err` must point to a `bool` and cannot be null. Ugly signature sorry.
1227 void decodeBase64(scope const(ubyte)[] data,
1228                   ref Vec!ubyte outBuffer,
1229                   char plusChar,  // typically: '+'
1230                   char slashChar, // typically: '/'
1231                   bool* err) nothrow @nogc @safe
1232 {
1233     outBuffer.clearContents();
1234     *err = false;
1235     // We expect data should be well-formed (with padding),
1236     // so we should throw if it is not well-formed.
1237     if (data.length % 4 != 0)
1238     {
1239         *err = true;
1240         return;
1241     }
1242 
1243     ubyte[3] decodedByteGroup;
1244     ubyte sz = 0;
1245 
1246     for (size_t i = 0; i < data.length; i += 4)
1247     {
1248         scope const(ubyte)[] group = data[i .. (i + 4)];
1249 
1250         ubyte[4] decodedBytes;
1251         decodedBytes[0] = lookup_decoding(group[0], plusChar, slashChar, err); if (*err) return;
1252         decodedBytes[1] = lookup_decoding(group[1], plusChar, slashChar, err); if (*err) return;
1253 
1254         uint transformed_group = (decodedBytes[0] << 26) | (decodedBytes[1] << 20);
1255 
1256         // According to RFC4648 Section 3.3, we don't have to accept extra padding characters,
1257         // and we can safely throw (and stay within spec).
1258         // x=== is also invalid, so we can just throw on that here.
1259         if (group[0] == '=' || group[1] == '=')
1260         {
1261             *err = true;
1262             return;
1263         }
1264 
1265         // xx=(=)?
1266         if (group[2] == '=')
1267         {
1268             // If we are not at the end of a string, according to RFC4648,
1269             // we can safely treat a padding character as "non-alphabet data",
1270             // and as such, we should throw. See RFC4648 Section 3.3 for more information
1271             if ((i / 4) != ((data.length / 4) - 1))
1272             {
1273                 *err = true;
1274                 return;
1275             }
1276 
1277             if (group[3] == '=')
1278             {
1279                 // xx==
1280                 sz = 1;
1281             }
1282             // xx=x (invalid)
1283             // Padding should not be in the middle of a chunk
1284             else
1285             {
1286                 *err = true;
1287                 return;
1288             }
1289         }
1290         // xxx=
1291         else if (group[3] == '=')
1292         {
1293             // If we are not at the end of a string, according to RFC4648,
1294             // we can safely treat a padding character as "non-alphabet data",
1295             // and as such, we should throw. See RFC4648 Section 3.3 for more information
1296             if ((i / 4) != ((data.length / 4) - 1))
1297             {
1298                 *err = true;
1299                 return;
1300             }
1301 
1302             decodedBytes[2] = lookup_decoding(group[2], plusChar, slashChar, err); if (*err) return;
1303             transformed_group |= (decodedBytes[2] << 14);
1304             sz = 2;
1305         }
1306         // xxxx
1307         else 
1308         {
1309             decodedBytes[2] = lookup_decoding(group[2], plusChar, slashChar, err); if (*err) return;
1310             decodedBytes[3] = lookup_decoding(group[3], plusChar, slashChar, err); if (*err) return;
1311             transformed_group |= ((decodedBytes[2] << 14) | (decodedBytes[3] << 8)); 
1312             sz = 3;
1313         }
1314 
1315         decodedByteGroup[0] = (transformed_group >> 24) & 0xff;
1316         decodedByteGroup[1] = (transformed_group >> 16) & 0xff;
1317         decodedByteGroup[2] = (transformed_group >> 8) & 0xff;
1318 
1319         // Only emit the transformed bytes that we got data for. 
1320         outBuffer.pushBack(decodedByteGroup[0 .. sz]);
1321     }
1322 }
1323 
1324 /// Test decoding of data which has a length which can be
1325 /// cleanly decoded.
1326 @trusted unittest
1327 {   
1328     // Note: the decoded strings are leaked in this test.
1329     assert("QUJD".decodeBase64 == "ABC");
1330     assert("QQ==".decodeBase64 == "A");
1331     assert("YSBiIGMgZCBlIGYgZyBoIGkgaiBrIGwgbSBuIG8gcCBxIHIgcyB0IHUgdiB3IHggeSB6".decodeBase64 
1332            == "a b c d e f g h i j k l m n o p q r s t u v w x y z");
1333     assert("LCAuIDsgLyBbICcgXSBcID0gLSAwIDkgOCA3IDYgNSA0IDMgMiAxIGAgfiAhIEAgIyAkICUgXiAmICogKCApIF8gKyB8IDogPCA+ID8="
1334            .decodeBase64 == ", . ; / [ ' ] \\ = - 0 9 8 7 6 5 4 3 2 1 ` ~ ! @ # $ % ^ & * ( ) _ + | : < > ?");
1335     assert("AAA=".decodeBase64 == "\x00\x00");
1336     assert("AAAABBCC".decodeBase64 == "\x00\x00\x00\x04\x10\x82");
1337     assert("AA==".decodeBase64 == "\x00");
1338     assert("AA/=".decodeBase64 == "\x00\x0f");
1339 }
1340 
1341 /// Test decoding invalid data
1342 @trusted unittest
1343 {
1344     void testFail(const(char)[] input) @trusted
1345     {
1346         ubyte[] decoded = input.decodeBase64;
1347         assert(decoded is null);
1348         free(decoded.ptr);
1349     }
1350 
1351     testFail("===A");
1352     testFail("A=");
1353     testFail("AA=");
1354     testFail("A=AA");
1355     testFail("AA=A");
1356     testFail("AA=A====");
1357     testFail("=AAA");
1358     testFail("AAA=QUJD");
1359     // This fails because we don't allow extra padding (than what is necessary)
1360     testFail("AA======");
1361     // This fails because we don't allow padding before the end of the string (otherwise we'd 
1362     // have a side-channel)
1363     testFail("QU==QUJD");
1364     testFail("QU======QUJD");
1365     // Invalid data that's out of the alphabet
1366     testFail("!@##@@!@");
1367 }
1368 
1369 
1370 /// Encode a ubyte array as Base64, returning the encoded value, which shall be destroyed with 
1371 /// `free`.
1372 ubyte[] encodeBase64(scope const(ubyte)[] buf, char plusChar = '+', char slashChar = '/') 
1373     nothrow @nogc @system
1374 {
1375     Vec!ubyte outBuf;
1376     encodeBase64(buf, outBuf, plusChar, slashChar);
1377     return outBuf.releaseData;
1378 }
1379 
1380 /// Encode a ubyte array as Base64, placing the result onto an `Vec!ubyte`.
1381 void encodeBase64(scope const(ubyte)[] input,
1382                   scope ref Vec!ubyte outBuf,
1383                   char plusChar = '+',
1384                   char slashChar = '/') nothrow @nogc @trusted
1385 {
1386     outBuf.clearContents();
1387 
1388     // Slice our input array so that n % 3 == 0 (we have a multiple of 3) 
1389     // If we have less then 3, then this is effectively a no-op (will result in a 0-length slice)
1390     ubyte[4] encodedByteGroup;
1391     const(ubyte)[] window = input[0 .. input.length - (input.length % 3)];
1392     assert((window.length % 3) == 0);
1393 
1394     for (size_t n = 0; n < window.length; n += 3)
1395     {
1396         uint group = (window[n] << 24) | (window[n+1] << 16) | (window[n+2] << 8);
1397         const(ubyte) a = (group >> 26) & 0x3f;
1398         const(ubyte) b = (group >> 20) & 0x3f;
1399         const(ubyte) c = (group >> 14) & 0x3f;
1400         const(ubyte) d = (group >> 8) & 0x3f;
1401         encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
1402         encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
1403         encodedByteGroup[2] = c.lookup_encoding(plusChar, slashChar);
1404         encodedByteGroup[3] = d.lookup_encoding(plusChar, slashChar);
1405         outBuf.pushBack(encodedByteGroup[]);
1406     }
1407 
1408     // If it's a clean multiple of 3, then it requires no padding.
1409     // If not, then we need to add padding.
1410     if (input.length % 3 != 0)
1411     {
1412         window = input[window.length .. input.length];
1413 
1414         uint group = (window[0] << 24);
1415 
1416         if (window.length == 1) {
1417             const(ubyte) a = (group >> 26) & 0x3f;
1418             const(ubyte) b = (group >> 20) & 0x3f;
1419             encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
1420             encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
1421             encodedByteGroup[2] = '=';
1422             encodedByteGroup[3] = '=';
1423         }
1424         else 
1425         {
1426             // Just in case 
1427             assert(window.length == 2);
1428 
1429             group |= (window[1] << 16);
1430             const(ubyte) a = (group >> 26) & 0x3f;
1431             const(ubyte) b = (group >> 20) & 0x3f;
1432             const(ubyte) c = (group >> 14) & 0x3f;
1433             encodedByteGroup[0] = a.lookup_encoding(plusChar, slashChar);
1434             encodedByteGroup[1] = b.lookup_encoding(plusChar, slashChar);
1435             encodedByteGroup[2] = c.lookup_encoding(plusChar, slashChar);
1436             encodedByteGroup[3] = '=';
1437         }
1438 
1439         outBuf.pushBack(encodedByteGroup[]);
1440     }
1441 }
1442 
1443 @trusted unittest
1444 {
1445     // Note: encoded data leaked there.
1446     // 3 bytes
1447     {
1448         enum data = cast(immutable(ubyte)[])"ABC";
1449         assert(data.encodeBase64 == "QUJD");
1450     }
1451 
1452     // 6 bytes
1453     {
1454         enum data = cast(immutable(ubyte)[])"ABCDEF";
1455         assert(data.encodeBase64 == "QUJDREVG");
1456     }
1457 
1458     // 9 bytes
1459     {
1460         enum data = cast(immutable(ubyte)[])"ABCDEFGHI";
1461         assert(data.encodeBase64 == "QUJDREVGR0hJ");
1462     }
1463 
1464     // 12 bytes
1465     {
1466         enum data = cast(immutable(ubyte)[])"ABCDEFGHIJKL";
1467         assert(data.encodeBase64 == "QUJDREVGR0hJSktM");
1468     }
1469 }
1470 
1471 /// Test encoding of data which has a length which CANNOT be cleanly encoded.
1472 /// This typically means that there's padding.
1473 @trusted unittest
1474 {
1475     // Note: encoded data leaked there.
1476     // 1 byte 
1477     {
1478         enum data = cast(immutable(ubyte)[])"A";
1479         assert(data.encodeBase64 == "QQ==");
1480     }
1481     // 2 bytes
1482     {
1483         enum data = cast(immutable(ubyte)[])"AB";
1484         assert(data.encodeBase64 == "QUI=");
1485     }
1486     // 2 bytes
1487     {
1488         enum data = [0xFF, 0xFF];
1489         assert(data.encodeBase64 == "//8=");
1490     }
1491     // 4 bytes
1492     {
1493         enum data = [0xDE, 0xAD, 0xBA, 0xBE];
1494         assert(data.encodeBase64 == "3q26vg==");
1495     }
1496     // 37 bytes
1497     {
1498         enum data = cast(immutable(ubyte)[])"A Very Very Very Very Large Test Blob";
1499         assert(data.encodeBase64 == "QSBWZXJ5IFZlcnkgVmVyeSBWZXJ5IExhcmdlIFRlc3QgQmxvYg==");
1500     }
1501 }
1502 
1503 /// Test nogc encoding
1504 @trusted unittest
1505 {
1506     {
1507         
1508         enum data = cast(immutable(ubyte)[])"A Very Very Very Very Large Test Blob";
1509         Vec!ubyte outBuf;
1510         data.encodeBase64(outBuf); 
1511         assert(outBuf[] == "QSBWZXJ5IFZlcnkgVmVyeSBWZXJ5IExhcmdlIFRlc3QgQmxvYg==");     
1512     }
1513 
1514     {
1515         enum data = cast(immutable(ubyte)[])"abc123!?$*&()'-=@~";
1516         Vec!ubyte outBuf;
1517         data.encodeBase64(outBuf);
1518         assert(outBuf[] == "YWJjMTIzIT8kKiYoKSctPUB+");
1519     }
1520 }
1521 
1522 /// Make sure we can decode what we encode.
1523 @trusted unittest
1524 {
1525     // Test an example string
1526     {
1527         enum data = cast(immutable(ubyte)[])"abc123!?$*&()'-=@~";
1528         assert(data.encodeBase64.decodeBase64 == data);
1529     }
1530     // Test an example from Ion data
1531     {
1532         enum data = cast(immutable(ubyte)[])"a b c d e f g h i j k l m n o p q r s t u v w x y z";
1533         assert(data.encodeBase64.decodeBase64 == data);
1534     }
1535 }