size.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // 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. package proto
  5. import (
  6. "google.golang.org/protobuf/encoding/protowire"
  7. "google.golang.org/protobuf/internal/encoding/messageset"
  8. "google.golang.org/protobuf/reflect/protoreflect"
  9. "google.golang.org/protobuf/runtime/protoiface"
  10. )
  11. // Size returns the size in bytes of the wire-format encoding of m.
  12. //
  13. // Note that Size might return more bytes than Marshal will write in the case of
  14. // lazily decoded messages that arrive in non-minimal wire format: see
  15. // https://protobuf.dev/reference/go/size/ for more details.
  16. func Size(m Message) int {
  17. return MarshalOptions{}.Size(m)
  18. }
  19. // Size returns the size in bytes of the wire-format encoding of m.
  20. //
  21. // Note that Size might return more bytes than Marshal will write in the case of
  22. // lazily decoded messages that arrive in non-minimal wire format: see
  23. // https://protobuf.dev/reference/go/size/ for more details.
  24. func (o MarshalOptions) Size(m Message) int {
  25. // Treat a nil message interface as an empty message; nothing to output.
  26. if m == nil {
  27. return 0
  28. }
  29. return o.size(m.ProtoReflect())
  30. }
  31. // size is a centralized function that all size operations go through.
  32. // For profiling purposes, avoid changing the name of this function or
  33. // introducing other code paths for size that do not go through this.
  34. func (o MarshalOptions) size(m protoreflect.Message) (size int) {
  35. methods := protoMethods(m)
  36. if methods != nil && methods.Size != nil {
  37. out := methods.Size(protoiface.SizeInput{
  38. Message: m,
  39. Flags: o.flags(),
  40. })
  41. return out.Size
  42. }
  43. if methods != nil && methods.Marshal != nil {
  44. // This is not efficient, but we don't have any choice.
  45. // This case is mainly used for legacy types with a Marshal method.
  46. out, _ := methods.Marshal(protoiface.MarshalInput{
  47. Message: m,
  48. Flags: o.flags(),
  49. })
  50. return len(out.Buf)
  51. }
  52. return o.sizeMessageSlow(m)
  53. }
  54. func (o MarshalOptions) sizeMessageSlow(m protoreflect.Message) (size int) {
  55. if messageset.IsMessageSet(m.Descriptor()) {
  56. return o.sizeMessageSet(m)
  57. }
  58. m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
  59. size += o.sizeField(fd, v)
  60. return true
  61. })
  62. size += len(m.GetUnknown())
  63. return size
  64. }
  65. func (o MarshalOptions) sizeField(fd protoreflect.FieldDescriptor, value protoreflect.Value) (size int) {
  66. num := fd.Number()
  67. switch {
  68. case fd.IsList():
  69. return o.sizeList(num, fd, value.List())
  70. case fd.IsMap():
  71. return o.sizeMap(num, fd, value.Map())
  72. default:
  73. return protowire.SizeTag(num) + o.sizeSingular(num, fd.Kind(), value)
  74. }
  75. }
  76. func (o MarshalOptions) sizeList(num protowire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) (size int) {
  77. sizeTag := protowire.SizeTag(num)
  78. if fd.IsPacked() && list.Len() > 0 {
  79. content := 0
  80. for i, llen := 0, list.Len(); i < llen; i++ {
  81. content += o.sizeSingular(num, fd.Kind(), list.Get(i))
  82. }
  83. return sizeTag + protowire.SizeBytes(content)
  84. }
  85. for i, llen := 0, list.Len(); i < llen; i++ {
  86. size += sizeTag + o.sizeSingular(num, fd.Kind(), list.Get(i))
  87. }
  88. return size
  89. }
  90. func (o MarshalOptions) sizeMap(num protowire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) (size int) {
  91. sizeTag := protowire.SizeTag(num)
  92. mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
  93. size += sizeTag
  94. size += protowire.SizeBytes(o.sizeField(fd.MapKey(), key.Value()) + o.sizeField(fd.MapValue(), value))
  95. return true
  96. })
  97. return size
  98. }