mirror of
https://git.mirrors.martin98.com/https://github.com/gulrak/filesystem
synced 2025-06-04 11:13:58 +08:00
Merge branch 'feature-70-path-comparison'
This commit is contained in:
commit
51d94c098a
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,7 @@
|
||||
/*build*/
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*~
|
||||
.DS_Store
|
||||
|
11
README.md
11
README.md
@ -489,9 +489,18 @@ to the expected behavior.
|
||||
|
||||
### v1.3.5 (WIP)
|
||||
|
||||
* Refactoring for [#73](https://github.com/gulrak/filesystem/issues/68), enhanced performance
|
||||
* Refactoring for [#73](https://github.com/gulrak/filesystem/issues/73), enhanced performance
|
||||
in path handling. the changes lead to much fewer path/string creations or copies, speeding
|
||||
up large directory iteration or operations on many path instances.
|
||||
* Bugfix for [#72](https://github.com/gulrak/filesystem/issues/72), the `TestAllocator` in
|
||||
`filesystem_test.cpp` was completed to fulfill the requirements to build on CentOS 7 with
|
||||
`devtoolset-9`. CentOS 7 and CentOS 8 are now part of the CI builds.
|
||||
* Bugfix for [#70](https://github.com/gulrak/filesystem/issues/70), root names are now case
|
||||
insensitive on Windows. This fix also adds the new behaviour switch `LWG_2936_BEHAVIOUR`
|
||||
that allows to enable post C++17 `fs::path::compare` behaviour, where the comparison is as
|
||||
if it was an element wise path comparison as described in
|
||||
[LWG 2936](https://cplusplus.github.io/LWG/issue2936) and C++20 `[fs.path.compare]`.
|
||||
It is default off in v1.3.6 and will be default starting from v1.4.0 as it changes ordering.
|
||||
|
||||
### [v1.3.4](https://github.com/gulrak/filesystem/releases/tag/v1.3.4)
|
||||
|
||||
|
@ -177,6 +177,15 @@
|
||||
// file with that name, it is superceded by P1164R1, so only activate if really needed
|
||||
// #define LWG_2935_BEHAVIOUR
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// LWG #2936 enables new element wise (more expensive) path comparison
|
||||
// * if this->root_name().native().compare(p.root_name().native()) != 0 return result
|
||||
// * if this->has_root_directory() and !p.has_root_directory() return -1
|
||||
// * if !this->has_root_directory() and p.has_root_directory() return -1
|
||||
// * else result of element wise comparison of path iteration where first comparison is != 0 or 0
|
||||
// if all comparisons are 0 (on Windows this implementation does case insensitive root_name()
|
||||
// comparison)
|
||||
// #define LWG_2936_BEHAVIOUR
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
|
||||
#define LWG_2937_BEHAVIOUR
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@ -1754,6 +1763,21 @@ GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2)
|
||||
#endif
|
||||
}
|
||||
|
||||
GHC_INLINE int compare_simple_insensitive(const char* str1, size_t len1, const char* str2, size_t len2)
|
||||
{
|
||||
while(len1 > 0 && len2 > 0 && ::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2)) {
|
||||
--len1; --len2;
|
||||
++str1; ++str2;
|
||||
}
|
||||
if (len1 && len2) {
|
||||
return *str1 < *str2 ? -1 : 1;
|
||||
}
|
||||
if(len1 == 0 && len2 == 0) {
|
||||
return 0;
|
||||
}
|
||||
return len1 == 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
GHC_INLINE const char* strerror_adapter(char* gnu, char*)
|
||||
{
|
||||
return gnu;
|
||||
@ -2652,24 +2676,78 @@ GHC_INLINE std::u32string path::generic_u32string() const
|
||||
// 30.10.8.4.8, compare
|
||||
GHC_INLINE int path::compare(const path& p) const noexcept
|
||||
{
|
||||
return native().compare(p.native());
|
||||
#ifdef LWG_2936_BEHAVIOUR
|
||||
auto rnl1 = root_name_length();
|
||||
auto rnl2 = p.root_name_length();
|
||||
#ifdef GHC_OS_WINDOWS
|
||||
auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2);
|
||||
#else
|
||||
auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2)));
|
||||
#endif
|
||||
if (rnc) {
|
||||
return rnc;
|
||||
}
|
||||
bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory();
|
||||
if (hrd1 != hrd2) {
|
||||
return hrd1 ? 1 : -1;
|
||||
}
|
||||
if (hrd1) {
|
||||
++rnl1;
|
||||
++rnl2;
|
||||
}
|
||||
auto iter1 = _path.begin() + static_cast<int>(rnl1);
|
||||
auto iter2 = p._path.begin() + static_cast<int>(rnl2);
|
||||
while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) {
|
||||
++iter1;
|
||||
++iter2;
|
||||
}
|
||||
if (iter1 == _path.end()) {
|
||||
return iter2 == p._path.end() ? 0 : -1;
|
||||
}
|
||||
if (iter2 == p._path.end()) {
|
||||
return 1;
|
||||
}
|
||||
if (*iter1 == '/') {
|
||||
return -1;
|
||||
}
|
||||
if (*iter2 == '/') {
|
||||
return 1;
|
||||
}
|
||||
return *iter1 < *iter2 ? -1 : 1;
|
||||
#else // LWG_2936_BEHAVIOUR
|
||||
#ifdef GHC_OS_WINDOWS
|
||||
auto rnl1 = root_name_length();
|
||||
auto rnl2 = p.root_name_length();
|
||||
auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2);
|
||||
if(rnc) {
|
||||
return rnc;
|
||||
}
|
||||
auto p1 = _path;
|
||||
std::replace(p1.begin()+static_cast<int>(rnl1), p1.end(), '/', '\\');
|
||||
auto p2 = p._path;
|
||||
std::replace(p2.begin()+static_cast<int>(rnl2), p2.end(), '/', '\\');
|
||||
return p1.compare(rnl1, std::string::npos, p2, rnl2, std::string::npos);
|
||||
#else
|
||||
return _path.compare(p._path);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
GHC_INLINE int path::compare(const string_type& s) const
|
||||
{
|
||||
return native().compare(path(s).native());
|
||||
return compare(path(s));
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_string_view
|
||||
GHC_INLINE int path::compare(std::basic_string_view<value_type> s) const
|
||||
{
|
||||
return native().compare(path(s).native());
|
||||
return compare(path(s));
|
||||
}
|
||||
#endif
|
||||
|
||||
GHC_INLINE int path::compare(const value_type* s) const
|
||||
{
|
||||
return native().compare(path(s).native());
|
||||
return compare(path(s));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -3116,32 +3194,32 @@ GHC_INLINE size_t hash_value(const path& p) noexcept
|
||||
|
||||
GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.generic_string() == rhs.generic_string();
|
||||
return lhs.compare(rhs) == 0;
|
||||
}
|
||||
|
||||
GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.generic_string() != rhs.generic_string();
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.generic_string() < rhs.generic_string();
|
||||
return lhs.compare(rhs) < 0;
|
||||
}
|
||||
|
||||
GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.generic_string() <= rhs.generic_string();
|
||||
return lhs.compare(rhs) <= 0;
|
||||
}
|
||||
|
||||
GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.generic_string() > rhs.generic_string();
|
||||
return lhs.compare(rhs) > 0;
|
||||
}
|
||||
|
||||
GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept
|
||||
{
|
||||
return lhs.generic_string() >= rhs.generic_string();
|
||||
return lhs.compare(rhs) >= 0;
|
||||
}
|
||||
|
||||
GHC_INLINE path operator/(const path& lhs, const path& rhs)
|
||||
|
@ -615,6 +615,17 @@ TEST_CASE("30.10.8.4.8 path compare", "[filesystem][path][fs.path.compare]")
|
||||
CHECK(fs::path("/foo/b").compare(fs::path("/foo/a")) > 0);
|
||||
CHECK(fs::path("/foo/b").compare(fs::path("/foo/b")) == 0);
|
||||
CHECK(fs::path("/foo/b").compare(fs::path("/foo/c")) < 0);
|
||||
|
||||
#ifdef GHC_OS_WINDOWS
|
||||
CHECK(fs::path("c:\\a\\b").compare("C:\\a\\b") == 0);
|
||||
CHECK(fs::path("c:\\a\\b").compare("d:\\a\\b") != 0);
|
||||
CHECK(fs::path("c:\\a\\b").compare("C:\\A\\b") != 0);
|
||||
#endif
|
||||
|
||||
#ifdef LWG_2936_BEHAVIOUR
|
||||
CHECK(fs::path("/a/b/").compare("/a/b/c") < 0);
|
||||
CHECK(fs::path("/a/b/").compare("a/c") > 0);
|
||||
#endif // LWG_2936_BEHAVIOUR
|
||||
}
|
||||
|
||||
TEST_CASE("30.10.8.4.9 path decomposition", "[filesystem][path][fs.path.decompose]")
|
||||
@ -897,6 +908,8 @@ TEST_CASE("30.10.8.4.11 path generation", "[filesystem][path][fs.path.gen]")
|
||||
CHECK(fs::path("c:/foo").lexically_relative("/bar") == "");
|
||||
CHECK(fs::path("c:foo").lexically_relative("c:/bar") == "");
|
||||
CHECK(fs::path("foo").lexically_relative("/bar") == "");
|
||||
CHECK(fs::path("c:/foo/bar.txt").lexically_relative("c:/foo/") == "bar.txt");
|
||||
CHECK(fs::path("c:/foo/bar.txt").lexically_relative("C:/foo/") == "bar.txt");
|
||||
#else
|
||||
CHECK(fs::path("/foo").lexically_relative("bar") == "");
|
||||
CHECK(fs::path("foo").lexically_relative("/bar") == "");
|
||||
@ -962,7 +975,13 @@ TEST_CASE("30.10.8.5 path iterators", "[filesystem][path][fs.path.itr]")
|
||||
CHECK("/,foo," == iterateResult(fs::path("/foo/")));
|
||||
CHECK("foo,bar" == iterateResult(fs::path("foo/bar")));
|
||||
CHECK("/,foo,bar" == iterateResult(fs::path("/foo/bar")));
|
||||
#ifndef USE_STD_FS
|
||||
// ghc::filesystem enforces redundant slashes to be reduced to one
|
||||
CHECK("/,foo,bar" == iterateResult(fs::path("///foo/bar")));
|
||||
#else
|
||||
// typically std::filesystem keeps them
|
||||
CHECK("///,foo,bar" == iterateResult(fs::path("///foo/bar")));
|
||||
#endif
|
||||
CHECK("/,foo,bar," == iterateResult(fs::path("/foo/bar///")));
|
||||
CHECK("foo,.,bar,..," == iterateResult(fs::path("foo/.///bar/../")));
|
||||
#ifdef GHC_OS_WINDOWS
|
||||
@ -979,7 +998,13 @@ TEST_CASE("30.10.8.5 path iterators", "[filesystem][path][fs.path.itr]")
|
||||
CHECK(",foo,/" == reverseIterateResult(fs::path("/foo/")));
|
||||
CHECK("bar,foo" == reverseIterateResult(fs::path("foo/bar")));
|
||||
CHECK("bar,foo,/" == reverseIterateResult(fs::path("/foo/bar")));
|
||||
#ifndef USE_STD_FS
|
||||
// ghc::filesystem enforces redundant slashes to be reduced to one
|
||||
CHECK("bar,foo,/" == reverseIterateResult(fs::path("///foo/bar")));
|
||||
#else
|
||||
// typically std::filesystem keeps them
|
||||
CHECK("bar,foo,///" == reverseIterateResult(fs::path("///foo/bar")));
|
||||
#endif
|
||||
CHECK(",bar,foo,/" == reverseIterateResult(fs::path("/foo/bar///")));
|
||||
CHECK(",..,bar,.,foo" == reverseIterateResult(fs::path("foo/.///bar/../")));
|
||||
#ifdef GHC_OS_WINDOWS
|
||||
@ -2331,12 +2356,9 @@ TEST_CASE("30.10.15.25 last_write_time", "[filesystem][operations][fs.op.last_wr
|
||||
CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
|
||||
nt = timeFromString("2015-10-21T04:29:00");
|
||||
CHECK_NOTHROW(fs::last_write_time("foo", nt, ec));
|
||||
std::cout << "about to call last_write_time" << std::endl;
|
||||
CHECK(std::abs(std::chrono::duration_cast<std::chrono::seconds>(fs::last_write_time("foo") - nt).count()) < 1);
|
||||
CHECK(!ec);
|
||||
std::cout << "about to call last_write_time" << std::endl;
|
||||
CHECK_THROWS_AS(fs::last_write_time("bar", nt), fs::filesystem_error);
|
||||
std::cout << "about to call last_write_time" << std::endl;
|
||||
CHECK_NOTHROW(fs::last_write_time("bar", nt, ec));
|
||||
CHECK(ec);
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user