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