< internal/oserror, math/bits
< RUNTIME;
- RUNTIME
- < sort
+ # slices depends on unsafe for overlapping check, cmp for comparison
+ # semantics, and math/bits for # calculating bitlength of numbers.
+ unsafe, cmp, math/bits
+ < slices;
+
+ RUNTIME, slices
+ < sort;
+
+ sort
< container/heap;
RUNTIME
< hash
< hash/adler32, hash/crc32, hash/crc64, hash/fnv;
- # slices depends on unsafe for overlapping check, cmp for comparison
- # semantics, and math/bits for # calculating bitlength of numbers.
- unsafe, cmp, math/bits
- < slices;
-
# math/big
FMT, encoding/binary, math/rand
< math/big;
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package slices
+package slices_test
import (
"cmp"
"internal/race"
"internal/testenv"
"math"
+ . "slices"
"strings"
"testing"
)
}
-func TestRotate(t *testing.T) {
- const N = 10
- s := make([]int, 0, N)
- for n := 0; n < N; n++ {
- for r := 0; r < n; r++ {
- s = s[:0]
- for i := 0; i < n; i++ {
- s = append(s, i)
- }
- rotateLeft(s, r)
- for i := 0; i < n; i++ {
- if s[i] != (i+r)%n {
- t.Errorf("expected n=%d r=%d i:%d want:%d got:%d", n, r, i, (i+r)%n, s[i])
- }
- }
- }
- }
-}
-
func TestInsertGrowthRate(t *testing.T) {
b := make([]byte, 1)
maxCap := cap(b)
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package slices
+package slices_test
import (
"fmt"
- "math/rand"
- "sort"
- "strconv"
- "strings"
+ "slices"
"testing"
)
-// These benchmarks compare sorting a large slice of int with sort.Ints vs.
-// slices.Sort
-func makeRandomInts(n int) []int {
- rand.Seed(42)
- ints := make([]int, n)
- for i := 0; i < n; i++ {
- ints[i] = rand.Intn(n)
- }
- return ints
-}
-
-func makeSortedInts(n int) []int {
- ints := make([]int, n)
- for i := 0; i < n; i++ {
- ints[i] = i
- }
- return ints
-}
-
-func makeReversedInts(n int) []int {
- ints := make([]int, n)
- for i := 0; i < n; i++ {
- ints[i] = n - i
- }
- return ints
-}
-
-const N = 100_000
-
-func BenchmarkSortInts(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ints := makeRandomInts(N)
- b.StartTimer()
- sort.Ints(ints)
- }
-}
-
-func makeSortedStrings(n int) []string {
- x := make([]string, n)
- for i := 0; i < n; i++ {
- x[i] = strconv.Itoa(i)
- }
- Sort(x)
- return x
-}
-
-func BenchmarkSlicesSortInts(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ints := makeRandomInts(N)
- b.StartTimer()
- Sort(ints)
- }
-}
-
-func BenchmarkSlicesSortInts_Sorted(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ints := makeSortedInts(N)
- b.StartTimer()
- Sort(ints)
- }
-}
-
-func BenchmarkSlicesSortInts_Reversed(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ints := makeReversedInts(N)
- b.StartTimer()
- Sort(ints)
- }
-}
-
-func BenchmarkIntsAreSorted(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ints := makeSortedInts(N)
- b.StartTimer()
- sort.IntsAreSorted(ints)
- }
-}
-
-func BenchmarkIsSorted(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ints := makeSortedInts(N)
- b.StartTimer()
- IsSorted(ints)
- }
-}
-
-// Since we're benchmarking these sorts against each other, make sure that they
-// generate similar results.
-func TestIntSorts(t *testing.T) {
- ints := makeRandomInts(200)
- ints2 := Clone(ints)
-
- sort.Ints(ints)
- Sort(ints2)
-
- for i := range ints {
- if ints[i] != ints2[i] {
- t.Fatalf("ints2 mismatch at %d; %d != %d", i, ints[i], ints2[i])
- }
- }
-}
-
-// The following is a benchmark for sorting strings.
-
-// makeRandomStrings generates n random strings with alphabetic runes of
-// varying lengths.
-func makeRandomStrings(n int) []string {
- rand.Seed(42)
- var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
- ss := make([]string, n)
- for i := 0; i < n; i++ {
- var sb strings.Builder
- slen := 2 + rand.Intn(50)
- for j := 0; j < slen; j++ {
- sb.WriteRune(letters[rand.Intn(len(letters))])
- }
- ss[i] = sb.String()
- }
- return ss
-}
-
-func TestStringSorts(t *testing.T) {
- ss := makeRandomStrings(200)
- ss2 := Clone(ss)
-
- sort.Strings(ss)
- Sort(ss2)
-
- for i := range ss {
- if ss[i] != ss2[i] {
- t.Fatalf("ss2 mismatch at %d; %s != %s", i, ss[i], ss2[i])
- }
- }
-}
-
-func BenchmarkSortStrings(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ss := makeRandomStrings(N)
- b.StartTimer()
- sort.Strings(ss)
- }
-}
-
-func BenchmarkSortStrings_Sorted(b *testing.B) {
- ss := makeSortedStrings(N)
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- sort.Strings(ss)
- }
-}
-
-func BenchmarkSlicesSortStrings(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ss := makeRandomStrings(N)
- b.StartTimer()
- Sort(ss)
- }
-}
-
-func BenchmarkSlicesSortStrings_Sorted(b *testing.B) {
- ss := makeSortedStrings(N)
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- Sort(ss)
- }
-}
-
-// These benchmarks compare sorting a slice of structs with sort.Sort vs.
-// slices.SortFunc.
-type myStruct struct {
- a, b, c, d string
- n int
-}
-
-type myStructs []*myStruct
-
-func (s myStructs) Len() int { return len(s) }
-func (s myStructs) Less(i, j int) bool { return s[i].n < s[j].n }
-func (s myStructs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-func makeRandomStructs(n int) myStructs {
- rand.Seed(42)
- structs := make([]*myStruct, n)
- for i := 0; i < n; i++ {
- structs[i] = &myStruct{n: rand.Intn(n)}
- }
- return structs
-}
-
-func TestStructSorts(t *testing.T) {
- ss := makeRandomStructs(200)
- ss2 := make([]*myStruct, len(ss))
- for i := range ss {
- ss2[i] = &myStruct{n: ss[i].n}
- }
-
- sort.Sort(ss)
- SortFunc(ss2, func(a, b *myStruct) int { return a.n - b.n })
-
- for i := range ss {
- if *ss[i] != *ss2[i] {
- t.Fatalf("ints2 mismatch at %d; %v != %v", i, *ss[i], *ss2[i])
- }
- }
-}
-
-func BenchmarkSortStructs(b *testing.B) {
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ss := makeRandomStructs(N)
- b.StartTimer()
- sort.Sort(ss)
- }
-}
-
-func BenchmarkSortFuncStructs(b *testing.B) {
- cmpFunc := func(a, b *myStruct) int { return a.n - b.n }
- for i := 0; i < b.N; i++ {
- b.StopTimer()
- ss := makeRandomStructs(N)
- b.StartTimer()
- SortFunc(ss, cmpFunc)
- }
-}
-
func BenchmarkBinarySearchFloats(b *testing.B) {
for _, size := range []int{16, 32, 64, 128, 512, 1024} {
b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
needle := (floats[midpoint] + floats[midpoint+1]) / 2
b.ResetTimer()
for i := 0; i < b.N; i++ {
- BinarySearch(floats, needle)
+ slices.BinarySearch(floats, needle)
}
})
}
}
+type myStruct struct {
+ a, b, c, d string
+ n int
+}
+
func BenchmarkBinarySearchFuncStruct(b *testing.B) {
for _, size := range []int{16, 32, 64, 128, 512, 1024} {
b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
lessFunc := func(a, b *myStruct) int { return a.n - b.n }
b.ResetTimer()
for i := 0; i < b.N; i++ {
- BinarySearchFunc(structs, needle, lessFunc)
+ slices.BinarySearchFunc(structs, needle, lessFunc)
}
})
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package slices
+package slices_test
import (
"cmp"
"fmt"
"math"
"math/rand"
- "sort"
+ . "slices"
"strconv"
"strings"
"testing"
var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
var float64s = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8, 74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3}
-var float64sWithNaNs = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
var strs = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
func TestSortIntSlice(t *testing.T) {
}
}
-func TestSortFloat64SliceWithNaNs(t *testing.T) {
- data := float64sWithNaNs[:]
- data2 := Clone(data)
-
- Sort(data)
- sort.Float64s(data2)
-
- if !IsSorted(data) {
- t.Error("IsSorted indicates data isn't sorted")
- }
-
- // Compare for equality using cmp.Compare, which considers NaNs equal.
- if !EqualFunc(data, data2, func(a, b float64) bool { return cmp.Compare(a, b) == 0 }) {
- t.Errorf("mismatch between Sort and sort.Float64: got %v, want %v", data, data2)
- }
-}
-
func TestSortStringSlice(t *testing.T) {
data := Clone(strs[:])
Sort(data)
//
// The less function must satisfy the same requirements as
// the Interface type's Less method.
+//
+// Note: in many situations, the newer slices.SortFunc function is more
+// ergonomic and runs faster.
func Slice(x any, less func(i, j int) bool) {
rv := reflectlite.ValueOf(x)
swap := reflectlite.Swapper(x)
//
// The less function must satisfy the same requirements as
// the Interface type's Less method.
+//
+// Note: in many situations, the newer slices.SortStableFunc function is more
+// ergonomic and runs faster.
func SliceStable(x any, less func(i, j int) bool) {
rv := reflectlite.ValueOf(x)
swap := reflectlite.Swapper(x)
// SliceIsSorted reports whether the slice x is sorted according to the provided less function.
// It panics if x is not a slice.
+//
+// Note: in many situations, the newer slices.IsSortedFunc function is more
+// ergonomic and runs faster.
func SliceIsSorted(x any, less func(i, j int) bool) bool {
rv := reflectlite.ValueOf(x)
n := rv.Len()
// Ints sorts a slice of ints in increasing order.
//
-// Note: consider using the newer slices.Sort function, which runs faster.
-func Ints(x []int) { Sort(IntSlice(x)) }
+// Note: as of Go 1.22, this function simply calls slices.Sort.
+func Ints(x []int) { intsImpl(x) }
// Float64s sorts a slice of float64s in increasing order.
// Not-a-number (NaN) values are ordered before other values.
//
-// Note: consider using the newer slices.Sort function, which runs faster.
-func Float64s(x []float64) { Sort(Float64Slice(x)) }
+// Note: as of Go 1.22, this function simply calls slices.Sort.
+func Float64s(x []float64) { float64sImpl(x) }
// Strings sorts a slice of strings in increasing order.
//
-// Note: consider using the newer slices.Sort function, which runs faster.
-func Strings(x []string) { Sort(StringSlice(x)) }
+// Note: as of Go 1.22, this function simply calls slices.Sort.
+func Strings(x []string) { stringsImpl(x) }
// IntsAreSorted reports whether the slice x is sorted in increasing order.
//
-// Note: consider using the newer slices.IsSorted function, which runs faster.
-func IntsAreSorted(x []int) bool { return IsSorted(IntSlice(x)) }
+// Note: as of Go 1.22, this function simply calls slices.IsSorted.
+func IntsAreSorted(x []int) bool { return intsAreSortedImpl(x) }
// Float64sAreSorted reports whether the slice x is sorted in increasing order,
// with not-a-number (NaN) values before any other values.
//
-// Note: consider using the newer slices.IsSorted function, which runs faster.
-func Float64sAreSorted(x []float64) bool { return IsSorted(Float64Slice(x)) }
+// Note: as of Go 1.22, this function simply calls slices.IsSorted.
+func Float64sAreSorted(x []float64) bool { return float64sAreSortedImpl(x) }
// StringsAreSorted reports whether the slice x is sorted in increasing order.
//
-// Note: consider using the newer slices.IsSorted function, which runs faster.
-func StringsAreSorted(x []string) bool { return IsSorted(StringSlice(x)) }
+// Note: as of Go 1.22, this function simply calls slices.IsSorted.
+func StringsAreSorted(x []string) bool { return stringsAreSortedImpl(x) }
// Notes on stable sorting:
// The used algorithms are simple and provable correct on all input and use
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !go1.21
+
+package sort
+
+func intsImpl(x []int) { Sort(IntSlice(x)) }
+func float64sImpl(x []float64) { Sort(Float64Slice(x)) }
+func stringsImpl(x []string) { Sort(StringSlice(x)) }
+
+func intsAreSortedImpl(x []int) bool { return IsSorted(IntSlice(x)) }
+func float64sAreSortedImpl(x []float64) bool { return IsSorted(Float64Slice(x)) }
+func stringsAreSortedImpl(x []string) bool { return IsSorted(StringSlice(x)) }
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+// Starting with Go 1.21, we can leverage the new generic functions from the
+// slices package to implement some `sort` functions faster. However, until
+// the bootstrap compiler uses Go 1.21 or later, we keep a fallback version
+// in sort_impl_120.go that retains the old implementation.
+
+package sort
+
+import "slices"
+
+func intsImpl(x []int) { slices.Sort(x) }
+func float64sImpl(x []float64) { slices.Sort(x) }
+func stringsImpl(x []string) { slices.Sort(x) }
+
+func intsAreSortedImpl(x []int) bool { return slices.IsSorted(x) }
+func float64sAreSortedImpl(x []float64) bool { return slices.IsSorted(x) }
+func stringsAreSortedImpl(x []string) bool { return slices.IsSorted(x) }
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sort_test
+
+import (
+ "math/rand"
+ "slices"
+ . "sort"
+ "strconv"
+ stringspkg "strings"
+ "testing"
+)
+
+// Benchmarks comparing sorting from the slices package with functions from
+// the sort package (avoiding functions that are just forwarding to the slices
+// package).
+
+func makeRandomInts(n int) []int {
+ rand.Seed(42)
+ ints := make([]int, n)
+ for i := 0; i < n; i++ {
+ ints[i] = rand.Intn(n)
+ }
+ return ints
+}
+
+func makeSortedInts(n int) []int {
+ ints := make([]int, n)
+ for i := 0; i < n; i++ {
+ ints[i] = i
+ }
+ return ints
+}
+
+func makeReversedInts(n int) []int {
+ ints := make([]int, n)
+ for i := 0; i < n; i++ {
+ ints[i] = n - i
+ }
+ return ints
+}
+
+func makeSortedStrings(n int) []string {
+ x := make([]string, n)
+ for i := 0; i < n; i++ {
+ x[i] = strconv.Itoa(i)
+ }
+ Strings(x)
+ return x
+}
+
+const N = 100_000
+
+func BenchmarkSortInts(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ints := makeRandomInts(N)
+ b.StartTimer()
+ Sort(IntSlice(ints))
+ }
+}
+
+func BenchmarkSlicesSortInts(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ints := makeRandomInts(N)
+ b.StartTimer()
+ slices.Sort(ints)
+ }
+}
+
+func BenchmarkSortIsSorted(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ints := makeSortedInts(N)
+ b.StartTimer()
+ IsSorted(IntSlice(ints))
+ }
+}
+
+func BenchmarkSlicesIsSorted(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ints := makeSortedInts(N)
+ b.StartTimer()
+ slices.IsSorted(ints)
+ }
+}
+
+// makeRandomStrings generates n random strings with alphabetic runes of
+// varying lengths.
+func makeRandomStrings(n int) []string {
+ rand.Seed(42)
+ var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ ss := make([]string, n)
+ for i := 0; i < n; i++ {
+ var sb stringspkg.Builder
+ slen := 2 + rand.Intn(50)
+ for j := 0; j < slen; j++ {
+ sb.WriteRune(letters[rand.Intn(len(letters))])
+ }
+ ss[i] = sb.String()
+ }
+ return ss
+}
+
+func BenchmarkSortStrings(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ss := makeRandomStrings(N)
+ b.StartTimer()
+ Sort(StringSlice(ss))
+ }
+}
+
+func BenchmarkSlicesSortStrings(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ss := makeRandomStrings(N)
+ b.StartTimer()
+ slices.Sort(ss)
+ }
+}
+
+func BenchmarkSortStrings_Sorted(b *testing.B) {
+ ss := makeSortedStrings(N)
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ Sort(StringSlice(ss))
+ }
+}
+
+func BenchmarkSlicesSortStrings_Sorted(b *testing.B) {
+ ss := makeSortedStrings(N)
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ slices.Sort(ss)
+ }
+}
+
+// These benchmarks compare sorting a slice of structs with sort.Sort vs.
+// slices.SortFunc.
+type myStruct struct {
+ a, b, c, d string
+ n int
+}
+
+type myStructs []*myStruct
+
+func (s myStructs) Len() int { return len(s) }
+func (s myStructs) Less(i, j int) bool { return s[i].n < s[j].n }
+func (s myStructs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func makeRandomStructs(n int) myStructs {
+ rand.Seed(42)
+ structs := make([]*myStruct, n)
+ for i := 0; i < n; i++ {
+ structs[i] = &myStruct{n: rand.Intn(n)}
+ }
+ return structs
+}
+
+func TestStructSorts(t *testing.T) {
+ ss := makeRandomStructs(200)
+ ss2 := make([]*myStruct, len(ss))
+ for i := range ss {
+ ss2[i] = &myStruct{n: ss[i].n}
+ }
+
+ Sort(ss)
+ slices.SortFunc(ss2, func(a, b *myStruct) int { return a.n - b.n })
+
+ for i := range ss {
+ if *ss[i] != *ss2[i] {
+ t.Fatalf("ints2 mismatch at %d; %v != %v", i, *ss[i], *ss2[i])
+ }
+ }
+}
+
+func BenchmarkSortStructs(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ss := makeRandomStructs(N)
+ b.StartTimer()
+ Sort(ss)
+ }
+}
+
+func BenchmarkSortFuncStructs(b *testing.B) {
+ cmpFunc := func(a, b *myStruct) int { return a.n - b.n }
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ ss := makeRandomStructs(N)
+ b.StartTimer()
+ slices.SortFunc(ss, cmpFunc)
+ }
+}
package sort_test
import (
+ "cmp"
"fmt"
"internal/testenv"
"math"
"math/rand"
+ "slices"
. "sort"
"strconv"
stringspkg "strings"
}
}
+// Compare Sort with slices.Sort sorting a float64 slice containing NaNs.
+func TestSortFloat64sCompareSlicesSort(t *testing.T) {
+ slice1 := slices.Clone(float64s[:])
+ slice2 := slices.Clone(float64s[:])
+
+ Sort(Float64Slice(slice1))
+ slices.Sort(slice2)
+
+ // Compare for equality using cmp.Compare, which considers NaNs equal.
+ if !slices.EqualFunc(slice1, slice1, func(a, b float64) bool { return cmp.Compare(a, b) == 0 }) {
+ t.Errorf("mismatch between Sort and slices.Sort: got %v, want %v", slice1, slice2)
+ }
+}
+
func TestSortStringSlice(t *testing.T) {
data := strings
a := StringSlice(data[0:])