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, Auburn Sounds. 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..string: strdup, memcpy, strlen; 23 import core.stdc.stdlib: malloc, free, getenv; 24 import core.memory: GC; 25 import core.exception: onOutOfMemoryErrorNoGC; 26 27 import std.conv: emplace; 28 import std.traits; 29 import std.array: empty; 30 import std.exception: assumeUnique; 31 32 // This module provides many utilities to deal with @nogc 33 34 version = doNotUseRuntime; 35 36 // 37 // Faking @nogc 38 // 39 40 auto assumeNoGC(T) (T t) 41 { 42 static if (isFunctionPointer!T || isDelegate!T) 43 { 44 enum attrs = functionAttributes!T | FunctionAttribute.nogc; 45 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 46 } 47 else 48 static assert(false); 49 } 50 51 auto assumeNothrowNoGC(T) (T t) 52 { 53 static if (isFunctionPointer!T || isDelegate!T) 54 { 55 enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_; 56 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 57 } 58 else 59 static assert(false); 60 } 61 62 unittest 63 { 64 void funcThatDoesGC() 65 { 66 int a = 4; 67 int[] b = [a, a, a]; 68 } 69 70 void anotherFunction() nothrow @nogc 71 { 72 assumeNothrowNoGC( (){ funcThatDoesGC(); } )(); 73 } 74 75 void aThirdFunction() @nogc 76 { 77 assumeNoGC( () { funcThatDoesGC(); } )(); 78 } 79 } 80 81 82 // 83 // Optimistic .destroy, which is @nogc nothrow by breaking the type-system 84 // 85 86 // for classes 87 void destroyNoGC(T)(T x) nothrow @nogc if (is(T == class) || is(T == interface)) 88 { 89 assumeNothrowNoGC( 90 (T x) 91 { 92 return destroy(x); 93 })(x); 94 } 95 96 // for struct 97 void destroyNoGC(T)(ref T obj) nothrow @nogc if (is(T == struct)) 98 { 99 assumeNothrowNoGC( 100 (ref T x) 101 { 102 return destroy(x); 103 })(obj); 104 } 105 /* 106 void destroyNoGC(T : U[n], U, size_t n)(ref T obj) nothrow @nogc 107 { 108 assumeNothrowNoGC( 109 (T x) 110 { 111 return destroy(x); 112 })(obj); 113 }*/ 114 115 void destroyNoGC(T)(ref T obj) nothrow @nogc 116 if (!is(T == struct) && !is(T == class) && !is(T == interface)) 117 { 118 assumeNothrowNoGC( 119 (ref T x) 120 { 121 return destroy(x); 122 })(obj); 123 } 124 125 126 127 // 128 // Constructing and destroying without the GC. 129 // 130 131 /// Allocates and construct a struct or class object. 132 /// Returns: Newly allocated object. 133 // Note: strangely enough, deprecated alias didn't work for this. 134 deprecated("Use mallocNew instead") 135 auto mallocEmplace(T, Args...)(Args args) 136 { 137 static if (is(T == class)) 138 immutable size_t allocSize = __traits(classInstanceSize, T); 139 else 140 immutable size_t allocSize = T.sizeof; 141 142 void* rawMemory = malloc(allocSize); 143 if (!rawMemory) 144 onOutOfMemoryErrorNoGC(); 145 146 version(doNotUseRuntime) 147 { 148 } 149 else 150 { 151 static if (hasIndirections!T) 152 GC.addRange(rawMemory, allocSize); 153 } 154 155 static if (is(T == class)) 156 { 157 T obj = emplace!T(rawMemory[0 .. allocSize], args); 158 } 159 else 160 { 161 T* obj = cast(T*)rawMemory; 162 emplace!T(obj, args); 163 } 164 165 return obj; 166 } 167 168 /// Allocates and construct a struct or class object. 169 /// Returns: Newly allocated object. 170 auto mallocNew(T, Args...)(Args args) 171 { 172 static if (is(T == class)) 173 immutable size_t allocSize = __traits(classInstanceSize, T); 174 else 175 immutable size_t allocSize = T.sizeof; 176 177 void* rawMemory = malloc(allocSize); 178 if (!rawMemory) 179 onOutOfMemoryErrorNoGC(); 180 181 version(doNotUseRuntime) 182 { 183 } 184 else 185 { 186 static if (hasIndirections!T) 187 GC.addRange(rawMemory, allocSize); 188 } 189 190 static if (is(T == class)) 191 { 192 T obj = emplace!T(rawMemory[0 .. allocSize], args); 193 } 194 else 195 { 196 T* obj = cast(T*)rawMemory; 197 emplace!T(obj, args); 198 } 199 200 return obj; 201 } 202 203 /// Destroys and frees a class object created with $(D mallocEmplace). 204 void destroyFree(T)(T p) if (is(T == class)) 205 { 206 if (p !is null) 207 { 208 destroyNoGC(p); 209 210 version(doNotUseRuntime) 211 { 212 } 213 else 214 { 215 static if (hasIndirections!T) 216 GC.removeRange(cast(void*)p); 217 } 218 219 free(cast(void*)p); 220 } 221 } 222 223 /// Destroys and frees an interface object created with $(D mallocEmplace). 224 void destroyFree(T)(T p) if (is(T == interface)) 225 { 226 if (p !is null) 227 { 228 void* here = cast(void*)(cast(Object)p); 229 destroyNoGC(p); 230 231 version(doNotUseRuntime) 232 { 233 } 234 else 235 { 236 static if (hasIndirections!T) 237 GC.removeRange(here); 238 } 239 240 free(cast(void*)here); 241 } 242 } 243 244 /// Destroys and frees a non-class object created with $(D mallocEmplace). 245 void destroyFree(T)(T* p) if (!is(T == class)) 246 { 247 if (p !is null) 248 { 249 destroyNoGC(p); 250 251 version(doNotUseRuntime) 252 { 253 } 254 else 255 { 256 static if (hasIndirections!T) 257 GC.removeRange(cast(void*)p); 258 } 259 260 free(cast(void*)p); 261 } 262 } 263 264 265 unittest 266 { 267 class A 268 { 269 int _i; 270 this(int i) 271 { 272 _i = i; 273 } 274 } 275 276 struct B 277 { 278 int i; 279 } 280 281 void testMallocEmplace() 282 { 283 A a = mallocNew!A(4); 284 destroyFree(a); 285 286 B* b = mallocNew!B(5); 287 destroyFree(b); 288 } 289 290 testMallocEmplace(); 291 } 292 293 version( D_InlineAsm_X86 ) 294 { 295 version = AsmX86; 296 } 297 else version( D_InlineAsm_X86_64 ) 298 { 299 version = AsmX86; 300 } 301 302 /// Allocates a slice with `malloc`. 303 T[] mallocSlice(T)(size_t count) nothrow @nogc 304 { 305 T[] slice = mallocSliceNoInit!T(count); 306 static if (is(T == struct)) 307 { 308 // we must avoid calling struct destructors with uninitialized memory 309 for(size_t i = 0; i < count; ++i) 310 { 311 T uninitialized; 312 memcpy(&slice[i], &uninitialized, T.sizeof); 313 } 314 } 315 else 316 slice[0..count] = T.init; 317 return slice; 318 } 319 320 /// Allocates a slice with `malloc`, but does not initialize the content. 321 T[] mallocSliceNoInit(T)(size_t count) nothrow @nogc 322 { 323 T* p = cast(T*) malloc(count * T.sizeof); 324 return p[0..count]; 325 } 326 327 /// Frees a slice allocated with `mallocSlice`. 328 void freeSlice(T)(const(T)[] slice) nothrow @nogc 329 { 330 free(cast(void*)(slice.ptr)); // const cast here 331 } 332 333 /// Duplicates a slice with `malloc`. Equivalent to `.dup` 334 /// Has to be cleaned-up with `free()`. 335 T[] mallocDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct)) 336 { 337 T[] copy = mallocSliceNoInit!T(slice.length); 338 memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof); 339 return copy; 340 } 341 342 /// Duplicates a slice with `malloc`. Equivalent to `.idup` 343 /// Has to be cleaned-up with `free()`. 344 immutable(T)[] mallocIDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct)) 345 { 346 return assumeUnique(mallocDup!T(slice)); 347 } 348 349 /// Duplicates a zero-terminated string with `malloc`, return a `char[]`. Equivalent to `.dup` 350 /// Has to be cleaned-up with `free()`. 351 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be converted 352 /// to a C string with `.ptr`. However the zero byte is not included in slice length. 353 char[] stringDup(const(char)* cstr) nothrow @nogc 354 { 355 assert(cstr !is null); 356 size_t len = strlen(cstr); 357 char* copy = strdup(cstr); 358 return copy[0..len]; 359 } 360 361 /// Duplicates a zero-terminated string with `malloc`, return a `string`. Equivalent to `.idup` 362 /// Has to be cleaned-up with `free()`. 363 /// Note: The zero-terminating byte is preserved. This allow to have a string which also can be converted 364 /// to a C string with `.ptr`. However the zero byte is not included in slice length. 365 string stringIDup(const(char)* cstr) nothrow @nogc 366 { 367 return assumeUnique(stringDup(cstr)); 368 } 369 370 unittest 371 { 372 int[] slice = mallocSlice!int(4); 373 freeSlice(slice); 374 assert(slice[3] == int.init); 375 376 slice = mallocSliceNoInit!int(4); 377 freeSlice(slice); 378 379 slice = mallocSliceNoInit!int(0); 380 assert(slice == []); 381 freeSlice(slice); 382 } 383 384 /// Semantic function to check that a D string implicitely conveys a 385 /// termination byte after the slice. 386 /// (typically those comes from string literals or `stringDup`/`stringIDup`) 387 const(char)* assumeZeroTerminated(const(char)[] input) nothrow @nogc 388 { 389 if (input.ptr is null) 390 return null; 391 392 // Check that the null character is there 393 assert(input.ptr[input.length] == '\0'); 394 return input.ptr; 395 } 396 397 398 // 399 // @nogc sorting. 400 // 401 402 /// Must return -1 if a < b 403 /// 0 if a == b 404 /// 1 if a > b 405 alias nogcComparisonFunction(T) = int delegate(in T a, in T b) nothrow @nogc; 406 407 /// @nogc quicksort 408 /// From the excellent: http://codereview.stackexchange.com/a/77788 409 deprecated("Use grailSort() instead, which is faster") void quicksort(T)(T[] array, nogcComparisonFunction!T comparison) nothrow @nogc 410 { 411 if (array.length < 2) 412 return; 413 414 static void swapElem(ref T lhs, ref T rhs) 415 { 416 T tmp = lhs; 417 lhs = rhs; 418 rhs = tmp; 419 } 420 421 int partition(T* arr, int left, int right) nothrow @nogc 422 { 423 immutable int mid = left + (right - left) / 2; 424 T pivot = arr[mid]; 425 // move the mid point value to the front. 426 swapElem(arr[mid],arr[left]); 427 int i = left + 1; 428 int j = right; 429 while (i <= j) 430 { 431 while(i <= j && comparison(arr[i], pivot) <= 0 ) 432 i++; 433 434 while(i <= j && comparison(arr[j], pivot) > 0) 435 j--; 436 437 if (i < j) 438 swapElem(arr[i], arr[j]); 439 } 440 swapElem(arr[i - 1], arr[left]); 441 return i - 1; 442 } 443 444 void doQsort(T* array, int left, int right) nothrow @nogc 445 { 446 if (left >= right) 447 return; 448 449 int part = partition(array, left, right); 450 doQsort(array, left, part - 1); 451 doQsort(array, part + 1, right); 452 } 453 454 doQsort(array.ptr, 0, cast(int)(array.length) - 1); 455 } 456 457 // 458 // STABLE IN-PLACE SORT (implementation is at bottom of file) 459 // 460 461 void grailSort(T)(T[] inoutElements, nogcComparisonFunction!T comparison) nothrow @nogc 462 { 463 GrailSort!T(inoutElements.ptr, cast(int)(inoutElements.length), comparison); 464 } 465 466 unittest 467 { 468 int[2][] testData = [[110, 0], [5, 0], [10, 0], [3, 0], [110, 1], [5, 1], [10, 1], [3, 1]]; 469 grailSort!(int[2])(testData, (a, b) => (a[0] - b[0])); 470 assert(testData == [[3, 0], [3, 1], [5, 0], [5, 1], [10, 0], [10, 1], [110, 0], [110, 1]]); 471 } 472 473 474 // 475 // STABLE MERGE SORT 476 // 477 478 /// Stable merge sort, using a temporary array. 479 /// Array A[] has the items to sort. 480 /// Array B[] is a work array. 481 /// `grailSort` is approx. 30% slower but doesn't need a scratchBuffer. 482 void mergeSort(T)(T[] inoutElements, T[] scratchBuffer, nogcComparisonFunction!T comparison) nothrow @nogc 483 { 484 // Left source half is A[ iBegin:iMiddle-1]. 485 // Right source half is A[iMiddle:iEnd-1 ]. 486 // Result is B[ iBegin:iEnd-1 ]. 487 void topDownMerge(T)(T* A, int iBegin, int iMiddle, int iEnd, T* B) nothrow @nogc 488 { 489 int i = iBegin; 490 int j = iMiddle; 491 492 // While there are elements in the left or right runs... 493 for (int k = iBegin; k < iEnd; k++) 494 { 495 // If left run head exists and is <= existing right run head. 496 if ( i < iMiddle && ( j >= iEnd || (comparison(A[i], A[j]) <= 0) ) ) 497 { 498 B[k] = A[i]; 499 i = i + 1; 500 } 501 else 502 { 503 B[k] = A[j]; 504 j = j + 1; 505 } 506 } 507 } 508 509 // Sort the given run of array A[] using array B[] as a source. 510 // iBegin is inclusive; iEnd is exclusive (A[iEnd] is not in the set). 511 void topDownSplitMerge(T)(T* B, int iBegin, int iEnd, T* A) nothrow @nogc 512 { 513 if(iEnd - iBegin < 2) // if run size == 1 514 return; // consider it sorted 515 // split the run longer than 1 item into halves 516 int iMiddle = (iEnd + iBegin) / 2; // iMiddle = mid point 517 // recursively sort both runs from array A[] into B[] 518 topDownSplitMerge!T(A, iBegin, iMiddle, B); // sort the left run 519 topDownSplitMerge!T(A, iMiddle, iEnd, B); // sort the right run 520 // merge the resulting runs from array B[] into A[] 521 topDownMerge!T(B, iBegin, iMiddle, iEnd, A); 522 } 523 524 assert(inoutElements.length == scratchBuffer.length); 525 int n = cast(int)inoutElements.length; 526 scratchBuffer[] = inoutElements[]; // copy data into temporary buffer 527 topDownSplitMerge(scratchBuffer.ptr, 0, n, inoutElements.ptr); 528 } 529 530 unittest 531 { 532 int[2][] scratch; 533 scratch.length = 8; 534 int[2][] testData = [[110, 0], [5, 0], [10, 0], [3, 0], [110, 1], [5, 1], [10, 1], [3, 1]]; 535 mergeSort!(int[2])(testData, scratch, (a, b) => (a[0] - b[0])); 536 assert(testData == [[3, 0], [3, 1], [5, 0], [5, 1], [10, 0], [10, 1], [110, 0], [110, 1]]); 537 } 538 539 /// To call for something that should never happen, but we still 540 /// want to make a "best effort" at runtime even if it can be meaningless. 541 /// MAYDO: change that name, it's not actually unrecoverable 542 /// MAYDO: stop using that function 543 void unrecoverableError() nothrow @nogc 544 { 545 debug 546 { 547 // Crash unconditionally 548 assert(false); 549 } 550 else 551 { 552 // There is a trade-off here, if we crash immediately we will be 553 // correctly identified by the user as the origin of the bug, which 554 // is always helpful. 555 // But crashing may in many-case also crash the host, which is not very friendly. 556 // Eg: a plugin not instancing vs host crashing. 557 // The reasoning is that the former is better from the user POV. 558 } 559 } 560 561 562 /// A bit faster than a dynamic cast. 563 /// This is to avoid TypeInfo look-up. 564 T unsafeObjectCast(T)(Object obj) 565 { 566 return cast(T)(cast(void*)(obj)); 567 } 568 569 570 /// Inserts a breakpoint instruction. useful to trigger the debugger. 571 void debugBreak() nothrow @nogc 572 { 573 version( AsmX86 ) 574 { 575 asm nothrow @nogc 576 { 577 int 3; 578 } 579 } 580 else version( GNU ) 581 { 582 // __builtin_trap() is not the same thing unfortunately 583 asm 584 { 585 "int $0x03" : : : ; 586 } 587 } 588 else 589 { 590 static assert(false, "No debugBreak() for this compiler"); 591 } 592 } 593 594 595 // Copy source into dest. 596 // dest must contain room for maxChars characters 597 // A zero-byte character is then appended. 598 void stringNCopy(char* dest, size_t maxChars, const(char)[] source) nothrow @nogc 599 { 600 if (maxChars == 0) 601 return; 602 603 size_t max = maxChars < source.length ? maxChars - 1 : source.length; 604 for (int i = 0; i < max; ++i) 605 dest[i] = source[i]; 606 dest[max] = '\0'; 607 } 608 609 610 // 611 // Low-cost C string conversions 612 // 613 alias CString = CStringImpl!char; 614 alias CString16 = CStringImpl!wchar; 615 616 /// Zero-terminated C string, to replace toStringz and toUTF16z 617 struct CStringImpl(CharType) if (is(CharType: char) || is(CharType: wchar)) 618 { 619 public: 620 nothrow: 621 @nogc: 622 623 const(CharType)* storage = null; 624 alias storage this; 625 626 627 this(const(CharType)[] s) 628 { 629 // Always copy. We can't assume anything about the input. 630 size_t len = s.length; 631 CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof); 632 buffer[0..len] = s[0..len]; 633 buffer[len] = '\0'; 634 storage = buffer; 635 wasAllocated = true; 636 } 637 638 // The constructor taking immutable can safely assume that such memory 639 // has been allocated by the GC or malloc, or an allocator that align 640 // pointer on at least 4 bytes. 641 this(immutable(CharType)[] s) 642 { 643 // Same optimizations that for toStringz 644 if (s.empty) 645 { 646 enum emptyString = cast(CharType[])""; 647 storage = emptyString.ptr; 648 return; 649 } 650 651 /* Peek past end of s[], if it's 0, no conversion necessary. 652 * Note that the compiler will put a 0 past the end of static 653 * strings, and the storage allocator will put a 0 past the end 654 * of newly allocated char[]'s. 655 */ 656 const(CharType)* p = s.ptr + s.length; 657 // Is p dereferenceable? A simple test: if the p points to an 658 // address multiple of 4, then conservatively assume the pointer 659 // might be pointing to another block of memory, which might be 660 // unreadable. Otherwise, it's definitely pointing to valid 661 // memory. 662 if ((cast(size_t) p & 3) && *p == 0) 663 { 664 storage = s.ptr; 665 return; 666 } 667 668 size_t len = s.length; 669 CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof); 670 buffer[0..len] = s[0..len]; 671 buffer[len] = '\0'; 672 storage = buffer; 673 wasAllocated = true; 674 } 675 676 ~this() 677 { 678 if (wasAllocated) 679 free(cast(void*)storage); 680 } 681 682 @disable this(this); 683 684 private: 685 bool wasAllocated = false; 686 } 687 688 689 // 690 // Launch browser, replaces std.process.browse 691 // 692 693 void browseNoGC(string url) nothrow @nogc 694 { 695 version(Windows) 696 { 697 import core.sys.windows.winuser; 698 import core.sys.windows.shellapi; 699 ShellExecuteA(null, CString("open").storage, CString(url).storage, null, null, SW_SHOWNORMAL); 700 } 701 702 version(OSX) 703 { 704 import core.sys.posix.unistd; 705 const(char)*[5] args; 706 707 auto curl = CString(url).storage; 708 const(char)* browser = getenv("BROWSER"); 709 if (browser) 710 { 711 browser = strdup(browser); 712 args[0] = browser; 713 args[1] = curl; 714 args[2] = null; 715 } 716 else 717 { 718 args[0] = "open".ptr; 719 args[1] = curl; 720 args[2] = null; 721 } 722 723 auto childpid = core.sys.posix.unistd.fork(); 724 if (childpid == 0) 725 { 726 core.sys.posix.unistd.execvp(args[0], cast(char**)args.ptr); 727 return; 728 } 729 if (browser) 730 free(cast(void*)browser); 731 } 732 } 733 734 735 736 // 737 // GRAIL SORT IMPLEMENTATION BELOW 738 // 739 // The MIT License (MIT) 740 // 741 // Copyright (c) 2013 Andrey Astrelin 742 // 743 // Permission is hereby granted, free of charge, to any person obtaining a copy of 744 // this software and associated documentation files (the "Software"), to deal in 745 // the Software without restriction, including without limitation the rights to 746 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 747 // the Software, and to permit persons to whom the Software is furnished to do so, 748 // subject to the following conditions: 749 // 750 // The above copyright notice and this permission notice shall be included in all 751 // copies or substantial portions of the Software. 752 // 753 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 754 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 755 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 756 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 757 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 758 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 759 760 private: 761 762 void grail_swap1(T)(T *a,T *b){ 763 T c=*a; 764 *a=*b; 765 *b=c; 766 } 767 void grail_swapN(T)(T *a,T *b,int n){ 768 while(n--) grail_swap1(a++,b++); 769 } 770 void grail_rotate(T)(T *a,int l1,int l2){ 771 while(l1 && l2){ 772 if(l1<=l2){ 773 grail_swapN(a,a+l1,l1); 774 a+=l1; l2-=l1; 775 } else{ 776 grail_swapN(a+(l1-l2),a+l1,l2); 777 l1-=l2; 778 } 779 } 780 } 781 782 int grail_BinSearchLeft(T)(T *arr,int len,T *key, nogcComparisonFunction!T comparison){ 783 int a=-1,b=len,c; 784 while(a<b-1){ 785 c=a+((b-a)>>1); 786 if(comparison(arr[c],*key)>=0) b=c; 787 else a=c; 788 } 789 return b; 790 } 791 int grail_BinSearchRight(T)(T *arr,int len,T *key, nogcComparisonFunction!T comparison){ 792 int a=-1,b=len,c; 793 while(a<b-1){ 794 c=a+((b-a)>>1); 795 if(comparison(arr[c],*key)>0) b=c; 796 else a=c; 797 } 798 return b; 799 } 800 801 // cost: 2*len+nk^2/2 802 int grail_FindKeys(T)(T *arr,int len,int nkeys, nogcComparisonFunction!T comparison){ 803 int h=1,h0=0; // first key is always here 804 int u=1,r; 805 while(u<len && h<nkeys){ 806 r=grail_BinSearchLeft!T(arr+h0,h,arr+u, comparison); 807 if(r==h || comparison(arr[u],arr[h0+r])!=0){ 808 grail_rotate(arr+h0,h,u-(h0+h)); 809 h0=u-h; 810 grail_rotate(arr+(h0+r),h-r,1); 811 h++; 812 } 813 u++; 814 } 815 grail_rotate(arr,h0,h); 816 return h; 817 } 818 819 // cost: min(L1,L2)^2+max(L1,L2) 820 void grail_MergeWithoutBuffer(T)(T *arr,int len1,int len2, nogcComparisonFunction!T comparison){ 821 int h; 822 if(len1<len2){ 823 while(len1){ 824 h=grail_BinSearchLeft!T(arr+len1,len2,arr, comparison); 825 if(h!=0){ 826 grail_rotate(arr,len1,h); 827 arr+=h; 828 len2-=h; 829 } 830 if(len2==0) break; 831 do{ 832 arr++; len1--; 833 } while(len1 && comparison(*arr,arr[len1])<=0); 834 } 835 } else{ 836 while(len2){ 837 h=grail_BinSearchRight!T(arr,len1,arr+(len1+len2-1), comparison); 838 if(h!=len1){ 839 grail_rotate(arr+h,len1-h,len2); 840 len1=h; 841 } 842 if(len1==0) break; 843 do{ 844 len2--; 845 } while(len2 && comparison(arr[len1-1],arr[len1+len2-1])<=0); 846 } 847 } 848 } 849 850 // arr[M..-1] - buffer, arr[0,L1-1]++arr[L1,L1+L2-1] -> arr[M,M+L1+L2-1] 851 void grail_MergeLeft(T)(T *arr,int L1,int L2,int M, nogcComparisonFunction!T comparison){ 852 int p0=0,p1=L1; L2+=L1; 853 while(p1<L2){ 854 if(p0==L1 || comparison(arr[p0],arr[p1])>0){ 855 grail_swap1(arr+(M++),arr+(p1++)); 856 } else{ 857 grail_swap1(arr+(M++),arr+(p0++)); 858 } 859 } 860 if(M!=p0) grail_swapN(arr+M,arr+p0,L1-p0); 861 } 862 void grail_MergeRight(T)(T *arr,int L1,int L2,int M, nogcComparisonFunction!T comparison){ 863 int p0=L1+L2+M-1,p2=L1+L2-1,p1=L1-1; 864 865 while(p1>=0){ 866 if(p2<L1 || comparison(arr[p1],arr[p2])>0){ 867 grail_swap1(arr+(p0--),arr+(p1--)); 868 } else{ 869 grail_swap1(arr+(p0--),arr+(p2--)); 870 } 871 } 872 if(p2!=p0) while(p2>=L1) grail_swap1(arr+(p0--),arr+(p2--)); 873 } 874 875 void grail_SmartMergeWithBuffer(T)(T *arr,int *alen1,int *atype,int len2,int lkeys, nogcComparisonFunction!T comparison){ 876 int p0=-lkeys,p1=0,p2=*alen1,q1=p2,q2=p2+len2; 877 int ftype=1-*atype; // 1 if inverted 878 while(p1<q1 && p2<q2){ 879 if(comparison(arr[p1],arr[p2])-ftype<0) grail_swap1(arr+(p0++),arr+(p1++)); 880 else grail_swap1(arr+(p0++),arr+(p2++)); 881 } 882 if(p1<q1){ 883 *alen1=q1-p1; 884 while(p1<q1) grail_swap1(arr+(--q1),arr+(--q2)); 885 } else{ 886 *alen1=q2-p2; 887 *atype=ftype; 888 } 889 } 890 void grail_SmartMergeWithoutBuffer(T)(T *arr,int *alen1,int *atype,int _len2, nogcComparisonFunction!T comparison){ 891 int len1,len2,ftype,h; 892 893 if(!_len2) return; 894 len1=*alen1; 895 len2=_len2; 896 ftype=1-*atype; 897 if(len1 && comparison(arr[len1-1],arr[len1])-ftype>=0){ 898 while(len1){ 899 h=ftype ? grail_BinSearchLeft!T(arr+len1,len2,arr, comparison) : grail_BinSearchRight!T(arr+len1,len2,arr, comparison); 900 if(h!=0){ 901 grail_rotate(arr,len1,h); 902 arr+=h; 903 len2-=h; 904 } 905 if(len2==0){ 906 *alen1=len1; 907 return; 908 } 909 do{ 910 arr++; len1--; 911 } while(len1 && comparison(*arr,arr[len1])-ftype<0); 912 } 913 } 914 *alen1=len2; *atype=ftype; 915 } 916 917 /***** Sort With Extra Buffer *****/ 918 919 // arr[M..-1] - free, arr[0,L1-1]++arr[L1,L1+L2-1] -> arr[M,M+L1+L2-1] 920 void grail_MergeLeftWithXBuf(T)(T *arr,int L1,int L2,int M, nogcComparisonFunction!T comparison){ 921 int p0=0,p1=L1; L2+=L1; 922 while(p1<L2){ 923 if(p0==L1 || comparison(arr[p0],arr[p1])>0) arr[M++]=arr[p1++]; 924 else arr[M++]=arr[p0++]; 925 } 926 if(M!=p0) while(p0<L1) arr[M++]=arr[p0++]; 927 } 928 929 void grail_SmartMergeWithXBuf(T)(T *arr,int *alen1,int *atype,int len2,int lkeys, nogcComparisonFunction!T comparison){ 930 int p0=-lkeys,p1=0,p2=*alen1,q1=p2,q2=p2+len2; 931 int ftype=1-*atype; // 1 if inverted 932 while(p1<q1 && p2<q2){ 933 if(comparison(arr[p1],arr[p2])-ftype<0) arr[p0++]=arr[p1++]; 934 else arr[p0++]=arr[p2++]; 935 } 936 if(p1<q1){ 937 *alen1=q1-p1; 938 while(p1<q1) arr[--q2]=arr[--q1]; 939 } else{ 940 *alen1=q2-p2; 941 *atype=ftype; 942 } 943 } 944 945 // arr - starting array. arr[-lblock..-1] - buffer (if havebuf). 946 // lblock - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded 947 // keys - arrays of keys, in same order as blocks. key<midkey means stream A 948 // nblock2 are regular blocks from stream A. llast is length of last (irregular) block from stream B, that should go before nblock2 blocks. 949 // llast=0 requires nblock2=0 (no irregular blocks). llast>0, nblock2=0 is possible. 950 void grail_MergeBuffersLeftWithXBuf(T)(T *keys,T *midkey,T *arr,int nblock,int lblock,int nblock2,int llast, nogcComparisonFunction!T comparison){ 951 int l,prest,lrest,frest,pidx,cidx,fnext,plast; 952 953 if(nblock==0){ 954 l=nblock2*lblock; 955 grail_MergeLeftWithXBuf!T(arr,l,llast,-lblock, comparison); 956 return; 957 } 958 959 lrest=lblock; 960 frest=comparison(*keys,*midkey)<0 ? 0 : 1; 961 pidx=lblock; 962 for(cidx=1;cidx<nblock;cidx++,pidx+=lblock){ 963 prest=pidx-lrest; 964 fnext=comparison(keys[cidx],*midkey)<0 ? 0 : 1; 965 if(fnext==frest){ 966 memcpy(arr+prest-lblock,arr+prest,lrest*T.sizeof); 967 prest=pidx; 968 lrest=lblock; 969 } else{ 970 grail_SmartMergeWithXBuf!T(arr+prest,&lrest,&frest,lblock,lblock, comparison); 971 } 972 } 973 prest=pidx-lrest; 974 if(llast){ 975 plast=pidx+lblock*nblock2; 976 if(frest){ 977 memcpy(arr+prest-lblock,arr+prest,lrest*T.sizeof); 978 prest=pidx; 979 lrest=lblock*nblock2; 980 frest=0; 981 } else{ 982 lrest+=lblock*nblock2; 983 } 984 grail_MergeLeftWithXBuf!T(arr+prest,lrest,llast,-lblock, comparison); 985 } else{ 986 memcpy(arr+prest-lblock,arr+prest,lrest*T.sizeof); 987 } 988 } 989 990 /***** End Sort With Extra Buffer *****/ 991 992 // build blocks of length K 993 // input: [-K,-1] elements are buffer 994 // output: first K elements are buffer, blocks 2*K and last subblock sorted 995 void grail_BuildBlocks(T)(T *arr,int L,int K,T *extbuf,int LExtBuf, nogcComparisonFunction!T comparison){ 996 int m,u,h,p0,p1,rest,restk,p,kbuf; 997 kbuf=K<LExtBuf ? K : LExtBuf; 998 while(kbuf&(kbuf-1)) kbuf&=kbuf-1; // max power or 2 - just in case 999 1000 if(kbuf){ 1001 memcpy(extbuf,arr-kbuf,kbuf*T.sizeof); 1002 for(m=1;m<L;m+=2){ 1003 u=0; 1004 if(comparison(arr[m-1],arr[m])>0) u=1; 1005 arr[m-3]=arr[m-1+u]; 1006 arr[m-2]=arr[m-u]; 1007 } 1008 if(L%2) arr[L-3]=arr[L-1]; 1009 arr-=2; 1010 for(h=2;h<kbuf;h*=2){ 1011 p0=0; 1012 p1=L-2*h; 1013 while(p0<=p1){ 1014 grail_MergeLeftWithXBuf!T(arr+p0,h,h,-h, comparison); 1015 p0+=2*h; 1016 } 1017 rest=L-p0; 1018 if(rest>h){ 1019 grail_MergeLeftWithXBuf!T(arr+p0,h,rest-h,-h, comparison); 1020 } else { 1021 for(;p0<L;p0++) arr[p0-h]=arr[p0]; 1022 } 1023 arr-=h; 1024 } 1025 memcpy(arr+L,extbuf,kbuf*T.sizeof); 1026 } else{ 1027 for(m=1;m<L;m+=2){ 1028 u=0; 1029 if(comparison(arr[m-1],arr[m])>0) u=1; 1030 grail_swap1(arr+(m-3),arr+(m-1+u)); 1031 grail_swap1(arr+(m-2),arr+(m-u)); 1032 } 1033 if(L%2) grail_swap1(arr+(L-1),arr+(L-3)); 1034 arr-=2; 1035 h=2; 1036 } 1037 for(;h<K;h*=2){ 1038 p0=0; 1039 p1=L-2*h; 1040 while(p0<=p1){ 1041 grail_MergeLeft!T(arr+p0,h,h,-h, comparison); 1042 p0+=2*h; 1043 } 1044 rest=L-p0; 1045 if(rest>h){ 1046 grail_MergeLeft!T(arr+p0,h,rest-h,-h, comparison); 1047 } else grail_rotate(arr+p0-h,h,rest); 1048 arr-=h; 1049 } 1050 restk=L%(2*K); 1051 p=L-restk; 1052 if(restk<=K) grail_rotate(arr+p,restk,K); 1053 else grail_MergeRight!T(arr+p,K,restk-K,K, comparison); 1054 while(p>0){ 1055 p-=2*K; 1056 grail_MergeRight!T(arr+p,K,K,K, comparison); 1057 } 1058 } 1059 1060 // arr - starting array. arr[-lblock..-1] - buffer (if havebuf). 1061 // lblock - length of regular blocks. First nblocks are stable sorted by 1st elements and key-coded 1062 // keys - arrays of keys, in same order as blocks. key<midkey means stream A 1063 // nblock2 are regular blocks from stream A. llast is length of last (irregular) block from stream B, that should go before nblock2 blocks. 1064 // llast=0 requires nblock2=0 (no irregular blocks). llast>0, nblock2=0 is possible. 1065 void grail_MergeBuffersLeft(T)(T *keys,T *midkey,T *arr,int nblock,int lblock,bool havebuf,int nblock2,int llast, nogcComparisonFunction!T comparison){ 1066 int l,prest,lrest,frest,pidx,cidx,fnext,plast; 1067 1068 if(nblock==0){ 1069 l=nblock2*lblock; 1070 if(havebuf) grail_MergeLeft!T(arr,l,llast,-lblock, comparison); 1071 else grail_MergeWithoutBuffer!T(arr,l,llast, comparison); 1072 return; 1073 } 1074 1075 lrest=lblock; 1076 frest=comparison(*keys,*midkey)<0 ? 0 : 1; 1077 pidx=lblock; 1078 for(cidx=1;cidx<nblock;cidx++,pidx+=lblock){ 1079 prest=pidx-lrest; 1080 fnext=comparison(keys[cidx],*midkey)<0 ? 0 : 1; 1081 if(fnext==frest){ 1082 if(havebuf) grail_swapN(arr+prest-lblock,arr+prest,lrest); 1083 prest=pidx; 1084 lrest=lblock; 1085 } else{ 1086 if(havebuf){ 1087 grail_SmartMergeWithBuffer!T(arr+prest,&lrest,&frest,lblock,lblock, comparison); 1088 } else{ 1089 grail_SmartMergeWithoutBuffer!T(arr+prest,&lrest,&frest,lblock, comparison); 1090 } 1091 1092 } 1093 } 1094 prest=pidx-lrest; 1095 if(llast){ 1096 plast=pidx+lblock*nblock2; 1097 if(frest){ 1098 if(havebuf) grail_swapN(arr+prest-lblock,arr+prest,lrest); 1099 prest=pidx; 1100 lrest=lblock*nblock2; 1101 frest=0; 1102 } else{ 1103 lrest+=lblock*nblock2; 1104 } 1105 if(havebuf) grail_MergeLeft!T(arr+prest,lrest,llast,-lblock, comparison); 1106 else grail_MergeWithoutBuffer!T(arr+prest,lrest,llast, comparison); 1107 } else{ 1108 if(havebuf) grail_swapN(arr+prest,arr+(prest-lblock),lrest); 1109 } 1110 } 1111 1112 void grail_SortIns(T)(T *arr,int len, nogcComparisonFunction!T comparison){ 1113 int i,j; 1114 for(i=1;i<len;i++){ 1115 for(j=i-1;j>=0 && comparison(arr[j+1],arr[j])<0;j--) grail_swap1(arr+j,arr+(j+1)); 1116 } 1117 } 1118 1119 void grail_LazyStableSort(T)(T *arr,int L, nogcComparisonFunction!T comparison){ 1120 int m,u,h,p0,p1,rest; 1121 for(m=1;m<L;m+=2){ 1122 u=0; 1123 if(comparison(arr[m-1],arr[m])>0) grail_swap1(arr+(m-1),arr+m); 1124 } 1125 for(h=2;h<L;h*=2){ 1126 p0=0; 1127 p1=L-2*h; 1128 while(p0<=p1){ 1129 grail_MergeWithoutBuffer!T(arr+p0,h,h, comparison); 1130 p0+=2*h; 1131 } 1132 rest=L-p0; 1133 if(rest>h) grail_MergeWithoutBuffer!T(arr+p0,h,rest-h, comparison); 1134 } 1135 } 1136 1137 // keys are on the left of arr. Blocks of length LL combined. We'll combine them in pairs 1138 // LL and nkeys are powers of 2. (2*LL/lblock) keys are guarantied 1139 void grail_CombineBlocks(T)(T *keys,T *arr,int len,int LL,int lblock,bool havebuf,T *xbuf, nogcComparisonFunction!T comparison){ 1140 int M,nkeys,b,NBlk,midkey,lrest,u,p,v,kc,nbl2,llast; 1141 T *arr1; 1142 1143 M=len/(2*LL); 1144 lrest=len%(2*LL); 1145 nkeys=(2*LL)/lblock; 1146 if(lrest<=LL){ 1147 len-=lrest; 1148 lrest=0; 1149 } 1150 if(xbuf) memcpy(xbuf,arr-lblock,lblock*T.sizeof); 1151 for(b=0;b<=M;b++){ 1152 if(b==M && lrest==0) break; 1153 arr1=arr+b*2*LL; 1154 NBlk=(b==M ? lrest : 2*LL)/lblock; 1155 grail_SortIns!T(keys,NBlk+(b==M ? 1 : 0), comparison); 1156 midkey=LL/lblock; 1157 for(u=1;u<NBlk;u++){ 1158 p=u-1; 1159 for(v=u;v<NBlk;v++){ 1160 kc=comparison(arr1[p*lblock],arr1[v*lblock]); 1161 if(kc>0 || (kc==0 && comparison(keys[p],keys[v])>0)) p=v; 1162 } 1163 if(p!=u-1){ 1164 grail_swapN(arr1+(u-1)*lblock,arr1+p*lblock,lblock); 1165 grail_swap1(keys+(u-1),keys+p); 1166 if(midkey==u-1 || midkey==p) midkey^=(u-1)^p; 1167 } 1168 } 1169 nbl2=llast=0; 1170 if(b==M) llast=lrest%lblock; 1171 if(llast!=0){ 1172 while(nbl2<NBlk && comparison(arr1[NBlk*lblock],arr1[(NBlk-nbl2-1)*lblock])<0) nbl2++; 1173 } 1174 if(xbuf) grail_MergeBuffersLeftWithXBuf!T(keys,keys+midkey,arr1,NBlk-nbl2,lblock,nbl2,llast, comparison); 1175 else grail_MergeBuffersLeft!T(keys,keys+midkey,arr1,NBlk-nbl2,lblock,havebuf,nbl2,llast, comparison); 1176 } 1177 if(xbuf){ 1178 for(p=len;--p>=0;) arr[p]=arr[p-lblock]; 1179 memcpy(arr-lblock,xbuf,lblock*T.sizeof); 1180 }else if(havebuf) while(--len>=0) grail_swap1(arr+len,arr+len-lblock); 1181 } 1182 1183 1184 void grail_commonSort(T)(T *arr,int Len,T *extbuf,int LExtBuf, nogcComparisonFunction!T comparison){ 1185 int lblock,nkeys,findkeys,ptr,cbuf,lb,nk; 1186 bool havebuf,chavebuf; 1187 long s; 1188 1189 if(Len<16){ 1190 grail_SortIns!T(arr,Len, comparison); 1191 return; 1192 } 1193 1194 lblock=1; 1195 while(lblock*lblock<Len) lblock*=2; 1196 nkeys=(Len-1)/lblock+1; 1197 findkeys=grail_FindKeys!T(arr,Len,nkeys+lblock, comparison); 1198 havebuf=true; 1199 if(findkeys<nkeys+lblock){ 1200 if(findkeys<4){ 1201 grail_LazyStableSort!T(arr,Len, comparison); 1202 return; 1203 } 1204 nkeys=lblock; 1205 while(nkeys>findkeys) nkeys/=2; 1206 havebuf=false; 1207 lblock=0; 1208 } 1209 ptr=lblock+nkeys; 1210 cbuf=havebuf ? lblock : nkeys; 1211 if(havebuf) grail_BuildBlocks!T(arr+ptr,Len-ptr,cbuf,extbuf,LExtBuf, comparison); 1212 else grail_BuildBlocks!T(arr+ptr,Len-ptr,cbuf,null,0, comparison); 1213 1214 // 2*cbuf are built 1215 while(Len-ptr>(cbuf*=2)){ 1216 lb=lblock; 1217 chavebuf=havebuf; 1218 if(!havebuf){ 1219 if(nkeys>4 && nkeys/8*nkeys>=cbuf){ 1220 lb=nkeys/2; 1221 chavebuf=true; 1222 } else{ 1223 nk=1; 1224 s=cast(long)cbuf*findkeys/2; 1225 while(nk<nkeys && s!=0){ 1226 nk*=2; s/=8; 1227 } 1228 lb=(2*cbuf)/nk; 1229 } 1230 } 1231 grail_CombineBlocks!T(arr,arr+ptr,Len-ptr,cbuf,lb,chavebuf,chavebuf && lb<=LExtBuf ? extbuf : null, comparison); 1232 } 1233 grail_SortIns!T(arr,ptr, comparison); 1234 grail_MergeWithoutBuffer!T(arr,ptr,Len-ptr, comparison); 1235 } 1236 1237 void GrailSort(T)(T *arr, int Len, nogcComparisonFunction!T comparison){ 1238 grail_commonSort!T(arr,Len,null,0, comparison); 1239 } 1240 1241 void GrailSortWithBuffer(T)(T *arr,int Len, nogcComparisonFunction!T comparison){ 1242 T[128] ExtBuf; 1243 grail_commonSort!T(arr,Len,ExtBuf.ptr,128, comparison); 1244 } 1245 1246 /****** classic MergeInPlace *************/ 1247 1248 void grail_RecMerge(T)(T *A,int L1,int L2, nogcComparisonFunction!T comparison){ 1249 int K,k1,k2,m1,m2; 1250 if(L1<3 || L2<3){ 1251 grail_MergeWithoutBuffer(A,L1,L2); return; 1252 } 1253 if(L1<L2) K=L1+L2/2; 1254 else K=L1/2; 1255 k1=k2=grail_BinSearchLeft(A,L1,A+K); 1256 if(k2<L1 && comparison(A+k2,A+K)==0) k2=grail_BinSearchRight(A+k1,L1-k1,A+K)+k1; 1257 m1=grail_BinSearchLeft(A+L1,L2,A+K); 1258 m2=m1; 1259 if(m2<L2 && comparison(A+L1+m2,A+K)==0) m2=grail_BinSearchRight(A+L1+m1,L2-m1,A+K)+m1; 1260 if(k1==k2) grail_rotate(A+k2,L1-k2,m2); 1261 else{ 1262 grail_rotate(A+k1,L1-k1,m1); 1263 if(m2!=m1) grail_rotate(A+(k2+m1),L1-k2,m2-m1); 1264 } 1265 grail_RecMerge(A+(k2+m2),L1-k2,L2-m2); 1266 grail_RecMerge(A,k1,m1); 1267 } 1268 void RecStableSort(T)(T *arr,int L){ 1269 int u,m,h,p0,p1,rest; 1270 1271 for(m=1;m<L;m+=2){ 1272 u=0; 1273 if(comparison(arr+m-1,arr+m)>0) grail_swap1(arr+(m-1),arr+m); 1274 } 1275 for(h=2;h<L;h*=2){ 1276 p0=0; 1277 p1=L-2*h; 1278 while(p0<=p1){ 1279 grail_RecMerge(arr+p0,h,h); 1280 p0+=2*h; 1281 } 1282 rest=L-p0; 1283 if(rest>h) grail_RecMerge(arr+p0,h,rest-h); 1284 } 1285 }