blob: a817cf7f910760e06f92ed272989ebb0a0c51bf6 [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 {
Damien Neil0d3e8cc2019-04-01 13:31:55 -070016 if size, err := sizeMessageFast(m); err == nil {
17 return size
18 }
Damien Neil61e93c72019-03-27 09:23:20 -070019 return sizeMessage(m.ProtoReflect())
20}
21
Damien Neil0d3e8cc2019-04-01 13:31:55 -070022func sizeMessageFast(m Message) (int, error) {
23 // TODO: Pass MarshalOptions to size to permit disabling fast path?
24 methods := protoMethods(m)
25 if methods == nil || methods.Size == nil {
26 return 0, errInternalNoFast
27 }
28 return methods.Size(m), nil
29}
30
Damien Neil61e93c72019-03-27 09:23:20 -070031func sizeMessage(m protoreflect.Message) (size int) {
32 fields := m.Type().Fields()
33 knownFields := m.KnownFields()
34 m.KnownFields().Range(func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
35 field := fields.ByNumber(num)
36 if field == nil {
37 field = knownFields.ExtensionTypes().ByNumber(num)
38 if field == nil {
39 panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Type().FullName()))
40 }
41 }
42 size += sizeField(field, value)
43 return true
44 })
45 m.UnknownFields().Range(func(_ protoreflect.FieldNumber, raw protoreflect.RawFields) bool {
46 size += len(raw)
47 return true
48 })
49 return size
50}
51
52func sizeField(field protoreflect.FieldDescriptor, value protoreflect.Value) (size int) {
53 num := field.Number()
54 kind := field.Kind()
55 switch {
56 case field.Cardinality() != protoreflect.Repeated:
57 return wire.SizeTag(num) + sizeSingular(num, kind, value)
58 case field.IsMap():
59 return sizeMap(num, kind, field.MessageType(), value.Map())
60 case field.IsPacked():
61 return sizePacked(num, kind, value.List())
62 default:
63 return sizeList(num, kind, value.List())
64 }
65}
66
67func sizeMap(num wire.Number, kind protoreflect.Kind, mdesc protoreflect.MessageDescriptor, mapv protoreflect.Map) (size int) {
68 keyf := mdesc.Fields().ByNumber(1)
69 valf := mdesc.Fields().ByNumber(2)
70 mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
71 size += wire.SizeTag(num)
72 size += wire.SizeBytes(sizeField(keyf, key.Value()) + sizeField(valf, value))
73 return true
74 })
75 return size
76}
77
78func sizePacked(num wire.Number, kind protoreflect.Kind, list protoreflect.List) (size int) {
79 content := 0
80 for i, llen := 0, list.Len(); i < llen; i++ {
81 content += sizeSingular(num, kind, list.Get(i))
82 }
83 return wire.SizeTag(num) + wire.SizeBytes(content)
84}
85
86func sizeList(num wire.Number, kind protoreflect.Kind, list protoreflect.List) (size int) {
87 for i, llen := 0, list.Len(); i < llen; i++ {
88 size += wire.SizeTag(num) + sizeSingular(num, kind, list.Get(i))
89 }
90 return size
91}