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