blob: e2a75b6cafcea6967c243258ad4d4e83e79f4339 [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) {
Joe Tsai0fc49f82019-05-01 12:29:25 -070037 fieldDescs := m.Descriptor().Fields()
Damien Neil61e93c72019-03-27 09:23:20 -070038 knownFields := m.KnownFields()
39 m.KnownFields().Range(func(num protoreflect.FieldNumber, value protoreflect.Value) bool {
Joe Tsai0fc49f82019-05-01 12:29:25 -070040 field := fieldDescs.ByNumber(num)
Damien Neil61e93c72019-03-27 09:23:20 -070041 if field == nil {
Joe Tsai0fc49f82019-05-01 12:29:25 -070042 field = knownFields.ExtensionTypes().ByNumber(num).Descriptor()
Damien Neil61e93c72019-03-27 09:23:20 -070043 if field == nil {
Joe Tsai0fc49f82019-05-01 12:29:25 -070044 panic(fmt.Errorf("no descriptor for field %d in %q", num, m.Descriptor().FullName()))
Damien Neil61e93c72019-03-27 09:23:20 -070045 }
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
Joe Tsaiac31a352019-05-13 14:32:56 -070057func sizeField(fd protoreflect.FieldDescriptor, value protoreflect.Value) (size int) {
58 num := fd.Number()
Damien Neil61e93c72019-03-27 09:23:20 -070059 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -070060 case fd.IsList():
61 return sizeList(num, fd, value.List())
62 case fd.IsMap():
63 return sizeMap(num, fd, value.Map())
Damien Neil61e93c72019-03-27 09:23:20 -070064 default:
Joe Tsaiac31a352019-05-13 14:32:56 -070065 return wire.SizeTag(num) + sizeSingular(num, fd.Kind(), value)
Damien Neil61e93c72019-03-27 09:23:20 -070066 }
67}
68
Joe Tsaiac31a352019-05-13 14:32:56 -070069func sizeList(num wire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) (size int) {
70 if fd.IsPacked() {
71 content := 0
72 for i, llen := 0, list.Len(); i < llen; i++ {
73 content += sizeSingular(num, fd.Kind(), list.Get(i))
74 }
75 return wire.SizeTag(num) + wire.SizeBytes(content)
76 }
77
78 for i, llen := 0, list.Len(); i < llen; i++ {
79 size += wire.SizeTag(num) + sizeSingular(num, fd.Kind(), list.Get(i))
80 }
Damien Neil61e93c72019-03-27 09:23:20 -070081 return size
82}
83
Joe Tsaiac31a352019-05-13 14:32:56 -070084func sizeMap(num wire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) (size int) {
85 mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
86 size += wire.SizeTag(num)
87 size += wire.SizeBytes(sizeField(fd.MapKey(), key.Value()) + sizeField(fd.MapValue(), value))
88 return true
89 })
Damien Neil61e93c72019-03-27 09:23:20 -070090 return size
91}