// filesystem standard header

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef _FILESYSTEM_
#define _FILESYSTEM_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR

#if !_HAS_CXX17
_EMIT_STL_WARNING(STL4038, "The contents of <filesystem> are available only with C++17 or later.");
#else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv
#include <algorithm>
#include <chrono>
#include <cwchar>
#include <iomanip>
#include <locale>
#include <memory>
#include <system_error>
#include <utility>
#include <vector>
#include <xfilesystem_abi.h>
#include <xstring>

#if _HAS_CXX20
#include <compare>
#endif // _HAS_CXX20

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new

_STD_BEGIN
namespace filesystem {
    _NODISCARD inline wstring _Convert_narrow_to_wide(const __std_code_page _Code_page, const string_view _Input) {
        wstring _Output;

        if (!_Input.empty()) {
            if (!_STD _In_range<int>(_Input.size())) {
                _Throw_system_error(errc::invalid_argument);
            }

            const int _Len = _Check_convert_result(__std_fs_convert_narrow_to_wide(
                _Code_page, _Input.data(), static_cast<int>(_Input.size()), nullptr, 0));

            _Output.resize(static_cast<size_t>(_Len));

            (void) _Check_convert_result(__std_fs_convert_narrow_to_wide(
                _Code_page, _Input.data(), static_cast<int>(_Input.size()), _Output.data(), _Len));
        }

        return _Output;
    }

    // More lenient version of _Convert_wide_to_narrow: Instead of failing on non-representable characters,
    // replace them with a replacement character.
    template <class _Traits, class _Alloc>
    _NODISCARD basic_string<typename _Traits::char_type, _Traits, _Alloc> _Convert_wide_to_narrow_replace_chars(
        const __std_code_page _Code_page, const wstring_view _Input, const _Alloc& _Al) {
        basic_string<typename _Traits::char_type, _Traits, _Alloc> _Output(_Al);

        if (!_Input.empty()) {
            if (!_STD _In_range<int>(_Input.size())) {
                _Throw_system_error(errc::invalid_argument);
            }

            const int _Len = _Check_convert_result(__std_fs_convert_wide_to_narrow_replace_chars(
                _Code_page, _Input.data(), static_cast<int>(_Input.size()), nullptr, 0));

            _Output.resize(static_cast<size_t>(_Len));

            const auto _Data_as_char = reinterpret_cast<char*>(_Output.data());

            (void) _Check_convert_result(__std_fs_convert_wide_to_narrow_replace_chars(
                _Code_page, _Input.data(), static_cast<int>(_Input.size()), _Data_as_char, _Len));
        }

        return _Output;
    }

    _NODISCARD inline wstring _Convert_utf32_to_wide(const u32string_view _Input) {
        wstring _Output;

        _Output.reserve(_Input.size()); // ideal when surrogate pairs are uncommon

        for (const auto& _Code_point : _Input) {
            if (_Code_point <= 0xD7FFU) {
                _Output.push_back(static_cast<wchar_t>(_Code_point));
            } else if (_Code_point <= 0xDFFFU) {
                _Throw_system_error(errc::invalid_argument);
            } else if (_Code_point <= 0xFFFFU) {
                _Output.push_back(static_cast<wchar_t>(_Code_point));
            } else if (_Code_point <= 0x10FFFFU) {
                _Output.push_back(static_cast<wchar_t>(0xD7C0U + (_Code_point >> 10)));
                _Output.push_back(static_cast<wchar_t>(0xDC00U + (_Code_point & 0x3FFU)));
            } else {
                _Throw_system_error(errc::invalid_argument);
            }
        }

        return _Output;
    }

    template <class _Traits, class _Alloc>
    _NODISCARD basic_string<char32_t, _Traits, _Alloc> _Convert_wide_to_utf32(
        const wstring_view _Input, const _Alloc& _Al) {
        basic_string<char32_t, _Traits, _Alloc> _Output(_Al);

        _Output.reserve(_Input.size()); // ideal when surrogate pairs are uncommon

        const wchar_t* _First      = _Input.data();
        const wchar_t* const _Last = _First + _Input.size();

        for (; _First != _Last; ++_First) {
            if (*_First <= 0xD7FFU) {
                _Output.push_back(*_First);
            } else if (*_First <= 0xDBFFU) { // found leading surrogate
                const char32_t _Leading = *_First; // widen for later math

                ++_First;

                if (_First == _Last) { // missing trailing surrogate
                    _Throw_system_error(errc::invalid_argument);
                }

                const char32_t _Trailing = *_First; // widen for later math

                if (0xDC00U <= _Trailing && _Trailing <= 0xDFFFU) { // valid trailing surrogate
                    _Output.push_back(0xFCA02400U + (_Leading << 10) + _Trailing);
                } else { // invalid trailing surrogate
                    _Throw_system_error(errc::invalid_argument);
                }
            } else if (*_First <= 0xDFFFU) { // found trailing surrogate by itself, invalid
                _Throw_system_error(errc::invalid_argument);
            } else {
                _Output.push_back(*_First);
            }
        }

        return _Output;
    }

    template <class _Traits, class _Alloc, class _EcharT = typename _Traits::char_type>
    _NODISCARD basic_string<_EcharT, _Traits, _Alloc> _Convert_wide_to(const wstring_view _Input, const _Alloc& _Al) {
        if constexpr (is_same_v<_EcharT, char>) {
            return _Convert_wide_to_narrow<_Traits>(__std_fs_code_page(), _Input, _Al);
        }
#ifdef __cpp_char8_t
        else if constexpr (is_same_v<_EcharT, char8_t>) {
            return _Convert_wide_to_narrow<_Traits>(__std_code_page::_Utf8, _Input, _Al);
        }
#endif // defined(__cpp_char8_t)
        else if constexpr (is_same_v<_EcharT, char32_t>) {
            return _Convert_wide_to_utf32<_Traits>(_Input, _Al);
        } else { // wchar_t, char16_t
            return basic_string<_EcharT, _Traits, _Alloc>(_Input.data(), _Input.data() + _Input.size(), _Al);
        }
    }

    template <class _Ty, class = void>
    constexpr bool _Is_Source_impl = false;

    template <class _Ty>
    constexpr bool _Is_Source_impl<_Ty, void_t<typename iterator_traits<_Ty>::value_type>> =
        _Is_EcharT<typename iterator_traits<_Ty>::value_type>;

    template <class _Ty>
    constexpr bool _Is_Source = _Is_Source_impl<decay_t<_Ty>>;

    _EXPORT_STD class path;

    template <>
    inline constexpr bool _Is_Source<path> = false; // to avoid constraint recursion via the converting constructor and
                                                    // iterator_traits when determining if path is copyable.

    template <class _Elem, class _Traits, class _Alloc>
    constexpr bool _Is_Source<basic_string<_Elem, _Traits, _Alloc>> = _Is_EcharT<_Elem>;

    template <class _Elem, class _Traits>
    constexpr bool _Is_Source<basic_string_view<_Elem, _Traits>> = _Is_EcharT<_Elem>;

    struct _Normal_conversion {};

    struct _Utf8_conversion {};

    // A "stringoid" is basic_string_view<_EcharT> or basic_string<_EcharT>.

    template <class _Conversion>
    _NODISCARD wstring _Convert_stringoid_to_wide(const string_view _Input, _Conversion) {
        _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Conversion, _Normal_conversion, _Utf8_conversion>);

        if constexpr (is_same_v<_Conversion, _Normal_conversion>) {
            return _Convert_narrow_to_wide(__std_fs_code_page(), _Input);
        } else {
            return _Convert_narrow_to_wide(__std_code_page::_Utf8, _Input);
        }
    }

    template <class _Conversion>
    _NODISCARD wstring _Convert_stringoid_to_wide(const wstring_view _Input, _Conversion) {
        static_assert(
            is_same_v<_Conversion, _Normal_conversion>, "invalid value_type, see N4950 [depr.fs.path.factory]/1");
        return wstring{_Input};
    }

#ifdef __cpp_char8_t
    template <class _Conversion>
    _NODISCARD wstring _Convert_stringoid_to_wide(const basic_string_view<char8_t> _Input, _Conversion) {
        _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Conversion, _Normal_conversion, _Utf8_conversion>);

        const string_view _Input_as_char{reinterpret_cast<const char*>(_Input.data()), _Input.size()};
        return _Convert_narrow_to_wide(__std_code_page::_Utf8, _Input_as_char);
    }
#endif // defined(__cpp_char8_t)

    template <class _Conversion>
    _NODISCARD wstring _Convert_stringoid_to_wide(const u16string_view _Input, _Conversion) {
        static_assert(
            is_same_v<_Conversion, _Normal_conversion>, "invalid value_type, see N4950 [depr.fs.path.factory]/1");
        return wstring{_Input.data(), _Input.data() + _Input.size()};
    }

    template <class _Conversion>
    _NODISCARD wstring _Convert_stringoid_to_wide(const u32string_view _Input, _Conversion) {
        static_assert(
            is_same_v<_Conversion, _Normal_conversion>, "invalid value_type, see N4950 [depr.fs.path.factory]/1");
        return _Convert_utf32_to_wide(_Input);
    }

    template <class _EcharT, class _Traits>
    _NODISCARD auto _Stringoid_from_Source(const basic_string_view<_EcharT, _Traits>& _Source) {
        return basic_string_view<_EcharT>(_Source.data(), _Source.size()); // erase mismatching _Traits
    }

    template <class _EcharT, class _Traits, class _Alloc>
    _NODISCARD auto _Stringoid_from_Source(const basic_string<_EcharT, _Traits, _Alloc>& _Source) {
        return basic_string_view<_EcharT>(_Source.data(), _Source.size()); // erase mismatching _Traits
    }

    template <class _Src>
    _NODISCARD auto _Stringoid_from_Source(const _Src& _Source) {
        using _EcharT = _Iter_value_t<decay_t<_Src>>;
        if constexpr (is_pointer_v<_Unwrapped_unverified_t<const _Src&>>) {
            return basic_string_view<_EcharT>(_Get_unwrapped_unverified(_Source));
        } else if constexpr (is_pointer_v<_Unwrapped_t<const _Src&>>) {
            const auto _Data = _Get_unwrapped(_Source);
            auto _Next       = _Source;
            while (*_Next != _EcharT{}) {
                ++_Next;
            }

            return basic_string_view<_EcharT>(_Data, static_cast<size_t>(_Get_unwrapped(_Next) - _Data));
        } else {
            basic_string<_EcharT> _Str;
            for (auto _Next = _Source; *_Next != _EcharT{}; ++_Next) {
                _Str.push_back(*_Next);
            }

            return _Str;
        }
    }

#if _ITERATOR_DEBUG_LEVEL == 2
    template <class _EcharT, size_t _SourceSize>
    _NODISCARD basic_string_view<_EcharT> _Stringoid_from_Source(const _EcharT (&_Src)[_SourceSize]) {
        for (size_t _Idx = 0;; ++_Idx) {
            _STL_VERIFY(_Idx < _SourceSize, "path input not null terminated");
            if (_Src[_Idx] == _EcharT{}) {
                return basic_string_view<_EcharT>(_Src, _Idx);
            }
        }
    }
