• 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 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