PR libstdc++/71044 fix off-by-one errors introduced recently
authorJonathan Wakely <jwakely@redhat.com>
Mon, 17 Dec 2018 22:43:31 +0000 (22:43 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Mon, 17 Dec 2018 22:43:31 +0000 (22:43 +0000)
The recent changes to append/concat directly from strings (without
constructing paths) introduced regressions where one of the components
could be omitted from the iteration sequence in the result.

PR libstdc++/71044
* src/filesystem/std-path.cc (path::_M_append): Fix off-by-one error
that caused a component to be lost from the iteration sequence.
(path::_M_concat): Likewise.
* testsuite/27_io/filesystem/path/append/source.cc: Test appending
long strings.
* testsuite/27_io/filesystem/path/concat/strings.cc: Test
concatenating long strings.
* testsuite/27_io/filesystem/path/construct/string_view.cc: Test
construction from long string.

From-SVN: r267222

libstdc++-v3/ChangeLog
libstdc++-v3/src/filesystem/std-path.cc
libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc
libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc
libstdc++-v3/testsuite/27_io/filesystem/path/construct/string_view.cc

index a4f6507c2bbd294fdbe62eb56549dde815db753d..d9dc973a440628e38441b5cf84a993165a8efe1f 100644 (file)
@@ -1,3 +1,16 @@
+2018-12-17  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/71044
+       * src/filesystem/std-path.cc (path::_M_append): Fix off-by-one error
+       that caused a component to be lost from the iteration sequence.
+       (path::_M_concat): Likewise.
+       * testsuite/27_io/filesystem/path/append/source.cc: Test appending
+       long strings.
+       * testsuite/27_io/filesystem/path/concat/strings.cc: Test
+       concatenating long strings.
+       * testsuite/27_io/filesystem/path/construct/string_view.cc: Test
+       construction from long string.
+
 2018-12-13  Jonathan Wakely  <jwakely@redhat.com>
 
        * src/filesystem/std-path.cc (SLASHSLASH_IS_ROOT_NAME): New macro to
index d2520492c0328a3c1d272c4fef992528c691aaaa..b5ddbdad149a22297ffe6446bb42989a2c0d7758 100644 (file)
@@ -781,10 +781,11 @@ path::_M_append(basic_string_view<value_type> s)
              ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
              ++_M_cmpts._M_impl->_M_size;
            }
-         for (auto c = parser.next(); c.valid(); c = parser.next())
+         while (cmpt.valid())
            {
-             ::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
+             ::new(output++) _Cmpt(cmpt.str, cmpt.type, parser.offset(cmpt));
              ++_M_cmpts._M_impl->_M_size;
+             cmpt = parser.next();
            }
 
          if (s.back() == '/')
@@ -1139,7 +1140,7 @@ path::_M_concat(basic_string_view<value_type> s)
            }
 #endif
        }
-      else if (orig_filenamelen == 0)
+      else if (orig_filenamelen == 0 && extra.empty())
        {
          // Replace empty filename at end of original path.
          std::prev(output)->_M_pathname = it->str;
index e440ca921c7955e598afc41daaf21c6bea1a20f3..21ae6be3d977a3823609031a9c88f77cfd3f9f8a 100644 (file)
@@ -137,6 +137,22 @@ test05()
   s = second->native();
   p3 /= s;
   VERIFY( p3.string() == "0/123456789/a/123456789" );
+  }
+
+void
+test06()
+{
+  const std::string s0 = "a/b/c";
+  path p = s0;
+  std::string s;
+  for (int i = 0; i < 10; ++i)
+    s += "0/1/2/3/4/5/6/7/8/9/";
+  // append a long string with many components
+  test(p, s.c_str());
+
+  // Same again but with a trailing slash on the left operand:
+  path p2 = s0 + '/';
+  test(p2, s.c_str());
 }
 
 int
@@ -147,4 +163,5 @@ main()
   test03();
   test04();
   test05();
+  test06();
 }
index eea9b6dc69b925353b164654845a2a1b026498be..316f7c3d7bd8df2348729c6ffa79e427f330360e 100644 (file)
 #include <filesystem>
 #include <testsuite_hooks.h>
 #include <testsuite_iterators.h>
+#include <testsuite_fs.h>
 
 using std::filesystem::path;
+using __gnu_test::compare_paths;
 
 void
 test01()
@@ -60,7 +62,7 @@ test01()
 void
 test02()
 {
-  std::basic_string_view<path::value_type> s;
+  std::basic_string_view<path::value_type> s, expected;
 
   path p = "0/1/2/3/4/5/6";
   // The string_view aliases the path's internal string:
@@ -68,25 +70,54 @@ test02()
   // Append that string_view, which must work correctly even though the
   // internal string will be reallocated during the operation:
   p += s;
-  VERIFY( p.string() == "0/1/2/3/4/5/60/1/2/3/4/5/6" );
+  compare_paths(p, "0/1/2/3/4/5/60/1/2/3/4/5/6");
 
   // Same again with a trailing slash:
   path p2 = "0/1/2/3/4/5/";
   s = p2.native();
   p2 += s;
-  VERIFY( p2.string() == "0/1/2/3/4/5/0/1/2/3/4/5/" );
+  compare_paths(p2, "0/1/2/3/4/5/0/1/2/3/4/5/");
 
   // And aliasing one of the components of the path:
   path p3 = "0/123456789";
   path::iterator second = std::next(p3.begin());
   s = second->native();
   p3 += s;
-  VERIFY( p3.string() == "0/123456789123456789" );
+  compare_paths(p3, "0/123456789123456789" );
 }
 
+void
+test03()
+{
+  const std::string s0 = "a/b/c";
+  path p = s0;
+  std::string s;
+  for (int i = 0; i < 10; ++i)
+    s += "0/1/2/3/4/5/6/7/8/9/";
+  // concat a long string with many components:
+  p += s;
+  compare_paths(p, path(s0+s));
+
+  // Same again but with a trailing slash on the left operand:
+  path p2 = s0 + '/';
+  p2 += s;
+  compare_paths(p2, path(s0+'/'+s));
+
+  // And again but with a leading slash on the right operand:
+  path p3 = s0;
+  s.insert(0, 1, '/');
+  p3 += s;
+  compare_paths(p2, path(s0+s));
+
+  // And again but with a slash on both operands:
+  path p4 = s0 + '/';
+  p4 += s;
+  compare_paths(p4, path(s0+'/'+s));
+}
 int
 main()
 {
   test01();
   test02();
+  test03();
 }
index 0339df73b305aae7e739995946810a318a1824ff..a6130d389bf461753ffd7e98ff202cffada7b82c 100644 (file)
@@ -25,6 +25,7 @@
 #include <string_view>
 #include <string>
 #include <testsuite_fs.h>
+#include <testsuite_hooks.h>
 
 using std::filesystem::path;
 using __gnu_test::compare_paths;
@@ -49,8 +50,31 @@ test01()
   }
 }
 
+void
+test02()
+{
+  std::string s;
+  for (int i = 0; i < 10; ++i)
+    s += "0/1/2/3/4/5/6/7/8/9/";
+  // Construct a path with a large number of components:
+  path p = s;
+  auto iter = p.begin();
+  for (int i = 0; i < 100; ++i)
+  {
+    char c = '0' + i % 10;
+    std::string_view sv(&c, 1);
+    VERIFY( iter != p.end() );
+    compare_paths( *iter, sv );
+    ++iter;
+  }
+  VERIFY( iter != p.end() );
+  VERIFY( iter->native().empty() );
+  VERIFY( ++iter == p.end() );
+}
+
 int
 main()
 {
   test01();
+  test02();
 }