diff --git a/internal/unescape/unescape.go b/internal/unescape/unescape.go index cf472d1..97fca29 100644 --- a/internal/unescape/unescape.go +++ b/internal/unescape/unescape.go @@ -8,22 +8,24 @@ import ( // Valid returns the unescaped version of str relying on str to be valid. // Don't use this function if str isn't guaranteed to contain no // invalid escape sequences. -func Valid[S ~[]byte | ~string, O ~[]byte | ~string](str S) O { +func Valid[S []byte | string, O []byte | string](str S) O { var oz O if len(str) < 1 { return oz } + + // Copy the string because it probably originates from a token + // which will be reused across Decode calls. var s string switch in := any(str).(type) { case string: - s = in + cp := unsafe.Slice(unsafe.StringData(in), len(in)) + copy(cp, in) + s = unsafe.String(unsafe.SliceData(cp), len(in)) case []byte: - // Avoid copying str to a string, treat the bytes as read-only instead - // since str is guaranteed to remain immutable. - s = unsafe.String(unsafe.SliceData(in), len(in)) - default: - s = string(str) + s = string(in) } + i := strings.IndexByte(s, '\\') if i < 0 { return O(s) diff --git a/internal/unescape/unescape_test.go b/internal/unescape/unescape_test.go index 49c4212..981a0bd 100644 --- a/internal/unescape/unescape_test.go +++ b/internal/unescape/unescape_test.go @@ -116,7 +116,7 @@ var tests = []test{ }(), } -func runTestValid[S ~[]byte | ~string, O ~[]byte | ~string]( +func runTestValid[S []byte | string, O []byte | string]( t *testing.T, name string, input S, expect O, ) { t.Run(name, func(t *testing.T) { @@ -138,7 +138,6 @@ func runTestValid[S ~[]byte | ~string, O ~[]byte | ~string]( } func TestValid(t *testing.T) { - type CustomString string for _, td := range tests { runTestValid[string, string]( t, td.Name+"/string2string", td.Input, td.Expect, @@ -152,9 +151,6 @@ func TestValid(t *testing.T) { runTestValid[[]byte, string]( t, td.Name+"/bytes2string", []byte(td.Input), td.Expect, ) - runTestValid[CustomString, CustomString]( - t, td.Name+"/bytes2string", CustomString(td.Input), CustomString(td.Expect), - ) } } diff --git a/jscandec.go b/jscandec.go index 46e5deb..91e5eec 100644 --- a/jscandec.go +++ b/jscandec.go @@ -3524,7 +3524,7 @@ func fieldFrameIndexByName[S []byte | string](fields []fieldStackFrame, name S) return noParentFrame } -func decodeAny[S ~[]byte | ~string]( +func decodeAny[S []byte | string]( str S, tokens []jscan.Token[S], ) (any, []jscan.Token[S], error) { switch tokens[0].Type {