| // Copyright 2022 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_NATURAL_CODING_TRAITS_H_ |
| #define SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_NATURAL_CODING_TRAITS_H_ |
| |
| #include <lib/fidl/cpp/box.h> |
| #include <lib/fidl/cpp/natural_decoder.h> |
| #include <lib/fidl/cpp/natural_encoder.h> |
| #include <lib/fidl/cpp/wire/traits.h> |
| #include <lib/stdcompat/optional.h> |
| |
| #ifdef __Fuchsia__ |
| #include <lib/zx/channel.h> |
| #endif // __Fuchsia__ |
| |
| #include <string> |
| |
| namespace fidl::internal { |
| |
| // |
| // Default construction helpers |
| // |
| // All natural domain objects are default constructible with sensible default |
| // states, with the exception of strict unions. There is no good default for a |
| // strict union. During decoding, we use this collection of traits and markers |
| // to help the FIDL runtime construct temporarily an invalid strict union (or |
| // aggregates thereof), and never give the object to the user if decoding errors |
| // prevent us from properly initializing it with a member. |
| // |
| |
| // |DefaultConstructPossiblyInvalidObjectTag| selects a constructor that is only |
| // usable by the FIDL runtime, and may construct the object in an invalid state. |
| // This is useful in decoding where we must first construct the object and then |
| // populate it with valid contents. |
| struct DefaultConstructPossiblyInvalidObjectTag {}; |
| |
| // |DefaultConstructPossiblyInvalidObject| has a |Make| that makes an instance |
| // of |T| without any external inputs. For objects containing strict unions, the |
| // strict unions will be constructed in an invalid state. |
| // |
| // It is a way to expose the dangerous powers of invalid default construction |
| // only to the FIDL runtime, and forcing end users to start their objects with |
| // valid state. |
| template <typename T> |
| struct DefaultConstructPossiblyInvalidObject { |
| static constexpr T Make() { |
| if constexpr (std::is_default_constructible_v<T>) { |
| return T{}; |
| } else { |
| return T{DefaultConstructPossiblyInvalidObjectTag{}}; |
| } |
| } |
| }; |
| |
| template <typename E, size_t N> |
| struct DefaultConstructPossiblyInvalidObject<std::array<E, N>> { |
| static constexpr std::array<E, N> Make() { |
| if constexpr (std::is_trivially_default_constructible_v<E>) { |
| // Shortcut for arrays of primitives etc. This does not guarantee copy |
| // elision, but eliminates zero initialization. Experimentation shows that |
| // usually the compiler can eliminate the copy of `t`. |
| std::array<E, N> t; |
| return t; |
| } else { |
| if constexpr (std::is_default_constructible_v<E>) { |
| // Shortcut for default constructible arrays. |
| return std::array<E, N>{}; |
| } else { |
| return ArrayMaker<N>::MakeArray( |
| [] { return E{DefaultConstructPossiblyInvalidObject<E>::Make()}; }); |
| } |
| } |
| } |
| |
| private: |
| template <std::size_t Idx = N> |
| struct ArrayMaker { |
| template <typename ElementMaker, typename... Ts> |
| static std::array<E, N> MakeArray(ElementMaker maker, Ts... tail) { |
| return ArrayMaker<Idx - 1>::MakeArray(maker, maker(), std::move(tail)...); |
| } |
| }; |
| |
| template <> |
| struct ArrayMaker<0> { |
| template <typename ElementMaker, typename... Ts> |
| static std::array<E, N> MakeArray(ElementMaker maker, Ts... tail) { |
| return std::array<E, N>{std::move(tail)...}; |
| } |
| }; |
| }; |
| |
| // |
| // Constraints |
| // |
| |
| struct NaturalCodingConstraintEmpty {}; |
| |
| template <zx_obj_type_t ObjType, zx_rights_t Rights, bool IsOptional> |
| struct NaturalCodingConstraintHandle { |
| static constexpr zx_obj_type_t obj_type = ObjType; |
| static constexpr zx_rights_t rights = Rights; |
| static constexpr bool is_optional = IsOptional; |
| }; |
| |
| template <size_t Limit = std::numeric_limits<size_t>::max()> |
| struct NaturalCodingConstraintString { |
| static constexpr size_t limit = Limit; |
| }; |
| |
| template <typename Inner_, size_t Limit = std::numeric_limits<size_t>::max()> |
| struct NaturalCodingConstraintVector { |
| using Inner = Inner_; |
| static constexpr size_t limit = Limit; |
| }; |
| |
| static constexpr size_t kRecursionDepthInitial = 0; |
| static constexpr size_t kRecursionDepthMax = FIDL_RECURSION_DEPTH; |
| |
| template <typename T, typename Constraint, class Enable = void> |
| struct NaturalCodingTraits; |
| |
| template <typename T, typename Constraint> |
| size_t NaturalEncodingInlineSize(NaturalEncoder* encoder) { |
| ZX_DEBUG_ASSERT(encoder->wire_format() == ::fidl::internal::WireFormatVersion::kV2); |
| return NaturalCodingTraits<T, Constraint>::inline_size_v2; |
| } |
| |
| template <typename T, typename Constraint> |
| size_t NaturalDecodingInlineSize(NaturalDecoder* decoder) { |
| ZX_DEBUG_ASSERT(decoder->wire_format() == ::fidl::internal::WireFormatVersion::kV2); |
| return NaturalCodingTraits<T, Constraint>::inline_size_v2; |
| } |
| |
| template <typename T, typename Constraint> |
| constexpr bool NaturalIsMemcpyCompatible() { |
| return NaturalCodingTraits<T, Constraint>::is_memcpy_compatible; |
| } |
| |
| template <typename T> |
| struct NaturalCodingTraits<T, NaturalCodingConstraintEmpty, |
| typename std::enable_if<IsPrimitive<T>::value>::type> { |
| static constexpr size_t inline_size_v2 = sizeof(T); |
| static constexpr bool is_memcpy_compatible = true; |
| |
| static void Encode(NaturalEncoder* encoder, const T* value, size_t offset, |
| size_t recursion_depth) { |
| *encoder->template GetPtr<T>(offset) = *value; |
| } |
| static void Decode(NaturalDecoder* decoder, T* value, size_t offset, size_t recursion_depth) { |
| *value = *decoder->template GetPtr<T>(offset); |
| } |
| }; |
| |
| template <> |
| struct NaturalCodingTraits<bool, NaturalCodingConstraintEmpty> { |
| static constexpr size_t inline_size_v2 = sizeof(bool); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, const bool* value, size_t offset, |
| size_t recursion_depth) { |
| *encoder->template GetPtr<bool>(offset) = *value; |
| } |
| static void Encode(NaturalEncoder* encoder, const std::vector<bool>::iterator& value, |
| size_t offset, size_t recursion_depth) { |
| bool b = *value; |
| Encode(encoder, &b, offset, recursion_depth); |
| } |
| static void Decode(NaturalDecoder* decoder, bool* value, size_t offset, size_t recursion_depth) { |
| uint8_t uintval = *decoder->template GetPtr<uint8_t>(offset); |
| if (!(uintval == 0 || uintval == 1)) { |
| decoder->SetError(kCodingErrorInvalidBoolean); |
| return; |
| } |
| |
| *value = *decoder->template GetPtr<bool>(offset); |
| } |
| static void Decode(NaturalDecoder* decoder, const std::vector<bool>::iterator& value, |
| size_t offset, size_t recursion_depth) { |
| bool b; |
| Decode(decoder, &b, offset, recursion_depth); |
| *value = b; |
| } |
| }; |
| |
| template <bool Value> |
| class NaturalUseStdCopy {}; |
| |
| template <typename T, typename Constraint> |
| void NaturalEncodeVectorBody(NaturalUseStdCopy<true>, NaturalEncoder* encoder, |
| typename std::vector<T>::iterator in_begin, |
| typename std::vector<T>::iterator in_end, size_t out_offset, |
| size_t recursion_depth) { |
| static_assert(NaturalCodingTraits<T, Constraint>::inline_size_v2 == sizeof(T), |
| "stride doesn't match object size"); |
| std::copy(in_begin, in_end, encoder->template GetPtr<T>(out_offset)); |
| } |
| |
| template <typename T, typename Constraint> |
| void NaturalEncodeVectorBody(NaturalUseStdCopy<false>, NaturalEncoder* encoder, |
| typename std::vector<T>::iterator in_begin, |
| typename std::vector<T>::iterator in_end, size_t out_offset, |
| size_t recursion_depth) { |
| size_t stride = NaturalEncodingInlineSize<T, Constraint>(encoder); |
| for (typename std::vector<T>::iterator in_it = in_begin; in_it != in_end; |
| in_it++, out_offset += stride) { |
| NaturalCodingTraits<T, Constraint>::Encode(encoder, &*in_it, out_offset, recursion_depth); |
| } |
| } |
| |
| template <typename T, typename Constraint> |
| void NaturalDecodeVectorBody(NaturalUseStdCopy<true>, NaturalDecoder* decoder, |
| size_t in_begin_offset, size_t in_end_offset, std::vector<T>* out, |
| size_t count, size_t recursion_depth) { |
| static_assert(NaturalCodingTraits<T, Constraint>::inline_size_v2 == sizeof(T), |
| "stride doesn't match object size"); |
| *out = std::vector<T>(decoder->template GetPtr<T>(in_begin_offset), |
| decoder->template GetPtr<T>(in_end_offset)); |
| } |
| |
| template <typename T, typename Constraint> |
| void NaturalDecodeVectorBody(NaturalUseStdCopy<false>, NaturalDecoder* decoder, |
| size_t in_begin_offset, size_t in_end_offset, std::vector<T>* out, |
| size_t count, size_t recursion_depth) { |
| out->reserve(count); |
| size_t stride = NaturalDecodingInlineSize<T, Constraint>(decoder); |
| size_t in_offset = in_begin_offset; |
| size_t index = 0; |
| for (; in_offset < in_end_offset; in_offset += stride, index++) { |
| // Avoid materializing a |T| if it's already default constructible. |
| // Large aggregates (e.g. arrays of primitives) can be constructed with little stack usage, |
| // in debug builds or ASAN builds. |
| // Note that in practice the two code paths should be equivalent under optimization. |
| if constexpr (std::is_default_constructible_v<T>) { |
| out->emplace_back(); |
| } else { |
| out->emplace_back(DefaultConstructPossiblyInvalidObject<T>::Make()); |
| } |
| NaturalCodingTraits<T, Constraint>::Decode(decoder, &(*out)[index], in_offset, recursion_depth); |
| } |
| } |
| |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits<::std::vector<T>, Constraint> { |
| using InnerConstraint = typename Constraint::Inner; |
| static constexpr size_t inline_size_v2 = sizeof(fidl_vector_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, ::std::vector<T>* value, size_t offset, |
| size_t recursion_depth) { |
| size_t count = value->size(); |
| if (count > Constraint::limit) { |
| encoder->SetError(kCodingErrorVectorLimitExceeded); |
| // Proceed to visit vector elements and collect handles to close. |
| } |
| if (recursion_depth + 1 > kRecursionDepthMax) { |
| encoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| fidl_vector_t* vector = encoder->template GetPtr<fidl_vector_t>(offset); |
| vector->count = count; |
| vector->data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT); |
| size_t stride = NaturalEncodingInlineSize<T, InnerConstraint>(encoder); |
| size_t base = encoder->Alloc(count * stride); |
| internal::NaturalEncodeVectorBody<T, InnerConstraint>( |
| internal::NaturalUseStdCopy<NaturalIsMemcpyCompatible<T, InnerConstraint>()>(), encoder, |
| value->begin(), value->end(), base, recursion_depth + 1); |
| } |
| static void Decode(NaturalDecoder* decoder, ::std::vector<T>* value, size_t offset, |
| size_t recursion_depth) { |
| fidl_vector_t* encoded = decoder->template GetPtr<fidl_vector_t>(offset); |
| size_t count = encoded->count; |
| if (count > Constraint::limit) { |
| decoder->SetError(kCodingErrorVectorLimitExceeded); |
| return; |
| } |
| switch (reinterpret_cast<uintptr_t>(encoded->data)) { |
| case FIDL_ALLOC_PRESENT: |
| break; |
| case FIDL_ALLOC_ABSENT: { |
| decoder->SetError(kCodingErrorNullDataReceivedForNonNullableVector); |
| return; |
| } |
| default: { |
| decoder->SetError(kCodingErrorInvalidPresenceIndicator); |
| return; |
| } |
| } |
| if (recursion_depth + 1 > kRecursionDepthMax) { |
| decoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| size_t stride = NaturalDecodingInlineSize<T, InnerConstraint>(decoder); |
| size_t base; |
| if (!decoder->Alloc(count * stride, &base)) { |
| return; |
| } |
| internal::NaturalDecodeVectorBody<T, InnerConstraint>( |
| internal::NaturalUseStdCopy<NaturalIsMemcpyCompatible<T, InnerConstraint>()>(), decoder, |
| base, base + stride * count, value, count, recursion_depth + 1); |
| } |
| }; |
| |
| template <typename T, size_t N, typename Constraint> |
| struct NaturalCodingTraits<::std::array<T, N>, Constraint> { |
| static constexpr size_t inline_size_v2 = NaturalCodingTraits<T, Constraint>::inline_size_v2 * N; |
| static constexpr bool is_memcpy_compatible = |
| NaturalCodingTraits<T, Constraint>::is_memcpy_compatible; |
| |
| static void Encode(NaturalEncoder* encoder, std::array<T, N>* value, size_t offset, |
| size_t recursion_depth) { |
| size_t stride = NaturalEncodingInlineSize<T, Constraint>(encoder); |
| if constexpr (is_memcpy_compatible) { |
| memcpy(encoder->template GetPtr<void>(offset), value->data(), N * stride); |
| } else { |
| for (size_t i = 0; i < N; ++i) { |
| NaturalCodingTraits<T, Constraint>::Encode(encoder, &value->at(i), offset + i * stride, |
| recursion_depth); |
| } |
| } |
| } |
| static void Decode(NaturalDecoder* decoder, std::array<T, N>* value, size_t offset, |
| size_t recursion_depth) { |
| size_t stride = NaturalDecodingInlineSize<T, Constraint>(decoder); |
| if constexpr (is_memcpy_compatible) { |
| memcpy(value->data(), decoder->template GetPtr<void>(offset), N * stride); |
| } else { |
| for (size_t i = 0; i < N; ++i) { |
| NaturalCodingTraits<T, Constraint>::Decode(decoder, &value->at(i), offset + i * stride, |
| recursion_depth); |
| } |
| } |
| } |
| }; |
| |
| #ifdef __Fuchsia__ |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits< |
| T, Constraint, typename std::enable_if<std::is_base_of<zx::object_base, T>::value>::type> { |
| static constexpr size_t inline_size_v2 = sizeof(zx_handle_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, zx::object_base* value, size_t offset, |
| size_t recursion_depth) { |
| encoder->EncodeHandle(value->release(), |
| { |
| .obj_type = Constraint::obj_type, |
| .rights = Constraint::rights, |
| }, |
| offset, Constraint::is_optional); |
| } |
| static void Decode(NaturalDecoder* decoder, zx::object_base* value, size_t offset, |
| size_t recursion_depth) { |
| zx_handle_t handle = ZX_HANDLE_INVALID; |
| decoder->DecodeHandle(&handle, |
| { |
| .obj_type = Constraint::obj_type, |
| .rights = Constraint::rights, |
| }, |
| offset, Constraint::is_optional); |
| value->reset(handle); |
| } |
| }; |
| #endif // __Fuchsia__ |
| |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits<cpp17::optional<std::vector<T>>, Constraint> { |
| static constexpr size_t inline_size_v2 = sizeof(fidl_vector_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, cpp17::optional<std::vector<T>>* value, size_t offset, |
| size_t recursion_depth) { |
| if (value->has_value()) { |
| fidl::internal::NaturalCodingTraits<std::vector<T>, Constraint>::Encode( |
| encoder, &value->value(), offset, recursion_depth); |
| return; |
| } |
| fidl_vector_t* vec = encoder->template GetPtr<fidl_vector_t>(offset); |
| vec->count = 0; |
| vec->data = reinterpret_cast<char*>(FIDL_ALLOC_ABSENT); |
| } |
| static void Decode(NaturalDecoder* decoder, cpp17::optional<std::vector<T>>* value, size_t offset, |
| size_t recursion_depth) { |
| fidl_vector_t* vec = decoder->template GetPtr<fidl_vector_t>(offset); |
| switch (reinterpret_cast<uintptr_t>(vec->data)) { |
| case FIDL_ALLOC_PRESENT: { |
| std::vector<T> unwrapped; |
| fidl::internal::NaturalCodingTraits<std::vector<T>, Constraint>::Decode( |
| decoder, &unwrapped, offset, recursion_depth); |
| value->emplace(std::move(unwrapped)); |
| return; |
| } |
| case FIDL_ALLOC_ABSENT: { |
| if (vec->count != 0) { |
| decoder->SetError(kCodingErrorNullVectorMustHaveSizeZero); |
| return; |
| } |
| value->reset(); |
| return; |
| } |
| default: { |
| decoder->SetError(kCodingErrorInvalidPresenceIndicator); |
| return; |
| } |
| } |
| } |
| }; |
| |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits<fidl::Box<T>, Constraint, |
| typename std::enable_if<!IsUnion<T>::value>::type> { |
| static constexpr size_t inline_size_v2 = sizeof(uintptr_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, fidl::Box<T>* value, size_t offset, |
| size_t recursion_depth) { |
| if (value->unique_ptr().get()) { |
| if (recursion_depth + 1 > kRecursionDepthMax) { |
| encoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| *encoder->template GetPtr<uintptr_t>(offset) = FIDL_ALLOC_PRESENT; |
| |
| size_t alloc_size = NaturalEncodingInlineSize<T, Constraint>(encoder); |
| NaturalCodingTraits<T, Constraint>::Encode(encoder, value->unique_ptr().get(), |
| encoder->Alloc(alloc_size), recursion_depth + 1); |
| } else { |
| *encoder->template GetPtr<uintptr_t>(offset) = FIDL_ALLOC_ABSENT; |
| } |
| } |
| static void Decode(NaturalDecoder* decoder, fidl::Box<T>* value, size_t offset, |
| size_t recursion_depth) { |
| uintptr_t ptr = *decoder->template GetPtr<uintptr_t>(offset); |
| if (!ptr) { |
| return value->reset(); |
| } |
| |
| if (recursion_depth + 1 > kRecursionDepthMax) { |
| decoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| *value = std::make_unique<T>(DefaultConstructPossiblyInvalidObjectTag{}); |
| size_t alloc_size = NaturalDecodingInlineSize<T, Constraint>(decoder); |
| size_t body_offset; |
| if (!decoder->Alloc(alloc_size, &body_offset)) { |
| return; |
| } |
| NaturalCodingTraits<T, Constraint>::Decode(decoder, value->unique_ptr().get(), body_offset, |
| recursion_depth + 1); |
| } |
| }; |
| |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits<fidl::Box<T>, Constraint, |
| typename std::enable_if<IsUnion<T>::value>::type> { |
| static constexpr size_t inline_size_v2 = sizeof(fidl_union_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, fidl::Box<T>* value, size_t offset, |
| size_t recursion_depth) { |
| if (*value) { |
| NaturalCodingTraits<T, Constraint>::Encode(encoder, value->unique_ptr().get(), offset, |
| recursion_depth); |
| return; |
| } |
| |
| // Buffer is zero-initialized. |
| } |
| |
| static void Decode(NaturalDecoder* decoder, fidl::Box<T>* value, size_t offset, |
| size_t recursion_depth) { |
| fidl_union_t* u = decoder->template GetPtr<fidl_union_t>(offset); |
| if (u->tag == 0) { |
| if (likely(FidlIsZeroEnvelope(&u->envelope))) { |
| *value = nullptr; |
| return; |
| } |
| decoder->SetError(kCodingErrorZeroTagButNonZeroEnvelope); |
| } |
| *value = std::make_unique<T>(DefaultConstructPossiblyInvalidObject<T>::Make()); |
| NaturalCodingTraits<T, Constraint>::Decode(decoder, value->unique_ptr().get(), offset, |
| recursion_depth); |
| } |
| }; |
| |
| template <typename Constraint> |
| struct NaturalCodingTraits<::std::string, Constraint> final { |
| static constexpr size_t inline_size_v2 = sizeof(fidl_string_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, std::string* value, size_t offset, |
| size_t recursion_depth) { |
| const size_t size = value->size(); |
| if (value->size() > Constraint::limit) { |
| encoder->SetError(kCodingErrorStringLimitExceeded); |
| return; |
| } |
| bool valid = utfutils_is_valid_utf8(value->data(), value->size()); |
| if (!valid) { |
| encoder->SetError(kCodingErrorStringNotValidUtf8); |
| return; |
| } |
| if (recursion_depth + 1 > kRecursionDepthMax) { |
| encoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| fidl_string_t* string = encoder->template GetPtr<fidl_string_t>(offset); |
| string->size = size; |
| string->data = reinterpret_cast<char*>(FIDL_ALLOC_PRESENT); |
| size_t base = encoder->Alloc(size); |
| char* payload = encoder->template GetPtr<char>(base); |
| memcpy(payload, value->data(), size); |
| } |
| static void Decode(NaturalDecoder* decoder, std::string* value, size_t offset, |
| size_t recursion_depth) { |
| if (recursion_depth + 1 > kRecursionDepthMax) { |
| decoder->SetError(kCodingErrorRecursionDepthExceeded); |
| return; |
| } |
| |
| fidl_string_t* string = decoder->template GetPtr<fidl_string_t>(offset); |
| if (string->size > Constraint::limit) { |
| decoder->SetError(kCodingErrorStringLimitExceeded); |
| return; |
| } |
| switch (reinterpret_cast<uintptr_t>(string->data)) { |
| case FIDL_ALLOC_PRESENT: |
| break; |
| case FIDL_ALLOC_ABSENT: { |
| decoder->SetError(kCodingErrorNullDataReceivedForNonNullableString); |
| return; |
| } |
| default: { |
| decoder->SetError(kCodingErrorInvalidPresenceIndicator); |
| return; |
| } |
| } |
| size_t base; |
| if (!decoder->Alloc(string->size, &base)) { |
| return; |
| } |
| char* payload = decoder->template GetPtr<char>(base); |
| bool valid = utfutils_is_valid_utf8(payload, string->size); |
| if (!valid) { |
| decoder->SetError(kCodingErrorStringNotValidUtf8); |
| return; |
| } |
| *value = std::string(payload, string->size); |
| } |
| }; |
| |
| template <typename Constraint> |
| struct NaturalCodingTraits<cpp17::optional<std::string>, Constraint> { |
| static constexpr size_t inline_size_v2 = sizeof(fidl_string_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, cpp17::optional<std::string>* value, size_t offset, |
| size_t recursion_depth) { |
| if (value->has_value()) { |
| fidl::internal::NaturalCodingTraits<std::string, Constraint>::Encode(encoder, &value->value(), |
| offset, recursion_depth); |
| return; |
| } |
| fidl_string_t* string = encoder->template GetPtr<fidl_string_t>(offset); |
| string->size = 0; |
| string->data = reinterpret_cast<char*>(FIDL_ALLOC_ABSENT); |
| } |
| static void Decode(NaturalDecoder* decoder, cpp17::optional<std::string>* value, size_t offset, |
| size_t recursion_depth) { |
| fidl_string_t* string = decoder->template GetPtr<fidl_string_t>(offset); |
| switch (reinterpret_cast<uintptr_t>(string->data)) { |
| case FIDL_ALLOC_PRESENT: { |
| std::string unwrapped; |
| fidl::internal::NaturalCodingTraits<std::string, Constraint>::Decode( |
| decoder, &unwrapped, offset, recursion_depth); |
| value->emplace(unwrapped); |
| return; |
| } |
| case FIDL_ALLOC_ABSENT: { |
| if (string->size != 0) { |
| decoder->SetError(kCodingErrorNullStringMustHaveSizeZero); |
| return; |
| } |
| value->reset(); |
| return; |
| } |
| default: { |
| decoder->SetError(kCodingErrorInvalidPresenceIndicator); |
| return; |
| } |
| } |
| } |
| }; |
| |
| #ifdef __Fuchsia__ |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits<ClientEnd<T>, Constraint> { |
| static constexpr size_t inline_size_v1_no_ee = sizeof(zx_handle_t); |
| static constexpr size_t inline_size_v2 = sizeof(zx_handle_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, ClientEnd<T>* value, size_t offset, |
| size_t recursion_depth) { |
| encoder->EncodeHandle(value->TakeChannel().release(), |
| { |
| .obj_type = Constraint::obj_type, |
| .rights = Constraint::rights, |
| }, |
| offset, Constraint::is_optional); |
| } |
| |
| static void Decode(NaturalDecoder* decoder, ClientEnd<T>* value, size_t offset, |
| size_t recursion_depth) { |
| zx_handle_t handle = ZX_HANDLE_INVALID; |
| decoder->DecodeHandle(&handle, |
| { |
| .obj_type = Constraint::obj_type, |
| .rights = Constraint::rights, |
| }, |
| offset, Constraint::is_optional); |
| *value = ClientEnd<T>(zx::channel(handle)); |
| } |
| }; |
| |
| template <typename T, typename Constraint> |
| struct NaturalCodingTraits<ServerEnd<T>, Constraint> { |
| static constexpr size_t inline_size_v1_no_ee = sizeof(zx_handle_t); |
| static constexpr size_t inline_size_v2 = sizeof(zx_handle_t); |
| static constexpr bool is_memcpy_compatible = false; |
| |
| static void Encode(NaturalEncoder* encoder, ServerEnd<T>* value, size_t offset, |
| size_t recursion_depth) { |
| encoder->EncodeHandle(value->TakeChannel().release(), |
| { |
| .obj_type = Constraint::obj_type, |
| .rights = Constraint::rights, |
| }, |
| offset, Constraint::is_optional); |
| } |
| |
| static void Decode(NaturalDecoder* decoder, ServerEnd<T>* value, size_t offset, |
| size_t recursion_depth) { |
| zx_handle_t handle = ZX_HANDLE_INVALID; |
| decoder->DecodeHandle(&handle, |
| { |
| .obj_type = Constraint::obj_type, |
| .rights = Constraint::rights, |
| }, |
| offset, Constraint::is_optional); |
| *value = ServerEnd<T>(zx::channel(handle)); |
| } |
| }; |
| #endif // __Fuchsia__ |
| |
| template <typename Constraint, typename T> |
| void NaturalEncode(NaturalEncoder* encoder, T* value, size_t offset, size_t recursion_depth) { |
| NaturalCodingTraits<T, Constraint>::Encode(encoder, value, offset, recursion_depth); |
| } |
| |
| template <typename Constraint, typename T> |
| void NaturalDecode(NaturalDecoder* decoder, T* value, size_t offset, size_t recursion_depth) { |
| NaturalCodingTraits<T, Constraint>::Decode(decoder, value, offset, recursion_depth); |
| } |
| |
| using NaturalTopLevelDecodeFn = void (*)(NaturalDecoder*, void* value, size_t offset); |
| |
| template <typename FidlType> |
| constexpr NaturalTopLevelDecodeFn MakeNaturalTopLevelDecodeFn() { |
| return [](NaturalDecoder* decoder, void* value, size_t offset) { |
| ::fidl::internal::NaturalCodingTraits<FidlType, NaturalCodingConstraintEmpty>::Decode( |
| decoder, reinterpret_cast<FidlType*>(value), offset, kRecursionDepthInitial); |
| }; |
| } |
| |
| // Create a |fidl::NaturalDecoder| and decode the inputted |message|. |
| // In case of error, handles in |message| are consumed. |
| // In case of success, handle values will be embedded in the natural type |value|; the caller |
| // must ensure that |value| points to an instance of default constructed natural type that |
| // matches the one decoded by |decode_fn|. |
| // This is the top-level function to call to perform decoding using coding traits. |
| fidl::Status NaturalDecode(::fidl::WireFormatMetadata metadata, size_t inline_size, |
| NaturalTopLevelDecodeFn decode_fn, ::fidl::EncodedMessage& message, |
| void* value); |
| |
| } // namespace fidl::internal |
| |
| #endif // SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_NATURAL_CODING_TRAITS_H_ |