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