blob: 8c9263fbf4da838f529501adc644ae4104e01d46 [file] [log] [blame]
Damien Neil61e93c72019-03-27 09:23:20 -07001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package proto
6
7import (
8 "fmt"
9
10 "github.com/golang/protobuf/v2/internal/encoding/wire"
11 "github.com/golang/protobuf/v2/reflect/protoreflect"
12)
13
14// Size returns the size in bytes of the wire-format encoding of m.
15func Size(m Message) int {
16 return sizeMessage(m.ProtoReflect())
17}
18
19func sizeMessage(m protoreflect.Message) (size int) {
20 fields := m.Type().Fields()
21 knownFields := m.KnownFields()
22 m.KnownFields().Range(func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
23 field := fields.ByNumber(num)
24 if field == nil {
25 field = knownFields.ExtensionTypes().ByNumber(num)
26 if field == nil {
27 panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Type().FullName()))
28 }
29 }
30 size += sizeField(field, value)
31 return true
32 })
33 m.UnknownFields().Range(func(_ protoreflect.FieldNumber, raw protoreflect.RawFields) bool {
34 size += len(raw)
35 return true
36 })
37 return size
38}
39
40func sizeField(field protoreflect.FieldDescriptor, value protoreflect.Value) (size int) {
41 num := field.Number()
42 kind := field.Kind()
43 switch {
44 case field.Cardinality() != protoreflect.Repeated:
45 return wire.SizeTag(num) + sizeSingular(num, kind, value)
46 case field.IsMap():
47 return sizeMap(num, kind, field.MessageType(), value.Map())
48 case field.IsPacked():
49 return sizePacked(num, kind, value.List())
50 default:
51 return sizeList(num, kind, value.List())
52 }
53}
54
55func sizeMap(num wire.Number, kind protoreflect.Kind, mdesc protoreflect.MessageDescriptor, mapv protoreflect.Map) (size int) {
56 keyf := mdesc.Fields().ByNumber(1)
57 valf := mdesc.Fields().ByNumber(2)
58 mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
59 size += wire.SizeTag(num)
60 size += wire.SizeBytes(sizeField(keyf, key.Value()) + sizeField(valf, value))
61 return true
62 })
63 return size
64}
65
66func sizePacked(num wire.Number, kind protoreflect.Kind, list protoreflect.List) (size int) {
67 content := 0
68 for i, llen := 0, list.Len(); i < llen; i++ {
69 content += sizeSingular(num, kind, list.Get(i))
70 }
71 return wire.SizeTag(num) + wire.SizeBytes(content)
72}
73
74func sizeList(num wire.Number, kind protoreflect.Kind, list protoreflect.List) (size int) {
75 for i, llen := 0, list.Len(); i < llen; i++ {
76 size += wire.SizeTag(num) + sizeSingular(num, kind, list.Get(i))
77 }
78 return size
79}