blob: 7182db8ad165978754784b6e9947d2fe0629bac8 [file] [log] [blame]
// Copyright 2018 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_LLCPP_INCLUDE_LIB_FIDL_LLCPP_TRAITS_H_
#define LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_TRAITS_H_
#include <lib/fidl/internal.h>
#include <lib/fidl/llcpp/internal/transport.h>
#include <zircon/fidl.h>
#ifdef __Fuchsia__
#include <lib/zx/object.h>
#endif
#include <cstdint>
#include <type_traits>
// Defines type traits used in the low-level C++ binding.
//
// The contracts of a FIDL type |T| are as follows:
//
// |IsFidlType<T>| resolves to std::true_type.
// |IsFidlTransactionalMessage<T>| resolves to std::true_type iff |T| is a transactional message.
// |IsResource<T>| resolves to std::true_type iff |T| is a resource type.
//
// |TypeTraits<T>| will be specialized for the type, see documentation below for
// what fields it will contain.
namespace fidl {
namespace internal {
// A type trait that indicates whether the given type is a primitive FIDL type.
template <typename T>
struct IsPrimitive : public std::false_type {};
// Const-ness is not significant for determining IsPrimitive.
template <typename T>
struct IsPrimitive<const T> : public IsPrimitive<T> {};
// clang-format off
template <> struct IsPrimitive<bool> : public std::true_type {};
template <> struct IsPrimitive<char> : public std::true_type {};
template <> struct IsPrimitive<uint8_t> : public std::true_type {};
template <> struct IsPrimitive<uint16_t> : public std::true_type {};
template <> struct IsPrimitive<uint32_t> : public std::true_type {};
template <> struct IsPrimitive<uint64_t> : public std::true_type {};
template <> struct IsPrimitive<int8_t> : public std::true_type {};
template <> struct IsPrimitive<int16_t> : public std::true_type {};
template <> struct IsPrimitive<int32_t> : public std::true_type {};
template <> struct IsPrimitive<int64_t> : public std::true_type {};
template <> struct IsPrimitive<float> : public std::true_type {};
template <> struct IsPrimitive<double> : public std::true_type {};
// clang-format on
} // namespace internal
// A type trait that indicates whether the given type is a request/response type
// i.e. has a FIDL message header.
template <typename T>
struct IsFidlTransactionalMessage : public std::false_type {};
// Code-gen will explicitly conform the generated FIDL transactional messages to
// IsFidlTransactionalMessage.
// A type trait that indicates whether the given type is allowed to appear in
// generated binding APIs and can be encoded/decoded.
// As a start, all handle types are supported.
#ifdef __Fuchsia__
template <typename T>
struct IsFidlType : public std::is_base_of<zx::object_base, T> {};
#else
template <typename T>
struct IsFidlType : public std::false_type {};
#endif
// Const-ness is not significant for determining IsFidlType.
template <typename T>
struct IsFidlType<const T> : public IsFidlType<T> {};
// clang-format off
// Specialize for primitives
template <> struct IsFidlType<bool> : public std::true_type {};
template <> struct IsFidlType<uint8_t> : public std::true_type {};
template <> struct IsFidlType<uint16_t> : public std::true_type {};
template <> struct IsFidlType<uint32_t> : public std::true_type {};
template <> struct IsFidlType<uint64_t> : public std::true_type {};
template <> struct IsFidlType<int8_t> : public std::true_type {};
template <> struct IsFidlType<int16_t> : public std::true_type {};
template <> struct IsFidlType<int32_t> : public std::true_type {};
template <> struct IsFidlType<int64_t> : public std::true_type {};
template <> struct IsFidlType<float> : public std::true_type {};
template <> struct IsFidlType<double> : public std::true_type {};
// clang-format on
// A type trait that indicates whether the given type is a resource type
// i.e. can contain handles.
#ifdef __Fuchsia__
template <typename T>
struct IsResource : public std::is_base_of<zx::object_base, T> {
static_assert(IsFidlType<T>::value, "IsResource only defined on FIDL types.");
};
#else
template <typename T>
struct IsResource : public std::false_type {
static_assert(IsFidlType<T>::value, "IsResource only defined on FIDL types.");
};
#endif
// Code-gen will explicitly conform the generated FIDL types to IsResource.
// A type trait that contains several properties of fidl types which are
// important for encoding/decoding.
//
// |kMaxNumHandles| is a uint32_t specifying the upper bound on the number of
// contained handles.
// |kPrimarySize| is a uint32_t specifying the size in bytes of the inline part
// of the message.
// |kMaxOutOfLine| is a uint32_t specifying the upper bound on the out-of-line
// message size. It is std::numeric_limits<uint32_t>::max() if
// |T| is unbounded.
// |kHasPointer| is a boolean specifying if the structure contains pointer
// indirections, hence requires linearization when sending.
//
// Additionally, if |T| is a transactional message:
//
// |kHasFlexibleEnvelope| is a bool specifying if this message contains a
// flexible xunion or a flexible table.
// |kMessageKind| identifies if this message is a request or a response.
// If undefined, the type may be used either as a request
// or a response.
template <typename T>
struct TypeTraits {};
// Code-gen will explicitly conform the generated FIDL types to TypeTraits.
// Const-ness is not significant for determining TypeTraits.
template <typename T>
struct TypeTraits<const T> : public TypeTraits<T> {};
template <typename T>
struct DeprecatedCTypeTraits;
// String
class StringView;
template <>
struct IsFidlType<StringView> : public std::true_type {};
template <typename T, typename Enable = void>
struct IsStringView : std::false_type {};
template <>
struct IsStringView<fidl::StringView, void> : std::true_type {};
template <typename T>
struct IsStringView<
T, typename std::enable_if<IsStringView<typename std::remove_const<T>::type>::value>::type>
: std::true_type {};
// Vector (conditional on element)
template <typename E>
class VectorView;
template <typename E>
struct IsFidlType<VectorView<E>> : public IsFidlType<E> {};
template <typename T, typename Enable = void>
struct IsVectorView : std::false_type {};
template <typename E>
struct IsResource<VectorView<E>> : public IsResource<E> {};
template <typename T>
struct IsVectorView<VectorView<T>, void> : std::true_type {};
template <typename T>
struct IsVectorView<
T, typename std::enable_if<IsVectorView<typename std::remove_const<T>::type>::value>::type>
: std::true_type {};
// Code-gen is responsible for emiting specializations for these traits
template <typename T, typename Enable = void>
struct IsTable : std::false_type {};
template <typename MaybeConstTable>
struct IsTable<MaybeConstTable,
typename std::enable_if<
std::is_const<MaybeConstTable>::value &&
IsTable<typename std::remove_const<MaybeConstTable>::type>::value>::type>
: std::true_type {};
template <typename T, typename Enable = void>
struct IsUnion : std::false_type {};
template <typename MaybeConstUnion>
struct IsUnion<MaybeConstUnion,
typename std::enable_if<
std::is_const<MaybeConstUnion>::value &&
IsUnion<typename std::remove_const<MaybeConstUnion>::type>::value>::type>
: std::true_type {};
template <typename T, typename Enable = void>
struct IsStruct : std::false_type {};
template <typename MaybeConstStruct>
struct IsStruct<MaybeConstStruct,
typename std::enable_if<
std::is_const<MaybeConstStruct>::value &&
IsStruct<typename std::remove_const<MaybeConstStruct>::type>::value>::type>
: std::true_type {};
// IsFidlObject is a subset of IsFidlType referring to user defined aggregate types, i.e.
// tables, unions, and structs.
template <typename T, typename Enable = void>
struct IsFidlObject : std::false_type {};
template <typename T>
struct IsFidlObject<
T, typename std::enable_if<IsTable<T>::value || IsUnion<T>::value || IsStruct<T>::value>::type>
: std::true_type {};
// Indicates if the parameterized type contains a handle.
template <typename T, typename Enable = void>
struct ContainsHandle;
// clang-format off
// Specialize for primitives
template <> struct ContainsHandle<bool> : public std::false_type {};
template <> struct ContainsHandle<uint8_t> : public std::false_type {};
template <> struct ContainsHandle<uint16_t> : public std::false_type {};
template <> struct ContainsHandle<uint32_t> : public std::false_type {};
template <> struct ContainsHandle<uint64_t> : public std::false_type {};
template <> struct ContainsHandle<int8_t> : public std::false_type {};
template <> struct ContainsHandle<int16_t> : public std::false_type {};
template <> struct ContainsHandle<int32_t> : public std::false_type {};
template <> struct ContainsHandle<int64_t> : public std::false_type {};
template <> struct ContainsHandle<float> : public std::false_type {};
template <> struct ContainsHandle<double> : public std::false_type {};
// clang-format on
#if __Fuchsia__
template <typename T>
struct ContainsHandle<T, typename std::enable_if<std::is_base_of<zx::object_base, T>::value>::type>
: std::true_type {};
#endif
template <typename Protocol>
class ClientEnd;
template <typename Protocol>
class UnownedClientEnd;
template <typename Protocol>
class ServerEnd;
template <typename Protocol>
struct IsResource<ClientEnd<Protocol>> : public std::true_type {};
template <typename Protocol>
struct IsResource<ServerEnd<Protocol>> : public std::true_type {};
template <typename Protocol>
struct ContainsHandle<ClientEnd<Protocol>> : std::true_type {};
template <typename Protocol>
struct ContainsHandle<ServerEnd<Protocol>> : std::true_type {};
template <typename T>
struct ContainsHandle<
T, typename std::enable_if<IsFidlType<T>::value && TypeTraits<T>::kMaxNumHandles == 0>::type>
: std::false_type {};
template <typename T>
struct ContainsHandle<
T, typename std::enable_if<(IsFidlType<T>::value && TypeTraits<T>::kMaxNumHandles > 0)>::type>
: std::true_type {};
template <typename T, size_t N>
struct Array;
template <typename T, size_t N>
struct ContainsHandle<Array<T, N>> : ContainsHandle<T> {};
template <typename T, size_t N>
struct IsResource<Array<T, N>> : public IsResource<T> {};
// Code-gen will explicitly conform the generated FIDL structures to IsFidlType.
// The direction where a message is going.
// This has implications on the allocated buffer and handle size.
enum class MessageDirection {
// Receiving the message from another end.
kReceiving,
// Sending the message to the other end.
kSending
};
// Utilities used internally by the llcpp binding.
namespace internal {
// Whether a FIDL transactional message is used as a request or a response.
enum class TransactionalMessageKind {
kRequest,
kResponse,
};
// C++ 14 compatible implementation of std::void_t.
#if defined(__cplusplus) && __cplusplus >= 201703L
template <typename... T>
using void_t = std::void_t<T...>;
#else
template <typename... T>
struct make_void {
typedef void type;
};
template <typename... T>
using void_t = typename make_void<T...>::type;
#endif
// IsResponseType<FidlType>() is true when FidlType is a FIDL response message type.
template <typename FidlType, typename = void_t<>>
struct IsResponseType : std::false_type {};
template <typename FidlType>
struct IsResponseType<FidlType, void_t<decltype(TypeTraits<FidlType>::kMessageKind)>>
: std::integral_constant<bool, TypeTraits<FidlType>::kMessageKind ==
TransactionalMessageKind::kResponse> {};
// Calculates the maximum possible message size for a FIDL type,
// clamped at the Zircon channel transport packet size.
template <typename FidlType, const MessageDirection Direction>
constexpr uint32_t ClampedMessageSize() {
static_assert(IsFidlType<FidlType>::value, "Only FIDL types allowed here");
if constexpr (Direction == MessageDirection::kReceiving) {
if (TypeTraits<FidlType>::kHasFlexibleEnvelope) {
return ZX_CHANNEL_MAX_MSG_BYTES;
}
}
// These can be modified to return the ::Alt variant when a migration
// is ongoing
uint64_t primary = FidlAlign(TypeTraits<FidlType>::kPrimarySizeV1);
uint64_t out_of_line = FidlAlign(TypeTraits<FidlType>::kMaxOutOfLineV1);
uint64_t sum = primary + out_of_line;
if (sum > ZX_CHANNEL_MAX_MSG_BYTES) {
return ZX_CHANNEL_MAX_MSG_BYTES;
} else {
return static_cast<uint32_t>(sum);
}
}
// Calculates the maximum possible handle count for a FIDL type,
// clamped at the Zircon channel transport handle limit.
template <typename FidlType, const MessageDirection Direction>
constexpr uint32_t ClampedHandleCount() {
static_assert(IsFidlType<FidlType>::value, "Only FIDL types allowed here");
if constexpr (Direction == MessageDirection::kReceiving) {
if (TypeTraits<FidlType>::kHasFlexibleEnvelope) {
return ZX_CHANNEL_MAX_MSG_HANDLES;
}
}
uint32_t raw_max_handles = TypeTraits<FidlType>::kMaxNumHandles;
if (raw_max_handles > ZX_CHANNEL_MAX_MSG_HANDLES) {
return ZX_CHANNEL_MAX_MSG_HANDLES;
} else {
return raw_max_handles;
}
}
} // namespace internal
} // namespace fidl
#endif // LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_TRAITS_H_