Although filesystem::path::iterator is only a bidirectional iterator,
the underlying sequence has random access iterators (specifically, raw
pointers). This means std::distance and std::advance can be implemented
more efficiently than the generic versions which apply ++ and --
repeatedly.

        PR libstdc++/71044 (partial)
        * include/bits/fs_path.h (__path_iter_distance, __path_iter_advance):
        New friend functions to implement std::distance and std::advance more
        efficiently.
        (distance, advance): Add overloads for path::iterator.
        * testsuite/27_io/filesystem/path/itr/components.cc: Test new
        overload.

Tested x86_64-linux, committed to trunk.

commit 994dfa1a5ca5ddd4e8d280873ef376ee6ab8352b
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Fri Nov 30 16:33:24 2018 +0000

    Overload std::distance and std::advance for path::iterator
    
    Although filesystem::path::iterator is only a bidirectional iterator,
    the underlying sequence has random access iterators (specifically, raw
    pointers). This means std::distance and std::advance can be implemented
    more efficiently than the generic versions which apply ++ and --
    repeatedly.
    
            PR libstdc++/71044 (partial)
            * include/bits/fs_path.h (__path_iter_distance, 
__path_iter_advance):
            New friend functions to implement std::distance and std::advance 
more
            efficiently.
            (distance, advance): Add overloads for path::iterator.
            * testsuite/27_io/filesystem/path/itr/components.cc: Test new
            overload.

diff --git a/libstdc++-v3/include/bits/fs_path.h 
b/libstdc++-v3/include/bits/fs_path.h
index cbaea7343a3..075b3ab5ef8 100644
--- a/libstdc++-v3/include/bits/fs_path.h
+++ b/libstdc++-v3/include/bits/fs_path.h
@@ -733,6 +733,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
   private:
     friend class path;
 
+    bool _M_is_multi() const { return _M_path->_M_type == _Type::_Multi; }
+
+    friend difference_type
+    __path_iter_distance(const iterator& __first, const iterator& __last)
+    {
+      __glibcxx_assert(__first._M_path != nullptr);
+      __glibcxx_assert(__first._M_path == __last._M_path);
+      if (__first._M_is_multi())
+       return std::distance(__first._M_cur, __last._M_cur);
+      else if (__first._M_at_end == __last._M_at_end)
+       return 0;
+      else
+       return __first._M_at_end ? -1 : 1;
+    }
+
+    friend void
+    __path_iter_advance(iterator& __i, difference_type __n)
+    {
+      if (__n == 1)
+       ++__i;
+      else if (__n == -1)
+       --__i;
+      else if (__n != 0)
+       {
+         __glibcxx_assert(__i._M_path != nullptr);
+         __glibcxx_assert(__i._M_is_multi());
+         // __glibcxx_assert(__i._M_path->_M_cmpts.end() - __i._M_cur >= __n);
+         __i._M_cur += __n;
+       }
+    }
+
     iterator(const path* __path, path::_List::const_iterator __iter)
     : _M_path(__path), _M_cur(__iter), _M_at_end()
     { }
@@ -1160,6 +1191,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
 _GLIBCXX_END_NAMESPACE_CXX11
 } // namespace filesystem
 
+inline ptrdiff_t
+distance(filesystem::path::iterator __first, filesystem::path::iterator __last)
+{ return __path_iter_distance(__first, __last); }
+
+template<typename _InputIterator, typename _Distance>
+  void
+  advance(filesystem::path::iterator& __i, _Distance __n)
+  { __path_iter_advance(__i, static_cast<ptrdiff_t>(__n)); }
+
 extern template class __shared_ptr<const filesystem::filesystem_error::_Impl>;
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc 
b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc
index 4852c03c78e..55760e82a9a 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc
@@ -136,10 +136,28 @@ test03()
     }
 }
 
+void
+test04()
+{
+  std::filesystem::path p = "/a/b/c/d/e/f/g";
+  VERIFY( std::distance(p.begin(), p.end()) == 8);
+  auto it = p.begin();
+  std::advance(it, 1);
+  VERIFY( std::distance(p.begin(), it) == 1 );
+  VERIFY( it->native() == "a" );
+  std::advance(it, 3);
+  VERIFY( std::distance(p.begin(), it) == 4 );
+  VERIFY( it->native() == "d" );
+  std::advance(it, -2);
+  VERIFY( std::distance(p.begin(), it) == 2 );
+  VERIFY( it->native() == "b" );
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
+  test04();
 }

Reply via email to