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 19// Package proto defines the protobuf codec. Importing this package will 20// register the codec. 21package proto 22 23import ( 24 "math" 25 "sync" 26 27 "github.com/golang/protobuf/proto" 28 "google.golang.org/grpc/encoding" 29) 30 31// Name is the name registered for the proto compressor. 32const Name = "proto" 33 34func init() { 35 encoding.RegisterCodec(codec{}) 36} 37 38// codec is a Codec implementation with protobuf. It is the default codec for gRPC. 39type codec struct{} 40 41type cachedProtoBuffer struct { 42 lastMarshaledSize uint32 43 proto.Buffer 44} 45 46func capToMaxInt32(val int) uint32 { 47 if val > math.MaxInt32 { 48 return uint32(math.MaxInt32) 49 } 50 return uint32(val) 51} 52 53func marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) { 54 protoMsg := v.(proto.Message) 55 newSlice := make([]byte, 0, cb.lastMarshaledSize) 56 57 cb.SetBuf(newSlice) 58 cb.Reset() 59 if err := cb.Marshal(protoMsg); err != nil { 60 return nil, err 61 } 62 out := cb.Bytes() 63 cb.lastMarshaledSize = capToMaxInt32(len(out)) 64 return out, nil 65} 66 67func (codec) Marshal(v interface{}) ([]byte, error) { 68 if pm, ok := v.(proto.Marshaler); ok { 69 // object can marshal itself, no need for buffer 70 return pm.Marshal() 71 } 72 73 cb := protoBufferPool.Get().(*cachedProtoBuffer) 74 out, err := marshal(v, cb) 75 76 // put back buffer and lose the ref to the slice 77 cb.SetBuf(nil) 78 protoBufferPool.Put(cb) 79 return out, err 80} 81 82func (codec) Unmarshal(data []byte, v interface{}) error { 83 protoMsg := v.(proto.Message) 84 protoMsg.Reset() 85 86 if pu, ok := protoMsg.(proto.Unmarshaler); ok { 87 // object can unmarshal itself, no need for buffer 88 return pu.Unmarshal(data) 89 } 90 91 cb := protoBufferPool.Get().(*cachedProtoBuffer) 92 cb.SetBuf(data) 93 err := cb.Unmarshal(protoMsg) 94 cb.SetBuf(nil) 95 protoBufferPool.Put(cb) 96 return err 97} 98 99func (codec) Name() string { 100 return Name 101} 102 103var protoBufferPool = &sync.Pool{ 104 New: func() interface{} { 105 return &cachedProtoBuffer{ 106 Buffer: proto.Buffer{}, 107 lastMarshaledSize: 16, 108 } 109 }, 110} 111