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