| // 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 "src/lib/fidl_codec/encoder.h" |
| |
| #include <lib/fidl/txn_header.h> |
| |
| #include <algorithm> |
| |
| #include "src/lib/fidl_codec/logger.h" |
| #include "src/lib/fidl_codec/type_visitor.h" |
| #include "src/lib/fidl_codec/wire_types.h" |
| |
| namespace fidl_codec { |
| |
| class NullVisitor : public TypeVisitor { |
| public: |
| explicit NullVisitor(Encoder* encoder) : encoder_(encoder) {} |
| |
| private: |
| void VisitType(const Type* type) override { |
| FX_LOGS_OR_CAPTURE(FATAL) << "Type " << type->Name() << " can't be null."; |
| } |
| |
| void VisitStringType(const StringType* type) override { |
| FX_DCHECK(type->Nullable()); |
| encoder_->WriteValue<uint64_t>(0); |
| encoder_->WriteValue<uint64_t>(0); |
| } |
| |
| void VisitUnionType(const UnionType* type) override { |
| FX_DCHECK(type->Nullable()); |
| encoder_->WriteValue<uint64_t>(0); |
| encoder_->WriteValue<uint32_t>(0); |
| encoder_->WriteValue<uint32_t>(0); |
| encoder_->WriteValue<uint64_t>(0); |
| } |
| |
| void VisitStructType(const StructType* type) override { |
| FX_DCHECK(type->Nullable()); |
| encoder_->WriteValue<uint64_t>(0); |
| } |
| |
| void VisitVectorType(const VectorType* type) override { |
| FX_DCHECK(type->Nullable()); |
| encoder_->WriteValue<uint64_t>(0); |
| encoder_->WriteValue<uint64_t>(0); |
| } |
| |
| Encoder* encoder_; |
| }; |
| |
| Encoder::Result Encoder::EncodeMessage(uint32_t tx_id, uint64_t ordinal, const uint8_t flags[3], |
| uint8_t magic, const StructValue& object) { |
| Encoder encoder; |
| |
| size_t object_size = object.struct_definition().size(); |
| encoder.AllocateObject(object_size); |
| encoder.WriteValue(tx_id); |
| encoder.WriteValue(flags[0]); |
| encoder.WriteValue(flags[1]); |
| encoder.WriteValue(flags[2]); |
| encoder.WriteValue(magic); |
| encoder.WriteValue(ordinal); |
| FX_DCHECK(sizeof(fidl_message_header_t) == encoder.current_offset_); |
| |
| // The primary object offsets include the header size, so the offset of the object is zero. |
| encoder.VisitStructValueBody(0, &object); |
| |
| return Result{std::move(encoder.bytes_), std::move(encoder.handles_)}; |
| } |
| |
| size_t Encoder::AllocateObject(size_t size) { |
| size_t object_offset = bytes_.size(); |
| bytes_.resize((bytes_.size() + size + 7) & ~7); |
| return object_offset; |
| } |
| |
| void Encoder::WriteData(const uint8_t* data, size_t size) { |
| FX_DCHECK(current_offset_ + size <= bytes_.size()) |
| << "needs " << size << " bytes at offset " << current_offset_ << " buffer size is " |
| << bytes_.size(); |
| std::copy(data, data + size, bytes_.data() + current_offset_); |
| current_offset_ += size; |
| } |
| |
| void Encoder::EncodeEnvelope(const Value* value, const Type* for_type) { |
| Encoder envelope_encoder; |
| envelope_encoder.AllocateObject(for_type->InlineSize()); |
| value->Visit(&envelope_encoder, for_type); |
| WriteValue<uint32_t>(static_cast<uint32_t>(envelope_encoder.bytes_.size())); |
| WriteValue<uint32_t>(static_cast<uint32_t>(envelope_encoder.handles_.size())); |
| WriteValue<uint64_t>(UINTPTR_MAX); |
| current_offset_ = AllocateObject(envelope_encoder.bytes_.size()); |
| WriteData(envelope_encoder.bytes_); |
| for (const auto handle : envelope_encoder.handles_) { |
| handles_.push_back(handle); |
| } |
| } |
| |
| void Encoder::VisitStructValueBody(size_t offset, const StructValue* node) { |
| for (const auto& member : node->struct_definition().members()) { |
| auto it = node->fields().find(member.get()); |
| FX_DCHECK(it != node->fields().end()); |
| current_offset_ = offset + member->offset(); |
| it->second->Visit(this, member->type()); |
| } |
| } |
| |
| void Encoder::VisitInvalidValue(const InvalidValue* node, const Type* for_type) { |
| FX_LOGS_OR_CAPTURE(FATAL) << "Can't encode invalid data."; |
| } |
| |
| void Encoder::VisitNullValue(const NullValue* node, const Type* for_type) { |
| FX_DCHECK(for_type != nullptr); |
| NullVisitor null_visitor(this); |
| for_type->Visit(&null_visitor); |
| } |
| |
| void Encoder::VisitRawValue(const RawValue* node, const Type* for_type) { WriteData(node->data()); } |
| |
| void Encoder::VisitBoolValue(const BoolValue* node, const Type* for_type) { |
| WriteValue<uint8_t>(node->value()); |
| } |
| |
| void Encoder::VisitIntegerValue(const IntegerValue* node, const Type* for_type) { |
| FX_DCHECK(for_type != nullptr); |
| size_t size = for_type->InlineSize(); |
| uint64_t value = node->absolute_value(); |
| if (node->negative()) { |
| value = -value; |
| } |
| WriteData(reinterpret_cast<const uint8_t*>(&value), size); |
| } |
| |
| void Encoder::VisitDoubleValue(const DoubleValue* node, const Type* for_type) { |
| FX_DCHECK(for_type != nullptr); |
| size_t size = for_type->InlineSize(); |
| if (size == sizeof(float)) { |
| float value = static_cast<float>(node->value()); |
| WriteData(reinterpret_cast<const uint8_t*>(&value), size); |
| } else { |
| double value = node->value(); |
| WriteData(reinterpret_cast<const uint8_t*>(&value), size); |
| } |
| } |
| |
| void Encoder::VisitStringValue(const StringValue* node, const Type* for_type) { |
| WriteValue<uint64_t>(node->string().size()); |
| WriteValue<uint64_t>(UINTPTR_MAX); |
| current_offset_ = AllocateObject(node->string().size()); |
| WriteData(reinterpret_cast<const uint8_t*>(node->string().data()), node->string().size()); |
| } |
| |
| void Encoder::VisitHandleValue(const HandleValue* node, const Type* for_type) { |
| if (node->handle().handle == FIDL_HANDLE_ABSENT) { |
| WriteValue<uint32_t>(FIDL_HANDLE_ABSENT); |
| } else { |
| WriteValue<uint32_t>(FIDL_HANDLE_PRESENT); |
| zx_handle_info_t info = {.handle = node->handle().handle, |
| .type = node->handle().type, |
| .rights = node->handle().rights}; |
| handles_.push_back(info); |
| } |
| } |
| |
| void Encoder::VisitUnionValue(const UnionValue* node, const Type* for_type) { |
| FX_DCHECK(for_type != nullptr); |
| WriteValue<uint64_t>(node->member().ordinal()); |
| EncodeEnvelope(node->value().get(), node->member().type()); |
| } |
| |
| void Encoder::VisitStructValue(const StructValue* node, const Type* for_type) { |
| FX_DCHECK(for_type != nullptr); |
| if (for_type->Nullable()) { |
| WriteValue<uint64_t>(UINTPTR_MAX); |
| size_t object_size = node->struct_definition().size(); |
| VisitStructValueBody(AllocateObject(object_size), node); |
| } else { |
| VisitStructValueBody(current_offset_, node); |
| } |
| } |
| |
| void Encoder::VisitVectorValue(const VectorValue* node, const Type* for_type) { |
| FX_DCHECK(for_type != nullptr); |
| const Type* component_type = for_type->GetComponentType(); |
| FX_DCHECK(component_type != nullptr); |
| size_t component_size = component_type->InlineSize(); |
| size_t offset; |
| if (for_type->IsArray()) { |
| offset = current_offset_; |
| } else { |
| WriteValue<uint64_t>(node->values().size()); |
| WriteValue<uint64_t>(UINTPTR_MAX); |
| offset = AllocateObject(component_size * node->values().size()); |
| } |
| for (const auto& value : node->values()) { |
| current_offset_ = offset; |
| value->Visit(this, component_type); |
| offset += component_size; |
| } |
| } |
| |
| void Encoder::VisitTableValue(const TableValue* node, const Type* for_type) { |
| WriteValue<uint64_t>(node->highest_member()); |
| WriteValue<uint64_t>(UINTPTR_MAX); |
| |
| constexpr size_t kEnvelopeSize = 2 * sizeof(uint32_t) + sizeof(uint64_t); |
| size_t offset = AllocateObject(node->highest_member() * kEnvelopeSize); |
| |
| for (Ordinal32 i = 1; i <= node->highest_member(); ++i) { |
| current_offset_ = offset; |
| auto it = node->members().find(node->table_definition().members()[i].get()); |
| if ((it == node->members().end()) || it->second->IsNull()) { |
| WriteValue<uint32_t>(0); |
| WriteValue<uint32_t>(0); |
| WriteValue<uint64_t>(0); |
| } else { |
| EncodeEnvelope(it->second.get(), it->first->type()); |
| } |
| offset += kEnvelopeSize; |
| } |
| } |
| |
| } // namespace fidl_codec |