summaryrefslogtreecommitdiffstats
path: root/src/third-party/scnlib/include
diff options
context:
space:
mode:
Diffstat (limited to 'src/third-party/scnlib/include')
-rw-r--r--src/third-party/scnlib/include/scn/all.h26
-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
-rw-r--r--src/third-party/scnlib/include/scn/fwd.h23
-rw-r--r--src/third-party/scnlib/include/scn/istream.h23
-rw-r--r--src/third-party/scnlib/include/scn/ranges/custom_impl.h1632
-rw-r--r--src/third-party/scnlib/include/scn/ranges/ranges.h49
-rw-r--r--src/third-party/scnlib/include/scn/ranges/std_impl.h67
-rw-r--r--src/third-party/scnlib/include/scn/ranges/util.h419
-rw-r--r--src/third-party/scnlib/include/scn/reader/common.h1663
-rw-r--r--src/third-party/scnlib/include/scn/reader/float.h246
-rw-r--r--src/third-party/scnlib/include/scn/reader/int.h537
-rw-r--r--src/third-party/scnlib/include/scn/reader/reader.h111
-rw-r--r--src/third-party/scnlib/include/scn/reader/string.h1336
-rw-r--r--src/third-party/scnlib/include/scn/reader/types.h220
-rw-r--r--src/third-party/scnlib/include/scn/scan/common.h131
-rw-r--r--src/third-party/scnlib/include/scn/scan/getline.h186
-rw-r--r--src/third-party/scnlib/include/scn/scan/ignore.h189
-rw-r--r--src/third-party/scnlib/include/scn/scan/istream.h147
-rw-r--r--src/third-party/scnlib/include/scn/scan/list.h450
-rw-r--r--src/third-party/scnlib/include/scn/scan/scan.h444
-rw-r--r--src/third-party/scnlib/include/scn/scan/vscan.h208
-rw-r--r--src/third-party/scnlib/include/scn/scn.h26
-rw-r--r--src/third-party/scnlib/include/scn/tuple_return.h23
-rw-r--r--src/third-party/scnlib/include/scn/tuple_return/tuple_return.h123
-rw-r--r--src/third-party/scnlib/include/scn/tuple_return/util.h176
-rw-r--r--src/third-party/scnlib/include/scn/unicode/common.h139
-rw-r--r--src/third-party/scnlib/include/scn/unicode/unicode.h243
-rw-r--r--src/third-party/scnlib/include/scn/unicode/utf16.h139
-rw-r--r--src/third-party/scnlib/include/scn/unicode/utf8.h297
-rw-r--r--src/third-party/scnlib/include/scn/util/algorithm.h80
-rw-r--r--src/third-party/scnlib/include/scn/util/array.h105
-rw-r--r--src/third-party/scnlib/include/scn/util/expected.h158
-rw-r--r--src/third-party/scnlib/include/scn/util/math.h121
-rw-r--r--src/third-party/scnlib/include/scn/util/memory.h404
-rw-r--r--src/third-party/scnlib/include/scn/util/meta.h77
-rw-r--r--src/third-party/scnlib/include/scn/util/optional.h105
-rw-r--r--src/third-party/scnlib/include/scn/util/small_vector.h788
-rw-r--r--src/third-party/scnlib/include/scn/util/span.h240
-rw-r--r--src/third-party/scnlib/include/scn/util/string_view.h270
-rw-r--r--src/third-party/scnlib/include/scn/util/unique_ptr.h118
51 files changed, 16641 insertions, 0 deletions
diff --git a/src/third-party/scnlib/include/scn/all.h b/src/third-party/scnlib/include/scn/all.h
new file mode 100644
index 0000000..b69e822
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/all.h
@@ -0,0 +1,26 @@
+// 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_ALL_H
+#define SCN_ALL_H
+
+#include "scn.h"
+
+#include "istream.h"
+#include "tuple_return.h"
+
+#endif // SCN_ALL_H
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
diff --git a/src/third-party/scnlib/include/scn/fwd.h b/src/third-party/scnlib/include/scn/fwd.h
new file mode 100644
index 0000000..e91a258
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/fwd.h
@@ -0,0 +1,23 @@
+// 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_FWD_H
+#define SCN_FWD_H
+
+#include "detail/fwd.h"
+
+#endif // SCN_FWD_H
diff --git a/src/third-party/scnlib/include/scn/istream.h b/src/third-party/scnlib/include/scn/istream.h
new file mode 100644
index 0000000..acb2774
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/istream.h
@@ -0,0 +1,23 @@
+// 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_ISTREAM_H
+#define SCN_ISTREAM_H
+
+#include "scan/istream.h"
+
+#endif // SCN_ISTREAM_H
diff --git a/src/third-party/scnlib/include/scn/ranges/custom_impl.h b/src/third-party/scnlib/include/scn/ranges/custom_impl.h
new file mode 100644
index 0000000..86d73d5
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/ranges/custom_impl.h
@@ -0,0 +1,1632 @@
+// 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
+//
+// The contents of this file are adapted from NanoRange.
+// https://github.com/tcbrindle/NanoRange
+// Copyright (c) 2018 Tristan Brindle
+// Distributed under the Boost Software License, Version 1.0
+
+#ifndef SCN_RANGES_CUSTOM_IMPL_H
+#define SCN_RANGES_CUSTOM_IMPL_H
+
+#include "util.h"
+
+#include "../util/string_view.h"
+
+#include <iterator>
+#include <utility>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace custom_ranges {
+ // iterator_category is span.h
+
+ template <typename T>
+ struct iterator_category;
+
+ namespace detail {
+ template <typename T, typename = void>
+ struct iterator_category {
+ };
+ template <typename T>
+ struct iterator_category<T*>
+ : std::enable_if<std::is_object<T>::value,
+ contiguous_iterator_tag> {
+ };
+ template <typename T>
+ struct iterator_category<const T> : iterator_category<T> {
+ };
+ template <typename T>
+ struct iterator_category<
+ T,
+ detail::void_t<typename T::iterator_category>> {
+ using type = typename T::iterator_category;
+ };
+ } // namespace detail
+
+ template <typename T>
+ struct iterator_category : detail::iterator_category<T> {
+ };
+ template <typename T>
+ using iterator_category_t = typename iterator_category<T>::type;
+
+ template <typename T>
+ using iter_reference_t = decltype(*SCN_DECLVAL(T&));
+
+ // iter_difference_t
+ template <typename>
+ struct incrementable_traits;
+
+ namespace detail {
+ struct empty {
+ };
+
+ template <typename T>
+ struct with_difference_type {
+ using difference_type = T;
+ };
+ template <typename, typename = void>
+ struct incrementable_traits_helper {
+ };
+
+ template <>
+ struct incrementable_traits_helper<void*> {
+ };
+ template <typename T>
+ struct incrementable_traits_helper<T*>
+ : std::conditional<std::is_object<T>::value,
+ with_difference_type<std::ptrdiff_t>,
+ empty>::type {
+ };
+ template <typename I>
+ struct incrementable_traits_helper<const I>
+ : incrementable_traits<typename std::decay<I>::type> {
+ };
+
+ template <typename, typename = void>
+ struct has_member_difference_type : std::false_type {
+ };
+ template <typename T>
+ struct has_member_difference_type<
+ T,
+ detail::void_t<typename T::difference_type>> : std::true_type {
+ };
+
+ template <typename T>
+ struct incrementable_traits_helper<
+ T,
+ typename std::enable_if<
+ has_member_difference_type<T>::value>::type> {
+ using difference_type = typename T::difference_type;
+ };
+ template <typename T>
+ struct incrementable_traits_helper<
+ T,
+ typename std::enable_if<
+ !std::is_pointer<T>::value &&
+ !has_member_difference_type<T>::value &&
+ std::is_integral<decltype(SCN_DECLVAL(const T&) -
+ SCN_DECLVAL(const T&))>::value>::
+ type>
+ : with_difference_type<typename std::make_signed<
+ decltype(SCN_DECLVAL(T) - SCN_DECLVAL(T))>::type> {
+ };
+ } // namespace detail
+
+ template <typename T>
+ struct incrementable_traits : detail::incrementable_traits_helper<T> {
+ };
+
+ template <typename T>
+ using iter_difference_t =
+ typename incrementable_traits<T>::difference_type;
+
+ // iter_value_t
+ template <typename>
+ struct readable_traits;
+
+ namespace detail {
+ template <typename T>
+ struct with_value_type {
+ using value_type = T;
+ };
+ template <typename, typename = void>
+ struct readable_traits_helper {
+ };
+
+ template <typename T>
+ struct readable_traits_helper<T*>
+ : std::conditional<
+ std::is_object<T>::value,
+ with_value_type<typename std::remove_cv<T>::type>,
+ empty>::type {
+ };
+
+ template <typename I>
+ struct readable_traits_helper<
+ I,
+ typename std::enable_if<std::is_array<I>::value>::type>
+ : readable_traits<typename std::decay<I>::type> {
+ };
+
+ template <typename I>
+ struct readable_traits_helper<
+ const I,
+ typename std::enable_if<!std::is_array<I>::value>::type>
+ : readable_traits<typename std::decay<I>::type> {
+ };
+
+ template <typename T, typename V = typename T::value_type>
+ struct member_value_type
+ : std::conditional<std::is_object<V>::value,
+ with_value_type<V>,
+ empty>::type {
+ };
+
+ template <typename T, typename E = typename T::element_type>
+ struct _member_element_type
+ : std::conditional<
+ std::is_object<E>::value,
+ with_value_type<typename std::remove_cv<E>::type>,
+ empty>::type {
+ };
+
+ template <typename T>
+ using member_value_type_t = typename T::value_type;
+
+ template <typename T>
+ struct has_member_value_type : exists<member_value_type_t, T> {
+ };
+
+ template <typename T>
+ using member_element_type_t = typename T::element_type;
+
+ template <typename T>
+ struct has_member_element_type : exists<member_element_type_t, T> {
+ };
+
+ template <typename T>
+ struct readable_traits_helper<
+ T,
+ typename std::enable_if<
+ has_member_value_type<T>::value &&
+ !has_member_element_type<T>::value>::type>
+ : member_value_type<T> {
+ };
+
+ template <typename T>
+ struct readable_traits_helper<
+ T,
+ typename std::enable_if<has_member_element_type<T>::value &&
+ !has_member_value_type<T>::value>::type>
+ : _member_element_type<T> {
+ };
+
+ template <typename T>
+ struct readable_traits_helper<
+ T,
+ typename std::enable_if<
+ has_member_element_type<T>::value &&
+ has_member_value_type<T>::value>::type> {
+ };
+ } // namespace detail
+
+ template <typename T>
+ struct readable_traits : detail::readable_traits_helper<T> {
+ };
+
+ template <typename T>
+ using iter_value_t = typename readable_traits<T>::value_type;
+
+ // sentinel_for
+ namespace detail {
+ struct sentinel_for_concept {
+ template <typename S, typename I>
+ auto _test_requires(S s, I i)
+ -> decltype(scn::detail::valid_expr(*i, i == s, i != s));
+ };
+ } // namespace detail
+ template <typename S, typename I>
+ struct sentinel_for
+ : std::integral_constant<
+ bool,
+ std::is_default_constructible<S>::value &&
+ std::is_copy_constructible<S>::value &&
+ detail::_requires<detail::sentinel_for_concept, S, I>::
+ value> {
+ };
+
+ // sized_sentinel_for
+ namespace detail {
+ struct sized_sentinel_for_concept {
+ template <typename S, typename I>
+ auto _test_requires(const S& s, const I& i) -> decltype(
+ detail::requires_expr<
+ std::is_same<decltype(s - i),
+ iter_difference_t<I>>::value>{},
+ detail::requires_expr<
+ std::is_same<decltype(i - s),
+ iter_difference_t<I>>::value>{});
+ };
+ } // namespace detail
+ template <typename S, typename I>
+ struct sized_sentinel_for
+ : std::integral_constant<
+ bool,
+ detail::_requires<detail::sized_sentinel_for_concept, S, I>::
+ value &&
+ sentinel_for<S, I>::value> {
+ };
+ template <typename S>
+ struct sized_sentinel_for<S, void*> : std::false_type {
+ };
+ template <typename I>
+ struct sized_sentinel_for<void*, I> : std::false_type {
+ };
+ template <>
+ struct sized_sentinel_for<void*, void*> : std::false_type {
+ };
+
+ // begin
+ namespace _begin {
+ template <typename T>
+ void begin(T&&) = delete;
+ template <typename T>
+ void begin(std::initializer_list<T>&&) = delete;
+
+ struct fn {
+ private:
+ template <typename T, std::size_t N>
+ static SCN_CONSTEXPR14 void impl(T(&&)[N],
+ detail::priority_tag<3>) =
+ delete;
+
+ template <typename T, std::size_t N>
+ static SCN_CONSTEXPR14 auto impl(
+ T (&t)[N],
+ detail::priority_tag<3>) noexcept -> decltype((t) + 0)
+ {
+ return (t) + 0;
+ }
+
+ template <typename C>
+ static SCN_CONSTEXPR14 auto impl(
+ basic_string_view<C> sv,
+ detail::priority_tag<2>) noexcept -> decltype(sv.begin())
+ {
+ return sv.begin();
+ }
+
+ template <typename T>
+ static SCN_CONSTEXPR14 auto
+ impl(T& t, detail::priority_tag<1>) noexcept(noexcept(
+ ::scn::custom_ranges::detail::decay_copy(t.begin())))
+ -> decltype(::scn::custom_ranges::detail::decay_copy(
+ t.begin()))
+ {
+ return ::scn::custom_ranges::detail::decay_copy(t.begin());
+ }
+
+ template <typename T>
+ static SCN_CONSTEXPR14 auto
+ impl(T&& t, detail::priority_tag<0>) noexcept(
+ noexcept(::scn::custom_ranges::detail::decay_copy(
+ begin(SCN_FWD(t)))))
+ -> decltype(::scn::custom_ranges::detail::decay_copy(
+ begin(SCN_FWD(t))))
+ {
+ return ::scn::custom_ranges::detail::decay_copy(
+ begin(SCN_FWD(t)));
+ }
+
+ public:
+ template <typename T>
+ SCN_CONSTEXPR14 auto operator()(T&& t) const noexcept(
+ noexcept(fn::impl(SCN_FWD(t), detail::priority_tag<3>{})))
+ -> decltype(fn::impl(SCN_FWD(t), detail::priority_tag<3>{}))
+ {
+ return fn::impl(SCN_FWD(t), detail::priority_tag<3>{});
+ }
+ };
+ } // namespace _begin
+ namespace {
+ constexpr auto& begin = detail::static_const<_begin::fn>::value;
+ }
+
+ // end
+ namespace _end {
+ template <typename T>
+ void end(T&&) = delete;
+ template <typename T>
+ void end(std::initializer_list<T>&&) = delete;
+
+ struct fn {
+ private:
+ template <typename T, std::size_t N>
+ static constexpr void impl(T(&&)[N],
+ detail::priority_tag<2>) = delete;
+
+ template <typename T, std::size_t N>
+ static constexpr auto impl(T (&t)[N],
+ detail::priority_tag<2>) noexcept
+ -> decltype((t) + N)
+ {
+ return (t) + N;
+ }
+
+ template <typename C>
+ static constexpr auto impl(basic_string_view<C> sv,
+ detail::priority_tag<2>) noexcept
+ -> decltype(sv.end())
+ {
+ return sv.end();
+ }
+
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wnoexcept")
+ template <typename T,
+ typename S =
+ decltype(::scn::custom_ranges::detail::decay_copy(
+ SCN_DECLVAL(T&).end())),
+ typename I = decltype(::scn::custom_ranges::begin(
+ SCN_DECLVAL(T&)))>
+ static constexpr auto
+ impl(T& t, detail::priority_tag<1>) noexcept(
+ noexcept(::scn::custom_ranges::detail::decay_copy(t.end())))
+ -> decltype(::scn::custom_ranges::detail::decay_copy(
+ t.end()))
+ {
+ return ::scn::custom_ranges::detail::decay_copy(t.end());
+ }
+
+ template <typename T,
+ typename S =
+ decltype(::scn::custom_ranges::detail::decay_copy(
+ end(SCN_DECLVAL(T)))),
+ typename I = decltype(::scn::custom_ranges::begin(
+ SCN_DECLVAL(T)))>
+ static constexpr auto
+ impl(T& t, detail::priority_tag<0>) noexcept(noexcept(
+ ::scn::custom_ranges::detail::decay_copy(end(SCN_FWD(t)))))
+ -> S
+ {
+ return ::scn::custom_ranges::detail::decay_copy(
+ end(SCN_FWD(t)));
+ }
+
+ public:
+ template <typename T>
+ constexpr auto operator()(T&& t) const noexcept(
+ noexcept(fn::impl(SCN_FWD(t), detail::priority_tag<2>{})))
+ -> decltype(fn::impl(SCN_FWD(t), detail::priority_tag<2>{}))
+ {
+ return fn::impl(SCN_FWD(t), detail::priority_tag<2>{});
+ }
+ SCN_GCC_POP
+ };
+ } // namespace _end
+ namespace {
+ constexpr auto& end = detail::static_const<_end::fn>::value;
+ }
+
+ // cbegin
+ namespace _cbegin {
+ struct fn {
+ template <typename T>
+ constexpr auto operator()(const T& t) const
+ noexcept(noexcept(::scn::custom_ranges::begin(t)))
+ -> decltype(::scn::custom_ranges::begin(t))
+ {
+ return ::scn::custom_ranges::begin(t);
+ }
+
+ template <typename T>
+ constexpr auto operator()(const T&& t) const noexcept(noexcept(
+ ::scn::custom_ranges::begin(static_cast<const T&&>(t))))
+ -> decltype(::scn::custom_ranges::begin(
+ static_cast<const T&&>(t)))
+ {
+ return ::scn::custom_ranges::begin(
+ static_cast<const T&&>(t));
+ }
+ };
+ } // namespace _cbegin
+ namespace {
+ constexpr auto& cbegin = detail::static_const<_cbegin::fn>::value;
+ }
+
+ // cend
+ namespace _cend {
+ struct fn {
+ template <typename T>
+ constexpr auto operator()(const T& t) const
+ noexcept(noexcept(::scn::custom_ranges::end(t)))
+ -> decltype(::scn::custom_ranges::end(t))
+ {
+ return ::scn::custom_ranges::end(t);
+ }
+
+ template <typename T>
+ constexpr auto operator()(const T&& t) const noexcept(noexcept(
+ ::scn::custom_ranges::end(static_cast<const T&&>(t))))
+ -> decltype(::scn::custom_ranges::end(
+ static_cast<const T&&>(t)))
+ {
+ return ::scn::custom_ranges::end(static_cast<const T&&>(t));
+ }
+ };
+ } // namespace _cend
+ namespace {
+ constexpr auto& cend = detail::static_const<_cend::fn>::value;
+ }
+
+ // range
+ namespace detail {
+ struct range_impl_concept {
+ template <typename T>
+ auto _test_requires(T&& t)
+ -> decltype(::scn::custom_ranges::begin(SCN_FWD(t)),
+ ::scn::custom_ranges::end(SCN_FWD(t)));
+ };
+ template <typename T>
+ struct range_impl : _requires<range_impl_concept, T> {
+ };
+ struct range_concept {
+ template <typename>
+ static auto test(long) -> std::false_type;
+ template <typename T>
+ static auto test(int) ->
+ typename std::enable_if<range_impl<T&>::value,
+ std::true_type>::type;
+ };
+ } // namespace detail
+ template <typename T>
+ struct range : decltype(detail::range_concept::test<T>(0)) {
+ };
+
+ template <typename T>
+ struct forwarding_range
+ : std::integral_constant<bool,
+ range<T>::value &&
+ detail::range_impl<T>::value> {
+ };
+
+ // typedefs
+ template <typename R>
+ using iterator_t =
+ typename std::enable_if<range<R>::value,
+ decltype(::scn::custom_ranges::begin(
+ SCN_DECLVAL(R&)))>::type;
+ template <typename R>
+ using sentinel_t =
+ typename std::enable_if<range<R>::value,
+ decltype(::scn::custom_ranges::end(
+ SCN_DECLVAL(R&)))>::type;
+ template <typename R>
+ using range_difference_t =
+ typename std::enable_if<range<R>::value,
+ iter_difference_t<iterator_t<R>>>::type;
+ template <typename R>
+ using range_value_t =
+ typename std::enable_if<range<R>::value,
+ iter_value_t<iterator_t<R>>>::type;
+ template <typename R>
+ using range_reference_t =
+ typename std::enable_if<range<R>::value,
+ iter_reference_t<iterator_t<R>>>::type;
+
+ // view
+ struct view_base {
+ };
+
+ namespace detail {
+ template <typename>
+ struct is_std_non_view : std::false_type {
+ };
+ template <typename T>
+ struct is_std_non_view<std::initializer_list<T>> : std::true_type {
+ };
+ template <typename T>
+ struct enable_view_helper
+ : std::conditional<
+ std::is_base_of<view_base, T>::value,
+ std::true_type,
+ typename std::conditional<
+ is_std_non_view<T>::value,
+ std::false_type,
+ typename std::conditional<
+ range<T>::value && range<const T>::value,
+ std::is_same<range_reference_t<T>,
+ range_reference_t<const T>>,
+ std::true_type>::type>::type>::type {
+ };
+ template <typename T>
+ struct view_impl
+ : std::integral_constant<
+ bool,
+ std::is_copy_constructible<T>::value &&
+ std::is_default_constructible<T>::value &&
+ detail::enable_view_helper<T>::value> {
+ };
+ } // namespace detail
+ template <typename T>
+ struct view : std::conditional<range<T>::value,
+ detail::view_impl<T>,
+ std::false_type>::type {
+ };
+
+ // data
+ template <typename P>
+ struct _is_object_pointer
+ : std::integral_constant<
+ bool,
+ std::is_pointer<P>::value &&
+ std::is_object<detail::test_t<iter_value_t, P>>::value> {
+ };
+
+ namespace _data {
+ struct fn {
+ private:
+ template <typename CharT, typename Traits, typename Allocator>
+ static constexpr auto impl(
+ std::basic_string<CharT, Traits, Allocator>& str,
+ detail::priority_tag<2>) noexcept -> typename std::
+ basic_string<CharT, Traits, Allocator>::pointer
+ {
+ return std::addressof(*str.begin());
+ }
+ template <typename CharT, typename Traits, typename Allocator>
+ static constexpr auto impl(
+ const std::basic_string<CharT, Traits, Allocator>& str,
+ detail::priority_tag<2>) noexcept -> typename std::
+ basic_string<CharT, Traits, Allocator>::const_pointer
+ {
+ return std::addressof(*str.begin());
+ }
+ template <typename CharT, typename Traits, typename Allocator>
+ static constexpr auto impl(
+ std::basic_string<CharT, Traits, Allocator>&& str,
+ detail::priority_tag<2>) noexcept -> typename std::
+ basic_string<CharT, Traits, Allocator>::pointer
+ {
+ return std::addressof(*str.begin());
+ }
+
+ template <typename T,
+ typename D =
+ decltype(::scn::custom_ranges::detail::decay_copy(
+ SCN_DECLVAL(T&).data()))>
+ static constexpr auto
+ impl(T& t, detail::priority_tag<1>) noexcept(noexcept(
+ ::scn::custom_ranges::detail::decay_copy(t.data()))) ->
+ typename std::enable_if<_is_object_pointer<D>::value,
+ D>::type
+ {
+ return ::scn::custom_ranges::detail::decay_copy(t.data());
+ }
+
+ template <typename T>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<0>) noexcept(
+ noexcept(::scn::custom_ranges::begin(SCN_FWD(t)))) ->
+ typename std::enable_if<
+ _is_object_pointer<decltype(::scn::custom_ranges::begin(
+ SCN_FWD(t)))>::value,
+ decltype(::scn::custom_ranges::begin(SCN_FWD(t)))>::type
+ {
+ return ::scn::custom_ranges::begin(SCN_FWD(t));
+ }
+
+ public:
+ template <typename T>
+ constexpr auto operator()(T&& t) const noexcept(
+ noexcept(fn::impl(SCN_FWD(t), detail::priority_tag<2>{})))
+ -> decltype(fn::impl(SCN_FWD(t), detail::priority_tag<2>{}))
+ {
+ return fn::impl(SCN_FWD(t), detail::priority_tag<2>{});
+ }
+ };
+ } // namespace _data
+ namespace {
+ constexpr auto& data = detail::static_const<_data::fn>::value;
+ }
+
+ // size
+ template <typename>
+ struct disable_sized_range : std::false_type {
+ };
+
+ namespace _size {
+ template <typename T>
+ void size(T&&) = delete;
+ template <typename T>
+ void size(T&) = delete;
+
+ struct fn {
+ private:
+ template <typename T, std::size_t N>
+ static constexpr std::size_t impl(
+ const T(&&)[N],
+ detail::priority_tag<3>) noexcept
+ {
+ return N;
+ }
+
+ template <typename T, std::size_t N>
+ static constexpr std::size_t impl(
+ const T (&)[N],
+ detail::priority_tag<3>) noexcept
+ {
+ return N;
+ }
+
+ template <typename T,
+ typename I =
+ decltype(::scn::custom_ranges::detail::decay_copy(
+ SCN_DECLVAL(T).size()))>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<2>) noexcept(
+ noexcept(::scn::custom_ranges::detail::decay_copy(
+ SCN_FWD(t).size()))) ->
+ typename std::enable_if<
+ std::is_integral<I>::value &&
+ !disable_sized_range<
+ detail::remove_cvref_t<T>>::value,
+ I>::type
+ {
+ return ::scn::custom_ranges::detail::decay_copy(
+ SCN_FWD(t).size());
+ }
+
+ template <typename T,
+ typename I =
+ decltype(::scn::custom_ranges::detail::decay_copy(
+ size(SCN_DECLVAL(T))))>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<1>) noexcept(noexcept(
+ ::scn::custom_ranges::detail::decay_copy(size(SCN_FWD(t)))))
+ -> typename std::enable_if<
+ std::is_integral<I>::value &&
+ !disable_sized_range<
+ detail::remove_cvref_t<T>>::value,
+ I>::type
+ {
+ return ::scn::custom_ranges::detail::decay_copy(
+ size(SCN_FWD(t)));
+ }
+
+ template <typename T,
+ typename I = decltype(::scn::custom_ranges::begin(
+ SCN_DECLVAL(T))),
+ typename S = decltype(::scn::custom_ranges::end(
+ SCN_DECLVAL(T))),
+ typename D =
+ decltype(::scn::custom_ranges::detail::decay_copy(
+ SCN_DECLVAL(S) - SCN_DECLVAL(I)))>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<0>) noexcept(
+ noexcept(::scn::custom_ranges::detail::decay_copy(
+ ::scn::custom_ranges::end(t) -
+ ::scn::custom_ranges::begin(t)))) ->
+ typename std::enable_if<
+ !std::is_array<detail::remove_cvref_t<T>>::value,
+ D>::type
+ {
+ return ::scn::custom_ranges::detail::decay_copy(
+ ::scn::custom_ranges::end(t) -
+ ::scn::custom_ranges::begin(t));
+ }
+
+ public:
+ template <typename T>
+ constexpr auto operator()(T&& t) const noexcept(
+ noexcept(fn::impl(SCN_FWD(t), detail::priority_tag<3>{})))
+ -> decltype(fn::impl(SCN_FWD(t), detail::priority_tag<3>{}))
+ {
+ return fn::impl(SCN_FWD(t), detail::priority_tag<3>{});
+ }
+ };
+ } // namespace _size
+ namespace {
+ constexpr auto& size = detail::static_const<_size::fn>::value;
+ }
+
+ // empty
+ namespace _empty_ns {
+ struct fn {
+ private:
+ template <typename T>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<2>) noexcept(
+ noexcept((bool(SCN_FWD(t).empty()))))
+ -> decltype((bool(SCN_FWD(t).empty())))
+ {
+ return bool((SCN_FWD(t).empty()));
+ }
+ template <typename T>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<1>) noexcept(
+ noexcept(::scn::custom_ranges::size(SCN_FWD(t)) == 0))
+ -> decltype(::scn::custom_ranges::size(SCN_FWD(t)) == 0)
+ {
+ return ::scn::custom_ranges::size(SCN_FWD(t)) == 0;
+ }
+
+ template <typename T,
+ typename I = decltype(::scn::custom_ranges::begin(
+ SCN_DECLVAL(T)))>
+ static constexpr auto
+ impl(T&& t, detail::priority_tag<0>) noexcept(
+ noexcept(::scn::custom_ranges::begin(t) ==
+ ::scn::custom_ranges::end(t)))
+ -> decltype(::scn::custom_ranges::begin(t) ==
+ ::scn::custom_ranges::end(t))
+ {
+ return ::scn::custom_ranges::begin(t) ==
+ ::scn::custom_ranges::end(t);
+ }
+
+ public:
+ template <typename T>
+ constexpr auto operator()(T&& t) const noexcept(
+ noexcept(fn::impl(SCN_FWD(t), detail::priority_tag<2>{})))
+ -> decltype(fn::impl(SCN_FWD(t), detail::priority_tag<2>{}))
+ {
+ return fn::impl(SCN_FWD(t), detail::priority_tag<2>{});
+ }
+ };
+ } // namespace _empty_ns
+ namespace {
+ constexpr auto& empty = detail::static_const<_empty_ns::fn>::value;
+ }
+
+ // sized_range
+ namespace detail {
+ struct sized_range_concept {
+ template <typename T>
+ auto _test_requires(T& t)
+ -> decltype(::scn::custom_ranges::size(t));
+ };
+ } // namespace detail
+ template <typename T>
+ struct sized_range
+ : std::integral_constant<
+ bool,
+ range<T>::value &&
+ !disable_sized_range<detail::remove_cvref_t<T>>::value &&
+ detail::_requires<detail::sized_range_concept,
+ T>::value> {
+ };
+
+ // contiguous_range
+ namespace detail {
+ struct contiguous_range_concept {
+ template <typename>
+ static auto test(long) -> std::false_type;
+ template <typename T>
+ static auto test(int) -> typename std::enable_if<
+ _requires<contiguous_range_concept, T>::value,
+ std::true_type>::type;
+
+ template <typename T>
+ auto _test_requires(T& t)
+ -> decltype(requires_expr<std::is_same<
+ decltype(::scn::custom_ranges::data(t)),
+ typename std::add_pointer<
+ range_reference_t<T>>::type>::value>{});
+ };
+ } // namespace detail
+ template <typename T>
+ struct contiguous_range
+ : decltype(detail::contiguous_range_concept::test<T>(0)) {
+ };
+
+ // subrange
+ template <typename D>
+ class view_interface : public view_base {
+ static_assert(std::is_class<D>::value, "");
+ static_assert(
+ std::is_same<D, typename std::remove_cv<D>::type>::value,
+ "");
+
+ private:
+ SCN_CONSTEXPR14 D& derived() noexcept
+ {
+ return static_cast<D&>(*this);
+ }
+ constexpr D& derived() const noexcept
+ {
+ return static_cast<const D&>(*this);
+ }
+
+ public:
+ SCN_NODISCARD SCN_CONSTEXPR14 bool empty()
+ {
+ return ::scn::custom_ranges::begin(derived()) ==
+ ::scn::custom_ranges::end(derived());
+ }
+ SCN_NODISCARD constexpr bool empty() const
+ {
+ return ::scn::custom_ranges::begin(derived()) ==
+ ::scn::custom_ranges::end(derived());
+ }
+
+ template <typename R = D,
+ typename = decltype(::scn::custom_ranges::empty(
+ SCN_DECLVAL(R&)))>
+ SCN_CONSTEXPR14 explicit operator bool()
+ {
+ return !::scn::custom_ranges::empty(derived());
+ }
+ template <typename R = D,
+ typename = decltype(::scn::custom_ranges::empty(
+ SCN_DECLVAL(const R&)))>
+ constexpr explicit operator bool() const
+ {
+ return !::scn::custom_ranges::empty(derived());
+ }
+
+ template <typename R = D,
+ typename std::enable_if<
+ contiguous_range<R>::value>::type* = nullptr>
+ auto data() -> decltype(std::addressof(
+ *::scn::custom_ranges::begin(static_cast<R&>(*this))))
+ {
+ return ::scn::custom_ranges::empty(derived())
+ ? nullptr
+ : std::addressof(
+ *::scn::custom_ranges::begin(derived()));
+ }
+ template <typename R = D,
+ typename std::enable_if<
+ contiguous_range<const R>::value>::type* = nullptr>
+ auto data() const -> decltype(std::addressof(
+ *::scn::custom_ranges::begin(static_cast<const R&>(*this))))
+ {
+ return ::scn::custom_ranges::empty(derived())
+ ? nullptr
+ : std::addressof(
+ *::scn::custom_ranges::begin(derived()));
+ }
+
+ template <typename R = D,
+ typename std::enable_if<
+ range<R>::value &&
+ sized_sentinel_for<sentinel_t<R>, iterator_t<R>>::
+ value>::type* = nullptr>
+ SCN_CONSTEXPR14 auto size()
+ -> decltype(::scn::custom_ranges::end(static_cast<R&>(*this)) -
+ ::scn::custom_ranges::begin(static_cast<R&>(*this)))
+ {
+ return ::scn::custom_ranges::end(derived()) -
+ ::scn::custom_ranges::begin(derived());
+ }
+
+ template <
+ typename R = D,
+ typename std::enable_if<
+ range<const R>::value &&
+ sized_sentinel_for<sentinel_t<const R>,
+ iterator_t<const R>>::value>::type* =
+ nullptr>
+ constexpr auto size() const
+ -> decltype(::scn::custom_ranges::end(
+ static_cast<const R&>(*this)) -
+ ::scn::custom_ranges::begin(
+ static_cast<const R&>(*this)))
+ {
+ return ::scn::custom_ranges::end(derived()) -
+ ::scn::custom_ranges::begin(derived());
+ }
+ };
+
+ enum class subrange_kind : bool { unsized, sized };
+
+ namespace detail {
+ template <typename I, typename S>
+ struct default_subrange_kind
+ : std::integral_constant<subrange_kind,
+ sized_sentinel_for<S, I>::value
+ ? subrange_kind::sized
+ : subrange_kind::unsized> {
+ };
+ } // namespace detail
+
+ namespace _subrange {
+ template <typename I,
+ typename S = I,
+ subrange_kind = scn::custom_ranges::detail::
+ default_subrange_kind<I, S>::value>
+ class subrange;
+ } // namespace _subrange
+
+ using _subrange::subrange;
+
+ namespace detail {
+ struct pair_like_concept {
+ template <typename>
+ static auto test(long) -> std::false_type;
+ template <typename T,
+ typename = typename std::tuple_size<T>::type>
+ static auto test(int) -> typename std::enable_if<
+ _requires<pair_like_concept, T>::value,
+ std::true_type>::type;
+
+ template <typename T>
+ auto _test_requires(T t) -> decltype(
+ requires_expr<
+ std::is_base_of<std::integral_constant<std::size_t, 2>,
+ std::tuple_size<T>>::value>{},
+ std::declval<std::tuple_element<
+ 0,
+ typename std::remove_const<T>::type>>(),
+ std::declval<std::tuple_element<
+ 1,
+ typename std::remove_const<T>::type>>(),
+ requires_expr<std::is_convertible<
+ decltype(std::get<0>(t)),
+ const std::tuple_element<0, T>&>::value>{},
+ requires_expr<std::is_convertible<
+ decltype(std::get<1>(t)),
+ const std::tuple_element<1, T>&>::value>{});
+ };
+ template <typename T>
+ struct pair_like
+ : std::integral_constant<
+ bool,
+ !std::is_reference<T>::value &&
+ decltype(pair_like_concept::test<T>(0))::value> {
+ };
+
+ struct pair_like_convertible_to_concept {
+ template <typename T, typename U, typename V>
+ auto _test_requires(T&& t) -> decltype(
+ requires_expr<
+ std::is_convertible<decltype(std::get<0>(SCN_FWD(t))),
+ U>::value>{},
+ requires_expr<
+ std::is_convertible<decltype(std::get<1>(SCN_FWD(t))),
+ V>::value>{});
+ };
+ template <typename T, typename U, typename V>
+ struct pair_like_convertible_to
+ : std::integral_constant<
+ bool,
+ !range<T>::value &&
+ pair_like<
+ typename std::remove_reference<T>::type>::value &&
+ _requires<pair_like_convertible_to_concept, T, U, V>::
+ value> {
+ };
+ template <typename T, typename U, typename V>
+ struct pair_like_convertible_from
+ : std::integral_constant<
+ bool,
+ !range<T>::value &&
+ pair_like<
+ typename std::remove_reference<T>::type>::value &&
+ std::is_constructible<T, U, V>::value> {
+ };
+
+ struct iterator_sentinel_pair_concept {
+ template <typename>
+ static auto test(long) -> std::false_type;
+ template <typename T>
+ static auto test(int) -> typename std::enable_if<
+ !range<T>::value && pair_like<T>::value &&
+ sentinel_for<
+ typename std::tuple_element<1, T>::type,
+ typename std::tuple_element<0, T>::type>::value,
+ std::true_type>::type;
+ };
+ template <typename T>
+ struct iterator_sentinel_pair
+ : decltype(iterator_sentinel_pair_concept::test<T>(0)) {
+ };
+
+ template <typename I, typename S, bool StoreSize = false>
+ struct subrange_data {
+ constexpr subrange_data() = default;
+ constexpr subrange_data(I&& b, S&& e)
+ : begin(SCN_MOVE(b)), end(SCN_MOVE(e))
+ {
+ }
+ template <bool Dependent = true>
+ constexpr subrange_data(
+ I&& b,
+ S&& e,
+ typename std::enable_if<Dependent,
+ iter_difference_t<I>>::type)
+ : begin(SCN_MOVE(b)), end(SCN_MOVE(e))
+ {
+ }
+
+ constexpr iter_difference_t<I> get_size() const
+ {
+ return distance(begin, end);
+ }
+
+ I begin{};
+ S end{};
+ };
+
+ template <typename I, typename S>
+ struct subrange_data<I, S, true> {
+ constexpr subrange_data() = default;
+ constexpr subrange_data(I&& b, S&& e, iter_difference_t<I> s)
+ : begin(SCN_MOVE(b)), end(SCN_MOVE(e)), size(s)
+ {
+ }
+
+ constexpr iter_difference_t<I> get_size() const
+ {
+ return size;
+ }
+
+ I begin{};
+ S end{};
+ iter_difference_t<I> size{0};
+ };
+
+ template <typename R, typename I, typename S, subrange_kind K>
+ auto subrange_range_constructor_constraint_helper_fn(long)
+ -> std::false_type;
+
+ template <typename R, typename I, typename S, subrange_kind K>
+ auto subrange_range_constructor_constraint_helper_fn(int) ->
+ typename std::enable_if<
+ forwarding_range<R>::value &&
+ std::is_convertible<iterator_t<R>, I>::value &&
+ std::is_convertible<sentinel_t<R>, S>::value,
+ std::true_type>::type;
+
+ template <typename R, typename I, typename S, subrange_kind K>
+ struct subrange_range_constructor_constraint_helper
+ : decltype(subrange_range_constructor_constraint_helper_fn<R,
+ I,
+ S,
+ K>(
+ 0)) {
+ };
+
+ template <typename R>
+ constexpr subrange_kind subrange_deduction_guide_helper()
+ {
+ return (sized_range<R>::value ||
+ sized_sentinel_for<sentinel_t<R>, iterator_t<R>>::value)
+ ? subrange_kind::sized
+ : subrange_kind::unsized;
+ }
+
+ template <typename T, typename U>
+ struct not_same_as : std::integral_constant<
+ bool,
+ !std::is_same<remove_cvref_t<T>,
+ remove_cvref_t<U>>::value> {
+ };
+ } // namespace detail
+
+ namespace _subrange {
+ template <typename I, typename S, subrange_kind K>
+ class subrange : public view_interface<subrange<I, S, K>> {
+ static_assert(sentinel_for<S, I>::value, "");
+ static_assert(K == subrange_kind::sized ||
+ !sized_sentinel_for<S, I>::value,
+ "");
+
+ static constexpr bool _store_size =
+ K == subrange_kind::sized &&
+ !sized_sentinel_for<S, I>::value;
+
+ public:
+ using iterator = I;
+ using sentinel = S;
+
+ subrange() = default;
+
+ template <bool SS = _store_size,
+ typename std::enable_if<!SS>::type* = nullptr>
+ SCN_CONSTEXPR14 subrange(I i, S s)
+ : m_data{SCN_MOVE(i), SCN_MOVE(s)}
+ {
+ }
+ template <bool Dependent = true,
+ subrange_kind KK = K,
+ typename std::enable_if<
+ KK == subrange_kind::sized>::type* = nullptr>
+ SCN_CONSTEXPR14 subrange(
+ I i,
+ S s,
+ typename std::enable_if<Dependent,
+ iter_difference_t<I>>::type n)
+ : m_data{SCN_MOVE(i), SCN_MOVE(s), n}
+ {
+ }
+
+ constexpr I begin() const noexcept
+ {
+ return m_data.begin;
+ }
+
+ constexpr S end() const noexcept
+ {
+ return m_data.end;
+ }
+
+ SCN_NODISCARD constexpr bool empty() const noexcept
+ {
+ return m_data.begin == m_data.end;
+ }
+
+ template <subrange_kind KK = K,
+ typename std::enable_if<
+ KK == subrange_kind::sized>::type* = nullptr>
+ constexpr iter_difference_t<I> size() const noexcept
+ {
+ return m_data.get_size();
+ }
+
+ private:
+ detail::subrange_data<I, S, _store_size> m_data{};
+ };
+
+ template <typename I, typename S, subrange_kind K>
+ I begin(subrange<I, S, K>&& r) noexcept
+ {
+ return r.begin();
+ }
+ template <typename I, typename S, subrange_kind K>
+ S end(subrange<I, S, K>&& r) noexcept
+ {
+ return r.end();
+ }
+ } // namespace _subrange
+
+ namespace detail {
+ template <std::size_t N>
+ struct subrange_get_impl;
+ template <>
+ struct subrange_get_impl<0> {
+ template <typename I, typename S, subrange_kind K>
+ static auto get(const subrange<I, S, K>& s)
+ -> decltype(s.begin())
+ {
+ return s.begin();
+ }
+ };
+ template <>
+ struct subrange_get_impl<1> {
+ template <typename I, typename S, subrange_kind K>
+ static auto get(const subrange<I, S, K>& s) -> decltype(s.end())
+ {
+ return s.end();
+ }
+ };
+ } // namespace detail
+
+ template <std::size_t N,
+ typename I,
+ typename S,
+ subrange_kind K,
+ typename std::enable_if<(N < 2)>::type* = nullptr>
+ auto get(const subrange<I, S, K>& s)
+ -> decltype(detail::subrange_get_impl<N>::get(s))
+ {
+ return detail::subrange_get_impl<N>::get(s);
+ }
+
+ // reconstructible_range
+ template <typename R>
+ struct pair_reconstructible_range
+ : std::integral_constant<
+ bool,
+ range<R>::value &&
+ forwarding_range<
+ typename std::remove_reference<R>::type>::value &&
+ std::is_constructible<R, iterator_t<R>, sentinel_t<R>>::
+ value> {
+ };
+ template <typename R>
+ struct reconstructible_range
+ : std::integral_constant<
+ bool,
+ range<R>::value &&
+ forwarding_range<
+ typename std::remove_reference<R>::type>::value &&
+ std::is_constructible<
+ R,
+ subrange<iterator_t<R>, sentinel_t<R>>>::value> {
+ };
+ } // namespace custom_ranges
+
+ namespace polyfill_2a {
+ // bidir iterator
+ namespace detail {
+ struct bidirectional_iterator_concept {
+ template <typename I>
+ auto _test_requires(I i)
+ -> decltype(custom_ranges::detail::requires_expr<
+ std::is_same<decltype(i--), I>::value>{});
+ template <typename>
+ static auto test(long) -> std::false_type;
+ template <typename I>
+ static auto test(int) -> typename std::enable_if<
+ std::is_base_of<
+ custom_ranges::bidirectional_iterator_tag,
+ custom_ranges::iterator_category_t<I>>::value &&
+ custom_ranges::detail::
+ _requires<bidirectional_iterator_concept, I>::value,
+ std::true_type>::type;
+ };
+ } // namespace detail
+ template <typename I>
+ struct bidirectional_iterator
+ : decltype(detail::bidirectional_iterator_concept::test<I>(0)) {
+ };
+
+ // random access iterator
+ namespace detail {
+ struct random_access_iterator_concept {
+ template <typename I>
+ auto _test_requires(I i,
+ const I j,
+ const custom_ranges::iter_difference_t<I> n)
+ -> decltype(valid_expr(
+ j + n,
+ custom_ranges::detail::requires_expr<
+ std::is_same<decltype(j + n), I>::value>{},
+ n + j,
+#ifndef _MSC_VER
+ custom_ranges::detail::requires_expr<
+ std::is_same<decltype(n + j), I>::value>{},
+#endif
+ j - n,
+ custom_ranges::detail::requires_expr<
+ std::is_same<decltype(j - n), I>::value>{},
+ j[n],
+ custom_ranges::detail::requires_expr<std::is_same<
+ decltype(j[n]),
+ custom_ranges::iter_reference_t<I>>::value>{},
+ custom_ranges::detail::requires_expr<
+ std::is_convertible<decltype(i < j),
+ bool>::value>{}));
+ template <typename>
+ static auto test(long) -> std::false_type;
+ template <typename I>
+ static auto test(int) -> typename std::enable_if<
+ bidirectional_iterator<I>::value &&
+ std::is_base_of<
+ custom_ranges::random_access_iterator_tag,
+ custom_ranges::iterator_category_t<I>>::value &&
+ custom_ranges::sized_sentinel_for<I, I>::value &&
+ custom_ranges::detail::
+ _requires<random_access_iterator_concept, I>::value,
+ std::true_type>::type;
+ };
+ } // namespace detail
+ template <typename I>
+ struct random_access_iterator
+ : decltype(detail::random_access_iterator_concept::test<I>(0)) {
+ };
+ } // namespace polyfill_2a
+
+ namespace custom_ranges {
+ // advance
+ namespace _advance {
+ struct fn {
+ private:
+ template <typename T>
+ static constexpr T abs(T t)
+ {
+ return t < T{0} ? -t : t;
+ }
+
+ template <
+ typename R,
+ typename std::enable_if<polyfill_2a::random_access_iterator<
+ R>::value>::type* = nullptr>
+ static SCN_CONSTEXPR14 void impl(R& r, iter_difference_t<R> n)
+ {
+ r += n;
+ }
+
+ template <
+ typename I,
+ typename std::enable_if<
+ polyfill_2a::bidirectional_iterator<I>::value &&
+ !polyfill_2a::random_access_iterator<I>::value>::type* =
+ nullptr>
+ static SCN_CONSTEXPR14 void impl(I& i, iter_difference_t<I> n)
+ {
+ constexpr auto zero = iter_difference_t<I>{0};
+
+ if (n > zero) {
+ while (n-- > zero) {
+ ++i;
+ }
+ }
+ else {
+ while (n++ < zero) {
+ --i;
+ }
+ }
+ }
+
+ template <
+ typename I,
+ typename std::enable_if<
+ !polyfill_2a::bidirectional_iterator<I>::value>::type* =
+ nullptr>
+ static SCN_CONSTEXPR14 void impl(I& i, iter_difference_t<I> n)
+ {
+ while (n-- > iter_difference_t<I>{0}) {
+ ++i;
+ }
+ }
+
+ template <
+ typename I,
+ typename S,
+ typename std::enable_if<
+ std::is_assignable<I&, S>::value>::type* = nullptr>
+ static SCN_CONSTEXPR14 void impl(I& i,
+ S bound,
+ detail::priority_tag<2>)
+ {
+ i = SCN_MOVE(bound);
+ }
+
+ template <typename I,
+ typename S,
+ typename std::enable_if<
+ sized_sentinel_for<S, I>::value>::type* = nullptr>
+ static SCN_CONSTEXPR14 void impl(I& i,
+ S bound,
+ detail::priority_tag<1>)
+ {
+ fn::impl(i, bound - i);
+ }
+
+ template <typename I, typename S>
+ static SCN_CONSTEXPR14 void impl(I& i,
+ S bound,
+ detail::priority_tag<0>)
+ {
+ while (i != bound) {
+ ++i;
+ }
+ }
+
+ template <typename I,
+ typename S,
+ typename std::enable_if<
+ sized_sentinel_for<S, I>::value>::type* = nullptr>
+ static SCN_CONSTEXPR14 auto impl(I& i,
+ iter_difference_t<I> n,
+ S bound)
+ -> iter_difference_t<I>
+ {
+ if (fn::abs(n) >= fn::abs(bound - i)) {
+ auto dist = bound - i;
+ fn::impl(i, bound, detail::priority_tag<2>{});
+ return dist;
+ }
+ else {
+ fn::impl(i, n);
+ return n;
+ }
+ }
+
+ template <
+ typename I,
+ typename S,
+ typename std::enable_if<
+ polyfill_2a::bidirectional_iterator<I>::value &&
+ !sized_sentinel_for<S, I>::value>::type* = nullptr>
+ static SCN_CONSTEXPR14 auto impl(I& i,
+ iter_difference_t<I> n,
+ S bound)
+ -> iter_difference_t<I>
+ {
+ constexpr iter_difference_t<I> zero{0};
+ iter_difference_t<I> counter{0};
+
+ if (n < zero) {
+ do {
+ --i;
+ --counter;
+ } while (++n < zero && i != bound);
+ }
+ else {
+ while (n-- > zero && i != bound) {
+ ++i;
+ ++counter;
+ }
+ }
+
+ return counter;
+ }
+
+ template <
+ typename I,
+ typename S,
+ typename std::enable_if<
+ !polyfill_2a::bidirectional_iterator<I>::value &&
+ !sized_sentinel_for<S, I>::value>::type* = nullptr>
+ static SCN_CONSTEXPR14 auto impl(I& i,
+ iter_difference_t<I> n,
+ S bound)
+ -> iter_difference_t<I>
+ {
+ constexpr iter_difference_t<I> zero{0};
+ iter_difference_t<I> counter{0};
+
+ while (n-- > zero && i != bound) {
+ ++i;
+ ++counter;
+ }
+
+ return counter;
+ }
+
+ public:
+ template <typename I>
+ SCN_CONSTEXPR14 void operator()(I& i,
+ iter_difference_t<I> n) const
+ {
+ fn::impl(i, n);
+ }
+
+ template <typename I,
+ typename S,
+ typename std::enable_if<
+ sentinel_for<S, I>::value>::type* = nullptr>
+ SCN_CONSTEXPR14 void operator()(I& i, S bound) const
+ {
+ fn::impl(i, bound, detail::priority_tag<2>{});
+ }
+
+ template <typename I,
+ typename S,
+ typename std::enable_if<
+ sentinel_for<S, I>::value>::type* = nullptr>
+ SCN_CONSTEXPR14 iter_difference_t<I>
+ operator()(I& i, iter_difference_t<I> n, S bound) const
+ {
+ return n - fn::impl(i, n, bound);
+ }
+ };
+ } // namespace _advance
+ namespace {
+ constexpr auto& advance = detail::static_const<_advance::fn>::value;
+ }
+
+ // distance
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wnoexcept")
+ namespace _distance {
+ struct fn {
+ private:
+ template <typename I, typename S>
+ static SCN_CONSTEXPR14 auto impl(I i,
+ S s) noexcept(noexcept(s - i))
+ -> typename std::enable_if<sized_sentinel_for<S, I>::value,
+ iter_difference_t<I>>::type
+ {
+ return s - i;
+ }
+
+ template <typename I, typename S>
+ static SCN_CONSTEXPR14 auto impl(I i, S s) noexcept(
+ noexcept(i != s, ++i, ++SCN_DECLVAL(iter_difference_t<I>&)))
+ -> typename std::enable_if<!sized_sentinel_for<S, I>::value,
+ iter_difference_t<I>>::type
+ {
+ iter_difference_t<I> counter{0};
+ while (i != s) {
+ ++i;
+ ++counter;
+ }
+ return counter;
+ }
+
+ template <typename R>
+ static SCN_CONSTEXPR14 auto impl(R&& r) noexcept(
+ noexcept(::scn::custom_ranges::size(r))) ->
+ typename std::enable_if<
+ sized_range<R>::value,
+ iter_difference_t<iterator_t<R>>>::type
+ {
+ return static_cast<iter_difference_t<iterator_t<R>>>(
+ ::scn::custom_ranges::size(r));
+ }
+
+ template <typename R>
+ static SCN_CONSTEXPR14 auto impl(R&& r) noexcept(
+ noexcept(fn::impl(::scn::custom_ranges::begin(r),
+ ::scn::custom_ranges::end(r)))) ->
+ typename std::enable_if<
+ !sized_range<R>::value,
+ iter_difference_t<iterator_t<R>>>::type
+ {
+ return fn::impl(::scn::custom_ranges::begin(r),
+ ::scn::custom_ranges::end(r));
+ }
+
+ public:
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 auto operator()(I first, S last) const
+ noexcept(noexcept(fn::impl(SCN_MOVE(first),
+ SCN_MOVE(last)))) ->
+ typename std::enable_if<sentinel_for<S, I>::value,
+ iter_difference_t<I>>::type
+ {
+ return fn::impl(SCN_MOVE(first), SCN_MOVE(last));
+ }
+
+ template <typename R>
+ SCN_CONSTEXPR14 auto operator()(R&& r) const
+ noexcept(noexcept(fn::impl(SCN_FWD(r)))) ->
+ typename std::enable_if<
+ range<R>::value,
+ iter_difference_t<iterator_t<R>>>::type
+ {
+ return fn::impl(SCN_FWD(r));
+ }
+ };
+ } // namespace _distance
+ namespace {
+ constexpr auto& distance =
+ detail::static_const<_distance::fn>::value;
+ }
+ SCN_GCC_POP // -Wnoexcept
+ } // namespace custom_ranges
+
+ namespace polyfill_2a {
+ template <typename T>
+ using iter_value_t = ::scn::custom_ranges::iter_value_t<T>;
+ template <typename T>
+ using iter_reference_t = ::scn::custom_ranges::iter_reference_t<T>;
+ template <typename T>
+ using iter_difference_t = ::scn::custom_ranges::iter_difference_t<T>;
+ } // namespace polyfill_2a
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+namespace std {
+ template <typename I, typename S, ::scn::custom_ranges::subrange_kind K>
+ struct tuple_size<::scn::custom_ranges::subrange<I, S, K>>
+ : public integral_constant<size_t, 2> {
+ };
+
+ template <typename I, typename S, ::scn::custom_ranges::subrange_kind K>
+ struct tuple_element<0, ::scn::custom_ranges::subrange<I, S, K>> {
+ using type = I;
+ };
+ template <typename I, typename S, ::scn::custom_ranges::subrange_kind K>
+ struct tuple_element<1, ::scn::custom_ranges::subrange<I, S, K>> {
+ using type = S;
+ };
+
+ using ::scn::custom_ranges::get;
+} // namespace std
+
+#define SCN_CHECK_CONCEPT(C) C::value
+
+#endif // SCN_RANGES_CUSTOM_IMPL_H
diff --git a/src/third-party/scnlib/include/scn/ranges/ranges.h b/src/third-party/scnlib/include/scn/ranges/ranges.h
new file mode 100644
index 0000000..aa70f50
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/ranges/ranges.h
@@ -0,0 +1,49 @@
+// 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_RANGES_RANGES_H
+#define SCN_RANGES_RANGES_H
+
+#include "../detail/config.h"
+
+#ifndef SCN_USE_STD_RANGES
+
+#if SCN_HAS_CONCEPTS && SCN_HAS_RANGES
+#define SCN_USE_STD_RANGES 1
+#else
+#define SCN_USE_STD_RANGES 0
+#endif
+
+#endif // !defined(SCN_USE_STD_RANGES)
+
+#if SCN_USE_STD_RANGES
+#include "std_impl.h"
+#define SCN_RANGES_NAMESPACE ::scn::std_ranges
+#else
+#include "custom_impl.h"
+#define SCN_RANGES_NAMESPACE ::scn::custom_ranges
+#endif
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace ranges = SCN_RANGES_NAMESPACE;
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_RANGES_RANGES_H
diff --git a/src/third-party/scnlib/include/scn/ranges/std_impl.h b/src/third-party/scnlib/include/scn/ranges/std_impl.h
new file mode 100644
index 0000000..abc3422
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/ranges/std_impl.h
@@ -0,0 +1,67 @@
+// 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_RANGES_STD_IMPL_H
+#define SCN_RANGES_STD_IMPL_H
+
+#include "../detail/config.h"
+
+#if SCN_HAS_CONCEPTS && SCN_HAS_RANGES
+
+SCN_GCC_PUSH
+SCN_GCC_IGNORE("-Wnoexcept")
+#include <iterator>
+#include <ranges>
+SCN_GCC_POP
+
+#include "util.h"
+
+#include "../util/string_view.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace std_ranges = ::std::ranges;
+
+ namespace polyfill_2a {
+ template <typename T>
+ using iter_value_t = ::std::iter_value_t<T>;
+ template <typename T>
+ using iter_reference_t = ::std::iter_reference_t<T>;
+ template <typename T>
+ using iter_difference_t = ::std::iter_difference_t<T>;
+
+ template <typename I>
+ concept bidirectional_iterator = std::bidirectional_iterator<I>;
+ template <typename I>
+ concept random_access_iterator = std::random_access_iterator<I>;
+ } // namespace polyfill_2a
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+namespace std::ranges {
+ template <typename CharT>
+ inline constexpr bool enable_view<::scn::basic_string_view<CharT>> = true;
+ template <typename T>
+ inline constexpr bool enable_view<::scn::span<T>> = true;
+} // namespace std
+
+#define SCN_CHECK_CONCEPT(C) C
+#endif
+
+#endif // SCN_RANGES_STD_IMPL_H
diff --git a/src/third-party/scnlib/include/scn/ranges/util.h b/src/third-party/scnlib/include/scn/ranges/util.h
new file mode 100644
index 0000000..d5954d1
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/ranges/util.h
@@ -0,0 +1,419 @@
+// 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
+//
+// The contents of this file are adapted from NanoRange.
+// https://github.com/tcbrindle/NanoRange
+// Copyright (c) 2018 Tristan Brindle
+// Distributed under the Boost Software License, Version 1.0
+
+#ifndef SCN_RANGES_UTIL_H
+#define SCN_RANGES_UTIL_H
+
+#include "../util/meta.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace custom_ranges {
+ namespace detail {
+ template <size_t N>
+ using priority_tag = ::scn::detail::priority_tag<N>;
+
+ template <typename... Ts>
+ using void_t = ::scn::detail::void_t<Ts...>;
+
+ template <typename T>
+ using static_const = ::scn::detail::static_const<T>;
+
+ template <typename T>
+ using remove_cvref_t = ::scn::detail::remove_cvref_t<T>;
+
+ template <typename T>
+ constexpr typename std::decay<T>::type decay_copy(T&& t) noexcept(
+ noexcept(static_cast<typename std::decay<T>::type>(SCN_FWD(t))))
+ {
+ return SCN_FWD(t);
+ }
+
+ struct nonesuch {
+ nonesuch() = delete;
+ nonesuch(nonesuch const&) = delete;
+ nonesuch& operator=(const nonesuch&) = delete;
+ ~nonesuch() = delete;
+ };
+
+ template <typename Void,
+ template <class...>
+ class Trait,
+ typename... Args>
+ struct test {
+ using type = nonesuch;
+ };
+
+ template <template <class...> class Trait, typename... Args>
+ struct test<void_t<Trait<Args...>>, Trait, Args...> {
+ using type = Trait<Args...>;
+ };
+
+ template <template <class...> class Trait, typename... Args>
+ using test_t = typename test<void, Trait, Args...>::type;
+
+ template <typename Void,
+ template <class...>
+ class AliasT,
+ typename... Args>
+ struct exists_helper : std::false_type {
+ };
+
+ template <template <class...> class AliasT, typename... Args>
+ struct exists_helper<void_t<AliasT<Args...>>, AliasT, Args...>
+ : std::true_type {
+ };
+
+ template <template <class...> class AliasT, typename... Args>
+ struct exists : exists_helper<void, AliasT, Args...> {
+ };
+
+ template <typename R,
+ typename... Args,
+ typename = decltype(&R::template _test_requires<Args...>)>
+ auto test_requires(R&) -> void;
+
+ template <typename R, typename... Args>
+ using test_requires_t =
+ decltype(test_requires<R, Args...>(SCN_DECLVAL(R&)));
+
+ template <typename R, typename... Args>
+ struct _requires : exists<test_requires_t, R, Args...> {
+ };
+
+ template <bool Expr>
+ using requires_expr = typename std::enable_if<Expr, int>::type;
+
+ template <typename...>
+ struct get_common_type;
+
+ template <typename T, typename U>
+ struct copy_cv {
+ using type = U;
+ };
+ template <typename T, typename U>
+ struct copy_cv<const T, U> {
+ using type = typename std::add_const<U>::type;
+ };
+ template <typename T, typename U>
+ struct copy_cv<volatile T, U> {
+ using type = typename std::add_volatile<U>::type;
+ };
+ template <typename T, typename U>
+ struct copy_cv<const volatile T, U> {
+ using type = typename std::add_cv<U>::type;
+ };
+ template <typename T, typename U>
+ using copy_cv_t = typename copy_cv<T, U>::type;
+
+ template <typename T>
+ using cref_t = typename std::add_lvalue_reference<
+ const typename std::remove_reference<T>::type>::type;
+
+ template <typename T>
+ struct rref_res {
+ using type = T;
+ };
+ template <typename T>
+ struct rref_res<T&> {
+ using type = typename std::remove_reference<T>::type&&;
+ };
+ template <typename T>
+ using rref_res_t = typename rref_res<T>::type;
+
+ template <typename T, typename U>
+ using cond_res_t =
+ decltype(SCN_DECLVAL(bool) ? std::declval<T (&)()>()()
+ : std::declval<U (&)()>()());
+
+ template <typename T, typename U>
+ struct simple_common_reference {
+ };
+
+ template <
+ typename T,
+ typename U,
+ typename C =
+ test_t<cond_res_t, copy_cv_t<T, U>&, copy_cv_t<U, T>&>>
+ struct lvalue_simple_common_reference
+ : std::enable_if<std::is_reference<C>::value, C> {
+ };
+ template <typename T, typename U>
+ using lvalue_scr_t =
+ typename lvalue_simple_common_reference<T, U>::type;
+ template <typename T, typename U>
+ struct simple_common_reference<T&, U&>
+ : lvalue_simple_common_reference<T, U> {
+ };
+
+ template <typename T,
+ typename U,
+ typename LCR = test_t<lvalue_scr_t, T, U>,
+ typename C = rref_res_t<LCR>>
+ struct rvalue_simple_common_reference
+ : std::enable_if<std::is_convertible<T&&, C>::value &&
+ std::is_convertible<U&&, C>::value>::type {
+ };
+ template <typename T, typename U>
+ struct simple_common_reference<T&&, U&&>
+ : rvalue_simple_common_reference<T, U> {
+ };
+
+ template <typename A,
+ typename B,
+ typename C = test_t<lvalue_scr_t, A, const B>>
+ struct mixed_simple_common_reference
+ : std::enable_if<std::is_convertible<B&&, C>::value, C>::type {
+ };
+
+ template <typename A, typename B>
+ struct simple_common_reference<A&, B&&>
+ : mixed_simple_common_reference<A, B> {
+ };
+ template <typename A, typename B>
+ struct simple_common_reference<A&&, B&>
+ : simple_common_reference<B&&, A&> {
+ };
+ template <typename T, typename U>
+ using simple_common_reference_t =
+ typename simple_common_reference<T, U>::type;
+
+ template <typename>
+ struct xref {
+ template <typename U>
+ using type = U;
+ };
+
+ template <typename A>
+ struct xref<A&> {
+ template <typename U>
+ using type = typename std::add_lvalue_reference<
+ typename xref<A>::template type<U>>::type;
+ };
+
+ template <typename A>
+ struct xref<A&&> {
+ template <typename U>
+ using type = typename std::add_rvalue_reference<
+ typename xref<A>::template type<U>>::type;
+ };
+
+ template <typename A>
+ struct xref<const A> {
+ template <typename U>
+ using type = typename std::add_const<
+ typename xref<A>::template type<U>>::type;
+ };
+
+ template <typename A>
+ struct xref<volatile A> {
+ template <typename U>
+ using type = typename std::add_volatile<
+ typename xref<A>::template type<U>>::type;
+ };
+
+ template <typename A>
+ struct xref<const volatile A> {
+ template <typename U>
+ using type = typename std::add_cv<
+ typename xref<A>::template type<U>>::type;
+ };
+
+ template <typename T,
+ typename U,
+ template <class>
+ class TQual,
+ template <class>
+ class UQual>
+ struct basic_common_reference {
+ };
+
+ template <typename...>
+ struct get_common_reference;
+ template <typename... Ts>
+ using get_common_reference_t =
+ typename get_common_reference<Ts...>::type;
+
+ template <>
+ struct get_common_reference<> {
+ };
+ template <typename T0>
+ struct get_common_reference<T0> {
+ using type = T0;
+ };
+
+ template <typename T, typename U>
+ struct has_simple_common_ref
+ : exists<simple_common_reference_t, T, U> {
+ };
+ template <typename T, typename U>
+ using basic_common_ref_t = typename basic_common_reference<
+ ::scn::detail::remove_cvref_t<T>,
+ ::scn::detail::remove_cvref_t<U>,
+ xref<T>::template type,
+ xref<U>::template type>::type;
+
+ template <typename T, typename U>
+ struct has_basic_common_ref : exists<basic_common_ref_t, T, U> {
+ };
+ template <typename T, typename U>
+ struct has_cond_res : exists<cond_res_t, T, U> {
+ };
+
+ template <typename T, typename U, typename = void>
+ struct binary_common_ref : get_common_type<T, U> {
+ };
+ template <typename T, typename U>
+ struct binary_common_ref<
+ T,
+ U,
+ typename std::enable_if<
+ has_simple_common_ref<T, U>::value>::type>
+ : simple_common_reference<T, U> {
+ };
+ template <typename T, typename U>
+ struct binary_common_ref<
+ T,
+ U,
+ typename std::enable_if<
+ has_basic_common_ref<T, U>::value &&
+ !has_simple_common_ref<T, U>::value>::type> {
+ using type = basic_common_ref_t<T, U>;
+ };
+ template <typename T, typename U>
+ struct binary_common_ref<
+ T,
+ U,
+ typename std::enable_if<
+ has_cond_res<T, U>::value &&
+ !has_basic_common_ref<T, U>::value &&
+ !has_simple_common_ref<T, U>::value>::type> {
+ using type = cond_res_t<T, U>;
+ };
+ template <typename T1, typename T2>
+ struct get_common_reference<T1, T2> : binary_common_ref<T1, T2> {
+ };
+
+ template <typename Void, typename T1, typename T2, typename... Rest>
+ struct multiple_common_reference {
+ };
+ template <typename T1, typename T2, typename... Rest>
+ struct multiple_common_reference<
+ void_t<get_common_reference_t<T1, T2>>,
+ T1,
+ T2,
+ Rest...> : get_common_reference<get_common_reference_t<T1, T2>,
+ Rest...> {
+ };
+ template <typename T1, typename T2, typename... Rest>
+ struct get_common_reference<T1, T2, Rest...>
+ : multiple_common_reference<void, T1, T2, Rest...> {
+ };
+
+ template <typename... Ts>
+ using get_common_type_t = typename get_common_type<Ts...>::type;
+
+ template <typename T, typename U>
+ struct _same_decayed
+ : std::integral_constant<
+ bool,
+ std::is_same<T, typename std::decay<T>::type>::value &&
+ std::is_same<U,
+ typename std::decay<U>::type>::value> {
+ };
+
+ template <typename T, typename U>
+ using ternary_return_t =
+ typename std::decay<decltype(false ? SCN_DECLVAL(T)
+ : SCN_DECLVAL(U))>::type;
+
+ template <typename, typename, typename = void>
+ struct binary_common_type {
+ };
+
+ template <typename T, typename U>
+ struct binary_common_type<
+ T,
+ U,
+ typename std::enable_if<!_same_decayed<T, U>::value>::type>
+ : get_common_type<typename std::decay<T>::type,
+ typename std::decay<U>::type> {
+ };
+
+ template <typename T, typename U>
+ struct binary_common_type<
+ T,
+ U,
+ typename std::enable_if<
+ _same_decayed<T, U>::value &&
+ exists<ternary_return_t, T, U>::value>::type> {
+ using type = ternary_return_t<T, U>;
+ };
+
+ template <typename T, typename U>
+ struct binary_common_type<
+ T,
+ U,
+ typename std::enable_if<
+ _same_decayed<T, U>::value &&
+ !exists<ternary_return_t, T, U>::value &&
+ exists<cond_res_t, cref_t<T>, cref_t<U>>::value>::type> {
+ using type =
+ typename std::decay<cond_res_t<cref_t<T>, cref_t<U>>>::type;
+ };
+
+ template <>
+ struct get_common_type<> {
+ };
+
+ template <typename T>
+ struct get_common_type<T> : get_common_type<T, T> {
+ };
+
+ template <typename T, typename U>
+ struct get_common_type<T, U> : binary_common_type<T, U> {
+ };
+
+ template <typename Void, typename...>
+ struct multiple_common_type {
+ };
+
+ template <typename T1, typename T2, typename... R>
+ struct multiple_common_type<void_t<get_common_type_t<T1, T2>>,
+ T1,
+ T2,
+ R...>
+ : get_common_type<get_common_type_t<T1, T2>, R...> {
+ };
+
+ template <typename T1, typename T2, typename... R>
+ struct get_common_type<T1, T2, R...>
+ : multiple_common_type<void, T1, T2, R...> {
+ };
+ } // namespace detail
+ } // namespace custom_ranges
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_RANGES_UTIL_H
diff --git a/src/third-party/scnlib/include/scn/reader/common.h b/src/third-party/scnlib/include/scn/reader/common.h
new file mode 100644
index 0000000..0f2b83b
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/reader/common.h
@@ -0,0 +1,1663 @@
+// 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_READER_COMMON_H
+#define SCN_READER_COMMON_H
+
+#include "../detail/error.h"
+#include "../detail/locale.h"
+#include "../detail/range.h"
+#include "../unicode/unicode.h"
+#include "../util/algorithm.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ // read_code_unit
+
+ namespace detail {
+ template <typename WrappedRange>
+ expected<typename WrappedRange::char_type>
+ read_code_unit_impl(WrappedRange& r, bool advance, std::true_type)
+ {
+ SCN_CLANG_PUSH
+ // clang 10 behaves weirdly
+ SCN_CLANG_IGNORE("-Wzero-as-null-pointer-constant")
+ SCN_EXPECT(r.begin() < r.end());
+ SCN_CLANG_POP
+ auto ch = *r.begin();
+ if (advance) {
+ r.advance();
+ }
+ return {ch};
+ }
+ template <typename WrappedRange>
+ expected<typename WrappedRange::char_type>
+ read_code_unit_impl(WrappedRange& r, bool advance, std::false_type)
+ {
+ SCN_EXPECT(r.begin() != r.end());
+ auto ch = *r.begin();
+ if (advance && ch) {
+ r.advance();
+ }
+ return ch;
+ }
+ } // namespace detail
+
+ /**
+ * Reads a single character (= code unit) from the range.
+ * Dereferences the begin iterator, wrapping it in an `expected` if
+ * necessary.
+ *
+ * Encoding-agnostic, doesn't care about code points, and may leave behind
+ * partial ones.
+ *
+ * \param r Range to read from
+ * \param advance If `true`, and the read was successful, the range is
+ * advanced by a single character, as if by calling `r.advance()`.
+ *
+ * \return The next character in the range, obtained as if by dereferencing
+ * the begin iterator `*r.begin()`.
+ * If `r.begin() == r.end()`, returns EOF.
+ * If `r` is direct, returns `*r.begin()` wrapped in an `expected`.
+ * If `r` is not direct, returns `*r.begin()` as-is, with any errors that
+ * may have been caused by the read.
+ */
+ template <typename WrappedRange>
+ expected<typename WrappedRange::char_type> read_code_unit(
+ WrappedRange& r,
+ bool advance = true)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+ return detail::read_code_unit_impl(
+ r, advance,
+ std::integral_constant<bool, WrappedRange::is_direct>{});
+ }
+
+ // putback_n
+
+ /// @{
+
+ /**
+ * Puts back `n` characters (= code units) into `r` as if by repeatedly
+ * calling `r.advance(-1)`.
+ *
+ * Encoding-agnostic, may leave behind partial code points.
+ *
+ * \param r Range to roll back
+ * \param n Characters to put back, must be less than or equal to the number
+ * of characters already read from `r`.
+ *
+ * \return If `r` is contiguous, will always return `error::good`.
+ * Otherwise, may return `error::unrecoverable_source_error`, if the putback
+ * fails.
+ */
+ template <
+ typename WrappedRange,
+ typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
+ error putback_n(WrappedRange& r, ranges::range_difference_t<WrappedRange> n)
+ {
+ SCN_EXPECT(n <= ranges::distance(r.begin_underlying(), r.begin()));
+ r.advance(-n);
+ return {};
+ }
+ template <
+ typename WrappedRange,
+ typename std::enable_if<!WrappedRange::is_contiguous>::type* = nullptr>
+ error putback_n(WrappedRange& r, ranges::range_difference_t<WrappedRange> n)
+ {
+ for (ranges::range_difference_t<WrappedRange> i = 0; i < n; ++i) {
+ r.advance(-1);
+ if (r.begin() == r.end()) {
+ return {error::unrecoverable_source_error, "Putback failed"};
+ }
+ }
+ return {};
+ }
+
+ /// @}
+
+ // read_code_point
+
+ /**
+ * Type returned by `read_code_point`
+ * \tparam CharT Character type of the range
+ */
+ template <typename CharT>
+ struct read_code_point_result {
+ /// Code units, may point to `writebuf` given to `read_code_point`
+ span<const CharT> chars;
+ /// Parsed code point
+ code_point cp;
+ };
+
+ namespace detail {
+ // contiguous && direct
+ template <typename CharT, typename WrappedRange>
+ expected<read_code_point_result<CharT>> read_code_point_impl(
+ WrappedRange& r,
+ span<CharT> writebuf,
+ std::true_type)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+
+ auto sbuf = r.get_buffer_and_advance(4 / sizeof(CharT));
+ if (sbuf.size() == 0) {
+ auto ret = read_code_unit(r, true);
+ if (!ret) {
+ return ret.error();
+ }
+ sbuf = writebuf.first(1);
+ writebuf[0] = ret.value();
+ }
+ int len = ::scn::get_sequence_length(sbuf[0]);
+ if (SCN_UNLIKELY(len == 0)) {
+ return error(error::invalid_encoding, "Invalid code point");
+ }
+ if (sbuf.ssize() > len) {
+ auto e = putback_n(r, sbuf.ssize() - len);
+ if (!e) {
+ return e;
+ }
+ sbuf = sbuf.first(static_cast<size_t>(len));
+ }
+ if (len == 1) {
+ // Single-char code point
+ return read_code_point_result<CharT>{sbuf.first(1),
+ make_code_point(sbuf[0])};
+ }
+ while (sbuf.ssize() < len) {
+ auto ret = read_code_unit(r, true);
+ if (!ret) {
+ auto e = putback_n(r, sbuf.ssize());
+ if (!e) {
+ return e;
+ }
+ if (ret.error().code() == error::end_of_range) {
+ return error(error::invalid_encoding,
+ "Invalid code point");
+ }
+ return ret.error();
+ }
+ sbuf = make_span(writebuf.begin(), sbuf.size() + 1);
+ writebuf[sbuf.size() - 1] = ret.value();
+ }
+
+ code_point cp{};
+ auto ret = parse_code_point(sbuf.begin(), sbuf.end(), cp);
+ if (!ret) {
+ return ret.error();
+ }
+ return read_code_point_result<CharT>{sbuf, cp};
+ }
+
+ template <typename CharT, typename WrappedRange>
+ expected<read_code_point_result<CharT>> read_code_point_impl(
+ WrappedRange& r,
+ span<CharT> writebuf,
+ std::false_type)
+ {
+ auto first = read_code_unit(r, false);
+ if (!first) {
+ return first.error();
+ }
+
+ auto len =
+ static_cast<size_t>(::scn::get_sequence_length(first.value()));
+ if (SCN_UNLIKELY(len == 0)) {
+ return error(error::invalid_encoding, "Invalid code point");
+ }
+ r.advance();
+
+ writebuf[0] = first.value();
+ if (len == 1) {
+ // Single-char code point
+ return read_code_point_result<CharT>{
+ make_span(writebuf.data(), 1),
+ make_code_point(first.value())};
+ }
+
+ size_t index = 1;
+
+ auto parse = [&]() -> expected<read_code_point_result<CharT>> {
+ code_point cp{};
+ auto ret = parse_code_point(writebuf.data(),
+ writebuf.data() + len, cp);
+ if (!ret) {
+ auto pb = putback_n(r, static_cast<std::ptrdiff_t>(len));
+ if (!pb) {
+ return pb;
+ }
+ return ret.error();
+ }
+ auto s = make_span(writebuf.data(), len);
+ return read_code_point_result<CharT>{s, cp};
+ };
+ auto advance = [&]() -> error {
+ auto ret = read_code_unit(r, false);
+ if (!ret) {
+ auto pb = putback_n(r, static_cast<std::ptrdiff_t>(index));
+ if (!pb) {
+ return pb;
+ }
+ return ret.error();
+ }
+ writebuf[index] = ret.value();
+ ++index;
+ r.advance();
+ return {};
+ };
+
+ while (index < 4) {
+ auto e = advance();
+ if (!e) {
+ return e;
+ }
+ if (index == len) {
+ return parse();
+ }
+ }
+ SCN_ENSURE(false);
+ SCN_UNREACHABLE;
+ }
+ } // namespace detail
+
+ /**
+ * Read a single Unicode code point from `r` as if by repeatedly calling
+ * `read_code_unit()`.
+ *
+ * Advances the range past the read code point. On error, rolls back the
+ * range into the state it was before calling this function, as if by
+ * calling `putback_n()`.
+ *
+ * \param r Range to read from
+ * \param writebuf Buffer to use for reading into, if necessary. `BufValueT`
+ * can be any trivial type. Must be at least 4 bytes long. May be written
+ * over.
+ *
+ * \return An instance of `read_code_point_result`, wrapped in an
+ * `expected`. `chars` contains the code units read from `r`, which may
+ * point to `writebuf`. `cp` contains the code point parsed.
+ * If `r.begin() == r.end()`, returns EOF.
+ * If `read_code_unit()` or `putback_n()` fails, returns any errors returned
+ * by it.
+ * If the code point was not encoded correctly, returns
+ * `error::invalid_encoding`.
+ */
+ template <typename WrappedRange, typename BufValueT>
+ expected<read_code_point_result<typename WrappedRange::char_type>>
+ read_code_point(WrappedRange& r, span<BufValueT> writebuf)
+ {
+ SCN_EXPECT(writebuf.size() * sizeof(BufValueT) >= 4);
+ using char_type = typename WrappedRange::char_type;
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wcast-align") // taken care of by the caller
+ return detail::read_code_point_impl<char_type>(
+ r,
+ make_span(reinterpret_cast<char_type*>(writebuf.data()),
+ writebuf.size() * sizeof(BufValueT) / sizeof(char_type)),
+ std::integral_constant<bool,
+ WrappedRange::provides_buffer_access>{});
+ SCN_GCC_POP
+ }
+
+ // read_zero_copy
+
+ /// @{
+
+ /**
+ * Reads up to `n` characters (= code units) from `r`, as if by repeatedly
+ * incrementing `r.begin()`, and returns a `span` pointing into `r`.
+ *
+ * Let `count` be `min(r.size(), n)`.
+ * Reads, and advances `r` by `count` characters.
+ * `r.begin()` is in no point dereferenced.
+ * If `r.size()` is not defined, the range is not contiguous, and an empty
+ * span is returned.
+ *
+ * \return A `span` pointing to `r`, starting from `r.begin()` and with a
+ * size of `count`.
+ * If `r.begin() == r.end()`, returns EOF.
+ * If the range does not satisfy `contiguous_range`, returns an empty
+ * `span`.
+ */
+ template <typename WrappedRange,
+ typename std::enable_if<
+ WrappedRange::provides_buffer_access>::type* = nullptr>
+ expected<span<const typename detail::extract_char_type<
+ typename WrappedRange::iterator>::type>>
+ read_zero_copy(WrappedRange& r, ranges::range_difference_t<WrappedRange> n)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+ return r.get_buffer_and_advance(static_cast<size_t>(n));
+ }
+ template <typename WrappedRange,
+ typename std::enable_if<
+ !WrappedRange::provides_buffer_access>::type* = nullptr>
+ expected<span<const typename detail::extract_char_type<
+ typename WrappedRange::iterator>::type>>
+ read_zero_copy(WrappedRange& r, ranges::range_difference_t<WrappedRange>)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+ return span<const typename detail::extract_char_type<
+ typename WrappedRange::iterator>::type>{};
+ }
+ /// @}
+
+ // read_all_zero_copy
+
+ /// @{
+ /**
+ * Reads every character from `r`, as if by repeatedly incrementing
+ * `r.begin()`, and returns a `span` pointing into `r`.
+ *
+ * If there's no error, `r` is advanced to the end.
+ * `r.begin()` is in no point dereferenced.
+ * If `r.size()` is not defined, the range is not contiguous, and an empty
+ * span is returned.
+ *
+ * \return A `span` pointing to `r`, starting at `r.begin()` and ending at
+ * `r.end()`.
+ * If `r.begin() == r.end()`, returns EOF.
+ * If the range does not satisfy `contiguous_range`, returns an empty
+ * `span`.
+ */
+ template <
+ typename WrappedRange,
+ typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
+ expected<span<const typename detail::extract_char_type<
+ typename WrappedRange::iterator>::type>>
+ read_all_zero_copy(WrappedRange& r)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+ auto s = make_span(r.data(), static_cast<size_t>(r.size()));
+ r.advance(r.size());
+ return s;
+ }
+ template <
+ typename WrappedRange,
+ typename std::enable_if<!WrappedRange::is_contiguous>::type* = nullptr>
+ expected<span<const typename detail::extract_char_type<
+ typename WrappedRange::iterator>::type>>
+ read_all_zero_copy(WrappedRange& r)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+ return span<const typename detail::extract_char_type<
+ typename WrappedRange::iterator>::type>{};
+ }
+ /// @}
+
+ // read_into
+
+ namespace detail {
+ template <typename WrappedRange, typename OutputIterator>
+ error read_into_impl(WrappedRange& r,
+ OutputIterator& it,
+ ranges::range_difference_t<WrappedRange> n)
+ {
+ for (; n != 0; --n) {
+ auto ret = read_code_unit(r, false);
+ if (!ret) {
+ return ret.error();
+ }
+ *it = ret.value();
+ r.advance();
+ }
+ return {};
+ }
+ } // namespace detail
+
+ /// @{
+
+ /**
+ * Reads up to `n` characters (= code units) from `r`, as if by repeatedly
+ * calling `read_code_unit()`, and writing the characters into `it`.
+ *
+ * If reading fails at any point, the error is returned.
+ * `r` is advanced by as many characters that were successfully read.
+ *
+ * \param r Range to read
+ * \param it Iterator to write into, e.g. `std::back_insert_iterator`. Must
+ * satisfy `output_iterator`, and be incrementable by `n` times.
+ * \param n Characters to read from `r`
+ *
+ * \return `error::good` if `n` characters were read.
+ * If `r.begin() == r.end()` at any point before `n` characters has been
+ * read, returns EOF.
+ * Any error returned by `read_code_unit()` if one
+ * occurred.
+ */
+ template <typename WrappedRange,
+ typename OutputIterator,
+ typename std::enable_if<
+ WrappedRange::provides_buffer_access>::type* = nullptr>
+ error read_into(WrappedRange& r,
+ OutputIterator& it,
+ ranges::range_difference_t<WrappedRange> n)
+ {
+ while (n != 0) {
+ if (r.begin() == r.end()) {
+ return {error::end_of_range, "EOF"};
+ }
+ auto s = read_zero_copy(r, n);
+ if (!s) {
+ return s.error();
+ }
+ if (s.value().size() == 0) {
+ break;
+ }
+ it = std::copy(s.value().begin(), s.value().end(), it);
+ n -= s.value().ssize();
+ }
+ if (n != 0) {
+ return detail::read_into_impl(r, it, n);
+ }
+ return {};
+ }
+ template <typename WrappedRange,
+ typename OutputIterator,
+ typename std::enable_if<
+ !WrappedRange::provides_buffer_access>::type* = nullptr>
+ error read_into(WrappedRange& r,
+ OutputIterator& it,
+ ranges::range_difference_t<WrappedRange> n)
+ {
+ if (r.begin() == r.end()) {
+ return {error::end_of_range, "EOF"};
+ }
+ return detail::read_into_impl(r, it, n);
+ }
+ /// @}
+
+ namespace detail {
+ template <typename WrappedRange, typename Predicate>
+ expected<span<const typename WrappedRange::char_type>>
+ read_until_pred_contiguous(WrappedRange& r,
+ Predicate&& pred,
+ bool pred_result_to_stop,
+ bool keep_final)
+ {
+ using span_type = span<const typename WrappedRange::char_type>;
+
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+
+ if (!pred.is_multibyte()) {
+ for (auto it = r.begin(); it != r.end(); ++it) {
+ if (pred(make_span(&*it, 1)) == pred_result_to_stop) {
+ auto begin = r.data();
+ auto end = keep_final ? it + 1 : it;
+ r.advance_to(end);
+ return span_type{
+ begin, to_address_safe(end, r.begin(), r.end())};
+ }
+ }
+ }
+ else {
+ for (auto it = r.begin(); it != r.end();) {
+ auto len = ::scn::get_sequence_length(*it);
+ if (len == 0 || ranges::distance(it, r.end()) < len) {
+ return error{error::invalid_encoding,
+ "Invalid code point"};
+ }
+ auto span =
+ make_span(to_address_safe(it, r.begin(), r.end()),
+ static_cast<size_t>(len));
+ code_point cp{};
+ auto i = parse_code_point(span.begin(), span.end(), cp);
+ if (!i) {
+ return i.error();
+ }
+ if (i.value() != span.end()) {
+ return error{error::invalid_encoding,
+ "Invalid code point"};
+ }
+ if (pred(span) == pred_result_to_stop) {
+ auto begin = r.data();
+ auto end = keep_final ? it + len : it;
+ r.advance_to(end);
+ return span_type{
+ begin, to_address_safe(end, r.begin(), r.end())};
+ }
+ it += len;
+ }
+ }
+ auto begin = r.data();
+ auto end = r.data() + r.size();
+ r.advance_to(r.end());
+ return span_type{begin, end};
+ }
+ } // namespace detail
+
+ // read_until_space_zero_copy
+
+ namespace detail {
+ template <typename WrappedRange, typename Predicate>
+ expected<span<const typename WrappedRange::char_type>>
+ read_until_space_zero_copy_impl(WrappedRange& r,
+ Predicate&& is_space,
+ bool keep_final_space,
+ std::true_type)
+ {
+ return detail::read_until_pred_contiguous(r, SCN_FWD(is_space),
+ true, keep_final_space);
+ }
+ template <typename WrappedRange, typename Predicate>
+ expected<span<const typename WrappedRange::char_type>>
+ read_until_space_zero_copy_impl(WrappedRange& r,
+ Predicate&&,
+ bool,
+ std::false_type)
+ {
+ if (r.begin() == r.end()) {
+ return error(error::end_of_range, "EOF");
+ }
+ return span<const typename WrappedRange::char_type>{};
+ }
+ } // namespace detail
+
+ /**
+ * Reads code points from `r`, until a space, as determined by `is_space`,
+ * is found, and returns a `span` pointing to `r`.
+ *
+ * If no error occurs `r` is advanced past the returned span.
+ * On error, `r` is not advanced.
+ *
+ * \param r Range to read from
+ *
+ * \param is_space Predicate taking a span of code units encompassing a code
+ * point, and returning a `bool`, where `true` means that the character is a
+ * space. Additionally, it must have a member function
+ * `is_space.is_multibyte()`, returning a `bool`, where `true` means that a
+ * space character can encompass multiple code units.
+ *
+ * \param keep_final_space If `true`, the space code point found is included
+ * in the returned span, and it is advanced past in `r`. If `false`, it is
+ * not included, and `r.begin()` will point to the space.
+ *
+ * \return Span of code units, pointing to `r`, starting at `r.begin()`, and
+ * ending at the space character, the precise location determined by the
+ * `keep_final_space` parameter.
+ * If `r.begin() == r.end()`, returns EOF.
+ * `r` reaching its end before a space character is found is not considered
+ * an error.
+ * If `r` contains invalid encoding, returns `error::invalid_encoding`.
+ * If the range is not contiguous, returns an empty `span`.
+ */
+ template <typename WrappedRange, typename Predicate>
+ expected<span<const typename WrappedRange::char_type>>
+ read_until_space_zero_copy(WrappedRange& r,
+ Predicate&& is_space,
+ bool keep_final_space)
+ {
+ return detail::read_until_space_zero_copy_impl(
+ r, SCN_FWD(is_space), keep_final_space,
+ std::integral_constant<bool, WrappedRange::is_contiguous>{});
+ }
+
+ // read_until_space
+
+ namespace detail {
+ template <typename WrappedRange,
+ typename Predicate,
+ typename OutputIt,
+ typename OutputItCmp>
+ error read_until_pred_buffer(WrappedRange& r,
+ Predicate&& pred,
+ bool pred_result_to_stop,
+ OutputIt& out,
+ OutputItCmp out_cmp,
+ bool keep_final,
+ bool& done,
+ std::true_type)
+ {
+ if (!pred.is_multibyte()) {
+ while (r.begin() != r.end() && !done) {
+ auto s = r.get_buffer_and_advance();
+ for (auto it = s.begin(); it != s.end() && out_cmp(out);
+ ++it) {
+ if (pred(make_span(&*it, 1)) == pred_result_to_stop) {
+ if (keep_final) {
+ *out = *it;
+ ++out;
+ }
+ auto e =
+ putback_n(r, ranges::distance(it, s.end()));
+ if (!e) {
+ return e;
+ }
+ done = true;
+ break;
+ }
+ *out = *it;
+ ++out;
+ }
+ if (!done && out_cmp(out)) {
+ auto ret = read_code_unit(r, false);
+ if (!ret) {
+ if (ret.error() == error::end_of_range) {
+ return {};
+ }
+ return ret.error();
+ }
+ if (pred(make_span(&ret.value(), 1)) ==
+ pred_result_to_stop) {
+ if (keep_final) {
+ r.advance();
+ *out = ret.value();
+ ++out;
+ }
+ done = true;
+ break;
+ }
+ r.advance();
+ *out = ret.value();
+ ++out;
+ }
+ }
+ }
+ else {
+ while (r.begin() != r.end() && !done) {
+ auto s = r.get_buffer_and_advance();
+ for (auto it = s.begin(); it != s.end() && out_cmp(out);) {
+ auto len = ::scn::get_sequence_length(*it);
+ if (len == 0) {
+ return error{error::invalid_encoding,
+ "Invalid code point"};
+ }
+ if (ranges::distance(it, s.end()) < len) {
+ auto e = putback_n(r, len);
+ if (!e) {
+ return e;
+ }
+ break;
+ }
+ auto cpspan = make_span(it, static_cast<size_t>(len));
+ code_point cp{};
+ auto i =
+ parse_code_point(cpspan.begin(), cpspan.end(), cp);
+ if (!i) {
+ return i.error();
+ }
+ if (i.value() != cpspan.end()) {
+ return error{error::invalid_encoding,
+ "Invalid code point"};
+ }
+ if (pred(cpspan) == pred_result_to_stop) {
+ if (keep_final) {
+ out = std::copy(cpspan.begin(), cpspan.end(),
+ out);
+ }
+ done = true;
+ break;
+ }
+ out = std::copy(cpspan.begin(), cpspan.end(), out);
+ }
+
+ if (!done && out_cmp(out)) {
+ alignas(typename WrappedRange::char_type) unsigned char
+ buf[4] = {0};
+ auto cpret = read_code_point(r, make_span(buf, 4));
+ if (!cpret) {
+ if (cpret.error() == error::end_of_range) {
+ return {};
+ }
+ return cpret.error();
+ }
+ if (pred(cpret.value().chars) == pred_result_to_stop) {
+ if (keep_final) {
+ out = std::copy(cpret.value().chars.begin(),
+ cpret.value().chars.end(), out);
+ }
+ else {
+ return putback_n(r,
+ cpret.value().chars.ssize());
+ }
+ done = true;
+ break;
+ }
+ out = std::copy(cpret.value().chars.begin(),
+ cpret.value().chars.end(), out);
+ }
+ }
+ }
+ return {};
+ }
+ template <typename WrappedRange,
+ typename Predicate,
+ typename OutputIt,
+ typename OutputItCmp>
+ error read_until_pred_buffer(WrappedRange&,
+ Predicate&&,
+ bool,
+ OutputIt&,
+ OutputItCmp,
+ bool,
+ bool& done,
+ std::false_type)
+ {
+ done = false;
+ return {};
+ }
+
+ template <typename WrappedRange,
+ typename Predicate,
+ typename OutputIt,
+ typename OutputItCmp>
+ error read_until_pred_non_contiguous(WrappedRange& r,
+ Predicate&& pred,
+ bool pred_result_to_stop,
+ OutputIt& out,
+ OutputItCmp out_cmp,
+ bool keep_final)
+ {
+ if (r.begin() == r.end()) {
+ return {error::end_of_range, "EOF"};
+ }
+
+ {
+ bool done = false;
+ auto e = read_until_pred_buffer(
+ r, pred, pred_result_to_stop, out, out_cmp, keep_final,
+ done,
+ std::integral_constant<
+ bool, WrappedRange::provides_buffer_access>{});
+ if (!e) {
+ return e;
+ }
+ if (done) {
+ return {};
+ }
+ }
+
+ if (!pred.is_multibyte()) {
+ while (r.begin() != r.end() && out_cmp(out)) {
+ auto cu = read_code_unit(r, false);
+ if (!cu) {
+ return cu.error();
+ }
+ if (pred(make_span(&cu.value(), 1)) ==
+ pred_result_to_stop) {
+ if (keep_final) {
+ r.advance();
+ *out = cu.value();
+ ++out;
+ }
+ return {};
+ }
+ r.advance();
+ *out = cu.value();
+ ++out;
+ }
+ }
+ else {
+ unsigned char buf[4] = {0};
+ while (r.begin() != r.end() && out_cmp(out)) {
+ auto cp = read_code_point(r, make_span(buf, 4));
+ if (!cp) {
+ return cp.error();
+ }
+ if (pred(cp.value().chars) == pred_result_to_stop) {
+ if (keep_final) {
+ out = std::copy(cp.value().chars.begin(),
+ cp.value().chars.end(), out);
+ return {};
+ }
+ else {
+ return putback_n(r, cp.value().chars.ssize());
+ }
+ }
+ out = std::copy(cp.value().chars.begin(),
+ cp.value().chars.end(), out);
+ }
+ }
+ return {};
+ }
+ } // namespace detail
+
+ /// @{
+
+ /**
+ * Reads code points from `r`, until a space, as determined by `is_space`,
+ * is found, and writes them into `out`, a single code unit at a time.
+ *
+ * If no error occurs, `r` is advanced past the last character written into
+ * `out`.
+ *
+ * On error, `r` is advanced an indeterminate amount, as if by calling
+ * `r.advance(n)`, where `n` is a non-negative integer.
+ * It is, however, not advanced past any space characters.
+ *
+ * \param r Range to read from
+ *
+ * \param out Iterator to write read characters into. Must satisfy
+ * `output_iterator`.
+ *
+ * \param is_space Predicate taking a span of code units encompassing a code
+ * point, and returning a `bool`, where `true` means that the character is a
+ * space. Additionally, it must have a member function
+ * `is_space.is_multibyte()`, returning a `bool`, where `true` means that a
+ * space character can encompass multiple code units.
+ *
+ * \param keep_final_space If `true`, the space code point found is written
+ * into `out`, and it is advanced past in `r`. If `false`, it is not
+ * included, and `r.begin()` will point to the space.
+ *
+ * \return `error::good` on success.
+ * If `r.begin() == r.end()`, returns EOF.
+ * `r` reaching its end before a space character is found is not considered
+ * an error.
+ * If `r` contains invalid encoding, returns `error::invalid_encoding`.
+ */
+ template <
+ typename WrappedRange,
+ typename OutputIterator,
+ typename Predicate,
+ typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
+ error read_until_space(WrappedRange& r,
+ OutputIterator& out,
+ Predicate&& is_space,
+ bool keep_final_space)
+ {
+ auto s =
+ read_until_space_zero_copy(r, SCN_FWD(is_space), keep_final_space);
+ if (!s) {
+ return s.error();
+ }
+ out = std::copy(s.value().begin(), s.value().end(), out);
+ return {};
+ }
+ template <
+ typename WrappedRange,
+ typename OutputIterator,
+ typename Predicate,
+ typename std::enable_if<!WrappedRange::is_contiguous>::type* = nullptr>
+ error read_until_space(WrappedRange& r,
+ OutputIterator& out,
+ Predicate&& is_space,
+ bool keep_final_space)
+ {
+ return detail::read_until_pred_non_contiguous(
+ r, SCN_FWD(is_space), true, out,
+ [](const OutputIterator&) { return true; }, keep_final_space);
+ }
+
+ /// @}
+
+ // read_until_space_ranged
+
+ /// @{
+
+ /**
+ * Otherwise equivalent to `read_until_space`, except will also stop reading
+ * if `out == end`.
+ *
+ * \see read_until_space
+ */
+ template <typename WrappedRange,
+ typename OutputIterator,
+ typename Sentinel,
+ typename Predicate>
+ error read_until_space_ranged(WrappedRange& r,
+ OutputIterator& out,
+ Sentinel end,
+ Predicate&& is_space,
+ bool keep_final_space)
+ {
+ return detail::read_until_pred_non_contiguous(
+ r, SCN_FWD(is_space), true, out,
+ [&end](const OutputIterator& it) { return it != end; },
+ keep_final_space);
+ }
+
+ /// @}
+
+ namespace detail {
+ /**
+ * Predicate to pass to read_until_space etc.
+ */
+ template <typename CharT>
+ struct is_space_predicate {
+ using char_type = CharT;
+ using locale_type = basic_locale_ref<char_type>;
+
+ /**
+ * \param l Locale to use, fetched from `ctx.locale()`
+ * \param localized If `true`, use `l.get_custom()`, otherwise use
+ * `l.get_static()`.
+ * \param width If `width != 0`, limit the number of code
+ * units to be read
+ */
+ SCN_CONSTEXPR14 is_space_predicate(const locale_type& l,
+ bool localized,
+ size_t width)
+ : m_locale{nullptr},
+ m_width{width},
+ m_fn{get_fn(localized, width != 0)}
+ {
+ if (localized) {
+ l.prepare_localized();
+ m_locale = l.get_localized_unsafe();
+ }
+ }
+
+ /**
+ * Returns `true` if `ch` is a code point according to the supplied
+ * locale, using either the static or custom locale, depending on
+ * the `localized` parameter given to the constructor.
+ *
+ * Returns also `true` if the maximum width, as determined by the
+ * `width` parameter given to the constructor, was reached.
+ */
+ bool operator()(span<const char_type> ch)
+ {
+ SCN_EXPECT(m_fn);
+ SCN_EXPECT(ch.size() >= 1);
+ return m_fn(m_locale, ch, m_i, m_width);
+ }
+
+ /**
+ * Returns `true`, if `*this` uses the custom locale for classifying
+ * space characters
+ */
+ constexpr bool is_localized() const
+ {
+ return m_locale != nullptr;
+ }
+ /**
+ * Returns `true` if a space character can encompass multiple code
+ * units
+ */
+ constexpr bool is_multibyte() const
+ {
+ return is_localized() && is_multichar_type(CharT{});
+ }
+
+ private:
+ using static_locale_type = typename locale_type::static_type;
+ using custom_locale_type = typename locale_type::custom_type;
+ const custom_locale_type* m_locale;
+ size_t m_width{0}, m_i{0};
+
+ constexpr static bool call(const custom_locale_type*,
+ span<const char_type> ch,
+ size_t&,
+ size_t)
+ {
+ return static_locale_type::is_space(ch);
+ }
+ static bool localized_call(const custom_locale_type* locale,
+ span<const char_type> ch,
+ size_t&,
+ size_t)
+ {
+ SCN_EXPECT(locale != nullptr);
+ return locale->is_space(ch);
+ }
+ SCN_CONSTEXPR14 static bool call_counting(const custom_locale_type*,
+ span<const char_type> ch,
+ size_t& i,
+ size_t max)
+ {
+ SCN_EXPECT(i <= max);
+ if (i == max || i + ch.size() > max) {
+ return true;
+ }
+ i += ch.size();
+ return static_locale_type::is_space(ch);
+ }
+ static bool localized_call_counting(
+ const custom_locale_type* locale,
+ span<const char_type> ch,
+ size_t& i,
+ size_t max)
+ {
+ SCN_EXPECT(locale != nullptr);
+ SCN_EXPECT(i <= max);
+ if (i == max || i + ch.size() > max) {
+ return true;
+ }
+ i += ch.size();
+ return locale->is_space(ch);
+ }
+
+ using fn_type = bool (*)(const custom_locale_type*,
+ span<const char_type>,
+ size_t&,
+ size_t);
+ fn_type m_fn{nullptr};
+
+ static SCN_CONSTEXPR14 fn_type get_fn(bool localized, bool counting)
+ {
+ if (localized) {
+ return counting ? localized_call_counting : localized_call;
+ }
+ return counting ? call_counting : call;
+ }
+ };
+
+ template <typename CharT>
+ is_space_predicate<CharT> make_is_space_predicate(
+ const basic_locale_ref<CharT>& locale,
+ bool localized,
+ size_t width = 0)
+ {
+ return {locale, localized, width};
+ }
+
+ template <typename CharT>
+ struct basic_skipws_iterator {
+ using value_type = void;
+ using reference = void;
+ using pointer = void;
+ using size_type = size_t;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::output_iterator_tag;
+
+ constexpr basic_skipws_iterator() = default;
+
+ basic_skipws_iterator& operator=(CharT)
+ {
+ return *this;
+ }
+ basic_skipws_iterator& operator*()
+ {
+ return *this;
+ }
+ basic_skipws_iterator& operator++()
+ {
+ return *this;
+ }
+ };
+ } // namespace detail
+
+ // skip_range_whitespace
+
+ /// @{
+
+ /**
+ * Reads code points from `ctx.range()`, as if by repeatedly calling
+ * `read_code_point()`, until a non-space character is found, or EOF is
+ * reached. That non-space character is then put back into the range.
+ *
+ * Whether a character is a space, is determined by `ctx.locale()` and the
+ * `localized` parameter.
+ *
+ * \param ctx Context to get the range and locale from.
+ *
+ * \param localized If `true`, `ctx.locale().get_custom()` is used.
+ * Otherwise, `ctx.locale().get_static()` is used.
+ * In practice, means whether locale-specific whitespace characters are
+ * accepted, or just those given by `std::isspace` with the `"C"` locale.
+ *
+ * \return `error::good` on success.
+ * If `ctx.range().begin() == ctx.range().end()`, returns EOF.
+ * If `ctx.range()` contains invalid encoding, returns
+ * `error::invalid_encoding`.
+ */
+ template <typename Context,
+ typename std::enable_if<
+ !Context::range_type::is_contiguous>::type* = nullptr>
+ error skip_range_whitespace(Context& ctx, bool localized) noexcept
+ {
+ auto is_space_pred =
+ detail::make_is_space_predicate(ctx.locale(), localized);
+ auto it = detail::basic_skipws_iterator<typename Context::char_type>{};
+ return detail::read_until_pred_non_contiguous(
+ ctx.range(), is_space_pred, false, it,
+ [](decltype(it)) { return true; }, false);
+ }
+ template <typename Context,
+ typename std::enable_if<
+ Context::range_type::is_contiguous>::type* = nullptr>
+ error skip_range_whitespace(Context& ctx, bool localized) noexcept
+ {
+ auto is_space_pred =
+ detail::make_is_space_predicate(ctx.locale(), localized);
+ return detail::read_until_pred_contiguous(ctx.range(), is_space_pred,
+ false, false)
+ .error();
+ }
+
+ /// @}
+
+ namespace detail {
+ template <typename T>
+ struct simple_integer_scanner {
+ template <typename CharT>
+ static expected<typename span<const CharT>::iterator> scan(
+ span<const CharT> buf,
+ T& val,
+ int base = 10,
+ uint16_t flags = 0);
+
+ template <typename CharT>
+ static expected<typename span<const CharT>::iterator> scan_lower(
+ span<const CharT> buf,
+ T& val,
+ int base = 10,
+ uint16_t flags = 0);
+ };
+ } // namespace detail
+
+ /**
+ * A very simple parser base class, which only accepts empty format string
+ * specifiers, e.g. `{}`, `{:}` or `{1:}`.
+ */
+ struct empty_parser : parser_base {
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ pctx.arg_begin();
+ if (SCN_UNLIKELY(!pctx)) {
+ return {error::invalid_format_string,
+ "Unexpected format string end"};
+ }
+ if (!pctx.check_arg_end()) {
+ return {error::invalid_format_string, "Expected argument end"};
+ }
+ pctx.arg_end();
+ return {};
+ }
+ };
+
+ /**
+ * Provides a framework for building a format string parser.
+ * Does not provide a `parse()` member function, so not a parser on to its
+ * own.
+ */
+ struct common_parser : parser_base {
+ static constexpr bool support_align_and_fill()
+ {
+ return true;
+ }
+
+ protected:
+ /**
+ * Parse the beginning of the argument.
+ * Returns `error::invalid_format_string` if `!pctx` (the format string
+ * ended)
+ */
+ template <typename ParseCtx>
+ error parse_common_begin(ParseCtx& pctx)
+ {
+ pctx.arg_begin();
+ if (SCN_UNLIKELY(!pctx)) {
+ return {error::invalid_format_string,
+ "Unexpected format string end"};
+ }
+ return {};
+ }
+
+ /**
+ * Returns `error::invalid_format_string` if the format string or the
+ * argument has ended.
+ */
+ template <typename ParseCtx>
+ error check_end(ParseCtx& pctx)
+ {
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+ return {};
+ }
+
+ /**
+ * Parse alignment, fill, width, and localization flags, and populate
+ * appropriate member variables.
+ *
+ * Returns `error::invalid_format_string` if an error occurred.
+ */
+ template <typename ParseCtx>
+ error parse_common_flags(ParseCtx& pctx)
+ {
+ SCN_EXPECT(check_end(pctx));
+ using char_type = typename ParseCtx::char_type;
+
+ auto ch = pctx.next_char();
+ auto next_char = [&]() -> error {
+ pctx.advance_char();
+ auto e = check_end(pctx);
+ if (!e) {
+ return e;
+ }
+ ch = pctx.next_char();
+ return {};
+ };
+ auto parse_number = [&](size_t& n) -> error {
+ SCN_EXPECT(pctx.locale().get_static().is_digit(ch));
+
+ auto it = pctx.begin();
+ for (; it != pctx.end(); ++it) {
+ if (!pctx.locale().get_static().is_digit(*it)) {
+ break;
+ }
+ }
+ auto buf = make_span(pctx.begin(), it);
+
+ auto s = detail::simple_integer_scanner<size_t>{};
+ auto res = s.scan(buf, n, 10);
+ if (!res) {
+ return res.error();
+ }
+
+ for (it = pctx.begin(); it != res.value();
+ pctx.advance_char(), it = pctx.begin()) {}
+ return {};
+ };
+
+ auto get_align_char = [&](char_type c) -> common_options_type {
+ if (c == detail::ascii_widen<char_type>('<')) {
+ return aligned_left;
+ }
+ if (c == detail::ascii_widen<char_type>('>')) {
+ return aligned_right;
+ }
+ if (c == detail::ascii_widen<char_type>('^')) {
+ return aligned_center;
+ }
+ return common_options_none;
+ };
+ auto parse_align = [&](common_options_type align, char_type fill) {
+ if (align != common_options_none) {
+ common_options |= align;
+ }
+ fill_char = static_cast<char32_t>(fill);
+ };
+
+ // align and fill
+ common_options_type align{};
+ bool align_set = false;
+ if (pctx.chars_left() > 1 &&
+ ch != detail::ascii_widen<char_type>('[')) {
+ const auto peek = pctx.peek_char();
+ align = get_align_char(peek);
+ if (align != common_options_none) {
+ // Arg is like "{:_x}", where _ is some fill character, and
+ // x is an alignment flag
+ // -> we have both alignment and fill
+ parse_align(align, ch);
+
+ auto e = next_char();
+ SCN_ENSURE(e);
+ if (!next_char()) {
+ return {};
+ }
+ align_set = true;
+ }
+ }
+ if (!align_set) {
+ align = get_align_char(ch);
+ if (align != common_options_none) {
+ // Arg is like "{:x}", where x is an alignment flag
+ // -> we have alignment with default fill (space ' ')
+ parse_align(align, detail::ascii_widen<char_type>(' '));
+ if (!next_char()) {
+ return {};
+ }
+ }
+ }
+
+ // digit -> width
+ if (pctx.locale().get_static().is_digit(ch)) {
+ common_options |= width_set;
+
+ size_t w{};
+ auto e = parse_number(w);
+ if (!e) {
+ return e;
+ }
+ field_width = w;
+ return {};
+ }
+ // L -> localized
+ if (ch == detail::ascii_widen<char_type>('L')) {
+ common_options |= localized;
+
+ if (!next_char()) {
+ return {};
+ }
+ }
+
+ return {};
+ }
+
+ /**
+ * Parse argument end.
+ *
+ * Returns `error::invalid_format_string` if argument end was not found.
+ */
+ template <typename ParseCtx>
+ error parse_common_end(ParseCtx& pctx)
+ {
+ if (!pctx || !pctx.check_arg_end()) {
+ return {error::invalid_format_string, "Expected argument end"};
+ }
+
+ pctx.arg_end();
+ return {};
+ }
+
+ /**
+ * A null callback to pass to `parse_common`, doing nothing and
+ * returning `error::good`.
+ */
+ template <typename ParseCtx>
+ static error null_type_cb(ParseCtx&, bool&)
+ {
+ return {};
+ }
+
+ public:
+ /**
+ * Parse a format string argument, using `parse_common_begin`,
+ * `parse_common_flags`, `parse_common_end`, and the supplied type
+ * flags.
+ *
+ * `type_options.size() == type_flags.size()` must be `true`.
+ * `pctx` must be valid, and must start at the format string argument
+ * specifiers, e.g. in the case of `"{1:foo}"` -> `pctx == "foo}"`
+ *
+ * \param pctx Format string to parse
+ * \param type_options A span of characters, where each character
+ * corresponds to a valid type flag. For example, for characters, this
+ * span would be \c ['c']
+ * \param type_flags A span of bools, where the values will be set to
+ * `true`, if a corresponding type flag from `type_options` was found.
+ * Should be initialized to all-`false`, as a `false` value will not be
+ * written.
+ * \param type_cb A callback to call, if none of the `type_options`
+ * matched. Must have the signature `(ParseCtx& pctx, bool& parsed) ->
+ * error`., where `parsed` is set to `true`, if the flag at
+ * `pctx.next_char()` was parsed and advanced past.
+ */
+ template <typename ParseCtx,
+ typename F,
+ typename CharT = typename ParseCtx::char_type>
+ error parse_common(ParseCtx& pctx,
+ span<const CharT> type_options,
+ span<bool> type_flags,
+ F&& type_cb)
+ {
+ SCN_EXPECT(type_options.size() == type_flags.size());
+
+ auto e = parse_common_begin(pctx);
+ if (!e) {
+ return e;
+ }
+
+ if (!pctx) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string"};
+ }
+ if (pctx.check_arg_end()) {
+ return {};
+ }
+
+ e = parse_common_flags(pctx);
+ if (!e) {
+ return e;
+ }
+
+ if (!pctx) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string"};
+ }
+ if (pctx.check_arg_end()) {
+ return {};
+ }
+
+ for (auto ch = pctx.next_char(); pctx && !pctx.check_arg_end();
+ ch = pctx.next_char()) {
+ bool parsed = false;
+ for (std::size_t i = 0; i < type_options.size() && !parsed;
+ ++i) {
+ if (ch == type_options[i]) {
+ if (SCN_UNLIKELY(type_flags[i])) {
+ return {error::invalid_format_string,
+ "Repeat flag in format string"};
+ }
+ type_flags[i] = true;
+ parsed = true;
+ }
+ }
+ if (parsed) {
+ pctx.advance_char();
+ if (!pctx || pctx.check_arg_end()) {
+ break;
+ }
+ continue;
+ }
+
+ e = type_cb(pctx, parsed);
+ if (!e) {
+ return e;
+ }
+ if (parsed) {
+ if (!pctx || pctx.check_arg_end()) {
+ break;
+ }
+ continue;
+ }
+ ch = pctx.next_char();
+
+ if (!parsed) {
+ return {error::invalid_format_string,
+ "Invalid character in format string"};
+ }
+ if (!pctx || pctx.check_arg_end()) {
+ break;
+ }
+ }
+
+ return parse_common_end(pctx);
+ }
+
+ void make_localized()
+ {
+ common_options |= localized;
+ }
+
+ /**
+ * Invoke `parse_common()` with default options (no type flags)
+ */
+ template <typename ParseCtx>
+ error parse_default(ParseCtx& pctx)
+ {
+ return parse_common(pctx, {}, {}, null_type_cb<ParseCtx>);
+ }
+
+ constexpr bool is_aligned_left() const noexcept
+ {
+ return (common_options & aligned_left) != 0 ||
+ (common_options & aligned_center) != 0;
+ }
+ constexpr bool is_aligned_right() const noexcept
+ {
+ return (common_options & aligned_right) != 0 ||
+ (common_options & aligned_center) != 0;
+ }
+ template <typename CharT>
+ constexpr CharT get_fill_char() const noexcept
+ {
+ return static_cast<CharT>(fill_char);
+ }
+
+ size_t field_width{0};
+ char32_t fill_char{0};
+ enum common_options_type : uint8_t {
+ common_options_none = 0,
+ localized = 1, // 'L',
+ aligned_left = 2, // '<'
+ aligned_right = 4, // '>'
+ aligned_center = 8, // '^'
+ width_set = 16, // width
+ common_options_all = 31,
+ };
+ uint8_t common_options{0};
+ };
+
+ /**
+ * Derives from `common_parser`, and implements `parse()` with
+ * `parse_default()`
+ */
+ struct common_parser_default : common_parser {
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ return parse_default(pctx);
+ }
+ };
+
+ namespace detail {
+ template <typename Context,
+ typename std::enable_if<
+ !Context::range_type::is_contiguous>::type* = nullptr>
+ error scan_alignment(Context& ctx,
+ typename Context::char_type fill) noexcept
+ {
+ while (true) {
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+
+ auto ch = read_code_unit(ctx.range());
+ if (SCN_UNLIKELY(!ch)) {
+ return ch.error();
+ }
+ if (ch.value() != fill) {
+ auto pb = putback_n(ctx.range(), 1);
+ if (SCN_UNLIKELY(!pb)) {
+ return pb;
+ }
+ break;
+ }
+
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+ return {};
+ }
+ template <typename Context,
+ typename std::enable_if<
+ Context::range_type::is_contiguous>::type* = nullptr>
+ error scan_alignment(Context& ctx,
+ typename Context::char_type fill) noexcept
+ {
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ const auto end = ctx.range().end();
+ for (auto it = ctx.range().begin(); it != end; ++it) {
+ if (*it != fill) {
+ ctx.range().advance_to(it);
+ return {};
+ }
+ }
+ ctx.range().advance_to(end);
+ return {};
+
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+
+ template <typename Scanner, typename = void>
+ struct scanner_supports_alignment : std::false_type {
+ };
+ template <typename Scanner>
+ struct scanner_supports_alignment<
+ Scanner,
+ typename std::enable_if<Scanner::support_align_and_fill()>::type>
+ : std::true_type {
+ };
+
+ template <typename Context, typename Scanner>
+ error skip_alignment(Context& ctx,
+ Scanner& scanner,
+ bool left,
+ std::true_type)
+ {
+ if (left && !scanner.is_aligned_left()) {
+ return {};
+ }
+ if (!left && !scanner.is_aligned_right()) {
+ return {};
+ }
+ return scan_alignment(
+ ctx,
+ scanner.template get_fill_char<typename Context::char_type>());
+ }
+ template <typename Context, typename Scanner>
+ error skip_alignment(Context&, Scanner&, bool, std::false_type)
+ {
+ return {};
+ }
+
+ /**
+ * Scan argument in `val`, from `ctx`, using `Scanner` and `pctx`.
+ *
+ * Parses `pctx` for `Scanner`, skips whitespace and alignment if
+ * necessary, and scans the argument into `val`.
+ */
+ template <typename Scanner,
+ typename T,
+ typename Context,
+ typename ParseCtx>
+ error visitor_boilerplate(T& val, Context& ctx, ParseCtx& pctx)
+ {
+ Scanner scanner;
+
+ auto err = pctx.parse(scanner);
+ if (!err) {
+ return err;
+ }
+
+ if (scanner.skip_preceding_whitespace()) {
+ err = skip_range_whitespace(ctx, false);
+ if (!err) {
+ return err;
+ }
+ }
+
+ err = skip_alignment(ctx, scanner, false,
+ scanner_supports_alignment<Scanner>{});
+ if (!err) {
+ return err;
+ }
+
+ err = scanner.scan(val, ctx);
+ if (!err) {
+ return err;
+ }
+
+ return skip_alignment(ctx, scanner, true,
+ scanner_supports_alignment<Scanner>{});
+ }
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/reader/float.h b/src/third-party/scnlib/include/scn/reader/float.h
new file mode 100644
index 0000000..24265a1
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/reader/float.h
@@ -0,0 +1,246 @@
+// 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_READER_FLOAT_H
+#define SCN_READER_FLOAT_H
+
+#include "../util/small_vector.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+ namespace detail {
+ template <typename T>
+ struct float_scanner_access;
+
+ template <typename T>
+ struct float_scanner : common_parser {
+ static_assert(std::is_floating_point<T>::value,
+ "float_scanner requires a floating point type");
+
+ friend struct float_scanner_access<T>;
+
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ using char_type = typename ParseCtx::char_type;
+
+ array<char_type, 10> options{
+ {// hex
+ ascii_widen<char_type>('a'), ascii_widen<char_type>('A'),
+ // scientific
+ ascii_widen<char_type>('e'), ascii_widen<char_type>('E'),
+ // fixed
+ ascii_widen<char_type>('f'), ascii_widen<char_type>('F'),
+ // general
+ ascii_widen<char_type>('g'), ascii_widen<char_type>('G'),
+ // localized digits
+ ascii_widen<char_type>('n'),
+ // thsep
+ ascii_widen<char_type>('\'')}};
+ bool flags[10] = {false};
+
+ auto e = parse_common(
+ pctx, span<const char_type>{options.begin(), options.end()},
+ span<bool>{flags, 10}, null_type_cb<ParseCtx>);
+ if (!e) {
+ return e;
+ }
+
+ if (flags[0] && flags[1]) {
+ return {error::invalid_format_string,
+ "Can't have both 'a' and 'A' flags with floats"};
+ }
+ if (flags[2] && flags[3]) {
+ return {error::invalid_format_string,
+ "Can't have both 'e' and 'E' flags with floats"};
+ }
+ if (flags[4] && flags[5]) {
+ return {error::invalid_format_string,
+ "Can't have both 'f' and 'F' flags with floats"};
+ }
+ if (flags[6] && flags[7]) {
+ return {error::invalid_format_string,
+ "Can't have both 'g' and 'G' flags with floats"};
+ }
+
+ bool set_hex = flags[0] || flags[1];
+ bool set_scientific = flags[2] || flags[3];
+ bool set_fixed = flags[4] || flags[5];
+ bool set_general = flags[6] || flags[7];
+ if (set_general && set_fixed) {
+ return {error::invalid_format_string,
+ "General float already implies fixed"};
+ }
+ if (set_general && set_scientific) {
+ return {error::invalid_format_string,
+ "General float already implies scientific"};
+ }
+
+ format_options = 0;
+ if (set_hex) {
+ format_options |= allow_hex;
+ }
+ if (set_scientific) {
+ format_options |= allow_scientific;
+ }
+ if (set_fixed) {
+ format_options |= allow_fixed;
+ }
+ if (set_general) {
+ format_options |= allow_fixed | allow_scientific;
+ }
+ if (format_options == 0) {
+ format_options |=
+ allow_fixed | allow_scientific | allow_hex;
+ }
+
+ // 'n'
+ if (flags[8]) {
+ common_options |= localized;
+ format_options |= localized_digits;
+ }
+
+ // thsep
+ if (flags[9]) {
+ format_options |= allow_thsep;
+ }
+
+ return {};
+ }
+
+ template <typename Context>
+ error scan(T& val, Context& ctx)
+ {
+ using char_type = typename Context::char_type;
+
+ auto do_parse_float = [&](span<const char_type> s) -> error {
+ T tmp = 0;
+ expected<std::ptrdiff_t> ret{0};
+ if (SCN_UNLIKELY((format_options & localized_digits) != 0 ||
+ ((common_options & localized) != 0 &&
+ (format_options & allow_hex) != 0))) {
+ // 'n' OR ('L' AND 'a')
+ // because none of our parsers support BOTH hexfloats
+ // and custom (localized) decimal points,
+ // so we have to fall back on iostreams
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ std::basic_string<char_type> str(s.data(), s.size());
+ ret =
+ ctx.locale().get_localized().read_num(tmp, str, 0);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+ else {
+ ret = _read_float(
+ tmp, s,
+ ctx.locale()
+ .get((common_options & localized) != 0)
+ .decimal_point());
+ }
+
+ if (!ret) {
+ return ret.error();
+ }
+ if (ret.value() != s.ssize()) {
+ auto pb =
+ putback_n(ctx.range(), s.ssize() - ret.value());
+ if (!pb) {
+ return pb;
+ }
+ }
+ val = tmp;
+ return {};
+ };
+
+ auto is_space_pred = make_is_space_predicate(
+ ctx.locale(), (common_options & localized) != 0,
+ field_width);
+
+ if (Context::range_type::is_contiguous) {
+ auto s = read_until_space_zero_copy(ctx.range(),
+ is_space_pred, false);
+ if (!s) {
+ return s.error();
+ }
+ return do_parse_float(s.value());
+ }
+
+ small_vector<char_type, 32> buf;
+ auto outputit = std::back_inserter(buf);
+ auto e = read_until_space(ctx.range(), outputit, is_space_pred,
+ false);
+ if (!e && buf.empty()) {
+ return e;
+ }
+
+ return do_parse_float(make_span(buf));
+ }
+
+ enum format_options_type {
+ allow_hex = 1,
+ allow_scientific = 2,
+ allow_fixed = 4,
+ localized_digits = 8,
+ allow_thsep = 16
+ };
+ uint8_t format_options{allow_hex | allow_scientific | allow_fixed};
+
+ private:
+ template <typename CharT>
+ expected<std::ptrdiff_t> _read_float(T& val,
+ span<const CharT> s,
+ CharT locale_decimal_point)
+ {
+ size_t chars{};
+ std::basic_string<CharT> str(s.data(), s.size());
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto ret =
+ _read_float_impl(str.data(), chars, locale_decimal_point);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!ret) {
+ return ret.error();
+ }
+ val = ret.value();
+ return static_cast<std::ptrdiff_t>(chars);
+ }
+
+ template <typename CharT>
+ expected<T> _read_float_impl(const CharT* str,
+ size_t& chars,
+ CharT locale_decimal_point);
+ };
+
+ // instantiate
+ template struct float_scanner<float>;
+ template struct float_scanner<double>;
+ template struct float_scanner<long double>;
+
+ template <typename T>
+ struct float_scanner_access : public float_scanner<T> {
+ using float_scanner<T>::_read_float;
+ using float_scanner<T>::_read_float_impl;
+ };
+ } // namespace detail
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && \
+ !defined(SCN_READER_FLOAT_CPP)
+#include "reader_float.cpp"
+#endif
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/reader/int.h b/src/third-party/scnlib/include/scn/reader/int.h
new file mode 100644
index 0000000..19bac44
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/reader/int.h
@@ -0,0 +1,537 @@
+// 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_READER_INT_H
+#define SCN_READER_INT_H
+
+#include "../util/math.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename T>
+ struct integer_scanner : common_parser {
+ static_assert(std::is_integral<T>::value,
+ "integer_scanner requires an integral type");
+
+ friend struct simple_integer_scanner<T>;
+
+ bool skip_preceding_whitespace()
+ {
+ // if format_options == single_code_unit,
+ // then we're scanning a char -> don't skip
+ return format_options != single_code_unit;
+ }
+
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ using char_type = typename ParseCtx::char_type;
+
+ format_options = 0;
+
+ int custom_base = 0;
+ auto each = [&](ParseCtx& p, bool& parsed) -> error {
+ parsed = false;
+ auto ch = pctx.next_char();
+
+ if (ch == detail::ascii_widen<char_type>('B')) {
+ // Custom base
+ p.advance_char();
+ if (SCN_UNLIKELY(!p)) {
+ return {error::invalid_format_string,
+ "Unexpected format string end"};
+ }
+ if (SCN_UNLIKELY(p.check_arg_end())) {
+ return {error::invalid_format_string,
+ "Unexpected argument end"};
+ }
+ ch = p.next_char();
+
+ const auto zero = detail::ascii_widen<char_type>('0'),
+ nine = detail::ascii_widen<char_type>('9');
+ integer_type_for_char<char_type> tmp = 0;
+ if (ch < zero || ch > nine) {
+ return {error::invalid_format_string,
+ "Invalid character after 'B', "
+ "expected digit"};
+ }
+ tmp = static_cast<integer_type_for_char<char_type>>(
+ p.next_char() - zero);
+ if (tmp < 1) {
+ return {error::invalid_format_string,
+ "Invalid base, must be between 2 and 36"};
+ }
+
+ p.advance_char();
+ if (!p) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string"};
+ }
+ if (p.check_arg_end()) {
+ custom_base = static_cast<uint8_t>(tmp);
+ parsed = true;
+ return {};
+ }
+ ch = p.next_char();
+
+ if (ch < zero || ch > nine) {
+ return {error::invalid_format_string,
+ "Invalid character after 'B', "
+ "expected digit"};
+ }
+ tmp *= 10;
+ tmp += static_cast<integer_type_for_char<char_type>>(
+ ch - zero);
+ if (tmp < 2 || tmp > 36) {
+ return {error::invalid_format_string,
+ "Invalid base, must be between 2 and 36"};
+ }
+ custom_base = static_cast<uint8_t>(tmp);
+ parsed = true;
+ pctx.advance_char();
+ return {};
+ }
+
+ return {};
+ };
+
+ array<char_type, 9> options{{// decimal
+ ascii_widen<char_type>('d'),
+ // binary
+ ascii_widen<char_type>('b'),
+ // octal
+ ascii_widen<char_type>('o'),
+ // hex
+ ascii_widen<char_type>('x'),
+ // detect base
+ ascii_widen<char_type>('i'),
+ // unsigned decimal
+ ascii_widen<char_type>('u'),
+ // code unit
+ ascii_widen<char_type>('c'),
+ // localized digits
+ ascii_widen<char_type>('n'),
+ // thsep
+ ascii_widen<char_type>('\'')}};
+ bool flags[9] = {false};
+
+ auto e = parse_common(
+ pctx, span<const char_type>{options.begin(), options.end()},
+ span<bool>{flags, 9}, each);
+ if (!e) {
+ return e;
+ }
+
+ int base_flags_set = int(flags[0]) + int(flags[1]) +
+ int(flags[2]) + int(flags[3]) +
+ int(flags[4]) + int(flags[5]) +
+ int(custom_base != 0);
+ if (SCN_UNLIKELY(base_flags_set > 1)) {
+ return {error::invalid_format_string,
+ "Up to one base flags ('d', 'i', 'u', 'b', 'o', "
+ "'x', 'B') allowed"};
+ }
+ else if (base_flags_set == 0) {
+ // Default:
+ // 'c' for CharT
+ // 'd' otherwise
+ if (std::is_same<T, typename ParseCtx::char_type>::value) {
+ format_options = single_code_unit;
+ }
+ else {
+ base = 10;
+ }
+ }
+ else if (custom_base != 0) {
+ // B__
+ base = static_cast<uint8_t>(custom_base);
+ }
+ else if (flags[0]) {
+ // 'd' flag
+ base = 10;
+ }
+ else if (flags[1]) {
+ // 'b' flag
+ base = 2;
+ format_options |= allow_base_prefix;
+ }
+ else if (flags[2]) {
+ // 'o' flag
+ base = 8;
+ format_options |= allow_base_prefix;
+ }
+ else if (flags[3]) {
+ // 'x' flag
+ base = 16;
+ format_options |= allow_base_prefix;
+ }
+ else if (flags[4]) {
+ // 'i' flag
+ base = 0;
+ }
+ else if (flags[5]) {
+ // 'u' flag
+ base = 10;
+ format_options |= only_unsigned;
+ }
+
+ // n set, implies L
+ if (flags[7]) {
+ common_options |= localized;
+ format_options |= localized_digits;
+ }
+ if ((format_options & localized_digits) != 0 &&
+ (base != 0 && base != 10 && base != 8 && base != 16)) {
+ return {error::invalid_format_string,
+ "Localized integers can only be scanned in "
+ "bases 8, 10 and 16"};
+ }
+
+ // thsep flag
+ if (flags[8]) {
+ format_options |= allow_thsep;
+ }
+
+ // 'c' flag -> no other options allowed
+ if (flags[6]) {
+ if (!(format_options == 0 ||
+ format_options == single_code_unit) ||
+ base_flags_set != 0) {
+ return {error::invalid_format_string,
+ "'c' flag cannot be used in conjunction with "
+ "any other flags"};
+ }
+ format_options = single_code_unit;
+ }
+
+ return {};
+ }
+
+ template <typename Context>
+ error scan(T& val, Context& ctx)
+ {
+ using char_type = typename Context::char_type;
+ auto do_parse_int = [&](span<const char_type> s) -> error {
+ T tmp = 0;
+ expected<std::ptrdiff_t> ret{0};
+ if (SCN_UNLIKELY((format_options & localized_digits) !=
+ 0)) {
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ int b{base};
+ auto r = parse_base_prefix<char_type>(s, b);
+ if (!r) {
+ return r.error();
+ }
+ if (b == -1) {
+ // -1 means we read a '0'
+ tmp = 0;
+ return {};
+ }
+ if (b != 10 && base != b && base != 0) {
+ return {error::invalid_scanned_value,
+ "Invalid base prefix"};
+ }
+ if (base == 0) {
+ base = static_cast<uint8_t>(b);
+ }
+ if (base != 8 && base != 10 && base != 16) {
+ return {error::invalid_scanned_value,
+ "Localized values have to be in base "
+ "8, 10 or 16"};
+ }
+
+ auto it = r.value();
+ std::basic_string<char_type> str(to_address(it),
+ s.size());
+ ret = ctx.locale().get_localized().read_num(
+ tmp, str, static_cast<int>(base));
+
+ if (tmp < T{0} &&
+ (format_options & only_unsigned) != 0) {
+ return {error::invalid_scanned_value,
+ "Parsed negative value when type was 'u'"};
+ }
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+ else {
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ ret = _parse_int(tmp, s);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+
+ if (!ret) {
+ return ret.error();
+ }
+ if (ret.value() != s.ssize()) {
+ auto pb =
+ putback_n(ctx.range(), s.ssize() - ret.value());
+ if (!pb) {
+ return pb;
+ }
+ }
+ val = tmp;
+ return {};
+ };
+
+ if (format_options == single_code_unit) {
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4127) // conditional expression is constant
+ if (sizeof(T) < sizeof(char_type)) {
+ // sizeof(char_type) > 1 -> wide range
+ // Code unit might not fit
+ return error{error::invalid_operation,
+ "Cannot read this type as a code unit "
+ "from a wide range"};
+ }
+ SCN_MSVC_POP
+ auto ch = read_code_unit(ctx.range());
+ if (!ch) {
+ return ch.error();
+ }
+ val = static_cast<T>(ch.value());
+ return {};
+ }
+
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4127) // conditional expression is constant
+ if ((std::is_same<T, char>::value ||
+ std::is_same<T, wchar_t>::value) &&
+ !std::is_same<T, char_type>::value) {
+ // T is a character type, but not char_type:
+ // Trying to read a char from a wide range, or wchar_t from
+ // a narrow one
+ // Reading a code unit is allowed, however
+ return error{error::invalid_operation,
+ "Cannot read a char from a wide range, or a "
+ "wchar_t from a narrow one"};
+ }
+ SCN_MSVC_POP
+
+ std::basic_string<char_type> buf{};
+ span<const char_type> bufspan{};
+ auto e = _read_source(
+ ctx, buf, bufspan,
+ std::integral_constant<
+ bool, Context::range_type::is_contiguous>{});
+ if (!e) {
+ return e;
+ }
+
+ return do_parse_int(bufspan);
+ }
+
+ enum format_options_type : uint8_t {
+ // "n" option -> localized digits and digit grouping
+ localized_digits = 1,
+ // "'" option -> accept thsep
+ // if "L" use locale, default=','
+ allow_thsep = 2,
+ // "u" option -> don't allow sign
+ only_unsigned = 4,
+ // Allow base prefix (e.g. 0B and 0x)
+ allow_base_prefix = 8,
+ // "c" option -> scan a code unit
+ single_code_unit = 16,
+ };
+ uint8_t format_options{default_format_options()};
+
+ // 0 = detect base
+ // Otherwise [2,36]
+ uint8_t base{0};
+
+ private:
+ static SCN_CONSTEXPR14 uint8_t default_format_options()
+ {
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4127) // conditional expression is constant
+ if (std::is_same<T, char>::value ||
+ std::is_same<T, wchar_t>::value) {
+ return single_code_unit;
+ }
+ return 0;
+ SCN_MSVC_POP
+ }
+
+ template <typename Context, typename Buf, typename CharT>
+ error _read_source(Context& ctx,
+ Buf& buf,
+ span<const CharT>& s,
+ std::false_type)
+ {
+ auto do_read = [&](Buf& b) -> error {
+ auto outputit = std::back_inserter(b);
+ auto is_space_pred = make_is_space_predicate(
+ ctx.locale(), (common_options & localized) != 0,
+ field_width);
+ auto e = read_until_space(ctx.range(), outputit,
+ is_space_pred, false);
+ if (!e && b.empty()) {
+ return e;
+ }
+
+ return {};
+ };
+
+ if (SCN_LIKELY((format_options & allow_thsep) == 0)) {
+ auto e = do_read(buf);
+ if (!e) {
+ return e;
+ }
+ s = make_span(buf.data(), buf.size());
+ return {};
+ }
+
+ Buf tmp;
+ auto e = do_read(tmp);
+ if (!e) {
+ return e;
+ }
+ auto thsep = ctx.locale()
+ .get((common_options & localized) != 0)
+ .thousands_separator();
+
+ auto it = tmp.begin();
+ for (; it != tmp.end(); ++it) {
+ if (*it == thsep) {
+ for (auto it2 = it; ++it2 != tmp.end();) {
+ *it++ = SCN_MOVE(*it2);
+ }
+ break;
+ }
+ }
+
+ auto n =
+ static_cast<std::size_t>(std::distance(tmp.begin(), it));
+ if (n == 0) {
+ return {error::invalid_scanned_value,
+ "Only a thousands separator found"};
+ }
+
+ buf = SCN_MOVE(tmp);
+ s = make_span(buf.data(), n);
+ return {};
+ }
+
+ template <typename Context, typename Buf, typename CharT>
+ error _read_source(Context& ctx,
+ Buf& buf,
+ span<const CharT>& s,
+ std::true_type)
+ {
+ if (SCN_UNLIKELY((format_options & allow_thsep) != 0)) {
+ return _read_source(ctx, buf, s, std::false_type{});
+ }
+ auto ret = read_zero_copy(
+ ctx.range(), field_width != 0
+ ? static_cast<std::ptrdiff_t>(field_width)
+ : ctx.range().size());
+ if (!ret) {
+ return ret.error();
+ }
+ s = ret.value();
+ return {};
+ }
+
+ template <typename CharT>
+ expected<typename span<const CharT>::iterator> parse_base_prefix(
+ span<const CharT> s,
+ int& b) const;
+
+ template <typename CharT>
+ expected<std::ptrdiff_t> _parse_int(T& val, span<const CharT> s);
+
+ template <typename CharT>
+ expected<typename span<const CharT>::iterator> _parse_int_impl(
+ T& val,
+ bool minus_sign,
+ span<const CharT> buf) const;
+ };
+
+ // instantiate
+ template struct integer_scanner<signed char>;
+ template struct integer_scanner<short>;
+ template struct integer_scanner<int>;
+ template struct integer_scanner<long>;
+ template struct integer_scanner<long long>;
+ template struct integer_scanner<unsigned char>;
+ template struct integer_scanner<unsigned short>;
+ template struct integer_scanner<unsigned int>;
+ template struct integer_scanner<unsigned long>;
+ template struct integer_scanner<unsigned long long>;
+ template struct integer_scanner<char>;
+ template struct integer_scanner<wchar_t>;
+
+ template <typename T>
+ template <typename CharT>
+ expected<typename span<const CharT>::iterator>
+ simple_integer_scanner<T>::scan(span<const CharT> buf,
+ T& val,
+ int base,
+ uint16_t flags)
+ {
+ SCN_EXPECT(buf.size() != 0);
+
+ integer_scanner<T> s{};
+ s.base = static_cast<uint8_t>(base);
+ s.format_options = flags & 0xffu;
+ s.common_options = static_cast<uint8_t>(flags >> 8u);
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto n = s._parse_int(val, buf);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!n) {
+ return n.error();
+ }
+ return buf.begin() + n.value();
+ }
+ template <typename T>
+ template <typename CharT>
+ expected<typename span<const CharT>::iterator>
+ simple_integer_scanner<T>::scan_lower(span<const CharT> buf,
+ T& val,
+ int base,
+ uint16_t flags)
+ {
+ SCN_EXPECT(buf.size() != 0);
+ SCN_EXPECT(base > 0);
+
+ integer_scanner<T> s{};
+ s.base = static_cast<uint8_t>(base);
+ s.format_options = flags & 0xffu;
+ s.common_options = static_cast<uint8_t>(flags >> 8u);
+
+ bool minus_sign = false;
+ if (buf[0] == ascii_widen<CharT>('-')) {
+ buf = buf.subspan(1);
+ minus_sign = true;
+ }
+
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ return s._parse_int_impl(val, minus_sign, buf);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+ } // namespace detail
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_READER_INT_CPP)
+#include "reader_int.cpp"
+#endif
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/reader/reader.h b/src/third-party/scnlib/include/scn/reader/reader.h
new file mode 100644
index 0000000..cd955ce
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/reader/reader.h
@@ -0,0 +1,111 @@
+// 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_READER_READER_H
+#define SCN_READER_READER_H
+
+#include "common.h"
+#include "float.h"
+#include "int.h"
+#include "string.h"
+#include "types.h"
+
+#include "../detail/args.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ template <>
+ struct scanner<code_point> : public detail::code_point_scanner {
+ };
+ template <>
+ struct scanner<bool> : public detail::bool_scanner {
+ };
+ template <>
+ struct scanner<char> : public detail::integer_scanner<char> {
+ };
+ template <>
+ struct scanner<wchar_t> : public detail::integer_scanner<wchar_t> {
+ };
+ template <>
+ struct scanner<signed char> : public detail::integer_scanner<signed char> {
+ };
+ template <>
+ struct scanner<short> : public detail::integer_scanner<short> {
+ };
+ template <>
+ struct scanner<int> : public detail::integer_scanner<int> {
+ };
+ template <>
+ struct scanner<long> : public detail::integer_scanner<long> {
+ };
+ template <>
+ struct scanner<long long> : public detail::integer_scanner<long long> {
+ };
+ template <>
+ struct scanner<unsigned char>
+ : public detail::integer_scanner<unsigned char> {
+ };
+ template <>
+ struct scanner<unsigned short>
+ : public detail::integer_scanner<unsigned short> {
+ };
+ template <>
+ struct scanner<unsigned int>
+ : public detail::integer_scanner<unsigned int> {
+ };
+ template <>
+ struct scanner<unsigned long>
+ : public detail::integer_scanner<unsigned long> {
+ };
+ template <>
+ struct scanner<unsigned long long>
+ : public detail::integer_scanner<unsigned long long> {
+ };
+ template <>
+ struct scanner<float> : public detail::float_scanner<float> {
+ };
+ template <>
+ struct scanner<double> : public detail::float_scanner<double> {
+ };
+ template <>
+ struct scanner<long double> : public detail::float_scanner<long double> {
+ };
+ template <typename CharT, typename Allocator>
+ struct scanner<std::basic_string<CharT, std::char_traits<CharT>, Allocator>>
+ : public detail::string_scanner {
+ };
+ template <typename CharT>
+ struct scanner<span<CharT>> : public detail::span_scanner {
+ };
+ template <typename CharT>
+ struct scanner<basic_string_view<CharT>>
+ : public detail::string_view_scanner {
+ };
+#if SCN_HAS_STRING_VIEW
+ template <typename CharT>
+ struct scanner<std::basic_string_view<CharT>>
+ : public detail::std_string_view_scanner {
+ };
+#endif
+ template <>
+ struct scanner<detail::monostate>;
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/reader/string.h b/src/third-party/scnlib/include/scn/reader/string.h
new file mode 100644
index 0000000..19727ee
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/reader/string.h
@@ -0,0 +1,1336 @@
+// 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_READER_STRING_H
+#define SCN_READER_STRING_H
+
+#include "../util/small_vector.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+ namespace detail {
+ class set_parser_type {
+ public:
+ constexpr set_parser_type() = default;
+
+ template <typename ParseCtx>
+ error parse_set(ParseCtx& pctx, bool& parsed)
+ {
+ using char_type = typename ParseCtx::char_type;
+ SCN_EXPECT(pctx.next_char() == ascii_widen<char_type>('['));
+
+ pctx.advance_char();
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+
+ get_option(flag::enabled) = true;
+ parsed = true;
+
+ if (pctx.next_char() == ascii_widen<char_type>('^')) {
+ // inverted
+ get_option(flag::inverted) = true;
+ pctx.advance_char();
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+ }
+
+ if (pctx.next_char() == ascii_widen<char_type>(']')) {
+ // end of range
+ get_option(flag::accept_all) = true;
+ pctx.advance_char();
+ return {};
+ }
+
+ while (true) {
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+
+ const auto ch = pctx.next_char();
+ if (ch == ascii_widen<char_type>(']')) {
+ break;
+ }
+
+ auto err = parse_next_char(pctx, true);
+ if (!err) {
+ return err;
+ }
+
+ err = pctx.advance_cp();
+ if (!err) {
+ pctx.advance_char();
+ }
+ }
+ auto err = pctx.advance_cp();
+ if (!err) {
+ pctx.advance_char();
+ }
+
+ return {};
+ }
+
+ error sanitize(bool localized)
+ {
+ // specifiers -> chars, if not localized
+ if (get_option(flag::use_specifiers)) {
+ if ((get_option(specifier::letters) ||
+ get_option(specifier::alpha)) &&
+ get_option(specifier::inverted_letters)) {
+ get_option(flag::accept_all) = true;
+ }
+ if (get_option(specifier::alnum_underscore) &&
+ get_option(specifier::inverted_alnum_underscore)) {
+ get_option(flag::accept_all) = true;
+ }
+ if ((get_option(specifier::whitespace) ||
+ get_option(specifier::space)) &&
+ get_option(specifier::inverted_whitespace)) {
+ get_option(flag::accept_all) = true;
+ }
+ if ((get_option(specifier::numbers) ||
+ get_option(specifier::digit)) &&
+ get_option(specifier::inverted_numbers)) {
+ get_option(flag::accept_all) = true;
+ }
+ }
+
+ if (get_option(flag::use_specifiers) &&
+ !get_option(flag::accept_all)) {
+ if (localized) {
+ if (get_option(specifier::letters)) {
+ get_option(specifier::letters) = false;
+ get_option(specifier::alpha) = true;
+ }
+ if (get_option(specifier::alnum_underscore)) {
+ get_option(specifier::alnum_underscore) = false;
+ get_option(specifier::alnum) = true;
+ get_option('_') = true;
+ }
+ if (get_option(specifier::whitespace)) {
+ get_option(specifier::whitespace) = false;
+ get_option(specifier::space) = true;
+ }
+ if (get_option(specifier::numbers)) {
+ get_option(specifier::numbers) = false;
+ get_option(specifier::digit) = true;
+ }
+ }
+ else {
+ auto do_range = [&](char a, char b) {
+ for (; a < b; ++a) {
+ get_option(a) = true;
+ }
+ get_option(b) = true;
+ };
+ auto do_lower = [&]() {
+ // a-z
+ do_range(0x61, 0x7a);
+ };
+ auto do_upper = [&]() {
+ // A-Z
+ do_range(0x41, 0x5a);
+ };
+ auto do_digit = [&]() {
+ // 0-9
+ do_range(0x30, 0x39);
+ };
+
+ if (get_option(specifier::alnum)) {
+ do_lower();
+ do_upper();
+ do_digit();
+ get_option(specifier::alnum) = false;
+ }
+ if (get_option(specifier::alpha)) {
+ do_lower();
+ do_upper();
+ get_option(specifier::alpha) = false;
+ }
+ if (get_option(specifier::blank)) {
+ get_option(' ') = true;
+ get_option('\t') = true;
+ get_option(specifier::blank) = false;
+ }
+ if (get_option(specifier::cntrl)) {
+ do_range(0, 0x1f);
+ get_option(0x7f) = true;
+ get_option(specifier::cntrl) = false;
+ }
+ if (get_option(specifier::digit)) {
+ do_digit();
+ get_option(specifier::digit) = false;
+ }
+ if (get_option(specifier::graph)) {
+ do_range(0x21, 0x7e);
+ get_option(specifier::graph) = false;
+ }
+ if (get_option(specifier::lower)) {
+ do_lower();
+ get_option(specifier::lower) = false;
+ }
+ if (get_option(specifier::print)) {
+ do_range(0x20, 0x7e);
+ get_option(specifier::print) = false;
+ }
+ if (get_option(specifier::punct)) {
+ do_range(0x21, 0x2f);
+ do_range(0x3a, 0x40);
+ do_range(0x5b, 0x60);
+ do_range(0x7b, 0x7e);
+ get_option(specifier::punct) = false;
+ }
+ if (get_option(specifier::space)) {
+ do_range(0x9, 0xd);
+ get_option(' ') = true;
+ get_option(specifier::space) = false;
+ }
+ if (get_option(specifier::upper)) {
+ do_upper();
+ get_option(specifier::upper) = false;
+ }
+ if (get_option(specifier::xdigit)) {
+ do_digit();
+ do_range(0x41, 0x46);
+ do_range(0x61, 0x66);
+ get_option(specifier::xdigit) = false;
+ }
+ if (get_option(specifier::letters)) {
+ do_upper();
+ do_lower();
+ get_option(specifier::letters) = false;
+ }
+ if (get_option(specifier::inverted_letters)) {
+ do_range(0x0, 0x2f);
+ do_range(0x3a, 0x40);
+ do_range(0x5b, 0x60);
+ do_range(0x7b, 0x7f);
+ get_option(specifier::inverted_letters) = false;
+ }
+ if (get_option(specifier::alnum_underscore)) {
+ do_digit();
+ do_upper();
+ do_lower();
+ get_option('_') = true;
+ get_option(specifier::alnum_underscore) = false;
+ }
+ if (get_option(specifier::inverted_alnum_underscore)) {
+ bool underscore = get_option('_');
+ do_range(0x0, 0x2f);
+ do_range(0x3a, 0x40);
+ do_range(0x5b, 0x60);
+ do_range(0x7b, 0x7f);
+ get_option('_') = underscore; // reset back
+ get_option(specifier::inverted_alnum_underscore) =
+ false;
+ }
+ if (get_option(specifier::whitespace)) {
+ do_range(0x9, 0xd);
+ get_option(' ') = true;
+ get_option(specifier::whitespace) = false;
+ }
+ if (get_option(specifier::inverted_whitespace)) {
+ do_range(0, 0x8);
+ do_range(0xe, 0x1f);
+ do_range(0x21, 0x7f);
+ get_option(specifier::inverted_whitespace) = false;
+ }
+ if (get_option(specifier::numbers)) {
+ do_digit();
+ get_option(specifier::numbers) = false;
+ }
+ if (get_option(specifier::inverted_numbers)) {
+ do_range(0, 0x2f);
+ do_range(0x3a, 0x7f);
+ get_option(specifier::inverted_numbers) = false;
+ }
+
+ {
+ bool first = get_option(0);
+ char i = 1;
+ for (; i < 0x7f; ++i) {
+ if (first != get_option(i)) {
+ break;
+ }
+ }
+ if (i == 0x7f && first == get_option(0x7f)) {
+ get_option(flag::accept_all) = true;
+ if (!first) {
+ get_option(flag::inverted) = true;
+ }
+ }
+ }
+
+ get_option(flag::use_specifiers) = false;
+ get_option(flag::use_chars) = true;
+ }
+ }
+
+ return {};
+ }
+
+ // true = char accepted
+ template <typename CharT, typename Locale>
+ bool check_character(CharT ch, bool localized, const Locale& loc)
+ {
+ SCN_EXPECT(get_option(flag::enabled));
+
+ const bool not_inverted = !get_option(flag::inverted);
+ if (get_option(flag::accept_all)) {
+ return not_inverted;
+ }
+
+ if (get_option(flag::use_specifiers)) {
+ SCN_EXPECT(localized); // ensured by sanitize()
+ SCN_UNUSED(localized);
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ if (get_option(specifier::alnum) &&
+ loc.get_localized().is_alnum(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::alpha) &&
+ loc.get_localized().is_alpha(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::blank) &&
+ loc.get_localized().is_blank(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::cntrl) &&
+ loc.get_localized().is_cntrl(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::digit) &&
+ loc.get_localized().is_digit(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::graph) &&
+ loc.get_localized().is_graph(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::lower) &&
+ loc.get_localized().is_lower(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::print) &&
+ loc.get_localized().is_print(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::punct) &&
+ loc.get_localized().is_punct(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::space) &&
+ loc.get_localized().is_space(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::upper) &&
+ loc.get_localized().is_upper(ch)) {
+ return not_inverted;
+ }
+ if (get_option(specifier::xdigit) &&
+ loc.get_localized().is_xdigit(ch)) {
+ return not_inverted;
+ }
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+ if (get_option(flag::use_chars) && (ch >= 0 && ch <= 0x7f)) {
+ if (get_option(static_cast<char>(ch))) {
+ return not_inverted;
+ }
+ }
+ if (get_option(flag::use_ranges)) {
+ const auto c = static_cast<uint32_t>(ch);
+ for (const auto& e : set_extra_ranges) {
+ if (c >= e.begin && c <= e.end) {
+ return not_inverted;
+ }
+ }
+ }
+ return !not_inverted;
+ }
+
+ enum class specifier : size_t {
+ alnum = 0x80,
+ alpha,
+ blank,
+ cntrl,
+ digit,
+ graph,
+ lower,
+ print,
+ punct,
+ space,
+ upper,
+ xdigit,
+ letters = 0x90, // \l
+ inverted_letters, // \L
+ alnum_underscore, // \w
+ inverted_alnum_underscore, // \W
+ whitespace, // \s
+ inverted_whitespace, // \S
+ numbers, // \d
+ inverted_numbers, // \D
+ last = 0x9f
+ };
+ enum class flag : size_t {
+ enabled = 0xa0, // using [set]
+ accept_all, // empty [set]
+ inverted, // ^ flag
+ // 0x00 - 0x7f
+ use_chars,
+ // 0x80 - 0x8f
+ use_specifiers,
+ // set_extra_ranges
+ use_ranges,
+ last = 0xaf
+ };
+
+ bool& get_option(char ch)
+ {
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wtype-limits")
+ SCN_EXPECT(ch >= 0 && ch <= 0x7f);
+ SCN_GCC_POP
+ return set_options[static_cast<size_t>(ch)];
+ }
+ SCN_NODISCARD bool get_option(char ch) const
+ {
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wtype-limits")
+ SCN_EXPECT(ch >= 0 && ch <= 0x7f);
+ SCN_GCC_POP
+ return set_options[static_cast<size_t>(ch)];
+ }
+
+ bool& get_option(specifier s)
+ {
+ return set_options[static_cast<size_t>(s)];
+ }
+ SCN_NODISCARD bool get_option(specifier s) const
+ {
+ return set_options[static_cast<size_t>(s)];
+ }
+
+ bool& get_option(flag f)
+ {
+ return set_options[static_cast<size_t>(f)];
+ }
+ SCN_NODISCARD bool get_option(flag f) const
+ {
+ return set_options[static_cast<size_t>(f)];
+ }
+
+ SCN_NODISCARD bool enabled() const
+ {
+ return get_option(flag::enabled);
+ }
+
+ private:
+ void accept_char(char ch)
+ {
+ get_option(ch) = true;
+ get_option(flag::use_chars) = true;
+ }
+ void accept_char(code_point cp)
+ {
+ if (cp >= 0 && cp <= 0x7f) {
+ return accept_char(static_cast<char>(cp));
+ }
+ set_extra_ranges.push_back(set_range::single(cp));
+ get_option(flag::use_ranges) = true;
+ }
+ void accept_char(wchar_t ch)
+ {
+ SCN_GCC_COMPAT_PUSH
+ SCN_GCC_COMPAT_IGNORE("-Wtype-limits")
+ if (ch >= 0 && ch <= 0x7f) {
+ return accept_char(static_cast<char>(ch));
+ }
+ SCN_GCC_COMPAT_POP
+ set_extra_ranges.push_back(set_range::single(ch));
+ get_option(flag::use_ranges) = true;
+ }
+
+ void accept_char_range(char first, char last)
+ {
+ SCN_EXPECT(first >= 0);
+ SCN_EXPECT(last >= 0);
+ SCN_EXPECT(first <= last);
+ get_option(flag::use_chars) = true;
+ for (; first != last; ++first) {
+ get_option(first) = true;
+ }
+ SCN_ENSURE(first == last);
+ get_option(last) = true;
+ }
+ void accept_char_range(code_point first, code_point last)
+ {
+ SCN_EXPECT(first <= last);
+ if (first >= 0 && last <= 0x7f) {
+ return accept_char_range(static_cast<char>(first),
+ static_cast<char>(last));
+ }
+ set_extra_ranges.push_back(set_range::range(first, last));
+ get_option(flag::use_ranges) = true;
+ }
+ void accept_char_range(wchar_t first, wchar_t last)
+ {
+ SCN_EXPECT(first <= last);
+ SCN_GCC_COMPAT_PUSH
+ SCN_GCC_COMPAT_IGNORE("-Wtype-limits")
+ if (first >= 0 && last <= 0x7f) {
+ return accept_char_range(static_cast<char>(first),
+ static_cast<char>(last));
+ }
+ SCN_GCC_COMPAT_POP
+ set_extra_ranges.push_back(set_range::range(first, last));
+ get_option(flag::use_ranges) = true;
+ }
+
+ template <typename ParseCtx>
+ error parse_range(ParseCtx& pctx, code_point begin)
+ {
+ using char_type = typename ParseCtx::char_type;
+ SCN_EXPECT(pctx.next_char() == ascii_widen<char_type>('-'));
+ if (pctx.can_peek_char() &&
+ pctx.peek_char() == ascii_widen<char_type>(']')) {
+ // Just a '-'
+ accept_char(begin);
+ accept_char(ascii_widen<char_type>('-'));
+ return {};
+ }
+ pctx.advance_char();
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+ return parse_next_char(pctx, false, begin);
+ }
+ template <typename ParseCtx>
+ error parse_literal(ParseCtx& pctx,
+ bool allow_range,
+ code_point begin = make_code_point(0))
+ {
+ using char_type = typename ParseCtx::char_type;
+ if (allow_range) {
+ auto e = pctx.peek_cp();
+ if (!e && e.error().code() != error::end_of_range) {
+ return e.error();
+ }
+ if (e && e.value() == ascii_widen<char_type>('-')) {
+ const auto cp = pctx.next_cp();
+ if (!cp) {
+ return cp.error();
+ }
+ auto err = pctx.advance_cp();
+ if (!err) {
+ return err;
+ }
+ return parse_range(pctx, cp.value());
+ }
+ }
+ const auto cp = pctx.next_cp();
+ if (!cp) {
+ return cp.error();
+ }
+ if (cp.value() >= 0 && cp.value() <= 0x7f) {
+ if (!allow_range) {
+ if (static_cast<
+ typename std::make_unsigned<char_type>::type>(
+ cp.value()) <
+ static_cast<
+ typename std::make_unsigned<char_type>::type>(
+ begin)) {
+ return {error::invalid_format_string,
+ "Last char in [set] range is less than the "
+ "first"};
+ }
+ accept_char_range(begin, cp.value());
+ }
+ else {
+ accept_char(cp.value());
+ }
+ }
+ else {
+ if (!allow_range) {
+ if (static_cast<
+ typename std::make_unsigned<char_type>::type>(
+ cp.value()) <
+ static_cast<
+ typename std::make_unsigned<char_type>::type>(
+ begin)) {
+ return {error::invalid_format_string,
+ "Last char in [set] range is less than the "
+ "first"};
+ }
+ set_extra_ranges.push_back(
+ set_range::range(begin, cp.value()));
+ }
+ else {
+ set_extra_ranges.push_back(
+ set_range::single(cp.value()));
+ }
+ get_option(flag::use_ranges) = true;
+ }
+ return {};
+ }
+ template <typename ParseCtx>
+ error parse_colon_specifier(ParseCtx& pctx)
+ {
+ using char_type = typename ParseCtx::char_type;
+ SCN_EXPECT(pctx.next_char() == ascii_widen<char_type>(':'));
+ pctx.advance_char();
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>(']')) {
+ return {
+ error::invalid_format_string,
+ "Unexpected end of [set] in format string after ':'"};
+ }
+
+ std::basic_string<char_type> buf;
+ while (true) {
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+ auto ch = pctx.next_char();
+ if (ch == ascii_widen<char_type>(':')) {
+ break;
+ }
+ if (ch == ascii_widen<char_type>(']')) {
+ return {error::invalid_format_string,
+ "Unexpected end of [set] :specifier:, did you "
+ "forget a terminating colon?"};
+ }
+ buf.push_back(ch);
+ pctx.advance_char();
+ }
+
+ auto ch = pctx.next_char();
+ if (buf == all_str(ch)) {
+ get_option(flag::accept_all) = true;
+ return {};
+ }
+ if (buf == alnum_str(ch)) {
+ get_option(specifier::alnum) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == alpha_str(ch)) {
+ get_option(specifier::alpha) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == blank_str(ch)) {
+ get_option(specifier::blank) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == cntrl_str(ch)) {
+ get_option(specifier::cntrl) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == digit_str(ch)) {
+ get_option(specifier::digit) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == graph_str(ch)) {
+ get_option(specifier::graph) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == lower_str(ch)) {
+ get_option(specifier::lower) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == print_str(ch)) {
+ get_option(specifier::print) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == punct_str(ch)) {
+ get_option(specifier::punct) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == space_str(ch)) {
+ get_option(specifier::space) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == upper_str(ch)) {
+ get_option(specifier::upper) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (buf == xdigit_str(ch)) {
+ get_option(specifier::xdigit) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+
+ return {error::invalid_format_string,
+ "Invalid :specifier: in [set]"};
+ }
+ template <typename ParseCtx>
+ error parse_backslash_hex(ParseCtx& pctx,
+ bool allow_range,
+ code_point begin = make_code_point(0))
+ {
+ using char_type = typename ParseCtx::char_type;
+ SCN_EXPECT(pctx.next_char() == ascii_widen<char_type>('x') ||
+ pctx.next_char() == ascii_widen<char_type>('u') ||
+ pctx.next_char() == ascii_widen<char_type>('U'));
+
+ const char_type flag_char = pctx.next_char();
+ const int chars = [flag_char]() {
+ auto ch = static_cast<char>(flag_char);
+ if (ch == 'x') {
+ return 2;
+ }
+ if (ch == 'u') {
+ return 4;
+ }
+ if (ch == 'U') {
+ return 8;
+ }
+ SCN_ENSURE(false);
+ SCN_UNREACHABLE;
+ }();
+
+ char_type str[8] = {0};
+ for (int i = 0; i < chars; ++i) {
+ pctx.advance_char();
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument "
+ "after '\\x', '\\u', or '\\U'"};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>(']')) {
+ return {error::invalid_format_string,
+ "Unexpected end of [set] in format string "
+ "after '\\x', '\\u', or '\\U'"};
+ }
+ str[i] = pctx.next_char();
+ }
+
+ auto scanner = simple_integer_scanner<uint64_t>{};
+ uint64_t i;
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto res = scanner.scan(
+ scn::make_span(str, static_cast<size_t>(chars)).as_const(),
+ i, 16);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!res) {
+ return {error::invalid_format_string,
+ "Failed to parse '\\x', '\\u', or '\\U' flag in "
+ "format string"};
+ }
+ const uint64_t min = 0;
+ const uint64_t max = [chars]() {
+ if (chars == 2) {
+ // \x
+ return uint64_t{0x7f};
+ }
+ if (chars == 4) {
+ return uint64_t{0xffff};
+ }
+ if (chars == 8) {
+ return uint64_t{0xffffffff};
+ }
+ SCN_ENSURE(false);
+ SCN_UNREACHABLE;
+ }();
+ if (i < min || i > max) {
+ return {error::invalid_format_string,
+ "'\\x', '\\u', or '\\U' option in format string "
+ "out of range"};
+ }
+
+ if (allow_range && pctx.can_peek_char() &&
+ pctx.peek_char() == ascii_widen<char_type>('-')) {
+ pctx.advance_char();
+ return parse_range(pctx, make_code_point(i));
+ }
+ if (!allow_range) {
+ accept_char_range(begin, make_code_point(i));
+ }
+ else {
+ accept_char(make_code_point(i));
+ }
+ return {};
+ }
+ template <typename ParseCtx>
+ error parse_backslash_specifier(
+ ParseCtx& pctx,
+ bool allow_range,
+ code_point begin = make_code_point(0))
+ {
+ using char_type = typename ParseCtx::char_type;
+ SCN_EXPECT(pctx.next_char() == ascii_widen<char_type>('\\'));
+ pctx.advance_char();
+
+ if (!pctx || pctx.check_arg_end()) {
+ return {error::invalid_format_string,
+ "Unexpected end of format string argument"};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>(']') &&
+ pctx.can_peek_char() &&
+ pctx.peek_char() == ascii_widen<char_type>('}')) {
+ return {error::invalid_format_string,
+ "Unexpected end of [set] in format string"};
+ }
+
+ if (pctx.next_char() == ascii_widen<char_type>('\\')) {
+ // Literal "\\"
+ accept_char(pctx.next_char());
+ return {};
+ }
+
+ // specifiers
+ if (pctx.next_char() == ascii_widen<char_type>('l')) {
+ // \l
+ get_option(specifier::letters) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>('L')) {
+ // \L
+ get_option(specifier::inverted_letters) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+
+ if (pctx.next_char() == ascii_widen<char_type>('w')) {
+ // \w
+ get_option(specifier::alnum_underscore) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>('W')) {
+ // \W
+ get_option(specifier::inverted_alnum_underscore) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+
+ if (pctx.next_char() == ascii_widen<char_type>('s')) {
+ // \s
+ get_option(specifier::whitespace) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>('S')) {
+ // \S
+ get_option(specifier::inverted_whitespace) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+
+ if (pctx.next_char() == ascii_widen<char_type>('d')) {
+ // \d
+ get_option(specifier::numbers) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+ if (pctx.next_char() == ascii_widen<char_type>('D')) {
+ // \D
+ get_option(specifier::inverted_numbers) = true;
+ get_option(flag::use_specifiers) = true;
+ return {};
+ }
+
+ if (pctx.next_char() == ascii_widen<char_type>('x') ||
+ pctx.next_char() == ascii_widen<char_type>('u') ||
+ pctx.next_char() == ascii_widen<char_type>('U')) {
+ // \x__, \u____, or \U________
+ return parse_backslash_hex(pctx, allow_range, begin);
+ }
+
+ // Literal, e.g. \: -> :
+ return parse_literal(pctx, true);
+ }
+ template <typename ParseCtx>
+ error parse_next_char(ParseCtx& pctx,
+ bool allow_range,
+ code_point begin = make_code_point(0))
+ {
+ using char_type = typename ParseCtx::char_type;
+ const auto ch = pctx.next_char();
+ if (ch == ascii_widen<char_type>('\\')) {
+ return parse_backslash_specifier(pctx, allow_range, begin);
+ }
+ if (allow_range && ch == ascii_widen<char_type>(':')) {
+ return parse_colon_specifier(pctx);
+ }
+ return parse_literal(pctx, allow_range, begin);
+ }
+
+ SCN_NODISCARD static constexpr const char* all_str(char)
+ {
+ return "all";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* all_str(wchar_t)
+ {
+ return L"all";
+ }
+ SCN_NODISCARD static constexpr const char* alnum_str(char)
+ {
+ return "alnum";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* alnum_str(wchar_t)
+ {
+ return L"alnum";
+ }
+ SCN_NODISCARD static constexpr const char* alpha_str(char)
+ {
+ return "alpha";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* alpha_str(wchar_t)
+ {
+ return L"alpha";
+ }
+ SCN_NODISCARD static constexpr const char* blank_str(char)
+ {
+ return "blank";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* blank_str(wchar_t)
+ {
+ return L"blank";
+ }
+ SCN_NODISCARD static constexpr const char* cntrl_str(char)
+ {
+ return "cntrl";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* cntrl_str(wchar_t)
+ {
+ return L"cntrl";
+ }
+ SCN_NODISCARD static constexpr const char* digit_str(char)
+ {
+ return "digit";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* digit_str(wchar_t)
+ {
+ return L"digit";
+ }
+ SCN_NODISCARD static constexpr const char* graph_str(char)
+ {
+ return "graph";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* graph_str(wchar_t)
+ {
+ return L"graph";
+ }
+ SCN_NODISCARD static constexpr const char* lower_str(char)
+ {
+ return "lower";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* lower_str(wchar_t)
+ {
+ return L"lower";
+ }
+ SCN_NODISCARD static constexpr const char* print_str(char)
+ {
+ return "print";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* print_str(wchar_t)
+ {
+ return L"print";
+ }
+ SCN_NODISCARD static constexpr const char* punct_str(char)
+ {
+ return "punct";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* punct_str(wchar_t)
+ {
+ return L"punct";
+ }
+ SCN_NODISCARD static constexpr const char* space_str(char)
+ {
+ return "space";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* space_str(wchar_t)
+ {
+ return L"space";
+ }
+ SCN_NODISCARD static constexpr const char* upper_str(char)
+ {
+ return "upper";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* upper_str(wchar_t)
+ {
+ return L"upper";
+ }
+ SCN_NODISCARD static constexpr const char* xdigit_str(char)
+ {
+ return "xdigit";
+ }
+ SCN_NODISCARD static constexpr const wchar_t* xdigit_str(wchar_t)
+ {
+ return L"xdigit";
+ }
+
+ // 0x00 - 0x7f, individual chars, true = accept
+ // 0x80 - 0x9f, specifiers, true = accept (if use_specifiers = true)
+ // 0xa0 - 0xaf, flags
+ array<bool, 0xb0> set_options{{false}};
+
+ struct set_range {
+ constexpr set_range(uint32_t b, uint32_t e) : begin(b), end(e)
+ {
+ }
+
+ uint32_t begin{};
+ uint32_t end{}; // inclusive
+
+ static set_range single(code_point cp)
+ {
+ return {static_cast<uint32_t>(cp),
+ static_cast<uint32_t>(cp)};
+ }
+ static set_range single(wchar_t ch)
+ {
+ return {static_cast<uint32_t>(ch),
+ static_cast<uint32_t>(ch)};
+ }
+
+ static set_range range(code_point begin, code_point end)
+ {
+ SCN_EXPECT(begin <= end);
+ return {static_cast<uint32_t>(begin),
+ static_cast<uint32_t>(end)};
+ }
+ static set_range range(wchar_t begin, wchar_t end)
+ {
+ SCN_EXPECT(begin <= end);
+ return {static_cast<uint32_t>(begin),
+ static_cast<uint32_t>(end)};
+ }
+ };
+ // Used if set_options[use_ranges] = true
+ small_vector<set_range, 1> set_extra_ranges{};
+ };
+
+ struct string_scanner : common_parser {
+ static constexpr bool skip_preceding_whitespace()
+ {
+ return false;
+ }
+
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ using char_type = typename ParseCtx::char_type;
+
+ auto s_flag = detail::ascii_widen<char_type>('s');
+ bool s_set{};
+
+ auto each = [&](ParseCtx& p, bool& parsed) -> error {
+ if (p.next_char() == ascii_widen<char_type>('[')) {
+ if (set_parser.get_option(
+ set_parser_type::flag::enabled)) {
+ return {error::invalid_format_string,
+ "[set] already specified for this argument "
+ "in format string"};
+ }
+ return set_parser.parse_set(p, parsed);
+ }
+ return {};
+ };
+ auto e = parse_common(pctx, span<const char_type>{&s_flag, 1},
+ span<bool>{&s_set, 1}, each);
+ if (!e) {
+ return e;
+ }
+ if (set_parser.enabled()) {
+ bool loc = (common_options & localized) != 0;
+ return set_parser.sanitize(loc);
+ }
+ return {};
+ }
+
+ template <typename Context, typename Allocator>
+ error scan(
+ std::basic_string<typename Context::char_type,
+ std::char_traits<typename Context::char_type>,
+ Allocator>& val,
+ Context& ctx)
+ {
+ if (set_parser.enabled()) {
+ bool loc = (common_options & localized) != 0;
+ bool mb = (loc || set_parser.get_option(
+ set_parser_type::flag::use_ranges)) &&
+ is_multichar_type(typename Context::char_type{});
+ return do_scan(ctx, val,
+ pred<Context>{ctx, set_parser, loc, mb});
+ }
+
+ auto e = skip_range_whitespace(ctx, false);
+ if (!e) {
+ return e;
+ }
+
+ auto is_space_pred = make_is_space_predicate(
+ ctx.locale(), (common_options & localized) != 0,
+ field_width);
+ return do_scan(ctx, val, is_space_pred);
+ }
+
+ set_parser_type set_parser;
+
+ protected:
+ template <typename Context, typename Allocator, typename Pred>
+ error do_scan(
+ Context& ctx,
+ std::basic_string<typename Context::char_type,
+ std::char_traits<typename Context::char_type>,
+ Allocator>& val,
+ Pred&& predicate)
+ {
+ using string_type = std::basic_string<
+ typename Context::char_type,
+ std::char_traits<typename Context::char_type>, Allocator>;
+
+ if (Context::range_type::is_contiguous) {
+ auto s = read_until_space_zero_copy(
+ ctx.range(), SCN_FWD(predicate), false);
+ if (!s) {
+ return s.error();
+ }
+ if (s.value().size() == 0) {
+ return {error::invalid_scanned_value,
+ "Empty string parsed"};
+ }
+ val.assign(s.value().data(), s.value().size());
+ return {};
+ }
+
+ string_type tmp(val.get_allocator());
+ auto outputit = std::back_inserter(tmp);
+ auto ret = read_until_space(ctx.range(), outputit,
+ SCN_FWD(predicate), false);
+ if (SCN_UNLIKELY(!ret)) {
+ return ret;
+ }
+ if (SCN_UNLIKELY(tmp.empty())) {
+ return {error::invalid_scanned_value,
+ "Empty string parsed"};
+ }
+ val = SCN_MOVE(tmp);
+
+ return {};
+ }
+
+ template <typename Context>
+ struct pred {
+ Context& ctx;
+ set_parser_type& set_parser;
+ bool localized;
+ bool multibyte;
+
+ bool operator()(span<const char> ch) const
+ {
+ SCN_EXPECT(ch.size() >= 1);
+ code_point cp{};
+ auto it = parse_code_point(ch.begin(), ch.end(), cp);
+ if (!it) {
+ // todo: is this really a good idea
+ return !set_parser.check_character(ch[0], localized,
+ ctx.locale());
+ }
+ return !set_parser.check_character(cp, localized,
+ ctx.locale());
+ }
+ bool operator()(span<const wchar_t> ch) const
+ {
+ SCN_EXPECT(ch.size() == 1);
+ return !set_parser.check_character(ch[0], localized,
+ ctx.locale());
+ }
+ constexpr bool is_localized() const
+ {
+ return localized;
+ }
+ constexpr bool is_multibyte() const
+ {
+ return multibyte;
+ }
+ };
+ };
+
+ struct span_scanner : public string_scanner {
+ template <typename Context>
+ error scan(span<typename Context::char_type>& val, Context& ctx)
+ {
+ if (val.size() == 0) {
+ return {error::invalid_scanned_value,
+ "Cannot scan into an empty span"};
+ }
+
+ if (set_parser.enabled()) {
+ bool loc = (common_options & localized) != 0;
+ bool mb = (loc || set_parser.get_option(
+ set_parser_type::flag::use_ranges)) &&
+ is_multichar_type(typename Context::char_type{});
+ return do_scan(ctx, val,
+ string_scanner::pred<Context>{
+ ctx, set_parser, loc, mb});
+ }
+
+ auto e = skip_range_whitespace(ctx, false);
+ if (!e) {
+ return e;
+ }
+
+ auto is_space_pred = make_is_space_predicate(
+ ctx.locale(), (common_options & localized) != 0,
+ field_width != 0 ? min(field_width, val.size())
+ : val.size());
+ return do_scan(ctx, val, is_space_pred);
+ }
+
+ protected:
+ template <typename Context, typename Pred>
+ error do_scan(Context& ctx,
+ span<typename Context::char_type>& val,
+ Pred&& predicate)
+ {
+ if (Context::range_type::is_contiguous) {
+ auto s = read_until_space_zero_copy(
+ ctx.range(), SCN_FWD(predicate), false);
+ if (!s) {
+ return s.error();
+ }
+ if (s.value().size() == 0) {
+ return {error::invalid_scanned_value,
+ "Empty string parsed"};
+ }
+ std::copy(s.value().begin(), s.value().end(), val.begin());
+ val = val.first(s.value().size());
+ return {};
+ }
+
+ std::basic_string<typename Context::char_type> tmp;
+ auto outputit = std::back_inserter(tmp);
+ auto ret = read_until_space(ctx.range(), outputit,
+ SCN_FWD(predicate), false);
+ if (SCN_UNLIKELY(!ret)) {
+ return ret;
+ }
+ if (SCN_UNLIKELY(tmp.empty())) {
+ return {error::invalid_scanned_value,
+ "Empty string parsed"};
+ }
+ std::copy(tmp.begin(), tmp.end(), val.begin());
+ val = val.first(tmp.size());
+
+ return {};
+ }
+ };
+
+ struct string_view_scanner : string_scanner {
+ public:
+ template <typename Context>
+ error scan(basic_string_view<typename Context::char_type>& val,
+ Context& ctx)
+ {
+ if (!Context::range_type::is_contiguous) {
+ return {error::invalid_operation,
+ "Cannot read a string_view from a "
+ "non-contiguous_range"};
+ }
+
+ if (set_parser.enabled()) {
+ bool loc = (common_options & localized) != 0;
+ bool mb = (loc || set_parser.get_option(
+ set_parser_type::flag::use_ranges)) &&
+ is_multichar_type(typename Context::char_type{});
+ return do_scan(ctx, val,
+ string_scanner::pred<Context>{
+ ctx, set_parser, loc, mb});
+ }
+
+ auto e = skip_range_whitespace(ctx, false);
+ if (!e) {
+ return e;
+ }
+
+ auto is_space_pred = make_is_space_predicate(
+ ctx.locale(), (common_options & localized) != 0,
+ field_width);
+ return do_scan(ctx, val, is_space_pred);
+ }
+
+ protected:
+ template <typename Context, typename Pred>
+ error do_scan(Context& ctx,
+ basic_string_view<typename Context::char_type>& val,
+ Pred&& predicate)
+ {
+ SCN_EXPECT(Context::range_type::is_contiguous);
+
+ auto s = read_until_space_zero_copy(ctx.range(),
+ SCN_FWD(predicate), false);
+ if (!s) {
+ return s.error();
+ }
+ if (s.value().size() == 0) {
+ return {error::invalid_scanned_value,
+ "Empty string parsed"};
+ }
+ val = basic_string_view<typename Context::char_type>(
+ s.value().data(), s.value().size());
+ return {};
+ }
+ };
+
+#if SCN_HAS_STRING_VIEW
+ struct std_string_view_scanner : string_view_scanner {
+ template <typename Context>
+ error scan(std::basic_string_view<typename Context::char_type>& val,
+ Context& ctx)
+ {
+ using char_type = typename Context::char_type;
+ auto sv =
+ ::scn::basic_string_view<char_type>(val.data(), val.size());
+ auto e = string_view_scanner::scan(sv, ctx);
+ if (e) {
+ val =
+ std::basic_string_view<char_type>(sv.data(), sv.size());
+ }
+ return e;
+ }
+ };
+#endif
+ } // namespace detail
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/reader/types.h b/src/third-party/scnlib/include/scn/reader/types.h
new file mode 100644
index 0000000..047d10d
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/reader/types.h
@@ -0,0 +1,220 @@
+// 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_READER_TYPES_H
+#define SCN_READER_TYPES_H
+
+#include "int.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+ namespace detail {
+ struct code_point_scanner : common_parser {
+ static constexpr bool skip_preceding_whitespace()
+ {
+ return false;
+ }
+
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ using char_type = typename ParseCtx::char_type;
+
+ auto c_flag = detail::ascii_widen<char_type>('c');
+ bool c_set{};
+ return parse_common(pctx, span<const char_type>{&c_flag, 1},
+ span<bool>{&c_set, 1},
+ null_type_cb<ParseCtx>);
+ }
+
+ template <typename Context>
+ error scan(code_point& val, Context& ctx)
+ {
+ unsigned char buf[4] = {0};
+ auto cp = read_code_point(ctx.range(), make_span(buf, 4));
+ if (!cp) {
+ return cp.error();
+ }
+ val = cp.value().cp;
+ return {};
+ }
+ };
+
+ struct bool_scanner : common_parser {
+ template <typename ParseCtx>
+ error parse(ParseCtx& pctx)
+ {
+ using char_type = typename ParseCtx::char_type;
+
+ array<char_type, 3> options{{
+ // Only strings
+ ascii_widen<char_type>('s'),
+ // Only ints
+ ascii_widen<char_type>('i'),
+ // Localized digits
+ ascii_widen<char_type>('n'),
+ }};
+ bool flags[3] = {false};
+ auto e = parse_common(
+ pctx, span<const char_type>{options.begin(), options.end()},
+ span<bool>{flags, 3}, null_type_cb<ParseCtx>);
+
+ if (!e) {
+ return e;
+ }
+
+ format_options = 0;
+ // default ('s' + 'i')
+ if (!flags[0] && !flags[1]) {
+ format_options |= allow_string | allow_int;
+ }
+ // 's'
+ if (flags[0]) {
+ format_options |= allow_string;
+ }
+ // 'i'
+ if (flags[1]) {
+ format_options |= allow_int;
+ }
+ // 'n'
+ if (flags[2]) {
+ format_options |= localized_digits;
+ // 'n' implies 'L'
+ common_options |= localized;
+ }
+ return {};
+ }
+
+ template <typename Context>
+ error scan(bool& val, Context& ctx)
+ {
+ using char_type = typename Context::char_type;
+
+ if ((format_options & allow_string) != 0) {
+ auto truename = ctx.locale().get_static().truename();
+ auto falsename = ctx.locale().get_static().falsename();
+ if ((common_options & localized) != 0) {
+ truename = ctx.locale().get_localized().truename();
+ falsename = ctx.locale().get_localized().falsename();
+ }
+ const auto max_len =
+ detail::max(truename.size(), falsename.size());
+ std::basic_string<char_type> buf;
+ buf.reserve(max_len);
+
+ auto tmp_it = std::back_inserter(buf);
+ auto is_space_pred = make_is_space_predicate(
+ ctx.locale(), (common_options & localized) != 0,
+ field_width);
+ auto e = read_until_space(ctx.range(), tmp_it,
+ is_space_pred, false);
+ if (!e) {
+ return e;
+ }
+
+ bool found = false;
+ if (buf.size() >= falsename.size()) {
+ if (std::equal(falsename.begin(), falsename.end(),
+ buf.begin())) {
+ val = false;
+ found = true;
+ }
+ }
+ if (!found && buf.size() >= truename.size()) {
+ if (std::equal(truename.begin(), truename.end(),
+ buf.begin())) {
+ val = true;
+ found = true;
+ }
+ }
+ if (found) {
+ return {};
+ }
+ else {
+ auto pb =
+ putback_n(ctx.range(),
+ static_cast<std::ptrdiff_t>(buf.size()));
+ if (!pb) {
+ return pb;
+ }
+ }
+ }
+
+ if ((format_options & allow_int) != 0) {
+ if ((format_options & localized_digits) != 0) {
+ int i{};
+ auto s = integer_scanner<int>{};
+ s.common_options = integer_scanner<int>::localized;
+ s.format_options =
+ integer_scanner<int>::only_unsigned |
+ integer_scanner<int>::localized_digits;
+ auto e = s.scan(i, ctx);
+ if (!e) {
+ return e;
+ }
+ if (SCN_UNLIKELY(i != 0 && i != 1)) {
+ return {
+ error::invalid_scanned_value,
+ "Scanned integral boolean not equal to 0 or 1"};
+ }
+ else if (i == 0) {
+ val = false;
+ }
+ else {
+ val = true;
+ }
+ return {};
+ }
+
+ unsigned char buf[4] = {0};
+ auto cp = read_code_point(ctx.range(), make_span(buf, 4));
+ if (!cp) {
+ return cp.error();
+ }
+ if (cp.value().cp == detail::ascii_widen<char_type>('0')) {
+ val = false;
+ return {};
+ }
+ if (cp.value().cp == detail::ascii_widen<char_type>('1')) {
+ val = true;
+ return {};
+ }
+ auto pb = putback_n(ctx.range(), cp.value().chars.ssize());
+ if (!pb) {
+ return pb;
+ }
+ }
+
+ return {error::invalid_scanned_value, "Couldn't scan bool"};
+ }
+
+ enum format_options_type {
+ // 's' option
+ allow_string = 1,
+ // 'i' option
+ allow_int = 2,
+ // 'n' option
+ localized_digits = 4
+ };
+ uint8_t format_options{allow_string | allow_int};
+ };
+
+ } // namespace detail
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/common.h b/src/third-party/scnlib/include/scn/scan/common.h
new file mode 100644
index 0000000..ccdd825
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/common.h
@@ -0,0 +1,131 @@
+// 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_SCAN_COMMON_H
+#define SCN_SCAN_COMMON_H
+
+#include "../detail/locale.h"
+#include "../detail/result.h"
+#include "../unicode/common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename CharT>
+ constexpr int to_format(int i)
+ {
+ return i;
+ }
+ template <typename T>
+ constexpr auto to_format(T&& f) -> decltype(string_view{SCN_FWD(f)})
+ {
+ return {SCN_FWD(f)};
+ }
+ template <typename T>
+ constexpr auto to_format(T&& f) -> decltype(wstring_view{SCN_FWD(f)})
+ {
+ return {SCN_FWD(f)};
+ }
+ template <typename CharT>
+ basic_string_view<CharT> to_format(const std::basic_string<CharT>& str)
+ {
+ return {str.data(), str.size()};
+ }
+
+ template <typename CharT>
+ struct until_pred {
+ array<CharT, 4> until;
+ size_t size;
+
+ constexpr until_pred(CharT ch) : until({{ch}}), size(1) {}
+ until_pred(code_point cp)
+ {
+ auto ret = encode_code_point(until.begin(), until.end(), cp);
+ SCN_ENSURE(ret);
+ size = ret.value() - until.begin();
+ }
+
+ SCN_CONSTEXPR14 bool operator()(span<const CharT> ch) const
+ {
+ if (ch.size() != size) {
+ return false;
+ }
+ for (size_t i = 0; i < ch.size(); ++i) {
+ if (ch[i] != until[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ static constexpr bool is_localized()
+ {
+ return false;
+ }
+ constexpr bool is_multibyte() const
+ {
+ return size != 1;
+ }
+ };
+
+ template <typename Error, typename Range>
+ using generic_scan_result_for_range = decltype(detail::wrap_result(
+ SCN_DECLVAL(Error),
+ SCN_DECLVAL(detail::range_tag<Range>),
+ SCN_DECLVAL(range_wrapper_for_t<Range>)));
+ template <typename Range>
+ using scan_result_for_range =
+ generic_scan_result_for_range<wrapped_error, Range>;
+ } // namespace detail
+
+ template <typename T>
+ struct discard_type {
+ discard_type() = default;
+ };
+
+ /**
+ * Scans an instance of `T`, but doesn't store it anywhere.
+ * Uses `scn::temp` internally, so the user doesn't have to bother.
+ *
+ * \code{.cpp}
+ * int i{};
+ * // 123 is discarded, 456 is read into `i`
+ * auto result = scn::scan("123 456", "{} {}", scn::discard<T>(), i);
+ * // result == true
+ * // i == 456
+ * \endcode
+ */
+ template <typename T>
+ discard_type<T>& discard()
+ {
+ return temp(discard_type<T>{})();
+ }
+
+ template <typename T>
+ struct scanner<discard_type<T>> : public scanner<T> {
+ template <typename Context>
+ error scan(discard_type<T>&, Context& ctx)
+ {
+ T tmp;
+ return scanner<T>::scan(tmp, ctx);
+ }
+ };
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/getline.h b/src/third-party/scnlib/include/scn/scan/getline.h
new file mode 100644
index 0000000..c330969
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/getline.h
@@ -0,0 +1,186 @@
+// 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_SCAN_GETLINE_H
+#define SCN_SCAN_GETLINE_H
+
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename WrappedRange,
+ typename String,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error getline_impl(WrappedRange& r, String& str, Until until)
+ {
+ auto pred = until_pred<CharT>{until};
+ auto s = read_until_space_zero_copy(r, pred, true);
+ if (!s) {
+ return s.error();
+ }
+ if (s.value().size() != 0) {
+ auto size = s.value().size();
+ if (pred(s.value().last(1))) {
+ --size;
+ }
+ str.clear();
+ str.resize(size);
+ std::copy(s.value().begin(), s.value().begin() + size,
+ str.begin());
+ return {};
+ }
+
+ String tmp;
+ auto out = std::back_inserter(tmp);
+ auto e = read_until_space(r, out, pred, true);
+ if (!e) {
+ return e;
+ }
+ if (pred(span<const CharT>(&*(tmp.end() - 1), 1))) {
+ tmp.pop_back();
+ }
+ str = SCN_MOVE(tmp);
+ return {};
+ }
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error getline_impl(WrappedRange& r,
+ basic_string_view<CharT>& str,
+ Until until)
+ {
+ static_assert(
+ WrappedRange::is_contiguous,
+ "Cannot getline a string_view from a non-contiguous range");
+ auto pred = until_pred<CharT>{until};
+ auto s = read_until_space_zero_copy(r, pred, true);
+ if (!s) {
+ return s.error();
+ }
+ SCN_ASSERT(s.value().size(), "");
+ auto size = s.value().size();
+ if (pred(s.value().last(1))) {
+ --size;
+ }
+ str = basic_string_view<CharT>{s.value().data(), size};
+ return {};
+ }
+#if SCN_HAS_STRING_VIEW
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ auto getline_impl(WrappedRange& r,
+ std::basic_string_view<CharT>& str,
+ Until until) -> error
+ {
+ auto sv = ::scn::basic_string_view<CharT>{};
+ auto ret = getline_impl(r, sv, until);
+ str = ::std::basic_string_view<CharT>{sv.data(), sv.size()};
+ return ret;
+ }
+#endif
+ } // namespace detail
+
+ /**
+ * Read the range in \c r into \c str until \c until is found.
+ * \c until will be skipped in parsing: it will not be pushed into \c
+ * str, and the returned range will go past it.
+ *
+ * If `str` is convertible to a `basic_string_view`:
+ * - And if `r` is a `contiguous_range`:
+ * - `str` is set to point inside `r` with the appropriate length
+ * - if not, returns an error
+ *
+ * Otherwise, clears `str` by calling `str.clear()`, and then reads the
+ * range into `str` as if by repeatedly calling \c str.push_back.
+ * `str.reserve()` is also required to be present.
+ *
+ * `Until` can either be the same as `r` character type (`char` or
+ * `wchar_t`), or `code_point`.
+ *
+ * \code{.cpp}
+ * auto source = "hello\nworld"
+ * std::string line;
+ * auto result = scn::getline(source, line, '\n');
+ * // line == "hello"
+ * // result.range() == "world"
+ *
+ * // Using the other overload
+ * result = scn::getline(result.range(), line);
+ * // line == "world"
+ * // result.empty() == true
+ * \endcode
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename String, typename Until>
+ auto getline(Range&& r, String& str, Until until)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename String, typename Until>
+ SCN_NODISCARD auto getline(Range&& r, String& str, Until until)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto wrapped = wrap(SCN_FWD(r));
+ auto err = getline_impl(wrapped, str, until);
+ if (!err) {
+ auto e = wrapped.reset_to_rollback_point();
+ if (!e) {
+ err = e;
+ }
+ }
+ else {
+ wrapped.set_rollback_point();
+ }
+ return detail::wrap_result(
+ wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped));
+ }
+#endif
+
+ /**
+ * Equivalent to \ref getline with the last parameter set to
+ * <tt>'\\n'</tt> with the appropriate character type.
+ *
+ * In other words, reads `r` into `str` until <tt>'\\n'</tt> is found.
+ *
+ * The character type is determined by `r`.
+ */
+#if SCN_DOXYGEN
+ template <typename Range,
+ typename String,
+ typename CharT = typename detail::extract_char_type<
+ ranges::iterator_t<range_wrapper_for_t<Range>>>::type>
+ auto getline(Range&& r, String& str)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range,
+ typename String,
+ typename CharT = typename detail::extract_char_type<
+ ranges::iterator_t<range_wrapper_for_t<Range>>>::type>
+ SCN_NODISCARD auto getline(Range&& r, String& str)
+ -> detail::scan_result_for_range<Range>
+ {
+ return getline(SCN_FWD(r), str, detail::ascii_widen<CharT>('\n'));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/ignore.h b/src/third-party/scnlib/include/scn/scan/ignore.h
new file mode 100644
index 0000000..415a877
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/ignore.h
@@ -0,0 +1,189 @@
+// 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_SCAN_IGNORE_H
+#define SCN_SCAN_IGNORE_H
+
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename CharT>
+ struct ignore_iterator {
+ using value_type = CharT;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::output_iterator_tag;
+
+ constexpr ignore_iterator() = default;
+
+ SCN_CONSTEXPR14 ignore_iterator& operator=(CharT) noexcept
+ {
+ return *this;
+ }
+ constexpr const ignore_iterator& operator=(CharT) const noexcept
+ {
+ return *this;
+ }
+
+ SCN_CONSTEXPR14 ignore_iterator& operator*() noexcept
+ {
+ return *this;
+ }
+ constexpr const ignore_iterator& operator*() const noexcept
+ {
+ return *this;
+ }
+
+ SCN_CONSTEXPR14 ignore_iterator& operator++() noexcept
+ {
+ return *this;
+ }
+ constexpr const ignore_iterator& operator++() const noexcept
+ {
+ return *this;
+ }
+ };
+
+ template <typename CharT>
+ struct ignore_iterator_n {
+ using value_type = CharT;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::output_iterator_tag;
+
+ ignore_iterator_n() = default;
+ ignore_iterator_n(difference_type n) : i(n) {}
+
+ constexpr const ignore_iterator_n& operator=(CharT) const noexcept
+ {
+ return *this;
+ }
+
+ constexpr const ignore_iterator_n& operator*() const noexcept
+ {
+ return *this;
+ }
+
+ SCN_CONSTEXPR14 ignore_iterator_n& operator++() noexcept
+ {
+ ++i;
+ return *this;
+ }
+
+ constexpr bool operator==(const ignore_iterator_n& o) const noexcept
+ {
+ return i == o.i;
+ }
+ constexpr bool operator!=(const ignore_iterator_n& o) const noexcept
+ {
+ return !(*this == o);
+ }
+
+ difference_type i{0};
+ };
+
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error ignore_until_impl(WrappedRange& r, Until until)
+ {
+ ignore_iterator<CharT> it{};
+ return read_until_space(r, it, until_pred<CharT>{until}, false);
+ }
+
+ template <typename WrappedRange,
+ typename Until,
+ typename CharT = typename WrappedRange::char_type>
+ error ignore_until_n_impl(WrappedRange& r,
+ ranges::range_difference_t<WrappedRange> n,
+ Until until)
+ {
+ ignore_iterator_n<CharT> begin{}, end{n};
+ return read_until_space_ranged(r, begin, end,
+ until_pred<CharT>{until}, false);
+ }
+ } // namespace detail
+
+ /**
+ * Advances the beginning of \c r until \c until is found.
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Until>
+ auto ignore_until(Range&& r, Until until)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Until>
+ SCN_NODISCARD auto ignore_until(Range&& r, Until until)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto wrapped = wrap(SCN_FWD(r));
+ auto err = detail::ignore_until_impl(wrapped, until);
+ if (!err) {
+ auto e = wrapped.reset_to_rollback_point();
+ if (!e) {
+ err = e;
+ }
+ }
+ else {
+ wrapped.set_rollback_point();
+ }
+ return detail::wrap_result(
+ wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped));
+ }
+#endif
+
+ /**
+ * Advances the beginning of \c r until \c until is found, or the
+ * beginning has been advanced \c n times.
+ *
+ * `Until` can be the `r` character type (`char` or `wchar_t`), or
+ * `code_point`.
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Until>
+ auto ignore_until_n(Range&& r,
+ ranges::range_difference_t<Range> n,
+ Until until) -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Until>
+ SCN_NODISCARD auto ignore_until_n(Range&& r,
+ ranges::range_difference_t<Range> n,
+ Until until)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto wrapped = wrap(SCN_FWD(r));
+ auto err = detail::ignore_until_n_impl(wrapped, n, until);
+ if (!err) {
+ auto e = wrapped.reset_to_rollback_point();
+ if (!e) {
+ err = e;
+ }
+ }
+ return detail::wrap_result(
+ wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/istream.h b/src/third-party/scnlib/include/scn/scan/istream.h
new file mode 100644
index 0000000..a9aef86
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/istream.h
@@ -0,0 +1,147 @@
+// 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_SCAN_ISTREAM_H
+#define SCN_SCAN_ISTREAM_H
+
+#include "../reader/common.h"
+#include "../detail/result.h"
+
+#include <istream>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename WrappedRange>
+ class range_streambuf
+ : public std::basic_streambuf<typename WrappedRange::char_type> {
+ using base = std::basic_streambuf<typename WrappedRange::char_type>;
+
+ public:
+ using range_type = WrappedRange;
+ using char_type = typename WrappedRange::char_type;
+ using traits_type = typename base::traits_type;
+ using int_type = typename base::int_type;
+
+ explicit range_streambuf(range_type& r) : m_range(std::addressof(r))
+ {
+ }
+
+ private:
+ int_type underflow() override
+ {
+ // already read
+ if (!traits_type::eq_int_type(m_ch, traits_type::eof())) {
+ return m_ch;
+ }
+
+ auto ret = read_code_unit(*m_range);
+ if (!ret) {
+ // error
+ // m_ch is already eof
+ return traits_type::eof();
+ }
+ m_ch = traits_type::to_int_type(ret.value());
+ return m_ch;
+ }
+ int_type uflow() override
+ {
+ auto ret = underflow();
+ if (ret != traits_type::eof()) {
+ m_ch = traits_type::eof();
+ }
+ return ret;
+ }
+ std::streamsize showmanyc() override
+ {
+ return traits_type::eq_int_type(m_ch, traits_type::eof()) ? 0
+ : 1;
+ }
+ int_type pbackfail(int_type) override
+ {
+ auto e = putback_n(*m_range, 1);
+ if (!e) {
+ return traits_type::eof();
+ }
+ return traits_type::to_int_type(0);
+ }
+
+ range_type* m_range;
+ int_type m_ch{traits_type::eof()};
+ };
+
+ // Trick stolen from {fmt}
+ template <typename CharT>
+ struct test_std_stream : std::basic_istream<CharT> {
+ private:
+ struct null;
+ // Hide all operator>> from std::basic_istream<CharT>
+ void operator>>(null);
+ };
+
+ // Check for user-defined operator>>
+ template <typename CharT, typename T, typename = void>
+ struct is_std_streamable : std::false_type {
+ };
+
+ template <typename CharT, typename T>
+ struct is_std_streamable<
+ CharT,
+ T,
+ void_t<decltype(SCN_DECLVAL(test_std_stream<CharT>&) >>
+ SCN_DECLVAL(T&))>> : std::true_type {
+ };
+ } // namespace detail
+
+ template <typename T>
+ struct scanner<T,
+ typename std::enable_if<
+ detail::is_std_streamable<char, T>::value ||
+ detail::is_std_streamable<wchar_t, T>::value>::type>
+ : public empty_parser {
+ template <typename Context>
+ error scan(T& val, Context& ctx)
+ {
+ static_assert(detail::is_std_streamable<typename Context::char_type,
+ T>::value,
+ "Type can not be read from a basic_istream of this "
+ "character type");
+ detail::range_streambuf<typename Context::range_type> streambuf(
+ ctx.range());
+ std::basic_istream<typename Context::char_type> stream(
+ std::addressof(streambuf));
+
+ if (!(stream >> val)) {
+ if (stream.eof()) {
+ return {error::end_of_range, "EOF"};
+ }
+ if (stream.bad()) {
+ return {error::unrecoverable_source_error,
+ "Bad std::istream after reading"};
+ }
+ return {error::invalid_scanned_value,
+ "Failed to read with std::istream"};
+ }
+ return {};
+ }
+ };
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_ISTREAM_H
diff --git a/src/third-party/scnlib/include/scn/scan/list.h b/src/third-party/scnlib/include/scn/scan/list.h
new file mode 100644
index 0000000..a5eeaf5
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/list.h
@@ -0,0 +1,450 @@
+// 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_SCAN_LIST_H
+#define SCN_SCAN_LIST_H
+
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * Adapts a `span` into a type that can be read into using \ref
+ * scan_list. This way, potentially unnecessary dynamic memory
+ * allocations can be avoided. To use as a parameter to \ref scan_list,
+ * use \ref make_span_list_wrapper.
+ *
+ * \code{.cpp}
+ * std::vector<int> buffer(8, 0);
+ * scn::span<int> s = scn::make_span(buffer);
+ *
+ * auto wrapper = scn::span_list_wrapper<int>(s);
+ * scn::scan_list("123 456", wrapper);
+ * // s[0] == buffer[0] == 123
+ * // s[1] == buffer[1] == 456
+ * \endcode
+ *
+ * \see scan_list
+ * \see make_span_list_wrapper
+ */
+ template <typename T>
+ struct span_list_wrapper {
+ using value_type = T;
+
+ span_list_wrapper(span<T> s) : m_span(s) {}
+
+ void push_back(T val)
+ {
+ SCN_EXPECT(n < max_size());
+ m_span[n] = SCN_MOVE(val);
+ ++n;
+ }
+
+ SCN_NODISCARD constexpr std::size_t size() const noexcept
+ {
+ return n;
+ }
+ SCN_NODISCARD constexpr std::size_t max_size() const noexcept
+ {
+ return m_span.size();
+ }
+
+ span<T> m_span;
+ std::size_t n{0};
+ };
+
+ namespace detail {
+ template <typename T>
+ using span_list_wrapper_for =
+ span_list_wrapper<typename decltype(make_span(
+ SCN_DECLVAL(T&)))::value_type>;
+ }
+
+ /**
+ * Adapts a contiguous buffer into a type containing a `span` that can
+ * be read into using \ref scn::scan_list.
+ *
+ * Example adapted from \ref span_list_wrapper:
+ * \code{.cpp}
+ * std::vector<int> buffer(8, 0);
+ * scn::scan_list("123 456", scn::make_span_list_wrapper(buffer));
+ * // s[0] == buffer[0] == 123
+ * // s[1] == buffer[1] == 456
+ * \endcode
+ *
+ * \see scan_list
+ * \see span_list_wrapper
+ */
+ template <typename T>
+ auto make_span_list_wrapper(T& s)
+ -> temporary<detail::span_list_wrapper_for<T>>
+ {
+ auto _s = make_span(s);
+ return temp(span_list_wrapper<typename decltype(_s)::value_type>(_s));
+ }
+
+ /**
+ * Used to customize `scan_list_ex()`.
+ *
+ * \tparam CharT Can be a code unit type (`char` or `wchar_t`, depending on
+ * the source range), or `code_point`.
+ *
+ * `list_separator`, `list_until` and `list_separator_and_until` can be used
+ * to create a value of this type, taking advantage of template argument
+ * deduction (no need to hand-specify `CharT`).
+ */
+ template <typename CharT>
+ struct scan_list_options {
+ /**
+ * If set, up to one separator character can be accepted between values,
+ * which may be surrounded by whitespace.
+ */
+ optional<CharT> separator{};
+ /**
+ * If set, reading the list is stopped if this character is found
+ * between values.
+ *
+ * In that case, it is advanced over, and no error is returned.
+ */
+ optional<CharT> until{};
+
+ scan_list_options() = default;
+ scan_list_options(optional<CharT> s, optional<CharT> u)
+ : separator(SCN_MOVE(s)), until(SCN_MOVE(u))
+ {
+ }
+ };
+
+ /**
+ * Create a `scan_list_options` for `scan_list_ex`, by using `ch` as the
+ * separator character.
+ */
+ template <typename CharT>
+ scan_list_options<CharT> list_separator(CharT ch)
+ {
+ return {optional<CharT>{ch}, nullopt};
+ }
+ /**
+ * Create a `scan_list_options` for `scan_list_ex`, by using `ch` as the
+ * until-character.
+ */
+ template <typename CharT>
+ scan_list_options<CharT> list_until(CharT ch)
+ {
+ return {nullopt, optional<CharT>{ch}};
+ }
+ /**
+ * Create a `scan_list_options` for `scan_list_ex`, by using `sep` as the
+ * separator, and `until` as the until-character.
+ */
+ template <typename CharT>
+ scan_list_options<CharT> list_separator_and_until(CharT sep, CharT until)
+ {
+ return {optional<CharT>{sep}, optional<CharT>{until}};
+ }
+
+ namespace detail {
+ template <typename WrappedRange, typename CharT>
+ expected<CharT> check_separator(WrappedRange& r, size_t& n, CharT)
+ {
+ auto ret = read_code_unit(r);
+ if (!ret) {
+ return ret.error();
+ }
+ n = 1;
+ return ret.value();
+ }
+ template <typename WrappedRange>
+ expected<code_point> check_separator(WrappedRange& r,
+ size_t& n,
+ code_point)
+ {
+ unsigned char buf[4] = {0};
+ auto ret = read_code_point(r, make_span(buf, 4));
+ if (!ret) {
+ return ret.error();
+ }
+ n = ret.value().chars.size();
+ return ret.value().cp;
+ }
+
+ template <typename Context, typename Container, typename Separator>
+ auto scan_list_impl(Context& ctx,
+ bool localized,
+ Container& c,
+ scan_list_options<Separator> options) -> error
+ {
+ using char_type = typename Context::char_type;
+ using value_type = typename Container::value_type;
+ value_type value;
+
+ auto args = make_args_for(ctx.range(), 1, value);
+
+ bool scanning = true;
+ while (scanning) {
+ if (c.size() == c.max_size()) {
+ break;
+ }
+
+ // read value
+ auto pctx = make_parse_context(1, ctx.locale(), localized);
+ auto err = visit(ctx, pctx, basic_args<char_type>{args});
+ if (!err) {
+ if (err == error::end_of_range) {
+ break;
+ }
+ return err;
+ }
+ c.push_back(SCN_MOVE(value));
+
+ auto next = static_cast<Separator>(0);
+ size_t n{0};
+
+ auto read_next = [&]() -> error {
+ auto ret = check_separator(ctx.range(), n,
+ static_cast<Separator>(0));
+ if (!ret) {
+ if (ret.error() == error::end_of_range) {
+ scanning = false;
+ return {};
+ }
+ return ret.error();
+ }
+ next = ret.value();
+
+ err =
+ putback_n(ctx.range(), static_cast<std::ptrdiff_t>(n));
+ if (!err) {
+ return err;
+ }
+
+ return {};
+ };
+
+ bool sep_found = false;
+ while (true) {
+ // read until
+ if (options.until) {
+ err = read_next();
+ if (!err) {
+ return err;
+ }
+ if (!scanning) {
+ break;
+ }
+
+ if (next == options.until.get()) {
+ scanning = false;
+ break;
+ }
+ }
+
+ // read sep
+ if (options.separator && !sep_found) {
+ err = read_next();
+ if (!err) {
+ return err;
+ }
+ if (!scanning) {
+ break;
+ }
+
+ if (next == options.separator.get()) {
+ // skip to next char
+ ctx.range().advance(static_cast<std::ptrdiff_t>(n));
+ continue;
+ }
+ }
+
+ err = read_next();
+ if (!err) {
+ return err;
+ }
+ if (!scanning) {
+ break;
+ }
+
+ if (ctx.locale().get_static().is_space(next)) {
+ // skip ws
+ ctx.range().advance(static_cast<std::ptrdiff_t>(n));
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return {};
+ }
+ } // namespace detail
+
+ /**
+ * Reads values repeatedly from `r` and writes them into `c`.
+ *
+ * The values read are of type `Container::value_type`, and they are
+ * written into `c` using `c.push_back`.
+ * The values are separated by whitespace.
+ *
+ * The range is read, until:
+ * - `c.max_size()` is reached, or
+ * - range `EOF` is reached
+ *
+ * In these cases, an error will not be returned, and the beginning
+ * of the returned range will point to the first character after the
+ * scanned list.
+ *
+ * If an invalid value is scanned, `error::invalid_scanned_value` is
+ * returned, but the values already in `vec` will remain there. The range is
+ * put back to the state it was before reading the invalid value.
+ *
+ * To scan into `span`, use \ref span_list_wrapper.
+ * \ref make_span_list_wrapper
+ *
+ * \code{.cpp}
+ * std::vector<int> vec{};
+ * auto result = scn::scan_list("123 456", vec);
+ * // vec == [123, 456]
+ * // result.empty() == true
+ *
+ * vec.clear();
+ * result = scn::scan_list("123 456 abc", vec);
+ * // vec == [123, 456]
+ * // result.error() == invalid_scanned_value
+ * // result.range() == " abc"
+ * \endcode
+ *
+ * \param r Range to read from
+ * \param c Container to write values to, using `c.push_back()`.
+ * `Container::value_type` will be used to determine the type of the values
+ * to read.
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Container>
+ auto scan_list(Range&& r, Container& c)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Container>
+ SCN_NODISCARD auto scan_list(Range&& r, Container& c)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto range = wrap(SCN_FWD(r));
+ auto ctx = make_context(SCN_MOVE(range));
+ using char_type = typename decltype(ctx)::char_type;
+
+ auto err = detail::scan_list_impl(ctx, false, c,
+ scan_list_options<char_type>{});
+
+ return detail::wrap_result(wrapped_error{err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ctx.range()));
+ }
+#endif
+
+ /**
+ * Otherwise equivalent to `scan_list()`, except can react to additional
+ * characters, based on `options`.
+ *
+ * See `scan_list_options` for more information.
+ *
+ * \param r Range to scan from
+ * \param c Container to write read values into
+ * \param options Options to use
+ *
+ * \code{.cpp}
+ * std::vector<int> vec{};
+ * auto result = scn::scan_list_ex("123, 456", vec,
+ * scn::list_separator(','));
+ * // vec == [123, 456]
+ * // result.empty() == true
+ * \endcode
+ *
+ * \see scan_list
+ * \see scan_list_options
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Container, typename CharT>
+ auto scan_list_ex(Range&& r, Container& c, scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Container, typename CharT>
+ SCN_NODISCARD auto scan_list_ex(Range&& r,
+ Container& c,
+ scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto range = wrap(SCN_FWD(r));
+ auto ctx = make_context(SCN_MOVE(range));
+
+ auto err = detail::scan_list_impl(ctx, false, c, options);
+
+ return detail::wrap_result(wrapped_error{err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ctx.range()));
+ }
+#endif
+
+ /**
+ * Otherwise equivalent to `scan_list_ex()`, except uses `loc` to scan the
+ * values.
+ *
+ * \param loc Locale to use for scanning. Must be a `std::locale`.
+ * \param r Range to scan from
+ * \param c Container to write read values into
+ * \param options Options to use
+ *
+ * \see scan_list_ex()
+ * \see scan_localized()
+ */
+#if SCN_DOXYGEN
+ template <typename Locale,
+ typename Range,
+ typename Container,
+ typename CharT>
+ auto scan_list_localized(const Locale& loc,
+ Range&& r,
+ Container& c,
+ scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Locale,
+ typename Range,
+ typename Container,
+ typename CharT>
+ SCN_NODISCARD auto scan_list_localized(const Locale& loc,
+ Range&& r,
+ Container& c,
+ scan_list_options<CharT> options)
+ -> detail::scan_result_for_range<Range>
+ {
+ auto range = wrap(SCN_FWD(r));
+ using char_type = typename decltype(range)::char_type;
+ auto locale = make_locale_ref<char_type>(loc);
+ auto ctx = make_context(SCN_MOVE(range), SCN_MOVE(locale));
+
+ auto err = detail::scan_list_impl(ctx, true, c, options);
+
+ return detail::wrap_result(wrapped_error{err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ctx.range()));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/scan/scan.h b/src/third-party/scnlib/include/scn/scan/scan.h
new file mode 100644
index 0000000..20f4cd1
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/scan.h
@@ -0,0 +1,444 @@
+// 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_SCAN_SCAN_H
+#define SCN_SCAN_SCAN_H
+
+#include "../util/optional.h"
+#include "common.h"
+#include "vscan.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace dummy {
+ }
+
+ /**
+ * \tparam OriginalRange The type of the range passed to the scanning
+ * function \param result Return value of `vscan` \return Result object
+ *
+ * \code{.cpp}
+ * template <typename Range, typename... Args>
+ * auto scan(Range&& r, string_view f, Args&... a) {
+ * auto range = scn::wrap(std::forward<Range>(r));
+ * auto args = scn::make_args_for(range, f, a...);
+ * auto ret = scn::vscan(std::move(range), f, {args});
+ * return scn::make_scan_result<Range>(std::move(ret));
+ * }
+ * \endcode
+ */
+ template <typename OriginalRange,
+ typename Error = wrapped_error,
+ typename WrappedRange>
+ auto make_scan_result(vscan_result<WrappedRange> result)
+ -> detail::scan_result_for_range<OriginalRange>
+ {
+ return detail::wrap_result(Error{result.err},
+ detail::range_tag<OriginalRange>{},
+ SCN_MOVE(result.range));
+ }
+
+ namespace detail {
+ template <typename Range, typename Format, typename... Args>
+ auto scan_boilerplate(Range&& r, const Format& f, Args&... a)
+ -> detail::scan_result_for_range<Range>
+ {
+ static_assert(sizeof...(Args) > 0,
+ "Have to scan at least a single argument");
+ static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
+ "Input needs to be a Range");
+
+ auto range = wrap(SCN_FWD(r));
+ auto format = detail::to_format(f);
+ auto args = make_args_for(range, format, a...);
+ auto ret = vscan(SCN_MOVE(range), format, {args});
+ return make_scan_result<Range>(SCN_MOVE(ret));
+ }
+
+ template <typename Range, typename... Args>
+ auto scan_boilerplate_default(Range&& r, Args&... a)
+ -> detail::scan_result_for_range<Range>
+ {
+ static_assert(sizeof...(Args) > 0,
+ "Have to scan at least a single argument");
+ static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
+ "Input needs to be a Range");
+
+ auto range = wrap(SCN_FWD(r));
+ auto format = static_cast<int>(sizeof...(Args));
+ auto args = make_args_for(range, format, a...);
+ auto ret = vscan_default(SCN_MOVE(range), format, {args});
+ return make_scan_result<Range>(SCN_MOVE(ret));
+ }
+
+ template <typename Locale,
+ typename Range,
+ typename Format,
+ typename... Args>
+ auto scan_boilerplate_localized(const Locale& loc,
+ Range&& r,
+ const Format& f,
+ Args&... a)
+ -> detail::scan_result_for_range<Range>
+ {
+ static_assert(sizeof...(Args) > 0,
+ "Have to scan at least a single argument");
+ static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>),
+ "Input needs to be a Range");
+
+ auto range = wrap(SCN_FWD(r));
+ auto format = detail::to_format(f);
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto locale =
+ make_locale_ref<typename decltype(range)::char_type>(loc);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+
+ auto args = make_args_for(range, format, a...);
+ auto ret = vscan_localized(SCN_MOVE(range), SCN_MOVE(locale),
+ format, {args});
+ return make_scan_result<Range>(SCN_MOVE(ret));
+ }
+
+ } // namespace detail
+
+ // scan
+
+ // For some reason, Doxygen dislikes SCN_NODISCARD
+
+ /**
+ * The most fundamental part of the scanning API.
+ * Reads from the range in \c r according to the format string \c f.
+ *
+ * \code{.cpp}
+ * int i;
+ * scn::scan("123", "{}", i);
+ * // i == 123
+ * \endcode
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename Format, typename... Args>
+ auto scan(Range&& r, const Format& f, Args&... a)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename Format, typename... Args>
+ SCN_NODISCARD auto scan(Range&& r, const Format& f, Args&... a)
+ -> detail::scan_result_for_range<Range>
+ {
+ return detail::scan_boilerplate(SCN_FWD(r), f, a...);
+ }
+#endif
+
+ // default format
+
+ /**
+ * Equivalent to \ref scan, but with a
+ * format string with the appropriate amount of space-separated `"{}"`s for
+ * the number of arguments. Because this function doesn't have to parse the
+ * format string, performance is improved.
+ *
+ * Adapted from the example for \ref scan
+ * \code{.cpp}
+ * int i;
+ * scn::scan_default("123", i);
+ * // i == 123
+ * \endcode
+ *
+ * \see scan
+ */
+#if SCN_DOXYGEN
+ template <typename Range, typename... Args>
+ auto scan_default(Range&& r, Args&... a)
+ -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Range, typename... Args>
+ SCN_NODISCARD auto scan_default(Range&& r, Args&... a)
+ -> detail::scan_result_for_range<Range>
+ {
+ return detail::scan_boilerplate_default(std::forward<Range>(r), a...);
+ }
+#endif
+
+ // scan localized
+
+ /**
+ * Read from the range in \c r using the locale in \c loc.
+ * \c loc must be a \c std::locale. The parameter is a template to avoid
+ * inclusion of `<locale>`.
+ *
+ * Use of this function is discouraged, due to the overhead involved
+ * with locales. Note, that the other functions are completely
+ * locale-agnostic, and aren't affected by changes to the global C
+ * locale.
+ *
+ * \code{.cpp}
+ * double d;
+ * scn::scan_localized(std::locale{"fi_FI"}, "3,14", "{}", d);
+ * // d == 3.14
+ * \endcode
+ *
+ * \see scan
+ */
+#if SCN_DOXYGEN
+ template <typename Locale,
+ typename Range,
+ typename Format,
+ typename... Args>
+ auto scan_localized(const Locale& loc,
+ Range&& r,
+ const Format& f,
+ Args&... a) -> detail::scan_result_for_range<Range>;
+#else
+ template <typename Locale,
+ typename Range,
+ typename Format,
+ typename... Args>
+ SCN_NODISCARD auto scan_localized(const Locale& loc,
+ Range&& r,
+ const Format& f,
+ Args&... a)
+ -> detail::scan_result_for_range<Range>
+ {
+ return detail::scan_boilerplate_localized(loc, std::forward<Range>(r),
+ f, a...);
+ }
+#endif
+
+ // value
+
+ /**
+ * Scans a single value with the default options, returning it instead of
+ * using an output parameter.
+ *
+ * The parsed value is in `ret.value()`, if `ret == true`.
+ * The return type of this function is otherwise similar to other scanning
+ * functions.
+ *
+ * \code{.cpp}
+ * auto ret = scn::scan_value<int>("42");
+ * if (ret) {
+ * // ret.value() == 42
+ * }
+ * \endcode
+ */
+#if SCN_DOXYGEN
+ template <typename T, typename Range>
+ auto scan_value(Range&& r)
+ -> detail::generic_scan_result_for_range<expected<T>, Range>;
+#else
+ template <typename T, typename Range>
+ SCN_NODISCARD auto scan_value(Range&& r)
+ -> detail::generic_scan_result_for_range<expected<T>, Range>
+ {
+ T value;
+ auto range = wrap(SCN_FWD(r));
+ auto args = make_args_for(range, 1, value);
+ auto ret = vscan_default(SCN_MOVE(range), 1, {args});
+ if (ret.err) {
+ return detail::wrap_result(expected<T>{value},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ret.range));
+ }
+ return detail::wrap_result(expected<T>{ret.err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ret.range));
+ }
+#endif
+
+ // input
+
+ /**
+ * Otherwise equivalent to \ref scan, expect reads from `stdin`.
+ * Character type is determined by the format string.
+ * Syncs with `<cstdio>`.
+ */
+ template <typename Format,
+ typename... Args,
+ typename CharT = ranges::range_value_t<Format>>
+#if SCN_DOXYGEN
+ auto input(const Format& f, Args&... a)
+ -> detail::scan_result_for_range<basic_file<CharT>&>;
+#else
+ SCN_NODISCARD auto input(const Format& f, Args&... a)
+ -> detail::scan_result_for_range<basic_file<CharT>&>
+ {
+ auto& range = stdin_range<CharT>();
+ auto ret = detail::scan_boilerplate(range, f, a...);
+ range.sync();
+ ret.range().reset_begin_iterator();
+ return ret;
+ }
+#endif
+
+ // prompt
+
+ namespace detail {
+ inline void put_stdout(const char* str)
+ {
+ std::fputs(str, stdout);
+ }
+ inline void put_stdout(const wchar_t* str)
+ {
+ std::fputws(str, stdout);
+ }
+ } // namespace detail
+
+ /**
+ * Equivalent to \ref input, except writes what's in `p` to `stdout`.
+ *
+ * \code{.cpp}
+ * int i{};
+ * scn::prompt("What's your favorite number? ", "{}", i);
+ * // Equivalent to:
+ * // std::fputs("What's your favorite number? ", stdout);
+ * // scn::input("{}", i);
+ * \endcode
+ */
+#if SCN_DOXYGEN
+ template <typename CharT, typename Format, typename... Args>
+ auto prompt(const CharT* p, const Format& f, Args&... a)
+ -> detail::scan_result_for_range<basic_file<CharT>&>;
+#else
+ template <typename CharT, typename Format, typename... Args>
+ SCN_NODISCARD auto prompt(const CharT* p, const Format& f, Args&... a)
+ -> detail::scan_result_for_range<basic_file<CharT>&>
+ {
+ SCN_EXPECT(p != nullptr);
+ detail::put_stdout(p);
+
+ return input(f, a...);
+ }
+#endif
+
+ // parse_integer
+
+ /**
+ * Parses an integer into \c val in base \c base from \c str.
+ * Returns a pointer past the last character read, or an error.
+ *
+ * @param str source, can't be empty, cannot have:
+ * - preceding whitespace
+ * - preceding \c "0x" or \c "0" (base is determined by the \c base
+ * parameter)
+ * - \c '+' sign (\c '-' is fine)
+ * @param val parsed integer, must be value-constructed
+ * @param base between [2,36]
+ */
+#if SCN_DOXYGEN
+ template <typename T, typename CharT>
+ expected<const CharT*> parse_integer(basic_string_view<CharT> str,
+ T& val,
+ int base = 10);
+#else
+ template <typename T, typename CharT>
+ SCN_NODISCARD expected<const CharT*>
+ parse_integer(basic_string_view<CharT> str, T& val, int base = 10)
+ {
+ SCN_EXPECT(!str.empty());
+ auto s = detail::simple_integer_scanner<T>{};
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto ret =
+ s.scan_lower(span<const CharT>(str.data(), str.size()), val, base);
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!ret) {
+ return ret.error();
+ }
+ return {ret.value()};
+ }
+#endif
+
+ /**
+ * Parses float into \c val from \c str.
+ * Returns a pointer past the last character read, or an error.
+ *
+ * @param str source, can't be empty
+ * @param val parsed float, must be value-constructed
+ */
+#if SCN_DOXYGEN
+ template <typename T, typename CharT>
+ expected<const CharT*> parse_float(basic_string_view<CharT> str, T& val);
+#else
+ template <typename T, typename CharT>
+ SCN_NODISCARD expected<const CharT*> parse_float(
+ basic_string_view<CharT> str,
+ T& val)
+ {
+ SCN_EXPECT(!str.empty());
+ auto s = detail::float_scanner_access<T>{};
+ auto ret = s._read_float(val, make_span(str.data(), str.size()),
+ detail::ascii_widen<CharT>('.'));
+ if (!ret) {
+ return ret.error();
+ }
+ return {str.data() + ret.value()};
+ }
+#endif
+
+ /**
+ * A convenience function for creating scanners for user-provided types.
+ *
+ * Wraps \ref vscan_usertype
+ *
+ * Example use:
+ *
+ * \code{.cpp}
+ * // Type has two integers, and its textual representation is
+ * // "[val1, val2]"
+ * struct user_type {
+ * int val1;
+ * int val2;
+ * };
+ *
+ * template <>
+ * struct scn::scanner<user_type> : public scn::empty_parser {
+ * template <typename Context>
+ * error scan(user_type& val, Context& ctx)
+ * {
+ * return scan_usertype(ctx, "[{}, {}]", val.val1, val.val2);
+ * }
+ * };
+ * \endcode
+ *
+ * \param ctx Context given to the scanning function
+ * \param f Format string to parse
+ * \param a Member types (etc) to parse
+ */
+#if SCN_DOXYGEN
+ template <typename WrappedRange, typename Format, typename... Args>
+ error scan_usertype(basic_context<WrappedRange>& ctx,
+ const Format& f,
+ Args&... a);
+#else
+ template <typename WrappedRange, typename Format, typename... Args>
+ SCN_NODISCARD error scan_usertype(basic_context<WrappedRange>& ctx,
+ const Format& f,
+ Args&... a)
+ {
+ static_assert(sizeof...(Args) > 0,
+ "Have to scan at least a single argument");
+
+ using char_type = typename WrappedRange::char_type;
+ auto args = make_args<basic_context<WrappedRange>,
+ basic_parse_context<char_type>>(a...);
+ return vscan_usertype(ctx, basic_string_view<char_type>(f), {args});
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_DETAIL_SCAN_H
diff --git a/src/third-party/scnlib/include/scn/scan/vscan.h b/src/third-party/scnlib/include/scn/scan/vscan.h
new file mode 100644
index 0000000..86bf23e
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scan/vscan.h
@@ -0,0 +1,208 @@
+// 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_SCAN_VSCAN_H
+#define SCN_SCAN_VSCAN_H
+
+#include "../detail/context.h"
+#include "../detail/file.h"
+#include "../detail/parse_context.h"
+#include "../detail/visitor.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ // Avoid documentation issues: without this, Doxygen will think
+ // SCN_BEGIN_NAMESPACE is a part of the vscan declaration
+ namespace dummy {
+ }
+
+ /**
+ * Type returned by `vscan` and others
+ */
+ template <typename WrappedRange>
+ struct vscan_result {
+ error err;
+ WrappedRange range;
+ };
+
+ namespace detail {
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_boilerplate(
+ WrappedRange&& r,
+ basic_string_view<CharT> fmt,
+ basic_args<CharT> args)
+ {
+ auto ctx = make_context(SCN_MOVE(r));
+ auto pctx = make_parse_context(fmt, ctx.locale());
+ auto err = visit(ctx, pctx, SCN_MOVE(args));
+ return {err, SCN_MOVE(ctx.range())};
+ }
+
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_boilerplate_default(
+ WrappedRange&& r,
+ int n_args,
+ basic_args<CharT> args)
+ {
+ auto ctx = make_context(SCN_MOVE(r));
+ auto pctx = make_parse_context(n_args, ctx.locale());
+ auto err = visit(ctx, pctx, SCN_MOVE(args));
+ return {err, SCN_MOVE(ctx.range())};
+ }
+
+ template <typename WrappedRange,
+ typename Format,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_boilerplate_localized(
+ WrappedRange&& r,
+ basic_locale_ref<CharT>&& loc,
+ const Format& fmt,
+ basic_args<CharT> args)
+ {
+ auto ctx = make_context(SCN_MOVE(r), SCN_MOVE(loc));
+ auto pctx = make_parse_context(fmt, ctx.locale());
+ auto err = visit(ctx, pctx, SCN_MOVE(args));
+ return {err, SCN_MOVE(ctx.range())};
+ }
+ } // namespace detail
+
+ /**
+ * In the spirit of {fmt}/`std::format` and `vformat`, `vscan` behaves
+ * similarly to \ref scan, except instead of taking a variadic argument
+ * pack, it takes an object of type `basic_args`, which type-erases the
+ * arguments to scan. This, in effect, will decrease generated code size and
+ * compile times dramatically.
+ *
+ * \param range Source range that has been wrapped with `detail::wrap`, and
+ * passed in as an rvalue.
+ * \param fmt Format string to use
+ * \param args Type-erased values to read
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan(WrappedRange range,
+ basic_string_view<CharT> fmt,
+ basic_args<CharT>&& args)
+ {
+ return detail::vscan_boilerplate(SCN_MOVE(range), fmt, SCN_MOVE(args));
+ }
+
+ /**
+ * To be used with `scan_default`
+ *
+ * \param range Source range that has been wrapped with `detail::wrap`, and
+ * passed in as an rvalue.
+ * \param n_args Number of arguments in args
+ * \param args Type-erased values to read
+ *
+ * \see vscan
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_default(WrappedRange range,
+ int n_args,
+ basic_args<CharT>&& args)
+ {
+ return detail::vscan_boilerplate_default(SCN_MOVE(range), n_args,
+ SCN_MOVE(args));
+ }
+
+ /**
+ * To be used with `scan_localized`
+ *
+ * \param loc Locale to use
+ * \param range Source range that has been wrapped with `detail::wrap`, and
+ * passed in as an rvalue.
+ * \param fmt Format string to use
+ * \param args Type-erased values to read
+ *
+ * \see vscan
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ vscan_result<WrappedRange> vscan_localized(WrappedRange range,
+ basic_locale_ref<CharT>&& loc,
+ basic_string_view<CharT> fmt,
+ basic_args<CharT>&& args)
+ {
+ return detail::vscan_boilerplate_localized(
+ SCN_MOVE(range), SCN_MOVE(loc), fmt, SCN_MOVE(args));
+ }
+
+ /**
+ * \see scan_usertype
+ * \see vscan
+ */
+ template <typename WrappedRange,
+ typename CharT = typename WrappedRange::char_type>
+ error vscan_usertype(basic_context<WrappedRange>& ctx,
+ basic_string_view<CharT> f,
+ basic_args<CharT>&& args)
+ {
+ auto pctx = make_parse_context(f, ctx.locale());
+ return visit(ctx, pctx, SCN_MOVE(args));
+ }
+
+#if !defined(SCN_HEADER_ONLY) || !SCN_HEADER_ONLY
+
+#define SCN_VSCAN_DECLARE(Range, WrappedAlias, CharAlias) \
+ namespace detail { \
+ namespace vscan_macro { \
+ using WrappedAlias = range_wrapper_for_t<Range>; \
+ using CharAlias = typename WrappedAlias::char_type; \
+ } \
+ } \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan( \
+ detail::vscan_macro::WrappedAlias&&, \
+ basic_string_view<detail::vscan_macro::CharAlias>, \
+ basic_args<detail::vscan_macro::CharAlias>&&); \
+ \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan_default( \
+ detail::vscan_macro::WrappedAlias&&, int, \
+ basic_args<detail::vscan_macro::CharAlias>&&); \
+ \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan_localized( \
+ detail::vscan_macro::WrappedAlias&&, \
+ basic_locale_ref<detail::vscan_macro::CharAlias>&&, \
+ basic_string_view<detail::vscan_macro::CharAlias>, \
+ basic_args<detail::vscan_macro::CharAlias>&&); \
+ \
+ error vscan_usertype(basic_context<detail::vscan_macro::WrappedAlias>&, \
+ basic_string_view<detail::vscan_macro::CharAlias>, \
+ basic_args<detail::vscan_macro::CharAlias>&&)
+
+ SCN_VSCAN_DECLARE(string_view, string_view_wrapped, string_view_char);
+ SCN_VSCAN_DECLARE(wstring_view, wstring_view_wrapped, wstring_view_char);
+ SCN_VSCAN_DECLARE(std::string, string_wrapped, string_char);
+ SCN_VSCAN_DECLARE(std::wstring, wstring_wrapped, wstring_char);
+ SCN_VSCAN_DECLARE(file&, file_ref_wrapped, file_ref_char);
+ SCN_VSCAN_DECLARE(wfile&, wfile_ref_wrapped, wfile_ref_char);
+
+#endif // !SCN_HEADER_ONLY
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_VSCAN_CPP)
+#include "vscan.cpp"
+#endif
+
+#endif // SCN_SCAN_VSCAN_H
diff --git a/src/third-party/scnlib/include/scn/scn.h b/src/third-party/scnlib/include/scn/scn.h
new file mode 100644
index 0000000..ad4e062
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/scn.h
@@ -0,0 +1,26 @@
+// 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_SCN_H
+#define SCN_SCN_H
+
+#include "scan/scan.h"
+#include "scan/getline.h"
+#include "scan/ignore.h"
+#include "scan/list.h"
+
+#endif // SCN_SCN_H
diff --git a/src/third-party/scnlib/include/scn/tuple_return.h b/src/third-party/scnlib/include/scn/tuple_return.h
new file mode 100644
index 0000000..fc5ecb0
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/tuple_return.h
@@ -0,0 +1,23 @@
+// 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_TUPLE_RETURN_H
+#define SCN_TUPLE_RETURN_H
+
+#include "tuple_return/tuple_return.h"
+
+#endif // SCN_TUPLE_RETURN_H
diff --git a/src/third-party/scnlib/include/scn/tuple_return/tuple_return.h b/src/third-party/scnlib/include/scn/tuple_return/tuple_return.h
new file mode 100644
index 0000000..91d6254
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/tuple_return/tuple_return.h
@@ -0,0 +1,123 @@
+// 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_TUPLE_RETURN_TUPLE_RETURN_H
+#define SCN_TUPLE_RETURN_TUPLE_RETURN_H
+
+#include "../scan/vscan.h"
+#include "util.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace dummy {
+ }
+
+/**
+ * Alternative interface for scanning, returning values as a tuple, instead
+ * of taking them by reference.
+ *
+ * It's highly recommended to use this interface only with C++17 or later,
+ * as structured bindings make it way more ergonomic.
+ *
+ * Compared to the regular scan interface, the performance of this interface
+ * is the same (generated code is virtually identical with optimizations
+ * enabled), but the compile time is slower.
+ *
+ * Values scanned by this function still need to be default-constructible.
+ * To scan a non-default-constructible value, use \c scn::optional
+ *
+ * \param r Input range
+ * \param f Format string to use
+ *
+ * \return Tuple, where the first element is the scan result, and the
+ * remaining elements are the scanned values.
+ */
+#if SCN_DOXYGEN
+ template <typename... Args, typename Range, typename Format>
+ auto scan_tuple(Range&& r, Format f)
+ -> std::tuple<detail::scan_result_for_range<Range>, Args...>;
+#else
+ template <typename... Args, typename Range, typename Format>
+ SCN_NODISCARD auto scan_tuple(Range&& r, Format f)
+ -> std::tuple<detail::scan_result_for_range<Range>, Args...>
+ {
+ using result = detail::scan_result_for_range<Range>;
+ using range_type = typename result::wrapped_range_type;
+
+ using context_type = basic_context<range_type>;
+ using parse_context_type =
+ basic_parse_context<typename context_type::locale_type>;
+ using char_type = typename range_type::char_type;
+
+ auto range = wrap(SCN_FWD(r));
+ auto scanfn = [&range, &f](Args&... a) {
+ auto args = make_args<context_type, parse_context_type>(a...);
+ return vscan(SCN_MOVE(range), basic_string_view<char_type>(f),
+ {args});
+ };
+
+ std::tuple<Args...> values{Args{}...};
+ auto ret = detail::apply(scanfn, values);
+ return std::tuple_cat(
+ std::tuple<result>{detail::wrap_result(wrapped_error{ret.err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ret.range))},
+ SCN_MOVE(values));
+ }
+#endif
+
+ /**
+ * Equivalent to `scan_tuple`, except uses `vscan_default` under the hood.
+ */
+#if SCN_DOXYGEN
+ template <typename... Args, typename Range>
+ auto scan_tuple_default(Range&& r)
+ -> std::tuple<detail::scan_result_for_range<Range>, Args...>;
+#else
+ template <typename... Args, typename Range>
+ SCN_NODISCARD auto scan_tuple_default(Range&& r)
+ -> std::tuple<detail::scan_result_for_range<Range>, Args...>
+ {
+ using result = detail::scan_result_for_range<Range>;
+ using range_type = typename result::wrapped_range_type;
+
+ using context_type = basic_context<range_type>;
+ using parse_context_type =
+ basic_empty_parse_context<typename context_type::locale_type>;
+
+ auto range = wrap(SCN_FWD(r));
+ auto scanfn = [&range](Args&... a) {
+ auto args = make_args<context_type, parse_context_type>(a...);
+ return vscan_default(SCN_MOVE(range),
+ static_cast<int>(sizeof...(Args)), {args});
+ };
+
+ std::tuple<Args...> values{Args{}...};
+ auto ret = detail::apply(scanfn, values);
+ return std::tuple_cat(
+ std::tuple<result>{detail::wrap_result(wrapped_error{ret.err},
+ detail::range_tag<Range>{},
+ SCN_MOVE(ret.range))},
+ SCN_MOVE(values));
+ }
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/tuple_return/util.h b/src/third-party/scnlib/include/scn/tuple_return/util.h
new file mode 100644
index 0000000..be0e2ab
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/tuple_return/util.h
@@ -0,0 +1,176 @@
+// 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_TUPLE_RETURN_UTIL_H
+#define SCN_TUPLE_RETURN_UTIL_H
+
+#include "../util/meta.h"
+
+#include <functional>
+#include <tuple>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ // From cppreference
+ template <typename Fn,
+ typename... Args,
+ typename std::enable_if<std::is_member_pointer<
+ typename std::decay<Fn>::type>::value>::type* = nullptr,
+ int = 0>
+ constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
+ noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...))
+ {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+ }
+
+ template <typename Fn,
+ typename... Args,
+ typename std::enable_if<!std::is_member_pointer<
+ typename std::decay<Fn>::type>::value>::type* = nullptr>
+ constexpr auto invoke(Fn&& f, Args&&... args) noexcept(
+ noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...))
+ {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+ }
+
+ // From Boost.mp11
+ template <typename T, T... I>
+ struct integer_sequence {
+ };
+
+ // iseq_if_c
+ template <bool C, typename T, typename E>
+ struct iseq_if_c_impl;
+
+ template <typename T, typename E>
+ struct iseq_if_c_impl<true, T, E> {
+ using type = T;
+ };
+
+ template <typename T, typename E>
+ struct iseq_if_c_impl<false, T, E> {
+ using type = E;
+ };
+
+ template <bool C, typename T, typename E>
+ using iseq_if_c = typename iseq_if_c_impl<C, T, E>::type;
+
+ // iseq_identity
+ template <typename T>
+ struct iseq_identity {
+ using type = T;
+ };
+
+ template <typename S1, typename S2>
+ struct append_integer_sequence;
+
+ template <typename T, T... I, T... J>
+ struct append_integer_sequence<integer_sequence<T, I...>,
+ integer_sequence<T, J...>> {
+ using type = integer_sequence<T, I..., (J + sizeof...(I))...>;
+ };
+
+ template <typename T, T N>
+ struct make_integer_sequence_impl;
+
+ template <typename T, T N>
+ struct make_integer_sequence_impl_ {
+ private:
+ static_assert(
+ N >= 0,
+ "make_integer_sequence<T, N>: N must not be negative");
+
+ static T const M = N / 2;
+ static T const R = N % 2;
+
+ using S1 = typename make_integer_sequence_impl<T, M>::type;
+ using S2 = typename append_integer_sequence<S1, S1>::type;
+ using S3 = typename make_integer_sequence_impl<T, R>::type;
+ using S4 = typename append_integer_sequence<S2, S3>::type;
+
+ public:
+ using type = S4;
+ };
+
+ template <typename T, T N>
+ struct make_integer_sequence_impl
+ : iseq_if_c<N == 0,
+ iseq_identity<integer_sequence<T>>,
+ iseq_if_c<N == 1,
+ iseq_identity<integer_sequence<T, 0>>,
+ make_integer_sequence_impl_<T, N>>> {
+ };
+
+ // make_integer_sequence
+ template <typename T, T N>
+ using make_integer_sequence =
+ typename detail::make_integer_sequence_impl<T, N>::type;
+
+ // index_sequence
+ template <std::size_t... I>
+ using index_sequence = integer_sequence<std::size_t, I...>;
+
+ // make_index_sequence
+ template <std::size_t N>
+ using make_index_sequence = make_integer_sequence<std::size_t, N>;
+
+ // index_sequence_for
+ template <typename... T>
+ using index_sequence_for =
+ make_integer_sequence<std::size_t, sizeof...(T)>;
+
+ // From cppreference
+ template <class F, class Tuple, std::size_t... I>
+ constexpr auto
+ apply_impl(F&& f, Tuple&& t, index_sequence<I...>) noexcept(
+ noexcept(detail::invoke(std::forward<F>(f),
+ std::get<I>(std::forward<Tuple>(t))...)))
+ -> decltype(detail::invoke(std::forward<F>(f),
+ std::get<I>(std::forward<Tuple>(t))...))
+ {
+ return detail::invoke(std::forward<F>(f),
+ std::get<I>(std::forward<Tuple>(t))...);
+ } // namespace detail
+
+ template <class F, class Tuple>
+ constexpr auto apply(F&& f, Tuple&& t) noexcept(
+ noexcept(detail::apply_impl(
+ std::forward<F>(f),
+ std::forward<Tuple>(t),
+ make_index_sequence<std::tuple_size<
+ typename std::remove_reference<Tuple>::type>::value>{})))
+ -> decltype(detail::apply_impl(
+ std::forward<F>(f),
+ std::forward<Tuple>(t),
+ make_index_sequence<std::tuple_size<
+ typename std::remove_reference<Tuple>::type>::value>{}))
+ {
+ return detail::apply_impl(
+ std::forward<F>(f), std::forward<Tuple>(t),
+ make_index_sequence<std::tuple_size<
+ typename std::remove_reference<Tuple>::type>::value>{});
+ }
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/unicode/common.h b/src/third-party/scnlib/include/scn/unicode/common.h
new file mode 100644
index 0000000..3807793
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/unicode/common.h
@@ -0,0 +1,139 @@
+// 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
+//
+// The contents of this file are based on utfcpp:
+// https://github.com/nemtrif/utfcpp
+// Copyright (c) 2006 Nemanja Trifunovic
+// Distributed under the Boost Software License, version 1.0
+
+#ifndef SCN_UNICODE_COMMON_H
+#define SCN_UNICODE_COMMON_H
+
+#include "../detail/fwd.h"
+
+#include <cstdint>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * A Unicode code point
+ */
+ enum class code_point : uint32_t {};
+
+ template <typename T>
+ constexpr bool operator==(code_point a, T b)
+ {
+ return static_cast<uint32_t>(a) == static_cast<uint32_t>(b);
+ }
+ template <typename T>
+ constexpr bool operator!=(code_point a, T b)
+ {
+ return static_cast<uint32_t>(a) != static_cast<uint32_t>(b);
+ }
+ template <typename T>
+ constexpr bool operator<(code_point a, T b)
+ {
+ return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
+ }
+ template <typename T>
+ constexpr bool operator>(code_point a, T b)
+ {
+ return static_cast<uint32_t>(a) > static_cast<uint32_t>(b);
+ }
+ template <typename T>
+ constexpr bool operator<=(code_point a, T b)
+ {
+ return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
+ }
+ template <typename T>
+ constexpr bool operator>=(code_point a, T b)
+ {
+ return static_cast<uint32_t>(a) >= static_cast<uint32_t>(b);
+ }
+
+ namespace detail {
+ static constexpr const uint16_t lead_surrogate_min = 0xd800;
+ static constexpr const uint16_t lead_surrogate_max = 0xdbff;
+ static constexpr const uint16_t trail_surrogate_min = 0xdc00;
+ static constexpr const uint16_t trail_surrogate_max = 0xdfff;
+ static constexpr const uint16_t lead_offset =
+ lead_surrogate_min - (0x10000u >> 10);
+ static constexpr const uint32_t surrogate_offset =
+ 0x10000u - (lead_surrogate_min << 10) - trail_surrogate_min;
+ static constexpr const uint32_t code_point_max = 0x10ffff;
+
+ template <typename Octet>
+ constexpr uint8_t mask8(Octet o)
+ {
+ return static_cast<uint8_t>(0xff & o);
+ }
+ template <typename U16>
+ constexpr uint16_t mask16(U16 v)
+ {
+ return static_cast<uint16_t>(0xffff & v);
+ }
+ template <typename U16>
+ constexpr bool is_lead_surrogate(U16 cp)
+ {
+ return cp >= lead_surrogate_min && cp <= lead_surrogate_max;
+ }
+ template <typename U16>
+ constexpr bool is_trail_surrogate(U16 cp)
+ {
+ return cp >= trail_surrogate_min && cp <= trail_surrogate_max;
+ }
+ template <typename U16>
+ constexpr bool is_surrogate(U16 cp)
+ {
+ return cp >= lead_surrogate_min && cp <= trail_surrogate_max;
+ }
+
+ constexpr inline bool is_code_point_valid(code_point cp)
+ {
+ return cp <= code_point_max && !is_surrogate(cp);
+ }
+ } // namespace detail
+
+ template <typename T>
+ constexpr code_point make_code_point(T ch)
+ {
+ return static_cast<code_point>(ch);
+ }
+
+ /**
+ * Returns `true`, if `cp` is valid, e.g. is less than or equal to the
+ * maximum value for a code point (U+10FFFF), and is not a surrogate (U+D800
+ * to U+DFFF).
+ */
+ constexpr inline bool is_valid_code_point(code_point cp)
+ {
+ return detail::is_code_point_valid(cp);
+ }
+ /**
+ * Returns `true` if `cp` can be encoded in ASCII as-is (is between U+0 and
+ * U+7F)
+ */
+ constexpr inline bool is_ascii_code_point(code_point cp)
+ {
+ return cp <= 0x7f;
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/unicode/unicode.h b/src/third-party/scnlib/include/scn/unicode/unicode.h
new file mode 100644
index 0000000..011b0b9
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/unicode/unicode.h
@@ -0,0 +1,243 @@
+// 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
+//
+// The contents of this file are based on utfcpp:
+// https://github.com/nemtrif/utfcpp
+// Copyright (c) 2006 Nemanja Trifunovic
+// Distributed under the Boost Software License, version 1.0
+
+#ifndef SCN_UNICODE_UNICODE_H
+#define SCN_UNICODE_UNICODE_H
+
+#include "utf16.h"
+#include "utf8.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ inline constexpr bool is_wide_multichar()
+ {
+ return sizeof(wchar_t) == 2;
+ }
+
+ inline constexpr bool is_multichar_type(char)
+ {
+ return true;
+ }
+ inline constexpr bool is_multichar_type(wchar_t)
+ {
+ return is_wide_multichar();
+ }
+
+ using utf8_tag = std::integral_constant<size_t, 1>;
+ using utf16_tag = std::integral_constant<size_t, 2>;
+ using utf32_tag = std::integral_constant<size_t, 4>;
+
+#define SCN_MAKE_UTF_TAG(CharT) \
+ std::integral_constant<size_t, sizeof(CharT)> {}
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> parse_code_point(I begin,
+ S end,
+ code_point& cp,
+ utf8_tag)
+ {
+ return utf8::parse_code_point(begin, end, cp);
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> parse_code_point(I begin,
+ S end,
+ code_point& cp,
+ utf16_tag)
+ {
+ return utf16::parse_code_point(begin, end, cp);
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> parse_code_point(I begin,
+ S end,
+ code_point& cp,
+ utf32_tag)
+ {
+ SCN_EXPECT(begin != end);
+ cp = make_code_point(*begin);
+ return {++begin};
+ }
+ } // namespace detail
+
+ /**
+ * Parses a Unicode code point from the range at `[begin, end)`, and writes
+ * it into `cp`.
+ *
+ * The encoding is determined by the size of the value type of the range.
+ * Let `n = sizeof(typename std::iterator_traits<I>::value_type)`.
+ * If `n == 1` -> UTF-8. If `n == 2` -> UTF-16. If `n == 4` -> UTF-32.
+ *
+ * `begin != end` must be `true`.
+ *
+ * On error, `cp` is not written into.
+ *
+ * \return On success, returns an iterator one-past the last code unit used
+ * to parse `cp`. If the code point is encoded incorrectly, returns
+ * `error::invalid_encoding`.
+ */
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> parse_code_point(I begin, S end, code_point& cp)
+ {
+ return detail::parse_code_point(
+ begin, end, cp,
+ SCN_MAKE_UTF_TAG(typename std::iterator_traits<I>::value_type));
+ }
+
+ namespace detail {
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> encode_code_point(I begin,
+ S end,
+ code_point cp,
+ utf8_tag)
+ {
+ return utf8::encode_code_point(begin, end, cp);
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> encode_code_point(I begin,
+ S end,
+ code_point cp,
+ utf16_tag)
+ {
+ return utf16::encode_code_point(begin, end, cp);
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> encode_code_point(I begin,
+ S end,
+ code_point cp,
+ utf32_tag)
+ {
+ SCN_EXPECT(begin + 1 >= end);
+ *begin++ = static_cast<uint32_t>(cp);
+ return {begin};
+ }
+ } // namespace detail
+
+ /**
+ * Writes the code point `cp` into `begin`, using the encoding determined by
+ * the type of `begin`.
+ *
+ * For more information on how the encoding is determined, see \ref
+ * parse_code_point().
+ *
+ * `end` must be reachable from `begin`, and must have enough room to encode
+ * the code point (4 code units for UTF-8, 2 for UTF-16, and 1 for UTF-32).
+ *
+ * \param begin Beginning of the range to write the result to
+ * \param end End of the range to write the result to
+ * \param cp Code point to encode
+ * \return On success, one-past the last code unit written.
+ * If `cp` was not a valid code point, returns `error::invalid_encoding`.
+ */
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> encode_code_point(I begin, S end, code_point cp)
+ {
+ return detail::encode_code_point(
+ begin, end, cp,
+ SCN_MAKE_UTF_TAG(typename std::iterator_traits<I>::value_type));
+ }
+
+ namespace detail {
+ template <typename T>
+ SCN_CONSTEXPR14 int get_sequence_length(T a, utf8_tag)
+ {
+ return utf8::get_sequence_length(a);
+ }
+ template <typename T>
+ SCN_CONSTEXPR14 int get_sequence_length(T a, utf16_tag)
+ {
+ return utf16::get_sequence_length(a);
+ }
+ template <typename T>
+ SCN_CONSTEXPR14 int get_sequence_length(T, utf32_tag)
+ {
+ return 1;
+ }
+ } // namespace detail
+
+ /**
+ * Returns the length of the code point starting from code unit `a` in code
+ * units.
+ *
+ * For information on how the encoding is determined, see \ref
+ * parse_code_point().
+ *
+ * \param a The first code unit in a code point.
+ *
+ * \return Length of the code point starting from `a`, in code units
+ * If the code point is encoded incorrectly, or this code unit is not the
+ * first code unit in a code point, returns 0.
+ */
+ template <typename T>
+ SCN_CONSTEXPR14 int get_sequence_length(T a)
+ {
+ return detail::get_sequence_length(a, SCN_MAKE_UTF_TAG(T));
+ }
+
+ namespace detail {
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<std::ptrdiff_t> code_point_distance(I begin,
+ S end,
+ utf8_tag)
+ {
+ return utf8::code_point_distance(begin, end);
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<std::ptrdiff_t> code_point_distance(I begin,
+ S end,
+ utf16_tag)
+ {
+ return utf16::code_point_distance(begin, end);
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<std::ptrdiff_t> code_point_distance(I begin,
+ S end,
+ utf32_tag)
+ {
+ return {end - begin};
+ }
+ } // namespace detail
+
+ /**
+ * Get the distance between two code points, in code points.
+ *
+ * `end >= begin` must be `true`.
+ * `begin` and `end` must both point to the first code units in a code
+ * point.
+ *
+ * \return The distance between `begin` and `end`, in code points. If the
+ * string was encoded incorrectly, returns `error::invalid_encoding`.
+ */
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<std::ptrdiff_t> code_point_distance(I begin, S end)
+ {
+ return detail::code_point_distance(
+ begin, end,
+ SCN_MAKE_UTF_TAG(typename std::iterator_traits<I>::value_type));
+ }
+
+#undef SCN_MAKE_UTF_TAG
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/unicode/utf16.h b/src/third-party/scnlib/include/scn/unicode/utf16.h
new file mode 100644
index 0000000..8d8a400
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/unicode/utf16.h
@@ -0,0 +1,139 @@
+// 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
+//
+// The contents of this file are based on utfcpp:
+// https://github.com/nemtrif/utfcpp
+// Copyright (c) 2006 Nemanja Trifunovic
+// Distributed under the Boost Software License, version 1.0
+
+#ifndef SCN_UNICODE_UTF16_H
+#define SCN_UNICODE_UTF16_H
+
+#include "../detail/error.h"
+#include "../util/expected.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ namespace utf16 {
+ template <typename U16>
+ SCN_CONSTEXPR14 int get_sequence_length(U16 ch)
+ {
+ uint16_t lead = mask16(ch);
+ if (is_lead_surrogate(lead)) {
+ return 2;
+ }
+ if (SCN_UNLIKELY(is_trail_surrogate(lead))) {
+ return 0;
+ }
+ return 1;
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error validate_next(I& it, S end, code_point& cp)
+ {
+ SCN_EXPECT(it != end);
+
+ uint16_t lead = mask16(*it);
+ if (is_lead_surrogate(lead)) {
+ ++it;
+ if (it == end) {
+ return {error::invalid_encoding,
+ "Lone utf16 lead surrogate"};
+ }
+ uint16_t trail = mask16(*it);
+ if (!is_trail_surrogate(trail)) {
+ return {error::invalid_encoding,
+ "Invalid utf16 trail surrogate"};
+ }
+ ++it;
+ cp = static_cast<code_point>(
+ static_cast<uint32_t>(lead << 10u) + trail +
+ surrogate_offset);
+ return {};
+ }
+ if (is_trail_surrogate(lead)) {
+ return {error::invalid_encoding,
+ "Lone utf16 trail surrogate"};
+ }
+
+ cp = static_cast<code_point>(*it);
+ ++it;
+ return {};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> parse_code_point(I begin,
+ S end,
+ code_point& cp)
+ {
+ auto e = validate_next(begin, end, cp);
+ if (!e) {
+ return e;
+ }
+ return {begin};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> encode_code_point(I begin,
+ S end,
+ code_point cp)
+ {
+ SCN_EXPECT(begin + 2 <= end);
+
+ if (!is_valid_code_point(cp)) {
+ return error(error::invalid_encoding,
+ "Invalid code point, cannot encode in UTF-16");
+ }
+
+ if (cp > 0xffffu) {
+ *begin++ = static_cast<uint16_t>(
+ (static_cast<uint32_t>(cp) >> 10u) + lead_offset);
+ *begin++ = static_cast<uint16_t>(
+ (static_cast<uint32_t>(cp) & 0x3ffu) +
+ trail_surrogate_min);
+ }
+ else {
+ *begin++ = static_cast<uint16_t>(cp);
+ }
+ return {begin};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<std::ptrdiff_t> code_point_distance(
+ I begin,
+ S end)
+ {
+ std::ptrdiff_t dist{};
+ code_point cp{};
+ for (; begin < end; ++dist) {
+ auto e = validate_next(begin, end, cp);
+ if (!e) {
+ return e;
+ }
+ }
+ return {dist};
+ }
+ } // namespace utf16
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/unicode/utf8.h b/src/third-party/scnlib/include/scn/unicode/utf8.h
new file mode 100644
index 0000000..d2ee54d
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/unicode/utf8.h
@@ -0,0 +1,297 @@
+// 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
+//
+// The contents of this file are based on utfcpp:
+// https://github.com/nemtrif/utfcpp
+// Copyright (c) 2006 Nemanja Trifunovic
+// Distributed under the Boost Software License, version 1.0
+
+#ifndef SCN_UNICODE_UTF8_H
+#define SCN_UNICODE_UTF8_H
+
+#include "../detail/error.h"
+#include "../util/expected.h"
+#include "common.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ namespace utf8 {
+ template <typename Octet>
+ constexpr bool is_trail(Octet o)
+ {
+ return (mask8(o) >> 6) == 2;
+ }
+
+ template <typename Octet>
+ SCN_CONSTEXPR14 int get_sequence_length(Octet ch)
+ {
+ uint8_t lead = detail::mask8(ch);
+ if (lead < 0x80) {
+ return 1;
+ }
+ else if ((lead >> 5) == 6) {
+ return 2;
+ }
+ else if ((lead >> 4) == 0xe) {
+ return 3;
+ }
+ else if ((lead >> 3) == 0x1e) {
+ return 4;
+ }
+ return 0;
+ }
+
+ SCN_CONSTEXPR14 bool is_overlong_sequence(code_point cp,
+ std::ptrdiff_t len)
+ {
+ if (cp < 0x80) {
+ if (len != 1) {
+ return true;
+ }
+ }
+ else if (cp < 0x800) {
+ if (len != 2) {
+ return true;
+ }
+ }
+ else if (cp < 0x10000) {
+ if (len != 3) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error increase_safely(I& it, S end)
+ {
+ if (++it == end) {
+ return {error::invalid_encoding,
+ "Unexpected end of range when decoding utf8 "
+ "(partial codepoint)"};
+ }
+ if (!is_trail(*it)) {
+ return {error::invalid_encoding,
+ "Invalid utf8 codepoint parsed"};
+ }
+ return {};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error get_sequence_1(I& it, S end, code_point& cp)
+ {
+ SCN_EXPECT(it != end);
+ cp = make_code_point(mask8(*it));
+ return {};
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error get_sequence_2(I& it, S end, code_point& cp)
+ {
+ SCN_EXPECT(it != end);
+ uint32_t c = mask8(*it);
+
+ auto e = increase_safely(it, end);
+ if (!e) {
+ return e;
+ }
+
+ c = static_cast<uint32_t>((c << 6u) & 0x7ffu) +
+ (static_cast<uint32_t>(*it) & 0x3fu);
+ cp = make_code_point(c);
+
+ return {};
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error get_sequence_3(I& it, S end, code_point& cp)
+ {
+ SCN_EXPECT(it != end);
+ uint32_t c = mask8(*it);
+
+ auto e = increase_safely(it, end);
+ if (!e) {
+ return e;
+ }
+
+ c = static_cast<uint32_t>((c << 12u) & 0xffffu) +
+ (static_cast<uint32_t>(mask8(*it) << 6u) & 0xfffu);
+
+ e = increase_safely(it, end);
+ if (!e) {
+ return e;
+ }
+
+ c += static_cast<uint32_t>(*it) & 0x3fu;
+ cp = make_code_point(c);
+
+ return {};
+ }
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error get_sequence_4(I& it, S end, code_point& cp)
+ {
+ SCN_EXPECT(it != end);
+ uint32_t c = mask8(*it);
+
+ auto e = increase_safely(it, end);
+ if (!e) {
+ return e;
+ }
+
+ c = ((c << 18u) & 0x1fffffu) +
+ (static_cast<uint32_t>(mask8(*it) << 12u) & 0x3ffffu);
+
+ e = increase_safely(it, end);
+ if (!e) {
+ return e;
+ }
+
+ c += static_cast<uint32_t>(mask8(*it) << 6u) & 0xfffu;
+
+ e = increase_safely(it, end);
+ if (!e) {
+ return e;
+ }
+
+ c += static_cast<uint32_t>(*it) & 0x3fu;
+ cp = make_code_point(c);
+
+ return {};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 error validate_next(I& it, S end, code_point& cp)
+ {
+ SCN_EXPECT(it != end);
+
+ int len = get_sequence_length(*it);
+ error e{};
+ switch (len) {
+ case 1:
+ e = get_sequence_1(it, end, cp);
+ break;
+ case 2:
+ e = get_sequence_2(it, end, cp);
+ break;
+ case 3:
+ e = get_sequence_3(it, end, cp);
+ break;
+ case 4:
+ e = get_sequence_4(it, end, cp);
+ break;
+ default:
+ return {error::invalid_encoding,
+ "Invalid lead byte for utf8"};
+ }
+
+ if (!e) {
+ return e;
+ }
+ if (!is_valid_code_point(cp) || is_overlong_sequence(cp, len)) {
+ return {error::invalid_encoding, "Invalid utf8 code point"};
+ }
+ ++it;
+ return {};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> parse_code_point(I begin,
+ S end,
+ code_point& cp)
+ {
+ code_point c{};
+ auto e = validate_next(begin, end, c);
+ if (e) {
+ cp = c;
+ return {begin};
+ }
+ return e;
+ }
+
+ template <typename I>
+ I append(code_point cp, I it)
+ {
+ SCN_EXPECT(is_code_point_valid(cp));
+
+ if (cp < 0x80) {
+ *(it++) = static_cast<uint8_t>(cp);
+ }
+ else if (cp < 0x800) {
+ *(it++) = static_cast<uint8_t>(
+ (static_cast<uint32_t>(cp) >> 6u) | 0xc0u);
+ *(it++) = static_cast<uint8_t>(
+ (static_cast<uint32_t>(cp) & 0x3fu) | 0x80u);
+ }
+ else if (cp < 0x10000) {
+ *(it++) = static_cast<uint8_t>(
+ (static_cast<uint32_t>(cp) >> 12u) | 0xe0u);
+ *(it++) = static_cast<uint8_t>(
+ ((static_cast<uint32_t>(cp) >> 6u) & 0x3fu) | 0x80u);
+ *(it++) = static_cast<uint8_t>(
+ (static_cast<uint32_t>(cp) & 0x3fu) | 0x80u);
+ }
+ else {
+ *(it++) = static_cast<uint8_t>(
+ (static_cast<uint32_t>(cp) >> 18u) | 0xf0u);
+ *(it++) = static_cast<uint8_t>(
+ ((static_cast<uint32_t>(cp) >> 12u) & 0x3fu) | 0x80u);
+ *(it++) = static_cast<uint8_t>(
+ ((static_cast<uint32_t>(cp) >> 6u) & 0x3fu) | 0x80u);
+ *(it++) = static_cast<uint8_t>(
+ (static_cast<uint32_t>(cp) & 0x3fu) | 0x80u);
+ }
+ return it;
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<I> encode_code_point(I begin,
+ S end,
+ code_point cp)
+ {
+ SCN_EXPECT(begin + 4 <= end);
+
+ if (!is_code_point_valid(cp)) {
+ return error(error::invalid_encoding,
+ "Invalid code point, cannot encode in UTF-8");
+ }
+ return {append(cp, begin)};
+ }
+
+ template <typename I, typename S>
+ SCN_CONSTEXPR14 expected<std::ptrdiff_t> code_point_distance(
+ I begin,
+ S end)
+ {
+ std::ptrdiff_t dist{};
+ code_point cp{};
+ for (; begin < end; ++dist) {
+ auto e = validate_next(begin, end, cp);
+ if (!e) {
+ return e;
+ }
+ }
+ return {dist};
+ }
+
+ } // namespace utf8
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/algorithm.h b/src/third-party/scnlib/include/scn/util/algorithm.h
new file mode 100644
index 0000000..a17b6b6
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/algorithm.h
@@ -0,0 +1,80 @@
+// 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_UTIL_ALGORITHM_H
+#define SCN_UTIL_ALGORITHM_H
+
+#include "../detail/fwd.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ /**
+ * Implementation of `std::exchange` for C++11
+ */
+ template <typename T, typename U = T>
+ SCN_CONSTEXPR14 T exchange(T& obj, U&& new_value)
+ {
+ T old_value = SCN_MOVE(obj);
+ obj = SCN_FWD(new_value);
+ return old_value;
+ }
+
+ /**
+ * Implementation of `std::max` without including `<algorithm>`
+ */
+ template <typename T>
+ constexpr T max(T a, T b) noexcept
+ {
+ return (a < b) ? b : a;
+ }
+
+ /**
+ * Implementation of `std::min_element` without including `<algorithm>`
+ */
+ template <typename It>
+ SCN_CONSTEXPR14 It min_element(It first, It last)
+ {
+ if (first == last) {
+ return last;
+ }
+
+ It smallest = first;
+ ++first;
+ for (; first != last; ++first) {
+ if (*first < *smallest) {
+ smallest = first;
+ }
+ }
+ return smallest;
+ }
+
+ /**
+ * Implementation of `std::min` without including `<algorithm>`
+ */
+ template <typename T>
+ constexpr T min(T a, T b) noexcept
+ {
+ return (b < a) ? b : a;
+ }
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/array.h b/src/third-party/scnlib/include/scn/util/array.h
new file mode 100644
index 0000000..6c86488
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/array.h
@@ -0,0 +1,105 @@
+// 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_UTIL_ARRAY_H
+#define SCN_UTIL_ARRAY_H
+
+#include "../detail/fwd.h"
+
+#include <cstdint>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ /**
+ * Implementation of `std::array` without including `<array>` (can be
+ * heavy-ish)
+ */
+ template <typename T, std::size_t N>
+ struct array {
+ static_assert(N > 0, "zero-sized array not supported");
+
+ using value_type = T;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reference = T&;
+ using const_reference = const T&;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+
+ SCN_CONSTEXPR14 reference operator[](size_type i)
+ {
+ SCN_EXPECT(i < size());
+ return m_data[i];
+ }
+ SCN_CONSTEXPR14 const_reference operator[](size_type i) const
+ {
+ SCN_EXPECT(i < size());
+ return m_data[i];
+ }
+
+ SCN_CONSTEXPR14 iterator begin() noexcept
+ {
+ return m_data;
+ }
+ constexpr const_iterator begin() const noexcept
+ {
+ return m_data;
+ }
+ constexpr const_iterator cbegin() const noexcept
+ {
+ return m_data;
+ }
+
+ SCN_CONSTEXPR14 iterator end() noexcept
+ {
+ return m_data + N;
+ }
+ constexpr const_iterator end() const noexcept
+ {
+ return m_data + N;
+ }
+ constexpr const_iterator cend() const noexcept
+ {
+ return m_data + N;
+ }
+
+ SCN_CONSTEXPR14 pointer data() noexcept
+ {
+ return m_data;
+ }
+ constexpr const_pointer data() const noexcept
+ {
+ return m_data;
+ }
+
+ SCN_NODISCARD constexpr size_type size() const noexcept
+ {
+ return N;
+ }
+
+ T m_data[N];
+ };
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/expected.h b/src/third-party/scnlib/include/scn/util/expected.h
new file mode 100644
index 0000000..f7c9a82
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/expected.h
@@ -0,0 +1,158 @@
+// 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_UTIL_EXPECTED_H
+#define SCN_UTIL_EXPECTED_H
+
+#include "memory.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ /**
+ * expected-like type.
+ * For situations where there can be a value in case of success or an error
+ * code.
+ */
+ template <typename T, typename Error, typename Enable>
+ class expected;
+
+ /**
+ * expected-like type for default-constructible success values.
+ * Not optimized for space-efficiency (both members are stored
+ * simultaneously).
+ * `error` is used as the error value and discriminant flag.
+ */
+ template <typename T, typename Error>
+ class expected<T,
+ Error,
+ typename std::enable_if<
+ std::is_default_constructible<T>::value>::type> {
+ public:
+ using success_type = T;
+ using error_type = Error;
+
+ constexpr expected() = default;
+ constexpr expected(success_type s) : m_s(s) {}
+ constexpr expected(error_type e) : m_e(e) {}
+
+ SCN_NODISCARD constexpr bool has_value() const noexcept
+ {
+ return m_e == Error{};
+ }
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value();
+ }
+ constexpr bool operator!() const noexcept
+ {
+ return !operator bool();
+ }
+
+ SCN_CONSTEXPR14 success_type& value() & noexcept
+ {
+ return m_s;
+ }
+ constexpr success_type value() const& noexcept
+ {
+ return m_s;
+ }
+ SCN_CONSTEXPR14 success_type value() && noexcept
+ {
+ return SCN_MOVE(m_s);
+ }
+
+ SCN_CONSTEXPR14 error_type& error() noexcept
+ {
+ return m_e;
+ }
+ constexpr error_type error() const noexcept
+ {
+ return m_e;
+ }
+
+ private:
+ success_type m_s{};
+ error_type m_e{error_type::success_tag()};
+ };
+
+ /**
+ * expected-like type for non-default-constructible success values.
+ * Not optimized for space-efficiency.
+ * `error` is used as the error value and discriminant flag.
+ */
+ template <typename T, typename Error>
+ class expected<T,
+ Error,
+ typename std::enable_if<
+ !std::is_default_constructible<T>::value>::type> {
+ public:
+ using success_type = T;
+ using success_storage = detail::erased_storage<T>;
+ using error_type = Error;
+
+ expected(success_type s) : m_s(SCN_MOVE(s)) {}
+ constexpr expected(error_type e) : m_e(e) {}
+
+ SCN_NODISCARD constexpr bool has_value() const noexcept
+ {
+ return m_e == Error{};
+ }
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value();
+ }
+ constexpr bool operator!() const noexcept
+ {
+ return !operator bool();
+ }
+
+ SCN_CONSTEXPR14 success_type& value() noexcept
+ {
+ return *m_s;
+ }
+ constexpr const success_type& value() const noexcept
+ {
+ return *m_s;
+ }
+
+ SCN_CONSTEXPR14 error_type& error() noexcept
+ {
+ return m_e;
+ }
+ constexpr error_type error() const noexcept
+ {
+ return m_e;
+ }
+
+ private:
+ success_storage m_s{};
+ error_type m_e{error_type::success_tag()};
+ };
+
+ template <typename T,
+ typename U = typename std::remove_cv<
+ typename std::remove_reference<T>::type>::type>
+ expected<U> make_expected(T&& val)
+ {
+ return expected<U>(std::forward<T>(val));
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/math.h b/src/third-party/scnlib/include/scn/util/math.h
new file mode 100644
index 0000000..4cca941
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/math.h
@@ -0,0 +1,121 @@
+// 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_UTIL_MATH_H
+#define SCN_UTIL_MATH_H
+
+#include "../detail/fwd.h"
+
+#include <cmath>
+#include <limits>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename Integral>
+ SCN_CONSTEXPR14 int _max_digits(int base) noexcept
+ {
+ using lim = std::numeric_limits<Integral>;
+
+ char base8_digits[8] = {3, 5, 0, 11, 0, 0, 0, 21};
+
+ if (base == 10) {
+ return lim::digits10;
+ }
+ if (base == 8) {
+ return static_cast<int>(base8_digits[sizeof(Integral) - 1]);
+ }
+ if (base == lim::radix) {
+ return lim::digits;
+ }
+
+ auto i = lim::max();
+
+ Integral digits = 0;
+ while (i) {
+ i /= static_cast<Integral>(base);
+ digits++;
+ }
+ return static_cast<int>(digits);
+ }
+
+ /**
+ * Returns the maximum number of digits that an integer in base `base`
+ * can have, including the sign.
+ *
+ * If `base == 0`, uses `2` (longest), and adds 2 to the result, to
+ * accommodate for a base prefix (e.g. `0x`)
+ */
+ template <typename Integral>
+ SCN_CONSTEXPR14 int max_digits(int base) noexcept
+ {
+ auto b = base == 0 ? 2 : base;
+ auto d = _max_digits<Integral>(b) +
+ (std::is_signed<Integral>::value ? 1 : 0);
+ if (base == 0) {
+ return d + 2; // accommodate for 0x/0o
+ }
+ return d;
+ }
+
+ /**
+ * Implementation of `std::div`, which is constexpr pre-C++23
+ */
+ template <typename T>
+ constexpr std::pair<T, T> div(T l, T r) noexcept
+ {
+ return {l / r, l % r};
+ }
+
+ template <typename T>
+ struct zero_value;
+ template <>
+ struct zero_value<float> {
+ static constexpr float value = 0.0f;
+ };
+ template <>
+ struct zero_value<double> {
+ static constexpr double value = 0.0;
+ };
+ template <>
+ struct zero_value<long double> {
+ static constexpr long double value = 0.0l;
+ };
+
+ /**
+ * Returns `true` if `ch` is a digit for an integer in base `base`.
+ */
+ template <typename CharT>
+ bool is_base_digit(CharT ch, int base)
+ {
+ if (base <= 10) {
+ return ch >= static_cast<CharT>('0') &&
+ ch <= static_cast<CharT>('0') + base - 1;
+ }
+ return is_base_digit(ch, 10) ||
+ (ch >= static_cast<CharT>('a') &&
+ ch <= static_cast<CharT>('a') + base - 1) ||
+ (ch >= static_cast<CharT>('A') &&
+ ch <= static_cast<CharT>('A') + base - 1);
+ }
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/memory.h b/src/third-party/scnlib/include/scn/util/memory.h
new file mode 100644
index 0000000..9dfb970
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/memory.h
@@ -0,0 +1,404 @@
+// 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_UTIL_MEMORY_H
+#define SCN_UTIL_MEMORY_H
+
+#include "meta.h"
+
+#include <cstring>
+#include <new>
+
+SCN_GCC_PUSH
+SCN_GCC_IGNORE("-Wnoexcept")
+#include <iterator>
+SCN_GCC_POP
+
+#if SCN_MSVC && SCN_HAS_STRING_VIEW
+#include <string_view>
+#endif
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename T>
+ struct pointer_traits;
+
+ template <typename T>
+ struct pointer_traits<T*> {
+ using pointer = T*;
+ using element_type = T;
+ using difference_type = std::ptrdiff_t;
+
+ template <typename U>
+ using rebind = U*;
+
+ template <typename U = T,
+ typename std::enable_if<!std::is_void<U>::value>::type* =
+ nullptr>
+ static constexpr pointer pointer_to(U& r) noexcept
+ {
+ return &r;
+ }
+ };
+
+ template <typename T>
+ constexpr T* to_address_impl(T* p, priority_tag<2>) noexcept
+ {
+ return p;
+ }
+ template <typename Ptr>
+ SCN_CONSTEXPR14 auto to_address_impl(const Ptr& p,
+ priority_tag<1>) noexcept
+ -> decltype(::scn::detail::pointer_traits<Ptr>::to_address(p))
+ {
+ return ::scn::detail::pointer_traits<Ptr>::to_address(p);
+ }
+ template <typename Ptr>
+ constexpr auto to_address_impl(const Ptr& p, priority_tag<0>) noexcept
+ -> decltype(::scn::detail::to_address_impl(p.operator->(),
+ priority_tag<2>{}))
+ {
+ return ::scn::detail::to_address_impl(p.operator->(),
+ priority_tag<2>{});
+ }
+
+ template <typename Ptr>
+ constexpr auto to_address(Ptr&& p) noexcept
+ -> decltype(::scn::detail::to_address_impl(SCN_FWD(p),
+ priority_tag<2>{}))
+ {
+ return ::scn::detail::to_address_impl(SCN_FWD(p),
+ priority_tag<2>{});
+ }
+
+#if SCN_WINDOWS
+ template <typename I, typename B, typename E>
+ SCN_CONSTEXPR14 auto to_address_safe(I&& p, B begin, E end) noexcept
+ -> decltype(to_address(SCN_FWD(p)))
+ {
+ if (p >= begin && p < end) {
+ return to_address(SCN_FWD(p));
+ }
+ if (begin == end) {
+ return to_address(SCN_FWD(p));
+ }
+ if (p == end) {
+ return to_address(SCN_FWD(p) - 1) + 1;
+ }
+ SCN_ENSURE(false);
+ SCN_UNREACHABLE;
+ }
+#else
+ template <typename I, typename B, typename E>
+ SCN_CONSTEXPR14 auto to_address_safe(I&& p, B, E) noexcept
+ -> decltype(to_address(SCN_FWD(p)))
+ {
+ return to_address(SCN_FWD(p));
+ }
+#endif
+
+ // Workaround for MSVC _String_view_iterator
+#if SCN_MSVC && SCN_HAS_STRING_VIEW
+ template <typename Traits>
+ struct pointer_traits<std::_String_view_iterator<Traits>> {
+ using iterator = std::_String_view_iterator<Traits>;
+ using pointer = typename iterator::pointer;
+ using element_type = typename iterator::value_type;
+ using difference_type = typename iterator::difference_type;
+
+ static constexpr pointer to_address(const iterator& it) noexcept
+ {
+ // operator-> of _String_view_iterator
+ // is checked for past-the-end dereference,
+ // even though operator-> isn't dereferencing anything :)))
+ return it._Unwrapped();
+ }
+ };
+#endif
+
+ template <typename T>
+ constexpr T* launder(T* p) noexcept
+ {
+#if SCN_HAS_LAUNDER
+ return std::launder(p);
+#else
+ return p;
+#endif
+ }
+
+ template <typename ForwardIt, typename T>
+ void uninitialized_fill(ForwardIt first,
+ ForwardIt last,
+ const T& value,
+ std::true_type) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ const auto dist = static_cast<size_t>(std::distance(first, last)) *
+ sizeof(value_type);
+ std::memset(&*first, static_cast<unsigned char>(value), dist);
+ }
+ template <typename ForwardIt, typename T>
+ void uninitialized_fill(ForwardIt first,
+ ForwardIt last,
+ const T& value,
+ std::false_type) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ ForwardIt current = first;
+ for (; current != last; ++current) {
+ ::new (static_cast<void*>(std::addressof(*current)))
+ value_type(value);
+ }
+ }
+ template <typename ForwardIt, typename T>
+ void uninitialized_fill(ForwardIt first,
+ ForwardIt last,
+ const T& value) noexcept
+ {
+ constexpr bool B = std::is_trivially_copyable<T>::value &&
+ std::is_pointer<ForwardIt>::value &&
+ sizeof(T) == 1;
+ return uninitialized_fill(first, last, value,
+ std::integral_constant<bool, B>{});
+ }
+
+ template <typename ForwardIt>
+ void uninitialized_fill_default_construct(ForwardIt first,
+ ForwardIt last) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ ForwardIt current = first;
+ for (; current != last; ++current) {
+ ::new (static_cast<void*>(std::addressof(*current))) value_type;
+ }
+ }
+ template <typename ForwardIt>
+ void uninitialized_fill_value_init(ForwardIt first,
+ ForwardIt last) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ ForwardIt current = first;
+ for (; current != last; ++current) {
+ ::new (static_cast<void*>(std::addressof(*current)))
+ value_type();
+ }
+ }
+
+ template <typename InputIt,
+ typename ForwardIt,
+ typename std::enable_if<
+ !std::is_trivially_copyable<typename std::iterator_traits<
+ ForwardIt>::value_type>::value>::type* = nullptr>
+ ForwardIt uninitialized_copy(InputIt first,
+ InputIt last,
+ ForwardIt d_first) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ ForwardIt current = d_first;
+ for (; first != last; ++first, (void)++current) {
+ ::new (static_cast<void*>(std::addressof(*current)))
+ value_type(*first);
+ }
+ return current;
+ }
+ template <typename InputIt,
+ typename ForwardIt,
+ typename std::enable_if<
+ std::is_trivially_copyable<typename std::iterator_traits<
+ ForwardIt>::value_type>::value>::type* = nullptr>
+ ForwardIt uninitialized_copy(InputIt first,
+ InputIt last,
+ ForwardIt d_first) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ using pointer = typename std::iterator_traits<ForwardIt>::pointer;
+ auto ptr =
+ std::memcpy(std::addressof(*d_first), std::addressof(*first),
+ static_cast<size_t>(std::distance(first, last)) *
+ sizeof(value_type));
+ return ForwardIt{static_cast<pointer>(ptr)};
+ }
+
+ template <typename InputIt,
+ typename ForwardIt,
+ typename std::enable_if<
+ !std::is_trivially_copyable<typename std::iterator_traits<
+ ForwardIt>::value_type>::value>::type* = nullptr>
+ ForwardIt uninitialized_move(InputIt first,
+ InputIt last,
+ ForwardIt d_first) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ ForwardIt current = d_first;
+ for (; first != last; ++first, (void)++current) {
+ ::new (static_cast<void*>(std::addressof(*current)))
+ value_type(std::move(*first));
+ }
+ return current;
+ }
+ template <typename InputIt,
+ typename ForwardIt,
+ typename std::enable_if<
+ std::is_trivially_copyable<typename std::iterator_traits<
+ ForwardIt>::value_type>::value>::type* = nullptr>
+ ForwardIt uninitialized_move(InputIt first,
+ InputIt last,
+ ForwardIt d_first) noexcept
+ {
+ using value_type =
+ typename std::iterator_traits<ForwardIt>::value_type;
+ using pointer = typename std::iterator_traits<ForwardIt>::pointer;
+ auto ptr =
+ std::memcpy(std::addressof(*d_first), std::addressof(*first),
+ static_cast<size_t>(std::distance(first, last)) *
+ sizeof(value_type));
+ return ForwardIt(static_cast<pointer>(ptr));
+ }
+
+ template <typename T>
+ class SCN_TRIVIAL_ABI erased_storage {
+ public:
+ using value_type = T;
+ using pointer = T*;
+ using storage_type = unsigned char[sizeof(T)];
+
+ constexpr erased_storage() noexcept = default;
+
+ erased_storage(T val) noexcept(
+ std::is_nothrow_move_constructible<T>::value)
+ : m_ptr(::new (static_cast<void*>(&m_data)) T(SCN_MOVE(val)))
+ {
+ }
+
+ erased_storage(const erased_storage& other)
+ : m_ptr(other ? ::new (static_cast<void*>(&m_data))
+ T(other.get())
+ : nullptr)
+ {
+ }
+ erased_storage& operator=(const erased_storage& other)
+ {
+ _destruct();
+ if (other) {
+ m_ptr = ::new (static_cast<void*>(&m_data)) T(other.get());
+ }
+ return *this;
+ }
+
+ erased_storage(erased_storage&& other) noexcept
+ : m_ptr(other ? ::new (static_cast<void*>(&m_data))
+ T(SCN_MOVE(other.get()))
+ : nullptr)
+ {
+ other.m_ptr = nullptr;
+ }
+ erased_storage& operator=(erased_storage&& other) noexcept
+ {
+ _destruct();
+ if (other) {
+ m_ptr = ::new (static_cast<void*>(&m_data))
+ T(SCN_MOVE(other.get()));
+ other.m_ptr = nullptr;
+ }
+ return *this;
+ }
+
+ ~erased_storage() noexcept
+ {
+ _destruct();
+ }
+
+ SCN_NODISCARD constexpr bool has_value() const noexcept
+ {
+ return m_ptr != nullptr;
+ }
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value();
+ }
+
+ SCN_CONSTEXPR14 T& get() noexcept
+ {
+ SCN_EXPECT(has_value());
+ return _get();
+ }
+ SCN_CONSTEXPR14 const T& get() const noexcept
+ {
+ SCN_EXPECT(has_value());
+ return _get();
+ }
+
+ SCN_CONSTEXPR14 T& operator*() noexcept
+ {
+ SCN_EXPECT(has_value());
+ return _get();
+ }
+ SCN_CONSTEXPR14 const T& operator*() const noexcept
+ {
+ SCN_EXPECT(has_value());
+ return _get();
+ }
+
+ SCN_CONSTEXPR14 T* operator->() noexcept
+ {
+ return m_ptr;
+ }
+ SCN_CONSTEXPR14 const T* operator->() const noexcept
+ {
+ return m_ptr;
+ }
+
+ private:
+ void _destruct()
+ {
+ if (m_ptr) {
+ _get().~T();
+ }
+ m_ptr = nullptr;
+ }
+ static pointer _toptr(storage_type& data)
+ {
+ return ::scn::detail::launder(
+ reinterpret_cast<T*>(reinterpret_cast<void*>(data.data())));
+ }
+ SCN_CONSTEXPR14 T& _get() noexcept
+ {
+ return *m_ptr;
+ }
+ SCN_CONSTEXPR14 const T& _get() const noexcept
+ {
+ return *m_ptr;
+ }
+
+ alignas(T) storage_type m_data{};
+ pointer m_ptr{nullptr};
+ };
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/meta.h b/src/third-party/scnlib/include/scn/util/meta.h
new file mode 100644
index 0000000..83b738c
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/meta.h
@@ -0,0 +1,77 @@
+// 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_UTIL_META_H
+#define SCN_UTIL_META_H
+
+#include "../detail/fwd.h"
+
+#include <type_traits>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename... Ts>
+ struct make_void {
+ using type = void;
+ };
+ template <typename... Ts>
+ using void_t = typename make_void<Ts...>::type;
+
+ template <typename... T>
+ void valid_expr(T&&...);
+
+ template <typename T>
+ struct remove_cvref {
+ using type = typename std::remove_cv<
+ typename std::remove_reference<T>::type>::type;
+ };
+ template <typename T>
+ using remove_cvref_t = typename remove_cvref<T>::type;
+
+ // Stolen from range-v3
+ template <typename T>
+ struct static_const {
+ static constexpr T value{};
+ };
+ template <typename T>
+ constexpr T static_const<T>::value;
+
+ template <std::size_t I>
+ struct priority_tag : priority_tag<I - 1> {
+ };
+ template <>
+ struct priority_tag<0> {
+ };
+
+ struct dummy_type {
+ };
+
+ template <typename T>
+ struct dependent_false : std::false_type {
+ };
+
+ template <typename T>
+ using integer_type_for_char = typename std::
+ conditional<std::is_signed<T>::value, int, unsigned>::type;
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/optional.h b/src/third-party/scnlib/include/scn/util/optional.h
new file mode 100644
index 0000000..9c0c808
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/optional.h
@@ -0,0 +1,105 @@
+// 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_UTIL_OPTIONAL_H
+#define SCN_UTIL_OPTIONAL_H
+
+#include "memory.h"
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ struct nullopt_t {
+ };
+ namespace {
+ static constexpr auto& nullopt = detail::static_const<nullopt_t>::value;
+ }
+
+ /**
+ * A very lackluster optional implementation.
+ * Useful when scanning non-default-constructible types, especially with
+ * <tuple_return.h>:
+ *
+ * \code{.cpp}
+ * // implement scn::scanner for optional<mytype>
+ * optional<mytype> val;
+ * scn::scan(source, "{}", val);
+ *
+ * // with tuple_return:
+ * auto [result, val] = scn::scan_tuple<optional<mytype>>(source, "{}");
+ * \endcode
+ */
+ template <typename T>
+ class optional {
+ public:
+ using value_type = T;
+ using storage_type = detail::erased_storage<T>;
+
+ optional() = default;
+ optional(nullopt_t) : m_storage{} {}
+
+ optional(value_type val) : m_storage(SCN_MOVE(val)) {}
+ optional& operator=(value_type val)
+ {
+ m_storage = storage_type(SCN_MOVE(val));
+ return *this;
+ }
+
+ SCN_NODISCARD constexpr bool has_value() const noexcept
+ {
+ return m_storage.operator bool();
+ }
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value();
+ }
+
+ SCN_CONSTEXPR14 T& get() noexcept
+ {
+ return m_storage.get();
+ }
+ SCN_CONSTEXPR14 const T& get() const noexcept
+ {
+ return m_storage.get();
+ }
+
+ SCN_CONSTEXPR14 T& operator*() noexcept
+ {
+ return get();
+ }
+ SCN_CONSTEXPR14 const T& operator*() const noexcept
+ {
+ return get();
+ }
+
+ SCN_CONSTEXPR14 T* operator->() noexcept
+ {
+ return m_storage.operator->();
+ }
+ SCN_CONSTEXPR14 const T* operator->() const noexcept
+ {
+ return m_storage.operator->();
+ }
+
+ private:
+ storage_type m_storage;
+ };
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif
diff --git a/src/third-party/scnlib/include/scn/util/small_vector.h b/src/third-party/scnlib/include/scn/util/small_vector.h
new file mode 100644
index 0000000..93a5514
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/small_vector.h
@@ -0,0 +1,788 @@
+// 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_UTIL_SMALL_VECTOR_H
+#define SCN_UTIL_SMALL_VECTOR_H
+
+#include "math.h"
+#include "memory.h"
+
+#include <cstdint>
+#include <cstring>
+
+SCN_GCC_PUSH
+SCN_GCC_IGNORE("-Wnoexcept")
+#include <iterator>
+SCN_GCC_POP
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename Iter>
+ std::reverse_iterator<Iter> make_reverse_iterator(Iter i)
+ {
+ return std::reverse_iterator<Iter>(i);
+ }
+
+ class small_vector_base {
+ static SCN_CONSTEXPR14 uint64_t _next_pow2_64(uint64_t x) noexcept
+ {
+ --x;
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ x |= (x >> 32);
+ return x + 1;
+ }
+ static SCN_CONSTEXPR14 uint32_t _next_pow2_32(uint32_t x) noexcept
+ {
+ --x;
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return x + 1;
+ }
+
+ protected:
+ size_t next_pow2(size_t x)
+ {
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4127) // conditional expression is constant
+ if (sizeof(size_t) == sizeof(uint64_t)) {
+ return static_cast<size_t>(
+ _next_pow2_64(static_cast<uint64_t>(x)));
+ }
+ SCN_MSVC_POP
+ return static_cast<size_t>(
+ _next_pow2_32(static_cast<uint32_t>(x)));
+ }
+ };
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wpadded")
+
+ template <typename T, size_t N>
+ struct basic_stack_storage {
+ alignas(T) unsigned char data[N * sizeof(T)];
+
+ T* reinterpret_data()
+ {
+ return ::scn::detail::launder(reinterpret_unconstructed_data());
+ }
+ const T* reinterpret_data() const
+ {
+ return ::scn::detail::launder(reinterpret_unconstructed_data());
+ }
+
+ SCN_NODISCARD T* reinterpret_unconstructed_data()
+ {
+ return static_cast<T*>(static_cast<void*>(data));
+ }
+ SCN_NODISCARD const T* reinterpret_unconstructed_data() const
+ {
+ return static_cast<const T*>(static_cast<const void*>(data));
+ }
+
+ SCN_NODISCARD SCN_CONSTEXPR14 unsigned char*
+ get_unconstructed_data()
+ {
+ return data;
+ }
+ SCN_NODISCARD constexpr const unsigned char*
+ get_unconstructed_data() const
+ {
+ return data;
+ }
+ };
+
+ // -Wpadded
+ SCN_CLANG_POP
+
+ template <typename T>
+ constexpr T constexpr_max(T val)
+ {
+ return val;
+ }
+ template <typename T, typename... Ts>
+ constexpr T constexpr_max(T val, Ts... a)
+ {
+ return val > constexpr_max(a...) ? val : constexpr_max(a...);
+ }
+
+ template <typename T>
+ struct alignas(constexpr_max(alignof(T),
+ alignof(T*))) basic_stack_storage<T, 0> {
+ T* reinterpret_data()
+ {
+ return nullptr;
+ }
+ const T* reinterpret_data() const
+ {
+ return nullptr;
+ }
+
+ T* reinterpret_unconstructed_data()
+ {
+ return nullptr;
+ }
+ const T* reinterpret_unconstructed_data() const
+ {
+ return nullptr;
+ }
+
+ unsigned char* get_unconstructed_data()
+ {
+ return nullptr;
+ }
+ const unsigned char* get_unconstructed_data() const
+ {
+ return nullptr;
+ }
+ };
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wpadded")
+
+ /**
+ * A contiguous container, that stores its values in the stack, if
+ * `size() <= StackN`
+ */
+ template <typename T, size_t StackN>
+ class small_vector : protected small_vector_base {
+ public:
+ using value_type = T;
+ using size_type = size_t;
+ using difference_type = std::ptrdiff_t;
+ using reference = T&;
+ using const_reference = const T&;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<pointer>;
+ using const_reverse_iterator = std::reverse_iterator<const_pointer>;
+
+ struct stack_storage : basic_stack_storage<T, StackN> {
+ };
+ struct heap_storage {
+ size_type cap{0};
+ };
+
+ small_vector() noexcept
+ : m_ptr(_construct_stack_storage()
+ .reinterpret_unconstructed_data())
+ {
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4127) // conditional expression is constant
+
+ if (StackN == 0) {
+ _destruct_stack_storage();
+ _construct_heap_storage();
+ }
+
+ SCN_MSVC_POP
+
+ SCN_ENSURE(size() == 0);
+ }
+
+ explicit small_vector(size_type count, const T& value)
+ {
+ if (!can_be_small(count)) {
+ auto& heap = _construct_heap_storage();
+ auto cap = next_pow2(count);
+ auto storage_ptr = new unsigned char[count * sizeof(T)];
+ auto ptr =
+ static_cast<pointer>(static_cast<void*>(storage_ptr));
+ uninitialized_fill(ptr, ptr + count, value);
+
+ heap.cap = cap;
+ m_size = count;
+ m_ptr = ::scn::detail::launder(ptr);
+ }
+ else {
+ auto& stack = _construct_stack_storage();
+ uninitialized_fill(
+ stack.reinterpret_unconstructed_data(),
+ stack.reinterpret_unconstructed_data() + StackN, value);
+ m_size = count;
+ m_ptr = stack.reinterpret_data();
+ }
+
+ SCN_ENSURE(data());
+ SCN_ENSURE(size() == count);
+ SCN_ENSURE(capacity() >= size());
+ }
+
+ explicit small_vector(size_type count)
+ {
+ if (!can_be_small(count)) {
+ auto& heap = _construct_heap_storage();
+ auto cap = next_pow2(count);
+ auto storage_ptr = new unsigned char[count * sizeof(T)];
+ auto ptr =
+ static_cast<pointer>(static_cast<void*>(storage_ptr));
+ uninitialized_fill_value_init(ptr, ptr + count);
+ heap.cap = cap;
+ m_size = count;
+ m_ptr = ::scn::detail::launder(ptr);
+ }
+ else {
+ auto& stack = _construct_stack_storage();
+ uninitialized_fill_value_init(
+ stack.reinterpret_unconstructed_data(),
+ stack.reinterpret_unconstructed_data() + count);
+ m_size = count;
+ m_ptr = stack.reinterpret_data();
+ }
+
+ SCN_ENSURE(data());
+ SCN_ENSURE(size() == count);
+ SCN_ENSURE(capacity() >= size());
+ }
+
+ small_vector(const small_vector& other)
+ {
+ if (other.empty()) {
+ auto& stack = _construct_stack_storage();
+ m_ptr = stack.reinterpret_unconstructed_data();
+ return;
+ }
+
+ auto s = other.size();
+ if (!other.is_small()) {
+ auto& heap = _construct_heap_storage();
+ auto cap = other.capacity();
+ auto optr = other.data();
+
+ auto storage_ptr = new unsigned char[cap * sizeof(T)];
+ auto ptr =
+ static_cast<pointer>(static_cast<void*>(storage_ptr));
+ uninitialized_copy(optr, optr + s, ptr);
+
+ m_ptr = ::scn::detail::launder(ptr);
+ m_size = s;
+ heap.cap = cap;
+ }
+ else {
+ auto& stack = _construct_stack_storage();
+ auto optr = other.data();
+ uninitialized_copy(optr, optr + s,
+ stack.reinterpret_unconstructed_data());
+ m_size = s;
+ m_ptr = stack.reinterpret_data();
+ }
+
+ SCN_ENSURE(data());
+ SCN_ENSURE(other.data());
+ SCN_ENSURE(other.size() == size());
+ SCN_ENSURE(other.capacity() == capacity());
+ }
+ small_vector(small_vector&& other) noexcept
+ {
+ if (other.empty()) {
+ auto& stack = _construct_stack_storage();
+ m_ptr = stack.reinterpret_unconstructed_data();
+ return;
+ }
+
+ auto s = other.size();
+ if (!other.is_small()) {
+ auto& heap = _construct_heap_storage();
+ m_ptr = other.data();
+
+ m_size = s;
+ heap.cap = other.capacity();
+ }
+ else {
+ auto& stack = _construct_stack_storage();
+ auto optr = other.data();
+ uninitialized_move(optr, optr + s,
+ stack.reinterpret_unconstructed_data());
+
+ m_size = s;
+ other._destruct_elements();
+ }
+ other.m_ptr = nullptr;
+
+ SCN_ENSURE(data());
+ }
+
+ small_vector& operator=(const small_vector& other)
+ {
+ _destruct_elements();
+
+ if (other.empty()) {
+ return *this;
+ }
+
+ SCN_ASSERT(size() == 0, "");
+
+ // this other
+ // s s false || true
+ // s h false || false second
+ // h s true || true
+ // h h true || false
+ if (!is_small() || other.is_small()) {
+ uninitialized_copy(other.data(),
+ other.data() + other.size(), data());
+ m_ptr = ::scn::detail::launder(data());
+ m_size = other.size();
+ if (!other.is_small()) {
+ _get_heap().cap = other.capacity();
+ }
+ }
+ else {
+ _destruct_stack_storage();
+ auto& heap = _construct_heap_storage();
+
+ auto cap = next_pow2(other.size());
+ auto storage_ptr = new unsigned char[cap * sizeof(T)];
+ auto ptr =
+ static_cast<pointer>(static_cast<void*>(storage_ptr));
+ uninitialized_copy(other.data(),
+ other.data() + other.size(), ptr);
+ m_ptr = ::scn::detail::launder(ptr);
+ m_size = other.size();
+ heap.cap = cap;
+ }
+ return *this;
+ }
+
+ small_vector& operator=(small_vector&& other) noexcept
+ {
+ _destruct_elements();
+
+ if (other.empty()) {
+ return *this;
+ }
+
+ SCN_ASSERT(size() == 0, "");
+
+ if (!is_small() && !other.is_small()) {
+ if (!is_small()) {
+ if (capacity() != 0) {
+ delete[] ::scn::detail::launder(
+ static_cast<unsigned char*>(
+ static_cast<void*>(m_ptr)));
+ }
+ }
+
+ m_ptr = other.data();
+ m_size = other.size();
+ _get_heap().cap = other.capacity();
+ }
+ else if (!is_small() || other.is_small()) {
+ uninitialized_move(other.data(),
+ other.data() + other.size(), data());
+ m_size = other.size();
+ other._destruct_elements();
+ }
+ else {
+ _destruct_stack_storage();
+ auto& heap = _construct_heap_storage();
+
+ m_ptr = other.data();
+ m_size = other.size();
+ heap.cap = other.capacity();
+ }
+
+ other.m_ptr = nullptr;
+
+ return *this;
+ }
+
+ ~small_vector()
+ {
+ _destruct();
+ }
+
+ SCN_NODISCARD SCN_CONSTEXPR14 pointer data() noexcept
+ {
+ return m_ptr;
+ }
+ SCN_NODISCARD constexpr const_pointer data() const noexcept
+ {
+ return m_ptr;
+ }
+ SCN_NODISCARD constexpr size_type size() const noexcept
+ {
+ return m_size;
+ }
+ SCN_NODISCARD size_type capacity() const noexcept
+ {
+ if (SCN_LIKELY(is_small())) {
+ return StackN;
+ }
+ return _get_heap().cap;
+ }
+
+ SCN_NODISCARD constexpr bool empty() const noexcept
+ {
+ return size() == 0;
+ }
+
+ SCN_NODISCARD bool is_small() const noexcept
+ {
+ // oh so very ub
+ return m_ptr == reinterpret_cast<const_pointer>(
+ std::addressof(m_stack_storage));
+ }
+ constexpr static bool can_be_small(size_type n) noexcept
+ {
+ return n <= StackN;
+ }
+
+ SCN_CONSTEXPR14 reference operator[](size_type pos)
+ {
+ SCN_EXPECT(pos < size());
+ return *(begin() + pos);
+ }
+ SCN_CONSTEXPR14 const_reference operator[](size_type pos) const
+ {
+ SCN_EXPECT(pos < size());
+ return *(begin() + pos);
+ }
+
+ SCN_CONSTEXPR14 reference front()
+ {
+ SCN_EXPECT(!empty());
+ return *begin();
+ }
+ SCN_CONSTEXPR14 const_reference front() const
+ {
+ SCN_EXPECT(!empty());
+ return *begin();
+ }
+
+ SCN_CONSTEXPR14 reference back()
+ {
+ SCN_EXPECT(!empty());
+ return *(end() - 1);
+ }
+ SCN_CONSTEXPR14 const_reference back() const
+ {
+ SCN_EXPECT(!empty());
+ return *(end() - 1);
+ }
+
+ SCN_CONSTEXPR14 iterator begin() noexcept
+ {
+ return data();
+ }
+ constexpr const_iterator begin() const noexcept
+ {
+ return data();
+ }
+ constexpr const_iterator cbegin() const noexcept
+ {
+ return begin();
+ }
+
+ SCN_CONSTEXPR14 iterator end() noexcept
+ {
+ return begin() + size();
+ }
+ constexpr const_iterator end() const noexcept
+ {
+ return begin() + size();
+ }
+ constexpr const_iterator cend() const noexcept
+ {
+ return end();
+ }
+
+ SCN_CONSTEXPR14 reverse_iterator rbegin() noexcept
+ {
+ return make_reverse_iterator(end());
+ }
+ constexpr const_reverse_iterator rbegin() const noexcept
+ {
+ return make_reverse_iterator(end());
+ }
+ constexpr const_reverse_iterator crbegin() const noexcept
+ {
+ return rbegin();
+ }
+
+ SCN_CONSTEXPR14 reverse_iterator rend() noexcept
+ {
+ return make_reverse_iterator(begin());
+ }
+ constexpr const_reverse_iterator rend() const noexcept
+ {
+ return make_reverse_iterator(begin());
+ }
+ constexpr const_reverse_iterator crend() const noexcept
+ {
+ return rend();
+ }
+
+ SCN_NODISCARD
+ constexpr size_type max_size() const noexcept
+ {
+ return std::numeric_limits<size_type>::max();
+ }
+
+ void make_small() noexcept
+ {
+ if (is_small() || !can_be_small(size())) {
+ return;
+ }
+
+ stack_storage s;
+ uninitialized_move(begin(), end(),
+ s.reinterpret_unconstructed_data());
+ auto tmp_size = size();
+
+ _destruct();
+ auto& stack = _construct_stack_storage();
+ uninitialized_move(s.reinterpret_data(),
+ s.reinterpret_data() + tmp_size,
+ stack.reinterpret_unconstructed_data());
+ m_size = tmp_size;
+ }
+
+ void reserve(size_type new_cap)
+ {
+ if (new_cap <= capacity()) {
+ return;
+ }
+ _realloc(next_pow2(new_cap));
+ }
+
+ void shrink_to_fit()
+ {
+ if (is_small()) {
+ return;
+ }
+ if (!can_be_small(size())) {
+ _realloc(size());
+ }
+ else {
+ make_small();
+ }
+ }
+
+ void clear() noexcept
+ {
+ _destruct_elements();
+ }
+
+ iterator erase(iterator pos)
+ {
+ if (pos == end()) {
+ pos->~T();
+ m_size = size() - 1;
+ return end();
+ }
+ else {
+ for (auto it = pos; it != end(); ++it) {
+ it->~T();
+ ::new (static_cast<void*>(it)) T(std::move(*(it + 1)));
+ }
+ (end() - 1)->~T();
+ m_size = size() - 1;
+ return pos;
+ }
+ }
+
+ iterator erase(iterator b, iterator e)
+ {
+ if (begin() == end()) {
+ return b;
+ }
+ if (e == end()) {
+ auto n = static_cast<size_t>(std::distance(b, e));
+ for (auto it = b; it != e; ++it) {
+ it->~T();
+ }
+ m_size = size() - n;
+ return end();
+ }
+ SCN_ENSURE(false);
+ SCN_UNREACHABLE;
+ }
+
+ void push_back(const T& value)
+ {
+ ::new (_prepare_push_back()) T(value);
+ m_size = size() + 1;
+ }
+ void push_back(T&& value)
+ {
+ ::new (_prepare_push_back()) T(std::move(value));
+ m_size = size() + 1;
+ }
+
+ template <typename... Args>
+ reference emplace_back(Args&&... args)
+ {
+ ::new (_prepare_push_back()) T(SCN_FWD(args)...);
+ m_size = size() + 1;
+ return back();
+ }
+
+ void pop_back()
+ {
+ back().~T();
+ m_size = size() - 1;
+ }
+
+ void resize(size_type count)
+ {
+ if (count > size()) {
+ if (count > capacity()) {
+ _realloc(next_pow2(capacity()));
+ }
+ uninitialized_fill_value_init(begin() + size(),
+ begin() + count);
+ }
+ else {
+ for (auto it = begin() + count; it != end(); ++it) {
+ it->~T();
+ }
+ }
+ m_size = count;
+ }
+
+ SCN_CONSTEXPR14 void swap(small_vector& other) noexcept
+ {
+ small_vector tmp{SCN_MOVE(other)};
+ other = std::move(*this);
+ *this = std::move(tmp);
+ }
+
+ private:
+ stack_storage& _construct_stack_storage() noexcept
+ {
+ ::new (std::addressof(m_stack_storage)) stack_storage;
+ m_ptr = m_stack_storage.reinterpret_unconstructed_data();
+ return m_stack_storage;
+ }
+ heap_storage& _construct_heap_storage() noexcept
+ {
+ ::new (std::addressof(m_heap_storage)) heap_storage;
+ m_ptr = nullptr;
+ return m_heap_storage;
+ }
+
+ void _destruct_stack_storage() noexcept
+ {
+ _get_stack().~stack_storage();
+ }
+ void _destruct_heap_storage() noexcept
+ {
+ if (capacity() != 0) {
+ delete[] static_cast<unsigned char*>(
+ static_cast<void*>(m_ptr));
+ }
+ _get_heap().~heap_storage();
+ }
+
+ void _destruct_elements() noexcept
+ {
+ const auto s = size();
+ for (size_type i = 0; i != s; ++i) {
+ m_ptr[i].~T();
+ }
+ m_size = 0;
+ }
+
+ void _destruct() noexcept
+ {
+ _destruct_elements();
+ if (SCN_UNLIKELY(!is_small())) {
+ _destruct_heap_storage();
+ }
+ else {
+ _destruct_stack_storage();
+ }
+ }
+
+ void _realloc(size_type new_cap)
+ {
+ auto storage_ptr = new unsigned char[new_cap * sizeof(T)];
+ auto ptr =
+ static_cast<pointer>(static_cast<void*>(storage_ptr));
+ auto n = size();
+ uninitialized_move(begin(), end(), ptr);
+ _destruct();
+ auto& heap = [this]() -> heap_storage& {
+ if (is_small()) {
+ return _construct_heap_storage();
+ }
+ return _get_heap();
+ }();
+ m_ptr = ptr;
+ m_size = n;
+ heap.cap = new_cap;
+ }
+
+ void* _prepare_push_back()
+ {
+ if (SCN_UNLIKELY(size() == capacity())) {
+ _realloc(next_pow2(size() + 1));
+ }
+ return m_ptr + size();
+ }
+
+ stack_storage& _get_stack() noexcept
+ {
+ return m_stack_storage;
+ }
+ const stack_storage& _get_stack() const noexcept
+ {
+ return m_stack_storage;
+ }
+
+ heap_storage& _get_heap() noexcept
+ {
+ return m_heap_storage;
+ }
+ const heap_storage& _get_heap() const noexcept
+ {
+ return m_heap_storage;
+ }
+
+ pointer m_ptr{nullptr};
+ size_type m_size{0};
+ union {
+ stack_storage m_stack_storage;
+ heap_storage m_heap_storage;
+ };
+ };
+
+ template <typename T, size_t N>
+ SCN_CONSTEXPR14 void swap(
+ small_vector<T, N>& l,
+ small_vector<T, N>& r) noexcept(noexcept(l.swap(r)))
+ {
+ l.swap(r);
+ }
+
+ SCN_CLANG_POP // -Wpadded
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_SMALL_VECTOR_H
diff --git a/src/third-party/scnlib/include/scn/util/span.h b/src/third-party/scnlib/include/scn/util/span.h
new file mode 100644
index 0000000..1abdd6c
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/span.h
@@ -0,0 +1,240 @@
+// 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_UTIL_SPAN_H
+#define SCN_UTIL_SPAN_H
+
+#include "memory.h"
+
+SCN_GCC_PUSH
+SCN_GCC_IGNORE("-Wnoexcept")
+#include <iterator>
+SCN_GCC_POP
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace custom_ranges {
+ // iterator_category
+ using std::bidirectional_iterator_tag;
+ using std::forward_iterator_tag;
+ using std::input_iterator_tag;
+ using std::output_iterator_tag;
+ using std::random_access_iterator_tag;
+ struct contiguous_iterator_tag : random_access_iterator_tag {
+ };
+ } // namespace custom_ranges
+
+ /**
+ * A view over a contiguous range.
+ * Stripped-down version of `std::span`.
+ */
+ template <typename T>
+ class span {
+ public:
+ using element_type = T;
+ using value_type = typename std::remove_cv<T>::type;
+ using index_type = std::size_t;
+ using ssize_type = std::ptrdiff_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ constexpr span() noexcept = default;
+
+ template <typename I,
+ typename = decltype(detail::to_address(SCN_DECLVAL(I)))>
+ SCN_CONSTEXPR14 span(I begin, index_type count) noexcept
+ : m_ptr(detail::to_address(begin)),
+ m_end(detail::to_address(begin) + count)
+ {
+ }
+
+ template <typename I,
+ typename S,
+ typename = decltype(detail::to_address(SCN_DECLVAL(I)),
+ detail::to_address(SCN_DECLVAL(S)))>
+ SCN_CONSTEXPR14 span(I first, S last) noexcept
+ : m_ptr(detail::to_address(first)),
+ m_end(detail::to_address_safe(last, first, last))
+ {
+ }
+
+ template <typename U = typename std::add_const<T>::type,
+ typename E = element_type,
+ typename = typename std::enable_if<
+ std::is_same<E, value_type>::value>::type>
+ constexpr span(span<U> other) : m_ptr(other.m_ptr), m_end(other.m_end)
+ {
+ }
+
+ template <size_t N>
+ constexpr span(element_type (&arr)[N]) noexcept
+ : m_ptr(&arr), m_end(&arr + N)
+ {
+ }
+
+ SCN_CONSTEXPR14 iterator begin() noexcept
+ {
+ return m_ptr;
+ }
+ SCN_CONSTEXPR14 iterator end() noexcept
+ {
+ return m_end;
+ }
+ SCN_CONSTEXPR14 reverse_iterator rbegin() noexcept
+ {
+ return reverse_iterator{end()};
+ }
+ SCN_CONSTEXPR14 reverse_iterator rend() noexcept
+ {
+ return reverse_iterator{begin()};
+ }
+
+ constexpr const_iterator begin() const noexcept
+ {
+ return m_ptr;
+ }
+ constexpr const_iterator end() const noexcept
+ {
+ return m_end;
+ }
+ constexpr const_reverse_iterator rbegin() const noexcept
+ {
+ return reverse_iterator{end()};
+ }
+ constexpr const_reverse_iterator rend() const noexcept
+ {
+ return reverse_iterator{begin()};
+ }
+
+ constexpr const_iterator cbegin() const noexcept
+ {
+ return m_ptr;
+ }
+ constexpr const_iterator cend() const noexcept
+ {
+ return m_end;
+ }
+ constexpr const_reverse_iterator crbegin() const noexcept
+ {
+ return reverse_iterator{cend()};
+ }
+ constexpr const_reverse_iterator crend() const noexcept
+ {
+ return reverse_iterator{cbegin()};
+ }
+
+ SCN_CONSTEXPR14 reference operator[](index_type i) const noexcept
+ {
+ SCN_EXPECT(size() > i);
+ return *(m_ptr + i);
+ }
+
+ constexpr pointer data() const noexcept
+ {
+ return m_ptr;
+ }
+ SCN_NODISCARD constexpr index_type size() const noexcept
+ {
+ return static_cast<index_type>(m_end - m_ptr);
+ }
+ SCN_NODISCARD constexpr ssize_type ssize() const noexcept
+ {
+ return m_end - m_ptr;
+ }
+
+ SCN_CONSTEXPR14 span<T> first(index_type n) const
+ {
+ SCN_EXPECT(size() >= n);
+ return span<T>(data(), data() + n);
+ }
+ SCN_CONSTEXPR14 span<T> last(index_type n) const
+ {
+ SCN_EXPECT(size() >= n);
+ return span<T>(data() + size() - n, data() + size());
+ }
+ SCN_CONSTEXPR14 span<T> subspan(index_type off) const
+ {
+ SCN_EXPECT(size() >= off);
+ return span<T>(data() + off, size() - off);
+ }
+ SCN_CONSTEXPR14 span<T> subspan(index_type off,
+ difference_type count) const
+ {
+ SCN_EXPECT(size() > off + count);
+ SCN_EXPECT(count > 0);
+ return span<T>(data() + off, count);
+ }
+
+ constexpr operator span<typename std::add_const<T>::type>() const
+ {
+ return {m_ptr, m_end};
+ }
+ constexpr span<typename std::add_const<T>::type> as_const() const
+ {
+ return {m_ptr, m_end};
+ }
+
+ private:
+ pointer m_ptr{nullptr};
+ pointer m_end{nullptr};
+ };
+
+ template <typename I,
+ typename S,
+ typename Ptr = decltype(detail::to_address(SCN_DECLVAL(I))),
+ typename SPtr = decltype(detail::to_address(SCN_DECLVAL(S))),
+ typename ValueT = typename detail::remove_reference<
+ typename std::remove_pointer<Ptr>::type>::type>
+ SCN_CONSTEXPR14 auto make_span(I first, S last) noexcept -> span<ValueT>
+ {
+ return {first, last};
+ }
+ template <typename I,
+ typename Ptr = decltype(detail::to_address(SCN_DECLVAL(I))),
+ typename ValueT = typename detail::remove_reference<
+ typename std::remove_pointer<Ptr>::type>::type>
+ SCN_CONSTEXPR14 auto make_span(I first, std::size_t len) noexcept
+ -> span<ValueT>
+ {
+ return {first, len};
+ }
+
+ template <typename T>
+ SCN_CONSTEXPR14 span<typename T::value_type> make_span(
+ T& container) noexcept
+ {
+ using std::begin;
+ using std::end;
+ return span<typename T::value_type>(
+ detail::to_address(begin(container)),
+ detail::to_address_safe(end(container), begin(container),
+ end(container)));
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_SPAN_H
diff --git a/src/third-party/scnlib/include/scn/util/string_view.h b/src/third-party/scnlib/include/scn/util/string_view.h
new file mode 100644
index 0000000..576b93e
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/string_view.h
@@ -0,0 +1,270 @@
+// 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_UTIL_STRING_VIEW_H
+#define SCN_UTIL_STRING_VIEW_H
+
+#include "algorithm.h"
+#include "span.h"
+
+#include <cstdint>
+#include <cstring>
+#include <cwchar>
+
+#if SCN_HAS_STRING_VIEW
+#include <string_view>
+#endif
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ inline size_t strlen(const char* s) noexcept
+ {
+ return ::std::strlen(s);
+ }
+ inline size_t strlen(const wchar_t* s) noexcept
+ {
+ return ::std::wcslen(s);
+ }
+ inline size_t strlen(const char16_t* s) noexcept
+ {
+ SCN_EXPECT(s);
+ auto end = s;
+ for (; *end != u'\0'; ++end)
+ ;
+ return static_cast<size_t>(end - s);
+ }
+ inline size_t strlen(const char32_t* s) noexcept
+ {
+ SCN_EXPECT(s);
+ auto end = s;
+ for (; *end != U'\0'; ++end)
+ ;
+ return static_cast<size_t>(end - s);
+ }
+#if SCN_HAS_CHAR8
+ inline size_t strlen(const char8_t* s) noexcept
+ {
+ return std::strlen(reinterpret_cast<const char*>(s));
+ }
+#endif
+ } // namespace detail
+
+ /**
+ * A view over a (sub)string.
+ * Used even when std::string_view is available to avoid compatibility
+ * issues.
+ */
+ template <typename CharT>
+ class basic_string_view {
+ public:
+ using value_type = CharT;
+ using span_type = span<const value_type>;
+
+ using pointer = value_type*;
+ using const_pointer = const value_type*;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using iterator = typename span_type::const_iterator;
+ using const_iterator = iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = reverse_iterator;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using span_index_type = typename span_type::index_type;
+
+ static constexpr const size_type npos = size_type(-1);
+
+ constexpr basic_string_view() noexcept = default;
+ constexpr basic_string_view(const_pointer s, size_type c)
+ : m_data(s, static_cast<span_index_type>(c))
+ {
+ }
+ constexpr basic_string_view(const_pointer s)
+ : m_data(s, static_cast<span_index_type>(detail::strlen(s)))
+ {
+ }
+ template <size_t N>
+ constexpr basic_string_view(const CharT (&s)[N]) : m_data(s, N)
+ {
+ }
+ constexpr basic_string_view(const_pointer first, const_pointer last)
+ : m_data(first, last)
+ {
+ }
+#if SCN_HAS_STRING_VIEW
+ constexpr basic_string_view(std::basic_string_view<value_type> str)
+ : m_data(str.data(), str.size())
+ {
+ }
+#endif
+
+ template <typename T = char16_t,
+ typename std::enable_if<std::is_same<T, char16_t>::value &&
+ std::is_same<CharT, wchar_t>::value &&
+ sizeof(char16_t) ==
+ sizeof(wchar_t)>::type* = nullptr>
+ constexpr basic_string_view(const T* s)
+ : basic_string_view(reinterpret_cast<const wchar_t*>(s))
+ {
+ }
+ template <typename T = char32_t,
+ typename std::enable_if<std::is_same<T, char32_t>::value &&
+ std::is_same<CharT, wchar_t>::value &&
+ sizeof(char32_t) ==
+ sizeof(wchar_t)>::type* = nullptr>
+ constexpr basic_string_view(const T* s)
+ : basic_string_view(reinterpret_cast<const wchar_t*>(s))
+ {
+ }
+#if SCN_HAS_CHAR8
+ template <typename T = char8_t,
+ typename std::enable_if<
+ std::is_same<T, char8_t>::value &&
+ std::is_same<CharT, char>::value>::type* = nullptr>
+ constexpr basic_string_view(const T* s)
+ : basic_string_view(reinterpret_cast<const char*>(s))
+ {
+ }
+#endif
+
+ constexpr const_iterator begin() const noexcept
+ {
+ return cbegin();
+ }
+ constexpr const_iterator cbegin() const noexcept
+ {
+ return m_data.cbegin();
+ }
+ constexpr const_iterator end() const noexcept
+ {
+ return cend();
+ }
+ constexpr const_iterator cend() const noexcept
+ {
+ return m_data.cend();
+ }
+
+ constexpr const_reverse_iterator rbegin() const noexcept
+ {
+ return crbegin();
+ }
+ constexpr const_reverse_iterator crbegin() const noexcept
+ {
+ return m_data.crbegin();
+ }
+ constexpr const_reverse_iterator rend() const noexcept
+ {
+ return crend();
+ }
+ constexpr const_reverse_iterator crend() const noexcept
+ {
+ return m_data.crend();
+ }
+
+ constexpr const_reference operator[](size_type pos) const
+ {
+ return m_data[static_cast<typename span_type::index_type>(pos)];
+ }
+ SCN_CONSTEXPR14 const_reference at(size_type pos) const
+ {
+ SCN_EXPECT(pos < size());
+ return m_data.at(static_cast<typename span_type::index_type>(pos));
+ }
+
+ constexpr const_reference front() const
+ {
+ return operator[](0);
+ }
+ constexpr const_reference back() const
+ {
+ return operator[](size() - 1);
+ }
+ constexpr const_pointer data() const noexcept
+ {
+ return m_data.data();
+ }
+
+ SCN_NODISCARD constexpr size_type size() const noexcept
+ {
+ return static_cast<size_type>(m_data.size());
+ }
+ SCN_NODISCARD constexpr size_type length() const noexcept
+ {
+ return size();
+ }
+ SCN_NODISCARD constexpr size_type max_size() const noexcept
+ {
+ return SIZE_MAX - 1;
+ }
+ SCN_NODISCARD constexpr bool empty() const noexcept
+ {
+ return size() == 0;
+ }
+
+ SCN_CONSTEXPR14 void remove_prefix(size_type n)
+ {
+ SCN_EXPECT(n <= size());
+ m_data = m_data.subspan(n);
+ }
+ SCN_CONSTEXPR14 void remove_suffix(size_type n)
+ {
+ SCN_EXPECT(n <= size());
+ m_data = m_data.first(size() - n);
+ }
+
+ SCN_CONSTEXPR14 void swap(basic_string_view& v) noexcept
+ {
+ using std::swap;
+ swap(m_data, v.m_data);
+ }
+
+ size_type copy(pointer dest, size_type count, size_type pos = 0) const
+ {
+ SCN_EXPECT(pos <= size());
+ auto n = detail::min(count, size() - pos);
+ /* std::copy(data() + pos, n, dest); */
+ std::memcpy(dest, begin() + pos, n * sizeof(value_type));
+ return n;
+ }
+ SCN_CONSTEXPR14 basic_string_view substr(size_type pos = 0,
+ size_type count = npos) const
+ {
+ SCN_EXPECT(pos <= size());
+ auto n = detail::min(count, size() - pos);
+ return m_data.subspan(pos, n);
+ }
+
+#if SCN_HAS_STRING_VIEW
+ operator std::basic_string_view<value_type>() const noexcept
+ {
+ return {m_data.data(), m_data.size()};
+ }
+#endif
+
+ private:
+ span_type m_data{};
+ };
+
+ using string_view = basic_string_view<char>;
+ using wstring_view = basic_string_view<wchar_t>;
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif // SCN_STRING_VIEW_H
diff --git a/src/third-party/scnlib/include/scn/util/unique_ptr.h b/src/third-party/scnlib/include/scn/util/unique_ptr.h
new file mode 100644
index 0000000..4723de8
--- /dev/null
+++ b/src/third-party/scnlib/include/scn/util/unique_ptr.h
@@ -0,0 +1,118 @@
+// 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_UTIL_UNIQUE_PTR_H
+#define SCN_UTIL_UNIQUE_PTR_H
+
+#include "../detail/fwd.h"
+
+#include <type_traits>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ /**
+ * `std::unique_ptr` implementation with [[clang::trivial_abi]], without
+ * including `<memory>`
+ */
+ template <typename T>
+ class SCN_TRIVIAL_ABI unique_ptr {
+ public:
+ using element_type = T;
+ using pointer = T*;
+
+ constexpr unique_ptr() noexcept = default;
+ constexpr unique_ptr(std::nullptr_t) noexcept {}
+
+ constexpr explicit unique_ptr(pointer p) noexcept : m_ptr(p) {}
+
+ template <
+ typename U,
+ typename std::enable_if<
+ std::is_convertible<U*, pointer>::value>::type* = nullptr>
+ SCN_CONSTEXPR14 unique_ptr(unique_ptr<U>&& u) noexcept
+ : m_ptr(SCN_MOVE(u.get()))
+ {
+ u.reset();
+ }
+
+ unique_ptr(const unique_ptr&) = delete;
+ unique_ptr& operator=(const unique_ptr&) = delete;
+
+ SCN_CONSTEXPR14 unique_ptr(unique_ptr&& p) noexcept
+ : m_ptr(SCN_MOVE(p.m_ptr))
+ {
+ p.m_ptr = nullptr;
+ }
+ unique_ptr& operator=(unique_ptr&& p) noexcept
+ {
+ delete m_ptr;
+ m_ptr = p.m_ptr;
+ p.m_ptr = nullptr;
+ return *this;
+ }
+
+ ~unique_ptr() noexcept
+ {
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ delete m_ptr;
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+
+ constexpr explicit operator bool() const noexcept
+ {
+ return get() != nullptr;
+ }
+
+ constexpr pointer get() const noexcept
+ {
+ return m_ptr;
+ }
+
+ constexpr pointer operator->() const noexcept
+ {
+ return m_ptr;
+ }
+ constexpr typename std::add_lvalue_reference<T>::type operator*()
+ const
+ {
+ return *m_ptr;
+ }
+
+ SCN_CONSTEXPR14 void reset()
+ {
+ m_ptr = nullptr;
+ }
+
+ private:
+ pointer m_ptr{nullptr};
+ };
+
+ template <typename T, typename... Args>
+ unique_ptr<T> make_unique(Args&&... a)
+ {
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ return unique_ptr<T>(new T(SCN_FWD(a)...));
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ }
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
+
+#endif