summaryrefslogtreecommitdiffstats
path: root/src/third-party/scnlib/include/scn/detail
diff options
context:
space:
mode:
Diffstat (limited to 'src/third-party/scnlib/include/scn/detail')
-rw-r--r--src/third-party/scnlib/include/scn/detail/args.h619
-rw-r--r--src/third-party/scnlib/include/scn/detail/config.h466
-rw-r--r--src/third-party/scnlib/include/scn/detail/context.h126
-rw-r--r--src/third-party/scnlib/include/scn/detail/error.h136
-rw-r--r--src/third-party/scnlib/include/scn/detail/file.h568
-rw-r--r--src/third-party/scnlib/include/scn/detail/fwd.h204
-rw-r--r--src/third-party/scnlib/include/scn/detail/locale.h595
-rw-r--r--src/third-party/scnlib/include/scn/detail/parse_context.h581
-rw-r--r--src/third-party/scnlib/include/scn/detail/range.h598
-rw-r--r--src/third-party/scnlib/include/scn/detail/result.h595
-rw-r--r--src/third-party/scnlib/include/scn/detail/vectored.h166
-rw-r--r--src/third-party/scnlib/include/scn/detail/visitor.h248
12 files changed, 4902 insertions, 0 deletions
diff --git a/src/third-party/scnlib/include/scn/detail/args.h b/src/third-party/scnlib/include/scn/detail/args.h
new file mode 100644
index 0000000..dd67852
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/args.h
@@ -0,0 +1,619 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_ARGS_H
+#define SCN_DETAIL_ARGS_H
+
+#include "../reader/common.h"
+#include "../util/array.h"
+
+SCN_GCC_PUSH
+SCN_GCC_IGNORE("-Wnoexcept")
+#include <string>
+SCN_GCC_POP
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * Allows reading an rvalue.
+ * Stores an rvalue and returns an lvalue reference to it via `operator()`.
+ * Create one with \ref temp.
+ */
+ template <typename T>
+ struct temporary {
+ temporary(T&& val) : value(SCN_MOVE(val)) {}
+
+ T& operator()() && noexcept
+ {
+ return value;
+ }
+
+ T value;
+ };
+ /**
+ * Factory function for \ref temporary.
+ *
+ * Canonical use case is with \ref scn::span:
+ * \code{.cpp}
+ * std::vector<char> buffer(32, '\0');
+ * auto result = scn::scan("123", "{}", scn::temp(scn::make_span(buffer)));
+ * // buffer == "123"
+ * \endcode
+ */
+ template <typename T,
+ typename std::enable_if<
+ !std::is_lvalue_reference<T>::value>::type* = nullptr>
+ temporary<T> temp(T&& val)
+ {
+ return {SCN_FWD(val)};
+ }
+
+ namespace detail {
+ enum type {
+ none_type = 0,
+ // signed integer
+ schar_type,
+ short_type,
+ int_type,
+ long_type,
+ long_long_type,
+ // unsigned integer
+ uchar_type,
+ ushort_type,
+ uint_type,
+ ulong_type,
+ ulong_long_type,
+ // other integral types
+ bool_type,
+ char_type,
+ code_point_type,
+ last_integer_type = code_point_type,
+ // floats
+ float_type,
+ double_type,
+ long_double_type,
+ last_numeric_type = long_double_type,
+ // other
+ buffer_type,
+ string_type,
+ string_view_type,
+
+ custom_type,
+ last_type = custom_type
+ };
+
+ constexpr bool is_integral(type t) noexcept
+ {
+ return t > none_type && t <= last_integer_type;
+ }
+ constexpr bool is_arithmetic(type t) noexcept
+ {
+ return t > none_type && t <= last_numeric_type;
+ }
+
+ struct custom_value {
+ // using scan_type = error (*)(void*, Context&, ParseCtx&);
+
+ void* value;
+ void (*scan)();
+ };
+
+ template <typename Context, typename ParseCtx, typename T>
+ error scan_custom_arg(void* arg, Context& ctx, ParseCtx& pctx) noexcept
+ {
+ return visitor_boilerplate<scanner<T>>(*static_cast<T*>(arg), ctx,
+ pctx);
+ }
+
+ struct monostate {
+ };
+
+ template <typename Ctx>
+ struct ctx_tag {
+ };
+ template <typename ParseCtx>
+ struct parse_ctx_tag {
+ };
+
+ class value {
+ public:
+ constexpr value() noexcept : m_empty{} {}
+
+ template <typename T>
+ explicit SCN_CONSTEXPR14 value(T& val) noexcept
+ : m_value(std::addressof(val))
+ {
+ }
+
+ template <typename Ctx, typename ParseCtx, typename T>
+ value(ctx_tag<Ctx>, parse_ctx_tag<ParseCtx>, T& val) noexcept
+ : m_custom(
+ custom_value{std::addressof(val),
+ reinterpret_cast<void (*)()>(
+ &scan_custom_arg<Ctx, ParseCtx, T>)})
+ {
+ }
+
+ template <typename T>
+ SCN_CONSTEXPR14 T& get_as() noexcept
+ {
+ return *static_cast<T*>(m_value);
+ }
+ template <typename T>
+ constexpr const T& get_as() const noexcept
+ {
+ return *static_cast<const T*>(m_value);
+ }
+
+ SCN_CONSTEXPR14 custom_value& get_custom() noexcept
+ {
+ return m_custom;
+ }
+ SCN_NODISCARD constexpr const custom_value& get_custom()
+ const noexcept
+ {
+ return m_custom;
+ }
+
+ private:
+ union {
+ monostate m_empty;
+ void* m_value;
+ custom_value m_custom;
+ };
+ };
+
+ template <typename CharT, typename T, type Type>
+ struct init {
+ T* val;
+ static const type type_tag = Type;
+
+ constexpr init(T& v) : val(std::addressof(v)) {}
+ template <typename Ctx, typename ParseCtx>
+ SCN_CONSTEXPR14 value get()
+ {
+ SCN_EXPECT(val != nullptr);
+ return value{*val};
+ }
+ };
+ template <typename CharT, typename T>
+ struct init<CharT, T, custom_type> {
+ T* val;
+ static const type type_tag = custom_type;
+
+ constexpr init(T& v) : val(std::addressof(v)) {}
+ template <typename Ctx, typename ParseCtx>
+ SCN_CONSTEXPR14 value get()
+ {
+ SCN_EXPECT(val != nullptr);
+ return {ctx_tag<Ctx>{}, parse_ctx_tag<ParseCtx>{}, *val};
+ }
+ };
+
+ template <typename Context,
+ typename ParseCtx,
+ typename T,
+ typename CharT = typename Context::char_type>
+ SCN_CONSTEXPR14 basic_arg<CharT> make_arg(T& value) noexcept;
+
+#define SCN_MAKE_VALUE(Tag, Type) \
+ template <typename CharT> \
+ constexpr init<CharT, Type, Tag> make_value(Type& val, \
+ priority_tag<1>) noexcept \
+ { \
+ return val; \
+ }
+
+ SCN_MAKE_VALUE(schar_type, signed char)
+ SCN_MAKE_VALUE(short_type, short)
+ SCN_MAKE_VALUE(int_type, int)
+ SCN_MAKE_VALUE(long_type, long)
+ SCN_MAKE_VALUE(long_long_type, long long)
+
+ SCN_MAKE_VALUE(uchar_type, unsigned char)
+ SCN_MAKE_VALUE(ushort_type, unsigned short)
+ SCN_MAKE_VALUE(uint_type, unsigned)
+ SCN_MAKE_VALUE(ulong_type, unsigned long)
+ SCN_MAKE_VALUE(ulong_long_type, unsigned long long)
+
+ SCN_MAKE_VALUE(bool_type, bool)
+ SCN_MAKE_VALUE(code_point_type, code_point)
+
+ SCN_MAKE_VALUE(float_type, float)
+ SCN_MAKE_VALUE(double_type, double)
+ SCN_MAKE_VALUE(long_double_type, long double)
+
+ SCN_MAKE_VALUE(buffer_type, span<CharT>)
+ SCN_MAKE_VALUE(string_type, std::basic_string<CharT>)
+ SCN_MAKE_VALUE(string_view_type, basic_string_view<CharT>)
+
+ template <typename CharT>
+ constexpr init<CharT, CharT, char_type> make_value(
+ CharT& val,
+ priority_tag<1>) noexcept
+ {
+ return val;
+ }
+
+ template <typename CharT, typename T>
+ constexpr inline auto make_value(T& val, priority_tag<0>) noexcept
+ -> init<CharT, T, custom_type>
+ {
+ return val;
+ }
+
+ enum : size_t {
+ packed_arg_bitsize = 5,
+ packed_arg_mask = (1 << packed_arg_bitsize) - 1,
+ max_packed_args = (sizeof(size_t) * 8 - 1) / packed_arg_bitsize,
+ is_unpacked_bit = size_t{1} << (sizeof(size_t) * 8ull - 1ull)
+ };
+ } // namespace detail
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wpadded")
+
+ /// Type-erased scanning argument.
+ template <typename CharT>
+ class SCN_TRIVIAL_ABI basic_arg {
+ public:
+ using char_type = CharT;
+
+ class handle {
+ public:
+ explicit handle(detail::custom_value custom) : m_custom(custom) {}
+
+ template <typename Context, typename ParseCtx>
+ error scan(Context& ctx, ParseCtx& pctx)
+ {
+ return reinterpret_cast<error (*)(void*, Context&, ParseCtx&)>(
+ m_custom.scan)(m_custom.value, ctx, pctx);
+ }
+
+ private:
+ detail::custom_value m_custom;
+ };
+
+ constexpr basic_arg() = default;
+
+ constexpr explicit operator bool() const noexcept
+ {
+ return m_type != detail::none_type;
+ }
+
+ SCN_NODISCARD constexpr detail::type type() const noexcept
+ {
+ return type;
+ }
+ SCN_NODISCARD constexpr bool is_integral() const noexcept
+ {
+ return detail::is_integral(m_type);
+ }
+ SCN_NODISCARD constexpr bool is_arithmetic() const noexcept
+ {
+ return detail::is_arithmetic(m_type);
+ }
+
+ private:
+ constexpr basic_arg(detail::value v, detail::type t) noexcept
+ : m_value(v), m_type(t)
+ {
+ }
+
+ template <typename Ctx, typename ParseCtx, typename T, typename C>
+ friend SCN_CONSTEXPR14 basic_arg<C> detail::make_arg(T& value) noexcept;
+
+ template <typename C, typename Visitor>
+ friend SCN_CONSTEXPR14 error visit_arg(Visitor&& vis,
+ basic_arg<C>& arg);
+
+ friend class basic_args<CharT>;
+
+ detail::value m_value;
+ detail::type m_type{detail::none_type};
+ };
+
+ SCN_CLANG_POP
+
+ template <typename CharT, typename Visitor>
+ SCN_CONSTEXPR14 error visit_arg(Visitor&& vis, basic_arg<CharT>& arg)
+ {
+ switch (arg.m_type) {
+ case detail::none_type:
+ break;
+
+ case detail::schar_type:
+ return vis(arg.m_value.template get_as<signed char>());
+ case detail::short_type:
+ return vis(arg.m_value.template get_as<short>());
+ case detail::int_type:
+ return vis(arg.m_value.template get_as<int>());
+ case detail::long_type:
+ return vis(arg.m_value.template get_as<long>());
+ case detail::long_long_type:
+ return vis(arg.m_value.template get_as<long long>());
+
+ case detail::uchar_type:
+ return vis(arg.m_value.template get_as<unsigned char>());
+ case detail::ushort_type:
+ return vis(arg.m_value.template get_as<unsigned short>());
+ case detail::uint_type:
+ return vis(arg.m_value.template get_as<unsigned int>());
+ case detail::ulong_type:
+ return vis(arg.m_value.template get_as<unsigned long>());
+ case detail::ulong_long_type:
+ return vis(arg.m_value.template get_as<unsigned long long>());
+
+ case detail::bool_type:
+ return vis(arg.m_value.template get_as<bool>());
+ case detail::char_type:
+ return vis(arg.m_value.template get_as<CharT>());
+ case detail::code_point_type:
+ return vis(arg.m_value.template get_as<code_point>());
+
+ case detail::float_type:
+ return vis(arg.m_value.template get_as<float>());
+ case detail::double_type:
+ return vis(arg.m_value.template get_as<double>());
+ case detail::long_double_type:
+ return vis(arg.m_value.template get_as<long double>());
+
+ case detail::buffer_type:
+ return vis(arg.m_value.template get_as<span<CharT>>());
+ case detail::string_type:
+ return vis(
+ arg.m_value.template get_as<std::basic_string<CharT>>());
+ case detail::string_view_type:
+ return vis(
+ arg.m_value.template get_as<basic_string_view<CharT>>());
+
+ case detail::custom_type:
+ return vis(typename basic_arg<CharT>::handle(
+ arg.m_value.get_custom()));
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wcovered-switch-default")
+ default:
+ return vis(detail::monostate{});
+ SCN_CLANG_POP
+ }
+ SCN_UNREACHABLE;
+ }
+
+ namespace detail {
+ template <typename CharT, typename T>
+ struct get_type {
+ using value_type = decltype(make_value<CharT>(
+ SCN_DECLVAL(typename std::remove_reference<
+ typename std::remove_cv<T>::type>::type&),
+ SCN_DECLVAL(priority_tag<1>)));
+ static const type value = value_type::type_tag;
+ };
+
+ template <typename CharT>
+ constexpr size_t get_types()
+ {
+ return 0;
+ }
+ template <typename CharT, typename Arg, typename... Args>
+ constexpr size_t get_types()
+ {
+ return static_cast<size_t>(get_type<CharT, Arg>::value) |
+ (get_types<CharT, Args...>() << 5);
+ }
+
+ template <typename Context,
+ typename ParseCtx,
+ typename T,
+ typename CharT>
+ SCN_CONSTEXPR14 basic_arg<CharT> make_arg(T& value) noexcept
+ {
+ basic_arg<CharT> arg;
+ arg.m_type = get_type<CharT, T>::value;
+ arg.m_value = make_value<CharT>(value, priority_tag<1>{})
+ .template get<Context, ParseCtx>();
+ return arg;
+ }
+
+ template <bool Packed,
+ typename Context,
+ typename ParseCtx,
+ typename T,
+ typename CharT = typename Context::char_type>
+ inline auto make_arg(T& v) ->
+ typename std::enable_if<Packed, value>::type
+ {
+ return make_value<CharT>(v, priority_tag<1>{})
+ .template get<Context, ParseCtx>();
+ }
+ template <bool Packed, typename Context, typename ParseCtx, typename T>
+ inline auto make_arg(T& v) -> typename std::
+ enable_if<!Packed, basic_arg<typename Context::char_type>>::type
+ {
+ return make_arg<Context, ParseCtx>(v);
+ }
+ } // namespace detail
+
+ template <typename CharT, typename... Args>
+ class arg_store {
+ static constexpr const size_t num_args = sizeof...(Args);
+ static const bool is_packed = num_args < detail::max_packed_args;
+
+ friend class basic_args<CharT>;
+
+ static constexpr size_t get_types()
+ {
+ return is_packed ? detail::get_types<CharT, Args...>()
+ : detail::is_unpacked_bit | num_args;
+ }
+
+ public:
+ static constexpr size_t types = get_types();
+ using arg_type = basic_arg<CharT>;
+
+ using value_type =
+ typename std::conditional<is_packed, detail::value, arg_type>::type;
+ static constexpr size_t data_size =
+ num_args + (is_packed && num_args != 0 ? 0 : 1);
+
+ template <typename Ctx, typename ParseCtx>
+ SCN_CONSTEXPR14 arg_store(detail::ctx_tag<Ctx>,
+ detail::parse_ctx_tag<ParseCtx>,
+ Args&... a) noexcept
+ : m_data{{detail::make_arg<is_packed, Ctx, ParseCtx>(a)...}}
+ {
+ }
+
+ SCN_CONSTEXPR14 span<value_type> data() noexcept
+ {
+ return make_span(m_data.data(),
+ static_cast<std::ptrdiff_t>(m_data.size()));
+ }
+
+ private:
+ detail::array<value_type, data_size> m_data;
+ };
+
+ template <typename Context, typename ParseCtx, typename... Args>
+ arg_store<typename Context::char_type, Args...> make_args(Args&... args)
+ {
+ return {detail::ctx_tag<Context>(), detail::parse_ctx_tag<ParseCtx>(),
+ args...};
+ }
+ template <typename WrappedRange,
+ typename Format,
+ typename... Args,
+ typename CharT = typename WrappedRange::char_type>
+ arg_store<CharT, Args...> make_args_for(WrappedRange&,
+ Format,
+ Args&... args)
+ {
+ using context_type = basic_context<WrappedRange>;
+ using parse_context_type =
+ typename detail::parse_context_template_for_format<
+ Format>::template type<typename context_type::char_type>;
+ return {detail::ctx_tag<context_type>(),
+ detail::parse_ctx_tag<parse_context_type>(), args...};
+ }
+
+ template <typename CharT>
+ class basic_args {
+ public:
+ using arg_type = basic_arg<CharT>;
+
+ constexpr basic_args() noexcept = default;
+
+ template <typename... Args>
+ SCN_CONSTEXPR14 basic_args(arg_store<CharT, Args...>& store) noexcept
+ : m_types(store.types)
+ {
+ set_data(store.m_data.data());
+ }
+
+ SCN_CONSTEXPR14 basic_args(span<arg_type> args) noexcept
+ : m_types(detail::is_unpacked_bit | args.size())
+ {
+ set_data(args.data());
+ }
+
+ SCN_CONSTEXPR14 arg_type get(std::ptrdiff_t i) const noexcept
+ {
+ return do_get(i);
+ }
+
+ SCN_NODISCARD SCN_CONSTEXPR14 bool check_id(
+ std::ptrdiff_t i) const noexcept
+ {
+ if (!is_packed()) {
+ return static_cast<size_t>(i) <
+ (m_types &
+ ~static_cast<size_t>(detail::is_unpacked_bit));
+ }
+ return type(i) != detail::none_type;
+ }
+
+ SCN_NODISCARD constexpr size_t max_size() const noexcept
+ {
+ return is_packed()
+ ? static_cast<size_t>(detail::max_packed_args)
+ : m_types &
+ ~static_cast<size_t>(detail::is_unpacked_bit);
+ }
+
+ private:
+ size_t m_types{0};
+ union {
+ detail::value* m_values;
+ arg_type* m_args;
+ };
+
+ SCN_NODISCARD constexpr bool is_packed() const noexcept
+ {
+ return (m_types & detail::is_unpacked_bit) == 0;
+ }
+
+ SCN_NODISCARD SCN_CONSTEXPR14 typename detail::type type(
+ std::ptrdiff_t i) const noexcept
+ {
+ size_t shift = static_cast<size_t>(i) * detail::packed_arg_bitsize;
+ return static_cast<typename detail::type>(
+ (static_cast<size_t>(m_types) >> shift) &
+ detail::packed_arg_mask);
+ }
+
+ SCN_CONSTEXPR14 void set_data(detail::value* values) noexcept
+ {
+ m_values = values;
+ }
+ SCN_CONSTEXPR14 void set_data(arg_type* args) noexcept
+ {
+ m_args = args;
+ }
+
+ SCN_CONSTEXPR14 arg_type do_get(std::ptrdiff_t i) const noexcept
+ {
+ SCN_EXPECT(i >= 0);
+
+ arg_type arg;
+ if (!is_packed()) {
+ auto num_args = static_cast<std::ptrdiff_t>(max_size());
+ if (SCN_LIKELY(i < num_args)) {
+ arg = m_args[i];
+ }
+ return arg;
+ }
+
+ SCN_EXPECT(m_values);
+ if (SCN_UNLIKELY(
+ i > static_cast<std::ptrdiff_t>(detail::max_packed_args))) {
+ return arg;
+ }
+
+ arg.m_type = type(i);
+ if (arg.m_type == detail::none_type) {
+ return arg;
+ }
+ arg.m_value = m_values[i];
+ return arg;
+ }
+ };
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_ARGS_H
diff --git a/src/third-party/scnlib/include/scn/detail/config.h b/src/third-party/scnlib/include/scn/detail/config.h
new file mode 100644
index 0000000..81d054c
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/config.h
@@ -0,0 +1,466 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_CONFIG_H
+#define SCN_DETAIL_CONFIG_H
+
+#include <cassert>
+
+#define SCN_STD_11 201103L
+#define SCN_STD_14 201402L
+#define SCN_STD_17 201703L
+
+#define SCN_COMPILER(major, minor, patch) \
+ ((major)*10000000 /* 10,000,000 */ + (minor)*10000 /* 10,000 */ + (patch))
+#define SCN_VERSION SCN_COMPILER(1, 1, 2)
+
+#ifdef __INTEL_COMPILER
+// Intel
+#define SCN_INTEL \
+ SCN_COMPILER(__INTEL_COMPILER / 100, (__INTEL_COMPILER / 10) % 10, \
+ __INTEL_COMPILER % 10)
+#elif defined(_MSC_VER) && defined(_MSC_FULL_VER)
+// MSVC
+#if _MSC_VER == _MSC_FULL_VER / 10000
+#define SCN_MSVC \
+ SCN_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000)
+#else
+#define SCN_MSVC \
+ SCN_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 10, \
+ _MSC_FULL_VER % 100000)
+#endif // _MSC_VER == _MSC_FULL_VER / 10000
+#elif defined(__clang__) && defined(__clang_minor__) && \
+ defined(__clang_patchlevel__)
+// Clang
+#define SCN_CLANG \
+ SCN_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+ defined(__GNUC_PATCHLEVEL__)
+// GCC
+#define SCN_GCC SCN_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#endif
+
+#ifndef SCN_INTEL
+#define SCN_INTEL 0
+#endif
+#ifndef SCN_MSVC
+#define SCN_MSVC 0
+#endif
+#ifndef SCN_CLANG
+#define SCN_CLANG 0
+#endif
+#ifndef SCN_GCC
+#define SCN_GCC 0
+#endif
+
+// Pretending to be gcc (clang, icc, etc.)
+#ifdef __GNUC__
+
+#ifdef __GNUC_MINOR__
+#define SCN_GCC_COMPAT_MINOR __GNUC_MINOR__
+#else
+#define SCN_GCC_COMPAT_MINOR 0
+#endif
+
+#ifdef __GNUC_PATCHLEVEL__
+#define SCN_GCC_COMPAT_PATCHLEVEL __GNUC_PATCHLEVEL__
+#else
+#define SCN_GCC_COMPAT_PATCHLEVEL 0
+#endif
+
+#define SCN_GCC_COMPAT \
+ SCN_COMPILER(__GNUC__, SCN_GCC_COMPAT_MINOR, SCN_GCC_COMPAT_PATCHLEVEL)
+#else
+#define SCN_GCC_COMPAT 0
+#endif // #ifdef __GNUC__
+
+#define SCN_STRINGIFY_APPLY(x) #x
+#define SCN_STRINGIFY(x) SCN_STRINGIFY_APPLY(x)
+
+// POSIX
+#if defined(__unix__) || defined(__APPLE__)
+#define SCN_POSIX 1
+#else
+#define SCN_POSIX 0
+#endif
+
+#if defined(__APPLE__)
+#define SCN_APPLE 1
+#else
+#define SCN_APPLE 0
+#endif
+
+// Windows
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && \
+ !defined(__CYGWIN__)
+#define SCN_WINDOWS 1
+#else
+#define SCN_WINDOWS 0
+#endif
+
+#ifdef _MSVC_LANG
+#define SCN_MSVC_LANG _MSVC_LANG
+#else
+#define SCN_MSVC_LANG 0
+#endif
+
+// Standard version
+#if SCN_MSVC
+#define SCN_STD SCN_MSVC_LANG
+#else
+#define SCN_STD __cplusplus
+#endif
+
+// Warning control
+#if SCN_GCC
+#define SCN_PRAGMA_APPLY(x) _Pragma(#x)
+
+#define SCN_GCC_PUSH _Pragma("GCC diagnostic push")
+#define SCN_GCC_POP _Pragma("GCC diagnostic pop")
+
+#define SCN_GCC_IGNORE(x) SCN_PRAGMA_APPLY(GCC diagnostic ignored x)
+#else
+#define SCN_GCC_PUSH
+#define SCN_GCC_POP
+#define SCN_GCC_IGNORE(x)
+#endif
+
+#if SCN_CLANG
+#define SCN_PRAGMA_APPLY(x) _Pragma(#x)
+
+#define SCN_CLANG_PUSH _Pragma("clang diagnostic push")
+#define SCN_CLANG_POP _Pragma("clang diagnostic pop")
+
+#define SCN_CLANG_IGNORE(x) SCN_PRAGMA_APPLY(clang diagnostic ignored x)
+
+#if SCN_CLANG >= SCN_COMPILER(3, 9, 0)
+#define SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE \
+ SCN_CLANG_PUSH SCN_CLANG_IGNORE("-Wundefined-func-template")
+#define SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE SCN_CLANG_POP
+#else
+#define SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+#define SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+#endif
+
+#else
+#define SCN_CLANG_PUSH
+#define SCN_CLANG_POP
+#define SCN_CLANG_IGNORE(x)
+#define SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+#define SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+#endif
+
+#if SCN_GCC_COMPAT && defined(SCN_PRAGMA_APPLY)
+#define SCN_GCC_COMPAT_PUSH SCN_PRAGMA_APPLY(GCC diagnostic push)
+#define SCN_GCC_COMPAT_POP SCN_PRAGMA_APPLY(GCC diagnostic pop)
+#define SCN_GCC_COMPAT_IGNORE(x) SCN_PRAGMA_APPLY(GCC diagnostic ignored x)
+#else
+#define SCN_GCC_COMPAT_PUSH
+#define SCN_GCC_COMPAT_POP
+#define SCN_GCC_COMPAT_IGNORE(x)
+#endif
+
+#if SCN_MSVC
+#define SCN_MSVC_PUSH __pragma(warning(push))
+#define SCN_MSVC_POP __pragma(warning(pop))
+
+#define SCN_MSVC_IGNORE(x) __pragma(warning(disable : x))
+#else
+#define SCN_MSVC_PUSH
+#define SCN_MSVC_POP
+#define SCN_MSVC_IGNORE(x)
+#endif
+
+#ifndef SCN_PREDEFINE_VSCAN_OVERLOADS
+#define SCN_PREDEFINE_VSCAN_OVERLOADS 0
+#endif
+
+#ifdef __cpp_exceptions
+#define SCN_HAS_EXCEPTIONS 1
+#endif
+#if !defined(SCN_HAS_EXCEPTIONS) && defined(__EXCEPTIONS)
+#define SCN_HAS_EXCEPTIONS 1
+#endif
+#if !defined(SCN_HAS_EXCEPTIONS) && defined(_HAS_EXCEPTIONS)
+#if _HAS_EXCEPTIONS
+#define SCN_HAS_EXCEPTIONS 1
+#else
+#define SCN_HAS_EXCEPTIONS 0
+#endif
+#endif
+#if !defined(SCN_HAS_EXCEPTIONS) && !defined(_CPPUNWIND)
+#define SCN_HAS_EXCEPTIONS 0
+#endif
+#ifndef SCN_HAS_EXCEPTIONS
+#define SCN_HAS_EXCEPTIONS 0
+#endif
+
+#if SCN_HAS_EXCEPTIONS
+#define SCN_TRY try
+#define SCN_CATCH(x) catch (x)
+#define SCN_THROW(x) throw x
+#define SCN_RETHROW throw
+#else
+#define SCN_TRY if (true)
+#define SCN_CATCH(x) if (false)
+#define SCN_THROW(x) ::std::abort()
+#define SCN_RETHROW ::std::abort()
+#endif
+
+#ifdef __has_include
+#define SCN_HAS_INCLUDE(x) __has_include(x)
+#else
+#define SCN_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+#define SCN_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#define SCN_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#ifdef __has_feature
+#define SCN_HAS_FEATURE(x) __has_feature(x)
+#else
+#define SCN_HAS_FEATURE(x) 0
+#endif
+
+#ifdef __has_builtin
+#define SCN_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#define SCN_HAS_BUILTIN(x) 0
+#endif
+
+#if SCN_HAS_INCLUDE(<version>)
+#include <version>
+#endif
+
+#if defined(_SCN_DOXYGEN) && _SCN_DOXYGEN
+#define SCN_DOXYGEN 1
+#else
+#define SCN_DOXYGEN 0
+#endif
+
+// Detect constexpr
+#if defined(__cpp_constexpr)
+#if __cpp_constexpr >= 201304
+#define SCN_HAS_RELAXED_CONSTEXPR 1
+#else
+#define SCN_HAS_RELAXED_CONSTEXPR 0
+#endif
+#endif
+
+#ifndef SCN_HAS_RELAXED_CONSTEXPR
+#if SCN_HAS_FEATURE(cxx_relaxed_constexpr) || \
+ SCN_MSVC >= SCN_COMPILER(19, 10, 0) || \
+ ((SCN_GCC >= SCN_COMPILER(6, 0, 0) || \
+ SCN_INTEL >= SCN_COMPILER(17, 0, 0)) && \
+ SCN_STD >= SCN_STD_14)
+#define SCN_HAS_RELAXED_CONSTEXPR 1
+#else
+#define SCN_HAS_RELAXED_CONSTEXPR 0
+#endif
+#endif
+
+#if SCN_HAS_RELAXED_CONSTEXPR || SCN_DOXYGEN
+#define SCN_CONSTEXPR14 constexpr
+#else
+#define SCN_CONSTEXPR14 inline
+#endif
+
+// Detect string_view
+#if defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201603 && \
+ SCN_STD >= SCN_STD_17
+#define SCN_HAS_STRING_VIEW 1
+#else
+#define SCN_HAS_STRING_VIEW 0
+#endif
+
+// Detect [[nodiscard]]
+#if (SCN_HAS_CPP_ATTRIBUTE(nodiscard) && __cplusplus >= SCN_STD_17) || \
+ (SCN_MSVC >= SCN_COMPILER(19, 11, 0) && SCN_MSVC_LANG >= SCN_STD_17) || \
+ ((SCN_GCC >= SCN_COMPILER(7, 0, 0) || \
+ SCN_INTEL >= SCN_COMPILER(18, 0, 0)) && \
+ __cplusplus >= SCN_STD_17) && !SCN_DOXYGEN
+#define SCN_NODISCARD [[nodiscard]]
+#else
+#define SCN_NODISCARD /*nodiscard*/
+#endif
+
+// Detect [[clang::trivial_abi]]
+#if SCN_HAS_CPP_ATTRIBUTE(clang::trivial_abi)
+#define SCN_TRIVIAL_ABI [[clang::trivial_abi]]
+#else
+#define SCN_TRIVIAL_ABI /*trivial_abi*/
+#endif
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY
+#define SCN_FUNC inline
+#else
+#define SCN_FUNC
+#endif
+
+// Detect <charconv>
+
+#if defined(_GLIBCXX_RELEASE) && __cplusplus >= SCN_STD_17
+#define SCN_HAS_INTEGER_CHARCONV (_GLIBCXX_RELEASE >= 9)
+#define SCN_HAS_FLOAT_CHARCONV (_GLIBCXX_RELEASE >= 11)
+#elif SCN_MSVC >= SCN_COMPILER(19, 14, 0)
+#define SCN_HAS_INTEGER_CHARCONV 1
+#define SCN_HAS_FLOAT_CHARCONV (SCN_MSVC >= SCN_COMPILER(19, 21, 0))
+#elif defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201606
+#define SCN_HAS_INTEGER_CHARCONV 1
+#define SCN_HAS_FLOAT_CHARCONV 1
+#endif // _GLIBCXX_RELEASE
+
+#ifndef SCN_HAS_INTEGER_CHARCONV
+#define SCN_HAS_INTEGER_CHARCONV 0
+#define SCN_HAS_FLOAT_CHARCONV 0
+#endif
+
+// Detect std::launder
+#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
+#define SCN_HAS_LAUNDER 1
+#else
+#define SCN_HAS_LAUNDER 0
+#endif
+
+// Detect __assume
+#if SCN_INTEL || SCN_MSVC
+#define SCN_HAS_ASSUME 1
+#else
+#define SCN_HAS_ASSUME 0
+#endif
+
+// Detect __builtin_assume
+#if SCN_HAS_BUILTIN(__builtin_assume)
+#define SCN_HAS_BUILTIN_ASSUME 1
+#else
+#define SCN_HAS_BUILTIN_ASSUME 0
+#endif
+
+// Detect __builtin_unreachable
+#if SCN_HAS_BUILTIN(__builtin_unreachable) || SCN_GCC_COMPAT
+#define SCN_HAS_BUILTIN_UNREACHABLE 1
+#else
+#define SCN_HAS_BUILTIN_UNREACHABLE 0
+#endif
+
+#if SCN_HAS_ASSUME
+#define SCN_ASSUME(x) __assume(x)
+#elif SCN_HAS_BUILTIN_ASSUME
+#define SCN_ASSUME(x) __builtin_assume(x)
+#elif SCN_HAS_BUILTIN_UNREACHABLE
+#define SCN_ASSUME(x) ((x) ? static_cast<void>(0) : __builtin_unreachable())
+#else
+#define SCN_ASSUME(x) static_cast<void>((x) ? 0 : 0)
+#endif
+
+#if SCN_HAS_BUILTIN_UNREACHABLE
+#define SCN_UNREACHABLE __builtin_unreachable()
+#else
+#define SCN_UNREACHABLE SCN_ASSUME(0)
+#endif
+
+// Detect __builtin_expect
+#if SCN_HAS_BUILTIN(__builtin_expect) || SCN_GCC_COMPAT
+#define SCN_HAS_BUILTIN_EXPECT 1
+#else
+#define SCN_HAS_BUILTIN_EXPECT 0
+#endif
+
+#if SCN_HAS_BUILTIN_EXPECT
+#define SCN_LIKELY(x) __builtin_expect(!!(x), 1)
+#define SCN_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define SCN_LIKELY(x) (x)
+#define SCN_UNLIKELY(x) (x)
+#endif
+
+#ifndef SCN_DEPRECATED
+
+#if (SCN_HAS_CPP_ATTRIBUTE(deprecated) && SCN_STD >= 201402L) || \
+ SCN_MSVC >= SCN_COMPILER(19, 0, 0) || SCN_DOXYGEN
+#define SCN_DEPRECATED [[deprecated]]
+#else
+
+#if SCN_GCC_COMPAT
+#define SCN_DEPRECATED __attribute__((deprecated))
+#elif SCN_MSVC
+#define SCN_DEPRECATED __declspec(deprecated)
+#else
+#define SCN_DEPRECATED /* deprecated */
+#endif
+
+#endif
+
+#endif // !defined(SCN_DEPRECATED)
+
+// Detect concepts
+#if defined(__cpp_concepts) && __cpp_concepts >= 201907L
+#define SCN_HAS_CONCEPTS 1
+#else
+#define SCN_HAS_CONCEPTS 0
+#endif
+
+// Detect ranges
+#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
+#define SCN_HAS_RANGES 1
+#else
+#define SCN_HAS_RANGES 0
+#endif
+
+// Detect char8_t
+#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
+#define SCN_HAS_CHAR8 1
+#else
+#define SCN_HAS_CHAR8 0
+#endif
+
+#define SCN_UNUSED(x) static_cast<void>(sizeof(x))
+
+#if SCN_HAS_RELAXED_CONSTEXPR
+#define SCN_ASSERT(cond, msg) \
+ do { \
+ static_cast<void>(SCN_LIKELY(cond)); \
+ assert((cond) && msg); \
+ } while (false)
+#define SCN_EXPECT(cond) SCN_ASSERT(cond, "Precondition violation")
+#define SCN_ENSURE(cond) SCN_ASSERT(cond, "Postcondition violation")
+#else
+#define SCN_ASSERT(cond, msg) SCN_UNUSED(cond)
+#define SCN_EXPECT(cond) SCN_UNUSED(cond)
+#define SCN_ENSURE(cond) SCN_UNUSED(cond)
+#endif
+
+#define SCN_MOVE(x) \
+ static_cast< \
+ typename ::scn::detail::remove_reference \
+ <decltype(x)>::type&&>(x)
+#define SCN_FWD(x) static_cast<decltype(x)&&>(x)
+#define SCN_DECLVAL(T) static_cast<T (*)()>(nullptr)()
+
+#define SCN_BEGIN_NAMESPACE inline namespace v1 {
+#define SCN_END_NAMESPACE }
+
+#if defined(SCN_HEADER_ONLY)
+#define SCN_INCLUDE_SOURCE_DEFINITIONS !SCN_HEADER_ONLY
+#else
+#define SCN_INCLUDE_SOURCE_DEFINITIONS 1
+#endif
+
+#endif // SCN_DETAIL_CONFIG_H
diff --git a/src/third-party/scnlib/include/scn/detail/context.h b/src/third-party/scnlib/include/scn/detail/context.h
new file mode 100644
index 0000000..5dff3b3
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/context.h
@@ -0,0 +1,126 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_CONTEXT_H
+#define SCN_DETAIL_CONTEXT_H
+
+#include "args.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ template <typename WrappedRange>
+ class basic_context {
+ public:
+ using range_type = WrappedRange;
+ using iterator = typename range_type::iterator;
+ using sentinel = typename range_type::sentinel;
+ using char_type = typename range_type::char_type;
+ using locale_type = basic_locale_ref<char_type>;
+
+ basic_context(range_type&& r) : m_range(SCN_MOVE(r)) {}
+ basic_context(range_type&& r, locale_type&& loc)
+ : m_range(SCN_MOVE(r)), m_locale(SCN_MOVE(loc))
+ {
+ }
+
+ SCN_NODISCARD iterator& begin()
+ {
+ return m_range.begin();
+ }
+ const sentinel& end() const
+ {
+ return m_range.end();
+ }
+
+ range_type& range() & noexcept
+ {
+ return m_range;
+ }
+ const range_type& range() const& noexcept
+ {
+ return m_range;
+ }
+ range_type range() && noexcept
+ {
+ return m_range;
+ }
+
+ locale_type& locale() noexcept
+ {
+ return m_locale;
+ }
+ const locale_type& locale() const noexcept
+ {
+ return m_locale;
+ }
+
+ private:
+ range_type m_range;
+ locale_type m_locale{};
+ };
+
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ basic_context<WrappedRange> make_context(WrappedRange r)
+ {
+ return {SCN_MOVE(r)};
+ }
+ template <typename WrappedRange, typename LocaleRef>
+ basic_context<WrappedRange> make_context(WrappedRange r, LocaleRef&& loc)
+ {
+ return {SCN_MOVE(r), SCN_FWD(loc)};
+ }
+
+ template <typename CharT>
+ auto get_arg(const basic_args<CharT>& args, std::ptrdiff_t id)
+ -> expected<basic_arg<CharT>>
+ {
+ auto a = args.get(id);
+ if (!a) {
+ return error(error::invalid_format_string,
+ "Argument id out of range");
+ }
+ return a;
+ }
+ template <typename CharT, typename ParseCtx>
+ auto get_arg(const basic_args<CharT>& args,
+ ParseCtx& pctx,
+ std::ptrdiff_t id) -> expected<basic_arg<CharT>>
+ {
+ return pctx.check_arg_id(id) ? get_arg(args, id)
+ : error(error::invalid_format_string,
+ "Argument id out of range");
+ }
+ template <typename CharT, typename ParseCtx>
+ auto get_arg(const basic_args<CharT>&, ParseCtx&, basic_string_view<CharT>)
+ -> expected<basic_arg<CharT>>
+ {
+ return error(error::invalid_format_string, "Argument id out of range");
+ }
+
+ template <typename CharT, typename ParseCtx>
+ auto next_arg(const basic_args<CharT>& args, ParseCtx& pctx)
+ -> expected<basic_arg<CharT>>
+ {
+ return get_arg(args, pctx.next_arg_id());
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_CONTEXT_H
diff --git a/src/third-party/scnlib/include/scn/detail/error.h b/src/third-party/scnlib/include/scn/detail/error.h
new file mode 100644
index 0000000..f79e741
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/error.h
@@ -0,0 +1,136 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_ERROR_H
+#define SCN_DETAIL_ERROR_H
+
+#include "fwd.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * Error class.
+ * Used as a return value for functions without a success value.
+ */
+ class SCN_TRIVIAL_ABI error {
+ public:
+ /// Error code
+ enum code : char {
+ /// No error
+ good = 0,
+ /// EOF
+ end_of_range,
+ /// Format string was invalid
+ invalid_format_string,
+ /// Scanned value was invalid for given type.
+ /// e.g. a period '.' when scanning for an int
+ invalid_scanned_value,
+ /// Stream does not support the performed operation
+ invalid_operation,
+ /// Scanned value was out of range for the desired type.
+ /// (e.g. `>2^32` for an `uint32_t`)
+ value_out_of_range,
+ /// Invalid argument given to operation
+ invalid_argument,
+ /// Source range has invalid (utf-8 or utf-16) encoding
+ invalid_encoding,
+ /// This operation is only possible with exceptions enabled
+ exceptions_required,
+ /// The source range emitted an error.
+ source_error,
+ /// The source range emitted an error that cannot be recovered
+ /// from. The stream is now unusable.
+ unrecoverable_source_error,
+
+ unrecoverable_internal_error,
+
+ max_error
+ };
+
+ struct success_tag_t {
+ };
+ static constexpr success_tag_t success_tag() noexcept
+ {
+ return {};
+ }
+
+ constexpr error() noexcept = default;
+ constexpr error(success_tag_t) noexcept : error() {}
+ constexpr error(enum code c, const char* m) noexcept
+ : m_msg(m), m_code(c)
+ {
+ }
+
+ /// Evaluated to true if there was no error
+ constexpr explicit operator bool() const noexcept
+ {
+ return m_code == good;
+ }
+ constexpr bool operator!() const noexcept
+ {
+ return !(operator bool());
+ }
+
+ constexpr operator enum code() const noexcept { return m_code; }
+
+ /// Get error code
+ SCN_NODISCARD constexpr enum code code() const noexcept
+ {
+ return m_code;
+ }
+ SCN_NODISCARD constexpr const char* msg() const noexcept
+ {
+ return m_msg;
+ }
+
+ /// Returns `true` if, after this error, the state of the given input
+ /// range is consistent, and thus, the range can be used for new
+ /// scanning operations.
+ SCN_NODISCARD constexpr bool is_recoverable() const noexcept
+ {
+ return !(m_code == unrecoverable_source_error ||
+ m_code == unrecoverable_internal_error);
+ }
+
+ private:
+ const char* m_msg{nullptr};
+ enum code m_code { good };
+ };
+
+ constexpr inline bool operator==(error a, error b) noexcept
+ {
+ return a.code() == b.code();
+ }
+ constexpr inline bool operator!=(error a, error b) noexcept
+ {
+ return !(a == b);
+ }
+
+ namespace detail {
+ struct error_handler {
+ constexpr error_handler() = default;
+
+ void on_error(error e);
+ void on_error(const char* msg);
+ };
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/detail/file.h b/src/third-party/scnlib/include/scn/detail/file.h
new file mode 100644
index 0000000..03ccff7
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/file.h
@@ -0,0 +1,568 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_FILE_H
+#define SCN_DETAIL_FILE_H
+
+#include <cstdio>
+#include <string>
+
+#include "../util/algorithm.h"
+#include "range.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ struct native_file_handle {
+#if SCN_WINDOWS
+ using handle_type = void*;
+#else
+ using handle_type = int;
+#endif
+
+ static native_file_handle invalid();
+
+ handle_type handle;
+ };
+
+ class byte_mapped_file {
+ public:
+ using iterator = const char*;
+ using sentinel = const char*;
+
+ byte_mapped_file() = default;
+ explicit byte_mapped_file(const char* filename);
+
+ byte_mapped_file(const byte_mapped_file&) = delete;
+ byte_mapped_file& operator=(const byte_mapped_file&) = delete;
+
+ byte_mapped_file(byte_mapped_file&& o) noexcept
+ : m_map(exchange(o.m_map, span<char>{})),
+ m_file(exchange(o.m_file, native_file_handle::invalid()))
+ {
+#if SCN_WINDOWS
+ m_map_handle =
+ exchange(o.m_map_handle, native_file_handle::invalid());
+#endif
+ SCN_ENSURE(!o.valid());
+ SCN_ENSURE(valid());
+ }
+ byte_mapped_file& operator=(byte_mapped_file&& o) noexcept
+ {
+ if (valid()) {
+ _destruct();
+ }
+
+ m_map = exchange(o.m_map, span<char>{});
+ m_file = exchange(o.m_file, native_file_handle::invalid());
+#if SCN_WINDOWS
+ m_map_handle =
+ exchange(o.m_map_handle, native_file_handle::invalid());
+#endif
+
+ SCN_ENSURE(!o.valid());
+ SCN_ENSURE(valid());
+ return *this;
+ }
+
+ ~byte_mapped_file()
+ {
+ if (valid()) {
+ _destruct();
+ }
+ }
+
+ SCN_NODISCARD bool valid() const
+ {
+ return m_file.handle != native_file_handle::invalid().handle;
+ }
+
+ SCN_NODISCARD iterator begin() const
+ {
+ return m_map.begin();
+ }
+ SCN_NODISCARD sentinel end() const
+ {
+ return m_map.end();
+ }
+
+ protected:
+ void _destruct();
+
+ span<char> m_map{};
+ native_file_handle m_file{native_file_handle::invalid().handle};
+#if SCN_WINDOWS
+ native_file_handle m_map_handle{
+ native_file_handle::invalid().handle};
+#endif
+ };
+ } // namespace detail
+
+ /**
+ * Memory-mapped file range.
+ * Manages the lifetime of the mapping itself.
+ */
+ template <typename CharT>
+ class basic_mapped_file : public detail::byte_mapped_file {
+ public:
+ using iterator = const CharT*;
+ using sentinel = const CharT*;
+
+ /// Constructs an empty mapping
+ basic_mapped_file() = default;
+
+ /// Constructs a mapping to a filename
+ explicit basic_mapped_file(const char* f) : detail::byte_mapped_file{f}
+ {
+ }
+
+ SCN_NODISCARD iterator begin() const noexcept
+ {
+ // embrace the UB
+ return reinterpret_cast<iterator>(byte_mapped_file::begin());
+ }
+ SCN_NODISCARD sentinel end() const noexcept
+ {
+ return reinterpret_cast<sentinel>(byte_mapped_file::end());
+ }
+
+ SCN_NODISCARD iterator data() const noexcept
+ {
+ return begin();
+ }
+ SCN_NODISCARD size_t size() const noexcept
+ {
+ return m_map.size() / sizeof(CharT);
+ }
+
+ /// Mapping data
+ span<const CharT> buffer() const
+ {
+ return {data(), size()};
+ }
+
+ detail::range_wrapper<basic_string_view<CharT>> wrap() const noexcept
+ {
+ return basic_string_view<CharT>{data(), size()};
+ }
+ };
+
+ using mapped_file = basic_mapped_file<char>;
+ using mapped_wfile = basic_mapped_file<wchar_t>;
+
+ namespace detail {
+ template <typename CharT>
+ struct basic_file_access;
+ template <typename CharT>
+ struct basic_file_iterator_access;
+ } // namespace detail
+
+ /**
+ * Range mapping to a C FILE*.
+ * Not copyable or reconstructible.
+ */
+ template <typename CharT>
+ class basic_file {
+ friend struct detail::basic_file_access<CharT>;
+ friend struct detail::basic_file_iterator_access<CharT>;
+
+ public:
+ class iterator {
+ friend struct detail::basic_file_iterator_access<CharT>;
+
+ public:
+ using char_type = CharT;
+ using value_type = expected<CharT>;
+ using reference = value_type;
+ using pointer = value_type*;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+ using file_type = basic_file<CharT>;
+
+ iterator() = default;
+
+ expected<CharT> operator*() const;
+
+ iterator& operator++()
+ {
+ SCN_EXPECT(m_file);
+ ++m_current;
+ return *this;
+ }
+ iterator operator++(int)
+ {
+ iterator tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ iterator& operator--()
+ {
+ SCN_EXPECT(m_file);
+ SCN_EXPECT(m_current > 0);
+
+ m_last_error = error{};
+ --m_current;
+
+ return *this;
+ }
+ iterator operator--(int)
+ {
+ iterator tmp(*this);
+ operator--();
+ return tmp;
+ }
+
+ bool operator==(const iterator& o) const;
+
+ bool operator!=(const iterator& o) const
+ {
+ return !operator==(o);
+ }
+
+ bool operator<(const iterator& o) const
+ {
+ // any valid iterator is before eof and null
+ if (!m_file) {
+ return !o.m_file;
+ }
+ if (!o.m_file) {
+ return !m_file;
+ }
+ SCN_EXPECT(m_file == o.m_file);
+ return m_current < o.m_current;
+ }
+ bool operator>(const iterator& o) const
+ {
+ return o.operator<(*this);
+ }
+ bool operator<=(const iterator& o) const
+ {
+ return !operator>(o);
+ }
+ bool operator>=(const iterator& o) const
+ {
+ return !operator<(o);
+ }
+
+ void reset_begin_iterator() const noexcept
+ {
+ m_current = 0;
+ }
+
+ private:
+ friend class basic_file;
+
+ iterator(const file_type& f, size_t i)
+ : m_file{std::addressof(f)}, m_current{i}
+ {
+ }
+
+ mutable error m_last_error{};
+ const file_type* m_file{nullptr};
+ mutable size_t m_current{0};
+ };
+
+ using sentinel = iterator;
+ using char_type = CharT;
+
+ /**
+ * Construct an empty file.
+ * Reading not possible: valid() is `false`
+ */
+ basic_file() = default;
+ /**
+ * Construct from a FILE*.
+ * Must be a valid handle that can be read from.
+ */
+ basic_file(FILE* f) : m_file{f} {}
+
+ basic_file(const basic_file&) = delete;
+ basic_file& operator=(const basic_file&) = delete;
+
+ basic_file(basic_file&& o) noexcept
+ : m_buffer(detail::exchange(o.m_buffer, {})),
+ m_file(detail::exchange(o.m_file, nullptr))
+ {
+ }
+ basic_file& operator=(basic_file&& o) noexcept
+ {
+ if (valid()) {
+ sync();
+ }
+ m_buffer = detail::exchange(o.m_buffer, {});
+ m_file = detail::exchange(o.m_file, nullptr);
+ return *this;
+ }
+
+ ~basic_file()
+ {
+ if (valid()) {
+ _sync_all();
+ }
+ }
+
+ /**
+ * Get the FILE* for this range.
+ * Only use this handle for reading sync() has been called and no
+ * reading operations have taken place after that.
+ *
+ * \see sync
+ */
+ FILE* handle() const
+ {
+ return m_file;
+ }
+
+ /**
+ * Reset the file handle.
+ * Calls sync(), if necessary, before resetting.
+ * @return The old handle
+ */
+ FILE* set_handle(FILE* f, bool allow_sync = true) noexcept
+ {
+ auto old = m_file;
+ if (old && allow_sync) {
+ sync();
+ }
+ m_file = f;
+ return old;
+ }
+
+ /// Whether the file has been opened
+ constexpr bool valid() const noexcept
+ {
+ return m_file != nullptr;
+ }
+
+ /**
+ * Synchronizes this file with the underlying FILE*.
+ * Invalidates all non-end iterators.
+ * File must be open.
+ *
+ * Necessary for mixing-and-matching scnlib and <cstdio>:
+ * \code{.cpp}
+ * scn::scan(file, ...);
+ * file.sync();
+ * std::fscanf(file.handle(), ...);
+ * \endcode
+ *
+ * Necessary for synchronizing result objects:
+ * \code{.cpp}
+ * auto result = scn::scan(file, ...);
+ * // only result.range() can now be used for scanning
+ * result = scn::scan(result.range(), ...);
+ * // .sync() allows the original file to also be used
+ * file.sync();
+ * result = scn::scan(file, ...);
+ * \endcode
+ */
+ void sync() noexcept
+ {
+ _sync_all();
+ m_buffer.clear();
+ }
+
+ iterator begin() const noexcept
+ {
+ return {*this, 0};
+ }
+ sentinel end() const noexcept
+ {
+ return {};
+ }
+
+ span<const CharT> get_buffer(iterator it,
+ size_t max_size) const noexcept
+ {
+ if (!it.m_file) {
+ return {};
+ }
+ const auto begin =
+ m_buffer.begin() + static_cast<std::ptrdiff_t>(it.m_current);
+ const auto end_diff = detail::min(
+ max_size,
+ static_cast<size_t>(ranges::distance(begin, m_buffer.end())));
+ return {begin, begin + static_cast<std::ptrdiff_t>(end_diff)};
+ }
+
+ private:
+ friend class iterator;
+
+ expected<CharT> _read_single() const;
+
+ void _sync_all() noexcept
+ {
+ _sync_until(m_buffer.size());
+ }
+ void _sync_until(size_t pos) noexcept;
+
+ CharT _get_char_at(size_t i) const
+ {
+ SCN_EXPECT(valid());
+ SCN_EXPECT(i < m_buffer.size());
+ return m_buffer[i];
+ }
+
+ bool _is_at_end(size_t i) const
+ {
+ SCN_EXPECT(valid());
+ return i >= m_buffer.size();
+ }
+
+ mutable std::basic_string<CharT> m_buffer{};
+ FILE* m_file{nullptr};
+ };
+
+ using file = basic_file<char>;
+ using wfile = basic_file<wchar_t>;
+
+ template <>
+ expected<char> file::iterator::operator*() const;
+ template <>
+ expected<wchar_t> wfile::iterator::operator*() const;
+ template <>
+ bool file::iterator::operator==(const file::iterator&) const;
+ template <>
+ bool wfile::iterator::operator==(const wfile::iterator&) const;
+
+ template <>
+ expected<char> file::_read_single() const;
+ template <>
+ expected<wchar_t> wfile::_read_single() const;
+ template <>
+ void file::_sync_until(size_t) noexcept;
+ template <>
+ void wfile::_sync_until(size_t) noexcept;
+
+ /**
+ * A child class for basic_file, handling fopen, fclose, and lifetimes with
+ * RAII.
+ */
+ template <typename CharT>
+ class basic_owning_file : public basic_file<CharT> {
+ public:
+ using char_type = CharT;
+
+ /// Open an empty file
+ basic_owning_file() = default;
+ /// Open a file, with fopen arguments
+ basic_owning_file(const char* f, const char* mode)
+ : basic_file<CharT>(std::fopen(f, mode))
+ {
+ }
+
+ /// Steal ownership of a FILE*
+ explicit basic_owning_file(FILE* f) : basic_file<CharT>(f) {}
+
+ ~basic_owning_file()
+ {
+ if (is_open()) {
+ close();
+ }
+ }
+
+ /// fopen
+ bool open(const char* f, const char* mode)
+ {
+ SCN_EXPECT(!is_open());
+
+ auto h = std::fopen(f, mode);
+ if (!h) {
+ return false;
+ }
+
+ const bool is_wide = sizeof(CharT) > 1;
+ auto ret = std::fwide(h, is_wide ? 1 : -1);
+ if ((is_wide && ret > 0) || (!is_wide && ret < 0) || ret == 0) {
+ this->set_handle(h);
+ return true;
+ }
+ return false;
+ }
+ /// Steal ownership
+ bool open(FILE* f)
+ {
+ SCN_EXPECT(!is_open());
+ if (std::ferror(f) != 0) {
+ return false;
+ }
+ this->set_handle(f);
+ return true;
+ }
+
+ /// Close file
+ void close()
+ {
+ SCN_EXPECT(is_open());
+ this->sync();
+ std::fclose(this->handle());
+ this->set_handle(nullptr, false);
+ }
+
+ /// Is the file open
+ SCN_NODISCARD bool is_open() const
+ {
+ return this->valid();
+ }
+ };
+
+ using owning_file = basic_owning_file<char>;
+ using owning_wfile = basic_owning_file<wchar_t>;
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wexit-time-destructors")
+
+ // Avoid documentation issues: without this, Doxygen will think
+ // SCN_CLANG_PUSH is a part of the stdin_range declaration
+ namespace dummy {
+ }
+
+ /**
+ * Get a reference to the global stdin range
+ */
+ template <typename CharT>
+ basic_file<CharT>& stdin_range()
+ {
+ static auto f = basic_file<CharT>{stdin};
+ return f;
+ }
+ /**
+ * Get a reference to the global `char`-oriented stdin range
+ */
+ inline file& cstdin()
+ {
+ return stdin_range<char>();
+ }
+ /**
+ * Get a reference to the global `wchar_t`-oriented stdin range
+ */
+ inline wfile& wcstdin()
+ {
+ return stdin_range<wchar_t>();
+ }
+ SCN_CLANG_POP
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_FILE_CPP)
+#include "file.cpp"
+#endif
+
+#endif // SCN_DETAIL_FILE_H
diff --git a/src/third-party/scnlib/include/scn/detail/fwd.h b/src/third-party/scnlib/include/scn/detail/fwd.h
new file mode 100644
index 0000000..3dcebf6
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/fwd.h
@@ -0,0 +1,204 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_FWD_H
+#define SCN_DETAIL_FWD_H
+
+#include "config.h"
+
+#include <cstddef>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ // args.h
+
+ template <typename CharT>
+ class basic_arg;
+ template <typename CharT>
+ class basic_args;
+ template <typename CharT, typename... Args>
+ class arg_store;
+
+ template <typename T>
+ struct temporary;
+
+ // error.h
+
+ class error;
+
+ // locale.h
+
+ template <typename CharT>
+ class basic_locale_ref;
+
+ // context.h
+
+ template <typename WrappedRange>
+ class basic_context;
+
+ // parse_context.h
+
+ template <typename CharT>
+ class basic_parse_context;
+ template <typename CharT>
+ class basic_empty_parse_context;
+
+ namespace detail {
+ template <typename T>
+ struct parse_context_template_for_format;
+ }
+
+ // reader/common.h
+
+ template <typename T, typename Enable = void>
+ struct scanner;
+
+ // defined here to avoid including <scn.h> if the user wants to create a
+ // scanner for their own type
+ /**
+ * Base class for all scanners.
+ * User-defined scanner must derive from this type.
+ */
+ struct parser_base {
+ /**
+ * Returns `true` if `skip_range_whitespace()` is to be called before
+ * scanning this value.
+ *
+ * Defaults to `true`. Is `false` for chars, code points and strings
+ * when using set scanning.
+ */
+ static constexpr bool skip_preceding_whitespace()
+ {
+ return true;
+ }
+ /**
+ * Returns `true` if this scanner supports parsing align and fill
+ * specifiers from the format string, and then scanning them.
+ *
+ * Defaults to `false`, `true` for all scnlib-defined scanners.
+ */
+ static constexpr bool support_align_and_fill()
+ {
+ return false;
+ }
+
+ static SCN_CONSTEXPR14 void make_localized() {}
+ };
+
+ struct empty_parser;
+ struct common_parser;
+ struct common_parser_default;
+
+ namespace detail {
+ template <typename T>
+ struct simple_integer_scanner;
+ }
+
+ // visitor.h
+
+ template <typename Context, typename ParseCtx>
+ class basic_visitor;
+
+ // file.h
+
+ template <typename CharT>
+ class basic_mapped_file;
+ template <typename CharT>
+ class basic_file;
+ template <typename CharT>
+ class basic_owning_file;
+
+ // scan.h
+
+ template <typename T>
+ struct span_list_wrapper;
+ template <typename T>
+ struct discard_type;
+
+ // util/array.h
+
+ namespace detail {
+ template <typename T, std::size_t N>
+ struct array;
+ }
+
+ // util/expected.h
+
+ template <typename T, typename Error = ::scn::error, typename Enable = void>
+ class expected;
+
+ // util/memory.h
+
+ namespace detail {
+ template <typename T>
+ struct pointer_traits;
+
+ template <typename T>
+ class erased_storage;
+
+ } // namespace detail
+
+ // util/optional.h
+
+ template <typename T>
+ class optional;
+
+ // util/small_vector.h
+
+ namespace detail {
+ template <typename T, size_t StackN>
+ class small_vector;
+ }
+
+ // util/span.h
+
+ template <typename T>
+ class span;
+
+ // util/string_view.h
+
+ template <typename CharT>
+ class basic_string_view;
+
+ // util/unique_ptr.h
+
+ namespace detail {
+ template <typename T>
+ class unique_ptr;
+ }
+
+ // for SCN_MOVE
+ namespace detail {
+ template <typename T>
+ struct remove_reference {
+ using type = T;
+ };
+ template <typename T>
+ struct remove_reference<T&> {
+ using type = T;
+ };
+ template <typename T>
+ struct remove_reference<T&&> {
+ using type = T;
+ };
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_FWD_H
diff --git a/src/third-party/scnlib/include/scn/detail/locale.h b/src/third-party/scnlib/include/scn/detail/locale.h
new file mode 100644
index 0000000..d4d0f8c
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/locale.h
@@ -0,0 +1,595 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_LOCALE_H
+#define SCN_DETAIL_LOCALE_H
+
+#include "../unicode/unicode.h"
+#include "../util/array.h"
+#include "../util/string_view.h"
+#include "../util/unique_ptr.h"
+
+#include <cwchar>
+#include <string>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ constexpr bool has_zero(uint64_t v)
+ {
+ return (v - UINT64_C(0x0101010101010101)) & ~v &
+ UINT64_C(0x8080808080808080);
+ }
+
+ template <typename CharT>
+ CharT ascii_widen(char ch);
+ template <>
+ constexpr char ascii_widen(char ch)
+ {
+ return ch;
+ }
+ template <>
+ constexpr wchar_t ascii_widen(char ch)
+ {
+ return static_cast<wchar_t>(ch);
+ }
+
+ // Hand write to avoid C locales and thus noticeable performance losses
+ inline bool is_space(char ch) noexcept
+ {
+ static constexpr detail::array<bool, 256> lookup = {
+ {false, false, false, false, false, false, false, false, false,
+ true, true, true, true, true, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, true, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false,
+ false, false, false, false}};
+ return lookup[static_cast<size_t>(static_cast<unsigned char>(ch))];
+ }
+ constexpr inline bool is_space(wchar_t ch) noexcept
+ {
+ return ch == 0x20 || (ch >= 0x09 && ch <= 0x0d);
+ }
+ constexpr inline bool is_space(code_point cp) noexcept
+ {
+ return cp == 0x20 || (cp >= 0x09 && cp <= 0x0d);
+ }
+
+ constexpr inline bool is_digit(char ch) noexcept
+ {
+ return ch >= '0' && ch <= '9';
+ }
+ constexpr inline bool is_digit(wchar_t ch) noexcept
+ {
+ return ch >= L'0' && ch <= L'9';
+ }
+ constexpr inline bool is_digit(code_point cp) noexcept
+ {
+ return cp >= '0' && cp <= '9';
+ }
+
+ template <typename CharT>
+ struct locale_defaults;
+ template <>
+ struct locale_defaults<char> {
+ static constexpr string_view truename()
+ {
+ return {"true"};
+ }
+ static constexpr string_view falsename()
+ {
+ return {"false"};
+ }
+ static constexpr char decimal_point() noexcept
+ {
+ return '.';
+ }
+ static constexpr char thousands_separator() noexcept
+ {
+ return ',';
+ }
+ };
+ template <>
+ struct locale_defaults<wchar_t> {
+ static constexpr wstring_view truename()
+ {
+ return {L"true"};
+ }
+ static constexpr wstring_view falsename()
+ {
+ return {L"false"};
+ }
+ static constexpr wchar_t decimal_point() noexcept
+ {
+ return L'.';
+ }
+ static constexpr wchar_t thousands_separator() noexcept
+ {
+ return L',';
+ }
+ };
+ } // namespace detail
+
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wpadded")
+
+ // scn::scan:
+ // - no L flag -> use hard-coded defaults, akin to "C"
+ // locale_ref.default() -> default_locale_ref
+ // - L flag -> use global C++ locale
+ // locale_ref.localized() -> custom_locale_ref (global C++)
+ // scn::scan_localized:
+ // - no L flag -> use hard-coded defaults, akin to "C"
+ // locale_ref.default() -> default_locale_ref
+ // - L flag -> use given C++ locale
+ // locale_ref.localized() -> custom_locale_ref (given locale)
+
+ namespace detail {
+ // constexpr locale
+ template <typename CharT, typename SV, typename Def>
+ struct basic_static_locale_ref_base {
+ using char_type = CharT;
+ using string_view_type = SV;
+ using defaults = Def;
+
+ static constexpr bool is_static = true;
+
+ constexpr basic_static_locale_ref_base() = default;
+
+ static constexpr bool is_space(char_type ch)
+ {
+ return detail::is_space(ch);
+ }
+ static constexpr bool is_digit(char_type ch)
+ {
+ return detail::is_digit(ch);
+ }
+
+ static SCN_CONSTEXPR14 bool is_space(span<const char_type> ch)
+ {
+ SCN_EXPECT(ch.size() >= 1);
+ return detail::is_space(ch[0]);
+ }
+ static SCN_CONSTEXPR14 bool is_digit(span<const char_type> ch)
+ {
+ SCN_EXPECT(ch.size() >= 1);
+ return detail::is_digit(ch[0]);
+ }
+
+ static constexpr char_type decimal_point()
+ {
+ return defaults::decimal_point();
+ }
+ static constexpr char_type thousands_separator()
+ {
+ return defaults::thousands_separator();
+ }
+
+ static constexpr string_view_type truename()
+ {
+ return defaults::truename();
+ }
+ static constexpr string_view_type falsename()
+ {
+ return defaults::falsename();
+ }
+ };
+ template <typename CharT>
+ struct basic_static_locale_ref
+ : basic_static_locale_ref_base<CharT,
+ basic_string_view<CharT>,
+ locale_defaults<CharT>> {
+ };
+ template <>
+ struct basic_static_locale_ref<code_point>
+ : basic_static_locale_ref_base<code_point,
+ string_view,
+ locale_defaults<char>> {
+ };
+
+ // base class
+ template <typename CharT>
+ class basic_locale_ref_impl_base {
+ public:
+ using char_type = CharT;
+ using string_type = std::basic_string<char_type>;
+ using string_view_type = basic_string_view<char_type>;
+
+ static constexpr bool is_static = false;
+
+ basic_locale_ref_impl_base() = default;
+
+ basic_locale_ref_impl_base(const basic_locale_ref_impl_base&) =
+ default;
+ basic_locale_ref_impl_base(basic_locale_ref_impl_base&&) = default;
+ basic_locale_ref_impl_base& operator=(
+ const basic_locale_ref_impl_base&) = default;
+ basic_locale_ref_impl_base& operator=(
+ basic_locale_ref_impl_base&&) = default;
+
+#define SCN_DEFINE_LOCALE_REF_CTYPE(f) \
+ bool is_##f(char_type ch) const \
+ { \
+ return do_is_##f(ch); \
+ } \
+ bool is_##f(span<const char_type> ch) const \
+ { \
+ return do_is_##f(ch); \
+ }
+
+ SCN_DEFINE_LOCALE_REF_CTYPE(space)
+ SCN_DEFINE_LOCALE_REF_CTYPE(digit)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(alnum)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(alpha)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(blank)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(cntrl)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(graph)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(lower)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(print)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(punct)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(upper)
+ // SCN_DEFINE_LOCALE_REF_CTYPE(xdigit)
+#undef SCN_DEFINE_LOCALE_REF_CTYPE
+
+ char_type decimal_point() const
+ {
+ return do_decimal_point();
+ }
+ char_type thousands_separator() const
+ {
+ return do_thousands_separator();
+ }
+
+ string_view_type truename() const
+ {
+ return do_truename();
+ }
+ string_view_type falsename() const
+ {
+ return do_falsename();
+ }
+
+ protected:
+ ~basic_locale_ref_impl_base() = default;
+
+ private:
+#define SCN_DECLARE_LOCALE_REF_CTYPE_DO(f) \
+ virtual bool do_is_##f(char_type) const = 0; \
+ virtual bool do_is_##f(span<const char_type>) const = 0;
+ SCN_DECLARE_LOCALE_REF_CTYPE_DO(space)
+ SCN_DECLARE_LOCALE_REF_CTYPE_DO(digit)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(alnum)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(alpha)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(blank)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(cntrl)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(graph)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(lower)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(print)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(punct)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(upper)
+ // SCN_DECLARE_LOCALE_REF_CTYPE_DO(xdigit)
+#undef SCN_DECLARE_LOCALE_REF_CTYPE_DO
+
+ virtual char_type do_decimal_point() const = 0;
+ virtual char_type do_thousands_separator() const = 0;
+ virtual string_view_type do_truename() const = 0;
+ virtual string_view_type do_falsename() const = 0;
+ };
+
+ // hardcoded "C", using static_locale_ref
+ template <typename CharT>
+ class basic_default_locale_ref final
+ : public basic_locale_ref_impl_base<CharT> {
+ using base = basic_locale_ref_impl_base<CharT>;
+
+ public:
+ using char_type = typename base::char_type;
+ using string_view_type = typename base::string_view_type;
+
+ basic_default_locale_ref() = default;
+
+ private:
+ using static_type = basic_static_locale_ref<char_type>;
+
+ bool do_is_space(char_type ch) const override
+ {
+ return static_type::is_space(ch);
+ }
+ bool do_is_digit(char_type ch) const override
+ {
+ return static_type::is_digit(ch);
+ }
+
+ bool do_is_space(span<const char_type> ch) const override
+ {
+ return static_type::is_space(ch);
+ }
+ bool do_is_digit(span<const char_type> ch) const override
+ {
+ return static_type::is_digit(ch);
+ }
+
+ char_type do_decimal_point() const override
+ {
+ return static_type::decimal_point();
+ }
+ char_type do_thousands_separator() const override
+ {
+ return static_type::thousands_separator();
+ }
+ string_view_type do_truename() const override
+ {
+ return static_type::truename();
+ }
+ string_view_type do_falsename() const override
+ {
+ return static_type::falsename();
+ }
+ };
+
+ // custom
+ template <typename CharT>
+ class basic_custom_locale_ref final
+ : public basic_locale_ref_impl_base<CharT> {
+ using base = basic_locale_ref_impl_base<CharT>;
+
+ public:
+ using char_type = typename base::char_type;
+ using string_type = typename base::string_type;
+ using string_view_type = typename base::string_view_type;
+
+ basic_custom_locale_ref();
+ basic_custom_locale_ref(const void* locale);
+
+ basic_custom_locale_ref(const basic_custom_locale_ref&) = delete;
+ basic_custom_locale_ref& operator=(const basic_custom_locale_ref&) =
+ delete;
+
+ basic_custom_locale_ref(basic_custom_locale_ref&&);
+ basic_custom_locale_ref& operator=(basic_custom_locale_ref&&);
+
+ ~basic_custom_locale_ref();
+
+ static basic_custom_locale_ref make_classic();
+
+ const void* get_locale() const
+ {
+ return m_locale;
+ }
+
+ void convert_to_global();
+ void convert_to_classic();
+
+ // narrow: locale multibyte -> locale wide
+ // wide: identity
+ error convert_to_wide(const CharT* from_begin,
+ const CharT* from_end,
+ const CharT*& from_next,
+ wchar_t* to_begin,
+ wchar_t* to_end,
+ wchar_t*& to_next) const;
+ expected<wchar_t> convert_to_wide(const CharT* from_begin,
+ const CharT* from_end) const;
+
+#define SCN_DEFINE_CUSTOM_LOCALE_CTYPE(f) \
+ bool is_##f(char_type) const; \
+ bool is_##f(span<const char_type>) const; \
+ bool is_##f(code_point) const;
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(alnum)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(alpha)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(blank)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(cntrl)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(graph)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(lower)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(print)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(punct)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(upper)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(xdigit)
+#undef SCN_DEFINE_CUSTOM_LOCALE_CTYPE
+
+ bool is_space(code_point) const;
+ using base::is_space;
+
+ bool is_digit(code_point) const;
+ using base::is_digit;
+
+ template <typename T>
+ expected<std::ptrdiff_t> read_num(T& val,
+ const string_type& buf,
+ int base) const;
+
+ private:
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ bool do_is_space(char_type ch) const override;
+ bool do_is_digit(char_type ch) const override;
+
+ bool do_is_space(span<const char_type> ch) const override;
+ bool do_is_digit(span<const char_type> ch) const override;
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+
+ char_type do_decimal_point() const override;
+ char_type do_thousands_separator() const override;
+ string_view_type do_truename() const override;
+ string_view_type do_falsename() const override;
+
+ void _initialize();
+
+ const void* m_locale{nullptr};
+ void* m_data{nullptr};
+ };
+ } // namespace detail
+
+ template <typename CharT>
+ class basic_locale_ref {
+ public:
+ using char_type = CharT;
+ using impl_base = detail::basic_locale_ref_impl_base<char_type>;
+ using static_type = detail::basic_static_locale_ref<char_type>;
+ using default_type = detail::basic_default_locale_ref<char_type>;
+ using custom_type = detail::basic_custom_locale_ref<char_type>;
+
+ // default
+ constexpr basic_locale_ref() = default;
+ // nullptr = global
+ constexpr basic_locale_ref(const void* p) : m_payload(p) {}
+
+ basic_locale_ref clone() const
+ {
+ return {m_payload};
+ }
+
+ constexpr bool has_custom() const
+ {
+ return m_payload != nullptr;
+ }
+
+ // hardcoded "C", constexpr, should be preferred whenever possible
+ constexpr static_type get_static() const
+ {
+ return {};
+ }
+
+ // hardcoded "C", not constexpr
+ default_type& get_default()
+ {
+ return m_default;
+ }
+ const default_type& get_default() const
+ {
+ return m_default;
+ }
+
+ // global locale or given locale
+ custom_type& get_localized()
+ {
+ _construct_custom();
+ return *m_custom;
+ }
+ const custom_type& get_localized() const
+ {
+ _construct_custom();
+ return *m_custom;
+ }
+
+ custom_type make_localized_classic() const
+ {
+ return custom_type::make_classic();
+ }
+
+ custom_type* get_localized_unsafe()
+ {
+ return m_custom.get();
+ }
+ const custom_type* get_localized_unsafe() const
+ {
+ return m_custom.get();
+ }
+
+ // virtual interface
+ impl_base& get(bool localized)
+ {
+ if (localized) {
+ return get_localized();
+ }
+ return get_default();
+ }
+ const impl_base& get(bool localized) const
+ {
+ if (localized) {
+ return get_localized();
+ }
+ return get_default();
+ }
+
+ void prepare_localized() const
+ {
+ _construct_custom();
+ }
+ void reset_locale(const void* payload)
+ {
+ m_custom.reset();
+ m_payload = payload;
+ _construct_custom();
+ }
+
+ private:
+ void _construct_custom() const
+ {
+ if (m_custom) {
+ // already constructed
+ return;
+ }
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ m_custom = detail::make_unique<custom_type>(m_payload);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+
+ mutable detail::unique_ptr<custom_type> m_custom{nullptr};
+ const void* m_payload{nullptr};
+ default_type m_default{};
+ };
+
+ template <typename CharT, typename Locale>
+ basic_locale_ref<CharT> make_locale_ref(const Locale& loc)
+ {
+ return {std::addressof(loc)};
+ }
+ template <typename CharT>
+ basic_locale_ref<CharT> make_default_locale_ref()
+ {
+ return {};
+ }
+
+ using locale_ref = basic_locale_ref<char>;
+ using wlocale_ref = basic_locale_ref<wchar_t>;
+
+ SCN_CLANG_POP // -Wpadded
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_LOCALE_CPP)
+#include "locale.cpp"
+#endif
+
+#endif // SCN_DETAIL_LOCALE_H
diff --git a/src/third-party/scnlib/include/scn/detail/parse_context.h b/src/third-party/scnlib/include/scn/detail/parse_context.h
new file mode 100644
index 0000000..91d6687
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/parse_context.h
@@ -0,0 +1,581 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_PARSE_CONTEXT_H
+#define SCN_DETAIL_PARSE_CONTEXT_H
+
+#include "../util/expected.h"
+#include "locale.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ class parse_context_base {
+ public:
+ SCN_CONSTEXPR14 std::ptrdiff_t next_arg_id()
+ {
+ return m_next_arg_id >= 0 ? m_next_arg_id++ : 0;
+ }
+ SCN_CONSTEXPR14 bool check_arg_id(std::ptrdiff_t)
+ {
+ if (m_next_arg_id > 0) {
+ return false;
+ }
+ m_next_arg_id = -1;
+ return true;
+ }
+
+ protected:
+ parse_context_base() = default;
+
+ std::ptrdiff_t m_next_arg_id{0};
+ };
+ } // namespace detail
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wpadded")
+
+ template <typename CharT>
+ class basic_parse_context : public detail::parse_context_base {
+ public:
+ using char_type = CharT;
+ using locale_type = basic_locale_ref<CharT>;
+ using string_view_type = basic_string_view<char_type>;
+ using iterator = typename string_view_type::iterator;
+
+ constexpr basic_parse_context(basic_string_view<char_type> f,
+ locale_type& loc)
+ : m_str(f), m_locale(loc)
+ {
+ }
+
+ /**
+ * Returns `true`, if `next_char()` is a whitespace character according
+ * to the static locale. This means, that `skip_range_whitespace()`
+ * should be called on the source range.
+ */
+ bool should_skip_ws()
+ {
+ bool skip = false;
+ while (*this && m_locale.get_static().is_space(next_char())) {
+ skip = true;
+ advance_char();
+ }
+ return skip;
+ }
+ /**
+ * Returns `true`, if a character equal to `next_char()` should be read
+ * from the source range.
+ *
+ * If `*this` currently points to an escaped
+ * brace character `"{{"` or `"}}"`, skips the first brace, so that
+ * after this function is called, `next_char()` returns the character
+ * that should be read.
+ */
+ bool should_read_literal()
+ {
+ const auto brace = detail::ascii_widen<char_type>('{');
+ if (next_char() != brace) {
+ if (next_char() == detail::ascii_widen<char_type>('}')) {
+ advance_char();
+ }
+ return true;
+ }
+ if (SCN_UNLIKELY(m_str.size() > 1 &&
+ *(m_str.begin() + 1) == brace)) {
+ advance_char();
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Returns `true` if `ch` is equal to `next_char()`
+ */
+ SCN_NODISCARD constexpr bool check_literal(char_type ch) const
+ {
+ return ch == next_char();
+ }
+ /**
+ * Returns `true` if the code units contained in `ch` are equal to the
+ * code units starting from `pctx.begin()`. If `chars_left() <
+ * ch.size()`, returns `false`.
+ */
+ SCN_NODISCARD SCN_CONSTEXPR14 bool check_literal(
+ span<const char_type> ch) const
+ {
+ if (chars_left() < ch.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < ch.size(); ++i) {
+ if (ch[i] != m_str[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ /**
+ * Returns `true` if `cp` is equal to the value returned by `next_cp()`.
+ * If `next_cp()` errored, returns that error
+ * (`error::invalid_encoding`).
+ */
+ SCN_NODISCARD SCN_CONSTEXPR14 expected<bool> check_literal_cp(
+ code_point cp) const
+ {
+ auto next = next_cp();
+ if (!next) {
+ return next.error();
+ }
+ return cp == next.value();
+ }
+
+ /**
+ * Returns `true` if there are characters left in `*this`.
+ */
+ constexpr bool good() const
+ {
+ return !m_str.empty();
+ }
+ constexpr explicit operator bool() const
+ {
+ return good();
+ }
+
+ /**
+ * Returns the next character (= code unit) in `*this`.
+ * `good()` must be `true`.
+ */
+ constexpr char_type next_char() const
+ {
+ return m_str.front();
+ }
+ /**
+ * Returns the next code point in `*this`.
+ * If the code point is encoded incorrectly, returns
+ * `error::invalid_encoding`.
+ */
+ SCN_NODISCARD SCN_CONSTEXPR14 expected<code_point> next_cp() const
+ {
+ code_point cp{};
+ auto it = parse_code_point(m_str.begin(), m_str.end(), cp);
+ if (!it) {
+ return it.error();
+ }
+ return {cp};
+ }
+
+ /**
+ * Returns the number of chars (= code units) left in `*this`.
+ */
+ constexpr std::size_t chars_left() const noexcept
+ {
+ return m_str.size();
+ }
+ /**
+ * Returns the number of code points left in `*this`. If `*this`
+ * contains invalid encoding, returns `error::invalid_encoding`.
+ */
+ SCN_NODISCARD SCN_CONSTEXPR14 expected<std::size_t> cp_left()
+ const noexcept
+ {
+ auto d = code_point_distance(m_str.begin(), m_str.end());
+ if (!d) {
+ return d.error();
+ }
+ return {static_cast<std::size_t>(d.value())};
+ }
+
+ /**
+ * Advances `*this` by `n` characters (= code units). `*this` must have
+ * at least `n` characters left.
+ */
+ SCN_CONSTEXPR14 void advance_char(std::ptrdiff_t n = 1) noexcept
+ {
+ SCN_EXPECT(chars_left() >= static_cast<std::size_t>(n));
+ m_str.remove_prefix(static_cast<std::size_t>(n));
+ }
+ /**
+ * Advances `*this` by a single code point. If the code point is encoded
+ * incorrectly, returns `error::invalid_encoding`.
+ */
+ SCN_NODISCARD SCN_CONSTEXPR14 error advance_cp() noexcept
+ {
+ code_point cp{};
+ auto it = parse_code_point(m_str.begin(), m_str.end(), cp);
+ if (!it) {
+ return it.error();
+ }
+ m_str.remove_prefix(
+ static_cast<size_t>(it.value() - m_str.begin()));
+ return {};
+ }
+
+ /**
+ * Returns `true`, if `*this` has over `n` characters (= code units)
+ * left, so that `peek_char()` with the same `n` parameter can be
+ * called.
+ */
+ constexpr bool can_peek_char(std::size_t n = 1) const noexcept
+ {
+ return chars_left() > n;
+ }
+
+ /**
+ * Returns the character (= code unit) `n` characters past the current
+ * character, so that `peek_char(0)` is equivalent to `next_char()`.
+ * `n <= chars_left()` must be `true`.
+ */
+ SCN_CONSTEXPR14 char_type peek_char(std::size_t n = 1) const noexcept
+ {
+ SCN_EXPECT(n <= chars_left());
+ return m_str[n];
+ }
+ /**
+ * Returns the code point past the current code point (`next_cp()`).
+ *
+ * If there is no code point to peek (the current code point is the last
+ * one in `*this`), returns `error::end_of_range`.
+ * If `*this` contains invalid encoding, returns
+ * `error::invalid_encoding`.
+ */
+ SCN_NODISCARD SCN_CONSTEXPR14 expected<code_point> peek_cp()
+ const noexcept
+ {
+ if (m_str.size() < 2) {
+ return error{error::end_of_range,
+ "End of format string, cannot peek"};
+ }
+
+ code_point cp{};
+ auto it = parse_code_point(m_str.begin(), m_str.end(), cp);
+ if (!it) {
+ return it.error();
+ }
+ if (it.value() == m_str.end()) {
+ return error{error::end_of_range,
+ "End of format string, cannot peek"};
+ }
+
+ it = parse_code_point(it.value(), m_str.end(), cp);
+ if (!it) {
+ return it.error();
+ }
+ return {cp};
+ }
+
+ SCN_CONSTEXPR14 iterator begin() const noexcept
+ {
+ return m_str.begin();
+ }
+ SCN_CONSTEXPR14 iterator end() const noexcept
+ {
+ return m_str.end();
+ }
+
+ /**
+ * Returns `true`, if `next_char() == '{'`
+ */
+ SCN_CONSTEXPR14 bool check_arg_begin() const
+ {
+ SCN_EXPECT(good());
+ return next_char() == detail::ascii_widen<char_type>('{');
+ }
+ /**
+ * Returns `true`, if `next_char() == '}'`
+ */
+ SCN_CONSTEXPR14 bool check_arg_end() const
+ {
+ SCN_EXPECT(good());
+ return next_char() == detail::ascii_widen<char_type>('}');
+ }
+
+ using parse_context_base::check_arg_id;
+ SCN_CONSTEXPR14 void check_arg_id(basic_string_view<CharT>) {}
+
+ SCN_CONSTEXPR14 void arg_begin() const noexcept {}
+ SCN_CONSTEXPR14 void arg_end() const noexcept {}
+
+ SCN_CONSTEXPR14 void arg_handled() const noexcept {}
+
+ const locale_type& locale() const
+ {
+ return m_locale;
+ }
+
+ /**
+ * Parse `*this` using `s`
+ */
+ template <typename Scanner>
+ error parse(Scanner& s)
+ {
+ return s.parse(*this);
+ }
+
+ bool has_arg_id()
+ {
+ SCN_EXPECT(good());
+ if (m_str.size() == 1) {
+ return true;
+ }
+ if (m_str[1] == detail::ascii_widen<char_type>('}')) {
+ advance_char();
+ return false;
+ }
+ if (m_str[1] == detail::ascii_widen<char_type>(':')) {
+ advance_char(2);
+ return false;
+ }
+ return true;
+ }
+ expected<string_view_type> parse_arg_id()
+ {
+ SCN_EXPECT(good());
+ advance_char();
+ if (SCN_UNLIKELY(!good())) {
+ return error(error::invalid_format_string,
+ "Unexpected end of format argument");
+ }
+ auto it = m_str.begin();
+ for (std::ptrdiff_t i = 0; good(); ++i, (void)advance_char()) {
+ if (check_arg_end()) {
+ return string_view_type{
+ it,
+ static_cast<typename string_view_type::size_type>(i)};
+ }
+ if (next_char() == detail::ascii_widen<char_type>(':')) {
+ advance_char();
+ return string_view_type{
+ it,
+ static_cast<typename string_view_type::size_type>(i)};
+ }
+ }
+ return error(error::invalid_format_string,
+ "Unexpected end of format argument");
+ }
+
+ private:
+ string_view_type m_str;
+ locale_type& m_locale;
+ };
+
+ template <typename CharT>
+ class basic_empty_parse_context : public detail::parse_context_base {
+ public:
+ using char_type = CharT;
+ using locale_type = basic_locale_ref<char_type>;
+ using string_view_type = basic_string_view<char_type>;
+
+ constexpr basic_empty_parse_context(int args,
+ locale_type& loc,
+ bool localized = false)
+ : m_locale(loc), m_args_left(args), m_localized(localized)
+ {
+ }
+
+ SCN_CONSTEXPR14 bool should_skip_ws()
+ {
+ if (m_should_skip_ws) {
+ m_should_skip_ws = false;
+ return true;
+ }
+ return false;
+ }
+ constexpr bool should_read_literal() const
+ {
+ return false;
+ }
+ constexpr bool check_literal(char_type) const
+ {
+ return false;
+ }
+ constexpr bool check_literal(span<const char_type>) const
+ {
+ return false;
+ }
+ constexpr bool check_literal_cp(code_point) const
+ {
+ return false;
+ }
+
+ constexpr bool good() const
+ {
+ return m_args_left > 0;
+ }
+ constexpr explicit operator bool() const
+ {
+ return good();
+ }
+
+ SCN_CONSTEXPR14 void advance_char(std::ptrdiff_t = 1) const noexcept {}
+ SCN_CONSTEXPR14 error advance_cp() const noexcept
+ {
+ return {};
+ }
+
+ char_type next_char() const
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+ expected<std::pair<code_point, std::ptrdiff_t>> next_cp() const
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+
+ std::size_t chars_left() const noexcept
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+ std::size_t cp_left() const noexcept
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+
+ constexpr bool can_peek_char() const noexcept
+ {
+ return false;
+ }
+ constexpr bool can_peek_cp() const noexcept
+ {
+ return false;
+ }
+
+ char_type peek_char(std::ptrdiff_t = 1) const noexcept
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+ expected<code_point> peek_cp() const noexcept
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+
+ constexpr bool check_arg_begin() const
+ {
+ return true;
+ }
+ constexpr bool check_arg_end() const
+ {
+ return true;
+ }
+
+ using parse_context_base::check_arg_id;
+ SCN_CONSTEXPR14 void check_arg_id(basic_string_view<CharT>) {}
+
+ SCN_CONSTEXPR14 void arg_begin() const noexcept {}
+ SCN_CONSTEXPR14 void arg_end() const noexcept {}
+
+ SCN_CONSTEXPR14 void arg_handled()
+ {
+ m_should_skip_ws = true;
+ --m_args_left;
+ }
+
+ const locale_type& locale() const
+ {
+ return m_locale;
+ }
+
+ template <typename Scanner>
+ SCN_CONSTEXPR14 error parse(Scanner& s) const
+ {
+ if (m_localized) {
+ s.make_localized();
+ }
+ return {};
+ }
+
+ constexpr bool has_arg_id() const
+ {
+ return false;
+ }
+ SCN_CONSTEXPR14 expected<string_view_type> parse_arg_id() const
+ {
+ SCN_EXPECT(good());
+ return string_view_type{};
+ }
+
+ void reset_args_left(int n)
+ {
+ m_args_left = n;
+ parse_context_base::m_next_arg_id = 0;
+ m_should_skip_ws = false;
+ }
+
+ private:
+ locale_type& m_locale;
+ int m_args_left;
+ bool m_localized;
+ bool m_should_skip_ws{false};
+ };
+
+ namespace detail {
+ template <typename CharT>
+ basic_parse_context<CharT> make_parse_context_impl(
+ basic_string_view<CharT> f,
+ basic_locale_ref<CharT>& loc,
+ bool)
+ {
+ return {f, loc};
+ }
+ template <typename CharT>
+ basic_empty_parse_context<CharT> make_parse_context_impl(
+ int i,
+ basic_locale_ref<CharT>& loc,
+ bool localized)
+ {
+ return {i, loc, localized};
+ }
+
+ template <typename CharT>
+ struct parse_context_template_for_format<basic_string_view<CharT>> {
+ template <typename T>
+ using type = basic_parse_context<T>;
+ };
+ template <>
+ struct parse_context_template_for_format<int> {
+ template <typename CharT>
+ using type = basic_empty_parse_context<CharT>;
+ };
+
+ template <typename F, typename CharT>
+ auto make_parse_context(F f,
+ basic_locale_ref<CharT>& locale,
+ bool localized)
+ -> decltype(make_parse_context_impl(f, locale, localized))
+ {
+ return make_parse_context_impl(f, locale, localized);
+ }
+ } // namespace detail
+
+ template <typename F, typename CharT>
+ auto make_parse_context(F f, basic_locale_ref<CharT>& locale)
+ -> decltype(detail::make_parse_context_impl(f, locale, false))
+ {
+ return detail::make_parse_context_impl(f, locale, false);
+ }
+
+ SCN_CLANG_POP // -Wpadded
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_PARSE_CONTEXT_H
diff --git a/src/third-party/scnlib/include/scn/detail/range.h b/src/third-party/scnlib/include/scn/detail/range.h
new file mode 100644
index 0000000..5b8802f
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/range.h
@@ -0,0 +1,598 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_RANGE_H
+#define SCN_DETAIL_RANGE_H
+
+#include "../ranges/ranges.h"
+#include "../util/algorithm.h"
+#include "../util/memory.h"
+#include "error.h"
+#include "vectored.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ namespace _reset_begin_iterator {
+ struct fn {
+ private:
+ template <typename Iterator>
+ static auto impl(Iterator& it, priority_tag<1>) noexcept(
+ noexcept(it.reset_begin_iterator()))
+ -> decltype(it.reset_begin_iterator())
+ {
+ return it.reset_begin_iterator();
+ }
+
+ template <typename Iterator>
+ static void impl(Iterator&, size_t, priority_tag<0>) noexcept
+ {
+ }
+
+ public:
+ template <typename Iterator>
+ auto operator()(Iterator& it) const
+ noexcept(noexcept(fn::impl(it, priority_tag<1>{})))
+ -> decltype(fn::impl(it, priority_tag<1>{}))
+ {
+ return fn::impl(it, priority_tag<1>{});
+ }
+ };
+ } // namespace _reset_begin_iterator
+ namespace {
+ static constexpr auto& reset_begin_iterator =
+ static_const<detail::_reset_begin_iterator::fn>::value;
+ }
+
+ template <typename Iterator, typename = void>
+ struct extract_char_type;
+ template <typename Iterator>
+ struct extract_char_type<
+ Iterator,
+ typename std::enable_if<std::is_integral<
+ polyfill_2a::iter_value_t<Iterator>>::value>::type> {
+ using type = polyfill_2a::iter_value_t<Iterator>;
+ };
+ template <typename Iterator>
+ struct extract_char_type<
+ Iterator,
+ void_t<
+ typename std::enable_if<!std::is_integral<
+ polyfill_2a::iter_value_t<Iterator>>::value>::type,
+ typename polyfill_2a::iter_value_t<Iterator>::success_type>> {
+ using type =
+ typename polyfill_2a::iter_value_t<Iterator>::success_type;
+ };
+
+ template <typename Range, typename = void>
+ struct is_direct_impl
+ : std::is_integral<ranges::range_value_t<const Range>> {
+ };
+
+ template <typename Range>
+ struct reconstruct_tag {
+ };
+
+ template <
+ typename Range,
+ typename Iterator,
+ typename Sentinel,
+ typename = typename std::enable_if<
+ std::is_constructible<Range, Iterator, Sentinel>::value>::type>
+ Range reconstruct(reconstruct_tag<Range>, Iterator begin, Sentinel end)
+ {
+ return {begin, end};
+ }
+#if SCN_HAS_STRING_VIEW
+ // std::string_view is not reconstructible pre-C++20
+ template <typename CharT,
+ typename Traits,
+ typename Iterator,
+ typename Sentinel>
+ std::basic_string_view<CharT, Traits> reconstruct(
+ reconstruct_tag<std::basic_string_view<CharT, Traits>>,
+ Iterator begin,
+ Sentinel end)
+ {
+ // On MSVC, string_view can't even be constructed from its
+ // iterators!
+ return {::scn::detail::to_address(begin),
+ static_cast<size_t>(ranges::distance(begin, end))};
+ }
+#endif // SCN_HAS_STRING_VIEW
+
+ template <typename T, bool>
+ struct range_wrapper_storage;
+ template <typename T>
+ struct range_wrapper_storage<T, true> {
+ using type = remove_cvref_t<T>;
+ using range_type = const type&;
+
+ const type* value{nullptr};
+
+ range_wrapper_storage() = default;
+ range_wrapper_storage(const type& v, dummy_type)
+ : value(std::addressof(v))
+ {
+ }
+
+ const type& get() const& noexcept
+ {
+ return *value;
+ }
+ type&& get() && noexcept
+ {
+ return *value;
+ }
+ };
+ template <typename T>
+ struct range_wrapper_storage<T, false> {
+ using range_type = T;
+
+ T value{};
+
+ range_wrapper_storage() = default;
+ template <typename U>
+ range_wrapper_storage(U&& v, dummy_type) : value(SCN_FWD(v))
+ {
+ }
+
+ const T& get() const& noexcept
+ {
+ return value;
+ }
+ T&& get() && noexcept
+ {
+ return value;
+ }
+ };
+
+ template <typename T>
+ using _range_wrapper_marker = typename T::range_wrapper_marker;
+
+ template <typename T>
+ struct _has_range_wrapper_marker
+ : custom_ranges::detail::exists<_range_wrapper_marker, T> {
+ };
+
+ /**
+ * Wraps a source range for more consistent behavior
+ */
+ template <typename Range>
+ class range_wrapper {
+ public:
+ using range_type = Range;
+ using range_nocvref_type = remove_cvref_t<Range>;
+ using iterator = ranges::iterator_t<const range_nocvref_type>;
+ using sentinel = ranges::sentinel_t<const range_nocvref_type>;
+ using char_type = typename extract_char_type<iterator>::type;
+ using difference_type =
+ ranges::range_difference_t<const range_nocvref_type>;
+ using storage_type =
+ range_wrapper_storage<Range, std::is_reference<Range>::value>;
+ using storage_range_type = typename storage_type::range_type;
+
+ using range_wrapper_marker = void;
+
+ template <
+ typename R,
+ typename = typename std::enable_if<
+ !_has_range_wrapper_marker<remove_cvref_t<R>>::value>::type>
+ range_wrapper(R&& r)
+ : m_range(SCN_FWD(r), dummy_type{}),
+ m_begin(ranges::cbegin(m_range.get()))
+ {
+ }
+
+ range_wrapper(const range_wrapper& o) : m_range(o.m_range)
+ {
+ const auto n =
+ ranges::distance(o.begin_underlying(), o.m_begin);
+ m_begin = ranges::cbegin(m_range.get());
+ ranges::advance(m_begin, n);
+ m_read = o.m_read;
+ }
+ range_wrapper& operator=(const range_wrapper& o)
+ {
+ const auto n =
+ ranges::distance(o.begin_underlying(), o.m_begin);
+ m_range = o.m_range;
+ m_begin = ranges::cbegin(m_range.get());
+ ranges::advance(m_begin, n);
+ m_read = o.m_read;
+ return *this;
+ }
+
+ range_wrapper(range_wrapper&& o) noexcept
+ {
+ const auto n =
+ ranges::distance(o.begin_underlying(), o.m_begin);
+ m_range = SCN_MOVE(o.m_range);
+ m_begin = ranges::cbegin(m_range.get());
+ ranges::advance(m_begin, n);
+ m_read = exchange(o.m_read, 0);
+ }
+ range_wrapper& operator=(range_wrapper&& o) noexcept
+ {
+ reset_to_rollback_point();
+
+ const auto n =
+ ranges::distance(o.begin_underlying(), o.m_begin);
+ m_range = SCN_MOVE(o.m_range);
+ m_begin = ranges::cbegin(m_range.get());
+ ranges::advance(m_begin, n);
+ m_read = exchange(o.m_read, 0);
+ return *this;
+ }
+
+ ~range_wrapper() = default;
+
+ iterator begin() const noexcept
+ {
+ return m_begin;
+ }
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wnoexcept")
+ sentinel end() const noexcept(
+ noexcept(ranges::end(SCN_DECLVAL(const storage_type&).get())))
+ {
+ return ranges::end(m_range.get());
+ }
+ SCN_GCC_POP
+
+ struct dummy {
+ };
+
+ /**
+ * Returns `true` if `begin() == end()`.
+ */
+ bool empty() const
+ {
+ return begin() == end();
+ }
+
+ /**
+ * Advance the begin iterator by `n` characters.
+ */
+ iterator advance(difference_type n = 1) noexcept
+ {
+ SCN_EXPECT(_advance_check(
+ n, std::integral_constant<bool, is_contiguous>{}));
+ m_read += n;
+ ranges::advance(m_begin, n);
+ return m_begin;
+ }
+
+ /// @{
+ /**
+ * Advance the begin iterator, until it's equal to `it`.
+ * Assumes that `it` is reachable by repeatedly incrementing begin,
+ * will hang otherwise.
+ */
+ template <typename R = range_nocvref_type,
+ typename std::enable_if<SCN_CHECK_CONCEPT(
+ ranges::sized_range<R>)>::type* = nullptr>
+ void advance_to(iterator it) noexcept
+ {
+ const auto diff = ranges::distance(m_begin, it);
+ m_read += diff;
+ m_begin = it;
+ }
+ template <typename R = range_nocvref_type,
+ typename std::enable_if<SCN_CHECK_CONCEPT(
+ !ranges::sized_range<R>)>::type* = nullptr>
+ void advance_to(iterator it) noexcept
+ {
+ while (m_begin != it) {
+ ++m_read;
+ ++m_begin;
+ }
+ }
+ /// @}
+
+ /**
+ * Returns the begin iterator of the underlying source range, is not
+ * necessarily equal to `begin()`.
+ */
+ iterator begin_underlying() const noexcept(noexcept(
+ ranges::cbegin(SCN_DECLVAL(const range_nocvref_type&))))
+ {
+ return ranges::cbegin(m_range.get());
+ }
+
+ /**
+ * Returns the underlying source range.
+ * Note that `range_underlying().begin()` may not be equal to
+ * `begin()`.
+ */
+ const range_type& range_underlying() const noexcept
+ {
+ return m_range.get();
+ }
+
+ /**
+ * Returns a pointer to the beginning of the range.
+ * `*this` must be contiguous.
+ */
+ template <typename R = range_nocvref_type,
+ typename std::enable_if<SCN_CHECK_CONCEPT(
+ ranges::contiguous_range<R>)>::type* = nullptr>
+ auto data() const
+ noexcept(noexcept(*SCN_DECLVAL(ranges::iterator_t<const R>)))
+ -> decltype(std::addressof(
+ *SCN_DECLVAL(ranges::iterator_t<const R>)))
+ {
+ return std::addressof(*m_begin);
+ }
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wnoexcept")
+ /**
+ * Returns `end() - begin()`.
+ * `*this` must be sized.
+ */
+ template <typename R = range_nocvref_type,
+ typename std::enable_if<SCN_CHECK_CONCEPT(
+ ranges::sized_range<R>)>::type* = nullptr>
+ auto size() const noexcept(noexcept(
+ ranges::distance(SCN_DECLVAL(ranges::iterator_t<const R>),
+ SCN_DECLVAL(ranges::sentinel_t<const R>))))
+ -> decltype(ranges::distance(
+ SCN_DECLVAL(ranges::iterator_t<const R>),
+ SCN_DECLVAL(ranges::sentinel_t<const R>)))
+ {
+ return ranges::distance(m_begin, end());
+ }
+ SCN_GCC_POP
+ struct dummy2 {
+ };
+
+ template <typename R = range_nocvref_type,
+ typename std::enable_if<provides_buffer_access_impl<
+ R>::value>::type* = nullptr>
+ span<const char_type> get_buffer_and_advance(
+ size_t max_size = std::numeric_limits<size_t>::max())
+ {
+ auto buf = get_buffer(m_range.get(), begin(), max_size);
+ if (buf.size() == 0) {
+ return buf;
+ }
+ advance(buf.ssize());
+ return buf;
+ }
+
+ /**
+ * Reset `begin()` to the rollback point, as if by repeatedly
+ * calling `operator--()` on the begin iterator.
+ *
+ * Returns `error::unrecoverable_source_error` on failure.
+ *
+ * \see set_rollback_point()
+ */
+ error reset_to_rollback_point()
+ {
+ for (; m_read != 0; --m_read) {
+ --m_begin;
+ if (m_begin == end()) {
+ return {error::unrecoverable_source_error,
+ "Putback failed"};
+ }
+ }
+ return {};
+ }
+ /**
+ * Sets the rollback point equal to the current `begin()` iterator.
+ *
+ * \see reset_to_rollback_point()
+ */
+ void set_rollback_point()
+ {
+ m_read = 0;
+ }
+
+ void reset_begin_iterator()
+ {
+ detail::reset_begin_iterator(m_begin);
+ }
+
+ /**
+ * Construct a new source range from `begin()` and `end()`, and wrap
+ * it in a new `range_wrapper`.
+ */
+ template <typename R>
+ auto reconstruct_and_rewrap() && -> range_wrapper<R>
+ {
+ auto reconstructed =
+ reconstruct(reconstruct_tag<R>{}, begin(), end());
+ return {SCN_MOVE(reconstructed)};
+ }
+
+ /**
+ * `true` if `value_type` is a character type (`char` or `wchar_t`)
+ * `false` if it's an `expected` containing a character
+ */
+ static constexpr bool is_direct =
+ is_direct_impl<range_nocvref_type>::value;
+ // can call .data() and memcpy
+ /**
+ * `true` if `this->data()` can be called, and `memcpy` can be
+ * performed on it.
+ */
+ static constexpr bool is_contiguous =
+ SCN_CHECK_CONCEPT(ranges::contiguous_range<range_nocvref_type>);
+ /**
+ * `true` if the range provides a way to access a contiguous buffer
+ * on it (`detail::get_buffer()`), which may not provide the entire
+ * source data, e.g. a `span` of `span`s (vectored I/O).
+ */
+ static constexpr bool provides_buffer_access =
+ provides_buffer_access_impl<range_nocvref_type>::value;
+
+ private:
+ template <typename R = Range>
+ bool _advance_check(std::ptrdiff_t n, std::true_type)
+ {
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wzero-as-null-pointer-constant")
+ return m_begin + n <= end();
+ SCN_CLANG_POP
+ }
+ template <typename R = Range>
+ bool _advance_check(std::ptrdiff_t, std::false_type)
+ {
+ return true;
+ }
+
+ storage_type m_range;
+ iterator m_begin;
+ mutable difference_type m_read{0};
+ };
+
+ namespace _wrap {
+ struct fn {
+ private:
+ template <typename Range>
+ static range_wrapper<Range> impl(const range_wrapper<Range>& r,
+ priority_tag<4>) noexcept
+ {
+ return r;
+ }
+ template <typename Range>
+ static range_wrapper<Range> impl(range_wrapper<Range>&& r,
+ priority_tag<4>) noexcept
+ {
+ return SCN_MOVE(r);
+ }
+
+ template <typename Range>
+ static auto impl(Range&& r, priority_tag<3>) noexcept(
+ noexcept(SCN_FWD(r).wrap())) -> decltype(SCN_FWD(r).wrap())
+ {
+ return SCN_FWD(r).wrap();
+ }
+
+ template <typename CharT, std::size_t N>
+ static auto impl(CharT (&str)[N], priority_tag<2>) noexcept
+ -> range_wrapper<
+ basic_string_view<typename std::remove_cv<CharT>::type>>
+ {
+ return {
+ basic_string_view<typename std::remove_cv<CharT>::type>(
+ str, str + N - 1)};
+ }
+
+ template <typename CharT, typename Allocator>
+ static auto impl(
+ const std::basic_string<CharT,
+ std::char_traits<CharT>,
+ Allocator>& str,
+ priority_tag<2>) noexcept
+ -> range_wrapper<basic_string_view<CharT>>
+ {
+ return {basic_string_view<CharT>{str.data(), str.size()}};
+ }
+ template <typename CharT, typename Allocator>
+ static auto impl(
+ std::basic_string<CharT,
+ std::char_traits<CharT>,
+ Allocator>&& str,
+ priority_tag<2>) noexcept(std::
+ is_nothrow_move_constructible<
+ decltype(str)>::value)
+ -> range_wrapper<std::basic_string<CharT,
+ std::char_traits<CharT>,
+ Allocator>>
+ {
+ return {SCN_MOVE(str)};
+ }
+
+#if SCN_HAS_STRING_VIEW
+ template <typename CharT>
+ static auto impl(const std::basic_string_view<CharT>& str,
+ priority_tag<1>) noexcept
+ -> range_wrapper<basic_string_view<CharT>>
+ {
+ return {basic_string_view<CharT>{str.data(), str.size()}};
+ }
+#endif
+ template <typename T,
+ typename CharT = typename std::remove_const<T>::type>
+ static auto impl(span<T> s, priority_tag<2>) noexcept
+ -> range_wrapper<basic_string_view<CharT>>
+ {
+ return {basic_string_view<CharT>{s.data(), s.size()}};
+ }
+
+ template <typename Range,
+ typename = typename std::enable_if<
+ SCN_CHECK_CONCEPT(ranges::view<Range>)>::type>
+ static auto impl(Range r, priority_tag<1>) noexcept
+ -> range_wrapper<Range>
+ {
+ return {r};
+ }
+
+ template <typename Range>
+ static auto impl(const Range& r, priority_tag<0>) noexcept
+ -> range_wrapper<Range&>
+ {
+ static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
+ "Input needs to be a Range");
+ return {r};
+ }
+ template <typename Range,
+ typename = typename std::enable_if<
+ !std::is_reference<Range>::value>::type>
+ static auto impl(Range&& r, priority_tag<0>) noexcept
+ -> range_wrapper<Range>
+ {
+ static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
+ "Input needs to be a Range");
+ return {SCN_MOVE(r)};
+ }
+
+ public:
+ template <typename Range>
+ auto operator()(Range&& r) const
+ noexcept(noexcept(fn::impl(SCN_FWD(r), priority_tag<4>{})))
+ -> decltype(fn::impl(SCN_FWD(r), priority_tag<4>{}))
+ {
+ return fn::impl(SCN_FWD(r), priority_tag<4>{});
+ }
+ };
+ } // namespace _wrap
+ } // namespace detail
+
+ namespace {
+ /**
+ * Create a `range_wrapper` for any supported source range.
+ */
+ static constexpr auto& wrap =
+ detail::static_const<detail::_wrap::fn>::value;
+ } // namespace
+
+ template <typename Range>
+ struct range_wrapper_for {
+ using type = decltype(wrap(SCN_DECLVAL(Range)));
+ };
+ template <typename Range>
+ using range_wrapper_for_t = typename range_wrapper_for<Range>::type;
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_RANGE_H
diff --git a/src/third-party/scnlib/include/scn/detail/result.h b/src/third-party/scnlib/include/scn/detail/result.h
new file mode 100644
index 0000000..6e5170d
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/result.h
@@ -0,0 +1,595 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_RESULT_H
+#define SCN_DETAIL_RESULT_H
+
+#include "../util/expected.h"
+#include "error.h"
+#include "range.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * Base class for the result type returned by most scanning functions
+ * (except for \ref scan_value). \ref scn::detail::scan_result_base inherits
+ * either from this class or \ref expected.
+ */
+ struct wrapped_error {
+ wrapped_error() = default;
+ wrapped_error(::scn::error e) : err(e) {}
+
+ /// Get underlying error
+ SCN_NODISCARD ::scn::error error() const
+ {
+ return err;
+ }
+
+ /// Did the operation succeed -- true means success
+ explicit operator bool() const
+ {
+ return err.operator bool();
+ }
+
+ ::scn::error err{};
+ };
+
+ namespace detail {
+ template <typename Base>
+ class scan_result_base_wrapper : public Base {
+ public:
+ scan_result_base_wrapper(Base&& b) : Base(SCN_MOVE(b)) {}
+
+ protected:
+ void set_base(const Base& b)
+ {
+ static_cast<Base&>(*this) = b;
+ }
+ void set_base(Base&& b)
+ {
+ static_cast<Base&>(*this) = SCN_MOVE(b);
+ }
+ };
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wdocumentation-unknown-command")
+
+ /// @{
+
+ /**
+ * Type returned by scanning functions.
+ * Contains an error (inherits from it: for \ref error, that's \ref
+ * wrapped_error; with \ref scan_value, inherits from \ref expected),
+ * and the leftover range after scanning.
+ *
+ * The leftover range may reference the range given to the scanning
+ * function. Please take the necessary measures to make sure that the
+ * original range outlives the leftover range. Alternatively, if
+ * possible for your specific range type, call the \ref reconstruct()
+ * member function to get a new, independent range.
+ */
+ template <typename WrappedRange, typename Base>
+ class scan_result_base : public scan_result_base_wrapper<Base> {
+ public:
+ using wrapped_range_type = WrappedRange;
+ using base_type = scan_result_base_wrapper<Base>;
+
+ using range_type = typename wrapped_range_type::range_type;
+ using iterator = typename wrapped_range_type::iterator;
+ using sentinel = typename wrapped_range_type::sentinel;
+ using char_type = typename wrapped_range_type::char_type;
+
+ scan_result_base(Base&& b, wrapped_range_type&& r)
+ : base_type(SCN_MOVE(b)), m_range(SCN_MOVE(r))
+ {
+ }
+
+ /// Beginning of the leftover range
+ iterator begin() const noexcept
+ {
+ return m_range.begin();
+ }
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wnoexcept")
+ // Mitigate problem where Doxygen would think that SCN_GCC_PUSH was
+ // a part of the definition of end()
+ public:
+ /// End of the leftover range
+ sentinel end() const
+ noexcept(noexcept(SCN_DECLVAL(wrapped_range_type).end()))
+ {
+ return m_range.end();
+ }
+
+ /// Whether the leftover range is empty
+ bool empty() const
+ noexcept(noexcept(SCN_DECLVAL(wrapped_range_type).end()))
+ {
+ return begin() == end();
+ }
+ SCN_GCC_POP
+ // See above at SCN_GCC_PUSH
+ public:
+ /// A subrange pointing to the leftover range
+ ranges::subrange<iterator, sentinel> subrange() const
+ {
+ return {begin(), end()};
+ }
+
+ /**
+ * Leftover range.
+ * If the leftover range is used to scan a new value, this member
+ * function should be used.
+ *
+ * \see range_wrapper
+ */
+ wrapped_range_type& range() &
+ {
+ return m_range;
+ }
+ /// \copydoc range()
+ const wrapped_range_type& range() const&
+ {
+ return m_range;
+ }
+ /// \copydoc range()
+ wrapped_range_type range() &&
+ {
+ return SCN_MOVE(m_range);
+ }
+
+ /**
+ * \defgroup range_as_range Contiguous leftover range convertors
+ *
+ * These member functions enable more convenient use of the
+ * leftover range for non-scnlib use cases. The range must be
+ * contiguous. The leftover range is not advanced, and can still be
+ * used.
+ *
+ * @{
+ */
+
+ /**
+ * \ingroup range_as_range
+ * Return a view into the leftover range as a \c string_view.
+ * Operations done to the leftover range after a call to this may
+ * cause issues with iterator invalidation. The returned range will
+ * reference to the leftover range, so be wary of
+ * use-after-free-problems.
+ */
+ template <
+ typename R = wrapped_range_type,
+ typename = typename std::enable_if<R::is_contiguous>::type>
+ basic_string_view<char_type> range_as_string_view() const
+ {
+ return {m_range.data(),
+ static_cast<std::size_t>(m_range.size())};
+ }
+ /**
+ * \ingroup range_as_range
+ * Return a view into the leftover range as a \c span.
+ * Operations done to the leftover range after a call to this may
+ * cause issues with iterator invalidation. The returned range will
+ * reference to the leftover range, so be wary of
+ * use-after-free-problems.
+ */
+ template <
+ typename R = wrapped_range_type,
+ typename = typename std::enable_if<R::is_contiguous>::type>
+ span<const char_type> range_as_span() const
+ {
+ return {m_range.data(),
+ static_cast<std::size_t>(m_range.size())};
+ }
+ /**
+ * \ingroup range_as_range
+ * Return the leftover range as a string. The contents are copied
+ * into the string, so using this will not lead to lifetime issues.
+ */
+ template <
+ typename R = wrapped_range_type,
+ typename = typename std::enable_if<R::is_contiguous>::type>
+ std::basic_string<char_type> range_as_string() const
+ {
+ return {m_range.data(),
+ static_cast<std::size_t>(m_range.size())};
+ }
+ /// @}
+
+ protected:
+ wrapped_range_type m_range;
+
+ private:
+ /// \publicsection
+
+ /**
+ * Reconstructs a range of the original type, independent of the
+ * leftover range, beginning from \ref begin and ending in \ref end.
+ *
+ * Compiles only if range is reconstructible.
+ */
+ template <typename R = typename WrappedRange::range_type>
+ R reconstruct() const;
+ };
+
+ template <typename WrappedRange, typename Base>
+ class intermediary_scan_result
+ : public scan_result_base<WrappedRange, Base> {
+ public:
+ using base_type = scan_result_base<WrappedRange, Base>;
+
+ intermediary_scan_result(Base&& b, WrappedRange&& r)
+ : base_type(SCN_MOVE(b), SCN_MOVE(r))
+ {
+ }
+
+ template <typename R = WrappedRange>
+ void reconstruct() const
+ {
+ static_assert(
+ dependent_false<R>::value,
+ "Cannot call .reconstruct() on intermediary_scan_result. "
+ "Assign this value to a previous result value returned by "
+ "a scanning function or make_result (type: "
+ "reconstructed_scan_result or "
+ "non_reconstructed_scan_result) ");
+ }
+ };
+ template <typename WrappedRange, typename Base>
+ class reconstructed_scan_result
+ : public intermediary_scan_result<WrappedRange, Base> {
+ public:
+ using unwrapped_range_type = typename WrappedRange::range_type;
+ using base_type = intermediary_scan_result<WrappedRange, Base>;
+
+ reconstructed_scan_result(Base&& b, WrappedRange&& r)
+ : base_type(SCN_MOVE(b), SCN_MOVE(r))
+ {
+ }
+
+ reconstructed_scan_result& operator=(
+ const intermediary_scan_result<WrappedRange, Base>& other)
+ {
+ this->set_base(other);
+ this->m_range = other.range();
+ return *this;
+ }
+ reconstructed_scan_result& operator=(
+ intermediary_scan_result<WrappedRange, Base>&& other)
+ {
+ this->set_base(other);
+ this->m_range = other.range();
+ return *this;
+ }
+
+ unwrapped_range_type reconstruct() const
+ {
+ return this->range().range_underlying();
+ }
+ };
+ template <typename WrappedRange, typename UnwrappedRange, typename Base>
+ class non_reconstructed_scan_result
+ : public intermediary_scan_result<WrappedRange, Base> {
+ public:
+ using unwrapped_range_type = UnwrappedRange;
+ using base_type = intermediary_scan_result<WrappedRange, Base>;
+
+ non_reconstructed_scan_result(Base&& b, WrappedRange&& r)
+ : base_type(SCN_MOVE(b), SCN_MOVE(r))
+ {
+ }
+
+ non_reconstructed_scan_result& operator=(
+ const intermediary_scan_result<WrappedRange, Base>& other)
+ {
+ this->set_base(other);
+ this->m_range = other.range();
+ return *this;
+ }
+ non_reconstructed_scan_result& operator=(
+ intermediary_scan_result<WrappedRange, Base>&& other)
+ {
+ this->set_base(other);
+ this->m_range = other.range();
+ return *this;
+ }
+
+ template <typename R = unwrapped_range_type>
+ R reconstruct() const
+ {
+ return ::scn::detail::reconstruct(reconstruct_tag<R>{},
+ this->begin(), this->end());
+ }
+ };
+
+ /// @}
+
+ // -Wdocumentation-unknown-command
+ SCN_CLANG_PUSH
+
+ template <typename T>
+ struct range_tag {
+ };
+
+ namespace _wrap_result {
+ struct fn {
+ private:
+ // Range = range_wrapper<ref>&
+ template <typename Error, typename Range>
+ static auto impl(Error e,
+ range_tag<range_wrapper<Range&>&>,
+ range_wrapper<Range&>&& range,
+ priority_tag<5>) noexcept
+ -> intermediary_scan_result<range_wrapper<Range&>, Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+ // Range = const range_wrapper<ref>&
+ template <typename Error, typename Range>
+ static auto impl(Error e,
+ range_tag<const range_wrapper<Range&>&>,
+ range_wrapper<Range&>&& range,
+ priority_tag<5>) noexcept
+ -> intermediary_scan_result<range_wrapper<Range&>, Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+ // Range = range_wrapper<ref>&&
+ template <typename Error, typename Range>
+ static auto impl(Error e,
+ range_tag<range_wrapper<Range&>>,
+ range_wrapper<Range&>&& range,
+ priority_tag<5>) noexcept
+ -> intermediary_scan_result<range_wrapper<Range&>, Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+
+ // Range = range_wrapper<non-ref>&
+ template <typename Error, typename Range>
+ static auto impl(Error e,
+ range_tag<range_wrapper<Range>&>,
+ range_wrapper<Range>&& range,
+ priority_tag<4>) noexcept
+ -> intermediary_scan_result<range_wrapper<Range>, Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+ // Range = const range_wrapper<non-ref>&
+ template <typename Error, typename Range>
+ static auto impl(Error e,
+ range_tag<const range_wrapper<Range>&>,
+ range_wrapper<Range>&& range,
+ priority_tag<4>) noexcept
+ -> intermediary_scan_result<range_wrapper<Range>, Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+ // Range = range_wrapper<non-ref>&&
+ template <typename Error, typename Range>
+ static auto impl(Error e,
+ range_tag<range_wrapper<Range>>,
+ range_wrapper<Range>&& range,
+ priority_tag<4>) noexcept
+ -> intermediary_scan_result<range_wrapper<Range>, Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+
+ // string literals are wonky
+ template <typename Error,
+ typename CharT,
+ size_t N,
+ typename NoCVRef = remove_cvref_t<CharT>>
+ static auto impl(
+ Error e,
+ range_tag<CharT (&)[N]>,
+ range_wrapper<basic_string_view<NoCVRef>>&& range,
+ priority_tag<3>) noexcept
+ -> reconstructed_scan_result<
+ range_wrapper<basic_string_view<NoCVRef>>,
+ Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)
+ .template reconstruct_and_rewrap<
+ basic_string_view<NoCVRef>>()};
+ }
+
+ // (const) InputRange&: View + Reconstructible
+ // wrapped<any>
+ template <typename Error,
+ typename InputRange,
+ typename InnerWrappedRange,
+ typename InputRangeNoConst =
+ typename std::remove_const<InputRange>::type,
+ typename = typename std::enable_if<SCN_CHECK_CONCEPT(
+ ranges::view<InputRangeNoConst>)>::type>
+ static auto impl(Error e,
+ range_tag<InputRange&>,
+ range_wrapper<InnerWrappedRange>&& range,
+ priority_tag<2>) noexcept
+ -> reconstructed_scan_result<
+ decltype(SCN_MOVE(range)
+ .template reconstruct_and_rewrap<
+ InputRangeNoConst>()),
+ Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)
+ .template reconstruct_and_rewrap<
+ InputRangeNoConst>()};
+ }
+
+ // (const) InputRange&: other
+ // wrapped<any>
+ template <typename Error,
+ typename InputRange,
+ typename InnerWrappedRange>
+ static auto impl(Error e,
+ range_tag<InputRange&>,
+ range_wrapper<InnerWrappedRange>&& range,
+ priority_tag<1>) noexcept
+ -> non_reconstructed_scan_result<
+ range_wrapper<InnerWrappedRange>,
+ typename std::remove_const<InputRange>::type,
+ Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+
+ // InputRange&&: View + Reconstructible
+ // wrapped<non-ref>
+ template <typename Error,
+ typename InputRange,
+ typename InnerWrappedRange,
+ typename InputRangeNoConst =
+ typename std::remove_const<InputRange>::type,
+ typename = typename std::enable_if<SCN_CHECK_CONCEPT(
+ ranges::view<InputRangeNoConst>)>::type>
+ static auto impl(Error e,
+ range_tag<InputRange>,
+ range_wrapper<InnerWrappedRange>&& range,
+ priority_tag<1>) noexcept
+ -> reconstructed_scan_result<
+ decltype(SCN_MOVE(range)
+ .template reconstruct_and_rewrap<
+ InputRangeNoConst>()),
+ Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)
+ .template reconstruct_and_rewrap<
+ InputRangeNoConst>()};
+ }
+
+ // InputRange&&: other
+ // wrapped<non-ref>
+ template <typename Error,
+ typename InputRange,
+ typename InnerWrappedRange>
+ static auto impl(Error e,
+ range_tag<InputRange>,
+ range_wrapper<InnerWrappedRange>&& range,
+ priority_tag<0>) noexcept
+ -> non_reconstructed_scan_result<
+ range_wrapper<InputRange>,
+ typename std::remove_const<InputRange>::type,
+ Error>
+ {
+ return {SCN_MOVE(e), SCN_MOVE(range)};
+ }
+
+#if 0
+ // InputRange&&
+ // wrapped<ref>
+ template <typename Error,
+ typename InputRange,
+ typename InnerWrappedRange,
+ typename NoRef = typename std::remove_reference<
+ InnerWrappedRange>::type>
+ static auto impl(Error e,
+ range_tag<InputRange>,
+ range_wrapper<InnerWrappedRange&>&& range,
+ priority_tag<0>) noexcept
+ -> reconstructed_scan_result<range_wrapper<NoRef>, Error>
+ {
+ return {SCN_MOVE(e),
+ SCN_MOVE(range)
+ .template rewrap_and_reconstruct<NoRef>()};
+ }
+#endif
+
+ public:
+ template <typename Error,
+ typename InputRange,
+ typename InnerWrappedRange>
+ auto operator()(Error e,
+ range_tag<InputRange> tag,
+ range_wrapper<InnerWrappedRange>&& range) const
+ noexcept(noexcept(impl(SCN_MOVE(e),
+ tag,
+ SCN_MOVE(range),
+ priority_tag<5>{})))
+ -> decltype(impl(SCN_MOVE(e),
+ tag,
+ SCN_MOVE(range),
+ priority_tag<5>{}))
+ {
+ static_assert(SCN_CHECK_CONCEPT(ranges::range<InputRange>),
+ "Input needs to be a Range");
+ return impl(SCN_MOVE(e), tag, SCN_MOVE(range),
+ priority_tag<5>{});
+ }
+ };
+ } // namespace _wrap_result
+ namespace {
+ static constexpr auto& wrap_result =
+ static_const<_wrap_result::fn>::value;
+ }
+
+ template <typename Error, typename InputRange, typename WrappedRange>
+ struct result_type_for {
+ using type =
+ decltype(wrap_result(SCN_DECLVAL(Error &&),
+ SCN_DECLVAL(range_tag<InputRange>),
+ SCN_DECLVAL(WrappedRange&&)));
+ };
+ template <typename Error, typename InputRange, typename WrappedRange>
+ using result_type_for_t =
+ typename result_type_for<Error, InputRange, WrappedRange>::type;
+ } // namespace detail
+
+ /**
+ * Create a result object for range \c Range.
+ * Useful if one wishes to scan from the same range in a loop.
+ *
+ * \code{.cpp}
+ * auto source = ...;
+ * auto result = make_result(source);
+ * // scan until failure (no more `int`s, or EOF)
+ * while (result) {
+ * int i;
+ * result = scn::scan(result.range(), "{}", i);
+ * // use i
+ * }
+ * // see result for why we exited the loop
+ * \endcode
+ *
+ * \c Error template parameter can be used to customize the error type for
+ * the result object. By default, it's \ref wrapped_error, which is what
+ * most of the scanning functions use. For \c scan_value, use \c
+ * expected<T>:
+ *
+ * \code{.cpp}
+ * auto result = make_result<scn::expected<int>>(source);
+ * while (result) {
+ * result = scn::scan_value<int>(result.range(), "{}");
+ * // use result.value()
+ * }
+ * \endcode
+ */
+ template <typename Error = wrapped_error, typename Range>
+ auto make_result(Range&& r)
+ -> detail::result_type_for_t<Error, Range, range_wrapper_for_t<Range>>
+ {
+ return detail::wrap_result(Error{}, detail::range_tag<Range>{},
+ wrap(r));
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_RESULT_H
diff --git a/src/third-party/scnlib/include/scn/detail/vectored.h b/src/third-party/scnlib/include/scn/detail/vectored.h
new file mode 100644
index 0000000..f5c3868
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/vectored.h
@@ -0,0 +1,166 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_VECTORED_H
+#define SCN_DETAIL_VECTORED_H
+
+#include "../ranges/util.h"
+#include "../util/math.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ namespace _get_buffer {
+ struct fn {
+ private:
+ template <typename It>
+ static It _get_end(It begin, It end, size_t max_size)
+ {
+ return begin +
+ min(max_size, static_cast<std::size_t>(
+ ranges::distance(begin, end)));
+ }
+
+ template <typename CharT>
+ static SCN_CONSTEXPR14
+ span<typename std::add_const<CharT>::type>
+ impl(span<span<CharT>> s,
+ typename span<CharT>::iterator begin,
+ size_t max_size,
+ priority_tag<3>) noexcept
+ {
+ auto buf_it = s.begin();
+ for (; buf_it != s.end(); ++buf_it) {
+ if (begin >= buf_it->begin() && begin < buf_it->end()) {
+ break;
+ }
+ if (begin == buf_it->end()) {
+ ++buf_it;
+ begin = buf_it->begin();
+ break;
+ }
+ }
+ if (buf_it == s.end()) {
+ return {};
+ }
+ return {begin, _get_end(begin, buf_it->end(), max_size)};
+ }
+
+ template <
+ typename Range,
+ typename std::enable_if<SCN_CHECK_CONCEPT(
+ ranges::contiguous_range<Range>)>::type* = nullptr>
+ static SCN_CONSTEXPR14 span<typename std::add_const<
+ ranges::range_value_t<const Range>>::type>
+ impl(const Range& s,
+ ranges::iterator_t<const Range> begin,
+ size_t max_size,
+ priority_tag<2>) noexcept
+ {
+ auto b = ranges::begin(s);
+ auto e = ranges::end(s);
+ return {to_address(begin),
+ _get_end(to_address(begin),
+ to_address_safe(e, b, e), max_size)};
+ }
+
+ template <typename Range, typename It>
+ static auto impl(
+ const Range& r,
+ It begin,
+ size_t max_size,
+ priority_tag<1>) noexcept(noexcept(r.get_buffer(begin,
+ max_size)))
+ -> decltype(r.get_buffer(begin, max_size))
+ {
+ return r.get_buffer(begin, max_size);
+ }
+
+ template <typename Range, typename It>
+ static auto impl(
+ const Range& r,
+ It begin,
+ size_t max_size,
+ priority_tag<0>) noexcept(noexcept(get_buffer(r,
+ begin,
+ max_size)))
+ -> decltype(get_buffer(r, begin, max_size))
+ {
+ return get_buffer(r, begin, max_size);
+ }
+
+ public:
+ template <typename Range, typename It>
+ SCN_CONSTEXPR14 auto operator()(const Range& r,
+ It begin,
+ size_t max_size) const
+ noexcept(noexcept(
+ fn::impl(r, begin, max_size, priority_tag<3>{})))
+ -> decltype(fn::impl(r,
+ begin,
+ max_size,
+ priority_tag<3>{}))
+ {
+ return fn::impl(r, begin, max_size, priority_tag<3>{});
+ }
+
+ template <typename Range, typename It>
+ SCN_CONSTEXPR14 auto operator()(const Range& r, It begin) const
+ noexcept(
+ noexcept(fn::impl(r,
+ begin,
+ std::numeric_limits<size_t>::max(),
+ priority_tag<3>{})))
+ -> decltype(fn::impl(r,
+ begin,
+ std::numeric_limits<size_t>::max(),
+ priority_tag<3>{}))
+ {
+ return fn::impl(r, begin,
+ std::numeric_limits<size_t>::max(),
+ priority_tag<3>{});
+ }
+ };
+ } // namespace _get_buffer
+
+ namespace {
+ static constexpr auto& get_buffer =
+ detail::static_const<_get_buffer::fn>::value;
+ } // namespace
+
+ struct provides_buffer_access_concept {
+ template <typename Range, typename Iterator>
+ auto _test_requires(const Range& r, Iterator begin)
+ -> decltype(scn::detail::valid_expr(
+ ::scn::detail::get_buffer(r, begin)));
+ };
+ template <typename Range, typename = void>
+ struct provides_buffer_access_impl
+ : std::integral_constant<
+ bool,
+ ::scn::custom_ranges::detail::_requires<
+ provides_buffer_access_concept,
+ Range,
+ ::scn::ranges::iterator_t<const Range>>::value> {
+ };
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/detail/visitor.h b/src/third-party/scnlib/include/scn/detail/visitor.h
new file mode 100644
index 0000000..e88e2f7
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/detail/visitor.h
@@ -0,0 +1,248 @@
+// Copyright 2017 Elias Kosunen
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// This file is a part of scnlib:
+// https://github.com/eliaskosunen/scnlib
+
+#ifndef SCN_DETAIL_VISITOR_H
+#define SCN_DETAIL_VISITOR_H
+
+#include "../reader/reader.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ template <typename Context, typename ParseCtx>
+ class basic_visitor {
+ public:
+ using context_type = Context;
+ using char_type = typename Context::char_type;
+ using arg_type = basic_arg<char_type>;
+
+ basic_visitor(Context& ctx, ParseCtx& pctx)
+ : m_ctx(std::addressof(ctx)), m_pctx(std::addressof(pctx))
+ {
+ }
+
+ template <typename T>
+ auto operator()(T&& val) -> error
+ {
+ return visit(SCN_FWD(val), detail::priority_tag<1>{});
+ }
+
+ private:
+ auto visit(code_point& val, detail::priority_tag<1>) -> error
+ {
+ return detail::visitor_boilerplate<detail::code_point_scanner>(
+ val, *m_ctx, *m_pctx);
+ }
+ auto visit(span<char_type>& val, detail::priority_tag<1>) -> error
+ {
+ return detail::visitor_boilerplate<detail::span_scanner>(
+ val, *m_ctx, *m_pctx);
+ }
+ auto visit(bool& val, detail::priority_tag<1>) -> error
+ {
+ return detail::visitor_boilerplate<detail::bool_scanner>(
+ val, *m_ctx, *m_pctx);
+ }
+
+#define SCN_VISIT_INT(T) \
+ error visit(T& val, detail::priority_tag<0>) \
+ { \
+ return detail::visitor_boilerplate<detail::integer_scanner<T>>( \
+ val, *m_ctx, *m_pctx); \
+ }
+ SCN_VISIT_INT(signed char)
+ SCN_VISIT_INT(short)
+ SCN_VISIT_INT(int)
+ SCN_VISIT_INT(long)
+ SCN_VISIT_INT(long long)
+ SCN_VISIT_INT(unsigned char)
+ SCN_VISIT_INT(unsigned short)
+ SCN_VISIT_INT(unsigned int)
+ SCN_VISIT_INT(unsigned long)
+ SCN_VISIT_INT(unsigned long long)
+ SCN_VISIT_INT(char_type)
+#undef SCN_VISIT_INT
+
+#define SCN_VISIT_FLOAT(T) \
+ error visit(T& val, detail::priority_tag<1>) \
+ { \
+ return detail::visitor_boilerplate<detail::float_scanner<T>>( \
+ val, *m_ctx, *m_pctx); \
+ }
+ SCN_VISIT_FLOAT(float)
+ SCN_VISIT_FLOAT(double)
+ SCN_VISIT_FLOAT(long double)
+#undef SCN_VISIT_FLOAT
+
+ auto visit(std::basic_string<char_type>& val, detail::priority_tag<1>)
+ -> error
+ {
+ return detail::visitor_boilerplate<detail::string_scanner>(
+ val, *m_ctx, *m_pctx);
+ }
+ auto visit(basic_string_view<char_type>& val, detail::priority_tag<1>)
+ -> error
+ {
+ return detail::visitor_boilerplate<detail::string_view_scanner>(
+ val, *m_ctx, *m_pctx);
+ }
+ auto visit(typename arg_type::handle val, detail::priority_tag<1>)
+ -> error
+ {
+ return val.scan(*m_ctx, *m_pctx);
+ }
+ [[noreturn]] auto visit(detail::monostate, detail::priority_tag<0>)
+ -> error
+ {
+ SCN_UNREACHABLE;
+ }
+
+ Context* m_ctx;
+ ParseCtx* m_pctx;
+ };
+
+ template <typename Context, typename ParseCtx>
+ error visit(Context& ctx,
+ ParseCtx& pctx,
+ basic_args<typename Context::char_type> args)
+ {
+ using char_type = typename Context::char_type;
+ using arg_type = basic_arg<char_type>;
+ auto arg = arg_type{};
+
+ while (pctx) {
+ if (pctx.should_skip_ws()) {
+ // Skip whitespace from format string and from stream
+ // EOF is not an error
+ auto ret = skip_range_whitespace(ctx, false);
+ if (SCN_UNLIKELY(!ret)) {
+ if (ret == error::end_of_range) {
+ break;
+ }
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto rb = ctx.range().reset_to_rollback_point();
+ if (!rb) {
+ return rb;
+ }
+ return ret;
+ }
+ // Don't advance pctx, should_skip_ws() does it for us
+ continue;
+ }
+
+ // Non-brace character, or
+ // Brace followed by another brace, meaning a literal '{'
+ if (pctx.should_read_literal()) {
+ if (SCN_UNLIKELY(!pctx)) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string"};
+ }
+ // Check for any non-specifier {foo} characters
+ alignas(typename Context::char_type) unsigned char buf[4] = {0};
+ auto ret = read_code_point(ctx.range(), make_span(buf, 4));
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!ret || !pctx.check_literal(ret.value().chars)) {
+ auto rb = ctx.range().reset_to_rollback_point();
+ if (!rb) {
+ // Failed rollback
+ return rb;
+ }
+ if (!ret) {
+ // Failed read
+ return ret.error();
+ }
+
+ // Mismatching characters in scan string and stream
+ return {error::invalid_scanned_value,
+ "Expected character from format string not "
+ "found in the stream"};
+ }
+ // Bump pctx to next char
+ if (!pctx.advance_cp()) {
+ pctx.advance_char();
+ }
+ }
+ else {
+ // Scan argument
+ auto arg_wrapped = [&]() -> expected<arg_type> {
+ if (!pctx.has_arg_id()) {
+ return next_arg(args, pctx);
+ }
+ auto id_wrapped = pctx.parse_arg_id();
+ if (!id_wrapped) {
+ return id_wrapped.error();
+ }
+ auto id = id_wrapped.value();
+ SCN_ENSURE(!id.empty());
+ if (ctx.locale().get_static().is_digit(id.front())) {
+ auto s =
+ detail::simple_integer_scanner<std::ptrdiff_t>{};
+ std::ptrdiff_t i{0};
+ auto span = make_span(id.data(), id.size());
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto ret = s.scan(span, i, 10);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!ret || ret.value() != span.end()) {
+ return error(error::invalid_format_string,
+ "Failed to parse argument id from "
+ "format string");
+ }
+ return get_arg(args, pctx, i);
+ }
+ return get_arg(args, pctx, id);
+ }();
+ if (!arg_wrapped) {
+ return arg_wrapped.error();
+ }
+ arg = arg_wrapped.value();
+ SCN_ENSURE(arg);
+ if (!pctx) {
+ return {error::invalid_format_string,
+ "Unexpected end of format argument"};
+ }
+ auto ret = visit_arg<char_type>(
+ basic_visitor<Context, ParseCtx>(ctx, pctx), arg);
+ if (!ret) {
+ auto rb = ctx.range().reset_to_rollback_point();
+ if (!rb) {
+ return rb;
+ }
+ return ret;
+ }
+ // Handle next arg and bump pctx
+ pctx.arg_handled();
+ if (pctx) {
+ auto e = pctx.advance_cp();
+ if (!e) {
+ return e;
+ }
+ }
+ }
+ }
+ if (pctx) {
+ // Format string not exhausted
+ return {error::invalid_format_string,
+ "Format string not exhausted"};
+ }
+ ctx.range().set_rollback_point();
+ return {};
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_VISITOR_H