github.com/jfcg/sorty/v2@v2.1.0/sorty.go (about) 1 /* Copyright (c) 2019, Serhat Şevki Dinçer. 2 This Source Code Form is subject to the terms of the Mozilla Public 3 License, v. 2.0. If a copy of the MPL was not distributed with this 4 file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 */ 6 7 // Package sorty is a type-specific, fast, efficient, concurrent / parallel sorting 8 // library. It is an innovative [QuickSort] implementation, hence in-place and does not 9 // require extra memory. You can call: 10 // 11 // import "github.com/jfcg/sorty/v2" 12 // 13 // sorty.SortSlice(native_slice) // []int, []float64, []string, []*T etc. in ascending order 14 // sorty.SortLen(len_slice) // []string or [][]T 'by length' in ascending order 15 // sorty.Sort(n, lesswap) // lesswap() based 16 // 17 // [QuickSort]: https://en.wikipedia.org/wiki/Quicksort 18 package sorty 19 20 import ( 21 "reflect" 22 "unsafe" 23 24 "github.com/jfcg/sixb" 25 ) 26 27 // MaxGor is the maximum number of goroutines (including caller) that can be 28 // concurrently used for sorting per Sort*() call. MaxGor can be changed live, even 29 // during ongoing Sort*() calls. MaxGor ≤ 1 (or a short input) yields single-goroutine 30 // sorting: sorty will not create any goroutines or channel. 31 var MaxGor uint64 = 3 32 33 func init() { 34 if !(4097 > MaxGor && MaxGor > 0 && MaxLenRec > MaxLenRecFC && MaxLenRecFC > 35 2*MaxLenIns && MaxLenIns > MaxLenInsFC && MaxLenInsFC > 2*nsShort) { 36 panic("sorty: check your MaxGor/MaxLen* values") 37 } 38 } 39 40 type FloatOption int32 41 42 const ( 43 NaNsmall FloatOption = iota - 1 44 NaNignore 45 NaNlarge 46 ) 47 48 // NaNoption determines how sorty handles [NaNs] in [SortSlice]() and [IsSortedSlice](). 49 // NaNs can be treated as smaller than, ignored or larger than other float values. 50 // By default NaNs will end up at the end of your ascending-sorted slice. If your slice 51 // contains NaNs and you choose to ignore them, the result is undefined behavior, and 52 // almost always not sorted properly. sorty is only tested with small/large options. 53 // 54 // [NaNs]: https://en.wikipedia.org/wiki/NaN 55 var NaNoption = NaNlarge 56 57 // Search returns lowest integer k in [0,n) where fn(k) is true, assuming: 58 // 59 // fn(k) implies fn(k+1) 60 // 61 // If there is no such k, it returns n. It can be used to locate an element 62 // in a sorted collection. 63 // 64 //go:nosplit 65 func Search(n int, fn func(int) bool) int { 66 l, h := 0, n 67 68 for l < h { 69 m := sixb.MeanI(l, h) 70 71 if fn(m) { 72 h = m 73 } else { 74 l = m + 1 75 } 76 } 77 return l 78 } 79 80 // synchronization variables for [g]long*() 81 type syncVar struct { 82 nGor uint64 // number of sorting goroutines 83 done chan int // end signal 84 } 85 86 // gorFull returns true if goroutine quota is full, inlined 87 // 88 //go:norace 89 func gorFull(sv *syncVar) bool { 90 mg := MaxGor 91 return sv.nGor >= mg 92 } 93 94 const ( 95 // #samples in pivot selection for 96 nsShort = 4 // short range 97 nsLong = 6 // long range 98 nsConc = 8 // dual range 99 ) 100 101 // Given n ≥ 2 and slice length ≥ 2n, select n equidistant samples 102 // from slice that minimizes max distance to non-selected members, inlined 103 func minMaxSample(slen, n uint) (first, step, last uint) { 104 step = slen / n // ≥ 2 105 n-- 106 span := n * step 107 tail := slen - span // 1 + #members in both tails 108 if tail > n && tail>>1 > (step+1)>>1 { 109 step++ 110 span += n 111 tail -= n 112 } 113 first = tail >> 1 // larger tail 114 last = first + span 115 return 116 } 117 118 var firstFour = [8]uint32{0, 0, ^uint32(0), 0, 0, 1, 1, 0} 119 var stepFour = [8]uint32{0, 0, 1, 1, 0, 0, 0, 1} 120 121 // optimized version of minMaxSample for n=4, inlined 122 func minMaxFour(slen uint32) (first, step uint32) { 123 mod := slen & 7 124 first = slen>>3 + firstFour[mod] 125 step = slen>>2 + stepFour[mod] 126 return 127 } 128 129 // inlined 130 func insertionI(slc []int) { 131 if unsafe.Sizeof(int(0)) == 8 { 132 insertionI8(*(*[]int64)(unsafe.Pointer(&slc))) 133 } else { 134 insertionI4(*(*[]int32)(unsafe.Pointer(&slc))) 135 } 136 } 137 138 const sliceBias reflect.Kind = 100 139 140 // extracts slice and element kind from ar 141 // 142 //go:nosplit 143 func extractSK(ar any) (slc sixb.Slice, kind reflect.Kind) { 144 tipe := reflect.TypeOf(ar) 145 if tipe.Kind() != reflect.Slice { 146 return 147 } 148 tipe = tipe.Elem() 149 kind = tipe.Kind() 150 151 switch kind { 152 // map int/uint/pointer types to hardware type 153 case reflect.Uintptr, reflect.Pointer, reflect.UnsafePointer: 154 kind = reflect.Uint32 + reflect.Kind(unsafe.Sizeof(uintptr(0))>>3) 155 case reflect.Uint: 156 kind = reflect.Uint32 + reflect.Kind(unsafe.Sizeof(uint(0))>>3) 157 case reflect.Int: 158 kind = reflect.Int32 + reflect.Kind(unsafe.Sizeof(int(0))>>3) 159 // map []T to sliceBias + Kind(T) 160 case reflect.Slice: 161 kind = sliceBias + tipe.Elem().Kind() 162 // other recognized types 163 case reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, 164 reflect.Float32, reflect.Float64, reflect.String: 165 default: 166 kind = reflect.Invalid 167 return 168 } 169 170 v := reflect.ValueOf(ar) 171 p, l := v.Pointer(), v.Len() 172 slc = sixb.Slice{Data: unsafe.Pointer(p), Len: l, Cap: l} 173 return 174 }