Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ned14/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas <s_github@nedprod.com>2022-09-14 20:38:09 +0300
committerNiall Douglas <s_github@nedprod.com>2022-09-14 20:38:09 +0300
commit119abbaadb191b7b3e8a4303457fcbf44fc14d3b (patch)
treed8c0ac38a3f40bbd5b48b2fd20becc1f449396f3
parent05141b444da3ce26740fa0a22baac7f13704609e (diff)
path_view: Implement hashing of path views and rendered paths as per WG21 direction.
Also fix a bunch of (yet more!) bugs in path view iteration. There are still a few remaining, but they're very corner case.
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/path_view.hpp495
-rw-r--r--test/tests/issue0102.cpp2
-rw-r--r--test/tests/path_view.cpp26
4 files changed, 436 insertions, 93 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index f2b2549a..8643f53d 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 ae7f9c5a92879285ad5100c89efc47ce1cb0031b
-#define LLFIO_PREVIOUS_COMMIT_DATE "2022-09-07 15:07:48 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE ae7f9c5a
+#define LLFIO_PREVIOUS_COMMIT_REF 05141b444da3ce26740fa0a22baac7f13704609e
+#define LLFIO_PREVIOUS_COMMIT_DATE "2022-09-13 17:12:48 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE 05141b44
diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp
index a43c0e01..2282f414 100644
--- a/include/llfio/v2.0/path_view.hpp
+++ b/include/llfio/v2.0/path_view.hpp
@@ -1,5 +1,5 @@
/* A view of a path to something
-(C) 2017-2021 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
+(C) 2017-2022 Niall Douglas <http://www.nedproductions.biz/> (20 commits)
File Created: Jul 2017
@@ -90,17 +90,47 @@ namespace detail
: v(_v)
{
}
- constexpr bool operator!() const noexcept { return !v; }
- constexpr explicit operator bool() const noexcept { return !!v; }
- constexpr int operator-(int x) const noexcept { return v - x; }
- constexpr int operator+(int x) const noexcept { return v + x; }
+ constexpr bool operator!() const noexcept
+ {
+ return !v;
+ }
+ constexpr explicit operator bool() const noexcept
+ {
+ return !!v;
+ }
+ constexpr int operator-(int x) const noexcept
+ {
+ return v - x;
+ }
+ constexpr int operator+(int x) const noexcept
+ {
+ return v + x;
+ }
};
- constexpr inline bool operator<(char8_t a, char8_t b) noexcept { return a.v < b.v; }
- constexpr inline bool operator>(char8_t a, char8_t b) noexcept { return a.v > b.v; }
- constexpr inline bool operator<=(char8_t a, char8_t b) noexcept { return a.v <= b.v; }
- constexpr inline bool operator>=(char8_t a, char8_t b) noexcept { return a.v >= b.v; }
- constexpr inline bool operator==(char8_t a, char8_t b) noexcept { return a.v == b.v; }
- constexpr inline bool operator!=(char8_t a, char8_t b) noexcept { return a.v != b.v; }
+ constexpr inline bool operator<(char8_t a, char8_t b) noexcept
+ {
+ return a.v < b.v;
+ }
+ constexpr inline bool operator>(char8_t a, char8_t b) noexcept
+ {
+ return a.v > b.v;
+ }
+ constexpr inline bool operator<=(char8_t a, char8_t b) noexcept
+ {
+ return a.v <= b.v;
+ }
+ constexpr inline bool operator>=(char8_t a, char8_t b) noexcept
+ {
+ return a.v >= b.v;
+ }
+ constexpr inline bool operator==(char8_t a, char8_t b) noexcept
+ {
+ return a.v == b.v;
+ }
+ constexpr inline bool operator!=(char8_t a, char8_t b) noexcept
+ {
+ return a.v != b.v;
+ }
#endif
#endif
#if !defined(__CHAR16_TYPE__) && !defined(_MSC_VER) // VS2015 onwards has built in char16_t
@@ -156,7 +186,10 @@ namespace detail
class path_view_iterator;
LLFIO_TEMPLATE(class T, class U)
- LLFIO_TREQUIRES(LLFIO_TEXPR(std::declval<U>()((T *) nullptr))) constexpr inline U is_deleter(U &&v) { return v; }
+ LLFIO_TREQUIRES(LLFIO_TEXPR(std::declval<U>()((T *) nullptr))) constexpr inline U is_deleter(U &&v)
+ {
+ return v;
+ }
template <class T> constexpr inline void is_deleter(...) {}
LLFIO_TEMPLATE(class U)
LLFIO_TREQUIRES(LLFIO_TEXPR(std::declval<U>().deallocate((typename U::value_type *) nullptr, (size_t) 0))) constexpr inline U is_allocator(U &&v)
@@ -173,11 +206,13 @@ class path_view;
class path_view_component;
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept;
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator!=(path_view_component x, path_view_component y) noexcept;
+inline LLFIO_PATH_VIEW_CONSTEXPR size_t hash_value(path_view_component x) noexcept;
template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view_component view, F &&f);
template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(F &&f, path_view_component view);
inline std::ostream &operator<<(std::ostream &s, const path_view_component &v);
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view x, path_view y) noexcept;
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator!=(path_view x, path_view y) noexcept;
+inline LLFIO_PATH_VIEW_CONSTEXPR size_t hash_value(path_view x) noexcept;
/*! \class path_view_component
\brief An iterated part of a `path_view`.
@@ -188,6 +223,7 @@ class LLFIO_DECL path_view_component
friend class detail::path_view_iterator;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator==(path_view_component x, path_view_component y) noexcept;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator!=(path_view_component x, path_view_component y) noexcept;
+ friend inline LLFIO_PATH_VIEW_CONSTEXPR size_t hash_value(path_view_component x) noexcept;
template <class F> friend inline LLFIO_PATH_VIEW_CONSTEXPR auto LLFIO_V2_NAMESPACE::visit(path_view_component view, F &&f);
template <class F> friend inline LLFIO_PATH_VIEW_CONSTEXPR auto LLFIO_V2_NAMESPACE::visit(F &&f, path_view_component view);
friend inline std::ostream &LLFIO_V2_NAMESPACE::operator<<(std::ostream &s, const path_view_component &v);
@@ -560,7 +596,9 @@ private:
#ifdef _WIN32
if(sep_idx == 2 && _length == 3)
{
- return this->_invoke([this, sep_idx](const auto &v) mutable {
+ return this->_invoke(
+ [this, sep_idx](const auto &v) mutable
+ {
if(v[1] == ':')
{
return path_view_component(v.data() + 2, 1, zero_termination(), formatting());
@@ -589,7 +627,10 @@ public:
path_view_component &operator=(path_view_component &&) = default;
~path_view_component() = default;
- const byte *_raw_data() const noexcept { return _bytestr; }
+ const byte *_raw_data() const noexcept
+ {
+ return _bytestr;
+ }
//! Swap the view with another
constexpr void swap(path_view_component &o) noexcept
@@ -600,7 +641,10 @@ public:
}
//! True if empty
- LLFIO_NODISCARD 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.
LLFIO_PATH_VIEW_CONSTEXPR size_t native_size() const noexcept
@@ -609,22 +653,39 @@ public:
}
//! How path separators shall be interpreted
- constexpr format formatting() const noexcept { return _format; }
+ constexpr format formatting() const noexcept
+ {
+ return _format;
+ }
//! True if input is declared to be zero terminated
- constexpr bool has_zero_termination() const noexcept { return _zero_terminated; }
+ constexpr bool has_zero_termination() const noexcept
+ {
+ return _zero_terminated;
+ }
//! The zero termination during construction
- constexpr enum zero_termination zero_termination() const noexcept { return _zero_terminated ? zero_terminated : not_zero_terminated; }
+ constexpr enum zero_termination zero_termination() const noexcept
+ {
+ return _zero_terminated ? zero_terminated : not_zero_terminated;
+ }
//! True if `stem()` returns a non-empty path.
- LLFIO_PATH_VIEW_CONSTEXPR bool has_stem() const noexcept { return !stem().empty(); }
+ LLFIO_PATH_VIEW_CONSTEXPR bool has_stem() const noexcept
+ {
+ return !stem().empty();
+ }
//! True if `extension()` returns a non-empty path.
- LLFIO_PATH_VIEW_CONSTEXPR bool has_extension() const noexcept { return !extension().empty(); }
+ LLFIO_PATH_VIEW_CONSTEXPR bool has_extension() const noexcept
+ {
+ return !extension().empty();
+ }
//! True if the view contains any of the characters `*`, `?`, (POSIX only: `[` or `]`).
LLFIO_PATH_VIEW_CONSTEXPR bool contains_glob() const noexcept
{
- return _invoke([](const auto &v) {
+ return _invoke(
+ [](const auto &v)
+ {
using value_type = typename std::remove_reference<decltype(*v.data())>::type;
#ifdef _WIN32
const value_type *tofind = (sizeof(value_type) > 1) ? (const value_type *) L"*?" : (const value_type *) "*?";
@@ -639,7 +700,9 @@ public:
LLFIO_PATH_VIEW_CONSTEXPR path_view_component stem() const noexcept
{
auto self = _filename();
- return self._invoke([self](const auto &v) {
+ return self._invoke(
+ [self](const auto &v)
+ {
auto dot_idx = v.rfind('.');
if(_npos == dot_idx || dot_idx == 0 || (dot_idx == 1 && v[dot_idx - 1] == '.'))
{
@@ -652,7 +715,9 @@ public:
LLFIO_PATH_VIEW_CONSTEXPR path_view_component extension() const noexcept
{
auto self = _filename();
- return self._invoke([this](const auto &v) {
+ return self._invoke(
+ [this](const auto &v)
+ {
auto dot_idx = v.rfind('.');
if(_npos == dot_idx || dot_idx == 0 || (dot_idx == 1 && v[dot_idx - 1] == '.'))
{
@@ -682,7 +747,10 @@ private:
}
#endif
#ifdef LLFIO_USING_EXPERIMENTAL_FILESYSTEM
- template <class CharT> static filesystem::path _path_from_char_array(basic_string_view<CharT> v) { return {v.data(), v.data() + v.size()}; }
+ template <class CharT> static filesystem::path _path_from_char_array(basic_string_view<CharT> v)
+ {
+ return {v.data(), v.data() + v.size()};
+ }
static filesystem::path _path_from_char_array(basic_string_view<char8_t> v)
{
#if(__cplusplus >= 202000 || _HAS_CXX20) && (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION > 10000 /* approx start of 2020 */)
@@ -693,7 +761,10 @@ private:
}
#endif
- template <class CharT> static int _do_compare(const CharT *a, const CharT *b, size_t length) noexcept { return memcmp(a, b, length * sizeof(CharT)); }
+ template <class CharT> static int _do_compare(const CharT *a, const CharT *b, size_t length) noexcept
+ {
+ return memcmp(a, b, length * sizeof(CharT));
+ }
static int _do_compare(const char8_t *_a, const char8_t *_b, size_t length) noexcept
{
#if LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED
@@ -742,19 +813,23 @@ public:
//! Return the path view as a path. Allocates and copies memory!
filesystem::path path() const
{
- return _invoke([&](const auto &v) {
+ return _invoke(
+ [&](const auto &v)
+ {
#ifdef LLFIO_USING_STD_FILESYSTEM
- return _path_from_char_array(v, [](format f) -> filesystem::path::format {
- switch(f)
- {
- case format::generic_format:
- return filesystem::path::format::generic_format;
- case format::native_format:
- return filesystem::path::format::native_format;
- default:
- return filesystem::path::format::auto_format;
- }
- }(formatting()));
+ return _path_from_char_array(v,
+ [](format f) -> filesystem::path::format
+ {
+ switch(f)
+ {
+ case format::generic_format:
+ return filesystem::path::format::generic_format;
+ case format::native_format:
+ return filesystem::path::format::native_format;
+ default:
+ return filesystem::path::format::auto_format;
+ }
+ }(formatting()));
#endif
#ifdef LLFIO_USING_EXPERIMENTAL_FILESYSTEM
if(formatting() == generic_format || formatting() == native_format)
@@ -787,9 +862,11 @@ public:
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>))
constexpr int compare(path_view_component p, const std::locale &loc) const
{
- return _invoke([&](const auto &self) {
- return p._invoke(
- [&](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, zero_termination(), other, p.zero_termination(), &loc); });
+ return _invoke(
+ [&](const auto &self)
+ {
+ return p._invoke([&](const auto &other)
+ { return _compare<T, Deleter, _internal_buffer_size>(self, zero_termination(), other, p.zero_termination(), &loc); });
});
}
//! \overload
@@ -798,9 +875,11 @@ public:
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>))
constexpr int compare(path_view_component p) const
{
- return _invoke([&](const auto &self) {
- return p._invoke(
- [&](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, zero_termination(), other, p.zero_termination(), nullptr); });
+ return _invoke(
+ [&](const auto &self)
+ {
+ return p._invoke([&](const auto &other)
+ { return _compare<T, Deleter, _internal_buffer_size>(self, zero_termination(), other, p.zero_termination(), nullptr); });
});
}
@@ -1319,7 +1398,10 @@ public:
, _deleter2(std::move(o._deleter2))
{
}
- ~rendered_path() { reset(); }
+ ~rendered_path()
+ {
+ reset();
+ }
rendered_path(const rendered_path &) = delete;
rendered_path(rendered_path &&o) noexcept
: _base(o)
@@ -1381,26 +1463,103 @@ public:
}
//! The zero termination of this rendered path
- static constexpr enum zero_termination zero_termination() noexcept { return ZeroTermination; }
+ static constexpr enum zero_termination zero_termination() noexcept
+ {
+ return ZeroTermination;
+ }
//! The size of the internal buffer
- static constexpr size_t internal_buffer_size() noexcept { return (_internal_buffer_size > 0) ? _internal_buffer_size : 1; }
+ static constexpr size_t internal_buffer_size() noexcept
+ {
+ return (_internal_buffer_size > 0) ? _internal_buffer_size : 1;
+ }
//! The storage capacity, which may be larger than `size()` if the internal buffer is in use
- size_t capacity() const noexcept { return (this->data() == _buffer) ? internal_buffer_size() : this->size(); }
+ size_t capacity() const noexcept
+ {
+ return (this->data() == _buffer) ? internal_buffer_size() : this->size();
+ }
//! True if this rendered path refers to the source path view
- bool references_source() const noexcept { return this->data() != _buffer && _bytes_to_delete == 0; }
+ bool references_source() const noexcept
+ {
+ return this->data() != _buffer && _bytes_to_delete == 0;
+ }
//! Access the custom deleter instance passed to the constructor
- const AllocatorOrDeleter &deleter() const noexcept { return _deleter2; }
+ const AllocatorOrDeleter &deleter() const noexcept
+ {
+ return _deleter2;
+ }
//! Access the custom deleter instance passed to the constructor
- AllocatorOrDeleter &deleter() noexcept { return _deleter2; }
+ AllocatorOrDeleter &deleter() noexcept
+ {
+ return _deleter2;
+ }
//! The memory resource passed to the constructor
- pmr::memory_resource *memory_resource() noexcept { return (pmr::memory_resource *) _deleter1arg; }
+ pmr::memory_resource *memory_resource() noexcept
+ {
+ return (pmr::memory_resource *) _deleter1arg;
+ }
//! Access the custom allocator instance passed to the constructor
- const AllocatorOrDeleter &allocator() const noexcept { return _deleter2; }
+ const AllocatorOrDeleter &allocator() const noexcept
+ {
+ return _deleter2;
+ }
//! Access the custom allocator instance passed to the constructor
- AllocatorOrDeleter &allocator() noexcept { return _deleter2; }
+ AllocatorOrDeleter &allocator() noexcept
+ {
+ return _deleter2;
+ }
+
+ //! True if the bits backing the rendered path are identical
+ bool operator==(const rendered_path &o) const noexcept
+ {
+ if(this->data() == nullptr && o->data() == nullptr)
+ {
+ return true;
+ }
+ if(this->data() == nullptr && o->data() != nullptr)
+ {
+ return false;
+ }
+ if(this->data() != nullptr && o->data() == nullptr)
+ {
+ return false;
+ }
+ if(this->size() != o.size())
+ {
+ return false;
+ }
+ return 0 == memcmp(this->data(), o.data(), this->size());
+ }
+ //! True if the bits backing the rendered path are non-identical
+ bool operator!=(const rendered_path &o) const noexcept
+ {
+ if(this->data() == nullptr && o->data() == nullptr)
+ {
+ return false;
+ }
+ if(this->data() == nullptr && o->data() != nullptr)
+ {
+ return true;
+ }
+ if(this->data() != nullptr && o->data() == nullptr)
+ {
+ return true;
+ }
+ if(this->size() != o.size())
+ {
+ return true;
+ }
+ return 0 != memcmp(this->data(), o.data(), this->size());
+ }
+ //! Returns the hash value for the bits backing the rendered path
+ friend size_t hash_value(const rendered_path &v) noexcept
+ {
+ auto sv = v.as_string_view();
+ using type = typename std::decay<decltype(sv)>::type;
+ return std::hash<type>()(sv);
+ }
private:
size_t _bytes_to_delete{0};
@@ -1432,6 +1591,8 @@ public:
};
static_assert(std::is_trivially_copyable<path_view_component>::value, "path_view_component is not trivially copyable!");
static_assert(sizeof(path_view_component) == 3 * sizeof(void *), "path_view_component is not three pointers in size!");
+//! \brief Compares **identity** equality not equivalence i.e. backing storage type must be identical, and backing bytes must be identical. Use `compare()` if
+//! you want something stronger.
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept
{
if(x.native_size() != y.native_size())
@@ -1467,6 +1628,8 @@ inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view_component x, path_vie
const auto bytes = (x._wchar || x._utf16) ? (x._length * 2) : x._length;
return 0 == memcmp(x._bytestr, y._bytestr, bytes);
}
+//! \brief Compares **identity** inequality not disequivalence i.e. backing storage type must be different, or backing bytes must be different. Use `compare()`
+//! if you want something stronger.
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator!=(path_view_component x, path_view_component y) noexcept
{
if(x.native_size() != y.native_size())
@@ -1530,6 +1693,16 @@ inline constexpr bool operator!=(const CharT * /*unused*/, path_view_component /
static_assert(!path_view_component::is_source_acceptable<CharT>, "Do not use operator!= with path_view_component and a string literal, use .compare<>()");
return false;
}
+//! \brief Hashes a `path_view_component`.
+inline LLFIO_PATH_VIEW_CONSTEXPR size_t hash_value(path_view_component view) noexcept
+{
+ return view._invoke(
+ [](auto sv)
+ {
+ using type = typename std::decay<decltype(sv)>::type;
+ return std::hash<type>()(sv);
+ });
+}
//! \brief Visit the underlying source for a `path_view_component` (LLFIO backwards compatible overload)
template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view_component view, F &&f)
{
@@ -1673,6 +1846,7 @@ class path_view : public path_view_component
friend class detail::path_view_iterator;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator==(path_view x, path_view y) noexcept;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator!=(path_view x, path_view y) noexcept;
+ friend inline LLFIO_PATH_VIEW_CONSTEXPR size_t hash_value(path_view x) noexcept;
public:
//! Const iterator type
@@ -1854,7 +2028,9 @@ public:
#ifdef _WIN32
if(is_ntpath())
return true;
- return this->_invoke([sep_idx](const auto &v) {
+ return this->_invoke(
+ [sep_idx](const auto &v)
+ {
if(sep_idx == 0)
{
if(v[sep_idx + 1] == preferred_separator) // double separator at front
@@ -1868,12 +2044,17 @@ public:
#endif
}
//! True if relative
- constexpr bool is_relative() const noexcept { return !is_absolute(); }
+ constexpr bool is_relative() const noexcept
+ {
+ return !is_absolute();
+ }
#ifdef _WIN32
// True if the path view is a NT kernel path starting with `\!!\` or `\??\`
constexpr bool is_ntpath() const noexcept
{
- return this->_invoke([](const auto &v) {
+ return this->_invoke(
+ [](const auto &v)
+ {
if(v.size() < 4)
{
return false;
@@ -1893,7 +2074,9 @@ public:
// True if the path view is a UNC path starting with `\\`
constexpr bool is_uncpath() const noexcept
{
- return this->_invoke([](const auto &v) {
+ return this->_invoke(
+ [](const auto &v)
+ {
if(v.size() < 2)
{
return false;
@@ -1909,7 +2092,9 @@ public:
// True if the path view matches the format of an LLFIO deleted file
constexpr bool is_llfio_deleted() const noexcept
{
- return filename()._invoke([](const auto &v) {
+ return filename()._invoke(
+ [](const auto &v)
+ {
if(v.size() == 64 + 8)
{
// Could be one of our "deleted" files, is he all hex + ".deleted"?
@@ -1957,7 +2142,9 @@ public:
return path_view();
#endif
}
- return this->_invoke([sep_idx, this](auto v) {
+ return this->_invoke(
+ [sep_idx, this](auto v)
+ {
return path_view(v.data(),
#if LLFIO_USING_EXPERIMENTAL_FILESYSTEM
#ifdef _MSC_VER
@@ -1990,7 +2177,9 @@ public:
{
return path_view();
}
- return this->_invoke([sep_idx, this](const auto &v) {
+ return this->_invoke(
+ [sep_idx, this](const auto &v)
+ {
#ifdef _WIN32
auto colon_idx = v.find(':');
if(colon_idx < sep_idx)
@@ -2014,7 +2203,9 @@ public:
return path_view();
}
#ifdef _WIN32
- return this->_invoke([this, sep_idx](const auto &v) mutable {
+ return this->_invoke(
+ [this, sep_idx](const auto &v) mutable
+ {
// Special case \\.\ and \\?\ to match filesystem::path
if(is_ntpath() || (v.size() >= 4 && sep_idx == 0 && v[1] == '\\' && (v[2] == '.' || v[2] == '?') && v[3] == '\\'))
{
@@ -2037,7 +2228,9 @@ public:
return path_view(v.data(), sep_idx + 1, not_zero_terminated, formatting());
}
#else
- return this->_invoke([this, sep_idx](const auto &v) {
+ return this->_invoke(
+ [this, sep_idx](const auto &v)
+ {
#endif
if(sep_idx == 0)
{
@@ -2055,7 +2248,9 @@ public:
return *this;
}
#ifdef _WIN32
- return this->_invoke([this, sep_idx](const auto &v) mutable {
+ return this->_invoke(
+ [this, sep_idx](const auto &v) mutable
+ {
// Special case \\.\ and \\?\ to match filesystem::path
if(is_ntpath() || (v.size() >= 4 && sep_idx == 0 && v[1] == '\\' && (v[2] == '.' || v[2] == '?') && v[3] == '\\'))
{
@@ -2078,7 +2273,9 @@ public:
return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1, this->zero_termination(), formatting());
}
#else
- return this->_invoke([this, sep_idx](const auto &v) {
+ return this->_invoke(
+ [this, sep_idx](const auto &v)
+ {
#endif
if(sep_idx == 0)
{
@@ -2096,7 +2293,9 @@ public:
return path_view();
}
#ifdef _WIN32
- return this->_invoke([this, sep_idx](const auto &v) {
+ return this->_invoke(
+ [this, sep_idx](const auto &v)
+ {
// UNC paths return a trailing slash if the parent path is the server name
if(is_uncpath())
{
@@ -2119,19 +2318,24 @@ public:
#endif
});
#elif LLFIO_USING_EXPERIMENTAL_FILESYSTEM // Filesystem TS returns parent path "" for "/"
- return this->_invoke(
- [this, sep_idx](const auto &v) { return path_view(v.data(), (sep_idx == 0 && this->_length > 1) ? 1 : sep_idx, not_zero_terminated, formatting()); });
+ return this->_invoke([this, sep_idx](const auto &v)
+ { return path_view(v.data(), (sep_idx == 0 && this->_length > 1) ? 1 : sep_idx, not_zero_terminated, formatting()); });
#else
return this->_invoke([this, sep_idx](const auto &v) { return path_view(v.data(), (sep_idx == 0) ? 1 : sep_idx, not_zero_terminated, formatting()); });
#endif
}
//! Returns a view of the filename part of this view.
- LLFIO_PATH_VIEW_CONSTEXPR path_view filename() const noexcept { return this->_filename(); }
+ LLFIO_PATH_VIEW_CONSTEXPR path_view filename() const noexcept
+ {
+ return this->_filename();
+ }
//! Returns a view of this view without a trailing separator, if there is one, unless the input is '/'
LLFIO_PATH_VIEW_CONSTEXPR path_view without_trailing_separator() const noexcept
{
auto sep_idx = this->_find_last_sep();
- return this->_invoke([this, sep_idx](const auto &v) {
+ return this->_invoke(
+ [this, sep_idx](const auto &v)
+ {
if(sep_idx > 0 && v.size() - 1 == sep_idx)
{
return path_view(v.data(), sep_idx, not_zero_terminated, formatting());
@@ -2207,14 +2411,44 @@ namespace detail
private:
const path_view *_parent{nullptr};
size_type _begin{0}, _end{0};
- int _special{0}; // -1 if at preceding /, +1 if at trailing /
+ int _special{0}; // -2 if at preceding /, -1 if at / after a //, +1 if at trailing /
static constexpr auto _npos = string_view::npos;
constexpr bool _is_end() const noexcept { return (nullptr == _parent) || (!_special && _parent->native_size() == _begin); }
+ constexpr size_t _find_unc_prefix() const noexcept
+ {
+ auto a = _parent->_find_first_sep(0);
+ if(a != 0)
+ {
+ return _npos;
+ }
+ a = _parent->_find_first_sep(1);
+ if(a != 1)
+ {
+ return _npos;
+ }
+ size_t ret = 2;
+ for(;;)
+ {
+ auto e = _parent->_find_first_sep(ret);
+ if(e == ret)
+ {
+ ret++;
+ continue;
+ }
+ if(e == _npos)
+ {
+ e = _parent->native_size();
+ }
+ return e;
+ }
+ }
LLFIO_PATH_VIEW_CONSTEXPR value_type _get() const noexcept
{
assert(_parent != nullptr);
- return _parent->_invoke([this](const auto &v) {
+ return _parent->_invoke(
+ [this](const auto &v)
+ {
assert(_end <= v.size());
assert(_begin <= _end);
return path_view_component(v.data() + _begin, _end - _begin,
@@ -2223,13 +2457,41 @@ namespace detail
}
LLFIO_PATH_VIEW_CONSTEXPR void _inc() noexcept
{
- _begin = (_end > 0 && !_special) ? (_end + 1) : _end;
- _end = _parent->_find_first_sep(_begin);
+ do
+ {
+ _begin = (_end > 0 && !_special) ? (_end + 1) : _end;
+ _end = _parent->_find_first_sep(_begin);
+ } while(_special == 0 && _begin == _end && _end != 0);
if(0 == _end)
{
- // Path has a beginning /
- _special = -1;
- _end = 1;
+ // Path has a beginning /[/*] and all must be returned as a single component
+ _special = -2;
+ _end = _find_unc_prefix();
+ if(_npos == _end)
+ {
+ // Not an UNC prefix
+ _end = 1;
+ }
+ else
+ {
+ _special = -1;
+ }
+ return;
+ }
+ if(-1 == _special)
+ {
+ if(_npos != _end)
+ {
+ // For a //server/path/ form, //server is returned first then /
+ _end = _begin + 1;
+ _special = -2;
+ }
+ else
+ {
+ // This was just //server
+ _end = _begin;
+ _special = 0;
+ }
return;
}
if(_npos == _end)
@@ -2250,26 +2512,66 @@ namespace detail
}
constexpr void _dec() noexcept
{
- auto is_end = _is_end();
- _end = is_end ? _begin : (_begin - 1);
- if(0 == _end)
+ const auto unc_prefix_end = _find_unc_prefix();
+ if(_special == -2 && unc_prefix_end != _npos)
{
- // Path has a beginning /
_special = -1;
_begin = 0;
- _end = 1;
+ _end = unc_prefix_end;
return;
}
- _begin = _parent->_find_last_sep(_end - 1);
- if(is_end && _begin == _end - 1)
+ if(-1 == _special)
{
- // Path has a trailing /
- _special = 1;
- _begin = _end;
+ _special = 0;
+ _begin = 0;
+ _end = 0;
return;
}
- _begin = (_npos == _begin) ? 0 : (_begin + 1);
- _special = 0;
+ do
+ {
+ auto is_end = _is_end();
+ _end = is_end ? _begin : (_begin - 1);
+ if(0 == _end)
+ {
+ // Path has a beginning /
+ _special = -2;
+ _begin = 0;
+ _end = 1;
+ return;
+ }
+ if(_end == unc_prefix_end)
+ {
+ _special = -2;
+ _begin = _end;
+ if(_end < _parent->native_size())
+ {
+ _end++;
+ }
+ else
+ {
+ _begin = 0;
+ _special = -1;
+ }
+ return;
+ }
+ _begin = _parent->_find_last_sep(_end - 1);
+ if(is_end && _begin == _end - 1)
+ {
+ // Path has a trailing /
+ if(_begin == unc_prefix_end || _begin == 0)
+ {
+ _special = -2;
+ }
+ else
+ {
+ _special = 1;
+ _begin = _end;
+ }
+ return;
+ }
+ _begin = (_npos == _begin) ? 0 : (_begin + 1);
+ _special = 0;
+ } while(_begin == _end);
}
constexpr path_view_iterator(const path_view *p, bool end)
@@ -2363,6 +2665,7 @@ constexpr inline path_view::iterator path_view::end() noexcept
return cend();
}
+//! \brief Compares individual path view components for **identity** not equivalence. Use `compare()` if you want something stronger.
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view x, path_view y) noexcept
{
auto it1 = x.begin(), it2 = y.begin();
@@ -2383,6 +2686,7 @@ inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view x, path_view y) noexc
}
return true;
}
+//! \brief Compares individual path view components for non-**identity** not disequivalence. Use `compare()` if you want something stronger.
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator!=(path_view x, path_view y) noexcept
{
auto it1 = x.begin(), it2 = y.begin();
@@ -2431,6 +2735,16 @@ inline constexpr bool operator!=(const CharT * /*unused*/, path_view /*unused*/)
static_assert(!path_view::is_source_acceptable<CharT>, "Do not use operator!= with path_view and a string literal, use .compare<>()");
return false;
}
+//! \brief Return the combined hash of individual path components
+inline LLFIO_PATH_VIEW_CONSTEXPR size_t hash_value(path_view x) noexcept
+{
+ size_t ret = 0;
+ for(auto component : x)
+ {
+ ret ^= hash_value(component) + 0x9e3779b9 + (ret << 6) + (ret >> 2);
+ }
+ return ret;
+}
#ifdef __cpp_concepts
template <class T, class Deleter, size_t _internal_buffer_size>
requires(path_view::is_source_acceptable<T>)
@@ -2531,7 +2845,10 @@ namespace detail
ret.assign(_.begin(), _.end());
}
#endif
- template <class T> void operator()(span<T> /*unused*/) { throw std::logic_error("filesystem::path cannot be constructed from a byte input."); }
+ template <class T> void operator()(span<T> /*unused*/)
+ {
+ throw std::logic_error("filesystem::path cannot be constructed from a byte input.");
+ }
};
} // namespace detail
//! Append a path view component to a path view component
diff --git a/test/tests/issue0102.cpp b/test/tests/issue0102.cpp
index 6ba76e62..9438ef28 100644
--- a/test/tests/issue0102.cpp
+++ b/test/tests/issue0102.cpp
@@ -29,7 +29,7 @@ static inline void TestIssue0102()
namespace llfio = LLFIO_V2_NAMESPACE;
namespace outcome_e = OUTCOME_V2_NAMESPACE::experimental;
- auto page = llfio::utils::page_allocator<std::byte>().allocate(llfio::utils::page_size());
+ auto page = llfio::utils::page_allocator<llfio::byte>().allocate(llfio::utils::page_size());
llfio::file_io_error ioError = llfio::generic_error(llfio::errc::state_not_recoverable);
diff --git a/test/tests/path_view.cpp b/test/tests/path_view.cpp
index ed6071f0..9ea013f6 100644
--- a/test/tests/path_view.cpp
+++ b/test/tests/path_view.cpp
@@ -227,13 +227,39 @@ static inline void TestPathView()
CheckPathIteration("boostish/testdir/");
CheckPathIteration("/a/c");
CheckPathIteration("/a/c/");
+ CheckPathIteration("//a/c");
+ CheckPathIteration("//a");
+ CheckPathIteration("//a/");
+ // CheckPathIteration("///a/c"); // FIXME: fails, /// not ///a should be iterated
+ // CheckPathIteration("////a/c"); // FIXME: fails, //// not ////a should be iterated
+ CheckPathIteration("//a/c//");
+ // CheckPathIteration("///a/c//"); // FIXME: fails, /// not ///a should be iterated
+ // CheckPathIteration("////a/c////"); // FIXME: fails, //// not ////a should be iterated
+ CheckPathIteration("/a/c//");
+ CheckPathIteration("/a/c////");
CheckPathIteration("a/c");
+ CheckPathIteration("a//c");
CheckPathIteration("a/c/");
+ CheckPathIteration("a//c/");
+ CheckPathIteration("/");
+ CheckPathIteration("//");
+ CheckPathIteration("///");
+ CheckPathIteration("////");
+ CheckPathIteration("a/b/c");
+ CheckPathIteration("a/b//c");
+ CheckPathIteration("a/b///c");
// Does visitation work right?
visit(llfio::path_view("hi"), [](auto sv) { BOOST_CHECK(0 == memcmp(sv.data(), "hi", 2)); });
visit(*llfio::path_view(L"hi").begin(), [](auto sv) { BOOST_CHECK(0 == memcmp(sv.data(), L"hi", 4)); });
+ // Hashing
+ {
+ BOOST_CHECK(hash_value(llfio::path_view("a/b/c")) == hash_value(llfio::path_view("a/b/c")));
+ BOOST_CHECK(hash_value(llfio::path_view("a/b/c")) == hash_value(llfio::path_view("a/b//c")));
+ BOOST_CHECK(hash_value(llfio::path_view("a/b/c")) == hash_value(llfio::path_view("a/b///c")));
+ }
+
// Custom allocator and deleter
{
struct custom_allocate