PR libstdc++/71044 optimize std::filesystem::path construction
This new implementation has a smaller footprint than the previous implementation, due to replacing std::vector<_Cmpt> with a custom pimpl type that only needs a single pointer. The _M_type enumeration is also combined with the pimpl type, by using a tagged pointer, reducing sizeof(path) further still. Construction and modification of paths is now done more efficiently, by splitting the input into a stack-based buffer of string_view objects instead of a dynamically-allocated vector containing strings. Once the final size is known only a single allocation is needed to reserve space for it. The append and concat operations no longer require constructing temporary path objects, nor re-parsing the entire native pathname. This results in algorithmic improvements to path construction, and working with large paths is much faster. PR libstdc++/71044 * include/bits/fs_path.h (path::path(path&&)): Add noexcept when appropriate. Move _M_cmpts instead of reparsing the native pathname. (path::operator=(const path&)): Do not define as defaulted. (path::operator/=, path::append): Call _M_append. (path::concat): Call _M_concat. (path::path(string_type, _Type): Change type of first parameter to basic_string_view<value_type>. (path::_M_append(basic_string_view<value_type>)): New member function. (path::_M_concat(basic_string_view<value_type>)): New member function. (_S_convert(value_type*, __null_terminated)): Return string view. (_S_convert(const value_type*, __null_terminated)): Return string view. (_S_convert(value_type*, value_type*)) (_S_convert(const value_type*, const value_type*)): Add overloads for pairs of pointers. (_S_convert(_InputIterator, __null_terminated)): Construct string_type explicitly, for cases where _S_convert returns a string view. (path::_S_is_dir_sep): Replace with non-member is_dir_sep. (path::_M_trim, path::_M_add_root_name, path::_M_add_root_dir) (path::_M_add_filename): Remove. (path::_M_type()): New member function to replace _M_type data member. (path::_List): Define new struct type instead of using std::vector. (path::_Cmpt::_Cmpt(string_type, _Type, size_t)): Change type of first parameter to basic_string_view<value_type>. (path::operator+=(const path&)): Do not define inline. (path::operator+=(const string_type&)): Call _M_concat. (path::operator+=(const value_type*)): Likewise. (path::operator+=(value_type)): Likewise. (path::operator+=(basic_string_view<value_type>)): Likewise. (path::operator/=(const path&)): Do not define inline. (path::_M_append(path)): Remove. * python/libstdcxx/v6/printers.py (StdPathPrinter): New printer that understands the new path::_List type. * src/filesystem/std-path.cc (is_dir_sep): New function to replace path::_S_is_dir_sep. (path::_Parser): New helper class to parse strings as paths. (path::_List::_Impl): Define container type for path components. (path::_List): Define members. (path::operator=(const path&)): Define explicitly, to provide the strong exception safety guarantee. (path::operator/=(const path&)): Implement manually by processing each component of the argument, rather than using _M_split_cmpts to parse the entire string again. (path::_M_append(string_type)): Likewise. (path::operator+=(const path&)): Likewise. (path::_M_concat(string_type)): Likewise. (path::remove_filename()): Perform trim directly instead of calling _M_trim(). (path::_M_split_cmpts()): Rewrite in terms of _Parser class. (path::_M_trim, path::_M_add_root_name, path::_M_add_root_dir) (path::_M_add_filename): Remove. * testsuite/27_io/filesystem/path/append/source.cc: Test appending a string view that aliases the path. testsuite/27_io/filesystem/path/concat/strings.cc: Test concatenating a string view that aliases the path. From-SVN: r267106
This commit is contained in:
parent
51beaeba8a
commit
4f87bb8d6e
|
@ -1,5 +1,61 @@
|
||||||
2018-12-13 Jonathan Wakely <jwakely@redhat.com>
|
2018-12-13 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
|
PR libstdc++/71044
|
||||||
|
* include/bits/fs_path.h (path::path(path&&)): Add noexcept when
|
||||||
|
appropriate. Move _M_cmpts instead of reparsing the native pathname.
|
||||||
|
(path::operator=(const path&)): Do not define as defaulted.
|
||||||
|
(path::operator/=, path::append): Call _M_append.
|
||||||
|
(path::concat): Call _M_concat.
|
||||||
|
(path::path(string_type, _Type): Change type of first parameter to
|
||||||
|
basic_string_view<value_type>.
|
||||||
|
(path::_M_append(basic_string_view<value_type>)): New member function.
|
||||||
|
(path::_M_concat(basic_string_view<value_type>)): New member function.
|
||||||
|
(_S_convert(value_type*, __null_terminated)): Return string view.
|
||||||
|
(_S_convert(const value_type*, __null_terminated)): Return string view.
|
||||||
|
(_S_convert(value_type*, value_type*))
|
||||||
|
(_S_convert(const value_type*, const value_type*)): Add overloads for
|
||||||
|
pairs of pointers.
|
||||||
|
(_S_convert(_InputIterator, __null_terminated)): Construct string_type
|
||||||
|
explicitly, for cases where _S_convert returns a string view.
|
||||||
|
(path::_S_is_dir_sep): Replace with non-member is_dir_sep.
|
||||||
|
(path::_M_trim, path::_M_add_root_name, path::_M_add_root_dir)
|
||||||
|
(path::_M_add_filename): Remove.
|
||||||
|
(path::_M_type()): New member function to replace _M_type data member.
|
||||||
|
(path::_List): Define new struct type instead of using std::vector.
|
||||||
|
(path::_Cmpt::_Cmpt(string_type, _Type, size_t)): Change type of
|
||||||
|
first parameter to basic_string_view<value_type>.
|
||||||
|
(path::operator+=(const path&)): Do not define inline.
|
||||||
|
(path::operator+=(const string_type&)): Call _M_concat.
|
||||||
|
(path::operator+=(const value_type*)): Likewise.
|
||||||
|
(path::operator+=(value_type)): Likewise.
|
||||||
|
(path::operator+=(basic_string_view<value_type>)): Likewise.
|
||||||
|
(path::operator/=(const path&)): Do not define inline.
|
||||||
|
(path::_M_append(path)): Remove.
|
||||||
|
* python/libstdcxx/v6/printers.py (StdPathPrinter): New printer that
|
||||||
|
understands the new path::_List type.
|
||||||
|
* src/filesystem/std-path.cc (is_dir_sep): New function to replace
|
||||||
|
path::_S_is_dir_sep.
|
||||||
|
(path::_Parser): New helper class to parse strings as paths.
|
||||||
|
(path::_List::_Impl): Define container type for path components.
|
||||||
|
(path::_List): Define members.
|
||||||
|
(path::operator=(const path&)): Define explicitly, to provide the
|
||||||
|
strong exception safety guarantee.
|
||||||
|
(path::operator/=(const path&)): Implement manually by processing
|
||||||
|
each component of the argument, rather than using _M_split_cmpts
|
||||||
|
to parse the entire string again.
|
||||||
|
(path::_M_append(string_type)): Likewise.
|
||||||
|
(path::operator+=(const path&)): Likewise.
|
||||||
|
(path::_M_concat(string_type)): Likewise.
|
||||||
|
(path::remove_filename()): Perform trim directly instead of calling
|
||||||
|
_M_trim().
|
||||||
|
(path::_M_split_cmpts()): Rewrite in terms of _Parser class.
|
||||||
|
(path::_M_trim, path::_M_add_root_name, path::_M_add_root_dir)
|
||||||
|
(path::_M_add_filename): Remove.
|
||||||
|
* testsuite/27_io/filesystem/path/append/source.cc: Test appending a
|
||||||
|
string view that aliases the path.
|
||||||
|
testsuite/27_io/filesystem/path/concat/strings.cc: Test concatenating
|
||||||
|
a string view that aliases the path.
|
||||||
|
|
||||||
* testsuite/27_io/filesystem/path/generation/proximate.cc: Use
|
* testsuite/27_io/filesystem/path/generation/proximate.cc: Use
|
||||||
preferred directory separators for normalized paths.
|
preferred directory separators for normalized paths.
|
||||||
* testsuite/27_io/filesystem/path/generation/relative.cc: Likewise.
|
* testsuite/27_io/filesystem/path/generation/relative.cc: Likewise.
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
@ -45,6 +44,7 @@
|
||||||
#include <bits/locale_conv.h>
|
#include <bits/locale_conv.h>
|
||||||
#include <ext/concurrence.h>
|
#include <ext/concurrence.h>
|
||||||
#include <bits/shared_ptr.h>
|
#include <bits/shared_ptr.h>
|
||||||
|
#include <bits/unique_ptr.h>
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
# define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1
|
# define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1
|
||||||
|
@ -169,12 +169,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
|
|
||||||
path(const path& __p) = default;
|
path(const path& __p) = default;
|
||||||
|
|
||||||
path(path&& __p) noexcept
|
path(path&& __p)
|
||||||
: _M_pathname(std::move(__p._M_pathname)), _M_type(__p._M_type)
|
#if _GLIBCXX_USE_CXX11_ABI || _GLIBCXX_FULLY_DYNAMIC_STRING == 0
|
||||||
{
|
noexcept
|
||||||
_M_split_cmpts();
|
#endif
|
||||||
__p.clear();
|
: _M_pathname(std::move(__p._M_pathname)),
|
||||||
}
|
_M_cmpts(std::move(__p._M_cmpts))
|
||||||
|
{ __p.clear(); }
|
||||||
|
|
||||||
path(string_type&& __source, format = auto_format)
|
path(string_type&& __source, format = auto_format)
|
||||||
: _M_pathname(std::move(__source))
|
: _M_pathname(std::move(__source))
|
||||||
|
@ -213,8 +214,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
|
|
||||||
// assignments
|
// assignments
|
||||||
|
|
||||||
path& operator=(const path& __p) = default;
|
path& operator=(const path&);
|
||||||
path& operator=(path&& __p) noexcept;
|
path& operator=(path&&) noexcept;
|
||||||
path& operator=(string_type&& __source);
|
path& operator=(string_type&& __source);
|
||||||
path& assign(string_type&& __source);
|
path& assign(string_type&& __source);
|
||||||
|
|
||||||
|
@ -240,17 +241,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
template <class _Source>
|
template <class _Source>
|
||||||
_Path<_Source>&
|
_Path<_Source>&
|
||||||
operator/=(_Source const& __source)
|
operator/=(_Source const& __source)
|
||||||
{ return _M_append(path(__source)); }
|
{
|
||||||
|
_M_append(_S_convert(_S_range_begin(__source), _S_range_end(__source)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename _Source>
|
template<typename _Source>
|
||||||
_Path<_Source>&
|
_Path<_Source>&
|
||||||
append(_Source const& __source)
|
append(_Source const& __source)
|
||||||
{ return _M_append(path(__source)); }
|
{
|
||||||
|
_M_append(_S_convert(_S_range_begin(__source), _S_range_end(__source)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename _InputIterator>
|
template<typename _InputIterator>
|
||||||
_Path<_InputIterator, _InputIterator>&
|
_Path<_InputIterator, _InputIterator>&
|
||||||
append(_InputIterator __first, _InputIterator __last)
|
append(_InputIterator __first, _InputIterator __last)
|
||||||
{ return _M_append(path(__first, __last)); }
|
{
|
||||||
|
_M_append(_S_convert(__first, __last));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// concatenation
|
// concatenation
|
||||||
|
|
||||||
|
@ -271,12 +281,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
template<typename _Source>
|
template<typename _Source>
|
||||||
_Path<_Source>&
|
_Path<_Source>&
|
||||||
concat(_Source const& __x)
|
concat(_Source const& __x)
|
||||||
{ return *this += _S_convert(_S_range_begin(__x), _S_range_end(__x)); }
|
{
|
||||||
|
_M_concat(_S_convert(_S_range_begin(__x), _S_range_end(__x)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename _InputIterator>
|
template<typename _InputIterator>
|
||||||
_Path<_InputIterator, _InputIterator>&
|
_Path<_InputIterator, _InputIterator>&
|
||||||
concat(_InputIterator __first, _InputIterator __last)
|
concat(_InputIterator __first, _InputIterator __last)
|
||||||
{ return *this += _S_convert(__first, __last); }
|
{
|
||||||
|
_M_concat(_S_convert(__first, __last));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// modifiers
|
// modifiers
|
||||||
|
|
||||||
|
@ -402,30 +418,41 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class _Type : unsigned char {
|
enum class _Type : unsigned char {
|
||||||
_Multi, _Root_name, _Root_dir, _Filename
|
_Multi = 0, _Root_name, _Root_dir, _Filename
|
||||||
};
|
};
|
||||||
|
|
||||||
path(string_type __str, _Type __type) : _M_pathname(__str), _M_type(__type)
|
path(basic_string_view<value_type> __str, _Type __type)
|
||||||
|
: _M_pathname(__str)
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_type != _Type::_Multi);
|
__glibcxx_assert(__type != _Type::_Multi);
|
||||||
|
_M_cmpts.type(__type);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class _Split { _Stem, _Extension };
|
enum class _Split { _Stem, _Extension };
|
||||||
|
|
||||||
path& _M_append(path __p);
|
void _M_append(basic_string_view<value_type>);
|
||||||
|
void _M_concat(basic_string_view<value_type>);
|
||||||
|
|
||||||
pair<const string_type*, size_t> _M_find_extension() const;
|
pair<const string_type*, size_t> _M_find_extension() const;
|
||||||
|
|
||||||
template<typename _CharT>
|
template<typename _CharT>
|
||||||
struct _Cvt;
|
struct _Cvt;
|
||||||
|
|
||||||
static string_type
|
static basic_string_view<value_type>
|
||||||
_S_convert(value_type* __src, __null_terminated)
|
_S_convert(value_type* __src, __null_terminated)
|
||||||
{ return string_type(__src); }
|
{ return __src; }
|
||||||
|
|
||||||
static string_type
|
static basic_string_view<value_type>
|
||||||
_S_convert(const value_type* __src, __null_terminated)
|
_S_convert(const value_type* __src, __null_terminated)
|
||||||
{ return string_type(__src); }
|
{ return __src; }
|
||||||
|
|
||||||
|
static basic_string_view<value_type>
|
||||||
|
_S_convert(value_type* __first, value_type* __last)
|
||||||
|
{ return {__first, __last - __first}; }
|
||||||
|
|
||||||
|
static basic_string_view<value_type>
|
||||||
|
_S_convert(const value_type* __first, const value_type* __last)
|
||||||
|
{ return {__first, __last - __first}; }
|
||||||
|
|
||||||
template<typename _Iter>
|
template<typename _Iter>
|
||||||
static string_type
|
static string_type
|
||||||
|
@ -440,8 +467,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
static string_type
|
static string_type
|
||||||
_S_convert(_InputIterator __src, __null_terminated)
|
_S_convert(_InputIterator __src, __null_terminated)
|
||||||
{
|
{
|
||||||
|
// Read from iterator into basic_string until a null value is seen:
|
||||||
auto __s = _S_string_from_iter(__src);
|
auto __s = _S_string_from_iter(__src);
|
||||||
return _S_convert(__s.c_str(), __s.c_str() + __s.size());
|
// Convert (if needed) from iterator's value type to path::value_type:
|
||||||
|
return string_type(_S_convert(__s.data(), __s.data() + __s.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static string_type
|
static string_type
|
||||||
|
@ -469,27 +498,65 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
static basic_string<_CharT, _Traits, _Allocator>
|
static basic_string<_CharT, _Traits, _Allocator>
|
||||||
_S_str_convert(const string_type&, const _Allocator& __a);
|
_S_str_convert(const string_type&, const _Allocator& __a);
|
||||||
|
|
||||||
bool _S_is_dir_sep(value_type __ch)
|
|
||||||
{
|
|
||||||
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
||||||
return __ch == L'/' || __ch == preferred_separator;
|
|
||||||
#else
|
|
||||||
return __ch == '/';
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void _M_split_cmpts();
|
void _M_split_cmpts();
|
||||||
void _M_trim();
|
|
||||||
void _M_add_root_name(size_t __n);
|
_Type _M_type() const noexcept { return _M_cmpts.type(); }
|
||||||
void _M_add_root_dir(size_t __pos);
|
|
||||||
void _M_add_filename(size_t __pos, size_t __n);
|
|
||||||
|
|
||||||
string_type _M_pathname;
|
string_type _M_pathname;
|
||||||
|
|
||||||
struct _Cmpt;
|
struct _Cmpt;
|
||||||
using _List = _GLIBCXX_STD_C::vector<_Cmpt>;
|
|
||||||
_List _M_cmpts; // empty unless _M_type == _Type::_Multi
|
struct _List
|
||||||
_Type _M_type = _Type::_Filename;
|
{
|
||||||
|
using value_type = _Cmpt;
|
||||||
|
using iterator = value_type*;
|
||||||
|
using const_iterator = const value_type*;
|
||||||
|
|
||||||
|
_List();
|
||||||
|
_List(const _List&);
|
||||||
|
_List(_List&&) = default;
|
||||||
|
_List& operator=(const _List&);
|
||||||
|
_List& operator=(_List&&) = default;
|
||||||
|
~_List() = default;
|
||||||
|
|
||||||
|
_Type type() const noexcept
|
||||||
|
{ return _Type{reinterpret_cast<uintptr_t>(_M_impl.get()) & 0x3}; }
|
||||||
|
|
||||||
|
void type(_Type) noexcept;
|
||||||
|
|
||||||
|
int size() const noexcept; // zero unless type() == _Type::_Multi
|
||||||
|
bool empty() const noexcept; // true unless type() == _Type::_Multi
|
||||||
|
void clear();
|
||||||
|
void swap(_List& __l) noexcept { _M_impl.swap(__l._M_impl); }
|
||||||
|
int capacity() const noexcept;
|
||||||
|
void reserve(int, bool); ///< @pre type() == _Type::_Multi
|
||||||
|
|
||||||
|
// All the member functions below here have a precondition !empty()
|
||||||
|
// (and they should only be called from within the library).
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator end() const;
|
||||||
|
|
||||||
|
value_type& front() noexcept;
|
||||||
|
value_type& back() noexcept;
|
||||||
|
const value_type& front() const noexcept;
|
||||||
|
const value_type& back() const noexcept;
|
||||||
|
|
||||||
|
void erase(const_iterator);
|
||||||
|
void erase(const_iterator, const_iterator);
|
||||||
|
|
||||||
|
struct _Impl;
|
||||||
|
struct _Impl_deleter
|
||||||
|
{
|
||||||
|
void operator()(_Impl*) const noexcept;
|
||||||
|
};
|
||||||
|
unique_ptr<_Impl, _Impl_deleter> _M_impl;
|
||||||
|
};
|
||||||
|
_List _M_cmpts;
|
||||||
|
|
||||||
|
struct _Parser;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); }
|
inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); }
|
||||||
|
@ -605,8 +672,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
|
|
||||||
struct path::_Cmpt : path
|
struct path::_Cmpt : path
|
||||||
{
|
{
|
||||||
_Cmpt(string_type __s, _Type __t, size_t __pos)
|
_Cmpt(basic_string_view<value_type> __s, _Type __t, size_t __pos)
|
||||||
: path(std::move(__s), __t), _M_pos(__pos) { }
|
: path(__s, __t), _M_pos(__pos) { }
|
||||||
|
|
||||||
_Cmpt() : _M_pos(-1) { }
|
_Cmpt() : _M_pos(-1) { }
|
||||||
|
|
||||||
|
@ -733,7 +800,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
private:
|
private:
|
||||||
friend class path;
|
friend class path;
|
||||||
|
|
||||||
bool _M_is_multi() const { return _M_path->_M_type == _Type::_Multi; }
|
bool _M_is_multi() const { return _M_path->_M_type() == _Type::_Multi; }
|
||||||
|
|
||||||
friend difference_type
|
friend difference_type
|
||||||
__path_iter_distance(const iterator& __first, const iterator& __last)
|
__path_iter_distance(const iterator& __first, const iterator& __last)
|
||||||
|
@ -785,7 +852,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
{
|
{
|
||||||
_M_pathname = std::move(__p._M_pathname);
|
_M_pathname = std::move(__p._M_pathname);
|
||||||
_M_cmpts = std::move(__p._M_cmpts);
|
_M_cmpts = std::move(__p._M_cmpts);
|
||||||
_M_type = __p._M_type;
|
|
||||||
__p.clear();
|
__p.clear();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -798,41 +864,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
path::assign(string_type&& __source)
|
path::assign(string_type&& __source)
|
||||||
{ return *this = path(std::move(__source)); }
|
{ return *this = path(std::move(__source)); }
|
||||||
|
|
||||||
inline path&
|
|
||||||
path::operator+=(const path& __p)
|
|
||||||
{
|
|
||||||
return operator+=(__p.native());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline path&
|
inline path&
|
||||||
path::operator+=(const string_type& __x)
|
path::operator+=(const string_type& __x)
|
||||||
{
|
{
|
||||||
_M_pathname += __x;
|
_M_concat(__x);
|
||||||
_M_split_cmpts();
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline path&
|
inline path&
|
||||||
path::operator+=(const value_type* __x)
|
path::operator+=(const value_type* __x)
|
||||||
{
|
{
|
||||||
_M_pathname += __x;
|
_M_concat(__x);
|
||||||
_M_split_cmpts();
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline path&
|
inline path&
|
||||||
path::operator+=(value_type __x)
|
path::operator+=(value_type __x)
|
||||||
{
|
{
|
||||||
_M_pathname += __x;
|
_M_concat(basic_string_view<value_type>(&__x, 1));
|
||||||
_M_split_cmpts();
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline path&
|
inline path&
|
||||||
path::operator+=(basic_string_view<value_type> __x)
|
path::operator+=(basic_string_view<value_type> __x)
|
||||||
{
|
{
|
||||||
_M_pathname.append(__x.data(), __x.size());
|
_M_concat(__x);
|
||||||
_M_split_cmpts();
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,7 +914,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
{
|
{
|
||||||
_M_pathname.swap(__rhs._M_pathname);
|
_M_pathname.swap(__rhs._M_pathname);
|
||||||
_M_cmpts.swap(__rhs._M_cmpts);
|
_M_cmpts.swap(__rhs._M_cmpts);
|
||||||
std::swap(_M_type, __rhs._M_type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename _CharT, typename _Traits, typename _Allocator>
|
template<typename _CharT, typename _Traits, typename _Allocator>
|
||||||
|
@ -968,7 +1023,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
#endif
|
#endif
|
||||||
string_type __str(__a);
|
string_type __str(__a);
|
||||||
|
|
||||||
if (_M_type == _Type::_Root_dir)
|
if (_M_type() == _Type::_Root_dir)
|
||||||
__str.assign(1, __slash);
|
__str.assign(1, __slash);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -979,7 +1034,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
if (__add_slash)
|
if (__add_slash)
|
||||||
__str += __slash;
|
__str += __slash;
|
||||||
__str += __elem._M_pathname;
|
__str += __elem._M_pathname;
|
||||||
__add_slash = __elem._M_type == _Type::_Filename;
|
__add_slash = __elem._M_type() == _Type::_Filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1026,14 +1081,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
{
|
{
|
||||||
if (empty())
|
if (empty())
|
||||||
return {};
|
return {};
|
||||||
else if (_M_type == _Type::_Filename)
|
else if (_M_type() == _Type::_Filename)
|
||||||
return *this;
|
return *this;
|
||||||
else if (_M_type == _Type::_Multi)
|
else if (_M_type() == _Type::_Multi)
|
||||||
{
|
{
|
||||||
if (_M_pathname.back() == preferred_separator)
|
if (_M_pathname.back() == preferred_separator)
|
||||||
return {};
|
return {};
|
||||||
auto& __last = *--end();
|
auto& __last = *--end();
|
||||||
if (__last._M_type == _Type::_Filename)
|
if (__last._M_type() == _Type::_Filename)
|
||||||
return __last;
|
return __last;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
@ -1084,7 +1139,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
inline path::iterator
|
inline path::iterator
|
||||||
path::begin() const
|
path::begin() const
|
||||||
{
|
{
|
||||||
if (_M_type == _Type::_Multi)
|
if (_M_type() == _Type::_Multi)
|
||||||
return iterator(this, _M_cmpts.begin());
|
return iterator(this, _M_cmpts.begin());
|
||||||
return iterator(this, empty());
|
return iterator(this, empty());
|
||||||
}
|
}
|
||||||
|
@ -1092,48 +1147,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
inline path::iterator
|
inline path::iterator
|
||||||
path::end() const
|
path::end() const
|
||||||
{
|
{
|
||||||
if (_M_type == _Type::_Multi)
|
if (_M_type() == _Type::_Multi)
|
||||||
return iterator(this, _M_cmpts.end());
|
return iterator(this, _M_cmpts.end());
|
||||||
return iterator(this, true);
|
return iterator(this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
||||||
inline path& path::operator/=(const path& __p)
|
|
||||||
{
|
|
||||||
// Much simpler than the specification in the standard,
|
|
||||||
// as any path with root-name or root-dir is absolute.
|
|
||||||
if (__p.is_absolute())
|
|
||||||
operator=(__p);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (has_filename() || (_M_type == _Type::_Root_name))
|
|
||||||
_M_pathname += preferred_separator;
|
|
||||||
_M_pathname += __p.native();
|
|
||||||
_M_split_cmpts();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline path&
|
|
||||||
path::_M_append(path __p)
|
|
||||||
{
|
|
||||||
if (__p.is_absolute())
|
|
||||||
operator=(std::move(__p));
|
|
||||||
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
||||||
else if (__p.has_root_name() && __p.root_name() != root_name())
|
|
||||||
operator=(std::move(__p));
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
operator/=(const_cast<const path&>(__p));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline path::iterator&
|
inline path::iterator&
|
||||||
path::iterator::operator++()
|
path::iterator::operator++()
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_path != nullptr);
|
__glibcxx_assert(_M_path != nullptr);
|
||||||
if (_M_path->_M_type == _Type::_Multi)
|
if (_M_path->_M_type() == _Type::_Multi)
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_cur != _M_path->_M_cmpts.end());
|
__glibcxx_assert(_M_cur != _M_path->_M_cmpts.end());
|
||||||
++_M_cur;
|
++_M_cur;
|
||||||
|
@ -1150,7 +1173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
path::iterator::operator--()
|
path::iterator::operator--()
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_path != nullptr);
|
__glibcxx_assert(_M_path != nullptr);
|
||||||
if (_M_path->_M_type == _Type::_Multi)
|
if (_M_path->_M_type() == _Type::_Multi)
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_cur != _M_path->_M_cmpts.begin());
|
__glibcxx_assert(_M_cur != _M_path->_M_cmpts.begin());
|
||||||
--_M_cur;
|
--_M_cur;
|
||||||
|
@ -1167,7 +1190,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
path::iterator::operator*() const
|
path::iterator::operator*() const
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_path != nullptr);
|
__glibcxx_assert(_M_path != nullptr);
|
||||||
if (_M_path->_M_type == _Type::_Multi)
|
if (_M_path->_M_type() == _Type::_Multi)
|
||||||
{
|
{
|
||||||
__glibcxx_assert(_M_cur != _M_path->_M_cmpts.end());
|
__glibcxx_assert(_M_cur != _M_path->_M_cmpts.end());
|
||||||
return *_M_cur;
|
return *_M_cur;
|
||||||
|
@ -1182,7 +1205,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
||||||
return false;
|
return false;
|
||||||
if (_M_path == nullptr)
|
if (_M_path == nullptr)
|
||||||
return true;
|
return true;
|
||||||
if (_M_path->_M_type == path::_Type::_Multi)
|
if (_M_path->_M_type() == path::_Type::_Multi)
|
||||||
return _M_cur == __rhs._M_cur;
|
return _M_cur == __rhs._M_cur;
|
||||||
return _M_at_end == __rhs._M_at_end;
|
return _M_at_end == __rhs._M_at_end;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1244,6 +1244,77 @@ class StdExpPathPrinter:
|
||||||
def children(self):
|
def children(self):
|
||||||
return self._iterator(self.val['_M_cmpts'])
|
return self._iterator(self.val['_M_cmpts'])
|
||||||
|
|
||||||
|
class StdPathPrinter:
|
||||||
|
"Print a std::filesystem::path"
|
||||||
|
|
||||||
|
def __init__ (self, typename, val):
|
||||||
|
self.val = val
|
||||||
|
self.typename = typename
|
||||||
|
impl = self.val['_M_cmpts']['_M_impl']['_M_t']['_M_t']['_M_head_impl']
|
||||||
|
self.type = impl.cast(gdb.lookup_type('uintptr_t')) & 3
|
||||||
|
if self.type == 0:
|
||||||
|
self.impl = impl
|
||||||
|
else:
|
||||||
|
self.impl = None
|
||||||
|
|
||||||
|
def _path_type(self):
|
||||||
|
t = str(self.type.cast(gdb.lookup_type(self.typename + '::_Type')))
|
||||||
|
if t[-9:] == '_Root_dir':
|
||||||
|
return "root-directory"
|
||||||
|
if t[-10:] == '_Root_name':
|
||||||
|
return "root-name"
|
||||||
|
return None
|
||||||
|
|
||||||
|
def to_string (self):
|
||||||
|
path = "%s" % self.val ['_M_pathname']
|
||||||
|
if self.type != 0:
|
||||||
|
t = self._path_type()
|
||||||
|
if t:
|
||||||
|
path = '%s [%s]' % (path, t)
|
||||||
|
return "filesystem::path %s" % path
|
||||||
|
|
||||||
|
class _iterator(Iterator):
|
||||||
|
def __init__(self, impl, pathtype):
|
||||||
|
if impl:
|
||||||
|
# We can't access _Impl::_M_size because _Impl is incomplete
|
||||||
|
# so cast to int* to access the _M_size member at offset zero,
|
||||||
|
int_type = gdb.lookup_type('int')
|
||||||
|
cmpt_type = gdb.lookup_type(pathtype+'::_Cmpt')
|
||||||
|
char_type = gdb.lookup_type('char')
|
||||||
|
impl = impl.cast(int_type.pointer())
|
||||||
|
size = impl.dereference()
|
||||||
|
#self.capacity = (impl + 1).dereference()
|
||||||
|
if hasattr(gdb.Type, 'alignof'):
|
||||||
|
sizeof_Impl = max(2 * int_type.sizeof, cmpt_type.alignof)
|
||||||
|
else:
|
||||||
|
sizeof_Impl = 2 * int_type.sizeof
|
||||||
|
begin = impl.cast(char_type.pointer()) + sizeof_Impl
|
||||||
|
self.item = begin.cast(cmpt_type.pointer())
|
||||||
|
self.finish = self.item + size
|
||||||
|
self.count = 0
|
||||||
|
else:
|
||||||
|
self.item = None
|
||||||
|
self.finish = None
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if self.item == self.finish:
|
||||||
|
raise StopIteration
|
||||||
|
item = self.item.dereference()
|
||||||
|
count = self.count
|
||||||
|
self.count = self.count + 1
|
||||||
|
self.item = self.item + 1
|
||||||
|
path = item['_M_pathname']
|
||||||
|
t = StdPathPrinter(item.type.name, item)._path_type()
|
||||||
|
if not t:
|
||||||
|
t = count
|
||||||
|
return ('[%s]' % t, path)
|
||||||
|
|
||||||
|
def children(self):
|
||||||
|
return self._iterator(self.impl, self.typename)
|
||||||
|
|
||||||
|
|
||||||
class StdPairPrinter:
|
class StdPairPrinter:
|
||||||
"Print a std::pair object, with 'first' and 'second' as children"
|
"Print a std::pair object, with 'first' and 'second' as children"
|
||||||
|
@ -1759,9 +1830,9 @@ def build_libstdcxx_dictionary ():
|
||||||
libstdcxx_printer.add_version('std::experimental::filesystem::v1::__cxx11::',
|
libstdcxx_printer.add_version('std::experimental::filesystem::v1::__cxx11::',
|
||||||
'path', StdExpPathPrinter)
|
'path', StdExpPathPrinter)
|
||||||
libstdcxx_printer.add_version('std::filesystem::',
|
libstdcxx_printer.add_version('std::filesystem::',
|
||||||
'path', StdExpPathPrinter)
|
'path', StdPathPrinter)
|
||||||
libstdcxx_printer.add_version('std::filesystem::__cxx11::',
|
libstdcxx_printer.add_version('std::filesystem::__cxx11::',
|
||||||
'path', StdExpPathPrinter)
|
'path', StdPathPrinter)
|
||||||
|
|
||||||
# C++17 components
|
# C++17 components
|
||||||
libstdcxx_printer.add_version('std::',
|
libstdcxx_printer.add_version('std::',
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -112,6 +112,33 @@ test04()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test05()
|
||||||
|
{
|
||||||
|
std::basic_string_view<path::value_type> s;
|
||||||
|
|
||||||
|
path p = "0/1/2/3/4/5/6";
|
||||||
|
// The string_view aliases the path's internal string:
|
||||||
|
s = p.native();
|
||||||
|
// 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/6/0/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/" );
|
||||||
|
|
||||||
|
// And aliasing one of the components of the path:
|
||||||
|
path p3 = "0/123456789/a";
|
||||||
|
path::iterator second = std::next(p3.begin());
|
||||||
|
s = second->native();
|
||||||
|
p3 /= s;
|
||||||
|
VERIFY( p3.string() == "0/123456789/a/123456789" );
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
|
@ -119,4 +146,5 @@ main()
|
||||||
test02();
|
test02();
|
||||||
test03();
|
test03();
|
||||||
test04();
|
test04();
|
||||||
|
test05();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,36 @@ test01()
|
||||||
VERIFY( p.filename().string() == file );
|
VERIFY( p.filename().string() == file );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test02()
|
||||||
|
{
|
||||||
|
std::basic_string_view<path::value_type> s;
|
||||||
|
|
||||||
|
path p = "0/1/2/3/4/5/6";
|
||||||
|
// The string_view aliases the path's internal string:
|
||||||
|
s = p.native();
|
||||||
|
// 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" );
|
||||||
|
|
||||||
|
// 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/" );
|
||||||
|
|
||||||
|
// 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" );
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
test01();
|
test01();
|
||||||
|
test02();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue