diff options
Diffstat (limited to 'vendor/toml++/impl/formatter.inl')
| -rw-r--r-- | vendor/toml++/impl/formatter.inl | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/vendor/toml++/impl/formatter.inl b/vendor/toml++/impl/formatter.inl new file mode 100644 index 0000000..f429d32 --- /dev/null +++ b/vendor/toml++/impl/formatter.inl @@ -0,0 +1,523 @@ +//# This file is a part of toml++ and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT +#pragma once + +#include "preprocessor.hpp" +//# {{ +#if !TOML_IMPLEMENTATION +#error This is an implementation-only header. +#endif +//# }} +#if TOML_ENABLE_FORMATTERS + +#include "formatter.hpp" +#include "print_to_stream.hpp" +#include "value.hpp" +#include "table.hpp" +#include "array.hpp" +#include "unicode.hpp" +#include "parse_result.hpp" +#include "header_start.hpp" + +TOML_IMPL_NAMESPACE_START +{ + enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned + { + none, + line_breaks = 1u << 0, // \n + tabs = 1u << 1, // \t + control_chars = 1u << 2, // also includes non-ascii vertical whitespace + single_quotes = 1u << 3, + non_bare = 1u << 4, // anything not satisfying "is bare key character" + non_ascii = 1u << 5, // any codepoint >= 128 + + all = (non_ascii << 1u) - 1u + }; + TOML_MAKE_FLAGS(formatted_string_traits); + + TOML_EXTERNAL_LINKAGE + formatter::formatter(const node* source_node, + const parse_result* source_pr, + const formatter_constants& constants, + const formatter_config& config) noexcept // +#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS + : source_{ source_pr && *source_pr ? &source_pr->table() : source_node }, + result_{ source_pr }, +#else + : source_{ source_pr ? source_pr : source_node }, +#endif + constants_{ &constants }, + config_{ config } + { + TOML_ASSERT_ASSUME(source_); + + config_.flags = (config_.flags | constants_->mandatory_flags) & ~constants_->ignored_flags; + + indent_columns_ = {}; + for (auto c : config_.indent) + indent_columns_ += c == '\t' ? 4u : 1u; + + int_format_mask_ = config_.flags + & (format_flags::allow_binary_integers | format_flags::allow_octal_integers + | format_flags::allow_hexadecimal_integers); + } + + TOML_EXTERNAL_LINKAGE + void formatter::attach(std::ostream & stream) noexcept + { + indent_ = {}; + naked_newline_ = true; + stream_ = &stream; + } + + TOML_EXTERNAL_LINKAGE + void formatter::detach() noexcept + { + stream_ = nullptr; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_newline(bool force) + { + if (!naked_newline_ || force) + { + print_to_stream(*stream_, '\n'); + naked_newline_ = true; + } + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_indent() + { + for (int i = 0; i < indent_; i++) + { + print_to_stream(*stream_, config_.indent); + naked_newline_ = false; + } + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_unformatted(char c) + { + print_to_stream(*stream_, c); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_unformatted(std::string_view str) + { + print_to_stream(*stream_, str); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_string(std::string_view str, + bool allow_multi_line, + bool allow_bare, + bool allow_literal_whitespace) + { + if (str.empty()) + { + print_unformatted(literal_strings_allowed() ? "''"sv : "\"\""sv); + return; + } + + // pre-scan the string to determine how we should output it + formatted_string_traits traits = {}; + + if (!allow_bare) + traits |= formatted_string_traits::non_bare; + bool unicode_allowed = unicode_strings_allowed(); + + // ascii fast path + if (is_ascii(str.data(), str.length())) + { + for (auto c : str) + { + switch (c) + { + case '\n': traits |= formatted_string_traits::line_breaks; break; + case '\t': traits |= formatted_string_traits::tabs; break; + case '\'': traits |= formatted_string_traits::single_quotes; break; + default: + { + if TOML_UNLIKELY(is_control_character(c)) + traits |= formatted_string_traits::control_chars; + + if (!is_ascii_bare_key_character(static_cast<char32_t>(c))) + traits |= formatted_string_traits::non_bare; + break; + } + } + + static constexpr auto all_ascii_traits = + formatted_string_traits::all & ~formatted_string_traits::non_ascii; + if (traits == all_ascii_traits) + break; + } + } + + // unicode slow path + else + { + traits |= formatted_string_traits::non_ascii; + utf8_decoder decoder; + + // if the unicode is malformed just treat the string as a single-line non-literal and + // escape all non-ascii characters (to ensure round-tripping and help with diagnostics) + const auto bad_unicode = [&]() noexcept + { + traits &= ~formatted_string_traits::line_breaks; + traits |= formatted_string_traits::control_chars | formatted_string_traits::non_bare; + unicode_allowed = false; + }; + + for (auto c : str) + { + decoder(c); + + if TOML_UNLIKELY(decoder.error()) + { + bad_unicode(); + break; + } + + if (!decoder.has_code_point()) + continue; + + switch (decoder.codepoint) + { + case U'\n': traits |= formatted_string_traits::line_breaks; break; + case U'\t': traits |= formatted_string_traits::tabs; break; + case U'\'': traits |= formatted_string_traits::single_quotes; break; + default: + { + if TOML_UNLIKELY(is_control_character(decoder.codepoint) + || is_non_ascii_vertical_whitespace(decoder.codepoint)) + traits |= formatted_string_traits::control_chars; + + if (!is_bare_key_character(decoder.codepoint)) + traits |= formatted_string_traits::non_bare; + break; + } + } + } + + if (decoder.needs_more_input()) + bad_unicode(); + } + + // strings with line breaks, tabs, and single-quotes can't be bare + if (!!(traits + & (formatted_string_traits::line_breaks | formatted_string_traits::tabs + | formatted_string_traits::single_quotes))) + traits |= formatted_string_traits::non_bare; + + // if the string meets the requirements of being 'bare' we can emit a bare string + // (bare strings are composed of letters and numbers; no whitespace, control chars, quotes, etc) + if (!(traits & formatted_string_traits::non_bare) + && (!(traits & formatted_string_traits::non_ascii) || unicode_allowed)) + { + print_unformatted(str); + return; + } + const auto real_tabs_allowed = allow_literal_whitespace && real_tabs_in_strings_allowed(); + + // determine if this should be a multi-line string (triple-quotes) + const auto multi_line = allow_literal_whitespace // + && allow_multi_line // + && multi_line_strings_allowed() // + && !!(traits & formatted_string_traits::line_breaks); + + // determine if this should be a literal string (single-quotes with no escaping) + const auto literal = literal_strings_allowed() // + && !(traits & formatted_string_traits::control_chars) // + && (!(traits & formatted_string_traits::single_quotes) || multi_line) // + && (!(traits & formatted_string_traits::tabs) || real_tabs_allowed) // + && (!(traits & formatted_string_traits::line_breaks) || multi_line) // + && (!(traits & formatted_string_traits::non_ascii) || unicode_allowed); + + // literal strings (single quotes, no escape codes) + if (literal) + { + const auto quot = multi_line ? R"(''')"sv : R"(')"sv; + print_unformatted(quot); + print_unformatted(str); + print_unformatted(quot); + return; + } + + // anything from here down is a non-literal string, so requires iteration and escaping. + print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); + + // ascii fast path + if (!(traits & formatted_string_traits::non_ascii)) + { + for (auto c : str) + { + switch (c) + { + case '"': print_to_stream(*stream_, R"(\")"sv); break; + case '\\': print_to_stream(*stream_, R"(\\)"sv); break; + case '\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break; + case '\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break; + case '\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break; + default: + { + // control characters from lookup table + if TOML_UNLIKELY(c >= '\x00' && c <= '\x1F') + print_to_stream(*stream_, control_char_escapes[c]); + + // regular characters + else + print_to_stream(*stream_, c); + } + } + } + } + + // unicode slow path + else + { + utf8_decoder decoder; + const char* cp_start = str.data(); + const char* cp_end = cp_start; + for (auto c : str) + { + decoder(c); + cp_end++; + + // if the decoder encounters malformed unicode just emit raw bytes and + if (decoder.error()) + { + while (cp_start != cp_end) + { + print_to_stream(*stream_, R"(\u00)"sv); + print_to_stream(*stream_, + static_cast<uint8_t>(*cp_start), + value_flags::format_as_hexadecimal, + 2); + cp_start++; + } + decoder.reset(); + continue; + } + + if (!decoder.has_code_point()) + continue; + + switch (decoder.codepoint) + { + case U'"': print_to_stream(*stream_, R"(\")"sv); break; + case U'\\': print_to_stream(*stream_, R"(\\)"sv); break; + case U'\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break; + case U'\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break; + case U'\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break; + default: + { + // control characters from lookup table + if TOML_UNLIKELY(decoder.codepoint <= U'\x1F') + print_to_stream(*stream_, + control_char_escapes[static_cast<uint_least32_t>(decoder.codepoint)]); + + // escaped unicode characters + else if (decoder.codepoint > U'\x7F' + && (!unicode_allowed || is_non_ascii_vertical_whitespace(decoder.codepoint))) + { + if (static_cast<uint_least32_t>(decoder.codepoint) > 0xFFFFu) + { + print_to_stream(*stream_, R"(\U)"sv); + print_to_stream(*stream_, + static_cast<uint_least32_t>(decoder.codepoint), + value_flags::format_as_hexadecimal, + 8); + } + else + { + print_to_stream(*stream_, R"(\u)"sv); + print_to_stream(*stream_, + static_cast<uint_least32_t>(decoder.codepoint), + value_flags::format_as_hexadecimal, + 4); + } + } + + // regular characters + else + print_to_stream(*stream_, cp_start, static_cast<size_t>(cp_end - cp_start)); + } + } + + cp_start = cp_end; + } + } + + print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<std::string>& val) + { + print_string(val.get()); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<int64_t>& val) + { + naked_newline_ = false; + + if (*val >= 0 && !!int_format_mask_) + { + static constexpr auto value_flags_mask = + value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal; + + const auto fmt = val.flags() & value_flags_mask; + switch (fmt) + { + case value_flags::format_as_binary: + if (!!(int_format_mask_ & format_flags::allow_binary_integers)) + { + print_to_stream(*stream_, "0b"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + case value_flags::format_as_octal: + if (!!(int_format_mask_ & format_flags::allow_octal_integers)) + { + print_to_stream(*stream_, "0o"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + case value_flags::format_as_hexadecimal: + if (!!(int_format_mask_ & format_flags::allow_hexadecimal_integers)) + { + print_to_stream(*stream_, "0x"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + default: break; + } + } + + // fallback to decimal + print_to_stream(*stream_, *val); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<double>& val) + { + const std::string_view* inf_nan = nullptr; + switch (fpclassify(*val)) + { + case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break; + case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break; + case fp_class::nan: inf_nan = &constants_->float_nan; break; + case fp_class::ok: + print_to_stream(*stream_, + *val, + value_flags::none, + !!(config_.flags & format_flags::relaxed_float_precision)); + break; + default: TOML_UNREACHABLE; + } + + if (inf_nan) + { + if (!!(config_.flags & format_flags::quote_infinities_and_nans)) + print_to_stream_bookended(*stream_, *inf_nan, '"'); + else + print_to_stream(*stream_, *inf_nan); + } + + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<bool>& val) + { + print_unformatted(*val ? constants_->bool_true : constants_->bool_false); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<date>& val) + { + if (!!(config_.flags & format_flags::quote_dates_and_times)) + print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); + else + print_to_stream(*stream_, *val); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<time>& val) + { + if (!!(config_.flags & format_flags::quote_dates_and_times)) + print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); + else + print_to_stream(*stream_, *val); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<date_time>& val) + { + if (!!(config_.flags & format_flags::quote_dates_and_times)) + print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); + else + print_to_stream(*stream_, *val); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_value(const node& val_node, node_type type) + { + TOML_ASSUME(type > node_type::array); + switch (type) + { + case node_type::string: print(*reinterpret_cast<const value<std::string>*>(&val_node)); break; + case node_type::integer: print(*reinterpret_cast<const value<int64_t>*>(&val_node)); break; + case node_type::floating_point: print(*reinterpret_cast<const value<double>*>(&val_node)); break; + case node_type::boolean: print(*reinterpret_cast<const value<bool>*>(&val_node)); break; + case node_type::date: print(*reinterpret_cast<const value<date>*>(&val_node)); break; + case node_type::time: print(*reinterpret_cast<const value<time>*>(&val_node)); break; + case node_type::date_time: print(*reinterpret_cast<const value<date_time>*>(&val_node)); break; + default: TOML_UNREACHABLE; + } + } + +#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS + + TOML_EXTERNAL_LINKAGE + bool formatter::dump_failed_parse_result() + { + if (result_ && !(*result_)) + { + stream() << result_->error(); + return true; + } + return false; + } + +#else + + TOML_EXTERNAL_LINKAGE + TOML_ATTR(const) + bool formatter::dump_failed_parse_result() + { + return false; + } + +#endif +} +TOML_IMPL_NAMESPACE_END; + +#include "header_end.hpp" +#endif // TOML_ENABLE_FORMATTERS |
