summaryrefslogtreecommitdiffstats
path: root/libcxx/include
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2018-07-20 01:22:32 +0000
committerEric Fiselier <eric@efcs.ca>2018-07-20 01:22:32 +0000
commitc16998649e1f55c61158004f461fd629d9b8b7f9 (patch)
treec1434a61a1da354f770f4ac06994211375a84253 /libcxx/include
parent40fa4a1a559765ada93268c4d45c54b62779166b (diff)
downloadbcm5719-llvm-c16998649e1f55c61158004f461fd629d9b8b7f9.tar.gz
bcm5719-llvm-c16998649e1f55c61158004f461fd629d9b8b7f9.zip
[libc++] Implement Directory Entry Caching -- Sort of.
Summary: This patch implements directory_entry caching *almost* as specified in P0317r1. However, I explicitly chose to deviate from the standard as I'll explain below. The approach I decided to take is a fully caching one. When `refresh()` is called, the cache is populated by calls to `stat` and `lstat` as needed. During directory iteration the cache is only populated with the `file_type` as reported by `readdir`. The cache can be in the following states: * `_Empty`: There is nothing in the cache (likely due to an error) * `_IterSymlink`: Created by directory iteration when we walk onto a symlink only the symlink file type is known. * `_IterNonSymlink`: Created by directory iteration when we walk onto a non-symlink. Both the regular file type and symlink file type are known. * `_RefreshSymlink` and `_RefreshNonSymlink`: A full cache created by `refresh()`. This case includes dead symlinks. * `_RefreshSymlinkUnresolved`: A partial cache created by refresh when we fail to resolve the file pointed to by a symlink (likely due to permissions). Symlink attributes are cached, but attributes about the linked entity are not. As mentioned, this implementation purposefully deviates from the standard. According to some readings of the specification, and the Windows filesystem implementation, the constructors and modifiers which don't pass an `error_code` must throw when the `directory_entry` points to a entity which doesn't exist. or when attribute resolution fails for another reason. @BillyONeal has proposed a more reasonable set of requirements, where modifiers other than refresh ignore errors. This is the behavior libc++ currently implements, with the expectation some form of the new language will be accepted into the standard. Some additional semantics which differ from the Windows implementation: 1. `refresh` will not throw when the entry doesn't exist. In this case we can still meet the functions specification, so we don't treat it as an error. 2. We don't clear the path name when a constructor fails via refresh (this will hopefully be changed in the standard as well). It should be noted that libstdc++'s current implementation has the same behavior as libc++, except for point (2). If the changes to the specification don't get accepted, we'll be able to make the changes later. [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0317r1.html Reviewers: mclow.lists, gromer, ldionne, aaron.ballman Subscribers: BillyONeal, christof, cfe-commits Differential Revision: https://reviews.llvm.org/D49530 llvm-svn: 337516
Diffstat (limited to 'libcxx/include')
-rw-r--r--libcxx/include/experimental/filesystem393
1 files changed, 382 insertions, 11 deletions
diff --git a/libcxx/include/experimental/filesystem b/libcxx/include/experimental/filesystem
index 1ca179699de..be73bf135bc 100644
--- a/libcxx/include/experimental/filesystem
+++ b/libcxx/include/experimental/filesystem
@@ -1903,6 +1903,10 @@ path weakly_canonical(path const& __p, error_code& __ec) {
}
+class directory_iterator;
+class recursive_directory_iterator;
+class __dir_stream;
+
class directory_entry
{
typedef _VSTD_FS::path _Path;
@@ -1914,7 +1918,15 @@ public:
directory_entry(directory_entry&&) _NOEXCEPT = default;
_LIBCPP_INLINE_VISIBILITY
- explicit directory_entry(_Path const& __p) : __p_(__p) {}
+ explicit directory_entry(_Path const& __p) : __p_(__p) {
+ error_code __ec;
+ __refresh(&__ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ directory_entry(_Path const& __p, error_code &__ec) : __p_(__p) {
+ __refresh(&__ec);
+ }
~directory_entry() {}
@@ -1924,14 +1936,38 @@ public:
_LIBCPP_INLINE_VISIBILITY
void assign(_Path const& __p) {
__p_ = __p;
+ error_code __ec;
+ __refresh(&__ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void assign(_Path const& __p, error_code& __ec) {
+ __p_ = __p;
+ __refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
void replace_filename(_Path const& __p) {
- __p_ = __p_.parent_path() / __p;
+ __p_.replace_filename(__p);
+ error_code __ec;
+ __refresh(&__ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void replace_filename(_Path const& __p, error_code &__ec) {
+ __p_ = __p_.parent_path() / __p;
+ __refresh(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
+ void refresh() {
+ __refresh();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void refresh(error_code& __ec) _NOEXCEPT { __refresh(&__ec); }
+
+ _LIBCPP_INLINE_VISIBILITY
_Path const& path() const _NOEXCEPT {
return __p_;
}
@@ -1942,23 +1978,142 @@ public:
}
_LIBCPP_INLINE_VISIBILITY
+ bool exists() const {
+ return _VSTD_FS::exists(file_status{__get_ft()});
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool exists(error_code& __ec) const noexcept {
+ return _VSTD_FS::exists(file_status{__get_ft(&__ec)});
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_block_file() const {
+ return __get_ft() == file_type::block;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_block_file(error_code& __ec) const noexcept {
+ return __get_ft(&__ec) == file_type::block;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_character_file() const {
+ return __get_ft() == file_type::character;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_character_file(error_code& __ec) const noexcept {
+ return __get_ft(&__ec) == file_type::character;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_directory() const {
+ return __get_ft() == file_type::directory;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_directory(error_code& __ec) const noexcept {
+ return __get_ft(&__ec) == file_type::directory;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_fifo() const {
+ return __get_ft() == file_type::fifo;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_fifo(error_code& __ec) const noexcept {
+ return __get_ft(&__ec) == file_type::fifo;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_other() const {
+ return _VSTD_FS::is_other(file_status{__get_ft()});
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_other(error_code& __ec) const noexcept {
+ return _VSTD_FS::is_other(file_status{__get_ft(&__ec)});
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_regular_file() const {
+ return __get_ft() == file_type::regular;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_regular_file(error_code& __ec) const noexcept {
+ return __get_ft(&__ec) == file_type::regular;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_socket() const {
+ return __get_ft() == file_type::socket;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_socket(error_code& __ec) const noexcept {
+ return __get_ft(&__ec) == file_type::socket;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_symlink() const {
+ return __get_sym_ft() == file_type::symlink;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ bool is_symlink(error_code& __ec) const noexcept {
+ return __get_sym_ft(&__ec) == file_type::symlink;
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ uintmax_t file_size() const {
+ return __get_size();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ uintmax_t file_size(error_code& __ec) const noexcept {
+ return __get_size(&__ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ uintmax_t hard_link_count() const {
+ return __get_nlink();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ uintmax_t hard_link_count(error_code& __ec) const noexcept {
+ return __get_nlink(&__ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_time_type last_write_time() const {
+ return __get_write_time();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_time_type last_write_time(error_code& __ec) const noexcept {
+ return __get_write_time(&__ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
file_status status() const {
- return _VSTD_FS::status(__p_);
+ return __get_status();
}
_LIBCPP_INLINE_VISIBILITY
file_status status(error_code& __ec) const _NOEXCEPT {
- return _VSTD_FS::status(__p_, __ec);
+ return __get_status(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
file_status symlink_status() const {
- return _VSTD_FS::symlink_status(__p_);
+ return __get_symlink_status();
}
_LIBCPP_INLINE_VISIBILITY
file_status symlink_status(error_code& __ec) const _NOEXCEPT {
- return _VSTD_FS::symlink_status(__p_, __ec);
+ return __get_symlink_status(&__ec);
}
_LIBCPP_INLINE_VISIBILITY
@@ -1990,14 +2145,230 @@ public:
bool operator>=(directory_entry const& __rhs) const _NOEXCEPT {
return __p_ >= __rhs.__p_;
}
+
private:
- _Path __p_;
-};
+ friend class directory_iterator;
+ friend class recursive_directory_iterator;
+ friend class __dir_stream;
+
+ enum _CacheType : unsigned char {
+ _Empty,
+ _IterSymlink,
+ _IterNonSymlink,
+ _RefreshSymlink,
+ _RefreshSymlinkUnresolved,
+ _RefreshNonSymlink
+ };
+ struct __cached_data {
+ uintmax_t __size_;
+ uintmax_t __nlink_;
+ file_time_type __write_time_;
+ perms __sym_perms_;
+ perms __non_sym_perms_;
+ file_type __type_;
+ _CacheType __cache_type_;
+
+ _LIBCPP_INLINE_VISIBILITY
+ __cached_data() noexcept { __reset(); }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void __reset() {
+ __cache_type_ = _Empty;
+ __type_ = file_type::none;
+ __sym_perms_ = __non_sym_perms_ = perms::unknown;
+ __size_ = __nlink_ = uintmax_t(-1);
+ __write_time_ = file_time_type::min();
+ }
+ };
-class directory_iterator;
-class recursive_directory_iterator;
-class __dir_stream;
+ _LIBCPP_INLINE_VISIBILITY
+ static __cached_data __create_iter_result(file_type __ft) {
+ __cached_data __data;
+ __data.__type_ = __ft;
+ __data.__cache_type_ =
+ __ft == file_type::symlink ? _IterSymlink : _IterNonSymlink;
+ return __data;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void __assign_iter_entry(_Path&& __p, __cached_data __dt) {
+ __p_ = std::move(__p);
+ __data_ = __dt;
+ }
+
+ _LIBCPP_FUNC_VIS
+ error_code __do_refresh() noexcept;
+
+ _LIBCPP_INLINE_VISIBILITY
+ static bool __is_dne_error(error_code const& __ec) {
+ if (!__ec)
+ return true;
+ switch (static_cast<errc>(__ec.value())) {
+ case errc::no_such_file_or_directory:
+ case errc::not_a_directory:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void __handle_error(const char* __msg, error_code* __dest_ec,
+ error_code const& __ec,
+ bool __allow_dne = false) const {
+ if (__dest_ec) {
+ *__dest_ec = __ec;
+ return;
+ }
+ if (__ec && (!__allow_dne || !__is_dne_error(__ec)))
+ __throw_filesystem_error(__msg, __p_, _Path{}, __ec);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ void __refresh(error_code* __ec = nullptr) {
+ __handle_error("refresh", __ec, __do_refresh(), /*allow_dne*/ true);
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_type __get_sym_ft(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ return __symlink_status(__p_, __ec).type();
+ case _IterSymlink:
+ case _RefreshSymlink:
+ case _RefreshSymlinkUnresolved:
+ if (__ec)
+ __ec->clear();
+ return file_type::symlink;
+ case _IterNonSymlink:
+ case _RefreshNonSymlink:
+ file_status __st(__data_.__type_);
+ if (__ec && !_VSTD_FS::exists(__st))
+ *__ec = make_error_code(errc::no_such_file_or_directory);
+ else if (__ec)
+ __ec->clear();
+ return __data_.__type_;
+ }
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_type __get_ft(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ case _IterSymlink:
+ case _RefreshSymlinkUnresolved:
+ return __status(__p_, __ec).type();
+ case _IterNonSymlink:
+ case _RefreshNonSymlink:
+ case _RefreshSymlink: {
+ file_status __st(__data_.__type_);
+ if (__ec && !_VSTD_FS::exists(__st))
+ *__ec = make_error_code(errc::no_such_file_or_directory);
+ else if (__ec)
+ __ec->clear();
+ return __data_.__type_;
+ }
+ }
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_status __get_status(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ case _IterNonSymlink:
+ case _IterSymlink:
+ case _RefreshSymlinkUnresolved:
+ return __status(__p_, __ec);
+ case _RefreshNonSymlink:
+ case _RefreshSymlink:
+ return file_status(__get_ft(__ec), __data_.__non_sym_perms_);
+ }
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_status __get_symlink_status(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ case _IterNonSymlink:
+ case _IterSymlink:
+ return __symlink_status(__p_, __ec);
+ case _RefreshNonSymlink:
+ return file_status(__get_sym_ft(__ec), __data_.__non_sym_perms_);
+ case _RefreshSymlink:
+ case _RefreshSymlinkUnresolved:
+ return file_status(__get_sym_ft(__ec), __data_.__sym_perms_);
+ }
+ }
+
+
+ _LIBCPP_INLINE_VISIBILITY
+ uintmax_t __get_size(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ case _IterNonSymlink:
+ case _IterSymlink:
+ case _RefreshSymlinkUnresolved:
+ return _VSTD_FS::__file_size(__p_, __ec);
+ case _RefreshSymlink:
+ case _RefreshNonSymlink: {
+ error_code __m_ec;
+ file_status __st(__get_ft(&__m_ec));
+ __handle_error("directory_entry::file_size", __ec, __m_ec);
+ if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) {
+ errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory
+ : errc::not_supported;
+ __handle_error("directory_entry::file_size", __ec,
+ make_error_code(__err_kind));
+ }
+ return __data_.__size_;
+ }
+ }
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ uintmax_t __get_nlink(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ case _IterNonSymlink:
+ case _IterSymlink:
+ case _RefreshSymlinkUnresolved:
+ return _VSTD_FS::__hard_link_count(__p_, __ec);
+ case _RefreshSymlink:
+ case _RefreshNonSymlink: {
+ error_code __m_ec;
+ (void)__get_ft(&__m_ec);
+ __handle_error("directory_entry::hard_link_count", __ec, __m_ec);
+ return __data_.__nlink_;
+ }
+ }
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ file_time_type __get_write_time(error_code *__ec = nullptr) const {
+ switch (__data_.__cache_type_) {
+ case _Empty:
+ case _IterNonSymlink:
+ case _IterSymlink:
+ case _RefreshSymlinkUnresolved:
+ return _VSTD_FS::__last_write_time(__p_, __ec);
+ case _RefreshSymlink:
+ case _RefreshNonSymlink: {
+ error_code __m_ec;
+ file_status __st(__get_ft(&__m_ec));
+ __handle_error("directory_entry::last_write_time", __ec, __m_ec);
+ if (_VSTD_FS::exists(__st) &&
+ __data_.__write_time_ == file_time_type::min())
+ __handle_error("directory_entry::last_write_time", __ec,
+ make_error_code(errc::value_too_large));
+ return __data_.__write_time_;
+ }
+ }
+ }
+private:
+ _Path __p_;
+ __cached_data __data_;
+};
class __dir_element_proxy {
public:
OpenPOWER on IntegriCloud