1 // Copyright 2022 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
15 #include "proto_bloat.h"
16
17 #include <array>
18 #include <cstdint>
19 #include <string_view>
20
21 #include "pw_containers/vector.h"
22 #include "pw_preprocessor/concat.h"
23 #include "pw_protobuf/decoder.h"
24 #include "pw_protobuf/encoder.h"
25 #include "pw_protobuf/internal/codegen.h"
26 #include "pw_protobuf/stream_decoder.h"
27 #include "pw_status/status.h"
28 #include "pw_stream/null_stream.h"
29 #include "pw_stream/stream.h"
30
31 namespace pw::protobuf_size_report {
32 namespace {
33
34 template <typename T>
GetIntegerArray()35 constexpr std::array<T, 4> GetIntegerArray() {
36 return std::array<T, 4>{958736, 2085792374, 0, 42};
37 }
38
39 template <typename T>
GetIntegerVector()40 constexpr Vector<T, 4> GetIntegerVector() {
41 return Vector<T, 4>{958736, 2085792374, 0, 42};
42 }
43
GetBoolArray()44 constexpr std::array<bool, 5> GetBoolArray() {
45 return std::array<bool, 5>{true, false, false, true, true};
46 }
47
GetBoolVector()48 Vector<bool, 5> GetBoolVector() {
49 return Vector<bool, 5>{true, false, false, true, true};
50 }
51
GetFloatArray()52 constexpr std::array<float, 5> GetFloatArray() {
53 return std::array<float, 5>{1.2f, 3.4f, 5.6e20f, 0.0789f, 0.0f};
54 }
55
GetFloatVector()56 Vector<float, 5> GetFloatVector() {
57 return Vector<float, 5>{1.2f, 3.4f, 5.6e20f, 0.0789f, 0.0f};
58 }
59
GetDoubleArray()60 constexpr std::array<double, 5> GetDoubleArray() {
61 return std::array<double, 5>{1.2, 3.4, 5.6e20, 0.0789, 0.0};
62 }
63
GetDoubleVector()64 Vector<double, 5> GetDoubleVector() {
65 return Vector<double, 5>{1.2, 3.4, 5.6e20, 0.0789, 0.0};
66 }
67
68 constexpr std::string_view kTestString("I eat chips too often");
69
70 constexpr protobuf::internal::MessageField kFakeTable[] = {
71 {4567,
72 protobuf::WireType::kDelimited,
73 234567,
74 protobuf::internal::VarintType::kNormal,
75 false,
76 true,
77 true,
78 true,
79 protobuf::internal::CallbackType::kSingleField,
80 260,
81 840245,
82 nullptr},
83 {4567,
84 protobuf::WireType::kDelimited,
85 234567,
86 protobuf::internal::VarintType::kNormal,
87 false,
88 true,
89 true,
90 true,
91 protobuf::internal::CallbackType::kSingleField,
92 260,
93 840245,
94 nullptr}};
95
96 class FakeMessageEncoder : public protobuf::StreamEncoder {
97 public:
FakeMessageEncoder(stream::Writer & writer)98 FakeMessageEncoder(stream::Writer& writer)
99 : protobuf::StreamEncoder(writer, ByteSpan()) {}
DoBloat()100 void DoBloat() { Write(ByteSpan(), kFakeTable).IgnoreError(); }
101 };
102
103 class FakeMessageDecoder : public protobuf::StreamDecoder {
104 public:
FakeMessageDecoder(stream::Reader & reader)105 FakeMessageDecoder(stream::Reader& reader)
106 : protobuf::StreamDecoder(reader) {}
DoBloat()107 void DoBloat() { Read(ByteSpan(), kFakeTable).IgnoreError(); }
108 };
109
CodeToSetUpSizeReportEnvironment()110 void CodeToSetUpSizeReportEnvironment() {
111 [[maybe_unused]] volatile auto arr1 = GetIntegerArray<uint32_t>();
112 [[maybe_unused]] volatile auto arr2 = GetIntegerArray<int32_t>();
113 [[maybe_unused]] volatile auto arr3 = GetIntegerArray<uint64_t>();
114 [[maybe_unused]] volatile auto arr4 = GetIntegerArray<int64_t>();
115
116 [[maybe_unused]] volatile auto vec1 = GetIntegerVector<uint32_t>();
117 [[maybe_unused]] volatile auto vec2 = GetIntegerVector<int32_t>();
118 [[maybe_unused]] volatile auto vec3 = GetIntegerVector<uint64_t>();
119 [[maybe_unused]] volatile auto vec4 = GetIntegerVector<int64_t>();
120
121 [[maybe_unused]] volatile auto bool1 = GetBoolArray();
122 [[maybe_unused]] volatile auto bool2 = GetBoolVector();
123
124 [[maybe_unused]] volatile auto float1 = GetFloatArray();
125 [[maybe_unused]] volatile auto float2 = GetFloatVector();
126
127 [[maybe_unused]] volatile auto double1 = GetDoubleArray();
128 [[maybe_unused]] volatile auto double2 = GetDoubleVector();
129
130 [[maybe_unused]] volatile std::string_view test_string = kTestString;
131
132 [[maybe_unused]] volatile stream::NullStream null_stream;
133 }
134
Dependencies()135 void Dependencies() {
136 std::array<std::byte, 2> buffer;
137 stream::NullStream null_stream;
138 stream::MemoryWriter memory_writer(buffer);
139 memory_writer.Write(buffer).IgnoreError();
140 null_stream.Write(buffer).IgnoreError();
141 stream::MemoryReader memory_reader(buffer);
142 memory_reader.Read(buffer).IgnoreError();
143 }
144
CodeToPullInProtoEncoder()145 void CodeToPullInProtoEncoder() {
146 std::array<std::byte, 1024> buffer;
147 protobuf::MemoryEncoder encoder(buffer);
148
149 encoder.WriteUint32(1, 1).IgnoreError();
150 encoder.WritePackedUint32(1, GetIntegerArray<uint32_t>()).IgnoreError();
151 encoder.WriteRepeatedUint32(1, GetIntegerVector<uint32_t>()).IgnoreError();
152
153 encoder.WriteInt32(1, 1).IgnoreError();
154 encoder.WritePackedInt32(1, GetIntegerArray<int32_t>()).IgnoreError();
155 encoder.WriteRepeatedInt32(1, GetIntegerVector<int32_t>()).IgnoreError();
156
157 encoder.WriteUint64(1, 1).IgnoreError();
158 encoder.WritePackedUint64(1, GetIntegerArray<uint64_t>()).IgnoreError();
159 encoder.WriteRepeatedUint64(1, GetIntegerVector<uint64_t>()).IgnoreError();
160
161 encoder.WriteInt64(1, 1).IgnoreError();
162 encoder.WritePackedInt64(1, GetIntegerArray<int64_t>()).IgnoreError();
163 encoder.WriteRepeatedInt64(1, GetIntegerVector<int64_t>()).IgnoreError();
164
165 encoder.WriteSint32(1, 1).IgnoreError();
166 encoder.WritePackedSint32(1, GetIntegerArray<int32_t>()).IgnoreError();
167 encoder.WriteRepeatedSint32(1, GetIntegerVector<int32_t>()).IgnoreError();
168
169 encoder.WriteSint64(1, 1).IgnoreError();
170 encoder.WritePackedSint64(1, GetIntegerArray<int64_t>()).IgnoreError();
171 encoder.WriteRepeatedSint64(1, GetIntegerVector<int64_t>()).IgnoreError();
172
173 encoder.WriteFixed32(1, 1).IgnoreError();
174 encoder.WritePackedFixed32(1, GetIntegerArray<uint32_t>()).IgnoreError();
175 encoder.WriteRepeatedFixed32(1, GetIntegerVector<uint32_t>()).IgnoreError();
176
177 encoder.WriteFixed64(1, 1).IgnoreError();
178 encoder.WritePackedFixed64(1, GetIntegerArray<uint64_t>()).IgnoreError();
179 encoder.WriteRepeatedFixed64(1, GetIntegerVector<uint64_t>()).IgnoreError();
180
181 encoder.WriteSfixed32(1, 1).IgnoreError();
182 encoder.WritePackedSfixed32(1, GetIntegerArray<int32_t>()).IgnoreError();
183 encoder.WriteRepeatedSfixed32(1, GetIntegerVector<int32_t>()).IgnoreError();
184
185 encoder.WriteSfixed64(1, 1).IgnoreError();
186 encoder.WritePackedSfixed64(1, GetIntegerArray<int64_t>()).IgnoreError();
187 encoder.WriteRepeatedSfixed64(1, GetIntegerVector<int64_t>()).IgnoreError();
188
189 {
190 protobuf::StreamEncoder child = encoder.GetNestedEncoder(0xc01dfee7);
191
192 child.WriteFloat(234, 3.14f).IgnoreError();
193 child.WritePackedFloat(234, GetFloatArray()).IgnoreError();
194 child.WriteRepeatedFloat(234, GetFloatVector()).IgnoreError();
195
196 child.WriteFloat(234, 3.14).IgnoreError();
197 child.WritePackedDouble(234, GetDoubleArray()).IgnoreError();
198 child.WriteRepeatedDouble(234, GetDoubleVector()).IgnoreError();
199
200 child.WriteBool(7, true).IgnoreError();
201 child.WritePackedBool(8, GetBoolArray()).IgnoreError();
202 child.WriteRepeatedBool(8, GetBoolVector()).IgnoreError();
203
204 encoder.WriteBytes(93, as_bytes(span<const double>(GetDoubleArray())))
205 .IgnoreError();
206 encoder.WriteString(21343, kTestString).IgnoreError();
207 }
208
209 stream::NullStream null_stream;
210 protobuf::StreamEncoder stream_encoder(null_stream, buffer);
211 stream_encoder.WriteBytesFromStream(3636, null_stream, 10824, buffer)
212 .IgnoreError();
213 }
214
CodeToPullInTableEncoder()215 void CodeToPullInTableEncoder() {
216 stream::NullStream stream;
217 FakeMessageEncoder fake_encoder(stream);
218 fake_encoder.DoBloat();
219 }
220
CodeToPullInTableDecoder()221 void CodeToPullInTableDecoder() {
222 stream::NullStream stream;
223 FakeMessageDecoder fake_decoder(stream);
224 fake_decoder.DoBloat();
225 }
226
227 #define _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(type_camel_case, underlying_type) \
228 do { \
229 Status status; \
230 Vector<underlying_type, 3> vec; \
231 span<underlying_type> packed_span; \
232 status.Update(decoder.PW_CONCAT(Read, type_camel_case)().status()); \
233 status.Update( \
234 decoder.PW_CONCAT(ReadPacked, type_camel_case)(packed_span).status()); \
235 status.Update(decoder.PW_CONCAT(ReadRepeated, type_camel_case)(vec)); \
236 [[maybe_unused]] volatile bool ok = status.ok(); \
237 } while (0)
238
CodeToPullInProtoStreamDecoder()239 void CodeToPullInProtoStreamDecoder() {
240 stream::NullStream null_stream;
241 protobuf::StreamDecoder decoder(null_stream);
242 decoder.Next().IgnoreError();
243
244 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Int32, int32_t);
245 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Uint32, uint32_t);
246 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Int64, int64_t);
247 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Uint64, uint64_t);
248
249 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sint32, int32_t);
250 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sint64, int64_t);
251
252 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Bool, bool);
253
254 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Fixed32, uint32_t);
255 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Fixed64, uint64_t);
256 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sfixed32, int32_t);
257 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Sfixed64, int64_t);
258
259 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Float, float);
260 _PW_USE_FUNCTIONS_FOR_STREAM_DECODER(Double, double);
261
262 {
263 Status status;
264 span<char> str_out;
265 span<std::byte> bytes_out;
266 status.Update(decoder.ReadString(str_out).status());
267 status.Update(decoder.ReadBytes(bytes_out).status());
268 status.Update(decoder.GetLengthDelimitedPayloadBounds().status());
269 [[maybe_unused]] volatile Result<uint32_t> field_number =
270 decoder.FieldNumber();
271 [[maybe_unused]] volatile protobuf::StreamDecoder::BytesReader
272 bytes_reader = decoder.GetBytesReader();
273 [[maybe_unused]] volatile bool ok = status.ok();
274 }
275 }
276
277 #define _PW_USE_FUNCTIONS_FOR_DECODER(type_camel_case, underlying_type) \
278 do { \
279 Status status; \
280 underlying_type val; \
281 status.Update(decoder.PW_CONCAT(Read, type_camel_case)(&val)); \
282 [[maybe_unused]] volatile bool ok = status.ok(); \
283 } while (0)
284
CodeToPullInProtoDecoder()285 void CodeToPullInProtoDecoder() {
286 std::array<std::byte, 3> buffer = {
287 std::byte(0x01), std::byte(0xff), std::byte(0x08)};
288 protobuf::Decoder decoder(buffer);
289 decoder.Next().IgnoreError();
290
291 _PW_USE_FUNCTIONS_FOR_DECODER(Int32, int32_t);
292 _PW_USE_FUNCTIONS_FOR_DECODER(Uint32, uint32_t);
293 _PW_USE_FUNCTIONS_FOR_DECODER(Int64, int64_t);
294 _PW_USE_FUNCTIONS_FOR_DECODER(Uint64, uint64_t);
295
296 _PW_USE_FUNCTIONS_FOR_DECODER(Sint32, int32_t);
297 _PW_USE_FUNCTIONS_FOR_DECODER(Sint64, int64_t);
298
299 _PW_USE_FUNCTIONS_FOR_DECODER(Bool, bool);
300
301 _PW_USE_FUNCTIONS_FOR_DECODER(Fixed32, uint32_t);
302 _PW_USE_FUNCTIONS_FOR_DECODER(Fixed64, uint64_t);
303 _PW_USE_FUNCTIONS_FOR_DECODER(Sfixed32, int32_t);
304 _PW_USE_FUNCTIONS_FOR_DECODER(Sfixed64, int64_t);
305
306 _PW_USE_FUNCTIONS_FOR_DECODER(Float, float);
307 _PW_USE_FUNCTIONS_FOR_DECODER(Double, double);
308
309 {
310 Status status;
311 std::string_view str_out;
312 span<const std::byte> bytes_out;
313 status.Update(decoder.ReadString(&str_out));
314 status.Update(decoder.ReadBytes(&bytes_out));
315 decoder.Reset(buffer);
316 [[maybe_unused]] volatile uint32_t field_number = decoder.FieldNumber();
317 [[maybe_unused]] volatile bool ok = status.ok();
318 }
319 }
320
321 } // namespace
322
BloatWithBase()323 void BloatWithBase() {
324 CodeToSetUpSizeReportEnvironment();
325 Dependencies();
326 }
327
BloatWithEncoder()328 void BloatWithEncoder() {
329 BloatWithBase();
330 CodeToPullInProtoEncoder();
331 }
332
BloatWithTableEncoder()333 void BloatWithTableEncoder() {
334 BloatWithBase();
335 CodeToPullInTableEncoder();
336 }
337
BloatWithTableDecoder()338 void BloatWithTableDecoder() {
339 BloatWithBase();
340 CodeToPullInTableDecoder();
341 }
342
BloatWithStreamDecoder()343 void BloatWithStreamDecoder() {
344 BloatWithBase();
345 CodeToPullInProtoStreamDecoder();
346 }
347
BloatWithDecoder()348 void BloatWithDecoder() {
349 BloatWithBase();
350 CodeToPullInProtoDecoder();
351 }
352
353 } // namespace pw::protobuf_size_report
354