diff --git a/go/serializers/ugorji.go b/go/serializers/ugorji.go index 133dd0fb..e83e0709 100644 --- a/go/serializers/ugorji.go +++ b/go/serializers/ugorji.go @@ -11,51 +11,50 @@ import ( // ugorjiHandle is shared machinery for ugorji/go/codec handles. // Recommended (go-codec primer): reuse Handle; NewEncoderBytes + ResetBytes per encode; // NewDecoderBytes + ResetBytes per decode; WriterBufferSize for stream throughput. +// +// lib implementation doesn't allow a reset across bytes vs io, so keep 2 separate pairs +// // https://github.com/ugorji/go/codec http://ugorji.net/blog/go-codec-primer type ugorjiCodec struct { name string h codec.Handle - out []byte + out []byte enc *codec.Encoder dec *codec.Decoder + strenc *codec.Encoder + strdec *codec.Decoder proto any } -func configureBasic(h *codec.BasicHandle) { +func newUgorjiCodec(h codec.Handle, bh *codec.BasicHandle) *ugorjiCodec { // Buffering improves stream encode; zero Indent for JSON throughput. - h.WriterBufferSize = 8192 - h.ReaderBufferSize = 8192 + bh.WriterBufferSize = 8192 + bh.ReaderBufferSize = 8192 + s := &ugorjiCodec{name: "ugorji/"+h.Name(), h: h} + s.enc = codec.NewEncoderBytes(&s.out, h) + s.dec = codec.NewDecoderBytes(nil, h) + s.strenc = codec.NewEncoder(nil, h) + s.strdec = codec.NewDecoder(nil, h) + return s } func newUgorjiJSON() *ugorjiCodec { - jh := new(codec.JsonHandle) - configureBasic(&jh.BasicHandle) + h := new(codec.JsonHandle) // Match high-throughput JSON: no HTML escape, no indent. - jh.HTMLCharsAsIs = true - jh.Indent = 0 - s := &ugorjiCodec{name: "ugorji/json", h: jh} - s.enc = codec.NewEncoderBytes(&s.out, jh) - s.dec = codec.NewDecoderBytes(nil, jh) - return s + h.HTMLCharsAsIs = true + h.Indent = 0 + return newUgorjiCodec(h, &h.BasicHandle) } func newUgorjiMsgpack() *ugorjiCodec { - mh := new(codec.MsgpackHandle) - configureBasic(&mh.BasicHandle) + h := new(codec.MsgpackHandle) // WriteExt improves type fidelity for time etc.; keep default for structs. - s := &ugorjiCodec{name: "ugorji/msgpack", h: mh} - s.enc = codec.NewEncoderBytes(&s.out, mh) - s.dec = codec.NewDecoderBytes(nil, mh) - return s + return newUgorjiCodec(h, &h.BasicHandle) } func newUgorjiCBOR() *ugorjiCodec { - ch := new(codec.CborHandle) - configureBasic(&ch.BasicHandle) - s := &ugorjiCodec{name: "ugorji/cbor", h: ch} - s.enc = codec.NewEncoderBytes(&s.out, ch) - s.dec = codec.NewDecoderBytes(nil, ch) - return s + h := new(codec.CborHandle) + return newUgorjiCodec(h, &h.BasicHandle) } func (s *ugorjiCodec) Name() string { return s.name } @@ -66,8 +65,6 @@ func (s *ugorjiCodec) Supports(n string) bool { return DefaultSupports(n) } func (s *ugorjiCodec) Prepare(fx model.Fixture) error { s.proto = fx.Value - s.out = s.out[:0] - s.enc.ResetBytes(&s.out) return nil } @@ -77,9 +74,7 @@ func (s *ugorjiCodec) SerializeBytes(fx model.Fixture) ([]byte, error) { if err := s.enc.Encode(fx.Value); err != nil { return nil, err } - out := make([]byte, len(s.out)) - copy(out, s.out) - return out, nil + return s.out, nil // honors zero-copy disciplined implementation } func (s *ugorjiCodec) DeserializeBytes(buf []byte) (any, error) { @@ -93,9 +88,8 @@ func (s *ugorjiCodec) DeserializeBytes(buf []byte) (any, error) { func (s *ugorjiCodec) SerializeStream(fx model.Fixture, w io.Writer) (int, error) { cw := &countWriter{w: w} - // Writer path: NewEncoder + Reset (cannot mix Bytes encoder with Writer Reset). - enc := codec.NewEncoder(cw, s.h) - if err := enc.Encode(fx.Value); err != nil { + s.strenc.Reset(cw) + if err := s.strenc.Encode(fx.Value); err != nil { return 0, err } return cw.n, nil @@ -103,8 +97,8 @@ func (s *ugorjiCodec) SerializeStream(fx model.Fixture, w io.Writer) (int, error func (s *ugorjiCodec) DeserializeStream(r io.Reader) (any, error) { dst := model.NewEmptyPtr(s.proto) - dec := codec.NewDecoder(r, s.h) - if err := dec.Decode(dst); err != nil { + s.strdec.Reset(r) + if err := s.strdec.Decode(dst); err != nil { return nil, err } return model.Deref(dst), nil