| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_INTERNAL_NATURAL_TYPES_H_ |
| #define SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_INTERNAL_NATURAL_TYPES_H_ |
| |
| #include <lib/fidl/cpp/box.h> |
| #include <lib/fidl/cpp/internal/framework_err.h> |
| #include <lib/fidl/cpp/natural_coding_traits.h> |
| #include <lib/fidl/cpp/natural_encoder.h> |
| #include <lib/fidl/cpp/wire/message.h> |
| #include <lib/fidl/cpp/wire/wire_types.h> |
| #include <lib/fit/result.h> |
| #include <lib/stdcompat/type_traits.h> |
| #include <zircon/fidl.h> |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <utility> |
| #include <variant> |
| |
| // # Natural domain objects |
| // |
| // This header contains forward definitions that are part of natural domain |
| // objects. The code generator should populate the implementation by generating |
| // template specializations for each FIDL data type. |
| namespace fidl { |
| |
| // |Error| is a type alias for when the result of an operation is an error. |
| using Error = Status; |
| |
| namespace internal { |
| |
| class NaturalEncoder; |
| |
| template <typename Constraint, typename Field> |
| void NaturalEnvelopeEncode(NaturalEncoder* encoder, Field* value, size_t offset, |
| size_t recursion_depth) { |
| if (value == nullptr) { |
| // Nothing to encode. |
| return; |
| } |
| |
| const size_t length_before = encoder->CurrentLength(); |
| const size_t handles_before = encoder->CurrentHandleCount(); |
| |
| if (::fidl::internal::NaturalEncodingInlineSize<Field, Constraint>(encoder) <= |
| FIDL_ENVELOPE_INLINING_SIZE_THRESHOLD) { |
| fidl::internal::NaturalEncode<Constraint>(encoder, value, offset, recursion_depth); |
| |
| // Call GetPtr after Encode because the buffer may move. |
| fidl_envelope_t* envelope = encoder->GetPtr<fidl_envelope_t>(offset); |
| envelope->num_handles = static_cast<uint16_t>(encoder->CurrentHandleCount() - handles_before); |
| envelope->flags = FIDL_ENVELOPE_FLAGS_INLINING_MASK; |
| return; |
| } |
| |
| fidl::internal::NaturalEncode<Constraint>( |
| encoder, value, |
| encoder->Alloc(::fidl::internal::NaturalEncodingInlineSize<Field, Constraint>(encoder)), |
| recursion_depth); |
| |
| // Call GetPtr after Encode because the buffer may move. |
| fidl_envelope_t* envelope = encoder->GetPtr<fidl_envelope_t>(offset); |
| envelope->num_bytes = static_cast<uint32_t>(encoder->CurrentLength() - length_before); |
| envelope->num_handles = static_cast<uint16_t>(encoder->CurrentHandleCount() - handles_before); |
| envelope->flags = 0; |
| } |
| |
| template <typename Constraint, typename Field> |
| void NaturalEnvelopeEncodeOptional(NaturalEncoder* encoder, std::optional<Field>* value, |
| size_t offset, size_t recursion_depth) { |
| if (!value->has_value()) { |
| // Nothing to encode. |
| return; |
| } |
| NaturalEnvelopeEncode<Constraint>(encoder, &value->value(), offset, recursion_depth); |
| } |
| |
| template <typename Constraint, typename Field> |
| void NaturalEnvelopeDecode(NaturalDecoder* decoder, Field* value, size_t offset, |
| size_t recursion_depth) { |
| size_t body_size = NaturalDecodingInlineSize<Field, Constraint>(decoder); |
| const size_t length_before = decoder->CurrentLength(); |
| const size_t handles_before = decoder->CurrentHandleCount(); |
| |
| fidl_envelope_t* envelope = decoder->GetPtr<fidl_envelope_t>(offset); |
| if (::fidl::internal::NaturalDecodingInlineSize<Field, Constraint>(decoder) <= |
| FIDL_ENVELOPE_INLINING_SIZE_THRESHOLD) { |
| if (envelope->flags != FIDL_ENVELOPE_FLAGS_INLINING_MASK) { |
| decoder->SetError(kCodingErrorInvalidInlineBit); |
| return; |
| } |
| |
| fidl::internal::NaturalDecode<Constraint>( |
| decoder, value, decoder->GetOffset(&envelope->inline_value), recursion_depth); |
| |
| if (decoder->CurrentHandleCount() != handles_before + envelope->num_handles) { |
| decoder->SetError(kCodingErrorInvalidNumHandlesSpecifiedInEnvelope); |
| } |
| |
| uint32_t padding; |
| switch (::fidl::internal::NaturalDecodingInlineSize<Field, Constraint>(decoder)) { |
| case 1: |
| padding = 0xffffff00; |
| break; |
| case 2: |
| padding = 0xffff0000; |
| break; |
| case 3: |
| padding = 0xff000000; |
| break; |
| case 4: |
| padding = 0x00000000; |
| break; |
| default: |
| __builtin_unreachable(); |
| } |
| if ((*decoder->GetPtr<uint32_t>(offset) & padding) != 0) { |
| decoder->SetError(kCodingErrorInvalidPaddingBytes); |
| } |
| |
| return; |
| } |
| |
| if (envelope->flags != 0) { |
| decoder->SetError(kCodingErrorInvalidInlineBit); |
| return; |
| } |
| |
| size_t body_offset; |
| if (!decoder->Alloc(body_size, &body_offset)) { |
| return; |
| } |
| fidl::internal::NaturalDecode<Constraint>(decoder, value, body_offset, recursion_depth); |
| |
| if (decoder->CurrentHandleCount() != handles_before + envelope->num_handles) { |
| decoder->SetError(kCodingErrorInvalidNumHandlesSpecifiedInEnvelope); |
| } |
| if (decoder->CurrentLength() != length_before + envelope->num_bytes) { |
| decoder->SetError(kCodingErrorInvalidNumBytesSpecifiedInEnvelope); |
| } |
| } |
| |
| template <typename Constraint, typename Field> |
| void NaturalEnvelopeDecodeOptional(NaturalDecoder* decoder, std::optional<Field>* value, |
| size_t offset, size_t recursion_depth) { |
| fidl_envelope_t* envelope = decoder->GetPtr<fidl_envelope_t>(offset); |
| if (*reinterpret_cast<const void* const*>(envelope) == nullptr) { |
| value->reset(); |
| return; |
| } |
| value->emplace(DefaultConstructPossiblyInvalidObject<Field>::Make()); |
| NaturalEnvelopeDecode<Constraint>(decoder, &value->value(), offset, recursion_depth); |
| } |
| |
| // MemberVisitor provides helpers to invoke visitor functions over natural struct and natural table |
| // members. This works because structs and tables have similar shapes in the natural bindings. |
| // There is an instance data member called `storage_` which is a struct containing the member data |
| // and a constexpr std::tuple member called `kMembers`, and each member of that has a `member_ptr` |
| // which is a member pointer into the `storage_` struct. |
| template <typename T> |
| struct MemberVisitor { |
| static constexpr auto kMembers = T::kMembers; |
| static constexpr size_t kNumMembers = std::tuple_size_v<decltype(kMembers)>; |
| |
| // Invokes |func| with |std::integral_constant<size_t, I>| for each |I| in |indexes|. |
| template <typename F, size_t... I> |
| static void Fold(F func, std::index_sequence<I...> indexes) { |
| (func(std::integral_constant<size_t, I>{}), ...); |
| } |
| |
| // Invokes |func| with |std::integral_constant<size_t, I>| for each |I| in |indexes|. |
| // Exits early if a |func| ever returns false in the middle. |
| template <typename F, size_t... I> |
| static void FoldWhile(F func, std::index_sequence<I...> indexes) { |
| (void)(... && func(std::integral_constant<size_t, I>{})); |
| } |
| |
| // Visit each of the members in order while the visitor function returns a truthy value. |
| template <typename U, typename Fn> |
| static void VisitWhile(U value, Fn&& func) { |
| static_assert(std::is_same_v<T*, U> || std::is_same_v<const T*, U>); |
| constexpr size_t N = std::tuple_size_v<decltype(T::kMembers)>; |
| FoldWhile( |
| [func = std::forward<Fn>(func), value](const auto& integral) -> bool { |
| constexpr size_t I = cpp20::remove_cvref_t<decltype(integral)>::value; |
| auto& member_info = std::get<I>(kMembers); |
| auto* member_ptr = &(value->storage_.*(member_info.member_ptr)); |
| return func(member_ptr, member_info); |
| }, |
| std::make_index_sequence<N>{}); |
| } |
| |
| // Visit all of the members in order. |
| template <typename U, typename Fn> |
| static void Visit(U value, Fn&& func) { |
| static_assert(std::is_same_v<T*, U> || std::is_same_v<const T*, U>); |
| VisitWhile(value, [func = std::forward<Fn>(func)](auto member_ptr, auto member_info) { |
| func(member_ptr, member_info); |
| return true; |
| }); |
| } |
| |
| // Visit each of the members of two structs or tables in order while the visitor function returns |
| // a truthy value. |
| template <typename U, typename Fn> |
| static void Visit2While(U value1, U value2, Fn&& func) { |
| static_assert(std::is_same_v<T*, U> || std::is_same_v<const T*, U>); |
| constexpr size_t N = std::tuple_size_v<decltype(T::kMembers)>; |
| FoldWhile( |
| [func = std::forward<Fn>(func), value1, value2](const auto& integral) -> bool { |
| constexpr size_t I = cpp20::remove_cvref_t<decltype(integral)>::value; |
| auto& member_info = std::get<I>(kMembers); |
| auto* member_ptr1 = &(value1->storage_.*(member_info.member_ptr)); |
| auto* member_ptr2 = &(value2->storage_.*(member_info.member_ptr)); |
| return func(member_ptr1, member_ptr2, member_info); |
| }, |
| std::make_index_sequence<N>{}); |
| } |
| |
| // Visit all of the members of two structs or tables in order. |
| template <typename U, typename Fn> |
| static void Visit2(U value1, U value2, Fn&& func) { |
| static_assert(std::is_same_v<T*, U> || std::is_same_v<const T*, U>); |
| Visit2While(value1, value2, |
| [func = std::forward<Fn>(func)](auto member1, auto member2, auto member_info) { |
| func(member1, member2, member_info); |
| return true; |
| }); |
| } |
| }; |
| |
| // This holds metadata about a struct member: a member pointer to the member's value in |
| // the struct's Storage_ type, offsets, and optionally handle information. |
| template <typename T, typename Field_, typename Constraint_> |
| struct NaturalStructMember final { |
| using Field = Field_; |
| using Constraint = Constraint_; |
| Field T::*member_ptr; |
| size_t offset; |
| explicit constexpr NaturalStructMember(Field T::*member_ptr, size_t offset) noexcept |
| : member_ptr(member_ptr), offset(offset) {} |
| }; |
| |
| template <typename MaskType> |
| struct NaturalStructPadding final { |
| // Offset within the struct (start of struct = 0). |
| size_t offset; |
| MaskType mask; |
| |
| [[nodiscard]] bool ValidatePadding(NaturalDecoder* decoder, size_t base_offset) { |
| return (*decoder->GetPtr<MaskType>(base_offset + offset) & mask) == 0; |
| } |
| }; |
| |
| template <typename T, size_t Size> |
| struct NaturalStructCodingTraits { |
| static constexpr size_t kInlineSize = Size; |
| // True iff all fields are memcpy compatible. |
| static constexpr bool are_members_memcpy_compatible = |
| TupleVisitor::All(T::kMembers, [](auto member_info) { |
| using Field = typename std::remove_reference_t<decltype(member_info)>::Field; |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| return NaturalIsMemcpyCompatible<Field, Constraint>(); |
| }); |
| // True iff fields are memcpy compatible and there is no padding. |
| static constexpr bool kIsMemcpyCompatible = |
| are_members_memcpy_compatible && std::tuple_size_v<decltype(T::kPadding)> == 0; |
| |
| static void Encode(NaturalEncoder* encoder, T* value, size_t offset, size_t recursion_depth) { |
| if constexpr (kIsMemcpyCompatible) { |
| memcpy(encoder->GetPtr<T>(offset), value, sizeof(T)); |
| } else { |
| MemberVisitor<T>::Visit(value, [&](auto* member, auto& member_info) -> void { |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| fidl::internal::NaturalEncode<Constraint>(encoder, member, offset + member_info.offset, |
| recursion_depth); |
| }); |
| } |
| } |
| |
| static void Decode(NaturalDecoder* decoder, T* value, size_t offset, size_t recursion_depth) { |
| if constexpr (kIsMemcpyCompatible) { |
| memcpy(value, decoder->GetPtr<T>(offset), sizeof(T)); |
| } else { |
| MemberVisitor<T>::Visit(value, [&](auto* member, auto& member_info) { |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| fidl::internal::NaturalDecode<Constraint>(decoder, member, offset + member_info.offset, |
| recursion_depth); |
| }); |
| |
| auto valid_padding_predicate = [decoder, offset](auto padding) { |
| return padding.ValidatePadding(decoder, offset); |
| }; |
| bool padding_valid = TupleVisitor::All(T::kPadding, valid_padding_predicate); |
| if (!padding_valid) { |
| decoder->SetError(kCodingErrorInvalidPaddingBytes); |
| } |
| } |
| } |
| |
| static bool Equal(const T* struct1, const T* struct2) { |
| bool equal = true; |
| MemberVisitor<T>::Visit2( |
| struct1, struct2, [&](const auto* member1, const auto* member2, auto& member_info) -> bool { |
| if (*member1 != *member2) { |
| equal = false; |
| return false; |
| } |
| return true; |
| }); |
| return equal; |
| } |
| }; |
| |
| template <typename T> |
| struct NaturalEmptyStructCodingTraits { |
| static constexpr size_t kInlineSize = 1; |
| static constexpr bool kIsMemcpyCompatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, T* value, size_t offset, size_t recursion_depth) {} |
| |
| static void Decode(NaturalDecoder* decoder, T* value, size_t offset, size_t recursion_depth) { |
| if (*decoder->GetPtr<uint8_t>(offset) != 0) { |
| decoder->SetError(kCodingErrorInvalidPaddingBytes); |
| } |
| } |
| }; |
| |
| // This holds metadata about a table member: its ordinal, a member pointer to the member's value in |
| // the table's Storage_ type, and optionally handle information. |
| template <typename T, typename Field, typename Constraint_> |
| struct NaturalTableMember final { |
| using Constraint = Constraint_; |
| size_t ordinal; |
| std::optional<Field> T::*member_ptr; |
| constexpr NaturalTableMember(size_t ordinal, std::optional<Field> T::*member_ptr) noexcept |
| : ordinal(ordinal), member_ptr(member_ptr) {} |
| }; |
| |
| template <typename T> |
| struct NaturalTableCodingTraits { |
| static constexpr size_t kInlineSize = 16; |
| static constexpr bool kIsMemcpyCompatible = false; |
| |
| struct TableMemberVisitor : public MemberVisitor<T> { |
| using Base = MemberVisitor<T>; |
| |
| // Visit all of the members in order, calling |func| with the previous member ordinal and the |
| // current member ordinal. The main purpose of this function is to optimize closing unknown |
| // envelopes in tables. The compiler can deterministically omit unknown envelope code paths if |
| // it statically knows that there is no gap between two ordinals. |
| // |
| // |func| should have the signature: |
| // |
| // void func( |
| // Member* member_ptr, |
| // const fidl::internal::NaturalTableMember<...>& member_info, |
| // std::integral_constant<size_t, PreviousOrdinal>, |
| // std::integral_constant<size_t, CurrentOrdinal>, |
| // ); |
| template <typename U, typename Fn> |
| static void VisitPrevAndCurOrdinals(U value, Fn&& func) { |
| static_assert(std::is_same_v<T*, U> || std::is_same_v<const T*, U>); |
| constexpr size_t N = std::tuple_size_v<decltype(T::kMembers)>; |
| Base::template Fold( |
| [func = std::forward<Fn>(func), value](const auto& integral) { |
| constexpr size_t I = cpp20::remove_cvref_t<decltype(integral)>::value; |
| auto& member_info = std::get<I>(Base::kMembers); |
| auto* member_ptr = &(value->storage_.*(member_info.member_ptr)); |
| if constexpr (I == 0) { |
| func(member_ptr, member_info, std::integral_constant<size_t, 0>{}, |
| std::integral_constant<size_t, member_info.ordinal>{}); |
| } else { |
| constexpr auto& prev_member_info = std::get<I - 1>(Base::kMembers); |
| func(member_ptr, member_info, |
| std::integral_constant<size_t, prev_member_info.ordinal>{}, |
| std::integral_constant<size_t, member_info.ordinal>{}); |
| } |
| }, |
| std::make_index_sequence<N>{}); |
| } |
| }; |
| |
| template <class EncoderImpl> |
| static void Encode(EncoderImpl* encoder, T* value, size_t offset, size_t recursion_depth) { |
| size_t max_ordinal = MaxOrdinalPresent(value); |
| fidl_vector_t* vector = encoder->template GetPtr<fidl_vector_t>(offset); |
| vector->count = max_ordinal; |
| vector->data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT); |
| if (max_ordinal == 0) |
| return; |
| if (recursion_depth + 2 > kRecursionDepthMax) { |
| encoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| size_t base = encoder->Alloc(max_ordinal * sizeof(fidl_envelope_t)); |
| MemberVisitor<T>::Visit(value, [&](auto* member, auto& member_info) { |
| size_t offset = base + (member_info.ordinal - 1) * sizeof(fidl_envelope_t); |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| NaturalEnvelopeEncodeOptional<Constraint>(encoder, member, offset, recursion_depth + 2); |
| }); |
| } |
| |
| // Returns the largest ordinal of a present table member. |
| template <size_t I = std::tuple_size_v<decltype(T::kMembers)> - 1> |
| static size_t MaxOrdinalPresent(T* value) { |
| if constexpr (I == -1) { |
| return 0; |
| } else { |
| auto T::Storage_::*member_ptr = std::get<I>(T::kMembers).member_ptr; |
| const auto& member = value->storage_.*member_ptr; |
| if (member.has_value()) { |
| return std::get<I>(T::kMembers).ordinal; |
| } |
| return MaxOrdinalPresent<I - 1>(value); |
| } |
| } |
| |
| // Returns the largest known ordinal in the FIDL schema. |
| constexpr static size_t MaxOrdinalInSchema() { |
| constexpr size_t kMemberCount = std::tuple_size_v<decltype(T::kMembers)>; |
| if constexpr (kMemberCount == 0) { |
| return 0; |
| } else { |
| return std::get<kMemberCount - 1>(T::kMembers).ordinal; |
| } |
| } |
| |
| static void Decode(NaturalDecoder* decoder, T* value, size_t offset, size_t recursion_depth) { |
| fidl_vector_t* encoded = decoder->template GetPtr<fidl_vector_t>(offset); |
| |
| switch (reinterpret_cast<uintptr_t>(encoded->data)) { |
| case FIDL_ALLOC_PRESENT: |
| break; |
| case FIDL_ALLOC_ABSENT: { |
| decoder->SetError(kCodingErrorNullDataReceivedForTable); |
| return; |
| } |
| default: { |
| decoder->SetError(kCodingErrorInvalidPresenceIndicator); |
| return; |
| } |
| } |
| if (recursion_depth + 2 > kRecursionDepthMax) { |
| decoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| size_t count = encoded->count; |
| size_t base; |
| if (!decoder->Alloc(sizeof(fidl_envelope_t) * count, &base)) { |
| return; |
| } |
| auto envelope_offset = [base](size_t ordinal) { |
| return base + (ordinal - 1) * sizeof(fidl_envelope_t); |
| }; |
| |
| // While visiting, if there's a gap in the ordinals, close the envelopes in the gaps. |
| // The gaps between 1 to first member ordinal, and the gaps between last member ordinal |
| // to count, must also be closed. |
| TableMemberVisitor::VisitPrevAndCurOrdinals(value, [=](auto* member, const auto& member_info, |
| const auto& prev_ordinal_integral, |
| const auto& ordinal_integral) { |
| constexpr size_t kPrevOrdinal = cpp20::remove_cvref_t<decltype(prev_ordinal_integral)>::value; |
| constexpr size_t kMemberOrdinal = cpp20::remove_cvref_t<decltype(ordinal_integral)>::value; |
| if (member_info.ordinal > count) { |
| member->reset(); |
| return; |
| } |
| // This block will disappear if there are no reserved ordinals in-between. |
| if constexpr (kPrevOrdinal + 1 < kMemberOrdinal) { |
| for (size_t i = kPrevOrdinal + 1; i < kMemberOrdinal; i++) { |
| decoder->DecodeUnknownEnvelopeOptional(envelope_offset(i)); |
| } |
| } |
| size_t member_offset = envelope_offset(member_info.ordinal); |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| NaturalEnvelopeDecodeOptional<Constraint>(decoder, member, member_offset, recursion_depth); |
| }); |
| for (size_t i = MaxOrdinalInSchema() + 1; i < count + 1; i++) { |
| decoder->DecodeUnknownEnvelopeOptional(envelope_offset(i)); |
| } |
| } |
| |
| static bool Equal(const T* table1, const T* table2) { |
| bool equal = true; |
| MemberVisitor<T>::Visit2(table1, table2, |
| [&](const auto* member1, const auto* member2, auto& member_info) { |
| if (*member1 != *member2) { |
| equal = false; |
| } |
| }); |
| return equal; |
| } |
| }; |
| |
| // This holds metadata about a union member: a member pointer to the member's value in |
| // the unions's Storage_ type, offsets, and optionally handle information. |
| template <typename Constraint_> |
| struct NaturalUnionMember final { |
| using Constraint = Constraint_; |
| }; |
| |
| template <typename T> |
| struct NaturalUnionCodingTraits { |
| static constexpr size_t kInlineSize = 16; |
| static constexpr bool kIsMemcpyCompatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, T* value, size_t offset, size_t recursion_depth) { |
| const size_t index = value->storage_->index(); |
| if (unlikely(index == 0)) { |
| // While it is not possible to construct a flexible union with unknown |
| // data in the natural C++ types, this may happen if e.g. someone tried |
| // to re-encode a flexible union that's received with unknown data. |
| encoder->SetError(kCodingErrorUnknownUnionTag); |
| return; |
| } |
| if (unlikely(recursion_depth + 1 > kRecursionDepthMax)) { |
| encoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| const size_t envelope_offset = offset + offsetof(fidl_union_t, envelope); |
| EncodeMember(encoder, value, envelope_offset, index, recursion_depth + 1); |
| // Call GetPtr after Encode because the buffer may move. |
| fidl_union_t* xunion = encoder->GetPtr<fidl_union_t>(offset); |
| xunion->tag = static_cast<fidl_union_tag_t>(T::IndexToTag(index).value()); |
| } |
| |
| template <size_t I = 1> |
| static void EncodeMember(NaturalEncoder* encoder, T* value, size_t envelope_offset, |
| const size_t index, size_t recursion_depth) { |
| static_assert(I > 0); |
| if constexpr (I < std::variant_size_v<typename T::Storage_>) { |
| if (I == index) { |
| auto& member_info = std::get<I>(T::kMembers); |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| NaturalEnvelopeEncode<Constraint>(encoder, &std::get<I>(*value->storage_), envelope_offset, |
| recursion_depth); |
| return; |
| } |
| return EncodeMember<I + 1>(encoder, value, envelope_offset, index, recursion_depth); |
| } |
| } |
| |
| static void Decode(NaturalDecoder* decoder, T* value, size_t offset, size_t recursion_depth) { |
| fidl_union_t* xunion = decoder->GetPtr<fidl_union_t>(offset); |
| const size_t index = T::TagToIndex(decoder, static_cast<typename T::Tag>(xunion->tag)); |
| if (unlikely(decoder->status() != ZX_OK)) { |
| return; |
| } |
| if (unlikely(recursion_depth + 1 > kRecursionDepthMax)) { |
| decoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| const size_t envelope_offset = offset + offsetof(fidl_union_t, envelope); |
| if (unlikely(index == 0)) { |
| // Flexible unknown envelope. |
| decoder->DecodeUnknownEnvelopeRequired(envelope_offset); |
| if (unlikely(xunion->tag == 0)) { |
| decoder->SetError(kCodingErrorZeroTagButNonZeroEnvelope); |
| } |
| return; |
| } |
| DecodeMember(decoder, value, envelope_offset, index, recursion_depth + 1); |
| } |
| |
| template <size_t I = 1> |
| static void DecodeMember(NaturalDecoder* decoder, T* value, size_t envelope_offset, |
| const size_t index, size_t recursion_depth) { |
| static_assert(I > 0); |
| if constexpr (I < std::variant_size_v<typename T::Storage_>) { |
| if (I == index) { |
| using FieldType = cpp20::remove_cvref_t<decltype(std::get<I>(*value->storage_))>; |
| value->storage_->template emplace<I>( |
| DefaultConstructPossiblyInvalidObject<FieldType>::Make()); |
| auto& member_info = std::get<I>(T::kMembers); |
| using Constraint = typename std::remove_reference_t<decltype(member_info)>::Constraint; |
| NaturalEnvelopeDecode<Constraint>(decoder, &std::get<I>(*value->storage_), envelope_offset, |
| recursion_depth); |
| // Success. |
| return; |
| } |
| // Tail recurse into attempting to decode as the next member. |
| return DecodeMember<I + 1>(decoder, value, envelope_offset, index, recursion_depth); |
| } |
| // Unknown member case is handled in |Decode|. |
| __builtin_unreachable(); |
| } |
| }; |
| |
| // Helpers for deep-copying some types that aren't copy-constructible. |
| // In particular ones that use fidl::Box, a common pattern in natural domain objects. |
| template <typename T> |
| struct NaturalCloneHelper { |
| static T Clone(const T& value) { return value; } |
| }; |
| |
| template <typename T> |
| struct NaturalCloneHelper<std::optional<T>> { |
| static std::optional<T> Clone(const std::optional<T>& value) { |
| if (value) { |
| return std::make_optional<T>(NaturalCloneHelper<T>::Clone(value.value())); |
| } |
| return std::nullopt; |
| } |
| }; |
| |
| template <typename T> |
| struct NaturalCloneHelper<fidl::Box<T>> { |
| static fidl::Box<T> Clone(const fidl::Box<T>& value) { |
| if (value) { |
| return std::make_unique<T>(*value.unique_ptr().get()); |
| } |
| return fidl::Box<T>{}; |
| } |
| }; |
| |
| template <typename T> |
| struct NaturalCloneHelper<std::vector<T>> { |
| static std::vector<T> Clone(const std::vector<T>& value) { |
| std::vector<T> clone{}; |
| clone.reserve(value.size()); |
| std::transform(value.begin(), value.end(), std::back_inserter(clone), |
| [](const T& v) { return NaturalCloneHelper<T>::Clone(v); }); |
| return clone; |
| } |
| }; |
| |
| template <typename T, size_t N, std::size_t... Indexes> |
| std::array<T, N> ArrayCloneHelper(const std::array<T, N>& value, std::index_sequence<Indexes...>) { |
| return std::array<T, N>{NaturalCloneHelper<T>::Clone(std::get<Indexes>(value))...}; |
| } |
| |
| template <typename T, size_t N> |
| struct NaturalCloneHelper<std::array<T, N>> { |
| static std::array<T, N> Clone(const std::array<T, N>& value) { |
| return ArrayCloneHelper(value, std::make_index_sequence<N>()); |
| } |
| }; |
| |
| template <typename T> |
| T NaturalClone(const T& value) { |
| return NaturalCloneHelper<T>::Clone(value); |
| } |
| |
| } // namespace internal |
| |
| } // namespace fidl |
| |
| #endif // SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_INTERNAL_NATURAL_TYPES_H_ |