#endif // _ITERATOR_DEBUG_LEVEL == 2

    template <class _Src, class _Conversion = _Normal_conversion>
    _NODISCARD wstring _Convert_Source_to_wide(const _Src& _Source, _Conversion _Tag = {}) {
        return _Convert_stringoid_to_wide(_Stringoid_from_Source(_Source), _Tag);
    }

    template <class _InIt>
    _NODISCARD auto _Stringoid_from_range(_InIt _First, _InIt _Last) {
        _Adl_verify_range(_First, _Last);
        const auto _UFirst = _Get_unwrapped(_First);
        const auto _ULast  = _Get_unwrapped(_Last);

        if constexpr (is_pointer_v<decltype(_UFirst)>) {
            return basic_string_view<_Iter_value_t<_InIt>>(_UFirst, static_cast<size_t>(_ULast - _UFirst));
        } else {
            return basic_string<_Iter_value_t<_InIt>>(_UFirst, _ULast);
        }
    }

    template <class _InIt, class _Conversion = _Normal_conversion>
    _NODISCARD wstring _Convert_range_to_wide(_InIt _First, _InIt _Last, _Conversion _Tag = {}) {
        return _Convert_stringoid_to_wide(_Stringoid_from_range(_First, _Last), _Tag);
    }

    _NODISCARD inline wstring _Convert_stringoid_with_locale_to_wide(const string_view _Input, const locale& _Loc) {
        const auto& _Facet = _STD use_facet<codecvt<wchar_t, char, mbstate_t>>(_Loc);

        wstring _Output(_Input.size(), L'\0'); // almost always sufficient

        for (;;) {
            mbstate_t _State{};
            const char* const _From_begin = _Input.data();
            const char* const _From_end   = _From_begin + _Input.size();
            const char* _From_next        = nullptr;
            wchar_t* const _To_begin      = _Output.data();
            wchar_t* const _To_end        = _To_begin + _Output.size();
            wchar_t* _To_next             = nullptr;

            const auto _Result = _Facet.in(_State, _From_begin, _From_end, _From_next, _To_begin, _To_end, _To_next);

            if (_From_next < _From_begin || _From_next > _From_end || _To_next < _To_begin || _To_next > _To_end) {
                _Throw_system_error(errc::invalid_argument);
            }

            switch (_Result) {
            case codecvt_base::ok:
                _Output.resize(static_cast<size_t>(_To_next - _To_begin));
                return _Output;

            case codecvt_base::partial:
                // N4950 [locale.codecvt.virtuals]/5:
                // "A return value of partial, if (from_next == from_end), indicates that either the
                // destination sequence has not absorbed all the available destination elements,
                // or that additional source elements are needed before another destination element can be produced."
                if ((_From_next == _From_end && _To_next != _To_end) || _Output.size() > _Output.max_size() / 2) {
                    _Throw_system_error(errc::invalid_argument);
                }

                _Output.resize(_Output.size() * 2);
                break; // out of switch, keep looping

            case codecvt_base::error:
            case codecvt_base::noconv:
            default:
                _Throw_system_error(errc::invalid_argument);
            }
        }
    }

    struct _Is_slash_oper { // predicate testing if input is a preferred-separator or fallback-separator
        _NODISCARD _STATIC_CALL_OPERATOR constexpr bool operator()(const wchar_t _Ch) _CONST_CALL_OPERATOR {
            return _Ch == L'\\' || _Ch == L'/';
        }
    };

    inline constexpr _Is_slash_oper _Is_slash{};

    _NODISCARD inline bool _Is_drive_prefix(const wchar_t* const _First) {
        // test if _First points to a prefix of the form X:
        // pre: _First points to at least 2 wchar_t instances
        // pre: Little endian
        unsigned int _Value;
        _CSTD memcpy(&_Value, _First, sizeof(_Value)); // load from possibly unaligned address
        _Value &= 0xFFFF'FFDFu; // transform lowercase drive letters into uppercase ones
        _Value -= (static_cast<unsigned int>(L':') << (sizeof(wchar_t) * CHAR_BIT)) | L'A';
        return _Value < 26;
    }

    _NODISCARD inline bool _Has_drive_letter_prefix(const wchar_t* const _First, const wchar_t* const _Last) {
        // test if [_First, _Last) has a prefix of the form X:
        return _Last - _First >= 2 && _Is_drive_prefix(_First);
    }

    _NODISCARD inline const wchar_t* _Find_root_name_end(const wchar_t* const _First, const wchar_t* const _Last) {
        // attempt to parse [_First, _Last) as a path and return the end of root-name if it exists; otherwise, _First

        // This is the place in the generic grammar where library implementations have the most freedom.
        // Below are example Windows paths, and what we've decided to do with them:
        // * X:DriveRelative, X:\DosAbsolute
        //   We parse X: as root-name, if and only if \ is present we consider that root-directory
        // * \RootRelative
        //   We parse no root-name, and \ as root-directory
        // * \\server\share
        //   We parse \\server as root-name, \ as root-directory, and share as the first element in relative-path.
        //   Technically, Windows considers all of \\server\share the logical "root", but for purposes
        //   of decomposition we want those split, so that path{R"(\\server\share)"}.replace_filename("other_share")
        //   is \\server\other_share
        // * \\?\device
        // * \??\device
        // * \\.\device
        //   CreateFile appears to treat these as the same thing; we will set the first three characters as root-name
        //   and the first \ as root-directory. Support for these prefixes varies by particular Windows version, but
        //   for the purposes of path decomposition we don't need to worry about that.
        // * \\?\UNC\server\share
        //   MSDN explicitly documents the \\?\UNC syntax as a special case. What actually happens is that the device
        //   Mup, or "Multiple UNC provider", owns the path \\?\UNC in the NT namespace, and is responsible for the
        //   network file access. When the user says \\server\share, CreateFile translates that into
        //   \\?\UNC\server\share to get the remote server access behavior. Because NT treats this like any other
        //   device, we have chosen to treat this as the \\?\ case above.
        if (_Last - _First < 2) {
            return _First;
        }

        if (_Has_drive_letter_prefix(_First, _Last)) { // check for X: first because it's the most common root-name
            return _First + 2;
        }

        if (!_Is_slash(_First[0])) { // all the other root-names start with a slash; check that first because
                                     // we expect paths without a leading slash to be very common
            return _First;
        }

        // $ means anything other than a slash, including potentially the end of the input
        if (_Last - _First >= 4 && _Is_slash(_First[3]) && (_Last - _First == 4 || !_Is_slash(_First[4])) // \xx\$
            && ((_Is_slash(_First[1]) && (_First[2] == L'?' || _First[2] == L'.')) // \\?\$ or \\.\$
                || (_First[1] == L'?' && _First[2] == L'?'))) { // \??\$
            return _First + 3;
        }

        if (_Last - _First >= 3 && _Is_slash(_First[1]) && !_Is_slash(_First[2])) { // \\server
            return _STD find_if(_First + 3, _Last, _Is_slash);
        }

        // no match
        return _First;
    }

    _NODISCARD inline wstring_view _Parse_root_name(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the root-name if it exists; otherwise, an empty view
        const auto _First = _Str.data();
        const auto _Last  = _First + _Str.size();
        return wstring_view(_First, static_cast<size_t>(_Find_root_name_end(_First, _Last) - _First));
    }

    _NODISCARD inline const wchar_t* _Find_relative_path(const wchar_t* const _First, const wchar_t* const _Last) {
        // attempt to parse [_First, _Last) as a path and return the start of relative-path
        return _STD find_if_not(_Find_root_name_end(_First, _Last), _Last, _Is_slash);
    }

    _NODISCARD inline wstring_view _Parse_root_directory(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the root-directory if it exists; otherwise, an empty view
        const auto _First         = _Str.data();
        const auto _Last          = _First + _Str.size();
        const auto _Root_name_end = _Find_root_name_end(_First, _Last);
        const auto _Relative_path = _STD find_if_not(_Root_name_end, _Last, _Is_slash);
        return wstring_view(_Root_name_end, static_cast<size_t>(_Relative_path - _Root_name_end));
    }

    _NODISCARD inline wstring_view _Parse_root_path(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the root-path if it exists; otherwise, an empty view
        const auto _First = _Str.data();
        const auto _Last  = _First + _Str.size();
        return wstring_view(_First, static_cast<size_t>(_Find_relative_path(_First, _Last) - _First));
    }

    _NODISCARD inline wstring_view _Parse_relative_path(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the relative-path if it exists; otherwise, an empty view
        const auto _First         = _Str.data();
        const auto _Last          = _First + _Str.size();
        const auto _Relative_path = _Find_relative_path(_First, _Last);
        return wstring_view(_Relative_path, static_cast<size_t>(_Last - _Relative_path));
    }

    _NODISCARD inline wstring_view _Parse_parent_path(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the parent_path if it exists; otherwise, an empty view
        const auto _First         = _Str.data();
        auto _Last                = _First + _Str.size();
        const auto _Relative_path = _Find_relative_path(_First, _Last);
        // case 1: relative-path ends in a directory-separator, remove the separator to remove "magic empty path"
        //  for example: R"(/cat/dog/\//\)"
        // case 2: relative-path doesn't end in a directory-separator, remove the filename and last directory-separator
        //  to prevent creation of a "magic empty path"
        //  for example: "/cat/dog"
        while (_Relative_path != _Last && !_Is_slash(_Last[-1])) {
            // handle case 2 by removing trailing filename, puts us into case 1
            --_Last;
        }

        while (_Relative_path != _Last && _Is_slash(_Last[-1])) { // handle case 1 by removing trailing slashes
            --_Last;
        }

        return wstring_view(_First, static_cast<size_t>(_Last - _First));
    }

    _NODISCARD inline const wchar_t* _Find_filename(const wchar_t* const _First, const wchar_t* _Last) {
        // attempt to parse [_First, _Last) as a path and return the start of filename if it exists; otherwise, _Last
        const auto _Relative_path = _Find_relative_path(_First, _Last);
        while (_Relative_path != _Last && !_Is_slash(_Last[-1])) {
            --_Last;
        }

        return _Last;
    }

    _NODISCARD inline wstring_view _Parse_filename(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the filename if it exists; otherwise, an empty view
        const auto _First    = _Str.data();
        const auto _Last     = _First + _Str.size();
        const auto _Filename = _Find_filename(_First, _Last);
        return wstring_view(_Filename, static_cast<size_t>(_Last - _Filename));
    }

    _NODISCARD constexpr const wchar_t* _Find_extension(const wchar_t* const _Filename, const wchar_t* const _Ads) {
        // find dividing point between stem and extension in a generic format filename consisting of [_Filename, _Ads)
        auto _Extension = _Ads;
        if (_Filename == _Extension) { // empty path
            return _Ads;
        }

        --_Extension;
        if (_Filename == _Extension) {
            // path is length 1 and either dot, or has no dots; either way, extension() is empty
            return _Ads;
        }

        if (*_Extension == L'.') { // we might have found the end of stem
            if (_Filename == _Extension - 1 && _Extension[-1] == L'.') { // dotdot special case
                return _Ads;
            } else { // x.
                return _Extension;
            }
        }

        while (_Filename != --_Extension) {
            if (*_Extension == L'.') { // found a dot which is not in first position, so it starts extension()
                return _Extension;
            }
        }

        // if we got here, either there are no dots, in which case extension is empty, or the first element
        // is a dot, in which case we have the leading single dot special case, which also makes extension empty
        return _Ads;
    }

    _NODISCARD inline wstring_view _Parse_stem(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the stem if it exists; otherwise, an empty view
        const auto _First    = _Str.data();
        const auto _Last     = _First + _Str.size();
        const auto _Filename = _Find_filename(_First, _Last);
        const auto _Ads =
            _STD find(_Filename, _Last, L':'); // strip alternate data streams in intra-filename decomposition
        const auto _Extension = _Find_extension(_Filename, _Ads);
        return wstring_view(_Filename, static_cast<size_t>(_Extension - _Filename));
    }

    _NODISCARD inline wstring_view _Parse_extension(const wstring_view _Str) {
        // attempt to parse _Str as a path and return the extension if it exists; otherwise, an empty view
        const auto _First    = _Str.data();
        const auto _Last     = _First + _Str.size();
        const auto _Filename = _Find_filename(_First, _Last);
        const auto _Ads =
            _STD find(_Filename, _Last, L':'); // strip alternate data streams in intra-filename decomposition
        const auto _Extension = _Find_extension(_Filename, _Ads);
        return wstring_view(_Extension, static_cast<size_t>(_Ads - _Extension));
    }

    _NODISCARD inline int _Range_compare(const wchar_t* const _Lfirst, const wchar_t* const _Llast,
        const wchar_t* const _Rfirst, const wchar_t* const _Rlast) {
        // 3 way compare [_Lfirst, _Llast) with [_Rfirst, _Rlast)
        return _Traits_compare<char_traits<wchar_t>>(
            _Lfirst, static_cast<size_t>(_Llast - _Lfirst), _Rfirst, static_cast<size_t>(_Rlast - _Rfirst));
    }

    _NODISCARD inline bool _Is_drive_prefix_with_slash_slash_question(const wstring_view _Text) {
        // test if _Text starts with a \\?\X: prefix
        return _Text.size() >= 6 && _Text._Starts_with(LR"(\\?\)"sv) && _Is_drive_prefix(_Text.data() + 4);
    }

    _NODISCARD inline bool _Is_dot_or_dotdot(const __std_fs_find_data& _Data) {
        // tests if _File_name of __std_fs_find_data is . or ..
        if (_Data._File_name[0] != L'.') {
            return false;
        }

        const auto _Second_char = _Data._File_name[1];
        if (_Second_char == 0) {
            return true;
        }

        if (_Second_char != L'.') {
            return false;
        }

        return _Data._File_name[2] == 0;
    }

    struct _Find_file_handle {
        __std_fs_dir_handle _Handle = __std_fs_dir_handle::_Invalid;

        _Find_file_handle() noexcept = default;
        _Find_file_handle(_Find_file_handle&& _Rhs) noexcept
            : _Handle(_STD exchange(_Rhs._Handle, __std_fs_dir_handle::_Invalid)) {}

        _Find_file_handle& operator=(_Find_file_handle&& _Rhs) noexcept {
            auto _Tmp = _STD exchange(_Rhs._Handle, __std_fs_dir_handle::_Invalid);
            _Tmp      = _STD exchange(_Handle, _Tmp);
            __std_fs_directory_iterator_close(_Tmp);
            return *this;
        }

        _NODISCARD __std_win_error _Open(const wchar_t* _Path_spec, __std_fs_find_data* _Results) noexcept {
            return __std_fs_directory_iterator_open(_Path_spec, &_Handle, _Results);
        }

        ~_Find_file_handle() noexcept {
            __std_fs_directory_iterator_close(_Handle);
        }

        explicit operator bool() const noexcept {
            return _Handle != __std_fs_dir_handle::_Invalid;
        }
    };

    template <class _Base_iter>
    class _Path_iterator;

    _EXPORT_STD class path {
        template <class _Base_iter>
        friend class _Path_iterator;
        friend inline path absolute(const path& _Input, error_code& _Ec);
        friend inline __std_win_error _Canonical(path& _Result, const wstring& _Text);
        friend inline path temp_directory_path(error_code& _Ec);
        friend inline path current_path(error_code& _Ec);
        friend inline void current_path(const path& _To);
        friend inline void current_path(const path& _To, error_code& _Ec) noexcept;
        friend inline __std_win_error _Read_symlink(const path& _Symlink_path, path& _Result);

    public:
        using value_type  = wchar_t;
        using string_type = _STD wstring;

        static constexpr wchar_t preferred_separator = L'\\';

        enum format { auto_format, native_format, generic_format };

        path()                           = default;
        path(const path&)                = default;
        path(path&&)                     = default;
        ~path()                          = default;
        path& operator=(const path&)     = default;
        path& operator=(path&&) noexcept = default;

        path(string_type&& _Source) : _Text(_STD move(_Source)) {}

        path(string_type&& _Source, format) : _Text(_STD move(_Source)) {
            // format has no meaning for this implementation, as the generic grammar is acceptable as a native path
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path(const _Src& _Source, format = auto_format) : _Text(_Convert_Source_to_wide(_Source)) {
            // format has no meaning for this implementation, as the generic grammar is acceptable as a native path
        }

        template <class _InIt>
        path(_InIt _First, _InIt _Last, format = auto_format) : _Text(_Convert_range_to_wide(_First, _Last)) {
            // format has no meaning for this implementation, as the generic grammar is acceptable as a native path
            static_assert(_Is_EcharT<_Iter_value_t<_InIt>>, "invalid value_type, see N4950 [fs.req]/3");
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path(const _Src& _Source, const locale& _Loc, format = auto_format)
            : _Text(_Convert_stringoid_with_locale_to_wide(_Stringoid_from_Source(_Source), _Loc)) {
            // format has no meaning for this implementation, as the generic grammar is acceptable as a native path
            using _Stringoid = decltype(_Stringoid_from_Source(_Source));
            static_assert(is_same_v<typename _Stringoid::value_type, char>,
                "invalid value_type, see N4950 [fs.path.construct]/5");
        }

        template <class _InIt>
        path(_InIt _First, _InIt _Last, const locale& _Loc, format = auto_format)
            : _Text(_Convert_stringoid_with_locale_to_wide(_Stringoid_from_range(_First, _Last), _Loc)) {
            // format has no meaning for this implementation, as the generic grammar is acceptable as a native path
            static_assert(is_same_v<_Iter_value_t<_InIt>, char>, "invalid value_type, see N4950 [fs.path.construct]/5");
        }

        path& operator=(string_type&& _Source) noexcept /* strengthened */ {
            // set native() to _Source
            _Text = _STD move(_Source);
            return *this;
        }

        path& assign(string_type&& _Source) noexcept /* strengthened */ {
            // set native() to _Source
            _Text = _STD move(_Source);
            return *this;
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path& operator=(const _Src& _Source) { // set native() to _Source
            _Text = _Convert_Source_to_wide(_Source);
            return *this;
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path& assign(const _Src& _Source) { // set native() to _Source
            _Text = _Convert_Source_to_wide(_Source);
            return *this;
        }

        template <class _InIt>
        path& assign(_InIt _First, _InIt _Last) { // set native() to [_First, _Last)
            static_assert(_Is_EcharT<_Iter_value_t<_InIt>>, "invalid value_type, see N4950 [fs.req]/3");
            _Text = _Convert_range_to_wide(_First, _Last);
            return *this;
        }

        path& operator/=(const path& _Other) {
            // set *this to the path lexically resolved by _Other relative to *this
            // examples:
            // path{"cat"} / "c:/dog"; // yields "c:/dog"
            // path{"cat"} / "c:"; // yields "c:"
            // path{"c:"} / ""; // yields "c:"
            // path{"c:cat"} / "/dog"; // yields "c:/dog"
            // path{"c:cat"} / "c:dog"; // yields "c:cat/dog"
            // path{"c:cat"} / "d:dog"; // yields "d:dog"
            // several places herein quote the standard, but the standard's variable p is replaced with _Other

            if (_Other.is_absolute()) { // if _Other.is_absolute(), then op=(_Other)
                return operator=(_Other);
            }

            const auto _My_first            = _Text.data();
            const auto _My_last             = _My_first + _Text.size();
            const auto _Other_first         = _Other._Text.data();
            const auto _Other_last          = _Other_first + _Other._Text.size();
            const auto _My_root_name_end    = _Find_root_name_end(_My_first, _My_last);
            const auto _Other_root_name_end = _Find_root_name_end(_Other_first, _Other_last);
            if (_Other_first != _Other_root_name_end
                && _Range_compare(_My_first, _My_root_name_end, _Other_first, _Other_root_name_end) != 0) {
                // if _Other.has_root_name() && _Other.root_name() != root_name(), then op=(_Other)
                return operator=(_Other);
            }

            if (_Other_root_name_end != _Other_last && _Is_slash(*_Other_root_name_end)) {
                // If _Other.has_root_directory() removes any root directory and relative-path from *this
                _Text.erase(static_cast<size_t>(_My_root_name_end - _My_first));
            } else {
                // Otherwise, if (!has_root_directory && is_absolute) || has_filename appends path::preferred_separator
                if (_My_root_name_end == _My_last) {
                    // Here, !has_root_directory && !has_filename
                    // Going through our root_name kinds:
                    // X: can't be absolute here, since those paths are absolute only when has_root_directory
                    // \\?\ can't exist without has_root_directory
                    // \\server can be absolute here
                    if (_My_root_name_end - _My_first >= 3) {
                        _Text.push_back(preferred_separator);
                    }
                } else {
                    // Here, has_root_directory || has_filename
                    // If there is a trailing slash, the trailing slash might be part of root_directory.
                    // If it is, has_root_directory && !has_filename, so the test fails.
                    // If there is a trailing slash not part of root_directory, then !has_filename, so only
                    // (!has_root_directory && is_absolute) remains
                    // Going through our root_name kinds:
                    // X:cat\ needs a root_directory to be absolute
                    // \\server\cat must have a root_directory to exist with a relative_path
                    // \\?\ must have a root_directory to exist
                    // As a result, the test fails if there is a trailing slash.
                    // If there is no trailing slash, then has_filename, so the test passes.
                    // Therefore, the test passes if and only if there is no trailing slash.
                    if (!_Is_slash(_My_last[-1])) {
                        _Text.push_back(preferred_separator);
                    }
                }
            }

            // Then appends the native format pathname of _Other, omitting any root-name from its generic format
            // pathname, to the native format pathname.
            _Text.append(_Other_root_name_end, static_cast<size_t>(_Other_last - _Other_root_name_end));
            return *this;
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path& operator/=(const _Src& _Source) {
            return operator/=(path{_Source});
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path& append(const _Src& _Source) {
            return operator/=(path{_Source});
        }

        template <class _InIt>
        path& append(_InIt _First, _InIt _Last) {
            static_assert(_Is_EcharT<_Iter_value_t<_InIt>>, "invalid value_type, see N4950 [fs.req]/3");
            return operator/=(path{_First, _Last});
        }

        path& operator+=(const path& _Added) { // concat _Added to native()
            return operator+=(_Added._Text);
        }

        path& operator+=(const string_type& _Added) { // concat _Added to native()
            _Text._Orphan_all();
            _Text += _Added;
            return *this;
        }

        path& operator+=(const wstring_view _Added) { // concat _Added to native()
            _Text._Orphan_all();
            _Text += _Added;
            return *this;
        }

        path& operator+=(const value_type* const _Added) { // concat _Added to native()
            _Text._Orphan_all();
            _Text += _Added;
            return *this;
        }

        path& operator+=(const value_type _Added) { // concat _Added to native()
            _Text._Orphan_all();
            _Text += _Added;
            return *this;
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path& operator+=(const _Src& _Added) { // concat _Added to native()
            return operator+=(path{_Added}._Text);
        }

        template <class _EcharT, enable_if_t<_Is_EcharT<_EcharT>, int> = 0>
        path& operator+=(const _EcharT _Added) { // concat _Added to native()
            return operator+=(path{&_Added, &_Added + 1}._Text);
        }

        template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
        path& concat(const _Src& _Added) { // concat _Added to native()
            return operator+=(path{_Added}._Text);
        }

        template <class _InIt>
        path& concat(_InIt _First, _InIt _Last) { // concat [_First, _Last) to native()
            static_assert(_Is_EcharT<_Iter_value_t<_InIt>>, "invalid value_type, see N4950 [fs.req]/3");
            return operator+=(path{_First, _Last}._Text);
        }

        void clear() noexcept { // set *this to the empty path
            _Text._Orphan_all();
            _Text.clear();
        }

        path& make_preferred() noexcept /* strengthened */ {
            // transform each fallback-separator into preferred-separator
            _Text._Orphan_all();
            _STD replace(_Text.begin(), _Text.end(), L'/', L'\\');
            return *this;
        }

        path& remove_filename() noexcept /* strengthened */ {
            // remove any filename from *this
            const auto _First    = _Text.data();
            const auto _Last     = _First + _Text.size();
            const auto _Filename = _Find_filename(_First, _Last);
            _Text._Orphan_all();
            _Text.erase(static_cast<size_t>(_Filename - _First));
            return *this;
        }

        void _Remove_filename_and_separator() noexcept { // remove filename and preceding non-root directory-separator
            const auto _First         = _Text.data();
            const auto _Last          = _First + _Text.size();
            const auto _Root_name_end = _Find_root_name_end(_First, _Last);
            const auto _Root_dir_end =
                (_Root_name_end != _Last && _Is_slash(*_Root_name_end)) ? _Root_name_end + 1 : _Root_name_end;

            using _Reverse_iter = reverse_iterator<const wchar_t*>;

            const _Reverse_iter _Rbegin{_Last};
            const _Reverse_iter _Rend{_Root_dir_end};

            const auto _Rslash_first = _STD find_if(_Rbegin, _Rend, _Is_slash);
            const auto _Rslash_last  = _STD find_if_not(_Rslash_first, _Rend, _Is_slash);

            const _Reverse_iter _Rlast{_First};

            _Text._Orphan_all();
            _Text.erase(static_cast<size_t>(_Rlast - _Rslash_last));
        }

        path& replace_filename(const path& _Replacement) { // remove any filename from *this and append _Replacement
            remove_filename();
            return operator/=(_Replacement);
        }

        path& replace_extension(/* const path& _Replacement = {} */) noexcept /* strengthened */ {
            // remove any extension() (and alternate data stream references) from *this's filename()
            const wchar_t* _First = _Text.data();
            const auto _Last      = _First + _Text.size();
            const auto _Filename  = _Find_filename(_First, _Last);
            const auto _Ads       = _STD find(_Filename, _Last, L':');
            const auto _Extension = _Find_extension(_Filename, _Ads);
            _Text._Orphan_all();
            _Text.erase(static_cast<size_t>(_Extension - _First));
            return *this;
        }

        path& replace_extension(const path& _Replacement) {
            // remove any extension() (and alternate data stream references) from *this's filename(), and concatenate
            // _Replacement
            replace_extension();
            if (!_Replacement.empty() && _Replacement._Text[0] != L'.') {
                _Text.push_back(L'.');
            }

            return operator+=(_Replacement._Text);
        }

        void swap(path& _Rhs) noexcept {
            _Text.swap(_Rhs._Text);
        }

        _NODISCARD const string_type& native() const noexcept {
            // return a reference to the internally stored wstring in the native format
            return _Text;
        }

        _NODISCARD const value_type* c_str() const noexcept {
            // return a NTCTS to the internally stored path in the native format
            return _Text.c_str();
        }

        operator string_type() const { // implicitly convert *this into a string containing the native format
            return _Text;
        }

        template <class _EcharT, class _Traits = char_traits<_EcharT>, class _Alloc = allocator<_EcharT>,
            enable_if_t<_Is_EcharT<_EcharT>, int> = 0>
        _NODISCARD basic_string<_EcharT, _Traits, _Alloc> string(const _Alloc& _Al = _Alloc()) const {
            // convert the native path from this instance into a basic_string
            return _Convert_wide_to<_Traits>(_Text, _Al);
        }

        _NODISCARD _STD string string() const { // convert the native path from this instance into a string
            return string<char>();
        }

        _NODISCARD _STD wstring wstring() const { // copy the native path from this instance into a wstring
            return _Text;
        }

        _NODISCARD auto u8string() const { // convert the native path from this instance into a UTF-8 string
#ifdef __cpp_lib_char8_t
            using _U8Ty = char8_t;
#else // ^^^ defined(__cpp_lib_char8_t) / !defined(__cpp_lib_char8_t) vvv
            using _U8Ty = char;
#endif // ^^^ !defined(__cpp_lib_char8_t) ^^^
            return _Convert_wide_to_narrow<char_traits<_U8Ty>>(__std_code_page::_Utf8, _Text, allocator<_U8Ty>{});
        }

        _NODISCARD _STD u16string u16string() const { // convert the native path from this instance into a u16string
            return string<char16_t>();
        }

        _NODISCARD _STD u32string u32string() const { // convert the native path from this instance into a u32string
            return string<char32_t>();
        }

        template <class _EcharT, class _Traits = char_traits<_EcharT>, class _Alloc = allocator<_EcharT>,
            enable_if_t<_Is_EcharT<_EcharT>, int> = 0>
        _NODISCARD basic_string<_EcharT, _Traits, _Alloc> generic_string(const _Alloc& _Al = _Alloc()) const {
            // convert the native path from this instance into a generic basic_string
            using _Alwide = _Rebind_alloc_t<_Alloc, wchar_t>;
            _Alwide _Al_wchar(_Al);
            basic_string<wchar_t, char_traits<wchar_t>, _Alwide> _Generic_str(_Al_wchar);
            _Generic_str.resize(_Text.size());
            _STD replace_copy(_Text.begin(), _Text.end(), _Generic_str.begin(), L'\\', L'/');
            return _Convert_wide_to<_Traits>(_Generic_str, _Al);
        }

        _NODISCARD _STD string generic_string() const {
            // convert the native path from this instance into a generic string
            return generic_string<char>();
        }

        _NODISCARD _STD wstring generic_wstring() const {
            // convert the current native path into a copy of it in the generic format
            // note: intra-filename() observers stem() and extension() strip alternate data
            // streams, but filenames with alternate data streams inside can serve as
            // perfectly valid values of filename in the generic format, so in the interest of
            // destroying less information we have preserved them here.
            _STD wstring _Result;
            _Result.resize(_Text.size());
            _STD replace_copy(_Text.begin(), _Text.end(), _Result.begin(), L'\\', L'/');
            return _Result;
        }

        _NODISCARD auto generic_u8string() const {
            // convert the native path from this instance into a generic UTF-8 string
            auto _Result = u8string();
            using _U8Ty  = decltype(_Result)::value_type;
            _STD replace(_Result.begin(), _Result.end(), _U8Ty{u8'\\'}, _U8Ty{u8'/'});
            return _Result;
        }

        _NODISCARD _STD u16string generic_u16string() const {
            // convert the native path from this instance into a generic u16string
            _STD u16string _Result = u16string();
            _STD replace(_Result.begin(), _Result.end(), u'\\', u'/');
            return _Result;
        }

        _NODISCARD _STD u32string generic_u32string() const {
            // convert the native path from this instance into a generic u32string
            _STD u32string _Result = u32string();
            _STD replace(_Result.begin(), _Result.end(), U'\\', U'/');
            return _Result;
        }

        _NODISCARD int compare(const path& _Other) const noexcept { // compare *this with _Other
            return compare(static_cast<wstring_view>(_Other._Text));
        }

        _NODISCARD int compare(const string_type& _Other) const noexcept /* strengthened */ {
            // compare *this with _Other
            return compare(static_cast<wstring_view>(_Other));
        }

        _NODISCARD int compare(const basic_string_view<value_type> _Other) const noexcept /* strengthened */ {
            // compare *this with _Other
            // several places herein quote the standard, but the standard's variable p is replaced with _Other
            const auto _My_first            = _Text.data();
            const auto _My_last             = _My_first + _Text.size();
            const auto _My_root_name_end    = _Find_root_name_end(_My_first, _My_last);
            const auto _Other_first         = _Other.data();
            const auto _Other_last          = _Other_first + _Other.size();
            const auto _Other_root_name_end = _Find_root_name_end(_Other_first, _Other_last);

            // Let rootNameComparison be the result of this->root_name().native().compare(_Other.root_name().native())
            const int _Root_cmp = _Range_compare(_My_first, _My_root_name_end, _Other_first, _Other_root_name_end);
            if (_Root_cmp != 0) { // If rootNameComparison is not 0, rootNameComparison
                return _Root_cmp;
            }

            auto _My_relative               = _STD find_if_not(_My_root_name_end, _My_last, _Is_slash);
            auto _Other_relative            = _STD find_if_not(_Other_root_name_end, _Other_last, _Is_slash);
            const bool _My_has_root_name    = _My_root_name_end != _My_relative;
            const bool _Other_has_root_name = _Other_root_name_end != _Other_relative;
            // If !this->has_root_directory() and _Other.has_root_directory(), a value less than 0
            // If this->has_root_directory() and !_Other.has_root_directory(), a value greater than 0
            const int _Root_name_cmp = _My_has_root_name - _Other_has_root_name;
            if (_Root_name_cmp != 0) {
                return _Root_name_cmp;
            }

            // Otherwise, lexicographic by element
            for (;;) {
                const bool _My_empty    = _My_relative == _My_last;
                const bool _Other_empty = _Other_relative == _Other_last;
                const int _Empty_cmp    = _Other_empty - _My_empty;
                if (_My_empty || _Empty_cmp != 0) {
                    return _Empty_cmp;
                }

                const bool _My_slash    = _Is_slash(*_My_relative);
                const bool _Other_slash = _Is_slash(*_Other_relative);
                const int _Slash_cmp    = _Other_slash - _My_slash;
                if (_Slash_cmp != 0) {
                    return _Slash_cmp;
                }

                if (_My_slash) { // comparing directory-separator
                    _My_relative    = _STD find_if_not(_My_relative + 1, _My_last, _Is_slash);
                    _Other_relative = _STD find_if_not(_Other_relative + 1, _Other_last, _Is_slash);
                    continue;
                }

                const int _Cmp = *_My_relative - *_Other_relative;
                if (_Cmp != 0) {
                    return _Cmp;
                }

                ++_My_relative;
                ++_Other_relative;
            }
        }

        _NODISCARD int compare(const value_type* const _Other) const noexcept /* strengthened */ {
            // compare *this with _Other
            return compare(static_cast<wstring_view>(_Other));
        }

        _NODISCARD path root_name() const {
            // parse the root-name from *this and return a copy if present; otherwise, return the empty path
            return _Parse_root_name(_Text);
        }

        _NODISCARD path root_directory() const {
            // parse the root-directory from *this and return a copy if present; otherwise, return the empty path
            return _Parse_root_directory(_Text);
        }

        _NODISCARD path root_path() const {
            // parse the root-path from *this and return a copy if present; otherwise, return the empty path
            return _Parse_root_path(_Text);
        }

        _NODISCARD path relative_path() const {
            // parse the relative-path from *this and return a copy if present; otherwise, return the empty path
            return _Parse_relative_path(_Text);
        }

        _NODISCARD path parent_path() const {
            // parse the parent-path from *this and return a copy if present; otherwise, return the empty path
            return _Parse_parent_path(_Text);
        }

        _NODISCARD path filename() const {
            // parse the filename from *this and return a copy if present; otherwise, return the empty path
            return _Parse_filename(_Text);
        }

        _NODISCARD path stem() const {
            // parse the stem from *this and return a copy if present; otherwise, return the empty path
            return _Parse_stem(_Text);
        }

        _NODISCARD path extension() const {
            // parse the extension from *this and return a copy if present; otherwise, return the empty path
            return _Parse_extension(_Text);
        }

        _NODISCARD bool empty() const noexcept {
            return _Text.empty();
        }

        _NODISCARD bool has_root_name() const noexcept /* strengthened */ {
            // parse the root-name from *this and return whether it exists
            return !_Parse_root_name(_Text).empty();
        }

        _NODISCARD bool has_root_directory() const noexcept /* strengthened */ {
            // parse the root-directory from *this and return whether it exists
            return !_Parse_root_directory(_Text).empty();
        }

        _NODISCARD bool has_root_path() const noexcept /* strengthened */ {
            // parse the root-path from *this and return whether it exists
            return !_Parse_root_path(_Text).empty();
        }

        _NODISCARD bool has_relative_path() const noexcept /* strengthened */ {
            // parse the relative-path from *this and return whether it exists
            return !_Parse_relative_path(_Text).empty();
        }

        _NODISCARD bool has_parent_path() const noexcept /* strengthened */ {
            // parse the parent-path from *this and return whether it exists
            return !_Parse_parent_path(_Text).empty();
        }

        _NODISCARD bool has_filename() const noexcept /* strengthened */ {
            // parse the filename from *this and return whether it exists
            return !_Parse_filename(_Text).empty();
        }

        _NODISCARD bool has_stem() const noexcept /* strengthened */ {
            // parse the stem from *this and return whether it exists
            return !_Parse_stem(_Text).empty();
        }

        _NODISCARD bool has_extension() const noexcept /* strengthened */ {
            // parse the extension from *this and return whether it exists
            return !_Parse_extension(_Text).empty();
        }

        _NODISCARD bool is_absolute() const noexcept /* strengthened */ {
            // paths with a root-name that is a drive letter and no root-directory are drive relative, such as x:example
            // paths with no root-name or root-directory are relative, such as example
            // paths with no root-name but a root-directory are root relative, such as \example
            // all other paths are absolute
            const auto _First = _Text.data();
            const auto _Last  = _First + _Text.size();
            if (_Has_drive_letter_prefix(_First, _Last)) { // test for X:\ but not X:cat
                return _Last - _First >= 3 && _Is_slash(_First[2]);
            }

            // if root-name is otherwise nonempty, then it must be one of the always-absolute prefixes like
            // \\?\ or \\server, so the path is absolute. Otherwise it is relative.
            return _First != _Find_root_name_end(_First, _Last);
        }

        _NODISCARD bool is_relative() const noexcept /* strengthened */ {
            // test if *this is a relative path
            return !is_absolute();
        }

        _NODISCARD path lexically_normal() const {
            constexpr wstring_view _Dot     = L"."sv;
            constexpr wstring_view _Dot_dot = L".."sv;

            // N4950 [fs.path.generic]/6:
            // "Normalization of a generic format pathname means:"

            // "1. If the path is empty, stop."
            if (empty()) {
                return {};
            }

            // "2. Replace each slash character in the root-name with a preferred-separator."
            const auto _First         = _Text.data();
            const auto _Last          = _First + _Text.size();
            const auto _Root_name_end = _Find_root_name_end(_First, _Last);
            string_type _Normalized(_First, _Root_name_end);
            _STD replace(_Normalized.begin(), _Normalized.end(), L'/', L'\\');

            // "3. Replace each directory-separator with a preferred-separator.
            // [ Note 4: The generic pathname grammar defines directory-separator
            // as one or more slashes and preferred-separators. -end note ]"
            vector<wstring_view> _Vec; // Empty wstring_view means directory-separator
                                       // that will be normalized to a preferred-separator.
                                       // Non-empty wstring_view means filename.
            _Vec.reserve(19); // avoid frequent re-allocations
            bool _Has_root_directory = false; // true: there is a slash right after root-name.
            auto _Ptr                = _Root_name_end;
            if (_Ptr != _Last && _Is_slash(*_Ptr)) {
                _Has_root_directory = true;
                _Normalized += preferred_separator;
                ++_Ptr;
                while (_Ptr != _Last && _Is_slash(*_Ptr)) {
                    ++_Ptr;
                }
            }
            // _Vec will start with a filename (if not empty).
            while (_Ptr != _Last) {
                if (_Is_slash(*_Ptr)) {
                    if (_Vec.empty() || !_Vec.back().empty()) {
                        // collapse one or more slashes and preferred-separators to one empty wstring_view
                        _Vec.emplace_back();
                    }
                    ++_Ptr;
                } else {
                    const auto _Filename_end = _STD find_if(_Ptr + 1, _Last, _Is_slash);
                    _Vec.emplace_back(_Ptr, static_cast<size_t>(_Filename_end - _Ptr));
                    _Ptr = _Filename_end;
                }
            }

            // "4. Remove each dot filename and any immediately following directory-separator."
            // "5. As long as any appear, remove a non-dot-dot filename immediately followed by a
            // directory-separator and a dot-dot filename, along with any immediately following directory-separator."
            // "6. If there is a root-directory, remove all dot-dot filenames
            // and any directory-separators immediately following them.
            // [ Note 5: These dot-dot filenames attempt to refer to nonexistent parent directories. -end note ]"
            auto _New_end = _Vec.begin();
            for (auto _Pos = _Vec.begin(); _Pos != _Vec.end();) {
                const auto _Elem = *_Pos++; // _Pos points at a filename here; it points at end or a separator after ++.
                if (_Elem == _Dot) {
                    // ignore dot (and following separator).
                    if (_Pos == _Vec.end()) {
                        break;
                    }
                } else if (_Elem != _Dot_dot) {
                    // append normal filename and separator.
                    *_New_end++ = _Elem; // _New_end points at end or a separator after ++.
                    if (_Pos == _Vec.end()) {
                        break;
                    }
                    ++_New_end; // _New_end(<=_Pos) doesn't point at end; accept separator.
                } else { // _Dot_dot
                    if (_New_end != _Vec.begin() && _New_end[-2] != _Dot_dot) {
                        // _New_end == _Vec.begin() + 2n here.
                        // remove preceding non-dot-dot filename and separator.
                        _New_end -= 2;
                        if (_Pos == _Vec.end()) {
                            break;
                        }
                    } else if (!_Has_root_directory) {
                        // due to 6, append dot-dot and separator only if !_Has_root_directory.
                        *_New_end++ = _Dot_dot;
                        if (_Pos == _Vec.end()) {
                            break;
                        }
                        ++_New_end;
                    } else {
                        // ignore dot-dot and separator.
                        if (_Pos == _Vec.end()) {
                            break;
                        }
                    }
                }
                ++_Pos; // _Pos points at a separator here; it points at end or a filename after ++.
            }
            _Vec.erase(_New_end, _Vec.end());

            // "7. If the last filename is dot-dot, remove any trailing directory-separator."
            if (_Vec.size() >= 2 && _Vec.back().empty() && _Vec.end()[-2] == _Dot_dot) {
                _Vec.pop_back();
            }

            // Build up _Normalized by flattening _Vec.
            for (const auto& _Elem : _Vec) {
                if (_Elem.empty()) {
                    _Normalized += preferred_separator;
                } else {
                    _Normalized += _Elem;
                }
            }

            // "8. If the path is empty, add a dot."
            if (_Normalized.empty()) {
                _Normalized = _Dot;
            }

            // "The result of normalization is a path in normal form, which is said to be normalized."
            return path(_STD move(_Normalized));
        }

        _NODISCARD inline path lexically_relative(const path& _Base) const;

        _NODISCARD path lexically_proximate(const path& _Base) const {
            path _Result = lexically_relative(_Base);

            if (_Result.empty()) {
                _Result = *this;
            }

            return _Result;
        }

        using iterator       = _Path_iterator<string_type::const_iterator>;
        using const_iterator = iterator;

        _NODISCARD inline iterator begin() const;
        _NODISCARD inline iterator end() const noexcept; // strengthened

        template <class _Elem, class _Traits>
        friend _STD basic_ostream<_Elem, _Traits>& operator<<( // TRANSITION, VSO-570323
            _STD basic_ostream<_Elem, _Traits>& _Ostr, const path& _Path) { // TRANSITION, VSO-570323
            // insert a path into a stream
            return _Ostr << _STD quoted(_Path.string<_Elem, _Traits>());
        }

        template <class _Elem, class _Traits>
        friend _STD basic_istream<_Elem, _Traits>& operator>>( // TRANSITION, VSO-570323
            _STD basic_istream<_Elem, _Traits>& _Istr, path& _Path) { // TRANSITION, VSO-570323
            // extract a path from a stream
            basic_string<_Elem, _Traits> _Tmp;
            _Istr >> _STD quoted(_Tmp);
            _Path = _STD move(_Tmp); // obvious optimization not depicted in N4950 [fs.path.io]/3
            return _Istr;
        }

        _NODISCARD friend bool operator==(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right._Text) == 0;
        }

#if _HAS_CXX20
        _NODISCARD friend strong_ordering operator<=>(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right._Text) <=> 0;
        }
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
        _NODISCARD friend bool operator!=(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right) != 0;
        }

        _NODISCARD friend bool operator<(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right) < 0;
        }

        _NODISCARD friend bool operator>(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right) > 0;
        }

        _NODISCARD friend bool operator<=(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right) <= 0;
        }

        _NODISCARD friend bool operator>=(const path& _Left, const path& _Right) noexcept {
            return _Left.compare(_Right) >= 0;
        }
#endif // ^^^ !_HAS_CXX20 ^^^

        _NODISCARD friend path operator/(const path& _Left, const path& _Right) { // append a pair of paths together
            const auto _Right_size  = _Right._Text.size();
            const auto _Right_first = _Right._Text.data();
            const auto _Right_last  = _Right_first + _Right_size;

            // Handle the most common case: !has_root_name(_Right) && !has_root_directory(_Right)
            if (_Right_size != 0 && !_Has_drive_letter_prefix(_Right_first, _Right_last) && !_Is_slash(*_Right_first)) {
                const auto _Left_size  = _Left._Text.size();
                const auto _Left_first = _Left._Text.data();
                const auto _Left_last  = _Left_first + _Left_size;

                // Appending a slash to "X:" would make it an absolute path
                const bool _Left_is_just_drive = _Left_size == 2 && _Is_drive_prefix(_Left_first);
                const bool _Is_slash_needed    = _Left_size != 0 && !_Left_is_just_drive && !_Is_slash(_Left_last[-1]);

                const auto _Total_size = _Left_size + static_cast<size_t>(_Is_slash_needed) + _Right_size;

                path _Tmp;
                _Tmp._Text._Resize_and_overwrite(_Total_size, [=](wchar_t* _Ptr, const size_t _Size) {
                    _CSTD memcpy(_Ptr, _Left_first, _Left_size * sizeof(wchar_t));
                    _Ptr += _Left_size;
                    if (_Is_slash_needed) {
                        *_Ptr++ = preferred_separator;
                    }
                    _CSTD memcpy(_Ptr, _Right_first, _Right_size * sizeof(wchar_t));
                    return _Size;
                });
                return _Tmp;
            }

            path _Tmp = _Left;
            _Tmp /= _Right;
            return _Tmp;
        }

    private:
        string_type _Text;
    };

    _EXPORT_STD template <class _Src, enable_if_t<_Is_Source<_Src>, int> = 0>
    _CXX20_DEPRECATE_U8PATH _NODISCARD path u8path(const _Src& _Source) { // construct a path from UTF-8 _Source
        return path{_Convert_Source_to_wide(_Source, _Utf8_conversion{})};
    }

    _EXPORT_STD template <class _InIt>
    _CXX20_DEPRECATE_U8PATH _NODISCARD path u8path(_InIt _First, _InIt _Last) {
        // construct a path from UTF-8 [_First, _Last)
        static_assert(_Is_EcharT<_Iter_value_t<_InIt>>, "invalid value_type, see N4950 [fs.req]/3");
        return path{_Convert_range_to_wide(_First, _Last, _Utf8_conversion{})};
    }

    template <class _Base_iter>
    class _Path_iterator { // almost bidirectional iterator for path
    public:
        // Note that the path::iterator can be decremented and can be dereferenced multiple times,
        // but doesn't actually meet the forward iterator requirements. See N4950 [fs.path.itr]/2:
        //  ... for dereferenceable iterators a and b of type path::iterator with a == b, there is no requirement
        //  that *a and *b are bound to the same object
        // This means that you can't give path::iterator to std::reverse_iterator, as operator* there ends
        // up returning a reference to a destroyed temporary. Other algorithms requiring bidirectional
        // iterators may be similarly affected, so we've marked input for now.
        // Also note, in the vector<path>(p.begin(), p.end()) case, the user actually probably wants
        // input behavior, as distance()-ing a path is fairly expensive.
        using iterator_category = input_iterator_tag;
        using value_type        = path;
        using difference_type   = ptrdiff_t;
        using pointer           = const path*;
        using reference         = const path&;

        _Path_iterator() = default;

        _Path_iterator(const _Base_iter& _Position_, const path* _Mypath_) noexcept
            : _Position(_Position_), _Element(), _Mypath(_Mypath_) {}

        _Path_iterator(const _Base_iter& _Position_, const path& _Element_, const path* _Mypath_)
            : _Position(_Position_), _Element(_Element_), _Mypath(_Mypath_) {}
        _Path_iterator(const _Base_iter& _Position_, path&& _Element_, const path* _Mypath_)
            : _Position(_Position_), _Element(_STD move(_Element_)), _Mypath(_Mypath_) {}

        _Path_iterator(const _Path_iterator&)            = default;
        _Path_iterator(_Path_iterator&&)                 = default;
        _Path_iterator& operator=(const _Path_iterator&) = default;
        _Path_iterator& operator=(_Path_iterator&&)      = default;

        _NODISCARD reference operator*() const noexcept {
            return _Element;
        }

        _NODISCARD pointer operator->() const noexcept {
            return _STD addressof(_Element);
        }

        _Path_iterator& operator++() {
            const auto& _Text = _Mypath->native();
            const auto _Size  = _Element.native().size();
            _Adl_verify_range(_Text.begin(), _Position); // engaged when *this is checked
            const auto _Begin = _Text.data();
            const auto _End   = _Begin + _Text.size();
            _Adl_verify_range(_Begin, _Position); // engaged when *this is unchecked
            if (_Begin == _Get_unwrapped(_Position)) { // test if the next element will be root-directory
                _Position += static_cast<ptrdiff_t>(_Size);
                const auto _First              = _Text.data();
                const auto _Last               = _First + _Text.size();
                const auto _Root_name_end      = _Find_root_name_end(_First, _Last);
                const auto _Root_directory_end = _STD find_if_not(_Root_name_end, _Last, _Is_slash);
                if (_First != _Root_name_end && _Root_name_end != _Root_directory_end) {
                    // current element is root-name, root-directory exists, so next is root-directory
                    _Element._Text.assign(_Root_name_end, _Root_directory_end);
                    return *this;
                }

                // If we get here, either there is no root-name, and by !_Is_slash(*_Position), no root-directory,
                // or, current element is root-name, and root-directory doesn't exist.
                // Either way, the next element is the first of relative-path
            } else if (_Is_slash(*_Position)) { // current element is root-directory, or the "magic empty path"
                if (_Size == 0) { // current element was "magic empty path", become end()
                    ++_Position;
                    return *this;
                }

                // current element was root-directory, advance to relative-path
                _Position += static_cast<ptrdiff_t>(_Size);
            } else { // current element is one of relative-path
                _Position += static_cast<ptrdiff_t>(_Size);
            }

            if (_Get_unwrapped(_Position) == _End) {
                _Element.clear();
                return *this;
            }

            // at this point, the next element is a standard filename from relative_path(), and _Position
            // points at the preferred-separator or fallback-separator after the previous element
            while (_Is_slash(*_Position)) { // advance to the start of the following path element
                if (_Get_unwrapped(++_Position) == _End) { // "magic" empty element selected
                    --_Position;
                    _Element.clear();
                    return *this;
                }
            }

            _Element._Text.assign(_Get_unwrapped(_Position), _STD find_if(_Get_unwrapped(_Position), _End, _Is_slash));
            return *this;
        }

        _Path_iterator operator++(int) {
            _Path_iterator _Tmp = *this;
            ++*this;
            return _Tmp;
        }

        _Path_iterator& operator--() {
            const auto& _Text = _Mypath->native();
            _Adl_verify_range(_Text.begin(), _Position); // engaged when *this is checked
            const auto _First = _Text.data();
            _Adl_verify_range(_First, _Position); // engaged when *this is unchecked
            const auto _Last                   = _First + _Text.size();
            const auto _Root_name_end_ptr      = _Find_root_name_end(_First, _Last);
            const auto _Root_directory_end_ptr = _STD find_if_not(_Root_name_end_ptr, _Last, _Is_slash);

            if (_Root_name_end_ptr != _Root_directory_end_ptr && _Get_unwrapped(_Position) == _Root_directory_end_ptr) {
                // current element is the first of relative-path, and the prev element is root-directory
                _Seek_wrapped(_Position, _Root_name_end_ptr);
                _Element._Text.assign(
                    _Root_name_end_ptr, static_cast<size_t>(_Root_directory_end_ptr - _Root_name_end_ptr));
                return *this;
            }

            if (_First != _Root_name_end_ptr && _Get_unwrapped(_Position) == _Root_name_end_ptr) {
                // current element is root-directory or, if that doesn't exist, first
                // element of relative-path prev element therefore is root-name
                _Seek_wrapped(_Position, _First);
                _Element._Text.assign(_First, static_cast<size_t>(_Root_name_end_ptr - _First));
                return *this;
            }

            // from here, the result will be somewhere in relative-path
            if (_Get_unwrapped(_Position) == _Last && _Is_slash(_Position[-1])) { // target is "magic empty path"
                --_Position;
                _Element.clear();
                return *this;
            }

            while (_Root_directory_end_ptr != _Get_unwrapped(_Position) && _Is_slash(_Position[-1])) {
                --_Position;
            }

            const auto _New_end = _Position;
            while (_Root_directory_end_ptr != _Get_unwrapped(_Position) && !_Is_slash(_Position[-1])) {
                --_Position;
            }

            _Element._Text.assign(_Position, _New_end);
            return *this;
        }

        _Path_iterator operator--(int) {
            _Path_iterator _Tmp = *this;
            --*this;
            return _Tmp;
        }

        _NODISCARD friend bool operator==(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) {
            return _Lhs._Position == _Rhs._Position;
        }

#if !_HAS_CXX20
        _NODISCARD friend bool operator!=(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) {
            return _Lhs._Position != _Rhs._Position;
        }
#endif // !_HAS_CXX20

#if _ITERATOR_DEBUG_LEVEL != 0
        friend void _Verify_range(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) {
            _Verify_range(_Lhs._Position, _Rhs._Position);
        }
#endif // _ITERATOR_DEBUG_LEVEL != 0

        using _Prevent_inheriting_unwrap = _Path_iterator;

        template <class _Iter2 = _Base_iter, enable_if_t<_Unwrappable_v<const _Iter2&>, int> = 0>
        _NODISCARD _Path_iterator<_Unwrapped_t<const _Iter2&>> _Unwrapped() const& {
            return {_Position._Unwrapped(), _Element, _Mypath};
        }
        template <class _Iter2 = _Base_iter, enable_if_t<_Unwrappable_v<_Iter2>, int> = 0>
        _NODISCARD _Path_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && noexcept {
            _STL_INTERNAL_STATIC_ASSERT(_Has_nothrow_unwrapped<_Iter2>);
            return {_Position._Unwrapped(), _STD move(_Element), _Mypath};
        }

        static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v<_Base_iter>;

        template <class _Other>
        friend class _Path_iterator;

        template <class _Other, enable_if_t<_Wrapped_seekable_v<_Base_iter, const _Other&>, int> = 0>
        constexpr void _Seek_to(const _Path_iterator<_Other>& _It) {
            _Position._Seek_to(_It._Position);
            _Element = _It._Element;
            // _Mypath intentionally unchanged
        }

    private:
        // if the current element is the "magic empty path",
        //   points to preferred-separator or fallback-separator immediately before
        // otherwise, points to the first element of _Element
        _Base_iter _Position{};
        path _Element{};
        const path* _Mypath{};
    };

    _EXPORT_STD inline void swap(path& _Left, path& _Right) noexcept {
        _Left.swap(_Right);
    }

    _EXPORT_STD _NODISCARD inline size_t hash_value(const path& _Path) noexcept {
        // calculate a hash value for _Path
        // See path::compare; we effectively decompose the path with special handling for root_name, root_directory.
        // Examples:
        // c:\cat\dog =>  {"c:", true , "cat", "dog"}
        // c:cat\dog =>   {"c:", false, "cat", "dog"}
        // \cat\dog =>    {""  , true , "cat", "dog"}
        // cat\dog =>     {""  , false, "cat", "dog"}
        // c:\cat\dog\ => {"c:", true , "cat", "dog", ""}
        size_t _Val       = _FNV_offset_basis;
        const auto& _Text = _Path.native();
        const auto _First = _Text.data();
        const auto _Last  = _First + _Text.size();

        // First, like compare, examine the raw root_name directly
        auto _Next = _Find_root_name_end(_First, _Last);
        _Val       = _Fnv1a_append_range(_Val, _First, _Next);

        // The remaining path elements, including root_directory, are effectively hashed by normalizing each
        // directory-separator into a single preferred-separator when that goes into the hash function.
        // path::compare has special handling for root_directory to ensure c:\cat sorts before c:cat, but hash only
        // cares about equality, so no special case is necessary.
        bool _Slash_inserted = false;
        for (; _Next != _Last; ++_Next) {
            if (_Is_slash(*_Next)) {
                if (!_Slash_inserted) {
                    _Val            = _Fnv1a_append_value(_Val, path::preferred_separator);
                    _Slash_inserted = true;
                }
            } else {
                _Val            = _Fnv1a_append_value(_Val, *_Next);
                _Slash_inserted = false;
            }
        }

        return _Val;
    }

    _NODISCARD inline bool _Starts_with_root_name(const wchar_t* const _First, const wchar_t* const _Last) {
        return _Find_root_name_end(_First, _Last) != _First;
    }

    _NODISCARD inline bool _Relative_path_contains_root_name(const path& _Path) {
        const auto& _Native = _Path.native();
        auto _First         = _Native.data();
        const auto _Last    = _First + _Native.size();
        _First              = _Find_relative_path(_First, _Last);
        while (_First != _Last) {
            const auto _Next = _STD find_if(_First, _Last, _Is_slash);
            if (_Starts_with_root_name(_First, _Next)) {
                return true;
            }
            _First = _STD find_if_not(_Next, _Last, _Is_slash);
        }
        return false;
    }

    _NODISCARD inline path path::lexically_relative(const path& _Base_raw) const {
        constexpr wstring_view _Dot     = L"."sv;
        constexpr wstring_view _Dot_dot = L".."sv;

        // LWG-3699: `lexically_relative` on UNC drive paths (`\\?\C:\...`) results in a default-constructed value
        // This avoids doing any unnecessary copies;
        // the return value of `relative_path()` is lifetime-extended if necessary.
        const bool _Both_unc_paths = _Is_drive_prefix_with_slash_slash_question(_Text)
                                  && _Is_drive_prefix_with_slash_slash_question(_Base_raw._Text);
        const path& _This = _Both_unc_paths ? relative_path() : *this;
        const path& _Base = _Both_unc_paths ? _Base_raw.relative_path() : _Base_raw;

        path _Result;

        if (_This.root_name() != _Base.root_name() || _This.is_absolute() != _Base.is_absolute()
            || (!_This.has_root_directory() && _Base.has_root_directory())
            || (_Relative_path_contains_root_name(_This) || _Relative_path_contains_root_name(_Base))) {
            return _Result;
        }

        const iterator _This_end   = _This.end();
        const iterator _Base_begin = _Base.begin();
        const iterator _Base_end   = _Base.end();

        auto _Mismatched  = _STD mismatch(_This.begin(), _This_end, _Base_begin, _Base_end);
        iterator& _A_iter = _Mismatched.first;
        iterator& _B_iter = _Mismatched.second;

        if (_A_iter == _This_end && _B_iter == _Base_end) {
            _Result = _Dot;
            return _Result;
        }

        { // Skip root-name and root-directory elements, N4950 [fs.path.itr]/4.1, 4.2
            ptrdiff_t _B_dist = _STD distance(_Base_begin, _B_iter);

            const ptrdiff_t _Base_root_dist =
                static_cast<ptrdiff_t>(_Base.has_root_name()) + static_cast<ptrdiff_t>(_Base.has_root_directory());

            while (_B_dist < _Base_root_dist) {
                ++_B_iter;
                ++_B_dist;
            }
        }

        ptrdiff_t _Num = 0;

        for (; _B_iter != _Base_end; ++_B_iter) {
            const path& _Elem = *_B_iter;

            if (_Elem.empty()) { // skip empty element, N4950 [fs.path.itr]/4.4
            } else if (_Elem == _Dot) { // skip filename elements that are dot, N4950 [fs.path.gen]/3.6
            } else if (_Elem == _Dot_dot) {
                --_Num;
            } else {
                ++_Num;
            }
        }

        if (_Num < 0) {
            return _Result;
        }

        if (_Num == 0 && (_A_iter == _This_end || _A_iter->empty())) {
            _Result = _Dot;
            return _Result;
        }

        for (; _Num > 0; --_Num) {
            _Result /= _Dot_dot;
        }

        for (; _A_iter != _This_end; ++_A_iter) {
            _Result /= *_A_iter;
        }

        return _Result;
    }

    _NODISCARD inline path::iterator path::begin() const {
        const auto _First         = _Text.data();
        const auto _Last          = _First + _Text.size();
        const auto _Root_name_end = _Find_root_name_end(_First, _Last);
        const wchar_t* _First_end;
        if (_First == _Root_name_end) { // first element isn't root-name
            auto _Root_directory_end = _STD find_if_not(_Root_name_end, _Last, _Is_slash);
            if (_First == _Root_directory_end) { // first element is first relative-path entry
                _First_end = _STD find_if(_Root_directory_end, _Last, _Is_slash);
            } else { // first element is root-directory
                _First_end = _Root_directory_end;
            }
        } else { // first element is root-name
            _First_end = _Root_name_end;
        }

        return iterator(_Text.cbegin(), wstring_view(_First, static_cast<size_t>(_First_end - _First)), this);
    }

    _NODISCARD inline path::iterator path::end() const noexcept /* strengthened */ {
        return iterator(_Text.cend(), this);
    }

    _EXPORT_STD class _NODISCARD filesystem_error : public system_error { // base of all filesystem-error exceptions
    public:
        filesystem_error(const string& _Message, const error_code _Errcode)
            : system_error(_Errcode, _Message), _What(runtime_error::what()) {}

        filesystem_error(const string& _Message, const path& _Path1_arg, const error_code _Errcode)
            : system_error(_Errcode, _Message), _Path1(_Path1_arg), _Path2(),
              _What(_Pretty_message(runtime_error::what(), _Path1_arg)) {}

        filesystem_error(
            const string& _Message, const path& _Path1_arg, const path& _Path2_arg, const error_code _Errcode)
            : system_error(_Errcode, _Message), _Path1(_Path1_arg), _Path2(_Path2_arg),
              _What(_Pretty_message(runtime_error::what(), _Path1_arg, _Path2_arg)) {}

        _NODISCARD const path& path1() const noexcept {
            return _Path1;
        }

        _NODISCARD const path& path2() const noexcept {
            return _Path2;
        }

        _NODISCARD const char* what() const noexcept override {
            return _What.c_str();
        }

    private:
        static string _Pretty_message(const string_view _Op, const path& _Path1, const path& _Path2 = {}) {
            string _Result;
            // Convert the paths to narrow encoding in a way that gracefully handles non-encodable characters
            const auto _Code_page   = __std_fs_code_page();
            const string _Path1_str = _Convert_wide_to_narrow_replace_chars<char_traits<char>>(
                _Code_page, _Path1.native(), allocator<char>{});
            const string _Path2_str = _Convert_wide_to_narrow_replace_chars<char_traits<char>>(
                _Code_page, _Path2.native(), allocator<char>{});
            _Result.reserve(_Op.size() + (_Path2_str.empty() ? 4 : 8) + _Path1_str.size() + _Path2_str.size());
            _Result += _Op;
            _Result += R"(: ")"sv; // 3 chars
            _Result += _Path1_str;
            if (!_Path2_str.empty()) {
                _Result += R"(", ")"sv; // 4 chars
                _Result += _Path2_str;
            }
            _Result += '"'; // 1 char
            return _Result;
        }

        path _Path1;
        path _Path2;
        string _What;

#if !_HAS_EXCEPTIONS
    protected:
        void _Doraise() const override { // perform class-specific exception handling
            _RAISE(*this);
        }
#endif // !_HAS_EXCEPTIONS
    };

    [[noreturn]] inline void _Throw_fs_error(const char* _Op, __std_win_error _Error) {
        _THROW(filesystem_error(_Op, _Make_ec(_Error)));
    }

    [[noreturn]] inline void _Throw_fs_error(const char* _Op, __std_win_error _Error, const path& _Path1) {
        _THROW(filesystem_error(_Op, _Path1, _Make_ec(_Error)));
    }

    [[noreturn]] inline void _Throw_fs_error(
        const char* _Op, __std_win_error _Error, const path& _Path1, const path& _Path2) {
        _THROW(filesystem_error(_Op, _Path1, _Path2, _Make_ec(_Error)));
    }

    [[noreturn]] inline void _Throw_fs_error(const char* _Op, const error_code& _Error) {
        _THROW(filesystem_error(_Op, _Error));
    }

    [[noreturn]] inline void _Throw_fs_error(const char* _Op, const error_code& _Error, const path& _Path1) {
        _THROW(filesystem_error(_Op, _Path1, _Error));
    }

    [[noreturn]] inline void _Throw_fs_error(
        const char* _Op, const error_code& _Error, const path& _Path1, const path& _Path2) {
        _THROW(filesystem_error(_Op, _Path1, _Path2, _Error));
    }

    _EXPORT_STD enum class file_type {
        none,
        not_found,
        regular,
        directory,
        symlink,

        block, // not used on Windows
        character, // not used in this implementation; theoretically some special files like CON
                   // might qualify, but querying for this is extremely expensive and unlikely
                   // to be useful in practice

        fifo, // not used on Windows (\\.\pipe named pipes don't behave exactly like POSIX fifos)
        socket, // not used on Windows
        unknown,

        junction // implementation-defined value indicating an NT junction
    };

    _EXPORT_STD enum class perms {
        none = 0,

        owner_read  = 0400,
        owner_write = 0200,
        owner_exec  = 0100,
        owner_all   = 0700,

        group_read  = 0040,
        group_write = 0020,
        group_exec  = 0010,
        group_all   = 0070,

        others_read  = 0004,
        others_write = 0002,
        others_exec  = 0001,
        others_all   = 0007,

        all        = 0777, // returned for all files without FILE_ATTRIBUTE_READONLY
        set_uid    = 04000,
        set_gid    = 02000,
        sticky_bit = 01000,
        mask       = 07777,
        unknown    = 0xFFFF,

        _All_write               = owner_write | group_write | others_write,
        _File_attribute_readonly = all & ~_All_write // returned for files with FILE_ATTRIBUTE_READONLY
    };

    _BITMASK_OPS(_EXPORT_STD, perms)

    _EXPORT_STD enum class copy_options {
        none = static_cast<int>(__std_fs_copy_options::_None),

        _Existing_mask     = static_cast<int>(__std_fs_copy_options::_Existing_mask),
        skip_existing      = static_cast<int>(__std_fs_copy_options::_Skip_existing),
        overwrite_existing = static_cast<int>(__std_fs_copy_options::_Overwrite_existing),
        update_existing    = static_cast<int>(__std_fs_copy_options::_Update_existing),

        recursive = 0x10,

        _Symlinks_mask = 0xF00,
        copy_symlinks  = 0x100,
        skip_symlinks  = 0x200,

        _Copy_form_mask   = 0xF000,
        directories_only  = 0x1000,
        create_symlinks   = 0x2000,
        create_hard_links = 0x4000,

        _Unspecified_copy_prevention_tag = 0x10000 // to be removed by LWG-3057
    };

    _BITMASK_OPS(_EXPORT_STD, copy_options)

    _EXPORT_STD class file_status {
    public:
        // [fs.file_status.cons], constructors and destructor
        file_status() noexcept = default;
        explicit file_status(file_type _Ft, perms _Perms = perms::unknown) noexcept : _Myftype(_Ft), _Myperms(_Perms) {}
        file_status(const file_status&) noexcept = default;
        file_status(file_status&&) noexcept      = default;
        ~file_status() noexcept                  = default;

        // assignments
        file_status& operator=(const file_status&) noexcept = default;
        file_status& operator=(file_status&&) noexcept      = default;

        // [fs.file_status.mods], modifiers
        void type(file_type _Ft) noexcept {
            _Myftype = _Ft;
        }

        void permissions(perms _Perms) noexcept {
            _Myperms = _Perms;
        }

        // [fs.file_status.obs], observers
        _NODISCARD file_type type() const noexcept {
            return _Myftype;
        }

        _NODISCARD perms permissions() const noexcept {
            return _Myperms;
        }

#if _HAS_CXX20
        _NODISCARD friend bool operator==(const file_status& _Lhs, const file_status& _Rhs) noexcept {
            return _Lhs._Myftype == _Rhs._Myftype && _Lhs._Myperms == _Rhs._Myperms;
        }
#endif // _HAS_CXX20

        void _Refresh(const __std_win_error _Error, const __std_fs_stats& _Stats) noexcept {
            if (_Error == __std_win_error::_Success) {
                const auto _Attrs = _Stats._Attributes;

                if (_Bitmask_includes_any(_Attrs, __std_fs_file_attr::_Readonly)) {
                    this->permissions(perms::_File_attribute_readonly);
                } else {
                    this->permissions(perms::all);
                }

                if (_Bitmask_includes_any(_Attrs, __std_fs_file_attr::_Reparse_point)) {
                    if (_Stats._Reparse_point_tag == __std_fs_reparse_tag::_Symlink) {
                        this->type(file_type::symlink);
                        return;
                    }

                    if (_Stats._Reparse_point_tag == __std_fs_reparse_tag::_Mount_point) {
                        this->type(file_type::junction);
                        return;
                    }

                    // All other reparse points considered ordinary files or directories
                }

                if (_Bitmask_includes_any(_Attrs, __std_fs_file_attr::_Directory)) {
                    this->type(file_type::directory);
                } else {
                    this->type(file_type::regular);
                }

                return;
            }

            this->permissions(perms::unknown);
            this->type(__std_is_file_not_found(_Error) ? file_type::not_found : file_type::none);
        }

    private:
        file_type _Myftype = file_type::none;
        perms _Myperms     = perms::unknown;
    };

    _EXPORT_STD _NODISCARD inline bool exists(const file_status _Status) noexcept {
        // tests whether _Status indicates an existing file
        switch (_Status.type()) {
        case file_type::none:
        case file_type::not_found:
            return false;
        case file_type::regular:
        case file_type::directory:
        case file_type::symlink:
        case file_type::block:
        case file_type::character:
        case file_type::fifo:
        case file_type::socket:
        case file_type::unknown:
        case file_type::junction:
        default:
            return true;
        }
    }

    _EXPORT_STD _NODISCARD inline bool is_block_file(const file_status _Status) noexcept {
        // tests whether _Status indicates a block file
        return _Status.type() == file_type::block;
    }

    _EXPORT_STD _NODISCARD inline bool is_character_file(const file_status _Status) noexcept {
        // tests whether _Status indicates a character file
        return _Status.type() == file_type::character;
    }

    _EXPORT_STD _NODISCARD inline bool is_directory(const file_status _Status) noexcept {
        // tests whether _Status indicates a directory
        return _Status.type() == file_type::directory;
    }

    _EXPORT_STD _NODISCARD inline bool is_fifo(const file_status _Status) noexcept {
        // tests whether _Status indicates a fifo
        return _Status.type() == file_type::fifo;
    }

    _EXPORT_STD _NODISCARD inline bool is_other(const file_status _Status) noexcept {
        // tests whether _Status indicates other file types
        switch (_Status.type()) {
        case file_type::none:
        case file_type::not_found:
        case file_type::regular:
        case file_type::directory:
        case file_type::symlink:
            return false;
        case file_type::block:
        case file_type::character:
        case file_type::fifo:
        case file_type::socket:
        case file_type::unknown:
        case file_type::junction:
        default:
            return true;
        }
    }

    _EXPORT_STD _NODISCARD inline bool is_regular_file(const file_status _Status) noexcept {
        // tests whether _Status indicates a regular file
        return _Status.type() == file_type::regular;
    }

    _EXPORT_STD _NODISCARD inline bool is_socket(const file_status _Status) noexcept {
        // tests whether _Status indicates a socket
        return _Status.type() == file_type::socket;
    }

    _EXPORT_STD _NODISCARD inline bool is_symlink(const file_status _Status) noexcept {
        // tests whether _Status indicates a symlink
        return _Status.type() == file_type::symlink;
    }

    struct _File_status_and_error {
        file_status _Status;
        __std_win_error _Error;

        _NODISCARD bool _Not_good() const noexcept {
            // [fs.op.status]/Throws: result values of file_status(file_type::not_found) and
            // file_status(file_type::unknown) are not considered failures and do not cause an exception to be thrown.
            return _Error != __std_win_error::_Success && _Status.type() != file_type::not_found
                && _Status.type() != file_type::unknown;
        }
    };

    inline constexpr auto _Status_stats_flags =
        __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Follow_symlinks;

    inline constexpr auto _Symlink_status_stats_flags =
        __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Reparse_tag;

