| // 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 |
| } |
| } |
| |
| // Groups don't have XXX_unrecognized fields. |
| if uf := x.FieldByName("XXX_unrecognized"); uf.IsValid() { |
| n += uf.Len() |
| } |
| |
| return n |
| } |
| |
| func sizeField(x reflect.Value, prop *Properties) (n int) { |
| // Handle easy ones first. |
| switch prop.WireType { |
| case WireFixed64: |
| return 8 |
| case WireFixed32: |
| return 4 |
| } |
| |
| 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 |
| } |
| |
| // 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. |
| var nb int |
| if !prop.Packed { |
| nb = len(prop.tagcode) * n |
| } else { |
| nb = len(prop.tagcode) + sizeVarint(uint64(n)) |
| } |
| |
| if et.Kind() == reflect.Bool { |
| // []bool is easy to compute: each element requires one byte. |
| return nb + n |
| } |
| for i := 0; i < n; i++ { |
| nb += sizeField(x.Index(i), prop) |
| } |
| return nb |
| } |
| |
| 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) |
| 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)))) |
| } |