summaryrefslogtreecommitdiffstats
path: root/src/third-party/scnlib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:44:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 17:44:55 +0000
commit5068d34c08f951a7ea6257d305a1627b09a95817 (patch)
tree08213e2be853396a3b07ce15dbe222644dcd9a89 /src/third-party/scnlib
parentInitial commit. (diff)
downloadlnav-5068d34c08f951a7ea6257d305a1627b09a95817.tar.xz
lnav-5068d34c08f951a7ea6257d305a1627b09a95817.zip
Adding upstream version 0.11.1.upstream/0.11.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-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
-rw-r--r--src/third-party/scnlib/licenses/README.md25
-rw-r--r--src/third-party/scnlib/licenses/fast_float-apache.txt190
-rw-r--r--src/third-party/scnlib/licenses/fast_float-mit.txt27
-rw-r--r--src/third-party/scnlib/licenses/fmt.rst27
-rw-r--r--src/third-party/scnlib/licenses/nanorange.txt23
-rw-r--r--src/third-party/scnlib/licenses/utfcpp.txt23
-rw-r--r--src/third-party/scnlib/src/Makefile.am65
-rw-r--r--src/third-party/scnlib/src/deps/fast_float/single_include/fast_float/fast_float.h2981
-rw-r--r--src/third-party/scnlib/src/file.cpp311
-rw-r--r--src/third-party/scnlib/src/locale.cpp668
-rw-r--r--src/third-party/scnlib/src/reader_float.cpp433
-rw-r--r--src/third-party/scnlib/src/reader_int.cpp372
-rw-r--r--src/third-party/scnlib/src/vscan.cpp80
64 files changed, 21866 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
diff --git a/src/third-party/scnlib/licenses/README.md b/src/third-party/scnlib/licenses/README.md
new file mode 100644
index 0000000..cd5e2d4
--- /dev/null
+++ b/src/third-party/scnlib/licenses/README.md
@@ -0,0 +1,25 @@
+# Third-party Software Licenses
+
+## {fmt}
+
+{fmt} is licensed under the MIT license.
+Copyright (c) 2012 - present, Victor Zverovich
+See `fmt.rst` for more
+
+## NanoRange
+
+NanoRange is licensed under the Boost Software License, version 1.0.
+Copyright (c) 2018 Tristan Brindle (tcbrindle at gmail dot com)
+See `nanorange.txt` for more
+
+## fast_float
+
+fast_float is licensed under either of Apache License, version 2.0 or MIT license.
+Copyright (c) 2021 The fast_float authors
+See `fast_float-apache.txt` and `fast_float-mit.txt` for more
+
+## utfcpp
+
+utfcpp is licensed under the Boost Software License, version 1.0.
+Copyright (c) 2006 Nemanja Trifunovic
+See `utfcpp.txt` for more
diff --git a/src/third-party/scnlib/licenses/fast_float-apache.txt b/src/third-party/scnlib/licenses/fast_float-apache.txt
new file mode 100644
index 0000000..26f4398
--- /dev/null
+++ b/src/third-party/scnlib/licenses/fast_float-apache.txt
@@ -0,0 +1,190 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2021 The fast_float authors
+
+ 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
+
+ http://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.
diff --git a/src/third-party/scnlib/licenses/fast_float-mit.txt b/src/third-party/scnlib/licenses/fast_float-mit.txt
new file mode 100644
index 0000000..2fb2a37
--- /dev/null
+++ b/src/third-party/scnlib/licenses/fast_float-mit.txt
@@ -0,0 +1,27 @@
+MIT License
+
+Copyright (c) 2021 The fast_float authors
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/third-party/scnlib/licenses/fmt.rst b/src/third-party/scnlib/licenses/fmt.rst
new file mode 100644
index 0000000..f0ec3db
--- /dev/null
+++ b/src/third-party/scnlib/licenses/fmt.rst
@@ -0,0 +1,27 @@
+Copyright (c) 2012 - present, Victor Zverovich
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+--- Optional exception to the license ---
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into a machine-executable object form of such
+source code, you may redistribute such embedded portions in such object form
+without including the above copyright and permission notices.
diff --git a/src/third-party/scnlib/licenses/nanorange.txt b/src/third-party/scnlib/licenses/nanorange.txt
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/src/third-party/scnlib/licenses/nanorange.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/third-party/scnlib/licenses/utfcpp.txt b/src/third-party/scnlib/licenses/utfcpp.txt
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/src/third-party/scnlib/licenses/utfcpp.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/third-party/scnlib/src/Makefile.am b/src/third-party/scnlib/src/Makefile.am
new file mode 100644
index 0000000..7e5c4ae
--- /dev/null
+++ b/src/third-party/scnlib/src/Makefile.am
@@ -0,0 +1,65 @@
+
+noinst_HEADERS = \
+ ../include/scn/reader/reader.h \
+ ../include/scn/reader/float.h \
+ ../include/scn/reader/types.h \
+ ../include/scn/reader/int.h \
+ ../include/scn/reader/common.h \
+ ../include/scn/reader/string.h \
+ ../include/scn/ranges/custom_impl.h \
+ ../include/scn/ranges/std_impl.h \
+ ../include/scn/ranges/ranges.h \
+ ../include/scn/ranges/util.h \
+ ../include/scn/fwd.h \
+ ../include/scn/util/algorithm.h \
+ ../include/scn/util/small_vector.h \
+ ../include/scn/util/optional.h \
+ ../include/scn/util/expected.h \
+ ../include/scn/util/array.h \
+ ../include/scn/util/unique_ptr.h \
+ ../include/scn/util/math.h \
+ ../include/scn/util/memory.h \
+ ../include/scn/util/span.h \
+ ../include/scn/util/meta.h \
+ ../include/scn/util/string_view.h \
+ ../include/scn/unicode/unicode.h \
+ ../include/scn/unicode/common.h \
+ ../include/scn/unicode/utf16.h \
+ ../include/scn/unicode/utf8.h \
+ ../include/scn/all.h \
+ ../include/scn/tuple_return/tuple_return.h \
+ ../include/scn/tuple_return/util.h \
+ ../include/scn/scan/ignore.h \
+ ../include/scn/scan/getline.h \
+ ../include/scn/scan/list.h \
+ ../include/scn/scan/common.h \
+ ../include/scn/scan/istream.h \
+ ../include/scn/scan/vscan.h \
+ ../include/scn/scan/scan.h \
+ ../include/scn/tuple_return.h \
+ ../include/scn/detail/error.h \
+ ../include/scn/detail/fwd.h \
+ ../include/scn/detail/range.h \
+ ../include/scn/detail/locale.h \
+ ../include/scn/detail/config.h \
+ ../include/scn/detail/file.h \
+ ../include/scn/detail/context.h \
+ ../include/scn/detail/result.h \
+ ../include/scn/detail/visitor.h \
+ ../include/scn/detail/args.h \
+ ../include/scn/detail/parse_context.h \
+ ../include/scn/detail/vectored.h \
+ ../include/scn/scn.h \
+ ../include/scn/istream.h \
+ deps/fast_float/single_include/fast_float/fast_float.h
+
+noinst_LIBRARIES = libscnlib.a
+
+AM_CPPFLAGS = -I$(srcdir)/../include -I$(srcdir)/deps/fast_float/single_include
+
+libscnlib_a_SOURCES = \
+ reader_float.cpp \
+ locale.cpp \
+ reader_int.cpp \
+ file.cpp \
+ vscan.cpp
diff --git a/src/third-party/scnlib/src/deps/fast_float/single_include/fast_float/fast_float.h b/src/third-party/scnlib/src/deps/fast_float/single_include/fast_float/fast_float.h
new file mode 100644
index 0000000..3f94372
--- /dev/null
+++ b/src/third-party/scnlib/src/deps/fast_float/single_include/fast_float/fast_float.h
@@ -0,0 +1,2981 @@
+// fast_float v3.4.0
+
+// fast_float by Daniel Lemire
+// fast_float by João Paulo Magalhaes
+//
+// with contributions from Eugene Golushkov
+// with contributions from Maksim Kita
+// with contributions from Marcin Wojdyr
+// with contributions from Neal Richardson
+// with contributions from Tim Paine
+// with contributions from Fabio Pellacini
+//
+// Licensed under the Apache License, Version 2.0, or the
+// MIT License at your option. This file may not be copied,
+// modified, or distributed except according to those terms.
+//
+// MIT License Notice
+//
+// MIT License
+//
+// Copyright (c) 2021 The fast_float authors
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in the
+// Software without restriction, including without
+// limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// Apache License (Version 2.0) Notice
+//
+// Copyright 2021 The fast_float authors
+// 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
+//
+// http://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
+//
+
+#ifndef FASTFLOAT_FAST_FLOAT_H
+#define FASTFLOAT_FAST_FLOAT_H
+
+#include <system_error>
+
+namespace fast_float {
+ enum chars_format {
+ scientific = 1<<0,
+ fixed = 1<<2,
+ hex = 1<<3,
+ general = fixed | scientific
+ };
+
+
+ struct from_chars_result {
+ const char *ptr;
+ std::errc ec;
+ };
+
+ struct parse_options {
+ constexpr explicit parse_options(chars_format fmt = chars_format::general,
+ char dot = '.')
+ : format(fmt), decimal_point(dot) {}
+
+ /** Which number formats are accepted */
+ chars_format format;
+ /** The character used as decimal point */
+ char decimal_point;
+ };
+
+ /**
+ * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
+ * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
+ * The resulting floating-point value is the closest floating-point values (using either float or double),
+ * using the "round to even" convention for values that would otherwise fall right in-between two values.
+ * That is, we provide exact parsing according to the IEEE standard.
+ *
+ * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
+ * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
+ * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
+ *
+ * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
+ *
+ * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
+ * the type `fast_float::chars_format`. It is a bitset value: we check whether
+ * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
+ * to determine whether we allowe the fixed point and scientific notation respectively.
+ * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
+ */
+ template<typename T>
+ from_chars_result from_chars(const char *first, const char *last,
+ T &value, chars_format fmt = chars_format::general) noexcept;
+
+ /**
+ * Like from_chars, but accepts an `options` argument to govern number parsing.
+ */
+ template<typename T>
+ from_chars_result from_chars_advanced(const char *first, const char *last,
+ T &value, parse_options options) noexcept;
+
+}
+#endif // FASTFLOAT_FAST_FLOAT_H
+
+#ifndef FASTFLOAT_FLOAT_COMMON_H
+#define FASTFLOAT_FLOAT_COMMON_H
+
+#include <cfloat>
+#include <cstdint>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
+ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
+ || defined(__MINGW64__) \
+ || defined(__s390x__) \
+ || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
+ || defined(__EMSCRIPTEN__))
+#define FASTFLOAT_64BIT
+#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
+ || defined(__arm__) || defined(_M_ARM) \
+ || defined(__MINGW32__))
+#define FASTFLOAT_32BIT
+#else
+ // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
+// We can never tell the register width, but the SIZE_MAX is a good approximation.
+// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
+#if SIZE_MAX == 0xffff
+#error Unknown platform (16-bit, unsupported)
+#elif SIZE_MAX == 0xffffffff
+#define FASTFLOAT_32BIT
+#elif SIZE_MAX == 0xffffffffffffffff
+#define FASTFLOAT_64BIT
+#else
+#error Unknown platform (not 32-bit, not 64-bit?)
+#endif
+#endif
+
+#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__))
+#include <intrin.h>
+#endif
+
+#if defined(_MSC_VER) && !defined(__clang__)
+#define FASTFLOAT_VISUAL_STUDIO 1
+#endif
+
+#ifdef _WIN32
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#else
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <machine/endian.h>
+#elif defined(sun) || defined(__sun)
+#include <sys/byteorder.h>
+#else
+#include <endian.h>
+#endif
+#
+#ifndef __BYTE_ORDER__
+// safe choice
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#endif
+#
+#ifndef __ORDER_LITTLE_ENDIAN__
+// safe choice
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#endif
+#
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#else
+#define FASTFLOAT_IS_BIG_ENDIAN 1
+#endif
+#endif
+
+#ifdef FASTFLOAT_VISUAL_STUDIO
+#define fastfloat_really_inline __forceinline
+#else
+#define fastfloat_really_inline inline __attribute__((always_inline))
+#endif
+
+#ifndef FASTFLOAT_ASSERT
+#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); }
+#endif
+
+#ifndef FASTFLOAT_DEBUG_ASSERT
+#include <cassert>
+#define FASTFLOAT_DEBUG_ASSERT(x) assert(x)
+#endif
+
+// rust style `try!()` macro, or `?` operator
+#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
+
+namespace fast_float {
+
+ // Compares two ASCII strings in a case insensitive manner.
+ inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
+ size_t length) {
+ char running_diff{0};
+ for (size_t i = 0; i < length; i++) {
+ running_diff |= (input1[i] ^ input2[i]);
+ }
+ return (running_diff == 0) || (running_diff == 32);
+ }
+
+#ifndef FLT_EVAL_METHOD
+#error "FLT_EVAL_METHOD should be defined, please include cfloat."
+#endif
+
+ // a pointer and a length to a contiguous block of memory
+ template <typename T>
+ struct span {
+ const T* ptr;
+ size_t length;
+ span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
+ span() : ptr(nullptr), length(0) {}
+
+ constexpr size_t len() const noexcept {
+ return length;
+ }
+
+ const T& operator[](size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return ptr[index];
+ }
+ };
+
+ struct value128 {
+ uint64_t low;
+ uint64_t high;
+ value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
+ value128() : low(0), high(0) {}
+ };
+
+ /* result might be undefined when input_num is zero */
+ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
+ assert(input_num > 0);
+#ifdef FASTFLOAT_VISUAL_STUDIO
+ #if defined(_M_X64) || defined(_M_ARM64)
+ unsigned long leading_zero = 0;
+ // Search the mask data from most significant bit (MSB)
+ // to least significant bit (LSB) for a set bit (1).
+ _BitScanReverse64(&leading_zero, input_num);
+ return (int)(63 - leading_zero);
+#else
+ int last_bit = 0;
+ if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32;
+ if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16;
+ if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8;
+ if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4;
+ if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2;
+ if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1;
+ return 63 - last_bit;
+#endif
+#else
+ return __builtin_clzll(input_num);
+#endif
+ }
+
+#ifdef FASTFLOAT_32BIT
+
+ // slow emulation routine for 32-bit
+ fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
+ return x * (uint64_t)y;
+ }
+
+// slow emulation routine for 32-bit
+#if !defined(__MINGW64__)
+ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
+ uint64_t *hi) {
+ uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
+ uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
+ uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
+ uint64_t adbc_carry = !!(adbc < ad);
+ uint64_t lo = bd + (adbc << 32);
+ *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
+ (adbc_carry << 32) + !!(lo < bd);
+ return lo;
+ }
+#endif // !__MINGW64__
+
+#endif // FASTFLOAT_32BIT
+
+
+ // compute 64-bit a*b
+ fastfloat_really_inline value128 full_multiplication(uint64_t a,
+ uint64_t b) {
+ value128 answer;
+#ifdef _M_ARM64
+ // ARM64 has native support for 64-bit multiplications, no need to emulate
+ answer.high = __umulh(a, b);
+ answer.low = a * b;
+#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
+ answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
+#elif defined(FASTFLOAT_64BIT)
+ __uint128_t r = ((__uint128_t)a) * b;
+ answer.low = uint64_t(r);
+ answer.high = uint64_t(r >> 64);
+#else
+#error Not implemented
+#endif
+ return answer;
+ }
+
+ struct adjusted_mantissa {
+ uint64_t mantissa{0};
+ int32_t power2{0}; // a negative value indicates an invalid result
+ adjusted_mantissa() = default;
+ bool operator==(const adjusted_mantissa &o) const {
+ return mantissa == o.mantissa && power2 == o.power2;
+ }
+ bool operator!=(const adjusted_mantissa &o) const {
+ return mantissa != o.mantissa || power2 != o.power2;
+ }
+ };
+
+ // Bias so we can get the real exponent with an invalid adjusted_mantissa.
+ constexpr static int32_t invalid_am_bias = -0x8000;
+
+ constexpr static double powers_of_ten_double[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
+ 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
+ constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5,
+ 1e6, 1e7, 1e8, 1e9, 1e10};
+
+ template <typename T> struct binary_format {
+ using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
+
+ static inline constexpr int mantissa_explicit_bits();
+ static inline constexpr int minimum_exponent();
+ static inline constexpr int infinite_power();
+ static inline constexpr int sign_index();
+ static inline constexpr int min_exponent_fast_path();
+ static inline constexpr int max_exponent_fast_path();
+ static inline constexpr int max_exponent_round_to_even();
+ static inline constexpr int min_exponent_round_to_even();
+ static inline constexpr uint64_t max_mantissa_fast_path();
+ static inline constexpr int largest_power_of_ten();
+ static inline constexpr int smallest_power_of_ten();
+ static inline constexpr T exact_power_of_ten(int64_t power);
+ static inline constexpr size_t max_digits();
+ static inline constexpr equiv_uint exponent_mask();
+ static inline constexpr equiv_uint mantissa_mask();
+ static inline constexpr equiv_uint hidden_bit_mask();
+ };
+
+ template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
+ return 52;
+ }
+ template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
+ return 23;
+ }
+
+ template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
+ return 23;
+ }
+
+ template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
+ return 10;
+ }
+
+ template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
+ return -4;
+ }
+
+ template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
+ return -17;
+ }
+
+ template <> inline constexpr int binary_format<double>::minimum_exponent() {
+ return -1023;
+ }
+ template <> inline constexpr int binary_format<float>::minimum_exponent() {
+ return -127;
+ }
+
+ template <> inline constexpr int binary_format<double>::infinite_power() {
+ return 0x7FF;
+ }
+ template <> inline constexpr int binary_format<float>::infinite_power() {
+ return 0xFF;
+ }
+
+ template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
+ template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
+
+ template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
+#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
+ return 0;
+#else
+ return -22;
+#endif
+ }
+ template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
+#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
+ return 0;
+#else
+ return -10;
+#endif
+ }
+
+ template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
+ return 22;
+ }
+ template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
+ return 10;
+ }
+
+ template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+ }
+ template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+ }
+
+ template <>
+ inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
+ return powers_of_ten_double[power];
+ }
+ template <>
+ inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
+
+ return powers_of_ten_float[power];
+ }
+
+
+ template <>
+ inline constexpr int binary_format<double>::largest_power_of_ten() {
+ return 308;
+ }
+ template <>
+ inline constexpr int binary_format<float>::largest_power_of_ten() {
+ return 38;
+ }
+
+ template <>
+ inline constexpr int binary_format<double>::smallest_power_of_ten() {
+ return -342;
+ }
+ template <>
+ inline constexpr int binary_format<float>::smallest_power_of_ten() {
+ return -65;
+ }
+
+ template <> inline constexpr size_t binary_format<double>::max_digits() {
+ return 769;
+ }
+ template <> inline constexpr size_t binary_format<float>::max_digits() {
+ return 114;
+ }
+
+ template <> inline constexpr binary_format<float>::equiv_uint
+ binary_format<float>::exponent_mask() {
+ return 0x7F800000;
+ }
+ template <> inline constexpr binary_format<double>::equiv_uint
+ binary_format<double>::exponent_mask() {
+ return 0x7FF0000000000000;
+ }
+
+ template <> inline constexpr binary_format<float>::equiv_uint
+ binary_format<float>::mantissa_mask() {
+ return 0x007FFFFF;
+ }
+ template <> inline constexpr binary_format<double>::equiv_uint
+ binary_format<double>::mantissa_mask() {
+ return 0x000FFFFFFFFFFFFF;
+ }
+
+ template <> inline constexpr binary_format<float>::equiv_uint
+ binary_format<float>::hidden_bit_mask() {
+ return 0x00800000;
+ }
+ template <> inline constexpr binary_format<double>::equiv_uint
+ binary_format<double>::hidden_bit_mask() {
+ return 0x0010000000000000;
+ }
+
+ template<typename T>
+ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
+ uint64_t word = am.mantissa;
+ word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
+ word = negative
+ ? word | (uint64_t(1) << binary_format<T>::sign_index()) : word;
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ if (std::is_same<T, float>::value) {
+ ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian
+ } else {
+ ::memcpy(&value, &word, sizeof(T));
+ }
+#else
+ // For little-endian systems:
+ ::memcpy(&value, &word, sizeof(T));
+#endif
+ }
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_ASCII_NUMBER_H
+#define FASTFLOAT_ASCII_NUMBER_H
+
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+
+
+namespace fast_float {
+
+ // Next function can be micro-optimized, but compilers are entirely
+ // able to optimize it well.
+ fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
+
+ fastfloat_really_inline uint64_t byteswap(uint64_t val) {
+ return (val & 0xFF00000000000000) >> 56
+ | (val & 0x00FF000000000000) >> 40
+ | (val & 0x0000FF0000000000) >> 24
+ | (val & 0x000000FF00000000) >> 8
+ | (val & 0x00000000FF000000) << 8
+ | (val & 0x0000000000FF0000) << 24
+ | (val & 0x000000000000FF00) << 40
+ | (val & 0x00000000000000FF) << 56;
+ }
+
+ fastfloat_really_inline uint64_t read_u64(const char *chars) {
+ uint64_t val;
+ ::memcpy(&val, chars, sizeof(uint64_t));
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ return val;
+ }
+
+ fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ ::memcpy(chars, &val, sizeof(uint64_t));
+ }
+
+ // credit @aqrit
+ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
+ const uint64_t mask = 0x000000FF000000FF;
+ const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
+ const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
+ val -= 0x3030303030303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
+ return uint32_t(val);
+ }
+
+ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
+ return parse_eight_digits_unrolled(read_u64(chars));
+ }
+
+ // credit @aqrit
+ fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
+ return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
+ 0x8080808080808080));
+ }
+
+ fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
+ return is_made_of_eight_digits_fast(read_u64(chars));
+ }
+
+ typedef span<const char> byte_span;
+
+ struct parsed_number_string {
+ int64_t exponent{0};
+ uint64_t mantissa{0};
+ const char *lastmatch{nullptr};
+ bool negative{false};
+ bool valid{false};
+ bool too_many_digits{false};
+ // contains the range of the significant digits
+ byte_span integer{}; // non-nullable
+ byte_span fraction{}; // nullable
+ };
+
+ // Assuming that you use no more than 19 digits, this will
+ // parse an ASCII string.
+ fastfloat_really_inline
+ parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
+ const chars_format fmt = options.format;
+ const char decimal_point = options.decimal_point;
+
+ parsed_number_string answer;
+ answer.valid = false;
+ answer.too_many_digits = false;
+ answer.negative = (*p == '-');
+ if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
+ ++p;
+ if (p == pend) {
+ return answer;
+ }
+ if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
+ return answer;
+ }
+ }
+ const char *const start_digits = p;
+
+ uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
+
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ // a multiplication by 10 is cheaper than an arbitrary integer
+ // multiplication
+ i = 10 * i +
+ uint64_t(*p - '0'); // might overflow, we will handle the overflow later
+ ++p;
+ }
+ const char *const end_of_integer_part = p;
+ int64_t digit_count = int64_t(end_of_integer_part - start_digits);
+ answer.integer = byte_span(start_digits, size_t(digit_count));
+ int64_t exponent = 0;
+ if ((p != pend) && (*p == decimal_point)) {
+ ++p;
+ const char* before = p;
+ // can occur at most twice without overflowing, but let it occur more, since
+ // for integers with many digits, digit parsing is the primary bottleneck.
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ ++p;
+ i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
+ }
+ exponent = before - p;
+ answer.fraction = byte_span(before, size_t(p - before));
+ digit_count -= exponent;
+ }
+ // we must have encountered at least one integer!
+ if (digit_count == 0) {
+ return answer;
+ }
+ int64_t exp_number = 0; // explicit exponential part
+ if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
+ const char * location_of_e = p;
+ ++p;
+ bool neg_exp = false;
+ if ((p != pend) && ('-' == *p)) {
+ neg_exp = true;
+ ++p;
+ } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
+ ++p;
+ }
+ if ((p == pend) || !is_integer(*p)) {
+ if(!(fmt & chars_format::fixed)) {
+ // We are in error.
+ return answer;
+ }
+ // Otherwise, we will be ignoring the 'e'.
+ p = location_of_e;
+ } else {
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ if (exp_number < 0x10000000) {
+ exp_number = 10 * exp_number + digit;
+ }
+ ++p;
+ }
+ if(neg_exp) { exp_number = - exp_number; }
+ exponent += exp_number;
+ }
+ } else {
+ // If it scientific and not fixed, we have to bail out.
+ if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
+ }
+ answer.lastmatch = p;
+ answer.valid = true;
+
+ // If we frequently had to deal with long strings of digits,
+ // we could extend our code by using a 128-bit integer instead
+ // of a 64-bit integer. However, this is uncommon.
+ //
+ // We can deal with up to 19 digits.
+ if (digit_count > 19) { // this is uncommon
+ // It is possible that the integer had an overflow.
+ // We have to handle the case where we have 0.0000somenumber.
+ // We need to be mindful of the case where we only have zeroes...
+ // E.g., 0.000000000...000.
+ const char *start = start_digits;
+ while ((start != pend) && (*start == '0' || *start == decimal_point)) {
+ if(*start == '0') { digit_count --; }
+ start++;
+ }
+ if (digit_count > 19) {
+ answer.too_many_digits = true;
+ // Let us start again, this time, avoiding overflows.
+ // We don't need to check if is_integer, since we use the
+ // pre-tokenized spans from above.
+ i = 0;
+ p = answer.integer.ptr;
+ const char* int_end = p + answer.integer.len();
+ const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
+ while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ if (i >= minimal_nineteen_digit_integer) { // We have a big integers
+ exponent = end_of_integer_part - p + exp_number;
+ } else { // We have a value with a fractional component.
+ p = answer.fraction.ptr;
+ const char* frac_end = p + answer.fraction.len();
+ while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ exponent = answer.fraction.ptr - p + exp_number;
+ }
+ // We have now corrected both exponent and i, to a truncated value
+ }
+ }
+ answer.exponent = exponent;
+ answer.mantissa = i;
+ return answer;
+ }
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_FAST_TABLE_H
+#define FASTFLOAT_FAST_TABLE_H
+
+#include <cstdint>
+
+namespace fast_float {
+
+ /**
+ * When mapping numbers from decimal to binary,
+ * we go from w * 10^q to m * 2^p but we have
+ * 10^q = 5^q * 2^q, so effectively
+ * we are trying to match
+ * w * 2^q * 5^q to m * 2^p. Thus the powers of two
+ * are not a concern since they can be represented
+ * exactly using the binary notation, only the powers of five
+ * affect the binary significand.
+ */
+
+ /**
+ * The smallest non-zero float (binary64) is 2^−1074.
+ * We take as input numbers of the form w x 10^q where w < 2^64.
+ * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
+ * However, we have that
+ * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074.
+ * Thus it is possible for a number of the form w * 10^-342 where
+ * w is a 64-bit value to be a non-zero floating-point number.
+ *********
+ * Any number of form w * 10^309 where w>= 1 is going to be
+ * infinite in binary64 so we never need to worry about powers
+ * of 5 greater than 308.
+ */
+ template <class unused = void>
+ struct powers_template {
+
+ constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
+ constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
+ constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
+ // Powers of five from 5^-342 all the way to 5^308 rounded toward one.
+ static const uint64_t power_of_five_128[number_of_entries];
+ };
+
+ template <class unused>
+ const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
+ 0xeef453d6923bd65a,0x113faa2906a13b3f,
+ 0x9558b4661b6565f8,0x4ac7ca59a424c507,
+ 0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
+ 0xe95a99df8ace6f53,0xf4d82c2c107973dc,
+ 0x91d8a02bb6c10594,0x79071b9b8a4be869,
+ 0xb64ec836a47146f9,0x9748e2826cdee284,
+ 0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
+ 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
+ 0xb208ef855c969f4f,0xbdbd2d335e51a935,
+ 0xde8b2b66b3bc4723,0xad2c788035e61382,
+ 0x8b16fb203055ac76,0x4c3bcb5021afcc31,
+ 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
+ 0xd953e8624b85dd78,0xd71d6dad34a2af0d,
+ 0x87d4713d6f33aa6b,0x8672648c40e5ad68,
+ 0xa9c98d8ccb009506,0x680efdaf511f18c2,
+ 0xd43bf0effdc0ba48,0x212bd1b2566def2,
+ 0x84a57695fe98746d,0x14bb630f7604b57,
+ 0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
+ 0xcf42894a5dce35ea,0x52064cac828675b9,
+ 0x818995ce7aa0e1b2,0x7343efebd1940993,
+ 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
+ 0xca66fa129f9b60a6,0xd41a26e077774ef6,
+ 0xfd00b897478238d0,0x8920b098955522b4,
+ 0x9e20735e8cb16382,0x55b46e5f5d5535b0,
+ 0xc5a890362fddbc62,0xeb2189f734aa831d,
+ 0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
+ 0x9a6bb0aa55653b2d,0x47b233c92125366e,
+ 0xc1069cd4eabe89f8,0x999ec0bb696e840a,
+ 0xf148440a256e2c76,0xc00670ea43ca250d,
+ 0x96cd2a865764dbca,0x380406926a5e5728,
+ 0xbc807527ed3e12bc,0xc605083704f5ecf2,
+ 0xeba09271e88d976b,0xf7864a44c633682e,
+ 0x93445b8731587ea3,0x7ab3ee6afbe0211d,
+ 0xb8157268fdae9e4c,0x5960ea05bad82964,
+ 0xe61acf033d1a45df,0x6fb92487298e33bd,
+ 0x8fd0c16206306bab,0xa5d3b6d479f8e056,
+ 0xb3c4f1ba87bc8696,0x8f48a4899877186c,
+ 0xe0b62e2929aba83c,0x331acdabfe94de87,
+ 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
+ 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
+ 0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
+ 0x892731ac9faf056e,0xbe311c083a225cd2,
+ 0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
+ 0xd64d3d9db981787d,0x92cbbccdad5b108,
+ 0x85f0468293f0eb4e,0x25bbf56008c58ea5,
+ 0xa76c582338ed2621,0xaf2af2b80af6f24e,
+ 0xd1476e2c07286faa,0x1af5af660db4aee1,
+ 0x82cca4db847945ca,0x50d98d9fc890ed4d,
+ 0xa37fce126597973c,0xe50ff107bab528a0,
+ 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
+ 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
+ 0x9faacf3df73609b1,0x77b191618c54e9ac,
+ 0xc795830d75038c1d,0xd59df5b9ef6a2417,
+ 0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
+ 0x9becce62836ac577,0x4ee367f9430aec32,
+ 0xc2e801fb244576d5,0x229c41f793cda73f,
+ 0xf3a20279ed56d48a,0x6b43527578c1110f,
+ 0x9845418c345644d6,0x830a13896b78aaa9,
+ 0xbe5691ef416bd60c,0x23cc986bc656d553,
+ 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
+ 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
+ 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
+ 0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
+ 0x91376c36d99995be,0x23100809b9c21fa1,
+ 0xb58547448ffffb2d,0xabd40a0c2832a78a,
+ 0xe2e69915b3fff9f9,0x16c90c8f323f516c,
+ 0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
+ 0xb1442798f49ffb4a,0x99cd11cfdf41779c,
+ 0xdd95317f31c7fa1d,0x40405643d711d583,
+ 0x8a7d3eef7f1cfc52,0x482835ea666b2572,
+ 0xad1c8eab5ee43b66,0xda3243650005eecf,
+ 0xd863b256369d4a40,0x90bed43e40076a82,
+ 0x873e4f75e2224e68,0x5a7744a6e804a291,
+ 0xa90de3535aaae202,0x711515d0a205cb36,
+ 0xd3515c2831559a83,0xd5a5b44ca873e03,
+ 0x8412d9991ed58091,0xe858790afe9486c2,
+ 0xa5178fff668ae0b6,0x626e974dbe39a872,
+ 0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
+ 0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
+ 0xa139029f6a239f72,0x1c1fffc1ebc44e80,
+ 0xc987434744ac874e,0xa327ffb266b56220,
+ 0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
+ 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
+ 0xc4ce17b399107c22,0xcb550fb4384d21d3,
+ 0xf6019da07f549b2b,0x7e2a53a146606a48,
+ 0x99c102844f94e0fb,0x2eda7444cbfc426d,
+ 0xc0314325637a1939,0xfa911155fefb5308,
+ 0xf03d93eebc589f88,0x793555ab7eba27ca,
+ 0x96267c7535b763b5,0x4bc1558b2f3458de,
+ 0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
+ 0xea9c227723ee8bcb,0x465e15a979c1cadc,
+ 0x92a1958a7675175f,0xbfacd89ec191ec9,
+ 0xb749faed14125d36,0xcef980ec671f667b,
+ 0xe51c79a85916f484,0x82b7e12780e7401a,
+ 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
+ 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
+ 0xdfbdcece67006ac9,0x67a791e093e1d49a,
+ 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
+ 0xaecc49914078536d,0x58fae9f773886e18,
+ 0xda7f5bf590966848,0xaf39a475506a899e,
+ 0x888f99797a5e012d,0x6d8406c952429603,
+ 0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
+ 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
+ 0x855c3be0a17fcd26,0x5cf2eea09a55067f,
+ 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
+ 0xd0601d8efc57b08b,0xf13b94daf124da26,
+ 0x823c12795db6ce57,0x76c53d08d6b70858,
+ 0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
+ 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
+ 0xfe5d54150b090b02,0xd3f93b35435d7c4c,
+ 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
+ 0xc6b8e9b0709f109a,0x359ab6419ca1091b,
+ 0xf867241c8cc6d4c0,0xc30163d203c94b62,
+ 0x9b407691d7fc44f8,0x79e0de63425dcf1d,
+ 0xc21094364dfb5636,0x985915fc12f542e4,
+ 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
+ 0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
+ 0xbd8430bd08277231,0x50c6ff782a838353,
+ 0xece53cec4a314ebd,0xa4f8bf5635246428,
+ 0x940f4613ae5ed136,0x871b7795e136be99,
+ 0xb913179899f68584,0x28e2557b59846e3f,
+ 0xe757dd7ec07426e5,0x331aeada2fe589cf,
+ 0x9096ea6f3848984f,0x3ff0d2c85def7621,
+ 0xb4bca50b065abe63,0xfed077a756b53a9,
+ 0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
+ 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
+ 0xb080392cc4349dec,0xbd8d794d96aacfb3,
+ 0xdca04777f541c567,0xecf0d7a0fc5583a0,
+ 0x89e42caaf9491b60,0xf41686c49db57244,
+ 0xac5d37d5b79b6239,0x311c2875c522ced5,
+ 0xd77485cb25823ac7,0x7d633293366b828b,
+ 0x86a8d39ef77164bc,0xae5dff9c02033197,
+ 0xa8530886b54dbdeb,0xd9f57f830283fdfc,
+ 0xd267caa862a12d66,0xd072df63c324fd7b,
+ 0x8380dea93da4bc60,0x4247cb9e59f71e6d,
+ 0xa46116538d0deb78,0x52d9be85f074e608,
+ 0xcd795be870516656,0x67902e276c921f8b,
+ 0x806bd9714632dff6,0xba1cd8a3db53b6,
+ 0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
+ 0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
+ 0xfad2a4b13d1b5d6c,0x796b805720085f81,
+ 0x9cc3a6eec6311a63,0xcbe3303674053bb0,
+ 0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
+ 0xf4f1b4d515acb93b,0xee92fb5515482d44,
+ 0x991711052d8bf3c5,0x751bdd152d4d1c4a,
+ 0xbf5cd54678eef0b6,0xd262d45a78a0635d,
+ 0xef340a98172aace4,0x86fb897116c87c34,
+ 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
+ 0xbae0a846d2195712,0x8974836059cca109,
+ 0xe998d258869facd7,0x2bd1a438703fc94b,
+ 0x91ff83775423cc06,0x7b6306a34627ddcf,
+ 0xb67f6455292cbf08,0x1a3bc84c17b1d542,
+ 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
+ 0x8e938662882af53e,0x547eb47b7282ee9c,
+ 0xb23867fb2a35b28d,0xe99e619a4f23aa43,
+ 0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
+ 0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
+ 0xae0b158b4738705e,0x9624ab50b148d445,
+ 0xd98ddaee19068c76,0x3badd624dd9b0957,
+ 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
+ 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
+ 0xd47487cc8470652b,0x7647c3200069671f,
+ 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
+ 0xa5fb0a17c777cf09,0xf468107100525890,
+ 0xcf79cc9db955c2cc,0x7182148d4066eeb4,
+ 0x81ac1fe293d599bf,0xc6f14cd848405530,
+ 0xa21727db38cb002f,0xb8ada00e5a506a7c,
+ 0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
+ 0xfd442e4688bd304a,0x908f4a166d1da663,
+ 0x9e4a9cec15763e2e,0x9a598e4e043287fe,
+ 0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
+ 0xf7549530e188c128,0xd12bee59e68ef47c,
+ 0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
+ 0xc13a148e3032d6e7,0xe36a52363c1faf01,
+ 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
+ 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
+ 0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
+ 0xebdf661791d60f56,0x111b495b3464ad21,
+ 0x936b9fcebb25c995,0xcab10dd900beec34,
+ 0xb84687c269ef3bfb,0x3d5d514f40eea742,
+ 0xe65829b3046b0afa,0xcb4a5a3112a5112,
+ 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
+ 0xb3f4e093db73a093,0x59ed216765690f56,
+ 0xe0f218b8d25088b8,0x306869c13ec3532c,
+ 0x8c974f7383725573,0x1e414218c73a13fb,
+ 0xafbd2350644eeacf,0xe5d1929ef90898fa,
+ 0xdbac6c247d62a583,0xdf45f746b74abf39,
+ 0x894bc396ce5da772,0x6b8bba8c328eb783,
+ 0xab9eb47c81f5114f,0x66ea92f3f326564,
+ 0xd686619ba27255a2,0xc80a537b0efefebd,
+ 0x8613fd0145877585,0xbd06742ce95f5f36,
+ 0xa798fc4196e952e7,0x2c48113823b73704,
+ 0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
+ 0x82ef85133de648c4,0x9a984d73dbe722fb,
+ 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
+ 0xcc963fee10b7d1b3,0x318df905079926a8,
+ 0xffbbcfe994e5c61f,0xfdf17746497f7052,
+ 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
+ 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
+ 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
+ 0x9c1661a651213e2d,0x6bea10ca65c084e,
+ 0xc31bfa0fe5698db8,0x486e494fcff30a62,
+ 0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
+ 0x986ddb5c6b3a76b7,0xf89629465a75e01c,
+ 0xbe89523386091465,0xf6bbb397f1135823,
+ 0xee2ba6c0678b597f,0x746aa07ded582e2c,
+ 0x94db483840b717ef,0xa8c2a44eb4571cdc,
+ 0xba121a4650e4ddeb,0x92f34d62616ce413,
+ 0xe896a0d7e51e1566,0x77b020baf9c81d17,
+ 0x915e2486ef32cd60,0xace1474dc1d122e,
+ 0xb5b5ada8aaff80b8,0xd819992132456ba,
+ 0xe3231912d5bf60e6,0x10e1fff697ed6c69,
+ 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
+ 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
+ 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
+ 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
+ 0xad4ab7112eb3929d,0x86c16c98d2c953c6,
+ 0xd89d64d57a607744,0xe871c7bf077ba8b7,
+ 0x87625f056c7c4a8b,0x11471cd764ad4972,
+ 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
+ 0xd389b47879823479,0x4aff1d108d4ec2c3,
+ 0x843610cb4bf160cb,0xcedf722a585139ba,
+ 0xa54394fe1eedb8fe,0xc2974eb4ee658828,
+ 0xce947a3da6a9273e,0x733d226229feea32,
+ 0x811ccc668829b887,0x806357d5a3f525f,
+ 0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
+ 0xc9bcff6034c13052,0xfc89b393dd02f0b5,
+ 0xfc2c3f3841f17c67,0xbbac2078d443ace2,
+ 0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
+ 0xc5029163f384a931,0xa9e795e65d4df11,
+ 0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
+ 0x99ea0196163fa42e,0x504bced1bf8e4e45,
+ 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
+ 0xf07da27a82c37088,0x5d767327bb4e5a4c,
+ 0x964e858c91ba2655,0x3a6a07f8d510f86f,
+ 0xbbe226efb628afea,0x890489f70a55368b,
+ 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
+ 0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
+ 0xb77ada0617e3bbcb,0x9ce6ebb40173744,
+ 0xe55990879ddcaabd,0xcc420a6a101d0515,
+ 0x8f57fa54c2a9eab6,0x9fa946824a12232d,
+ 0xb32df8e9f3546564,0x47939822dc96abf9,
+ 0xdff9772470297ebd,0x59787e2b93bc56f7,
+ 0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
+ 0xaefae51477a06b03,0xede622920b6b23f1,
+ 0xdab99e59958885c4,0xe95fab368e45eced,
+ 0x88b402f7fd75539b,0x11dbcb0218ebb414,
+ 0xaae103b5fcd2a881,0xd652bdc29f26a119,
+ 0xd59944a37c0752a2,0x4be76d3346f0495f,
+ 0x857fcae62d8493a5,0x6f70a4400c562ddb,
+ 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
+ 0xd097ad07a71f26b2,0x7e2000a41346a7a7,
+ 0x825ecc24c873782f,0x8ed400668c0c28c8,
+ 0xa2f67f2dfa90563b,0x728900802f0f32fa,
+ 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
+ 0xfea126b7d78186bc,0xe2f610c84987bfa8,
+ 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
+ 0xc6ede63fa05d3143,0x91503d1c79720dbb,
+ 0xf8a95fcf88747d94,0x75a44c6397ce912a,
+ 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
+ 0xc24452da229b021b,0xfbe85badce996168,
+ 0xf2d56790ab41c2a2,0xfae27299423fb9c3,
+ 0x97c560ba6b0919a5,0xdccd879fc967d41a,
+ 0xbdb6b8e905cb600f,0x5400e987bbc1c920,
+ 0xed246723473e3813,0x290123e9aab23b68,
+ 0x9436c0760c86e30b,0xf9a0b6720aaf6521,
+ 0xb94470938fa89bce,0xf808e40e8d5b3e69,
+ 0xe7958cb87392c2c2,0xb60b1d1230b20e04,
+ 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
+ 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
+ 0xe2280b6c20dd5232,0x25c6da63c38de1b0,
+ 0x8d590723948a535f,0x579c487e5a38ad0e,
+ 0xb0af48ec79ace837,0x2d835a9df0c6d851,
+ 0xdcdb1b2798182244,0xf8e431456cf88e65,
+ 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
+ 0xac8b2d36eed2dac5,0xe272467e3d222f3f,
+ 0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
+ 0x86ccbb52ea94baea,0x98e947129fc2b4e9,
+ 0xa87fea27a539e9a5,0x3f2398d747b36224,
+ 0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
+ 0x83a3eeeef9153e89,0x1953cf68300424ac,
+ 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
+ 0xcdb02555653131b6,0x3792f412cb06794d,
+ 0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
+ 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
+ 0xc8de047564d20a8b,0xf245825a5a445275,
+ 0xfb158592be068d2e,0xeed6e2f0f0d56712,
+ 0x9ced737bb6c4183d,0x55464dd69685606b,
+ 0xc428d05aa4751e4c,0xaa97e14c3c26b886,
+ 0xf53304714d9265df,0xd53dd99f4b3066a8,
+ 0x993fe2c6d07b7fab,0xe546a8038efe4029,
+ 0xbf8fdb78849a5f96,0xde98520472bdd033,
+ 0xef73d256a5c0f77c,0x963e66858f6d4440,
+ 0x95a8637627989aad,0xdde7001379a44aa8,
+ 0xbb127c53b17ec159,0x5560c018580d5d52,
+ 0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
+ 0x9226712162ab070d,0xcab3961304ca70e8,
+ 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
+ 0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
+ 0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
+ 0xb267ed1940f1c61c,0x55f038b237591ed3,
+ 0xdf01e85f912e37a3,0x6b6c46dec52f6688,
+ 0x8b61313bbabce2c6,0x2323ac4b3b3da015,
+ 0xae397d8aa96c1b77,0xabec975e0a0d081a,
+ 0xd9c7dced53c72255,0x96e7bd358c904a21,
+ 0x881cea14545c7575,0x7e50d64177da2e54,
+ 0xaa242499697392d2,0xdde50bd1d5d0b9e9,
+ 0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
+ 0x84ec3c97da624ab4,0xbd5af13bef0b113e,
+ 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
+ 0xcfb11ead453994ba,0x67de18eda5814af2,
+ 0x81ceb32c4b43fcf4,0x80eacf948770ced7,
+ 0xa2425ff75e14fc31,0xa1258379a94d028d,
+ 0xcad2f7f5359a3b3e,0x96ee45813a04330,
+ 0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
+ 0x9e74d1b791e07e48,0x775ea264cf55347e,
+ 0xc612062576589dda,0x95364afe032a819e,
+ 0xf79687aed3eec551,0x3a83ddbd83f52205,
+ 0x9abe14cd44753b52,0xc4926a9672793543,
+ 0xc16d9a0095928a27,0x75b7053c0f178294,
+ 0xf1c90080baf72cb1,0x5324c68b12dd6339,
+ 0x971da05074da7bee,0xd3f6fc16ebca5e04,
+ 0xbce5086492111aea,0x88f4bb1ca6bcf585,
+ 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6,
+ 0x9392ee8e921d5d07,0x3aff322e62439fd0,
+ 0xb877aa3236a4b449,0x9befeb9fad487c3,
+ 0xe69594bec44de15b,0x4c2ebe687989a9b4,
+ 0x901d7cf73ab0acd9,0xf9d37014bf60a11,
+ 0xb424dc35095cd80f,0x538484c19ef38c95,
+ 0xe12e13424bb40e13,0x2865a5f206b06fba,
+ 0x8cbccc096f5088cb,0xf93f87b7442e45d4,
+ 0xafebff0bcb24aafe,0xf78f69a51539d749,
+ 0xdbe6fecebdedd5be,0xb573440e5a884d1c,
+ 0x89705f4136b4a597,0x31680a88f8953031,
+ 0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
+ 0xd6bf94d5e57a42bc,0x3d32907604691b4d,
+ 0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
+ 0xa7c5ac471b478423,0xfcf80dc33721d54,
+ 0xd1b71758e219652b,0xd3c36113404ea4a9,
+ 0x83126e978d4fdf3b,0x645a1cac083126ea,
+ 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
+ 0xcccccccccccccccc,0xcccccccccccccccd,
+ 0x8000000000000000,0x0,
+ 0xa000000000000000,0x0,
+ 0xc800000000000000,0x0,
+ 0xfa00000000000000,0x0,
+ 0x9c40000000000000,0x0,
+ 0xc350000000000000,0x0,
+ 0xf424000000000000,0x0,
+ 0x9896800000000000,0x0,
+ 0xbebc200000000000,0x0,
+ 0xee6b280000000000,0x0,
+ 0x9502f90000000000,0x0,
+ 0xba43b74000000000,0x0,
+ 0xe8d4a51000000000,0x0,
+ 0x9184e72a00000000,0x0,
+ 0xb5e620f480000000,0x0,
+ 0xe35fa931a0000000,0x0,
+ 0x8e1bc9bf04000000,0x0,
+ 0xb1a2bc2ec5000000,0x0,
+ 0xde0b6b3a76400000,0x0,
+ 0x8ac7230489e80000,0x0,
+ 0xad78ebc5ac620000,0x0,
+ 0xd8d726b7177a8000,0x0,
+ 0x878678326eac9000,0x0,
+ 0xa968163f0a57b400,0x0,
+ 0xd3c21bcecceda100,0x0,
+ 0x84595161401484a0,0x0,
+ 0xa56fa5b99019a5c8,0x0,
+ 0xcecb8f27f4200f3a,0x0,
+ 0x813f3978f8940984,0x4000000000000000,
+ 0xa18f07d736b90be5,0x5000000000000000,
+ 0xc9f2c9cd04674ede,0xa400000000000000,
+ 0xfc6f7c4045812296,0x4d00000000000000,
+ 0x9dc5ada82b70b59d,0xf020000000000000,
+ 0xc5371912364ce305,0x6c28000000000000,
+ 0xf684df56c3e01bc6,0xc732000000000000,
+ 0x9a130b963a6c115c,0x3c7f400000000000,
+ 0xc097ce7bc90715b3,0x4b9f100000000000,
+ 0xf0bdc21abb48db20,0x1e86d40000000000,
+ 0x96769950b50d88f4,0x1314448000000000,
+ 0xbc143fa4e250eb31,0x17d955a000000000,
+ 0xeb194f8e1ae525fd,0x5dcfab0800000000,
+ 0x92efd1b8d0cf37be,0x5aa1cae500000000,
+ 0xb7abc627050305ad,0xf14a3d9e40000000,
+ 0xe596b7b0c643c719,0x6d9ccd05d0000000,
+ 0x8f7e32ce7bea5c6f,0xe4820023a2000000,
+ 0xb35dbf821ae4f38b,0xdda2802c8a800000,
+ 0xe0352f62a19e306e,0xd50b2037ad200000,
+ 0x8c213d9da502de45,0x4526f422cc340000,
+ 0xaf298d050e4395d6,0x9670b12b7f410000,
+ 0xdaf3f04651d47b4c,0x3c0cdd765f114000,
+ 0x88d8762bf324cd0f,0xa5880a69fb6ac800,
+ 0xab0e93b6efee0053,0x8eea0d047a457a00,
+ 0xd5d238a4abe98068,0x72a4904598d6d880,
+ 0x85a36366eb71f041,0x47a6da2b7f864750,
+ 0xa70c3c40a64e6c51,0x999090b65f67d924,
+ 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
+ 0x82818f1281ed449f,0xbff8f10e7a8921a4,
+ 0xa321f2d7226895c7,0xaff72d52192b6a0d,
+ 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
+ 0xfee50b7025c36a08,0x2f236d04753d5b4,
+ 0x9f4f2726179a2245,0x1d762422c946590,
+ 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
+ 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
+ 0x9b934c3b330c8577,0x63cc55f49f88eb2f,
+ 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
+ 0xf316271c7fc3908a,0x8bef464e3945ef7a,
+ 0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
+ 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
+ 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
+ 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
+ 0xb975d6b6ee39e436,0xb3e2fd538e122b44,
+ 0xe7d34c64a9c85d44,0x60dbbca87196b616,
+ 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
+ 0xb51d13aea4a488dd,0x6babab6398bdbe41,
+ 0xe264589a4dcdab14,0xc696963c7eed2dd1,
+ 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
+ 0xb0de65388cc8ada8,0x3b25a55f43294bcb,
+ 0xdd15fe86affad912,0x49ef0eb713f39ebe,
+ 0x8a2dbf142dfcc7ab,0x6e3569326c784337,
+ 0xacb92ed9397bf996,0x49c2c37f07965404,
+ 0xd7e77a8f87daf7fb,0xdc33745ec97be906,
+ 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
+ 0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
+ 0xd2d80db02aabd62b,0xf50a3fa490c30190,
+ 0x83c7088e1aab65db,0x792667c6da79e0fa,
+ 0xa4b8cab1a1563f52,0x577001b891185938,
+ 0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
+ 0x80b05e5ac60b6178,0x544f8158315b05b4,
+ 0xa0dc75f1778e39d6,0x696361ae3db1c721,
+ 0xc913936dd571c84c,0x3bc3a19cd1e38e9,
+ 0xfb5878494ace3a5f,0x4ab48a04065c723,
+ 0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
+ 0xc45d1df942711d9a,0x3ba5d0bd324f8394,
+ 0xf5746577930d6500,0xca8f44ec7ee36479,
+ 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
+ 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
+ 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
+ 0x95d04aee3b80ece5,0xbba1f1d158724a12,
+ 0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
+ 0xea1575143cf97226,0xf52d09d71a3293bd,
+ 0x924d692ca61be758,0x593c2626705f9c56,
+ 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
+ 0xe498f455c38b997a,0xb6dfb9c0f956447,
+ 0x8edf98b59a373fec,0x4724bd4189bd5eac,
+ 0xb2977ee300c50fe7,0x58edec91ec2cb657,
+ 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
+ 0x8b865b215899f46c,0xbd79e0d20082ee74,
+ 0xae67f1e9aec07187,0xecd8590680a3aa11,
+ 0xda01ee641a708de9,0xe80e6f4820cc9495,
+ 0x884134fe908658b2,0x3109058d147fdcdd,
+ 0xaa51823e34a7eede,0xbd4b46f0599fd415,
+ 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
+ 0x850fadc09923329e,0x3e2cf6bc604ddb0,
+ 0xa6539930bf6bff45,0x84db8346b786151c,
+ 0xcfe87f7cef46ff16,0xe612641865679a63,
+ 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
+ 0xa26da3999aef7749,0xe3be5e330f38f09d,
+ 0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
+ 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
+ 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
+ 0xc646d63501a1511d,0xb281e1fd541501b8,
+ 0xf7d88bc24209a565,0x1f225a7ca91a4226,
+ 0x9ae757596946075f,0x3375788de9b06958,
+ 0xc1a12d2fc3978937,0x52d6b1641c83ae,
+ 0xf209787bb47d6b84,0xc0678c5dbd23a49a,
+ 0x9745eb4d50ce6332,0xf840b7ba963646e0,
+ 0xbd176620a501fbff,0xb650e5a93bc3d898,
+ 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
+ 0x93ba47c980e98cdf,0xc66f336c36b10137,
+ 0xb8a8d9bbe123f017,0xb80b0047445d4184,
+ 0xe6d3102ad96cec1d,0xa60dc059157491e5,
+ 0x9043ea1ac7e41392,0x87c89837ad68db2f,
+ 0xb454e4a179dd1877,0x29babe4598c311fb,
+ 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
+ 0x8ce2529e2734bb1d,0x1899e4a65f58660c,
+ 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
+ 0xdc21a1171d42645d,0x76707543f4fa1f73,
+ 0x899504ae72497eba,0x6a06494a791c53a8,
+ 0xabfa45da0edbde69,0x487db9d17636892,
+ 0xd6f8d7509292d603,0x45a9d2845d3c42b6,
+ 0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
+ 0xa7f26836f282b732,0x8e6cac7768d7141e,
+ 0xd1ef0244af2364ff,0x3207d795430cd926,
+ 0x8335616aed761f1f,0x7f44e6bd49e807b8,
+ 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
+ 0xcd036837130890a1,0x36dba887c37a8c0f,
+ 0x802221226be55a64,0xc2494954da2c9789,
+ 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
+ 0xc83553c5c8965d3d,0x6f92829494e5acc7,
+ 0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
+ 0x9c69a97284b578d7,0xff2a760414536efb,
+ 0xc38413cf25e2d70d,0xfef5138519684aba,
+ 0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
+ 0x98bf2f79d5993802,0xef2f773ffbd97a61,
+ 0xbeeefb584aff8603,0xaafb550ffacfd8fa,
+ 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
+ 0x952ab45cfa97a0b2,0xdd945a747bf26183,
+ 0xba756174393d88df,0x94f971119aeef9e4,
+ 0xe912b9d1478ceb17,0x7a37cd5601aab85d,
+ 0x91abb422ccb812ee,0xac62e055c10ab33a,
+ 0xb616a12b7fe617aa,0x577b986b314d6009,
+ 0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
+ 0x8e41ade9fbebc27d,0x14588f13be847307,
+ 0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
+ 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
+ 0x8aec23d680043bee,0x25de7bb9480d5854,
+ 0xada72ccc20054ae9,0xaf561aa79a10ae6a,
+ 0xd910f7ff28069da4,0x1b2ba1518094da04,
+ 0x87aa9aff79042286,0x90fb44d2f05d0842,
+ 0xa99541bf57452b28,0x353a1607ac744a53,
+ 0xd3fa922f2d1675f2,0x42889b8997915ce8,
+ 0x847c9b5d7c2e09b7,0x69956135febada11,
+ 0xa59bc234db398c25,0x43fab9837e699095,
+ 0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
+ 0x8161afb94b44f57d,0x1d1be0eebac278f5,
+ 0xa1ba1ba79e1632dc,0x6462d92a69731732,
+ 0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
+ 0xfcb2cb35e702af78,0x5cda735244c3d43e,
+ 0x9defbf01b061adab,0x3a0888136afa64a7,
+ 0xc56baec21c7a1916,0x88aaa1845b8fdd0,
+ 0xf6c69a72a3989f5b,0x8aad549e57273d45,
+ 0x9a3c2087a63f6399,0x36ac54e2f678864b,
+ 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
+ 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
+ 0x969eb7c47859e743,0x9f644ae5a4b1b325,
+ 0xbc4665b596706114,0x873d5d9f0dde1fee,
+ 0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
+ 0x9316ff75dd87cbd8,0x9a7f12442d588f2,
+ 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
+ 0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
+ 0x8fa475791a569d10,0xf96e017d694487bc,
+ 0xb38d92d760ec4455,0x37c981dcc395a9ac,
+ 0xe070f78d3927556a,0x85bbe253f47b1417,
+ 0x8c469ab843b89562,0x93956d7478ccec8e,
+ 0xaf58416654a6babb,0x387ac8d1970027b2,
+ 0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
+ 0x88fcf317f22241e2,0x441fece3bdf81f03,
+ 0xab3c2fddeeaad25a,0xd527e81cad7626c3,
+ 0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
+ 0x85c7056562757456,0xf6872d5667844e49,
+ 0xa738c6bebb12d16c,0xb428f8ac016561db,
+ 0xd106f86e69d785c7,0xe13336d701beba52,
+ 0x82a45b450226b39c,0xecc0024661173473,
+ 0xa34d721642b06084,0x27f002d7f95d0190,
+ 0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
+ 0xff290242c83396ce,0x7e67047175a15271,
+ 0x9f79a169bd203e41,0xf0062c6e984d386,
+ 0xc75809c42c684dd1,0x52c07b78a3e60868,
+ 0xf92e0c3537826145,0xa7709a56ccdf8a82,
+ 0x9bbcc7a142b17ccb,0x88a66076400bb691,
+ 0xc2abf989935ddbfe,0x6acff893d00ea435,
+ 0xf356f7ebf83552fe,0x583f6b8c4124d43,
+ 0x98165af37b2153de,0xc3727a337a8b704a,
+ 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
+ 0xeda2ee1c7064130c,0x1162def06f79df73,
+ 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
+ 0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
+ 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
+ 0x910ab1d4db9914a0,0x1d9c9892400a22a2,
+ 0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
+ 0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
+ 0x8da471a9de737e24,0x5ceaecfed289e5d2,
+ 0xb10d8e1456105dad,0x7425a83e872c5f47,
+ 0xdd50f1996b947518,0xd12f124e28f77719,
+ 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
+ 0xace73cbfdc0bfb7b,0x636cc64d1001550b,
+ 0xd8210befd30efa5a,0x3c47f7e05401aa4e,
+ 0x8714a775e3e95c78,0x65acfaec34810a71,
+ 0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
+ 0xd31045a8341ca07c,0x1ede48111209a050,
+ 0x83ea2b892091e44d,0x934aed0aab460432,
+ 0xa4e4b66b68b65d60,0xf81da84d5617853f,
+ 0xce1de40642e3f4b9,0x36251260ab9d668e,
+ 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
+ 0xa1075a24e4421730,0xb24cf65b8612f81f,
+ 0xc94930ae1d529cfc,0xdee033f26797b627,
+ 0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
+ 0x9d412e0806e88aa5,0x8e1f289560ee864e,
+ 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
+ 0xf5b5d7ec8acb58a2,0xae10af696774b1db,
+ 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
+ 0xbff610b0cc6edd3f,0x17fd090a58d32af3,
+ 0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
+ 0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
+ 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
+ 0xea53df5fd18d5513,0x84c86189216dc5ed,
+ 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
+ 0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
+ 0xe4d5e82392a40515,0xfabaf3feaa5334a,
+ 0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
+ 0xb2c71d5bca9023f8,0x743e20e9ef511012,
+ 0xdf78e4b2bd342cf6,0x914da9246b255416,
+ 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
+ 0xae9672aba3d0c320,0xa184ac2473b529b1,
+ 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
+ 0x8865899617fb1871,0x7e2fa67c7a658892,
+ 0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
+ 0xd51ea6fa85785631,0x552a74227f3ea565,
+ 0x8533285c936b35de,0xd53a88958f87275f,
+ 0xa67ff273b8460356,0x8a892abaf368f137,
+ 0xd01fef10a657842c,0x2d2b7569b0432d85,
+ 0x8213f56a67f6b29b,0x9c3b29620e29fc73,
+ 0xa298f2c501f45f42,0x8349f3ba91b47b8f,
+ 0xcb3f2f7642717713,0x241c70a936219a73,
+ 0xfe0efb53d30dd4d7,0xed238cd383aa0110,
+ 0x9ec95d1463e8a506,0xf4363804324a40aa,
+ 0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
+ 0xf81aa16fdc1b81da,0xdd94b7868e94050a,
+ 0x9b10a4e5e9913128,0xca7cf2b4191c8326,
+ 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
+ 0xf24a01a73cf2dccf,0xbc633b39673c8cec,
+ 0x976e41088617ca01,0xd5be0503e085d813,
+ 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
+ 0xec9c459d51852ba2,0xddf8e7d60ed1219e,
+ 0x93e1ab8252f33b45,0xcabb90e5c942b503,
+ 0xb8da1662e7b00a17,0x3d6a751f3b936243,
+ 0xe7109bfba19c0c9d,0xcc512670a783ad4,
+ 0x906a617d450187e2,0x27fb2b80668b24c5,
+ 0xb484f9dc9641e9da,0xb1f9f660802dedf6,
+ 0xe1a63853bbd26451,0x5e7873f8a0396973,
+ 0x8d07e33455637eb2,0xdb0b487b6423e1e8,
+ 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
+ 0xdc5c5301c56b75f7,0x7641a140cc7810fb,
+ 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
+ 0xac2820d9623bf429,0x546345fa9fbdcd44,
+ 0xd732290fbacaf133,0xa97c177947ad4095,
+ 0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
+ 0xa81f301449ee8c70,0x5c68f256bfff5a74,
+ 0xd226fc195c6a2f8c,0x73832eec6fff3111,
+ 0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
+ 0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
+ 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
+ 0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
+ 0xa0555e361951c366,0xd7e105bcc332621f,
+ 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
+ 0xfa856334878fc150,0xb14f98f6f0feb951,
+ 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
+ 0xc3b8358109e84f07,0xa862f80ec4700c8,
+ 0xf4a642e14c6262c8,0xcd27bb612758c0fa,
+ 0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
+ 0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
+ 0xeeea5d5004981478,0x1858ccfce06cac74,
+ 0x95527a5202df0ccb,0xf37801e0c43ebc8,
+ 0xbaa718e68396cffd,0xd30560258f54e6ba,
+ 0xe950df20247c83fd,0x47c6b82ef32a2069,
+ 0x91d28b7416cdd27e,0x4cdc331d57fa5441,
+ 0xb6472e511c81471d,0xe0133fe4adf8e952,
+ 0xe3d8f9e563a198e5,0x58180fddd97723a6,
+ 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
+ using powers = powers_template<>;
+
+}
+
+#endif
+
+#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H
+#define FASTFLOAT_DECIMAL_TO_BINARY_H
+
+#include <cfloat>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+
+namespace fast_float {
+
+ // This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
+ // the result, with the "high" part corresponding to the most significant bits and the
+ // low part corresponding to the least significant bits.
+ //
+ template <int bit_precision>
+ fastfloat_really_inline
+ value128 compute_product_approximation(int64_t q, uint64_t w) {
+ const int index = 2 * int(q - powers::smallest_power_of_five);
+ // For small values of q, e.g., q in [0,27], the answer is always exact because
+ // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
+ // gives the exact answer.
+ value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
+ static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
+ constexpr uint64_t precision_mask = (bit_precision < 64) ?
+ (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
+ : uint64_t(0xFFFFFFFFFFFFFFFF);
+ if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
+ // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
+ value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
+ firstproduct.low += secondproduct.high;
+ if(secondproduct.high > firstproduct.low) {
+ firstproduct.high++;
+ }
+ }
+ return firstproduct;
+ }
+
+ namespace detail {
+ /**
+ * For q in (0,350), we have that
+ * f = (((152170 + 65536) * q ) >> 16);
+ * is equal to
+ * floor(p) + q
+ * where
+ * p = log(5**q)/log(2) = q * log(5)/log(2)
+ *
+ * For negative values of q in (-400,0), we have that
+ * f = (((152170 + 65536) * q ) >> 16);
+ * is equal to
+ * -ceil(p) + q
+ * where
+ * p = log(5**-q)/log(2) = -q * log(5)/log(2)
+ */
+ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
+ return (((152170 + 65536) * q) >> 16) + 63;
+ }
+ } // namespace detail
+
+ // create an adjusted mantissa, biased by the invalid power2
+ // for significant digits already multiplied by 10 ** q.
+ template <typename binary>
+ fastfloat_really_inline
+ adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
+ int hilz = int(w >> 63) ^ 1;
+ adjusted_mantissa answer;
+ answer.mantissa = w << hilz;
+ int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
+ answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
+ return answer;
+ }
+
+ // w * 10 ** q, without rounding the representation up.
+ // the power2 in the exponent will be adjusted by invalid_am_bias.
+ template <typename binary>
+ fastfloat_really_inline
+ adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
+ int lz = leading_zeroes(w);
+ w <<= lz;
+ value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
+ return compute_error_scaled<binary>(q, product.high, lz);
+ }
+
+ // w * 10 ** q
+ // The returned value should be a valid ieee64 number that simply need to be packed.
+ // However, in some very rare cases, the computation will fail. In such cases, we
+ // return an adjusted_mantissa with a negative power of 2: the caller should recompute
+ // in such cases.
+ template <typename binary>
+ fastfloat_really_inline
+ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
+ adjusted_mantissa answer;
+ if ((w == 0) || (q < binary::smallest_power_of_ten())) {
+ answer.power2 = 0;
+ answer.mantissa = 0;
+ // result should be zero
+ return answer;
+ }
+ if (q > binary::largest_power_of_ten()) {
+ // we want to get infinity:
+ answer.power2 = binary::infinite_power();
+ answer.mantissa = 0;
+ return answer;
+ }
+ // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
+
+ // We want the most significant bit of i to be 1. Shift if needed.
+ int lz = leading_zeroes(w);
+ w <<= lz;
+
+ // The required precision is binary::mantissa_explicit_bits() + 3 because
+ // 1. We need the implicit bit
+ // 2. We need an extra bit for rounding purposes
+ // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
+
+ value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
+ if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further
+ // In some very rare cases, this could happen, in which case we might need a more accurate
+ // computation that what we can provide cheaply. This is very, very unlikely.
+ //
+ const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0,
+ // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation.
+ if(!inside_safe_exponent) {
+ return compute_error_scaled<binary>(q, product.high, lz);
+ }
+ }
+ // The "compute_product_approximation" function can be slightly slower than a branchless approach:
+ // value128 product = compute_product(q, w);
+ // but in practice, we can win big with the compute_product_approximation if its additional branch
+ // is easily predicted. Which is best is data specific.
+ int upperbit = int(product.high >> 63);
+
+ answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
+
+ answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
+ if (answer.power2 <= 0) { // we have a subnormal?
+ // Here have that answer.power2 <= 0 so -answer.power2 >= 0
+ if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
+ answer.power2 = 0;
+ answer.mantissa = 0;
+ // result should be zero
+ return answer;
+ }
+ // next line is safe because -answer.power2 + 1 < 64
+ answer.mantissa >>= -answer.power2 + 1;
+ // Thankfully, we can't have both "round-to-even" and subnormals because
+ // "round-to-even" only occurs for powers close to 0.
+ answer.mantissa += (answer.mantissa & 1); // round up
+ answer.mantissa >>= 1;
+ // There is a weird scenario where we don't have a subnormal but just.
+ // Suppose we start with 2.2250738585072013e-308, we end up
+ // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
+ // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
+ // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
+ // subnormal, but we can only know this after rounding.
+ // So we only declare a subnormal if we are smaller than the threshold.
+ answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
+ return answer;
+ }
+
+ // usually, we round *up*, but if we fall right in between and and we have an
+ // even basis, we need to round down
+ // We are only concerned with the cases where 5**q fits in single 64-bit word.
+ if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
+ ((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
+ // To be in-between two floats we need that in doing
+ // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
+ // ... we dropped out only zeroes. But if this happened, then we can go back!!!
+ if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
+ answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
+ }
+ }
+
+ answer.mantissa += (answer.mantissa & 1); // round up
+ answer.mantissa >>= 1;
+ if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
+ answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
+ answer.power2++; // undo previous addition
+ }
+
+ answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
+ if (answer.power2 >= binary::infinite_power()) { // infinity
+ answer.power2 = binary::infinite_power();
+ answer.mantissa = 0;
+ }
+ return answer;
+ }
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_BIGINT_H
+#define FASTFLOAT_BIGINT_H
+
+#include <algorithm>
+#include <cstdint>
+#include <climits>
+#include <cstring>
+
+
+namespace fast_float {
+
+// the limb width: we want efficient multiplication of double the bits in
+// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
+// extract the high and low parts efficiently. this is every 64-bit
+// architecture except for sparc, which emulates 128-bit multiplication.
+// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
+// doing `8 * sizeof(limb)`.
+#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
+#define FASTFLOAT_64BIT_LIMB
+ typedef uint64_t limb;
+ constexpr size_t limb_bits = 64;
+#else
+ #define FASTFLOAT_32BIT_LIMB
+ typedef uint32_t limb;
+ constexpr size_t limb_bits = 32;
+#endif
+
+ typedef span<limb> limb_span;
+
+ // number of bits in a bigint. this needs to be at least the number
+ // of bits required to store the largest bigint, which is
+ // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
+ // ~3600 bits, so we round to 4000.
+ constexpr size_t bigint_bits = 4000;
+ constexpr size_t bigint_limbs = bigint_bits / limb_bits;
+
+ // vector-like type that is allocated on the stack. the entire
+ // buffer is pre-allocated, and only the length changes.
+ template <uint16_t size>
+ struct stackvec {
+ limb data[size];
+ // we never need more than 150 limbs
+ uint16_t length{0};
+
+ stackvec() = default;
+ stackvec(const stackvec &) = delete;
+ stackvec &operator=(const stackvec &) = delete;
+ stackvec(stackvec &&) = delete;
+ stackvec &operator=(stackvec &&other) = delete;
+
+ // create stack vector from existing limb span.
+ stackvec(limb_span s) {
+ FASTFLOAT_ASSERT(try_extend(s));
+ }
+
+ limb& operator[](size_t index) noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return data[index];
+ }
+ const limb& operator[](size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return data[index];
+ }
+ // index from the end of the container
+ const limb& rindex(size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ size_t rindex = length - index - 1;
+ return data[rindex];
+ }
+
+ // set the length, without bounds checking.
+ void set_len(size_t len) noexcept {
+ length = uint16_t(len);
+ }
+ constexpr size_t len() const noexcept {
+ return length;
+ }
+ constexpr bool is_empty() const noexcept {
+ return length == 0;
+ }
+ constexpr size_t capacity() const noexcept {
+ return size;
+ }
+ // append item to vector, without bounds checking
+ void push_unchecked(limb value) noexcept {
+ data[length] = value;
+ length++;
+ }
+ // append item to vector, returning if item was added
+ bool try_push(limb value) noexcept {
+ if (len() < capacity()) {
+ push_unchecked(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // add items to the vector, from a span, without bounds checking
+ void extend_unchecked(limb_span s) noexcept {
+ limb* ptr = data + length;
+ ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len());
+ set_len(len() + s.len());
+ }
+ // try to add items to the vector, returning if items were added
+ bool try_extend(limb_span s) noexcept {
+ if (len() + s.len() <= capacity()) {
+ extend_unchecked(s);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // resize the vector, without bounds checking
+ // if the new size is longer than the vector, assign value to each
+ // appended item.
+ void resize_unchecked(size_t new_len, limb value) noexcept {
+ if (new_len > len()) {
+ size_t count = new_len - len();
+ limb* first = data + len();
+ limb* last = first + count;
+ ::std::fill(first, last, value);
+ set_len(new_len);
+ } else {
+ set_len(new_len);
+ }
+ }
+ // try to resize the vector, returning if the vector was resized.
+ bool try_resize(size_t new_len, limb value) noexcept {
+ if (new_len > capacity()) {
+ return false;
+ } else {
+ resize_unchecked(new_len, value);
+ return true;
+ }
+ }
+ // check if any limbs are non-zero after the given index.
+ // this needs to be done in reverse order, since the index
+ // is relative to the most significant limbs.
+ bool nonzero(size_t index) const noexcept {
+ while (index < len()) {
+ if (rindex(index) != 0) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+ // normalize the big integer, so most-significant zero limbs are removed.
+ void normalize() noexcept {
+ while (len() > 0 && rindex(0) == 0) {
+ length--;
+ }
+ }
+ };
+
+ fastfloat_really_inline
+ uint64_t empty_hi64(bool& truncated) noexcept {
+ truncated = false;
+ return 0;
+ }
+
+ fastfloat_really_inline
+ uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
+ truncated = false;
+ int shl = leading_zeroes(r0);
+ return r0 << shl;
+ }
+
+ fastfloat_really_inline
+ uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
+ int shl = leading_zeroes(r0);
+ if (shl == 0) {
+ truncated = r1 != 0;
+ return r0;
+ } else {
+ int shr = 64 - shl;
+ truncated = (r1 << shl) != 0;
+ return (r0 << shl) | (r1 >> shr);
+ }
+ }
+
+ fastfloat_really_inline
+ uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
+ return uint64_hi64(r0, truncated);
+ }
+
+ fastfloat_really_inline
+ uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
+ uint64_t x0 = r0;
+ uint64_t x1 = r1;
+ return uint64_hi64((x0 << 32) | x1, truncated);
+ }
+
+ fastfloat_really_inline
+ uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
+ uint64_t x0 = r0;
+ uint64_t x1 = r1;
+ uint64_t x2 = r2;
+ return uint64_hi64(x0, (x1 << 32) | x2, truncated);
+ }
+
+ // add two small integers, checking for overflow.
+ // we want an efficient operation. for msvc, where
+ // we don't have built-in intrinsics, this is still
+ // pretty fast.
+ fastfloat_really_inline
+ limb scalar_add(limb x, limb y, bool& overflow) noexcept {
+ limb z;
+
+// gcc and clang
+#if defined(__has_builtin)
+ #if __has_builtin(__builtin_add_overflow)
+ overflow = __builtin_add_overflow(x, y, &z);
+ return z;
+#endif
+#endif
+
+ // generic, this still optimizes correctly on MSVC.
+ z = x + y;
+ overflow = z < x;
+ return z;
+ }
+
+ // multiply two small integers, getting both the high and low bits.
+ fastfloat_really_inline
+ limb scalar_mul(limb x, limb y, limb& carry) noexcept {
+#ifdef FASTFLOAT_64BIT_LIMB
+#if defined(__SIZEOF_INT128__)
+ // GCC and clang both define it as an extension.
+ __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
+ carry = limb(z >> limb_bits);
+ return limb(z);
+#else
+ // fallback, no native 128-bit integer multiplication with carry.
+ // on msvc, this optimizes identically, somehow.
+ value128 z = full_multiplication(x, y);
+ bool overflow;
+ z.low = scalar_add(z.low, carry, overflow);
+ z.high += uint64_t(overflow); // cannot overflow
+ carry = z.high;
+ return z.low;
+#endif
+#else
+ uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
+ carry = limb(z >> limb_bits);
+ return limb(z);
+#endif
+ }
+
+ // add scalar value to bigint starting from offset.
+ // used in grade school multiplication
+ template <uint16_t size>
+ inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
+ size_t index = start;
+ limb carry = y;
+ bool overflow;
+ while (carry != 0 && index < vec.len()) {
+ vec[index] = scalar_add(vec[index], carry, overflow);
+ carry = limb(overflow);
+ index += 1;
+ }
+ if (carry != 0) {
+ FASTFLOAT_TRY(vec.try_push(carry));
+ }
+ return true;
+ }
+
+ // add scalar value to bigint.
+ template <uint16_t size>
+ fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept {
+ return small_add_from(vec, y, 0);
+ }
+
+ // multiply bigint by scalar value.
+ template <uint16_t size>
+ inline bool small_mul(stackvec<size>& vec, limb y) noexcept {
+ limb carry = 0;
+ for (size_t index = 0; index < vec.len(); index++) {
+ vec[index] = scalar_mul(vec[index], y, carry);
+ }
+ if (carry != 0) {
+ FASTFLOAT_TRY(vec.try_push(carry));
+ }
+ return true;
+ }
+
+ // add bigint to bigint starting from index.
+ // used in grade school multiplication
+ template <uint16_t size>
+ bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
+ // the effective x buffer is from `xstart..x.len()`, so exit early
+ // if we can't get that current range.
+ if (x.len() < start || y.len() > x.len() - start) {
+ FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
+ }
+
+ bool carry = false;
+ for (size_t index = 0; index < y.len(); index++) {
+ limb xi = x[index + start];
+ limb yi = y[index];
+ bool c1 = false;
+ bool c2 = false;
+ xi = scalar_add(xi, yi, c1);
+ if (carry) {
+ xi = scalar_add(xi, 1, c2);
+ }
+ x[index + start] = xi;
+ carry = c1 | c2;
+ }
+
+ // handle overflow
+ if (carry) {
+ FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
+ }
+ return true;
+ }
+
+ // add bigint to bigint.
+ template <uint16_t size>
+ fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
+ return large_add_from(x, y, 0);
+ }
+
+ // grade-school multiplication algorithm
+ template <uint16_t size>
+ bool long_mul(stackvec<size>& x, limb_span y) noexcept {
+ limb_span xs = limb_span(x.data, x.len());
+ stackvec<size> z(xs);
+ limb_span zs = limb_span(z.data, z.len());
+
+ if (y.len() != 0) {
+ limb y0 = y[0];
+ FASTFLOAT_TRY(small_mul(x, y0));
+ for (size_t index = 1; index < y.len(); index++) {
+ limb yi = y[index];
+ stackvec<size> zi;
+ if (yi != 0) {
+ // re-use the same buffer throughout
+ zi.set_len(0);
+ FASTFLOAT_TRY(zi.try_extend(zs));
+ FASTFLOAT_TRY(small_mul(zi, yi));
+ limb_span zis = limb_span(zi.data, zi.len());
+ FASTFLOAT_TRY(large_add_from(x, zis, index));
+ }
+ }
+ }
+
+ x.normalize();
+ return true;
+ }
+
+ // grade-school multiplication algorithm
+ template <uint16_t size>
+ bool large_mul(stackvec<size>& x, limb_span y) noexcept {
+ if (y.len() == 1) {
+ FASTFLOAT_TRY(small_mul(x, y[0]));
+ } else {
+ FASTFLOAT_TRY(long_mul(x, y));
+ }
+ return true;
+ }
+
+ // big integer type. implements a small subset of big integer
+ // arithmetic, using simple algorithms since asymptotically
+ // faster algorithms are slower for a small number of limbs.
+ // all operations assume the big-integer is normalized.
+ struct bigint {
+ // storage of the limbs, in little-endian order.
+ stackvec<bigint_limbs> vec;
+
+ bigint(): vec() {}
+ bigint(const bigint &) = delete;
+ bigint &operator=(const bigint &) = delete;
+ bigint(bigint &&) = delete;
+ bigint &operator=(bigint &&other) = delete;
+
+ bigint(uint64_t value): vec() {
+#ifdef FASTFLOAT_64BIT_LIMB
+ vec.push_unchecked(value);
+#else
+ vec.push_unchecked(uint32_t(value));
+ vec.push_unchecked(uint32_t(value >> 32));
+#endif
+ vec.normalize();
+ }
+
+ // get the high 64 bits from the vector, and if bits were truncated.
+ // this is to get the significant digits for the float.
+ uint64_t hi64(bool& truncated) const noexcept {
+#ifdef FASTFLOAT_64BIT_LIMB
+ if (vec.len() == 0) {
+ return empty_hi64(truncated);
+ } else if (vec.len() == 1) {
+ return uint64_hi64(vec.rindex(0), truncated);
+ } else {
+ uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
+ truncated |= vec.nonzero(2);
+ return result;
+ }
+#else
+ if (vec.len() == 0) {
+ return empty_hi64(truncated);
+ } else if (vec.len() == 1) {
+ return uint32_hi64(vec.rindex(0), truncated);
+ } else if (vec.len() == 2) {
+ return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
+ } else {
+ uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
+ truncated |= vec.nonzero(3);
+ return result;
+ }
+#endif
+ }
+
+ // compare two big integers, returning the large value.
+ // assumes both are normalized. if the return value is
+ // negative, other is larger, if the return value is
+ // positive, this is larger, otherwise they are equal.
+ // the limbs are stored in little-endian order, so we
+ // must compare the limbs in ever order.
+ int compare(const bigint& other) const noexcept {
+ if (vec.len() > other.vec.len()) {
+ return 1;
+ } else if (vec.len() < other.vec.len()) {
+ return -1;
+ } else {
+ for (size_t index = vec.len(); index > 0; index--) {
+ limb xi = vec[index - 1];
+ limb yi = other.vec[index - 1];
+ if (xi > yi) {
+ return 1;
+ } else if (xi < yi) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+ }
+
+ // shift left each limb n bits, carrying over to the new limb
+ // returns true if we were able to shift all the digits.
+ bool shl_bits(size_t n) noexcept {
+ // Internally, for each item, we shift left by n, and add the previous
+ // right shifted limb-bits.
+ // For example, we transform (for u8) shifted left 2, to:
+ // b10100100 b01000010
+ // b10 b10010001 b00001000
+ FASTFLOAT_DEBUG_ASSERT(n != 0);
+ FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
+
+ size_t shl = n;
+ size_t shr = limb_bits - shl;
+ limb prev = 0;
+ for (size_t index = 0; index < vec.len(); index++) {
+ limb xi = vec[index];
+ vec[index] = (xi << shl) | (prev >> shr);
+ prev = xi;
+ }
+
+ limb carry = prev >> shr;
+ if (carry != 0) {
+ return vec.try_push(carry);
+ }
+ return true;
+ }
+
+ // move the limbs left by `n` limbs.
+ bool shl_limbs(size_t n) noexcept {
+ FASTFLOAT_DEBUG_ASSERT(n != 0);
+ if (n + vec.len() > vec.capacity()) {
+ return false;
+ } else if (!vec.is_empty()) {
+ // move limbs
+ limb* dst = vec.data + n;
+ const limb* src = vec.data;
+ ::memmove(dst, src, sizeof(limb) * vec.len());
+ // fill in empty limbs
+ limb* first = vec.data;
+ limb* last = first + n;
+ ::std::fill(first, last, 0);
+ vec.set_len(n + vec.len());
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+ // move the limbs left by `n` bits.
+ bool shl(size_t n) noexcept {
+ size_t rem = n % limb_bits;
+ size_t div = n / limb_bits;
+ if (rem != 0) {
+ FASTFLOAT_TRY(shl_bits(rem));
+ }
+ if (div != 0) {
+ FASTFLOAT_TRY(shl_limbs(div));
+ }
+ return true;
+ }
+
+ // get the number of leading zeros in the bigint.
+ int ctlz() const noexcept {
+ if (vec.is_empty()) {
+ return 0;
+ } else {
+#ifdef FASTFLOAT_64BIT_LIMB
+ return leading_zeroes(vec.rindex(0));
+#else
+ // no use defining a specialized leading_zeroes for a 32-bit type.
+ uint64_t r0 = vec.rindex(0);
+ return leading_zeroes(r0 << 32);
+#endif
+ }
+ }
+
+ // get the number of bits in the bigint.
+ int bit_length() const noexcept {
+ int lz = ctlz();
+ return int(limb_bits * vec.len()) - lz;
+ }
+
+ bool mul(limb y) noexcept {
+ return small_mul(vec, y);
+ }
+
+ bool add(limb y) noexcept {
+ return small_add(vec, y);
+ }
+
+ // multiply as if by 2 raised to a power.
+ bool pow2(uint32_t exp) noexcept {
+ return shl(exp);
+ }
+
+ // multiply as if by 5 raised to a power.
+ bool pow5(uint32_t exp) noexcept {
+ // multiply by a power of 5
+ static constexpr uint32_t large_step = 135;
+ static constexpr uint64_t small_power_of_5[] = {
+ 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
+ 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
+ 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
+ 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
+ 2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
+ 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
+ };
+#ifdef FASTFLOAT_64BIT_LIMB
+ constexpr static limb large_power_of_5[] = {
+ 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
+ 10482974169319127550UL, 198276706040285095UL};
+#else
+ constexpr static limb large_power_of_5[] = {
+ 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
+ 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
+#endif
+ size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
+ limb_span large = limb_span(large_power_of_5, large_length);
+ while (exp >= large_step) {
+ FASTFLOAT_TRY(large_mul(vec, large));
+ exp -= large_step;
+ }
+#ifdef FASTFLOAT_64BIT_LIMB
+ uint32_t small_step = 27;
+ limb max_native = 7450580596923828125UL;
+#else
+ uint32_t small_step = 13;
+ limb max_native = 1220703125U;
+#endif
+ while (exp >= small_step) {
+ FASTFLOAT_TRY(small_mul(vec, max_native));
+ exp -= small_step;
+ }
+ if (exp != 0) {
+ FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp])));
+ }
+
+ return true;
+ }
+
+ // multiply as if by 10 raised to a power.
+ bool pow10(uint32_t exp) noexcept {
+ FASTFLOAT_TRY(pow5(exp));
+ return pow2(exp);
+ }
+ };
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_ASCII_NUMBER_H
+#define FASTFLOAT_ASCII_NUMBER_H
+
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+
+
+namespace fast_float {
+
+ // Next function can be micro-optimized, but compilers are entirely
+ // able to optimize it well.
+ fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
+
+ fastfloat_really_inline uint64_t byteswap(uint64_t val) {
+ return (val & 0xFF00000000000000) >> 56
+ | (val & 0x00FF000000000000) >> 40
+ | (val & 0x0000FF0000000000) >> 24
+ | (val & 0x000000FF00000000) >> 8
+ | (val & 0x00000000FF000000) << 8
+ | (val & 0x0000000000FF0000) << 24
+ | (val & 0x000000000000FF00) << 40
+ | (val & 0x00000000000000FF) << 56;
+ }
+
+ fastfloat_really_inline uint64_t read_u64(const char *chars) {
+ uint64_t val;
+ ::memcpy(&val, chars, sizeof(uint64_t));
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ return val;
+ }
+
+ fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ ::memcpy(chars, &val, sizeof(uint64_t));
+ }
+
+ // credit @aqrit
+ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
+ const uint64_t mask = 0x000000FF000000FF;
+ const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
+ const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
+ val -= 0x3030303030303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
+ return uint32_t(val);
+ }
+
+ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
+ return parse_eight_digits_unrolled(read_u64(chars));
+ }
+
+ // credit @aqrit
+ fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
+ return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
+ 0x8080808080808080));
+ }
+
+ fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
+ return is_made_of_eight_digits_fast(read_u64(chars));
+ }
+
+ typedef span<const char> byte_span;
+
+ struct parsed_number_string {
+ int64_t exponent{0};
+ uint64_t mantissa{0};
+ const char *lastmatch{nullptr};
+ bool negative{false};
+ bool valid{false};
+ bool too_many_digits{false};
+ // contains the range of the significant digits
+ byte_span integer{}; // non-nullable
+ byte_span fraction{}; // nullable
+ };
+
+ // Assuming that you use no more than 19 digits, this will
+ // parse an ASCII string.
+ fastfloat_really_inline
+ parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
+ const chars_format fmt = options.format;
+ const char decimal_point = options.decimal_point;
+
+ parsed_number_string answer;
+ answer.valid = false;
+ answer.too_many_digits = false;
+ answer.negative = (*p == '-');
+ if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
+ ++p;
+ if (p == pend) {
+ return answer;
+ }
+ if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
+ return answer;
+ }
+ }
+ const char *const start_digits = p;
+
+ uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
+
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ // a multiplication by 10 is cheaper than an arbitrary integer
+ // multiplication
+ i = 10 * i +
+ uint64_t(*p - '0'); // might overflow, we will handle the overflow later
+ ++p;
+ }
+ const char *const end_of_integer_part = p;
+ int64_t digit_count = int64_t(end_of_integer_part - start_digits);
+ answer.integer = byte_span(start_digits, size_t(digit_count));
+ int64_t exponent = 0;
+ if ((p != pend) && (*p == decimal_point)) {
+ ++p;
+ const char* before = p;
+ // can occur at most twice without overflowing, but let it occur more, since
+ // for integers with many digits, digit parsing is the primary bottleneck.
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ ++p;
+ i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
+ }
+ exponent = before - p;
+ answer.fraction = byte_span(before, size_t(p - before));
+ digit_count -= exponent;
+ }
+ // we must have encountered at least one integer!
+ if (digit_count == 0) {
+ return answer;
+ }
+ int64_t exp_number = 0; // explicit exponential part
+ if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
+ const char * location_of_e = p;
+ ++p;
+ bool neg_exp = false;
+ if ((p != pend) && ('-' == *p)) {
+ neg_exp = true;
+ ++p;
+ } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
+ ++p;
+ }
+ if ((p == pend) || !is_integer(*p)) {
+ if(!(fmt & chars_format::fixed)) {
+ // We are in error.
+ return answer;
+ }
+ // Otherwise, we will be ignoring the 'e'.
+ p = location_of_e;
+ } else {
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ if (exp_number < 0x10000000) {
+ exp_number = 10 * exp_number + digit;
+ }
+ ++p;
+ }
+ if(neg_exp) { exp_number = - exp_number; }
+ exponent += exp_number;
+ }
+ } else {
+ // If it scientific and not fixed, we have to bail out.
+ if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
+ }
+ answer.lastmatch = p;
+ answer.valid = true;
+
+ // If we frequently had to deal with long strings of digits,
+ // we could extend our code by using a 128-bit integer instead
+ // of a 64-bit integer. However, this is uncommon.
+ //
+ // We can deal with up to 19 digits.
+ if (digit_count > 19) { // this is uncommon
+ // It is possible that the integer had an overflow.
+ // We have to handle the case where we have 0.0000somenumber.
+ // We need to be mindful of the case where we only have zeroes...
+ // E.g., 0.000000000...000.
+ const char *start = start_digits;
+ while ((start != pend) && (*start == '0' || *start == decimal_point)) {
+ if(*start == '0') { digit_count --; }
+ start++;
+ }
+ if (digit_count > 19) {
+ answer.too_many_digits = true;
+ // Let us start again, this time, avoiding overflows.
+ // We don't need to check if is_integer, since we use the
+ // pre-tokenized spans from above.
+ i = 0;
+ p = answer.integer.ptr;
+ const char* int_end = p + answer.integer.len();
+ const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
+ while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ if (i >= minimal_nineteen_digit_integer) { // We have a big integers
+ exponent = end_of_integer_part - p + exp_number;
+ } else { // We have a value with a fractional component.
+ p = answer.fraction.ptr;
+ const char* frac_end = p + answer.fraction.len();
+ while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ exponent = answer.fraction.ptr - p + exp_number;
+ }
+ // We have now corrected both exponent and i, to a truncated value
+ }
+ }
+ answer.exponent = exponent;
+ answer.mantissa = i;
+ return answer;
+ }
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_DIGIT_COMPARISON_H
+#define FASTFLOAT_DIGIT_COMPARISON_H
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+
+
+namespace fast_float {
+
+ // 1e0 to 1e19
+ constexpr static uint64_t powers_of_ten_uint64[] = {
+ 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
+ 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
+ 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
+ 1000000000000000000UL, 10000000000000000000UL};
+
+ // calculate the exponent, in scientific notation, of the number.
+ // this algorithm is not even close to optimized, but it has no practical
+ // effect on performance: in order to have a faster algorithm, we'd need
+ // to slow down performance for faster algorithms, and this is still fast.
+ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept {
+ uint64_t mantissa = num.mantissa;
+ int32_t exponent = int32_t(num.exponent);
+ while (mantissa >= 10000) {
+ mantissa /= 10000;
+ exponent += 4;
+ }
+ while (mantissa >= 100) {
+ mantissa /= 100;
+ exponent += 2;
+ }
+ while (mantissa >= 10) {
+ mantissa /= 10;
+ exponent += 1;
+ }
+ return exponent;
+ }
+
+ // this converts a native floating-point number to an extended-precision float.
+ template <typename T>
+ fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
+ using equiv_uint = typename binary_format<T>::equiv_uint;
+ constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
+ constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
+ constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
+
+ adjusted_mantissa am;
+ int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
+ equiv_uint bits;
+ ::memcpy(&bits, &value, sizeof(T));
+ if ((bits & exponent_mask) == 0) {
+ // denormal
+ am.power2 = 1 - bias;
+ am.mantissa = bits & mantissa_mask;
+ } else {
+ // normal
+ am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
+ am.power2 -= bias;
+ am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
+ }
+
+ return am;
+ }
+
+ // get the extended precision value of the halfway point between b and b+u.
+ // we are given a native float that represents b, so we need to adjust it
+ // halfway between b and b+u.
+ template <typename T>
+ fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept {
+ adjusted_mantissa am = to_extended(value);
+ am.mantissa <<= 1;
+ am.mantissa += 1;
+ am.power2 -= 1;
+ return am;
+ }
+
+ // round an extended-precision float to the nearest machine float.
+ template <typename T, typename callback>
+ fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept {
+ int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
+ if (-am.power2 >= mantissa_shift) {
+ // have a denormal float
+ int32_t shift = -am.power2 + 1;
+ cb(am, std::min(shift, 64));
+ // check for round-up: if rounding-nearest carried us to the hidden bit.
+ am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
+ return;
+ }
+
+ // have a normal float, use the default shift.
+ cb(am, mantissa_shift);
+
+ // check for carry
+ if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
+ am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
+ am.power2++;
+ }
+
+ // check for infinite: we could have carried to an infinite power
+ am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
+ if (am.power2 >= binary_format<T>::infinite_power()) {
+ am.power2 = binary_format<T>::infinite_power();
+ am.mantissa = 0;
+ }
+ }
+
+ template <typename callback>
+ fastfloat_really_inline
+ void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
+ uint64_t mask;
+ uint64_t halfway;
+ if (shift == 64) {
+ mask = UINT64_MAX;
+ } else {
+ mask = (uint64_t(1) << shift) - 1;
+ }
+ if (shift == 0) {
+ halfway = 0;
+ } else {
+ halfway = uint64_t(1) << (shift - 1);
+ }
+ uint64_t truncated_bits = am.mantissa & mask;
+ uint64_t is_above = truncated_bits > halfway;
+ uint64_t is_halfway = truncated_bits == halfway;
+
+ // shift digits into position
+ if (shift == 64) {
+ am.mantissa = 0;
+ } else {
+ am.mantissa >>= shift;
+ }
+ am.power2 += shift;
+
+ bool is_odd = (am.mantissa & 1) == 1;
+ am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
+ }
+
+ fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
+ if (shift == 64) {
+ am.mantissa = 0;
+ } else {
+ am.mantissa >>= shift;
+ }
+ am.power2 += shift;
+ }
+
+ fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept {
+ uint64_t val;
+ while (std::distance(first, last) >= 8) {
+ ::memcpy(&val, first, sizeof(uint64_t));
+ if (val != 0x3030303030303030) {
+ break;
+ }
+ first += 8;
+ }
+ while (first != last) {
+ if (*first != '0') {
+ break;
+ }
+ first++;
+ }
+ }
+
+ // determine if any non-zero digits were truncated.
+ // all characters must be valid digits.
+ fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept {
+ // do 8-bit optimizations, can just compare to 8 literal 0s.
+ uint64_t val;
+ while (std::distance(first, last) >= 8) {
+ ::memcpy(&val, first, sizeof(uint64_t));
+ if (val != 0x3030303030303030) {
+ return true;
+ }
+ first += 8;
+ }
+ while (first != last) {
+ if (*first != '0') {
+ return true;
+ }
+ first++;
+ }
+ return false;
+ }
+
+ fastfloat_really_inline bool is_truncated(byte_span s) noexcept {
+ return is_truncated(s.ptr, s.ptr + s.len());
+ }
+
+ fastfloat_really_inline
+ void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
+ value = value * 100000000 + parse_eight_digits_unrolled(p);
+ p += 8;
+ counter += 8;
+ count += 8;
+ }
+
+ fastfloat_really_inline
+ void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
+ value = value * 10 + limb(*p - '0');
+ p++;
+ counter++;
+ count++;
+ }
+
+ fastfloat_really_inline
+ void add_native(bigint& big, limb power, limb value) noexcept {
+ big.mul(power);
+ big.add(value);
+ }
+
+ fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept {
+ // need to round-up the digits, but need to avoid rounding
+ // ....9999 to ...10000, which could cause a false halfway point.
+ add_native(big, 10, 1);
+ count++;
+ }
+
+ // parse the significant digits into a big integer
+ inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept {
+ // try to minimize the number of big integer and scalar multiplication.
+ // therefore, try to parse 8 digits at a time, and multiply by the largest
+ // scalar value (9 or 19 digits) for each step.
+ size_t counter = 0;
+ digits = 0;
+ limb value = 0;
+#ifdef FASTFLOAT_64BIT_LIMB
+ size_t step = 19;
+#else
+ size_t step = 9;
+#endif
+
+ // process all integer digits.
+ const char* p = num.integer.ptr;
+ const char* pend = p + num.integer.len();
+ skip_zeros(p, pend);
+ // process all digits, in increments of step per loop
+ while (p != pend) {
+ while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
+ parse_eight_digits(p, value, counter, digits);
+ }
+ while (counter < step && p != pend && digits < max_digits) {
+ parse_one_digit(p, value, counter, digits);
+ }
+ if (digits == max_digits) {
+ // add the temporary value, then check if we've truncated any digits
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ bool truncated = is_truncated(p, pend);
+ if (num.fraction.ptr != nullptr) {
+ truncated |= is_truncated(num.fraction);
+ }
+ if (truncated) {
+ round_up_bigint(result, digits);
+ }
+ return;
+ } else {
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ counter = 0;
+ value = 0;
+ }
+ }
+
+ // add our fraction digits, if they're available.
+ if (num.fraction.ptr != nullptr) {
+ p = num.fraction.ptr;
+ pend = p + num.fraction.len();
+ if (digits == 0) {
+ skip_zeros(p, pend);
+ }
+ // process all digits, in increments of step per loop
+ while (p != pend) {
+ while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
+ parse_eight_digits(p, value, counter, digits);
+ }
+ while (counter < step && p != pend && digits < max_digits) {
+ parse_one_digit(p, value, counter, digits);
+ }
+ if (digits == max_digits) {
+ // add the temporary value, then check if we've truncated any digits
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ bool truncated = is_truncated(p, pend);
+ if (truncated) {
+ round_up_bigint(result, digits);
+ }
+ return;
+ } else {
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ counter = 0;
+ value = 0;
+ }
+ }
+ }
+
+ if (counter != 0) {
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ }
+ }
+
+ template <typename T>
+ inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
+ FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
+ adjusted_mantissa answer;
+ bool truncated;
+ answer.mantissa = bigmant.hi64(truncated);
+ int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
+ answer.power2 = bigmant.bit_length() - 64 + bias;
+
+ round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
+ round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
+ return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
+ });
+ });
+
+ return answer;
+ }
+
+ // the scaling here is quite simple: we have, for the real digits `m * 10^e`,
+ // and for the theoretical digits `n * 2^f`. Since `e` is always negative,
+ // to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
+ // we then need to scale by `2^(f- e)`, and then the two significant digits
+ // are of the same magnitude.
+ template <typename T>
+ inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
+ bigint& real_digits = bigmant;
+ int32_t real_exp = exponent;
+
+ // get the value of `b`, rounded down, and get a bigint representation of b+h
+ adjusted_mantissa am_b = am;
+ // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
+ round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
+ T b;
+ to_float(false, am_b, b);
+ adjusted_mantissa theor = to_extended_halfway(b);
+ bigint theor_digits(theor.mantissa);
+ int32_t theor_exp = theor.power2;
+
+ // scale real digits and theor digits to be same power.
+ int32_t pow2_exp = theor_exp - real_exp;
+ uint32_t pow5_exp = uint32_t(-real_exp);
+ if (pow5_exp != 0) {
+ FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
+ }
+ if (pow2_exp > 0) {
+ FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
+ } else if (pow2_exp < 0) {
+ FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
+ }
+
+ // compare digits, and use it to director rounding
+ int ord = real_digits.compare(theor_digits);
+ adjusted_mantissa answer = am;
+ round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
+ round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
+ (void)_; // not needed, since we've done our comparison
+ (void)__; // not needed, since we've done our comparison
+ if (ord > 0) {
+ return true;
+ } else if (ord < 0) {
+ return false;
+ } else {
+ return is_odd;
+ }
+ });
+ });
+
+ return answer;
+ }
+
+ // parse the significant digits as a big integer to unambiguously round the
+ // the significant digits. here, we are trying to determine how to round
+ // an extended float representation close to `b+h`, halfway between `b`
+ // (the float rounded-down) and `b+u`, the next positive float. this
+ // algorithm is always correct, and uses one of two approaches. when
+ // the exponent is positive relative to the significant digits (such as
+ // 1234), we create a big-integer representation, get the high 64-bits,
+ // determine if any lower bits are truncated, and use that to direct
+ // rounding. in case of a negative exponent relative to the significant
+ // digits (such as 1.2345), we create a theoretical representation of
+ // `b` as a big-integer type, scaled to the same binary exponent as
+ // the actual digits. we then compare the big integer representations
+ // of both, and use that to direct rounding.
+ template <typename T>
+ inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept {
+ // remove the invalid exponent bias
+ am.power2 -= invalid_am_bias;
+
+ int32_t sci_exp = scientific_exponent(num);
+ size_t max_digits = binary_format<T>::max_digits();
+ size_t digits = 0;
+ bigint bigmant;
+ parse_mantissa(bigmant, num, max_digits, digits);
+ // can't underflow, since digits is at most max_digits.
+ int32_t exponent = sci_exp + 1 - int32_t(digits);
+ if (exponent >= 0) {
+ return positive_digit_comp<T>(bigmant, exponent);
+ } else {
+ return negative_digit_comp<T>(bigmant, am, exponent);
+ }
+ }
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_PARSE_NUMBER_H
+#define FASTFLOAT_PARSE_NUMBER_H
+
+
+#include <cmath>
+#include <cstring>
+#include <limits>
+#include <system_error>
+
+namespace fast_float {
+
+
+ namespace detail {
+ /**
+ * Special case +inf, -inf, nan, infinity, -infinity.
+ * The case comparisons could be made much faster given that we know that the
+ * strings a null-free and fixed.
+ **/
+ template <typename T>
+ from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
+ from_chars_result answer;
+ answer.ptr = first;
+ answer.ec = std::errc(); // be optimistic
+ bool minusSign = false;
+ if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
+ minusSign = true;
+ ++first;
+ }
+ if (last - first >= 3) {
+ if (fastfloat_strncasecmp(first, "nan", 3)) {
+ answer.ptr = (first += 3);
+ value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
+ // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
+ if(first != last && *first == '(') {
+ for(const char* ptr = first + 1; ptr != last; ++ptr) {
+ if (*ptr == ')') {
+ answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
+ break;
+ }
+ else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_'))
+ break; // forbidden char, not nan(n-char-seq-opt)
+ }
+ }
+ return answer;
+ }
+ if (fastfloat_strncasecmp(first, "inf", 3)) {
+ if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) {
+ answer.ptr = first + 8;
+ } else {
+ answer.ptr = first + 3;
+ }
+ value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
+ return answer;
+ }
+ }
+ answer.ec = std::errc::invalid_argument;
+ return answer;
+ }
+
+ } // namespace detail
+
+ template<typename T>
+ from_chars_result from_chars(const char *first, const char *last,
+ T &value, chars_format fmt /*= chars_format::general*/) noexcept {
+ return from_chars_advanced(first, last, value, parse_options{fmt});
+ }
+
+ template<typename T>
+ from_chars_result from_chars_advanced(const char *first, const char *last,
+ T &value, parse_options options) noexcept {
+
+ static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
+
+
+ from_chars_result answer;
+ if (first == last) {
+ answer.ec = std::errc::invalid_argument;
+ answer.ptr = first;
+ return answer;
+ }
+ parsed_number_string pns = parse_number_string(first, last, options);
+ if (!pns.valid) {
+ return detail::parse_infnan(first, last, value);
+ }
+ answer.ec = std::errc(); // be optimistic
+ answer.ptr = pns.lastmatch;
+ // Next is Clinger's fast path.
+ if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) {
+ value = T(pns.mantissa);
+ if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
+ else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
+ if (pns.negative) { value = -value; }
+ return answer;
+ }
+ adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
+ if(pns.too_many_digits && am.power2 >= 0) {
+ if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
+ am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
+ }
+ }
+ // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
+ // then we need to go the long way around again. This is very uncommon.
+ if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
+ to_float(pns.negative, am, value);
+ return answer;
+ }
+
+} // namespace fast_float
+
+#endif
+
diff --git a/src/third-party/scnlib/src/file.cpp b/src/third-party/scnlib/src/file.cpp
new file mode 100644
index 0000000..ec53145
--- /dev/null
+++ b/src/third-party/scnlib/src/file.cpp
@@ -0,0 +1,311 @@
+// 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
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY
+#define SCN_FILE_CPP
+#endif
+
+#include <scn/detail/error.h>
+#include <scn/detail/file.h>
+#include <scn/util/expected.h>
+
+#include <cstdio>
+
+#if SCN_POSIX
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#elif SCN_WINDOWS
+
+#ifdef WIN32_LEAN_AND_MEAN
+#define SCN_WIN32_LEAN_DEFINED 1
+#else
+#define WIN32_LEAN_AND_MEAN
+#define SCN_WIN32_LEAN_DEFINED 0
+#endif
+
+#ifdef NOMINMAX
+#define SCN_NOMINMAX_DEFINED 1
+#else
+#define NOMINMAX
+#define SCN_NOMINMAX_DEFINED 0
+#endif
+
+#include <Windows.h>
+
+#if !SCN_NOMINMAX_DEFINED
+#undef NOMINMAX
+#endif
+
+#if !SCN_WIN32_LEAN_DEFINED
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+#endif
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ SCN_FUNC native_file_handle native_file_handle::invalid()
+ {
+#if SCN_WINDOWS
+ return {INVALID_HANDLE_VALUE};
+#else
+ return {-1};
+#endif
+ }
+
+ SCN_FUNC byte_mapped_file::byte_mapped_file(const char* filename)
+ {
+#if SCN_POSIX
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ return;
+ }
+
+ struct stat s {
+ };
+ int status = fstat(fd, &s);
+ if (status == -1) {
+ close(fd);
+ return;
+ }
+ auto size = s.st_size;
+
+ auto ptr =
+ static_cast<char*>(mmap(nullptr, static_cast<size_t>(size),
+ PROT_READ, MAP_PRIVATE, fd, 0));
+ if (ptr == MAP_FAILED) {
+ close(fd);
+ return;
+ }
+
+ m_file.handle = fd;
+ m_map = span<char>{ptr, static_cast<size_t>(size)};
+#elif SCN_WINDOWS
+ auto f = ::CreateFileA(
+ filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (f == INVALID_HANDLE_VALUE) {
+ return;
+ }
+
+ LARGE_INTEGER _size;
+ if (::GetFileSizeEx(f, &_size) == 0) {
+ ::CloseHandle(f);
+ return;
+ }
+ auto size = static_cast<size_t>(_size.QuadPart);
+
+ auto h = ::CreateFileMappingA(
+ f, nullptr, PAGE_READONLY,
+#ifdef _WIN64
+ static_cast<DWORD>(size >> 32ull),
+#else
+ DWORD{0},
+#endif
+ static_cast<DWORD>(size & 0xffffffffull), nullptr);
+ if (h == INVALID_HANDLE_VALUE || h == nullptr) {
+ ::CloseHandle(f);
+ return;
+ }
+
+ auto start = ::MapViewOfFile(h, FILE_MAP_READ, 0, 0, size);
+ if (!start) {
+ ::CloseHandle(h);
+ ::CloseHandle(f);
+ return;
+ }
+
+ m_file.handle = f;
+ m_map_handle.handle = h;
+ m_map = span<char>{static_cast<char*>(start), size};
+#else
+ SCN_UNUSED(filename);
+#endif
+ }
+
+ SCN_FUNC void byte_mapped_file::_destruct()
+ {
+#if SCN_POSIX
+ munmap(m_map.data(), m_map.size());
+ close(m_file.handle);
+#elif SCN_WINDOWS
+ ::CloseHandle(m_map_handle.handle);
+ ::CloseHandle(m_file.handle);
+ m_map_handle = native_file_handle::invalid();
+#endif
+
+ m_file = native_file_handle::invalid();
+ m_map = span<char>{};
+
+ SCN_ENSURE(!valid());
+ }
+
+ } // namespace detail
+
+ namespace detail {
+ template <typename CharT>
+ struct basic_file_iterator_access {
+ using iterator = typename basic_file<CharT>::iterator;
+
+ basic_file_iterator_access(const iterator& it) : self(it) {}
+
+ SCN_NODISCARD expected<CharT> deref() const
+ {
+ SCN_EXPECT(self.m_file);
+
+ if (self.m_file->m_buffer.empty()) {
+ // no chars have been read
+ return self.m_file->_read_single();
+ }
+ if (!self.m_last_error) {
+ // last read failed
+ return self.m_last_error;
+ }
+ return self.m_file->_get_char_at(self.m_current);
+ }
+
+ SCN_NODISCARD bool eq(const iterator& o) const
+ {
+ if (self.m_file && (self.m_file == o.m_file || !o.m_file)) {
+ if (self.m_file->_is_at_end(self.m_current) &&
+ self.m_last_error.code() != error::end_of_range &&
+ !o.m_file) {
+ self.m_last_error = error{};
+ auto r = self.m_file->_read_single();
+ if (!r) {
+ self.m_last_error = r.error();
+ return !o.m_file || self.m_current == o.m_current ||
+ o.m_last_error.code() == error::end_of_range;
+ }
+ }
+ }
+
+ // null file == null file
+ if (!self.m_file && !o.m_file) {
+ return true;
+ }
+ // null file == eof file
+ if (!self.m_file && o.m_file) {
+ // lhs null, rhs potentially eof
+ return o.m_last_error.code() == error::end_of_range;
+ }
+ // eof file == null file
+ if (self.m_file && !o.m_file) {
+ // rhs null, lhs potentially eof
+ return self.m_last_error.code() == error::end_of_range;
+ }
+ // eof file == eof file
+ if (self.m_last_error == o.m_last_error &&
+ self.m_last_error.code() == error::end_of_range) {
+ return true;
+ }
+
+ return self.m_file == o.m_file && self.m_current == o.m_current;
+ }
+
+ const iterator& self;
+ };
+ } // namespace detail
+
+ template <>
+ SCN_FUNC expected<char> basic_file<char>::iterator::operator*() const
+ {
+ return detail::basic_file_iterator_access<char>(*this).deref();
+ }
+ template <>
+ SCN_FUNC expected<wchar_t> basic_file<wchar_t>::iterator::operator*() const
+ {
+ return detail::basic_file_iterator_access<wchar_t>(*this).deref();
+ }
+
+ template <>
+ SCN_FUNC bool basic_file<char>::iterator::operator==(
+ const basic_file<char>::iterator& o) const
+ {
+ return detail::basic_file_iterator_access<char>(*this).eq(o);
+ }
+ template <>
+ SCN_FUNC bool basic_file<wchar_t>::iterator::operator==(
+ const basic_file<wchar_t>::iterator& o) const
+ {
+ return detail::basic_file_iterator_access<wchar_t>(*this).eq(o);
+ }
+
+ template <>
+ SCN_FUNC expected<char> file::_read_single() const
+ {
+ SCN_EXPECT(valid());
+ int tmp = std::fgetc(m_file);
+ if (tmp == EOF) {
+ if (std::feof(m_file) != 0) {
+ return error(error::end_of_range, "EOF");
+ }
+ if (std::ferror(m_file) != 0) {
+ return error(error::source_error, "fgetc error");
+ }
+ return error(error::unrecoverable_source_error,
+ "Unknown fgetc error");
+ }
+ auto ch = static_cast<char>(tmp);
+ m_buffer.push_back(ch);
+ return ch;
+ }
+ template <>
+ SCN_FUNC expected<wchar_t> wfile::_read_single() const
+ {
+ SCN_EXPECT(valid());
+ wint_t tmp = std::fgetwc(m_file);
+ if (tmp == WEOF) {
+ if (std::feof(m_file) != 0) {
+ return error(error::end_of_range, "EOF");
+ }
+ if (std::ferror(m_file) != 0) {
+ return error(error::source_error, "fgetc error");
+ }
+ return error(error::unrecoverable_source_error,
+ "Unknown fgetc error");
+ }
+ auto ch = static_cast<wchar_t>(tmp);
+ m_buffer.push_back(ch);
+ return ch;
+ }
+
+ template <>
+ SCN_FUNC void file::_sync_until(std::size_t pos) noexcept
+ {
+ for (auto it = m_buffer.rbegin();
+ it != m_buffer.rend() - static_cast<std::ptrdiff_t>(pos); ++it) {
+ std::ungetc(static_cast<unsigned char>(*it), m_file);
+ }
+ }
+ template <>
+ SCN_FUNC void wfile::_sync_until(std::size_t pos) noexcept
+ {
+ for (auto it = m_buffer.rbegin();
+ it != m_buffer.rend() - static_cast<std::ptrdiff_t>(pos); ++it) {
+ std::ungetwc(static_cast<wint_t>(*it), m_file);
+ }
+ }
+
+ SCN_END_NAMESPACE
+} // namespace scn
diff --git a/src/third-party/scnlib/src/locale.cpp b/src/third-party/scnlib/src/locale.cpp
new file mode 100644
index 0000000..bc628e0
--- /dev/null
+++ b/src/third-party/scnlib/src/locale.cpp
@@ -0,0 +1,668 @@
+// 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
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY
+#define SCN_LOCALE_CPP
+#endif
+
+#include <scn/detail/locale.h>
+#include <scn/util/math.h>
+
+#include <cctype>
+#include <cmath>
+#include <cwchar>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ template <typename CharT>
+ struct locale_data {
+ using char_type = CharT;
+ using string_type = std::basic_string<char_type>;
+
+ std::locale global_locale{std::locale()};
+ std::locale classic_locale{std::locale::classic()};
+
+ string_type truename{};
+ string_type falsename{};
+ char_type decimal_point{};
+ char_type thousands_separator{};
+ };
+
+ template <typename CharT>
+ const std::locale& to_locale(const basic_custom_locale_ref<CharT>& l)
+ {
+ SCN_EXPECT(l.get_locale());
+ return *static_cast<const std::locale*>(l.get_locale());
+ }
+
+ // Buggy on gcc 5 and 6
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wmaybe-uninitialized")
+
+ template <typename CharT>
+ basic_custom_locale_ref<CharT>::basic_custom_locale_ref()
+ {
+ auto data = new locale_data<CharT>{};
+ m_data = data;
+ m_locale = &data->global_locale;
+ _initialize();
+ }
+ template <typename CharT>
+ basic_custom_locale_ref<CharT>::basic_custom_locale_ref(
+ const void* locale)
+ : m_locale(locale)
+ {
+ auto data = new locale_data<CharT>{};
+ m_data = data;
+ if (!locale) {
+ m_locale = &data->global_locale;
+ }
+ _initialize();
+ }
+
+ SCN_GCC_POP
+
+ template <typename CharT>
+ void basic_custom_locale_ref<CharT>::_initialize()
+ {
+ const auto& facet =
+ std::use_facet<std::numpunct<CharT>>(to_locale(*this));
+
+ auto& data = *static_cast<locale_data<CharT>*>(m_data);
+ data.truename = facet.truename();
+ data.falsename = facet.falsename();
+ data.decimal_point = facet.decimal_point();
+ data.thousands_separator = facet.thousands_sep();
+ }
+
+ template <typename CharT>
+ basic_custom_locale_ref<CharT>::basic_custom_locale_ref(
+ basic_custom_locale_ref&& o)
+ {
+ m_data = o.m_data;
+ m_locale = o.m_locale;
+
+ o.m_data = nullptr;
+ o.m_locale = nullptr;
+
+ _initialize();
+ }
+ template <typename CharT>
+ auto basic_custom_locale_ref<CharT>::operator=(
+ basic_custom_locale_ref&& o) -> basic_custom_locale_ref&
+ {
+ delete static_cast<locale_data<CharT>*>(m_data);
+
+ m_data = o.m_data;
+ m_locale = o.m_locale;
+
+ o.m_data = nullptr;
+ o.m_locale = nullptr;
+
+ _initialize();
+
+ return *this;
+ }
+
+ template <typename CharT>
+ basic_custom_locale_ref<CharT>::~basic_custom_locale_ref()
+ {
+ delete static_cast<locale_data<CharT>*>(m_data);
+ }
+
+ template <typename CharT>
+ auto basic_custom_locale_ref<CharT>::make_classic()
+ -> basic_custom_locale_ref
+ {
+ basic_custom_locale_ref loc{};
+ loc.convert_to_classic();
+ return loc;
+ }
+
+ template <typename CharT>
+ void basic_custom_locale_ref<CharT>::convert_to_classic()
+ {
+ m_locale =
+ &static_cast<locale_data<CharT>*>(m_data)->classic_locale;
+ }
+ template <typename CharT>
+ void basic_custom_locale_ref<CharT>::convert_to_global()
+ {
+ SCN_EXPECT(m_data);
+ m_locale = &static_cast<locale_data<CharT>*>(m_data)->global_locale;
+ }
+
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::do_is_space(char_type ch) const
+ {
+ return std::isspace(ch, to_locale(*this));
+ }
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::do_is_digit(char_type ch) const
+ {
+ return std::isdigit(ch, to_locale(*this));
+ }
+
+ template <typename CharT>
+ auto basic_custom_locale_ref<CharT>::do_decimal_point() const
+ -> char_type
+ {
+ return static_cast<locale_data<CharT>*>(m_data)->decimal_point;
+ }
+ template <typename CharT>
+ auto basic_custom_locale_ref<CharT>::do_thousands_separator() const
+ -> char_type
+ {
+ return static_cast<locale_data<CharT>*>(m_data)
+ ->thousands_separator;
+ }
+ template <typename CharT>
+ auto basic_custom_locale_ref<CharT>::do_truename() const
+ -> string_view_type
+ {
+ const auto& str =
+ static_cast<locale_data<CharT>*>(m_data)->truename;
+ return {str.data(), str.size()};
+ }
+ template <typename CharT>
+ auto basic_custom_locale_ref<CharT>::do_falsename() const
+ -> string_view_type
+ {
+ const auto& str =
+ static_cast<locale_data<CharT>*>(m_data)->falsename;
+ return {str.data(), str.size()};
+ }
+
+ static inline error convert_to_wide_impl(const std::locale&,
+ const char*,
+ const char*,
+ const char*&,
+ wchar_t*,
+ wchar_t*,
+ wchar_t*&)
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+ static inline error convert_to_wide_impl(const std::locale&,
+ const wchar_t*,
+ const wchar_t*,
+ const wchar_t*&,
+ wchar_t*,
+ wchar_t*,
+ wchar_t*&)
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+
+ template <typename CharT>
+ error basic_custom_locale_ref<CharT>::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
+ {
+ return convert_to_wide_impl(to_locale(*this), from_begin, from_end,
+ from_next, to_begin, to_end, to_next);
+ }
+
+ static inline expected<wchar_t> convert_to_wide_impl(const std::locale&,
+ const char*,
+ const char*)
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+ static inline expected<wchar_t> convert_to_wide_impl(const std::locale&,
+ const wchar_t*,
+ const wchar_t*)
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+
+ template <typename CharT>
+ expected<wchar_t> basic_custom_locale_ref<CharT>::convert_to_wide(
+ const CharT* from_begin,
+ const CharT* from_end) const
+ {
+ return convert_to_wide_impl(to_locale(*this), from_begin, from_end);
+ }
+
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::do_is_space(
+ span<const char_type> ch) const
+ {
+ const auto& locale = to_locale(*this);
+ if (sizeof(CharT) == 1) {
+ SCN_EXPECT(ch.size() >= 1);
+ code_point cp{};
+ auto it = parse_code_point(ch.begin(), ch.end(), cp);
+ SCN_EXPECT(it);
+ return is_space(cp);
+ }
+ SCN_EXPECT(ch.size() == 1);
+ return std::isspace(ch[0], locale);
+ }
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::do_is_digit(
+ span<const char_type> ch) const
+ {
+ const auto& locale = to_locale(*this);
+ if (sizeof(CharT) == 1) {
+ SCN_EXPECT(ch.size() >= 1);
+ code_point cp{};
+ auto it = parse_code_point(ch.begin(), ch.end(), cp);
+ SCN_EXPECT(it);
+ return is_digit(cp);
+ }
+ SCN_EXPECT(ch.size() == 1);
+ return std::isdigit(ch[0], locale);
+ }
+
+#define SCN_DEFINE_CUSTOM_LOCALE_CTYPE(f) \
+ template <typename CharT> \
+ bool basic_custom_locale_ref<CharT>::is_##f(char_type ch) const \
+ { \
+ return std::is##f(ch, to_locale(*this)); \
+ } \
+ template <typename CharT> \
+ bool basic_custom_locale_ref<CharT>::is_##f(code_point cp) const \
+ { \
+ return std::is##f(static_cast<wchar_t>(cp), to_locale(*this)); \
+ } \
+ template <typename CharT> \
+ bool basic_custom_locale_ref<CharT>::is_##f(span<const char_type> ch) \
+ const \
+ { \
+ const auto& locale = to_locale(*this); \
+ if (sizeof(CharT) == 1) { \
+ SCN_EXPECT(ch.size() >= 1); \
+ code_point cp{}; \
+ auto it = parse_code_point(ch.begin(), ch.end(), cp); \
+ SCN_EXPECT(it); \
+ return is_##f(cp); \
+ } \
+ SCN_EXPECT(ch.size() == 1); \
+ return std::is##f(ch[0], locale); \
+ }
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(alnum)
+ SCN_DEFINE_CUSTOM_LOCALE_CTYPE(alpha)
+ 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
+
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::is_space(code_point cp) const
+ {
+ return std::isspace(static_cast<wchar_t>(cp), to_locale(*this));
+ }
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::is_digit(code_point cp) const
+ {
+ return std::isdigit(static_cast<wchar_t>(cp), to_locale(*this));
+ }
+
+ // For some reason, there's no isblank in libc++
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::is_blank(char_type ch) const
+ {
+ return std::use_facet<std::ctype<CharT>>(to_locale(*this))
+ .is(std::ctype_base::blank, ch);
+ }
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::is_blank(code_point ch) const
+ {
+ return std::use_facet<std::ctype<wchar_t>>(to_locale(*this))
+ .is(std::ctype_base::blank, static_cast<wchar_t>(ch));
+ }
+ template <typename CharT>
+ bool basic_custom_locale_ref<CharT>::is_blank(
+ span<const char_type> ch) const
+ {
+ const auto& locale = to_locale(*this);
+ if (sizeof(CharT) == 1) {
+ SCN_EXPECT(ch.size() >= 1);
+ code_point cp{};
+ auto it = parse_code_point(ch.begin(), ch.end(), cp);
+ SCN_EXPECT(it);
+ return is_blank(cp);
+ }
+ SCN_EXPECT(ch.size() == 1);
+ return std::use_facet<std::ctype<CharT>>(locale).is(
+ std::ctype_base::blank, ch[0]);
+ }
+
+ template <typename T>
+ auto read_num_check_range(T val) ->
+ typename std::enable_if<std::is_integral<T>::value, error>::type
+ {
+ if (val == std::numeric_limits<T>::max()) {
+ return error(error::value_out_of_range,
+ "Scanned number out of range: overflow");
+ }
+ if (val == std::numeric_limits<T>::min()) {
+ return error(error::value_out_of_range,
+ "Scanned number out of range: underflow");
+ }
+ return error(error::invalid_scanned_value,
+ "Localized number read failed");
+ }
+ template <typename T>
+ auto read_num_check_range(T val) ->
+ typename std::enable_if<std::is_floating_point<T>::value,
+ error>::type
+ {
+ SCN_GCC_COMPAT_PUSH
+ SCN_GCC_COMPAT_IGNORE("-Wfloat-equal")
+ if (val == std::numeric_limits<T>::max() ||
+ val == -std::numeric_limits<T>::max()) {
+ return error(error::value_out_of_range,
+ "Scanned number out of range: overflow");
+ }
+ if (val == zero_value<T>::value) {
+ return error(error::value_out_of_range,
+ "Scanned number out of range: underflow");
+ }
+ SCN_GCC_COMPAT_POP
+ return error(error::invalid_scanned_value,
+ "Localized number read failed");
+ }
+
+ template <typename T, typename CharT>
+ error do_read_num_impl(T& val, std::basic_istringstream<CharT>& ss)
+ {
+ ss >> val;
+ return {};
+ }
+ template <typename CharT>
+ error do_read_num_impl(CharT& val, std::basic_istringstream<CharT>& ss)
+ {
+ long long tmp;
+ if (!(ss >> tmp)) {
+ return {};
+ }
+ if (tmp > std::numeric_limits<CharT>::max()) {
+ return {error::value_out_of_range,
+ "Scanned number out of range: overflow"};
+ }
+ if (tmp < std::numeric_limits<CharT>::min()) {
+ return {error::value_out_of_range,
+ "Scanned number out of range: underflow"};
+ }
+ val = static_cast<CharT>(tmp);
+ return {};
+ }
+ template <typename CharT>
+ error do_read_num_impl(signed char& val,
+ std::basic_istringstream<CharT>& ss)
+ {
+ int tmp;
+ if (!(ss >> tmp)) {
+ return {};
+ }
+ if (tmp > std::numeric_limits<signed char>::max()) {
+ return {error::value_out_of_range,
+ "Scanned number out of range: overflow"};
+ }
+ if (tmp < std::numeric_limits<signed char>::min()) {
+ return {error::value_out_of_range,
+ "Scanned number out of range: underflow"};
+ }
+ val = static_cast<signed char>(tmp);
+ return {};
+ }
+ template <typename CharT>
+ error do_read_num_impl(unsigned char& val,
+ std::basic_istringstream<CharT>& ss)
+ {
+ int tmp;
+ if (!(ss >> tmp)) {
+ return {};
+ }
+ if (tmp > std::numeric_limits<unsigned char>::max()) {
+ return {error::value_out_of_range,
+ "Scanned number out of range: overflow"};
+ }
+ if (tmp < 0) {
+ return {error::value_out_of_range,
+ "Scanned number out of range: underflow"};
+ }
+ val = static_cast<unsigned char>(tmp);
+ return {};
+ }
+
+ template <typename T, typename CharT>
+ expected<std::ptrdiff_t> do_read_num(
+ T& val,
+ const std::locale& loc,
+ const std::basic_string<CharT>& buf,
+ int base)
+ {
+#if SCN_HAS_EXCEPTIONS
+ std::basic_istringstream<CharT> ss(buf);
+ ss.imbue(loc);
+ ss >> std::setbase(base);
+
+ try {
+ T tmp;
+ auto e = do_read_num_impl(tmp, ss);
+ if (ss.bad()) {
+ return error(error::unrecoverable_internal_error,
+ "Localized stringstream is bad");
+ }
+ if (!e) {
+ return e;
+ }
+ if (ss.fail()) {
+ return read_num_check_range(tmp);
+ }
+ val = tmp;
+ }
+ catch (const std::ios_base::failure& f) {
+ return error(error::invalid_scanned_value, f.what());
+ }
+ return ss.eof() ? static_cast<std::ptrdiff_t>(buf.size())
+ : static_cast<std::ptrdiff_t>(ss.tellg());
+#else
+ SCN_UNUSED(val);
+ SCN_UNUSED(loc);
+ SCN_UNUSED(buf);
+ return error(error::exceptions_required,
+ "Localized number reading is only supported with "
+ "exceptions enabled");
+#endif
+ }
+
+ template <>
+ expected<std::ptrdiff_t> do_read_num(wchar_t&,
+ const std::locale&,
+ const std::string&,
+ int)
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+ template <>
+ expected<std::ptrdiff_t> do_read_num(char&,
+ const std::locale&,
+ const std::wstring&,
+ int)
+ {
+ SCN_EXPECT(false);
+ SCN_UNREACHABLE;
+ }
+
+ template <typename CharT>
+ template <typename T>
+ expected<std::ptrdiff_t> basic_custom_locale_ref<CharT>::read_num(
+ T& val,
+ const string_type& buf,
+ int b) const
+ {
+ return do_read_num<T, CharT>(val, to_locale(*this), buf, b);
+ }
+
+#if SCN_INCLUDE_SOURCE_DEFINITIONS
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wpadded")
+ SCN_CLANG_IGNORE("-Wweak-template-vtables")
+ template class basic_custom_locale_ref<char>;
+ template class basic_custom_locale_ref<wchar_t>;
+ SCN_CLANG_POP
+
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<signed char>(signed char&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<short>(short&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<int>(int&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<long>(long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<long long>(long long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ char>::read_num<unsigned char>(unsigned char&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ char>::read_num<unsigned short>(unsigned short&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ char>::read_num<unsigned int>(unsigned int&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ char>::read_num<unsigned long>(unsigned long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ char>::read_num<unsigned long long>(unsigned long long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<char>(char&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<wchar_t>(wchar_t&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<float>(float&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<double>(double&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<char>::read_num<long double>(long double&,
+ const string_type&,
+ int) const;
+
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<signed char>(signed char&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<short>(short&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<int>(int&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<long>(long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<long long>(long long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<unsigned char>(unsigned char&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<unsigned short>(unsigned short&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<unsigned int>(unsigned int&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<unsigned long>(unsigned long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<unsigned long long>(unsigned long long&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<float>(float&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<double>(double&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t> basic_custom_locale_ref<
+ wchar_t>::read_num<long double>(long double&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<char>(char&,
+ const string_type&,
+ int) const;
+ template expected<std::ptrdiff_t>
+ basic_custom_locale_ref<wchar_t>::read_num<wchar_t>(wchar_t&,
+ const string_type&,
+ int) const;
+#endif
+
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
diff --git a/src/third-party/scnlib/src/reader_float.cpp b/src/third-party/scnlib/src/reader_float.cpp
new file mode 100644
index 0000000..77c66a5
--- /dev/null
+++ b/src/third-party/scnlib/src/reader_float.cpp
@@ -0,0 +1,433 @@
+// 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
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY
+#define SCN_READER_FLOAT_CPP
+#endif
+
+#include <scn/detail/args.h>
+#include <scn/reader/float.h>
+
+#include <cerrno>
+#include <clocale>
+
+#if SCN_HAS_FLOAT_CHARCONV
+#include <charconv>
+#endif
+
+SCN_GCC_PUSH
+SCN_GCC_IGNORE("-Wold-style-cast")
+SCN_GCC_IGNORE("-Wnoexcept")
+SCN_GCC_IGNORE("-Wshift-count-overflow")
+SCN_GCC_IGNORE("-Wsign-conversion")
+
+SCN_CLANG_PUSH
+SCN_CLANG_IGNORE("-Wold-style-cast")
+
+#if SCN_CLANG >= SCN_COMPILER(13, 0, 0)
+SCN_CLANG_IGNORE("-Wreserved-identifier")
+#endif
+
+#if SCN_CLANG >= SCN_COMPILER(10, 0, 0)
+SCN_CLANG_IGNORE("-Wextra-semi-stmt")
+#endif
+
+#include <fast_float/fast_float.h>
+
+SCN_CLANG_POP
+SCN_GCC_POP
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace read_float {
+ static bool is_hexfloat(const char* str, std::size_t len) noexcept
+ {
+ if (len < 3) {
+ return false;
+ }
+ return str[0] == '0' && (str[1] == 'x' || str[1] == 'X');
+ }
+ static bool is_hexfloat(const wchar_t* str, std::size_t len) noexcept
+ {
+ if (len < 3) {
+ return false;
+ }
+ return str[0] == L'0' && (str[1] == L'x' || str[1] == L'X');
+ }
+
+ namespace cstd {
+#if SCN_GCC >= SCN_COMPILER(7, 0, 0)
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wnoexcept-type")
+#endif
+ template <typename T, typename CharT, typename F>
+ expected<T> impl(F&& f_strtod,
+ T huge_value,
+ const CharT* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ // Get current C locale
+ const auto loc = std::setlocale(LC_NUMERIC, nullptr);
+ // For whatever reason, this cannot be stored in the heap if
+ // setlocale hasn't been called before, or msan errors with
+ // 'use-of-unitialized-value' when resetting the locale back.
+ // POSIX specifies that the content of loc may not be static, so
+ // we need to save it ourselves
+ char locbuf[64] = {0};
+ std::strcpy(locbuf, loc);
+
+ std::setlocale(LC_NUMERIC, "C");
+
+ CharT* end{};
+ errno = 0;
+ T f = f_strtod(str, &end);
+ chars = static_cast<size_t>(end - str);
+ auto err = errno;
+ // Reset locale
+ std::setlocale(LC_NUMERIC, locbuf);
+ errno = 0;
+
+ SCN_GCC_COMPAT_PUSH
+ SCN_GCC_COMPAT_IGNORE("-Wfloat-equal")
+ // No conversion
+ if (f == detail::zero_value<T>::value && chars == 0) {
+ return error(error::invalid_scanned_value, "strtod");
+ }
+ // Range error
+ if (err == ERANGE) {
+ // Underflow
+ if (f == detail::zero_value<T>::value) {
+ return error(
+ error::value_out_of_range,
+ "Floating-point value out of range: underflow");
+ }
+ // Overflow
+ if (f == huge_value || f == -huge_value) {
+ return error(
+ error::value_out_of_range,
+ "Floating-point value out of range: overflow");
+ }
+ // Subnormals cause ERANGE but a value is still returned
+ }
+
+ if (is_hexfloat(str, detail::strlen(str)) &&
+ (options & detail::float_scanner<T>::allow_hex) == 0) {
+ return error(error::invalid_scanned_value,
+ "Hexfloats not allowed by the format string");
+ }
+
+ SCN_GCC_COMPAT_POP
+ return f;
+ }
+#if SCN_GCC >= SCN_COMPILER(7, 0, 0)
+ SCN_GCC_POP
+#endif
+
+ template <typename CharT, typename T>
+ struct read;
+
+ template <>
+ struct read<char, float> {
+ static expected<float> get(const char* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ return impl<float>(strtof, HUGE_VALF, str, chars, options);
+ }
+ };
+
+ template <>
+ struct read<char, double> {
+ static expected<double> get(const char* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ return impl<double>(strtod, HUGE_VAL, str, chars, options);
+ }
+ };
+
+ template <>
+ struct read<char, long double> {
+ static expected<long double> get(const char* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ return impl<long double>(strtold, HUGE_VALL, str, chars,
+ options);
+ }
+ };
+
+ template <>
+ struct read<wchar_t, float> {
+ static expected<float> get(const wchar_t* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ return impl<float>(wcstof, HUGE_VALF, str, chars, options);
+ }
+ };
+ template <>
+ struct read<wchar_t, double> {
+ static expected<double> get(const wchar_t* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ return impl<double>(wcstod, HUGE_VAL, str, chars, options);
+ }
+ };
+ template <>
+ struct read<wchar_t, long double> {
+ static expected<long double> get(const wchar_t* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ return impl<long double>(wcstold, HUGE_VALL, str, chars,
+ options);
+ }
+ };
+ } // namespace cstd
+
+ namespace from_chars {
+#if SCN_HAS_FLOAT_CHARCONV
+ template <typename T>
+ struct read {
+ static expected<T> get(const char* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ const auto len = std::strlen(str);
+ std::chars_format flags{};
+ if (((options & detail::float_scanner<T>::allow_hex) !=
+ 0) &&
+ is_hexfloat(str, len)) {
+ str += 2;
+ flags = std::chars_format::hex;
+ }
+ else {
+ if ((options & detail::float_scanner<T>::allow_fixed) !=
+ 0) {
+ flags |= std::chars_format::fixed;
+ }
+ if ((options &
+ detail::float_scanner<T>::allow_scientific) != 0) {
+ flags |= std::chars_format::scientific;
+ }
+ }
+ if (flags == static_cast<std::chars_format>(0)) {
+ return error{error::invalid_scanned_value,
+ "Expected a hexfloat"};
+ }
+
+ T value{};
+ const auto result =
+ std::from_chars(str, str + len, value, flags);
+ if (result.ec == std::errc::invalid_argument) {
+ return error(error::invalid_scanned_value,
+ "from_chars");
+ }
+ if (result.ec == std::errc::result_out_of_range) {
+ // Out of range, may be subnormal -> fall back to strtod
+ // On gcc std::from_chars doesn't parse subnormals
+ return cstd::read<char, T>::get(str, chars, options);
+ }
+ chars = static_cast<size_t>(result.ptr - str);
+ return value;
+ }
+ };
+#else
+ template <typename T>
+ struct read {
+ static expected<T> get(const char* str,
+ size_t& chars,
+ uint8_t options)
+ {
+ // Fall straight back to strtod
+ return cstd::read<char, T>::get(str, chars, options);
+ }
+ };
+#endif
+ } // namespace from_chars
+
+ namespace fast_float {
+ template <typename T>
+ expected<T> impl(const char* str,
+ size_t& chars,
+ uint8_t options,
+ char locale_decimal_point)
+ {
+ const auto len = std::strlen(str);
+ if (((options & detail::float_scanner<T>::allow_hex) != 0) &&
+ is_hexfloat(str, len)) {
+ // fast_float doesn't support hexfloats
+ return from_chars::read<T>::get(str, chars, options);
+ }
+
+ T value{};
+ ::fast_float::parse_options flags{};
+ if ((options & detail::float_scanner<T>::allow_fixed) != 0) {
+ flags.format = ::fast_float::fixed;
+ }
+ if ((options & detail::float_scanner<T>::allow_scientific) !=
+ 0) {
+ flags.format = static_cast<::fast_float::chars_format>(
+ flags.format | ::fast_float::scientific);
+ }
+ if ((options & detail::float_scanner<T>::localized) != 0) {
+ flags.decimal_point = locale_decimal_point;
+ }
+
+ const auto result = ::fast_float::from_chars_advanced(
+ str, str + len, value, flags);
+ if (result.ec == std::errc::invalid_argument) {
+ return error(error::invalid_scanned_value, "fast_float");
+ }
+ if (result.ec == std::errc::result_out_of_range) {
+ return error(error::value_out_of_range, "fast_float");
+ }
+ if (std::isinf(value)) {
+ // fast_float represents very large or small values as inf
+ // But, it also parses "inf", which from_chars does not
+ if (!(len >= 3 && (str[0] == 'i' || str[0] == 'I'))) {
+ // Input was not actually infinity ->
+ // invalid result, fall back to from_chars
+ return from_chars::read<T>::get(str, chars, options);
+ }
+ }
+ chars = static_cast<size_t>(result.ptr - str);
+ return value;
+ }
+
+ template <typename T>
+ struct read;
+
+ template <>
+ struct read<float> {
+ static expected<float> get(const char* str,
+ size_t& chars,
+ uint8_t options,
+ char locale_decimal_point)
+ {
+ return impl<float>(str, chars, options,
+ locale_decimal_point);
+ }
+ };
+ template <>
+ struct read<double> {
+ static expected<double> get(const char* str,
+ size_t& chars,
+ uint8_t options,
+ char locale_decimal_points)
+ {
+ return impl<double>(str, chars, options,
+ locale_decimal_points);
+ }
+ };
+ template <>
+ struct read<long double> {
+ static expected<long double> get(const char* str,
+ size_t& chars,
+ uint8_t options,
+ char)
+ {
+ // Fallback to strtod
+ // fast_float doesn't support long double
+ return cstd::read<char, long double>::get(str, chars,
+ options);
+ }
+ };
+ } // namespace fast_float
+
+ template <typename CharT, typename T>
+ struct read;
+
+ template <typename T>
+ struct read<char, T> {
+ static expected<T> get(const char* str,
+ size_t& chars,
+ uint8_t options,
+ char locale_decimal_points)
+ {
+ // char -> default to fast_float,
+ // fallback to strtod if necessary
+ return read_float::fast_float::read<T>::get(
+ str, chars, options, locale_decimal_points);
+ }
+ };
+ template <typename T>
+ struct read<wchar_t, T> {
+ static expected<T> get(const wchar_t* str,
+ size_t& chars,
+ uint8_t options,
+ wchar_t)
+ {
+ // wchar_t -> straight to strtod
+ return read_float::cstd::read<wchar_t, T>::get(str, chars,
+ options);
+ }
+ };
+ } // namespace read_float
+
+ namespace detail {
+ template <typename T>
+ template <typename CharT>
+ expected<T> float_scanner<T>::_read_float_impl(
+ const CharT* str,
+ size_t& chars,
+ CharT locale_decimal_point)
+ {
+ // Parsing algorithm to use:
+ // If CharT == wchar_t -> strtod
+ // If CharT == char:
+ // 1. fast_float
+ // fallback if a hex float, or incorrectly parsed an inf
+ // (very large or small value)
+ // 2. std::from_chars
+ // fallback if not available (C++17) or float is subnormal
+ // 3. std::strtod
+ return read_float::read<CharT, T>::get(str, chars, format_options,
+ locale_decimal_point);
+ }
+
+#if SCN_INCLUDE_SOURCE_DEFINITIONS
+
+ template expected<float>
+ float_scanner<float>::_read_float_impl(const char*, size_t&, char);
+ template expected<double>
+ float_scanner<double>::_read_float_impl(const char*, size_t&, char);
+ template expected<long double>
+ float_scanner<long double>::_read_float_impl(const char*,
+ size_t&,
+ char);
+ template expected<float> float_scanner<float>::_read_float_impl(
+ const wchar_t*,
+ size_t&,
+ wchar_t);
+ template expected<double> float_scanner<double>::_read_float_impl(
+ const wchar_t*,
+ size_t&,
+ wchar_t);
+ template expected<long double>
+ float_scanner<long double>::_read_float_impl(const wchar_t*,
+ size_t&,
+ wchar_t);
+#endif
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
diff --git a/src/third-party/scnlib/src/reader_int.cpp b/src/third-party/scnlib/src/reader_int.cpp
new file mode 100644
index 0000000..9b953d2
--- /dev/null
+++ b/src/third-party/scnlib/src/reader_int.cpp
@@ -0,0 +1,372 @@
+// 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
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY
+#define SCN_READER_INT_CPP
+#endif
+
+#include <scn/detail/args.h>
+#include <scn/reader/int.h>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+ namespace detail {
+ SCN_NODISCARD static unsigned char _char_to_int(char ch)
+ {
+ static constexpr unsigned char digits_arr[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255};
+ return digits_arr[static_cast<unsigned char>(ch)];
+ }
+ SCN_NODISCARD static unsigned char _char_to_int(wchar_t ch)
+ {
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wconversion")
+ if (ch >= std::numeric_limits<char>::min() &&
+ ch <= std::numeric_limits<char>::max()) {
+ return _char_to_int(static_cast<char>(ch));
+ }
+ return 255;
+ SCN_GCC_POP
+ }
+
+ template <typename T>
+ template <typename CharT>
+ expected<typename span<const CharT>::iterator>
+ integer_scanner<T>::parse_base_prefix(span<const CharT> s, int& b) const
+ {
+ auto it = s.begin();
+ // If base can be detected, it should start with '0'
+ if (*it == ascii_widen<CharT>('0')) {
+ ++it;
+ if (it == s.end()) {
+ // It's really just 0
+ // Return it to begininng, where '0' is
+ b = -1;
+ return it;
+ }
+ if (*it == ascii_widen<CharT>('x') ||
+ *it == ascii_widen<CharT>('X')) {
+ // Hex: 0x or 0X
+ ++it;
+ if (SCN_UNLIKELY(it == s.end())) {
+ // 0x/0X not a valid number
+ b = -1;
+ return --it;
+ }
+ if (b == 0) {
+ // Detect base
+ b = 16;
+ }
+ else if (b != 16) {
+ // Invalid prefix for base
+ return error(error::invalid_scanned_value,
+ "Invalid base prefix");
+ }
+ }
+ else if (*it == ascii_widen<CharT>('b') ||
+ *it == ascii_widen<CharT>('B')) {
+ // Binary: 0b or 0b
+ ++it;
+ if (SCN_UNLIKELY(it == s.end())) {
+ // 0b/0B not a valid number
+ b = -1;
+ return --it;
+ }
+ if (b == 0) {
+ // Detect base
+ b = 2;
+ }
+ else if (b != 2) {
+ // Invalid prefix for base
+ return error(error::invalid_scanned_value,
+ "Invalid base prefix");
+ }
+ }
+ else if (*it == ascii_widen<CharT>('o') ||
+ *it == ascii_widen<CharT>('O')) {
+ // Octal: 0o or 0O
+ ++it;
+ if (SCN_UNLIKELY(it == s.end())) {
+ // 0o/0O not a valid number
+ b = -1;
+ return --it;
+ }
+ if (b == 0) {
+ // Detect base
+ b = 8;
+ }
+ else if (b != 8) {
+ // Invalid prefix for base
+ return error(error::invalid_scanned_value,
+ "Invalid base prefix");
+ }
+ }
+ else if (b == 0) {
+ // Starting with only 0 -> octal
+ b = 8;
+ }
+ }
+ if (b == 0) {
+ // None detected, default to 10
+ b = 10;
+ }
+ return it;
+ }
+
+ template <typename T>
+ template <typename CharT>
+ expected<std::ptrdiff_t> integer_scanner<T>::_parse_int(
+ T& val,
+ span<const CharT> s)
+ {
+ SCN_EXPECT(s.size() > 0);
+
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4244)
+ SCN_MSVC_IGNORE(4127) // conditional expression is constant
+
+ if (std::is_unsigned<T>::value) {
+ if (s[0] == detail::ascii_widen<CharT>('-')) {
+ return error(error::invalid_scanned_value,
+ "Unexpected sign '-' when scanning an "
+ "unsigned integer");
+ }
+ }
+
+ SCN_MSVC_POP
+
+ T tmp = 0;
+ bool minus_sign = false;
+ auto it = s.begin();
+
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wsign-conversion")
+ if (s[0] == ascii_widen<CharT>('-')) {
+ if (SCN_UNLIKELY((format_options & only_unsigned) != 0)) {
+ // 'u' option -> negative values disallowed
+ return error(error::invalid_scanned_value,
+ "Parsed negative value when type was 'u'");
+ }
+ minus_sign = true;
+ ++it;
+ }
+ else if (s[0] == ascii_widen<CharT>('+')) {
+ ++it;
+ }
+ SCN_GCC_POP
+ if (SCN_UNLIKELY(it == s.end())) {
+ return error(error::invalid_scanned_value,
+ "Expected number after sign");
+ }
+
+ // Format string was 'i' or empty -> detect base
+ // or
+ // allow_base_prefix (skip 0x etc.)
+ if (SCN_UNLIKELY(base == 0 ||
+ (format_options & allow_base_prefix) != 0)) {
+ int b{base};
+ auto r = parse_base_prefix<CharT>({it, s.end()}, b);
+ if (!r) {
+ return r.error();
+ }
+ if (b == -1) {
+ // -1 means we read a '0'
+ val = 0;
+ return ranges::distance(s.begin(), r.value());
+ }
+ if (b != 10 && base != b && base != 0) {
+ return error(error::invalid_scanned_value,
+ "Invalid base prefix");
+ }
+ if (base == 0) {
+ base = static_cast<uint8_t>(b);
+ }
+ it = r.value();
+ }
+
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wconversion")
+ SCN_GCC_IGNORE("-Wsign-conversion")
+ SCN_GCC_IGNORE("-Wsign-compare")
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wconversion")
+ SCN_CLANG_IGNORE("-Wsign-conversion")
+ SCN_CLANG_IGNORE("-Wsign-compare")
+
+ SCN_ASSUME(base > 0);
+
+ SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
+ auto r = _parse_int_impl(tmp, minus_sign, make_span(it, s.end()));
+ SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
+ if (!r) {
+ return r.error();
+ }
+ it = r.value();
+ if (s.begin() == it) {
+ return error(error::invalid_scanned_value, "custom::read_int");
+ }
+ val = tmp;
+ return ranges::distance(s.begin(), it);
+
+ SCN_CLANG_POP
+ SCN_GCC_POP
+ }
+
+ template <typename T>
+ template <typename CharT>
+ expected<typename span<const CharT>::iterator>
+ integer_scanner<T>::_parse_int_impl(T& val,
+ bool minus_sign,
+ span<const CharT> buf) const
+ {
+ SCN_GCC_PUSH
+ SCN_GCC_IGNORE("-Wconversion")
+ SCN_GCC_IGNORE("-Wsign-conversion")
+ SCN_GCC_IGNORE("-Wsign-compare")
+
+ SCN_CLANG_PUSH
+ SCN_CLANG_IGNORE("-Wconversion")
+ SCN_CLANG_IGNORE("-Wsign-conversion")
+ SCN_CLANG_IGNORE("-Wsign-compare")
+
+ SCN_MSVC_PUSH
+ SCN_MSVC_IGNORE(4018) // > signed/unsigned mismatch
+ SCN_MSVC_IGNORE(4389) // == signed/unsigned mismatch
+ SCN_MSVC_IGNORE(4244) // lossy conversion
+
+ using utype = typename std::make_unsigned<T>::type;
+
+ const auto ubase = static_cast<utype>(base);
+ SCN_ASSUME(ubase > 0);
+
+ constexpr auto uint_max = static_cast<utype>(-1);
+ constexpr auto int_max = static_cast<utype>(uint_max >> 1);
+ constexpr auto abs_int_min = static_cast<utype>(int_max + 1);
+
+ const auto cut = div(
+ [&]() -> utype {
+ if (std::is_signed<T>::value) {
+ if (minus_sign) {
+ return abs_int_min;
+ }
+ return int_max;
+ }
+ return uint_max;
+ }(),
+ ubase);
+ const auto cutoff = cut.first;
+ const auto cutlim = cut.second;
+
+ auto it = buf.begin();
+ const auto end = buf.end();
+ utype tmp = 0;
+ for (; it != end; ++it) {
+ const auto digit = _char_to_int(*it);
+ if (digit >= ubase) {
+ break;
+ }
+ if (SCN_UNLIKELY(tmp > cutoff ||
+ (tmp == cutoff && digit > cutlim))) {
+ if (!minus_sign) {
+ return error(error::value_out_of_range,
+ "Out of range: integer overflow");
+ }
+ return error(error::value_out_of_range,
+ "Out of range: integer underflow");
+ }
+ tmp = tmp * ubase + digit;
+ }
+ if (minus_sign) {
+ // special case: signed int minimum's absolute value can't
+ // be represented with the same type
+ //
+ // For example, short int -- range is [-32768, 32767], 32768
+ // can't be represented
+ //
+ // In that case, -static_cast<T>(tmp) would trigger UB
+ if (SCN_UNLIKELY(tmp == abs_int_min)) {
+ val = std::numeric_limits<T>::min();
+ }
+ else {
+ val = -static_cast<T>(tmp);
+ }
+ }
+ else {
+ val = static_cast<T>(tmp);
+ }
+ return it;
+
+ SCN_MSVC_POP
+ SCN_CLANG_POP
+ SCN_GCC_POP
+ }
+
+#if SCN_INCLUDE_SOURCE_DEFINITIONS
+
+#define SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(CharT, T) \
+ template expected<std::ptrdiff_t> integer_scanner<T>::_parse_int( \
+ T& val, span<const CharT> s); \
+ template expected<typename span<const CharT>::iterator> \
+ integer_scanner<T>::_parse_int_impl(T& val, bool minus_sign, \
+ span<const CharT> buf) const; \
+ template expected<typename span<const CharT>::iterator> \
+ integer_scanner<T>::parse_base_prefix(span<const CharT>, int&) const;
+
+#define SCN_DEFINE_INTEGER_SCANNER_MEMBERS(Char) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, signed char) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, short) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, int) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, long) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, long long) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, unsigned char) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, unsigned short) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, unsigned int) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, unsigned long) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, unsigned long long) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, char) \
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS_IMPL(Char, wchar_t)
+
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS(char)
+ SCN_DEFINE_INTEGER_SCANNER_MEMBERS(wchar_t)
+
+#endif
+
+ } // namespace detail
+
+ SCN_END_NAMESPACE
+} // namespace scn
diff --git a/src/third-party/scnlib/src/vscan.cpp b/src/third-party/scnlib/src/vscan.cpp
new file mode 100644
index 0000000..7365773
--- /dev/null
+++ b/src/third-party/scnlib/src/vscan.cpp
@@ -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
+
+#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY
+#define SCN_VSCAN_CPP
+#endif
+
+#include <scn/scan/vscan.h>
+
+#include <scn/detail/context.h>
+#include <scn/detail/parse_context.h>
+#include <scn/detail/visitor.h>
+
+namespace scn {
+ SCN_BEGIN_NAMESPACE
+
+#if SCN_INCLUDE_SOURCE_DEFINITIONS
+
+#define SCN_VSCAN_DEFINE(Range, WrappedAlias, CharAlias) \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan( \
+ detail::vscan_macro::WrappedAlias&& range, \
+ basic_string_view<detail::vscan_macro::CharAlias> fmt, \
+ basic_args<detail::vscan_macro::CharAlias>&& args) \
+ { \
+ return detail::vscan_boilerplate(SCN_MOVE(range), fmt, \
+ SCN_MOVE(args)); \
+ } \
+ \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan_default( \
+ detail::vscan_macro::WrappedAlias&& range, int n_args, \
+ basic_args<detail::vscan_macro::CharAlias>&& args) \
+ { \
+ return detail::vscan_boilerplate_default(SCN_MOVE(range), n_args, \
+ SCN_MOVE(args)); \
+ } \
+ \
+ vscan_result<detail::vscan_macro::WrappedAlias> vscan_localized( \
+ detail::vscan_macro::WrappedAlias&& range, \
+ basic_locale_ref<detail::vscan_macro::CharAlias>&& loc, \
+ basic_string_view<detail::vscan_macro::CharAlias> fmt, \
+ basic_args<detail::vscan_macro::CharAlias>&& args) \
+ { \
+ return detail::vscan_boilerplate_localized( \
+ SCN_MOVE(range), SCN_MOVE(loc), fmt, SCN_MOVE(args)); \
+ } \
+ \
+ error vscan_usertype( \
+ basic_context<detail::vscan_macro::WrappedAlias>& ctx, \
+ basic_string_view<detail::vscan_macro::CharAlias> f, \
+ basic_args<detail::vscan_macro::CharAlias>&& args) \
+ { \
+ auto pctx = make_parse_context(f, ctx.locale()); \
+ return visit(ctx, pctx, SCN_MOVE(args)); \
+ }
+
+ SCN_VSCAN_DEFINE(string_view, string_view_wrapped, string_view_char)
+ SCN_VSCAN_DEFINE(wstring_view, wstring_view_wrapped, wstring_view_char)
+ SCN_VSCAN_DEFINE(std::string, string_wrapped, string_char)
+ SCN_VSCAN_DEFINE(std::wstring, wstring_wrapped, wstring_char)
+ SCN_VSCAN_DEFINE(file&, file_ref_wrapped, file_ref_char)
+ SCN_VSCAN_DEFINE(wfile&, wfile_ref_wrapped, wfile_ref_char)
+
+#endif
+
+ SCN_END_NAMESPACE
+} // namespace scn