summaryrefslogtreecommitdiffstats
path: root/lib/libcxx/src/experimental/filesystem
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libcxx/src/experimental/filesystem')
-rw-r--r--lib/libcxx/src/experimental/filesystem/directory_iterator.cpp148
-rw-r--r--lib/libcxx/src/experimental/filesystem/filesystem_time_helper.h173
-rw-r--r--lib/libcxx/src/experimental/filesystem/operations.cpp184
-rw-r--r--lib/libcxx/src/experimental/filesystem/path.cpp557
4 files changed, 696 insertions, 366 deletions
diff --git a/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp b/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp
index fa217ba7a12..a552fdc4461 100644
--- a/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp
+++ b/lib/libcxx/src/experimental/filesystem/directory_iterator.cpp
@@ -1,28 +1,32 @@
+//===------------------ directory_iterator.cpp ----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
#include "experimental/filesystem"
+#include "__config"
+#if defined(_LIBCPP_WIN32API)
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#else
#include <dirent.h>
+#endif
#include <errno.h>
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
namespace { namespace detail {
+#if !defined(_LIBCPP_WIN32API)
inline error_code capture_errno() {
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
return error_code{errno, std::generic_category()};
}
-
-template <class ...Args>
-inline bool capture_error_or_throw(std::error_code* user_ec,
- const char* msg, Args&&... args)
-{
- std::error_code my_ec = capture_errno();
- if (user_ec) {
- *user_ec = my_ec;
- return true;
- }
- __libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
- return false;
-}
+#endif
template <class ...Args>
inline bool set_or_throw(std::error_code& my_ec,
@@ -33,29 +37,91 @@ inline bool set_or_throw(std::error_code& my_ec,
*user_ec = my_ec;
return true;
}
- __libcpp_throw(filesystem_error(msg, std::forward<Args>(args)..., my_ec));
+ __throw_filesystem_error(msg, std::forward<Args>(args)..., my_ec);
return false;
}
-typedef path::string_type string_type;
-
-
-inline string_type posix_readdir(DIR *dir_stream, error_code& ec) {
+#if !defined(_LIBCPP_WIN32API)
+inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) {
struct dirent* dir_entry_ptr = nullptr;
errno = 0; // zero errno in order to detect errors
+ ec.clear();
if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
- ec = capture_errno();
+ if (errno)
+ ec = capture_errno();
return {};
} else {
- ec.clear();
return dir_entry_ptr->d_name;
}
}
+#endif
}} // namespace detail
using detail::set_or_throw;
+#if defined(_LIBCPP_WIN32API)
+class __dir_stream {
+public:
+ __dir_stream() = delete;
+ __dir_stream& operator=(const __dir_stream&) = delete;
+
+ __dir_stream(__dir_stream&& __ds) noexcept
+ : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)),
+ __entry_(std::move(__ds.__entry_)) {
+ __ds.__stream_ = INVALID_HANDLE_VALUE;
+ }
+
+ __dir_stream(const path& root, directory_options opts, error_code& ec)
+ : __stream_(INVALID_HANDLE_VALUE), __root_(root) {
+ __stream_ = ::FindFirstFile(root.c_str(), &__data_);
+ if (__stream_ == INVALID_HANDLE_VALUE) {
+ ec = error_code(::GetLastError(), std::generic_category());
+ const bool ignore_permission_denied =
+ bool(opts & directory_options::skip_permission_denied);
+ if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED)
+ ec.clear();
+ return;
+ }
+ }
+
+ ~__dir_stream() noexcept {
+ if (__stream_ == INVALID_HANDLE_VALUE)
+ return;
+ close();
+ }
+
+ bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
+
+ bool advance(error_code& ec) {
+ while (::FindNextFile(__stream_, &__data_)) {
+ if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
+ continue;
+ __entry_.assign(__root_ / __data_.cFileName);
+ return true;
+ }
+ ec = error_code(::GetLastError(), std::generic_category());
+ close();
+ return false;
+ }
+
+private:
+ std::error_code close() noexcept {
+ std::error_code ec;
+ if (!::FindClose(__stream_))
+ ec = error_code(::GetLastError(), std::generic_category());
+ __stream_ = INVALID_HANDLE_VALUE;
+ return ec;
+ }
+
+ HANDLE __stream_{INVALID_HANDLE_VALUE};
+ WIN32_FIND_DATA __data_;
+
+public:
+ path __root_;
+ directory_entry __entry_;
+};
+#else
class __dir_stream {
public:
__dir_stream() = delete;
@@ -117,6 +183,7 @@ public:
path __root_;
directory_entry __entry_;
};
+#endif
// directory_iterator
@@ -149,7 +216,7 @@ directory_iterator& directory_iterator::__increment(error_code *ec)
}
-directory_entry const& directory_iterator::__deref() const {
+directory_entry const& directory_iterator::__dereference() const {
_LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
return __imp_->__entry_;
}
@@ -195,7 +262,7 @@ int recursive_directory_iterator::depth() const {
return __imp_->__stack_.size() - 1;
}
-const directory_entry& recursive_directory_iterator::__deref() const {
+const directory_entry& recursive_directory_iterator::__dereference() const {
return __imp_->__stack_.top().__entry_;
}
@@ -229,24 +296,43 @@ void recursive_directory_iterator::__advance(error_code* ec) {
}
bool recursive_directory_iterator::__try_recursion(error_code *ec) {
-
bool rec_sym =
bool(options() & directory_options::follow_directory_symlink);
+
auto& curr_it = __imp_->__stack_.top();
- if (is_directory(curr_it.__entry_.status()) &&
- (!is_symlink(curr_it.__entry_.symlink_status()) || rec_sym))
- {
- std::error_code m_ec;
+ bool skip_rec = false;
+ std::error_code m_ec;
+ if (!rec_sym) {
+ file_status st = curr_it.__entry_.symlink_status(m_ec);
+ if (m_ec && status_known(st))
+ m_ec.clear();
+ if (m_ec || is_symlink(st) || !is_directory(st))
+ skip_rec = true;
+ } else {
+ file_status st = curr_it.__entry_.status(m_ec);
+ if (m_ec && status_known(st))
+ m_ec.clear();
+ if (m_ec || !is_directory(st))
+ skip_rec = true;
+ }
+
+ if (!skip_rec) {
__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
if (new_it.good()) {
__imp_->__stack_.push(_VSTD::move(new_it));
return true;
}
- if (m_ec) {
- __imp_.reset();
- set_or_throw(m_ec, ec,
- "recursive_directory_iterator::operator++()");
+ }
+ if (m_ec) {
+ const bool allow_eacess = bool(__imp_->__options_
+ & directory_options::skip_permission_denied);
+ if (m_ec.value() == EACCES && allow_eacess) {
+ if (ec) ec->clear();
+ } else {
+ __imp_.reset();
+ set_or_throw(m_ec, ec,
+ "recursive_directory_iterator::operator++()");
}
}
return false;
diff --git a/lib/libcxx/src/experimental/filesystem/filesystem_time_helper.h b/lib/libcxx/src/experimental/filesystem/filesystem_time_helper.h
new file mode 100644
index 00000000000..a60fdef5f0d
--- /dev/null
+++ b/lib/libcxx/src/experimental/filesystem/filesystem_time_helper.h
@@ -0,0 +1,173 @@
+//===----------------------------------------------------------------------===////
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===////
+
+#ifndef FILESYSTEM_TIME_HELPER_H
+#define FILESYSTEM_TIME_HELPER_H
+
+#include "experimental/__config"
+#include "chrono"
+#include "cstdlib"
+#include "climits"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#if !defined(UTIME_OMIT)
+#include <sys/time.h> // for ::utimes as used in __last_write_time
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
+
+namespace time_detail { namespace {
+
+using namespace chrono;
+
+template <class FileTimeT,
+ bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
+struct fs_time_util_base {
+ static constexpr auto max_seconds =
+ duration_cast<seconds>(FileTimeT::duration::max()).count();
+
+ static constexpr auto max_nsec =
+ duration_cast<nanoseconds>(FileTimeT::duration::max() -
+ seconds(max_seconds))
+ .count();
+
+ static constexpr auto min_seconds =
+ duration_cast<seconds>(FileTimeT::duration::min()).count();
+
+ static constexpr auto min_nsec_timespec =
+ duration_cast<nanoseconds>(
+ (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
+ .count();
+
+ // Static assert that these values properly round trip.
+ static_assert((seconds(min_seconds) +
+ duration_cast<microseconds>(nanoseconds(min_nsec_timespec))) -
+ duration_cast<microseconds>(seconds(1)) ==
+ FileTimeT::duration::min(),
+ "");
+};
+
+template <class FileTimeT>
+struct fs_time_util_base<FileTimeT, true> {
+ static const long long max_seconds;
+ static const long long max_nsec;
+ static const long long min_seconds;
+ static const long long min_nsec_timespec;
+};
+
+template <class FileTimeT>
+const long long fs_time_util_base<FileTimeT, true>::max_seconds =
+ duration_cast<seconds>(FileTimeT::duration::max()).count();
+
+template <class FileTimeT>
+const long long fs_time_util_base<FileTimeT, true>::max_nsec =
+ duration_cast<nanoseconds>(FileTimeT::duration::max() -
+ seconds(max_seconds))
+ .count();
+
+template <class FileTimeT>
+const long long fs_time_util_base<FileTimeT, true>::min_seconds =
+ duration_cast<seconds>(FileTimeT::duration::min()).count();
+
+template <class FileTimeT>
+const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
+ duration_cast<nanoseconds>((FileTimeT::duration::min() -
+ seconds(min_seconds)) +
+ seconds(1))
+ .count();
+
+template <class FileTimeT, class TimeT, class TimeSpecT>
+struct fs_time_util : fs_time_util_base<FileTimeT> {
+ using Base = fs_time_util_base<FileTimeT>;
+ using Base::max_nsec;
+ using Base::max_seconds;
+ using Base::min_nsec_timespec;
+ using Base::min_seconds;
+
+public:
+ template <class CType, class ChronoType>
+ static bool checked_set(CType* out, ChronoType time) {
+ using Lim = numeric_limits<CType>;
+ if (time > Lim::max() || time < Lim::min())
+ return false;
+ *out = static_cast<CType>(time);
+ return true;
+ }
+
+ static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
+ if (tm.tv_sec >= 0) {
+ return (tm.tv_sec < max_seconds) ||
+ (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
+ } else if (tm.tv_sec == (min_seconds - 1)) {
+ return tm.tv_nsec >= min_nsec_timespec;
+ } else {
+ return (tm.tv_sec >= min_seconds);
+ }
+ }
+
+ static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
+ auto secs = duration_cast<seconds>(tm.time_since_epoch());
+ auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
+ if (nsecs.count() < 0) {
+ secs = secs + seconds(1);
+ nsecs = nsecs + seconds(1);
+ }
+ using TLim = numeric_limits<TimeT>;
+ if (secs.count() >= 0)
+ return secs.count() <= TLim::max();
+ return secs.count() >= TLim::min();
+ }
+
+ static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
+ convert_timespec(TimeSpecT tm) {
+ auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
+ if (tm.tv_sec >= 0) {
+ auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
+ return FileTimeT(Dur);
+ } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() ==
+ 0) {
+ return FileTimeT(seconds(tm.tv_sec));
+ } else { // tm.tv_sec < 0
+ auto adj_subsec =
+ duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
+ auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
+ return FileTimeT(Dur);
+ }
+ }
+
+ template <class SubSecDurT, class SubSecT>
+ static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
+ FileTimeT tp) {
+ using namespace chrono;
+ auto dur = tp.time_since_epoch();
+ auto sec_dur = duration_cast<seconds>(dur);
+ auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
+ // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
+ if (subsec_dur.count() < 0) {
+ if (sec_dur.count() > min_seconds) {
+ sec_dur -= seconds(1);
+ subsec_dur += seconds(1);
+ } else {
+ subsec_dur = SubSecDurT::zero();
+ }
+ }
+ return checked_set(sec_out, sec_dur.count()) &&
+ checked_set(subsec_out, subsec_dur.count());
+ }
+};
+
+} // end namespace
+} // end namespace time_detail
+
+using time_detail::fs_time_util;
+
+_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
+
+#endif // FILESYSTEM_TIME_HELPER_H
diff --git a/lib/libcxx/src/experimental/filesystem/operations.cpp b/lib/libcxx/src/experimental/filesystem/operations.cpp
index 369996fcbe6..662fa7b8626 100644
--- a/lib/libcxx/src/experimental/filesystem/operations.cpp
+++ b/lib/libcxx/src/experimental/filesystem/operations.cpp
@@ -15,6 +15,8 @@
#include "cstdlib"
#include "climits"
+#include "filesystem_time_helper.h"
+
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
@@ -35,12 +37,9 @@ namespace detail { namespace {
using value_type = path::value_type;
using string_type = path::string_type;
-
-
inline std::error_code capture_errno() {
- _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
- std::error_code m_ec(errno, std::generic_category());
- return m_ec;
+ _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
+ return std::error_code(errno, std::generic_category());
}
void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
@@ -51,7 +50,7 @@ void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
} else {
string msg_s("std::experimental::filesystem::");
msg_s += msg;
- __libcpp_throw(filesystem_error(msg_s, p, p2, m_ec));
+ __throw_filesystem_error(msg_s, p, p2, m_ec);
}
}
@@ -183,20 +182,20 @@ void __copy(const path& from, const path& to, copy_options options,
const bool sym_status2 = bool(options &
copy_options::copy_symlinks);
- std::error_code m_ec;
+ std::error_code m_ec1;
struct ::stat f_st = {};
const file_status f = sym_status || sym_status2
- ? detail::posix_lstat(from, f_st, &m_ec)
- : detail::posix_stat(from, f_st, &m_ec);
- if (m_ec)
- return set_or_throw(m_ec, ec, "copy", from, to);
+ ? detail::posix_lstat(from, f_st, &m_ec1)
+ : detail::posix_stat(from, f_st, &m_ec1);
+ if (m_ec1)
+ return set_or_throw(m_ec1, ec, "copy", from, to);
struct ::stat t_st = {};
- const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec)
- : detail::posix_stat(to, t_st, &m_ec);
+ const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
+ : detail::posix_stat(to, t_st, &m_ec1);
if (not status_known(t))
- return set_or_throw(m_ec, ec, "copy", from, to);
+ return set_or_throw(m_ec1, ec, "copy", from, to);
if (!exists(f) || is_other(f) || is_other(t)
|| (is_directory(f) && is_regular_file(t))
@@ -236,12 +235,11 @@ void __copy(const path& from, const path& to, copy_options options,
}
return;
}
- else if (is_directory(f)) {
- if (not bool(copy_options::recursive & options) &&
- bool(copy_options::__in_recursive_copy & options))
- {
- return;
- }
+ else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
+ return set_or_throw(make_error_code(errc::is_a_directory), ec, "copy");
+ }
+ else if (is_directory(f) && (bool(copy_options::recursive & options) ||
+ copy_options::none == options)) {
if (!exists(t)) {
// create directory to with attributes from 'from'.
@@ -251,9 +249,9 @@ void __copy(const path& from, const path& to, copy_options options,
directory_iterator it = ec ? directory_iterator(from, *ec)
: directory_iterator(from);
if (ec && *ec) { return; }
- std::error_code m_ec;
- for (; it != directory_iterator(); it.increment(m_ec)) {
- if (m_ec) return set_or_throw(m_ec, ec, "copy", from, to);
+ std::error_code m_ec2;
+ for (; it != directory_iterator(); it.increment(m_ec2)) {
+ if (m_ec2) return set_or_throw(m_ec2, ec, "copy", from, to);
__copy(it->path(), to / it->path().filename(),
options | copy_options::__in_recursive_copy, ec);
if (ec && *ec) { return; }
@@ -283,6 +281,10 @@ bool __copy_file(const path& from, const path& to, copy_options options,
}
const bool to_exists = exists(to_st);
+ if (to_exists && !is_regular_file(to_st)) {
+ set_or_throw(make_error_code(errc::not_supported), ec, "copy_file", from, to);
+ return false;
+ }
if (to_exists && bool(copy_options::skip_existing & options)) {
return false;
}
@@ -303,6 +305,8 @@ bool __copy_file(const path& from, const path& to, copy_options options,
set_or_throw(make_error_code(errc::file_exists), ec, "copy", from, to);
return false;
}
+
+ _LIBCPP_UNREACHABLE();
}
void __copy_symlink(const path& existing_symlink, const path& new_symlink,
@@ -424,17 +428,20 @@ void __current_path(const path& p, std::error_code *ec) {
bool __equivalent(const path& p1, const path& p2, std::error_code *ec)
{
+ auto make_unsupported_error = [&]() {
+ set_or_throw(make_error_code(errc::not_supported), ec,
+ "equivalent", p1, p2);
+ return false;
+ };
std::error_code ec1, ec2;
struct ::stat st1 = {};
struct ::stat st2 = {};
auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
+ if (!exists(s1))
+ return make_unsupported_error();
auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
-
- if ((!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2))) {
- set_or_throw(make_error_code(errc::not_supported), ec,
- "equivalent", p1, p2);
- return false;
- }
+ if (!exists(s2))
+ return make_unsupported_error();
if (ec) ec->clear();
return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
}
@@ -476,55 +483,50 @@ bool __fs_is_empty(const path& p, std::error_code *ec)
std::error_code m_ec;
struct ::stat pst;
auto st = detail::posix_stat(p, pst, &m_ec);
- if (is_directory(st))
- return directory_iterator(p) == directory_iterator{};
+ if (m_ec) {
+ set_or_throw(m_ec, ec, "is_empty", p);
+ return false;
+ }
+ else if (!is_directory(st) && !is_regular_file(st)) {
+ m_ec = make_error_code(errc::not_supported);
+ set_or_throw(m_ec, ec, "is_empty");
+ return false;
+ }
+ else if (is_directory(st)) {
+ auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
+ if (ec && *ec)
+ return false;
+ return it == directory_iterator{};
+ }
else if (is_regular_file(st))
return static_cast<std::uintmax_t>(pst.st_size) == 0;
- // else
- set_or_throw(m_ec, ec, "is_empty", p);
- return false;
+
+ _LIBCPP_UNREACHABLE();
}
namespace detail { namespace {
-template <class CType, class ChronoType>
-bool checked_set(CType* out, ChronoType time) {
- using Lim = numeric_limits<CType>;
- if (time > Lim::max() || time < Lim::min())
- return false;
- *out = static_cast<CType>(time);
- return true;
-}
-
-constexpr long long min_seconds = file_time_type::duration::min().count()
- / file_time_type::period::den;
-
-template <class SubSecDurT, class SubSecT>
-bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp) {
- using namespace chrono;
- auto dur = tp.time_since_epoch();
- auto sec_dur = duration_cast<seconds>(dur);
- auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
- // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
- if (subsec_dur.count() < 0) {
- if (sec_dur.count() > min_seconds) {
+using TimeSpec = struct timespec;
+using StatT = struct stat;
- sec_dur -= seconds(1);
- subsec_dur += seconds(1);
- } else {
- subsec_dur = SubSecDurT::zero();
- }
- }
- return checked_set(sec_out, sec_dur.count())
- && checked_set(subsec_out, subsec_dur.count());
-}
+#if defined(__APPLE__)
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
+__attribute__((unused)) // Suppress warning
+TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
+#else
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
+__attribute__((unused)) // Suppress warning
+TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
+#endif
}} // end namespace detail
+using FSTime = fs_time_util<file_time_type, time_t, struct timespec>;
file_time_type __last_write_time(const path& p, std::error_code *ec)
{
+ using namespace ::std::chrono;
std::error_code m_ec;
struct ::stat st;
detail::posix_stat(p, st, &m_ec);
@@ -533,7 +535,13 @@ file_time_type __last_write_time(const path& p, std::error_code *ec)
return file_time_type::min();
}
if (ec) ec->clear();
- return file_time_type::clock::from_time_t(st.st_mtime);
+ auto ts = detail::extract_mtime(st);
+ if (!FSTime::is_representable(ts)) {
+ set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
+ "last_write_time", p);
+ return file_time_type::min();
+ }
+ return FSTime::convert_timespec(ts);
}
void __last_write_time(const path& p, file_time_type new_time,
@@ -554,10 +562,11 @@ void __last_write_time(const path& p, file_time_type new_time,
set_or_throw(m_ec, ec, "last_write_time", p);
return;
}
+ auto atime = detail::extract_atime(st);
struct ::timeval tbuf[2];
- tbuf[0].tv_sec = st.st_atime;
- tbuf[0].tv_usec = 0;
- const bool overflowed = !detail::set_times_checked<microseconds>(
+ tbuf[0].tv_sec = atime.tv_sec;
+ tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
+ const bool overflowed = !FSTime::set_times_checked<microseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
if (overflowed) {
@@ -573,7 +582,7 @@ void __last_write_time(const path& p, file_time_type new_time,
tbuf[0].tv_sec = 0;
tbuf[0].tv_nsec = UTIME_OMIT;
- const bool overflowed = !detail::set_times_checked<nanoseconds>(
+ const bool overflowed = !FSTime::set_times_checked<nanoseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_nsec, new_time);
if (overflowed) {
set_or_throw(make_error_code(errc::invalid_argument),
@@ -701,7 +710,7 @@ void __rename(const path& from, const path& to, std::error_code *ec) {
}
void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) {
- if (::truncate(p.c_str(), static_cast<long>(size)) == -1)
+ if (::truncate(p.c_str(), static_cast<::off_t>(size)) == -1)
set_or_throw(ec, "resize_file", p);
else if (ec)
ec->clear();
@@ -720,7 +729,7 @@ space_info __space(const path& p, std::error_code *ec) {
// Multiply with overflow checking.
auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
out = other * m_svfs.f_frsize;
- if (out / other != m_svfs.f_frsize || other == 0)
+ if (other == 0 || out / other != m_svfs.f_frsize)
out = static_cast<std::uintmax_t>(-1);
};
do_mult(si.capacity, m_svfs.f_blocks);
@@ -742,23 +751,28 @@ path __system_complete(const path& p, std::error_code *ec) {
return absolute(p, current_path());
}
-path __temp_directory_path(std::error_code *ec) {
- const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
- const char* ret = nullptr;
- for (auto & ep : env_paths) {
- if ((ret = std::getenv(ep)))
- break;
- }
- path p(ret ? ret : "/tmp");
- std::error_code m_ec;
- if (is_directory(p, m_ec)) {
- if (ec) ec->clear();
- return p;
- }
+path __temp_directory_path(std::error_code* ec) {
+ const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
+ const char* ret = nullptr;
+
+ for (auto& ep : env_paths)
+ if ((ret = std::getenv(ep)))
+ break;
+ if (ret == nullptr)
+ ret = "/tmp";
+
+ path p(ret);
+ std::error_code m_ec;
+ if (!exists(p, m_ec) || !is_directory(p, m_ec)) {
if (!m_ec || m_ec == make_error_code(errc::no_such_file_or_directory))
- m_ec = make_error_code(errc::not_a_directory);
+ m_ec = make_error_code(errc::not_a_directory);
set_or_throw(m_ec, ec, "temp_directory_path");
return {};
+ }
+
+ if (ec)
+ ec->clear();
+ return p;
}
// An absolute path is composed according to the table in [fs.op.absolute].
diff --git a/lib/libcxx/src/experimental/filesystem/path.cpp b/lib/libcxx/src/experimental/filesystem/path.cpp
index 38c449832f6..dd4026cfe13 100644
--- a/lib/libcxx/src/experimental/filesystem/path.cpp
+++ b/lib/libcxx/src/experimental/filesystem/path.cpp
@@ -7,241 +7,284 @@
//
//===----------------------------------------------------------------------===//
#include "experimental/filesystem"
-#include "experimental/string_view"
+#include "string_view"
#include "utility"
-_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
-
-_LIBCPP_CONSTEXPR path::value_type path::preferred_separator;
-
namespace { namespace parser
{
+using namespace std;
+using namespace std::experimental::filesystem;
+
+using string_view_t = path::__string_view;
+using string_view_pair = pair<string_view_t, string_view_t>;
+using PosPtr = path::value_type const*;
+
+struct PathParser {
+ enum ParserState : unsigned char {
+ // Zero is a special sentinel value used by default constructed iterators.
+ PS_BeforeBegin = 1,
+ PS_InRootName,
+ PS_InRootDir,
+ PS_InFilenames,
+ PS_InTrailingSep,
+ PS_AtEnd
+ };
-using string_type = string_view;
-using value_type = path::value_type;
-
-using string_view_pair = pair<string_view, string_view>;
-
-// status reporting
-constexpr size_t npos = static_cast<size_t>(-1);
-
-inline bool good(size_t pos) { return pos != npos; }
-
-// lexical elements
-constexpr value_type preferred_separator = path::preferred_separator;
-constexpr value_type const * preferred_separator_str = "/";
-constexpr value_type const * dot = ".";
-
-// forward //
-bool is_separator(string_type const &, size_t);
-bool is_root_name(const string_type&, size_t);
-bool is_root_directory(string_type const &, size_t);
-bool is_trailing_separator(string_type const &, size_t);
-
-size_t start_of(string_type const &, size_t);
-size_t end_of(string_type const &, size_t);
-
-size_t root_name_start(const string_type& s);
-size_t root_name_end(const string_type&);
-
-size_t root_directory_start(string_type const &);
-size_t root_directory_end(string_type const &);
+ const string_view_t Path;
+ string_view_t RawEntry;
+ ParserState State;
-string_view_pair separate_filename(string_type const &);
-string_view extract_raw(string_type const &, size_t);
-string_view extract_preferred(string_type const &, size_t);
+private:
+ PathParser(string_view_t P, ParserState State) noexcept
+ : Path(P), State(State) {}
-inline bool is_separator(const string_type& s, size_t pos) {
- return (pos < s.size() && s[pos] == preferred_separator);
-}
+public:
+ PathParser(string_view_t P, string_view_t E, unsigned char S)
+ : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
+ // S cannot be '0' or PS_BeforeBegin.
+ }
-inline bool is_root_name(const string_type& s, size_t pos) {
- return good(pos) && pos == 0 ? root_name_start(s) == pos : false;
-}
+ static PathParser CreateBegin(string_view_t P) noexcept {
+ PathParser PP(P, PS_BeforeBegin);
+ PP.increment();
+ return PP;
+ }
-inline bool is_root_directory(const string_type& s, size_t pos) {
- return good(pos) ? root_directory_start(s) == pos : false;
-}
+ static PathParser CreateEnd(string_view_t P) noexcept {
+ PathParser PP(P, PS_AtEnd);
+ return PP;
+ }
-inline bool is_trailing_separator(const string_type& s, size_t pos) {
- return (pos < s.size() && is_separator(s, pos) &&
- end_of(s, pos) == s.size()-1 &&
- !is_root_directory(s, pos) && !is_root_name(s, pos));
-}
+ PosPtr peek() const noexcept {
+ auto TkEnd = getNextTokenStartPos();
+ auto End = getAfterBack();
+ return TkEnd == End ? nullptr : TkEnd;
+ }
-size_t start_of(const string_type& s, size_t pos) {
- if (pos >= s.size()) return npos;
- bool in_sep = (s[pos] == preferred_separator);
- while (pos - 1 < s.size() &&
- (s[pos-1] == preferred_separator) == in_sep)
- { --pos; }
- if (pos == 2 && !in_sep && s[0] == preferred_separator &&
- s[1] == preferred_separator)
- { return 0; }
- return pos;
-}
+ void increment() noexcept {
+ const PosPtr End = getAfterBack();
+ const PosPtr Start = getNextTokenStartPos();
+ if (Start == End)
+ return makeState(PS_AtEnd);
+
+ switch (State) {
+ case PS_BeforeBegin: {
+ PosPtr TkEnd = consumeSeparator(Start, End);
+ // If we consumed exactly two separators we have a root name.
+ if (TkEnd && TkEnd == Start + 2) {
+ // FIXME Do we need to consume a name or is '//' a root name on its own?
+ // what about '//.', '//..', '//...'?
+ auto NameEnd = consumeName(TkEnd, End);
+ if (NameEnd)
+ TkEnd = NameEnd;
+ return makeState(PS_InRootName, Start, TkEnd);
+ }
+ else if (TkEnd)
+ return makeState(PS_InRootDir, Start, TkEnd);
+ else
+ return makeState(PS_InFilenames, Start, consumeName(Start, End));
+ }
-size_t end_of(const string_type& s, size_t pos) {
- if (pos >= s.size()) return npos;
- // special case for root name
- if (pos == 0 && is_root_name(s, pos)) return root_name_end(s);
- bool in_sep = (s[pos] == preferred_separator);
- while (pos + 1 < s.size() && (s[pos+1] == preferred_separator) == in_sep)
- { ++pos; }
- return pos;
-}
+ case PS_InRootName:
+ return makeState(PS_InRootDir, Start, consumeSeparator(Start, End));
+ case PS_InRootDir:
+ return makeState(PS_InFilenames, Start, consumeName(Start, End));
+
+ case PS_InFilenames: {
+ PosPtr SepEnd = consumeSeparator(Start, End);
+ if (SepEnd != End) {
+ PosPtr TkEnd = consumeName(SepEnd, End);
+ if (TkEnd)
+ return makeState(PS_InFilenames, SepEnd, TkEnd);
+ }
+ return makeState(PS_InTrailingSep, Start, SepEnd);
+ }
-inline size_t root_name_start(const string_type& s) {
- return good(root_name_end(s)) ? 0 : npos;
-}
+ case PS_InTrailingSep:
+ return makeState(PS_AtEnd);
-size_t root_name_end(const string_type& s) {
- if (s.size() < 2 || s[0] != preferred_separator
- || s[1] != preferred_separator) {
- return npos;
+ case PS_AtEnd:
+ _LIBCPP_UNREACHABLE();
}
- if (s.size() == 2) {
- return 1;
+ }
+
+ void decrement() noexcept {
+ const PosPtr REnd = getBeforeFront();
+ const PosPtr RStart = getCurrentTokenStartPos() - 1;
+
+ switch (State) {
+ case PS_AtEnd: {
+ // Try to consume a trailing separator or root directory first.
+ if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
+ if (SepEnd == REnd)
+ return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
+ Path.data(), RStart + 1);
+ // Check if we're seeing the root directory separator
+ auto PP = CreateBegin(Path);
+ bool InRootDir = PP.State == PS_InRootName &&
+ &PP.RawEntry.back() == SepEnd;
+ return makeState(InRootDir ? PS_InRootDir : PS_InTrailingSep,
+ SepEnd + 1, RStart + 1);
+ } else {
+ PosPtr TkStart = consumeName(RStart, REnd);
+ if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd)
+ return makeState(PS_InRootName, Path.data(), RStart + 1);
+ else
+ return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
+ }
}
- size_t index = 2; // current position
- if (s[index] == preferred_separator) {
- return npos;
+ case PS_InTrailingSep:
+ return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
+ case PS_InFilenames: {
+ PosPtr SepEnd = consumeSeparator(RStart, REnd);
+ if (SepEnd == REnd)
+ return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
+ Path.data(), RStart + 1);
+ PosPtr TkEnd = consumeName(SepEnd, REnd);
+ if (TkEnd == REnd + 2 && consumeSeparator(TkEnd, REnd) == REnd)
+ return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
+ return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
}
- while (index + 1 < s.size() && s[index+1] != preferred_separator) {
- ++index;
+ case PS_InRootDir:
+ return makeState(PS_InRootName, Path.data(), RStart + 1);
+ case PS_InRootName:
+ case PS_BeforeBegin:
+ _LIBCPP_UNREACHABLE();
}
- return index;
-}
-
-size_t root_directory_start(const string_type& s) {
- size_t e = root_name_end(s);
- if (!good(e))
- return is_separator(s, 0) ? 0 : npos;
- return is_separator(s, e + 1) ? e + 1 : npos;
-}
-
-size_t root_directory_end(const string_type& s) {
- size_t st = root_directory_start(s);
- if (!good(st)) return npos;
- size_t index = st;
- while (index + 1 < s.size() && s[index + 1] == preferred_separator)
- { ++index; }
- return index;
-}
-
-string_view_pair separate_filename(string_type const & s) {
- if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
- auto pos = s.find_last_of('.');
- if (pos == string_type::npos) return string_view_pair{s, string_view{}};
- return string_view_pair{s.substr(0, pos), s.substr(pos)};
-}
-
-inline string_view extract_raw(const string_type& s, size_t pos) {
- size_t end_i = end_of(s, pos);
- if (!good(end_i)) return string_view{};
- return string_view(s).substr(pos, end_i - pos + 1);
-}
-
-string_view extract_preferred(const string_type& s, size_t pos) {
- string_view raw = extract_raw(s, pos);
- if (raw.empty())
- return raw;
- if (is_trailing_separator(s, pos))
- return string_view{dot};
- if (is_separator(s, pos) && !is_root_name(s, pos))
- return string_view(preferred_separator_str);
- return raw;
-}
-
-}} // namespace parser
-
-
-////////////////////////////////////////////////////////////////////////////////
-// path_view_iterator
-////////////////////////////////////////////////////////////////////////////////
-namespace {
-
-struct path_view_iterator {
- const string_view __s_;
- size_t __pos_;
+ }
- explicit path_view_iterator(string_view const& __s) : __s_(__s), __pos_(__s_.empty() ? parser::npos : 0) {}
- explicit path_view_iterator(string_view const& __s, size_t __p) : __s_(__s), __pos_(__p) {}
+ /// \brief Return a view with the "preferred representation" of the current
+ /// element. For example trailing separators are represented as a '.'
+ string_view_t operator*() const noexcept {
+ switch (State) {
+ case PS_BeforeBegin:
+ case PS_AtEnd:
+ return "";
+ case PS_InRootDir:
+ return "/";
+ case PS_InTrailingSep:
+ return ".";
+ case PS_InRootName:
+ case PS_InFilenames:
+ return RawEntry;
+ }
+ _LIBCPP_UNREACHABLE();
+ }
- string_view operator*() const {
- return parser::extract_preferred(__s_, __pos_);
+ explicit operator bool() const noexcept {
+ return State != PS_BeforeBegin && State != PS_AtEnd;
}
- path_view_iterator& operator++() {
+ PathParser& operator++() noexcept {
increment();
return *this;
}
- path_view_iterator& operator--() {
+ PathParser& operator--() noexcept {
decrement();
return *this;
}
- void increment() {
- if (__pos_ == parser::npos) return;
- while (! set_position(parser::end_of(__s_, __pos_)+1))
- ;
- return;
+private:
+ void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
+ State = NewState;
+ RawEntry = string_view_t(Start, End - Start);
+ }
+ void makeState(ParserState NewState) noexcept {
+ State = NewState;
+ RawEntry = {};
}
- void decrement() {
- if (__pos_ == 0) {
- set_position(0);
- }
- else if (__pos_ == parser::npos) {
- auto const str_size = __s_.size();
- set_position(parser::start_of(
- __s_, str_size != 0 ? str_size - 1 : str_size));
- } else {
- while (!set_position(parser::start_of(__s_, __pos_-1)))
- ;
- }
+ PosPtr getAfterBack() const noexcept {
+ return Path.data() + Path.size();
+ }
+
+ PosPtr getBeforeFront() const noexcept {
+ return Path.data() - 1;
}
- bool set_position(size_t pos) {
- if (pos >= __s_.size()) {
- __pos_ = parser::npos;
- } else {
- __pos_ = pos;
+ /// \brief Return a pointer to the first character after the currently
+ /// lexed element.
+ PosPtr getNextTokenStartPos() const noexcept {
+ switch (State) {
+ case PS_BeforeBegin:
+ return Path.data();
+ case PS_InRootName:
+ case PS_InRootDir:
+ case PS_InFilenames:
+ return &RawEntry.back() + 1;
+ case PS_InTrailingSep:
+ case PS_AtEnd:
+ return getAfterBack();
}
- return valid_iterator_position();
+ _LIBCPP_UNREACHABLE();
}
- bool valid_iterator_position() const {
- if (__pos_ == parser::npos) return true; // end position is valid
- return (!parser::is_separator (__s_, __pos_) ||
- parser::is_root_directory (__s_, __pos_) ||
- parser::is_trailing_separator(__s_, __pos_) ||
- parser::is_root_name (__s_, __pos_));
+ /// \brief Return a pointer to the first character in the currently lexed
+ /// element.
+ PosPtr getCurrentTokenStartPos() const noexcept {
+ switch (State) {
+ case PS_BeforeBegin:
+ case PS_InRootName:
+ return &Path.front();
+ case PS_InRootDir:
+ case PS_InFilenames:
+ case PS_InTrailingSep:
+ return &RawEntry.front();
+ case PS_AtEnd:
+ return &Path.back() + 1;
+ }
+ _LIBCPP_UNREACHABLE();
}
- bool is_end() const { return __pos_ == parser::npos; }
+ PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
+ if (P == End || *P != '/')
+ return nullptr;
+ const int Inc = P < End ? 1 : -1;
+ P += Inc;
+ while (P != End && *P == '/')
+ P += Inc;
+ return P;
+ }
- inline bool operator==(path_view_iterator const& __p) {
- return __pos_ == __p.__pos_;
+ PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
+ if (P == End || *P == '/')
+ return nullptr;
+ const int Inc = P < End ? 1 : -1;
+ P += Inc;
+ while (P != End && *P != '/')
+ P += Inc;
+ return P;
}
};
-path_view_iterator pbegin(path const& p) {
- return path_view_iterator(p.native());
+string_view_pair separate_filename(string_view_t const & s) {
+ if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
+ auto pos = s.find_last_of('.');
+ if (pos == string_view_t::npos)
+ return string_view_pair{s, string_view_t{}};
+ return string_view_pair{s.substr(0, pos), s.substr(pos)};
}
-path_view_iterator pend(path const& p) {
- path_view_iterator __p(p.native());
- __p.__pos_ = parser::npos;
- return __p;
+string_view_t createView(PosPtr S, PosPtr E) noexcept {
+ return {S, static_cast<size_t>(E - S) + 1};
}
-} // end namespace
+}} // namespace parser
+
+_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
+
+using parser::string_view_t;
+using parser::string_view_pair;
+using parser::PathParser;
+using parser::createView;
+
///////////////////////////////////////////////////////////////////////////////
// path definitions
///////////////////////////////////////////////////////////////////////////////
+constexpr path::value_type path::preferred_separator;
+
path & path::replace_extension(path const & replacement)
{
path p = extension();
@@ -260,131 +303,145 @@ path & path::replace_extension(path const & replacement)
///////////////////////////////////////////////////////////////////////////////
// path.decompose
-string_view path::__root_name() const
+string_view_t path::__root_name() const
{
- return parser::is_root_name(__pn_, 0)
- ? parser::extract_preferred(__pn_, 0)
- : string_view{};
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (PP.State == PathParser::PS_InRootName)
+ return *PP;
+ return {};
}
-string_view path::__root_directory() const
+string_view_t path::__root_directory() const
{
- auto start_i = parser::root_directory_start(__pn_);
- if(!parser::good(start_i)) {
- return {};
- }
- return parser::extract_preferred(__pn_, start_i);
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (PP.State == PathParser::PS_InRootName)
+ ++PP;
+ if (PP.State == PathParser::PS_InRootDir)
+ return *PP;
+ return {};
}
-string_view path::__relative_path() const
+string_view_t path::__root_path_raw() const
{
- if (empty()) {
- return {__pn_};
- }
- auto end_i = parser::root_directory_end(__pn_);
- if (not parser::good(end_i)) {
- end_i = parser::root_name_end(__pn_);
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (PP.State == PathParser::PS_InRootName) {
+ auto NextCh = PP.peek();
+ if (NextCh && *NextCh == '/') {
+ ++PP;
+ return createView(__pn_.data(), &PP.RawEntry.back());
+ }
+ return PP.RawEntry;
}
- if (not parser::good(end_i)) {
- return {__pn_};
- }
- return string_view(__pn_).substr(end_i+1);
+ if (PP.State == PathParser::PS_InRootDir)
+ return *PP;
+ return {};
}
-string_view path::__parent_path() const
+string_view_t path::__relative_path() const
{
- if (empty() || pbegin(*this) == --pend(*this)) {
- return {};
- }
- auto end_it = --(--pend(*this));
- auto end_i = parser::end_of(__pn_, end_it.__pos_);
- return string_view(__pn_).substr(0, end_i+1);
+ auto PP = PathParser::CreateBegin(__pn_);
+ while (PP.State <= PathParser::PS_InRootDir)
+ ++PP;
+ if (PP.State == PathParser::PS_AtEnd)
+ return {};
+ return createView(PP.RawEntry.data(), &__pn_.back());
}
-string_view path::__filename() const
+string_view_t path::__parent_path() const
{
- return empty() ? string_view{} : *--pend(*this);
+ if (empty())
+ return {};
+ auto PP = PathParser::CreateEnd(__pn_);
+ --PP;
+ if (PP.RawEntry.data() == __pn_.data())
+ return {};
+ --PP;
+ return createView(__pn_.data(), &PP.RawEntry.back());
}
-string_view path::__stem() const
+string_view_t path::__filename() const
+{
+ if (empty()) return {};
+ return *(--PathParser::CreateEnd(__pn_));
+}
+
+string_view_t path::__stem() const
{
return parser::separate_filename(__filename()).first;
}
-string_view path::__extension() const
+string_view_t path::__extension() const
{
return parser::separate_filename(__filename()).second;
}
////////////////////////////////////////////////////////////////////////////
// path.comparisons
-int path::__compare(const value_type* __s) const {
- path_view_iterator thisIter(this->native());
- path_view_iterator sIter(__s);
- while (!thisIter.is_end() && !sIter.is_end()) {
- int res = (*thisIter).compare(*sIter);
+int path::__compare(string_view_t __s) const {
+ auto PP = PathParser::CreateBegin(__pn_);
+ auto PP2 = PathParser::CreateBegin(__s);
+ while (PP && PP2) {
+ int res = (*PP).compare(*PP2);
if (res != 0) return res;
- ++thisIter; ++sIter;
+ ++PP; ++PP2;
}
- if (thisIter.is_end() && sIter.is_end())
+ if (PP.State == PP2.State && PP.State == PathParser::PS_AtEnd)
return 0;
- if (thisIter.is_end())
+ if (PP.State == PathParser::PS_AtEnd)
return -1;
return 1;
}
////////////////////////////////////////////////////////////////////////////
// path.nonmembers
-size_t hash_value(const path& __p) _NOEXCEPT {
- path_view_iterator thisIter(__p.native());
- struct HashPairT {
- size_t first;
- size_t second;
- };
- HashPairT hp = {0, 0};
- std::hash<string_view> hasher;
- std::__scalar_hash<decltype(hp)> pair_hasher;
- while (!thisIter.is_end()) {
- hp.second = hasher(*thisIter);
- hp.first = pair_hasher(hp);
- ++thisIter;
+size_t hash_value(const path& __p) noexcept {
+ auto PP = PathParser::CreateBegin(__p.native());
+ size_t hash_value = 0;
+ std::hash<string_view_t> hasher;
+ while (PP) {
+ hash_value = __hash_combine(hash_value, hasher(*PP));
+ ++PP;
}
- return hp.first;
+ return hash_value;
}
////////////////////////////////////////////////////////////////////////////
// path.itr
path::iterator path::begin() const
{
- path_view_iterator pit = pbegin(*this);
+ auto PP = PathParser::CreateBegin(__pn_);
iterator it;
it.__path_ptr_ = this;
- it.__pos_ = pit.__pos_;
- it.__elem_.__assign_view(*pit);
+ it.__state_ = PP.State;
+ it.__entry_ = PP.RawEntry;
+ it.__stashed_elem_.__assign_view(*PP);
return it;
}
path::iterator path::end() const
{
iterator it{};
+ it.__state_ = PathParser::PS_AtEnd;
it.__path_ptr_ = this;
- it.__pos_ = parser::npos;
return it;
}
path::iterator& path::iterator::__increment() {
- path_view_iterator it(__path_ptr_->native(), __pos_);
- it.increment();
- __pos_ = it.__pos_;
- __elem_.__assign_view(*it);
+ static_assert(__at_end == PathParser::PS_AtEnd, "");
+ PathParser PP(__path_ptr_->native(), __entry_, __state_);
+ ++PP;
+ __state_ = PP.State;
+ __entry_ = PP.RawEntry;
+ __stashed_elem_.__assign_view(*PP);
return *this;
}
path::iterator& path::iterator::__decrement() {
- path_view_iterator it(__path_ptr_->native(), __pos_);
- it.decrement();
- __pos_ = it.__pos_;
- __elem_.__assign_view(*it);
+ PathParser PP(__path_ptr_->native(), __entry_, __state_);
+ --PP;
+ __state_ = PP.State;
+ __entry_ = PP.RawEntry;
+ __stashed_elem_.__assign_view(*PP);
return *this;
}