From aa608f70320e8047db9d5f39353fef610cb76798 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Wed, 21 Jun 2023 17:31:01 -0400 Subject: [PATCH 1/3] slices: Add Concat --- api/next/56353.txt | 1 + src/slices/slices.go | 16 +++++++ src/slices/slices_test.go | 94 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 api/next/56353.txt diff --git a/api/next/56353.txt b/api/next/56353.txt new file mode 100644 index 00000000000000..c2504a7f63abd0 --- /dev/null +++ b/api/next/56353.txt @@ -0,0 +1 @@ +pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353 diff --git a/src/slices/slices.go b/src/slices/slices.go index 725d91d8f5c151..900c309e4818ba 100644 --- a/src/slices/slices.go +++ b/src/slices/slices.go @@ -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 := make(S, 0, size) + for _, s := range slices { + newslice = append(newslice, s...) + } + return newslice +} diff --git a/src/slices/slices_test.go b/src/slices/slices_test.go index 8ea93c66d76686..a6839b95dc46cf 100644 --- a/src/slices/slices_test.go +++ b/src/slices/slices_test.go @@ -1055,3 +1055,97 @@ 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 { + t.Fatalf("Concat(%v) had %f allocs; expected 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) + } + } +} From e98968aeb055acae10beb95a687c4100ef6ca0dd Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Mon, 7 Aug 2023 17:29:45 -0400 Subject: [PATCH 2/3] requested change --- src/slices/slices.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slices/slices.go b/src/slices/slices.go index 900c309e4818ba..a4d9f7e3f5ff3d 100644 --- a/src/slices/slices.go +++ b/src/slices/slices.go @@ -503,7 +503,7 @@ func Concat[S ~[]E, E any](slices ...S) S { panic("len out of range") } } - newslice := make(S, 0, size) + newslice := Grow[S](nil, size) for _, s := range slices { newslice = append(newslice, s...) } From 96a35e524c168e5004c5cd28e693437462218eeb Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Mon, 7 Aug 2023 21:11:31 -0400 Subject: [PATCH 3/3] fix test --- src/slices/slices_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slices/slices_test.go b/src/slices/slices_test.go index a6839b95dc46cf..ac779f5bd955db 100644 --- a/src/slices/slices_test.go +++ b/src/slices/slices_test.go @@ -1089,7 +1089,11 @@ func TestConcat(t *testing.T) { }) _ = sink if allocs > 1 { - t.Fatalf("Concat(%v) had %f allocs; expected 1", tc.s, allocs) + errorf := t.Errorf + if testenv.OptimizationOff() || race.Enabled { + errorf = t.Logf + } + errorf("Concat(%v) allocated %v times; want 1", tc.s, allocs) } } }