| // 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. |
| |
| #include "src/lib/fidl/transformer/transformer.h" |
| |
| #include <lib/fidl/internal.h> |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <cstring> |
| |
| #define SRC_VALUE(v1_value, v2_value) \ |
| ((transformation_ == FIDL_TRANSFORMATION_V1_TO_V2) ? (v1_value) : (v2_value)) |
| #define DST_VALUE(v1_value, v2_value) \ |
| ((transformation_ == FIDL_TRANSFORMATION_V1_TO_V2) ? (v2_value) : (v1_value)) |
| |
| namespace { |
| |
| struct WireEnvelopeV1 { |
| uint32_t num_bytes; |
| uint32_t num_handles; |
| uint64_t presence_marker; |
| }; |
| |
| struct WireEnvelopeV2 { |
| union { |
| uint32_t num_bytes; |
| uint8_t inlined_value[4]; |
| }; |
| uint16_t num_handles; |
| uint16_t flags; |
| }; |
| |
| constexpr uint16_t EmptyFlags = 0x00; |
| constexpr uint16_t InlinedEnvelopeFlag = 0x01; |
| constexpr bool ValidFlags(const WireEnvelopeV2 envelope) { |
| return (envelope.flags & ~InlinedEnvelopeFlag) == 0; |
| } |
| constexpr bool IsInlined(const WireEnvelopeV2 envelope) { |
| return (envelope.flags & InlinedEnvelopeFlag) != 0; |
| } |
| |
| struct WireTableHeader { |
| uint64_t count; |
| uint64_t presence_marker; |
| }; |
| |
| constexpr uint32_t PrimitiveSize(const FidlCodedPrimitiveSubtype primitive) { |
| switch (primitive) { |
| case kFidlCodedPrimitiveSubtype_Bool: |
| case kFidlCodedPrimitiveSubtype_Int8: |
| case kFidlCodedPrimitiveSubtype_Uint8: |
| return 1; |
| case kFidlCodedPrimitiveSubtype_Int16: |
| case kFidlCodedPrimitiveSubtype_Uint16: |
| return 2; |
| case kFidlCodedPrimitiveSubtype_Int32: |
| case kFidlCodedPrimitiveSubtype_Uint32: |
| case kFidlCodedPrimitiveSubtype_Float32: |
| return 4; |
| case kFidlCodedPrimitiveSubtype_Int64: |
| case kFidlCodedPrimitiveSubtype_Uint64: |
| case kFidlCodedPrimitiveSubtype_Float64: |
| return 8; |
| } |
| __builtin_unreachable(); |
| } |
| |
| constexpr uint32_t TypeSizeV1(const fidl_type_t* type) { |
| switch (type->type_tag()) { |
| case kFidlTypePrimitive: |
| return PrimitiveSize(type->coded_primitive().type); |
| case kFidlTypeEnum: |
| return PrimitiveSize(type->coded_enum().underlying_type); |
| case kFidlTypeBits: |
| return PrimitiveSize(type->coded_bits().underlying_type); |
| case kFidlTypeStructPointer: |
| return sizeof(uint64_t); |
| case kFidlTypeHandle: |
| return sizeof(zx_handle_t); |
| case kFidlTypeStruct: |
| return type->coded_struct().size_v1; |
| case kFidlTypeTable: |
| return sizeof(fidl_vector_t); |
| case kFidlTypeXUnion: |
| return 24; |
| case kFidlTypeString: |
| return sizeof(fidl_string_t); |
| case kFidlTypeArray: |
| return type->coded_array().array_size_v1; |
| case kFidlTypeVector: |
| return sizeof(fidl_vector_t); |
| } |
| __builtin_unreachable(); |
| } |
| |
| constexpr uint32_t TypeSizeV2(const fidl_type_t* type) { |
| switch (type->type_tag()) { |
| case kFidlTypePrimitive: |
| return PrimitiveSize(type->coded_primitive().type); |
| case kFidlTypeEnum: |
| return PrimitiveSize(type->coded_enum().underlying_type); |
| case kFidlTypeBits: |
| return PrimitiveSize(type->coded_bits().underlying_type); |
| case kFidlTypeStructPointer: |
| return sizeof(uint64_t); |
| case kFidlTypeHandle: |
| return sizeof(zx_handle_t); |
| case kFidlTypeStruct: |
| return type->coded_struct().size_v2; |
| case kFidlTypeTable: |
| return sizeof(fidl_vector_t); |
| case kFidlTypeXUnion: |
| return 16; |
| case kFidlTypeString: |
| return sizeof(fidl_string_t); |
| case kFidlTypeArray: |
| return type->coded_array().array_size_v2; |
| case kFidlTypeVector: |
| return sizeof(fidl_vector_t); |
| } |
| __builtin_unreachable(); |
| } |
| |
| // Transformer that converts between the V1 and V2 wire formats (and vice versa). |
| // |
| // The invariant held by Transform* methods is that each Transform* method is responsible for |
| // writing "itself" to the output. That is, TransformVector will ensure the vector header |
| // and body is converted to the appropriate wire format and copied. |
| class Transformer { |
| public: |
| Transformer(fidl_transformation_t transformation, const uint8_t* src_bytes, |
| uint32_t src_num_bytes, uint8_t* dst_bytes, uint32_t dst_num_bytes_capacity) |
| : transformation_(transformation), |
| src_bytes_(src_bytes), |
| dst_bytes_(dst_bytes), |
| src_next_out_of_line_(0), |
| dst_next_out_of_line_(0), |
| src_num_bytes_(src_num_bytes), |
| dst_num_bytes_capacity_(dst_num_bytes_capacity), |
| error(nullptr) { |
| ZX_ASSERT(transformation_ == FIDL_TRANSFORMATION_V1_TO_V2 || |
| transformation_ == FIDL_TRANSFORMATION_V2_TO_V1); |
| ZX_ASSERT(FidlIsAligned(src_bytes)); |
| ZX_ASSERT(FidlIsAligned(dst_bytes)); |
| } |
| |
| // Performs a transform inside of a primary / new out of line object. |
| zx_status_t Transform(const fidl_type_t* type, uint32_t src_offset, uint32_t dst_offset) { |
| zx_status_t status = |
| IncreaseNextOutOfLineV1V2(FIDL_ALIGN(TypeSizeV1(type)), FIDL_ALIGN(TypeSizeV2(type))); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return TransformInline(type, src_offset, dst_offset); |
| } |
| |
| const char* err_msg() { return error; } |
| uint32_t dst_num_bytes() { return dst_next_out_of_line_; } |
| |
| private: |
| // Performs a transform inside of an inline object. |
| zx_status_t TransformInline(const fidl_type_t* type, uint32_t src_offset, uint32_t dst_offset) { |
| switch (type->type_tag()) { |
| case kFidlTypePrimitive: |
| return TransformPrimitive(&type->coded_primitive(), src_offset, dst_offset); |
| case kFidlTypeEnum: |
| return TransformEnum(&type->coded_enum(), src_offset, dst_offset); |
| case kFidlTypeBits: |
| return TransformBits(&type->coded_bits(), src_offset, dst_offset); |
| case kFidlTypeHandle: |
| return TransformHandle(&type->coded_handle(), src_offset, dst_offset); |
| case kFidlTypeStruct: |
| return TransformStruct(&type->coded_struct(), src_offset, dst_offset); |
| case kFidlTypeArray: |
| return TransformArray(&type->coded_array(), src_offset, dst_offset); |
| case kFidlTypeStructPointer: |
| return TransformStructPointer(&type->coded_struct_pointer(), src_offset, dst_offset); |
| case kFidlTypeString: |
| return TransformString(&type->coded_string(), src_offset, dst_offset); |
| case kFidlTypeVector: |
| return TransformVector(&type->coded_vector(), src_offset, dst_offset); |
| case kFidlTypeXUnion: |
| return TransformXUnion(&type->coded_xunion(), src_offset, dst_offset); |
| case kFidlTypeTable: |
| return TransformTable(&type->coded_table(), src_offset, dst_offset); |
| } |
| __builtin_unreachable(); |
| } |
| |
| zx_status_t TransformPrimitive(const FidlCodedPrimitive* coded_primitive, uint32_t src_offset, |
| uint32_t dst_offset) { |
| uint32_t size = PrimitiveSize(coded_primitive->type); |
| memcpy(&dst_bytes_[dst_offset], &src_bytes_[src_offset], size); |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformEnum(const FidlCodedEnum* coded_enum, uint32_t src_offset, |
| uint32_t dst_offset) { |
| uint32_t size = PrimitiveSize(coded_enum->underlying_type); |
| memcpy(&dst_bytes_[dst_offset], &src_bytes_[src_offset], size); |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformBits(const FidlCodedBits* coded_bits, uint32_t src_offset, |
| uint32_t dst_offset) { |
| uint32_t size = PrimitiveSize(coded_bits->underlying_type); |
| memcpy(&dst_bytes_[dst_offset], &src_bytes_[src_offset], size); |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformHandle(const FidlCodedHandle* coded_handle, uint32_t src_offset, |
| uint32_t dst_offset) { |
| memcpy(&dst_bytes_[dst_offset], &src_bytes_[src_offset], sizeof(zx_handle_t)); |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformStruct(const FidlCodedStruct* coded_struct, uint32_t src_offset, |
| uint32_t dst_offset) { |
| // 1. Copy up to the next non-padding field. |
| // 2. Call the inner transformer for that field. |
| // 3. Repeat 1 & 2 as needed. |
| // 4. Copy up to the end of the struct. |
| |
| uint32_t inner_src_offset = 0; |
| uint32_t inner_dst_offset = 0; |
| for (uint32_t i = 0; i < coded_struct->element_count; i++) { |
| const struct FidlStructElement& element = coded_struct->elements[i]; |
| switch (element.header.element_type) { |
| case kFidlStructElementType_Field: { |
| uint32_t new_inner_src_offset = |
| SRC_VALUE(element.field.offset_v1, element.field.offset_v2); |
| uint32_t new_inner_dst_offset = |
| DST_VALUE(element.field.offset_v1, element.field.offset_v2); |
| |
| ZX_ASSERT(new_inner_src_offset - inner_src_offset == |
| new_inner_dst_offset - inner_dst_offset); |
| if (new_inner_src_offset > inner_src_offset) { |
| memcpy(&dst_bytes_[dst_offset + inner_dst_offset], |
| &src_bytes_[src_offset + inner_src_offset], |
| new_inner_src_offset - inner_src_offset); |
| } |
| |
| zx_status_t status = |
| TransformInline(element.field.field_type, src_offset + new_inner_src_offset, |
| dst_offset + new_inner_dst_offset); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| inner_src_offset = new_inner_src_offset + SRC_VALUE(TypeSizeV1(element.field.field_type), |
| TypeSizeV2(element.field.field_type)); |
| inner_dst_offset = new_inner_dst_offset + DST_VALUE(TypeSizeV1(element.field.field_type), |
| TypeSizeV2(element.field.field_type)); |
| } break; |
| case kFidlStructElementType_Padding64: |
| break; |
| case kFidlStructElementType_Padding32: |
| break; |
| case kFidlStructElementType_Padding16: |
| break; |
| } |
| } |
| |
| uint32_t src_size = SRC_VALUE(coded_struct->size_v1, coded_struct->size_v2); |
| uint32_t dst_size = DST_VALUE(coded_struct->size_v1, coded_struct->size_v2); |
| ZX_ASSERT(src_size - inner_src_offset == dst_size - inner_dst_offset); |
| if (src_size > inner_src_offset) { |
| memcpy(&dst_bytes_[dst_offset + inner_dst_offset], &src_bytes_[src_offset + inner_src_offset], |
| src_size - inner_src_offset); |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformArray(const FidlCodedArray* coded_array, uint32_t src_offset, |
| uint32_t dst_offset) { |
| uint32_t src_element_size = |
| SRC_VALUE(coded_array->element_size_v1, coded_array->element_size_v2); |
| uint32_t dst_element_size = |
| DST_VALUE(coded_array->element_size_v1, coded_array->element_size_v2); |
| uint32_t src_offset_end = |
| src_offset + SRC_VALUE(coded_array->array_size_v1, coded_array->array_size_v2); |
| |
| for (; src_offset < src_offset_end; |
| src_offset += src_element_size, dst_offset += dst_element_size) { |
| zx_status_t status = TransformInline(coded_array->element, src_offset, dst_offset); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformStructPointer(const FidlCodedStructPointer* coded_struct_pointer, |
| uint32_t src_offset, uint32_t dst_offset) { |
| switch (src_u64(src_offset)) { |
| case FIDL_ALLOC_ABSENT: |
| dst_u64(dst_offset) = FIDL_ALLOC_ABSENT; |
| return ZX_OK; |
| case FIDL_ALLOC_PRESENT: { |
| dst_u64(dst_offset) = FIDL_ALLOC_PRESENT; |
| |
| uint32_t src_next_offset = src_next_out_of_line_; |
| uint32_t dst_next_offset = dst_next_out_of_line_; |
| |
| zx_status_t status = |
| IncreaseNextOutOfLineV1V2(FIDL_ALIGN(coded_struct_pointer->struct_type->size_v1), |
| FIDL_ALIGN(coded_struct_pointer->struct_type->size_v2)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| return TransformStruct(coded_struct_pointer->struct_type, src_next_offset, dst_next_offset); |
| } |
| default: |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| |
| zx_status_t TransformString(const FidlCodedString* coded_string, uint32_t src_offset, |
| uint32_t dst_offset) { |
| // Copy count. |
| uint64_t count = src_u64(src_offset); |
| dst_u64(dst_offset) = count; |
| |
| // Copy presence marker. |
| if (count > 0 && src_u64(src_offset + 8) != FIDL_ALLOC_PRESENT) { |
| error = "expected present marker on non-empty string"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| dst_u64(dst_offset + 8) = src_u64(src_offset + 8); |
| |
| // Copy body. |
| uint32_t src_body_offset = src_next_out_of_line_; |
| uint32_t dst_body_offset = dst_next_out_of_line_; |
| zx_status_t status = IncreaseNextOutOfLine(FIDL_ALIGN(count), FIDL_ALIGN(count)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| memcpy(&dst_bytes_[dst_body_offset], &src_bytes_[src_body_offset], count); |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformVector(const FidlCodedVector* coded_vector, uint32_t src_offset, |
| uint32_t dst_offset) { |
| // Copy count. |
| uint64_t count = src_u64(src_offset); |
| dst_u64(dst_offset) = count; |
| |
| // Copy presence marker. |
| if (count > 0 && src_u64(src_offset + 8) != FIDL_ALLOC_PRESENT) { |
| error = "expected present marker on non-empty vector"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| dst_u64(dst_offset + 8) = src_u64(src_offset + 8); |
| |
| uint32_t src_element_size = |
| SRC_VALUE(coded_vector->element_size_v1, coded_vector->element_size_v2); |
| uint32_t dst_element_size = |
| DST_VALUE(coded_vector->element_size_v1, coded_vector->element_size_v2); |
| |
| uint32_t src_body_offset = src_next_out_of_line_; |
| uint32_t dst_body_offset = dst_next_out_of_line_; |
| zx_status_t status = IncreaseNextOutOfLine(FIDL_ALIGN(count * src_element_size), |
| FIDL_ALIGN(count * dst_element_size)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| for (uint32_t i = 0; i < count; i++) { |
| status = TransformInline(coded_vector->element, src_body_offset, dst_body_offset); |
| if (status != ZX_OK) { |
| return status; |
| } |
| src_body_offset += src_element_size; |
| dst_body_offset += dst_element_size; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformEnvelopeV1ToV2(const fidl_type_t* type, uint32_t src_offset, |
| uint32_t dst_offset) { |
| WireEnvelopeV1 src_envelope = src<WireEnvelopeV1>(src_offset); |
| |
| switch (src_envelope.presence_marker) { |
| case FIDL_ALLOC_PRESENT: |
| if (src_envelope.num_bytes == 0) { |
| error = "envelope is present but num_bytes is 0"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| break; |
| case FIDL_ALLOC_ABSENT: |
| if (src_envelope.num_bytes == 0) { |
| return ZX_OK; |
| } else { |
| error = "envelope is absent but num_bytes is not 0"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| break; |
| default: |
| error = "invalid presence marker"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (src_envelope.num_handles > std::numeric_limits<uint16_t>::max()) { |
| error = "num_handles exceeds the maximum value that fits in a uint16_t"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (type == nullptr) { |
| // Unknown value. |
| if (src_envelope.num_bytes % 8 != 0) { |
| error = "unknown value contained non 8-byte aligned payload"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| dst<WireEnvelopeV2>(dst_offset) = WireEnvelopeV2{ |
| .num_bytes = src_envelope.num_bytes, |
| .num_handles = static_cast<uint16_t>(src_envelope.num_handles), |
| .flags = EmptyFlags, |
| }; |
| memcpy(&dst_bytes_[dst_next_out_of_line_], &src_bytes_[src_next_out_of_line_], |
| src_envelope.num_bytes); |
| return IncreaseNextOutOfLine(src_envelope.num_bytes, src_envelope.num_bytes); |
| } |
| |
| if (TypeSizeV2(type) <= 4) { |
| // Inlined value. |
| WireEnvelopeV2 dst_envelope{ |
| .num_handles = static_cast<uint16_t>(src_envelope.num_handles), |
| .flags = InlinedEnvelopeFlag, |
| }; |
| memcpy(dst_envelope.inlined_value, &src_bytes_[src_next_out_of_line_], |
| sizeof(dst_envelope.inlined_value)); |
| dst<WireEnvelopeV2>(dst_offset) = dst_envelope; |
| return IncreaseNextOutOfLine(8, 0); |
| } |
| |
| uint32_t checkpoint_dst_next_out_of_line = dst_next_out_of_line_; |
| zx_status_t status = Transform(type, src_next_out_of_line_, dst_next_out_of_line_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| dst<WireEnvelopeV2>(dst_offset) = WireEnvelopeV2{ |
| .num_bytes = dst_next_out_of_line_ - checkpoint_dst_next_out_of_line, |
| .num_handles = static_cast<uint16_t>(src_envelope.num_handles), |
| .flags = EmptyFlags, |
| }; |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformEnvelopeV2ToV1(const fidl_type_t* type, uint32_t src_offset, |
| uint32_t dst_offset) { |
| WireEnvelopeV2 src_envelope = src<WireEnvelopeV2>(src_offset); |
| |
| if (!ValidFlags(src_envelope)) { |
| error = "invalid inline marker"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (IsInlined(src_envelope)) { |
| dst<WireEnvelopeV1>(dst_offset) = WireEnvelopeV1{ |
| .num_bytes = 8, |
| .num_handles = src_envelope.num_handles, |
| .presence_marker = FIDL_ALLOC_PRESENT, |
| }; |
| |
| memcpy(&dst_bytes_[dst_next_out_of_line_], src_envelope.inlined_value, |
| sizeof(src_envelope.inlined_value)); |
| memset(&dst_bytes_[dst_next_out_of_line_ + 4], 0, 4); |
| return IncreaseNextOutOfLine(0, 8); |
| } |
| |
| if (src_envelope.num_bytes > 0 || src_envelope.num_handles > 0) { |
| if (type != nullptr) { |
| uint32_t checkpoint_dst_next_out_of_line = dst_next_out_of_line_; |
| zx_status_t status = Transform(type, src_next_out_of_line_, dst_next_out_of_line_); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| dst<WireEnvelopeV1>(dst_offset) = WireEnvelopeV1{ |
| .num_bytes = dst_next_out_of_line_ - checkpoint_dst_next_out_of_line, |
| .num_handles = src_envelope.num_handles, |
| .presence_marker = FIDL_ALLOC_PRESENT, |
| }; |
| return ZX_OK; |
| } |
| |
| // Unknown value. |
| if (src_envelope.num_bytes % 8 != 0) { |
| error = "unknown value contained non 8-byte aligned payload"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| dst<WireEnvelopeV1>(dst_offset) = WireEnvelopeV1{ |
| .num_bytes = src_envelope.num_bytes, |
| .num_handles = src_envelope.num_handles, |
| .presence_marker = FIDL_ALLOC_PRESENT, |
| }; |
| memcpy(&dst_bytes_[dst_next_out_of_line_], &src_bytes_[src_next_out_of_line_], |
| src_envelope.num_bytes); |
| |
| return IncreaseNextOutOfLine(src_envelope.num_bytes, src_envelope.num_bytes); |
| } |
| |
| dst<WireEnvelopeV1>(dst_offset) = WireEnvelopeV1{ |
| .num_bytes = 0, |
| .num_handles = src_envelope.num_handles, |
| .presence_marker = FIDL_ALLOC_ABSENT, |
| }; |
| return ZX_OK; |
| } |
| |
| zx_status_t TransformEnvelope(const fidl_type_t* type, uint32_t src_offset, uint32_t dst_offset) { |
| if (transformation_ == FIDL_TRANSFORMATION_V2_TO_V1) { |
| return TransformEnvelopeV2ToV1(type, src_offset, dst_offset); |
| } else { |
| return TransformEnvelopeV1ToV2(type, src_offset, dst_offset); |
| } |
| } |
| |
| zx_status_t TransformXUnion(const FidlCodedXUnion* coded_xunion, uint32_t src_offset, |
| uint32_t dst_offset) { |
| uint64_t ordinal = src_u64(src_offset); |
| dst_u64(dst_offset) = ordinal; |
| const fidl_type_t* field_type = nullptr; |
| if (ordinal > 0 && ordinal <= coded_xunion->field_count) { |
| field_type = coded_xunion->fields[ordinal - 1].type; |
| } |
| return TransformEnvelope(field_type, src_offset + sizeof(ordinal), |
| dst_offset + sizeof(ordinal)); |
| } |
| |
| zx_status_t TransformTable(const FidlCodedTable* coded_table, uint32_t src_offset, |
| uint32_t dst_offset) { |
| const WireTableHeader& src_table_header = |
| *reinterpret_cast<const WireTableHeader*>(&src_bytes_[src_offset]); |
| WireTableHeader& dst_table_header = |
| *reinterpret_cast<WireTableHeader*>(&dst_bytes_[dst_offset]); |
| |
| if (src_table_header.count > std::numeric_limits<uint32_t>::max()) { |
| error = "count exceeds the maximum value that fits in a uint32_t"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (src_table_header.count > 0 && src_table_header.presence_marker != FIDL_ALLOC_PRESENT) { |
| error = "expected present marker on non-empty table"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| dst_table_header.count = src_table_header.count; |
| dst_table_header.presence_marker = src_table_header.presence_marker; |
| |
| uint32_t src_element_size = SRC_VALUE(16, 8); |
| uint32_t dst_element_size = DST_VALUE(16, 8); |
| uint32_t src_body_offset = src_next_out_of_line_; |
| uint32_t dst_body_offset = dst_next_out_of_line_; |
| zx_status_t status = |
| IncreaseNextOutOfLine(static_cast<uint32_t>(src_table_header.count) * src_element_size, |
| static_cast<uint32_t>(src_table_header.count) * dst_element_size); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Process the table body. |
| for (uint32_t i = 0; i < src_table_header.count; i++) { |
| const fidl_type_t* field_type = nullptr; |
| if (i < coded_table->field_count) { |
| field_type = coded_table->fields[i].type; |
| } |
| zx_status_t status = TransformEnvelope(field_type, src_body_offset, dst_body_offset); |
| if (status != ZX_OK) { |
| return status; |
| } |
| src_body_offset += src_element_size; |
| dst_body_offset += dst_element_size; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t IncreaseNextOutOfLineV1V2(uint32_t v1_size, uint32_t v2_size) { |
| return IncreaseNextOutOfLine(SRC_VALUE(v1_size, v2_size), DST_VALUE(v1_size, v2_size)); |
| } |
| |
| zx_status_t IncreaseNextOutOfLine(uint32_t src_size, uint32_t dst_size) { |
| ZX_ASSERT(src_size % 8 == 0); |
| ZX_ASSERT(dst_size % 8 == 0); |
| |
| uint32_t new_src_next_out_of_line; |
| if (add_overflow(src_next_out_of_line_, src_size, &new_src_next_out_of_line)) { |
| error = "overflow in src_next_out_of_line"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (new_src_next_out_of_line > src_num_bytes_) { |
| error = "exceeded src array size"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| src_next_out_of_line_ = new_src_next_out_of_line; |
| |
| uint32_t new_dst_next_out_of_line; |
| if (add_overflow(dst_next_out_of_line_, dst_size, &new_dst_next_out_of_line)) { |
| error = "overflow in dst_next_out_of_line"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (new_dst_next_out_of_line > dst_num_bytes_capacity_) { |
| error = "exceeded dst array size"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| dst_next_out_of_line_ = new_dst_next_out_of_line; |
| |
| return ZX_OK; |
| } |
| |
| template <typename T> |
| T src(uint32_t offset) { |
| ZX_ASSERT(offset % 8 == 0); |
| // Use memcpy rather than reinterpret_cast to avoid issues |
| // due to the strict aliasing rule. |
| T value; |
| memcpy(&value, &src_bytes_[offset], sizeof(T)); |
| return value; |
| } |
| |
| uint64_t src_u64(uint32_t offset) { return src<uint64_t>(offset); } |
| |
| // Target to facillitate assignment with dst() and dst_u64(). |
| // e.g. dst(offset) = value; |
| template <typename T> |
| class DstTarget { |
| public: |
| DstTarget(Transformer* transformer, uint32_t offset) |
| : transformer_(transformer), offset_(offset) {} |
| |
| DstTarget& operator=(const T& value) { |
| // Use memcpy rather than reinterpret_cast to avoid issues |
| // due to the strict aliasing rule. |
| memcpy(&transformer_->dst_bytes_[offset_], &value, sizeof(T)); |
| return *this; |
| } |
| |
| private: |
| Transformer* transformer_; |
| uint32_t offset_; |
| }; |
| |
| template <typename T> |
| DstTarget<T> dst(uint32_t offset) { |
| ZX_ASSERT(offset % 8 == 0); |
| return DstTarget<T>(this, offset); |
| } |
| |
| DstTarget<uint64_t> dst_u64(uint32_t offset) { return dst<uint64_t>(offset); } |
| |
| fidl_transformation_t transformation_; |
| |
| const uint8_t* src_bytes_; |
| uint8_t* dst_bytes_; |
| |
| uint32_t src_next_out_of_line_; |
| uint32_t dst_next_out_of_line_; |
| |
| uint32_t src_num_bytes_; |
| uint32_t dst_num_bytes_capacity_; |
| |
| const char* error; |
| }; |
| |
| } // namespace |
| |
| zx_status_t fidl_transform(fidl_transformation_t transformation, const fidl_type_t* 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 (transformation == FIDL_TRANSFORMATION_NONE) { |
| // Fast path - directly copy if no transformation needs to be performed. |
| if (dst_num_bytes_capacity < src_num_bytes) { |
| *out_error_msg = "destination capacity too small"; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| memcpy(dst_bytes, src_bytes, src_num_bytes); |
| *out_dst_num_bytes = src_num_bytes; |
| return ZX_OK; |
| } |
| |
| Transformer transformer(transformation, src_bytes, src_num_bytes, dst_bytes, |
| dst_num_bytes_capacity); |
| zx_status_t status = transformer.Transform(type, 0, 0); |
| *out_error_msg = transformer.err_msg(); |
| *out_dst_num_bytes = transformer.dst_num_bytes(); |
| return status; |
| } |