diff --git a/go.mod b/go.mod index 6e5c52e..dbe6683 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,19 @@ module go.unistack.org/micro-codec-proto/v4 -go 1.22.0 +go 1.25 require ( go.unistack.org/micro-proto/v4 v4.1.0 - go.unistack.org/micro/v4 v4.1.2 - google.golang.org/protobuf v1.36.5 + go.unistack.org/micro/v4 v4.1.29 + google.golang.org/grpc v1.79.1 + google.golang.org/protobuf v1.36.11 ) require ( github.com/ash3in/uuidv8 v1.2.0 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/matoous/go-nanoid v1.5.1 // indirect + golang.org/x/sys v0.41.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index eecd680..128ebb6 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/ash3in/uuidv8 v1.2.0/go.mod h1:BnU0wJBxnzdEKmVg4xckBkD+VZuecTFTUP3M0d github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -17,12 +18,34 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= go.unistack.org/micro-proto/v4 v4.1.0 h1:qPwL2n/oqh9RE3RTTDgt28XK3QzV597VugQPaw9lKUk= go.unistack.org/micro-proto/v4 v4.1.0/go.mod h1:ArmK7o+uFvxSY3dbJhKBBX4Pm1rhWdLEFf3LxBrMtec= go.unistack.org/micro/v4 v4.1.2 h1:9SOlPYyPNNFpg1A7BsvhDyQm3gysLH1AhWbDCp1hyoY= go.unistack.org/micro/v4 v4.1.2/go.mod h1:lr3oYED8Ay1vjK68QqRw30QOtdk/ffpZqMFDasOUhKw= +go.unistack.org/micro/v4 v4.1.29 h1:oCE3z2q1/7uQOr4Ed8y61Ilk0VLy8jmfrxEWGHioFrY= +go.unistack.org/micro/v4 v4.1.29/go.mod h1:TscgP4Qp+P+8FYxfySJjd1FUlyzHopwR39A0+N5JITg= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 h1:Z7FRVJPSMaHQxD0uXU8WdgFh8PseLM8Q8NzhnpMrBhQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= +google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/options.go b/options.go index e5a0c64..4ce7b1a 100644 --- a/options.go +++ b/options.go @@ -2,6 +2,7 @@ package proto import ( codec "go.unistack.org/micro/v4/codec" + "google.golang.org/grpc/mem" "google.golang.org/protobuf/proto" ) @@ -16,3 +17,9 @@ type marshalOptionsKey struct{} func MarshalOptions(o proto.MarshalOptions) codec.Option { return codec.SetOption(marshalOptionsKey{}, o) } + +type memBufferPoolKey struct{} + +func BufferPool(p mem.BufferPool) codec.Option { + return codec.SetOption(memBufferPoolKey{}, p) +} diff --git a/proto.go b/proto.go index e7f2c81..311b5f4 100644 --- a/proto.go +++ b/proto.go @@ -5,6 +5,9 @@ import ( pb "go.unistack.org/micro-proto/v4/codec" "go.unistack.org/micro/v4/codec" rutil "go.unistack.org/micro/v4/util/reflect" + "google.golang.org/grpc/encoding" + _ "google.golang.org/grpc/encoding/proto" + "google.golang.org/grpc/mem" "google.golang.org/protobuf/proto" ) @@ -27,9 +30,15 @@ type protoCodecV2 struct { opts codec.Options } +type protoCodecGRPC struct { + opts codec.Options + bp mem.BufferPool +} + var ( - _ codec.Codec = (*protoCodec)(nil) - _ codec.CodecV2 = (*protoCodecV2)(nil) + _ codec.Codec = (*protoCodec)(nil) + _ codec.CodecV2 = (*protoCodecV2)(nil) + _ encoding.CodecV2 = (*protoCodecGRPC)(nil) ) func (c *protoCodec) Marshal(v interface{}, opts ...codec.Option) ([]byte, error) { @@ -195,5 +204,109 @@ func (c *protoCodecV2) String() string { } func NewCodecV2(opts ...codec.Option) codec.CodecV2 { + options := codec.NewOptions(opts...) + + bp, ok := options.Context.Value(memBufferPoolKey{}).(mem.BufferPool) + if !ok || bp != nil { + bp = mem.NewTieredBufferPool(1024, 2048, 4096, 8092, 1*1024*1024*1024, 4*1024*1024*1024) + } + return &protoCodecV2{opts: codec.NewOptions(opts...)} } + +func NewCodecGRPC(opts ...codec.Option) *protoCodecGRPC { + options := codec.NewOptions(opts...) + + bp, ok := options.Context.Value(memBufferPoolKey{}).(mem.BufferPool) + if !ok || bp != nil { + bp = mem.NewTieredBufferPool(1024, 2048, 4096, 8092, 1*1024*1024*1024, 4*1024*1024*1024) + } + + return &protoCodecGRPC{opts: codec.NewOptions(opts...)} +} + +func (c *protoCodecGRPC) Marshal(v any) (out mem.BufferSlice, err error) { + if v == nil { + return nil, nil + } + + options := c.opts + + if options.Flatten { + if nv, nerr := rutil.StructFieldByTag(v, options.TagName, "flatten"); nerr == nil { + v = nv + } + } + + switch m := v.(type) { + case *codec.Frame: + buf := c.bp.Get(len(m.Data)) + *buf = append((*buf)[:0], m.Data...) + out = append(out, mem.NewBuffer(buf, c.bp)) + return out, nil + case *pb.Frame: + buf := c.bp.Get(len(m.Data)) + *buf = append((*buf)[:0], m.Data...) + out = append(out, mem.NewBuffer(buf, c.bp)) + return out, nil + case proto.Message: + marshalOptions := DefaultMarshalOptions + if options.Context != nil { + if f, ok := options.Context.Value(marshalOptionsKey{}).(proto.MarshalOptions); ok { + marshalOptions = f + } + } + size := proto.Size(m) + + buf := c.bp.Get(size) + + if _, err := marshalOptions.MarshalAppend((*buf)[:0], m); err != nil { + c.bp.Put(buf) + return nil, err + } + out = append(out, mem.NewBuffer(buf, c.bp)) + return out, nil + default: + return nil, codec.ErrInvalidMessage + } +} + +func (c *protoCodecGRPC) Unmarshal(data mem.BufferSlice, v any) (err error) { + if v == nil || len(data) == 0 { + return nil + } + + options := c.opts + + if options.Flatten { + if nv, nerr := rutil.StructFieldByTag(v, options.TagName, "flatten"); nerr == nil { + v = nv + } + } + + buf := data.MaterializeToBuffer(c.bp) + defer buf.Free() + + switch m := v.(type) { + case *codec.Frame: + m.Data = buf.ReadOnlyData() + return nil + case *pb.Frame: + m.Data = buf.ReadOnlyData() + return nil + case proto.Message: + unmarshalOptions := DefaultUnmarshalOptions + if options.Context != nil { + if f, ok := options.Context.Value(marshalOptionsKey{}).(proto.UnmarshalOptions); ok { + unmarshalOptions = f + } + } + return unmarshalOptions.Unmarshal(buf.ReadOnlyData(), m) + default: + return codec.ErrInvalidMessage + } +} + +func (c *protoCodecGRPC) Name() string { + return "proto" +}