diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2019-10-08 14:11:14 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2019-10-08 14:11:14 +0300 |
commit | 6261c7e51df7610610230fc1390fcf3ccd2769a0 (patch) | |
tree | bb416caa400e1872d21f151de5adece2c9990316 | |
parent | efb2cc2069d8b7fbad0ff43205ab5bbc73f7ce90 (diff) | |
parent | 29b50c514e41b1571649b4282d4f91dffbe27c3d (diff) |
Merge branch 'develop' into path_view_refactor
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/path_view.ipp | 2 | ||||
-rw-r--r-- | include/llfio/v2.0/path_view.hpp | 438 | ||||
-rw-r--r-- | include/llfio/v2.0/status_code.hpp | 73 |
5 files changed, 265 insertions, 261 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f36ac09..f45d9d7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,10 +42,16 @@ if(NOT PROJECT_IS_DEPENDENCY) UpdateRevisionHppFromGit("${CMAKE_CURRENT_SOURCE_DIR}/include/llfio/revision.hpp") endif() # Find my library dependencies +find_quickcpplib_library(quickcpplib + GIT_REPOSITORY "https://github.com/ned14/quickcpplib.git" + REQUIRED + IS_HEADER_ONLY +) find_quickcpplib_library(outcome GIT_REPOSITORY "https://github.com/ned14/outcome.git" GIT_TAG "develop" REQUIRED + IS_HEADER_ONLY ) if(WIN32) add_subdirectory("include/llfio/ntkernel-error-category" EXCLUDE_FROM_ALL) @@ -226,6 +232,7 @@ if(NOT PROJECT_IS_DEPENDENCY) find_quickcpplib_library(kerneltest GIT_REPOSITORY "https://github.com/ned14/kerneltest.git" REQUIRED + IS_HEADER_ONLY ) foreach(test_target ${llfio_TEST_TARGETS}) target_link_libraries(${test_target} PRIVATE kerneltest::hl) diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index d99b2911..28402bcd 100644 --- a/include/llfio/revision.hpp +++ b/include/llfio/revision.hpp @@ -1,4 +1,4 @@ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define LLFIO_PREVIOUS_COMMIT_REF dbf76606c10d325eee673b82173ec0897e0db3ff -#define LLFIO_PREVIOUS_COMMIT_DATE "2019-10-07 09:55:37 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE dbf76606 +#define LLFIO_PREVIOUS_COMMIT_REF efb2cc2069d8b7fbad0ff43205ab5bbc73f7ce90 +#define LLFIO_PREVIOUS_COMMIT_DATE "2019-10-07 10:23:15 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE efb2cc20 diff --git a/include/llfio/v2.0/detail/impl/windows/path_view.ipp b/include/llfio/v2.0/detail/impl/windows/path_view.ipp index 0388def6..97801a6a 100644 --- a/include/llfio/v2.0/detail/impl/windows/path_view.ipp +++ b/include/llfio/v2.0/detail/impl/windows/path_view.ipp @@ -27,6 +27,7 @@ Distributed under the Boost Software License, Version 1.0. LLFIO_V2_NAMESPACE_BEGIN +#if 0 LLFIO_HEADERS_ONLY_MEMFUNC_SPEC std::unique_ptr<path_view_component::char8_t[]> path_view_component::_ansi_path_to_utf8(basic_string_view<char8_t> &out, basic_string_view<char> in) noexcept { windows_nt_kernel::init(); @@ -69,7 +70,6 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC std::unique_ptr<path_view_component::char8_t[]> return ret; } -#if 0 LLFIO_HEADERS_ONLY_MEMFUNC_SPEC void path_view::c_str::_from_utf8(const path_view &view) noexcept { windows_nt_kernel::init(); diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp index a5b98cec..4b3dc0e4 100644 --- a/include/llfio/v2.0/path_view.hpp +++ b/include/llfio/v2.0/path_view.hpp @@ -83,22 +83,26 @@ namespace detail }; #endif - template <class T> struct is_path_view_component_source_type : std::false_type + template <class T> struct is_source_chartype_acceptable : std::false_type { }; - template <> struct is_path_view_component_source_type<LLFIO_V2_NAMESPACE::byte> : std::true_type + template <> struct is_source_chartype_acceptable<char> : std::true_type { }; - template <> struct is_path_view_component_source_type<char> : std::true_type + template <> struct is_source_chartype_acceptable<wchar_t> : std::true_type { }; - template <> struct is_path_view_component_source_type<wchar_t> : std::true_type + template <> struct is_source_chartype_acceptable<char8_t> : std::true_type { }; - template <> struct is_path_view_component_source_type<char8_t> : std::true_type + template <> struct is_source_chartype_acceptable<char16_t> : std::true_type { }; - template <> struct is_path_view_component_source_type<char16_t> : std::true_type + + template <class T> struct is_source_acceptable : is_source_chartype_acceptable<T> + { + }; + template <> struct is_source_acceptable<LLFIO_V2_NAMESPACE::byte> : std::true_type { }; @@ -130,6 +134,13 @@ namespace detail } // namespace detail class path_view; +class path_view_component; +inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept; +inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator!=(path_view_component x, path_view_component y) noexcept; +inline std::ostream &operator<<(std::ostream &s, const path_view_component &v); +inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator==(path_view x, path_view y) noexcept; +inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator!=(path_view x, path_view y) noexcept; +inline std::ostream &operator<<(std::ostream &s, const path_view &v); /*! \class path_view_component \brief An iterated part of a `path_view`. @@ -138,6 +149,9 @@ class LLFIO_DECL path_view_component { friend class path_view; friend class detail::path_view_iterator; + friend inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept; + friend inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator!=(path_view_component x, path_view_component y) noexcept; + friend inline std::ostream &operator<<(std::ostream &s, const path_view_component &v); public: //! The preferred separator type @@ -152,8 +166,18 @@ public: using char16_t = detail::char16_t; #endif + //! True if path views can be constructed from this character type. + //! i.e. is one of `char`, `wchar_t`, `char8_t`, `char16_t` + template <class Char> static constexpr bool is_source_chartype_acceptable = detail::is_source_chartype_acceptable<Char>::value; + + //! True if path views can be constructed from this source. + //! i.e. `is_source_chartype_acceptable`, or is `byte` + template <class Char> static constexpr bool is_source_acceptable = detail::is_source_acceptable<Char>::value; + + //! The default internal buffer size used by `c_str`. + static constexpr size_t default_internal_buffer_size = 1024; // 2Kb for wchar_t, 1Kb for char + private: - template <class Char> static constexpr bool _is_constructible = detail::is_path_view_component_source_type<std::decay_t<Char>>::value; static constexpr auto _npos = string_view::npos; union { const byte *_bytestr{nullptr}; @@ -293,7 +317,7 @@ public: ~path_view_component() = default; //! True if empty - constexpr bool empty() const noexcept { return _length == 0; } + LLFIO_NODISCARD constexpr bool empty() const noexcept { return _length == 0; } //! Returns the size of the view in characters. constexpr size_t native_size() const noexcept @@ -359,128 +383,22 @@ private: static detail::_codecvt<InT, OutT> ret; return ret; } - template <class CharT> static int _compare(basic_string_view<CharT> a, basic_string_view<CharT> b) noexcept { return a.compare(b); } -#ifdef _WIN32 - // On Windows only, char is the native narrow encoding, which is locale dependent - static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC std::unique_ptr<char8_t[]> _ansi_path_to_utf8(basic_string_view<char8_t> &out, basic_string_view<char> in) noexcept; - static int _compare(basic_string_view<char> a, basic_string_view<char> b) noexcept { return a.compare(b); } - template <class CharT> static int _compare(basic_string_view<CharT> a, basic_string_view<char> b) noexcept { return -_compare(b, a); } - template <class CharT> static int _compare(basic_string_view<char> a, basic_string_view<CharT> b) noexcept - { - // Convert a from native narrow encoding to utf8 - basic_string_view<char8_t> a_utf8; - auto h = _ansi_path_to_utf8(a_utf8, a); - if(!h) - { - // Failure to allocate memory, or convert - assert(h); - return -99; - } - return _compare(a_utf8, b); - } -#endif - template <class Char1T, class Char2T> static int _compare(basic_string_view<Char1T> a, basic_string_view<Char2T> b) noexcept + // Identical source encodings compare lexiographically + template <class DestT, class Deleter, size_t _internal_buffer_size, class CharT> static int _compare(basic_string_view<CharT> a, basic_string_view<CharT> b) noexcept { return a.compare(b); } + // Disparate source encodings compare via c_str + template <class DestT, class Deleter, size_t _internal_buffer_size, class Char1T, class Char2T> static int _compare(basic_string_view<Char1T> a, basic_string_view<Char2T> b) noexcept { - static constexpr size_t codepoints_at_a_time = 8 * 4; - // Convert both to utf8, then to utf32, and compare -#if !defined(__CHAR8_TYPE__) && __cplusplus < 20200000 - using utf8_type = char; -#else - using utf8_type = char8_t; -#endif - auto &convert_a = _get_codecvt<Char1T, utf8_type>(); - auto &convert_b = _get_codecvt<Char2T, utf8_type>(); - std::mbstate_t a_state{}, b_state{}; - auto *a_ptr = detail::cast_char8_t_ptr(a.data()); - auto *b_ptr = detail::cast_char8_t_ptr(b.data()); - const auto *a_back = detail::cast_char8_t_ptr(&a.back()); - const auto *b_back = detail::cast_char8_t_ptr(&b.back()); - while(a_ptr <= a_back && b_ptr <= b_back) + c_str<DestT, Deleter, _internal_buffer_size> _a(a, true); + c_str<DestT, Deleter, _internal_buffer_size> _b(b, true); + if(_a.length < _b.length) { - // Try to convert 5 to 32 chars at a time - utf8_type a_out[codepoints_at_a_time + 1], b_out[codepoints_at_a_time + 1], *a_out_end = a_out, *b_out_end = b_out; - auto a_result = convert_a.out(a_state, a_ptr, a_back + 1, a_ptr, a_out, a_out + codepoints_at_a_time, a_out_end); - auto b_result = convert_b.out(b_state, b_ptr, b_back + 1, b_ptr, b_out, b_out + codepoints_at_a_time, b_out_end); - assert(std::codecvt_base::noconv != a_result); - if(std::codecvt_base::noconv == a_result) - { - size_t tocopy = std::min(codepoints_at_a_time, (size_t)(a_back + 1 - a_ptr)); - memcpy(a_out, a_ptr, tocopy); - a_out_end = a_out + tocopy; - a_ptr += tocopy; - } - if(std::codecvt_base::partial == a_result && a_out_end == a_out + codepoints_at_a_time) - { - // Needs one more character from input - a_result = convert_a.out(a_state, a_ptr, a_ptr + 1, a_ptr, a_out + codepoints_at_a_time, a_out + codepoints_at_a_time + 1, a_out_end); - assert(std::codecvt_base::partial != a_result); - } - if(std::codecvt_base::error == a_result) - { - assert(false); - return -99; - } - assert(std::codecvt_base::noconv != b_result); - if(std::codecvt_base::noconv == b_result) - { - size_t tocopy = std::min(codepoints_at_a_time, (size_t)(b_back + 1 - b_ptr)); - memcpy(b_out, b_ptr, tocopy); - b_out_end = b_out + tocopy; - b_ptr += tocopy; - } - if(std::codecvt_base::partial == b_result && b_out_end == b_out + codepoints_at_a_time) - { - // Needs one more character from input - b_result = convert_b.out(b_state, b_ptr, b_ptr + 1, b_ptr, b_out + codepoints_at_a_time, b_out + codepoints_at_a_time + 1, b_out_end); - assert(std::codecvt_base::partial != b_result); - } - if(std::codecvt_base::error == b_result) - { - assert(false); - return 99; - } - if((a_out_end - a_out) < (b_out_end - b_out)) - { - return -2; - } - if((a_out_end - a_out) > (b_out_end - b_out)) - { - return 2; - } -#if !defined(__CHAR8_TYPE__) && __cplusplus < 20200000 - // Before C++ 20, no facility to char_traits::compare utf8, so convert to utf32 - const utf8_type *a_out_end_ = a_out_end, *b_out_end_ = b_out_end; - char32_t a32[codepoints_at_a_time + 1], b32[codepoints_at_a_time + 1], *a32_end = a32, *b32_end = b32; - std::mbstate_t a32_state{}, b32_state{}; - auto &convert32 = _get_codecvt<char32_t, char>(); - convert32.in(a32_state, a_out, a_out_end, a_out_end_, a32, a32 + codepoints_at_a_time + 1, a32_end); - convert32.in(b32_state, b_out, b_out_end, b_out_end_, b32, b32 + codepoints_at_a_time + 1, b32_end); - if((a32_end - a32) < (b32_end - b32)) - { - return -2; - } - if((a32_end - a32) > (b32_end - b32)) - { - return 2; - } - int ret = std::char_traits<char32_t>::compare(a32, b32, a32_end - a32); -#else - int ret = std::char_traits<char8_t>::compare(a_out, b_out, a_out_end - a_out); -#endif - if(ret != 0) - { - return ret; - } - } - if(a_ptr >= a_back) - { - return -2; + return -1; } - if(b_ptr >= b_back) + if(_a.length > _b.length) { - return 2; + return 1; } - return 0; // equal + return memcmp(_a.buffer, _b.buffer, _a.length); } public: @@ -490,32 +408,43 @@ public: return _invoke([](const auto &v) { return _path_from_char_array(v); }); } - /*! Compares the two path views for equivalence or ordering. - Be aware that comparing path views of differing source encodings will be expensive - as a conversion to utf8 is performed. Be further aware that on - Windows, `char` source must undergo a narrow native encoding to utf8 conversion via - the Windows conversion APIs, which is extremely expensive, if not comparing `char`-`char` - views. + /*! Compares the two path views for equivalence or ordering using `T` + as the destination encoding, if necessary. + + If the source encodings of the two path views are compatible, a + lexicographical comparison is performed. If they are incompatible, + either or both views are converted to the destination encoding + using `c_str<T, Delete, _internal_buffer_size>`, and then a + lexicographical comparison is performed. + + This can, for obvious reasons, be expensive. It can also throw + exceptions, as `c_str` does. + + If the destination encoding is `byte`, `memcmp()` is used, + and `c_str` is never invoked as the two sources are byte + compared directly. */ - constexpr int compare(const path_view_component &p) const noexcept + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) + constexpr int compare(const path_view_component &p) const { - return _invoke([&p](const auto &self) { return p._invoke([&self](const auto &other) { return _compare(self, other); }); }); + return _invoke([&p](const auto &self) { return p._invoke([&self](const auto &other) { return _compare<T>(self, other); }); }); } //! \overload - LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) - constexpr int compare(const Char *s) const noexcept { return compare(path_view_component(s)); } + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>)) + constexpr int compare(const Char *s) const noexcept { return compare<T>(path_view_component(s)); } //! \overload - LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) - constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare(path_view_component(s)); } + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_chartype_acceptable<Char>)) + constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare<T>(path_view_component(s)); } /*! Instantiate from a `path_view_component` to get a path suitable for feeding to other code. \tparam T The destination encoding required. \tparam Deleter A custom deleter for any temporary buffer. - \tparam disable_internal_buffer Set to true to disable the internal temporary buffer, thus + \tparam _internal_buffer_size Override the size of the internal temporary buffer, thus reducing stack space consumption (most compilers optimise away the internal temporary buffer - if it can be proved it will never be used). + if it can be proved it will never be used). The default is 1024 values of `T`. This makes the input to the path view component into a destination format suitable for consumption by other code. If the source has the same format as the destination, and @@ -526,18 +455,22 @@ public: and the source is not zero terminated, a straight memory copy is performed into the temporary buffer. - `c_str` contains a 4Kb internal temporary buffer. Output below that amount involves - no dynamic memory allocation. Output above that amount calls `operator new[]`. You - can use an externally supplied larger temporary buffer to avoid dynamic memory - allocation in all situations. + `c_str` contains a temporary buffer sized according to the template parameter. Output + below that amount involves no dynamic memory allocation. Output above that amount calls + `operator new[]`. You can use an externally supplied larger temporary buffer to avoid + dynamic memory allocation in all situations. */ - template <class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, bool disable_internal_buffer = false> struct c_str + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) + struct c_str { - static_assert(_is_constructible<T>, "path_view_component::c_str<T> does not have a T which is one of byte, char, wchar_t, char8_t nor char16_t"); + static_assert(is_source_acceptable<T>, "path_view_component::c_str<T> does not have a T which is one of byte, char, wchar_t, char8_t nor char16_t"); //! Type of the value type using value_type = T; //! Type of the deleter using deleter_type = Deleter; + //! The size of the internal temporary buffer + static constexpr size_t internal_buffer_size = (_internal_buffer_size == 0) ? 1 : _internal_buffer_size; //! Number of characters, excluding zero terminating char, at buffer size_t length{0}; @@ -653,7 +586,7 @@ public: std::mbstate_t cstate{}; auto *src_ptr = src.data(); auto *dest_ptr = _buffer; - if(!disable_internal_buffer) + if(_internal_buffer_size != 0) { // First try the internal buffer, if we overflow, fall back to the allocator auto result = convert.out(cstate, src_ptr, &src.back() + 1, src_ptr, dest_ptr, _buffer + sizeof(_buffer) / sizeof(value_type) - 1, dest_ptr); @@ -665,7 +598,14 @@ public: } if(std::codecvt_base::error == result) { - throw std::system_error(make_error_code(std::errc::illegal_byte_sequence)); + // If input is supposed to be valid UTF, barf + if(view._utf8 || view._utf16) + { + throw std::system_error(make_error_code(std::errc::illegal_byte_sequence)); + } + // Otherwise proceed anyway :) + LLFIO_LOG_WARN(nullptr, "path_view_component::c_str saw failure to completely convert input encoding"); + result = std::codecvt_base::ok; } if(std::codecvt_base::ok == result) { @@ -704,7 +644,14 @@ public: } if(std::codecvt_base::error == result) { - throw std::system_error(std::make_error_code(std::errc::illegal_byte_sequence)); + // If input is supposed to be valid UTF, barf + if(view._utf8 || view._utf16) + { + throw std::system_error(std::make_error_code(std::errc::illegal_byte_sequence)); + } + // Otherwise proceed anyway :) + LLFIO_LOG_WARN(nullptr, "path_view_component::c_str saw failure to completely convert input encoding"); + result = std::codecvt_base::ok; } assert(std::codecvt_base::ok == result); if(std::codecvt_base::ok != result) @@ -735,10 +682,102 @@ public: Deleter _deleter; // MAKE SURE this is the final item in storage, the compiler will elide the storage // under optimisation if it can prove it is never used. - value_type _buffer[disable_internal_buffer ? 1 : (4096 / sizeof(value_type))]{}; + value_type _buffer[internal_buffer_size]{}; }; - template <class, class, bool> friend struct c_str; + LLFIO_TEMPLATE(class T, class Deleter, size_t _internal_buffer_size) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) + friend struct c_str; }; +inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept +{ + if(x.native_size() != y.native_size()) + { + return false; + } + if(x._passthrough != y._passthrough) + { + return false; + } + if(x._char != y._char) + { + return false; + } + if(x._wchar != y._wchar) + { + return false; + } + if(x._utf8 != y._utf8) + { + return false; + } + if(x._utf16 != y._utf16) + { + return false; + } + return 0 == memcmp(x._bytestr, y._bytestr, x._length); +} +inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator!=(path_view_component x, path_view_component y) noexcept +{ + if(x.native_size() != y.native_size()) + { + return true; + } + if(x._passthrough != y._passthrough) + { + return true; + } + if(x._char != y._char) + { + return true; + } + if(x._wchar != y._wchar) + { + return true; + } + if(x._utf8 != y._utf8) + { + return true; + } + if(x._utf16 != y._utf16) + { + return true; + } + return 0 != memcmp(x._bytestr, y._bytestr, x._length); +} +namespace detail +{ + struct string_view_printer + { + std::ostream &s; + template <class CharT> void operator()(basic_string_view<CharT> v) { s << v; } + void operator()(basic_string_view<char16_t> _v) + { + // Cheat by going via filesystem::path + s << filesystem::path(_v.begin(), _v.end()); + } + void operator()(basic_string_view<wchar_t> _v) + { + // Cheat by going via filesystem::path + s << filesystem::path(_v.begin(), _v.end()); + } +#if !defined(__CHAR8_TYPE__) && __cplusplus < 20200000 + void operator()(basic_string_view<char8_t> _v) + { + basic_string_view<char> v((char *) _v.data(), _v.size()); + s << v; + } +#endif + }; +} // namespace detail +inline std::ostream &operator<<(std::ostream &s, const path_view_component &v) +{ + if(v._passthrough) + { + return s << QUICKCPPLIB_NAMESPACE::algorithm::string::to_hex_string({v._charstr, v._length}); + } + v._invoke(detail::string_view_printer{s}); + return s; +} /*! \class path_view @@ -831,8 +870,12 @@ maximum compatibility you should still use the Win32 API. */ class path_view { -public: friend class detail::path_view_iterator; + friend inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator==(path_view x, path_view y) noexcept; + friend inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator!=(path_view x, path_view y) noexcept; + friend inline std::ostream &operator<<(std::ostream &s, const path_view &v); + +public: //! Const iterator type using const_iterator = detail::path_view_iterator; //! iterator type @@ -860,6 +903,17 @@ public: }; #endif + //! True if path views can be constructed from this character type. + //! i.e. is one of `char`, `wchar_t`, `char8_t`, `char16_t` + template <class Char> static constexpr bool is_source_chartype_acceptable = path_view_component::is_source_chartype_acceptable<Char>; + + //! True if path views can be constructed from this source. + //! i.e. `is_source_chartype_acceptable`, or is `byte` + template <class Char> static constexpr bool is_source_acceptable = path_view_component::is_source_acceptable<Char>; + + //! The default internal buffer size used by `c_str`. + static constexpr size_t default_internal_buffer_size = path_view_component::default_internal_buffer_size; // 2Kb for wchar_t, 1Kb for char + private: static constexpr auto _npos = string_view::npos; @@ -907,7 +961,7 @@ public: string MUST continue to exist for this view to be valid. */ LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) + LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_acceptable<Char>)) constexpr path_view(const Char *v, size_t len, bool is_zero_terminated) noexcept : _state(v, len, is_zero_terminated) { @@ -916,7 +970,7 @@ public: `char`, `wchar_t`, `char8_t` or `char16_t`. */ LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) + LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_chartype_acceptable<Char>)) constexpr path_view(const std::basic_string<Char> &v) noexcept // NOLINT : path_view(v.data(), v.size(), true) { @@ -925,7 +979,7 @@ public: `char`, `wchar_t`, `char8_t` or `char16_t`. */ LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) + LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_chartype_acceptable<Char>)) constexpr path_view(basic_string_view<Char> v, bool is_zero_terminated) noexcept // NOLINT : path_view(v.data(), v.size(), is_zero_terminated) { @@ -944,10 +998,7 @@ public: constexpr void swap(path_view &o) noexcept { _state.swap(o._state); } //! True if empty - LLFIO_PATH_VIEW_GCC_CONSTEXPR LLFIO_NODISCARD bool empty() const noexcept - { - return _state.empty(); - } + LLFIO_NODISCARD LLFIO_PATH_VIEW_GCC_CONSTEXPR bool empty() const noexcept { return _state.empty(); } LLFIO_PATH_VIEW_GCC_CONSTEXPR bool has_root_path() const noexcept { return !root_path().empty(); } LLFIO_PATH_VIEW_GCC_CONSTEXPR bool has_root_name() const noexcept { return !root_name().empty(); } LLFIO_PATH_VIEW_GCC_CONSTEXPR bool has_root_directory() const noexcept { return !root_directory().empty(); } @@ -1175,28 +1226,42 @@ public: //! Return the path view as a path. Allocates and copies memory! filesystem::path path() const { return _state.path(); } - /*! Compares the two path views for equivalence or ordering. - Be aware that comparing path views of differing source encodings will be expensive - as a conversion to utf8 is performed for each path component. Be further aware that on - Windows, `char` source must undergo a narrow native encoding to utf8 conversion via - the Windows conversion APIs, which is extremely expensive, if not comparing `char`-`char` - views. + /*! Compares the two path views for equivalence or ordering using `T` + as the destination encoding, if necessary. + + If the source encodings of the two path views are compatible, a + lexicographical comparison is performed. If they are incompatible, + either or both views are converted to the destination encoding + using `c_str<T, Delete, _internal_buffer_size>`, and then a + lexicographical comparison is performed. + + This can, for obvious reasons, be expensive. It can also throw + exceptions, as `c_str` does. + + If the destination encoding is `byte`, `memcmp()` is used, + and `c_str` is never invoked as the two sources are byte + compared directly. */ - constexpr inline int compare(const path_view &o) const noexcept; + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) + constexpr int compare(const path_view &p) const; //! \overload - LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) - constexpr int compare(const Char *s) const noexcept { return _state.compare(s); } + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>)) + constexpr int compare(const Char *s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s)); } //! \overload - LLFIO_TEMPLATE(class Char) - LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::_is_constructible<Char>)) - constexpr int compare(const basic_string_view<Char> s) const noexcept { return _state.compare(s); } + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_chartype_acceptable<Char>)) + constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s)); } + //! Instantiate from a `path_view` to get a path suitable for feeding to other code. See `path_view_component::c_str`. - template <class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, bool disable_internal_buffer = false> struct c_str : path_view_component::c_str<T, Deleter, disable_internal_buffer> + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) + struct c_str { //! Number of characters, excluding zero terminating char, at buffer - using _base = path_view_component::c_str<T, Deleter, disable_internal_buffer>; + using _base = path_view_component::c_str<T, Deleter, _internal_buffer_size>; /*! See constructor for `path_view_component::c_str`. */ template <class U> @@ -1210,27 +1275,21 @@ public: { } }; - template <class, class, bool> friend struct c_str; + LLFIO_TEMPLATE(class T, class Deleter, size_t _internal_buffer_size) + LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) + friend struct c_str; }; inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator==(path_view x, path_view y) noexcept { - if(x.native_size() != y.native_size()) - { - return false; - } - return x.compare(y) == 0; + return x._state == y._state; } inline LLFIO_PATH_VIEW_GCC_CONSTEXPR bool operator!=(path_view x, path_view y) noexcept { - if(x.native_size() != y.native_size()) - { - return true; - } - return x.compare(y) != 0; + return x._state != y._state; } inline std::ostream &operator<<(std::ostream &s, const path_view &v) { - return s << v.path(); + return s << v._state; } namespace detail @@ -1390,12 +1449,13 @@ constexpr inline path_view::iterator path_view::end() noexcept { return cend(); } -constexpr inline int path_view::compare(const path_view &o) const noexcept +template<class T, class Deleter, size_t _internal_buffer_size, class> +constexpr inline int path_view::compare(const path_view &o) const { auto it1 = begin(), it2 = o.begin(); for(; it1 != end() && it2 != o.end(); ++it1, ++it2) { - int res = it1->compare(*it2); + int res = it1->compare<T, Deleter, _internal_buffer_size>(*it2); if(res != 0) { return res; diff --git a/include/llfio/v2.0/status_code.hpp b/include/llfio/v2.0/status_code.hpp index 14a691c6..bd201ae1 100644 --- a/include/llfio/v2.0/status_code.hpp +++ b/include/llfio/v2.0/status_code.hpp @@ -57,6 +57,8 @@ as that (a) enables safe header only LLFIO on Windows (b) produces better codege // Bring in a result implementation based on status_code #include "outcome/experimental/status_result.hpp" #include "outcome/try.hpp" +// Bring in status code utility +#include "outcome/experimental/status-code/include/system_code_from_exception.hpp" #if __cpp_coroutines #include "outcome/experimental/coroutine_support.hpp" #ifdef OUTCOME_FOUND_COROUTINE_HEADER @@ -252,7 +254,7 @@ template <class T> using atomic_eager = OUTCOME_V2_NAMESPACE::experimental::awai template <class T> using atomic_lazy = OUTCOME_V2_NAMESPACE::experimental::awaitables::atomic_lazy<T>; template <class T> using eager = OUTCOME_V2_NAMESPACE::experimental::awaitables::eager<T>; template <class T> using lazy = OUTCOME_V2_NAMESPACE::experimental::awaitables::lazy<T>; -template <class T = void> using coroutine_handle = OUTCOME_V2_NAMESPACE::experimental::awaitables::coroutine_handle<T>; +template <class T = void> using coroutine_handle = OUTCOME_V2_NAMESPACE::awaitables::coroutine_handle<T>; #endif //! Choose an errc implementation @@ -274,74 +276,9 @@ namespace detail { inline std::ostream &operator<<(std::ostream &s, const file_io_error &v) { return s << "llfio::file_io_error(" << v.message().c_str() << ")"; } } // namespace detail -inline file_io_error error_from_exception(std::exception_ptr &&ep = std::current_exception(), file_io_error not_matched = generic_error(errc::resource_unavailable_try_again)) noexcept +inline file_io_error error_from_exception(std::exception_ptr &&ep = std::current_exception(), SYSTEM_ERROR2_NAMESPACE::system_code not_matched = errc::resource_unavailable_try_again) noexcept { - if(!ep) - { - return generic_error(errc::success); - } - try - { - std::rethrow_exception(ep); - } - catch(const std::invalid_argument & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::invalid_argument); - } - catch(const std::domain_error & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::argument_out_of_domain); - } - catch(const std::length_error & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::argument_list_too_long); - } - catch(const std::out_of_range & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::result_out_of_range); - } - catch(const std::logic_error & /*unused*/) /* base class for this group */ - { - ep = std::exception_ptr(); - return generic_error(errc::invalid_argument); - } - catch(const std::system_error &e) /* also catches ios::failure */ - { - ep = std::exception_ptr(); - if(e.code().category() == std::generic_category()) - { - return generic_error(static_cast<errc>(static_cast<int>(e.code().value()))); - } - // Don't know this error code category, so fall through - } - catch(const std::overflow_error & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::value_too_large); - } - catch(const std::range_error & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::result_out_of_range); - } - catch(const std::runtime_error & /*unused*/) /* base class for this group */ - { - ep = std::exception_ptr(); - return generic_error(errc::resource_unavailable_try_again); - } - catch(const std::bad_alloc & /*unused*/) - { - ep = std::exception_ptr(); - return generic_error(errc::not_enough_memory); - } - catch(...) - { - } - return not_matched; + return SYSTEM_ERROR2_NAMESPACE::system_code_from_exception(static_cast<std::exception_ptr &&>(ep), static_cast<SYSTEM_ERROR2_NAMESPACE::system_code &&>(not_matched)); } LLFIO_V2_NAMESPACE_END |