1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package shark.internal.aosp 17 18 import kotlin.math.min 19 20 /* 21 This is TimSort.java from AOSP (Jelly Bean MR2, Apache 2 license), converted to Kotlin and adapted 22 to work with byte array chunks. The passed in byte array is virtually divided into entries of a 23 fixed number of bytes N. Each entry is compared by a custom comparator. 24 25 Copied from https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/java/util/TimSort.java 26 */ 27 28 /** 29 * A stable, adaptive, iterative mergesort that requires far fewer than 30 * n lg(n) comparisons when running on partially sorted arrays, while 31 * offering performance comparable to a traditional mergesort when run 32 * on random arrays. Like all proper mergesorts, this sort is stable and 33 * runs O(n log n) time (worst case). In the worst case, this sort requires 34 * temporary storage space for n/2 object references; in the best case, 35 * it requires only a small constant amount of space. 36 * 37 * This implementation was adapted from Tim Peters's list sort for 38 * Python, which is described in detail here: 39 * 40 * http://svn.python.org/projects/python/trunk/Objects/listsort.txt 41 * 42 * Tim's C code may be found here: 43 * 44 * http://svn.python.org/projects/python/trunk/Objects/listobject.c 45 * 46 * The underlying techniques are described in this paper (and may have 47 * even earlier origins): 48 * 49 * "Optimistic Sorting and Information Theoretic Complexity" 50 * Peter McIlroy 51 * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), 52 * pp 467-474, Austin, Texas, 25-27 January 1993. 53 * 54 * While the API to this class consists solely of static methods, it is 55 * (privately) instantiable; a TimSort instance holds the state of an ongoing 56 * sort, assuming the input array is large enough to warrant the full-blown 57 * TimSort. Small arrays are sorted in place, using a binary insertion sort. 58 */ 59 @Suppress("detekt.complexity", "detekt.style") 60 internal class ByteArrayTimSort 61 /** 62 * Creates a TimSort instance to maintain the state of an ongoing sort. 63 * 64 * @param a the array to be sorted 65 * @param c the comparator to determine the order of the sort 66 */ 67 private constructor( 68 /** 69 * The array being sorted. 70 */ 71 private val a: ByteArray, 72 /** 73 * The comparator for this sort. 74 */ 75 private val c: ByteArrayComparator, 76 77 private val entrySize: Int 78 ) { 79 /** 80 * This controls when we get *into* galloping mode. It is initialized 81 * to MIN_GALLOP. The mergeLo and mergeHi methods nudge it higher for 82 * random data, and lower for highly structured data. 83 */ 84 private var minGallop = MIN_GALLOP 85 86 /** 87 * Temp storage for merges. 88 */ 89 private var tmp: ByteArray? = null // Actual runtime type will be Object[], regardless of T 90 91 /** 92 * A stack of pending runs yet to be merged. Run i starts at 93 * address `base[i]` and extends for `len[i]` elements. It's always 94 * true (so long as the indices are in bounds) that: 95 * 96 * `runBase[i] + runLen[i] == runBase[i + 1]` 97 * 98 * so we could cut the storage for this, but it's a minor amount, 99 * and keeping all the info explicit simplifies the code. 100 */ 101 private var stackSize = 0 // Number of pending runs on stack 102 private val runBase: IntArray 103 private val runLen: IntArray 104 105 init { 106 // Allocate temp storage (which may be increased later if necessary) 107 val len = a.size / entrySize 108 val newArray = ByteArray( 109 entrySize * 110 if (len < 2 * INITIAL_TMP_STORAGE_LENGTH) 111 len.ushr(1) 112 else 113 INITIAL_TMP_STORAGE_LENGTH 114 ) 115 tmp = newArray 116 /* 117 * Allocate runs-to-be-merged stack (which cannot be expanded). The 118 * stack length requirements are described in listsort.txt. The C 119 * version always uses the same stack length (85), but this was 120 * measured to be too expensive when sorting "mid-sized" arrays (e.g., 121 * 100 elements) in Java. Therefore, we use smaller (but sufficiently 122 * large) stack lengths for smaller arrays. The "magic numbers" in the 123 * computation below must be changed if MIN_MERGE is decreased. See 124 * the MIN_MERGE declaration above for more information. 125 */ 126 val stackLen = when { 127 len < 120 -> 5 128 len < 1542 -> 10 129 len < 119151 -> 19 130 else -> 40 131 } 132 runBase = IntArray(stackLen) 133 runLen = IntArray(stackLen) 134 } 135 136 /** 137 * Pushes the specified run onto the pending-run stack. 138 * 139 * @param runBase index of the first element in the run 140 * @param runLen the number of elements in the run 141 */ pushRunnull142 private fun pushRun( 143 runBase: Int, 144 runLen: Int 145 ) { 146 this.runBase[stackSize] = runBase 147 this.runLen[stackSize] = runLen 148 stackSize++ 149 } 150 151 /** 152 * Examines the stack of runs waiting to be merged and merges adjacent runs 153 * until the stack invariants are reestablished: 154 * 155 * 1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1] 156 * 2. runLen[i - 2] > runLen[i - 1] 157 * 158 * This method is called each time a new run is pushed onto the stack, 159 * so the invariants are guaranteed to hold for i < stackSize upon 160 * entry to the method. 161 */ 162 // Fixed with http://www.envisage-project.eu/proving-android-java-and-python-sorting-algorithm-is-broken-and-how-to-fix-it/ mergeCollapsenull163 private fun mergeCollapse() { 164 while (stackSize > 1) { 165 var n = stackSize - 2 166 if (n >= 1 && runLen[n - 1] <= runLen[n] + runLen[n + 1] || n >= 2 && runLen[n - 2] <= runLen[n] + runLen[n - 1]) { 167 if (runLen[n - 1] < runLen[n + 1]) 168 n-- 169 } else if (runLen[n] > runLen[n + 1]) { 170 break // Invariant is established 171 } 172 mergeAt(n) 173 } 174 } 175 176 /** 177 * Merges all runs on the stack until only one remains. This method is 178 * called once, to complete the sort. 179 */ mergeForceCollapsenull180 private fun mergeForceCollapse() { 181 while (stackSize > 1) { 182 var n = stackSize - 2 183 if (n > 0 && runLen[n - 1] < runLen[n + 1]) 184 n-- 185 mergeAt(n) 186 } 187 } 188 189 /** 190 * Merges the two runs at stack indices i and i+1. Run i must be 191 * the penultimate or antepenultimate run on the stack. In other words, 192 * i must be equal to stackSize-2 or stackSize-3. 193 * 194 * @param i stack index of the first of the two runs to merge 195 */ mergeAtnull196 private fun mergeAt(i: Int) { 197 if (DEBUG) assert(stackSize >= 2) 198 if (DEBUG) assert(i >= 0) 199 if (DEBUG) assert(i == stackSize - 2 || i == stackSize - 3) 200 var base1 = runBase[i] 201 var len1 = runLen[i] 202 val base2 = runBase[i + 1] 203 var len2 = runLen[i + 1] 204 if (DEBUG) assert(len1 > 0 && len2 > 0) 205 if (DEBUG) assert(base1 + len1 == base2) 206 /* 207 * Record the length of the combined runs; if i is the 3rd-last 208 * run now, also slide over the last run (which isn't involved 209 * in this merge). The current run (i+1) goes away in any case. 210 */ 211 runLen[i] = len1 + len2 212 if (i == stackSize - 3) { 213 runBase[i + 1] = runBase[i + 2] 214 runLen[i + 1] = runLen[i + 2] 215 } 216 stackSize-- 217 /* 218 * Find where the first element of run2 goes in run1. Prior elements 219 * in run1 can be ignored (because they're already in place). 220 */ 221 val k = gallopRight(a, base2, a, base1, len1, 0, entrySize, c) 222 if (DEBUG) assert(k >= 0) 223 base1 += k 224 len1 -= k 225 if (len1 == 0) 226 return 227 /* 228 * Find where the last element of run1 goes in run2. Subsequent elements 229 * in run2 can be ignored (because they're already in place). 230 */ 231 len2 = gallopLeft(a, base1 + len1 - 1, a, base2, len2, len2 - 1, entrySize, c) 232 if (DEBUG) assert(len2 >= 0) 233 if (len2 == 0) 234 return 235 // Merge remaining runs, using tmp array with min(len1, len2) elements 236 if (len1 <= len2) 237 mergeLo(base1, len1, base2, len2) 238 else 239 mergeHi(base1, len1, base2, len2) 240 } 241 242 /** 243 * Merges two adjacent runs in place, in a stable fashion. The first 244 * element of the first run must be greater than the first element of the 245 * second run (a[base1] > a[base2]), and the last element of the first run 246 * (a[base1 + len1-1]) must be greater than all elements of the second run. 247 * 248 * For performance, this method should be called only when len1 <= len2; 249 * its twin, mergeHi should be called if len1 >= len2. (Either method 250 * may be called if len1 == len2.) 251 * 252 * @param base1 index of first element in first run to be merged 253 * @param len1 length of first run to be merged (must be > 0) 254 * @param base2 index of first element in second run to be merged 255 * (must be aBase + aLen) 256 * @param len2 length of second run to be merged (must be > 0) 257 */ mergeLonull258 private fun mergeLo( 259 base1: Int, 260 len1: Int, 261 base2: Int, 262 len2: Int 263 ) { 264 var len1 = len1 265 var len2 = len2 266 if (DEBUG) assert(len1 > 0 && len2 > 0 && base1 + len1 == base2) 267 // Copy first run into temp array 268 val a = this.a // For performance 269 val entrySize = entrySize 270 val tmp = ensureCapacity(len1) 271 System.arraycopy(a, base1 * entrySize, tmp, 0, len1 * entrySize) 272 var cursor1 = 0 // Indexes into tmp array 273 var cursor2 = base2 // Indexes int a 274 var dest = base1 // Indexes int a 275 // Move first element of second run and deal with degenerate cases 276 val destIndex = dest * entrySize 277 val cursor2Index = cursor2 * entrySize 278 for (i in 0 until entrySize) { 279 a[destIndex + i] = a[cursor2Index + i] 280 } 281 dest++ 282 cursor2++ 283 284 if (--len2 == 0) { 285 System.arraycopy(tmp, cursor1 * entrySize, a, dest * entrySize, len1 * entrySize) 286 return 287 } 288 if (len1 == 1) { 289 System.arraycopy(a, cursor2 * entrySize, a, dest * entrySize, len2 * entrySize) 290 val destLen2Index = (dest + len2) * entrySize 291 val cursor1Index = cursor1 * entrySize 292 for (i in 0 until entrySize) { 293 a[destLen2Index + i] = tmp[cursor1Index + i] // Last elt of run 1 to end of merge 294 } 295 return 296 } 297 val c = this.c // Use local variable for performance 298 var minGallop = this.minGallop // " " " " " 299 outer@ while (true) { 300 var count1 = 0 // Number of times in a row that first run won 301 var count2 = 0 // Number of times in a row that second run won 302 /* 303 * Do the straightforward thing until (if ever) one run starts 304 * winning consistently. 305 */ 306 do { 307 if (DEBUG) assert(len1 > 1 && len2 > 0) 308 if (c.compare(entrySize, a, cursor2, tmp, cursor1) < 0) { 309 val destIndex = dest * entrySize 310 val cursor2Index = cursor2 * entrySize 311 for (i in 0 until entrySize) { 312 a[destIndex + i] = a[cursor2Index + i] 313 } 314 dest++ 315 cursor2++ 316 count2++ 317 count1 = 0 318 if (--len2 == 0) 319 break@outer 320 } else { 321 val destIndex = dest * entrySize 322 val cursor1Index = cursor1 * entrySize 323 for (i in 0 until entrySize) { 324 a[destIndex + i] = tmp[cursor1Index + i] 325 } 326 dest++ 327 cursor1++ 328 count1++ 329 count2 = 0 330 if (--len1 == 1) 331 break@outer 332 } 333 } while (count1 or count2 < minGallop) 334 /* 335 * One run is winning so consistently that galloping may be a 336 * huge win. So try that, and continue galloping until (if ever) 337 * neither run appears to be winning consistently anymore. 338 */ 339 do { 340 if (DEBUG) assert(len1 > 1 && len2 > 0) 341 count1 = gallopRight(a, cursor2, tmp, cursor1, len1, 0, entrySize, c) 342 if (count1 != 0) { 343 System.arraycopy(tmp, cursor1 * entrySize, a, dest * entrySize, count1 * entrySize) 344 dest += count1 345 cursor1 += count1 346 len1 -= count1 347 if (len1 <= 1) 348 // len1 == 1 || len1 == 0 349 break@outer 350 } 351 var destIndex = dest * entrySize 352 val cursor2Index = cursor2 * entrySize 353 for (i in 0 until entrySize) { 354 a[destIndex + i] = a[cursor2Index + i] 355 } 356 dest++ 357 cursor2++ 358 if (--len2 == 0) 359 break@outer 360 count2 = gallopLeft(tmp, cursor1, a, cursor2, len2, 0, entrySize, c) 361 if (count2 != 0) { 362 System.arraycopy(a, cursor2 * entrySize, a, dest * entrySize, count2 * entrySize) 363 dest += count2 364 cursor2 += count2 365 len2 -= count2 366 if (len2 == 0) 367 break@outer 368 } 369 destIndex = dest * entrySize 370 val cursor1Index = cursor1 * entrySize 371 for (i in 0 until entrySize) { 372 a[destIndex + i] = tmp[cursor1Index + i] 373 } 374 dest++ 375 cursor1++ 376 if (--len1 == 1) 377 break@outer 378 minGallop-- 379 } while ((count1 >= MIN_GALLOP) or (count2 >= MIN_GALLOP)) 380 if (minGallop < 0) 381 minGallop = 0 382 minGallop += 2 // Penalize for leaving gallop mode 383 } // End of "outer" loop 384 this.minGallop = if (minGallop < 1) 1 else minGallop // Write back to field 385 when (len1) { 386 1 -> { 387 if (DEBUG) assert(len2 > 0) 388 System.arraycopy(a, cursor2 * entrySize, a, dest * entrySize, len2 * entrySize) 389 val destLen2Index = (dest + len2) * entrySize 390 val cursor1Index = cursor1 * entrySize 391 for (i in 0 until entrySize) { 392 a[destLen2Index + i] = tmp[cursor1Index + i] // Last elt of run 1 to end of merge 393 } 394 } 395 0 -> { 396 throw IllegalArgumentException( 397 "Comparison method violates its general contract!" 398 ) 399 } 400 else -> { 401 if (DEBUG) assert(len2 == 0) 402 if (DEBUG) assert(len1 > 1) 403 System.arraycopy(tmp, cursor1 * entrySize, a, dest * entrySize, len1 * entrySize) 404 } 405 } 406 } 407 408 /** 409 * Like mergeLo, except that this method should be called only if 410 * len1 >= len2; mergeLo should be called if len1 <= len2. (Either method 411 * may be called if len1 == len2.) 412 * 413 * @param base1 index of first element in first run to be merged 414 * @param len1 length of first run to be merged (must be > 0) 415 * @param base2 index of first element in second run to be merged 416 * (must be aBase + aLen) 417 * @param len2 length of second run to be merged (must be > 0) 418 */ mergeHinull419 private fun mergeHi( 420 base1: Int, 421 len1: Int, 422 base2: Int, 423 len2: Int 424 ) { 425 var len1 = len1 426 var len2 = len2 427 if (DEBUG) assert(len1 > 0 && len2 > 0 && base1 + len1 == base2) 428 // Copy second run into temp array 429 val a = this.a // For performance 430 val tmp = ensureCapacity(len2) 431 val entrySize = entrySize 432 System.arraycopy(a, base2 * entrySize, tmp, 0, len2 * entrySize) 433 var cursor1 = base1 + len1 - 1 // Indexes into a 434 var cursor2 = len2 - 1 // Indexes into tmp array 435 var dest = base2 + len2 - 1 // Indexes into a 436 // Move last element of first run and deal with degenerate cases 437 var destIndex = dest * entrySize 438 val cursor1Index = cursor1 * entrySize 439 for (i in 0 until entrySize) { 440 a[destIndex + i] = a[cursor1Index + i] 441 } 442 dest-- 443 cursor1-- 444 if (--len1 == 0) { 445 System.arraycopy(tmp, 0, a, (dest - (len2 - 1)) * entrySize, len2 * entrySize) 446 return 447 } 448 if (len2 == 1) { 449 dest -= len1 450 cursor1 -= len1 451 System.arraycopy(a, (cursor1 + 1) * entrySize, a, (dest + 1) * entrySize, len1 * entrySize) 452 val destIndex = dest * entrySize 453 val cursor2Index = cursor2 * entrySize 454 for (i in 0 until entrySize) { 455 a[destIndex + i] = tmp[cursor2Index + i] 456 } 457 return 458 } 459 val c = this.c // Use local variable for performance 460 var minGallop = this.minGallop // " " " " " 461 outer@ while (true) { 462 var count1 = 0 // Number of times in a row that first run won 463 var count2 = 0 // Number of times in a row that second run won 464 /* 465 * Do the straightforward thing until (if ever) one run 466 * appears to win consistently. 467 */ 468 do { 469 if (DEBUG) assert(len1 > 0 && len2 > 1) 470 if (c.compare(entrySize, tmp, cursor2, a, cursor1) < 0) { 471 val destIndex = dest * entrySize 472 val cursor1Index = cursor1 * entrySize 473 for (i in 0 until entrySize) { 474 a[destIndex + i] = a[cursor1Index + i] 475 } 476 dest-- 477 cursor1-- 478 count1++ 479 count2 = 0 480 if (--len1 == 0) 481 break@outer 482 } else { 483 val destIndex = dest * entrySize 484 val cursor2Index = cursor2 * entrySize 485 for (i in 0 until entrySize) { 486 a[destIndex + i] = tmp[cursor2Index + i] 487 } 488 dest-- 489 cursor2-- 490 count2++ 491 count1 = 0 492 if (--len2 == 1) 493 break@outer 494 } 495 } while (count1 or count2 < minGallop) 496 /* 497 * One run is winning so consistently that galloping may be a 498 * huge win. So try that, and continue galloping until (if ever) 499 * neither run appears to be winning consistently anymore. 500 */ 501 do { 502 if (DEBUG) assert(len1 > 0 && len2 > 1) 503 count1 = len1 - gallopRight(tmp, cursor2, a, base1, len1, len1 - 1, entrySize, c) 504 if (count1 != 0) { 505 dest -= count1 506 cursor1 -= count1 507 len1 -= count1 508 System.arraycopy( 509 a, (cursor1 + 1) * entrySize, a, (dest + 1) * entrySize, count1 * entrySize 510 ) 511 if (len1 == 0) 512 break@outer 513 } 514 destIndex = dest * entrySize 515 val cursor2Index = cursor2 * entrySize 516 for (i in 0 until entrySize) { 517 a[destIndex + i] = tmp[cursor2Index + i] 518 } 519 dest-- 520 cursor2-- 521 if (--len2 == 1) 522 break@outer 523 count2 = len2 - gallopLeft(a, cursor1, tmp, 0, len2, len2 - 1, entrySize, c) 524 if (count2 != 0) { 525 dest -= count2 526 cursor2 -= count2 527 len2 -= count2 528 System.arraycopy( 529 tmp, (cursor2 + 1) * entrySize, a, (dest + 1) * entrySize, count2 * entrySize 530 ) 531 if (len2 <= 1) 532 // len2 == 1 || len2 == 0 533 break@outer 534 } 535 val destIndex = dest * entrySize 536 val cursor1Index = cursor1 * entrySize 537 for (i in 0 until entrySize) { 538 a[destIndex + i] = a[cursor1Index + i] 539 } 540 dest-- 541 cursor1-- 542 if (--len1 == 0) 543 break@outer 544 minGallop-- 545 } while ((count1 >= MIN_GALLOP) or (count2 >= MIN_GALLOP)) 546 if (minGallop < 0) 547 minGallop = 0 548 minGallop += 2 // Penalize for leaving gallop mode 549 } // End of "outer" loop 550 this.minGallop = if (minGallop < 1) 1 else minGallop // Write back to field 551 when (len2) { 552 1 -> { 553 if (DEBUG) assert(len1 > 0) 554 dest -= len1 555 cursor1 -= len1 556 System.arraycopy(a, (cursor1 + 1) * entrySize, a, (dest + 1) * entrySize, len1 * entrySize) 557 val destIndex = dest * entrySize 558 val cursor2Index = cursor2 * entrySize 559 for (i in 0 until entrySize) { 560 a[destIndex + i] = tmp[cursor2Index + i] // Move first elt of run2 to front of merge 561 } 562 } 563 0 -> { 564 throw IllegalArgumentException( 565 "Comparison method violates its general contract!" 566 ) 567 } 568 else -> { 569 if (DEBUG) assert(len1 == 0) 570 if (DEBUG) assert(len2 > 0) 571 System.arraycopy(tmp, 0, a, (dest - (len2 - 1)) * entrySize, len2 * entrySize) 572 } 573 } 574 } 575 576 /** 577 * Ensures that the external array tmp has at least the specified 578 * number of elements, increasing its size if necessary. The size 579 * increases exponentially to ensure amortized linear time complexity. 580 * 581 * @param minCapacity the minimum required capacity of the tmp array 582 * @return tmp, whether or not it grew 583 */ ensureCapacitynull584 private fun ensureCapacity(minCapacity: Int): ByteArray { 585 if (tmp!!.size < minCapacity * entrySize) { 586 // Compute smallest power of 2 > minCapacity 587 var newSize = minCapacity 588 newSize = newSize or (newSize shr 1) 589 newSize = newSize or (newSize shr 2) 590 newSize = newSize or (newSize shr 4) 591 newSize = newSize or (newSize shr 8) 592 newSize = newSize or (newSize shr 16) 593 newSize++ 594 newSize = if (newSize < 0) 595 // Not bloody likely! 596 minCapacity 597 else 598 min(newSize, (a.size / entrySize).ushr(1)) 599 val newArray = ByteArray(newSize * entrySize) 600 tmp = newArray 601 } 602 return tmp!! 603 } 604 605 companion object { 606 /** 607 * This is the minimum sized sequence that will be merged. Shorter 608 * sequences will be lengthened by calling binarySort. If the entire 609 * array is less than this length, no merges will be performed. 610 * 611 * This constant should be a power of two. It was 64 in Tim Peter's C 612 * implementation, but 32 was empirically determined to work better in 613 * this implementation. In the unlikely event that you set this constant 614 * to be a number that's not a power of two, you'll need to change the 615 * [.minRunLength] computation. 616 * 617 * If you decrease this constant, you must change the stackLen 618 * computation in the TimSort constructor, or you risk an 619 * ArrayOutOfBounds exception. See listsort.txt for a discussion 620 * of the minimum stack length required as a function of the length 621 * of the array being sorted and the minimum merge sequence length. 622 */ 623 private const val MIN_MERGE = 32 624 625 /** 626 * When we get into galloping mode, we stay there until both runs win less 627 * often than MIN_GALLOP consecutive times. 628 */ 629 private const val MIN_GALLOP = 7 630 631 /** 632 * Maximum initial size of tmp array, which is used for merging. The array 633 * can grow to accommodate demand. 634 * 635 * Unlike Tim's original C version, we do not allocate this much storage 636 * when sorting smaller arrays. This change was required for performance. 637 */ 638 private const val INITIAL_TMP_STORAGE_LENGTH = 256 639 640 /** 641 * Asserts have been placed in if-statements for performace. To enable them, 642 * set this field to true and enable them in VM with a command line flag. 643 * If you modify this class, please do test the asserts! 644 */ 645 private const val DEBUG = false 646 647 /* 648 * The next two methods (which are package private and static) constitute 649 * the entire API of this class. Each of these methods obeys the contract 650 * of the public method with the same signature in java.util.Arrays. 651 */ sortnull652 fun sort( 653 a: ByteArray, 654 entrySize: Int, 655 c: ByteArrayComparator 656 ) { 657 sort(a, 0, a.size / entrySize, entrySize, c) 658 } 659 sortnull660 fun sort( 661 a: ByteArray, 662 lo: Int, 663 hi: Int, 664 entrySize: Int, 665 c: ByteArrayComparator 666 ) { 667 var lo = lo 668 checkStartAndEnd(a.size / entrySize, lo, hi) 669 var nRemaining = hi - lo 670 if (nRemaining < 2) 671 return // Arrays of size 0 and 1 are always sorted 672 // If array is small, do a "mini-TimSort" with no merges 673 if (nRemaining < MIN_MERGE) { 674 val initRunLen = countRunAndMakeAscending(a, lo, hi, entrySize, c) 675 binarySort(a, lo, hi, lo + initRunLen, entrySize, c) 676 return 677 } 678 /** 679 * March over the array once, left to right, finding natural runs, 680 * extending short natural runs to minRun elements, and merging runs 681 * to maintain stack invariant. 682 */ 683 val ts = ByteArrayTimSort(a, c, entrySize) 684 val minRun = minRunLength(nRemaining) 685 do { 686 // Identify next run 687 var runLen = countRunAndMakeAscending(a, lo, hi, entrySize, c) 688 // If run is short, extend to min(minRun, nRemaining) 689 if (runLen < minRun) { 690 val force = if (nRemaining <= minRun) nRemaining else minRun 691 binarySort(a, lo, lo + force, lo + runLen, entrySize, c) 692 runLen = force 693 } 694 // Push run onto pending-run stack, and maybe merge 695 ts.pushRun(lo, runLen) 696 ts.mergeCollapse() 697 // Advance to find next run 698 lo += runLen 699 nRemaining -= runLen 700 } while (nRemaining != 0) 701 // Merge all remaining runs to complete sort 702 if (DEBUG) assert(lo == hi) 703 ts.mergeForceCollapse() 704 if (DEBUG) assert(ts.stackSize == 1) 705 } 706 checkStartAndEndnull707 private fun checkStartAndEnd( 708 len: Int, 709 start: Int, 710 end: Int 711 ) { 712 if (start < 0 || end > len) { 713 throw ArrayIndexOutOfBoundsException( 714 "start < 0 || end > len." 715 + " start=" + start + ", end=" + end + ", len=" + len 716 ) 717 } 718 if (start > end) { 719 throw IllegalArgumentException("start > end: $start > $end") 720 } 721 } 722 723 /** 724 * Sorts the specified portion of the specified array using a binary 725 * insertion sort. This is the best method for sorting small numbers 726 * of elements. It requires O(n log n) compares, but O(n^2) data 727 * movement (worst case). 728 * 729 * If the initial part of the specified range is already sorted, 730 * this method can take advantage of it: the method assumes that the 731 * elements from index `lo`, inclusive, to `start`, 732 * exclusive are already sorted. 733 * 734 * @param a the array in which a range is to be sorted 735 * @param lo the index of the first element in the range to be sorted 736 * @param hi the index after the last element in the range to be sorted 737 * @param start the index of the first element in the range that is 738 * not already known to be sorted (@code lo <= start <= hi} 739 * @param c comparator to used for the sort 740 */ binarySortnull741 private fun binarySort( 742 a: ByteArray, 743 lo: Int, 744 hi: Int, 745 start: Int, 746 entrySize: Int, 747 c: ByteArrayComparator 748 ) { 749 var start = start 750 if (DEBUG) assert(start in lo..hi) 751 if (start == lo) 752 start++ 753 val pivot = ByteArray(entrySize) 754 while (start < hi) { 755 val startIndex = start * entrySize 756 for (i in 0 until entrySize) { 757 pivot[i] = a[startIndex + i] 758 } 759 // Set left (and right) to the index where a[start] (pivot) belongs 760 var left = lo 761 var right = start 762 if (DEBUG) assert(left <= right) 763 /* 764 * Invariants: 765 * pivot >= all in [lo, left). 766 * pivot < all in [right, start). 767 */ 768 while (left < right) { 769 val mid = (left + right).ushr(1) 770 if (c.compare(entrySize, pivot, 0, a, mid) < 0) 771 right = mid 772 else 773 left = mid + 1 774 } 775 if (DEBUG) assert(left == right) 776 /* 777 * The invariants still hold: pivot >= all in [lo, left) and 778 * pivot < all in [left, start), so pivot belongs at left. Note 779 * that if there are elements equal to pivot, left points to the 780 * first slot after them -- that's why this sort is stable. 781 * Slide elements over to make room for pivot. 782 */ 783 // Switch is just an optimization for arraycopy in default case 784 when (val n = start - left) { // The number of elements to move 785 2 -> { 786 val leftIndex = left * entrySize 787 val leftPlusOneIndex = (left + 1) * entrySize 788 val leftPlusTwoIndex = (left + 2) * entrySize 789 for (i in 0 until entrySize) { 790 a[leftPlusTwoIndex + i] = a[leftPlusOneIndex + i] 791 } 792 for (i in 0 until entrySize) { 793 a[leftPlusOneIndex + i] = a[leftIndex + i] 794 } 795 } 796 1 -> { 797 val leftIndex = left * entrySize 798 val leftPlusOneIndex = (left + 1) * entrySize 799 for (i in 0 until entrySize) { 800 a[leftPlusOneIndex + i] = a[leftIndex + i] 801 } 802 } 803 else -> { 804 System.arraycopy(a, left * entrySize, a, (left + 1) * entrySize, n * entrySize) 805 } 806 } 807 val leftIndex = left * entrySize 808 for (i in 0 until entrySize) { 809 a[leftIndex + i] = pivot[i] 810 } 811 start++ 812 } 813 } 814 815 /** 816 * Returns the length of the run beginning at the specified position in 817 * the specified array and reverses the run if it is descending (ensuring 818 * that the run will always be ascending when the method returns). 819 * 820 * A run is the longest ascending sequence with: 821 * 822 * a[lo] <= a[lo + 1] <= a[lo + 2] <= ... 823 * 824 * or the longest descending sequence with: 825 * 826 * a[lo] > a[lo + 1] > a[lo + 2] > ... 827 * 828 * For its intended use in a stable mergesort, the strictness of the 829 * definition of "descending" is needed so that the call can safely 830 * reverse a descending sequence without violating stability. 831 * 832 * @param a the array in which a run is to be counted and possibly reversed 833 * @param lo index of the first element in the run 834 * @param hi index after the last element that may be contained in the run. 835 * It is required that @code{lo < hi}. 836 * @param c the comparator to used for the sort 837 * @return the length of the run beginning at the specified position in 838 * the specified array 839 */ countRunAndMakeAscendingnull840 private fun countRunAndMakeAscending( 841 a: ByteArray, 842 lo: Int, 843 hi: Int, 844 entrySize: Int, 845 c: ByteArrayComparator 846 ): Int { 847 if (DEBUG) assert(lo < hi) 848 var runHi = lo + 1 849 if (runHi == hi) 850 return 1 851 // Find end of run, and reverse range if descending 852 853 val comparison = c.compare(entrySize, a, runHi, a, lo) 854 runHi++ 855 if (comparison < 0) { // Descending 856 while (runHi < hi && c.compare(entrySize, a, runHi, a, runHi - 1) < 0) 857 runHi++ 858 reverseRange(a, lo, runHi, entrySize) 859 } else { // Ascending 860 while (runHi < hi && c.compare(entrySize, a, runHi, a, runHi - 1) >= 0) 861 runHi++ 862 } 863 return runHi - lo 864 } 865 866 /** 867 * Reverse the specified range of the specified array. 868 * 869 * @param a the array in which a range is to be reversed 870 * @param lo the index of the first element in the range to be reversed 871 * @param hi the index after the last element in the range to be reversed 872 */ reverseRangenull873 private fun reverseRange( 874 a: ByteArray, 875 lo: Int, 876 hi: Int, 877 entrySize: Int 878 ) { 879 var lo = lo 880 var hi = hi 881 hi-- 882 while (lo < hi) { 883 val loIndex = lo * entrySize 884 val hiIndex = hi * entrySize 885 for (i in 0 until entrySize) { 886 val t = a[loIndex + i] 887 a[loIndex + i] = a[hiIndex + i] 888 a[hiIndex + i] = t 889 } 890 lo++ 891 hi-- 892 } 893 } 894 895 /** 896 * Returns the minimum acceptable run length for an array of the specified 897 * length. Natural runs shorter than this will be extended with 898 * [.binarySort]. 899 * 900 * Roughly speaking, the computation is: 901 * 902 * If n < MIN_MERGE, return n (it's too small to bother with fancy stuff). 903 * Else if n is an exact power of 2, return MIN_MERGE/2. 904 * Else return an int k, MIN_MERGE/2 <= k <= MIN_MERGE, such that n/k 905 * is close to, but strictly less than, an exact power of 2. 906 * 907 * For the rationale, see listsort.txt. 908 * 909 * @param n the length of the array to be sorted 910 * @return the length of the minimum run to be merged 911 */ minRunLengthnull912 private fun minRunLength(n: Int): Int { 913 var n = n 914 if (DEBUG) assert(n >= 0) 915 var r = 0 // Becomes 1 if any 1 bits are shifted off 916 while (n >= MIN_MERGE) { 917 r = r or (n and 1) 918 n = n shr 1 919 } 920 return n + r 921 } 922 923 /** 924 * Locates the position at which to insert the specified key into the 925 * specified sorted range; if the range contains an element equal to key, 926 * returns the index of the leftmost equal element. 927 * 928 * @param keyIndex the key whose insertion point to search for 929 * @param a the array in which to search 930 * @param base the index of the first element in the range 931 * @param len the length of the range; must be > 0 932 * @param hint the index at which to begin the search, 0 <= hint < n. 933 * The closer hint is to the result, the faster this method will run. 934 * @param c the comparator used to order the range, and to search 935 * @return the int k, 0 <= k <= n such that a[b + k - 1] < key <= a[b + k], 936 * pretending that a[b - 1] is minus infinity and a[b + n] is infinity. 937 * In other words, key belongs at index b + k; or in other words, 938 * the first k elements of a should precede key, and the last n - k 939 * should follow it. 940 */ gallopLeftnull941 private fun gallopLeft( 942 keyArray: ByteArray, 943 // Index already divided by entrySize 944 keyIndex: Int, 945 a: ByteArray, 946 base: Int, 947 len: Int, 948 hint: Int, 949 entrySize: Int, 950 c: ByteArrayComparator 951 ): Int { 952 if (DEBUG) assert(len > 0 && hint >= 0 && hint < len) 953 var lastOfs = 0 954 var ofs = 1 955 if (c.compare(entrySize, keyArray, keyIndex, a, base + hint) > 0) { 956 // Gallop right until a[base+hint+lastOfs] < key <= a[base+hint+ofs] 957 val maxOfs = len - hint 958 while (ofs < maxOfs && c.compare(entrySize, keyArray, keyIndex, a, base + hint + ofs) > 0) { 959 lastOfs = ofs 960 ofs = ofs * 2 + 1 961 if (ofs <= 0) 962 // int overflow 963 ofs = maxOfs 964 } 965 if (ofs > maxOfs) 966 ofs = maxOfs 967 // Make offsets relative to base 968 lastOfs += hint 969 ofs += hint 970 } else { // key <= a[base + hint] 971 // Gallop left until a[base+hint-ofs] < key <= a[base+hint-lastOfs] 972 val maxOfs = hint + 1 973 while (ofs < maxOfs && c.compare( 974 entrySize, keyArray, keyIndex, a, base + hint - ofs 975 ) <= 0 976 ) { 977 lastOfs = ofs 978 ofs = ofs * 2 + 1 979 if (ofs <= 0) 980 // int overflow 981 ofs = maxOfs 982 } 983 if (ofs > maxOfs) 984 ofs = maxOfs 985 // Make offsets relative to base 986 val tmp = lastOfs 987 lastOfs = hint - ofs 988 ofs = hint - tmp 989 } 990 if (DEBUG) assert(-1 <= lastOfs && lastOfs < ofs && ofs <= len) 991 /* 992 * Now a[base+lastOfs] < key <= a[base+ofs], so key belongs somewhere 993 * to the right of lastOfs but no farther right than ofs. Do a binary 994 * search, with invariant a[base + lastOfs - 1] < key <= a[base + ofs]. 995 */ 996 lastOfs++ 997 while (lastOfs < ofs) { 998 val m = lastOfs + (ofs - lastOfs).ushr(1) 999 if (c.compare(entrySize, keyArray, keyIndex, a, base + m) > 0) 1000 lastOfs = m + 1 // a[base + m] < key 1001 else 1002 ofs = m // key <= a[base + m] 1003 } 1004 if (DEBUG) assert(lastOfs == ofs) // so a[base + ofs - 1] < key <= a[base + ofs] 1005 return ofs 1006 } 1007 1008 /** 1009 * Like gallopLeft, except that if the range contains an element equal to 1010 * key, gallopRight returns the index after the rightmost equal element. 1011 * 1012 * @param keyIndex the key whose insertion point to search for 1013 * @param a the array in which to search 1014 * @param base the index of the first element in the range 1015 * @param len the length of the range; must be > 0 1016 * @param hint the index at which to begin the search, 0 <= hint < n. 1017 * The closer hint is to the result, the faster this method will run. 1018 * @param c the comparator used to order the range, and to search 1019 * @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k] 1020 */ gallopRightnull1021 private fun gallopRight( 1022 keyArray: ByteArray, 1023 // Index already divided by entrySize 1024 keyIndex: Int, 1025 a: ByteArray, 1026 base: Int, 1027 len: Int, 1028 hint: Int, 1029 entrySize: Int, 1030 c: ByteArrayComparator 1031 ): Int { 1032 if (DEBUG) assert(len > 0 && hint >= 0 && hint < len) 1033 var ofs = 1 1034 var lastOfs = 0 1035 if (c.compare(entrySize, keyArray, keyIndex, a, base + hint) < 0) { 1036 // Gallop left until a[b+hint - ofs] <= key < a[b+hint - lastOfs] 1037 val maxOfs = hint + 1 1038 while (ofs < maxOfs && c.compare(entrySize, keyArray, keyIndex, a, base + hint - ofs) < 0) { 1039 lastOfs = ofs 1040 ofs = ofs * 2 + 1 1041 if (ofs <= 0) 1042 // int overflow 1043 ofs = maxOfs 1044 } 1045 if (ofs > maxOfs) 1046 ofs = maxOfs 1047 // Make offsets relative to b 1048 val tmp = lastOfs 1049 lastOfs = hint - ofs 1050 ofs = hint - tmp 1051 } else { // a[b + hint] <= key 1052 // Gallop right until a[b+hint + lastOfs] <= key < a[b+hint + ofs] 1053 val maxOfs = len - hint 1054 while (ofs < maxOfs && c.compare( 1055 entrySize, keyArray, keyIndex, a, base + hint + ofs 1056 ) >= 0 1057 ) { 1058 lastOfs = ofs 1059 ofs = ofs * 2 + 1 1060 if (ofs <= 0) 1061 // int overflow 1062 ofs = maxOfs 1063 } 1064 if (ofs > maxOfs) 1065 ofs = maxOfs 1066 // Make offsets relative to b 1067 lastOfs += hint 1068 ofs += hint 1069 } 1070 if (DEBUG) assert(-1 <= lastOfs && lastOfs < ofs && ofs <= len) 1071 /* 1072 * Now a[b + lastOfs] <= key < a[b + ofs], so key belongs somewhere to 1073 * the right of lastOfs but no farther right than ofs. Do a binary 1074 * search, with invariant a[b + lastOfs - 1] <= key < a[b + ofs]. 1075 */ 1076 lastOfs++ 1077 while (lastOfs < ofs) { 1078 val m = lastOfs + (ofs - lastOfs).ushr(1) 1079 if (c.compare(entrySize, keyArray, keyIndex, a, base + m) < 0) 1080 ofs = m // key < a[b + m] 1081 else 1082 lastOfs = m + 1 // a[b + m] <= key 1083 } 1084 if (DEBUG) assert(lastOfs == ofs) // so a[b + ofs - 1] <= key < a[b + ofs] 1085 return ofs 1086 } 1087 } 1088 } 1089