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