blob: 5f26693220bad5a6277e480f3dbfd6742129a9dc [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 (
Damien Neil302cb322019-06-19 15:22:13 -07008 "google.golang.org/protobuf/internal/encoding/messageset"
Damien Neile89e6242019-05-13 23:55:40 -07009 "google.golang.org/protobuf/internal/encoding/wire"
10 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsaif8b855d2019-07-12 13:37:59 -070011 "google.golang.org/protobuf/runtime/protoiface"
Damien Neil61e93c72019-03-27 09:23:20 -070012)
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 Neil61e93c72019-03-27 09:23:20 -070021 return sizeMessage(m.ProtoReflect())
22}
23
Joe Tsai0f81b382019-07-10 23:14:31 -070024func sizeMessage(m protoreflect.Message) (size int) {
Damien Neilb0d217f2020-01-06 11:17:07 -080025 methods := protoMethods(m)
26 if methods != nil && methods.Size != nil {
Joe Tsaif8b855d2019-07-12 13:37:59 -070027 return methods.Size(m, protoiface.MarshalOptions{})
Damien Neil0d3e8cc2019-04-01 13:31:55 -070028 }
Damien Neil61781dd2020-01-21 13:29:51 -080029 if methods != nil && methods.Marshal != nil {
Damien Neilb0d217f2020-01-06 11:17:07 -080030 // This is not efficient, but we don't have any choice.
31 // This case is mainly used for legacy types with a Marshal method.
Damien Neild30e5612020-01-22 10:28:16 -080032 out, _ := methods.Marshal(m, protoiface.MarshalInput{}, protoiface.MarshalOptions{})
Damien Neil61781dd2020-01-21 13:29:51 -080033 return len(out.Buf)
Damien Neilb0d217f2020-01-06 11:17:07 -080034 }
Joe Tsai0f81b382019-07-10 23:14:31 -070035 return sizeMessageSlow(m)
Damien Neil0d3e8cc2019-04-01 13:31:55 -070036}
37
Joe Tsai0f81b382019-07-10 23:14:31 -070038func sizeMessageSlow(m protoreflect.Message) (size int) {
Damien Neil302cb322019-06-19 15:22:13 -070039 if messageset.IsMessageSet(m.Descriptor()) {
40 return sizeMessageSet(m)
41 }
Joe Tsai378c1322019-04-25 23:48:08 -070042 m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
43 size += sizeField(fd, v)
Damien Neil61e93c72019-03-27 09:23:20 -070044 return true
45 })
Joe Tsai378c1322019-04-25 23:48:08 -070046 size += len(m.GetUnknown())
Damien Neil61e93c72019-03-27 09:23:20 -070047 return size
48}
49
Joe Tsaiac31a352019-05-13 14:32:56 -070050func sizeField(fd protoreflect.FieldDescriptor, value protoreflect.Value) (size int) {
51 num := fd.Number()
Damien Neil61e93c72019-03-27 09:23:20 -070052 switch {
Joe Tsaiac31a352019-05-13 14:32:56 -070053 case fd.IsList():
54 return sizeList(num, fd, value.List())
55 case fd.IsMap():
56 return sizeMap(num, fd, value.Map())
Damien Neil61e93c72019-03-27 09:23:20 -070057 default:
Joe Tsaiac31a352019-05-13 14:32:56 -070058 return wire.SizeTag(num) + sizeSingular(num, fd.Kind(), value)
Damien Neil61e93c72019-03-27 09:23:20 -070059 }
60}
61
Joe Tsaiac31a352019-05-13 14:32:56 -070062func sizeList(num wire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) (size int) {
Joe Tsai378c1322019-04-25 23:48:08 -070063 if fd.IsPacked() && list.Len() > 0 {
Joe Tsaiac31a352019-05-13 14:32:56 -070064 content := 0
65 for i, llen := 0, list.Len(); i < llen; i++ {
66 content += sizeSingular(num, fd.Kind(), list.Get(i))
67 }
68 return wire.SizeTag(num) + wire.SizeBytes(content)
69 }
70
71 for i, llen := 0, list.Len(); i < llen; i++ {
72 size += wire.SizeTag(num) + sizeSingular(num, fd.Kind(), list.Get(i))
73 }
Damien Neil61e93c72019-03-27 09:23:20 -070074 return size
75}
76
Joe Tsaiac31a352019-05-13 14:32:56 -070077func sizeMap(num wire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) (size int) {
78 mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
79 size += wire.SizeTag(num)
80 size += wire.SizeBytes(sizeField(fd.MapKey(), key.Value()) + sizeField(fd.MapValue(), value))
81 return true
82 })
Damien Neil61e93c72019-03-27 09:23:20 -070083 return size
84}