#if _HAS_CXX20
    _EXPORT_STD using file_time_type = _CHRONO time_point<_CHRONO file_clock>;
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
    using file_time_type = _CHRONO time_point<filesystem::_File_time_clock>;
#endif // ^^^ !_HAS_CXX20 ^^^

    struct _Dir_enum_impl;
    struct _Recursive_dir_enum_impl;

    _EXPORT_STD class directory_entry {
    public:
        // [fs.dir.entry.cons], constructors and destructor
        directory_entry() noexcept : _Cached_data{}, _Path() {}

        directory_entry(const directory_entry&)     = default;
        directory_entry(directory_entry&&) noexcept = default;
        explicit directory_entry(const filesystem::path& _Path_arg) : _Cached_data{}, _Path(_Path_arg) {
            refresh();
        }

        directory_entry(const filesystem::path& _Path_arg, error_code& _Ec) : _Cached_data{}, _Path(_Path_arg) {
            refresh(_Ec);
            if (_Ec) {
                _Path.clear();
            }
        }

        ~directory_entry() = default;

        // assignments
        directory_entry& operator=(const directory_entry&)     = default;
        directory_entry& operator=(directory_entry&&) noexcept = default;

        // [fs.dir.entry.mods], modifiers
        void assign(const filesystem::path& _Path_arg) {
            _Path.assign(_Path_arg);
            refresh();
        }

        void assign(const filesystem::path& _Path_arg, error_code& _Ec) {
            _Ec.clear(); // for exception safety
            _Path.assign(_Path_arg);
            refresh(_Ec);
        }

        void replace_filename(const filesystem::path& _Path_arg) {
            _Path.replace_filename(_Path_arg);
            refresh();
        }

        void replace_filename(const filesystem::path& _Path_arg, error_code& _Ec) {
            _Ec.clear(); // for exception safety
            _Path.replace_filename(_Path_arg);
            refresh(_Ec);
        }

        void refresh() {
            const auto _Error = _Refresh(_Cached_data, _Path);
            if (_Error != __std_win_error::_Success && !__std_is_file_not_found(_Error)) {
                _Throw_fs_error("directory_entry::refresh", _Error, _Path);
            }
        }

        void refresh(error_code& _Ec) noexcept {
            _Ec.clear();
            const auto _Error = _Refresh(_Cached_data, _Path);
            if (_Error != __std_win_error::_Success && !__std_is_file_not_found(_Error)) {
                _Ec = _Make_ec(_Error);
            }
        }

    private:
        _NODISCARD bool _Has_cached_attribute(const __std_fs_file_attr _Attrs) const noexcept {
            return _Bitmask_includes_any(_Cached_data._Attributes, _Attrs);
        }

    public:
        // [fs.dir.entry.obs], observers
        _NODISCARD const filesystem::path& path() const noexcept {
            return _Path;
        }

        operator const filesystem::path&() const noexcept {
            return _Path;
        }

        _NODISCARD bool exists() const {
            const auto _Type = this->status().type();
            return _Type != file_type::not_found && _Type != file_type::none;
        }

        _NODISCARD bool exists(error_code& _Ec) const noexcept {
            const auto _Type = this->status(_Ec).type();
            return _Type != file_type::not_found && _Type != file_type::none;
        }

        _NODISCARD bool is_block_file() const {
            return false;
        }

        _NODISCARD bool is_block_file(error_code& _Ec) const noexcept {
            _Ec.clear();
            return false;
        }

        _NODISCARD bool is_character_file() const {
            return false;
        }

        _NODISCARD bool is_character_file(error_code& _Ec) const noexcept {
            _Ec.clear();
            return false;
        }

        _NODISCARD bool is_directory() const {
            return _STD filesystem::is_directory(this->status());
        }

        _NODISCARD bool is_directory(error_code& _Ec) const noexcept {
            return _STD filesystem::is_directory(this->status(_Ec));
        }

        _NODISCARD bool is_fifo() const {
            return false;
        }

        _NODISCARD bool is_fifo(error_code& _Ec) const noexcept {
            _Ec.clear();
            return false;
        }

        _NODISCARD bool is_other() const {
            return _STD filesystem::is_other(this->status());
        }

        _NODISCARD bool is_other(error_code& _Ec) const noexcept {
            return _STD filesystem::is_other(this->status(_Ec));
        }

        _NODISCARD bool is_regular_file() const {
            return _STD filesystem::is_regular_file(this->status());
        }

        _NODISCARD bool is_regular_file(error_code& _Ec) const noexcept {
            return _STD filesystem::is_regular_file(this->status(_Ec));
        }

        _NODISCARD bool is_socket() const {
            return false;
        }

        _NODISCARD bool is_socket(error_code& _Ec) const noexcept {
            _Ec.clear();
            return false;
        }

        _NODISCARD bool _Is_symlink_or_junction() const noexcept {
            return _Has_cached_attribute(__std_fs_file_attr::_Reparse_point)
                && (_Cached_data._Reparse_point_tag == __std_fs_reparse_tag::_Symlink
                    || _Cached_data._Reparse_point_tag == __std_fs_reparse_tag::_Mount_point);
        }

        _NODISCARD bool is_symlink() const {
            return _STD filesystem::is_symlink(this->symlink_status());
        }

        _NODISCARD bool is_symlink(error_code& _Ec) const noexcept {
            return _STD filesystem::is_symlink(this->symlink_status(_Ec));
        }

        _NODISCARD bool _Available(const __std_fs_stats_flags _Flags) const noexcept {
            return _Bitmask_includes_any(_Cached_data._Available, _Flags);
        }

    private:
        _NODISCARD __std_win_error _File_size(uintmax_t& _Result) const noexcept {
            if (_Available(__std_fs_stats_flags::_File_size)) {
                _Result = _Cached_data._File_size;
                return __std_win_error::_Success;
            }

            __std_fs_stats _Stats;
            constexpr auto _Flags = __std_fs_stats_flags::_File_size | __std_fs_stats_flags::_Follow_symlinks;
            const auto _Error =
                __std_fs_get_stats(_Path.c_str(), &_Stats, _Flags, _Cached_data._Symlink_hint_attributes());
            if (_Error == __std_win_error::_Success) {
                _Result = _Stats._File_size;
            } else {
                _Result = static_cast<uintmax_t>(-1);
            }

            return _Error;
        }

    public:
        _NODISCARD uintmax_t file_size() const {
            uintmax_t _Result;
            const auto _Error = _File_size(_Result);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("directory_entry::file_size", _Error, _Path);
            }

            return _Result;
        }

        _NODISCARD uintmax_t file_size(error_code& _Ec) const noexcept {
            uintmax_t _Result;
            _Ec = _Make_ec(_File_size(_Result));
            return _Result;
        }

    private:
        _NODISCARD __std_win_error _Hard_link_count(uintmax_t& _Result) const noexcept {
            if (_Available(__std_fs_stats_flags::_Link_count)) {
                _Result = _Cached_data._Link_count;
                return __std_win_error::_Success;
            }

            __std_fs_stats _Stats;
            constexpr auto _Flags = __std_fs_stats_flags::_Link_count | __std_fs_stats_flags::_Follow_symlinks;
            const auto _Error =
                __std_fs_get_stats(_Path.c_str(), &_Stats, _Flags, _Cached_data._Symlink_hint_attributes());
            if (_Error == __std_win_error::_Success) {
                _Result = _Stats._Link_count;
            } else {
                _Result = static_cast<uintmax_t>(-1);
            }

            return _Error;
        }

    public:
        _NODISCARD uintmax_t hard_link_count() const {
            uintmax_t _Result;
            const auto _Error = _Hard_link_count(_Result);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("directory_entry::hard_link_count", _Error, _Path);
            }

            return _Result;
        }

        _NODISCARD uintmax_t hard_link_count(error_code& _Ec) const noexcept {
            uintmax_t _Result;
            _Ec = _Make_ec(_Hard_link_count(_Result));
            return _Result;
        }

    private:
        _NODISCARD __std_win_error _Last_write_time(file_time_type& _Result) const noexcept {
            if (_Available(__std_fs_stats_flags::_Last_write_time)) {
                _Result = file_time_type{file_time_type::duration{_Cached_data._Last_write_time}};
                return __std_win_error::_Success;
            }

            __std_fs_stats _Stats;
            constexpr auto _Flags = __std_fs_stats_flags::_Last_write_time | __std_fs_stats_flags::_Follow_symlinks;
            const auto _Error =
                __std_fs_get_stats(_Path.c_str(), &_Stats, _Flags, _Cached_data._Symlink_hint_attributes());
            if (_Error == __std_win_error::_Success) {
                _Result = file_time_type{file_time_type::duration{_Stats._Last_write_time}};
            } else {
                _Result = file_time_type{file_time_type::duration{LLONG_MIN}};
            }

            return _Error;
        }

    public:
        _NODISCARD file_time_type last_write_time() const {
            file_time_type _Result;
            const auto _Error = _Last_write_time(_Result);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("directory_entry::last_write_time", _Error, _Path);
            }

            return _Result;
        }

        _NODISCARD file_time_type last_write_time(error_code& _Ec) const noexcept {
            file_time_type _Result;
            _Ec = _Make_ec(_Last_write_time(_Result));
            return _Result;
        }

    private:
        _NODISCARD _File_status_and_error _Get_any_status(const __std_fs_stats_flags _Flags) const noexcept {
            _File_status_and_error _Result;
            __std_fs_stats _Stats;

            if (_Bitmask_includes_all(_Cached_data._Available, _Flags)) {
                _Result._Error = __std_win_error::_Success;
                _Result._Status._Refresh(__std_win_error::_Success, _Cached_data);
            } else {
                const auto _Error =
                    __std_fs_get_stats(_Path.c_str(), &_Stats, _Flags, _Cached_data._Symlink_hint_attributes());
                _Result._Error = _Error;
                _Result._Status._Refresh(_Error, _Stats);
            }

            return _Result;
        }

    public:
        _NODISCARD file_status status() const {
            const auto _Result = _Get_any_status(_Status_stats_flags);
            if (_Result._Not_good()) {
                _Throw_fs_error("directory_entry::status", _Result._Error, _Path);
            }

            return _Result._Status;
        }

        _NODISCARD file_status status(error_code& _Ec) const noexcept {
            const auto _Result = _Get_any_status(_Status_stats_flags);
            _Ec                = _Make_ec(_Result._Error);
            return _Result._Status;
        }

        _NODISCARD file_status symlink_status() const {
            const auto _Result = _Get_any_status(_Symlink_status_stats_flags);
            if (_Result._Not_good()) {
                _Throw_fs_error("directory_entry::symlink_status", _Result._Error, _Path);
            }

            return _Result._Status;
        }

        _NODISCARD file_status symlink_status(error_code& _Ec) const noexcept {
            const auto _Result = _Get_any_status(_Symlink_status_stats_flags);
            _Ec                = _Make_ec(_Result._Error);
            return _Result._Status;
        }

        _NODISCARD bool operator==(const directory_entry& _Rhs) const noexcept {
            return _Path == _Rhs._Path;
        }

