fixed treating non-ASCII horizontal whitespace as valid (closes #135)

also:
- added `parse_result::at_path()`
This commit is contained in:
Mark Gillard 2022-01-09 13:01:56 +02:00
parent cdf85a9b60
commit e37e6d2bce
8 changed files with 499 additions and 362 deletions

View File

@ -18,9 +18,7 @@ template:
## Unreleased ## Unreleased
This release will be a major version bump, so it's ABI breaks all around. This release will be a major version bump, so it's ABI breaks all around.
Any changes that might cause code or build systems to break are indicated with ⚠️. Any changes that are likely to cause migration issues (API changes, build system breakage, etc.) are indicated with ⚠️.
Highlights are indicated with ❤️.
#### Fixes: #### Fixes:
- ⚠️ fixed `toml::table` init-list constructor requiring double-brackets - ⚠️ fixed `toml::table` init-list constructor requiring double-brackets
@ -28,50 +26,50 @@ Highlights are indicated with ❤️.
- ⚠️ fixed incorrect `noexcept` specifications on many functions - ⚠️ fixed incorrect `noexcept` specifications on many functions
- ⚠️ fixed missing `TOML_API` on some interfaces - ⚠️ fixed missing `TOML_API` on some interfaces
- fixed `toml::json_formatter` not formatting inf and nan incorrectly - fixed `toml::json_formatter` not formatting inf and nan incorrectly
- fixed a number of spec conformance issues (#127, #128, #129, #130, #131, #132) (@moorereason) - fixed a number of spec conformance issues (#127, #128, #129, #130, #131, #132, #135) (@moorereason)
- fixed an illegal table redefinition edge case (#112) (@python36) - fixed an illegal table redefinition edge case (#112) (@python36)
- fixed documentation issues - fixed documentation issues
- fixed GCC bug causing memory leak during parse failures (#123, #124) (@rsmmr, @ronalabraham)
- fixed incorrect handling of vertical whitespace in keys when printing TOML to streams - fixed incorrect handling of vertical whitespace in keys when printing TOML to streams
- fixed incorrect source position in redefinition error messages - fixed incorrect source position in redefinition error messages
- fixed memory leak during parse failures when compiled using GCC (#123, #124) (@rsmmr, @ronalabraham) - fixed missing includes `<initializer_list>`, `<utility>`
- fixed missing `#include <initializer_list>`
- fixed missing `#include <utility>`
- fixed parser not correctly round-tripping the format of binary and octal integers in some cases - fixed parser not correctly round-tripping the format of binary and octal integers in some cases
- fixed some incorrect unicode scalar sequence transformations (#125) - fixed some incorrect unicode scalar sequence transformations (#125)
- fixed strong exception guarantee edge-cases in `toml::table` and `toml::array` - fixed strong exception guarantee edge-cases in `toml::table` and `toml::array`
#### Additions: #### Additions:
- ❤&#xFE0F; added `operator->` to `toml::value` for class types - added value flags to array + table insert methods (#44) (@levicki)
- ❤&#xFE0F; added `toml::at_path()`, `toml::node::at_path()` and `toml::node_view::at_path()` for qualified path-based lookups (#118) (@ben-crowhurst) - added support for Unicode 14.0
- ❤&#xFE0F; added `toml::key` - provides a facility to access the source_regions of parsed keys (#82) (@vaartis) - added support for ref categories and cv-qualifiers in `toml::node::ref()`
- ❤&#xFE0F; added `toml::yaml_formatter` - added magic `toml::value_flags` constant `toml::preserve_source_value_flags`
- ❤&#xFE0F; added support for Unicode 14.0 - added clang's enum annotation attributes to all enums
- added `parse_benchmark` example - added `TOML_ENABLE_FORMATTERS` option
- added `toml::array::at()` (same semantics as `std::vector::at()`) - added `toml::yaml_formatter`
- added `toml::array::prune()` - added `toml::value` copy+move constructor overloads with flags override
- added `toml::array::replace()` (#109) (@LebJe) - added `toml::table::prune()`
- added `toml::array::resize()` param `default_init_flags` - added `toml::table::lower_bound()` (same semantics as `std::map::lower_bound()`)
- added `toml::date_time` converting constructors from `toml::date` and `toml::time` - added `toml::table::emplace_hint()` (same semantics as `std::map::emplace_hint()`)
- added `toml::format_flags::allow_binary_integers` - added `toml::table::at()` (same semantics as `std::map::at()`)
- added `toml::format_flags::allow_hexadecimal_integers` - added `toml::node_view::operator==`
- added `toml::format_flags::allow_octal_integers` - added `toml::key` - provides a facility to access the source_regions of parsed keys (#82) (@vaartis)
- added `toml::format_flags::allow_real_tabs_in_strings` - added `toml::is_key<>` and toml::is_key_or_convertible<>` metafunctions
- added `toml::format_flags::allow_unicode_strings`
- added `toml::format_flags::indent_array_elements` (#120) (@W4RH4WK)
- added `toml::format_flags::indent_sub_tables` (#120) (@W4RH4WK)
- added `toml::format_flags::relaxed_float_precision` (#89) (@vaartis) - added `toml::format_flags::relaxed_float_precision` (#89) (@vaartis)
- added `toml::format_flags::quote_infinities_and_nans` - added `toml::format_flags::quote_infinities_and_nans`
- added `toml::is_key<>` and toml::is_key_or_convertible<>` metafunctions - added `toml::format_flags::indent_sub_tables` (#120) (@W4RH4WK)
- added `toml::node_view::operator==` - added `toml::format_flags::indent_array_elements` (#120) (@W4RH4WK)
- added `toml::table::at()` (same semantics as `std::map::at()`) - added `toml::format_flags::allow_unicode_strings`
- added `toml::table::emplace_hint()` (same semantics as `std::map::emplace_hint()`) - added `toml::format_flags::allow_real_tabs_in_strings`
- added `toml::table::lower_bound()` (same semantics as `std::map::lower_bound()`) - added `toml::format_flags::allow_octal_integers`
- added `toml::table::prune()` - added `toml::format_flags::allow_hexadecimal_integers`
- added `toml::value` copy+move constructor overloads with flags override - added `toml::format_flags::allow_binary_integers`
- added `TOML_ENABLE_FORMATTERS` option - added `toml::date_time` converting constructors from `toml::date` and `toml::time`
- added clang's enum annotation attributes to all enums - added `toml::at_path()`, `toml::node::at_path()` and `toml::node_view::at_path()` for qualified path-based lookups (#118) (@ben-crowhurst)
- added magic `toml::value_flags` constant `toml::preserve_source_value_flags` - added `toml::array::resize()` param `default_init_flags`
- added value flags to array + table insert methods (#44) (@levicki) - added `toml::array::replace()` (#109) (@LebJe)
- added `toml::array::prune()`
- added `toml::array::at()` (same semantics as `std::vector::at()`)
- added `parse_benchmark` example
- added `operator->` to `toml::value` for class types
#### Changes: #### Changes:
- ⚠&#xFE0F; `toml::format_flags` is now backed by `uint64_t` (was previously `uint8_t`) - ⚠&#xFE0F; `toml::format_flags` is now backed by `uint64_t` (was previously `uint8_t`)
@ -83,8 +81,7 @@ Highlights are indicated with ❤&#xFE0F;.
- ⚠&#xFE0F; renamed `TOML_PARSER` option to `TOML_ENABLE_PARSER` (`TOML_PARSER` will continue to work but is deprecated) - ⚠&#xFE0F; renamed `TOML_PARSER` option to `TOML_ENABLE_PARSER` (`TOML_PARSER` will continue to work but is deprecated)
- ⚠&#xFE0F; renamed `TOML_UNRELEASED_FEATURES` to `TOML_ENABLE_UNRELEASED_FEATURES` (`TOML_UNRELEASED_FEATURES` will continue to work but is deprecated) - ⚠&#xFE0F; renamed `TOML_UNRELEASED_FEATURES` to `TOML_ENABLE_UNRELEASED_FEATURES` (`TOML_UNRELEASED_FEATURES` will continue to work but is deprecated)
- ⚠&#xFE0F; renamed `TOML_WINDOWS_COMPAT` to `TOML_ENABLE_WINDOWS_COMPAT` (`TOML_WINDOWS_COMPAT` will continue to work but is deprecated) - ⚠&#xFE0F; renamed `TOML_WINDOWS_COMPAT` to `TOML_ENABLE_WINDOWS_COMPAT` (`TOML_WINDOWS_COMPAT` will continue to work but is deprecated)
- ❤&#xFE0F; `toml::node::ref()` now supports explicit ref categories and cv-qualifiers - applied clang-format to all the things 🎉&#xFE0F;
- ❤&#xFE0F; applied clang-format to all the things 🎉&#xFE0F;
- exposed `TOML_NAMESPACE_START` and `TOML_NAMESPACE_END` macros to help with ADL specialization scenarios - exposed `TOML_NAMESPACE_START` and `TOML_NAMESPACE_END` macros to help with ADL specialization scenarios
- improved performance of parser - improved performance of parser
- made date/time constructors accept any integral types - made date/time constructors accept any integral types
@ -95,7 +92,7 @@ Highlights are indicated with ❤&#xFE0F;.
#### Removals: #### Removals:
- ⚠&#xFE0F; removed `toml::format_flags::allow_value_format_flags` - ⚠&#xFE0F; removed `toml::format_flags::allow_value_format_flags`
- ⚠&#xFE0F; removed `TOML_LARGE_FILES` (it is now default - explicitly setting `TOML_LARGE_FILES` to `0` will invoke an `#error`) - ⚠&#xFE0F; removed `TOML_LARGE_FILES` (it is now default - explicitly setting `TOML_LARGE_FILES` to `0` will invoke an `#error`)
- removed unnecessary template machinery (esp. where ostreams were involved) - &#xFE0F; removed unnecessary template machinery (esp. where ostreams were involved)
- removed unnecessary uses of `final` - removed unnecessary uses of `final`
#### Build system: #### Build system:

View File

@ -22,6 +22,7 @@ namespace
"# bar\bkek"sv, "# bar\bkek"sv,
"# \xf1\x63"sv, "# \xf1\x63"sv,
"# val1 = 1\fval2 = 2"sv, "# val1 = 1\fval2 = 2"sv,
"foo = 1\n\u2000\nbar = 2"sv,
"########## inline tables"sv, "########## inline tables"sv,
"val = {,}"sv, "val = {,}"sv,

View File

@ -39,7 +39,7 @@ TOML_NAMESPACE_START
/// \ecpp /// \ecpp
/// <br> /// <br>
/// Additionally, TOML allows '.' (period) characters to appear in keys if they are quoted strings. /// Additionally, TOML allows '.' (period) characters to appear in keys if they are quoted strings.
/// This function makes no allowance for this this, instead treating all period characters as sub-table delimiters. /// This function makes no allowance for this, instead treating all period characters as sub-table delimiters.
/// If you have periods in your table keys, first consider: /// If you have periods in your table keys, first consider:
/// 1. Not doing that /// 1. Not doing that
/// 2. Using node_view::operator[] instead. /// 2. Using node_view::operator[] instead.

View File

@ -312,25 +312,6 @@ TOML_NAMESPACE_START
/// @} /// @}
/// \name Iterators (ADL)
/// @{
/// \brief Returns an iterator to the first character in a key's backing string.
TOML_PURE_INLINE_GETTER
friend const_iterator begin(const key& k) noexcept
{
return k.begin();
}
/// \brief Returns an iterator to one-past-the-last character in a key's backing string.
TOML_PURE_INLINE_GETTER
friend const_iterator end(const key& k) noexcept
{
return k.end();
}
/// @}
/// \brief Prints the key's underlying string out to the stream. /// \brief Prints the key's underlying string out to the stream.
friend std::ostream& operator<<(std::ostream& lhs, const key& rhs) friend std::ostream& operator<<(std::ostream& lhs, const key& rhs)
{ {

View File

@ -30,7 +30,7 @@ TOML_NAMESPACE_START
/// or a toml::parse_error. Most member functions assume a particular one of these two states, /// or a toml::parse_error. Most member functions assume a particular one of these two states,
/// and calling them when in the wrong state will cause errors (e.g. attempting to access the /// and calling them when in the wrong state will cause errors (e.g. attempting to access the
/// error object when parsing was successful). \cpp /// error object when parsing was successful). \cpp
/// parse_result result = toml::parse_file("config.toml"); /// toml::parse_result result = toml::parse_file("config.toml");
/// if (result) /// if (result)
/// do_stuff_with_a_table(result); //implicitly converts to table& /// do_stuff_with_a_table(result); //implicitly converts to table&
/// else /// else
@ -45,7 +45,7 @@ TOML_NAMESPACE_START
/// (error occurred at line 1, column 13 of 'config.toml') /// (error occurred at line 1, column 13 of 'config.toml')
/// \eout /// \eout
/// ///
/// Getting node_views (`operator[]`) and using the iterator accessor functions (`begin(), end()` etc.) are /// Getting node_views (`operator[]`, `at_path()`) and using the iterator accessor functions (`begin()`, `end()` etc.) are
/// unconditionally safe; when parsing fails these just return 'empty' values. A ranged-for loop on a failed /// unconditionally safe; when parsing fails these just return 'empty' values. A ranged-for loop on a failed
/// parse_result is also safe since `begin()` and `end()` return the same iterator and will not lead to any /// parse_result is also safe since `begin()` and `end()` return the same iterator and will not lead to any
/// dereferences and iterations. /// dereferences and iterations.
@ -88,117 +88,7 @@ TOML_NAMESPACE_START
/// \brief A BidirectionalIterator for iterating over const key-value pairs in a wrapped toml::table. /// \brief A BidirectionalIterator for iterating over const key-value pairs in a wrapped toml::table.
using const_iterator = const_table_iterator; using const_iterator = const_table_iterator;
/// \brief Returns true if parsing succeeeded. /// \brief Default constructs an 'error' result.
TOML_NODISCARD
bool succeeded() const noexcept
{
return !err_;
}
/// \brief Returns true if parsing failed.
TOML_NODISCARD
bool failed() const noexcept
{
return err_;
}
/// \brief Returns true if parsing succeeded.
TOML_NODISCARD
explicit operator bool() const noexcept
{
return !err_;
}
/// \brief Returns the internal toml::table.
TOML_NODISCARD
toml::table& table() & noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<toml::table>(storage_);
}
/// \brief Returns the internal toml::table (rvalue overload).
TOML_NODISCARD
toml::table&& table() && noexcept
{
TOML_ASSERT_ASSUME(!err_);
return static_cast<toml::table&&>(*get_as<toml::table>(storage_));
}
/// \brief Returns the internal toml::table (const lvalue overload).
TOML_NODISCARD
const toml::table& table() const& noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<const toml::table>(storage_);
}
/// \brief Returns the internal toml::parse_error.
TOML_NODISCARD
parse_error& error() & noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<parse_error>(storage_);
}
/// \brief Returns the internal toml::parse_error (rvalue overload).
TOML_NODISCARD
parse_error&& error() && noexcept
{
TOML_ASSERT_ASSUME(err_);
return static_cast<parse_error&&>(*get_as<parse_error>(storage_));
}
/// \brief Returns the internal toml::parse_error (const lvalue overload).
TOML_NODISCARD
const parse_error& error() const& noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<const parse_error>(storage_);
}
/// \brief Returns the internal toml::table.
TOML_NODISCARD
operator toml::table&() noexcept
{
return table();
}
/// \brief Returns the internal toml::table (rvalue overload).
TOML_NODISCARD
operator toml::table&&() noexcept
{
return std::move(table());
}
/// \brief Returns the internal toml::table (const lvalue overload).
TOML_NODISCARD
operator const toml::table&() const noexcept
{
return table();
}
/// \brief Returns the internal toml::parse_error.
TOML_NODISCARD
explicit operator parse_error&() noexcept
{
return error();
}
/// \brief Returns the internal toml::parse_error (rvalue overload).
TOML_NODISCARD
explicit operator parse_error&&() noexcept
{
return std::move(error());
}
/// \brief Returns the internal toml::parse_error (const lvalue overload).
TOML_NODISCARD
explicit operator const parse_error&() const noexcept
{
return error();
}
TOML_NODISCARD_CTOR TOML_NODISCARD_CTOR
parse_result() noexcept // parse_result() noexcept //
: err_{ true } : err_{ true }
@ -259,69 +149,134 @@ TOML_NAMESPACE_START
destroy(); destroy();
} }
/// \brief Gets a node_view for the selected key-value pair in the wrapped table. /// \name Result state
/// /// @{
/// \param key The key used for the lookup.
/// /// \brief Returns true if parsing succeeeded.
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD TOML_NODISCARD
node_view<node> operator[](std::string_view key) noexcept bool succeeded() const noexcept
{ {
return err_ ? node_view<node>{} : table()[key]; return !err_;
} }
/// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload). /// \brief Returns true if parsing failed.
///
/// \param key The key used for the lookup.
///
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD TOML_NODISCARD
node_view<const node> operator[](std::string_view key) const noexcept bool failed() const noexcept
{ {
return err_ ? node_view<const node>{} : table()[key]; return err_;
} }
#if TOML_ENABLE_WINDOWS_COMPAT /// \brief Returns true if parsing succeeded.
/// \brief Gets a node_view for the selected key-value pair in the wrapped table.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \param key The key used for the lookup.
///
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD TOML_NODISCARD
node_view<node> operator[](std::wstring_view key) noexcept explicit operator bool() const noexcept
{ {
return err_ ? node_view<node>{} : table()[key]; return !err_;
} }
/// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload). /// @}
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. /// \name Successful parses
/// /// @{
/// \param key The key used for the lookup.
/// /// \brief Returns the internal toml::table.
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD TOML_NODISCARD
node_view<const node> operator[](std::wstring_view key) const noexcept toml::table& table() & noexcept
{ {
return err_ ? node_view<const node>{} : table()[key]; TOML_ASSERT_ASSUME(!err_);
return *get_as<toml::table>(storage_);
} }
#endif // TOML_ENABLE_WINDOWS_COMPAT /// \brief Returns the internal toml::table (rvalue overload).
TOML_NODISCARD
toml::table&& table() && noexcept
{
TOML_ASSERT_ASSUME(!err_);
return static_cast<toml::table&&>(*get_as<toml::table>(storage_));
}
/// \brief Returns the internal toml::table (const lvalue overload).
TOML_NODISCARD
const toml::table& table() const& noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<const toml::table>(storage_);
}
/// \brief Returns the internal toml::table.
TOML_NODISCARD
/* implicit */ operator toml::table&() noexcept
{
return table();
}
/// \brief Returns the internal toml::table (rvalue overload).
TOML_NODISCARD
/* implicit */ operator toml::table&&() noexcept
{
return std::move(table());
}
/// \brief Returns the internal toml::table (const lvalue overload).
TOML_NODISCARD
/* implicit */ operator const toml::table&() const noexcept
{
return table();
}
/// @}
/// \name Failed parses
/// @{
/// \brief Returns the internal toml::parse_error.
TOML_NODISCARD
parse_error& error() & noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<parse_error>(storage_);
}
/// \brief Returns the internal toml::parse_error (rvalue overload).
TOML_NODISCARD
parse_error&& error() && noexcept
{
TOML_ASSERT_ASSUME(err_);
return static_cast<parse_error&&>(*get_as<parse_error>(storage_));
}
/// \brief Returns the internal toml::parse_error (const lvalue overload).
TOML_NODISCARD
const parse_error& error() const& noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<const parse_error>(storage_);
}
/// \brief Returns the internal toml::parse_error.
TOML_NODISCARD
explicit operator parse_error&() noexcept
{
return error();
}
/// \brief Returns the internal toml::parse_error (rvalue overload).
TOML_NODISCARD
explicit operator parse_error&&() noexcept
{
return std::move(error());
}
/// \brief Returns the internal toml::parse_error (const lvalue overload).
TOML_NODISCARD
explicit operator const parse_error&() const noexcept
{
return error();
}
/// @}
/// \name Iterators
/// @{
/// \brief Returns an iterator to the first key-value pair in the wrapped table. /// \brief Returns an iterator to the first key-value pair in the wrapped table.
/// \remarks Returns a default-constructed 'nothing' iterator if the parsing failed. /// \remarks Returns a default-constructed 'nothing' iterator if the parsing failed.
@ -371,6 +326,117 @@ TOML_NAMESPACE_START
return err_ ? const_table_iterator{} : table().cend(); return err_ ? const_table_iterator{} : table().cend();
} }
/// @}
/// \name Node views
/// @{
/// \brief Gets a node_view for the selected key-value pair in the wrapped table.
///
/// \param key The key used for the lookup.
///
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD
node_view<node> operator[](std::string_view key) noexcept
{
return err_ ? node_view<node>{} : table()[key];
}
/// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload).
///
/// \param key The key used for the lookup.
///
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD
node_view<const node> operator[](std::string_view key) const noexcept
{
return err_ ? node_view<const node>{} : table()[key];
}
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
node_view<node> at_path(std::string_view path) noexcept
{
return err_ ? node_view<node>{} : table().at_path(path);
}
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
node_view<const node> at_path(std::string_view path) const noexcept
{
return err_ ? node_view<const node>{} : table().at_path(path);
}
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Gets a node_view for the selected key-value pair in the wrapped table.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \param key The key used for the lookup.
///
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD
node_view<node> operator[](std::wstring_view key) noexcept
{
return err_ ? node_view<node>{} : table()[key];
}
/// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload).
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \param key The key used for the lookup.
///
/// \returns A view of the value at the given key if parsing was successful and a matching key existed,
/// or an empty node view.
///
/// \see toml::node_view
TOML_NODISCARD
node_view<const node> operator[](std::wstring_view key) const noexcept
{
return err_ ? node_view<const node>{} : table()[key];
}
/// \brief Returns a view of the subnode matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
node_view<node> at_path(std::wstring_view path) noexcept
{
return err_ ? node_view<node>{} : table().at_path(path);
}
/// \brief Returns a const view of the subnode matching a fully-qualified "TOML path".
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
///
/// \see #toml::at_path(node&, std::string_view)
TOML_NODISCARD
node_view<const node> at_path(std::wstring_view path) const noexcept
{
return err_ ? node_view<const node>{} : table().at_path(path);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
/// @}
#if TOML_ENABLE_FORMATTERS #if TOML_ENABLE_FORMATTERS
/// \brief Prints the held error or table object out to a text stream. /// \brief Prints the held error or table object out to a text stream.

View File

@ -686,9 +686,9 @@ TOML_ANON_NAMESPACE_START
TOML_INTERNAL_LINKAGE TOML_INTERNAL_LINKAGE
std::string_view to_sv(const utf8_codepoint& cp) noexcept std::string_view to_sv(const utf8_codepoint& cp) noexcept
{ {
if TOML_UNLIKELY(cp.value <= U'\x1F') if (cp.value <= U'\x1F')
return impl::control_char_escapes[cp.value]; return impl::control_char_escapes[cp.value];
else if TOML_UNLIKELY(cp.value == U'\x7F') else if (cp.value == U'\x7F')
return "\\u007F"sv; return "\\u007F"sv;
else else
return std::string_view{ cp.bytes, cp.count }; return std::string_view{ cp.bytes, cp.count };
@ -703,15 +703,21 @@ TOML_ANON_NAMESPACE_START
return ""sv; return ""sv;
} }
struct escaped_codepoint
{
const utf8_codepoint& cp;
};
template <typename T> template <typename T>
TOML_ATTR(nonnull) TOML_ATTR(nonnull)
TOML_INTERNAL_LINKAGE TOML_INTERNAL_LINKAGE
TOML_NEVER_INLINE TOML_NEVER_INLINE
void concatenate(char*& write_pos, char* const buf_end, const T& arg) noexcept void concatenate(char*& write_pos, char* const buf_end, const T& arg) noexcept
{ {
static_assert(impl::is_one_of<impl::remove_cvref<T>, std::string_view, int64_t, uint64_t, double>, static_assert(
"concatenate inputs are limited to std::string_view, int64_t, uint64_t and double to keep " impl::is_one_of<impl::remove_cvref<T>, std::string_view, int64_t, uint64_t, double, escaped_codepoint>,
"instantiations to a minimum as an anti-bloat measure (hint: to_sv will probably help)"); "concatenate inputs are limited to [std::string_view, int64_t, uint64_t, double, escaped_codepoint] to "
"keep instantiations at a minimum as an anti-bloat measure (hint: to_sv will probably help)");
if (write_pos >= buf_end) if (write_pos >= buf_end)
return; return;
@ -750,6 +756,25 @@ TOML_ANON_NAMESPACE_START
concatenate(write_pos, buf_end, to_sv(std::move(ss).str())); concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
#endif #endif
} }
else if constexpr (std::is_same_v<arg_t, 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 });
}
}
else else
static_assert(impl::dependent_false<T>, "Evaluated unreachable branch!"); static_assert(impl::dependent_false<T>, "Evaluated unreachable branch!");
} }
@ -1143,6 +1168,9 @@ TOML_IMPL_NAMESPACE_START
bool consumed = false; bool consumed = false;
while (!is_eof() && is_horizontal_whitespace(*cp)) 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; consumed = true;
advance_and_return_if_error({}); advance_and_return_if_error({});
} }
@ -1155,17 +1183,19 @@ TOML_IMPL_NAMESPACE_START
if TOML_UNLIKELY(is_match(*cp, U'\v', U'\f')) if TOML_UNLIKELY(is_match(*cp, U'\v', U'\f'))
set_error_and_return_default( set_error_and_return_default(
R"(vertical tabs '\v' and form-feeds '\f' are not legal whitespace in TOML.)"sv); R"(vertical tabs '\v' and form-feeds '\f' are not legal line breaks in TOML)"sv);
if (*cp == U'\r') if (*cp == U'\r')
{ {
advance_and_return_if_error({}); // skip \r advance_and_return_if_error({}); // skip \r
if TOML_UNLIKELY(is_eof()) if TOML_UNLIKELY(is_eof())
set_error_and_return_default("expected \\n after \\r, saw EOF"sv); set_error_and_return_default("expected '\\n' after '\\r', saw EOF"sv);
if TOML_UNLIKELY(*cp != U'\n') if TOML_UNLIKELY(*cp != U'\n')
set_error_and_return_default("expected \\n after \\r, saw '"sv, to_sv(*cp), "'"sv); set_error_and_return_default("expected '\\n' after '\\r', saw '"sv,
escaped_codepoint{ *cp },
"'"sv);
} }
else if (*cp != U'\n') else if (*cp != U'\n')
return false; return false;
@ -2807,7 +2837,7 @@ TOML_IMPL_NAMESPACE_START
utf8_buffered_reader::max_history_length - 2u; utf8_buffered_reader::max_history_length - 2u;
if TOML_UNLIKELY(!eof_while_scanning && advance_count > max_numeric_value_length) 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, set_error_and_return_default("numeric value too long to identify type - cannot exceed "sv,
max_numeric_value_length, static_cast<uint64_t>(max_numeric_value_length),
" characters"sv); " characters"sv);
val.reset(new value{ parse_integer<10>() }); val.reset(new value{ parse_integer<10>() });
@ -3359,7 +3389,8 @@ TOML_IMPL_NAMESPACE_START
return_after_error({}); return_after_error({});
} }
// create the key first since the key buffer will likely get overritten during value parsing (inline tables) // 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); auto last_key = make_key(key_buffer.size() - 1u);
// now we can actually parse the value // now we can actually parse the value

