gofront 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +14 -14
  2. package/package.json +10 -38
  3. package/src/backend/js/expressions.js +2060 -389
  4. package/src/backend/js/index.js +586 -174
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +936 -1644
  7. package/src/backend/js/statements.js +1349 -132
  8. package/src/backend/js/stdlib/index.js +51 -94
  9. package/src/backend/js/templ.js +67 -118
  10. package/src/backend/js/type-info.js +436 -0
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +80 -0
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +753 -872
  15. package/src/backend/wasm/emit-builtins.js +914 -237
  16. package/src/backend/wasm/emit-exprs.js +1456 -195
  17. package/src/backend/wasm/emit-maps.js +78 -380
  18. package/src/backend/wasm/emit-stmts.js +1659 -214
  19. package/src/backend/wasm/emit.js +614 -88
  20. package/src/backend/wasm/encode.js +43 -13
  21. package/src/backend/wasm/globals.js +491 -0
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +406 -3195
  24. package/src/backend/wasm/method-expr.js +392 -0
  25. package/src/backend/wasm/module-emitter.js +1615 -0
  26. package/src/backend/wasm/monomorph.js +164 -9
  27. package/src/backend/wasm/natives.js +596 -0
  28. package/src/backend/wasm/prescan.js +249 -0
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -37
  31. package/src/backend/wasm/stdlib/simd.js +293 -28
  32. package/src/backend/wasm/types.js +260 -27
  33. package/src/backend/wasm/variadic.js +93 -0
  34. package/src/backend/wasm/wat.js +47 -20
  35. package/src/{cli-core.js → cli/cli-core.js} +241 -11
  36. package/src/{test-runner.js → cli/test-runner.js} +188 -122
  37. package/src/{vendor.js → cli/vendor.js} +3 -4
  38. package/src/compiler.js +202 -386
  39. package/src/index.js +15 -169
  40. package/src/{lexer.js → lexer/lexer.js} +203 -62
  41. package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
  42. package/src/{tokens.js → lexer/tokens.js} +2 -0
  43. package/src/link/sourcemap.js +97 -0
  44. package/src/link/stdlib.js +737 -0
  45. package/src/link/type-names.js +148 -0
  46. package/src/link/wasm.js +318 -0
  47. package/src/lower/bounds.js +547 -0
  48. package/src/lower/boxing.js +1 -1
  49. package/src/lower/captures.js +267 -64
  50. package/src/lower/embedding.js +32 -3
  51. package/src/lower/errors-as.js +196 -0
  52. package/src/lower/fmt-formatters.js +905 -0
  53. package/src/lower/functions.js +2 -0
  54. package/src/lower/gen-types.js +274 -0
  55. package/src/lower/index.js +33 -19
  56. package/src/lower/json-codecs.js +1379 -0
  57. package/src/lower/ownership.js +47 -8
  58. package/src/lower/range.js +86 -1
  59. package/src/lower/reachability.js +150 -0
  60. package/src/lower/walk.js +12 -0
  61. package/src/parser/declarations.js +83 -15
  62. package/src/parser/expressions.js +110 -18
  63. package/src/parser/index.js +2 -1
  64. package/src/parser/statements.js +38 -22
  65. package/src/{templ-parser.js → parser/templ.js} +179 -17
  66. package/src/parser/types.js +70 -9
  67. package/src/resolver.js +21 -1
  68. package/src/typechecker/assignability.js +291 -19
  69. package/src/typechecker/const-fold.js +399 -0
  70. package/src/typechecker/constants.js +282 -0
  71. package/src/typechecker/expressions.js +1220 -183
  72. package/src/typechecker/index.js +659 -92
  73. package/src/typechecker/init-order.js +426 -0
  74. package/src/typechecker/operand-text.js +40 -0
  75. package/src/typechecker/resolve.js +613 -62
  76. package/src/typechecker/statements.js +212 -20
  77. package/src/typechecker/stdlib/excluded.js +902 -0
  78. package/src/typechecker/stdlib/index.js +38 -0
  79. package/src/typechecker/stdlib/simd.js +65 -15
  80. package/src/typechecker/termination.js +11 -2
  81. package/src/typechecker/types.js +268 -514
  82. package/stdlib/bytes/buffer.go +479 -0
  83. package/stdlib/bytes/bytes.go +1010 -0
  84. package/stdlib/bytes/reader.go +161 -0
  85. package/stdlib/cmp/cmp.go +65 -0
  86. package/stdlib/context/context.go +115 -0
  87. package/stdlib/crypto/rand/rand.go +34 -0
  88. package/stdlib/crypto/sha256/sha256.go +249 -0
  89. package/stdlib/encoding/base64/base64.go +331 -0
  90. package/stdlib/encoding/binary/binary.go +332 -0
  91. package/stdlib/encoding/hex/hex.go +140 -0
  92. package/stdlib/encoding/json/errors.go +31 -0
  93. package/stdlib/encoding/json/indent.go +71 -0
  94. package/stdlib/encoding/json/json.go +62 -0
  95. package/stdlib/encoding/json/number.go +12 -0
  96. package/stdlib/encoding/json/stream.go +173 -0
  97. package/stdlib/errors/errors.go +174 -0
  98. package/stdlib/fmt/errors.go +110 -0
  99. package/stdlib/fmt/format.go +732 -0
  100. package/stdlib/fmt/print.go +1232 -0
  101. package/stdlib/gom/gom.go +293 -0
  102. package/stdlib/hash/hash.go +53 -0
  103. package/stdlib/html/html.go +251 -0
  104. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  105. package/stdlib/internal/jsonrt/dec.go +871 -0
  106. package/stdlib/internal/jsonrt/enc.go +516 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +658 -0
  111. package/stdlib/io/io.go +502 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/maps.go +131 -0
  114. package/stdlib/math/bits/bits.go +507 -0
  115. package/stdlib/math/extra.go +272 -0
  116. package/stdlib/math/math.go +439 -0
  117. package/stdlib/math/rand/rand.go +297 -0
  118. package/stdlib/os/os.go +93 -0
  119. package/stdlib/path/filepath/filepath.go +47 -0
  120. package/stdlib/path/path.go +394 -0
  121. package/stdlib/regexp/regexp.go +231 -0
  122. package/stdlib/slices/slices.go +526 -0
  123. package/stdlib/slices/sort.go +209 -0
  124. package/stdlib/slices/zsort_cmp.go +480 -0
  125. package/stdlib/slices/zsort_ordered.go +482 -0
  126. package/stdlib/sort/sort.go +500 -0
  127. package/stdlib/sort/zsort_func.go +480 -0
  128. package/stdlib/sort/zsort_iface.go +480 -0
  129. package/stdlib/strconv/float.go +1699 -0
  130. package/stdlib/strconv/isprint.go +752 -0
  131. package/stdlib/strconv/quote.go +526 -0
  132. package/stdlib/strconv/strconv.go +616 -0
  133. package/stdlib/strings/builder.go +95 -0
  134. package/stdlib/strings/reader.go +163 -0
  135. package/stdlib/strings/replacer.go +102 -0
  136. package/stdlib/strings/strings.go +807 -0
  137. package/stdlib/sync/sync.go +268 -0
  138. package/stdlib/testing/async_js.go +61 -0
  139. package/stdlib/testing/benchmark.go +297 -0
  140. package/stdlib/testing/testing.go +523 -0
  141. package/stdlib/time/duration.go +233 -0
  142. package/stdlib/time/format.go +745 -0
  143. package/stdlib/time/parse.go +790 -0
  144. package/stdlib/time/sleep_js.go +63 -0
  145. package/stdlib/time/time.go +834 -0
  146. package/stdlib/unicode/unicode.go +250 -0
  147. package/stdlib/unicode/utf8/utf8.go +464 -0
  148. package/src/backend/js/stdlib/base64.js +0 -12
  149. package/src/backend/js/stdlib/binary.js +0 -102
  150. package/src/backend/js/stdlib/bits.js +0 -46
  151. package/src/backend/js/stdlib/builder.js +0 -63
  152. package/src/backend/js/stdlib/bytes.js +0 -122
  153. package/src/backend/js/stdlib/errors.js +0 -44
  154. package/src/backend/js/stdlib/fmt.js +0 -178
  155. package/src/backend/js/stdlib/gom.js +0 -155
  156. package/src/backend/js/stdlib/hex.js +0 -17
  157. package/src/backend/js/stdlib/html.js +0 -20
  158. package/src/backend/js/stdlib/io.js +0 -137
  159. package/src/backend/js/stdlib/json.js +0 -972
  160. package/src/backend/js/stdlib/maps.js +0 -45
  161. package/src/backend/js/stdlib/math.js +0 -69
  162. package/src/backend/js/stdlib/os.js +0 -13
  163. package/src/backend/js/stdlib/path.js +0 -48
  164. package/src/backend/js/stdlib/rand.js +0 -32
  165. package/src/backend/js/stdlib/regexp.js +0 -54
  166. package/src/backend/js/stdlib/sha256.js +0 -12
  167. package/src/backend/js/stdlib/shared.js +0 -17
  168. package/src/backend/js/stdlib/simd.js +0 -24
  169. package/src/backend/js/stdlib/slices.js +0 -77
  170. package/src/backend/js/stdlib/sort.js +0 -30
  171. package/src/backend/js/stdlib/strconv.js +0 -74
  172. package/src/backend/js/stdlib/strings.js +0 -222
  173. package/src/backend/js/stdlib/sync.js +0 -13
  174. package/src/backend/js/stdlib/testing.js +0 -34
  175. package/src/backend/js/stdlib/time.js +0 -128
  176. package/src/backend/js/stdlib/unicode.js +0 -25
  177. package/src/backend/js/stdlib/utf8.js +0 -26
  178. package/src/backend/wasm/const-fold.js +0 -205
  179. package/src/backend/wasm/emit-stdlib.js +0 -13
  180. package/src/backend/wasm/stdlib/bytes.js +0 -5039
  181. package/src/backend/wasm/stdlib/core.js +0 -8903
  182. package/src/backend/wasm/stdlib/io.js +0 -2161
  183. package/src/backend/wasm/stdlib/packages.js +0 -2400
  184. package/src/backend/wasm/stdlib/pure.js +0 -720
  185. package/src/backend/wasm/stdlib/time.js +0 -1200
  186. package/src/typechecker/stdlib/core.js +0 -801
  187. package/src/typechecker/stdlib/extended.js +0 -914
  188. package/src/typechecker/stdlib.js +0 -22
  189. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  190. /package/src/{colors.js → cli/colors.js} +0 -0
  191. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  192. /package/src/{minifier.js → cli/minifier.js} +0 -0
  193. /package/src/{project-config.js → cli/project-config.js} +0 -0
  194. /package/src/{pwa.js → cli/pwa.js} +0 -0
