Author: ericwf Date: Sun Oct 30 18:30:38 2016 New Revision: 285526 URL: http://llvm.org/viewvc/llvm-project?rev=285526&view=rev Log: Rewrite std::filesystem::path iterators and parser
This patch entirely rewrites the parsing logic for paths. Unlike the previous implementation this one stores information about the current state; For example if we are in a trailing separator or a root separator. This avoids the need for extra lookahead (and extra work) when incrementing or decrementing an iterator. Roughly this gives us a 15% speedup over the previous implementation. Unfortunately this implementation is still a lot slower than libstdc++'s. Because libstdc++ pre-parses and splits the path upon construction their iterators are trivial to increment/decrement. This makes libc++ lazy parsing 100x slower than libstdc++. However the pre-parsing libstdc++ causes a ton of extra and unneeded allocations when constructing the string. For example `path("/foo/bar/")` would require at least 5 allocations with libstdc++ whereas libc++ uses only one. The non-allocating behavior is much preferable when you consider filesystem usages like 'exists("/foo/bar/")'. Even then libc++'s path seems to be twice as slow to simply construct compared to libstdc++. More investigation is needed about this. Added: libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.itr/ libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.itr/iterator_db.pass.cpp Modified: libcxx/trunk/benchmarks/CMakeLists.txt libcxx/trunk/include/experimental/filesystem libcxx/trunk/src/experimental/filesystem/path.cpp Modified: libcxx/trunk/benchmarks/CMakeLists.txt URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/benchmarks/CMakeLists.txt?rev=285526&r1=285525&r2=285526&view=diff ============================================================================== --- libcxx/trunk/benchmarks/CMakeLists.txt (original) +++ libcxx/trunk/benchmarks/CMakeLists.txt Sun Oct 30 18:30:38 2016 @@ -115,7 +115,8 @@ macro(add_benchmark_test name source_fil if (LIBCXX_BENCHMARK_NATIVE_STDLIB) set(native_target ${name}_native) add_executable(${native_target} EXCLUDE_FROM_ALL ${source_file}) - add_dependencies(${native_target} google-benchmark-native) + add_dependencies(${native_target} google-benchmark-native + google-benchmark-libcxx) target_link_libraries(${native_target} -lbenchmark) if (LIBCXX_BENCHMARK_NATIVE_STDLIB STREQUAL "libstdc++") target_link_libraries(${native_target} -lstdc++fs) Modified: libcxx/trunk/include/experimental/filesystem URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/experimental/filesystem?rev=285526&r1=285525&r2=285526&view=diff ============================================================================== --- libcxx/trunk/include/experimental/filesystem (original) +++ libcxx/trunk/include/experimental/filesystem Sun Oct 30 18:30:38 2016 @@ -1083,7 +1083,8 @@ public: typedef const path& reference; public: _LIBCPP_INLINE_VISIBILITY - iterator() : __elem_(), __path_ptr_(nullptr), __pos_(0) {} + iterator() : __stashed_elem_(), __path_ptr_(nullptr), + __entry_(), __state_(__singular) {} iterator(const iterator&) = default; ~iterator() = default; @@ -1092,16 +1093,20 @@ public: _LIBCPP_INLINE_VISIBILITY reference operator*() const { - return __elem_; + return __stashed_elem_; } _LIBCPP_INLINE_VISIBILITY pointer operator->() const { - return &__elem_; + return &__stashed_elem_; } _LIBCPP_INLINE_VISIBILITY iterator& operator++() { + _LIBCPP_ASSERT(__state_ != __singular, + "attempting to increment a singular iterator"); + _LIBCPP_ASSERT(__state_ != __at_end, + "attempting to increment the end iterator"); return __increment(); } @@ -1114,6 +1119,10 @@ public: _LIBCPP_INLINE_VISIBILITY iterator& operator--() { + _LIBCPP_ASSERT(__state_ != __singular, + "attempting to decrement a singular iterator"); + _LIBCPP_ASSERT(__entry_.data() != __path_ptr_->native().data(), + "attempting to decrement the begin iterator"); return __decrement(); } @@ -1127,21 +1136,25 @@ public: private: friend class path; + static constexpr unsigned char __singular = 0; + static constexpr unsigned char __at_end = 6; + inline _LIBCPP_INLINE_VISIBILITY friend bool operator==(const iterator&, const iterator&); _LIBCPP_FUNC_VIS iterator& __increment(); _LIBCPP_FUNC_VIS iterator& __decrement(); - path __elem_; + path __stashed_elem_; const path* __path_ptr_; - size_t __pos_; + path::__string_view __entry_; + unsigned char __state_; }; inline _LIBCPP_INLINE_VISIBILITY bool operator==(const path::iterator& __lhs, const path::iterator& __rhs) { return __lhs.__path_ptr_ == __rhs.__path_ptr_ && - __lhs.__pos_ == __rhs.__pos_; + __lhs.__entry_.data() == __rhs.__entry_.data(); } inline _LIBCPP_INLINE_VISIBILITY Modified: libcxx/trunk/src/experimental/filesystem/path.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/path.cpp?rev=285526&r1=285525&r2=285526&view=diff ============================================================================== --- libcxx/trunk/src/experimental/filesystem/path.cpp (original) +++ libcxx/trunk/src/experimental/filesystem/path.cpp Sun Oct 30 18:30:38 2016 @@ -9,240 +9,282 @@ #include "experimental/filesystem" #include "string_view" #include "utility" - -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM - -_LIBCPP_CONSTEXPR path::value_type path::preferred_separator; - - -using string_view_t = path::__string_view; +#include "cassert" namespace { namespace parser { +using namespace std; +using namespace std::experimental::filesystem; -using value_type = path::value_type; +using string_view_t = path::__string_view; using string_view_pair = pair<string_view_t, string_view_t>; +using PosPtr = path::value_type const*; -// 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_view_t const &, size_t); -bool is_root_name(const string_view_t&, size_t); -bool is_root_directory(string_view_t const &, size_t); -bool is_trailing_separator(string_view_t const &, size_t); - -size_t start_of(string_view_t const &, size_t); -size_t end_of(string_view_t const &, size_t); - -size_t root_name_start(const string_view_t& s); -size_t root_name_end(const string_view_t&); - -size_t root_directory_start(string_view_t const &); -size_t root_directory_end(string_view_t const &); - -string_view_pair separate_filename(string_view_t const &); -string_view extract_raw(string_view_t const &, size_t); -string_view extract_preferred(string_view_t const &, size_t); - -inline bool is_separator(const string_view_t& s, size_t pos) { - return (pos < s.size() && s[pos] == preferred_separator); -} - -inline bool is_root_name(const string_view_t& s, size_t pos) { - return good(pos) && pos == 0 ? root_name_start(s) == pos : false; -} - -inline bool is_root_directory(const string_view_t& s, size_t pos) { - return good(pos) ? root_directory_start(s) == pos : false; -} +struct PathParser { + enum ParserState : unsigned char { + // Zero is a special sentinel value used by default constructed iterators. + PS_BeforeBegin = 1, + PS_InRootName, + PS_InRootDir, + PS_InFilenames, + PS_InTrailingSep, + PS_AtEnd + }; -inline bool is_trailing_separator(const string_view_t& 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)); -} + const string_view_t Path; + string_view_t RawEntry; + ParserState State; + +private: + PathParser(string_view_t P, ParserState State) noexcept + : Path(P), State(State) {} + +public: + PathParser(string_view_t P, string_view_t E, unsigned char S) + : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) { + assert(S != 0); + assert(S != PS_BeforeBegin); + } -size_t start_of(const string_view_t& 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; -} + static PathParser CreateBegin(string_view_t P) noexcept { + PathParser PP(P, PS_BeforeBegin); + PP.increment(); + return PP; + } -size_t end_of(const string_view_t& 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; -} + static PathParser CreateEnd(string_view_t P) noexcept { + PathParser PP(P, PS_AtEnd); + return PP; + } -inline size_t root_name_start(const string_view_t& s) { - return good(root_name_end(s)) ? 0 : npos; -} + PosPtr peek() const noexcept { + auto End = &Path.back() + 1; + auto TkEnd = getNextTokenStartPos(); + return TkEnd == End ? nullptr : TkEnd; + } -size_t root_name_end(const string_view_t& s) { - if (s.size() < 2 || s[0] != preferred_separator - || s[1] != preferred_separator) { - return npos; - } - if (s.size() == 2) { - return 1; + void increment() noexcept { + const PosPtr End = &Path.back() + 1; + const PosPtr Start = getNextTokenStartPos(); + if (Start == End) + return makeState(PS_AtEnd); + + switch (State) { + case PS_BeforeBegin: { + PosPtr TkEnd = consumeSeparator(Start, End); + // If we consumed exactly two separators we have a root name. + if (TkEnd && TkEnd == Start + 2) { + // FIXME Do we need to consume a name or is '//' a root name on its own? + // what about '//.', '//..', '//...'? + auto NameEnd = consumeName(TkEnd, End); + if (NameEnd) + TkEnd = NameEnd; + return makeState(PS_InRootName, Start, TkEnd); + } + else if (TkEnd) + return makeState(PS_InRootDir, Start, TkEnd); + else + return makeState(PS_InFilenames, Start, consumeName(Start, End)); + } + + case PS_InRootName: + return makeState(PS_InRootDir, Start, consumeSeparator(Start, End)); + case PS_InRootDir: + return makeState(PS_InFilenames, Start, consumeName(Start, End)); + + case PS_InFilenames: { + PosPtr SepEnd = consumeSeparator(Start, End); + assert(SepEnd); + if (SepEnd != End) { + PosPtr TkEnd = consumeName(SepEnd, End); + if (TkEnd) + return makeState(PS_InFilenames, SepEnd, TkEnd); + } + return makeState(PS_InTrailingSep, Start, SepEnd); } - 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_view_t& 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_view_t& 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_view_t const & s) { - if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; - auto pos = s.find_last_of('.'); - if (pos == string_view_t::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_view_t& 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_view_t& 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 + case PS_InTrailingSep: + return makeState(PS_AtEnd); -//////////////////////////////////////////////////////////////////////////////// -// path_view_iterator -//////////////////////////////////////////////////////////////////////////////// -namespace { + case PS_AtEnd: + _LIBCPP_UNREACHABLE(); + } + } -struct path_view_iterator { - const string_view __s_; - size_t __pos_; + void decrement() noexcept { + const PosPtr REnd = &Path.front() - 1; + const PosPtr RStart = getCurrentTokenStartPos() - 1; + assert(RStart != REnd); + + switch (State) { + case PS_AtEnd: { + // Try to consume a trailing separator or root directory first. + if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) { + if (SepEnd == REnd) + return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir, + Path.data(), RStart + 1); + // Check if we're seeing the root directory separator + auto PP = CreateBegin(Path); + bool InRootDir = PP.State == PS_InRootName && + &PP.RawEntry.back() == SepEnd; + return makeState(InRootDir ? PS_InRootDir : PS_InTrailingSep, + SepEnd + 1, RStart + 1); + } else { + PosPtr TkStart = consumeName(RStart, REnd); + assert(TkStart); + if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd) + return makeState(PS_InRootName, Path.data(), RStart + 1); + else + return makeState(PS_InFilenames, TkStart + 1, RStart + 1); + } + } + case PS_InTrailingSep: + return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1); + case PS_InFilenames: { + PosPtr SepEnd = consumeSeparator(RStart, REnd); + if (SepEnd == REnd) + return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir, + Path.data(), RStart + 1); + PosPtr TkEnd = consumeName(SepEnd, REnd); + if (TkEnd == REnd + 2 && consumeSeparator(TkEnd, REnd) == REnd) + return makeState(PS_InRootDir, SepEnd + 1, RStart + 1); + return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1); + } + case PS_InRootDir: + return makeState(PS_InRootName, Path.data(), RStart + 1); + case PS_InRootName: + case PS_BeforeBegin: + _LIBCPP_UNREACHABLE(); + } + } - 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) {} + /// \brief Return a view with the "preferred representation" of the current + /// element. For example trailing separators are represented as a '.' + string_view_t operator*() const noexcept { + switch (State) { + case PS_BeforeBegin: + case PS_AtEnd: + return ""; + case PS_InRootDir: + return "/"; + case PS_InTrailingSep: + return "."; + case PS_InRootName: + case PS_InFilenames: + return RawEntry; + } + _LIBCPP_UNREACHABLE(); + } - string_view operator*() const { - return parser::extract_preferred(__s_, __pos_); + explicit operator bool() const noexcept { + return State != PS_BeforeBegin && State != PS_AtEnd; } - path_view_iterator& operator++() { + PathParser& operator++() noexcept { increment(); return *this; } - path_view_iterator& operator--() { + PathParser& operator--() noexcept { decrement(); return *this; } - void increment() { - if (__pos_ == parser::npos) return; - while (! set_position(parser::end_of(__s_, __pos_)+1)) - ; - return; +private: + void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept { + assert(NewState != PS_BeforeBegin && NewState != PS_AtEnd); + State = NewState; + assert(Start < End); + assert(Start >= &Path.front() && End <= &Path.back() + 1); + RawEntry = string_view_t(Start, End - Start); + } + void makeState(ParserState NewState) noexcept { + assert(NewState == PS_BeforeBegin || NewState == PS_AtEnd); + State = NewState; + RawEntry = {}; } - 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))) - ; + /// \brief Return a pointer to the first character after the currently + /// lexed element. + PosPtr getNextTokenStartPos() const noexcept { + switch (State) { + case PS_BeforeBegin: + return &Path.front(); + case PS_InRootName: + case PS_InRootDir: + case PS_InFilenames: + return &RawEntry.back() + 1; + case PS_InTrailingSep: + case PS_AtEnd: + return &Path.back() + 1; } + _LIBCPP_UNREACHABLE(); } - bool set_position(size_t pos) { - if (pos >= __s_.size()) { - __pos_ = parser::npos; - } else { - __pos_ = pos; + /// \brief Return a pointer to the first character in the currently lexed + /// element. + PosPtr getCurrentTokenStartPos() const noexcept { + switch (State) { + case PS_BeforeBegin: + case PS_InRootName: + return &Path.front(); + case PS_InRootDir: + case PS_InFilenames: + case PS_InTrailingSep: + return &RawEntry.front(); + case PS_AtEnd: + return &Path.back() + 1; } - return valid_iterator_position(); + _LIBCPP_UNREACHABLE(); } - 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_)); + PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept { + if (P == End || *P != '/') + return nullptr; + const int Inc = P < End ? 1 : -1; + P += Inc; + while (P != End && *P == '/') + P += Inc; + return P; } - bool is_end() const { return __pos_ == parser::npos; } - - inline bool operator==(path_view_iterator const& __p) { - return __pos_ == __p.__pos_; + PosPtr consumeName(PosPtr P, PosPtr End) const noexcept { + if (P == End || *P == '/') + return nullptr; + const int Inc = P < End ? 1 : -1; + P += Inc; + while (P != End && *P != '/') + P += Inc; + return P; } }; -path_view_iterator pbegin(path const& p) { - return path_view_iterator(p.native()); +string_view_pair separate_filename(string_view_t const & s) { + if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; + auto pos = s.find_last_of('.'); + if (pos == string_view_t::npos) return string_view_pair{s, string_view{}}; + return string_view_pair{s.substr(0, pos), s.substr(pos)}; } -path_view_iterator pend(path const& p) { - path_view_iterator __p(p.native()); - __p.__pos_ = parser::npos; - return __p; +string_view_t createView(PosPtr S, PosPtr E) noexcept { + return {S, static_cast<size_t>(E - S) + 1}; } -} // end namespace +}} // namespace parser + +_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM + +using parser::string_view_t; +using parser::string_view_pair; +using parser::PathParser; +using parser::createView; + /////////////////////////////////////////////////////////////////////////////// // path definitions /////////////////////////////////////////////////////////////////////////////// +constexpr path::value_type path::preferred_separator; + path & path::replace_extension(path const & replacement) { path p = extension(); @@ -263,58 +305,65 @@ path & path::replace_extension(path cons string_view_t path::__root_name() const { - return parser::is_root_name(__pn_, 0) - ? parser::extract_preferred(__pn_, 0) - : string_view_t{}; + auto PP = PathParser::CreateBegin(__pn_); + if (PP.State == PathParser::PS_InRootName) + return *PP; + return {}; } string_view_t 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); + auto PP = PathParser::CreateBegin(__pn_); + if (PP.State == PathParser::PS_InRootName) + ++PP; + if (PP.State == PathParser::PS_InRootDir) + return *PP; + return {}; } string_view_t path::__root_path_raw() const { - size_t e = parser::root_directory_end(__pn_); - if (!parser::good(e)) - e = parser::root_name_end(__pn_); - if (parser::good(e)) - return string_view_t(__pn_).substr(0, e + 1); + auto PP = PathParser::CreateBegin(__pn_); + if (PP.State == PathParser::PS_InRootName) { + auto NextCh = PP.peek(); + if (NextCh && *NextCh == '/') { + ++PP; + assert(PP.State == PathParser::PS_InRootDir); + return createView(__pn_.data(), &PP.RawEntry.back()); + } + return PP.RawEntry; + } + if (PP.State == PathParser::PS_InRootDir) + return *PP; return {}; } string_view_t 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_t(__pn_).substr(end_i+1); + auto PP = PathParser::CreateBegin(__pn_); + while (PP.State <= PathParser::PS_InRootDir) + ++PP; + if (PP.State == PathParser::PS_AtEnd) + return {}; + return createView(PP.RawEntry.data(), &__pn_.back()); } string_view_t 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_t(__pn_).substr(0, end_i+1); + if (empty()) + return {}; + auto PP = PathParser::CreateEnd(__pn_); + --PP; + if (PP.RawEntry.data() == __pn_.data()) + return {}; + --PP; + return createView(__pn_.data(), &PP.RawEntry.back()); } string_view_t path::__filename() const { - return empty() ? string_view_t{} : *--pend(*this); + if (empty()) return {}; + return *(--PathParser::CreateEnd(__pn_)); } string_view_t path::__stem() const @@ -330,24 +379,24 @@ string_view_t path::__extension() const //////////////////////////////////////////////////////////////////////////// // path.comparisons int path::__compare(string_view_t __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); + auto PP = PathParser::CreateBegin(__pn_); + auto PP2 = PathParser::CreateBegin(__s); + while (PP && PP2) { + int res = (*PP).compare(*PP2); if (res != 0) return res; - ++thisIter; ++sIter; + ++PP; ++PP2; } - if (thisIter.is_end() && sIter.is_end()) + if (PP.State == PP2.State && PP.State == PathParser::PS_AtEnd) return 0; - if (thisIter.is_end()) + if (PP.State == PathParser::PS_AtEnd) return -1; return 1; } //////////////////////////////////////////////////////////////////////////// // path.nonmembers -size_t hash_value(const path& __p) _NOEXCEPT { - path_view_iterator thisIter(__p.native()); +size_t hash_value(const path& __p) noexcept { + auto PP = PathParser::CreateBegin(__p.native()); struct HashPairT { size_t first; size_t second; @@ -355,10 +404,10 @@ size_t hash_value(const path& __p) _NOEX HashPairT hp = {0, 0}; std::hash<string_view> hasher; std::__scalar_hash<decltype(hp)> pair_hasher; - while (!thisIter.is_end()) { - hp.second = hasher(*thisIter); + while (PP) { + hp.second = hasher(*PP); hp.first = pair_hasher(hp); - ++thisIter; + ++PP; } return hp.first; } @@ -367,35 +416,39 @@ size_t hash_value(const path& __p) _NOEX // path.itr path::iterator path::begin() const { - path_view_iterator pit = pbegin(*this); + auto PP = PathParser::CreateBegin(__pn_); iterator it; it.__path_ptr_ = this; - it.__pos_ = pit.__pos_; - it.__elem_.__assign_view(*pit); + it.__state_ = PP.State; + it.__entry_ = PP.RawEntry; + it.__stashed_elem_.__assign_view(*PP); return it; } path::iterator path::end() const { iterator it{}; + it.__state_ = PathParser::PS_AtEnd; 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); + static_assert(__at_end == PathParser::PS_AtEnd, ""); + PathParser PP(__path_ptr_->native(), __entry_, __state_); + ++PP; + __state_ = PP.State; + __entry_ = PP.RawEntry; + __stashed_elem_.__assign_view(*PP); return *this; } path::iterator& path::iterator::__decrement() { - path_view_iterator it(__path_ptr_->native(), __pos_); - it.decrement(); - __pos_ = it.__pos_; - __elem_.__assign_view(*it); + PathParser PP(__path_ptr_->native(), __entry_, __state_); + --PP; + __state_ = PP.State; + __entry_ = PP.RawEntry; + __stashed_elem_.__assign_view(*PP); return *this; } Added: libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.itr/iterator_db.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.itr/iterator_db.pass.cpp?rev=285526&view=auto ============================================================================== --- libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.itr/iterator_db.pass.cpp (added) +++ libcxx/trunk/test/libcxx/experimental/filesystem/class.path/path.itr/iterator_db.pass.cpp Sun Oct 30 18:30:38 2016 @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// 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. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03 +// UNSUPPORTED: libcpp-no-exceptions + +// <experimental/filesystem> + +// class path + +#define _LIBCPP_DEBUG 0 +#define _LIBCPP_ASSERT(cond, msg) ((cond) ? ((void)0) : throw 42) + +#include <experimental/filesystem> +#include <iterator> +#include <type_traits> +#include <cassert> + +#include "test_macros.h" +#include "filesystem_test_helper.hpp" + +namespace fs = std::experimental::filesystem; + +int main() { + using namespace fs; + // Test incrementing/decrementing a singular iterator + { + path::iterator singular; + try { + ++singular; + assert(false); + } catch (int) {} + try { + --singular; + assert(false); + } catch (int) {} + } + // Test decrementing the begin iterator + { + path p("foo/bar"); + auto it = p.begin(); + try { + --it; + assert(false); + } catch (int) {} + ++it; + ++it; + try { + ++it; + assert(false); + } catch (int) {} + } + // Test incrementing the end iterator + { + path p("foo/bar"); + auto it = p.end(); + try { + ++it; + assert(false); + } catch (int) {} + --it; + --it; + try { + --it; + assert(false); + } catch (int) {} + } +} \ No newline at end of file _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits