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 }