]> Cypherpunks repositories - gostls13.git/commitdiff
slices: add func Repeat
authorJes Cok <xigua67damn@gmail.com>
Fri, 15 Mar 2024 05:47:53 +0000 (13:47 +0800)
committerKeith Randall <khr@golang.org>
Tue, 19 Mar 2024 21:38:37 +0000 (21:38 +0000)
Fixes #65238

Change-Id: I32ae4d922788cc6fbbe80f5b558a075951e3c892
Reviewed-on: https://go-review.googlesource.com/c/go/+/571895
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

api/next/65238.txt [new file with mode: 0644]
doc/next/6-stdlib/99-minor/slices/65238.md [new file with mode: 0644]
src/slices/slices.go
src/slices/slices_test.go

diff --git a/api/next/65238.txt b/api/next/65238.txt
new file mode 100644 (file)
index 0000000..e04939e
--- /dev/null
@@ -0,0 +1 @@
+pkg slices, func Repeat[$0 interface{ ~[]$1 }, $1 interface{}]($0, int) $0 #65238
diff --git a/doc/next/6-stdlib/99-minor/slices/65238.md b/doc/next/6-stdlib/99-minor/slices/65238.md
new file mode 100644 (file)
index 0000000..9204eb5
--- /dev/null
@@ -0,0 +1,2 @@
+The [`Repeat`](/pkg/slices#Repeat) function returns a new slice
+that repeats the provided slice the given number of times.
index 326584064c0c1c9db419fa89a564cd92af521a45..271e8cb32559270ba1d4877942383c8e03db64bb 100644 (file)
@@ -7,6 +7,7 @@ package slices
 
 import (
        "cmp"
+       "math/bits"
        "unsafe"
 )
 
@@ -474,3 +475,26 @@ func Concat[S ~[]E, E any](slices ...S) S {
        }
        return newslice
 }
+
+// Repeat returns a new slice that repeats the provided slice the given number of times.
+// The result has length and capacity len(x) * count.
+// The result is never nil.
+// Repeat panics if count is negative or if the result of (len(x) * count)
+// overflows.
+func Repeat[S ~[]E, E any](x S, count int) S {
+       if count < 0 {
+               panic("cannot be negative")
+       }
+
+       const maxInt = ^uint(0) >> 1
+       if hi, lo := bits.Mul(uint(len(x)), uint(count)); hi > 0 || lo > maxInt {
+               panic("the result of (len(x) * count) overflows")
+       }
+
+       newslice := make(S, len(x)*count)
+       n := copy(newslice, x)
+       for n < len(newslice) {
+               n += copy(newslice[n:], newslice[:n])
+       }
+       return newslice
+}
index 4b5f0355df2af713a11f34e3d546a6117308050e..55de2f57d06db19c2bef7bd3279a303b330121ab 100644 (file)
@@ -1335,3 +1335,77 @@ func TestConcat_too_large(t *testing.T) {
                }
        }
 }
