Path: blob/main/contrib/llvm-project/libcxx/src/filesystem/time_utils.h
35231 views
//===----------------------------------------------------------------------===////1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===////78#ifndef FILESYSTEM_TIME_UTILS_H9#define FILESYSTEM_TIME_UTILS_H1011#include <__config>12#include <array>13#include <chrono>14#include <filesystem>15#include <limits>16#include <ratio>17#include <system_error>18#include <type_traits>19#include <utility>2021#include "error.h"22#include "format_string.h"2324#if defined(_LIBCPP_WIN32API)25# define WIN32_LEAN_AND_MEAN26# define NOMINMAX27# include <windows.h>28#else29# include <fcntl.h>30# include <sys/stat.h>31# include <sys/time.h> // for ::utimes as used in __last_write_time32#endif3334// We can use the presence of UTIME_OMIT to detect platforms that provide utimensat.35#if defined(UTIME_OMIT)36# define _LIBCPP_USE_UTIMENSAT37#endif3839_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM4041namespace detail {4243#if defined(_LIBCPP_WIN32API)44// Various C runtime versions (UCRT, or the legacy msvcrt.dll used by45// some mingw toolchains) provide different stat function implementations,46// with a number of limitations with respect to what we want from the47// stat function. Instead provide our own which does exactly what we want,48// along with our own stat structure and flag macros.4950struct TimeSpec {51int64_t tv_sec;52int64_t tv_nsec;53};54struct StatT {55unsigned st_mode;56TimeSpec st_atim;57TimeSpec st_mtim;58uint64_t st_dev; // FILE_ID_INFO::VolumeSerialNumber59struct FileIdStruct {60unsigned char id[16]; // FILE_ID_INFO::FileId61bool operator==(const FileIdStruct& other) const {62for (int i = 0; i < 16; i++)63if (id[i] != other.id[i])64return false;65return true;66}67} st_ino;68uint32_t st_nlink;69uintmax_t st_size;70};7172// There were 369 years and 89 leap days from the Windows epoch73// (1601) to the Unix epoch (1970).74# define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60))7576inline TimeSpec filetime_to_timespec(LARGE_INTEGER li) {77TimeSpec ret;78ret.tv_sec = li.QuadPart / 10000000 - FILE_TIME_OFFSET_SECS;79ret.tv_nsec = (li.QuadPart % 10000000) * 100;80return ret;81}8283inline TimeSpec filetime_to_timespec(FILETIME ft) {84LARGE_INTEGER li;85li.LowPart = ft.dwLowDateTime;86li.HighPart = ft.dwHighDateTime;87return filetime_to_timespec(li);88}8990inline FILETIME timespec_to_filetime(TimeSpec ts) {91LARGE_INTEGER li;92li.QuadPart = ts.tv_nsec / 100 + (ts.tv_sec + FILE_TIME_OFFSET_SECS) * 10000000;93FILETIME ft;94ft.dwLowDateTime = li.LowPart;95ft.dwHighDateTime = li.HighPart;96return ft;97}9899#else100using TimeSpec = struct timespec;101using TimeVal = struct timeval;102using StatT = struct stat;103104inline TimeVal make_timeval(TimeSpec const& ts) {105using namespace chrono;106auto Convert = [](long nsec) {107using int_type = decltype(std::declval<TimeVal>().tv_usec);108auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();109return static_cast<int_type>(dur);110};111TimeVal TV = {};112TV.tv_sec = ts.tv_sec;113TV.tv_usec = Convert(ts.tv_nsec);114return TV;115}116#endif117118using chrono::duration;119using chrono::duration_cast;120121template <class FileTimeT, class TimeT, bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>122struct time_util_base {123using rep = typename FileTimeT::rep;124using fs_duration = typename FileTimeT::duration;125using fs_seconds = duration<rep>;126using fs_nanoseconds = duration<rep, nano>;127using fs_microseconds = duration<rep, micro>;128129static constexpr rep max_seconds = duration_cast<fs_seconds>(FileTimeT::duration::max()).count();130131static constexpr rep max_nsec =132duration_cast<fs_nanoseconds>(FileTimeT::duration::max() - fs_seconds(max_seconds)).count();133134static constexpr rep min_seconds = duration_cast<fs_seconds>(FileTimeT::duration::min()).count();135136static constexpr rep min_nsec_timespec =137duration_cast<fs_nanoseconds>((FileTimeT::duration::min() - fs_seconds(min_seconds)) + fs_seconds(1)).count();138139private:140static constexpr fs_duration get_min_nsecs() {141return duration_cast<fs_duration>(fs_nanoseconds(min_nsec_timespec) - duration_cast<fs_nanoseconds>(fs_seconds(1)));142}143// Static assert that these values properly round trip.144static_assert(fs_seconds(min_seconds) + get_min_nsecs() == FileTimeT::duration::min(), "value doesn't roundtrip");145146static constexpr bool check_range() {147// This kinda sucks, but it's what happens when we don't have __int128_t.148if (sizeof(TimeT) == sizeof(rep)) {149typedef duration<long long, ratio<3600 * 24 * 365> > Years;150return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&151duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);152}153return max_seconds >= numeric_limits<TimeT>::max() && min_seconds <= numeric_limits<TimeT>::min();154}155#if _LIBCPP_STD_VER >= 14156static_assert(check_range(), "the representable range is unacceptable small");157#endif158};159160template <class FileTimeT, class TimeT>161struct time_util_base<FileTimeT, TimeT, true> {162using rep = typename FileTimeT::rep;163using fs_duration = typename FileTimeT::duration;164using fs_seconds = duration<rep>;165using fs_nanoseconds = duration<rep, nano>;166using fs_microseconds = duration<rep, micro>;167168static const rep max_seconds;169static const rep max_nsec;170static const rep min_seconds;171static const rep min_nsec_timespec;172};173174template <class FileTimeT, class TimeT>175const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_seconds =176duration_cast<fs_seconds>(FileTimeT::duration::max()).count();177178template <class FileTimeT, class TimeT>179const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =180duration_cast<fs_nanoseconds>(FileTimeT::duration::max() - fs_seconds(max_seconds)).count();181182template <class FileTimeT, class TimeT>183const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::min_seconds =184duration_cast<fs_seconds>(FileTimeT::duration::min()).count();185186template <class FileTimeT, class TimeT>187const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =188duration_cast<fs_nanoseconds>((FileTimeT::duration::min() - fs_seconds(min_seconds)) + fs_seconds(1)).count();189190template <class FileTimeT, class TimeT, class TimeSpecT>191struct time_util : time_util_base<FileTimeT, TimeT> {192using Base = time_util_base<FileTimeT, TimeT>;193using Base::max_nsec;194using Base::max_seconds;195using Base::min_nsec_timespec;196using Base::min_seconds;197198using typename Base::fs_duration;199using typename Base::fs_microseconds;200using typename Base::fs_nanoseconds;201using typename Base::fs_seconds;202203public:204template <class CType, class ChronoType>205static constexpr bool checked_set(CType* out, ChronoType time) {206using Lim = numeric_limits<CType>;207if (time > Lim::max() || time < Lim::min())208return false;209*out = static_cast<CType>(time);210return true;211}212213static constexpr bool is_representable(TimeSpecT tm) {214if (tm.tv_sec >= 0) {215return tm.tv_sec < max_seconds || (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);216} else if (tm.tv_sec == (min_seconds - 1)) {217return tm.tv_nsec >= min_nsec_timespec;218} else {219return tm.tv_sec >= min_seconds;220}221}222223static constexpr bool is_representable(FileTimeT tm) {224auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());225auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);226if (nsecs.count() < 0) {227secs = secs + fs_seconds(1);228nsecs = nsecs + fs_seconds(1);229}230using TLim = numeric_limits<TimeT>;231if (secs.count() >= 0)232return secs.count() <= TLim::max();233return secs.count() >= TLim::min();234}235236static constexpr FileTimeT convert_from_timespec(TimeSpecT tm) {237if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {238return FileTimeT(fs_seconds(tm.tv_sec) + duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));239} else { // tm.tv_sec < 0240auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) - fs_nanoseconds(tm.tv_nsec));241auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;242return FileTimeT(Dur);243}244}245246template <class SubSecT>247static constexpr bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {248auto dur = tp.time_since_epoch();249auto sec_dur = duration_cast<fs_seconds>(dur);250auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);251// The tv_nsec and tv_usec fields must not be negative so adjust accordingly252if (subsec_dur.count() < 0) {253if (sec_dur.count() > min_seconds) {254sec_dur = sec_dur - fs_seconds(1);255subsec_dur = subsec_dur + fs_seconds(1);256} else {257subsec_dur = fs_nanoseconds::zero();258}259}260return checked_set(sec_out, sec_dur.count()) && checked_set(subsec_out, subsec_dur.count());261}262static constexpr bool convert_to_timespec(TimeSpecT& dest, FileTimeT tp) {263if (!is_representable(tp))264return false;265return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);266}267};268269#if defined(_LIBCPP_WIN32API)270using fs_time = time_util<file_time_type, int64_t, TimeSpec>;271#else272using fs_time = time_util<file_time_type, time_t, TimeSpec>;273#endif274275#if defined(__APPLE__)276inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }277inline TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }278#elif defined(__MVS__)279inline TimeSpec extract_mtime(StatT const& st) {280TimeSpec TS = {st.st_mtime, 0};281return TS;282}283inline TimeSpec extract_atime(StatT const& st) {284TimeSpec TS = {st.st_atime, 0};285return TS;286}287#elif defined(_AIX)288inline TimeSpec extract_mtime(StatT const& st) {289TimeSpec TS = {st.st_mtime, st.st_mtime_n};290return TS;291}292inline TimeSpec extract_atime(StatT const& st) {293TimeSpec TS = {st.st_atime, st.st_atime_n};294return TS;295}296#else297inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }298inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }299#endif300301#ifndef _LIBCPP_HAS_NO_FILESYSTEM302303# if !defined(_LIBCPP_WIN32API)304inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {305TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};306if (::utimes(p.c_str(), ConvertedTS) == -1) {307ec = capture_errno();308return true;309}310return false;311}312313# if defined(_LIBCPP_USE_UTIMENSAT)314inline bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {315if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {316ec = capture_errno();317return true;318}319return false;320}321# endif322323inline bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS, error_code& ec) {324# if !defined(_LIBCPP_USE_UTIMENSAT)325return posix_utimes(p, TS, ec);326# else327return posix_utimensat(p, TS, ec);328# endif329}330331# endif // !_LIBCPP_WIN32API332333inline file_time_type __extract_last_write_time(const path& p, const StatT& st, error_code* ec) {334using detail::fs_time;335ErrorHandler<file_time_type> err("last_write_time", ec, &p);336337auto ts = detail::extract_mtime(st);338if (!fs_time::is_representable(ts))339return err.report(errc::value_too_large);340341return fs_time::convert_from_timespec(ts);342}343344#endif // !_LIBCPP_HAS_NO_FILESYSTEM345346} // end namespace detail347348_LIBCPP_END_NAMESPACE_FILESYSTEM349350#endif // FILESYSTEM_TIME_UTILS_H351352353