diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-08 19:39:03 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-08 19:39:03 +0000 | 
| commit | aed8d94e97d1be8b4e26ce1ffb4d0547aee8ab1d (patch) | |
| tree | f4b32ab9e34cda2eead99ae084f0b02d0dab6891 /contrib/libc++/src/experimental/filesystem/path.cpp | |
| parent | 657db61c2068b2bcc0d930546402284e2c9c9abd (diff) | |
| parent | 4a2db4d30e1653093d4d8b06e8221e2f8b723507 (diff) | |
Notes
Diffstat (limited to 'contrib/libc++/src/experimental/filesystem/path.cpp')
| -rw-r--r-- | contrib/libc++/src/experimental/filesystem/path.cpp | 550 | 
1 files changed, 301 insertions, 249 deletions
| diff --git a/contrib/libc++/src/experimental/filesystem/path.cpp b/contrib/libc++/src/experimental/filesystem/path.cpp index 38c449832f6e..96b81f7b0a70 100644 --- a/contrib/libc++/src/experimental/filesystem/path.cpp +++ b/contrib/libc++/src/experimental/filesystem/path.cpp @@ -6,242 +6,279 @@  // Source Licenses. See LICENSE.TXT for details.  //  //===----------------------------------------------------------------------===// +#undef NDEBUG  #include "experimental/filesystem" -#include "experimental/string_view" +#include "string_view"  #include "utility" - -_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM - -_LIBCPP_CONSTEXPR path::value_type path::preferred_separator; +#include "cassert"  namespace { namespace parser  { +using namespace std; +using namespace std::experimental::filesystem; + +using string_view_t = path::__string_view; +using string_view_pair = pair<string_view_t, string_view_t>; +using PosPtr = path::value_type const*; + +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 +  }; -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 &); +  const string_view_t Path; +  string_view_t RawEntry; +  ParserState State; -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); +private: +  PathParser(string_view_t P, ParserState State) noexcept +      : Path(P), State(State) {} -inline bool is_separator(const string_type& s, size_t pos) { -    return (pos < s.size() && s[pos] == preferred_separator); -} +public: +  PathParser(string_view_t P, string_view_t E, unsigned char S) +      : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) { +    // S cannot be '0' or PS_BeforeBegin. +  } -inline bool is_root_name(const string_type& s, size_t pos) { -  return good(pos) && pos == 0 ? root_name_start(s) == pos : false; -} +  static PathParser CreateBegin(string_view_t P) noexcept { +    PathParser PP(P, PS_BeforeBegin); +    PP.increment(); +    return PP; +  } -inline bool is_root_directory(const string_type& s, size_t pos) { -    return good(pos) ? root_directory_start(s) == pos : false; -} +  static PathParser CreateEnd(string_view_t P) noexcept { +    PathParser PP(P, PS_AtEnd); +    return PP; +  } -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)); -} +  PosPtr peek() const noexcept { +    auto End = &Path.back() + 1; +    auto TkEnd = getNextTokenStartPos(); +    return TkEnd == End ? nullptr : TkEnd; +  } -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; -} +  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)); +    } -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; -} +    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); +      if (SepEnd != End) { +        PosPtr TkEnd = consumeName(SepEnd, End); +        if (TkEnd) +          return makeState(PS_InFilenames, SepEnd, TkEnd); +      } +      return makeState(PS_InTrailingSep, Start, SepEnd); +    } -inline size_t root_name_start(const string_type& s) { -    return good(root_name_end(s)) ? 0 : npos; -} +    case PS_InTrailingSep: +      return makeState(PS_AtEnd); -size_t root_name_end(const string_type& s) { -    if (s.size() < 2 || s[0] != preferred_separator -        || s[1] != preferred_separator) { -        return npos; +    case PS_AtEnd: +      _LIBCPP_UNREACHABLE();      } -    if (s.size() == 2) { -        return 1; +  } + +  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); +        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); +      }      } -    size_t index = 2; // current position -    if (s[index] == preferred_separator) { -        return npos; +    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);      } -    while (index + 1 < s.size() && s[index+1] != preferred_separator) { -        ++index; +    case PS_InRootDir: +      return makeState(PS_InRootName, Path.data(), RStart + 1); +    case PS_InRootName: +    case PS_BeforeBegin: +      _LIBCPP_UNREACHABLE();      } -    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) {} +  /// \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 { +    State = NewState; +    RawEntry = string_view_t(Start, End - Start); +  } +  void makeState(ParserState NewState) noexcept { +    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(); @@ -260,131 +297,146 @@ path & path::replace_extension(path const & replacement)  ///////////////////////////////////////////////////////////////////////////////  // path.decompose -string_view path::__root_name() const +string_view_t path::__root_name() const  { -    return parser::is_root_name(__pn_, 0) -      ? parser::extract_preferred(__pn_, 0) -      : string_view{}; +    auto PP = PathParser::CreateBegin(__pn_); +    if (PP.State == PathParser::PS_InRootName) +      return *PP; +    return {};  } -string_view path::__root_directory() const +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 path::__relative_path() const +string_view_t path::__root_path_raw() 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_); +    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 (not parser::good(end_i)) { -        return {__pn_}; -    } -    return string_view(__pn_).substr(end_i+1); +    if (PP.State == PathParser::PS_InRootDir) +      return *PP; +    return {};  } -string_view path::__parent_path() const +string_view_t path::__relative_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); +    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 path::__filename() const +string_view_t path::__parent_path() const  { -    return empty() ? string_view{} : *--pend(*this); +    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 path::__stem() const +string_view_t path::__filename() const +{ +    if (empty()) return {}; +    return *(--PathParser::CreateEnd(__pn_)); +} + +string_view_t path::__stem() const  {      return parser::separate_filename(__filename()).first;  } -string_view path::__extension() const +string_view_t 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); +int path::__compare(string_view_t __s) const { +    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()); -  struct HashPairT { -    size_t first; -    size_t second; -  }; -  HashPairT hp = {0, 0}; +size_t hash_value(const path& __p) noexcept { +  auto PP = PathParser::CreateBegin(__p.native()); +  size_t hash_value = 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; +  while (PP) { +    hash_value = __hash_combine(hash_value, hasher(*PP)); +    ++PP;    } -  return hp.first; +  return hash_value;  }  ////////////////////////////////////////////////////////////////////////////  // 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;  } | 
