diff options
Diffstat (limited to '')
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 |