| // 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>(envelope_encoder.bytes_.size()); | |
| WriteValue<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 = 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); | |
| handles_.push_back(node->handle()); | |
| } | |
| } | |
| 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 |