diff options
Diffstat (limited to 'pw_protobuf/public/pw_protobuf/find.h')
-rw-r--r-- | pw_protobuf/public/pw_protobuf/find.h | 742 |
1 files changed, 722 insertions, 20 deletions
diff --git a/pw_protobuf/public/pw_protobuf/find.h b/pw_protobuf/public/pw_protobuf/find.h index 64ad94dcc..ceef025ca 100644 --- a/pw_protobuf/public/pw_protobuf/find.h +++ b/pw_protobuf/public/pw_protobuf/find.h @@ -1,4 +1,4 @@ -// Copyright 2020 The Pigweed Authors +// Copyright 2023 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of @@ -13,34 +13,736 @@ // the License. #pragma once +/// @file pw_protobuf/find.h +/// +/// Sometimes, only a single field from a serialized message needs to be read. +/// In these cases, setting up a decoder and iterating through the message is a +/// lot of boilerplate. ``pw_protobuf`` provides convenient ``Find*()`` +/// functions which handle this for you. +/// +/// @note Each call to ``Find*()`` linearly scans through the message. If you +/// have to read multiple fields, it is more efficient to instantiate your own +/// decoder as described above. +/// +/// @code{.cpp} +/// +/// pw::Status PrintCustomerAge(pw::ConstByteSpan serialized_customer) { +/// pw::Result<uint32_t> age = pw::protobuf::FindUint32( +/// serialized_customer, Customer::Fields::kAge); +/// if (!age.ok()) { +/// return age.status(); +/// } +/// +/// PW_LOG_INFO("Customer's age is %u", *age); +/// return pw::OkStatus(); +/// } +/// +/// @endcode + +#include "pw_bytes/span.h" #include "pw_protobuf/decoder.h" +#include "pw_protobuf/stream_decoder.h" +#include "pw_result/result.h" +#include "pw_status/try.h" +#include "pw_string/string.h" namespace pw::protobuf { +namespace internal { + +Status AdvanceToField(Decoder& decoder, uint32_t field_number); +Status AdvanceToField(StreamDecoder& decoder, uint32_t field_number); + +template <typename T, auto ReadFn> +Result<T> Find(ConstByteSpan message, uint32_t field_number) { + T output; + Decoder decoder(message); + PW_TRY(AdvanceToField(decoder, field_number)); + PW_TRY((decoder.*ReadFn)(&output)); + return output; +} + +template <typename T, auto ReadFn> +Result<T> Find(stream::Reader& reader, uint32_t field_number) { + StreamDecoder decoder(reader); + PW_TRY(AdvanceToField(decoder, field_number)); + Result<T> result = (decoder.*ReadFn)(); + + // The StreamDecoder returns a NOT_FOUND if trying to read the wrong type for + // a field. Remap this to FAILED_PRECONDITION for consistency with the + // non-stream Find. + return result.status().IsNotFound() ? Result<T>(Status::FailedPrecondition()) + : result; +} + +} // namespace internal + +/// @brief Scans a serialized protobuf message for a `uint32` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint32_t> FindUint32(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<uint32_t, &Decoder::ReadUint32>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint32_t> FindUint32(ConstByteSpan message, T field) { + return FindUint32(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `uint32` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint32_t> FindUint32(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<uint32_t, &StreamDecoder::ReadUint32>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint32_t> FindUint32(stream::Reader& message_stream, T field) { + return FindUint32(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `int32` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int32_t> FindInt32(ConstByteSpan message, uint32_t field_number) { + return internal::Find<int32_t, &Decoder::ReadInt32>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int32_t> FindInt32(ConstByteSpan message, T field) { + return FindInt32(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `int32` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int32_t> FindInt32(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<int32_t, &StreamDecoder::ReadInt32>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int32_t> FindInt32(stream::Reader& message_stream, T field) { + return FindInt32(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sint32` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int32_t> FindSint32(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<int32_t, &Decoder::ReadSint32>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int32_t> FindSint32(ConstByteSpan message, T field) { + return FindSint32(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sint32` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int32_t> FindSint32(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<int32_t, &StreamDecoder::ReadSint32>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int32_t> FindSint32(stream::Reader& message_stream, T field) { + return FindSint32(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `uint64` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint64_t> FindUint64(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<uint64_t, &Decoder::ReadUint64>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint64_t> FindUint64(ConstByteSpan message, T field) { + return FindUint64(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `uint64` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint64_t> FindUint64(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<uint64_t, &StreamDecoder::ReadUint64>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint64_t> FindUint64(stream::Reader& message_stream, T field) { + return FindUint64(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `int64` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int64_t> FindInt64(ConstByteSpan message, uint32_t field_number) { + return internal::Find<int64_t, &Decoder::ReadInt64>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int64_t> FindInt64(ConstByteSpan message, T field) { + return FindInt64(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `int64` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int64_t> FindInt64(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<int64_t, &StreamDecoder::ReadInt64>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int64_t> FindInt64(stream::Reader& message_stream, T field) { + return FindInt64(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sint64` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int64_t> FindSint64(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<int64_t, &Decoder::ReadSint64>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int64_t> FindSint64(ConstByteSpan message, T field) { + return FindSint64(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sint64` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int64_t> FindSint64(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<int64_t, &StreamDecoder::ReadSint64>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int64_t> FindSint64(stream::Reader& message_stream, T field) { + return FindSint64(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `bool` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<bool> FindBool(ConstByteSpan message, uint32_t field_number) { + return internal::Find<bool, &Decoder::ReadBool>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<bool> FindBool(ConstByteSpan message, T field) { + return FindBool(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `bool` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<bool> FindBool(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<bool, &StreamDecoder::ReadBool>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<bool> FindBool(stream::Reader& message_stream, T field) { + return FindBool(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `fixed32` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint32_t> FindFixed32(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<uint32_t, &Decoder::ReadFixed32>(message, field_number); +} -// DecodeHandler that searches for a specific field in a proto message. If the -// field is found, it cancels the decode operation. Supports searching for -// nested fields by passing in another instance of a FindDecodeHandler for the -// nested message. -class FindDecodeHandler final : public DecodeHandler { - public: - constexpr FindDecodeHandler(uint32_t field_number) - : FindDecodeHandler(field_number, nullptr) {} +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint32_t> FindFixed32(ConstByteSpan message, T field) { + return FindFixed32(message, static_cast<uint32_t>(field)); +} - constexpr FindDecodeHandler(uint32_t field_number, FindDecodeHandler* nested) - : field_number_(field_number), found_(false), nested_handler_(nested) {} +/// @brief Scans a serialized protobuf message for a `fixed32` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint32_t> FindFixed32(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<uint32_t, &StreamDecoder::ReadFixed32>(message_stream, + field_number); +} - Status ProcessField(CallbackDecoder& decoder, uint32_t field_number) override; +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint32_t> FindFixed32(stream::Reader& message_stream, T field) { + return FindFixed32(message_stream, static_cast<uint32_t>(field)); +} - bool found() const { return found_; } +/// @brief Scans a serialized protobuf message for a `fixed64` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint64_t> FindFixed64(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<uint64_t, &Decoder::ReadFixed64>(message, field_number); +} - void set_nested_handler(FindDecodeHandler* nested) { - nested_handler_ = nested; +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint64_t> FindFixed64(ConstByteSpan message, T field) { + return FindFixed64(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `fixed64` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<uint64_t> FindFixed64(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<uint64_t, &StreamDecoder::ReadFixed64>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<uint64_t> FindFixed64(stream::Reader& message_stream, T field) { + return FindFixed64(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sfixed32` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int32_t> FindSfixed32(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<int32_t, &Decoder::ReadSfixed32>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int32_t> FindSfixed32(ConstByteSpan message, T field) { + return FindSfixed32(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sfixed32` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int32_t> FindSfixed32(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<int32_t, &StreamDecoder::ReadSfixed32>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int32_t> FindSfixed32(stream::Reader& message_stream, T field) { + return FindSfixed32(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sfixed64` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int64_t> FindSfixed64(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<int64_t, &Decoder::ReadSfixed64>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int64_t> FindSfixed64(ConstByteSpan message, T field) { + return FindSfixed64(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for an `sfixed64` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<int64_t> FindSfixed64(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<int64_t, &StreamDecoder::ReadSfixed64>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<int64_t> FindSfixed64(stream::Reader& message_stream, T field) { + return FindSfixed64(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `float` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<float> FindFloat(ConstByteSpan message, uint32_t field_number) { + return internal::Find<float, &Decoder::ReadFloat>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<float> FindFloat(ConstByteSpan message, T field) { + return FindFloat(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `float` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<float> FindFloat(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<float, &StreamDecoder::ReadFloat>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<float> FindFloat(stream::Reader& message_stream, T field) { + return FindFloat(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `double` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<double> FindDouble(ConstByteSpan message, uint32_t field_number) { + return internal::Find<double, &Decoder::ReadDouble>(message, field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<double> FindDouble(ConstByteSpan message, T field) { + return FindDouble(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `double` field. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The field if found, or a status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<double> FindDouble(stream::Reader& message_stream, + uint32_t field_number) { + return internal::Find<double, &StreamDecoder::ReadDouble>(message_stream, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<double> FindDouble(stream::Reader& message_stream, T field) { + return FindDouble(message_stream, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `string` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns Subspan of the buffer containing the string field if found, or a +/// status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +/// *NOTE*: The returned string is NOT null-terminated. +inline Result<std::string_view> FindString(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<std::string_view, &Decoder::ReadString>(message, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<std::string_view> FindString(ConstByteSpan message, T field) { + return FindString(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `string`field, copying its +/// data into the provided buffer. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// @param out The buffer to which to write the string. +/// +/// @returns The size of the copied data if found, or a status indicating the +/// error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +/// *NOTE*: The returned string is NOT null-terminated. +inline StatusWithSize FindString(stream::Reader& message_stream, + uint32_t field_number, + span<char> out) { + StreamDecoder decoder(message_stream); + Status status = internal::AdvanceToField(decoder, field_number); + if (!status.ok()) { + return StatusWithSize(status, 0); } + StatusWithSize sws = decoder.ReadString(out); + + // The StreamDecoder returns a NOT_FOUND if trying to read the wrong type for + // a field. Remap this to FAILED_PRECONDITION for consistency with the + // non-stream Find. + return sws.status().IsNotFound() ? StatusWithSize::FailedPrecondition() : sws; +} + +/// @brief Scans a serialized protobuf message for a `string`field, copying its +/// data into the provided buffer. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// @param out String to which to write the found value. +/// +/// @returns The size of the copied data if found, or a status indicating the +/// error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline StatusWithSize FindString(stream::Reader& message_stream, + uint32_t field_number, + InlineString<>& out) { + StatusWithSize sws; + + out.resize_and_overwrite([&](char* data, size_t size) { + sws = FindString(message_stream, field_number, span(data, size)); + return sws.size(); + }); + + return sws; +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline StatusWithSize FindString(stream::Reader& message_stream, + T field, + span<char> out) { + return FindString(message_stream, static_cast<uint32_t>(field), out); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline StatusWithSize FindString(stream::Reader& message_stream, + T field, + InlineString<>& out) { + return FindString(message_stream, static_cast<uint32_t>(field), out); +} + +/// @brief Scans a serialized protobuf message for a `bytes` field. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns Subspan of the buffer containing the bytes field if found, or a +/// status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<ConstByteSpan> FindBytes(ConstByteSpan message, + uint32_t field_number) { + return internal::Find<ConstByteSpan, &Decoder::ReadBytes>(message, + field_number); +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<ConstByteSpan> FindBytes(ConstByteSpan message, T field) { + return FindBytes(message, static_cast<uint32_t>(field)); +} + +/// @brief Scans a serialized protobuf message for a `bytes` field, copying its +/// data into the provided buffer. +/// +/// @param message_stream The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns The size of the copied data if found, or a status indicating the +/// error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline StatusWithSize FindBytes(stream::Reader& message_stream, + uint32_t field_number, + ByteSpan out) { + StreamDecoder decoder(message_stream); + Status status = internal::AdvanceToField(decoder, field_number); + if (!status.ok()) { + return StatusWithSize(status, 0); + } + StatusWithSize sws = decoder.ReadBytes(out); + + // The StreamDecoder returns a NOT_FOUND if trying to read the wrong type for + // a field. Remap this to FAILED_PRECONDITION for consistency with the + // non-stream Find. + return sws.status().IsNotFound() ? StatusWithSize::FailedPrecondition() : sws; +} + +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline StatusWithSize FindBytes(stream::Reader& message_stream, + T field, + ByteSpan out) { + return FindBytes(message_stream, static_cast<uint32_t>(field), out); +} + +/// @brief Scans a serialized protobuf message for a submessage. +/// +/// @param message The serialized message to search. +/// @param field_number Protobuf field number of the field. +/// +/// @returns Subspan of the buffer containing the submessage if found, or a +/// status indicating the error otherwise: +/// * `NOT_FOUND` - The field is not present. +/// * `DATA_LOSS` - The serialized message not a valid protobuf. +/// * `FAILED_PRECONDITION` - The field exists, but is not the correct type. +inline Result<ConstByteSpan> FindSubmessage(ConstByteSpan message, + uint32_t field_number) { + // On the wire, a submessage is identical to bytes. This function exists only + // to clarify users' intent. + return FindBytes(message, field_number); +} - private: - uint32_t field_number_; - bool found_; - FindDecodeHandler* nested_handler_; -}; +template <typename T, typename = std::enable_if_t<std::is_enum_v<T>>> +inline Result<ConstByteSpan> FindSubmessage(ConstByteSpan message, T field) { + return FindSubmessage(message, static_cast<uint32_t>(field)); +} } // namespace pw::protobuf |