| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- // Go support for Protocol Buffers - Google's data interchange format
- //
- // Copyright 2012 The Go Authors. All rights reserved.
- // http://code.google.com/p/goprotobuf/
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- // Functions to determine the size of an encoded protocol buffer.
- package proto
- import (
- "log"
- "reflect"
- "strings"
- )
- // Size returns the encoded size of a protocol buffer.
- // This function is expensive enough to be avoided unless proven worthwhile with instrumentation.
- func Size(pb Message) int {
- in := reflect.ValueOf(pb)
- if in.IsNil() {
- return 0
- }
- return sizeStruct(in.Elem())
- }
- func sizeStruct(x reflect.Value) (n int) {
- sprop := GetProperties(x.Type())
- for _, prop := range sprop.Prop {
- if strings.HasPrefix(prop.Name, "XXX_") { // handled below
- continue
- }
- fi, _ := sprop.decoderTags.get(prop.Tag)
- f := x.Field(fi)
- switch f.Kind() {
- case reflect.Ptr:
- if f.IsNil() {
- continue
- }
- n += len(prop.tagcode)
- f = f.Elem() // avoid a recursion in sizeField
- case reflect.Slice:
- if f.IsNil() {
- continue
- }
- if f.Len() == 0 && f.Type().Elem().Kind() != reflect.Uint8 {
- // short circuit for empty repeated fields.
- // []byte isn't a repeated field.
- continue
- }
- default:
- log.Printf("proto: unknown struct field type %v", f.Type())
- continue
- }
- n += sizeField(f, prop)
- }
- if em, ok := x.Addr().Interface().(extendableProto); ok {
- for _, ext := range em.ExtensionMap() {
- ms := len(ext.enc)
- if ext.enc == nil {
- props := new(Properties)
- props.Init(reflect.TypeOf(ext.desc.ExtensionType), "x", ext.desc.Tag, nil)
- ms = len(props.tagcode) + sizeField(reflect.ValueOf(ext.value), props)
- }
- n += ms
- }
- }
- if uf := x.FieldByName("XXX_unrecognized"); uf.IsValid() {
- n += uf.Len()
- }
- return n
- }
- func sizeField(x reflect.Value, prop *Properties) (n int) {
- if x.Type().Kind() == reflect.Slice {
- n := x.Len()
- et := x.Type().Elem()
- if et.Kind() == reflect.Uint8 {
- // []byte is easy.
- return len(prop.tagcode) + sizeVarint(uint64(n)) + n
- }
- var nb int
- // []bool and repeated fixed integer types are easy.
- switch {
- case et.Kind() == reflect.Bool:
- nb += n
- case prop.WireType == WireFixed64:
- nb += n * 8
- case prop.WireType == WireFixed32:
- nb += n * 4
- default:
- for i := 0; i < n; i++ {
- nb += sizeField(x.Index(i), prop)
- }
- }
- // Non-packed repeated fields have a per-element header of the tagcode.
- // Packed repeated fields only have a single header: the tag code plus a varint of the number of bytes.
- if !prop.Packed {
- nb += len(prop.tagcode) * n
- } else {
- nb += len(prop.tagcode) + sizeVarint(uint64(nb))
- }
- return nb
- }
- // easy scalars
- switch prop.WireType {
- case WireFixed64:
- return 8
- case WireFixed32:
- return 4
- }
- switch x.Kind() {
- case reflect.Bool:
- return 1
- case reflect.Int32, reflect.Int64:
- if prop.Wire == "varint" {
- return sizeVarint(uint64(x.Int()))
- } else if prop.Wire == "zigzag32" || prop.Wire == "zigzag64" {
- return sizeZigZag(uint64(x.Int()))
- }
- case reflect.Ptr:
- return sizeField(x.Elem(), prop)
- case reflect.String:
- n := x.Len()
- return sizeVarint(uint64(n)) + n
- case reflect.Struct:
- nb := sizeStruct(x)
- if prop.Wire == "group" {
- // Groups have start and end tags instead of a start tag and a length.
- return nb + len(prop.tagcode)
- }
- return sizeVarint(uint64(nb)) + nb
- case reflect.Uint32, reflect.Uint64:
- if prop.Wire == "varint" {
- return sizeVarint(uint64(x.Uint()))
- } else if prop.Wire == "zigzag32" || prop.Wire == "zigzag64" {
- return sizeZigZag(uint64(x.Int()))
- }
- default:
- log.Printf("proto.sizeField: unhandled kind %v", x.Kind())
- }
- // unknown type, so not a protocol buffer
- log.Printf("proto: don't know size of %v", x.Type())
- return 0
- }
- func sizeVarint(x uint64) (n int) {
- for {
- n++
- x >>= 7
- if x == 0 {
- break
- }
- }
- return n
- }
- func sizeZigZag(x uint64) (n int) {
- return sizeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63))))
- }
|