Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 28 additions & 34 deletions go/serializers/ugorji.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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
}

Expand All @@ -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) {
Expand All @@ -93,18 +88,17 @@ 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
}

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
Expand Down
Loading