1
0
mirror of https://github.com/golang/go synced 2024-11-11 19:21:37 -07:00

slices: add Concat

Fixes #56353

Change-Id: I985e1553e7b02237403b833e96fb5ceec890f5b8
GitHub-Last-Rev: 96a35e524c
GitHub-Pull-Request: golang/go#60929
Reviewed-on: https://go-review.googlesource.com/c/go/+/504882
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Carl Johnson 2023-08-08 01:14:22 +00:00 committed by Gopher Robot
parent 24f83ed4e2
commit 2c95fa4f31
3 changed files with 115 additions and 0 deletions

1
api/next/56353.txt Normal file
View File

@ -0,0 +1 @@
pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353

View File

@ -493,3 +493,19 @@ func Reverse[S ~[]E, E any](s S) {
s[i], s[j] = s[j], s[i]
}
}
// Concat returns a new slice concatenating the passed in slices.
func Concat[S ~[]E, E any](slices ...S) S {
size := 0
for _, s := range slices {
size += len(s)
if size < 0 {
panic("len out of range")
}
}
newslice := Grow[S](nil, size)
for _, s := range slices {
newslice = append(newslice, s...)
}
return newslice
}

View File

@ -1055,3 +1055,101 @@ func TestInference(t *testing.T) {
t.Errorf("Reverse(%v) = %v, want %v", S{4, 5, 6}, s2, want)
}
}
func TestConcat(t *testing.T) {
cases := []struct {
s [][]int
want []int
}{
{
s: [][]int{nil},
want: nil,
},
{
s: [][]int{{1}},
want: []int{1},
},
{
s: [][]int{{1}, {2}},
want: []int{1, 2},
},
{
s: [][]int{{1}, nil, {2}},
want: []int{1, 2},
},
}
for _, tc := range cases {
got := Concat(tc.s...)
if !Equal(tc.want, got) {
t.Errorf("Concat(%v) = %v, want %v", tc.s, got, tc.want)
}
var sink []int
allocs := testing.AllocsPerRun(5, func() {
sink = Concat(tc.s...)
})
_ = sink
if allocs > 1 {
errorf := t.Errorf
if testenv.OptimizationOff() || race.Enabled {
errorf = t.Logf
}
errorf("Concat(%v) allocated %v times; want 1", tc.s, allocs)
}
}
}
func TestConcat_too_large(t *testing.T) {
// Use zero length element to minimize memory in testing
type void struct{}
cases := []struct {
lengths []int
shouldPanic bool
}{
{
lengths: []int{0, 0},
shouldPanic: false,
},
{
lengths: []int{math.MaxInt, 0},
shouldPanic: false,
},
{
lengths: []int{0, math.MaxInt},
shouldPanic: false,
},
{
lengths: []int{math.MaxInt - 1, 1},
shouldPanic: false,
},
{
lengths: []int{math.MaxInt - 1, 1, 1},
shouldPanic: true,
},
{
lengths: []int{math.MaxInt, 1},
shouldPanic: true,
},
{
lengths: []int{math.MaxInt, math.MaxInt},
shouldPanic: true,
},
}
for _, tc := range cases {
var r any
ss := make([][]void, 0, len(tc.lengths))
for _, l := range tc.lengths {
s := make([]void, l)
ss = append(ss, s)
}
func() {
defer func() {
r = recover()
}()
_ = Concat(ss...)
}()
if didPanic := r != nil; didPanic != tc.shouldPanic {
t.Errorf("slices.Concat(lens(%v)) got panic == %v",
tc.lengths, didPanic)
}
}
}