Skip to content

Commit 0ac2911

Browse files
earthboundkideric
authored andcommitted
slices: add Concat
Fixes golang#56353 Change-Id: I985e1553e7b02237403b833e96fb5ceec890f5b8 GitHub-Last-Rev: 96a35e5 GitHub-Pull-Request: golang#60929 Reviewed-on: https://go-review.googlesource.com/c/go/+/504882 Auto-Submit: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 7ecdc06 commit 0ac2911

File tree

3 files changed

+115
-0
lines changed

3 files changed

+115
-0
lines changed

api/next/56353.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353

src/slices/slices.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,3 +496,19 @@ func Reverse[S ~[]E, E any](s S) {
496496
s[i], s[j] = s[j], s[i]
497497
}
498498
}
499+
500+
// Concat returns a new slice concatenating the passed in slices.
501+
func Concat[S ~[]E, E any](slices ...S) S {
502+
size := 0
503+
for _, s := range slices {
504+
size += len(s)
505+
if size < 0 {
506+
panic("len out of range")
507+
}
508+
}
509+
newslice := Grow[S](nil, size)
510+
for _, s := range slices {
511+
newslice = append(newslice, s...)
512+
}
513+
return newslice
514+
}

src/slices/slices_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,101 @@ func TestInference(t *testing.T) {
10731073
t.Errorf("Reverse(%v) = %v, want %v", S{4, 5, 6}, s2, want)
10741074
}
10751075
}
1076+
1077+
func TestConcat(t *testing.T) {
1078+
cases := []struct {
1079+
s [][]int
1080+
want []int
1081+
}{
1082+
{
1083+
s: [][]int{nil},
1084+
want: nil,
1085+
},
1086+
{
1087+
s: [][]int{{1}},
1088+
want: []int{1},
1089+
},
1090+
{
1091+
s: [][]int{{1}, {2}},
1092+
want: []int{1, 2},
1093+
},
1094+
{
1095+
s: [][]int{{1}, nil, {2}},
1096+
want: []int{1, 2},
1097+
},
1098+
}
1099+
for _, tc := range cases {
1100+
got := Concat(tc.s...)
1101+
if !Equal(tc.want, got) {
1102+
t.Errorf("Concat(%v) = %v, want %v", tc.s, got, tc.want)
1103+
}
1104+
var sink []int
1105+
allocs := testing.AllocsPerRun(5, func() {
1106+
sink = Concat(tc.s...)
1107+
})
1108+
_ = sink
1109+
if allocs > 1 {
1110+
errorf := t.Errorf
1111+
if testenv.OptimizationOff() || race.Enabled {
1112+
errorf = t.Logf
1113+
}
1114+
errorf("Concat(%v) allocated %v times; want 1", tc.s, allocs)
1115+
}
1116+
}
1117+
}
1118+
1119+
func TestConcat_too_large(t *testing.T) {
1120+
// Use zero length element to minimize memory in testing
1121+
type void struct{}
1122+
cases := []struct {
1123+
lengths []int
1124+
shouldPanic bool
1125+
}{
1126+
{
1127+
lengths: []int{0, 0},
1128+
shouldPanic: false,
1129+
},
1130+
{
1131+
lengths: []int{math.MaxInt, 0},
1132+
shouldPanic: false,
1133+
},
1134+
{
1135+
lengths: []int{0, math.MaxInt},
1136+
shouldPanic: false,
1137+
},
1138+
{
1139+
lengths: []int{math.MaxInt - 1, 1},
1140+
shouldPanic: false,
1141+
},
1142+
{
1143+
lengths: []int{math.MaxInt - 1, 1, 1},
1144+
shouldPanic: true,
1145+
},
1146+
{
1147+
lengths: []int{math.MaxInt, 1},
1148+
shouldPanic: true,
1149+
},
1150+
{
1151+
lengths: []int{math.MaxInt, math.MaxInt},
1152+
shouldPanic: true,
1153+
},
1154+
}
1155+
for _, tc := range cases {
1156+
var r any
1157+
ss := make([][]void, 0, len(tc.lengths))
1158+
for _, l := range tc.lengths {
1159+
s := make([]void, l)
1160+
ss = append(ss, s)
1161+
}
1162+
func() {
1163+
defer func() {
1164+
r = recover()
1165+
}()
1166+
_ = Concat(ss...)
1167+
}()
1168+
if didPanic := r != nil; didPanic != tc.shouldPanic {
1169+
t.Errorf("slices.Concat(lens(%v)) got panic == %v",
1170+
tc.lengths, didPanic)
1171+
}
1172+
}
1173+
}

0 commit comments

Comments
 (0)