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>
|
||||
|
||||
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
|
||||
preferred directory separators for normalized paths.
|
||||
* testsuite/27_io/filesystem/path/generation/relative.cc: Likewise.
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <locale>
|
||||
#include <iosfwd>
|
||||
#include <iomanip>
|
||||
|
@ -45,6 +44,7 @@
|
|||
#include <bits/locale_conv.h>
|
||||
#include <ext/concurrence.h>
|
||||
#include <bits/shared_ptr.h>
|
||||
#include <bits/unique_ptr.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
# define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1
|
||||
|
@ -169,12 +169,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
|
||||
path(const path& __p) = default;
|
||||
|
||||
path(path&& __p) noexcept
|
||||
: _M_pathname(std::move(__p._M_pathname)), _M_type(__p._M_type)
|
||||
{
|
||||
_M_split_cmpts();
|
||||
__p.clear();
|
||||
}
|
||||
path(path&& __p)
|
||||
#if _GLIBCXX_USE_CXX11_ABI || _GLIBCXX_FULLY_DYNAMIC_STRING == 0
|
||||
noexcept
|
||||
#endif
|
||||
: _M_pathname(std::move(__p._M_pathname)),
|
||||
_M_cmpts(std::move(__p._M_cmpts))
|
||||
{ __p.clear(); }
|
||||
|
||||
path(string_type&& __source, format = auto_format)
|
||||
: _M_pathname(std::move(__source))
|
||||
|
@ -213,8 +214,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
|
||||
// assignments
|
||||
|
||||
path& operator=(const path& __p) = default;
|
||||
path& operator=(path&& __p) noexcept;
|
||||
path& operator=(const path&);
|
||||
path& operator=(path&&) noexcept;
|
||||
path& operator=(string_type&& __source);
|
||||
path& assign(string_type&& __source);
|
||||
|
||||
|
@ -240,17 +241,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
template <class _Source>
|
||||
_Path<_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>
|
||||
_Path<_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>
|
||||
_Path<_InputIterator, _InputIterator>&
|
||||
append(_InputIterator __first, _InputIterator __last)
|
||||
{ return _M_append(path(__first, __last)); }
|
||||
{
|
||||
_M_append(_S_convert(__first, __last));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// concatenation
|
||||
|
||||
|
@ -271,12 +281,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
template<typename _Source>
|
||||
_Path<_Source>&
|
||||
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>
|
||||
_Path<_InputIterator, _InputIterator>&
|
||||
concat(_InputIterator __first, _InputIterator __last)
|
||||
{ return *this += _S_convert(__first, __last); }
|
||||
{
|
||||
_M_concat(_S_convert(__first, __last));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
|
||||
|
@ -402,30 +418,41 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
|
||||
private:
|
||||
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 };
|
||||
|
||||
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;
|
||||
|
||||
template<typename _CharT>
|
||||
struct _Cvt;
|
||||
|
||||
static string_type
|
||||
static basic_string_view<value_type>
|
||||
_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)
|
||||
{ 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>
|
||||
static string_type
|
||||
|
@ -440,8 +467,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
static string_type
|
||||
_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);
|
||||
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
|
||||
|
@ -469,27 +498,65 @@ _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)
|
||||
{
|
||||
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
||||
return __ch == L'/' || __ch == preferred_separator;
|
||||
#else
|
||||
return __ch == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
void _M_split_cmpts();
|
||||
void _M_trim();
|
||||
void _M_add_root_name(size_t __n);
|
||||
void _M_add_root_dir(size_t __pos);
|
||||
void _M_add_filename(size_t __pos, size_t __n);
|
||||
|
||||
_Type _M_type() const noexcept { return _M_cmpts.type(); }
|
||||
|
||||
string_type _M_pathname;
|
||||
|
||||
struct _Cmpt;
|
||||
using _List = _GLIBCXX_STD_C::vector<_Cmpt>;
|
||||
_List _M_cmpts; // empty unless _M_type == _Type::_Multi
|
||||
_Type _M_type = _Type::_Filename;
|
||||
|
||||
struct _List
|
||||
{
|
||||
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); }
|
||||
|
@ -605,8 +672,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
|
||||
struct path::_Cmpt : path
|
||||
{
|
||||
_Cmpt(string_type __s, _Type __t, size_t __pos)
|
||||
: path(std::move(__s), __t), _M_pos(__pos) { }
|
||||
_Cmpt(basic_string_view<value_type> __s, _Type __t, size_t __pos)
|
||||
: path(__s, __t), _M_pos(__pos) { }
|
||||
|
||||
_Cmpt() : _M_pos(-1) { }
|
||||
|
||||
|
@ -733,7 +800,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
private:
|
||||
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
|
||||
__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_cmpts = std::move(__p._M_cmpts);
|
||||
_M_type = __p._M_type;
|
||||
__p.clear();
|
||||
return *this;
|
||||
}
|
||||
|
@ -798,41 +864,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
path::assign(string_type&& __source)
|
||||
{ return *this = path(std::move(__source)); }
|
||||
|
||||
inline path&
|
||||
path::operator+=(const path& __p)
|
||||
{
|
||||
return operator+=(__p.native());
|
||||
}
|
||||
|
||||
inline path&
|
||||
path::operator+=(const string_type& __x)
|
||||
{
|
||||
_M_pathname += __x;
|
||||
_M_split_cmpts();
|
||||
_M_concat(__x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline path&
|
||||
path::operator+=(const value_type* __x)
|
||||
{
|
||||
_M_pathname += __x;
|
||||
_M_split_cmpts();
|
||||
_M_concat(__x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline path&
|
||||
path::operator+=(value_type __x)
|
||||
{
|
||||
_M_pathname += __x;
|
||||
_M_split_cmpts();
|
||||
_M_concat(basic_string_view<value_type>(&__x, 1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline path&
|
||||
path::operator+=(basic_string_view<value_type> __x)
|
||||
{
|
||||
_M_pathname.append(__x.data(), __x.size());
|
||||
_M_split_cmpts();
|
||||
_M_concat(__x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -858,7 +914,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
{
|
||||
_M_pathname.swap(__rhs._M_pathname);
|
||||
_M_cmpts.swap(__rhs._M_cmpts);
|
||||
std::swap(_M_type, __rhs._M_type);
|
||||
}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Allocator>
|
||||
|
@ -968,7 +1023,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
#endif
|
||||
string_type __str(__a);
|
||||
|
||||
if (_M_type == _Type::_Root_dir)
|
||||
if (_M_type() == _Type::_Root_dir)
|
||||
__str.assign(1, __slash);
|
||||
else
|
||||
{
|
||||
|
@ -979,7 +1034,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
if (__add_slash)
|
||||
__str += __slash;
|
||||
__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())
|
||||
return {};
|
||||
else if (_M_type == _Type::_Filename)
|
||||
else if (_M_type() == _Type::_Filename)
|
||||
return *this;
|
||||
else if (_M_type == _Type::_Multi)
|
||||
else if (_M_type() == _Type::_Multi)
|
||||
{
|
||||
if (_M_pathname.back() == preferred_separator)
|
||||
return {};
|
||||
auto& __last = *--end();
|
||||
if (__last._M_type == _Type::_Filename)
|
||||
if (__last._M_type() == _Type::_Filename)
|
||||
return __last;
|
||||
}
|
||||
return {};
|
||||
|
@ -1084,7 +1139,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
inline path::iterator
|
||||
path::begin() const
|
||||
{
|
||||
if (_M_type == _Type::_Multi)
|
||||
if (_M_type() == _Type::_Multi)
|
||||
return iterator(this, _M_cmpts.begin());
|
||||
return iterator(this, empty());
|
||||
}
|
||||
|
@ -1092,48 +1147,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
inline path::iterator
|
||||
path::end() const
|
||||
{
|
||||
if (_M_type == _Type::_Multi)
|
||||
if (_M_type() == _Type::_Multi)
|
||||
return iterator(this, _M_cmpts.end());
|
||||
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&
|
||||
path::iterator::operator++()
|
||||
{
|
||||
__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());
|
||||
++_M_cur;
|
||||
|
@ -1150,7 +1173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
path::iterator::operator--()
|
||||
{
|
||||
__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());
|
||||
--_M_cur;
|
||||
|
@ -1167,7 +1190,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
path::iterator::operator*() const
|
||||
{
|
||||
__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());
|
||||
return *_M_cur;
|
||||
|
@ -1182,7 +1205,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
|
|||
return false;
|
||||
if (_M_path == nullptr)
|
||||
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_at_end == __rhs._M_at_end;
|
||||
}
|
||||
|
|
|
@ -1244,6 +1244,77 @@ class StdExpPathPrinter:
|
|||
def children(self):
|
||||
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:
|
||||
"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::',
|
||||
'path', StdExpPathPrinter)
|
||||
libstdcxx_printer.add_version('std::filesystem::',
|
||||
'path', StdExpPathPrinter)
|
||||
'path', StdPathPrinter)
|
||||
libstdcxx_printer.add_version('std::filesystem::__cxx11::',
|
||||
'path', StdExpPathPrinter)
|
||||
'path', StdPathPrinter)
|
||||
|
||||
# C++17 components
|
||||
libstdcxx_printer.add_version('std::',
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -112,6 +112,33 @@ test04()
|
|||
#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
|
||||
main()
|
||||
{
|
||||
|
@ -119,4 +146,5 @@ main()
|
|||
test02();
|
||||
test03();
|
||||
test04();
|
||||
test05();
|
||||
}
|
||||
|
|
|
@ -57,8 +57,36 @@ test01()
|
|||
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
|
||||
main()
|
||||
{
|
||||
test01();
|
||||
test02();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue