1 // Copyright 2023 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 /// @file pw_protobuf/find.h
17 ///
18 /// Sometimes, only a single field from a serialized message needs to be read.
19 /// In these cases, setting up a decoder and iterating through the message is a
20 /// lot of boilerplate. ``pw_protobuf`` provides convenient ``Find*()``
21 /// functions which handle this for you.
22 ///
23 /// @note Each call to ``Find*()`` linearly scans through the message. If you
24 /// have to read multiple fields, it is more efficient to instantiate your own
25 /// decoder as described above.
26 ///
27 /// @code{.cpp}
28 ///
29 /// pw::Status PrintCustomerAge(pw::ConstByteSpan serialized_customer) {
30 /// pw::Result<uint32_t> age = pw::protobuf::FindUint32(
31 /// serialized_customer, Customer::Fields::kAge);
32 /// if (!age.ok()) {
33 /// return age.status();
34 /// }
35 ///
36 /// PW_LOG_INFO("Customer's age is %u", *age);
37 /// return pw::OkStatus();
38 /// }
39 ///
40 /// @endcode
41
42 #include "pw_bytes/span.h"
43 #include "pw_protobuf/decoder.h"
44 #include "pw_protobuf/stream_decoder.h"
45 #include "pw_result/result.h"
46 #include "pw_status/try.h"
47 #include "pw_string/string.h"
48
49 namespace pw::protobuf {
50
51 namespace internal {
52
53 Status AdvanceToField(Decoder& decoder, uint32_t field_number);
54 Status AdvanceToField(StreamDecoder& decoder, uint32_t field_number);
55
56 } // namespace internal
57
58 template <typename T, auto kReadFn>
59 class Finder {
60 public:
Finder(ConstByteSpan message,uint32_t field_number)61 constexpr Finder(ConstByteSpan message, uint32_t field_number)
62 : decoder_(message), field_number_(field_number) {}
63
Next()64 Result<T> Next() {
65 T output;
66 PW_TRY(internal::AdvanceToField(decoder_, field_number_));
67 PW_TRY((decoder_.*kReadFn)(&output));
68 return output;
69 }
70
71 private:
72 Decoder decoder_;
73 uint32_t field_number_;
74 };
75
76 template <typename T, auto kReadFn>
77 class StreamFinder {
78 public:
StreamFinder(stream::Reader & reader,uint32_t field_number)79 constexpr StreamFinder(stream::Reader& reader, uint32_t field_number)
80 : decoder_(reader), field_number_(field_number) {}
81
Next()82 Result<T> Next() {
83 PW_TRY(internal::AdvanceToField(decoder_, field_number_));
84 Result<T> result = (decoder_.*kReadFn)();
85
86 // The StreamDecoder returns a NOT_FOUND if trying to read the wrong type
87 // for a field. Remap this to FAILED_PRECONDITION for consistency with the
88 // non-stream Find.
89 return result.status().IsNotFound()
90 ? Result<T>(Status::FailedPrecondition())
91 : result;
92 }
93
94 private:
95 StreamDecoder decoder_;
96 uint32_t field_number_;
97 };
98
99 template <typename T>
100 class EnumFinder : private Finder<uint32_t, &Decoder::ReadUint32> {
101 public:
102 using Finder::Finder;
103
Next()104 Result<T> Next() {
105 Result<uint32_t> result = Finder::Next();
106 if (!result.ok()) {
107 return result.status();
108 }
109 return static_cast<T>(result.value());
110 }
111 };
112
113 template <typename T>
114 class EnumStreamFinder : private StreamFinder<uint32_t, &Decoder::ReadUint32> {
115 public:
116 using StreamFinder::StreamFinder;
117
Next()118 Result<T> Next() {
119 Result<uint32_t> result = StreamFinder::Next();
120 if (!result.ok()) {
121 return result.status();
122 }
123 return static_cast<T>(result.value());
124 }
125 };
126
127 namespace internal {
128 template <typename T, auto kReadFn>
Find(ConstByteSpan message,uint32_t field_number)129 Result<T> Find(ConstByteSpan message, uint32_t field_number) {
130 Finder<T, kReadFn> finder(message, field_number);
131 return finder.Next();
132 }
133
134 template <typename T, auto kReadFn>
Find(stream::Reader & reader,uint32_t field_number)135 Result<T> Find(stream::Reader& reader, uint32_t field_number) {
136 StreamFinder<T, kReadFn> finder(reader, field_number);
137 return finder.Next();
138 }
139
140 } // namespace internal
141
142 /// @brief Scans a serialized protobuf message for a `uint32` field.
143 ///
144 /// @param message The serialized message to search.
145 /// @param field_number Protobuf field number of the field.
146 ///
147 /// @returns @rst
148 ///
149 /// .. pw-status-codes::
150 ///
151 /// OK: Returns the field.
152 ///
153 /// NOT_FOUND: The field is not present.
154 ///
155 /// DATA_LOSS: The serialized message is not a valid protobuf.
156 ///
157 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
158 ///
159 /// @endrst
FindUint32(ConstByteSpan message,uint32_t field_number)160 inline Result<uint32_t> FindUint32(ConstByteSpan message,
161 uint32_t field_number) {
162 return internal::Find<uint32_t, &Decoder::ReadUint32>(message, field_number);
163 }
164
165 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindUint32(ConstByteSpan message,T field)166 inline Result<uint32_t> FindUint32(ConstByteSpan message, T field) {
167 return FindUint32(message, static_cast<uint32_t>(field));
168 }
169
170 /// @brief Scans a serialized protobuf message for a `uint32` field.
171 ///
172 /// @param message_stream The serialized message to search.
173 /// @param field_number Protobuf field number of the field.
174 ///
175 /// @returns @rst
176 ///
177 /// .. pw-status-codes::
178 ///
179 /// OK: Returns the field.
180 ///
181 /// NOT_FOUND: The field is not present.
182 ///
183 /// DATA_LOSS: The serialized message is not a valid protobuf.
184 ///
185 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
186 ///
187 /// @endrst
FindUint32(stream::Reader & message_stream,uint32_t field_number)188 inline Result<uint32_t> FindUint32(stream::Reader& message_stream,
189 uint32_t field_number) {
190 return internal::Find<uint32_t, &StreamDecoder::ReadUint32>(message_stream,
191 field_number);
192 }
193
194 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindUint32(stream::Reader & message_stream,T field)195 inline Result<uint32_t> FindUint32(stream::Reader& message_stream, T field) {
196 return FindUint32(message_stream, static_cast<uint32_t>(field));
197 }
198
199 using Uint32Finder = Finder<uint32_t, &Decoder::ReadUint32>;
200 using Uint32StreamFinder = StreamFinder<uint32_t, &StreamDecoder::ReadUint32>;
201
202 /// @brief Scans a serialized protobuf message for an `int32` field.
203 ///
204 /// @param message The serialized message to search.
205 /// @param field_number Protobuf field number of the field.
206 ///
207 /// @returns @rst
208 ///
209 /// .. pw-status-codes::
210 ///
211 /// OK: Returns the field.
212 ///
213 /// NOT_FOUND: The field is not present.
214 ///
215 /// DATA_LOSS: The serialized message is not a valid protobuf.
216 ///
217 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
218 ///
219 /// @endrst
FindInt32(ConstByteSpan message,uint32_t field_number)220 inline Result<int32_t> FindInt32(ConstByteSpan message, uint32_t field_number) {
221 return internal::Find<int32_t, &Decoder::ReadInt32>(message, field_number);
222 }
223
224 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindInt32(ConstByteSpan message,T field)225 inline Result<int32_t> FindInt32(ConstByteSpan message, T field) {
226 return FindInt32(message, static_cast<uint32_t>(field));
227 }
228
229 /// @brief Scans a serialized protobuf message for an `int32` field.
230 ///
231 /// @param message_stream The serialized message to search.
232 /// @param field_number Protobuf field number of the field.
233 ///
234 /// @returns @rst
235 ///
236 /// .. pw-status-codes::
237 ///
238 /// OK: Returns the field.
239 ///
240 /// NOT_FOUND: The field is not present.
241 ///
242 /// DATA_LOSS: The serialized message is not a valid protobuf.
243 ///
244 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
245 ///
246 /// @endrst
FindInt32(stream::Reader & message_stream,uint32_t field_number)247 inline Result<int32_t> FindInt32(stream::Reader& message_stream,
248 uint32_t field_number) {
249 return internal::Find<int32_t, &StreamDecoder::ReadInt32>(message_stream,
250 field_number);
251 }
252
253 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindInt32(stream::Reader & message_stream,T field)254 inline Result<int32_t> FindInt32(stream::Reader& message_stream, T field) {
255 return FindInt32(message_stream, static_cast<uint32_t>(field));
256 }
257
258 using Int32Finder = Finder<int32_t, &Decoder::ReadInt32>;
259 using Int32StreamFinder = StreamFinder<int32_t, &StreamDecoder::ReadInt32>;
260
261 /// @brief Scans a serialized protobuf message for an `sint32` field.
262 ///
263 /// @param message The serialized message to search.
264 /// @param field_number Protobuf field number of the field.
265 ///
266 /// @returns @rst
267 ///
268 /// .. pw-status-codes::
269 ///
270 /// OK: Returns the field.
271 ///
272 /// NOT_FOUND: The field is not present.
273 ///
274 /// DATA_LOSS: The serialized message is not a valid protobuf.
275 ///
276 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
277 ///
278 /// @endrst
FindSint32(ConstByteSpan message,uint32_t field_number)279 inline Result<int32_t> FindSint32(ConstByteSpan message,
280 uint32_t field_number) {
281 return internal::Find<int32_t, &Decoder::ReadSint32>(message, field_number);
282 }
283
284 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSint32(ConstByteSpan message,T field)285 inline Result<int32_t> FindSint32(ConstByteSpan message, T field) {
286 return FindSint32(message, static_cast<uint32_t>(field));
287 }
288
289 /// @brief Scans a serialized protobuf message for an `sint32` field.
290 ///
291 /// @param message_stream The serialized message to search.
292 /// @param field_number Protobuf field number of the field.
293 ///
294 /// @returns @rst
295 ///
296 /// .. pw-status-codes::
297 ///
298 /// OK: Returns the field.
299 ///
300 /// NOT_FOUND: The field is not present.
301 ///
302 /// DATA_LOSS: The serialized message is not a valid protobuf.
303 ///
304 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
305 ///
306 /// @endrst
FindSint32(stream::Reader & message_stream,uint32_t field_number)307 inline Result<int32_t> FindSint32(stream::Reader& message_stream,
308 uint32_t field_number) {
309 return internal::Find<int32_t, &StreamDecoder::ReadSint32>(message_stream,
310 field_number);
311 }
312
313 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSint32(stream::Reader & message_stream,T field)314 inline Result<int32_t> FindSint32(stream::Reader& message_stream, T field) {
315 return FindSint32(message_stream, static_cast<uint32_t>(field));
316 }
317
318 using Sint32Finder = Finder<int32_t, &Decoder::ReadSint32>;
319 using Sint32StreamFinder = StreamFinder<int32_t, &StreamDecoder::ReadSint32>;
320
321 /// @brief Scans a serialized protobuf message for a `uint64` field.
322 ///
323 /// @param message The serialized message to search.
324 /// @param field_number Protobuf field number of the field.
325 ///
326 /// @returns @rst
327 ///
328 /// .. pw-status-codes::
329 ///
330 /// OK: Returns the field.
331 ///
332 /// NOT_FOUND: The field is not present.
333 ///
334 /// DATA_LOSS: The serialized message is not a valid protobuf.
335 ///
336 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
337 ///
338 /// @endrst
FindUint64(ConstByteSpan message,uint32_t field_number)339 inline Result<uint64_t> FindUint64(ConstByteSpan message,
340 uint32_t field_number) {
341 return internal::Find<uint64_t, &Decoder::ReadUint64>(message, field_number);
342 }
343
344 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindUint64(ConstByteSpan message,T field)345 inline Result<uint64_t> FindUint64(ConstByteSpan message, T field) {
346 return FindUint64(message, static_cast<uint32_t>(field));
347 }
348
349 /// @brief Scans a serialized protobuf message for a `uint64` field.
350 ///
351 /// @param message_stream The serialized message to search.
352 /// @param field_number Protobuf field number of the field.
353 ///
354 /// @returns @rst
355 ///
356 /// .. pw-status-codes::
357 ///
358 /// OK: Returns the field.
359 ///
360 /// NOT_FOUND: The field is not present.
361 ///
362 /// DATA_LOSS: The serialized message is not a valid protobuf.
363 ///
364 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
365 ///
366 /// @endrst
FindUint64(stream::Reader & message_stream,uint32_t field_number)367 inline Result<uint64_t> FindUint64(stream::Reader& message_stream,
368 uint32_t field_number) {
369 return internal::Find<uint64_t, &StreamDecoder::ReadUint64>(message_stream,
370 field_number);
371 }
372
373 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindUint64(stream::Reader & message_stream,T field)374 inline Result<uint64_t> FindUint64(stream::Reader& message_stream, T field) {
375 return FindUint64(message_stream, static_cast<uint32_t>(field));
376 }
377
378 using Uint64Finder = Finder<uint64_t, &Decoder::ReadUint64>;
379 using Uint64StreamFinder = StreamFinder<uint64_t, &StreamDecoder::ReadUint64>;
380
381 /// @brief Scans a serialized protobuf message for an `int64` field.
382 ///
383 /// @param message The serialized message to search.
384 /// @param field_number Protobuf field number of the field.
385 ///
386 /// @returns @rst
387 ///
388 /// .. pw-status-codes::
389 ///
390 /// OK: Returns the field.
391 ///
392 /// NOT_FOUND: The field is not present.
393 ///
394 /// DATA_LOSS: The serialized message is not a valid protobuf.
395 ///
396 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
397 ///
398 /// @endrst
FindInt64(ConstByteSpan message,uint32_t field_number)399 inline Result<int64_t> FindInt64(ConstByteSpan message, uint32_t field_number) {
400 return internal::Find<int64_t, &Decoder::ReadInt64>(message, field_number);
401 }
402
403 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindInt64(ConstByteSpan message,T field)404 inline Result<int64_t> FindInt64(ConstByteSpan message, T field) {
405 return FindInt64(message, static_cast<uint32_t>(field));
406 }
407
408 /// @brief Scans a serialized protobuf message for an `int64` field.
409 ///
410 /// @param message_stream The serialized message to search.
411 /// @param field_number Protobuf field number of the field.
412 ///
413 /// @returns @rst
414 ///
415 /// .. pw-status-codes::
416 ///
417 /// OK: Returns the field.
418 ///
419 /// NOT_FOUND: The field is not present.
420 ///
421 /// DATA_LOSS: The serialized message is not a valid protobuf.
422 ///
423 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
424 ///
425 /// @endrst
FindInt64(stream::Reader & message_stream,uint32_t field_number)426 inline Result<int64_t> FindInt64(stream::Reader& message_stream,
427 uint32_t field_number) {
428 return internal::Find<int64_t, &StreamDecoder::ReadInt64>(message_stream,
429 field_number);
430 }
431
432 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindInt64(stream::Reader & message_stream,T field)433 inline Result<int64_t> FindInt64(stream::Reader& message_stream, T field) {
434 return FindInt64(message_stream, static_cast<uint32_t>(field));
435 }
436
437 using Int64Finder = Finder<int64_t, &Decoder::ReadInt64>;
438 using Int64StreamFinder = StreamFinder<int64_t, &StreamDecoder::ReadInt64>;
439
440 /// @brief Scans a serialized protobuf message for an `sint64` field.
441 ///
442 /// @param message The serialized message to search.
443 /// @param field_number Protobuf field number of the field.
444 ///
445 /// @returns @rst
446 ///
447 /// .. pw-status-codes::
448 ///
449 /// OK: Returns the field.
450 ///
451 /// NOT_FOUND: The field is not present.
452 ///
453 /// DATA_LOSS: The serialized message is not a valid protobuf.
454 ///
455 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
456 ///
457 /// @endrst
FindSint64(ConstByteSpan message,uint32_t field_number)458 inline Result<int64_t> FindSint64(ConstByteSpan message,
459 uint32_t field_number) {
460 return internal::Find<int64_t, &Decoder::ReadSint64>(message, field_number);
461 }
462
463 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSint64(ConstByteSpan message,T field)464 inline Result<int64_t> FindSint64(ConstByteSpan message, T field) {
465 return FindSint64(message, static_cast<uint32_t>(field));
466 }
467
468 /// @brief Scans a serialized protobuf message for an `sint64` field.
469 ///
470 /// @param message_stream The serialized message to search.
471 /// @param field_number Protobuf field number of the field.
472 ///
473 /// @returns @rst
474 ///
475 /// .. pw-status-codes::
476 ///
477 /// OK: Returns the field.
478 ///
479 /// NOT_FOUND: The field is not present.
480 ///
481 /// DATA_LOSS: The serialized message is not a valid protobuf.
482 ///
483 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
484 ///
485 /// @endrst
FindSint64(stream::Reader & message_stream,uint32_t field_number)486 inline Result<int64_t> FindSint64(stream::Reader& message_stream,
487 uint32_t field_number) {
488 return internal::Find<int64_t, &StreamDecoder::ReadSint64>(message_stream,
489 field_number);
490 }
491
492 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSint64(stream::Reader & message_stream,T field)493 inline Result<int64_t> FindSint64(stream::Reader& message_stream, T field) {
494 return FindSint64(message_stream, static_cast<uint32_t>(field));
495 }
496
497 using Sint64Finder = Finder<int64_t, &Decoder::ReadSint64>;
498 using Sint64StreamFinder = StreamFinder<int64_t, &StreamDecoder::ReadSint64>;
499
500 /// @brief Scans a serialized protobuf message for a `bool` field.
501 ///
502 /// @param message The serialized message to search.
503 /// @param field_number Protobuf field number of the field.
504 ///
505 /// @returns @rst
506 ///
507 /// .. pw-status-codes::
508 ///
509 /// OK: Returns the field.
510 ///
511 /// NOT_FOUND: The field is not present.
512 ///
513 /// DATA_LOSS: The serialized message is not a valid protobuf.
514 ///
515 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
516 ///
517 /// @endrst
FindBool(ConstByteSpan message,uint32_t field_number)518 inline Result<bool> FindBool(ConstByteSpan message, uint32_t field_number) {
519 return internal::Find<bool, &Decoder::ReadBool>(message, field_number);
520 }
521
522 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindBool(ConstByteSpan message,T field)523 inline Result<bool> FindBool(ConstByteSpan message, T field) {
524 return FindBool(message, static_cast<uint32_t>(field));
525 }
526
527 /// @brief Scans a serialized protobuf message for a `bool` field.
528 ///
529 /// @param message_stream The serialized message to search.
530 /// @param field_number Protobuf field number of the field.
531 ///
532 /// @returns @rst
533 ///
534 /// .. pw-status-codes::
535 ///
536 /// OK: Returns the field.
537 ///
538 /// NOT_FOUND: The field is not present.
539 ///
540 /// DATA_LOSS: The serialized message is not a valid protobuf.
541 ///
542 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
543 ///
544 /// @endrst
FindBool(stream::Reader & message_stream,uint32_t field_number)545 inline Result<bool> FindBool(stream::Reader& message_stream,
546 uint32_t field_number) {
547 return internal::Find<bool, &StreamDecoder::ReadBool>(message_stream,
548 field_number);
549 }
550
551 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindBool(stream::Reader & message_stream,T field)552 inline Result<bool> FindBool(stream::Reader& message_stream, T field) {
553 return FindBool(message_stream, static_cast<uint32_t>(field));
554 }
555
556 using BoolFinder = Finder<bool, &Decoder::ReadBool>;
557 using BoolStreamFinder = StreamFinder<bool, &StreamDecoder::ReadBool>;
558
559 /// @brief Scans a serialized protobuf message for a `fixed32` field.
560 ///
561 /// @param message The serialized message to search.
562 /// @param field_number Protobuf field number of the field.
563 ///
564 /// @returns @rst
565 ///
566 /// .. pw-status-codes::
567 ///
568 /// OK: Returns the field.
569 ///
570 /// NOT_FOUND: The field is not present.
571 ///
572 /// DATA_LOSS: The serialized message is not a valid protobuf.
573 ///
574 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
575 ///
576 /// @endrst
FindFixed32(ConstByteSpan message,uint32_t field_number)577 inline Result<uint32_t> FindFixed32(ConstByteSpan message,
578 uint32_t field_number) {
579 return internal::Find<uint32_t, &Decoder::ReadFixed32>(message, field_number);
580 }
581
582 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindFixed32(ConstByteSpan message,T field)583 inline Result<uint32_t> FindFixed32(ConstByteSpan message, T field) {
584 return FindFixed32(message, static_cast<uint32_t>(field));
585 }
586
587 /// @brief Scans a serialized protobuf message for a `fixed32` field.
588 ///
589 /// @param message_stream The serialized message to search.
590 /// @param field_number Protobuf field number of the field.
591 ///
592 /// @returns @rst
593 ///
594 /// .. pw-status-codes::
595 ///
596 /// OK: Returns the field.
597 ///
598 /// NOT_FOUND: The field is not present.
599 ///
600 /// DATA_LOSS: The serialized message is not a valid protobuf.
601 ///
602 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
603 ///
604 /// @endrst
FindFixed32(stream::Reader & message_stream,uint32_t field_number)605 inline Result<uint32_t> FindFixed32(stream::Reader& message_stream,
606 uint32_t field_number) {
607 return internal::Find<uint32_t, &StreamDecoder::ReadFixed32>(message_stream,
608 field_number);
609 }
610
611 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindFixed32(stream::Reader & message_stream,T field)612 inline Result<uint32_t> FindFixed32(stream::Reader& message_stream, T field) {
613 return FindFixed32(message_stream, static_cast<uint32_t>(field));
614 }
615
616 using Fixed32Finder = Finder<uint32_t, &Decoder::ReadFixed32>;
617 using Fixed32StreamFinder = StreamFinder<uint32_t, &StreamDecoder::ReadFixed32>;
618
619 /// @brief Scans a serialized protobuf message for a `fixed64` field.
620 ///
621 /// @param message The serialized message to search.
622 /// @param field_number Protobuf field number of the field.
623 ///
624 /// @returns @rst
625 ///
626 /// .. pw-status-codes::
627 ///
628 /// OK: Returns the field.
629 ///
630 /// NOT_FOUND: The field is not present.
631 ///
632 /// DATA_LOSS: The serialized message is not a valid protobuf.
633 ///
634 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
635 ///
636 /// @endrst
FindFixed64(ConstByteSpan message,uint32_t field_number)637 inline Result<uint64_t> FindFixed64(ConstByteSpan message,
638 uint32_t field_number) {
639 return internal::Find<uint64_t, &Decoder::ReadFixed64>(message, field_number);
640 }
641
642 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindFixed64(ConstByteSpan message,T field)643 inline Result<uint64_t> FindFixed64(ConstByteSpan message, T field) {
644 return FindFixed64(message, static_cast<uint32_t>(field));
645 }
646
647 /// @brief Scans a serialized protobuf message for a `fixed64` field.
648 ///
649 /// @param message_stream The serialized message to search.
650 /// @param field_number Protobuf field number of the field.
651 ///
652 /// @returns @rst
653 ///
654 /// .. pw-status-codes::
655 ///
656 /// OK: Returns the field.
657 ///
658 /// NOT_FOUND: The field is not present.
659 ///
660 /// DATA_LOSS: The serialized message is not a valid protobuf.
661 ///
662 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
663 ///
664 /// @endrst
FindFixed64(stream::Reader & message_stream,uint32_t field_number)665 inline Result<uint64_t> FindFixed64(stream::Reader& message_stream,
666 uint32_t field_number) {
667 return internal::Find<uint64_t, &StreamDecoder::ReadFixed64>(message_stream,
668 field_number);
669 }
670
671 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindFixed64(stream::Reader & message_stream,T field)672 inline Result<uint64_t> FindFixed64(stream::Reader& message_stream, T field) {
673 return FindFixed64(message_stream, static_cast<uint32_t>(field));
674 }
675
676 using Fixed64Finder = Finder<uint64_t, &Decoder::ReadFixed64>;
677 using Fixed64StreamFinder = StreamFinder<uint64_t, &StreamDecoder::ReadFixed64>;
678
679 /// @brief Scans a serialized protobuf message for an `sfixed32` field.
680 ///
681 /// @param message The serialized message to search.
682 /// @param field_number Protobuf field number of the field.
683 ///
684 /// @returns @rst
685 ///
686 /// .. pw-status-codes::
687 ///
688 /// OK: Returns the field.
689 ///
690 /// NOT_FOUND: The field is not present.
691 ///
692 /// DATA_LOSS: The serialized message is not a valid protobuf.
693 ///
694 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
695 ///
696 /// @endrst
FindSfixed32(ConstByteSpan message,uint32_t field_number)697 inline Result<int32_t> FindSfixed32(ConstByteSpan message,
698 uint32_t field_number) {
699 return internal::Find<int32_t, &Decoder::ReadSfixed32>(message, field_number);
700 }
701
702 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSfixed32(ConstByteSpan message,T field)703 inline Result<int32_t> FindSfixed32(ConstByteSpan message, T field) {
704 return FindSfixed32(message, static_cast<uint32_t>(field));
705 }
706
707 /// @brief Scans a serialized protobuf message for an `sfixed32` field.
708 ///
709 /// @param message_stream The serialized message to search.
710 /// @param field_number Protobuf field number of the field.
711 ///
712 /// @returns @rst
713 ///
714 /// .. pw-status-codes::
715 ///
716 /// OK: Returns the field.
717 ///
718 /// NOT_FOUND: The field is not present.
719 ///
720 /// DATA_LOSS: The serialized message is not a valid protobuf.
721 ///
722 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
723 ///
724 /// @endrst
FindSfixed32(stream::Reader & message_stream,uint32_t field_number)725 inline Result<int32_t> FindSfixed32(stream::Reader& message_stream,
726 uint32_t field_number) {
727 return internal::Find<int32_t, &StreamDecoder::ReadSfixed32>(message_stream,
728 field_number);
729 }
730
731 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSfixed32(stream::Reader & message_stream,T field)732 inline Result<int32_t> FindSfixed32(stream::Reader& message_stream, T field) {
733 return FindSfixed32(message_stream, static_cast<uint32_t>(field));
734 }
735
736 using Sfixed32Finder = Finder<int32_t, &Decoder::ReadSfixed32>;
737 using Sfixed32StreamFinder =
738 StreamFinder<int32_t, &StreamDecoder::ReadSfixed32>;
739
740 /// @brief Scans a serialized protobuf message for an `sfixed64` field.
741 ///
742 /// @param message The serialized message to search.
743 /// @param field_number Protobuf field number of the field.
744 ///
745 /// @returns @rst
746 ///
747 /// .. pw-status-codes::
748 ///
749 /// OK: Returns the field.
750 ///
751 /// NOT_FOUND: The field is not present.
752 ///
753 /// DATA_LOSS: The serialized message is not a valid protobuf.
754 ///
755 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
756 ///
757 /// @endrst
FindSfixed64(ConstByteSpan message,uint32_t field_number)758 inline Result<int64_t> FindSfixed64(ConstByteSpan message,
759 uint32_t field_number) {
760 return internal::Find<int64_t, &Decoder::ReadSfixed64>(message, field_number);
761 }
762
763 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSfixed64(ConstByteSpan message,T field)764 inline Result<int64_t> FindSfixed64(ConstByteSpan message, T field) {
765 return FindSfixed64(message, static_cast<uint32_t>(field));
766 }
767
768 /// @brief Scans a serialized protobuf message for an `sfixed64` field.
769 ///
770 /// @param message_stream The serialized message to search.
771 /// @param field_number Protobuf field number of the field.
772 ///
773 /// @returns @rst
774 ///
775 /// .. pw-status-codes::
776 ///
777 /// OK: Returns the field.
778 ///
779 /// NOT_FOUND: The field is not present.
780 ///
781 /// DATA_LOSS: The serialized message is not a valid protobuf.
782 ///
783 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
784 ///
785 /// @endrst
FindSfixed64(stream::Reader & message_stream,uint32_t field_number)786 inline Result<int64_t> FindSfixed64(stream::Reader& message_stream,
787 uint32_t field_number) {
788 return internal::Find<int64_t, &StreamDecoder::ReadSfixed64>(message_stream,
789 field_number);
790 }
791
792 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSfixed64(stream::Reader & message_stream,T field)793 inline Result<int64_t> FindSfixed64(stream::Reader& message_stream, T field) {
794 return FindSfixed64(message_stream, static_cast<uint32_t>(field));
795 }
796
797 using Sfixed64Finder = Finder<int64_t, &Decoder::ReadSfixed64>;
798 using Sfixed64StreamFinder =
799 StreamFinder<int64_t, &StreamDecoder::ReadSfixed64>;
800
801 /// @brief Scans a serialized protobuf message for a `float` field.
802 ///
803 /// @param message The serialized message to search.
804 /// @param field_number Protobuf field number of the field.
805 ///
806 /// @returns @rst
807 ///
808 /// .. pw-status-codes::
809 ///
810 /// OK: Returns the field.
811 ///
812 /// NOT_FOUND: The field is not present.
813 ///
814 /// DATA_LOSS: The serialized message is not a valid protobuf.
815 ///
816 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
817 ///
818 /// @endrst
FindFloat(ConstByteSpan message,uint32_t field_number)819 inline Result<float> FindFloat(ConstByteSpan message, uint32_t field_number) {
820 return internal::Find<float, &Decoder::ReadFloat>(message, field_number);
821 }
822
823 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindFloat(ConstByteSpan message,T field)824 inline Result<float> FindFloat(ConstByteSpan message, T field) {
825 return FindFloat(message, static_cast<uint32_t>(field));
826 }
827
828 /// @brief Scans a serialized protobuf message for a `float` field.
829 ///
830 /// @param message_stream The serialized message to search.
831 /// @param field_number Protobuf field number of the field.
832 ///
833 /// @returns @rst
834 ///
835 /// .. pw-status-codes::
836 ///
837 /// OK: Returns the field.
838 ///
839 /// NOT_FOUND: The field is not present.
840 ///
841 /// DATA_LOSS: The serialized message is not a valid protobuf.
842 ///
843 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
844 ///
845 /// @endrst
FindFloat(stream::Reader & message_stream,uint32_t field_number)846 inline Result<float> FindFloat(stream::Reader& message_stream,
847 uint32_t field_number) {
848 return internal::Find<float, &StreamDecoder::ReadFloat>(message_stream,
849 field_number);
850 }
851
852 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindFloat(stream::Reader & message_stream,T field)853 inline Result<float> FindFloat(stream::Reader& message_stream, T field) {
854 return FindFloat(message_stream, static_cast<uint32_t>(field));
855 }
856
857 using FloatFinder = Finder<float, &Decoder::ReadFloat>;
858 using FloatStreamFinder = StreamFinder<float, &StreamDecoder::ReadFloat>;
859
860 /// @brief Scans a serialized protobuf message for a `double` field.
861 ///
862 /// @param message The serialized message to search.
863 /// @param field_number Protobuf field number of the field.
864 ///
865 /// @returns @rst
866 ///
867 /// .. pw-status-codes::
868 ///
869 /// OK: Returns the field.
870 ///
871 /// NOT_FOUND: The field is not present.
872 ///
873 /// DATA_LOSS: The serialized message is not a valid protobuf.
874 ///
875 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
876 ///
877 /// @endrst
FindDouble(ConstByteSpan message,uint32_t field_number)878 inline Result<double> FindDouble(ConstByteSpan message, uint32_t field_number) {
879 return internal::Find<double, &Decoder::ReadDouble>(message, field_number);
880 }
881
882 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindDouble(ConstByteSpan message,T field)883 inline Result<double> FindDouble(ConstByteSpan message, T field) {
884 return FindDouble(message, static_cast<uint32_t>(field));
885 }
886
887 /// @brief Scans a serialized protobuf message for a `double` field.
888 ///
889 /// @param message_stream The serialized message to search.
890 /// @param field_number Protobuf field number of the field.
891 ///
892 /// @returns @rst
893 ///
894 /// .. pw-status-codes::
895 ///
896 /// OK: Returns the field.
897 ///
898 /// NOT_FOUND: The field is not present.
899 ///
900 /// DATA_LOSS: The serialized message is not a valid protobuf.
901 ///
902 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
903 ///
904 /// @endrst
905
FindDouble(stream::Reader & message_stream,uint32_t field_number)906 inline Result<double> FindDouble(stream::Reader& message_stream,
907 uint32_t field_number) {
908 return internal::Find<double, &StreamDecoder::ReadDouble>(message_stream,
909 field_number);
910 }
911
912 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindDouble(stream::Reader & message_stream,T field)913 inline Result<double> FindDouble(stream::Reader& message_stream, T field) {
914 return FindDouble(message_stream, static_cast<uint32_t>(field));
915 }
916
917 using DoubleFinder = Finder<double, &Decoder::ReadDouble>;
918 using DoubleStreamFinder = StreamFinder<double, &StreamDecoder::ReadDouble>;
919
920 /// @brief Scans a serialized protobuf message for a `string` field.
921 ///
922 /// @param message The serialized message to search.
923 /// @param field_number Protobuf field number of the field.
924 ///
925 /// @returns @rst
926 ///
927 /// .. pw-status-codes::
928 ///
929 /// OK: Returns a subspan of the buffer containing the string field.
930 /// **NOTE**: The returned string is NOT null-terminated.
931 ///
932 /// NOT_FOUND: The field is not present.
933 ///
934 /// DATA_LOSS: The serialized message is not a valid protobuf.
935 ///
936 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
937 ///
938 /// @endrst
FindString(ConstByteSpan message,uint32_t field_number)939 inline Result<std::string_view> FindString(ConstByteSpan message,
940 uint32_t field_number) {
941 return internal::Find<std::string_view, &Decoder::ReadString>(message,
942 field_number);
943 }
944
945 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindString(ConstByteSpan message,T field)946 inline Result<std::string_view> FindString(ConstByteSpan message, T field) {
947 return FindString(message, static_cast<uint32_t>(field));
948 }
949
950 /// @brief Scans a serialized protobuf message for a `string`field, copying its
951 /// data into the provided buffer.
952 ///
953 /// @param message_stream The serialized message to search.
954 /// @param field_number Protobuf field number of the field.
955 /// @param out The buffer to which to write the string.
956 ///
957 /// @returns @rst
958 ///
959 /// .. pw-status-codes::
960 ///
961 /// OK: Returns the size of the copied data.
962 /// **NOTE**: The returned string is NOT null-terminated.
963 ///
964 /// NOT_FOUND: The field is not present.
965 ///
966 /// DATA_LOSS: The serialized message is not a valid protobuf.
967 ///
968 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
969 ///
970 /// @endrst
FindString(stream::Reader & message_stream,uint32_t field_number,span<char> out)971 inline StatusWithSize FindString(stream::Reader& message_stream,
972 uint32_t field_number,
973 span<char> out) {
974 StreamDecoder decoder(message_stream);
975 Status status = internal::AdvanceToField(decoder, field_number);
976 if (!status.ok()) {
977 return StatusWithSize(status, 0);
978 }
979 StatusWithSize sws = decoder.ReadString(out);
980
981 // The StreamDecoder returns a NOT_FOUND if trying to read the wrong type for
982 // a field. Remap this to FAILED_PRECONDITION for consistency with the
983 // non-stream Find.
984 return sws.status().IsNotFound() ? StatusWithSize::FailedPrecondition() : sws;
985 }
986
987 /// @brief Scans a serialized protobuf message for a `string`field, copying its
988 /// data into the provided buffer.
989 ///
990 /// @param message_stream The serialized message to search.
991 /// @param field_number Protobuf field number of the field.
992 /// @param out String to which to write the found value.
993 ///
994 /// @returns @rst
995 ///
996 /// .. pw-status-codes::
997 ///
998 /// OK: Returns the size of the copied data.
999 ///
1000 /// NOT_FOUND: The field is not present.
1001 ///
1002 /// DATA_LOSS: The serialized message is not a valid protobuf.
1003 ///
1004 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
1005 ///
1006 /// @endrst
FindString(stream::Reader & message_stream,uint32_t field_number,InlineString<> & out)1007 inline StatusWithSize FindString(stream::Reader& message_stream,
1008 uint32_t field_number,
1009 InlineString<>& out) {
1010 StatusWithSize sws;
1011
1012 out.resize_and_overwrite([&](char* data, size_t size) {
1013 sws = FindString(message_stream, field_number, span(data, size));
1014 return sws.size();
1015 });
1016
1017 return sws;
1018 }
1019
1020 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindString(stream::Reader & message_stream,T field,span<char> out)1021 inline StatusWithSize FindString(stream::Reader& message_stream,
1022 T field,
1023 span<char> out) {
1024 return FindString(message_stream, static_cast<uint32_t>(field), out);
1025 }
1026
1027 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindString(stream::Reader & message_stream,T field,InlineString<> & out)1028 inline StatusWithSize FindString(stream::Reader& message_stream,
1029 T field,
1030 InlineString<>& out) {
1031 return FindString(message_stream, static_cast<uint32_t>(field), out);
1032 }
1033
1034 using StringFinder = Finder<std::string_view, &Decoder::ReadString>;
1035
1036 /// @brief Scans a serialized protobuf message for a `bytes` field.
1037 ///
1038 /// @param message The serialized message to search.
1039 /// @param field_number Protobuf field number of the field.
1040 ///
1041 /// @returns @rst
1042 ///
1043 /// .. pw-status-codes::
1044 ///
1045 /// OK: Returns the subspan of the buffer containing the bytes field.
1046 ///
1047 /// NOT_FOUND: The field is not present.
1048 ///
1049 /// DATA_LOSS: The serialized message is not a valid protobuf.
1050 ///
1051 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
1052 ///
1053 /// @endrst
FindBytes(ConstByteSpan message,uint32_t field_number)1054 inline Result<ConstByteSpan> FindBytes(ConstByteSpan message,
1055 uint32_t field_number) {
1056 return internal::Find<ConstByteSpan, &Decoder::ReadBytes>(message,
1057 field_number);
1058 }
1059
1060 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindBytes(ConstByteSpan message,T field)1061 inline Result<ConstByteSpan> FindBytes(ConstByteSpan message, T field) {
1062 return FindBytes(message, static_cast<uint32_t>(field));
1063 }
1064
1065 /// @brief Scans a serialized protobuf message for a `bytes` field, copying its
1066 /// data into the provided buffer.
1067 ///
1068 /// @param message_stream The serialized message to search.
1069 /// @param field_number Protobuf field number of the field.
1070 ///
1071 /// @returns @rst
1072 ///
1073 /// .. pw-status-codes::
1074 ///
1075 /// OK: Returns the size of the copied data.
1076 ///
1077 /// NOT_FOUND: The field is not present.
1078 ///
1079 /// DATA_LOSS: The serialized message is not a valid protobuf.
1080 ///
1081 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
1082 ///
1083 /// @endrst
FindBytes(stream::Reader & message_stream,uint32_t field_number,ByteSpan out)1084 inline StatusWithSize FindBytes(stream::Reader& message_stream,
1085 uint32_t field_number,
1086 ByteSpan out) {
1087 StreamDecoder decoder(message_stream);
1088 Status status = internal::AdvanceToField(decoder, field_number);
1089 if (!status.ok()) {
1090 return StatusWithSize(status, 0);
1091 }
1092 StatusWithSize sws = decoder.ReadBytes(out);
1093
1094 // The StreamDecoder returns a NOT_FOUND if trying to read the wrong type for
1095 // a field. Remap this to FAILED_PRECONDITION for consistency with the
1096 // non-stream Find.
1097 return sws.status().IsNotFound() ? StatusWithSize::FailedPrecondition() : sws;
1098 }
1099
1100 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindBytes(stream::Reader & message_stream,T field,ByteSpan out)1101 inline StatusWithSize FindBytes(stream::Reader& message_stream,
1102 T field,
1103 ByteSpan out) {
1104 return FindBytes(message_stream, static_cast<uint32_t>(field), out);
1105 }
1106
1107 using BytesFinder = Finder<ConstByteSpan, &Decoder::ReadBytes>;
1108
1109 /// @brief Scans a serialized protobuf message for a submessage.
1110 ///
1111 /// @param message The serialized message to search.
1112 /// @param field_number Protobuf field number of the field.
1113 ///
1114 /// @returns @rst
1115 ///
1116 /// .. pw-status-codes::
1117 ///
1118 /// OK: Returns the subspan of the buffer containing the submessage.
1119 ///
1120 /// NOT_FOUND: The field is not present.
1121 ///
1122 /// DATA_LOSS: The serialized message is not a valid protobuf.
1123 ///
1124 /// FAILED_PRECONDITION: The field exists, but is not the correct type.
1125 ///
1126 /// @endrst
FindSubmessage(ConstByteSpan message,uint32_t field_number)1127 inline Result<ConstByteSpan> FindSubmessage(ConstByteSpan message,
1128 uint32_t field_number) {
1129 // On the wire, a submessage is identical to bytes. This function exists only
1130 // to clarify users' intent.
1131 return FindBytes(message, field_number);
1132 }
1133
1134 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindSubmessage(ConstByteSpan message,T field)1135 inline Result<ConstByteSpan> FindSubmessage(ConstByteSpan message, T field) {
1136 return FindSubmessage(message, static_cast<uint32_t>(field));
1137 }
1138
1139 /// Returns a span containing the raw bytes of the value.
FindRaw(ConstByteSpan message,uint32_t field_number)1140 inline Result<ConstByteSpan> FindRaw(ConstByteSpan message,
1141 uint32_t field_number) {
1142 Decoder decoder(message);
1143 PW_TRY(internal::AdvanceToField(decoder, field_number));
1144 return decoder.RawFieldBytes();
1145 }
1146
1147 template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
FindRaw(ConstByteSpan message,T field)1148 Result<ConstByteSpan> FindRaw(ConstByteSpan message, T field) {
1149 return FindRaw(message, static_cast<uint32_t>(field));
1150 }
1151
1152 } // namespace pw::protobuf
1153