You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

111 lines
2.5 KiB

  1. /*
  2. *
  3. * Copyright 2018 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Package proto defines the protobuf codec. Importing this package will
  19. // register the codec.
  20. package proto
  21. import (
  22. "math"
  23. "sync"
  24. "github.com/golang/protobuf/proto"
  25. "google.golang.org/grpc/encoding"
  26. )
  27. // Name is the name registered for the proto compressor.
  28. const Name = "proto"
  29. func init() {
  30. encoding.RegisterCodec(codec{})
  31. }
  32. // codec is a Codec implementation with protobuf. It is the default codec for gRPC.
  33. type codec struct{}
  34. type cachedProtoBuffer struct {
  35. lastMarshaledSize uint32
  36. proto.Buffer
  37. }
  38. func capToMaxInt32(val int) uint32 {
  39. if val > math.MaxInt32 {
  40. return uint32(math.MaxInt32)
  41. }
  42. return uint32(val)
  43. }
  44. func marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) {
  45. protoMsg := v.(proto.Message)
  46. newSlice := make([]byte, 0, cb.lastMarshaledSize)
  47. cb.SetBuf(newSlice)
  48. cb.Reset()
  49. if err := cb.Marshal(protoMsg); err != nil {
  50. return nil, err
  51. }
  52. out := cb.Bytes()
  53. cb.lastMarshaledSize = capToMaxInt32(len(out))
  54. return out, nil
  55. }
  56. func (codec) Marshal(v interface{}) ([]byte, error) {
  57. if pm, ok := v.(proto.Marshaler); ok {
  58. // object can marshal itself, no need for buffer
  59. return pm.Marshal()
  60. }
  61. cb := protoBufferPool.Get().(*cachedProtoBuffer)
  62. out, err := marshal(v, cb)
  63. // put back buffer and lose the ref to the slice
  64. cb.SetBuf(nil)
  65. protoBufferPool.Put(cb)
  66. return out, err
  67. }
  68. func (codec) Unmarshal(data []byte, v interface{}) error {
  69. protoMsg := v.(proto.Message)
  70. protoMsg.Reset()
  71. if pu, ok := protoMsg.(proto.Unmarshaler); ok {
  72. // object can unmarshal itself, no need for buffer
  73. return pu.Unmarshal(data)
  74. }
  75. cb := protoBufferPool.Get().(*cachedProtoBuffer)
  76. cb.SetBuf(data)
  77. err := cb.Unmarshal(protoMsg)
  78. cb.SetBuf(nil)
  79. protoBufferPool.Put(cb)
  80. return err
  81. }
  82. func (codec) Name() string {
  83. return Name
  84. }
  85. var protoBufferPool = &sync.Pool{
  86. New: func() interface{} {
  87. return &cachedProtoBuffer{
  88. Buffer: proto.Buffer{},
  89. lastMarshaledSize: 16,
  90. }
  91. },
  92. }