From d987d62459d59a6d95ffb815d5e6eccd8dbde1dd Mon Sep 17 00:00:00 2001 From: Amlal Date: Mon, 11 Aug 2025 23:31:39 +0200 Subject: meta: ran format command. Signed-off-by: Amlal --- dev/BuildKit/IManifestBuilder.h | 6 +- dev/BuildKit/Imports.h | 4 +- dev/BuildKit/JSONManifestBuilder.h | 4 +- dev/BuildKit/TOMLManifestBuilder.h | 4 +- dev/cli/Tool.cc | 4 +- dev/src/JSONManifestBuilder.cc | 15 +- examples/example_02_libnebuild/libnebuild.cc | 2 +- vendor/toml++/impl/array.inl | 617 +-- vendor/toml++/impl/at_path.inl | 472 +- vendor/toml++/impl/formatter.inl | 970 ++-- vendor/toml++/impl/json_formatter.inl | 187 +- vendor/toml++/impl/node.inl | 218 +- vendor/toml++/impl/parser.inl | 6938 ++++++++++++-------------- vendor/toml++/impl/path.inl | 822 ++- vendor/toml++/impl/print_to_stream.inl | 791 ++- vendor/toml++/impl/std_string.inl | 116 +- vendor/toml++/impl/table.inl | 498 +- vendor/toml++/impl/toml_formatter.inl | 731 ++- vendor/toml++/impl/unicode.inl | 70 +- vendor/toml++/impl/yaml_formatter.inl | 299 +- 20 files changed, 5980 insertions(+), 6788 deletions(-) diff --git a/dev/BuildKit/IManifestBuilder.h b/dev/BuildKit/IManifestBuilder.h index 1dd8bce..c1d45d0 100644 --- a/dev/BuildKit/IManifestBuilder.h +++ b/dev/BuildKit/IManifestBuilder.h @@ -14,7 +14,7 @@ namespace NeBuild { /// @note This class is meant to be used as an interface. class IManifestBuilder { public: - IManifestBuilder() = default; + IManifestBuilder() = default; virtual ~IManifestBuilder() = default; IManifestBuilder& operator=(const IManifestBuilder&) = default; @@ -25,7 +25,7 @@ class IManifestBuilder { /// @param arg_val filename path. /// @retval true succeeded. /// @retval false failed. - virtual bool BuildTarget(const std::string& arg, const bool dry_run = false) = 0; - virtual const char* BuildSystem() = 0; + virtual bool BuildTarget(const std::string& arg, const bool dry_run = false) = 0; + virtual const char* BuildSystem() = 0; }; } // namespace NeBuild \ No newline at end of file diff --git a/dev/BuildKit/Imports.h b/dev/BuildKit/Imports.h index 9260c1e..c542737 100644 --- a/dev/BuildKit/Imports.h +++ b/dev/BuildKit/Imports.h @@ -6,6 +6,8 @@ #ifndef NEBUILD_INCLUDES_H #define NEBUILD_INCLUDES_H +#include +#include #include #include #include @@ -13,7 +15,5 @@ #include #include #include -#include -#include #endif // NEBUILD_INCLUDES_H diff --git a/dev/BuildKit/JSONManifestBuilder.h b/dev/BuildKit/JSONManifestBuilder.h index b853ccf..63a67b7 100644 --- a/dev/BuildKit/JSONManifestBuilder.h +++ b/dev/BuildKit/JSONManifestBuilder.h @@ -15,7 +15,7 @@ namespace NeBuild { /// @brief JSON builder class JSONManifestBuilder final NEBUILD_MANIFEST_BUILDER { public: - JSONManifestBuilder() = default; + JSONManifestBuilder() = default; ~JSONManifestBuilder() override = default; JSONManifestBuilder& operator=(const JSONManifestBuilder&) = default; @@ -27,7 +27,7 @@ class JSONManifestBuilder final NEBUILD_MANIFEST_BUILDER { /// @param arg_val filename path. /// @retval true build succeeded. /// @retval false failed to build. - bool BuildTarget(const std::string& arg_val, const bool dry_run = false) override; + bool BuildTarget(const std::string& arg_val, const bool dry_run = false) override; const char* BuildSystem() override; }; } // namespace NeBuild \ No newline at end of file diff --git a/dev/BuildKit/TOMLManifestBuilder.h b/dev/BuildKit/TOMLManifestBuilder.h index e300ba6..10a8200 100644 --- a/dev/BuildKit/TOMLManifestBuilder.h +++ b/dev/BuildKit/TOMLManifestBuilder.h @@ -15,7 +15,7 @@ namespace NeBuild { /// @brief TOML builder class TOMLManifestBuilder final NEBUILD_MANIFEST_BUILDER { public: - TOMLManifestBuilder() = default; + TOMLManifestBuilder() = default; ~TOMLManifestBuilder() override = default; TOMLManifestBuilder& operator=(const TOMLManifestBuilder&) = default; @@ -27,7 +27,7 @@ class TOMLManifestBuilder final NEBUILD_MANIFEST_BUILDER { /// @param arg_val filename path. /// @retval true build succeeded. /// @retval false failed to build. - bool BuildTarget(int arg_sz, const char* arg_val, const bool dry_run = false) override; + bool BuildTarget(int arg_sz, const char* arg_val, const bool dry_run = false) override; const char* BuildSystem() override; }; } // namespace NeBuild \ No newline at end of file diff --git a/dev/cli/Tool.cc b/dev/cli/Tool.cc index 315f197..1d49c9f 100644 --- a/dev/cli/Tool.cc +++ b/dev/cli/Tool.cc @@ -52,8 +52,8 @@ int main(int argc, char** argv) { return; } } else { - NeBuild::Logger::info() << "error: file '" << index_path << "' is not a JSON file!" - << std::endl; + NeBuild::Logger::info() + << "error: file '" << index_path << "' is not a JSON file!" << std::endl; kFailed = true; return; } diff --git a/dev/src/JSONManifestBuilder.cc b/dev/src/JSONManifestBuilder.cc index 3b03bf8..33d7f62 100644 --- a/dev/src/JSONManifestBuilder.cc +++ b/dev/src/JSONManifestBuilder.cc @@ -5,7 +5,7 @@ #include -using JSON = nlohmann::json; +using JSON = nlohmann::json; namespace FS = std::filesystem; @@ -26,7 +26,8 @@ bool JSONManifestBuilder::BuildTarget(const std::string& argv_val, const bool dr path = argv_val; if (!FS::exists(path)) { - NeBuild::Logger::info() << "nebuild: error: file '" << path << "' does not exist" << std::endl; + NeBuild::Logger::info() << "nebuild: error: file '" << path << "' does not exist" + << std::endl; return false; } } @@ -35,7 +36,8 @@ bool JSONManifestBuilder::BuildTarget(const std::string& argv_val, const bool dr std::ifstream json(path); if (!json.good()) { - NeBuild::Logger::info() << "nebuild: error: file '" << path << "' is not a valid JSON" << std::endl; + NeBuild::Logger::info() << "nebuild: error: file '" << path << "' is not a valid JSON" + << std::endl; return false; } @@ -90,15 +92,16 @@ bool JSONManifestBuilder::BuildTarget(const std::string& argv_val, const bool dr auto ret_exec = std::system(command.c_str()); if (ret_exec > 0) { - NeBuild::Logger::info() << "error: exit with message: " << std::strerror(ret_exec) << "" << std::endl; + NeBuild::Logger::info() << "error: exit with message: " << std::strerror(ret_exec) << "" + << std::endl; return false; } try { if (json_obj["run_after_build"].get()) { if (target.ends_with(".so")) { - NeBuild::Logger::info() << "error: can't open dynamic library, it mayn't have an entrypoint" - << std::endl; + NeBuild::Logger::info() + << "error: can't open dynamic library, it mayn't have an entrypoint" << std::endl; return true; } else if (target.ends_with(".dylib") || target.ends_with(".dll")) { diff --git a/examples/example_02_libnebuild/libnebuild.cc b/examples/example_02_libnebuild/libnebuild.cc index 74cbe18..5540503 100644 --- a/examples/example_02_libnebuild/libnebuild.cc +++ b/examples/example_02_libnebuild/libnebuild.cc @@ -10,6 +10,6 @@ static auto kPath = ".\\win64.json"; int main(int argc, char** argv) { auto builder = new NeBuild::JSONManifestBuilder(); if (!builder) return EXIT_FAILURE; - + return builder->BuildTarget(kPath); } diff --git a/vendor/toml++/impl/array.inl b/vendor/toml++/impl/array.inl index a3c6243..7ae8ee1 100644 --- a/vendor/toml++/impl/array.inl +++ b/vendor/toml++/impl/array.inl @@ -1,381 +1,306 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard +// # 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 -//# }} +// # }} #include "array.hpp" #include "header_start.hpp" -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - array::array() noexcept - { +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + array::array() noexcept { #if TOML_LIFETIME_HOOKS - TOML_ARRAY_CREATED; + TOML_ARRAY_CREATED; #endif - } + } - TOML_EXTERNAL_LINKAGE - array::~array() noexcept - { + TOML_EXTERNAL_LINKAGE + array::~array() noexcept { #if TOML_LIFETIME_HOOKS - TOML_ARRAY_DESTROYED; + TOML_ARRAY_DESTROYED; #endif - } + } - TOML_EXTERNAL_LINKAGE - array::array(const impl::array_init_elem* b, const impl::array_init_elem* e) - { + TOML_EXTERNAL_LINKAGE + array::array(const impl::array_init_elem* b, const impl::array_init_elem* e) { #if TOML_LIFETIME_HOOKS - TOML_ARRAY_CREATED; + TOML_ARRAY_CREATED; #endif - TOML_ASSERT_ASSUME(b); - TOML_ASSERT_ASSUME(e); - TOML_ASSERT_ASSUME(b <= e); - - if TOML_UNLIKELY(b == e) - return; - - size_t cap{}; - for (auto it = b; it != e; it++) - { - if (it->value) - cap++; - } - if TOML_UNLIKELY(!cap) - return; - - elems_.reserve(cap); - for (; b != e; b++) - { - if (b->value) - elems_.push_back(std::move(b->value)); - } - } - - TOML_EXTERNAL_LINKAGE - array::array(const array& other) // - : node(other) - { - elems_.reserve(other.elems_.size()); - for (const auto& elem : other) - elems_.emplace_back(impl::make_node(elem)); + TOML_ASSERT_ASSUME(b); + TOML_ASSERT_ASSUME(e); + TOML_ASSERT_ASSUME(b <= e); + + if TOML_UNLIKELY (b == e) return; + + size_t cap{}; + for (auto it = b; it != e; it++) { + if (it->value) cap++; + } + if TOML_UNLIKELY (!cap) return; + + elems_.reserve(cap); + for (; b != e; b++) { + if (b->value) elems_.push_back(std::move(b->value)); + } + } + + TOML_EXTERNAL_LINKAGE + array::array(const array& other) // + : node(other) { + elems_.reserve(other.elems_.size()); + for (const auto& elem : other) elems_.emplace_back(impl::make_node(elem)); #if TOML_LIFETIME_HOOKS - TOML_ARRAY_CREATED; + TOML_ARRAY_CREATED; #endif - } + } - TOML_EXTERNAL_LINKAGE - array::array(array && other) noexcept // - : node(std::move(other)), - elems_(std::move(other.elems_)) - { + TOML_EXTERNAL_LINKAGE + array::array(array && other) noexcept // + : node(std::move(other)), elems_(std::move(other.elems_)) { #if TOML_LIFETIME_HOOKS - TOML_ARRAY_CREATED; + TOML_ARRAY_CREATED; #endif - } - - TOML_EXTERNAL_LINKAGE - array& array::operator=(const array& rhs) - { - if (&rhs != this) - { - node::operator=(rhs); - elems_.clear(); - elems_.reserve(rhs.elems_.size()); - for (const auto& elem : rhs) - elems_.emplace_back(impl::make_node(elem)); - } - return *this; - } - - TOML_EXTERNAL_LINKAGE - array& array::operator=(array&& rhs) noexcept - { - if (&rhs != this) - { - node::operator=(std::move(rhs)); - elems_ = std::move(rhs.elems_); - } - return *this; - } - - TOML_EXTERNAL_LINKAGE - void array::preinsertion_resize(size_t idx, size_t count) - { - TOML_ASSERT(idx <= elems_.size()); - TOML_ASSERT_ASSUME(count >= 1u); - const auto old_size = elems_.size(); - const auto new_size = old_size + count; - const auto inserting_at_end = idx == old_size; - elems_.resize(new_size); - if (!inserting_at_end) - { - for (size_t left = old_size, right = new_size - 1u; left-- > idx; right--) - elems_[right] = std::move(elems_[left]); - } - } - - TOML_EXTERNAL_LINKAGE - void array::insert_at_back(impl::node_ptr && elem) - { - TOML_ASSERT(elem); - elems_.push_back(std::move(elem)); - } - - TOML_EXTERNAL_LINKAGE - array::vector_iterator array::insert_at(const_vector_iterator pos, impl::node_ptr && elem) - { - return elems_.insert(pos, std::move(elem)); - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool array::is_homogeneous(node_type ntype) const noexcept - { - if (elems_.empty()) - return false; - - if (ntype == node_type::none) - ntype = elems_[0]->type(); - - for (const auto& val : elems_) - if (val->type() != ntype) - return false; - - return true; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool array::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept - { - if (elems_.empty()) - { - first_nonmatch = {}; - return false; - } - if (ntype == node_type::none) - ntype = elems_[0]->type(); - for (const auto& val : elems_) - { - if (val->type() != ntype) - { - first_nonmatch = val.get(); - return false; - } - } - return true; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool array::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept - { - node* fnm = nullptr; - const auto result = const_cast(*this).is_homogeneous(ntype, fnm); - first_nonmatch = fnm; - return result; - } - - TOML_EXTERNAL_LINKAGE - node& array::at(size_t index) - { + } + + TOML_EXTERNAL_LINKAGE + array& array::operator=(const array& rhs) { + if (&rhs != this) { + node::operator=(rhs); + elems_.clear(); + elems_.reserve(rhs.elems_.size()); + for (const auto& elem : rhs) elems_.emplace_back(impl::make_node(elem)); + } + return *this; + } + + TOML_EXTERNAL_LINKAGE + array& array::operator=(array&& rhs) noexcept { + if (&rhs != this) { + node::operator=(std::move(rhs)); + elems_ = std::move(rhs.elems_); + } + return *this; + } + + TOML_EXTERNAL_LINKAGE + void array::preinsertion_resize(size_t idx, size_t count) { + TOML_ASSERT(idx <= elems_.size()); + TOML_ASSERT_ASSUME(count >= 1u); + const auto old_size = elems_.size(); + const auto new_size = old_size + count; + const auto inserting_at_end = idx == old_size; + elems_.resize(new_size); + if (!inserting_at_end) { + for (size_t left = old_size, right = new_size - 1u; left-- > idx; right--) + elems_[right] = std::move(elems_[left]); + } + } + + TOML_EXTERNAL_LINKAGE + void array::insert_at_back(impl::node_ptr && elem) { + TOML_ASSERT(elem); + elems_.push_back(std::move(elem)); + } + + TOML_EXTERNAL_LINKAGE + array::vector_iterator array::insert_at(const_vector_iterator pos, impl::node_ptr && elem) { + return elems_.insert(pos, std::move(elem)); + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype) const noexcept { + if (elems_.empty()) return false; + + if (ntype == node_type::none) ntype = elems_[0]->type(); + + for (const auto& val : elems_) + if (val->type() != ntype) return false; + + return true; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept { + if (elems_.empty()) { + first_nonmatch = {}; + return false; + } + if (ntype == node_type::none) ntype = elems_[0]->type(); + for (const auto& val : elems_) { + if (val->type() != ntype) { + first_nonmatch = val.get(); + return false; + } + } + return true; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept { + node* fnm = nullptr; + const auto result = const_cast(*this).is_homogeneous(ntype, fnm); + first_nonmatch = fnm; + return result; + } + + TOML_EXTERNAL_LINKAGE + node& array::at(size_t index) { #if TOML_COMPILER_HAS_EXCEPTIONS - return *elems_.at(index); + return *elems_.at(index); #else - auto n = get(index); - TOML_ASSERT_ASSUME(n && "element index not found in array!"); - return *n; + auto n = get(index); + TOML_ASSERT_ASSUME(n && "element index not found in array!"); + return *n; #endif - } - - TOML_EXTERNAL_LINKAGE - void array::reserve(size_t new_capacity) - { - elems_.reserve(new_capacity); - } - - TOML_EXTERNAL_LINKAGE - void array::shrink_to_fit() - { - elems_.shrink_to_fit(); - } - - TOML_EXTERNAL_LINKAGE - void array::truncate(size_t new_size) - { - if (new_size < elems_.size()) - elems_.resize(new_size); - } - - TOML_EXTERNAL_LINKAGE - array::iterator array::erase(const_iterator pos) noexcept - { - return iterator{ elems_.erase(const_vector_iterator{ pos }) }; - } - - TOML_EXTERNAL_LINKAGE - array::iterator array::erase(const_iterator first, const_iterator last) noexcept - { - return iterator{ elems_.erase(const_vector_iterator{ first }, const_vector_iterator{ last }) }; - } - - TOML_EXTERNAL_LINKAGE - size_t array::total_leaf_count() const noexcept - { - size_t leaves{}; - for (size_t i = 0, e = elems_.size(); i < e; i++) - { - auto arr = elems_[i]->as_array(); - leaves += arr ? arr->total_leaf_count() : size_t{ 1 }; - } - return leaves; - } - - TOML_EXTERNAL_LINKAGE - void array::flatten_child(array && child, size_t & dest_index) noexcept - { - for (size_t i = 0, e = child.size(); i < e; i++) - { - auto type = child.elems_[i]->type(); - if (type == node_type::array) - { - array& arr = *reinterpret_cast(child.elems_[i].get()); - if (!arr.empty()) - flatten_child(std::move(arr), dest_index); - } - else - elems_[dest_index++] = std::move(child.elems_[i]); - } - } - - TOML_EXTERNAL_LINKAGE - array& array::flatten()& - { - if (elems_.empty()) - return *this; - - bool requires_flattening = false; - size_t size_after_flattening = elems_.size(); - for (size_t i = elems_.size(); i-- > 0u;) - { - auto arr = elems_[i]->as_array(); - if (!arr) - continue; - size_after_flattening--; // discount the array itself - const auto leaf_count = arr->total_leaf_count(); - if (leaf_count > 0u) - { - requires_flattening = true; - size_after_flattening += leaf_count; - } - else - elems_.erase(elems_.cbegin() + static_cast(i)); - } - - if (!requires_flattening) - return *this; - - elems_.reserve(size_after_flattening); - - size_t i = 0; - while (i < elems_.size()) - { - auto arr = elems_[i]->as_array(); - if (!arr) - { - i++; - continue; - } - - impl::node_ptr arr_storage = std::move(elems_[i]); - const auto leaf_count = arr->total_leaf_count(); - if (leaf_count > 1u) - preinsertion_resize(i + 1u, leaf_count - 1u); - flatten_child(std::move(*arr), i); // increments i - } - - return *this; - } - - TOML_EXTERNAL_LINKAGE - array& array::prune(bool recursive)& noexcept - { - if (elems_.empty()) - return *this; - - for (size_t i = elems_.size(); i-- > 0u;) - { - if (auto arr = elems_[i]->as_array()) - { - if (recursive) - arr->prune(true); - if (arr->empty()) - elems_.erase(elems_.cbegin() + static_cast(i)); - } - else if (auto tbl = elems_[i]->as_table()) - { - if (recursive) - tbl->prune(true); - if (tbl->empty()) - elems_.erase(elems_.cbegin() + static_cast(i)); - } - } - - return *this; - } - - TOML_EXTERNAL_LINKAGE - void array::pop_back() noexcept - { - elems_.pop_back(); - } - - TOML_EXTERNAL_LINKAGE - void array::clear() noexcept - { - elems_.clear(); - } - - TOML_EXTERNAL_LINKAGE - bool TOML_CALLCONV array::equal(const array& lhs, const array& rhs) noexcept - { - if (&lhs == &rhs) - return true; - if (lhs.elems_.size() != rhs.elems_.size()) - return false; - for (size_t i = 0, e = lhs.elems_.size(); i < e; i++) - { - const auto lhs_type = lhs.elems_[i]->type(); - const node& rhs_ = *rhs.elems_[i]; - const auto rhs_type = rhs_.type(); - if (lhs_type != rhs_type) - return false; - - const bool equal = lhs.elems_[i]->visit( - [&](const auto& lhs_) noexcept - { return lhs_ == *reinterpret_cast*>(&rhs_); }); - if (!equal) - return false; - } - return true; - } + } + + TOML_EXTERNAL_LINKAGE + void array::reserve(size_t new_capacity) { + elems_.reserve(new_capacity); + } + + TOML_EXTERNAL_LINKAGE + void array::shrink_to_fit() { + elems_.shrink_to_fit(); + } + + TOML_EXTERNAL_LINKAGE + void array::truncate(size_t new_size) { + if (new_size < elems_.size()) elems_.resize(new_size); + } + + TOML_EXTERNAL_LINKAGE + array::iterator array::erase(const_iterator pos) noexcept { + return iterator{elems_.erase(const_vector_iterator{pos})}; + } + + TOML_EXTERNAL_LINKAGE + array::iterator array::erase(const_iterator first, const_iterator last) noexcept { + return iterator{elems_.erase(const_vector_iterator{first}, const_vector_iterator{last})}; + } + + TOML_EXTERNAL_LINKAGE + size_t array::total_leaf_count() const noexcept { + size_t leaves{}; + for (size_t i = 0, e = elems_.size(); i < e; i++) { + auto arr = elems_[i]->as_array(); + leaves += arr ? arr->total_leaf_count() : size_t{1}; + } + return leaves; + } + + TOML_EXTERNAL_LINKAGE + void array::flatten_child(array && child, size_t & dest_index) noexcept { + for (size_t i = 0, e = child.size(); i < e; i++) { + auto type = child.elems_[i]->type(); + if (type == node_type::array) { + array& arr = *reinterpret_cast(child.elems_[i].get()); + if (!arr.empty()) flatten_child(std::move(arr), dest_index); + } else + elems_[dest_index++] = std::move(child.elems_[i]); + } + } + + TOML_EXTERNAL_LINKAGE + array& array::flatten()& { + if (elems_.empty()) return *this; + + bool requires_flattening = false; + size_t size_after_flattening = elems_.size(); + for (size_t i = elems_.size(); i-- > 0u;) { + auto arr = elems_[i]->as_array(); + if (!arr) continue; + size_after_flattening--; // discount the array itself + const auto leaf_count = arr->total_leaf_count(); + if (leaf_count > 0u) { + requires_flattening = true; + size_after_flattening += leaf_count; + } else + elems_.erase(elems_.cbegin() + static_cast(i)); + } + + if (!requires_flattening) return *this; + + elems_.reserve(size_after_flattening); + + size_t i = 0; + while (i < elems_.size()) { + auto arr = elems_[i]->as_array(); + if (!arr) { + i++; + continue; + } + + impl::node_ptr arr_storage = std::move(elems_[i]); + const auto leaf_count = arr->total_leaf_count(); + if (leaf_count > 1u) preinsertion_resize(i + 1u, leaf_count - 1u); + flatten_child(std::move(*arr), i); // increments i + } + + return *this; + } + + TOML_EXTERNAL_LINKAGE + array& array::prune(bool recursive)& noexcept { + if (elems_.empty()) return *this; + + for (size_t i = elems_.size(); i-- > 0u;) { + if (auto arr = elems_[i]->as_array()) { + if (recursive) arr->prune(true); + if (arr->empty()) elems_.erase(elems_.cbegin() + static_cast(i)); + } else if (auto tbl = elems_[i]->as_table()) { + if (recursive) tbl->prune(true); + if (tbl->empty()) elems_.erase(elems_.cbegin() + static_cast(i)); + } + } + + return *this; + } + + TOML_EXTERNAL_LINKAGE + void array::pop_back() noexcept { + elems_.pop_back(); + } + + TOML_EXTERNAL_LINKAGE + void array::clear() noexcept { + elems_.clear(); + } + + TOML_EXTERNAL_LINKAGE + bool TOML_CALLCONV array::equal(const array& lhs, const array& rhs) noexcept { + if (&lhs == &rhs) return true; + if (lhs.elems_.size() != rhs.elems_.size()) return false; + for (size_t i = 0, e = lhs.elems_.size(); i < e; i++) { + const auto lhs_type = lhs.elems_[i]->type(); + const node& rhs_ = *rhs.elems_[i]; + const auto rhs_type = rhs_.type(); + if (lhs_type != rhs_type) return false; + + const bool equal = lhs.elems_[i]->visit([&](const auto& lhs_) noexcept { + return lhs_ == *reinterpret_cast*>(&rhs_); + }); + if (!equal) return false; + } + return true; + } } TOML_NAMESPACE_END; diff --git a/vendor/toml++/impl/at_path.inl b/vendor/toml++/impl/at_path.inl index 976c813..a90adea 100644 --- a/vendor/toml++/impl/at_path.inl +++ b/vendor/toml++/impl/at_path.inl @@ -1,18 +1,18 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard +// # 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 -//# }} +// # }} -#include "at_path.hpp" #include "array.hpp" +#include "at_path.hpp" #include "table.hpp" TOML_DISABLE_WARNINGS; #if TOML_INT_CHARCONV @@ -23,267 +23,229 @@ TOML_DISABLE_WARNINGS; TOML_ENABLE_WARNINGS; #include "header_start.hpp" -TOML_IMPL_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - bool TOML_CALLCONV parse_path(const std::string_view path, - void* const data, - const parse_path_callback on_key, - const parse_path_callback on_index) - { - // a blank string is a valid path; it's just one component representing the "" key - if (path.empty()) - return on_key(data, ""sv); - - size_t pos = 0; - const auto end = path.length(); - bool prev_was_array_indexer = false; - bool prev_was_dot = true; // invisible root 'dot' - - while (pos < end) - { - // start of an array indexer - if (path[pos] == '[') - { - // find first digit in index - size_t index_start = pos + 1u; - while (true) - { - if TOML_UNLIKELY(index_start >= path.length()) - return false; - - const auto c = path[index_start]; - if TOML_LIKELY(c >= '0' && c <= '9') - break; - else if (c == ' ' || c == '\t') - index_start++; - else - return false; - } - TOML_ASSERT(path[index_start] >= '0'); - TOML_ASSERT(path[index_start] <= '9'); - - // find end of index (first non-digit character) - size_t index_end = index_start + 1u; - while (true) - { - // if an array indexer is missing the trailing ']' at the end of the string, permissively accept it - if TOML_UNLIKELY(index_end >= path.length()) - break; - - const auto c = path[index_end]; - if (c >= '0' && c <= '9') - index_end++; - else if (c == ']' || c == ' ' || c == '\t' || c == '.' || c == '[') - break; - else - return false; - } - TOML_ASSERT(path[index_end - 1u] >= '0'); - TOML_ASSERT(path[index_end - 1u] <= '9'); - - // move pos to after indexer (char after closing ']' or permissively EOL/subkey '.'/next opening '[') - pos = index_end; - while (true) - { - if TOML_UNLIKELY(pos >= path.length()) - break; - - const auto c = path[pos]; - if (c == ']') - { - pos++; - break; - } - else if TOML_UNLIKELY(c == '.' || c == '[') - break; - else if (c == '\t' || c == ' ') - pos++; - else - return false; - } - - // get array index substring - auto index_str = path.substr(index_start, index_end - index_start); - - // parse the actual array index to an integer type - size_t index; - if (index_str.length() == 1u) - index = static_cast(index_str[0] - '0'); - else - { +TOML_IMPL_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + bool TOML_CALLCONV parse_path(const std::string_view path, void* const data, + const parse_path_callback on_key, + const parse_path_callback on_index) { + // a blank string is a valid path; it's just one component representing the "" key + if (path.empty()) return on_key(data, ""sv); + + size_t pos = 0; + const auto end = path.length(); + bool prev_was_array_indexer = false; + bool prev_was_dot = true; // invisible root 'dot' + + while (pos < end) { + // start of an array indexer + if (path[pos] == '[') { + // find first digit in index + size_t index_start = pos + 1u; + while (true) { + if TOML_UNLIKELY (index_start >= path.length()) return false; + + const auto c = path[index_start]; + if TOML_LIKELY (c >= '0' && c <= '9') + break; + else if (c == ' ' || c == '\t') + index_start++; + else + return false; + } + TOML_ASSERT(path[index_start] >= '0'); + TOML_ASSERT(path[index_start] <= '9'); + + // find end of index (first non-digit character) + size_t index_end = index_start + 1u; + while (true) { + // if an array indexer is missing the trailing ']' at the end of the string, permissively + // accept it + if TOML_UNLIKELY (index_end >= path.length()) break; + + const auto c = path[index_end]; + if (c >= '0' && c <= '9') + index_end++; + else if (c == ']' || c == ' ' || c == '\t' || c == '.' || c == '[') + break; + else + return false; + } + TOML_ASSERT(path[index_end - 1u] >= '0'); + TOML_ASSERT(path[index_end - 1u] <= '9'); + + // move pos to after indexer (char after closing ']' or permissively EOL/subkey '.'/next + // opening '[') + pos = index_end; + while (true) { + if TOML_UNLIKELY (pos >= path.length()) break; + + const auto c = path[pos]; + if (c == ']') { + pos++; + break; + } else if TOML_UNLIKELY (c == '.' || c == '[') + break; + else if (c == '\t' || c == ' ') + pos++; + else + return false; + } + + // get array index substring + auto index_str = path.substr(index_start, index_end - index_start); + + // parse the actual array index to an integer type + size_t index; + if (index_str.length() == 1u) + index = static_cast(index_str[0] - '0'); + else { #if TOML_INT_CHARCONV - auto fc_result = std::from_chars(index_str.data(), index_str.data() + index_str.length(), index); - if (fc_result.ec != std::errc{}) - return false; + auto fc_result = + std::from_chars(index_str.data(), index_str.data() + index_str.length(), index); + if (fc_result.ec != std::errc{}) return false; #else - std::stringstream ss; - ss.imbue(std::locale::classic()); - ss.write(index_str.data(), static_cast(index_str.length())); - if (!(ss >> index)) - return false; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss.write(index_str.data(), static_cast(index_str.length())); + if (!(ss >> index)) return false; #endif - } - - prev_was_dot = false; - prev_was_array_indexer = true; - - if (!on_index(data, index)) - return false; - } - - // start of a new table child - else if (path[pos] == '.') - { - // a dot immediately following another dot (or at the beginning of the string) is as if we'd asked - // for an empty child in between, e.g. - // - // foo..bar - // - // is equivalent to - // - // "foo".""."bar" - // - if (prev_was_dot && !on_key(data, ""sv)) - return false; - - pos++; - prev_was_dot = true; - prev_was_array_indexer = false; - } - - // an errant closing ']' - else if TOML_UNLIKELY(path[pos] == ']') - return false; - - // some regular subkey - else - { - const auto subkey_start = pos; - const auto subkey_len = - impl::min(path.find_first_of(".[]"sv, subkey_start + 1u), path.length()) - subkey_start; - const auto subkey = path.substr(subkey_start, subkey_len); - - // a regular subkey segment immediately after an array indexer is OK if it was all whitespace, e.g.: - // - // "foo[0] .bar" - // ^^ skip this - // - // otherwise its an error (since it would have to be preceeded by a dot) - if (prev_was_array_indexer) - { - auto non_ws = subkey.find_first_not_of(" \t"); - if (non_ws == std::string_view::npos) - { - pos += subkey_len; - prev_was_dot = false; - prev_was_array_indexer = false; - continue; - } - else - return false; - } - - pos += subkey_len; - prev_was_dot = false; - prev_was_array_indexer = false; - - if (!on_key(data, subkey)) - return false; - } - } - - // Last character was a '.', which implies an empty string key at the end of the path - if (prev_was_dot && !on_key(data, ""sv)) - return false; - - return true; - } + } + + prev_was_dot = false; + prev_was_array_indexer = true; + + if (!on_index(data, index)) return false; + } + + // start of a new table child + else if (path[pos] == '.') { + // a dot immediately following another dot (or at the beginning of the string) is as if we'd + // asked for an empty child in between, e.g. + // + // foo..bar + // + // is equivalent to + // + // "foo".""."bar" + // + if (prev_was_dot && !on_key(data, ""sv)) return false; + + pos++; + prev_was_dot = true; + prev_was_array_indexer = false; + } + + // an errant closing ']' + else if TOML_UNLIKELY (path[pos] == ']') + return false; + + // some regular subkey + else { + const auto subkey_start = pos; + const auto subkey_len = + impl::min(path.find_first_of(".[]"sv, subkey_start + 1u), path.length()) - subkey_start; + const auto subkey = path.substr(subkey_start, subkey_len); + + // a regular subkey segment immediately after an array indexer is OK if it was all + // whitespace, e.g.: + // + // "foo[0] .bar" + // ^^ skip this + // + // otherwise its an error (since it would have to be preceeded by a dot) + if (prev_was_array_indexer) { + auto non_ws = subkey.find_first_not_of(" \t"); + if (non_ws == std::string_view::npos) { + pos += subkey_len; + prev_was_dot = false; + prev_was_array_indexer = false; + continue; + } else + return false; + } + + pos += subkey_len; + prev_was_dot = false; + prev_was_array_indexer = false; + + if (!on_key(data, subkey)) return false; + } + } + + // Last character was a '.', which implies an empty string key at the end of the path + if (prev_was_dot && !on_key(data, ""sv)) return false; + + return true; + } } TOML_IMPL_NAMESPACE_END; -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - node_view TOML_CALLCONV at_path(node & root, std::string_view path) noexcept - { - // early-exit sanity-checks - if (root.is_value()) - return {}; - if (auto tbl = root.as_table(); tbl && tbl->empty()) - return {}; - if (auto arr = root.as_array(); arr && arr->empty()) - return {}; - - node* current = &root; - - static constexpr auto on_key = [](void* data, std::string_view key) noexcept -> bool - { - auto& curr = *static_cast(data); - TOML_ASSERT_ASSUME(curr); - - const auto current_table = curr->as(); - if (!current_table) - return false; - - curr = current_table->get(key); - return curr != nullptr; - }; - - static constexpr auto on_index = [](void* data, size_t index) noexcept -> bool - { - auto& curr = *static_cast(data); - TOML_ASSERT_ASSUME(curr); - - const auto current_array = curr->as(); - if (!current_array) - return false; - - curr = current_array->get(index); - return curr != nullptr; - }; - - if (!impl::parse_path(path, ¤t, on_key, on_index)) - current = nullptr; - - return node_view{ current }; - } - - TOML_EXTERNAL_LINKAGE - node_view TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept - { - return node_view{ at_path(const_cast(root), path).node() }; - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + node_view TOML_CALLCONV at_path(node & root, std::string_view path) noexcept { + // early-exit sanity-checks + if (root.is_value()) return {}; + if (auto tbl = root.as_table(); tbl && tbl->empty()) return {}; + if (auto arr = root.as_array(); arr && arr->empty()) return {}; + + node* current = &root; + + static constexpr auto on_key = [](void* data, std::string_view key) noexcept -> bool { + auto& curr = *static_cast(data); + TOML_ASSERT_ASSUME(curr); + + const auto current_table = curr->as
(); + if (!current_table) return false; + + curr = current_table->get(key); + return curr != nullptr; + }; + + static constexpr auto on_index = [](void* data, size_t index) noexcept -> bool { + auto& curr = *static_cast(data); + TOML_ASSERT_ASSUME(curr); + + const auto current_array = curr->as(); + if (!current_array) return false; + + curr = current_array->get(index); + return curr != nullptr; + }; + + if (!impl::parse_path(path, ¤t, on_key, on_index)) current = nullptr; + + return node_view{current}; + } + + TOML_EXTERNAL_LINKAGE + node_view TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept { + return node_view{at_path(const_cast(root), path).node()}; + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - node_view TOML_CALLCONV at_path(node & root, std::wstring_view path) - { - // these are the same top-level checks from the narrow-string version; - // they're hoisted up here to avoid doing the wide -> narrow conversion where it would not be necessary - // (avoids an allocation) - if (root.is_value()) - return {}; - if (auto tbl = root.as_table(); tbl && tbl->empty()) - return {}; - if (auto arr = root.as_array(); arr && arr->empty()) - return {}; - - return at_path(root, impl::narrow(path)); - } - - TOML_EXTERNAL_LINKAGE - node_view TOML_CALLCONV at_path(const node& root, std::wstring_view path) - { - return node_view{ at_path(const_cast(root), path).node() }; - } - -#endif // TOML_ENABLE_WINDOWS_COMPAT + TOML_EXTERNAL_LINKAGE + node_view TOML_CALLCONV at_path(node & root, std::wstring_view path) { + // these are the same top-level checks from the narrow-string version; + // they're hoisted up here to avoid doing the wide -> narrow conversion where it would not be + // necessary (avoids an allocation) + if (root.is_value()) return {}; + if (auto tbl = root.as_table(); tbl && tbl->empty()) return {}; + if (auto arr = root.as_array(); arr && arr->empty()) return {}; + + return at_path(root, impl::narrow(path)); + } + + TOML_EXTERNAL_LINKAGE + node_view TOML_CALLCONV at_path(const node& root, std::wstring_view path) { + return node_view{at_path(const_cast(root), path).node()}; + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT } TOML_NAMESPACE_END; diff --git a/vendor/toml++/impl/formatter.inl b/vendor/toml++/impl/formatter.inl index f429d32..937ba47 100644 --- a/vendor/toml++/impl/formatter.inl +++ b/vendor/toml++/impl/formatter.inl @@ -1,523 +1,503 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard +// # 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 "array.hpp" #include "formatter.hpp" +#include "header_start.hpp" +#include "parse_result.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" +#include "value.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 // +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 }, + : source_{source_pr && *source_pr ? &source_pr->table() : source_node}, + result_{source_pr}, #else - : source_{ source_pr ? source_pr : source_node }, + : 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(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(*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(decoder.codepoint)]); - - // escaped unicode characters - else if (decoder.codepoint > U'\x7F' - && (!unicode_allowed || is_non_ascii_vertical_whitespace(decoder.codepoint))) - { - if (static_cast(decoder.codepoint) > 0xFFFFu) - { - print_to_stream(*stream_, R"(\U)"sv); - print_to_stream(*stream_, - static_cast(decoder.codepoint), - value_flags::format_as_hexadecimal, - 8); - } - else - { - print_to_stream(*stream_, R"(\u)"sv); - print_to_stream(*stream_, - static_cast(decoder.codepoint), - value_flags::format_as_hexadecimal, - 4); - } - } - - // regular characters - else - print_to_stream(*stream_, cp_start, static_cast(cp_end - cp_start)); - } - } - - cp_start = cp_end; - } - } - - print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value& val) - { - print_string(val.get()); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value& 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& 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& val) - { - print_unformatted(*val ? constants_->bool_true : constants_->bool_false); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value& 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
(); - } - else - { - if (!is_arr && p.type() == node_type::table) - set_error_and_return_default("cannot redefine existing table '"sv, - to_sv(recording_buffer), - "'"sv); - else - set_error_and_return_default("cannot redefine existing "sv, - to_sv(p.type()), - " '"sv, - to_sv(recording_buffer), - "' as "sv, - is_arr ? "array-of-tables"sv : "table"sv); - } - } - - // need to create a new implicit table - else - { - pit = parent->emplace_hint
(pit, make_key(i)); - table& p = pit->second.ref_cast
(); - p.source_ = { header_begin_pos, header_end_pos, reader.source_path() }; - - implicit_tables.push_back(&p); - parent = &p; - } - } - - const auto last_segment = key_buffer.back(); - auto it = parent->lower_bound(last_segment); - - // if there was already a matching node some sanity checking is necessary; - // this is ok if we're making an array and the existing element is already an array (new element) - // or if we're making a table and the existing element is an implicitly-created table (promote it), - // otherwise this is a redefinition error. - if (it != parent->end() && it->first == last_segment) - { - node& matching_node = it->second; - if (auto arr = matching_node.as_array(); - is_arr && arr && impl::find(table_arrays.begin(), table_arrays.end(), arr)) - { - table& tbl = arr->emplace_back
(); - tbl.source_ = { header_begin_pos, header_end_pos, reader.source_path() }; - return &tbl; - } - - else if (auto tbl = matching_node.as_table(); !is_arr && tbl && !implicit_tables.empty()) - { - if (auto found = impl::find(implicit_tables.begin(), implicit_tables.end(), tbl); found) - { - bool ok = true; - if (!tbl->empty()) - { - for (auto& [_, child] : *tbl) - { - if (!child.is_table() && !child.is_array_of_tables()) - { - ok = false; - break; - } - } - } - - if (ok) - { - implicit_tables.erase(implicit_tables.cbegin() + (found - implicit_tables.data())); - tbl->source_.begin = header_begin_pos; - tbl->source_.end = header_end_pos; - return tbl; - } - } - } - - // if we get here it's a redefinition error. - if (!is_arr && matching_node.type() == node_type::table) - { - set_error_at(header_begin_pos, - "cannot redefine existing table '"sv, - to_sv(recording_buffer), - "'"sv); - return_after_error({}); - } - else - { - set_error_at(header_begin_pos, - "cannot redefine existing "sv, - to_sv(matching_node.type()), - " '"sv, - to_sv(recording_buffer), - "' as "sv, - is_arr ? "array-of-tables"sv : "table"sv); - return_after_error({}); - } - } - - // there was no matching node, sweet - we can freely instantiate a new table/table array. - else - { - auto last_key = make_key(key_buffer.size() - 1u); - - // if it's an array we need to make the array and it's first table element, - // set the starting regions, and return the table element - if (is_arr) - { - it = parent->emplace_hint(it, std::move(last_key)); - array& tbl_arr = it->second.ref_cast(); - table_arrays.push_back(&tbl_arr); - tbl_arr.source_ = { header_begin_pos, header_end_pos, reader.source_path() }; - - table& tbl = tbl_arr.emplace_back
(); - tbl.source_ = { header_begin_pos, header_end_pos, reader.source_path() }; - return &tbl; - } - - // otherwise we're just making a table - else - { - it = parent->emplace_hint
(it, std::move(last_key)); - table& tbl = it->second.ref_cast
(); - tbl.source_ = { header_begin_pos, header_end_pos, reader.source_path() }; - return &tbl; - } - } - } - - TOML_NEVER_INLINE - bool parse_key_value_pair_and_insert(table* tbl) - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(is_string_delimiter(*cp) || is_bare_key_character(*cp)); - push_parse_scope("key-value pair"sv); - - // read the key into the key buffer - start_recording(); - parse_key(); - stop_recording(1u); - return_if_error({}); - TOML_ASSERT(key_buffer.size() >= 1u); - - // skip past any whitespace that followed the key - consume_leading_whitespace(); - set_error_and_return_if_eof({}); - - // '=' - if (*cp != U'=') - set_error_and_return_default("expected '=', saw '"sv, to_sv(*cp), "'"sv); - advance_and_return_if_error_or_eof({}); - - // skip past any whitespace that followed the '=' - consume_leading_whitespace(); - return_if_error({}); - set_error_and_return_if_eof({}); - - // check that the next character could actually be a value - if (is_value_terminator(*cp)) - set_error_and_return_default("expected value, saw '"sv, to_sv(*cp), "'"sv); - - // if it's a dotted kvp we need to spawn the parent sub-tables if necessary, - // and set the target table to the second-to-last one in the chain - if (key_buffer.size() > 1u) - { - for (size_t i = 0; i < key_buffer.size() - 1u; i++) - { - const std::string_view segment = key_buffer[i]; - auto pit = tbl->lower_bound(segment); - - // parent already existed - if (pit != tbl->end() && pit->first == segment) - { - table* p = pit->second.as_table(); - - // redefinition - if TOML_UNLIKELY(!p - || !(impl::find(dotted_key_tables.begin(), dotted_key_tables.end(), p) - || impl::find(implicit_tables.begin(), implicit_tables.end(), p))) - { - set_error_at(key_buffer.starts[i], - "cannot redefine existing "sv, - to_sv(pit->second.type()), - " as dotted key-value pair"sv); - return_after_error({}); - } - - tbl = p; - } - - // need to create a new implicit table - else - { - pit = tbl->emplace_hint
(pit, make_key(i)); - table& p = pit->second.ref_cast
(); - p.source_ = pit->first.source(); - - dotted_key_tables.push_back(&p); - tbl = &p; - } - } - } - - // ensure this isn't a redefinition - const std::string_view last_segment = key_buffer.back(); - auto it = tbl->lower_bound(last_segment); - if (it != tbl->end() && it->first == last_segment) - { - set_error("cannot redefine existing "sv, - to_sv(it->second.type()), - " '"sv, - to_sv(recording_buffer), - "'"sv); - return_after_error({}); - } - - // create the key first since the key buffer will likely get overwritten during value parsing (inline - // tables) - auto last_key = make_key(key_buffer.size() - 1u); - - // now we can actually parse the value - node_ptr val = parse_value(); - return_if_error({}); - - tbl->emplace_hint(it, std::move(last_key), std::move(val)); - return true; - } - - void parse_document() - { - assert_not_error(); - assert_not_eof(); - push_parse_scope("root table"sv); - - table* current_table = &root; - - do - { - return_if_error(); - - // leading whitespace, line endings, comments - if (consume_leading_whitespace() || consume_line_break() || consume_comment()) - continue; - return_if_error(); - - // [tables] - // [[table array]] - if (*cp == U'[') - current_table = parse_table_header(); - - // bare_keys - // dotted.keys - // "quoted keys" - else if (is_bare_key_character(*cp) || is_string_delimiter(*cp)) - { - push_parse_scope("key-value pair"sv); - - parse_key_value_pair_and_insert(current_table); - - // handle the rest of the line after the kvp - // (this is not done in parse_key_value_pair() because that is also used for inline tables) - consume_leading_whitespace(); - return_if_error(); - if (!is_eof() && !consume_comment() && !consume_line_break()) - set_error("expected a comment or whitespace, saw '"sv, to_sv(cp), "'"sv); - } - - else // ?? - set_error("expected keys, tables, whitespace or comments, saw '"sv, to_sv(cp), "'"sv); - } - while (!is_eof()); - - auto eof_pos = current_position(1); - root.source_.end = eof_pos; - if (current_table && current_table != &root && current_table->source_.end <= current_table->source_.begin) - current_table->source_.end = eof_pos; - } - - static void update_region_ends(node& nde) noexcept - { - const auto type = nde.type(); - if (type > node_type::array) - return; - - if (type == node_type::table) - { - auto& tbl = nde.ref_cast
(); - if (tbl.is_inline()) // inline tables (and all their inline descendants) are already correctly - // terminated - return; - - auto end = nde.source_.end; - for (auto&& [k, v] : tbl) - { - TOML_UNUSED(k); - update_region_ends(v); - if (end < v.source_.end) - end = v.source_.end; - } - } - else // arrays - { - auto& arr = nde.ref_cast(); - auto end = nde.source_.end; - for (auto&& v : arr) - { - update_region_ends(v); - if (end < v.source_.end) - end = v.source_.end; - } - nde.source_.end = end; - } - } - - public: - parser(utf8_reader_interface&& reader_) // - : reader{ reader_ } - { - root.source_ = { prev_pos, prev_pos, reader.source_path() }; - - if (!reader.peek_eof()) - { - cp = reader.read_next(); + } + + TOML_NODISCARD + TOML_NEVER_INLINE + double parse_hex_float() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_match(*cp, U'0', U'+', U'-')); + push_parse_scope("hexadecimal floating-point"sv); + +#if TOML_LANG_UNRELEASED // toml/issues/562 (hexfloats) + + // sign + const int sign = *cp == U'-' ? -1 : 1; + if (is_match(*cp, U'+', U'-')) advance_and_return_if_error_or_eof({}); + + // '0' + if (*cp != U'0') set_error_and_return_default(" expected '0', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // 'x' or 'X' + if (!is_match(*cp, U'x', U'X')) + set_error_and_return_default("expected 'x' or 'X', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // ([.])? [pP] [+-]? + + // consume value fragments + struct fragment { + char chars[24]; + size_t length; + double value; + }; + fragment fragments[] = { + {}, // mantissa, whole part + {}, // mantissa, fractional part + {} // exponent + }; + fragment* current_fragment = fragments; + const utf8_codepoint* prev = {}; + int exponent_sign = 1; + while (!is_eof() && !is_value_terminator(*cp)) { + if (*cp == U'_') { + if (!prev || !is_hexadecimal_digit(*prev)) + set_error_and_return_default("underscores may only follow digits"sv); + + prev = cp; + advance_and_return_if_error_or_eof({}); + continue; + } else if (prev && *prev == U'_' && !is_hexadecimal_digit(*cp)) + set_error_and_return_default("underscores must be followed by digits"sv); + else if (*cp == U'.') { + // 0x10.0p-.0 (exponent cannot have '.') + if (current_fragment == fragments + 2) + set_error_and_return_default("expected exponent digit or sign, saw '.'"sv); + + // 0x10.0.p-0 (multiple '.') + else if (current_fragment == fragments + 1) + set_error_and_return_default("expected hexadecimal digit or exponent, saw '.'"sv); + + else + current_fragment++; + } else if (is_match(*cp, U'p', U'P')) { + // 0x10.0pp-0 (multiple 'p') + if (current_fragment == fragments + 2) + set_error_and_return_default("expected exponent digit or sign, saw '"sv, to_sv(*cp), + "'"sv); + + // 0x.p-0 (mantissa is just '.') + else if (fragments[0].length == 0u && fragments[1].length == 0u) + set_error_and_return_default("expected hexadecimal digit, saw '"sv, to_sv(*cp), "'"sv); + + else + current_fragment = fragments + 2; + } else if (is_match(*cp, U'+', U'-')) { + // 0x-10.0p-0 (sign in mantissa) + if (current_fragment != fragments + 2) + set_error_and_return_default("expected hexadecimal digit or '.', saw '"sv, to_sv(*cp), + "'"sv); + + // 0x10.0p0- (misplaced exponent sign) + else if (!is_match(*prev, U'p', U'P')) + set_error_and_return_default("expected exponent digit, saw '"sv, to_sv(*cp), "'"sv); + + else + exponent_sign = *cp == U'-' ? -1 : 1; + } else if (current_fragment < fragments + 2 && !is_hexadecimal_digit(*cp)) + set_error_and_return_default("expected hexadecimal digit or '.', saw '"sv, to_sv(*cp), + "'"sv); + else if (current_fragment == fragments + 2 && !is_decimal_digit(*cp)) + set_error_and_return_default("expected exponent digit or sign, saw '"sv, to_sv(*cp), + "'"sv); + else if (current_fragment->length == sizeof(fragment::chars)) + set_error_and_return_default("fragment exceeeds maximum length of "sv, + sizeof(fragment::chars), " characters"sv); + else + current_fragment->chars[current_fragment->length++] = static_cast(cp->bytes[0]); + + prev = cp; + advance_and_return_if_error({}); + } + + // sanity-check ending state + if (current_fragment != fragments + 2 || current_fragment->length == 0u) { + set_error_and_return_if_eof({}); + set_error_and_return_default("missing exponent"sv); + } else if (prev && *prev == U'_') { + set_error_and_return_if_eof({}); + set_error_and_return_default("underscores must be followed by digits"sv); + } + + // calculate values for the three fragments + for (int fragment_idx = 0; fragment_idx < 3; fragment_idx++) { + auto& f = fragments[fragment_idx]; + const uint32_t base = fragment_idx == 2 ? 10u : 16u; + + // left-trim zeroes + const char* c = f.chars; + size_t sig = {}; + while (f.length && *c == '0') { + f.length--; + c++; + sig++; + } + if (!f.length) continue; + + // calculate value + auto place = 1u; + for (size_t i = 0; i < f.length - 1u; i++) place *= base; + uint32_t val{}; + while (place) { + if (base == 16) + val += place * hex_to_dec(*c); + else + val += place * static_cast(*c - '0'); + if (fragment_idx == 1) sig++; + c++; + place /= base; + } + f.value = static_cast(val); + + // shift the fractional part + if (fragment_idx == 1) { + while (sig--) f.value /= base; + } + } + + return (fragments[0].value + fragments[1].value) * + pow(2.0, fragments[2].value * exponent_sign) * sign; + +#else // !TOML_LANG_UNRELEASED + + set_error_and_return_default( + "hexadecimal floating-point values are not supported " + "in TOML 1.0.0 and earlier"sv); + +#endif // !TOML_LANG_UNRELEASED + } + + template + TOML_NODISCARD TOML_NEVER_INLINE int64_t parse_integer() { + return_if_error({}); + assert_not_eof(); + using traits = parse_integer_traits; + push_parse_scope(traits::scope_qualifier); + + [[maybe_unused]] int64_t sign = 1; + if constexpr (traits::is_signed) { + sign = *cp == U'-' ? -1 : 1; + if (is_match(*cp, U'+', U'-')) advance_and_return_if_error_or_eof({}); + } + + if constexpr (base == 10) { + if (!traits::is_digit(*cp)) + set_error_and_return_default("expected expected digit or sign, saw '"sv, to_sv(*cp), + "'"sv); + } else { + // '0' + if (*cp != U'0') set_error_and_return_default("expected '0', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // 'b', 'o', 'x' + if (*cp != traits::prefix_codepoint) + set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), + "'"sv); + advance_and_return_if_error_or_eof({}); + + if (!traits::is_digit(*cp)) + set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv); + } + + // consume digits + char digits[utf8_buffered_reader::max_history_length]; + size_t length = {}; + const utf8_codepoint* prev = {}; + while (!is_eof() && !is_value_terminator(*cp)) { + if (*cp == U'_') { + if (!prev || !traits::is_digit(*prev)) + set_error_and_return_default("underscores may only follow digits"sv); + + prev = cp; + advance_and_return_if_error_or_eof({}); + continue; + } else if TOML_UNLIKELY (prev && *prev == U'_' && !traits::is_digit(*cp)) + set_error_and_return_default("underscores must be followed by digits"sv); + else if TOML_UNLIKELY (!traits::is_digit(*cp)) + set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv); + else if TOML_UNLIKELY (length == sizeof(digits)) + set_error_and_return_default("exceeds length limit of "sv, sizeof(digits), " digits"sv); + else + digits[length++] = static_cast(cp->bytes[0]); + + prev = cp; + advance_and_return_if_error({}); + } + + // sanity check ending state + if (prev && *prev == U'_') { + set_error_and_return_if_eof({}); + set_error_and_return_default("underscores must be followed by digits"sv); + } + + // single digits can be converted trivially + if (length == 1u) { + int64_t result; + + if constexpr (base == 16) + result = static_cast(hex_to_dec(digits[0])); + else + result = static_cast(digits[0] - '0'); + + if constexpr (traits::is_signed) result *= sign; + + return result; + } + + // bin, oct and hex allow leading zeroes so trim them first + const char* end = digits + length; + const char* msd = digits; + if constexpr (base != 10) { + while (msd < end && *msd == '0') msd++; + if (msd == end) return 0ll; + } + + // decimal integers do not allow leading zeroes + else { + if TOML_UNLIKELY (digits[0] == '0') + set_error_and_return_default("leading zeroes are prohibited"sv); + } + + // range check + if TOML_UNLIKELY (static_cast(end - msd) > traits::max_digits) + set_error_and_return_default("'"sv, traits::full_prefix, std::string_view{digits, length}, + "' is not representable in 64 bits"sv); + + // do the thing + { + uint64_t result = {}; + { + uint64_t power = 1; + while (--end >= msd) { + if constexpr (base == 16) + result += power * hex_to_dec(*end); + else + result += power * static_cast(*end - '0'); + + power *= base; + } + } + + // range check + static constexpr auto i64_max = + static_cast((std::numeric_limits::max)()); + if TOML_UNLIKELY (result > i64_max + (sign < 0 ? 1u : 0u)) + set_error_and_return_default("'"sv, traits::full_prefix, std::string_view{digits, length}, + "' is not representable in 64 bits"sv); + + if constexpr (traits::is_signed) { + // avoid signed multiply UB when parsing INT64_MIN + if TOML_UNLIKELY (sign < 0 && result == i64_max + 1u) + return (std::numeric_limits::min)(); + + return static_cast(result) * sign; + } else + return static_cast(result); + } + } + + TOML_NODISCARD + TOML_NEVER_INLINE + date parse_date(bool part_of_datetime = false) { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_decimal_digit(*cp)); + push_parse_scope("date"sv); + + // "YYYY" + uint32_t digits[4]; + if (!consume_digit_sequence(digits, 4u)) + set_error_and_return_default("expected 4-digit year, saw '"sv, to_sv(cp), "'"sv); + const auto year = digits[3] + digits[2] * 10u + digits[1] * 100u + digits[0] * 1000u; + const auto is_leap_year = (year % 4u == 0u) && ((year % 100u != 0u) || (year % 400u == 0u)); + set_error_and_return_if_eof({}); + + // '-' + if (*cp != U'-') set_error_and_return_default("expected '-', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "MM" + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit month, saw '"sv, to_sv(cp), "'"sv); + const auto month = digits[1] + digits[0] * 10u; + if (month == 0u || month > 12u) + set_error_and_return_default("expected month between 1 and 12 (inclusive), saw "sv, month); + const auto max_days_in_month = + month == 2u ? (is_leap_year ? 29u : 28u) + : (month == 4u || month == 6u || month == 9u || month == 11u ? 30u : 31u); + set_error_and_return_if_eof({}); + + // '-' + if (*cp != U'-') set_error_and_return_default("expected '-', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "DD" + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit day, saw '"sv, to_sv(cp), "'"sv); + const auto day = digits[1] + digits[0] * 10u; + if (day == 0u || day > max_days_in_month) + set_error_and_return_default("expected day between 1 and "sv, max_days_in_month, + " (inclusive), saw "sv, day); + + if (!part_of_datetime && !is_eof() && !is_value_terminator(*cp)) + set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); + + return {year, month, day}; + } + + TOML_NODISCARD + TOML_NEVER_INLINE + time parse_time(bool part_of_datetime = false) { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_decimal_digit(*cp)); + push_parse_scope("time"sv); + + static constexpr size_t max_digits = 64; // far more than necessary but needed to allow + // fractional millisecond truncation per the spec + uint32_t digits[max_digits]; + + // "HH" + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit hour, saw '"sv, to_sv(cp), "'"sv); + const auto hour = digits[1] + digits[0] * 10u; + if (hour > 23u) + set_error_and_return_default("expected hour between 0 to 59 (inclusive), saw "sv, hour); + set_error_and_return_if_eof({}); + + // ':' + if (*cp != U':') set_error_and_return_default("expected ':', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "MM" + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit minute, saw '"sv, to_sv(cp), "'"sv); + const auto minute = digits[1] + digits[0] * 10u; + if (minute > 59u) + set_error_and_return_default("expected minute between 0 and 59 (inclusive), saw "sv, + minute); + auto time = toml::time{hour, minute}; + + // ':' + if constexpr (TOML_LANG_UNRELEASED) // toml/issues/671 (allow omission of seconds) + { + if (is_eof() || is_value_terminator(*cp) || + (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z'))) + return time; + } else + set_error_and_return_if_eof({}); + if (*cp != U':') set_error_and_return_default("expected ':', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "SS" + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit second, saw '"sv, to_sv(cp), "'"sv); + const auto second = digits[1] + digits[0] * 10u; + if (second > 59u) + set_error_and_return_default("expected second between 0 and 59 (inclusive), saw "sv, + second); + time.second = static_cast(second); + + // '.' (early-exiting is allowed; fractional is optional) + if (is_eof() || is_value_terminator(*cp) || + (part_of_datetime && is_match(*cp, U'+', U'-', U'Z', U'z'))) + return time; + if (*cp != U'.') set_error_and_return_default("expected '.', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "FFFFFFFFF" + size_t digit_count = consume_variable_length_digit_sequence(digits, max_digits); + if (!digit_count) { + set_error_and_return_if_eof({}); + set_error_and_return_default("expected fractional digits, saw '"sv, to_sv(*cp), "'"sv); + } else if (!is_eof()) { + if (digit_count == max_digits && is_decimal_digit(*cp)) + set_error_and_return_default("fractional component exceeds maximum precision of "sv, + max_digits); + else if (!part_of_datetime && !is_value_terminator(*cp)) + set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); + } + uint32_t value = 0u; + uint32_t place = 1u; + for (auto i = impl::min(digit_count, 9u); i-- > 0u;) { + value += digits[i] * place; + place *= 10u; + } + for (auto i = digit_count; i < 9u; i++) // implicit zeros + value *= 10u; + time.nanosecond = value; + return time; + } + + TOML_NODISCARD + TOML_NEVER_INLINE + date_time parse_date_time() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_decimal_digit(*cp)); + push_parse_scope("date-time"sv); + + // "YYYY-MM-DD" + auto date = parse_date(true); + set_error_and_return_if_eof({}); + + // ' ', 'T' or 't' + if (!is_match(*cp, U' ', U'T', U't')) + set_error_and_return_default("expected space, 'T' or 't', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "HH:MM:SS.FFFFFFFFF" + auto time = parse_time(true); + return_if_error({}); + + // no offset + if (is_eof() || is_value_terminator(*cp)) return {date, time}; + + // zero offset ('Z' or 'z') + time_offset offset{}; + if (is_match(*cp, U'Z', U'z')) advance_and_return_if_error({}); + + // explicit offset ("+/-HH:MM") + else if (is_match(*cp, U'+', U'-')) { + push_parse_scope("date-time offset"sv); + + // sign + int sign = *cp == U'-' ? -1 : 1; + advance_and_return_if_error_or_eof({}); + + // "HH" + int digits[2]; + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit hour, saw '"sv, to_sv(cp), "'"sv); + const auto hour = digits[1] + digits[0] * 10; + if (hour > 23) + set_error_and_return_default("expected hour between 0 and 23 (inclusive), saw "sv, hour); + set_error_and_return_if_eof({}); + + // ':' + if (*cp != U':') set_error_and_return_default("expected ':', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // "MM" + if (!consume_digit_sequence(digits, 2u)) + set_error_and_return_default("expected 2-digit minute, saw '"sv, to_sv(cp), "'"sv); + const auto minute = digits[1] + digits[0] * 10; + if (minute > 59) + set_error_and_return_default("expected minute between 0 and 59 (inclusive), saw "sv, + minute); + offset.minutes = static_cast((hour * 60 + minute) * sign); + } + + if (!is_eof() && !is_value_terminator(*cp)) + set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); + + return {date, time, offset}; + } + + TOML_NODISCARD + node_ptr parse_array(); + + TOML_NODISCARD + node_ptr parse_inline_table(); + + TOML_NODISCARD + node_ptr parse_value_known_prefixes() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(!is_control_character(*cp)); + TOML_ASSERT_ASSUME(*cp != U'_'); + + switch (cp->value) { + // arrays + case U'[': + return parse_array(); + + // inline tables + case U'{': + return parse_inline_table(); + + // floats beginning with '.' + case U'.': + return node_ptr{new value{parse_float()}}; + + // strings + case U'"': + [[fallthrough]]; + case U'\'': + return node_ptr{new value{parse_string().value}}; + + default: { + const auto cp_upper = static_cast(cp->value) & ~0x20u; + + // bools + if (cp_upper == 70u || cp_upper == 84u) // F or T + return node_ptr{new value{parse_boolean()}}; + + // inf/nan + else if (cp_upper == 73u || cp_upper == 78u) // I or N + return node_ptr{new value{parse_inf_or_nan()}}; + + else + return nullptr; + } + } + TOML_UNREACHABLE; + } + + TOML_NODISCARD + node_ptr parse_value() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(!is_value_terminator(*cp)); + push_parse_scope("value"sv); + + const depth_counter_scope depth_counter{nested_values}; + if TOML_UNLIKELY (nested_values > max_nested_values) + set_error_and_return_default("exceeded maximum nested value depth of "sv, max_nested_values, + " (TOML_MAX_NESTED_VALUES)"sv); + + // check if it begins with some control character + // (note that this will also fail for whitespace but we're assuming we've + // called consume_leading_whitespace() before calling parse_value()) + if TOML_UNLIKELY (is_control_character(*cp)) + set_error_and_return_default("unexpected control character"sv); + + // underscores at the beginning + else if (*cp == U'_') + set_error_and_return_default("values may not begin with underscores"sv); + + const auto begin_pos = cp->position; + node_ptr val; + + do { + TOML_ASSERT_ASSUME(!is_control_character(*cp)); + TOML_ASSERT_ASSUME(*cp != U'_'); + + // detect the value type and parse accordingly, + // starting with value types that can be detected + // unambiguously from just one character. + + val = parse_value_known_prefixes(); + return_if_error({}); + if (val) break; + + // value types from here down require more than one character to unambiguously identify + // so scan ahead and collect a set of value 'traits'. + enum TOML_CLOSED_FLAGS_ENUM value_traits : int { + has_nothing = 0, + has_digits = 1, + has_b = 1 << 1, // as second char only (0b) + has_e = 1 << 2, // only float exponents + has_o = 1 << 3, // as second char only (0o) + has_p = 1 << 4, // only hexfloat exponents + has_t = 1 << 5, + has_x = 1 << 6, // as second or third char only (0x, -0x, +0x) + has_z = 1 << 7, + has_colon = 1 << 8, + has_plus = 1 << 9, + has_minus = 1 << 10, + has_dot = 1 << 11, + begins_sign = 1 << 12, + begins_digit = 1 << 13, + begins_zero = 1 << 14, + + signs_msk = has_plus | has_minus, + bdigit_msk = has_digits | begins_digit, + bzero_msk = bdigit_msk | begins_zero, + }; + value_traits traits = has_nothing; + const auto has_any = [&](auto t) noexcept { return (traits & t) != has_nothing; }; + const auto has_none = [&](auto t) noexcept { return (traits & t) == has_nothing; }; + const auto add_trait = [&](auto t) noexcept { + traits = static_cast(traits | t); + }; + + // examine the first character to get the 'begins with' traits + // (good fail-fast opportunity; all the remaining types begin with numeric digits or signs) + if (is_decimal_digit(*cp)) { + add_trait(begins_digit); + if (*cp == U'0') add_trait(begins_zero); + } else if (is_match(*cp, U'+', U'-')) + add_trait(begins_sign); + else + break; + + // scan the rest of the value to determine the remaining traits + char32_t chars[utf8_buffered_reader::max_history_length]; + size_t char_count = {}, advance_count = {}; + bool eof_while_scanning = false; + const auto scan = [&]() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) { + if (is_eof()) return; + TOML_ASSERT_ASSUME(!is_value_terminator(*cp)); + + do { + if (const auto c = **cp; c != U'_') { + chars[char_count++] = c; + + if (is_decimal_digit(c)) + add_trait(has_digits); + else if (is_ascii_letter(c)) { + TOML_ASSERT_ASSUME((c >= U'a' && c <= U'z') || (c >= U'A' && c <= U'Z')); + switch (static_cast(c | 32u)) { + case U'b': + if (char_count == 2u && has_any(begins_zero)) add_trait(has_b); + break; + + case U'e': + if (char_count > 1u && + has_none(has_b | has_o | has_p | has_t | has_x | has_z | has_colon) && + (has_none(has_plus | has_minus) || has_any(begins_sign))) + add_trait(has_e); + break; + + case U'o': + if (char_count == 2u && has_any(begins_zero)) add_trait(has_o); + break; + + case U'p': + if (has_any(has_x)) add_trait(has_p); + break; + + case U'x': + if ((char_count == 2u && has_any(begins_zero)) || + (char_count == 3u && has_any(begins_sign) && chars[1] == U'0')) + add_trait(has_x); + break; + + case U't': + add_trait(has_t); + break; + case U'z': + add_trait(has_z); + break; + } + } else if (c <= U':') { + TOML_ASSERT_ASSUME(c < U'0' || c > U'9'); + switch (c) { + case U'+': + add_trait(has_plus); + break; + case U'-': + add_trait(has_minus); + break; + case U'.': + add_trait(has_dot); + break; + case U':': + add_trait(has_colon); + break; + } + } + } + + advance_and_return_if_error(); + advance_count++; + eof_while_scanning = is_eof(); + } while (advance_count < (utf8_buffered_reader::max_history_length - 1u) && !is_eof() && + !is_value_terminator(*cp)); + }; + scan(); + return_if_error({}); + + // force further scanning if this could have been a date-time with a space instead of a T + if (char_count == 10u // + && (traits | begins_zero) == (bzero_msk | has_minus) // + && chars[4] == U'-' // + && chars[7] == U'-' // + && !is_eof() // + && *cp == U' ') { + const auto pre_advance_count = advance_count; + const auto pre_scan_traits = traits; + chars[char_count++] = *cp; + add_trait(has_t); + + const auto backpedal = [&]() noexcept { + go_back(advance_count - pre_advance_count); + advance_count = pre_advance_count; + traits = pre_scan_traits; + char_count = 10u; + }; + + advance_and_return_if_error({}); + advance_count++; + + if (is_eof() || !is_decimal_digit(*cp)) + backpedal(); + else { + chars[char_count++] = *cp; + + advance_and_return_if_error({}); + advance_count++; + + scan(); + return_if_error({}); + + if (char_count == 12u) backpedal(); + } + } + + // set the reader back to where we started + go_back(advance_count); + + // if after scanning ahead we still only have one value character, + // the only valid value type is an integer. + if (char_count == 1u) { + if (has_any(begins_digit)) { + val.reset(new value{static_cast(chars[0] - U'0')}); + advance(); // skip the digit + break; + } + + // anything else would be ambiguous. + else + set_error_and_return_default(eof_while_scanning ? "encountered end-of-file"sv + : "could not determine value type"sv); + } + + // now things that can be identified from two or more characters + return_if_error({}); + TOML_ASSERT_ASSUME(char_count >= 2u); + + // do some 'fuzzy matching' where there's no ambiguity, since that allows the specific + // typed parse functions to take over and show better diagnostics if there's an issue + // (as opposed to the fallback "could not determine type" message) + if (has_any(has_p)) + val.reset(new value{parse_hex_float()}); + else if (has_any(has_x | has_o | has_b)) { + int64_t i; + value_flags flags; + if (has_any(has_x)) { + i = parse_integer<16>(); + flags = value_flags::format_as_hexadecimal; + } else if (has_any(has_o)) { + i = parse_integer<8>(); + flags = value_flags::format_as_octal; + } else // has_b + { + i = parse_integer<2>(); + flags = value_flags::format_as_binary; + } + return_if_error({}); + + val.reset(new value{i}); + val->ref_cast().flags(flags); + } else if (has_any(has_e) || (has_any(begins_digit) && chars[1] == U'.')) + val.reset(new value{parse_float()}); + else if (has_any(begins_sign)) { + // single-digit signed integers + if (char_count == 2u && has_any(has_digits)) { + val.reset( + new value{static_cast(chars[1] - U'0') * (chars[0] == U'-' ? -1LL : 1LL)}); + advance(); // skip the sign + advance(); // skip the digit + break; + } + + // simple signed floats (e.g. +1.0) + if (is_decimal_digit(chars[1]) && chars[2] == U'.') val.reset(new value{parse_float()}); + + // signed infinity or nan + else if (is_match(chars[1], U'i', U'n', U'I', U'N')) + val.reset(new value{parse_inf_or_nan()}); + } + + return_if_error({}); + if (val) break; + + // match trait masks against what they can match exclusively. + // all correct value parses will come out of this list, so doing this as a switch is likely + // to be a better friend to the optimizer on the success path (failure path can be slow but + // that doesn't matter much). + switch (unwrap_enum(traits)) { + // binary integers + // 0b10 + case bzero_msk | has_b: + val.reset(new value{parse_integer<2>()}); + val->ref_cast().flags(value_flags::format_as_binary); + break; + + // octal integers + // 0o10 + case bzero_msk | has_o: + val.reset(new value{parse_integer<8>()}); + val->ref_cast().flags(value_flags::format_as_octal); + break; + + // decimal integers + // 00 + // 10 + // +10 + // -10 + case bzero_msk: + [[fallthrough]]; + case bdigit_msk: + [[fallthrough]]; + case begins_sign | has_digits | has_minus: + [[fallthrough]]; + case begins_sign | has_digits | has_plus: { + // if the value was so long we exhausted the history buffer it's reasonable to assume + // there was more and the value's actual type is impossible to identify without making + // the buffer bigger (since it could have actually been a float), so emit an error. + // + // (this will likely only come up during fuzzing and similar scenarios) + static constexpr size_t max_numeric_value_length = + utf8_buffered_reader::max_history_length - 2u; + if TOML_UNLIKELY (!eof_while_scanning && advance_count > max_numeric_value_length) + set_error_and_return_default( + "numeric value too long to identify type - cannot exceed "sv, + max_numeric_value_length, " characters"sv); + + val.reset(new value{parse_integer<10>()}); + break; + } + + // hexadecimal integers + // 0x10 + case bzero_msk | has_x: + val.reset(new value{parse_integer<16>()}); + val->ref_cast().flags(value_flags::format_as_hexadecimal); + break; + + // decimal floats + // 0e1 + // 0e-1 + // 0e+1 + // 0.0 + // 0.0e1 + // 0.0e-1 + // 0.0e+1 + case bzero_msk | has_e: + [[fallthrough]]; + case bzero_msk | has_e | has_minus: + [[fallthrough]]; + case bzero_msk | has_e | has_plus: + [[fallthrough]]; + case bzero_msk | has_dot: + [[fallthrough]]; + case bzero_msk | has_dot | has_e: + [[fallthrough]]; + case bzero_msk | has_dot | has_e | has_minus: + [[fallthrough]]; + case bzero_msk | has_dot | has_e | has_plus: + [[fallthrough]]; + // 1e1 + // 1e-1 + // 1e+1 + // 1.0 + // 1.0e1 + // 1.0e-1 + // 1.0e+1 + case bdigit_msk | has_e: + [[fallthrough]]; + case bdigit_msk | has_e | has_minus: + [[fallthrough]]; + case bdigit_msk | has_e | has_plus: + [[fallthrough]]; + case bdigit_msk | has_dot: + [[fallthrough]]; + case bdigit_msk | has_dot | has_e: + [[fallthrough]]; + case bdigit_msk | has_dot | has_e | has_minus: + [[fallthrough]]; + case bdigit_msk | has_dot | has_e | has_plus: + [[fallthrough]]; + // +1e1 + // +1.0 + // +1.0e1 + // +1.0e+1 + // +1.0e-1 + // -1.0e+1 + case begins_sign | has_digits | has_e | has_plus: + [[fallthrough]]; + case begins_sign | has_digits | has_dot | has_plus: + [[fallthrough]]; + case begins_sign | has_digits | has_dot | has_e | has_plus: + [[fallthrough]]; + case begins_sign | has_digits | has_dot | has_e | signs_msk: + [[fallthrough]]; + // -1e1 + // -1e+1 + // +1e-1 + // -1.0 + // -1.0e1 + // -1.0e-1 + case begins_sign | has_digits | has_e | has_minus: + [[fallthrough]]; + case begins_sign | has_digits | has_e | signs_msk: + [[fallthrough]]; + case begins_sign | has_digits | has_dot | has_minus: + [[fallthrough]]; + case begins_sign | has_digits | has_dot | has_e | has_minus: + val.reset(new value{parse_float()}); + break; + + // hexadecimal floats + // 0x10p0 + // 0x10p-0 + // 0x10p+0 + case bzero_msk | has_x | has_p: + [[fallthrough]]; + case bzero_msk | has_x | has_p | has_minus: + [[fallthrough]]; + case bzero_msk | has_x | has_p | has_plus: + [[fallthrough]]; + // -0x10p0 + // -0x10p-0 + // +0x10p0 + // +0x10p+0 + // -0x10p+0 + // +0x10p-0 + case begins_sign | has_digits | has_x | has_p | has_minus: + [[fallthrough]]; + case begins_sign | has_digits | has_x | has_p | has_plus: + [[fallthrough]]; + case begins_sign | has_digits | has_x | has_p | signs_msk: + [[fallthrough]]; + // 0x10.1p0 + // 0x10.1p-0 + // 0x10.1p+0 + case bzero_msk | has_x | has_dot | has_p: + [[fallthrough]]; + case bzero_msk | has_x | has_dot | has_p | has_minus: + [[fallthrough]]; + case bzero_msk | has_x | has_dot | has_p | has_plus: + [[fallthrough]]; + // -0x10.1p0 + // -0x10.1p-0 + // +0x10.1p0 + // +0x10.1p+0 + // -0x10.1p+0 + // +0x10.1p-0 + case begins_sign | has_digits | has_x | has_dot | has_p | has_minus: + [[fallthrough]]; + case begins_sign | has_digits | has_x | has_dot | has_p | has_plus: + [[fallthrough]]; + case begins_sign | has_digits | has_x | has_dot | has_p | signs_msk: + val.reset(new value{parse_hex_float()}); + break; + + // times + // HH:MM + // HH:MM:SS + // HH:MM:SS.FFFFFF + case bzero_msk | has_colon: + [[fallthrough]]; + case bzero_msk | has_colon | has_dot: + [[fallthrough]]; + case bdigit_msk | has_colon: + [[fallthrough]]; + case bdigit_msk | has_colon | has_dot: + val.reset(new value{parse_time()}); + break; + + // local dates + // YYYY-MM-DD + case bzero_msk | has_minus: + [[fallthrough]]; + case bdigit_msk | has_minus: + val.reset(new value{parse_date()}); + break; + + // date-times + // YYYY-MM-DDTHH:MM + // YYYY-MM-DDTHH:MM-HH:MM + // YYYY-MM-DDTHH:MM+HH:MM + // YYYY-MM-DD HH:MM + // YYYY-MM-DD HH:MM-HH:MM + // YYYY-MM-DD HH:MM+HH:MM + // YYYY-MM-DDTHH:MM:SS + // YYYY-MM-DDTHH:MM:SS-HH:MM + // YYYY-MM-DDTHH:MM:SS+HH:MM + // YYYY-MM-DD HH:MM:SS + // YYYY-MM-DD HH:MM:SS-HH:MM + // YYYY-MM-DD HH:MM:SS+HH:MM + case bzero_msk | has_minus | has_colon | has_t: + [[fallthrough]]; + case bzero_msk | signs_msk | has_colon | has_t: + [[fallthrough]]; + case bdigit_msk | has_minus | has_colon | has_t: + [[fallthrough]]; + case bdigit_msk | signs_msk | has_colon | has_t: + [[fallthrough]]; + // YYYY-MM-DDTHH:MM:SS.FFFFFF + // YYYY-MM-DDTHH:MM:SS.FFFFFF-HH:MM + // YYYY-MM-DDTHH:MM:SS.FFFFFF+HH:MM + // YYYY-MM-DD HH:MM:SS.FFFFFF + // YYYY-MM-DD HH:MM:SS.FFFFFF-HH:MM + // YYYY-MM-DD HH:MM:SS.FFFFFF+HH:MM + case bzero_msk | has_minus | has_colon | has_dot | has_t: + [[fallthrough]]; + case bzero_msk | signs_msk | has_colon | has_dot | has_t: + [[fallthrough]]; + case bdigit_msk | has_minus | has_colon | has_dot | has_t: + [[fallthrough]]; + case bdigit_msk | signs_msk | has_colon | has_dot | has_t: + [[fallthrough]]; + // YYYY-MM-DDTHH:MMZ + // YYYY-MM-DD HH:MMZ + // YYYY-MM-DDTHH:MM:SSZ + // YYYY-MM-DD HH:MM:SSZ + // YYYY-MM-DDTHH:MM:SS.FFFFFFZ + // YYYY-MM-DD HH:MM:SS.FFFFFFZ + case bzero_msk | has_minus | has_colon | has_z | has_t: + [[fallthrough]]; + case bzero_msk | has_minus | has_colon | has_dot | has_z | has_t: + [[fallthrough]]; + case bdigit_msk | has_minus | has_colon | has_z | has_t: + [[fallthrough]]; + case bdigit_msk | has_minus | has_colon | has_dot | has_z | has_t: + val.reset(new value{parse_date_time()}); + break; + } + } while (false); + + if (!val) { + set_error_at(begin_pos, "could not determine value type"sv); + return_after_error({}); + } + + val->source_ = {begin_pos, current_position(1), reader.source_path()}; + return val; + } + + TOML_NEVER_INLINE + bool parse_key() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_bare_key_character(*cp) || is_string_delimiter(*cp)); + push_parse_scope("key"sv); + + key_buffer.clear(); + recording_whitespace = false; + + while (!is_error()) { + std::string_view key_segment; + const auto key_begin = current_position(); + + // bare_key_segment + if (is_bare_key_character(*cp)) key_segment = parse_bare_key_segment(); + + // "quoted key segment" + else if (is_string_delimiter(*cp)) { + const auto begin_pos = cp->position; + + recording_whitespace = true; + parsed_string str = parse_string(); + recording_whitespace = false; + return_if_error({}); + + if (str.was_multi_line) { + set_error_at(begin_pos, "multi-line strings are prohibited in "sv, + key_buffer.empty() ? ""sv : "dotted "sv, "keys"sv); + return_after_error({}); + } else + key_segment = str.value; + } + + // ??? + else + set_error_and_return_default( + "expected bare key starting character or string delimiter, saw '"sv, to_sv(*cp), + "'"sv); + + const auto key_end = current_position(); + + // whitespace following the key segment + consume_leading_whitespace(); + + // store segment + key_buffer.push_back(key_segment, key_begin, key_end); + + // eof or no more key to come + if (is_eof() || *cp != U'.') break; + + // was a dotted key - go around again + advance_and_return_if_error_or_eof({}); + consume_leading_whitespace(); + set_error_and_return_if_eof({}); + } + return_if_error({}); + + return true; + } + + TOML_NODISCARD + key make_key(size_t segment_index) const { + TOML_ASSERT(key_buffer.size() > segment_index); + + return key{key_buffer[segment_index], + source_region{key_buffer.starts[segment_index], key_buffer.ends[segment_index], + root.source().path}}; + } + + TOML_NODISCARD + TOML_NEVER_INLINE + table* parse_table_header() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(*cp == U'['); + push_parse_scope("table header"sv); + + const source_position header_begin_pos = cp->position; + source_position header_end_pos; + bool is_arr = false; + + // parse header + { + // skip first '[' + advance_and_return_if_error_or_eof({}); + + // skip past any whitespace that followed the '[' + const bool had_leading_whitespace = consume_leading_whitespace(); + set_error_and_return_if_eof({}); + + // skip second '[' (if present) + if (*cp == U'[') { + if (had_leading_whitespace) + set_error_and_return_default( + "[[array-of-table]] brackets must be contiguous (i.e. [ [ this ] ] is prohibited)"sv); + + is_arr = true; + advance_and_return_if_error_or_eof({}); + + // skip past any whitespace that followed the '[' + consume_leading_whitespace(); + set_error_and_return_if_eof({}); + } + + // check for a premature closing ']' + if (*cp == U']') + set_error_and_return_default("tables with blank bare keys are explicitly prohibited"sv); + + // get the actual key + start_recording(); + parse_key(); + stop_recording(1u); + return_if_error({}); + + // skip past any whitespace that followed the key + consume_leading_whitespace(); + return_if_error({}); + set_error_and_return_if_eof({}); + + // consume the closing ']' + if (*cp != U']') set_error_and_return_default("expected ']', saw '"sv, to_sv(*cp), "'"sv); + if (is_arr) { + advance_and_return_if_error_or_eof({}); + if (*cp != U']') set_error_and_return_default("expected ']', saw '"sv, to_sv(*cp), "'"sv); + } + advance_and_return_if_error({}); + header_end_pos = current_position(1); + + // handle the rest of the line after the header + consume_leading_whitespace(); + if (!is_eof() && !consume_comment() && !consume_line_break()) + set_error_and_return_default("expected a comment or whitespace, saw '"sv, to_sv(cp), + "'"sv); + } + TOML_ASSERT(!key_buffer.empty()); + + // check if each parent is a table/table array, or can be created implicitly as a table. + table* parent = &root; + for (size_t i = 0, e = key_buffer.size() - 1u; i < e; i++) { + const std::string_view segment = key_buffer[i]; + auto pit = parent->lower_bound(segment); + + // parent already existed + if (pit != parent->end() && pit->first == segment) { + node& p = pit->second; + + if (auto tbl = p.as_table()) { + // adding to closed inline tables is illegal + if (tbl->is_inline() && + !impl::find(open_inline_tables.begin(), open_inline_tables.end(), tbl)) + set_error_and_return_default("cannot insert '"sv, to_sv(recording_buffer), + "' into existing inline table"sv); + + parent = tbl; + } else if (auto arr = p.as_array(); + arr && impl::find(table_arrays.begin(), table_arrays.end(), arr)) { + // table arrays are a special case; + // the spec dictates we select the most recently declared element in the array. + TOML_ASSERT(!arr->empty()); + TOML_ASSERT(arr->back().is_table()); + parent = &arr->back().ref_cast
(); + } else { + if (!is_arr && p.type() == node_type::table) + set_error_and_return_default("cannot redefine existing table '"sv, + to_sv(recording_buffer), "'"sv); + else + set_error_and_return_default("cannot redefine existing "sv, to_sv(p.type()), " '"sv, + to_sv(recording_buffer), "' as "sv, + is_arr ? "array-of-tables"sv : "table"sv); + } + } + + // need to create a new implicit table + else { + pit = parent->emplace_hint
(pit, make_key(i)); + table& p = pit->second.ref_cast
(); + p.source_ = {header_begin_pos, header_end_pos, reader.source_path()}; + + implicit_tables.push_back(&p); + parent = &p; + } + } + + const auto last_segment = key_buffer.back(); + auto it = parent->lower_bound(last_segment); + + // if there was already a matching node some sanity checking is necessary; + // this is ok if we're making an array and the existing element is already an array (new + // element) or if we're making a table and the existing element is an implicitly-created table + // (promote it), otherwise this is a redefinition error. + if (it != parent->end() && it->first == last_segment) { + node& matching_node = it->second; + if (auto arr = matching_node.as_array(); + is_arr && arr && impl::find(table_arrays.begin(), table_arrays.end(), arr)) { + table& tbl = arr->emplace_back
(); + tbl.source_ = {header_begin_pos, header_end_pos, reader.source_path()}; + return &tbl; + } + + else if (auto tbl = matching_node.as_table(); !is_arr && tbl && !implicit_tables.empty()) { + if (auto found = impl::find(implicit_tables.begin(), implicit_tables.end(), tbl); found) { + bool ok = true; + if (!tbl->empty()) { + for (auto& [_, child] : *tbl) { + if (!child.is_table() && !child.is_array_of_tables()) { + ok = false; + break; + } + } + } + + if (ok) { + implicit_tables.erase(implicit_tables.cbegin() + (found - implicit_tables.data())); + tbl->source_.begin = header_begin_pos; + tbl->source_.end = header_end_pos; + return tbl; + } + } + } + + // if we get here it's a redefinition error. + if (!is_arr && matching_node.type() == node_type::table) { + set_error_at(header_begin_pos, "cannot redefine existing table '"sv, + to_sv(recording_buffer), "'"sv); + return_after_error({}); + } else { + set_error_at(header_begin_pos, "cannot redefine existing "sv, to_sv(matching_node.type()), + " '"sv, to_sv(recording_buffer), "' as "sv, + is_arr ? "array-of-tables"sv : "table"sv); + return_after_error({}); + } + } + + // there was no matching node, sweet - we can freely instantiate a new table/table array. + else { + auto last_key = make_key(key_buffer.size() - 1u); + + // if it's an array we need to make the array and it's first table element, + // set the starting regions, and return the table element + if (is_arr) { + it = parent->emplace_hint(it, std::move(last_key)); + array& tbl_arr = it->second.ref_cast(); + table_arrays.push_back(&tbl_arr); + tbl_arr.source_ = {header_begin_pos, header_end_pos, reader.source_path()}; + + table& tbl = tbl_arr.emplace_back
(); + tbl.source_ = {header_begin_pos, header_end_pos, reader.source_path()}; + return &tbl; + } + + // otherwise we're just making a table + else { + it = parent->emplace_hint
(it, std::move(last_key)); + table& tbl = it->second.ref_cast
(); + tbl.source_ = {header_begin_pos, header_end_pos, reader.source_path()}; + return &tbl; + } + } + } + + TOML_NEVER_INLINE + bool parse_key_value_pair_and_insert(table* tbl) { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_string_delimiter(*cp) || is_bare_key_character(*cp)); + push_parse_scope("key-value pair"sv); + + // read the key into the key buffer + start_recording(); + parse_key(); + stop_recording(1u); + return_if_error({}); + TOML_ASSERT(key_buffer.size() >= 1u); + + // skip past any whitespace that followed the key + consume_leading_whitespace(); + set_error_and_return_if_eof({}); + + // '=' + if (*cp != U'=') set_error_and_return_default("expected '=', saw '"sv, to_sv(*cp), "'"sv); + advance_and_return_if_error_or_eof({}); + + // skip past any whitespace that followed the '=' + consume_leading_whitespace(); + return_if_error({}); + set_error_and_return_if_eof({}); + + // check that the next character could actually be a value + if (is_value_terminator(*cp)) + set_error_and_return_default("expected value, saw '"sv, to_sv(*cp), "'"sv); + + // if it's a dotted kvp we need to spawn the parent sub-tables if necessary, + // and set the target table to the second-to-last one in the chain + if (key_buffer.size() > 1u) { + for (size_t i = 0; i < key_buffer.size() - 1u; i++) { + const std::string_view segment = key_buffer[i]; + auto pit = tbl->lower_bound(segment); + + // parent already existed + if (pit != tbl->end() && pit->first == segment) { + table* p = pit->second.as_table(); + + // redefinition + if TOML_UNLIKELY (!p || + !(impl::find(dotted_key_tables.begin(), dotted_key_tables.end(), p) || + impl::find(implicit_tables.begin(), implicit_tables.end(), p))) { + set_error_at(key_buffer.starts[i], "cannot redefine existing "sv, + to_sv(pit->second.type()), " as dotted key-value pair"sv); + return_after_error({}); + } + + tbl = p; + } + + // need to create a new implicit table + else { + pit = tbl->emplace_hint
(pit, make_key(i)); + table& p = pit->second.ref_cast
(); + p.source_ = pit->first.source(); + + dotted_key_tables.push_back(&p); + tbl = &p; + } + } + } + + // ensure this isn't a redefinition + const std::string_view last_segment = key_buffer.back(); + auto it = tbl->lower_bound(last_segment); + if (it != tbl->end() && it->first == last_segment) { + set_error("cannot redefine existing "sv, to_sv(it->second.type()), " '"sv, + to_sv(recording_buffer), "'"sv); + return_after_error({}); + } + + // create the key first since the key buffer will likely get overwritten during value parsing + // (inline tables) + auto last_key = make_key(key_buffer.size() - 1u); + + // now we can actually parse the value + node_ptr val = parse_value(); + return_if_error({}); + + tbl->emplace_hint(it, std::move(last_key), std::move(val)); + return true; + } + + void parse_document() { + assert_not_error(); + assert_not_eof(); + push_parse_scope("root table"sv); + + table* current_table = &root; + + do { + return_if_error(); + + // leading whitespace, line endings, comments + if (consume_leading_whitespace() || consume_line_break() || consume_comment()) continue; + return_if_error(); + + // [tables] + // [[table array]] + if (*cp == U'[') current_table = parse_table_header(); + + // bare_keys + // dotted.keys + // "quoted keys" + else if (is_bare_key_character(*cp) || is_string_delimiter(*cp)) { + push_parse_scope("key-value pair"sv); + + parse_key_value_pair_and_insert(current_table); + + // handle the rest of the line after the kvp + // (this is not done in parse_key_value_pair() because that is also used for inline + // tables) + consume_leading_whitespace(); + return_if_error(); + if (!is_eof() && !consume_comment() && !consume_line_break()) + set_error("expected a comment or whitespace, saw '"sv, to_sv(cp), "'"sv); + } + + else // ?? + set_error("expected keys, tables, whitespace or comments, saw '"sv, to_sv(cp), "'"sv); + } while (!is_eof()); + + auto eof_pos = current_position(1); + root.source_.end = eof_pos; + if (current_table && current_table != &root && + current_table->source_.end <= current_table->source_.begin) + current_table->source_.end = eof_pos; + } + + static void update_region_ends(node& nde) noexcept { + const auto type = nde.type(); + if (type > node_type::array) return; + + if (type == node_type::table) { + auto& tbl = nde.ref_cast
(); + if (tbl.is_inline()) // inline tables (and all their inline descendants) are already + // correctly terminated + return; + + auto end = nde.source_.end; + for (auto&& [k, v] : tbl) { + TOML_UNUSED(k); + update_region_ends(v); + if (end < v.source_.end) end = v.source_.end; + } + } else // arrays + { + auto& arr = nde.ref_cast(); + auto end = nde.source_.end; + for (auto&& v : arr) { + update_region_ends(v); + if (end < v.source_.end) end = v.source_.end; + } + nde.source_.end = end; + } + } + + public: + parser(utf8_reader_interface&& reader_) // + : reader{reader_} { + root.source_ = {prev_pos, prev_pos, reader.source_path()}; + + if (!reader.peek_eof()) { + cp = reader.read_next(); #if !TOML_EXCEPTIONS - if (reader.error()) - { - err = std::move(reader.error()); - return; - } + if (reader.error()) { + err = std::move(reader.error()); + return; + } #endif - if (cp) - parse_document(); - } + if (cp) parse_document(); + } - update_region_ends(root); - } + update_region_ends(root); + } - TOML_NODISCARD - operator parse_result() && noexcept - { + TOML_NODISCARD + operator parse_result() && noexcept { #if TOML_EXCEPTIONS - return { std::move(root) }; + return {std::move(root)}; #else - if (err) - return parse_result{ *std::move(err) }; - else - return parse_result{ std::move(root) }; + if (err) + return parse_result{*std::move(err)}; + else + return parse_result{std::move(root)}; #endif - } - }; - - TOML_EXTERNAL_LINKAGE - node_ptr parser::parse_array() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(*cp == U'['); - push_parse_scope("array"sv); - - // skip opening '[' - advance_and_return_if_error_or_eof({}); - - node_ptr arr_ptr{ new array{} }; - array& arr = arr_ptr->ref_cast(); - enum class TOML_CLOSED_ENUM parse_type : int - { - none, - comma, - val - }; - parse_type prev = parse_type::none; - - while (!is_error()) - { - while (consume_leading_whitespace() || consume_line_break() || consume_comment()) - continue; - set_error_and_return_if_eof({}); - - // commas - only legal after a value - if (*cp == U',') - { - if (prev == parse_type::val) - { - prev = parse_type::comma; - advance_and_return_if_error_or_eof({}); - continue; - } - set_error_and_return_default("expected value or closing ']', saw comma"sv); - } - - // closing ']' - else if (*cp == U']') - { - advance_and_return_if_error({}); - break; - } - - // must be a value - else - { - if (prev == parse_type::val) - { - set_error_and_return_default("expected comma or closing ']', saw '"sv, to_sv(*cp), "'"sv); - continue; - } - prev = parse_type::val; - - auto val = parse_value(); - return_if_error({}); - - if (!arr.capacity()) - arr.reserve(4u); - arr.emplace_back(std::move(val)); - } - } - - return_if_error({}); - return arr_ptr; - } - - TOML_EXTERNAL_LINKAGE - node_ptr parser::parse_inline_table() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(*cp == U'{'); - push_parse_scope("inline table"sv); - - // skip opening '{' - advance_and_return_if_error_or_eof({}); - - node_ptr tbl_ptr{ new table{} }; - table& tbl = tbl_ptr->ref_cast
(); - tbl.is_inline(true); - table_vector_scope table_scope{ open_inline_tables, tbl }; - - enum class TOML_CLOSED_ENUM parse_type : int - { - none, - comma, - kvp - }; - parse_type prev = parse_type::none; - while (!is_error()) - { - if constexpr (TOML_LANG_UNRELEASED) // toml/issues/516 (newlines/trailing commas in inline tables) - { - while (consume_leading_whitespace() || consume_line_break() || consume_comment()) - continue; - } - else - { - while (consume_leading_whitespace()) - continue; - } - return_if_error({}); - set_error_and_return_if_eof({}); - - // commas - only legal after a key-value pair - if (*cp == U',') - { - if (prev == parse_type::kvp) - { - prev = parse_type::comma; - advance_and_return_if_error_or_eof({}); - } - else - set_error_and_return_default("expected key-value pair or closing '}', saw comma"sv); - } - - // closing '}' - else if (*cp == U'}') - { - if constexpr (!TOML_LANG_UNRELEASED) // toml/issues/516 (newlines/trailing commas in inline tables) - { - if (prev == parse_type::comma) - { - set_error_and_return_default("expected key-value pair, saw closing '}' (dangling comma)"sv); - continue; - } - } - advance_and_return_if_error({}); - break; - } - - // key-value pair - else if (is_string_delimiter(*cp) || is_bare_key_character(*cp)) - { - if (prev == parse_type::kvp) - set_error_and_return_default("expected comma or closing '}', saw '"sv, to_sv(*cp), "'"sv); - else - { - prev = parse_type::kvp; - parse_key_value_pair_and_insert(&tbl); - } - } - - /// ??? - else - set_error_and_return_default("expected key or closing '}', saw '"sv, to_sv(*cp), "'"sv); - } - - return_if_error({}); - return tbl_ptr; - } - - TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS + } + }; + + TOML_EXTERNAL_LINKAGE + node_ptr parser::parse_array() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(*cp == U'['); + push_parse_scope("array"sv); + + // skip opening '[' + advance_and_return_if_error_or_eof({}); + + node_ptr arr_ptr{new array{}}; + array& arr = arr_ptr->ref_cast(); + enum class TOML_CLOSED_ENUM parse_type : int { none, comma, val }; + parse_type prev = parse_type::none; + + while (!is_error()) { + while (consume_leading_whitespace() || consume_line_break() || consume_comment()) continue; + set_error_and_return_if_eof({}); + + // commas - only legal after a value + if (*cp == U',') { + if (prev == parse_type::val) { + prev = parse_type::comma; + advance_and_return_if_error_or_eof({}); + continue; + } + set_error_and_return_default("expected value or closing ']', saw comma"sv); + } + + // closing ']' + else if (*cp == U']') { + advance_and_return_if_error({}); + break; + } + + // must be a value + else { + if (prev == parse_type::val) { + set_error_and_return_default("expected comma or closing ']', saw '"sv, to_sv(*cp), "'"sv); + continue; + } + prev = parse_type::val; + + auto val = parse_value(); + return_if_error({}); + + if (!arr.capacity()) arr.reserve(4u); + arr.emplace_back(std::move(val)); + } + } + + return_if_error({}); + return arr_ptr; + } + + TOML_EXTERNAL_LINKAGE + node_ptr parser::parse_inline_table() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(*cp == U'{'); + push_parse_scope("inline table"sv); + + // skip opening '{' + advance_and_return_if_error_or_eof({}); + + node_ptr tbl_ptr{new table{}}; + table& tbl = tbl_ptr->ref_cast
(); + tbl.is_inline(true); + table_vector_scope table_scope{open_inline_tables, tbl}; + + enum class TOML_CLOSED_ENUM parse_type : int { none, comma, kvp }; + parse_type prev = parse_type::none; + while (!is_error()) { + if constexpr (TOML_LANG_UNRELEASED) // toml/issues/516 (newlines/trailing commas in inline + // tables) + { + while (consume_leading_whitespace() || consume_line_break() || consume_comment()) continue; + } else { + while (consume_leading_whitespace()) continue; + } + return_if_error({}); + set_error_and_return_if_eof({}); + + // commas - only legal after a key-value pair + if (*cp == U',') { + if (prev == parse_type::kvp) { + prev = parse_type::comma; + advance_and_return_if_error_or_eof({}); + } else + set_error_and_return_default("expected key-value pair or closing '}', saw comma"sv); + } + + // closing '}' + else if (*cp == U'}') { + if constexpr (!TOML_LANG_UNRELEASED) // toml/issues/516 (newlines/trailing commas in inline + // tables) + { + if (prev == parse_type::comma) { + set_error_and_return_default( + "expected key-value pair, saw closing '}' (dangling comma)"sv); + continue; + } + } + advance_and_return_if_error({}); + break; + } + + // key-value pair + else if (is_string_delimiter(*cp) || is_bare_key_character(*cp)) { + if (prev == parse_type::kvp) + set_error_and_return_default("expected comma or closing '}', saw '"sv, to_sv(*cp), "'"sv); + else { + prev = parse_type::kvp; + parse_key_value_pair_and_insert(&tbl); + } + } + + /// ??? + else + set_error_and_return_default("expected key or closing '}', saw '"sv, to_sv(*cp), "'"sv); + } + + return_if_error({}); + return tbl_ptr; + } + + TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS } TOML_IMPL_NAMESPACE_END; @@ -3747,171 +3354,160 @@ TOML_IMPL_NAMESPACE_END; #undef utf8_reader_error_check #undef utf8_reader_return_after_error -//#--------------------------------------------------------------------------------------------------------------------- -//# PARSER PUBLIC IMPLEMENTATION -//#--------------------------------------------------------------------------------------------------------------------- - -TOML_ANON_NAMESPACE_START -{ - TOML_NODISCARD - TOML_INTERNAL_LINKAGE - parse_result do_parse(utf8_reader_interface && reader) - { - return impl::parser{ std::move(reader) }; - } - - TOML_NODISCARD - TOML_INTERNAL_LINKAGE - parse_result do_parse_file(std::string_view file_path) - { +// #--------------------------------------------------------------------------------------------------------------------- +// # PARSER PUBLIC IMPLEMENTATION +// #--------------------------------------------------------------------------------------------------------------------- + +TOML_ANON_NAMESPACE_START { + TOML_NODISCARD + TOML_INTERNAL_LINKAGE + parse_result do_parse(utf8_reader_interface && reader) { + return impl::parser{std::move(reader)}; + } + + TOML_NODISCARD + TOML_INTERNAL_LINKAGE + parse_result do_parse_file(std::string_view file_path) { #if TOML_EXCEPTIONS -#define TOML_PARSE_FILE_ERROR(msg, path) \ - throw parse_error{ msg, source_position{}, std::make_shared(std::move(path)) } +#define TOML_PARSE_FILE_ERROR(msg, path) \ + throw parse_error { \ + msg, source_position{}, std::make_shared(std::move(path)) \ + } #else -#define TOML_PARSE_FILE_ERROR(msg, path) \ - return parse_result \ - { \ - parse_error \ - { \ - msg, source_position{}, std::make_shared(std::move(path)) \ - } \ - } +#define TOML_PARSE_FILE_ERROR(msg, path) \ + return parse_result { \ + parse_error { \ + msg, source_position{}, std::make_shared(std::move(path)) \ + } \ + } #endif - std::string file_path_str(file_path); + std::string file_path_str(file_path); - // open file with a custom-sized stack buffer - std::ifstream file; - TOML_OVERALIGNED char file_buffer[sizeof(void*) * 1024u]; - file.rdbuf()->pubsetbuf(file_buffer, sizeof(file_buffer)); + // open file with a custom-sized stack buffer + std::ifstream file; + TOML_OVERALIGNED char file_buffer[sizeof(void*) * 1024u]; + file.rdbuf()->pubsetbuf(file_buffer, sizeof(file_buffer)); #if TOML_WINDOWS - file.open(impl::widen(file_path_str).c_str(), std::ifstream::in | std::ifstream::binary | std::ifstream::ate); + file.open(impl::widen(file_path_str).c_str(), + std::ifstream::in | std::ifstream::binary | std::ifstream::ate); #else - file.open(file_path_str, std::ifstream::in | std::ifstream::binary | std::ifstream::ate); + file.open(file_path_str, std::ifstream::in | std::ifstream::binary | std::ifstream::ate); #endif - if (!file.is_open()) - TOML_PARSE_FILE_ERROR("File could not be opened for reading", file_path_str); - - // get size - const auto file_size = file.tellg(); - if (file_size == -1) - TOML_PARSE_FILE_ERROR("Could not determine file size", file_path_str); - file.seekg(0, std::ifstream::beg); - - // read the whole file into memory first if the file isn't too large - constexpr auto large_file_threshold = 1024 * 1024 * 2; // 2 MB - if (file_size <= large_file_threshold) - { - std::vector file_data; - file_data.resize(static_cast(file_size)); - file.read(file_data.data(), static_cast(file_size)); - return parse(std::string_view{ file_data.data(), file_data.size() }, std::move(file_path_str)); - } - - // otherwise parse it using the streams - else - return parse(file, std::move(file_path_str)); + if (!file.is_open()) + TOML_PARSE_FILE_ERROR("File could not be opened for reading", file_path_str); + + // get size + const auto file_size = file.tellg(); + if (file_size == -1) TOML_PARSE_FILE_ERROR("Could not determine file size", file_path_str); + file.seekg(0, std::ifstream::beg); + + // read the whole file into memory first if the file isn't too large + constexpr auto large_file_threshold = 1024 * 1024 * 2; // 2 MB + if (file_size <= large_file_threshold) { + std::vector file_data; + file_data.resize(static_cast(file_size)); + file.read(file_data.data(), static_cast(file_size)); + return parse(std::string_view{file_data.data(), file_data.size()}, std::move(file_path_str)); + } + + // otherwise parse it using the streams + else + return parse(file, std::move(file_path_str)); #undef TOML_PARSE_FILE_ERROR - } + } } TOML_ANON_NAMESPACE_END; -TOML_NAMESPACE_START -{ - TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex); - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::string_view doc, std::string_view source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, source_path }); - } - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::string_view doc, std::string && source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, std::move(source_path) }); - } - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::istream & doc, std::string_view source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, source_path }); - } - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::istream & doc, std::string && source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, std::move(source_path) }); - } - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse_file(std::string_view file_path) - { - return TOML_ANON_NAMESPACE::do_parse_file(file_path); - } +TOML_NAMESPACE_START { + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex); + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::string_view doc, std::string_view source_path) { + return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{doc, source_path}); + } + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::string_view doc, std::string && source_path) { + return TOML_ANON_NAMESPACE::do_parse( + TOML_ANON_NAMESPACE::utf8_reader{doc, std::move(source_path)}); + } + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::istream & doc, std::string_view source_path) { + return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{doc, source_path}); + } + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::istream & doc, std::string && source_path) { + return TOML_ANON_NAMESPACE::do_parse( + TOML_ANON_NAMESPACE::utf8_reader{doc, std::move(source_path)}); + } + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse_file(std::string_view file_path) { + return TOML_ANON_NAMESPACE::do_parse_file(file_path); + } #if TOML_HAS_CHAR8 - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string_view source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, source_path }); - } - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string && source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, std::move(source_path) }); - } - - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse_file(std::u8string_view file_path) - { - std::string file_path_str; - file_path_str.resize(file_path.length()); - memcpy(file_path_str.data(), file_path.data(), file_path.length()); - return TOML_ANON_NAMESPACE::do_parse_file(file_path_str); - } - -#endif // TOML_HAS_CHAR8 + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string_view source_path) { + return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{doc, source_path}); + } + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string && source_path) { + return TOML_ANON_NAMESPACE::do_parse( + TOML_ANON_NAMESPACE::utf8_reader{doc, std::move(source_path)}); + } + + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse_file(std::u8string_view file_path) { + std::string file_path_str; + file_path_str.resize(file_path.length()); + memcpy(file_path_str.data(), file_path.data(), file_path.length()); + return TOML_ANON_NAMESPACE::do_parse_file(file_path_str); + } + +#endif // TOML_HAS_CHAR8 #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::string_view doc, std::wstring_view source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, impl::narrow(source_path) }); - } + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::string_view doc, std::wstring_view source_path) { + return TOML_ANON_NAMESPACE::do_parse( + TOML_ANON_NAMESPACE::utf8_reader{doc, impl::narrow(source_path)}); + } - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::istream & doc, std::wstring_view source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, impl::narrow(source_path) }); - } + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::istream & doc, std::wstring_view source_path) { + return TOML_ANON_NAMESPACE::do_parse( + TOML_ANON_NAMESPACE::utf8_reader{doc, impl::narrow(source_path)}); + } - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse_file(std::wstring_view file_path) - { - return TOML_ANON_NAMESPACE::do_parse_file(impl::narrow(file_path)); - } + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse_file(std::wstring_view file_path) { + return TOML_ANON_NAMESPACE::do_parse_file(impl::narrow(file_path)); + } -#endif // TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_ENABLE_WINDOWS_COMPAT #if TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - parse_result TOML_CALLCONV parse(std::u8string_view doc, std::wstring_view source_path) - { - return TOML_ANON_NAMESPACE::do_parse(TOML_ANON_NAMESPACE::utf8_reader{ doc, impl::narrow(source_path) }); - } + TOML_EXTERNAL_LINKAGE + parse_result TOML_CALLCONV parse(std::u8string_view doc, std::wstring_view source_path) { + return TOML_ANON_NAMESPACE::do_parse( + TOML_ANON_NAMESPACE::utf8_reader{doc, impl::narrow(source_path)}); + } -#endif // TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT - TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS + TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS } TOML_NAMESPACE_END; #undef TOML_OVERALIGNED #include "header_end.hpp" -#endif // TOML_ENABLE_PARSER +#endif // TOML_ENABLE_PARSER diff --git a/vendor/toml++/impl/path.inl b/vendor/toml++/impl/path.inl index 999d651..c33632f 100644 --- a/vendor/toml++/impl/path.inl +++ b/vendor/toml++/impl/path.inl @@ -1,18 +1,18 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard +// # 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 -//# }} +// # }} -#include "path.hpp" #include "at_path.hpp" +#include "path.hpp" #include "print_to_stream.hpp" TOML_DISABLE_WARNINGS; #if TOML_INT_CHARCONV @@ -22,501 +22,427 @@ TOML_DISABLE_WARNINGS; TOML_ENABLE_WARNINGS; #include "header_start.hpp" -//#===================================================================================================================== -//# toml::path_component -//#===================================================================================================================== - -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - path_component::path_component() // - : type_{ path_component_type::key } - { - store_key("", value_storage_); - } - - TOML_EXTERNAL_LINKAGE - path_component::path_component(size_t index) noexcept // - : type_(path_component_type::array_index) - { - store_index(index, value_storage_); - } - - TOML_EXTERNAL_LINKAGE - path_component::path_component(std::string_view key) // - : type_(path_component_type::key) - { - store_key(key, value_storage_); - } +// #===================================================================================================================== +// # toml::path_component +// #===================================================================================================================== + +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + path_component::path_component() // + : type_{path_component_type::key} { + store_key("", value_storage_); + } + + TOML_EXTERNAL_LINKAGE + path_component::path_component(size_t index) noexcept // + : type_(path_component_type::array_index) { + store_index(index, value_storage_); + } + + TOML_EXTERNAL_LINKAGE + path_component::path_component(std::string_view key) // + : type_(path_component_type::key) { + store_key(key, value_storage_); + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - path_component::path_component(std::wstring_view key) // - : path_component(impl::narrow(key)) - {} + TOML_EXTERNAL_LINKAGE + path_component::path_component(std::wstring_view key) // + : path_component(impl::narrow(key)) {} #endif - TOML_EXTERNAL_LINKAGE - path_component::path_component(const path_component& pc) // - : type_{ pc.type_ } - { - if (type_ == path_component_type::array_index) - store_index(pc.index(), value_storage_); - else - store_key(pc.key(), value_storage_); - } - - TOML_EXTERNAL_LINKAGE - path_component::path_component(path_component && pc) noexcept // - : type_{ pc.type_ } - { - if (type_ == path_component_type::array_index) - store_index(pc.index_ref(), value_storage_); - else - store_key(std::move(pc.key_ref()), value_storage_); - } - - TOML_EXTERNAL_LINKAGE - path_component& path_component::operator=(const path_component& rhs) - { - if (type_ != rhs.type_) - { - destroy(); - - type_ = rhs.type_; - if (type_ == path_component_type::array_index) - store_index(rhs.index(), value_storage_); - else - store_key(rhs.key(), value_storage_); - } - else - { - if (type_ == path_component_type::array_index) - index_ref() = rhs.index(); - else - key_ref() = rhs.key(); - } - return *this; - } - - TOML_EXTERNAL_LINKAGE - path_component& path_component::operator=(path_component&& rhs) noexcept - { - if (type_ != rhs.type_) - { - destroy(); - - type_ = rhs.type_; - if (type_ == path_component_type::array_index) - store_index(rhs.index(), value_storage_); - else - store_key(std::move(rhs.key_ref()), value_storage_); - } - else - { - if (type_ == path_component_type::array_index) - index_ref() = rhs.index(); - else - key_ref() = std::move(rhs.key_ref()); - } - return *this; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool TOML_CALLCONV path_component::equal(const path_component& lhs, const path_component& rhs) noexcept - { - // Different comparison depending on contents - if (lhs.type_ != rhs.type_) - return false; - - if (lhs.type_ == path_component_type::array_index) - return lhs.index() == rhs.index(); - else // path_component_type::key - return lhs.key() == rhs.key(); - } - - TOML_EXTERNAL_LINKAGE - path_component& path_component::operator=(size_t new_index) noexcept - { - // If currently a key, string will need to be destroyed regardless - destroy(); - - type_ = path_component_type::array_index; - store_index(new_index, value_storage_); - - return *this; - } - - TOML_EXTERNAL_LINKAGE - path_component& path_component::operator=(std::string_view new_key) - { - if (type_ == path_component_type::key) - key_ref() = new_key; - else - { - type_ = path_component_type::key; - store_key(new_key, value_storage_); - } - - return *this; - } + TOML_EXTERNAL_LINKAGE + path_component::path_component(const path_component& pc) // + : type_{pc.type_} { + if (type_ == path_component_type::array_index) + store_index(pc.index(), value_storage_); + else + store_key(pc.key(), value_storage_); + } + + TOML_EXTERNAL_LINKAGE + path_component::path_component(path_component && pc) noexcept // + : type_{pc.type_} { + if (type_ == path_component_type::array_index) + store_index(pc.index_ref(), value_storage_); + else + store_key(std::move(pc.key_ref()), value_storage_); + } + + TOML_EXTERNAL_LINKAGE + path_component& path_component::operator=(const path_component& rhs) { + if (type_ != rhs.type_) { + destroy(); + + type_ = rhs.type_; + if (type_ == path_component_type::array_index) + store_index(rhs.index(), value_storage_); + else + store_key(rhs.key(), value_storage_); + } else { + if (type_ == path_component_type::array_index) + index_ref() = rhs.index(); + else + key_ref() = rhs.key(); + } + return *this; + } + + TOML_EXTERNAL_LINKAGE + path_component& path_component::operator=(path_component&& rhs) noexcept { + if (type_ != rhs.type_) { + destroy(); + + type_ = rhs.type_; + if (type_ == path_component_type::array_index) + store_index(rhs.index(), value_storage_); + else + store_key(std::move(rhs.key_ref()), value_storage_); + } else { + if (type_ == path_component_type::array_index) + index_ref() = rhs.index(); + else + key_ref() = std::move(rhs.key_ref()); + } + return *this; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool TOML_CALLCONV path_component::equal(const path_component& lhs, + const path_component& rhs) noexcept { + // Different comparison depending on contents + if (lhs.type_ != rhs.type_) return false; + + if (lhs.type_ == path_component_type::array_index) + return lhs.index() == rhs.index(); + else // path_component_type::key + return lhs.key() == rhs.key(); + } + + TOML_EXTERNAL_LINKAGE + path_component& path_component::operator=(size_t new_index) noexcept { + // If currently a key, string will need to be destroyed regardless + destroy(); + + type_ = path_component_type::array_index; + store_index(new_index, value_storage_); + + return *this; + } + + TOML_EXTERNAL_LINKAGE + path_component& path_component::operator=(std::string_view new_key) { + if (type_ == path_component_type::key) + key_ref() = new_key; + else { + type_ = path_component_type::key; + store_key(new_key, value_storage_); + } + + return *this; + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - path_component& path_component::operator=(std::wstring_view new_key) - { - if (type_ == path_component_type::key) - key_ref() = impl::narrow(new_key); - else - { - type_ = path_component_type::key; - store_key(impl::narrow(new_key), value_storage_); - } + TOML_EXTERNAL_LINKAGE + path_component& path_component::operator=(std::wstring_view new_key) { + if (type_ == path_component_type::key) + key_ref() = impl::narrow(new_key); + else { + type_ = path_component_type::key; + store_key(impl::narrow(new_key), value_storage_); + } - return *this; - } + return *this; + } #endif } TOML_NAMESPACE_END; -//#===================================================================================================================== -//# toml::path -//#===================================================================================================================== - -TOML_ANON_NAMESPACE_START -{ - TOML_INTERNAL_LINKAGE - bool parse_path_into(std::string_view path_str, std::vector & components) - { - using components_type = std::remove_reference_t; - - const auto original_size = components.size(); - - static constexpr auto on_key = [](void* data, std::string_view key) -> bool - { - auto& comps = *static_cast(data); - comps.emplace_back(key); - return true; - }; - - static constexpr auto on_index = [](void* data, size_t index) -> bool - { - auto& comps = *static_cast(data); - comps.emplace_back(index); - return true; - }; - - if (!impl::parse_path(path_str, &components, on_key, on_index)) - { - components.resize(original_size); - return false; - } - - return true; - } +// #===================================================================================================================== +// # toml::path +// #===================================================================================================================== + +TOML_ANON_NAMESPACE_START { + TOML_INTERNAL_LINKAGE + bool parse_path_into(std::string_view path_str, std::vector & components) { + using components_type = std::remove_reference_t; + + const auto original_size = components.size(); + + static constexpr auto on_key = [](void* data, std::string_view key) -> bool { + auto& comps = *static_cast(data); + comps.emplace_back(key); + return true; + }; + + static constexpr auto on_index = [](void* data, size_t index) -> bool { + auto& comps = *static_cast(data); + comps.emplace_back(index); + return true; + }; + + if (!impl::parse_path(path_str, &components, on_key, on_index)) { + components.resize(original_size); + return false; + } + + return true; + } } TOML_ANON_NAMESPACE_END; -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - void path::print_to(std::ostream & os) const - { - bool root = true; - for (const auto& component : components_) - { - if (component.type() == path_component_type::key) // key - { - if (!root) - impl::print_to_stream(os, '.'); - impl::print_to_stream(os, component.key()); - } - else if (component.type() == path_component_type::array_index) // array - { - impl::print_to_stream(os, '['); - impl::print_to_stream(os, component.index()); - impl::print_to_stream(os, ']'); - } - root = false; - } - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool TOML_CALLCONV path::equal(const path& lhs, const path& rhs) noexcept - { - return lhs.components_ == rhs.components_; - } - - //#=== constructors ================================================= - - TOML_EXTERNAL_LINKAGE - path::path(std::string_view str) // - { - TOML_ANON_NAMESPACE::parse_path_into(str, components_); - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + void path::print_to(std::ostream & os) const { + bool root = true; + for (const auto& component : components_) { + if (component.type() == path_component_type::key) // key + { + if (!root) impl::print_to_stream(os, '.'); + impl::print_to_stream(os, component.key()); + } else if (component.type() == path_component_type::array_index) // array + { + impl::print_to_stream(os, '['); + impl::print_to_stream(os, component.index()); + impl::print_to_stream(os, ']'); + } + root = false; + } + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool TOML_CALLCONV path::equal(const path& lhs, const path& rhs) noexcept { + return lhs.components_ == rhs.components_; + } + + // #=== constructors ================================================= + + TOML_EXTERNAL_LINKAGE + path::path(std::string_view str) // + { + TOML_ANON_NAMESPACE::parse_path_into(str, components_); + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - path::path(std::wstring_view str) // - : path(impl::narrow(str)) - {} + TOML_EXTERNAL_LINKAGE + path::path(std::wstring_view str) // + : path(impl::narrow(str)) {} #endif - //#=== assignment ================================================= + // #=== assignment ================================================= - TOML_EXTERNAL_LINKAGE - path& path::operator=(std::string_view rhs) - { - components_.clear(); - TOML_ANON_NAMESPACE::parse_path_into(rhs, components_); - return *this; - } + TOML_EXTERNAL_LINKAGE + path& path::operator=(std::string_view rhs) { + components_.clear(); + TOML_ANON_NAMESPACE::parse_path_into(rhs, components_); + return *this; + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - path& path::operator=(std::wstring_view rhs) - { - return assign(impl::narrow(rhs)); - } + TOML_EXTERNAL_LINKAGE + path& path::operator=(std::wstring_view rhs) { + return assign(impl::narrow(rhs)); + } #endif - //#=== appending ================================================= - - TOML_EXTERNAL_LINKAGE - path& path::operator+=(const path& rhs) - { - components_.insert(components_.cend(), rhs.begin(), rhs.end()); - return *this; - } - - TOML_EXTERNAL_LINKAGE - path& path::operator+=(path&& rhs) - { - components_.insert(components_.end(), - std::make_move_iterator(rhs.components_.begin()), - std::make_move_iterator(rhs.components_.end())); - return *this; - } - - TOML_EXTERNAL_LINKAGE - path& path::operator+=(std::string_view str) - { - TOML_ANON_NAMESPACE::parse_path_into(str, components_); - return *this; - } + // #=== appending ================================================= + + TOML_EXTERNAL_LINKAGE + path& path::operator+=(const path& rhs) { + components_.insert(components_.cend(), rhs.begin(), rhs.end()); + return *this; + } + + TOML_EXTERNAL_LINKAGE + path& path::operator+=(path&& rhs) { + components_.insert(components_.end(), std::make_move_iterator(rhs.components_.begin()), + std::make_move_iterator(rhs.components_.end())); + return *this; + } + + TOML_EXTERNAL_LINKAGE + path& path::operator+=(std::string_view str) { + TOML_ANON_NAMESPACE::parse_path_into(str, components_); + return *this; + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - path& path::operator+=(std::wstring_view str) - { - return *this += impl::narrow(str); - } + TOML_EXTERNAL_LINKAGE + path& path::operator+=(std::wstring_view str) { + return *this += impl::narrow(str); + } #endif - //#=== prepending ================================================= - - TOML_EXTERNAL_LINKAGE - path& path::prepend(const path& source) - { - components_.insert(components_.begin(), source.components_.begin(), source.components_.end()); - return *this; - } - - TOML_EXTERNAL_LINKAGE - path& path::prepend(path && source) - { - components_.insert(components_.begin(), - std::make_move_iterator(source.components_.begin()), - std::make_move_iterator(source.components_.end())); - return *this; - } - - TOML_EXTERNAL_LINKAGE - path& path::prepend(std::string_view source) - { - return prepend(path{ source }); - } + // #=== prepending ================================================= + + TOML_EXTERNAL_LINKAGE + path& path::prepend(const path& source) { + components_.insert(components_.begin(), source.components_.begin(), source.components_.end()); + return *this; + } + + TOML_EXTERNAL_LINKAGE + path& path::prepend(path && source) { + components_.insert(components_.begin(), std::make_move_iterator(source.components_.begin()), + std::make_move_iterator(source.components_.end())); + return *this; + } + + TOML_EXTERNAL_LINKAGE + path& path::prepend(std::string_view source) { + return prepend(path{source}); + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - path& path::prepend(std::wstring_view source) - { - return prepend(impl::narrow(source)); - } + TOML_EXTERNAL_LINKAGE + path& path::prepend(std::wstring_view source) { + return prepend(impl::narrow(source)); + } #endif - //#=== string conversion ================================================= + // #=== string conversion ================================================= - TOML_EXTERNAL_LINKAGE - std::string path::str() const - { - if (empty()) - return ""; + TOML_EXTERNAL_LINKAGE + std::string path::str() const { + if (empty()) return ""; - std::ostringstream ss; - print_to(ss); - return std::move(ss).str(); - } + std::ostringstream ss; + print_to(ss); + return std::move(ss).str(); + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - std::wstring path::wide_str() const - { - return impl::widen(str()); - } + TOML_EXTERNAL_LINKAGE + std::wstring path::wide_str() const { + return impl::widen(str()); + } #endif - //#=== equality and comparison ================================================= - - TOML_EXTERNAL_LINKAGE - void path::clear() noexcept - { - components_.clear(); - } - - TOML_EXTERNAL_LINKAGE - path& path::truncate(size_t n) - { - n = n > components_.size() ? components_.size() : n; - - auto it_end = components_.end(); - components_.erase(it_end - static_cast(n), it_end); - - return *this; - } - - TOML_EXTERNAL_LINKAGE - path path::truncated(size_t n) const - { - path truncated_path{}; - - n = n > components_.size() ? components_.size() : n; - - // Copy all components except one - // Need at least two path components to have a parent, since if there is - // only one path component, the parent is the root/null path "" - truncated_path.components_.insert(truncated_path.components_.begin(), - components_.begin(), - components_.end() - static_cast(n)); - - return truncated_path; - } - - TOML_EXTERNAL_LINKAGE - path path::parent() const - { - return truncated(1); - } - - TOML_EXTERNAL_LINKAGE - path path::leaf(size_t n) const - { - path leaf_path{}; - - n = n > components_.size() ? components_.size() : n; - - if (n > 0) - { - leaf_path.components_.insert(leaf_path.components_.begin(), - components_.end() - static_cast(n), - components_.end()); - } - - return leaf_path; - } - - TOML_EXTERNAL_LINKAGE - path path::subpath(std::vector::const_iterator start, - std::vector::const_iterator end) const - { - if (start >= end) - return {}; - - path subpath; - subpath.components_.insert(subpath.components_.begin(), start, end); - return subpath; - } - - TOML_EXTERNAL_LINKAGE - path path::subpath(size_t start, size_t length) const - { - return subpath(begin() + static_cast(start), begin() + static_cast(start + length)); - } + // #=== equality and comparison ================================================= + + TOML_EXTERNAL_LINKAGE + void path::clear() noexcept { + components_.clear(); + } + + TOML_EXTERNAL_LINKAGE + path& path::truncate(size_t n) { + n = n > components_.size() ? components_.size() : n; + + auto it_end = components_.end(); + components_.erase(it_end - static_cast(n), it_end); + + return *this; + } + + TOML_EXTERNAL_LINKAGE + path path::truncated(size_t n) const { + path truncated_path{}; + + n = n > components_.size() ? components_.size() : n; + + // Copy all components except one + // Need at least two path components to have a parent, since if there is + // only one path component, the parent is the root/null path "" + truncated_path.components_.insert(truncated_path.components_.begin(), components_.begin(), + components_.end() - static_cast(n)); + + return truncated_path; + } + + TOML_EXTERNAL_LINKAGE + path path::parent() const { + return truncated(1); + } + + TOML_EXTERNAL_LINKAGE + path path::leaf(size_t n) const { + path leaf_path{}; + + n = n > components_.size() ? components_.size() : n; + + if (n > 0) { + leaf_path.components_.insert(leaf_path.components_.begin(), + components_.end() - static_cast(n), components_.end()); + } + + return leaf_path; + } + + TOML_EXTERNAL_LINKAGE + path path::subpath(std::vector::const_iterator start, + std::vector::const_iterator end) const { + if (start >= end) return {}; + + path subpath; + subpath.components_.insert(subpath.components_.begin(), start, end); + return subpath; + } + + TOML_EXTERNAL_LINKAGE + path path::subpath(size_t start, size_t length) const { + return subpath(begin() + static_cast(start), begin() + static_cast(start + length)); + } } TOML_NAMESPACE_END; -//#===================================================================================================================== -//# at_path() overloads for toml::path -//#===================================================================================================================== - -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - node_view TOML_CALLCONV at_path(node & root, const toml::path& path) noexcept - { - // early-exit sanity-checks - if (root.is_value()) - return {}; - if (auto tbl = root.as_table(); tbl && tbl->empty()) - return {}; - if (auto arr = root.as_array(); arr && arr->empty()) - return {}; - - node* current = &root; - - for (const auto& component : path) - { - auto type = component.type(); - if (type == path_component_type::array_index) - { - const auto current_array = current->as(); - if (!current_array) - return {}; // not an array, using array index doesn't work - - current = current_array->get(component.index()); - } - else if (type == path_component_type::key) - { - const auto current_table = current->as
(); - if (!current_table) - return {}; - - current = current_table->get(component.key()); - } - else - { - // Error: invalid component - return {}; - } - - if (!current) - return {}; // not found - } - - return node_view{ current }; - } - - TOML_EXTERNAL_LINKAGE - node_view TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept - { - return node_view{ at_path(const_cast(root), path).node() }; - } +// #===================================================================================================================== +// # at_path() overloads for toml::path +// #===================================================================================================================== + +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + node_view TOML_CALLCONV at_path(node & root, const toml::path& path) noexcept { + // early-exit sanity-checks + if (root.is_value()) return {}; + if (auto tbl = root.as_table(); tbl && tbl->empty()) return {}; + if (auto arr = root.as_array(); arr && arr->empty()) return {}; + + node* current = &root; + + for (const auto& component : path) { + auto type = component.type(); + if (type == path_component_type::array_index) { + const auto current_array = current->as(); + if (!current_array) return {}; // not an array, using array index doesn't work + + current = current_array->get(component.index()); + } else if (type == path_component_type::key) { + const auto current_table = current->as
(); + if (!current_table) return {}; + + current = current_table->get(component.key()); + } else { + // Error: invalid component + return {}; + } + + if (!current) return {}; // not found + } + + return node_view{current}; + } + + TOML_EXTERNAL_LINKAGE + node_view TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept { + return node_view{at_path(const_cast(root), path).node()}; + } } TOML_NAMESPACE_END; diff --git a/vendor/toml++/impl/print_to_stream.inl b/vendor/toml++/impl/print_to_stream.inl index 57dbf76..aaeac16 100644 --- a/vendor/toml++/impl/print_to_stream.inl +++ b/vendor/toml++/impl/print_to_stream.inl @@ -1,23 +1,23 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard +// # 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 -//# }} +// # }} +#include "array.hpp" +#include "date_time.hpp" #include "print_to_stream.hpp" #include "source_region.hpp" -#include "date_time.hpp" +#include "table.hpp" #include "toml_formatter.hpp" #include "value.hpp" -#include "array.hpp" -#include "table.hpp" TOML_DISABLE_WARNINGS; #include #if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV @@ -32,456 +32,405 @@ TOML_DISABLE_WARNINGS; TOML_ENABLE_WARNINGS; #include "header_start.hpp" -TOML_ANON_NAMESPACE_START -{ - template - inline constexpr size_t charconv_buffer_length = 0; +TOML_ANON_NAMESPACE_START { + template + inline constexpr size_t charconv_buffer_length = 0; - template <> - inline constexpr size_t charconv_buffer_length = 4; // strlen("-128") + template <> + inline constexpr size_t charconv_buffer_length = 4; // strlen("-128") - template <> - inline constexpr size_t charconv_buffer_length = 6; // strlen("-32768") + template <> + inline constexpr size_t charconv_buffer_length = 6; // strlen("-32768") - template <> - inline constexpr size_t charconv_buffer_length = 11; // strlen("-2147483648") + template <> + inline constexpr size_t charconv_buffer_length = 11; // strlen("-2147483648") - template <> - inline constexpr size_t charconv_buffer_length = 20; // strlen("-9223372036854775808") + template <> + inline constexpr size_t charconv_buffer_length = 20; // strlen("-9223372036854775808") - template <> - inline constexpr size_t charconv_buffer_length = 3; // strlen("255") + template <> + inline constexpr size_t charconv_buffer_length = 3; // strlen("255") - template <> - inline constexpr size_t charconv_buffer_length = 5; // strlen("65535") + template <> + inline constexpr size_t charconv_buffer_length = 5; // strlen("65535") - template <> - inline constexpr size_t charconv_buffer_length = 10; // strlen("4294967295") + template <> + inline constexpr size_t charconv_buffer_length = 10; // strlen("4294967295") - template <> - inline constexpr size_t charconv_buffer_length = 20; // strlen("18446744073709551615") + template <> + inline constexpr size_t charconv_buffer_length = 20; // strlen("18446744073709551615") - template <> - inline constexpr size_t charconv_buffer_length = 64; + template <> + inline constexpr size_t charconv_buffer_length = 64; - template <> - inline constexpr size_t charconv_buffer_length = 64; + template <> + inline constexpr size_t charconv_buffer_length = 64; - template - TOML_INTERNAL_LINKAGE - void print_integer_to_stream(std::ostream & stream, T val, value_flags format = {}, size_t min_digits = 0) - { - if (!val) - { - if (!min_digits) - min_digits = 1; + template + TOML_INTERNAL_LINKAGE void print_integer_to_stream( + std::ostream & stream, T val, value_flags format = {}, size_t min_digits = 0) { + if (!val) { + if (!min_digits) min_digits = 1; - for (size_t i = 0; i < min_digits; i++) - stream.put('0'); + for (size_t i = 0; i < min_digits; i++) stream.put('0'); - return; - } + return; + } - static constexpr auto value_flags_mask = - value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal; - format &= value_flags_mask; + static constexpr auto value_flags_mask = value_flags::format_as_binary | + value_flags::format_as_octal | + value_flags::format_as_hexadecimal; + format &= value_flags_mask; - int base = 10; - if (format != value_flags::none && val > T{}) - { - switch (format) - { - case value_flags::format_as_binary: base = 2; break; - case value_flags::format_as_octal: base = 8; break; - case value_flags::format_as_hexadecimal: base = 16; break; - default: break; - } - } + int base = 10; + if (format != value_flags::none && val > T{}) { + switch (format) { + case value_flags::format_as_binary: + base = 2; + break; + case value_flags::format_as_octal: + base = 8; + break; + case value_flags::format_as_hexadecimal: + base = 16; + break; + default: + break; + } + } #if TOML_INT_CHARCONV - char buf[(sizeof(T) * CHAR_BIT)]; - const auto res = std::to_chars(buf, buf + sizeof(buf), val, base); - const auto len = static_cast(res.ptr - buf); - for (size_t i = len; i < min_digits; i++) - stream.put('0'); - if (base == 16) - { - for (size_t i = 0; i < len; i++) - if (buf[i] >= 'a') - buf[i] -= 32; - } - impl::print_to_stream(stream, buf, len); + char buf[(sizeof(T) * CHAR_BIT)]; + const auto res = std::to_chars(buf, buf + sizeof(buf), val, base); + const auto len = static_cast(res.ptr - buf); + for (size_t i = len; i < min_digits; i++) stream.put('0'); + if (base == 16) { + for (size_t i = 0; i < len; i++) + if (buf[i] >= 'a') buf[i] -= 32; + } + impl::print_to_stream(stream, buf, len); #else - using unsigned_type = std::conditional_t<(sizeof(T) > sizeof(unsigned)), std::make_unsigned_t, unsigned>; - using cast_type = std::conditional_t, std::make_signed_t, unsigned_type>; - - if (base == 2) - { - const auto len = sizeof(T) * CHAR_BIT; - for (size_t i = len; i < min_digits; i++) - stream.put('0'); - - bool found_one = false; - const auto v = static_cast(val); - unsigned_type mask = unsigned_type{ 1 } << (len - 1u); - for (size_t i = 0; i < len; i++) - { - if ((v & mask)) - { - stream.put('1'); - found_one = true; - } - else if (found_one) - stream.put('0'); - mask >>= 1; - } - } - else - { - std::ostringstream ss; - ss.imbue(std::locale::classic()); - ss << std::uppercase << std::setbase(base); - if (min_digits) - ss << std::setfill('0') << std::setw(static_cast(min_digits)); - ss << static_cast(val); - const auto str = std::move(ss).str(); - impl::print_to_stream(stream, str); - } + using unsigned_type = + std::conditional_t<(sizeof(T) > sizeof(unsigned)), std::make_unsigned_t, unsigned>; + using cast_type = + std::conditional_t, std::make_signed_t, unsigned_type>; + + if (base == 2) { + const auto len = sizeof(T) * CHAR_BIT; + for (size_t i = len; i < min_digits; i++) stream.put('0'); + + bool found_one = false; + const auto v = static_cast(val); + unsigned_type mask = unsigned_type{1} << (len - 1u); + for (size_t i = 0; i < len; i++) { + if ((v & mask)) { + stream.put('1'); + found_one = true; + } else if (found_one) + stream.put('0'); + mask >>= 1; + } + } else { + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss << std::uppercase << std::setbase(base); + if (min_digits) ss << std::setfill('0') << std::setw(static_cast(min_digits)); + ss << static_cast(val); + const auto str = std::move(ss).str(); + impl::print_to_stream(stream, str); + } #endif - } - - template - TOML_INTERNAL_LINKAGE - void print_floating_point_to_stream(std::ostream & stream, - T val, - value_flags format, - [[maybe_unused]] bool relaxed_precision) - { - switch (impl::fpclassify(val)) - { - case impl::fp_class::neg_inf: impl::print_to_stream(stream, "-inf"sv); break; - - case impl::fp_class::pos_inf: impl::print_to_stream(stream, "inf"sv); break; - - case impl::fp_class::nan: impl::print_to_stream(stream, "nan"sv); break; - - case impl::fp_class::ok: - { - static constexpr auto needs_decimal_point = [](auto&& s) noexcept - { - for (auto c : s) - if (c == '.' || c == 'E' || c == 'e') - return false; - return true; - }; + } + + template + TOML_INTERNAL_LINKAGE void print_floating_point_to_stream( + std::ostream & stream, T val, value_flags format, [[maybe_unused]] bool relaxed_precision) { + switch (impl::fpclassify(val)) { + case impl::fp_class::neg_inf: + impl::print_to_stream(stream, "-inf"sv); + break; + + case impl::fp_class::pos_inf: + impl::print_to_stream(stream, "inf"sv); + break; + + case impl::fp_class::nan: + impl::print_to_stream(stream, "nan"sv); + break; + + case impl::fp_class::ok: { + static constexpr auto needs_decimal_point = [](auto&& s) noexcept { + for (auto c : s) + if (c == '.' || c == 'E' || c == 'e') return false; + return true; + }; #if TOML_FLOAT_CHARCONV - const auto hex = !!(format & value_flags::format_as_hexadecimal); - char buf[charconv_buffer_length]; - auto res = hex ? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex) - : std::to_chars(buf, buf + sizeof(buf), val); - auto str = std::string_view{ buf, static_cast(res.ptr - buf) }; - - char buf2[charconv_buffer_length]; - if (!hex && relaxed_precision) - { - res = std::to_chars(buf2, buf2 + sizeof(buf2), val, std::chars_format::general, 6); - const auto str2 = std::string_view{ buf2, static_cast(res.ptr - buf2) }; - if (str2.length() < str.length()) - str = str2; - } - - impl::print_to_stream(stream, str); - if (!hex && needs_decimal_point(str)) - toml::impl::print_to_stream(stream, ".0"sv); + const auto hex = !!(format & value_flags::format_as_hexadecimal); + char buf[charconv_buffer_length]; + auto res = hex ? std::to_chars(buf, buf + sizeof(buf), val, std::chars_format::hex) + : std::to_chars(buf, buf + sizeof(buf), val); + auto str = std::string_view{buf, static_cast(res.ptr - buf)}; + + char buf2[charconv_buffer_length]; + if (!hex && relaxed_precision) { + res = std::to_chars(buf2, buf2 + sizeof(buf2), val, std::chars_format::general, 6); + const auto str2 = std::string_view{buf2, static_cast(res.ptr - buf2)}; + if (str2.length() < str.length()) str = str2; + } + + impl::print_to_stream(stream, str); + if (!hex && needs_decimal_point(str)) toml::impl::print_to_stream(stream, ".0"sv); #else - std::ostringstream ss; - ss.imbue(std::locale::classic()); - if (!relaxed_precision) - ss.precision(std::numeric_limits::max_digits10); - if (!!(format & value_flags::format_as_hexadecimal)) - ss << std::hexfloat; - ss << val; - const auto str = std::move(ss).str(); - impl::print_to_stream(stream, str); - if (!(format & value_flags::format_as_hexadecimal) && needs_decimal_point(str)) - impl::print_to_stream(stream, ".0"sv); + std::ostringstream ss; + ss.imbue(std::locale::classic()); + if (!relaxed_precision) ss.precision(std::numeric_limits::max_digits10); + if (!!(format & value_flags::format_as_hexadecimal)) ss << std::hexfloat; + ss << val; + const auto str = std::move(ss).str(); + impl::print_to_stream(stream, str); + if (!(format & value_flags::format_as_hexadecimal) && needs_decimal_point(str)) + impl::print_to_stream(stream, ".0"sv); #endif - } - break; + } break; - default: TOML_UNREACHABLE; - } - } + default: + TOML_UNREACHABLE; + } + } } TOML_ANON_NAMESPACE_END; -TOML_IMPL_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - TOML_ATTR(nonnull) - void TOML_CALLCONV print_to_stream(std::ostream & stream, const char* val, size_t len) - { - stream.write(val, static_cast(len)); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, std::string_view val) - { - stream.write(val.data(), static_cast(val.length())); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const std::string& val) - { - stream.write(val.data(), static_cast(val.length())); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, char val) - { - stream.put(val); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, signed char val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, signed short val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, signed int val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, signed long val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, - signed long long val, - value_flags format, - size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned char val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned short val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned int val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned long val, value_flags format, size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, - unsigned long long val, - value_flags format, - size_t min_digits) - { - TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, float val, value_flags format, bool relaxed_precision) - { - TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, double val, value_flags format, bool relaxed_precision) - { - TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, bool val) - { - print_to_stream(stream, val ? "true"sv : "false"sv); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::date& val) - { - print_to_stream(stream, val.year, {}, 4); - stream.put('-'); - print_to_stream(stream, val.month, {}, 2); - stream.put('-'); - print_to_stream(stream, val.day, {}, 2); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::time& val) - { - print_to_stream(stream, val.hour, {}, 2); - stream.put(':'); - print_to_stream(stream, val.minute, {}, 2); - stream.put(':'); - print_to_stream(stream, val.second, {}, 2); - if (val.nanosecond && val.nanosecond <= 999999999u) - { - stream.put('.'); - auto ns = val.nanosecond; - size_t digits = 9u; - while (ns % 10u == 0u) - { - ns /= 10u; - digits--; - } - print_to_stream(stream, ns, {}, digits); - } - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::time_offset& val) - { - if (!val.minutes) - { - stream.put('Z'); - return; - } - - auto mins = static_cast(val.minutes); - if (mins < 0) - { - stream.put('-'); - mins = -mins; - } - else - stream.put('+'); - const auto hours = mins / 60; - if (hours) - { - print_to_stream(stream, static_cast(hours), {}, 2); - mins -= hours * 60; - } - else - print_to_stream(stream, "00"sv); - stream.put(':'); - print_to_stream(stream, static_cast(mins), {}, 2); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::date_time& val) - { - print_to_stream(stream, val.date); - stream.put('T'); - print_to_stream(stream, val.time); - if (val.offset) - print_to_stream(stream, *val.offset); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_position& val) - { - print_to_stream(stream, "line "sv); - print_to_stream(stream, val.line); - print_to_stream(stream, ", column "sv); - print_to_stream(stream, val.column); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_region& val) - { - print_to_stream(stream, val.begin); - if (val.path) - { - print_to_stream(stream, " of '"sv); - print_to_stream(stream, *val.path); - stream.put('\''); - } - } +TOML_IMPL_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + TOML_ATTR(nonnull) + void TOML_CALLCONV print_to_stream(std::ostream & stream, const char* val, size_t len) { + stream.write(val, static_cast(len)); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, std::string_view val) { + stream.write(val.data(), static_cast(val.length())); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const std::string& val) { + stream.write(val.data(), static_cast(val.length())); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, char val) { + stream.put(val); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, signed char val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, signed short val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, signed int val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, signed long val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, signed long long val, + value_flags format, size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned char val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned short val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned int val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned long val, value_flags format, + size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, unsigned long long val, + value_flags format, size_t min_digits) { + TOML_ANON_NAMESPACE::print_integer_to_stream(stream, val, format, min_digits); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, float val, value_flags format, + bool relaxed_precision) { + TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, double val, value_flags format, + bool relaxed_precision) { + TOML_ANON_NAMESPACE::print_floating_point_to_stream(stream, val, format, relaxed_precision); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, bool val) { + print_to_stream(stream, val ? "true"sv : "false"sv); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::date& val) { + print_to_stream(stream, val.year, {}, 4); + stream.put('-'); + print_to_stream(stream, val.month, {}, 2); + stream.put('-'); + print_to_stream(stream, val.day, {}, 2); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::time& val) { + print_to_stream(stream, val.hour, {}, 2); + stream.put(':'); + print_to_stream(stream, val.minute, {}, 2); + stream.put(':'); + print_to_stream(stream, val.second, {}, 2); + if (val.nanosecond && val.nanosecond <= 999999999u) { + stream.put('.'); + auto ns = val.nanosecond; + size_t digits = 9u; + while (ns % 10u == 0u) { + ns /= 10u; + digits--; + } + print_to_stream(stream, ns, {}, digits); + } + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::time_offset& val) { + if (!val.minutes) { + stream.put('Z'); + return; + } + + auto mins = static_cast(val.minutes); + if (mins < 0) { + stream.put('-'); + mins = -mins; + } else + stream.put('+'); + const auto hours = mins / 60; + if (hours) { + print_to_stream(stream, static_cast(hours), {}, 2); + mins -= hours * 60; + } else + print_to_stream(stream, "00"sv); + stream.put(':'); + print_to_stream(stream, static_cast(mins), {}, 2); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const toml::date_time& val) { + print_to_stream(stream, val.date); + stream.put('T'); + print_to_stream(stream, val.time); + if (val.offset) print_to_stream(stream, *val.offset); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_position& val) { + print_to_stream(stream, "line "sv); + print_to_stream(stream, val.line); + print_to_stream(stream, ", column "sv); + print_to_stream(stream, val.column); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const source_region& val) { + print_to_stream(stream, val.begin); + if (val.path) { + print_to_stream(stream, " of '"sv); + print_to_stream(stream, *val.path); + stream.put('\''); + } + } #if TOML_ENABLE_FORMATTERS - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const array& arr) - { - stream << toml_formatter{ arr }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const table& tbl) - { - stream << toml_formatter{ tbl }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value