]>
git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/filesystem/std-path.cc
1 // Class filesystem::path -*- C++ -*-
3 // Copyright (C) 2014-2019 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
30 // Interpret "//x" as a root-name, not root-dir + filename
31 # define SLASHSLASH_IS_ROOTNAME 1
36 #include <bits/stl_uninitialized.h>
38 namespace fs
= std::filesystem
;
41 static inline bool is_dir_sep(path::value_type ch
)
43 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
44 return ch
== L
'/' || ch
== path::preferred_separator
;
52 using string_view_type
= std::basic_string_view
<value_type
>;
57 _Type type
= _Type::_Multi
;
59 bool valid() const { return type
!= _Type::_Multi
; }
62 string_view_type input
;
63 string_view_type::size_type pos
= 0;
65 _Type last_type
= _Type::_Multi
;
67 _Parser(string_view_type s
, size_t o
= 0) : input(s
), origin(o
) { }
69 pair
<cmpt
, cmpt
> root_path() noexcept
72 pair
<cmpt
, cmpt
> root
;
74 const size_t len
= input
.size();
76 // look for root name or root directory
77 if (is_dir_sep(input
[0]))
79 #if SLASHSLASH_IS_ROOTNAME
80 // look for root name, such as "//foo"
81 if (len
> 2 && input
[1] == input
[0])
83 if (!is_dir_sep(input
[2]))
85 // got root name, find its end
87 while (pos
< len
&& !is_dir_sep(input
[pos
]))
89 root
.first
.str
= input
.substr(0, pos
);
90 root
.first
.type
= _Type::_Root_name
;
92 if (pos
< len
) // also got root directory
94 root
.second
.str
= input
.substr(pos
, 1);
95 root
.second
.type
= _Type::_Root_dir
;
101 // got something like "///foo" which is just a root directory
102 // composed of multiple redundant directory separators
103 root
.first
.str
= input
.substr(0, 1);
104 root
.first
.type
= _Type::_Root_dir
;
111 root
.first
.str
= input
.substr(0, 1);
112 root
.first
.type
= _Type::_Root_dir
;
115 // Find the start of the first filename
116 while (pos
< len
&& is_dir_sep(input
[pos
]))
119 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
120 else if (len
> 1 && input
[1] == L
':')
122 // got disk designator
123 root
.first
.str
= input
.substr(0, 2);
124 root
.first
.type
= _Type::_Root_name
;
125 if (len
> 2 && is_dir_sep(input
[2]))
127 root
.second
.str
= input
.substr(2, 1);
128 root
.second
.type
= _Type::_Root_dir
;
130 pos
= input
.find_first_not_of(L
"/\\", 2);
134 if (root
.second
.valid())
135 last_type
= root
.second
.type
;
137 last_type
= root
.first
.type
;
144 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
145 string_view_type sep
= L
"/\\";
150 const int last_pos
= pos
;
153 if (pos
!= input
.npos
)
155 pos
= input
.find_first_not_of(sep
, pos
);
156 if (pos
!= input
.npos
)
158 const auto end
= input
.find_first_of(sep
, pos
);
159 f
.str
= input
.substr(pos
, end
- pos
);
160 f
.type
= _Type::_Filename
;
163 else if (last_type
== _Type::_Filename
164 || (last_pos
== 0 && !input
.empty()))
166 // [fs.path.itr]/4 An empty element, if trailing non-root
167 // directory-separator present.
168 __glibcxx_assert(is_dir_sep(input
.back()));
169 f
.str
= input
.substr(input
.length(), 0);
170 f
.type
= _Type::_Filename
;
177 string_view_type::size_type
178 offset(const cmpt
& c
) const noexcept
179 { return origin
+ c
.str
.data() - input
.data(); }
182 struct path::_List::_Impl
184 using value_type
= _Cmpt
;
186 _Impl(int cap
) : _M_size(0), _M_capacity(cap
) { }
188 alignas(value_type
) int _M_size
;
191 using iterator
= value_type
*;
192 using const_iterator
= const value_type
*;
194 iterator
begin() { return reinterpret_cast<value_type
*>(this + 1); }
195 iterator
end() { return begin() + size(); }
197 const_iterator
begin() const
198 { return reinterpret_cast<const value_type
*>(this + 1); }
199 const_iterator
end() const { return begin() + size(); }
201 const value_type
& front() const { return *begin(); }
202 const value_type
& back() const { return end()[-1]; }
204 int size() const { return _M_size
; }
205 int capacity() const { return _M_capacity
; }
206 bool empty() const { return _M_size
== 0; }
208 void clear() { std::destroy_n(begin(), _M_size
); _M_size
= 0; }
210 void erase(const_iterator cpos
)
212 iterator pos
= begin() + (cpos
- begin());
213 if (pos
+ 1 != end())
214 std::move(pos
+ 1, end(), pos
);
219 void erase(const_iterator cfirst
, const_iterator clast
)
221 iterator first
= begin() + (cfirst
- begin());
222 iterator last
= begin() + (clast
- begin());
224 std::move(last
, end(), first
);
225 std::destroy(first
+ (end() - last
), end());
226 _M_size
-= last
- first
;
229 unique_ptr
<_Impl
, _Impl_deleter
> copy() const
231 const auto n
= size();
232 void* p
= ::operator new(sizeof(_Impl
) + n
* sizeof(value_type
));
233 unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new (p
) _Impl
{n
});
234 std::uninitialized_copy_n(begin(), n
, newptr
->begin());
239 // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
240 static _Impl
* notype(_Impl
* p
)
242 constexpr uintptr_t mask
= ~(uintptr_t)0x3;
243 return reinterpret_cast<_Impl
*>(reinterpret_cast<uintptr_t>(p
) & mask
);
247 void path::_List::_Impl_deleter::operator()(_Impl
* p
) const noexcept
249 p
= _Impl::notype(p
);
252 __glibcxx_assert(p
->_M_size
<= p
->_M_capacity
);
254 ::operator delete(p
, sizeof(*p
) + p
->_M_capacity
* sizeof(value_type
));
258 path::_List::_List() : _M_impl(reinterpret_cast<_Impl
*>(_Type::_Filename
)) { }
260 path::_List::_List(const _List
& other
)
263 _M_impl
= other
._M_impl
->copy();
269 path::_List::operator=(const _List
& other
)
273 // copy in-place if there is capacity
274 const int newsize
= other
._M_impl
->size();
275 auto impl
= _Impl::notype(_M_impl
.get());
276 if (impl
&& impl
->capacity() >= newsize
)
278 const int oldsize
= impl
->_M_size
;
279 auto to
= impl
->begin();
280 auto from
= other
._M_impl
->begin();
281 const int minsize
= std::min(newsize
, oldsize
);
282 for (int i
= 0; i
< minsize
; ++i
)
283 to
[i
]._M_pathname
.reserve(from
[i
]._M_pathname
.length());
284 if (newsize
> oldsize
)
286 std::uninitialized_copy_n(to
+ oldsize
, newsize
- oldsize
,
288 impl
->_M_size
= newsize
;
290 else if (newsize
< oldsize
)
291 impl
->erase(impl
->begin() + newsize
, impl
->end());
292 std::copy_n(from
, minsize
, to
);
296 _M_impl
= other
._M_impl
->copy();
307 path::_List::type(_Type t
) noexcept
309 auto val
= reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl
.release()));
310 _M_impl
.reset(reinterpret_cast<_Impl
*>(val
| (unsigned char)t
));
314 path::_List::size() const noexcept
316 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
322 path::_List::capacity() const noexcept
324 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
325 return ptr
->capacity();
330 path::_List::empty() const noexcept
336 path::_List::begin() noexcept
339 __glibcxx_assert(!empty());
340 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
346 path::_List::end() noexcept
349 __glibcxx_assert(!empty());
350 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
356 path::_List::begin() const noexcept
359 __glibcxx_assert(!empty());
360 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
366 path::_List::end() const noexcept
369 __glibcxx_assert(!empty());
370 if (auto* ptr
= _Impl::notype(_M_impl
.get()))
376 path::_List::front() noexcept
379 return *_M_impl
->begin();
383 path::_List::back() noexcept
386 return _M_impl
->begin()[_M_impl
->size() - 1];
390 path::_List::front() const noexcept
393 return *_M_impl
->begin();
397 path::_List::back() const noexcept
400 return _M_impl
->begin()[_M_impl
->size() - 1];
404 path::_List::erase(const_iterator pos
)
410 path::_List::erase(const_iterator first
, const_iterator last
)
412 _M_impl
->erase(first
, last
);
418 if (auto ptr
= _Impl::notype(_M_impl
.get()))
423 path::_List::reserve(int newcap
, bool exact
= false)
425 // __glibcxx_assert(type() == _Type::_Multi);
427 _Impl
* curptr
= _Impl::notype(_M_impl
.get());
429 int curcap
= curptr
? curptr
->capacity() : 0;
433 if (!exact
&& newcap
< int(1.5 * curcap
))
434 newcap
= 1.5 * curcap
;
436 void* p
= ::operator new(sizeof(_Impl
) + newcap
* sizeof(value_type
));
437 std::unique_ptr
<_Impl
, _Impl_deleter
> newptr(::new(p
) _Impl
{newcap
});
438 const int cursize
= curptr
? curptr
->size() : 0;
441 std::uninitialized_move_n(curptr
->begin(), cursize
, newptr
->begin());
442 newptr
->_M_size
= cursize
;
444 std::swap(newptr
, _M_impl
);
449 path::operator=(const path
& p
)
451 _M_pathname
.reserve(p
._M_pathname
.length());
452 _M_cmpts
= p
._M_cmpts
; // might throw
453 _M_pathname
= p
._M_pathname
; // won't throw because we reserved enough space
458 path::operator/=(const path
& __p
)
460 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
461 if (__p
.is_absolute()
462 || (__p
.has_root_name() && __p
.root_name() != root_name()))
463 return operator=(__p
);
465 basic_string_view
<value_type
> __lhs
= _M_pathname
;
466 bool __add_sep
= false;
468 if (__p
.has_root_directory())
470 // Remove any root directory and relative path
471 if (_M_type() != _Type::_Root_name
)
473 if (!_M_cmpts
.empty()
474 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
475 __lhs
= _M_cmpts
.front()._M_pathname
;
480 else if (has_filename() || (!has_root_directory() && is_absolute()))
483 basic_string_view
<value_type
> __rhs
= __p
._M_pathname
;
484 // Omit any root-name from the generic format pathname:
485 if (__p
._M_type() == _Type::_Root_name
)
487 else if (!__p
._M_cmpts
.empty()
488 && __p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
489 __rhs
.remove_prefix(__p
._M_cmpts
.front()._M_pathname
.size());
491 const size_t __len
= __lhs
.size() + (int)__add_sep
+ __rhs
.size();
492 const int __maxcmpts
= _M_cmpts
.size() + __p
._M_cmpts
.size();
493 if (_M_pathname
.capacity() < __len
|| _M_cmpts
.capacity() < __maxcmpts
)
495 // Construct new path and swap (strong exception-safety guarantee).
497 __tmp
.reserve(__len
);
500 __tmp
+= preferred_separator
;
502 path __newp
= std::move(__tmp
);
509 _M_pathname
+= preferred_separator
;
510 _M_pathname
+= __rhs
;
519 // try to restore original state
520 _M_pathname
.resize(__lhs
.length());
525 // give up, basic exception safety guarantee only:
527 __throw_exception_again
;
532 // POSIX version is simpler than the specification in the standard,
533 // as any path with root-name or root-dir is absolute.
535 if (__p
.is_absolute() || this->empty())
537 return operator=(__p
);
540 using string_view_type
= basic_string_view
<value_type
>;
542 string_view_type sep
;
544 sep
= { &preferred_separator
, 1 }; // need to add a separator
545 #if SLASHSLASH_IS_ROOTNAME
546 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
547 sep
= { &preferred_separator
, 1 }; // need to add a separator
549 else if (__p
.empty())
550 return *this; // nothing to do
552 const auto orig_pathlen
= _M_pathname
.length();
553 const auto orig_size
= _M_cmpts
.size();
554 const auto orig_type
= _M_type();
557 if (_M_type() == _Type::_Multi
)
558 capacity
+= _M_cmpts
.size();
561 if (__p
._M_type() == _Type::_Multi
)
562 capacity
+= __p
._M_cmpts
.size();
563 else if (!__p
.empty() || !sep
.empty())
565 #if SLASHSLASH_IS_ROOTNAME
566 if (orig_type
== _Type::_Root_name
)
567 ++capacity
; // Need to insert root-directory after root-name
570 if (orig_type
== _Type::_Multi
)
572 const int curcap
= _M_cmpts
._M_impl
->capacity();
573 if (capacity
> curcap
)
574 capacity
= std::max(capacity
, (int) (curcap
* 1.5));
577 _M_pathname
.reserve(_M_pathname
.length() + sep
.length()
578 + __p
._M_pathname
.length());
583 const auto basepos
= _M_pathname
.length();
584 _M_pathname
+= __p
.native();
586 _M_cmpts
.type(_Type::_Multi
);
587 _M_cmpts
.reserve(capacity
);
588 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
590 if (orig_type
== _Type::_Multi
)
592 // Remove empty final component
593 if (_M_cmpts
._M_impl
->back().empty())
594 _M_cmpts
._M_impl
->erase(--output
);
596 else if (orig_pathlen
!= 0)
598 // Create single component from original path
599 string_view_type
s(_M_pathname
.data(), orig_pathlen
);
600 ::new(output
++) _Cmpt(s
, orig_type
, 0);
601 ++_M_cmpts
._M_impl
->_M_size
;
602 #if SLASHSLASH_IS_ROOTNAME
603 if (orig_type
== _Type::_Root_name
)
605 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
606 orig_pathlen
+ sep
.length());
607 ++_M_cmpts
._M_impl
->_M_size
;
612 if (__p
._M_type() == _Type::_Multi
)
614 for (auto& c
: *__p
._M_cmpts
._M_impl
)
616 ::new(output
++) _Cmpt(c
._M_pathname
, _Type::_Filename
,
618 ++_M_cmpts
._M_impl
->_M_size
;
621 else if (!__p
.empty() || !sep
.empty())
623 __glibcxx_assert(__p
._M_type() == _Type::_Filename
);
624 ::new(output
) _Cmpt(__p
._M_pathname
, __p
._M_type(), basepos
);
625 ++_M_cmpts
._M_impl
->_M_size
;
630 _M_pathname
.resize(orig_pathlen
);
631 if (orig_type
== _Type::_Multi
)
632 _M_cmpts
.erase(_M_cmpts
.begin() + orig_size
, _M_cmpts
.end());
635 _M_cmpts
.type(orig_type
);
636 __throw_exception_again
;
644 path::_M_append(basic_string_view
<value_type
> s
)
647 auto root_path
= parser
.root_path();
649 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
650 bool is_absolute
= root_path
.second
.type
== _Type::_Root_dir
;
651 bool has_root_name
= root_path
.first
.type
== _Type::_Root_name
;
652 if (is_absolute
|| (has_root_name
&& root_path
.first
.str
!= root_name()))
658 basic_string_view
<value_type
> lhs
= _M_pathname
;
659 bool add_sep
= false;
661 bool has_root_directory
= root_path
.first
.type
== _Type::_Root_dir
662 || root_path
.second
.type
== _Type::_Root_dir
;
664 if (has_root_directory
)
666 // Remove any root directory and relative path
667 if (_M_type() != _Type::_Root_name
)
669 if (!_M_cmpts
.empty()
670 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
671 lhs
= _M_cmpts
.front()._M_pathname
;
676 else if (has_filename() || (!has_root_directory
&& is_absolute
))
679 basic_string_view
<value_type
> rhs
= s
;
680 // Omit any root-name from the generic format pathname:
682 rhs
.remove_prefix(root_path
.first
.str
.length());
684 // Construct new path and swap (strong exception-safety guarantee).
686 tmp
.reserve(lhs
.size() + (int)add_sep
+ rhs
.size());
689 tmp
+= preferred_separator
;
691 path newp
= std::move(tmp
);
695 bool is_absolute
= root_path
.first
.type
== _Type::_Root_dir
696 || root_path
.second
.type
== _Type::_Root_dir
;
697 if (is_absolute
|| this->empty())
703 const auto orig_pathlen
= _M_pathname
.length();
704 const auto orig_size
= _M_cmpts
.size();
705 const auto orig_type
= _M_type();
707 basic_string_view
<value_type
> sep
;
709 sep
= { &preferred_separator
, 1 }; // need to add a separator
710 #if SLASHSLASH_IS_ROOTNAME
711 else if (_M_type() == _Type::_Root_name
) // root-name with no root-dir
712 sep
= { &preferred_separator
, 1 }; // need to add a separator
715 return; // nothing to do
717 // Copy the input into _M_pathname:
719 _M_pathname
.insert(orig_pathlen
, sep
);
720 // Update s to refer to the new copy (this ensures s is not a dangling
721 // reference to deallocated characters, in the case where it was referring
722 // into _M_pathname or a member of _M_cmpts).
724 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
725 s
.remove_prefix(orig_pathlen
+ sep
.length());
727 parser
.input
= s
; // reset parser to use updated string view
728 const auto basepos
= orig_pathname
.length() + sep
.length();
729 parser
.origin
= basepos
;
731 std::array
<_Parser::cmpt
, 64> buf
;
732 auto next
= buf
.begin();
735 if (_M_type() == _Type::_Multi
)
736 capacity
+= _M_cmpts
.size();
740 auto cmpt
= parser
.next();
746 cmpt
= parser
.next();
748 while (cmpt
.valid() && next
!= buf
.end());
750 capacity
+= next
- buf
.begin();
751 if (cmpt
.valid()) // filled buffer before parsing whole input
754 _Parser
parser2(parser
);
755 while (parser2
.next().valid())
759 else if (!sep
.empty())
762 #if SLASHSLASH_IS_ROOTNAME
763 if (orig_type
== _Type::_Root_name
)
764 ++capacity
; // Need to insert root-directory after root-name
769 _M_cmpts
.type(_Type::_Multi
);
770 _M_cmpts
.reserve(capacity
);
771 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
773 if (orig_type
== _Type::_Multi
)
775 // Remove empty final component
776 if (_M_cmpts
._M_impl
->back().empty())
777 _M_cmpts
._M_impl
->erase(--output
);
779 else if (orig_pathlen
!= 0)
781 // Create single component from original path
782 ::new(output
++) _Cmpt(orig_pathname
, orig_type
, 0);
783 ++_M_cmpts
._M_impl
->_M_size
;
785 #if SLASHSLASH_IS_ROOTNAME
786 if (!sep
.empty() && orig_type
== _Type::_Root_name
)
788 ::new(output
++) _Cmpt(sep
, _Type::_Root_dir
,
789 orig_pathlen
+ sep
.length());
790 ++_M_cmpts
._M_impl
->_M_size
;
795 if (next
!= buf
.begin())
797 for (auto it
= buf
.begin(); it
!= next
; ++it
)
800 ::new(output
++) _Cmpt(c
.str
, c
.type
, parser
.offset(c
));
801 ++_M_cmpts
._M_impl
->_M_size
;
805 ::new(output
++) _Cmpt(cmpt
.str
, cmpt
.type
, parser
.offset(cmpt
));
806 ++_M_cmpts
._M_impl
->_M_size
;
807 cmpt
= parser
.next();
810 else if (!sep
.empty())
812 // Empty filename at the end:
813 ::new(output
) _Cmpt({}, _Type::_Filename
, basepos
);
814 ++_M_cmpts
._M_impl
->_M_size
;
819 _M_pathname
.resize(orig_pathlen
);
820 if (orig_type
== _Type::_Multi
)
821 _M_cmpts
.erase(_M_cmpts
.begin() + orig_size
, _M_cmpts
.end());
824 _M_cmpts
.type(orig_type
);
825 __throw_exception_again
;
832 path::operator+=(const path
& p
)
843 const auto orig_pathlen
= _M_pathname
.length();
844 const auto orig_type
= _M_type();
845 const auto orig_size
= _M_cmpts
.size();
846 int orig_filenamelen
= -1;
847 basic_string_view
<value_type
> extra
;
849 // Ensure that '_M_pathname += p._M_pathname' won't throw:
850 _M_pathname
.reserve(orig_pathlen
+ p
._M_pathname
.length());
854 _Cmpt
* last
= nullptr;
855 if (p
._M_type() == _Type::_Multi
)
857 it
= p
._M_cmpts
._M_impl
->begin();
858 last
= p
._M_cmpts
._M_impl
->end();
862 c
= _Cmpt(p
._M_pathname
, p
._M_type(), 0);
867 if (it
->_M_type() == _Type::_Filename
)
869 // See if there's a filename or root-name at the end of the original path
870 // that we can add to.
871 if (_M_type() == _Type::_Filename
872 #if SLASHSLASH_IS_ROOTNAME
873 || _M_type() == _Type::_Root_name
877 if (p
._M_type() == _Type::_Filename
)
879 // Simplest case where we just add the whole of p to the
881 _M_pathname
+= p
._M_pathname
;
884 // Only the first component of s should be appended, do so below:
885 extra
= it
->_M_pathname
;
888 else if (_M_type() == _Type::_Multi
889 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
891 auto& back
= _M_cmpts
.back();
892 if (p
._M_type() == _Type::_Filename
)
894 basic_string_view
<value_type
> s
= p
._M_pathname
;
895 back
._M_pathname
+= s
;
900 orig_filenamelen
= back
._M_pathname
.length();
901 back
._M_pathname
+= it
->_M_pathname
;
902 extra
= it
->_M_pathname
;
906 else if (is_dir_sep(_M_pathname
.back()) && _M_type() == _Type::_Multi
907 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
908 orig_filenamelen
= 0; // current path has empty filename at end
911 if (_M_type() == _Type::_Multi
)
912 capacity
+= _M_cmpts
.size();
915 if (p
._M_type() == _Type::_Multi
)
916 capacity
+= p
._M_cmpts
.size();
922 _M_cmpts
.type(_Type::_Multi
);
923 _M_cmpts
.reserve(capacity
);
924 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
926 if (orig_type
!= _Type::_Multi
)
928 // Create single component from original path
929 auto ptr
= ::new(output
++) _Cmpt({}, orig_type
, 0);
930 ++_M_cmpts
._M_impl
->_M_size
;
931 ptr
->_M_pathname
.reserve(_M_pathname
.length() + extra
.length());
932 ptr
->_M_pathname
= _M_pathname
;
933 ptr
->_M_pathname
+= extra
;
935 #if SLASHSLASH_IS_ROOTNAME
936 if (orig_type
== _Type::_Root_name
)
938 basic_string_view
<value_type
> s(p
._M_pathname
);
939 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
940 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
941 ++_M_cmpts
._M_impl
->_M_size
;
945 else if (orig_filenamelen
== 0 && it
!= last
)
947 // Remove empty filename at end of original path.
948 _M_cmpts
.erase(std::prev(output
));
951 if (it
!= last
&& it
->_M_type() == _Type::_Root_name
)
953 basic_string_view
<value_type
> s
= it
->_M_pathname
;
954 auto pos
= orig_pathlen
;
955 #if SLASHSLASH_IS_ROOTNAME
959 ::new(output
++) _Cmpt(s
, _Type::_Filename
, pos
);
960 ++_M_cmpts
._M_impl
->_M_size
;
964 if (it
!= last
&& it
->_M_type() == _Type::_Root_dir
)
969 // This root-dir becomes a trailing slash
970 auto pos
= _M_pathname
.length() + p
._M_pathname
.length();
971 ::new(output
++) _Cmpt({}, _Type::_Filename
, pos
);
972 ++_M_cmpts
._M_impl
->_M_size
;
978 auto pos
= it
->_M_pos
+ orig_pathlen
;
979 ::new(output
++) _Cmpt(it
->_M_pathname
, _Type::_Filename
, pos
);
980 ++_M_cmpts
._M_impl
->_M_size
;
984 _M_pathname
+= p
._M_pathname
;
986 if (is_dir_sep(_M_pathname
.back()))
988 ::new(output
++) _Cmpt({}, _Type::_Filename
, _M_pathname
.length());
989 ++_M_cmpts
._M_impl
->_M_size
;
994 _M_pathname
.resize(orig_pathlen
);
995 if (orig_type
== _Type::_Multi
)
997 if (_M_cmpts
.size() > orig_size
)
998 _M_cmpts
.erase(_M_cmpts
.begin() + orig_size
, _M_cmpts
.end());
999 if (orig_filenamelen
!= -1)
1001 if (_M_cmpts
.size() == orig_size
)
1003 auto& back
= _M_cmpts
.back();
1004 back
._M_pathname
.resize(orig_filenamelen
);
1005 if (orig_filenamelen
== 0)
1006 back
._M_pos
= orig_pathlen
;
1010 auto output
= _M_cmpts
._M_impl
->end();
1011 ::new(output
) _Cmpt({}, _Type::_Filename
, orig_pathlen
);
1012 ++_M_cmpts
._M_impl
->_M_size
;
1018 _M_cmpts
.type(orig_type
);
1019 __throw_exception_again
;
1026 path::_M_concat(basic_string_view
<value_type
> s
)
1037 const auto orig_pathlen
= _M_pathname
.length();
1038 const auto orig_type
= _M_type();
1039 const auto orig_size
= _M_cmpts
.size();
1040 int orig_filenamelen
= -1;
1041 basic_string_view
<value_type
> extra
;
1043 // Copy the input into _M_pathname:
1045 // Update s to refer to the new copy (this ensures s is not a dangling
1046 // reference to deallocated characters, in the case where it was referring
1047 // into _M_pathname or a member of _M_cmpts).
1049 const auto orig_pathname
= s
.substr(0, orig_pathlen
);
1050 s
.remove_prefix(orig_pathlen
);
1052 _Parser
parser(s
, orig_pathlen
);
1053 auto cmpt
= parser
.next();
1055 if (cmpt
.str
.data() == s
.data())
1057 // See if there's a filename or root-name at the end of the original path
1058 // that we can add to.
1059 if (_M_type() == _Type::_Filename
1060 #if SLASHSLASH_IS_ROOTNAME
1061 || _M_type() == _Type::_Root_name
1065 if (cmpt
.str
.length() == s
.length())
1067 // Simplest case where we just need to add the whole of s
1068 // to the original path, which was already done above.
1071 // Only the first component of s should be appended, do so below:
1073 cmpt
= {}; // so we don't process it again
1075 else if (_M_type() == _Type::_Multi
1076 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1078 auto& back
= _M_cmpts
.back();
1079 if (cmpt
.str
.length() == s
.length())
1081 back
._M_pathname
+= s
;
1085 orig_filenamelen
= back
._M_pathname
.length();
1086 back
._M_pathname
+= cmpt
.str
;
1091 else if (is_dir_sep(orig_pathname
.back()) && _M_type() == _Type::_Multi
1092 && _M_cmpts
.back()._M_type() == _Type::_Filename
)
1093 orig_filenamelen
= 0; // original path had empty filename at end
1095 std::array
<_Parser::cmpt
, 64> buf
;
1096 auto next
= buf
.begin();
1101 cmpt
= parser
.next();
1102 while (cmpt
.valid() && next
!= buf
.end())
1105 cmpt
= parser
.next();
1109 if (_M_type() == _Type::_Multi
)
1110 capacity
+= _M_cmpts
.size();
1114 capacity
+= next
- buf
.begin();
1116 if (cmpt
.valid()) // filled buffer before parsing whole input
1119 _Parser
parser2(parser
);
1120 while (parser2
.next().valid())
1124 #if SLASHSLASH_IS_ROOTNAME
1125 if (orig_type
== _Type::_Root_name
)
1126 ++capacity
; // Need to insert root-directory after root-name
1131 _M_cmpts
.type(_Type::_Multi
);
1132 _M_cmpts
.reserve(capacity
);
1133 _Cmpt
* output
= _M_cmpts
._M_impl
->end();
1134 auto it
= buf
.begin();
1136 if (orig_type
!= _Type::_Multi
)
1138 // Create single component from original path
1139 auto p
= ::new(output
++) _Cmpt({}, orig_type
, 0);
1140 ++_M_cmpts
._M_impl
->_M_size
;
1141 p
->_M_pathname
.reserve(orig_pathname
.length() + extra
.length());
1142 p
->_M_pathname
= orig_pathname
;
1143 p
->_M_pathname
+= extra
;
1145 #if SLASHSLASH_IS_ROOTNAME
1146 if (orig_type
== _Type::_Root_name
)
1148 ::new(output
++) _Cmpt(s
.substr(extra
.length(), 1),
1149 _Type::_Root_dir
, orig_pathlen
+ extra
.length());
1150 ++_M_cmpts
._M_impl
->_M_size
;
1154 else if (orig_filenamelen
== 0 && extra
.empty())
1156 // Replace empty filename at end of original path.
1157 std::prev(output
)->_M_pathname
= it
->str
;
1158 std::prev(output
)->_M_pos
= parser
.offset(*it
);
1164 ::new(output
++) _Cmpt(it
->str
, _Type::_Filename
, parser
.offset(*it
));
1165 ++_M_cmpts
._M_impl
->_M_size
;
1169 if (next
== buf
.end())
1171 while (cmpt
.valid())
1173 auto pos
= parser
.offset(cmpt
);
1174 ::new(output
++) _Cmpt(cmpt
.str
, _Type::_Filename
, pos
);
1175 ++_M_cmpts
._M_impl
->_M_size
;
1176 cmpt
= parser
.next();
1182 _M_pathname
.resize(orig_pathlen
);
1183 if (orig_type
== _Type::_Multi
)
1185 _M_cmpts
.erase(_M_cmpts
.begin() + orig_size
, _M_cmpts
.end());
1186 if (orig_filenamelen
!= -1)
1188 auto& back
= _M_cmpts
.back();
1189 back
._M_pathname
.resize(orig_filenamelen
);
1190 if (orig_filenamelen
== 0)
1191 back
._M_pos
= orig_pathlen
;
1196 _M_cmpts
.type(orig_type
);
1197 __throw_exception_again
;
1202 path::remove_filename()
1204 if (_M_type() == _Type::_Multi
)
1206 if (!_M_cmpts
.empty())
1208 auto cmpt
= std::prev(_M_cmpts
.end());
1209 if (cmpt
->_M_type() == _Type::_Filename
&& !cmpt
->empty())
1211 _M_pathname
.erase(cmpt
->_M_pos
);
1212 auto prev
= std::prev(cmpt
);
1213 if (prev
->_M_type() == _Type::_Root_dir
1214 || prev
->_M_type() == _Type::_Root_name
)
1216 _M_cmpts
.erase(cmpt
);
1217 if (_M_cmpts
.size() == 1)
1219 _M_cmpts
.type(_M_cmpts
.front()._M_type());
1228 else if (_M_type() == _Type::_Filename
)
1234 path::replace_filename(const path
& replacement
)
1237 operator/=(replacement
);
1241 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1242 const fs::path::value_type dot
= L
'.';
1244 const fs::path::value_type dot
= '.';
1248 path::replace_extension(const path
& replacement
)
1250 auto ext
= _M_find_extension();
1251 // Any existing extension() is removed
1252 if (ext
.first
&& ext
.second
!= string_type::npos
)
1254 if (ext
.first
== &_M_pathname
)
1255 _M_pathname
.erase(ext
.second
);
1258 const auto& back
= _M_cmpts
.back();
1259 if (ext
.first
!= &back
._M_pathname
)
1260 _GLIBCXX_THROW_OR_ABORT(
1261 std::logic_error("path::replace_extension failed"));
1262 _M_pathname
.erase(back
._M_pos
+ ext
.second
);
1265 // If replacement is not empty and does not begin with a dot character,
1266 // a dot character is appended
1267 if (!replacement
.empty() && replacement
.native()[0] != dot
)
1269 operator+=(replacement
);
1274 path::compare(const path
& p
) const noexcept
1276 if (_M_pathname
== p
._M_pathname
)
1279 basic_string_view
<value_type
> lroot
, rroot
;
1280 if (_M_type() == _Type::_Root_name
)
1281 lroot
= _M_pathname
;
1282 else if (_M_type() == _Type::_Multi
1283 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1284 lroot
= _M_cmpts
.front()._M_pathname
;
1285 if (p
._M_type() == _Type::_Root_name
)
1286 rroot
= p
._M_pathname
;
1287 else if (p
._M_type() == _Type::_Multi
1288 && p
._M_cmpts
.front()._M_type() == _Type::_Root_name
)
1289 rroot
= p
._M_cmpts
.front()._M_pathname
;
1290 if (int rootNameComparison
= lroot
.compare(rroot
))
1291 return rootNameComparison
;
1293 if (!this->has_root_directory() && p
.has_root_directory())
1295 else if (this->has_root_directory() && !p
.has_root_directory())
1298 using Iterator
= const _Cmpt
*;
1299 Iterator begin1
, end1
, begin2
, end2
;
1300 if (_M_type() == _Type::_Multi
)
1302 begin1
= _M_cmpts
.begin();
1303 end1
= _M_cmpts
.end();
1304 // Find start of this->relative_path()
1305 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1309 begin1
= end1
= nullptr;
1311 if (p
._M_type() == _Type::_Multi
)
1313 begin2
= p
._M_cmpts
.begin();
1314 end2
= p
._M_cmpts
.end();
1315 // Find start of p.relative_path()
1316 while (begin2
!= end2
&& begin2
->_M_type() != _Type::_Filename
)
1320 begin2
= end2
= nullptr;
1322 if (_M_type() == _Type::_Filename
)
1324 if (p
._M_type() == _Type::_Filename
)
1325 return native().compare(p
.native());
1326 else if (begin2
!= end2
)
1328 if (int ret
= native().compare(begin2
->native()))
1331 return ++begin2
== end2
? 0 : -1;
1336 else if (p
._M_type() == _Type::_Filename
)
1340 if (int ret
= begin1
->native().compare(p
.native()))
1343 return ++begin1
== end1
? 0 : +1;
1350 while (begin1
!= end1
&& begin2
!= end2
)
1352 if (int i
= begin1
->native().compare(begin2
->native()))
1368 path::compare(basic_string_view
<value_type
> s
) const noexcept
1370 if (_M_pathname
== s
)
1375 basic_string_view
<value_type
> lroot
, rroot
;
1376 if (_M_type() == _Type::_Root_name
)
1377 lroot
= _M_pathname
;
1378 else if (_M_type() == _Type::_Multi
1379 && _M_cmpts
.front()._M_type() == _Type::_Root_name
)
1380 lroot
= _M_cmpts
.front()._M_pathname
;
1381 auto root_path
= parser
.root_path();
1382 if (root_path
.first
.type
== _Type::_Root_name
)
1383 rroot
= root_path
.first
.str
;
1384 if (int rootNameComparison
= lroot
.compare(rroot
))
1385 return rootNameComparison
;
1387 const bool has_root_dir
= root_path
.first
.type
== _Type::_Root_dir
1388 || root_path
.second
.type
== _Type::_Root_dir
;
1389 if (!this->has_root_directory() && has_root_dir
)
1391 else if (this->has_root_directory() && !has_root_dir
)
1394 using Iterator
= const _Cmpt
*;
1395 Iterator begin1
, end1
;
1396 if (_M_type() == _Type::_Filename
)
1398 auto cmpt
= parser
.next();
1401 if (int ret
= this->native().compare(cmpt
.str
))
1403 return parser
.next().valid() ? -1 : 0;
1408 else if (_M_type() == _Type::_Multi
)
1410 begin1
= _M_cmpts
.begin();
1411 end1
= _M_cmpts
.end();
1412 while (begin1
!= end1
&& begin1
->_M_type() != _Type::_Filename
)
1416 begin1
= end1
= nullptr;
1419 auto cmpt
= parser
.next();
1420 while (begin1
!= end1
&& cmpt
.valid())
1422 if (int i
= begin1
->native().compare(cmpt
.str
))
1425 cmpt
= parser
.next();
1438 path::root_name() const
1441 if (_M_type() == _Type::_Root_name
)
1443 else if (_M_cmpts
.size() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1444 __ret
= *_M_cmpts
.begin();
1449 path::root_directory() const
1452 if (_M_type() == _Type::_Root_dir
)
1454 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1455 __ret
._M_pathname
.assign(1, preferred_separator
);
1457 else if (!_M_cmpts
.empty())
1459 auto __it
= _M_cmpts
.begin();
1460 if (__it
->_M_type() == _Type::_Root_name
)
1462 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1469 path::root_path() const
1472 if (_M_type() == _Type::_Root_name
)
1474 else if (_M_type() == _Type::_Root_dir
)
1476 __ret
._M_pathname
.assign(1, preferred_separator
);
1477 __ret
._M_cmpts
.type(_Type::_Root_dir
);
1479 else if (!_M_cmpts
.empty())
1481 auto __it
= _M_cmpts
.begin();
1482 if (__it
->_M_type() == _Type::_Root_name
)
1485 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1488 else if (__it
->_M_type() == _Type::_Root_dir
)
1495 path::relative_path() const
1498 if (_M_type() == _Type::_Filename
)
1500 else if (!_M_cmpts
.empty())
1502 auto __it
= _M_cmpts
.begin();
1503 if (__it
->_M_type() == _Type::_Root_name
)
1505 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1507 if (__it
!= _M_cmpts
.end())
1508 __ret
.assign(_M_pathname
.substr(__it
->_M_pos
));
1514 path::parent_path() const
1517 if (!has_relative_path())
1519 else if (_M_cmpts
.size() >= 2)
1521 for (auto __it
= _M_cmpts
.begin(), __end
= std::prev(_M_cmpts
.end());
1522 __it
!= __end
; ++__it
)
1531 path::has_root_name() const
1533 if (_M_type() == _Type::_Root_name
)
1535 if (!_M_cmpts
.empty() && _M_cmpts
.begin()->_M_type() == _Type::_Root_name
)
1541 path::has_root_directory() const
1543 if (_M_type() == _Type::_Root_dir
)
1545 if (!_M_cmpts
.empty())
1547 auto __it
= _M_cmpts
.begin();
1548 if (__it
->_M_type() == _Type::_Root_name
)
1550 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1557 path::has_root_path() const
1559 if (_M_type() == _Type::_Root_name
|| _M_type() == _Type::_Root_dir
)
1561 if (!_M_cmpts
.empty())
1563 auto __type
= _M_cmpts
.front()._M_type();
1564 if (__type
== _Type::_Root_name
|| __type
== _Type::_Root_dir
)
1571 path::has_relative_path() const
1573 if (_M_type() == _Type::_Filename
&& !_M_pathname
.empty())
1575 if (!_M_cmpts
.empty())
1577 auto __it
= _M_cmpts
.begin();
1578 if (__it
->_M_type() == _Type::_Root_name
)
1580 if (__it
!= _M_cmpts
.end() && __it
->_M_type() == _Type::_Root_dir
)
1582 if (__it
!= _M_cmpts
.end() && !__it
->_M_pathname
.empty())
1590 path::has_parent_path() const
1592 if (!has_relative_path())
1594 return _M_cmpts
.size() >= 2;
1598 path::has_filename() const
1602 if (_M_type() == _Type::_Filename
)
1603 return !_M_pathname
.empty();
1604 if (_M_type() == _Type::_Multi
)
1606 if (_M_pathname
.back() == preferred_separator
)
1608 return _M_cmpts
.back().has_filename();
1615 inline bool is_dot(fs::path::value_type c
) { return c
== dot
; }
1617 inline bool is_dot(const fs::path
& path
)
1619 const auto& filename
= path
.native();
1620 return filename
.size() == 1 && is_dot(filename
[0]);
1623 inline bool is_dotdot(const fs::path
& path
)
1625 const auto& filename
= path
.native();
1626 return filename
.size() == 2 && is_dot(filename
[0]) && is_dot(filename
[1]);
1631 path::lexically_normal() const
1634 C++17 [fs.path.generic] p6
1635 - If the path is empty, stop.
1636 - Replace each slash character in the root-name with a preferred-separator.
1637 - Replace each directory-separator with a preferred-separator.
1638 - Remove each dot filename and any immediately following directory-separator.
1639 - As long as any appear, remove a non-dot-dot filename immediately followed
1640 by a directory-separator and a dot-dot filename, along with any immediately
1641 following directory-separator.
1642 - If there is a root-directory, remove all dot-dot filenames and any
1643 directory-separators immediately following them.
1644 - If the last filename is dot-dot, remove any trailing directory-separator.
1645 - If the path is empty, add a dot.
1648 // If the path is empty, stop.
1651 for (auto& p
: *this)
1653 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1654 // Replace each slash character in the root-name
1655 if (p
._M_type() == _Type::_Root_name
|| p
._M_type() == _Type::_Root_dir
)
1657 string_type s
= p
.native();
1658 std::replace(s
.begin(), s
.end(), L
'/', L
'\\');
1665 if (ret
.has_filename())
1667 // remove a non-dot-dot filename immediately followed by /..
1668 if (!is_dotdot(ret
.filename()))
1669 ret
.remove_filename();
1673 else if (!ret
.has_relative_path())
1675 // remove a dot-dot filename immediately after root-directory
1676 if (!ret
.has_root_directory())
1681 // Got a path with a relative path (i.e. at least one non-root
1682 // element) and no filename at the end (i.e. empty last element),
1683 // so must have a trailing slash. See what is before it.
1684 auto elem
= std::prev(ret
.end(), 2);
1685 if (elem
->has_filename() && !is_dotdot(*elem
))
1687 // Remove the filename before the trailing slash
1688 // (equiv. to ret = ret.parent_path().remove_filename())
1690 if (elem
== ret
.begin())
1694 ret
._M_pathname
.erase(elem
._M_cur
->_M_pos
);
1695 // Do we still have a trailing slash?
1696 if (std::prev(elem
)->_M_type() == _Type::_Filename
)
1697 ret
._M_cmpts
.erase(elem
._M_cur
);
1699 ret
._M_cmpts
.erase(elem
._M_cur
, ret
._M_cmpts
.end());
1708 #if SLASHSLASH_IS_ROOTNAME
1709 else if (p
._M_type() == _Type::_Root_dir
)
1710 ret
+= '/'; // using operator/=('/') would replace whole of ret
1716 if (ret
._M_cmpts
.size() >= 2)
1718 auto back
= std::prev(ret
.end());
1719 // If the last filename is dot-dot, ...
1720 if (back
->empty() && is_dotdot(*std::prev(back
)))
1721 // ... remove any trailing directory-separator.
1722 ret
= ret
.parent_path();
1724 // If the path is empty, add a dot.
1725 else if (ret
.empty())
1732 path::lexically_relative(const path
& base
) const
1735 if (root_name() != base
.root_name())
1737 if (is_absolute() != base
.is_absolute())
1739 if (!has_root_directory() && base
.has_root_directory())
1741 auto [a
, b
] = std::mismatch(begin(), end(), base
.begin(), base
.end());
1742 if (a
== end() && b
== base
.end())
1747 for (; b
!= base
.end(); ++b
)
1752 else if (!p
.empty() && !is_dot(p
))
1755 if (n
== 0 && (a
== end() || a
->empty()))
1759 const path
dotdot("..");
1762 for (; a
!= end(); ++a
)
1770 path::lexically_proximate(const path
& base
) const
1772 path rel
= lexically_relative(base
);
1778 std::pair
<const path::string_type
*, std::size_t>
1779 path::_M_find_extension() const
1781 const string_type
* s
= nullptr;
1783 if (_M_type() == _Type::_Filename
)
1785 else if (_M_type() == _Type::_Multi
&& !_M_cmpts
.empty())
1787 const auto& c
= _M_cmpts
.back();
1788 if (c
._M_type() == _Type::_Filename
)
1794 if (auto sz
= s
->size())
1796 if (sz
<= 2 && (*s
)[0] == dot
)
1797 return { s
, string_type::npos
};
1798 const auto pos
= s
->rfind(dot
);
1799 return { s
, pos
? pos
: string_type::npos
};
1806 path::_M_split_cmpts()
1810 if (_M_pathname
.empty())
1812 _M_cmpts
.type(_Type::_Filename
);
1815 if (_M_pathname
.length() == 1 && _M_pathname
[0] == preferred_separator
)
1817 _M_cmpts
.type(_Type::_Root_dir
);
1821 _Parser
parser(_M_pathname
);
1823 std::array
<_Parser::cmpt
, 64> buf
;
1824 auto next
= buf
.begin();
1826 // look for root name or root directory
1827 auto root_path
= parser
.root_path();
1828 if (root_path
.first
.valid())
1830 *next
++ = root_path
.first
;
1831 if (root_path
.second
.valid())
1832 *next
++ = root_path
.second
;
1835 auto cmpt
= parser
.next();
1836 while (cmpt
.valid())
1841 cmpt
= parser
.next();
1843 while (cmpt
.valid() && next
!= buf
.end());
1845 if (next
== buf
.end())
1847 _M_cmpts
.type(_Type::_Multi
);
1848 _M_cmpts
.reserve(_M_cmpts
.size() + buf
.size());
1849 auto output
= _M_cmpts
._M_impl
->end();
1852 auto pos
= c
.str
.data() - _M_pathname
.data();
1853 ::new(output
++) _Cmpt(c
.str
, c
.type
, pos
);
1854 ++_M_cmpts
._M_impl
->_M_size
;
1860 if (auto n
= next
- buf
.begin())
1862 if (n
== 1 && _M_cmpts
.empty())
1864 _M_cmpts
.type(buf
.front().type
);
1868 _M_cmpts
.type(_Type::_Multi
);
1869 _M_cmpts
.reserve(_M_cmpts
.size() + n
, true);
1870 auto output
= _M_cmpts
._M_impl
->end();
1871 for (int i
= 0; i
< n
; ++i
)
1874 auto pos
= c
.str
.data() - _M_pathname
.data();
1875 ::new(output
++) _Cmpt(c
.str
, c
.type
, pos
);
1876 ++_M_cmpts
._M_impl
->_M_size
;
1882 path::_S_convert_loc(const char* __first
, const char* __last
,
1883 const std::locale
& __loc
)
1885 #if _GLIBCXX_USE_WCHAR_T
1886 auto& __cvt
= std::use_facet
<codecvt
<wchar_t, char, mbstate_t>>(__loc
);
1887 basic_string
<wchar_t> __ws
;
1888 if (!__str_codecvt_in(__first
, __last
, __ws
, __cvt
))
1889 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
1890 "Cannot convert character sequence",
1891 std::make_error_code(errc::illegal_byte_sequence
)));
1892 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1895 return _Cvt
<wchar_t>::_S_convert(__ws
.data(), __ws
.data() + __ws
.size());
1898 return {__first
, __last
};
1903 fs::hash_value(const path
& p
) noexcept
1905 // [path.non-member]
1906 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
1907 // Equality works as if by traversing the range [begin(), end()), meaning
1908 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
1909 // but need to iterate over individual elements. Use the hash_combine from
1910 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
1912 for (const auto& x
: p
)
1914 seed
^= std::hash
<path::string_type
>()(x
.native()) + 0x9e3779b9
1915 + (seed
<<6) + (seed
>>2);
1920 struct fs::filesystem_error::_Impl
1922 _Impl(const string
& what_arg
, const path
& p1
, const path
& p2
)
1923 : path1(p1
), path2(p2
), what(make_what(what_arg
, &p1
, &p2
))
1926 _Impl(const string
& what_arg
, const path
& p1
)
1927 : path1(p1
), path2(), what(make_what(what_arg
, &p1
, nullptr))
1930 _Impl(const string
& what_arg
)
1931 : what(make_what(what_arg
, nullptr, nullptr))
1935 make_what(const std::string
& s
, const path
* p1
, const path
* p2
)
1937 const std::string pstr1
= p1
? p1
->u8string() : std::string
{};
1938 const std::string pstr2
= p2
? p2
->u8string() : std::string
{};
1939 const size_t len
= 18 + s
.length()
1940 + (pstr1
.length() ? pstr1
.length() + 3 : 0)
1941 + (pstr2
.length() ? pstr2
.length() + 3 : 0);
1944 w
= "filesystem error: ";
1966 template class std::__shared_ptr
<const fs::filesystem_error::_Impl
>;
1968 fs::filesystem_error::
1969 filesystem_error(const string
& what_arg
, error_code ec
)
1970 : system_error(ec
, what_arg
),
1971 _M_impl(std::__make_shared
<_Impl
>(what_arg
))
1974 fs::filesystem_error::
1975 filesystem_error(const string
& what_arg
, const path
& p1
, error_code ec
)
1976 : system_error(ec
, what_arg
),
1977 _M_impl(std::__make_shared
<_Impl
>(what_arg
, p1
))
1980 fs::filesystem_error::
1981 filesystem_error(const string
& what_arg
, const path
& p1
, const path
& p2
,
1983 : system_error(ec
, what_arg
),
1984 _M_impl(std::__make_shared
<_Impl
>(what_arg
, p1
, p2
))
1987 fs::filesystem_error::~filesystem_error() = default;
1990 fs::filesystem_error::path1() const noexcept
1991 { return _M_impl
->path1
; }
1994 fs::filesystem_error::path2() const noexcept
1995 { return _M_impl
->path2
; }
1998 fs::filesystem_error::what() const noexcept
1999 { return _M_impl
->what
.c_str(); }