#if _HAS_CXX20
        _NODISCARD strong_ordering operator<=>(const directory_entry& _Rhs) const noexcept {
            return _Path <=> _Rhs._Path;
        }
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
        _NODISCARD bool operator!=(const directory_entry& _Rhs) const noexcept {
            return _Path != _Rhs._Path;
        }

        _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept {
            return _Path < _Rhs._Path;
        }

        _NODISCARD bool operator<=(const directory_entry& _Rhs) const noexcept {
            return _Path <= _Rhs._Path;
        }

        _NODISCARD bool operator>(const directory_entry& _Rhs) const noexcept {
            return _Path > _Rhs._Path;
        }

        _NODISCARD bool operator>=(const directory_entry& _Rhs) const noexcept {
            return _Path >= _Rhs._Path;
        }
#endif // ^^^ !_HAS_CXX20 ^^^

        // [fs.dir.entry.io], inserter
        template <class _Elem, class _Traits>
        friend _STD basic_ostream<_Elem, _Traits>& operator<<( // TRANSITION, VSO-570323
            _STD basic_ostream<_Elem, _Traits>& _Ostr, const directory_entry& _Entry) { // TRANSITION, VSO-570323
            // insert a directory_entry into a stream
            return _Ostr << _Entry.path();
        }

    private:
        void _Refresh(const __std_fs_find_data& _Data) noexcept {
            _Cached_data._Attributes        = _Data._Attributes;
            _Cached_data._Reparse_point_tag = _Data._Reparse_point_tag;
            _Cached_data._Available         = __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Reparse_tag;
            if (!_Bitmask_includes_any(_Data._Attributes, __std_fs_file_attr::_Reparse_point)) {
                _Cached_data._File_size = (static_cast<uintmax_t>(_Data._File_size_high) << 32)
                                        + static_cast<uintmax_t>(_Data._File_size_low);
                _CSTD memcpy(
                    &_Cached_data._Last_write_time, &_Data._Last_write_time, sizeof(_Cached_data._Last_write_time));
                _Cached_data._Available |= __std_fs_stats_flags::_File_size | __std_fs_stats_flags::_Last_write_time;
            }
        }

        _NODISCARD static __std_win_error _Refresh(__std_fs_stats& _Stats, const filesystem::path& _Path) noexcept {
            const auto _Error = __std_fs_get_stats(_Path.c_str(), &_Stats, __std_fs_stats_flags::_All_data);
            if (_Error == __std_win_error::_Success) {
                if (_Bitmask_includes_any(_Stats._Attributes, __std_fs_file_attr::_Reparse_point)) {
                    _Stats._Available = __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Reparse_tag;
                } else {
                    _Stats._Available = __std_fs_stats_flags::_All_data;
                }
            } else {
                _Stats._Available = __std_fs_stats_flags::_None;
            }

            return _Error;
        }

        friend _Dir_enum_impl;
        friend _Recursive_dir_enum_impl;
        friend void _Copy_impl(
            const directory_entry& _From, const _STD filesystem::path& _To, copy_options _Options, error_code& _Ec);

        __std_fs_stats _Cached_data;
        filesystem::path _Path;
    };

    _EXPORT_STD enum class directory_options { none = 0, follow_directory_symlink = 1, skip_permission_denied = 2 };
    _BITMASK_OPS(_EXPORT_STD, directory_options)

    _EXPORT_STD _NODISCARD inline bool exists(const path& _Target, error_code& _Ec) noexcept;

    struct _Dir_enum_impl {
        _NODISCARD static __std_win_error _Advance_and_reset_if_no_more_files(shared_ptr<_Dir_enum_impl>& _Ptr) {
            auto& _Impl = *_Ptr;
            __std_fs_find_data _Data;
            do {
                const auto _Error = __std_fs_directory_iterator_advance(_Impl._Dir._Handle, &_Data);
                if (_Error == __std_win_error::_No_more_files) {
                    _Ptr.reset();
                    return __std_win_error::_Success;
                }

                if (_Error != __std_win_error::_Success) {
                    return _Error;
                }
            } while (_Is_dot_or_dotdot(_Data));
            _Impl._Refresh(_Data); // can throw
            return __std_win_error::_Success;
        }

        _NODISCARD static __std_win_error _Skip_dots(
            __std_fs_dir_handle _Dir_handle, __std_fs_find_data& _Data) noexcept {
            while (_Is_dot_or_dotdot(_Data)) {
                const auto _Error = __std_fs_directory_iterator_advance(_Dir_handle, &_Data);
                if (_Error != __std_win_error::_Success) {
                    return _Error;
                }
            }

            return __std_win_error::_Success;
        }

        _NODISCARD static __std_win_error _Open_dir(
            path& _Path, const directory_options _Options_arg, _Find_file_handle& _Dir, __std_fs_find_data& _Data) {
            const size_t _Null_term_len = _CSTD wcslen(_Path.c_str());
            if (_Null_term_len == 0 || _Null_term_len != _Path.native().size()) {
                return __std_win_error::_File_not_found;
            }

            const path _Original_path = _Path;
            _Path /= L"*"sv;
            auto _Error = _Dir._Open(_Path.c_str(), &_Data);
            if (_Error == __std_win_error::_Success) {
                return _Skip_dots(_Dir._Handle, _Data);
            }

            if (_Error == __std_win_error::_Access_denied
                && _Bitmask_includes_any(_Options_arg, directory_options::skip_permission_denied)) {
                _Error = __std_win_error::_No_more_files;
            } else if (_Error == __std_win_error::_File_not_found) {
                error_code _Ignored; // When exists() returns true, that implies that the error_code is successful.
                // When exists() returns false, we don't want to interfere with _Open_dir()'s behavior,
                // as it's going to return __std_win_error::_File_not_found.
                if (_STD filesystem::exists(_Original_path, _Ignored)) {
                    _Error = __std_win_error::_No_more_files; // Handle empty volumes, see GH-4291
                }
            }

            return _Error;
        }

        struct _Creator { // factored out part common to recursive and non-recursive implementation
            path _Path;
            _Find_file_handle _Dir;
            __std_fs_find_data _Find_data;

            struct _Create_status {
                bool _Should_create_impl;
                __std_win_error _Error;
            };

            _Create_status _Status;

            _Creator(const path& _Path_arg, const directory_options _Options) : _Path(_Path_arg) {
                const auto _Error = _Open_dir(_Path, _Options, _Dir, _Find_data);
                if (_Error == __std_win_error::_Success) {
                    _Status = {true, __std_win_error::_Success};
                } else if (_Error == __std_win_error::_No_more_files) {
                    _Status = {false, __std_win_error::_Success};
                } else {
                    _Status = {false, _Error};
                }
            }
        };

        void _Refresh(const __std_fs_find_data& _Data) {
            _Entry._Refresh(_Data);
            _Entry._Path.replace_filename(wstring_view{_Data._File_name});
        }

        explicit _Dir_enum_impl(_Creator&& _Create_data, const directory_options = {})
            : _Dir(_STD move(_Create_data._Dir)) {
            // directory_options provided, but unused to keep signature identical to recursive_directory_iterator
            _Entry._Path = _STD move(_Create_data._Path);
            _Refresh(_Create_data._Find_data);
        }

        template <class _Dir_enum_kind>
        _NODISCARD static __std_win_error _Initialize_dir_enum(
            shared_ptr<_Dir_enum_kind>& _Impl, const path& _Path, const directory_options _Options = {}) {
            _Creator _Create_data(_Path, _Options);
            if (_Create_data._Status._Should_create_impl) {
                _Impl = _STD make_shared<_Dir_enum_kind>(_STD move(_Create_data), _Options);
            }
            return _Create_data._Status._Error;
        }

        directory_entry _Entry;
        _Find_file_handle _Dir;
    };

    _EXPORT_STD class directory_iterator;
    _EXPORT_STD class recursive_directory_iterator;

    struct _Directory_entry_proxy {
        _NODISCARD directory_entry operator*() && noexcept {
            return _STD move(_Entry);
        }

    private:
        friend directory_iterator;
        friend recursive_directory_iterator;

        explicit _Directory_entry_proxy(const directory_entry& _Entry_arg) : _Entry(_Entry_arg) {}

        directory_entry _Entry;
    };

    _EXPORT_STD class directory_iterator {
    public:
        using iterator_category = input_iterator_tag;
        using value_type        = directory_entry;
        using difference_type   = ptrdiff_t;
        using pointer           = const directory_entry*;
        using reference         = const directory_entry&;

        // [fs.dir.itr.members], member functions
        directory_iterator() noexcept = default;
        explicit directory_iterator(const path& _Path) {
            const auto _Error = _Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("directory_iterator::directory_iterator", _Error, _Path);
            }
        }

        directory_iterator(const path& _Path, const directory_options _Options) {
            const auto _Error = _Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path, _Options);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("directory_iterator::directory_iterator", _Error, _Path);
            }
        }

        directory_iterator(const path& _Path, error_code& _Ec) {
            _Ec = _Make_ec(_Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path));
        }

        directory_iterator(const path& _Path, const directory_options _Options, error_code& _Ec) {
            _Ec = _Make_ec(_Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path, _Options));
        }

        directory_iterator(const directory_iterator&) noexcept = default; // strengthened
        directory_iterator(directory_iterator&&) noexcept      = default;
        ~directory_iterator() noexcept                         = default;

        directory_iterator& operator=(const directory_iterator&) noexcept = default; // strengthened
        directory_iterator& operator=(directory_iterator&&) noexcept      = default;

        _NODISCARD const directory_entry& operator*() const noexcept /* strengthened */ {
            return _Impl->_Entry;
        }

        _NODISCARD const directory_entry* operator->() const noexcept /* strengthened */ {
            return &**this;
        }

        directory_iterator& operator++() {
            const auto _Error = _Dir_enum_impl::_Advance_and_reset_if_no_more_files(_Impl);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("directory_iterator::operator++", _Error);
            }

            return *this;
        }

        directory_iterator& increment(error_code& _Ec) {
            _Ec = _Make_ec(_Dir_enum_impl::_Advance_and_reset_if_no_more_files(_Impl));
            return *this;
        }

        // other members as required by [input.iterators]:
        _NODISCARD bool operator==(const directory_iterator& _Rhs) const noexcept /* strengthened */ {
            return _Impl == _Rhs._Impl;
        }

