diff options
Diffstat (limited to 'contrib/libc++/src/experimental/filesystem/path.cpp')
-rw-r--r-- | contrib/libc++/src/experimental/filesystem/path.cpp | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/contrib/libc++/src/experimental/filesystem/path.cpp b/contrib/libc++/src/experimental/filesystem/path.cpp new file mode 100644 index 0000000..38c4498 --- /dev/null +++ b/contrib/libc++/src/experimental/filesystem/path.cpp @@ -0,0 +1,391 @@ +//===--------------------- filesystem/path.cpp ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "experimental/filesystem" +#include "experimental/string_view" +#include "utility" + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM + +_LIBCPP_CONSTEXPR path::value_type path::preferred_separator; + +namespace { namespace parser +{ + +using string_type = string_view; +using value_type = path::value_type; + +using string_view_pair = pair<string_view, string_view>; + +// status reporting +constexpr size_t npos = static_cast<size_t>(-1); + +inline bool good(size_t pos) { return pos != npos; } + +// lexical elements +constexpr value_type preferred_separator = path::preferred_separator; +constexpr value_type const * preferred_separator_str = "/"; +constexpr value_type const * dot = "."; + +// forward // +bool is_separator(string_type const &, size_t); +bool is_root_name(const string_type&, size_t); +bool is_root_directory(string_type const &, size_t); +bool is_trailing_separator(string_type const &, size_t); + +size_t start_of(string_type const &, size_t); +size_t end_of(string_type const &, size_t); + +size_t root_name_start(const string_type& s); +size_t root_name_end(const string_type&); + +size_t root_directory_start(string_type const &); +size_t root_directory_end(string_type const &); + +string_view_pair separate_filename(string_type const &); +string_view extract_raw(string_type const &, size_t); +string_view extract_preferred(string_type const &, size_t); + +inline bool is_separator(const string_type& s, size_t pos) { + return (pos < s.size() && s[pos] == preferred_separator); +} + +inline bool is_root_name(const string_type& s, size_t pos) { + return good(pos) && pos == 0 ? root_name_start(s) == pos : false; +} + +inline bool is_root_directory(const string_type& s, size_t pos) { + return good(pos) ? root_directory_start(s) == pos : false; +} + +inline bool is_trailing_separator(const string_type& s, size_t pos) { + return (pos < s.size() && is_separator(s, pos) && + end_of(s, pos) == s.size()-1 && + !is_root_directory(s, pos) && !is_root_name(s, pos)); +} + +size_t start_of(const string_type& s, size_t pos) { + if (pos >= s.size()) return npos; + bool in_sep = (s[pos] == preferred_separator); + while (pos - 1 < s.size() && + (s[pos-1] == preferred_separator) == in_sep) + { --pos; } + if (pos == 2 && !in_sep && s[0] == preferred_separator && + s[1] == preferred_separator) + { return 0; } + return pos; +} + +size_t end_of(const string_type& s, size_t pos) { + if (pos >= s.size()) return npos; + // special case for root name + if (pos == 0 && is_root_name(s, pos)) return root_name_end(s); + bool in_sep = (s[pos] == preferred_separator); + while (pos + 1 < s.size() && (s[pos+1] == preferred_separator) == in_sep) + { ++pos; } + return pos; +} + +inline size_t root_name_start(const string_type& s) { + return good(root_name_end(s)) ? 0 : npos; +} + +size_t root_name_end(const string_type& s) { + if (s.size() < 2 || s[0] != preferred_separator + || s[1] != preferred_separator) { + return npos; + } + if (s.size() == 2) { + return 1; + } + size_t index = 2; // current position + if (s[index] == preferred_separator) { + return npos; + } + while (index + 1 < s.size() && s[index+1] != preferred_separator) { + ++index; + } + return index; +} + +size_t root_directory_start(const string_type& s) { + size_t e = root_name_end(s); + if (!good(e)) + return is_separator(s, 0) ? 0 : npos; + return is_separator(s, e + 1) ? e + 1 : npos; +} + +size_t root_directory_end(const string_type& s) { + size_t st = root_directory_start(s); + if (!good(st)) return npos; + size_t index = st; + while (index + 1 < s.size() && s[index + 1] == preferred_separator) + { ++index; } + return index; +} + +string_view_pair separate_filename(string_type const & s) { + if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; + auto pos = s.find_last_of('.'); + if (pos == string_type::npos) return string_view_pair{s, string_view{}}; + return string_view_pair{s.substr(0, pos), s.substr(pos)}; +} + +inline string_view extract_raw(const string_type& s, size_t pos) { + size_t end_i = end_of(s, pos); + if (!good(end_i)) return string_view{}; + return string_view(s).substr(pos, end_i - pos + 1); +} + +string_view extract_preferred(const string_type& s, size_t pos) { + string_view raw = extract_raw(s, pos); + if (raw.empty()) + return raw; + if (is_trailing_separator(s, pos)) + return string_view{dot}; + if (is_separator(s, pos) && !is_root_name(s, pos)) + return string_view(preferred_separator_str); + return raw; +} + +}} // namespace parser + + +//////////////////////////////////////////////////////////////////////////////// +// path_view_iterator +//////////////////////////////////////////////////////////////////////////////// +namespace { + +struct path_view_iterator { + const string_view __s_; + size_t __pos_; + + explicit path_view_iterator(string_view const& __s) : __s_(__s), __pos_(__s_.empty() ? parser::npos : 0) {} + explicit path_view_iterator(string_view const& __s, size_t __p) : __s_(__s), __pos_(__p) {} + + string_view operator*() const { + return parser::extract_preferred(__s_, __pos_); + } + + path_view_iterator& operator++() { + increment(); + return *this; + } + + path_view_iterator& operator--() { + decrement(); + return *this; + } + + void increment() { + if (__pos_ == parser::npos) return; + while (! set_position(parser::end_of(__s_, __pos_)+1)) + ; + return; + } + + void decrement() { + if (__pos_ == 0) { + set_position(0); + } + else if (__pos_ == parser::npos) { + auto const str_size = __s_.size(); + set_position(parser::start_of( + __s_, str_size != 0 ? str_size - 1 : str_size)); + } else { + while (!set_position(parser::start_of(__s_, __pos_-1))) + ; + } + } + + bool set_position(size_t pos) { + if (pos >= __s_.size()) { + __pos_ = parser::npos; + } else { + __pos_ = pos; + } + return valid_iterator_position(); + } + + bool valid_iterator_position() const { + if (__pos_ == parser::npos) return true; // end position is valid + return (!parser::is_separator (__s_, __pos_) || + parser::is_root_directory (__s_, __pos_) || + parser::is_trailing_separator(__s_, __pos_) || + parser::is_root_name (__s_, __pos_)); + } + + bool is_end() const { return __pos_ == parser::npos; } + + inline bool operator==(path_view_iterator const& __p) { + return __pos_ == __p.__pos_; + } +}; + +path_view_iterator pbegin(path const& p) { + return path_view_iterator(p.native()); +} + +path_view_iterator pend(path const& p) { + path_view_iterator __p(p.native()); + __p.__pos_ = parser::npos; + return __p; +} + +} // end namespace +/////////////////////////////////////////////////////////////////////////////// +// path definitions +/////////////////////////////////////////////////////////////////////////////// + +path & path::replace_extension(path const & replacement) +{ + path p = extension(); + if (not p.empty()) { + __pn_.erase(__pn_.size() - p.native().size()); + } + if (!replacement.empty()) { + if (replacement.native()[0] != '.') { + __pn_ += "."; + } + __pn_.append(replacement.__pn_); + } + return *this; +} + +/////////////////////////////////////////////////////////////////////////////// +// path.decompose + +string_view path::__root_name() const +{ + return parser::is_root_name(__pn_, 0) + ? parser::extract_preferred(__pn_, 0) + : string_view{}; +} + +string_view path::__root_directory() const +{ + auto start_i = parser::root_directory_start(__pn_); + if(!parser::good(start_i)) { + return {}; + } + return parser::extract_preferred(__pn_, start_i); +} + +string_view path::__relative_path() const +{ + if (empty()) { + return {__pn_}; + } + auto end_i = parser::root_directory_end(__pn_); + if (not parser::good(end_i)) { + end_i = parser::root_name_end(__pn_); + } + if (not parser::good(end_i)) { + return {__pn_}; + } + return string_view(__pn_).substr(end_i+1); +} + +string_view path::__parent_path() const +{ + if (empty() || pbegin(*this) == --pend(*this)) { + return {}; + } + auto end_it = --(--pend(*this)); + auto end_i = parser::end_of(__pn_, end_it.__pos_); + return string_view(__pn_).substr(0, end_i+1); +} + +string_view path::__filename() const +{ + return empty() ? string_view{} : *--pend(*this); +} + +string_view path::__stem() const +{ + return parser::separate_filename(__filename()).first; +} + +string_view path::__extension() const +{ + return parser::separate_filename(__filename()).second; +} + +//////////////////////////////////////////////////////////////////////////// +// path.comparisons +int path::__compare(const value_type* __s) const { + path_view_iterator thisIter(this->native()); + path_view_iterator sIter(__s); + while (!thisIter.is_end() && !sIter.is_end()) { + int res = (*thisIter).compare(*sIter); + if (res != 0) return res; + ++thisIter; ++sIter; + } + if (thisIter.is_end() && sIter.is_end()) + return 0; + if (thisIter.is_end()) + return -1; + return 1; +} + +//////////////////////////////////////////////////////////////////////////// +// path.nonmembers +size_t hash_value(const path& __p) _NOEXCEPT { + path_view_iterator thisIter(__p.native()); + struct HashPairT { + size_t first; + size_t second; + }; + HashPairT hp = {0, 0}; + std::hash<string_view> hasher; + std::__scalar_hash<decltype(hp)> pair_hasher; + while (!thisIter.is_end()) { + hp.second = hasher(*thisIter); + hp.first = pair_hasher(hp); + ++thisIter; + } + return hp.first; +} + +//////////////////////////////////////////////////////////////////////////// +// path.itr +path::iterator path::begin() const +{ + path_view_iterator pit = pbegin(*this); + iterator it; + it.__path_ptr_ = this; + it.__pos_ = pit.__pos_; + it.__elem_.__assign_view(*pit); + return it; +} + +path::iterator path::end() const +{ + iterator it{}; + it.__path_ptr_ = this; + it.__pos_ = parser::npos; + return it; +} + +path::iterator& path::iterator::__increment() { + path_view_iterator it(__path_ptr_->native(), __pos_); + it.increment(); + __pos_ = it.__pos_; + __elem_.__assign_view(*it); + return *this; +} + +path::iterator& path::iterator::__decrement() { + path_view_iterator it(__path_ptr_->native(), __pos_); + it.decrement(); + __pos_ = it.__pos_; + __elem_.__assign_view(*it); + return *this; +} + +_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM |