blob: 04c8d49e94f9cb4127810efcacd1ecabc3bb13e1 [file] [log] [blame]
// 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 LIB_FIDL_CPP_WIRE_WIRE_TYPES_H_
#define LIB_FIDL_CPP_WIRE_WIRE_TYPES_H_
#include <lib/fidl/cpp/wire/decoded_value.h>
#include <lib/fidl/cpp/wire/envelope.h>
#include <lib/fidl/cpp/wire/incoming_message.h>
#include <lib/fidl/cpp/wire/message_storage.h>
#include <lib/fidl/cpp/wire/object_view.h>
#include <lib/fidl/cpp/wire/outgoing_message.h>
#include <lib/fidl/cpp/wire/status.h>
#include <lib/fidl/cpp/wire/wire_coding_traits.h>
#include <lib/fidl/cpp/wire_format_metadata.h>
#include <lib/fit/inline_any.h>
#include <lib/stdcompat/span.h>
#include <zircon/assert.h>
#include <zircon/fidl.h>
#include <zircon/types.h>
#ifdef __Fuchsia__
#include <lib/fidl/cpp/wire/internal/transport_channel.h>
#else
#include <lib/fidl/cpp/wire/internal/transport_channel_host.h>
#endif // __Fuchsia__
// # Wire domain objects
//
// This header contains forward definitions that are part of wire domain
// objects. The code generator should populate the implementation by generating
// template specializations for each FIDL data type.
namespace fidl {
// |WireTableFrame| stores the envelope header for each field in a table.
// In their current wire format representation, a table is a vector of
// envelopes. The table frame is the vector body portion of the table.
//
// It is recommended that table frames are managed automatically using arenas.
// Only directly construct a table frame when performance is key and arenas are
// insufficient. Once created, a frame can only be used for one single table.
template <typename FidlTable>
struct WireTableFrame;
// |WireTableBuilder| is a helper class for building tables. They're created by
// calling the static |Build(AnyArena&)| on a FIDL wire table type. The table's
// frame and members will be allocated from supplied arena.
//
// Table members are set by passing constructor arguments or |ObjectView|s into
// a builder method named for the member.
//
// To get the built table call |Build()|. The builder must not be used after
// |Build()| has been called.
template <typename FidlTable>
class WireTableBuilder;
// |WireTableExternalBuilder| is a low-level helper class for building tables.
// They're created by calling the static
// |Build(fidl::ObjectView<fidl::WireTableFrame<T>>)| on a FIDL wire table type,
// passing in an externally managed table frame object view.
//
// Table members are set by passing |ObjectView|s into a builder method named
// for the member.
//
// To get the built table call |Build()|. The builder must not be used after
// |Build()| has been called.
template <typename FidlTable>
class WireTableExternalBuilder;
namespace internal {
// |WireTableBaseBuilder| holds the shared code between |WireTableBuilder| and
// |WireTableExternalBuilder|. It shouldn't be used directly.
template <typename FidlTable, typename Builder>
class WireTableBaseBuilder;
// Use it like `template <typename T, EnableIfWireType<T> = nullptr>` to enable the
// declaration only for wire types.
template <typename FidlType>
using EnableIfWireType =
std::enable_if_t<static_cast<bool>(internal::TopLevelCodingTraits<FidlType>::kInlineSize)>*;
// The wire format version used when encoding.
constexpr WireFormatVersion kLLCPPWireFormatVersion = WireFormatVersion::kV2;
// Marker to allow references/pointers to the unowned input objects in OwnedEncodedMessage.
// This enables iovec optimizations but requires the input objects to stay in scope until the
// encoded result has been consumed.
struct AllowUnownedInputRef {};
// This type exists because of class initialization order.
// If these are members of UnownedEncodedMessage, they will be initialized before
// UnownedEncodedMessageBase
template <typename FidlType, typename Transport>
struct UnownedEncodedMessageHandleContainer {
protected:
static constexpr uint32_t kNumHandles =
fidl::internal::ClampedHandleCount<FidlType, fidl::MessageDirection::kSending>();
std::array<zx_handle_t, kNumHandles> handle_storage_;
std::array<typename Transport::HandleMetadata, kNumHandles> handle_metadata_storage_;
};
template <typename Transport>
class UnownedEncodedMessageBase {
public:
zx_status_t status() const { return message_.status(); }
const char* status_string() const { return message_.status_string(); }
bool ok() const { return message_.status() == ZX_OK; }
std::string FormatDescription() const { return message_.FormatDescription(); }
const char* lossy_description() const { return message_.lossy_description(); }
const ::fidl::Status& error() const { return message_.error(); }
::fidl::OutgoingMessage& GetOutgoingMessage() { return message_; }
::fidl::WireFormatMetadata wire_format_metadata() const {
return fidl::internal::WireFormatMetadataForVersion(wire_format_version_);
}
template <typename TransportObject>
void Write(TransportObject&& client, WriteOptions options = {}) {
message_.Write(std::forward<TransportObject>(client), std::move(options));
}
protected:
UnownedEncodedMessageBase(::fidl::internal::WireFormatVersion wire_format_version,
uint32_t iovec_capacity,
::fit::result<::fidl::Error, ::fidl::BufferSpan> backing_buffer,
fidl_handle_t* handles, fidl_handle_metadata_t* handle_metadata,
uint32_t handle_capacity, bool is_transactional, void* value,
size_t inline_size, TopLevelEncodeFn encode_fn)
: message_(likely(backing_buffer.is_ok())
? ::fidl::OutgoingMessage::Create_InternalMayBreak({
.transport_vtable = &Transport::VTable,
.iovecs = iovecs_,
.iovec_capacity = iovec_capacity,
.handles = handles,
.handle_metadata = handle_metadata,
.handle_capacity = handle_capacity,
.backing_buffer = backing_buffer->data,
.backing_buffer_capacity = backing_buffer->capacity,
.is_transactional = is_transactional,
})
: ::fidl::OutgoingMessage{backing_buffer.error_value()}),
wire_format_version_(wire_format_version) {
if (likely(message_.ok())) {
ZX_DEBUG_ASSERT(iovec_capacity <= std::size(iovecs_));
message_.EncodeImpl(wire_format_version, value, inline_size, encode_fn);
}
}
UnownedEncodedMessageBase(const UnownedEncodedMessageBase&) = delete;
UnownedEncodedMessageBase(UnownedEncodedMessageBase&&) = delete;
UnownedEncodedMessageBase* operator=(const UnownedEncodedMessageBase&) = delete;
UnownedEncodedMessageBase* operator=(UnownedEncodedMessageBase&&) = delete;
private:
zx_channel_iovec_t iovecs_[Transport::kNumIovecs];
fidl::OutgoingMessage message_;
fidl::internal::WireFormatVersion wire_format_version_;
};
// This class manages the handles within |FidlType| and encodes the message automatically upon
// construction. Different from |OwnedEncodedMessage|, it takes in a caller-allocated buffer and
// uses that as the backing storage for the message. The buffer must outlive instances of this
// class.
template <typename FidlType, typename Transport = internal::ChannelTransport>
class UnownedEncodedMessage final
: public fidl::internal::UnownedEncodedMessageHandleContainer<FidlType, Transport>,
public fidl::internal::UnownedEncodedMessageBase<Transport> {
using UnownedEncodedMessageHandleContainer =
fidl::internal::UnownedEncodedMessageHandleContainer<FidlType, Transport>;
using UnownedEncodedMessageBase = ::fidl::internal::UnownedEncodedMessageBase<Transport>;
public:
UnownedEncodedMessage(uint8_t* backing_buffer, uint32_t backing_buffer_size, FidlType* response)
: UnownedEncodedMessage(Transport::kNumIovecs, backing_buffer, backing_buffer_size,
response) {}
UnownedEncodedMessage(fidl::internal::WireFormatVersion wire_format_version,
uint8_t* backing_buffer, uint32_t backing_buffer_size, FidlType* response)
: UnownedEncodedMessage(wire_format_version, Transport::kNumIovecs, backing_buffer,
backing_buffer_size, response) {}
UnownedEncodedMessage(uint32_t iovec_capacity, uint8_t* backing_buffer,
uint32_t backing_buffer_size, FidlType* response)
: UnownedEncodedMessage(fidl::internal::kLLCPPWireFormatVersion, iovec_capacity,
backing_buffer, backing_buffer_size, response) {}
// Encodes |value| by allocating a backing buffer from |backing_buffer_allocator|.
UnownedEncodedMessage(fidl::internal::AnyBufferAllocator& backing_buffer_allocator,
uint32_t backing_buffer_size, FidlType* value)
: UnownedEncodedMessage(::fidl::internal::kLLCPPWireFormatVersion, Transport::kNumIovecs,
backing_buffer_allocator.TryAllocate(backing_buffer_size), value) {}
// Encodes |value| using an existing |backing_buffer|.
UnownedEncodedMessage(fidl::internal::WireFormatVersion wire_format_version,
uint32_t iovec_capacity, uint8_t* backing_buffer,
uint32_t backing_buffer_size, FidlType* value)
: UnownedEncodedMessage(wire_format_version, iovec_capacity,
::fit::ok(::fidl::BufferSpan(backing_buffer, backing_buffer_size)),
value) {}
private:
// Core implementation which other constructors delegate to.
UnownedEncodedMessage(::fidl::internal::WireFormatVersion wire_format_version,
uint32_t iovec_capacity,
::fit::result<::fidl::Error, ::fidl::BufferSpan> backing_buffer,
FidlType* value)
: UnownedEncodedMessageBase(
wire_format_version, iovec_capacity, backing_buffer,
UnownedEncodedMessageHandleContainer::handle_storage_.data(),
reinterpret_cast<fidl_handle_metadata_t*>(
UnownedEncodedMessageHandleContainer::handle_metadata_storage_.data()),
UnownedEncodedMessageHandleContainer::kNumHandles,
fidl::IsFidlTransactionalMessage<FidlType>::value, value,
internal::TopLevelCodingTraits<FidlType>::kInlineSize,
internal::MakeTopLevelEncodeFn<FidlType>()) {}
UnownedEncodedMessage(const UnownedEncodedMessage&) = delete;
UnownedEncodedMessage(UnownedEncodedMessage&&) = delete;
UnownedEncodedMessage* operator=(const UnownedEncodedMessage&) = delete;
UnownedEncodedMessage* operator=(UnownedEncodedMessage&&) = delete;
};
class EncodeResult {
public:
EncodeResult() = default;
virtual ~EncodeResult() = default;
virtual ::fidl::OutgoingMessage& message() = 0;
virtual ::fidl::WireFormatMetadata wire_format_metadata() const = 0;
};
// |AnyEncodeResult| can hold and own the encode result of either wire or natural
// domain objects. Its primary purpose is to share the same easy to use public
// encode APIs between wire and natural types, without necessarily incurring
// allocation.
//
// It reserves 2048 bytes of inline space. This is a conservative reservation to
// contain 64 handles and their metadata, plus 512 bytes of inline byte storage
// in the case of wire domain objects, and any miscellaneous bookkeeping. We
// would be able to shrink this by shifting large allocations to the heap
// without changing API.
using AnyEncodeResult = fit::pinned_inline_any<EncodeResult, /* Reserve */ 2048, /* Align */ 16>;
std::vector<uint8_t> ConcatMetadataAndMessage(fidl::WireFormatMetadata metadata,
fidl::OutgoingMessage& message);
fit::result<fidl::Error, std::tuple<fidl::WireFormatMetadata, cpp20::span<uint8_t>>>
SplitMetadataAndMessage(cpp20::span<uint8_t> persisted);
} // namespace internal
// |OwnedEncodeResult| holds a message encoded for writing, along with the
// required storage.
//
// When holding encode results of wire domain objects, |OwnedEncodeResult|
// will try not to heap allocate when the encoded message size is small.
// For this reason, it is not moveable to prevent expensive byte copies.
class OwnedEncodeResult {
public:
// The message encoded for writing, or an error.
//
// Before using the message, one should first check it for encoding errors:
//
// fidl::OwnedEncodeResult result = fidl::StandaloneEncode(...);
// if (!result.message().ok()) {
// // Handle errors...
// fidl::Error error = result.message().error();
// }
//
// Note that as an optimization, handle types and rights are not validated.
// Validation happens when writing to the transport. To proactively perform
// validation, consult |OutgoingToEncodedMessage|.
::fidl::OutgoingMessage& message() { return result_->message(); }
// The format and revision of the encoded FIDL message.
::fidl::WireFormatMetadata wire_format_metadata() const {
return result_->wire_format_metadata();
}
template <typename T, typename... Args>
explicit OwnedEncodeResult(cpp17::in_place_type_t<T> tag, Args&&... args)
: result_(tag, std::forward<Args>(args)...) {}
private:
internal::AnyEncodeResult result_;
};
// Encodes an instance of |FidlType| for use over the Zircon channel transport.
// Supported types are structs, tables, and unions. |FidlType| should be a
// wire domain object.
//
// Handles in the current instance, if any, are moved to the returned
// |OwnedEncodeResult|.
//
// Errors during encoding (e.g. constraint validation) are reflected in the
// |message| of the returned |OwnedEncodeResult|.
//
// Example:
//
// fuchsia_my_lib::wire::SomeType some_value = {...};
// fidl::OwnedEncodeResult encoded = fidl::StandaloneEncode(std::move(some_value));
//
// if (!encoded.message().ok()) {
// // Handle errors...
// }
//
// // Different ways to access the encoded payload:
// // 1. View each iovec (output is always in vectorized chunks).
// for (uint32_t i = 0; i < encoded.message().iovec_actual(); ++i) {
// encoded.message().iovecs()[i].buffer;
// encoded.message().iovecs()[i].capacity;
// }
//
// // 2. Copy the bytes to contiguous storage.
// fidl::OutgoingMessage::CopiedBytes bytes = encoded.message().CopyBytes();
//
template <typename FidlType, internal::EnableIfWireType<FidlType> = nullptr>
OwnedEncodeResult StandaloneEncode(FidlType& value) {
static_assert(IsFidlType<FidlType>::value, "Only FIDL types are supported");
class Encoded final : public internal::EncodeResult {
public:
explicit Encoded(FidlType* value)
: message_(1u, backing_buffer_.data(), static_cast<uint32_t>(backing_buffer_.size()),
value) {}
::fidl::OutgoingMessage& message() override { return message_.GetOutgoingMessage(); }
::fidl::WireFormatMetadata wire_format_metadata() const override {
return message_.wire_format_metadata();
}
private:
::fidl::internal::OutgoingMessageBuffer<FidlType> backing_buffer_;
::fidl::internal::UnownedEncodedMessage<FidlType, internal::ChannelTransport> message_;
};
return OwnedEncodeResult(cpp17::in_place_type_t<Encoded>{}, &value);
}
// |StandaloneInplaceDecode| decodes the |message| to a wire domain
// object |FidlType|. Supported types are structs, tables, and unions.
// Bytes are mutated in-place. The bytes must remain alive if one needs to
// access the decoded result.
//
// Example:
//
// // Create a message referencing an encoded payload.
// fidl::EncodedMessage message = fidl::EncodedMessage::Create(byte_span);
//
// // Decode the message.
// fit::result decoded = fidl::StandaloneInplaceDecode<fuchsia_my_lib::wire::SomeType>(
// std::move(message), wire_format_metadata);
//
// // Use the decoded value.
// if (!decoded.is_ok()) {
// // Handle errors...
// }
// fuchsia_my_lib::wire::SomeType& obj = *decoded.value();
//
// |message| is always consumed. |metadata| informs the wire format of the
// encoded message.
template <typename FidlType>
::fit::result<::fidl::Error, ::fidl::DecodedValue<FidlType>> StandaloneInplaceDecode(
EncodedMessage message, WireFormatMetadata metadata) {
static_assert(IsFidlType<FidlType>::value, "Only FIDL types are supported");
size_t inline_size = internal::TopLevelCodingTraits<FidlType>::kInlineSize;
const internal::TopLevelDecodeFn decode_fn = internal::MakeTopLevelDecodeFn<FidlType>();
const Status status = internal::WireDecode(metadata, inline_size, decode_fn, message);
if (!status.ok()) {
return ::fit::error(status);
}
return ::fit::ok(DecodedValue<FidlType>(reinterpret_cast<FidlType*>(message.bytes().data())));
}
// |Persist| encodes a wire domain object |FidlType| into bytes, following the
// [convention for FIDL data persistence][persistence-convention]: the wire
// format metadata followed by the encoded bytes. |FidlType| needs to satisfy
// these requirements:
//
// - |FidlType| is a wire struct/union/table.
// - |FidlType| is not a resource type.
//
// Example:
//
// fuchsia_my_lib::wire::SomeType obj = ...;
// fit::result result = fidl::Persist(obj);
// if (result.is_error()) {
// // Handle errors...
// }
// // Get the persisted data.
// std::vector<uint8_t>& data = result.value();
//
// [persistence-convention]:
// https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0120_standalone_use_of_fidl_wire_format?hl=en#convention_for_data_persistence
template <typename FidlType, internal::EnableIfWireType<FidlType> = nullptr>
fit::result<fidl::Error, std::vector<uint8_t>> Persist(const FidlType& value) {
static_assert(fidl::IsFidlType<FidlType>::value, "|FidlType| must be a FIDL domain object.");
static_assert(
!fidl::IsResource<FidlType>::value,
"|FidlType| cannot be a resource type. Resources cannot be persisted. "
"If you need to send resource types to another process, consider using a FIDL protocol.");
// Const safety: because there are no handles in non-resource types,
// encoding will not mutate the input tree of values.
fidl::OwnedEncodeResult encoded = fidl::StandaloneEncode(const_cast<FidlType&>(value));
if (!encoded.message().ok()) {
return fit::error(encoded.message().error());
}
return fit::ok(
internal::ConcatMetadataAndMessage(encoded.wire_format_metadata(), encoded.message()));
}
// |InplaceUnpersist| borrows a sequence of bytes stored in the [convention for
// FIDL data persistence][persistence-convention] and decodes them into an
// instance of |FidlType| in-place, mutating those bytes. |FidlType| needs to
// satisfy these requirements:
//
// - |FidlType| is a wire struct/union/table.
// - |FidlType| is not a resource type.
//
// The bytes referenced by |data| must remain alive if one needs to
// access the decoded result.
//
// Example:
//
// std::vector<uint8_t> data = ...;
// fit::result result = fidl::InplaceUnpersist<
// fuchsia_my_lib::wire::SomeType>(cpp20::span(data));
// if (result.is_error()) {
// // Handle errors...
// }
// // Get the decoded object.
// fuchsia_my_lib::wire::SomeType& obj = *result.value();
//
// [persistence-convention]:
// https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/0120_standalone_use_of_fidl_wire_format?hl=en#convention_for_data_persistence
template <typename FidlType>
fit::result<fidl::Error, fidl::ObjectView<FidlType>> InplaceUnpersist(cpp20::span<uint8_t> data) {
static_assert(fidl::IsFidlType<FidlType>::value, "|FidlType| must be a FIDL domain object.");
static_assert(
!fidl::IsResource<FidlType>::value,
"|FidlType| cannot be a resource type. Resources cannot be persisted. "
"If you need to send resource types to another process, consider using a FIDL protocol.");
fit::result split = internal::SplitMetadataAndMessage(data);
if (split.is_error()) {
return split.take_error();
}
auto [metadata, bytes] = split.value();
fit::result decoded =
fidl::StandaloneInplaceDecode<FidlType>(fidl::EncodedMessage::Create(bytes), metadata);
if (decoded.is_error()) {
return decoded.take_error();
}
return fit::ok(fidl::ObjectView<FidlType>::FromExternal(decoded.value().pointer()));
}
} // namespace fidl
#endif // LIB_FIDL_CPP_WIRE_WIRE_TYPES_H_