diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f6ce4d..b91dbf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1) project(googerteller VERSION 1.0 DESCRIPTION "Audible feedback on Google communications" - LANGUAGES CXX) + LANGUAGES CXX C) set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard to use") @@ -20,7 +20,7 @@ set(CMAKE_CXX_EXTENSIONS ON) #target_include_directories(support PUBLIC ext/powerblog/ext/simplesocket ext/powerblog/ext ext/fmt-7.1.3/include/) #target_link_libraries(support PUBLIC -lh2o-evloop -lssl -lcrypto Threads::Threads) -add_executable(teller teller.cc ) +add_executable(teller teller.cc ext/lpm.c) target_link_libraries(teller -lpcaudio -lpthread) #enable_testing() diff --git a/README.md b/README.md index 966faf0..3c95cb9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Audible feedback on just how much your browsing feeds into Google. By bert@hubertnet.nl / https://berthub.eu/ Makes a little bit of noise any time your computer sends a packet to a -Google service, which excludes Google Cloud users. +tracker or a Google service, which excludes Google Cloud users. Demo video [in this tweet](https://twitter.com/bert_hu_bert/status/1561466204602220544) @@ -23,51 +23,9 @@ make ``` ## How to run -Google is so large its IPv4 and IPv6 footprint can't be handled by tcpdump, -or at least not efficiently. Therefore we need to define an ip(6)tables -`ipset`. This will first exclude Google Cloud, and then include all the -other Google IP addresses. - -Install iptables 'ipset', and run (as root) the `ipset-setup.sh` script, or -execute: - +Start as: ``` -ipset create google-services hash:net -for a in $(cat goog-cloud-prefixes.txt) -do -echo $a - ipset add google-services $a nomatch -done -for a in $(cat goog-prefixes.txt) -do - ipset add google-services $a -done - -ipset create google-services6 hash:net family inet6 -for a in $(cat goog-cloud-prefixes6.txt) -do - ipset add google-services6 $a nomatch -done - -for a in $(cat goog-prefixes6.txt) -do - ipset add google-services6 $a -done -iptables -I OUTPUT -m set --match-set google-services dst -j NFLOG --nflog-group 20 --nflog-threshold 1 -ip6tables -I OUTPUT -m set --match-set google-services6 dst -j NFLOG --nflog-group 20 --nflog-threshold 1 -``` - -Then start as: -``` -sudo tcpdump -i nflog:20 -ln | ./teller -``` - -Or, to track a single process, fe `firefox`, start it and run: - -```shell -sudo bpftrace netsendmsg.bt | - grep --line-buffered ^$(pgrep firefox) | - stdbuf -oL cut -f2 | ./cidr.py | ./teller +sudo tcpdump -nql | ./teller ``` And cry. @@ -80,3 +38,15 @@ Note that this splits out Google services and Google cloud user IP addresses. However, it appears the Google services set includes the cloud IP addresses, so you must check both sets before determining something is in fact a Google service and not a Google customer. + +# To run on a single process on Linux + +Or, to track a single process, fe `firefox`, start it and run: + +```shell +sudo bpftrace netsendmsg.bt | + grep --line-buffered ^$(pgrep firefox) | + stdbuf -oL cut -f2 | ./cidr.py | ./teller +``` + +And cry. diff --git a/ext/lpm.c b/ext/lpm.c new file mode 100644 index 0000000..34419ed --- /dev/null +++ b/ext/lpm.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu> + * All rights reserved. + * + * Use is subject to license terms, as specified in the LICENSE file. + */ + +/* + * Longest Prefix Match (LPM) library supporting IPv4 and IPv6. + * + * Algorithm: + * + * Each prefix gets its own hash map and all added prefixes are saved + * in a bitmap. On a lookup, we perform a linear scan of hash maps, + * iterating through the added prefixes only. Usually, there are only + * a few unique prefixes used and such simple algorithm is very efficient. + * With many IPv6 prefixes, the linear scan might become a bottleneck. + */ + +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stddef.h> +#include <inttypes.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> + +#include "lpm.h" + +#define LPM_MAX_PREFIX (128) +#define LPM_MAX_WORDS (LPM_MAX_PREFIX >> 5) +#define LPM_TO_WORDS(x) ((x) >> 2) +#define LPM_HASH_STEP (8) +#define LPM_LEN_IDX(len) ((len) >> 4) + +#ifdef DEBUG +#define ASSERT assert +#else +#define ASSERT(x) +#endif + +typedef struct lpm_ent { + struct lpm_ent *next; + void * val; + unsigned len; + uint8_t key[]; +} lpm_ent_t; + +typedef struct { + unsigned hashsize; + unsigned nitems; + lpm_ent_t ** bucket; +} lpm_hmap_t; + +struct lpm { + uint32_t bitmask[LPM_MAX_WORDS]; + void * defvals[2]; + lpm_hmap_t prefix[LPM_MAX_PREFIX + 1]; +}; + +static const uint32_t zero_address[LPM_MAX_WORDS]; + +lpm_t * +lpm_create(void) +{ + lpm_t *lpm; + + if ((lpm = calloc(1, sizeof(lpm_t))) == NULL) { + return NULL; + } + return lpm; +} + +void +lpm_clear(lpm_t *lpm, lpm_dtor_t dtor, void *arg) +{ + for (unsigned n = 0; n <= LPM_MAX_PREFIX; n++) { + lpm_hmap_t *hmap = &lpm->prefix[n]; + + if (!hmap->hashsize) { + ASSERT(!hmap->bucket); + continue; + } + for (unsigned i = 0; i < hmap->hashsize; i++) { + lpm_ent_t *entry = hmap->bucket[i]; + + while (entry) { + lpm_ent_t *next = entry->next; + + if (dtor) { + dtor(arg, entry->key, + entry->len, entry->val); + } + free(entry); + entry = next; + } + } + free(hmap->bucket); + hmap->bucket = NULL; + hmap->hashsize = 0; + hmap->nitems = 0; + } + if (dtor) { + dtor(arg, zero_address, 4, lpm->defvals[0]); + dtor(arg, zero_address, 16, lpm->defvals[1]); + } + memset(lpm->bitmask, 0, sizeof(lpm->bitmask)); + memset(lpm->defvals, 0, sizeof(lpm->defvals)); +} + +void +lpm_destroy(lpm_t *lpm) +{ + lpm_clear(lpm, NULL, NULL); + free(lpm); +} + +/* + * fnv1a_hash: Fowler-Noll-Vo hash function (FNV-1a variant). + */ +static uint32_t +fnv1a_hash(const void *buf, size_t len) +{ + uint32_t hash = 2166136261UL; + const uint8_t *p = buf; + + while (len--) { + hash ^= *p++; + hash *= 16777619U; + } + return hash; +} + +static bool +hashmap_rehash(lpm_hmap_t *hmap, unsigned size) +{ + lpm_ent_t **bucket; + unsigned hashsize; + + for (hashsize = 1; hashsize < size; hashsize <<= 1) { + continue; + } + if ((bucket = calloc(1, hashsize * sizeof(lpm_ent_t *))) == NULL) { + return false; + } + for (unsigned n = 0; n < hmap->hashsize; n++) { + lpm_ent_t *list = hmap->bucket[n]; + + while (list) { + lpm_ent_t *entry = list; + uint32_t hash = fnv1a_hash(entry->key, entry->len); + const unsigned i = hash & (hashsize - 1); + + list = entry->next; + entry->next = bucket[i]; + bucket[i] = entry; + } + } + hmap->hashsize = hashsize; + free(hmap->bucket); // may be NULL + hmap->bucket = bucket; + return true; +} + +static lpm_ent_t * +hashmap_insert(lpm_hmap_t *hmap, const void *key, size_t len) +{ + const unsigned target = hmap->nitems + LPM_HASH_STEP; + const size_t entlen = offsetof(lpm_ent_t, key[len]); + uint32_t hash, i; + lpm_ent_t *entry; + + if (hmap->hashsize < target && !hashmap_rehash(hmap, target)) { + return NULL; + } + + hash = fnv1a_hash(key, len); + i = hash & (hmap->hashsize - 1); + entry = hmap->bucket[i]; + while (entry) { + if (entry->len == len && memcmp(entry->key, key, len) == 0) { + return entry; + } + entry = entry->next; + } + + if ((entry = malloc(entlen)) != NULL) { + memcpy(entry->key, key, len); + entry->next = hmap->bucket[i]; + entry->len = len; + + hmap->bucket[i] = entry; + hmap->nitems++; + } + return entry; +} + +static lpm_ent_t * +hashmap_lookup(lpm_hmap_t *hmap, const void *key, size_t len) +{ + const uint32_t hash = fnv1a_hash(key, len); + const unsigned i = hash & (hmap->hashsize - 1); + lpm_ent_t *entry; + + if (hmap->hashsize == 0) { + return NULL; + } + entry = hmap->bucket[i]; + + while (entry) { + if (entry->len == len && memcmp(entry->key, key, len) == 0) { + return entry; + } + entry = entry->next; + } + return NULL; +} + +static int +hashmap_remove(lpm_hmap_t *hmap, const void *key, size_t len) +{ + const uint32_t hash = fnv1a_hash(key, len); + const unsigned i = hash & (hmap->hashsize - 1); + lpm_ent_t *prev = NULL, *entry; + + if (hmap->hashsize == 0) { + return -1; + } + entry = hmap->bucket[i]; + + while (entry) { + if (entry->len == len && memcmp(entry->key, key, len) == 0) { + if (prev) { + prev->next = entry->next; + } else { + hmap->bucket[i] = entry->next; + } + free(entry); + return 0; + } + prev = entry; + entry = entry->next; + } + return -1; +} + +/* + * compute_prefix: given the address and prefix length, compute and + * return the address prefix. + */ +static inline void +compute_prefix(const unsigned nwords, const uint32_t *addr, + unsigned preflen, uint32_t *prefix) +{ + uint32_t addr2[4]; + + if ((uintptr_t)addr & 3) { + /* Unaligned address: just copy for now. */ + memcpy(addr2, addr, nwords * 4); + addr = addr2; + } + for (unsigned i = 0; i < nwords; i++) { + if (preflen == 0) { + prefix[i] = 0; + continue; + } + if (preflen < 32) { + uint32_t mask = htonl(0xffffffff << (32 - preflen)); + prefix[i] = addr[i] & mask; + preflen = 0; + } else { + prefix[i] = addr[i]; + preflen -= 32; + } + } +} + +/* + * lpm_insert: insert the CIDR into the LPM table. + * + * => Returns zero on success and -1 on failure. + */ +int +lpm_insert(lpm_t *lpm, const void *addr, + size_t len, unsigned preflen, void *val) +{ + const unsigned nwords = LPM_TO_WORDS(len); + uint32_t prefix[nwords]; + lpm_ent_t *entry; + ASSERT(len == 4 || len == 16); + + if (preflen == 0) { + /* 0-length prefix is a special case. */ + lpm->defvals[LPM_LEN_IDX(len)] = val; + return 0; + } + compute_prefix(nwords, addr, preflen, prefix); + entry = hashmap_insert(&lpm->prefix[preflen], prefix, len); + if (entry) { + const unsigned n = --preflen >> 5; + lpm->bitmask[n] |= 0x80000000U >> (preflen & 31); + entry->val = val; + return 0; + } + return -1; +} + +/* + * lpm_remove: remove the specified prefix. + */ +int +lpm_remove(lpm_t *lpm, const void *addr, size_t len, unsigned preflen) +{ + const unsigned nwords = LPM_TO_WORDS(len); + uint32_t prefix[nwords]; + ASSERT(len == 4 || len == 16); + + if (preflen == 0) { + lpm->defvals[LPM_LEN_IDX(len)] = NULL; + return 0; + } + compute_prefix(nwords, addr, preflen, prefix); + return hashmap_remove(&lpm->prefix[preflen], prefix, len); +} + +/* + * lpm_lookup: find the longest matching prefix given the IP address. + * + * => Returns the associated value on success or NULL on failure. + */ +void * +lpm_lookup(lpm_t *lpm, const void *addr, size_t len) +{ + const unsigned nwords = LPM_TO_WORDS(len); + unsigned i, n = nwords; + uint32_t prefix[nwords]; + + while (n--) { + uint32_t bitmask = lpm->bitmask[n]; + + while ((i = ffs(bitmask)) != 0) { + const unsigned preflen = (32 * n) + (32 - --i); + lpm_hmap_t *hmap = &lpm->prefix[preflen]; + lpm_ent_t *entry; + + compute_prefix(nwords, addr, preflen, prefix); + entry = hashmap_lookup(hmap, prefix, len); + if (entry) { + return entry->val; + } + bitmask &= ~(1U << i); + } + } + return lpm->defvals[LPM_LEN_IDX(len)]; +} + +/* + * lpm_lookup_prefix: return the value associated with a prefix + * + * => Returns the associated value on success or NULL on failure. + */ +void * +lpm_lookup_prefix(lpm_t *lpm, const void *addr, size_t len, unsigned preflen) +{ + const unsigned nwords = LPM_TO_WORDS(len); + uint32_t prefix[nwords]; + lpm_ent_t *entry; + ASSERT(len == 4 || len == 16); + + if (preflen == 0) { + return lpm->defvals[LPM_LEN_IDX(len)]; + } + compute_prefix(nwords, addr, preflen, prefix); + entry = hashmap_lookup(&lpm->prefix[preflen], prefix, len); + if (entry) { + return entry->val; + } + return NULL; +} + +/* + * lpm_strtobin: convert CIDR string to the binary IP address and mask. + * + * => The address will be in the network byte order. + * => Returns 0 on success or -1 on failure. + */ +int +lpm_strtobin(const char *cidr, void *addr, size_t *len, unsigned *preflen) +{ + char *p, buf[INET6_ADDRSTRLEN]; + + strncpy(buf, cidr, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + if ((p = strchr(buf, '/')) != NULL) { + const ptrdiff_t off = p - buf; + *preflen = atoi(&buf[off + 1]); + buf[off] = '\0'; + } else { + *preflen = LPM_MAX_PREFIX; + } + + if (inet_pton(AF_INET6, buf, addr) == 1) { + *len = 16; + return 0; + } + if (inet_pton(AF_INET, buf, addr) == 1) { + if (*preflen == LPM_MAX_PREFIX) { + *preflen = 32; + } + *len = 4; + return 0; + } + return -1; +} diff --git a/ext/lpm.h b/ext/lpm.h new file mode 100644 index 0000000..cdd5e99 --- /dev/null +++ b/ext/lpm.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 Mindaugas Rasiukevicius <rmind at noxt eu> + * All rights reserved. + * + * Use is subject to license terms, as specified in the LICENSE file. + */ + +#ifndef _LPM_H_ +#define _LPM_H_ + +__BEGIN_DECLS + +typedef struct lpm lpm_t; +typedef void (*lpm_dtor_t)(void *, const void *, size_t, void *); + +lpm_t * lpm_create(void); +void lpm_destroy(lpm_t *); +void lpm_clear(lpm_t *, lpm_dtor_t, void *); + +int lpm_insert(lpm_t *, const void *, size_t, unsigned, void *); +int lpm_remove(lpm_t *, const void *, size_t, unsigned); +void * lpm_lookup(lpm_t *, const void *, size_t); +void * lpm_lookup_prefix(lpm_t *, const void *, size_t, unsigned); +int lpm_strtobin(const char *, void *, size_t *, unsigned *); + +__END_DECLS + +#endif diff --git a/ext/toml.hpp b/ext/toml.hpp new file mode 100644 index 0000000..83ea245 --- /dev/null +++ b/ext/toml.hpp @@ -0,0 +1,17169 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// toml++ v3.1.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 + +#ifdef __clang__ +#define TOML_CLANG __clang_major__ +#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 +#if defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64) || defined(_M_ARM64) || defined(__ARM_64BIT_STATE) \ + || defined(__arm__) || defined(_M_ARM) || defined(__ARM_32BIT_STATE) +#define TOML_ARM 1 +#else +#define TOML_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) \ + 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 : 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 : 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 : 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)) \ + 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") \ + 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 + +#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__) + +// clang-format off + +#ifdef __FLT16_MANT_DIG__ + #if __FLT_RADIX__ == 2 \ + && __FLT16_MANT_DIG__ == 11 \ + && __FLT16_DIG__ == 3 \ + && __FLT16_MIN_EXP__ == -13 \ + && __FLT16_MIN_10_EXP__ == -4 \ + && __FLT16_MAX_EXP__ == 16 \ + && __FLT16_MAX_10_EXP__ == 4 + #if TOML_ARM && (TOML_GCC || TOML_CLANG) + #define TOML_FP16 __fp16 + #endif + #if TOML_ARM && TOML_CLANG // not present in g++ + #define TOML_FLOAT16 _Float16 + #endif + #endif +#endif + +#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 on + +// clang-format off + +//******** impl/version.h ******************************************************************************************** + +#define TOML_LIB_MAJOR 3 +#define TOML_LIB_MINOR 1 +#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 + +#define TOML_MAKE_VERSION(major, minor, patch) \ + ((major) * 10000 + (minor) * 100 + (patch)) + +#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 +#pragma clang diagnostic ignored "-Wheader-hygiene" +#if TOML_CLANG >= 12 +#pragma clang diagnostic ignored "-Wc++20-extensions" +#endif +#if (TOML_CLANG == 13) && !defined(__APPLE__) +#pragma clang diagnostic ignored "-Wreserved-identifier" +#endif +#endif + +//******** impl/std_new.h ******************************************************************************************** + +TOML_DISABLE_WARNINGS; +#include <new> +TOML_ENABLE_WARNINGS; + +#if 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 +#pragma inline_recursion(on) +#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), + }; + 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> + {}; +#ifdef TOML_FP16 + template <> + struct float_traits<TOML_FP16> : float_traits_base<TOML_FP16, __FLT16_MANT_DIG__, __FLT16_DIG__> + {}; +#endif +#ifdef TOML_FLOAT16 + template <> + struct float_traits<TOML_FLOAT16> : float_traits_base<TOML_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> + {}; +#ifdef TOML_FP16 + template <> + struct value_traits<TOML_FP16> : float_traits<TOML_FP16> + {}; +#endif +#ifdef TOML_FLOAT16 + template <> + struct value_traits<TOML_FLOAT16> : float_traits<TOML_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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/print_to_stream.h ************************************************************************************ + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/source_region.h ************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/date_time.h ****************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/value.h ********************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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) noexcept + { + 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) }; + } +} +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&>>); + + template <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_{ other.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 + } + + 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_ = rhs.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> + 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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/make_node.h ****************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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) }; + } + + // 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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/array.h ********************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/key.h ************************************************************************************************ + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/unicode_autogenerated.h ****************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/unicode.h ******************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_PARSER && !TOML_EXCEPTIONS + +//******** impl/parser.h ********************************************************************************************* + +#if TOML_ENABLE_PARSER + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_PARSER + +//******** impl/formatter.h ****************************************************************************************** + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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_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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +//******** impl/toml_formatter.h ************************************************************************************* + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +//******** impl/json_formatter.h ************************************************************************************* + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +//******** impl/yaml_formatter.h ************************************************************************************* + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +#if TOML_IMPLEMENTATION + +//******** impl/std_string.inl *************************************************************************************** + +#if TOML_ENABLE_WINDOWS_COMPAT + +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_WINDOWS_COMPAT + +//******** 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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/node.inl ********************************************************************************************* + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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, ¤t, on_key, on_index)) + current = nullptr; + + return node_view{ current }; + } + + TOML_EXTERNAL_LINKAGE + node_view<const node> TOML_CALLCONV at_path(const node& root, std::string_view path) noexcept + { + return node_view<const node>{ at_path(const_cast<node&>(root), path).node() }; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node_view<node> TOML_CALLCONV at_path(node & root, std::wstring_view path) + { + // these are the same top-level checks from the narrow-string version; + // they're hoisted up here to avoid doing the wide -> narrow conversion where it would not be necessary + // (avoids an allocation) + if (root.is_value()) + return {}; + if (auto tbl = root.as_table(); tbl && tbl->empty()) + return {}; + if (auto arr = root.as_array(); arr && arr->empty()) + return {}; + + return at_path(root, impl::narrow(path)); + } + + TOML_EXTERNAL_LINKAGE + node_view<const node> TOML_CALLCONV at_path(const node& root, std::wstring_view path) + { + return node_view<const node>{ at_path(const_cast<node&>(root), path).node() }; + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT +} +TOML_NAMESPACE_END; + +#ifdef _MSC_VER +#pragma pop_macro("min") +#pragma pop_macro("max") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/array.inl ******************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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) + { + 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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +//******** impl/table.inl ******************************************************************************************** + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#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 +#pragma inline_recursion(on) +#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 + if TOML_UNLIKELY(result > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()) + (sign < 0 ? 1ull : 0ull)) + 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) + 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 && (tbl->empty() || tbl->is_homogeneous<table>())) + { + 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 TOML_CLOSED_ENUM parse_elem : int + { + none, + comma, + val + }; + parse_elem prev = 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 == val) + { + prev = 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 == val) + { + set_error_and_return_default("expected comma or closing ']', saw '"sv, to_sv(*cp), "'"sv); + continue; + } + prev = val; + if (!arr.capacity()) + arr.reserve(4u); + arr.emplace_back<node_ptr>(parse_value()); + } + } + + 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 TOML_CLOSED_ENUM parse_elem : int + { + none, + comma, + kvp + }; + parse_elem prev = 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 == kvp) + { + prev = 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 == 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 == kvp) + set_error_and_return_default("expected comma or closing '}', saw '"sv, to_sv(*cp), "'"sv); + else + { + prev = 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)); + file.open(file_path_str, std::ifstream::in | std::ifstream::binary | std::ifstream::ate); + 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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_PARSER + +//******** impl/formatter.inl **************************************************************************************** + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +//******** impl/toml_formatter.inl *********************************************************************************** + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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); + 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); + 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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +//******** impl/json_formatter.inl *********************************************************************************** + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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); + 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") +#pragma inline_recursion(off) +#endif +TOML_POP_WARNINGS; + +#endif // TOML_ENABLE_FORMATTERS + +//******** impl/yaml_formatter.inl *********************************************************************************** + +#if TOML_ENABLE_FORMATTERS + +TOML_PUSH_WARNINGS; +#ifdef _MSC_VER +#pragma inline_recursion(on) +#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); + 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") +#pragma inline_recursion(off) +#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_ARM +#undef TOML_ASSERT +#undef TOML_ASSERT_ASSUME +#undef TOML_ASSUME +#undef TOML_ASYMMETRICAL_EQUALITY_OPS +#undef TOML_ATTR +#undef TOML_CLANG +#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 +#undef TOML_FP16 +#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 diff --git a/teller.cc b/teller.cc index 7dc4439..4e4e02c 100644 --- a/teller.cc +++ b/teller.cc @@ -5,57 +5,203 @@ #include <thread> #include <atomic> #include <pcaudiolib/audio.h> +#include "ext/toml.hpp" +extern "C" { +#include "ext/lpm.h" +} using namespace std; -int main() +class LPMWrapper { - audio_object* ao; +public: + LPMWrapper() + { + d_lpm = lpm_create(); + } + ~LPMWrapper() + { + lpm_destroy(d_lpm); + } + void insert(const std::string& str, void* val=(void*)1) + { + char addr[16]; + size_t len=sizeof(addr); + unsigned preflen=0; + lpm_strtobin(str.c_str(), addr, &len, &preflen); + if(lpm_insert(d_lpm, addr, len, preflen, val) < 0) + throw std::runtime_error("Error inserting prefix"); + } + + void* lookup(const char*str) + { + char addr[16]; + size_t len=sizeof(addr); + unsigned preflen=0; + lpm_strtobin(str, addr, &len, &preflen); + return lpm_lookup(d_lpm, addr, len); + } + +private: + lpm_t* d_lpm; +}; + +struct TrackerConf +{ + double freq{400}; + double balance{0.5}; // 0 is left, 1 is right + std::atomic<int64_t> counter{0}; +}; + + +void playerThread(const TrackerConf* tcptr) +{ + auto& counter = tcptr->counter; + audio_object* ao; ao=create_audio_device_object(0, "teller", ""); if(!ao) { cerr<<"Unable to open audio file "<<endl; - return 0; + return; } - int res = audio_object_open(ao, AUDIO_OBJECT_FORMAT_S16LE, 44100, 1); + int res = audio_object_open(ao, AUDIO_OBJECT_FORMAT_S16LE, 44100, 2); if(res < 0) { cerr<<"Error opening audio: "<<audio_object_strerror(ao, res)<<endl; + return; } - std::atomic<int64_t> counter = 0; - auto player = [&]() { - vector<int16_t> data; - int ourcounter=0; - data.reserve(44100); - while(counter >= 0) { - data.clear(); - if(ourcounter < counter) { - for(int n=0; n < 250; ++n) { - int16_t val = 20000 * sin((n/44100.0) * 500 * 2 * M_PI); - data.push_back(val); - } - ourcounter++; - if(counter - ourcounter > 1000) - ourcounter = counter; + vector<int16_t> data; + int ourcounter=0; + data.reserve(44100); + while(counter >= 0) { + data.clear(); + if(ourcounter < counter) { + for(int n=0; n < 250; ++n) { + int16_t val = 20000 * sin((n/44100.0) * tcptr->freq * 2 * M_PI); + int16_t lval = tcptr->balance * val; + int16_t rval = (1.0-tcptr->balance) * val; + + data.push_back(lval); + data.push_back(rval); } - else { - for(int n=0; n < 150; ++n) { - data.push_back(0); - } - } - - audio_object_write(ao, &data[0], data.size() * sizeof(decltype(data)::value_type)); - // audio_object_flush(ao); + ourcounter++; + if(counter - ourcounter > 1000) + ourcounter = counter; } - }; + else { + for(int n=0; n < 150; ++n) { + data.push_back(0); + data.push_back(0); + } + } + + audio_object_write(ao, &data[0], data.size() * sizeof(decltype(data)::value_type)); + // audio_object_flush(ao); + } +} - std::thread athread(player); + +int main(int argc, char** argv) +{ + toml::table conftbl, trackertbl; + try + { + trackertbl = toml::parse_file("trackers.conf"); + conftbl = toml::parse_file("teller.conf"); + // std::cout << tbl << "\n"; + } + catch (const toml::parse_error& err) + { + std::cerr << "Parsing failed:\n" << err << "\n"; + return 1; + } + + map<string, TrackerConf> trackerdb; + auto tellerarr = conftbl.as_table(); + for(const auto& t : *tellerarr) { + auto& entry = trackerdb[(string)t.first]; + entry.balance = conftbl[t.first]["balance"].value_or(0.5); + entry.freq = conftbl[t.first]["freq"].value_or(500); + cout <<"Want to play sound for tracker "<<t.first<<", balance= "<<entry.balance<<" frequency = "<<entry.freq<<endl; + + } + + LPMWrapper trackspos, tracksneg; + + auto tarr = trackertbl.as_table(); + for(const auto& t : *tarr) { + cout<<"Defining tracker "<<t.first<<endl; + if(trackerdb.count((string)t.first)==0) { + cout<<"Skipping tracker "<<t.first<<", user doesn't want it"<<endl; + continue; + } + auto trackerptr = &trackerdb[(string)t.first]; + auto track = trackertbl[t.first]["positive"].as_array(); + + if(track) { + track->for_each([&trackspos, &trackerptr](auto&& el) { + if constexpr (toml::is_string<decltype(el)>) { + trackspos.insert(*el, (void*)trackerptr); + } + + }); + } + else { + cout<<"Not array?"<<endl; + } + + track = trackertbl[t.first]["negative"].as_array(); + if(track) { + track->for_each([&tracksneg, &trackerptr](auto&& el) { + if constexpr (toml::is_string<decltype(el)>) { + tracksneg.insert(*el, (void*)trackerptr); + } + + }); + } + else { + cout<<"Negative "<<t.first<<" not array?"<<endl; + } + } + + + for(const auto& t : trackerdb) { + std::thread athread(playerThread, &t.second); + athread.detach(); + } string line; while(getline(cin, line)) { - counter++; + + /* + 22:42:25.323984 IP 13.81.0.219.29601 > 10.0.0.3.32902: tcp 1186 + 22:42:25.323997 IP 10.0.0.3.32902 > 13.81.0.219.29601: tcp 0 + 22:42:25.327216 b0:95:75:c3:68:92 > ff:ff:ff:ff:ff:ff, RRCP-0x25 query + */ + auto pos = line.find('>'); + if(pos == string::npos) + continue; + + auto pos2 = line.find('.', pos); + if(pos2 == string::npos) continue; + pos2 = line.find('.', pos2+1); + if(pos2 == string::npos) continue; + pos2 = line.find('.', pos2+1); + if(pos2 == string::npos) continue; + pos2 = line.find_first_of(".:", pos2+1); + if(pos2 == string::npos) continue; + + line.resize(pos2); + string ip=line.substr(pos+2, pos2 - pos - 2); + // cout<<&line.at(pos+2)<<endl; + if(tracksneg.lookup(&line.at(pos+2))) { + cout<<ip<<" negative match"<<endl; + } + else if(auto fptr = trackspos.lookup(&line.at(pos+2))) { + cout<<ip<<" match!"<<endl; + ((TrackerConf*)fptr)->counter++; + } + } - counter = -1; - athread.join(); sleep(1); } diff --git a/trackers.conf b/trackers.conf new file mode 100644 index 0000000..68e1127 --- /dev/null +++ b/trackers.conf @@ -0,0 +1,783 @@ +[google] + positive= ["8.8.4.0/24", +"8.8.8.0/24", +"8.34.208.0/20", +"8.35.192.0/20", +"23.236.48.0/20", +"23.251.128.0/19", +"34.0.0.0/15", +"34.2.0.0/16", +"34.3.0.0/23", +"34.3.3.0/24", +"34.3.4.0/24", +"34.3.8.0/21", +"34.3.16.0/20", +"34.3.32.0/19", +"34.3.64.0/18", +"34.3.128.0/17", +"34.4.0.0/14", +"34.8.0.0/13", +"34.16.0.0/12", +"34.32.0.0/11", +"34.64.0.0/10", +"34.128.0.0/10", +"35.184.0.0/13", +"35.192.0.0/14", +"35.196.0.0/15", +"35.198.0.0/16", +"35.199.0.0/17", +"35.199.128.0/18", +"35.200.0.0/13", +"35.208.0.0/12", +"35.224.0.0/12", +"35.240.0.0/13", +"64.15.112.0/20", +"64.233.160.0/19", +"66.22.228.0/23", +"66.102.0.0/20", +"66.249.64.0/19", +"70.32.128.0/19", +"72.14.192.0/18", +"74.114.24.0/21", +"74.125.0.0/16", +"104.154.0.0/15", +"104.196.0.0/14", +"104.237.160.0/19", +"107.167.160.0/19", +"107.178.192.0/18", +"108.59.80.0/20", +"108.170.192.0/18", +"108.177.0.0/17", +"130.211.0.0/16", +"136.112.0.0/12", +"142.250.0.0/15", +"146.148.0.0/17", +"162.216.148.0/22", +"162.222.176.0/21", +"172.110.32.0/21", +"172.217.0.0/16", +"172.253.0.0/16", +"173.194.0.0/16", +"173.255.112.0/20", +"192.158.28.0/22", +"192.178.0.0/15", +"193.186.4.0/24", +"199.36.154.0/23", +"199.36.156.0/24", +"199.192.112.0/22", +"199.223.232.0/21", +"207.223.160.0/20", +"208.65.152.0/22", +"208.68.108.0/22", +"208.81.188.0/22", +"208.117.224.0/19", +"209.85.128.0/17", +"216.58.192.0/19", +"216.73.80.0/20", +"216.239.32.0/19"] + + negative=["34.80.0.0/15", +"34.137.0.0/16", +"35.185.128.0/19", +"35.185.160.0/20", +"35.187.144.0/20", +"35.189.160.0/19", +"35.194.128.0/17", +"35.201.128.0/17", +"35.206.192.0/18", +"35.220.32.0/21", +"35.221.128.0/17", +"35.229.128.0/17", +"35.234.0.0/18", +"35.235.16.0/20", +"35.236.128.0/18", +"35.242.32.0/21", +"104.155.192.0/19", +"104.155.224.0/20", +"104.199.128.0/18", +"104.199.192.0/19", +"104.199.224.0/20", +"104.199.242.0/23", +"104.199.244.0/22", +"104.199.248.0/21", +"107.167.176.0/20", +"130.211.240.0/20", +"34.92.0.0/16", +"34.96.128.0/17", +"34.104.88.0/21", +"34.124.24.0/21", +"34.150.0.0/17", +"35.215.128.0/18", +"35.220.27.0/24", +"35.220.128.0/17", +"35.241.64.0/18", +"35.242.27.0/24", +"35.243.8.0/21", +"34.84.0.0/16", +"34.85.0.0/17", +"34.104.62.0/23", +"34.104.128.0/17", +"34.127.190.0/23", +"34.146.0.0/16", +"34.157.64.0/20", +"34.157.164.0/22", +"34.157.192.0/20", +"35.187.192.0/19", +"35.189.128.0/19", +"35.190.224.0/20", +"35.194.96.0/19", +"35.200.0.0/17", +"35.213.0.0/17", +"35.220.56.0/22", +"35.221.64.0/18", +"35.230.240.0/20", +"35.242.56.0/22", +"35.243.64.0/18", +"104.198.80.0/20", +"104.198.112.0/20", +"34.97.0.0/16", +"34.104.49.0/24", +"34.127.177.0/24", +"35.217.128.0/17", +"35.220.45.0/24", +"35.242.45.0/24", +"35.243.56.0/21", +"34.64.32.0/19", +"34.64.64.0/22", +"34.64.68.0/22", +"34.64.72.0/21", +"34.64.80.0/20", +"34.64.96.0/19", +"34.64.128.0/22", +"34.64.132.0/22", +"34.64.136.0/21", +"34.64.144.0/20", +"34.64.160.0/19", +"34.64.192.0/18", +"35.216.0.0/17", +"34.93.0.0/16", +"34.100.128.0/17", +"34.104.108.0/23", +"34.124.44.0/23", +"34.157.87.0/24", +"34.157.215.0/24", +"35.200.128.0/17", +"35.201.41.0/24", +"35.207.192.0/18", +"35.220.42.0/24", +"35.234.208.0/20", +"35.242.42.0/24", +"35.244.0.0/18", +"34.104.120.0/23", +"34.124.56.0/23", +"34.126.208.0/20", +"34.131.0.0/16", +"34.87.0.0/17", +"34.87.128.0/18", +"34.104.58.0/23", +"34.104.106.0/23", +"34.124.42.0/23", +"34.124.128.0/17", +"34.126.64.0/18", +"34.126.128.0/18", +"34.142.128.0/17", +"34.143.128.0/17", +"34.157.82.0/23", +"34.157.88.0/23", +"34.157.210.0/23", +"35.185.176.0/20", +"35.186.144.0/20", +"35.187.224.0/19", +"35.197.128.0/19", +"35.198.192.0/18", +"35.213.128.0/18", +"35.220.24.0/23", +"35.234.192.0/20", +"35.240.128.0/17", +"35.242.24.0/23", +"35.247.128.0/18", +"34.101.18.0/24", +"34.101.20.0/22", +"34.101.24.0/22", +"34.101.32.0/19", +"34.101.64.0/18", +"34.101.128.0/17", +"34.128.64.0/18", +"35.219.0.0/17", +"34.87.192.0/18", +"34.104.104.0/23", +"34.116.64.0/18", +"34.124.40.0/23", +"34.151.64.0/18", +"34.151.128.0/18", +"35.189.0.0/18", +"35.197.160.0/19", +"35.201.0.0/19", +"35.213.192.0/18", +"35.220.41.0/24", +"35.234.224.0/20", +"35.242.41.0/24", +"35.244.64.0/18", +"34.104.122.0/23", +"34.124.58.0/23", +"34.126.192.0/20", +"34.129.0.0/16", +"34.104.116.0/22", +"34.116.128.0/17", +"34.118.0.0/17", +"34.124.52.0/22", +"34.88.0.0/16", +"34.104.96.0/21", +"34.124.32.0/21", +"35.203.232.0/21", +"35.217.0.0/18", +"35.220.26.0/24", +"35.228.0.0/16", +"35.242.26.0/24", +"34.157.44.0/23", +"34.157.172.0/23", +"34.164.0.0/16", +"34.175.0.0/16", +"8.34.208.0/23", +"8.34.211.0/24", +"8.34.220.0/22", +"23.251.128.0/20", +"34.76.0.0/14", +"34.118.254.0/23", +"34.140.0.0/16", +"35.187.0.0/17", +"35.187.160.0/19", +"35.189.192.0/18", +"35.190.192.0/19", +"35.195.0.0/16", +"35.205.0.0/16", +"35.206.128.0/18", +"35.210.0.0/16", +"35.220.96.0/19", +"35.233.0.0/17", +"35.240.0.0/17", +"35.241.128.0/17", +"35.242.64.0/19", +"104.155.0.0/17", +"104.199.0.0/18", +"104.199.66.0/23", +"104.199.68.0/22", +"104.199.72.0/21", +"104.199.80.0/20", +"104.199.96.0/20", +"130.211.48.0/20", +"130.211.64.0/19", +"130.211.96.0/20", +"146.148.2.0/23", +"146.148.4.0/22", +"146.148.8.0/21", +"146.148.16.0/20", +"146.148.112.0/20", +"192.158.28.0/22", +"34.89.0.0/17", +"34.105.128.0/17", +"34.127.186.0/23", +"34.142.0.0/17", +"34.147.128.0/17", +"34.157.36.0/22", +"34.157.40.0/22", +"34.157.168.0/22", +"35.189.64.0/18", +"35.197.192.0/18", +"35.203.210.0/23", +"35.203.212.0/22", +"35.203.216.0/22", +"35.214.0.0/17", +"35.220.20.0/22", +"35.230.128.0/19", +"35.234.128.0/19", +"35.235.48.0/20", +"35.242.20.0/22", +"35.242.128.0/18", +"35.246.0.0/17", +"34.89.128.0/17", +"34.104.112.0/23", +"34.107.0.0/17", +"34.118.244.0/22", +"34.124.48.0/23", +"34.141.0.0/17", +"34.157.48.0/20", +"34.157.176.0/20", +"34.159.0.0/16", +"35.198.64.0/18", +"35.198.128.0/18", +"35.207.64.0/18", +"35.207.128.0/18", +"35.220.18.0/23", +"35.234.64.0/18", +"35.235.32.0/20", +"35.242.18.0/23", +"35.242.192.0/18", +"35.246.128.0/17", +"34.90.0.0/15", +"34.104.126.0/23", +"34.124.62.0/23", +"34.141.128.0/17", +"34.147.0.0/17", +"34.157.80.0/23", +"34.157.92.0/22", +"34.157.208.0/23", +"34.157.220.0/22", +"35.204.0.0/16", +"35.214.128.0/17", +"35.220.16.0/23", +"35.234.160.0/20", +"35.242.16.0/23", +"34.65.0.0/16", +"34.104.110.0/23", +"34.124.46.0/23", +"35.216.128.0/17", +"35.220.44.0/24", +"35.235.216.0/21", +"35.242.44.0/24", +"34.154.0.0/16", +"34.157.8.0/23", +"34.157.136.0/23", +"35.219.224.0/19", +"34.155.0.0/16", +"34.157.12.0/22", +"34.157.140.0/22", +"34.163.0.0/16", +"34.95.64.0/18", +"34.96.64.0/18", +"34.98.64.0/18", +"34.102.128.0/17", +"34.104.27.0/24", +"34.107.128.0/17", +"34.110.128.0/17", +"34.111.0.0/16", +"34.116.0.0/21", +"34.117.0.0/16", +"34.120.0.0/16", +"34.128.128.0/18", +"34.144.192.0/18", +"34.149.0.0/16", +"34.160.0.0/16", +"35.186.192.0/18", +"35.190.0.0/18", +"35.190.64.0/19", +"35.190.112.0/20", +"35.201.64.0/18", +"35.227.192.0/18", +"35.241.0.0/18", +"35.244.128.0/17", +"107.178.240.0/20", +"130.211.4.0/22", +"130.211.8.0/21", +"130.211.16.0/20", +"130.211.32.0/20", +"34.95.0.0/18", +"34.104.76.0/22", +"34.118.128.0/18", +"34.124.12.0/22", +"34.152.0.0/18", +"35.203.0.0/17", +"35.215.0.0/18", +"35.220.43.0/24", +"35.234.240.0/20", +"35.242.43.0/24", +"34.104.114.0/23", +"34.124.50.0/23", +"34.124.112.0/20", +"34.130.0.0/16", +"34.95.128.0/17", +"34.104.80.0/21", +"34.124.16.0/21", +"34.151.0.0/18", +"34.151.192.0/18", +"35.198.0.0/18", +"35.199.64.0/18", +"35.215.192.0/18", +"35.220.40.0/24", +"35.235.0.0/20", +"35.242.40.0/24", +"35.247.192.0/18", +"34.104.50.0/23", +"34.127.178.0/23", +"34.176.0.0/16", +"8.34.210.0/24", +"8.34.212.0/22", +"8.34.216.0/22", +"8.35.192.0/21", +"23.236.48.0/20", +"23.251.144.0/20", +"34.16.0.0/17", +"34.66.0.0/15", +"34.68.0.0/14", +"34.72.0.0/16", +"34.118.200.0/21", +"34.121.0.0/16", +"34.122.0.0/15", +"34.132.0.0/14", +"34.136.0.0/16", +"34.157.84.0/23", +"34.157.96.0/20", +"34.157.212.0/23", +"34.157.224.0/20", +"34.170.0.0/15", +"34.172.0.0/15", +"35.184.0.0/16", +"35.188.0.0/17", +"35.188.128.0/18", +"35.188.192.0/19", +"35.192.0.0/15", +"35.194.0.0/18", +"35.202.0.0/16", +"35.206.64.0/18", +"35.208.0.0/15", +"35.220.64.0/19", +"35.222.0.0/15", +"35.224.0.0/15", +"35.226.0.0/16", +"35.232.0.0/16", +"35.238.0.0/15", +"35.242.96.0/19", +"104.154.16.0/20", +"104.154.32.0/19", +"104.154.64.0/19", +"104.154.96.0/20", +"104.154.113.0/24", +"104.154.114.0/23", +"104.154.116.0/22", +"104.154.120.0/23", +"104.154.128.0/17", +"104.155.128.0/18", +"104.197.0.0/16", +"104.198.16.0/20", +"104.198.32.0/19", +"104.198.64.0/20", +"104.198.128.0/17", +"107.178.208.0/20", +"108.59.80.0/21", +"130.211.112.0/20", +"130.211.128.0/18", +"130.211.192.0/19", +"130.211.224.0/20", +"146.148.32.0/19", +"146.148.64.0/19", +"146.148.96.0/20", +"162.222.176.0/21", +"173.255.112.0/21", +"199.192.115.0/24", +"199.223.232.0/22", +"199.223.236.0/24", +"35.186.0.0/17", +"35.186.128.0/20", +"35.206.32.0/19", +"35.220.46.0/24", +"35.242.46.0/24", +"107.167.160.0/20", +"108.59.88.0/21", +"173.255.120.0/21", +"34.73.0.0/16", +"34.74.0.0/15", +"34.98.128.0/21", +"34.118.250.0/23", +"34.138.0.0/15", +"34.148.0.0/16", +"35.185.0.0/17", +"35.190.128.0/18", +"35.196.0.0/16", +"35.207.0.0/18", +"35.211.0.0/16", +"35.220.0.0/20", +"35.227.0.0/17", +"35.229.16.0/20", +"35.229.32.0/19", +"35.229.64.0/18", +"35.231.0.0/16", +"35.237.0.0/16", +"35.242.0.0/20", +"35.243.128.0/17", +"104.196.0.0/18", +"104.196.65.0/24", +"104.196.66.0/23", +"104.196.68.0/22", +"104.196.96.0/19", +"104.196.128.0/18", +"104.196.192.0/19", +"162.216.148.0/22", +"34.85.128.0/17", +"34.86.0.0/16", +"34.104.60.0/23", +"34.104.124.0/23", +"34.118.252.0/23", +"34.124.60.0/23", +"34.127.188.0/23", +"34.145.128.0/17", +"34.150.128.0/17", +"34.157.0.0/21", +"34.157.16.0/20", +"34.157.128.0/21", +"34.157.144.0/20", +"35.186.160.0/19", +"35.188.224.0/19", +"35.194.64.0/19", +"35.199.0.0/18", +"35.212.0.0/17", +"35.220.60.0/22", +"35.221.0.0/18", +"35.230.160.0/19", +"35.234.176.0/20", +"35.236.192.0/18", +"35.242.60.0/22", +"35.243.40.0/21", +"35.245.0.0/16", +"34.157.32.0/22", +"34.157.160.0/22", +"34.162.0.0/16", +"34.104.56.0/23", +"34.127.184.0/23", +"34.161.0.0/16", +"35.206.10.0/23", +"34.157.46.0/23", +"34.157.174.0/23", +"34.174.0.0/16", +"34.82.0.0/15", +"34.105.0.0/17", +"34.118.192.0/21", +"34.127.0.0/17", +"34.145.0.0/17", +"34.157.112.0/21", +"34.157.240.0/21", +"34.168.0.0/15", +"35.185.192.0/18", +"35.197.0.0/17", +"35.199.144.0/20", +"35.199.160.0/19", +"35.203.128.0/18", +"35.212.128.0/17", +"35.220.48.0/21", +"35.227.128.0/18", +"35.230.0.0/17", +"35.233.128.0/17", +"35.242.48.0/21", +"35.243.32.0/21", +"35.247.0.0/17", +"104.196.224.0/19", +"104.198.0.0/20", +"104.198.96.0/20", +"104.199.112.0/20", +"34.94.0.0/16", +"34.102.0.0/17", +"34.104.64.0/21", +"34.108.0.0/16", +"34.118.248.0/23", +"34.124.0.0/21", +"35.215.64.0/18", +"35.220.47.0/24", +"35.235.64.0/18", +"35.236.0.0/17", +"35.242.47.0/24", +"35.243.0.0/21", +"34.104.52.0/24", +"34.106.0.0/16", +"34.127.180.0/24", +"35.217.64.0/18", +"35.220.31.0/24", +"35.242.31.0/24", +"34.104.72.0/22", +"34.118.240.0/22", +"34.124.8.0/22", +"34.125.0.0/16", +"35.219.128.0/18"] + +[facebook] +positive=["31.13.24.0/21", +"31.13.64.0/19", +"31.13.64.0/24", +"31.13.69.0/24", +"31.13.70.0/24", +"31.13.71.0/24", +"31.13.72.0/24", +"31.13.73.0/24", +"31.13.75.0/24", +"31.13.76.0/24", +"31.13.77.0/24", +"31.13.78.0/24", +"31.13.79.0/24", +"31.13.80.0/24", +"66.220.144.0/20", +"66.220.144.0/21", +"66.220.149.11/16", +"66.220.152.0/21", +"66.220.158.11/16", +"66.220.159.0/24", +"69.63.176.0/21", +"69.63.176.0/24", +"69.63.184.0/21", +"69.171.224.0/19", +"69.171.224.0/20", +"69.171.224.37/16", +"69.171.229.11/16", +"69.171.239.0/24", +"69.171.240.0/20", +"69.171.242.11/16", +"69.171.255.0/24", +"74.119.76.0/22", +"173.252.64.0/19", +"173.252.70.0/24", +"173.252.96.0/19", +"204.15.20.0/22", +"157.240.0.0/16", +"31.13.24.0/21", +"31.13.64.0/18", +"31.13.65.0/24", +"31.13.66.0/24", +"31.13.67.0/24", +"31.13.68.0/24", +"31.13.69.0/24", +"31.13.70.0/24", +"31.13.71.0/24", +"31.13.72.0/24", +"31.13.73.0/24", +"31.13.74.0/24", +"31.13.75.0/24", +"31.13.76.0/24", +"31.13.77.0/24", +"31.13.78.0/24", +"31.13.79.0/24", +"31.13.80.0/24", +"31.13.81.0/24", +"31.13.82.0/24", +"31.13.83.0/24", +"31.13.84.0/24", +"31.13.85.0/24", +"31.13.86.0/24", +"31.13.87.0/24", +"31.13.88.0/24", +"31.13.89.0/24", +"31.13.92.0/24", +"31.13.93.0/24", +"31.13.94.0/24", +"31.13.95.0/24", +"31.13.96.0/19", +"45.64.40.0/22", +"66.220.144.0/20", +"66.220.144.0/21", +"66.220.152.0/21", +"69.63.176.0/20", +"69.63.176.0/21", +"69.63.184.0/21", +"69.171.224.0/19", +"69.171.224.0/20", +"69.171.240.0/20", +"69.171.250.0/24", +"74.119.76.0/22", +"102.132.96.0/20", +"102.132.96.0/24", +"102.132.99.0/24", +"102.132.100.0/24", +"102.132.101.0/24", +"103.4.96.0/22", +"103.161.130.0/23", +"103.161.130.0/24", +"103.161.131.0/24", +"129.134.0.0/17", +"129.134.25.0/24", +"129.134.26.0/24", +"129.134.27.0/24", +"129.134.28.0/24", +"129.134.29.0/24", +"129.134.30.0/23", +"129.134.30.0/24", +"129.134.31.0/24", +"129.134.160.0/24", +"157.240.0.0/17", +"157.240.1.0/24", +"157.240.2.0/24", +"157.240.3.0/24", +"157.240.6.0/24", +"157.240.7.0/24", +"157.240.8.0/24", +"157.240.9.0/24", +"157.240.10.0/24", +"157.240.11.0/24", +"157.240.12.0/24", +"157.240.13.0/24", +"157.240.14.0/24", +"157.240.15.0/24", +"157.240.16.0/24", +"157.240.17.0/24", +"157.240.18.0/24", +"157.240.19.0/24", +"157.240.20.0/24", +"157.240.21.0/24", +"157.240.22.0/24", +"157.240.23.0/24", +"157.240.24.0/24", +"157.240.25.0/24", +"157.240.26.0/24", +"157.240.27.0/24", +"157.240.28.0/24", +"157.240.30.0/24", +"157.240.192.0/18", +"157.240.192.0/24", +"157.240.194.0/24", +"157.240.195.0/24", +"157.240.196.0/24", +"157.240.197.0/24", +"157.240.198.0/24", +"157.240.199.0/24", +"157.240.200.0/24", +"157.240.201.0/24", +"157.240.203.0/24", +"157.240.204.0/24", +"157.240.205.0/24", +"157.240.206.0/24", +"157.240.208.0/24", +"157.240.209.0/24", +"157.240.210.0/24", +"157.240.211.0/24", +"157.240.212.0/24", +"157.240.213.0/24", +"157.240.214.0/24", +"157.240.215.0/24", +"157.240.216.0/24", +"157.240.217.0/24", +"157.240.218.0/24", +"157.240.221.0/24", +"157.240.222.0/24", +"157.240.223.0/24", +"157.240.225.0/24", +"157.240.226.0/24", +"157.240.227.0/24", +"157.240.229.0/24", +"157.240.231.0/24", +"157.240.232.0/24", +"157.240.233.0/24", +"157.240.234.0/24", +"157.240.235.0/24", +"157.240.236.0/24", +"157.240.237.0/24", +"157.240.238.0/24", +"157.240.239.0/24", +"157.240.240.0/24", +"157.240.241.0/24", +"157.240.242.0/24", +"157.240.243.0/24", +"157.240.244.0/24", +"157.240.245.0/24", +"157.240.247.0/24", +"157.240.249.0/24", +"173.252.64.0/19", +"173.252.88.0/21", +"173.252.96.0/19", +"179.60.192.0/22", +"179.60.192.0/24", +"179.60.193.0/24", +"179.60.194.0/24", +"179.60.195.0/24", +"185.60.216.0/22", +"185.60.216.0/24", +"185.60.217.0/24", +"185.60.218.0/24", +"185.89.218.0/23", +"185.89.218.0/24", +"185.89.219.0/24", +"204.15.20.0/22"] \ No newline at end of file