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