From: Jonathan Wakely Date: Wed, 23 May 2018 22:48:51 +0000 (+0100) Subject: Refactor path construction from null terminated iterator ranges X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=49d729eaee6bf42c46bbe3182b26b7f15a3909b5;p=gcc.git Refactor path construction from null terminated iterator ranges Move duplicated code to new _S_string_from_iter function and fix constraints to accept iterators with const value type. * include/bits/fs_path.h (path::__is_encoded_char): Change from class template to alias template. (path::__value_type_is_char): Use remove_const_t. (path:_S_string_from_iter): New helper function. (path::_S_convert(InputIter, __null_terminated)) (path::_S_convert_loc(InputIter, __null_terminated, const locale&)): Use _S_string_from_iter. (path::string<_CharT, _Allocator>(const _Allocator&)): Allow sharing rep for COW strings. * include/experimental/bits/fs_path.h (path::__is_encoded_char): Change from class template to alias template. (path::__value_type_is_char): Use remove_const. (path:_S_string_from_iter): New helper function. (path::_S_convert(InputIter, __null_terminated)) (path::_S_convert_loc(InputIter, __null_terminated, const locale&)): Use _S_string_from_iter. * testsuite/27_io/filesystem/path/append/source.cc: Test appending wide strings. * testsuite/27_io/filesystem/path/concat/strings.cc: Check for exact string equality, not path equivalence. * testsuite/27_io/filesystem/path/construct/format.cc: Check construction from std::string and std::wstring and input iterators. * testsuite/27_io/filesystem/path/construct/locale.cc: Check construction from iterators. * testsuite/experimental/filesystem/path/concat/strings.cc: Check for exact string equality, not path equivalence. * testsuite/experimental/filesystem/path/construct/locale.cc: Check construction from iterators. From-SVN: r260628 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index f5d6b10af1a..9fa9d1f4cf5 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,34 @@ 2018-05-23 Jonathan Wakely + * include/bits/fs_path.h (path::__is_encoded_char): Change from class + template to alias template. + (path::__value_type_is_char): Use remove_const_t. + (path:_S_string_from_iter): New helper function. + (path::_S_convert(InputIter, __null_terminated)) + (path::_S_convert_loc(InputIter, __null_terminated, const locale&)): + Use _S_string_from_iter. + (path::string<_CharT, _Allocator>(const _Allocator&)): Allow sharing + rep for COW strings. + * include/experimental/bits/fs_path.h (path::__is_encoded_char): + Change from class template to alias template. + (path::__value_type_is_char): Use remove_const. + (path:_S_string_from_iter): New helper function. + (path::_S_convert(InputIter, __null_terminated)) + (path::_S_convert_loc(InputIter, __null_terminated, const locale&)): + Use _S_string_from_iter. + * testsuite/27_io/filesystem/path/append/source.cc: Test appending + wide strings. + * testsuite/27_io/filesystem/path/concat/strings.cc: Check for exact + string equality, not path equivalence. + * testsuite/27_io/filesystem/path/construct/format.cc: Check + construction from std::string and std::wstring and input iterators. + * testsuite/27_io/filesystem/path/construct/locale.cc: Check + construction from iterators. + * testsuite/experimental/filesystem/path/concat/strings.cc: Check for + exact string equality, not path equivalence. + * testsuite/experimental/filesystem/path/construct/locale.cc: Check + construction from iterators. + * include/bits/fs_path.h (path::_M_type): Change default member initializer to _Filename. (path::begin): Create past-the-end iterator for empty path. diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h index 79a341830db..2dbde74e0d4 100644 --- a/libstdc++-v3/include/bits/fs_path.h +++ b/libstdc++-v3/include/bits/fs_path.h @@ -65,8 +65,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 /// A filesystem path. class path { - template - struct __is_encoded_char : std::false_type { }; + template> + using __is_encoded_char + = __or_, is_same<_Ch, wchar_t>, + is_same<_Ch, char16_t>, is_same<_Ch, char32_t>>; template> @@ -144,7 +146,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())), typename _Val = typename std::iterator_traits<_Iter>::value_type> using __value_type_is_char - = typename std::enable_if::value>::type; + = std::enable_if_t, char>>; public: #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS @@ -391,6 +393,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 iterator begin() const; iterator end() const; + // Create a basic_string by reading until a null character. + template, + typename _CharT + = typename std::remove_cv_t> + static std::basic_string<_CharT> + _S_string_from_iter(_InputIterator __source) + { + std::basic_string<_CharT> __str; + for (_CharT __ch = *__source; __ch != _CharT(); __ch = *++__source) + __str.push_back(__ch); + return __str; + } + private: enum class _Type : unsigned char { _Multi, _Root_name, _Root_dir, _Filename @@ -443,11 +459,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 static string_type _S_convert(_InputIterator __src, __null_terminated) { - using _Tp = typename std::iterator_traits<_InputIterator>::value_type; - std::basic_string::type> __tmp; - for (; *__src != _Tp{}; ++__src) - __tmp.push_back(*__src); - return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size()); + auto __s = _S_string_from_iter(__src); + return _S_convert(__s.c_str(), __s.c_str() + __s.size()); } static string_type @@ -467,10 +480,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _S_convert_loc(_InputIterator __src, __null_terminated, const std::locale& __loc) { - std::string __tmp; - while (*__src != '\0') - __tmp.push_back(*__src++); - return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc); + std::string __s = _S_string_from_iter(__src); + return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc); } template @@ -500,25 +511,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _Type _M_type = _Type::_Filename; }; - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = char; }; - - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = wchar_t; }; - - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = char16_t; }; - - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = char32_t; }; - - template - struct path::__is_encoded_char : __is_encoded_char<_Tp> { }; - inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); } size_t hash_value(const path& __p) noexcept; @@ -915,11 +907,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 path::string(const _Allocator& __a) const { if constexpr (is_same_v<_CharT, value_type>) + { #if _GLIBCXX_USE_CXX11_ABI - return { _M_pathname, __a }; + return { _M_pathname, __a }; #else - return { _M_pathname, string_type::size_type(0), __a }; + if constexpr (is_same_v<_Allocator, string_type::allocator_type>) + return _M_pathname; + else + return { _M_pathname, string_type::size_type(0), __a }; #endif + } else return _S_str_convert<_CharT, _Traits>(_M_pathname, __a); } diff --git a/libstdc++-v3/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h index ada7c1791aa..3ce2cd95b73 100644 --- a/libstdc++-v3/include/experimental/bits/fs_path.h +++ b/libstdc++-v3/include/experimental/bits/fs_path.h @@ -79,8 +79,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 /// A filesystem path. class path { - template - struct __is_encoded_char : std::false_type { }; + template::type> + using __is_encoded_char + = __or_, is_same<_Ch, wchar_t>, + is_same<_Ch, char16_t>, is_same<_Ch, char32_t>>; template> @@ -161,8 +164,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 template())), typename _Val = typename std::iterator_traits<_Iter>::value_type> - using __value_type_is_char - = typename std::enable_if::value>::type; + using __value_type_is_char = typename std::enable_if< + std::is_same::type, char>::value + >::type; public: #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS @@ -378,6 +382,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 iterator begin() const; iterator end() const; + // Create a basic_string by reading until a null character. + template, + typename _CharT + = typename std::remove_cv::type> + static std::basic_string<_CharT> + _S_string_from_iter(_InputIterator __source) + { + std::basic_string<_CharT> __str; + for (_CharT __ch = *__source; __ch != _CharT(); __ch = *++__source) + __str.push_back(__ch); + return __str; + } + private: enum class _Type : unsigned char { _Multi, _Root_name, _Root_dir, _Filename @@ -427,11 +445,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 static string_type _S_convert(_InputIterator __src, __null_terminated) { - using _Tp = typename std::iterator_traits<_InputIterator>::value_type; - std::basic_string::type> __tmp; - for (; *__src != _Tp{}; ++__src) - __tmp.push_back(*__src); - return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size()); + auto __s = _S_string_from_iter(__src); + return _S_convert(__s.c_str(), __s.c_str() + __s.size()); } static string_type @@ -451,10 +466,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _S_convert_loc(_InputIterator __src, __null_terminated, const std::locale& __loc) { - std::string __tmp; - while (*__src != '\0') - __tmp.push_back(*__src++); - return _S_convert_loc(__tmp.data(), __tmp.data()+__tmp.size(), __loc); + std::string __s = _S_string_from_iter(__src); + return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc); } bool _S_is_dir_sep(value_type __ch) @@ -594,25 +607,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 std::string _M_what = _M_gen_what(); }; - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = char; }; - - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = wchar_t; }; - - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = char16_t; }; - - template<> - struct path::__is_encoded_char : std::true_type - { using value_type = char32_t; }; - - template - struct path::__is_encoded_char : __is_encoded_char<_Tp> { }; - struct path::_Cmpt : path { _Cmpt(string_type __s, _Type __t, size_t __pos) diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc index 316d6313b0a..df917c9c5e8 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/append/source.cc @@ -36,7 +36,8 @@ using __gnu_test::compare_paths; // path::append(InputIterator first, InputIterator last) // Equivalent to: return operator/=(path(first, last)); -void test(const path& p, std::string_view s) +template +void test(const path& p, const Char* s) { path expected = p; expected /= path(s); @@ -47,8 +48,8 @@ void test(const path& p, std::string_view s) path func = p; func.append(s); - __gnu_test::test_container - input_range(s.begin(), s.end()); + __gnu_test::test_container + input_range(s, s + std::char_traits::length(s)); path range = p; range.append(input_range.begin(), input_range.end()); @@ -94,7 +95,21 @@ test03() { for (const path& p : __gnu_test::test_paths) for (const path& q : __gnu_test::test_paths) - test(p, q.native()); + { + test(p, q.c_str()); + if constexpr (!std::is_same_v) + test(p, q.string().c_str()); + } +} + +void +test04() +{ +#ifdef _GLIBCXX_USE_WCHAR_T + test( "foo", L"/bar" ); + test( L"foo", "/bar" ); + test( L"foo", L"/bar" ); +#endif } int @@ -103,4 +118,5 @@ main() test01(); test02(); test03(); + test04(); } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc index e8833ec1333..67637890c7f 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/concat/strings.cc @@ -23,6 +23,7 @@ #include #include +#include using std::filesystem::path; @@ -30,23 +31,30 @@ void test01() { path p("/"); - p += path::string_type("foo"); - VERIFY( p.filename() == "foo" ); + p += "foo"; + VERIFY( p.filename().string() == "foo" ); p += "bar"; - VERIFY( p.filename() == "foobar" ); + VERIFY( p.filename().string() == "foobar" ); p += '/'; - VERIFY( p.parent_path() == "/foobar" && p.filename() == "" ); + VERIFY( p.parent_path().string() == "/foobar" ); + VERIFY( p.filename().string() == "" ); #if _GLIBCXX_USE_WCHAR_T + VERIFY( p.parent_path().wstring() == L"/foobar" ); + VERIFY( p.filename().wstring() == L"" ); p += L"baz.txt"; #else p += "baz.txt"; #endif - VERIFY( p.filename() == "baz.txt" ); + VERIFY( p.filename().string() == "baz.txt" ); p.concat("/dir/"); - VERIFY( p.parent_path() == "/foobar/baz.txt/dir" && p.filename() == "" ); - std::string file = "file"; - p.concat(file.begin(), file.end()); - VERIFY( p.filename() == "file" ); + // N.B. on Windows p.parent_path() is "/foobar\\baz.txt\\dir" + VERIFY( p.parent_path() == path("/foobar/baz.txt/dir") ); + VERIFY( p.filename().string() == "" ); + const char file[] = "file"; + __gnu_test::test_container + input(file, file + 4); + p.concat(input.begin(), input.end()); + VERIFY( p.filename().string() == file ); } int diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/format.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/format.cc index 0e245392f33..a793451aada 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/format.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/format.cc @@ -20,13 +20,16 @@ // { dg-require-filesystem-ts "" } #include +#include #include +#include using std::filesystem::path; void test01() { + // path(string_type&&, format) auto s = [&]() -> path::string_type { return "foo/bar"; }; path p0(s()); path p1(s(), path::auto_format); @@ -40,6 +43,7 @@ test01() void test02() { + // path(const Source&, format) path::string_type s = "foo/bar"; path p0(s); path p1(s, path::auto_format); @@ -53,7 +57,8 @@ test02() void test03() { - const char* s = "foo/bar"; + // path(const Source&, format) + std::string s = "foo/bar"; path p0(s); path p1(s, path::auto_format); VERIFY( p1 == p0 ); @@ -66,19 +71,57 @@ test03() void test04() { - const char s[] = "foo/bar"; - path p0(std::begin(s), std::end(s)); - path p1(std::begin(s), std::end(s), path::auto_format); +#ifdef _GLIBCXX_USE_WCHAR_T + // path(const Source&, format) + std::wstring s = L"foo/bar"; + path p0(s); + path p1(s, path::auto_format); VERIFY( p1 == p0 ); - path p2(std::begin(s), std::end(s), path::native_format); + path p2(s, path::native_format); VERIFY( p2 == p0 ); - path p3(std::begin(s), std::end(s), path::generic_format); + path p3(s, path::generic_format); VERIFY( p3 == p0 ); +#endif } void test05() { + // path(const Source&, format) + const char* s = "foo/bar"; + path p0(s); + path p1(s, path::auto_format); + VERIFY( p1 == p0 ); + path p2(s, path::native_format); + VERIFY( p2 == p0 ); + path p3(s, path::generic_format); + VERIFY( p3 == p0 ); +} + +void +test06() +{ + // path(InputIterator, InputIterator, format) + const char s[] = "foo/bar"; + using namespace __gnu_test; + const test_container c(s, s + strlen(s)); + auto c0 = c; + path p0(std::begin(c0), std::end(c0)); + auto c1 = c; + path p1(std::begin(c1), std::end(c1), path::auto_format); + VERIFY( p1 == p0 ); + auto c2 = c; + path p2(std::begin(c2), std::end(c2), path::native_format); + VERIFY( p2 == p0 ); + auto c3 = c; + path p3(std::begin(c3), std::end(c3), path::generic_format); + VERIFY( p3 == p0 ); +} + +void +test07() +{ + // path(const Source&, const locale&, format) const char* s = "foo/bar"; std::locale loc; path p0(s, loc); @@ -91,16 +134,23 @@ test05() } void -test06() +test08() { + // path(InputIterator, InputIterator, const locale&, format) const char s[] = "foo/bar"; + using namespace __gnu_test; + const test_container c(s, s + strlen(s)); std::locale loc; - path p0(std::begin(s), std::end(s), loc); - path p1(std::begin(s), std::end(s), loc, path::auto_format); + auto c0 = c; + path p0(std::begin(c0), std::end(c0), loc); + auto c1 = c; + path p1(std::begin(c1), std::end(c1), loc, path::auto_format); VERIFY( p1 == p0 ); - path p2(std::begin(s), std::end(s), loc, path::native_format); + auto c2 = c; + path p2(std::begin(c2), std::end(c2), loc, path::native_format); VERIFY( p2 == p0 ); - path p3(std::begin(s), std::end(s), loc, path::generic_format); + auto c3 = c; + path p3(std::begin(c3), std::end(c3), loc, path::generic_format); VERIFY( p3 == p0 ); } @@ -113,4 +163,6 @@ main() test04(); test05(); test06(); + test07(); + test08(); } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/locale.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/locale.cc index 498c1421fbf..c32c647e167 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/locale.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/locale.cc @@ -23,6 +23,7 @@ #include #include +#include using std::filesystem::path; @@ -33,8 +34,41 @@ test01() VERIFY( p.native() == "/foo/bar" ); } +void +test02() +{ + using __gnu_test::test_container; + using __gnu_test::input_iterator_wrapper; + // Test with input iterators and const value_types + + const std::locale loc; + const std::string s = "foo/bar/"; + const path p0(s); + + test_container + r1((char*)s.c_str(), (char*)s.c_str() + s.size()); + path p1(r1.begin(), r1.end(), loc); + VERIFY( p1 == p0 ); + + test_container + r2((char*)s.c_str(), (char*)s.c_str() + s.size() + 1); // includes null-terminator + path p2(r2.begin(), loc); + VERIFY( p2 == p0 ); + + test_container + r3(s.c_str(), s.c_str() + s.size()); + path p3(r3.begin(), r3.end(), loc); + VERIFY( p3 == p0 ); + + test_container + r4(s.c_str(), s.c_str() + s.size() + 1); // includes null-terminator + path p4(r4.begin(), loc); + VERIFY( p4 == p0 ); +} + int main() { test01(); + test02(); } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/path/concat/strings.cc b/libstdc++-v3/testsuite/experimental/filesystem/path/concat/strings.cc index 649ad0bb0c2..37ea0ebb798 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/path/concat/strings.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/concat/strings.cc @@ -23,6 +23,7 @@ #include #include +#include using std::experimental::filesystem::path; @@ -30,23 +31,30 @@ void test01() { path p("/"); - p += path::string_type("foo"); - VERIFY( p.filename() == "foo" ); + p += std::string("foo"); + VERIFY( p.filename().string() == "foo" ); p += "bar"; - VERIFY( p.filename() == "foobar" ); + VERIFY( p.filename().string() == "foobar" ); p += '/'; - VERIFY( p.parent_path() == "/foobar" && p.filename() == "." ); + VERIFY( p.parent_path().string() == "/foobar" ); + VERIFY( p.filename().string() == "." ); #if _GLIBCXX_USE_WCHAR_T + VERIFY( p.parent_path().wstring() == L"/foobar" ); + VERIFY( p.filename().wstring() == L"." ); p += L"baz.txt"; #else p += "baz.txt"; #endif - VERIFY( p.filename() == "baz.txt" ); + VERIFY( p.filename().string() == "baz.txt" ); p.concat("/dir/"); - VERIFY( p.parent_path() == "/foobar/baz.txt/dir" && p.filename() == "." ); - std::string file = "file"; - p.concat(file.begin(), file.end()); - VERIFY( p.filename() == "file" ); + // N.B. on Windows p.parent_path() is "/foobar\\baz.txt\\dir" + VERIFY( p.parent_path() == path("/foobar/baz.txt/dir") ); + VERIFY( p.filename().string() == "." ); + const char file[] = "file"; + __gnu_test::test_container + input(file, file + 4); + p.concat(input.begin(), input.end()); + VERIFY( p.filename().string() == file ); } int diff --git a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/locale.cc b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/locale.cc index 46e0758e294..3ca16edc2d7 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/locale.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/locale.cc @@ -23,6 +23,7 @@ #include #include +#include using std::experimental::filesystem::path; @@ -33,8 +34,41 @@ test01() VERIFY( p.string() == "/foo/bar" ); } +void +test02() +{ + using __gnu_test::test_container; + using __gnu_test::input_iterator_wrapper; + // Test with input iterators and const value_types + + const std::locale loc; + const std::string s = "foo/bar/"; + const path p0(s); + + test_container + r1((char*)s.c_str(), (char*)s.c_str() + s.size()); + path p1(r1.begin(), r1.end(), loc); + VERIFY( p1 == p0 ); + + test_container + r2((char*)s.c_str(), (char*)s.c_str() + s.size() + 1); // includes null-terminator + path p2(r2.begin(), loc); + VERIFY( p2 == p0 ); + + test_container + r3(s.c_str(), s.c_str() + s.size()); + path p3(r3.begin(), r3.end(), loc); + VERIFY( p3 == p0 ); + + test_container + r4(s.c_str(), s.c_str() + s.size() + 1); // includes null-terminator + path p4(r4.begin(), loc); + VERIFY( p4 == p0 ); +} + int main() { test01(); + test02(); }