summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
Diffstat (limited to 'deps')
-rw-r--r--deps/CMakeLists.txt1
-rw-r--r--deps/PackageList.txt4
-rw-r--r--deps/acore/bash-lib/src/common/boolean.sh5
-rwxr-xr-xdeps/acore/joiner/joiner.sh17
-rw-r--r--deps/fkYAML/CMakeLists.txt17
-rw-r--r--deps/fkYAML/fkYAML/node.hpp14730
-rw-r--r--deps/recastnavigation/Detour/CMakeLists.txt2
-rw-r--r--deps/recastnavigation/Recast/CMakeLists.txt2
8 files changed, 14774 insertions, 4 deletions
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 8364181a9f..869351a371 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -45,4 +45,5 @@ endif()
if (BUILD_TOOLS_MAPS)
add_subdirectory(bzip2)
add_subdirectory(libmpq)
+ add_subdirectory(fkYAML)
endif()
diff --git a/deps/PackageList.txt b/deps/PackageList.txt
index f771c3b734..70e9f695c3 100644
--- a/deps/PackageList.txt
+++ b/deps/PackageList.txt
@@ -81,3 +81,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse
{fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams.
https://github.com/fmtlib/fmt
Version: 7.1.3
+
+fkYAML (A C++ header-only YAML library)
+ https://github.com/fktn-k/fkYAML
+ Version: 721edb3e1a817e527fd9e1e18a3bea300822522e
diff --git a/deps/acore/bash-lib/src/common/boolean.sh b/deps/acore/bash-lib/src/common/boolean.sh
new file mode 100644
index 0000000000..4f2e365dbb
--- /dev/null
+++ b/deps/acore/bash-lib/src/common/boolean.sh
@@ -0,0 +1,5 @@
+function isTrue() {
+ local val
+ val=$(echo "$1" | tr '[:upper:]' '[:lower:]')
+ [[ "$val" == "1" || "$val" == "true" || "$val" == "yes" || "$val" == "on" ]]
+} \ No newline at end of file
diff --git a/deps/acore/joiner/joiner.sh b/deps/acore/joiner/joiner.sh
index 1b13007162..a67badaa2b 100755
--- a/deps/acore/joiner/joiner.sh
+++ b/deps/acore/joiner/joiner.sh
@@ -116,7 +116,21 @@ function Joiner:add_repo() (
if [ -e "$path/.git/" ]; then
# if exists , update
echo "Updating $name on branch $branch..."
- git --git-dir="$path/.git/" --work-tree="$path" rev-parse && git --git-dir="$path/.git/" --work-tree="$path" pull origin "$branch" | grep 'Already up-to-date.' && changed="no" || true
+ if ! git --git-dir="$path/.git/" --work-tree="$path" rev-parse >/dev/null 2>&1; then
+ echo "Unable to read repository at $path/.git/"
+ return $FALSE
+ fi
+
+ local pull_output
+ if ! pull_output=$(git --git-dir="$path/.git/" --work-tree="$path" pull origin "$branch" 2>&1); then
+ printf "%s\n" "$pull_output"
+ return $FALSE
+ fi
+
+ printf "%s\n" "$pull_output"
+ if echo "$pull_output" | grep -qE 'Already up[- ]to-date.'; then
+ changed="no"
+ fi
else
# otherwise clone
echo "Cloning $name on branch $branch..."
@@ -440,4 +454,3 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
else
Joiner:_checkOptions $@
fi
-
diff --git a/deps/fkYAML/CMakeLists.txt b/deps/fkYAML/CMakeLists.txt
new file mode 100644
index 0000000000..e186f5293e
--- /dev/null
+++ b/deps/fkYAML/CMakeLists.txt
@@ -0,0 +1,17 @@
+#
+# This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+
+add_library(fkYAML INTERFACE)
+
+target_include_directories(fkYAML INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+
+set_target_properties(fkYAML PROPERTIES FOLDER "deps")
diff --git a/deps/fkYAML/fkYAML/node.hpp b/deps/fkYAML/fkYAML/node.hpp
new file mode 100644
index 0000000000..773e852cf7
--- /dev/null
+++ b/deps/fkYAML/fkYAML/node.hpp
@@ -0,0 +1,14730 @@
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_NODE_HPP
+#define FK_YAML_NODE_HPP
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <initializer_list>
+#include <map>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_MACROS_DEFINE_MACROS_HPP
+#define FK_YAML_DETAIL_MACROS_DEFINE_MACROS_HPP
+
+// #include <fkYAML/detail/macros/version_macros.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+// Check version definitions if already defined.
+#if defined(FK_YAML_MAJOR_VERSION) && defined(FK_YAML_MINOR_VERSION) && defined(FK_YAML_PATCH_VERSION)
+#if FK_YAML_MAJOR_VERSION != 0 || FK_YAML_MINOR_VERSION != 4 || FK_YAML_PATCH_VERSION != 2
+#warning Already included a different version of the fkYAML library!
+#else
+// define macros to skip defining macros down below.
+#define FK_YAML_VERCHECK_SUCCEEDED
+#endif
+#endif
+
+#ifndef FK_YAML_VERCHECK_SUCCEEDED
+
+#define FK_YAML_MAJOR_VERSION 0
+#define FK_YAML_MINOR_VERSION 4
+#define FK_YAML_PATCH_VERSION 2
+
+#define FK_YAML_NAMESPACE_VERSION_CONCAT_IMPL(major, minor, patch) v##major##_##minor##_##patch
+
+#define FK_YAML_NAMESPACE_VERSION_CONCAT(major, minor, patch) FK_YAML_NAMESPACE_VERSION_CONCAT_IMPL(major, minor, patch)
+
+#define FK_YAML_NAMESPACE_VERSION \
+ FK_YAML_NAMESPACE_VERSION_CONCAT(FK_YAML_MAJOR_VERSION, FK_YAML_MINOR_VERSION, FK_YAML_PATCH_VERSION)
+
+#define FK_YAML_NAMESPACE_BEGIN \
+ namespace fkyaml { \
+ inline namespace FK_YAML_NAMESPACE_VERSION {
+
+#define FK_YAML_NAMESPACE_END \
+ } /* inline namespace FK_YAML_NAMESPACE_VERSION */ \
+ } // namespace fkyaml
+
+#define FK_YAML_DETAIL_NAMESPACE_BEGIN \
+ FK_YAML_NAMESPACE_BEGIN \
+ namespace detail {
+
+#define FK_YAML_DETAIL_NAMESPACE_END \
+ } /* namespace detail */ \
+ FK_YAML_NAMESPACE_END
+
+#endif // !defined(FK_YAML_VERCHECK_SUCCEEDED)
+
+// #include <fkYAML/detail/macros/cpp_config_macros.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_MACROS_CPP_CONFIG_MACROS_HPP
+#define FK_YAML_DETAIL_MACROS_CPP_CONFIG_MACROS_HPP
+
+// This file is assumed to be included only by version_macros.hpp file.
+// To avoid redundant inclusion, do not include version_macros.hpp file as the other files do.
+
+// With the MSVC compilers, the value of __cplusplus is by default always "199611L"(C++98).
+// To avoid that, the library instead references _MSVC_LANG which is always set a correct value.
+// See https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ for more details.
+#if defined(_MSVC_LANG) && !defined(__clang__)
+#define FK_YAML_CPLUSPLUS _MSVC_LANG
+#else
+#define FK_YAML_CPLUSPLUS __cplusplus
+#endif
+
+// C++ language standard detection
+// Skip detection if the definitions listed below already exist.
+#if !defined(FK_YAML_HAS_CXX_23) && !defined(FK_YAML_HAS_CXX_20) && !defined(FK_YAML_HAS_CXX_17) && \
+ !defined(FK_YAML_HAS_CXX_14) && !defined(FK_YAML_CXX_11)
+#if FK_YAML_CPLUSPLUS >= 202302L
+#define FK_YAML_HAS_CXX_23
+#define FK_YAML_HAS_CXX_20
+#define FK_YAML_HAS_CXX_17
+#define FK_YAML_HAS_CXX_14
+#elif FK_YAML_CPLUSPLUS >= 202002L
+#define FK_YAML_HAS_CXX_20
+#define FK_YAML_HAS_CXX_17
+#define FK_YAML_HAS_CXX_14
+#elif FK_YAML_CPLUSPLUS >= 201703L
+#define FK_YAML_HAS_CXX_17
+#define FK_YAML_HAS_CXX_14
+#elif FK_YAML_CPLUSPLUS >= 201402L
+#define FK_YAML_HAS_CXX_14
+#endif
+
+// C++11 is the minimum required version of the fkYAML library.
+#define FK_YAML_HAS_CXX_11
+#endif
+
+// switch usage of the deprecated attribute. [[deprecated]] is available since C++14.
+#if defined(FK_YAML_HAS_CXX_14)
+#define FK_YAML_DEPRECATED(msg) [[deprecated(msg)]]
+#else
+#if defined(_MSC_VER)
+#define FK_YAML_DEPRECATED(msg) __declspec(deprecated(msg))
+#elif defined(__GNUC__) || defined(__clang__)
+#define FK_YAML_DEPRECATED(msg) __attribute__((deprecated(msg)))
+#else
+#define FK_YAML_DEPRECATED(msg)
+#endif
+#endif
+
+// switch usage of inline variables which have been available since C++17.
+#if defined(FK_YAML_HAS_CXX_17)
+#define FK_YAML_INLINE_VAR inline
+#else
+#define FK_YAML_INLINE_VAR
+#endif
+
+// switch usage of constexpr keyword depending on active C++ standard.
+#if defined(FK_YAML_HAS_CXX_17)
+#define FK_YAML_CXX17_CONSTEXPR constexpr
+#else
+#define FK_YAML_CXX17_CONSTEXPR
+#endif
+
+// Detect __has_* macros.
+// The following macros replace redundant `defined(__has_*) && __has_*(...)`.
+
+#ifdef __has_include
+#define FK_YAML_HAS_INCLUDE(header) __has_include(header)
+#else
+#define FK_YAML_HAS_INCLUDE(header) (0)
+#endif
+
+#ifdef __has_builtin
+#define FK_YAML_HAS_BUILTIN(builtin) __has_builtin(builtin)
+#else
+#define FK_YAML_HAS_BUILTIN(builtin) (0)
+#endif
+
+#ifdef __has_cpp_attribute
+#define FK_YAML_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
+#else
+#define FK_YAML_HAS_CPP_ATTRIBUTE(attr) (0)
+#endif
+
+#ifdef __has_feature
+#define FK_YAML_HAS_FEATURE(feat) __has_feature(feat)
+#else
+#define FK_YAML_HAS_FEATURE(feat) (0)
+#endif
+
+// switch usage of the no_sanitize attribute only when Clang sanitizer is active.
+#if defined(__clang__) && FK_YAML_HAS_FEATURE(address_sanitizer)
+#define FK_YAML_NO_SANITIZE(...) __attribute__((no_sanitize(__VA_ARGS__)))
+#else
+#define FK_YAML_NO_SANITIZE(...)
+#endif
+
+#if FK_YAML_HAS_INCLUDE(<version>)
+// <version> is available since C++20
+#include <version>
+#endif
+
+//
+// C++ feature detections
+//
+
+// switch usages of the std::to_chars()/std::from_chars() functions which have been available since C++17.
+#if defined(FK_YAML_HAS_CXX_17) && defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
+#define FK_YAML_HAS_TO_CHARS (1)
+#else
+#define FK_YAML_HAS_TO_CHARS (0)
+#endif
+
+// switch usage of char8_t which has been available since C++20.
+#if defined(FK_YAML_HAS_CXX_20) && defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
+#define FK_YAML_HAS_CHAR8_T (1)
+#else
+#define FK_YAML_HAS_CHAR8_T (0)
+#endif
+
+//
+// utility macros
+//
+
+// switch usage of [[likely]] C++ attribute which has been available since C++20.
+#if defined(FK_YAML_HAS_CXX_20) && FK_YAML_HAS_CPP_ATTRIBUTE(likely) >= 201803L
+#define FK_YAML_LIKELY(expr) (!!(expr)) [[likely]]
+#elif FK_YAML_HAS_BUILTIN(__builtin_expect)
+#define FK_YAML_LIKELY(expr) (__builtin_expect(!!(expr), 1))
+#else
+#define FK_YAML_LIKELY(expr) (!!(expr))
+#endif
+
+// switch usage of [[unlikely]] C++ attribute which has been available since C++20.
+#if defined(FK_YAML_HAS_CXX_20) && FK_YAML_HAS_CPP_ATTRIBUTE(unlikely) >= 201803L
+#define FK_YAML_UNLIKELY(expr) (!!(expr)) [[unlikely]]
+#elif FK_YAML_HAS_BUILTIN(__builtin_expect)
+#define FK_YAML_UNLIKELY(expr) (__builtin_expect(!!(expr), 0))
+#else
+#define FK_YAML_UNLIKELY(expr) (!!(expr))
+#endif
+
+#endif /* FK_YAML_DETAIL_MACROS_CPP_CONFIG_MACROS_HPP */
+
+
+#endif /* FK_YAML_DETAIL_MACROS_DEFINE_MACROS_HPP */
+
+// #include <fkYAML/detail/assert.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ASSERT_HPP
+#define FK_YAML_DETAIL_ASSERT_HPP
+
+// if FK_YAML_ASSERT is not user-defined. apply the default assert impl.
+#ifndef FK_YAML_ASSERT
+#ifndef NDEBUG
+#include <cassert>
+#define FK_YAML_ASSERT(x) assert(x)
+#else
+#define FK_YAML_ASSERT(x)
+#endif
+#endif
+
+#endif /* FK_YAML_DETAIL_ASSERT_HPP */
+
+// #include <fkYAML/detail/document_metainfo.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_DOCUMENT_METAINFO_HPP
+#define FK_YAML_DETAIL_DOCUMENT_METAINFO_HPP
+
+#include <string>
+#include <map>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_META_NODE_TRAITS_HPP
+#define FK_YAML_DETAIL_META_NODE_TRAITS_HPP
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/detect.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_META_DETECT_HPP
+#define FK_YAML_DETAIL_META_DETECT_HPP
+
+#include <iterator>
+#include <type_traits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_META_STL_SUPPLEMENT_HPP
+#define FK_YAML_DETAIL_META_STL_SUPPLEMENT_HPP
+
+#include <cstddef>
+#include <type_traits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+#ifdef FK_YAML_HAS_CXX_14
+#include <utility>
+#endif
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// For contributors:
+// This file is for supplementing future C++ STL implementations to utilize some useful features
+// implemented in C++14 or better.
+// This file is needed to keep the fkYAML library requirement to C++11.
+// **DO NOT** implement features which are not included any version of STL in this file.
+// Such implementations must be in the type_traits.hpp file.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef FK_YAML_HAS_CXX_14
+
+/// @brief An alias template for std::add_pointer::type with C++11.
+/// @note std::add_pointer_t is available since C++14.
+/// @sa https://en.cppreference.com/w/cpp/types/add_pointer
+/// @tparam T A type to be added a pointer.
+template <typename T>
+using add_pointer_t = typename std::add_pointer<T>::type;
+
+/// @brief An alias template for std::enable_if::type with C++11.
+/// @note std::enable_if_t is available since C++14.
+/// @sa https://en.cppreference.com/w/cpp/types/enable_if
+/// @tparam Condition A condition tested at compile time.
+/// @tparam T The type defined only if Condition is true.
+template <bool Condition, typename T = void>
+using enable_if_t = typename std::enable_if<Condition, T>::type;
+
+/// @brief A simple implementation to use std::is_null_pointer with C++11.
+/// @note std::is_null_pointer is available since C++14.
+/// @sa https://en.cppreference.com/w/cpp/types/is_null_pointer
+/// @tparam T The type to be checked if it's equal to std::nullptr_t.
+template <typename T>
+struct is_null_pointer : std::is_same<std::nullptr_t, typename std::remove_cv<T>::type> {};
+
+/// @brief An alias template for std::remove_cv::type with C++11.
+/// @note std::remove_cv_t is available since C++14.
+/// @sa https://en.cppreference.com/w/cpp/types/remove_cv
+/// @tparam T A type from which const-volatile qualifiers are removed.
+template <typename T>
+using remove_cv_t = typename std::remove_cv<T>::type;
+
+/// @brief An alias template for std::remove_pointer::type with C++11.
+/// @note std::remove_pointer_t is available since C++14.
+/// @sa https://en.cppreference.com/w/cpp/types/remove_pointer
+/// @tparam T A type from which a pointer is removed.
+template <typename T>
+using remove_pointer_t = typename std::remove_pointer<T>::type;
+
+/// @brief An alias template for std::remove_reference::type with C++11.
+/// @note std::remove_reference_t is available since C++14.
+/// @sa https://en.cppreference.com/w/cpp/types/remove_reference
+/// @tparam T A type from which a reference is removed.
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+
+template <typename T, T... I>
+struct integer_sequence {
+ using value_type = T;
+ static constexpr std::size_t size() noexcept {
+ return sizeof...(I);
+ }
+};
+
+#if !FK_YAML_HAS_BUILTIN(__make_integer_seq) && !FK_YAML_HAS_BUILTIN(__integer_pack)
+
+namespace make_int_seq_impl {
+
+template <typename IntSeq0, typename IntSeq1>
+struct merger;
+
+template <typename T, T... Ints0, T... Ints1>
+struct merger<integer_sequence<T, Ints0...>, integer_sequence<T, Ints1...>> {
+ using type = integer_sequence<T, Ints0..., (sizeof...(Ints0) + Ints1)...>;
+};
+
+template <typename T, std::size_t Num>
+struct generator {
+ using type =
+ typename merger<typename generator<T, Num / 2>::type, typename generator<T, Num - Num / 2>::type>::type;
+};
+
+template <typename T>
+struct generator<T, 0> {
+ using type = integer_sequence<T>;
+};
+
+template <typename T>
+struct generator<T, 1> {
+ using type = integer_sequence<T, 0>;
+};
+
+} // namespace make_int_seq_impl
+
+#endif
+
+template <typename T, T Num>
+using make_integer_sequence
+#if FK_YAML_HAS_BUILTIN(__make_integer_seq)
+ // clang defines built-in __make_integer_seq to generate an integer sequence.
+ = __make_integer_seq<integer_sequence, T, Num>;
+#elif FK_YAML_HAS_BUILTIN(__integer_pack)
+ // GCC or other compilers may implement built-in __integer_pack to generate an
+ // integer sequence.
+ = integer_sequence<T, __integer_pack(Num)...>;
+#else
+ // fallback to the library implementation of make_integer_sequence.
+ = typename make_int_seq_impl::generator<T, Num>::type;
+#endif
+
+template <std::size_t... Idx>
+using index_sequence = integer_sequence<std::size_t, Idx...>;
+
+template <std::size_t Num>
+using make_index_sequence = make_integer_sequence<std::size_t, Num>;
+
+template <typename... Types>
+using index_sequence_for = make_index_sequence<sizeof...(Types)>;
+
+#else // !defined(FK_YAML_HAS_CXX_14)
+
+using std::add_pointer_t;
+using std::enable_if_t;
+using std::index_sequence;
+using std::index_sequence_for;
+using std::integer_sequence;
+using std::is_null_pointer;
+using std::make_index_sequence;
+using std::make_integer_sequence;
+using std::remove_cv_t;
+using std::remove_pointer_t;
+using std::remove_reference_t;
+
+#endif // !defined(FK_YAML_HAS_CXX_14)
+
+#ifndef FK_YAML_HAS_CXX_17
+
+/// @brief A simple implementation to use std::bool_constant with C++11/C++14.
+/// @tparam Val
+template <bool Val>
+using bool_constant = std::integral_constant<bool, Val>;
+
+/// @brief A simple implementation to use std::void_t with C++11/C++14.
+/// @note
+/// std::conjunction is available since C++17.
+/// This is applied when no traits are specified as inputs.
+/// @sa https://en.cppreference.com/w/cpp/types/conjunction
+/// @tparam Traits Type traits to be checked if their ::value are all true.
+template <typename... Traits>
+struct conjunction : std::true_type {};
+
+/// @brief A partial specialization of conjunction if only one Trait is given.
+/// @tparam Trait Type trait to be checked if its ::value is true.
+template <typename Trait>
+struct conjunction<Trait> : Trait {};
+
+/// @brief A partial specialization of conjunction if more than one traits are given.
+/// @tparam First The first type trait to be checked if its ::value is true.
+/// @tparam Rest The rest of traits passed as another conjunction template arguments if First::value is true.
+template <typename First, typename... Rest>
+struct conjunction<First, Rest...> : std::conditional<First::value, conjunction<Rest...>, First>::type {};
+
+/// @brief A simple implementation to use std::disjunction with C++11/C++14.
+/// @note
+/// std::disjunction is available since C++17.
+/// This is applied when no traits are specified as inputs.
+/// @sa https://en.cppreference.com/w/cpp/types/disjunction
+/// @tparam Traits Type traits to be checked if at least one of their ::value is true.
+template <typename... Traits>
+struct disjunction : std::false_type {};
+
+/// @brief A partial specialization of disjunction if only one Trait is given.
+/// @tparam Trait Type trait to be checked if its ::value is true.
+template <typename Trait>
+struct disjunction<Trait> : Trait {};
+
+/// @brief A partial specialization of disjunction if more than one traits are given.
+/// @tparam First The first type trait to be checked if its ::value is true.
+/// @tparam Rest The rest of traits passed as another conjunction template arguments if First::value is false.
+template <typename First, typename... Rest>
+struct disjunction<First, Rest...> : std::conditional<First::value, First, disjunction<Rest...>>::type {};
+
+/// @brief A simple implementation to use std::negation with C++11/C++14.
+/// @note std::negation is available since C++17.
+/// @sa https://en.cppreference.com/w/cpp/types/negation
+/// @tparam Trait Type trait whose ::value is negated.
+template <typename Trait>
+struct negation : std::integral_constant<bool, !Trait::value> {};
+
+/// @brief A helper for void_t.
+/// @tparam Types Any types to be transformed to void type.
+template <typename... Types>
+struct make_void {
+ using type = void;
+};
+
+/// @brief A simple implementation to use std::void_t with C++11/C++14.
+/// @note std::void_t is available since C++17.
+/// @sa https://en.cppreference.com/w/cpp/types/void_t
+/// @tparam Types Any types to be transformed to void type.
+template <typename... Types>
+using void_t = typename make_void<Types...>::type;
+
+#else // !defined(FK_YAML_HAS_CXX_17)
+
+using std::bool_constant;
+using std::conjunction;
+using std::disjunction;
+using std::negation;
+using std::void_t;
+
+#endif // !defined(FK_YAML_HAS_CXX_17)
+
+#ifndef FK_YAML_HAS_CXX_20
+
+/// @brief A simple implementation to use std::remove_cvref_t with C++11/C++14/C++17.
+/// @note std::remove_cvref & std::remove_cvref_t are available since C++20.
+/// @sa https://en.cppreference.com/w/cpp/types/remove_cvref
+/// @tparam T A type from which cv-qualifiers and reference are removed.
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+#else
+
+using std::remove_cvref_t;
+
+#endif
+
+/// @brief A wrapper function to call std::unreachable() (since C++23) or similar compiler specific extensions.
+/// @note This function is implemented only for better code optimization against dead code and thus excluded from
+/// coverage report.
+// LCOV_EXCL_START
+[[noreturn]] inline void unreachable() {
+ // use compiler specific extensions if possible.
+ // undefined behavior should be raised by an empty function with noreturn attribute.
+
+#if defined(FK_YAML_HAS_CXX_23) || (defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L)
+ std::unreachable();
+#elif defined(_MSC_VER) && !defined(__clang__) // MSVC
+ __assume(false);
+#else
+ __builtin_unreachable();
+#endif
+}
+// LCOV_EXCL_STOP
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_META_STL_SUPPLEMENT_HPP */
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A dummy struct to represent detection failure.
+struct nonesuch {
+ nonesuch() = delete;
+ ~nonesuch() = delete;
+ nonesuch(const nonesuch&) = delete;
+ nonesuch(nonesuch&&) = delete;
+ nonesuch& operator=(const nonesuch&) = delete;
+ nonesuch& operator=(nonesuch&&) = delete;
+};
+
+/// @brief namespace to implement detector type traits
+namespace detector_impl {
+
+/// @brief A helper for general type detection.
+/// @tparam Default A type to represent detection failure.
+/// @tparam AlwaysVoid This must be void type.
+/// @tparam Op A type for desired operation type.
+/// @tparam Args Argument types passed to desired operation.
+template <typename Default, typename AlwaysVoid, template <typename...> class Op, typename... Args>
+struct detector : std::false_type {
+ /// @brief A type which represents detection failure.
+ using type = Default;
+};
+
+/// @brief A partial specialization of detector if desired operation type is found.
+/// @tparam Default A type to represent detection failure.
+/// @tparam Op A type for desired operation type.
+/// @tparam Args Argument types passed to desired operation.
+template <typename Default, template <typename...> class Op, typename... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...> : std::true_type {
+ /// @brief A detected type.
+ using type = Op<Args...>;
+};
+
+} // namespace detector_impl
+
+/// @brief Type traits to detect Op operation with Args argument types
+/// @tparam Op A desired operation type.
+/// @tparam Args Argument types passed to desired operation.
+template <template <typename...> class Op, typename... Args>
+using is_detected = detector_impl::detector<nonesuch, void, Op, Args...>;
+
+/// @brief Type traits to represent a detected type.
+/// @tparam Op A type for desired operation type.
+/// @tparam Args Argument types passed to desired operation.
+template <template <typename...> class Op, typename... Args>
+using detected_t = typename detector_impl::detector<nonesuch, void, Op, Args...>::type;
+
+/// @brief Type traits to check if Expected and a detected type are exactly the same.
+/// @tparam Expected An expected detection result type.
+/// @tparam Op A type for desired operation.
+/// @tparam Args Argument types passed to desired operation.
+template <typename Expected, template <typename...> class Op, typename... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+/// @brief namespace for member type detections of aliases and functions.
+namespace detect {
+
+/// @brief The type which represents `iterator` member type.
+/// @tparam T A target type.
+template <typename T>
+using iterator_t = typename T::iterator;
+
+/// @brief The type which represents `key_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using key_type_t = typename T::key_type;
+
+/// @brief The type which represents `mapped_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using mapped_type_t = typename T::mapped_type;
+
+/// @brief The type which represents `value_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using value_type_t = typename T::value_type;
+
+/// @brief The type which represents `difference_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using difference_type_t = typename T::difference_type;
+
+/// @brief The type which represents `pointer` member type.
+/// @tparam T A target type.
+template <typename T>
+using pointer_t = typename T::pointer;
+
+/// @brief The type which represents `reference` member type.
+/// @tparam T A target type.
+template <typename T>
+using reference_t = typename T::reference;
+
+/// @brief The type which represents `iterator_category` member type.
+/// @tparam T A target type.
+template <typename T>
+using iterator_category_t = typename T::iterator_category;
+
+/// @brief The type which represents `container_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using container_type_t = typename T::container_type;
+
+/// @brief The type which represents emplace member function.
+/// @tparam T A target type.
+template <typename T, typename... Args>
+using emplace_fn_t = decltype(std::declval<T>().emplace(std::declval<Args>()...));
+
+/// @brief The type which represents reserve member function.
+/// @tparam T A target type.
+template <typename T>
+using reserve_fn_t = decltype(std::declval<T>().reserve(std::declval<typename remove_cvref_t<T>::size_type>()));
+
+/// @brief Type traits to check if T has `iterator` member type.
+/// @tparam T A target type.
+template <typename T>
+using has_iterator = is_detected<iterator_t, remove_cvref_t<T>>;
+
+/// @brief Type traits to check if T has `key_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using has_key_type = is_detected<key_type_t, remove_cvref_t<T>>;
+
+/// @brief Type traits to check if T has `mapped_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using has_mapped_type = is_detected<mapped_type_t, remove_cvref_t<T>>;
+
+/// @brief Type traits to check if T has `value_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using has_value_type = is_detected<value_type_t, remove_cvref_t<T>>;
+
+/// @brief Type traits to check if T is a std::iterator_traits like type.
+/// @tparam T A target type.
+template <typename T>
+struct is_iterator_traits : conjunction<
+ is_detected<difference_type_t, remove_cvref_t<T>>, has_value_type<remove_cvref_t<T>>,
+ is_detected<pointer_t, remove_cvref_t<T>>, is_detected<reference_t, remove_cvref_t<T>>,
+ is_detected<iterator_category_t, remove_cvref_t<T>>> {};
+
+/// @brief Type traits to check if T has `container_type` member type.
+/// @tparam T A target type.
+template <typename T>
+using has_container_type = is_detected<container_type_t, remove_cvref_t<T>>;
+
+/// @brief Type traits to check if T has reserve member function.
+/// @tparam T A target type.
+template <typename T>
+using has_reserve = is_detected<reserve_fn_t, T>;
+
+// fallback to these STL functions.
+using std::begin;
+using std::end;
+
+/// @brief Type traits to check if begin/end functions can be called on a T object.
+/// @tparam T A target type.
+template <typename T, typename = void>
+struct has_begin_end : std::false_type {};
+
+/// @brief Type traits to check if begin/end functions can be called on a T object.
+/// @tparam T A target type.
+template <typename T>
+struct has_begin_end<T, void_t<decltype(begin(std::declval<T>()), end(std::declval<T>()))>> : std::true_type {};
+
+} // namespace detect
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_META_DETECT_HPP */
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_META_TYPE_TRAITS_HPP
+#define FK_YAML_DETAIL_META_TYPE_TRAITS_HPP
+
+#include <iterator>
+#include <type_traits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Type trait to check if T and U are comparable types.
+/// @tparam Comparator An object type to compare T and U objects.
+/// @tparam T A type for comparison.
+/// @tparam U The other type for comparison.
+/// @tparam typename Placeholder for determining T and U are comparable types.
+template <typename Comparator, typename T, typename U, typename = void>
+struct is_comparable : std::false_type {};
+
+/// @brief A partial specialization of is_comparable if T and U are comparable types.
+/// @tparam Comparator An object type to compare T and U objects.
+/// @tparam T A type for comparison.
+/// @tparam U The other type for comparison.
+template <typename Comparator, typename T, typename U>
+struct is_comparable<
+ Comparator, T, U,
+ void_t<
+ decltype(std::declval<Comparator>()(std::declval<T>(), std::declval<U>())),
+ decltype(std::declval<Comparator>()(std::declval<U>(), std::declval<T>()))>> : std::true_type {};
+
+/// @brief Type trait to check if KeyType can be used as key type.
+/// @tparam Comparator An object type to compare T and U objects.
+/// @tparam ObjectKeyType The original key type.
+/// @tparam KeyType A type to be used as key type.
+template <typename Comparator, typename ObjectKeyType, typename KeyType>
+using is_usable_as_key_type = is_comparable<Comparator, ObjectKeyType, KeyType>;
+
+/// @brief Type trait to check if T is of non-boolean integral types.
+/// @tparam T A type to be checked.
+template <typename T>
+using is_non_bool_integral = conjunction<std::is_integral<T>, negation<std::is_same<bool, T>>>;
+
+/// @brief Type traits to check if T is a complete type.
+/// @tparam T A type to be checked if a complete type.
+/// @tparam typename N/A
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+/// @brief A partial specialization of is_complete_type if T is a complete type.
+/// @tparam T
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+/// @brief A utility alias to test if the value type of `ItrType` is `T`.
+/// @tparam ItrType An iterator type.
+/// @tparam T The target iterator value type.
+template <typename ItrType, typename T>
+using is_iterator_of = std::is_same<remove_cv_t<typename std::iterator_traits<ItrType>::value_type>, T>;
+
+/// @brief A utility struct to generate static constant instance.
+/// @tparam T A target type for the resulting static constant instance.
+template <typename T>
+struct static_const {
+ static FK_YAML_INLINE_VAR constexpr T value {}; // NOLINT(readability-identifier-naming)
+};
+
+#ifndef FK_YAML_HAS_CXX_17
+/// @brief A instantiation of static_const::value instance.
+/// @note This is required if inline variables are not available. C++11-14 do not provide such a feature yet.
+/// @tparam T A target type for the resulting static constant instance.
+template <typename T>
+constexpr T static_const<T>::value;
+#endif
+
+/// @brief A helper structure for tag dispatch.
+/// @tparam T A tag type.
+template <typename T>
+struct type_tag {
+ /// @brief A tagged type.
+ using type = T;
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_META_TYPE_TRAITS_HPP */
+
+// #include <fkYAML/fkyaml_fwd.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_FKYAML_FWD_HPP
+#define FK_YAML_FKYAML_FWD_HPP
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <vector>
+
+// #include <fkYAML/detail/macros/version_macros.hpp>
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+/// @brief An ADL friendly converter between basic_node objects and native data objects.
+/// @tparam ValueType A target data type.
+/// @sa https://fktn-k.github.io/fkYAML/api/node_value_converter/
+template <typename ValueType, typename = void>
+class node_value_converter;
+
+/// @brief A class to store value of YAML nodes.
+/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/
+template <
+ template <typename, typename...> class SequenceType = std::vector,
+ template <typename, typename, typename...> class MappingType = std::map, typename BooleanType = bool,
+ typename IntegerType = std::int64_t, typename FloatNumberType = double, typename StringType = std::string,
+ template <typename, typename = void> class ConverterType = node_value_converter>
+class basic_node;
+
+/// @brief default YAML node value container.
+/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/node/
+using node = basic_node<>;
+
+/// @brief A minimal map-like container which preserves insertion order.
+/// @tparam Key A type for keys.
+/// @tparam Value A type for values.
+/// @tparam IgnoredCompare A placeholder for key comparison. This will be ignored.
+/// @tparam Allocator A class for allocators.
+/// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+template <typename Key, typename Value, typename IgnoredCompare, typename Allocator>
+class ordered_map;
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_FKYAML_FWD_HPP */
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/////////////////////////////
+// is_basic_node traits
+/////////////////////////////
+
+/// @brief Actual implementation of the is_basic_node type traits struct.
+/// @tparam T A class to be checked if it's a basic_node template class instance type.
+template <typename T>
+struct is_basic_node_impl : std::false_type {};
+
+/// @brief A partial specialization of is_basic_node_impl for basic_node template class.
+/// @tparam SequenceType A type for sequence node value containers.
+/// @tparam MappingType A type for mapping node value containers.
+/// @tparam BooleanType A type for boolean node values.
+/// @tparam IntegerType A type for integer node values.
+/// @tparam FloatNumberType A type for float number node values.
+/// @tparam StringType A type for string node values.
+/// @tparam Converter A type for node-value converter
+template <
+ template <typename, typename...> class SequenceType, template <typename, typename, typename...> class MappingType,
+ typename BooleanType, typename IntegerType, typename FloatNumberType, typename StringType,
+ template <typename, typename> class Converter>
+struct is_basic_node_impl<
+ basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, Converter>>
+ : std::true_type {};
+
+/// @brief A struct to check the template parameter class is a basic_node template class instance type.
+/// @tparam T A class to be checked if it's a basic_node template class instance type.
+template <typename T>
+struct is_basic_node : is_basic_node_impl<remove_cvref_t<T>> {};
+
+///////////////////////////////////
+// is_node_ref_storage traits
+///////////////////////////////////
+
+// forward declaration for node_ref_storage<...>
+template <typename>
+class node_ref_storage;
+
+/// @brief A struct to check the template parameter class is a kind of node_ref_storage_template class.
+/// @tparam T A type to be checked if it's a kind of node_ref_storage template class.
+template <typename T>
+struct is_node_ref_storage : std::false_type {};
+
+/// @brief A partial specialization for node_ref_storage template class.
+/// @tparam T A template parameter type of node_ref_storage template class.
+template <typename T>
+struct is_node_ref_storage<node_ref_storage<T>> : std::true_type {};
+
+///////////////////////////////////////////////////////
+// basic_node conversion API representative types
+///////////////////////////////////////////////////////
+
+/// @brief A type represent from_node function.
+/// @tparam T A type which provides from_node function.
+/// @tparam Args Argument types passed to from_node function.
+template <typename T, typename... Args>
+using from_node_function_t = decltype(T::from_node(std::declval<Args>()...));
+
+/// @brief A type which represent to_node function.
+/// @tparam T A type which provides to_node function.
+/// @tparam Args Argument types passed to to_node function.
+template <typename T, typename... Args>
+using to_node_function_t = decltype(T::to_node(std::declval<Args>()...));
+
+///////////////////////////////////////////////////
+// basic_node conversion API detection traits
+///////////////////////////////////////////////////
+
+/// @brief Type traits to check if T is a compatible type for BasicNodeType in terms of from_node function.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A target type passed to from_node function.
+/// @tparam typename N/A
+template <typename BasicNodeType, typename T, typename = void>
+struct has_from_node : std::false_type {};
+
+/// @brief A partial specialization of has_from_node if T is not a basic_node template instance type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A target type passed to from_node function.
+template <typename BasicNodeType, typename T>
+struct has_from_node<BasicNodeType, T, enable_if_t<negation<is_basic_node<T>>::value>> {
+ using converter = typename BasicNodeType::template value_converter_type<T, void>;
+
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ static constexpr bool value =
+ is_detected_exact<void, from_node_function_t, converter, const BasicNodeType&, T&>::value;
+};
+
+/// @brief Type traits to check if T is a compatible type for BasicNodeType in terms of to_node function.
+/// @warning Do not pass basic_node type as BasicNodeType to avoid infinite type instantiation.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A target type passed to to_node function.
+/// @tparam typename N/A
+template <typename BasicNodeType, typename T, typename = void>
+struct has_to_node : std::false_type {};
+
+/// @brief A partial specialization of has_to_node if T is not a basic_node template instance type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A target type passed to to_node function.
+template <typename BasicNodeType, typename T>
+struct has_to_node<BasicNodeType, T, enable_if_t<negation<is_basic_node<T>>::value>> {
+ using converter = typename BasicNodeType::template value_converter_type<T, void>;
+
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ static constexpr bool value = is_detected_exact<void, to_node_function_t, converter, BasicNodeType&, T>::value;
+};
+
+///////////////////////////////////////
+// is_node_compatible_type traits
+///////////////////////////////////////
+
+/// @brief Type traits implementation of is_node_compatible_type to check if CompatibleType is a compatible type for
+/// BasicNodeType.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatibleType A target type for compatibility check.
+/// @tparam typename N/A
+template <typename BasicNodeType, typename CompatibleType, typename = void>
+struct is_node_compatible_type_impl : std::false_type {};
+
+/// @brief A partial specialization of is_node_compatible_type_impl if CompatibleType is a complete type and is
+/// compatible for BasicNodeType.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatibleType A target type for compatibility check.
+template <typename BasicNodeType, typename CompatibleType>
+struct is_node_compatible_type_impl<
+ BasicNodeType, CompatibleType,
+ enable_if_t<conjunction<is_complete_type<CompatibleType>, has_to_node<BasicNodeType, CompatibleType>>::value>>
+ : std::true_type {};
+
+/// @brief Type traits to check if CompatibleType is a compatible type for BasicNodeType.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatibleType A target type for compatibility check.
+template <typename BasicNodeType, typename CompatibleType>
+struct is_node_compatible_type : is_node_compatible_type_impl<BasicNodeType, CompatibleType> {};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_META_NODE_TRAITS_HPP */
+
+// #include <fkYAML/yaml_version_type.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_YAML_VERSION_TYPE_HPP
+#define FK_YAML_YAML_VERSION_TYPE_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+enum class yaml_version_type : std::uint8_t {
+ VERSION_1_1, //!< YAML version 1.1
+ VERSION_1_2, //!< YAML version 1.2
+};
+
+inline const char* to_string(yaml_version_type t) noexcept {
+ switch (t) {
+ case yaml_version_type::VERSION_1_1:
+ return "VERSION_1_1";
+ case yaml_version_type::VERSION_1_2:
+ return "VERSION_1_2";
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+}
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_YAML_VERSION_TYPE_HPP */
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief The set of directives for a YAML document.
+template <typename BasicNodeType, typename = enable_if_t<is_basic_node<BasicNodeType>::value>>
+struct document_metainfo {
+ /// The YAML version used for the YAML document.
+ yaml_version_type version {yaml_version_type::VERSION_1_2};
+ /// Whether the YAML version has been specified.
+ bool is_version_specified {false};
+ /// The prefix of the primary handle.
+ std::string primary_handle_prefix;
+ /// The prefix of the secondary handle.
+ std::string secondary_handle_prefix;
+ /// The map of handle-prefix pairs.
+ std::map<std::string /*handle*/, std::string /*prefix*/> named_handle_map;
+ /// The map of anchor node which allows for key duplication.
+ std::multimap<std::string /*anchor name*/, BasicNodeType> anchor_table {};
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_DOCUMENT_METAINFO_HPP */
+
+// #include <fkYAML/detail/exception_safe_allocation.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP
+#define FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP
+
+#include <memory>
+#include <utility>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Helper struct which ensures destruction/deallocation of heap-allocated objects.
+/// @tparam ObjT Object type.
+/// @tparam AllocTraits Allocator traits type for the object.
+template <typename ObjT, typename AllocTraits>
+struct tidy_guard {
+ tidy_guard() = delete;
+
+ /// @brief Construct a tidy_guard with a pointer to the object.
+ /// @param p_obj
+ tidy_guard(ObjT* p_obj) noexcept
+ : p_obj(p_obj) {
+ }
+
+ // move-only
+ tidy_guard(const tidy_guard&) = delete;
+ tidy_guard& operator=(const tidy_guard&) = delete;
+
+ /// @brief Move constructs a tidy_guard object.
+ tidy_guard(tidy_guard&&) = default;
+
+ /// @brief Move assigns a tidy_guard object.
+ /// @return Reference to this tidy_guard object.
+ tidy_guard& operator=(tidy_guard&&) = default;
+
+ /// @brief Destroys this tidy_guard object. Destruction/deallocation happen if the pointer is not null.
+ ~tidy_guard() {
+ if FK_YAML_UNLIKELY (p_obj != nullptr) {
+ typename AllocTraits::allocator_type alloc {};
+ AllocTraits::destroy(alloc, p_obj);
+ AllocTraits::deallocate(alloc, p_obj, 1);
+ p_obj = nullptr;
+ }
+ }
+
+ /// @brief Get the pointer to the object.
+ /// @return The pointer to the object.
+ ObjT* get() const noexcept {
+ return p_obj;
+ }
+
+ /// @brief Checks if the pointer is not null.
+ explicit operator bool() const noexcept {
+ return p_obj != nullptr;
+ }
+
+ /// @brief Releases the pointer to the object. No destruction/deallocation happen after this function gets called.
+ /// @return The pointer to the object.
+ ObjT* release() noexcept {
+ ObjT* ret = p_obj;
+ p_obj = nullptr;
+ return ret;
+ }
+
+ /// @brief The pointer to the object.
+ ObjT* p_obj {nullptr};
+};
+
+/// @brief Allocates and constructs an `ObjT` object with given arguments.
+/// @tparam ObjT The object type.
+/// @tparam ...Args The argument types.
+/// @param ...args The arguments for construction.
+/// @return An address of allocated memory on the heap.
+template <typename ObjT, typename... Args>
+inline ObjT* create_object(Args&&... args) {
+ using alloc_type = std::allocator<ObjT>;
+ using alloc_traits_type = std::allocator_traits<alloc_type>;
+
+ alloc_type alloc {};
+ tidy_guard<ObjT, alloc_traits_type> tg {alloc_traits_type::allocate(alloc, 1)};
+ alloc_traits_type::construct(alloc, tg.get(), std::forward<Args>(args)...);
+
+ FK_YAML_ASSERT(tg);
+ return tg.release();
+}
+
+/// @brief Destroys and deallocates an `ObjT` object.
+/// @tparam ObjT The object type.
+/// @param p_obj A pointer to the object.
+template <typename ObjT>
+inline void destroy_object(ObjT* p_obj) {
+ FK_YAML_ASSERT(p_obj != nullptr);
+ std::allocator<ObjT> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, p_obj);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, p_obj, 1);
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP */
+
+// #include <fkYAML/detail/input/deserializer.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_DESERIALIZER_HPP
+#define FK_YAML_DETAIL_INPUT_DESERIALIZER_HPP
+
+#include <algorithm>
+#include <deque>
+#include <vector>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/document_metainfo.hpp>
+
+// #include <fkYAML/detail/input/lexical_analyzer.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_LEXICAL_ANALYZER_HPP
+#define FK_YAML_DETAIL_INPUT_LEXICAL_ANALYZER_HPP
+
+#include <algorithm>
+#include <cctype>
+#include <cstdlib>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+// #include <fkYAML/detail/encodings/uri_encoding.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ENCODINGS_URI_ENCODING_HPP
+#define FK_YAML_DETAIL_ENCODINGS_URI_ENCODING_HPP
+
+#include <cctype>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A class which handles URI encodings.
+class uri_encoding {
+public:
+ /// @brief Validates the encoding of the given character sequence.
+ /// @param begin An iterator to the first element of the character sequence.
+ /// @param end An iterator to the past-the-end element of the character sequence.
+ /// @return true if all the characters are valid, false otherwise.
+ static bool validate(const char* begin, const char* end) noexcept {
+ if (begin == end) {
+ return true;
+ }
+
+ const char* current = begin;
+
+ for (; current != end; ++current) {
+ if (*current == '%') {
+ const bool are_valid_octets = validate_octets(++current, end);
+ if (!are_valid_octets) {
+ return false;
+ }
+
+ continue;
+ }
+
+ const bool is_allowed_character = validate_character(*current);
+ if (!is_allowed_character) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+private:
+ /// @brief Validates the given octets.
+ /// @param begin An iterator to the first octet.
+ /// @param end An iterator to the past-the-end element of the whole character sequence.
+ /// @return true if the octets are valid, false otherwise.
+ static bool validate_octets(const char*& begin, const char*& end) {
+ for (int i = 0; i < 2; i++, ++begin) {
+ if (begin == end) {
+ return false;
+ }
+
+ // Normalize a character for a-f/A-F comparison
+ const int octet = std::tolower(*begin);
+
+ if ('0' <= octet && octet <= '9') {
+ continue;
+ }
+
+ if ('a' <= octet && octet <= 'f') {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /// @brief Verify if the given character is allowed as a URI character.
+ /// @param c The target character.
+ /// @return true if the given character is allowed as a URI character, false otherwise.
+ static bool validate_character(const char c) {
+ // Check if the current character is one of reserved/unreserved characters which are allowed for
+ // use. See the following links for details:
+ // * reserved characters: https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+ // * unreserved characters: https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
+
+ switch (c) {
+ // reserved characters (gen-delims)
+ case ':':
+ case '/':
+ case '?':
+ case '#':
+ case '[':
+ case ']':
+ case '@':
+ // reserved characters (sub-delims)
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ // unreserved characters
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ return true;
+ default:
+ // alphabets and numbers are also allowed.
+ return static_cast<bool>(std::isalnum(c));
+ }
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_ENCODINGS_URI_ENCODING_HPP */
+
+// #include <fkYAML/detail/encodings/utf_encodings.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ENCODINGS_UTF_ENCODINGS_HPP
+#define FK_YAML_DETAIL_ENCODINGS_UTF_ENCODINGS_HPP
+
+#include <array>
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/exception.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_EXCEPTION_HPP
+#define FK_YAML_EXCEPTION_HPP
+
+#include <array>
+#include <initializer_list>
+#include <stdexcept>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/string_formatter.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_STRING_FORMATTER_HPP
+#define FK_YAML_DETAIL_STRING_FORMATTER_HPP
+
+#include <cstdarg>
+#include <cstdio>
+#include <memory>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+inline std::string format(const char* fmt, ...) {
+ // NOLINTBEGIN(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay)
+ va_list vl;
+ va_start(vl, fmt);
+ int size = std::vsnprintf(nullptr, 0, fmt, vl);
+ va_end(vl);
+
+ // LCOV_EXCL_START
+ if (size < 0) {
+ return "";
+ }
+ // LCOV_EXCL_STOP
+
+ const std::unique_ptr<char[]> buffer {new char[size + 1] {}};
+
+ va_start(vl, fmt);
+ size = std::vsnprintf(buffer.get(), size + 1, fmt, vl);
+ va_end(vl);
+ // NOLINTEND(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay)
+
+ return {buffer.get(), static_cast<std::size_t>(size)};
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_STRING_FORMATTER_HPP */
+
+// #include <fkYAML/detail/types/node_t.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_TYPES_NODE_T_HPP
+#define FK_YAML_DETAIL_TYPES_NODE_T_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/node_type.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_NODE_TYPE_HPP
+#define FK_YAML_NODE_TYPE_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+enum class node_type : std::uint8_t {
+ SEQUENCE, //!< sequence value type
+ MAPPING, //!< mapping value type
+ NULL_OBJECT, //!< null value type
+ BOOLEAN, //!< boolean value type
+ INTEGER, //!< integer value type
+ FLOAT, //!< float point value type
+ STRING, //!< string value type
+};
+
+inline const char* to_string(node_type t) noexcept {
+ switch (t) {
+ case node_type::SEQUENCE:
+ return "SEQUENCE";
+ case node_type::MAPPING:
+ return "MAPPING";
+ case node_type::NULL_OBJECT:
+ return "NULL_OBJECT";
+ case node_type::BOOLEAN:
+ return "BOOLEAN";
+ case node_type::INTEGER:
+ return "INTEGER";
+ case node_type::FLOAT:
+ return "FLOAT";
+ case node_type::STRING:
+ return "STRING";
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+}
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_NODE_TYPE_HPP */
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Definition of node value types.
+enum class node_t : std::uint8_t {
+ SEQUENCE, //!< sequence value type
+ MAPPING, //!< mapping value type
+ NULL_OBJECT, //!< null value type
+ BOOLEAN, //!< boolean value type
+ INTEGER, //!< integer value type
+ FLOAT_NUMBER, //!< float number value type
+ STRING, //!< string value type
+};
+
+inline const char* to_string(node_t t) noexcept {
+ switch (t) {
+ case node_t::SEQUENCE:
+ return "sequence";
+ case node_t::MAPPING:
+ return "mapping";
+ case node_t::NULL_OBJECT:
+ return "null";
+ case node_t::BOOLEAN:
+ return "boolean";
+ case node_t::INTEGER:
+ return "integer";
+ case node_t::FLOAT_NUMBER:
+ return "float";
+ case node_t::STRING:
+ return "string";
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+}
+
+inline node_t convert_from_node_type(node_type t) {
+ switch (t) {
+ case node_type::SEQUENCE:
+ return node_t::SEQUENCE;
+ case node_type::MAPPING:
+ return node_t::MAPPING;
+ case node_type::NULL_OBJECT:
+ return node_t::NULL_OBJECT;
+ case node_type::BOOLEAN:
+ return node_t::BOOLEAN;
+ case node_type::INTEGER:
+ return node_t::INTEGER;
+ case node_type::FLOAT:
+ return node_t::FLOAT_NUMBER;
+ case node_type::STRING:
+ return node_t::STRING;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+}
+
+inline node_type convert_to_node_type(node_t t) {
+ switch (t) {
+ case node_t::SEQUENCE:
+ return node_type::SEQUENCE;
+ case node_t::MAPPING:
+ return node_type::MAPPING;
+ case node_t::NULL_OBJECT:
+ return node_type::NULL_OBJECT;
+ case node_t::BOOLEAN:
+ return node_type::BOOLEAN;
+ case node_t::INTEGER:
+ return node_type::INTEGER;
+ case node_t::FLOAT_NUMBER:
+ return node_type::FLOAT;
+ case node_t::STRING:
+ return node_type::STRING;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_TYPES_NODE_T_HPP */
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+/// @brief A base exception class used in fkYAML library.
+/// @sa https://fktn-k.github.io/fkYAML/api/exception/
+class exception : public std::exception {
+public:
+ /// @brief Construct a new exception object without any error messages.
+ /// @sa https://fktn-k.github.io/fkYAML/api/exception/constructor/
+ exception() = default;
+
+ /// @brief Construct a new exception object with an error message.
+ /// @param[in] msg An error message.
+ /// @sa https://fktn-k.github.io/fkYAML/api/exception/constructor/
+ explicit exception(const char* msg) noexcept {
+ if (msg) {
+ m_error_msg = msg;
+ }
+ }
+
+public:
+ /// @brief Returns an error message internally held. If nothing, a non-null, empty string will be returned.
+ /// @return An error message internally held. The message might be empty.
+ /// @sa https://fktn-k.github.io/fkYAML/api/exception/what/
+ const char* what() const noexcept override {
+ return m_error_msg.c_str();
+ }
+
+private:
+ /// An error message holder.
+ std::string m_error_msg;
+};
+
+/// @brief An exception class indicating an encoding error.
+/// @sa https://fktn-k.github.io/fkYAML/api/exception/invalid_encoding/
+class invalid_encoding : public exception {
+public:
+ /// @brief Construct a new invalid_encoding object for UTF-8 related errors.
+ /// @param msg An error message.
+ /// @param u8 The UTF-8 character bytes.
+ explicit invalid_encoding(const char* msg, const std::initializer_list<uint8_t>& u8) noexcept
+ : exception(generate_error_message(msg, u8).c_str()) {
+ }
+
+ /// @brief Construct a new invalid_encoding object for UTF-16 related errors.
+ /// @param msg An error message.
+ /// @param u16_h The first UTF-16 encoded element used for the UTF-8 encoding.
+ /// @param u16_l The second UTF-16 encoded element used for the UTF-8 encoding.
+ explicit invalid_encoding(const char* msg, std::array<char16_t, 2> u16) noexcept
+ : exception(generate_error_message(msg, u16).c_str()) {
+ }
+
+ /// @brief Construct a new invalid_encoding object for UTF-32 related errors.
+ /// @param msg An error message.
+ /// @param u32 The UTF-32 encoded element used for the UTF-8 encoding.
+ explicit invalid_encoding(const char* msg, char32_t u32) noexcept
+ : exception(generate_error_message(msg, u32).c_str()) {
+ }
+
+private:
+ static std::string generate_error_message(const char* msg, const std::initializer_list<uint8_t>& u8) noexcept {
+ const auto* itr = u8.begin();
+ const auto* end_itr = u8.end();
+ std::string formatted = detail::format("invalid_encoding: %s in=[ 0x%02x", msg, *itr++);
+ while (itr != end_itr) {
+ formatted += detail::format(", 0x%02x", *itr++);
+ }
+ formatted += " ]";
+ return formatted;
+ }
+
+ /// @brief Generate an error message from the given parameters for the UTF-16 encoding.
+ /// @param msg An error message.
+ /// @param h The first UTF-16 encoded element used for the UTF-8 encoding.
+ /// @param l The second UTF-16 encoded element used for the UTF-8 encoding.
+ /// @return A generated error message.
+ static std::string generate_error_message(const char* msg, std::array<char16_t, 2> u16) noexcept {
+ // uint16_t is large enough for UTF-16 encoded elements.
+ return detail::format(
+ "invalid_encoding: %s in=[ 0x%04x, 0x%04x ]",
+ msg,
+ static_cast<uint16_t>(u16[0]),
+ static_cast<uint16_t>(u16[1]));
+ }
+
+ /// @brief Generate an error message from the given parameters for the UTF-32 encoding.
+ /// @param msg An error message.
+ /// @param u32 The UTF-32 encoded element used for the UTF-8 encoding.
+ /// @return A generated error message.
+ static std::string generate_error_message(const char* msg, char32_t u32) noexcept {
+ // uint32_t is large enough for UTF-32 encoded elements.
+ return detail::format("invalid_encoding: %s in=0x%08x", msg, static_cast<uint32_t>(u32));
+ }
+};
+
+/// @brief An exception class indicating an error in parsing.
+/// @sa https://fktn-k.github.io/fkYAML/api/exception/parse_error/
+class parse_error : public exception {
+public:
+ /// @brief Constructs a new parse_error object with an error message and counts of lines and columns at the error.
+ /// @param[in] msg An error message.
+ /// @param[in] lines Count of lines.
+ /// @param[in] cols_in_line Count of columns.
+ explicit parse_error(const char* msg, uint32_t lines, uint32_t cols_in_line) noexcept
+ : exception(generate_error_message(msg, lines, cols_in_line).c_str()) {
+ }
+
+private:
+ static std::string generate_error_message(const char* msg, uint32_t lines, uint32_t cols_in_line) noexcept {
+ return detail::format("parse_error: %s (at line %u, column %u)", msg, lines, cols_in_line);
+ }
+};
+
+/// @brief An exception class indicating an invalid type conversion.
+/// @sa https://fktn-k.github.io/fkYAML/api/exception/type_error/
+class type_error : public exception {
+public:
+ /// @brief Construct a new type_error object with an error message and a node type.
+ /// @param[in] msg An error message.
+ /// @param[in] type The type of a source node value.
+ explicit type_error(const char* msg, node_type type) noexcept
+ : exception(generate_error_message(msg, type).c_str()) {
+ }
+
+ /// @brief Construct a new type_error object with an error message and a node type.
+ /// @deprecated Use type_error(const char*, node_type) constructor. (since 0.3.12).
+ /// @param[in] msg An error message.
+ /// @param[in] type The type of a source node value.
+ FK_YAML_DEPRECATED("Since 0.3.12; Use explicit type_error(const char*, node_type)")
+ explicit type_error(const char* msg, detail::node_t type) noexcept
+ : type_error(msg, detail::convert_to_node_type(type)) {
+ }
+
+private:
+ /// @brief Generate an error message from given parameters.
+ /// @param msg An error message.
+ /// @param type The type of a source node value.
+ /// @return A generated error message.
+ static std::string generate_error_message(const char* msg, node_type type) noexcept {
+ return detail::format("type_error: %s type=%s", msg, to_string(type));
+ }
+};
+
+/// @brief An exception class indicating an out-of-range error.
+/// @sa https://fktn-k.github.io/fkYAML/api/exception/out_of_range/
+class out_of_range : public exception {
+public:
+ /// @brief Construct a new out_of_range object with an invalid index value.
+ /// @param[in] index An invalid index value.
+ explicit out_of_range(int index) noexcept
+ : exception(generate_error_message(index).c_str()) {
+ }
+
+ /// @brief Construct a new out_of_range object with invalid key contents.
+ /// @param[in] key Invalid key contents
+ explicit out_of_range(const char* key) noexcept
+ : exception(generate_error_message(key).c_str()) {
+ }
+
+private:
+ static std::string generate_error_message(int index) noexcept {
+ return detail::format("out_of_range: index %d is out of range", index);
+ }
+
+ static std::string generate_error_message(const char* key) noexcept {
+ return detail::format("out_of_range: key \'%s\' is not found.", key);
+ }
+};
+
+/// @brief An exception class indicating an invalid tag.
+/// @sa https://fktn-k.github.io/fkYAML/api/exception/invalid_tag/
+class invalid_tag : public exception {
+public:
+ /// @brief Constructs a new invalid_tag object with an error message and invalid tag contents.
+ /// @param[in] msg An error message.
+ /// @param[in] tag Invalid tag contents.
+ explicit invalid_tag(const char* msg, const char* tag)
+ : exception(generate_error_message(msg, tag).c_str()) {
+ }
+
+private:
+ static std::string generate_error_message(const char* msg, const char* tag) noexcept {
+ return detail::format("invalid_tag: %s tag=%s", msg, tag);
+ }
+};
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_EXCEPTION_HPP */
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/////////////////////////
+// UTF-8 Encoding ///
+/////////////////////////
+
+/// @brief A class which handles UTF-8 encodings.
+namespace utf8 {
+
+/// @brief Query the number of UTF-8 character bytes with the first byte.
+/// @param first_byte The first byte of a UTF-8 character.
+/// @return The number of UTF-8 character bytes.
+inline uint32_t get_num_bytes(uint8_t first_byte) {
+ // The first byte starts with 0b0XXX'XXXX -> 1-byte character
+ if FK_YAML_LIKELY (first_byte < 0x80) {
+ return 1;
+ }
+ // The first byte starts with 0b110X'XXXX -> 2-byte character
+ if ((first_byte & 0xE0) == 0xC0) {
+ return 2;
+ }
+ // The first byte starts with 0b1110'XXXX -> 3-byte character
+ if ((first_byte & 0xF0) == 0xE0) {
+ return 3;
+ }
+ // The first byte starts with 0b1111'0XXX -> 4-byte character
+ if ((first_byte & 0xF8) == 0xF0) {
+ return 4;
+ }
+
+ // The first byte starts with 0b10XX'XXXX or 0b1111'1XXX -> invalid
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first_byte});
+}
+
+/// @brief Checks if `byte` is a valid 1-byte UTF-8 character.
+/// @param[in] byte The byte value.
+/// @return true if `byte` is a valid 1-byte UTF-8 character, false otherwise.
+inline bool validate(uint8_t byte) noexcept {
+ // U+0000..U+007F
+ return byte <= 0x7Fu;
+}
+
+/// @brief Checks if the given bytes are a valid 2-byte UTF-8 character.
+/// @param[in] byte0 The first byte value.
+/// @param[in] byte1 The second byte value.
+/// @return true if the given bytes a valid 3-byte UTF-8 character, false otherwise.
+inline bool validate(uint8_t byte0, uint8_t byte1) noexcept {
+ // U+0080..U+07FF
+ // 1st Byte: 0xC2..0xDF
+ // 2nd Byte: 0x80..0xBF
+ if FK_YAML_LIKELY (0xC2u <= byte0 && byte0 <= 0xDFu) {
+ if FK_YAML_LIKELY (0x80u <= byte1 && byte1 <= 0xBFu) {
+ return true;
+ }
+ }
+
+ // The rest of byte combinations are invalid.
+ return false;
+}
+
+/// @brief Checks if the given bytes are a valid 3-byte UTF-8 character.
+/// @param[in] byte0 The first byte value.
+/// @param[in] byte1 The second byte value.
+/// @param[in] byte2 The third byte value.
+/// @return true if the given bytes a valid 2-byte UTF-8 character, false otherwise.
+inline bool validate(uint8_t byte0, uint8_t byte1, uint8_t byte2) noexcept {
+ // U+1000..U+CFFF:
+ // 1st Byte: 0xE0..0xEC
+ // 2nd Byte: 0x80..0xBF
+ // 3rd Byte: 0x80..0xBF
+ if (0xE0u <= byte0 && byte0 <= 0xECu) {
+ if FK_YAML_LIKELY (0x80u <= byte1 && byte1 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte2 && byte2 <= 0xBFu) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // U+D000..U+D7FF:
+ // 1st Byte: 0xED
+ // 2nd Byte: 0x80..0x9F
+ // 3rd Byte: 0x80..0xBF
+ if (byte0 == 0xEDu) {
+ if FK_YAML_LIKELY (0x80u <= byte1 && byte1 <= 0x9Fu) {
+ if FK_YAML_LIKELY (0x80u <= byte2 && byte2 <= 0xBFu) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // U+E000..U+FFFF:
+ // 1st Byte: 0xEE..0xEF
+ // 2nd Byte: 0x80..0xBF
+ // 3rd Byte: 0x80..0xBF
+ if FK_YAML_LIKELY (byte0 == 0xEEu || byte0 == 0xEFu) {
+ if FK_YAML_LIKELY (0x80u <= byte1 && byte1 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte2 && byte2 <= 0xBFu) {
+ return true;
+ }
+ }
+ }
+
+ // The rest of byte combinations are invalid.
+ return false;
+}
+
+/// @brief Checks if the given bytes are a valid 4-byte UTF-8 character.
+/// @param[in] byte0 The first byte value.
+/// @param[in] byte1 The second byte value.
+/// @param[in] byte2 The third byte value.
+/// @param[in] byte3 The fourth byte value.
+/// @return true if the given bytes a valid 4-byte UTF-8 character, false otherwise.
+inline bool validate(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3) noexcept {
+ // U+10000..U+3FFFF:
+ // 1st Byte: 0xF0
+ // 2nd Byte: 0x90..0xBF
+ // 3rd Byte: 0x80..0xBF
+ // 4th Byte: 0x80..0xBF
+ if (byte0 == 0xF0u) {
+ if FK_YAML_LIKELY (0x90u <= byte1 && byte1 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte2 && byte2 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte3 && byte3 <= 0xBFu) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // U+40000..U+FFFFF:
+ // 1st Byte: 0xF1..0xF3
+ // 2nd Byte: 0x80..0xBF
+ // 3rd Byte: 0x80..0xBF
+ // 4th Byte: 0x80..0xBF
+ if (0xF1u <= byte0 && byte0 <= 0xF3u) {
+ if FK_YAML_LIKELY (0x80u <= byte1 && byte1 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte2 && byte2 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte3 && byte3 <= 0xBFu) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // U+100000..U+10FFFF:
+ // 1st Byte: 0xF4
+ // 2nd Byte: 0x80..0x8F
+ // 3rd Byte: 0x80..0xBF
+ // 4th Byte: 0x80..0xBF
+ if FK_YAML_LIKELY (byte0 == 0xF4u) {
+ if FK_YAML_LIKELY (0x80u <= byte1 && byte1 <= 0x8Fu) {
+ if FK_YAML_LIKELY (0x80u <= byte2 && byte2 <= 0xBFu) {
+ if FK_YAML_LIKELY (0x80u <= byte3 && byte3 <= 0xBFu) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // The rest of byte combinations are invalid.
+ return false;
+}
+
+/// @brief Converts UTF-16 encoded characters to UTF-8 encoded bytes.
+/// @param[in] utf16 UTF-16 encoded character(s).
+/// @param[out] utf8 UTF-8 encoded bytes.
+/// @param[out] consumed_size The number of UTF-16 encoded characters used for the conversion.
+/// @param[out] encoded_size The size of UTF-encoded bytes.
+inline void from_utf16(
+ std::array<char16_t, 2> utf16, std::array<uint8_t, 4>& utf8, uint32_t& consumed_size, uint32_t& encoded_size) {
+ const auto first = utf16[0];
+ const auto second = utf16[1];
+ if (first < 0x80u) {
+ utf8[0] = static_cast<uint8_t>(first & 0x7Fu);
+ consumed_size = 1;
+ encoded_size = 1;
+ }
+ else if (first <= 0x7FFu) {
+ const auto utf8_chunk = static_cast<uint16_t>(0xC080u | ((first & 0x07C0u) << 2) | (first & 0x3Fu));
+ utf8[0] = static_cast<uint8_t>(utf8_chunk >> 8);
+ utf8[1] = static_cast<uint8_t>(utf8_chunk);
+ consumed_size = 1;
+ encoded_size = 2;
+ }
+ else if (first < 0xD800u || 0xE000u <= first) {
+ const auto utf8_chunk =
+ static_cast<uint32_t>(0xE08080u | ((first & 0xF000u) << 4) | ((first & 0x0FC0u) << 2) | (first & 0x3Fu));
+ utf8[0] = static_cast<uint8_t>(utf8_chunk >> 16);
+ utf8[1] = static_cast<uint8_t>(utf8_chunk >> 8);
+ utf8[2] = static_cast<uint8_t>(utf8_chunk);
+ consumed_size = 1;
+ encoded_size = 3;
+ }
+ else if (first <= 0xDBFFu && 0xDC00u <= second && second <= 0xDFFFu) {
+ // surrogate pair
+ const uint32_t code_point = 0x10000u + ((first & 0x03FFu) << 10) + (second & 0x03FFu);
+ const auto utf8_chunk = static_cast<uint32_t>(
+ 0xF0808080u | ((code_point & 0x1C0000u) << 6) | ((code_point & 0x03F000u) << 4) |
+ ((code_point & 0x0FC0u) << 2) | (code_point & 0x3Fu));
+ utf8[0] = static_cast<uint8_t>(utf8_chunk >> 24);
+ utf8[1] = static_cast<uint8_t>(utf8_chunk >> 16);
+ utf8[2] = static_cast<uint8_t>(utf8_chunk >> 8);
+ utf8[3] = static_cast<uint8_t>(utf8_chunk);
+ consumed_size = 2;
+ encoded_size = 4;
+ }
+ else {
+ throw invalid_encoding("Invalid UTF-16 encoding detected.", utf16);
+ }
+}
+
+/// @brief Converts a UTF-32 encoded character to UTF-8 encoded bytes.
+/// @param[in] utf32 A UTF-32 encoded character.
+/// @param[out] utf8 UTF-8 encoded bytes.
+/// @param[in] encoded_size The size of UTF-encoded bytes.
+inline void from_utf32(const char32_t utf32, std::array<uint8_t, 4>& utf8, uint32_t& encoded_size) {
+ if (utf32 < 0x80u) {
+ utf8[0] = static_cast<uint8_t>(utf32 & 0x007F);
+ encoded_size = 1;
+ }
+ else if (utf32 <= 0x7FFu) {
+ const auto utf8_chunk = static_cast<uint16_t>(0xC080u | ((utf32 & 0x07C0u) << 2) | (utf32 & 0x3Fu));
+ utf8[0] = static_cast<uint8_t>(utf8_chunk >> 8);
+ utf8[1] = static_cast<uint8_t>(utf8_chunk);
+ encoded_size = 2;
+ }
+ else if (utf32 <= 0xFFFFu) {
+ const auto utf8_chunk =
+ static_cast<uint32_t>(0xE08080u | ((utf32 & 0xF000u) << 4) | ((utf32 & 0x0FC0u) << 2) | (utf32 & 0x3F));
+ utf8[0] = static_cast<uint8_t>(utf8_chunk >> 16);
+ utf8[1] = static_cast<uint8_t>(utf8_chunk >> 8);
+ utf8[2] = static_cast<uint8_t>(utf8_chunk);
+ encoded_size = 3;
+ }
+ else if (utf32 <= 0x10FFFFu) {
+ const auto utf8_chunk = static_cast<uint32_t>(
+ 0xF0808080u | ((utf32 & 0x1C0000u) << 6) | ((utf32 & 0x03F000u) << 4) | ((utf32 & 0x0FC0u) << 2) |
+ (utf32 & 0x3Fu));
+ utf8[0] = static_cast<uint8_t>(utf8_chunk >> 24);
+ utf8[1] = static_cast<uint8_t>(utf8_chunk >> 16);
+ utf8[2] = static_cast<uint8_t>(utf8_chunk >> 8);
+ utf8[3] = static_cast<uint8_t>(utf8_chunk);
+ encoded_size = 4;
+ }
+ else {
+ throw invalid_encoding("Invalid UTF-32 encoding detected.", utf32);
+ }
+}
+
+} // namespace utf8
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODINGS_HPP */
+
+// #include <fkYAML/detail/input/block_scalar_header.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_BLOCK_SCALAR_HEADER_HPP
+#define FK_YAML_DETAIL_INPUT_BLOCK_SCALAR_HEADER_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Definition of chomping indicator types.
+enum class chomping_indicator_t : std::uint8_t {
+ STRIP, //!< excludes final line breaks and trailing empty lines indicated by `-`.
+ CLIP, //!< preserves final line breaks but excludes trailing empty lines. no indicator means this type.
+ KEEP, //!< preserves final line breaks and trailing empty lines indicated by `+`.
+};
+
+/// @brief Block scalar header information.
+struct block_scalar_header {
+ /// Chomping indicator type.
+ chomping_indicator_t chomp {chomping_indicator_t::CLIP};
+ /// Content indentation level of a block scalar.
+ uint32_t indent {0};
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_BLOCK_SCALAR_HEADER_HPP */
+
+// #include <fkYAML/detail/input/position_tracker.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP
+#define FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP
+
+#include <algorithm>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/str_view.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_STR_VIEW_HPP
+#define FK_YAML_DETAIL_STR_VIEW_HPP
+
+#include <limits>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Non owning view into constant character sequence.
+/// @note
+/// This class is a minimal implementation of std::basic_string_view which has been available since C++17
+/// but pretty useful and efficient for referencing/investigating character sequences.
+/// @warning
+/// This class intentionally omits a lot of value checks to improve efficiency. Necessary checks should be
+/// made before calling this class' APIs for safety.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type which defaults to std::char_traits<CharT>.
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+class basic_str_view {
+ static_assert(!std::is_array<CharT>::value, "CharT must not be an array type.");
+ static_assert(
+ std::is_trivial<CharT>::value && std::is_standard_layout<CharT>::value,
+ "CharT must be a trivial, standard layout type.");
+ static_assert(
+ std::is_same<CharT, typename Traits::char_type>::value, "CharT & Traits::char_type must be the same type.");
+
+public:
+ /// Character traits type.
+ using traits_type = Traits;
+ /// Character type.
+ using value_type = CharT;
+ /// Pointer type to a character.
+ using pointer = value_type*;
+ /// Constant pointer type to a character.
+ using const_pointer = const value_type*;
+ /// Reference type to a character.
+ using reference = value_type&;
+ /// Constant reference type to a character.
+ using const_reference = const value_type&;
+ /// Constant iterator type to a character.
+ using const_iterator = const value_type*;
+ /// Iterator type to a character.
+ /// (Always constant since this class isn't meant to provide any mutating features.)
+ using iterator = const_iterator;
+ /// Constant reverse iterator type to a character.
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ /// Reverse iterator type to a character.
+ /// (Always constant since this class isn't meant to provide any mutating features.)
+ using reverse_iterator = const_reverse_iterator;
+ /// Size type for character sequence sizes.
+ using size_type = std::size_t;
+ /// Difference type for distances between characters.
+ using difference_type = std::ptrdiff_t;
+
+ /// Invalid position value.
+ static constexpr size_type npos = static_cast<size_type>(-1);
+
+ /// Constructs a basic_str_view object.
+ constexpr basic_str_view() noexcept = default;
+
+ /// Destroys a basic_str_view object.
+ ~basic_str_view() noexcept = default;
+
+ /// @brief Copy constructs a basic_str_view object.
+ /// @param _ A basic_str_view object to copy from.
+ constexpr basic_str_view(const basic_str_view&) noexcept = default;
+
+ /// @brief Move constructs a basic_str_view object.
+ /// @param _ A basic_str_view object to move from.
+ constexpr basic_str_view(basic_str_view&&) noexcept = default;
+
+ /// @brief Constructs a basic_str_view object from a pointer to a character sequence.
+ /// @note std::char_traits::length() is constexpr from C++17.
+ /// @param p_str A pointer to a character sequence. (Must be null-terminated, or an undefined behavior.)
+ template <
+ typename CharPtrT,
+ enable_if_t<
+ conjunction<
+ negation<std::is_array<CharPtrT>>, std::is_pointer<CharPtrT>,
+ disjunction<std::is_same<CharPtrT, value_type*>, std::is_same<CharPtrT, const value_type*>>>::value,
+ int> = 0>
+ FK_YAML_CXX17_CONSTEXPR basic_str_view(CharPtrT p_str) noexcept
+ : m_len(traits_type::length(p_str)),
+ mp_str(p_str) {
+ }
+
+ /// @brief Constructs a basic_str_view object from a C-style char array.
+ /// @note
+ /// This constructor assumes the last element is the null character ('\0'). If that's not desirable, consider using
+ /// one of the other overloads.
+ /// @tparam N The size of a C-style char array.
+ /// @param str A C-style char array. (Must be null-terminated)
+ template <std::size_t N>
+ constexpr basic_str_view(const value_type (&str)[N]) noexcept
+ : m_len(N - 1),
+ mp_str(&str[0]) {
+ }
+
+ /// @brief Construction from a null pointer is forbidden.
+ basic_str_view(std::nullptr_t) = delete;
+
+ /// @brief Constructs a basic_str_view object from a pointer to a character sequence and its size.
+ /// @param p_str A pointer to a character sequence. (May or may not be null-terminated.)
+ /// @param len The length of a character sequence.
+ constexpr basic_str_view(const value_type* p_str, size_type len) noexcept
+ : m_len(len),
+ mp_str(p_str) {
+ }
+
+ /// @brief Constructs a basic_str_view object from compatible begin/end iterators
+ /// @tparam ItrType Iterator type to a character.
+ /// @param first The iterator to the first element of a character sequence.
+ /// @param last The iterator to the past-the-end of a character sequence.
+ template <
+ typename ItrType,
+ enable_if_t<
+ conjunction<
+ is_iterator_of<ItrType, CharT>,
+ std::is_base_of<
+ std::random_access_iterator_tag, typename std::iterator_traits<ItrType>::iterator_category>>::value,
+ int> = 0>
+ basic_str_view(ItrType first, ItrType last) noexcept
+ : m_len(last - first),
+ mp_str(&*first) {
+ }
+
+ /// @brief Constructs a basic_str_view object from a compatible std::basic_string object.
+ /// @param str A compatible character sequence container.
+ basic_str_view(const std::basic_string<CharT>& str) noexcept
+ : m_len(str.length()),
+ mp_str(str.data()) {
+ }
+
+ /// @brief Copy assignment operator for this basic_str_view class.
+ /// @param _ A basic_str_view object to copy from.
+ /// @return Reference to this basic_str_view object.
+ basic_str_view& operator=(const basic_str_view&) noexcept = default;
+
+ /// @brief Move assignment operator for this basic_str_view class.
+ /// @param _ A basic_str_view object to move from.
+ /// @return Reference to this basic_str_view object.
+ basic_str_view& operator=(basic_str_view&&) noexcept = default;
+
+ /// @brief Get the iterator to the first element. (Always constant)
+ /// @return The iterator to the first element.
+ const_iterator begin() const noexcept {
+ return mp_str;
+ }
+
+ /// @brief Get the iterator to the past-the-end element. (Always constant)
+ /// @return The iterator to the past-the-end element.
+ const_iterator end() const noexcept {
+ return mp_str + m_len;
+ }
+
+ /// @brief Get the iterator to the first element. (Always constant)
+ /// @return The iterator to the first element.
+ const_iterator cbegin() const noexcept {
+ return mp_str;
+ }
+
+ /// @brief Get the iterator to the past-the-end element. (Always constant)
+ /// @return The iterator to the past-the-end element.
+ const_iterator cend() const noexcept {
+ return mp_str + m_len;
+ }
+
+ /// @brief Get the iterator to the first element in the reverse order. (Always constant)
+ /// @return The iterator to the first element in the reverse order.
+ const_reverse_iterator rbegin() const noexcept {
+ return const_reverse_iterator(end());
+ }
+
+ /// @brief Get the iterator to the past-the-end element in the reverse order. (Always constant)
+ /// @return The iterator to the past-the-end element in the reverse order.
+ const_reverse_iterator rend() const noexcept {
+ return const_reverse_iterator(begin());
+ }
+
+ /// @brief Get the iterator to the first element in the reverse order. (Always constant)
+ /// @return The iterator to the first element in the reverse order.
+ const_reverse_iterator crbegin() const noexcept {
+ return const_reverse_iterator(end());
+ }
+
+ /// @brief Get the iterator to the past-the-end element in the reverse order. (Always constant)
+ /// @return The iterator to the past-the-end element in the reverse order.
+ const_reverse_iterator crend() const noexcept {
+ return const_reverse_iterator(begin());
+ }
+
+ /// @brief Get the size of the referenced character sequence.
+ /// @return The size of the referenced character sequence.
+ size_type size() const noexcept {
+ return m_len;
+ }
+
+ /// @brief Get the size of the referenced character sequence.
+ /// @return The size of the referenced character sequence.
+ size_type length() const noexcept {
+ return m_len;
+ }
+
+ /// @brief Get the maximum number of the character sequence size.
+ /// @return The maximum number of the character sequence size.
+ constexpr size_type max_size() const noexcept {
+ return static_cast<size_type>(std::numeric_limits<difference_type>::max());
+ }
+
+ /// @brief Checks if the referenced character sequence is empty.
+ /// @return true if empty, false otherwise.
+ bool empty() const noexcept {
+ return m_len == 0;
+ }
+
+ /// @brief Get the element at the given position.
+ /// @param pos The position of the target element.
+ /// @return The element at the given position.
+ const_reference operator[](size_type pos) const noexcept {
+ return *(mp_str + pos);
+ }
+
+ /// @brief Get the element at the given position with bounds checks.
+ /// @warning Throws an fkyaml::out_of_range exception if the position exceeds the character sequence size.
+ /// @param pos The position of the target element.
+ /// @return The element at the given position.
+ const_reference at(size_type pos) const {
+ if FK_YAML_UNLIKELY (pos >= m_len) {
+ throw fkyaml::out_of_range(static_cast<int>(pos));
+ }
+ return *(mp_str + pos);
+ }
+
+ /// @brief Get the first element.
+ /// @return The first element.
+ const_reference front() const noexcept {
+ return *mp_str;
+ }
+
+ /// @brief Get the last element.
+ /// @return The last element.
+ const_reference back() const {
+ return *(mp_str + m_len - 1);
+ }
+
+ /// @brief Get the pointer to the raw data of referenced character sequence.
+ /// @return The pointer to the raw data of referenced character sequence.
+ const_pointer data() const noexcept {
+ return mp_str;
+ }
+
+ /// @brief Moves the beginning position by `n` elements.
+ /// @param n The number of elements by which to move the beginning position.
+ void remove_prefix(size_type n) noexcept {
+ mp_str += n;
+ m_len -= n;
+ }
+
+ /// @brief Shrinks the referenced character sequence from the last by `n` elements.
+ /// @param n The number of elements by which to shrink the sequence from the last.
+ void remove_suffix(size_type n) noexcept {
+ m_len -= n;
+ }
+
+ /// @brief Swaps data with the given basic_str_view object.
+ /// @param other A basic_str_view object to swap data with.
+ void swap(basic_str_view& other) noexcept {
+ auto tmp = *this;
+ *this = other;
+ other = tmp;
+ }
+
+ /// @brief Copys the referenced character sequence values from `pos` by `n` size.
+ /// @warning Throws an fkyaml::out_of_range exception if the given `pos` is bigger than the length.
+ /// @param p_str The pointer to a character sequence buffer for output.
+ /// @param n The number of elements to write into `p_str`.
+ /// @param pos The offset of the beginning position to copy values.
+ /// @return The number of elements to be written into `p_str`.
+ size_type copy(CharT* p_str, size_type n, size_type pos = 0) const {
+ if FK_YAML_UNLIKELY (pos > m_len) {
+ throw fkyaml::out_of_range(static_cast<int>(pos));
+ }
+ const size_type rlen = std::min(n, m_len - pos);
+ traits_type::copy(p_str, mp_str + pos, rlen);
+ return rlen;
+ }
+
+ /// @brief Constructs a sub basic_str_view object from `pos` by `n` size.
+ /// @warning Throws an fkyaml::out_of_range exception if the given `pos` is bigger than the length.
+ /// @param pos The offset of the beginning position.
+ /// @param n The number of elements to the end of a new sub basic_str_view object.
+ /// @return A newly created sub basic_str_view object.
+ basic_str_view substr(size_type pos = 0, size_type n = npos) const {
+ if FK_YAML_UNLIKELY (pos > m_len) {
+ throw fkyaml::out_of_range(static_cast<int>(pos));
+ }
+ const size_type rlen = std::min(n, m_len - pos);
+ return basic_str_view(mp_str + pos, rlen);
+ }
+
+ /// @brief Compares the referenced character sequence values with the given basic_str_view object.
+ /// @param sv The basic_str_view object to compare with.
+ /// @return The lexicographical comparison result. The values are same as std::strncmp().
+ int compare(basic_str_view sv) const noexcept {
+ const size_type rlen = std::min(m_len, sv.m_len);
+ int ret = traits_type::compare(mp_str, sv.mp_str, rlen);
+
+ if (ret == 0) {
+ using int_limits = std::numeric_limits<int>;
+ const difference_type diff =
+ m_len > sv.m_len ? m_len - sv.m_len
+ : static_cast<difference_type>(-1) * static_cast<difference_type>(sv.m_len - m_len);
+
+ if (diff > int_limits::max()) {
+ ret = int_limits::max();
+ }
+ else if (diff < int_limits::min()) {
+ ret = int_limits::min();
+ }
+ else {
+ ret = static_cast<int>(diff);
+ }
+ }
+
+ return ret;
+ }
+
+ /// @brief Compares the referenced character sequence values from `pos1` by `n1` characters with `sv`.
+ /// @param pos1 The offset of the beginning element.
+ /// @param n1 The length of character sequence used for comparison.
+ /// @param sv A basic_str_view object to compare with.
+ /// @return The lexicographical comparison result. The values are same as std::strncmp().
+ int compare(size_type pos1, size_type n1, basic_str_view sv) const {
+ return substr(pos1, n1).compare(sv);
+ }
+
+ /// @brief Compares the referenced character sequence value from `pos1` by `n1` characters with `sv` from `pos2` by
+ /// `n2` characters.
+ /// @param pos1 The offset of the beginning element in this character sequence.
+ /// @param n1 The length of this character sequence used for comparison.
+ /// @param sv A basic_str_view object to compare with.
+ /// @param pos2 The offset of the beginning element in `sv`.
+ /// @param n2 The length of `sv` used for comparison.
+ /// @return The lexicographical comparison result. The values are same as std::strncmp().
+ int compare(size_type pos1, size_type n1, basic_str_view sv, size_type pos2, size_type n2) const {
+ return substr(pos1, n1).compare(sv.substr(pos2, n2));
+ }
+
+ /// @brief Compares the referenced character sequence with `s` character sequence.
+ /// @param s The pointer to a character sequence to compare with.
+ /// @return The lexicographical comparison result. The values are same as std::strncmp().
+ int compare(const CharT* s) const {
+ return compare(basic_str_view(s));
+ }
+
+ /// @brief Compares the referenced character sequence from `pos1` by `n1` characters with `s` character sequence.
+ /// @param pos1 The offset of the beginning element in this character sequence.
+ /// @param n1 The length of this character sequence used fo comparison.
+ /// @param s The pointer to a character sequence to compare with.
+ /// @return The lexicographical comparison result. The values are same as std::strncmp().
+ int compare(size_type pos1, size_type n1, const CharT* s) const {
+ return substr(pos1, n1).compare(basic_str_view(s));
+ }
+
+ /// @brief Compares the referenced character sequence from `pos1` by `n1` characters with `s` character sequence by
+ /// `n2` characters.
+ /// @param pos1 The offset of the beginning element in this character sequence.
+ /// @param n1 The length of this character sequence used fo comparison.
+ /// @param s The pointer to a character sequence to compare with.
+ /// @param n2 The length of `s` used fo comparison.
+ /// @return
+ int compare(size_type pos1, size_type n1, const CharT* s, size_type n2) const {
+ return substr(pos1, n1).compare(basic_str_view(s, n2));
+ }
+
+ /// @brief Checks if this character sequence starts with `sv` characters.
+ /// @param sv The character sequence to compare with.
+ /// @return true if the character sequence starts with `sv` characters, false otherwise.
+ bool starts_with(basic_str_view sv) const {
+ return substr(0, sv.size()) == sv;
+ }
+
+ /// @brief Checks if this character sequence starts with `c` character.
+ /// @param c The character to compare with.
+ /// @return true if the character sequence starts with `c` character, false otherwise.
+ bool starts_with(CharT c) const noexcept {
+ return !empty() && traits_type::eq(front(), c);
+ }
+
+ /// @brief Checks if this character sequence starts with `s` characters.
+ /// @param s The character sequence to compare with.
+ /// @return true if the character sequence starts with `s` characters, false otherwise.
+ bool starts_with(const CharT* s) const {
+ return starts_with(basic_str_view(s));
+ }
+
+ /// @brief Checks if this character sequence ends with `sv` characters.
+ /// @param sv The character sequence to compare with.
+ /// @return true if the character sequence ends with `sv` characters, false otherwise.
+ bool ends_with(basic_str_view sv) const noexcept {
+ const size_type size = m_len;
+ const size_type sv_size = sv.size();
+ return size >= sv_size && traits_type::compare(end() - sv_size, sv.data(), sv_size) == 0;
+ }
+
+ /// @brief Checks if this character sequence ends with `c` character.
+ /// @param c The character to compare with.
+ /// @return true if the character sequence ends with `c` character, false otherwise.
+ bool ends_with(CharT c) const noexcept {
+ return !empty() && traits_type::eq(back(), c);
+ }
+
+ /// @brief Checks if this character sequence ends with `s` characters.
+ /// @param s The character sequence to compare with.
+ /// @return true if the character sequence ends with `s` characters, false otherwise.
+ bool ends_with(const CharT* s) const noexcept {
+ return ends_with(basic_str_view(s));
+ }
+
+ /// @brief Checks if this character sequence contains `sv` characters.
+ /// @param sv The character sequence to compare with.
+ /// @return true if the character sequence contains `sv` characters, false otherwise.
+ bool contains(basic_str_view sv) const noexcept {
+ return find(sv) != npos;
+ }
+
+ /// @brief Checks if this character sequence contains `c` character.
+ /// @param c The character to compare with.
+ /// @return true if the character sequence contains `c` character, false otherwise.
+ bool contains(CharT c) const noexcept {
+ return find(c) != npos;
+ }
+
+ /// @brief Checks if this character sequence contains `s` characters.
+ /// @param s The character sequence to compare with.
+ /// @return true if the character sequence contains `s` characters, false otherwise.
+ bool contains(const CharT* s) const noexcept {
+ return find(s) != npos;
+ }
+
+ /// @brief Finds the beginning position of `sv` characters in this referenced character sequence.
+ /// @param sv The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `sv` characters, `npos` otherwise.
+ size_type find(basic_str_view sv, size_type pos = 0) const noexcept {
+ return find(sv.mp_str, pos, sv.m_len);
+ }
+
+ /// @brief Finds the beginning position of `c` character in this referenced character sequence.
+ /// @param sv The character to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `c` character, `npos` otherwise.
+ size_type find(CharT c, size_type pos = 0) const noexcept {
+ size_type ret = npos;
+
+ if FK_YAML_LIKELY (pos < m_len) {
+ const size_type n = m_len - pos;
+ const CharT* p_found = traits_type::find(mp_str + pos, n, c);
+ if (p_found) {
+ ret = p_found - mp_str;
+ }
+ }
+
+ return ret;
+ }
+
+ /// @brief Finds the beginning position of `s` character sequence by `n` characters in this referenced character
+ /// sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @param n The length of `s` character sequence used for comparison.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type find(const CharT* s, size_type pos, size_type n) const noexcept {
+ if FK_YAML_UNLIKELY (n == 0) {
+ return pos <= m_len ? pos : npos;
+ }
+
+ if FK_YAML_UNLIKELY (pos >= m_len) {
+ return npos;
+ }
+
+ CharT s0 = s[0];
+ const CharT* p_first = mp_str + pos;
+ const CharT* p_last = mp_str + m_len;
+ size_type len = m_len - pos;
+
+ while (len >= n) {
+ // find the first occurrence of s0
+ p_first = traits_type::find(p_first, len - n + 1, s0);
+ if (!p_first) {
+ return npos;
+ }
+
+ // compare the full strings from the first occurrence of s0
+ if (traits_type::compare(p_first, s, n) == 0) {
+ return p_first - mp_str;
+ }
+
+ len = p_last - (++p_first);
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the beginning position of `s` character sequence in this referenced character sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type find(const CharT* s, size_type pos = 0) const noexcept {
+ return find(basic_str_view(s), pos);
+ }
+
+ /// @brief Retrospectively finds the beginning position of `sv` characters in this referenced character sequence.
+ /// @param sv The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `sv` characters, `npos` otherwise.
+ size_type rfind(basic_str_view sv, size_type pos = npos) const noexcept {
+ return rfind(sv.mp_str, pos, sv.m_len);
+ }
+
+ /// @brief Retrospectively finds the beginning position of `c` character in this referenced character sequence.
+ /// @param sv The character to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `c` character, `npos` otherwise.
+ size_type rfind(CharT c, size_type pos = npos) const noexcept {
+ if FK_YAML_UNLIKELY (m_len == 0) {
+ return npos;
+ }
+
+ const size_type idx = std::min(m_len - 1, pos);
+
+ for (size_type i = 0; i <= idx; i++) {
+ if (traits_type::eq(mp_str[idx - i], c)) {
+ return idx - i;
+ }
+ }
+
+ return npos;
+ }
+
+ /// @brief Retrospectively finds the beginning position of `s` character sequence by `n` characters in this
+ /// referenced character sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @param n The length of `s` character sequence used for comparison.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type rfind(const CharT* s, size_type pos, size_type n) const noexcept {
+ if FK_YAML_LIKELY (n <= m_len) {
+ pos = std::min(m_len - n, pos) + 1;
+
+ do {
+ if (traits_type::compare(mp_str + --pos, s, n) == 0) {
+ return pos;
+ }
+ } while (pos > 0);
+ }
+
+ return npos;
+ }
+
+ /// @brief Retrospectively finds the beginning position of `s` character sequence in this referenced character
+ /// sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type rfind(const CharT* s, size_type pos = npos) const noexcept {
+ return rfind(basic_str_view(s), pos);
+ }
+
+ /// @brief Finds the first occurrence of `sv` character sequence in this referenced character sequence.
+ /// @param sv The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `sv` characters, `npos` otherwise.
+ size_type find_first_of(basic_str_view sv, size_type pos = 0) const noexcept {
+ return find_first_of(sv.mp_str, pos, sv.m_len);
+ }
+
+ /// @brief Finds the first occurrence of `c` character in this referenced character sequence.
+ /// @param c The character to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `c` character, `npos` otherwise.
+ size_type find_first_of(CharT c, size_type pos = 0) const noexcept {
+ return find(c, pos);
+ }
+
+ /// @brief Finds the first occurrence of `s` character sequence by `n` characters in this referenced character
+ /// sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @param n The length of `s` character sequence used for comparison.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type find_first_of(const CharT* s, size_type pos, size_type n) const noexcept {
+ if FK_YAML_UNLIKELY (n == 0) {
+ return npos;
+ }
+
+ for (size_type idx = pos; idx < m_len; ++idx) {
+ const CharT* p_found = traits_type::find(s, n, mp_str[idx]);
+ if (p_found) {
+ return idx;
+ }
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the first occurrence of `s` character sequence in this referenced character sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type find_first_of(const CharT* s, size_type pos = 0) const noexcept {
+ return find_first_of(basic_str_view(s), pos);
+ }
+
+ /// @brief Finds the last occurrence of `sv` character sequence in this referenced character sequence.
+ /// @param sv The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `sv` characters, `npos` otherwise.
+ size_type find_last_of(basic_str_view sv, size_type pos = npos) const noexcept {
+ return find_last_of(sv.mp_str, pos, sv.m_len);
+ }
+
+ /// @brief Finds the last occurrence of `c` character in this referenced character sequence.
+ /// @param c The character to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `c` character, `npos` otherwise.
+ size_type find_last_of(CharT c, size_type pos = npos) const noexcept {
+ return rfind(c, pos);
+ }
+
+ /// @brief Finds the last occurrence of `s` character sequence by `n` characters in this referenced character
+ /// sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @param n The length of `s` character sequence used for comparison.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type find_last_of(const CharT* s, size_type pos, size_type n) const noexcept {
+ if FK_YAML_LIKELY (n <= m_len) {
+ pos = std::min(m_len - n - 1, pos);
+
+ do {
+ const CharT* p_found = traits_type::find(s, n, mp_str[pos]);
+ if (p_found) {
+ return pos;
+ }
+ } while (pos-- != 0);
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the last occurrence of `s` character sequence in this referenced character sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of `s` characters, `npos` otherwise.
+ size_type find_last_of(const CharT* s, size_type pos = npos) const noexcept {
+ return find_last_of(basic_str_view(s), pos);
+ }
+
+ /// @brief Finds the first absence of `sv` character sequence in this referenced character sequence.
+ /// @param sv The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of non `sv` characters, `npos` otherwise.
+ size_type find_first_not_of(basic_str_view sv, size_type pos = 0) const noexcept {
+ return find_first_not_of(sv.mp_str, pos, sv.m_len);
+ }
+
+ /// @brief Finds the first absence of `c` character in this referenced character sequence.
+ /// @param c The character to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of non `c` character, `npos` otherwise.
+ size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept {
+ for (; pos < m_len; ++pos) {
+ if (!traits_type::eq(mp_str[pos], c)) {
+ return pos;
+ }
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the first absence of `s` character sequence by `n` characters in this referenced character
+ /// sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @param n The length of `s` character sequence used for comparison.
+ /// @return The beginning position of non `s` characters, `npos` otherwise.
+ size_type find_first_not_of(const CharT* s, size_type pos, size_type n) const noexcept {
+ for (; pos < m_len; ++pos) {
+ const CharT* p_found = traits_type::find(s, n, mp_str[pos]);
+ if (!p_found) {
+ return pos;
+ }
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the first absence of `s` character sequence in this referenced character sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of non `s` characters, `npos` otherwise.
+ size_type find_first_not_of(const CharT* s, size_type pos = 0) const noexcept {
+ return find_first_not_of(basic_str_view(s), pos);
+ }
+
+ /// @brief Finds the last absence of `sv` character sequence in this referenced character sequence.
+ /// @param sv The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of non `sv` characters, `npos` otherwise.
+ size_type find_last_not_of(basic_str_view sv, size_type pos = npos) const noexcept {
+ return find_last_not_of(sv.mp_str, pos, sv.m_len);
+ }
+
+ /// @brief Finds the last absence of `c` character in this referenced character sequence.
+ /// @param c The character to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of non `c` character, `npos` otherwise.
+ size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept {
+ if FK_YAML_LIKELY (m_len > 0) {
+ pos = std::min(m_len, pos);
+
+ do {
+ if (!traits_type::eq(mp_str[--pos], c)) {
+ return pos;
+ }
+ } while (pos > 0);
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the last absence of `s` character sequence by `n` characters in this referenced character
+ /// sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @param n The length of `s` character sequence used for comparison.
+ /// @return The beginning position of non `s` characters, `npos` otherwise.
+ size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const noexcept {
+ if FK_YAML_UNLIKELY (n <= m_len) {
+ pos = std::min(m_len - n, pos) + 1;
+
+ do {
+ const CharT* p_found = traits_type::find(s, n, mp_str[--pos]);
+ if (!p_found) {
+ return pos;
+ }
+ } while (pos > 0);
+ }
+
+ return npos;
+ }
+
+ /// @brief Finds the last absence of `s` character sequence in this referenced character sequence.
+ /// @param s The character sequence to compare with.
+ /// @param pos The offset of the search beginning position in this referenced character sequence.
+ /// @return The beginning position of non `s` characters, `npos` otherwise.
+ size_type find_last_not_of(const CharT* s, size_type pos = npos) const noexcept {
+ return find_last_not_of(basic_str_view(s), pos);
+ }
+
+private:
+ size_type m_len {0};
+ const value_type* mp_str {nullptr};
+};
+
+// Prior to C++17, a static constexpr class member needs an out-of-class definition.
+#ifndef FK_YAML_HAS_CXX_17
+
+template <typename CharT, typename Traits>
+constexpr typename basic_str_view<CharT, Traits>::size_type basic_str_view<CharT, Traits>::npos;
+
+#endif // !defined(FK_YAML_HAS_CXX_17)
+
+/// @brief An equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if the two objects are the same, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator==(basic_str_view<CharT, Traits> lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ // Comparing the lengths first will omit unnecessary value comparison in compare().
+ return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+/// @brief An equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_string object to compare with.
+/// @return true if the two objects are the same, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator==(basic_str_view<CharT, Traits> lhs, const std::basic_string<CharT, Traits>& rhs) noexcept {
+ return lhs == basic_str_view<CharT, Traits>(rhs);
+}
+
+/// @brief An equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_string object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if the two objects are the same, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator==(const std::basic_string<CharT, Traits>& lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return basic_str_view<CharT, Traits>(lhs) == rhs;
+}
+
+/// @brief An equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @tparam N The length of the character array.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A character array to compare with.
+/// @return true if the two objects are the same, false otherwise.
+template <typename CharT, typename Traits, std::size_t N>
+inline bool operator==(basic_str_view<CharT, Traits> lhs, const CharT (&rhs)[N]) noexcept {
+ // assume `rhs` is null terminated
+ return lhs == basic_str_view<CharT, Traits>(rhs);
+}
+
+/// @brief An equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @tparam N The length of the character array.
+/// @param rhs A character array for comparison.
+/// @param lhs A basic_str_view object to compare with.
+/// @return true if the two objects are the same, false otherwise.
+template <typename CharT, typename Traits, std::size_t N>
+inline bool operator==(const CharT (&lhs)[N], basic_str_view<CharT, Traits> rhs) noexcept {
+ // assume `lhs` is null terminated
+ return basic_str_view<CharT, Traits>(lhs) == rhs;
+}
+
+/// @brief An not-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if the two objects are different, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator!=(basic_str_view<CharT, Traits> lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+/// @brief An not-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_string object to compare with.
+/// @return true if the two objects are different, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator!=(basic_str_view<CharT, Traits> lhs, const std::basic_string<CharT, Traits>& rhs) noexcept {
+ return !(lhs == basic_str_view<CharT, Traits>(rhs));
+}
+
+/// @brief An not-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_string object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if the two objects are different, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator!=(const std::basic_string<CharT, Traits>& lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return !(basic_str_view<CharT, Traits>(lhs) == rhs);
+}
+
+/// @brief An not-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @tparam N The length of the character array.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A character array to compare with.
+/// @return true if the two objects are different, false otherwise.
+template <typename CharT, typename Traits, std::size_t N>
+inline bool operator!=(basic_str_view<CharT, Traits> lhs, const CharT (&rhs)[N]) noexcept {
+ // assume `rhs` is null terminated.
+ return !(lhs == basic_str_view<CharT, Traits>(rhs, N - 1));
+}
+
+/// @brief An not-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @tparam N The length of the character array.
+/// @param rhs A character array for comparison.
+/// @param lhs A basic_str_view object to compare with.
+/// @return true if the two objects are different, false otherwise.
+template <typename CharT, typename Traits, std::size_t N>
+inline bool operator!=(const CharT (&lhs)[N], basic_str_view<CharT, Traits> rhs) noexcept {
+ // assume `lhs` is null terminate
+ return !(basic_str_view<CharT, Traits>(lhs, N - 1) == rhs);
+}
+
+/// @brief An less-than operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if `lhs` is less than `rhs`, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator<(basic_str_view<CharT, Traits> lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return lhs.compare(rhs) < 0;
+}
+
+/// @brief An less-than-or-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if `lhs` is less than or equal to `rhs`, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator<=(basic_str_view<CharT, Traits> lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return lhs.compare(rhs) <= 0;
+}
+
+/// @brief An greater-than operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if `lhs` is greater than `rhs`, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator>(basic_str_view<CharT, Traits> lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return lhs.compare(rhs) > 0;
+}
+
+/// @brief An greater-than-or-equal-to operator of the basic_str_view class.
+/// @tparam CharT Character type
+/// @tparam Traits Character traits type.
+/// @param lhs A basic_str_view object for comparison.
+/// @param rhs A basic_str_view object to compare with.
+/// @return true if `lhs` is greater than or equal to `rhs`, false otherwise.
+template <typename CharT, typename Traits>
+inline bool operator>=(basic_str_view<CharT, Traits> lhs, basic_str_view<CharT, Traits> rhs) noexcept {
+ return lhs.compare(rhs) >= 0;
+}
+
+/// @brief Insertion operator of the basic_str_view class.
+/// @tparam CharT Character type.
+/// @tparam Traits Character traits type.
+/// @param os An output stream object.
+/// @param sv A basic_str_view object.
+/// @return Reference to the output stream object `os`.
+template <typename CharT, typename Traits>
+inline std::basic_ostream<CharT, Traits>& operator<<(
+ std::basic_ostream<CharT, Traits>& os, basic_str_view<CharT, Traits> sv) {
+ return os.write(sv.data(), static_cast<std::streamsize>(sv.size()));
+}
+
+/// @brief view into `char` sequence.
+using str_view = basic_str_view<char>;
+
+#if FK_YAML_HAS_CHAR8_T
+/// @brief view into `char8_t` sequence.
+using u8str_view = basic_str_view<char8_t>;
+#endif
+
+/// @brief view into `char16_t` sequence.
+using u16str_view = basic_str_view<char16_t>;
+
+/// @brief view into `char32_t` sequence.
+using u32str_view = basic_str_view<char32_t>;
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_STR_VIEW_HPP */
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A position tracker of the target buffer.
+class position_tracker {
+public:
+ void set_target_buffer(str_view buffer) noexcept {
+ m_begin = m_last = buffer.begin();
+ m_end = buffer.end();
+ }
+
+ /// @brief Update the set of the current position information.
+ /// @note This function doesn't support cases where cur_pos has moved backward from the last call.
+ /// @param cur_pos The iterator to the current element of the buffer.
+ void update_position(const char* p_current) {
+ const auto diff = static_cast<uint32_t>(p_current - m_last);
+ if (diff == 0) {
+ return;
+ }
+
+ m_cur_pos += diff;
+ const uint32_t prev_lines_read = m_lines_read;
+ m_lines_read += static_cast<uint32_t>(std::count(m_last, p_current, '\n'));
+ m_last = p_current;
+
+ if (prev_lines_read == m_lines_read) {
+ m_cur_pos_in_line += diff;
+ return;
+ }
+
+ uint32_t count = 0;
+ const char* p_begin = m_begin;
+ while (--p_current != p_begin) {
+ if (*p_current == '\n') {
+ break;
+ }
+ count++;
+ }
+ m_cur_pos_in_line = count;
+ }
+
+ uint32_t get_cur_pos() const noexcept {
+ return m_cur_pos;
+ }
+
+ /// @brief Get the current position in the current line.
+ /// @return uint32_t The current position in the current line.
+ uint32_t get_cur_pos_in_line() const noexcept {
+ return m_cur_pos_in_line;
+ }
+
+ /// @brief Get the number of lines which have already been read.
+ /// @return uint32_t The number of lines which have already been read.
+ uint32_t get_lines_read() const noexcept {
+ return m_lines_read;
+ }
+
+private:
+ /// The iterator to the beginning element in the target buffer.
+ const char* m_begin {};
+ /// The iterator to the past-the-end element in the target buffer.
+ const char* m_end {};
+ /// The iterator to the last updated element in the target buffer.
+ const char* m_last {};
+ /// The current position from the beginning of an input buffer.
+ uint32_t m_cur_pos {0};
+ /// The current position in the current line.
+ uint32_t m_cur_pos_in_line {0};
+ /// The number of lines which have already been read.
+ uint32_t m_lines_read {0};
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_POSITION_TRACKER_HPP */
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/str_view.hpp>
+
+// #include <fkYAML/detail/types/lexical_token_t.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP
+#define FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Definition of lexical token types.
+enum class lexical_token_t : std::uint8_t {
+ END_OF_BUFFER, //!< the end of input buffer.
+ EXPLICIT_KEY_PREFIX, //!< the character for explicit mapping key prefix `?`.
+ KEY_SEPARATOR, //!< the key separator `:`
+ VALUE_SEPARATOR, //!< the value separator `,`
+ ANCHOR_PREFIX, //!< the character for anchor prefix `&`
+ ALIAS_PREFIX, //!< the character for alias prefix `*`
+ YAML_VER_DIRECTIVE, //!< a YAML version directive found. use get_yaml_version() to get a value.
+ TAG_DIRECTIVE, //!< a TAG directive found. use GetTagInfo() to get the tag information.
+ TAG_PREFIX, //!< the character for tag prefix `!`
+ INVALID_DIRECTIVE, //!< an invalid directive found. do not try to get the value.
+ SEQUENCE_BLOCK_PREFIX, //!< the character for sequence block prefix `- `
+ SEQUENCE_FLOW_BEGIN, //!< the character for sequence flow begin `[`
+ SEQUENCE_FLOW_END, //!< the character for sequence flow end `]`
+ MAPPING_FLOW_BEGIN, //!< the character for mapping begin `{`
+ MAPPING_FLOW_END, //!< the character for mapping end `}`
+ PLAIN_SCALAR, //!< plain (unquoted) scalars
+ SINGLE_QUOTED_SCALAR, //!< single-quoted scalars
+ DOUBLE_QUOTED_SCALAR, //!< double-quoted scalars
+ BLOCK_LITERAL_SCALAR, //!< block literal style scalars
+ BLOCK_FOLDED_SCALAR, //!< block folded style scalars
+ END_OF_DIRECTIVES, //!< the end of declaration of directives specified by `---`.
+ END_OF_DOCUMENT, //!< the end of a YAML document specified by `...`.
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_TYPES_LEXICAL_TOKEN_T_HPP */
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Lexical token information
+struct lexical_token {
+ lexical_token() = default;
+
+ lexical_token(lexical_token_t t, str_view s) noexcept
+ : type(t),
+ str(s) {
+ }
+
+ lexical_token(lexical_token_t t) noexcept
+ : type(t) {
+ }
+
+ lexical_token(const lexical_token&) = default;
+ lexical_token& operator=(const lexical_token&) = default;
+ lexical_token(lexical_token&&) = default;
+ lexical_token& operator=(lexical_token&&) = default;
+ ~lexical_token() = default;
+
+ /// Lexical token type.
+ lexical_token_t type {lexical_token_t::END_OF_BUFFER};
+ /// Lexical token contents.
+ str_view str;
+};
+
+/// @brief A class which lexically analyzes YAML formatted inputs.
+class lexical_analyzer {
+ // whether the current context is flow(1) or block(0)
+ static constexpr uint32_t flow_context_bit = 1u << 0u;
+ // whether the current document part is directive(1) or content(0)
+ static constexpr uint32_t document_directive_bit = 1u << 1u;
+
+public:
+ /// @brief Construct a new lexical_analyzer object.
+ /// @param input_buffer An input buffer.
+ explicit lexical_analyzer(str_view input_buffer) noexcept
+ : m_begin_itr(input_buffer.begin()),
+ m_cur_itr(input_buffer.begin()),
+ m_end_itr(input_buffer.end()) {
+ m_pos_tracker.set_target_buffer(input_buffer);
+ }
+
+ /// @brief Get the next lexical token by scanning the left of the input buffer.
+ /// @return lexical_token The next lexical token.
+ lexical_token get_next_token() {
+ skip_white_spaces_and_newline_codes();
+
+ m_token_begin_itr = m_cur_itr;
+ m_pos_tracker.update_position(m_cur_itr);
+ m_last_token_begin_pos = m_pos_tracker.get_cur_pos_in_line();
+ m_last_token_begin_line = m_pos_tracker.get_lines_read();
+
+ if (m_cur_itr == m_end_itr) {
+ return {lexical_token_t::END_OF_BUFFER};
+ }
+
+ switch (*m_cur_itr) {
+ case '?':
+ if (++m_cur_itr == m_end_itr) {
+ return {lexical_token_t::PLAIN_SCALAR, {m_token_begin_itr, 1}};
+ }
+
+ if (*m_cur_itr == ' ') {
+ return {lexical_token_t::EXPLICIT_KEY_PREFIX};
+ }
+ break;
+ case ':': // key separator
+ if (++m_cur_itr == m_end_itr) {
+ return {lexical_token_t::KEY_SEPARATOR};
+ }
+
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\t':
+ case '\n':
+ return {lexical_token_t::KEY_SEPARATOR};
+ default:
+ if ((m_state & flow_context_bit) == 0) {
+ // in a block context
+ break;
+ }
+
+ switch (*m_cur_itr) {
+ case ',':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ // Flow indicators are not "safe" to be followed in a flow context.
+ // See https://yaml.org/spec/1.2.2/#733-plain-style for more details.
+ return {lexical_token_t::KEY_SEPARATOR};
+ default:
+ // At least '{' or '[' must precedes this token.
+ FK_YAML_ASSERT(m_token_begin_itr != m_begin_itr);
+
+ // if a key inside a flow mapping is JSON-like (surrounded by indicators, see below), YAML allows
+ // the following value to be specified adjacent to the ":" mapping value indicator.
+ // ```yaml
+ // # the following flow mapping entries are all valid.
+ // {
+ // "foo":true,
+ // 'bar':false, # 'bar' is actually not JSON but allowed in YAML
+ // # since its surrounded by the single quotes.
+ // {[1,2,3]:null}:"baz"
+ // }
+ // ```
+ switch (*(m_token_begin_itr - 1)) {
+ case '\'':
+ case '\"':
+ case ']':
+ case '}':
+ return {lexical_token_t::KEY_SEPARATOR};
+ default:
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case ',': // value separator
+ ++m_cur_itr;
+ return {lexical_token_t::VALUE_SEPARATOR};
+ case '&': // anchor prefix
+ return {lexical_token_t::ANCHOR_PREFIX, extract_anchor_name()};
+ case '*': // alias prefix
+ return {lexical_token_t::ALIAS_PREFIX, extract_anchor_name()};
+ case '!': // tag prefix
+ return {lexical_token_t::TAG_PREFIX, extract_tag_name()};
+ case '#': // comment prefix
+ scan_comment();
+ return get_next_token();
+ case '%': // directive prefix
+ if (m_state & document_directive_bit) {
+ return {scan_directive()};
+ }
+ // The '%' character can be safely used as the first character in document contents.
+ // See https://yaml.org/spec/1.2.2/#912-document-markers for more details.
+ break;
+ case '-': {
+ switch (*(m_cur_itr + 1)) {
+ case ' ':
+ case '\t':
+ case '\n':
+ // Move a cursor to the beginning of the next token.
+ m_cur_itr += 2;
+ return {lexical_token_t::SEQUENCE_BLOCK_PREFIX};
+ default:
+ break;
+ }
+
+ if (m_pos_tracker.get_cur_pos_in_line() == 0) {
+ if ((m_end_itr - m_cur_itr) > 2) {
+ const bool is_dir_end = std::equal(m_token_begin_itr, m_cur_itr + 3, "---");
+ if (is_dir_end) {
+ m_cur_itr += 3;
+ return {lexical_token_t::END_OF_DIRECTIVES};
+ }
+ }
+ }
+
+ break;
+ }
+ case '[': // sequence flow begin
+ ++m_cur_itr;
+ return {lexical_token_t::SEQUENCE_FLOW_BEGIN};
+ case ']': // sequence flow end
+ ++m_cur_itr;
+ return {lexical_token_t::SEQUENCE_FLOW_END};
+ case '{': // mapping flow begin
+ ++m_cur_itr;
+ return {lexical_token_t::MAPPING_FLOW_BEGIN};
+ case '}': // mapping flow end
+ ++m_cur_itr;
+ return {lexical_token_t::MAPPING_FLOW_END};
+ case '@':
+ emit_error("Any token cannot start with at(@). It is a reserved indicator for YAML.");
+ case '`':
+ emit_error("Any token cannot start with grave accent(`). It is a reserved indicator for YAML.");
+ case '\"':
+ ++m_token_begin_itr;
+ return {lexical_token_t::DOUBLE_QUOTED_SCALAR, determine_double_quoted_scalar_range()};
+ case '\'':
+ ++m_token_begin_itr;
+ return {lexical_token_t::SINGLE_QUOTED_SCALAR, determine_single_quoted_scalar_range()};
+ case '.': {
+ if (m_pos_tracker.get_cur_pos_in_line() == 0) {
+ const auto rem_size = m_end_itr - m_cur_itr;
+ if FK_YAML_LIKELY (rem_size > 2) {
+ const bool is_doc_end = std::equal(m_cur_itr, m_cur_itr + 3, "...");
+ if (is_doc_end) {
+ if (rem_size > 3) {
+ switch (*(m_cur_itr + 3)) {
+ case ' ':
+ case '\t':
+ case '\n':
+ m_cur_itr += 4;
+ break;
+ default:
+ // See https://yaml.org/spec/1.2.2/#912-document-markers for more details.
+ emit_error("The document end marker \"...\" must not be followed by non-ws char.");
+ }
+ }
+ else {
+ m_cur_itr += 3;
+ }
+ return {lexical_token_t::END_OF_DOCUMENT};
+ }
+ }
+ }
+ break;
+ }
+ case '|':
+ case '>': {
+ const str_view sv {m_token_begin_itr, m_end_itr};
+ const std::size_t header_end_pos = sv.find('\n');
+ FK_YAML_ASSERT(header_end_pos != str_view::npos);
+ const uint32_t base_indent = get_current_indent_level(&sv[header_end_pos]);
+
+ const lexical_token_t type = *m_token_begin_itr == '|' ? lexical_token_t::BLOCK_LITERAL_SCALAR
+ : lexical_token_t::BLOCK_FOLDED_SCALAR;
+ const str_view header_line = sv.substr(1, header_end_pos - 1);
+ m_block_scalar_header = convert_to_block_scalar_header(header_line);
+
+ m_token_begin_itr = sv.begin() + (header_end_pos + 1);
+
+ return {
+ type,
+ determine_block_scalar_content_range(
+ base_indent, m_block_scalar_header.indent, m_block_scalar_header.indent)};
+ }
+ default:
+ break;
+ }
+
+ return {lexical_token_t::PLAIN_SCALAR, determine_plain_scalar_range()};
+ }
+
+ /// @brief Get the beginning position of a last token.
+ /// @return uint32_t The beginning position of a last token.
+ uint32_t get_last_token_begin_pos() const noexcept {
+ return m_last_token_begin_pos;
+ }
+
+ /// @brief Get the number of lines already processed.
+ /// @return uint32_t The number of lines already processed.
+ uint32_t get_lines_processed() const noexcept {
+ return m_last_token_begin_line;
+ }
+
+ /// @brief Get the YAML version specification.
+ /// @return str_view A YAML version specification.
+ str_view get_yaml_version() const noexcept {
+ return m_yaml_version;
+ }
+
+ /// @brief Get the YAML tag handle defined in the TAG directive.
+ /// @return str_view A tag handle.
+ str_view get_tag_handle() const noexcept {
+ return m_tag_handle;
+ }
+
+ /// @brief Get the YAML tag prefix defined in the TAG directive.
+ /// @return str_view A tag prefix.
+ str_view get_tag_prefix() const noexcept {
+ return m_tag_prefix;
+ }
+
+ /// @brief Get block scalar header information.
+ /// @return block_scalar_header Block scalar header information.
+ block_scalar_header get_block_scalar_header() const noexcept {
+ return m_block_scalar_header;
+ }
+
+ /// @brief Toggles the context state between flow and block.
+ /// @param is_flow_context true: flow context, false: block context
+ void set_context_state(bool is_flow_context) noexcept {
+ m_state &= ~flow_context_bit;
+ if (is_flow_context) {
+ m_state |= flow_context_bit;
+ }
+ }
+
+ /// @brief Toggles the document state between directive and content.
+ /// @param is_directive true: directive, false: content
+ void set_document_state(bool is_directive) noexcept {
+ m_state &= ~document_directive_bit;
+ if (is_directive) {
+ m_state |= document_directive_bit;
+ }
+ }
+
+private:
+ uint32_t get_current_indent_level(const char* p_line_end) {
+ // get the beginning position of the current line.
+ std::size_t line_begin_pos = str_view(m_begin_itr, p_line_end - 1).find_last_of('\n');
+ if (line_begin_pos == str_view::npos) {
+ line_begin_pos = 0;
+ }
+ else {
+ ++line_begin_pos;
+ }
+ const char* p_line_begin = m_begin_itr + line_begin_pos;
+ const char* cur_itr = p_line_begin;
+
+ // get the indentation of the current line.
+ uint32_t indent = 0;
+ bool indent_found = false;
+ // 0: none, 1: block seq item, 2: explicit map key, 3: explicit map value
+ uint32_t context = 0;
+ while (cur_itr != p_line_end && !indent_found) {
+ switch (*cur_itr) {
+ case ' ':
+ ++indent;
+ ++cur_itr;
+ break;
+ case '-':
+ switch (*(cur_itr + 1)) {
+ case ' ':
+ case '\t':
+ indent += 2;
+ cur_itr += 2;
+ context = 1;
+ break;
+ default:
+ indent_found = true;
+ break;
+ }
+ break;
+ case '?':
+ if (*(cur_itr + 1) == ' ') {
+ indent += 2;
+ cur_itr += 2;
+ context = 2;
+ break;
+ }
+
+ indent_found = true;
+ break;
+ case ':':
+ switch (*(cur_itr + 1)) {
+ case ' ':
+ case '\t':
+ indent += 2;
+ cur_itr += 2;
+ context = 3;
+ break;
+ default:
+ indent_found = true;
+ break;
+ }
+ break;
+ default:
+ indent_found = true;
+ break;
+ }
+ }
+
+ // If "- ", "? " and/or ": " occur in the first line of this plain scalar content.
+ if (context > 0) {
+ // Check if the first line contains the key separator ": ".
+ // If so, the indent value remains the current one.
+ // Otherwise, the indent value is changed based on the last ocurrence of the above 3.
+ // In any case, multiline plain scalar content must be indented more than the indent value.
+ const str_view line_content_part {p_line_begin + indent, p_line_end};
+ std::size_t key_sep_pos = line_content_part.find(": ");
+ if (key_sep_pos == str_view::npos) {
+ key_sep_pos = line_content_part.find(":\t");
+ }
+
+ if (key_sep_pos == str_view::npos) {
+ constexpr char targets[] = "-?:";
+ FK_YAML_ASSERT(context - 1 < sizeof(targets));
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
+ const char target_char = targets[context - 1];
+
+ // Find the position of the last ocuurence of "- ", "? " or ": ".
+ const str_view line_indent_part {p_line_begin, indent};
+ const std::size_t block_seq_item_begin_pos = line_indent_part.find_last_of(target_char);
+ FK_YAML_ASSERT(block_seq_item_begin_pos != str_view::npos);
+ indent = static_cast<uint32_t>(block_seq_item_begin_pos);
+ }
+ }
+
+ return indent;
+ }
+
+ /// @brief Skip until a newline code or a null character is found.
+ void scan_comment() {
+ FK_YAML_ASSERT(*m_cur_itr == '#');
+ if FK_YAML_LIKELY (m_cur_itr != m_begin_itr) {
+ switch (*(m_cur_itr - 1)) {
+ case ' ':
+ case '\t':
+ case '\n':
+ break;
+ default:
+ emit_error("Comment must not begin right after non-break characters");
+ }
+ }
+ skip_until_line_end();
+ }
+
+ /// @brief Scan directives starting with the prefix '%'
+ /// @note Currently, only %YAML directive is supported. If not, returns invalid or throws an exception.
+ /// @return lexical_token_t The lexical token type for directives.
+ lexical_token_t scan_directive() {
+ FK_YAML_ASSERT(*m_cur_itr == '%');
+
+ m_token_begin_itr = ++m_cur_itr;
+
+ bool ends_loop = false;
+ while (!ends_loop && m_cur_itr != m_end_itr) {
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\t':
+ ends_loop = true;
+ break;
+ case '\n':
+ skip_until_line_end();
+ return lexical_token_t::INVALID_DIRECTIVE;
+ default:
+ ++m_cur_itr;
+ break;
+ }
+ }
+
+ const str_view dir_name(m_token_begin_itr, m_cur_itr);
+
+ if (dir_name == "TAG") {
+ if FK_YAML_UNLIKELY (!ends_loop) {
+ emit_error("There must be at least one white space between \"%TAG\" and tag info.");
+ }
+ skip_white_spaces();
+ return scan_tag_directive();
+ }
+
+ if (dir_name == "YAML") {
+ if FK_YAML_UNLIKELY (!ends_loop) {
+ emit_error("There must be at least one white space between \"%YAML\" and version.");
+ }
+ skip_white_spaces();
+ return scan_yaml_version_directive();
+ }
+
+ skip_until_line_end();
+ return lexical_token_t::INVALID_DIRECTIVE;
+ }
+
+ /// @brief Scan a YAML tag directive.
+ /// @return lexical_token_t The lexical token type for YAML tag directives.
+ lexical_token_t scan_tag_directive() {
+ m_token_begin_itr = m_cur_itr;
+
+ //
+ // extract a tag handle
+ //
+
+ if FK_YAML_UNLIKELY (*m_cur_itr != '!') {
+ emit_error("Tag handle must start with \'!\'.");
+ }
+
+ if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) {
+ emit_error("invalid TAG directive is found.");
+ }
+
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\t':
+ // primary handle (!)
+ break;
+ case '!':
+ if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) {
+ emit_error("invalid TAG directive is found.");
+ }
+ if FK_YAML_UNLIKELY (*m_cur_itr != ' ' && *m_cur_itr != '\t') {
+ emit_error("invalid tag handle is found.");
+ }
+ break;
+ default: {
+ bool ends_loop = false;
+ do {
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\t':
+ emit_error("invalid tag handle is found.");
+ case '!': {
+ if (m_cur_itr + 1 == m_end_itr) {
+ ends_loop = true;
+ break;
+ }
+ const char next = *(m_cur_itr + 1);
+ if FK_YAML_UNLIKELY (next != ' ' && next != '\t') {
+ emit_error("invalid tag handle is found.");
+ }
+ ends_loop = true;
+ break;
+ }
+ case '-':
+ break;
+ default:
+ if FK_YAML_UNLIKELY (!isalnum(*m_cur_itr)) {
+ // See https://yaml.org/spec/1.2.2/#rule-c-named-tag-handle for more details.
+ emit_error("named handle can contain only numbers(0-9), alphabets(A-Z,a-z) and hyphens(-).");
+ }
+ break;
+ }
+
+ if FK_YAML_UNLIKELY (++m_cur_itr == m_end_itr) {
+ emit_error("invalid TAG directive is found.");
+ }
+ } while (!ends_loop);
+ break;
+ }
+ }
+
+ m_tag_handle = str_view {m_token_begin_itr, m_cur_itr};
+
+ skip_white_spaces();
+
+ //
+ // extract a tag prefix.
+ //
+
+ m_token_begin_itr = m_cur_itr;
+ const char* p_tag_prefix_begin = m_cur_itr;
+ switch (*m_cur_itr) {
+ // a tag prefix must not start with flow indicators to avoid ambiguity.
+ // See https://yaml.org/spec/1.2.2/#rule-ns-global-tag-prefix for more details.
+ case ',':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ emit_error("tag prefix must not start with flow indicators (\',\', [], {}).");
+ default:
+ break;
+ }
+
+ // extract the rest of a tag prefix.
+ bool ends_loop = false;
+ do {
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\t':
+ case '\n':
+ ends_loop = true;
+ break;
+ default:
+ break;
+ }
+ } while (!ends_loop && ++m_cur_itr != m_end_itr);
+
+ const bool is_valid = uri_encoding::validate(p_tag_prefix_begin, m_cur_itr);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ emit_error("invalid URI character is found in a tag prefix.");
+ }
+
+ m_tag_prefix = str_view {p_tag_prefix_begin, m_cur_itr};
+
+ return lexical_token_t::TAG_DIRECTIVE;
+ }
+
+ /// @brief Scan a YAML version directive.
+ /// @note Only 1.1 and 1.2 are supported. If not, throws an exception.
+ /// @return lexical_token_t The lexical token type for YAML version directives.
+ lexical_token_t scan_yaml_version_directive() {
+ m_token_begin_itr = m_cur_itr;
+
+ bool ends_loop = false;
+ while (!ends_loop && m_cur_itr != m_end_itr) {
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\t':
+ case '\n':
+ ends_loop = true;
+ break;
+ default:
+ ++m_cur_itr;
+ break;
+ }
+ }
+
+ m_yaml_version = str_view {m_token_begin_itr, m_cur_itr};
+
+ if FK_YAML_UNLIKELY (m_yaml_version.compare("1.1") != 0 && m_yaml_version.compare("1.2") != 0) {
+ emit_error("Only 1.1 and 1.2 can be specified as the YAML version.");
+ }
+
+ return lexical_token_t::YAML_VER_DIRECTIVE;
+ }
+
+ /// @brief Extracts an anchor name from the input.
+ /// @return The extracted anchor name.
+ str_view extract_anchor_name() {
+ FK_YAML_ASSERT(*m_cur_itr == '&' || *m_cur_itr == '*');
+
+ m_token_begin_itr = ++m_cur_itr;
+
+ bool ends_loop = false;
+ for (; m_cur_itr != m_end_itr; ++m_cur_itr) {
+ switch (*m_cur_itr) {
+ // anchor name must not contain white spaces, newline codes and flow indicators.
+ // See https://yaml.org/spec/1.2.2/#692-node-anchors for more details.
+ case ' ':
+ case '\t':
+ case '\n':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ',':
+ ends_loop = true;
+ break;
+ default:
+ break;
+ }
+
+ if (ends_loop) {
+ break;
+ }
+ }
+
+ if FK_YAML_UNLIKELY (m_token_begin_itr == m_cur_itr) {
+ emit_error("anchor name must not be empty.");
+ }
+
+ return {m_token_begin_itr, m_cur_itr};
+ }
+
+ /// @brief Extracts a tag name from the input.
+ /// @return A tag name.
+ str_view extract_tag_name() {
+ FK_YAML_ASSERT(*m_cur_itr == '!');
+
+ if (++m_cur_itr == m_end_itr) {
+ // Just "!" is a non-specific tag.
+ return {m_token_begin_itr, m_end_itr};
+ }
+
+ bool is_verbatim = false;
+ bool allows_another_tag_prefix = false;
+
+ switch (*m_cur_itr) {
+ case ' ':
+ case '\n':
+ // Just "!" is a non-specific tag.
+ return {m_token_begin_itr, m_cur_itr};
+ case '!':
+ // Secondary tag handles (!!suffix)
+ break;
+ case '<':
+ // Verbatim tags (!<TAG>)
+ is_verbatim = true;
+ ++m_cur_itr;
+ break;
+ default:
+ // Either local tags (!suffix) or named handles (!tag!suffix)
+ allows_another_tag_prefix = true;
+ break;
+ }
+
+ bool is_named_handle = false;
+ bool ends_loop = false;
+ do {
+ if (++m_cur_itr == m_end_itr) {
+ break;
+ }
+
+ switch (*m_cur_itr) {
+ // Tag names must not contain spaces or newline codes.
+ case ' ':
+ case '\t':
+ case '\n':
+ ends_loop = true;
+ break;
+ case '!':
+ if FK_YAML_UNLIKELY (!allows_another_tag_prefix) {
+ emit_error("invalid tag prefix (!) is found.");
+ }
+
+ is_named_handle = true;
+ // tag prefix must not appear three times.
+ allows_another_tag_prefix = false;
+ break;
+ default:
+ break;
+ }
+ } while (!ends_loop);
+
+ str_view tag_name {m_token_begin_itr, m_cur_itr};
+
+ if (is_verbatim) {
+ const char last = tag_name.back();
+ if FK_YAML_UNLIKELY (last != '>') {
+ emit_error("verbatim tag (!<TAG>) must be ended with \'>\'.");
+ }
+
+ // only the `TAG` part of the `!<TAG>` for URI validation.
+ const str_view tag_body = tag_name.substr(2, tag_name.size() - 3);
+ if FK_YAML_UNLIKELY (tag_body.empty()) {
+ emit_error("verbatim tag(!<TAG>) must not be empty.");
+ }
+
+ const bool is_valid_uri = uri_encoding::validate(tag_body.begin(), tag_body.end());
+ if FK_YAML_UNLIKELY (!is_valid_uri) {
+ emit_error("invalid URI character is found in a verbatim tag.");
+ }
+
+ return tag_name;
+ }
+
+ if (is_named_handle) {
+ const char last = tag_name.back();
+ if FK_YAML_UNLIKELY (last == '!') {
+ // Tag shorthand must be followed by a non-empty suffix.
+ // See the "Tag Shorthands" section in https://yaml.org/spec/1.2.2/#691-node-tags.
+ emit_error("named handle has no suffix.");
+ }
+ }
+
+ // get the position of last tag prefix character (!) to extract body of tag shorthands.
+ // tag shorthand is either primary(!tag), secondary(!!tag) or named(!handle!tag).
+ const std::size_t last_tag_prefix_pos = tag_name.find_last_of('!');
+ FK_YAML_ASSERT(last_tag_prefix_pos != str_view::npos);
+
+ const str_view tag_uri = tag_name.substr(last_tag_prefix_pos + 1);
+ const bool is_valid_uri = uri_encoding::validate(tag_uri.begin(), tag_uri.end());
+ if FK_YAML_UNLIKELY (!is_valid_uri) {
+ emit_error("Invalid URI character is found in a named tag handle.");
+ }
+
+ // Tag shorthands cannot contain flow indicators({}[],).
+ // See the "Tag Shorthands" section in https://yaml.org/spec/1.2.2/#691-node-tags.
+ const std::size_t invalid_char_pos = tag_uri.find_first_of("{}[],");
+ if (invalid_char_pos != str_view::npos) {
+ emit_error("Tag shorthand cannot contain flow indicators({}[],).");
+ }
+
+ return tag_name;
+ }
+
+ /// @brief Determines the range of single quoted scalar by scanning remaining input buffer contents.
+ /// @return A single quoted scalar.
+ str_view determine_single_quoted_scalar_range() {
+ const str_view sv {m_token_begin_itr, m_end_itr};
+
+ std::size_t pos = sv.find('\'');
+ while (pos != str_view::npos) {
+ FK_YAML_ASSERT(pos < sv.size());
+ if FK_YAML_LIKELY (pos == sv.size() - 1 || sv[pos + 1] != '\'') {
+ // closing single quote is found.
+ m_cur_itr = m_token_begin_itr + (pos + 1);
+ str_view single_quoted_scalar {m_token_begin_itr, pos};
+ check_scalar_content(single_quoted_scalar);
+ return single_quoted_scalar;
+ }
+
+ // If single quotation marks are repeated twice in a single quoted scalar, they are considered as an
+ // escaped single quotation mark. Skip the second one which would otherwise be detected as a closing
+ // single quotation mark in the next loop.
+ pos = sv.find('\'', pos + 2);
+ }
+
+ m_cur_itr = m_end_itr; // update for error information
+ emit_error("Invalid end of input buffer in a single-quoted scalar token.");
+ }
+
+ /// @brief Determines the range of double quoted scalar by scanning remaining input buffer contents.
+ /// @return A double quoted scalar.
+ str_view determine_double_quoted_scalar_range() {
+ const str_view sv {m_token_begin_itr, m_end_itr};
+
+ std::size_t pos = sv.find('\"');
+ while (pos != str_view::npos) {
+ FK_YAML_ASSERT(pos < sv.size());
+
+ bool is_closed = true;
+ if FK_YAML_LIKELY (pos > 0) {
+ // Double quotation marks can be escaped by a preceding backslash and the number of backslashes matters
+ // to determine if the found double quotation mark is escaped since the backslash itself can also be
+ // escaped:
+ // * odd number of backslashes -> double quotation mark IS escaped (e.g., "\\\"")
+ // * even number of backslashes -> double quotation mark IS NOT escaped (e.g., "\\"")
+ uint32_t backslash_counts = 0;
+ const char* p = m_token_begin_itr + (pos - 1);
+ do {
+ if (*p-- != '\\') {
+ break;
+ }
+ ++backslash_counts;
+ } while (p != m_token_begin_itr);
+ is_closed = ((backslash_counts & 1u) == 0); // true: even, false: odd
+ }
+
+ if (is_closed) {
+ // closing double quote is found.
+ m_cur_itr = m_token_begin_itr + (pos + 1);
+ str_view double_quoted_scalar {m_token_begin_itr, pos};
+ check_scalar_content(double_quoted_scalar);
+ return double_quoted_scalar;
+ }
+
+ pos = sv.find('\"', pos + 1);
+ }
+
+ m_cur_itr = m_end_itr; // update for error information
+ emit_error("Invalid end of input buffer in a double-quoted scalar token.");
+ }
+
+ /// @brief Determines the range of plain scalar by scanning remaining input buffer contents.
+ /// @return A plain scalar.
+ str_view determine_plain_scalar_range() {
+ const str_view sv {m_token_begin_itr, m_end_itr};
+
+ // flow indicators are checked only within a flow context.
+ const str_view filter = (m_state & flow_context_bit) ? "\t\n :{}[]," : "\t\n :";
+ std::size_t pos = sv.find_first_of(filter);
+ if FK_YAML_UNLIKELY (pos == str_view::npos) {
+ check_scalar_content(sv);
+ m_cur_itr = m_end_itr;
+ return sv;
+ }
+
+ bool ends_loop = false;
+ uint32_t indent = std::numeric_limits<uint32_t>::max();
+ do {
+ FK_YAML_ASSERT(pos < sv.size());
+ switch (sv[pos]) {
+ case '\n': {
+ if (indent == std::numeric_limits<uint32_t>::max()) {
+ indent = get_current_indent_level(&sv[pos]);
+ }
+
+ constexpr str_view space_filter {" \t\n"};
+ const std::size_t non_space_pos = sv.find_first_not_of(space_filter, pos);
+ const std::size_t last_newline_pos = sv.find_last_of('\n', non_space_pos);
+ FK_YAML_ASSERT(last_newline_pos != str_view::npos);
+
+ if (non_space_pos == str_view::npos || non_space_pos - last_newline_pos - 1 <= indent) {
+ ends_loop = true;
+ break;
+ }
+
+ pos = non_space_pos;
+ break;
+ }
+ case ' ':
+ case '\t':
+ if FK_YAML_UNLIKELY (pos == sv.size() - 1) {
+ // trim trailing space.
+ ends_loop = true;
+ break;
+ }
+
+ // Allow a space in a plain scalar only if the space is surrounded by non-space characters, but not
+ // followed by the comment prefix " #".
+ // Also, flow indicators are not allowed to be followed after a space in a flow context.
+ // See https://yaml.org/spec/1.2.2/#733-plain-style for more details.
+ switch (sv[pos + 1]) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '#':
+ ends_loop = true;
+ break;
+ case ':':
+ // " :" is permitted in a plain style string token, but not when followed by a space.
+ ends_loop = (pos < sv.size() - 2) && (sv[pos + 2] == ' ');
+ break;
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ',':
+ ends_loop = (m_state & flow_context_bit);
+ break;
+ default:
+ break;
+ }
+ break;
+ case ':':
+ if FK_YAML_LIKELY (pos + 1 < sv.size()) {
+ switch (sv[pos + 1]) {
+ case ' ':
+ case '\t':
+ case '\n':
+ ends_loop = true;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ',':
+ // This check is enabled only in a flow context.
+ ends_loop = true;
+ break;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+
+ if (ends_loop) {
+ break;
+ }
+
+ pos = sv.find_first_of(filter, pos + 1);
+ } while (pos != str_view::npos);
+
+ str_view plain_scalar = sv.substr(0, pos);
+ check_scalar_content(plain_scalar);
+ m_cur_itr = plain_scalar.end();
+ return plain_scalar;
+ }
+
+ /// @brief Scan a block style string token either in the literal or folded style.
+ /// @param base_indent The base indent level of the block scalar.
+ /// @param indicated_indent The indicated indent level in the block scalar header. 0 means it's not indicated.
+ /// @param token Storage for the scanned block scalar range.
+ /// @return The content indentation level of the block scalar.
+ str_view determine_block_scalar_content_range(
+ uint32_t base_indent, uint32_t indicated_indent, uint32_t& content_indent) {
+ const str_view sv {m_token_begin_itr, m_end_itr};
+ const std::size_t remain_input_len = sv.size();
+
+ // Handle leading all-space lines.
+ uint32_t cur_indent = 0;
+ uint32_t max_leading_indent = 0;
+ const char* cur_itr = m_token_begin_itr;
+ bool stop_increment = false;
+
+ while (cur_itr != m_end_itr) {
+ switch (*cur_itr++) {
+ case ' ':
+ if FK_YAML_LIKELY (!stop_increment) {
+ ++cur_indent;
+ }
+ continue;
+ case '\t':
+ // Tabs are not counted as an indent character but still part of an empty line.
+ // See https://yaml.org/spec/1.2.2/#rule-s-indent and https://yaml.org/spec/1.2.2/#64-empty-lines.
+ stop_increment = true;
+ continue;
+ case '\n':
+ max_leading_indent = std::max(cur_indent, max_leading_indent);
+ cur_indent = 0;
+ stop_increment = false;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+
+ // all the block scalar contents are empty lines, and no subsequent token exists.
+ if FK_YAML_UNLIKELY (cur_itr == m_end_itr) {
+ // Without the following iterator update, lexer cannot reach the end of input buffer and causes infinite
+ // loops from the next loop. (https://github.com/fktn-k/fkYAML/pull/410)
+ m_cur_itr = m_end_itr;
+
+ // If there's no non-empty line, the content indentation level is equal to the number of spaces on the
+ // longest line. https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator
+ content_indent =
+ indicated_indent == 0 ? std::max(cur_indent, max_leading_indent) : base_indent + indicated_indent;
+ return sv;
+ }
+
+ // Any leading empty line must not contain more spaces than the first non-empty line.
+ if FK_YAML_UNLIKELY (cur_indent < max_leading_indent) {
+ emit_error("Any leading empty line must not be more indented than the first non-empty line.");
+ }
+
+ if (indicated_indent == 0) {
+ FK_YAML_ASSERT(base_indent < cur_indent);
+ indicated_indent = cur_indent - base_indent;
+ }
+ else if FK_YAML_UNLIKELY (cur_indent < base_indent + indicated_indent) {
+ emit_error("The first non-empty line in the block scalar is less indented.");
+ }
+
+ std::size_t last_newline_pos = sv.find('\n', cur_itr - m_token_begin_itr + 1);
+ if (last_newline_pos == str_view::npos) {
+ last_newline_pos = remain_input_len;
+ }
+
+ content_indent = base_indent + indicated_indent;
+ while (last_newline_pos < remain_input_len) {
+ std::size_t cur_line_end_pos = sv.find('\n', last_newline_pos + 1);
+ if (cur_line_end_pos == str_view::npos) {
+ cur_line_end_pos = remain_input_len;
+ }
+
+ const std::size_t cur_line_content_begin_pos = sv.find_first_not_of(' ', last_newline_pos + 1);
+ if (cur_line_content_begin_pos == str_view::npos) {
+ last_newline_pos = cur_line_end_pos;
+ continue;
+ }
+
+ FK_YAML_ASSERT(last_newline_pos < cur_line_content_begin_pos);
+ cur_indent = static_cast<uint32_t>(cur_line_content_begin_pos - last_newline_pos - 1);
+ if (cur_indent < content_indent && sv[cur_line_content_begin_pos] != '\n') {
+ if FK_YAML_UNLIKELY (cur_indent > base_indent) {
+ // This path assumes an input like the following:
+ // ```yaml
+ // foo: |
+ // text
+ // invalid # this line is less indented than the content indent level (2)
+ // # but more indented than the base indent level (0)
+ // ```
+ // In such cases, the less indented line cannot be the start of the next token.
+ emit_error("A content line of the block scalar is less indented.");
+ }
+
+ // Interpret less indented non-space characters as the start of the next token.
+ break;
+ }
+
+ last_newline_pos = cur_line_end_pos;
+ }
+
+ // include last newline character if not all characters have been consumed yet.
+ if (last_newline_pos < remain_input_len) {
+ ++last_newline_pos;
+ }
+
+ m_cur_itr = m_token_begin_itr + last_newline_pos;
+ return sv.substr(0, last_newline_pos);
+ }
+
+ /// @brief Checks if the given scalar contains no unescaped control characters.
+ /// @param scalar Scalar contents.
+ void check_scalar_content(const str_view& scalar) const {
+ const char* p_current = scalar.begin();
+ const char* p_end = scalar.end();
+
+ while (p_current != p_end) {
+ const uint32_t num_bytes = utf8::get_num_bytes(static_cast<uint8_t>(*p_current));
+ if (num_bytes > 1) {
+ // Multibyte characters are already checked in the input_adapter module.
+ p_current += num_bytes;
+ continue;
+ }
+
+ switch (*p_current++) {
+ // 0x00(NULL) has already been handled above.
+ case 0x01:
+ emit_error("Control character U+0001 (SOH) must be escaped to \\u0001.");
+ case 0x02:
+ emit_error("Control character U+0002 (STX) must be escaped to \\u0002.");
+ case 0x03:
+ emit_error("Control character U+0003 (ETX) must be escaped to \\u0003.");
+ case 0x04:
+ emit_error("Control character U+0004 (EOT) must be escaped to \\u0004.");
+ case 0x05:
+ emit_error("Control character U+0005 (ENQ) must be escaped to \\u0005.");
+ case 0x06:
+ emit_error("Control character U+0006 (ACK) must be escaped to \\u0006.");
+ case 0x07:
+ emit_error("Control character U+0007 (BEL) must be escaped to \\a or \\u0007.");
+ case 0x08:
+ emit_error("Control character U+0008 (BS) must be escaped to \\b or \\u0008.");
+ case 0x09: // HT
+ // horizontal tabs (\t) are safe to use without escaping.
+ break;
+ // 0x0A(LF) has already been handled above.
+ case 0x0B:
+ emit_error("Control character U+000B (VT) must be escaped to \\v or \\u000B.");
+ case 0x0C:
+ emit_error("Control character U+000C (FF) must be escaped to \\f or \\u000C.");
+ // 0x0D(CR) has already been handled above.
+ case 0x0E:
+ emit_error("Control character U+000E (SO) must be escaped to \\u000E.");
+ case 0x0F:
+ emit_error("Control character U+000F (SI) must be escaped to \\u000F.");
+ case 0x10:
+ emit_error("Control character U+0010 (DLE) must be escaped to \\u0010.");
+ case 0x11:
+ emit_error("Control character U+0011 (DC1) must be escaped to \\u0011.");
+ case 0x12:
+ emit_error("Control character U+0012 (DC2) must be escaped to \\u0012.");
+ case 0x13:
+ emit_error("Control character U+0013 (DC3) must be escaped to \\u0013.");
+ case 0x14:
+ emit_error("Control character U+0014 (DC4) must be escaped to \\u0014.");
+ case 0x15:
+ emit_error("Control character U+0015 (NAK) must be escaped to \\u0015.");
+ case 0x16:
+ emit_error("Control character U+0016 (SYN) must be escaped to \\u0016.");
+ case 0x17:
+ emit_error("Control character U+0017 (ETB) must be escaped to \\u0017.");
+ case 0x18:
+ emit_error("Control character U+0018 (CAN) must be escaped to \\u0018.");
+ case 0x19:
+ emit_error("Control character U+0019 (EM) must be escaped to \\u0019.");
+ case 0x1A:
+ emit_error("Control character U+001A (SUB) must be escaped to \\u001A.");
+ case 0x1B:
+ emit_error("Control character U+001B (ESC) must be escaped to \\e or \\u001B.");
+ case 0x1C:
+ emit_error("Control character U+001C (FS) must be escaped to \\u001C.");
+ case 0x1D:
+ emit_error("Control character U+001D (GS) must be escaped to \\u001D.");
+ case 0x1E:
+ emit_error("Control character U+001E (RS) must be escaped to \\u001E.");
+ case 0x1F:
+ emit_error("Control character U+001F (US) must be escaped to \\u001F.");
+ default:
+ break;
+ }
+ }
+ }
+
+ /// @brief Gets the metadata of a following block style string scalar.
+ /// @param chomp_type A variable to store the retrieved chomping style type.
+ /// @param indent A variable to store the retrieved indent size.
+ /// @return Block scalar header information converted from the header line.
+ block_scalar_header convert_to_block_scalar_header(str_view line) {
+ constexpr str_view comment_prefix {" #"};
+ const std::size_t comment_begin_pos = line.find(comment_prefix);
+ if (comment_begin_pos != str_view::npos) {
+ line = line.substr(0, comment_begin_pos);
+ }
+
+ if (line.empty()) {
+ return {};
+ }
+
+ block_scalar_header header {};
+ for (const char c : line) {
+ switch (c) {
+ case '-':
+ if FK_YAML_UNLIKELY (header.chomp != chomping_indicator_t::CLIP) {
+ emit_error("Too many block chomping indicators specified.");
+ }
+ header.chomp = chomping_indicator_t::STRIP;
+ break;
+ case '+':
+ if FK_YAML_UNLIKELY (header.chomp != chomping_indicator_t::CLIP) {
+ emit_error("Too many block chomping indicators specified.");
+ }
+ header.chomp = chomping_indicator_t::KEEP;
+ break;
+ case '0':
+ emit_error("An indentation level for a block scalar cannot be 0.");
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if FK_YAML_UNLIKELY (header.indent > 0) {
+ emit_error("Invalid indentation level for a block scalar. It must be between 1 and 9.");
+ }
+ header.indent = static_cast<uint32_t>(c - '0');
+ break;
+ case ' ':
+ case '\t':
+ break;
+ default:
+ emit_error("Invalid character found in a block scalar header.");
+ }
+ }
+
+ return header;
+ }
+
+ /// @brief Skip white spaces (half-width spaces and tabs) from the current position.
+ void skip_white_spaces() {
+ m_cur_itr = std::find_if_not(m_cur_itr, m_end_itr, [](char c) { return (c == ' ' || c == '\t'); });
+ }
+
+ /// @brief Skip white spaces and newline codes (CR/LF) from the current position.
+ void skip_white_spaces_and_newline_codes() {
+ if (m_cur_itr != m_end_itr) {
+ m_cur_itr = std::find_if_not(m_cur_itr, m_end_itr, [](char c) {
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ return true;
+ default:
+ return false;
+ }
+ });
+ }
+ }
+
+ /// @brief Skip the rest in the current line.
+ void skip_until_line_end() {
+ while (m_cur_itr != m_end_itr) {
+ switch (*m_cur_itr) {
+ case '\n':
+ ++m_cur_itr;
+ return;
+ default:
+ ++m_cur_itr;
+ break;
+ }
+ }
+ }
+
+ /// @brief Emits an error with the given message.
+ /// @param msg A message for the resulting error.
+ [[noreturn]] void emit_error(const char* msg) const {
+ m_pos_tracker.update_position(m_cur_itr);
+ throw fkyaml::parse_error(msg, m_pos_tracker.get_lines_read(), m_pos_tracker.get_cur_pos_in_line());
+ }
+
+private:
+ /// The iterator to the first element in the input buffer.
+ const char* m_begin_itr {};
+ /// The iterator to the current character in the input buffer.
+ const char* m_cur_itr {};
+ /// The iterator to the beginning of the current token.
+ const char* m_token_begin_itr {};
+ /// The iterator to the past-the-end element in the input buffer.
+ const char* m_end_itr {};
+ /// The current position tracker of the input buffer.
+ mutable position_tracker m_pos_tracker {};
+ /// The last yaml version.
+ str_view m_yaml_version;
+ /// The last tag handle.
+ str_view m_tag_handle;
+ /// The last tag prefix.
+ str_view m_tag_prefix;
+ /// The last block scalar header.
+ block_scalar_header m_block_scalar_header {};
+ /// The beginning position of the last lexical token. (zero origin)
+ uint32_t m_last_token_begin_pos {0};
+ /// The beginning line of the last lexical token. (zero origin)
+ uint32_t m_last_token_begin_line {0};
+ /// The current depth of flow context.
+ uint32_t m_state {0};
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_LEXICAL_ANALYZER_HPP */
+
+// #include <fkYAML/detail/input/scalar_parser.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_SCALAR_PARSER_HPP
+#define FK_YAML_DETAIL_INPUT_SCALAR_PARSER_HPP
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+// #include <fkYAML/detail/conversions/scalar_conv.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+// **NOTE FOR LIBRARY DEVELOPERS**:
+// Implementations in this header file are intentionally optimized for conversions between YAML scalars and native C++
+// types. So, some implementations don't follow the conversions in the standard C++ functions. For example, octals must
+// begin with "0o" (not "0"), which is specified in the YAML spec 1.2.
+
+#ifndef FK_YAML_CONVERSIONS_SCALAR_CONV_HPP
+#define FK_YAML_CONVERSIONS_SCALAR_CONV_HPP
+
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+
+#if FK_YAML_HAS_TO_CHARS
+// Prefer std::to_chars() and std::from_chars() functions if available.
+#include <charconv>
+#else
+// Fallback to legacy string conversion functions otherwise.
+#include <string> // std::stof(), std::stod(), std::stold()
+#endif
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+//////////////////////////
+// conv_limits_base //
+//////////////////////////
+
+/// @brief A structure which provides limits for conversions between scalars and integers.
+/// @note This structure contains common limits in both signed and unsigned integers.
+/// @tparam NumBytes The number of bytes for the integer type.
+template <std::size_t NumBytes>
+struct conv_limits_base {};
+
+/// @brief The specialization of conv_limits_base for 1 byte integers, e.g., int8_t, uint8_t.
+template <>
+struct conv_limits_base<1u> {
+ /// max characters for octals (0o377) without the prefix part.
+ static constexpr std::size_t max_chars_oct = 3;
+ /// max characters for hexadecimals (0xFF) without the prefix part.
+ static constexpr std::size_t max_chars_hex = 2;
+
+ /// @brief Check if the given octals are safely converted into 1 byte integer.
+ /// @param octs The pointer to octal characters
+ /// @param len The length of octal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept {
+ return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '3');
+ }
+
+ /// @brief Check if the given hexadecimals are safely converted into 1 byte integer.
+ /// @param octs The pointer to hexadecimal characters
+ /// @param len The length of hexadecimal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept {
+ return len <= max_chars_hex;
+ }
+};
+
+/// @brief The specialization of conv_limits_base for 2 byte integers, e.g., int16_t, uint16_t.
+template <>
+struct conv_limits_base<2u> {
+ /// max characters for octals (0o177777) without the prefix part.
+ static constexpr std::size_t max_chars_oct = 6;
+ /// max characters for hexadecimals (0xFFFF) without the prefix part.
+ static constexpr std::size_t max_chars_hex = 4;
+
+ /// @brief Check if the given octals are safely converted into 2 byte integer.
+ /// @param octs The pointer to octal characters
+ /// @param len The length of octal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept {
+ return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '1');
+ }
+
+ /// @brief Check if the given hexadecimals are safely converted into 2 byte integer.
+ /// @param octs The pointer to hexadecimal characters
+ /// @param len The length of hexadecimal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept {
+ return len <= max_chars_hex;
+ }
+};
+
+/// @brief The specialization of conv_limits_base for 4 byte integers, e.g., int32_t, uint32_t.
+template <>
+struct conv_limits_base<4u> {
+ /// max characters for octals (0o37777777777) without the prefix part.
+ static constexpr std::size_t max_chars_oct = 11;
+ /// max characters for hexadecimals (0xFFFFFFFF) without the prefix part.
+ static constexpr std::size_t max_chars_hex = 8;
+
+ /// @brief Check if the given octals are safely converted into 4 byte integer.
+ /// @param octs The pointer to octal characters
+ /// @param len The length of octal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept {
+ return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '3');
+ }
+
+ /// @brief Check if the given hexadecimals are safely converted into 4 byte integer.
+ /// @param octs The pointer to hexadecimal characters
+ /// @param len The length of hexadecimal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept {
+ return len <= max_chars_hex;
+ }
+};
+
+/// @brief The specialization of conv_limits_base for 8 byte integers, e.g., int64_t, uint64_t.
+template <>
+struct conv_limits_base<8u> {
+ /// max characters for octals (0o1777777777777777777777) without the prefix part.
+ static constexpr std::size_t max_chars_oct = 22;
+ /// max characters for hexadecimals (0xFFFFFFFFFFFFFFFF) without the prefix part.
+ static constexpr std::size_t max_chars_hex = 16;
+
+ /// @brief Check if the given octals are safely converted into 8 byte integer.
+ /// @param octs The pointer to octal characters
+ /// @param len The length of octal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_octs_safe(const char* octs, std::size_t len) noexcept {
+ return (len < max_chars_oct) || (len == max_chars_oct && octs[0] <= '1');
+ }
+
+ /// @brief Check if the given hexadecimals are safely converted into 8 byte integer.
+ /// @param octs The pointer to hexadecimal characters
+ /// @param len The length of hexadecimal characters
+ /// @return true is safely convertible, false otherwise.
+ static bool check_if_hexs_safe(const char* /*unused*/, std::size_t len) noexcept {
+ return len <= max_chars_hex;
+ }
+};
+
+/////////////////////
+// conv_limits //
+/////////////////////
+
+/// @brief A structure which provides limits for conversions between scalars and integers.
+/// @note This structure contains limits which differs based on signedness.
+/// @tparam NumBytes The number of bytes for the integer type.
+/// @tparam IsSigned Whether an integer is signed or unsigned
+template <std::size_t NumBytes, bool IsSigned>
+struct conv_limits {};
+
+/// @brief The specialization of conv_limits for 1 byte signed integers, e.g., int8_t.
+template <>
+struct conv_limits<1u, true> : conv_limits_base<1u> {
+ /// with or without sign.
+ static constexpr bool is_signed = true;
+
+ /// max characters for decimals (-128..127) without sign.
+ static constexpr std::size_t max_chars_dec = 3;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ // Making this function a static constexpr variable, a link error happens.
+ // Although the issue has been fixed since C++17, this workaround is necessary to let this functionality work
+ // with C++11 (the library's default C++ standard version).
+ // The same thing is applied to similar functions in the other specializations.
+
+ static constexpr char max_value_chars[] = "127";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value without sign.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "128";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 1 byte unsigned integers, e.g., uint8_t.
+template <>
+struct conv_limits<1u, false> : conv_limits_base<1u> {
+ /// with or without sign.
+ static constexpr bool is_signed = false;
+
+ /// max characters for decimals (0..255) without sign.
+ static constexpr std::size_t max_chars_dec = 3;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "255";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "0";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 2 byte signed integers, e.g., int16_t.
+template <>
+struct conv_limits<2u, true> : conv_limits_base<2u> {
+ /// with or without sign.
+ static constexpr bool is_signed = true;
+
+ /// max characters for decimals (-32768..32767) without sign.
+ static constexpr std::size_t max_chars_dec = 5;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "32767";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value without sign.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "32768";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 2 byte unsigned integers, e.g., uint16_t.
+template <>
+struct conv_limits<2u, false> : conv_limits_base<2u> {
+ /// with or without sign.
+ static constexpr bool is_signed = false;
+
+ /// max characters for decimals (0..65535) without sign.
+ static constexpr std::size_t max_chars_dec = 5;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "65535";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "0";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 4 byte signed integers, e.g., int32_t.
+template <>
+struct conv_limits<4u, true> : conv_limits_base<4u> {
+ /// with or without sign.
+ static constexpr bool is_signed = true;
+
+ /// max characters for decimals (-2147483648..2147483647) without sign.
+ static constexpr std::size_t max_chars_dec = 10;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "2147483647";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value without sign.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "2147483648";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 4 byte unsigned integers, e.g., uint32_t.
+template <>
+struct conv_limits<4u, false> : conv_limits_base<4u> {
+ /// with or without sign.
+ static constexpr bool is_signed = false;
+
+ /// max characters for decimals (0..4294967295) without sign.
+ static constexpr std::size_t max_chars_dec = 10;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "4294967295";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "0";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 8 byte signed integers, e.g., int64_t.
+template <>
+struct conv_limits<8u, true> : conv_limits_base<8u> {
+ /// with or without sign.
+ static constexpr bool is_signed = true;
+
+ /// max characters for decimals (-9223372036854775808..9223372036854775807) without sign.
+ static constexpr std::size_t max_chars_dec = 19;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "9223372036854775807";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value without sign.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "9223372036854775808";
+ return &min_value_chars[0];
+ }
+};
+
+/// @brief The specialization of conv_limits for 8 byte unsigned integers, e.g., uint64_t.
+template <>
+struct conv_limits<8u, false> : conv_limits_base<8u> {
+ /// with or without sign.
+ static constexpr bool is_signed = false;
+
+ /// max characters for decimals (0..18446744073709551615) without sign.
+ static constexpr std::size_t max_chars_dec = 20;
+
+ /// string representation of max decimal value.
+ static const char* max_value_chars_dec() noexcept {
+ static constexpr char max_value_chars[] = "18446744073709551615";
+ return &max_value_chars[0];
+ }
+
+ /// string representation of min decimal value.
+ static const char* min_value_chars_dec() noexcept {
+ static constexpr char min_value_chars[] = "0";
+ return &min_value_chars[0];
+ }
+};
+
+//////////////////////////
+// scalar <--> null //
+//////////////////////////
+
+/// @brief Converts a scalar into a null value
+/// @tparam CharItr Type of char iterators. Its value type must be `char` (maybe cv-qualified).
+/// @param begin The iterator to the first element of the scalar.
+/// @param end The iterator to the past-the-end element of the scalar.
+/// @param /*unused*/ The null value holder (unused since it can only have `nullptr`)
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename CharItr>
+inline bool aton(CharItr begin, CharItr end, std::nullptr_t& /*unused*/) noexcept {
+ static_assert(is_iterator_of<CharItr, char>::value, "aton() accepts iterators for char type");
+
+ if FK_YAML_UNLIKELY (begin == end) {
+ return false;
+ }
+
+ const auto len = static_cast<uint32_t>(std::distance(begin, end));
+
+ // This path is the most probable case, so check it first.
+ if FK_YAML_LIKELY (len == 4) {
+ const char* p_begin = &*begin;
+ return (std::strncmp(p_begin, "null", 4) == 0) || (std::strncmp(p_begin, "Null", 4) == 0) ||
+ (std::strncmp(p_begin, "NULL", 4) == 0);
+ }
+
+ if (len == 1) {
+ return *begin == '~';
+ }
+
+ return false;
+}
+
+/////////////////////////////
+// scalar <--> boolean //
+/////////////////////////////
+
+/// @brief Converts a scalar into a boolean value
+/// @tparam CharItr The type of char iterators. Its value type must be `char` (maybe cv-qualified).
+/// @tparam BoolType The output boolean type.
+/// @param begin The iterator to the first element of the scalar.
+/// @param end The iterator to the past-the-end element of the scalar.
+/// @param boolean The boolean value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename CharItr, typename BoolType>
+inline bool atob(CharItr begin, CharItr end, BoolType& boolean) noexcept {
+ static_assert(is_iterator_of<CharItr, char>::value, "atob() accepts iterators for char type");
+
+ if FK_YAML_UNLIKELY (begin == end) {
+ return false;
+ }
+
+ const auto len = static_cast<uint32_t>(std::distance(begin, end));
+ const char* p_begin = &*begin;
+
+ if (len == 4) {
+ const bool is_true = (std::strncmp(p_begin, "true", 4) == 0) || (std::strncmp(p_begin, "True", 4) == 0) ||
+ (std::strncmp(p_begin, "TRUE", 4) == 0);
+
+ if FK_YAML_LIKELY (is_true) {
+ boolean = static_cast<BoolType>(true);
+ }
+ return is_true;
+ }
+
+ if (len == 5) {
+ const bool is_false = (std::strncmp(p_begin, "false", 5) == 0) || (std::strncmp(p_begin, "False", 5) == 0) ||
+ (std::strncmp(p_begin, "FALSE", 5) == 0);
+
+ if FK_YAML_LIKELY (is_false) {
+ boolean = static_cast<BoolType>(false);
+ }
+ return is_false;
+ }
+
+ return false;
+}
+
+/////////////////////////////
+// scalar <--> integer //
+/////////////////////////////
+
+//
+// scalar --> decimals
+//
+
+/// @brief Converts a scalar into decimals. This is common implementation for both signed/unsigned integer types.
+/// @warning
+/// This function does NOT care about overflows if IntType is unsigned. The source string value must be validated
+/// beforehand by calling either atoi_dec_pos() or atoi_dec_neg() functions.
+/// Furthermore, `p_begin` and `p_end` must NOT be null. Validate them before calling this function.
+/// @tparam IntType The output integer type. It can be either signed or unsigned.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param i The output integer value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename IntType>
+inline bool atoi_dec_unchecked(const char* p_begin, const char* p_end, IntType& i) noexcept {
+ static_assert(
+ is_non_bool_integral<IntType>::value,
+ "atoi_dec_unchecked() accepts non-boolean integral types as an output type");
+
+ i = 0;
+ do {
+ const char c = *p_begin;
+ if FK_YAML_UNLIKELY (c < '0' || '9' < c) {
+ return false;
+ }
+ // Overflow is intentional when the IntType is signed.
+ i = i * static_cast<IntType>(10) + static_cast<IntType>(c - '0');
+ } while (++p_begin != p_end);
+
+ return true;
+}
+
+/// @brief Converts a scalar into positive decimals. This function executes bounds check to avoid overflow.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @tparam IntType The output integer type. It can be either signed or unsigned.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param i The output integer value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename IntType>
+inline bool atoi_dec_pos(const char* p_begin, const char* p_end, IntType& i) noexcept {
+ static_assert(
+ is_non_bool_integral<IntType>::value, "atoi_dec_pos() accepts non-boolean integral types as an output type");
+
+ if FK_YAML_UNLIKELY (p_begin == p_end) {
+ return false;
+ }
+
+ using conv_limits_type = conv_limits<sizeof(IntType), std::is_signed<IntType>::value>;
+
+ const auto len = static_cast<std::size_t>(p_end - p_begin);
+ if FK_YAML_UNLIKELY (len > conv_limits_type::max_chars_dec) {
+ // Overflow will happen.
+ return false;
+ }
+
+ if (len == conv_limits_type::max_chars_dec) {
+ const char* p_max_value_chars_dec = conv_limits_type::max_value_chars_dec();
+
+ for (std::size_t idx = 0; idx < conv_limits_type::max_chars_dec; idx++) {
+ if (p_begin[idx] < p_max_value_chars_dec[idx]) {
+ // No need to check the lower digits. Overflow will no longer happen.
+ break;
+ }
+
+ if FK_YAML_UNLIKELY (p_begin[idx] > p_max_value_chars_dec[idx]) {
+ // Overflow will happen.
+ return false;
+ }
+ }
+ }
+
+ return atoi_dec_unchecked(p_begin, p_end, i);
+}
+
+/// @brief Converts a scalar into negative decimals. This function executes bounds check to avoid underflow.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @tparam IntType The output integer type. It must be signed.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param i The output integer value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename IntType>
+inline bool atoi_dec_neg(const char* p_begin, const char* p_end, IntType& i) noexcept {
+ static_assert(
+ is_non_bool_integral<IntType>::value, "atoi_dec_neg() accepts non-boolean integral types as an output type");
+
+ if FK_YAML_UNLIKELY (p_begin == p_end) {
+ return false;
+ }
+
+ using conv_limits_type = conv_limits<sizeof(IntType), std::is_signed<IntType>::value>;
+
+ const auto len = static_cast<std::size_t>(p_end - p_begin);
+ if FK_YAML_UNLIKELY (len > conv_limits_type::max_chars_dec) {
+ // Underflow will happen.
+ return false;
+ }
+
+ if (len == conv_limits_type::max_chars_dec) {
+ const char* p_min_value_chars_dec = conv_limits_type::min_value_chars_dec();
+
+ for (std::size_t idx = 0; idx < conv_limits_type::max_chars_dec; idx++) {
+ if (p_begin[idx] < p_min_value_chars_dec[idx]) {
+ // No need to check the lower digits. Underflow will no longer happen.
+ break;
+ }
+
+ if FK_YAML_UNLIKELY (p_begin[idx] > p_min_value_chars_dec[idx]) {
+ // Underflow will happen.
+ return false;
+ }
+ }
+ }
+
+ return atoi_dec_unchecked(p_begin, p_end, i);
+}
+
+//
+// scalar --> octals
+//
+
+/// @brief Converts a scalar into octals. This function executes bounds check to avoid overflow.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @tparam IntType The output integer type. It can be either signed or unsigned.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param i The output integer value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename IntType>
+inline bool atoi_oct(const char* p_begin, const char* p_end, IntType& i) noexcept {
+ static_assert(
+ is_non_bool_integral<IntType>::value, "atoi_oct() accepts non-boolean integral types as an output type");
+
+ if FK_YAML_UNLIKELY (p_begin == p_end) {
+ return false;
+ }
+
+ using conv_limits_type = conv_limits<sizeof(IntType), std::is_signed<IntType>::value>;
+
+ const auto len = static_cast<std::size_t>(p_end - p_begin);
+ if FK_YAML_UNLIKELY (!conv_limits_type::check_if_octs_safe(p_begin, len)) {
+ return false;
+ }
+
+ i = 0;
+ do {
+ const char c = *p_begin;
+ if FK_YAML_UNLIKELY (c < '0' || '7' < c) {
+ return false;
+ }
+ i = i * static_cast<IntType>(8) + static_cast<IntType>(c - '0');
+ } while (++p_begin != p_end);
+
+ return true;
+}
+
+//
+// scalar --> hexadecimals
+//
+
+/// @brief Converts a scalar into hexadecimals. This function executes bounds check to avoid overflow.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @tparam IntType The output integer type. It can be either signed or unsigned.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param i The output integer value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename IntType>
+inline bool atoi_hex(const char* p_begin, const char* p_end, IntType& i) noexcept {
+ static_assert(
+ is_non_bool_integral<IntType>::value, "atoi_hex() accepts non-boolean integral types as an output type");
+
+ if FK_YAML_UNLIKELY (p_begin == p_end) {
+ return false;
+ }
+
+ using conv_limits_type = conv_limits<sizeof(IntType), std::is_signed<IntType>::value>;
+
+ const auto len = static_cast<std::size_t>(p_end - p_begin);
+ if FK_YAML_UNLIKELY (!conv_limits_type::check_if_hexs_safe(p_begin, len)) {
+ return false;
+ }
+
+ i = 0;
+ do {
+ // NOLINTBEGIN(bugprone-misplaced-widening-cast)
+ const char c = *p_begin;
+ IntType ci = 0;
+ if ('0' <= c && c <= '9') {
+ ci = static_cast<IntType>(c - '0');
+ }
+ else if ('A' <= c && c <= 'F') {
+ ci = static_cast<IntType>(c - 'A' + 10);
+ }
+ else if ('a' <= c && c <= 'f') {
+ ci = static_cast<IntType>(c - 'a' + 10);
+ }
+ else {
+ return false;
+ }
+ i = i * static_cast<IntType>(16) + ci;
+ // NOLINTEND(bugprone-misplaced-widening-cast)
+ } while (++p_begin != p_end);
+
+ return true;
+}
+
+//
+// atoi() & itoa()
+//
+
+/// @brief Converts a scalar into integers. This function executes bounds check to avoid overflow/underflow.
+/// @tparam CharItr The type of char iterators. Its value type must be char (maybe cv-qualified).
+/// @tparam IntType The output integer type. It can be either signed or unsigned.
+/// @param begin The iterator to the first element of the scalar.
+/// @param end The iterator to the past-the-end element of the scalar.
+/// @param i The output integer value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename CharItr, typename IntType>
+inline bool atoi(CharItr begin, CharItr end, IntType& i) noexcept {
+ static_assert(is_iterator_of<CharItr, char>::value, "atoi() accepts iterators for char type");
+ static_assert(is_non_bool_integral<IntType>::value, "atoi() accepts non-boolean integral types as an output type");
+
+ if FK_YAML_UNLIKELY (begin == end) {
+ return false;
+ }
+
+ const auto len = static_cast<uint32_t>(std::distance(begin, end));
+ const char* p_begin = &*begin;
+ const char* p_end = p_begin + len;
+
+ const char first = *begin;
+ if (first == '+') {
+ return atoi_dec_pos(p_begin + 1, p_end, i);
+ }
+
+ if (first == '-') {
+ if (!std::numeric_limits<IntType>::is_signed) {
+ return false;
+ }
+
+ const bool success = atoi_dec_neg(p_begin + 1, p_end, i);
+ if (success) {
+ i *= static_cast<IntType>(-1);
+ }
+
+ return success;
+ }
+
+ if (first != '0') {
+ return atoi_dec_pos(p_begin, p_end, i);
+ }
+
+ if (p_begin + 1 != p_end) {
+ switch (*(p_begin + 1)) {
+ case 'o':
+ return atoi_oct(p_begin + 2, p_end, i);
+ case 'x':
+ return atoi_hex(p_begin + 2, p_end, i);
+ default:
+ // The YAML spec doesn't allow decimals starting with 0.
+ return false;
+ }
+ }
+
+ i = 0;
+ return true;
+}
+
+///////////////////////////
+// scalar <--> float //
+///////////////////////////
+
+/// @brief Set an infinite `float` value based on the given signedness.
+/// @param f The output `float` value holder.
+/// @param sign Whether the infinite value should be positive or negative.
+inline void set_infinity(float& f, const float sign) noexcept {
+ f = std::numeric_limits<float>::infinity() * sign;
+}
+
+/// @brief Set an infinite `double` value based on the given signedness.
+/// @param f The output `double` value holder.
+/// @param sign Whether the infinite value should be positive or negative.
+inline void set_infinity(double& f, const double sign) noexcept {
+ f = std::numeric_limits<double>::infinity() * sign;
+}
+
+/// @brief Set a NaN `float` value.
+/// @param f The output `float` value holder.
+inline void set_nan(float& f) noexcept {
+ f = std::nanf("");
+}
+
+/// @brief Set a NaN `double` value.
+/// @param f The output `double` value holder.
+inline void set_nan(double& f) noexcept {
+ f = std::nan("");
+}
+
+#if FK_YAML_HAS_TO_CHARS
+
+/// @brief Converts a scalar into a floating point value.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param f The output floating point value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename FloatType>
+inline bool atof_impl(const char* p_begin, const char* p_end, FloatType& f) noexcept {
+ static_assert(std::is_floating_point_v<FloatType>, "atof_impl() accepts floating point types as an output type");
+ if (auto [ptr, ec] = std::from_chars(p_begin, p_end, f); ec == std::errc {}) {
+ return ptr == p_end;
+ }
+ return false;
+}
+
+#else
+
+/// @brief Converts a scalar into a `float` value.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param f The output `float` value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+inline bool atof_impl(const char* p_begin, const char* p_end, float& f) {
+ std::size_t idx = 0;
+ f = std::stof(std::string(p_begin, p_end), &idx);
+ return idx == static_cast<std::size_t>(p_end - p_begin);
+}
+
+/// @brief Converts a scalar into a `double` value.
+/// @warning `p_begin` and `p_end` must not be null. Validate them before calling this function.
+/// @param p_begin The pointer to the first element of the scalar.
+/// @param p_end The pointer to the past-the-end element of the scalar.
+/// @param f The output `double` value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+inline bool atof_impl(const char* p_begin, const char* p_end, double& f) {
+ std::size_t idx = 0;
+ f = std::stod(std::string(p_begin, p_end), &idx);
+ return idx == static_cast<std::size_t>(p_end - p_begin);
+}
+
+#endif // FK_YAML_HAS_TO_CHARS
+
+/// @brief Converts a scalar into a floating point value.
+/// @tparam CharItr The type of char iterators. Its value type must be char (maybe cv-qualified).
+/// @tparam FloatType The output floating point value type.
+/// @param begin The iterator to the first element of the scalar.
+/// @param end The iterator to the past-the-end element of the scalar.
+/// @param f The output floating point value holder.
+/// @return true if the conversion completes successfully, false otherwise.
+template <typename CharItr, typename FloatType>
+inline bool atof(CharItr begin, CharItr end, FloatType& f) noexcept(noexcept(atof_impl(&*begin, &*begin, f))) {
+ static_assert(is_iterator_of<CharItr, char>::value, "atof() accepts iterators for char type");
+ static_assert(std::is_floating_point<FloatType>::value, "atof() accepts floating point types as an output type");
+
+ if FK_YAML_UNLIKELY (begin == end) {
+ return false;
+ }
+
+ const auto len = static_cast<uint32_t>(std::distance(begin, end));
+ const char* p_begin = &*begin;
+ const char* p_end = p_begin + len;
+
+ if (*p_begin == '-' || *p_begin == '+') {
+ if (len == 5) {
+ const char* p_from_second = p_begin + 1;
+ const bool is_inf = (std::strncmp(p_from_second, ".inf", 4) == 0) ||
+ (std::strncmp(p_from_second, ".Inf", 4) == 0) ||
+ (std::strncmp(p_from_second, ".INF", 4) == 0);
+ if (is_inf) {
+ set_infinity(f, *p_begin == '-' ? static_cast<FloatType>(-1.) : static_cast<FloatType>(1.));
+ return true;
+ }
+ }
+
+ if (*p_begin == '+') {
+ // Skip the positive sign since it's sometimes not recognized as part of float value.
+ ++p_begin;
+ }
+ }
+ else if (len == 4) {
+ const bool is_inf = (std::strncmp(p_begin, ".inf", 4) == 0) || (std::strncmp(p_begin, ".Inf", 4) == 0) ||
+ (std::strncmp(p_begin, ".INF", 4) == 0);
+ if (is_inf) {
+ set_infinity(f, static_cast<FloatType>(1.));
+ return true;
+ }
+
+ const bool is_nan = (std::strncmp(p_begin, ".nan", 4) == 0) || (std::strncmp(p_begin, ".NaN", 4) == 0) ||
+ (std::strncmp(p_begin, ".NAN", 4) == 0);
+ if (is_nan) {
+ set_nan(f);
+ return true;
+ }
+ }
+
+#if FK_YAML_HAS_TO_CHARS
+ return atof_impl(p_begin, p_end, f);
+#else
+ bool success = false;
+ try {
+ success = atof_impl(p_begin, p_end, f);
+ }
+ catch (const std::exception& /*unused*/) {
+ success = false;
+ }
+
+ return success;
+#endif
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_CONVERSIONS_SCALAR_CONV_HPP */
+
+// #include <fkYAML/detail/encodings/yaml_escaper.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ENCODINGS_YAML_ESCAPER_HPP
+#define FK_YAML_DETAIL_ENCODINGS_YAML_ESCAPER_HPP
+
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+// #include <fkYAML/detail/encodings/utf_encodings.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+class yaml_escaper {
+ using iterator = ::std::string::const_iterator;
+
+public:
+ static bool unescape(const char*& begin, const char* end, std::string& buff) {
+ FK_YAML_ASSERT(*begin == '\\' && std::distance(begin, end) > 0);
+ bool ret = true;
+
+ switch (*++begin) {
+ case 'a':
+ buff.push_back('\a');
+ break;
+ case 'b':
+ buff.push_back('\b');
+ break;
+ case 't':
+ case '\t':
+ buff.push_back('\t');
+ break;
+ case 'n':
+ buff.push_back('\n');
+ break;
+ case 'v':
+ buff.push_back('\v');
+ break;
+ case 'f':
+ buff.push_back('\f');
+ break;
+ case 'r':
+ buff.push_back('\r');
+ break;
+ case 'e':
+ buff.push_back(static_cast<char>(0x1B));
+ break;
+ case ' ':
+ buff.push_back(' ');
+ break;
+ case '\"':
+ buff.push_back('\"');
+ break;
+ case '/':
+ buff.push_back('/');
+ break;
+ case '\\':
+ buff.push_back('\\');
+ break;
+ case 'N': // next line
+ unescape_escaped_unicode(0x85u, buff);
+ break;
+ case '_': // non-breaking space
+ unescape_escaped_unicode(0xA0u, buff);
+ break;
+ case 'L': // line separator
+ unescape_escaped_unicode(0x2028u, buff);
+ break;
+ case 'P': // paragraph separator
+ unescape_escaped_unicode(0x2029u, buff);
+ break;
+ case 'x': {
+ char32_t codepoint {0};
+ ret = extract_codepoint(begin, end, 1, codepoint);
+ if FK_YAML_LIKELY (ret) {
+ unescape_escaped_unicode(codepoint, buff);
+ }
+ break;
+ }
+ case 'u': {
+ char32_t codepoint {0};
+ ret = extract_codepoint(begin, end, 2, codepoint);
+ if FK_YAML_LIKELY (ret) {
+ unescape_escaped_unicode(codepoint, buff);
+ }
+ break;
+ }
+ case 'U': {
+ char32_t codepoint {0};
+ ret = extract_codepoint(begin, end, 4, codepoint);
+ if FK_YAML_LIKELY (ret) {
+ unescape_escaped_unicode(codepoint, buff);
+ }
+ break;
+ }
+ default:
+ // Unsupported escape sequence is found in a string token.
+ ret = false;
+ break;
+ }
+
+ return ret;
+ }
+
+ static ::std::string escape(const char* begin, const char* end, bool& is_escaped) {
+ ::std::string escaped {};
+ escaped.reserve(std::distance(begin, end));
+ for (; begin != end; ++begin) {
+ switch (*begin) {
+ case 0x01:
+ escaped += "\\u0001";
+ is_escaped = true;
+ break;
+ case 0x02:
+ escaped += "\\u0002";
+ is_escaped = true;
+ break;
+ case 0x03:
+ escaped += "\\u0003";
+ is_escaped = true;
+ break;
+ case 0x04:
+ escaped += "\\u0004";
+ is_escaped = true;
+ break;
+ case 0x05:
+ escaped += "\\u0005";
+ is_escaped = true;
+ break;
+ case 0x06:
+ escaped += "\\u0006";
+ is_escaped = true;
+ break;
+ case '\a':
+ escaped += "\\a";
+ is_escaped = true;
+ break;
+ case '\b':
+ escaped += "\\b";
+ is_escaped = true;
+ break;
+ case '\t':
+ escaped += "\\t";
+ is_escaped = true;
+ break;
+ case '\n':
+ escaped += "\\n";
+ is_escaped = true;
+ break;
+ case '\v':
+ escaped += "\\v";
+ is_escaped = true;
+ break;
+ case '\f':
+ escaped += "\\f";
+ is_escaped = true;
+ break;
+ case '\r':
+ escaped += "\\r";
+ is_escaped = true;
+ break;
+ case 0x0E:
+ escaped += "\\u000E";
+ is_escaped = true;
+ break;
+ case 0x0F:
+ escaped += "\\u000F";
+ is_escaped = true;
+ break;
+ case 0x10:
+ escaped += "\\u0010";
+ is_escaped = true;
+ break;
+ case 0x11:
+ escaped += "\\u0011";
+ is_escaped = true;
+ break;
+ case 0x12:
+ escaped += "\\u0012";
+ is_escaped = true;
+ break;
+ case 0x13:
+ escaped += "\\u0013";
+ is_escaped = true;
+ break;
+ case 0x14:
+ escaped += "\\u0014";
+ is_escaped = true;
+ break;
+ case 0x15:
+ escaped += "\\u0015";
+ is_escaped = true;
+ break;
+ case 0x16:
+ escaped += "\\u0016";
+ is_escaped = true;
+ break;
+ case 0x17:
+ escaped += "\\u0017";
+ is_escaped = true;
+ break;
+ case 0x18:
+ escaped += "\\u0018";
+ is_escaped = true;
+ break;
+ case 0x19:
+ escaped += "\\u0019";
+ is_escaped = true;
+ break;
+ case 0x1A:
+ escaped += "\\u001A";
+ is_escaped = true;
+ break;
+ case 0x1B:
+ escaped += "\\e";
+ is_escaped = true;
+ break;
+ case 0x1C:
+ escaped += "\\u001C";
+ is_escaped = true;
+ break;
+ case 0x1D:
+ escaped += "\\u001D";
+ is_escaped = true;
+ break;
+ case 0x1E:
+ escaped += "\\u001E";
+ is_escaped = true;
+ break;
+ case 0x1F:
+ escaped += "\\u001F";
+ is_escaped = true;
+ break;
+ case '\"':
+ escaped += "\\\"";
+ is_escaped = true;
+ break;
+ case '\\':
+ escaped += "\\\\";
+ is_escaped = true;
+ break;
+ default:
+ const std::ptrdiff_t diff = static_cast<int>(std::distance(begin, end));
+ if (diff > 1) {
+ if (*begin == static_cast<char>(0xC2u) && *(begin + 1) == static_cast<char>(0x85u)) {
+ escaped += "\\N";
+ std::advance(begin, 1);
+ is_escaped = true;
+ break;
+ }
+ if (*begin == static_cast<char>(0xC2u) && *(begin + 1) == static_cast<char>(0xA0u)) {
+ escaped += "\\_";
+ std::advance(begin, 1);
+ is_escaped = true;
+ break;
+ }
+
+ if (diff > 2) {
+ if (*begin == static_cast<char>(0xE2u) && *(begin + 1) == static_cast<char>(0x80u) &&
+ *(begin + 2) == static_cast<char>(0xA8u)) {
+ escaped += "\\L";
+ std::advance(begin, 2);
+ is_escaped = true;
+ break;
+ }
+ if (*begin == static_cast<char>(0xE2u) && *(begin + 1) == static_cast<char>(0x80u) &&
+ *(begin + 2) == static_cast<char>(0xA9u)) {
+ escaped += "\\P";
+ std::advance(begin, 2);
+ is_escaped = true;
+ break;
+ }
+ }
+ }
+ escaped += *begin;
+ break;
+ }
+ }
+ return escaped;
+ } // LCOV_EXCL_LINE
+
+private:
+ static bool convert_hexchar_to_byte(char source, uint8_t& byte) {
+ if ('0' <= source && source <= '9') {
+ // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+ byte = static_cast<uint8_t>(source - '0');
+ return true;
+ }
+
+ if ('A' <= source && source <= 'F') {
+ // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+ byte = static_cast<uint8_t>(source - 'A' + 10);
+ return true;
+ }
+
+ if ('a' <= source && source <= 'f') {
+ // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+ byte = static_cast<uint8_t>(source - 'a' + 10);
+ return true;
+ }
+
+ // The given character is not hexadecimal.
+ return false;
+ }
+
+ static bool extract_codepoint(const char*& begin, const char* end, int bytes_to_read, char32_t& codepoint) {
+ const bool has_enough_room = static_cast<int>(std::distance(begin, end)) >= (bytes_to_read - 1);
+ if (!has_enough_room) {
+ return false;
+ }
+
+ const int read_size = bytes_to_read * 2;
+ uint8_t byte {0};
+ codepoint = 0;
+
+ for (int i = read_size - 1; i >= 0; i--) {
+ const bool is_valid = convert_hexchar_to_byte(*++begin, byte);
+ if (!is_valid) {
+ return false;
+ }
+ // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+ codepoint |= static_cast<char32_t>(byte << (4 * i));
+ }
+
+ return true;
+ }
+
+ static void unescape_escaped_unicode(char32_t codepoint, std::string& buff) {
+ // the inner curly braces are necessary to build with older compilers.
+ std::array<uint8_t, 4> encode_buff {{}};
+ uint32_t encoded_size {0};
+ utf8::from_utf32(codepoint, encode_buff, encoded_size);
+ buff.append(reinterpret_cast<char*>(encode_buff.data()), encoded_size);
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_ENCODINGS_YAML_ESCAPER_HPP */
+
+// #include <fkYAML/detail/input/block_scalar_header.hpp>
+
+// #include <fkYAML/detail/input/scalar_scanner.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP
+#define FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP
+
+#include <cstring>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+// #include <fkYAML/node_type.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief The class which detects a scalar value type by scanning contents.
+class scalar_scanner {
+public:
+ /// @brief Detects a scalar value type by scanning the contents ranged by the given iterators.
+ /// @param begin The iterator to the first element of the scalar.
+ /// @param end The iterator to the past-the-end element of the scalar.
+ /// @return A detected scalar value type.
+ static node_type scan(const char* begin, const char* end) noexcept {
+ if (begin == end) {
+ return node_type::STRING;
+ }
+
+ const auto len = static_cast<uint32_t>(std::distance(begin, end));
+ if (len > 5) {
+ return scan_possible_number_token(begin, len);
+ }
+
+ const char* p_begin = &*begin;
+
+ switch (len) {
+ case 1:
+ if (*p_begin == '~') {
+ return node_type::NULL_OBJECT;
+ }
+ break;
+ case 4:
+ switch (*p_begin) {
+ case 'n':
+ // no possible case of begin a number otherwise.
+ return (std::strncmp(p_begin + 1, "ull", 3) == 0) ? node_type::NULL_OBJECT : node_type::STRING;
+ case 'N':
+ // no possible case of begin a number otherwise.
+ return ((std::strncmp(p_begin + 1, "ull", 3) == 0) || (std::strncmp(p_begin + 1, "ULL", 3) == 0))
+ ? node_type::NULL_OBJECT
+ : node_type::STRING;
+ case 't':
+ // no possible case of being a number otherwise.
+ return (std::strncmp(p_begin + 1, "rue", 3) == 0) ? node_type::BOOLEAN : node_type::STRING;
+ case 'T':
+ // no possible case of being a number otherwise.
+ return ((std::strncmp(p_begin + 1, "rue", 3) == 0) || (std::strncmp(p_begin + 1, "RUE", 3) == 0))
+ ? node_type::BOOLEAN
+ : node_type::STRING;
+ case '.': {
+ const char* p_from_second = p_begin + 1;
+ const bool is_inf_or_nan_scalar =
+ (std::strncmp(p_from_second, "inf", 3) == 0) || (std::strncmp(p_from_second, "Inf", 3) == 0) ||
+ (std::strncmp(p_from_second, "INF", 3) == 0) || (std::strncmp(p_from_second, "nan", 3) == 0) ||
+ (std::strncmp(p_from_second, "NaN", 3) == 0) || (std::strncmp(p_from_second, "NAN", 3) == 0);
+ if (is_inf_or_nan_scalar) {
+ return node_type::FLOAT;
+ }
+ // maybe a number.
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case 5:
+ switch (*p_begin) {
+ case 'f':
+ // no possible case of being a number otherwise.
+ return (std::strncmp(p_begin + 1, "alse", 4) == 0) ? node_type::BOOLEAN : node_type::STRING;
+ case 'F':
+ // no possible case of being a number otherwise.
+ return ((std::strncmp(p_begin + 1, "alse", 4) == 0) || (std::strncmp(p_begin + 1, "ALSE", 4) == 0))
+ ? node_type::BOOLEAN
+ : node_type::STRING;
+ case '+':
+ case '-':
+ if (*(p_begin + 1) == '.') {
+ const char* p_from_third = p_begin + 2;
+ const bool is_min_inf = (std::strncmp(p_from_third, "inf", 3) == 0) ||
+ (std::strncmp(p_from_third, "Inf", 3) == 0) ||
+ (std::strncmp(p_from_third, "INF", 3) == 0);
+ if (is_min_inf) {
+ return node_type::FLOAT;
+ }
+ }
+ // maybe a number.
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return scan_possible_number_token(begin, len);
+ }
+
+private:
+ /// @brief Detects a scalar value type from the contents (possibly an integer or a floating-point value).
+ /// @param itr The iterator to the first element of the scalar.
+ /// @param len The length of the scalar contents.
+ /// @return A detected scalar value type.
+ static node_type scan_possible_number_token(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ switch (*itr) {
+ case '-':
+ return (len > 1) ? scan_negative_number(++itr, --len) : node_type::STRING;
+ case '+':
+ return (len > 1) ? scan_decimal_number(++itr, --len) : node_type::STRING;
+ case '.':
+ // some integer(s) required after the decimal point as a floating point value.
+ return (len > 1) ? scan_after_decimal_point(++itr, --len) : node_type::STRING;
+ case '0':
+ return (len > 1) ? scan_after_zero_at_first(++itr, --len) : node_type::INTEGER;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return (len > 1) ? scan_decimal_number(++itr, --len) : node_type::INTEGER;
+ default:
+ return node_type::STRING;
+ }
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents right after the negative sign.
+ /// @param itr The iterator to the past-the-negative-sign element of the scalar.
+ /// @param len The length of the scalar contents left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_negative_number(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ if (is_digit(*itr)) {
+ return (len > 1) ? scan_decimal_number(++itr, --len) : node_type::INTEGER;
+ }
+
+ if (*itr == '.') {
+ // some integer(s) required after "-." as a floating point value.
+ return (len > 1) ? scan_after_decimal_point(++itr, --len) : node_type::STRING;
+ }
+
+ return node_type::STRING;
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents right after the beginning 0.
+ /// @param itr The iterator to the past-the-zero element of the scalar.
+ /// @param len The length of the scalar left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_after_zero_at_first(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ if (is_digit(*itr)) {
+ // a token consisting of the beginning '0' and some following numbers, e.g., `0123`, is not an integer
+ // according to https://yaml.org/spec/1.2.2/#10213-integer.
+ return node_type::STRING;
+ }
+
+ switch (*itr) {
+ case '.':
+ // 0 can be omitted after `0.`.
+ return (len > 1) ? scan_after_decimal_point(++itr, --len) : node_type::FLOAT;
+ case 'e':
+ case 'E':
+ // some integer(s) required after the exponent sign as a floating point value.
+ return (len > 1) ? scan_after_exponent(++itr, --len) : node_type::STRING;
+ case 'o':
+ return (len > 1) ? scan_octal_number(++itr, --len) : node_type::STRING;
+ case 'x':
+ return (len > 1) ? scan_hexadecimal_number(++itr, --len) : node_type::STRING;
+ default:
+ return node_type::STRING;
+ }
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents part starting with a decimal.
+ /// @param itr The iterator to the beginning decimal element of the scalar.
+ /// @param len The length of the scalar left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_decimal_number(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ if (is_digit(*itr)) {
+ return (len > 1) ? scan_decimal_number(++itr, --len) : node_type::INTEGER;
+ }
+
+ switch (*itr) {
+ case '.': {
+ // 0 can be omitted after the decimal point
+ return (len > 1) ? scan_after_decimal_point(++itr, --len) : node_type::FLOAT;
+ }
+ case 'e':
+ case 'E':
+ // some integer(s) required after the exponent
+ return (len > 1) ? scan_after_exponent(++itr, --len) : node_type::STRING;
+ default:
+ return node_type::STRING;
+ }
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents right after a decimal point.
+ /// @param itr The iterator to the past-the-decimal-point element of the scalar.
+ /// @param len The length of the scalar left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_after_decimal_point(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ for (uint32_t i = 0; i < len; i++) {
+ const char c = *itr++;
+
+ if (is_digit(c)) {
+ continue;
+ }
+
+ if (c == 'e' || c == 'E') {
+ if (i == len - 1) {
+ // some integer(s) required after the exponent
+ return node_type::STRING;
+ }
+ return scan_after_exponent(itr, len - i - 1);
+ }
+
+ return node_type::STRING;
+ }
+
+ return node_type::FLOAT;
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents right after the exponent prefix ("e" or "E").
+ /// @param itr The iterator to the past-the-exponent-prefix element of the scalar.
+ /// @param len The length of the scalar left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_after_exponent(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ const char c = *itr;
+ if (c == '+' || c == '-') {
+ if (len == 1) {
+ // some integer(s) required after the sign.
+ return node_type::STRING;
+ }
+ ++itr;
+ --len;
+ }
+
+ for (uint32_t i = 0; i < len; i++) {
+ if (!is_digit(*itr++)) {
+ return node_type::STRING;
+ }
+ }
+
+ return node_type::FLOAT;
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents assuming octal numbers.
+ /// @param itr The iterator to the octal-number element of the scalar.
+ /// @param len The length of the scalar left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_octal_number(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ switch (*itr) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ return (len > 1) ? scan_octal_number(++itr, --len) : node_type::INTEGER;
+ default:
+ return node_type::STRING;
+ }
+ }
+
+ /// @brief Detects a scalar value type by scanning the contents assuming hexadecimal numbers.
+ /// @param itr The iterator to the hexadecimal-number element of the scalar.
+ /// @param len The length of the scalar left unscanned.
+ /// @return A detected scalar value type.
+ static node_type scan_hexadecimal_number(const char* itr, uint32_t len) noexcept {
+ FK_YAML_ASSERT(len > 0);
+
+ if (is_xdigit(*itr)) {
+ return (len > 1) ? scan_hexadecimal_number(++itr, --len) : node_type::INTEGER;
+ }
+ return node_type::STRING;
+ }
+
+ /// @brief Check if the given character is a digit.
+ /// @note This function is needed to avoid assertion failures in `std::isdigit()` especially when compiled with
+ /// MSVC.
+ /// @param c A character to be checked.
+ /// @return true if the given character is a digit, false otherwise.
+ static bool is_digit(char c) {
+ return ('0' <= c && c <= '9');
+ }
+
+ /// @brief Check if the given character is a hex-digit.
+ /// @note This function is needed to avoid assertion failures in `std::isxdigit()` especially when compiled with
+ /// MSVC.
+ /// @param c A character to be checked.
+ /// @return true if the given character is a hex-digit, false otherwise.
+ static bool is_xdigit(char c) {
+ return (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'));
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_SCALAR_SCANNER_HPP */
+
+// #include <fkYAML/detail/input/tag_t.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_TAG_T_HPP
+#define FK_YAML_DETAIL_INPUT_TAG_T_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Definition of YAML tag types.
+enum class tag_t : std::uint8_t {
+ NONE, //!< Represents a non-specific tag "?".
+ NON_SPECIFIC, //!< Represents a non-specific tag "!".
+ CUSTOM_TAG, //!< Represents a custom tag
+ SEQUENCE, //!< Represents a sequence tag.
+ MAPPING, //!< Represents a mapping tag.
+ NULL_VALUE, //!< Represents a null value tag.
+ BOOLEAN, //!< Represents a boolean tag.
+ INTEGER, //!< Represents an integer type
+ FLOATING_NUMBER, //!< Represents a floating point number tag.
+ STRING, //!< Represents a string tag.
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_TAG_T_HPP */
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/str_view.hpp>
+
+// #include <fkYAML/detail/types/lexical_token_t.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+// #include <fkYAML/node_type.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A parser for YAML scalars.
+/// @tparam BasicNodeType A type of the container for parsed YAML scalars.
+template <typename BasicNodeType>
+class scalar_parser {
+ static_assert(is_basic_node<BasicNodeType>::value, "scalar_parser only accepts basic_node<...>");
+
+public:
+ using basic_node_type = BasicNodeType;
+
+private:
+ /** A type for boolean node values. */
+ using boolean_type = typename basic_node_type::boolean_type;
+ /** A type for integer node values. */
+ using integer_type = typename basic_node_type::integer_type;
+ /** A type for floating point node values. */
+ using float_number_type = typename basic_node_type::float_number_type;
+ /** A type for string node values. */
+ using string_type = typename basic_node_type::string_type;
+
+public:
+ /// @brief Constructs a new scalar_parser object.
+ /// @param line Current line.
+ /// @param indent Current indentation.
+ scalar_parser(uint32_t line, uint32_t indent) noexcept
+ : m_line(line),
+ m_indent(indent) {
+ }
+
+ /// @brief Destroys a scalar_parser object.
+ ~scalar_parser() noexcept = default;
+
+ // std::string's copy constructor/assignment operator may throw a exception.
+ scalar_parser(const scalar_parser&) = default;
+ scalar_parser& operator=(const scalar_parser&) = default;
+
+ scalar_parser(scalar_parser&&) noexcept = default;
+ scalar_parser& operator=(scalar_parser&&) noexcept(std::is_nothrow_move_assignable<std::string>::value) = default;
+
+ /// @brief Parses a token into a flow scalar (either plain, single quoted or double quoted)
+ /// @param lex_type Lexical token type for the scalar.
+ /// @param tag_type Tag type for the scalar.
+ /// @param token Scalar contents.
+ /// @return Parsed YAML flow scalar object.
+ basic_node_type parse_flow(lexical_token_t lex_type, tag_t tag_type, str_view token) {
+ FK_YAML_ASSERT(
+ lex_type == lexical_token_t::PLAIN_SCALAR || lex_type == lexical_token_t::SINGLE_QUOTED_SCALAR ||
+ lex_type == lexical_token_t::DOUBLE_QUOTED_SCALAR);
+ FK_YAML_ASSERT(tag_type != tag_t::SEQUENCE && tag_type != tag_t::MAPPING);
+
+ token = parse_flow_scalar_token(lex_type, token);
+ const node_type value_type = decide_value_type(lex_type, tag_type, token);
+ return create_scalar_node(value_type, tag_type, token);
+ }
+
+ /// @brief Parses a token into a block scalar (either literal or folded)
+ /// @param lex_type Lexical token type for the scalar.
+ /// @param tag_type Tag type for the scalar.
+ /// @param token Scalar contents.
+ /// @param header Block scalar header information.
+ /// @return Parsed YAML block scalar object.
+ basic_node_type parse_block(
+ lexical_token_t lex_type, tag_t tag_type, str_view token, const block_scalar_header& header) {
+ FK_YAML_ASSERT(
+ lex_type == lexical_token_t::BLOCK_LITERAL_SCALAR || lex_type == lexical_token_t::BLOCK_FOLDED_SCALAR);
+ FK_YAML_ASSERT(tag_type != tag_t::SEQUENCE && tag_type != tag_t::MAPPING);
+
+ if (lex_type == lexical_token_t::BLOCK_LITERAL_SCALAR) {
+ token = parse_block_literal_scalar(token, header);
+ }
+ else {
+ token = parse_block_folded_scalar(token, header);
+ }
+
+ const node_type value_type = decide_value_type(lex_type, tag_type, token);
+ return create_scalar_node(value_type, tag_type, token);
+ }
+
+private:
+ /// @brief Parses a token into a flow scalar contents.
+ /// @param lex_type Lexical token type for the scalar.
+ /// @param token Scalar contents.
+ /// @return View into the parsed scalar contents.
+ str_view parse_flow_scalar_token(lexical_token_t lex_type, str_view token) {
+ switch (lex_type) {
+ case lexical_token_t::PLAIN_SCALAR:
+ token = parse_plain_scalar(token);
+ break;
+ case lexical_token_t::SINGLE_QUOTED_SCALAR:
+ token = parse_single_quoted_scalar(token);
+ break;
+ case lexical_token_t::DOUBLE_QUOTED_SCALAR:
+ token = parse_double_quoted_scalar(token);
+ break;
+ default: // LCOV_EXCL_LINE
+ unreachable(); // LCOV_EXCL_LINE
+ }
+
+ return token;
+ }
+
+ /// @brief Parses plain scalar contents.
+ /// @param token Scalar contents.
+ /// @return View into the parsed scalar contents.
+ str_view parse_plain_scalar(str_view token) noexcept {
+ // plain scalars cannot be empty.
+ FK_YAML_ASSERT(!token.empty());
+
+ std::size_t newline_pos = token.find('\n');
+ if (newline_pos == str_view::npos) {
+ return token;
+ }
+
+ m_use_owned_buffer = true;
+
+ if (m_buffer.capacity() < token.size()) {
+ m_buffer.reserve(token.size());
+ }
+
+ do {
+ process_line_folding(token, newline_pos);
+ newline_pos = token.find('\n');
+ } while (newline_pos != str_view::npos);
+
+ m_buffer.append(token.begin(), token.size());
+
+ return {m_buffer};
+ }
+
+ /// @brief Parses single quoted scalar contents.
+ /// @param token Scalar contents.
+ /// @return View into the parsed scalar contents.
+ str_view parse_single_quoted_scalar(str_view token) noexcept {
+ if (token.empty()) {
+ return token;
+ }
+
+ constexpr str_view filter {"\'\n"};
+ std::size_t pos = token.find_first_of(filter);
+ if (pos == str_view::npos) {
+ return token;
+ }
+
+ m_use_owned_buffer = true;
+
+ if (m_buffer.capacity() < token.size()) {
+ m_buffer.reserve(token.size());
+ }
+
+ do {
+ FK_YAML_ASSERT(pos < token.size());
+ FK_YAML_ASSERT(token[pos] == '\'' || token[pos] == '\n');
+
+ if (token[pos] == '\'') {
+ // unescape escaped single quote. ('' -> ')
+ FK_YAML_ASSERT(pos + 1 < token.size());
+ m_buffer.append(token.begin(), token.begin() + (pos + 1));
+ token.remove_prefix(pos + 2); // move next to the escaped single quote.
+ }
+ else {
+ process_line_folding(token, pos);
+ }
+
+ pos = token.find_first_of(filter);
+ } while (pos != str_view::npos);
+
+ if (!token.empty()) {
+ m_buffer.append(token.begin(), token.size());
+ }
+
+ return {m_buffer};
+ }
+
+ /// @brief Parses double quoted scalar contents.
+ /// @param token Scalar contents.
+ /// @return View into the parsed scalar contents.
+ str_view parse_double_quoted_scalar(str_view token) {
+ if (token.empty()) {
+ return token;
+ }
+
+ constexpr str_view filter {"\\\n"};
+ std::size_t pos = token.find_first_of(filter);
+ if (pos == str_view::npos) {
+ return token;
+ }
+
+ m_use_owned_buffer = true;
+
+ if (m_buffer.capacity() < token.size()) {
+ m_buffer.reserve(token.size());
+ }
+
+ do {
+ FK_YAML_ASSERT(pos < token.size());
+ FK_YAML_ASSERT(token[pos] == '\\' || token[pos] == '\n');
+
+ if (token[pos] == '\\') {
+ FK_YAML_ASSERT(pos + 1 < token.size());
+ m_buffer.append(token.begin(), token.begin() + pos);
+
+ if (token[pos + 1] != '\n') {
+ token.remove_prefix(pos);
+ const char* p_escape_begin = token.begin();
+ const bool is_valid_escaping = yaml_escaper::unescape(p_escape_begin, token.end(), m_buffer);
+ if FK_YAML_UNLIKELY (!is_valid_escaping) {
+ throw parse_error(
+ "Unsupported escape sequence is found in a double quoted scalar.", m_line, m_indent);
+ }
+
+ // `p_escape_begin` points to the last element of the escape sequence.
+ token.remove_prefix((p_escape_begin - token.begin()) + 1);
+ }
+ else {
+ std::size_t non_space_pos = token.find_first_not_of(" \t", pos + 2);
+ if (non_space_pos == str_view::npos) {
+ non_space_pos = token.size();
+ }
+ token.remove_prefix(non_space_pos);
+ }
+ }
+ else {
+ process_line_folding(token, pos);
+ }
+
+ pos = token.find_first_of(filter);
+ } while (pos != str_view::npos);
+
+ if (!token.empty()) {
+ m_buffer.append(token.begin(), token.size());
+ }
+
+ return {m_buffer};
+ }
+
+ /// @brief Parses block literal scalar contents.
+ /// @param token Scalar contents.
+ /// @param header Block scalar header information.
+ /// @return View into the parsed scalar contents.
+ str_view parse_block_literal_scalar(str_view token, const block_scalar_header& header) {
+ if FK_YAML_UNLIKELY (token.empty()) {
+ return token;
+ }
+
+ m_use_owned_buffer = true;
+ m_buffer.reserve(token.size());
+
+ std::size_t cur_line_begin_pos = 0;
+ do {
+ bool has_newline_at_end = true;
+ std::size_t cur_line_end_pos = token.find('\n', cur_line_begin_pos);
+ if (cur_line_end_pos == str_view::npos) {
+ has_newline_at_end = false;
+ cur_line_end_pos = token.size();
+ }
+
+ const std::size_t line_size = cur_line_end_pos - cur_line_begin_pos;
+ const str_view line = token.substr(cur_line_begin_pos, line_size);
+
+ if (line.size() > header.indent) {
+ m_buffer.append(line.begin() + header.indent, line.end());
+ }
+
+ if (!has_newline_at_end) {
+ break;
+ }
+
+ m_buffer.push_back('\n');
+ cur_line_begin_pos = cur_line_end_pos + 1;
+ } while (cur_line_begin_pos < token.size());
+
+ process_chomping(header.chomp);
+
+ return {m_buffer};
+ }
+
+ /// @brief Parses block folded scalar contents.
+ /// @param token Scalar contents.
+ /// @param header Block scalar header information.
+ /// @return View into the parsed scalar contents.
+ str_view parse_block_folded_scalar(str_view token, const block_scalar_header& header) {
+ if FK_YAML_UNLIKELY (token.empty()) {
+ return token;
+ }
+
+ m_use_owned_buffer = true;
+ m_buffer.reserve(token.size());
+
+ constexpr str_view white_space_filter {" \t"};
+
+ std::size_t cur_line_begin_pos = 0;
+ bool has_newline_at_end = true;
+ bool can_be_folded = false;
+ do {
+ std::size_t cur_line_end_pos = token.find('\n', cur_line_begin_pos);
+ if (cur_line_end_pos == str_view::npos) {
+ has_newline_at_end = false;
+ cur_line_end_pos = token.size();
+ }
+
+ const std::size_t line_size = cur_line_end_pos - cur_line_begin_pos;
+ const str_view line = token.substr(cur_line_begin_pos, line_size);
+ const bool is_empty = line.find_first_not_of(white_space_filter) == str_view::npos;
+
+ if (line.size() <= header.indent) {
+ // A less-indented line is turned into a newline.
+ m_buffer.push_back('\n');
+ can_be_folded = false;
+ }
+ else if (is_empty) {
+ // more-indented empty lines are not folded.
+ m_buffer.push_back('\n');
+ m_buffer.append(line.begin() + header.indent, line.end());
+ m_buffer.push_back('\n');
+ }
+ else {
+ const std::size_t non_space_pos = line.find_first_not_of(white_space_filter);
+ const bool is_more_indented = (non_space_pos != str_view::npos) && (non_space_pos > header.indent);
+
+ if (can_be_folded) {
+ if (is_more_indented) {
+ // The content line right before more-indented lines is not folded.
+ m_buffer.push_back('\n');
+ }
+ else {
+ m_buffer.push_back(' ');
+ }
+
+ can_be_folded = false;
+ }
+
+ m_buffer.append(line.begin() + header.indent, line.end());
+
+ if (is_more_indented && has_newline_at_end) {
+ // more-indented lines are not folded.
+ m_buffer.push_back('\n');
+ }
+ else {
+ can_be_folded = true;
+ }
+ }
+
+ if (!has_newline_at_end) {
+ break;
+ }
+
+ cur_line_begin_pos = cur_line_end_pos + 1;
+ } while (cur_line_begin_pos < token.size());
+
+ if (has_newline_at_end && can_be_folded) {
+ // The final content line break are not folded.
+ m_buffer.push_back('\n');
+ }
+
+ process_chomping(header.chomp);
+
+ return {m_buffer};
+ }
+
+ /// @brief Discards final content line break and trailing empty lines depending on the given chomping type.
+ /// @param chomp Chomping method type.
+ void process_chomping(chomping_indicator_t chomp) {
+ switch (chomp) {
+ case chomping_indicator_t::STRIP: {
+ const std::size_t content_end_pos = m_buffer.find_last_not_of('\n');
+ if (content_end_pos == std::string::npos) {
+ // if the scalar has no content line, all lines are considered as trailing empty lines.
+ m_buffer.clear();
+ break;
+ }
+
+ if (content_end_pos == m_buffer.size() - 1) {
+ // no last content line break nor trailing empty lines.
+ break;
+ }
+
+ // remove the last content line break and all trailing empty lines.
+ m_buffer.erase(content_end_pos + 1);
+
+ break;
+ }
+ case chomping_indicator_t::CLIP: {
+ const std::size_t content_end_pos = m_buffer.find_last_not_of('\n');
+ if (content_end_pos == std::string::npos) {
+ // if the scalar has no content line, all lines are considered as trailing empty lines.
+ m_buffer.clear();
+ break;
+ }
+
+ if (content_end_pos == m_buffer.size() - 1) {
+ // no trailing empty lines
+ break;
+ }
+
+ // remove all trailing empty lines.
+ m_buffer.erase(content_end_pos + 2);
+
+ break;
+ }
+ case chomping_indicator_t::KEEP:
+ break;
+ }
+ }
+
+ /// @brief Applies line folding to flow scalar contents.
+ /// @param token Flow scalar contents.
+ /// @param newline_pos Position of the target newline code.
+ void process_line_folding(str_view& token, std::size_t newline_pos) noexcept {
+ // discard trailing white spaces which precedes the line break in the current line.
+ const std::size_t last_non_space_pos = token.substr(0, newline_pos + 1).find_last_not_of(" \t");
+ if (last_non_space_pos == str_view::npos) {
+ m_buffer.append(token.begin(), newline_pos);
+ }
+ else {
+ m_buffer.append(token.begin(), last_non_space_pos + 1);
+ }
+ token.remove_prefix(newline_pos + 1); // move next to the LF
+
+ uint32_t empty_line_counts = 0;
+ do {
+ const std::size_t non_space_pos = token.find_first_not_of(" \t");
+ if (non_space_pos == str_view::npos) {
+ // Line folding ignores trailing spaces.
+ token.remove_prefix(token.size());
+ break;
+ }
+ if (token[non_space_pos] != '\n') {
+ token.remove_prefix(non_space_pos);
+ break;
+ }
+
+ token.remove_prefix(non_space_pos + 1);
+ ++empty_line_counts;
+ } while (true);
+
+ if (empty_line_counts > 0) {
+ m_buffer.append(empty_line_counts, '\n');
+ }
+ else {
+ m_buffer.push_back(' ');
+ }
+ }
+
+ /// @brief Decides scalar value type based on the lexical/tag types and scalar contents.
+ /// @param lex_type Lexical token type for the scalar.
+ /// @param tag_type Tag type for the scalar.
+ /// @param token Scalar contents.
+ /// @return Scalar value type.
+ node_type decide_value_type(lexical_token_t lex_type, tag_t tag_type, str_view token) const noexcept {
+ node_type value_type {node_type::STRING};
+ if (lex_type == lexical_token_t::PLAIN_SCALAR) {
+ value_type = scalar_scanner::scan(token.begin(), token.end());
+ }
+
+ switch (tag_type) {
+ case tag_t::NULL_VALUE:
+ value_type = node_type::NULL_OBJECT;
+ break;
+ case tag_t::BOOLEAN:
+ value_type = node_type::BOOLEAN;
+ break;
+ case tag_t::INTEGER:
+ value_type = node_type::INTEGER;
+ break;
+ case tag_t::FLOATING_NUMBER:
+ value_type = node_type::FLOAT;
+ break;
+ case tag_t::STRING:
+ case tag_t::NON_SPECIFIC:
+ // scalars with the non-specific tag is resolved to a string tag.
+ // See the "Non-Specific Tags" section in https://yaml.org/spec/1.2.2/#691-node-tags.
+ value_type = node_type::STRING;
+ break;
+ case tag_t::NONE:
+ case tag_t::CUSTOM_TAG:
+ default:
+ break;
+ }
+
+ return value_type;
+ }
+
+ /// @brief Creates YAML scalar object based on the value type and contents.
+ /// @param type Scalar value type.
+ /// @param token Scalar contents.
+ /// @return A YAML scalar object.
+ basic_node_type create_scalar_node(node_type val_type, tag_t tag_type, str_view token) {
+ switch (val_type) {
+ case node_type::NULL_OBJECT: {
+ std::nullptr_t null = nullptr;
+ const bool converted = detail::aton(token.begin(), token.end(), null);
+ if FK_YAML_UNLIKELY (!converted) {
+ throw parse_error("Failed to convert a scalar to a null.", m_line, m_indent);
+ }
+ // The default basic_node object is a null scalar node.
+ return basic_node_type {};
+ }
+ case node_type::BOOLEAN: {
+ auto boolean = static_cast<boolean_type>(false);
+ const bool converted = detail::atob(token.begin(), token.end(), boolean);
+ if FK_YAML_UNLIKELY (!converted) {
+ throw parse_error("Failed to convert a scalar to a boolean.", m_line, m_indent);
+ }
+ return basic_node_type(boolean);
+ }
+ case node_type::INTEGER: {
+ integer_type integer = 0;
+ const bool converted = detail::atoi(token.begin(), token.end(), integer);
+ if FK_YAML_LIKELY (converted) {
+ return basic_node_type(integer);
+ }
+ if FK_YAML_UNLIKELY (tag_type == tag_t::INTEGER) {
+ throw parse_error("Failed to convert a scalar to an integer.", m_line, m_indent);
+ }
+
+ // conversion error from a scalar which is not tagged with !!int is recovered by treating it as a string
+ // scalar. See https://github.com/fktn-k/fkYAML/issues/428.
+ return basic_node_type(string_type(token.begin(), token.end()));
+ }
+ case node_type::FLOAT: {
+ float_number_type float_val = 0;
+ const bool converted = detail::atof(token.begin(), token.end(), float_val);
+ if FK_YAML_LIKELY (converted) {
+ return basic_node_type(float_val);
+ }
+ if FK_YAML_UNLIKELY (tag_type == tag_t::FLOATING_NUMBER) {
+ throw parse_error("Failed to convert a scalar to a floating point value", m_line, m_indent);
+ }
+
+ // conversion error from a scalar which is not tagged with !!float is recovered by treating it as a string
+ // scalar. See https://github.com/fktn-k/fkYAML/issues/428.
+ return basic_node_type(string_type(token.begin(), token.end()));
+ }
+ case node_type::STRING:
+ if (!m_use_owned_buffer) {
+ return basic_node_type(string_type(token.begin(), token.end()));
+ }
+ m_use_owned_buffer = false;
+ return basic_node_type(std::move(m_buffer));
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+ /// Current line
+ uint32_t m_line {0};
+ /// Current indentation for the scalar
+ uint32_t m_indent {0};
+ /// Whether the parsed contents are stored in an owned buffer.
+ bool m_use_owned_buffer {false};
+ /// Owned buffer storage for parsing. This buffer is used when scalar contents need mutation.
+ std::string m_buffer;
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_SCALAR_PARSER_HPP */
+
+// #include <fkYAML/detail/input/tag_resolver.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_TAG_RESOLVER_HPP
+#define FK_YAML_DETAIL_INPUT_TAG_RESOLVER_HPP
+
+#include <memory>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+// #include <fkYAML/detail/document_metainfo.hpp>
+
+// #include <fkYAML/detail/input/tag_t.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/str_view.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+static constexpr str_view default_primary_handle_prefix {"!"};
+static constexpr str_view default_secondary_handle_prefix {"tag:yaml.org,2002:"};
+
+template <typename BasicNodeType>
+class tag_resolver {
+ static_assert(is_basic_node<BasicNodeType>::value, "tag_resolver only accepts basic_node<...>.");
+ using doc_metainfo_type = document_metainfo<BasicNodeType>;
+
+public:
+ /// @brief Resolve the input tag name into an expanded tag name prepended with a registered prefix.
+ /// @param tag The input tag name.
+ /// @return The type of a node deduced from the given tag name.
+ static tag_t resolve_tag(const str_view tag, const std::shared_ptr<doc_metainfo_type>& directives) {
+ const std::string normalized = normalize_tag_name(tag, directives);
+ return convert_to_tag_type(normalized);
+ }
+
+private:
+ static std::string normalize_tag_name(const str_view tag, const std::shared_ptr<doc_metainfo_type>& directives) {
+ if FK_YAML_UNLIKELY (tag.empty()) {
+ throw invalid_tag("tag must not be empty.", "");
+ }
+ if FK_YAML_UNLIKELY (tag[0] != '!') {
+ throw invalid_tag("tag must start with \'!\'", std::string(tag.begin(), tag.end()).c_str());
+ }
+
+ if (tag.size() == 1) {
+ // Non-specific tag ("!") will be interpreted as one of the following:
+ // * tag:yaml.org,2002:seq
+ // * tag:yaml.org,2002:map
+ // * tag:yaml.org,2002:str
+ // See the "Non-Specific Tags" section in https://yaml.org/spec/1.2.2/#691-node-tags.
+ // The interpretation cannot take place here because the input lacks the corresponding value.
+ return {tag.begin(), tag.end()};
+ }
+
+ std::string normalized {"!<"};
+ switch (tag[1]) {
+ case '!': {
+ // handle a secondary tag handle (!!suffix -> !<[secondary][suffix]>)
+ const bool is_null_or_empty = !directives || directives->secondary_handle_prefix.empty();
+ if (is_null_or_empty) {
+ normalized.append(default_secondary_handle_prefix.begin(), default_secondary_handle_prefix.end());
+ }
+ else {
+ normalized += directives->secondary_handle_prefix;
+ }
+
+ const str_view body = tag.substr(2);
+ normalized.append(body.begin(), body.end());
+ break;
+ }
+ case '<':
+ if (tag[2] == '!') {
+ const bool is_null_or_empty = !directives || directives->primary_handle_prefix.empty();
+ if (is_null_or_empty) {
+ normalized.append(default_primary_handle_prefix.begin(), default_primary_handle_prefix.end());
+ }
+ else {
+ normalized += directives->primary_handle_prefix;
+ }
+
+ const str_view body = tag.substr(3);
+ return normalized.append(body.begin(), body.end());
+ }
+
+ // verbatim tags must be delivered as-is to the application.
+ // See https://yaml.org/spec/1.2.2/#691-node-tags for more details.
+ return {tag.begin(), tag.end()};
+ default: {
+ const std::size_t tag_end_pos = tag.find_first_of('!', 1);
+
+ // handle a named handle (!tag!suffix -> !<[tag][suffix]>)
+ if (tag_end_pos != std::string::npos) {
+ // there must be a non-empty suffix. (already checked by the lexer.)
+ FK_YAML_ASSERT(tag_end_pos < tag.size() - 1);
+
+ const bool is_null_or_empty = !directives || directives->named_handle_map.empty();
+ if FK_YAML_UNLIKELY (is_null_or_empty) {
+ throw invalid_tag(
+ "named handle has not been registered.", std::string(tag.begin(), tag.end()).c_str());
+ }
+
+ // find the extracted named handle in the map.
+ const str_view named_handle = tag.substr(0, tag_end_pos + 1);
+ auto named_handle_itr = directives->named_handle_map.find({named_handle.begin(), named_handle.end()});
+ auto end_itr = directives->named_handle_map.end();
+ if FK_YAML_UNLIKELY (named_handle_itr == end_itr) {
+ throw invalid_tag(
+ "named handle has not been registered.", std::string(tag.begin(), tag.end()).c_str());
+ }
+
+ // The YAML spec prohibits expanding the percent-encoded characters (%xx -> a UTF-8 byte).
+ // So no conversion takes place.
+ // See https://yaml.org/spec/1.2.2/#56-miscellaneous-characters for more details.
+
+ normalized += named_handle_itr->second;
+ const str_view body = tag.substr(tag_end_pos + 1);
+ normalized.append(body.begin(), body.end());
+ break;
+ }
+
+ // handle a primary tag handle (!suffix -> !<[primary][suffix]>)
+ const bool is_null_or_empty = !directives || directives->primary_handle_prefix.empty();
+ if (is_null_or_empty) {
+ normalized.append(default_primary_handle_prefix.begin(), default_primary_handle_prefix.end());
+ }
+ else {
+ normalized += directives->primary_handle_prefix;
+ }
+
+ const str_view body = tag.substr(1);
+ normalized.append(body.begin(), body.end());
+ break;
+ }
+ }
+
+ normalized += ">";
+ return normalized;
+ }
+
+ static tag_t convert_to_tag_type(const std::string& normalized) {
+ if (normalized == "!") {
+ return tag_t::NON_SPECIFIC;
+ }
+
+ if (normalized.size() < 24 /* size of !<tag:yaml.org,2002:xxx */) {
+ return tag_t::CUSTOM_TAG;
+ }
+ if (normalized.rfind("!<tag:yaml.org,2002:", 0) == std::string::npos) {
+ return tag_t::CUSTOM_TAG;
+ }
+
+ if (normalized == "!<tag:yaml.org,2002:seq>") {
+ return tag_t::SEQUENCE;
+ }
+ if (normalized == "!<tag:yaml.org,2002:map>") {
+ return tag_t::MAPPING;
+ }
+ if (normalized == "!<tag:yaml.org,2002:null>") {
+ return tag_t::NULL_VALUE;
+ }
+ if (normalized == "!<tag:yaml.org,2002:bool>") {
+ return tag_t::BOOLEAN;
+ }
+ if (normalized == "!<tag:yaml.org,2002:int>") {
+ return tag_t::INTEGER;
+ }
+ if (normalized == "!<tag:yaml.org,2002:float>") {
+ return tag_t::FLOATING_NUMBER;
+ }
+ if (normalized == "!<tag:yaml.org,2002:str>") {
+ return tag_t::STRING;
+ }
+
+ return tag_t::CUSTOM_TAG;
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_TAG_RESOLVER_HPP */
+
+// #include <fkYAML/detail/meta/input_adapter_traits.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP
+#define FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP
+
+#include <type_traits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/detect.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+///////////////////////////////////////////
+// Input Adapter API detection traits
+///////////////////////////////////////////
+
+/// @brief A type which represents get_buffer_view function.
+/// @tparam T A target type.
+template <typename T>
+using get_buffer_view_fn_t = decltype(std::declval<T>().get_buffer_view());
+
+/// @brief Type traits to check if InputAdapterType has get_buffer_view member function.
+/// @tparam InputAdapterType An input adapter type to check if it has get_buffer_view function.
+/// @tparam typename N/A
+template <typename InputAdapterType>
+using has_get_buffer_view = is_detected<get_buffer_view_fn_t, InputAdapterType>;
+
+////////////////////////////////
+// is_input_adapter traits
+////////////////////////////////
+
+/// @brief Type traits to check if T is an input adapter type.
+/// @tparam T A target type.
+/// @tparam typename N/A
+template <typename T, typename = void>
+struct is_input_adapter : std::false_type {};
+
+/// @brief A partial specialization of is_input_adapter if T is an input adapter type.
+/// @tparam InputAdapterType
+template <typename InputAdapterType>
+struct is_input_adapter<InputAdapterType, enable_if_t<has_get_buffer_view<InputAdapterType>::value>> : std::true_type {
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_META_INPUT_ADAPTER_TRAITS_HPP */
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/node_attrs.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_NODE_ATTRS_HPP
+#define FK_YAML_DETAIL_NODE_ATTRS_HPP
+
+#include <cstdint>
+#include <limits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/node_type.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief The type for node attribute bits.
+using node_attr_t = uint32_t;
+
+/// @brief The namespace to define bit masks for node attribute bits.
+namespace node_attr_mask {
+
+/// The bit mask for node value type bits.
+constexpr node_attr_t value = 0x0000FFFFu;
+/// The bit mask for node style type bits. (bits are not yet defined.)
+constexpr node_attr_t style = 0x00FF0000u;
+/// The bit mask for node property related bits.
+constexpr node_attr_t props = 0xFF000000u;
+/// The bit mask for anchor/alias node type bits.
+constexpr node_attr_t anchoring = 0x03000000u;
+/// The bit mask for anchor offset value bits.
+constexpr node_attr_t anchor_offset = 0xFC000000u;
+/// The bit mask for all the bits for node attributes.
+constexpr node_attr_t all = std::numeric_limits<node_attr_t>::max();
+
+} // namespace node_attr_mask
+
+/// @brief The namespace to define bits for node attributes.
+namespace node_attr_bits {
+
+/// The sequence node bit.
+constexpr node_attr_t seq_bit = 1u << 0;
+/// The mapping node bit.
+constexpr node_attr_t map_bit = 1u << 1;
+/// The null scalar node bit.
+constexpr node_attr_t null_bit = 1u << 2;
+/// The boolean scalar node bit.
+constexpr node_attr_t bool_bit = 1u << 3;
+/// The integer scalar node bit.
+constexpr node_attr_t int_bit = 1u << 4;
+/// The floating point scalar node bit.
+constexpr node_attr_t float_bit = 1u << 5;
+/// The string scalar node bit.
+constexpr node_attr_t string_bit = 1u << 6;
+
+/// A utility bit set to filter scalar node bits.
+constexpr node_attr_t scalar_bits = null_bit | bool_bit | int_bit | float_bit | string_bit;
+
+/// The anchor node bit.
+constexpr node_attr_t anchor_bit = 0x01000000u;
+/// The alias node bit.
+constexpr node_attr_t alias_bit = 0x02000000u;
+
+/// A utility bit set for initialization.
+constexpr node_attr_t default_bits = null_bit;
+
+/// @brief Converts a node_type value to a node_attr_t value.
+/// @param t A type of node value.
+/// @return The associated node value bit.
+inline node_attr_t from_node_type(node_type t) noexcept {
+ switch (t) {
+ case node_type::SEQUENCE:
+ return seq_bit;
+ case node_type::MAPPING:
+ return map_bit;
+ case node_type::NULL_OBJECT:
+ return null_bit;
+ case node_type::BOOLEAN:
+ return bool_bit;
+ case node_type::INTEGER:
+ return int_bit;
+ case node_type::FLOAT:
+ return float_bit;
+ case node_type::STRING:
+ return string_bit;
+ default: // LCOV_EXCL_LINE
+ return node_attr_mask::all; // LCOV_EXCL_LINE
+ }
+}
+
+/// @brief Converts a node_attr_t value to a node_type value.
+/// @param bits node attribute bits
+/// @return An associated node value type with the given node value bit.
+inline node_type to_node_type(node_attr_t bits) noexcept {
+ switch (bits & node_attr_mask::value) {
+ case seq_bit:
+ return node_type::SEQUENCE;
+ case map_bit:
+ return node_type::MAPPING;
+ case null_bit:
+ return node_type::NULL_OBJECT;
+ case bool_bit:
+ return node_type::BOOLEAN;
+ case int_bit:
+ return node_type::INTEGER;
+ case float_bit:
+ return node_type::FLOAT;
+ case string_bit:
+ return node_type::STRING;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+}
+
+/// @brief Get an anchor offset used to reference an anchor node from the given attribute bits.
+/// @param attrs node attribute bits
+/// @return An anchor offset value.
+inline uint32_t get_anchor_offset(node_attr_t attrs) noexcept {
+ return (attrs & node_attr_mask::anchor_offset) >> 26;
+}
+
+/// @brief Set an anchor offset value to the appropriate bits.
+/// @param offset An anchor offset value.
+/// @param attrs node attribute bit set into which the offset value is written.
+inline void set_anchor_offset(uint32_t offset, node_attr_t& attrs) noexcept {
+ attrs &= ~node_attr_mask::anchor_offset;
+ attrs |= (offset & 0x3Fu) << 26;
+}
+
+} // namespace node_attr_bits
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_NODE_ATTRS_HPP */
+
+// #include <fkYAML/detail/node_property.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_NODE_PROPERTY_HPP
+#define FK_YAML_DETAIL_NODE_PROPERTY_HPP
+
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+struct node_property {
+ /// The tag name property.
+ std::string tag {}; // NOLINT(readability-redundant-member-init) necessary for older compilers
+ /// The anchor name property.
+ std::string anchor {}; // NOLINT(readability-redundant-member-init) necessary for older compilers
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_NODE_PROPERTY_HPP */
+
+// #include <fkYAML/detail/types/lexical_token_t.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A class which provides the feature of deserializing YAML documents.
+/// @tparam BasicNodeType A type of the container for deserialized YAML values.
+template <typename BasicNodeType>
+class basic_deserializer {
+ static_assert(is_basic_node<BasicNodeType>::value, "basic_deserializer only accepts basic_node<...>");
+
+ /** A type for the target basic_node. */
+ using basic_node_type = BasicNodeType;
+ /** A type for the lexical analyzer. */
+ using lexer_type = lexical_analyzer;
+ /** A type for the document metainfo. */
+ using doc_metainfo_type = document_metainfo<basic_node_type>;
+ /** A type for the tag resolver. */
+ using tag_resolver_type = tag_resolver<basic_node_type>;
+ /** A type for the scalar parser. */
+ using scalar_parser_type = scalar_parser<basic_node_type>;
+ /** A type for sequence node value containers. */
+ using sequence_type = typename basic_node_type::sequence_type;
+ /** A type for mapping node value containers. */
+ using mapping_type = typename basic_node_type::mapping_type;
+
+ /// @brief Definition of state types of parse contexts.
+ enum class context_state_t : std::uint8_t {
+ BLOCK_MAPPING, //!< The underlying node is a block mapping.
+ BLOCK_MAPPING_EXPLICIT_KEY, //!< The underlying node is an explicit block mapping key.
+ BLOCK_MAPPING_EXPLICIT_VALUE, //!< The underlying node is an explicit block mapping value.
+ MAPPING_VALUE, //!< The underlying node is a block mapping value.
+ BLOCK_SEQUENCE, //!< The underlying node is a block sequence.
+ BLOCK_SEQUENCE_ENTRY, //!< The underlying node is a block sequence entry.
+ FLOW_SEQUENCE, //!< The underlying node is a flow sequence.
+ FLOW_SEQUENCE_KEY, //!< The underlying node is a flow sequence as a key.
+ FLOW_MAPPING, //!< The underlying node is a flow mapping.
+ FLOW_MAPPING_KEY, //!< The underlying node is a flow mapping as a key.
+ };
+
+ /// @brief Context information set for parsing.
+ struct parse_context {
+ /// @brief Construct a new parse_context object.
+ parse_context() = default;
+
+ /// @brief Construct a new parse_context object with non-default values for each parameter.
+ /// @param line The current line. (count from zero)
+ /// @param indent The indentation width in the current line. (count from zero)
+ /// @param state The parse context type.
+ /// @param p_node The underlying node associated to this context.
+ parse_context(uint32_t line, uint32_t indent, context_state_t state, basic_node_type* p_node) noexcept
+ : line(line),
+ indent(indent),
+ state(state),
+ p_node(p_node) {
+ }
+
+ parse_context(const parse_context&) noexcept = default;
+ parse_context& operator=(const parse_context&) noexcept = default;
+ parse_context(parse_context&&) noexcept = default;
+ parse_context& operator=(parse_context&&) noexcept = default;
+
+ ~parse_context() {
+ switch (state) {
+ case context_state_t::BLOCK_MAPPING_EXPLICIT_KEY:
+ case context_state_t::FLOW_SEQUENCE_KEY:
+ case context_state_t::FLOW_MAPPING_KEY:
+ delete p_node;
+ p_node = nullptr;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /// The current line. (count from zero)
+ uint32_t line {0};
+ /// The indentation width in the current line. (count from zero)
+ uint32_t indent {0};
+ /// The parse context type.
+ context_state_t state {context_state_t::BLOCK_MAPPING};
+ /// The pointer to the associated node to this context.
+ basic_node_type* p_node {nullptr};
+ };
+
+ /// @brief Definitions of state types for expected flow token hints.
+ enum class flow_token_state_t : std::uint8_t {
+ NEEDS_VALUE_OR_SUFFIX, //!< Either value or flow suffix (`]` or `}`)
+ NEEDS_SEPARATOR_OR_SUFFIX, //!< Either separator (`,`) or flow suffix (`]` or `}`)
+ };
+
+public:
+ /// @brief Construct a new basic_deserializer object.
+ basic_deserializer() = default;
+
+public:
+ /// @brief Deserialize a single YAML document into a YAML node.
+ /// @note
+ /// If the input consists of multiple YAML documents, this function only parses the first.
+ /// If the input may have multiple YAML documents all of which must be parsed into nodes,
+ /// prefer the `deserialize_docs()` function.
+ /// @tparam InputAdapterType The type of an input adapter object.
+ /// @param input_adapter An input adapter object for the input source buffer.
+ /// @return basic_node_type A root YAML node deserialized from the source string.
+ template <typename InputAdapterType, enable_if_t<is_input_adapter<InputAdapterType>::value, int> = 0>
+ basic_node_type deserialize(InputAdapterType&& input_adapter) { // NOLINT(cppcoreguidelines-missing-std-forward)
+ const str_view input_view = input_adapter.get_buffer_view();
+ lexer_type lexer(input_view);
+
+ lexical_token_t type {lexical_token_t::END_OF_BUFFER};
+ return deserialize_document(lexer, type);
+ }
+
+ /// @brief Deserialize multiple YAML documents into YAML nodes.
+ /// @tparam InputAdapterType The type of an adapter object.
+ /// @param input_adapter An input adapter object for the input source buffer.
+ /// @return std::vector<basic_node_type> Root YAML nodes for deserialized YAML documents.
+ template <typename InputAdapterType, enable_if_t<is_input_adapter<InputAdapterType>::value, int> = 0>
+ // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
+ std::vector<basic_node_type> deserialize_docs(InputAdapterType&& input_adapter) {
+ const str_view input_view = input_adapter.get_buffer_view();
+ lexer_type lexer(input_view);
+
+ std::vector<basic_node_type> nodes {};
+ lexical_token_t type {lexical_token_t::END_OF_BUFFER};
+
+ do {
+ nodes.emplace_back(deserialize_document(lexer, type));
+ } while (type != lexical_token_t::END_OF_BUFFER);
+
+ return nodes;
+ } // LCOV_EXCL_LINE
+
+private:
+ /// @brief Deserialize a YAML document into a YAML node.
+ /// @param lexer The lexical analyzer to be used.
+ /// @param last_type The variable to store the last lexical token type.
+ /// @return basic_node_type A root YAML node deserialized from the YAML document.
+ basic_node_type deserialize_document(lexer_type& lexer, lexical_token_t& last_type) {
+ lexical_token token {};
+
+ basic_node_type root;
+ mp_current_node = &root;
+ mp_meta = root.mp_meta;
+
+ // parse directives first.
+ deserialize_directives(lexer, token);
+
+ // parse node properties for root node if any
+ uint32_t line = lexer.get_lines_processed();
+ uint32_t indent = lexer.get_last_token_begin_pos();
+ const bool found_props = deserialize_node_properties(lexer, token, line, indent);
+
+ switch (token.type) {
+ case lexical_token_t::SEQUENCE_BLOCK_PREFIX: {
+ root = basic_node_type::sequence({basic_node_type()});
+ apply_directive_set(root);
+ if (found_props) {
+ // If node properties are found before the block sequence entry prefix, the properties belong to the
+ // root sequence node.
+ apply_node_properties(root);
+ }
+
+ parse_context context(
+ lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::BLOCK_SEQUENCE, &root);
+ m_context_stack.emplace_back(context);
+
+ mp_current_node = &(root.as_seq().back());
+ apply_directive_set(*mp_current_node);
+ context.state = context_state_t::BLOCK_SEQUENCE_ENTRY;
+ context.p_node = mp_current_node;
+ m_context_stack.emplace_back(std::move(context));
+
+ token = lexer.get_next_token();
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ break;
+ }
+ case lexical_token_t::SEQUENCE_FLOW_BEGIN:
+ ++m_flow_context_depth;
+ lexer.set_context_state(true);
+ root = basic_node_type::sequence();
+ apply_directive_set(root);
+ apply_node_properties(root);
+ m_context_stack.emplace_back(
+ lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::FLOW_SEQUENCE, &root);
+ token = lexer.get_next_token();
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ break;
+ case lexical_token_t::MAPPING_FLOW_BEGIN:
+ ++m_flow_context_depth;
+ lexer.set_context_state(true);
+ root = basic_node_type::mapping();
+ apply_directive_set(root);
+ apply_node_properties(root);
+ m_context_stack.emplace_back(
+ lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::FLOW_MAPPING, &root);
+ token = lexer.get_next_token();
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ break;
+ case lexical_token_t::EXPLICIT_KEY_PREFIX: {
+ // If the explicit key prefix (? ) is detected here, the root node of current document must be a mapping.
+ // Also, tag and anchor if any are associated to the root mapping node.
+ // No get_next_token() call here to handle the token event in the deserialize_node() function.
+ root = basic_node_type::mapping();
+ apply_directive_set(root);
+ apply_node_properties(root);
+ parse_context context(
+ lexer.get_lines_processed(), lexer.get_last_token_begin_pos(), context_state_t::BLOCK_MAPPING, &root);
+ m_context_stack.emplace_back(std::move(context));
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ break;
+ }
+ case lexical_token_t::BLOCK_LITERAL_SCALAR:
+ case lexical_token_t::BLOCK_FOLDED_SCALAR:
+ // If a block scalar token is detected here, current document contains single scalar.
+ // Do nothing here since the token is handled in the deserialize_node() function.
+ break;
+ case lexical_token_t::PLAIN_SCALAR:
+ case lexical_token_t::SINGLE_QUOTED_SCALAR:
+ case lexical_token_t::DOUBLE_QUOTED_SCALAR:
+ case lexical_token_t::ALIAS_PREFIX:
+ // Defer handling the above token events until the next call on the deserialize_scalar() function since the
+ // meaning depends on subsequent events.
+ if (found_props && line < lexer.get_lines_processed()) {
+ // If node properties and a followed node are on the different line, the properties belong to the root
+ // node.
+ if (m_needs_anchor_impl) {
+ m_root_anchor_name = m_anchor_name;
+ m_needs_anchor_impl = false;
+ m_anchor_name = {};
+ }
+
+ if (m_needs_tag_impl) {
+ m_root_tag_name = m_tag_name;
+ m_needs_tag_impl = false;
+ m_tag_name = {};
+ }
+
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ }
+ break;
+ default:
+ // Do nothing since current document has no contents.
+ break;
+ }
+
+ // parse YAML nodes recursively
+ deserialize_node(lexer, token, line, indent, last_type);
+ FK_YAML_ASSERT(
+ last_type == lexical_token_t::END_OF_BUFFER || last_type == lexical_token_t::END_OF_DIRECTIVES ||
+ last_type == lexical_token_t::END_OF_DOCUMENT);
+
+ // reset parameters for the next call.
+ mp_current_node = nullptr;
+ mp_meta.reset();
+ m_needs_tag_impl = false;
+ m_needs_anchor_impl = false;
+ m_flow_context_depth = 0;
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+ m_context_stack.clear();
+
+ return root;
+ }
+
+ /// @brief Deserializes the YAML directives if specified.
+ /// @param lexer The lexical analyzer to be used.
+ /// @param last_token Storage for last lexical token type.
+ void deserialize_directives(lexer_type& lexer, lexical_token& last_token) {
+ bool lacks_end_of_directives_marker = false;
+ lexer.set_document_state(true);
+
+ for (;;) {
+ const lexical_token token = lexer.get_next_token();
+
+ switch (token.type) {
+ case lexical_token_t::YAML_VER_DIRECTIVE:
+ if FK_YAML_UNLIKELY (mp_meta->is_version_specified) {
+ throw parse_error(
+ "YAML version cannot be specified more than once.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+
+ mp_meta->version = convert_yaml_version(lexer.get_yaml_version());
+ mp_meta->is_version_specified = true;
+ lacks_end_of_directives_marker = true;
+ break;
+ case lexical_token_t::TAG_DIRECTIVE: {
+ const str_view tag_handle_view = lexer.get_tag_handle();
+ switch (tag_handle_view.size()) {
+ case 1 /* ! */: {
+ const bool is_already_specified = !mp_meta->primary_handle_prefix.empty();
+ if FK_YAML_UNLIKELY (is_already_specified) {
+ throw parse_error(
+ "Primary handle cannot be specified more than once.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+ const str_view tag_prefix = lexer.get_tag_prefix();
+ mp_meta->primary_handle_prefix.assign(tag_prefix.begin(), tag_prefix.end());
+ lacks_end_of_directives_marker = true;
+ break;
+ }
+ case 2 /* !! */: {
+ const bool is_already_specified = !mp_meta->secondary_handle_prefix.empty();
+ if FK_YAML_UNLIKELY (is_already_specified) {
+ throw parse_error(
+ "Secondary handle cannot be specified more than once.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+ const str_view tag_prefix = lexer.get_tag_prefix();
+ mp_meta->secondary_handle_prefix.assign(tag_prefix.begin(), tag_prefix.end());
+ lacks_end_of_directives_marker = true;
+ break;
+ }
+ default /* !<handle>! */: {
+ std::string tag_handle(tag_handle_view.begin(), tag_handle_view.end());
+ const str_view tag_prefix_view = lexer.get_tag_prefix();
+ std::string tag_prefix(tag_prefix_view.begin(), tag_prefix_view.end());
+ const bool is_already_specified =
+ !(mp_meta->named_handle_map.emplace(std::move(tag_handle), std::move(tag_prefix)).second);
+ if FK_YAML_UNLIKELY (is_already_specified) {
+ throw parse_error(
+ "The same named handle cannot be specified more than once.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+ lacks_end_of_directives_marker = true;
+ break;
+ }
+ }
+ break;
+ }
+ case lexical_token_t::INVALID_DIRECTIVE:
+ // TODO: should output a warning log. Currently just ignore this case.
+ break;
+ case lexical_token_t::END_OF_DIRECTIVES:
+ lacks_end_of_directives_marker = false;
+ break;
+ default:
+ if FK_YAML_UNLIKELY (lacks_end_of_directives_marker) {
+ throw parse_error(
+ "The end of directives marker (---) is missing after directives.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+ // end the parsing of directives if the other tokens are found.
+ last_token = token;
+ lexer.set_document_state(false);
+ return;
+ }
+ }
+ }
+
+ /// @brief Deserializes the YAML nodes recursively.
+ /// @param lexer The lexical analyzer to be used.
+ /// @param first_type The first lexical token.
+ /// @param last_type Storage for last lexical token type.
+ void deserialize_node(
+ lexer_type& lexer, const lexical_token& first_token, uint32_t first_line, uint32_t first_indent,
+ lexical_token_t& last_type) {
+ lexical_token token = first_token;
+ uint32_t line = first_line;
+ uint32_t indent = first_indent;
+
+ do {
+ switch (token.type) {
+ case lexical_token_t::EXPLICIT_KEY_PREFIX: {
+ const bool needs_to_move_back = indent == 0 || indent < m_context_stack.back().indent;
+ if (needs_to_move_back) {
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ return c.state == context_state_t::BLOCK_MAPPING && indent == c.indent;
+ });
+ }
+
+ switch (m_context_stack.back().state) {
+ case context_state_t::MAPPING_VALUE:
+ case context_state_t::BLOCK_MAPPING_EXPLICIT_KEY:
+ case context_state_t::BLOCK_MAPPING_EXPLICIT_VALUE:
+ case context_state_t::BLOCK_SEQUENCE_ENTRY:
+ // This path is needed in case the input contains nested explicit keys.
+ // ```yaml
+ // foo:
+ // ? ? foo
+ // : bar
+ // : ? baz
+ // : - ? qux
+ // : 123
+ // ```
+ *mp_current_node = basic_node_type::mapping();
+ apply_directive_set(*mp_current_node);
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ break;
+ default:
+ break;
+ }
+
+ token = lexer.get_next_token();
+ if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) {
+ // heap-allocated node will be freed in handling the corresponding KEY_SEPARATOR event
+ auto* p_node = new basic_node_type(node_type::SEQUENCE);
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, p_node);
+
+ apply_directive_set(*p_node);
+ parse_context context(
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos(),
+ context_state_t::BLOCK_SEQUENCE,
+ p_node);
+ m_context_stack.emplace_back(context);
+
+ p_node->as_seq().emplace_back(basic_node_type());
+ mp_current_node = &(p_node->as_seq().back());
+ apply_directive_set(*mp_current_node);
+ context.state = context_state_t::BLOCK_SEQUENCE_ENTRY;
+ context.p_node = mp_current_node;
+ m_context_stack.emplace_back(std::move(context));
+
+ break;
+ }
+
+ // heap-allocated node will be freed in handling the corresponding KEY_SEPARATOR event
+ m_context_stack.emplace_back(
+ line, indent, context_state_t::BLOCK_MAPPING_EXPLICIT_KEY, new basic_node_type());
+ mp_current_node = m_context_stack.back().p_node;
+ apply_directive_set(*mp_current_node);
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+
+ continue;
+ }
+ case lexical_token_t::KEY_SEPARATOR: {
+ FK_YAML_ASSERT(!m_context_stack.empty());
+ if FK_YAML_UNLIKELY (m_context_stack.back().state == context_state_t::BLOCK_SEQUENCE_ENTRY) {
+ // empty mapping keys are not supported.
+ // ```yaml
+ // - : foo
+ // ```
+ throw parse_error("sequence key should not be empty.", line, indent);
+ }
+
+ if (m_flow_context_depth > 0) {
+ break;
+ }
+
+ // hold the line count of the key separator for later use.
+ const uint32_t old_indent = indent;
+ const uint32_t old_line = line;
+
+ token = lexer.get_next_token();
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+
+ const bool found_props = deserialize_node_properties(lexer, token, line, indent);
+ if (found_props && line == lexer.get_lines_processed()) {
+ // defer applying node properties for the subsequent node on the same line.
+ continue;
+ }
+
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+
+ const bool is_implicit_same_line =
+ (line == old_line) && (m_context_stack.empty() || old_indent > m_context_stack.back().indent);
+ if (is_implicit_same_line) {
+ // a key separator for an implicit key with its value on the same line.
+ continue;
+ }
+
+ if (line > old_line) {
+ if (m_needs_tag_impl) {
+ const tag_t tag_type = tag_resolver_type::resolve_tag(m_tag_name, mp_meta);
+ if (tag_type == tag_t::MAPPING || tag_type == tag_t::CUSTOM_TAG) {
+ // set YAML node properties here to distinguish them from those for the first key node
+ // as shown in the following snippet:
+ //
+ // ```yaml
+ // foo: !!map
+ // !!str 123: true
+ // ^
+ // this !!str tag overwrites the preceding !!map tag.
+ // ```
+ *mp_current_node = basic_node_type::mapping();
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ continue;
+ }
+ }
+
+ if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) {
+ // a key separator preceding block sequence entries
+ *mp_current_node = basic_node_type::sequence({basic_node_type()});
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+ auto& cur_context = m_context_stack.back();
+ cur_context.line = line;
+ cur_context.indent = indent;
+ cur_context.state = context_state_t::BLOCK_SEQUENCE;
+
+ mp_current_node = &(mp_current_node->as_seq().back());
+ apply_directive_set(*mp_current_node);
+ parse_context entry_context = cur_context;
+ entry_context.state = context_state_t::BLOCK_SEQUENCE_ENTRY;
+ entry_context.p_node = mp_current_node;
+ m_context_stack.emplace_back(std::move(entry_context));
+
+ token = lexer.get_next_token();
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+
+ const bool has_props = deserialize_node_properties(lexer, token, line, indent);
+ if (has_props) {
+ const uint32_t line_after_props = lexer.get_lines_processed();
+ if (line == line_after_props) {
+ // Skip updating the current indent to avoid stacking a wrong indentation.
+ //
+ // ```yaml
+ // &foo bar: baz
+ // ^
+ // the correct indent width for the "bar" node key.
+ // ```
+ continue;
+ }
+
+ // if node properties and the followed node are on different lines (i.e., the properties are
+ // for a container node), the application and the line advancement must happen here.
+ // Otherwise, a false indent error will be emitted. See
+ // https://github.com/fktn-k/fkYAML/issues/368 for more details.
+ line = line_after_props;
+ indent = lexer.get_last_token_begin_pos();
+ *mp_current_node = basic_node_type::mapping();
+ m_context_stack.emplace_back(
+ line_after_props, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+ }
+
+ continue;
+ }
+
+ if (indent <= m_context_stack.back().indent) {
+ FK_YAML_ASSERT(m_context_stack.back().state == context_state_t::MAPPING_VALUE);
+
+ // Mapping values can be omitted and are considered to be null.
+ // ```yaml
+ // foo:
+ // bar:
+ // baz:
+ // qux:
+ // # -> {foo: null, bar: {baz: null}, qux: null}
+ // ```
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ return (c.state == context_state_t::BLOCK_MAPPING) && (indent == c.indent);
+ });
+ }
+
+ // defer checking the existence of a key separator after the following scalar until the next
+ // deserialize_scalar() call.
+ continue;
+ }
+
+ // handle explicit mapping key separators.
+ FK_YAML_ASSERT(m_context_stack.back().state == context_state_t::BLOCK_MAPPING_EXPLICIT_KEY);
+
+ basic_node_type key_node = std::move(*m_context_stack.back().p_node);
+ m_context_stack.pop_back();
+ m_context_stack.back().p_node->as_map().emplace(key_node, basic_node_type());
+ mp_current_node = &(m_context_stack.back().p_node->operator[](std::move(key_node)));
+ m_context_stack.emplace_back(
+ old_line, old_indent, context_state_t::BLOCK_MAPPING_EXPLICIT_VALUE, mp_current_node);
+
+ if (token.type == lexical_token_t::SEQUENCE_BLOCK_PREFIX) {
+ *mp_current_node = basic_node_type::sequence({basic_node_type()});
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE, mp_current_node);
+
+ mp_current_node = &(mp_current_node->as_seq().back());
+ parse_context entry_context = m_context_stack.back();
+ entry_context.state = context_state_t::BLOCK_SEQUENCE_ENTRY;
+ entry_context.p_node = mp_current_node;
+ m_context_stack.emplace_back(std::move(entry_context));
+ break;
+ }
+
+ continue;
+ }
+ case lexical_token_t::ANCHOR_PREFIX:
+ case lexical_token_t::TAG_PREFIX:
+ deserialize_node_properties(lexer, token, line, indent);
+ // Skip updating the current indent to avoid stacking a wrong indentation.
+ // Note that node properties for block sequences as a mapping value are processed when a
+ // `lexical_token_t::KEY_SEPARATOR` token is processed.
+ //
+ // ```yaml
+ // &foo bar: baz
+ // ^
+ // the correct indent width for the "bar" node key.
+ // ```
+ continue;
+ case lexical_token_t::SEQUENCE_BLOCK_PREFIX: {
+ FK_YAML_ASSERT(!m_context_stack.empty());
+ const uint32_t parent_indent = m_context_stack.back().indent;
+ if (indent == parent_indent) {
+ // If the previous block sequence entry is empty, just move to the parent context.
+ // ```yaml
+ // foo:
+ // -
+ // - bar
+ // # ^ (here)
+ // # -> {foo: [null, bar]}
+ // ```
+ pop_to_parent_node(line, indent, [](const parse_context& c) {
+ return c.state == context_state_t::BLOCK_SEQUENCE;
+ });
+ }
+ else if (indent < parent_indent) {
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ return c.state == context_state_t::BLOCK_SEQUENCE && indent == c.indent;
+ });
+ }
+ else /*parent_indent < indent*/ {
+ if FK_YAML_UNLIKELY (m_context_stack.back().state == context_state_t::BLOCK_SEQUENCE) {
+ // bad indentation like the following YAML:
+ // ```yaml
+ // - "foo"
+ // - bar
+ // # ^
+ // ```
+ throw parse_error("bad indentation of a mapping entry.", line, indent);
+ }
+
+ *mp_current_node = basic_node_type::sequence();
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE, mp_current_node);
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+ }
+
+ auto& seq = mp_current_node->as_seq();
+ seq.emplace_back(basic_node_type());
+ mp_current_node = &(seq.back());
+ apply_directive_set(*mp_current_node);
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_SEQUENCE_ENTRY, mp_current_node);
+ break;
+ }
+ case lexical_token_t::SEQUENCE_FLOW_BEGIN:
+ if (m_flow_context_depth == 0) {
+ lexer.set_context_state(true);
+
+ if (indent <= m_context_stack.back().indent) {
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ switch (c.state) {
+ case context_state_t::BLOCK_MAPPING:
+ case context_state_t::MAPPING_VALUE:
+ return indent == c.indent;
+ default:
+ return false;
+ }
+ });
+ }
+ }
+ else if FK_YAML_UNLIKELY (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) {
+ throw parse_error("Flow sequence beginning is found without separated with a comma.", line, indent);
+ }
+
+ ++m_flow_context_depth;
+
+ switch (m_context_stack.back().state) {
+ case context_state_t::BLOCK_SEQUENCE:
+ case context_state_t::FLOW_SEQUENCE:
+ mp_current_node->as_seq().emplace_back(basic_node_type::sequence());
+ mp_current_node = &(mp_current_node->as_seq().back());
+ m_context_stack.emplace_back(line, indent, context_state_t::FLOW_SEQUENCE, mp_current_node);
+ break;
+ case context_state_t::BLOCK_MAPPING:
+ case context_state_t::FLOW_MAPPING:
+ // heap-allocated node will be freed in handling the corresponding SEQUENCE_FLOW_END event.
+ m_context_stack.emplace_back(
+ line, indent, context_state_t::FLOW_SEQUENCE_KEY, new basic_node_type(node_type::SEQUENCE));
+ mp_current_node = m_context_stack.back().p_node;
+ break;
+ default: {
+ *mp_current_node = basic_node_type::sequence();
+ parse_context& last_context = m_context_stack.back();
+ last_context.line = line;
+ last_context.indent = indent;
+ last_context.state = context_state_t::FLOW_SEQUENCE;
+ break;
+ }
+ }
+
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+ break;
+ case lexical_token_t::SEQUENCE_FLOW_END: {
+ if FK_YAML_UNLIKELY (m_flow_context_depth == 0) {
+ throw parse_error("Flow sequence ending is found outside the flow context.", line, indent);
+ }
+
+ if (--m_flow_context_depth == 0) {
+ lexer.set_context_state(false);
+ }
+
+ // find the corresponding flow sequence beginning.
+ auto itr = std::find_if( // LCOV_EXCL_LINE
+ m_context_stack.rbegin(),
+ m_context_stack.rend(),
+ [](const parse_context& c) {
+ switch (c.state) {
+ case context_state_t::FLOW_SEQUENCE_KEY:
+ case context_state_t::FLOW_SEQUENCE:
+ return true;
+ default:
+ return false;
+ }
+ });
+
+ const bool is_valid = itr != m_context_stack.rend();
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw parse_error("No corresponding flow sequence beginning is found.", line, indent);
+ }
+
+ // keep the last state for later processing.
+ parse_context& last_context = m_context_stack.back();
+ mp_current_node = last_context.p_node;
+ last_context.p_node = nullptr;
+ indent = last_context.indent;
+ const context_state_t state = last_context.state;
+ m_context_stack.pop_back();
+
+ // handle cases where the flow sequence is a mapping key node.
+
+ if (!m_context_stack.empty() && state == context_state_t::FLOW_SEQUENCE_KEY) {
+ basic_node_type key_node = std::move(*mp_current_node);
+ delete mp_current_node;
+ mp_current_node = m_context_stack.back().p_node;
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+
+ add_new_key(std::move(key_node), line, indent);
+ break;
+ }
+
+ token = lexer.get_next_token();
+ if (token.type == lexical_token_t::KEY_SEPARATOR) {
+ basic_node_type key_node = basic_node_type::mapping();
+ apply_directive_set(key_node);
+ mp_current_node->swap(key_node);
+
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+
+ add_new_key(std::move(key_node), line, indent);
+ }
+ else {
+ if (!m_context_stack.empty()) {
+ mp_current_node = m_context_stack.back().p_node;
+ }
+ if (m_flow_context_depth > 0) {
+ m_flow_token_state = flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX;
+ }
+ }
+
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+ continue;
+ }
+ case lexical_token_t::MAPPING_FLOW_BEGIN:
+ if (m_flow_context_depth == 0) {
+ lexer.set_context_state(true);
+
+ if (indent <= m_context_stack.back().indent) {
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ switch (c.state) {
+ case context_state_t::BLOCK_MAPPING:
+ case context_state_t::MAPPING_VALUE:
+ return indent == c.indent;
+ default:
+ return false;
+ }
+ });
+ }
+ }
+ else if FK_YAML_UNLIKELY (m_flow_token_state == flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) {
+ throw parse_error("Flow mapping beginning is found without separated with a comma.", line, indent);
+ }
+
+ ++m_flow_context_depth;
+
+ switch (m_context_stack.back().state) {
+ case context_state_t::BLOCK_SEQUENCE:
+ case context_state_t::FLOW_SEQUENCE:
+ mp_current_node->as_seq().emplace_back(basic_node_type::mapping());
+ mp_current_node = &(mp_current_node->as_seq().back());
+ m_context_stack.emplace_back(line, indent, context_state_t::FLOW_MAPPING, mp_current_node);
+ break;
+ case context_state_t::BLOCK_MAPPING:
+ case context_state_t::FLOW_MAPPING:
+ // heap-allocated node will be freed in handling the corresponding MAPPING_FLOW_END event.
+ m_context_stack.emplace_back(
+ line, indent, context_state_t::FLOW_MAPPING_KEY, new basic_node_type(node_type::MAPPING));
+ mp_current_node = m_context_stack.back().p_node;
+ break;
+ default: {
+ *mp_current_node = basic_node_type::mapping();
+ parse_context& last_context = m_context_stack.back();
+ last_context.line = line;
+ last_context.indent = indent;
+ last_context.state = context_state_t::FLOW_MAPPING;
+ break;
+ }
+ }
+
+ apply_directive_set(*mp_current_node);
+ apply_node_properties(*mp_current_node);
+
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+ break;
+ case lexical_token_t::MAPPING_FLOW_END: {
+ if FK_YAML_UNLIKELY (m_flow_context_depth == 0) {
+ throw parse_error("Flow mapping ending is found outside the flow context.", line, indent);
+ }
+
+ if (--m_flow_context_depth == 0) {
+ lexer.set_context_state(false);
+ }
+
+ // find the corresponding flow mapping beginning.
+ auto itr = std::find_if( // LCOV_EXCL_LINE
+ m_context_stack.rbegin(),
+ m_context_stack.rend(),
+ [](const parse_context& c) {
+ switch (c.state) {
+ case context_state_t::FLOW_MAPPING_KEY:
+ case context_state_t::FLOW_MAPPING:
+ return true;
+ default:
+ return false;
+ }
+ });
+
+ const bool is_valid = itr != m_context_stack.rend();
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw parse_error("No corresponding flow mapping beginning is found.", line, indent);
+ }
+
+ // keep the last state for later processing.
+ parse_context& last_context = m_context_stack.back();
+ mp_current_node = last_context.p_node;
+ last_context.p_node = nullptr;
+ indent = last_context.indent;
+ const context_state_t state = last_context.state;
+ m_context_stack.pop_back();
+
+ // handle cases where the flow mapping is a mapping key node.
+
+ if (!m_context_stack.empty() && state == context_state_t::FLOW_MAPPING_KEY) {
+ basic_node_type key_node = std::move(*mp_current_node);
+ delete mp_current_node;
+ mp_current_node = m_context_stack.back().p_node;
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+
+ add_new_key(std::move(key_node), line, indent);
+ break;
+ }
+
+ token = lexer.get_next_token();
+ if (token.type == lexical_token_t::KEY_SEPARATOR) {
+ basic_node_type key_node = basic_node_type::mapping();
+ apply_directive_set(key_node);
+ mp_current_node->swap(key_node);
+
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+
+ add_new_key(std::move(key_node), line, indent);
+ }
+ else {
+ if (!m_context_stack.empty()) {
+ mp_current_node = m_context_stack.back().p_node;
+ }
+ if (m_flow_context_depth > 0) {
+ m_flow_token_state = flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX;
+ }
+ }
+
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+ continue;
+ }
+ case lexical_token_t::VALUE_SEPARATOR:
+ FK_YAML_ASSERT(m_flow_context_depth > 0);
+ if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX) {
+ throw parse_error("invalid value separator is found.", line, indent);
+ }
+ m_flow_token_state = flow_token_state_t::NEEDS_VALUE_OR_SUFFIX;
+ break;
+ case lexical_token_t::ALIAS_PREFIX: {
+ // An alias node must not specify any properties (tag, anchor).
+ // https://yaml.org/spec/1.2.2/#71-alias-nodes
+ if FK_YAML_UNLIKELY (m_needs_tag_impl) {
+ throw parse_error("Tag cannot be specified to an alias node", line, indent);
+ }
+ if FK_YAML_UNLIKELY (m_needs_anchor_impl) {
+ throw parse_error("Anchor cannot be specified to an alias node.", line, indent);
+ }
+
+ std::string token_str = std::string(token.str.begin(), token.str.end());
+
+ const auto anchor_counts = static_cast<uint32_t>(mp_meta->anchor_table.count(token_str));
+ if FK_YAML_UNLIKELY (anchor_counts == 0) {
+ throw parse_error("The given anchor name must appear prior to the alias node.", line, indent);
+ }
+
+ basic_node_type node {};
+ node.m_attrs |= detail::node_attr_bits::alias_bit;
+ node.m_prop.anchor = std::move(token_str);
+ detail::node_attr_bits::set_anchor_offset(anchor_counts - 1, node.m_attrs);
+
+ apply_directive_set(node);
+ apply_node_properties(node);
+
+ deserialize_scalar(lexer, std::move(node), indent, line, token);
+ continue;
+ }
+ case lexical_token_t::PLAIN_SCALAR:
+ case lexical_token_t::SINGLE_QUOTED_SCALAR:
+ case lexical_token_t::DOUBLE_QUOTED_SCALAR: {
+ tag_t tag_type {tag_t::NONE};
+ if (m_needs_tag_impl) {
+ tag_type = tag_resolver_type::resolve_tag(m_tag_name, mp_meta);
+ }
+
+ basic_node_type node = scalar_parser_type(line, indent).parse_flow(token.type, tag_type, token.str);
+ apply_directive_set(node);
+ apply_node_properties(node);
+
+ deserialize_scalar(lexer, std::move(node), indent, line, token);
+ continue;
+ }
+ case lexical_token_t::BLOCK_LITERAL_SCALAR:
+ case lexical_token_t::BLOCK_FOLDED_SCALAR: {
+ tag_t tag_type {tag_t::NONE};
+ if (m_needs_tag_impl) {
+ tag_type = tag_resolver_type::resolve_tag(m_tag_name, mp_meta);
+ }
+
+ basic_node_type node =
+ scalar_parser_type(line, indent)
+ .parse_block(token.type, tag_type, token.str, lexer.get_block_scalar_header());
+ apply_directive_set(node);
+ apply_node_properties(node);
+
+ deserialize_scalar(lexer, std::move(node), indent, line, token);
+ continue;
+ }
+ // these tokens end parsing the current YAML document.
+ case lexical_token_t::END_OF_BUFFER:
+ // This handles an empty input.
+ last_type = token.type;
+ return;
+ case lexical_token_t::END_OF_DIRECTIVES:
+ case lexical_token_t::END_OF_DOCUMENT:
+ if FK_YAML_UNLIKELY (m_flow_context_depth > 0) {
+ throw parse_error("An invalid document marker found in a flow collection", line, indent);
+ }
+ last_type = token.type;
+ return;
+ // no way to come here while lexically analyzing document contents.
+ case lexical_token_t::YAML_VER_DIRECTIVE: // LCOV_EXCL_LINE
+ case lexical_token_t::TAG_DIRECTIVE: // LCOV_EXCL_LINE
+ case lexical_token_t::INVALID_DIRECTIVE: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+
+ token = lexer.get_next_token();
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+ } while (token.type != lexical_token_t::END_OF_BUFFER);
+
+ last_type = token.type;
+ }
+
+ /// @brief Deserializes YAML node properties (anchor and/or tag names) if they exist
+ /// @param lexer The lexical analyzer to be used.
+ /// @param last_type The variable to store the last lexical token type.
+ /// @param line The variable to store the line of either the first property or the last non-property token.
+ /// @param indent The variable to store the indent of either the first property or the last non-property token.
+ /// @return true if any property is found, false otherwise.
+ bool deserialize_node_properties(lexer_type& lexer, lexical_token& last_token, uint32_t& line, uint32_t& indent) {
+ m_needs_anchor_impl = m_needs_tag_impl = false;
+
+ lexical_token token = last_token;
+ bool ends_loop {false};
+ do {
+ if (line < lexer.get_lines_processed()) {
+ break;
+ }
+
+ switch (token.type) {
+ case lexical_token_t::ANCHOR_PREFIX:
+ if FK_YAML_UNLIKELY (m_needs_anchor_impl) {
+ throw parse_error(
+ "anchor name cannot be specified more than once to the same node.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+
+ m_anchor_name = token.str;
+ m_needs_anchor_impl = true;
+
+ if (!m_needs_tag_impl) {
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ }
+
+ token = lexer.get_next_token();
+ break;
+ case lexical_token_t::TAG_PREFIX: {
+ if FK_YAML_UNLIKELY (m_needs_tag_impl) {
+ throw parse_error(
+ "tag name cannot be specified more than once to the same node.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+
+ m_tag_name = token.str;
+ m_needs_tag_impl = true;
+
+ if (!m_needs_anchor_impl) {
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ }
+
+ token = lexer.get_next_token();
+ break;
+ }
+ default:
+ ends_loop = true;
+ break;
+ }
+ } while (!ends_loop);
+
+ last_token = token;
+ const bool prop_specified = m_needs_anchor_impl || m_needs_tag_impl;
+ if (!prop_specified) {
+ line = lexer.get_lines_processed();
+ indent = lexer.get_last_token_begin_pos();
+ }
+
+ return prop_specified;
+ }
+
+ /// @brief Add new key string to the current YAML node.
+ /// @param key a key string to be added to the current YAML node.
+ /// @param line The line where the key is found.
+ /// @param indent The indentation width in the current line where the key is found.
+ void add_new_key(basic_node_type&& key, const uint32_t line, const uint32_t indent) {
+ if (m_flow_context_depth == 0) {
+ if FK_YAML_UNLIKELY (m_context_stack.back().indent < indent) {
+ // bad indentation like the following YAML:
+ // ```yaml
+ // foo: true
+ // baz: 123
+ // # ^
+ // ```
+ throw parse_error("bad indentation of a mapping entry.", line, indent);
+ }
+
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ return (c.state == context_state_t::BLOCK_MAPPING) && (indent == c.indent);
+ });
+ }
+ else {
+ if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) {
+ throw parse_error("Flow mapping entry is found without separated with a comma.", line, indent);
+ }
+
+ if (mp_current_node->is_sequence()) {
+ mp_current_node->as_seq().emplace_back(basic_node_type::mapping());
+ mp_current_node = &(mp_current_node->operator[](mp_current_node->size() - 1));
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ }
+ }
+
+ auto itr = mp_current_node->as_map().emplace(std::move(key), basic_node_type());
+ if FK_YAML_UNLIKELY (!itr.second) {
+ throw parse_error("Detected duplication in mapping keys.", line, indent);
+ }
+
+ mp_current_node = &(itr.first->second);
+ const parse_context& key_context = m_context_stack.back();
+ m_context_stack.emplace_back(
+ key_context.line, key_context.indent, context_state_t::MAPPING_VALUE, mp_current_node);
+ }
+
+ /// @brief Assign node value to the current node.
+ /// @param node_value A rvalue basic_node_type object to be assigned to the current node.
+ void assign_node_value(basic_node_type&& node_value, const uint32_t line, const uint32_t indent) {
+ if (mp_current_node->is_sequence()) {
+ FK_YAML_ASSERT(m_flow_context_depth > 0);
+
+ if FK_YAML_UNLIKELY (m_flow_token_state != flow_token_state_t::NEEDS_VALUE_OR_SUFFIX) {
+ // Flow sequence entries are not allowed to be empty.
+ // ```yaml
+ // [foo,,bar]
+ // ```
+ throw parse_error("flow sequence entry is found without separated with a comma.", line, indent);
+ }
+
+ mp_current_node->as_seq().emplace_back(std::move(node_value));
+ m_flow_token_state = flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX;
+ return;
+ }
+
+ // a scalar node
+ *mp_current_node = std::move(node_value);
+ if FK_YAML_UNLIKELY (m_context_stack.empty()) {
+ // single scalar document.
+ return;
+ }
+
+ if FK_YAML_LIKELY (m_context_stack.back().state != context_state_t::BLOCK_MAPPING_EXPLICIT_KEY) {
+ m_context_stack.pop_back();
+ mp_current_node = m_context_stack.back().p_node;
+
+ if (m_flow_context_depth > 0) {
+ m_flow_token_state = flow_token_state_t::NEEDS_SEPARATOR_OR_SUFFIX;
+ }
+ }
+ }
+
+ /// @brief Deserialize a detected scalar node.
+ /// @param lexer The lexical analyzer to be used.
+ /// @param node A scalar node.
+ /// @param indent The current indentation width. Can be updated in this function.
+ /// @param line The number of processed lines. Can be updated in this function.
+ /// @param token The storage for last lexical token.
+ /// @return true if next token has already been got, false otherwise.
+ void deserialize_scalar(
+ lexer_type& lexer, basic_node_type&& node, uint32_t& indent, uint32_t& line, lexical_token& token) {
+ token = lexer.get_next_token();
+ if (mp_current_node->is_mapping()) {
+ const bool is_key_sep_followed =
+ (token.type == lexical_token_t::KEY_SEPARATOR) && (line == lexer.get_lines_processed());
+ if FK_YAML_UNLIKELY (!is_key_sep_followed) {
+ throw parse_error(
+ "The \":\" mapping value indicator must be followed after a mapping key.",
+ lexer.get_lines_processed(),
+ lexer.get_last_token_begin_pos());
+ }
+ add_new_key(std::move(node), line, indent);
+ }
+ else if (token.type == lexical_token_t::KEY_SEPARATOR) {
+ if FK_YAML_UNLIKELY (line != lexer.get_lines_processed()) {
+ // This path is for explicit mapping key separator like:
+ // ```yaml
+ // ? foo
+ // : bar
+ // # ^ this separator
+ // ```
+ assign_node_value(std::move(node), line, indent);
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+
+ if (m_context_stack.back().state != context_state_t::BLOCK_MAPPING_EXPLICIT_KEY) {
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ return c.state == context_state_t::BLOCK_MAPPING_EXPLICIT_KEY && indent == c.indent;
+ });
+ }
+ return;
+ }
+
+ if (mp_current_node->is_scalar()) {
+ if FK_YAML_LIKELY (!m_context_stack.empty()) {
+ parse_context& cur_context = m_context_stack.back();
+ switch (cur_context.state) {
+ case context_state_t::BLOCK_MAPPING_EXPLICIT_KEY:
+ case context_state_t::BLOCK_MAPPING_EXPLICIT_VALUE:
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ break;
+ case context_state_t::BLOCK_SEQUENCE_ENTRY:
+ if FK_YAML_UNLIKELY (cur_context.indent >= indent) {
+ // This handles combination of empty block sequence entry and block mapping entry with the
+ // same indentation level, for examples:
+ // ```yaml
+ // foo:
+ // bar:
+ // - # These entries are indented
+ // baz: 123 # with the same width.
+ // # ^^^
+ // ```
+ pop_to_parent_node(line, indent, [indent](const parse_context& c) {
+ return c.state == context_state_t::BLOCK_MAPPING && indent == c.indent;
+ });
+ add_new_key(std::move(node), line, indent);
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+ return;
+ }
+
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ break;
+ default:
+ if FK_YAML_UNLIKELY (cur_context.line == line) {
+ throw parse_error("Multiple mapping keys are specified on the same line.", line, indent);
+ }
+ cur_context.line = line;
+ cur_context.indent = indent;
+ cur_context.state = context_state_t::BLOCK_MAPPING;
+ break;
+ }
+
+ *mp_current_node = basic_node_type::mapping();
+ apply_directive_set(*mp_current_node);
+ }
+ else {
+ // root mapping node
+
+ m_context_stack.emplace_back(line, indent, context_state_t::BLOCK_MAPPING, mp_current_node);
+ *mp_current_node = basic_node_type::mapping();
+ apply_directive_set(*mp_current_node);
+
+ // apply node properties if any to the root mapping node.
+ if (!m_root_anchor_name.empty()) {
+ mp_current_node->add_anchor_name(
+ std::string(m_root_anchor_name.begin(), m_root_anchor_name.end()));
+ m_root_anchor_name = {};
+ }
+ if (!m_root_tag_name.empty()) {
+ mp_current_node->add_tag_name(std::string(m_root_tag_name.begin(), m_root_tag_name.end()));
+ m_root_tag_name = {};
+ }
+ }
+ }
+ add_new_key(std::move(node), line, indent);
+ }
+ else {
+ assign_node_value(std::move(node), line, indent);
+ }
+
+ indent = lexer.get_last_token_begin_pos();
+ line = lexer.get_lines_processed();
+ }
+
+ /// @brief Pops parent contexts to a block mapping with the given indentation.
+ /// @tparam Pred Functor type to test parent contexts.
+ /// @param line The current line count.
+ /// @param indent The indentation level of the target parent block mapping.
+ template <typename Pred>
+ void pop_to_parent_node(uint32_t line, uint32_t indent, Pred&& pred) {
+ FK_YAML_ASSERT(!m_context_stack.empty());
+
+ // LCOV_EXCL_START
+ auto itr = std::find_if(m_context_stack.rbegin(), m_context_stack.rend(), std::forward<Pred>(pred));
+ // LCOV_EXCL_STOP
+ const bool is_indent_valid = (itr != m_context_stack.rend());
+ if FK_YAML_UNLIKELY (!is_indent_valid) {
+ throw parse_error("Detected invalid indentation.", line, indent);
+ }
+
+ const auto pop_num = static_cast<uint32_t>(std::distance(m_context_stack.rbegin(), itr));
+
+ // move back to the parent block mapping.
+ for (uint32_t i = 0; i < pop_num; i++) {
+ m_context_stack.pop_back();
+ }
+ mp_current_node = m_context_stack.back().p_node;
+ }
+
+ /// @brief Set YAML directive properties to the given node.
+ /// @param node A basic_node_type object to be set YAML directive properties.
+ void apply_directive_set(basic_node_type& node) noexcept {
+ node.mp_meta = mp_meta;
+ }
+
+ /// @brief Set YAML node properties (anchor and/or tag names) to the given node.
+ /// @param node A node type object to be set YAML node properties.
+ void apply_node_properties(basic_node_type& node) {
+ if (m_needs_anchor_impl) {
+ node.add_anchor_name(std::string(m_anchor_name.begin(), m_anchor_name.end()));
+ m_needs_anchor_impl = false;
+ m_anchor_name = {};
+ }
+
+ if (m_needs_tag_impl) {
+ node.add_tag_name(std::string(m_tag_name.begin(), m_tag_name.end()));
+ m_needs_tag_impl = false;
+ m_tag_name = {};
+ }
+ }
+
+ /// @brief Update the target YAML version with an input string.
+ /// @param version_str A YAML version string.
+ yaml_version_type convert_yaml_version(str_view version_str) noexcept {
+ return (version_str.compare("1.1") == 0) ? yaml_version_type::VERSION_1_1 : yaml_version_type::VERSION_1_2;
+ }
+
+private:
+ /// The currently focused YAML node.
+ basic_node_type* mp_current_node {nullptr};
+ /// The stack of parse contexts.
+ std::deque<parse_context> m_context_stack {};
+ /// The current depth of flow contexts.
+ uint32_t m_flow_context_depth {0};
+ /// The set of YAML directives.
+ std::shared_ptr<doc_metainfo_type> mp_meta {};
+ /// A flag to determine the need for YAML anchor node implementation.
+ bool m_needs_anchor_impl {false};
+ /// A flag to determine the need for a corresponding node with the last YAML tag.
+ bool m_needs_tag_impl {false};
+ /// A flag to determine the need for a value separator or a flow suffix to follow.
+ flow_token_state_t m_flow_token_state {flow_token_state_t::NEEDS_VALUE_OR_SUFFIX};
+ /// The last YAML anchor name.
+ str_view m_anchor_name;
+ /// The last tag name.
+ str_view m_tag_name;
+ /// The root YAML anchor name. (maybe empty and unused)
+ str_view m_root_anchor_name;
+ /// The root tag name. (maybe empty and unused)
+ str_view m_root_tag_name;
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_DESERIALIZER_HPP */
+
+// #include <fkYAML/detail/input/input_adapter.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_INPUT_INPUT_ADAPTER_HPP
+#define FK_YAML_DETAIL_INPUT_INPUT_ADAPTER_HPP
+
+#include <array>
+#include <cstdio>
+#include <cstring>
+#include <deque>
+#include <istream>
+#include <iterator>
+#include <string>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/assert.hpp>
+
+// #include <fkYAML/detail/encodings/utf_encode_detector.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP
+#define FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP
+
+#include <cstdint>
+#include <istream>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/encodings/utf_encode_t.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_T_HPP
+#define FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_T_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Definition of Unicode encoding types
+/// @note Since fkYAML doesn't treat UTF-16/UTF-32 encoded characters per byte, endians do not matter.
+enum class utf_encode_t : std::uint8_t {
+ UTF_8, //!< UTF-8
+ UTF_16BE, //!< UTF-16 Big Endian
+ UTF_16LE, //!< UTF-16 Little Endian
+ UTF_32BE, //!< UTF-32 Big Endian
+ UTF_32LE, //!< UTF-32 Little Endian
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_T_HPP */
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Detect an encoding type for UTF-8 expected inputs.
+/// @note This function doesn't support the case where the first character is null.
+/// @param[in] bytes 4 bytes of an input character sequence.
+/// @param[out] has_bom Whether the input contains a BOM.
+/// @return A detected encoding type.
+inline utf_encode_t detect_encoding_type(const std::array<uint8_t, 4>& bytes, bool& has_bom) noexcept {
+ has_bom = false;
+
+ const uint8_t byte0 = bytes[0];
+ const uint8_t byte1 = bytes[1];
+ const uint8_t byte2 = bytes[2];
+ const uint8_t byte3 = bytes[3];
+
+ // Check if a BOM exists.
+
+ if (byte0 == static_cast<uint8_t>(0xEFu) && byte1 == static_cast<uint8_t>(0xBBu) &&
+ byte2 == static_cast<uint8_t>(0xBFu)) {
+ has_bom = true;
+ return utf_encode_t::UTF_8;
+ }
+
+ if (byte0 == 0 && byte1 == 0 && byte2 == static_cast<uint8_t>(0xFEu) && byte3 == static_cast<uint8_t>(0xFFu)) {
+ has_bom = true;
+ return utf_encode_t::UTF_32BE;
+ }
+
+ if (byte0 == static_cast<uint8_t>(0xFFu) && byte1 == static_cast<uint8_t>(0xFEu) && byte2 == 0 && byte3 == 0) {
+ has_bom = true;
+ return utf_encode_t::UTF_32LE;
+ }
+
+ if (byte0 == static_cast<uint8_t>(0xFEu) && byte1 == static_cast<uint8_t>(0xFFu)) {
+ has_bom = true;
+ return utf_encode_t::UTF_16BE;
+ }
+
+ if (byte0 == static_cast<uint8_t>(0xFFu) && byte1 == static_cast<uint8_t>(0xFEu)) {
+ has_bom = true;
+ return utf_encode_t::UTF_16LE;
+ }
+
+ // Test the first character assuming it's an ASCII character.
+
+ if (byte0 == 0 && byte1 == 0 && byte2 == 0 && 0 < byte3 && byte3 < static_cast<uint8_t>(0x80u)) {
+ return utf_encode_t::UTF_32BE;
+ }
+
+ if (0 < byte0 && byte0 < static_cast<uint8_t>(0x80u) && byte1 == 0 && byte2 == 0 && byte3 == 0) {
+ return utf_encode_t::UTF_32LE;
+ }
+
+ if (byte0 == 0 && 0 < byte1 && byte1 < static_cast<uint8_t>(0x80u)) {
+ return utf_encode_t::UTF_16BE;
+ }
+
+ if (0 < byte0 && byte0 < static_cast<uint8_t>(0x80u) && byte1 == 0) {
+ return utf_encode_t::UTF_16LE;
+ }
+
+ return utf_encode_t::UTF_8;
+}
+
+/// @brief A class which detects UTF encoding type and the existence of a BOM at the beginning.
+/// @tparam ItrType Type of iterators for the input.
+template <typename ItrType, typename = void>
+struct utf_encode_detector {};
+
+/// @brief The partial specialization of utf_encode_detector for char iterators.
+/// @tparam ItrType An iterator type.
+template <typename ItrType>
+struct utf_encode_detector<ItrType, enable_if_t<is_iterator_of<ItrType, char>::value>> {
+ /// @brief Detects the encoding type of the input, and consumes a BOM if it exists.
+ /// @param begin The iterator to the first element of an input.
+ /// @param end The iterator to the past-the end element of an input.
+ /// @return A detected encoding type.
+ static utf_encode_t detect(ItrType& begin, const ItrType& end) noexcept {
+ if FK_YAML_UNLIKELY (begin == end) {
+ return utf_encode_t::UTF_8;
+ }
+
+ // the inner curly braces are necessary for older compilers
+ std::array<uint8_t, 4> bytes {{}};
+ bytes.fill(0xFFu);
+ auto current = begin;
+ for (int i = 0; i < 4 && current != end; i++, ++current) {
+ bytes[i] = static_cast<uint8_t>(*current); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+ }
+
+ bool has_bom = false;
+ const utf_encode_t encode_type = detect_encoding_type(bytes, has_bom);
+
+ if (has_bom) {
+ // skip reading the BOM.
+ switch (encode_type) {
+ case utf_encode_t::UTF_8:
+ std::advance(begin, 3);
+ break;
+ case utf_encode_t::UTF_16BE:
+ case utf_encode_t::UTF_16LE:
+ std::advance(begin, 2);
+ break;
+ case utf_encode_t::UTF_32BE:
+ case utf_encode_t::UTF_32LE:
+ std::advance(begin, 4);
+ break;
+ }
+ }
+
+ return encode_type;
+ }
+};
+
+#if FK_YAML_HAS_CHAR8_T
+
+/// @brief The partial specialization of utf_encode_detector for char8_t iterators.
+/// @tparam ItrType An iterator type.
+template <typename ItrType>
+struct utf_encode_detector<ItrType, enable_if_t<is_iterator_of<ItrType, char8_t>::value>> {
+ /// @brief Detects the encoding type of the input, and consumes a BOM if it exists.
+ /// @param begin The iterator to the first element of an input.
+ /// @param end The iterator to the past-the end element of an input.
+ /// @return A detected encoding type.
+ static utf_encode_t detect(ItrType& begin, const ItrType& end) {
+ if FK_YAML_UNLIKELY (begin == end) {
+ return utf_encode_t::UTF_8;
+ }
+
+ std::array<uint8_t, 4> bytes {};
+ bytes.fill(0xFFu);
+ auto current = begin;
+ for (int i = 0; i < 4 && current != end; i++, ++current) {
+ bytes[i] = uint8_t(*current); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+ }
+
+ bool has_bom = false;
+ const utf_encode_t encode_type = detect_encoding_type(bytes, has_bom);
+
+ if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_8) {
+ throw exception("char8_t characters must be encoded in the UTF-8 format.");
+ }
+
+ if (has_bom) {
+ // skip reading the BOM.
+ std::advance(begin, 3);
+ }
+
+ return encode_type;
+ }
+};
+
+#endif // FK_YAML_HAS_CHAR8_T
+
+/// @brief The partial specialization of utf_encode_detector for char16_t iterators.
+/// @tparam ItrType An iterator type.
+template <typename ItrType>
+struct utf_encode_detector<ItrType, enable_if_t<is_iterator_of<ItrType, char16_t>::value>> {
+ /// @brief Detects the encoding type of the input, and consumes a BOM if it exists.
+ /// @param begin The iterator to the first element of an input.
+ /// @param end The iterator to the past-the end element of an input.
+ /// @return A detected encoding type.
+ static utf_encode_t detect(ItrType& begin, const ItrType& end) {
+ if FK_YAML_UNLIKELY (begin == end) {
+ return utf_encode_t::UTF_16BE;
+ }
+
+ // the inner curly braces are necessary for older compilers
+ std::array<uint8_t, 4> bytes {{}};
+ bytes.fill(0xFFu);
+ auto current = begin;
+ for (int i = 0; i < 2 && current != end; i++, ++current) {
+ // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index)
+ const char16_t elem = *current;
+ const int idx_base = i * 2;
+ bytes[idx_base] = static_cast<uint8_t>(elem >> 8);
+ bytes[idx_base + 1] = static_cast<uint8_t>(elem);
+ // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index)
+ }
+
+ bool has_bom = false;
+ const utf_encode_t encode_type = detect_encoding_type(bytes, has_bom);
+
+ if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_16BE && encode_type != utf_encode_t::UTF_16LE) {
+ throw exception("char16_t characters must be encoded in the UTF-16 format.");
+ }
+
+ if (has_bom) {
+ // skip reading the BOM.
+ std::advance(begin, 1);
+ }
+
+ return encode_type;
+ }
+};
+
+/// @brief The partial specialization of utf_encode_detector for char32_t iterators.
+/// @tparam ItrType An iterator type.
+template <typename ItrType>
+struct utf_encode_detector<ItrType, enable_if_t<is_iterator_of<ItrType, char32_t>::value>> {
+ /// @brief Detects the encoding type of the input, and consumes a BOM if it exists.
+ /// @param begin The iterator to the first element of an input.
+ /// @param end The iterator to the past-the end element of an input.
+ /// @return A detected encoding type.
+ static utf_encode_t detect(ItrType& begin, const ItrType& end) {
+ if FK_YAML_UNLIKELY (begin == end) {
+ return utf_encode_t::UTF_32BE;
+ }
+
+ // the inner curly braces are necessary for older compilers
+ std::array<uint8_t, 4> bytes {{}};
+ const char32_t elem = *begin;
+ bytes[0] = static_cast<uint8_t>(elem >> 24);
+ bytes[1] = static_cast<uint8_t>(elem >> 16);
+ bytes[2] = static_cast<uint8_t>(elem >> 8);
+ bytes[3] = static_cast<uint8_t>(elem);
+
+ bool has_bom = false;
+ const utf_encode_t encode_type = detect_encoding_type(bytes, has_bom);
+
+ if FK_YAML_UNLIKELY (encode_type != utf_encode_t::UTF_32BE && encode_type != utf_encode_t::UTF_32LE) {
+ throw exception("char32_t characters must be encoded in the UTF-32 format.");
+ }
+
+ if (has_bom) {
+ // skip reading the BOM.
+ std::advance(begin, 1);
+ }
+
+ return encode_type;
+ }
+};
+
+/// @brief A class which detects UTF encoding type and the existence of a BOM from the input file.
+struct file_utf_encode_detector {
+ /// @brief Detects the encoding type of the input, and consumes a BOM if it exists.
+ /// @param p_file The input file handle.
+ /// @return A detected encoding type.
+ static utf_encode_t detect(std::FILE* p_file) noexcept {
+ // the inner curly braces are necessary for older compilers
+ std::array<uint8_t, 4> bytes {{}};
+ bytes.fill(0xFFu);
+ for (int i = 0; i < 4; i++) {
+ char byte = 0;
+ const std::size_t size = std::fread(&byte, sizeof(char), 1, p_file);
+ if (size != sizeof(char)) {
+ break;
+ }
+ bytes[i] = static_cast<uint8_t>(byte & 0xFF); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+ }
+
+ bool has_bom = false;
+ const utf_encode_t encode_type = detect_encoding_type(bytes, has_bom);
+
+ // move back to the beginning if a BOM doesn't exist.
+ long offset = 0; // NOLINT(google-runtime-int)
+ if (has_bom) {
+ switch (encode_type) {
+ case utf_encode_t::UTF_8:
+ offset = 3;
+ break;
+ case utf_encode_t::UTF_16BE:
+ case utf_encode_t::UTF_16LE:
+ offset = 2;
+ break;
+ case utf_encode_t::UTF_32BE:
+ case utf_encode_t::UTF_32LE:
+ offset = 4;
+ break;
+ }
+ }
+ std::fseek(p_file, offset, SEEK_SET); // NOLINT(cert-err33-c)
+
+ return encode_type;
+ }
+};
+
+/// @brief A class which detects UTF encoding type and the existence of a BOM from the input file.
+struct stream_utf_encode_detector {
+ /// @brief Detects the encoding type of the input, and consumes a BOM if it exists.
+ /// @param p_file The input file handle.
+ /// @return A detected encoding type.
+ static utf_encode_t detect(std::istream& is) noexcept {
+ // the inner curly braces are necessary for older compilers
+ std::array<uint8_t, 4> bytes {{}};
+ bytes.fill(0xFFu);
+ for (int i = 0; i < 4; i++) {
+ char ch = 0;
+ is.read(&ch, 1);
+ const std::streamsize size = is.gcount();
+ if (size != 1) {
+ // without this, seekg() will fail.
+ is.clear();
+ break;
+ }
+ bytes[i] = static_cast<uint8_t>(ch & 0xFF); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
+ }
+
+ bool has_bom = false;
+ const utf_encode_t encode_type = detect_encoding_type(bytes, has_bom);
+
+ // move back to the beginning if a BOM doesn't exist.
+ std::streamoff offset = 0;
+ if (has_bom) {
+ switch (encode_type) {
+ case utf_encode_t::UTF_8:
+ offset = 3;
+ break;
+ case utf_encode_t::UTF_16BE:
+ case utf_encode_t::UTF_16LE:
+ offset = 2;
+ break;
+ case utf_encode_t::UTF_32BE:
+ case utf_encode_t::UTF_32LE:
+ offset = 4;
+ break;
+ }
+ }
+ is.seekg(offset, std::ios_base::beg);
+
+ return encode_type;
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_ENCODINGS_UTF_ENCODE_DETECTOR_HPP */
+
+// #include <fkYAML/detail/encodings/utf_encode_t.hpp>
+
+// #include <fkYAML/detail/encodings/utf_encodings.hpp>
+
+// #include <fkYAML/detail/meta/input_adapter_traits.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/str_view.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+///////////////////////
+// input_adapter //
+///////////////////////
+
+template <typename IterType, typename = void>
+class iterator_input_adapter;
+
+/// @brief An input adapter for iterators of type char.
+/// @tparam IterType An iterator type.
+template <typename IterType>
+class iterator_input_adapter<IterType, enable_if_t<is_iterator_of<IterType, char>::value>> {
+public:
+ /// @brief Construct a new iterator_input_adapter object.
+ iterator_input_adapter() = default;
+
+ /// @brief Construct a new iterator_input_adapter object.
+ /// @param begin The beginning of iterators.
+ /// @param end The end of iterators.
+ /// @param encode_type The encoding type for this input adapter.
+ /// @param is_contiguous Whether iterators are contiguous or not.
+ iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept
+ : m_begin(begin),
+ m_end(end),
+ m_encode_type(encode_type),
+ m_is_contiguous(is_contiguous) {
+ }
+
+ // allow only move construct/assignment like other input adapters.
+ iterator_input_adapter(const iterator_input_adapter&) = delete;
+ iterator_input_adapter(iterator_input_adapter&& rhs) = default;
+ iterator_input_adapter& operator=(const iterator_input_adapter&) = delete;
+ iterator_input_adapter& operator=(iterator_input_adapter&&) = default;
+ ~iterator_input_adapter() = default;
+
+ /// @brief Get view into the input buffer contents.
+ /// @return View into the input buffer contents.
+ str_view get_buffer_view() {
+ if FK_YAML_UNLIKELY (m_begin == m_end) {
+ return {};
+ }
+
+ m_buffer.clear();
+
+ switch (m_encode_type) {
+ case utf_encode_t::UTF_8:
+ return get_buffer_view_utf8();
+ case utf_encode_t::UTF_16BE:
+ case utf_encode_t::UTF_16LE:
+ return get_buffer_view_utf16();
+ case utf_encode_t::UTF_32BE:
+ case utf_encode_t::UTF_32LE:
+ return get_buffer_view_utf32();
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+private:
+ /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf8() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8);
+
+ IterType current = m_begin;
+ std::deque<IterType> cr_itrs {};
+ while (current != m_end) {
+ const auto first = static_cast<uint8_t>(*current);
+ const uint32_t num_bytes = utf8::get_num_bytes(first);
+
+ switch (num_bytes) {
+ case 1:
+ if FK_YAML_UNLIKELY (first == 0x0D /*CR*/) {
+ cr_itrs.emplace_back(current);
+ }
+ break;
+ case 2: {
+ const auto second = static_cast<uint8_t>(*++current);
+ const bool is_valid = utf8::validate(first, second);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second});
+ }
+ break;
+ }
+ case 3: {
+ const auto second = static_cast<uint8_t>(*++current);
+ const auto third = static_cast<uint8_t>(*++current);
+ const bool is_valid = utf8::validate(first, second, third);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third});
+ }
+ break;
+ }
+ case 4: {
+ const auto second = static_cast<uint8_t>(*++current);
+ const auto third = static_cast<uint8_t>(*++current);
+ const auto fourth = static_cast<uint8_t>(*++current);
+ const bool is_valid = utf8::validate(first, second, third, fourth);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third, fourth});
+ }
+ break;
+ }
+ default: // LCOV_EXCL_LINE
+ unreachable(); // LCOV_EXCL_LINE
+ }
+
+ ++current;
+ }
+
+ const bool is_contiguous_no_cr = cr_itrs.empty() && m_is_contiguous;
+ if FK_YAML_LIKELY (is_contiguous_no_cr) {
+ // The input iterators (begin, end) can be used as-is during parsing.
+ FK_YAML_ASSERT(m_begin != m_end);
+ return str_view {&*m_begin, static_cast<std::size_t>(std::distance(m_begin, m_end))};
+ }
+
+ m_buffer.reserve(std::distance(m_begin, m_end) - cr_itrs.size());
+
+ current = m_begin;
+ for (const auto& cr_itr : cr_itrs) {
+ m_buffer.append(current, cr_itr);
+ current = std::next(cr_itr);
+ }
+ m_buffer.append(current, m_end);
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+ /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf16() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE);
+
+ // Assume the input characters are all ASCII characters.
+ // That's the most probably the case.
+ m_buffer.reserve(std::distance(m_begin, m_end) / 2);
+
+ int shift_bits[2] {0, 0};
+ if (m_encode_type == utf_encode_t::UTF_16BE) {
+ shift_bits[0] = 8;
+ }
+ else // m_encode_type == utf_encode_t::UTF_16LE
+ {
+ shift_bits[1] = 8;
+ }
+
+ std::array<char16_t, 2> encoded_buffer {{0, 0}};
+ uint32_t encoded_buf_size {0};
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ IterType current = m_begin;
+ while (current != m_end || encoded_buf_size != 0) {
+ while (current != m_end && encoded_buf_size < 2) {
+ auto utf16 = static_cast<char16_t>(static_cast<uint8_t>(*current) << shift_bits[0]);
+ utf16 |= static_cast<char16_t>(static_cast<uint8_t>(*++current) << shift_bits[1]);
+ ++current;
+
+ // skip appending CRs.
+ if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
+ encoded_buffer[encoded_buf_size++] = utf16;
+ }
+ }
+
+ uint32_t consumed_size = 0;
+ utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size);
+
+ if FK_YAML_LIKELY (consumed_size == 1) {
+ encoded_buffer[0] = encoded_buffer[1];
+ }
+ encoded_buf_size -= consumed_size;
+
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+ /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf32() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE);
+
+ // Assume the input characters are all ASCII characters.
+ // That's the most probably the case.
+ m_buffer.reserve(std::distance(m_begin, m_end) / 4);
+
+ int shift_bits[4] {0, 0, 0, 0};
+ if (m_encode_type == utf_encode_t::UTF_32BE) {
+ shift_bits[0] = 24;
+ shift_bits[1] = 16;
+ shift_bits[2] = 8;
+ }
+ else // m_encode_type == utf_encode_t::UTF_32LE
+ {
+ shift_bits[1] = 8;
+ shift_bits[2] = 16;
+ shift_bits[3] = 24;
+ }
+
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ IterType current = m_begin;
+ while (current != m_end) {
+ auto utf32 = static_cast<char32_t>(*current << shift_bits[0]);
+ ++current;
+ utf32 |= static_cast<char32_t>(*current << shift_bits[1]);
+ ++current;
+ utf32 |= static_cast<char32_t>(*current << shift_bits[2]);
+ ++current;
+ utf32 |= static_cast<char32_t>(*current << shift_bits[3]);
+ ++current;
+
+ if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) {
+ utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size);
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+private:
+ /// The iterator at the beginning of input.
+ IterType m_begin {};
+ /// The iterator at the end of input.
+ IterType m_end {};
+ /// The encoding type for this input adapter.
+ utf_encode_t m_encode_type {utf_encode_t::UTF_8};
+ /// The normalized owned buffer.
+ std::string m_buffer;
+ /// Whether ItrType is a contiguous iterator.
+ bool m_is_contiguous {false};
+};
+
+#if FK_YAML_HAS_CHAR8_T
+
+/// @brief An input adapter for iterators of type char8_t.
+/// @tparam IterType An iterator type.
+template <typename IterType>
+class iterator_input_adapter<IterType, enable_if_t<is_iterator_of<IterType, char8_t>::value>> {
+public:
+ /// @brief Construct a new iterator_input_adapter object.
+ iterator_input_adapter() = default;
+
+ /// @brief Construct a new iterator_input_adapter object.
+ /// @param begin The beginning of iterators.
+ /// @param end The end of iterators.
+ /// @param encode_type The encoding type for this input adapter.
+ /// @param is_contiguous Whether iterators are contiguous or not.
+ iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept
+ : m_begin(begin),
+ m_end(end),
+ m_encode_type(encode_type),
+ m_is_contiguous(is_contiguous) {
+ // char8_t characters must be encoded in the UTF-8 format.
+ // See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0482r6.html.
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8);
+ }
+
+ // allow only move construct/assignment like other input adapters.
+ iterator_input_adapter(const iterator_input_adapter&) = delete;
+ iterator_input_adapter(iterator_input_adapter&& rhs) = default;
+ iterator_input_adapter& operator=(const iterator_input_adapter&) = delete;
+ iterator_input_adapter& operator=(iterator_input_adapter&&) = default;
+ ~iterator_input_adapter() = default;
+
+ /// @brief Get view into the input buffer contents.
+ /// @return View into the input buffer contents.
+ str_view get_buffer_view() {
+ if FK_YAML_UNLIKELY (m_begin == m_end) {
+ return {};
+ }
+
+ IterType current = m_begin;
+ std::deque<IterType> cr_itrs {};
+ while (current != m_end) {
+ const auto first = static_cast<uint8_t>(*current);
+ const uint32_t num_bytes = utf8::get_num_bytes(first);
+
+ switch (num_bytes) {
+ case 1:
+ if FK_YAML_UNLIKELY (first == 0x0D /*CR*/) {
+ cr_itrs.emplace_back(current);
+ }
+ break;
+ case 2: {
+ const auto second = static_cast<uint8_t>(*++current);
+ const bool is_valid = utf8::validate(first, second);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second});
+ }
+ break;
+ }
+ case 3: {
+ const auto second = static_cast<uint8_t>(*++current);
+ const auto third = static_cast<uint8_t>(*++current);
+ const bool is_valid = utf8::validate(first, second, third);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third});
+ }
+ break;
+ }
+ case 4: {
+ const auto second = static_cast<uint8_t>(*++current);
+ const auto third = static_cast<uint8_t>(*++current);
+ const auto fourth = static_cast<uint8_t>(*++current);
+ const bool is_valid = utf8::validate(first, second, third, fourth);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third, fourth});
+ }
+ break;
+ }
+ default: // LCOV_EXCL_LINE
+ unreachable(); // LCOV_EXCL_LINE
+ }
+
+ ++current;
+ }
+
+ m_buffer.reserve(std::distance(m_begin, m_end) - cr_itrs.size());
+ current = m_begin;
+ for (const auto& cr_itr : cr_itrs) {
+ std::transform(
+ current, cr_itr, std::back_inserter(m_buffer), [](char8_t c) { return static_cast<char>(c); });
+ current = std::next(cr_itr);
+ }
+ std::transform(current, m_end, std::back_inserter(m_buffer), [](char8_t c) { return static_cast<char>(c); });
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+private:
+ /// The iterator at the beginning of input.
+ IterType m_begin {};
+ /// The iterator at the end of input.
+ IterType m_end {};
+ /// The encoding type for this input adapter.
+ utf_encode_t m_encode_type {utf_encode_t::UTF_8};
+ /// The normalized owned buffer.
+ std::string m_buffer;
+ /// Whether ItrType is a contiguous iterator.
+ bool m_is_contiguous {false};
+};
+
+#endif // FK_YAML_HAS_CHAR8_T
+
+/// @brief An input adapter for iterators of type char16_t.
+/// @tparam IterType An iterator type.
+template <typename IterType>
+class iterator_input_adapter<IterType, enable_if_t<is_iterator_of<IterType, char16_t>::value>> {
+public:
+ /// @brief Construct a new iterator_input_adapter object.
+ iterator_input_adapter() = default;
+
+ /// @brief Construct a new iterator_input_adapter object.
+ /// @param begin The beginning of iterators.
+ /// @param end The end of iterators.
+ /// @param encode_type The encoding type for this input adapter.
+ /// @param is_contiguous Whether iterators are contiguous or not.
+ iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept
+ : m_begin(begin),
+ m_end(end),
+ m_encode_type(encode_type),
+ m_is_contiguous(is_contiguous) {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE);
+ }
+
+ // allow only move construct/assignment like other input adapters.
+ iterator_input_adapter(const iterator_input_adapter&) = delete;
+ iterator_input_adapter(iterator_input_adapter&& rhs) = default;
+ iterator_input_adapter& operator=(const iterator_input_adapter&) = delete;
+ iterator_input_adapter& operator=(iterator_input_adapter&&) = default;
+ ~iterator_input_adapter() = default;
+
+ /// @brief Get view into the input buffer contents.
+ /// @return View into the input buffer contents.
+ str_view get_buffer_view() {
+ if FK_YAML_UNLIKELY (m_begin == m_end) {
+ return {};
+ }
+
+ const int shift_bits = (m_encode_type == utf_encode_t::UTF_16BE) ? 0 : 8;
+
+ std::array<char16_t, 2> encoded_buffer {{0, 0}};
+ uint32_t encoded_buf_size {0};
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ // Assume the input characters are all ASCII characters.
+ // That's the most probably the case.
+ m_buffer.reserve(std::distance(m_begin, m_end));
+
+ IterType current = m_begin;
+ while (current != m_end || encoded_buf_size != 0) {
+ while (current != m_end && encoded_buf_size < 2) {
+ char16_t utf16 = *current;
+ ++current;
+ utf16 = static_cast<char16_t>(((utf16 & 0x00FFu) << shift_bits) | ((utf16 & 0xFF00u) >> shift_bits));
+
+ if FK_YAML_LIKELY (utf16 != char16_t(0x000Du)) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
+ encoded_buffer[encoded_buf_size++] = utf16;
+ }
+ }
+
+ uint32_t consumed_size = 0;
+ utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size);
+
+ if FK_YAML_LIKELY (consumed_size == 1) {
+ encoded_buffer[0] = encoded_buffer[1];
+ encoded_buffer[1] = 0;
+ }
+ encoded_buf_size -= consumed_size;
+
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+private:
+ /// The iterator at the beginning of input.
+ IterType m_begin {};
+ /// The iterator at the end of input.
+ IterType m_end {};
+ /// The encoding type for this input adapter.
+ utf_encode_t m_encode_type {utf_encode_t::UTF_16BE};
+ /// The normalized owned buffer.
+ std::string m_buffer;
+ /// Whether ItrType is a contiguous iterator.
+ bool m_is_contiguous {false};
+};
+
+/// @brief An input adapter for iterators of type char32_t.
+/// @tparam IterType An iterator type.
+template <typename IterType>
+class iterator_input_adapter<IterType, enable_if_t<is_iterator_of<IterType, char32_t>::value>> {
+public:
+ /// @brief Construct a new iterator_input_adapter object.
+ iterator_input_adapter() = default;
+
+ /// @brief Construct a new iterator_input_adapter object.
+ /// @param begin The beginning of iterators.
+ /// @param end The end of iterators.
+ /// @param encode_type The encoding type for this input adapter.
+ /// @param is_contiguous Whether iterators are contiguous or not.
+ iterator_input_adapter(IterType begin, IterType end, utf_encode_t encode_type, bool is_contiguous) noexcept
+ : m_begin(begin),
+ m_end(end),
+ m_encode_type(encode_type),
+ m_is_contiguous(is_contiguous) {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE);
+ }
+
+ // allow only move construct/assignment like other input adapters.
+ iterator_input_adapter(const iterator_input_adapter&) = delete;
+ iterator_input_adapter(iterator_input_adapter&& rhs) = default;
+ iterator_input_adapter& operator=(const iterator_input_adapter&) = delete;
+ iterator_input_adapter& operator=(iterator_input_adapter&&) = default;
+ ~iterator_input_adapter() = default;
+
+ /// @brief Get view into the input buffer contents.
+ /// @return View into the input buffer contents.
+ str_view get_buffer_view() {
+ if FK_YAML_UNLIKELY (m_begin == m_end) {
+ return {};
+ }
+
+ int shift_bits[4] {0, 0, 0, 0};
+ if (m_encode_type == utf_encode_t::UTF_32LE) {
+ shift_bits[0] = 24;
+ shift_bits[1] = 8;
+ shift_bits[2] = 8;
+ shift_bits[3] = 24;
+ }
+
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ // Assume the input characters are all ASCII characters.
+ // That's the most probably the case.
+ m_buffer.reserve(std::distance(m_begin, m_end));
+
+ IterType current = m_begin;
+ while (current != m_end) {
+ const char32_t tmp = *current;
+ ++current;
+ const auto utf32 = static_cast<char32_t>(
+ ((tmp & 0xFF000000u) >> shift_bits[0]) | ((tmp & 0x00FF0000u) >> shift_bits[1]) |
+ ((tmp & 0x0000FF00u) << shift_bits[2]) | ((tmp & 0x000000FFu) << shift_bits[3]));
+
+ if FK_YAML_UNLIKELY (utf32 != static_cast<char32_t>(0x0000000Du)) {
+ utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size);
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+private:
+ /// The iterator at the beginning of input.
+ IterType m_begin {};
+ /// The iterator at the end of input.
+ IterType m_end {};
+ /// The encoding type for this input adapter.
+ utf_encode_t m_encode_type {utf_encode_t::UTF_32BE};
+ /// The normalized owned buffer.
+ std::string m_buffer;
+ /// Whether ItrType is a contiguous iterator.
+ bool m_is_contiguous {false};
+};
+
+/// @brief An input adapter for C-style file handles.
+class file_input_adapter {
+public:
+ /// @brief Construct a new file_input_adapter object.
+ file_input_adapter() = default;
+
+ /// @brief Construct a new file_input_adapter object.
+ /// @note
+ /// This class doesn't call fopen() nor fclose().
+ /// It's user's responsibility to call those functions.
+ /// @param file A file handle for this adapter. (A non-null pointer is assumed.)
+ /// @param encode_type The encoding type for this input adapter.
+ explicit file_input_adapter(std::FILE* file, utf_encode_t encode_type) noexcept
+ : m_file(file),
+ m_encode_type(encode_type) {
+ }
+
+ // allow only move construct/assignment
+ file_input_adapter(const file_input_adapter&) = delete;
+ file_input_adapter(file_input_adapter&& rhs) = default;
+ file_input_adapter& operator=(const file_input_adapter&) = delete;
+ file_input_adapter& operator=(file_input_adapter&&) = default;
+ ~file_input_adapter() = default;
+
+ /// @brief Get view into the input buffer contents.
+ /// @return View into the input buffer contents.
+ str_view get_buffer_view() {
+ switch (m_encode_type) {
+ case utf_encode_t::UTF_8:
+ return get_buffer_view_utf8();
+ case utf_encode_t::UTF_16BE:
+ case utf_encode_t::UTF_16LE:
+ return get_buffer_view_utf16();
+ case utf_encode_t::UTF_32BE:
+ case utf_encode_t::UTF_32LE:
+ return get_buffer_view_utf32();
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+private:
+ /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf8() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8);
+
+ m_buffer.clear();
+ char tmp_buf[256] {};
+ constexpr std::size_t buf_size = sizeof(tmp_buf) / sizeof(tmp_buf[0]);
+ std::size_t read_size = 0;
+ while ((read_size = std::fread(&tmp_buf[0], sizeof(char), buf_size, m_file)) > 0) {
+ char* p_current = &tmp_buf[0];
+ char* p_end = p_current + read_size;
+
+ // copy tmp_buf to m_buffer, dropping CRs.
+ char* p_cr = p_current;
+ do {
+ if FK_YAML_UNLIKELY (*p_cr == '\r') {
+ m_buffer.append(p_current, p_cr);
+ p_current = p_cr + 1;
+ }
+ ++p_cr;
+ } while (p_cr != p_end);
+
+ m_buffer.append(p_current, p_end);
+ }
+
+ if FK_YAML_UNLIKELY (m_buffer.empty()) {
+ return {};
+ }
+
+ auto current = m_buffer.begin();
+ auto end = m_buffer.end();
+ while (current != end) {
+ const auto first = static_cast<uint8_t>(*current++);
+ const uint32_t num_bytes = utf8::get_num_bytes(first);
+
+ switch (num_bytes) {
+ case 1:
+ break;
+ case 2: {
+ const auto second = static_cast<uint8_t>(*current++);
+ const bool is_valid = utf8::validate(first, second);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second});
+ }
+ break;
+ }
+ case 3: {
+ const auto second = static_cast<uint8_t>(*current++);
+ const auto third = static_cast<uint8_t>(*current++);
+ const bool is_valid = utf8::validate(first, second, third);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third});
+ }
+ break;
+ }
+ case 4: {
+ const auto second = static_cast<uint8_t>(*current++);
+ const auto third = static_cast<uint8_t>(*current++);
+ const auto fourth = static_cast<uint8_t>(*current++);
+ const bool is_valid = utf8::validate(first, second, third, fourth);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third, fourth});
+ }
+ break;
+ }
+ default: // LCOV_EXCL_LINE
+ unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+ /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf16() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE);
+
+ int shift_bits[2] {0, 0};
+ if (m_encode_type == utf_encode_t::UTF_16BE) {
+ shift_bits[0] = 8;
+ }
+ else { // m_encode_type == utf_encode_t::UTF_16LE
+ shift_bits[1] = 8;
+ }
+
+ char chars[2] = {0, 0};
+ std::array<char16_t, 2> encoded_buffer {{0, 0}};
+ uint32_t encoded_buf_size {0};
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ while (std::feof(m_file) == 0) {
+ while (encoded_buf_size < 2 && std::fread(&chars[0], sizeof(char), 2, m_file) == 2) {
+ const auto utf16 = static_cast<char16_t>(
+ (static_cast<uint8_t>(chars[0]) << shift_bits[0]) |
+ (static_cast<uint8_t>(chars[1]) << shift_bits[1]));
+ if FK_YAML_LIKELY (utf16 != static_cast<char16_t>(0x000Du)) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
+ encoded_buffer[encoded_buf_size++] = utf16;
+ }
+ }
+
+ uint32_t consumed_size = 0;
+ utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size);
+
+ if FK_YAML_LIKELY (consumed_size == 1) {
+ encoded_buffer[0] = encoded_buffer[1];
+ }
+ encoded_buf_size -= consumed_size;
+
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+ /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf32() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE);
+
+ int shift_bits[4] {0, 0, 0, 0};
+ if (m_encode_type == utf_encode_t::UTF_32BE) {
+ shift_bits[0] = 24;
+ shift_bits[1] = 16;
+ shift_bits[2] = 8;
+ }
+ else { // m_encode_type == utf_encode_t::UTF_32LE
+ shift_bits[1] = 8;
+ shift_bits[2] = 16;
+ shift_bits[3] = 24;
+ }
+
+ char chars[4] = {0, 0, 0, 0};
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ while (std::feof(m_file) == 0) {
+ const std::size_t size = std::fread(&chars[0], sizeof(char), 4, m_file);
+ if (size != 4) {
+ break;
+ }
+
+ const auto utf32 = static_cast<char32_t>(
+ (static_cast<uint8_t>(chars[0]) << shift_bits[0]) | (static_cast<uint8_t>(chars[1]) << shift_bits[1]) |
+ (static_cast<uint8_t>(chars[2]) << shift_bits[2]) | (static_cast<uint8_t>(chars[3]) << shift_bits[3]));
+
+ if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) {
+ utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size);
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+private:
+ /// A pointer to the input file handle.
+ std::FILE* m_file {nullptr};
+ /// The encoding type for this input adapter.
+ utf_encode_t m_encode_type {utf_encode_t::UTF_8};
+ /// The normalized owned buffer.
+ std::string m_buffer;
+};
+
+/// @brief An input adapter for streams
+class stream_input_adapter {
+public:
+ /// @brief Construct a new stream_input_adapter object.
+ stream_input_adapter() = default;
+
+ /// @brief Construct a new stream_input_adapter object.
+ /// @param is A reference to the target input stream.
+ /// @param encode_type The encoding type for this input adapter.
+ explicit stream_input_adapter(std::istream& is, utf_encode_t encode_type) noexcept
+ : m_istream(&is),
+ m_encode_type(encode_type) {
+ }
+
+ // allow only move construct/assignment
+ stream_input_adapter(const stream_input_adapter&) = delete;
+ stream_input_adapter& operator=(const stream_input_adapter&) = delete;
+ stream_input_adapter(stream_input_adapter&&) = default;
+ stream_input_adapter& operator=(stream_input_adapter&&) = default;
+ ~stream_input_adapter() = default;
+
+ /// @brief Get view into the input buffer contents.
+ /// @return View into the input buffer contents.
+ str_view get_buffer_view() {
+ switch (m_encode_type) {
+ case utf_encode_t::UTF_8:
+ return get_buffer_view_utf8();
+ case utf_encode_t::UTF_16BE:
+ case utf_encode_t::UTF_16LE:
+ return get_buffer_view_utf16();
+ case utf_encode_t::UTF_32BE:
+ case utf_encode_t::UTF_32LE:
+ return get_buffer_view_utf32();
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+private:
+ /// @brief The concrete implementation of get_buffer_view() for UTF-8 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf8() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_8);
+
+ m_buffer.clear();
+ char tmp_buf[256] {};
+ do {
+ m_istream->read(&tmp_buf[0], 256);
+ const auto read_size = static_cast<std::size_t>(m_istream->gcount());
+ if FK_YAML_UNLIKELY (read_size == 0) {
+ break;
+ }
+
+ char* p_current = &tmp_buf[0];
+ char* p_end = p_current + read_size;
+
+ // copy tmp_buf to m_buffer, dropping CRs.
+ char* p_cr = p_current;
+ do {
+ if FK_YAML_UNLIKELY (*p_cr == '\r') {
+ m_buffer.append(p_current, p_cr);
+ p_current = p_cr + 1;
+ }
+ ++p_cr;
+ } while (p_cr != p_end);
+
+ m_buffer.append(p_current, p_end);
+ } while (!m_istream->eof());
+
+ if FK_YAML_UNLIKELY (m_buffer.empty()) {
+ return {};
+ }
+
+ auto current = m_buffer.begin();
+ auto end = m_buffer.end();
+ while (current != end) {
+ const auto first = static_cast<uint8_t>(*current++);
+ const uint32_t num_bytes = utf8::get_num_bytes(first);
+
+ switch (num_bytes) {
+ case 1:
+ break;
+ case 2: {
+ const auto second = static_cast<uint8_t>(*current++);
+ const bool is_valid = utf8::validate(first, second);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second});
+ }
+ break;
+ }
+ case 3: {
+ const auto second = static_cast<uint8_t>(*current++);
+ const auto third = static_cast<uint8_t>(*current++);
+ const bool is_valid = utf8::validate(first, second, third);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third});
+ }
+ break;
+ }
+ case 4: {
+ const auto second = static_cast<uint8_t>(*current++);
+ const auto third = static_cast<uint8_t>(*current++);
+ const auto fourth = static_cast<uint8_t>(*current++);
+ const bool is_valid = utf8::validate(first, second, third, fourth);
+ if FK_YAML_UNLIKELY (!is_valid) {
+ throw fkyaml::invalid_encoding("Invalid UTF-8 encoding.", {first, second, third, fourth});
+ }
+ break;
+ }
+ default: // LCOV_EXCL_LINE
+ unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+ /// @brief The concrete implementation of get_buffer_view() for UTF-16 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf16() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_16BE || m_encode_type == utf_encode_t::UTF_16LE);
+
+ int shift_bits[2] {0, 0};
+ if (m_encode_type == utf_encode_t::UTF_16BE) {
+ shift_bits[0] = 8;
+ }
+ else { // m_encode_type == utf_encode_t::UTF_16LE
+ shift_bits[1] = 8;
+ }
+
+ char chars[2] = {0, 0};
+ std::array<char16_t, 2> encoded_buffer {{0, 0}};
+ uint32_t encoded_buf_size {0};
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ do {
+ while (encoded_buf_size < 2) {
+ m_istream->read(&chars[0], 2);
+ const std::streamsize size = m_istream->gcount();
+ if FK_YAML_UNLIKELY (size != 2) {
+ break;
+ }
+
+ const auto utf16 = static_cast<char16_t>(
+ (static_cast<uint8_t>(chars[0]) << shift_bits[0]) |
+ (static_cast<uint8_t>(chars[1]) << shift_bits[1]));
+
+ if FK_YAML_LIKELY (utf16 != static_cast<char16_t>(0x000Du)) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
+ encoded_buffer[encoded_buf_size++] = utf16;
+ }
+ }
+
+ uint32_t consumed_size = 0;
+ utf8::from_utf16(encoded_buffer, utf8_buffer, consumed_size, utf8_buf_size);
+
+ if FK_YAML_LIKELY (consumed_size == 1) {
+ encoded_buffer[0] = encoded_buffer[1];
+ }
+ encoded_buf_size -= consumed_size;
+
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ } while (!m_istream->eof());
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+ /// @brief The concrete implementation of get_buffer_view() for UTF-32 encoded inputs.
+ /// @return View into the UTF-8 encoded input buffer contents.
+ str_view get_buffer_view_utf32() {
+ FK_YAML_ASSERT(m_encode_type == utf_encode_t::UTF_32BE || m_encode_type == utf_encode_t::UTF_32LE);
+
+ int shift_bits[4] {0, 0, 0, 0};
+ if (m_encode_type == utf_encode_t::UTF_32BE) {
+ shift_bits[0] = 24;
+ shift_bits[1] = 16;
+ shift_bits[2] = 8;
+ }
+ else { // m_encode_type == utf_encode_t::UTF_32LE
+ shift_bits[1] = 8;
+ shift_bits[2] = 16;
+ shift_bits[3] = 24;
+ }
+
+ char chars[4] = {0, 0, 0, 0};
+ std::array<uint8_t, 4> utf8_buffer {{0, 0, 0, 0}};
+ uint32_t utf8_buf_size {0};
+
+ do {
+ m_istream->read(&chars[0], 4);
+ const std::streamsize size = m_istream->gcount();
+ if FK_YAML_UNLIKELY (size != 4) {
+ break;
+ }
+
+ const auto utf32 = static_cast<char32_t>(
+ (static_cast<uint8_t>(chars[0]) << shift_bits[0]) | (static_cast<uint8_t>(chars[1]) << shift_bits[1]) |
+ (static_cast<uint8_t>(chars[2]) << shift_bits[2]) | (static_cast<uint8_t>(chars[3]) << shift_bits[3]));
+
+ if FK_YAML_LIKELY (utf32 != char32_t(0x0000000Du)) {
+ utf8::from_utf32(utf32, utf8_buffer, utf8_buf_size);
+ m_buffer.append(reinterpret_cast<const char*>(utf8_buffer.data()), utf8_buf_size);
+ }
+ } while (!m_istream->eof());
+
+ return str_view {m_buffer.begin(), m_buffer.end()};
+ }
+
+private:
+ /// A pointer to the input stream object.
+ std::istream* m_istream {nullptr};
+ /// The encoding type for this input adapter.
+ utf_encode_t m_encode_type {utf_encode_t::UTF_8};
+ /// The normalized owned buffer.
+ std::string m_buffer;
+};
+
+/////////////////////////////////
+// input_adapter providers //
+/////////////////////////////////
+
+/// @brief A concrete factory method for iterator_input_adapter objects with iterators.
+/// @tparam ItrType An iterator type.
+/// @param begin The beginning of iterators.
+/// @param end The end of iterators.
+/// @param is_contiguous Whether iterators refer to a contiguous byte array.
+/// @return An iterator_input_adapter object for the target iterator type.
+template <typename ItrType>
+inline iterator_input_adapter<ItrType> create_iterator_input_adapter(ItrType begin, ItrType end, bool is_contiguous) {
+ const utf_encode_t encode_type = utf_encode_detector<ItrType>::detect(begin, end);
+ return iterator_input_adapter<ItrType>(begin, end, encode_type, is_contiguous);
+}
+
+/// @brief A factory method for iterator_input_adapter objects with iterator values.
+/// @tparam ItrType An iterator type.
+/// @param begin The beginning of iterators.
+/// @param end The end of iterators.
+/// @return iterator_input_adapter<ItrType> An iterator_input_adapter object for the target iterator type.
+template <typename ItrType>
+inline iterator_input_adapter<ItrType> input_adapter(ItrType begin, ItrType end) {
+ bool is_contiguous = true;
+ const auto size = std::distance(begin, end);
+
+ // Check if `begin` & `end` are contiguous iterators.
+ // Getting distance between begin and (end - 1) avoids dereferencing an invalid sentinel.
+ if FK_YAML_LIKELY (size > 0) {
+ using char_ptr_t = remove_cvref_t<typename std::iterator_traits<ItrType>::pointer>;
+ char_ptr_t p_begin = &*begin;
+ char_ptr_t p_second_last = &*std::next(begin, size - 1);
+ is_contiguous = (p_second_last - p_begin == size);
+ }
+ return create_iterator_input_adapter(begin, end, is_contiguous);
+}
+
+/// @brief A factory method for iterator_input_adapter objects with C-style arrays.
+/// @tparam T A type of arrayed objects.
+/// @tparam N A size of an array.
+/// @return decltype(input_adapter(array, array + N)) An iterator_input_adapter object for the target array.
+template <typename T, std::size_t N>
+inline auto input_adapter(T (&array)[N]) -> decltype(create_iterator_input_adapter(array, array + (N - 1), true)) {
+ return create_iterator_input_adapter(array, array + (N - 1), true);
+}
+
+/// @brief A namespace to implement container_input_adapter_factory for internal use.
+namespace input_adapter_factory {
+
+using std::begin;
+using std::end;
+
+/// @brief A factory of input adapters for containers.
+/// @tparam ContainerType A container type.
+/// @tparam typename N/A
+template <typename ContainerType, typename = void>
+struct container_input_adapter_factory {};
+
+/// @brief A partial specialization of container_input_adapter_factory if begin()/end() are available for ContainerType.
+/// @tparam ContainerType A container type.
+template <typename ContainerType>
+struct container_input_adapter_factory<
+ ContainerType, void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>> {
+ /// A type for resulting input adapter object.
+ using adapter_type =
+ decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
+
+ /// @brief A factory method of input adapter objects for the target container objects.
+ /// @param container A container-like input object.
+ /// @return adapter_type An iterator_input_adapter object.
+ static adapter_type create(const ContainerType& container) {
+ return input_adapter(begin(container), end(container));
+ }
+};
+
+} // namespace input_adapter_factory
+
+/// @brief A factory method for iterator_input_adapter objects with containers.
+/// @tparam ContainerType A container type.
+/// @param container A container object.
+/// @return input_adapter_factory::container_input_adapter_factory<ContainerType>::adapter_type
+template <typename ContainerType>
+inline typename input_adapter_factory::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(
+ const ContainerType& container) {
+ return input_adapter_factory::container_input_adapter_factory<ContainerType>::create(container);
+}
+
+/// @brief A factory method for file_input_adapter objects with C-style file handles.
+/// @param file A file handle.
+/// @return file_input_adapter A file_input_adapter object.
+inline file_input_adapter input_adapter(std::FILE* file) {
+ if FK_YAML_UNLIKELY (!file) {
+ throw fkyaml::exception("Invalid FILE object pointer.");
+ }
+
+ const utf_encode_t encode_type = file_utf_encode_detector::detect(file);
+ return file_input_adapter(file, encode_type);
+}
+
+/// @brief A factory method for stream_input_adapter objects with std::istream objects.
+/// @param stream An input stream.
+/// @return stream_input_adapter A stream_input_adapter object.
+inline stream_input_adapter input_adapter(std::istream& stream) {
+ if FK_YAML_UNLIKELY (!stream.good()) {
+ throw fkyaml::exception("Invalid stream.");
+ }
+
+ const utf_encode_t encode_type = stream_utf_encode_detector::detect(stream);
+ return stream_input_adapter(stream, encode_type);
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_INPUT_INPUT_ADAPTER_HPP */
+
+// #include <fkYAML/detail/iterator.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_ITERATOR_HPP
+#define FK_YAML_DETAIL_ITERATOR_HPP
+
+#include <cstddef>
+#include <iterator>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief The template definitions of type information used in @ref Iterator class
+/// @tparam ValueType The type of iterated elements.
+template <typename ValueType>
+struct iterator_traits {
+ /// A type of iterated elements.
+ using value_type = typename ValueType::value_type;
+ /// A type to represent difference between iterators.
+ using difference_type = typename ValueType::difference_type;
+ /// A type of an element pointer.
+ using pointer = typename ValueType::pointer;
+ /// A type of reference to an element.
+ using reference = typename ValueType::reference;
+};
+
+/// @brief A specialization of @ref iterator_traits for constant value types.
+/// @tparam ValueType The type of iterated elements.
+template <typename ValueType>
+struct iterator_traits<const ValueType> {
+ /// A type of iterated elements.
+ using value_type = typename ValueType::value_type;
+ /// A type to represent difference between iterators.
+ using difference_type = typename ValueType::difference_type;
+ /// A type of a constant element pointer.
+ using pointer = typename ValueType::const_pointer;
+ /// A type of constant reference to an element.
+ using reference = typename ValueType::const_reference;
+};
+
+/// @brief Definitions of iterator types for iterators internally held.
+enum class iterator_t : std::uint8_t {
+ SEQUENCE, //!< sequence iterator type.
+ MAPPING, //!< mapping iterator type.
+};
+
+/// @brief The actual storage for iterators internally held in iterator.
+template <typename BasicNodeType>
+struct iterator_holder {
+ static_assert(
+ is_basic_node<BasicNodeType>::value,
+ "iterator_holder class only accepts a basic_node as its template parameter.");
+
+ /// A sequence iterator object.
+ typename BasicNodeType::sequence_type::iterator sequence_iterator {};
+ /// A mapping iterator object.
+ typename BasicNodeType::mapping_type::iterator mapping_iterator {};
+};
+
+/// @brief A class which holds iterators either of sequence or mapping type
+/// @tparam ValueType The type of iterated elements.
+template <typename ValueType>
+class iterator {
+ /// @brief The iterator type with ValueType of different const-ness.
+ using other_iterator_type = typename std::conditional<
+ std::is_const<ValueType>::value, iterator<typename std::remove_const<ValueType>::type>,
+ iterator<const ValueType>>::type;
+
+ friend other_iterator_type;
+
+public:
+ /// A type for iterator traits of instantiated @Iterator template class.
+ using iterator_traits_type = iterator_traits<ValueType>;
+
+ /// A type for iterator category tag.
+ using iterator_category = std::bidirectional_iterator_tag;
+ /// A type of iterated element.
+ using value_type = typename iterator_traits_type::value_type;
+ /// A type to represent differences between iterators.
+ using difference_type = typename iterator_traits_type::difference_type;
+ /// A type of an element pointer.
+ using pointer = typename iterator_traits_type::pointer;
+ /// A type of reference to an element.
+ using reference = typename iterator_traits_type::reference;
+
+ static_assert(is_basic_node<value_type>::value, "iterator class only accepts a basic_node as its value type.");
+
+ /// @brief Constructs an iterator object.
+ iterator() = default;
+
+ /// @brief Construct a new iterator object with sequence iterator object.
+ /// @param[in] itr An sequence iterator object.
+ iterator(const typename value_type::sequence_type::iterator& itr) noexcept {
+ m_iterator_holder.sequence_iterator = itr;
+ }
+
+ /// @brief Construct a new iterator object with mapping iterator object.
+ /// @param[in] itr An mapping iterator object.
+ iterator(const typename value_type::mapping_type::iterator& itr) noexcept
+ : m_inner_iterator_type(iterator_t::MAPPING) {
+ m_iterator_holder.mapping_iterator = itr;
+ }
+
+ /// @brief Copy constructs an iterator.
+ iterator(const iterator&) = default;
+
+ /// @brief Copy constructs an iterator from another iterator with different const-ness in ValueType.
+ /// @note This copy constructor is not defined if ValueType is not const to avoid const removal from ValueType.
+ /// @tparam OtherIterator The iterator type to copy from.
+ /// @param other An iterator to copy from with different const-ness in ValueType.
+ template <
+ typename OtherIterator,
+ enable_if_t<
+ conjunction<std::is_same<OtherIterator, other_iterator_type>, std::is_const<ValueType>>::value, int> = 0>
+ iterator(const OtherIterator& other) noexcept
+ : m_inner_iterator_type(other.m_inner_iterator_type),
+ m_iterator_holder(other.m_iterator_holder) {
+ }
+
+ /// @brief A copy assignment operator of the iterator class.
+ iterator& operator=(const iterator&) = default;
+
+ template <
+ typename OtherIterator,
+ enable_if_t<
+ conjunction<std::is_same<OtherIterator, other_iterator_type>, std::is_const<ValueType>>::value, int> = 0>
+ iterator& operator=(const OtherIterator& other) noexcept {
+ m_inner_iterator_type = other.m_inner_iterator_type;
+ m_iterator_holder = other.m_iterator_holder;
+ return *this;
+ }
+
+ /// @brief Move constructs an iterator.
+ iterator(iterator&&) = default;
+
+ /// @brief A move assignment operator of the iterator class.
+ iterator& operator=(iterator&&) = default;
+
+ /// @brief Destroys an iterator.
+ ~iterator() = default;
+
+ /// @brief An arrow operator of the iterator class.
+ /// @return pointer A pointer to the BasicNodeType object internally referenced by the actual iterator object.
+ pointer operator->() noexcept {
+ if (m_inner_iterator_type == iterator_t::SEQUENCE) {
+ return &(*(m_iterator_holder.sequence_iterator));
+ }
+
+ // m_inner_iterator_type == iterator_t::MAPPING:
+ return &(m_iterator_holder.mapping_iterator->second);
+ }
+
+ /// @brief A dereference operator of the iterator class.
+ /// @return reference Reference to the Node object internally referenced by the actual iterator object.
+ reference operator*() const noexcept {
+ if (m_inner_iterator_type == iterator_t::SEQUENCE) {
+ return *(m_iterator_holder.sequence_iterator);
+ }
+
+ // m_inner_iterator_type == iterator_t::MAPPING:
+ return m_iterator_holder.mapping_iterator->second;
+ }
+
+ /// @brief A compound assignment operator by sum of the Iterator class.
+ /// @param i The difference from this Iterator object with which it moves forward.
+ /// @return Iterator& Reference to this Iterator object.
+ iterator& operator+=(difference_type i) noexcept {
+ switch (m_inner_iterator_type) {
+ case iterator_t::SEQUENCE:
+ std::advance(m_iterator_holder.sequence_iterator, i);
+ break;
+ case iterator_t::MAPPING:
+ std::advance(m_iterator_holder.mapping_iterator, i);
+ break;
+ }
+ return *this;
+ }
+
+ /// @brief A plus operator of the iterator class.
+ /// @param i The difference from this iterator object.
+ /// @return iterator An iterator object which has been added @a i.
+ iterator operator+(difference_type i) const noexcept {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ /// @brief An pre-increment operator of the iterator class.
+ /// @return iterator& Reference to this iterator object.
+ iterator& operator++() noexcept {
+ switch (m_inner_iterator_type) {
+ case iterator_t::SEQUENCE:
+ std::advance(m_iterator_holder.sequence_iterator, 1);
+ break;
+ case iterator_t::MAPPING:
+ std::advance(m_iterator_holder.mapping_iterator, 1);
+ break;
+ }
+ return *this;
+ }
+
+ /// @brief A post-increment operator of the iterator class.
+ /// @return iterator An iterator object which has been incremented.
+ iterator operator++(int) & noexcept {
+ auto result = *this;
+ ++(*this);
+ return result;
+ }
+
+ /// @brief A compound assignment operator by difference of the iterator class.
+ /// @param i The difference from this iterator object with which it moves backward.
+ /// @return iterator& Reference to this iterator object.
+ iterator& operator-=(difference_type i) noexcept {
+ return operator+=(-i);
+ }
+
+ /// @brief A minus operator of the iterator class.
+ /// @param i The difference from this iterator object.
+ /// @return iterator An iterator object from which has been subtracted @ i.
+ iterator operator-(difference_type i) const noexcept {
+ auto result = *this;
+ result -= i;
+ return result;
+ }
+
+ /// @brief A pre-decrement operator of the iterator class.
+ /// @return iterator& Reference to this iterator object.
+ iterator& operator--() noexcept {
+ switch (m_inner_iterator_type) {
+ case iterator_t::SEQUENCE:
+ std::advance(m_iterator_holder.sequence_iterator, -1);
+ break;
+ case iterator_t::MAPPING:
+ std::advance(m_iterator_holder.mapping_iterator, -1);
+ break;
+ }
+ return *this;
+ }
+
+ /// @brief A post-decrement operator of the iterator class
+ /// @return iterator An iterator object which has been decremented.
+ iterator operator--(int) & noexcept {
+ auto result = *this;
+ --(*this);
+ return result;
+ }
+
+ /// @brief An equal-to operator of the iterator class.
+ /// @param rhs An iterator object to be compared with this iterator object.
+ /// @return true This iterator object is equal to the other.
+ /// @return false This iterator object is not equal to the other.
+ template <
+ typename Iterator,
+ enable_if_t<
+ disjunction<std::is_same<Iterator, iterator>, std::is_same<Iterator, other_iterator_type>>::value, int> = 0>
+ bool operator==(const Iterator& rhs) const {
+ if FK_YAML_UNLIKELY (m_inner_iterator_type != rhs.m_inner_iterator_type) {
+ throw fkyaml::exception("Cannot compare iterators of different container types.");
+ }
+
+ if (m_inner_iterator_type == iterator_t::SEQUENCE) {
+ return (m_iterator_holder.sequence_iterator == rhs.m_iterator_holder.sequence_iterator);
+ }
+
+ // m_inner_iterator_type == iterator_t::MAPPING
+ return (m_iterator_holder.mapping_iterator == rhs.m_iterator_holder.mapping_iterator);
+ }
+
+ /// @brief An not-equal-to operator of the iterator class.
+ /// @param rhs An iterator object to be compared with this iterator object.
+ /// @return true This iterator object is not equal to the other.
+ /// @return false This iterator object is equal to the other.
+ template <
+ typename Iterator,
+ enable_if_t<
+ disjunction<std::is_same<Iterator, iterator>, std::is_same<Iterator, other_iterator_type>>::value, int> = 0>
+ bool operator!=(const Iterator& rhs) const {
+ return !operator==(rhs);
+ }
+
+ /// @brief A less-than operator of the iterator class.
+ /// @param rhs An iterator object to be compared with this iterator object.
+ /// @return true This iterator object is less than the other.
+ /// @return false This iterator object is not less than the other.
+ template <
+ typename Iterator,
+ enable_if_t<
+ disjunction<std::is_same<Iterator, iterator>, std::is_same<Iterator, other_iterator_type>>::value, int> = 0>
+ bool operator<(const Iterator& rhs) const {
+ if FK_YAML_UNLIKELY (m_inner_iterator_type != rhs.m_inner_iterator_type) {
+ throw fkyaml::exception("Cannot compare iterators of different container types.");
+ }
+
+ if FK_YAML_UNLIKELY (m_inner_iterator_type == iterator_t::MAPPING) {
+ throw fkyaml::exception("Cannot compare order of iterators of the mapping container type");
+ }
+
+ return (m_iterator_holder.sequence_iterator < rhs.m_iterator_holder.sequence_iterator);
+ }
+
+ /// @brief A less-than-or-equal-to operator of the iterator class.
+ /// @param rhs An iterator object to be compared with this iterator object.
+ /// @return true This iterator object is either less than or equal to the other.
+ /// @return false This iterator object is neither less than nor equal to the other.
+ template <
+ typename Iterator,
+ enable_if_t<
+ disjunction<std::is_same<Iterator, iterator>, std::is_same<Iterator, other_iterator_type>>::value, int> = 0>
+ bool operator<=(const Iterator& rhs) const {
+ return !rhs.operator<(*this);
+ }
+
+ /// @brief A greater-than operator of the iterator class.
+ /// @param rhs An iterator object to be compared with this iterator object.
+ /// @return true This iterator object is greater than the other.
+ /// @return false This iterator object is not greater than the other.
+ template <
+ typename Iterator,
+ enable_if_t<
+ disjunction<std::is_same<Iterator, iterator>, std::is_same<Iterator, other_iterator_type>>::value, int> = 0>
+ bool operator>(const Iterator& rhs) const {
+ return !operator<=(rhs);
+ }
+
+ /// @brief A greater-than-or-equal-to operator of the iterator class.
+ /// @param rhs An iterator object to be compared with this iterator object.
+ /// @return true This iterator object is either greater than or equal to the other.
+ /// @return false This iterator object is neither greater than nor equal to the other.
+ template <
+ typename Iterator,
+ enable_if_t<
+ disjunction<std::is_same<Iterator, iterator>, std::is_same<Iterator, other_iterator_type>>::value, int> = 0>
+ bool operator>=(const Iterator& rhs) const {
+ return !operator<(rhs);
+ }
+
+public:
+ /// @brief Get the type of the internal iterator implementation.
+ /// @return iterator_t The type of the internal iterator implementation.
+ iterator_t type() const noexcept {
+ return m_inner_iterator_type;
+ }
+
+ /// @brief Get the mapping key node of the current iterator.
+ /// @return The mapping key node of the current iterator.
+ const typename value_type::mapping_type::key_type& key() const {
+ if FK_YAML_UNLIKELY (m_inner_iterator_type == iterator_t::SEQUENCE) {
+ throw fkyaml::exception("Cannot retrieve key from non-mapping iterators.");
+ }
+
+ return m_iterator_holder.mapping_iterator->first;
+ }
+
+ /// @brief Get reference to the YAML node of the current iterator.
+ /// @return Reference to the YAML node of the current iterator.
+ reference value() const noexcept {
+ return operator*();
+ }
+
+private:
+ /// A type of the internally-held iterator.
+ iterator_t m_inner_iterator_type {iterator_t::SEQUENCE};
+ /// A holder of actual iterators.
+ iterator_holder<value_type> m_iterator_holder {};
+};
+
+/// @brief Get reference to a mapping key node.
+/// @tparam ValueType The iterator value type.
+/// @tparam I The element index.
+/// @param i An iterator object.
+/// @return Reference to a mapping key node.
+template <std::size_t I, typename ValueType, enable_if_t<I == 0, int> = 0>
+inline auto get(const iterator<ValueType>& i) -> decltype(i.key()) {
+ return i.key();
+}
+
+/// @brief Get reference to a mapping value node.
+/// @tparam ValueType The iterator value type.
+/// @tparam I The element index
+/// @param i An iterator object.
+/// @return Reference to a mapping value node.
+template <std::size_t I, typename ValueType, enable_if_t<I == 1, int> = 0>
+inline auto get(const iterator<ValueType>& i) -> decltype(i.value()) {
+ return i.value();
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+namespace std {
+
+#ifdef __clang__
+// clang emits warnings against mixed usage of class/struct for tuple_size/tuple_element.
+// see also: https://groups.google.com/a/isocpp.org/g/std-discussion/c/QC-AMb5oO1w
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+
+/// @brief Partial specialization of std::tuple_size for iterator class.
+/// @tparam ValueType The iterator value type.
+template <typename ValueType>
+// NOLINTNEXTLINE(cert-dcl58-cpp)
+struct tuple_size<::fkyaml::detail::iterator<ValueType>> : integral_constant<size_t, 2> {};
+
+/// @brief Partial specialization of std::tuple_element for iterator class.
+/// @tparam ValueType The iterator value type.
+/// @tparam I The element index.
+template <size_t I, typename ValueType>
+// NOLINTNEXTLINE(cert-dcl58-cpp)
+struct tuple_element<I, ::fkyaml::detail::iterator<ValueType>> {
+ using type = decltype(get<I>(std::declval<::fkyaml::detail::iterator<ValueType>>()));
+};
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace std
+
+#endif /* FK_YAML_DETAIL_ITERATOR_HPP */
+
+// #include <fkYAML/detail/map_range_proxy.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_MAP_RANGE_PROXY_HPP
+#define FK_YAML_DETAIL_MAP_RANGE_PROXY_HPP
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A helper iterator class which wraps a mapping iterator object.
+/// @tparam Iterator The base iterator type.
+template <typename Iterator>
+class map_iterator_proxy {
+public:
+ /// @brief The type of the pointed-to elements by base iterators.
+ using value_type = Iterator;
+
+ /// @brief The type to represent difference between the pointed-to elements by base iterators.
+ using difference_type = std::ptrdiff_t;
+
+ /// @brief The type of the pointed-to element references by base iterators.
+ using reference = value_type&;
+
+ /// @brief The type of the pointed-to element pointers by base iterators.
+ using pointer = value_type*;
+
+ /// @brief The iterator category.
+ using iterator_category = std::forward_iterator_tag;
+
+ /// @brief Constructs a map_iterator_proxy object.
+ map_iterator_proxy() = default;
+
+ /// @brief Constructs a map_iterator_proxy object with an Iterator object.
+ /// @param i A base iterator object.
+ map_iterator_proxy(const Iterator& i) noexcept
+ : m_base_iterator(i) {
+ }
+
+ /// @brief Copy constructs a map_iterator_proxy object.
+ map_iterator_proxy(const map_iterator_proxy&) = default;
+
+ /// @brief Copy assigns a map_iterator_proxy object.
+ map_iterator_proxy& operator=(const map_iterator_proxy&) = default;
+
+ /// @brief Move constructs a map_iterator_proxy object.
+ map_iterator_proxy(map_iterator_proxy&&) = default;
+
+ /// @brief Move assigns a map_iterator_proxy object.
+ map_iterator_proxy& operator=(map_iterator_proxy&&) = default;
+
+ /// @brief Destructs a map_iterator_proxy object.
+ ~map_iterator_proxy() = default;
+
+ /// @brief Get reference to the base iterator object.
+ /// @return Reference to the base iterator object.
+ reference operator*() noexcept {
+ return m_base_iterator;
+ }
+
+ /// @brief Get pointer to the base iterator object.
+ /// @return Pointer to the base iterator object.
+ pointer operator->() noexcept {
+ return &m_base_iterator;
+ }
+
+ /// @brief Pre-increments the base iterator object.
+ /// @return Reference to this map_iterator_proxy object.
+ map_iterator_proxy& operator++() noexcept {
+ ++m_base_iterator;
+ return *this;
+ }
+
+ /// @brief Post-increments the base iterator object.
+ /// @return A map_iterator_proxy object with its base iterator incremented.
+ map_iterator_proxy operator++(int) & noexcept {
+ auto result = *this;
+ ++(*this);
+ return result;
+ }
+
+ /// @brief Check equality between map_iterator_proxy objects.
+ /// @param rhs A map_iterator_proxy object to compare with.
+ /// @return true if this map_iterator_proxy object is equal to `rhs`, false otherwise.
+ bool operator==(const map_iterator_proxy& rhs) const noexcept {
+ return m_base_iterator == rhs.m_base_iterator;
+ }
+
+ /// @brief Check inequality between map_iterator_proxy objects.
+ /// @param rhs A map_iterator_proxy object to compare with.
+ /// @return true if this map_iterator_proxy object is not equal to `rhs`, false otherwise.
+ bool operator!=(const map_iterator_proxy& rhs) const noexcept {
+ return m_base_iterator != rhs.m_base_iterator;
+ }
+
+ /// @brief Get the mapping key node pointed by the base iterator.
+ /// @return Reference to the mapping key node.
+ typename Iterator::reference key() const {
+ return m_base_iterator.key();
+ }
+
+ /// @brief Get the mapping value node pointed by the base iterator.
+ /// @return Reference to the mapping value node.
+ typename Iterator::reference value() const noexcept {
+ return m_base_iterator.value();
+ }
+
+private:
+ /// The base iterator object.
+ Iterator m_base_iterator {};
+};
+
+/// @brief A helper struct which allows accessing node iterator member functions in range-based for loops.
+/// @tparam BasicNodeType A basic_node template instance type.
+template <typename BasicNodeType>
+class map_range_proxy {
+ static_assert(
+ is_basic_node<BasicNodeType>::value,
+ "map_range_proxy only accepts a basic_node type as its template parameter.");
+
+public:
+ /// @brief The type of non-const iterators.
+ using iterator = map_iterator_proxy<typename std::conditional<
+ std::is_const<BasicNodeType>::value, typename BasicNodeType::const_iterator,
+ typename BasicNodeType::iterator>::type>;
+
+ /// @brief The type of const iterators.
+ using const_iterator = map_iterator_proxy<typename BasicNodeType::const_iterator>;
+
+ /// @brief Constructs a map_range_proxy object with a BasicNodeType object.
+ /// @param map A mapping node object.
+ map_range_proxy(BasicNodeType& map) noexcept
+ : mp_map(&map) {
+ }
+
+ /// @brief Copy constructs a map_range_proxy object.
+ map_range_proxy(const map_range_proxy&) = default;
+
+ /// @brief Copy assigns a map_range_proxy object.
+ /// @return Reference to this map_range_proxy object.
+ map_range_proxy& operator=(const map_range_proxy&) = default;
+
+ /// @brief Move constructs a map_range_proxy object.
+ map_range_proxy(map_range_proxy&&) = default;
+
+ /// @brief Move assigns a map_range_proxy object.
+ /// @return Reference to this map_range_proxy object.
+ map_range_proxy& operator=(map_range_proxy&&) = default;
+
+ /// @brief Destructs a map_range_proxy object.
+ ~map_range_proxy() = default;
+
+ /// @brief Get an iterator to the first element.
+ /// @return An iterator to the first element.
+ iterator begin() noexcept {
+ return {mp_map->begin()};
+ }
+
+ /// @brief Get a const iterator to the first element.
+ /// @return A const iterator to the first element.
+ const_iterator begin() const noexcept {
+ return {mp_map->cbegin()};
+ }
+
+ /// @brief Get an iterator to the past-the-last element.
+ /// @return An iterator to the past-the-last element.
+ iterator end() noexcept {
+ return {mp_map->end()};
+ }
+
+ /// @brief Get a const iterator to the past-the-last element.
+ /// @return A const iterator to the past-the-last element.
+ const_iterator end() const noexcept {
+ return {mp_map->cend()};
+ }
+
+private:
+ /// Pointer to the mapping node object. (non-null)
+ BasicNodeType* mp_map {nullptr};
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_MAP_RANGE_PROXY_HPP */
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+// #include <fkYAML/detail/node_attrs.hpp>
+
+// #include <fkYAML/detail/node_property.hpp>
+
+// #include <fkYAML/detail/node_ref_storage.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_NODE_REF_STORAGE_HPP
+#define FK_YAML_DETAIL_NODE_REF_STORAGE_HPP
+
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A temporal storage for basic_node class objects.
+/// @note This class makes it easier to handle lvalue basic_node objects in basic_node ctor with std::initializer_list.
+/// @tparam BasicNodeType A basic_node template instance type.
+template <typename BasicNodeType>
+class node_ref_storage {
+ static_assert(is_basic_node<BasicNodeType>::value, "node_ref_storage only accepts basic_node<...>");
+
+ using node_type = BasicNodeType;
+
+public:
+ /// @brief Construct a new node ref storage object with an rvalue basic_node object.
+ /// @param n An rvalue basic_node object.
+ explicit node_ref_storage(node_type&& n) noexcept(std::is_nothrow_move_constructible<node_type>::value)
+ : m_owned_value(std::move(n)) {
+ }
+
+ /// @brief Construct a new node ref storage object with an lvalue basic_node object.
+ /// @param n An lvalue basic_node object.
+ explicit node_ref_storage(const node_type& n) noexcept
+ : m_value_ref(&n) {
+ }
+
+ /// @brief Construct a new node ref storage object with a std::initializer_list object.
+ /// @param init A std::initializer_list object.
+ node_ref_storage(std::initializer_list<node_ref_storage> init)
+ : m_owned_value(init) {
+ }
+
+ /// @brief Construct a new node ref storage object with variadic template arguments
+ /// @tparam Args Types of arguments to construct a basic_node object.
+ /// @param args Arguments to construct a basic_node object.
+ template <typename... Args, enable_if_t<std::is_constructible<node_type, Args...>::value, int> = 0>
+ node_ref_storage(Args&&... args)
+ : m_owned_value(std::forward<Args>(args)...) {
+ }
+
+ // allow only move construct/assignment
+ node_ref_storage(const node_ref_storage&) = delete;
+ node_ref_storage(node_ref_storage&&) = default;
+ node_ref_storage& operator=(const node_ref_storage&) = delete;
+ node_ref_storage& operator=(node_ref_storage&&) = default;
+
+ ~node_ref_storage() = default;
+
+public:
+ /// @brief An arrow operator for node_ref_storage objects.
+ /// @return const node_type* A constant pointer to a basic_node object.
+ const node_type* operator->() const noexcept {
+ return m_value_ref ? m_value_ref : &m_owned_value;
+ }
+
+ /// @brief Releases a basic_node object internally held.
+ /// @return node_type A basic_node object internally held.
+ node_type release() const noexcept {
+ return m_value_ref ? *m_value_ref : std::move(m_owned_value);
+ }
+
+private:
+ /// A storage for a basic_node object given with rvalue reference.
+ mutable node_type m_owned_value = nullptr;
+ /// A pointer to a basic_node object given with lvalue reference.
+ const node_type* m_value_ref = nullptr;
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_NODE_REF_STORAGE_HPP */
+
+// #include <fkYAML/detail/output/serializer.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_OUTPUT_SERIALIZER_HPP
+#define FK_YAML_DETAIL_OUTPUT_SERIALIZER_HPP
+
+#include <cmath>
+#include <sstream>
+#include <string>
+#include <vector>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/conversions/to_string.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_CONVERSIONS_TO_STRING_HPP
+#define FK_YAML_DETAIL_CONVERSIONS_TO_STRING_HPP
+
+#include <cmath>
+#include <limits>
+#include <string>
+#include <sstream>
+#include <type_traits>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Converts a ValueType object to a string YAML token.
+/// @tparam ValueType A source value type.
+/// @tparam CharType The type of characters for the conversion result.
+/// @param s A resulting output string.
+/// @param v A source value.
+template <typename ValueType, typename CharType>
+inline void to_string(ValueType v, std::basic_string<CharType>& s) noexcept;
+
+/// @brief Specialization of to_string() for null values.
+/// @param s A resulting string YAML token.
+/// @param (unused) nullptr
+template <>
+inline void to_string(std::nullptr_t /*unused*/, std::string& s) noexcept {
+ s = "null";
+}
+
+/// @brief Specialization of to_string() for booleans.
+/// @param s A resulting string YAML token.
+/// @param v A boolean source value.
+template <>
+inline void to_string(bool v, std::string& s) noexcept {
+ s = v ? "true" : "false";
+}
+
+/// @brief Specialization of to_string() for integers.
+/// @tparam IntegerType An integer type.
+/// @param s A resulting string YAML token.
+/// @param i An integer source value.
+template <typename IntegerType>
+inline enable_if_t<is_non_bool_integral<IntegerType>::value> to_string(IntegerType v, std::string& s) noexcept {
+ s = std::to_string(v);
+}
+
+/// @brief Specialization of to_string() for floating point numbers.
+/// @tparam FloatType A floating point number type.
+/// @param s A resulting string YAML token.
+/// @param f A floating point number source value.
+template <typename FloatType>
+inline enable_if_t<std::is_floating_point<FloatType>::value> to_string(FloatType v, std::string& s) noexcept {
+ if (std::isnan(v)) {
+ s = ".nan";
+ return;
+ }
+
+ if (std::isinf(v)) {
+ if (v == std::numeric_limits<FloatType>::infinity()) {
+ s = ".inf";
+ }
+ else {
+ s = "-.inf";
+ }
+ return;
+ }
+
+ std::ostringstream oss;
+ oss << v;
+ s = oss.str();
+
+ // If `v` is actually an integer and no scientific notation is used for serialization, ".0" must be appended.
+ // The result would cause a roundtrip issue otherwise. https://github.com/fktn-k/fkYAML/issues/405
+ const std::size_t pos = s.find_first_of(".e");
+ if (pos == std::string::npos) {
+ s += ".0";
+ }
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_CONVERSIONS_TO_STRING_HPP */
+
+// #include <fkYAML/detail/encodings/yaml_escaper.hpp>
+
+// #include <fkYAML/detail/input/scalar_scanner.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+// #include <fkYAML/node_type.hpp>
+
+// #include <fkYAML/yaml_version_type.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief A basic implementation of serialization feature for YAML nodes.
+/// @tparam BasicNodeType A BasicNode template class instantiation.
+template <typename BasicNodeType>
+class basic_serializer {
+ static_assert(detail::is_basic_node<BasicNodeType>::value, "basic_serializer only accepts basic_node<...>");
+
+public:
+ /// @brief Construct a new basic_serializer object.
+ basic_serializer() = default;
+
+ /// @brief Serialize the given Node value.
+ /// @param node A Node object to be serialized.
+ /// @return std::string A serialization result of the given Node value.
+ std::string serialize(const BasicNodeType& node) {
+ std::string str {};
+ serialize_document(node, str);
+ return str;
+ } // LCOV_EXCL_LINE
+
+ std::string serialize_docs(const std::vector<BasicNodeType>& docs) {
+ std::string str {};
+
+ const auto size = static_cast<uint32_t>(docs.size());
+ for (uint32_t i = 0; i < size; i++) {
+ serialize_document(docs[i], str);
+ if (i + 1 < size) {
+ // Append the end-of-document marker for the next document.
+ str += "...\n";
+ }
+ }
+
+ return str;
+ } // LCOV_EXCL_LINE
+
+private:
+ void serialize_document(const BasicNodeType& node, std::string& str) {
+ const bool dirs_serialized = serialize_directives(node, str);
+
+ // the root node cannot be an alias node.
+ const bool root_has_props = node.is_anchor() || node.has_tag_name();
+
+ if (root_has_props) {
+ if (dirs_serialized) {
+ str.back() = ' '; // replace the last LF with a white space
+ }
+ bool is_anchor_appended = try_append_anchor(node, false, str);
+ try_append_tag(node, is_anchor_appended, str);
+ str += "\n";
+ }
+ serialize_node(node, 0, str);
+ }
+
+ /// @brief Serialize the directives if any is applied to the node.
+ /// @param node The target node.
+ /// @param str A string to hold serialization result.
+ /// @return bool true if any directive is serialized, false otherwise.
+ bool serialize_directives(const BasicNodeType& node, std::string& str) {
+ const auto& p_meta = node.mp_meta;
+ bool needs_directive_end = false;
+
+ if (p_meta->is_version_specified) {
+ str += "%YAML ";
+ switch (p_meta->version) {
+ case yaml_version_type::VERSION_1_1:
+ str += "1.1\n";
+ break;
+ case yaml_version_type::VERSION_1_2:
+ str += "1.2\n";
+ break;
+ }
+ needs_directive_end = true;
+ }
+
+ if (!p_meta->primary_handle_prefix.empty()) {
+ str += "%TAG ! ";
+ str += p_meta->primary_handle_prefix;
+ str += "\n";
+ needs_directive_end = true;
+ }
+
+ if (!p_meta->secondary_handle_prefix.empty()) {
+ str += "%TAG !! ";
+ str += p_meta->secondary_handle_prefix;
+ str += "\n";
+ needs_directive_end = true;
+ }
+
+ if (!p_meta->named_handle_map.empty()) {
+ for (const auto& itr : p_meta->named_handle_map) {
+ str += "%TAG ";
+ str += itr.first;
+ str += " ";
+ str += itr.second;
+ str += "\n";
+ }
+ needs_directive_end = true;
+ }
+
+ if (needs_directive_end) {
+ str += "---\n";
+ }
+
+ return needs_directive_end;
+ }
+
+ /// @brief Recursively serialize each Node object.
+ /// @param node A Node object to be serialized.
+ /// @param cur_indent The current indent width
+ /// @param str A string to hold serialization result.
+ void serialize_node(const BasicNodeType& node, const uint32_t cur_indent, std::string& str) {
+ switch (node.get_type()) {
+ case node_type::SEQUENCE:
+ if (node.size() == 0) {
+ str += "[]\n";
+ return;
+ }
+ for (const auto& seq_item : node) {
+ insert_indentation(cur_indent, str);
+ str += "-";
+
+ const bool is_appended = try_append_alias(seq_item, true, str);
+ if (is_appended) {
+ str += "\n";
+ continue;
+ }
+
+ try_append_anchor(seq_item, true, str);
+ try_append_tag(seq_item, true, str);
+
+ const bool is_scalar = seq_item.is_scalar();
+ if (is_scalar) {
+ str += " ";
+ serialize_node(seq_item, cur_indent, str);
+ str += "\n";
+ continue;
+ }
+
+ const bool is_empty = seq_item.empty();
+ if (!is_empty) {
+ str += "\n";
+ serialize_node(seq_item, cur_indent + 2, str);
+ continue;
+ }
+
+ // an empty sequence or mapping
+ if (seq_item.is_sequence()) {
+ str += " []\n";
+ }
+ else /*seq_item.is_mapping()*/ {
+ str += " {}\n";
+ }
+ }
+ break;
+ case node_type::MAPPING:
+ if (node.size() == 0) {
+ str += "{}\n";
+ return;
+ }
+ for (auto itr : node.map_items()) {
+ insert_indentation(cur_indent, str);
+
+ // serialize a mapping key node.
+ const auto& key_node = itr.key();
+
+ bool is_appended = try_append_alias(key_node, false, str);
+ if (is_appended) {
+ // The trailing white space is necessary since anchor names can contain a colon (:) at its end.
+ str += " ";
+ }
+ else {
+ const bool is_anchor_appended = try_append_anchor(key_node, false, str);
+ const bool is_tag_appended = try_append_tag(key_node, is_anchor_appended, str);
+ if (is_anchor_appended || is_tag_appended) {
+ str += " ";
+ }
+
+ const bool is_container = !key_node.is_scalar();
+ if (is_container) {
+ str += "? ";
+ }
+ const auto indent = static_cast<uint32_t>(get_cur_indent(str));
+ serialize_node(key_node, indent, str);
+ if (is_container) {
+ // a newline code is already inserted in the above serialize_node() call.
+ insert_indentation(indent - 2, str);
+ }
+ }
+
+ str += ":";
+
+ // serialize a mapping value node.
+ const auto& value_node = itr.value();
+
+ is_appended = try_append_alias(value_node, true, str);
+ if (is_appended) {
+ str += "\n";
+ continue;
+ }
+
+ try_append_anchor(value_node, true, str);
+ try_append_tag(value_node, true, str);
+
+ const bool is_scalar = itr->is_scalar();
+ if (is_scalar) {
+ str += " ";
+ serialize_node(value_node, cur_indent, str);
+ str += "\n";
+ continue;
+ }
+
+ const bool is_empty = itr->empty();
+ if (is_empty) {
+ str += " ";
+ }
+ else {
+ str += "\n";
+ }
+ serialize_node(value_node, cur_indent + 2, str);
+ }
+ break;
+ case node_type::NULL_OBJECT:
+ to_string(nullptr, m_tmp_str_buff);
+ str += m_tmp_str_buff;
+ break;
+ case node_type::BOOLEAN:
+ to_string(node.template get_value<typename BasicNodeType::boolean_type>(), m_tmp_str_buff);
+ str += m_tmp_str_buff;
+ break;
+ case node_type::INTEGER:
+ to_string(node.template get_value<typename BasicNodeType::integer_type>(), m_tmp_str_buff);
+ str += m_tmp_str_buff;
+ break;
+ case node_type::FLOAT:
+ to_string(node.template get_value<typename BasicNodeType::float_number_type>(), m_tmp_str_buff);
+ str += m_tmp_str_buff;
+ break;
+ case node_type::STRING: {
+ bool is_escaped = false;
+ auto str_val = get_string_node_value(node, is_escaped);
+
+ if (is_escaped) {
+ // There's no other token type with escapes than strings.
+ // Also, escapes must be in double-quoted strings.
+ str += '\"';
+ str += str_val;
+ str += '\"';
+ break;
+ }
+
+ // The next line is intentionally excluded from the LCOV coverage target since the next line is somehow
+ // misrecognized as it has a binary branch. Possibly begin() or end() has some conditional branch(es)
+ // internally. Confirmed with LCOV 1.14 on Ubuntu22.04.
+ const node_type type_if_plain =
+ scalar_scanner::scan(str_val.c_str(), str_val.c_str() + str_val.size()); // LCOV_EXCL_LINE
+
+ if (type_if_plain != node_type::STRING) {
+ // Surround a string value with double quotes to keep semantic equality.
+ // Without them, serialized values will become non-string. (e.g., "1" -> 1)
+ str += '\"';
+ str += str_val;
+ str += '\"';
+ }
+ else {
+ str += str_val;
+ }
+ break;
+ }
+ }
+ }
+
+ /// @brief Get the current indentation width.
+ /// @param s The target string object.
+ /// @return The current indentation width.
+ std::size_t get_cur_indent(const std::string& s) const noexcept {
+ const bool is_empty = s.empty();
+ if (is_empty) {
+ return 0;
+ }
+
+ const std::size_t last_lf_pos = s.rfind('\n');
+ return (last_lf_pos != std::string::npos) ? s.size() - last_lf_pos - 1 : s.size();
+ }
+
+ /// @brief Insert indentation to the serialization result.
+ /// @param indent The indent width to be inserted.
+ /// @param str A string to hold serialization result.
+ void insert_indentation(const uint32_t indent, std::string& str) const noexcept {
+ if (indent == 0) {
+ return;
+ }
+
+ str.append(indent - get_cur_indent(str), ' ');
+ }
+
+ /// @brief Append an anchor property if it's available. Do nothing otherwise.
+ /// @param node The target node which is possibly an anchor node.
+ /// @param prepends_space Whether to prepend a space before an anchor property.
+ /// @param str A string to hold serialization result.
+ /// @return true if an anchor property has been appended, false otherwise.
+ bool try_append_anchor(const BasicNodeType& node, bool prepends_space, std::string& str) const {
+ if (node.is_anchor()) {
+ if (prepends_space) {
+ str += " ";
+ }
+ str += "&" + node.get_anchor_name();
+ return true;
+ }
+ return false;
+ }
+
+ /// @brief Append an alias property if it's available. Do nothing otherwise.
+ /// @param node The target node which is possibly an alias node.
+ /// @param prepends_space Whether to prepend a space before an alias property.
+ /// @param str A string to hold serialization result.
+ /// @return true if an alias property has been appended, false otherwise.
+ bool try_append_alias(const BasicNodeType& node, bool prepends_space, std::string& str) const {
+ if (node.is_alias()) {
+ if (prepends_space) {
+ str += " ";
+ }
+ str += "*" + node.get_anchor_name();
+ return true;
+ }
+ return false;
+ }
+
+ /// @brief Append a tag name if it's available. Do nothing otherwise.
+ /// @param[in] node The target node which possibly has a tag name.
+ /// @param[out] str A string to hold serialization result.
+ /// @return true if a tag name has been appended, false otherwise.
+ bool try_append_tag(const BasicNodeType& node, bool prepends_space, std::string& str) const {
+ if (node.has_tag_name()) {
+ if (prepends_space) {
+ str += " ";
+ }
+ str += node.get_tag_name();
+ return true;
+ }
+ return false;
+ }
+
+ /// @brief Get a string value from the given node and, if necessary, escape its contents.
+ /// @param[in] node The target string YAML node.
+ /// @param[out] is_escaped Whether the contents of an output string has been escaped.
+ /// @return The (escaped) string node value.
+ typename BasicNodeType::string_type get_string_node_value(const BasicNodeType& node, bool& is_escaped) {
+ FK_YAML_ASSERT(node.is_string());
+
+ const auto& s = node.as_str();
+ return yaml_escaper::escape(s.c_str(), s.c_str() + s.size(), is_escaped);
+ } // LCOV_EXCL_LINE
+
+private:
+ /// A temporal buffer for conversion from a scalar to a string.
+ std::string m_tmp_str_buff;
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_OUTPUT_SERIALIZER_HPP */
+
+// #include <fkYAML/detail/reverse_iterator.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_REVERSE_ITERATOR_HPP
+#define FK_YAML_DETAIL_REVERSE_ITERATOR_HPP
+
+#include <iterator>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief An iterator adapter class that reverses the direction of a given node iterator.
+/// @tparam Iterator The base iterator type.
+template <typename Iterator>
+class reverse_iterator {
+ static_assert(
+ is_basic_node<typename Iterator::value_type>::value,
+ "reverse_iterator only accepts a basic_node type as the underlying iterator's value type");
+
+public:
+ /// @brief The base iterator type.
+ using iterator_type = Iterator;
+
+ /// @brief The base iterator category.
+ using iterator_category = typename Iterator::iterator_category;
+
+ /// @brief The type of the pointed-to elements by base iterators.
+ using value_type = typename Iterator::value_type;
+
+ /// @brief The type to represent differences between the pointed-to elements by the base iterators.
+ using difference_type = typename Iterator::difference_type;
+
+ /// @brief The type of the pointed-to element pointers by base iterators.
+ using pointer = typename Iterator::pointer;
+
+ /// @brief The type of the pointed-to element references by base iterators.
+ using reference = typename Iterator::reference;
+
+ /// @brief Constructs a reverse_iterator object.
+ reverse_iterator() = default;
+
+ /// @brief Copy constructs a reverse_iterator object.
+ reverse_iterator(const reverse_iterator&) = default;
+
+ /// @brief Copy assignments a reverse_iterator object.
+ reverse_iterator& operator=(const reverse_iterator&) = default;
+
+ /// @brief Move constructs a reverse_iterator object.
+ reverse_iterator(reverse_iterator&&) = default;
+
+ /// @brief Move assignments a reverse_iterator object.
+ reverse_iterator& operator=(reverse_iterator&&) = default;
+
+ /// @brief Constructs a reverse_iterator object with an underlying iterator object.
+ /// @param i A base iterator object.
+ reverse_iterator(const Iterator& i) noexcept
+ : m_current(i) {
+ }
+
+ /// @brief Copy constructs a reverse_iterator object with a compatible reverse_iterator object.
+ /// @tparam U A compatible iterator type with Iterator.
+ /// @param other A compatible reverse_iterator object.
+ template <typename U, enable_if_t<negation<std::is_same<U, Iterator>>::value, int> = 0>
+ reverse_iterator(const reverse_iterator<U>& other) noexcept
+ : m_current(other.base()) {
+ }
+
+ /// @brief Copy assigns a reverse_iterator object with a compatible reverse_iterator object.
+ /// @tparam U A compatible iterator type with Iterator.
+ /// @param other A compatible reverse_iterator object.
+ /// @return Reference to this reverse_iterator object.
+ template <typename U, enable_if_t<negation<std::is_same<U, Iterator>>::value, int> = 0>
+ reverse_iterator& operator=(const reverse_iterator<U>& other) noexcept {
+ m_current = other.base();
+ return *this;
+ }
+
+ /// @brief Destructs a reverse_iterator object.
+ ~reverse_iterator() = default;
+
+ /// @brief Accesses the underlying iterator object.
+ /// @return The underlying iterator object.
+ Iterator base() const noexcept {
+ return m_current;
+ }
+
+ /// @brief Get reference to the pointed-to element.
+ /// @return Reference to the pointed-to element.
+ reference operator*() const noexcept {
+ Iterator tmp = m_current;
+ return *--tmp;
+ }
+
+ /// @brief Get pointer to the pointed-to element.
+ /// @return Pointer to the pointed-to element.
+ pointer operator->() const noexcept {
+ return &(operator*());
+ }
+
+ /// @brief Pre-increments the underlying iterator object.
+ /// @return Reference to this reverse_iterator object with its underlying iterator incremented.
+ reverse_iterator& operator++() noexcept {
+ --m_current;
+ return *this;
+ }
+
+ /// @brief Post-increments the underlying iterator object.
+ /// @return A reverse_iterator object with the underlying iterator as-is.
+ reverse_iterator operator++(int) & noexcept {
+ auto result = *this;
+ --m_current;
+ return result;
+ }
+
+ /// @brief Pre-decrements the underlying iterator object.
+ /// @return Reference to this reverse_iterator with its underlying iterator decremented.
+ reverse_iterator& operator--() noexcept {
+ ++m_current;
+ return *this;
+ }
+
+ /// @brief Post-decrements the underlying iterator object.
+ /// @return A reverse_iterator object with the underlying iterator as-is.
+ reverse_iterator operator--(int) & noexcept {
+ auto result = *this;
+ ++m_current;
+ return result;
+ }
+
+ /// @brief Advances the underlying iterator object by `n`.
+ /// @param n The distance by which the underlying iterator is advanced.
+ /// @return A reverse_iterator object with the underlying iterator advanced by `n`.
+ reverse_iterator operator+(difference_type n) const noexcept {
+ return reverse_iterator(m_current - n);
+ }
+
+ /// @brief Advances the underlying iterator object by `n`.
+ /// @param n The distance by which the underlying iterator is advanced.
+ /// @return Reference to this reverse_iterator object with the underlying iterator advanced by `n`.
+ reverse_iterator& operator+=(difference_type n) noexcept {
+ m_current -= n;
+ return *this;
+ }
+
+ /// @brief Decrements the underlying iterator object by `n`.
+ /// @param n The distance by which the underlying iterator is decremented.
+ /// @return A reverse_iterator object with the underlying iterator decremented by `n`.
+ reverse_iterator operator-(difference_type n) const noexcept {
+ return reverse_iterator(m_current + n);
+ }
+
+ /// @brief Decrements the underlying iterator object by `n`.
+ /// @param n The distance by which the underlying iterator is decremented.
+ /// @return Reference to this reverse_iterator object with the underlying iterator decremented by `n`.
+ reverse_iterator& operator-=(difference_type n) noexcept {
+ m_current += n;
+ return *this;
+ }
+
+ /// @brief Get the mapping key node of the underlying iterator.
+ /// @return The mapping key node of the underlying iterator.
+ auto key() const -> decltype(std::declval<Iterator>().key()) {
+ Iterator itr = --(base());
+ return itr.key();
+ }
+
+ /// @brief Get reference to the underlying iterator's value.
+ /// @return Reference to the underlying iterator's value.
+ reference value() noexcept {
+ Iterator itr = --(base());
+ return *itr;
+ }
+
+private:
+ ///
+ Iterator m_current;
+};
+
+/// @brief Check equality between reverse_iterator objects.
+/// @tparam IteratorL Base iterator type for `lhs`.
+/// @tparam IteratorR Base iterator type for `rhs`.
+/// @param lhs A reverse_iterator object.
+/// @param rhs A reverse_iterator object.
+/// @return true if the two reverse_iterator objects are equal, false otherwise.
+template <typename IteratorL, typename IteratorR>
+inline bool operator==(const reverse_iterator<IteratorL>& lhs, const reverse_iterator<IteratorR>& rhs) {
+ return lhs.base() == rhs.base();
+}
+
+/// @brief Check inequality between reverse_iterator objects.
+/// @tparam IteratorL Base iterator type for `lhs`.
+/// @tparam IteratorR Base iterator type for `rhs`.
+/// @param lhs A reverse_iterator object.
+/// @param rhs A reverse_iterator object.
+/// @return true if the two reverse_iterator objects are not equal, false otherwise.
+template <typename IteratorL, typename IteratorR>
+inline bool operator!=(const reverse_iterator<IteratorL>& lhs, const reverse_iterator<IteratorR>& rhs) {
+ return lhs.base() != rhs.base();
+}
+
+/// @brief Check if `lhs` is less than `rhs`.
+/// @tparam IteratorL Base iterator type for `lhs`.
+/// @tparam IteratorR Base iterator type for `rhs`.
+/// @param lhs A reverse_iterator object.
+/// @param rhs A reverse_iterator object.
+/// @return true if `lhs` is less than `rhs`, false otherwise.
+template <typename IteratorL, typename IteratorR>
+inline bool operator<(const reverse_iterator<IteratorL>& lhs, const reverse_iterator<IteratorR>& rhs) {
+ return lhs.base() > rhs.base();
+}
+
+/// @brief Check if `lhs` is less than or equal to `rhs`.
+/// @tparam IteratorL Base iterator type for `lhs`.
+/// @tparam IteratorR Base iterator type for `rhs`.
+/// @param lhs A reverse_iterator object.
+/// @param rhs A reverse_iterator object.
+/// @return true if `lhs` is less than or equal to `rhs`, false otherwise.
+template <typename IteratorL, typename IteratorR>
+inline bool operator<=(const reverse_iterator<IteratorL>& lhs, const reverse_iterator<IteratorR>& rhs) {
+ return lhs.base() >= rhs.base();
+}
+
+/// @brief Check if `lhs` is greater than `rhs`.
+/// @tparam IteratorL Base iterator type for `lhs`.
+/// @tparam IteratorR Base iterator type for `rhs`.
+/// @param lhs A reverse_iterator object.
+/// @param rhs A reverse_iterator object.
+/// @return true if `lhs` is greater than `rhs`, false otherwise.
+template <typename IteratorL, typename IteratorR>
+inline bool operator>(const reverse_iterator<IteratorL>& lhs, const reverse_iterator<IteratorR>& rhs) {
+ return lhs.base() < rhs.base();
+}
+
+/// @brief Check if `lhs` is greater than or equal to `rhs`.
+/// @tparam IteratorL Base iterator type for `lhs`.
+/// @tparam IteratorR Base iterator type for `rhs`.
+/// @param lhs A reverse_iterator object.
+/// @param rhs A reverse_iterator object.
+/// @return true if `lhs` is greater than or equal to `rhs`, false otherwise.
+template <typename IteratorL, typename IteratorR>
+inline bool operator>=(const reverse_iterator<IteratorL>& lhs, const reverse_iterator<IteratorR>& rhs) {
+ return lhs.base() <= rhs.base();
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_REVERSE_ITERATOR_HPP */
+
+// #include <fkYAML/detail/types/node_t.hpp>
+
+// #include <fkYAML/detail/types/yaml_version_t.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP
+#define FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP
+
+#include <cstdint>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/yaml_version_type.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+/// @brief Definition of YAML version types.
+enum class yaml_version_t : std::uint8_t {
+ VER_1_1, //!< YAML version 1.1
+ VER_1_2, //!< YAML version 1.2
+};
+
+inline yaml_version_t convert_from_yaml_version_type(yaml_version_type t) noexcept {
+ switch (t) {
+ case yaml_version_type::VERSION_1_1:
+ return yaml_version_t::VER_1_1;
+ case yaml_version_type::VERSION_1_2:
+ default:
+ return yaml_version_t::VER_1_2;
+ }
+}
+
+inline yaml_version_type convert_to_yaml_version_type(yaml_version_t t) noexcept {
+ switch (t) {
+ case yaml_version_t::VER_1_1:
+ return yaml_version_type::VERSION_1_1;
+ case yaml_version_t::VER_1_2:
+ default:
+ return yaml_version_type::VERSION_1_2;
+ }
+}
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_TYPES_YAML_VERSION_T_HPP */
+
+// #include <fkYAML/exception.hpp>
+
+// #include <fkYAML/node_type.hpp>
+
+// #include <fkYAML/node_value_converter.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_NODE_VALUE_CONVERTER_HPP
+#define FK_YAML_NODE_VALUE_CONVERTER_HPP
+
+#include <utility>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/conversions/from_node.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_CONVERSIONS_FROM_NODE_HPP
+#define FK_YAML_DETAIL_CONVERSIONS_FROM_NODE_HPP
+
+#include <array>
+#include <cmath>
+#include <forward_list>
+#include <limits>
+#include <utility>
+#include <valarray>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+// #include <fkYAML/detail/types/node_t.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+#ifdef FK_YAML_HAS_CXX_17
+#include <optional>
+#endif
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+///////////////////
+// from_node //
+///////////////////
+
+// utility type traits and functors
+
+/// @brief Utility traits type alias to detect constructible associative container types from a mapping node, e.g.,
+/// std::map or std::unordered_map.
+/// @tparam T A target type for detection.
+template <typename T>
+using is_constructible_mapping_type =
+ conjunction<detect::has_key_type<T>, detect::has_mapped_type<T>, detect::has_value_type<T>>;
+
+/// @brief Utility traits type alias to detect constructible container types from a sequence node, e.g., std::vector or
+/// std::list.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A target type for detection.
+template <typename BasicNodeType, typename T>
+using is_constructible_sequence_type = conjunction<
+ negation<is_basic_node<T>>, detect::has_iterator<T>, detect::is_iterator_traits<typename T::iterator>,
+ detect::has_begin_end<T>, negation<std::is_same<T, typename BasicNodeType::mapping_type>>,
+ negation<is_constructible_mapping_type<T>>>;
+
+/// @brief Utility traits type alias to detect a sequence container adapter type, e.g., std::stack or std::queue.
+/// @tparam T A target type for detection.
+template <typename T>
+using is_sequence_container_adapter = conjunction<
+ negation<is_basic_node<T>>, detect::has_container_type<T>, detect::has_value_type<T>,
+ negation<detect::has_key_type<T>>>;
+
+/// @brief Helper struct for reserve() member function call switch for types which do not have reserve function.
+/// @tparam ContainerType A container type.
+template <typename ContainerType, typename = void>
+struct call_reserve_if_available {
+ /// @brief Do nothing since ContainerType does not have reserve function.
+ static void call(ContainerType& /*unused*/, typename ContainerType::size_type /*unused*/) {
+ }
+};
+
+/// @brief Helper struct for reserve() member function call switch for types which have reserve function.
+/// @tparam ContainerType A container type.
+template <typename ContainerType>
+struct call_reserve_if_available<ContainerType, enable_if_t<detect::has_reserve<ContainerType>::value>> {
+ /// @brief Call reserve function on the ContainerType object with a given size.
+ /// @param c A container object.
+ /// @param n A size to reserve.
+ static void call(ContainerType& c, typename ContainerType::size_type n) {
+ c.reserve(n);
+ }
+};
+
+// from_node() implementations
+
+/// @brief from_node function for C-style 1D arrays whose element type must be a basic_node template instance type or a
+/// compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T Element type of C-style 1D array.
+/// @tparam N Size of the array.
+/// @param n A basic_node object.
+/// @param array An array object.
+template <typename BasicNodeType, typename T, std::size_t N>
+inline auto from_node(const BasicNodeType& n, T (&array)[N])
+ -> decltype(n.get_value_inplace(std::declval<T&>()), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created.
+ for (std::size_t i = 0; i < N; i++) {
+ n.at(i).get_value_inplace(array[i]);
+ }
+}
+
+/// @brief from_node function for C-style 2D arrays whose element type must be a basic_node template instance type or a
+/// compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T Element type of C-style 2D array.
+/// @tparam N0 Size of the outer dimension.
+/// @tparam N1 Size of the inner dimension.
+/// @param n A basic_node object.
+/// @param array An array object.
+template <typename BasicNodeType, typename T, std::size_t N0, std::size_t N1>
+inline auto from_node(const BasicNodeType& n, T (&array)[N0][N1])
+ -> decltype(n.get_value_inplace(std::declval<T&>()), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created.
+ for (std::size_t i0 = 0; i0 < N0; i0++) {
+ for (std::size_t i1 = 0; i1 < N1; i1++) {
+ n.at(i0).at(i1).get_value_inplace(array[i0][i1]);
+ }
+ }
+}
+
+/// @brief from_node function for C-style 2D arrays whose element type must be a basic_node template instance type or a
+/// compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T Element type of C-style 2D array.
+/// @tparam N0 Size of the outermost dimension.
+/// @tparam N1 Size of the middle dimension.
+/// @tparam N2 Size of the innermost dimension.
+/// @param n A basic_node object.
+/// @param array An array object.
+template <typename BasicNodeType, typename T, std::size_t N0, std::size_t N1, std::size_t N2>
+inline auto from_node(const BasicNodeType& n, T (&array)[N0][N1][N2])
+ -> decltype(n.get_value_inplace(std::declval<T&>()), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created.
+ for (std::size_t i0 = 0; i0 < N0; i0++) {
+ for (std::size_t i1 = 0; i1 < N1; i1++) {
+ for (std::size_t i2 = 0; i2 < N2; i2++) {
+ n.at(i0).at(i1).at(i2).get_value_inplace(array[i0][i1][i2]);
+ }
+ }
+ }
+}
+
+/// @brief from_node function for std::array objects whose element type must be a basic_node template instance type or a
+/// compatible type. This function is necessary since insert function is not implemented for std::array.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T Element type of std::array.
+/// @tparam N Size of std::array.
+/// @param n A basic_node object.
+/// @param arr A std::array object.
+template <typename BasicNodeType, typename T, std::size_t N>
+inline auto from_node(const BasicNodeType& n, std::array<T, N>& arr)
+ -> decltype(n.get_value_inplace(std::declval<T&>()), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ for (std::size_t i = 0; i < N; i++) {
+ // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created.
+ n.at(i).get_value_inplace(arr.at(i));
+ }
+}
+
+/// @brief from_node function for std::valarray objects whose element type must be a basic_node template instance type
+/// or a compatible type. This function is necessary since insert function is not implemented for std::valarray.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T Element type of std::valarray.
+/// @param n A basic_node object.
+/// @param va A std::valarray object.
+template <typename BasicNodeType, typename T>
+inline auto from_node(const BasicNodeType& n, std::valarray<T>& va)
+ -> decltype(n.get_value_inplace(std::declval<T&>()), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ std::size_t count = n.size();
+ va.resize(count);
+ for (std::size_t i = 0; i < count; i++) {
+ // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created.
+ n.at(i).get_value_inplace(va[i]);
+ }
+}
+
+/// @brief from_node function for std::forward_list objects whose element type must be a basic_node template instance
+/// type or a compatible type. This function is necessary since insert function is not implemented for
+/// std::forward_list.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T Element type of std::forward_list.
+/// @tparam Alloc Allocator type of std::forward_list.
+/// @param n A basic_node object.
+/// @param fl A std::forward_list object.
+template <typename BasicNodeType, typename T, typename Alloc>
+inline auto from_node(const BasicNodeType& n, std::forward_list<T, Alloc>& fl)
+ -> decltype(n.template get_value<T>(), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value is not sequence type.", n.get_type());
+ }
+
+ fl.clear();
+
+ // std::forward_list does not have insert function.
+ auto insert_pos_itr = fl.before_begin();
+ for (const auto& elem : n) {
+ insert_pos_itr = fl.emplace_after(insert_pos_itr, elem.template get_value<T>());
+ }
+}
+
+/// @brief from_node function for container objects of only keys or values, e.g., std::vector or std::set, whose element
+/// type must be a basic_node template instance type or a compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatSeqType A container type.
+/// @param n A basic_node object.
+/// @param s A container object.
+template <
+ typename BasicNodeType, typename CompatSeqType,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>, is_constructible_sequence_type<BasicNodeType, CompatSeqType>,
+ negation<std::is_constructible<typename BasicNodeType::string_type, CompatSeqType>>>::value,
+ int> = 0>
+inline auto from_node(const BasicNodeType& n, CompatSeqType& s)
+ -> decltype(n.template get_value<typename CompatSeqType::value_type>(), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value is not sequence type.", n.get_type());
+ }
+
+ s.clear();
+
+ // call reserve function first if it's available (like std::vector).
+ call_reserve_if_available<CompatSeqType>::call(s, n.size());
+
+ // transform a sequence node into a destination type object by calling insert function.
+ using std::end;
+ std::transform(n.begin(), n.end(), std::inserter(s, end(s)), [](const BasicNodeType& elem) {
+ return elem.template get_value<typename CompatSeqType::value_type>();
+ });
+}
+
+/// @brief from_node function for sequence container adapter objects, e.g., std::stack or std::queue, whose element type
+/// must be either a basic_node template instance type or a compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam SeqContainerAdapter A sequence container adapter type.
+/// @param n A node object.
+/// @param ca A sequence container adapter object.
+template <
+ typename BasicNodeType, typename SeqContainerAdapter,
+ enable_if_t<
+ conjunction<is_basic_node<BasicNodeType>, is_sequence_container_adapter<SeqContainerAdapter>>::value, int> = 0>
+inline auto from_node(const BasicNodeType& n, SeqContainerAdapter& ca)
+ -> decltype(n.template get_value<typename SeqContainerAdapter::value_type>(), ca.push(std::declval<typename SeqContainerAdapter::value_type>()), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value is not sequence type.", n.get_type());
+ }
+
+ // clear existing elements manually since clear function is not implemented for container adapter classes.
+ while (!ca.empty()) {
+ ca.pop();
+ }
+
+ for (const auto& elem : n) {
+ // container adapter classes commonly have push function.
+ // emplace function cannot be used in case SeqContainerAdapter::container_type is std::vector<bool> in C++11.
+ ca.push(elem.template get_value<typename SeqContainerAdapter::value_type>());
+ }
+}
+
+/// @brief from_node function for mappings whose key and value are of both compatible types.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatibleKeyType Mapping key type compatible with BasicNodeType.
+/// @tparam CompatibleValueType Mapping value type compatible with BasicNodeType.
+/// @tparam Compare Comparator type for mapping keys.
+/// @tparam Allocator Allocator type for destination mapping object.
+/// @param n A node object.
+/// @param m Mapping container object to store converted key/value objects.
+template <typename BasicNodeType, typename CompatMapType, enable_if_t<is_constructible_mapping_type<CompatMapType>::value, int> = 0>
+inline auto from_node(const BasicNodeType& n, CompatMapType& m)
+ -> decltype(
+ std::declval<const BasicNodeType&>().template get_value<typename CompatMapType::key_type>(),
+ std::declval<const BasicNodeType&>().template get_value<typename CompatMapType::mapped_type>(),
+ m.emplace(std::declval<typename CompatMapType::key_type>(), std::declval<typename CompatMapType::mapped_type>()),
+ void()) {
+ if FK_YAML_UNLIKELY (!n.is_mapping()) {
+ throw type_error("The target node value type is not mapping type.", n.get_type());
+ }
+
+ m.clear();
+ call_reserve_if_available<CompatMapType>::call(m, n.size());
+
+ for (const auto& pair : n.as_map()) {
+ m.emplace(
+ pair.first.template get_value<typename CompatMapType::key_type>(),
+ pair.second.template get_value<typename CompatMapType::mapped_type>());
+ }
+}
+
+/// @brief from_node function for nullptr.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @param n A node object.
+/// @param null Storage for a null value.
+template <typename BasicNodeType, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline void from_node(const BasicNodeType& n, std::nullptr_t& null) {
+ // to ensure the target node value type is null.
+ if FK_YAML_UNLIKELY (!n.is_null()) {
+ throw type_error("The target node value type is not null type.", n.get_type());
+ }
+ null = nullptr;
+}
+
+/// @brief from_node function for booleans.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @param n A node object.
+/// @param b Storage for a boolean value.
+template <typename BasicNodeType, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline void from_node(const BasicNodeType& n, bool& b) {
+ switch (n.get_type()) {
+ case node_type::NULL_OBJECT:
+ // nullptr is converted to false just as C++ implicitly does.
+ b = false;
+ break;
+ case node_type::BOOLEAN:
+ b = static_cast<bool>(n.as_bool());
+ break;
+ case node_type::INTEGER:
+ // true: non-zero, false: zero
+ b = (n.as_int() != 0);
+ break;
+ case node_type::FLOAT:
+ // true: non-zero, false: zero
+ using float_type = typename BasicNodeType::float_number_type;
+ b = (n.as_float() != static_cast<float_type>(0.));
+ break;
+ case node_type::SEQUENCE:
+ case node_type::MAPPING:
+ case node_type::STRING:
+ default:
+ throw type_error("The target node value type is not compatible with boolean type.", n.get_type());
+ }
+}
+
+/// @brief Helper struct for node-to-int conversion.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam IntType Target integer value type (same as BasicNodeType::integer_type)
+template <
+ typename BasicNodeType, typename IntType, bool = std::is_same<typename BasicNodeType::integer_type, IntType>::value>
+struct from_node_int_helper {
+ /// @brief Convert node's integer value to the target integer type.
+ /// @param n A node object.
+ /// @return An integer value converted from the node's integer value.
+ static IntType convert(const BasicNodeType& n) {
+ return n.as_int();
+ }
+};
+
+/// @brief Helper struct for node-to-int conversion if IntType is not the node's integer value type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam IntType Target integer value type (different from BasicNodeType::integer_type)
+template <typename BasicNodeType, typename IntType>
+struct from_node_int_helper<BasicNodeType, IntType, false> {
+ /// @brief Convert node's integer value to non-uint64_t integer types.
+ /// @param n A node object.
+ /// @return An integer value converted from the node's integer value.
+ static IntType convert(const BasicNodeType& n) {
+ using node_int_type = typename BasicNodeType::integer_type;
+ const node_int_type tmp_int = n.as_int();
+
+ // under/overflow check.
+ if (std::is_same<IntType, uint64_t>::value) {
+ if FK_YAML_UNLIKELY (tmp_int < 0) {
+ throw exception("Integer value underflow detected.");
+ }
+ }
+ else {
+ if FK_YAML_UNLIKELY (tmp_int < static_cast<node_int_type>(std::numeric_limits<IntType>::min())) {
+ throw exception("Integer value underflow detected.");
+ }
+ if FK_YAML_UNLIKELY (static_cast<node_int_type>(std::numeric_limits<IntType>::max()) < tmp_int) {
+ throw exception("Integer value overflow detected.");
+ }
+ }
+
+ return static_cast<IntType>(tmp_int);
+ }
+};
+
+/// @brief from_node function for integers.
+/// @note If node's value is null, boolean, or float, such a value is converted into an integer internally.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam IntegerType An integer value type.
+/// @param n A node object.
+/// @param i Storage for an integer value.
+template <
+ typename BasicNodeType, typename IntegerType,
+ enable_if_t<conjunction<is_basic_node<BasicNodeType>, is_non_bool_integral<IntegerType>>::value, int> = 0>
+inline void from_node(const BasicNodeType& n, IntegerType& i) {
+ switch (n.get_type()) {
+ case node_type::NULL_OBJECT:
+ // nullptr is interpreted as 0
+ i = static_cast<IntegerType>(0);
+ break;
+ case node_type::BOOLEAN:
+ i = static_cast<bool>(n.as_bool()) ? static_cast<IntegerType>(1) : static_cast<IntegerType>(0);
+ break;
+ case node_type::INTEGER:
+ i = from_node_int_helper<BasicNodeType, IntegerType>::convert(n);
+ break;
+ case node_type::FLOAT: {
+ // int64_t should be safe to express the integer part of possible floating point types.
+ const auto tmp_int = static_cast<int64_t>(n.as_float());
+
+ // under/overflow check.
+ if (std::is_same<IntegerType, uint64_t>::value) {
+ if FK_YAML_UNLIKELY (tmp_int < 0) {
+ throw exception("Integer value underflow detected.");
+ }
+ }
+ else {
+ if FK_YAML_UNLIKELY (tmp_int < static_cast<int64_t>(std::numeric_limits<IntegerType>::min())) {
+ throw exception("Integer value underflow detected.");
+ }
+ if FK_YAML_UNLIKELY (static_cast<int64_t>(std::numeric_limits<IntegerType>::max()) < tmp_int) {
+ throw exception("Integer value overflow detected.");
+ }
+ }
+
+ i = static_cast<IntegerType>(tmp_int);
+ break;
+ }
+ case node_type::SEQUENCE:
+ case node_type::MAPPING:
+ case node_type::STRING:
+ default:
+ throw type_error("The target node value type is not compatible with integer type.", n.get_type());
+ }
+}
+
+/// @brief Helper struct for node-to-float conversion if FloatType is the node's floating point value type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam FloatType Target floating point value type (same as the BasicNodeType::float_number_type)
+template <
+ typename BasicNodeType, typename FloatType,
+ bool = std::is_same<typename BasicNodeType::float_number_type, FloatType>::value>
+struct from_node_float_helper {
+ /// @brief Convert node's floating point value to the target floating point type.
+ /// @param n A node object.
+ /// @return A floating point value converted from the node's floating point value.
+ static FloatType convert(const BasicNodeType& n) {
+ return n.as_float();
+ }
+};
+
+/// @brief Helper struct for node-to-float conversion if IntType is not the node's floating point value type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam FloatType Target floating point value type (different from BasicNodeType::float_number_type)
+template <typename BasicNodeType, typename FloatType>
+struct from_node_float_helper<BasicNodeType, FloatType, false> {
+ /// @brief Convert node's floating point value to the target floating point type.
+ /// @param n A node object.
+ /// @return A floating point value converted from the node's floating point value.
+ static FloatType convert(const BasicNodeType& n) {
+ using node_float_type = typename BasicNodeType::float_number_type;
+ auto tmp_float = n.as_float();
+
+ // check if the value is an infinite number (either positive or negative)
+ if (std::isinf(tmp_float)) {
+ if (tmp_float == std::numeric_limits<node_float_type>::infinity()) {
+ return std::numeric_limits<FloatType>::infinity();
+ }
+
+ return static_cast<FloatType>(-1.) * std::numeric_limits<FloatType>::infinity();
+ }
+
+ // check if the value is not a number
+ if (std::isnan(tmp_float)) {
+ return std::numeric_limits<FloatType>::quiet_NaN();
+ }
+
+ // check if the value is expressible as FloatType.
+ if FK_YAML_UNLIKELY (tmp_float < std::numeric_limits<FloatType>::lowest()) {
+ throw exception("Floating point value underflow detected.");
+ }
+ if FK_YAML_UNLIKELY (std::numeric_limits<FloatType>::max() < tmp_float) {
+ throw exception("Floating point value overflow detected.");
+ }
+
+ return static_cast<FloatType>(tmp_float);
+ }
+};
+
+/// @brief from_node function for floating point values.
+/// @note If node's value is null, boolean, or integer, such a value is converted into a floating point internally.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam FloatType A floating point value type.
+/// @param n A node object.
+/// @param f Storage for a float point value.
+template <
+ typename BasicNodeType, typename FloatType,
+ enable_if_t<conjunction<is_basic_node<BasicNodeType>, std::is_floating_point<FloatType>>::value, int> = 0>
+inline void from_node(const BasicNodeType& n, FloatType& f) {
+ switch (n.get_type()) {
+ case node_type::NULL_OBJECT:
+ // nullptr is interpreted as 0.0
+ f = static_cast<FloatType>(0.);
+ break;
+ case node_type::BOOLEAN:
+ f = static_cast<bool>(n.as_bool()) ? static_cast<FloatType>(1.) : static_cast<FloatType>(0.);
+ break;
+ case node_type::INTEGER:
+ f = static_cast<FloatType>(n.as_int());
+ break;
+ case node_type::FLOAT:
+ f = from_node_float_helper<BasicNodeType, FloatType>::convert(n);
+ break;
+ case node_type::SEQUENCE:
+ case node_type::MAPPING:
+ case node_type::STRING:
+ default:
+ throw type_error("The target node value type is not compatible with float number type.", n.get_type());
+ }
+}
+
+/// @brief from_node function for BasicNodeType::string_type objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @param n A basic_node object.
+/// @param s A string node value object.
+template <typename BasicNodeType, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline void from_node(const BasicNodeType& n, typename BasicNodeType::string_type& s) {
+ if FK_YAML_UNLIKELY (!n.is_string()) {
+ throw type_error("The target node value type is not string type.", n.get_type());
+ }
+ s = n.as_str();
+}
+
+/// @brief from_node function for compatible string type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatibleStringType A compatible string type.
+/// @param n A basic_node object.
+/// @param s A compatible string object.
+template <
+ typename BasicNodeType, typename CompatibleStringType,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>,
+ negation<std::is_same<CompatibleStringType, typename BasicNodeType::string_type>>,
+ disjunction<
+ std::is_constructible<CompatibleStringType, const typename BasicNodeType::string_type&>,
+ std::is_assignable<CompatibleStringType, const typename BasicNodeType::string_type&>>>::value,
+ int> = 0>
+inline void from_node(const BasicNodeType& n, CompatibleStringType& s) {
+ if FK_YAML_UNLIKELY (!n.is_string()) {
+ throw type_error("The target node value type is not string type.", n.get_type());
+ }
+ s = n.as_str();
+}
+
+/// @brief from_node function for std::pair objects whose element types must be either a basic_node template instance
+/// type or a compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T The first type of the std::pair.
+/// @tparam U The second type of the std::pair.
+/// @param n A basic_node object.
+/// @param p A std::pair object.
+template <typename BasicNodeType, typename T, typename U, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline auto from_node(const BasicNodeType& n, std::pair<T, U>& p)
+ -> decltype(std::declval<const BasicNodeType&>().template get_value<T>(), std::declval<const BasicNodeType&>().template get_value<U>(), void()) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ // call get_value_inplace(), not get_value(), since the storage to fill the result into is already created.
+ n.at(0).get_value_inplace(p.first);
+ n.at(1).get_value_inplace(p.second);
+}
+
+/// @brief concrete implementation of from_node function for std::tuple objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam ...Types The value types of std::tuple.
+/// @tparam ...Idx Index sequence values for std::tuples value types.
+/// @param n A basic_node object
+/// @param _ Index sequence values (unused).
+/// @return A std::tuple object converted from the sequence node values.
+template <typename BasicNodeType, typename... Types, std::size_t... Idx>
+inline std::tuple<Types...> from_node_tuple_impl(const BasicNodeType& n, index_sequence<Idx...> /*unused*/) {
+ return std::make_tuple(n.at(Idx).template get_value<Types>()...);
+}
+
+/// @brief from_node function for std::tuple objects whose value types must all be either a basic_node template instance
+/// type or a compatible type
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam ...Types Value types of std::tuple.
+/// @param n A basic_node object.
+/// @param t A std::tuple object.
+template <typename BasicNodeType, typename... Types, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline void from_node(const BasicNodeType& n, std::tuple<Types...>& t) {
+ if FK_YAML_UNLIKELY (!n.is_sequence()) {
+ throw type_error("The target node value type is not sequence type.", n.get_type());
+ }
+
+ // Types... must be explicitly specified; the return type would otherwise be std::tuple with no value types.
+ t = from_node_tuple_impl<BasicNodeType, Types...>(n, index_sequence_for<Types...> {});
+}
+
+#ifdef FK_YAML_HAS_CXX_17
+
+/// @brief from_node function for std::optional objects whose value type must be either a basic_node template instance
+/// type or a compatible type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A value type of the std::optional.
+/// @param n A basic_node object.
+/// @param o A std::optional object.
+template <typename BasicNodeType, typename T, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline auto from_node(const BasicNodeType& n, std::optional<T>& o) -> decltype(n.template get_value<T>(), void()) {
+ try {
+ o.emplace(n.template get_value<T>());
+ }
+ catch (const std::exception& /*unused*/) {
+ // Any exception derived from std::exception is interpreted as a conversion failure in some way
+ // since user-defined from_node function may throw a different object from a fkyaml::type_error.
+ // and std::exception is usually the base class of user-defined exception types.
+ o = std::nullopt;
+ }
+}
+
+#endif // defined(FK_YAML_HAS_CXX_17)
+
+/// @brief A function object to call from_node functions.
+/// @note User-defined specialization is available by providing implementation **OUTSIDE** fkyaml namespace.
+struct from_node_fn {
+ /// @brief Call from_node function suitable for the given T type.
+ /// @tparam BasicNodeType A basic_node template instance type.
+ /// @tparam T A target value type assigned from the basic_node object.
+ /// @param n A basic_node object.
+ /// @param val A target object assigned from the basic_node object.
+ /// @return decltype(from_node(n, std::forward<T>(val))) void by default. User can set it to some other type.
+ template <typename BasicNodeType, typename T>
+ auto operator()(const BasicNodeType& n, T&& val) const
+ noexcept(noexcept(from_node(n, std::forward<T>(val)))) -> decltype(from_node(n, std::forward<T>(val))) {
+ return from_node(n, std::forward<T>(val));
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+FK_YAML_NAMESPACE_BEGIN
+
+#ifndef FK_YAML_HAS_CXX_17
+// anonymous namespace to hold `from_node` functor.
+// see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html for why it's needed.
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+
+/// @brief A global object to represent ADL friendly from_node functor.
+// NOLINTNEXTLINE(misc-definitions-in-headers)
+FK_YAML_INLINE_VAR constexpr const auto& from_node = detail::static_const<detail::from_node_fn>::value;
+
+#ifndef FK_YAML_HAS_CXX_17
+} // namespace
+#endif
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_CONVERSIONS_FROM_NODE_HPP */
+
+// #include <fkYAML/detail/conversions/to_node.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_DETAIL_CONVERSIONS_TO_NODE_HPP
+#define FK_YAML_DETAIL_CONVERSIONS_TO_NODE_HPP
+
+#include <utility>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/exception_safe_allocation.hpp>
+
+// #include <fkYAML/detail/meta/node_traits.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+// #include <fkYAML/detail/meta/stl_supplement.hpp>
+
+// #include <fkYAML/detail/node_attrs.hpp>
+
+// #include <fkYAML/node_type.hpp>
+
+
+FK_YAML_DETAIL_NAMESPACE_BEGIN
+
+///////////////////////////////////
+// external_node_constructor //
+///////////////////////////////////
+
+/// @brief The external constructor template for basic_node objects.
+/// @note All the non-specialized instantiations results in compilation error since such instantiations are not
+/// supported.
+/// @warning All the specialization must call n.m_value.destroy() first in the construct function to avoid
+/// memory leak.
+/// @tparam node_type The resulting YAML node value type.
+template <typename BasicNodeType>
+struct external_node_constructor {
+ template <typename... Args>
+ static void sequence(BasicNodeType& n, Args&&... args) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::seq_bit;
+ n.m_value.p_seq = create_object<typename BasicNodeType::sequence_type>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ static void mapping(BasicNodeType& n, Args&&... args) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::map_bit;
+ n.m_value.p_map = create_object<typename BasicNodeType::mapping_type>(std::forward<Args>(args)...);
+ }
+
+ static void null_scalar(BasicNodeType& n, std::nullptr_t) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::null_bit;
+ n.m_value.p_map = nullptr;
+ }
+
+ static void boolean_scalar(BasicNodeType& n, const typename BasicNodeType::boolean_type b) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::bool_bit;
+ n.m_value.boolean = b;
+ }
+
+ static void integer_scalar(BasicNodeType& n, const typename BasicNodeType::integer_type i) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::int_bit;
+ n.m_value.integer = i;
+ }
+
+ static void float_scalar(BasicNodeType& n, const typename BasicNodeType::float_number_type f) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::float_bit;
+ n.m_value.float_val = f;
+ }
+
+ template <typename... Args>
+ static void string_scalar(BasicNodeType& n, Args&&... args) {
+ destroy(n);
+ n.m_attrs |= node_attr_bits::string_bit;
+ n.m_value.p_str = create_object<typename BasicNodeType::string_type>(std::forward<Args>(args)...);
+ }
+
+private:
+ static void destroy(BasicNodeType& n) {
+ n.m_value.destroy(n.m_attrs & node_attr_mask::value);
+ n.m_attrs &= ~node_attr_mask::value;
+ }
+};
+
+/////////////////
+// to_node //
+/////////////////
+
+/// @brief to_node function for BasicNodeType::sequence_type objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A sequence node value type.
+/// @param n A basic_node object.
+/// @param s A sequence node value object.
+template <
+ typename BasicNodeType, typename T,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>,
+ std::is_same<typename BasicNodeType::sequence_type, remove_cvref_t<T>>>::value,
+ int> = 0>
+inline void to_node(BasicNodeType& n, T&& s) noexcept {
+ external_node_constructor<BasicNodeType>::sequence(n, std::forward<T>(s));
+}
+
+/// @brief to_node function for compatible sequence types.
+/// @note This overload is enabled when
+/// * both begin()/end() functions are callable on a `CompatSeqType` object
+/// * CompatSeqType doesn't have `mapped_type` (mapping-like type)
+/// * BasicNodeType::string_type cannot be constructed from a CompatSeqType object (string-like type)
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatSeqType A container type.
+/// @param n A basic_node object.
+/// @param s A container object.
+template <
+ typename BasicNodeType, typename CompatSeqType,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>,
+ negation<std::is_same<typename BasicNodeType::sequence_type, remove_cvref_t<CompatSeqType>>>,
+ negation<is_basic_node<CompatSeqType>>, detect::has_begin_end<CompatSeqType>,
+ negation<conjunction<detect::has_key_type<CompatSeqType>, detect::has_mapped_type<CompatSeqType>>>,
+ negation<std::is_constructible<typename BasicNodeType::string_type, CompatSeqType>>>::value,
+ int> = 0>
+// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
+inline void to_node(BasicNodeType& n, CompatSeqType&& s) {
+ using std::begin;
+ using std::end;
+ external_node_constructor<BasicNodeType>::sequence(n, begin(s), end(s));
+}
+
+/// @brief to_node function for std::pair objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T The first type of std::pair.
+/// @tparam U The second type of std::pair.
+/// @param n A basic_node object.
+/// @param p A std::pair object.
+template <typename BasicNodeType, typename T, typename U>
+inline void to_node(BasicNodeType& n, const std::pair<T, U>& p) {
+ n = {p.first, p.second};
+}
+
+/// @brief concrete implementation of to_node function for std::tuple objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam ...Types The value types of std::tuple.
+/// @tparam ...Idx Index sequence values for std::tuple value types.
+/// @param n A basic_node object.
+/// @param t A std::tuple object.
+/// @param _ An index sequence. (unused)
+template <typename BasicNodeType, typename... Types, std::size_t... Idx>
+inline void to_node_tuple_impl(BasicNodeType& n, const std::tuple<Types...>& t, index_sequence<Idx...> /*unused*/) {
+ n = {std::get<Idx>(t)...};
+}
+
+/// @brief to_node function for std::tuple objects with no value types.
+/// @note This implementation is needed since calling `to_node_tuple_impl()` with an empty tuple creates a null node.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @param n A basic_node object.
+/// @param _ A std::tuple object. (unused)
+template <typename BasicNodeType>
+inline void to_node(BasicNodeType& n, const std::tuple<>& /*unused*/) {
+ n = BasicNodeType::sequence();
+}
+
+/// @brief to_node function for std::tuple objects with at least one value type.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam ...FirstType The first value types of std::tuple.
+/// @tparam ...RestTypes The rest value types of std::tuple. (maybe empty)
+/// @param n A basic_node object.
+/// @param t A std::tuple object.
+template <typename BasicNodeType, typename FirstType, typename... RestTypes>
+inline void to_node(BasicNodeType& n, const std::tuple<FirstType, RestTypes...>& t) {
+ to_node_tuple_impl(n, t, index_sequence_for<FirstType, RestTypes...> {});
+}
+
+/// @brief to_node function for BasicNodeType::mapping_type objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A mapping node value type.
+/// @param n A basic_node object.
+/// @param m A mapping node value object.
+template <
+ typename BasicNodeType, typename T,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>, std::is_same<typename BasicNodeType::mapping_type, remove_cvref_t<T>>>::value,
+ int> = 0>
+inline void to_node(BasicNodeType& n, T&& m) noexcept {
+ external_node_constructor<BasicNodeType>::mapping(n, std::forward<T>(m));
+}
+
+/// @brief to_node function for compatible mapping types.
+/// @note This overload is enabled when
+/// * both begin()/end() functions are callable on a `CompatMapType` object
+/// * CompatMapType has both `key_type` and `mapped_type`
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam CompatMapType A container type.
+/// @param n A basic_node object.
+/// @param m A container object.
+template <
+ typename BasicNodeType, typename CompatMapType,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>, negation<is_basic_node<CompatMapType>>,
+ negation<std::is_same<typename BasicNodeType::mapping_type, remove_cvref_t<CompatMapType>>>,
+ detect::has_begin_end<CompatMapType>, detect::has_key_type<CompatMapType>,
+ detect::has_mapped_type<CompatMapType>>::value,
+ int> = 0>
+inline void to_node(BasicNodeType& n, CompatMapType&& m) {
+ external_node_constructor<BasicNodeType>::mapping(n);
+ auto& map = n.as_map();
+ for (const auto& pair : std::forward<CompatMapType>(m)) {
+ map.emplace(pair.first, pair.second);
+ }
+}
+
+/// @brief to_node function for null objects.
+/// @tparam BasicNodeType A mapping node value type.
+/// @tparam NullType This must be std::nullptr_t type
+template <typename BasicNodeType, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline void to_node(BasicNodeType& n, std::nullptr_t /*unused*/) {
+ external_node_constructor<BasicNodeType>::null_scalar(n, nullptr);
+}
+
+/// @brief to_node function for BasicNodeType::boolean_type objects.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A boolean scalar node value type.
+/// @param n A basic_node object.
+/// @param b A boolean scalar node value object.
+template <typename BasicNodeType, enable_if_t<is_basic_node<BasicNodeType>::value, int> = 0>
+inline void to_node(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept {
+ external_node_constructor<BasicNodeType>::boolean_scalar(n, b);
+}
+
+/// @brief to_node function for integers.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T An integer type.
+/// @param n A basic_node object.
+/// @param i An integer object.
+template <
+ typename BasicNodeType, typename T,
+ enable_if_t<conjunction<is_basic_node<BasicNodeType>, is_non_bool_integral<T>>::value, int> = 0>
+inline void to_node(BasicNodeType& n, T i) noexcept {
+ external_node_constructor<BasicNodeType>::integer_scalar(n, i);
+}
+
+/// @brief to_node function for floating point numbers.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A floating point number type.
+/// @param n A basic_node object.
+/// @param f A floating point number object.
+template <
+ typename BasicNodeType, typename T,
+ enable_if_t<conjunction<is_basic_node<BasicNodeType>, std::is_floating_point<T>>::value, int> = 0>
+inline void to_node(BasicNodeType& n, T f) noexcept {
+ external_node_constructor<BasicNodeType>::float_scalar(n, f);
+}
+
+/// @brief to_node function for compatible strings.
+/// @tparam BasicNodeType A basic_node template instance type.
+/// @tparam T A compatible string type.
+/// @param n A basic_node object.
+/// @param s A compatible string object.
+template <
+ typename BasicNodeType, typename T,
+ enable_if_t<
+ conjunction<
+ is_basic_node<BasicNodeType>, negation<is_null_pointer<T>>,
+ std::is_constructible<typename BasicNodeType::string_type, T>>::value,
+ int> = 0>
+inline void to_node(BasicNodeType& n, T&& s) {
+ external_node_constructor<BasicNodeType>::string_scalar(n, std::forward<T>(s));
+}
+
+/// @brief A function object to call to_node functions.
+/// @note User-defined specialization is available by providing implementation **OUTSIDE** fkyaml namespace.
+struct to_node_fn {
+ /// @brief Call to_node function suitable for the given T type.
+ /// @tparam BasicNodeType A basic_node template instance type.
+ /// @tparam T A target value type assigned to the basic_node object.
+ /// @param n A basic_node object.
+ /// @param val A target object assigned to the basic_node object.
+ /// @return decltype(to_node(n, std::forward<T>(val))) void by default. User can set it to some other type.
+ template <typename BasicNodeType, typename T>
+ auto operator()(BasicNodeType& n, T&& val) const
+ noexcept(noexcept(to_node(n, std::forward<T>(val)))) -> decltype(to_node(n, std::forward<T>(val))) {
+ return to_node(n, std::forward<T>(val));
+ }
+};
+
+FK_YAML_DETAIL_NAMESPACE_END
+
+FK_YAML_NAMESPACE_BEGIN
+
+#ifndef FK_YAML_HAS_CXX_17
+// anonymous namespace to hold `to_node` functor.
+// see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html for why it's needed.
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+
+/// @brief A global object to represent ADL friendly to_node functor.
+// NOLINTNEXTLINE(misc-definitions-in-headers)
+FK_YAML_INLINE_VAR constexpr const auto& to_node = detail::static_const<detail::to_node_fn>::value;
+
+#ifndef FK_YAML_HAS_CXX_17
+} // namespace
+#endif
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_DETAIL_CONVERSIONS_TO_NODE_HPP */
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+/// @brief An ADL friendly converter between basic_node objects and native data objects.
+/// @tparam ValueType A default target data type.
+/// @sa https://fktn-k.github.io/fkYAML/api/node_value_converter/
+template <typename ValueType, typename>
+class node_value_converter {
+public:
+ /// @brief Convert a YAML node value into compatible native data.
+ /// @tparam BasicNodeType A basic_node template instance type.
+ /// @tparam TargetType A native data type for conversion.
+ /// @param n A basic_node object.
+ /// @param val A native data object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/node_value_converter/from_node/
+ template <typename BasicNodeType, typename TargetType = ValueType>
+ static auto from_node(BasicNodeType&& n, TargetType& val) noexcept(
+ noexcept(::fkyaml::from_node(std::forward<BasicNodeType>(n), val)))
+ -> decltype(::fkyaml::from_node(std::forward<BasicNodeType>(n), val), void()) {
+ ::fkyaml::from_node(std::forward<BasicNodeType>(n), val);
+ }
+
+ /// @brief Convert compatible native data into a YAML node.
+ /// @tparam BasicNodeType A basic_node template instance type.
+ /// @tparam TargetType A native data type for conversion.
+ /// @param n A basic_node object.
+ /// @param val A native data object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/node_value_converter/to_node/
+ template <typename BasicNodeType, typename TargetType = ValueType>
+ static auto to_node(BasicNodeType& n, TargetType&& val) noexcept(noexcept(::fkyaml::to_node(
+ n, std::forward<TargetType>(val)))) -> decltype(::fkyaml::to_node(n, std::forward<TargetType>(val))) {
+ ::fkyaml::to_node(n, std::forward<TargetType>(val));
+ }
+};
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_NODE_VALUE_CONVERTER_HPP */
+
+// #include <fkYAML/ordered_map.hpp>
+// _______ __ __ __ _____ __ __ __
+// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library
+// | __| _ < \_ _/| ___ | _ | |___ version 0.4.2
+// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML
+//
+// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani <fktn.dev@gmail.com>
+// SPDX-License-Identifier: MIT
+
+#ifndef FK_YAML_ORDERED_MAP_HPP
+#define FK_YAML_ORDERED_MAP_HPP
+
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <utility>
+#include <vector>
+
+// #include <fkYAML/detail/macros/define_macros.hpp>
+
+// #include <fkYAML/detail/meta/type_traits.hpp>
+
+// #include <fkYAML/exception.hpp>
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+/// @brief A minimal map-like container which preserves insertion order.
+/// @tparam Key A type for keys.
+/// @tparam Value A type for values.
+/// @tparam IgnoredCompare A placeholder for key comparison. This will be ignored.
+/// @tparam Allocator A class for allocators.
+/// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+template <
+ typename Key, typename Value, typename IgnoredCompare = std::less<Key>,
+ typename Allocator = std::allocator<std::pair<const Key, Value>>>
+class ordered_map : public std::vector<std::pair<const Key, Value>, Allocator> {
+public:
+ /// @brief A type for keys.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using key_type = Key;
+
+ /// @brief A type for values.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using mapped_type = Value;
+
+ /// @brief A type for internal key-value containers.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using Container = std::vector<std::pair<const Key, Value>, Allocator>;
+
+ /// @brief A type for key-value pairs.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using value_type = typename Container::value_type;
+
+ /// @brief A type for non-const iterators.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using iterator = typename Container::iterator;
+
+ /// @brief A type for const iterators.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using const_iterator = typename Container::const_iterator;
+
+ /// @brief A type for size parameters used in this class.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using size_type = typename Container::size_type;
+
+ /// @brief A type for comparison between keys.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/
+ using key_compare = std::equal_to<Key>;
+
+public:
+ /// @brief Construct a new ordered_map object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/constructor/
+ ordered_map() noexcept(noexcept(Container()))
+ : Container() {
+ }
+
+ /// @brief Construct a new ordered_map object with an initializer list.
+ /// @param init An initializer list to construct the inner container object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/constructor/
+ ordered_map(std::initializer_list<value_type> init)
+ : Container {init} {
+ }
+
+public:
+ /// @brief A subscript operator for ordered_map objects.
+ /// @tparam KeyType A type for the input key.
+ /// @param key A key to the target value.
+ /// @return mapped_type& Reference to a mapped_type object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/operator[]/
+ template <
+ typename KeyType,
+ detail::enable_if_t<detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ mapped_type& operator[](KeyType&& key) noexcept {
+ return emplace(std::forward<KeyType>(key), mapped_type()).first->second;
+ }
+
+public:
+ /// @brief Emplace a new key-value pair if the new key does not exist.
+ /// @tparam KeyType A type for the input key.
+ /// @param key A key to be emplaced to this ordered_map object.
+ /// @param value A value to be emplaced to this ordered_map object.
+ /// @return std::pair<iterator, bool> A result of emplacement of the new key-value pair.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/emplace/
+ template <
+ typename KeyType,
+ detail::enable_if_t<detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ std::pair<iterator, bool> emplace(KeyType&& key, const mapped_type& value) noexcept {
+ for (auto itr = this->begin(); itr != this->end(); ++itr) {
+ if (m_compare(itr->first, key)) {
+ return {itr, false};
+ }
+ }
+ this->emplace_back(std::forward<KeyType>(key), value);
+ return {std::prev(this->end()), true};
+ }
+
+ /// @brief Find a value associated to the given key. Throws an exception if the search fails.
+ /// @tparam KeyType A type for the input key.
+ /// @param key A key to find a value with.
+ /// @return mapped_type& The value associated to the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/at/
+ template <
+ typename KeyType,
+ detail::enable_if_t<detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ mapped_type& at(KeyType&& key) { // NOLINT(cppcoreguidelines-missing-std-forward)
+ for (auto itr = this->begin(); itr != this->end(); ++itr) {
+ if (m_compare(itr->first, key)) {
+ return itr->second;
+ }
+ }
+ throw fkyaml::exception("key not found.");
+ }
+
+ /// @brief Find a value associated to the given key. Throws an exception if the search fails.
+ /// @tparam KeyType A type for the input key.
+ /// @param key A key to find a value with.
+ /// @return const mapped_type& The value associated to the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/at/
+ template <
+ typename KeyType,
+ detail::enable_if_t<detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ const mapped_type& at(KeyType&& key) const { // NOLINT(cppcoreguidelines-missing-std-forward)
+ for (auto itr = this->begin(); itr != this->end(); ++itr) {
+ if (m_compare(itr->first, key)) {
+ return itr->second;
+ }
+ }
+ throw fkyaml::exception("key not found.");
+ }
+
+ /// @brief Find a value with the given key.
+ /// @tparam KeyType A type for the input key.
+ /// @param key A key to find a value with.
+ /// @return iterator The iterator for the found value, or the result of end().
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/find/
+ template <
+ typename KeyType,
+ detail::enable_if_t<detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ iterator find(KeyType&& key) noexcept { // NOLINT(cppcoreguidelines-missing-std-forward)
+ for (auto itr = this->begin(); itr != this->end(); ++itr) {
+ if (m_compare(itr->first, key)) {
+ return itr;
+ }
+ }
+ return this->end();
+ }
+
+ /// @brief Find a value with the given key.
+ /// @tparam KeyType A type for the input key.
+ /// @param key A key to find a value with.
+ /// @return const_iterator The constant iterator for the found value, or the result of end().
+ /// @sa https://fktn-k.github.io/fkYAML/api/ordered_map/find/
+ template <
+ typename KeyType,
+ detail::enable_if_t<detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ const_iterator find(KeyType&& key) const noexcept { // NOLINT(cppcoreguidelines-missing-std-forward)
+ for (auto itr = this->begin(); itr != this->end(); ++itr) {
+ if (m_compare(itr->first, key)) {
+ return itr;
+ }
+ }
+ return this->end();
+ }
+
+private:
+ /// The object for comparing keys.
+ key_compare m_compare {};
+};
+
+FK_YAML_NAMESPACE_END
+
+#endif /* FK_YAML_ORDERED_MAP_HPP */
+
+
+FK_YAML_NAMESPACE_BEGIN
+
+/// @brief A class to store value of YAML nodes.
+/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/
+template <
+ template <typename, typename...> class SequenceType, template <typename, typename, typename...> class MappingType,
+ typename BooleanType, typename IntegerType, typename FloatNumberType, typename StringType,
+ template <typename, typename = void> class ConverterType>
+class basic_node {
+public:
+ /// @brief A type for sequence basic_node values.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence_type/
+ using sequence_type = SequenceType<basic_node, std::allocator<basic_node>>;
+
+ /// @brief A type for mapping basic_node values.
+ /// @note std::unordered_map is not supported since it does not allow incomplete types.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping_type/
+ using mapping_type = MappingType<basic_node, basic_node>;
+
+ /// @brief A type for boolean basic_node values.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/boolean_type/
+ using boolean_type = BooleanType;
+
+ /// @brief A type for integer basic_node values.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/integer_type/
+ using integer_type = IntegerType;
+
+ /// @brief A type for float number basic_node values.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/float_number_type/
+ using float_number_type = FloatNumberType;
+
+ /// @brief A type for string basic_node values.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/string_type/
+ using string_type = StringType;
+
+ /// @brief A type of elements in a basic_node container.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using value_type = basic_node;
+
+ /// @brief A type of reference to a basic_node element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using reference = value_type&;
+
+ /// @brief A type of constant reference to a basic_node element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using const_reference = const value_type&;
+
+ /// @brief A type of a pointer to a basic_node element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using pointer = value_type*;
+
+ /// @brief A type of a constant pointer to a basic_node element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using const_pointer = const value_type*;
+
+ /// @brief A type to represent basic_node container sizes.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using size_type = std::size_t;
+
+ /// @brief A type to represent differences between basic_node iterators.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/#container-types
+ using difference_type = std::ptrdiff_t;
+
+ /// @brief A type for iterators of basic_node containers.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/iterator/
+ using iterator = fkyaml::detail::iterator<basic_node>;
+
+ /// @brief A type for constant iterators of basic_node containers.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/iterator/
+ using const_iterator = fkyaml::detail::iterator<const basic_node>;
+
+ /// @brief A type for reverse iterators of basic_node containers.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/reverse_iterator/
+ using reverse_iterator = fkyaml::detail::reverse_iterator<iterator>;
+
+ /// @brief A type for constant reverse iterators of basic_node containers.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/reverse_iterator/
+ using const_reverse_iterator = fkyaml::detail::reverse_iterator<const_iterator>;
+
+ /// @brief A helper alias to determine converter type for the given target native data type.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/value_converter_type/
+ template <typename T, typename SFINAE>
+ using value_converter_type = ConverterType<T, SFINAE>;
+
+ /// @brief Definition of node value types.
+ /// @deprecated Use fkyaml::node_type enum class. (since 0.3.12)
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/node_t/
+ using node_t = detail::node_t;
+
+ /// @brief Definition of YAML version types.
+ /// @deprecated Use fkyaml::yaml_version_type enum class. (since 0.3.12)
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/yaml_version_t/
+ using yaml_version_t = detail::yaml_version_t;
+
+ /// @brief A type for mapping range objects for the map_items() function.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/map_range/
+ using map_range = fkyaml::detail::map_range_proxy<basic_node>;
+
+ /// @brief A type for constant mapping range objects for the map_items() function.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/map_range/
+ using const_map_range = fkyaml::detail::map_range_proxy<const basic_node>;
+
+private:
+ template <typename BasicNodeType>
+ friend struct fkyaml::detail::external_node_constructor;
+
+ template <typename BasicNodeType>
+ friend class fkyaml::detail::basic_deserializer;
+
+ template <typename BasicNodeType>
+ friend class fkyaml::detail::basic_serializer;
+
+ /// @brief A type for YAML docs deserializers.
+ using deserializer_type = detail::basic_deserializer<basic_node>;
+ /// @brief A type for YAML docs serializers.
+ using serializer_type = detail::basic_serializer<basic_node>;
+ /// @brief A helper type alias for std::initializer_list.
+ using initializer_list_t = std::initializer_list<detail::node_ref_storage<basic_node>>;
+
+ /// @brief The actual storage for a YAML node value of the @ref basic_node class.
+ /// @details This union combines the different storage types for the YAML value types defined in @ref node_t.
+ /// @note Container types are stored as pointers so that the size of this union will not exceed 64 bits by
+ /// default.
+ union node_value {
+ /// @brief Constructs a new basic_node Value object for null types.
+ node_value() = default;
+
+ /// @brief Constructs a new basic_node value object with a node type. The default value for the specified
+ /// type will be assigned.
+ /// @param[in] type A node type.
+ explicit node_value(detail::node_attr_t value_type_bit) {
+ switch (value_type_bit) {
+ case detail::node_attr_bits::seq_bit:
+ p_seq = detail::create_object<sequence_type>();
+ break;
+ case detail::node_attr_bits::map_bit:
+ p_map = detail::create_object<mapping_type>();
+ break;
+ case detail::node_attr_bits::null_bit:
+ p_map = nullptr;
+ break;
+ case detail::node_attr_bits::bool_bit:
+ boolean = static_cast<boolean_type>(false);
+ break;
+ case detail::node_attr_bits::int_bit:
+ integer = static_cast<integer_type>(0);
+ break;
+ case detail::node_attr_bits::float_bit:
+ float_val = static_cast<float_number_type>(0.0);
+ break;
+ case detail::node_attr_bits::string_bit:
+ p_str = detail::create_object<string_type>();
+ break;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+ /// @brief Destroys the existing Node value. This process is recursive if the specified node type is for
+ /// containers.
+ /// @param[in] type A Node type to determine the value to be destroyed.
+ void destroy(detail::node_attr_t value_type_bit) {
+ switch (value_type_bit) {
+ case detail::node_attr_bits::seq_bit:
+ p_seq->clear();
+ detail::destroy_object<sequence_type>(p_seq);
+ p_seq = nullptr;
+ break;
+ case detail::node_attr_bits::map_bit:
+ p_map->clear();
+ detail::destroy_object<mapping_type>(p_map);
+ p_map = nullptr;
+ break;
+ case detail::node_attr_bits::string_bit:
+ detail::destroy_object<string_type>(p_str);
+ p_str = nullptr;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /// A pointer to the value of sequence type.
+ sequence_type* p_seq;
+ /// A pointer to the value of mapping type. This pointer is also used when node type is null.
+ mapping_type* p_map {nullptr};
+ /// A value of boolean type.
+ boolean_type boolean;
+ /// A value of integer type.
+ integer_type integer;
+ /// A value of float number type.
+ float_number_type float_val;
+ /// A pointer to the value of string type.
+ string_type* p_str;
+ };
+
+public:
+ /// @brief Constructs a new basic_node object of null type.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ basic_node() = default;
+
+ /// @brief Constructs a new basic_node object with a specified type.
+ /// @param[in] type A YAML node type.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ FK_YAML_DEPRECATED("Since 0.3.12; Use explicit basic_node(const node_type)")
+ explicit basic_node(const node_t type)
+ : basic_node(detail::convert_to_node_type(type)) {
+ }
+
+ explicit basic_node(const node_type type)
+ : m_attrs(detail::node_attr_bits::from_node_type(type)),
+ m_value(m_attrs & detail::node_attr_mask::value) {
+ }
+
+ /// @brief Copy constructor of the basic_node class.
+ /// @param[in] rhs A basic_node object to be copied with.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ basic_node(const basic_node& rhs)
+ : m_attrs(rhs.m_attrs),
+ mp_meta(rhs.mp_meta),
+ m_prop(rhs.m_prop) {
+ if FK_YAML_LIKELY (!has_anchor_name()) {
+ switch (m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ m_value.p_seq = detail::create_object<sequence_type>(*(rhs.m_value.p_seq));
+ break;
+ case detail::node_attr_bits::map_bit:
+ m_value.p_map = detail::create_object<mapping_type>(*(rhs.m_value.p_map));
+ break;
+ case detail::node_attr_bits::null_bit:
+ m_value.p_map = nullptr;
+ break;
+ case detail::node_attr_bits::bool_bit:
+ m_value.boolean = rhs.m_value.boolean;
+ break;
+ case detail::node_attr_bits::int_bit:
+ m_value.integer = rhs.m_value.integer;
+ break;
+ case detail::node_attr_bits::float_bit:
+ m_value.float_val = rhs.m_value.float_val;
+ break;
+ case detail::node_attr_bits::string_bit:
+ m_value.p_str = detail::create_object<string_type>(*(rhs.m_value.p_str));
+ break;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+ }
+
+ /// @brief Move constructor of the basic_node class.
+ /// @param[in] rhs A basic_node object to be moved from.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ basic_node(basic_node&& rhs) noexcept
+ : m_attrs(rhs.m_attrs),
+ mp_meta(std::move(rhs.mp_meta)),
+ m_prop(std::move(rhs.m_prop)) {
+ if FK_YAML_LIKELY (!has_anchor_name()) {
+ switch (m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ FK_YAML_ASSERT(rhs.m_value.p_seq != nullptr);
+ m_value.p_seq = rhs.m_value.p_seq;
+ rhs.m_value.p_seq = nullptr;
+ break;
+ case detail::node_attr_bits::map_bit:
+ FK_YAML_ASSERT(rhs.m_value.p_map != nullptr);
+ m_value.p_map = rhs.m_value.p_map;
+ rhs.m_value.p_map = nullptr;
+ break;
+ case detail::node_attr_bits::null_bit:
+ FK_YAML_ASSERT(rhs.m_value.p_map == nullptr);
+ m_value.p_map = rhs.m_value.p_map;
+ break;
+ case detail::node_attr_bits::bool_bit:
+ m_value.boolean = rhs.m_value.boolean;
+ rhs.m_value.boolean = static_cast<boolean_type>(false);
+ break;
+ case detail::node_attr_bits::int_bit:
+ m_value.integer = rhs.m_value.integer;
+ rhs.m_value.integer = static_cast<integer_type>(0);
+ break;
+ case detail::node_attr_bits::float_bit:
+ m_value.float_val = rhs.m_value.float_val;
+ rhs.m_value.float_val = static_cast<float_number_type>(0.0);
+ break;
+ case detail::node_attr_bits::string_bit:
+ FK_YAML_ASSERT(rhs.m_value.p_str != nullptr);
+ m_value.p_str = rhs.m_value.p_str;
+ rhs.m_value.p_str = nullptr;
+ break;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+ rhs.m_attrs = detail::node_attr_bits::default_bits;
+ rhs.m_value.p_map = nullptr;
+ }
+
+ /// @brief Construct a new basic_node object from a value of compatible types.
+ /// @tparam CompatibleType Type of native data which is compatible with node values.
+ /// @tparam U Type of compatible native data without cv-qualifiers and reference.
+ /// @param[in] val The value of a compatible type.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ template <
+ typename CompatibleType, typename U = detail::remove_cvref_t<CompatibleType>,
+ detail::enable_if_t<
+ detail::conjunction<
+ detail::negation<detail::is_basic_node<U>>,
+ detail::disjunction<detail::is_node_compatible_type<basic_node, U>>>::value,
+ int> = 0>
+ basic_node(CompatibleType&& val) noexcept(
+ noexcept(ConverterType<U, void>::to_node(std::declval<basic_node&>(), std::declval<CompatibleType>()))) {
+ ConverterType<U, void>::to_node(*this, std::forward<CompatibleType>(val));
+ }
+
+ /// @brief Construct a new basic node object with a node_ref_storage object.
+ /// @tparam NodeRefStorageType Type of basic_node with reference.
+ /// @param[in] node_ref_storage A node_ref_storage template class object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ template <
+ typename NodeRefStorageType,
+ detail::enable_if_t<detail::is_node_ref_storage<NodeRefStorageType>::value, int> = 0>
+ basic_node(const NodeRefStorageType& node_ref_storage) noexcept
+ : basic_node(node_ref_storage.release()) {
+ }
+
+ /// @brief Construct a new basic node object with std::initializer_list.
+ /// @param[in] init A initializer list of basic_node objects.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/
+ basic_node(initializer_list_t init) {
+ bool is_mapping =
+ std::all_of(init.begin(), init.end(), [](const detail::node_ref_storage<basic_node>& node_ref) {
+ // Do not use is_sequence_impl() since node_ref may be an anchor or alias.
+ return node_ref->is_sequence() && node_ref->size() == 2;
+ });
+
+ if (is_mapping) {
+ m_attrs = detail::node_attr_bits::map_bit;
+ m_value.p_map = detail::create_object<mapping_type>();
+
+ auto& map = *m_value.p_map;
+ for (auto& elem_ref : init) {
+ auto elem = elem_ref.release();
+ auto& seq = *elem.m_value.p_seq;
+ map.emplace(std::move(seq[0]), std::move(seq[1]));
+ }
+ }
+ else {
+ m_attrs = detail::node_attr_bits::seq_bit;
+ m_value.p_seq = detail::create_object<sequence_type>();
+
+ auto& seq = *m_value.p_seq;
+ seq.reserve(std::distance(init.begin(), init.end()));
+ for (auto& elem_ref : init) {
+ seq.emplace_back(std::move(elem_ref.release()));
+ }
+ }
+ }
+
+ /// @brief Destroy the basic_node object and its value storage.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/destructor/
+ ~basic_node() noexcept // NOLINT(bugprone-exception-escape)
+ {
+ if (m_attrs & detail::node_attr_mask::anchoring) {
+ if (m_attrs & detail::node_attr_bits::anchor_bit) {
+ auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first;
+ std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs));
+ itr->second.m_value.destroy(itr->second.m_attrs & detail::node_attr_mask::value);
+ itr->second.m_attrs = detail::node_attr_bits::default_bits;
+ itr->second.mp_meta.reset();
+ }
+ }
+ else if ((m_attrs & detail::node_attr_bits::null_bit) == 0) {
+ m_value.destroy(m_attrs & detail::node_attr_mask::value);
+ }
+
+ m_attrs = detail::node_attr_bits::default_bits;
+ mp_meta.reset();
+ }
+
+public:
+ /// @brief Deserialize the first YAML document in the input into a basic_node object.
+ /// @tparam InputType Type of a compatible input.
+ /// @param[in] input An input source in the YAML format.
+ /// @return The resulting basic_node object deserialized from the input source.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/deserialize/
+ template <typename InputType>
+ static basic_node deserialize(InputType&& input) {
+ return deserializer_type().deserialize(detail::input_adapter(std::forward<InputType>(input)));
+ }
+
+ /// @brief Deserialize the first YAML document in the input ranged by the iterators into a basic_node object.
+ /// @note
+ /// Iterators must satisfy the LegacyInputIterator requirements.
+ /// See https://en.cppreference.com/w/cpp/named_req/InputIterator.
+ /// @tparam ItrType Type of a compatible iterator
+ /// @param[in] begin An iterator to the first element of an input sequence.
+ /// @param[in] end An iterator to the past-the-last element of an input sequence.
+ /// @return The resulting basic_node object deserialized from the pair of iterators.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/deserialize/
+ template <typename ItrType>
+ static basic_node deserialize(ItrType begin, ItrType end) {
+ return deserializer_type().deserialize(
+ detail::input_adapter(std::forward<ItrType>(begin), std::forward<ItrType>(end)));
+ }
+
+ /// @brief Deserialize all YAML documents in the input into basic_node objects.
+ /// @tparam InputType Type of a compatible input.
+ /// @param[in] input An input source in the YAML format.
+ /// @return The resulting basic_node objects deserialized from the input.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/deserialize_docs/
+ template <typename InputType>
+ static std::vector<basic_node> deserialize_docs(InputType&& input) {
+ return deserializer_type().deserialize_docs(detail::input_adapter(std::forward<InputType>(input)));
+ }
+
+ /// @brief Deserialize all YAML documents in the input ranged by the iterators into basic_node objects.
+ /// @tparam ItrType Type of a compatible iterator.
+ /// @param[in] begin An iterator to the first element of an input sequence.
+ /// @param[in] end An iterator to the past-the-last element of an input sequence.
+ /// @return The resulting basic_node objects deserialized from the pair of iterators.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/deserialize_docs/
+ template <typename ItrType>
+ static std::vector<basic_node> deserialize_docs(ItrType&& begin, ItrType&& end) {
+ return deserializer_type().deserialize_docs(
+ detail::input_adapter(std::forward<ItrType>(begin), std::forward<ItrType>(end)));
+ }
+
+ /// @brief Serialize a basic_node object into a string.
+ /// @param[in] node A basic_node object to be serialized.
+ /// @return The resulting string object from the serialization of the given node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/serialize/
+ static std::string serialize(const basic_node& node) {
+ return serializer_type().serialize(node);
+ }
+
+ /// @brief Serialize basic_node objects into a string.
+ /// @param docs basic_node objects to be serialized.
+ /// @return The resulting string object from the serialization of the given nodes.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/serialize_docs/
+ static std::string serialize_docs(const std::vector<basic_node>& docs) {
+ return serializer_type().serialize_docs(docs);
+ }
+
+ /// @brief A factory method for sequence basic_node objects without sequence_type objects.
+ /// @return A YAML sequence node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/
+ static basic_node sequence() {
+ basic_node node;
+ node.m_attrs = detail::node_attr_bits::seq_bit;
+ node.m_value.p_seq = detail::create_object<sequence_type>();
+ return node;
+ } // LCOV_EXCL_LINE
+
+ /// @brief A factory method for sequence basic_node objects with lvalue sequence_type objects.
+ /// @param[in] seq A lvalue sequence node value.
+ /// @return A YAML sequence node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/
+ static basic_node sequence(const sequence_type& seq) {
+ basic_node node;
+ node.m_attrs = detail::node_attr_bits::seq_bit;
+ node.m_value.p_seq = detail::create_object<sequence_type>(seq);
+ return node;
+ } // LCOV_EXCL_LINE
+
+ /// @brief A factory method for sequence basic_node objects with rvalue sequence_type objects.
+ /// @param[in] seq A rvalue sequence node value.
+ /// @return A YAML sequence node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/sequence/
+ static basic_node sequence(sequence_type&& seq) {
+ basic_node node;
+ node.m_attrs = detail::node_attr_bits::seq_bit;
+ node.m_value.p_seq = detail::create_object<sequence_type>(std::move(seq));
+ return node;
+ } // LCOV_EXCL_LINE
+
+ /// @brief A factory method for mapping basic_node objects without mapping_type objects.
+ /// @return A YAML mapping node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/
+ static basic_node mapping() {
+ basic_node node;
+ node.m_attrs = detail::node_attr_bits::map_bit;
+ node.m_value.p_map = detail::create_object<mapping_type>();
+ return node;
+ } // LCOV_EXCL_LINE
+
+ /// @brief A factory method for mapping basic_node objects with lvalue mapping_type objects.
+ /// @param[in] map A lvalue mapping node value.
+ /// @return A YAML mapping node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/
+ static basic_node mapping(const mapping_type& map) {
+ basic_node node;
+ node.m_attrs = detail::node_attr_bits::map_bit;
+ node.m_value.p_map = detail::create_object<mapping_type>(map);
+ return node;
+ } // LCOV_EXCL_LINE
+
+ /// @brief A factory method for mapping basic_node objects with rvalue mapping_type objects.
+ /// @param[in] map A rvalue mapping node value.
+ /// @return A YAML mapping node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/mapping/
+ static basic_node mapping(mapping_type&& map) {
+ basic_node node;
+ node.m_attrs = detail::node_attr_bits::map_bit;
+ node.m_value.p_map = detail::create_object<mapping_type>(std::move(map));
+ return node;
+ } // LCOV_EXCL_LINE
+
+ /// @brief A factory method for alias basic_node objects referencing the given anchor basic_node object.
+ /// @note The given anchor basic_node must have a non-empty anchor name.
+ /// @param[in] anchor_node A basic_node object with an anchor name.
+ /// @return An alias YAML node created from the given anchor node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/alias_of/
+ static basic_node alias_of(const basic_node& anchor_node) {
+ constexpr detail::node_attr_t anchor_bit = detail::node_attr_bits::anchor_bit;
+
+ if FK_YAML_UNLIKELY (!anchor_node.has_anchor_name() || !(anchor_node.m_attrs & anchor_bit)) {
+ throw fkyaml::exception("Cannot create an alias without anchor name.");
+ }
+
+ basic_node node = anchor_node;
+ node.m_attrs &= ~detail::node_attr_mask::anchoring;
+ node.m_attrs |= detail::node_attr_bits::alias_bit;
+ return node;
+ } // LCOV_EXCL_LINE
+
+public:
+ /// @brief A copy assignment operator of the basic_node class.
+ /// @param[in] rhs A lvalue basic_node object to be copied with.
+ /// @return Reference to this basic_node object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator=/
+ basic_node& operator=(const basic_node& rhs) noexcept {
+ basic_node(rhs).swap(*this);
+ return *this;
+ }
+
+ /// @brief A move assignment operator of the basic_node class.
+ /// @param[in] rhs A rvalue basic_node object to be moved from.
+ /// @return Reference to this basic_node object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator=/
+ basic_node& operator=(basic_node&& rhs) noexcept {
+ basic_node(std::move(rhs)).swap(*this);
+ return *this;
+ }
+
+ /// @brief A subscript operator of the basic_node class with a key of a compatible type with basic_node.
+ /// @tparam KeyType A key type compatible with basic_node
+ /// @param key A key to the target value in a sequence/mapping node.
+ /// @return The value associated with the given key, or a default basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator[]/
+ template <
+ typename KeyType, detail::enable_if_t<
+ detail::conjunction<
+ detail::negation<detail::is_basic_node<KeyType>>,
+ detail::is_node_compatible_type<basic_node, KeyType>>::value,
+ int> = 0>
+ basic_node& operator[](KeyType&& key) {
+ basic_node& act_node = resolve_reference();
+
+ if FK_YAML_UNLIKELY (act_node.is_scalar_impl()) {
+ throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type());
+ }
+
+ basic_node key_node = std::forward<KeyType>(key);
+
+ if (act_node.is_sequence_impl()) {
+ // Do not use is_integer_impl() since n may be an anchor or alias.
+ if FK_YAML_UNLIKELY (!key_node.is_integer()) {
+ throw fkyaml::type_error(
+ "An argument of operator[] for sequence nodes must be an integer.", get_type());
+ }
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return act_node.m_value.p_seq->operator[](key_node.get_value<int>());
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return act_node.m_value.p_map->operator[](std::move(key_node));
+ }
+
+ /// @brief A subscript operator of the basic_node class with a key of a compatible type with basic_node.
+ /// @tparam KeyType A key type compatible with basic_node
+ /// @param key A key to the target value in a sequence/mapping node.
+ /// @return The value associated with the given key, or a default basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator[]/
+ template <
+ typename KeyType, detail::enable_if_t<
+ detail::conjunction<
+ detail::negation<detail::is_basic_node<KeyType>>,
+ detail::is_node_compatible_type<basic_node, KeyType>>::value,
+ int> = 0>
+ const basic_node& operator[](KeyType&& key) const {
+ const basic_node& act_node = resolve_reference();
+
+ if FK_YAML_UNLIKELY (act_node.is_scalar_impl()) {
+ throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type());
+ }
+
+ basic_node key_node = std::forward<KeyType>(key);
+
+ if (act_node.is_sequence_impl()) {
+ if FK_YAML_UNLIKELY (!key_node.is_integer_impl()) {
+ throw fkyaml::type_error(
+ "An argument of operator[] for sequence nodes must be an integer.", get_type());
+ }
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return act_node.m_value.p_seq->operator[](key_node.get_value<int>());
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return act_node.m_value.p_map->operator[](std::move(key_node));
+ }
+
+ /// @brief A subscript operator of the basic_node class with a basic_node key object.
+ /// @tparam KeyType A key type which is a kind of the basic_node template class.
+ /// @param key A key to the target value in a sequence/mapping node.
+ /// @return The value associated with the given key, or a default basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator[]/
+ template <typename KeyType, detail::enable_if_t<detail::is_basic_node<KeyType>::value, int> = 0>
+ basic_node& operator[](KeyType&& key) {
+ if FK_YAML_UNLIKELY (is_scalar()) {
+ throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type());
+ }
+
+ const node_value& node_value = resolve_reference().m_value;
+
+ if (is_sequence()) {
+ if FK_YAML_UNLIKELY (!key.is_integer()) {
+ throw fkyaml::type_error(
+ "An argument of operator[] for sequence nodes must be an integer.", get_type());
+ }
+ FK_YAML_ASSERT(node_value.p_seq != nullptr);
+ return node_value.p_seq->operator[](std::forward<KeyType>(key).template get_value<int>());
+ }
+
+ FK_YAML_ASSERT(node_value.p_map != nullptr);
+ return node_value.p_map->operator[](std::forward<KeyType>(key));
+ }
+
+ /// @brief A subscript operator of the basic_node class with a basic_node key object.
+ /// @tparam KeyType A key type which is a kind of the basic_node template class.
+ /// @param key A key to the target value in a sequence/mapping node.
+ /// @return The value associated with the given key, or a default basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator[]/
+ template <typename KeyType, detail::enable_if_t<detail::is_basic_node<KeyType>::value, int> = 0>
+ const basic_node& operator[](KeyType&& key) const {
+ if FK_YAML_UNLIKELY (is_scalar()) {
+ throw fkyaml::type_error("operator[] is unavailable for a scalar node.", get_type());
+ }
+
+ const node_value& node_value = resolve_reference().m_value;
+
+ if (is_sequence()) {
+ if FK_YAML_UNLIKELY (!key.is_integer()) {
+ throw fkyaml::type_error(
+ "An argument of operator[] for sequence nodes must be an integer.", get_type());
+ }
+ FK_YAML_ASSERT(node_value.p_seq != nullptr);
+ return node_value.p_seq->operator[](key.template get_value<int>());
+ }
+
+ FK_YAML_ASSERT(node_value.p_map != nullptr);
+ return node_value.p_map->operator[](std::forward<KeyType>(key));
+ }
+
+ /// @brief An equal-to operator of the basic_node class.
+ /// @param rhs A basic_node object to be compared with this basic_node object.
+ /// @return true if both types and values are equal, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_eq/
+ bool operator==(const basic_node& rhs) const noexcept {
+ const basic_node& lhs = resolve_reference();
+ const basic_node& act_rhs = rhs.resolve_reference();
+
+ const detail::node_attr_t lhs_val_bit = lhs.m_attrs & detail::node_attr_mask::value;
+ if (lhs_val_bit != (act_rhs.m_attrs & detail::node_attr_mask::value)) {
+ return false;
+ }
+
+ bool ret = false;
+ switch (lhs_val_bit) {
+ case detail::node_attr_bits::seq_bit:
+ ret = (*(lhs.m_value.p_seq) == *(act_rhs.m_value.p_seq));
+ break;
+ case detail::node_attr_bits::map_bit:
+ ret = (*(lhs.m_value.p_map) == *(act_rhs.m_value.p_map));
+ break;
+ case detail::node_attr_bits::null_bit:
+ // Always true for comparisons between null nodes.
+ ret = true;
+ break;
+ case detail::node_attr_bits::bool_bit:
+ ret = (lhs.m_value.boolean == act_rhs.m_value.boolean);
+ break;
+ case detail::node_attr_bits::int_bit:
+ ret = (lhs.m_value.integer == act_rhs.m_value.integer);
+ break;
+ case detail::node_attr_bits::float_bit:
+ ret =
+ (std::abs(lhs.m_value.float_val - act_rhs.m_value.float_val) <
+ std::numeric_limits<float_number_type>::epsilon());
+ break;
+ case detail::node_attr_bits::string_bit:
+ ret = (*(lhs.m_value.p_str) == *(act_rhs.m_value.p_str));
+ break;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+
+ return ret;
+ }
+
+ /// @brief A not-equal-to operator of the basic_node class.
+ /// @param rhs A basic_node object to be compared with this basic_node object.
+ /// @return true if either types or values are different, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_ne/
+ bool operator!=(const basic_node& rhs) const noexcept {
+ return !operator==(rhs);
+ }
+
+ /// @brief A less-than operator of the basic_node class.
+ /// @param rhs A basic_node object to be compared with this basic_node object.
+ /// @return true this basic_node object is less than `rhs`.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_lt/
+ bool operator<(const basic_node& rhs) const noexcept {
+ if (operator==(rhs)) {
+ return false;
+ }
+
+ const basic_node& lhs = resolve_reference();
+ const basic_node& act_rhs = rhs.resolve_reference();
+
+ const detail::node_attr_t lhs_val_bit = lhs.m_attrs & detail::node_attr_mask::value;
+ const detail::node_attr_t rhs_val_bit = act_rhs.m_attrs & detail::node_attr_mask::value;
+
+ if (lhs_val_bit < rhs_val_bit) {
+ return true;
+ }
+
+ if (lhs_val_bit != rhs_val_bit) {
+ return false;
+ }
+
+ bool ret = false;
+ switch (lhs_val_bit) {
+ case detail::node_attr_bits::seq_bit:
+ ret = (*(lhs.m_value.p_seq) < *(act_rhs.m_value.p_seq));
+ break;
+ case detail::node_attr_bits::map_bit:
+ ret = (*(lhs.m_value.p_map) < *(act_rhs.m_value.p_map));
+ break;
+ case detail::node_attr_bits::null_bit: // LCOV_EXCL_LINE
+ // Will not come here since null nodes are always the same.
+ detail::unreachable(); // LCOV_EXCL_LINE
+ case detail::node_attr_bits::bool_bit:
+ // false < true
+ ret = (!lhs.m_value.boolean && act_rhs.m_value.boolean);
+ break;
+ case detail::node_attr_bits::int_bit:
+ ret = (lhs.m_value.integer < act_rhs.m_value.integer);
+ break;
+ case detail::node_attr_bits::float_bit:
+ ret = (lhs.m_value.float_val < act_rhs.m_value.float_val);
+ break;
+ case detail::node_attr_bits::string_bit:
+ ret = (*(lhs.m_value.p_str) < *(act_rhs.m_value.p_str));
+ break;
+ default: // LCOV_EXCL_LINE
+ detail::unreachable(); // LCOV_EXCL_LINE
+ }
+
+ return ret;
+ }
+
+ /// @brief A less-than-or-equal-to operator of the basic_node class.
+ /// @param rhs A basic_node object to be compared with this basic_node object.
+ /// @return true this basic_node object is less than or equal to `rhs`.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_le/
+ bool operator<=(const basic_node& rhs) const noexcept {
+ return !rhs.operator<(*this);
+ }
+
+ /// @brief A greater-than operator of the basic_node class.
+ /// @param rhs A basic_node object to be compared with this basic_node object.
+ /// @return true this basic_node object is greater than `rhs`.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_gt/
+ bool operator>(const basic_node& rhs) const noexcept {
+ return !operator<=(rhs);
+ }
+
+ /// @brief A greater-than-or-equal-to operator of the basic_node class.
+ /// @param rhs A basic_node object to be compared with this basic_node object.
+ /// @return true this basic_node object is greater than or equal to `rhs`.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/operator_ge/
+ bool operator>=(const basic_node& rhs) const noexcept {
+ return !operator<(rhs);
+ }
+
+public:
+ /// @brief Returns the type of the current basic_node value.
+ /// @return The type of the YAML node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_type/
+ node_type get_type() const noexcept {
+ return detail::node_attr_bits::to_node_type(resolve_reference().m_attrs);
+ }
+
+ /// @brief Returns the type of the current basic_node value.
+ /// @deprecated Use get_type() function. (since 0.3.12)
+ /// @return The type of the YAML node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/type/
+ FK_YAML_DEPRECATED("Since 0.3.12; Use get_type()")
+ node_t type() const noexcept {
+ node_type tmp_type = get_type();
+ return detail::convert_from_node_type(tmp_type);
+ }
+
+ /// @brief Tests whether the current basic_node value is of sequence type.
+ /// @return true if the type is sequence, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_sequence/
+ bool is_sequence() const noexcept {
+ return resolve_reference().is_sequence_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of mapping type.
+ /// @return true if the type is mapping, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_mapping/
+ bool is_mapping() const noexcept {
+ return resolve_reference().is_mapping_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of null type.
+ /// @return true if the type is null, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_null/
+ bool is_null() const noexcept {
+ return resolve_reference().is_null_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of boolean type.
+ /// @return true if the type is boolean, false otherwise
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_boolean/
+ bool is_boolean() const noexcept {
+ return resolve_reference().is_boolean_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of integer type.
+ /// @return true if the type is integer, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_integer/
+ bool is_integer() const noexcept {
+ return resolve_reference().is_integer_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of float number type.
+ /// @return true if the type is floating point number, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_float_number/
+ bool is_float_number() const noexcept {
+ return resolve_reference().is_float_number_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of string type.
+ /// @return true if the type is string, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_string/
+ bool is_string() const noexcept {
+ return resolve_reference().is_string_impl();
+ }
+
+ /// @brief Tests whether the current basic_node value is of scalar types.
+ /// @return true if the type is scalar, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_scalar/
+ bool is_scalar() const noexcept {
+ return resolve_reference().is_scalar_impl();
+ }
+
+ /// @brief Tests whether the current basic_node is an anchor node.
+ /// @return true if the current basic_node is an anchor node, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_anchor/
+ bool is_anchor() const noexcept {
+ return m_attrs & detail::node_attr_bits::anchor_bit;
+ }
+
+ /// @brief Tests whether the current basic_node is an alias node.
+ /// @return true if the current basic_node is an alias node, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/is_alias/
+ bool is_alias() const noexcept {
+ return m_attrs & detail::node_attr_bits::alias_bit;
+ }
+
+ /// @brief Tests whether the current basic_node value (sequence, mapping, string) is empty.
+ /// @return true if the node value is empty, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/empty/
+ bool empty() const {
+ const basic_node& act_node = resolve_reference();
+ switch (act_node.m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit: {
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return act_node.m_value.p_seq->empty();
+ }
+ case detail::node_attr_bits::map_bit: {
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return act_node.m_value.p_map->empty();
+ }
+ case detail::node_attr_bits::string_bit: {
+ FK_YAML_ASSERT(act_node.m_value.p_str != nullptr);
+ return act_node.m_value.p_str->empty();
+ }
+ default:
+ throw fkyaml::type_error("The target node is not of a container type.", get_type());
+ }
+ }
+
+ /// @brief Returns the size of the current basic_node value (sequence, mapping, string).
+ /// @return The size of a node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/size/
+ std::size_t size() const {
+ const basic_node& act_node = resolve_reference();
+ switch (act_node.m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return act_node.m_value.p_seq->size();
+ case detail::node_attr_bits::map_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return act_node.m_value.p_map->size();
+ case detail::node_attr_bits::string_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_str != nullptr);
+ return act_node.m_value.p_str->size();
+ default:
+ throw fkyaml::type_error("The target node is not of a container type.", get_type());
+ }
+ }
+
+ /// @brief Check whether this basic_node object has a given key in its inner mapping node value.
+ /// @tparam KeyType A key type compatible with basic_node.
+ /// @param key A key to the target value in the mapping node value.
+ /// @return true if the target node is a mapping and has the given key, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/contains/
+ template <
+ typename KeyType, detail::enable_if_t<
+ detail::disjunction<
+ detail::is_basic_node<KeyType>,
+ detail::is_node_compatible_type<basic_node, detail::remove_cvref_t<KeyType>>>::value,
+ int> = 0>
+ bool contains(KeyType&& key) const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.m_attrs & detail::node_attr_bits::map_bit) {
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ const auto& map = *act_node.m_value.p_map;
+ return map.find(std::forward<KeyType>(key)) != map.end();
+ }
+
+ return false;
+ }
+
+ /// @brief Get a basic_node object with a key of a compatible type.
+ /// @tparam KeyType A key type compatible with basic_node
+ /// @param key A key to the target basic_node object in a sequence/mapping node.
+ /// @return Reference to the basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/at/
+ template <
+ typename KeyType, detail::enable_if_t<
+ detail::conjunction<
+ detail::negation<detail::is_basic_node<KeyType>>,
+ detail::is_node_compatible_type<basic_node, KeyType>>::value,
+ int> = 0>
+ basic_node& at(KeyType&& key) {
+ basic_node& act_node = resolve_reference();
+
+ if FK_YAML_UNLIKELY (act_node.is_scalar_impl()) {
+ throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type());
+ }
+
+ basic_node node_key = std::forward<KeyType>(key);
+
+ if (act_node.is_sequence_impl()) {
+ if FK_YAML_UNLIKELY (!node_key.is_integer_impl()) {
+ throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type());
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ sequence_type& seq = *act_node.m_value.p_seq;
+ int index = std::move(node_key).template get_value<int>();
+ int size = static_cast<int>(seq.size());
+ if FK_YAML_UNLIKELY (index >= size) {
+ throw fkyaml::out_of_range(index);
+ }
+ return seq[index];
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ mapping_type& map = *act_node.m_value.p_map;
+ const bool is_found = map.find(node_key) != map.end();
+ if FK_YAML_UNLIKELY (!is_found) {
+ throw fkyaml::out_of_range(serialize(node_key).c_str());
+ }
+ return map[std::move(node_key)];
+ }
+
+ /// @brief Get a basic_node object with a key of a compatible type.
+ /// @tparam KeyType A key type compatible with basic_node
+ /// @param key A key to the target basic_node object in a sequence/mapping node.
+ /// @return Constant reference to the basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/at/
+ template <
+ typename KeyType, detail::enable_if_t<
+ detail::conjunction<
+ detail::negation<detail::is_basic_node<KeyType>>,
+ detail::is_node_compatible_type<basic_node, KeyType>>::value,
+ int> = 0>
+ const basic_node& at(KeyType&& key) const {
+ const basic_node& act_node = resolve_reference();
+
+ if FK_YAML_UNLIKELY (act_node.is_scalar_impl()) {
+ throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type());
+ }
+
+ basic_node node_key = std::forward<KeyType>(key);
+
+ if (act_node.is_sequence_impl()) {
+ if FK_YAML_UNLIKELY (!node_key.is_integer()) {
+ throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type());
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ const sequence_type& seq = *act_node.m_value.p_seq;
+ int index = std::move(node_key).template get_value<int>();
+ int size = static_cast<int>(seq.size());
+ if FK_YAML_UNLIKELY (index >= size) {
+ throw fkyaml::out_of_range(index);
+ }
+ return seq[index];
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ const mapping_type& map = *act_node.m_value.p_map;
+ const bool is_found = map.find(node_key) != map.end();
+ if FK_YAML_UNLIKELY (!is_found) {
+ throw fkyaml::out_of_range(serialize(node_key).c_str());
+ }
+ return map.at(std::move(node_key));
+ }
+
+ /// @brief Get a basic_node object with a basic_node key object.
+ /// @tparam KeyType A key type which is a kind of the basic_node template class.
+ /// @param key A key to the target basic_node object in a sequence/mapping node.
+ /// @return Reference to the basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/at/
+ template <typename KeyType, detail::enable_if_t<detail::is_basic_node<KeyType>::value, int> = 0>
+ basic_node& at(KeyType&& key) {
+ basic_node& act_node = resolve_reference();
+ if FK_YAML_UNLIKELY (act_node.is_scalar_impl()) {
+ throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type());
+ }
+
+ if (act_node.is_sequence_impl()) {
+ if FK_YAML_UNLIKELY (!key.is_integer()) {
+ throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type());
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ sequence_type& seq = *act_node.m_value.p_seq;
+ int index = std::forward<KeyType>(key).template get_value<int>();
+ int size = static_cast<int>(seq.size());
+ if FK_YAML_UNLIKELY (index >= size) {
+ throw fkyaml::out_of_range(index);
+ }
+ return seq[index];
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ mapping_type& map = *act_node.m_value.p_map;
+ bool is_found = map.find(key) != map.end();
+ if FK_YAML_UNLIKELY (!is_found) {
+ throw fkyaml::out_of_range(serialize(key).c_str());
+ }
+ return map[std::forward<KeyType>(key)];
+ }
+
+ /// @brief Get a basic_node object with a basic_node key object.
+ /// @tparam KeyType A key type which is a kind of the basic_node template class.
+ /// @param key A key to the target basic_node object in a sequence/mapping node.
+ /// @return Constant reference to the basic_node object associated with the given key.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/at/
+ template <typename KeyType, detail::enable_if_t<detail::is_basic_node<KeyType>::value, int> = 0>
+ const basic_node& at(KeyType&& key) const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_UNLIKELY (act_node.is_scalar_impl()) {
+ throw fkyaml::type_error("at() is unavailable for a scalar node.", get_type());
+ }
+
+ if (act_node.is_sequence_impl()) {
+ if FK_YAML_UNLIKELY (!key.is_integer()) {
+ throw fkyaml::type_error("An argument of at() for sequence nodes must be an integer.", get_type());
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ const sequence_type& seq = *act_node.m_value.p_seq;
+ int index = std::forward<KeyType>(key).template get_value<int>();
+ int size = static_cast<int>(seq.size());
+ if FK_YAML_UNLIKELY (index >= size) {
+ throw fkyaml::out_of_range(index);
+ }
+ return seq[index];
+ }
+
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ const mapping_type& map = *act_node.m_value.p_map;
+ bool is_found = map.find(key) != map.end();
+ if FK_YAML_UNLIKELY (!is_found) {
+ throw fkyaml::out_of_range(serialize(key).c_str());
+ }
+ return map.at(std::forward<KeyType>(key));
+ }
+
+ /// @brief Get the YAML version for this basic_node object.
+ /// @return The YAML version if already set, `yaml_version_type::VERSION_1_2` otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version_type/
+ yaml_version_type get_yaml_version_type() const noexcept {
+ return mp_meta->is_version_specified ? mp_meta->version : yaml_version_type::VERSION_1_2;
+ }
+
+ /// @brief Set the YAML version for this basic_node object.
+ /// @param[in] version The target YAML version.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version_type/
+ void set_yaml_version_type(const yaml_version_type version) noexcept {
+ mp_meta->version = version;
+ mp_meta->is_version_specified = true;
+ }
+
+ /// @brief Get the YAML version for this basic_node object.
+ /// @deprecated Use get_yaml_version_type() function. (since 0.3.12)
+ /// @return The YAML version if already set, `yaml_version_t::VER_1_2` otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_yaml_version/
+ FK_YAML_DEPRECATED("Since 0.3.12; Use get_yaml_version_type()")
+ yaml_version_t get_yaml_version() const noexcept {
+ yaml_version_type tmp_type = get_yaml_version_type();
+ return detail::convert_from_yaml_version_type(tmp_type);
+ }
+
+ /// @brief Set the YAML version for this basic_node object.
+ /// @deprecated Use set_yaml_version_type(yaml_version_type) function. (since 0.3.12)
+ /// @param[in] version The target YAML version.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/set_yaml_version/
+ FK_YAML_DEPRECATED("Since 0.3.12; Use set_yaml_version_type(const yaml_version_type)")
+ void set_yaml_version(const yaml_version_t version) noexcept {
+ set_yaml_version_type(detail::convert_to_yaml_version_type(version));
+ }
+
+ /// @brief Check whether this basic_node object has already had any anchor name.
+ /// @return true if ths basic_node has an anchor name, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/has_anchor_name/
+ bool has_anchor_name() const noexcept {
+ return (m_attrs & detail::node_attr_mask::anchoring) && !m_prop.anchor.empty();
+ }
+
+ /// @brief Get the anchor name associated with this basic_node object.
+ /// @note Some anchor name must be set before calling this method. Call has_anchor_name() to see if this basic_node
+ /// object has any anchor name.
+ /// @return The anchor name associated with the node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_anchor_name/
+ const std::string& get_anchor_name() const {
+ if FK_YAML_UNLIKELY (!has_anchor_name()) {
+ throw fkyaml::exception("No anchor name has been set.");
+ }
+ return m_prop.anchor;
+ }
+
+ /// @brief Add an anchor name to this basic_node object.
+ /// @note If this basic_node object has already had any anchor name, the new anchor name will overwrite the old one.
+ /// @param[in] anchor_name An anchor name. This should not be empty.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_anchor_name/
+ void add_anchor_name(const std::string& anchor_name) {
+ if (is_anchor()) {
+ m_attrs &= ~detail::node_attr_mask::anchoring;
+ auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first;
+ std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs));
+ mp_meta.reset();
+ itr->second.swap(*this);
+ mp_meta->anchor_table.erase(itr);
+ }
+
+ auto p_meta = mp_meta;
+
+ basic_node node;
+ node.swap(*this);
+ p_meta->anchor_table.emplace(anchor_name, std::move(node));
+
+ m_attrs &= ~detail::node_attr_mask::anchoring;
+ m_attrs |= detail::node_attr_bits::anchor_bit;
+ mp_meta = p_meta;
+ const auto offset = static_cast<uint32_t>(mp_meta->anchor_table.count(anchor_name) - 1);
+ detail::node_attr_bits::set_anchor_offset(offset, m_attrs);
+ m_prop.anchor = anchor_name;
+ }
+
+ /// @brief Add an anchor name to this basic_node object.
+ /// @note If this basic_node object has already had any anchor name, the new anchor name will overwrite the old one.
+ /// @param[in] anchor_name An anchor name. This should not be empty.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_anchor_name/
+ void add_anchor_name(std::string&& anchor_name) {
+ if (is_anchor()) {
+ m_attrs &= ~detail::node_attr_mask::anchoring;
+ auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first;
+ std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs));
+ mp_meta.reset();
+ itr->second.swap(*this);
+ mp_meta->anchor_table.erase(itr);
+ }
+
+ auto p_meta = mp_meta;
+
+ basic_node node;
+ node.swap(*this);
+ p_meta->anchor_table.emplace(anchor_name, std::move(node));
+
+ m_attrs &= ~detail::node_attr_mask::anchoring;
+ m_attrs |= detail::node_attr_bits::anchor_bit;
+ mp_meta = p_meta;
+ auto offset = static_cast<uint32_t>(mp_meta->anchor_table.count(anchor_name) - 1);
+ detail::node_attr_bits::set_anchor_offset(offset, m_attrs);
+ m_prop.anchor = std::move(anchor_name);
+ }
+
+ /// @brief Check whether this basic_node object has already had any tag name.
+ /// @return true if ths basic_node has a tag name, false otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/has_tag_name/
+ bool has_tag_name() const noexcept {
+ return !m_prop.tag.empty();
+ }
+
+ /// @brief Get the tag name associated with this basic_node object.
+ /// @note Some tag name must be set before calling this method. Call has_tag_name() to see if this basic_node
+ /// object has any tag name.
+ /// @return The tag name associated with the node. It may be empty.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_tag_name/
+ const std::string& get_tag_name() const {
+ if FK_YAML_UNLIKELY (!has_tag_name()) {
+ throw fkyaml::exception("No tag name has been set.");
+ }
+ return m_prop.tag;
+ }
+
+ /// @brief Add a tag name to this basic_node object.
+ /// @note If this basic_node object has already had any tag name, the new tag name will overwrite the old one.
+ /// @param[in] tag_name A tag name to get associated with this basic_node object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_tag_name/
+ void add_tag_name(const std::string& tag_name) {
+ m_prop.tag = tag_name;
+ }
+
+ /// @brief Add a tag name to this basic_node object.
+ /// @note If this basic_node object has already had any tag name, the new tag name will overwrite the old one.
+ /// @param[in] tag_name A tag name to get associated with this basic_node object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/add_tag_name/
+ void add_tag_name(std::string&& tag_name) {
+ m_prop.tag = std::move(tag_name);
+ }
+
+ /// @brief Get the node value object converted into a given type.
+ /// @note This function requires T objects to be default constructible. Also, T cannot be either a reference,
+ /// pointer or C-style array type.
+ /// @tparam T A compatible value type which may be cv-qualified.
+ /// @tparam ValueType A compatible value type (T without cv-qualifiers by default).
+ /// @return A value converted from this basic_node object.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value/
+ template <
+ typename T, typename ValueType = detail::remove_cv_t<T>,
+ detail::enable_if_t<
+ detail::conjunction<std::is_default_constructible<ValueType>, detail::negation<std::is_pointer<T>>>::value,
+ int> = 0>
+ T get_value() const noexcept(
+ noexcept(std::declval<const basic_node&>().template get_value_impl<ValueType>(std::declval<ValueType&>()))) {
+ // emit a compile error if T is either a reference, pointer or C-style array type.
+ static_assert(
+ !std::is_reference<T>::value,
+ "get_value() cannot be called with reference types. "
+ "You might want to call one of as_seq(), as_map(), as_bool(), as_int(), as_float() or as_str().");
+ static_assert(
+ !std::is_array<T>::value,
+ "get_value() cannot be called with C-style array types. You might want to call get_value_inplace().");
+
+ auto ret = ValueType();
+ resolve_reference().get_value_impl(ret);
+ return ret;
+ }
+
+ /// @brief Get the node value object converted into a given type. The conversion result is filled into `value_ref`.
+ /// @tparam T A compatible value type.
+ /// @param value_ref A storage into which the conversion result is filled.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_inplace/
+ template <typename T>
+ void get_value_inplace(T& value_ref) const
+ noexcept(noexcept(std::declval<const basic_node&>().template get_value_impl<T>(std::declval<T&>()))) {
+ resolve_reference().get_value_impl(value_ref);
+ }
+
+ /// @brief Get the node value object converted to a given type. If the conversion fails, this function returns a
+ /// given default value instead.
+ /// @note This function requires T to be default constructible. Also, T cannot be either a reference, pointer or
+ /// C-style array type.
+ /// @tparam T A compatible value type which may be cv-qualified.
+ /// @tparam U A default value type from which T must be constructible.
+ /// @param default_value The default value returned if conversion fails.
+ /// @return A value converted from this basic_node object if conversion succeeded, the given default value
+ /// otherwise.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_or/
+ template <
+ typename T, typename U,
+ detail::enable_if_t<
+ detail::conjunction<
+ std::is_constructible<T, U>, std::is_default_constructible<T>,
+ detail::negation<std::is_pointer<T>>>::value,
+ int> = 0>
+ T get_value_or(U&& default_value) const noexcept {
+ static_assert(
+ !std::is_reference<T>::value,
+ "get_value_or() cannot be called with reference types. "
+ "You might want to call one of as_seq(), as_map(), as_bool(), as_int(), as_float() or as_str().");
+ static_assert(
+ !std::is_array<T>::value,
+ "get_value_or() cannot be called with C-style array types. You might want to call get_value_inplace().");
+
+ // TODO:
+ // Ideally, there should be no exception thrown in this kind of function. However, achieving that would require
+ // a lot of refactoring and/or some API changes, especially `from_node` interface definition. So, try-catch is
+ // used instead for now.
+ try {
+ return get_value<T>();
+ }
+ catch (const std::exception& /*unused*/) {
+ // Any exception derived from std::exception is interpreted as a conversion failure in some way
+ // since user-defined from_node function may throw a different object from a fkyaml::type_error.
+ // and std::exception is usually the base class of user-defined exception types.
+ return std::forward<U>(default_value);
+ }
+ }
+
+ /// @brief Explicit reference access to the internally stored YAML node value.
+ /// @tparam ReferenceType Reference type to the target YAML node value.
+ /// @return Reference to the internally stored YAML node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_ref/
+ template <typename ReferenceType, detail::enable_if_t<std::is_reference<ReferenceType>::value, int> = 0>
+ FK_YAML_DEPRECATED("Since 0.4.3; Use one of as_seq(), as_map(), as_bool(), as_int(), as_float() or as_str()")
+ ReferenceType get_value_ref() {
+ return get_value_ref_impl(static_cast<detail::add_pointer_t<ReferenceType>>(nullptr));
+ }
+
+ /// @brief Explicit reference access to the internally stored YAML node value.
+ /// @tparam ReferenceType Constant reference type to the target YAML node value.
+ /// @return Constant reference to the internally stored YAML node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/get_value_ref/
+ template <
+ typename ReferenceType,
+ detail::enable_if_t<
+ detail::conjunction<
+ std::is_reference<ReferenceType>, std::is_const<detail::remove_reference_t<ReferenceType>>>::value,
+ int> = 0>
+ FK_YAML_DEPRECATED("Since 0.4.3; Use one of as_seq(), as_map(), as_bool(), as_int(), as_float() or as_str()")
+ ReferenceType get_value_ref() const {
+ return get_value_ref_impl(static_cast<detail::add_pointer_t<ReferenceType>>(nullptr));
+ }
+
+ /// @brief Returns reference to the sequence node value.
+ /// @throw fkyaml::type_error The node value is not a sequence.
+ /// @return Reference to the sequence node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_seq/
+ sequence_type& as_seq() {
+ basic_node& act_node = resolve_reference(); // NOLINT(misc-const-correctness)
+ if FK_YAML_LIKELY (act_node.is_sequence_impl()) {
+ return *act_node.m_value.p_seq;
+ }
+ throw fkyaml::type_error("The node value is not a sequence.", get_type());
+ }
+
+ /// @brief Returns constant reference to the sequence node value.
+ /// @throw fkyaml::type_error The node value is not a sequence.
+ /// @return Constant reference to the sequence node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_seq/
+ const sequence_type& as_seq() const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_sequence_impl()) {
+ return *act_node.m_value.p_seq;
+ }
+ throw fkyaml::type_error("The node value is not a sequence.", get_type());
+ }
+
+ /// @brief Returns reference to the mapping node value.
+ /// @throw fkyaml::type_error The node value is not a mapping.
+ /// @return Reference to the mapping node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_map/
+ mapping_type& as_map() {
+ basic_node& act_node = resolve_reference(); // NOLINT(misc-const-correctness)
+ if FK_YAML_LIKELY (act_node.is_mapping_impl()) {
+ return *act_node.m_value.p_map;
+ }
+ throw fkyaml::type_error("The node value is not a mapping.", get_type());
+ }
+
+ /// @brief Returns constant reference to the mapping node value.
+ /// @throw fkyaml::type_error The node value is not a mapping.
+ /// @return Constant reference to the mapping node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_map/
+ const mapping_type& as_map() const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_mapping_impl()) {
+ return *act_node.m_value.p_map;
+ }
+ throw fkyaml::type_error("The node value is not a mapping.", get_type());
+ }
+
+ /// @brief Returns reference to the boolean node value.
+ /// @throw fkyaml::type_error The node value is not a boolean.
+ /// @return Reference to the boolean node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_bool/
+ boolean_type& as_bool() {
+ basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_boolean_impl()) {
+ return act_node.m_value.boolean;
+ }
+ throw fkyaml::type_error("The node value is not a boolean.", get_type());
+ }
+
+ /// @brief Returns reference to the boolean node value.
+ /// @throw fkyaml::type_error The node value is not a boolean.
+ /// @return Constant reference to the boolean node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_bool/
+ const boolean_type& as_bool() const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_boolean_impl()) {
+ return act_node.m_value.boolean;
+ }
+ throw fkyaml::type_error("The node value is not a boolean.", get_type());
+ }
+
+ /// @brief Returns reference to the integer node value.
+ /// @throw fkyaml::type_error The node value is not an integer.
+ /// @return Reference to the integer node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_int/
+ integer_type& as_int() {
+ basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_integer_impl()) {
+ return act_node.m_value.integer;
+ }
+ throw fkyaml::type_error("The node value is not an integer.", get_type());
+ }
+
+ /// @brief Returns reference to the integer node value.
+ /// @throw fkyaml::type_error The node value is not an integer.
+ /// @return Constant reference to the integer node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_int/
+ const integer_type& as_int() const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_integer_impl()) {
+ return act_node.m_value.integer;
+ }
+ throw fkyaml::type_error("The node value is not an integer.", get_type());
+ }
+
+ /// @brief Returns reference to the float node value.
+ /// @throw fkyaml::type_error The node value is not a float.
+ /// @return Reference to the float node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_float/
+ float_number_type& as_float() {
+ basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_float_number_impl()) {
+ return act_node.m_value.float_val;
+ }
+ throw fkyaml::type_error("The node value is not a float.", get_type());
+ }
+
+ /// @brief Returns reference to the float node value.
+ /// @throw fkyaml::type_error The node value is not a float.
+ /// @return Constant reference to the float node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_float/
+ const float_number_type& as_float() const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_float_number_impl()) {
+ return act_node.m_value.float_val;
+ }
+ throw fkyaml::type_error("The node value is not a float.", get_type());
+ }
+
+ /// @brief Returns reference to the string node value.
+ /// @throw fkyaml::type_error The node value is not a string.
+ /// @return Reference to the string node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_str/
+ string_type& as_str() {
+ basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_string_impl()) {
+ return *act_node.m_value.p_str;
+ }
+ throw fkyaml::type_error("The node value is not a string.", get_type());
+ }
+
+ /// @brief Returns reference to the string node value.
+ /// @throw fkyaml::type_error The node value is not a string.
+ /// @return Constant reference to the string node value.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/as_str/
+ const string_type& as_str() const {
+ const basic_node& act_node = resolve_reference();
+ if FK_YAML_LIKELY (act_node.is_string_impl()) {
+ return *act_node.m_value.p_str;
+ }
+ throw fkyaml::type_error("The node value is not a string.", get_type());
+ }
+
+ /// @brief Swaps the internally stored data with the specified basic_node object.
+ /// @param[in] rhs A basic_node object to be swapped with.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/swap/
+ void swap(basic_node& rhs) noexcept {
+ using std::swap;
+ swap(m_attrs, rhs.m_attrs);
+ swap(mp_meta, rhs.mp_meta);
+
+ node_value tmp {};
+ std::memcpy(&tmp, &m_value, sizeof(node_value));
+ std::memcpy(&m_value, &rhs.m_value, sizeof(node_value));
+ std::memcpy(&rhs.m_value, &tmp, sizeof(node_value));
+
+ swap(m_prop.tag, rhs.m_prop.tag);
+ swap(m_prop.anchor, rhs.m_prop.anchor);
+ }
+
+ /// @brief Returns an iterator to the first element of a container node (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return An iterator to the first element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/
+ iterator begin() {
+ basic_node& act_node = resolve_reference();
+ switch (act_node.m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return {act_node.m_value.p_seq->begin()};
+ case detail::node_attr_bits::map_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return {act_node.m_value.p_map->begin()};
+ default:
+ throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type());
+ }
+ }
+
+ /// @brief Returns a const iterator to the first element of a container node (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the first element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/
+ const_iterator begin() const {
+ const basic_node& act_node = resolve_reference();
+ switch (act_node.m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return {act_node.m_value.p_seq->begin()};
+ case detail::node_attr_bits::map_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return {act_node.m_value.p_map->begin()};
+ default:
+ throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type());
+ }
+ }
+
+ /// @brief Returns a const iterator to the first element of a container node (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the first element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/begin/
+ const_iterator cbegin() const {
+ return begin();
+ }
+
+ /// @brief Returns an iterator to the past-the-last element of a container node (sequence or mapping).
+ /// @throw `type_error` if the basic_node value is not of container types.
+ /// @return An iterator to the past-the-last element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/
+ iterator end() {
+ basic_node& act_node = resolve_reference();
+ switch (act_node.m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return {act_node.m_value.p_seq->end()};
+ case detail::node_attr_bits::map_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return {act_node.m_value.p_map->end()};
+ default:
+ throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type());
+ }
+ }
+
+ /// @brief Returns a const iterator to the past-the-last element of a container node (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the past-the-last element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/
+ const_iterator end() const {
+ const basic_node& act_node = resolve_reference();
+ switch (act_node.m_attrs & detail::node_attr_mask::value) {
+ case detail::node_attr_bits::seq_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_seq != nullptr);
+ return {act_node.m_value.p_seq->end()};
+ case detail::node_attr_bits::map_bit:
+ FK_YAML_ASSERT(act_node.m_value.p_map != nullptr);
+ return {act_node.m_value.p_map->end()};
+ default:
+ throw fkyaml::type_error("The target node is neither of sequence nor mapping types.", get_type());
+ }
+ }
+
+ /// @brief Returns a const iterator to the past-the-last element of a container node (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the past-the-last element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/end/
+ const_iterator cend() const {
+ return end();
+ }
+
+ /// @brief Returns an iterator to the reverse-beginning (i.e., last) element of a container node (sequence or
+ /// mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return An iterator to the reverse-beginning element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/rbegin/
+ reverse_iterator rbegin() {
+ return {end()};
+ }
+
+ /// @brief Returns a const iterator to the reverse-beginning (i.e., last) element of a container node (sequence or
+ /// mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the reverse-beginning element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/rbegin/
+ const_reverse_iterator rbegin() const {
+ return {end()};
+ }
+
+ /// @brief Returns a const iterator to the reverse-beginning (i.e., last) element of a container node (sequence or
+ /// mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the reverse-beginning element of a container node.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/rbegin/
+ const_reverse_iterator crbegin() const {
+ return rbegin();
+ }
+
+ /// @brief Returns an iterator to the reverse-end (i.e., one before the first) element of a container node (sequence
+ /// or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return An iterator to the reverse-end element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/rend/
+ reverse_iterator rend() {
+ return {begin()};
+ }
+
+ /// @brief Returns a const iterator to the reverse-end (i.e., one before the first) element of a container node
+ /// (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the reverse-end element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/rend/
+ const_reverse_iterator rend() const {
+ return {begin()};
+ }
+
+ /// @brief Returns a const iterator to the reverse-end (i.e., one before the first) element of a container node
+ /// (sequence or mapping).
+ /// @throw `type_error` if this basic_node is neither a sequence nor mapping node.
+ /// @return A const iterator to the reverse-end element.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/rend/
+ const_reverse_iterator crend() const {
+ return rend();
+ }
+
+ /// @brief Returns a range of mapping entries.
+ /// @throw `type_error` if this basic_node is not a mapping.
+ /// @return A range of mapping entries.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/map_items/
+ map_range map_items() {
+ if FK_YAML_UNLIKELY (!is_mapping()) {
+ throw type_error("map_items() cannot be called on a non-mapping node.", get_type());
+ }
+ return {*this};
+ }
+
+ /// @brief Returns a const range of mapping entries.
+ /// @throw `type_error` if this basic_node is not a mapping.
+ /// @return A const range of mapping entries.
+ /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/map_items/
+ const_map_range map_items() const {
+ if FK_YAML_UNLIKELY (!is_mapping()) {
+ throw type_error("map_items() cannot be called on a non-mapping node.", get_type());
+ }
+ return {*this};
+ }
+
+private:
+ /// @brief Resolves anchor/alias reference and returns reference to an actual value node.
+ /// @return Reference to an actual value node.
+ basic_node& resolve_reference() {
+ if FK_YAML_UNLIKELY (has_anchor_name()) {
+ auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first;
+ std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs));
+ return itr->second;
+ }
+ return *this;
+ }
+
+ /// @brief Resolves anchor/alias reference and returns const reference to an actual value node.
+ /// @return Const reference to an actual value node.
+ const basic_node& resolve_reference() const {
+ if FK_YAML_UNLIKELY (has_anchor_name()) {
+ auto itr = mp_meta->anchor_table.equal_range(m_prop.anchor).first;
+ std::advance(itr, detail::node_attr_bits::get_anchor_offset(m_attrs));
+ return itr->second;
+ }
+ return *this;
+ }
+
+ bool is_sequence_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::seq_bit;
+ }
+
+ bool is_mapping_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::map_bit;
+ }
+
+ bool is_null_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::null_bit;
+ }
+
+ bool is_boolean_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::bool_bit;
+ }
+
+ bool is_integer_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::int_bit;
+ }
+
+ bool is_float_number_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::float_bit;
+ }
+
+ bool is_string_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::string_bit;
+ }
+
+ bool is_scalar_impl() const noexcept {
+ return m_attrs & detail::node_attr_bits::scalar_bits;
+ }
+
+ template <
+ typename ValueType, detail::enable_if_t<detail::negation<detail::is_basic_node<ValueType>>::value, int> = 0>
+ void get_value_impl(ValueType& v) const
+ noexcept(noexcept(ConverterType<ValueType, void>::from_node(std::declval<const basic_node&>(), v))) {
+ ConverterType<ValueType, void>::from_node(*this, v);
+ }
+
+ template <typename ValueType, detail::enable_if_t<detail::is_basic_node<ValueType>::value, int> = 0>
+ void get_value_impl(ValueType& v) const {
+ v = *this;
+ }
+
+ /// @brief Returns reference to the sequence node value.
+ /// @throw fkyaml::exception The node value is not a sequence.
+ /// @return Reference to the sequence node value.
+ sequence_type& get_value_ref_impl(sequence_type* /*unused*/) {
+ return as_seq();
+ }
+
+ /// @brief Returns constant reference to the sequence node value.
+ /// @throw fkyaml::exception The node value is not a sequence.
+ /// @return Constant reference to the sequence node value.
+ const sequence_type& get_value_ref_impl(const sequence_type* /*unused*/) const {
+ return as_seq();
+ }
+
+ /// @brief Returns reference to the mapping node value.
+ /// @throw fkyaml::exception The node value is not a mapping.
+ /// @return Reference to the mapping node value.
+ mapping_type& get_value_ref_impl(mapping_type* /*unused*/) {
+ return as_map();
+ }
+
+ /// @brief Returns constant reference to the mapping node value.
+ /// @throw fkyaml::exception The node value is not a mapping.
+ /// @return Constant reference to the mapping node value.
+ const mapping_type& get_value_ref_impl(const mapping_type* /*unused*/) const {
+ return as_map();
+ }
+
+ /// @brief Returns reference to the boolean node value.
+ /// @throw fkyaml::exception The node value is not a boolean.
+ /// @return Reference to the boolean node value.
+ boolean_type& get_value_ref_impl(boolean_type* /*unused*/) {
+ return as_bool();
+ }
+
+ /// @brief Returns reference to the boolean node value.
+ /// @throw fkyaml::exception The node value is not a boolean.
+ /// @return Constant reference to the boolean node value.
+ const boolean_type& get_value_ref_impl(const boolean_type* /*unused*/) const {
+ return as_bool();
+ }
+
+ /// @brief Returns reference to the integer node value.
+ /// @throw fkyaml::exception The node value is not an integer.
+ /// @return Reference to the integer node value.
+ integer_type& get_value_ref_impl(integer_type* /*unused*/) {
+ return as_int();
+ }
+
+ /// @brief Returns reference to the integer node value.
+ /// @throw fkyaml::exception The node value is not an integer.
+ /// @return Constant reference to the integer node value.
+ const integer_type& get_value_ref_impl(const integer_type* /*unused*/) const {
+ return as_int();
+ }
+
+ /// @brief Returns reference to the floating point number node value.
+ /// @throw fkyaml::exception The node value is not a floating point number.
+ /// @return Reference to the floating point number node value.
+ float_number_type& get_value_ref_impl(float_number_type* /*unused*/) {
+ return as_float();
+ }
+
+ /// @brief Returns reference to the floating point number node value.
+ /// @throw fkyaml::exception The node value is not a floating point number.
+ /// @return Constant reference to the floating point number node value.
+ const float_number_type& get_value_ref_impl(const float_number_type* /*unused*/) const {
+ return as_float();
+ }
+
+ /// @brief Returns reference to the string node value.
+ /// @throw fkyaml::exception The node value is not a string.
+ /// @return Reference to the string node value.
+ string_type& get_value_ref_impl(string_type* /*unused*/) {
+ return as_str();
+ }
+
+ /// @brief Returns reference to the string node value.
+ /// @throw fkyaml::exception The node value is not a string.
+ /// @return Constant reference to the string node value.
+ const string_type& get_value_ref_impl(const string_type* /*unused*/) const {
+ return as_str();
+ }
+
+ /// The current node attributes.
+ detail::node_attr_t m_attrs {detail::node_attr_bits::default_bits};
+ /// The shared set of YAML directives applied to this node.
+ mutable std::shared_ptr<detail::document_metainfo<basic_node>> mp_meta {
+ // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
+ std::shared_ptr<detail::document_metainfo<basic_node>>(new detail::document_metainfo<basic_node>())};
+ /// The current node value.
+ node_value m_value {};
+ /// The property set of this node.
+ detail::node_property m_prop {};
+};
+
+/// @brief Swap function for basic_node objects.
+/// @param[in] lhs A left-side-hand basic_node object to be swapped with.
+/// @param[in] rhs A right-side-hand basic_node object to be swapped with.
+/// @sa https://fktn-k.github.io/fkYAML/api/swap/
+template <
+ template <typename, typename...> class SequenceType, template <typename, typename, typename...> class MappingType,
+ typename BooleanType, typename IntegerType, typename FloatNumberType, typename StringType,
+ template <typename, typename = void> class ConverterType>
+inline void swap(
+ basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>& lhs,
+ basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>&
+ rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ lhs.swap(rhs);
+}
+
+/// @brief Insertion operator for basic_node template class. A wrapper for the serialization feature.
+/// @param[in] os An output stream object.
+/// @param[in] n A basic_node object.
+/// @return Reference to the output stream object `os`.
+/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/insertion_operator/
+template <
+ template <typename, typename...> class SequenceType, template <typename, typename, typename...> class MappingType,
+ typename BooleanType, typename IntegerType, typename FloatNumberType, typename StringType,
+ template <typename, typename = void> class ConverterType>
+inline std::ostream& operator<<(
+ std::ostream& os,
+ const basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>&
+ n) {
+ os << basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>::
+ serialize(n);
+ return os;
+}
+
+/// @brief Extraction operator for basic_node template class. A wrapper for the deserialization feature with input
+/// streams.
+/// @param[in] is An input stream object.
+/// @param[in] n A basic_node object.
+/// @return Reference to the input stream object `is`.
+/// @sa https://fktn-k.github.io/fkYAML/api/basic_node/extraction_operator/
+template <
+ template <typename, typename...> class SequenceType, template <typename, typename, typename...> class MappingType,
+ typename BooleanType, typename IntegerType, typename FloatNumberType, typename StringType,
+ template <typename, typename = void> class ConverterType>
+inline std::istream& operator>>(
+ std::istream& is,
+ basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>& n) {
+ n = basic_node<SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>::
+ deserialize(is);
+ return is;
+}
+
+/// @brief namespace for user-defined literals for the fkYAML library.
+inline namespace literals {
+/// @brief namespace for user-defined literals for YAML node objects.
+inline namespace yaml_literals {
+
+// Whitespace before the literal operator identifier is deprecated in C++23 or better but required in C++11.
+// Ignore the warning as a workaround. https://github.com/fktn-k/fkYAML/pull/417
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 6)
+#define FK_YAML_QUOTE_OPERATOR operator""_yaml
+#else
+#define FK_YAML_QUOTE_OPERATOR operator"" _yaml
+#endif
+
+/// @brief The user-defined string literal which deserializes a `char` array into a `node` object.
+/// @param s An input `char` array.
+/// @param n The size of `s`.
+/// @return The resulting `node` object deserialized from `s`.
+/// @sa https://fktn-k.github.io/fkYAML/api/operator_literal_yaml/
+inline fkyaml::node FK_YAML_QUOTE_OPERATOR(const char* s, std::size_t n) {
+ return fkyaml::node::deserialize(s, s + n);
+}
+
+/// @brief The user-defined string literal which deserializes a `char16_t` array into a `node` object.
+/// @param s An input `char16_t` array.
+/// @param n The size of `s`.
+/// @return The resulting `node` object deserialized from `s`.
+/// @sa https://fktn-k.github.io/fkYAML/api/operator_literal_yaml/
+inline fkyaml::node FK_YAML_QUOTE_OPERATOR(const char16_t* s, std::size_t n) {
+ return fkyaml::node::deserialize(s, s + n);
+}
+
+/// @brief The user-defined string literal which deserializes a `char32_t` array into a `node` object.
+/// @param s An input `char32_t` array.
+/// @param n The size of `s`.
+/// @return The resulting `node` object deserialized from `s`.
+/// @sa https://fktn-k.github.io/fkYAML/api/operator_literal_yaml/
+inline fkyaml::node FK_YAML_QUOTE_OPERATOR(const char32_t* s, std::size_t n) {
+ return fkyaml::node::deserialize(s, s + n);
+}
+
+#if FK_YAML_HAS_CHAR8_T
+/// @brief The user-defined string literal which deserializes a `char8_t` array into a `node` object.
+/// @param s An input `char8_t` array.
+/// @param n The size of `s`.
+/// @return The resulting `node` object deserialized from `s`.
+inline fkyaml::node FK_YAML_QUOTE_OPERATOR(const char8_t* s, std::size_t n) {
+ return fkyaml::node::deserialize((const char8_t*)s, (const char8_t*)s + n);
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+} // namespace yaml_literals
+} // namespace literals
+
+FK_YAML_NAMESPACE_END
+
+namespace std {
+
+template <
+ template <typename, typename...> class SequenceType, template <typename, typename, typename...> class MappingType,
+ typename BooleanType, typename IntegerType, typename FloatNumberType, typename StringType,
+ template <typename, typename = void> class ConverterType>
+// NOLINTNEXTLINE(cert-dcl58-cpp)
+struct hash<fkyaml::basic_node<
+ SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>> {
+ using node_t = fkyaml::basic_node<
+ SequenceType, MappingType, BooleanType, IntegerType, FloatNumberType, StringType, ConverterType>;
+
+ std::size_t operator()(const node_t& n) const {
+ using boolean_type = typename node_t::boolean_type;
+ using integer_type = typename node_t::integer_type;
+ using float_number_type = typename node_t::float_number_type;
+ using string_type = typename node_t::string_type;
+
+ const auto type = n.get_type();
+
+ std::size_t seed = 0;
+ hash_combine(seed, std::hash<uint8_t>()(static_cast<uint8_t>(type)));
+
+ switch (type) {
+ case fkyaml::node_type::SEQUENCE:
+ hash_combine(seed, n.size());
+ for (const auto& elem : n) {
+ hash_combine(seed, operator()(elem));
+ }
+ return seed;
+
+ case fkyaml::node_type::MAPPING:
+ hash_combine(seed, n.size());
+ for (auto itr = n.begin(), end_itr = n.end(); itr != end_itr; ++itr) {
+ hash_combine(seed, operator()(itr.key()));
+ hash_combine(seed, operator()(itr.value()));
+ }
+ return seed;
+
+ case fkyaml::node_type::NULL_OBJECT:
+ hash_combine(seed, 0);
+ return seed;
+ case fkyaml::node_type::BOOLEAN:
+ hash_combine(seed, std::hash<boolean_type>()(n.template get_value<boolean_type>()));
+ return seed;
+ case fkyaml::node_type::INTEGER:
+ hash_combine(seed, std::hash<integer_type>()(n.template get_value<integer_type>()));
+ return seed;
+ case fkyaml::node_type::FLOAT:
+ hash_combine(seed, std::hash<float_number_type>()(n.template get_value<float_number_type>()));
+ return seed;
+ case fkyaml::node_type::STRING:
+ hash_combine(seed, std::hash<string_type>()(n.template get_value<string_type>()));
+ return seed;
+ default: // LCOV_EXCL_LINE
+ fkyaml::detail::unreachable(); // LCOV_EXCL_LINE
+ }
+ }
+
+private:
+ // taken from boost::hash_combine
+ FK_YAML_NO_SANITIZE("unsigned-shift-base", "unsigned-integer-overflow")
+ static void hash_combine(std::size_t& seed, std::size_t v) {
+ seed ^= v + 0x9e3779b9 + (seed << 6u) + (seed >> 2u);
+ }
+};
+
+} // namespace std
+
+#endif /* FK_YAML_NODE_HPP */
diff --git a/deps/recastnavigation/Detour/CMakeLists.txt b/deps/recastnavigation/Detour/CMakeLists.txt
index e658bef152..e1294b0e01 100644
--- a/deps/recastnavigation/Detour/CMakeLists.txt
+++ b/deps/recastnavigation/Detour/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
-# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE
+# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
diff --git a/deps/recastnavigation/Recast/CMakeLists.txt b/deps/recastnavigation/Recast/CMakeLists.txt
index ab1641bad8..c54d570c3a 100644
--- a/deps/recastnavigation/Recast/CMakeLists.txt
+++ b/deps/recastnavigation/Recast/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
-# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE
+# Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without