| // 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 "wire_object.h" |
| |
| #include <iomanip> |
| #include <iostream> |
| #include <memory> |
| #include <vector> |
| |
| #include "src/lib/fidl_codec/colors.h" |
| #include "src/lib/fidl_codec/display_handle.h" |
| #include "src/lib/fidl_codec/json_visitor.h" |
| #include "src/lib/fidl_codec/library_loader.h" |
| #include "src/lib/fidl_codec/visitor.h" |
| #include "src/lib/fidl_codec/wire_types.h" |
| #include "src/lib/fxl/logging.h" |
| |
| namespace fidl_codec { |
| |
| constexpr char kInvalid[] = "invalid"; |
| constexpr char kNull[] = "null"; |
| |
| const Colors WithoutColors("", "", "", "", "", ""); |
| const Colors WithColors(/*new_reset=*/"\u001b[0m", /*new_red=*/"\u001b[31m", |
| /*new_green=*/"\u001b[32m", /*new_blue=*/"\u001b[34m", |
| /*new_white_on_magenta=*/"\u001b[45m\u001b[37m", |
| /*new_yellow_background=*/"\u001b[103m"); |
| |
| void Value::Visit(Visitor* visitor) const { visitor->VisitValue(this); } |
| |
| bool NullableValue::DecodeNullable(MessageDecoder* decoder, uint64_t offset, uint64_t size) { |
| uintptr_t data; |
| if (!decoder->GetValueAt(offset, &data)) { |
| return false; |
| } |
| |
| if (data == FIDL_ALLOC_ABSENT) { |
| is_null_ = true; |
| return true; |
| } |
| if (data != FIDL_ALLOC_PRESENT) { |
| if (type() == nullptr) { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + offset) << std::dec |
| << ": Invalid value <" << std::hex << data << std::dec |
| << "> for nullable\n"; |
| } else { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + offset) << std::dec |
| << ": Invalid value <" << std::hex << data << std::dec |
| << "> for nullable " << type()->Name() << "\n"; |
| } |
| return false; |
| } |
| uint64_t nullable_offset = decoder->next_object_offset(); |
| // Set the offset for the next object (just after this one). |
| decoder->SkipObject(size); |
| // Decode the object. |
| DecodeContent(decoder, nullable_offset); |
| return true; |
| } |
| |
| void NullableValue::Visit(Visitor* visitor) const { visitor->VisitNullableValue(this); } |
| |
| void InlineValue::DecodeContent(MessageDecoder* /*decoder*/, uint64_t /*offset*/) { |
| FXL_LOG(FATAL) << "Value is defined inline"; |
| } |
| |
| void InlineValue::Visit(Visitor* visitor) const { visitor->VisitInlineValue(this); } |
| |
| int RawValue::DisplaySize(int /*remaining_size*/) const { |
| return data_ ? static_cast<int>(data_->size()) * 3 - 1 : 0; |
| } |
| |
| void RawValue::PrettyPrint(std::ostream& os, const Colors& /*colors*/, |
| const fidl_message_header_t* /*header*/, |
| std::string_view /*line_header*/, int /*tabs*/, int /*remaining_size*/, |
| int /*max_line_size*/) const { |
| if (!data_ || data_->size() == 0) { |
| return; |
| } |
| size_t buffer_size = data_->size() * 3; |
| std::vector<char> buffer(buffer_size); |
| for (size_t i = 0; i < data_->size(); ++i) { |
| if (i != 0) { |
| buffer[i * 3 - 1] = ' '; |
| } |
| snprintf(buffer.data() + (i * 3), 4, "%02x", (*data_)[i]); |
| } |
| os << buffer.data(); |
| } |
| |
| void RawValue::Visit(Visitor* visitor) const { visitor->VisitRawValue(this); } |
| |
| template <> |
| void NumericValue<uint8_t>::Visit(Visitor* visitor) const { |
| visitor->VisitU8Value(this); |
| } |
| template <> |
| void NumericValue<uint16_t>::Visit(Visitor* visitor) const { |
| visitor->VisitU16Value(this); |
| } |
| template <> |
| void NumericValue<uint32_t>::Visit(Visitor* visitor) const { |
| visitor->VisitU32Value(this); |
| } |
| template <> |
| void NumericValue<uint64_t>::Visit(Visitor* visitor) const { |
| visitor->VisitU64Value(this); |
| } |
| template <> |
| void NumericValue<int8_t>::Visit(Visitor* visitor) const { |
| visitor->VisitI8Value(this); |
| } |
| template <> |
| void NumericValue<int16_t>::Visit(Visitor* visitor) const { |
| visitor->VisitI16Value(this); |
| } |
| template <> |
| void NumericValue<int32_t>::Visit(Visitor* visitor) const { |
| visitor->VisitI32Value(this); |
| } |
| template <> |
| void NumericValue<int64_t>::Visit(Visitor* visitor) const { |
| visitor->VisitI64Value(this); |
| } |
| template <> |
| void NumericValue<float>::Visit(Visitor* visitor) const { |
| visitor->VisitF32Value(this); |
| } |
| template <> |
| void NumericValue<double>::Visit(Visitor* visitor) const { |
| visitor->VisitF64Value(this); |
| } |
| |
| int StringValue::DisplaySize(int /*remaining_size*/) const { |
| if (is_null()) { |
| return strlen(kNull); |
| } |
| if (!string_) { |
| return strlen(kInvalid); |
| } |
| return static_cast<int>(string_->size()) + 2; // The two quotes. |
| } |
| |
| void StringValue::DecodeContent(MessageDecoder* decoder, uint64_t offset) { |
| auto data = reinterpret_cast<const char*>(decoder->GetAddress(offset, string_length_)); |
| string_ = data ? std::optional(std::string(data, data + string_length_)) : std::nullopt; |
| } |
| |
| void StringValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* /*header*/, |
| std::string_view /*line_header*/, int /*tabs*/, |
| int /*remaining_size*/, int /*max_line_size*/) const { |
| os << colors.red; |
| if (is_null()) { |
| os << kNull; |
| } else if (!string_) { |
| os << kInvalid; |
| } else { |
| os << '"' << *string_ << '"'; |
| } |
| os << colors.reset; |
| } |
| |
| void StringValue::Visit(Visitor* visitor) const { visitor->VisitStringValue(this); } |
| |
| int BoolValue::DisplaySize(int /*remaining_size*/) const { |
| constexpr int kTrueSize = 4; |
| constexpr int kFalseSize = 5; |
| constexpr int kInvalidSize = 7; |
| return value_ ? kInvalidSize : (*value_ ? kTrueSize : kFalseSize); |
| } |
| |
| void BoolValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* /*header*/, |
| std::string_view /*line_header*/, int /*tabs*/, int /*remaining_size*/, |
| int /*max_line_size*/) const { |
| if (!value_) { |
| os << colors.red << "invalid" << colors.reset; |
| } else { |
| os << colors.blue << (*value_ ? "true" : "false") << colors.reset; |
| } |
| } |
| |
| void BoolValue::Visit(Visitor* visitor) const { visitor->VisitBoolValue(this); } |
| |
| int Object::DisplaySize(int remaining_size) const { |
| if (is_null()) { |
| return 4; |
| } |
| int size = 0; |
| for (const auto& [name, value] : fields_) { |
| // Two characters for the separator ("{ " or ", ") and three characters for |
| // equal (" = "). |
| constexpr int kExtraSize = 5; |
| size += static_cast<int>(name.size()) + kExtraSize; |
| if (value->type() != nullptr) { |
| // Two characters for ": ". |
| size += static_cast<int>(value->type()->Name().size()) + 2; |
| } |
| size += value->DisplaySize(remaining_size - size); |
| if (size > remaining_size) { |
| return size; |
| } |
| } |
| // Two characters for the closing brace (" }"). |
| size += 2; |
| return size; |
| } |
| |
| void Object::DecodeContent(MessageDecoder* decoder, uint64_t offset) { DecodeAt(decoder, offset); } |
| |
| void Object::DecodeAt(MessageDecoder* decoder, uint64_t base_offset) { |
| for (const auto& member : struct_definition_.members()) { |
| std::unique_ptr<Value> value = |
| member->type()->Decode(decoder, base_offset + member->Offset(decoder)); |
| if (value != nullptr) { |
| fields_[std::string(member->name())] = std::move(value); |
| } |
| } |
| } |
| |
| void Object::ExtractJson(rapidjson::Document::AllocatorType& allocator, |
| rapidjson::Value& result) const { |
| JsonVisitor visitor(&result, &allocator); |
| |
| Visit(&visitor); |
| } |
| |
| void Object::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* header, std::string_view line_header, |
| int tabs, int remaining_size, int max_line_size) const { |
| if (is_null()) { |
| os << colors.blue << "null" << colors.reset; |
| } else if (fields_.empty()) { |
| os << "{}"; |
| } else if (DisplaySize(remaining_size) + static_cast<int>(line_header.size()) <= remaining_size) { |
| const char* separator = "{ "; |
| for (const auto& member : struct_definition_.members()) { |
| auto it = fields_.find(std::string(member->name())); |
| if (it == fields_.end()) |
| continue; |
| const auto& [name, value] = *it; |
| os << separator << name; |
| separator = ", "; |
| if (value->type() != nullptr) { |
| std::string type_name = value->type()->Name(); |
| os << ": " << colors.green << type_name << colors.reset; |
| } |
| os << " = "; |
| value->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size, max_line_size); |
| } |
| os << " }"; |
| } else { |
| os << "{\n"; |
| for (const auto& member : struct_definition_.members()) { |
| auto it = fields_.find(std::string(member->name())); |
| if (it == fields_.end()) |
| continue; |
| const auto& [name, value] = *it; |
| int size = (tabs + 1) * kTabSize + static_cast<int>(name.size()); |
| os << line_header << std::string((tabs + 1) * kTabSize, ' ') << name; |
| if (value->type() != nullptr) { |
| std::string type_name = value->type()->Name(); |
| // Two characters for ": ". |
| size += static_cast<int>(type_name.size()) + 2; |
| os << ": " << colors.green << type_name << colors.reset; |
| } |
| size += 3; |
| os << " = "; |
| value->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size - size, |
| max_line_size); |
| os << "\n"; |
| } |
| os << line_header << std::string(tabs * kTabSize, ' ') << '}'; |
| } |
| } |
| |
| void Object::Visit(Visitor* visitor) const { visitor->VisitObject(this); } |
| |
| EnvelopeValue::EnvelopeValue(const Type* type) : NullableValue(type) {} |
| |
| int EnvelopeValue::DisplaySize(int remaining_size) const { |
| if (is_null() || (value_ == nullptr)) { |
| return 4; |
| } |
| return value_->DisplaySize(remaining_size); |
| } |
| |
| void EnvelopeValue::DecodeContent(MessageDecoder* decoder, uint64_t offset) { |
| if (offset + num_bytes_ > decoder->num_bytes()) { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + offset) << std::dec |
| << ": Not enough data to decode an envelope\n"; |
| return; |
| } |
| if (num_handles_ > decoder->GetRemainingHandles()) { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + offset) << std::dec |
| << ": Not enough handles to decode an envelope\n"; |
| return; |
| } |
| MessageDecoder envelope_decoder(decoder, offset, num_bytes_, num_handles_); |
| value_ = envelope_decoder.DecodeValue(type()); |
| } |
| |
| void EnvelopeValue::DecodeAt(MessageDecoder* decoder, uint64_t base_offset) { |
| decoder->GetValueAt(base_offset, &num_bytes_); |
| base_offset += sizeof(num_bytes_); |
| decoder->GetValueAt(base_offset, &num_handles_); |
| base_offset += sizeof(num_handles_); |
| |
| if (DecodeNullable(decoder, base_offset, num_bytes_)) { |
| if (type() == nullptr) { |
| if (!is_null()) { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + base_offset) << std::dec |
| << ": The envelope should be null\n"; |
| } |
| } |
| if (is_null()) { |
| if (num_bytes_ != 0) { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + base_offset) << std::dec |
| << ": Null envelope shouldn't have bytes\n"; |
| } |
| if (num_handles_ != 0) { |
| decoder->AddError() << std::hex << (decoder->absolute_offset() + base_offset) << std::dec |
| << ": Null envelope shouldn't have handles\n"; |
| } |
| } |
| } |
| } |
| |
| void EnvelopeValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* header, std::string_view line_header, |
| int tabs, int remaining_size, int max_line_size) const { |
| if (is_null() || (value_ == nullptr)) { |
| os << colors.red << "null" << colors.reset; |
| } else { |
| value_->PrettyPrint(os, colors, header, line_header, tabs, remaining_size, max_line_size); |
| } |
| } |
| |
| void EnvelopeValue::Visit(Visitor* visitor) const { visitor->VisitEnvelopeValue(this); } |
| |
| TableValue::TableValue(const Type* type, const Table& table_definition, uint64_t envelope_count) |
| : NullableValue(type), table_definition_(table_definition), envelope_count_(envelope_count) {} |
| |
| int TableValue::DisplaySize(int remaining_size) const { |
| int size = 0; |
| for (const auto& field : envelopes_) { |
| if ((field.value() != nullptr) && !field.value()->is_null()) { |
| // Two characters for the separator ("{ " or ", ") and three characters |
| // for equal (" = "). |
| constexpr int kExtraSize = 5; |
| size += static_cast<int>(field.name().size()) + kExtraSize; |
| if (field.value()->type() != nullptr) { |
| size += static_cast<int>(field.value()->type()->Name().size()) + 2; |
| } |
| size += field.value()->DisplaySize(remaining_size - size); |
| if (size > remaining_size) { |
| return size; |
| } |
| } |
| } |
| // Two characters for the closing brace (" }"). |
| size += 2; |
| return size; |
| } |
| |
| void TableValue::DecodeContent(MessageDecoder* decoder, uint64_t offset) { |
| for (uint64_t envelope_id = 0; envelope_id < envelope_count_; ++envelope_id) { |
| const TableMember* member = (envelope_id < table_definition_.members().size() - 1) |
| ? table_definition_.members()[envelope_id + 1] |
| : nullptr; |
| std::unique_ptr<EnvelopeValue> envelope; |
| std::string key_name; |
| if (member == nullptr) { |
| key_name = std::string("unknown$") + std::to_string(envelope_id + 1); |
| envelope = std::make_unique<EnvelopeValue>(table_definition_.unknown_member_type()); |
| } else { |
| key_name = member->name(); |
| envelope = std::make_unique<EnvelopeValue>(member->type()); |
| } |
| envelope->DecodeAt(decoder, offset); |
| envelopes_.emplace_back(key_name, std::move(envelope)); |
| offset += 2 * sizeof(uint64_t); |
| } |
| } |
| |
| void TableValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* header, std::string_view line_header, |
| int tabs, int remaining_size, int max_line_size) const { |
| int display_size = DisplaySize(remaining_size); |
| if (display_size == 2) { |
| os << "{}"; |
| } else if (DisplaySize(remaining_size) + static_cast<int>(line_header.size()) <= remaining_size) { |
| const char* separator = "{ "; |
| for (const auto& field : envelopes_) { |
| if ((field.value() != nullptr) && !field.value()->is_null()) { |
| os << separator << field.name(); |
| separator = ", "; |
| if (field.value()->type() != nullptr) { |
| std::string type_name = field.value()->type()->Name(); |
| os << ": " << colors.green << type_name << colors.reset; |
| } |
| os << " = "; |
| field.value()->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size, |
| max_line_size); |
| } |
| } |
| os << " }"; |
| } else { |
| os << "{\n"; |
| for (const auto& field : envelopes_) { |
| if ((field.value() != nullptr) && !field.value()->is_null()) { |
| int size = (tabs + 1) * kTabSize + static_cast<int>(field.name().size()) + 3; |
| os << line_header << std::string((tabs + 1) * kTabSize, ' ') << field.name(); |
| if (field.value()->type() != nullptr) { |
| std::string type_name = field.value()->type()->Name(); |
| size += static_cast<int>(type_name.size()) + 2; |
| os << ": " << colors.green << type_name << colors.reset; |
| } |
| os << " = "; |
| field.value()->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size - size, |
| max_line_size); |
| os << "\n"; |
| } |
| } |
| os << line_header << std::string(tabs * kTabSize, ' ') << '}'; |
| } |
| } |
| |
| void TableValue::Visit(Visitor* visitor) const { visitor->VisitTableValue(this); } |
| |
| int UnionValue::DisplaySize(int remaining_size) const { |
| if (is_null() || field_.value()->is_null()) { |
| return 4; |
| } |
| // Two characters for the opening brace ("{ ") + three characters for equal |
| // (" = ") and two characters for the closing brace (" }"). |
| constexpr int kExtraSize = 7; |
| int size = static_cast<int>(field_.name().size()) + kExtraSize; |
| if (field_.value() != nullptr) { |
| if (field_.value()->type() != nullptr) { |
| // Two characters for ": ". |
| size += static_cast<int>(field_.value()->type()->Name().size()) + 2; |
| } |
| size += field_.value()->DisplaySize(remaining_size - size); |
| } |
| return size; |
| } |
| |
| void UnionValue::DecodeContent(MessageDecoder* decoder, uint64_t offset) { |
| DecodeAt(decoder, offset); |
| } |
| |
| void UnionValue::DecodeAt(MessageDecoder* decoder, uint64_t base_offset) { |
| uint32_t tag = 0; |
| decoder->GetValueAt(base_offset, &tag); |
| const UnionMember* member = union_definition_.MemberWithTag(tag); |
| if (member == nullptr) { |
| field_ = |
| Field("unknown$" + std::to_string(tag), std::make_unique<RawValue>(nullptr, std::nullopt)); |
| } else { |
| field_ = Field(std::string(member->name()), |
| member->type()->Decode(decoder, base_offset + member->offset())); |
| } |
| } |
| |
| void UnionValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* header, std::string_view line_header, |
| int tabs, int remaining_size, int max_line_size) const { |
| if (header != nullptr) { |
| os << (fidl_should_decode_union_from_xunion(header) ? "v1!" : "v0!"); |
| } |
| if (is_null() || field_.value()->is_null()) { |
| os << colors.blue << "null" << colors.reset; |
| } else if (DisplaySize(remaining_size) + static_cast<int>(line_header.size()) <= remaining_size) { |
| // Two characters for the opening brace ("{ ") + three characters for equal |
| // (" = ") and two characters for the closing brace (" }"). |
| constexpr int kExtraSize = 7; |
| int size = static_cast<int>(field_.name().size()) + kExtraSize; |
| os << "{ " << field_.name(); |
| if (field_.value() != nullptr) { |
| if (field_.value()->type() != nullptr) { |
| std::string type_name = field_.value()->type()->Name(); |
| // Two characters for ": ". |
| size += static_cast<int>(type_name.size()) + 2; |
| os << ": " << colors.green << type_name << colors.reset; |
| } |
| os << " = "; |
| field_.value()->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size - size, |
| max_line_size); |
| } |
| os << " }"; |
| } else { |
| os << "{\n"; |
| // Three characters for " = ". |
| int size = (tabs + 1) * kTabSize + static_cast<int>(field_.name().size()) + 3; |
| os << line_header << std::string((tabs + 1) * kTabSize, ' ') << field_.name(); |
| if (field_.value() != nullptr) { |
| if (field_.value()->type() != nullptr) { |
| std::string type_name = field_.value()->type()->Name(); |
| // Two characters for ": ". |
| size += static_cast<int>(type_name.size()) + 2; |
| os << ": " << colors.green << type_name << colors.reset; |
| } |
| os << " = "; |
| field_.value()->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size - size, |
| max_line_size); |
| } |
| os << '\n'; |
| os << line_header << std::string(tabs * kTabSize, ' ') << "}"; |
| } |
| } |
| |
| void UnionValue::Visit(Visitor* visitor) const { visitor->VisitUnionValue(this); } |
| |
| void XUnionValue::Visit(Visitor* visitor) const { visitor->VisitXUnionValue(this); } |
| |
| int ArrayValue::DisplaySize(int remaining_size) const { |
| int size = 2; |
| for (const auto& value : values_) { |
| // Two characters for ", ". |
| size += value->DisplaySize(remaining_size - size) + 2; |
| if (size > remaining_size) { |
| return size; |
| } |
| } |
| return size; |
| } |
| |
| void ArrayValue::DecodeContent(MessageDecoder* /*decoder*/, uint64_t /*offset*/) { |
| FXL_LOG(FATAL) << "Value is defined inline"; |
| } |
| |
| void ArrayValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* header, std::string_view line_header, |
| int tabs, int remaining_size, int max_line_size) const { |
| if (values_.empty()) { |
| os << "[]"; |
| } else if (DisplaySize(remaining_size) + static_cast<int>(line_header.size()) <= remaining_size) { |
| const char* separator = "[ "; |
| for (const auto& value : values_) { |
| os << separator; |
| separator = ", "; |
| value->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size, max_line_size); |
| } |
| os << " ]"; |
| } else { |
| os << "[\n"; |
| for (const auto& value : values_) { |
| int size = (tabs + 1) * kTabSize; |
| os << line_header << std::string((tabs + 1) * kTabSize, ' '); |
| value->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size - size, |
| max_line_size); |
| os << "\n"; |
| } |
| os << line_header << std::string(tabs * kTabSize, ' ') << ']'; |
| } |
| } |
| |
| void ArrayValue::Visit(Visitor* visitor) const { visitor->VisitArrayValue(this); } |
| |
| int VectorValue::DisplaySize(int remaining_size) const { |
| if (is_null()) { |
| return 4; |
| } |
| if (is_string_) { |
| return static_cast<int>(size_ + 2); // The string and the two quotes. |
| } |
| int size = 0; |
| for (const auto& value : values_) { |
| // Two characters for the separator ("[ " or ", "). |
| size += value->DisplaySize(remaining_size - size) + 2; |
| if (size > remaining_size) { |
| return size; |
| } |
| } |
| // Two characters for the closing bracket (" ]"). |
| size += 2; |
| return size; |
| } |
| |
| void VectorValue::DecodeContent(MessageDecoder* decoder, uint64_t offset) { |
| if (size_ == 0) { |
| return; |
| } |
| is_string_ = true; |
| for (uint64_t i = 0; |
| (i < size_) && (offset + component_type_->InlineSize(decoder) <= decoder->num_bytes()); |
| ++i) { |
| std::unique_ptr<Value> value = component_type_->Decode(decoder, offset); |
| if (value != nullptr) { |
| uint8_t uvalue = value->GetUint8Value(); |
| if (!std::isprint(uvalue)) { |
| if ((uvalue == '\r') || (uvalue == '\n')) { |
| has_new_line_ = true; |
| } else { |
| is_string_ = false; |
| } |
| } |
| values_.push_back(std::move(value)); |
| } |
| offset += component_type_->InlineSize(decoder); |
| } |
| } |
| |
| void VectorValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* header, std::string_view line_header, |
| int tabs, int remaining_size, int max_line_size) const { |
| if (is_null()) { |
| os << colors.blue << "null" << colors.reset; |
| } else if (values_.empty()) { |
| os << "[]"; |
| } else if (is_string_) { |
| if (has_new_line_) { |
| os << "[\n"; |
| bool needs_header = true; |
| for (const auto& value : values_) { |
| if (needs_header) { |
| os << line_header << std::string((tabs + 1) * kTabSize, ' '); |
| needs_header = false; |
| } |
| uint8_t uvalue = value->GetUint8Value(); |
| os << uvalue; |
| if (uvalue == '\n') { |
| needs_header = true; |
| } |
| } |
| if (!needs_header) { |
| os << '\n'; |
| } |
| os << line_header << std::string(tabs * kTabSize, ' ') << ']'; |
| } else { |
| os << '"'; |
| for (const auto& value : values_) { |
| os << value->GetUint8Value(); |
| } |
| os << '"'; |
| } |
| } else if (DisplaySize(remaining_size) + static_cast<int>(line_header.size()) <= remaining_size) { |
| const char* separator = "[ "; |
| for (const auto& value : values_) { |
| os << separator; |
| separator = ", "; |
| value->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size, max_line_size); |
| } |
| os << " ]"; |
| } else { |
| os << "[\n"; |
| int size = 0; |
| for (const auto& value : values_) { |
| int value_size = value->DisplaySize(max_line_size - size); |
| if (size == 0) { |
| os << line_header << std::string((tabs + 1) * kTabSize, ' '); |
| size = (tabs + 1) * kTabSize; |
| } else if (value_size + 3 > max_line_size - size) { |
| os << ",\n"; |
| os << line_header << std::string((tabs + 1) * kTabSize, ' '); |
| size = (tabs + 1) * kTabSize; |
| } else { |
| os << ", "; |
| size += 2; |
| } |
| value->PrettyPrint(os, colors, header, line_header, tabs + 1, max_line_size - size, |
| max_line_size); |
| size += value_size; |
| } |
| os << '\n'; |
| os << line_header << std::string(tabs * kTabSize, ' ') << ']'; |
| } |
| } |
| |
| void VectorValue::Visit(Visitor* visitor) const { visitor->VisitVectorValue(this); } |
| |
| int EnumValue::DisplaySize(int /*remaining_size*/) const { |
| if (!data_) { |
| return strlen(kInvalid); |
| } |
| return enum_definition_.GetNameFromBytes(data_->data()).size(); |
| } |
| |
| void EnumValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* /*header*/, |
| std::string_view /*line_header*/, int /*tabs*/, int /*remaining_size*/, |
| int /*max_line_size*/) const { |
| if (!data_) { |
| os << colors.red << kInvalid << colors.reset; |
| } else { |
| os << colors.blue << enum_definition_.GetNameFromBytes(data_->data()) << colors.reset; |
| } |
| } |
| |
| void EnumValue::Visit(Visitor* visitor) const { visitor->VisitEnumValue(this); } |
| |
| int BitsValue::DisplaySize(int /*remaining_size*/) const { |
| if (!data_) { |
| return strlen(kInvalid); |
| } |
| return bits_definition_.GetNameFromBytes(data_->data()).size(); |
| } |
| |
| void BitsValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* /*header*/, |
| std::string_view /*line_header*/, int /*tabs*/, int /*remaining_size*/, |
| int /*max_line_size*/) const { |
| if (!data_) { |
| os << colors.red << kInvalid << colors.reset; |
| } else { |
| os << colors.blue << bits_definition_.GetNameFromBytes(data_->data()) << colors.reset; |
| } |
| } |
| |
| void BitsValue::Visit(Visitor* visitor) const { visitor->VisitBitsValue(this); } |
| |
| int HandleValue::DisplaySize(int /*remaining_size*/) const { |
| return std::to_string(handle_.handle).size(); |
| } |
| |
| void HandleValue::DecodeContent(MessageDecoder* /*decoder*/, uint64_t /*offset*/) { |
| FXL_LOG(FATAL) << "Handle value is defined inline"; |
| } |
| |
| void HandleValue::PrettyPrint(std::ostream& os, const Colors& colors, |
| const fidl_message_header_t* /*header*/, |
| std::string_view /*line_header*/, int /*tabs*/, |
| int /*remaining_size*/, int /*max_line_size*/) const { |
| DisplayHandle(colors, handle_, os); |
| } |
| |
| void HandleValue::Visit(Visitor* visitor) const { visitor->VisitHandleValue(this); } |
| |
| } // namespace fidl_codec |