• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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