From: Eric Fiselier Date: Mon, 10 Oct 2016 04:22:58 +0000 (+0000) Subject: Remove use of int128_t inside the filesystem implementation X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a52ce7fd039a6ca0625aa046ce7bb3ed50b9c813;p=platform%2Fupstream%2Fllvm.git Remove use of int128_t inside the filesystem implementation llvm-svn: 283712 --- diff --git a/libcxx/src/experimental/filesystem/operations.cpp b/libcxx/src/experimental/filesystem/operations.cpp index 6073d3a..7959400 100644 --- a/libcxx/src/experimental/filesystem/operations.cpp +++ b/libcxx/src/experimental/filesystem/operations.cpp @@ -488,6 +488,8 @@ bool __fs_is_empty(const path& p, std::error_code *ec) namespace detail { namespace { +using namespace std::chrono; + template bool checked_set(CType* out, ChronoType time) { using Lim = numeric_limits; @@ -497,8 +499,120 @@ bool checked_set(CType* out, ChronoType time) { return true; } -constexpr long long min_seconds = file_time_type::duration::min().count() - / file_time_type::period::den; +using TimeSpec = struct ::timespec; +using StatT = struct ::stat; + +#if defined(__APPLE__) +TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; } +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 + +constexpr auto max_seconds = duration_cast( + file_time_type::duration::max()).count(); + +constexpr auto max_nsec = duration_cast( + file_time_type::duration::max() - seconds(max_seconds)).count(); + +constexpr auto min_seconds = duration_cast( + file_time_type::duration::min()).count(); + +constexpr auto min_nsec_timespec = duration_cast( + (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(nanoseconds(min_nsec_timespec))) + - duration_cast(seconds(1)) + == file_time_type::duration::min(), ""); + +constexpr auto max_time_t = numeric_limits::max(); +constexpr auto min_time_t = numeric_limits::min(); + +#if !defined(__LP64__) && defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + +constexpr 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); + } +} + +#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 + +constexpr bool is_representable(file_time_type const& tm) { + auto secs = duration_cast(tm.time_since_epoch()); + auto nsecs = duration_cast(tm.time_since_epoch() - secs); + if (nsecs.count() < 0) { + secs = secs + seconds(1); + nsecs = nsecs + seconds(1); + } + using TLim = numeric_limits; + if (secs.count() >= 0) + return secs.count() <= TLim::max(); + return secs.count() >= TLim::min(); +} +#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 + +template struct Dummy; +constexpr file_time_type convert_timespec(TimeSpec const& tm) { + auto adj_msec = duration_cast(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(nanoseconds(tm.tv_nsec)).count() == 0) { + return file_time_type(seconds(tm.tv_sec)); + } else { // tm.tv_sec < 0 + auto adj_subsec = duration_cast(seconds(1) - nanoseconds(tm.tv_nsec)); + auto Dur = seconds(tm.tv_sec + 1) - adj_subsec; + return file_time_type(Dur); + } +} +#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 + +#if !defined(__LP64__) && defined(__clang__) +#pragma clang diagnostic pop +#endif template bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp) { @@ -519,21 +633,8 @@ bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp) && checked_set(subsec_out, subsec_dur.count()); } -using TimeSpec = struct ::timespec; -using StatT = struct ::stat; - -#if defined(__APPLE__) -TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; } -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 - file_time_type __last_write_time(const path& p, std::error_code *ec) { using namespace ::std::chrono; @@ -546,25 +647,12 @@ file_time_type __last_write_time(const path& p, std::error_code *ec) } if (ec) ec->clear(); auto ts = detail::extract_mtime(st); -#ifndef _LIBCPP_HAS_NO_INT128 - using IntMax = __int128_t; - // FIXME: The value may not be representable as file_time_type. Fix - // file_time_type so it can represent all possible values returned by the - // filesystem. For now we do the calculation with the largest possible types - // and then truncate, this prevents signed integer overflow bugs. - const auto NsDur = duration(ts.tv_nsec) + seconds(ts.tv_sec); - if (NsDur > file_time_type::max().time_since_epoch() || - NsDur < file_time_type::min().time_since_epoch()) { + if (!detail::is_representable(ts)) { set_or_throw(error_code(EOVERFLOW, generic_category()), ec, "last_write_time", p); return file_time_type::min(); } - return file_time_type(duration_cast(NsDur)); -#else - // FIXME the under/overflow check done above overflows if we don't have - // a 128 bit integer type. - return file_time_type::clock::from_time_t(ts.tv_sec); -#endif + return detail::convert_timespec(ts); } void __last_write_time(const path& p, file_time_type new_time,