diff options
Diffstat (limited to 'src/experimental/filesystem')
-rw-r--r-- | src/experimental/filesystem/filesystem_time_helper.h | 173 | ||||
-rw-r--r-- | src/experimental/filesystem/operations.cpp | 167 |
2 files changed, 190 insertions, 150 deletions
diff --git a/src/experimental/filesystem/filesystem_time_helper.h b/src/experimental/filesystem/filesystem_time_helper.h new file mode 100644 index 000000000000..a60fdef5f0d9 --- /dev/null +++ b/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/src/experimental/filesystem/operations.cpp b/src/experimental/filesystem/operations.cpp index e9bc0eb6e30e..641a3c53636d 100644 --- a/src/experimental/filesystem/operations.cpp +++ b/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> @@ -426,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); } @@ -502,17 +507,6 @@ bool __fs_is_empty(const path& p, std::error_code *ec) namespace detail { namespace { -using namespace std::chrono; - -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; -} - using TimeSpec = struct timespec; using StatT = struct stat; @@ -525,137 +519,10 @@ __attribute__((unused)) // Suppress warning TimeSpec extract_atime(StatT const& st) { return st.st_atim; } #endif -constexpr auto max_seconds = duration_cast<seconds>( - file_time_type::duration::max()).count(); - -constexpr auto max_nsec = duration_cast<nanoseconds>( - file_time_type::duration::max() - seconds(max_seconds)).count(); - -constexpr auto min_seconds = duration_cast<seconds>( - file_time_type::duration::min()).count(); - -constexpr auto min_nsec_timespec = duration_cast<nanoseconds>( - (file_time_type::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)) - == file_time_type::duration::min(), ""); - -constexpr auto max_time_t = numeric_limits<time_t>::max(); -constexpr auto min_time_t = numeric_limits<time_t>::min(); - -#if !defined(__LP64__) && defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#endif - -_LIBCPP_CONSTEXPR_AFTER_CXX11 -bool is_representable(TimeSpec const& 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); - } -} -#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR -#if defined(__LP64__) -static_assert(is_representable({max_seconds, max_nsec}), ""); -static_assert(!is_representable({max_seconds + 1, 0}), ""); -static_assert(!is_representable({max_seconds, max_nsec + 1}), ""); -static_assert(!is_representable({max_time_t, 0}), ""); -static_assert(is_representable({min_seconds, 0}), ""); -static_assert(is_representable({min_seconds - 1, min_nsec_timespec}), ""); -static_assert(is_representable({min_seconds - 1, min_nsec_timespec + 1}), ""); -static_assert(!is_representable({min_seconds - 1, min_nsec_timespec - 1}), ""); -static_assert(!is_representable({min_time_t, 999999999}), ""); -#else -static_assert(is_representable({max_time_t, 999999999}), ""); -static_assert(is_representable({max_time_t, 1000000000}), ""); -static_assert(is_representable({min_time_t, 0}), ""); -#endif -#endif - -_LIBCPP_CONSTEXPR_AFTER_CXX11 -bool is_representable(file_time_type const& 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<time_t>; - if (secs.count() >= 0) - return secs.count() <= TLim::max(); - return secs.count() >= TLim::min(); -} -#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR -#if defined(__LP64__) -static_assert(is_representable(file_time_type::max()), ""); -static_assert(is_representable(file_time_type::min()), ""); -#else -static_assert(!is_representable(file_time_type::max()), ""); -static_assert(!is_representable(file_time_type::min()), ""); -static_assert(is_representable(file_time_type(seconds(max_time_t))), ""); -static_assert(is_representable(file_time_type(seconds(min_time_t))), ""); -#endif -#endif - -_LIBCPP_CONSTEXPR_AFTER_CXX11 -file_time_type convert_timespec(TimeSpec const& 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 file_time_type(Dur); - } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() == 0) { - return file_time_type(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 file_time_type(Dur); - } -} -#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR -#if defined(__LP64__) -static_assert(convert_timespec({max_seconds, max_nsec}) == file_time_type::max(), ""); -static_assert(convert_timespec({max_seconds, max_nsec - 1}) < file_time_type::max(), ""); -static_assert(convert_timespec({max_seconds - 1, 999999999}) < file_time_type::max(), ""); -static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec}) == file_time_type::min(), ""); -static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec + 1}) > file_time_type::min(), ""); -static_assert(convert_timespec({min_seconds , 0}) > file_time_type::min(), ""); -#else -// FIXME add tests for 32 bit builds -#endif -#endif - -#if !defined(__LP64__) && defined(__clang__) -#pragma clang diagnostic pop -#endif - -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) { - 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 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; @@ -668,12 +535,12 @@ file_time_type __last_write_time(const path& p, std::error_code *ec) } if (ec) ec->clear(); auto ts = detail::extract_mtime(st); - if (!detail::is_representable(ts)) { + if (!FSTime::is_representable(ts)) { set_or_throw(error_code(EOVERFLOW, generic_category()), ec, "last_write_time", p); return file_time_type::min(); } - return detail::convert_timespec(ts); + return FSTime::convert_timespec(ts); } void __last_write_time(const path& p, file_time_type new_time, @@ -698,7 +565,7 @@ void __last_write_time(const path& p, file_time_type new_time, struct ::timeval tbuf[2]; tbuf[0].tv_sec = atime.tv_sec; tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count(); - const bool overflowed = !detail::set_times_checked<microseconds>( + const bool overflowed = !FSTime::set_times_checked<microseconds>( &tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time); if (overflowed) { @@ -714,7 +581,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), |