goldendict-ng/thirdparty/tomlplusplus/toml++/toml.h
shenleban tongying 0c75d4f399
cmake: updates
* add USE_SYSTEM_FMT & USE_SYSTEM_TOML
* switch back to libeb
2023-06-01 01:28:45 -04:00

17415 lines
465 KiB
C++

//----------------------------------------------------------------------------------------------------------------------
//
// toml++ v3.3.0
// https://github.com/marzer/tomlplusplus
// SPDX-License-Identifier: MIT
//
//----------------------------------------------------------------------------------------------------------------------
//
// - THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY -
//
// If you wish to submit a contribution to toml++, hooray and thanks! Before you crack on, please be aware that this
// file was assembled from a number of smaller files by a python script, and code contributions should not be made
// against it directly. You should instead make your changes in the relevant source file(s). The file names of the files
// that contributed to this header can be found at the beginnings and ends of the corresponding sections of this file.
//
//----------------------------------------------------------------------------------------------------------------------
//
// TOML Language Specifications:
// latest: https://github.com/toml-lang/toml/blob/master/README.md
// v1.0.0: https://toml.io/en/v1.0.0
// v0.5.0: https://toml.io/en/v0.5.0
// changelog: https://github.com/toml-lang/toml/blob/master/CHANGELOG.md
//
//----------------------------------------------------------------------------------------------------------------------
//
// MIT License
//
// Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//----------------------------------------------------------------------------------------------------------------------
#ifndef TOMLPLUSPLUS_H
#define TOMLPLUSPLUS_H
#define INCLUDE_TOMLPLUSPLUS_H // old guard name used pre-v3
//******** impl/preprocessor.h ***************************************************************************************
#ifndef __cplusplus
#error toml++ is a C++ library.
#endif
#ifdef _MSVC_LANG
#define TOML_CPP _MSVC_LANG
#else
#define TOML_CPP __cplusplus
#endif
#if TOML_CPP >= 202002L
#undef TOML_CPP
#define TOML_CPP 20
#elif TOML_CPP >= 201703L
#undef TOML_CPP
#define TOML_CPP 17
#else
#if TOML_CPP < 201103L
#error toml++ requires C++17 or higher. For a pre-C++11 TOML library see https://github.com/ToruNiina/Boost.toml
#elif TOML_CPP < 201703L
#error toml++ requires C++17 or higher. For a C++11 TOML library see https://github.com/ToruNiina/toml11
#endif
#endif
#define TOML_MAKE_VERSION(major, minor, patch) (((major)*10000) + ((minor)*100) + ((patch)))
#ifdef __clang__
#define TOML_CLANG __clang_major__
#define TOML_CLANG_VERSION TOML_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
#else
#define TOML_CLANG 0
#endif
#ifdef __INTEL_COMPILER
#define TOML_ICC __INTEL_COMPILER
#ifdef __ICL
#define TOML_ICC_CL TOML_ICC
#else
#define TOML_ICC_CL 0
#endif
#else
#define TOML_ICC 0
#define TOML_ICC_CL 0
#endif
#if defined(_MSC_VER) && !TOML_CLANG && !TOML_ICC
#define TOML_MSVC _MSC_VER
#else
#define TOML_MSVC 0
#endif
#if defined(__GNUC__) && !TOML_CLANG && !TOML_ICC
#define TOML_GCC __GNUC__
#else
#define TOML_GCC 0
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__)
#define TOML_WINDOWS 1
#else
#define TOML_WINDOWS 0
#endif
#if defined(DOXYGEN) || defined(__DOXYGEN__) || defined(__POXY__) || defined(__poxy__)
#define TOML_DOXYGEN 1
#else
#define TOML_DOXYGEN 0
#endif
#ifdef __INTELLISENSE__
#define TOML_INTELLISENSE 1
#else
#define TOML_INTELLISENSE 0
#endif
// special handling for apple clang; see:
// - https://github.com/marzer/tomlplusplus/issues/189
// - https://en.wikipedia.org/wiki/Xcode
// - https://stackoverflow.com/questions/19387043/how-can-i-reliably-detect-the-version-of-clang-at-preprocessing-time
#if TOML_CLANG && defined(__apple_build_version__)
#undef TOML_CLANG
#if TOML_CLANG_VERSION >= TOML_MAKE_VERSION(14, 0, 0)
#define TOML_CLANG 14
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(13, 1, 6)
#define TOML_CLANG 13
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(13, 0, 0)
#define TOML_CLANG 12
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(12, 0, 5)
#define TOML_CLANG 11
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(12, 0, 0)
#define TOML_CLANG 10
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(11, 0, 3)
#define TOML_CLANG 9
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(11, 0, 0)
#define TOML_CLANG 8
#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(10, 0, 1)
#define TOML_CLANG 7
#else
#define TOML_CLANG 6 // not strictly correct but doesn't matter below this
#endif
#endif
// IA64
#if defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64)
#define TOML_ARCH_ITANIUM 1
#else
#define TOML_ARCH_ITANIUM 0
#endif
// AMD64
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
#define TOML_ARCH_AMD64 1
#else
#define TOML_ARCH_AMD64 0
#endif
// 32-bit x86
#if defined(__i386__) || defined(_M_IX86)
#define TOML_ARCH_X86 1
#else
#define TOML_ARCH_X86 0
#endif
// ARM
#if defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64) || defined(_M_ARM64) || defined(__ARM_64BIT_STATE) \
|| defined(_M_ARM64EC)
#define TOML_ARCH_ARM32 0
#define TOML_ARCH_ARM64 1
#define TOML_ARCH_ARM 1
#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_32BIT_STATE)
#define TOML_ARCH_ARM32 1
#define TOML_ARCH_ARM64 0
#define TOML_ARCH_ARM 1
#else
#define TOML_ARCH_ARM32 0
#define TOML_ARCH_ARM64 0
#define TOML_ARCH_ARM 0
#endif
// TOML_HAS_INCLUDE
#ifdef __has_include
#define TOML_HAS_INCLUDE(header) __has_include(header)
#else
#define TOML_HAS_INCLUDE(header) 0
#endif
#ifdef __has_builtin
#define TOML_HAS_BUILTIN(name) __has_builtin(name)
#else
#define TOML_HAS_BUILTIN(name) 0
#endif
// TOML_HAS_FEATURE
#ifdef __has_feature
#define TOML_HAS_FEATURE(name) __has_feature(name)
#else
#define TOML_HAS_FEATURE(name) 0
#endif
// TOML_HAS_ATTR
#ifdef __has_attribute
#define TOML_HAS_ATTR(attr) __has_attribute(attr)
#else
#define TOML_HAS_ATTR(attr) 0
#endif
// TOML_HAS_CPP_ATTR
#ifdef __has_cpp_attribute
#define TOML_HAS_CPP_ATTR(attr) __has_cpp_attribute(attr)
#else
#define TOML_HAS_CPP_ATTR(attr) 0
#endif
// TOML_COMPILER_HAS_EXCEPTIONS
#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(__cpp_exceptions)
#define TOML_COMPILER_HAS_EXCEPTIONS 1
#else
#define TOML_COMPILER_HAS_EXCEPTIONS 0
#endif
// TOML_COMPILER_HAS_RTTI
#if defined(_CPPRTTI) || defined(__GXX_RTTI) || TOML_HAS_FEATURE(cxx_rtti)
#define TOML_COMPILER_HAS_RTTI 1
#else
#define TOML_COMPILER_HAS_RTTI 0
#endif
// TOML_ATTR (gnu attributes)
#if TOML_CLANG || TOML_GCC || defined(__GNUC__)
#define TOML_ATTR(...) __attribute__((__VA_ARGS__))
#else
#define TOML_ATTR(...)
#endif
// TOML_DECLSPEC (msvc attributes)
#ifdef _MSC_VER
#define TOML_DECLSPEC(...) __declspec(__VA_ARGS__)
#else
#define TOML_DECLSPEC(...)
#endif
// TOML_CONCAT
#define TOML_CONCAT_1(x, y) x##y
#define TOML_CONCAT(x, y) TOML_CONCAT_1(x, y)
// TOML_MAKE_STRING
#define TOML_MAKE_STRING_1(s) #s
#define TOML_MAKE_STRING(s) TOML_MAKE_STRING_1(s)
// TOML_PRAGMA_XXXX (compiler-specific pragmas)
#if TOML_CLANG
#define TOML_PRAGMA_CLANG(decl) _Pragma(TOML_MAKE_STRING(clang decl))
#else
#define TOML_PRAGMA_CLANG(decl)
#endif
#if TOML_CLANG >= 9
#define TOML_PRAGMA_CLANG_GE_9(decl) TOML_PRAGMA_CLANG(decl)
#else
#define TOML_PRAGMA_CLANG_GE_9(decl)
#endif
#if TOML_CLANG >= 10
#define TOML_PRAGMA_CLANG_GE_10(decl) TOML_PRAGMA_CLANG(decl)
#else
#define TOML_PRAGMA_CLANG_GE_10(decl)
#endif
#if TOML_CLANG >= 11
#define TOML_PRAGMA_CLANG_GE_11(decl) TOML_PRAGMA_CLANG(decl)
#else
#define TOML_PRAGMA_CLANG_GE_11(decl)
#endif
#if TOML_GCC
#define TOML_PRAGMA_GCC(decl) _Pragma(TOML_MAKE_STRING(GCC decl))
#else
#define TOML_PRAGMA_GCC(decl)
#endif
#if TOML_MSVC
#define TOML_PRAGMA_MSVC(...) __pragma(__VA_ARGS__)
#else
#define TOML_PRAGMA_MSVC(...)
#endif
#if TOML_ICC
#define TOML_PRAGMA_ICC(...) __pragma(__VA_ARGS__)
#else
#define TOML_PRAGMA_ICC(...)
#endif
// TOML_ALWAYS_INLINE
#ifdef _MSC_VER
#define TOML_ALWAYS_INLINE __forceinline
#elif TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__always_inline__)
#define TOML_ALWAYS_INLINE \
TOML_ATTR(__always_inline__) \
inline
#else
#define TOML_ALWAYS_INLINE inline
#endif
// TOML_NEVER_INLINE
#ifdef _MSC_VER
#define TOML_NEVER_INLINE TOML_DECLSPEC(noinline)
#elif TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__noinline__)
#define TOML_NEVER_INLINE TOML_ATTR(__noinline__)
#else
#define TOML_NEVER_INLINE
#endif
// MSVC attributes
#define TOML_ABSTRACT_INTERFACE TOML_DECLSPEC(novtable)
#define TOML_EMPTY_BASES TOML_DECLSPEC(empty_bases)
// TOML_TRIVIAL_ABI
#if TOML_CLANG || TOML_HAS_ATTR(__trivial_abi__)
#define TOML_TRIVIAL_ABI TOML_ATTR(__trivial_abi__)
#else
#define TOML_TRIVIAL_ABI
#endif
// TOML_NODISCARD
#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201603
#define TOML_NODISCARD [[nodiscard]]
#elif TOML_CLANG || TOML_GCC || TOML_HAS_ATTR(__warn_unused_result__)
#define TOML_NODISCARD TOML_ATTR(__warn_unused_result__)
#else
#define TOML_NODISCARD
#endif
// TOML_NODISCARD_CTOR
#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201907
#define TOML_NODISCARD_CTOR [[nodiscard]]
#else
#define TOML_NODISCARD_CTOR
#endif
// pure + const
// clang-format off
#ifdef NDEBUG
#define TOML_PURE TOML_DECLSPEC(noalias) TOML_ATTR(__pure__)
#define TOML_CONST TOML_DECLSPEC(noalias) TOML_ATTR(__const__)
#define TOML_PURE_GETTER TOML_NODISCARD TOML_PURE
#define TOML_CONST_GETTER TOML_NODISCARD TOML_CONST
#define TOML_PURE_INLINE_GETTER TOML_NODISCARD TOML_ALWAYS_INLINE TOML_PURE
#define TOML_CONST_INLINE_GETTER TOML_NODISCARD TOML_ALWAYS_INLINE TOML_CONST
#else
#define TOML_PURE
#define TOML_CONST
#define TOML_PURE_GETTER TOML_NODISCARD
#define TOML_CONST_GETTER TOML_NODISCARD
#define TOML_PURE_INLINE_GETTER TOML_NODISCARD TOML_ALWAYS_INLINE
#define TOML_CONST_INLINE_GETTER TOML_NODISCARD TOML_ALWAYS_INLINE
#endif
// clang-format on
// TOML_ASSUME
#ifdef _MSC_VER
#define TOML_ASSUME(...) __assume(__VA_ARGS__)
#elif TOML_ICC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_assume)
#define TOML_ASSUME(...) __builtin_assume(__VA_ARGS__)
#else
#define TOML_ASSUME(...) static_assert(true)
#endif
// TOML_UNREACHABLE
#ifdef _MSC_VER
#define TOML_UNREACHABLE __assume(0)
#elif TOML_ICC || TOML_CLANG || TOML_GCC || TOML_HAS_BUILTIN(__builtin_unreachable)
#define TOML_UNREACHABLE __builtin_unreachable()
#else
#define TOML_UNREACHABLE static_assert(true)
#endif
// TOML_LIKELY
#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(likely) >= 201803
#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]]
#define TOML_LIKELY_CASE [[likely]]
#elif TOML_GCC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_expect)
#define TOML_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1))
#else
#define TOML_LIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_LIKELY_CASE
#define TOML_LIKELY_CASE
#endif
// TOML_UNLIKELY
#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(unlikely) >= 201803
#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#define TOML_UNLIKELY_CASE [[unlikely]]
#elif TOML_GCC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_expect)
#define TOML_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0))
#else
#define TOML_UNLIKELY(...) (__VA_ARGS__)
#endif
#ifndef TOML_UNLIKELY_CASE
#define TOML_UNLIKELY_CASE
#endif
// TOML_FLAGS_ENUM
#if TOML_CLANG || TOML_HAS_ATTR(flag_enum)
#define TOML_FLAGS_ENUM __attribute__((flag_enum))
#else
#define TOML_FLAGS_ENUM
#endif
// TOML_OPEN_ENUM + TOML_CLOSED_ENUM
#if TOML_CLANG || TOML_HAS_ATTR(enum_extensibility)
#define TOML_OPEN_ENUM __attribute__((enum_extensibility(open)))
#define TOML_CLOSED_ENUM __attribute__((enum_extensibility(closed)))
#else
#define TOML_OPEN_ENUM
#define TOML_CLOSED_ENUM
#endif
// TOML_OPEN_FLAGS_ENUM + TOML_CLOSED_FLAGS_ENUM
#define TOML_OPEN_FLAGS_ENUM TOML_OPEN_ENUM TOML_FLAGS_ENUM
#define TOML_CLOSED_FLAGS_ENUM TOML_CLOSED_ENUM TOML_FLAGS_ENUM
// TOML_MAKE_FLAGS
#define TOML_MAKE_FLAGS_2(T, op, linkage) \
TOML_CONST_INLINE_GETTER \
linkage constexpr T operator op(T lhs, T rhs) noexcept \
{ \
using under = std::underlying_type_t<T>; \
return static_cast<T>(static_cast<under>(lhs) op static_cast<under>(rhs)); \
} \
\
linkage constexpr T& operator TOML_CONCAT(op, =)(T & lhs, T rhs) noexcept \
{ \
return lhs = (lhs op rhs); \
} \
\
static_assert(true)
#define TOML_MAKE_FLAGS_1(T, linkage) \
static_assert(std::is_enum_v<T>); \
\
TOML_MAKE_FLAGS_2(T, &, linkage); \
TOML_MAKE_FLAGS_2(T, |, linkage); \
TOML_MAKE_FLAGS_2(T, ^, linkage); \
\
TOML_CONST_INLINE_GETTER \
linkage constexpr T operator~(T val) noexcept \
{ \
using under = std::underlying_type_t<T>; \
return static_cast<T>(~static_cast<under>(val)); \
} \
\
TOML_CONST_INLINE_GETTER \
linkage constexpr bool operator!(T val) noexcept \
{ \
using under = std::underlying_type_t<T>; \
return !static_cast<under>(val); \
} \
\
static_assert(true)
#define TOML_MAKE_FLAGS(T) TOML_MAKE_FLAGS_1(T, )
#define TOML_UNUSED(...) static_cast<void>(__VA_ARGS__)
#define TOML_DELETE_DEFAULTS(T) \
T(const T&) = delete; \
T(T&&) = delete; \
T& operator=(const T&) = delete; \
T& operator=(T&&) = delete
#define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \
__VA_ARGS__ TOML_NODISCARD \
friend bool operator==(RHS rhs, LHS lhs) noexcept \
{ \
return lhs == rhs; \
} \
__VA_ARGS__ TOML_NODISCARD \
friend bool operator!=(LHS lhs, RHS rhs) noexcept \
{ \
return !(lhs == rhs); \
} \
__VA_ARGS__ TOML_NODISCARD \
friend bool operator!=(RHS rhs, LHS lhs) noexcept \
{ \
return !(lhs == rhs); \
} \
static_assert(true)
#define TOML_EVAL_BOOL_1(T, F) T
#define TOML_EVAL_BOOL_0(T, F) F
#if !defined(__POXY__) && !defined(POXY_IMPLEMENTATION_DETAIL)
#define POXY_IMPLEMENTATION_DETAIL(...) __VA_ARGS__
#endif
// COMPILER-SPECIFIC WARNING MANAGEMENT
#if TOML_CLANG
#define TOML_PUSH_WARNINGS \
TOML_PRAGMA_CLANG(diagnostic push) \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wunknown-warning-option") \
static_assert(true)
#define TOML_DISABLE_SWITCH_WARNINGS \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wswitch") \
static_assert(true)
#define TOML_DISABLE_ARITHMETIC_WARNINGS \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wfloat-equal") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wdouble-promotion") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wchar-subscripts") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wshift-sign-overflow") \
static_assert(true)
#define TOML_DISABLE_SPAM_WARNINGS \
TOML_PRAGMA_CLANG_GE_9(diagnostic ignored "-Wctad-maybe-unsupported") \
TOML_PRAGMA_CLANG_GE_10(diagnostic ignored "-Wzero-as-null-pointer-constant") \
TOML_PRAGMA_CLANG_GE_11(diagnostic ignored "-Wsuggest-destructor-override") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wweak-template-vtables") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wdouble-promotion") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wchar-subscripts") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wmissing-field-initializers") \
TOML_PRAGMA_CLANG(diagnostic ignored "-Wpadded") \
static_assert(true)
#define TOML_POP_WARNINGS \
TOML_PRAGMA_CLANG(diagnostic pop) \
static_assert(true)
#define TOML_DISABLE_WARNINGS \
TOML_PRAGMA_CLANG(diagnostic push) \
TOML_PRAGMA_CLANG(diagnostic ignored "-Weverything") \
static_assert(true, "")
#define TOML_ENABLE_WARNINGS \
TOML_PRAGMA_CLANG(diagnostic pop) \
static_assert(true)
#define TOML_SIMPLE_STATIC_ASSERT_MESSAGES 1
#elif TOML_MSVC
#define TOML_PUSH_WARNINGS \
__pragma(warning(push)) \
static_assert(true)
#if TOML_HAS_INCLUDE(<CodeAnalysis/Warnings.h>)
#pragma warning(push, 0)
#include <CodeAnalysis/Warnings.h>
#pragma warning(pop)
#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS \
__pragma(warning(disable : ALL_CODE_ANALYSIS_WARNINGS)) \
static_assert(true)
#else
#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true)
#endif
#define TOML_DISABLE_SWITCH_WARNINGS \
__pragma(warning(disable : 4061)) \
__pragma(warning(disable : 4062)) \
__pragma(warning(disable : 4063)) \
__pragma(warning(disable : 5262)) /* switch-case implicit fallthrough (false-positive) */ \
__pragma(warning(disable : 26819)) /* cg: unannotated fallthrough */ \
static_assert(true)
#define TOML_DISABLE_SPAM_WARNINGS \
__pragma(warning(disable : 4127)) /* conditional expr is constant */ \
__pragma(warning(disable : 4324)) /* structure was padded due to alignment specifier */ \
__pragma(warning(disable : 4348)) \
__pragma(warning(disable : 4464)) /* relative include path contains '..' */ \
__pragma(warning(disable : 4505)) /* unreferenced local function removed */ \
__pragma(warning(disable : 4514)) /* unreferenced inline function has been removed */ \
__pragma(warning(disable : 4582)) /* constructor is not implicitly called */ \
__pragma(warning(disable : 4619)) /* there is no warning number 'XXXX' */ \
__pragma(warning(disable : 4623)) /* default constructor was implicitly defined as deleted */ \
__pragma(warning(disable : 4625)) /* copy constructor was implicitly defined as deleted */ \
__pragma(warning(disable : 4626)) /* assignment operator was implicitly defined as deleted */ \
__pragma(warning(disable : 4710)) /* function not inlined */ \
__pragma(warning(disable : 4711)) /* function selected for automatic expansion */ \
__pragma(warning(disable : 4820)) /* N bytes padding added */ \
__pragma(warning(disable : 4946)) /* reinterpret_cast used between related classes */ \
__pragma(warning(disable : 5026)) /* move constructor was implicitly defined as deleted */ \
__pragma(warning(disable : 5027)) /* move assignment operator was implicitly defined as deleted */ \
__pragma(warning(disable : 5039)) /* potentially throwing function passed to 'extern "C"' function */ \
__pragma(warning(disable : 5045)) /* Compiler will insert Spectre mitigation */ \
__pragma(warning(disable : 5264)) /* const variable is not used (false-positive) */ \
__pragma(warning(disable : 26451)) \
__pragma(warning(disable : 26490)) \
__pragma(warning(disable : 26495)) \
__pragma(warning(disable : 26812)) \
__pragma(warning(disable : 26819)) \
static_assert(true)
#define TOML_DISABLE_ARITHMETIC_WARNINGS \
__pragma(warning(disable : 4365)) /* argument signed/unsigned mismatch */ \
__pragma(warning(disable : 4738)) /* storing 32-bit float result in memory */ \
__pragma(warning(disable : 5219)) /* implicit conversion from integral to float */ \
static_assert(true)
#define TOML_POP_WARNINGS \
__pragma(warning(pop)) \
static_assert(true)
#define TOML_DISABLE_WARNINGS \
__pragma(warning(push, 0)) \
__pragma(warning(disable : 4348)) \
__pragma(warning(disable : 4668)) \
__pragma(warning(disable : 5105)) \
__pragma(warning(disable : 5264)) \
TOML_DISABLE_CODE_ANALYSIS_WARNINGS; \
TOML_DISABLE_SWITCH_WARNINGS; \
TOML_DISABLE_SPAM_WARNINGS; \
TOML_DISABLE_ARITHMETIC_WARNINGS; \
static_assert(true)
#define TOML_ENABLE_WARNINGS TOML_POP_WARNINGS
#elif TOML_ICC
#define TOML_PUSH_WARNINGS \
__pragma(warning(push)) \
static_assert(true)
#define TOML_DISABLE_SPAM_WARNINGS \
__pragma(warning(disable : 82)) /* storage class is not first */ \
__pragma(warning(disable : 111)) /* statement unreachable (false-positive) */ \
__pragma(warning(disable : 869)) /* unreferenced parameter */ \
__pragma(warning(disable : 1011)) /* missing return (false-positive) */ \
__pragma(warning(disable : 2261)) /* assume expr side-effects discarded */ \
static_assert(true)
#define TOML_POP_WARNINGS \
__pragma(warning(pop)) \
static_assert(true)
#define TOML_DISABLE_WARNINGS \
__pragma(warning(push, 0)) \
TOML_DISABLE_SPAM_WARNINGS
#define TOML_ENABLE_WARNINGS \
__pragma(warning(pop)) \
static_assert(true)
#elif TOML_GCC
#define TOML_PUSH_WARNINGS \
TOML_PRAGMA_GCC(diagnostic push) \
static_assert(true)
#define TOML_DISABLE_SWITCH_WARNINGS \
TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch-enum") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch-default") \
static_assert(true)
#define TOML_DISABLE_ARITHMETIC_WARNINGS \
TOML_PRAGMA_GCC(diagnostic ignored "-Wfloat-equal") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wsign-conversion") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wchar-subscripts") \
static_assert(true)
#define TOML_DISABLE_SUGGEST_ATTR_WARNINGS \
TOML_PRAGMA_GCC(diagnostic ignored "-Wsuggest-attribute=const") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wsuggest-attribute=pure") \
static_assert(true)
#define TOML_DISABLE_SPAM_WARNINGS \
TOML_PRAGMA_GCC(diagnostic ignored "-Wpadded") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wcast-align") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wcomment") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wtype-limits") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wuseless-cast") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wchar-subscripts") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wsubobject-linkage") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wmissing-field-initializers") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wmaybe-uninitialized") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wnoexcept") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wnull-dereference") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wduplicated-branches") \
static_assert(true)
#define TOML_POP_WARNINGS \
TOML_PRAGMA_GCC(diagnostic pop) \
static_assert(true)
#define TOML_DISABLE_WARNINGS \
TOML_PRAGMA_GCC(diagnostic push) \
TOML_PRAGMA_GCC(diagnostic ignored "-Wall") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wextra") \
TOML_PRAGMA_GCC(diagnostic ignored "-Wpedantic") \
TOML_DISABLE_SWITCH_WARNINGS; \
TOML_DISABLE_ARITHMETIC_WARNINGS; \
TOML_DISABLE_SUGGEST_ATTR_WARNINGS; \
TOML_DISABLE_SPAM_WARNINGS; \
static_assert(true)
#define TOML_ENABLE_WARNINGS \
TOML_PRAGMA_GCC(diagnostic pop) \
static_assert(true)
#endif
#ifndef TOML_PUSH_WARNINGS
#define TOML_PUSH_WARNINGS static_assert(true)
#endif
#ifndef TOML_DISABLE_CODE_ANALYSIS_WARNINGS
#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true)
#endif
#ifndef TOML_DISABLE_SWITCH_WARNINGS
#define TOML_DISABLE_SWITCH_WARNINGS static_assert(true)
#endif
#ifndef TOML_DISABLE_SUGGEST_ATTR_WARNINGS
#define TOML_DISABLE_SUGGEST_ATTR_WARNINGS static_assert(true)
#endif
#ifndef TOML_DISABLE_SPAM_WARNINGS
#define TOML_DISABLE_SPAM_WARNINGS static_assert(true)
#endif
#ifndef TOML_DISABLE_ARITHMETIC_WARNINGS
#define TOML_DISABLE_ARITHMETIC_WARNINGS static_assert(true)
#endif
#ifndef TOML_POP_WARNINGS
#define TOML_POP_WARNINGS static_assert(true)
#endif
#ifndef TOML_DISABLE_WARNINGS
#define TOML_DISABLE_WARNINGS static_assert(true)
#endif
#ifndef TOML_ENABLE_WARNINGS
#define TOML_ENABLE_WARNINGS static_assert(true)
#endif
#ifndef TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#define TOML_SIMPLE_STATIC_ASSERT_MESSAGES 0
#endif
#ifdef TOML_CONFIG_HEADER
#include TOML_CONFIG_HEADER
#endif
// is the library being built as a shared lib/dll using meson and friends?
#ifndef TOML_SHARED_LIB
#define TOML_SHARED_LIB 0
#endif
// header-only mode
#if !defined(TOML_HEADER_ONLY) && defined(TOML_ALL_INLINE) // was TOML_ALL_INLINE pre-2.0
#define TOML_HEADER_ONLY TOML_ALL_INLINE
#endif
#if !defined(TOML_HEADER_ONLY) || (defined(TOML_HEADER_ONLY) && TOML_HEADER_ONLY) || TOML_INTELLISENSE
#undef TOML_HEADER_ONLY
#define TOML_HEADER_ONLY 1
#endif
#if TOML_DOXYGEN || TOML_SHARED_LIB
#undef TOML_HEADER_ONLY
#define TOML_HEADER_ONLY 0
#endif
// internal implementation switch
#if defined(TOML_IMPLEMENTATION) || TOML_HEADER_ONLY
#undef TOML_IMPLEMENTATION
#define TOML_IMPLEMENTATION 1
#else
#define TOML_IMPLEMENTATION 0
#endif
// dll/shared lib function exports (legacy - TOML_API was the old name for this setting)
#if !defined(TOML_EXPORTED_MEMBER_FUNCTION) && !defined(TOML_EXPORTED_STATIC_FUNCTION) \
&& !defined(TOML_EXPORTED_FREE_FUNCTION) && !defined(TOML_EXPORTED_CLASS) && defined(TOML_API)
#define TOML_EXPORTED_MEMBER_FUNCTION TOML_API
#define TOML_EXPORTED_STATIC_FUNCTION TOML_API
#define TOML_EXPORTED_FREE_FUNCTION TOML_API
#endif
// dll/shared lib exports
#if TOML_SHARED_LIB
#undef TOML_API
#undef TOML_EXPORTED_CLASS
#undef TOML_EXPORTED_MEMBER_FUNCTION
#undef TOML_EXPORTED_STATIC_FUNCTION
#undef TOML_EXPORTED_FREE_FUNCTION
#if TOML_WINDOWS
#if TOML_IMPLEMENTATION
#define TOML_EXPORTED_CLASS __declspec(dllexport)
#define TOML_EXPORTED_FREE_FUNCTION __declspec(dllexport)
#else
#define TOML_EXPORTED_CLASS __declspec(dllimport)
#define TOML_EXPORTED_FREE_FUNCTION __declspec(dllimport)
#endif
#ifndef TOML_CALLCONV
#define TOML_CALLCONV __cdecl
#endif
#elif defined(__GNUC__) && __GNUC__ >= 4
#define TOML_EXPORTED_CLASS __attribute__((visibility("default")))
#define TOML_EXPORTED_MEMBER_FUNCTION __attribute__((visibility("default")))
#define TOML_EXPORTED_STATIC_FUNCTION __attribute__((visibility("default")))
#define TOML_EXPORTED_FREE_FUNCTION __attribute__((visibility("default")))
#endif
#endif
#ifndef TOML_EXPORTED_CLASS
#define TOML_EXPORTED_CLASS
#endif
#ifndef TOML_EXPORTED_MEMBER_FUNCTION
#define TOML_EXPORTED_MEMBER_FUNCTION
#endif
#ifndef TOML_EXPORTED_STATIC_FUNCTION
#define TOML_EXPORTED_STATIC_FUNCTION
#endif
#ifndef TOML_EXPORTED_FREE_FUNCTION
#define TOML_EXPORTED_FREE_FUNCTION
#endif
// experimental language features
#if !defined(TOML_ENABLE_UNRELEASED_FEATURES) && defined(TOML_UNRELEASED_FEATURES) // was TOML_UNRELEASED_FEATURES
// pre-3.0
#define TOML_ENABLE_UNRELEASED_FEATURES TOML_UNRELEASED_FEATURES
#endif
#if (defined(TOML_ENABLE_UNRELEASED_FEATURES) && TOML_ENABLE_UNRELEASED_FEATURES) || TOML_INTELLISENSE
#undef TOML_ENABLE_UNRELEASED_FEATURES
#define TOML_ENABLE_UNRELEASED_FEATURES 1
#endif
#ifndef TOML_ENABLE_UNRELEASED_FEATURES
#define TOML_ENABLE_UNRELEASED_FEATURES 0
#endif
// parser
#if !defined(TOML_ENABLE_PARSER) && defined(TOML_PARSER) // was TOML_PARSER pre-3.0
#define TOML_ENABLE_PARSER TOML_PARSER
#endif
#if !defined(TOML_ENABLE_PARSER) || (defined(TOML_ENABLE_PARSER) && TOML_ENABLE_PARSER) || TOML_INTELLISENSE
#undef TOML_ENABLE_PARSER
#define TOML_ENABLE_PARSER 1
#endif
// formatters
#if !defined(TOML_ENABLE_FORMATTERS) || (defined(TOML_ENABLE_FORMATTERS) && TOML_ENABLE_FORMATTERS) || TOML_INTELLISENSE
#undef TOML_ENABLE_FORMATTERS
#define TOML_ENABLE_FORMATTERS 1
#endif
// SIMD
#if !defined(TOML_ENABLE_SIMD) || (defined(TOML_ENABLE_SIMD) && TOML_ENABLE_SIMD) || TOML_INTELLISENSE
#undef TOML_ENABLE_SIMD
#define TOML_ENABLE_SIMD 1
#endif
// windows compat
#if !defined(TOML_ENABLE_WINDOWS_COMPAT) && defined(TOML_WINDOWS_COMPAT) // was TOML_WINDOWS_COMPAT pre-3.0
#define TOML_ENABLE_WINDOWS_COMPAT TOML_WINDOWS_COMPAT
#endif
#if !defined(TOML_ENABLE_WINDOWS_COMPAT) || (defined(TOML_ENABLE_WINDOWS_COMPAT) && TOML_ENABLE_WINDOWS_COMPAT) \
|| TOML_INTELLISENSE
#undef TOML_ENABLE_WINDOWS_COMPAT
#define TOML_ENABLE_WINDOWS_COMPAT 1
#endif
#if !TOML_WINDOWS
#undef TOML_ENABLE_WINDOWS_COMPAT
#define TOML_ENABLE_WINDOWS_COMPAT 0
#endif
#ifndef TOML_INCLUDE_WINDOWS_H
#define TOML_INCLUDE_WINDOWS_H 0
#endif
// custom optional
#ifdef TOML_OPTIONAL_TYPE
#define TOML_HAS_CUSTOM_OPTIONAL_TYPE 1
#else
#define TOML_HAS_CUSTOM_OPTIONAL_TYPE 0
#endif
// exceptions (library use)
#if TOML_COMPILER_HAS_EXCEPTIONS
#if !defined(TOML_EXCEPTIONS) || (defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS)
#undef TOML_EXCEPTIONS
#define TOML_EXCEPTIONS 1
#endif
#else
#if defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS
#error TOML_EXCEPTIONS was explicitly enabled but exceptions are disabled/unsupported by the compiler.
#endif
#undef TOML_EXCEPTIONS
#define TOML_EXCEPTIONS 0
#endif
// calling convention for static/free/friend functions
#ifndef TOML_CALLCONV
#define TOML_CALLCONV
#endif
#ifndef TOML_UNDEF_MACROS
#define TOML_UNDEF_MACROS 1
#endif
#ifndef TOML_MAX_NESTED_VALUES
#define TOML_MAX_NESTED_VALUES 256
// this refers to the depth of nested values, e.g. inline tables and arrays.
// 256 is crazy high! if you're hitting this limit with real input, TOML is probably the wrong tool for the job...
#endif
#ifdef TOML_CHAR_8_STRINGS
#if TOML_CHAR_8_STRINGS
#error TOML_CHAR_8_STRINGS was removed in toml++ 2.0.0; all value setters and getters now work with char8_t strings implicitly.
#endif
#endif
#ifdef TOML_LARGE_FILES
#if !TOML_LARGE_FILES
#error Support for !TOML_LARGE_FILES (i.e. 'small files') was removed in toml++ 3.0.0.
#endif
#endif
#ifndef TOML_LIFETIME_HOOKS
#define TOML_LIFETIME_HOOKS 0
#endif
#ifdef NDEBUG
#undef TOML_ASSERT
#define TOML_ASSERT(expr) static_assert(true)
#endif
#ifndef TOML_ASSERT
#ifndef assert
TOML_DISABLE_WARNINGS;
#include <cassert>
TOML_ENABLE_WARNINGS;
#endif
#define TOML_ASSERT(expr) assert(expr)
#endif
#ifdef NDEBUG
#define TOML_ASSERT_ASSUME(expr) TOML_ASSUME(expr)
#else
#define TOML_ASSERT_ASSUME(expr) TOML_ASSERT(expr)
#endif
#ifndef TOML_ENABLE_FLOAT16
#define TOML_ENABLE_FLOAT16 0
#endif
#if !defined(TOML_FLOAT_CHARCONV) && (TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL))
// not supported by any version of GCC or Clang as of 26/11/2020
// not supported by any version of ICC on Linux as of 11/01/2021
#define TOML_FLOAT_CHARCONV 0
#endif
#if !defined(TOML_INT_CHARCONV) && (defined(__EMSCRIPTEN__) || defined(__APPLE__))
// causes link errors on emscripten
// causes Mac OS SDK version errors on some versions of Apple Clang
#define TOML_INT_CHARCONV 0
#endif
#ifndef TOML_INT_CHARCONV
#define TOML_INT_CHARCONV 1
#endif
#ifndef TOML_FLOAT_CHARCONV
#define TOML_FLOAT_CHARCONV 1
#endif
#if (TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV) && !TOML_HAS_INCLUDE(<charconv>)
#undef TOML_INT_CHARCONV
#undef TOML_FLOAT_CHARCONV
#define TOML_INT_CHARCONV 0
#define TOML_FLOAT_CHARCONV 0
#endif
#if defined(__cpp_concepts) && __cpp_concepts >= 201907
#define TOML_REQUIRES(...) requires(__VA_ARGS__)
#else
#define TOML_REQUIRES(...)
#endif
#define TOML_ENABLE_IF(...) , typename std::enable_if<(__VA_ARGS__), int>::type = 0
#define TOML_CONSTRAINED_TEMPLATE(condition, ...) \
template <__VA_ARGS__ TOML_ENABLE_IF(condition)> \
TOML_REQUIRES(condition)
#define TOML_HIDDEN_CONSTRAINT(condition, ...) TOML_CONSTRAINED_TEMPLATE(condition, __VA_ARGS__)
#if defined(__SIZEOF_FLOAT128__) && defined(__FLT128_MANT_DIG__) && defined(__LDBL_MANT_DIG__) \
&& __FLT128_MANT_DIG__ > __LDBL_MANT_DIG__
#define TOML_FLOAT128 __float128
#endif
#ifdef __SIZEOF_INT128__
#define TOML_INT128 __int128_t
#define TOML_UINT128 __uint128_t
#endif
// clang-format off
//******** impl/version.h ********************************************************************************************
#define TOML_LIB_MAJOR 3
#define TOML_LIB_MINOR 3
#define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 1
#define TOML_LANG_MINOR 0
#define TOML_LANG_PATCH 0
//******** impl/preprocessor.h ***************************************************************************************
#define TOML_LIB_SINGLE_HEADER 1
#if TOML_ENABLE_UNRELEASED_FEATURES
#define TOML_LANG_EFFECTIVE_VERSION \
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1)
#else
#define TOML_LANG_EFFECTIVE_VERSION \
TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH)
#endif
#define TOML_LANG_HIGHER_THAN(major, minor, patch) \
(TOML_LANG_EFFECTIVE_VERSION > TOML_MAKE_VERSION(major, minor, patch))
#define TOML_LANG_AT_LEAST(major, minor, patch) \
(TOML_LANG_EFFECTIVE_VERSION >= TOML_MAKE_VERSION(major, minor, patch))
#define TOML_LANG_UNRELEASED \
TOML_LANG_HIGHER_THAN(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH)
#ifndef TOML_ABI_NAMESPACES
#if TOML_DOXYGEN
#define TOML_ABI_NAMESPACES 0
#else
#define TOML_ABI_NAMESPACES 1
#endif
#endif
#if TOML_ABI_NAMESPACES
#define TOML_NAMESPACE_START namespace toml { inline namespace TOML_CONCAT(v, TOML_LIB_MAJOR)
#define TOML_NAMESPACE_END } static_assert(true)
#define TOML_NAMESPACE ::toml::TOML_CONCAT(v, TOML_LIB_MAJOR)
#define TOML_ABI_NAMESPACE_START(name) inline namespace name { static_assert(true)
#define TOML_ABI_NAMESPACE_BOOL(cond, T, F) TOML_ABI_NAMESPACE_START(TOML_CONCAT(TOML_EVAL_BOOL_, cond)(T, F))
#define TOML_ABI_NAMESPACE_END } static_assert(true)
#else
#define TOML_NAMESPACE_START namespace toml
#define TOML_NAMESPACE_END static_assert(true)
#define TOML_NAMESPACE toml
#define TOML_ABI_NAMESPACE_START(...) static_assert(true)
#define TOML_ABI_NAMESPACE_BOOL(...) static_assert(true)
#define TOML_ABI_NAMESPACE_END static_assert(true)
#endif
#define TOML_IMPL_NAMESPACE_START TOML_NAMESPACE_START { namespace impl
#define TOML_IMPL_NAMESPACE_END } TOML_NAMESPACE_END
#if TOML_HEADER_ONLY
#define TOML_ANON_NAMESPACE_START static_assert(TOML_IMPLEMENTATION); TOML_IMPL_NAMESPACE_START
#define TOML_ANON_NAMESPACE_END TOML_IMPL_NAMESPACE_END
#define TOML_ANON_NAMESPACE TOML_NAMESPACE::impl
#define TOML_EXTERNAL_LINKAGE inline
#define TOML_INTERNAL_LINKAGE inline
#else
#define TOML_ANON_NAMESPACE_START static_assert(TOML_IMPLEMENTATION); \
using namespace toml; \
namespace
#define TOML_ANON_NAMESPACE_END static_assert(true)
#define TOML_ANON_NAMESPACE
#define TOML_EXTERNAL_LINKAGE
#define TOML_INTERNAL_LINKAGE static
#endif
// clang-format on
// clang-format off
#if TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#define TOML_SA_NEWLINE " "
#define TOML_SA_LIST_SEP ", "
#define TOML_SA_LIST_BEG " ("
#define TOML_SA_LIST_END ")"
#define TOML_SA_LIST_NEW " "
#define TOML_SA_LIST_NXT ", "
#else
#define TOML_SA_NEWLINE "\n| "
#define TOML_SA_LIST_SEP TOML_SA_NEWLINE " - "
#define TOML_SA_LIST_BEG TOML_SA_LIST_SEP
#define TOML_SA_LIST_END
#define TOML_SA_LIST_NEW TOML_SA_NEWLINE TOML_SA_NEWLINE
#define TOML_SA_LIST_NXT TOML_SA_LIST_NEW
#endif
#define TOML_SA_NATIVE_VALUE_TYPE_LIST \
TOML_SA_LIST_BEG "std::string" \
TOML_SA_LIST_SEP "int64_t" \
TOML_SA_LIST_SEP "double" \
TOML_SA_LIST_SEP "bool" \
TOML_SA_LIST_SEP "toml::date" \
TOML_SA_LIST_SEP "toml::time" \
TOML_SA_LIST_SEP "toml::date_time" \
TOML_SA_LIST_END
#define TOML_SA_NODE_TYPE_LIST \
TOML_SA_LIST_BEG "toml::table" \
TOML_SA_LIST_SEP "toml::array" \
TOML_SA_LIST_SEP "toml::value<std::string>" \
TOML_SA_LIST_SEP "toml::value<int64_t>" \
TOML_SA_LIST_SEP "toml::value<double>" \
TOML_SA_LIST_SEP "toml::value<bool>" \
TOML_SA_LIST_SEP "toml::value<toml::date>" \
TOML_SA_LIST_SEP "toml::value<toml::time>" \
TOML_SA_LIST_SEP "toml::value<toml::date_time>" \
TOML_SA_LIST_END
#define TOML_SA_UNWRAPPED_NODE_TYPE_LIST \
TOML_SA_LIST_NEW "A native TOML value type" \
TOML_SA_NATIVE_VALUE_TYPE_LIST \
\
TOML_SA_LIST_NXT "A TOML node type" \
TOML_SA_NODE_TYPE_LIST
// clang-format on
TOML_PUSH_WARNINGS;
TOML_DISABLE_SPAM_WARNINGS;
TOML_DISABLE_SWITCH_WARNINGS;
TOML_DISABLE_SUGGEST_ATTR_WARNINGS;
// misc warning false-positives
#if TOML_MSVC
#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch
#if TOML_SHARED_LIB
#pragma warning(disable : 4251) // dll exports for std lib types
#endif
#elif TOML_CLANG
TOML_PRAGMA_CLANG(diagnostic ignored "-Wheader-hygiene")
#if TOML_CLANG >= 12
TOML_PRAGMA_CLANG(diagnostic ignored "-Wc++20-extensions")
#endif
#if TOML_CLANG == 13
TOML_PRAGMA_CLANG(diagnostic ignored "-Wreserved-identifier")
#endif
#endif
//******** impl/std_new.h ********************************************************************************************
TOML_DISABLE_WARNINGS;
#include <new>
TOML_ENABLE_WARNINGS;
#if (!defined(__apple_build_version__) && TOML_CLANG >= 8) || TOML_GCC >= 7 || TOML_ICC >= 1910 || TOML_MSVC >= 1914
#define TOML_LAUNDER(x) __builtin_launder(x)
#elif defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
#define TOML_LAUNDER(x) std::launder(x)
#else
#define TOML_LAUNDER(x) x
#endif
//******** impl/std_string.h *****************************************************************************************
TOML_DISABLE_WARNINGS;
#include <string_view>
#include <string>
TOML_ENABLE_WARNINGS;
#if TOML_DOXYGEN \
|| (defined(__cpp_char8_t) && __cpp_char8_t >= 201811 && defined(__cpp_lib_char8_t) \
&& __cpp_lib_char8_t >= 201907)
#define TOML_HAS_CHAR8 1
#else
#define TOML_HAS_CHAR8 0
#endif
namespace toml // non-abi namespace; this is not an error
{
using namespace std::string_literals;
using namespace std::string_view_literals;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_IMPL_NAMESPACE_START
{
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
std::string narrow(std::wstring_view);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
std::wstring widen(std::string_view);
#if TOML_HAS_CHAR8
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
std::wstring widen(std::u8string_view);
#endif
}
TOML_IMPL_NAMESPACE_END;
#endif // TOML_ENABLE_WINDOWS_COMPAT
//******** impl/std_optional.h ***************************************************************************************
TOML_DISABLE_WARNINGS;
#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE
#include <optional>
#endif
TOML_ENABLE_WARNINGS;
TOML_NAMESPACE_START
{
#if TOML_HAS_CUSTOM_OPTIONAL_TYPE
template <typename T>
using optional = TOML_OPTIONAL_TYPE<T>;
#else
template <typename T>
using optional = std::optional<T>;
#endif
}
TOML_NAMESPACE_END;
//******** impl/forward_declarations.h *******************************************************************************
TOML_DISABLE_WARNINGS;
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <cfloat>
#include <climits>
#include <cmath>
#include <limits>
#include <memory>
#include <iosfwd>
#include <type_traits>
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
#ifndef TOML_DISABLE_ENVIRONMENT_CHECKS
#define TOML_ENV_MESSAGE \
"If you're seeing this error it's because you're building toml++ for an environment that doesn't conform to " \
"one of the 'ground truths' assumed by the library. Essentially this just means that I don't have the " \
"resources to test on more platforms, but I wish I did! You can try disabling the checks by defining " \
"TOML_DISABLE_ENVIRONMENT_CHECKS, but your mileage may vary. Please consider filing an issue at " \
"https://github.com/marzer/tomlplusplus/issues to help me improve support for your target environment. " \
"Thanks!"
static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE);
static_assert(FLT_RADIX == 2, TOML_ENV_MESSAGE);
static_assert('A' == 65, TOML_ENV_MESSAGE);
static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE);
static_assert(std::numeric_limits<double>::is_iec559, TOML_ENV_MESSAGE);
static_assert(std::numeric_limits<double>::digits == 53, TOML_ENV_MESSAGE);
static_assert(std::numeric_limits<double>::digits10 == 15, TOML_ENV_MESSAGE);
#undef TOML_ENV_MESSAGE
#endif // !TOML_DISABLE_ENVIRONMENT_CHECKS
// undocumented forward declarations are hidden from doxygen because they fuck it up =/
namespace toml // non-abi namespace; this is not an error
{
using ::std::size_t;
using ::std::intptr_t;
using ::std::uintptr_t;
using ::std::ptrdiff_t;
using ::std::nullptr_t;
using ::std::int8_t;
using ::std::int16_t;
using ::std::int32_t;
using ::std::int64_t;
using ::std::uint8_t;
using ::std::uint16_t;
using ::std::uint32_t;
using ::std::uint64_t;
using ::std::uint_least32_t;
using ::std::uint_least64_t;
}
TOML_NAMESPACE_START
{
struct date;
struct time;
struct time_offset;
TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt);
struct date_time;
TOML_ABI_NAMESPACE_END;
struct source_position;
struct source_region;
class node;
template <typename>
class node_view;
class key;
class array;
class table;
template <typename>
class value;
class path;
class toml_formatter;
class json_formatter;
class yaml_formatter;
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
#if TOML_EXCEPTIONS
using parse_result = table;
#else
class parse_result;
#endif
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
using node_ptr = std::unique_ptr<node>;
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex);
class parser;
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
// clang-format off
inline constexpr std::string_view control_char_escapes[] =
{
"\\u0000"sv,
"\\u0001"sv,
"\\u0002"sv,
"\\u0003"sv,
"\\u0004"sv,
"\\u0005"sv,
"\\u0006"sv,
"\\u0007"sv,
"\\b"sv,
"\\t"sv,
"\\n"sv,
"\\u000B"sv,
"\\f"sv,
"\\r"sv,
"\\u000E"sv,
"\\u000F"sv,
"\\u0010"sv,
"\\u0011"sv,
"\\u0012"sv,
"\\u0013"sv,
"\\u0014"sv,
"\\u0015"sv,
"\\u0016"sv,
"\\u0017"sv,
"\\u0018"sv,
"\\u0019"sv,
"\\u001A"sv,
"\\u001B"sv,
"\\u001C"sv,
"\\u001D"sv,
"\\u001E"sv,
"\\u001F"sv,
};
inline constexpr std::string_view node_type_friendly_names[] =
{
"none"sv,
"table"sv,
"array"sv,
"string"sv,
"integer"sv,
"floating-point"sv,
"boolean"sv,
"date"sv,
"time"sv,
"date-time"sv
};
// clang-format on
}
TOML_IMPL_NAMESPACE_END;
#if TOML_ABI_NAMESPACES
#if TOML_EXCEPTIONS
#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_ex::parser
#else
#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_noex::parser
#endif
#else
#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::parser
#endif
namespace toml
{
}
TOML_NAMESPACE_START // abi namespace
{
inline namespace literals
{
}
enum class TOML_CLOSED_ENUM node_type : uint8_t
{
none,
table,
array,
string,
integer,
floating_point,
boolean,
date,
time,
date_time
};
template <typename Char>
inline std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& lhs, node_type rhs)
{
const auto str = impl::node_type_friendly_names[static_cast<std::underlying_type_t<node_type>>(rhs)];
using str_char_t = decltype(str)::value_type;
if constexpr (std::is_same_v<Char, str_char_t>)
return lhs << str;
else
{
if constexpr (sizeof(Char) == sizeof(str_char_t))
return lhs << std::basic_string_view<Char>{ reinterpret_cast<const Char*>(str.data()), str.length() };
else
return lhs << str.data();
}
}
enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error
{
none,
format_as_binary = 1,
format_as_octal = 2,
format_as_hexadecimal = 3,
};
TOML_MAKE_FLAGS(value_flags);
inline constexpr value_flags preserve_source_value_flags =
POXY_IMPLEMENTATION_DETAIL(value_flags{ static_cast<std::underlying_type_t<value_flags>>(-1) });
enum class TOML_CLOSED_FLAGS_ENUM format_flags : uint64_t
{
none,
quote_dates_and_times = (1ull << 0),
quote_infinities_and_nans = (1ull << 1),
allow_literal_strings = (1ull << 2),
allow_multi_line_strings = (1ull << 3),
allow_real_tabs_in_strings = (1ull << 4),
allow_unicode_strings = (1ull << 5),
allow_binary_integers = (1ull << 6),
allow_octal_integers = (1ull << 7),
allow_hexadecimal_integers = (1ull << 8),
indent_sub_tables = (1ull << 9),
indent_array_elements = (1ull << 10),
indentation = indent_sub_tables | indent_array_elements,
relaxed_float_precision = (1ull << 11),
terse_key_value_pairs = (1ull << 12),
};
TOML_MAKE_FLAGS(format_flags);
template <typename T>
struct TOML_TRIVIAL_ABI inserter
{
static_assert(std::is_reference_v<T>);
T value;
};
template <typename T>
inserter(T&&) -> inserter<T&&>;
template <typename T>
inserter(T&) -> inserter<T&>;
using default_formatter = toml_formatter;
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
template <typename T>
using remove_cvref = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename... T>
using common_signed_type = std::common_type_t<std::make_signed_t<T>...>;
template <typename T, typename... U>
inline constexpr bool is_one_of = (false || ... || std::is_same_v<T, U>);
template <typename... T>
inline constexpr bool all_integral = (std::is_integral_v<T> && ...);
template <typename T>
inline constexpr bool is_cvref = std::is_reference_v<T> || std::is_const_v<T> || std::is_volatile_v<T>;
template <typename T>
inline constexpr bool is_wide_string =
is_one_of<std::decay_t<T>, const wchar_t*, wchar_t*, std::wstring_view, std::wstring>;
template <typename T>
inline constexpr bool value_retrieval_is_nothrow = !std::is_same_v<remove_cvref<T>, std::string>
#if TOML_HAS_CHAR8
&& !std::is_same_v<remove_cvref<T>, std::u8string>
#endif
&& !is_wide_string<T>;
template <typename, typename>
struct copy_ref_;
template <typename Dest, typename Src>
using copy_ref = typename copy_ref_<Dest, Src>::type;
template <typename Dest, typename Src>
struct copy_ref_
{
using type = Dest;
};
template <typename Dest, typename Src>
struct copy_ref_<Dest, Src&>
{
using type = std::add_lvalue_reference_t<Dest>;
};
template <typename Dest, typename Src>
struct copy_ref_<Dest, Src&&>
{
using type = std::add_rvalue_reference_t<Dest>;
};
template <typename, typename>
struct copy_cv_;
template <typename Dest, typename Src>
using copy_cv = typename copy_cv_<Dest, Src>::type;
template <typename Dest, typename Src>
struct copy_cv_
{
using type = Dest;
};
template <typename Dest, typename Src>
struct copy_cv_<Dest, const Src>
{
using type = std::add_const_t<Dest>;
};
template <typename Dest, typename Src>
struct copy_cv_<Dest, volatile Src>
{
using type = std::add_volatile_t<Dest>;
};
template <typename Dest, typename Src>
struct copy_cv_<Dest, const volatile Src>
{
using type = std::add_cv_t<Dest>;
};
template <typename Dest, typename Src>
using copy_cvref =
copy_ref<copy_ref<copy_cv<std::remove_reference_t<Dest>, std::remove_reference_t<Src>>, Dest>, Src>;
template <typename T>
inline constexpr bool dependent_false = false;
template <typename T, typename... U>
inline constexpr bool first_is_same = false;
template <typename T, typename... U>
inline constexpr bool first_is_same<T, T, U...> = true;
// general value traits
// (as they relate to their equivalent native TOML type)
template <typename T>
struct value_traits
{
using native_type = void;
static constexpr bool is_native = false;
static constexpr bool is_losslessly_convertible_to_native = false;
static constexpr bool can_represent_native = false;
static constexpr bool can_partially_represent_native = false;
static constexpr auto type = node_type::none;
};
template <typename T>
struct value_traits<const T> : value_traits<T>
{};
template <typename T>
struct value_traits<volatile T> : value_traits<T>
{};
template <typename T>
struct value_traits<const volatile T> : value_traits<T>
{};
template <typename T>
struct value_traits<T&> : value_traits<T>
{};
template <typename T>
struct value_traits<T&&> : value_traits<T>
{};
// integer value_traits specializations - standard types
template <typename T>
struct integer_limits
{
static constexpr auto min = (std::numeric_limits<T>::min)();
static constexpr auto max = (std::numeric_limits<T>::max)();
};
template <typename T>
struct integer_traits_base : integer_limits<T>
{
using native_type = int64_t;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = static_cast<T>(-1) < T{}; // for impls not specializing std::is_signed<T>
static constexpr auto type = node_type::integer;
static constexpr bool can_partially_represent_native = true;
};
template <typename T>
struct unsigned_integer_traits : integer_traits_base<T>
{
static constexpr bool is_losslessly_convertible_to_native = integer_limits<T>::max <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
};
template <typename T>
struct signed_integer_traits : integer_traits_base<T>
{
using native_type = int64_t;
static constexpr bool is_losslessly_convertible_to_native =
integer_limits<T>::min >= (-9223372036854775807LL - 1LL) && integer_limits<T>::max <= 9223372036854775807LL;
static constexpr bool can_represent_native =
integer_limits<T>::min <= (-9223372036854775807LL - 1LL) && integer_limits<T>::max >= 9223372036854775807LL;
};
template <typename T, bool S = integer_traits_base<T>::is_signed>
struct integer_traits : signed_integer_traits<T>
{};
template <typename T>
struct integer_traits<T, false> : unsigned_integer_traits<T>
{};
template <>
struct value_traits<signed char> : integer_traits<signed char>
{};
template <>
struct value_traits<unsigned char> : integer_traits<unsigned char>
{};
template <>
struct value_traits<signed short> : integer_traits<signed short>
{};
template <>
struct value_traits<unsigned short> : integer_traits<unsigned short>
{};
template <>
struct value_traits<signed int> : integer_traits<signed int>
{};
template <>
struct value_traits<unsigned int> : integer_traits<unsigned int>
{};
template <>
struct value_traits<signed long> : integer_traits<signed long>
{};
template <>
struct value_traits<unsigned long> : integer_traits<unsigned long>
{};
template <>
struct value_traits<signed long long> : integer_traits<signed long long>
{};
template <>
struct value_traits<unsigned long long> : integer_traits<unsigned long long>
{};
static_assert(value_traits<int64_t>::is_native);
static_assert(value_traits<int64_t>::is_signed);
static_assert(value_traits<int64_t>::is_losslessly_convertible_to_native);
static_assert(value_traits<int64_t>::can_represent_native);
static_assert(value_traits<int64_t>::can_partially_represent_native);
// integer value_traits specializations - non-standard types
#ifdef TOML_INT128
template <>
struct integer_limits<TOML_INT128>
{
static constexpr TOML_INT128 max =
static_cast<TOML_INT128>((TOML_UINT128{ 1u } << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1);
static constexpr TOML_INT128 min = -max - TOML_INT128{ 1 };
};
template <>
struct integer_limits<TOML_UINT128>
{
static constexpr TOML_UINT128 min = TOML_UINT128{};
static constexpr TOML_UINT128 max = (2u * static_cast<TOML_UINT128>(integer_limits<TOML_INT128>::max)) + 1u;
};
template <>
struct value_traits<TOML_INT128> : integer_traits<TOML_INT128>
{};
template <>
struct value_traits<TOML_UINT128> : integer_traits<TOML_UINT128>
{};
#endif
#ifdef TOML_SMALL_INT_TYPE
template <>
struct value_traits<TOML_SMALL_INT_TYPE> : signed_integer_traits<TOML_SMALL_INT_TYPE>
{};
#endif
// floating-point traits base
template <typename T, int MantissaDigits, int DecimalDigits>
struct float_traits_base
{
static constexpr auto type = node_type::floating_point;
using native_type = double;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = true;
static constexpr int bits = static_cast<int>(sizeof(T) * CHAR_BIT);
static constexpr int digits = MantissaDigits;
static constexpr int digits10 = DecimalDigits;
static constexpr bool is_losslessly_convertible_to_native = bits <= 64 //
&& digits <= 53 // DBL_MANT_DIG
&& digits10 <= 15; // DBL_DIG
static constexpr bool can_represent_native = digits >= 53 // DBL_MANT_DIG
&& digits10 >= 15; // DBL_DIG
static constexpr bool can_partially_represent_native = digits > 0 && digits10 > 0;
};
template <typename T>
struct float_traits : float_traits_base<T, std::numeric_limits<T>::digits, std::numeric_limits<T>::digits10>
{};
#if TOML_ENABLE_FLOAT16
template <>
struct float_traits<_Float16> : float_traits_base<_Float16, __FLT16_MANT_DIG__, __FLT16_DIG__>
{};
#endif
#ifdef TOML_FLOAT128
template <>
struct float_traits<TOML_FLOAT128> : float_traits_base<TOML_FLOAT128, __FLT128_MANT_DIG__, __FLT128_DIG__>
{};
#endif
// floating-point traits
template <>
struct value_traits<float> : float_traits<float>
{};
template <>
struct value_traits<double> : float_traits<double>
{};
template <>
struct value_traits<long double> : float_traits<long double>
{};
#if TOML_ENABLE_FLOAT16
template <>
struct value_traits<_Float16> : float_traits<_Float16>
{};
#endif
#ifdef TOML_FLOAT128
template <>
struct value_traits<TOML_FLOAT128> : float_traits<TOML_FLOAT128>
{};
#endif
#ifdef TOML_SMALL_FLOAT_TYPE
template <>
struct value_traits<TOML_SMALL_FLOAT_TYPE> : float_traits<TOML_SMALL_FLOAT_TYPE>
{};
#endif
static_assert(value_traits<double>::is_native);
static_assert(value_traits<double>::is_losslessly_convertible_to_native);
static_assert(value_traits<double>::can_represent_native);
static_assert(value_traits<double>::can_partially_represent_native);
// string value_traits specializations - char-based strings
template <typename T>
struct string_traits
{
using native_type = std::string;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_losslessly_convertible_to_native = true;
static constexpr bool can_represent_native =
!std::is_array_v<T> && (!std::is_pointer_v<T> || std::is_const_v<std::remove_pointer_t<T>>);
static constexpr bool can_partially_represent_native = can_represent_native;
static constexpr auto type = node_type::string;
};
template <>
struct value_traits<std::string> : string_traits<std::string>
{};
template <>
struct value_traits<std::string_view> : string_traits<std::string_view>
{};
template <>
struct value_traits<const char*> : string_traits<const char*>
{};
template <size_t N>
struct value_traits<const char[N]> : string_traits<const char[N]>
{};
template <>
struct value_traits<char*> : string_traits<char*>
{};
template <size_t N>
struct value_traits<char[N]> : string_traits<char[N]>
{};
// string value_traits specializations - char8_t-based strings
#if TOML_HAS_CHAR8
template <>
struct value_traits<std::u8string> : string_traits<std::u8string>
{};
template <>
struct value_traits<std::u8string_view> : string_traits<std::u8string_view>
{};
template <>
struct value_traits<const char8_t*> : string_traits<const char8_t*>
{};
template <size_t N>
struct value_traits<const char8_t[N]> : string_traits<const char8_t[N]>
{};
template <>
struct value_traits<char8_t*> : string_traits<char8_t*>
{};
template <size_t N>
struct value_traits<char8_t[N]> : string_traits<char8_t[N]>
{};
#endif
// string value_traits specializations - wchar_t-based strings on Windows
#if TOML_ENABLE_WINDOWS_COMPAT
template <typename T>
struct wstring_traits
{
using native_type = std::string;
static constexpr bool is_native = false;
static constexpr bool is_losslessly_convertible_to_native = true; // narrow
static constexpr bool can_represent_native = std::is_same_v<T, std::wstring>; // widen
static constexpr bool can_partially_represent_native = can_represent_native;
static constexpr auto type = node_type::string;
};
template <>
struct value_traits<std::wstring> : wstring_traits<std::wstring>
{};
template <>
struct value_traits<std::wstring_view> : wstring_traits<std::wstring_view>
{};
template <>
struct value_traits<const wchar_t*> : wstring_traits<const wchar_t*>
{};
template <size_t N>
struct value_traits<const wchar_t[N]> : wstring_traits<const wchar_t[N]>
{};
template <>
struct value_traits<wchar_t*> : wstring_traits<wchar_t*>
{};
template <size_t N>
struct value_traits<wchar_t[N]> : wstring_traits<wchar_t[N]>
{};
#endif
// other 'native' value_traits specializations
template <typename T, node_type NodeType>
struct native_value_traits
{
using native_type = T;
static constexpr bool is_native = true;
static constexpr bool is_losslessly_convertible_to_native = true;
static constexpr bool can_represent_native = true;
static constexpr bool can_partially_represent_native = true;
static constexpr auto type = NodeType;
};
template <>
struct value_traits<bool> : native_value_traits<bool, node_type::boolean>
{};
template <>
struct value_traits<date> : native_value_traits<date, node_type::date>
{};
template <>
struct value_traits<time> : native_value_traits<time, node_type::time>
{};
template <>
struct value_traits<date_time> : native_value_traits<date_time, node_type::date_time>
{};
// native value category queries
template <typename T>
using native_type_of = typename value_traits<T>::native_type;
template <typename T>
inline constexpr bool is_native = value_traits<T>::is_native;
template <typename T>
inline constexpr bool can_represent_native = value_traits<T>::can_represent_native;
template <typename T>
inline constexpr bool can_partially_represent_native = value_traits<T>::can_partially_represent_native;
template <typename T>
inline constexpr bool is_losslessly_convertible_to_native = value_traits<T>::is_losslessly_convertible_to_native;
template <typename T, typename... U>
inline constexpr bool is_natively_one_of = is_one_of<native_type_of<T>, U...>;
// native value types => nodes
template <typename T>
struct node_wrapper
{
using type = T;
};
template <typename T>
struct node_wrapper<const T>
{
using type = std::add_const_t<typename node_wrapper<T>::type>;
};
template <typename T>
struct node_wrapper<volatile T>
{
using type = std::add_volatile_t<typename node_wrapper<T>::type>;
};
template <typename T>
struct node_wrapper<const volatile T>
{
using type = std::add_const_t<std::add_volatile_t<typename node_wrapper<T>::type>>;
};
template <>
struct node_wrapper<std::string>
{
using type = value<std::string>;
};
template <>
struct node_wrapper<int64_t>
{
using type = value<int64_t>;
};
template <>
struct node_wrapper<double>
{
using type = value<double>;
};
template <>
struct node_wrapper<bool>
{
using type = value<bool>;
};
template <>
struct node_wrapper<date>
{
using type = value<date>;
};
template <>
struct node_wrapper<time>
{
using type = value<time>;
};
template <>
struct node_wrapper<date_time>
{
using type = value<date_time>;
};
template <typename T>
using wrap_node = typename node_wrapper<std::remove_reference_t<T>>::type;
// nodes => native value types
template <typename T>
struct node_unwrapper
{
using type = T;
};
template <typename T>
struct node_unwrapper<value<T>>
{
using type = T;
};
template <typename T>
struct node_unwrapper<const value<T>>
{
using type = std::add_const_t<T>;
};
template <typename T>
struct node_unwrapper<volatile value<T>>
{
using type = std::add_volatile_t<T>;
};
template <typename T>
struct node_unwrapper<const volatile value<T>>
{
using type = std::add_volatile_t<std::add_const_t<T>>;
};
template <typename T>
using unwrap_node = typename node_unwrapper<std::remove_reference_t<T>>::type;
template <typename T>
struct node_type_getter
{
static constexpr auto value = value_traits<T>::type;
};
template <>
struct node_type_getter<table>
{
static constexpr auto value = node_type::table;
};
template <>
struct node_type_getter<array>
{
static constexpr auto value = node_type::array;
};
template <>
struct node_type_getter<void>
{
static constexpr auto value = node_type::none;
};
template <typename T>
inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref<T>>>::value;
template <typename T, typename ConvertFrom>
inline constexpr bool is_constructible_or_convertible = std::is_constructible_v<T, ConvertFrom> //
|| std::is_convertible_v<ConvertFrom, T>;
}
TOML_IMPL_NAMESPACE_END;
TOML_NAMESPACE_START
{
template <typename T>
inline constexpr bool is_table = std::is_same_v<impl::remove_cvref<T>, table>;
template <typename T>
inline constexpr bool is_array = std::is_same_v<impl::remove_cvref<T>, array>;
template <typename T>
inline constexpr bool is_container = is_table<T> || is_array<T>;
template <typename T>
inline constexpr bool is_string = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<std::string>>;
template <typename T>
inline constexpr bool is_integer = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<int64_t>>;
template <typename T>
inline constexpr bool is_floating_point = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<double>>;
template <typename T>
inline constexpr bool is_number = is_integer<T> || is_floating_point<T>;
template <typename T>
inline constexpr bool is_boolean = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<bool>>;
template <typename T>
inline constexpr bool is_date = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<date>>;
template <typename T>
inline constexpr bool is_time = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<time>>;
template <typename T>
inline constexpr bool is_date_time = std::is_same_v< //
impl::remove_cvref<impl::wrap_node<impl::remove_cvref<T>>>, //
value<date_time>>;
template <typename T>
inline constexpr bool is_chronological = is_date<T> || is_time<T> || is_date_time<T>;
template <typename T>
inline constexpr bool is_value = is_string<T> || is_number<T> || is_boolean<T> || is_chronological<T>;
template <typename T>
inline constexpr bool is_node = std::is_same_v<toml::node, impl::remove_cvref<T>> //
|| std::is_base_of_v<toml::node, impl::remove_cvref<T>>;
template <typename T>
inline constexpr bool is_node_view = impl::is_one_of<impl::remove_cvref<T>, node_view<node>, node_view<const node>>;
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
template <typename T>
TOML_CONST_INLINE_GETTER
constexpr std::underlying_type_t<T> unwrap_enum(T val) noexcept
{
return static_cast<std::underlying_type_t<T>>(val);
}
// Q: "why not use std::fpclassify?"
// A: Because it gets broken by -ffast-math and friends
enum class TOML_CLOSED_ENUM fp_class : unsigned
{
ok,
neg_inf,
pos_inf,
nan
};
TOML_PURE_GETTER
inline fp_class fpclassify(const double& val) noexcept
{
static_assert(sizeof(uint64_t) == sizeof(double));
static constexpr uint64_t sign = 0b1000000000000000000000000000000000000000000000000000000000000000ull;
static constexpr uint64_t exponent = 0b0111111111110000000000000000000000000000000000000000000000000000ull;
static constexpr uint64_t mantissa = 0b0000000000001111111111111111111111111111111111111111111111111111ull;
uint64_t val_bits;
std::memcpy(&val_bits, &val, sizeof(val));
if ((val_bits & exponent) != exponent)
return fp_class::ok;
if ((val_bits & mantissa))
return fp_class::nan;
return (val_bits & sign) ? fp_class::neg_inf : fp_class::pos_inf;
}
// Q: "why not use std::find and std::min?"
// A: Because <algorithm> is _huge_ and these would be the only things I used from it.
// I don't want to impose such a heavy compile-time burden on users.
template <typename Iterator, typename T>
TOML_PURE_GETTER
inline auto find(Iterator start, Iterator end, const T& needle) noexcept //
->decltype(&(*start))
{
for (; start != end; start++)
if (*start == needle)
return &(*start);
return nullptr;
}
template <typename T>
TOML_PURE_INLINE_GETTER
constexpr const T& min(const T& a, const T& b) noexcept //
{
return a < b ? a : b;
}
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/print_to_stream.h ************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
// Q: "why does print_to_stream() exist? why not just use ostream::write(), ostream::put() etc?"
// A: - I'm using <charconv> to format numerics. Faster and locale-independent.
// - I can (potentially) avoid forcing users to drag in <sstream> and <iomanip>.
// - Strings in C++. Honestly.
TOML_EXPORTED_FREE_FUNCTION
TOML_ATTR(nonnull)
void TOML_CALLCONV print_to_stream(std::ostream&, const char*, size_t);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, std::string_view);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const std::string&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, char);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, signed char, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, signed short, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, signed int, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, signed long, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, signed long long, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned char, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned short, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned int, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned long, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, unsigned long long, value_flags = {}, size_t min_digits = 0);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, float, value_flags = {}, bool relaxed_precision = false);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, double, value_flags = {}, bool relaxed_precision = false);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, bool);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::date&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::time&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::time_offset&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const toml::date_time&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const source_position&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const source_region&);
#if TOML_ENABLE_FORMATTERS
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const array&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const table&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<std::string>&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<int64_t>&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<double>&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<bool>&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<date>&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<time>&);
TOML_EXPORTED_FREE_FUNCTION
void TOML_CALLCONV print_to_stream(std::ostream&, const value<date_time>&);
#endif
template <typename T, typename U>
inline void print_to_stream_bookended(std::ostream & stream, const T& val, const U& bookend)
{
print_to_stream(stream, bookend);
print_to_stream(stream, val);
print_to_stream(stream, bookend);
}
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/source_region.h **************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
using source_index = uint32_t;
using source_path_ptr = std::shared_ptr<const std::string>;
struct TOML_TRIVIAL_ABI source_position
{
source_index line;
source_index column;
TOML_PURE_GETTER
explicit constexpr operator bool() const noexcept
{
return line > source_index{} //
&& column > source_index{};
}
TOML_PURE_GETTER
friend constexpr bool operator==(const source_position& lhs, const source_position& rhs) noexcept
{
return lhs.line == rhs.line //
&& lhs.column == rhs.column;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator!=(const source_position& lhs, const source_position& rhs) noexcept
{
return !(lhs == rhs);
}
private:
TOML_PURE_GETTER
static constexpr uint64_t pack(const source_position& pos) noexcept
{
return static_cast<uint64_t>(pos.line) << 32 | static_cast<uint64_t>(pos.column);
}
public:
TOML_PURE_GETTER
friend constexpr bool operator<(const source_position& lhs, const source_position& rhs) noexcept
{
return pack(lhs) < pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator<=(const source_position& lhs, const source_position& rhs) noexcept
{
return pack(lhs) <= pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator>(const source_position& lhs, const source_position& rhs) noexcept
{
return pack(lhs) > pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator>=(const source_position& lhs, const source_position& rhs) noexcept
{
return pack(lhs) >= pack(rhs);
}
friend std::ostream& operator<<(std::ostream& lhs, const source_position& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
};
struct source_region
{
source_position begin;
source_position end;
source_path_ptr path;
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
optional<std::wstring> wide_path() const
{
if (!path || path->empty())
return {};
return { impl::widen(*path) };
}
#endif
friend std::ostream& operator<<(std::ostream& lhs, const source_region& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
};
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/date_time.h ******************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
struct TOML_TRIVIAL_ABI date
{
uint16_t year;
uint8_t month;
uint8_t day;
TOML_NODISCARD_CTOR
date() noexcept = default;
TOML_CONSTRAINED_TEMPLATE((impl::all_integral<Y, M, D>), typename Y, typename M, typename D)
TOML_NODISCARD_CTOR
constexpr date(Y y, M m, D d) noexcept //
: year{ static_cast<uint16_t>(y) },
month{ static_cast<uint8_t>(m) },
day{ static_cast<uint8_t>(d) }
{}
TOML_PURE_GETTER
friend constexpr bool operator==(const date& lhs, const date& rhs) noexcept
{
return lhs.year == rhs.year //
&& lhs.month == rhs.month //
&& lhs.day == rhs.day;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator!=(const date& lhs, const date& rhs) noexcept
{
return !(lhs == rhs);
}
private:
TOML_PURE_GETTER
static constexpr uint32_t pack(const date& d) noexcept
{
return (static_cast<uint32_t>(d.year) << 16) | (static_cast<uint32_t>(d.month) << 8)
| static_cast<uint32_t>(d.day);
}
public:
TOML_PURE_GETTER
friend constexpr bool operator<(const date& lhs, const date& rhs) noexcept
{
return pack(lhs) < pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator<=(const date& lhs, const date& rhs) noexcept
{
return pack(lhs) <= pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator>(const date& lhs, const date& rhs) noexcept
{
return pack(lhs) > pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator>=(const date& lhs, const date& rhs) noexcept
{
return pack(lhs) >= pack(rhs);
}
friend std::ostream& operator<<(std::ostream& lhs, const date& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
};
struct TOML_TRIVIAL_ABI time
{
uint8_t hour;
uint8_t minute;
uint8_t second;
uint32_t nanosecond;
TOML_NODISCARD_CTOR
time() noexcept = default;
TOML_CONSTRAINED_TEMPLATE((impl::all_integral<H, M, S, NS>),
typename H,
typename M,
typename S = uint8_t,
typename NS = uint32_t)
TOML_NODISCARD_CTOR
constexpr time(H h, M m, S s = S{}, NS ns = NS{}) noexcept //
: hour{ static_cast<uint8_t>(h) },
minute{ static_cast<uint8_t>(m) },
second{ static_cast<uint8_t>(s) },
nanosecond{ static_cast<uint32_t>(ns) }
{}
TOML_PURE_GETTER
friend constexpr bool operator==(const time& lhs, const time& rhs) noexcept
{
return lhs.hour == rhs.hour //
&& lhs.minute == rhs.minute //
&& lhs.second == rhs.second //
&& lhs.nanosecond == rhs.nanosecond;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator!=(const time& lhs, const time& rhs) noexcept
{
return !(lhs == rhs);
}
private:
TOML_PURE_GETTER
static constexpr uint64_t pack(const time& t) noexcept
{
return static_cast<uint64_t>(t.hour) << 48 | static_cast<uint64_t>(t.minute) << 40
| static_cast<uint64_t>(t.second) << 32 | static_cast<uint64_t>(t.nanosecond);
}
public:
TOML_PURE_GETTER
friend constexpr bool operator<(const time& lhs, const time& rhs) noexcept
{
return pack(lhs) < pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator<=(const time& lhs, const time& rhs) noexcept
{
return pack(lhs) <= pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator>(const time& lhs, const time& rhs) noexcept
{
return pack(lhs) > pack(rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator>=(const time& lhs, const time& rhs) noexcept
{
return pack(lhs) >= pack(rhs);
}
friend std::ostream& operator<<(std::ostream& lhs, const time& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
};
struct TOML_TRIVIAL_ABI time_offset
{
int16_t minutes;
TOML_NODISCARD_CTOR
time_offset() noexcept = default;
TOML_CONSTRAINED_TEMPLATE((impl::all_integral<H, M>), typename H, typename M)
TOML_NODISCARD_CTOR
constexpr time_offset(H h, M m) noexcept //
: minutes{ static_cast<int16_t>(static_cast<impl::common_signed_type<H, M>>(h)
* impl::common_signed_type<H, M>{ 60 }
+ static_cast<impl::common_signed_type<H, M>>(m)) }
{}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator==(time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes == rhs.minutes;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator!=(time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes != rhs.minutes;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator<(time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes < rhs.minutes;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator<=(time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes <= rhs.minutes;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator>(time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes > rhs.minutes;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator>=(time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes >= rhs.minutes;
}
friend std::ostream& operator<<(std::ostream& lhs, const time_offset& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
};
TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt);
struct date_time
{
toml::date date;
toml::time time;
optional<toml::time_offset> offset;
TOML_NODISCARD_CTOR
date_time() noexcept = default;
TOML_NODISCARD_CTOR
constexpr date_time(const toml::date& d, const toml::time& t) noexcept //
: date{ d },
time{ t },
offset{} // TINAE - icc bugfix
{}
TOML_NODISCARD_CTOR
explicit constexpr date_time(const toml::date& d) noexcept //
: date{ d },
time{},
offset{} // TINAE - icc bugfix
{}
TOML_NODISCARD_CTOR
explicit constexpr date_time(const toml::time& t) noexcept //
: date{},
time{ t },
offset{} // TINAE - icc bugfix
{}
TOML_NODISCARD_CTOR
constexpr date_time(const toml::date& d, const toml::time& t, const toml::time_offset& off) noexcept
: date{ d },
time{ t },
offset{ off }
{}
TOML_PURE_INLINE_GETTER
constexpr bool is_local() const noexcept
{
return !offset.has_value();
}
TOML_PURE_GETTER
friend constexpr bool operator==(const date_time& lhs, const date_time& rhs) noexcept
{
return lhs.date == rhs.date //
&& lhs.time == rhs.time //
&& lhs.offset == rhs.offset;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator!=(const date_time& lhs, const date_time& rhs) noexcept
{
return !(lhs == rhs);
}
TOML_PURE_GETTER
friend constexpr bool operator<(const date_time& lhs, const date_time& rhs) noexcept
{
if (lhs.date != rhs.date)
return lhs.date < rhs.date;
if (lhs.time != rhs.time)
return lhs.time < rhs.time;
return lhs.offset < rhs.offset;
}
TOML_PURE_GETTER
friend constexpr bool operator<=(const date_time& lhs, const date_time& rhs) noexcept
{
if (lhs.date != rhs.date)
return lhs.date < rhs.date;
if (lhs.time != rhs.time)
return lhs.time < rhs.time;
return lhs.offset <= rhs.offset;
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator>(const date_time& lhs, const date_time& rhs) noexcept
{
return !(lhs <= rhs);
}
TOML_PURE_INLINE_GETTER
friend constexpr bool operator>=(const date_time& lhs, const date_time& rhs) noexcept
{
return !(lhs < rhs);
}
friend std::ostream& operator<<(std::ostream& lhs, const date_time& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
};
TOML_ABI_NAMESPACE_END; // TOML_HAS_CUSTOM_OPTIONAL_TYPE
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/at_path.h ********************************************************************************************
TOML_IMPL_NAMESPACE_START
{
template <typename T>
using parse_path_callback = bool(TOML_CALLCONV*)(void*, T);
TOML_NODISCARD
bool TOML_CALLCONV parse_path(std::string_view,
void*,
parse_path_callback<std::string_view>,
parse_path_callback<size_t>);
}
TOML_IMPL_NAMESPACE_END;
TOML_NAMESPACE_START
{
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<node> TOML_CALLCONV at_path(node & root, std::string_view path) noexcept;
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<node> TOML_CALLCONV at_path(node & root, std::wstring_view path);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path);
#endif
}
TOML_NAMESPACE_END;
//******** impl/std_vector.h *****************************************************************************************
TOML_DISABLE_WARNINGS;
#include <vector>
#include <iterator>
TOML_ENABLE_WARNINGS;
//******** impl/path.h ***********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
enum class TOML_CLOSED_ENUM path_component_type : uint8_t
{
key = 0x1,
array_index = 0x2
};
class TOML_EXPORTED_CLASS path_component
{
struct storage_t
{
static constexpr size_t size =
(sizeof(size_t) < sizeof(std::string) ? sizeof(std::string) : sizeof(size_t));
static constexpr size_t align =
(alignof(size_t) < alignof(std::string) ? alignof(std::string) : alignof(size_t));
alignas(align) unsigned char bytes[size];
};
alignas(storage_t::align) mutable storage_t value_storage_;
path_component_type type_;
TOML_PURE_GETTER
TOML_EXPORTED_STATIC_FUNCTION
static bool TOML_CALLCONV equal(const path_component&, const path_component&) noexcept;
template <typename Type>
TOML_PURE_INLINE_GETTER
static Type* get_as(storage_t& s) noexcept
{
return TOML_LAUNDER(reinterpret_cast<Type*>(s.bytes));
}
static void store_key(std::string_view key, storage_t& storage_)
{
::new (static_cast<void*>(storage_.bytes)) std::string{ key };
}
static void store_index(size_t index, storage_t& storage_) noexcept
{
::new (static_cast<void*>(storage_.bytes)) std::size_t{ index };
}
void destroy() noexcept
{
if (type_ == path_component_type::key)
get_as<std::string>(value_storage_)->~basic_string();
}
TOML_NODISCARD
size_t& index_ref() noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::array_index);
return *get_as<size_t>(value_storage_);
}
TOML_NODISCARD
std::string& key_ref() noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::key);
return *get_as<std::string>(value_storage_);
}
public:
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component();
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(size_t index) noexcept;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(std::string_view key);
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(std::wstring_view key);
#endif
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(const path_component& pc);
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(path_component&& pc) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(const path_component& rhs);
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(path_component&& rhs) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(size_t new_index) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(std::string_view new_key);
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(std::wstring_view new_key);
#endif
~path_component() noexcept
{
destroy();
}
TOML_PURE_GETTER
size_t index() const noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::array_index);
return *get_as<const size_t>(value_storage_);
}
TOML_PURE_INLINE_GETTER
explicit operator size_t() const noexcept
{
return index();
}
TOML_PURE_GETTER
const std::string& key() const noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::key);
return *get_as<const std::string>(value_storage_);
}
TOML_PURE_INLINE_GETTER
explicit operator const std::string&() const noexcept
{
return key();
}
TOML_PURE_INLINE_GETTER
path_component_type type() const noexcept
{
return type_;
}
TOML_PURE_INLINE_GETTER
friend bool operator==(const path_component& lhs, const path_component& rhs) noexcept
{
return equal(lhs, rhs);
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(const path_component& lhs, const path_component& rhs) noexcept
{
return !equal(lhs, rhs);
}
};
class TOML_EXPORTED_CLASS path
{
private:
std::vector<path_component> components_;
TOML_EXPORTED_MEMBER_FUNCTION
void print_to(std::ostream&) const;
TOML_PURE_GETTER
TOML_EXPORTED_STATIC_FUNCTION
static bool TOML_CALLCONV equal(const path&, const path&) noexcept;
public:
TOML_NODISCARD_CTOR
path() noexcept = default;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
explicit path(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
explicit path(std::wstring_view);
#endif
~path() noexcept = default;
TOML_NODISCARD_CTOR
path(const path&) = default;
TOML_NODISCARD_CTOR
path(path&&) noexcept = default;
TOML_PURE_INLINE_GETTER
size_t size() const noexcept
{
return components_.size();
}
TOML_PURE_INLINE_GETTER
explicit operator bool() const noexcept
{
return !components_.empty();
}
TOML_PURE_INLINE_GETTER
bool empty() const noexcept
{
return components_.empty();
}
TOML_PURE_INLINE_GETTER
path_component& operator[](size_t index) noexcept
{
TOML_ASSERT(index < size());
return components_[index];
}
TOML_PURE_INLINE_GETTER
const path_component& operator[](size_t index) const noexcept
{
TOML_ASSERT(index < size());
return components_[index];
}
path& operator=(const path&) = default;
path& operator=(path&&) noexcept = default;
TOML_EXPORTED_MEMBER_FUNCTION
path& operator=(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXPORTED_MEMBER_FUNCTION
path& operator=(std::wstring_view);
#endif
TOML_ALWAYS_INLINE
path& assign(const path& p)
{
return *this = p;
}
TOML_ALWAYS_INLINE
path& assign(path&& p) noexcept
{
return *this = std::move(p);
}
TOML_ALWAYS_INLINE
path& assign(std::string_view str)
{
return *this = str;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_ALWAYS_INLINE
path& assign(std::wstring_view str)
{
return *this = str;
}
#endif
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(const path&);
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(path&&);
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(std::wstring_view);
#endif
TOML_ALWAYS_INLINE
path& append(const path& p)
{
return *this += p;
}
TOML_ALWAYS_INLINE
path& append(path&& p)
{
return *this += std::move(p);
}
TOML_ALWAYS_INLINE
path& append(std::string_view str)
{
return *this += str;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_ALWAYS_INLINE
path& append(std::wstring_view str)
{
return *this += str;
}
#endif
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(const path&);
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(path&&);
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(std::wstring_view);
#endif
TOML_NODISCARD
friend path operator+(const path& lhs, const path& rhs)
{
path result = lhs;
result += rhs;
return result;
}
TOML_NODISCARD
friend path operator+(const path& lhs, std::string_view rhs)
{
path result = lhs;
result += rhs;
return result;
}
TOML_NODISCARD
friend path operator+(std::string_view lhs, const path& rhs)
{
path result = rhs;
result.prepend(lhs);
return result;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
friend path operator+(const path& lhs, std::wstring_view rhs)
{
path result = lhs;
result += rhs;
return result;
}
TOML_NODISCARD
friend path operator+(std::wstring_view lhs, const path& rhs)
{
path result = rhs;
result.prepend(lhs);
return result;
}
#endif
TOML_ALWAYS_INLINE
friend std::ostream& operator<<(std::ostream& os, const path& rhs)
{
rhs.print_to(os);
return os;
}
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
std::string str() const;
TOML_NODISCARD
TOML_ALWAYS_INLINE
explicit operator std::string() const
{
return str();
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
std::wstring wide_str() const;
TOML_NODISCARD
TOML_ALWAYS_INLINE
explicit operator std::wstring() const
{
return wide_str();
}
#endif
TOML_PURE_INLINE_GETTER
friend bool operator==(const path& lhs, const path& rhs) noexcept
{
return equal(lhs, rhs);
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(const path& lhs, const path& rhs) noexcept
{
return !equal(lhs, rhs);
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(const path& lhs, std::string_view rhs)
{
return lhs == path{ rhs };
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(std::string_view lhs, const path& rhs)
{
return rhs == lhs;
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(const path& lhs, std::string_view rhs)
{
return lhs != path{ rhs };
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(std::string_view lhs, const path& rhs)
{
return rhs != lhs;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(const path& lhs, std::wstring_view rhs)
{
return lhs == path{ rhs };
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(std::wstring_view lhs, const path& rhs)
{
return rhs == lhs;
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(const path& lhs, std::wstring_view rhs)
{
return lhs != path{ rhs };
}
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(std::wstring_view lhs, const path& rhs)
{
return rhs != lhs;
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
using iterator = std::vector<path_component>::iterator;
using const_iterator = std::vector<path_component>::const_iterator;
TOML_PURE_INLINE_GETTER
iterator begin() noexcept
{
return components_.begin();
}
TOML_PURE_INLINE_GETTER
iterator end() noexcept
{
return components_.end();
}
TOML_PURE_INLINE_GETTER
const_iterator begin() const noexcept
{
return components_.begin();
}
TOML_PURE_INLINE_GETTER
const_iterator end() const noexcept
{
return components_.end();
}
TOML_PURE_INLINE_GETTER
const_iterator cbegin() const noexcept
{
return components_.begin();
}
TOML_PURE_INLINE_GETTER
const_iterator cend() const noexcept
{
return components_.end();
}
TOML_EXPORTED_MEMBER_FUNCTION
void clear() noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
path& truncate(size_t n);
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path truncated(size_t n) const;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path parent() const;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path leaf(size_t n = 1) const;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path subpath(const_iterator start, const_iterator end) const;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path subpath(size_t start, size_t length) const;
};
inline namespace literals
{
TOML_NODISCARD
TOML_ALWAYS_INLINE
path operator"" _tpath(const char* str, size_t len)
{
return path(std::string_view{ str, len });
}
}
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<node> TOML_CALLCONV at_path(node & root, const toml::path& path) noexcept;
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<const node> TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept;
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/std_utility.h ****************************************************************************************
TOML_DISABLE_WARNINGS;
#include <utility>
TOML_ENABLE_WARNINGS;
//******** impl/node.h ***********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
class TOML_ABSTRACT_INTERFACE TOML_EXPORTED_CLASS node
{
private:
friend class TOML_PARSER_TYPENAME;
source_region source_{};
template <typename T>
TOML_NODISCARD
decltype(auto) get_value_exact() const noexcept(impl::value_retrieval_is_nothrow<T>);
template <typename T, typename N>
using ref_type_ = std::conditional_t< //
std::is_reference_v<T>, //
impl::copy_ref<impl::copy_cv<impl::unwrap_node<T>, std::remove_reference_t<N>>, T>, //
impl::copy_cvref<impl::unwrap_node<T>, N> //
>;
template <typename T, typename N>
using ref_type = std::conditional_t< //
std::is_reference_v<N>, //
ref_type_<T, N>, //
ref_type_<T, std::add_lvalue_reference_t<N>> //
>;
template <typename T, typename N>
TOML_PURE_GETTER
static ref_type<T, N&&> do_ref(N&& n) noexcept
{
using unwrapped_type = impl::unwrap_node<T>;
static_assert(toml::is_value<unwrapped_type> || toml::is_container<unwrapped_type>,
"The template type argument of node::ref() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
TOML_ASSERT_ASSUME(
n.template is<unwrapped_type>()
&& "template type argument provided to toml::node::ref() didn't match the node's actual type");
using node_ref = std::remove_volatile_t<std::remove_reference_t<N>>&;
using val_type = std::remove_volatile_t<unwrapped_type>;
using out_ref = ref_type<T, N&&>;
static_assert(std::is_reference_v<out_ref>);
if constexpr (toml::is_value<unwrapped_type>)
return static_cast<out_ref>(const_cast<node_ref>(n).template ref_cast<val_type>().get());
else
return static_cast<out_ref>(const_cast<node_ref>(n).template ref_cast<val_type>());
}
protected:
TOML_EXPORTED_MEMBER_FUNCTION
node() noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
node(const node&) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
node(node&&) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
node& operator=(const node&) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
node& operator=(node&&) noexcept;
template <typename T, typename N>
using ref_cast_type_ = std::conditional_t< //
std::is_reference_v<T>, //
impl::copy_ref<impl::copy_cv<impl::wrap_node<T>, std::remove_reference_t<N>>, T>, //
impl::copy_cvref<impl::wrap_node<T>, N> //
>;
template <typename T, typename N>
using ref_cast_type = std::conditional_t< //
std::is_reference_v<N>, //
ref_cast_type_<T, N>, //
ref_cast_type_<T, std::add_lvalue_reference_t<N>> //
>;
template <typename T>
TOML_PURE_INLINE_GETTER
ref_cast_type<T, node&> ref_cast() & noexcept
{
using out_ref = ref_cast_type<T, node&>;
using out_type = std::remove_reference_t<out_ref>;
return static_cast<out_ref>(*reinterpret_cast<out_type*>(this));
}
template <typename T>
TOML_PURE_INLINE_GETTER
ref_cast_type<T, node&&> ref_cast() && noexcept
{
using out_ref = ref_cast_type<T, node&&>;
using out_type = std::remove_reference_t<out_ref>;
return static_cast<out_ref>(*reinterpret_cast<out_type*>(this));
}
template <typename T>
TOML_PURE_INLINE_GETTER
ref_cast_type<T, const node&> ref_cast() const& noexcept
{
using out_ref = ref_cast_type<T, const node&>;
using out_type = std::remove_reference_t<out_ref>;
return static_cast<out_ref>(*reinterpret_cast<out_type*>(this));
}
template <typename T>
TOML_PURE_INLINE_GETTER
ref_cast_type<T, const node&&> ref_cast() const&& noexcept
{
using out_ref = ref_cast_type<T, const node&&>;
using out_type = std::remove_reference_t<out_ref>;
return static_cast<out_ref>(*reinterpret_cast<out_type*>(this));
}
public:
TOML_EXPORTED_MEMBER_FUNCTION
virtual ~node() noexcept;
TOML_PURE_GETTER
virtual bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept = 0;
TOML_PURE_GETTER
virtual bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_homogeneous(node_type ntype) const noexcept = 0;
template <typename ElemType = void>
TOML_PURE_GETTER
bool is_homogeneous() const noexcept
{
using type = impl::remove_cvref<impl::unwrap_node<ElemType>>;
static_assert(std::is_void_v<type> || toml::is_value<type> || toml::is_container<type>,
"The template type argument of node::is_homogeneous() must be void or one "
"of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
return is_homogeneous(impl::node_type_of<type>);
}
TOML_PURE_GETTER
virtual node_type type() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_table() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_array() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_array_of_tables() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_value() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_string() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_integer() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_floating_point() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_number() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_boolean() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_date() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_time() const noexcept = 0;
TOML_PURE_GETTER
virtual bool is_date_time() const noexcept = 0;
template <typename T>
TOML_PURE_INLINE_GETTER
bool is() const noexcept
{
using type = impl::remove_cvref<impl::unwrap_node<T>>;
static_assert(toml::is_value<type> || toml::is_container<type>,
"The template type argument of node::is() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
if constexpr (std::is_same_v<type, table>)
return is_table();
else if constexpr (std::is_same_v<type, array>)
return is_array();
else if constexpr (std::is_same_v<type, std::string>)
return is_string();
else if constexpr (std::is_same_v<type, int64_t>)
return is_integer();
else if constexpr (std::is_same_v<type, double>)
return is_floating_point();
else if constexpr (std::is_same_v<type, bool>)
return is_boolean();
else if constexpr (std::is_same_v<type, date>)
return is_date();
else if constexpr (std::is_same_v<type, time>)
return is_time();
else if constexpr (std::is_same_v<type, date_time>)
return is_date_time();
}
TOML_PURE_GETTER
virtual table* as_table() noexcept = 0;
TOML_PURE_GETTER
virtual array* as_array() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<std::string>* as_string() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<int64_t>* as_integer() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<double>* as_floating_point() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<bool>* as_boolean() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<date>* as_date() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<time>* as_time() noexcept = 0;
TOML_PURE_GETTER
virtual toml::value<date_time>* as_date_time() noexcept = 0;
TOML_PURE_GETTER
virtual const table* as_table() const noexcept = 0;
TOML_PURE_GETTER
virtual const array* as_array() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<std::string>* as_string() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<int64_t>* as_integer() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<double>* as_floating_point() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<bool>* as_boolean() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<date>* as_date() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<time>* as_time() const noexcept = 0;
TOML_PURE_GETTER
virtual const toml::value<date_time>* as_date_time() const noexcept = 0;
template <typename T>
TOML_PURE_INLINE_GETTER
impl::wrap_node<T>* as() noexcept
{
using unwrapped_type = impl::unwrap_node<impl::remove_cvref<T>>;
static_assert(toml::is_value<unwrapped_type> || toml::is_container<unwrapped_type>,
"The template type argument of node::as() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
if constexpr (std::is_same_v<unwrapped_type, table>)
return as_table();
else if constexpr (std::is_same_v<unwrapped_type, array>)
return as_array();
else if constexpr (std::is_same_v<unwrapped_type, std::string>)
return as_string();
else if constexpr (std::is_same_v<unwrapped_type, int64_t>)
return as_integer();
else if constexpr (std::is_same_v<unwrapped_type, double>)
return as_floating_point();
else if constexpr (std::is_same_v<unwrapped_type, bool>)
return as_boolean();
else if constexpr (std::is_same_v<unwrapped_type, date>)
return as_date();
else if constexpr (std::is_same_v<unwrapped_type, time>)
return as_time();
else if constexpr (std::is_same_v<unwrapped_type, date_time>)
return as_date_time();
}
template <typename T>
TOML_PURE_INLINE_GETTER
const impl::wrap_node<T>* as() const noexcept
{
using unwrapped_type = impl::unwrap_node<impl::remove_cvref<T>>;
static_assert(toml::is_value<unwrapped_type> || toml::is_container<unwrapped_type>,
"The template type argument of node::as() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
if constexpr (std::is_same_v<unwrapped_type, table>)
return as_table();
else if constexpr (std::is_same_v<unwrapped_type, array>)
return as_array();
else if constexpr (std::is_same_v<unwrapped_type, std::string>)
return as_string();
else if constexpr (std::is_same_v<unwrapped_type, int64_t>)
return as_integer();
else if constexpr (std::is_same_v<unwrapped_type, double>)
return as_floating_point();
else if constexpr (std::is_same_v<unwrapped_type, bool>)
return as_boolean();
else if constexpr (std::is_same_v<unwrapped_type, date>)
return as_date();
else if constexpr (std::is_same_v<unwrapped_type, time>)
return as_time();
else if constexpr (std::is_same_v<unwrapped_type, date_time>)
return as_date_time();
}
template <typename T>
TOML_NODISCARD
optional<T> value_exact() const noexcept(impl::value_retrieval_is_nothrow<T>);
template <typename T>
TOML_NODISCARD
optional<T> value() const noexcept(impl::value_retrieval_is_nothrow<T>);
template <typename T>
TOML_NODISCARD
auto value_or(T&& default_value) const noexcept(impl::value_retrieval_is_nothrow<T>);
template <typename T>
TOML_PURE_GETTER
decltype(auto) ref() & noexcept
{
return do_ref<T>(*this);
}
template <typename T>
TOML_PURE_GETTER
decltype(auto) ref() && noexcept
{
return do_ref<T>(std::move(*this));
}
template <typename T>
TOML_PURE_GETTER
decltype(auto) ref() const& noexcept
{
return do_ref<T>(*this);
}
template <typename T>
TOML_PURE_GETTER
decltype(auto) ref() const&& noexcept
{
return do_ref<T>(std::move(*this));
}
TOML_PURE_INLINE_GETTER
const source_region& source() const noexcept
{
return source_;
}
private:
template <typename Func, typename Node, typename T>
static constexpr bool can_visit = std::is_invocable_v<Func, ref_cast_type<T, Node>>;
template <typename Func, typename Node, typename T>
static constexpr bool can_visit_nothrow = std::is_nothrow_invocable_v<Func, ref_cast_type<T, Node>>;
template <typename Func, typename Node>
static constexpr bool can_visit_any = can_visit<Func, Node, table> //
|| can_visit<Func, Node, array> //
|| can_visit<Func, Node, std::string> //
|| can_visit<Func, Node, int64_t> //
|| can_visit<Func, Node, double> //
|| can_visit<Func, Node, bool> //
|| can_visit<Func, Node, date> //
|| can_visit<Func, Node, time> //
|| can_visit<Func, Node, date_time>;
// clang-format off
template <typename Func, typename Node>
static constexpr bool can_visit_all = can_visit<Func, Node, table> //
&& can_visit<Func, Node, array> //
&& can_visit<Func, Node, std::string> //
&& can_visit<Func, Node, int64_t> //
&& can_visit<Func, Node, double> //
&& can_visit<Func, Node, bool> //
&& can_visit<Func, Node, date> //
&& can_visit<Func, Node, time> //
&& can_visit<Func, Node, date_time>;
template <typename Func, typename Node, typename T>
static constexpr bool visit_is_nothrow_one = !can_visit<Func, Node, T> || can_visit_nothrow<Func, Node, T>;
template <typename Func, typename Node>
static constexpr bool visit_is_nothrow = visit_is_nothrow_one<Func, Node, table> //
&& visit_is_nothrow_one<Func, Node, array> //
&& visit_is_nothrow_one<Func, Node, std::string> //
&& visit_is_nothrow_one<Func, Node, int64_t> //
&& visit_is_nothrow_one<Func, Node, double> //
&& visit_is_nothrow_one<Func, Node, bool> //
&& visit_is_nothrow_one<Func, Node, date> //
&& visit_is_nothrow_one<Func, Node, time> //
&& visit_is_nothrow_one<Func, Node, date_time>;
// clang-format on
template <typename Func, typename Node, typename T, bool = can_visit<Func, Node, T>>
struct visit_return_type_
{
using type = decltype(std::declval<Func>()(std::declval<ref_cast_type<T, Node>>()));
};
template <typename Func, typename Node, typename T>
struct visit_return_type_<Func, Node, T, false>
{
using type = void;
};
template <typename Func, typename Node, typename T>
using visit_return_type = typename visit_return_type_<Func, Node, T>::type;
template <typename A, typename B>
using nonvoid = std::conditional_t<std::is_void_v<A>, B, A>;
template <typename Func, typename Node>
static decltype(auto) do_visit(Func&& visitor, Node&& n) noexcept(visit_is_nothrow<Func&&, Node&&>)
{
static_assert(can_visit_any<Func&&, Node&&>,
"TOML node visitors must be invocable for at least one of the toml::node "
"specializations:" TOML_SA_NODE_TYPE_LIST);
switch (n.type())
{
case node_type::table:
if constexpr (can_visit<Func&&, Node&&, table>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<table>());
break;
case node_type::array:
if constexpr (can_visit<Func&&, Node&&, array>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<array>());
break;
case node_type::string:
if constexpr (can_visit<Func&&, Node&&, std::string>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<std::string>());
break;
case node_type::integer:
if constexpr (can_visit<Func&&, Node&&, int64_t>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<int64_t>());
break;
case node_type::floating_point:
if constexpr (can_visit<Func&&, Node&&, double>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<double>());
break;
case node_type::boolean:
if constexpr (can_visit<Func&&, Node&&, bool>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<bool>());
break;
case node_type::date:
if constexpr (can_visit<Func&&, Node&&, date>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<date>());
break;
case node_type::time:
if constexpr (can_visit<Func&&, Node&&, time>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<time>());
break;
case node_type::date_time:
if constexpr (can_visit<Func&&, Node&&, date_time>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<date_time>());
break;
case node_type::none: TOML_UNREACHABLE;
default: TOML_UNREACHABLE;
}
if constexpr (!can_visit_all<Func&&, Node&&>)
{
// clang-format off
using return_type =
nonvoid<visit_return_type<Func&&, Node&&, table>,
nonvoid<visit_return_type<Func&&, Node&&, array>,
nonvoid<visit_return_type<Func&&, Node&&, std::string>,
nonvoid<visit_return_type<Func&&, Node&&, int64_t>,
nonvoid<visit_return_type<Func&&, Node&&, double>,
nonvoid<visit_return_type<Func&&, Node&&, bool>,
nonvoid<visit_return_type<Func&&, Node&&, date>,
nonvoid<visit_return_type<Func&&, Node&&, time>,
visit_return_type<Func&&, Node&&, date_time>
>>>>>>>>;
// clang-format on
if constexpr (!std::is_void_v<return_type>)
{
static_assert(std::is_default_constructible_v<return_type>,
"Non-exhaustive visitors must return a default-constructible type, or void");
return return_type{};
}
}
}
public:
template <typename Func>
decltype(auto) visit(Func&& visitor) & noexcept(visit_is_nothrow<Func&&, node&>)
{
return do_visit(static_cast<Func&&>(visitor), *this);
}
template <typename Func>
decltype(auto) visit(Func&& visitor) && noexcept(visit_is_nothrow<Func&&, node&&>)
{
return do_visit(static_cast<Func&&>(visitor), static_cast<node&&>(*this));
}
template <typename Func>
decltype(auto) visit(Func&& visitor) const& noexcept(visit_is_nothrow<Func&&, const node&>)
{
return do_visit(static_cast<Func&&>(visitor), *this);
}
template <typename Func>
decltype(auto) visit(Func&& visitor) const&& noexcept(visit_is_nothrow<Func&&, const node&&>)
{
return do_visit(static_cast<Func&&>(visitor), static_cast<const node&&>(*this));
}
TOML_NODISCARD
explicit operator node_view<node>() noexcept;
TOML_NODISCARD
explicit operator node_view<const node>() const noexcept;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<node> at_path(std::string_view path) noexcept;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<const node> at_path(std::string_view path) const noexcept;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<node> at_path(const toml::path& path) noexcept;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<const node> at_path(const toml::path& path) const noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<node> at_path(std::wstring_view path);
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<const node> at_path(std::wstring_view path) const;
#endif // TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<node> operator[](const toml::path& path) noexcept;
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node_view<const node> operator[](const toml::path& path) const noexcept;
};
}
TOML_NAMESPACE_END;
TOML_IMPL_NAMESPACE_START
{
TOML_PURE_GETTER
TOML_EXPORTED_FREE_FUNCTION
bool TOML_CALLCONV node_deep_equality(const node*, const node*) noexcept;
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/std_initializer_list.h *******************************************************************************
TOML_DISABLE_WARNINGS;
#include <initializer_list>
TOML_ENABLE_WARNINGS;
//******** impl/node_view.h ******************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_DISABLE_ARITHMETIC_WARNINGS;
TOML_NAMESPACE_START
{
template <typename ViewedType>
class TOML_TRIVIAL_ABI node_view
{
static_assert(impl::is_one_of<ViewedType, toml::node, const toml::node>,
"A toml::node_view<> must wrap toml::node or const toml::node.");
public:
using viewed_type = ViewedType;
private:
template <typename T>
friend class node_view;
mutable viewed_type* node_ = nullptr;
public:
TOML_NODISCARD_CTOR
node_view() noexcept = default;
TOML_NODISCARD_CTOR
explicit node_view(viewed_type* node) noexcept //
: node_{ node }
{}
TOML_NODISCARD_CTOR
explicit node_view(viewed_type& node) noexcept //
: node_{ &node }
{}
TOML_NODISCARD_CTOR
node_view(const node_view&) noexcept = default;
TOML_NODISCARD_CTOR
node_view(node_view&&) noexcept = default;
node_view& operator=(const node_view&) & noexcept = default;
node_view& operator=(node_view&&) & noexcept = default;
TOML_PURE_INLINE_GETTER
explicit operator bool() const noexcept
{
return node_ != nullptr;
}
TOML_PURE_INLINE_GETTER
viewed_type* node() const noexcept
{
return node_;
}
TOML_PURE_GETTER
node_type type() const noexcept
{
return node_ ? node_->type() : node_type::none;
}
TOML_PURE_GETTER
bool is_table() const noexcept
{
return node_ && node_->is_table();
}
TOML_PURE_GETTER
bool is_array() const noexcept
{
return node_ && node_->is_array();
}
TOML_PURE_GETTER
bool is_value() const noexcept
{
return node_ && node_->is_value();
}
TOML_PURE_GETTER
bool is_string() const noexcept
{
return node_ && node_->is_string();
}
TOML_PURE_GETTER
bool is_integer() const noexcept
{
return node_ && node_->is_integer();
}
TOML_PURE_GETTER
bool is_floating_point() const noexcept
{
return node_ && node_->is_floating_point();
}
TOML_PURE_GETTER
bool is_number() const noexcept
{
return node_ && node_->is_number();
}
TOML_PURE_GETTER
bool is_boolean() const noexcept
{
return node_ && node_->is_boolean();
}
TOML_PURE_GETTER
bool is_date() const noexcept
{
return node_ && node_->is_date();
}
TOML_PURE_GETTER
bool is_time() const noexcept
{
return node_ && node_->is_time();
}
TOML_PURE_GETTER
bool is_date_time() const noexcept
{
return node_ && node_->is_date_time();
}
TOML_PURE_GETTER
bool is_array_of_tables() const noexcept
{
return node_ && node_->is_array_of_tables();
}
template <typename T>
TOML_PURE_GETTER
bool is() const noexcept
{
return node_ ? node_->template is<impl::unwrap_node<impl::remove_cvref<T>>>() : false;
}
TOML_NODISCARD
bool is_homogeneous(node_type ntype, viewed_type*& first_nonmatch) const noexcept
{
if (!node_)
{
first_nonmatch = {};
return false;
}
return node_->is_homogeneous(ntype, first_nonmatch);
}
TOML_NODISCARD
bool is_homogeneous(node_type ntype) const noexcept
{
return node_ ? node_->is_homogeneous(ntype) : false;
}
template <typename ElemType = void>
TOML_PURE_GETTER
bool is_homogeneous() const noexcept
{
return node_ ? node_->template is_homogeneous<impl::unwrap_node<impl::remove_cvref<ElemType>>>() : false;
}
template <typename T>
TOML_PURE_GETTER
auto* as() const noexcept
{
return node_ ? node_->template as<T>() : nullptr;
}
TOML_PURE_GETTER
auto* as_table() const noexcept
{
return as<table>();
}
TOML_PURE_GETTER
auto* as_array() const noexcept
{
return as<array>();
}
TOML_PURE_GETTER
auto* as_string() const noexcept
{
return as<std::string>();
}
TOML_PURE_GETTER
auto* as_integer() const noexcept
{
return as<int64_t>();
}
TOML_PURE_GETTER
auto* as_floating_point() const noexcept
{
return as<double>();
}
TOML_PURE_GETTER
auto* as_boolean() const noexcept
{
return as<bool>();
}
TOML_PURE_GETTER
auto* as_date() const noexcept
{
return as<date>();
}
TOML_PURE_GETTER
auto* as_time() const noexcept
{
return as<time>();
}
TOML_PURE_GETTER
auto* as_date_time() const noexcept
{
return as<date_time>();
}
template <typename T>
TOML_NODISCARD
optional<T> value_exact() const noexcept(impl::value_retrieval_is_nothrow<T>)
{
if (node_)
return node_->template value_exact<T>();
return {};
}
template <typename T>
TOML_NODISCARD
optional<T> value() const noexcept(impl::value_retrieval_is_nothrow<T>)
{
if (node_)
return node_->template value<T>();
return {};
}
template <typename T>
TOML_NODISCARD
auto value_or(T&& default_value) const noexcept(impl::value_retrieval_is_nothrow<T>)
{
using namespace ::toml::impl;
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
"Retrieving values as wide-character strings is only "
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
if constexpr (is_wide_string<T>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
if (node_)
return node_->value_or(static_cast<T&&>(default_value));
return std::wstring{ static_cast<T&&>(default_value) };
#else
static_assert(impl::dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
{
using value_type =
std::conditional_t<std::is_pointer_v<std::decay_t<T>>,
std::add_pointer_t<std::add_const_t<std::remove_pointer_t<std::decay_t<T>>>>,
std::decay_t<T>>;
if (node_)
return node_->value_or(static_cast<T&&>(default_value));
if constexpr (std::is_pointer_v<value_type>)
return value_type{ default_value };
else
return static_cast<T&&>(default_value);
}
}
template <typename T>
TOML_PURE_INLINE_GETTER
decltype(auto) ref() const noexcept
{
TOML_ASSERT_ASSUME(node_ && "toml::node_view::ref() called on a node_view that did not reference a node");
return node_->template ref<T>();
}
private:
template <typename Func>
static constexpr bool visit_is_nothrow = noexcept(std::declval<viewed_type*>()->visit(std::declval<Func>()));
public:
template <typename Func>
decltype(auto) visit(Func&& visitor) const noexcept(visit_is_nothrow<Func&&>)
{
using return_type = decltype(node_->visit(static_cast<Func&&>(visitor)));
if (node_)
return node_->visit(static_cast<Func&&>(visitor));
if constexpr (!std::is_void_v<return_type>)
return return_type{};
}
public:
template <typename T>
TOML_PURE_GETTER
friend bool operator==(const node_view& lhs, const node_view<T>& rhs) noexcept
{
return impl::node_deep_equality(lhs.node_, rhs.node_);
}
template <typename T>
TOML_PURE_GETTER
friend bool operator!=(const node_view& lhs, const node_view<T>& rhs) noexcept
{
return !impl::node_deep_equality(lhs.node_, rhs.node_);
}
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const table& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto tbl = lhs.as<table>();
return tbl && *tbl == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const table&, );
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const array& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const array&, );
template <typename T>
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const toml::value<T>& rhs) noexcept
{
if (lhs.node_ == &rhs)
return true;
const auto val = lhs.as<T>();
return val && *val == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const toml::value<T>&, template <typename T>);
TOML_CONSTRAINED_TEMPLATE(impl::is_losslessly_convertible_to_native<T>, typename T)
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const T& rhs) noexcept(!impl::is_wide_string<T>)
{
static_assert(!impl::is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
"Comparison with wide-character strings is only "
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
if constexpr (impl::is_wide_string<T>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
return lhs == impl::narrow(rhs);
#else
static_assert(impl::dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
{
const auto val = lhs.as<impl::native_type_of<T>>();
return val && *val == rhs;
}
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&,
const T&,
TOML_CONSTRAINED_TEMPLATE(impl::is_losslessly_convertible_to_native<T>,
typename T));
template <typename T>
TOML_NODISCARD
friend bool operator==(const node_view& lhs,
const std::initializer_list<T>& rhs) noexcept(!impl::is_wide_string<T>)
{
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::initializer_list<T>&, template <typename T>);
template <typename T>
TOML_NODISCARD
friend bool operator==(const node_view& lhs, const std::vector<T>& rhs) noexcept(!impl::is_wide_string<T>)
{
const auto arr = lhs.as<array>();
return arr && *arr == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::vector<T>&, template <typename T>);
TOML_NODISCARD
node_view operator[](std::string_view key) const noexcept
{
if (auto tbl = this->as_table())
return node_view{ tbl->get(key) };
return {};
}
TOML_NODISCARD
node_view operator[](const toml::path& path) const noexcept
{
return node_ ? node_->at_path(path) : node_view{};
}
TOML_NODISCARD
node_view at_path(std::string_view path) const noexcept
{
return node_ ? node_->at_path(path) : node_view{};
}
TOML_NODISCARD
node_view at_path(const toml::path& path) const noexcept
{
return node_ ? node_->at_path(path) : node_view{};
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view operator[](std::wstring_view key) const
{
if (auto tbl = this->as_table())
return node_view{ tbl->get(key) };
return {};
}
TOML_NODISCARD
node_view at_path(std::wstring_view path) const
{
return node_ ? node_->at_path(path) : node_view{};
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view operator[](size_t index) const noexcept
{
if (auto arr = this->as_array())
return node_view{ arr->get(index) };
return {};
}
#if TOML_ENABLE_FORMATTERS
friend std::ostream& operator<<(std::ostream& os, const node_view& nv)
{
if (nv.node_)
nv.node_->visit([&os](const auto& n) { os << n; });
return os;
}
#endif
};
template <typename T>
node_view(const T&) -> node_view<const node>;
template <typename T>
node_view(const T*) -> node_view<const node>;
template <typename T>
node_view(T&) -> node_view<node>;
template <typename T>
node_view(T*) -> node_view<node>;
}
TOML_NAMESPACE_END;
TOML_NAMESPACE_START
{
inline node::operator node_view<node>() noexcept
{
return node_view<node>{ this };
}
inline node::operator node_view<const node>() const noexcept
{
return node_view<const node>{ this };
}
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/value.h **********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_DISABLE_ARITHMETIC_WARNINGS;
// clang-format off
#if TOML_ENABLE_WINDOWS_COMPAT
#define TOML_SA_VALUE_MESSAGE_WSTRING TOML_SA_LIST_SEP "std::wstring"
#else
#define TOML_SA_VALUE_MESSAGE_WSTRING
#endif
#if TOML_HAS_CHAR8
#define TOML_SA_VALUE_MESSAGE_U8STRING_VIEW TOML_SA_LIST_SEP "std::u8string_view"
#define TOML_SA_VALUE_MESSAGE_CONST_CHAR8 TOML_SA_LIST_SEP "const char8_t*"
#else
#define TOML_SA_VALUE_MESSAGE_U8STRING_VIEW
#define TOML_SA_VALUE_MESSAGE_CONST_CHAR8
#endif
#define TOML_SA_VALUE_EXACT_FUNC_MESSAGE(type_arg) \
"The " type_arg " must be one of:" \
TOML_SA_LIST_NEW "A native TOML value type" \
TOML_SA_NATIVE_VALUE_TYPE_LIST \
\
TOML_SA_LIST_NXT "A non-view type capable of losslessly representing a native TOML value type" \
TOML_SA_LIST_BEG "std::string" \
TOML_SA_VALUE_MESSAGE_WSTRING \
TOML_SA_LIST_SEP "any signed integer type >= 64 bits" \
TOML_SA_LIST_SEP "any floating-point type >= 64 bits" \
TOML_SA_LIST_END \
\
TOML_SA_LIST_NXT "An immutable view type not requiring additional temporary storage" \
TOML_SA_LIST_BEG "std::string_view" \
TOML_SA_VALUE_MESSAGE_U8STRING_VIEW \
TOML_SA_LIST_SEP "const char*" \
TOML_SA_VALUE_MESSAGE_CONST_CHAR8 \
TOML_SA_LIST_END
#define TOML_SA_VALUE_FUNC_MESSAGE(type_arg) \
"The " type_arg " must be one of:" \
TOML_SA_LIST_NEW "A native TOML value type" \
TOML_SA_NATIVE_VALUE_TYPE_LIST \
\
TOML_SA_LIST_NXT "A non-view type capable of losslessly representing a native TOML value type" \
TOML_SA_LIST_BEG "std::string" \
TOML_SA_VALUE_MESSAGE_WSTRING \
TOML_SA_LIST_SEP "any signed integer type >= 64 bits" \
TOML_SA_LIST_SEP "any floating-point type >= 64 bits" \
TOML_SA_LIST_END \
\
TOML_SA_LIST_NXT "A non-view type capable of (reasonably) representing a native TOML value type" \
TOML_SA_LIST_BEG "any other integer type" \
TOML_SA_LIST_SEP "any floating-point type" \
TOML_SA_LIST_END \
\
TOML_SA_LIST_NXT "An immutable view type not requiring additional temporary storage" \
TOML_SA_LIST_BEG "std::string_view" \
TOML_SA_VALUE_MESSAGE_U8STRING_VIEW \
TOML_SA_LIST_SEP "const char*" \
TOML_SA_VALUE_MESSAGE_CONST_CHAR8 \
TOML_SA_LIST_END
// clang-format on
TOML_IMPL_NAMESPACE_START
{
template <typename T, typename...>
struct native_value_maker
{
template <typename... Args>
TOML_NODISCARD
static T make(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args&&...>)
{
if constexpr (std::is_aggregate_v<T>)
return T{ static_cast<Args&&>(args)... };
else
return T(static_cast<Args&&>(args)...);
}
};
template <typename T>
struct native_value_maker<T, T>
{
template <typename U>
TOML_NODISCARD
TOML_ALWAYS_INLINE
static U&& make(U&& val) noexcept
{
return static_cast<U&&>(val);
}
};
#if TOML_HAS_CHAR8 || TOML_ENABLE_WINDOWS_COMPAT
struct string_maker
{
template <typename T>
TOML_NODISCARD
static std::string make(T&& arg)
{
using arg_type = std::decay_t<T>;
#if TOML_HAS_CHAR8
if constexpr (is_one_of<arg_type, char8_t*, const char8_t*>)
{
return std::string(reinterpret_cast<const char*>(static_cast<const char8_t*>(arg)));
}
if constexpr (is_one_of<arg_type, std::u8string, std::u8string_view>)
{
return std::string(reinterpret_cast<const char*>(static_cast<const char8_t*>(arg.data())),
arg.length());
}
#endif
#if TOML_ENABLE_WINDOWS_COMPAT
if constexpr (is_wide_string<arg_type>)
{
return narrow(static_cast<T&&>(arg));
}
#endif
}
};
#if TOML_HAS_CHAR8
template <>
struct native_value_maker<std::string, char8_t*> : string_maker
{};
template <>
struct native_value_maker<std::string, const char8_t*> : string_maker
{};
template <>
struct native_value_maker<std::string, std::u8string> : string_maker
{};
template <>
struct native_value_maker<std::string, std::u8string_view> : string_maker
{};
#endif // TOML_HAS_CHAR8
#if TOML_ENABLE_WINDOWS_COMPAT
template <>
struct native_value_maker<std::string, wchar_t*> : string_maker
{};
template <>
struct native_value_maker<std::string, const wchar_t*> : string_maker
{};
template <>
struct native_value_maker<std::string, std::wstring> : string_maker
{};
template <>
struct native_value_maker<std::string, std::wstring_view> : string_maker
{};
#endif // TOML_ENABLE_WINDOWS_COMPAT
#endif // TOML_HAS_CHAR8 || TOML_ENABLE_WINDOWS_COMPAT
template <typename T>
TOML_CONST_GETTER
inline optional<T> node_integer_cast(int64_t val) noexcept
{
static_assert(node_type_of<T> == node_type::integer);
static_assert(!is_cvref<T>);
using traits = value_traits<T>;
if constexpr (!traits::is_signed)
{
if constexpr ((sizeof(T) * CHAR_BIT) < 63) // 63 bits == int64_max
{
using common_t = decltype(int64_t{} + T{});
if (val < int64_t{} || static_cast<common_t>(val) > static_cast<common_t>(traits::max))
return {};
}
else
{
if (val < int64_t{})
return {};
}
}
else
{
if (val < traits::min || val > traits::max)
return {};
}
return { static_cast<T>(val) };
}
template <typename...>
struct value_variadic_ctor_allowed : std::true_type
{};
template <typename T, typename... Args>
struct value_variadic_ctor_allowed<value<T>, value<T>, Args...> : std::false_type
{};
}
TOML_IMPL_NAMESPACE_END;
TOML_NAMESPACE_START
{
template <typename ValueType>
class value : public node
{
static_assert(impl::is_native<ValueType> && !impl::is_cvref<ValueType>,
"A toml::value<> must model one of the native TOML value types:" TOML_SA_NATIVE_VALUE_TYPE_LIST);
private:
friend class TOML_PARSER_TYPENAME;
template <typename T, typename U>
TOML_CONST_INLINE_GETTER
static auto as_value([[maybe_unused]] U* ptr) noexcept
{
if constexpr (std::is_same_v<value_type, T>)
return ptr;
else
return nullptr;
}
ValueType val_;
value_flags flags_ = value_flags::none;
public:
using value_type = ValueType;
using value_arg = POXY_IMPLEMENTATION_DETAIL(
std::conditional_t<
std::is_same_v<value_type, std::string>,
std::string_view,
std::conditional_t<impl::is_one_of<value_type, double, int64_t, bool>, value_type, const value_type&>>);
TOML_HIDDEN_CONSTRAINT(
(impl::value_variadic_ctor_allowed<value<ValueType>, impl::remove_cvref<Args>...>::value),
typename... Args)
TOML_NODISCARD_CTOR
explicit value(Args&&... args) noexcept(noexcept(value_type(
impl::native_value_maker<value_type, std::decay_t<Args>...>::make(static_cast<Args&&>(args)...))))
: val_(impl::native_value_maker<value_type, std::decay_t<Args>...>::make(static_cast<Args&&>(args)...))
{
#if TOML_LIFETIME_HOOKS
TOML_VALUE_CREATED;
#endif
}
TOML_NODISCARD_CTOR
value(const value& other) noexcept //
: node(other),
val_{ other.val_ },
flags_{ other.flags_ }
{
#if TOML_LIFETIME_HOOKS
TOML_VALUE_CREATED;
#endif
}
TOML_NODISCARD_CTOR
value(const value& other, value_flags flags) noexcept //
: node(other),
val_{ other.val_ },
flags_{ flags == preserve_source_value_flags ? other.flags_ : flags }
{
#if TOML_LIFETIME_HOOKS
TOML_VALUE_CREATED;
#endif
}
TOML_NODISCARD_CTOR
value(value&& other) noexcept //
: node(std::move(other)),
val_{ std::move(other.val_) },
flags_{ std::exchange(other.flags_, value_flags{}) }
{
#if TOML_LIFETIME_HOOKS
TOML_VALUE_CREATED;
#endif
}
TOML_NODISCARD_CTOR
value(value&& other, value_flags flags) noexcept //
: node(std::move(other)),
val_{ std::move(other.val_) },
flags_{ flags == preserve_source_value_flags ? other.flags_ : flags }
{
#if TOML_LIFETIME_HOOKS
TOML_VALUE_CREATED;
#endif
other.flags_ = {};
}
value& operator=(const value& rhs) noexcept
{
node::operator=(rhs);
val_ = rhs.val_;
flags_ = rhs.flags_;
return *this;
}
value& operator=(value&& rhs) noexcept
{
if (&rhs != this)
{
node::operator=(std::move(rhs));
val_ = std::move(rhs.val_);
flags_ = std::exchange(rhs.flags_, value_flags{});
}
return *this;
}
#if TOML_LIFETIME_HOOKS
~value() noexcept
{
TOML_VALUE_DESTROYED;
}
#endif
TOML_CONST_INLINE_GETTER
node_type type() const noexcept final
{
return impl::node_type_of<value_type>;
}
TOML_PURE_GETTER
bool is_homogeneous(node_type ntype) const noexcept final
{
return ntype == node_type::none || ntype == impl::node_type_of<value_type>;
}
TOML_PURE_GETTER
bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final
{
if (ntype != node_type::none && ntype != impl::node_type_of<value_type>)
{
first_nonmatch = this;
return false;
}
return true;
}
TOML_PURE_GETTER
bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final
{
if (ntype != node_type::none && ntype != impl::node_type_of<value_type>)
{
first_nonmatch = this;
return false;
}
return true;
}
template <typename ElemType = void>
TOML_PURE_GETTER
bool is_homogeneous() const noexcept
{
using type = impl::remove_cvref<impl::unwrap_node<ElemType>>;
static_assert(std::is_void_v<type> || toml::is_value<type> || toml::is_container<type>,
"The template type argument of value::is_homogeneous() must be void or one "
"of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
if constexpr (std::is_void_v<type>)
return true;
else
return impl::node_type_of<type> == impl::node_type_of<value_type>;
}
TOML_CONST_INLINE_GETTER
bool is_table() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_array() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_array_of_tables() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_value() const noexcept final
{
return true;
}
TOML_CONST_INLINE_GETTER
bool is_string() const noexcept final
{
return std::is_same_v<value_type, std::string>;
}
TOML_CONST_INLINE_GETTER
bool is_integer() const noexcept final
{
return std::is_same_v<value_type, int64_t>;
}
TOML_CONST_INLINE_GETTER
bool is_floating_point() const noexcept final
{
return std::is_same_v<value_type, double>;
}
TOML_CONST_INLINE_GETTER
bool is_number() const noexcept final
{
return impl::is_one_of<value_type, int64_t, double>;
}
TOML_CONST_INLINE_GETTER
bool is_boolean() const noexcept final
{
return std::is_same_v<value_type, bool>;
}
TOML_CONST_INLINE_GETTER
bool is_date() const noexcept final
{
return std::is_same_v<value_type, date>;
}
TOML_CONST_INLINE_GETTER
bool is_time() const noexcept final
{
return std::is_same_v<value_type, time>;
}
TOML_CONST_INLINE_GETTER
bool is_date_time() const noexcept final
{
return std::is_same_v<value_type, date_time>;
}
TOML_CONST_INLINE_GETTER
table* as_table() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
array* as_array() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
value<std::string>* as_string() noexcept final
{
return as_value<std::string>(this);
}
TOML_CONST_INLINE_GETTER
value<int64_t>* as_integer() noexcept final
{
return as_value<int64_t>(this);
}
TOML_CONST_INLINE_GETTER
value<double>* as_floating_point() noexcept final
{
return as_value<double>(this);
}
TOML_CONST_INLINE_GETTER
value<bool>* as_boolean() noexcept final
{
return as_value<bool>(this);
}
TOML_CONST_INLINE_GETTER
value<date>* as_date() noexcept final
{
return as_value<date>(this);
}
TOML_CONST_INLINE_GETTER
value<time>* as_time() noexcept final
{
return as_value<time>(this);
}
TOML_CONST_INLINE_GETTER
value<date_time>* as_date_time() noexcept final
{
return as_value<date_time>(this);
}
TOML_CONST_INLINE_GETTER
const table* as_table() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const array* as_array() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const value<std::string>* as_string() const noexcept final
{
return as_value<std::string>(this);
}
TOML_CONST_INLINE_GETTER
const value<int64_t>* as_integer() const noexcept final
{
return as_value<int64_t>(this);
}
TOML_CONST_INLINE_GETTER
const value<double>* as_floating_point() const noexcept final
{
return as_value<double>(this);
}
TOML_CONST_INLINE_GETTER
const value<bool>* as_boolean() const noexcept final
{
return as_value<bool>(this);
}
TOML_CONST_INLINE_GETTER
const value<date>* as_date() const noexcept final
{
return as_value<date>(this);
}
TOML_CONST_INLINE_GETTER
const value<time>* as_time() const noexcept final
{
return as_value<time>(this);
}
TOML_CONST_INLINE_GETTER
const value<date_time>* as_date_time() const noexcept final
{
return as_value<date_time>(this);
}
TOML_PURE_INLINE_GETTER
value_type& get() & noexcept
{
return val_;
}
TOML_PURE_INLINE_GETTER
value_type&& get() && noexcept
{
return static_cast<value_type&&>(val_);
}
TOML_PURE_INLINE_GETTER
const value_type& get() const& noexcept
{
return val_;
}
TOML_PURE_INLINE_GETTER
const value_type&& get() const&& noexcept
{
return static_cast<const value_type&&>(val_);
}
TOML_PURE_INLINE_GETTER
value_type& operator*() & noexcept
{
return val_;
}
TOML_PURE_INLINE_GETTER
value_type&& operator*() && noexcept
{
return static_cast<value_type&&>(val_);
}
TOML_PURE_INLINE_GETTER
const value_type& operator*() const& noexcept
{
return val_;
}
TOML_PURE_INLINE_GETTER
const value_type&& operator*() const&& noexcept
{
return static_cast<const value_type&&>(val_);
}
TOML_PURE_INLINE_GETTER
explicit operator value_type&() & noexcept
{
return val_;
}
TOML_PURE_INLINE_GETTER
explicit operator value_type&&() && noexcept
{
return static_cast<value_type&&>(val_);
}
TOML_PURE_INLINE_GETTER
explicit operator const value_type&() const& noexcept
{
return val_;
}
TOML_PURE_INLINE_GETTER
explicit operator const value_type&&() && noexcept
{
return static_cast<const value_type&&>(val_);
}
TOML_HIDDEN_CONSTRAINT(std::is_class_v<T>, typename T = value_type)
TOML_PURE_INLINE_GETTER
value_type* operator->() noexcept
{
return &val_;
}
TOML_HIDDEN_CONSTRAINT(std::is_class_v<T>, typename T = value_type)
TOML_PURE_INLINE_GETTER
const value_type* operator->() const noexcept
{
return &val_;
}
TOML_NODISCARD
value_flags flags() const noexcept
{
return flags_;
}
value& flags(value_flags new_flags) noexcept
{
flags_ = new_flags;
return *this;
}
value& operator=(value_arg rhs) noexcept
{
if constexpr (std::is_same_v<value_type, std::string>)
val_.assign(rhs);
else
val_ = rhs;
return *this;
}
TOML_CONSTRAINED_TEMPLATE((std::is_same_v<T, std::string>), typename T = value_type)
value& operator=(std::string&& rhs) noexcept
{
val_ = std::move(rhs);
return *this;
}
TOML_PURE_GETTER
friend bool operator==(const value& lhs, value_arg rhs) noexcept
{
if constexpr (std::is_same_v<value_type, double>)
{
const auto lhs_nan = impl::fpclassify(lhs.val_) == impl::fp_class::nan;
const auto rhs_nan = impl::fpclassify(rhs) == impl::fp_class::nan;
if (lhs_nan != rhs_nan)
return false;
if (lhs_nan)
return true;
}
return lhs.val_ == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg, );
TOML_PURE_GETTER
friend bool operator<(const value& lhs, value_arg rhs) noexcept
{
return lhs.val_ < rhs;
}
TOML_PURE_GETTER
friend bool operator<(value_arg lhs, const value& rhs) noexcept
{
return lhs < rhs.val_;
}
TOML_PURE_GETTER
friend bool operator<=(const value& lhs, value_arg rhs) noexcept
{
return lhs.val_ <= rhs;
}
TOML_PURE_GETTER
friend bool operator<=(value_arg lhs, const value& rhs) noexcept
{
return lhs <= rhs.val_;
}
TOML_PURE_GETTER
friend bool operator>(const value& lhs, value_arg rhs) noexcept
{
return lhs.val_ > rhs;
}
TOML_PURE_GETTER
friend bool operator>(value_arg lhs, const value& rhs) noexcept
{
return lhs > rhs.val_;
}
TOML_PURE_GETTER
friend bool operator>=(const value& lhs, value_arg rhs) noexcept
{
return lhs.val_ >= rhs;
}
TOML_PURE_GETTER
friend bool operator>=(value_arg lhs, const value& rhs) noexcept
{
return lhs >= rhs.val_;
}
template <typename T>
TOML_PURE_GETTER
friend bool operator==(const value& lhs, const value<T>& rhs) noexcept
{
if constexpr (std::is_same_v<value_type, T>)
return lhs == rhs.val_; // calls asymmetrical value-equality operator defined above
else
return false;
}
template <typename T>
TOML_PURE_INLINE_GETTER
friend bool operator!=(const value& lhs, const value<T>& rhs) noexcept
{
return !(lhs == rhs);
}
template <typename T>
TOML_PURE_GETTER
friend bool operator<(const value& lhs, const value<T>& rhs) noexcept
{
if constexpr (std::is_same_v<value_type, T>)
return lhs.val_ < rhs.val_;
else
return impl::node_type_of<value_type> < impl::node_type_of<T>;
}
template <typename T>
TOML_PURE_GETTER
friend bool operator<=(const value& lhs, const value<T>& rhs) noexcept
{
if constexpr (std::is_same_v<value_type, T>)
return lhs.val_ <= rhs.val_;
else
return impl::node_type_of<value_type> <= impl::node_type_of<T>;
}
template <typename T>
TOML_PURE_GETTER
friend bool operator>(const value& lhs, const value<T>& rhs) noexcept
{
if constexpr (std::is_same_v<value_type, T>)
return lhs.val_ > rhs.val_;
else
return impl::node_type_of<value_type> > impl::node_type_of<T>;
}
template <typename T>
TOML_PURE_GETTER
friend bool operator>=(const value& lhs, const value<T>& rhs) noexcept
{
if constexpr (std::is_same_v<value_type, T>)
return lhs.val_ >= rhs.val_;
else
return impl::node_type_of<value_type> >= impl::node_type_of<T>;
}
#if TOML_ENABLE_FORMATTERS
friend std::ostream& operator<<(std::ostream& lhs, const value& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
#endif
};
template <typename T>
value(T) -> value<impl::native_type_of<impl::remove_cvref<T>>>;
template <typename T>
value(T, value_flags) -> value<impl::native_type_of<impl::remove_cvref<T>>>;
template <typename T>
TOML_NODISCARD
inline decltype(auto) node::get_value_exact() const noexcept(impl::value_retrieval_is_nothrow<T>)
{
using namespace impl;
static_assert(node_type_of<T> != node_type::none);
static_assert(node_type_of<T> != node_type::table);
static_assert(node_type_of<T> != node_type::array);
static_assert(is_native<T> || can_represent_native<T>);
static_assert(!is_cvref<T>);
TOML_ASSERT(this->type() == node_type_of<T>);
if constexpr (node_type_of<T> == node_type::string)
{
const auto& str = *ref_cast<std::string>();
if constexpr (std::is_same_v<T, std::string>)
return str;
else if constexpr (std::is_same_v<T, std::string_view>)
return T{ str };
else if constexpr (std::is_same_v<T, const char*>)
return str.c_str();
else if constexpr (std::is_same_v<T, std::wstring>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
return widen(str);
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
#if TOML_HAS_CHAR8
// char -> char8_t (potentially unsafe - the feature is 'experimental'!)
else if constexpr (is_one_of<T, std::u8string, std::u8string_view>)
return T(reinterpret_cast<const char8_t*>(str.c_str()), str.length());
else if constexpr (std::is_same_v<T, const char8_t*>)
return reinterpret_cast<const char8_t*>(str.c_str());
else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
return static_cast<T>(*ref_cast<native_type_of<T>>());
}
template <typename T>
TOML_NODISCARD
inline optional<T> node::value_exact() const noexcept(impl::value_retrieval_is_nothrow<T>)
{
using namespace impl;
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
"Retrieving values as wide-character strings with node::value_exact() is only "
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
static_assert((is_native<T> || can_represent_native<T>)&&!is_cvref<T>,
TOML_SA_VALUE_EXACT_FUNC_MESSAGE("return type of node::value_exact()"));
// prevent additional compiler error spam when the static_assert fails by gating behind if constexpr
if constexpr ((is_native<T> || can_represent_native<T>)&&!is_cvref<T>)
{
if (type() == node_type_of<T>)
return { this->get_value_exact<T>() };
else
return {};
}
}
template <typename T>
TOML_NODISCARD
inline optional<T> node::value() const noexcept(impl::value_retrieval_is_nothrow<T>)
{
using namespace impl;
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
"Retrieving values as wide-character strings with node::value() is only "
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
static_assert((is_native<T> || can_represent_native<T> || can_partially_represent_native<T>)&&!is_cvref<T>,
TOML_SA_VALUE_FUNC_MESSAGE("return type of node::value()"));
// when asking for strings, dates, times and date_times there's no 'fuzzy' conversion
// semantics to be mindful of so the exact retrieval is enough.
if constexpr (is_natively_one_of<T, std::string, time, date, date_time>)
{
if (type() == node_type_of<T>)
return { this->get_value_exact<T>() };
else
return {};
}
// everything else requires a bit of logicking.
else
{
switch (type())
{
// int -> *
case node_type::integer:
{
// int -> int
if constexpr (is_natively_one_of<T, int64_t>)
{
if constexpr (is_native<T> || can_represent_native<T>)
return static_cast<T>(*ref_cast<int64_t>());
else
return node_integer_cast<T>(*ref_cast<int64_t>());
}
// int -> float
else if constexpr (is_natively_one_of<T, double>)
{
const int64_t val = *ref_cast<int64_t>();
if constexpr (std::numeric_limits<T>::digits < 64)
{
constexpr auto largest_whole_float = (int64_t{ 1 } << std::numeric_limits<T>::digits);
if (val < -largest_whole_float || val > largest_whole_float)
return {};
}
return static_cast<T>(val);
}
// int -> bool
else if constexpr (is_natively_one_of<T, bool>)
return static_cast<bool>(*ref_cast<int64_t>());
// int -> anything else
else
return {};
}
// float -> *
case node_type::floating_point:
{
// float -> float
if constexpr (is_natively_one_of<T, double>)
{
if constexpr (is_native<T> || can_represent_native<T>)
return { static_cast<T>(*ref_cast<double>()) };
else
{
const double val = *ref_cast<double>();
if (impl::fpclassify(val) == fp_class::ok
&& (val < (std::numeric_limits<T>::lowest)() || val > (std::numeric_limits<T>::max)()))
return {};
return { static_cast<T>(val) };
}
}
// float -> int
else if constexpr (is_natively_one_of<T, int64_t>)
{
const double val = *ref_cast<double>();
if (impl::fpclassify(val) == fp_class::ok
&& static_cast<double>(static_cast<int64_t>(val)) == val)
return node_integer_cast<T>(static_cast<int64_t>(val));
else
return {};
}
// float -> anything else
else
return {};
}
// bool -> *
case node_type::boolean:
{
// bool -> bool
if constexpr (is_natively_one_of<T, bool>)
return { *ref_cast<bool>() };
// bool -> int
else if constexpr (is_natively_one_of<T, int64_t>)
return { static_cast<T>(*ref_cast<bool>()) };
// bool -> anything else
else
return {};
}
}
// non-values, or 'exact' types covered above
return {};
}
}
template <typename T>
TOML_NODISCARD
inline auto node::value_or(T && default_value) const noexcept(impl::value_retrieval_is_nothrow<T>)
{
using namespace impl;
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
"Retrieving values as wide-character strings with node::value_or() is only "
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
if constexpr (is_wide_string<T>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
if (type() == node_type::string)
return widen(*ref_cast<std::string>());
return std::wstring{ static_cast<T&&>(default_value) };
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
{
using value_type =
std::conditional_t<std::is_pointer_v<std::decay_t<T>>,
std::add_pointer_t<std::add_const_t<std::remove_pointer_t<std::decay_t<T>>>>,
std::decay_t<T>>;
using traits = value_traits<value_type>;
// clang-format off
static_assert(
traits::is_native || traits::can_represent_native || traits::can_partially_represent_native,
"The default value type of node::value_or() must be one of:"
TOML_SA_LIST_NEW "A native TOML value type"
TOML_SA_NATIVE_VALUE_TYPE_LIST
TOML_SA_LIST_NXT "A non-view type capable of losslessly representing a native TOML value type"
TOML_SA_LIST_BEG "std::string"
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_SA_LIST_SEP "std::wstring"
#endif
TOML_SA_LIST_SEP "any signed integer type >= 64 bits"
TOML_SA_LIST_SEP "any floating-point type >= 64 bits"
TOML_SA_LIST_END
TOML_SA_LIST_NXT "A non-view type capable of (reasonably) representing a native TOML value type"
TOML_SA_LIST_BEG "any other integer type"
TOML_SA_LIST_SEP "any floating-point type"
TOML_SA_LIST_END
TOML_SA_LIST_NXT "A compatible view type"
TOML_SA_LIST_BEG "std::string_view"
#if TOML_HAS_CHAR8
TOML_SA_LIST_SEP "std::u8string_view"
#endif
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_SA_LIST_SEP "std::wstring_view"
#endif
TOML_SA_LIST_SEP "const char*"
#if TOML_HAS_CHAR8
TOML_SA_LIST_SEP "const char8_t*"
#endif
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_SA_LIST_SEP "const wchar_t*"
#endif
TOML_SA_LIST_END
);
// clang-format on
// prevent additional compiler error spam when the static_assert fails by gating behind if constexpr
if constexpr (traits::is_native || traits::can_represent_native || traits::can_partially_represent_native)
{
if constexpr (traits::is_native)
{
if (type() == node_type_of<value_type>)
return *ref_cast<typename traits::native_type>();
}
if (auto val = this->value<value_type>())
return *val;
if constexpr (std::is_pointer_v<value_type>)
return value_type{ default_value };
else
return static_cast<T&&>(default_value);
}
}
}
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/make_node.h ******************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
template <typename T>
TOML_NODISCARD
TOML_ATTR(returns_nonnull)
auto* make_node_impl_specialized(T && val, [[maybe_unused]] value_flags flags)
{
using unwrapped_type = unwrap_node<remove_cvref<T>>;
static_assert(!std::is_same_v<unwrapped_type, node>);
static_assert(!is_node_view<unwrapped_type>);
// arrays + tables - invoke copy/move ctor
if constexpr (is_one_of<unwrapped_type, array, table>)
{
return new unwrapped_type(static_cast<T&&>(val));
}
// values
else
{
using native_type = native_type_of<unwrapped_type>;
using value_type = value<native_type>;
value_type* out;
// copy/move ctor
if constexpr (std::is_same_v<remove_cvref<T>, value_type>)
{
out = new value_type{ static_cast<T&&>(val), flags };
}
// creating from raw value
else
{
static_assert(!is_wide_string<T> || TOML_ENABLE_WINDOWS_COMPAT,
"Instantiating values from wide-character strings is only "
"supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled.");
if constexpr (!is_losslessly_convertible_to_native<unwrapped_type>)
{
if constexpr (std::is_same_v<native_type, int64_t>)
static_assert(dependent_false<T>,
"Integral value initializers must be losslessly convertible to int64_t");
else if constexpr (std::is_same_v<native_type, double>)
static_assert(dependent_false<T>,
"Floating-point value initializers must be losslessly convertible to double");
else
static_assert(
dependent_false<T>,
"Value initializers must be losslessly convertible to one of the TOML value types");
}
if constexpr (is_wide_string<T>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
out = new value_type{ narrow(static_cast<T&&>(val)) };
#else
static_assert(dependent_false<T>, "Evaluated unreachable branch!");
#endif
}
else
out = new value_type{ static_cast<T&&>(val) };
if (flags != preserve_source_value_flags)
out->flags(flags);
}
return out;
}
}
template <typename T>
TOML_NODISCARD
auto* make_node_impl(T && val, value_flags flags = preserve_source_value_flags)
{
using unwrapped_type = unwrap_node<remove_cvref<T>>;
if constexpr (std::is_same_v<unwrapped_type, node> || is_node_view<unwrapped_type>)
{
if constexpr (is_node_view<unwrapped_type>)
{
if (!val)
return static_cast<toml::node*>(nullptr);
}
return static_cast<T&&>(val).visit(
[flags](auto&& concrete) {
return static_cast<toml::node*>(
make_node_impl_specialized(static_cast<decltype(concrete)&&>(concrete), flags));
});
}
else
return make_node_impl_specialized(static_cast<T&&>(val), flags);
}
template <typename T>
TOML_NODISCARD
auto* make_node_impl(inserter<T> && val, value_flags flags = preserve_source_value_flags)
{
return make_node_impl(static_cast<T&&>(val.value), flags);
}
template <typename T, bool = (is_node<T> || is_node_view<T> || is_value<T> || can_partially_represent_native<T>)>
struct inserted_type_of_
{
using type = std::remove_pointer_t<decltype(make_node_impl(std::declval<T>()))>;
};
template <typename T>
struct inserted_type_of_<inserter<T>, false>
{
using type = typename inserted_type_of_<remove_cvref<T>>::type;
};
template <typename T>
struct inserted_type_of_<T, false>
{
using type = void;
};
template <typename T>
TOML_NODISCARD
node_ptr make_node(T && val, value_flags flags = preserve_source_value_flags)
{
return node_ptr{ make_node_impl(static_cast<T&&>(val), flags) };
}
template <typename... T>
struct emplaced_type_of_
{
using type = void;
};
template <typename T>
struct emplaced_type_of_<T>
{
using type = std::conditional_t<is_one_of<T, node, node_view<node>, node_view<const node>>,
void,
typename inserted_type_of_<T>::type>;
};
template <typename T>
struct emplaced_type_of_<inserter<T>>
{
using type = typename emplaced_type_of_<remove_cvref<T>>::type;
};
template <typename... T>
using emplaced_type_of = typename emplaced_type_of_<remove_cvref<T>...>::type;
}
TOML_IMPL_NAMESPACE_END;
TOML_NAMESPACE_START
{
template <typename T>
using inserted_type_of = POXY_IMPLEMENTATION_DETAIL(typename impl::inserted_type_of_<impl::remove_cvref<T>>::type);
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/array.h **********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
template <bool IsConst>
class TOML_TRIVIAL_ABI array_iterator
{
private:
template <bool>
friend class array_iterator;
using mutable_vector_iterator = std::vector<node_ptr>::iterator;
using const_vector_iterator = std::vector<node_ptr>::const_iterator;
using vector_iterator = std::conditional_t<IsConst, const_vector_iterator, mutable_vector_iterator>;
mutable vector_iterator iter_;
public:
using value_type = std::conditional_t<IsConst, const node, node>;
using reference = value_type&;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = typename std::iterator_traits<vector_iterator>::iterator_category;
TOML_NODISCARD_CTOR
array_iterator() noexcept = default;
TOML_NODISCARD_CTOR
explicit array_iterator(mutable_vector_iterator iter) noexcept //
: iter_{ iter }
{}
TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst)
TOML_NODISCARD_CTOR
explicit array_iterator(const_vector_iterator iter) noexcept //
: iter_{ iter }
{}
TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst)
TOML_NODISCARD_CTOR
array_iterator(const array_iterator<false>& other) noexcept //
: iter_{ other.iter_ }
{}
TOML_NODISCARD_CTOR
array_iterator(const array_iterator&) noexcept = default;
array_iterator& operator=(const array_iterator&) noexcept = default;
array_iterator& operator++() noexcept // ++pre
{
++iter_;
return *this;
}
array_iterator operator++(int) noexcept // post++
{
array_iterator out{ iter_ };
++iter_;
return out;
}
array_iterator& operator--() noexcept // --pre
{
--iter_;
return *this;
}
array_iterator operator--(int) noexcept // post--
{
array_iterator out{ iter_ };
--iter_;
return out;
}
TOML_PURE_INLINE_GETTER
reference operator*() const noexcept
{
return *iter_->get();
}
TOML_PURE_INLINE_GETTER
pointer operator->() const noexcept
{
return iter_->get();
}
TOML_PURE_INLINE_GETTER
explicit operator const vector_iterator&() const noexcept
{
return iter_;
}
TOML_CONSTRAINED_TEMPLATE(!C, bool C = IsConst)
TOML_PURE_INLINE_GETTER
explicit operator const const_vector_iterator() const noexcept
{
return iter_;
}
array_iterator& operator+=(ptrdiff_t rhs) noexcept
{
iter_ += rhs;
return *this;
}
array_iterator& operator-=(ptrdiff_t rhs) noexcept
{
iter_ -= rhs;
return *this;
}
TOML_NODISCARD
friend array_iterator operator+(const array_iterator& lhs, ptrdiff_t rhs) noexcept
{
return array_iterator{ lhs.iter_ + rhs };
}
TOML_NODISCARD
friend array_iterator operator+(ptrdiff_t lhs, const array_iterator& rhs) noexcept
{
return array_iterator{ rhs.iter_ + lhs };
}
TOML_NODISCARD
friend array_iterator operator-(const array_iterator& lhs, ptrdiff_t rhs) noexcept
{
return array_iterator{ lhs.iter_ - rhs };
}
TOML_PURE_INLINE_GETTER
friend ptrdiff_t operator-(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ - rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator==(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ == rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ != rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator<(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ < rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator<=(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ <= rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator>(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ > rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator>=(const array_iterator& lhs, const array_iterator& rhs) noexcept
{
return lhs.iter_ >= rhs.iter_;
}
TOML_PURE_INLINE_GETTER
reference operator[](ptrdiff_t idx) const noexcept
{
return *(iter_ + idx)->get();
}
};
struct array_init_elem
{
mutable node_ptr value;
template <typename T>
TOML_NODISCARD_CTOR
array_init_elem(T&& val, value_flags flags = preserve_source_value_flags) //
: value{ make_node(static_cast<T&&>(val), flags) }
{}
};
}
TOML_IMPL_NAMESPACE_END;
TOML_NAMESPACE_START
{
using array_iterator = POXY_IMPLEMENTATION_DETAIL(impl::array_iterator<false>);
using const_array_iterator = POXY_IMPLEMENTATION_DETAIL(impl::array_iterator<true>);
class TOML_EXPORTED_CLASS array : public node
{
private:
using vector_type = std::vector<impl::node_ptr>;
using vector_iterator = typename vector_type::iterator;
using const_vector_iterator = typename vector_type::const_iterator;
vector_type elems_;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
array(const impl::array_init_elem*, const impl::array_init_elem*);
TOML_NODISCARD_CTOR
array(std::false_type, std::initializer_list<impl::array_init_elem> elems) //
: array{ elems.begin(), elems.end() }
{}
TOML_EXPORTED_MEMBER_FUNCTION
void preinsertion_resize(size_t idx, size_t count);
TOML_EXPORTED_MEMBER_FUNCTION
void insert_at_back(impl::node_ptr&&);
TOML_EXPORTED_MEMBER_FUNCTION
vector_iterator insert_at(const_vector_iterator, impl::node_ptr&&);
template <typename T>
void emplace_back_if_not_empty_view(T&& val, value_flags flags)
{
if constexpr (is_node_view<T>)
{
if (!val)
return;
}
insert_at_back(impl::make_node(static_cast<T&&>(val), flags));
}
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
size_t total_leaf_count() const noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
void flatten_child(array&& child, size_t& dest_index) noexcept;
public:
using value_type = node;
using size_type = size_t;
using difference_type = ptrdiff_t;
using reference = node&;
using const_reference = const node&;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
array() noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
~array() noexcept;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
array(const array&);
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
array(array&& other) noexcept;
TOML_CONSTRAINED_TEMPLATE((sizeof...(ElemTypes) > 0 || !std::is_same_v<impl::remove_cvref<ElemType>, array>),
typename ElemType,
typename... ElemTypes)
TOML_NODISCARD_CTOR
explicit array(ElemType&& val, ElemTypes&&... vals)
: array{ std::false_type{},
std::initializer_list<impl::array_init_elem>{ static_cast<ElemType&&>(val),
static_cast<ElemTypes&&>(vals)... } }
{}
TOML_EXPORTED_MEMBER_FUNCTION
array& operator=(const array&);
TOML_EXPORTED_MEMBER_FUNCTION
array& operator=(array&& rhs) noexcept;
TOML_CONST_INLINE_GETTER
node_type type() const noexcept final
{
return node_type::array;
}
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
bool is_homogeneous(node_type ntype) const noexcept final;
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final;
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final;
template <typename ElemType = void>
TOML_PURE_GETTER
bool is_homogeneous() const noexcept
{
using type = impl::remove_cvref<impl::unwrap_node<ElemType>>;
static_assert(std::is_void_v<type> || toml::is_value<type> || toml::is_container<type>,
"The template type argument of array::is_homogeneous() must be void or one "
"of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
return is_homogeneous(impl::node_type_of<type>);
}
TOML_CONST_INLINE_GETTER
bool is_table() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_array() const noexcept final
{
return true;
}
TOML_PURE_GETTER
bool is_array_of_tables() const noexcept final
{
return is_homogeneous(node_type::table);
}
TOML_CONST_INLINE_GETTER
bool is_value() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_string() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_integer() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_floating_point() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_number() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_boolean() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_date() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_time() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_date_time() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
table* as_table() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
array* as_array() noexcept final
{
return this;
}
TOML_CONST_INLINE_GETTER
toml::value<std::string>* as_string() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<int64_t>* as_integer() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<double>* as_floating_point() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<bool>* as_boolean() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<date>* as_date() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<time>* as_time() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<date_time>* as_date_time() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const table* as_table() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const array* as_array() const noexcept final
{
return this;
}
TOML_CONST_INLINE_GETTER
const toml::value<std::string>* as_string() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<int64_t>* as_integer() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<double>* as_floating_point() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<bool>* as_boolean() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<date>* as_date() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<time>* as_time() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<date_time>* as_date_time() const noexcept final
{
return nullptr;
}
TOML_PURE_INLINE_GETTER
node* get(size_t index) noexcept
{
return index < elems_.size() ? elems_[index].get() : nullptr;
}
TOML_PURE_INLINE_GETTER
const node* get(size_t index) const noexcept
{
return const_cast<array&>(*this).get(index);
}
template <typename ElemType>
TOML_NODISCARD
impl::wrap_node<ElemType>* get_as(size_t index) noexcept
{
if (auto val = get(index))
return val->template as<ElemType>();
return nullptr;
}
template <typename ElemType>
TOML_NODISCARD
const impl::wrap_node<ElemType>* get_as(size_t index) const noexcept
{
return const_cast<array&>(*this).template get_as<ElemType>(index);
}
using node::operator[]; // inherit operator[toml::path]
TOML_NODISCARD
node& operator[](size_t index) noexcept
{
return *elems_[index];
}
TOML_NODISCARD
const node& operator[](size_t index) const noexcept
{
return *elems_[index];
}
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node& at(size_t index);
TOML_NODISCARD
const node& at(size_t index) const
{
return const_cast<array&>(*this).at(index);
}
TOML_NODISCARD
node& front() noexcept
{
return *elems_.front();
}
TOML_NODISCARD
const node& front() const noexcept
{
return *elems_.front();
}
TOML_NODISCARD
node& back() noexcept
{
return *elems_.back();
}
TOML_NODISCARD
const node& back() const noexcept
{
return *elems_.back();
}
using iterator = array_iterator;
using const_iterator = const_array_iterator;
TOML_NODISCARD
iterator begin() noexcept
{
return iterator{ elems_.begin() };
}
TOML_NODISCARD
const_iterator begin() const noexcept
{
return const_iterator{ elems_.cbegin() };
}
TOML_NODISCARD
const_iterator cbegin() const noexcept
{
return const_iterator{ elems_.cbegin() };
}
TOML_NODISCARD
iterator end() noexcept
{
return iterator{ elems_.end() };
}
TOML_NODISCARD
const_iterator end() const noexcept
{
return const_iterator{ elems_.cend() };
}
TOML_NODISCARD
const_iterator cend() const noexcept
{
return const_iterator{ elems_.cend() };
}
private:
template <typename T, typename Array>
using for_each_elem_ref = impl::copy_cvref<impl::wrap_node<impl::remove_cvref<impl::unwrap_node<T>>>, Array>;
template <typename Func, typename Array, typename T>
static constexpr bool can_for_each = std::is_invocable_v<Func, for_each_elem_ref<T, Array>, size_t> //
|| std::is_invocable_v<Func, size_t, for_each_elem_ref<T, Array>> //
|| std::is_invocable_v<Func, for_each_elem_ref<T, Array>>;
template <typename Func, typename Array, typename T>
static constexpr bool can_for_each_nothrow =
std::is_nothrow_invocable_v<Func, for_each_elem_ref<T, Array>, size_t> //
|| std::is_nothrow_invocable_v<Func, size_t, for_each_elem_ref<T, Array>> //
|| std::is_nothrow_invocable_v<Func, for_each_elem_ref<T, Array>>;
template <typename Func, typename Array>
static constexpr bool can_for_each_any = can_for_each<Func, Array, table> //
|| can_for_each<Func, Array, array> //
|| can_for_each<Func, Array, std::string> //
|| can_for_each<Func, Array, int64_t> //
|| can_for_each<Func, Array, double> //
|| can_for_each<Func, Array, bool> //
|| can_for_each<Func, Array, date> //
|| can_for_each<Func, Array, time> //
|| can_for_each<Func, Array, date_time>;
template <typename Func, typename Array, typename T>
static constexpr bool for_each_is_nothrow_one = !can_for_each<Func, Array, T> //
|| can_for_each_nothrow<Func, Array, T>;
// clang-format off
template <typename Func, typename Array>
static constexpr bool for_each_is_nothrow = for_each_is_nothrow_one<Func, Array, table> //
&& for_each_is_nothrow_one<Func, Array, array> //
&& for_each_is_nothrow_one<Func, Array, std::string> //
&& for_each_is_nothrow_one<Func, Array, int64_t> //
&& for_each_is_nothrow_one<Func, Array, double> //
&& for_each_is_nothrow_one<Func, Array, bool> //
&& for_each_is_nothrow_one<Func, Array, date> //
&& for_each_is_nothrow_one<Func, Array, time> //
&& for_each_is_nothrow_one<Func, Array, date_time>;
// clang-format on
template <typename Func, typename Array>
static void do_for_each(Func&& visitor, Array&& arr) noexcept(for_each_is_nothrow<Func&&, Array&&>)
{
static_assert(can_for_each_any<Func&&, Array&&>,
"TOML array for_each visitors must be invocable for at least one of the toml::node "
"specializations:" TOML_SA_NODE_TYPE_LIST);
for (size_t i = 0; i < arr.size(); i++)
{
using node_ref = impl::copy_cvref<toml::node, Array&&>;
static_assert(std::is_reference_v<node_ref>);
const auto keep_going =
static_cast<node_ref>(static_cast<Array&&>(arr)[i])
.visit(
[&](auto&& elem)
#if !TOML_MSVC // MSVC thinks this is invalid syntax O_o
noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>)
#endif
{
using elem_ref = for_each_elem_ref<decltype(elem), Array&&>;
static_assert(std::is_reference_v<elem_ref>);
// func(elem, i)
if constexpr (std::is_invocable_v<Func&&, elem_ref, size_t>)
{
using return_type =
decltype(static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i));
if constexpr (impl::is_constructible_or_convertible<bool, return_type>)
{
return static_cast<bool>(
static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i));
}
else
{
static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i);
return true;
}
}
// func(i, elem)
else if constexpr (std::is_invocable_v<Func&&, size_t, elem_ref>)
{
using return_type =
decltype(static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem)));
if constexpr (impl::is_constructible_or_convertible<bool, return_type>)
{
return static_cast<bool>(
static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem)));
}
else
{
static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem));
return true;
}
}
// func(elem)
else if constexpr (std::is_invocable_v<Func&&, elem_ref>)
{
using return_type =
decltype(static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem)));
if constexpr (impl::is_constructible_or_convertible<bool, return_type>)
{
return static_cast<bool>(
static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem)));
}
else
{
static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem));
return true;
}
}
// visitor not compatible with this particular type
else
return true;
});
if (!keep_going)
return;
}
}
public:
template <typename Func>
array& for_each(Func&& visitor) & noexcept(for_each_is_nothrow<Func&&, array&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
array&& for_each(Func&& visitor) && noexcept(for_each_is_nothrow<Func&&, array&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<array&&>(*this));
return static_cast<array&&>(*this);
}
template <typename Func>
const array& for_each(Func&& visitor) const& noexcept(for_each_is_nothrow<Func&&, const array&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
const array&& for_each(Func&& visitor) const&& noexcept(for_each_is_nothrow<Func&&, const array&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<const array&&>(*this));
return static_cast<const array&&>(*this);
}
TOML_NODISCARD
bool empty() const noexcept
{
return elems_.empty();
}
TOML_NODISCARD
size_t size() const noexcept
{
return elems_.size();
}
TOML_NODISCARD
size_t max_size() const noexcept
{
return elems_.max_size();
}
TOML_NODISCARD
size_t capacity() const noexcept
{
return elems_.capacity();
}
TOML_EXPORTED_MEMBER_FUNCTION
void reserve(size_t new_capacity);
TOML_EXPORTED_MEMBER_FUNCTION
void shrink_to_fit();
TOML_EXPORTED_MEMBER_FUNCTION
void truncate(size_t new_size);
template <typename ElemType>
void resize(size_t new_size,
ElemType&& default_init_val,
value_flags default_init_flags = preserve_source_value_flags)
{
static_assert(!is_node_view<ElemType>,
"The default element type argument to toml::array::resize may not be toml::node_view.");
if (!new_size)
clear();
else if (new_size > elems_.size())
insert(cend(), new_size - elems_.size(), static_cast<ElemType&&>(default_init_val), default_init_flags);
else
truncate(new_size);
}
TOML_EXPORTED_MEMBER_FUNCTION
iterator erase(const_iterator pos) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
iterator erase(const_iterator first, const_iterator last) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
array& flatten() &;
array&& flatten() &&
{
return static_cast<toml::array&&>(this->flatten());
}
TOML_EXPORTED_MEMBER_FUNCTION
array& prune(bool recursive = true) & noexcept;
array&& prune(bool recursive = true) && noexcept
{
return static_cast<toml::array&&>(this->prune(recursive));
}
TOML_EXPORTED_MEMBER_FUNCTION
void pop_back() noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
void clear() noexcept;
template <typename ElemType>
iterator insert(const_iterator pos, ElemType&& val, value_flags flags = preserve_source_value_flags)
{
if constexpr (is_node_view<ElemType>)
{
if (!val)
return end();
}
return iterator{ insert_at(const_vector_iterator{ pos },
impl::make_node(static_cast<ElemType&&>(val), flags)) };
}
template <typename ElemType>
iterator insert(const_iterator pos,
size_t count,
ElemType&& val,
value_flags flags = preserve_source_value_flags)
{
if constexpr (is_node_view<ElemType>)
{
if (!val)
return end();
}
switch (count)
{
case 0: return iterator{ elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()) };
case 1: return insert(pos, static_cast<ElemType&&>(val), flags);
default:
{
const auto start_idx = static_cast<size_t>(const_vector_iterator{ pos } - elems_.cbegin());
preinsertion_resize(start_idx, count);
size_t i = start_idx;
for (size_t e = start_idx + count - 1u; i < e; i++)
elems_[i] = impl::make_node(val, flags);
elems_[i] = impl::make_node(static_cast<ElemType&&>(val), flags);
return iterator{ elems_.begin() + static_cast<ptrdiff_t>(start_idx) };
}
}
}
template <typename Iter>
iterator insert(const_iterator pos, Iter first, Iter last, value_flags flags = preserve_source_value_flags)
{
const auto distance = std::distance(first, last);
if (distance <= 0)
return iterator{ elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()) };
else
{
auto count = distance;
using deref_type = decltype(*first);
if constexpr (is_node_view<deref_type>)
{
for (auto it = first; it != last; it++)
if (!(*it))
count--;
if (!count)
return iterator{ elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()) };
}
const auto start_idx = static_cast<size_t>(const_vector_iterator{ pos } - elems_.cbegin());
preinsertion_resize(start_idx, static_cast<size_t>(count));
size_t i = start_idx;
for (auto it = first; it != last; it++)
{
if constexpr (is_node_view<deref_type>)
{
if (!(*it))
continue;
}
if constexpr (std::is_rvalue_reference_v<deref_type>)
elems_[i++] = impl::make_node(std::move(*it), flags);
else
elems_[i++] = impl::make_node(*it, flags);
}
return iterator{ elems_.begin() + static_cast<ptrdiff_t>(start_idx) };
}
}
template <typename ElemType>
iterator insert(const_iterator pos,
std::initializer_list<ElemType> ilist,
value_flags flags = preserve_source_value_flags)
{
return insert(pos, ilist.begin(), ilist.end(), flags);
}
template <typename ElemType = void, typename... Args>
iterator emplace(const_iterator pos, Args&&... args)
{
using raw_elem_type = impl::remove_cvref<ElemType>;
using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, //
impl::emplaced_type_of<Args&&...>,
raw_elem_type>;
using type = impl::remove_cvref<impl::unwrap_node<elem_type>>;
static_assert(impl::is_native<type> || impl::is_one_of<type, table, array>,
"Emplacement type parameter must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
return iterator{ insert_at(const_vector_iterator{ pos },
impl::node_ptr{ new impl::wrap_node<type>{ static_cast<Args&&>(args)... } }) };
}
template <typename ElemType>
iterator replace(const_iterator pos, ElemType&& val, value_flags flags = preserve_source_value_flags)
{
TOML_ASSERT(pos >= cbegin() && pos < cend());
if constexpr (is_node_view<ElemType>)
{
if (!val)
return end();
}
const auto it = elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin());
*it = impl::make_node(static_cast<ElemType&&>(val), flags);
return iterator{ it };
}
template <typename ElemType>
void push_back(ElemType&& val, value_flags flags = preserve_source_value_flags)
{
emplace_back_if_not_empty_view(static_cast<ElemType&&>(val), flags);
}
template <typename ElemType = void, typename... Args>
decltype(auto) emplace_back(Args&&... args)
{
using raw_elem_type = impl::remove_cvref<ElemType>;
using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, //
impl::emplaced_type_of<Args&&...>,
raw_elem_type>;
static constexpr auto moving_node_ptr = std::is_same_v<elem_type, impl::node_ptr> //
&& sizeof...(Args) == 1u //
&& impl::first_is_same<impl::node_ptr&&, Args&&...>;
using unwrapped_type = impl::remove_cvref<impl::unwrap_node<elem_type>>;
static_assert(
moving_node_ptr //
|| impl::is_native<unwrapped_type> //
|| impl::is_one_of<unwrapped_type, table, array>, //
"ElemType argument of array::emplace_back() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
if constexpr (moving_node_ptr)
{
insert_at_back(static_cast<Args&&>(args)...);
return *elems_.back();
}
else
{
auto ptr = new impl::wrap_node<unwrapped_type>{ static_cast<Args&&>(args)... };
insert_at_back(impl::node_ptr{ ptr });
return *ptr;
}
}
private:
TOML_NODISCARD
TOML_EXPORTED_STATIC_FUNCTION
static bool TOML_CALLCONV equal(const array&, const array&) noexcept;
template <typename T>
TOML_NODISCARD
static bool equal_to_container(const array& lhs, const T& rhs) noexcept
{
using element_type = std::remove_const_t<typename T::value_type>;
static_assert(impl::is_losslessly_convertible_to_native<element_type>,
"Container element type must be losslessly convertible one of the native TOML value types");
if (lhs.size() != rhs.size())
return false;
if (rhs.size() == 0u)
return true;
size_t i{};
for (auto& list_elem : rhs)
{
const auto elem = lhs.get_as<impl::native_type_of<element_type>>(i++);
if (!elem || *elem != list_elem)
return false;
}
return true;
}
public:
TOML_NODISCARD
friend bool operator==(const array& lhs, const array& rhs) noexcept
{
return equal(lhs, rhs);
}
TOML_NODISCARD
friend bool operator!=(const array& lhs, const array& rhs) noexcept
{
return !equal(lhs, rhs);
}
template <typename T>
TOML_NODISCARD
friend bool operator==(const array& lhs, const std::initializer_list<T>& rhs) noexcept
{
return equal_to_container(lhs, rhs);
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list<T>&, template <typename T>);
template <typename T>
TOML_NODISCARD
friend bool operator==(const array& lhs, const std::vector<T>& rhs) noexcept
{
return equal_to_container(lhs, rhs);
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector<T>&, template <typename T>);
#if TOML_ENABLE_FORMATTERS
friend std::ostream& operator<<(std::ostream& lhs, const array& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
#endif
};
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/key.h ************************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
class key
{
private:
std::string key_;
source_region source_;
public:
TOML_NODISCARD_CTOR
key() noexcept = default;
TOML_NODISCARD_CTOR
explicit key(std::string_view k, source_region&& src = {}) //
: key_{ k },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
explicit key(std::string_view k, const source_region& src) //
: key_{ k },
source_{ src }
{}
TOML_NODISCARD_CTOR
explicit key(std::string&& k, source_region&& src = {}) noexcept //
: key_{ std::move(k) },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
explicit key(std::string&& k, const source_region& src) noexcept //
: key_{ std::move(k) },
source_{ src }
{}
TOML_NODISCARD_CTOR
explicit key(const char* k, source_region&& src = {}) //
: key_{ k },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
explicit key(const char* k, const source_region& src) //
: key_{ k },
source_{ src }
{}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD_CTOR
explicit key(std::wstring_view k, source_region&& src = {}) //
: key_{ impl::narrow(k) },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
explicit key(std::wstring_view k, const source_region& src) //
: key_{ impl::narrow(k) },
source_{ src }
{}
#endif
TOML_PURE_INLINE_GETTER
std::string_view str() const noexcept
{
return std::string_view{ key_ };
}
TOML_PURE_INLINE_GETTER
/*implicit*/ operator std::string_view() const noexcept
{
return str();
}
TOML_PURE_INLINE_GETTER
bool empty() const noexcept
{
return key_.empty();
}
TOML_PURE_INLINE_GETTER
const char* data() const noexcept
{
return key_.data();
}
TOML_PURE_INLINE_GETTER
size_t length() const noexcept
{
return key_.length();
}
TOML_PURE_INLINE_GETTER
const source_region& source() const noexcept
{
return source_;
}
TOML_PURE_INLINE_GETTER
friend bool operator==(const key& lhs, const key& rhs) noexcept
{
return lhs.key_ == rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(const key& lhs, const key& rhs) noexcept
{
return lhs.key_ != rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator<(const key& lhs, const key& rhs) noexcept
{
return lhs.key_ < rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator<=(const key& lhs, const key& rhs) noexcept
{
return lhs.key_ <= rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator>(const key& lhs, const key& rhs) noexcept
{
return lhs.key_ > rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator>=(const key& lhs, const key& rhs) noexcept
{
return lhs.key_ >= rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator==(const key& lhs, std::string_view rhs) noexcept
{
return lhs.key_ == rhs;
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(const key& lhs, std::string_view rhs) noexcept
{
return lhs.key_ != rhs;
}
TOML_PURE_INLINE_GETTER
friend bool operator<(const key& lhs, std::string_view rhs) noexcept
{
return lhs.key_ < rhs;
}
TOML_PURE_INLINE_GETTER
friend bool operator<=(const key& lhs, std::string_view rhs) noexcept
{
return lhs.key_ <= rhs;
}
TOML_PURE_INLINE_GETTER
friend bool operator>(const key& lhs, std::string_view rhs) noexcept
{
return lhs.key_ > rhs;
}
TOML_PURE_INLINE_GETTER
friend bool operator>=(const key& lhs, std::string_view rhs) noexcept
{
return lhs.key_ >= rhs;
}
TOML_PURE_INLINE_GETTER
friend bool operator==(std::string_view lhs, const key& rhs) noexcept
{
return lhs == rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(std::string_view lhs, const key& rhs) noexcept
{
return lhs != rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator<(std::string_view lhs, const key& rhs) noexcept
{
return lhs < rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator<=(std::string_view lhs, const key& rhs) noexcept
{
return lhs <= rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator>(std::string_view lhs, const key& rhs) noexcept
{
return lhs > rhs.key_;
}
TOML_PURE_INLINE_GETTER
friend bool operator>=(std::string_view lhs, const key& rhs) noexcept
{
return lhs >= rhs.key_;
}
using const_iterator = const char*;
using iterator = const_iterator;
TOML_PURE_INLINE_GETTER
const_iterator begin() const noexcept
{
return key_.data();
}
TOML_PURE_INLINE_GETTER
const_iterator end() const noexcept
{
return key_.data() + key_.length();
}
friend std::ostream& operator<<(std::ostream& lhs, const key& rhs)
{
impl::print_to_stream(lhs, rhs.key_);
return lhs;
}
};
template <typename T>
inline constexpr bool is_key = std::is_same_v<impl::remove_cvref<T>, toml::key>;
template <typename T>
inline constexpr bool is_key_or_convertible = is_key<T> //
|| impl::is_constructible_or_convertible<toml::key, T>;
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/std_map.h ********************************************************************************************
TOML_DISABLE_WARNINGS;
#include <map>
#include <iterator>
TOML_ENABLE_WARNINGS;
//******** impl/table.h **********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
template <bool IsConst>
struct table_proxy_pair
{
using value_type = std::conditional_t<IsConst, const node, node>;
const toml::key& first;
value_type& second;
};
template <bool IsConst>
class table_iterator
{
private:
template <bool>
friend class table_iterator;
using proxy_type = table_proxy_pair<IsConst>;
using mutable_map_iterator = std::map<toml::key, node_ptr, std::less<>>::iterator;
using const_map_iterator = std::map<toml::key, node_ptr, std::less<>>::const_iterator;
using map_iterator = std::conditional_t<IsConst, const_map_iterator, mutable_map_iterator>;
mutable map_iterator iter_;
alignas(proxy_type) mutable unsigned char proxy_[sizeof(proxy_type)];
mutable bool proxy_instantiated_ = false;
TOML_NODISCARD
proxy_type* get_proxy() const noexcept
{
if (!proxy_instantiated_)
{
auto p = ::new (static_cast<void*>(proxy_)) proxy_type{ iter_->first, *iter_->second.get() };
proxy_instantiated_ = true;
return p;
}
else
return TOML_LAUNDER(reinterpret_cast<proxy_type*>(proxy_));
}
public:
TOML_NODISCARD_CTOR
table_iterator() noexcept = default;
TOML_NODISCARD_CTOR
explicit table_iterator(mutable_map_iterator iter) noexcept //
: iter_{ iter }
{}
TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst)
TOML_NODISCARD_CTOR
explicit table_iterator(const_map_iterator iter) noexcept //
: iter_{ iter }
{}
TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst)
TOML_NODISCARD_CTOR
table_iterator(const table_iterator<false>& other) noexcept //
: iter_{ other.iter_ }
{}
TOML_NODISCARD_CTOR
table_iterator(const table_iterator& other) noexcept //
: iter_{ other.iter_ }
{}
table_iterator& operator=(const table_iterator& rhs) noexcept
{
iter_ = rhs.iter_;
proxy_instantiated_ = false;
return *this;
}
using value_type = table_proxy_pair<IsConst>;
using reference = value_type&;
using pointer = value_type*;
using difference_type = typename std::iterator_traits<map_iterator>::difference_type;
using iterator_category = typename std::iterator_traits<map_iterator>::iterator_category;
table_iterator& operator++() noexcept // ++pre
{
++iter_;
proxy_instantiated_ = false;
return *this;
}
table_iterator operator++(int) noexcept // post++
{
table_iterator out{ iter_ };
++iter_;
proxy_instantiated_ = false;
return out;
}
table_iterator& operator--() noexcept // --pre
{
--iter_;
proxy_instantiated_ = false;
return *this;
}
table_iterator operator--(int) noexcept // post--
{
table_iterator out{ iter_ };
--iter_;
proxy_instantiated_ = false;
return out;
}
TOML_PURE_INLINE_GETTER
reference operator*() const noexcept
{
return *get_proxy();
}
TOML_PURE_INLINE_GETTER
pointer operator->() const noexcept
{
return get_proxy();
}
TOML_PURE_INLINE_GETTER
explicit operator const map_iterator&() const noexcept
{
return iter_;
}
TOML_CONSTRAINED_TEMPLATE(!C, bool C = IsConst)
TOML_PURE_INLINE_GETTER
explicit operator const const_map_iterator() const noexcept
{
return iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator==(const table_iterator& lhs, const table_iterator& rhs) noexcept
{
return lhs.iter_ == rhs.iter_;
}
TOML_PURE_INLINE_GETTER
friend bool operator!=(const table_iterator& lhs, const table_iterator& rhs) noexcept
{
return lhs.iter_ != rhs.iter_;
}
};
struct table_init_pair
{
mutable toml::key key;
mutable node_ptr value;
template <typename K, typename V>
TOML_NODISCARD_CTOR
table_init_pair(K&& k, V&& v, value_flags flags = preserve_source_value_flags) //
: key{ static_cast<K&&>(k) },
value{ make_node(static_cast<V&&>(v), flags) }
{}
};
}
TOML_IMPL_NAMESPACE_END;
TOML_NAMESPACE_START
{
using table_iterator = POXY_IMPLEMENTATION_DETAIL(impl::table_iterator<false>);
using const_table_iterator = POXY_IMPLEMENTATION_DETAIL(impl::table_iterator<true>);
class TOML_EXPORTED_CLASS table : public node
{
private:
using map_type = std::map<toml::key, impl::node_ptr, std::less<>>;
using map_pair = std::pair<const toml::key, impl::node_ptr>;
using map_iterator = typename map_type::iterator;
using const_map_iterator = typename map_type::const_iterator;
map_type map_;
bool inline_ = false;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
table(const impl::table_init_pair*, const impl::table_init_pair*);
public:
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
table() noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
~table() noexcept;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
table(const table&);
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
table(table&& other) noexcept;
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
explicit table(std::initializer_list<impl::table_init_pair> kvps) //
: table(kvps.begin(), kvps.end())
{}
TOML_EXPORTED_MEMBER_FUNCTION
table& operator=(const table&);
TOML_EXPORTED_MEMBER_FUNCTION
table& operator=(table&& rhs) noexcept;
TOML_CONST_INLINE_GETTER
node_type type() const noexcept final
{
return node_type::table;
}
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
bool is_homogeneous(node_type ntype) const noexcept final;
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final;
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final;
template <typename ElemType = void>
TOML_PURE_GETTER
bool is_homogeneous() const noexcept
{
using type = impl::remove_cvref<impl::unwrap_node<ElemType>>;
static_assert(std::is_void_v<type> || toml::is_value<type> || toml::is_container<type>,
"The template type argument of table::is_homogeneous() must be void or one "
"of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
return is_homogeneous(impl::node_type_of<type>);
}
TOML_CONST_INLINE_GETTER
bool is_table() const noexcept final
{
return true;
}
TOML_CONST_INLINE_GETTER
bool is_array() const noexcept final
{
return false;
}
TOML_PURE_GETTER
bool is_array_of_tables() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_value() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_string() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_integer() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_floating_point() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_number() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_boolean() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_date() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_time() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
bool is_date_time() const noexcept final
{
return false;
}
TOML_CONST_INLINE_GETTER
table* as_table() noexcept final
{
return this;
}
TOML_CONST_INLINE_GETTER
array* as_array() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<std::string>* as_string() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<int64_t>* as_integer() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<double>* as_floating_point() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<bool>* as_boolean() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<date>* as_date() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<time>* as_time() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
toml::value<date_time>* as_date_time() noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const table* as_table() const noexcept final
{
return this;
}
TOML_CONST_INLINE_GETTER
const array* as_array() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<std::string>* as_string() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<int64_t>* as_integer() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<double>* as_floating_point() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<bool>* as_boolean() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<date>* as_date() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<time>* as_time() const noexcept final
{
return nullptr;
}
TOML_CONST_INLINE_GETTER
const toml::value<date_time>* as_date_time() const noexcept final
{
return nullptr;
}
TOML_PURE_INLINE_GETTER
bool is_inline() const noexcept
{
return inline_;
}
void is_inline(bool val) noexcept
{
inline_ = val;
}
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
node* get(std::string_view key) noexcept;
TOML_PURE_INLINE_GETTER
const node* get(std::string_view key) const noexcept
{
return const_cast<table&>(*this).get(key);
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node* get(std::wstring_view key)
{
if (empty())
return nullptr;
return get(impl::narrow(key));
}
TOML_NODISCARD
const node* get(std::wstring_view key) const
{
return const_cast<table&>(*this).get(key);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
template <typename T>
TOML_PURE_GETTER
impl::wrap_node<T>* get_as(std::string_view key) noexcept
{
const auto n = this->get(key);
return n ? n->template as<T>() : nullptr;
}
template <typename T>
TOML_PURE_GETTER
const impl::wrap_node<T>* get_as(std::string_view key) const noexcept
{
return const_cast<table&>(*this).template get_as<T>(key);
}
#if TOML_ENABLE_WINDOWS_COMPAT
template <typename T>
TOML_NODISCARD
impl::wrap_node<T>* get_as(std::wstring_view key)
{
if (empty())
return nullptr;
return get_as<T>(impl::narrow(key));
}
template <typename T>
TOML_NODISCARD
const impl::wrap_node<T>* get_as(std::wstring_view key) const
{
return const_cast<table&>(*this).template get_as<T>(key);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
node& at(std::string_view key);
TOML_NODISCARD
const node& at(std::string_view key) const
{
return const_cast<table&>(*this).at(key);
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node& at(std::wstring_view key)
{
return at(impl::narrow(key));
}
TOML_NODISCARD
const node& at(std::wstring_view key) const
{
return const_cast<table&>(*this).at(key);
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
using iterator = toml::table_iterator;
using const_iterator = toml::const_table_iterator;
TOML_PURE_INLINE_GETTER
iterator begin() noexcept
{
return iterator{ map_.begin() };
}
TOML_PURE_INLINE_GETTER
const_iterator begin() const noexcept
{
return const_iterator{ map_.cbegin() };
}
TOML_PURE_INLINE_GETTER
const_iterator cbegin() const noexcept
{
return const_iterator{ map_.cbegin() };
}
TOML_PURE_INLINE_GETTER
iterator end() noexcept
{
return iterator{ map_.end() };
}
TOML_PURE_INLINE_GETTER
const_iterator end() const noexcept
{
return const_iterator{ map_.cend() };
}
TOML_PURE_INLINE_GETTER
const_iterator cend() const noexcept
{
return const_iterator{ map_.cend() };
}
private:
template <typename T, typename Table>
using for_each_value_ref = impl::copy_cvref<impl::wrap_node<impl::remove_cvref<impl::unwrap_node<T>>>, Table>;
template <typename Func, typename Table, typename T>
static constexpr bool can_for_each = std::is_invocable_v<Func, const key&, for_each_value_ref<T, Table>> //
|| std::is_invocable_v<Func, for_each_value_ref<T, Table>>;
template <typename Func, typename Table, typename T>
static constexpr bool can_for_each_nothrow =
std::is_nothrow_invocable_v<Func, const key&, for_each_value_ref<T, Table>> //
|| std::is_nothrow_invocable_v<Func, for_each_value_ref<T, Table>>;
template <typename Func, typename Table>
static constexpr bool can_for_each_any = can_for_each<Func, Table, table> //
|| can_for_each<Func, Table, array> //
|| can_for_each<Func, Table, std::string> //
|| can_for_each<Func, Table, int64_t> //
|| can_for_each<Func, Table, double> //
|| can_for_each<Func, Table, bool> //
|| can_for_each<Func, Table, date> //
|| can_for_each<Func, Table, time> //
|| can_for_each<Func, Table, date_time>;
template <typename Func, typename Table, typename T>
static constexpr bool for_each_is_nothrow_one = !can_for_each<Func, Table, T> //
|| can_for_each_nothrow<Func, Table, T>;
// clang-format off
template <typename Func, typename Table>
static constexpr bool for_each_is_nothrow = for_each_is_nothrow_one<Func, Table, table> //
&& for_each_is_nothrow_one<Func, Table, array> //
&& for_each_is_nothrow_one<Func, Table, std::string> //
&& for_each_is_nothrow_one<Func, Table, int64_t> //
&& for_each_is_nothrow_one<Func, Table, double> //
&& for_each_is_nothrow_one<Func, Table, bool> //
&& for_each_is_nothrow_one<Func, Table, date> //
&& for_each_is_nothrow_one<Func, Table, time> //
&& for_each_is_nothrow_one<Func, Table, date_time>;
// clang-format on
template <typename Func, typename Table>
static void do_for_each(Func&& visitor, Table&& tbl) noexcept(for_each_is_nothrow<Func&&, Table&&>)
{
static_assert(can_for_each_any<Func&&, Table&&>,
"TOML table for_each visitors must be invocable for at least one of the toml::node "
"specializations:" TOML_SA_NODE_TYPE_LIST);
using kvp_type = impl::copy_cv<map_pair, std::remove_reference_t<Table>>;
for (kvp_type& kvp : tbl.map_)
{
using node_ref = impl::copy_cvref<toml::node, Table&&>;
static_assert(std::is_reference_v<node_ref>);
const auto keep_going =
static_cast<node_ref>(*kvp.second)
.visit(
[&](auto&& v)
#if !TOML_MSVC // MSVC thinks this is invalid syntax O_o
noexcept(for_each_is_nothrow_one<Func&&, Table&&, decltype(v)>)
#endif
{
using value_ref = for_each_value_ref<decltype(v), Table&&>;
static_assert(std::is_reference_v<value_ref>);
// func(key, val)
if constexpr (std::is_invocable_v<Func&&, const key&, value_ref>)
{
using return_type = decltype(static_cast<Func&&>(
visitor)(static_cast<const key&>(kvp.first), static_cast<value_ref>(v)));
if constexpr (impl::is_constructible_or_convertible<bool, return_type>)
{
return static_cast<bool>(static_cast<Func&&>(
visitor)(static_cast<const key&>(kvp.first), static_cast<value_ref>(v)));
}
else
{
static_cast<Func&&>(visitor)(static_cast<const key&>(kvp.first),
static_cast<value_ref>(v));
return true;
}
}
// func(val)
else if constexpr (std::is_invocable_v<Func&&, value_ref>)
{
using return_type =
decltype(static_cast<Func&&>(visitor)(static_cast<value_ref>(v)));
if constexpr (impl::is_constructible_or_convertible<bool, return_type>)
{
return static_cast<bool>(
static_cast<Func&&>(visitor)(static_cast<value_ref>(v)));
}
else
{
static_cast<Func&&>(visitor)(static_cast<value_ref>(v));
return true;
}
}
// visitor not compatible with this particular type
else
return true;
});
if (!keep_going)
return;
}
}
public:
template <typename Func>
table& for_each(Func&& visitor) & noexcept(for_each_is_nothrow<Func&&, table&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
table&& for_each(Func&& visitor) && noexcept(for_each_is_nothrow<Func&&, table&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<table&&>(*this));
return static_cast<table&&>(*this);
}
template <typename Func>
const table& for_each(Func&& visitor) const& noexcept(for_each_is_nothrow<Func&&, const table&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
const table&& for_each(Func&& visitor) const&& noexcept(for_each_is_nothrow<Func&&, const table&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<const table&&>(*this));
return static_cast<const table&&>(*this);
}
TOML_PURE_INLINE_GETTER
bool empty() const noexcept
{
return map_.empty();
}
TOML_PURE_INLINE_GETTER
size_t size() const noexcept
{
return map_.size();
}
private:
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
map_iterator get_lower_bound(std::string_view) noexcept;
public:
TOML_PURE_GETTER
iterator lower_bound(std::string_view key) noexcept
{
return iterator{ get_lower_bound(key) };
}
TOML_PURE_GETTER
const_iterator lower_bound(std::string_view key) const noexcept
{
return const_iterator{ const_cast<table&>(*this).get_lower_bound(key) };
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
iterator lower_bound(std::wstring_view key)
{
if (empty())
return end();
return lower_bound(impl::narrow(key));
}
TOML_NODISCARD
const_iterator lower_bound(std::wstring_view key) const
{
if (empty())
return end();
return lower_bound(impl::narrow(key));
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
iterator find(std::string_view key) noexcept;
TOML_PURE_GETTER
TOML_EXPORTED_MEMBER_FUNCTION
const_iterator find(std::string_view key) const noexcept;
TOML_PURE_GETTER
bool contains(std::string_view key) const noexcept
{
return get(key) != nullptr;
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
iterator find(std::wstring_view key)
{
if (empty())
return end();
return find(impl::narrow(key));
}
TOML_NODISCARD
const_iterator find(std::wstring_view key) const
{
return find(impl::narrow(key));
}
TOML_NODISCARD
bool contains(std::wstring_view key) const
{
return contains(impl::narrow(key));
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
private:
TOML_EXPORTED_MEMBER_FUNCTION
map_iterator erase(const_map_iterator) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
map_iterator erase(const_map_iterator, const_map_iterator) noexcept;
public:
iterator erase(iterator pos) noexcept
{
return iterator{ erase(const_map_iterator{ pos }) };
}
iterator erase(const_iterator pos) noexcept
{
return iterator{ erase(const_map_iterator{ pos }) };
}
iterator erase(const_iterator begin, const_iterator end) noexcept
{
return iterator{ erase(const_map_iterator{ begin }, const_map_iterator{ end }) };
}
TOML_EXPORTED_MEMBER_FUNCTION
size_t erase(std::string_view key) noexcept;
#if TOML_ENABLE_WINDOWS_COMPAT
size_t erase(std::wstring_view key)
{
if (empty())
return false;
return erase(impl::narrow(key));
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
TOML_EXPORTED_MEMBER_FUNCTION
table& prune(bool recursive = true) & noexcept;
table&& prune(bool recursive = true) && noexcept
{
return static_cast<toml::table&&>(this->prune(recursive));
}
TOML_EXPORTED_MEMBER_FUNCTION
void clear() noexcept;
private:
TOML_EXPORTED_MEMBER_FUNCTION
map_iterator insert_with_hint(const_iterator, key&&, impl::node_ptr&&);
public:
TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>),
typename ValueType = void,
typename KeyType,
typename... ValueArgs)
iterator emplace_hint(const_iterator hint, KeyType&& key, ValueArgs&&... args)
{
static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT,
"Emplacement using wide-character keys is only supported on Windows with "
"TOML_ENABLE_WINDOWS_COMPAT enabled.");
using raw_value_type = impl::remove_cvref<ValueType>;
using value_type = std::
conditional_t<std::is_void_v<raw_value_type>, impl::emplaced_type_of<ValueArgs&&...>, raw_value_type>;
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
return emplace_hint<value_type>(hint,
impl::narrow(static_cast<KeyType&&>(key)),
static_cast<ValueArgs&&>(args)...);
#else
static_assert(impl::dependent_false<KeyType>, "Evaluated unreachable branch!");
#endif
}
else
{
static constexpr auto moving_node_ptr = std::is_same_v<value_type, impl::node_ptr> //
&& sizeof...(ValueArgs) == 1u //
&& impl::first_is_same<impl::node_ptr&&, ValueArgs&&...>;
using unwrapped_type = impl::remove_cvref<impl::unwrap_node<value_type>>;
static_assert(moving_node_ptr //
|| impl::is_native<unwrapped_type> //
|| impl::is_one_of<unwrapped_type, table, array>, //
"ValueType argument of table::emplace_hint() must be one "
"of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
map_iterator ipos = insert_with_hint(hint, toml::key{ static_cast<KeyType&&>(key) }, nullptr);
// if second is nullptr then we successully claimed the key and inserted the empty sentinel,
// so now we have to construct the actual value
if (!ipos->second)
{
if constexpr (moving_node_ptr)
ipos->second = std::move(static_cast<ValueArgs&&>(args)...);
else
{
#if TOML_COMPILER_HAS_EXCEPTIONS
try
{
#endif
ipos->second.reset(
new impl::wrap_node<unwrapped_type>{ static_cast<ValueArgs&&>(args)... });
#if TOML_COMPILER_HAS_EXCEPTIONS
}
catch (...)
{
erase(const_map_iterator{ ipos }); // strong exception guarantee
throw;
}
#endif
}
}
return iterator{ ipos };
}
}
TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>),
typename KeyType,
typename ValueType)
std::pair<iterator, bool> insert(KeyType&& key,
ValueType&& val,
value_flags flags = preserve_source_value_flags)
{
static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT,
"Insertion using wide-character keys is only supported on Windows with "
"TOML_ENABLE_WINDOWS_COMPAT enabled.");
if constexpr (is_node_view<ValueType>)
{
if (!val)
return { end(), false };
}
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
return insert(impl::narrow(static_cast<KeyType&&>(key)), static_cast<ValueType&&>(val), flags);
#else
static_assert(impl::dependent_false<KeyType>, "Evaluated unreachable branch!");
#endif
}
else
{
const auto key_view = std::string_view{ key };
map_iterator ipos = get_lower_bound(key_view);
if (ipos == map_.end() || ipos->first != key_view)
{
ipos = insert_with_hint(const_iterator{ ipos },
toml::key{ static_cast<KeyType&&>(key) },
impl::make_node(static_cast<ValueType&&>(val), flags));
return { iterator{ ipos }, true };
}
return { iterator{ ipos }, false };
}
}
TOML_CONSTRAINED_TEMPLATE((!is_key_or_convertible<Iter> && !impl::is_wide_string<Iter>), typename Iter)
void insert(Iter begin, Iter end, value_flags flags = preserve_source_value_flags)
{
if (begin == end)
return;
for (auto it = begin; it != end; it++)
{
if constexpr (std::is_rvalue_reference_v<decltype(*it)>)
insert(std::move((*it).first), std::move((*it).second), flags);
else
insert((*it).first, (*it).second, flags);
}
}
TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>),
typename KeyType,
typename ValueType)
std::pair<iterator, bool> insert_or_assign(KeyType&& key,
ValueType&& val,
value_flags flags = preserve_source_value_flags)
{
static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT,
"Insertion using wide-character keys is only supported on Windows with "
"TOML_ENABLE_WINDOWS_COMPAT enabled.");
if constexpr (is_node_view<ValueType>)
{
if (!val)
return { end(), false };
}
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
return insert_or_assign(impl::narrow(static_cast<KeyType&&>(key)),
static_cast<ValueType&&>(val),
flags);
#else
static_assert(impl::dependent_false<KeyType>, "Evaluated unreachable branch!");
#endif
}
else
{
const auto key_view = std::string_view{ key };
map_iterator ipos = get_lower_bound(key_view);
if (ipos == map_.end() || ipos->first != key_view)
{
ipos = insert_with_hint(const_iterator{ ipos },
toml::key{ static_cast<KeyType&&>(key) },
impl::make_node(static_cast<ValueType&&>(val), flags));
return { iterator{ ipos }, true };
}
else
{
(*ipos).second = impl::make_node(static_cast<ValueType&&>(val), flags);
return { iterator{ ipos }, false };
}
}
}
TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>),
typename ValueType = void,
typename KeyType,
typename... ValueArgs)
std::pair<iterator, bool> emplace(KeyType&& key, ValueArgs&&... args)
{
static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT,
"Emplacement using wide-character keys is only supported on Windows with "
"TOML_ENABLE_WINDOWS_COMPAT enabled.");
using raw_value_type = impl::remove_cvref<ValueType>;
using value_type = std::
conditional_t<std::is_void_v<raw_value_type>, impl::emplaced_type_of<ValueArgs&&...>, raw_value_type>;
if constexpr (impl::is_wide_string<KeyType>)
{
#if TOML_ENABLE_WINDOWS_COMPAT
return emplace<value_type>(impl::narrow(static_cast<KeyType&&>(key)),
static_cast<ValueArgs&&>(args)...);
#else
static_assert(impl::dependent_false<KeyType>, "Evaluated unreachable branch!");
#endif
}
else
{
using unwrapped_type = impl::remove_cvref<impl::unwrap_node<value_type>>;
static_assert((impl::is_native<unwrapped_type> || impl::is_one_of<unwrapped_type, table, array>),
"ValueType argument of table::emplace() must be one "
"of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST);
const auto key_view = std::string_view{ key };
auto ipos = get_lower_bound(key_view);
if (ipos == map_.end() || ipos->first != key_view)
{
ipos = insert_with_hint(
const_iterator{ ipos },
toml::key{ static_cast<KeyType&&>(key) },
impl::node_ptr{ new impl::wrap_node<unwrapped_type>{ static_cast<ValueArgs&&>(args)... } });
return { iterator{ ipos }, true };
}
return { iterator{ ipos }, false };
}
}
using node::operator[]; // inherit operator[toml::path]
TOML_NODISCARD
node_view<node> operator[](std::string_view key) noexcept
{
return node_view<node>{ get(key) };
}
TOML_NODISCARD
node_view<const node> operator[](std::string_view key) const noexcept
{
return node_view<const node>{ get(key) };
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view<node> operator[](std::wstring_view key)
{
return node_view<node>{ get(key) };
}
TOML_NODISCARD
node_view<const node> operator[](std::wstring_view key) const
{
return node_view<const node>{ get(key) };
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
private:
TOML_PURE_GETTER
TOML_EXPORTED_STATIC_FUNCTION
static bool TOML_CALLCONV equal(const table&, const table&) noexcept;
public:
TOML_NODISCARD
friend bool operator==(const table& lhs, const table& rhs) noexcept
{
return equal(lhs, rhs);
}
TOML_NODISCARD
friend bool operator!=(const table& lhs, const table& rhs) noexcept
{
return !equal(lhs, rhs);
}
#if TOML_ENABLE_FORMATTERS
friend std::ostream& operator<<(std::ostream& lhs, const table& rhs)
{
impl::print_to_stream(lhs, rhs);
return lhs;
}
#endif
};
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/unicode_autogenerated.h ******************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
#if TOML_GCC && TOML_GCC < 9
#pragma GCC push_options
#pragma GCC optimize("O1") // codegen bugs
#endif
// the functions in this namespace block are automatically generated by a tool - they are not meant to be hand-edited
TOML_IMPL_NAMESPACE_START
{
TOML_CONST_GETTER
constexpr bool is_ascii_horizontal_whitespace(char32_t c) noexcept
{
return c == U'\t' || c == U' ';
}
TOML_CONST_GETTER
constexpr bool is_non_ascii_horizontal_whitespace(char32_t c) noexcept
{
// 20 code units from 8 ranges (spanning a search area of 65120)
if (c < U'\xA0' || c > U'\uFEFF')
return false;
const auto child_index_0 = (static_cast<uint_least64_t>(c) - 0xA0ull) / 0x3FAull;
if ((1ull << child_index_0) & 0x7FFFFFFFFFFFF75Eull)
return false;
if (c == U'\xA0' || c == U'\u3000' || c == U'\uFEFF')
return true;
switch (child_index_0)
{
case 0x05: return c == U'\u1680' || c == U'\u180E';
case 0x07:
return (U'\u2000' <= c && c <= U'\u200B') || (U'\u205F' <= c && c <= U'\u2060') || c == U'\u202F';
default: TOML_UNREACHABLE;
}
TOML_UNREACHABLE;
}
TOML_CONST_GETTER
constexpr bool is_ascii_vertical_whitespace(char32_t c) noexcept
{
return c >= U'\n' && c <= U'\r';
}
TOML_CONST_GETTER
constexpr bool is_non_ascii_vertical_whitespace(char32_t c) noexcept
{
return (U'\u2028' <= c && c <= U'\u2029') || c == U'\x85';
}
TOML_CONST_GETTER
constexpr bool is_ascii_bare_key_character(char32_t c) noexcept
{
#if TOML_LANG_UNRELEASED // toml/issues/644 ('+' in bare keys)
if TOML_UNLIKELY(c == U'+')
return true;
#endif
// 64 code units from 5 ranges (spanning a search area of 78)
if (c < U'-' || c > U'z')
return false;
return (((static_cast<uint_least64_t>(c) - 0x2Dull) / 0x40ull) != 0ull)
|| ((1ull << (static_cast<uint_least64_t>(c) - 0x2Dull)) & 0xFFF43FFFFFF01FF9ull);
}
#if TOML_LANG_UNRELEASED // toml/pull/891 (unicode bare keys)
TOML_CONST_GETTER
constexpr bool is_non_ascii_bare_key_character(char32_t c) noexcept
{
// 971732 code units from 16 ranges (spanning a search area of 982862)
if (c < U'\xB2' || c > U'\U000EFFFF')
return false;
const auto child_index_0 = (static_cast<uint_least64_t>(c) - 0xB2ull) / 0x3BFEull;
if ((1ull << child_index_0) & 0xFFFFFFFFFFFFFFE6ull)
return true;
switch (child_index_0)
{
case 0x00: // [0] 00B2 - 3CAF
{
// 12710 code units from 13 ranges (spanning a search area of 15358)
TOML_ASSUME(c >= U'\xB2' && c <= U'\u3CAF');
constexpr uint_least64_t bitmask_table_1[] = {
0xFFFFFFDFFFFFDC83u, 0xFFFFFFFFFFFFFFDFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFEFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0x000000000C003FFFu, 0xC000000000006000u, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0x000000003FFFFFFFu,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0xFFFFC00000000000u, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0x0000000000003FFFu, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u,
0x0000000000000000u, 0xFFFFFFFFFFFFC000u, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0x3FFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFF8000u, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu,
0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0xFFFFFFFFFFFFFFFFu, 0x3FFFFFFFFFFFFFFFu,
};
return bitmask_table_1[(static_cast<uint_least64_t>(c) - 0xB2ull) / 0x40ull]
& (0x1ull << ((static_cast<uint_least64_t>(c) - 0xB2ull) % 0x40ull));
}
case 0x03: return c <= U'\uD7FF';
case 0x04:
return (U'\uF900' <= c && c <= U'\uFDCF') || (U'\uFDF0' <= c && c <= U'\uFFFD') || U'\U00010000' <= c;
default: TOML_UNREACHABLE;
}
TOML_UNREACHABLE;
}
#endif // TOML_LANG_UNRELEASED
}
TOML_IMPL_NAMESPACE_END;
#if TOML_GCC && TOML_GCC < 9
#pragma GCC pop_options
#endif
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/unicode.h ********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
TOML_CONST_GETTER
constexpr bool is_string_delimiter(char32_t c) noexcept
{
return c == U'"' || c == U'\'';
}
TOML_CONST_GETTER
constexpr bool is_ascii_letter(char32_t c) noexcept
{
return (c >= U'a' && c <= U'z') || (c >= U'A' && c <= U'Z');
}
TOML_CONST_GETTER
constexpr bool is_binary_digit(char32_t c) noexcept
{
return c == U'0' || c == U'1';
}
TOML_CONST_GETTER
constexpr bool is_octal_digit(char32_t c) noexcept
{
return (c >= U'0' && c <= U'7');
}
TOML_CONST_GETTER
constexpr bool is_decimal_digit(char32_t c) noexcept
{
return (c >= U'0' && c <= U'9');
}
TOML_CONST_GETTER
constexpr bool is_hexadecimal_digit(char32_t c) noexcept
{
return U'0' <= c && c <= U'f' && (1ull << (static_cast<uint_least64_t>(c) - 0x30u)) & 0x7E0000007E03FFull;
}
template <typename T>
TOML_CONST_GETTER
constexpr uint_least32_t hex_to_dec(const T c) noexcept
{
if constexpr (std::is_same_v<remove_cvref<T>, uint_least32_t>)
return c >= 0x41u // >= 'A'
? 10u + (c | 0x20u) - 0x61u // - 'a'
: c - 0x30u // - '0'
;
else
return hex_to_dec(static_cast<uint_least32_t>(c));
}
TOML_CONST_GETTER
constexpr bool is_horizontal_whitespace(char32_t c) noexcept
{
return is_ascii_horizontal_whitespace(c) || is_non_ascii_horizontal_whitespace(c);
}
TOML_CONST_GETTER
constexpr bool is_vertical_whitespace(char32_t c) noexcept
{
return is_ascii_vertical_whitespace(c) || is_non_ascii_vertical_whitespace(c);
}
TOML_CONST_GETTER
constexpr bool is_whitespace(char32_t c) noexcept
{
return is_horizontal_whitespace(c) || is_vertical_whitespace(c);
}
TOML_CONST_GETTER
constexpr bool is_bare_key_character(char32_t c) noexcept
{
return is_ascii_bare_key_character(c)
#if TOML_LANG_UNRELEASED // toml/pull/891 (unicode bare keys)
|| is_non_ascii_bare_key_character(c)
#endif
;
}
TOML_CONST_GETTER
constexpr bool is_value_terminator(char32_t c) noexcept
{
return is_whitespace(c) || c == U']' || c == U'}' || c == U',' || c == U'#';
}
TOML_CONST_GETTER
constexpr bool is_control_character(char c) noexcept
{
return c <= '\u001F' || c == '\u007F';
}
TOML_CONST_GETTER
constexpr bool is_control_character(char32_t c) noexcept
{
return c <= U'\u001F' || c == U'\u007F';
}
TOML_CONST_GETTER
constexpr bool is_nontab_control_character(char32_t c) noexcept
{
return c <= U'\u0008' || (c >= U'\u000A' && c <= U'\u001F') || c == U'\u007F';
}
TOML_CONST_GETTER
constexpr bool is_unicode_surrogate(char32_t c) noexcept
{
return c >= 0xD800u && c <= 0xDFFF;
}
struct utf8_decoder
{
// utf8_decoder based on this: https://bjoern.hoehrmann.de/utf-8/decoder/dfa/
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
uint_least32_t state{};
char32_t codepoint{};
static constexpr uint8_t state_table[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6,
6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 12,
12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12,
12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 12, 12, 36, 12,
36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12
};
TOML_PURE_INLINE_GETTER
constexpr bool error() const noexcept
{
return state == uint_least32_t{ 12u };
}
TOML_PURE_INLINE_GETTER
constexpr bool has_code_point() const noexcept
{
return state == uint_least32_t{};
}
TOML_PURE_INLINE_GETTER
constexpr bool needs_more_input() const noexcept
{
return !has_code_point() && !error();
}
constexpr void operator()(uint8_t byte) noexcept
{
TOML_ASSERT_ASSUME(!error());
const auto type = state_table[byte];
codepoint = static_cast<char32_t>(has_code_point() ? (uint_least32_t{ 255u } >> type) & byte
: (byte& uint_least32_t{ 63u })
| (static_cast<uint_least32_t>(codepoint) << 6));
state = state_table[state + uint_least32_t{ 256u } + type];
}
TOML_ALWAYS_INLINE
constexpr void operator()(char c) noexcept
{
operator()(static_cast<uint8_t>(c));
}
TOML_ALWAYS_INLINE
constexpr void reset() noexcept
{
state = {};
}
};
TOML_PURE_GETTER
TOML_ATTR(nonnull)
bool is_ascii(const char* str, size_t len) noexcept;
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/parse_error.h ****************************************************************************************
#if TOML_ENABLE_PARSER
//******** impl/std_except.h *****************************************************************************************
TOML_DISABLE_WARNINGS;
#if TOML_EXCEPTIONS
#include <stdexcept>
#endif
TOML_ENABLE_WARNINGS;
//******** impl/parse_error.h ****************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
#if TOML_DOXYGEN || !TOML_EXCEPTIONS
#define TOML_PARSE_ERROR_BASE
#else
#define TOML_PARSE_ERROR_BASE : public std::runtime_error
#endif
TOML_NAMESPACE_START
{
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
class parse_error TOML_PARSE_ERROR_BASE
{
private:
#if !TOML_EXCEPTIONS
std::string description_;
#endif
source_region source_;
public:
#if TOML_EXCEPTIONS
TOML_NODISCARD_CTOR
TOML_ATTR(nonnull)
parse_error(const char* desc, source_region&& src) noexcept //
: std::runtime_error{ desc },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
TOML_ATTR(nonnull)
parse_error(const char* desc, const source_region& src) noexcept //
: parse_error{ desc, source_region{ src } }
{}
TOML_NODISCARD_CTOR
TOML_ATTR(nonnull)
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ desc, source_region{ position, position, path } }
{}
#else
TOML_NODISCARD_CTOR
parse_error(std::string&& desc, source_region&& src) noexcept //
: description_{ std::move(desc) },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
parse_error(std::string&& desc, const source_region& src) noexcept //
: parse_error{ std::move(desc), source_region{ src } }
{}
TOML_NODISCARD_CTOR
parse_error(std::string&& desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ std::move(desc), source_region{ position, position, path } }
{}
#endif
TOML_NODISCARD
std::string_view description() const noexcept
{
#if TOML_EXCEPTIONS
return std::string_view{ what() };
#else
return description_;
#endif
}
TOML_NODISCARD
const source_region& source() const noexcept
{
return source_;
}
friend std::ostream& operator<<(std::ostream& lhs, const parse_error& rhs)
{
impl::print_to_stream(lhs, rhs.description());
impl::print_to_stream(lhs, "\n\t(error occurred at "sv);
impl::print_to_stream(lhs, rhs.source());
impl::print_to_stream(lhs, ")"sv);
return lhs;
}
};
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
}
TOML_NAMESPACE_END;
#undef TOML_PARSE_ERROR_BASE
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_PARSER
//******** impl/parse_result.h ***************************************************************************************
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
TOML_ABI_NAMESPACE_START(noex);
class parse_result
{
private:
struct storage_t
{
static constexpr size_t size =
(sizeof(toml::table) < sizeof(parse_error) ? sizeof(parse_error) : sizeof(toml::table));
static constexpr size_t align =
(alignof(toml::table) < alignof(parse_error) ? alignof(parse_error) : alignof(toml::table));
alignas(align) unsigned char bytes[size];
};
alignas(storage_t::align) mutable storage_t storage_;
bool err_;
template <typename Type>
TOML_NODISCARD
TOML_ALWAYS_INLINE
static Type* get_as(storage_t& s) noexcept
{
return TOML_LAUNDER(reinterpret_cast<Type*>(s.bytes));
}
void destroy() noexcept
{
if (err_)
get_as<parse_error>(storage_)->~parse_error();
else
get_as<toml::table>(storage_)->~table();
}
public:
TOML_NODISCARD_CTOR
parse_result() noexcept //
: err_{ true }
{
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::string{}, source_region{} };
}
TOML_NODISCARD_CTOR
explicit parse_result(toml::table&& tbl) noexcept //
: err_{ false }
{
::new (static_cast<void*>(storage_.bytes)) toml::table{ std::move(tbl) };
}
TOML_NODISCARD_CTOR
explicit parse_result(parse_error&& err) noexcept //
: err_{ true }
{
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::move(err) };
}
TOML_NODISCARD_CTOR
parse_result(parse_result&& res) noexcept //
: err_{ res.err_ }
{
if (err_)
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::move(res).error() };
else
::new (static_cast<void*>(storage_.bytes)) toml::table{ std::move(res).table() };
}
parse_result& operator=(parse_result&& rhs) noexcept
{
if (err_ != rhs.err_)
{
destroy();
err_ = rhs.err_;
if (err_)
::new (static_cast<void*>(storage_.bytes)) parse_error{ std::move(rhs).error() };
else
::new (static_cast<void*>(storage_.bytes)) toml::table{ std::move(rhs).table() };
}
else
{
if (err_)
error() = std::move(rhs).error();
else
table() = std::move(rhs).table();
}
return *this;
}
~parse_result() noexcept
{
destroy();
}
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
/* 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();
}
using iterator = table_iterator;
using const_iterator = const_table_iterator;
TOML_NODISCARD
table_iterator begin() noexcept
{
return err_ ? table_iterator{} : table().begin();
}
TOML_NODISCARD
const_table_iterator begin() const noexcept
{
return err_ ? const_table_iterator{} : table().begin();
}
TOML_NODISCARD
const_table_iterator cbegin() const noexcept
{
return err_ ? const_table_iterator{} : table().cbegin();
}
TOML_NODISCARD
table_iterator end() noexcept
{
return err_ ? table_iterator{} : table().end();
}
TOML_NODISCARD
const_table_iterator end() const noexcept
{
return err_ ? const_table_iterator{} : table().end();
}
TOML_NODISCARD
const_table_iterator cend() const noexcept
{
return err_ ? const_table_iterator{} : table().cend();
}
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);
}
TOML_NODISCARD
node_view<node> at_path(const toml::path& path) noexcept
{
return err_ ? node_view<node>{} : table().at_path(path);
}
TOML_NODISCARD
node_view<const node> at_path(const toml::path& path) const noexcept
{
return err_ ? node_view<const node>{} : table().at_path(path);
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view<node> at_path(std::wstring_view path)
{
return err_ ? node_view<node>{} : table().at_path(path);
}
TOML_NODISCARD
node_view<const node> at_path(std::wstring_view path) const
{
return err_ ? node_view<const node>{} : table().at_path(path);
}
#endif
TOML_NODISCARD
node_view<node> operator[](const toml::path& path) noexcept
{
return err_ ? node_view<node>{} : table()[path];
}
TOML_NODISCARD
node_view<const node> operator[](const toml::path& path) const noexcept
{
return err_ ? node_view<const node>{} : table()[path];
}
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];
}
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
node_view<node> operator[](std::wstring_view key)
{
return err_ ? node_view<node>{} : table()[key];
}
TOML_NODISCARD
node_view<const node> operator[](std::wstring_view key) const
{
return err_ ? node_view<const node>{} : table()[key];
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
#if TOML_ENABLE_FORMATTERS
friend std::ostream& operator<<(std::ostream& os, const parse_result& result)
{
return result.err_ ? (os << result.error()) : (os << result.table());
}
#endif
};
TOML_ABI_NAMESPACE_END;
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
//******** impl/parser.h *********************************************************************************************
#if TOML_ENABLE_PARSER
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::string_view doc, std::string_view source_path = {});
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::string_view doc, std::string && source_path);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse_file(std::string_view file_path);
#if TOML_HAS_CHAR8
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string_view source_path = {});
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::u8string_view doc, std::string && source_path);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse_file(std::u8string_view file_path);
#endif // TOML_HAS_CHAR8
#if TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::string_view doc, std::wstring_view source_path);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::istream & doc, std::wstring_view source_path);
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse_file(std::wstring_view file_path);
#endif // TOML_ENABLE_WINDOWS_COMPAT
#if TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::u8string_view doc, std::wstring_view source_path);
#endif // TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::istream & doc, std::string_view source_path = {});
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
parse_result TOML_CALLCONV parse(std::istream & doc, std::string && source_path);
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
inline namespace literals
{
TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, lit_ex, lit_noex);
TOML_NODISCARD
TOML_ALWAYS_INLINE
parse_result operator"" _toml(const char* str, size_t len)
{
return parse(std::string_view{ str, len });
}
#if TOML_HAS_CHAR8
TOML_NODISCARD
TOML_ALWAYS_INLINE
parse_result operator"" _toml(const char8_t* str, size_t len)
{
return parse(std::u8string_view{ str, len });
}
#endif // TOML_HAS_CHAR8
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
}
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_PARSER
//******** impl/formatter.h ******************************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
struct formatter_constants
{
format_flags mandatory_flags;
format_flags ignored_flags;
std::string_view float_pos_inf;
std::string_view float_neg_inf;
std::string_view float_nan;
std::string_view bool_true;
std::string_view bool_false;
};
struct formatter_config
{
format_flags flags;
std::string_view indent;
};
class TOML_EXPORTED_CLASS formatter
{
private:
const node* source_;
#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
const parse_result* result_;
#endif
const formatter_constants* constants_;
formatter_config config_;
size_t indent_columns_;
format_flags int_format_mask_;
std::ostream* stream_; //
int indent_; // these are set in attach()
bool naked_newline_; //
protected:
TOML_PURE_INLINE_GETTER
const node& source() const noexcept
{
return *source_;
}
TOML_PURE_INLINE_GETTER
std::ostream& stream() const noexcept
{
return *stream_;
}
TOML_PURE_INLINE_GETTER
int indent() const noexcept
{
return indent_;
}
void indent(int level) noexcept
{
indent_ = level;
}
void increase_indent() noexcept
{
indent_++;
}
void decrease_indent() noexcept
{
indent_--;
}
TOML_PURE_INLINE_GETTER
size_t indent_columns() const noexcept
{
return indent_columns_;
}
TOML_PURE_INLINE_GETTER
bool indent_array_elements() const noexcept
{
return !!(config_.flags & format_flags::indent_array_elements);
}
TOML_PURE_INLINE_GETTER
bool indent_sub_tables() const noexcept
{
return !!(config_.flags & format_flags::indent_sub_tables);
}
TOML_PURE_INLINE_GETTER
bool literal_strings_allowed() const noexcept
{
return !!(config_.flags & format_flags::allow_literal_strings);
}
TOML_PURE_INLINE_GETTER
bool multi_line_strings_allowed() const noexcept
{
return !!(config_.flags & format_flags::allow_multi_line_strings);
}
TOML_PURE_INLINE_GETTER
bool real_tabs_in_strings_allowed() const noexcept
{
return !!(config_.flags & format_flags::allow_real_tabs_in_strings);
}
TOML_PURE_INLINE_GETTER
bool unicode_strings_allowed() const noexcept
{
return !!(config_.flags & format_flags::allow_unicode_strings);
}
TOML_PURE_INLINE_GETTER
bool terse_kvps() const noexcept
{
return !!(config_.flags & format_flags::terse_key_value_pairs);
}
TOML_EXPORTED_MEMBER_FUNCTION
void attach(std::ostream& stream) noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
void detach() noexcept;
TOML_EXPORTED_MEMBER_FUNCTION
void print_newline(bool force = false);
TOML_EXPORTED_MEMBER_FUNCTION
void print_indent();
TOML_EXPORTED_MEMBER_FUNCTION
void print_unformatted(char);
TOML_EXPORTED_MEMBER_FUNCTION
void print_unformatted(std::string_view);
TOML_EXPORTED_MEMBER_FUNCTION
void print_string(std::string_view str, bool allow_multi_line = true, bool allow_bare = false);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<std::string>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<int64_t>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<double>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<bool>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<date>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<time>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<date_time>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print_value(const node&, node_type);
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
bool dump_failed_parse_result();
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
formatter(const node*, const parse_result*, const formatter_constants&, const formatter_config&) noexcept;
};
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
//******** impl/toml_formatter.h *************************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
class TOML_EXPORTED_CLASS toml_formatter : impl::formatter
{
private:
using base = impl::formatter;
std::vector<const key*> key_path_;
bool pending_table_separator_ = false;
TOML_EXPORTED_MEMBER_FUNCTION
void print_pending_table_separator();
TOML_EXPORTED_MEMBER_FUNCTION
void print(const key&);
TOML_EXPORTED_MEMBER_FUNCTION
void print_inline(const toml::table&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const toml::array&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const toml::table&);
TOML_EXPORTED_MEMBER_FUNCTION
void print();
static constexpr impl::formatter_constants constants = { format_flags::none, // mandatory
format_flags::none, // ignored
"inf"sv,
"-inf"sv,
"nan"sv,
"true"sv,
"false"sv };
public:
static constexpr format_flags default_flags = constants.mandatory_flags //
| format_flags::allow_literal_strings //
| format_flags::allow_multi_line_strings //
| format_flags::allow_unicode_strings //
| format_flags::allow_real_tabs_in_strings //
| format_flags::allow_binary_integers //
| format_flags::allow_octal_integers //
| format_flags::allow_hexadecimal_integers //
| format_flags::indentation;
TOML_NODISCARD_CTOR
explicit toml_formatter(const toml::node& source, format_flags flags = default_flags) noexcept
: base{ &source, nullptr, constants, { flags, " "sv } }
{}
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
TOML_NODISCARD_CTOR
explicit toml_formatter(const toml::parse_result& result, format_flags flags = default_flags) noexcept
: base{ nullptr, &result, constants, { flags, " "sv } }
{}
#endif
friend std::ostream& operator<<(std::ostream& lhs, toml_formatter& rhs)
{
rhs.attach(lhs);
rhs.key_path_.clear();
rhs.print();
rhs.detach();
return lhs;
}
friend std::ostream& operator<<(std::ostream& lhs, toml_formatter&& rhs)
{
return lhs << rhs; // as lvalue
}
};
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
//******** impl/json_formatter.h *************************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
class TOML_EXPORTED_CLASS json_formatter : impl::formatter
{
private:
using base = impl::formatter;
TOML_EXPORTED_MEMBER_FUNCTION
void print(const toml::table&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const toml::array&);
TOML_EXPORTED_MEMBER_FUNCTION
void print();
static constexpr impl::formatter_constants constants = {
format_flags::quote_dates_and_times, // mandatory
format_flags::allow_literal_strings | format_flags::allow_multi_line_strings, // ignored
"Infinity"sv,
"-Infinity"sv,
"NaN"sv,
"true"sv,
"false"sv
};
public:
static constexpr format_flags default_flags = constants.mandatory_flags //
| format_flags::quote_infinities_and_nans //
| format_flags::allow_unicode_strings //
| format_flags::indentation;
TOML_NODISCARD_CTOR
explicit json_formatter(const toml::node& source, format_flags flags = default_flags) noexcept
: base{ &source, nullptr, constants, { flags, " "sv } }
{}
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
TOML_NODISCARD_CTOR
explicit json_formatter(const toml::parse_result& result, format_flags flags = default_flags) noexcept
: base{ nullptr, &result, constants, { flags, " "sv } }
{}
#endif
friend std::ostream& operator<<(std::ostream& lhs, json_formatter& rhs)
{
rhs.attach(lhs);
rhs.print();
rhs.detach();
return lhs;
}
friend std::ostream& operator<<(std::ostream& lhs, json_formatter&& rhs)
{
return lhs << rhs; // as lvalue
}
};
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
//******** impl/yaml_formatter.h *************************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
class TOML_EXPORTED_CLASS yaml_formatter : impl::formatter
{
private:
using base = impl::formatter;
TOML_EXPORTED_MEMBER_FUNCTION
void print_yaml_string(const value<std::string>&);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const toml::table&, bool = false);
TOML_EXPORTED_MEMBER_FUNCTION
void print(const toml::array&, bool = false);
TOML_EXPORTED_MEMBER_FUNCTION
void print();
static constexpr impl::formatter_constants constants = {
//
format_flags::quote_dates_and_times | format_flags::indentation, // mandatory
format_flags::allow_multi_line_strings, // ignored
".inf"sv,
"-.inf"sv,
".NAN"sv,
"true"sv,
"false"sv
};
public:
static constexpr format_flags default_flags = constants.mandatory_flags //
| format_flags::allow_literal_strings //
| format_flags::allow_unicode_strings //
| format_flags::allow_octal_integers //
| format_flags::allow_hexadecimal_integers;
TOML_NODISCARD_CTOR
explicit yaml_formatter(const toml::node& source, format_flags flags = default_flags) noexcept
: base{ &source, nullptr, constants, { flags, " "sv } }
{}
#if TOML_DOXYGEN || (TOML_ENABLE_PARSER && !TOML_EXCEPTIONS)
TOML_NODISCARD_CTOR
explicit yaml_formatter(const toml::parse_result& result, format_flags flags = default_flags) noexcept
: base{ nullptr, &result, constants, { flags, " "sv } }
{}
#endif
friend std::ostream& TOML_CALLCONV operator<<(std::ostream& lhs, yaml_formatter& rhs)
{
rhs.attach(lhs);
rhs.print();
rhs.detach();
return lhs;
}
friend std::ostream& TOML_CALLCONV operator<<(std::ostream& lhs, yaml_formatter&& rhs)
{
return lhs << rhs; // as lvalue
}
};
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
#if TOML_IMPLEMENTATION
//******** impl/std_string.inl ***************************************************************************************
#if TOML_WINDOWS
#ifndef _WINDOWS_
#if TOML_INCLUDE_WINDOWS_H
#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 MultiByteToWideChar(unsigned int CodePage,
unsigned long dwFlags,
const char* lpMultiByteStr,
int cbMultiByte,
wchar_t* lpWideCharStr,
int cchWideChar);
#endif // TOML_INCLUDE_WINDOWS_H
#endif // _WINDOWS_
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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;
}
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;
}
#if TOML_HAS_CHAR8
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() });
}
#endif // TOML_HAS_CHAR8
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_WINDOWS
//******** impl/print_to_stream.inl **********************************************************************************
TOML_DISABLE_WARNINGS;
#include <ostream>
#if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV
#include <charconv>
#endif
#if !TOML_INT_CHARCONV || !TOML_FLOAT_CHARCONV
#include <sstream>
#endif
#if !TOML_INT_CHARCONV
#include <iomanip>
#endif
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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<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<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<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<uint64_t> = 20; // strlen("18446744073709551615")
template <>
inline constexpr size_t charconv_buffer_length<float> = 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;
for (size_t i = 0; i < min_digits; i++)
stream.put('0');
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;
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);
#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);
}
#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;
};
#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);
#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);
#endif
}
break;
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('\'');
}
}
#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 };
}
#endif
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/node.inl *********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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_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_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/at_path.inl ******************************************************************************************
TOML_DISABLE_WARNINGS;
#if TOML_INT_CHARCONV
#include <charconv>
#else
#include <sstream>
#endif
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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;
#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;
#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;
}
}
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, &current, 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_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/path.inl *********************************************************************************************
TOML_DISABLE_WARNINGS;
#if TOML_INT_CHARCONV
#include <charconv>
#endif
#include <sstream>
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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))
{}
#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;
}
#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_);
}
return *this;
}
#endif
}
TOML_NAMESPACE_END;
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_;
}
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))
{}
#endif
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));
}
#endif
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);
}
#endif
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));
}
#endif
TOML_EXTERNAL_LINKAGE
std::string path::str() const
{
if (empty())
return "";
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());
}
#endif
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;
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;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/array.inl ********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
array::array() noexcept
{
#if TOML_LIFETIME_HOOKS
TOML_ARRAY_CREATED;
#endif
}
TOML_EXTERNAL_LINKAGE
array::~array() noexcept
{
#if TOML_LIFETIME_HOOKS
TOML_ARRAY_DESTROYED;
#endif
}
TOML_EXTERNAL_LINKAGE
array::array(const impl::array_init_elem* b, const impl::array_init_elem* e)
{
#if TOML_LIFETIME_HOOKS
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));
#if TOML_LIFETIME_HOOKS
TOML_ARRAY_CREATED;
#endif
}
TOML_EXTERNAL_LINKAGE
array::array(array && other) noexcept //
: node(std::move(other)),
elems_(std::move(other.elems_))
{
#if TOML_LIFETIME_HOOKS
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)
{
#if TOML_COMPILER_HAS_EXCEPTIONS
return *elems_.at(index);
#else
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_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/table.inl ********************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
table::table() noexcept
{
#if TOML_LIFETIME_HOOKS
TOML_TABLE_CREATED;
#endif
}
TOML_EXTERNAL_LINKAGE
table::~table() noexcept
{
#if TOML_LIFETIME_HOOKS
TOML_TABLE_DESTROYED;
#endif
}
TOML_EXTERNAL_LINKAGE
table::table(const impl::table_init_pair* b, const impl::table_init_pair* e)
{
#if TOML_LIFETIME_HOOKS
TOML_TABLE_CREATED;
#endif
TOML_ASSERT_ASSUME(b);
TOML_ASSERT_ASSUME(e);
TOML_ASSERT_ASSUME(b <= e);
if TOML_UNLIKELY(b == e)
return;
for (; b != e; b++)
{
if (!b->value) // empty node_views
continue;
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));
#if TOML_LIFETIME_HOOKS
TOML_TABLE_CREATED;
#endif
}
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;
#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);
#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 };
}
#else
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;
}
}
TOML_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/simd.h ***********************************************************************************************
#if TOML_ENABLE_SIMD
#if defined(__SSE2__) \
|| (defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)))
#define TOML_HAS_SSE2 1
#endif
#if defined(__SSE4_1__) || (defined(_MSC_VER) && (defined(__AVX__) || defined(__AVX2__)))
#define TOML_HAS_SSE4_1 1
#endif
#endif // TOML_ENABLE_SIMD
#ifndef TOML_HAS_SSE2
#define TOML_HAS_SSE2 0
#endif
#ifndef TOML_HAS_SSE4_1
#define TOML_HAS_SSE4_1 0
#endif
TOML_DISABLE_WARNINGS;
#if TOML_HAS_SSE4_1
#include <smmintrin.h>
#endif
#if TOML_HAS_SSE2
#include <emmintrin.h>
#endif
TOML_ENABLE_WARNINGS;
//******** impl/unicode.inl ******************************************************************************************
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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);
#if TOML_HAS_SSE4_1
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;
#endif
}
}
#endif
for (; str < end; str++)
if (static_cast<unsigned char>(*str) > 127u)
return false;
return true;
}
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
//******** impl/parser.inl *******************************************************************************************
#if TOML_ENABLE_PARSER
TOML_DISABLE_WARNINGS;
#include <istream>
#include <fstream>
#if TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV
#include <charconv>
#endif
#if !TOML_INT_CHARCONV || !TOML_FLOAT_CHARCONV
#include <sstream>
#endif
#if !TOML_INT_CHARCONV
#include <iomanip>
#endif
TOML_ENABLE_WARNINGS;
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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;
#endif
virtual ~utf8_reader_interface() noexcept = default;
};
#if TOML_EXCEPTIONS
#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)
#else
#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)
#endif
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
#define TOML_OVERALIGNED
#else
#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_;
#if !TOML_EXCEPTIONS
optional<parse_error> err_;
#endif
bool read_next_block() noexcept(!TOML_COMPILER_HAS_EXCEPTIONS)
{
TOML_ASSERT(stream_);
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);
}
#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();
}
#if !TOML_EXCEPTIONS
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>>;
#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)
#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();
}
#if !TOML_EXCEPTIONS
TOML_NODISCARD
optional<parse_error>&& error() noexcept
{
return reader_.error();
}
#endif
};
}
TOML_ANON_NAMESPACE_END;
#if TOML_EXCEPTIONS
#define TOML_RETURNS_BY_THROWING [[noreturn]]
#else
#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>)
{
#if TOML_FLOAT_CHARCONV
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()));
#endif
}
// 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;
#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()));
#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::dependent_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 };
#else
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);
};
#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);
};
}
TOML_ANON_NAMESPACE_END;
#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
// 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++.
#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)
#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 return_if_error_or_eof(...) return_if_eof(__VA_ARGS__)
#else
#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)
#endif
#if defined(TOML_BREAK_AT_PARSE_ERRORS) && TOML_BREAK_AT_PARSE_ERRORS
#if defined(__has_builtin)
#if __has_builtin(__builtin_debugtrap)
#define parse_error_break() __builtin_debugtrap()
#elif __has_builtin(__debugbreak)
#define parse_error_break() __debugbreak()
#endif
#endif
#ifndef parse_error_break
#if TOML_MSVC || TOML_ICC
#define parse_error_break() __debugbreak()
#else
#define parse_error_break() TOML_ASSERT(false)
#endif
#endif
#else
#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_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 = {};
#if !TOML_EXCEPTIONS
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) };
}
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), ...);
parse_error_break();
#if TOML_EXCEPTIONS
builder.finish(pos, reader.source_path());
#else
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...);
}
void go_back(size_t count = 1) noexcept
{
return_if_error();
TOML_ASSERT_ASSUME(count);
cp = reader.step_back(count);
prev_pos = cp->position;
}
void advance()
{
return_if_error();
assert_not_eof();
prev_pos = cp->position;
cp = reader.read_next();
#if !TOML_EXCEPTIONS
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 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/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;
#else
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]];
#else
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);
#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);
#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 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);
#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;
#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);
}
}
#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);
}
#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();
#if !TOML_EXCEPTIONS
if (reader.error())
{
err = std::move(reader.error());
return;
}
#endif
if (cp)
parse_document();
}
update_region_ends(root);
}
TOML_NODISCARD
operator parse_result() && noexcept
{
#if TOML_EXCEPTIONS
return { std::move(root) };
#else
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_IMPL_NAMESPACE_END;
#undef TOML_RETURNS_BY_THROWING
#undef advance_and_return_if_error
#undef advance_and_return_if_error_or_eof
#undef assert_not_eof
#undef assert_not_error
#undef is_eof
#undef is_error
#undef parse_error_break
#undef push_parse_scope
#undef push_parse_scope_1
#undef push_parse_scope_2
#undef return_after_error
#undef return_if_eof
#undef return_if_error
#undef return_if_error_or_eof
#undef set_error_and_return
#undef set_error_and_return_default
#undef set_error_and_return_if_eof
#undef utf8_buffered_reader_error_check
#undef utf8_reader_error
#undef utf8_reader_error_check
#undef utf8_reader_return_after_error
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)) }
#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)) \
} \
}
#endif
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));
#if TOML_WINDOWS
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);
#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));
#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);
}
#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
#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::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));
}
#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) });
}
#endif // TOML_HAS_CHAR8 && TOML_ENABLE_WINDOWS_COMPAT
TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS
}
TOML_NAMESPACE_END;
#undef TOML_OVERALIGNED
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_PARSER
//******** impl/formatter.inl ****************************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_IMPL_NAMESPACE_START
{
enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned
{
none,
line_breaks = 1u << 0, // \n
tabs = 1u << 1, // \t
control_chars = 1u << 2, // also includes non-ascii vertical whitespace
single_quotes = 1u << 3,
non_bare = 1u << 4, // anything not satisfying "is bare key character"
non_ascii = 1u << 5, // any codepoint >= 128
all = (non_ascii << 1u) - 1u
};
TOML_MAKE_FLAGS(formatted_string_traits);
TOML_EXTERNAL_LINKAGE
formatter::formatter(const node* source_node,
const parse_result* source_pr,
const formatter_constants& constants,
const formatter_config& config) noexcept //
#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
: source_{ source_pr && *source_pr ? &source_pr->table() : source_node },
result_{ source_pr },
#else
: source_{ source_pr ? source_pr : source_node },
#endif
constants_{ &constants },
config_{ config }
{
TOML_ASSERT_ASSUME(source_);
config_.flags = (config_.flags | constants_->mandatory_flags) & ~constants_->ignored_flags;
indent_columns_ = {};
for (auto c : config_.indent)
indent_columns_ += c == '\t' ? 4u : 1u;
int_format_mask_ = config_.flags
& (format_flags::allow_binary_integers | format_flags::allow_octal_integers
| format_flags::allow_hexadecimal_integers);
}
TOML_EXTERNAL_LINKAGE
void formatter::attach(std::ostream & stream) noexcept
{
indent_ = {};
naked_newline_ = true;
stream_ = &stream;
}
TOML_EXTERNAL_LINKAGE
void formatter::detach() noexcept
{
stream_ = nullptr;
}
TOML_EXTERNAL_LINKAGE
void formatter::print_newline(bool force)
{
if (!naked_newline_ || force)
{
print_to_stream(*stream_, '\n');
naked_newline_ = true;
}
}
TOML_EXTERNAL_LINKAGE
void formatter::print_indent()
{
for (int i = 0; i < indent_; i++)
{
print_to_stream(*stream_, config_.indent);
naked_newline_ = false;
}
}
TOML_EXTERNAL_LINKAGE
void formatter::print_unformatted(char c)
{
print_to_stream(*stream_, c);
naked_newline_ = false;
}
TOML_EXTERNAL_LINKAGE
void formatter::print_unformatted(std::string_view str)
{
print_to_stream(*stream_, str);
naked_newline_ = false;
}
TOML_EXTERNAL_LINKAGE
void formatter::print_string(std::string_view str, bool allow_multi_line, bool allow_bare)
{
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();
}
// 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;
}
// determine if this should be a multi-line string (triple-quotes)
const auto multi_line = 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_in_strings_allowed()) //
&& (!(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);
const auto real_tabs_allowed = real_tabs_in_strings_allowed();
// ascii fast path
if (!(traits & formatted_string_traits::non_ascii))
{
for (auto c : str)
{
switch (c)
{
case '"': print_to_stream(*stream_, R"(\")"sv); break;
case '\\': print_to_stream(*stream_, R"(\\)"sv); break;
case '\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break;
case '\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break;
case '\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break;
default:
{
// control characters from lookup table
if TOML_UNLIKELY(c >= '\x00' && c <= '\x1F')
print_to_stream(*stream_, control_char_escapes[c]);
// regular characters
else
print_to_stream(*stream_, c);
}
}
}
}
// unicode slow path
else
{
utf8_decoder decoder;
const char* cp_start = str.data();
const char* cp_end = cp_start;
for (auto c : str)
{
decoder(c);
cp_end++;
// if the decoder encounters malformed unicode just emit raw bytes and
if (decoder.error())
{
while (cp_start != cp_end)
{
print_to_stream(*stream_, R"(\u00)"sv);
print_to_stream(*stream_,
static_cast<uint8_t>(*cp_start),
value_flags::format_as_hexadecimal,
2);
cp_start++;
}
decoder.reset();
continue;
}
if (!decoder.has_code_point())
continue;
switch (decoder.codepoint)
{
case U'"': print_to_stream(*stream_, R"(\")"sv); break;
case U'\\': print_to_stream(*stream_, R"(\\)"sv); break;
case U'\x7F': print_to_stream(*stream_, R"(\u007F)"sv); break;
case U'\t': print_to_stream(*stream_, real_tabs_allowed ? "\t"sv : R"(\t)"sv); break;
case U'\n': print_to_stream(*stream_, multi_line ? "\n"sv : R"(\n)"sv); break;
default:
{
// control characters from lookup table
if TOML_UNLIKELY(decoder.codepoint <= U'\x1F')
print_to_stream(*stream_,
control_char_escapes[static_cast<uint_least32_t>(decoder.codepoint)]);
// escaped unicode characters
else if (decoder.codepoint > U'\x7F'
&& (!unicode_allowed || is_non_ascii_vertical_whitespace(decoder.codepoint)))
{
if (static_cast<uint_least32_t>(decoder.codepoint) > 0xFFFFu)
{
print_to_stream(*stream_, R"(\U)"sv);
print_to_stream(*stream_,
static_cast<uint_least32_t>(decoder.codepoint),
value_flags::format_as_hexadecimal,
8);
}
else
{
print_to_stream(*stream_, R"(\u)"sv);
print_to_stream(*stream_,
static_cast<uint_least32_t>(decoder.codepoint),
value_flags::format_as_hexadecimal,
4);
}
}
// regular characters
else
print_to_stream(*stream_, cp_start, static_cast<size_t>(cp_end - cp_start));
}
}
cp_start = cp_end;
}
}
print_unformatted(multi_line ? R"(""")"sv : R"(")"sv);
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<std::string>& val)
{
print_string(val.get());
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<int64_t>& val)
{
naked_newline_ = false;
if (*val >= 0 && !!int_format_mask_)
{
static constexpr auto value_flags_mask =
value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal;
const auto fmt = val.flags() & value_flags_mask;
switch (fmt)
{
case value_flags::format_as_binary:
if (!!(int_format_mask_ & format_flags::allow_binary_integers))
{
print_to_stream(*stream_, "0b"sv);
print_to_stream(*stream_, *val, fmt);
return;
}
break;
case value_flags::format_as_octal:
if (!!(int_format_mask_ & format_flags::allow_octal_integers))
{
print_to_stream(*stream_, "0o"sv);
print_to_stream(*stream_, *val, fmt);
return;
}
break;
case value_flags::format_as_hexadecimal:
if (!!(int_format_mask_ & format_flags::allow_hexadecimal_integers))
{
print_to_stream(*stream_, "0x"sv);
print_to_stream(*stream_, *val, fmt);
return;
}
break;
default: break;
}
}
// fallback to decimal
print_to_stream(*stream_, *val);
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<double>& val)
{
const std::string_view* inf_nan = nullptr;
switch (fpclassify(*val))
{
case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break;
case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break;
case fp_class::nan: inf_nan = &constants_->float_nan; break;
case fp_class::ok:
print_to_stream(*stream_,
*val,
value_flags::none,
!!(config_.flags & format_flags::relaxed_float_precision));
break;
default: TOML_UNREACHABLE;
}
if (inf_nan)
{
if (!!(config_.flags & format_flags::quote_infinities_and_nans))
print_to_stream_bookended(*stream_, *inf_nan, '"');
else
print_to_stream(*stream_, *inf_nan);
}
naked_newline_ = false;
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<bool>& val)
{
print_unformatted(*val ? constants_->bool_true : constants_->bool_false);
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<date>& val)
{
if (!!(config_.flags & format_flags::quote_dates_and_times))
print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"');
else
print_to_stream(*stream_, *val);
naked_newline_ = false;
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<time>& val)
{
if (!!(config_.flags & format_flags::quote_dates_and_times))
print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"');
else
print_to_stream(*stream_, *val);
naked_newline_ = false;
}
TOML_EXTERNAL_LINKAGE
void formatter::print(const value<date_time>& val)
{
if (!!(config_.flags & format_flags::quote_dates_and_times))
print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"');
else
print_to_stream(*stream_, *val);
naked_newline_ = false;
}
TOML_EXTERNAL_LINKAGE
void formatter::print_value(const node& val_node, node_type type)
{
TOML_ASSUME(type > node_type::array);
switch (type)
{
case node_type::string: print(*reinterpret_cast<const value<std::string>*>(&val_node)); break;
case node_type::integer: print(*reinterpret_cast<const value<int64_t>*>(&val_node)); break;
case node_type::floating_point: print(*reinterpret_cast<const value<double>*>(&val_node)); break;
case node_type::boolean: print(*reinterpret_cast<const value<bool>*>(&val_node)); break;
case node_type::date: print(*reinterpret_cast<const value<date>*>(&val_node)); break;
case node_type::time: print(*reinterpret_cast<const value<time>*>(&val_node)); break;
case node_type::date_time: print(*reinterpret_cast<const value<date_time>*>(&val_node)); break;
default: TOML_UNREACHABLE;
}
}
#if TOML_ENABLE_PARSER && !TOML_EXCEPTIONS
TOML_EXTERNAL_LINKAGE
bool formatter::dump_failed_parse_result()
{
if (result_ && !(*result_))
{
stream() << result_->error();
return true;
}
return false;
}
#else
TOML_EXTERNAL_LINKAGE
TOML_ATTR(const)
bool formatter::dump_failed_parse_result()
{
return false;
}
#endif
}
TOML_IMPL_NAMESPACE_END;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
//******** impl/toml_formatter.inl ***********************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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_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);
}
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;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
//******** impl/json_formatter.inl ***********************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
TOML_NAMESPACE_START
{
TOML_EXTERNAL_LINKAGE
void json_formatter::print(const toml::table& tbl)
{
if (tbl.empty())
{
print_unformatted("{}"sv);
return;
}
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();
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();
print_unformatted('}');
}
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();
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;
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;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
//******** impl/yaml_formatter.inl ***********************************************************************************
#if TOML_ENABLE_FORMATTERS
TOML_PUSH_WARNINGS;
#ifdef _MSC_VER
#ifndef __clang__
#pragma inline_recursion(on)
#endif
#pragma push_macro("min")
#pragma push_macro("max")
#undef min
#undef max
#endif
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;
#ifdef _MSC_VER
#pragma pop_macro("min")
#pragma pop_macro("max")
#ifndef __clang__
#pragma inline_recursion(off)
#endif
#endif
TOML_POP_WARNINGS;
#endif // TOML_ENABLE_FORMATTERS
#endif // TOML_IMPLEMENTATION
TOML_POP_WARNINGS;
// macro hygiene
#if TOML_UNDEF_MACROS
#undef TOML_ABI_NAMESPACE_BOOL
#undef TOML_ABI_NAMESPACE_END
#undef TOML_ABI_NAMESPACE_START
#undef TOML_ABI_NAMESPACES
#undef TOML_ABSTRACT_INTERFACE
#undef TOML_ALWAYS_INLINE
#undef TOML_ANON_NAMESPACE
#undef TOML_ANON_NAMESPACE_END
#undef TOML_ANON_NAMESPACE_START
#undef TOML_ARCH_AMD64
#undef TOML_ARCH_ARM
#undef TOML_ARCH_ARM32
#undef TOML_ARCH_ARM64
#undef TOML_ARCH_ITANIUM
#undef TOML_ARCH_X86
#undef TOML_ASSERT
#undef TOML_ASSERT_ASSUME
#undef TOML_ASSUME
#undef TOML_ASYMMETRICAL_EQUALITY_OPS
#undef TOML_ATTR
#undef TOML_CLANG
#undef TOML_CLANG_VERSION
#undef TOML_CLOSED_ENUM
#undef TOML_CLOSED_FLAGS_ENUM
#undef TOML_COMPILER_HAS_EXCEPTIONS
#undef TOML_COMPILER_HAS_RTTI
#undef TOML_CONST
#undef TOML_CONST_GETTER
#undef TOML_CONST_INLINE_GETTER
#undef TOML_CONSTRAINED_TEMPLATE
#undef TOML_CPP
#undef TOML_DECLSPEC
#undef TOML_DELETE_DEFAULTS
#undef TOML_DISABLE_ARITHMETIC_WARNINGS
#undef TOML_DISABLE_CODE_ANALYSIS_WARNINGS
#undef TOML_DISABLE_SPAM_WARNINGS
#undef TOML_DISABLE_SPAM_WARNINGS_CLANG_10
#undef TOML_DISABLE_SPAM_WARNINGS_CLANG_11
#undef TOML_DISABLE_SUGGEST_ATTR_WARNINGS
#undef TOML_DISABLE_SWITCH_WARNINGS
#undef TOML_DISABLE_WARNINGS
#undef TOML_DOXYGEN
#undef TOML_EMPTY_BASES
#undef TOML_ENABLE_IF
#undef TOML_ENABLE_WARNINGS
#undef TOML_EVAL_BOOL_0
#undef TOML_EVAL_BOOL_1
#undef TOML_EXTERNAL_LINKAGE
#undef TOML_FLAGS_ENUM
#undef TOML_FLOAT_CHARCONV
#undef TOML_FLOAT128
#undef TOML_FLOAT16_DIG
#undef TOML_FLOAT16_LIMITS_SET
#undef TOML_FLOAT16_MANT_DIG
#undef TOML_FLOAT16_MAX_10_EXP
#undef TOML_FLOAT16_MAX_EXP
#undef TOML_FLOAT16_MIN_10_EXP
#undef TOML_FLOAT16_MIN_EXP
#undef TOML_GCC
#undef TOML_HAS_ATTR
#undef TOML_HAS_BUILTIN
#undef TOML_HAS_CHAR8
#undef TOML_HAS_CPP_ATTR
#undef TOML_HAS_CUSTOM_OPTIONAL_TYPE
#undef TOML_HAS_FEATURE
#undef TOML_HAS_INCLUDE
#undef TOML_HAS_SSE2
#undef TOML_HAS_SSE4_1
#undef TOML_HIDDEN_CONSTRAINT
#undef TOML_ICC
#undef TOML_ICC_CL
#undef TOML_IMPL_NAMESPACE_END
#undef TOML_IMPL_NAMESPACE_START
#undef TOML_IMPLEMENTATION
#undef TOML_INCLUDE_WINDOWS_H
#undef TOML_INT_CHARCONV
#undef TOML_INT128
#undef TOML_INTELLISENSE
#undef TOML_INTERNAL_LINKAGE
#undef TOML_LANG_AT_LEAST
#undef TOML_LANG_EFFECTIVE_VERSION
#undef TOML_LANG_HIGHER_THAN
#undef TOML_LANG_UNRELEASED
#undef TOML_LAUNDER
#undef TOML_LIFETIME_HOOKS
#undef TOML_LIKELY
#undef TOML_LIKELY_CASE
#undef TOML_MAKE_FLAGS
#undef TOML_MAKE_FLAGS_
#undef TOML_MAKE_FLAGS_1
#undef TOML_MAKE_FLAGS_2
#undef TOML_MAKE_STRING
#undef TOML_MAKE_STRING_1
#undef TOML_MAKE_VERSION
#undef TOML_MSVC
#undef TOML_NAMESPACE
#undef TOML_NEVER_INLINE
#undef TOML_NODISCARD
#undef TOML_NODISCARD_CTOR
#undef TOML_OPEN_ENUM
#undef TOML_OPEN_FLAGS_ENUM
#undef TOML_PARSER_TYPENAME
#undef TOML_POP_WARNINGS
#undef TOML_PRAGMA_CLANG
#undef TOML_PRAGMA_CLANG_GE_10
#undef TOML_PRAGMA_CLANG_GE_11
#undef TOML_PRAGMA_CLANG_GE_9
#undef TOML_PRAGMA_GCC
#undef TOML_PRAGMA_ICC
#undef TOML_PRAGMA_MSVC
#undef TOML_PURE
#undef TOML_PURE_GETTER
#undef TOML_PURE_INLINE_GETTER
#undef TOML_PUSH_WARNINGS
#undef TOML_REQUIRES
#undef TOML_SA_LIST_BEG
#undef TOML_SA_LIST_END
#undef TOML_SA_LIST_NEW
#undef TOML_SA_LIST_NXT
#undef TOML_SA_LIST_SEP
#undef TOML_SA_NATIVE_VALUE_TYPE_LIST
#undef TOML_SA_NEWLINE
#undef TOML_SA_NODE_TYPE_LIST
#undef TOML_SA_UNWRAPPED_NODE_TYPE_LIST
#undef TOML_SA_VALUE_EXACT_FUNC_MESSAGE
#undef TOML_SA_VALUE_FUNC_MESSAGE
#undef TOML_SA_VALUE_MESSAGE_CONST_CHAR8
#undef TOML_SA_VALUE_MESSAGE_U8STRING_VIEW
#undef TOML_SA_VALUE_MESSAGE_WSTRING
#undef TOML_SIMPLE_STATIC_ASSERT_MESSAGES
#undef TOML_TRIVIAL_ABI
#undef TOML_UINT128
#undef TOML_UNLIKELY
#undef TOML_UNLIKELY_CASE
#undef TOML_UNREACHABLE
#undef TOML_UNUSED
#undef TOML_WINDOWS
#endif
#endif // TOMLPLUSPLUS_H