• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 size of a proto field key in wire format, including the key
50 // field number + wire type.
51 // type).
52 //
53 // Args:
54 //   field_number: The field number for the field.
55 //
56 // Returns:
57 //   The size of the field key.
58 //
59 // Precondition: The field_number must be a ValidFieldNumber.
60 template <typename T>
FieldNumberSizeBytes(T field_number)61 constexpr size_t FieldNumberSizeBytes(T field_number) {
62   static_assert((std::is_enum<T>() || std::is_integral<T>()) &&
63                     sizeof(T) <= sizeof(uint32_t),
64                 "Field numbers must be 32-bit enums or integers");
65   // The wiretype does not impact the serialized size, so use kVarint (0), which
66   // will be optimized out by the compiler.
67   return varint::EncodedSize(
68       FieldKey(static_cast<uint32_t>(field_number), WireType::kVarint));
69 }
70 
71 // Calculates the size of a varint field (uint32/64, int32/64, sint32/64, enum).
72 template <typename T, typename U>
SizeOfVarintField(T field_number,U value)73 constexpr size_t SizeOfVarintField(T field_number, U value) {
74   return FieldNumberSizeBytes(field_number) + varint::EncodedSize(value);
75 }
76 
77 // Calculates the size of a delimited field (string, bytes, nested message,
78 // packed repeated), excluding the data itself. This accounts for the field
79 // tag and length varint only. The default value for length_bytes assumes
80 // the length is kMaxSizeOfLength bytes long.
81 template <typename T>
82 constexpr size_t SizeOfDelimitedFieldWithoutValue(
83     T field_number,
84     uint32_t length_bytes = std::numeric_limits<uint32_t>::max()) {
85   return FieldNumberSizeBytes(field_number) + varint::EncodedSize(length_bytes);
86 }
87 
88 // Calculates the total size of a delimited field (string, bytes, nested
89 // message, packed repeated), including the data itself.
90 template <typename T>
SizeOfDelimitedField(T field_number,uint32_t length_bytes)91 constexpr size_t SizeOfDelimitedField(T field_number, uint32_t length_bytes) {
92   return SizeOfDelimitedFieldWithoutValue(field_number, length_bytes) +
93          length_bytes;
94 }
95 
96 // Calculates the size of a proto field in the wire format. This is the size of
97 // a final serialized protobuf entry, including the key (field number + wire
98 // type), encoded payload size (for length-delimited types), and data.
99 //
100 // Args:
101 //   field_number: The field number for the field.
102 //   type: The wire type of the field
103 //   data_size: The size of the payload.
104 //
105 // Returns:
106 //   The size of the field.
107 //
108 // Precondition: The field_number must be a ValidFieldNumber.
109 // Precondition: `data_size_bytes` must be smaller than
110 //   std::numeric_limits<uint32_t>::max()
111 template <typename T>
SizeOfField(T field_number,WireType type,size_t data_size_bytes)112 constexpr size_t SizeOfField(T field_number,
113                              WireType type,
114                              size_t data_size_bytes) {
115   if (type == WireType::kDelimited) {
116     return SizeOfDelimitedField(field_number, data_size_bytes);
117   }
118   return FieldNumberSizeBytes(field_number) + data_size_bytes;
119 }
120 
121 // Functions for calculating the size of each type of protobuf field. Varint
122 // fields (int32, uint64, etc.) accept a value argument that defaults to the
123 // largest-to-encode value for the type.
124 template <typename T>
SizeOfFieldFloat(T field_number)125 constexpr size_t SizeOfFieldFloat(T field_number) {
126   return FieldNumberSizeBytes(field_number) + sizeof(float);
127 }
128 template <typename T>
SizeOfFieldDouble(T field_number)129 constexpr size_t SizeOfFieldDouble(T field_number) {
130   return FieldNumberSizeBytes(field_number) + sizeof(double);
131 }
132 template <typename T>
133 constexpr size_t SizeOfFieldInt32(T field_number, int32_t value = -1) {
134   return SizeOfVarintField(field_number, value);
135 }
136 template <typename T>
137 constexpr size_t SizeOfFieldInt64(T field_number, int64_t value = -1) {
138   return SizeOfVarintField(field_number, value);
139 }
140 template <typename T>
141 constexpr size_t SizeOfFieldSint32(
142     T field_number, int32_t value = std::numeric_limits<int32_t>::min()) {
143   return SizeOfVarintField(field_number, varint::ZigZagEncode(value));
144 }
145 template <typename T>
146 constexpr size_t SizeOfFieldSint64(
147     T field_number, int64_t value = std::numeric_limits<int64_t>::min()) {
148   return SizeOfVarintField(field_number, varint::ZigZagEncode(value));
149 }
150 template <typename T>
151 constexpr size_t SizeOfFieldUint32(
152     T field_number, uint32_t value = std::numeric_limits<uint32_t>::max()) {
153   return SizeOfVarintField(field_number, value);
154 }
155 template <typename T>
156 constexpr size_t SizeOfFieldUint64(
157     T field_number, uint64_t value = std::numeric_limits<uint64_t>::max()) {
158   return SizeOfVarintField(field_number, value);
159 }
160 template <typename T>
SizeOfFieldFixed32(T field_number)161 constexpr size_t SizeOfFieldFixed32(T field_number) {
162   return FieldNumberSizeBytes(field_number) + sizeof(uint32_t);
163 }
164 template <typename T>
SizeOfFieldFixed64(T field_number)165 constexpr size_t SizeOfFieldFixed64(T field_number) {
166   return FieldNumberSizeBytes(field_number) + sizeof(uint64_t);
167 }
168 template <typename T>
SizeOfFieldSfixed32(T field_number)169 constexpr size_t SizeOfFieldSfixed32(T field_number) {
170   return FieldNumberSizeBytes(field_number) + sizeof(uint32_t);
171 }
172 template <typename T>
SizeOfFieldSfixed64(T field_number)173 constexpr size_t SizeOfFieldSfixed64(T field_number) {
174   return FieldNumberSizeBytes(field_number) + sizeof(uint64_t);
175 }
176 template <typename T>
SizeOfFieldBool(T field_number)177 constexpr size_t SizeOfFieldBool(T field_number) {
178   return FieldNumberSizeBytes(field_number) + 1;
179 }
180 template <typename T>
SizeOfFieldString(T field_number,uint32_t length_bytes)181 constexpr size_t SizeOfFieldString(T field_number, uint32_t length_bytes) {
182   return SizeOfDelimitedField(field_number, length_bytes);
183 }
184 template <typename T>
SizeOfFieldBytes(T field_number,uint32_t length_bytes)185 constexpr size_t SizeOfFieldBytes(T field_number, uint32_t length_bytes) {
186   return SizeOfDelimitedField(field_number, length_bytes);
187 }
188 template <typename T, typename U = int32_t>
189 constexpr size_t SizeOfFieldEnum(T field_number, U value = static_cast<U>(-1)) {
190   static_assert((std::is_enum<U>() || std::is_integral<U>()) &&
191                     sizeof(U) <= sizeof(uint32_t),
192                 "Enum values must be 32-bit enums or integers");
193   return SizeOfFieldInt32(field_number, static_cast<int32_t>(value));
194 }
195 
196 }  // namespace pw::protobuf
197