+
+func TestRepeat(t *testing.T) {
+       // normal cases
+       for _, tc := range []struct {
+               x     []int
+               count int
+               want  []int
+       }{
+               {x: []int(nil), count: 0, want: []int{}},
+               {x: []int(nil), count: 1, want: []int{}},
+               {x: []int(nil), count: math.MaxInt, want: []int{}},
+               {x: []int{}, count: 0, want: []int{}},
+               {x: []int{}, count: 1, want: []int{}},
+               {x: []int{}, count: math.MaxInt, want: []int{}},
+               {x: []int{0}, count: 0, want: []int{}},
+               {x: []int{0}, count: 1, want: []int{0}},
+               {x: []int{0}, count: 2, want: []int{0, 0}},
+               {x: []int{0}, count: 3, want: []int{0, 0, 0}},
+               {x: []int{0}, count: 4, want: []int{0, 0, 0, 0}},
+               {x: []int{0, 1}, count: 0, want: []int{}},
+               {x: []int{0, 1}, count: 1, want: []int{0, 1}},
+               {x: []int{0, 1}, count: 2, want: []int{0, 1, 0, 1}},
+               {x: []int{0, 1}, count: 3, want: []int{0, 1, 0, 1, 0, 1}},
+               {x: []int{0, 1}, count: 4, want: []int{0, 1, 0, 1, 0, 1, 0, 1}},
+               {x: []int{0, 1, 2}, count: 0, want: []int{}},
+               {x: []int{0, 1, 2}, count: 1, want: []int{0, 1, 2}},
+               {x: []int{0, 1, 2}, count: 2, want: []int{0, 1, 2, 0, 1, 2}},
+               {x: []int{0, 1, 2}, count: 3, want: []int{0, 1, 2, 0, 1, 2, 0, 1, 2}},
+               {x: []int{0, 1, 2}, count: 4, want: []int{0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2}},
+       } {
+               if got := Repeat(tc.x, tc.count); got == nil || cap(got) != cap(tc.want) || !Equal(got, tc.want) {
+                       t.Errorf("Repeat(%v, %v): got: %v, want: %v, (got == nil): %v, cap(got): %v, cap(want): %v",
+                               tc.x, tc.count, got, tc.want, got == nil, cap(got), cap(tc.want))
+               }
+       }
+
+       // big slices
+       for _, tc := range []struct {
+               x     []struct{}
+               count int
+               want  []struct{}
+       }{
+               {x: make([]struct{}, math.MaxInt/1-0), count: 1, want: make([]struct{}, 1*(math.MaxInt/1-0))},
+               {x: make([]struct{}, math.MaxInt/2-1), count: 2, want: make([]struct{}, 2*(math.MaxInt/2-1))},
+               {x: make([]struct{}, math.MaxInt/3-2), count: 3, want: make([]struct{}, 3*(math.MaxInt/3-2))},
+               {x: make([]struct{}, math.MaxInt/4-3), count: 4, want: make([]struct{}, 4*(math.MaxInt/4-3))},
+               {x: make([]struct{}, math.MaxInt/5-4), count: 5, want: make([]struct{}, 5*(math.MaxInt/5-4))},
+               {x: make([]struct{}, math.MaxInt/6-5), count: 6, want: make([]struct{}, 6*(math.MaxInt/6-5))},
+               {x: make([]struct{}, math.MaxInt/7-6), count: 7, want: make([]struct{}, 7*(math.MaxInt/7-6))},
+               {x: make([]struct{}, math.MaxInt/8-7), count: 8, want: make([]struct{}, 8*(math.MaxInt/8-7))},
+               {x: make([]struct{}, math.MaxInt/9-8), count: 9, want: make([]struct{}, 9*(math.MaxInt/9-8))},
+       } {
+               if got := Repeat(tc.x, tc.count); got == nil || len(got) != len(tc.want) || cap(got) != cap(tc.want) {
+                       t.Errorf("Repeat(make([]struct{}, %v), %v): (got == nil): %v, len(got): %v, len(want): %v, cap(got): %v, cap(want): %v",
+                               len(tc.x), tc.count, got == nil, len(got), len(tc.want), cap(got), cap(tc.want))
+               }
+       }
+}
+
+func TestRepeatPanics(t *testing.T) {
+       for _, test := range []struct {
+               name  string
+               x     []struct{}
+               count int
+       }{
+               {name: "cannot be negative", x: make([]struct{}, 0), count: -1},
+               {name: "the result of (len(x) * count) overflows, hi > 0", x: make([]struct{}, 3), count: math.MaxInt},
+               {name: "the result of (len(x) * count) overflows, lo > maxInt", x: make([]struct{}, 2), count: 1 + math.MaxInt/2},
+       } {
+               if !panics(func() { _ = Repeat(test.x, test.count) }) {
+                       t.Errorf("Repeat %s: got no panic, want panic", test.name)
+               }
+       }
+}