#if !_HAS_CXX20
        _NODISCARD bool operator!=(const directory_iterator& _Rhs) const noexcept /* strengthened */ {
            return _Impl != _Rhs._Impl;
        }
#endif // !_HAS_CXX20

#if _HAS_CXX20
        _NODISCARD bool operator==(default_sentinel_t) const noexcept {
            return !_Impl;
        }
#endif // _HAS_CXX20

        _Directory_entry_proxy operator++(int) {
            _Directory_entry_proxy _Proxy(**this);
            ++*this;
            return _Proxy;
        }

        _NODISCARD bool _At_end() const noexcept {
            return !_Impl;
        }

    private:
        shared_ptr<_Dir_enum_impl> _Impl;
    };

    _EXPORT_STD _NODISCARD inline directory_iterator begin(directory_iterator _Iter) noexcept {
        return _Iter;
    }

    _EXPORT_STD _NODISCARD inline directory_iterator end(directory_iterator) noexcept {
        return {};
    }

    struct _Should_recurse_result {
        bool _Should_recurse;
        __std_win_error _Error;
    };

    struct _Recursive_dir_enum_impl : _Dir_enum_impl {
        vector<_Find_file_handle> _Stack;
        directory_options _Options = {};
        bool _Recursion_pending    = true;

        _NODISCARD _Should_recurse_result _Should_recurse() const noexcept {
            bool _Should_recurse   = false;
            __std_win_error _Error = __std_win_error::_Success;
            if (_Recursion_pending) {
                if (_Entry._Is_symlink_or_junction()) {
                    if (_Bitmask_includes_any(_Options, directory_options::follow_directory_symlink)) {
                        // check for broken symlink/junction
                        __std_fs_stats _Target_stats;
                        constexpr auto _Flags =
                            __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Follow_symlinks;
                        _Error = __std_fs_get_stats(
                            _Entry._Path.c_str(), &_Target_stats, _Flags, _Entry._Cached_data._Attributes);
                        if (_Error == __std_win_error::_Success) {
                            _Should_recurse =
                                _Bitmask_includes_any(_Target_stats._Attributes, __std_fs_file_attr::_Directory);
                        } else if (__std_is_file_not_found(_Error)
                                   || (_Error == __std_win_error::_Access_denied
                                       && _Bitmask_includes_any(_Options, directory_options::skip_permission_denied))) {
                            // skip broken symlinks and permission denied (when configured)
                            _Error = __std_win_error::_Success;
                        }
                    }
                } else {
                    _Should_recurse = _Entry._Has_cached_attribute(__std_fs_file_attr::_Directory);
                }
            }

            return {_Should_recurse, _Error};
        }

        _NODISCARD __std_win_error _Advance_and_skip_dots(__std_fs_find_data& _Data) noexcept {
            const auto _Error = __std_fs_directory_iterator_advance(_Dir._Handle, &_Data);
            if (_Error != __std_win_error::_Success) {
                return _Error;
            }

            return _Skip_dots(_Dir._Handle, _Data);
        }

        _NODISCARD static __std_win_error _Pop_and_reset_if_no_more_files(shared_ptr<_Recursive_dir_enum_impl>& _Ptr) {
            __std_win_error _Error;
            auto& _Impl = *_Ptr;
            __std_fs_find_data _Data;

            _Impl._Recursion_pending = true;

            do {
                if (_Impl._Stack.empty()) {
                    _Error = __std_win_error::_Success;
                    break;
                }

                _Impl._Dir = _STD move(_Impl._Stack.back());
                _Impl._Stack.pop_back();
                _Impl._Entry._Path._Remove_filename_and_separator();
                _Error = _Impl._Advance_and_skip_dots(_Data);

                if (_Error == __std_win_error::_Success) {
                    _Impl._Refresh(_Data);
                    return __std_win_error::_Success;
                }
            } while (_Error == __std_win_error::_No_more_files);

            _Ptr.reset();
            return _Error;
        }

        _NODISCARD static __std_win_error _Advance_and_reset_if_no_more_files(
            shared_ptr<_Recursive_dir_enum_impl>& _Ptr) {
            auto& _Impl = *_Ptr;
            __std_fs_find_data _Data;
            auto [_Should_recurse, _Error] = _Impl._Should_recurse();
            if (_Error != __std_win_error::_Success) {
                _Ptr.reset();
                return _Error;
            }

            if (_Should_recurse) {
                _Impl._Stack.push_back(_STD move(_Impl._Dir));
                _Error = _Open_dir(_Impl._Entry._Path, _Impl._Options, _Impl._Dir, _Data);
            } else {
                _Error = _Impl._Advance_and_skip_dots(_Data);
            }

            _Impl._Recursion_pending = true;
            for (;; _Error = _Impl._Advance_and_skip_dots(_Data)) {
                if (_Error == __std_win_error::_Success) {
                    _Impl._Refresh(_Data);
                    return __std_win_error::_Success;
                }

                if (_Error != __std_win_error::_No_more_files) {
                    break;
                }

                // no more files at this level, see if we can pop
                if (_Impl._Stack.empty()) { // nothing to pop, clear the error, reset and return
                    _Error = __std_win_error::_Success;
                    break;
                }

                _Impl._Dir = _STD move(_Impl._Stack.back());
                _Impl._Stack.pop_back();
                _Impl._Entry._Path._Remove_filename_and_separator();
            }

            _Ptr.reset();
            return _Error;
        }

        _Recursive_dir_enum_impl(_Dir_enum_impl::_Creator&& _Create_data, const directory_options _Options_arg)
            : _Dir_enum_impl(_STD move(_Create_data)), _Options(_Options_arg) {}
    };

    _EXPORT_STD class recursive_directory_iterator {
    public:
        using iterator_category = input_iterator_tag;
        using value_type        = directory_entry;
        using difference_type   = ptrdiff_t;
        using pointer           = const directory_entry*;
        using reference         = const directory_entry&;

        // [fs.rec.dir.itr.members], constructors and destructor

        recursive_directory_iterator() noexcept = default;
        explicit recursive_directory_iterator(const path& _Path) {
            const auto _Error = _Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("recursive_directory_iterator::recursive_directory_iterator", _Error, _Path);
            }
        }

        recursive_directory_iterator(const path& _Path, const directory_options _Options) {
            const auto _Error = _Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path, _Options);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("recursive_directory_iterator::recursive_directory_iterator", _Error, _Path);
            }
        }

        recursive_directory_iterator(const path& _Path, const directory_options _Options, error_code& _Ec) {
            _Ec = _Make_ec(_Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path, _Options));
        }

        recursive_directory_iterator(const path& _Path, error_code& _Ec) {
            _Ec = _Make_ec(_Dir_enum_impl::_Initialize_dir_enum(_Impl, _Path));
        }

        recursive_directory_iterator(const recursive_directory_iterator&) noexcept = default; // strengthened
        recursive_directory_iterator(recursive_directory_iterator&&) noexcept      = default;
        ~recursive_directory_iterator() noexcept                                   = default;

        // [fs.rec.dir.itr.members], observers
        _NODISCARD directory_options options() const noexcept /* strengthened */ {
            return _Impl->_Options;
        }
        _NODISCARD int depth() const noexcept /* strengthened */ {
            // NT uses uint32_t to store the length of the path
            // that allows us 2^31 wchar_t per path.
            // A directory name should be at least 1 character, otherwise
            // adjacent directory separators will be treated as one.
            // Hence, we can only get to 2^30 entries in the stack.
            return static_cast<int>(_Impl->_Stack.size());
        }
        _NODISCARD bool recursion_pending() const noexcept /* strengthened */ {
            return _Impl->_Recursion_pending;
        }

        _NODISCARD const directory_entry& operator*() const noexcept /* strengthened */ {
            return _Impl->_Entry;
        }

        _NODISCARD const directory_entry* operator->() const noexcept /* strengthened */ {
            return &**this;
        }

        // [fs.rec.dir.itr.members], modifiers
        recursive_directory_iterator& operator=(recursive_directory_iterator&&) noexcept      = default;
        recursive_directory_iterator& operator=(const recursive_directory_iterator&) noexcept = default; // strengthened

        recursive_directory_iterator& operator++() {
            const auto _Error = _Recursive_dir_enum_impl::_Advance_and_reset_if_no_more_files(_Impl);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("recursive_directory_iterator::operator++", _Error);
            }
            return *this;
        }

        recursive_directory_iterator& increment(error_code& _Ec) {
            _Ec = _Make_ec(_Recursive_dir_enum_impl::_Advance_and_reset_if_no_more_files(_Impl));
            return *this;
        }

        void pop() {
            const auto _Error = _Recursive_dir_enum_impl::_Pop_and_reset_if_no_more_files(_Impl);
            if (_Error != __std_win_error::_Success) {
                _Throw_fs_error("recursive_directory_iterator::pop", _Error);
            }
        }

        void pop(error_code& _Ec) {
            _Ec = _Make_ec(_Recursive_dir_enum_impl::_Pop_and_reset_if_no_more_files(_Impl));
        }

        void disable_recursion_pending() noexcept {
            _Impl->_Recursion_pending = false;
        }

        // other members as required by [input.iterators]:
        _NODISCARD bool operator==(const recursive_directory_iterator& _Rhs) const noexcept {
            return _Impl == _Rhs._Impl;
        }

