blob: d260016c2b13b543de94bf68ec8bcb9ca34c7b88 [file] [log] [blame]
// Copyright 2017 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_INTERNAL_H_
#define LIB_FIDL_INTERNAL_H_
#include <assert.h>
#include <lib/fidl/coding.h>
#include <stdbool.h>
#include <stdint.h>
#include <zircon/assert.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#ifdef __Fuchsia__
#ifdef __cplusplus
#include <zircon/syscalls.h>
#endif
#endif
#ifdef __cplusplus
#include <type_traits>
#endif // __cplusplus
__BEGIN_CDECLS
zx_status_t FidlHandleCloseMany(const zx_handle_t* handles, size_t num_handles);
zx_status_t FidlHandleDispositionCloseMany(const zx_handle_disposition_t* handle_dispositions,
size_t num_handles);
zx_status_t FidlHandleInfoCloseMany(const zx_handle_info_t* handle_infos, size_t num_handles);
// All sizes here are given as uint32_t. Fidl message sizes are bounded to well below UINT32_MAX.
// This also applies to arrays and vectors. For arrays, element_count * element_size will always fit
// with 32 bits. For vectors, max_count * element_size will always fit within 32 bits.
// Pointers to other type tables within a type are always nonnull, with the exception of vectors.
// In that case, a null pointer indicates that the element type of the vector has no interesting
// information to be decoded (i.e. no pointers or handles). The vector type still needs to be
// emitted as it contains the information about the size of its secondary object. Contrast this with
// arrays: being inline, ones with no interesting coding information can be elided, just like a
// uint32 field in a struct is elided.
typedef bool FidlNullability;
static const bool kFidlNullability_Nonnullable = false;
static const bool kFidlNullability_Nullable = true;
typedef bool FidlStrictness;
static const bool kFidlStrictness_Flexible = false;
static const bool kFidlStrictness_Strict = true;
// TODO(fxbug.dev/42792): Remove either this FidlAlign function or the FIDL_ALIGN macro in
// zircon/fidl.h.
// clang-format off
#ifdef __cplusplus
constexpr
#endif // __cplusplus
static inline uint64_t FidlAlign(uint32_t offset) {
const uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
return (offset + alignment_mask) & ~alignment_mask;
}
// clang-format on
// Determine if the pointer is aligned to |FIDL_ALIGNMENT|.
static inline bool FidlIsAligned(const uint8_t* ptr) {
uintptr_t uintptr = (uintptr_t)(ptr);
const uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
return (uintptr & alignment_mask) == 0;
}
// Add |size| to out-of-line |offset|, maintaining alignment. For example, a pointer to a struct
// that is 4 bytes still needs to advance the next out-of-line offset by 8 to maintain
// the aligned-to-FIDL_ALIGNMENT property.
// Returns false on overflow. Otherwise, resulting offset is stored in |out_offset|.
static inline bool FidlAddOutOfLine(uint32_t offset, uint32_t size, uint32_t* out_offset) {
const uint32_t kMask = FIDL_ALIGNMENT - 1;
uint32_t new_offset = offset;
if (add_overflow(new_offset, size, &new_offset) || add_overflow(new_offset, kMask, &new_offset)) {
return false;
}
new_offset &= ~kMask;
*out_offset = new_offset;
return true;
}
typedef uint8_t FidlStructElementType;
static const FidlStructElementType kFidlStructElementType_Field = (uint8_t)1u;
static const FidlStructElementType kFidlStructElementType_Padding64 = (uint8_t)2u;
static const FidlStructElementType kFidlStructElementType_Padding32 = (uint8_t)3u;
static const FidlStructElementType kFidlStructElementType_Padding16 = (uint8_t)4u;
typedef bool FidlIsResource;
static const FidlIsResource kFidlIsResource_Resource = true;
static const FidlIsResource kFidlIsResource_NotResource = false;
struct FidlStructElementHeader {
FidlStructElementType element_type;
FidlIsResource is_resource;
};
struct FidlStructField {
struct FidlStructElementHeader header;
uint32_t offset;
const fidl_type_t* field_type;
};
struct FidlStructPadding {
struct FidlStructElementHeader header;
uint32_t offset;
// Masks with 0xff on bytes with padding and 0x00 otherwise.
// They are used by VisitInternalPadding to zero (encoding) and validate (decoding)
// padding bytes.
union {
uint16_t mask_16;
uint32_t mask_32;
uint64_t mask_64;
};
};
// A struct element is either a field or padding.
struct FidlStructElement {
union {
struct FidlStructElementHeader header;
struct FidlStructField field;
struct FidlStructPadding padding;
};
#ifdef __cplusplus
static constexpr FidlStructElement Field(const fidl_type* type, uint32_t offset,
FidlIsResource is_resource) {
return FidlStructElement{
.field =
FidlStructField{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Field,
.is_resource = is_resource,
},
.offset = offset,
.field_type = type,
},
};
}
static constexpr FidlStructElement Padding64(uint32_t offset, uint64_t mask) {
return FidlStructElement{
.padding =
FidlStructPadding{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Padding64,
.is_resource = kFidlIsResource_NotResource,
},
.offset = offset,
.mask_64 = mask,
},
};
}
static constexpr FidlStructElement Padding32(uint32_t offset, uint32_t mask) {
return FidlStructElement{
.padding =
FidlStructPadding{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Padding32,
.is_resource = kFidlIsResource_NotResource,
},
.offset = offset,
.mask_32 = mask,
},
};
}
static constexpr FidlStructElement Padding16(uint32_t offset, uint16_t mask) {
return FidlStructElement{
.padding =
FidlStructPadding{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Padding16,
.is_resource = kFidlIsResource_NotResource,
},
.offset = offset,
.mask_16 = mask,
},
};
}
#endif // __cplusplus
};
struct FidlTableField {
const fidl_type_t* type;
uint32_t ordinal;
};
struct FidlXUnionField {
const fidl_type_t* type;
};
// TODO(fxbug.dev/42793): Consider starting enum values for FidlTypeTag from 1, not 0.
typedef uint8_t FidlTypeTag;
static const uint8_t kFidlTypePrimitive = 0;
static const uint8_t kFidlTypeEnum = 1;
static const uint8_t kFidlTypeBits = 2;
static const uint8_t kFidlTypeStruct = 3;
static const uint8_t kFidlTypeStructPointer = 4;
static const uint8_t kFidlTypeArray = 5;
static const uint8_t kFidlTypeString = 6;
static const uint8_t kFidlTypeHandle = 7;
static const uint8_t kFidlTypeVector = 8;
static const uint8_t kFidlTypeTable = 9;
static const uint8_t kFidlTypeXUnion = 10;
// TODO(fxbug.dev/42793): Consider starting enum values for FidlCodedPrimitive from 1, not 0.
typedef uint8_t FidlCodedPrimitiveSubtype;
static const uint8_t kFidlCodedPrimitiveSubtype_Bool = 0;
static const uint8_t kFidlCodedPrimitiveSubtype_Int8 = 1;
static const uint8_t kFidlCodedPrimitiveSubtype_Int16 = 2;
static const uint8_t kFidlCodedPrimitiveSubtype_Int32 = 3;
static const uint8_t kFidlCodedPrimitiveSubtype_Int64 = 4;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint8 = 5;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint16 = 6;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint32 = 7;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint64 = 8;
static const uint8_t kFidlCodedPrimitiveSubtype_Float32 = 9;
static const uint8_t kFidlCodedPrimitiveSubtype_Float64 = 10;
typedef bool (*EnumValidationPredicate)(uint64_t);
// Coding Table Definitions
//
// FIDL coding tables describe the layout and constraints of the messages.
// Each coding table must start with a `tag`, to identify the kind of the
// coding table at runtime. For improved convenience working with these types,
// we provide an empty C++ type `fidl_type_t`, which is inherited by the
// coding tables in C++ mode, and dispatches to one of the subclasses based
// on the tag.
//
// Coding tables are generated in C files to avoid delayed-initialization
// issues, but are meant to be consumed by C++ files such as the walker.
// Hence parts of below are ifdef'ed with C++-specific blocks.
struct FidlCodedPrimitive;
struct FidlCodedEnum;
struct FidlCodedBits;
struct FidlCodedStruct;
struct FidlCodedStructPointer;
struct FidlCodedTable;
struct FidlCodedXUnion;
struct FidlCodedArray;
struct FidlCodedHandle;
struct FidlCodedString;
struct FidlCodedVector;
#ifdef __cplusplus
// Empty struct containing helper functions for casting to derived
// coding table types. C++ empty base class optimization ensure that this
// struct shares the same starting address with any of its subclasses.
struct fidl_type {
constexpr FidlTypeTag type_tag() const;
constexpr const FidlCodedPrimitive& coded_primitive() const;
constexpr const FidlCodedEnum& coded_enum() const;
constexpr const FidlCodedBits& coded_bits() const;
constexpr const FidlCodedStruct& coded_struct() const;
constexpr const FidlCodedStructPointer& coded_struct_pointer() const;
constexpr const FidlCodedTable& coded_table() const;
constexpr const FidlCodedXUnion& coded_xunion() const;
constexpr const FidlCodedArray& coded_array() const;
constexpr const FidlCodedHandle& coded_handle() const;
constexpr const FidlCodedString& coded_string() const;
constexpr const FidlCodedVector& coded_vector() const;
private:
// Prevent instances of this class from being accidentally used standalone
// as a value.
constexpr fidl_type() = default;
};
#define FIDL_INTERNAL_INHERIT_TYPE_T \
final: \
fidl_type
// When compiling in C++14 mode, the rules around initialization
// changes such that the compiler requires an explicit constructor.
// Since coding tables are defined in C and consumed in C++,
// it is safe to delete the constructor.
// Note that this still allows the use of C++ designated initializers.
#define FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(cls) cls() = delete;
#else
// No inheritance in C mode. This is okay because inheriting
// from an empty class does not affect the object layout at all.
#define FIDL_INTERNAL_INHERIT_TYPE_T
#define FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(cls)
#endif // __cplusplus
struct FidlCodedPrimitive FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlCodedPrimitiveSubtype type;
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedPrimitive)
};
struct FidlCodedEnum FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlCodedPrimitiveSubtype underlying_type;
const FidlStrictness strictness;
// The validate predicate is only used for strict enums, and is NULL for
// flexible enums.
const EnumValidationPredicate validate;
const char* name; // may be nullptr if omitted at compile time
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedEnum)
};
struct FidlCodedBits FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlCodedPrimitiveSubtype underlying_type;
const FidlStrictness strictness;
const uint64_t mask;
const char* name; // may be nullptr if omitted at compile time
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedBits)
};
// Though the |size| is implied by the fields, computing that information is not
// the purview of this library. It's easier for the compiler to stash it.
struct FidlCodedStruct FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
// element_count should be a uint32_t, but for the sake of binary size
// a uint16_t is used (all existing values fit within this size).
// If a larger size is needed, replace FidlCodedStruct or add a second
// variant that supports the larger size.
const uint16_t element_count;
const uint32_t size;
const struct FidlStructElement* const elements;
const char* name; // may be nullptr if omitted at compile time
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedStruct)
};
struct FidlCodedStructPointer FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const struct FidlCodedStruct* const struct_type;
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedStructPointer)
};
struct FidlCodedTable FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlIsResource is_resource;
const uint32_t field_count;
const struct FidlTableField* const fields;
const char* name; // may be nullptr if omitted at compile time
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedTable)
};
struct FidlCodedXUnion FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const FidlStrictness strictness;
const FidlIsResource is_resource;
const uint32_t field_count;
// The fields are in ordinal order, with ordinal 1 at index 0.
const struct FidlXUnionField* const fields;
const char* name; // may be nullptr if omitted at compile time
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedXUnion)
};
// An array is essentially a struct with |array_size / element_size| of the same field, named at
// |element|.
struct FidlCodedArray FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
// element_size should be a uint32_t, but for the sake of binary size
// a uint16_t is used (all existing values fit within this size).
// If a larger size is needed, replace FidlCodedArray or add a second
// variant that supports the larger size.
const uint16_t element_size;
const uint32_t array_size;
const fidl_type_t* const element;
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedArray)
};
struct FidlCodedHandle FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const zx_obj_type_t handle_subtype;
const zx_rights_t handle_rights;
static_assert(ZX_OBJ_TYPE_UPPER_BOUND <= UINT32_MAX, "");
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedHandle)
};
struct FidlCodedString FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const uint32_t max_size;
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedString)
};
// Note that |max_count * element_size| is guaranteed to fit into a uint32_t. Unlike other types,
// the |element| pointer may be null. This occurs when the element type contains no interesting
// bits (i.e. pointers or handles).
struct FidlCodedVector FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const uint32_t max_count;
const uint32_t element_size;
const fidl_type_t* const element;
FIDL_INTERNAL_DELETE_DEFAULT_CONSTRUCTOR(FidlCodedVector)
};
#ifdef __cplusplus
struct FidlHasTypeTag final : fidl_type {
const FidlTypeTag tag;
FidlHasTypeTag() = delete;
};
constexpr FidlTypeTag fidl_type::type_tag() const {
return static_cast<const FidlHasTypeTag*>(this)->tag;
}
constexpr const FidlCodedPrimitive& fidl_type::coded_primitive() const {
return *static_cast<const FidlCodedPrimitive*>(this);
}
constexpr const FidlCodedEnum& fidl_type::coded_enum() const {
return *static_cast<const FidlCodedEnum*>(this);
}
constexpr const FidlCodedBits& fidl_type::coded_bits() const {
return *static_cast<const FidlCodedBits*>(this);
}
constexpr const FidlCodedStruct& fidl_type::coded_struct() const {
return *static_cast<const FidlCodedStruct*>(this);
}
constexpr const FidlCodedStructPointer& fidl_type::coded_struct_pointer() const {
return *static_cast<const FidlCodedStructPointer*>(this);
}
constexpr const FidlCodedTable& fidl_type::coded_table() const {
return *static_cast<const FidlCodedTable*>(this);
}
constexpr const FidlCodedXUnion& fidl_type::coded_xunion() const {
return *static_cast<const FidlCodedXUnion*>(this);
}
constexpr const FidlCodedArray& fidl_type::coded_array() const {
return *static_cast<const FidlCodedArray*>(this);
}
constexpr const FidlCodedHandle& fidl_type::coded_handle() const {
return *static_cast<const FidlCodedHandle*>(this);
}
constexpr const FidlCodedString& fidl_type::coded_string() const {
return *static_cast<const FidlCodedString*>(this);
}
constexpr const FidlCodedVector& fidl_type::coded_vector() const {
return *static_cast<const FidlCodedVector*>(this);
}
#endif // __cplusplus
extern const struct FidlCodedPrimitive fidl_internal_kBoolTable;
extern const struct FidlCodedPrimitive fidl_internal_kInt8Table;
extern const struct FidlCodedPrimitive fidl_internal_kInt16Table;
extern const struct FidlCodedPrimitive fidl_internal_kInt32Table;
extern const struct FidlCodedPrimitive fidl_internal_kInt64Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint8Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint16Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint32Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint64Table;
extern const struct FidlCodedPrimitive fidl_internal_kFloat32Table;
extern const struct FidlCodedPrimitive fidl_internal_kFloat64Table;
__END_CDECLS
#ifdef __cplusplus
// All the data in coding tables should be pure data.
static_assert(std::is_standard_layout<FidlTypeTag>::value, "");
static_assert(std::is_standard_layout<FidlStructField>::value, "");
static_assert(std::is_standard_layout<FidlTableField>::value, "");
static_assert(std::is_standard_layout<FidlCodedStruct>::value, "");
static_assert(std::is_standard_layout<FidlCodedStructPointer>::value, "");
static_assert(std::is_standard_layout<FidlCodedXUnion>::value, "");
static_assert(std::is_standard_layout<FidlCodedArray>::value, "");
static_assert(std::is_standard_layout<FidlCodedVector>::value, "");
static_assert(std::is_standard_layout<FidlCodedString>::value, "");
static_assert(std::is_standard_layout<FidlCodedHandle>::value, "");
static_assert(std::is_standard_layout<FidlStructElement>::value, "");
#endif // __cplusplus
static_assert(offsetof(struct FidlCodedStruct, tag) == 0, "");
static_assert(offsetof(struct FidlCodedStructPointer, tag) == 0, "");
static_assert(offsetof(struct FidlCodedXUnion, tag) == 0, "");
static_assert(offsetof(struct FidlCodedArray, tag) == 0, "");
static_assert(offsetof(struct FidlCodedVector, tag) == 0, "");
static_assert(offsetof(struct FidlCodedString, tag) == 0, "");
static_assert(offsetof(struct FidlCodedHandle, tag) == 0, "");
// Take caution when increasing the size numbers below. While they
// can be changed as needed when the structure evolves, these growing
// has a large impact on binary size and memory footprint.
static_assert(sizeof(struct FidlCodedPrimitive) == 2, "");
static_assert(sizeof(struct FidlCodedEnum) == 24, "");
static_assert(sizeof(struct FidlCodedBits) == 24, "");
static_assert(sizeof(struct FidlCodedStruct) == 24, "");
static_assert(sizeof(struct FidlCodedStructPointer) == 16, "");
static_assert(sizeof(struct FidlCodedXUnion) == 24, "");
static_assert(sizeof(struct FidlCodedArray) == 16, "");
static_assert(sizeof(struct FidlCodedVector) == 24, "");
static_assert(sizeof(struct FidlCodedString) == 8, "");
static_assert(sizeof(struct FidlCodedHandle) == 12, "");
static_assert(sizeof(struct FidlStructField) == 16, "");
static_assert(sizeof(struct FidlTableField) == 16, "");
static_assert(sizeof(struct FidlXUnionField) == 8, "");
static_assert(sizeof(struct FidlStructElement) == 16, "");
#endif // LIB_FIDL_INTERNAL_H_