diff options
Diffstat (limited to 'vendor/toml++')
| -rw-r--r-- | vendor/toml++/impl/array.inl | 617 | ||||
| -rw-r--r-- | vendor/toml++/impl/at_path.inl | 472 | ||||
| -rw-r--r-- | vendor/toml++/impl/formatter.inl | 970 | ||||
| -rw-r--r-- | vendor/toml++/impl/json_formatter.inl | 187 | ||||
| -rw-r--r-- | vendor/toml++/impl/node.inl | 218 | ||||
| -rw-r--r-- | vendor/toml++/impl/parser.inl | 6938 | ||||
| -rw-r--r-- | vendor/toml++/impl/path.inl | 822 | ||||
| -rw-r--r-- | vendor/toml++/impl/print_to_stream.inl | 791 | ||||
| -rw-r--r-- | vendor/toml++/impl/std_string.inl | 116 | ||||
| -rw-r--r-- | vendor/toml++/impl/table.inl | 498 | ||||
| -rw-r--r-- | vendor/toml++/impl/toml_formatter.inl | 731 | ||||
| -rw-r--r-- | vendor/toml++/impl/unicode.inl | 70 | ||||
| -rw-r--r-- | vendor/toml++/impl/yaml_formatter.inl | 299 |
13 files changed, 5959 insertions, 6770 deletions
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 <mark.gillard@outlook.com.au> -//# 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 <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #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<array&>(*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<array&>(*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<array*>(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<ptrdiff_t>(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<ptrdiff_t>(i)); - } - else if (auto tbl = elems_[i]->as_table()) - { - if (recursive) - tbl->prune(true); - if (tbl->empty()) - elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(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<std::remove_reference_t<decltype(lhs_)>*>(&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<array*>(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<ptrdiff_t>(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<ptrdiff_t>(i)); + } else if (auto tbl = elems_[i]->as_table()) { + if (recursive) tbl->prune(true); + if (tbl->empty()) elems_.erase(elems_.cbegin() + static_cast<ptrdiff_t>(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<std::remove_reference_t<decltype(lhs_)>*>(&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 <mark.gillard@outlook.com.au> -//# 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 <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} -#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<std::string_view> on_key, - const parse_path_callback<size_t> 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<size_t>(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<std::string_view> on_key, + const parse_path_callback<size_t> 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<size_t>(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<std::streamsize>(index_str.length())); - if (!(ss >> index)) - return false; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss.write(index_str.data(), static_cast<std::streamsize>(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<node> 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<node**>(data); - TOML_ASSERT_ASSUME(curr); - - const auto current_table = curr->as<table>(); - 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<node**>(data); - TOML_ASSERT_ASSUME(curr); - - const auto current_array = curr->as<array>(); - 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<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept - { - return node_view<const node>{ at_path(const_cast<node&>(root), path).node() }; - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + node_view<node> 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<node**>(data); + TOML_ASSERT_ASSUME(curr); + + const auto current_table = curr->as<table>(); + 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<node**>(data); + TOML_ASSERT_ASSUME(curr); + + const auto current_array = curr->as<array>(); + 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<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept { + return node_view<const node>{at_path(const_cast<node&>(root), path).node()}; + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - node_view<node> 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<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path) - { - return node_view<const node>{ at_path(const_cast<node&>(root), path).node() }; - } - -#endif // TOML_ENABLE_WINDOWS_COMPAT + TOML_EXTERNAL_LINKAGE + node_view<node> 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<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path) { + return node_view<const node>{at_path(const_cast<node&>(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 <mark.gillard@outlook.com.au> -//# 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 <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once #include "preprocessor.hpp" -//# {{ +// # {{ #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #if TOML_ENABLE_FORMATTERS +#include "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<char32_t>(c))) - traits |= formatted_string_traits::non_bare; - break; - } - } - - static constexpr auto all_ascii_traits = - formatted_string_traits::all & ~formatted_string_traits::non_ascii; - if (traits == all_ascii_traits) - break; - } - } - - // unicode slow path - else - { - traits |= formatted_string_traits::non_ascii; - utf8_decoder decoder; - - // if the unicode is malformed just treat the string as a single-line non-literal and - // escape all non-ascii characters (to ensure round-tripping and help with diagnostics) - const auto bad_unicode = [&]() noexcept - { - traits &= ~formatted_string_traits::line_breaks; - traits |= formatted_string_traits::control_chars | formatted_string_traits::non_bare; - unicode_allowed = false; - }; - - for (auto c : str) - { - decoder(c); - - if TOML_UNLIKELY(decoder.error()) - { - bad_unicode(); - break; - } - - if (!decoder.has_code_point()) - continue; - - switch (decoder.codepoint) - { - case U'\n': traits |= formatted_string_traits::line_breaks; break; - case U'\t': traits |= formatted_string_traits::tabs; break; - case U'\'': traits |= formatted_string_traits::single_quotes; break; - default: - { - if TOML_UNLIKELY(is_control_character(decoder.codepoint) - || is_non_ascii_vertical_whitespace(decoder.codepoint)) - traits |= formatted_string_traits::control_chars; - - if (!is_bare_key_character(decoder.codepoint)) - traits |= formatted_string_traits::non_bare; - break; - } - } - } - - if (decoder.needs_more_input()) - bad_unicode(); - } - - // strings with line breaks, tabs, and single-quotes can't be bare - if (!!(traits - & (formatted_string_traits::line_breaks | formatted_string_traits::tabs - | formatted_string_traits::single_quotes))) - traits |= formatted_string_traits::non_bare; - - // if the string meets the requirements of being 'bare' we can emit a bare string - // (bare strings are composed of letters and numbers; no whitespace, control chars, quotes, etc) - if (!(traits & formatted_string_traits::non_bare) - && (!(traits & formatted_string_traits::non_ascii) || unicode_allowed)) - { - print_unformatted(str); - return; - } - const auto real_tabs_allowed = allow_literal_whitespace && real_tabs_in_strings_allowed(); - - // determine if this should be a multi-line string (triple-quotes) - const auto multi_line = allow_literal_whitespace // - && allow_multi_line // - && multi_line_strings_allowed() // - && !!(traits & formatted_string_traits::line_breaks); - - // determine if this should be a literal string (single-quotes with no escaping) - const auto literal = literal_strings_allowed() // - && !(traits & formatted_string_traits::control_chars) // - && (!(traits & formatted_string_traits::single_quotes) || multi_line) // - && (!(traits & formatted_string_traits::tabs) || real_tabs_allowed) // - && (!(traits & formatted_string_traits::line_breaks) || multi_line) // - && (!(traits & formatted_string_traits::non_ascii) || unicode_allowed); - - // literal strings (single quotes, no escape codes) - if (literal) - { - const auto quot = multi_line ? R"(''')"sv : R"(')"sv; - print_unformatted(quot); - print_unformatted(str); - print_unformatted(quot); - return; - } - - // anything from here down is a non-literal string, so requires iteration and escaping. - print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); - - // ascii fast path - if (!(traits & formatted_string_traits::non_ascii)) - { - for (auto c : str) - { - switch (c) - { - case '"': print_to_stream(*stream_, R"(\")"sv); break; - case '\\': print_to_stream(*stream_, R"(\\)"sv); break; - case '\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break; - case '\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break; - case '\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break; - default: - { - // control characters from lookup table - if TOML_UNLIKELY(c >= '\x00' && c <= '\x1F') - print_to_stream(*stream_, control_char_escapes[c]); - - // regular characters - else - print_to_stream(*stream_, c); - } - } - } - } - - // unicode slow path - else - { - utf8_decoder decoder; - const char* cp_start = str.data(); - const char* cp_end = cp_start; - for (auto c : str) - { - decoder(c); - cp_end++; - - // if the decoder encounters malformed unicode just emit raw bytes and - if (decoder.error()) - { - while (cp_start != cp_end) - { - print_to_stream(*stream_, R"(\u00)"sv); - print_to_stream(*stream_, - static_cast<uint8_t>(*cp_start), - value_flags::format_as_hexadecimal, - 2); - cp_start++; - } - decoder.reset(); - continue; - } - - if (!decoder.has_code_point()) - continue; - - switch (decoder.codepoint) - { - case U'"': print_to_stream(*stream_, R"(\")"sv); break; - case U'\\': print_to_stream(*stream_, R"(\\)"sv); break; - case U'\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break; - case U'\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break; - case U'\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break; - default: - { - // control characters from lookup table - if TOML_UNLIKELY(decoder.codepoint <= U'\x1F') - print_to_stream(*stream_, - control_char_escapes[static_cast<uint_least32_t>(decoder.codepoint)]); - - // escaped unicode characters - else if (decoder.codepoint > U'\x7F' - && (!unicode_allowed || is_non_ascii_vertical_whitespace(decoder.codepoint))) - { - if (static_cast<uint_least32_t>(decoder.codepoint) > 0xFFFFu) - { - print_to_stream(*stream_, R"(\U)"sv); - print_to_stream(*stream_, - static_cast<uint_least32_t>(decoder.codepoint), - value_flags::format_as_hexadecimal, - 8); - } - else - { - print_to_stream(*stream_, R"(\u)"sv); - print_to_stream(*stream_, - static_cast<uint_least32_t>(decoder.codepoint), - value_flags::format_as_hexadecimal, - 4); - } - } - - // regular characters - else - print_to_stream(*stream_, cp_start, static_cast<size_t>(cp_end - cp_start)); - } - } - - cp_start = cp_end; - } - } - - print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<std::string>& val) - { - print_string(val.get()); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<int64_t>& val) - { - naked_newline_ = false; - - if (*val >= 0 && !!int_format_mask_) - { - static constexpr auto value_flags_mask = - value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal; - - const auto fmt = val.flags() & value_flags_mask; - switch (fmt) - { - case value_flags::format_as_binary: - if (!!(int_format_mask_ & format_flags::allow_binary_integers)) - { - print_to_stream(*stream_, "0b"sv); - print_to_stream(*stream_, *val, fmt); - return; - } - break; - - case value_flags::format_as_octal: - if (!!(int_format_mask_ & format_flags::allow_octal_integers)) - { - print_to_stream(*stream_, "0o"sv); - print_to_stream(*stream_, *val, fmt); - return; - } - break; - - case value_flags::format_as_hexadecimal: - if (!!(int_format_mask_ & format_flags::allow_hexadecimal_integers)) - { - print_to_stream(*stream_, "0x"sv); - print_to_stream(*stream_, *val, fmt); - return; - } - break; - - default: break; - } - } - - // fallback to decimal - print_to_stream(*stream_, *val); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<double>& val) - { - const std::string_view* inf_nan = nullptr; - switch (fpclassify(*val)) - { - case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break; - case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break; - case fp_class::nan: inf_nan = &constants_->float_nan; break; - case fp_class::ok: - print_to_stream(*stream_, - *val, - value_flags::none, - !!(config_.flags & format_flags::relaxed_float_precision)); - break; - default: TOML_UNREACHABLE; - } - - if (inf_nan) - { - if (!!(config_.flags & format_flags::quote_infinities_and_nans)) - print_to_stream_bookended(*stream_, *inf_nan, '"'); - else - print_to_stream(*stream_, *inf_nan); - } - - naked_newline_ = false; - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<bool>& val) - { - print_unformatted(*val ? constants_->bool_true : constants_->bool_false); - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<date>& val) - { - if (!!(config_.flags & format_flags::quote_dates_and_times)) - print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); - else - print_to_stream(*stream_, *val); - naked_newline_ = false; - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<time>& val) - { - if (!!(config_.flags & format_flags::quote_dates_and_times)) - print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); - else - print_to_stream(*stream_, *val); - naked_newline_ = false; - } - - TOML_EXTERNAL_LINKAGE - void formatter::print(const value<date_time>& val) - { - if (!!(config_.flags & format_flags::quote_dates_and_times)) - print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); - else - print_to_stream(*stream_, *val); - naked_newline_ = false; - } - - TOML_EXTERNAL_LINKAGE - void formatter::print_value(const node& val_node, node_type type) - { - TOML_ASSUME(type > node_type::array); - switch (type) - { - case node_type::string: print(*reinterpret_cast<const value<std::string>*>(&val_node)); break; - case node_type::integer: print(*reinterpret_cast<const value<int64_t>*>(&val_node)); break; - case node_type::floating_point: print(*reinterpret_cast<const value<double>*>(&val_node)); break; - case node_type::boolean: print(*reinterpret_cast<const value<bool>*>(&val_node)); break; - case node_type::date: print(*reinterpret_cast<const value<date>*>(&val_node)); break; - case node_type::time: print(*reinterpret_cast<const value<time>*>(&val_node)); break; - case node_type::date_time: print(*reinterpret_cast<const value<date_time>*>(&val_node)); break; - default: TOML_UNREACHABLE; - } - } + constants_{&constants}, + config_{config} { + TOML_ASSERT_ASSUME(source_); + + config_.flags = (config_.flags | constants_->mandatory_flags) & ~constants_->ignored_flags; + + indent_columns_ = {}; + for (auto c : config_.indent) indent_columns_ += c == '\t' ? 4u : 1u; + + int_format_mask_ = + config_.flags & (format_flags::allow_binary_integers | format_flags::allow_octal_integers | + format_flags::allow_hexadecimal_integers); + } + + TOML_EXTERNAL_LINKAGE + void formatter::attach(std::ostream & stream) noexcept { + indent_ = {}; + naked_newline_ = true; + stream_ = &stream; + } + + TOML_EXTERNAL_LINKAGE + void formatter::detach() noexcept { + stream_ = nullptr; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_newline(bool force) { + if (!naked_newline_ || force) { + print_to_stream(*stream_, '\n'); + naked_newline_ = true; + } + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_indent() { + for (int i = 0; i < indent_; i++) { + print_to_stream(*stream_, config_.indent); + naked_newline_ = false; + } + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_unformatted(char c) { + print_to_stream(*stream_, c); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_unformatted(std::string_view str) { + print_to_stream(*stream_, str); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_string(std::string_view str, bool allow_multi_line, bool allow_bare, + bool allow_literal_whitespace) { + if (str.empty()) { + print_unformatted(literal_strings_allowed() ? "''"sv : "\"\""sv); + return; + } + + // pre-scan the string to determine how we should output it + formatted_string_traits traits = {}; + + if (!allow_bare) traits |= formatted_string_traits::non_bare; + bool unicode_allowed = unicode_strings_allowed(); + + // ascii fast path + if (is_ascii(str.data(), str.length())) { + for (auto c : str) { + switch (c) { + case '\n': + traits |= formatted_string_traits::line_breaks; + break; + case '\t': + traits |= formatted_string_traits::tabs; + break; + case '\'': + traits |= formatted_string_traits::single_quotes; + break; + default: { + if TOML_UNLIKELY (is_control_character(c)) + traits |= formatted_string_traits::control_chars; + + if (!is_ascii_bare_key_character(static_cast<char32_t>(c))) + traits |= formatted_string_traits::non_bare; + break; + } + } + + static constexpr auto all_ascii_traits = + formatted_string_traits::all & ~formatted_string_traits::non_ascii; + if (traits == all_ascii_traits) break; + } + } + + // unicode slow path + else { + traits |= formatted_string_traits::non_ascii; + utf8_decoder decoder; + + // if the unicode is malformed just treat the string as a single-line non-literal and + // escape all non-ascii characters (to ensure round-tripping and help with diagnostics) + const auto bad_unicode = [&]() noexcept { + traits &= ~formatted_string_traits::line_breaks; + traits |= formatted_string_traits::control_chars | formatted_string_traits::non_bare; + unicode_allowed = false; + }; + + for (auto c : str) { + decoder(c); + + if TOML_UNLIKELY (decoder.error()) { + bad_unicode(); + break; + } + + if (!decoder.has_code_point()) continue; + + switch (decoder.codepoint) { + case U'\n': + traits |= formatted_string_traits::line_breaks; + break; + case U'\t': + traits |= formatted_string_traits::tabs; + break; + case U'\'': + traits |= formatted_string_traits::single_quotes; + break; + default: { + if TOML_UNLIKELY (is_control_character(decoder.codepoint) || + is_non_ascii_vertical_whitespace(decoder.codepoint)) + traits |= formatted_string_traits::control_chars; + + if (!is_bare_key_character(decoder.codepoint)) + traits |= formatted_string_traits::non_bare; + break; + } + } + } + + if (decoder.needs_more_input()) bad_unicode(); + } + + // strings with line breaks, tabs, and single-quotes can't be bare + if (!!(traits & (formatted_string_traits::line_breaks | formatted_string_traits::tabs | + formatted_string_traits::single_quotes))) + traits |= formatted_string_traits::non_bare; + + // if the string meets the requirements of being 'bare' we can emit a bare string + // (bare strings are composed of letters and numbers; no whitespace, control chars, quotes, etc) + if (!(traits & formatted_string_traits::non_bare) && + (!(traits & formatted_string_traits::non_ascii) || unicode_allowed)) { + print_unformatted(str); + return; + } + const auto real_tabs_allowed = allow_literal_whitespace && real_tabs_in_strings_allowed(); + + // determine if this should be a multi-line string (triple-quotes) + const auto multi_line = allow_literal_whitespace // + && allow_multi_line // + && multi_line_strings_allowed() // + && !!(traits & formatted_string_traits::line_breaks); + + // determine if this should be a literal string (single-quotes with no escaping) + const auto literal = literal_strings_allowed() // + && !(traits & formatted_string_traits::control_chars) // + && (!(traits & formatted_string_traits::single_quotes) || multi_line) // + && (!(traits & formatted_string_traits::tabs) || real_tabs_allowed) // + && (!(traits & formatted_string_traits::line_breaks) || multi_line) // + && (!(traits & formatted_string_traits::non_ascii) || unicode_allowed); + + // literal strings (single quotes, no escape codes) + if (literal) { + const auto quot = multi_line ? R"(''')"sv : R"(')"sv; + print_unformatted(quot); + print_unformatted(str); + print_unformatted(quot); + return; + } + + // anything from here down is a non-literal string, so requires iteration and escaping. + print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); + + // ascii fast path + if (!(traits & formatted_string_traits::non_ascii)) { + for (auto c : str) { + switch (c) { + case '"': + print_to_stream(*stream_, R"(\")"sv); + break; + case '\\': + print_to_stream(*stream_, R"(\\)"sv); + break; + case '\x7F': + print_to_stream(*stream_, R"(\u007F)"sv); + break; + case '\t': + print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); + break; + case '\n': + print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); + break; + default: { + // control characters from lookup table + if TOML_UNLIKELY (c >= '\x00' && c <= '\x1F') + print_to_stream(*stream_, control_char_escapes[c]); + + // regular characters + else + print_to_stream(*stream_, c); + } + } + } + } + + // unicode slow path + else { + utf8_decoder decoder; + const char* cp_start = str.data(); + const char* cp_end = cp_start; + for (auto c : str) { + decoder(c); + cp_end++; + + // if the decoder encounters malformed unicode just emit raw bytes and + if (decoder.error()) { + while (cp_start != cp_end) { + print_to_stream(*stream_, R"(\u00)"sv); + print_to_stream(*stream_, static_cast<uint8_t>(*cp_start), + value_flags::format_as_hexadecimal, 2); + cp_start++; + } + decoder.reset(); + continue; + } + + if (!decoder.has_code_point()) continue; + + switch (decoder.codepoint) { + case U'"': + print_to_stream(*stream_, R"(\")"sv); + break; + case U'\\': + print_to_stream(*stream_, R"(\\)"sv); + break; + case U'\x7F': + print_to_stream(*stream_, R"(\u007F)"sv); + break; + case U'\t': + print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); + break; + case U'\n': + print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); + break; + default: { + // control characters from lookup table + if TOML_UNLIKELY (decoder.codepoint <= U'\x1F') + print_to_stream(*stream_, + control_char_escapes[static_cast<uint_least32_t>(decoder.codepoint)]); + + // escaped unicode characters + else if (decoder.codepoint > U'\x7F' && + (!unicode_allowed || is_non_ascii_vertical_whitespace(decoder.codepoint))) { + if (static_cast<uint_least32_t>(decoder.codepoint) > 0xFFFFu) { + print_to_stream(*stream_, R"(\U)"sv); + print_to_stream(*stream_, static_cast<uint_least32_t>(decoder.codepoint), + value_flags::format_as_hexadecimal, 8); + } else { + print_to_stream(*stream_, R"(\u)"sv); + print_to_stream(*stream_, static_cast<uint_least32_t>(decoder.codepoint), + value_flags::format_as_hexadecimal, 4); + } + } + + // regular characters + else + print_to_stream(*stream_, cp_start, static_cast<size_t>(cp_end - cp_start)); + } + } + + cp_start = cp_end; + } + } + + print_unformatted(multi_line ? R"(""")"sv : R"(")"sv); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<std::string>& val) { + print_string(val.get()); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<int64_t>& val) { + naked_newline_ = false; + + if (*val >= 0 && !!int_format_mask_) { + static constexpr auto value_flags_mask = value_flags::format_as_binary | + value_flags::format_as_octal | + value_flags::format_as_hexadecimal; + + const auto fmt = val.flags() & value_flags_mask; + switch (fmt) { + case value_flags::format_as_binary: + if (!!(int_format_mask_ & format_flags::allow_binary_integers)) { + print_to_stream(*stream_, "0b"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + case value_flags::format_as_octal: + if (!!(int_format_mask_ & format_flags::allow_octal_integers)) { + print_to_stream(*stream_, "0o"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + case value_flags::format_as_hexadecimal: + if (!!(int_format_mask_ & format_flags::allow_hexadecimal_integers)) { + print_to_stream(*stream_, "0x"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + default: + break; + } + } + + // fallback to decimal + print_to_stream(*stream_, *val); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<double>& val) { + const std::string_view* inf_nan = nullptr; + switch (fpclassify(*val)) { + case fp_class::neg_inf: + inf_nan = &constants_->float_neg_inf; + break; + case fp_class::pos_inf: + inf_nan = &constants_->float_pos_inf; + break; + case fp_class::nan: + inf_nan = &constants_->float_nan; + break; + case fp_class::ok: + print_to_stream(*stream_, *val, value_flags::none, + !!(config_.flags & format_flags::relaxed_float_precision)); + break; + default: + TOML_UNREACHABLE; + } + + if (inf_nan) { + if (!!(config_.flags & format_flags::quote_infinities_and_nans)) + print_to_stream_bookended(*stream_, *inf_nan, '"'); + else + print_to_stream(*stream_, *inf_nan); + } + + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<bool>& val) { + print_unformatted(*val ? constants_->bool_true : constants_->bool_false); + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<date>& val) { + if (!!(config_.flags & format_flags::quote_dates_and_times)) + print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); + else + print_to_stream(*stream_, *val); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<time>& val) { + if (!!(config_.flags & format_flags::quote_dates_and_times)) + print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); + else + print_to_stream(*stream_, *val); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print(const value<date_time>& val) { + if (!!(config_.flags & format_flags::quote_dates_and_times)) + print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); + else + print_to_stream(*stream_, *val); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_value(const node& val_node, node_type type) { + TOML_ASSUME(type > node_type::array); + switch (type) { + case node_type::string: + print(*reinterpret_cast<const value<std::string>*>(&val_node)); + break; + case node_type::integer: + print(*reinterpret_cast<const value<int64_t>*>(&val_node)); + break; + case node_type::floating_point: + print(*reinterpret_cast<const value<double>*>(&val_node)); + break; + case node_type::boolean: + print(*reinterpret_cast<const value<bool>*>(&val_node)); + break; + case node_type::date: + print(*reinterpret_cast<const value<date>*>(&val_node)); + break; + case node_type::time: + print(*reinterpret_cast<const value<time>*>(&val_node)); + break; + case node_type::date_time: + print(*reinterpret_cast<const value<date_time>*>(&val_node)); + break; + default: + TOML_UNREACHABLE; + } + } #if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS - TOML_EXTERNAL_LINKAGE - bool formatter::dump_failed_parse_result() - { - if (result_ && !(*result_)) - { - stream() << result_->error(); - return true; - } - return false; - } + TOML_EXTERNAL_LINKAGE + bool formatter::dump_failed_parse_result() { + if (result_ && !(*result_)) { + stream() << result_->error(); + return true; + } + return false; + } #else - TOML_EXTERNAL_LINKAGE - TOML_ATTR(const) - bool formatter::dump_failed_parse_result() - { - return false; - } + TOML_EXTERNAL_LINKAGE + TOML_ATTR(const) + bool formatter::dump_failed_parse_result() { + return false; + } #endif } TOML_IMPL_NAMESPACE_END; #include "header_end.hpp" -#endif // TOML_ENABLE_FORMATTERS +#endif // TOML_ENABLE_FORMATTERS diff --git a/vendor/toml++/impl/json_formatter.inl b/vendor/toml++/impl/json_formatter.inl index ceffbd4..16bffc9 100644 --- a/vendor/toml++/impl/json_formatter.inl +++ b/vendor/toml++/impl/json_formatter.inl @@ -1,121 +1,118 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once #include "preprocessor.hpp" -//# {{ +// # {{ #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #if TOML_ENABLE_FORMATTERS +#include "array.hpp" +#include "header_start.hpp" #include "json_formatter.hpp" #include "print_to_stream.hpp" #include "table.hpp" -#include "array.hpp" -#include "header_start.hpp" -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - void json_formatter::print(const toml::table& tbl) - { - if (tbl.empty()) - { - print_unformatted("{}"sv); - return; - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + void json_formatter::print(const toml::table& tbl) { + if (tbl.empty()) { + print_unformatted("{}"sv); + return; + } - print_unformatted('{'); + print_unformatted('{'); - if (indent_sub_tables()) - increase_indent(); - bool first = false; - for (auto&& [k, v] : tbl) - { - if (first) - print_unformatted(','); - first = true; - print_newline(true); - print_indent(); + if (indent_sub_tables()) increase_indent(); + bool first = false; + for (auto&& [k, v] : tbl) { + if (first) print_unformatted(','); + first = true; + print_newline(true); + print_indent(); - print_string(k.str(), false); - if (terse_kvps()) - print_unformatted(":"sv); - else - print_unformatted(" : "sv); + print_string(k.str(), false); + if (terse_kvps()) + print_unformatted(":"sv); + else + print_unformatted(" : "sv); - const auto type = v.type(); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print(*reinterpret_cast<const table*>(&v)); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v)); break; - default: print_value(v, type); - } - } - if (indent_sub_tables()) - decrease_indent(); - print_newline(true); - print_indent(); + const auto type = v.type(); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print(*reinterpret_cast<const table*>(&v)); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v)); + break; + default: + print_value(v, type); + } + } + if (indent_sub_tables()) decrease_indent(); + print_newline(true); + print_indent(); - print_unformatted('}'); - } + print_unformatted('}'); + } - TOML_EXTERNAL_LINKAGE - void json_formatter::print(const toml::array& arr) - { - if (arr.empty()) - { - print_unformatted("[]"sv); - return; - } + TOML_EXTERNAL_LINKAGE + void json_formatter::print(const toml::array& arr) { + if (arr.empty()) { + print_unformatted("[]"sv); + return; + } - print_unformatted('['); - if (indent_array_elements()) - increase_indent(); - for (size_t i = 0; i < arr.size(); i++) - { - if (i > 0u) - print_unformatted(','); - print_newline(true); - print_indent(); + print_unformatted('['); + if (indent_array_elements()) increase_indent(); + for (size_t i = 0; i < arr.size(); i++) { + if (i > 0u) print_unformatted(','); + print_newline(true); + print_indent(); - auto& v = arr[i]; - const auto type = v.type(); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print(*reinterpret_cast<const table*>(&v)); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v)); break; - default: print_value(v, type); - } - } - if (indent_array_elements()) - decrease_indent(); - print_newline(true); - print_indent(); - print_unformatted(']'); - } + auto& v = arr[i]; + const auto type = v.type(); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print(*reinterpret_cast<const table*>(&v)); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v)); + break; + default: + print_value(v, type); + } + } + if (indent_array_elements()) decrease_indent(); + print_newline(true); + print_indent(); + print_unformatted(']'); + } - TOML_EXTERNAL_LINKAGE - void json_formatter::print() - { - if (dump_failed_parse_result()) - return; + TOML_EXTERNAL_LINKAGE + void json_formatter::print() { + if (dump_failed_parse_result()) return; - switch (auto source_type = source().type()) - { - case node_type::table: print(*reinterpret_cast<const table*>(&source())); break; - case node_type::array: print(*reinterpret_cast<const array*>(&source())); break; - default: print_value(source(), source_type); - } - } + switch (auto source_type = source().type()) { + case node_type::table: + print(*reinterpret_cast<const table*>(&source())); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&source())); + break; + default: + print_value(source(), source_type); + } + } } TOML_NAMESPACE_END; #include "header_end.hpp" -#endif // TOML_ENABLE_FORMATTERS +#endif // TOML_ENABLE_FORMATTERS diff --git a/vendor/toml++/impl/node.inl b/vendor/toml++/impl/node.inl index 171e05d..060832b 100644 --- a/vendor/toml++/impl/node.inl +++ b/vendor/toml++/impl/node.inl @@ -1,140 +1,120 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} +#include "array.hpp" +#include "at_path.hpp" +#include "header_start.hpp" #include "node.hpp" #include "node_view.hpp" -#include "at_path.hpp" #include "table.hpp" -#include "array.hpp" #include "value.hpp" -#include "header_start.hpp" -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - node::node() noexcept = default; - - TOML_EXTERNAL_LINKAGE - node::~node() noexcept = default; - - TOML_EXTERNAL_LINKAGE - node::node(node && other) noexcept // - : source_{ std::exchange(other.source_, {}) } - {} - - TOML_EXTERNAL_LINKAGE - node::node(const node& /*other*/) noexcept - { - // does not copy source information - this is not an error - // - // see https://github.com/marzer/tomlplusplus/issues/49#issuecomment-665089577 - } - - TOML_EXTERNAL_LINKAGE - node& node::operator=(const node& /*rhs*/) noexcept - { - // does not copy source information - this is not an error - // - // see https://github.com/marzer/tomlplusplus/issues/49#issuecomment-665089577 - - source_ = {}; - return *this; - } - - TOML_EXTERNAL_LINKAGE - node& node::operator=(node&& rhs) noexcept - { - if (&rhs != this) - source_ = std::exchange(rhs.source_, {}); - return *this; - } - - TOML_EXTERNAL_LINKAGE - node_view<node> node::at_path(std::string_view path) noexcept - { - return toml::at_path(*this, path); - } - - TOML_EXTERNAL_LINKAGE - node_view<const node> node::at_path(std::string_view path) const noexcept - { - return toml::at_path(*this, path); - } - - TOML_EXTERNAL_LINKAGE - node_view<node> node::at_path(const path& p) noexcept - { - return toml::at_path(*this, p); - } - - TOML_EXTERNAL_LINKAGE - node_view<const node> node::at_path(const path& p) const noexcept - { - return toml::at_path(*this, p); - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + node::node() noexcept = default; + + TOML_EXTERNAL_LINKAGE + node::~node() noexcept = default; + + TOML_EXTERNAL_LINKAGE + node::node(node && other) noexcept // + : source_{std::exchange(other.source_, {})} {} + + TOML_EXTERNAL_LINKAGE + node::node(const node& /*other*/) noexcept { + // does not copy source information - this is not an error + // + // see https://github.com/marzer/tomlplusplus/issues/49#issuecomment-665089577 + } + + TOML_EXTERNAL_LINKAGE + node& node::operator=(const node& /*rhs*/) noexcept { + // does not copy source information - this is not an error + // + // see https://github.com/marzer/tomlplusplus/issues/49#issuecomment-665089577 + + source_ = {}; + return *this; + } + + TOML_EXTERNAL_LINKAGE + node& node::operator=(node&& rhs) noexcept { + if (&rhs != this) source_ = std::exchange(rhs.source_, {}); + return *this; + } + + TOML_EXTERNAL_LINKAGE + node_view<node> node::at_path(std::string_view path) noexcept { + return toml::at_path(*this, path); + } + + TOML_EXTERNAL_LINKAGE + node_view<const node> node::at_path(std::string_view path) const noexcept { + return toml::at_path(*this, path); + } + + TOML_EXTERNAL_LINKAGE + node_view<node> node::at_path(const path& p) noexcept { + return toml::at_path(*this, p); + } + + TOML_EXTERNAL_LINKAGE + node_view<const node> node::at_path(const path& p) const noexcept { + return toml::at_path(*this, p); + } #if TOML_ENABLE_WINDOWS_COMPAT - TOML_EXTERNAL_LINKAGE - node_view<node> node::at_path(std::wstring_view path) - { - return toml::at_path(*this, path); - } - - TOML_EXTERNAL_LINKAGE - node_view<const node> node::at_path(std::wstring_view path) const - { - return toml::at_path(*this, path); - } - -#endif // TOML_ENABLE_WINDOWS_COMPAT - - TOML_EXTERNAL_LINKAGE - node_view<node> node::operator[](const path& p) noexcept - { - return toml::at_path(*this, p); - } - - TOML_EXTERNAL_LINKAGE - node_view<const node> node::operator[](const path& p) const noexcept - { - return toml::at_path(*this, p); - } + TOML_EXTERNAL_LINKAGE + node_view<node> node::at_path(std::wstring_view path) { + return toml::at_path(*this, path); + } + + TOML_EXTERNAL_LINKAGE + node_view<const node> node::at_path(std::wstring_view path) const { + return toml::at_path(*this, path); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node_view<node> node::operator[](const path& p) noexcept { + return toml::at_path(*this, p); + } + + TOML_EXTERNAL_LINKAGE + node_view<const node> node::operator[](const path& p) const noexcept { + return toml::at_path(*this, p); + } } TOML_NAMESPACE_END; -TOML_IMPL_NAMESPACE_START -{ - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool TOML_CALLCONV node_deep_equality(const node* lhs, const node* rhs) noexcept - { - // both same or both null - if (lhs == rhs) - return true; - - // lhs null != rhs null or different types - if ((!lhs != !rhs) || lhs->type() != rhs->type()) - return false; - - return lhs->visit( - [=](auto& l) noexcept - { - using concrete_type = remove_cvref<decltype(l)>; - - return l == *(rhs->as<concrete_type>()); - }); - } +TOML_IMPL_NAMESPACE_START { + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool TOML_CALLCONV node_deep_equality(const node* lhs, const node* rhs) noexcept { + // both same or both null + if (lhs == rhs) return true; + + // lhs null != rhs null or different types + if ((!lhs != !rhs) || lhs->type() != rhs->type()) return false; + + return lhs->visit([=](auto& l) noexcept { + using concrete_type = remove_cvref<decltype(l)>; + + return l == *(rhs->as<concrete_type>()); + }); + } } TOML_IMPL_NAMESPACE_END; diff --git a/vendor/toml++/impl/parser.inl b/vendor/toml++/impl/parser.inl index 6f0136f..1530e5a 100644 --- a/vendor/toml++/impl/parser.inl +++ b/vendor/toml++/impl/parser.inl @@ -1,29 +1,29 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once #include "preprocessor.hpp" -//# {{ +// # {{ #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #if TOML_ENABLE_PARSER +#include "array.hpp" +#include "date_time.hpp" +#include "parse_error.hpp" #include "parser.hpp" -#include "std_optional.hpp" #include "source_region.hpp" -#include "parse_error.hpp" -#include "date_time.hpp" -#include "value.hpp" -#include "array.hpp" +#include "std_optional.hpp" #include "table.hpp" #include "unicode.hpp" +#include "value.hpp" TOML_DISABLE_WARNINGS; -#include <istream> #include <fstream> +#include <istream> #if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV #include <charconv> #endif @@ -36,190 +36,153 @@ TOML_DISABLE_WARNINGS; TOML_ENABLE_WARNINGS; #include "header_start.hpp" -//#--------------------------------------------------------------------------------------------------------------------- -//# UTF8 STREAMS -//#--------------------------------------------------------------------------------------------------------------------- - -TOML_ANON_NAMESPACE_START -{ - template <typename T> - class utf8_byte_stream; - - TOML_INTERNAL_LINKAGE - constexpr auto utf8_byte_order_mark = "\xEF\xBB\xBF"sv; - - template <typename Char> - class utf8_byte_stream<std::basic_string_view<Char>> - { - static_assert(sizeof(Char) == 1); - - private: - std::basic_string_view<Char> source_; - size_t position_ = {}; - - public: - TOML_NODISCARD_CTOR - explicit constexpr utf8_byte_stream(std::basic_string_view<Char> sv) noexcept // - : source_{ sv } - { - // skip bom - if (source_.length() >= 3u && memcmp(utf8_byte_order_mark.data(), source_.data(), 3u) == 0) - position_ += 3u; - } - - TOML_CONST_INLINE_GETTER - constexpr bool error() const noexcept - { - return false; - } - - TOML_PURE_INLINE_GETTER - constexpr bool eof() const noexcept - { - return position_ >= source_.length(); - } - - TOML_PURE_INLINE_GETTER - explicit constexpr operator bool() const noexcept - { - return !eof(); - } - - TOML_PURE_INLINE_GETTER - constexpr bool peek_eof() const noexcept - { - return eof(); - } - - TOML_NODISCARD - TOML_ATTR(nonnull) - size_t operator()(void* dest, size_t num) noexcept - { - TOML_ASSERT_ASSUME(!eof()); - - num = impl::min(position_ + num, source_.length()) - position_; - std::memcpy(dest, source_.data() + position_, num); - position_ += num; - return num; - } - }; - - template <> - class utf8_byte_stream<std::istream> - { - private: - std::istream* source_; - - public: - TOML_NODISCARD_CTOR - explicit utf8_byte_stream(std::istream& stream) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) // - : source_{ &stream } - { - if (!*this) // eof, bad - return; - - const auto initial_pos = source_->tellg(); - char bom[3]; - source_->read(bom, 3); - if (source_->bad() || (source_->gcount() == 3 && memcmp(utf8_byte_order_mark.data(), bom, 3u) == 0)) - return; - - source_->clear(); - source_->seekg(initial_pos, std::istream::beg); - } - - TOML_PURE_INLINE_GETTER - bool error() const noexcept - { - return !!(source_->rdstate() & std::istream::badbit); - } - - TOML_PURE_INLINE_GETTER - bool eof() const noexcept - { - return !!(source_->rdstate() & std::istream::eofbit); - } - - TOML_PURE_INLINE_GETTER - explicit operator bool() const noexcept - { - return !(source_->rdstate() & (std::istream::badbit | std::istream::eofbit)); - } - - TOML_NODISCARD - bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) - { - return eof() || source_->peek() == std::istream::traits_type::eof(); - } - - TOML_NODISCARD - TOML_ATTR(nonnull) - size_t operator()(void* dest, size_t num) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) - { - TOML_ASSERT(*this); - - source_->read(static_cast<char*>(dest), static_cast<std::streamsize>(num)); - return static_cast<size_t>(source_->gcount()); - } - }; - - struct utf8_codepoint - { - char32_t value; - char bytes[4]; - size_t count; - source_position position; - - TOML_PURE_INLINE_GETTER - constexpr operator const char32_t&() const noexcept - { - return value; - } - - TOML_PURE_INLINE_GETTER - constexpr const char32_t& operator*() const noexcept - { - return value; - } - }; - static_assert(std::is_trivial_v<utf8_codepoint>); - static_assert(std::is_standard_layout_v<utf8_codepoint>); - - struct TOML_ABSTRACT_INTERFACE utf8_reader_interface - { - TOML_NODISCARD - virtual const source_path_ptr& source_path() const noexcept = 0; - - TOML_NODISCARD - virtual const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) = 0; - - TOML_NODISCARD - virtual bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) = 0; +// #--------------------------------------------------------------------------------------------------------------------- +// # UTF8 STREAMS +// #--------------------------------------------------------------------------------------------------------------------- + +TOML_ANON_NAMESPACE_START { + template <typename T> + class utf8_byte_stream; + + TOML_INTERNAL_LINKAGE + constexpr auto utf8_byte_order_mark = "\xEF\xBB\xBF"sv; + + template <typename Char> + class utf8_byte_stream<std::basic_string_view<Char>> { + static_assert(sizeof(Char) == 1); + + private: + std::basic_string_view<Char> source_; + size_t position_ = {}; + + public: + TOML_NODISCARD_CTOR + explicit constexpr utf8_byte_stream(std::basic_string_view<Char> sv) noexcept // + : source_{sv} { + // skip bom + if (source_.length() >= 3u && memcmp(utf8_byte_order_mark.data(), source_.data(), 3u) == 0) + position_ += 3u; + } + + TOML_CONST_INLINE_GETTER + constexpr bool error() const noexcept { return false; } + + TOML_PURE_INLINE_GETTER + constexpr bool eof() const noexcept { return position_ >= source_.length(); } + + TOML_PURE_INLINE_GETTER + explicit constexpr operator bool() const noexcept { return !eof(); } + + TOML_PURE_INLINE_GETTER + constexpr bool peek_eof() const noexcept { return eof(); } + + TOML_NODISCARD + TOML_ATTR(nonnull) + size_t operator()(void* dest, size_t num) noexcept { + TOML_ASSERT_ASSUME(!eof()); + + num = impl::min(position_ + num, source_.length()) - position_; + std::memcpy(dest, source_.data() + position_, num); + position_ += num; + return num; + } + }; + + template <> + class utf8_byte_stream<std::istream> { + private: + std::istream* source_; + + public: + TOML_NODISCARD_CTOR + explicit utf8_byte_stream(std::istream& stream) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) // + : source_{&stream} { + if (!*this) // eof, bad + return; + + const auto initial_pos = source_->tellg(); + char bom[3]; + source_->read(bom, 3); + if (source_->bad() || + (source_->gcount() == 3 && memcmp(utf8_byte_order_mark.data(), bom, 3u) == 0)) + return; + + source_->clear(); + source_->seekg(initial_pos, std::istream::beg); + } + + TOML_PURE_INLINE_GETTER + bool error() const noexcept { return !!(source_->rdstate() & std::istream::badbit); } + + TOML_PURE_INLINE_GETTER + bool eof() const noexcept { return !!(source_->rdstate() & std::istream::eofbit); } + + TOML_PURE_INLINE_GETTER + explicit operator bool() const noexcept { + return !(source_->rdstate() & (std::istream::badbit | std::istream::eofbit)); + } + + TOML_NODISCARD + bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) { + return eof() || source_->peek() == std::istream::traits_type::eof(); + } + + TOML_NODISCARD + TOML_ATTR(nonnull) + size_t operator()(void* dest, size_t num) noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) { + TOML_ASSERT(*this); + + source_->read(static_cast<char*>(dest), static_cast<std::streamsize>(num)); + return static_cast<size_t>(source_->gcount()); + } + }; + + struct utf8_codepoint { + char32_t value; + char bytes[4]; + size_t count; + source_position position; + + TOML_PURE_INLINE_GETTER + constexpr operator const char32_t&() const noexcept { return value; } + + TOML_PURE_INLINE_GETTER + constexpr const char32_t& operator*() const noexcept { return value; } + }; + static_assert(std::is_trivial_v<utf8_codepoint>); + static_assert(std::is_standard_layout_v<utf8_codepoint>); + + struct TOML_ABSTRACT_INTERFACE utf8_reader_interface { + TOML_NODISCARD + virtual const source_path_ptr& source_path() const noexcept = 0; + + TOML_NODISCARD + virtual const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) = 0; + + TOML_NODISCARD + virtual bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) = 0; #if !TOML_EXCEPTIONS - TOML_NODISCARD - virtual optional<parse_error>&& error() noexcept = 0; + TOML_NODISCARD + virtual optional<parse_error>&& error() noexcept = 0; #endif - virtual ~utf8_reader_interface() noexcept = default; - }; + virtual ~utf8_reader_interface() noexcept = default; + }; #if TOML_EXCEPTIONS -#define utf8_reader_error(...) throw parse_error(__VA_ARGS__) +#define utf8_reader_error(...) throw parse_error(__VA_ARGS__) #define utf8_reader_return_after_error(...) static_assert(true) -#define utf8_reader_error_check(...) static_assert(true) +#define utf8_reader_error_check(...) static_assert(true) #else -#define utf8_reader_error(...) err_.emplace(__VA_ARGS__) +#define utf8_reader_error(...) err_.emplace(__VA_ARGS__) #define utf8_reader_return_after_error(...) return __VA_ARGS__ -#define utf8_reader_error_check(...) \ - do \ - { \ - if TOML_UNLIKELY(err_) \ - return __VA_ARGS__; \ - } \ - while (false) +#define utf8_reader_error_check(...) \ + do { \ + if TOML_UNLIKELY (err_) return __VA_ARGS__; \ + } while (false) #endif @@ -229,373 +192,312 @@ TOML_ANON_NAMESPACE_START #define TOML_OVERALIGNED alignas(32) #endif - template <typename T> - class TOML_EMPTY_BASES utf8_reader final : public utf8_reader_interface - { - private: - static constexpr size_t block_capacity = 32; - utf8_byte_stream<T> stream_; - source_position next_pos_ = { 1, 1 }; - - impl::utf8_decoder decoder_; - struct currently_decoding_t - { - char bytes[4]; - size_t count; - } currently_decoding_; - - struct codepoints_t - { - TOML_OVERALIGNED utf8_codepoint buffer[block_capacity]; - size_t current; - size_t count; - } codepoints_; - - source_path_ptr source_path_; + template <typename T> + class TOML_EMPTY_BASES utf8_reader final : public utf8_reader_interface { + private: + static constexpr size_t block_capacity = 32; + utf8_byte_stream<T> stream_; + source_position next_pos_ = {1, 1}; + + impl::utf8_decoder decoder_; + struct currently_decoding_t { + char bytes[4]; + size_t count; + } currently_decoding_; + + struct codepoints_t { + TOML_OVERALIGNED utf8_codepoint buffer[block_capacity]; + size_t current; + size_t count; + } codepoints_; + + source_path_ptr source_path_; #if !TOML_EXCEPTIONS - optional<parse_error> err_; + optional<parse_error> err_; #endif - bool read_next_block() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) - { - TOML_ASSERT(stream_); + bool read_next_block() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) { + TOML_ASSERT(stream_); - TOML_OVERALIGNED char raw_bytes[block_capacity]; - size_t raw_bytes_read; + TOML_OVERALIGNED char raw_bytes[block_capacity]; + size_t raw_bytes_read; - // read the next raw (encoded) block in from the stream - if constexpr (noexcept(stream_(raw_bytes, block_capacity)) || !TOML_EXCEPTIONS) - { - raw_bytes_read = stream_(raw_bytes, block_capacity); - } + // read the next raw (encoded) block in from the stream + if constexpr (noexcept(stream_(raw_bytes, block_capacity)) || !TOML_EXCEPTIONS) { + raw_bytes_read = stream_(raw_bytes, block_capacity); + } #if TOML_EXCEPTIONS - else - { - try - { - raw_bytes_read = stream_(raw_bytes, block_capacity); - } - catch (const std::exception& exc) - { - throw parse_error{ exc.what(), next_pos_, source_path_ }; - } - catch (...) - { - throw parse_error{ "An unspecified error occurred", next_pos_, source_path_ }; - } - } -#endif // TOML_EXCEPTIONS - - // handle a zero-byte read - if TOML_UNLIKELY(!raw_bytes_read) - { - if (stream_.eof()) - { - // EOF only sets the error state if the decoder wants more input, otherwise - // a zero-byte read might have just caused the underlying stream to realize it's exhaused and set - // the EOF flag, and that's totally fine - if (decoder_.needs_more_input()) - utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence", - next_pos_, - source_path_); - } - else - { - utf8_reader_error("Reading from the underlying stream failed - zero bytes read", - next_pos_, - source_path_); - } - return false; - } - - TOML_ASSERT_ASSUME(raw_bytes_read); - std::memset(&codepoints_, 0, sizeof(codepoints_)); - - // helper for calculating decoded codepoint line+cols - const auto calc_positions = [&]() noexcept - { - for (size_t i = 0; i < codepoints_.count; i++) - { - auto& cp = codepoints_.buffer[i]; - cp.position = next_pos_; - - if (cp == U'\n') - { - next_pos_.line++; - next_pos_.column = source_index{ 1 }; - } - else - next_pos_.column++; - } - }; - - // decide whether we need to use the UTF-8 decoder or if we can treat this block as plain ASCII - const auto ascii_fast_path = !decoder_.needs_more_input() && impl::is_ascii(raw_bytes, raw_bytes_read); - - // ASCII fast-path - if (ascii_fast_path) - { - decoder_.reset(); - currently_decoding_.count = {}; - - codepoints_.count = raw_bytes_read; - for (size_t i = 0; i < codepoints_.count; i++) - { - auto& cp = codepoints_.buffer[i]; - cp.value = static_cast<char32_t>(raw_bytes[i]); - cp.bytes[0] = raw_bytes[i]; - cp.count = 1u; - } - } - - // UTF-8 slow-path - else - { - // helper for getting precise error location - const auto error_pos = [&]() noexcept -> const source_position& - { // - return codepoints_.count ? codepoints_.buffer[codepoints_.count - 1u].position : next_pos_; - }; - - for (size_t i = 0; i < raw_bytes_read; i++) - { - decoder_(static_cast<uint8_t>(raw_bytes[i])); - if TOML_UNLIKELY(decoder_.error()) - { - calc_positions(); - utf8_reader_error("Encountered invalid utf-8 sequence", error_pos(), source_path_); - utf8_reader_return_after_error(false); - } - - currently_decoding_.bytes[currently_decoding_.count++] = raw_bytes[i]; - - if (decoder_.has_code_point()) - { - auto& cp = codepoints_.buffer[codepoints_.count++]; - - cp.value = decoder_.codepoint; - cp.count = currently_decoding_.count; - std::memcpy(cp.bytes, currently_decoding_.bytes, currently_decoding_.count); - currently_decoding_.count = {}; - } - else if TOML_UNLIKELY(currently_decoding_.count == 4u) - { - calc_positions(); - utf8_reader_error("Encountered overlong utf-8 sequence", error_pos(), source_path_); - utf8_reader_return_after_error(false); - } - } - if TOML_UNLIKELY(decoder_.needs_more_input() && stream_.eof()) - { - calc_positions(); - utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence", - error_pos(), - source_path_); - utf8_reader_return_after_error(false); - } - } - - TOML_ASSERT_ASSUME(codepoints_.count); - calc_positions(); - - // handle general I/O errors - // (down here so the next_pos_ benefits from calc_positions()) - if TOML_UNLIKELY(stream_.error()) - { - utf8_reader_error("An I/O error occurred while reading from the underlying stream", - next_pos_, - source_path_); - utf8_reader_return_after_error(false); - } - - return true; - } - - public: - template <typename U, typename String = std::string_view> - TOML_NODISCARD_CTOR - explicit utf8_reader(U&& source, String&& source_path = {}) noexcept( - std::is_nothrow_constructible_v<utf8_byte_stream<T>, U&&>) - : stream_{ static_cast<U&&>(source) } - { - currently_decoding_.count = {}; - - codepoints_.current = {}; - codepoints_.count = {}; - - if (!source_path.empty()) - source_path_ = std::make_shared<const std::string>(static_cast<String&&>(source_path)); - } - - TOML_PURE_INLINE_GETTER - const source_path_ptr& source_path() const noexcept final - { - return source_path_; - } - - TOML_NODISCARD - const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final - { - utf8_reader_error_check({}); - - if (codepoints_.current == codepoints_.count) - { - if TOML_UNLIKELY(!stream_ || !read_next_block()) - return nullptr; - - TOML_ASSERT_ASSUME(!codepoints_.current); - } - TOML_ASSERT_ASSUME(codepoints_.count); - TOML_ASSERT_ASSUME(codepoints_.count <= block_capacity); - TOML_ASSERT_ASSUME(codepoints_.current < codepoints_.count); - - return &codepoints_.buffer[codepoints_.current++]; - } - - TOML_NODISCARD - bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final - { - return stream_.peek_eof(); - } + else { + try { + raw_bytes_read = stream_(raw_bytes, block_capacity); + } catch (const std::exception& exc) { + throw parse_error{exc.what(), next_pos_, source_path_}; + } catch (...) { + throw parse_error{"An unspecified error occurred", next_pos_, source_path_}; + } + } +#endif // TOML_EXCEPTIONS + + // handle a zero-byte read + if TOML_UNLIKELY (!raw_bytes_read) { + if (stream_.eof()) { + // EOF only sets the error state if the decoder wants more input, otherwise + // a zero-byte read might have just caused the underlying stream to realize it's exhaused + // and set the EOF flag, and that's totally fine + if (decoder_.needs_more_input()) + utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence", + next_pos_, source_path_); + } else { + utf8_reader_error("Reading from the underlying stream failed - zero bytes read", + next_pos_, source_path_); + } + return false; + } + + TOML_ASSERT_ASSUME(raw_bytes_read); + std::memset(&codepoints_, 0, sizeof(codepoints_)); + + // helper for calculating decoded codepoint line+cols + const auto calc_positions = [&]() noexcept { + for (size_t i = 0; i < codepoints_.count; i++) { + auto& cp = codepoints_.buffer[i]; + cp.position = next_pos_; + + if (cp == U'\n') { + next_pos_.line++; + next_pos_.column = source_index{1}; + } else + next_pos_.column++; + } + }; + + // decide whether we need to use the UTF-8 decoder or if we can treat this block as plain + // ASCII + const auto ascii_fast_path = + !decoder_.needs_more_input() && impl::is_ascii(raw_bytes, raw_bytes_read); + + // ASCII fast-path + if (ascii_fast_path) { + decoder_.reset(); + currently_decoding_.count = {}; + + codepoints_.count = raw_bytes_read; + for (size_t i = 0; i < codepoints_.count; i++) { + auto& cp = codepoints_.buffer[i]; + cp.value = static_cast<char32_t>(raw_bytes[i]); + cp.bytes[0] = raw_bytes[i]; + cp.count = 1u; + } + } + + // UTF-8 slow-path + else { + // helper for getting precise error location + const auto error_pos = [&]() noexcept -> const source_position& { // + return codepoints_.count ? codepoints_.buffer[codepoints_.count - 1u].position + : next_pos_; + }; + + for (size_t i = 0; i < raw_bytes_read; i++) { + decoder_(static_cast<uint8_t>(raw_bytes[i])); + if TOML_UNLIKELY (decoder_.error()) { + calc_positions(); + utf8_reader_error("Encountered invalid utf-8 sequence", error_pos(), source_path_); + utf8_reader_return_after_error(false); + } + + currently_decoding_.bytes[currently_decoding_.count++] = raw_bytes[i]; + + if (decoder_.has_code_point()) { + auto& cp = codepoints_.buffer[codepoints_.count++]; + + cp.value = decoder_.codepoint; + cp.count = currently_decoding_.count; + std::memcpy(cp.bytes, currently_decoding_.bytes, currently_decoding_.count); + currently_decoding_.count = {}; + } else if TOML_UNLIKELY (currently_decoding_.count == 4u) { + calc_positions(); + utf8_reader_error("Encountered overlong utf-8 sequence", error_pos(), source_path_); + utf8_reader_return_after_error(false); + } + } + if TOML_UNLIKELY (decoder_.needs_more_input() && stream_.eof()) { + calc_positions(); + utf8_reader_error("Encountered EOF during incomplete utf-8 code point sequence", + error_pos(), source_path_); + utf8_reader_return_after_error(false); + } + } + + TOML_ASSERT_ASSUME(codepoints_.count); + calc_positions(); + + // handle general I/O errors + // (down here so the next_pos_ benefits from calc_positions()) + if TOML_UNLIKELY (stream_.error()) { + utf8_reader_error("An I/O error occurred while reading from the underlying stream", + next_pos_, source_path_); + utf8_reader_return_after_error(false); + } + + return true; + } + + public: + template <typename U, typename String = std::string_view> + TOML_NODISCARD_CTOR explicit utf8_reader(U&& source, String&& source_path = {}) noexcept( + std::is_nothrow_constructible_v<utf8_byte_stream<T>, U&&>) + : stream_{static_cast<U&&>(source)} { + currently_decoding_.count = {}; + + codepoints_.current = {}; + codepoints_.count = {}; + + if (!source_path.empty()) + source_path_ = std::make_shared<const std::string>(static_cast<String&&>(source_path)); + } + + TOML_PURE_INLINE_GETTER + const source_path_ptr& source_path() const noexcept final { return source_path_; } + + TOML_NODISCARD + const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final { + utf8_reader_error_check({}); + + if (codepoints_.current == codepoints_.count) { + if TOML_UNLIKELY (!stream_ || !read_next_block()) return nullptr; + + TOML_ASSERT_ASSUME(!codepoints_.current); + } + TOML_ASSERT_ASSUME(codepoints_.count); + TOML_ASSERT_ASSUME(codepoints_.count <= block_capacity); + TOML_ASSERT_ASSUME(codepoints_.current < codepoints_.count); + + return &codepoints_.buffer[codepoints_.current++]; + } + + TOML_NODISCARD + bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) final { + return stream_.peek_eof(); + } #if !TOML_EXCEPTIONS - TOML_NODISCARD - optional<parse_error>&& error() noexcept final - { - return std::move(err_); - } + TOML_NODISCARD + optional<parse_error>&& error() noexcept final { return std::move(err_); } #endif - }; - - template <typename Char> - utf8_reader(std::basic_string_view<Char>, std::string_view) -> utf8_reader<std::basic_string_view<Char>>; - template <typename Char> - utf8_reader(std::basic_string_view<Char>, std::string&&) -> utf8_reader<std::basic_string_view<Char>>; - template <typename Char> - utf8_reader(std::basic_istream<Char>&, std::string_view) -> utf8_reader<std::basic_istream<Char>>; - template <typename Char> - utf8_reader(std::basic_istream<Char>&, std::string&&) -> utf8_reader<std::basic_istream<Char>>; + }; + + template <typename Char> + utf8_reader(std::basic_string_view<Char>, + std::string_view) -> utf8_reader<std::basic_string_view<Char>>; + template <typename Char> + utf8_reader(std::basic_string_view<Char>, + std::string&&) -> utf8_reader<std::basic_string_view<Char>>; + template <typename Char> + utf8_reader(std::basic_istream<Char>&, std::string_view) -> utf8_reader<std::basic_istream<Char>>; + template <typename Char> + utf8_reader(std::basic_istream<Char>&, std::string&&) -> utf8_reader<std::basic_istream<Char>>; #if TOML_EXCEPTIONS #define utf8_buffered_reader_error_check(...) static_assert(true) #else -#define utf8_buffered_reader_error_check(...) \ - do \ - { \ - if TOML_UNLIKELY(reader_.error()) \ - return __VA_ARGS__; \ - } \ - while (false) +#define utf8_buffered_reader_error_check(...) \ + do { \ + if TOML_UNLIKELY (reader_.error()) return __VA_ARGS__; \ + } while (false) #endif - class TOML_EMPTY_BASES utf8_buffered_reader - { - public: - static constexpr size_t max_history_length = 128; - - private: - static constexpr size_t history_buffer_size = max_history_length - 1; //'head' is stored in the reader - utf8_reader_interface& reader_; - struct - { - utf8_codepoint buffer[history_buffer_size]; - size_t count, first; - } history_ = {}; - const utf8_codepoint* head_ = {}; - size_t negative_offset_ = {}; - - public: - TOML_NODISCARD_CTOR - explicit utf8_buffered_reader(utf8_reader_interface& reader) noexcept // - : reader_{ reader } - {} - - TOML_PURE_INLINE_GETTER - const source_path_ptr& source_path() const noexcept - { - return reader_.source_path(); - } - - TOML_NODISCARD - const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) - { - utf8_buffered_reader_error_check({}); - - if (negative_offset_) - { - negative_offset_--; - - // an entry negative offset of 1 just means "replay the current head" - if (!negative_offset_) - return head_; - - // otherwise step back into the history buffer - else - return history_.buffer - + ((history_.first + history_.count - negative_offset_) % history_buffer_size); - } - else - { - // first character read from stream - if TOML_UNLIKELY(!history_.count && !head_) - head_ = reader_.read_next(); - - // subsequent characters and not eof - else if (head_) - { - if TOML_UNLIKELY(history_.count < history_buffer_size) - history_.buffer[history_.count++] = *head_; - else - history_.buffer[(history_.first++ + history_buffer_size) % history_buffer_size] = *head_; - - head_ = reader_.read_next(); - } - - return head_; - } - } - - TOML_NODISCARD - const utf8_codepoint* step_back(size_t count) noexcept - { - utf8_buffered_reader_error_check({}); - - TOML_ASSERT_ASSUME(history_.count); - TOML_ASSERT_ASSUME(negative_offset_ + count <= history_.count); - - negative_offset_ += count; - - return negative_offset_ - ? history_.buffer + ((history_.first + history_.count - negative_offset_) % history_buffer_size) - : head_; - } - - TOML_NODISCARD - bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) - { - return reader_.peek_eof(); - } + class TOML_EMPTY_BASES utf8_buffered_reader { + public: + static constexpr size_t max_history_length = 128; + + private: + static constexpr size_t history_buffer_size = + max_history_length - 1; //'head' is stored in the reader + utf8_reader_interface& reader_; + struct { + utf8_codepoint buffer[history_buffer_size]; + size_t count, first; + } history_ = {}; + const utf8_codepoint* head_ = {}; + size_t negative_offset_ = {}; + + public: + TOML_NODISCARD_CTOR + explicit utf8_buffered_reader(utf8_reader_interface& reader) noexcept // + : reader_{reader} {} + + TOML_PURE_INLINE_GETTER + const source_path_ptr& source_path() const noexcept { return reader_.source_path(); } + + TOML_NODISCARD + const utf8_codepoint* read_next() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) { + utf8_buffered_reader_error_check({}); + + if (negative_offset_) { + negative_offset_--; + + // an entry negative offset of 1 just means "replay the current head" + if (!negative_offset_) return head_; + + // otherwise step back into the history buffer + else + return history_.buffer + + ((history_.first + history_.count - negative_offset_) % history_buffer_size); + } else { + // first character read from stream + if TOML_UNLIKELY (!history_.count && !head_) head_ = reader_.read_next(); + + // subsequent characters and not eof + else if (head_) { + if TOML_UNLIKELY (history_.count < history_buffer_size) + history_.buffer[history_.count++] = *head_; + else + history_.buffer[(history_.first++ + history_buffer_size) % history_buffer_size] = + *head_; + + head_ = reader_.read_next(); + } + + return head_; + } + } + + TOML_NODISCARD + const utf8_codepoint* step_back(size_t count) noexcept { + utf8_buffered_reader_error_check({}); + + TOML_ASSERT_ASSUME(history_.count); + TOML_ASSERT_ASSUME(negative_offset_ + count <= history_.count); + + negative_offset_ += count; + + return negative_offset_ + ? history_.buffer + + ((history_.first + history_.count - negative_offset_) % history_buffer_size) + : head_; + } + + TOML_NODISCARD + bool peek_eof() const noexcept(!TOML_COMPILER_HAS_EXCEPTIONS) { return reader_.peek_eof(); } #if !TOML_EXCEPTIONS - TOML_NODISCARD - optional<parse_error>&& error() noexcept - { - return reader_.error(); - } + TOML_NODISCARD + optional<parse_error>&& error() noexcept { return reader_.error(); } #endif - }; + }; } TOML_ANON_NAMESPACE_END; -//#--------------------------------------------------------------------------------------------------------------------- -//# PARSER INTERNAL IMPLEMENTATION -//#--------------------------------------------------------------------------------------------------------------------- +// #--------------------------------------------------------------------------------------------------------------------- +// # PARSER INTERNAL IMPLEMENTATION +// #--------------------------------------------------------------------------------------------------------------------- #if TOML_EXCEPTIONS #define TOML_RETURNS_BY_THROWING [[noreturn]] @@ -603,398 +505,331 @@ TOML_ANON_NAMESPACE_END; #define TOML_RETURNS_BY_THROWING #endif -TOML_ANON_NAMESPACE_START -{ - template <typename... T> - TOML_CONST_GETTER - TOML_INTERNAL_LINKAGE - constexpr bool is_match(char32_t codepoint, T... vals) noexcept - { - static_assert((std::is_same_v<char32_t, T> && ...)); - return ((codepoint == vals) || ...); - } - - template <uint64_t> - struct parse_integer_traits; - template <> - struct parse_integer_traits<2> - { - static constexpr auto scope_qualifier = "binary integer"sv; - static constexpr auto is_digit = impl::is_binary_digit; - static constexpr auto is_signed = false; - static constexpr auto max_digits = 63; - static constexpr auto prefix_codepoint = U'b'; - static constexpr auto prefix = "b"sv; - static constexpr auto full_prefix = "0b"sv; - }; - template <> - struct parse_integer_traits<8> - { - static constexpr auto scope_qualifier = "octal integer"sv; - static constexpr auto is_digit = impl::is_octal_digit; - static constexpr auto is_signed = false; - static constexpr auto max_digits = 21; // strlen("777777777777777777777") - static constexpr auto prefix_codepoint = U'o'; - static constexpr auto prefix = "o"sv; - static constexpr auto full_prefix = "0o"sv; - }; - template <> - struct parse_integer_traits<10> - { - static constexpr auto scope_qualifier = "decimal integer"sv; - static constexpr auto is_digit = impl::is_decimal_digit; - static constexpr auto is_signed = true; - static constexpr auto max_digits = 19; // strlen("9223372036854775807") - static constexpr auto full_prefix = ""sv; - }; - template <> - struct parse_integer_traits<16> - { - static constexpr auto scope_qualifier = "hexadecimal integer"sv; - static constexpr auto is_digit = impl::is_hexadecimal_digit; - static constexpr auto is_signed = false; - static constexpr auto max_digits = 16; // strlen("7FFFFFFFFFFFFFFF") - static constexpr auto prefix_codepoint = U'x'; - static constexpr auto prefix = "x"sv; - static constexpr auto full_prefix = "0x"sv; - }; - - TOML_PURE_GETTER - TOML_INTERNAL_LINKAGE - std::string_view to_sv(node_type val) noexcept - { - return impl::node_type_friendly_names[impl::unwrap_enum(val)]; - } - - TOML_PURE_GETTER - TOML_INTERNAL_LINKAGE - std::string_view to_sv(const std::string& str) noexcept - { - return std::string_view{ str }; - } - - TOML_CONST_GETTER - TOML_INTERNAL_LINKAGE - std::string_view to_sv(bool val) noexcept - { - using namespace std::string_view_literals; - - return val ? "true"sv : "false"sv; - } - - TOML_PURE_GETTER - TOML_INTERNAL_LINKAGE - std::string_view to_sv(const utf8_codepoint& cp) noexcept - { - if (cp.value <= U'\x1F') - return impl::control_char_escapes[cp.value]; - else if (cp.value == U'\x7F') - return "\\u007F"sv; - else - return std::string_view{ cp.bytes, cp.count }; - } - - TOML_PURE_GETTER - TOML_INTERNAL_LINKAGE - std::string_view to_sv(const utf8_codepoint* cp) noexcept - { - if (cp) - return to_sv(*cp); - return ""sv; - } - - struct escaped_codepoint - { - const utf8_codepoint& cp; - }; - - template <typename T> - TOML_ATTR(nonnull) - TOML_INTERNAL_LINKAGE - void concatenate(char*& write_pos, char* const buf_end, const T& arg) noexcept - { - if TOML_UNLIKELY(write_pos >= buf_end) - return; - - using arg_type = impl::remove_cvref<T>; - - // string views - if constexpr (std::is_same_v<arg_type, std::string_view>) - { - const auto max_chars = static_cast<size_t>(buf_end - write_pos); - const auto len = max_chars < arg.length() ? max_chars : arg.length(); - std::memcpy(write_pos, arg.data(), len); - write_pos += len; - } - - // doubles - else if constexpr (std::is_same_v<arg_type, double>) - { +TOML_ANON_NAMESPACE_START { + template <typename... T> + TOML_CONST_GETTER TOML_INTERNAL_LINKAGE constexpr bool is_match(char32_t codepoint, + T... vals) noexcept { + static_assert((std::is_same_v<char32_t, T> && ...)); + return ((codepoint == vals) || ...); + } + + template <uint64_t> + struct parse_integer_traits; + template <> + struct parse_integer_traits<2> { + static constexpr auto scope_qualifier = "binary integer"sv; + static constexpr auto is_digit = impl::is_binary_digit; + static constexpr auto is_signed = false; + static constexpr auto max_digits = 63; + static constexpr auto prefix_codepoint = U'b'; + static constexpr auto prefix = "b"sv; + static constexpr auto full_prefix = "0b"sv; + }; + template <> + struct parse_integer_traits<8> { + static constexpr auto scope_qualifier = "octal integer"sv; + static constexpr auto is_digit = impl::is_octal_digit; + static constexpr auto is_signed = false; + static constexpr auto max_digits = 21; // strlen("777777777777777777777") + static constexpr auto prefix_codepoint = U'o'; + static constexpr auto prefix = "o"sv; + static constexpr auto full_prefix = "0o"sv; + }; + template <> + struct parse_integer_traits<10> { + static constexpr auto scope_qualifier = "decimal integer"sv; + static constexpr auto is_digit = impl::is_decimal_digit; + static constexpr auto is_signed = true; + static constexpr auto max_digits = 19; // strlen("9223372036854775807") + static constexpr auto full_prefix = ""sv; + }; + template <> + struct parse_integer_traits<16> { + static constexpr auto scope_qualifier = "hexadecimal integer"sv; + static constexpr auto is_digit = impl::is_hexadecimal_digit; + static constexpr auto is_signed = false; + static constexpr auto max_digits = 16; // strlen("7FFFFFFFFFFFFFFF") + static constexpr auto prefix_codepoint = U'x'; + static constexpr auto prefix = "x"sv; + static constexpr auto full_prefix = "0x"sv; + }; + + TOML_PURE_GETTER + TOML_INTERNAL_LINKAGE + std::string_view to_sv(node_type val) noexcept { + return impl::node_type_friendly_names[impl::unwrap_enum(val)]; + } + + TOML_PURE_GETTER + TOML_INTERNAL_LINKAGE + std::string_view to_sv(const std::string& str) noexcept { + return std::string_view{str}; + } + + TOML_CONST_GETTER + TOML_INTERNAL_LINKAGE + std::string_view to_sv(bool val) noexcept { + using namespace std::string_view_literals; + + return val ? "true"sv : "false"sv; + } + + TOML_PURE_GETTER + TOML_INTERNAL_LINKAGE + std::string_view to_sv(const utf8_codepoint& cp) noexcept { + if (cp.value <= U'\x1F') + return impl::control_char_escapes[cp.value]; + else if (cp.value == U'\x7F') + return "\\u007F"sv; + else + return std::string_view{cp.bytes, cp.count}; + } + + TOML_PURE_GETTER + TOML_INTERNAL_LINKAGE + std::string_view to_sv(const utf8_codepoint* cp) noexcept { + if (cp) return to_sv(*cp); + return ""sv; + } + + struct escaped_codepoint { + const utf8_codepoint& cp; + }; + + template <typename T> + TOML_ATTR(nonnull) + TOML_INTERNAL_LINKAGE void concatenate(char*& write_pos, char* const buf_end, + const T& arg) noexcept { + if TOML_UNLIKELY (write_pos >= buf_end) return; + + using arg_type = impl::remove_cvref<T>; + + // string views + if constexpr (std::is_same_v<arg_type, std::string_view>) { + const auto max_chars = static_cast<size_t>(buf_end - write_pos); + const auto len = max_chars < arg.length() ? max_chars : arg.length(); + std::memcpy(write_pos, arg.data(), len); + write_pos += len; + } + + // doubles + else if constexpr (std::is_same_v<arg_type, double>) { #if TOML_FLOAT_CHARCONV - const auto result = std::to_chars(write_pos, buf_end, arg); - write_pos = result.ptr; + const auto result = std::to_chars(write_pos, buf_end, arg); + write_pos = result.ptr; #else - std::ostringstream ss; - ss.imbue(std::locale::classic()); - ss.precision(std::numeric_limits<arg_type>::max_digits10); - ss << arg; - concatenate(write_pos, buf_end, to_sv(std::move(ss).str())); + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss.precision(std::numeric_limits<arg_type>::max_digits10); + ss << arg; + concatenate(write_pos, buf_end, to_sv(std::move(ss).str())); #endif - } + } - // 64-bit integers - else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>) - { + // 64-bit integers + else if constexpr (impl::is_one_of<arg_type, int64_t, uint64_t>) { #if TOML_INT_CHARCONV - const auto result = std::to_chars(write_pos, buf_end, arg); - write_pos = result.ptr; + const auto result = std::to_chars(write_pos, buf_end, arg); + write_pos = result.ptr; #else - std::ostringstream ss; - ss.imbue(std::locale::classic()); - using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>; - ss << static_cast<cast_type>(arg); - concatenate(write_pos, buf_end, to_sv(std::move(ss).str())); + std::ostringstream ss; + ss.imbue(std::locale::classic()); + using cast_type = std::conditional_t<std::is_signed_v<arg_type>, int64_t, uint64_t>; + ss << static_cast<cast_type>(arg); + concatenate(write_pos, buf_end, to_sv(std::move(ss).str())); #endif - } - - // escaped_codepoint - else if constexpr (std::is_same_v<arg_type, escaped_codepoint>) - { - if (arg.cp.value <= U'\x7F') - concatenate(write_pos, buf_end, to_sv(arg.cp)); - else - { - auto val = static_cast<uint_least32_t>(arg.cp.value); - const auto digits = val > 0xFFFFu ? 8u : 4u; - constexpr auto mask = uint_least32_t{ 0xFu }; - char buf[10] = { '\\', digits > 4 ? 'U' : 'u' }; - for (auto i = 2u + digits; i-- > 2u;) - { - const auto hexdig = val & mask; - buf[i] = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig)); - val >>= 4; - } - concatenate(write_pos, buf_end, std::string_view{ buf, digits + 2u }); - } - } - - // all other floats (fallback - coerce to double) - else if constexpr (std::is_floating_point_v<arg_type>) - concatenate(write_pos, buf_end, static_cast<double>(arg)); - - // all other integers (fallback - coerce to (u)int64_t) - else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>) - { - using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>; - concatenate(write_pos, buf_end, static_cast<cast_type>(arg)); - } - - else - { - static_assert( - impl::always_false<T>, - "concatenate() inputs are limited to std::string_views, integers, floats, and escaped_codepoint"); - } - } - - struct error_builder - { - static constexpr std::size_t buf_size = 512; - char buf[buf_size]; - char* write_pos = buf; - char* const max_write_pos = buf + (buf_size - std::size_t{ 1 }); // allow for null terminator - - TOML_NODISCARD_CTOR - error_builder(std::string_view scope) noexcept - { - concatenate(write_pos, max_write_pos, "Error while parsing "sv); - concatenate(write_pos, max_write_pos, scope); - concatenate(write_pos, max_write_pos, ": "sv); - } - - template <typename T> - void append(const T& arg) noexcept - { - concatenate(write_pos, max_write_pos, arg); - } - - TOML_RETURNS_BY_THROWING - auto finish(const source_position& pos, const source_path_ptr& source_path) const - { - *write_pos = '\0'; + } + + // escaped_codepoint + else if constexpr (std::is_same_v<arg_type, escaped_codepoint>) { + if (arg.cp.value <= U'\x7F') + concatenate(write_pos, buf_end, to_sv(arg.cp)); + else { + auto val = static_cast<uint_least32_t>(arg.cp.value); + const auto digits = val > 0xFFFFu ? 8u : 4u; + constexpr auto mask = uint_least32_t{0xFu}; + char buf[10] = {'\\', digits > 4 ? 'U' : 'u'}; + for (auto i = 2u + digits; i-- > 2u;) { + const auto hexdig = val & mask; + buf[i] = static_cast<char>(hexdig >= 0xAu ? ('A' + (hexdig - 0xAu)) : ('0' + hexdig)); + val >>= 4; + } + concatenate(write_pos, buf_end, std::string_view{buf, digits + 2u}); + } + } + + // all other floats (fallback - coerce to double) + else if constexpr (std::is_floating_point_v<arg_type>) + concatenate(write_pos, buf_end, static_cast<double>(arg)); + + // all other integers (fallback - coerce to (u)int64_t) + else if constexpr (std::is_arithmetic_v<arg_type> && std::is_integral_v<arg_type>) { + using cast_type = std::conditional_t<std::is_unsigned_v<arg_type>, uint64_t, int64_t>; + concatenate(write_pos, buf_end, static_cast<cast_type>(arg)); + } + + else { + static_assert(impl::always_false<T>, + "concatenate() inputs are limited to std::string_views, integers, floats, and " + "escaped_codepoint"); + } + } + + struct error_builder { + static constexpr std::size_t buf_size = 512; + char buf[buf_size]; + char* write_pos = buf; + char* const max_write_pos = buf + (buf_size - std::size_t{1}); // allow for null terminator + + TOML_NODISCARD_CTOR + error_builder(std::string_view scope) noexcept { + concatenate(write_pos, max_write_pos, "Error while parsing "sv); + concatenate(write_pos, max_write_pos, scope); + concatenate(write_pos, max_write_pos, ": "sv); + } + + template <typename T> + void append(const T& arg) noexcept { + concatenate(write_pos, max_write_pos, arg); + } + + TOML_RETURNS_BY_THROWING + auto finish(const source_position& pos, const source_path_ptr& source_path) const { + *write_pos = '\0'; #if TOML_EXCEPTIONS - throw parse_error{ buf, pos, source_path }; + throw parse_error{buf, pos, source_path}; #else - return parse_error{ std::string(buf, static_cast<size_t>(write_pos - buf)), pos, source_path }; + return parse_error{std::string(buf, static_cast<size_t>(write_pos - buf)), pos, source_path}; #endif - } - - TOML_DELETE_DEFAULTS(error_builder); - }; - - struct parse_scope - { - std::string_view& storage_; - std::string_view parent_; - - TOML_NODISCARD_CTOR - explicit parse_scope(std::string_view& current_scope, std::string_view new_scope) noexcept - : storage_{ current_scope }, - parent_{ current_scope } - { - storage_ = new_scope; - } - - ~parse_scope() noexcept - { - storage_ = parent_; - } - - TOML_DELETE_DEFAULTS(parse_scope); - }; + } + + TOML_DELETE_DEFAULTS(error_builder); + }; + + struct parse_scope { + std::string_view& storage_; + std::string_view parent_; + + TOML_NODISCARD_CTOR + explicit parse_scope(std::string_view& current_scope, std::string_view new_scope) noexcept + : storage_{current_scope}, parent_{current_scope} { + storage_ = new_scope; + } + + ~parse_scope() noexcept { storage_ = parent_; } + + TOML_DELETE_DEFAULTS(parse_scope); + }; #define push_parse_scope_2(scope, line) parse_scope ps_##line(current_scope, scope) #define push_parse_scope_1(scope, line) push_parse_scope_2(scope, line) -#define push_parse_scope(scope) push_parse_scope_1(scope, __LINE__) - - struct parse_key_buffer - { - std::string buffer; - std::vector<std::pair<size_t, size_t>> segments; - std::vector<source_position> starts; - std::vector<source_position> ends; - - void clear() noexcept - { - buffer.clear(); - segments.clear(); - starts.clear(); - ends.clear(); - } - - void push_back(std::string_view segment, source_position b, source_position e) - { - segments.push_back({ buffer.length(), segment.length() }); - buffer.append(segment); - starts.push_back(b); - ends.push_back(e); - } - - TOML_PURE_INLINE_GETTER - std::string_view operator[](size_t i) const noexcept - { - return std::string_view{ buffer.c_str() + segments[i].first, segments[i].second }; - } - - TOML_PURE_INLINE_GETTER - std::string_view back() const noexcept - { - return (*this)[segments.size() - 1u]; - } - - TOML_PURE_INLINE_GETTER - bool empty() const noexcept - { - return segments.empty(); - } - - TOML_PURE_INLINE_GETTER - size_t size() const noexcept - { - return segments.size(); - } - }; - - struct depth_counter_scope - { - size_t& depth_; - - TOML_NODISCARD_CTOR - explicit depth_counter_scope(size_t& depth) noexcept // - : depth_{ depth } - { - depth_++; - } - - ~depth_counter_scope() noexcept - { - depth_--; - } - - TOML_DELETE_DEFAULTS(depth_counter_scope); - }; - - struct parsed_string - { - std::string_view value; - bool was_multi_line; - }; - - struct table_vector_scope - { - std::vector<table*>& tables; - - TOML_NODISCARD_CTOR - explicit table_vector_scope(std::vector<table*>& tables_, table& tbl) // - : tables{ tables_ } - { - tables.push_back(&tbl); - } - - ~table_vector_scope() noexcept - { - tables.pop_back(); - } - - TOML_DELETE_DEFAULTS(table_vector_scope); - }; +#define push_parse_scope(scope) push_parse_scope_1(scope, __LINE__) + + struct parse_key_buffer { + std::string buffer; + std::vector<std::pair<size_t, size_t>> segments; + std::vector<source_position> starts; + std::vector<source_position> ends; + + void clear() noexcept { + buffer.clear(); + segments.clear(); + starts.clear(); + ends.clear(); + } + + void push_back(std::string_view segment, source_position b, source_position e) { + segments.push_back({buffer.length(), segment.length()}); + buffer.append(segment); + starts.push_back(b); + ends.push_back(e); + } + + TOML_PURE_INLINE_GETTER + std::string_view operator[](size_t i) const noexcept { + return std::string_view{buffer.c_str() + segments[i].first, segments[i].second}; + } + + TOML_PURE_INLINE_GETTER + std::string_view back() const noexcept { return (*this)[segments.size() - 1u]; } + + TOML_PURE_INLINE_GETTER + bool empty() const noexcept { return segments.empty(); } + + TOML_PURE_INLINE_GETTER + size_t size() const noexcept { return segments.size(); } + }; + + struct depth_counter_scope { + size_t& depth_; + + TOML_NODISCARD_CTOR + explicit depth_counter_scope(size_t& depth) noexcept // + : depth_{depth} { + depth_++; + } + + ~depth_counter_scope() noexcept { depth_--; } + + TOML_DELETE_DEFAULTS(depth_counter_scope); + }; + + struct parsed_string { + std::string_view value; + bool was_multi_line; + }; + + struct table_vector_scope { + std::vector<table*>& tables; + + TOML_NODISCARD_CTOR + explicit table_vector_scope(std::vector<table*>& tables_, table& tbl) // + : tables{tables_} { + tables.push_back(&tbl); + } + + ~table_vector_scope() noexcept { tables.pop_back(); } + + TOML_DELETE_DEFAULTS(table_vector_scope); + }; } TOML_ANON_NAMESPACE_END; -#if 1 // parser helper macros +#if 1 // parser helper macros // Q: "what the fuck is this? MACROS????" // A: The parser needs to work in exceptionless mode (returning error objects directly) -// and exception mode (reporting parse failures by throwing). Two totally different control flows. -// These macros encapsulate the differences between the two modes so I can write code code +// and exception mode (reporting parse failures by throwing). Two totally different control +// flows. These macros encapsulate the differences between the two modes so I can write code code // as though I was only targeting one mode and not want yeet myself into the sun. -// They're all #undef'd at the bottom of the parser's implementation so they should be harmless outside -// of toml++. +// They're all #undef'd at the bottom of the parser's implementation so they should be harmless +// outside of toml++. -#define is_eof() !cp +#define is_eof() !cp #define assert_not_eof() TOML_ASSERT_ASSUME(cp != nullptr) -#define return_if_eof(...) \ - do \ - { \ - if TOML_UNLIKELY(is_eof()) \ - return __VA_ARGS__; \ - } \ - while (false) +#define return_if_eof(...) \ + do { \ + if TOML_UNLIKELY (is_eof()) return __VA_ARGS__; \ + } while (false) #if TOML_EXCEPTIONS -#define is_error() false -#define return_after_error(...) TOML_UNREACHABLE -#define assert_not_error() static_assert(true) -#define return_if_error(...) static_assert(true) +#define is_error() false +#define return_after_error(...) TOML_UNREACHABLE +#define assert_not_error() static_assert(true) +#define return_if_error(...) static_assert(true) #define return_if_error_or_eof(...) return_if_eof(__VA_ARGS__) #else -#define is_error() !!err +#define is_error() !!err #define return_after_error(...) return __VA_ARGS__ -#define assert_not_error() TOML_ASSERT(!is_error()) -#define return_if_error(...) \ - do \ - { \ - if TOML_UNLIKELY(is_error()) \ - return __VA_ARGS__; \ - } \ - while (false) -#define return_if_error_or_eof(...) \ - do \ - { \ - if TOML_UNLIKELY(is_eof() || is_error()) \ - return __VA_ARGS__; \ - } \ - while (false) +#define assert_not_error() TOML_ASSERT(!is_error()) +#define return_if_error(...) \ + do { \ + if TOML_UNLIKELY (is_error()) return __VA_ARGS__; \ + } while (false) +#define return_if_error_or_eof(...) \ + do { \ + if TOML_UNLIKELY (is_eof() || is_error()) return __VA_ARGS__; \ + } while (false) #endif #if defined(TOML_BREAK_AT_PARSE_ERRORS) && TOML_BREAK_AT_PARSE_ERRORS @@ -1016,2711 +851,2483 @@ TOML_ANON_NAMESPACE_END; #define parse_error_break() static_assert(true) #endif -#define set_error_and_return(ret, ...) \ - do \ - { \ - if (!is_error()) \ - set_error(__VA_ARGS__); \ - return_after_error(ret); \ - } \ - while (false) +#define set_error_and_return(ret, ...) \ + do { \ + if (!is_error()) set_error(__VA_ARGS__); \ + return_after_error(ret); \ + } while (false) #define set_error_and_return_default(...) set_error_and_return({}, __VA_ARGS__) -#define set_error_and_return_if_eof(...) \ - do \ - { \ - if TOML_UNLIKELY(is_eof()) \ - set_error_and_return(__VA_ARGS__, "encountered end-of-file"sv); \ - } \ - while (false) - -#define advance_and_return_if_error(...) \ - do \ - { \ - assert_not_eof(); \ - advance(); \ - return_if_error(__VA_ARGS__); \ - } \ - while (false) - -#define advance_and_return_if_error_or_eof(...) \ - do \ - { \ - assert_not_eof(); \ - advance(); \ - return_if_error(__VA_ARGS__); \ - set_error_and_return_if_eof(__VA_ARGS__); \ - } \ - while (false) - -#endif // parser helper macros - -TOML_IMPL_NAMESPACE_START -{ - TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex); - - class parser - { - private: - static constexpr size_t max_nested_values = TOML_MAX_NESTED_VALUES; - - utf8_buffered_reader reader; - table root; - source_position prev_pos = { 1, 1 }; - const utf8_codepoint* cp = {}; - std::vector<table*> implicit_tables; - std::vector<table*> dotted_key_tables; - std::vector<table*> open_inline_tables; - std::vector<array*> table_arrays; - parse_key_buffer key_buffer; - std::string string_buffer; - std::string recording_buffer; // for diagnostics - bool recording = false, recording_whitespace = true; - std::string_view current_scope; - size_t nested_values = {}; +#define set_error_and_return_if_eof(...) \ + do { \ + if TOML_UNLIKELY (is_eof()) set_error_and_return(__VA_ARGS__, "encountered end-of-file"sv); \ + } while (false) + +#define advance_and_return_if_error(...) \ + do { \ + assert_not_eof(); \ + advance(); \ + return_if_error(__VA_ARGS__); \ + } while (false) + +#define advance_and_return_if_error_or_eof(...) \ + do { \ + assert_not_eof(); \ + advance(); \ + return_if_error(__VA_ARGS__); \ + set_error_and_return_if_eof(__VA_ARGS__); \ + } while (false) + +#endif // parser helper macros + +TOML_IMPL_NAMESPACE_START { + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex); + + class parser { + private: + static constexpr size_t max_nested_values = TOML_MAX_NESTED_VALUES; + + utf8_buffered_reader reader; + table root; + source_position prev_pos = {1, 1}; + const utf8_codepoint* cp = {}; + std::vector<table*> implicit_tables; + std::vector<table*> dotted_key_tables; + std::vector<table*> open_inline_tables; + std::vector<array*> table_arrays; + parse_key_buffer key_buffer; + std::string string_buffer; + std::string recording_buffer; // for diagnostics + bool recording = false, recording_whitespace = true; + std::string_view current_scope; + size_t nested_values = {}; #if !TOML_EXCEPTIONS - mutable optional<parse_error> err; + mutable optional<parse_error> err; #endif - TOML_NODISCARD - source_position current_position(source_index fallback_offset = 0) const noexcept - { - if (!is_eof()) - return cp->position; - return { prev_pos.line, static_cast<source_index>(prev_pos.column + fallback_offset) }; - } + TOML_NODISCARD + source_position current_position(source_index fallback_offset = 0) const noexcept { + if (!is_eof()) return cp->position; + return {prev_pos.line, static_cast<source_index>(prev_pos.column + fallback_offset)}; + } - template <typename... T> - TOML_RETURNS_BY_THROWING - TOML_NEVER_INLINE - void set_error_at(source_position pos, const T&... reason) const - { - static_assert(sizeof...(T) > 0); - return_if_error(); + template <typename... T> + TOML_RETURNS_BY_THROWING TOML_NEVER_INLINE void set_error_at(source_position pos, + const T&... reason) const { + static_assert(sizeof...(T) > 0); + return_if_error(); - error_builder builder{ current_scope }; - (builder.append(reason), ...); + error_builder builder{current_scope}; + (builder.append(reason), ...); - parse_error_break(); + parse_error_break(); #if TOML_EXCEPTIONS - builder.finish(pos, reader.source_path()); + builder.finish(pos, reader.source_path()); #else - err.emplace(builder.finish(pos, reader.source_path())); + err.emplace(builder.finish(pos, reader.source_path())); #endif - } + } - template <typename... T> - TOML_RETURNS_BY_THROWING - void set_error(const T&... reason) const - { - set_error_at(current_position(1), reason...); - } + template <typename... T> + TOML_RETURNS_BY_THROWING void set_error(const T&... reason) const { + set_error_at(current_position(1), reason...); + } - void go_back(size_t count = 1) noexcept - { - return_if_error(); - TOML_ASSERT_ASSUME(count); + void go_back(size_t count = 1) noexcept { + return_if_error(); + TOML_ASSERT_ASSUME(count); - cp = reader.step_back(count); - prev_pos = cp->position; - } + cp = reader.step_back(count); + prev_pos = cp->position; + } - void advance() - { - return_if_error(); - assert_not_eof(); + void advance() { + return_if_error(); + assert_not_eof(); - prev_pos = cp->position; - cp = reader.read_next(); + prev_pos = cp->position; + 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 (recording && !is_eof()) - { - if (recording_whitespace || !is_whitespace(*cp)) - recording_buffer.append(cp->bytes, cp->count); - } - } - - void start_recording(bool include_current = true) noexcept - { - return_if_error(); - - recording = true; - recording_whitespace = true; - recording_buffer.clear(); - if (include_current && !is_eof()) - recording_buffer.append(cp->bytes, cp->count); - } - - void stop_recording(size_t pop_bytes = 0) noexcept - { - return_if_error(); - - recording = false; - if (pop_bytes) - { - if (pop_bytes >= recording_buffer.length()) - recording_buffer.clear(); - else if (pop_bytes == 1u) - recording_buffer.pop_back(); - else - recording_buffer.erase(recording_buffer.begin() - + static_cast<ptrdiff_t>(recording_buffer.length() - pop_bytes), - recording_buffer.end()); - } - } - - bool consume_leading_whitespace() - { - return_if_error_or_eof({}); - - bool consumed = false; - while (!is_eof() && is_horizontal_whitespace(*cp)) - { - if TOML_UNLIKELY(!is_ascii_horizontal_whitespace(*cp)) - set_error_and_return_default("expected space or tab, saw '"sv, escaped_codepoint{ *cp }, "'"sv); - - consumed = true; - advance_and_return_if_error({}); - } - return consumed; - } - - bool consume_line_break() - { - return_if_error_or_eof({}); - - if TOML_UNLIKELY(is_match(*cp, U'\v', U'\f')) - set_error_and_return_default( - R"(vertical tabs '\v' and form-feeds '\f' are not legal line breaks in TOML)"sv); - - if (*cp == U'\r') - { - advance_and_return_if_error({}); // skip \r - - if TOML_UNLIKELY(is_eof()) - set_error_and_return_default("expected '\\n' after '\\r', saw EOF"sv); - - if TOML_UNLIKELY(*cp != U'\n') - set_error_and_return_default("expected '\\n' after '\\r', saw '"sv, - escaped_codepoint{ *cp }, - "'"sv); - } - else if (*cp != U'\n') - return false; - - advance_and_return_if_error({}); // skip \n - return true; - } - - bool consume_rest_of_line() - { - return_if_error_or_eof({}); - - do - { - if (is_ascii_vertical_whitespace(*cp)) - return consume_line_break(); - else - advance(); - return_if_error({}); - } - while (!is_eof()); - - return true; - } - - bool consume_comment() - { - return_if_error_or_eof({}); - - if (*cp != U'#') - return false; - - push_parse_scope("comment"sv); - - advance_and_return_if_error({}); // skip the '#' - - while (!is_eof()) - { - if (consume_line_break()) - return true; - return_if_error({}); + if (recording && !is_eof()) { + if (recording_whitespace || !is_whitespace(*cp)) + recording_buffer.append(cp->bytes, cp->count); + } + } + + void start_recording(bool include_current = true) noexcept { + return_if_error(); + + recording = true; + recording_whitespace = true; + recording_buffer.clear(); + if (include_current && !is_eof()) recording_buffer.append(cp->bytes, cp->count); + } + + void stop_recording(size_t pop_bytes = 0) noexcept { + return_if_error(); + + recording = false; + if (pop_bytes) { + if (pop_bytes >= recording_buffer.length()) + recording_buffer.clear(); + else if (pop_bytes == 1u) + recording_buffer.pop_back(); + else + recording_buffer.erase(recording_buffer.begin() + + static_cast<ptrdiff_t>(recording_buffer.length() - pop_bytes), + recording_buffer.end()); + } + } + + bool consume_leading_whitespace() { + return_if_error_or_eof({}); + + bool consumed = false; + while (!is_eof() && is_horizontal_whitespace(*cp)) { + if TOML_UNLIKELY (!is_ascii_horizontal_whitespace(*cp)) + set_error_and_return_default("expected space or tab, saw '"sv, escaped_codepoint{*cp}, + "'"sv); + + consumed = true; + advance_and_return_if_error({}); + } + return consumed; + } + + bool consume_line_break() { + return_if_error_or_eof({}); + + if TOML_UNLIKELY (is_match(*cp, U'\v', U'\f')) + set_error_and_return_default( + R"(vertical tabs '\v' and form-feeds '\f' are not legal line breaks in TOML)"sv); + + if (*cp == U'\r') { + advance_and_return_if_error({}); // skip \r + + if TOML_UNLIKELY (is_eof()) + set_error_and_return_default("expected '\\n' after '\\r', saw EOF"sv); + + if TOML_UNLIKELY (*cp != U'\n') + set_error_and_return_default("expected '\\n' after '\\r', saw '"sv, + escaped_codepoint{*cp}, "'"sv); + } else if (*cp != U'\n') + return false; + + advance_and_return_if_error({}); // skip \n + return true; + } + + bool consume_rest_of_line() { + return_if_error_or_eof({}); + + do { + if (is_ascii_vertical_whitespace(*cp)) + return consume_line_break(); + else + advance(); + return_if_error({}); + } while (!is_eof()); + + return true; + } + + bool consume_comment() { + return_if_error_or_eof({}); + + if (*cp != U'#') return false; + + push_parse_scope("comment"sv); + + advance_and_return_if_error({}); // skip the '#' + + while (!is_eof()) { + if (consume_line_break()) return true; + return_if_error({}); #if TOML_LANG_AT_LEAST(1, 0, 0) - // toml/issues/567 (disallow non-TAB control characters in comments) - if TOML_UNLIKELY(is_nontab_control_character(*cp)) - set_error_and_return_default( - "control characters other than TAB (U+0009) are explicitly prohibited in comments"sv); + // toml/issues/567 (disallow non-TAB control characters in comments) + if TOML_UNLIKELY (is_nontab_control_character(*cp)) + set_error_and_return_default( + "control characters other than TAB (U+0009) are explicitly prohibited in comments"sv); - // toml/pull/720 (disallow surrogates in comments) - else if TOML_UNLIKELY(is_unicode_surrogate(*cp)) - set_error_and_return_default( - "unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv); + // toml/pull/720 (disallow surrogates in comments) + else if TOML_UNLIKELY (is_unicode_surrogate(*cp)) + set_error_and_return_default( + "unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited in comments"sv); #endif - advance_and_return_if_error({}); - } - - return true; - } - - TOML_NODISCARD - bool consume_expected_sequence(std::u32string_view seq) - { - return_if_error({}); - TOML_ASSERT(!seq.empty()); - - for (auto c : seq) - { - set_error_and_return_if_eof({}); - if (*cp != c) - return false; - advance_and_return_if_error({}); - } - return true; - } - - template <typename T> - TOML_NODISCARD - bool consume_digit_sequence(T* digits, size_t len) - { - return_if_error({}); - TOML_ASSERT_ASSUME(digits); - TOML_ASSERT_ASSUME(len); - - for (size_t i = 0; i < len; i++) - { - set_error_and_return_if_eof({}); - if (!is_decimal_digit(*cp)) - return false; - - digits[i] = static_cast<T>(*cp - U'0'); - advance_and_return_if_error({}); - } - return true; - } - - template <typename T> - TOML_NODISCARD - size_t consume_variable_length_digit_sequence(T* buffer, size_t max_len) - { - return_if_error({}); - TOML_ASSERT_ASSUME(buffer); - TOML_ASSERT_ASSUME(max_len); - - size_t i = {}; - for (; i < max_len; i++) - { - if (is_eof() || !is_decimal_digit(*cp)) - break; - - buffer[i] = static_cast<T>(*cp - U'0'); - advance_and_return_if_error({}); - } - return i; - } - - TOML_NODISCARD - TOML_NEVER_INLINE - std::string_view parse_basic_string(bool multi_line) - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(*cp == U'"'); - push_parse_scope("string"sv); - - // skip the '"' - advance_and_return_if_error_or_eof({}); - - // multi-line strings ignore a single line ending right at the beginning - if (multi_line) - { - consume_line_break(); - return_if_error({}); - set_error_and_return_if_eof({}); - } - - auto& str = string_buffer; - str.clear(); - bool escaped = false; - bool skipping_whitespace = false; - do - { - if (escaped) - { - escaped = false; - - // handle 'line ending slashes' in multi-line mode - if (multi_line && is_whitespace(*cp)) - { - consume_leading_whitespace(); - - if TOML_UNLIKELY(!consume_line_break()) - set_error_and_return_default( - "line-ending backslashes must be the last non-whitespace character on the line"sv); - - skipping_whitespace = true; - return_if_error({}); - continue; - } - - bool skip_escaped_codepoint = true; - assert_not_eof(); - switch (const auto escaped_codepoint = *cp) - { - // 'regular' escape codes - case U'b': str += '\b'; break; - case U'f': str += '\f'; break; - case U'n': str += '\n'; break; - case U'r': str += '\r'; break; - case U't': str += '\t'; break; - case U'"': str += '"'; break; - case U'\\': str += '\\'; break; - -#if TOML_LANG_UNRELEASED // toml/pull/790 (\e shorthand for \x1B) - case U'e': str += '\x1B'; break; + advance_and_return_if_error({}); + } + + return true; + } + + TOML_NODISCARD + bool consume_expected_sequence(std::u32string_view seq) { + return_if_error({}); + TOML_ASSERT(!seq.empty()); + + for (auto c : seq) { + set_error_and_return_if_eof({}); + if (*cp != c) return false; + advance_and_return_if_error({}); + } + return true; + } + + template <typename T> + TOML_NODISCARD bool consume_digit_sequence(T* digits, size_t len) { + return_if_error({}); + TOML_ASSERT_ASSUME(digits); + TOML_ASSERT_ASSUME(len); + + for (size_t i = 0; i < len; i++) { + set_error_and_return_if_eof({}); + if (!is_decimal_digit(*cp)) return false; + + digits[i] = static_cast<T>(*cp - U'0'); + advance_and_return_if_error({}); + } + return true; + } + + template <typename T> + TOML_NODISCARD size_t consume_variable_length_digit_sequence(T* buffer, size_t max_len) { + return_if_error({}); + TOML_ASSERT_ASSUME(buffer); + TOML_ASSERT_ASSUME(max_len); + + size_t i = {}; + for (; i < max_len; i++) { + if (is_eof() || !is_decimal_digit(*cp)) break; + + buffer[i] = static_cast<T>(*cp - U'0'); + advance_and_return_if_error({}); + } + return i; + } + + TOML_NODISCARD + TOML_NEVER_INLINE + std::string_view parse_basic_string(bool multi_line) { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(*cp == U'"'); + push_parse_scope("string"sv); + + // skip the '"' + advance_and_return_if_error_or_eof({}); + + // multi-line strings ignore a single line ending right at the beginning + if (multi_line) { + consume_line_break(); + return_if_error({}); + set_error_and_return_if_eof({}); + } + + auto& str = string_buffer; + str.clear(); + bool escaped = false; + bool skipping_whitespace = false; + do { + if (escaped) { + escaped = false; + + // handle 'line ending slashes' in multi-line mode + if (multi_line && is_whitespace(*cp)) { + consume_leading_whitespace(); + + if TOML_UNLIKELY (!consume_line_break()) + set_error_and_return_default( + "line-ending backslashes must be the last non-whitespace character on the line"sv); + + skipping_whitespace = true; + return_if_error({}); + continue; + } + + bool skip_escaped_codepoint = true; + assert_not_eof(); + switch (const auto escaped_codepoint = *cp) { + // 'regular' escape codes + case U'b': + str += '\b'; + break; + case U'f': + str += '\f'; + break; + case U'n': + str += '\n'; + break; + case U'r': + str += '\r'; + break; + case U't': + str += '\t'; + break; + case U'"': + str += '"'; + break; + case U'\\': + str += '\\'; + break; + +#if TOML_LANG_UNRELEASED // toml/pull/790 (\e shorthand for \x1B) + case U'e': + str += '\x1B'; + break; #else - case U'e': - set_error_and_return_default( - "escape sequence '\\e' is not supported in TOML 1.0.0 and earlier"sv); + case U'e': + set_error_and_return_default( + "escape sequence '\\e' is not supported in TOML 1.0.0 and earlier"sv); #endif -#if TOML_LANG_UNRELEASED // toml/pull/796 (\xHH unicode scalar sequences) - case U'x': [[fallthrough]]; +#if TOML_LANG_UNRELEASED // toml/pull/796 (\xHH unicode scalar sequences) + case U'x': + [[fallthrough]]; #else - case U'x': - set_error_and_return_default( - "escape sequence '\\x' is not supported in TOML 1.0.0 and earlier"sv); + case U'x': + set_error_and_return_default( + "escape sequence '\\x' is not supported in TOML 1.0.0 and earlier"sv); #endif - // unicode scalar sequences - case U'u': [[fallthrough]]; - case U'U': - { - push_parse_scope("unicode scalar sequence"sv); - advance_and_return_if_error_or_eof({}); - skip_escaped_codepoint = false; - - uint32_t place_value = - escaped_codepoint == U'U' ? 0x10000000u : (escaped_codepoint == U'u' ? 0x1000u : 0x10u); - uint32_t sequence_value{}; - while (place_value) - { - set_error_and_return_if_eof({}); - - if TOML_UNLIKELY(!is_hexadecimal_digit(*cp)) - set_error_and_return_default("expected hex digit, saw '"sv, to_sv(*cp), "'"sv); - - sequence_value += place_value * hex_to_dec(*cp); - place_value /= 16u; - advance_and_return_if_error({}); - } - - if TOML_UNLIKELY(is_unicode_surrogate(sequence_value)) - set_error_and_return_default( - "unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv); - else if TOML_UNLIKELY(sequence_value > 0x10FFFFu) - set_error_and_return_default("values greater than U+10FFFF are invalid"sv); - - if (sequence_value < 0x80) - { - str += static_cast<char>(sequence_value); - } - else if (sequence_value < 0x800u) - { - str += static_cast<char>((sequence_value >> 6) | 0xC0u); - str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u); - } - else if (sequence_value < 0x10000u) - { - str += static_cast<char>((sequence_value >> 12) | 0xE0u); - str += static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u); - str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u); - } - else if (sequence_value < 0x110000u) - { - str += static_cast<char>((sequence_value >> 18) | 0xF0u); - str += static_cast<char>(((sequence_value >> 12) & 0x3Fu) | 0x80u); - str += static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u); - str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u); - } - break; - } - - // ??? - TOML_UNLIKELY_CASE - default: set_error_and_return_default("unknown escape sequence '\\"sv, to_sv(*cp), "'"sv); - } - - if (skip_escaped_codepoint) - advance_and_return_if_error_or_eof({}); - } - else - { - // handle closing delimiters - if (*cp == U'"') - { - if (multi_line) - { - size_t lookaheads = {}; - size_t consecutive_delimiters = 1; - do - { - advance_and_return_if_error({}); - lookaheads++; - if (!is_eof() && *cp == U'"') - consecutive_delimiters++; - else - break; - } - while (lookaheads < 4u); - - switch (consecutive_delimiters) - { - // """ " (one quote somewhere in a ML string) - case 1: - str += '"'; - skipping_whitespace = false; - continue; - - // """ "" (two quotes somewhere in a ML string) - case 2: - str.append("\"\""sv); - skipping_whitespace = false; - continue; - - // """ """ (the end of the string) - case 3: return str; - - // """ """" (one at the end of the string) - case 4: str += '"'; return str; - - // """ """"" (two quotes at the end of the string) - case 5: - str.append("\"\""sv); - advance_and_return_if_error({}); // skip the last '"' - return str; - - default: TOML_UNREACHABLE; - } - } - else - { - advance_and_return_if_error({}); // skip the closing delimiter - return str; - } - } - - // handle escapes - else if (*cp == U'\\') - { - advance_and_return_if_error_or_eof({}); // skip the '\' - skipping_whitespace = false; - escaped = true; - continue; - } - - // handle line endings in multi-line mode - if (multi_line && is_ascii_vertical_whitespace(*cp)) - { - consume_line_break(); - return_if_error({}); - if (!skipping_whitespace) - str += '\n'; - continue; - } - - // handle control characters - if TOML_UNLIKELY(is_nontab_control_character(*cp)) - set_error_and_return_default( - "unescaped control characters other than TAB (U+0009) are explicitly prohibited"sv); + // unicode scalar sequences + case U'u': + [[fallthrough]]; + case U'U': { + push_parse_scope("unicode scalar sequence"sv); + advance_and_return_if_error_or_eof({}); + skip_escaped_codepoint = false; + + uint32_t place_value = escaped_codepoint == U'U' + ? 0x10000000u + : (escaped_codepoint == U'u' ? 0x1000u : 0x10u); + uint32_t sequence_value{}; + while (place_value) { + set_error_and_return_if_eof({}); + + if TOML_UNLIKELY (!is_hexadecimal_digit(*cp)) + set_error_and_return_default("expected hex digit, saw '"sv, to_sv(*cp), "'"sv); + + sequence_value += place_value * hex_to_dec(*cp); + place_value /= 16u; + advance_and_return_if_error({}); + } + + if TOML_UNLIKELY (is_unicode_surrogate(sequence_value)) + set_error_and_return_default( + "unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv); + else if TOML_UNLIKELY (sequence_value > 0x10FFFFu) + set_error_and_return_default("values greater than U+10FFFF are invalid"sv); + + if (sequence_value < 0x80) { + str += static_cast<char>(sequence_value); + } else if (sequence_value < 0x800u) { + str += static_cast<char>((sequence_value >> 6) | 0xC0u); + str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u); + } else if (sequence_value < 0x10000u) { + str += static_cast<char>((sequence_value >> 12) | 0xE0u); + str += static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u); + str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u); + } else if (sequence_value < 0x110000u) { + str += static_cast<char>((sequence_value >> 18) | 0xF0u); + str += static_cast<char>(((sequence_value >> 12) & 0x3Fu) | 0x80u); + str += static_cast<char>(((sequence_value >> 6) & 0x3Fu) | 0x80u); + str += static_cast<char>((sequence_value & 0x3Fu) | 0x80u); + } + break; + } + + // ??? + TOML_UNLIKELY_CASE + default: + set_error_and_return_default("unknown escape sequence '\\"sv, to_sv(*cp), "'"sv); + } + + if (skip_escaped_codepoint) advance_and_return_if_error_or_eof({}); + } else { + // handle closing delimiters + if (*cp == U'"') { + if (multi_line) { + size_t lookaheads = {}; + size_t consecutive_delimiters = 1; + do { + advance_and_return_if_error({}); + lookaheads++; + if (!is_eof() && *cp == U'"') + consecutive_delimiters++; + else + break; + } while (lookaheads < 4u); + + switch (consecutive_delimiters) { + // """ " (one quote somewhere in a ML string) + case 1: + str += '"'; + skipping_whitespace = false; + continue; + + // """ "" (two quotes somewhere in a ML string) + case 2: + str.append("\"\""sv); + skipping_whitespace = false; + continue; + + // """ """ (the end of the string) + case 3: + return str; + + // """ """" (one at the end of the string) + case 4: + str += '"'; + return str; + + // """ """"" (two quotes at the end of the string) + case 5: + str.append("\"\""sv); + advance_and_return_if_error({}); // skip the last '"' + return str; + + default: + TOML_UNREACHABLE; + } + } else { + advance_and_return_if_error({}); // skip the closing delimiter + return str; + } + } + + // handle escapes + else if (*cp == U'\\') { + advance_and_return_if_error_or_eof({}); // skip the '\' + skipping_whitespace = false; + escaped = true; + continue; + } + + // handle line endings in multi-line mode + if (multi_line && is_ascii_vertical_whitespace(*cp)) { + consume_line_break(); + return_if_error({}); + if (!skipping_whitespace) str += '\n'; + continue; + } + + // handle control characters + if TOML_UNLIKELY (is_nontab_control_character(*cp)) + set_error_and_return_default( + "unescaped control characters other than TAB (U+0009) are explicitly prohibited"sv); #if TOML_LANG_AT_LEAST(1, 0, 0) - // handle surrogates in strings - if TOML_UNLIKELY(is_unicode_surrogate(*cp)) - set_error_and_return_default( - "unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv); + // handle surrogates in strings + if TOML_UNLIKELY (is_unicode_surrogate(*cp)) + set_error_and_return_default( + "unescaped unicode surrogates (U+D800 to U+DFFF) are explicitly prohibited"sv); #endif - if (multi_line) - { - if (!skipping_whitespace || !is_horizontal_whitespace(*cp)) - { - skipping_whitespace = false; - str.append(cp->bytes, cp->count); - } - } - else - str.append(cp->bytes, cp->count); - - advance_and_return_if_error({}); - } - } - while (!is_eof()); - - set_error_and_return_default("encountered end-of-file"sv); - } - - TOML_NODISCARD - TOML_NEVER_INLINE - std::string_view parse_literal_string(bool multi_line) - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(*cp == U'\''); - push_parse_scope("literal string"sv); - - // skip the delimiter - advance_and_return_if_error_or_eof({}); - - // multi-line strings ignore a single line ending right at the beginning - if (multi_line) - { - consume_line_break(); - return_if_error({}); - set_error_and_return_if_eof({}); - } - - auto& str = string_buffer; - str.clear(); - do - { - return_if_error({}); - - // handle closing delimiters - if (*cp == U'\'') - { - if (multi_line) - { - size_t lookaheads = {}; - size_t consecutive_delimiters = 1; - do - { - advance_and_return_if_error({}); - lookaheads++; - if (!is_eof() && *cp == U'\'') - consecutive_delimiters++; - else - break; - } - while (lookaheads < 4u); - - switch (consecutive_delimiters) - { - // ''' ' (one quote somewhere in a ML string) - case 1: str += '\''; continue; - - // ''' '' (two quotes somewhere in a ML string) - case 2: str.append("''"sv); continue; - - // ''' ''' (the end of the string) - case 3: return str; - - // ''' '''' (one at the end of the string) - case 4: str += '\''; return str; - - // ''' ''''' (two quotes at the end of the string) - case 5: - str.append("''"sv); - advance_and_return_if_error({}); // skip the last ' - return str; - - default: TOML_UNREACHABLE; - } - } - else - { - advance_and_return_if_error({}); // skip the closing delimiter - return str; - } - } - - // handle line endings in multi-line mode - if (multi_line && is_ascii_vertical_whitespace(*cp)) - { - consume_line_break(); - return_if_error({}); - str += '\n'; - continue; - } - - // handle control characters - if TOML_UNLIKELY(is_nontab_control_character(*cp)) - set_error_and_return_default( - "control characters other than TAB (U+0009) are explicitly prohibited"sv); + if (multi_line) { + if (!skipping_whitespace || !is_horizontal_whitespace(*cp)) { + skipping_whitespace = false; + str.append(cp->bytes, cp->count); + } + } else + str.append(cp->bytes, cp->count); + + advance_and_return_if_error({}); + } + } while (!is_eof()); + + set_error_and_return_default("encountered end-of-file"sv); + } + + TOML_NODISCARD + TOML_NEVER_INLINE + std::string_view parse_literal_string(bool multi_line) { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(*cp == U'\''); + push_parse_scope("literal string"sv); + + // skip the delimiter + advance_and_return_if_error_or_eof({}); + + // multi-line strings ignore a single line ending right at the beginning + if (multi_line) { + consume_line_break(); + return_if_error({}); + set_error_and_return_if_eof({}); + } + + auto& str = string_buffer; + str.clear(); + do { + return_if_error({}); + + // handle closing delimiters + if (*cp == U'\'') { + if (multi_line) { + size_t lookaheads = {}; + size_t consecutive_delimiters = 1; + do { + advance_and_return_if_error({}); + lookaheads++; + if (!is_eof() && *cp == U'\'') + consecutive_delimiters++; + else + break; + } while (lookaheads < 4u); + + switch (consecutive_delimiters) { + // ''' ' (one quote somewhere in a ML string) + case 1: + str += '\''; + continue; + + // ''' '' (two quotes somewhere in a ML string) + case 2: + str.append("''"sv); + continue; + + // ''' ''' (the end of the string) + case 3: + return str; + + // ''' '''' (one at the end of the string) + case 4: + str += '\''; + return str; + + // ''' ''''' (two quotes at the end of the string) + case 5: + str.append("''"sv); + advance_and_return_if_error({}); // skip the last ' + return str; + + default: + TOML_UNREACHABLE; + } + } else { + advance_and_return_if_error({}); // skip the closing delimiter + return str; + } + } + + // handle line endings in multi-line mode + if (multi_line && is_ascii_vertical_whitespace(*cp)) { + consume_line_break(); + return_if_error({}); + str += '\n'; + continue; + } + + // handle control characters + if TOML_UNLIKELY (is_nontab_control_character(*cp)) + set_error_and_return_default( + "control characters other than TAB (U+0009) are explicitly prohibited"sv); #if TOML_LANG_AT_LEAST(1, 0, 0) - // handle surrogates in strings - if TOML_UNLIKELY(is_unicode_surrogate(*cp)) - set_error_and_return_default("unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv); + // handle surrogates in strings + if TOML_UNLIKELY (is_unicode_surrogate(*cp)) + set_error_and_return_default( + "unicode surrogates (U+D800 - U+DFFF) are explicitly prohibited"sv); #endif - str.append(cp->bytes, cp->count); - advance_and_return_if_error({}); - } - while (!is_eof()); - - set_error_and_return_default("encountered end-of-file"sv); - } - - TOML_NODISCARD - TOML_NEVER_INLINE - parsed_string parse_string() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(is_string_delimiter(*cp)); - push_parse_scope("string"sv); - - // get the first three characters to determine the string type - const auto first = cp->value; - advance_and_return_if_error_or_eof({}); - const auto second = cp->value; - advance_and_return_if_error({}); - const auto third = cp ? cp->value : U'\0'; - - // if we were eof at the third character then first and second need to be - // the same string character (otherwise it's an unterminated string) - if (is_eof()) - { - if (second == first) - return {}; - - set_error_and_return_default("encountered end-of-file"sv); - } - - // if the first three characters are all the same string delimiter then - // it's a multi-line string. - else if (first == second && first == third) - { - return { first == U'\'' ? parse_literal_string(true) : parse_basic_string(true), true }; - } - - // otherwise it's just a regular string. - else - { - // step back two characters so that the current - // character is the string delimiter - go_back(2u); - - return { first == U'\'' ? parse_literal_string(false) : parse_basic_string(false), false }; - } - } - - TOML_NODISCARD - TOML_NEVER_INLINE - std::string_view parse_bare_key_segment() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(is_bare_key_character(*cp)); - - string_buffer.clear(); - - while (!is_eof()) - { - if (!is_bare_key_character(*cp)) - break; - - string_buffer.append(cp->bytes, cp->count); - advance_and_return_if_error({}); - } - - return string_buffer; - } - - TOML_NODISCARD - TOML_NEVER_INLINE - bool parse_boolean() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(is_match(*cp, U't', U'f', U'T', U'F')); - push_parse_scope("boolean"sv); - - start_recording(true); - auto result = is_match(*cp, U't', U'T'); - if (!consume_expected_sequence(result ? U"true"sv : U"false"sv)) - set_error_and_return_default("expected '"sv, - to_sv(result), - "', saw '"sv, - to_sv(recording_buffer), - "'"sv); - stop_recording(); - - if (cp && !is_value_terminator(*cp)) - set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); - - return result; - } - - TOML_NODISCARD - TOML_NEVER_INLINE - double parse_inf_or_nan() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(is_match(*cp, U'i', U'n', U'I', U'N', U'+', U'-')); - push_parse_scope("floating-point"sv); - - start_recording(true); - const bool negative = *cp == U'-'; - if (negative || *cp == U'+') - advance_and_return_if_error_or_eof({}); - - const bool inf = is_match(*cp, U'i', U'I'); - if (!consume_expected_sequence(inf ? U"inf"sv : U"nan"sv)) - set_error_and_return_default("expected '"sv, - inf ? "inf"sv : "nan"sv, - "', saw '"sv, - to_sv(recording_buffer), - "'"sv); - stop_recording(); - - if (cp && !is_value_terminator(*cp)) - set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); - - return inf ? (negative ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity()) - : std::numeric_limits<double>::quiet_NaN(); - } - - TOML_NODISCARD - TOML_NEVER_INLINE - double parse_float() - { - return_if_error({}); - assert_not_eof(); - TOML_ASSERT_ASSUME(is_match(*cp, U'+', U'-', U'.') || is_decimal_digit(*cp)); - push_parse_scope("floating-point"sv); - - // sign - const int sign = *cp == U'-' ? -1 : 1; - if (is_match(*cp, U'+', U'-')) - advance_and_return_if_error_or_eof({}); - - // consume value chars - char chars[utf8_buffered_reader::max_history_length]; - size_t length = {}; - const utf8_codepoint* prev = {}; - bool seen_decimal = false, seen_exponent = false; - char first_integer_part = '\0'; - while (!is_eof() && !is_value_terminator(*cp)) - { - if (*cp == U'_') - { - if (!prev || !is_decimal_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'_' && !is_decimal_digit(*cp)) - set_error_and_return_default("underscores must be followed by digits"sv); - else if TOML_UNLIKELY(length == sizeof(chars)) - set_error_and_return_default("exceeds length limit of "sv, - sizeof(chars), - " digits"sv, - (seen_exponent ? ""sv : " (consider using exponent notation)"sv)); - else if (*cp == U'.') - { - // .1 - // -.1 - // +.1 (no integer part) - if (!first_integer_part) - set_error_and_return_default("expected decimal digit, saw '.'"sv); - - // 1.0e+.10 (exponent cannot have '.') - else if (seen_exponent) - set_error_and_return_default("expected exponent decimal digit or sign, saw '.'"sv); - - // 1.0.e+.10 - // 1..0 - // (multiple '.') - else if (seen_decimal) - set_error_and_return_default("expected decimal digit or exponent, saw '.'"sv); - - seen_decimal = true; - } - else if (is_match(*cp, U'e', U'E')) - { - if (prev && !is_decimal_digit(*prev)) - set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); - - // 1.0ee+10 (multiple 'e') - else if (seen_exponent) - set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); - - seen_decimal = true; // implied - seen_exponent = true; - } - else if (is_match(*cp, U'+', U'-')) - { - // 1.-0 (sign in mantissa) - if (!seen_exponent) - set_error_and_return_default("expected decimal digit or '.', saw '"sv, to_sv(*cp), "'"sv); - - // 1.0e1-0 (misplaced exponent sign) - else if (!is_match(*prev, U'e', U'E')) - set_error_and_return_default("expected exponent digit, saw '"sv, to_sv(*cp), "'"sv); - } - else if (is_decimal_digit(*cp)) - { - if (!seen_decimal) - { - if (!first_integer_part) - first_integer_part = static_cast<char>(cp->bytes[0]); - else if (first_integer_part == '0') - set_error_and_return_default("leading zeroes are prohibited"sv); - } - } - else - set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); - - chars[length++] = static_cast<char>(cp->bytes[0]); - prev = cp; - advance_and_return_if_error({}); - } - - // sanity-check ending state - if (prev) - { - if (*prev == U'_') - { - set_error_and_return_if_eof({}); - set_error_and_return_default("underscores must be followed by digits"sv); - } - else if (is_match(*prev, U'e', U'E', U'+', U'-', U'.')) - { - set_error_and_return_if_eof({}); - set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); - } - } - - // convert to double - double result; + str.append(cp->bytes, cp->count); + advance_and_return_if_error({}); + } while (!is_eof()); + + set_error_and_return_default("encountered end-of-file"sv); + } + + TOML_NODISCARD + TOML_NEVER_INLINE + parsed_string parse_string() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_string_delimiter(*cp)); + push_parse_scope("string"sv); + + // get the first three characters to determine the string type + const auto first = cp->value; + advance_and_return_if_error_or_eof({}); + const auto second = cp->value; + advance_and_return_if_error({}); + const auto third = cp ? cp->value : U'\0'; + + // if we were eof at the third character then first and second need to be + // the same string character (otherwise it's an unterminated string) + if (is_eof()) { + if (second == first) return {}; + + set_error_and_return_default("encountered end-of-file"sv); + } + + // if the first three characters are all the same string delimiter then + // it's a multi-line string. + else if (first == second && first == third) { + return {first == U'\'' ? parse_literal_string(true) : parse_basic_string(true), true}; + } + + // otherwise it's just a regular string. + else { + // step back two characters so that the current + // character is the string delimiter + go_back(2u); + + return {first == U'\'' ? parse_literal_string(false) : parse_basic_string(false), false}; + } + } + + TOML_NODISCARD + TOML_NEVER_INLINE + std::string_view parse_bare_key_segment() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_bare_key_character(*cp)); + + string_buffer.clear(); + + while (!is_eof()) { + if (!is_bare_key_character(*cp)) break; + + string_buffer.append(cp->bytes, cp->count); + advance_and_return_if_error({}); + } + + return string_buffer; + } + + TOML_NODISCARD + TOML_NEVER_INLINE + bool parse_boolean() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_match(*cp, U't', U'f', U'T', U'F')); + push_parse_scope("boolean"sv); + + start_recording(true); + auto result = is_match(*cp, U't', U'T'); + if (!consume_expected_sequence(result ? U"true"sv : U"false"sv)) + set_error_and_return_default("expected '"sv, to_sv(result), "', saw '"sv, + to_sv(recording_buffer), "'"sv); + stop_recording(); + + if (cp && !is_value_terminator(*cp)) + set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); + + return result; + } + + TOML_NODISCARD + TOML_NEVER_INLINE + double parse_inf_or_nan() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_match(*cp, U'i', U'n', U'I', U'N', U'+', U'-')); + push_parse_scope("floating-point"sv); + + start_recording(true); + const bool negative = *cp == U'-'; + if (negative || *cp == U'+') advance_and_return_if_error_or_eof({}); + + const bool inf = is_match(*cp, U'i', U'I'); + if (!consume_expected_sequence(inf ? U"inf"sv : U"nan"sv)) + set_error_and_return_default("expected '"sv, inf ? "inf"sv : "nan"sv, "', saw '"sv, + to_sv(recording_buffer), "'"sv); + stop_recording(); + + if (cp && !is_value_terminator(*cp)) + set_error_and_return_default("expected value-terminator, saw '"sv, to_sv(*cp), "'"sv); + + return inf ? (negative ? -std::numeric_limits<double>::infinity() + : std::numeric_limits<double>::infinity()) + : std::numeric_limits<double>::quiet_NaN(); + } + + TOML_NODISCARD + TOML_NEVER_INLINE + double parse_float() { + return_if_error({}); + assert_not_eof(); + TOML_ASSERT_ASSUME(is_match(*cp, U'+', U'-', U'.') || is_decimal_digit(*cp)); + push_parse_scope("floating-point"sv); + + // sign + const int sign = *cp == U'-' ? -1 : 1; + if (is_match(*cp, U'+', U'-')) advance_and_return_if_error_or_eof({}); + + // consume value chars + char chars[utf8_buffered_reader::max_history_length]; + size_t length = {}; + const utf8_codepoint* prev = {}; + bool seen_decimal = false, seen_exponent = false; + char first_integer_part = '\0'; + while (!is_eof() && !is_value_terminator(*cp)) { + if (*cp == U'_') { + if (!prev || !is_decimal_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'_' && !is_decimal_digit(*cp)) + set_error_and_return_default("underscores must be followed by digits"sv); + else if TOML_UNLIKELY (length == sizeof(chars)) + set_error_and_return_default( + "exceeds length limit of "sv, sizeof(chars), " digits"sv, + (seen_exponent ? ""sv : " (consider using exponent notation)"sv)); + else if (*cp == U'.') { + // .1 + // -.1 + // +.1 (no integer part) + if (!first_integer_part) + set_error_and_return_default("expected decimal digit, saw '.'"sv); + + // 1.0e+.10 (exponent cannot have '.') + else if (seen_exponent) + set_error_and_return_default("expected exponent decimal digit or sign, saw '.'"sv); + + // 1.0.e+.10 + // 1..0 + // (multiple '.') + else if (seen_decimal) + set_error_and_return_default("expected decimal digit or exponent, saw '.'"sv); + + seen_decimal = true; + } else if (is_match(*cp, U'e', U'E')) { + if (prev && !is_decimal_digit(*prev)) + set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); + + // 1.0ee+10 (multiple 'e') + else if (seen_exponent) + set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); + + seen_decimal = true; // implied + seen_exponent = true; + } else if (is_match(*cp, U'+', U'-')) { + // 1.-0 (sign in mantissa) + if (!seen_exponent) + set_error_and_return_default("expected decimal digit or '.', saw '"sv, to_sv(*cp), + "'"sv); + + // 1.0e1-0 (misplaced exponent sign) + else if (!is_match(*prev, U'e', U'E')) + set_error_and_return_default("expected exponent digit, saw '"sv, to_sv(*cp), "'"sv); + } else if (is_decimal_digit(*cp)) { + if (!seen_decimal) { + if (!first_integer_part) + first_integer_part = static_cast<char>(cp->bytes[0]); + else if (first_integer_part == '0') + set_error_and_return_default("leading zeroes are prohibited"sv); + } + } else + set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); + + chars[length++] = static_cast<char>(cp->bytes[0]); + prev = cp; + advance_and_return_if_error({}); + } + + // sanity-check ending state + if (prev) { + if (*prev == U'_') { + set_error_and_return_if_eof({}); + set_error_and_return_default("underscores must be followed by digits"sv); + } else if (is_match(*prev, U'e', U'E', U'+', U'-', U'.')) { + set_error_and_return_if_eof({}); + set_error_and_return_default("expected decimal digit, saw '"sv, to_sv(*cp), "'"sv); + } + } + + // convert to double + double result; #if TOML_FLOAT_CHARCONV - { - auto fc_result = std::from_chars(chars, chars + length, result); - switch (fc_result.ec) - { - TOML_LIKELY_CASE - case std::errc{}: // ok - return result * sign; - - case std::errc::invalid_argument: - set_error_and_return_default("'"sv, - std::string_view{ chars, length }, - "' could not be interpreted as a value"sv); - break; - - case std::errc::result_out_of_range: - set_error_and_return_default("'"sv, - std::string_view{ chars, length }, - "' is not representable in 64 bits"sv); - break; - - default: //?? - set_error_and_return_default("an unspecified error occurred while trying to interpret '"sv, - std::string_view{ chars, length }, - "' as a value"sv); - } - } + { + auto fc_result = std::from_chars(chars, chars + length, result); + switch (fc_result.ec) { + TOML_LIKELY_CASE + case std::errc{}: // ok + return result * sign; + + case std::errc::invalid_argument: + set_error_and_return_default("'"sv, std::string_view{chars, length}, + "' could not be interpreted as a value"sv); + break; + + case std::errc::result_out_of_range: + set_error_and_return_default("'"sv, std::string_view{chars, length}, + "' is not representable in 64 bits"sv); + break; + + default: //?? + set_error_and_return_default( + "an unspecified error occurred while trying to interpret '"sv, + std::string_view{chars, length}, "' as a value"sv); + } + } #else - { - std::stringstream ss; - ss.imbue(std::locale::classic()); - ss.write(chars, static_cast<std::streamsize>(length)); - if ((ss >> result)) - return result * sign; - else - set_error_and_return_default("'"sv, - std::string_view{ chars, length }, - "' could not be interpreted as a value"sv); - } + { + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss.write(chars, static_cast<std::streamsize>(length)); + if ((ss >> result)) + return result * sign; + else + set_error_and_return_default("'"sv, std::string_view{chars, length}, + "' could not be interpreted as a value"sv); + } #endif - } - - 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({}); - - // <HEX DIGITS> ([.]<HEX DIGITS>)? [pP] [+-]? <DEC DIGITS> - - // 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<char>(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<uint32_t>(*c - '0'); - if (fragment_idx == 1) - sig++; - c++; - place /= base; - } - f.value = static_cast<double>(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 <uint64_t base> - TOML_NODISCARD - TOML_NEVER_INLINE - int64_t parse_integer() - { - return_if_error({}); - assert_not_eof(); - using traits = parse_integer_traits<base>; - 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<char>(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<int64_t>(hex_to_dec(digits[0])); - else - result = static_cast<int64_t>(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<size_t>(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<uint64_t>(*end - '0'); - - power *= base; - } - } - - // range check - static constexpr auto i64_max = static_cast<uint64_t>((std::numeric_limits<int64_t>::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<int64_t>::min)(); - - return static_cast<int64_t>(result) * sign; - } - else - return static_cast<int64_t>(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<decltype(time.second)>(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<size_t>(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<decltype(offset.minutes)>((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<uint_least32_t>(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<value_traits>(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<char32_t>(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<int64_t>(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<int64_t>().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<int64_t>(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<int64_t>().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<int64_t>().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<int64_t>().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<table>(); - } - 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<table>(pit, make_key(i)); - table& p = pit->second.ref_cast<table>(); - 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<table>(); - 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<array>(it, std::move(last_key)); - array& tbl_arr = it->second.ref_cast<array>(); - table_arrays.push_back(&tbl_arr); - tbl_arr.source_ = { header_begin_pos, header_end_pos, reader.source_path() }; - - table& tbl = tbl_arr.emplace_back<table>(); - 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<table>(it, std::move(last_key)); - table& tbl = it->second.ref_cast<table>(); - 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<table>(pit, make_key(i)); - table& p = pit->second.ref_cast<table>(); - 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<node_ptr>(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<table>(); - 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<array>(); - 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({}); + + // <HEX DIGITS> ([.]<HEX DIGITS>)? [pP] [+-]? <DEC DIGITS> + + // 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<char>(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<uint32_t>(*c - '0'); + if (fragment_idx == 1) sig++; + c++; + place /= base; + } + f.value = static_cast<double>(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 <uint64_t base> + TOML_NODISCARD TOML_NEVER_INLINE int64_t parse_integer() { + return_if_error({}); + assert_not_eof(); + using traits = parse_integer_traits<base>; + 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<char>(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<int64_t>(hex_to_dec(digits[0])); + else + result = static_cast<int64_t>(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<size_t>(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<uint64_t>(*end - '0'); + + power *= base; + } + } + + // range check + static constexpr auto i64_max = + static_cast<uint64_t>((std::numeric_limits<int64_t>::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<int64_t>::min)(); + + return static_cast<int64_t>(result) * sign; + } else + return static_cast<int64_t>(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<decltype(time.second)>(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<size_t>(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<decltype(offset.minutes)>((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<uint_least32_t>(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<value_traits>(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<char32_t>(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<int64_t>(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<int64_t>().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<int64_t>(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<int64_t>().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<int64_t>().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<int64_t>().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<table>(); + } 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<table>(pit, make_key(i)); + table& p = pit->second.ref_cast<table>(); + 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<table>(); + 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<array>(it, std::move(last_key)); + array& tbl_arr = it->second.ref_cast<array>(); + table_arrays.push_back(&tbl_arr); + tbl_arr.source_ = {header_begin_pos, header_end_pos, reader.source_path()}; + + table& tbl = tbl_arr.emplace_back<table>(); + 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<table>(it, std::move(last_key)); + table& tbl = it->second.ref_cast<table>(); + 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<table>(pit, make_key(i)); + table& p = pit->second.ref_cast<table>(); + 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<node_ptr>(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<table>(); + 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<array>(); + 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<array>(); - 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<node_ptr>(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<table>(); - 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<array>(); + 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<node_ptr>(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<table>(); + 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<const std::string>(std::move(path)) } +#define TOML_PARSE_FILE_ERROR(msg, path) \ + throw parse_error { \ + msg, source_position{}, std::make_shared<const std::string>(std::move(path)) \ + } #else -#define TOML_PARSE_FILE_ERROR(msg, path) \ - return parse_result \ - { \ - parse_error \ - { \ - msg, source_position{}, std::make_shared<const std::string>(std::move(path)) \ - } \ - } +#define TOML_PARSE_FILE_ERROR(msg, path) \ + return parse_result { \ + parse_error { \ + msg, source_position{}, std::make_shared<const std::string>(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<char> file_data; - file_data.resize(static_cast<size_t>(file_size)); - file.read(file_data.data(), static_cast<std::streamsize>(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<char> file_data; + file_data.resize(static_cast<size_t>(file_size)); + file.read(file_data.data(), static_cast<std::streamsize>(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 <mark.gillard@outlook.com.au> -//# 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 <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} -#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<path_component> & components) - { - using components_type = std::remove_reference_t<decltype(components)>; - - const auto original_size = components.size(); - - static constexpr auto on_key = [](void* data, std::string_view key) -> bool - { - auto& comps = *static_cast<components_type*>(data); - comps.emplace_back(key); - return true; - }; - - static constexpr auto on_index = [](void* data, size_t index) -> bool - { - auto& comps = *static_cast<components_type*>(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<path_component> & components) { + using components_type = std::remove_reference_t<decltype(components)>; + + const auto original_size = components.size(); + + static constexpr auto on_key = [](void* data, std::string_view key) -> bool { + auto& comps = *static_cast<components_type*>(data); + comps.emplace_back(key); + return true; + }; + + static constexpr auto on_index = [](void* data, size_t index) -> bool { + auto& comps = *static_cast<components_type*>(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<int>(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<int>(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<int>(n), - components_.end()); - } - - return leaf_path; - } - - TOML_EXTERNAL_LINKAGE - path path::subpath(std::vector<path_component>::const_iterator start, - std::vector<path_component>::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<int>(start), begin() + static_cast<int>(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<int>(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<int>(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<int>(n), components_.end()); + } + + return leaf_path; + } + + TOML_EXTERNAL_LINKAGE + path path::subpath(std::vector<path_component>::const_iterator start, + std::vector<path_component>::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<int>(start), begin() + static_cast<int>(start + length)); + } } TOML_NAMESPACE_END; -//#===================================================================================================================== -//# at_path() overloads for toml::path -//#===================================================================================================================== - -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - node_view<node> 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<array>(); - 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<table>(); - 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<const node> TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept - { - return node_view<const node>{ at_path(const_cast<node&>(root), path).node() }; - } +// #===================================================================================================================== +// # at_path() overloads for toml::path +// #===================================================================================================================== + +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + node_view<node> 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<array>(); + 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<table>(); + 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<const node> TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept { + return node_view<const node>{at_path(const_cast<node&>(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 <mark.gillard@outlook.com.au> -//# 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 <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} +#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 <ostream> #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 <typename T> - inline constexpr size_t charconv_buffer_length = 0; +TOML_ANON_NAMESPACE_START { + template <typename T> + inline constexpr size_t charconv_buffer_length = 0; - template <> - inline constexpr size_t charconv_buffer_length<int8_t> = 4; // strlen("-128") + template <> + inline constexpr size_t charconv_buffer_length<int8_t> = 4; // strlen("-128") - template <> - inline constexpr size_t charconv_buffer_length<int16_t> = 6; // strlen("-32768") + template <> + inline constexpr size_t charconv_buffer_length<int16_t> = 6; // strlen("-32768") - template <> - inline constexpr size_t charconv_buffer_length<int32_t> = 11; // strlen("-2147483648") + template <> + inline constexpr size_t charconv_buffer_length<int32_t> = 11; // strlen("-2147483648") - template <> - inline constexpr size_t charconv_buffer_length<int64_t> = 20; // strlen("-9223372036854775808") + template <> + inline constexpr size_t charconv_buffer_length<int64_t> = 20; // strlen("-9223372036854775808") - template <> - inline constexpr size_t charconv_buffer_length<uint8_t> = 3; // strlen("255") + template <> + inline constexpr size_t charconv_buffer_length<uint8_t> = 3; // strlen("255") - template <> - inline constexpr size_t charconv_buffer_length<uint16_t> = 5; // strlen("65535") + template <> + inline constexpr size_t charconv_buffer_length<uint16_t> = 5; // strlen("65535") - template <> - inline constexpr size_t charconv_buffer_length<uint32_t> = 10; // strlen("4294967295") + template <> + inline constexpr size_t charconv_buffer_length<uint32_t> = 10; // strlen("4294967295") - template <> - inline constexpr size_t charconv_buffer_length<uint64_t> = 20; // strlen("18446744073709551615") + template <> + inline constexpr size_t charconv_buffer_length<uint64_t> = 20; // strlen("18446744073709551615") - template <> - inline constexpr size_t charconv_buffer_length<float> = 64; + template <> + inline constexpr size_t charconv_buffer_length<float> = 64; - template <> - inline constexpr size_t charconv_buffer_length<double> = 64; + template <> + inline constexpr size_t charconv_buffer_length<double> = 64; - template <typename T> - 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 <typename T> + 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<size_t>(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<size_t>(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<T>, unsigned>; - using cast_type = std::conditional_t<std::is_signed_v<T>, std::make_signed_t<unsigned_type>, 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<unsigned_type>(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<int>(min_digits)); - ss << static_cast<cast_type>(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<T>, unsigned>; + using cast_type = + std::conditional_t<std::is_signed_v<T>, std::make_signed_t<unsigned_type>, 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<unsigned_type>(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<int>(min_digits)); + ss << static_cast<cast_type>(val); + const auto str = std::move(ss).str(); + impl::print_to_stream(stream, str); + } #endif - } - - template <typename T> - 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 <typename T> + 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<T>]; - 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<size_t>(res.ptr - buf) }; - - char buf2[charconv_buffer_length<T>]; - 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<size_t>(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<T>]; + 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<size_t>(res.ptr - buf)}; + + char buf2[charconv_buffer_length<T>]; + 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<size_t>(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<T>::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<T>::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<std::streamsize>(len)); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, std::string_view val) - { - stream.write(val.data(), static_cast<std::streamsize>(val.length())); - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const std::string& val) - { - stream.write(val.data(), static_cast<std::streamsize>(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<int>(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<unsigned int>(hours), {}, 2); - mins -= hours * 60; - } - else - print_to_stream(stream, "00"sv); - stream.put(':'); - print_to_stream(stream, static_cast<unsigned int>(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<std::streamsize>(len)); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, std::string_view val) { + stream.write(val.data(), static_cast<std::streamsize>(val.length())); + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const std::string& val) { + stream.write(val.data(), static_cast<std::streamsize>(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<int>(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<unsigned int>(hours), {}, 2); + mins -= hours * 60; + } else + print_to_stream(stream, "00"sv); + stream.put(':'); + print_to_stream(stream, static_cast<unsigned int>(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<std::string>& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<int64_t>& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<double>& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<bool>& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<date>& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<time>& val) - { - stream << toml_formatter{ val }; - } - - TOML_EXTERNAL_LINKAGE - void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<date_time>& val) - { - stream << toml_formatter{ val }; - } + 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<std::string>& val) { + stream << toml_formatter{val}; + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<int64_t>& val) { + stream << toml_formatter{val}; + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<double>& val) { + stream << toml_formatter{val}; + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<bool>& val) { + stream << toml_formatter{val}; + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<date>& val) { + stream << toml_formatter{val}; + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<time>& val) { + stream << toml_formatter{val}; + } + + TOML_EXTERNAL_LINKAGE + void TOML_CALLCONV print_to_stream(std::ostream & stream, const value<date_time>& val) { + stream << toml_formatter{val}; + } #endif } diff --git a/vendor/toml++/impl/std_string.inl b/vendor/toml++/impl/std_string.inl index a20d662..9cbf989 100644 --- a/vendor/toml++/impl/std_string.inl +++ b/vendor/toml++/impl/std_string.inl @@ -1,15 +1,15 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #if TOML_WINDOWS #include "std_string.hpp" @@ -18,82 +18,60 @@ #include <Windows.h> #else -extern "C" __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int CodePage, - unsigned long dwFlags, - const wchar_t* lpWideCharStr, - int cchWideChar, - char* lpMultiByteStr, - int cbMultiByte, - const char* lpDefaultChar, - int* lpUsedDefaultChar); +extern "C" __declspec(dllimport) int __stdcall WideCharToMultiByte( + unsigned int CodePage, unsigned long dwFlags, const wchar_t* lpWideCharStr, int cchWideChar, + char* lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, int* lpUsedDefaultChar); -extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, - unsigned long dwFlags, - const char* lpMultiByteStr, - int cbMultiByte, - wchar_t* lpWideCharStr, - int cchWideChar); +extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar( + unsigned int CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, + wchar_t* lpWideCharStr, int cchWideChar); -#endif // TOML_INCLUDE_WINDOWS_H -#endif // _WINDOWS_ +#endif // TOML_INCLUDE_WINDOWS_H +#endif // _WINDOWS_ #include "header_start.hpp" -TOML_IMPL_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - std::string narrow(std::wstring_view str) - { - if (str.empty()) - return {}; +TOML_IMPL_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + std::string narrow(std::wstring_view str) { + if (str.empty()) return {}; - std::string s; - const auto len = - ::WideCharToMultiByte(65001, 0, str.data(), static_cast<int>(str.length()), nullptr, 0, nullptr, nullptr); - if (len) - { - s.resize(static_cast<size_t>(len)); - ::WideCharToMultiByte(65001, - 0, - str.data(), - static_cast<int>(str.length()), - s.data(), - len, - nullptr, - nullptr); - } - return s; - } + std::string s; + const auto len = ::WideCharToMultiByte(65001, 0, str.data(), static_cast<int>(str.length()), + nullptr, 0, nullptr, nullptr); + if (len) { + s.resize(static_cast<size_t>(len)); + ::WideCharToMultiByte(65001, 0, str.data(), static_cast<int>(str.length()), s.data(), len, + nullptr, nullptr); + } + return s; + } - TOML_EXTERNAL_LINKAGE - std::wstring widen(std::string_view str) - { - if (str.empty()) - return {}; + TOML_EXTERNAL_LINKAGE + std::wstring widen(std::string_view str) { + if (str.empty()) return {}; - std::wstring s; - const auto len = ::MultiByteToWideChar(65001, 0, str.data(), static_cast<int>(str.length()), nullptr, 0); - if (len) - { - s.resize(static_cast<size_t>(len)); - ::MultiByteToWideChar(65001, 0, str.data(), static_cast<int>(str.length()), s.data(), len); - } - return s; - } + std::wstring s; + const auto len = + ::MultiByteToWideChar(65001, 0, str.data(), static_cast<int>(str.length()), nullptr, 0); + if (len) { + s.resize(static_cast<size_t>(len)); + ::MultiByteToWideChar(65001, 0, str.data(), static_cast<int>(str.length()), s.data(), len); + } + return s; + } #if TOML_HAS_CHAR8 - TOML_EXTERNAL_LINKAGE - std::wstring widen(std::u8string_view str) - { - if (str.empty()) - return {}; + TOML_EXTERNAL_LINKAGE + std::wstring widen(std::u8string_view str) { + if (str.empty()) return {}; - return widen(std::string_view{ reinterpret_cast<const char*>(str.data()), str.length() }); - } + return widen(std::string_view{reinterpret_cast<const char*>(str.data()), str.length()}); + } -#endif // TOML_HAS_CHAR8 +#endif // TOML_HAS_CHAR8 } TOML_IMPL_NAMESPACE_END; #include "header_end.hpp" -#endif // TOML_WINDOWS +#endif // TOML_WINDOWS diff --git a/vendor/toml++/impl/table.inl b/vendor/toml++/impl/table.inl index 01cba01..ca7473e 100644 --- a/vendor/toml++/impl/table.inl +++ b/vendor/toml++/impl/table.inl @@ -1,317 +1,259 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} -#include "table.hpp" -#include "node_view.hpp" #include "header_start.hpp" +#include "node_view.hpp" +#include "table.hpp" -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - table::table() noexcept - { +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + table::table() noexcept { #if TOML_LIFETIME_HOOKS - TOML_TABLE_CREATED; + TOML_TABLE_CREATED; #endif - } + } - TOML_EXTERNAL_LINKAGE - table::~table() noexcept - { + TOML_EXTERNAL_LINKAGE + table::~table() noexcept { #if TOML_LIFETIME_HOOKS - TOML_TABLE_DESTROYED; + TOML_TABLE_DESTROYED; #endif - } + } - TOML_EXTERNAL_LINKAGE - table::table(const impl::table_init_pair* b, const impl::table_init_pair* e) - { + TOML_EXTERNAL_LINKAGE + table::table(const impl::table_init_pair* b, const impl::table_init_pair* e) { #if TOML_LIFETIME_HOOKS - TOML_TABLE_CREATED; + TOML_TABLE_CREATED; #endif - TOML_ASSERT_ASSUME(b); - TOML_ASSERT_ASSUME(e); - TOML_ASSERT_ASSUME(b <= e); + TOML_ASSERT_ASSUME(b); + TOML_ASSERT_ASSUME(e); + TOML_ASSERT_ASSUME(b <= e); - if TOML_UNLIKELY(b == e) - return; + if TOML_UNLIKELY (b == e) return; - for (; b != e; b++) - { - if (!b->value) // empty node_views - continue; + for (; b != e; b++) { + if (!b->value) // empty node_views + continue; - map_.insert_or_assign(std::move(b->key), std::move(b->value)); - } - } + map_.insert_or_assign(std::move(b->key), std::move(b->value)); + } + } - TOML_EXTERNAL_LINKAGE - table::table(const table& other) // - : node(other), - inline_{ other.inline_ } - { - for (auto&& [k, v] : other.map_) - map_.emplace_hint(map_.end(), k, impl::make_node(*v)); + TOML_EXTERNAL_LINKAGE + table::table(const table& other) // + : node(other), inline_{other.inline_} { + for (auto&& [k, v] : other.map_) map_.emplace_hint(map_.end(), k, impl::make_node(*v)); #if TOML_LIFETIME_HOOKS - TOML_TABLE_CREATED; + TOML_TABLE_CREATED; #endif - } - - TOML_EXTERNAL_LINKAGE - table::table(table && other) noexcept // - : node(std::move(other)), - map_{ std::move(other.map_) }, - inline_{ other.inline_ } - { + } + + TOML_EXTERNAL_LINKAGE + table::table(table && other) noexcept // + : node(std::move(other)), map_{std::move(other.map_)}, inline_{other.inline_} { #if TOML_LIFETIME_HOOKS - TOML_TABLE_CREATED; + TOML_TABLE_CREATED; #endif - } - - TOML_EXTERNAL_LINKAGE - table& table::operator=(const table& rhs) - { - if (&rhs != this) - { - node::operator=(rhs); - map_.clear(); - for (auto&& [k, v] : rhs.map_) - map_.emplace_hint(map_.end(), k, impl::make_node(*v)); - inline_ = rhs.inline_; - } - return *this; - } - - TOML_EXTERNAL_LINKAGE - table& table::operator=(table&& rhs) noexcept - { - if (&rhs != this) - { - node::operator=(std::move(rhs)); - map_ = std::move(rhs.map_); - inline_ = rhs.inline_; - } - return *this; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool table::is_homogeneous(node_type ntype) const noexcept - { - if (map_.empty()) - return false; - - if (ntype == node_type::none) - ntype = map_.cbegin()->second->type(); - - for (auto&& [k, v] : map_) - { - TOML_UNUSED(k); - if (v->type() != ntype) - return false; - } - - return true; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept - { - if (map_.empty()) - { - first_nonmatch = {}; - return false; - } - if (ntype == node_type::none) - ntype = map_.cbegin()->second->type(); - for (const auto& [k, v] : map_) - { - TOML_UNUSED(k); - if (v->type() != ntype) - { - first_nonmatch = v.get(); - return false; - } - } - return true; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept - { - node* fnm = nullptr; - const auto result = const_cast<table&>(*this).is_homogeneous(ntype, fnm); - first_nonmatch = fnm; - return result; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - node* table::get(std::string_view key) noexcept - { - if (auto it = map_.find(key); it != map_.end()) - return it->second.get(); - return nullptr; - } - - TOML_EXTERNAL_LINKAGE - node& table::at(std::string_view key) - { - auto n = get(key); + } + + TOML_EXTERNAL_LINKAGE + table& table::operator=(const table& rhs) { + if (&rhs != this) { + node::operator=(rhs); + map_.clear(); + for (auto&& [k, v] : rhs.map_) map_.emplace_hint(map_.end(), k, impl::make_node(*v)); + inline_ = rhs.inline_; + } + return *this; + } + + TOML_EXTERNAL_LINKAGE + table& table::operator=(table&& rhs) noexcept { + if (&rhs != this) { + node::operator=(std::move(rhs)); + map_ = std::move(rhs.map_); + inline_ = rhs.inline_; + } + return *this; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool table::is_homogeneous(node_type ntype) const noexcept { + if (map_.empty()) return false; + + if (ntype == node_type::none) ntype = map_.cbegin()->second->type(); + + for (auto&& [k, v] : map_) { + TOML_UNUSED(k); + if (v->type() != ntype) return false; + } + + return true; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept { + if (map_.empty()) { + first_nonmatch = {}; + return false; + } + if (ntype == node_type::none) ntype = map_.cbegin()->second->type(); + for (const auto& [k, v] : map_) { + TOML_UNUSED(k); + if (v->type() != ntype) { + first_nonmatch = v.get(); + return false; + } + } + return true; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept { + node* fnm = nullptr; + const auto result = const_cast<table&>(*this).is_homogeneous(ntype, fnm); + first_nonmatch = fnm; + return result; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + node* table::get(std::string_view key) noexcept { + if (auto it = map_.find(key); it != map_.end()) return it->second.get(); + return nullptr; + } + + TOML_EXTERNAL_LINKAGE + node& table::at(std::string_view key) { + auto n = get(key); #if TOML_COMPILER_HAS_EXCEPTIONS - if (!n) - { - auto err = "key '"s; - err.append(key); - err.append("' not found in table"sv); - throw std::out_of_range{ err }; - } + if (!n) { + auto err = "key '"s; + err.append(key); + err.append("' not found in table"sv); + throw std::out_of_range{err}; + } #else - TOML_ASSERT_ASSUME(n && "key not found in table!"); + TOML_ASSERT_ASSUME(n && "key not found in table!"); #endif - return *n; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - table::map_iterator table::get_lower_bound(std::string_view key) noexcept - { - return map_.lower_bound(key); - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - table::iterator table::find(std::string_view key) noexcept - { - return iterator{ map_.find(key) }; - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - table::const_iterator table::find(std::string_view key) const noexcept - { - return const_iterator{ map_.find(key) }; - } - - TOML_EXTERNAL_LINKAGE - table::map_iterator table::erase(const_map_iterator pos) noexcept - { - return map_.erase(pos); - } - - TOML_EXTERNAL_LINKAGE - table::map_iterator table::erase(const_map_iterator begin, const_map_iterator end) noexcept - { - return map_.erase(begin, end); - } - - TOML_EXTERNAL_LINKAGE - size_t table::erase(std::string_view key) noexcept - { - if (auto it = map_.find(key); it != map_.end()) - { - map_.erase(it); - return size_t{ 1 }; - } - return size_t{}; - } - - TOML_EXTERNAL_LINKAGE - table& table::prune(bool recursive)& noexcept - { - if (map_.empty()) - return *this; - - for (auto it = map_.begin(); it != map_.end();) - { - if (auto arr = it->second->as_array()) - { - if (recursive) - arr->prune(true); - - if (arr->empty()) - { - it = map_.erase(it); - continue; - } - } - else if (auto tbl = it->second->as_table()) - { - if (recursive) - tbl->prune(true); - - if (tbl->empty()) - { - it = map_.erase(it); - continue; - } - } - it++; - } - - return *this; - } - - TOML_EXTERNAL_LINKAGE - void table::clear() noexcept - { - map_.clear(); - } - - TOML_EXTERNAL_LINKAGE - table::map_iterator table::insert_with_hint(const_iterator hint, key && k, impl::node_ptr && v) - { - return map_.emplace_hint(const_map_iterator{ hint }, std::move(k), std::move(v)); - } - - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool TOML_CALLCONV table::equal(const table& lhs, const table& rhs) noexcept - { - if (&lhs == &rhs) - return true; - if (lhs.map_.size() != rhs.map_.size()) - return false; - - for (auto l = lhs.map_.begin(), r = rhs.map_.begin(), e = lhs.map_.end(); l != e; l++, r++) - { - if (l->first != r->first) - return false; - - const auto lhs_type = l->second->type(); - const node& rhs_ = *r->second; - const auto rhs_type = rhs_.type(); - if (lhs_type != rhs_type) - return false; - - const bool equal = l->second->visit( - [&](const auto& lhs_) noexcept - { return lhs_ == *reinterpret_cast<std::remove_reference_t<decltype(lhs_)>*>(&rhs_); }); - if (!equal) - return false; - } - return true; - } + return *n; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + table::map_iterator table::get_lower_bound(std::string_view key) noexcept { + return map_.lower_bound(key); + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + table::iterator table::find(std::string_view key) noexcept { + return iterator{map_.find(key)}; + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + table::const_iterator table::find(std::string_view key) const noexcept { + return const_iterator{map_.find(key)}; + } + + TOML_EXTERNAL_LINKAGE + table::map_iterator table::erase(const_map_iterator pos) noexcept { + return map_.erase(pos); + } + + TOML_EXTERNAL_LINKAGE + table::map_iterator table::erase(const_map_iterator begin, const_map_iterator end) noexcept { + return map_.erase(begin, end); + } + + TOML_EXTERNAL_LINKAGE + size_t table::erase(std::string_view key) noexcept { + if (auto it = map_.find(key); it != map_.end()) { + map_.erase(it); + return size_t{1}; + } + return size_t{}; + } + + TOML_EXTERNAL_LINKAGE + table& table::prune(bool recursive)& noexcept { + if (map_.empty()) return *this; + + for (auto it = map_.begin(); it != map_.end();) { + if (auto arr = it->second->as_array()) { + if (recursive) arr->prune(true); + + if (arr->empty()) { + it = map_.erase(it); + continue; + } + } else if (auto tbl = it->second->as_table()) { + if (recursive) tbl->prune(true); + + if (tbl->empty()) { + it = map_.erase(it); + continue; + } + } + it++; + } + + return *this; + } + + TOML_EXTERNAL_LINKAGE + void table::clear() noexcept { + map_.clear(); + } + + TOML_EXTERNAL_LINKAGE + table::map_iterator table::insert_with_hint(const_iterator hint, key && k, impl::node_ptr && v) { + return map_.emplace_hint(const_map_iterator{hint}, std::move(k), std::move(v)); + } + + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool TOML_CALLCONV table::equal(const table& lhs, const table& rhs) noexcept { + if (&lhs == &rhs) return true; + if (lhs.map_.size() != rhs.map_.size()) return false; + + for (auto l = lhs.map_.begin(), r = rhs.map_.begin(), e = lhs.map_.end(); l != e; l++, r++) { + if (l->first != r->first) return false; + + const auto lhs_type = l->second->type(); + const node& rhs_ = *r->second; + const auto rhs_type = rhs_.type(); + if (lhs_type != rhs_type) return false; + + const bool equal = l->second->visit([&](const auto& lhs_) noexcept { + return lhs_ == *reinterpret_cast<std::remove_reference_t<decltype(lhs_)>*>(&rhs_); + }); + if (!equal) return false; + } + return true; + } } TOML_NAMESPACE_END; diff --git a/vendor/toml++/impl/toml_formatter.inl b/vendor/toml++/impl/toml_formatter.inl index e764448..02c6d0b 100644 --- a/vendor/toml++/impl/toml_formatter.inl +++ b/vendor/toml++/impl/toml_formatter.inl @@ -1,405 +1,366 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once #include "preprocessor.hpp" -//# {{ +// # {{ #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #if TOML_ENABLE_FORMATTERS -#include "toml_formatter.hpp" +#include "array.hpp" +#include "header_start.hpp" #include "print_to_stream.hpp" -#include "value.hpp" #include "table.hpp" -#include "array.hpp" +#include "toml_formatter.hpp" #include "unicode.hpp" -#include "header_start.hpp" +#include "value.hpp" TOML_DISABLE_ARITHMETIC_WARNINGS; -TOML_ANON_NAMESPACE_START -{ - TOML_INTERNAL_LINKAGE - size_t toml_formatter_count_inline_columns(const node& node, size_t line_wrap_cols) noexcept - { - switch (node.type()) - { - case node_type::table: - { - auto& tbl = *reinterpret_cast<const table*>(&node); - if (tbl.empty()) - return 2u; // "{}" - size_t weight = 3u; // "{ }" - for (auto&& [k, v] : tbl) - { - weight += k.length() + toml_formatter_count_inline_columns(v, line_wrap_cols) + 2u; // + ", " - if (weight >= line_wrap_cols) - break; - } - return weight; - } - - case node_type::array: - { - auto& arr = *reinterpret_cast<const array*>(&node); - if (arr.empty()) - return 2u; // "[]" - size_t weight = 3u; // "[ ]" - for (auto& elem : arr) - { - weight += toml_formatter_count_inline_columns(elem, line_wrap_cols) + 2u; // + ", " - if (weight >= line_wrap_cols) - break; - } - return weight; - } - - case node_type::string: - { - // todo: proper utf8 decoding? - // todo: tab awareness? - auto& str = (*reinterpret_cast<const value<std::string>*>(&node)).get(); - return str.length() + 2u; // + "" - } - - case node_type::integer: - { - auto val = (*reinterpret_cast<const value<int64_t>*>(&node)).get(); - if (!val) - return 1u; - size_t weight = {}; - if (val < 0) - { - weight += 1u; - val *= -1; - } - return weight + static_cast<size_t>(log10(static_cast<double>(val))) + 1u; - } - - case node_type::floating_point: - { - auto val = (*reinterpret_cast<const value<double>*>(&node)).get(); - if (val == 0.0) - return 3u; // "0.0" - size_t weight = 2u; // ".0" - if (val < 0.0) - { - weight += 1u; - val *= -1.0; - } - return weight + static_cast<size_t>(log10(val)) + 1u; - break; - } - - case node_type::boolean: return 5u; - case node_type::date: [[fallthrough]]; - case node_type::time: return 10u; - case node_type::date_time: return 30u; - case node_type::none: TOML_UNREACHABLE; - default: TOML_UNREACHABLE; - } - - TOML_UNREACHABLE; - } - - TOML_INTERNAL_LINKAGE - bool toml_formatter_forces_multiline(const node& node, size_t line_wrap_cols, size_t starting_column_bias) noexcept - { - return (toml_formatter_count_inline_columns(node, line_wrap_cols) + starting_column_bias) >= line_wrap_cols; - } +TOML_ANON_NAMESPACE_START { + TOML_INTERNAL_LINKAGE + size_t toml_formatter_count_inline_columns(const node& node, size_t line_wrap_cols) noexcept { + switch (node.type()) { + case node_type::table: { + auto& tbl = *reinterpret_cast<const table*>(&node); + if (tbl.empty()) return 2u; // "{}" + size_t weight = 3u; // "{ }" + for (auto&& [k, v] : tbl) { + weight += + k.length() + toml_formatter_count_inline_columns(v, line_wrap_cols) + 2u; // + ", " + if (weight >= line_wrap_cols) break; + } + return weight; + } + + case node_type::array: { + auto& arr = *reinterpret_cast<const array*>(&node); + if (arr.empty()) return 2u; // "[]" + size_t weight = 3u; // "[ ]" + for (auto& elem : arr) { + weight += toml_formatter_count_inline_columns(elem, line_wrap_cols) + 2u; // + ", " + if (weight >= line_wrap_cols) break; + } + return weight; + } + + case node_type::string: { + // todo: proper utf8 decoding? + // todo: tab awareness? + auto& str = (*reinterpret_cast<const value<std::string>*>(&node)).get(); + return str.length() + 2u; // + "" + } + + case node_type::integer: { + auto val = (*reinterpret_cast<const value<int64_t>*>(&node)).get(); + if (!val) return 1u; + size_t weight = {}; + if (val < 0) { + weight += 1u; + val *= -1; + } + return weight + static_cast<size_t>(log10(static_cast<double>(val))) + 1u; + } + + case node_type::floating_point: { + auto val = (*reinterpret_cast<const value<double>*>(&node)).get(); + if (val == 0.0) return 3u; // "0.0" + size_t weight = 2u; // ".0" + if (val < 0.0) { + weight += 1u; + val *= -1.0; + } + return weight + static_cast<size_t>(log10(val)) + 1u; + break; + } + + case node_type::boolean: + return 5u; + case node_type::date: + [[fallthrough]]; + case node_type::time: + return 10u; + case node_type::date_time: + return 30u; + case node_type::none: + TOML_UNREACHABLE; + default: + TOML_UNREACHABLE; + } + + TOML_UNREACHABLE; + } + + TOML_INTERNAL_LINKAGE + bool toml_formatter_forces_multiline(const node& node, size_t line_wrap_cols, + size_t starting_column_bias) noexcept { + return (toml_formatter_count_inline_columns(node, line_wrap_cols) + starting_column_bias) >= + line_wrap_cols; + } } TOML_ANON_NAMESPACE_END; -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - void toml_formatter::print_pending_table_separator() - { - if (pending_table_separator_) - { - print_newline(true); - print_newline(true); - pending_table_separator_ = false; - } - } - - TOML_EXTERNAL_LINKAGE - void toml_formatter::print(const key& k) - { - print_string(k.str(), false, true, false); - } - - TOML_EXTERNAL_LINKAGE - void toml_formatter::print_inline(const table& tbl) - { - if (tbl.empty()) - { - print_unformatted("{}"sv); - return; - } - - print_unformatted("{ "sv); - - bool first = false; - for (auto&& [k, v] : tbl) - { - if (first) - print_unformatted(", "sv); - first = true; - - print(k); - if (terse_kvps()) - print_unformatted("="sv); - else - print_unformatted(" = "sv); - - const auto type = v.type(); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v)); break; - default: print_value(v, type); - } - } - - print_unformatted(" }"sv); - } - - TOML_EXTERNAL_LINKAGE - void toml_formatter::print(const array& arr) - { - if (arr.empty()) - { - print_unformatted("[]"sv); - return; - } - - const auto original_indent = indent(); - const auto multiline = TOML_ANON_NAMESPACE::toml_formatter_forces_multiline( - arr, - 120u, - indent_columns() * static_cast<size_t>(original_indent < 0 ? 0 : original_indent)); - - print_unformatted("["sv); - - if (multiline) - { - if (original_indent < 0) - indent(0); - if (indent_array_elements()) - increase_indent(); - } - else - print_unformatted(' '); - - for (size_t i = 0; i < arr.size(); i++) - { - if (i > 0u) - { - print_unformatted(','); - if (!multiline) - print_unformatted(' '); - } - - if (multiline) - { - print_newline(true); - print_indent(); - } - - auto& v = arr[i]; - const auto type = v.type(); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v)); break; - default: print_value(v, type); - } - } - if (multiline) - { - indent(original_indent); - print_newline(true); - print_indent(); - } - else - print_unformatted(' '); - - print_unformatted("]"sv); - } - - TOML_EXTERNAL_LINKAGE - void toml_formatter::print(const table& tbl) - { - static constexpr auto is_non_inline_array_of_tables = [](const node& n) noexcept - { - const auto arr = n.as_array(); - if (!arr || !arr->is_array_of_tables()) - return false; - - return !reinterpret_cast<const table*>(&(*arr)[0])->is_inline(); - }; - - // values, arrays, and inline tables/table arrays - for (auto&& [k, v] : tbl) - { - const auto type = v.type(); - if ((type == node_type::table && !reinterpret_cast<const table*>(&v)->is_inline()) - || (type == node_type::array && is_non_inline_array_of_tables(v))) - continue; - - pending_table_separator_ = true; - print_newline(); - print_indent(); - print(k); - if (terse_kvps()) - print_unformatted("="sv); - else - print_unformatted(" = "sv); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print_inline(*reinterpret_cast<const table*>(&v)); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v)); break; - default: print_value(v, type); - } - } - - const auto print_key_path = [&]() - { - size_t i{}; - for (const auto k : key_path_) - { - if (i++) - print_unformatted('.'); - print(*k); - } - }; - - // non-inline tables - for (auto&& [k, v] : tbl) - { - const auto type = v.type(); - if (type != node_type::table || reinterpret_cast<const table*>(&v)->is_inline()) - continue; - auto& child_tbl = *reinterpret_cast<const table*>(&v); - - // we can skip indenting and emitting the headers for tables that only contain other tables - // (so we don't over-nest) - size_t child_value_count{}; // includes inline tables and non-table arrays - size_t child_table_count{}; - size_t child_table_array_count{}; - for (auto&& [child_k, child_v] : child_tbl) - { - TOML_UNUSED(child_k); - const auto child_type = child_v.type(); - TOML_ASSUME(child_type != node_type::none); - switch (child_type) - { - case node_type::table: - if (reinterpret_cast<const table*>(&child_v)->is_inline()) - child_value_count++; - else - child_table_count++; - break; - - case node_type::array: - if (is_non_inline_array_of_tables(child_v)) - child_table_array_count++; - else - child_value_count++; - break; - - default: child_value_count++; - } - } - bool skip_self = false; - if (child_value_count == 0u && (child_table_count > 0u || child_table_array_count > 0u)) - skip_self = true; - - key_path_.push_back(&k); - - if (!skip_self) - { - print_pending_table_separator(); - if (indent_sub_tables()) - increase_indent(); - print_indent(); - print_unformatted("["sv); - print_key_path(); - print_unformatted("]"sv); - pending_table_separator_ = true; - } - - print(child_tbl); - - key_path_.pop_back(); - if (!skip_self && indent_sub_tables()) - decrease_indent(); - } - - // table arrays - for (auto&& [k, v] : tbl) - { - if (!is_non_inline_array_of_tables(v)) - continue; - auto& arr = *reinterpret_cast<const array*>(&v); - - if (indent_sub_tables()) - increase_indent(); - key_path_.push_back(&k); - - for (size_t i = 0; i < arr.size(); i++) - { - print_pending_table_separator(); - print_indent(); - print_unformatted("[["sv); - print_key_path(); - print_unformatted("]]"sv); - pending_table_separator_ = true; - print(*reinterpret_cast<const table*>(&arr[i])); - } - - key_path_.pop_back(); - if (indent_sub_tables()) - decrease_indent(); - } - } - - TOML_EXTERNAL_LINKAGE - void toml_formatter::print() - { - if (dump_failed_parse_result()) - return; - - switch (auto source_type = source().type()) - { - case node_type::table: - { - auto& tbl = *reinterpret_cast<const table*>(&source()); - if (tbl.is_inline()) - print_inline(tbl); - else - { - decrease_indent(); // so root kvps and tables have the same indent - print(tbl); - } - break; - } - - case node_type::array: print(*reinterpret_cast<const array*>(&source())); break; - - default: print_value(source(), source_type); - } - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + void toml_formatter::print_pending_table_separator() { + if (pending_table_separator_) { + print_newline(true); + print_newline(true); + pending_table_separator_ = false; + } + } + + TOML_EXTERNAL_LINKAGE + void toml_formatter::print(const key& k) { + print_string(k.str(), false, true, false); + } + + TOML_EXTERNAL_LINKAGE + void toml_formatter::print_inline(const table& tbl) { + if (tbl.empty()) { + print_unformatted("{}"sv); + return; + } + + print_unformatted("{ "sv); + + bool first = false; + for (auto&& [k, v] : tbl) { + if (first) print_unformatted(", "sv); + first = true; + + print(k); + if (terse_kvps()) + print_unformatted("="sv); + else + print_unformatted(" = "sv); + + const auto type = v.type(); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print_inline(*reinterpret_cast<const table*>(&v)); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v)); + break; + default: + print_value(v, type); + } + } + + print_unformatted(" }"sv); + } + + TOML_EXTERNAL_LINKAGE + void toml_formatter::print(const array& arr) { + if (arr.empty()) { + print_unformatted("[]"sv); + return; + } + + const auto original_indent = indent(); + const auto multiline = TOML_ANON_NAMESPACE::toml_formatter_forces_multiline( + arr, 120u, + indent_columns() * static_cast<size_t>(original_indent < 0 ? 0 : original_indent)); + + print_unformatted("["sv); + + if (multiline) { + if (original_indent < 0) indent(0); + if (indent_array_elements()) increase_indent(); + } else + print_unformatted(' '); + + for (size_t i = 0; i < arr.size(); i++) { + if (i > 0u) { + print_unformatted(','); + if (!multiline) print_unformatted(' '); + } + + if (multiline) { + print_newline(true); + print_indent(); + } + + auto& v = arr[i]; + const auto type = v.type(); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print_inline(*reinterpret_cast<const table*>(&v)); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v)); + break; + default: + print_value(v, type); + } + } + if (multiline) { + indent(original_indent); + print_newline(true); + print_indent(); + } else + print_unformatted(' '); + + print_unformatted("]"sv); + } + + TOML_EXTERNAL_LINKAGE + void toml_formatter::print(const table& tbl) { + static constexpr auto is_non_inline_array_of_tables = [](const node& n) noexcept { + const auto arr = n.as_array(); + if (!arr || !arr->is_array_of_tables()) return false; + + return !reinterpret_cast<const table*>(&(*arr)[0])->is_inline(); + }; + + // values, arrays, and inline tables/table arrays + for (auto&& [k, v] : tbl) { + const auto type = v.type(); + if ((type == node_type::table && !reinterpret_cast<const table*>(&v)->is_inline()) || + (type == node_type::array && is_non_inline_array_of_tables(v))) + continue; + + pending_table_separator_ = true; + print_newline(); + print_indent(); + print(k); + if (terse_kvps()) + print_unformatted("="sv); + else + print_unformatted(" = "sv); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print_inline(*reinterpret_cast<const table*>(&v)); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v)); + break; + default: + print_value(v, type); + } + } + + const auto print_key_path = [&]() { + size_t i{}; + for (const auto k : key_path_) { + if (i++) print_unformatted('.'); + print(*k); + } + }; + + // non-inline tables + for (auto&& [k, v] : tbl) { + const auto type = v.type(); + if (type != node_type::table || reinterpret_cast<const table*>(&v)->is_inline()) continue; + auto& child_tbl = *reinterpret_cast<const table*>(&v); + + // we can skip indenting and emitting the headers for tables that only contain other tables + // (so we don't over-nest) + size_t child_value_count{}; // includes inline tables and non-table arrays + size_t child_table_count{}; + size_t child_table_array_count{}; + for (auto&& [child_k, child_v] : child_tbl) { + TOML_UNUSED(child_k); + const auto child_type = child_v.type(); + TOML_ASSUME(child_type != node_type::none); + switch (child_type) { + case node_type::table: + if (reinterpret_cast<const table*>(&child_v)->is_inline()) + child_value_count++; + else + child_table_count++; + break; + + case node_type::array: + if (is_non_inline_array_of_tables(child_v)) + child_table_array_count++; + else + child_value_count++; + break; + + default: + child_value_count++; + } + } + bool skip_self = false; + if (child_value_count == 0u && (child_table_count > 0u || child_table_array_count > 0u)) + skip_self = true; + + key_path_.push_back(&k); + + if (!skip_self) { + print_pending_table_separator(); + if (indent_sub_tables()) increase_indent(); + print_indent(); + print_unformatted("["sv); + print_key_path(); + print_unformatted("]"sv); + pending_table_separator_ = true; + } + + print(child_tbl); + + key_path_.pop_back(); + if (!skip_self && indent_sub_tables()) decrease_indent(); + } + + // table arrays + for (auto&& [k, v] : tbl) { + if (!is_non_inline_array_of_tables(v)) continue; + auto& arr = *reinterpret_cast<const array*>(&v); + + if (indent_sub_tables()) increase_indent(); + key_path_.push_back(&k); + + for (size_t i = 0; i < arr.size(); i++) { + print_pending_table_separator(); + print_indent(); + print_unformatted("[["sv); + print_key_path(); + print_unformatted("]]"sv); + pending_table_separator_ = true; + print(*reinterpret_cast<const table*>(&arr[i])); + } + + key_path_.pop_back(); + if (indent_sub_tables()) decrease_indent(); + } + } + + TOML_EXTERNAL_LINKAGE + void toml_formatter::print() { + if (dump_failed_parse_result()) return; + + switch (auto source_type = source().type()) { + case node_type::table: { + auto& tbl = *reinterpret_cast<const table*>(&source()); + if (tbl.is_inline()) + print_inline(tbl); + else { + decrease_indent(); // so root kvps and tables have the same indent + print(tbl); + } + break; + } + + case node_type::array: + print(*reinterpret_cast<const array*>(&source())); + break; + + default: + print_value(source(), source_type); + } + } } TOML_NAMESPACE_END; #include "header_end.hpp" -#endif // TOML_ENABLE_FORMATTERS +#endif // TOML_ENABLE_FORMATTERS diff --git a/vendor/toml++/impl/unicode.inl b/vendor/toml++/impl/unicode.inl index 4064c58..93a7d52 100644 --- a/vendor/toml++/impl/unicode.inl +++ b/vendor/toml++/impl/unicode.inl @@ -1,59 +1,53 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once -//# {{ +// # {{ #include "preprocessor.hpp" #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} -#include "unicode.hpp" -#include "simd.hpp" #include "header_start.hpp" +#include "simd.hpp" +#include "unicode.hpp" -TOML_IMPL_NAMESPACE_START -{ - TOML_PURE_GETTER - TOML_EXTERNAL_LINKAGE - bool is_ascii(const char* str, size_t len) noexcept - { - const char* const end = str + len; +TOML_IMPL_NAMESPACE_START { + TOML_PURE_GETTER + TOML_EXTERNAL_LINKAGE + bool is_ascii(const char* str, size_t len) noexcept { + const char* const end = str + len; #if TOML_HAS_SSE2 && (128 % CHAR_BIT) == 0 - { - constexpr size_t chars_per_vector = 128u / CHAR_BIT; - - if (const size_t simdable = len - (len % chars_per_vector)) - { - __m128i mask = _mm_setzero_si128(); - for (const char* const e = str + simdable; str < e; str += chars_per_vector) - { - const __m128i current_bytes = _mm_loadu_si128(reinterpret_cast<const __m128i*>(str)); - mask = _mm_or_si128(mask, current_bytes); - } - const __m128i has_error = _mm_cmpgt_epi8(_mm_setzero_si128(), mask); + { + constexpr size_t chars_per_vector = 128u / CHAR_BIT; + + if (const size_t simdable = len - (len % chars_per_vector)) { + __m128i mask = _mm_setzero_si128(); + for (const char* const e = str + simdable; str < e; str += chars_per_vector) { + const __m128i current_bytes = _mm_loadu_si128(reinterpret_cast<const __m128i*>(str)); + mask = _mm_or_si128(mask, current_bytes); + } + const __m128i has_error = _mm_cmpgt_epi8(_mm_setzero_si128(), mask); #if TOML_HAS_SSE4_1 - if (!_mm_testz_si128(has_error, has_error)) - return false; + if (!_mm_testz_si128(has_error, has_error)) return false; #else - if (_mm_movemask_epi8(_mm_cmpeq_epi8(has_error, _mm_setzero_si128())) != 0xFFFF) - return false; + if (_mm_movemask_epi8(_mm_cmpeq_epi8(has_error, _mm_setzero_si128())) != 0xFFFF) + return false; #endif - } - } + } + } #endif - for (; str < end; str++) - if (static_cast<unsigned char>(*str) > 127u) - return false; + for (; str < end; str++) + if (static_cast<unsigned char>(*str) > 127u) return false; - return true; - } + return true; + } } TOML_IMPL_NAMESPACE_END; diff --git a/vendor/toml++/impl/yaml_formatter.inl b/vendor/toml++/impl/yaml_formatter.inl index 95cb08d..7bf4c5f 100644 --- a/vendor/toml++/impl/yaml_formatter.inl +++ b/vendor/toml++/impl/yaml_formatter.inl @@ -1,165 +1,164 @@ -//# This file is a part of toml++ and is subject to the the terms of the MIT license. -//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> -//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. -// SPDX-License-Identifier: MIT +// # This file is a part of toml++ and is subject to the the terms of the MIT license. +// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT #pragma once #include "preprocessor.hpp" -//# {{ +// # {{ #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif -//# }} +// # }} #if TOML_ENABLE_FORMATTERS -#include "yaml_formatter.hpp" -#include "print_to_stream.hpp" -#include "table.hpp" #include "array.hpp" #include "header_start.hpp" +#include "print_to_stream.hpp" +#include "table.hpp" +#include "yaml_formatter.hpp" -TOML_NAMESPACE_START -{ - TOML_EXTERNAL_LINKAGE - void yaml_formatter::print_yaml_string(const value<std::string>& str) - { - if (str->empty()) - { - base::print(str); - return; - } - - bool contains_newline = false; - for (auto c = str->c_str(), e = str->c_str() + str->length(); c < e && !contains_newline; c++) - contains_newline = *c == '\n'; - - if (contains_newline) - { - print_unformatted("|-"sv); - - increase_indent(); - - auto line_end = str->c_str() - 1u; - const auto end = str->c_str() + str->length(); - while (line_end != end) - { - auto line_start = line_end + 1u; - line_end = line_start; - for (; line_end != end && *line_end != '\n'; line_end++) - ; - - if TOML_LIKELY(line_start != line_end || line_end != end) - { - print_newline(); - print_indent(); - print_unformatted(std::string_view{ line_start, static_cast<size_t>(line_end - line_start) }); - } - } - - decrease_indent(); - } - else - print_string(*str, false, true); - } - - TOML_EXTERNAL_LINKAGE - void yaml_formatter::print(const toml::table& tbl, bool parent_is_array) - { - if (tbl.empty()) - { - print_unformatted("{}"sv); - return; - } - - increase_indent(); - - for (auto&& [k, v] : tbl) - { - if (!parent_is_array) - { - print_newline(); - print_indent(); - } - parent_is_array = false; - - print_string(k.str(), false, true); - if (terse_kvps()) - print_unformatted(":"sv); - else - print_unformatted(": "sv); - - const auto type = v.type(); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print(*reinterpret_cast<const table*>(&v)); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v)); break; - case node_type::string: print_yaml_string(*reinterpret_cast<const value<std::string>*>(&v)); break; - default: print_value(v, type); - } - } - - decrease_indent(); - } - - TOML_EXTERNAL_LINKAGE - void yaml_formatter::print(const toml::array& arr, bool parent_is_array) - { - if (arr.empty()) - { - print_unformatted("[]"sv); - return; - } - - increase_indent(); - - for (auto&& v : arr) - { - if (!parent_is_array) - { - print_newline(); - print_indent(); - } - parent_is_array = false; - - print_unformatted("- "sv); - - const auto type = v.type(); - TOML_ASSUME(type != node_type::none); - switch (type) - { - case node_type::table: print(*reinterpret_cast<const table*>(&v), true); break; - case node_type::array: print(*reinterpret_cast<const array*>(&v), true); break; - case node_type::string: print_yaml_string(*reinterpret_cast<const value<std::string>*>(&v)); break; - default: print_value(v, type); - } - } - - decrease_indent(); - } - - TOML_EXTERNAL_LINKAGE - void yaml_formatter::print() - { - if (dump_failed_parse_result()) - return; - - switch (auto source_type = source().type()) - { - case node_type::table: - decrease_indent(); // so root kvps and tables have the same indent - print(*reinterpret_cast<const table*>(&source())); - break; - - case node_type::array: print(*reinterpret_cast<const array*>(&source())); break; - - case node_type::string: print_yaml_string(*reinterpret_cast<const value<std::string>*>(&source())); break; - - default: print_value(source(), source_type); - } - } +TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + void yaml_formatter::print_yaml_string(const value<std::string>& str) { + if (str->empty()) { + base::print(str); + return; + } + + bool contains_newline = false; + for (auto c = str->c_str(), e = str->c_str() + str->length(); c < e && !contains_newline; c++) + contains_newline = *c == '\n'; + + if (contains_newline) { + print_unformatted("|-"sv); + + increase_indent(); + + auto line_end = str->c_str() - 1u; + const auto end = str->c_str() + str->length(); + while (line_end != end) { + auto line_start = line_end + 1u; + line_end = line_start; + for (; line_end != end && *line_end != '\n'; line_end++); + + if TOML_LIKELY (line_start != line_end || line_end != end) { + print_newline(); + print_indent(); + print_unformatted( + std::string_view{line_start, static_cast<size_t>(line_end - line_start)}); + } + } + + decrease_indent(); + } else + print_string(*str, false, true); + } + + TOML_EXTERNAL_LINKAGE + void yaml_formatter::print(const toml::table& tbl, bool parent_is_array) { + if (tbl.empty()) { + print_unformatted("{}"sv); + return; + } + + increase_indent(); + + for (auto&& [k, v] : tbl) { + if (!parent_is_array) { + print_newline(); + print_indent(); + } + parent_is_array = false; + + print_string(k.str(), false, true); + if (terse_kvps()) + print_unformatted(":"sv); + else + print_unformatted(": "sv); + + const auto type = v.type(); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print(*reinterpret_cast<const table*>(&v)); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v)); + break; + case node_type::string: + print_yaml_string(*reinterpret_cast<const value<std::string>*>(&v)); + break; + default: + print_value(v, type); + } + } + + decrease_indent(); + } + + TOML_EXTERNAL_LINKAGE + void yaml_formatter::print(const toml::array& arr, bool parent_is_array) { + if (arr.empty()) { + print_unformatted("[]"sv); + return; + } + + increase_indent(); + + for (auto&& v : arr) { + if (!parent_is_array) { + print_newline(); + print_indent(); + } + parent_is_array = false; + + print_unformatted("- "sv); + + const auto type = v.type(); + TOML_ASSUME(type != node_type::none); + switch (type) { + case node_type::table: + print(*reinterpret_cast<const table*>(&v), true); + break; + case node_type::array: + print(*reinterpret_cast<const array*>(&v), true); + break; + case node_type::string: + print_yaml_string(*reinterpret_cast<const value<std::string>*>(&v)); + break; + default: + print_value(v, type); + } + } + + decrease_indent(); + } + + TOML_EXTERNAL_LINKAGE + void yaml_formatter::print() { + if (dump_failed_parse_result()) return; + + switch (auto source_type = source().type()) { + case node_type::table: + decrease_indent(); // so root kvps and tables have the same indent + print(*reinterpret_cast<const table*>(&source())); + break; + + case node_type::array: + print(*reinterpret_cast<const array*>(&source())); + break; + + case node_type::string: + print_yaml_string(*reinterpret_cast<const value<std::string>*>(&source())); + break; + + default: + print_value(source(), source_type); + } + } } TOML_NAMESPACE_END; #include "header_end.hpp" -#endif // TOML_ENABLE_FORMATTERS +#endif // TOML_ENABLE_FORMATTERS |
