From ea8882bf26b2a84f4c1880f36126fef40baf4d7e Mon Sep 17 00:00:00 2001 From: sususu Date: Sat, 8 Aug 2026 10:53:58 +0800 Subject: [PATCH] Add a no-copy GJSON parse helper gjson.ParseBytes copies the entire document before returning a result, which is prohibitive for multi-megabyte requests. ParseGJSONBytesNoCopy mirrors the existing GetGJSONBytesNoCopy contract: the returned result references the caller's bytes, so callers must neither retain it nor mutate the buffer while it is in use. --- internal/util/gjson.go | 11 +++++++++++ internal/util/gjson_test.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/internal/util/gjson.go b/internal/util/gjson.go index cd6fd7c75..840cf7682 100644 --- a/internal/util/gjson.go +++ b/internal/util/gjson.go @@ -14,3 +14,14 @@ func GetGJSONBytesNoCopy(data []byte, path string) gjson.Result { } return gjson.Get(unsafe.String(unsafe.SliceData(data), len(data)), path) } + +// ParseGJSONBytesNoCopy parses data into a GJSON result that references data +// directly. gjson.ParseBytes copies the whole document, which is prohibitive +// for multi-megabyte payloads. Callers must not retain the result or mutate +// data while using it. +func ParseGJSONBytesNoCopy(data []byte) gjson.Result { + if len(data) == 0 { + return gjson.Result{} + } + return gjson.Parse(unsafe.String(unsafe.SliceData(data), len(data))) +} diff --git a/internal/util/gjson_test.go b/internal/util/gjson_test.go index 8ce36958c..b0f03b312 100644 --- a/internal/util/gjson_test.go +++ b/internal/util/gjson_test.go @@ -1,6 +1,9 @@ package util -import "testing" +import ( + "testing" + "unsafe" +) func TestGetGJSONBytesNoCopy(t *testing.T) { input := []byte(`{"request":{"contents":[{"role":"user"}]}}`) @@ -15,3 +18,28 @@ func TestGetGJSONBytesNoCopyEmptyInput(t *testing.T) { t.Fatalf("empty input result = %s, want missing", result.Raw) } } + +func TestParseGJSONBytesNoCopy(t *testing.T) { + input := []byte(`{"request":{"contents":[{"role":"user"}]}}`) + root := ParseGJSONBytesNoCopy(input) + if !root.IsObject() || root.Get("request.contents.0.role").String() != "user" { + t.Fatalf("parsed root = %s, want user content array", root.Raw) + } +} + +func TestParseGJSONBytesNoCopyReferencesInput(t *testing.T) { + input := []byte(`{"contents":[{"role":"user"}]}`) + root := ParseGJSONBytesNoCopy(input) + if len(root.Raw) != len(input) { + t.Fatalf("raw length = %d, want %d", len(root.Raw), len(input)) + } + if unsafe.StringData(root.Raw) != unsafe.SliceData(input) { + t.Fatal("parsed result copied the input instead of referencing it") + } +} + +func TestParseGJSONBytesNoCopyEmptyInput(t *testing.T) { + if result := ParseGJSONBytesNoCopy(nil); result.Exists() { + t.Fatalf("empty input result = %s, want missing", result.Raw) + } +}