//  _______   __ __   __  _____   __  __  __
// |   __| |_/  |  \_/  |/  _  \ /  \/  \|  |     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 <map>
#include <string>

// #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 uint32_t utf8_chunk = 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 uint32_t utf8_chunk = 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 uint32_t utf8_chunk = 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 uint32_t utf8_chunk = 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 <sstream>
#include <string>
#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(s, 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 */
