Skip to content

Commit

Permalink
chore: add new Concat method from go1.22 source (to be replaced when …
Browse files Browse the repository at this point in the history
…we move to 1.22+ in the future)

chore: update a couple of examples where Concat simplifies our code significantly
  • Loading branch information
superlinkx committed Jan 29, 2024
1 parent fb664f7 commit ae2d39d
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 25 deletions.
11 changes: 2 additions & 9 deletions packages/go/analysis/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,10 @@ func ClearSystemTags(ctx context.Context, db graph.Database) error {

func ValidKinds() []graph.Kind {
var (
lenCalc = len(ad.Nodes()) + len(ad.Relationships()) + len(azure.NodeKinds()) + len(azure.Relationships())
kinds = make([]graph.Kind, 0, lenCalc)
metaKinds = []graph.Kind{metaKind, metaDetailKind}
)

kinds = append(kinds, ad.Nodes()...)
kinds = append(kinds, ad.Relationships()...)
kinds = append(kinds, azure.NodeKinds()...)
kinds = append(kinds, azure.Relationships()...)
kinds = append(kinds, metaKind, metaDetailKind)

return kinds
return slicesext.Concat(ad.Nodes(), ad.Relationships(), azure.NodeKinds(), azure.Relationships(), metaKinds)
}

func ParseKind(rawKind string) (graph.Kind, error) {
Expand Down
13 changes: 2 additions & 11 deletions packages/go/analysis/analysis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/specterops/bloodhound/dawgs/graph"
"github.com/specterops/bloodhound/graphschema/ad"
"github.com/specterops/bloodhound/graphschema/azure"
slicesext "github.com/specterops/bloodhound/slicesext"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -46,17 +47,7 @@ func (s kindStr) Is(others ...graph.Kind) bool {
}

func validKinds() graph.Kinds {
var (
lenCalc = len(ad.NodeKinds()) + len(ad.Relationships()) + len(azure.NodeKinds()) + len(azure.Relationships())
kinds = make(graph.Kinds, 0, lenCalc)
)

kinds = append(kinds, ad.NodeKinds()...)
kinds = append(kinds, ad.Relationships()...)
kinds = append(kinds, azure.NodeKinds()...)
kinds = append(kinds, azure.Relationships()...)

return kinds
return slicesext.Concat(ad.NodeKinds(), ad.Relationships(), azure.NodeKinds(), azure.Relationships())
}

func validKindStrings() []string {
Expand Down
20 changes: 20 additions & 0 deletions packages/go/slicesext/slicesext.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
// Package slicesext extends the standard library slices package with additional slice utilities
package slicesext

import "slices"

// Filter applies a predicate function over each element in a given slice and returns a new slice containing only the elements in which the predicate returns true
func Filter[T any](slice []T, fn func(T) bool) []T {
var out []T
Expand Down Expand Up @@ -115,3 +117,21 @@ func Last[T any](list []T) T {
func Init[T any](list []T) []T {
return list[:len(list)-1]
}

// Concat returns a new slice concatenating the passed in slices.
// This was ripped from go1.22 source and should be replaced with the stdlib
// implementation when we move to 1.22
func Concat[S ~[]E, E any](s ...S) S {
size := 0
for _, slice := range s {
size += len(slice)
if size < 0 {
panic("len out of range")
}
}
newslice := slices.Grow[S](nil, size)
for _, s := range s {
newslice = append(newslice, s...)
}
return newslice
}
103 changes: 98 additions & 5 deletions packages/go/slicesext/slicesext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package slicesext_test

import (
"fmt"
"math"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -67,11 +68,6 @@ func TestUnique(t *testing.T) {
require.Equal(t, []int{1, 2, 3}, slicesext.Unique([]int{1, 1, 2, 2, 3}))
}

func TestContains(t *testing.T) {
require.True(t, slices.Contains([]string{"a", "b", "c"}, "c"))
require.False(t, slices.Contains([]string{"a", "b", "c"}, "d"))
}

func BenchmarkHead(b *testing.B) {
for i := 10; i < 1000000; i = i * 10 {
list := make([]int, i)
Expand Down Expand Up @@ -126,6 +122,103 @@ func BenchmarkTail(b *testing.B) {
}
}

// Ripped from go1.22 source, can be removed when we can switch to stdlib
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 := slicesext.Concat(tc.s...)
if !slices.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 = slicesext.Concat(tc.s...)
})
_ = sink
if allocs > 1 {
errorf := t.Errorf
errorf("Concat(%v) allocated %v times; want 1", tc.s, allocs)
}
}
}

// Ripped from go1.22 source, can be removed when we can switch to stdlib
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()
}()
_ = slicesext.Concat(ss...)
}()
if didPanic := r != nil; didPanic != tc.shouldPanic {
t.Errorf("slices.Concat(lens(%v)) got panic == %v",
tc.lengths, didPanic)
}
}
}

func abs(n int) uint {
mask := n >> (strconv.IntSize - 1)
return uint((n ^ mask) - mask)
Expand Down

0 comments on commit ae2d39d

Please sign in to comment.