From d1d5a8438767675bde5f436405dff3242812e928 Mon Sep 17 00:00:00 2001 From: Stephan Renatus Date: Wed, 5 Nov 2025 09:14:44 +0100 Subject: [PATCH] topdown: fix split(..., "") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/open-policy-agent/opa/issues/8018 goos: darwin goarch: arm64 pkg: github.com/open-policy-agent/opa/v1/topdown cpu: Apple M4 Max │ main.bench │ pr.bench │ │ sec/op │ sec/op vs base │ Split-16 238.7n ± 1% 254.2n ± 1% +6.47% (p=0.000 n=10) │ main.bench │ pr.bench │ │ B/op │ B/op vs base │ Split-16 384.0 ± 0% 384.0 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal │ main.bench │ pr.bench │ │ allocs/op │ allocs/op vs base │ Split-16 14.00 ± 0% 14.00 ± 0% ~ (p=1.000 n=10) ¹ ¹ all samples are equal Signed-off-by: Stephan Renatus --- .../v1/strings/test-strings-0903.yaml | 20 ++++++++++++++++++- v1/util/performance.go | 13 +++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml b/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml index 4bd0d3e44c..62e729bef0 100644 --- a/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml +++ b/v1/test/cases/testdata/v1/strings/test-strings-0903.yaml @@ -1,6 +1,6 @@ --- cases: - - note: "strings/split: empty string" + - note: "strings/split: empty text string" query: data.generated.p = x modules: - | @@ -12,3 +12,21 @@ cases: data: {} want_result: - x: "" + - note: "strings/split: empty delim string" + query: data.test.p = x + modules: + - | + package test + + p := split("test", "") + want_result: + - x: [t, e, s, t] + - note: "strings/split: empty text and delim string" + query: data.test.p = x + modules: + - | + package test + + p := split("", "") + want_result: + - x: [] diff --git a/v1/util/performance.go b/v1/util/performance.go index 09a8e74011..c7bd57ea04 100644 --- a/v1/util/performance.go +++ b/v1/util/performance.go @@ -79,17 +79,10 @@ func KeysCount[K comparable, V any](m map[K]V, p func(K) bool) int { // SplitMap calls fn for each delim-separated part of text and returns a slice of the results. // Cheaper than calling fn on strings.Split(text, delim), as it avoids allocating an intermediate slice of strings. func SplitMap[T any](text string, delim string, fn func(string) T) []T { - before, after, found := strings.Cut(text, delim) - if !found { - return []T{fn(text)} + sl := make([]T, 0, strings.Count(text, delim)+1) + for s := range strings.SplitSeq(text, delim) { + sl = append(sl, fn(s)) } - - sl := append(make([]T, 0, strings.Count(text, delim)+1), fn(before)) - for found { - before, after, found = strings.Cut(after, delim) - sl = append(sl, fn(before)) - } - return sl }