PR libstdc++/90634 * src/filesystem/path.cc (path::_M_split_cmpts()): Fix check for "/". * testsuite/27_io/filesystem/path/construct/90634.cc: New test. * testsuite/experimental/filesystem/path/construct/90634.cc: New test.
PR libstdc++/90634 * include/bits/fs_path.h (path::path(path&&)): Only call _M_split_cmpts() for a path with multiple components. (path::_S_is_dir_sep()): Add missing 'static' keyword to function. * include/experimental/bits/fs_path.h: Likewise. * src/filesystem/path.cc (path::_M_split_cmpts()): Count number of components and reserve space in vector. Return early when there is only one component. * src/filesystem/std-path.cc (path::_M_split_cmpts()): Likewise. Tested x86_64-linux, committed to gcc-8-branch.
commit 1a21d448211978f10434ddad60bc72f70d1bd9ce Author: redi <redi@138bc75d-0d04-0410-961f-82ee72b054a4> Date: Tue May 28 16:08:51 2019 +0000 Fix check for root-directory path and add tests PR libstdc++/90634 * src/filesystem/path.cc (path::_M_split_cmpts()): Fix check for "/". * testsuite/27_io/filesystem/path/construct/90634.cc: New test. * testsuite/experimental/filesystem/path/construct/90634.cc: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-8-branch@271712 138bc75d-0d04-0410-961f-82ee72b054a4 diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 872e292ecaa..8c876da2875 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,10 @@ 2019-05-28 Jonathan Wakely <jwak...@redhat.com> + PR libstdc++/90634 + * src/filesystem/path.cc (path::_M_split_cmpts()): Fix check for "/". + * testsuite/27_io/filesystem/path/construct/90634.cc: New test. + * testsuite/experimental/filesystem/path/construct/90634.cc: New test. + PR libstdc++/90634 * include/bits/fs_path.h (path::path(path&&)): Only call _M_split_cmpts() for a path with multiple components. diff --git a/libstdc++-v3/src/filesystem/path.cc b/libstdc++-v3/src/filesystem/path.cc index ae29b29aab9..e4d339a1208 100644 --- a/libstdc++-v3/src/filesystem/path.cc +++ b/libstdc++-v3/src/filesystem/path.cc @@ -393,7 +393,7 @@ path::_M_split_cmpts() _M_add_root_dir(0); } } - else if (pos == len) // got root directory only + else if (len == 1) // got root directory only { _M_type = _Type::_Root_dir; return; diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/90634.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/90634.cc new file mode 100644 index 00000000000..dc31a86089e --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/90634.cc @@ -0,0 +1,70 @@ +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++17 -lstdc++fs" } +// { dg-do run { target c++17 } } +// { dg-require-filesystem-ts "" } + +#include <filesystem> +#include <cstdlib> +#include <testsuite_hooks.h> + +std::size_t bytes_allocated = 0; + +void* operator new(std::size_t n) +{ + bytes_allocated += n; + return std::malloc(n); +} + +void operator delete(void* p) noexcept { std::free(p); } +#if __cpp_sized_deallocation +void operator delete(void* p, std::size_t) noexcept { std::free(p); } +#endif + +void +test01() +{ + std::string s0; + std::string s1 = "/"; + std::string s2 = "///"; + std::string s3 = "filename"; + std::string s4 = "C:"; + std::string s5 = "\\"; + + using std::filesystem::path; + + bytes_allocated = 0; + path p0 = std::move(s0); + VERIFY( bytes_allocated == 0 ); + path p1 = std::move(s1); + VERIFY( bytes_allocated == 0 ); + path p2 = std::move(s2); + VERIFY( bytes_allocated == 0 ); + path p3 = std::move(s3); + VERIFY( bytes_allocated == 0 ); + path p4 = std::move(s4); + VERIFY( bytes_allocated == 0 ); + path p5 = std::move(s5); + VERIFY( bytes_allocated == 0 ); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/90634.cc b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/90634.cc new file mode 100644 index 00000000000..ef4a804de42 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/90634.cc @@ -0,0 +1,67 @@ +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-DUSE_FILESYSTEM_TS -lstdc++fs" } +// { dg-do run { target c++11 } } +// { dg-require-filesystem-ts "" } + +#include <experimental/filesystem> +#include <cstdlib> +#include <testsuite_hooks.h> + +std::size_t bytes_allocated = 0; + +void* operator new(std::size_t n) +{ + bytes_allocated += n; + return std::malloc(n); +} + +void operator delete(void* p) noexcept { std::free(p); } +#if __cpp_sized_deallocation +void operator delete(void* p, std::size_t) noexcept { std::free(p); } +#endif + +void +test01() +{ + std::string s0; + std::string s1 = "/"; + std::string s2 = "filename"; + std::string s3 = "C:"; + std::string s4 = "\\"; + + using std::experimental::filesystem::path; + + bytes_allocated = 0; + path p0 = std::move(s0); + VERIFY( bytes_allocated == 0 ); + path p1 = std::move(s1); + VERIFY( bytes_allocated == 0 ); + path p2 = std::move(s2); + VERIFY( bytes_allocated == 0 ); + path p3 = std::move(s3); + VERIFY( bytes_allocated == 0 ); + path p4 = std::move(s4); + VERIFY( bytes_allocated == 0 ); +} + +int +main() +{ + test01(); +} commit ccc5703fb33af14a0b39711c3d183ec7a49b372e Author: redi <redi@138bc75d-0d04-0410-961f-82ee72b054a4> Date: Tue May 28 14:57:35 2019 +0000 PR libstdc++/90634 reduce allocations in filesystem::path construction PR libstdc++/90634 * include/bits/fs_path.h (path::path(path&&)): Only call _M_split_cmpts() for a path with multiple components. (path::_S_is_dir_sep()): Add missing 'static' keyword to function. * include/experimental/bits/fs_path.h: Likewise. * src/filesystem/path.cc (path::_M_split_cmpts()): Count number of components and reserve space in vector. Return early when there is only one component. * src/filesystem/std-path.cc (path::_M_split_cmpts()): Likewise. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-8-branch@271710 138bc75d-0d04-0410-961f-82ee72b054a4 diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 5f359ede685..872e292ecaa 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,15 @@ +2019-05-28 Jonathan Wakely <jwak...@redhat.com> + + PR libstdc++/90634 + * include/bits/fs_path.h (path::path(path&&)): Only call + _M_split_cmpts() for a path with multiple components. + (path::_S_is_dir_sep()): Add missing 'static' keyword to function. + * include/experimental/bits/fs_path.h: Likewise. + * src/filesystem/path.cc (path::_M_split_cmpts()): Count number of + components and reserve space in vector. Return early when there is + only one component. + * src/filesystem/std-path.cc (path::_M_split_cmpts()): Likewise. + 2019-05-23 Jonathan Wakely <jwak...@redhat.com> Backport from mainline diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h index 256b1d0a97f..7ae68f623d1 100644 --- a/libstdc++-v3/include/bits/fs_path.h +++ b/libstdc++-v3/include/bits/fs_path.h @@ -168,7 +168,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 path(path&& __p) noexcept : _M_pathname(std::move(__p._M_pathname)), _M_type(__p._M_type) { - _M_split_cmpts(); + if (_M_type == _Type::_Multi) + _M_split_cmpts(); __p.clear(); } @@ -478,7 +479,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 static basic_string<_CharT, _Traits, _Allocator> _S_str_convert(const string_type&, const _Allocator& __a); - bool _S_is_dir_sep(value_type __ch) + static bool _S_is_dir_sep(value_type __ch) { #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS return __ch == L'/' || __ch == preferred_separator; diff --git a/libstdc++-v3/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h index e419569b8ca..705f76331b5 100644 --- a/libstdc++-v3/include/experimental/bits/fs_path.h +++ b/libstdc++-v3/include/experimental/bits/fs_path.h @@ -186,7 +186,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 path(path&& __p) noexcept : _M_pathname(std::move(__p._M_pathname)), _M_type(__p._M_type) { - _M_split_cmpts(); + if (_M_type == _Type::_Multi) + _M_split_cmpts(); __p.clear(); } @@ -460,7 +461,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc); } - bool _S_is_dir_sep(value_type __ch) + static bool _S_is_dir_sep(value_type __ch) { #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS return __ch == L'/' || __ch == preferred_separator; diff --git a/libstdc++-v3/src/filesystem/path.cc b/libstdc++-v3/src/filesystem/path.cc index 899d94e0067..ae29b29aab9 100644 --- a/libstdc++-v3/src/filesystem/path.cc +++ b/libstdc++-v3/src/filesystem/path.cc @@ -334,6 +334,28 @@ path::_M_split_cmpts() if (_M_pathname.empty()) return; + { + // Approximate count of components, to reserve space in _M_cmpts vector: + int count = 1; + bool saw_sep_last = _S_is_dir_sep(_M_pathname[0]); + bool saw_non_sep = !saw_sep_last; + for (value_type c : _M_pathname) + { + if (_S_is_dir_sep(c)) + saw_sep_last = true; + else if (saw_sep_last) + { + ++count; + saw_sep_last = false; + saw_non_sep = true; + } + } + if (saw_non_sep && saw_sep_last) + ++count; // empty filename after trailing slash + if (count > 1) + _M_cmpts.reserve(count); + } + size_t pos = 0; const size_t len = _M_pathname.size(); @@ -356,9 +378,13 @@ path::_M_split_cmpts() pos = 3; while (pos < len && !_S_is_dir_sep(_M_pathname[pos])) ++pos; + if (pos == len) + { + _M_type = _Type::_Root_name; + return; + } _M_add_root_name(pos); - if (pos < len) // also got root directory - _M_add_root_dir(pos); + _M_add_root_dir(pos); } else { @@ -367,6 +393,11 @@ path::_M_split_cmpts() _M_add_root_dir(0); } } + else if (pos == len) // got root directory only + { + _M_type = _Type::_Root_dir; + return; + } else // got root directory _M_add_root_dir(0); ++pos; @@ -375,12 +406,28 @@ path::_M_split_cmpts() else if (len > 1 && _M_pathname[1] == L':') { // got disk designator + if (len == 2) + { + _M_type = _Type::_Root_name; + return; + } _M_add_root_name(2); if (len > 2 && _S_is_dir_sep(_M_pathname[2])) _M_add_root_dir(2); pos = 2; } #endif + else + { + size_t n = 1; + for (; n < _M_pathname.size() && !_S_is_dir_sep(_M_pathname[n]); ++n) + { } + if (n == _M_pathname.size()) + { + _M_type = _Type::_Filename; + return; + } + } size_t back = pos; while (pos < len) diff --git a/libstdc++-v3/src/filesystem/std-path.cc b/libstdc++-v3/src/filesystem/std-path.cc index c5bf8099036..ec8384340be 100644 --- a/libstdc++-v3/src/filesystem/std-path.cc +++ b/libstdc++-v3/src/filesystem/std-path.cc @@ -539,6 +539,28 @@ path::_M_split_cmpts() } _M_type = _Type::_Multi; + { + // Approximate count of components, to reserve space in _M_cmpts vector: + int count = 1; + bool saw_sep_last = _S_is_dir_sep(_M_pathname[0]); + bool saw_non_sep = !saw_sep_last; + for (value_type c : _M_pathname) + { + if (_S_is_dir_sep(c)) + saw_sep_last = true; + else if (saw_sep_last) + { + ++count; + saw_sep_last = false; + saw_non_sep = true; + } + } + if (saw_non_sep && saw_sep_last) + ++count; // empty filename after trailing slash + if (count > 1) + _M_cmpts.reserve(count); + } + size_t pos = 0; const size_t len = _M_pathname.size(); @@ -555,9 +577,13 @@ path::_M_split_cmpts() pos = 3; while (pos < len && !_S_is_dir_sep(_M_pathname[pos])) ++pos; + if (pos == len) + { + _M_type = _Type::_Root_name; + return; + } _M_add_root_name(pos); - if (pos < len) // also got root directory - _M_add_root_dir(pos); + _M_add_root_dir(pos); } else { @@ -584,12 +610,28 @@ path::_M_split_cmpts() else if (len > 1 && _M_pathname[1] == L':') { // got disk designator + if (len == 2) + { + _M_type = _Type::_Root_name; + return; + } _M_add_root_name(2); if (len > 2 && _S_is_dir_sep(_M_pathname[2])) _M_add_root_dir(2); pos = 2; } #endif + else + { + size_t n = 1; + for (; n < _M_pathname.size() && !_S_is_dir_sep(_M_pathname[n]); ++n) + { } + if (n == _M_pathname.size()) + { + _M_type = _Type::_Filename; + return; + } + } size_t back = pos; while (pos < len)