blob: 506ff25f9a6db19ad8062a224c07c61e19f31f22 [file] [log] [blame]
// Class filesystem::path -*- C++ -*-
// Copyright (C) 2014-2021 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
#ifndef _GLIBCXX_USE_CXX11_ABI
# define _GLIBCXX_USE_CXX11_ABI 1
#endif
#ifdef __CYGWIN__
// Interpret "//x" as a root-name, not root-dir + filename
# define SLASHSLASH_IS_ROOTNAME 1
#endif
#include <filesystem>
#include <algorithm>
#include <bits/stl_uninitialized.h>
namespace fs = std::filesystem;
using fs::path;
static inline bool is_dir_sep(path::value_type ch)
{
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
return ch == L'/' || ch == path::preferred_separator;
#else
return ch == '/';
#endif
}
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
static inline bool is_disk_designator(std::wstring_view s)
{
return s.length() == 2 && s[1] == L':';
}
#endif
struct path::_Parser
{
using string_view_type = std::basic_string_view<value_type>;
struct cmpt
{
string_view_type str;
_Type type = _Type::_Multi;
bool valid() const { return type != _Type::_Multi; }
};
string_view_type input;
string_view_type::size_type pos = 0;
size_t origin;
_Type last_type = _Type::_Multi;
_Parser(string_view_type s, size_t o = 0) : input(s), origin(o) { }
pair<cmpt, cmpt> root_path() noexcept
{
pos = 0;
pair<cmpt, cmpt> root;
const size_t len = input.size();
// look for root name or root directory
if (len && is_dir_sep(input[0]))
{
#if SLASHSLASH_IS_ROOTNAME
// look for root name, such as "//foo"
if (len > 2 && input[1] == input[0])
{
if (!is_dir_sep(input[2]))
{
// got root name, find its end
pos = 3;
while (pos < len && !is_dir_sep(input[pos]))
++pos;
root.first.str = input.substr(0, pos);
root.first.type = _Type::_Root_name;
if (pos < len) // also got root directory
{
root.second.str = input.substr(pos, 1);
root.second.type = _Type::_Root_dir;
++pos;
}
}
else
{
// got something like "///foo" which is just a root directory
// composed of multiple redundant directory separators
root.first.str = input.substr(0, 1);
root.first.type = _Type::_Root_dir;
pos += 2;
}
}
else
#endif
{
root.first.str = input.substr(0, 1);
root.first.type = _Type::_Root_dir;
++pos;
}
// Find the start of the first filename
while (pos < len && is_dir_sep(input[pos]))
++pos;
}
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
else if (is_disk_designator(input.substr(0, 2)))
{
// got disk designator
root.first.str = input.substr(0, 2);
root.first.type = _Type::_Root_name;
if (len > 2 && is_dir_sep(input[2]))
{
root.second.str = input.substr(2, 1);
root.second.type = _Type::_Root_dir;
}
pos = input.find_first_not_of(L"/\\", 2);
}
#endif
if (root.second.valid())
last_type = root.second.type;
else
last_type = root.first.type;
return root;
}
cmpt next() noexcept
{
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
string_view_type sep = L"/\\";
#else
char sep = '/';
#endif
const int last_pos = pos;
cmpt f;
if (pos != input.npos)
{
pos = input.find_first_not_of(sep, pos);
if (pos != input.npos)
{
const auto end = input.find_first_of(sep, pos);
f.str = input.substr(pos, end - pos);
f.type = _Type::_Filename;
pos = end;
}
else if (last_type == _Type::_Filename
|| (last_pos == 0 && !input.empty()))
{
// [fs.path.itr]/4 An empty element, if trailing non-root
// directory-separator present.
__glibcxx_assert(is_dir_sep(input.back()));
f.str = input.substr(input.length(), 0);
f.type = _Type::_Filename;
}
}
last_type = f.type;
return f;
}
string_view_type::size_type
offset(const cmpt& c) const noexcept
{ return origin + c.str.data() - input.data(); }
};
struct path::_List::_Impl
{
using value_type = _Cmpt;
_Impl(int cap) : _M_size(0), _M_capacity(cap) { }
alignas(value_type) int _M_size;
int _M_capacity;
using iterator = value_type*;
using const_iterator = const value_type*;
iterator begin() { return reinterpret_cast<value_type*>(this + 1); }
iterator end() { return begin() + size(); }
const_iterator begin() const
{ return reinterpret_cast<const value_type*>(this + 1); }
const_iterator end() const { return begin() + size(); }
const value_type& front() const { return *begin(); }
const value_type& back() const { return end()[-1]; }
int size() const { return _M_size; }
int capacity() const { return _M_capacity; }
bool empty() const { return _M_size == 0; }
void clear() { std::destroy_n(begin(), _M_size); _M_size = 0; }
void pop_back()
{
back().~_Cmpt();
--_M_size;
}
void _M_erase_from(const_iterator pos)
{
iterator first = begin() + (pos - begin());
iterator last = end();
std::destroy(first, last);
_M_size -= last - first;
}
unique_ptr<_Impl, _Impl_deleter> copy() const
{
const auto n = size();
void* p = ::operator new(sizeof(_Impl) + n * sizeof(value_type));
unique_ptr<_Impl, _Impl_deleter> newptr(::new (p) _Impl{n});
std::uninitialized_copy_n(begin(), n, newptr->begin());
newptr->_M_size = n;
return newptr;
}
// Clear the lowest two bits from the pointer (i.e. remove the _Type value)
static _Impl* notype(_Impl* p)
{
constexpr uintptr_t mask = ~(uintptr_t)0x3;
return reinterpret_cast<_Impl*>(reinterpret_cast<uintptr_t>(p) & mask);
}
};
void path::_List::_Impl_deleter::operator()(_Impl* p) const noexcept
{
p = _Impl::notype(p);
if (p)
{
__glibcxx_assert(p->_M_size <= p->_M_capacity);
p->clear();
::operator delete(p, sizeof(*p) + p->_M_capacity * sizeof(value_type));
}
}
path::_List::_List() : _M_impl(reinterpret_cast<_Impl*>(_Type::_Filename)) { }
path::_List::_List(const _List& other)
{
if (!other.empty())
_M_impl = other._M_impl->copy();
else
type(other.type());
}
path::_List&
path::_List::operator=(const _List& other)
{
if (!other.empty())
{
// copy in-place if there is capacity
const int newsize = other._M_impl->size();
auto impl = _Impl::notype(_M_impl.get());
if (impl && impl->capacity() >= newsize)
{
const int oldsize = impl->_M_size;
auto to = impl->begin();
auto from = other._M_impl->begin();
const int minsize = std::min(newsize, oldsize);
for (int i = 0; i < minsize; ++i)
to[i]._M_pathname.reserve(from[i]._M_pathname.length());
if (newsize > oldsize)
{
std::uninitialized_copy_n(from + oldsize, newsize - oldsize,
to + oldsize);
impl->_M_size = newsize;
}
else if (newsize < oldsize)
impl->_M_erase_from(impl->begin() + newsize);
std::copy_n(from, minsize, to);
type(_Type::_Multi);
}
else
_M_impl = other._M_impl->copy();
}
else
{
clear();
type(other.type());
}
return *this;
}
inline void
path::_List::type(_Type t) noexcept
{
auto val = reinterpret_cast<uintptr_t>(_Impl::notype(_M_impl.release()));
_M_impl.reset(reinterpret_cast<_Impl*>(val | (unsigned char)t));
}
inline int
path::_List::size() const noexcept
{
if (auto* ptr = _Impl::notype(_M_impl.get()))
return ptr->size();
return 0;
}
inline int
path::_List::capacity() const noexcept
{
if (auto* ptr = _Impl::notype(_M_impl.get()))
return ptr->capacity();
return 0;
}
inline bool
path::_List::empty() const noexcept
{
return size() == 0;
}
inline auto
path::_List::begin() noexcept
-> iterator
{
__glibcxx_assert(!empty());
if (auto* ptr = _Impl::notype(_M_impl.get()))
return ptr->begin();
return nullptr;
}
inline auto
path::_List::end() noexcept
-> iterator
{
__glibcxx_assert(!empty());
if (auto* ptr = _Impl::notype(_M_impl.get()))
return ptr->end();
return nullptr;
}
auto
path::_List::begin() const noexcept
-> const_iterator
{
__glibcxx_assert(!empty());
if (auto* ptr = _Impl::notype(_M_impl.get()))
return ptr->begin();
return nullptr;
}
auto
path::_List::end() const noexcept
-> const_iterator
{
__glibcxx_assert(!empty());
if (auto* ptr = _Impl::notype(_M_impl.get()))
return ptr->end();
return nullptr;
}
inline auto
path::_List::front() noexcept
-> value_type&
{
return *_M_impl->begin();
}
inline auto
path::_List::back() noexcept
-> value_type&
{
return _M_impl->begin()[_M_impl->size() - 1];
}
inline auto
path::_List::front() const noexcept
-> const value_type&
{
return *_M_impl->begin();
}
inline auto
path::_List::back() const noexcept
-> const value_type&
{
return _M_impl->begin()[_M_impl->size() - 1];
}
inline void
path::_List::pop_back()
{
__glibcxx_assert(size() > 0);
_M_impl->pop_back();
}
inline void
path::_List::_M_erase_from(const_iterator pos)
{
_M_impl->_M_erase_from(pos);
}
inline void
path::_List::clear()
{
if (auto ptr = _Impl::notype(_M_impl.get()))
ptr->clear();
}
void
path::_List::reserve(int newcap, bool exact = false)
{
// __glibcxx_assert(type() == _Type::_Multi);
_Impl* curptr = _Impl::notype(_M_impl.get());
int curcap = curptr ? curptr->capacity() : 0;
if (curcap < newcap)
{
if (!exact && newcap < int(1.5 * curcap))
newcap = 1.5 * curcap;
void* p = ::operator new(sizeof(_Impl) + newcap * sizeof(value_type));
std::unique_ptr<_Impl, _Impl_deleter> newptr(::new(p) _Impl{newcap});
const int cursize = curptr ? curptr->size() : 0;
if (cursize)
{
std::uninitialized_move_n(curptr->begin(), cursize, newptr->begin());
newptr->_M_size = cursize;
}
std::swap(newptr, _M_impl);
}
}
path&
path::operator=(const path& p)
{
if (&p == this) [[__unlikely__]]
return *this;
_M_pathname.reserve(p._M_pathname.length());
_M_cmpts = p._M_cmpts; // might throw
_M_pathname = p._M_pathname; // won't throw because we reserved enough space
return *this;
}
path&
path::operator/=(const path& __p)
{
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
if (__p.is_absolute()
|| (__p.has_root_name() && __p.root_name() != root_name()))
return operator=(__p);
basic_string_view<value_type> __lhs = _M_pathname;
bool __add_sep = false;
if (__p.has_root_directory())
{
// Remove any root directory and relative path
if (_M_type() != _Type::_Root_name)
{
if (!_M_cmpts.empty()
&& _M_cmpts.front()._M_type() == _Type::_Root_name)
__lhs = _M_cmpts.front()._M_pathname;
else
__lhs = {};
}
}
else if (has_filename() || (!has_root_directory() && is_absolute()))
__add_sep = true;
basic_string_view<value_type> __rhs = __p._M_pathname;
// Omit any root-name from the generic format pathname:
if (__p._M_type() == _Type::_Root_name)
__rhs = {};
else if (!__p._M_cmpts.empty()
&& __p._M_cmpts.front()._M_type() == _Type::_Root_name)
__rhs.remove_prefix(__p._M_cmpts.front()._M_pathname.size());
const size_t __len = __lhs.size() + (int)__add_sep + __rhs.size();
const int __maxcmpts = _M_cmpts.size() + __p._M_cmpts.size();
if (_M_pathname.capacity() < __len || _M_cmpts.capacity() < __maxcmpts)
{
// Construct new path and swap (strong exception-safety guarantee).
string_type __tmp;
__tmp.reserve(__len);
__tmp = __lhs;
if (__add_sep)
__tmp += preferred_separator;
__tmp += __rhs;
path __newp = std::move(__tmp);
swap(__newp);
}
else
{
_M_pathname = __lhs;
if (__add_sep)
_M_pathname += preferred_separator;
_M_pathname += __rhs;
__try
{
_M_split_cmpts();
}
__catch (...)
{
__try
{
// try to restore original state
_M_pathname.resize(__lhs.length());
_M_split_cmpts();
}
__catch (...)
{
// give up, basic exception safety guarantee only:
clear();
__throw_exception_again;
}
}
}
#else
// POSIX version is simpler than the specification in the standard,
// as any path with root-name or root-dir is absolute.
if (__p.is_absolute() || this->empty())
{
return operator=(__p);
}
using string_view_type = basic_string_view<value_type>;
string_view_type sep;
if (has_filename())
sep = { &preferred_separator, 1 }; // need to add a separator
#if SLASHSLASH_IS_ROOTNAME
else if (_M_type() == _Type::_Root_name) // root-name with no root-dir
sep = { &preferred_separator, 1 }; // need to add a separator
#endif
else if (__p.empty())
return *this; // nothing to do
const auto orig_pathlen = _M_pathname.length();
const auto orig_size = _M_cmpts.size();
const auto orig_type = _M_type();
int capacity = 0;
if (_M_type() == _Type::_Multi)
capacity += _M_cmpts.size();
else if (!empty())
capacity += 1;
if (__p._M_type() == _Type::_Multi)
capacity += __p._M_cmpts.size();
else if (!__p.empty() || !sep.empty())
capacity += 1;
#if SLASHSLASH_IS_ROOTNAME
if (orig_type == _Type::_Root_name)
++capacity; // Need to insert root-directory after root-name
#endif
if (orig_type == _Type::_Multi)
{
const int curcap = _M_cmpts._M_impl->capacity();
if (capacity > curcap)
capacity = std::max(capacity, (int) (curcap * 1.5));
}
_M_pathname.reserve(_M_pathname.length() + sep.length()
+ __p._M_pathname.length());
__try
{
_M_pathname += sep;
const auto basepos = _M_pathname.length();
_M_pathname += __p.native();
_M_cmpts.type(_Type::_Multi);
_M_cmpts.reserve(capacity);
_Cmpt* output = _M_cmpts._M_impl->end();
if (orig_type == _Type::_Multi)
{
// Remove empty final component
if (_M_cmpts._M_impl->back().empty())
{
_M_cmpts.pop_back();
--output;
}
}
else if (orig_pathlen != 0)
{
// Create single component from original path
string_view_type s(_M_pathname.data(), orig_pathlen);
::new(output++) _Cmpt(s, orig_type, 0);
++_M_cmpts._M_impl->_M_size;
#if SLASHSLASH_IS_ROOTNAME
if (orig_type == _Type::_Root_name)
{
::new(output++) _Cmpt(sep, _Type::_Root_dir,
orig_pathlen + sep.length());
++_M_cmpts._M_impl->_M_size;
}
#endif
}
if (__p._M_type() == _Type::_Multi)
{
for (auto& c : *__p._M_cmpts._M_impl)
{
::new(output++) _Cmpt(c._M_pathname, _Type::_Filename,
c._M_pos + basepos);
++_M_cmpts._M_impl->_M_size;
}
}
else if (!__p.empty() || !sep.empty())
{
__glibcxx_assert(__p._M_type() == _Type::_Filename);
::new(output) _Cmpt(__p._M_pathname, __p._M_type(), basepos);
++_M_cmpts._M_impl->_M_size;
}
}
__catch (...)
{
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
_M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
else
_M_cmpts.clear();
_M_cmpts.type(orig_type);
__throw_exception_again;
}
#endif
return *this;
}
// [fs.path.append]
void
path::_M_append(basic_string_view<value_type> s)
{
_Parser parser(s);
auto root_path = parser.root_path();
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
bool is_absolute = root_path.second.type == _Type::_Root_dir;
bool has_root_name = root_path.first.type == _Type::_Root_name;
if (is_absolute || (has_root_name && root_path.first.str != root_name()))
{
operator=(s);
return;
}
basic_string_view<value_type> lhs = _M_pathname;
bool add_sep = false;
bool has_root_directory = root_path.first.type == _Type::_Root_dir
|| root_path.second.type == _Type::_Root_dir;
if (has_root_directory)
{
// Remove any root directory and relative path
if (_M_type() != _Type::_Root_name)
{
if (!_M_cmpts.empty()
&& _M_cmpts.front()._M_type() == _Type::_Root_name)
lhs = _M_cmpts.front()._M_pathname;
else
lhs = {};
}
}
else if (has_filename() || (!has_root_directory && is_absolute))
add_sep = true;
basic_string_view<value_type> rhs = s;
// Omit any root-name from the generic format pathname:
if (has_root_name)
rhs.remove_prefix(root_path.first.str.length());
// Construct new path and swap (strong exception-safety guarantee).
string_type tmp;
tmp.reserve(lhs.size() + (int)add_sep + rhs.size());
tmp = lhs;
if (add_sep)
tmp += preferred_separator;
tmp += rhs;
path newp = std::move(tmp);
swap(newp);
#else
bool is_absolute = root_path.first.type == _Type::_Root_dir
|| root_path.second.type == _Type::_Root_dir;
if (is_absolute || this->empty())
{
operator=(s);
return;
}
const auto orig_pathlen = _M_pathname.length();
const auto orig_size = _M_cmpts.size();
const auto orig_type = _M_type();
basic_string_view<value_type> sep;
if (has_filename())
sep = { &preferred_separator, 1 }; // need to add a separator
#if SLASHSLASH_IS_ROOTNAME
else if (_M_type() == _Type::_Root_name) // root-name with no root-dir
sep = { &preferred_separator, 1 }; // need to add a separator
#endif
else if (s.empty())
return; // nothing to do
// Copy the input into _M_pathname:
_M_pathname += s;
_M_pathname.insert(orig_pathlen, sep);
// Update s to refer to the new copy (this ensures s is not a dangling
// reference to deallocated characters, in the case where it was referring
// into _M_pathname or a member of _M_cmpts).
s = _M_pathname;
const auto orig_pathname = s.substr(0, orig_pathlen);
s.remove_prefix(orig_pathlen + sep.length());
parser.input = s; // reset parser to use updated string view
const auto basepos = orig_pathname.length() + sep.length();
parser.origin = basepos;
std::array<_Parser::cmpt, 64> buf;
auto next = buf.begin();
int capacity = 0;
if (_M_type() == _Type::_Multi)
capacity += _M_cmpts.size();
else if (!empty())
capacity += 1;
auto cmpt = parser.next();
if (cmpt.valid())
{
do
{
*next++ = cmpt;
cmpt = parser.next();
}
while (cmpt.valid() && next != buf.end());
capacity += next - buf.begin();
if (cmpt.valid()) // filled buffer before parsing whole input
{
++capacity;
_Parser parser2(parser);
while (parser2.next().valid())
++capacity;
}
}
else if (!sep.empty())
++capacity;
#if SLASHSLASH_IS_ROOTNAME
if (orig_type == _Type::_Root_name)
++capacity; // Need to insert root-directory after root-name
#endif
__try
{
_M_cmpts.type(_Type::_Multi);
_M_cmpts.reserve(capacity);
_Cmpt* output = _M_cmpts._M_impl->end();
if (orig_type == _Type::_Multi)
{
// Remove empty final component
if (_M_cmpts._M_impl->back().empty())
{
_M_cmpts.pop_back();
--output;
}
}
else if (orig_pathlen != 0)
{
// Create single component from original path
::new(output++) _Cmpt(orig_pathname, orig_type, 0);
++_M_cmpts._M_impl->_M_size;
#if SLASHSLASH_IS_ROOTNAME
if (!sep.empty() && orig_type == _Type::_Root_name)
{
::new(output++) _Cmpt(sep, _Type::_Root_dir,
orig_pathlen + sep.length());
++_M_cmpts._M_impl->_M_size;
}
#endif
}
if (next != buf.begin())
{
for (auto it = buf.begin(); it != next; ++it)
{
auto c = *it;
::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
++_M_cmpts._M_impl->_M_size;
}
while (cmpt.valid())
{
::new(output++) _Cmpt(cmpt.str, cmpt.type, parser.offset(cmpt));
++_M_cmpts._M_impl->_M_size;
cmpt = parser.next();
}
}
else if (!sep.empty())
{
// Empty filename at the end:
::new(output) _Cmpt({}, _Type::_Filename, basepos);
++_M_cmpts._M_impl->_M_size;
}
}
__catch (...)
{
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
_M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
else
_M_cmpts.clear();
_M_cmpts.type(orig_type);
__throw_exception_again;
}
#endif
}
// [fs.path.concat]
path&
path::operator+=(const path& p)
{
if (p.empty())
return *this;
if (this->empty())
{
operator=(p);
return *this;
}
#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
if (_M_type() == _Type::_Root_name
|| (_M_type() == _Type::_Filename && _M_pathname.size() == 1))
{
// Handle path("C") += path(":") and path("C:") += path("/x")
// FIXME: do this more efficiently
*this = path(_M_pathname + p._M_pathname);
return *this;
}
#endif
#if SLASHSLASH_IS_ROOTNAME
if (_M_type() == _Type::_Root_dir)
{
// Handle path("/") += path("/x") and path("//") += path("x")
// FIXME: do this more efficiently
*this = path(_M_pathname + p._M_pathname);
return *this;
}
#endif
const auto orig_pathlen = _M_pathname.length();
const auto orig_type = _M_type();
const auto orig_size = _M_cmpts.size();
int orig_filenamelen = -1;
basic_string_view<value_type> extra;
// Ensure that '_M_pathname += p._M_pathname' won't throw:
_M_pathname.reserve(orig_pathlen + p._M_pathname.length());
_Cmpt c;
_Cmpt* it = nullptr;
_Cmpt* last = nullptr;
if (p._M_type() == _Type::_Multi)
{
it = p._M_cmpts._M_impl->begin();
last = p._M_cmpts._M_impl->end();
}
else
{
c = _Cmpt(p._M_pathname, p._M_type(), 0);
it = &c;
last = it + 1;
}
if (it->_M_type() == _Type::_Filename)
{
// See if there's a filename or root-name at the end of the original path
// that we can add to.
if (_M_type() == _Type::_Filename
#if SLASHSLASH_IS_ROOTNAME
|| _M_type() == _Type::_Root_name
#endif
)
{
if (p._M_type() == _Type::_Filename)
{
// Simplest case where we just add the whole of p to the
// original path.
_M_pathname += p._M_pathname;
return *this;
}
// Only the first component of s should be appended, do so below:
extra = it->_M_pathname;
++it;
}
else if (_M_type() == _Type::_Multi
&& _M_cmpts.back()._M_type() == _Type::_Filename)
{
auto& back = _M_cmpts.back();
if (p._M_type() == _Type::_Filename)
{
basic_string_view<value_type> s = p._M_pathname;
back._M_pathname += s;
_M_pathname += s;
return *this;
}
orig_filenamelen = back._M_pathname.length();
back._M_pathname += it->_M_pathname;
extra = it->_M_pathname;
++it;
}
}
else if (is_dir_sep(_M_pathname.back()) && _M_type() == _Type::_Multi
&& _M_cmpts.back()._M_type() == _Type::_Filename)
orig_filenamelen = 0; // current path has empty filename at end
int capacity = 0;
if (_M_type() == _Type::_Multi)
capacity += _M_cmpts.size();
else
capacity += 1;
if (p._M_type() == _Type::_Multi)
capacity += p._M_cmpts.size();
else
capacity += 1;
__try
{
_M_cmpts.type(_Type::_Multi);
_M_cmpts.reserve(capacity);
_Cmpt* output = _M_cmpts._M_impl->end();
if (orig_type != _Type::_Multi)
{
// Create single component from original path
auto ptr = ::new(output++) _Cmpt({}, orig_type, 0);
++_M_cmpts._M_impl->_M_size;
ptr->_M_pathname.reserve(_M_pathname.length() + extra.length());
ptr->_M_pathname = _M_pathname;
ptr->_M_pathname += extra;
#if SLASHSLASH_IS_ROOTNAME
if (orig_type == _Type::_Root_name)
{
basic_string_view<value_type> s(p._M_pathname);
::new(output++) _Cmpt(s.substr(extra.length(), 1),
_Type::_Root_dir, orig_pathlen + extra.length());
++_M_cmpts._M_impl->_M_size;
}
#endif
}
else if (orig_filenamelen == 0 && it != last)
{
// Remove empty filename at end of original path.
_M_cmpts.pop_back();
--output;
}
if (it != last && it->_M_type() == _Type::_Root_name)
{
basic_string_view<value_type> s = it->_M_pathname;
auto pos = orig_pathlen;
#if SLASHSLASH_IS_ROOTNAME
s.remove_prefix(2);
pos += 2;
#endif
::new(output++) _Cmpt(s, _Type::_Filename, pos);
++_M_cmpts._M_impl->_M_size;
++it;
}
if (it != last && it->_M_type() == _Type::_Root_dir)
++it;
while (it != last)
{
auto pos = it->_M_pos + orig_pathlen;
::new(output++) _Cmpt(it->_M_pathname, _Type::_Filename, pos);
++_M_cmpts._M_impl->_M_size;
++it;
}
_M_pathname += p._M_pathname;
if (is_dir_sep(_M_pathname.back()))
{
::new(output++) _Cmpt({}, _Type::_Filename, _M_pathname.length());
++_M_cmpts._M_impl->_M_size;
}
}
__catch (...)
{
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
{
if (_M_cmpts.size() > orig_size)
_M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
if (orig_filenamelen != -1)
{
if (_M_cmpts.size() == orig_size)
{
auto& back = _M_cmpts.back();
back._M_pathname.resize(orig_filenamelen);
if (orig_filenamelen == 0)
back._M_pos = orig_pathlen;
}
else
{
auto output = _M_cmpts._M_impl->end();
::new(output) _Cmpt({}, _Type::_Filename, orig_pathlen);
++_M_cmpts._M_impl->_M_size;
}
}
}
else
_M_cmpts.clear();
_M_cmpts.type(orig_type);
__throw_exception_again;
}
return *this;
}
// [fs.path.concat]
void
path::_M_concat(basic_string_view<value_type> s)
{
if (s.empty())
return;
if (this->empty())
{
operator=(s);
return;
}
#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
if (_M_type() == _Type::_Root_name
|| (_M_type() == _Type::_Filename && _M_pathname.size() == 1))
{
// Handle path("C") += ":" and path("C:") += "/x"
// FIXME: do this more efficiently
*this = path(_M_pathname + string_type(s));
return;
}
#endif
#if SLASHSLASH_IS_ROOTNAME
if (_M_type() == _Type::_Root_dir)
{
// Handle path("/") += "/x" and path("//") += "x"
// FIXME: do this more efficiently
*this = path(_M_pathname + string_type(s));
return;
}
#endif
const auto orig_pathlen = _M_pathname.length();
const auto orig_type = _M_type();
const auto orig_size = _M_cmpts.size();
int orig_filenamelen = -1;
basic_string_view<value_type> extra;
// Copy the input into _M_pathname:
_M_pathname += s;
// Update s to refer to the new copy (this ensures s is not a dangling
// reference to deallocated characters, in the case where it was referring
// into _M_pathname or a member of _M_cmpts).
s = _M_pathname;
const auto orig_pathname = s.substr(0, orig_pathlen);
s.remove_prefix(orig_pathlen);
_Parser parser(s, orig_pathlen);
auto cmpt = parser.next();
if (cmpt.str.data() == s.data())
{
// See if there's a filename or root-name at the end of the original path
// that we can add to.
if (_M_type() == _Type::_Filename
#if SLASHSLASH_IS_ROOTNAME
|| _M_type() == _Type::_Root_name
#endif
)
{
if (cmpt.str.length() == s.length())
{
// Simplest case where we just need to add the whole of s
// to the original path, which was already done above.
return;
}
// Only the first component of s should be appended, do so below:
extra = cmpt.str;
cmpt = {}; // so we don't process it again
}
else if (_M_type() == _Type::_Multi
&& _M_cmpts.back()._M_type() == _Type::_Filename)
{
auto& back = _M_cmpts.back();
if (cmpt.str.length() == s.length())
{
back._M_pathname += s;
return;
}
orig_filenamelen = back._M_pathname.length();
back._M_pathname += cmpt.str;
extra = cmpt.str;
cmpt = {};
}
}
else if (is_dir_sep(orig_pathname.back()) && _M_type() == _Type::_Multi
&& _M_cmpts.back()._M_type() == _Type::_Filename)
orig_filenamelen = 0; // original path had empty filename at end
std::array<_Parser::cmpt, 64> buf;
auto next = buf.begin();
if (cmpt.valid())
*next++ = cmpt;
cmpt = parser.next();
while (cmpt.valid() && next != buf.end())
{
*next++ = cmpt;
cmpt = parser.next();
}
int capacity = 0;
if (_M_type() == _Type::_Multi)
capacity += _M_cmpts.size();
else
capacity += 1;
capacity += next - buf.begin();
if (cmpt.valid()) // filled buffer before parsing whole input
{
++capacity;
_Parser parser2(parser);
while (parser2.next().valid())
++capacity;
}
#if SLASHSLASH_IS_ROOTNAME
if (orig_type == _Type::_Root_name)
++capacity; // Need to insert root-directory after root-name
#endif
__try
{
_M_cmpts.type(_Type::_Multi);
_M_cmpts.reserve(capacity);
_Cmpt* output = _M_cmpts._M_impl->end();
auto it = buf.begin();
if (orig_type != _Type::_Multi)
{
// Create single component from original path
auto p = ::new(output++) _Cmpt({}, orig_type, 0);
++_M_cmpts._M_impl->_M_size;
p->_M_pathname.reserve(orig_pathname.length() + extra.length());
p->_M_pathname = orig_pathname;
p->_M_pathname += extra;
#if SLASHSLASH_IS_ROOTNAME
if (orig_type == _Type::_Root_name)
{
::new(output++) _Cmpt(s.substr(extra.length(), 1),
_Type::_Root_dir, orig_pathlen + extra.length());
++_M_cmpts._M_impl->_M_size;
}
#endif
}
else if (orig_filenamelen == 0 && extra.empty())
{
// Replace empty filename at end of original path.
std::prev(output)->_M_pathname = it->str;
std::prev(output)->_M_pos = parser.offset(*it);
++it;
}
while (it != next)
{
::new(output++) _Cmpt(it->str, _Type::_Filename, parser.offset(*it));
++_M_cmpts._M_impl->_M_size;
++it;
}
if (next == buf.end())
{
while (cmpt.valid())
{
auto pos = parser.offset(cmpt);
::new(output++) _Cmpt(cmpt.str, _Type::_Filename, pos);
++_M_cmpts._M_impl->_M_size;
cmpt = parser.next();
}
}
}
__catch (...)
{
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
{
_M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
if (orig_filenamelen != -1)
{
auto& back = _M_cmpts.back();
back._M_pathname.resize(orig_filenamelen);
if (orig_filenamelen == 0)
back._M_pos = orig_pathlen;
}
}
else
_M_cmpts.clear();
_M_cmpts.type(orig_type);
__throw_exception_again;
}
}
path&
path::remove_filename()
{
if (_M_type() == _Type::_Multi)
{
if (!_M_cmpts.empty())
{
auto cmpt = std::prev(_M_cmpts.end());
if (cmpt->_M_type() == _Type::_Filename && !cmpt->empty())
{
_M_pathname.erase(cmpt->_M_pos);
auto prev = std::prev(cmpt);
if (prev->_M_type() == _Type::_Root_dir
|| prev->_M_type() == _Type::_Root_name)
{
_M_cmpts.pop_back();
if (_M_cmpts.size() == 1)
{
_M_cmpts.type(_M_cmpts.front()._M_type());
_M_cmpts.clear();
}
}
else
cmpt->clear();
}
}
}
else if (_M_type() == _Type::_Filename)
clear();
return *this;
}
path&
path::replace_filename(const path& replacement)
{
remove_filename();
operator/=(replacement);
return *this;
}
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
const fs::path::value_type dot = L'.';
#else
const fs::path::value_type dot = '.';
#endif
path&
path::replace_extension(const path& replacement)
{
auto ext = _M_find_extension();
// Any existing extension() is removed
if (ext.first && ext.second != string_type::npos)
{
if (ext.first == &_M_pathname)
_M_pathname.erase(ext.second);
else
{
auto& back = _M_cmpts.back();
__glibcxx_assert( ext.first == &back._M_pathname );
back._M_pathname.erase(ext.second);
_M_pathname.erase(back._M_pos + ext.second);
}
}
// If replacement is not empty and does not begin with a dot character,
// a dot character is appended
if (!replacement.empty() && replacement.native()[0] != dot)
operator+=(".");
operator+=(replacement);
return *this;
}
int
path::compare(const path& p) const noexcept
{
if (_M_pathname == p._M_pathname)
return 0;
basic_string_view<value_type> lroot, rroot;
if (_M_type() == _Type::_Root_name)
lroot = _M_pathname;
else if (_M_type() == _Type::_Multi
&& _M_cmpts.front()._M_type() == _Type::_Root_name)
lroot = _M_cmpts.front()._M_pathname;
if (p._M_type() == _Type::_Root_name)
rroot = p._M_pathname;
else if (p._M_type() == _Type::_Multi
&& p._M_cmpts.front()._M_type() == _Type::_Root_name)
rroot = p._M_cmpts.front()._M_pathname;
if (int rootNameComparison = lroot.compare(rroot))
return rootNameComparison;
if (!this->has_root_directory() && p.has_root_directory())
return -1;
else if (this->has_root_directory() && !p.has_root_directory())
return +1;
using Iterator = const _Cmpt*;
Iterator begin1, end1, begin2, end2;
if (_M_type() == _Type::_Multi)
{
begin1 = _M_cmpts.begin();
end1 = _M_cmpts.end();
// Find start of this->relative_path()
while (begin1 != end1 && begin1->_M_type() != _Type::_Filename)
++begin1;
}
else
begin1 = end1 = nullptr;
if (p._M_type() == _Type::_Multi)
{
begin2 = p._M_cmpts.begin();
end2 = p._M_cmpts.end();
// Find start of p.relative_path()
while (begin2 != end2 && begin2->_M_type() != _Type::_Filename)
++begin2;
}
else
begin2 = end2 = nullptr;
if (_M_type() == _Type::_Filename)
{
if (p._M_type() == _Type::_Filename)
return native().compare(p.native());
else if (begin2 != end2)
{
if (int ret = native().compare(begin2->native()))
return ret;
else
return ++begin2 == end2 ? 0 : -1;
}
else
return +1;
}
else if (p._M_type() == _Type::_Filename)
{
if (begin1 != end1)
{
if (int ret = begin1->native().compare(p.native()))
return ret;
else
return ++begin1 == end1 ? 0 : +1;
}
else
return -1;
}
int count = 1;
while (begin1 != end1 && begin2 != end2)
{
if (int i = begin1->native().compare(begin2->native()))
return i;
++begin1;
++begin2;
++count;
}
if (begin1 == end1)
{
if (begin2 == end2)
return 0;
return -count;
}
return count;
}
int
path::compare(basic_string_view<value_type> s) const noexcept
{
if (_M_pathname == s)
return 0;
_Parser parser(s);
basic_string_view<value_type> lroot, rroot;
if (_M_type() == _Type::_Root_name)
lroot = _M_pathname;
else if (_M_type() == _Type::_Multi
&& _M_cmpts.front()._M_type() == _Type::_Root_name)
lroot = _M_cmpts.front()._M_pathname;
auto root_path = parser.root_path();
if (root_path.first.type == _Type::_Root_name)
rroot = root_path.first.str;
if (int rootNameComparison = lroot.compare(rroot))
return rootNameComparison;
const bool has_root_dir = root_path.first.type == _Type::_Root_dir
|| root_path.second.type == _Type::_Root_dir;
if (!this->has_root_directory() && has_root_dir)
return -1;
else if (this->has_root_directory() && !has_root_dir)
return +1;
using Iterator = const _Cmpt*;
Iterator begin1, end1;
if (_M_type() == _Type::_Filename)
{
auto cmpt = parser.next();
if (cmpt.valid())
{
if (int ret = this->native().compare(cmpt.str))
return ret;
return parser.next().valid() ? -1 : 0;
}
else
return +1;
}
else if (_M_type() == _Type::_Multi)
{
begin1 = _M_cmpts.begin();
end1 = _M_cmpts.end();
while (begin1 != end1 && begin1->_M_type() != _Type::_Filename)
++begin1;
}
else
begin1 = end1 = nullptr;
int count = 1;
auto cmpt = parser.next();
while (begin1 != end1 && cmpt.valid())
{
if (int i = begin1->native().compare(cmpt.str))
return i;
++begin1;
cmpt = parser.next();
++count;
}
if (begin1 == end1)
{
if (!cmpt.valid())
return 0;
return -count;
}
return +count;
}
path
path::root_name() const
{
path __ret;
if (_M_type() == _Type::_Root_name)
__ret = *this;
else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type() == _Type::_Root_name)
__ret = *_M_cmpts.begin();
return __ret;
}
path
path::root_directory() const
{
path __ret;
if (_M_type() == _Type::_Root_dir)
{
__ret._M_cmpts.type(_Type::_Root_dir);
__ret._M_pathname.assign(1, preferred_separator);
}
else if (!_M_cmpts.empty())
{
auto __it = _M_cmpts.begin();
if (__it->_M_type() == _Type::_Root_name)
++__it;
if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
__ret = *__it;
}
return __ret;
}
path
path::root_path() const
{
path __ret;
if (_M_type() == _Type::_Root_name)
__ret = *this;
else if (_M_type() == _Type::_Root_dir)
{
__ret._M_pathname.assign(1, preferred_separator);
__ret._M_cmpts.type(_Type::_Root_dir);
}
else if (!_M_cmpts.empty())
{
auto __it = _M_cmpts.begin();
if (__it->_M_type() == _Type::_Root_name)
{
__ret = *__it++;
if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
__ret /= *__it;
}
else if (__it->_M_type() == _Type::_Root_dir)
__ret = *__it;
}
return __ret;
}
path
path::relative_path() const
{
path __ret;
if (_M_type() == _Type::_Filename)
__ret = *this;
else if (!_M_cmpts.empty())
{
auto __it = _M_cmpts.begin();
if (__it->_M_type() == _Type::_Root_name)
++__it;
if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
++__it;
if (__it != _M_cmpts.end())
__ret.assign(_M_pathname.substr(__it->_M_pos));
}
return __ret;
}
path
path::parent_path() const
{
path __ret;
if (!has_relative_path())
__ret = *this;
else if (_M_cmpts.size() >= 2)
{
const auto parent = std::prev(_M_cmpts.end(), 2);
const auto len = parent->_M_pos + parent->_M_pathname.length();
__ret.assign(_M_pathname.substr(0, len));
}
return __ret;
}
bool
path::has_root_name() const noexcept
{
if (_M_type() == _Type::_Root_name)
return true;
if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type() == _Type::_Root_name)
return true;
return false;
}
bool
path::has_root_directory() const noexcept
{
if (_M_type() == _Type::_Root_dir)
return true;
if (!_M_cmpts.empty())
{
auto __it = _M_cmpts.begin();
if (__it->_M_type() == _Type::_Root_name)
++__it;
if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
return true;
}
return false;
}
bool
path::has_root_path() const noexcept
{
if (_M_type() == _Type::_Root_name || _M_type() == _Type::_Root_dir)
return true;
if (!_M_cmpts.empty())
{
auto __type = _M_cmpts.front()._M_type();
if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
return true;
}
return false;
}
bool
path::has_relative_path() const noexcept
{
if (_M_type() == _Type::_Filename && !_M_pathname.empty())
return true;
if (!_M_cmpts.empty())
{
auto __it = _M_cmpts.begin();
if (__it->_M_type() == _Type::_Root_name)
++__it;
if (__it != _M_cmpts.end() && __it->_M_type() == _Type::_Root_dir)
++__it;
if (__it != _M_cmpts.end() && !__it->_M_pathname.empty())
return true;
}
return false;
}
bool
path::has_parent_path() const noexcept
{
if (!has_relative_path())
return !empty();
return _M_cmpts.size() >= 2;
}
bool
path::has_filename() const noexcept
{
if (empty())
return false;
if (_M_type() == _Type::_Filename)
return !_M_pathname.empty();
if (_M_type() == _Type::_Multi)
{
if (_M_pathname.back() == preferred_separator)
return false;
return _M_cmpts.back().has_filename();
}
return false;
}
namespace
{
inline bool is_dot(fs::path::value_type c) { return c == dot; }
inline bool is_dot(const fs::path& path)
{
const auto& filename = path.native();
return filename.size() == 1 && is_dot(filename[0]);
}
inline bool is_dotdot(const fs::path& path)
{
const auto& filename = path.native();
return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
}
} // namespace
path
path::lexically_normal() const
{
/*
C++17 [fs.path.generic] p6
- If the path is empty, stop.
- Replace each slash character in the root-name with a preferred-separator.
- Replace each directory-separator with a preferred-separator.
- Remove each dot filename and any immediately following directory-separator.
- As long as any appear, remove a non-dot-dot filename immediately followed
by a directory-separator and a dot-dot filename, along with any immediately
following directory-separator.
- If there is a root-directory, remove all dot-dot filenames and any
directory-separators immediately following them.
- If the last filename is dot-dot, remove any trailing directory-separator.
- If the path is empty, add a dot.
*/
path ret;
// If the path is empty, stop.
if (empty())
return ret;
for (auto& p : *this)
{
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
// Replace each slash character in the root-name
if (p._M_type() == _Type::_Root_name || p._M_type() == _Type::_Root_dir)
{
string_type s = p.native();
std::replace(s.begin(), s.end(), L'/', L'\\');
ret /= s;
continue;
}
#endif
if (is_dotdot(p))
{
if (ret.has_filename())
{
// remove a non-dot-dot filename immediately followed by /..
if (!is_dotdot(ret.filename()))
ret.remove_filename();
else
ret /= p;
}
else if (!ret.has_relative_path())
{
// remove a dot-dot filename immediately after root-directory
if (!ret.has_root_directory())
ret /= p;
}
else
{
// Got a path with a relative path (i.e. at least one non-root
// element) and no filename at the end (i.e. empty last element),
// so must have a trailing slash. See what is before it.
auto elem = ret._M_cmpts.end() - 2;
if (elem->has_filename() && !is_dotdot(*elem))
{
// Remove the filename before the trailing slash
// (equiv. to ret = ret.parent_path().remove_filename())
if (elem == ret._M_cmpts.begin())
ret.clear();
else
{
ret._M_pathname.erase(elem->_M_pos);
// Remove empty filename at the end:
ret._M_cmpts.pop_back();
// If we still have a trailing non-root dir separator
// then leave an empty filename at the end:
if (std::prev(elem)->_M_type() == _Type::_Filename)
elem->clear();
else // remove the component completely:
ret._M_cmpts.pop_back();
}
}
else
// Append the ".." to something ending in "../" which happens
// when normalising paths like ".././.." and "../a/../.."
ret /= p;
}
}
else if (is_dot(p))
ret /= path();
#if SLASHSLASH_IS_ROOTNAME
else if (p._M_type() == _Type::_Root_dir)
ret += '/'; // using operator/=('/') would replace whole of ret
#endif
else
ret /= p;
}
if (ret._M_cmpts.size() >= 2)
{
auto back = std::prev(ret.end());
// If the last filename is dot-dot, ...
if (back->empty() && is_dotdot(*std::prev(back)))
// ... remove any trailing directory-separator.
ret = ret.parent_path();
}
// If the path is empty, add a dot.
else if (ret.empty())
ret = ".";
return ret;
}
path
path::lexically_relative(const path& base) const
{
path ret;
if (root_name() != base.root_name())
return ret;
if (is_absolute() != base.is_absolute())
return ret;
if (!has_root_directory() && base.has_root_directory())
return ret;
auto [a, b] = std::mismatch(begin(), end(), base.begin(), base.end());
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3070. path::lexically_relative causes surprising results if a filename
// can also be a root-name
if (!empty())
for (auto& p : _M_cmpts)
if (p._M_type() == _Type::_Filename && is_disk_designator(p.native()))
return ret;
if (!base.empty())
for (auto i = b, end = base.end(); i != end; ++i)
if (i->_M_type() == _Type::_Filename && is_disk_designator(i->native()))
return ret;
#endif
if (a == end() && b == base.end())
ret = ".";
else
{
int n = 0;
for (; b != base.end(); ++b)
{
const path& p = *b;
if (is_dotdot(p))
--n;
else if (!p.empty() && !is_dot(p))
++n;
}
if (n == 0 && (a == end() || a->empty()))
ret = ".";
else if (n >= 0)
{
const path dotdot("..");
while (n--)
ret /= dotdot;
for (; a != end(); ++a)
ret /= *a;
}
}
return ret;
}
path
path::lexically_proximate(const path& base) const
{
path rel = lexically_relative(base);
if (rel.empty())
rel = *this;
return rel;
}
std::pair<const path::string_type*, std::size_t>
path::_M_find_extension() const noexcept
{
const string_type* s = nullptr;
if (_M_type() == _Type::_Filename)
s = &_M_pathname;
else if (_M_type() == _Type::_Multi && !_M_cmpts.empty())
{
const auto& c = _M_cmpts.back();
if (c._M_type() == _Type::_Filename)
s = &c._M_pathname;
}
if (s)
{
if (auto sz = s->size())
{
if (sz <= 2 && (*s)[0] == dot)
return { s, string_type::npos };
if (const auto pos = s->rfind(dot))
return { s , pos };
return { s, string_type::npos };
}
}
return {};
}
void
path::_M_split_cmpts()
{
_M_cmpts.clear();
if (_M_pathname.empty())
{
_M_cmpts.type(_Type::_Filename);
return;
}
if (_M_pathname.length() == 1 && _M_pathname[0] == preferred_separator)
{
_M_cmpts.type(_Type::_Root_dir);
return;
}
_Parser parser(_M_pathname);
std::array<_Parser::cmpt, 64> buf;
auto next = buf.begin();
// look for root name or root directory
auto root_path = parser.root_path();
if (root_path.first.valid())
{
*next++ = root_path.first;
if (root_path.second.valid())
*next++ = root_path.second;
}
auto cmpt = parser.next();
while (cmpt.valid())
{
do
{
*next++ = cmpt;
cmpt = parser.next();
}
while (cmpt.valid() && next != buf.end());
if (next == buf.end())
{
_M_cmpts.type(_Type::_Multi);
_M_cmpts.reserve(_M_cmpts.size() + buf.size());
auto output = _M_cmpts._M_impl->end();
for (const auto& c : buf)
{
::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
++_M_cmpts._M_impl->_M_size;
}
next = buf.begin();
}
}
if (auto n = next - buf.begin())
{
if (n == 1 && _M_cmpts.empty())
{
_M_cmpts.type(buf.front().type);
return;
}
_M_cmpts.type(_Type::_Multi);
_M_cmpts.reserve(_M_cmpts.size() + n, true);
auto output = _M_cmpts._M_impl->end();
for (int i = 0; i < n; ++i)
{
const auto& c = buf[i];
::new(output++) _Cmpt(c.str, c.type, parser.offset(c));
++_M_cmpts._M_impl->_M_size;
}
}
}
path::string_type
path::_S_convert_loc(const char* __first, const char* __last,
const std::locale& __loc)
{
#if _GLIBCXX_USE_WCHAR_T
auto& __cvt = std::use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc);
basic_string<wchar_t> __ws;
if (!__str_codecvt_in_all(__first, __last, __ws, __cvt))
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
"Cannot convert character sequence",
std::make_error_code(errc::illegal_byte_sequence)));
return _S_convert(std::move(__ws));
#else
return {__first, __last};
#endif
}
std::size_t
fs::hash_value(const path& p) noexcept
{
// [path.non-member]
// "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
// Equality works as if by traversing the range [begin(), end()), meaning
// e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
// but need to iterate over individual elements. Use the hash_combine from
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
size_t seed = 0;
for (const auto& x : p)
{
seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
+ (seed<<6) + (seed>>2);
}
return seed;
}
struct fs::filesystem_error::_Impl
{
_Impl(string_view what_arg, const path& p1, const path& p2)
: path1(p1), path2(p2), what(make_what(what_arg, &p1, &p2))
{ }
_Impl(string_view what_arg, const path& p1)
: path1(p1), path2(), what(make_what(what_arg, &p1, nullptr))
{ }
_Impl(string_view what_arg)
: what(make_what(what_arg, nullptr, nullptr))
{ }
static std::string
make_what(string_view s, const path* p1, const path* p2)
{
const std::string pstr1 = p1 ? p1->u8string() : std::string{};
const std::string pstr2 = p2 ? p2->u8string() : std::string{};
const size_t len = 18 + s.length()
+ (pstr1.length() ? pstr1.length() + 3 : 0)
+ (pstr2.length() ? pstr2.length() + 3 : 0);
std::string w;
w.reserve(len);
w = "filesystem error: ";
w += s;
if (p1)
{
w += " [";
w += pstr1;
w += ']';
if (p2)
{
w += " [";
w += pstr2;
w += ']';
}
}
return w;
}
path path1;
path path2;
std::string what;
};
template class std::__shared_ptr<const fs::filesystem_error::_Impl>;
fs::filesystem_error::
filesystem_error(const string& what_arg, error_code ec)
: system_error(ec, what_arg),
_M_impl(std::__make_shared<_Impl>(system_error::what()))
{ }
fs::filesystem_error::
filesystem_error(const string& what_arg, const path& p1, error_code ec)
: system_error(ec, what_arg),
_M_impl(std::__make_shared<_Impl>(system_error::what(), p1))
{ }
fs::filesystem_error::
filesystem_error(const string& what_arg, const path& p1, const path& p2,
error_code ec)
: system_error(ec, what_arg),
_M_impl(std::__make_shared<_Impl>(system_error::what(), p1, p2))
{ }
fs::filesystem_error::~filesystem_error() = default;
const fs::path&
fs::filesystem_error::path1() const noexcept
{ return _M_impl->path1; }
const fs::path&
fs::filesystem_error::path2() const noexcept
{ return _M_impl->path2; }
const char*
fs::filesystem_error::what() const noexcept
{ return _M_impl->what.c_str(); }