blob: 532e18be337ae09d1ec33aabaedf325afd6fa4a1 [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 "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