#if !_HAS_CXX20
        _NODISCARD bool operator!=(const recursive_directory_iterator& _Rhs) const noexcept {
            return _Impl != _Rhs._Impl;
        }
#endif // !_HAS_CXX20

#if _HAS_CXX20
        _NODISCARD bool operator==(default_sentinel_t) const noexcept {
            return !_Impl;
        }
#endif // _HAS_CXX20

        _Directory_entry_proxy operator++(int) {
            _Directory_entry_proxy _Proxy(**this);
            ++*this;
            return _Proxy;
        }

    private:
        shared_ptr<_Recursive_dir_enum_impl> _Impl;
    };

    _EXPORT_STD _NODISCARD inline recursive_directory_iterator begin(recursive_directory_iterator _Iter) noexcept {
        return _Iter;
    }

    _EXPORT_STD _NODISCARD inline recursive_directory_iterator end(recursive_directory_iterator) noexcept {
        return {};
    }

    _EXPORT_STD _NODISCARD inline path absolute(const path& _Input, error_code& _Ec) {
        // normalize path according to system semantics, without touching the disk
        // calls GetFullPathNameW
        _Ec.clear(); // for exception safety
        path _Result;
        if (!_Input._Text.empty()) {
            _Result._Text.resize(__std_fs_max_path);
            for (;;) {
                const auto _Requested_size = static_cast<unsigned long>(_Result._Text.size());
                const auto _Full_path_result =
                    __std_fs_get_full_path_name(_Input._Text.c_str(), _Requested_size, _Result._Text.data());
                _Result._Text.resize(_Full_path_result._Size);
                if (_Full_path_result._Size < _Requested_size) {
                    _Ec = _Make_ec(_Full_path_result._Error);
                    break;
                }
            }
        }
        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path absolute(const path& _Input) {
        // normalize path according to system semantics, without touching the disk
        // calls GetFullPathNameW
        error_code _Ec;
        path _Result(_STD filesystem::absolute(_Input, _Ec));
        if (_Ec) {
            _Throw_fs_error("absolute", _Ec, _Input);
        }
        return _Result;
    }

    _NODISCARD inline __std_win_error _Canonical(path& _Result, const wstring& _Text) { // pre: _Result.empty()
        if (_Text.empty()) {
            return __std_win_error::_Success;
        }

        auto _Name_kind = __std_fs_volume_name_kind::_Dos;
        {
            __std_win_error _Err;
            const _Fs_file _Handle(_Text.c_str(), __std_access_rights::_File_read_attributes,
                __std_fs_file_flags::_Backup_semantics, &_Err);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }

            _Result._Text.resize(__std_fs_max_path);
            for (;;) {
                const auto _Requested_size    = static_cast<unsigned long>(_Result._Text.size());
                const auto _Final_path_result = __std_fs_get_final_path_name_by_handle(
                    _Handle._Raw, _Result._Text.data(), _Requested_size, _Name_kind);
                _Err = _Final_path_result._Error;
                if (_Final_path_result._Size == 0) {
                    if (_Err == __std_win_error::_Path_not_found && _Name_kind == __std_fs_volume_name_kind::_Dos) {
                        // maybe there is no DOS name for the supplied path, retry with NT path
                        _Name_kind = __std_fs_volume_name_kind::_Nt;
                        continue;
                    }

                    _Result._Text.clear();
                    return _Err;
                }

                _Result._Text.resize(_Final_path_result._Size);
                if (_Final_path_result._Size < _Requested_size) {
                    break;
                }
            }
        } // close _Handle

        if (_Name_kind == __std_fs_volume_name_kind::_Dos) {
            if (_Is_drive_prefix_with_slash_slash_question(_Result._Text)) {
                // the result contains a \\?\ prefix but is just a drive letter, strip the \\?\ prefix
                _Result._Text.erase(0, 4);
            } else if (_Result._Text._Starts_with(LR"(\\?\UNC\)"sv)) {
                // the result contains a \\?\UNC\ prefix, replace with the simpler \\ prefix
                _Result._Text.erase(2, 6); // chop out ?\UNC\ leaving two preferred-separators
            }
        } else { // result is in the NT namespace, so apply the DOS to NT namespace prefix
            _Result._Text.insert(0, LR"(\\?\GLOBALROOT)"sv);
        }

        return __std_win_error::_Success;
    }

    _EXPORT_STD _NODISCARD inline path canonical(const path& _Input) {
        // resolve the final path according to system semantics, by opening the file
        // calls GetFinalPathNameByHandleW
        path _Result;
        const auto _Err = _Canonical(_Result, _Input.native());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("canonical", _Err, _Input);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path canonical(const path& _Input, error_code& _Ec) {
        // resolve the final path according to system semantics, by opening the file
        // calls GetFinalPathNameByHandleW
        _Ec.clear(); // for exception safety
        path _Result;
        _Ec = _Make_ec(_Canonical(_Result, _Input.native()));
        return _Result;
    }

    _NODISCARD inline unique_ptr<wchar_t[]> _Get_cleaned_symlink_target(const path& _To) noexcept {
        // transforms /s in the root-name to \s, and all other directory-separators into single \s
        // example: a/\/b -> a\b
        // example: //server/a////////b////////c////////d -> \\server\a\b\c\d
        const auto& _To_str = _To.native();
        // protected from overflow by wstring's max_size cap:
        unique_ptr<wchar_t[]> _Cleaned_link(::new (nothrow) wchar_t[_To_str.size() + 1]);
        if (!_Cleaned_link) {
            return _Cleaned_link;
        }

        const auto _First = _To_str.c_str();
        const auto _Last  = _First + _To_str.size();
        auto _Next        = _Find_root_name_end(_First, _Last);
        auto _Dest        = _STD replace_copy_if(_First, _Next, _Cleaned_link.get(), _Is_slash, L'\\');
        for (;;) {
            const wchar_t _Ch = *_Next;
            if (_Is_slash(_Ch)) {
                *_Dest = L'\\';
                do {
                    ++_Next;
                } while (_Is_slash(*_Next));
            } else {
                *_Dest = _Ch;
                if (_Ch == L'\0') {
                    break;
                }

                ++_Next;
            }

            ++_Dest;
        }

        return _Cleaned_link;
    }

    _EXPORT_STD inline void create_directory_symlink(const path& _To, const path& _New_symlink) {
        // create a symlink for a directory, _New_symlink -> _To
        const auto _Cleaned = _Get_cleaned_symlink_target(_To);
        if (!_Cleaned) {
            _Xbad_alloc();
        }

        // note reversed parameter order:
        const auto _Err = __std_fs_create_directory_symbolic_link(_New_symlink.c_str(), _Cleaned.get());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("create_directory_symlink", _Err, _To, _New_symlink);
        }
    }

    _EXPORT_STD inline void create_directory_symlink(
        const path& _To, const path& _New_symlink, error_code& _Ec) noexcept {
        // create a symlink for a directory, _New_symlink -> _To
        const auto _Cleaned = _Get_cleaned_symlink_target(_To);
        if (_Cleaned) {
            // note reversed parameter order:
            _Ec = _Make_ec(__std_fs_create_directory_symbolic_link(_New_symlink.c_str(), _Cleaned.get()));
        } else {
            _Ec = _STD make_error_code(errc::not_enough_memory);
        }
    }

    _EXPORT_STD inline void create_hard_link(const path& _To, const path& _New_hard_link) {
        // create a hard link for a file, _New_hard_link -> _To
        // note reversed parameter order:
        const auto _Err = __std_fs_create_hard_link(_New_hard_link.c_str(), _To.c_str());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("create_hard_link", _Err, _To, _New_hard_link);
        }
    }

    _EXPORT_STD inline void create_hard_link(const path& _To, const path& _New_hard_link, error_code& _Ec) noexcept {
        // create a hard link for a file, _New_hard_link -> _To
        // note reversed parameter order:
        _Ec = _Make_ec(__std_fs_create_hard_link(_New_hard_link.c_str(), _To.c_str()));
    }

    _EXPORT_STD inline void create_symlink(const path& _To, const path& _New_symlink) {
        // create a symlink for a file, _New_symlink -> _To
        const auto _Cleaned = _Get_cleaned_symlink_target(_To);
        if (!_Cleaned) {
            _Xbad_alloc();
        }

        // note reversed parameter order:
        const auto _Err = __std_fs_create_symbolic_link(_New_symlink.c_str(), _Cleaned.get());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("create_symlink", _Err, _To, _New_symlink);
        }
    }

    _EXPORT_STD inline void create_symlink(const path& _To, const path& _New_symlink, error_code& _Ec) noexcept {
        // create a symlink for a file, _New_symlink -> _To
        const auto _Cleaned = _Get_cleaned_symlink_target(_To);
        if (_Cleaned) {
            // note reversed parameter order:
            _Ec = _Make_ec(__std_fs_create_symbolic_link(_New_symlink.c_str(), _Cleaned.get()));
        } else {
            _Ec = _STD make_error_code(errc::not_enough_memory);
        }
    }

    _NODISCARD inline __std_win_error _Read_reparse_data(
        const _Fs_file& _Handle, unique_ptr<char[]>& _Buffer_unique_ptr) noexcept {
        constexpr auto _Buffer_size = 16 * 1024 + sizeof(wchar_t); // MAXIMUM_REPARSE_DATA_BUFFER_SIZE + sizeof(wchar_t)

        _Buffer_unique_ptr.reset(::new (nothrow) char[_Buffer_size]);
        if (!_Buffer_unique_ptr) {
            return __std_win_error::_Not_enough_memory;
        }

        const auto _Buffer         = reinterpret_cast<__std_fs_reparse_data_buffer*>(_Buffer_unique_ptr.get());
        const __std_win_error _Err = __std_fs_read_reparse_data_buffer(_Handle._Raw, _Buffer, _Buffer_size);
        if (_Err != __std_win_error::_Success) {
            return _Err;
        }

        return __std_win_error::_Success;
    }

    _NODISCARD inline __std_win_error _Read_symlink(const path& _Symlink_path, path& _Result) {
        __std_win_error _Err;
        unique_ptr<char[]> _Buffer_unique_ptr;
        {
            const _Fs_file _Handle(_Symlink_path.c_str(), __std_access_rights::_File_read_attributes,
                __std_fs_file_flags::_Backup_semantics | __std_fs_file_flags::_Open_reparse_point, &_Err);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }

            _Err = _Read_reparse_data(_Handle, _Buffer_unique_ptr);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }
        } // Close _Handle

        const auto _Buffer = reinterpret_cast<__std_fs_reparse_data_buffer*>(_Buffer_unique_ptr.get());
        unsigned short _Length;
        wchar_t* _Offset;
        _Err = __std_fs_read_name_from_reparse_data_buffer(_Buffer, &_Offset, &_Length);
        if (_Err != __std_win_error::_Success) {
            return _Err;
        }

        _Result._Text.assign(_Offset, _Length);

        return __std_win_error::_Success;
    }

    _EXPORT_STD _NODISCARD inline path read_symlink(const path& _Symlink_path, error_code& _Ec) {
        _Ec.clear();
        path _Result;
        _Ec = _Make_ec(_Read_symlink(_Symlink_path, _Result));
        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path read_symlink(const path& _Symlink_path) {
        path _Result;
        const auto _Err = _Read_symlink(_Symlink_path, _Result);
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("read_symlink", _Err, _Symlink_path);
        }
        return _Result;
    }

    _NODISCARD inline __std_win_error _Copy_symlink(const path& _Symlink, const path& _New_symlink) noexcept {
        __std_win_error _Err;
        unique_ptr<char[]> _Buffer_unique_ptr;
        bool _Is_directory;
        {
            const _Fs_file _Handle(_Symlink.c_str(), __std_access_rights::_File_read_attributes,
                __std_fs_file_flags::_Backup_semantics | __std_fs_file_flags::_Open_reparse_point, &_Err);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }

            _Err = _Read_reparse_data(_Handle, _Buffer_unique_ptr);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }

            unsigned long _File_attributes;
            _Err = __std_fs_get_file_attributes_by_handle(_Handle._Raw, &_File_attributes);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }

            _Is_directory = (_File_attributes & static_cast<unsigned long>(__std_fs_file_attr::_Directory)) != 0;
        } // Close _Handle

        const auto _Buffer = reinterpret_cast<__std_fs_reparse_data_buffer*>(_Buffer_unique_ptr.get());

        // LWG-3744: `copy_symlink(junction, new_symlink)`'s behavior is unclear
        // `read_symlink(junction)` should be allowed, but `copy_symlink(junction)` is not.
        if (__std_fs_is_junction_from_reparse_data_buffer(_Buffer)) {
            _Err = __std_win_error::_Reparse_tag_invalid;
            return _Err;
        }

        unsigned short _Length;
        wchar_t* _Offset;
        _Err = __std_fs_read_name_from_reparse_data_buffer(_Buffer, &_Offset, &_Length);
        if (_Err != __std_win_error::_Success) {
            return _Err;
        }

        _Offset[_Length] = L'\0';

        if (_Is_directory) {
            _Err = __std_fs_create_directory_symbolic_link(_New_symlink.c_str(), _Offset);
        } else {
            _Err = __std_fs_create_symbolic_link(_New_symlink.c_str(), _Offset);
        }

        return _Err;
    }

    _NODISCARD inline __std_win_error _Copy_junction(const path& _Junction, const path& _New_junction) noexcept {
        __std_win_error _Err;
        unique_ptr<char[]> _Buffer_unique_ptr;
        {
            const _Fs_file _Handle(_Junction.c_str(), __std_access_rights::_File_read_attributes,
                __std_fs_file_flags::_Backup_semantics | __std_fs_file_flags::_Open_reparse_point, &_Err);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }

            _Err = _Read_reparse_data(_Handle, _Buffer_unique_ptr);
            if (_Err != __std_win_error::_Success) {
                return _Err;
            }
        } // Close _Handle

        const auto _Buffer = reinterpret_cast<__std_fs_reparse_data_buffer*>(_Buffer_unique_ptr.get());

        const auto _Create_dir_res = __std_fs_create_directory(_New_junction.c_str());

        if (_Create_dir_res._Error != __std_win_error::_Success) {
            return _Create_dir_res._Error;
        } else if (!_Create_dir_res._Created) {
            return __std_win_error::_Already_exists;
        }

        struct _NODISCARD _Delete_directory_scope_guard {
            const wchar_t* _Path;
            ~_Delete_directory_scope_guard() {
                if (_Path) {
                    (void) __std_fs_remove(_Path);
                }
            }
        };
        _Delete_directory_scope_guard _Delete_directory{_New_junction.c_str()};

        _Fs_file _To_handle{_New_junction.c_str(), __std_access_rights::_File_write_attributes,
            __std_fs_file_flags::_Backup_semantics, &_Err};
        if (_Err != __std_win_error::_Success) {
            return _Err;
        }
        _Err = __std_fs_write_reparse_data_buffer(_To_handle._Raw, _Buffer);
        if (_Err == __std_win_error::_Success) {
            // don't delete the directory if we succeeded in making it a junction
            _Delete_directory._Path = nullptr;
        }

        return _Err;
    }

    _EXPORT_STD inline void copy_symlink(const path& _Symlink, const path& _New_symlink, error_code& _Ec) {
        _Ec = _Make_ec(_Copy_symlink(_Symlink, _New_symlink));
    }

    _EXPORT_STD inline void copy_symlink(const path& _Symlink, const path& _New_symlink) {
        const auto _Err = _Copy_symlink(_Symlink, _New_symlink);
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("copy_symlink", _Err, _Symlink, _New_symlink);
        }
    }

    _EXPORT_STD inline bool copy_file(
        const path& _From, const path& _To, const copy_options _Options, error_code& _Ec) noexcept
    /* strengthened */ {
        // copy a file _From -> _To according to _Options
        const auto _Result =
            __std_fs_copy_file(_From.c_str(), _To.c_str(), static_cast<__std_fs_copy_options>(_Options));
        _Ec = _Make_ec(_Result._Error);
        return _Result._Copied;
    }

    _EXPORT_STD inline bool copy_file(const path& _From, const path& _To, const copy_options _Options) {
        // copy a file _From -> _To according to _Options
        const auto _Result =
            __std_fs_copy_file(_From.c_str(), _To.c_str(), static_cast<__std_fs_copy_options>(_Options));
        if (_Result._Error != __std_win_error::_Success) {
            _Throw_fs_error("copy_file", _Result._Error, _From, _To);
        }

        return _Result._Copied;
    }

    _EXPORT_STD inline bool copy_file(const path& _From, const path& _To, error_code& _Ec) noexcept /* strengthened */ {
        // copy a file _From -> _To, failing if the destination exists
        return _STD filesystem::copy_file(_From, _To, copy_options::none, _Ec);
    }

    _EXPORT_STD inline bool copy_file(const path& _From, const path& _To) {
        // copy a file _From -> _To, failing if the destination exists
        return _STD filesystem::copy_file(_From, _To, copy_options::none);
    }

    _EXPORT_STD _NODISCARD inline bool equivalent(const path& _Lhs, const path& _Rhs) {
        // test if the paths _Lhs and _Rhs refer to the same file
        const auto _Result = __std_fs_equivalent(_Lhs.c_str(), _Rhs.c_str());
        if (_Result._Error != __std_win_error::_Success) {
            _Throw_fs_error("equivalent", _Result._Error, _Lhs, _Rhs);
        }

        return _Result._Equivalent;
    }

    _EXPORT_STD _NODISCARD inline bool equivalent(const path& _Lhs, const path& _Rhs, error_code& _Ec) noexcept {
        // test if the paths _Lhs and _Rhs refer to the same file
        const auto _Result = __std_fs_equivalent(_Lhs.c_str(), _Rhs.c_str());
        _Ec                = _Make_ec(_Result._Error);
        return _Result._Equivalent;
    }

    _EXPORT_STD _NODISCARD inline file_status status(const path& _Path);
    _EXPORT_STD _NODISCARD inline file_status status(const path& _Path, error_code& _Ec) noexcept;
    _EXPORT_STD _NODISCARD inline file_status symlink_status(const path& _Path);
    _EXPORT_STD _NODISCARD inline file_status symlink_status(const path& _Path, error_code& _Ec) noexcept;

    _EXPORT_STD _NODISCARD inline bool exists(const path& _Target, error_code& _Ec) noexcept {
        const auto _Type = _STD filesystem::status(_Target, _Ec).type();
        if (_Type != file_type::none) {
            _Ec.clear();
            return _Type != file_type::not_found;
        }

        return false;
    }

    _EXPORT_STD _NODISCARD inline bool exists(const path& _Target) {
        error_code _Ec;
        const bool _Result = _STD filesystem::exists(_Target, _Ec);
        if (_Ec) {
            _Throw_fs_error("exists", _Ec, _Target);
        }

        return _Result;
    }

    _NODISCARD inline __std_win_error _File_size(const path& _Path, uintmax_t& _Result) noexcept {
        __std_fs_stats _Stats;
        const auto _Error = __std_fs_get_stats(
            _Path.c_str(), &_Stats, __std_fs_stats_flags::_Follow_symlinks | __std_fs_stats_flags::_File_size);
        if (_Error == __std_win_error::_Success) {
            _Result = _Stats._File_size;
        } else {
            _Result = static_cast<uintmax_t>(-1);
        }

        return _Error;
    }

    _EXPORT_STD _NODISCARD inline uintmax_t file_size(const path& _Path) {
        uintmax_t _Result;
        const auto _Error = _File_size(_Path, _Result);
        if (_Error != __std_win_error::_Success) {
            _Throw_fs_error("file_size", _Error, _Path);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline uintmax_t file_size(const path& _Path, error_code& _Ec) noexcept {
        uintmax_t _Result;
        _Ec = _Make_ec(_File_size(_Path, _Result));
        return _Result;
    }

    _NODISCARD inline __std_win_error _Hard_link_count(const path& _Path, uintmax_t& _Result) noexcept {
        __std_fs_stats _Stats;
        const auto _Error = __std_fs_get_stats(
            _Path.c_str(), &_Stats, __std_fs_stats_flags::_Follow_symlinks | __std_fs_stats_flags::_Link_count);
        if (_Error == __std_win_error::_Success) {
            _Result = _Stats._Link_count;
        } else {
            _Result = static_cast<uintmax_t>(-1);
        }

        return _Error;
    }

    _EXPORT_STD _NODISCARD inline uintmax_t hard_link_count(const path& _Target) {
        // get the number of hard links to _Target
        uintmax_t _Result;
        const auto _Err = _Hard_link_count(_Target.c_str(), _Result);
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("hard_link_count", _Err, _Target);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline uintmax_t hard_link_count(const path& _Target, error_code& _Ec) noexcept {
        // get the number of hard links to _Target
        uintmax_t _Result;
        _Ec = _Make_ec(_Hard_link_count(_Target.c_str(), _Result));
        return _Result;
    }

    _EXPORT_STD _NODISCARD inline bool is_block_file(const path&) noexcept /* strengthened */ {
        // tests whether the input path is a block special file (never on Windows)
        return false;
    }

    _EXPORT_STD _NODISCARD inline bool is_block_file(const path& _Path, error_code& _Ec) noexcept {
        // tests whether the input path is a block special file (never on Windows)
        (void) _STD filesystem::status(_Path, _Ec);
        return false; // note status sets _Ec to an error on nonexistent input
    }

    _EXPORT_STD _NODISCARD inline bool is_character_file(const path&) noexcept /* strengthened */ {
        // tests whether the input path is a character special file (never on Windows)
        return false;
    }

    _EXPORT_STD _NODISCARD inline bool is_character_file(const path& _Path, error_code& _Ec) noexcept {
        // tests whether the input path is a character special file (never on Windows)
        (void) _STD filesystem::status(_Path, _Ec);
        return false; // note status sets _Ec to an error on nonexistent input
    }

    _EXPORT_STD _NODISCARD inline bool is_directory(const path& _Path) {
        // tests whether _Path is a directory
        return _STD filesystem::is_directory(_STD filesystem::status(_Path));
    }

    _EXPORT_STD _NODISCARD inline bool is_directory(const path& _Path, error_code& _Ec) noexcept {
        // tests whether _Path is a directory
        return _STD filesystem::is_directory(_STD filesystem::status(_Path, _Ec));
    }

    _EXPORT_STD _NODISCARD inline bool is_empty(const path& _Path, error_code& _Ec) {
        // test whether _Path refers to a zero sized file or empty directory
        constexpr auto _Flags = __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_File_size
                              | __std_fs_stats_flags::_Follow_symlinks;
        __std_fs_stats _Stats;
        const auto _Error = __std_fs_get_stats(_Path.c_str(), &_Stats, _Flags);
        _Ec               = _Make_ec(_Error);
        if (_Error != __std_win_error::_Success) {
            return false;
        }

        if ((_Stats._Attributes & __std_fs_file_attr::_Directory) == __std_fs_file_attr{}) {
            return _Stats._File_size == 0;
        } else {
            directory_iterator _Iter(_Path, _Ec);
            return !_Ec && _Iter._At_end();
        }
    }

    _EXPORT_STD _NODISCARD inline bool is_empty(const path& _Path) {
        // test whether _Path refers to a zero sized file or empty directory
        error_code _Ec;
        const bool _Result = is_empty(_Path, _Ec);
        if (_Ec) {
            _Throw_fs_error("is_empty", _Ec, _Path);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline bool is_fifo(const path&) noexcept /* strengthened */ {
        // tests whether the input path is a fifo (never on Windows)
        return false;
    }

    _EXPORT_STD _NODISCARD inline bool is_fifo(const path& _Path, error_code& _Ec) noexcept {
        // tests whether the input path is a fifo (never on Windows)
        (void) _STD filesystem::status(_Path, _Ec);
        return false; // note status sets _Ec to an error on nonexistent input
    }

    _EXPORT_STD _NODISCARD inline bool is_other(const path& _Path) {
        // tests whether _Path is an "other" file (such as a junction)
        return _STD filesystem::is_other(_STD filesystem::status(_Path));
    }

    _EXPORT_STD _NODISCARD inline bool is_other(const path& _Path, error_code& _Ec) noexcept {
        // tests whether _Path is an "other" file (such as a junction)
        return _STD filesystem::is_other(_STD filesystem::status(_Path, _Ec));
    }

    _EXPORT_STD _NODISCARD inline bool is_regular_file(const path& _Path) {
        // tests whether _Path is a regular file
        return _STD filesystem::is_regular_file(_STD filesystem::status(_Path));
    }

    _EXPORT_STD _NODISCARD inline bool is_regular_file(const path& _Path, error_code& _Ec) noexcept {
        // tests whether _Path is a regular file
        return _STD filesystem::is_regular_file(_STD filesystem::status(_Path, _Ec));
    }

    _EXPORT_STD _NODISCARD inline bool is_socket(const path&) noexcept /* strengthened */ {
        // tests whether the input path is a socket (never on Windows)
        return false;
    }

    _EXPORT_STD _NODISCARD inline bool is_socket(const path& _Path, error_code& _Ec) noexcept {
        // tests whether the input path is a socket (never on Windows)
        (void) _STD filesystem::status(_Path, _Ec);
        return false; // note status sets _Ec to an error on nonexistent input
    }

    _EXPORT_STD _NODISCARD inline bool is_symlink(const path& _Path) {
        // tests whether _Path is a symlink
        return _STD filesystem::is_symlink(_STD filesystem::symlink_status(_Path));
    }

    _EXPORT_STD _NODISCARD inline bool is_symlink(const path& _Path, error_code& _Ec) noexcept {
        // tests whether _Path is a symlink
        return _STD filesystem::is_symlink(_STD filesystem::symlink_status(_Path, _Ec));
    }

    _EXPORT_STD inline bool remove(const path& _Target) {
        // remove file _Target (even if it is a directory); returns whether the file was removed
        // note !exists(_Target) is not an error, and merely returns false
        const auto _Result = __std_fs_remove(_Target.c_str());
        if (_Result._Error != __std_win_error::_Success) {
            _Throw_fs_error("remove", _Result._Error, _Target);
        }

        return _Result._Removed;
    }

    _EXPORT_STD inline bool remove(const path& _Target, error_code& _Ec) noexcept {
        // remove file _Target (even if it is a directory); returns whether the file was removed
        // note !exists(_Target) is not an error, and merely returns false
        const auto _Result = __std_fs_remove(_Target.c_str());
        _Ec                = _Make_ec(_Result._Error);
        return _Result._Removed;
    }

    _EXPORT_STD inline void rename(const path& _Old_p, const path& _New_p) {
        // rename _Old_p to _New_p, overwriting _New_p if it is an existing non-directory file
        // the standard explicitly allows an implementation to not replace _New_p if it is a directory,
        // and we take advantage of that here to be able to use MoveFileEx(... MOVEFILE_REPLACE_EXISTING)
        const auto _Err = __std_fs_rename(_Old_p.c_str(), _New_p.c_str());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("rename", _Err, _Old_p, _New_p);
        }
    }

    _EXPORT_STD inline void rename(const path& _Old_p, const path& _New_p, error_code& _Ec) noexcept {
        // rename _Old_p to _New_p, overwriting _New_p if it is an existing non-directory file
        // the standard explicitly allows an implementation to not replace _New_p if it is a directory,
        // and we take advantage of that here to be able to use MoveFileEx(... MOVEFILE_REPLACE_EXISTING)
        _Ec = _Make_ec(__std_fs_rename(_Old_p.c_str(), _New_p.c_str()));
    }

    _EXPORT_STD inline void resize_file(const path& _Target, const uintmax_t _New_size) {
        // set the size of _Target to _New_size
        const auto _Err = __std_fs_resize_file(_Target.c_str(), _New_size);
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("resize_file", _Err, _Target);
        }
    }

    _EXPORT_STD inline void resize_file(const path& _Target, const uintmax_t _New_size, error_code& _Ec) noexcept {
        // set the size of _Target to _New_size
        _Ec = _Make_ec(__std_fs_resize_file(_Target.c_str(), _New_size));
    }

    _EXPORT_STD struct space_info {
        uintmax_t capacity;
        uintmax_t free;
        uintmax_t available;

#if _HAS_CXX20
        _NODISCARD friend constexpr bool operator==(const space_info&, const space_info&) noexcept = default;
#endif // _HAS_CXX20
    };

    _EXPORT_STD _NODISCARD inline space_info space(const path& _Target) {
        // get capacity information for the volume on which the file _Target resides
        space_info _Result;
        const auto _Last_error = __std_fs_space(_Target.c_str(), &_Result.available, &_Result.capacity, &_Result.free);
        if (_Last_error != __std_win_error::_Success) {
            _Throw_fs_error("space", _Last_error, _Target);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline space_info space(const path& _Target, error_code& _Ec) noexcept {
        // get capacity information for the volume on which the file _Target resides
        space_info _Result;
        _Ec = _Make_ec(__std_fs_space(_Target.c_str(), &_Result.available, &_Result.capacity, &_Result.free));
        return _Result;
    }

    _EXPORT_STD _NODISCARD inline bool status_known(const file_status _Status) noexcept {
        return _Status.type() != file_type::none;
    }

    _NODISCARD inline _File_status_and_error _Get_any_status(
        const path& _Path, const __std_fs_stats_flags _Flags) noexcept {
        _File_status_and_error _Result;
        __std_fs_stats _Stats;

        const auto _Error = __std_fs_get_stats(_Path.c_str(), &_Stats, _Flags);
        _Result._Error    = _Error;
        _Result._Status._Refresh(_Error, _Stats);

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline file_status status(const path& _Path, error_code& _Ec) noexcept {
        const auto _Result = _Get_any_status(_Path, _Status_stats_flags);
        _Ec                = _Make_ec(_Result._Error);
        return _Result._Status;
    }

    _EXPORT_STD _NODISCARD inline file_status status(const path& _Path) {
        const auto _Result = _Get_any_status(_Path, _Status_stats_flags);
        if (_Result._Not_good()) {
            _Throw_fs_error("status", _Result._Error, _Path);
        }
        return _Result._Status;
    }

    _EXPORT_STD _NODISCARD inline file_status symlink_status(const path& _Path, error_code& _Ec) noexcept {
        const auto _Result = _Get_any_status(_Path, _Symlink_status_stats_flags);
        _Ec                = _Make_ec(_Result._Error);
        return _Result._Status;
    }

    _EXPORT_STD _NODISCARD inline file_status symlink_status(const path& _Path) {
        const auto _Result = _Get_any_status(_Path, _Symlink_status_stats_flags);
        if (_Result._Not_good()) {
            _Throw_fs_error("symlink_status", _Result._Error, _Path);
        }

        return _Result._Status;
    }

    _EXPORT_STD inline bool create_directory(const path& _Path) {
        const auto _Result = __std_fs_create_directory(_Path.c_str());
        if (_Result._Error != __std_win_error::_Success) {
            _Throw_fs_error("create_directory", _Result._Error, _Path);
        }

        return _Result._Created;
    }

    _EXPORT_STD inline bool create_directory(const path& _Path, error_code& _Ec) noexcept {
        const auto _Result = __std_fs_create_directory(_Path.c_str());
        _Ec                = _Make_ec(_Result._Error);
        return _Result._Created;
    }

    _EXPORT_STD inline bool create_directory(const path& _Path, const path& _Existing_p) {
        const auto _Result = __std_fs_create_directory(_Path.c_str());
        if (_Result._Error != __std_win_error::_Success) {
            _Throw_fs_error("create_directory", _Result._Error, _Path, _Existing_p);
        }

        return _Result._Created;
    }

    _EXPORT_STD inline bool create_directory(const path& _Path, const path&, error_code& _Ec) noexcept {
        const auto _Result = __std_fs_create_directory(_Path.c_str());
        _Ec                = _Make_ec(_Result._Error);
        return _Result._Created;
    }

    _EXPORT_STD inline bool create_directories(const path& _Path, error_code& _Ec) {
        if (_Path.empty()) {
            _Ec = _Make_ec(__std_win_error::_Path_not_found);
            return false;
        }

        _Ec.clear(); // for exception safety
        const wstring& _Text = _Path.native();
        wstring _Tmp;
        _Tmp.reserve(_Text.size());
        auto _Cursor        = _Text.data();
        const auto _End     = _Text.data() + _Text.size();
        auto _Root_path_end = _Find_relative_path(_Cursor, _End);
        if (_Root_path_end != _Cursor && _End - _Root_path_end >= 3 && _Is_drive_prefix(_Root_path_end)
            && _Is_slash(_Root_path_end[2])) {
            // \\?\ prefixes may have a drive letter suffix Windows will reject, strip
            _Root_path_end += 2;
        }

        _Tmp.append(_Cursor, _Root_path_end);
        _Cursor = _Root_path_end;

        // When creating directories, sometimes we get error reports on earlier directories.
        // Consider a case like X:\cat\dog\elk, where we get the following errors:
        // X: ERROR_ACCESS_DENIED
        // X:\cat ERROR_ALREADY_EXISTS
        // X:\cat\dog ERROR_ACCESS_DENIED
        // X:\cat\dog\elk ERROR_FILE_NOT_FOUND
        // Here, the previous access denied error prevented us from creating a parent directory,
        // and the subsequent ERROR_FILE_NOT_FOUND is not the interesting error for the user.
        // Therefore:
        //   If the last directory creation reports success, we return success.
        //   If the last directory creation fails, we return the most recent non-file-not-found error.
        //   If there is no such non-file-not-found error, we return the most recent error.

        bool _Created_last                              = false;
        __std_win_error _Error                          = __std_win_error::_Success;
        __std_win_error _Most_recent_not_file_not_found = __std_win_error::_Success;
        while (_Cursor != _End) {
            const auto _Added_end = _STD find_if(_STD find_if_not(_Cursor, _End, _Is_slash), _End, _Is_slash);
            _Tmp.append(_Cursor, _Added_end);
            const auto _Create_result = __std_fs_create_directory(_Tmp.c_str());
            _Error                    = _Create_result._Error;
            _Created_last             = _Create_result._Created;
            if (_Error != __std_win_error::_Success && !__std_is_file_not_found(_Error)) {
                _Most_recent_not_file_not_found = _Error;
            }

            _Cursor = _Added_end;
        }

        if (_Error != __std_win_error::_Success && _Most_recent_not_file_not_found != __std_win_error::_Success) {
            _Error = _Most_recent_not_file_not_found;
        }

        _Ec = _Make_ec(_Error);
        return _Created_last;
    }

    _EXPORT_STD inline bool create_directories(const path& _Path) {
        error_code _Ec;
        const bool _Result = _STD filesystem::create_directories(_Path, _Ec);
        if (_Ec) {
            _Throw_fs_error("create_directories", _Ec, _Path);
        }

        return _Result;
    }

    inline constexpr int _Remove_all_retry_count = 10;

    inline void _Remove_all_dir(const path& _Path, error_code& _Ec, uintmax_t& _Removed_count) {
        // remove _Path, including any contents
        for (directory_iterator _It(_Path, _Ec);; _It.increment(_Ec)) { // remove nonempty directory contents
            if (_Ec) {
                if (_Ec.category() != _STD system_category()
                    || !__std_is_file_not_found(static_cast<__std_win_error>(_Ec.value()))) {
                    return;
                }

                break;
            }

            if (_It._At_end()) {
                break;
            }

            const auto& _Subpath  = _It->path();
            const auto _Substatus = _It->symlink_status(_Ec);
            if (_Ec) {
                return;
            }

            if (_Substatus.type() == file_type::directory) {
                _Remove_all_dir(_Subpath, _Ec, _Removed_count);
            } else {
                _Removed_count += _STD filesystem::remove(_Subpath, _Ec);
            }

            if (_Ec) {
                return;
            }
        }

        for (int _Retry = 0; _Retry < _Remove_all_retry_count; ++_Retry) {
            // retry up to _Remove_all_retry_count for resilience against
            // A/V tools, search indexers, backup tools, etc.
            const auto _Path_remove_result = __std_fs_remove(_Path.c_str());
            _Removed_count += _Path_remove_result._Removed;
            _Ec = _Make_ec(_Path_remove_result._Error);
            if (_Path_remove_result._Error != __std_win_error::_Directory_not_empty
                && _Path_remove_result._Error != __std_win_error::_Access_denied) {
                // ERROR_DIRECTORY_NOT_EMPTY if we're waiting for handles to children to be closed,
                // ERROR_ACCESS_DENIED if the directory we're targeting itself is marked for deletion.
                return;
            }
        }
    }

    _EXPORT_STD inline uintmax_t remove_all(const path& _Path, error_code& _Ec) {
        // remove _Path, including any contents
        _Ec.clear(); // for exception safety
        const auto _First_remove_result = __std_fs_remove(_Path.c_str());
        uintmax_t _Removed_count        = _First_remove_result._Removed;
        _Ec                             = _Make_ec(_First_remove_result._Error);
        if (_First_remove_result._Error == __std_win_error::_Directory_not_empty) {
            _Remove_all_dir(_Path, _Ec, _Removed_count);
        }

        if (_Ec) {
            _Removed_count = static_cast<uintmax_t>(-1);
        }

        return _Removed_count;
    }

    _EXPORT_STD inline uintmax_t remove_all(const path& _Path) {
        error_code _Ec;
        const auto _Removed_count = _STD filesystem::remove_all(_Path, _Ec);
        if (_Ec) {
            _Throw_fs_error("remove_all", _Ec, _Path);
        }

        return _Removed_count;
    }

    _NODISCARD inline __std_win_error _Last_write_time(const path& _Path, file_time_type& _Result) noexcept {
        __std_fs_stats _Stats;
        const auto _Error = __std_fs_get_stats(
            _Path.c_str(), &_Stats, __std_fs_stats_flags::_Follow_symlinks | __std_fs_stats_flags::_Last_write_time);
        if (_Error == __std_win_error::_Success) {
            _Result = file_time_type{file_time_type::duration{_Stats._Last_write_time}};
        } else {
            _Result = (file_time_type::min)();
        }

        return _Error;
    }

    _EXPORT_STD _NODISCARD inline file_time_type last_write_time(const path& _Path) {
        file_time_type _Result;
        const auto _Error = _Last_write_time(_Path, _Result);
        if (_Error != __std_win_error::_Success) {
            _Throw_fs_error("last_write_time", _Error, _Path);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline file_time_type last_write_time(const path& _Path, error_code& _Ec) noexcept {
        file_time_type _Result;
        _Ec = _Make_ec(_Last_write_time(_Path, _Result));
        return _Result;
    }

    _EXPORT_STD inline void last_write_time(const path& _Target, const file_time_type _New_time) {
        // set the last write time of _Target to _New_time
        const auto _Err = __std_fs_set_last_write_time(_New_time.time_since_epoch().count(), _Target.c_str());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("last_write_time", _Err, _Target);
        }
    }

    _EXPORT_STD inline void last_write_time(
        const path& _Target, const file_time_type _New_time, error_code& _Ec) noexcept {
        // set the last write time of _Target to _New_time
        _Ec = _Make_ec(__std_fs_set_last_write_time(_New_time.time_since_epoch().count(), _Target.c_str()));
    }

    _EXPORT_STD enum class perm_options { replace = 0x1, add = 0x2, remove = 0x4, nofollow = 0x8 };

    _BITMASK_OPS(_EXPORT_STD, perm_options)

    _NODISCARD inline __std_win_error _Permissions(
        const path& _Target, const perms _Perms, perm_options _Options) noexcept {
        bool _Readonly;
        const bool _Follow_symlinks = (_Options & perm_options::nofollow) == perm_options{};
        _Options &= ~perm_options::nofollow;
        const auto _Write_perms = _Perms & perms::_All_write;
        switch (_Options) {
        case perm_options::replace:
            // always apply FILE_ATTRIBUTE_READONLY according to _Perms
            _Readonly = _Write_perms == perms::none;
            break;
        case perm_options::add:
            if (_Write_perms == perms::none) {
                // if we aren't adding any write bits, then we won't change
                // FILE_ATTRIBUTE_READONLY, so there's nothing to do
                return __std_win_error::_Success;
            }

            _Readonly = false;
            break;
        case perm_options::remove:
            if (_Write_perms != perms::_All_write) {
                // if we aren't removing all write bits, then we won't change
                // FILE_ATTRIBUTE_READONLY, so there's nothing to do
                return __std_win_error::_Success;
            }

            _Readonly = true;
            break;
        case perm_options::nofollow: // avoid C4061
        default:
            return __std_win_error::_Invalid_parameter;
        }

        return __std_fs_change_permissions(_Target.c_str(), _Follow_symlinks, _Readonly);
    }

    _EXPORT_STD inline void permissions(
        const path& _Target, const perms _Perms, const perm_options _Options = perm_options::replace) {
        const auto _Err = _Permissions(_Target, _Perms, _Options);
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("permissions", _Err, _Target);
        }
    }

    _EXPORT_STD inline void permissions(
        const path& _Target, const perms _Perms, const perm_options _Options, error_code& _Ec) noexcept {
        _Ec = _Make_ec(_Permissions(_Target, _Perms, _Options));
    }

    _EXPORT_STD inline void permissions(const path& _Target, const perms _Perms, error_code& _Ec) noexcept {
        _STD filesystem::permissions(_Target, _Perms, perm_options::replace, _Ec);
    }

    _EXPORT_STD _NODISCARD inline path temp_directory_path(error_code& _Ec) {
        // get a location suitable for temporary storage, and verify that it is a directory
        _Ec.clear(); // for exception safety
        path _Result;
        _Result._Text.resize(__std_fs_temp_path_max);
        const auto _Temp_result = __std_fs_get_temp_path(_Result._Text.data());
        _Result._Text.resize(_Temp_result._Size);
        if (_Temp_result._Error == __std_win_error::_Max) { // path could be retrieved, but was not a directory
            _Ec = _STD make_error_code(errc::not_a_directory);
        } else {
            _Ec = _Make_ec(_Temp_result._Error);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path temp_directory_path() {
        // get a location suitable for temporary storage, and verify that it is a directory
        error_code _Ec; // unusual arrangement to allow thrown error_code to have generic_category()
        path _Result(_STD filesystem::temp_directory_path(_Ec));
        if (_Ec) {
            _Throw_fs_error("temp_directory_path", _Ec, _Result);
        }
        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path current_path(error_code& _Ec) {
        _Ec.clear(); // for exception safety
        path _Result;
        _Result._Text.resize(__std_fs_max_path);
        for (;;) {
            const auto _Requested_size = static_cast<unsigned long>(_Result._Text.size());
            const auto _Temp_result    = __std_fs_get_current_path(_Requested_size, _Result._Text.data());
            _Result._Text.resize(_Temp_result._Size);
            if (_Temp_result._Size < _Requested_size) {
                _Ec = _Make_ec(_Temp_result._Error);
                return _Result;
            }
        }
    }

    _EXPORT_STD _NODISCARD inline path current_path() {
        error_code _Ec;
        path _Result(_STD filesystem::current_path(_Ec));
        if (_Ec) {
            _Throw_fs_error("current_path()", _Ec);
        }
        return _Result;
    }

    _EXPORT_STD inline void current_path(const path& _To, error_code& _Ec) noexcept { // set the current path
        _Ec = _Make_ec(__std_fs_set_current_path(_To._Text.data()));
    }

    _EXPORT_STD inline void current_path(const path& _To) { // set the current path
        const auto _Err = __std_fs_set_current_path(_To._Text.data());
        if (_Err != __std_win_error::_Success) {
            _Throw_fs_error("current_path(const path&)", _Err, _To);
        }
    }

    _EXPORT_STD _NODISCARD inline path weakly_canonical(const path& _Input, error_code& _Ec) {
        // eventually calls GetFinalPathNameByHandleW
        _Ec.clear(); // for exception safety

        path _Temp;

        {
            const auto _Err = _Canonical(_Temp, _Input.native());

            if (_Err == __std_win_error::_Success) {
                return _Temp;
            }

            if (!__std_is_file_not_found(_Err)) {
                _Ec = _Make_ec(_Err);
                return {};
            }
        }

        const path _Normalized = _Input.lexically_normal();

        path _Result = _Normalized.root_path();

        const path _Normalized_relative = _Normalized.relative_path();

        bool _Call_canonical = true;

        for (const auto& _Elem : _Normalized_relative) {
            _Result /= _Elem;

            if (_Call_canonical) {
                _Temp.clear();

                const auto _Err = _Canonical(_Temp, _Result.native());

                if (_Err == __std_win_error::_Success) {
                    _Result = _STD move(_Temp);
                } else if (__std_is_file_not_found(_Err)) {
                    _Call_canonical = false;
                } else {
                    _Ec = _Make_ec(_Err);
                    return {};
                }
            }
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path weakly_canonical(const path& _Input) {
        // eventually calls GetFinalPathNameByHandleW
        error_code _Ec;

        path _Result = _STD filesystem::weakly_canonical(_Input, _Ec);

        if (_Ec) {
            _Throw_fs_error("weakly_canonical", _Ec, _Input);
        }

        return _Result;
    }

    _EXPORT_STD _NODISCARD inline path proximate(
        const path& _Path, const path& _Base = _STD filesystem::current_path()) {
        // eventually calls GetFinalPathNameByHandleW
        const path _Weakly_canonical_path = _STD filesystem::weakly_canonical(_Path);
        const path _Weakly_canonical_base = _STD filesystem::weakly_canonical(_Base);
        return _Weakly_canonical_path.lexically_proximate(_Weakly_canonical_base);
    }

    _EXPORT_STD _NODISCARD inline path proximate(const path& _Path, const path& _Base, error_code& _Ec) {
        // eventually calls GetFinalPathNameByHandleW
        const path _Weakly_canonical_path = _STD filesystem::weakly_canonical(_Path, _Ec);

        if (_Ec) {
            return {};
        }

        const path _Weakly_canonical_base = _STD filesystem::weakly_canonical(_Base, _Ec);

        if (_Ec) {
            return {};
        }

        return _Weakly_canonical_path.lexically_proximate(_Weakly_canonical_base);
    }

    _EXPORT_STD _NODISCARD inline path proximate(const path& _Path, error_code& _Ec) {
        // eventually calls GetFinalPathNameByHandleW
        const path _Base = _STD filesystem::current_path(_Ec);
        // N4950 [fs.op.proximate]/1 incorrectly calls current_path()
        if (_Ec) {
            return {};
        }

        return _STD filesystem::proximate(_Path, _Base, _Ec);
    }

    _EXPORT_STD _NODISCARD inline path relative(
        const path& _Path, const path& _Base = _STD filesystem::current_path()) {
        // eventually calls GetFinalPathNameByHandleW
        const path _Weakly_canonical_path = _STD filesystem::weakly_canonical(_Path);
        const path _Weakly_canonical_base = _STD filesystem::weakly_canonical(_Base);
        return _Weakly_canonical_path.lexically_relative(_Weakly_canonical_base);
    }

    _EXPORT_STD _NODISCARD inline path relative(const path& _Path, const path& _Base, error_code& _Ec) {
        // eventually calls GetFinalPathNameByHandleW
        const path _Weakly_canonical_path = _STD filesystem::weakly_canonical(_Path, _Ec);

        if (_Ec) {
            return {};
        }

        const path _Weakly_canonical_base = _STD filesystem::weakly_canonical(_Base, _Ec);

        if (_Ec) {
            return {};
        }

        return _Weakly_canonical_path.lexically_relative(_Weakly_canonical_base);
    }

    _EXPORT_STD _NODISCARD inline path relative(const path& _Path, error_code& _Ec) {
        // eventually calls GetFinalPathNameByHandleW
        const path _Base = _STD filesystem::current_path(_Ec);
        // N4950 [fs.op.relative]/1 incorrectly calls current_path()
        if (_Ec) {
            return {};
        }

        return _STD filesystem::relative(_Path, _Base, _Ec);
    }

    inline void _Copy_impl(
        const directory_entry& _From, const path& _To, const copy_options _Options, error_code& _Ec) {
        // implement copy, does not clear _Ec for callers
        // Standard quotes herein are relative to N4950
        // The following parts of LWG-3057 are implemented:
        // * guarding equivalent() from nonexistent to
        // * replacing unspecified recursion prevention tag with copy_options::directories_only
        // Other parts of LWG-3057 remain under discussion in the committee and are not yet implemented.
        //  (In particular, changes to existing destination flags, and error handling).
        const bool _Flink = (_Options & (copy_options::skip_symlinks | copy_options::copy_symlinks))
                         != copy_options::none; // create_symlinks intentionally removed by LWG-3057
        const auto _Fstat = _From._Get_any_status(_Flink ? _Symlink_status_stats_flags : _Status_stats_flags);
        if (_Fstat._Error != __std_win_error::_Success) { // report an error if exists(f) is false
            _Ec = _Make_ec(_Fstat._Error);
            return;
        }

        const bool _Tlink =
            (_Options & (copy_options::create_symlinks | copy_options::skip_symlinks)) != copy_options::none;
        const auto _Tstat = _Get_any_status(_To, _Tlink ? _Symlink_status_stats_flags : _Status_stats_flags);
        if (_Tstat._Not_good()) {
            _Ec = _Make_ec(_Tstat._Error);
            return;
        }

        if (_STD filesystem::exists(_Tstat._Status)) {
            if (_STD filesystem::equivalent(_From, _To, _Ec)) { // report an error if equivalent(from, to) is true
                _Ec = _STD make_error_code(errc::file_exists);
                return;
            }

            if (_Ec) {
                return;
            }
        }

        const bool _Fstat_is_other =
            _STD filesystem::is_other(_Fstat._Status) && _Fstat._Status.type() != file_type::junction;
        const bool _Tstat_is_other =
            _STD filesystem::is_other(_Tstat._Status) && _Tstat._Status.type() != file_type::junction;
        if (_Fstat_is_other || _Tstat_is_other) {
            // report an error if is_other(f) || is_other(t) is true, and it's not a junction
            _Ec = _STD make_error_code(errc::operation_not_supported);
            return;
        }

        if (_STD filesystem::is_directory(_Fstat._Status) && _STD filesystem::is_regular_file(_Tstat._Status)) {
            // report an error if is_directory(f) && is_regular_file(t) is true
            _Ec = _STD make_error_code(errc::file_exists);
            return;
        }

        if (_Fstat._Status.type() == file_type::junction) {
            if ((_Options & copy_options::skip_symlinks) != copy_options::none) {
                return;
            }

            // _Options includes copy_options::copy_symlinks,
            // since _Fstat is only allowed to be a symbolic link when either skip_symlinks or copy_symlinks
            if (!_STD filesystem::exists(_Tstat._Status)) {
                _Ec = _Make_ec(_Copy_junction(_From, _To));
            }
        }

        if (_STD filesystem::is_symlink(_Fstat._Status)) {
            if ((_Options & copy_options::skip_symlinks) != copy_options::none) {
                return;
            }

            // _Options includes copy_options::copy_symlinks,
            // since _Fstat is only allowed to be a symbolic link when either skip_symlinks or copy_symlinks
            if (!_STD filesystem::exists(_Tstat._Status)) {
                _STD filesystem::copy_symlink(_From, _To, _Ec);
                return;
            }

            // otherwise report an error
            _Ec = _STD make_error_code(errc::operation_not_supported);
            return;
        }

        if (_STD filesystem::is_regular_file(_Fstat._Status)) {
            if ((_Options & copy_options::directories_only) != copy_options::none) {
                return;
            }

            if ((_Options & copy_options::create_symlinks) != copy_options::none) {
                // Otherwise, if (condition) then create a symbolic link to the source file
                _STD filesystem::create_symlink(_From, _To, _Ec);
                return;
            }

            if ((_Options & copy_options::create_hard_links) != copy_options::none) {
                // Otherwise, if (condition) then create a hard link to the source file
                _STD filesystem::create_hard_link(_From, _To, _Ec);
                return;
            }

            if (_STD filesystem::is_directory(_Tstat._Status)) {
                // Otherwise, if is_directory(t), then copy_file(from, to / from.filename(), options)
                _STD filesystem::copy_file(_From, _To / _From.path().filename(), _Options, _Ec);
                return;
            }

            // Otherwise, copy_file(_From, _To, _Options)
            _STD filesystem::copy_file(_From, _To, _Options, _Ec);
            return;
        }

        // The following condition modified by LWG-3057:
        if (_STD filesystem::is_directory(_Fstat._Status)) {
            if ((_Options & copy_options::create_symlinks) != copy_options::none) {
                _Ec = _STD make_error_code(errc::is_a_directory);
                return;
            }

            _STD filesystem::create_directory(_To, _From, _Ec);
            if (_Ec) {
                return;
            }

            // Note LWG-3057 uses directories_only as the flag, instead of an unspecified copy_options value:
            if ((_Options & copy_options::recursive) != copy_options::none
                || (_Options & copy_options::directories_only) == copy_options::none) {
                for (directory_iterator _It(_From, _Ec);; _It.increment(_Ec)) {
                    if (_Ec || _It._At_end()) {
                        return;
                    }

                    // if ((options & copy_options::recursive) != copy_options::none ||
                    //      !is_directory(linkf ? symlink_status(x.path()) : status(x.path())))
                    //        copy(x.path(), to/x.path().filename(), options);
                    bool _Recurse = (_Options & copy_options::recursive) != copy_options::none;
                    if (!_Recurse) {
                        const auto _Child_status_result =
                            _It->_Get_any_status(_Flink ? _Symlink_status_stats_flags : _Status_stats_flags);
                        if (_Child_status_result._Error != __std_win_error::_Success) {
                            _Ec = _Make_ec(_Child_status_result._Error);
                            return;
                        }

                        _Recurse = !_STD filesystem::is_directory(_Child_status_result._Status);
                    }

                    if (_Recurse) {
                        _Copy_impl(*_It, _To / _It->path().filename(), _Options, _Ec);
                        if (_Ec) {
                            return;
                        }
                    }
                }
            }
        }

        // Otherwise, no effects.
    }

    _EXPORT_STD inline void copy(const path& _From, const path& _To, const copy_options _Options, error_code& _Ec) {
        const directory_entry _From_dir(_From, _Ec);
        if (_Ec) { // report an error if exists(f) is false
            return;
        }

        _Copy_impl(_From_dir, _To, _Options, _Ec);
    }

    _EXPORT_STD inline void copy(const path& _From, const path& _To, const copy_options _Options) {
        error_code _Ec;
        _STD filesystem::copy(_From, _To, _Options, _Ec);
        if (_Ec) {
            _Throw_fs_error("copy", _Ec, _From, _To);
        }
    }

    _EXPORT_STD inline void copy(const path& _From, const path& _To, error_code& _Ec) {
        return _STD filesystem::copy(_From, _To, copy_options::none, _Ec);
    }

    _EXPORT_STD inline void copy(const path& _From, const path& _To) {
        return _STD filesystem::copy(_From, _To, copy_options::none);
    }
} // namespace filesystem

template <>
struct hash<filesystem::path> {
    using _ARGUMENT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS = filesystem::path;
    using _RESULT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS   = size_t;
    _NODISCARD _STATIC_CALL_OPERATOR size_t operator()(const filesystem::path& _Path) _CONST_CALL_OPERATOR noexcept {
        return _STD filesystem::hash_value(_Path);
    }
};

#if _HAS_CXX20
namespace ranges {
    template <>
    inline constexpr bool enable_borrowed_range<filesystem::directory_iterator> = true;
    template <>
    inline constexpr bool enable_borrowed_range<filesystem::recursive_directory_iterator> = true;
    template <>
    inline constexpr bool enable_view<filesystem::directory_iterator> = true;
    template <>
    inline constexpr bool enable_view<filesystem::recursive_directory_iterator> = true;
} // namespace ranges
#endif // _HAS_CXX20

_STD_END

#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // ^^^ _HAS_CXX17 ^^^
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _FILESYSTEM_
