From d488c5fb945e30a491057905bd01ab1e8c58df5d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 27 Jun 2017 19:08:41 -0700 Subject: [PATCH] finished implementing util::filesystem::path --- src/util/filesystem.cpp | 33 ++++-- src/util/filesystem.h | 240 +++++++++++++++++++++++++++++++++++----- 2 files changed, 235 insertions(+), 38 deletions(-) diff --git a/src/util/filesystem.cpp b/src/util/filesystem.cpp index b95952e..aab9daf 100644 --- a/src/util/filesystem.cpp +++ b/src/util/filesystem.cpp @@ -32,7 +32,7 @@ namespace filesystem { namespace detail { -#if 1 +#if 0 #warning testing util::filesystem::path struct Path_tester { @@ -108,6 +108,9 @@ struct Path_tester "a/a.a", "a/.a.", "a/.a.a", + "a/b/c/d/../.././e/../../f", + "C:../.", + "/../.././", }) { Path p(test_path_string); @@ -117,18 +120,28 @@ struct Path_tester std::cout << "make_preferred -> " << Path(p).make_preferred() << std::endl; std::cout << "remove_filename -> " << Path(p).remove_filename() << std::endl; std::cout << "lexically_normal -> " << p.lexically_normal() << std::endl; - std::cout << "operator/=:"; + std::cout << "root_name -> " << p.root_name() << std::endl; + std::cout << "root_directory -> " << p.root_directory() << std::endl; + std::cout << "root_path -> " << p.root_path() << std::endl; + std::cout << "relative_path -> " << p.relative_path() << std::endl; + std::cout << "parent_path -> " << p.parent_path() << std::endl; + std::cout << "filename -> " << p.filename() << std::endl; + std::cout << "stem -> " << p.stem() << std::endl; + std::cout << "extension -> " << p.extension() << std::endl; + std::cout << "operator/:"; for(auto *appended_path : { - "", - "/abc", - "C:abc", - "//a/abc", - "C:/abc", - "abc", + "", "/abc", "C:abc", "//a/abc", "C:/abc", "abc", + }) + { + std::cout << " \"" << appended_path << "\"->" << p / appended_path; + } + std::cout << std::endl; + std::cout << "lexically_proximate:"; + for(auto *base_path : { + "", "/abc", "C:abc", "//a/abc", "C:/abc", "abc", }) { - std::cout << " \"" << appended_path << "\"->" - << Path(p).operator/=(appended_path); + std::cout << " \"" << base_path << "\"->" << p.lexically_proximate(base_path); } std::cout << std::endl; } diff --git a/src/util/filesystem.h b/src/util/filesystem.h index 080182a..7d4c805 100644 --- a/src/util/filesystem.h +++ b/src/util/filesystem.h @@ -322,10 +322,15 @@ struct Path_convert_range }; template -struct Path_convert_range::value>::type> +struct + Path_convert_range:: + Char_type>::value + && Path_is_convertable_iterator_type::value>::type> { static constexpr bool is_convertible = true; template , @@ -377,13 +382,14 @@ template , typename std:: - enable_if:: - const_iterator>:: - is_convertible>::type> + enable_if::value + && Path_convert_range:: + const_iterator>:: + is_convertible>::type> { typedef Path_convert_range:: @@ -518,9 +524,24 @@ template ::preferred_separator, bool Needs_root_name_to_be_absolute = detail::Path_traits::needs_root_name_to_be_absolute> +class basic_path; + +template +std::size_t hash_value( + const basic_path + &v) noexcept; + +template class basic_path { friend struct detail::Path_tester; + friend std::size_t hash_value(const basic_path &v) noexcept; static_assert(std::is_same::value || std::is_same::value, ""); @@ -1251,10 +1272,12 @@ public: basic_path &operator/=(const string_type &p) { append_string(p); + return *this; } basic_path &operator/=(const string_view_type &p) { append_string(p); + return *this; } template basic_path &operator/=(const Source &source) @@ -1275,6 +1298,14 @@ public: detail::Path_convert_range::to_string(first, last)); return *this; } + friend basic_path operator/(const basic_path &l, const basic_path &r) + { + return basic_path(l) /= r; + } + friend basic_path operator/(basic_path &&l, const basic_path &r) + { + return l /= r; + } basic_path &operator+=(const basic_path &p) { value += p.value; @@ -1565,7 +1596,7 @@ private: Path_part_kind b_kind) noexcept { constexpr Char_type generic_separator_char = '/'; - constexpr string_view_type generic_separator(&generic_separator_char, 1); + string_view_type generic_separator(&generic_separator_char, 1); if(a_kind == Path_part_kind::root_dir) a = generic_separator; if(b_kind == Path_part_kind::root_dir) @@ -1652,6 +1683,78 @@ public: { return compare(rt.value); } + friend bool operator==(const basic_path &l, const basic_path &r) noexcept + { + return l.compare(r) == 0; + } + friend bool operator!=(const basic_path &l, const basic_path &r) noexcept + { + return l.compare(r) != 0; + } + friend bool operator<=(const basic_path &l, const basic_path &r) noexcept + { + return l.compare(r) <= 0; + } + friend bool operator>=(const basic_path &l, const basic_path &r) noexcept + { + return l.compare(r) >= 0; + } + friend bool operator<(const basic_path &l, const basic_path &r) noexcept + { + return l.compare(r) < 0; + } + friend bool operator>(const basic_path &l, const basic_path &r) noexcept + { + return l.compare(r) > 0; + } + friend bool operator==(const basic_path &l, string_view_type r) noexcept + { + return l.compare(r) == 0; + } + friend bool operator!=(const basic_path &l, string_view_type r) noexcept + { + return l.compare(r) != 0; + } + friend bool operator<=(const basic_path &l, string_view_type r) noexcept + { + return l.compare(r) <= 0; + } + friend bool operator>=(const basic_path &l, string_view_type r) noexcept + { + return l.compare(r) >= 0; + } + friend bool operator<(const basic_path &l, string_view_type r) noexcept + { + return l.compare(r) < 0; + } + friend bool operator>(const basic_path &l, string_view_type r) noexcept + { + return l.compare(r) > 0; + } + friend bool operator==(string_view_type l, const basic_path &r) noexcept + { + return r.compare(l) == 0; + } + friend bool operator!=(string_view_type l, const basic_path &r) noexcept + { + return r.compare(l) != 0; + } + friend bool operator<=(string_view_type l, const basic_path &r) noexcept + { + return r.compare(l) >= 0; + } + friend bool operator>=(string_view_type l, const basic_path &r) noexcept + { + return r.compare(l) <= 0; + } + friend bool operator<(string_view_type l, const basic_path &r) noexcept + { + return r.compare(l) > 0; + } + friend bool operator>(string_view_type l, const basic_path &r) noexcept + { + return r.compare(l) < 0; + } iterator begin() const noexcept { return iterator(this, 0); @@ -1662,27 +1765,17 @@ public: } basic_path root_name() const { - auto iter = begin(); - if(iter == end()) + auto index_range = get_root_name_index_range(value); + if(index_range.empty()) return {}; - if(iter->kind == Path_part_kind::relative_root_name - || iter->kind == Path_part_kind::absolute_root_name) - return *iter; - return {}; + return value.substr(index_range.begin, index_range.size()); } basic_path root_directory() const { - auto iter = begin(); - if(iter == end()) - return {}; - if(iter->kind == Path_part_kind::relative_root_name - || iter->kind == Path_part_kind::absolute_root_name) - ++iter; - if(iter == end()) + auto index_range = get_root_dir_index_range(value); + if(index_range.empty()) return {}; - if(iter->kind == Path_part_kind::root_dir) - return *iter; - return {}; + return value.substr(index_range.begin, index_range.size()); } basic_path root_path() const { @@ -1888,6 +1981,66 @@ public: retval.parse(); return retval; } + basic_path lexically_relative(const basic_path &base) const + { + constexpr Char_type dot_char = '.'; + constexpr std::size_t dot_dot_size = 2; + constexpr std::size_t dot_size = 1; + constexpr Char_type dot_dot_storage[dot_dot_size + 1] = {dot_char, dot_char}; + constexpr Char_type dot_storage[dot_size + 1] = {dot_char}; + string_view_type dot_dot(dot_dot_storage, dot_dot_size); + string_view_type dot(dot_storage, dot_size); + if(root_name() != base.root_name()) + return {}; + if(is_absolute() != base.is_absolute()) + return {}; + if(!has_root_directory() && base.has_root_directory()) + return {}; + auto a = begin(); + auto b = base.begin(); + while(a != end() && b != base.end() && *a == *b) + { + ++a; + ++b; + } + if(a == end() && b == base.end()) + return dot; + std::ptrdiff_t n = 0; + for(auto i = b; i != base.end(); ++i) + { + if(i->kind == Path_part_kind::file_name) + { + if(i->value == dot_dot) + n--; + else if(i->value != dot) + n++; + } + } + if(n < 0) + return {}; + std::size_t retval_value_reserve_size = static_cast(n) * (dot_dot.size() + 1); + std::size_t retval_parts_reserve_size = n; + for(auto i = a; i != end(); ++i) + { + retval_value_reserve_size += 1 + i->value.size(); + retval_parts_reserve_size++; + } + basic_path retval; + retval.value.reserve(retval_value_reserve_size); + retval.parts.reserve(retval_parts_reserve_size); + for(std::size_t i = n; i > 0; i--) + retval /= dot_dot; + for(auto i = a; i != end(); ++i) + retval /= *i; + return retval; + } + basic_path lexically_proximate(const basic_path &base) const + { + auto retval = lexically_relative(base); + if(retval.empty()) + return *this; + return retval; + } #warning finish }; @@ -1903,6 +2056,25 @@ void swap( l.swap(r); } +/** @note the filesystem specification specifies to have hash_value instead of a std::hash + * specialization */ +template +std::size_t hash_value( + const basic_path + &v) noexcept +{ + std::size_t retval = 0; + for(auto &part : v.parts) + { + retval *= 849372545UL; + retval ^= std::hash>()(part.value); + } + return retval; +} + template path; + +template +path u8path(const Source &source) +{ + return path(source); +} + +template +path u8path(Input_iterator first, Input_iterator last) +{ + return path(first, last); +} } } } -- 2.30.2