| // Copyright 2020 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/printer.h" | |
| #include <lib/syslog/cpp/macros.h> | |
| #include "src/lib/fidl_codec/display_handle.h" | |
| #include "src/lib/fidl_codec/status.h" | |
| namespace fidl_codec { | |
| constexpr int kCharactersPerByte = 2; | |
| 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"); | |
| PrettyPrinter::PrettyPrinter(std::ostream& os, const Colors& colors, bool pretty_print, | |
| std::string_view line_header, int max_line_size, | |
| bool header_on_every_line, int tabulations) | |
| : os_(os), | |
| colors_(colors), | |
| pretty_print_(pretty_print), | |
| line_header_(line_header), | |
| max_line_size_(max_line_size), | |
| header_on_every_line_(header_on_every_line), | |
| tabulations_(tabulations), | |
| remaining_size_(max_line_size - tabulations * kTabSize) { | |
| // Computes the displayed size of the header. The header can contain escape sequences (to add some | |
| // colors) which don't count as displayed characters. Here we count the number of characters in | |
| // the line header skiping everything between escape ('\u001b') and 'm'. | |
| size_t i = 0; | |
| while (i < line_header.size()) { | |
| if (line_header[i] == '\u001b') { | |
| i = line_header.find_first_of('m', i + 1); | |
| if (i == std::string_view::npos) { | |
| break; | |
| } | |
| ++i; | |
| } else { | |
| ++i; | |
| ++line_header_size_; | |
| } | |
| } | |
| } | |
| void PrettyPrinter::DisplayHandle(const zx_handle_info_t& handle) { | |
| fidl_codec::DisplayHandle(handle, *this); | |
| } | |
| #define BtiPermNameCase(name) \ | |
| if ((perm & (name)) == (name)) { \ | |
| *this << separator << #name; \ | |
| separator = " | "; \ | |
| } | |
| void PrettyPrinter::DisplayBtiPerm(uint32_t perm) { | |
| if (perm == 0) { | |
| *this << Red << "0" << ResetColor; | |
| return; | |
| } | |
| *this << Blue; | |
| const char* separator = ""; | |
| BtiPermNameCase(ZX_BTI_PERM_READ); | |
| BtiPermNameCase(ZX_BTI_PERM_WRITE); | |
| BtiPermNameCase(ZX_BTI_PERM_EXECUTE); | |
| BtiPermNameCase(ZX_BTI_COMPRESS); | |
| BtiPermNameCase(ZX_BTI_CONTIGUOUS); | |
| *this << ResetColor; | |
| } | |
| #define CachePolicyNameCase(name) \ | |
| case name: \ | |
| *this << Blue << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayCachePolicy(uint32_t cache_policy) { | |
| switch (cache_policy) { | |
| CachePolicyNameCase(ZX_CACHE_POLICY_CACHED); | |
| CachePolicyNameCase(ZX_CACHE_POLICY_UNCACHED); | |
| CachePolicyNameCase(ZX_CACHE_POLICY_UNCACHED_DEVICE); | |
| CachePolicyNameCase(ZX_CACHE_POLICY_WRITE_COMBINING); | |
| default: | |
| *this << Red << cache_policy << ResetColor; | |
| return; | |
| } | |
| } | |
| #define ClockNameCase(name) \ | |
| case name: \ | |
| *this << Blue << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayClock(zx_clock_t clock) { | |
| switch (clock) { | |
| ClockNameCase(ZX_CLOCK_MONOTONIC); | |
| ClockNameCase(ZX_CLOCK_UTC); | |
| ClockNameCase(ZX_CLOCK_THREAD); | |
| default: | |
| *this << Red << clock << ResetColor; | |
| return; | |
| } | |
| } | |
| void PrettyPrinter::DisplayDuration(zx_duration_t duration_ns) { | |
| if (duration_ns == ZX_TIME_INFINITE) { | |
| *this << Blue << "ZX_TIME_INFINITE" << ResetColor; | |
| return; | |
| } | |
| if (duration_ns == ZX_TIME_INFINITE_PAST) { | |
| *this << Blue << "ZX_TIME_INFINITE_PAST" << ResetColor; | |
| return; | |
| } | |
| *this << Blue; | |
| if (duration_ns < 0) { | |
| *this << '-'; | |
| duration_ns = -duration_ns; | |
| } | |
| const char* separator = ""; | |
| int64_t nanoseconds = duration_ns % kOneBillion; | |
| int64_t seconds = duration_ns / kOneBillion; | |
| if (seconds != 0) { | |
| int64_t minutes = seconds / kSecondsPerMinute; | |
| if (minutes != 0) { | |
| int64_t hours = minutes / kMinutesPerHour; | |
| if (hours != 0) { | |
| int64_t days = hours / kHoursPerDay; | |
| if (days != 0) { | |
| *this << days << " days"; | |
| separator = ", "; | |
| } | |
| *this << separator << (hours % kHoursPerDay) << " hours"; | |
| separator = ", "; | |
| } | |
| *this << separator << (minutes % kMinutesPerHour) << " minutes"; | |
| separator = ", "; | |
| } | |
| *this << separator << (seconds % kSecondsPerMinute) << " seconds"; | |
| if (nanoseconds != 0) { | |
| *this << " and " << nanoseconds << " nano seconds"; | |
| } | |
| } else if (nanoseconds != 0) { | |
| *this << nanoseconds << " nano seconds"; | |
| } else { | |
| *this << "0 seconds"; | |
| } | |
| *this << ResetColor; | |
| } | |
| #define ExceptionStateNameCase(name) \ | |
| case name: \ | |
| *this << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayExceptionState(uint32_t state) { | |
| *this << Blue; | |
| switch (state) { | |
| ExceptionStateNameCase(ZX_EXCEPTION_STATE_TRY_NEXT); | |
| ExceptionStateNameCase(ZX_EXCEPTION_STATE_HANDLED); | |
| default: | |
| *this << static_cast<uint32_t>(state) << ResetColor; | |
| return; | |
| } | |
| } | |
| void PrettyPrinter::DisplayHexa8(uint8_t value) { | |
| std::vector<char> buffer(sizeof(uint8_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%02" PRIx8, value); | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| void PrettyPrinter::DisplayHexa16(uint16_t value) { | |
| std::vector<char> buffer(sizeof(uint16_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%04" PRIx16, value); | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| void PrettyPrinter::DisplayHexa32(uint32_t value) { | |
| std::vector<char> buffer(sizeof(uint32_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%08" PRIx32, value); | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| void PrettyPrinter::DisplayHexa64(uint64_t value) { | |
| std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIx64, value); | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| #define TopicNameCase(name) \ | |
| case name: \ | |
| *this << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayObjectInfoTopic(uint32_t topic) { | |
| *this << Blue; | |
| switch (topic) { | |
| TopicNameCase(ZX_INFO_NONE); | |
| TopicNameCase(ZX_INFO_HANDLE_VALID); | |
| TopicNameCase(ZX_INFO_HANDLE_BASIC); | |
| TopicNameCase(ZX_INFO_PROCESS); | |
| TopicNameCase(ZX_INFO_PROCESS_THREADS); | |
| TopicNameCase(ZX_INFO_VMAR); | |
| TopicNameCase(ZX_INFO_JOB_CHILDREN); | |
| TopicNameCase(ZX_INFO_JOB_PROCESSES); | |
| TopicNameCase(ZX_INFO_THREAD); | |
| TopicNameCase(ZX_INFO_THREAD_EXCEPTION_REPORT); | |
| TopicNameCase(ZX_INFO_TASK_STATS); | |
| TopicNameCase(ZX_INFO_PROCESS_MAPS); | |
| TopicNameCase(ZX_INFO_PROCESS_VMOS); | |
| TopicNameCase(ZX_INFO_THREAD_STATS); | |
| TopicNameCase(ZX_INFO_CPU_STATS); | |
| TopicNameCase(ZX_INFO_KMEM_STATS); | |
| TopicNameCase(ZX_INFO_RESOURCE); | |
| TopicNameCase(ZX_INFO_HANDLE_COUNT); | |
| TopicNameCase(ZX_INFO_BTI); | |
| TopicNameCase(ZX_INFO_PROCESS_HANDLE_STATS); | |
| TopicNameCase(ZX_INFO_SOCKET); | |
| TopicNameCase(ZX_INFO_VMO); | |
| TopicNameCase(ZX_INFO_JOB); | |
| default: | |
| *this << "topic=" << topic << ResetColor; | |
| return; | |
| } | |
| } | |
| void PrettyPrinter::DisplayPaddr(zx_paddr_t addr) { | |
| std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1); | |
| #ifdef __MACH__ | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, addr); | |
| #else | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIx64, addr); | |
| #endif | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| #define PacketGuestVcpuTypeNameCase(name) \ | |
| case name: \ | |
| *this << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayPacketGuestVcpuType(uint8_t type) { | |
| *this << Blue; | |
| switch (type) { | |
| PacketGuestVcpuTypeNameCase(ZX_PKT_GUEST_VCPU_INTERRUPT); | |
| PacketGuestVcpuTypeNameCase(ZX_PKT_GUEST_VCPU_STARTUP); | |
| default: | |
| *this << static_cast<uint32_t>(type) << ResetColor; | |
| return; | |
| } | |
| } | |
| #define PacketPageRequestCommandNameCase(name) \ | |
| case name: \ | |
| *this << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayPacketPageRequestCommand(uint16_t command) { | |
| *this << Blue; | |
| switch (command) { | |
| PacketPageRequestCommandNameCase(ZX_PAGER_VMO_READ); | |
| PacketPageRequestCommandNameCase(ZX_PAGER_VMO_COMPLETE); | |
| default: | |
| *this << static_cast<uint32_t>(command) << ResetColor; | |
| return; | |
| } | |
| } | |
| #define PciBarTypeNameCase(name) \ | |
| case name: \ | |
| *this << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayPciBarType(uint32_t type) { | |
| *this << Blue; | |
| switch (type) { | |
| PciBarTypeNameCase(ZX_PCI_BAR_TYPE_UNUSED); | |
| PciBarTypeNameCase(ZX_PCI_BAR_TYPE_MMIO); | |
| PciBarTypeNameCase(ZX_PCI_BAR_TYPE_PIO); | |
| default: | |
| *this << static_cast<uint32_t>(type) << ResetColor; | |
| return; | |
| } | |
| } | |
| #define ProfileInfoFlagsNameCase(name) \ | |
| if ((flags & (name)) == (name)) { \ | |
| *this << separator << #name; \ | |
| separator = " | "; \ | |
| } | |
| void PrettyPrinter::DisplayProfileInfoFlags(uint32_t flags) { | |
| *this << Blue; | |
| if (flags == 0) { | |
| *this << "0" << ResetColor; | |
| return; | |
| } | |
| const char* separator = ""; | |
| ProfileInfoFlagsNameCase(ZX_PROFILE_INFO_FLAG_PRIORITY); | |
| ProfileInfoFlagsNameCase(ZX_PROFILE_INFO_FLAG_CPU_MASK); | |
| *this << ResetColor; | |
| } | |
| #define PortPacketTypeNameCase(name) \ | |
| case name: \ | |
| *this << #name << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayPortPacketType(uint32_t type) { | |
| *this << Blue; | |
| switch (type) { | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_USER); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_SIGNAL_ONE); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_BELL); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_MEM); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_IO); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_GUEST_VCPU); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_INTERRUPT); | |
| PortPacketTypeNameCase(ZX_PKT_TYPE_PAGE_REQUEST); | |
| default: | |
| *this << "port_packet_type=" << type << ResetColor; | |
| return; | |
| } | |
| } | |
| // ZX_PROP_REGISTER_GS and ZX_PROP_REGISTER_FS are defined in | |
| // <zircon/system/public/zircon/syscalls/object.h> | |
| // but only available for amd64. | |
| // We need these values in all the environments. | |
| #ifndef ZX_PROP_REGISTER_GS | |
| #define ZX_PROP_REGISTER_GS ((uint32_t)2u) | |
| #endif | |
| #ifndef ZX_PROP_REGISTER_FS | |
| #define ZX_PROP_REGISTER_FS ((uint32_t)4u) | |
| #endif | |
| #define PropTypeNameCase(name) \ | |
| case name: \ | |
| *this << #name; \ | |
| *this << ResetColor; \ | |
| return | |
| void PrettyPrinter::DisplayPropType(uint32_t type) { | |
| *this << Blue; | |
| switch (type) { | |
| PropTypeNameCase(ZX_PROP_NAME); | |
| PropTypeNameCase(ZX_PROP_REGISTER_FS); | |
| PropTypeNameCase(ZX_PROP_REGISTER_GS); | |
| PropTypeNameCase(ZX_PROP_PROCESS_DEBUG_ADDR); | |
| PropTypeNameCase(ZX_PROP_PROCESS_VDSO_BASE_ADDRESS); | |
| PropTypeNameCase(ZX_PROP_SOCKET_RX_THRESHOLD); | |
| PropTypeNameCase(ZX_PROP_SOCKET_TX_THRESHOLD); | |
| PropTypeNameCase(ZX_PROP_JOB_KILL_ON_OOM); | |
| PropTypeNameCase(ZX_PROP_EXCEPTION_STATE); | |
| default: | |
| *this << type << ResetColor; | |
| return; | |
| } | |
| } | |
| #define RightsNameCase(name) \ | |
| if ((rights & (name)) != 0) { \ | |
| *this << separator << #name; \ | |
| separator = " | "; \ | |
| } | |
| void PrettyPrinter::DisplayRights(uint32_t rights) { | |
| *this << Blue; | |
| if (rights == 0) { | |
| *this << "ZX_RIGHT_NONE" << ResetColor; | |
| return; | |
| } | |
| const char* separator = ""; | |
| RightsNameCase(ZX_RIGHT_DUPLICATE); | |
| RightsNameCase(ZX_RIGHT_TRANSFER); | |
| RightsNameCase(ZX_RIGHT_READ); | |
| RightsNameCase(ZX_RIGHT_WRITE); | |
| RightsNameCase(ZX_RIGHT_EXECUTE); | |
| RightsNameCase(ZX_RIGHT_MAP); | |
| RightsNameCase(ZX_RIGHT_GET_PROPERTY); | |
| RightsNameCase(ZX_RIGHT_SET_PROPERTY); | |
| RightsNameCase(ZX_RIGHT_ENUMERATE); | |
| RightsNameCase(ZX_RIGHT_DESTROY); | |
| RightsNameCase(ZX_RIGHT_SET_POLICY); | |
| RightsNameCase(ZX_RIGHT_GET_POLICY); | |
| RightsNameCase(ZX_RIGHT_SIGNAL); | |
| RightsNameCase(ZX_RIGHT_SIGNAL_PEER); | |
| RightsNameCase(ZX_RIGHT_WAIT); | |
| RightsNameCase(ZX_RIGHT_INSPECT); | |
| RightsNameCase(ZX_RIGHT_MANAGE_JOB); | |
| RightsNameCase(ZX_RIGHT_MANAGE_PROCESS); | |
| RightsNameCase(ZX_RIGHT_MANAGE_THREAD); | |
| RightsNameCase(ZX_RIGHT_APPLY_PROFILE); | |
| RightsNameCase(ZX_RIGHT_SAME_RIGHTS); | |
| *this << ResetColor; | |
| } | |
| #define SignalNameCase(name) \ | |
| if ((signals & (name)) == (name)) { \ | |
| *this << separator << #name; \ | |
| separator = " | "; \ | |
| } | |
| void PrettyPrinter::DisplaySignals(zx_signals_t signals) { | |
| *this << Blue; | |
| if (signals == 0) { | |
| *this << "0" << ResetColor; | |
| return; | |
| } | |
| if (signals == __ZX_OBJECT_SIGNAL_ALL) { | |
| *this << "__ZX_OBJECT_SIGNAL_ALL" << ResetColor; | |
| return; | |
| } | |
| const char* separator = ""; | |
| SignalNameCase(__ZX_OBJECT_READABLE); | |
| SignalNameCase(__ZX_OBJECT_WRITABLE); | |
| SignalNameCase(__ZX_OBJECT_PEER_CLOSED); | |
| SignalNameCase(__ZX_OBJECT_SIGNALED); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_4); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_5); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_6); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_7); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_8); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_9); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_10); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_11); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_12); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_13); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_14); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_15); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_16); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_17); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_18); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_19); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_20); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_21); | |
| SignalNameCase(__ZX_OBJECT_SIGNAL_22); | |
| SignalNameCase(__ZX_OBJECT_HANDLE_CLOSED); | |
| SignalNameCase(ZX_USER_SIGNAL_0); | |
| SignalNameCase(ZX_USER_SIGNAL_1); | |
| SignalNameCase(ZX_USER_SIGNAL_2); | |
| SignalNameCase(ZX_USER_SIGNAL_3); | |
| SignalNameCase(ZX_USER_SIGNAL_4); | |
| SignalNameCase(ZX_USER_SIGNAL_5); | |
| SignalNameCase(ZX_USER_SIGNAL_6); | |
| SignalNameCase(ZX_USER_SIGNAL_7); | |
| *this << ResetColor; | |
| } | |
| void PrettyPrinter::DisplayStatus(zx_status_t status) { | |
| if (status == ZX_OK) { | |
| *this << Green; | |
| } else { | |
| *this << Red; | |
| } | |
| *this << StatusName(status) << ResetColor; | |
| } | |
| void PrettyPrinter::DisplayString(std::string_view string) { | |
| if (string.data() == nullptr) { | |
| *this << "nullptr\n"; | |
| } else { | |
| *this << Red << '"'; | |
| for (char value : string) { | |
| switch (value) { | |
| case 0: | |
| break; | |
| case '\\': | |
| *this << "\\\\"; | |
| break; | |
| case '\n': | |
| *this << "\\n"; | |
| break; | |
| default: | |
| *this << value; | |
| break; | |
| } | |
| } | |
| *this << '"' << ResetColor; | |
| } | |
| } | |
| void PrettyPrinter::DisplayTime(zx_time_t time_ns) { | |
| if (time_ns == ZX_TIME_INFINITE) { | |
| (*this) << Blue << "ZX_TIME_INFINITE" << ResetColor; | |
| } else if (time_ns == ZX_TIME_INFINITE_PAST) { | |
| (*this) << Blue << "ZX_TIME_INFINITE_PAST" << ResetColor; | |
| } else { | |
| // Gets the time in seconds. | |
| time_t value = time_ns / kOneBillion; | |
| struct tm tm; | |
| if (localtime_r(&value, &tm) == &tm) { | |
| char buffer[100]; | |
| strftime(buffer, sizeof(buffer), "%c", &tm); | |
| // And now, displays the nano seconds. | |
| (*this) << Blue << buffer << " and "; | |
| snprintf(buffer, sizeof(buffer), "%09" PRId64, time_ns % kOneBillion); | |
| (*this) << buffer << " ns" << ResetColor; | |
| } else { | |
| (*this) << Red << "unknown time" << ResetColor; | |
| } | |
| } | |
| } | |
| #ifdef __MACH__ | |
| void PrettyPrinter::DisplayUintptr(uintptr_t ptr) { | |
| std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, ptr); | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| #else | |
| void PrettyPrinter::DisplayUintptr(uint64_t ptr) { | |
| std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIx64, ptr); | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| #endif | |
| void PrettyPrinter::DisplayVaddr(zx_vaddr_t addr) { | |
| std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1); | |
| #ifdef __MACH__ | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, addr); | |
| #else | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIx64, addr); | |
| #endif | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| void PrettyPrinter::DisplayGpAddr(zx_gpaddr_t addr) { | |
| #ifdef __MACH__ | |
| std::vector<char> buffer(sizeof(uintptr_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIxPTR, addr); | |
| #else | |
| std::vector<char> buffer(sizeof(uint64_t) * kCharactersPerByte + 1); | |
| snprintf(buffer.data(), buffer.size(), "%016" PRIx64, addr); | |
| #endif | |
| *this << Blue << buffer.data() << ResetColor; | |
| } | |
| void PrettyPrinter::IncrementTabulations() { | |
| ++tabulations_; | |
| if (need_to_print_header_) { | |
| remaining_size_ -= kTabSize; | |
| } | |
| } | |
| void PrettyPrinter::DecrementTabulations() { | |
| --tabulations_; | |
| if (need_to_print_header_) { | |
| remaining_size_ += kTabSize; | |
| } | |
| } | |
| void PrettyPrinter::NeedHeader() { | |
| remaining_size_ = max_line_size_ - line_header_size_ - tabulations_ * kTabSize; | |
| need_to_print_header_ = true; | |
| } | |
| void PrettyPrinter::PrintHeader(char first_character) { | |
| FX_DCHECK(need_to_print_header_); | |
| need_to_print_header_ = false; | |
| if (line_header_size_ > 0) { | |
| os_ << line_header_; | |
| if (!header_on_every_line_) { | |
| line_header_size_ = 0; | |
| } | |
| } | |
| if (first_character != '\n') { | |
| for (int tab = tabulations_ * kTabSize; tab > 0; --tab) { | |
| os_ << ' '; | |
| } | |
| } | |
| } | |
| PrettyPrinter& PrettyPrinter::operator<<(std::string_view data) { | |
| if (data.empty()) { | |
| return *this; | |
| } | |
| if (need_to_print_header_) { | |
| PrintHeader(data[0]); | |
| } | |
| size_t end_of_line = data.find('\n', 0); | |
| if (end_of_line == std::string_view::npos) { | |
| os_ << data; | |
| remaining_size_ -= data.size(); | |
| return *this; | |
| } | |
| size_t current = 0; | |
| for (;;) { | |
| std::string_view tmp = data.substr(current, end_of_line - current + 1); | |
| os_ << tmp; | |
| NeedHeader(); | |
| current = end_of_line + 1; | |
| if (current >= data.size()) { | |
| return *this; | |
| } | |
| end_of_line = data.find('\n', current); | |
| if (end_of_line == std::string_view::npos) { | |
| os_ << data; | |
| remaining_size_ -= data.size(); | |
| return *this; | |
| } | |
| PrintHeader(data[current]); | |
| } | |
| } | |
| } // namespace fidl_codec |