View File

@ -302,4 +302,22 @@ b = []
parsing_should_fail(FILE_LINE_ARGS, " val = 0x8000000000000000"sv); // int64_t max + 1 parsing_should_fail(FILE_LINE_ARGS, " val = 0x8000000000000000"sv); // int64_t max + 1
parse_expected_value(FILE_LINE_ARGS, " 0x7FFFFFFFFFFFFFFF"sv, INT64_MAX); parse_expected_value(FILE_LINE_ARGS, " 0x7FFFFFFFFFFFFFFF"sv, INT64_MAX);
} }
SECTION("github/issues/135") // https://github.com/marzer/tomlplusplus/issues/135
{
parsing_should_succeed(FILE_LINE_ARGS, "0=0"sv);
parsing_should_succeed(FILE_LINE_ARGS, "1=1"sv);
parsing_should_succeed(FILE_LINE_ARGS, "2=2"sv);
parsing_should_succeed(FILE_LINE_ARGS,
"0=0\n"
"1=1\n"
"2=2\n"sv);
parsing_should_fail(FILE_LINE_ARGS,
"0=0\n"
"\u2000\u2000\n"
"1=1\n"
"2=2\n"sv);
}
} }

309
toml.hpp
View File

@ -6196,18 +6196,6 @@ TOML_NAMESPACE_START
return key_.data() + key_.length(); return key_.data() + key_.length();
} }
TOML_PURE_INLINE_GETTER
friend const_iterator begin(const key& k) noexcept
{
return k.begin();
}
TOML_PURE_INLINE_GETTER
friend const_iterator end(const key& k) noexcept
{
return k.end();
}
friend std::ostream& operator<<(std::ostream& lhs, const key& rhs) friend std::ostream& operator<<(std::ostream& lhs, const key& rhs)
{ {
impl::print_to_stream(lhs, rhs.key_); impl::print_to_stream(lhs, rhs.key_);
@ -8406,102 +8394,6 @@ TOML_NAMESPACE_START
using const_iterator = const_table_iterator; using const_iterator = const_table_iterator;
TOML_NODISCARD
bool succeeded() const noexcept
{
return !err_;
}
TOML_NODISCARD
bool failed() const noexcept
{
return err_;
}
TOML_NODISCARD
explicit operator bool() const noexcept
{
return !err_;
}
TOML_NODISCARD
toml::table& table() & noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<toml::table>(storage_);
}
TOML_NODISCARD
toml::table&& table() && noexcept
{
TOML_ASSERT_ASSUME(!err_);
return static_cast<toml::table&&>(*get_as<toml::table>(storage_));
}
TOML_NODISCARD
const toml::table& table() const& noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<const toml::table>(storage_);
}
TOML_NODISCARD
parse_error& error() & noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<parse_error>(storage_);
}
TOML_NODISCARD
parse_error&& error() && noexcept
{
TOML_ASSERT_ASSUME(err_);
return static_cast<parse_error&&>(*get_as<parse_error>(storage_));
}
TOML_NODISCARD
const parse_error& error() const& noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<const parse_error>(storage_);
}
TOML_NODISCARD
operator toml::table&() noexcept
{
return table();
}
TOML_NODISCARD
operator toml::table&&() noexcept
{
return std::move(table());
}
TOML_NODISCARD
operator const toml::table&() const noexcept
{
return table();
}
TOML_NODISCARD
explicit operator parse_error&() noexcept
{
return error();
}
TOML_NODISCARD
explicit operator parse_error&&() noexcept
{
return std::move(error());
}
TOML_NODISCARD
explicit operator const parse_error&() const noexcept
{
return error();
}
TOML_NODISCARD_CTOR TOML_NODISCARD_CTOR
parse_result() noexcept // parse_result() noexcept //
: err_{ true } : err_{ true }
@ -8560,32 +8452,100 @@ TOML_NAMESPACE_START
} }
TOML_NODISCARD TOML_NODISCARD
node_view<node> operator[](std::string_view key) noexcept bool succeeded() const noexcept
{ {
return err_ ? node_view<node>{} : table()[key]; return !err_;
} }
TOML_NODISCARD TOML_NODISCARD
node_view<const node> operator[](std::string_view key) const noexcept bool failed() const noexcept
{ {
return err_ ? node_view<const node>{} : table()[key]; return err_;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view<node> operator[](std::wstring_view key) noexcept
{
return err_ ? node_view<node>{} : table()[key];
} }
TOML_NODISCARD TOML_NODISCARD
node_view<const node> operator[](std::wstring_view key) const noexcept explicit operator bool() const noexcept
{ {
return err_ ? node_view<const node>{} : table()[key]; return !err_;
} }
#endif // TOML_ENABLE_WINDOWS_COMPAT TOML_NODISCARD
toml::table& table() & noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<toml::table>(storage_);
}
TOML_NODISCARD
toml::table&& table() && noexcept
{
TOML_ASSERT_ASSUME(!err_);
return static_cast<toml::table&&>(*get_as<toml::table>(storage_));
}
TOML_NODISCARD
const toml::table& table() const& noexcept
{
TOML_ASSERT_ASSUME(!err_);
return *get_as<const toml::table>(storage_);
}
TOML_NODISCARD
/* implicit */ operator toml::table&() noexcept
{
return table();
}
TOML_NODISCARD
/* implicit */ operator toml::table&&() noexcept
{
return std::move(table());
}
TOML_NODISCARD
/* implicit */ operator const toml::table&() const noexcept
{
return table();
}
TOML_NODISCARD
parse_error& error() & noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<parse_error>(storage_);
}
TOML_NODISCARD
parse_error&& error() && noexcept
{
TOML_ASSERT_ASSUME(err_);
return static_cast<parse_error&&>(*get_as<parse_error>(storage_));
}
TOML_NODISCARD
const parse_error& error() const& noexcept
{
TOML_ASSERT_ASSUME(err_);
return *get_as<const parse_error>(storage_);
}
TOML_NODISCARD
explicit operator parse_error&() noexcept
{
return error();
}
TOML_NODISCARD
explicit operator parse_error&&() noexcept
{
return std::move(error());
}
TOML_NODISCARD
explicit operator const parse_error&() const noexcept
{
return error();
}
TOML_NODISCARD TOML_NODISCARD
table_iterator begin() noexcept table_iterator begin() noexcept
@ -8623,6 +8583,58 @@ TOML_NAMESPACE_START
return err_ ? const_table_iterator{} : table().cend(); return err_ ? const_table_iterator{} : table().cend();
} }
TOML_NODISCARD
node_view<node> operator[](std::string_view key) noexcept
{
return err_ ? node_view<node>{} : table()[key];
}
TOML_NODISCARD
node_view<const node> operator[](std::string_view key) const noexcept
{
return err_ ? node_view<const node>{} : table()[key];
}
TOML_NODISCARD
node_view<node> at_path(std::string_view path) noexcept
{
return err_ ? node_view<node>{} : table().at_path(path);
}
TOML_NODISCARD
node_view<const node> at_path(std::string_view path) const noexcept
{
return err_ ? node_view<const node>{} : table().at_path(path);
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view<node> operator[](std::wstring_view key) noexcept
{
return err_ ? node_view<node>{} : table()[key];
}
TOML_NODISCARD
node_view<const node> operator[](std::wstring_view key) const noexcept
{
return err_ ? node_view<const node>{} : table()[key];
}
TOML_NODISCARD
node_view<node> at_path(std::wstring_view path) noexcept
{
return err_ ? node_view<node>{} : table().at_path(path);
}
TOML_NODISCARD
node_view<const node> at_path(std::wstring_view path) const noexcept
{
return err_ ? node_view<const node>{} : table().at_path(path);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
#if TOML_ENABLE_FORMATTERS #if TOML_ENABLE_FORMATTERS
friend std::ostream& operator<<(std::ostream& os, const parse_result& result) friend std::ostream& operator<<(std::ostream& os, const parse_result& result)
@ -11693,9 +11705,9 @@ TOML_ANON_NAMESPACE_START
TOML_INTERNAL_LINKAGE TOML_INTERNAL_LINKAGE
std::string_view to_sv(const utf8_codepoint& cp) noexcept std::string_view to_sv(const utf8_codepoint& cp) noexcept
{ {
if TOML_UNLIKELY(cp.value <= U'\x1F') if (cp.value <= U'\x1F')
return impl::control_char_escapes[cp.value]; return impl::control_char_escapes[cp.value];
else if TOML_UNLIKELY(cp.value == U'\x7F') else if (cp.value == U'\x7F')
return "\\u007F"sv; return "\\u007F"sv;
else else
return std::string_view{ cp.bytes, cp.count }; return std::string_view{ cp.bytes, cp.count };
@ -11710,15 +11722,21 @@ TOML_ANON_NAMESPACE_START
return ""sv; return ""sv;
} }
struct escaped_codepoint
{
const utf8_codepoint& cp;
};
template <typename T> template <typename T>
TOML_ATTR(nonnull) TOML_ATTR(nonnull)
TOML_INTERNAL_LINKAGE TOML_INTERNAL_LINKAGE
TOML_NEVER_INLINE TOML_NEVER_INLINE
void concatenate(char*& write_pos, char* const buf_end, const T& arg) noexcept void concatenate(char*& write_pos, char* const buf_end, const T& arg) noexcept
{ {
static_assert(impl::is_one_of<impl::remove_cvref<T>, std::string_view, int64_t, uint64_t, double>, static_assert(
"concatenate inputs are limited to std::string_view, int64_t, uint64_t and double to keep " impl::is_one_of<impl::remove_cvref<T>, std::string_view, int64_t, uint64_t, double, escaped_codepoint>,
"instantiations to a minimum as an anti-bloat measure (hint: to_sv will probably help)"); "concatenate inputs are limited to [std::string_view, int64_t, uint64_t, double, escaped_codepoint] to "
"keep instantiations at a minimum as an anti-bloat measure (hint: to_sv will probably help)");
if (write_pos >= buf_end) if (write_pos >= buf_end)
return; return;
@ -11757,6 +11775,25 @@ TOML_ANON_NAMESPACE_START
concatenate(write_pos, buf_end, to_sv(std::move(ss).str())); concatenate(write_pos, buf_end, to_sv(std::move(ss).str()));
#endif #endif
} }
else if constexpr (std::is_same_v<arg_t, 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 });
}
}
else else
static_assert(impl::dependent_false<T>, "Evaluated unreachable branch!"); static_assert(impl::dependent_false<T>, "Evaluated unreachable branch!");
} }
@ -12150,6 +12187,9 @@ TOML_IMPL_NAMESPACE_START
bool consumed = false; bool consumed = false;
while (!is_eof() && is_horizontal_whitespace(*cp)) 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; consumed = true;
advance_and_return_if_error({}); advance_and_return_if_error({});
} }
@ -12162,17 +12202,19 @@ TOML_IMPL_NAMESPACE_START
if TOML_UNLIKELY(is_match(*cp, U'\v', U'\f')) if TOML_UNLIKELY(is_match(*cp, U'\v', U'\f'))
set_error_and_return_default( set_error_and_return_default(
R"(vertical tabs '\v' and form-feeds '\f' are not legal whitespace in TOML.)"sv); R"(vertical tabs '\v' and form-feeds '\f' are not legal line breaks in TOML)"sv);
if (*cp == U'\r') if (*cp == U'\r')
{ {
advance_and_return_if_error({}); // skip \r advance_and_return_if_error({}); // skip \r
if TOML_UNLIKELY(is_eof()) if TOML_UNLIKELY(is_eof())
set_error_and_return_default("expected \\n after \\r, saw EOF"sv); set_error_and_return_default("expected '\\n' after '\\r', saw EOF"sv);
if TOML_UNLIKELY(*cp != U'\n') if TOML_UNLIKELY(*cp != U'\n')
set_error_and_return_default("expected \\n after \\r, saw '"sv, to_sv(*cp), "'"sv); set_error_and_return_default("expected '\\n' after '\\r', saw '"sv,
escaped_codepoint{ *cp },
"'"sv);
} }
else if (*cp != U'\n') else if (*cp != U'\n')
return false; return false;
@ -13813,7 +13855,7 @@ TOML_IMPL_NAMESPACE_START
utf8_buffered_reader::max_history_length - 2u; utf8_buffered_reader::max_history_length - 2u;
if TOML_UNLIKELY(!eof_while_scanning && advance_count > max_numeric_value_length) 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, set_error_and_return_default("numeric value too long to identify type - cannot exceed "sv,
max_numeric_value_length, static_cast<uint64_t>(max_numeric_value_length),
" characters"sv); " characters"sv);
val.reset(new value{ parse_integer<10>() }); val.reset(new value{ parse_integer<10>() });
@ -14365,7 +14407,8 @@ TOML_IMPL_NAMESPACE_START
return_after_error({}); return_after_error({});
} }
// create the key first since the key buffer will likely get overritten during value parsing (inline tables) // 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); auto last_key = make_key(key_buffer.size() - 1u);
// now we can actually parse the value // now we can actually parse the value