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 disabled.
34 
35 //
36 // Faking @nogc
37 //
38 
39 version = useTimSort;
40 
41 auto assumeNoGC(T) (T t)
42 {
43     static if (isFunctionPointer!T || isDelegate!T)
44     {
45         enum attrs = functionAttributes!T | FunctionAttribute.nogc;
46         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
47     }
48     else
49         static assert(false);
50 }
51 
52 auto assumeNothrowNoGC(T) (T t)
53 {
54     static if (isFunctionPointer!T || isDelegate!T)
55     {
56         enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_;
57         return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
58     }
59     else
60         static assert(false);
61 }
62 
63 unittest
64 {
65     void funcThatDoesGC()
66     {
67         int a = 4;
68         int[] b = [a, a, a];
69     }
70 
71     void anotherFunction() nothrow @nogc
72     {
73         assumeNothrowNoGC( (){ funcThatDoesGC(); } )();
74     }
75 
76     void aThirdFunction() @nogc
77     {
78         assumeNoGC( () { funcThatDoesGC(); } )();
79     }
80 }
81 
82 
83 //
84 // Optimistic .destroy, which is @nogc nothrow by breaking the type-system
85 //
86 
87 // for classes
88 void destroyNoGC(T)(T x) nothrow @nogc if (is(T == class) || is(T == interface))
89 {
90     assumeNothrowNoGC(
91         (T x)
92         {
93             return destroy(x);
94         })(x);
95 }
96 
97 // for struct
98 void destroyNoGC(T)(ref T obj) nothrow @nogc if (is(T == struct))
99 {
100     assumeNothrowNoGC(
101         (ref T x)
102         {
103             return destroy(x);
104         })(obj);
105 }
106 /*
107 void destroyNoGC(T : U[n], U, size_t n)(ref T obj) nothrow @nogc
108 {
109     assumeNothrowNoGC(
110         (T x)
111         {
112             return destroy(x);
113         })(obj);
114 }*/
115 
116 void destroyNoGC(T)(ref T obj) nothrow @nogc
117     if (!is(T == struct) && !is(T == class) && !is(T == interface))
118 {
119     assumeNothrowNoGC(
120                       (ref T x)
121                       {
122                           return destroy(x);
123                       })(obj);
124 }
125 
126 
127 
128 //
129 // Constructing and destroying without the GC.
130 //
131 
132 /// Allocates and construct a struct or class object.
133 /// Returns: Newly allocated object.
134 auto mallocNew(T, Args...)(Args args)
135 {
136     static if (is(T == class))
137         immutable size_t allocSize = __traits(classInstanceSize, T);
138     else
139         immutable size_t allocSize = T.sizeof;
140 
141     void* rawMemory = malloc(allocSize);
142     if (!rawMemory)
143         onOutOfMemoryErrorNoGC();
144 
145     static if (is(T == class))
146     {
147         T obj = emplace!T(rawMemory[0 .. allocSize], args);
148     }
149     else
150     {
151         T* obj = cast(T*)rawMemory;
152         emplace!T(obj, args);
153     }
154 
155     return obj;
156 }
157 
158 /// Destroys and frees a class object created with $(D mallocEmplace).
159 void destroyFree(T)(T p) if (is(T == class))
160 {
161     if (p !is null)
162     {
163         destroyNoGC(p);
164         free(cast(void*)p);
165     }
166 }
167 
168 /// Destroys and frees an interface object created with $(D mallocEmplace).
169 void destroyFree(T)(T p) if (is(T == interface))
170 {
171     if (p !is null)
172     {
173         void* here = cast(void*)(cast(Object)p);
174         destroyNoGC(p);
175         free(cast(void*)here);
176     }
177 }
178 
179 /// Destroys and frees a non-class object created with $(D mallocEmplace).
180 void destroyFree(T)(T* p) if (!is(T == class))
181 {
182     if (p !is null)
183     {
184         destroyNoGC(p);
185         free(cast(void*)p);
186     }
187 }
188 
189 
190 unittest
191 {
192     class A
193     {
194         int _i;
195         this(int i)
196         {
197             _i = i;
198         }
199     }
200 
201     struct B
202     {
203         int i;
204     }
205 
206     void testMallocEmplace()
207     {
208         A a = mallocNew!A(4);
209         destroyFree(a);
210 
211         B* b = mallocNew!B(5);
212         destroyFree(b);
213     }
214 
215     testMallocEmplace();
216 }
217 
218 version( D_InlineAsm_X86 )
219 {
220     version = AsmX86;
221 }
222 else version( D_InlineAsm_X86_64 )
223 {
224     version = AsmX86;
225 }
226 
227 /// Allocates a slice with `malloc`.
228 T[] mallocSlice(T)(size_t count) nothrow @nogc
229 {
230     T[] slice = mallocSliceNoInit!T(count);
231     static if (is(T == struct))
232     {
233         // we must avoid calling struct destructors with uninitialized memory
234         for(size_t i = 0; i < count; ++i)
235         {
236             T uninitialized;
237             memcpy(&slice[i], &uninitialized, T.sizeof); // memcpy OK
238         }
239     }
240     else
241         slice[0..count] = T.init;
242     return slice;
243 }
244 
245 /// Allocates a slice with `malloc`, but does not initialize the content.
246 T[] mallocSliceNoInit(T)(size_t count) nothrow @nogc
247 {
248     T* p = cast(T*) malloc(count * T.sizeof);
249     return p[0..count];
250 }
251 
252 /// Frees a slice allocated with `mallocSlice`.
253 void freeSlice(T)(const(T)[] slice) nothrow @nogc
254 {
255     free(cast(void*)(slice.ptr)); // const cast here
256 }
257 
258 /// Duplicates a slice with `malloc`. Equivalent to `.dup`
259 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`.
260 T[] mallocDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct))
261 {
262     T[] copy = mallocSliceNoInit!T(slice.length);
263     memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof);
264     return copy;
265 }
266 
267 /// Duplicates a slice with `malloc`. Equivalent to `.idup`
268 /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`.
269 immutable(T)[] mallocIDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct))
270 {
271     return cast(immutable(T)[]) (mallocDup!T(slice));
272 }
273 
274 /// Duplicates a zero-terminated string with `malloc`, return a `char[]` with zero-terminated byte.
275 /// Has to be cleaned-up with `free(s.ptr)`.
276 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be converted
277 /// to a C string with `.ptr`. However the zero byte is not included in slice length.
278 char[] stringDup(const(char)* cstr) nothrow @nogc
279 {
280     assert(cstr !is null);
281     size_t len = strlen(cstr);
282     char* copy = strdup(cstr);
283     return copy[0..len];
284 }
285 
286 /// Duplicates a zero-terminated string with `malloc`, return a `string`. with zero-terminated byte. 
287 /// Has to be cleaned-up with `free(s.ptr)`.
288 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be converted
289 /// to a C string with `.ptr`. However the zero byte is not included in slice length.
290 string stringIDup(const(char)* cstr) nothrow @nogc
291 {
292     return cast(string) stringDup(cstr);
293 }
294 
295 unittest
296 {
297     int[] slice = mallocSlice!int(4);
298     assert(slice[3] == int.init);
299     freeSlice(slice);    
300 
301     slice = mallocSliceNoInit!int(4);
302     freeSlice(slice);
303 
304     slice = mallocSliceNoInit!int(0);
305     assert(slice == []);
306     freeSlice(slice);
307 }
308 
309 /// Semantic function to check that a D string implicitely conveys a
310 /// termination byte after the slice.
311 /// (typically those comes from string literals or `stringDup`/`stringIDup`)
312 const(char)* assumeZeroTerminated(const(char)[] input) nothrow @nogc
313 {
314     if (input.ptr is null)
315         return null;
316 
317     // Check that the null character is there
318     assert(input.ptr[input.length] == '\0');
319     return input.ptr;
320 }
321 
322 //
323 // STABLE IN-PLACE SORT (implementation is at bottom of file)
324 //
325 // Here is how to use it:
326 unittest
327 {
328     {
329         int[2][] testData = [[110, 0], [5, 0], [10, 0], [3, 0], [110, 1], [5, 1], [10, 1], [3, 1]];
330         version(useTimSort)
331         {
332             Vec!(int[2]) tempBuf;
333             timSort!(int[2])(testData, tempBuf, (a, b) => (a[0] - b[0]));        
334         }
335         assert(testData == [[3, 0], [3, 1], [5, 0], [5, 1], [10, 0], [10, 1], [110, 0], [110, 1]]);
336     }    
337 }
338 
339 
340 //
341 // STABLE MERGE SORT
342 //
343 
344 /// A bit faster than a dynamic cast.
345 /// This is to avoid TypeInfo look-up.
346 T unsafeObjectCast(T)(Object obj)
347 {
348     return cast(T)(cast(void*)(obj));
349 }
350 
351 /// Outputs a debug string in either:
352 ///  - stdout on POSIX-like (visible in the command-line)
353 ///  - the Output Windows on Windows (visible withing Visual Studio or with dbgview.exe)
354 /// Warning: no end-of-line added!
355 void debugLog(const(char)* message) nothrow @nogc
356 {
357     version(Windows)
358     {
359         import core.sys.windows.windows;
360         OutputDebugStringA(message);
361     }
362     else
363     {
364         import core.stdc.stdio;
365         printf("%s\n", message);
366     }
367 }
368 
369 ///ditto
370 extern (C) void debugLogf(const(char)* fmt, ...) nothrow @nogc
371 {
372     // This is a complete hack to be able to build in Ubuntu Focal, which distributes D front-ends based
373     // upon DMDFE 2.090. In these compilers, va_start is not marked @nogc.
374     static if (__VERSION__ > 2090)
375     {
376         import core.stdc.stdio;
377 
378         char[256] buffer;
379         va_list args;
380         va_start (args, fmt);
381         vsnprintf (buffer.ptr, 256, fmt, args);
382         va_end (args);
383 
384         version(Windows)
385         {
386             import core.sys.windows.windows;
387             OutputDebugStringA(buffer.ptr);
388         }
389         else
390         {        
391             printf("%s\n", buffer.ptr);
392         }
393     }
394 }
395 
396 /// Inserts a breakpoint instruction. useful to trigger the debugger.
397 void debugBreak() nothrow @nogc
398 {
399     version( AsmX86 )
400     {
401         asm nothrow @nogc
402         {
403             int 3;
404         }
405     }
406     else version( GNU )
407     {
408         // __builtin_trap() is not the same thing unfortunately
409         asm
410         {
411             "int $0x03" : : : ;
412         }
413     }
414     else version(LDC)
415     {
416     	import ldc.intrinsics;
417     	llvm_debugtrap();
418     }
419     else
420     {
421         static assert(false, "No debugBreak() for this compiler");
422     }
423 }
424 
425 
426 // Copy source into dest.
427 // dest must contain room for maxChars characters
428 // A zero-byte character is then appended.
429 void stringNCopy(char* dest, size_t maxChars, const(char)[] source) nothrow @nogc
430 {
431     if (maxChars == 0)
432         return;
433 
434     size_t max = maxChars < source.length ? maxChars - 1 : source.length;
435     for (int i = 0; i < max; ++i)
436         dest[i] = source[i];
437     dest[max] = '\0';
438 }
439 
440 
441 //
442 // Low-cost C string conversions
443 //
444 alias CString = CStringImpl!char;
445 alias CString16 = CStringImpl!wchar;
446 
447 /// Zero-terminated C string, to replace toStringz and toUTF16z
448 struct CStringImpl(CharType) if (is(CharType: char) || is(CharType: wchar))
449 {
450 public:
451 nothrow:
452 @nogc:
453 
454     const(CharType)* storage = null;
455     alias storage this;
456 
457 
458     this(const(CharType)[] s)
459     {
460         // Always copy. We can't assume anything about the input.
461         size_t len = s.length;
462         CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof);
463         buffer[0..len] = s[0..len];
464         buffer[len] = '\0';
465         storage = buffer;
466         wasAllocated = true;
467     }
468 
469     // The constructor taking immutable can safely assume that such memory
470     // has been allocated by the GC or malloc, or an allocator that align
471     // pointer on at least 4 bytes.
472     this(immutable(CharType)[] s)
473     {
474         // Same optimizations that for toStringz
475         if (s.length == 0)
476         {
477             enum emptyString = cast(CharType[])"";
478             storage = emptyString.ptr;
479             return;
480         }
481 
482         /* Peek past end of s[], if it's 0, no conversion necessary.
483         * Note that the compiler will put a 0 past the end of static
484         * strings, and the storage allocator will put a 0 past the end
485         * of newly allocated char[]'s.
486         */
487         const(CharType)* p = s.ptr + s.length;
488         // Is p dereferenceable? A simple test: if the p points to an
489         // address multiple of 4, then conservatively assume the pointer
490         // might be pointing to another block of memory, which might be
491         // unreadable. Otherwise, it's definitely pointing to valid
492         // memory.
493         if ((cast(size_t) p & 3) && *p == 0)
494         {
495             storage = s.ptr;
496             return;
497         }
498 
499         size_t len = s.length;
500         CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof);
501         buffer[0..len] = s[0..len];
502         buffer[len] = '\0';
503         storage = buffer;
504         wasAllocated = true;
505     }
506 
507     ~this()
508     {
509         if (wasAllocated)
510             free(cast(void*)storage);
511     }
512 
513     @disable this(this);
514 
515 private:
516     bool wasAllocated = false;
517 }
518 
519 
520 //
521 // Launch browser, replaces std.process.browse
522 //
523 
524 void browseNoGC(string url) nothrow @nogc
525 {
526     version(Windows)
527     {
528         import core.sys.windows.winuser;
529         import core.sys.windows.shellapi;
530         ShellExecuteA(null, CString("open").storage, CString(url).storage, null, null, SW_SHOWNORMAL);
531     }
532 
533     version(OSX)
534     {
535         import core.sys.posix.unistd;
536         const(char)*[5] args;
537 
538         auto curl = CString(url).storage;
539         const(char)* browser = getenv("BROWSER");
540         if (browser)
541         {
542             browser = strdup(browser);
543             args[0] = browser;
544             args[1] = curl;
545             args[2] = null;
546         }
547         else
548         {
549             args[0] = "open".ptr;
550             args[1] = curl;
551             args[2] = null;
552         }
553 
554         auto childpid = core.sys.posix.unistd.fork();
555         if (childpid == 0)
556         {
557             core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr);
558             return;
559         }
560         if (browser)
561             free(cast(void*)browser);
562     }
563     version(linux)
564     {
565         import core.sys.posix.stdlib;
566         import core.stdc.stdio;
567         char[256] curl;
568         sprintf(curl.ptr, "%s %s", "xdg-open".ptr, CString(url).storage);
569         system(curl.ptr);
570     }
571 }
572 
573 //
574 // @nogc sorting.
575 //
576 
577 /// Must return -1 if a < b
578 ///              0 if a == b
579 ///              1 if a > b
580 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc;
581 
582 
583 version(useTimSort)
584 {
585 
586     public void timSort(T)(T[] dst, 
587                        ref Vec!T storeBuf, // content unimportant, this will be use a temp storage.
588                                            // it should be "grow-only"
589                        nogcComparisonFunction!T comparison) nothrow @nogc
590     {
591         const size_t size = dst.length;
592 
593         /* don't bother sorting an array of size 1 */
594         if (size <= 1) {
595             return;
596         }
597 
598         if (size < 64) 
599         {
600             // uh... all out test cases are here
601             tim_sort_binary_inversion_sort!T(dst.ptr, size, comparison);
602             return;
603         }
604 
605         // Why would it be used only there???
606         enum TIM_SORT_STACK_SIZE = 64;
607         tim_sort_run_t[TIM_SORT_STACK_SIZE] run_stack;
608         size_t stack_curr = 0;
609         size_t curr = 0;
610 
611         /* compute the minimum run length */
612         size_t minrun = tim_sort_compute_minrun(size);
613 
614         if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, &curr, comparison)) {
615             return;
616         }
617 
618         if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, &curr, comparison)) {
619             return;
620         }
621 
622         if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, &curr, comparison)) {
623             return;
624         }
625 
626         while (1) {
627             if (!tim_sort_check_invariant(run_stack.ptr, cast(int)stack_curr)) {
628                 stack_curr = tim_sort_collapse!T(dst.ptr, run_stack.ptr, cast(int)stack_curr, storeBuf, size, comparison);
629                 continue;
630             }
631 
632             if (!tim_sort_push_next!T(dst.ptr, size, storeBuf, minrun, run_stack.ptr, &stack_curr, &curr, comparison)) {
633                 return;
634             }
635         }
636     }
637 
638     private:
639 
640 
641     /* adapted from Hacker's Delight */
642     static int clzll(ulong x) pure nothrow @nogc
643     {
644         if (x == 0)
645             return 64;
646 
647         // Note: not worth optimizing further with `63 - bsr(x)`
648         // It's simply called once.
649         int n = 0;
650 
651         if (x <= 0x00000000FFFFFFFFL) 
652         {
653             n = n + 32;
654             x = x << 32;
655         }
656 
657         if (x <= 0x0000FFFFFFFFFFFFL) 
658         {
659             n = n + 16;
660             x = x << 16;
661         }
662 
663         if (x <= 0x00FFFFFFFFFFFFFFL) 
664         {
665             n = n + 8;
666             x = x << 8;
667         }
668 
669         if (x <= 0x0FFFFFFFFFFFFFFFL) 
670         {
671             n = n + 4;
672             x = x << 4;
673         }
674 
675         if (x <= 0x3FFFFFFFFFFFFFFFL) 
676         {
677             n = n + 2;
678             x = x << 2;
679         }
680 
681         if (x <= 0x7FFFFFFFFFFFFFFFL) 
682         {
683             n = n + 1;
684         }
685         return n;
686     }
687     unittest
688     {
689         assert(clzll(0) == 64);
690         assert(clzll(1) == 63);
691         assert(clzll(-1) == 0);
692     }
693 
694     static int tim_sort_compute_minrun(const ulong size) pure nothrow @nogc
695     {
696         int top_bit = 64 - clzll(size);
697         int maxbit = top_bit > 6 ? top_bit : 6;
698         int shift = maxbit - 6;
699         int minrun = cast(int)(size >> shift);
700         ulong mask = ((cast(ulong)1) << shift) - 1;
701         if (mask & size) 
702         {
703             return minrun + 1;
704         }
705         return minrun;
706     }
707 
708 
709     struct tim_sort_run_t
710     {
711         size_t start;
712         size_t length;
713     }
714 
715     /* Function used to do a binary search for binary insertion sort */
716     size_t tim_sort_binary_inversion_find(T)(T *dst, const T x, const size_t size, nogcComparisonFunction!T comparison) nothrow @nogc
717     {
718         size_t l, c, r;
719         T cx;
720         l = 0;
721         r = size - 1;
722         c = r >> 1;
723 
724         /* check for out of bounds at the beginning. */
725         if (comparison(x, dst[0]) < 0) 
726         {
727             return 0;
728         } else if (comparison(x, dst[r]) > 0) 
729         {
730             return r;
731         }
732 
733         cx = dst[c];
734 
735         while (1) 
736         {
737             const int val = comparison(x, cx);
738 
739             if (val < 0) 
740             {
741                 if (c - l <= 1) 
742                 {
743                     return c;
744                 }
745 
746                 r = c;
747             } else 
748             { /* allow = for stability. The binary search favors the right. */
749                 if (r - c <= 1) 
750                 {
751                     return c + 1;
752                 }
753                 l = c;
754             }
755 
756             c = l + ((r - l) >> 1);
757             cx = dst[c];
758         }
759     }
760 
761     /* Binary insertion sort, but knowing that the first "start" entries are sorted.  Used in timsort. */
762     static void tim_sort_binary_inversion_sort_start(T)(T *dst, 
763                                                const size_t start, 
764                                                const size_t size, 
765                                                nogcComparisonFunction!T comparison) nothrow @nogc 
766     {
767         size_t i;
768 
769         for (i = start; i < size; i++) {
770             size_t j;
771             T x;
772             size_t location;
773 
774             /* If this entry is already correct, just move along */
775             if (comparison(dst[i - 1], dst[i]) <= 0) {
776                 continue;
777             }
778 
779             /* Else we need to find the right place, shift everything over, and squeeze in */
780             x = dst[i];
781             location = tim_sort_binary_inversion_find!T(dst, x, i, comparison);
782 
783             for (j = i - 1; j >= location; j--) {
784                 dst[j + 1] = dst[j];
785 
786                 if (j == 0) { /* check edge case because j is unsigned */
787                     break;
788                 }
789             }
790 
791             dst[location] = x;
792         }
793     }
794 
795     /* Binary insertion sort */
796     static void tim_sort_binary_inversion_sort(T)(T *dst, 
797                                          const size_t size, 
798                                          nogcComparisonFunction!T comparison) 
799     {
800         /* don't bother sorting an array of size <= 1 */
801         if (size <= 1) {
802             return;
803         }
804         tim_sort_binary_inversion_sort_start!T(dst, 1, size, comparison);
805     }
806 
807     /* timsort implementation, based on timsort.txt */
808 
809     static void tim_sort_reverse_elements(T)(T *dst, size_t start, size_t end) 
810     {
811         while (1) {
812             if (start >= end) {
813                 return;
814             }
815 
816             T temp = dst[start]; // swap
817             dst[start] = dst[end];
818             dst[end] = temp;
819 
820             start++;
821             end--;
822         }
823     }
824 
825     static size_t tim_sort_count_run(T)(T *dst, const size_t start, const size_t size, nogcComparisonFunction!T comparison) 
826     {
827         size_t curr;
828 
829         if (size - start == 1) {
830             return 1;
831         }
832 
833         if (start >= size - 2) {
834             if (comparison(dst[size - 2], dst[size - 1]) > 0) 
835             {
836                 // swap
837                 T temp = dst[size - 2];
838                 dst[size - 2] = dst[size - 1];
839                 dst[size - 1] = temp;
840             }
841 
842             return 2;
843         }
844 
845         curr = start + 2;
846 
847         if (comparison(dst[start], dst[start + 1]) <= 0) {
848             /* increasing run */
849             while (1) {
850                 if (curr == size - 1) {
851                     break;
852                 }
853 
854                 if (comparison(dst[curr - 1], dst[curr]) > 0) {
855                     break;
856                 }
857 
858                 curr++;
859             }
860 
861             return curr - start;
862         } else {
863             /* decreasing run */
864             while (1) {
865                 if (curr == size - 1) {
866                     break;
867                 }
868 
869                 if (comparison(dst[curr - 1], dst[curr]) <= 0) {
870                     break;
871                 }
872 
873                 curr++;
874             }
875 
876             /* reverse in-place */
877             tim_sort_reverse_elements!T(dst, start, curr - 1);
878             return curr - start;
879         }
880     }
881 
882     static int tim_sort_check_invariant(tim_sort_run_t *stack, const int stack_curr) nothrow @nogc
883     {
884         size_t A, B, C;
885 
886         if (stack_curr < 2) {
887             return 1;
888         }
889 
890         if (stack_curr == 2) {
891             const size_t A1 = stack[stack_curr - 2].length;
892             const size_t B1 = stack[stack_curr - 1].length;
893 
894             if (A1 <= B1) {
895                 return 0;
896             }
897 
898             return 1;
899         }
900 
901         A = stack[stack_curr - 3].length;
902         B = stack[stack_curr - 2].length;
903         C = stack[stack_curr - 1].length;
904 
905         if ((A <= B + C) || (B <= C)) 
906         {
907             return 0;
908         }
909 
910         return 1;
911     }
912 
913     static void tim_sort_merge(T)(T *dst, 
914                                   const tim_sort_run_t *stack, 
915                                   const int stack_curr,
916                                   ref Vec!T storeBuf,
917                                   nogcComparisonFunction!T comparison) 
918     {
919         const size_t A = stack[stack_curr - 2].length;
920         const size_t B = stack[stack_curr - 1].length;
921         const size_t curr = stack[stack_curr - 2].start;
922         
923         size_t i, j, k;
924 
925         size_t minSize = (A < B) ? A : B;
926 
927         storeBuf.resize( minSize );
928         T* storage = storeBuf.ptr;
929 
930         /* left merge */
931         if (A < B) {
932             memcpy(storage, &dst[curr], A * T.sizeof);
933             i = 0;
934             j = curr + A;
935 
936             for (k = curr; k < curr + A + B; k++) {
937                 if ((i < A) && (j < curr + A + B)) {
938                     if (comparison(storage[i], dst[j]) <= 0) {
939                         dst[k] = storage[i++];
940                     } else {
941                         dst[k] = dst[j++];
942                     }
943                 } else if (i < A) {
944                     dst[k] = storage[i++];
945                 } else {
946                     break;
947                 }
948             }
949         } else {
950             /* right merge */
951             memcpy(storage, &dst[curr + A], B * T.sizeof);
952             i = B;
953             j = curr + A;
954             k = curr + A + B;
955 
956             while (k > curr) {
957                 k--;
958                 if ((i > 0) && (j > curr)) {
959                     if (comparison(dst[j - 1], storage[i - 1]) > 0) {
960                         dst[k] = dst[--j];
961                     } else {
962                         dst[k] = storage[--i];
963                     }
964                 } else if (i > 0) {
965                     dst[k] = storage[--i];
966                 } else {
967                     break;
968                 }
969             }
970         }
971     }
972 
973     static int tim_sort_collapse(T)(T *dst, 
974                                     tim_sort_run_t *stack, 
975                                     int stack_curr,
976                                     ref Vec!T storeBuf,
977                                     const size_t size, 
978                                     nogcComparisonFunction!T comparison) nothrow @nogc
979     {
980         while (1) 
981         {
982             size_t A, B, C, D;
983             int ABC, BCD, CD;
984 
985             /* if the stack only has one thing on it, we are done with the collapse */
986             if (stack_curr <= 1) {
987                 break;
988             }
989 
990             /* if this is the last merge, just do it */
991             if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {
992                 tim_sort_merge!T(dst, stack, stack_curr, storeBuf, comparison);
993                 stack[0].length += stack[1].length;
994                 stack_curr--;
995                 break;
996             }
997             /* check if the invariant is off for a stack of 2 elements */
998             else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {
999                 tim_sort_merge!T(dst, stack, stack_curr, storeBuf, comparison);
1000                 stack[0].length += stack[1].length;
1001                 stack_curr--;
1002                 break;
1003             } else if (stack_curr == 2) {
1004                 break;
1005             }
1006 
1007             B = stack[stack_curr - 3].length;
1008             C = stack[stack_curr - 2].length;
1009             D = stack[stack_curr - 1].length;
1010 
1011             if (stack_curr >= 4) {
1012                 A = stack[stack_curr - 4].length;
1013                 ABC = (A <= B + C);
1014             } else {
1015                 ABC = 0;
1016             }
1017 
1018             BCD = (B <= C + D) || ABC;
1019             CD = (C <= D);
1020 
1021             /* Both invariants are good */
1022             if (!BCD && !CD) {
1023                 break;
1024             }
1025 
1026             /* left merge */
1027             if (BCD && !CD) {
1028                 tim_sort_merge!T(dst, stack, stack_curr - 1, storeBuf, comparison);
1029                 stack[stack_curr - 3].length += stack[stack_curr - 2].length;
1030                 stack[stack_curr - 2] = stack[stack_curr - 1];
1031                 stack_curr--;
1032             } else {
1033                 /* right merge */
1034                 tim_sort_merge!T(dst, stack, stack_curr, storeBuf, comparison);
1035                 stack[stack_curr - 2].length += stack[stack_curr - 1].length;
1036                 stack_curr--;
1037             }
1038         }
1039 
1040         return stack_curr;
1041     }
1042 
1043     static int tim_sort_push_next(T)(T *dst,
1044                             const size_t size,
1045                             ref Vec!T storeBuf,
1046                             const size_t minrun,
1047                             tim_sort_run_t *run_stack,
1048                             size_t *stack_curr,
1049                             size_t *curr,
1050                             nogcComparisonFunction!T comparison) 
1051     {
1052         size_t len = tim_sort_count_run!T(dst, *curr, size, comparison);
1053         size_t run = minrun;
1054 
1055         if (run > size - *curr) {
1056             run = size - *curr;
1057         }
1058 
1059         if (run > len) {
1060             tim_sort_binary_inversion_sort_start!T(&dst[*curr], len, run, comparison);
1061             len = run;
1062         }
1063 
1064         run_stack[*stack_curr].start = *curr;
1065         run_stack[*stack_curr].length = len;
1066         (*stack_curr)++;
1067         *curr += len;
1068 
1069         if (*curr == size) {
1070             /* finish up */
1071             while (*stack_curr > 1) {
1072                 tim_sort_merge!T(dst, run_stack, cast(int) *stack_curr, storeBuf, comparison);
1073                 run_stack[*stack_curr - 2].length += run_stack[*stack_curr - 1].length;
1074                 (*stack_curr)--;
1075             }
1076 
1077             return 0;
1078         }
1079 
1080         return 1;
1081     }
1082 }