@@ -0,0 +1,209 @@
1
+ //go:build gofront
2
+
3
+ //gofront:target both
4
+
5
+ // Copyright 2023 The Go Authors. All rights reserved.
6
+ // Use of this source code is governed by a BSD-style
7
+ // license that can be found in the LICENSE file.
8
+
9
+ //go:generate go run $GOROOT/src/sort/gen_sort_variants.go -generic
10
+
11
+ package slices
12
+
13
+ import "cmp"
14
+
15
+ // Sort sorts a slice of any ordered type in ascending order.
16
+ // When sorting floating-point numbers, NaNs are ordered before other values.
17
+ //gofront:native slices.Sort
18
+ func Sort[S ~[]E, E cmp.Ordered](x S) {
19
+ n := len(x)
20
+ pdqsortOrdered(x, 0, n, bitsLen(uint(n)))
21
+ }
22
+
23
+ // SortFunc sorts the slice x in ascending order as determined by the cmp
24
+ // function. This sort is not guaranteed to be stable.
25
+ // cmp(a, b) should return a negative number when a < b, a positive number when
26
+ // a > b and zero when a == b or a and b are incomparable in the sense of
27
+ // a strict weak ordering.
28
+ //
29
+ // SortFunc requires that cmp is a strict weak ordering.
30
+ // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
31
+ // The function should return 0 for incomparable items.
32
+ func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
33
+ n := len(x)
34
+ pdqsortCmpFunc(x, 0, n, bitsLen(uint(n)), cmp)
35
+ }
36
+
37
+ // SortStableFunc sorts the slice x while keeping the original order of equal
38
+ // elements, using cmp to compare elements in the same way as [SortFunc].
39
+ func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
40
+ stableCmpFunc(x, len(x), cmp)
41
+ }
42
+
43
+ // IsSorted reports whether x is sorted in ascending order.
44
+ func IsSorted[S ~[]E, E cmp.Ordered](x S) bool {
45
+ for i := len(x) - 1; i > 0; i-- {
46
+ if cmp.Less(x[i], x[i-1]) {
47
+ return false
48
+ }
49
+ }
50
+ return true
51
+ }
52
+
53
+ // IsSortedFunc reports whether x is sorted in ascending order, with cmp as the
54
+ // comparison function as defined by [SortFunc].
55
+ func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool {
56
+ for i := len(x) - 1; i > 0; i-- {
57
+ if cmp(x[i], x[i-1]) < 0 {
58
+ return false
59
+ }
60
+ }
61
+ return true
62
+ }
63
+
64
+ // Min returns the minimal value in x. It panics if x is empty.
65
+ // For floating-point numbers, Min propagates NaNs (any NaN value in x
66
+ // forces the output to be NaN).
67
+ func Min[S ~[]E, E cmp.Ordered](x S) E {
68
+ if len(x) < 1 {
69
+ panic("slices.Min: empty list")
70
+ }
71
+ m := x[0]
72
+ for i := 1; i < len(x); i++ {
73
+ m = min(m, x[i])
74
+ }
75
+ return m
76
+ }
77
+
78
+ // MinFunc returns the minimal value in x, using cmp to compare elements.
79
+ // It panics if x is empty. If there is more than one minimal element
80
+ // according to the cmp function, MinFunc returns the first one.
81
+ func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
82
+ if len(x) < 1 {
83
+ panic("slices.MinFunc: empty list")
84
+ }
85
+ m := x[0]
86
+ for i := 1; i < len(x); i++ {
87
+ if cmp(x[i], m) < 0 {
88
+ m = x[i]
89
+ }
90
+ }
91
+ return m
92
+ }
93
+
94
+ // Max returns the maximal value in x. It panics if x is empty.
95
+ // For floating-point E, Max propagates NaNs (any NaN value in x
96
+ // forces the output to be NaN).
97
+ func Max[S ~[]E, E cmp.Ordered](x S) E {
98
+ if len(x) < 1 {
99
+ panic("slices.Max: empty list")
100
+ }
101
+ m := x[0]
102
+ for i := 1; i < len(x); i++ {
103
+ m = max(m, x[i])
104
+ }
105
+ return m
106
+ }
107
+
108
+ // MaxFunc returns the maximal value in x, using cmp to compare elements.
109
+ // It panics if x is empty. If there is more than one maximal element
110
+ // according to the cmp function, MaxFunc returns the first one.
111
+ func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
112
+ if len(x) < 1 {
113
+ panic("slices.MaxFunc: empty list")
114
+ }
115
+ m := x[0]
116
+ for i := 1; i < len(x); i++ {
117
+ if cmp(x[i], m) > 0 {
118
+ m = x[i]
119
+ }
120
+ }
121
+ return m
122
+ }
123
+
124
+ // BinarySearch searches for target in a sorted slice and returns the earliest
125
+ // position where target is found, or the position where target would appear
126
+ // in the sort order; it also returns a bool saying whether the target is
127
+ // really found in the slice. The slice must be sorted in increasing order.
128
+ func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool) {
129
+ // Inlining is faster than calling BinarySearchFunc with a lambda.
130
+ n := len(x)
131
+ // Define x[-1] < target and x[n] >= target.
132
+ // Invariant: x[i-1] < target, x[j] >= target.
133
+ i, j := 0, n
134
+ for i < j {
135
+ h := int(uint(i+j) >> 1) // avoid overflow when computing h
136
+ // i ≤ h < j
137
+ if cmp.Less(x[h], target) {
138
+ i = h + 1 // preserves x[i-1] < target
139
+ } else {
140
+ j = h // preserves x[j] >= target
141
+ }
142
+ }
143
+ // i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i.
144
+ return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target)))
145
+ }
146
+
147
+ // BinarySearchFunc works like [BinarySearch], but uses a custom comparison
148
+ // function. The slice must be sorted in increasing order, where "increasing"
149
+ // is defined by cmp. cmp should return 0 if the slice element matches
150
+ // the target, a negative number if the slice element precedes the target,
151
+ // or a positive number if the slice element follows the target.
152
+ // cmp must implement the same ordering as the slice, such that if
153
+ // cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice.
154
+ func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) {
155
+ n := len(x)
156
+ // Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
157
+ // Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
158
+ i, j := 0, n
159
+ for i < j {
160
+ h := int(uint(i+j) >> 1) // avoid overflow when computing h
161
+ // i ≤ h < j
162
+ if cmp(x[h], target) < 0 {
163
+ i = h + 1 // preserves cmp(x[i - 1], target) < 0
164
+ } else {
165
+ j = h // preserves cmp(x[j], target) >= 0
166
+ }
167
+ }
168
+ // i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i.
169
+ return i, i < n && cmp(x[i], target) == 0
170
+ }
171
+
172
+ type sortedHint int // hint for pdqsort when choosing the pivot
173
+
174
+ const (
175
+ unknownHint sortedHint = iota
176
+ increasingHint
177
+ decreasingHint
178
+ )
179
+
180
+ // xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
181
+ type xorshift uint64
182
+
183
+ func (r *xorshift) Next() uint64 {
184
+ *r ^= *r << 13
185
+ *r ^= *r >> 7
186
+ *r ^= *r << 17
187
+ return uint64(*r)
188
+ }
189
+
190
+ // bitsLen returns the minimum number of bits required to represent n; it
191
+ // is math/bits.Len for a uint.
192
+ func bitsLen(n uint) int {
193
+ var l int
194
+ for n > 0 {
195
+ l++
196
+ n >>= 1
197
+ }
198
+ return l
199
+ }
200
+
201
+ func nextPowerOfTwo(length int) uint {
202
+ return 1 << bitsLen(uint(length))
203
+ }
204
+
205
+ // isNaN reports whether x is a NaN without requiring the math package.
206
+ // This will always return false if T is not floating-point.
207
+ func isNaN[T cmp.Ordered](x T) bool {
208
+ return x != x
209
+ }
@@ -0,0 +1,480 @@
1
+ // Code generated by scripts/gen-pdq.js; DO NOT EDIT.
2
+
3
+ // Copyright 2022 The Go Authors. All rights reserved.
4
+ // Use of this source code is governed by a BSD-style
5
+ // license that can be found in the LICENSE file.
6
+
7
+ package slices
8
+
9
+ // insertionSortCmpFunc sorts data[a:b] using insertion sort.
10
+ func insertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
11
+ for i := a + 1; i < b; i++ {
12
+ for j := i; j > a && (cmp(data[j], data[j-1]) < 0); j-- {
13
+ data[j], data[j-1] = data[j-1], data[j]
14
+ }
15
+ }
16
+ }
17
+
18
+ // siftDownCmpFunc implements the heap property on data[lo:hi].
19
+ // first is an offset into the array where the root of the heap lies.
20
+ func siftDownCmpFunc[E any](data []E, lo, hi, first int, cmp func(a, b E) int) {
21
+ root := lo
22
+ for {
23
+ child := 2*root + 1
24
+ if child >= hi {
25
+ break
26
+ }
27
+ if child+1 < hi && (cmp(data[first+child], data[first+child+1]) < 0) {
28
+ child++
29
+ }
30
+ if !(cmp(data[first+root], data[first+child]) < 0) {
31
+ return
32
+ }
33
+ data[first+root], data[first+child] = data[first+child], data[first+root]
34
+ root = child
35
+ }
36
+ }
37
+
38
+ func heapSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
39
+ first := a
40
+ lo := 0
41
+ hi := b - a
42
+
43
+ // Build heap with greatest element at top.
44
+ for i := (hi - 1) / 2; i >= 0; i-- {
45
+ siftDownCmpFunc(data, i, hi, first, cmp)
46
+ }
47
+
48
+ // Pop elements, largest first, into end of data.
49
+ for i := hi - 1; i >= 0; i-- {
50
+ data[first], data[first+i] = data[first+i], data[first]
51
+ siftDownCmpFunc(data, lo, i, first, cmp)
52
+ }
53
+ }
54
+
55
+ // pdqsortCmpFunc sorts data[a:b].
56
+ // The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
57
+ // pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
58
+ // C++ implementation: https://github.com/orlp/pdqsort
59
+ // Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
60
+ // limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
61
+ func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) {
62
+ const maxInsertion = 12
63
+
64
+ var (
65
+ wasBalanced = true // whether the last partitioning was reasonably balanced
66
+ wasPartitioned = true // whether the slice was already partitioned
67
+ )
68
+
69
+ for {
70
+ length := b - a
71
+
72
+ if length <= maxInsertion {
73
+ insertionSortCmpFunc(data, a, b, cmp)
74
+ return
75
+ }
76
+
77
+ // Fall back to heapsort if too many bad choices were made.
78
+ if limit == 0 {
79
+ heapSortCmpFunc(data, a, b, cmp)
80
+ return
81
+ }
82
+
83
+ // If the last partitioning was imbalanced, we need to breaking patterns.
84
+ if !wasBalanced {
85
+ breakPatternsCmpFunc(data, a, b, cmp)
86
+ limit--
87
+ }
88
+
89
+ pivot, hint := choosePivotCmpFunc(data, a, b, cmp)
90
+ if hint == decreasingHint {
91
+ reverseRangeCmpFunc(data, a, b, cmp)
92
+ // The chosen pivot was pivot-a elements after the start of the array.
93
+ // After reversing it is pivot-a elements before the end of the array.
94
+ // The idea came from Rust's implementation.
95
+ pivot = (b - 1) - (pivot - a)
96
+ hint = increasingHint
97
+ }
98
+
99
+ // The slice is likely already sorted.
100
+ if wasBalanced && wasPartitioned && hint == increasingHint {
101
+ if partialInsertionSortCmpFunc(data, a, b, cmp) {
102
+ return
103
+ }
104
+ }
105
+
106
+ // Probably the slice contains many duplicate elements, partition the slice into
107
+ // elements equal to and elements greater than the pivot.
108
+ if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) {
109
+ mid := partitionEqualCmpFunc(data, a, b, pivot, cmp)
110
+ a = mid
111
+ continue
112
+ }
113
+
114
+ mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp)
115
+ wasPartitioned = alreadyPartitioned
116
+
117
+ leftLen, rightLen := mid-a, b-mid
118
+ balanceThreshold := length / 8
119
+ if leftLen < rightLen {
120
+ wasBalanced = leftLen >= balanceThreshold
121
+ pdqsortCmpFunc(data, a, mid, limit, cmp)
122
+ a = mid + 1
123
+ } else {
124
+ wasBalanced = rightLen >= balanceThreshold
125
+ pdqsortCmpFunc(data, mid+1, b, limit, cmp)
126
+ b = mid
127
+ }
128
+ }
129
+ }
130
+
131
+ // partitionCmpFunc does one quicksort partition.
132
+ // Let p = data[pivot]
133
+ // Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
134
+ // On return, data[newpivot] = p
135
+ func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) {
136
+ data[a], data[pivot] = data[pivot], data[a]
137
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
138
+
139
+ for i <= j && (cmp(data[i], data[a]) < 0) {
140
+ i++
141
+ }
142
+ for i <= j && !(cmp(data[j], data[a]) < 0) {
143
+ j--
144
+ }
145
+ if i > j {
146
+ data[j], data[a] = data[a], data[j]
147
+ return j, true
148
+ }
149
+ data[i], data[j] = data[j], data[i]
150
+ i++
151
+ j--
152
+
153
+ for {
154
+ for i <= j && (cmp(data[i], data[a]) < 0) {
155
+ i++
156
+ }
157
+ for i <= j && !(cmp(data[j], data[a]) < 0) {
158
+ j--
159
+ }
160
+ if i > j {
161
+ break
162
+ }
163
+ data[i], data[j] = data[j], data[i]
164
+ i++
165
+ j--
166
+ }
167
+ data[j], data[a] = data[a], data[j]
168
+ return j, false
169
+ }
170
+
171
+ // partitionEqualCmpFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
172
+ // It assumed that data[a:b] does not contain elements smaller than the data[pivot].
173
+ func partitionEqualCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) {
174
+ data[a], data[pivot] = data[pivot], data[a]
175
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
176
+
177
+ for {
178
+ for i <= j && !(cmp(data[a], data[i]) < 0) {
179
+ i++
180
+ }
181
+ for i <= j && (cmp(data[a], data[j]) < 0) {
182
+ j--
183
+ }
184
+ if i > j {
185
+ break
186
+ }
187
+ data[i], data[j] = data[j], data[i]
188
+ i++
189
+ j--
190
+ }
191
+ return i
192
+ }
193
+
194
+ // partialInsertionSortCmpFunc partially sorts a slice, returns true if the slice is sorted at the end.
195
+ func partialInsertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) bool {
196
+ const (
197
+ maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
198
+ shortestShifting = 50 // don't shift any elements on short arrays
199
+ )
200
+ i := a + 1
201
+ for j := 0; j < maxSteps; j++ {
202
+ for i < b && !(cmp(data[i], data[i-1]) < 0) {
203
+ i++
204
+ }
205
+
206
+ if i == b {
207
+ return true
208
+ }
209
+
210
+ if b-a < shortestShifting {
211
+ return false
212
+ }
213
+
214
+ data[i], data[i-1] = data[i-1], data[i]
215
+
216
+ // Shift the smaller one to the left.
217
+ if i-a >= 2 {
218
+ for j := i - 1; j >= 1; j-- {
219
+ if !(cmp(data[j], data[j-1]) < 0) {
220
+ break
221
+ }
222
+ data[j], data[j-1] = data[j-1], data[j]
223
+ }
224
+ }
225
+ // Shift the greater one to the right.
226
+ if b-i >= 2 {
227
+ for j := i + 1; j < b; j++ {
228
+ if !(cmp(data[j], data[j-1]) < 0) {
229
+ break
230
+ }
231
+ data[j], data[j-1] = data[j-1], data[j]
232
+ }
233
+ }
234
+ }
235
+ return false
236
+ }
237
+
238
+ // breakPatternsCmpFunc scatters some elements around in an attempt to break some patterns
239
+ // that might cause imbalanced partitions in quicksort.
240
+ func breakPatternsCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
241
+ length := b - a
242
+ if length >= 8 {
243
+ random := xorshift(length)
244
+ modulus := nextPowerOfTwo(length)
245
+
246
+ for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
247
+ // Masked as a uint64 first: Go's uint(random.Next()) would round a value above 2^53 on JS.
248
+ other := int(random.Next() & uint64(modulus-1))
249
+ if other >= length {
250
+ other -= length
251
+ }
252
+ data[idx], data[a+other] = data[a+other], data[idx]
253
+ }
254
+ }
255
+ }
256
+
257
+ // choosePivotCmpFunc chooses a pivot in data[a:b].
258
+ //
259
+ // [0,8): chooses a static pivot.
260
+ // [8,shortestNinther): uses the simple median-of-three method.
261
+ // [shortestNinther,∞): uses the Tukey ninther method.
262
+ func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) {
263
+ const (
264
+ shortestNinther = 50
265
+ maxSwaps = 4 * 3
266
+ )
267
+
268
+ l := b - a
269
+
270
+ var (
271
+ swaps int
272
+ i = a + l/4*1
273
+ j = a + l/4*2
274
+ k = a + l/4*3
275
+ )
276
+
277
+ if l >= 8 {
278
+ if l >= shortestNinther {
279
+ // Tukey ninther method, the idea came from Rust's implementation.
280
+ i = medianAdjacentCmpFunc(data, i, &swaps, cmp)
281
+ j = medianAdjacentCmpFunc(data, j, &swaps, cmp)
282
+ k = medianAdjacentCmpFunc(data, k, &swaps, cmp)
283
+ }
284
+ // Find the median among i, j, k and stores it into j.
285
+ j = medianCmpFunc(data, i, j, k, &swaps, cmp)
286
+ }
287
+
288
+ switch swaps {
289
+ case 0:
290
+ return j, increasingHint
291
+ case maxSwaps:
292
+ return j, decreasingHint
293
+ default:
294
+ return j, unknownHint
295
+ }
296
+ }
297
+
298
+ // order2CmpFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
299
+ func order2CmpFunc[E any](data []E, a, b int, swaps *int, cmp func(a, b E) int) (int, int) {
300
+ if cmp(data[b], data[a]) < 0 {
301
+ *swaps++
302
+ return b, a
303
+ }
304
+ return a, b
305
+ }
306
+
307
+ // medianCmpFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
308
+ func medianCmpFunc[E any](data []E, a, b, c int, swaps *int, cmp func(a, b E) int) int {
309
+ a, b = order2CmpFunc(data, a, b, swaps, cmp)
310
+ b, c = order2CmpFunc(data, b, c, swaps, cmp)
311
+ a, b = order2CmpFunc(data, a, b, swaps, cmp)
312
+ return b
313
+ }
314
+
315
+ // medianAdjacentCmpFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
316
+ func medianAdjacentCmpFunc[E any](data []E, a int, swaps *int, cmp func(a, b E) int) int {
317
+ return medianCmpFunc(data, a-1, a, a+1, swaps, cmp)
318
+ }
319
+
320
+ func reverseRangeCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
321
+ i := a
322
+ j := b - 1
323
+ for i < j {
324
+ data[i], data[j] = data[j], data[i]
325
+ i++
326
+ j--
327
+ }
328
+ }
329
+
330
+ func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) {
331
+ for i := 0; i < n; i++ {
332
+ data[a+i], data[b+i] = data[b+i], data[a+i]
333
+ }
334
+ }
335
+
336
+ func stableCmpFunc[E any](data []E, n int, cmp func(a, b E) int) {
337
+ blockSize := 20 // must be > 0
338
+ a, b := 0, blockSize
339
+ for b <= n {
340
+ insertionSortCmpFunc(data, a, b, cmp)
341
+ a = b
342
+ b += blockSize
343
+ }
344
+ insertionSortCmpFunc(data, a, n, cmp)
345
+
346
+ for blockSize < n {
347
+ a, b = 0, 2*blockSize
348
+ for b <= n {
349
+ symMergeCmpFunc(data, a, a+blockSize, b, cmp)
350
+ a = b
351
+ b += 2 * blockSize
352
+ }
353
+ if m := a + blockSize; m < n {
354
+ symMergeCmpFunc(data, a, m, n, cmp)
355
+ }
356
+ blockSize *= 2
357
+ }
358
+ }
359
+
360
+ // symMergeCmpFunc merges the two sorted subsequences data[a:m] and data[m:b] using
361
+ // the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
362
+ // Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
363
+ // Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
364
+ // Computer Science, pages 714-723. Springer, 2004.
365
+ //
366
+ // Let M = m-a and N = b-n. Wolog M < N.
367
+ // The recursion depth is bound by ceil(log(N+M)).
368
+ // The algorithm needs O(M*log(N/M + 1)) calls to data.Less.
369
+ // The algorithm needs O((M+N)*log(M)) calls to data.Swap.
370
+ //
371
+ // The paper gives O((M+N)*log(M)) as the number of assignments assuming a
372
+ // rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation
373
+ // in the paper carries through for Swap operations, especially as the block
374
+ // swapping rotate uses only O(M+N) Swaps.
375
+ //
376
+ // symMerge assumes non-degenerate arguments: a < m && m < b.
377
+ // Having the caller check this condition eliminates many leaf recursion calls,
378
+ // which improves performance.
379
+ func symMergeCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
380
+ // Avoid unnecessary recursions of symMerge
381
+ // by direct insertion of data[a] into data[m:b]
382
+ // if data[a:m] only contains one element.
383
+ if m-a == 1 {
384
+ // Use binary search to find the lowest index i
385
+ // such that data[i] >= data[a] for m <= i < b.
386
+ // Exit the search loop with i == b in case no such index exists.
387
+ i := m
388
+ j := b
389
+ for i < j {
390
+ h := int(uint(i+j) >> 1)
391
+ if cmp(data[h], data[a]) < 0 {
392
+ i = h + 1
393
+ } else {
394
+ j = h
395
+ }
396
+ }
397
+ // Swap values until data[a] reaches the position before i.
398
+ for k := a; k < i-1; k++ {
399
+ data[k], data[k+1] = data[k+1], data[k]
400
+ }
401
+ return
402
+ }
403
+
404
+ // Avoid unnecessary recursions of symMerge
405
+ // by direct insertion of data[m] into data[a:m]
406
+ // if data[m:b] only contains one element.
407
+ if b-m == 1 {
408
+ // Use binary search to find the lowest index i
409
+ // such that data[i] > data[m] for a <= i < m.
410
+ // Exit the search loop with i == m in case no such index exists.
411
+ i := a
412
+ j := m
413
+ for i < j {
414
+ h := int(uint(i+j) >> 1)
415
+ if !(cmp(data[m], data[h]) < 0) {
416
+ i = h + 1
417
+ } else {
418
+ j = h
419
+ }
420
+ }
421
+ // Swap values until data[m] reaches the position i.
422
+ for k := m; k > i; k-- {
423
+ data[k], data[k-1] = data[k-1], data[k]
424
+ }
425
+ return
426
+ }
427
+
428
+ mid := int(uint(a+b) >> 1)
429
+ n := mid + m
430
+ var start, r int
431
+ if m > mid {
432
+ start = n - b
433
+ r = mid
434
+ } else {
435
+ start = a
436
+ r = m
437
+ }
438
+ p := n - 1
439
+
440
+ for start < r {
441
+ c := int(uint(start+r) >> 1)
442
+ if !(cmp(data[p-c], data[c]) < 0) {
443
+ start = c + 1
444
+ } else {
445
+ r = c
446
+ }
447
+ }
448
+
449
+ end := n - start
450
+ if start < m && m < end {
451
+ rotateCmpFunc(data, start, m, end, cmp)
452
+ }
453
+ if a < start && start < mid {
454
+ symMergeCmpFunc(data, a, start, mid, cmp)
455
+ }
456
+ if mid < end && end < b {
457
+ symMergeCmpFunc(data, mid, end, b, cmp)
458
+ }
459
+ }
460
+
461
+ // rotateCmpFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
462
+ // Data of the form 'x u v y' is changed to 'x v u y'.
463
+ // rotate performs at most b-a many calls to data.Swap,
464
+ // and it assumes non-degenerate arguments: a < m && m < b.
465
+ func rotateCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
466
+ i := m - a
467
+ j := b - m
468
+
469
+ for i != j {
470
+ if i > j {
471
+ swapRangeCmpFunc(data, m-i, m, j, cmp)
472
+ i -= j
473
+ } else {
474
+ swapRangeCmpFunc(data, m-i, m+j-i, i, cmp)
475
+ j -= i
476
+ }
477
+ }
478
+ // i == j
479
+ swapRangeCmpFunc(data, m-i, m, i, cmp)
480
+ }