mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
A decoded string owns its bytes
zap reads text zero-copy over the frame that arrived, and Call releases that frame to the pool when it returns, so a reply's strings pointed into memory the next call overwrites. The generated codecs already copy text for this reason; the reflective decoder now does the same. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package zapenc_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zap-proto/zip/internal/zapenc"
|
||||
)
|
||||
|
||||
// A decoded value owns its strings. The transport hands Unmarshal a pooled frame
|
||||
// and reuses it for the next call, so a string that still pointed into the frame
|
||||
// would change under whoever kept it.
|
||||
func TestDecodedTextOutlivesTheFrame(t *testing.T) {
|
||||
type site struct {
|
||||
Bucket string
|
||||
Prefix string
|
||||
Opt *string
|
||||
Inner inner
|
||||
}
|
||||
s := "gone"
|
||||
frame, err := zapenc.Marshal(&site{Bucket: "hanzo-sites", Prefix: "hanzo/bootnode", Opt: &s, Inner: inner{Slug: "bootnode"}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var got site
|
||||
if err := zapenc.Unmarshal(frame, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := range frame {
|
||||
frame[i] = 'x'
|
||||
}
|
||||
if got.Bucket != "hanzo-sites" || got.Prefix != "hanzo/bootnode" || got.Opt == nil || *got.Opt != "gone" || got.Inner.Slug != "bootnode" {
|
||||
t.Fatalf("decoded strings follow the frame: %+v opt=%v", got, got.Opt)
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
zap "github.com/zap-proto/go"
|
||||
@@ -593,7 +594,10 @@ func readField(o zap.Object, f field, target reflect.Value) error {
|
||||
case kFloat64:
|
||||
target.SetFloat(o.Float64(f.offset))
|
||||
case kText:
|
||||
target.SetString(o.Text(f.offset))
|
||||
// zap reads text zero-copy, over the frame that arrived, and the caller's
|
||||
// transport reuses that frame once the call returns. The decoded value
|
||||
// outlives the call, so it is copied here, as the generated codecs copy.
|
||||
target.SetString(strings.Clone(o.Text(f.offset)))
|
||||
case kBytes:
|
||||
if b := o.Bytes(f.offset); len(b) > 0 {
|
||||
target.SetBytes(append([]byte(nil), b...))
|
||||
|
||||
Reference in New Issue
Block a user