blob: 43113c496c366b562d0a7f529c8ffe58be0691c5 [file] [log] [blame]
// Copyright 2019 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.
#include <lib/fidl/coding.h>
#include <lib/fidl/internal.h>
#include <lib/fidl/transformer.h>
#include <cassert>
#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <string>
// Disable warning about implicit fallthrough, since it's intentionally used a
// lot in this code, and the switch()es end up being harder to read without it.
// Note that "#pragma GCC" works for both GCC & Clang.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
namespace {
// This is an array of 32-bit ordinals that's intended to help debugging. The
// array is normally empty, but you can add an ordinal to this array in your
// local tree if you encounter a message in-the-field that the transformer is
// having issues with.
constexpr uint64_t kDebugOrdinals[] = {
// 0x61f19458'00000000, // example ordinal
};
enum struct WireFormat {
kOld,
kV1,
};
// Call this macro instead of assert() when inside a TransformerBase method: it
// will print out useful debugging information. This macro inlines the code in
// order to get precise line number information, and to know the assertion being
// evaluated.
#define TRANSFORMER_ASSERT(assertion, position) \
{ \
const auto ok = static_cast<bool>(assertion); \
if (!ok) { \
debug_info_->RecordFailure(__LINE__, (#assertion), (position)); \
assert(assertion); \
} \
}
// Call this macro instead of the TransformerBase::Fail() method. This is a simple wrapper to pass
// the current __LINE__ number to Fail().
#define TRANSFORMER_FAIL(status, position, error_message) \
Fail(status, position, __LINE__, error_message);
// Every Transform() method outputs a TraversalResult, which indicates how many out-of-line bytes
// that transform method consumed, and the actual (not max) number of handles that were encountered
// during the transformation. This is needed for writing the correct size and handle information in
// an envelope. (This isn't a great name. A better name would be welcome!)
struct TraversalResult {
uint32_t src_out_of_line_size = 0u;
uint32_t dst_out_of_line_size = 0u;
uint32_t handle_count = 0u;
TraversalResult& operator+=(const TraversalResult rhs) {
src_out_of_line_size += rhs.src_out_of_line_size;
dst_out_of_line_size += rhs.dst_out_of_line_size;
handle_count += rhs.handle_count;
return *this;
}
};
constexpr uint32_t PrimitiveSize(const FidlCodedPrimitive primitive) {
switch (primitive) {
case kFidlCodedPrimitive_Bool:
case kFidlCodedPrimitive_Int8:
case kFidlCodedPrimitive_Uint8:
return 1;
case kFidlCodedPrimitive_Int16:
case kFidlCodedPrimitive_Uint16:
return 2;
case kFidlCodedPrimitive_Int32:
case kFidlCodedPrimitive_Uint32:
case kFidlCodedPrimitive_Float32:
return 4;
case kFidlCodedPrimitive_Int64:
case kFidlCodedPrimitive_Uint64:
case kFidlCodedPrimitive_Float64:
return 8;
}
__builtin_unreachable();
}
// This function is named UnsafeInlineSize since it assumes that |type| will be
// non-null. Don't call this function directly; instead, call
// TransformerBase::InlineSize().
uint32_t UnsafeInlineSize(const fidl_type_t* type, WireFormat wire_format) {
assert(type);
switch (type->type_tag) {
case kFidlTypePrimitive:
return PrimitiveSize(type->coded_primitive);
case kFidlTypeEnum:
return PrimitiveSize(type->coded_enum.underlying_type);
case kFidlTypeBits:
return PrimitiveSize(type->coded_bits.underlying_type);
case kFidlTypeStructPointer:
return 8;
case kFidlTypeUnionPointer:
assert(wire_format == WireFormat::kOld);
return 8;
case kFidlTypeVector:
case kFidlTypeString:
return 16;
case kFidlTypeStruct:
return type->coded_struct.size;
case kFidlTypeUnion:
assert(wire_format == WireFormat::kOld);
return type->coded_union.size;
case kFidlTypeArray:
return type->coded_array.array_size;
case kFidlTypeXUnion:
return 24;
case kFidlTypeHandle:
return 4;
case kFidlTypeTable:
return 16;
}
// This is needed to suppress a GCC warning "control reaches end of non-void function", since GCC
// treats switch() on enums as non-exhaustive without a default case.
assert(false && "unexpected non-exhaustive switch on FidlTypeTag");
return 0;
}
struct Position {
uint32_t src_inline_offset = 0;
uint32_t src_out_of_line_offset = 0;
uint32_t dst_inline_offset = 0;
uint32_t dst_out_of_line_offset = 0;
Position(uint32_t src_inline_offset, uint32_t src_out_of_line_offset, uint32_t dst_inline_offset,
uint32_t dst_out_of_line_offset)
: src_inline_offset(src_inline_offset),
src_out_of_line_offset(src_out_of_line_offset),
dst_inline_offset(dst_inline_offset),
dst_out_of_line_offset(dst_out_of_line_offset) {}
Position(const Position&) = default;
Position& operator=(const Position&) = default;
inline Position IncreaseInlineOffset(uint32_t increase) const
__attribute__((warn_unused_result)) {
return IncreaseSrcInlineOffset(increase).IncreaseDstInlineOffset(increase);
}
inline Position IncreaseSrcInlineOffset(uint32_t increase) const
__attribute__((warn_unused_result)) {
return Position(src_inline_offset + increase, src_out_of_line_offset, dst_inline_offset,
dst_out_of_line_offset);
}
inline Position IncreaseSrcOutOfLineOffset(uint32_t increase) const
__attribute__((warn_unused_result)) {
return Position(src_inline_offset, src_out_of_line_offset + increase, dst_inline_offset,
dst_out_of_line_offset);
}
inline Position IncreaseDstInlineOffset(uint32_t increase) const
__attribute__((warn_unused_result)) {
return Position(src_inline_offset, src_out_of_line_offset, dst_inline_offset + increase,
dst_out_of_line_offset);
}
inline Position IncreaseDstOutOfLineOffset(uint32_t increase) const
__attribute__((warn_unused_result)) {
return Position(src_inline_offset, src_out_of_line_offset, dst_inline_offset,
dst_out_of_line_offset + increase);
}
std::string ToString() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "{0x%02x, 0x%02x, 0x%02x, 0x%02x}", src_inline_offset,
src_out_of_line_offset, dst_inline_offset, dst_out_of_line_offset);
return std::string(buffer);
}
};
class SrcDst final {
public:
SrcDst(const uint8_t* src_bytes, const uint32_t src_num_bytes, uint8_t* dst_bytes,
uint32_t dst_num_bytes_capacity)
: src_bytes_(src_bytes),
src_max_offset_(src_num_bytes),
dst_bytes_(dst_bytes),
dst_max_offset_(dst_num_bytes_capacity) {}
SrcDst(const SrcDst&) = delete;
// Reads |T| from |src_bytes|.
// This may update the max src read offset if needed.
template <typename T>
const T* __attribute__((warn_unused_result)) Read(const Position& position) {
return Read<T>(position, sizeof(T));
}
// Reads |size| bytes from |src_bytes|, but only returns a pointer to |T|
// which may be smaller, i.e. |sizeof(T)| can be smaller than |size|.
// This may update the max src read offset if needed.
template <typename T>
const T* __attribute__((warn_unused_result)) Read(const Position& position, uint32_t size) {
assert(sizeof(T) <= size);
const auto status = src_max_offset_.Update(position.src_inline_offset + size);
if (status != ZX_OK) {
return nullptr;
}
return reinterpret_cast<const T*>(src_bytes_ + position.src_inline_offset);
}
// Copies |size| bytes from |src_bytes| to |dst_bytes|.
// This may update the max src read offset if needed.
// This may update the max dst written offset if needed.
zx_status_t __attribute__((warn_unused_result)) Copy(const Position& position, uint32_t size) {
const auto src_status = src_max_offset_.Update(position.src_inline_offset + size);
if (src_status != ZX_OK) {
return src_status;
}
const auto dst_status = dst_max_offset_.Update(position.dst_inline_offset + size);
if (dst_status != ZX_OK) {
return dst_status;
}
memcpy(dst_bytes_ + position.dst_inline_offset, src_bytes_ + position.src_inline_offset, size);
return ZX_OK;
}
// Pads |size| bytes in |dst_bytes|.
// This may update the max dst written offset if needed.
zx_status_t __attribute__((warn_unused_result)) Pad(const Position& position, uint32_t size) {
const auto status = dst_max_offset_.Update(position.dst_inline_offset + size);
if (status != ZX_OK) {
return status;
}
memset(dst_bytes_ + position.dst_inline_offset, 0, size);
return ZX_OK;
}
// Writes |value| in |dst_bytes|.
// This may update the max dst written offset if needed.
template <typename T>
zx_status_t __attribute__((warn_unused_result)) Write(const Position& position, T value) {
const auto size = static_cast<uint32_t>(sizeof(value));
const auto status = dst_max_offset_.Update(position.dst_inline_offset + size);
if (status != ZX_OK) {
return status;
}
auto ptr = reinterpret_cast<T*>(dst_bytes_ + position.dst_inline_offset);
*ptr = value;
return ZX_OK;
}
const uint8_t* src_bytes() const { return src_bytes_; }
uint32_t src_num_bytes() const { return src_max_offset_.capacity_; }
uint32_t src_max_offset_read() const { return src_max_offset_.max_offset_; }
uint8_t* dst_bytes() const { return dst_bytes_; }
uint32_t dst_num_bytes_capacity() const { return dst_max_offset_.capacity_; }
uint32_t dst_max_offset_written() const { return dst_max_offset_.max_offset_; }
private:
struct MaxOffset {
MaxOffset(uint32_t capacity) : capacity_(capacity) {}
const uint32_t capacity_;
uint32_t max_offset_ = 0;
zx_status_t __attribute__((warn_unused_result)) Update(uint32_t offset) {
if (offset > capacity_) {
return ZX_ERR_BAD_STATE;
}
if (offset > max_offset_) {
max_offset_ = offset;
}
return ZX_OK;
}
};
const uint8_t* src_bytes_;
MaxOffset src_max_offset_;
uint8_t* dst_bytes_;
MaxOffset dst_max_offset_;
};
// Debug related information, which is set both on construction, and as we
// transform. On destruction, this object writes any collected error message
// if an |out_error_msg| is provided.
class DebugInfo final {
public:
DebugInfo(fidl_transformation_t transformation, const fidl_type_t* type, const SrcDst& src_dst,
const char** out_error_msg)
: transformation_(transformation),
type_(type),
src_dst_(src_dst),
out_error_msg_(out_error_msg) {}
DebugInfo(const DebugInfo&) = delete;
~DebugInfo() {
if (has_failed_) {
Print("ERROR");
}
if (out_error_msg_) {
*out_error_msg_ = error_msg_;
}
}
void RecordFailure(int line_number, const char* error_msg) {
has_failed_ = true;
error_msg_ = error_msg;
line_number_ = line_number;
}
void RecordFailure(int line_number, const char* error_msg, const Position& position) {
RecordFailure(line_number, error_msg);
position_ = position;
}
void DebugPrint(int line_number, const char* error_msg, const Position& position) const {
DebugInfo dup(transformation_, type_, src_dst_, nullptr);
dup.RecordFailure(line_number, error_msg, position);
dup.Print("INFO");
// Avoid printing twice.
dup.has_failed_ = false;
}
private:
void Print(const std::string& failure_type) const {
printf("=== TRANSFORMER %s ===\n", failure_type.c_str());
char type_desc[256] = {};
fidl_format_type_name(type_, type_desc, sizeof(type_desc));
printf("src: " __FILE__ "\n");
printf("direction: %s\n", direction().c_str());
printf("transformer.cc:%d: %s\n", line_number_, error_msg_);
printf("top level type: %s\n", type_desc);
printf("position: %s\n", position_.ToString().c_str());
auto print_bytes = [&](const uint8_t* buffer, uint32_t size, uint32_t out_of_line_offset) {
for (uint32_t i = 0; i < size; i++) {
if (i == out_of_line_offset) {
printf(" // out-of-line\n");
}
if (i % 8 == 0) {
printf(" ");
}
printf("0x%02x, ", buffer[i]);
if (i % 0x10 == 0x07) {
printf(" // 0x%02x\n", i - 7);
} else if (i % 0x08 == 0x07) {
printf("\n");
}
}
};
printf("uint8_t src_bytes[0x%02x] = {\n", src_dst_.src_num_bytes());
print_bytes(src_dst_.src_bytes(), src_dst_.src_num_bytes(), position_.src_out_of_line_offset);
printf("}\n");
printf("uint8_t dst_bytes[0x%02x] = { // capacity = 0x%02x\n",
src_dst_.dst_max_offset_written(), src_dst_.dst_num_bytes_capacity());
print_bytes(src_dst_.dst_bytes(), src_dst_.dst_max_offset_written(),
position_.dst_out_of_line_offset);
printf("}\n");
printf("=== END TRANSFORMER %s ===\n", failure_type.c_str());
}
std::string direction() const {
switch (transformation_) {
case FIDL_TRANSFORMATION_NONE:
return "none";
case FIDL_TRANSFORMATION_V1_TO_OLD:
return "v1 to old";
case FIDL_TRANSFORMATION_OLD_TO_V1:
return "old to v1";
default:
return "unknown";
}
}
// Set on construction, and duplicated.
const fidl_transformation_t transformation_;
const fidl_type_t* type_;
const SrcDst& src_dst_;
// Set on construction, never duplicated.
const char** out_error_msg_;
// Set post construction, never duplicated.
bool has_failed_ = false;
const char* error_msg_ = nullptr;
int line_number_ = -1;
Position position_{0, 0, 0, 0};
};
class TransformerBase {
public:
TransformerBase(SrcDst* src_dst, const fidl_type_t* top_level_src_type, DebugInfo* debug_info)
: src_dst(src_dst), debug_info_(debug_info), top_level_src_type_(top_level_src_type) {}
virtual ~TransformerBase() = default;
uint32_t InlineSize(const fidl_type_t* type, WireFormat wire_format, const Position& position) {
TRANSFORMER_ASSERT(type, position);
return UnsafeInlineSize(type, wire_format);
}
uint32_t AltInlineSize(const fidl_type_t* type, const Position& position) {
TRANSFORMER_ASSERT(type, position);
switch (type->type_tag) {
case kFidlTypeStruct:
return InlineSize(type->coded_struct.alt_type, To(), position);
case kFidlTypeUnion:
return InlineSize(type->coded_union.alt_type, To(), position);
case kFidlTypeArray:
return InlineSize(type->coded_array.alt_type, To(), position);
case kFidlTypeXUnion:
return InlineSize(type->coded_xunion.alt_type, To(), position);
case kFidlTypePrimitive:
case kFidlTypeEnum:
case kFidlTypeBits:
case kFidlTypeStructPointer:
case kFidlTypeUnionPointer:
case kFidlTypeVector:
case kFidlTypeString:
case kFidlTypeHandle:
case kFidlTypeTable:
return InlineSize(type, To(), position);
}
TRANSFORMER_ASSERT(false && "unexpected non-exhaustive switch on FidlTypeTag", position);
return 0;
}
void MaybeDebugPrintTopLevelStruct(const Position& position) {
if (sizeof(kDebugOrdinals) == 0) {
return;
}
auto maybe_ordinal = src_dst->Read<uint64_t>(
position.IncreaseSrcInlineOffset(offsetof(fidl_message_header_t, ordinal)));
if (!maybe_ordinal) {
return;
}
for (uint64_t debug_ordinal : kDebugOrdinals) {
if (debug_ordinal != *maybe_ordinal) {
continue;
}
char buffer[16];
snprintf(buffer, sizeof(buffer), "0x%016" PRIx64, debug_ordinal);
debug_info_->DebugPrint(__LINE__, buffer, position);
}
}
zx_status_t TransformTopLevelStruct() {
if (top_level_src_type_->type_tag != kFidlTypeStruct) {
return TRANSFORMER_FAIL(ZX_ERR_INVALID_ARGS, (Position{0, 0, 0, 0}),
"only top-level structs supported");
}
const auto& src_coded_struct = top_level_src_type_->coded_struct;
const auto& dst_coded_struct = src_coded_struct.alt_type->coded_struct;
// Since this is the top-level struct, the first secondary object (i.e.
// out-of-line offset) is exactly placed after this struct, i.e. the
// struct's inline size.
const auto start_position = Position(0, src_coded_struct.size, 0, dst_coded_struct.size);
TraversalResult discarded_traversal_result;
const zx_status_t status =
TransformStruct(src_coded_struct, dst_coded_struct, start_position,
FIDL_ALIGN(dst_coded_struct.size), &discarded_traversal_result);
MaybeDebugPrintTopLevelStruct(start_position);
return status;
}
protected:
zx_status_t Transform(const fidl_type_t* type, const Position& position, const uint32_t dst_size,
TraversalResult* out_traversal_result) {
if (!type) {
return src_dst->Copy(position, dst_size);
}
switch (type->type_tag) {
case kFidlTypeHandle:
return TransformHandle(position, dst_size, out_traversal_result);
case kFidlTypePrimitive:
case kFidlTypeEnum:
case kFidlTypeBits:
return src_dst->Copy(position, dst_size);
case kFidlTypeStructPointer: {
const auto& src_coded_struct = *type->coded_struct_pointer.struct_type;
const auto& dst_coded_struct = src_coded_struct.alt_type->coded_struct;
return TransformStructPointer(src_coded_struct, dst_coded_struct, position,
out_traversal_result);
}
case kFidlTypeUnionPointer: {
const auto& src_coded_union = *type->coded_union_pointer.union_type;
const auto& dst_coded_xunion = src_coded_union.alt_type->coded_xunion;
return TransformUnionPointerToOptionalXUnion(src_coded_union, dst_coded_xunion, position,
out_traversal_result);
}
case kFidlTypeStruct: {
const auto& src_coded_struct = type->coded_struct;
const auto& dst_coded_struct = src_coded_struct.alt_type->coded_struct;
return TransformStruct(src_coded_struct, dst_coded_struct, position, dst_size,
out_traversal_result);
}
case kFidlTypeUnion: {
const auto& src_coded_union = type->coded_union;
const auto& dst_coded_union = src_coded_union.alt_type->coded_xunion;
return TransformUnionToXUnion(src_coded_union, dst_coded_union, position, dst_size,
out_traversal_result);
}
case kFidlTypeArray: {
const auto convert = [](const FidlCodedArray& coded_array) {
FidlCodedArrayNew result = {
.element = coded_array.element,
.element_count = coded_array.array_size / coded_array.element_size,
.element_size = coded_array.element_size,
.element_padding = 0,
.alt_type = nullptr /* alt_type unused, we provide both src and dst */};
return result;
};
auto src_coded_array = convert(type->coded_array);
auto dst_coded_array = convert(type->coded_array.alt_type->coded_array);
return TransformArray(src_coded_array, dst_coded_array, position, dst_size,
out_traversal_result);
}
case kFidlTypeString:
return TransformString(position, out_traversal_result);
case kFidlTypeVector: {
const auto& src_coded_vector = type->coded_vector;
const auto& dst_coded_vector = src_coded_vector.alt_type->coded_vector;
return TransformVector(src_coded_vector, dst_coded_vector, position, out_traversal_result);
}
case kFidlTypeTable:
return TransformTable(type->coded_table, position, out_traversal_result);
case kFidlTypeXUnion:
TRANSFORMER_ASSERT(type->coded_xunion.alt_type, position);
switch (type->coded_xunion.alt_type->type_tag) {
case kFidlTypeUnion:
return TransformXUnionToUnion(type->coded_xunion,
type->coded_xunion.alt_type->coded_union, position,
dst_size, out_traversal_result);
case kFidlTypeUnionPointer:
return TransformOptionalXUnionToUnionPointer(
type->coded_xunion, *type->coded_xunion.alt_type->coded_union_pointer.union_type,
position, out_traversal_result);
case kFidlTypeXUnion:
// NOTE: after https://fuchsia-review.googlesource.com/c/fuchsia/+/365937,
// the alt type of a xunion should always be a union, so this path should
// never be exercised
return TransformXUnion(type->coded_xunion, position, out_traversal_result);
case kFidlTypePrimitive:
case kFidlTypeEnum:
case kFidlTypeBits:
case kFidlTypeStruct:
case kFidlTypeStructPointer:
case kFidlTypeArray:
case kFidlTypeString:
case kFidlTypeHandle:
case kFidlTypeVector:
case kFidlTypeTable:
TRANSFORMER_ASSERT(false && "Invalid src_xunion alt_type->type_tag", position);
__builtin_unreachable();
}
}
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "unknown type tag");
}
zx_status_t TransformHandle(const Position& position, uint32_t dst_size,
TraversalResult* out_traversal_result) {
auto presence = src_dst->Read<uint32_t>(position);
if (!presence) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "handle presence missing");
}
switch (*presence) {
case FIDL_HANDLE_ABSENT:
// Ok
break;
case FIDL_HANDLE_PRESENT:
out_traversal_result->handle_count++;
break;
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "handle presence invalid");
}
return src_dst->Copy(position, dst_size);
}
zx_status_t TransformStructPointer(const FidlCodedStruct& src_coded_struct,
const FidlCodedStruct& dst_coded_struct,
const Position& position,
TraversalResult* out_traversal_result) {
auto presence = src_dst->Read<uint64_t>(position);
if (!presence) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "struct pointer missing");
}
auto status_copy_struct_pointer = src_dst->Copy(position, sizeof(uint64_t));
if (status_copy_struct_pointer != ZX_OK) {
return status_copy_struct_pointer;
}
switch (*presence) {
case FIDL_ALLOC_ABSENT:
// Early exit on absent struct.
return ZX_OK;
case FIDL_ALLOC_PRESENT:
// Ok
break;
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "struct pointer invalid");
}
uint32_t src_aligned_size = FIDL_ALIGN(src_coded_struct.size);
uint32_t dst_aligned_size = FIDL_ALIGN(dst_coded_struct.size);
const auto struct_position = Position{
position.src_out_of_line_offset,
position.src_out_of_line_offset + src_aligned_size,
position.dst_out_of_line_offset,
position.dst_out_of_line_offset + dst_aligned_size,
};
out_traversal_result->src_out_of_line_size += src_aligned_size;
out_traversal_result->dst_out_of_line_size += dst_aligned_size;
return TransformStruct(src_coded_struct, dst_coded_struct, struct_position, dst_aligned_size,
out_traversal_result);
}
zx_status_t TransformStruct(const FidlCodedStruct& src_coded_struct,
const FidlCodedStruct& dst_coded_struct, const Position& position,
uint32_t dst_size, TraversalResult* out_traversal_result) {
TRANSFORMER_ASSERT(src_coded_struct.field_count == dst_coded_struct.field_count, position);
// Note: we cannot use dst_coded_struct.size, and must instead rely on
// the provided dst_size since this struct could be placed in an alignment
// context that is larger than its inherent size.
// Copy structs without any coded fields, and done.
if (src_coded_struct.field_count == 0) {
return src_dst->Copy(position, dst_size);
}
const uint32_t src_start_of_struct = position.src_inline_offset;
const uint32_t dst_start_of_struct = position.dst_inline_offset;
auto current_position = position;
for (uint32_t field_index = 0; field_index < src_coded_struct.field_count; field_index++) {
const auto& src_field = src_coded_struct.fields[field_index];
const auto& dst_field = dst_coded_struct.fields[field_index];
if (!src_field.type) {
const uint32_t dst_field_size =
src_start_of_struct + src_field.padding_offset - current_position.src_inline_offset;
const auto status_copy_field = src_dst->Copy(current_position, dst_field_size);
if (status_copy_field != ZX_OK) {
return status_copy_field;
}
current_position = current_position.IncreaseInlineOffset(dst_field_size);
} else {
// The only case where the amount we've written shouldn't match the specified offset is
// for request/response structs, where the txn header is not specified in the coding table.
if (current_position.src_inline_offset != src_start_of_struct + src_field.offset) {
TRANSFORMER_ASSERT(src_field.offset == dst_field.offset, current_position);
const auto status_copy_field = src_dst->Copy(current_position, src_field.offset);
if (status_copy_field != ZX_OK) {
return status_copy_field;
}
current_position = current_position.IncreaseInlineOffset(src_field.offset);
}
TRANSFORMER_ASSERT(
current_position.src_inline_offset == src_start_of_struct + src_field.offset,
current_position);
TRANSFORMER_ASSERT(
current_position.dst_inline_offset == dst_start_of_struct + dst_field.offset,
current_position);
// Transform field.
uint32_t src_next_field_offset = current_position.src_inline_offset +
InlineSize(src_field.type, From(), current_position);
uint32_t dst_next_field_offset =
current_position.dst_inline_offset + InlineSize(dst_field.type, To(), current_position);
uint32_t dst_field_size = dst_next_field_offset - (dst_start_of_struct + dst_field.offset);
TraversalResult field_traversal_result;
const zx_status_t status =
Transform(src_field.type, current_position, dst_field_size, &field_traversal_result);
if (status != ZX_OK) {
return status;
}
*out_traversal_result += field_traversal_result;
// Update current position for next iteration.
current_position.src_inline_offset = src_next_field_offset;
current_position.dst_inline_offset = dst_next_field_offset;
current_position.src_out_of_line_offset += field_traversal_result.src_out_of_line_size;
current_position.dst_out_of_line_offset += field_traversal_result.dst_out_of_line_size;
}
// Pad (possibly with 0 bytes).
const auto status_pad = src_dst->Pad(current_position, dst_field.padding);
if (status_pad != ZX_OK) {
return TRANSFORMER_FAIL(status_pad, current_position,
"unable to pad end of struct element");
}
current_position = current_position.IncreaseDstInlineOffset(dst_field.padding);
current_position = current_position.IncreaseSrcInlineOffset(src_field.padding);
}
// Pad (possibly with 0 bytes).
const uint32_t dst_end_of_struct = position.dst_inline_offset + dst_size;
const auto status_pad =
src_dst->Pad(current_position, dst_end_of_struct - current_position.dst_inline_offset);
if (status_pad != ZX_OK) {
return TRANSFORMER_FAIL(status_pad, current_position, "unable to pad end of struct");
}
return ZX_OK;
}
zx_status_t TransformVector(const FidlCodedVector& src_coded_vector,
const FidlCodedVector& dst_coded_vector, const Position& position,
TraversalResult* out_traversal_result) {
const auto src_vector = src_dst->Read<fidl_vector_t>(position);
if (!src_vector) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "vector missing");
}
// Copy vector header.
const auto status_copy_vector_hdr = src_dst->Copy(position, sizeof(fidl_vector_t));
if (status_copy_vector_hdr != ZX_OK) {
return status_copy_vector_hdr;
}
const auto presence = reinterpret_cast<uint64_t>(src_vector->data);
switch (presence) {
case FIDL_ALLOC_ABSENT:
// Early exit on nullable vectors.
return ZX_OK;
case FIDL_ALLOC_PRESENT:
// OK
break;
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "vector presence invalid");
}
const auto convert = [&](const FidlCodedVector& coded_vector) {
FidlCodedArrayNew result = {
.element = coded_vector.element,
.element_count = static_cast<uint32_t>(src_vector->count),
.element_size = coded_vector.element_size,
.element_padding = 0,
.alt_type = nullptr /* alt_type unused, we provide both src and dst */};
return result;
};
const auto src_vector_data_as_coded_array = convert(src_coded_vector);
const auto dst_vector_data_as_coded_array = convert(dst_coded_vector);
// Calculate vector size. They fit in uint32_t due to automatic bounds.
uint32_t src_vector_size =
FIDL_ALIGN(static_cast<uint32_t>(src_vector->count * (src_coded_vector.element_size)));
uint32_t dst_vector_size =
FIDL_ALIGN(static_cast<uint32_t>(src_vector->count * (dst_coded_vector.element_size)));
// Transform elements.
auto vector_data_position = Position{
position.src_out_of_line_offset, position.src_out_of_line_offset + src_vector_size,
position.dst_out_of_line_offset, position.dst_out_of_line_offset + dst_vector_size};
const zx_status_t status =
TransformArray(src_vector_data_as_coded_array, dst_vector_data_as_coded_array,
vector_data_position, dst_vector_size, out_traversal_result);
if (status != ZX_OK) {
return status;
}
out_traversal_result->src_out_of_line_size += src_vector_size;
out_traversal_result->dst_out_of_line_size += dst_vector_size;
return ZX_OK;
}
zx_status_t TransformString(const Position& position, TraversalResult* out_traversal_result) {
FidlCodedVector string_as_coded_vector = {
.element = nullptr,
.max_count = 0 /*unused*/,
.element_size = 1,
.nullable = kFidlNullability_Nullable, /* constraints are not checked, i.e. unused */
.alt_type = nullptr /* alt_type unused, we provide both src and dst */};
return TransformVector(string_as_coded_vector, string_as_coded_vector, position,
out_traversal_result);
}
zx_status_t TransformEnvelope(bool known_type, const fidl_type_t* type, const Position& position,
TraversalResult* out_traversal_result) {
auto src_envelope = src_dst->Read<const fidl_envelope_t>(position);
if (!src_envelope) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "envelope missing");
}
switch (src_envelope->presence) {
case FIDL_ALLOC_ABSENT: {
const auto status = src_dst->Copy(position, sizeof(fidl_envelope_t));
if (status != ZX_OK) {
return TRANSFORMER_FAIL(status, position, "unable to copy envelope header");
}
return ZX_OK;
}
case FIDL_ALLOC_PRESENT:
// We write the transformed envelope after the transformation, since
// the num_bytes may be different in the dst.
break;
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "envelope presence invalid");
}
if (!known_type) {
// When we encounter an unknown type, the best we can do is to copy the
// envelope header (which includes the num_bytes and num_handles), and
// copy the envelope's data. While it's possible that transformation was
// needed, since we do not have the type, we cannot perform it.
const auto status_copy_hdr = src_dst->Copy(position, sizeof(fidl_envelope_t));
if (status_copy_hdr != ZX_OK) {
return TRANSFORMER_FAIL(status_copy_hdr, position,
"unable to copy envelope header (unknown type)");
}
const auto data_position =
Position{position.src_out_of_line_offset,
position.src_out_of_line_offset + src_envelope->num_bytes,
position.dst_out_of_line_offset,
position.dst_out_of_line_offset + src_envelope->num_bytes};
const auto status_copy_data = src_dst->Copy(data_position, src_envelope->num_bytes);
if (status_copy_data != ZX_OK) {
return TRANSFORMER_FAIL(status_copy_data, data_position,
"unable to copy envelope data (unknown type)");
}
out_traversal_result->src_out_of_line_size += src_envelope->num_bytes;
out_traversal_result->dst_out_of_line_size += src_envelope->num_bytes;
out_traversal_result->handle_count += src_envelope->num_handles;
return ZX_OK;
}
const uint32_t src_contents_inline_size = [&] {
if (!type) {
// The envelope contents are either a primitive or an array of primitives,
// because |type| is nullptr. There's no size information
// available for the type in the coding tables, but since the data is a
// primitive or array of primitives, there can never be any out-of-line
// data, so it's safe to use the envelope's num_bytes to determine the
// content's inline size.
return src_envelope->num_bytes;
}
return InlineSize(type, From(), position);
}();
const uint32_t dst_contents_inline_size = FIDL_ALIGN(AltInlineSize(type, position));
Position data_position = Position{position.src_out_of_line_offset,
position.src_out_of_line_offset + src_contents_inline_size,
position.dst_out_of_line_offset,
position.dst_out_of_line_offset + dst_contents_inline_size};
TraversalResult contents_traversal_result;
zx_status_t result =
Transform(type, data_position, dst_contents_inline_size, &contents_traversal_result);
if (result != ZX_OK) {
return result;
}
const uint32_t src_contents_size =
FIDL_ALIGN(src_contents_inline_size) + contents_traversal_result.src_out_of_line_size;
const uint32_t dst_contents_size =
dst_contents_inline_size + contents_traversal_result.dst_out_of_line_size;
fidl_envelope_t dst_envelope = *src_envelope;
dst_envelope.num_bytes = dst_contents_size;
const auto status_write = src_dst->Write(position, dst_envelope);
if (status_write != ZX_OK) {
return TRANSFORMER_FAIL(status_write, position, "unable to write envelope");
}
out_traversal_result->src_out_of_line_size += src_contents_size;
out_traversal_result->dst_out_of_line_size += dst_contents_size;
out_traversal_result->handle_count += src_envelope->num_handles;
return ZX_OK;
}
zx_status_t TransformXUnion(const FidlCodedXUnion& coded_xunion, const Position& position,
TraversalResult* out_traversal_result) {
auto xunion = src_dst->Read<const fidl_xunion_t>(position);
if (!xunion) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "xunion missing");
}
const auto status_copy_xunion_hdr = src_dst->Copy(position, sizeof(fidl_xunion_t));
if (status_copy_xunion_hdr != ZX_OK) {
return status_copy_xunion_hdr;
}
const FidlXUnionField* field = nullptr;
for (uint32_t i = 0; i < coded_xunion.field_count; i++) {
const FidlXUnionField* candidate_field = coded_xunion.fields + i;
if (candidate_field->ordinal == xunion->tag) {
field = candidate_field;
break;
}
}
const Position envelope_position = {
position.src_inline_offset + static_cast<uint32_t>(offsetof(fidl_xunion_t, envelope)),
position.src_out_of_line_offset,
position.dst_inline_offset + static_cast<uint32_t>(offsetof(fidl_xunion_t, envelope)),
position.dst_out_of_line_offset,
};
return TransformEnvelope(field != nullptr, field ? field->type : nullptr, envelope_position,
out_traversal_result);
}
zx_status_t TransformTable(const FidlCodedTable& coded_table, const Position& position,
TraversalResult* out_traversal_result) {
auto table = src_dst->Read<const fidl_table_t>(position);
if (!table) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "table header missing");
}
const auto status_copy_table_hdr = src_dst->Copy(position, sizeof(fidl_table_t));
if (status_copy_table_hdr != ZX_OK) {
return TRANSFORMER_FAIL(status_copy_table_hdr, position, "unable to copy table header");
}
const uint32_t envelopes_vector_size =
static_cast<uint32_t>(table->envelopes.count * sizeof(fidl_envelope_t));
out_traversal_result->src_out_of_line_size += envelopes_vector_size;
out_traversal_result->dst_out_of_line_size += envelopes_vector_size;
auto current_envelope_position = Position{
position.src_out_of_line_offset,
position.src_out_of_line_offset + envelopes_vector_size,
position.dst_out_of_line_offset,
position.dst_out_of_line_offset + envelopes_vector_size,
};
const auto max_field_index = coded_table.field_count - 1;
for (uint32_t ordinal = 1, field_index = 0; ordinal <= table->envelopes.count; ordinal++) {
const FidlTableField& field = coded_table.fields[field_index];
const bool known_field = (ordinal == field.ordinal);
if (known_field) {
if (field_index < max_field_index)
field_index++;
}
TraversalResult envelope_traversal_result;
zx_status_t status = TransformEnvelope(known_field, known_field ? field.type : nullptr,
current_envelope_position, &envelope_traversal_result);
if (status != ZX_OK) {
return status;
}
current_envelope_position.src_inline_offset += static_cast<uint32_t>(sizeof(fidl_envelope_t));
current_envelope_position.dst_inline_offset += static_cast<uint32_t>(sizeof(fidl_envelope_t));
current_envelope_position.src_out_of_line_offset +=
envelope_traversal_result.src_out_of_line_size;
current_envelope_position.dst_out_of_line_offset +=
envelope_traversal_result.dst_out_of_line_size;
*out_traversal_result += envelope_traversal_result;
}
return ZX_OK;
}
zx_status_t TransformArray(const FidlCodedArrayNew& src_coded_array,
const FidlCodedArrayNew& dst_coded_array, const Position& position,
uint32_t dst_array_size, TraversalResult* out_traversal_result) {
TRANSFORMER_ASSERT(src_coded_array.element_count == dst_coded_array.element_count, position);
// Fast path for elements without coding tables (e.g. strings).
if (!src_coded_array.element) {
return src_dst->Copy(position, dst_array_size);
}
// Slow path otherwise.
auto current_element_position = position;
for (uint32_t i = 0; i < src_coded_array.element_count; i++) {
TraversalResult element_traversal_result;
const zx_status_t status = Transform(src_coded_array.element, current_element_position,
dst_coded_array.element_size, &element_traversal_result);
if (status != ZX_OK) {
return status;
}
// Pad end of an element.
auto padding_position =
current_element_position.IncreaseSrcInlineOffset(src_coded_array.element_size)
.IncreaseDstInlineOffset(dst_coded_array.element_size);
const auto status_pad = src_dst->Pad(padding_position, dst_coded_array.element_padding);
if (status_pad != ZX_OK) {
return TRANSFORMER_FAIL(status_pad, padding_position, "unable to pad array element");
}
current_element_position =
padding_position.IncreaseSrcInlineOffset(src_coded_array.element_padding)
.IncreaseDstInlineOffset(dst_coded_array.element_padding)
.IncreaseSrcOutOfLineOffset(element_traversal_result.src_out_of_line_size)
.IncreaseDstOutOfLineOffset(element_traversal_result.dst_out_of_line_size);
*out_traversal_result += element_traversal_result;
}
// Pad end of elements.
uint32_t padding =
dst_array_size + position.dst_inline_offset - current_element_position.dst_inline_offset;
const auto status_pad = src_dst->Pad(current_element_position, padding);
if (status_pad != ZX_OK) {
return TRANSFORMER_FAIL(status_pad, current_element_position, "unable to pad end of array");
}
return ZX_OK;
}
zx_status_t TransformUnionPointerToOptionalXUnion(const FidlCodedUnion& src_coded_union,
const FidlCodedXUnion& dst_coded_xunion,
const Position& position,
TraversalResult* out_traversal_result) {
auto presence = src_dst->Read<uint64_t>(position);
if (!presence) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "union pointer missing");
}
switch (*presence) {
case FIDL_ALLOC_ABSENT: {
fidl_xunion_t absent = {};
const auto status = src_dst->Write(position, absent);
if (status != ZX_OK) {
return TRANSFORMER_FAIL(status, position, "unable to write union pointer absense");
}
return ZX_OK;
}
case FIDL_ALLOC_PRESENT:
// Ok
break;
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "union pointer invalid");
}
uint32_t src_aligned_size = FIDL_ALIGN(src_coded_union.size);
const auto union_position = Position{
position.src_out_of_line_offset,
position.src_out_of_line_offset + src_aligned_size,
position.dst_inline_offset,
position.dst_out_of_line_offset,
};
out_traversal_result->src_out_of_line_size += src_aligned_size;
return TransformUnionToXUnion(src_coded_union, dst_coded_xunion, union_position,
0 /* unused: xunions are FIDL_ALIGNed */, out_traversal_result);
}
zx_status_t TransformUnionToXUnion(const FidlCodedUnion& src_coded_union,
const FidlCodedXUnion& dst_coded_xunion,
const Position& position, uint32_t /* unused: dst_size */,
TraversalResult* out_traversal_result) {
TRANSFORMER_ASSERT(src_coded_union.field_count == dst_coded_xunion.field_count, position);
// Read: union tag.
const auto union_tag_ptr =
src_dst->Read<const fidl_union_tag_t>(position, src_coded_union.size);
if (!union_tag_ptr) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "union tag missing");
}
const auto union_tag = *union_tag_ptr;
// Retrieve: union field/variant.
if (union_tag >= src_coded_union.field_count) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "invalid union tag");
}
const FidlUnionField& src_field = src_coded_union.fields[union_tag];
const FidlXUnionField& dst_field = dst_coded_xunion.fields[union_tag];
// Write: xunion tag & envelope.
const uint32_t dst_inline_field_size = [&] {
if (src_field.type) {
return AltInlineSize(src_field.type, position);
} else {
return src_coded_union.size - src_coded_union.data_offset - src_field.padding;
}
}();
// Transform: static-union field to xunion field.
auto field_position = Position{
position.src_inline_offset + src_coded_union.data_offset,
position.src_out_of_line_offset,
position.dst_out_of_line_offset,
position.dst_out_of_line_offset + FIDL_ALIGN(dst_inline_field_size),
};
TraversalResult field_traversal_result;
zx_status_t status =
Transform(src_field.type, field_position, dst_inline_field_size, &field_traversal_result);
if (status != ZX_OK) {
return status;
}
// Pad field (if needed).
const uint32_t dst_field_size =
dst_inline_field_size + field_traversal_result.dst_out_of_line_size;
const uint32_t dst_padding = FIDL_ALIGN(dst_field_size) - dst_field_size;
const auto status_pad_field =
src_dst->Pad(field_position.IncreaseDstInlineOffset(dst_field_size), dst_padding);
if (status_pad_field != ZX_OK) {
return TRANSFORMER_FAIL(status_pad_field, field_position,
"unable to pad union-as-xunion variant");
}
// Write envelope header.
fidl_xunion_t xunion;
xunion.tag = dst_field.ordinal;
xunion.envelope.num_bytes = FIDL_ALIGN(dst_field_size);
xunion.envelope.num_handles = field_traversal_result.handle_count;
xunion.envelope.presence = FIDL_ALLOC_PRESENT;
const auto status_write_xunion = src_dst->Write(position, xunion);
if (status_write_xunion != ZX_OK) {
return TRANSFORMER_FAIL(status_write_xunion, position,
"unable to write union-as-xunion header");
}
out_traversal_result->src_out_of_line_size += field_traversal_result.src_out_of_line_size;
out_traversal_result->dst_out_of_line_size += FIDL_ALIGN(dst_field_size);
out_traversal_result->handle_count += field_traversal_result.handle_count;
return ZX_OK;
}
zx_status_t TransformOptionalXUnionToUnionPointer(const FidlCodedXUnion& src_coded_xunion,
const FidlCodedUnion& dst_coded_union,
const Position& position,
TraversalResult* out_traversal_result) {
auto src_xunion = src_dst->Read<const fidl_xunion_t>(position);
if (!src_xunion) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "union-as-xunion missing");
}
switch (src_xunion->envelope.presence) {
case FIDL_ALLOC_ABSENT:
case FIDL_ALLOC_PRESENT: {
const auto status = src_dst->Write(position, src_xunion->envelope.presence);
if (status != ZX_OK) {
return TRANSFORMER_FAIL(status, position, "unable to write union pointer absence");
}
if (src_xunion->envelope.presence == FIDL_ALLOC_ABSENT) {
return ZX_OK;
}
break;
}
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position,
"union-as-xunion envelope presence invalid");
}
const uint32_t dst_aligned_size = FIDL_ALIGN(dst_coded_union.size);
const auto union_position = Position{
position.src_inline_offset,
position.src_out_of_line_offset,
position.dst_out_of_line_offset,
position.dst_out_of_line_offset + dst_aligned_size,
};
out_traversal_result->dst_out_of_line_size += dst_aligned_size;
return TransformXUnionToUnion(src_coded_xunion, dst_coded_union, union_position,
FIDL_ALIGN(dst_coded_union.size), out_traversal_result);
}
zx_status_t TransformXUnionToUnion(const FidlCodedXUnion& src_coded_xunion,
const FidlCodedUnion& dst_coded_union,
const Position& position, uint32_t dst_size,
TraversalResult* out_traversal_result) {
TRANSFORMER_ASSERT(src_coded_xunion.field_count == dst_coded_union.field_count, position);
// Read: extensible-union ordinal.
const auto src_xunion = src_dst->Read<const fidl_xunion_t>(position);
if (!src_xunion) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "union-as-xunion missing");
}
switch (src_xunion->envelope.presence) {
case FIDL_ALLOC_PRESENT:
// OK
break;
case FIDL_ALLOC_ABSENT:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position,
"union-as-xunion envelope is invalid FIDL_ALLOC_ABSENT");
default:
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position,
"union-as-xunion envelope presence invalid");
}
// Retrieve: flexible-union field (or variant).
bool src_field_found = false;
uint32_t src_field_index = 0;
const FidlXUnionField* src_field = nullptr;
for (/* src_field_index needed after the loop */;
src_field_index < src_coded_xunion.field_count; src_field_index++) {
const FidlXUnionField* candidate_src_field = &src_coded_xunion.fields[src_field_index];
if (candidate_src_field->ordinal == src_xunion->tag) {
src_field_found = true;
src_field = candidate_src_field;
break;
}
}
if (!src_field_found) {
return TRANSFORMER_FAIL(ZX_ERR_BAD_STATE, position, "ordinal has no corresponding variant");
}
const FidlUnionField& dst_field = dst_coded_union.fields[src_field_index];
// Write: static-union tag, and pad (if needed).
switch (dst_coded_union.data_offset) {
case 4: {
const auto status = src_dst->Write(position, src_field_index);
if (status != ZX_OK) {
return TRANSFORMER_FAIL(status, position, "unable to write union tag");
}
break;
}
case 8: {
const auto status = src_dst->Write(position, static_cast<uint64_t>(src_field_index));
if (status != ZX_OK) {
return TRANSFORMER_FAIL(status, position, "unable to write union tag");
}
break;
}
default:
TRANSFORMER_ASSERT(false && "static-union data offset can only be 4 or 8", position);
}
const uint32_t src_field_inline_size = [&] {
if (!src_field->type) {
// src_field's type is either a primitive or an array of primitives,
// because src_field->type is nullptr. There's no size information
// available for the field in the coding tables, but since the data is a
// primitive or array of primitives, there can never be any out-of-line
// data, so it's safe to use the envelope's num_bytes to determine the
// field's inline size.
return src_xunion->envelope.num_bytes;
}
return FIDL_ALIGN(InlineSize(src_field->type, From(), position));
}();
// Transform: xunion field to static-union field (or variant).
auto field_position = Position{
position.src_out_of_line_offset,
position.src_out_of_line_offset + src_field_inline_size,
position.dst_inline_offset + dst_coded_union.data_offset,
position.dst_out_of_line_offset,
};
uint32_t dst_field_unpadded_size =
dst_coded_union.size - dst_coded_union.data_offset - dst_field.padding;
zx_status_t status =
Transform(src_field->type, field_position, dst_field_unpadded_size, out_traversal_result);
if (status != ZX_OK) {
return status;
}
// Pad after static-union data.
auto field_padding_position = field_position.IncreaseDstInlineOffset(dst_field_unpadded_size);
const auto dst_padding = (dst_size - dst_coded_union.size) + dst_field.padding;
const auto status_pad_field = src_dst->Pad(field_padding_position, dst_padding);
if (status_pad_field != ZX_OK) {
return TRANSFORMER_FAIL(status_pad_field, field_padding_position,
"unable to pad union variant");
}
out_traversal_result->src_out_of_line_size += src_field_inline_size;
return ZX_OK;
}
virtual WireFormat From() const = 0;
virtual WireFormat To() const = 0;
inline zx_status_t Fail(zx_status_t status, const Position& position, const int line_number,
const char* error_msg) {
debug_info_->RecordFailure(line_number, error_msg, position);
return status;
}
SrcDst* src_dst;
DebugInfo* const debug_info_;
private:
const fidl_type_t* top_level_src_type_;
};
class V1ToOld final : public TransformerBase {
public:
V1ToOld(SrcDst* src_dst, const fidl_type_t* top_level_src_type, DebugInfo* debug_info)
: TransformerBase(src_dst, top_level_src_type, debug_info) {}
WireFormat From() const override { return WireFormat::kV1; }
WireFormat To() const override { return WireFormat::kOld; }
};
class OldToV1 final : public TransformerBase {
public:
OldToV1(SrcDst* src_dst, const fidl_type_t* top_level_src_type, DebugInfo* debug_info)
: TransformerBase(src_dst, top_level_src_type, debug_info) {}
private:
WireFormat From() const override { return WireFormat::kOld; }
WireFormat To() const override { return WireFormat::kV1; }
};
} // namespace
zx_status_t fidl_transform(fidl_transformation_t transformation, const fidl_type_t* src_type,
const uint8_t* src_bytes, uint32_t src_num_bytes, uint8_t* dst_bytes,
uint32_t dst_num_bytes_capacity, uint32_t* out_dst_num_bytes,
const char** out_error_msg) {
if (!src_type || !src_bytes || !dst_bytes || !out_dst_num_bytes || !FidlIsAligned(src_bytes) ||
!FidlIsAligned(dst_bytes)) {
return ZX_ERR_INVALID_ARGS;
}
SrcDst src_dst(src_bytes, src_num_bytes, dst_bytes, dst_num_bytes_capacity);
DebugInfo debug_info(transformation, src_type, src_dst, out_error_msg);
const zx_status_t status = [&] {
switch (transformation) {
case FIDL_TRANSFORMATION_NONE: {
const auto start = Position{
0, UINT16_MAX, /* unused: src_out_of_line_offset */
0, UINT16_MAX, /* unused: dst_out_of_line_offset */
};
return src_dst.Copy(start, src_num_bytes);
}
case FIDL_TRANSFORMATION_V1_TO_OLD:
return V1ToOld(&src_dst, src_type, &debug_info).TransformTopLevelStruct();
case FIDL_TRANSFORMATION_OLD_TO_V1:
return OldToV1(&src_dst, src_type, &debug_info).TransformTopLevelStruct();
default:
debug_info.RecordFailure(__LINE__, "unsupported transformation");
return ZX_ERR_INVALID_ARGS;
}
}();
if (status != ZX_OK) {
return status;
}
if (FIDL_ALIGN(src_dst.src_max_offset_read()) != src_num_bytes) {
debug_info.RecordFailure(__LINE__, "did not read all provided bytes during transformation");
return ZX_ERR_INVALID_ARGS;
}
*out_dst_num_bytes = src_dst.dst_max_offset_written();
return ZX_OK;
}
#pragma GCC diagnostic pop // "-Wimplicit-fallthrough"