xref: /aosp_15_r20/external/pigweed/pw_protobuf/public/pw_protobuf/serialized_size.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <cstdint>
17 
18 #include "pw_protobuf/wire_format.h"
19 #include "pw_varint/varint.h"
20 
21 namespace pw::protobuf {
22 
23 // Field types that directly map to fixed wire types:
24 inline constexpr size_t kMaxSizeBytesFixed32 = 4;
25 inline constexpr size_t kMaxSizeBytesFixed64 = 8;
26 inline constexpr size_t kMaxSizeBytesSfixed32 = 4;
27 inline constexpr size_t kMaxSizeBytesSfixed64 = 8;
28 inline constexpr size_t kMaxSizeBytesFloat = kMaxSizeBytesFixed32;
29 inline constexpr size_t kMaxSizeBytesDouble = kMaxSizeBytesFixed64;
30 
31 // Field types that map to varint:
32 inline constexpr size_t kMaxSizeBytesUint32 = varint::kMaxVarint32SizeBytes;
33 inline constexpr size_t kMaxSizeBytesUint64 = varint::kMaxVarint64SizeBytes;
34 inline constexpr size_t kMaxSizeBytesSint32 = varint::kMaxVarint32SizeBytes;
35 inline constexpr size_t kMaxSizeBytesSint64 = varint::kMaxVarint64SizeBytes;
36 // The int32 field type does not use zigzag encoding, ergo negative values
37 // can result in the worst case varint size.
38 inline constexpr size_t kMaxSizeBytesInt32 = varint::kMaxVarint64SizeBytes;
39 inline constexpr size_t kMaxSizeBytesInt64 = varint::kMaxVarint64SizeBytes;
40 // The bool field type is backed by a varint, but has a limited value range.
41 inline constexpr size_t kMaxSizeBytesBool = 1;
42 
43 inline constexpr size_t kMaxSizeBytesEnum = kMaxSizeBytesInt32;
44 
45 inline constexpr size_t kMaxSizeOfFieldNumber = varint::kMaxVarint32SizeBytes;
46 
47 inline constexpr size_t kMaxSizeOfLength = varint::kMaxVarint32SizeBytes;
48 
49 // Calculate the serialized size of a proto tag (field number + wire type).
50 //
51 // Args:
52 //   field_number: The field number for the field.
53 //
54 // Returns:
55 //   The size of the field's encoded tag.
56 //
57 // Precondition: The field_number must be a ValidFieldNumber.
58 template <typename T>
TagSizeBytes(T field_number)59 constexpr size_t TagSizeBytes(T field_number) {
60   static_assert((std::is_enum<T>() || std::is_integral<T>()) &&
61                     sizeof(T) <= sizeof(uint32_t),
62                 "Field numbers must be 32-bit enums or integers");
63   // The wiretype does not impact the serialized size, so use kVarint (0), which
64   // will be optimized out by the compiler.
65   return varint::EncodedSize(
66       FieldKey(static_cast<uint32_t>(field_number), WireType::kVarint));
67 }
68 
69 // Calculates the size of a varint field (uint32/64, int32/64, sint32/64, enum).
70 template <typename T, typename U>
SizeOfVarintField(T field_number,U value)71 constexpr size_t SizeOfVarintField(T field_number, U value) {
72   return TagSizeBytes(field_number) +
73          varint::EncodedSize(static_cast<uint64_t>(value));
74 }
75 
76 // Calculates the size of a delimited field (string, bytes, nested message,
77 // packed repeated), excluding the data itself. This accounts for the field
78 // tag and length varint only. The default value for length_bytes assumes
79 // the length is kMaxSizeOfLength bytes long.
80 template <typename T>
81 constexpr size_t SizeOfDelimitedFieldWithoutValue(
82     T field_number,
83     uint32_t length_bytes = std::numeric_limits<uint32_t>::max()) {
84   return TagSizeBytes(field_number) + varint::EncodedSize(length_bytes);
85 }
86 
87 // Calculates the total size of a delimited field (string, bytes, nested
88 // message, packed repeated), including the data itself.
89 template <typename T>
SizeOfDelimitedField(T field_number,uint32_t length_bytes)90 constexpr size_t SizeOfDelimitedField(T field_number, uint32_t length_bytes) {
91   return SizeOfDelimitedFieldWithoutValue(field_number, length_bytes) +
92          length_bytes;
93 }
94 
95 // Calculates the size of a proto field in the wire format. This is the size of
96 // a final serialized protobuf entry, including the key (field number + wire
97 // type), encoded payload size (for length-delimited types), and data.
98 //
99 // Args:
100 //   field_number: The field number for the field.
101 //   type: The wire type of the field
102 //   data_size: The size of the payload.
103 //
104 // Returns:
105 //   The size of the field.
106 //
107 // Precondition: The field_number must be a ValidFieldNumber.
108 // Precondition: `data_size_bytes` must be smaller than
109 //   std::numeric_limits<uint32_t>::max()
110 template <typename T>
SizeOfField(T field_number,WireType type,size_t data_size_bytes)111 constexpr size_t SizeOfField(T field_number,
112                              WireType type,
113                              size_t data_size_bytes) {
114   if (type == WireType::kDelimited) {
115     PW_DASSERT(data_size_bytes <= std::numeric_limits<uint32_t>::max());
116     return SizeOfDelimitedField(field_number,
117                                 static_cast<uint32_t>(data_size_bytes));
118   }
119   return TagSizeBytes(field_number) + data_size_bytes;
120 }
121 
122 // Functions for calculating the size of each type of protobuf field. Varint
123 // fields (int32, uint64, etc.) accept a value argument that defaults to the
124 // largest-to-encode value for the type.
125 template <typename T>
SizeOfFieldFloat(T field_number)126 constexpr size_t SizeOfFieldFloat(T field_number) {
127   return TagSizeBytes(field_number) + sizeof(float);
128 }
129 template <typename T>
SizeOfFieldDouble(T field_number)130 constexpr size_t SizeOfFieldDouble(T field_number) {
131   return TagSizeBytes(field_number) + sizeof(double);
132 }
133 template <typename T>
134 constexpr size_t SizeOfFieldInt32(T field_number, int32_t value = -1) {
135   return SizeOfVarintField(field_number, value);
136 }
137 template <typename T>
138 constexpr size_t SizeOfFieldInt64(T field_number, int64_t value = -1) {
139   return SizeOfVarintField(field_number, value);
140 }
141 template <typename T>
142 constexpr size_t SizeOfFieldSint32(
143     T field_number, int32_t value = std::numeric_limits<int32_t>::min()) {
144   return SizeOfVarintField(field_number, varint::ZigZagEncode(value));
145 }
146 template <typename T>
147 constexpr size_t SizeOfFieldSint64(
148     T field_number, int64_t value = std::numeric_limits<int64_t>::min()) {
149   return SizeOfVarintField(field_number, varint::ZigZagEncode(value));
150 }
151 template <typename T>
152 constexpr size_t SizeOfFieldUint32(
153     T field_number, uint32_t value = std::numeric_limits<uint32_t>::max()) {
154   return SizeOfVarintField(field_number, value);
155 }
156 template <typename T>
157 constexpr size_t SizeOfFieldUint64(
158     T field_number, uint64_t value = std::numeric_limits<uint64_t>::max()) {
159   return SizeOfVarintField(field_number, value);
160 }
161 template <typename T>
SizeOfFieldFixed32(T field_number)162 constexpr size_t SizeOfFieldFixed32(T field_number) {
163   return TagSizeBytes(field_number) + sizeof(uint32_t);
164 }
165 template <typename T>
SizeOfFieldFixed64(T field_number)166 constexpr size_t SizeOfFieldFixed64(T field_number) {
167   return TagSizeBytes(field_number) + sizeof(uint64_t);
168 }
169 template <typename T>
SizeOfFieldSfixed32(T field_number)170 constexpr size_t SizeOfFieldSfixed32(T field_number) {
171   return TagSizeBytes(field_number) + sizeof(uint32_t);
172 }
173 template <typename T>
SizeOfFieldSfixed64(T field_number)174 constexpr size_t SizeOfFieldSfixed64(T field_number) {
175   return TagSizeBytes(field_number) + sizeof(uint64_t);
176 }
177 template <typename T>
SizeOfFieldBool(T field_number)178 constexpr size_t SizeOfFieldBool(T field_number) {
179   return TagSizeBytes(field_number) + 1;
180 }
181 template <typename T>
SizeOfFieldString(T field_number,uint32_t length_bytes)182 constexpr size_t SizeOfFieldString(T field_number, uint32_t length_bytes) {
183   return SizeOfDelimitedField(field_number, length_bytes);
184 }
185 template <typename T>
SizeOfFieldBytes(T field_number,uint32_t length_bytes)186 constexpr size_t SizeOfFieldBytes(T field_number, uint32_t length_bytes) {
187   return SizeOfDelimitedField(field_number, length_bytes);
188 }
189 template <typename T, typename U = int32_t>
190 constexpr size_t SizeOfFieldEnum(T field_number, U value = static_cast<U>(-1)) {
191   static_assert((std::is_enum<U>() || std::is_integral<U>()) &&
192                     sizeof(U) <= sizeof(uint32_t),
193                 "Enum values must be 32-bit enums or integers");
194   return SizeOfFieldInt32(field_number, static_cast<int32_t>(value));
195 }
196 
197 }  // namespace pw::protobuf
198