diff options
author | Jenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz> | 2020-07-10 02:00:16 +0300 |
---|---|---|
committer | Jenkins nedprod CI <jenkins-nedprod@europe5.nedproductions.biz> | 2020-07-10 02:00:16 +0300 |
commit | 9699920798dc7786e543ac7832ed9e6f8cb1e500 (patch) | |
tree | 12ee75015aac2732104daaa16b2f91bc542cbc4b | |
parent | 9050f5c8446e147ea5ed1f1470da6cd40f8d883a (diff) | |
parent | 804dcb157c208bf918aea37eb98e149934b51e4f (diff) |
Merged from develop branch as CDash reports all green
-rw-r--r-- | include/llfio/v2.0/algorithm/clone.hpp | 4 | ||||
-rw-r--r-- | include/llfio/v2.0/algorithm/contents.hpp | 242 | ||||
-rw-r--r-- | include/llfio/v2.0/algorithm/difference.hpp | 137 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/clone.ipp | 3 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp | 44 | ||||
-rw-r--r-- | include/llfio/v2.0/llfio.hpp | 1 | ||||
-rw-r--r-- | include/llfio/v2.0/path_view.hpp | 198 | ||||
-rw-r--r-- | include/llfio/v2.0/stat.hpp | 131 | ||||
-rw-r--r-- | include/llfio/v2.0/symlink_handle.hpp | 43 |
9 files changed, 697 insertions, 106 deletions
diff --git a/include/llfio/v2.0/algorithm/clone.hpp b/include/llfio/v2.0/algorithm/clone.hpp index 570ef089..9175a93f 100644 --- a/include/llfio/v2.0/algorithm/clone.hpp +++ b/include/llfio/v2.0/algorithm/clone.hpp @@ -46,8 +46,8 @@ namespace algorithm \param force_copy_now Parameter to pass to `file_handle::clone_extents()` to force extents to be copied now, not copy-on-write lazily later. \param creation How to create the destination file handle. NOTE that if this - is NOT `always_new`, if the destination has identical maximum extent and - timestamps (and permissions on POSIX) to the source, it is NOT copied, and + is NOT `always_new`, if the destination has identical maximum extent and last + modified timestamp (and permissions on POSIX) to the source, it is NOT copied, and zero is returned. \param d Deadline by which to complete the operation. diff --git a/include/llfio/v2.0/algorithm/contents.hpp b/include/llfio/v2.0/algorithm/contents.hpp new file mode 100644 index 00000000..813fa8eb --- /dev/null +++ b/include/llfio/v2.0/algorithm/contents.hpp @@ -0,0 +1,242 @@ +/* A filesystem algorithm which returns the contents of a directory tree +(C) 2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits) +File Created: July 2020 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef LLFIO_ALGORITHM_CONTENTS_HPP +#define LLFIO_ALGORITHM_CONTENTS_HPP + +#include "traverse.hpp" + +#include <memory> +#include <mutex> + +//! \file contents.hpp Provides a directory tree contents algorithm. + +LLFIO_V2_NAMESPACE_BEGIN + +namespace algorithm +{ + /*! \brief A visitor for the filesystem contents algorithm. + */ + struct contents_visitor : traverse_visitor + { + //! Whether to include files in the contents. + bool contents_include_files{true}; + //! Whether to include directories in the contents. + bool contents_include_directories{true}; + //! Whether to include symlinks in the contents. + bool contents_include_symlinks{true}; + //! What `stat_t::want` to include, if enumeration doesn't provide these, they will be additionally fetched. + stat_t::want contents_include_metadata{stat_t::want::none}; + + /*! \brief Enumerated contents, and what parts of their `stat_t` is valid. + */ + struct contents_type : public std::vector<std::pair<filesystem::path, stat_t>> + { + //! The metadata valid within all the `stat_t` in the contents traversed. + stat_t::want metadata{stat_t::want::none}; + }; + + //! Default construtor + contents_visitor() = default; + //! Construct an instance + explicit contents_visitor(stat_t::want _metadata, bool _include_files = true, bool _include_directories = true, bool _include_symlinks = true) + : contents_include_files(_include_files) + , contents_include_directories(_include_directories) + , contents_include_symlinks(_include_symlinks) + , contents_include_metadata(_metadata) + { + } + + friend inline result<contents_type> contents(const path_handle &dirh, contents_visitor *visitor, size_t threads, bool force_slow_path) noexcept; + + protected: + struct _state_type + { + const path_handle &rootdirh; + std::atomic<size_t> rootdirpathlen{0}; + std::atomic<stat_t::want> metadata{stat_t::want::all}; + contents_type contents; + + std::mutex lock; + std::vector<std::shared_ptr<contents_type>> all_thread_contents; + + explicit _state_type(const path_handle &_rootdirh) + : rootdirh(_rootdirh) + { + } + }; + + static std::shared_ptr<contents_type> _thread_contents(_state_type *state) noexcept + { + try + { + static thread_local std::weak_ptr<contents_type> mycontents; + auto ret = mycontents.lock(); + if(ret) + { + return ret; + } + ret = std::make_unique<contents_type>(); + mycontents = ret; + std::lock_guard<std::mutex> g(state->lock); + state->all_thread_contents.push_back(ret); + return ret; + } + catch(...) + { + return {}; + } + } + + public: + /*! \brief The default implementation accumulates the contents into thread + local storage. At traverse end, all the thread local storages are coalesced + into a single result, the member variable `contents`. + */ + virtual result<void> post_enumeration(void *data, const directory_handle &dirh, directory_handle::buffers_type &contents, size_t depth) noexcept + { + try + { + auto *state = (_state_type *) data; + (void) depth; + if(!contents.empty()) + { + contents_type toadd; + toadd.reserve(contents.size()); + filesystem::path dirhpath; + for(;;) + { + OUTCOME_TRY(dirhpath, dirh.current_path()); + auto rootdirpathlen = state->rootdirpathlen.load(std::memory_order_relaxed); + if(dirhpath.native().size() <= rootdirpathlen) + { + break; + } + dirhpath = dirhpath.native().substr(rootdirpathlen); + auto r = directory_handle::directory(state->rootdirh, dirhpath); + if(r && r.value().unique_id() == dirh.unique_id()) + { + break; + } + OUTCOME_TRY(dirhpath, state->rootdirh.current_path()); + state->rootdirpathlen.store(dirhpath.native().size() + 1, std::memory_order_relaxed); + } + auto _metadata_ = state->metadata.load(std::memory_order_relaxed); + if((_metadata_ & (contents_include_metadata | contents.metadata())) != _metadata_) + { + state->metadata.store(_metadata_ & (contents_include_metadata | contents.metadata()), std::memory_order_relaxed); + } + auto into = _thread_contents(state); + for(auto &entry : contents) + { + auto need_stat = contents_include_metadata & ~contents.metadata(); + if((contents_include_files && entry.stat.st_type == filesystem::file_type::regular) || + (contents_include_directories && entry.stat.st_type == filesystem::file_type::directory) || + (contents_include_symlinks && entry.stat.st_type == filesystem::file_type::symlink)) + { + if(!need_stat) + { + into->emplace_back(dirhpath / entry.leafname, entry.stat); + continue; + } + auto r = file_handle::file(dirh, entry.leafname, file_handle::mode::attr_read); + if(r) + { + OUTCOME_TRY(entry.stat.fill(r.value(), need_stat)); + into->emplace_back(dirhpath / entry.leafname, entry.stat); + } + } + } + } + return success(); + } + catch(...) + { + return error_from_exception(); + } + } + + /*! \brief Called when a traversal finishes, this default implementation merges + all the thread local results into `contents`, and deallocates the thread local + results. + */ + virtual result<size_t> finished(void *data, result<size_t> result) noexcept + { + try + { + auto *state = (_state_type *) data; + state->contents.clear(); + state->contents.metadata = state->metadata.load(std::memory_order_relaxed); + size_t count = 0; + for(auto &i : state->all_thread_contents) + { + count += i->size(); + } + state->contents.reserve(count); + for(auto &i : state->all_thread_contents) + { + state->contents.insert(state->contents.end(), std::make_move_iterator(i->begin()), std::make_move_iterator(i->end())); + } + state->all_thread_contents.clear(); + return result; + } + catch(...) + { + return error_from_exception(); + } + } + }; + + + /*! \brief Calculate the contents of everything within and under `dirh`. What + is returned is unordered. + + This is a very thin veneer over `traverse()` which came out of the fact that + I kept writing "get me the contents" traversal visitors again and again, so + eventually I just wrote a library edition. Its only "clever" bit is that it + stores the contents in thread local storage, and merges the contents afterwards. + + It is race free to concurrent relocations of `dirh`. It is entirely implemented + in header-only code, as it is very simple. + */ + inline result<contents_visitor::contents_type> contents(const path_handle &dirh, contents_visitor *visitor = nullptr, size_t threads = 0, + bool force_slow_path = false) noexcept + { + contents_visitor default_visitor; + if(visitor == nullptr) + { + visitor = &default_visitor; + } + contents_visitor::_state_type state(dirh); + OUTCOME_TRY(auto dirhpath, dirh.current_path()); + state.rootdirpathlen.store(dirhpath.native().size() + 1, std::memory_order_relaxed); + OUTCOME_TRY(traverse(dirh, visitor, threads, &state, force_slow_path)); + return {std::move(state.contents)}; + } + +} // namespace algorithm + +LLFIO_V2_NAMESPACE_END + +#endif diff --git a/include/llfio/v2.0/algorithm/difference.hpp b/include/llfio/v2.0/algorithm/difference.hpp new file mode 100644 index 00000000..81de992e --- /dev/null +++ b/include/llfio/v2.0/algorithm/difference.hpp @@ -0,0 +1,137 @@ +/* A filesystem algorithm which generates the difference between two directory trees +(C) 2020 Niall Douglas <http://www.nedproductions.biz/> (12 commits) +File Created: July 2020 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef LLFIO_ALGORITHM_DIFFERENCE_HPP +#define LLFIO_ALGORITHM_DIFFERENCE_HPP + +#include "traverse.hpp" + +//! \file compare.hpp Provides a directory tree difference algorithm. + +LLFIO_V2_NAMESPACE_BEGIN + +namespace algorithm +{ + /*! + */ + struct difference_item + { + enum change_t : uint8_t + { + unknown, + content_metadata_changed, //!< Maximum extent or modified timestamp metadata has changed + noncontent_metadata_changed, //!< Non-content metadata (perms, non-modified timestamps etc) has changed + file_added, //!< A new file was added + file_renamed, //!< A file was renamed within this tree + file_linked, //!< A hard link to something else also in this tree was added + file_removed, //!< A file was removed + directory_added, //!< A new directory was added + directory_renamed, //!< A directory was renamed within this tree + directory_removed, //!< A directory was removed + symlink_added, //!< A symlink was added + symlink_removed //!< A symlink was removed + } changed{change_t::unknown}; + int8_t content_comparison{0}; //!< `memcmp()` of content, if requested + }; + /*! \brief A visitor for the filesystem traversal and comparison algorithm. + + Note that at any time, returning a failure causes `compare()` to exit as soon + as possible with the same failure. + + You can override the members here inherited from `traverse_visitor`, however note + that `compare()` is entirely implemented using `traverse()`, so not calling the + implementations here will affect operation. + */ + struct compare_visitor : public traverse_visitor + { + //! This override ignores failures to traverse into the directory. + virtual result<directory_handle> directory_open_failed(void *data, result<void>::error_type &&error, const directory_handle &dirh, path_view leaf, + size_t depth) noexcept override + { + (void) error; + (void) dirh; + (void) leaf; + (void) depth; + auto *state = (traversal_summary *) data; + lock_guard<spinlock> g(state->_lock); + state->directory_opens_failed++; + return success(); // ignore failure to enter + } + //! This override implements the summary + virtual result<void> post_enumeration(void *data, const directory_handle &dirh, directory_handle::buffers_type &contents, size_t depth) noexcept override + { + try + { + auto *state = (traversal_summary *) data; + traversal_summary acc; + acc.max_depth = depth; + for(auto &entry : contents) + { + OUTCOME_TRY(accumulate(acc, state, &dirh, entry, contents.metadata())); + } + state->operator+=(acc); + return success(); + } + catch(...) + { + return error_from_exception(); + } + } + }; + + /*! \brief Summarise the directory identified `dirh`, and everything therein. + + It can be useful to summarise a directory hierarchy, especially to determine how + much storage it occupies, but also how many mounted filesystems it straddles etc. + You should specify what metadata you wish to summarise, if this is a subset of + what metadata `directory_handle::read()` returns, performance will be considerably + better. The default summarises all possible metadata. + + This is a trivial implementation on top of `algorithm::traverse()`, indeed it is + implemented entirely as header code. You should review the documentation for + `algorithm::traverse()`, as this algorithm is entirely implemented using that algorithm. + */ + inline result<traversal_summary> summarize(const path_handle &dirh, stat_t::want want = traversal_summary::default_metadata(), + summarize_visitor *visitor = nullptr, size_t threads = 0, bool force_slow_path = false) noexcept + { + LLFIO_LOG_FUNCTION_CALL(&dirh); + summarize_visitor default_visitor; + if(visitor == nullptr) + { + visitor = &default_visitor; + } + result<traversal_summary> state(in_place_type<traversal_summary>); + state.assume_value().want = want; + directory_entry entry{{}, stat_t(nullptr)}; + OUTCOME_TRY(entry.stat.fill(dirh, want)); + OUTCOME_TRY(summarize_visitor::accumulate(state.assume_value(), &state.assume_value(), nullptr, entry, want)); + OUTCOME_TRY(traverse(dirh, visitor, threads, &state.assume_value(), force_slow_path)); + return state; + } + +} // namespace algorithm + +LLFIO_V2_NAMESPACE_END + +#endif diff --git a/include/llfio/v2.0/detail/impl/clone.ipp b/include/llfio/v2.0/detail/impl/clone.ipp index 4ceb58e9..12317038 100644 --- a/include/llfio/v2.0/detail/impl/clone.ipp +++ b/include/llfio/v2.0/detail/impl/clone.ipp @@ -53,8 +53,7 @@ namespace algorithm { stat_t deststat(nullptr); OUTCOME_TRY(deststat.fill(r.value())); - if((stat.st_type == deststat.st_type) && (stat.st_mtim == deststat.st_mtim) && (stat.st_ctim == deststat.st_ctim) && - (stat.st_birthtim == deststat.st_birthtim) && (stat.st_size == deststat.st_size) + if((stat.st_type == deststat.st_type) && (stat.st_mtim == deststat.st_mtim) && (stat.st_size == deststat.st_size) #ifndef _WIN32 && (stat.st_perms == deststat.st_perms) && (stat.st_uid == deststat.st_uid) && (stat.st_gid == deststat.st_gid) && (stat.st_rdev == deststat.st_rdev) #endif diff --git a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp index b13c5909..9a013611 100644 --- a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp @@ -45,9 +45,10 @@ namespace detail return success(); #endif } -} +} // namespace detail -LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, bool atomic_replace) noexcept +LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, + deadline d, bool atomic_replace, bool exists_is_ok) noexcept { std::chrono::steady_clock::time_point began_steady; std::chrono::system_clock::time_point end_utc; @@ -101,7 +102,8 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(con return posix_error(); } // std::cerr << "renameat " << dirh.native_handle().fd << " " << randomname << " " << filename << std::endl; - if(-1 == ::renameat(dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, randomname.c_str(), dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, filename.c_str())) + if(-1 == ::renameat(dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, randomname.c_str(), dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, + filename.c_str())) { return posix_error(); } @@ -113,6 +115,10 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(con // std::cerr << "symlinkat " << zpath.buffer << " " << dirh.native_handle().fd << " " << filename << std::endl; if(-1 == ::symlinkat(zpath.buffer, dirh.is_valid() ? dirh.native_handle().fd : AT_FDCWD, filename.c_str())) { + if(exists_is_ok && EEXIST == errno) + { + return success(); + } return posix_error(); } return success(); @@ -280,7 +286,9 @@ result<void> symlink_handle::unlink(deadline d) noexcept } #endif -LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(const path_handle &base, symlink_handle::path_view_type path, symlink_handle::mode _mode, symlink_handle::creation _creation, flag flags) noexcept +LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(const path_handle &base, symlink_handle::path_view_type path, + symlink_handle::mode _mode, symlink_handle::creation _creation, + flag flags) noexcept { result<symlink_handle> ret(symlink_handle(native_handle_type(), 0, 0, flags)); native_handle_type &nativeh = ret.value()._v; @@ -295,11 +303,11 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c nativeh.behaviour &= ~native_handle_type::disposition::nonblocking; nativeh.behaviour &= ~native_handle_type::disposition::seekable; // not seekable #if !LLFIO_SYMLINK_HANDLE_IS_FAKED - path_handle dirh; + path_handle _dirh_, *dirh = &_dirh_; path_type leafname; #else (void) attribs; - path_handle &dirh = ret.value()._dirh; + path_handle *dirh = &ret.value()._dirh; path_type &leafname = ret.value()._leafname; #endif int dirhfd = AT_FDCWD; @@ -312,10 +320,11 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c { #if !LLFIO_SYMLINK_HANDLE_IS_FAKED dirhfd = base.native_handle().fd; + dirh = const_cast<path_handle *>(&base); #else - OUTCOME_TRY(auto &&dh, base.clone()); - dirh = path_handle(std::move(dh)); - dirhfd = dirh.native_handle().fd; + OUTCOME_TRY(auto dh, base.clone()); + *dirh = path_handle(std::move(dh)); + dirhfd = dirh->native_handle().fd; #endif } else @@ -323,9 +332,9 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c if(!path_parent.empty()) #endif { - OUTCOME_TRY(auto &&dh, path_handle::path(base, path_parent.empty() ? "." : path_parent)); - dirh = std::move(dh); - dirhfd = dirh.native_handle().fd; + // If faking the symlink, write this directly into the member variable cache + OUTCOME_TRY(*dirh, path_handle::path(base, path_parent.empty() ? "." : path_parent)); + dirhfd = dirh->native_handle().fd; } } catch(...) @@ -350,19 +359,16 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c case creation::always_new: { // Create an empty symlink, ignoring any file exists errors, unless only_if_not_exist - auto r = ret.value()._create_symlink(dirh, leafname, + auto r = ret.value()._create_symlink(*dirh, leafname, #if defined(__linux__) || defined(__APPLE__) ".", // Linux and Mac OS is not POSIX conforming here, and refuses to create empty symlinks #else "", #endif - std::chrono::seconds(10), creation::always_new == _creation); + std::chrono::seconds(10), creation::always_new == _creation, creation::if_needed == _creation); if(!r) { - if(_creation == creation::only_if_not_exist || r.error() != errc::file_exists) - { - return std::move(r).error(); - } + return std::move(r).error(); } break; } @@ -448,7 +454,7 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle: const path_handle &dirh = _dirh; const path_type &filename = _leafname; #endif - OUTCOME_TRY(_create_symlink(dirh, filename, req.buffers.path(), d, true)); + OUTCOME_TRY(_create_symlink(dirh, filename, req.buffers.path(), d, true, false)); #if !LLFIO_SYMLINK_HANDLE_IS_FAKED { // Current fd now points at the symlink we just atomically replaced, so need to reopen diff --git a/include/llfio/v2.0/llfio.hpp b/include/llfio/v2.0/llfio.hpp index 01da3bb9..7beb7620 100644 --- a/include/llfio/v2.0/llfio.hpp +++ b/include/llfio/v2.0/llfio.hpp @@ -73,6 +73,7 @@ import LLFIO_MODULE_NAME; #include "symlink_handle.hpp" #include "algorithm/clone.hpp" +#include "algorithm/contents.hpp" #include "algorithm/handle_adapter/cached_parent.hpp" #include "algorithm/reduce.hpp" #include "algorithm/shared_fs_mutex/atomic_append.hpp" diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp index 8fb5787c..87d3dc64 100644 --- a/include/llfio/v2.0/path_view.hpp +++ b/include/llfio/v2.0/path_view.hpp @@ -49,7 +49,8 @@ clang defines __cpp_char8_t if there is a char8_t. MSVC seems to only implement char8_t if C++ 20 is enabled. */ #ifndef LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED -#if(defined(_MSC_VER) && !defined(__clang__) && !_HAS_CXX20) || (defined(__GNUC__) && !defined(__clang__) && !defined(__CHAR8_TYPE__)) || (defined(__clang__) && !defined(__cpp_char8_t)) +#if(defined(_MSC_VER) && !defined(__clang__) && !_HAS_CXX20) || (defined(__GNUC__) && !defined(__clang__) && !defined(__CHAR8_TYPE__)) || \ +(defined(__clang__) && !defined(__cpp_char8_t)) #define LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED 1 #else #define LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED 0 @@ -123,17 +124,27 @@ namespace detail { }; - LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer, size_t src_buffer_length); - - LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer, size_t src_buffer_length); - LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer, size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, + const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char *src_buffer, + size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer, + size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer, + size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC char *reencode_path_to(size_t &toallocate, char *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer, + size_t src_buffer_length); + + LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, + const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char *src_buffer, + size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const wchar_t *src_buffer, + size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char8_t *src_buffer, + size_t src_buffer_length); + LLFIO_HEADERS_ONLY_FUNC_SPEC wchar_t *reencode_path_to(size_t &toallocate, wchar_t *dest_buffer, size_t dest_buffer_length, const char16_t *src_buffer, + size_t src_buffer_length); class path_view_iterator; } // namespace detail @@ -187,7 +198,8 @@ public: private: static constexpr auto _npos = string_view::npos; - union { + union + { const byte *_bytestr{nullptr}; const char *_charstr; const wchar_t *_wcharstr; @@ -442,9 +454,14 @@ private: return a.compare(b); } // Identical source encodings compare lexiographically - template <class DestT, class Deleter, size_t _internal_buffer_size, class CharT> static int _compare(basic_string_view<CharT> a, basic_string_view<CharT> b) noexcept { return a.compare(b); } + template <class DestT, class Deleter, size_t _internal_buffer_size, class CharT> + static int _compare(basic_string_view<CharT> a, basic_string_view<CharT> b) noexcept + { + return a.compare(b); + } // Disparate source encodings compare via c_str - template <class DestT, class Deleter, size_t _internal_buffer_size, class Char1T, class Char2T> static int _compare(basic_string_view<Char1T> a, basic_string_view<Char2T> b) noexcept + template <class DestT, class Deleter, size_t _internal_buffer_size, class Char1T, class Char2T> + static int _compare(basic_string_view<Char1T> a, basic_string_view<Char2T> b) noexcept { c_str<DestT, Deleter, _internal_buffer_size> _a({a.data(), a.size(), false}, true); c_str<DestT, Deleter, _internal_buffer_size> _b({b.data(), b.size(), false}, true); @@ -482,18 +499,25 @@ public: and `c_str` is never invoked as the two sources are byte compared directly. */ - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) constexpr int compare(path_view_component p) const { - return _invoke([&p](const auto &self) { return p._invoke([&self](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, other); }); }); + return _invoke( + [&p](const auto &self) { return p._invoke([&self](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, other); }); }); } //! \overload - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size, class Char) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>)) - constexpr int compare(const Char *s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true)); } + constexpr int compare(const Char *s) const noexcept + { + return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true)); + } //! \overload - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size, class Char) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_chartype_acceptable<Char>)) constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s)); } @@ -518,7 +542,8 @@ public: `operator new[]`. You can use an externally supplied larger temporary buffer to avoid dynamic memory allocation in all situations. */ - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) struct c_str { @@ -537,12 +562,16 @@ public: const value_type *buffer{nullptr}; private: - template <class U, class source_type> void _make_passthrough(path_view_component /*unused*/, bool /*unused*/, U & /*unused*/, const source_type * /*unused*/) {} + template <class U, class source_type> + void _make_passthrough(path_view_component /*unused*/, bool /*unused*/, U & /*unused*/, const source_type * /*unused*/) + { + } template <class U> void _make_passthrough(path_view_component view, bool no_zero_terminate, U &allocate, const value_type *source) { using LLFIO_V2_NAMESPACE::basic_string_view; // If the consuming API is a NT kernel API, and we have / in the path, we shall need to do slash conversion - const bool needs_slash_translation = (filesystem::path::preferred_separator != '/') && no_zero_terminate && view._invoke([](auto sv) { return sv.find('/') != _npos; }); + const bool needs_slash_translation = + (filesystem::path::preferred_separator != '/') && no_zero_terminate && view._invoke([](auto sv) { return sv.find('/') != _npos; }); length = view._length; if(!needs_slash_translation && (no_zero_terminate || view._zero_terminated)) { @@ -896,11 +925,16 @@ inline constexpr bool operator!=(const CharT * /*unused*/, path_view_component / static_assert(!path_view_component::is_source_acceptable<CharT>, "Do not use operator!= with path_view_component and a string literal, use .compare<>()"); return false; } -//! \brief Visit the underlying source for a `path_view_component` +//! \brief Visit the underlying source for a `path_view_component` (LLFIO backwards compatible overload) template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view_component view, F &&f) { return view._invoke(static_cast<F &&>(f)); } +//! \brief Visit the underlying source for a `path_view_component` (std compatible overload) +template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(F &&f, path_view_component view) +{ + return view._invoke(static_cast<F &&>(f)); +} namespace detail { @@ -1097,22 +1131,26 @@ public: { } - //! Implicitly constructs a path view from a zero terminated `const char *`. Convenience wrapper for the `byte` constructor. The input string MUST continue to exist for this view to be valid. + //! Implicitly constructs a path view from a zero terminated `const char *`. Convenience wrapper for the `byte` constructor. The input string MUST continue to + //! exist for this view to be valid. constexpr path_view(const char *v) noexcept // NOLINT : _state(v, detail::constexpr_strlen(v), true) { } - //! Implicitly constructs a path view from a zero terminated `const wchar_t *`. Convenience wrapper for the `byte` constructor. The input string MUST continue to exist for this view to be valid. + //! Implicitly constructs a path view from a zero terminated `const wchar_t *`. Convenience wrapper for the `byte` constructor. The input string MUST continue + //! to exist for this view to be valid. constexpr path_view(const wchar_t *v) noexcept // NOLINT : _state(v, detail::constexpr_strlen(v), true) { } - //! Implicitly constructs a path view from a zero terminated `const char8_t *`. Performs a UTF-8 to native encoding if necessary. The input string MUST continue to exist for this view to be valid. + //! Implicitly constructs a path view from a zero terminated `const char8_t *`. Performs a UTF-8 to native encoding if necessary. The input string MUST + //! continue to exist for this view to be valid. constexpr path_view(const char8_t *v) noexcept // NOLINT : _state(v, detail::constexpr_strlen(v), true) { } - //! Implicitly constructs a path view from a zero terminated `const char16_t *`. Performs a UTF-16 to native encoding if necessary. The input string MUST continue to exist for this view to be valid. + //! Implicitly constructs a path view from a zero terminated `const char16_t *`. Performs a UTF-16 to native encoding if necessary. The input string MUST + //! continue to exist for this view to be valid. constexpr path_view(const char16_t *v) noexcept // NOLINT : _state(v, detail::constexpr_strlen(v), true) { @@ -1457,21 +1495,28 @@ public: and `c_str` is never invoked as the two sources are byte compared directly. */ - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(path_view::is_source_acceptable<T>)) constexpr inline int compare(path_view p) const; //! \overload - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size, class Char) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>)) - constexpr int compare(const Char *s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true)); } + constexpr int compare(const Char *s) const noexcept + { + return compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true)); + } //! \overload - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size, class Char) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size, class Char) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_chartype_acceptable<Char>)) constexpr int compare(const basic_string_view<Char> s) const noexcept { return compare<T, Deleter, _internal_buffer_size>(path_view_component(s)); } //! Instantiate from a `path_view` to get a path suitable for feeding to other code. See `path_view_component::c_str`. - LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, size_t _internal_buffer_size = default_internal_buffer_size) + LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = std::default_delete<T[]>, + size_t _internal_buffer_size = default_internal_buffer_size) LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>)) struct c_str : public path_view_component::c_str<T, Deleter, _internal_buffer_size> { @@ -1507,11 +1552,16 @@ template <class T, class Deleter, size_t _internal_buffer_size, typename std::en #endif friend struct c_str; }; -//! \brief Visit the underlying source for a `path_view` +//! \brief Visit the underlying source for a `path_view` (LLFIO backwards compatible overload) template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view view, F &&f) { return view._state._invoke(static_cast<F &&>(f)); } +//! \brief Visit the underlying source for a `path_view` (std compatible overload) +template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(F &&f, path_view view) +{ + return view._state._invoke(static_cast<F &&>(f)); +} inline std::ostream &operator<<(std::ostream &s, const path_view &v) { return s << v._state; @@ -1807,6 +1857,86 @@ constexpr inline int path_view::compare(path_view o) const return 0; // identical } +namespace detail +{ + template <bool do_concat> struct path_view_concatenate_visitor + { + filesystem::path &a; + explicit path_view_concatenate_visitor(filesystem::path &_a) + : a(_a) + { + } + template <class CharT> void operator()(basic_string_view<CharT> sv) const + { + if(do_concat) + { + a.concat(sv); + } + else + { + a.append(sv); + } + } + void operator()(basic_string_view<char8_t> sv) + { +#if(__cplusplus >= 202000 || _HAS_CXX20) && (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION > 10000 /* approx start of 2020 */) + if(do_concat) + { + a.concat(sv); + } + else + { + a.append(sv); + } +#else + if(do_concat) + { + a+=filesystem::u8path((const char *) sv.data(), (const char *) sv.data() + sv.size()); + } + else + { + a/=filesystem::u8path((const char *) sv.data(), (const char *) sv.data() + sv.size()); + } +#endif + } + }; +} // namespace detail + +//! Append a path view component to a path +inline filesystem::path &operator+=(filesystem::path &a, path_view_component b) +{ + visit(b, detail::path_view_concatenate_visitor<true>(a)); + return a; +} +//! Append a path view component to a path +inline filesystem::path &operator/=(filesystem::path &a, path_view_component b) +{ + visit(b, detail::path_view_concatenate_visitor<false>(a)); + return a; +} +//! Append a path view component to a path +inline filesystem::path operator/(filesystem::path a, path_view_component b) +{ + return a /= b; +} +//! Append a path view to a path +inline filesystem::path &operator+=(filesystem::path &a, path_view b) +{ + visit(b, detail::path_view_concatenate_visitor<true>(a)); + return a; +} +//! Append a path view to a path +inline filesystem::path &operator/=(filesystem::path &a, path_view b) +{ + visit(b, detail::path_view_concatenate_visitor<false>(a)); + return a; +} +//! Append a path view to a path +inline filesystem::path operator/(filesystem::path a, path_view b) +{ + return a /= b; +} + #ifndef NDEBUG static_assert(std::is_trivially_copyable<path_view>::value, "path_view is not a trivially copyable!"); diff --git a/include/llfio/v2.0/stat.hpp b/include/llfio/v2.0/stat.hpp index 265207b0..03a861e8 100644 --- a/include/llfio/v2.0/stat.hpp +++ b/include/llfio/v2.0/stat.hpp @@ -85,54 +85,92 @@ struct LLFIO_DECL stat_t // NOLINT int16_t st_gid; /*!< group ID of the file (POSIX only) */ dev_t st_rdev; /*!< id of file if special (POSIX only) */ #endif - std::chrono::system_clock::time_point st_atim; /*!< time of last access (Windows, POSIX) */ - std::chrono::system_clock::time_point st_mtim; /*!< time of last data modification (Windows, POSIX) */ - std::chrono::system_clock::time_point st_ctim; /*!< time of last status change (Windows, POSIX) */ - handle::extent_type st_size; /*!< file size, in bytes (Windows, POSIX) */ - handle::extent_type st_allocated; /*!< bytes allocated for file (Windows, POSIX) */ - handle::extent_type st_blocks; /*!< number of blocks allocated (Windows, POSIX) */ - uint16_t st_blksize; /*!< block size used by this device (Windows, POSIX) */ - uint32_t st_flags; /*!< user defined flags for file (FreeBSD, OS X, zero - otherwise) */ - uint32_t st_gen; /*!< file generation number (FreeBSD, OS X, zero - otherwise)*/ - std::chrono::system_clock::time_point st_birthtim; /*!< time of file creation (Windows, POSIX) */ + union + { + std::chrono::system_clock::time_point st_atim; /*!< time of last access (Windows, POSIX) */ + }; + union + { + std::chrono::system_clock::time_point st_mtim; /*!< time of last data modification (Windows, POSIX) */ + }; + union + { + std::chrono::system_clock::time_point st_ctim; /*!< time of last status change (Windows, POSIX) */ + }; + handle::extent_type st_size; /*!< file size, in bytes (Windows, POSIX) */ + handle::extent_type st_allocated; /*!< bytes allocated for file (Windows, POSIX) */ + handle::extent_type st_blocks; /*!< number of blocks allocated (Windows, POSIX) */ + uint16_t st_blksize; /*!< block size used by this device (Windows, POSIX) */ + uint32_t st_flags; /*!< user defined flags for file (FreeBSD, OS X, zero + otherwise) */ + uint32_t st_gen; /*!< file generation number (FreeBSD, OS X, zero + otherwise)*/ + union + { + std::chrono::system_clock::time_point st_birthtim; /*!< time of file creation (Windows, POSIX) */ + }; + static_assert(std::is_trivially_destructible<std::chrono::system_clock::time_point>::value, + "std::chrono::system_clock::time_point is not trivally destructible!"); unsigned st_sparse : 1; /*!< if this file is sparse, or this directory capable of sparse files (Windows, POSIX) */ unsigned st_compressed : 1; /*!< if this file is compressed, or this directory capable of compressed files (Windows, Linux) */ unsigned st_reparse_point : 1; /*!< if this file or directory is a reparse point (Windows) */ //! Used to indicate what metadata should be filled in - QUICKCPPLIB_BITFIELD_BEGIN(want) - { - dev = 1 << 0, ino = 1 << 1, type = 1 << 2, perms = 1 << 3, nlink = 1 << 4, uid = 1 << 5, gid = 1 << 6, rdev = 1 << 7, atim = 1 << 8, mtim = 1 << 9, ctim = 1 << 10, size = 1 << 11, allocated = 1 << 12, blocks = 1 << 13, blksize = 1 << 14, flags = 1 << 15, gen = 1 << 16, birthtim = 1 << 17, sparse = 1 << 24, - compressed = 1 << 25, reparse_point = 1 << 26, all = static_cast<unsigned>(-1), none = 0 - } - QUICKCPPLIB_BITFIELD_END(want) + QUICKCPPLIB_BITFIELD_BEGIN(want){dev = 1 << 0, + ino = 1 << 1, + type = 1 << 2, + perms = 1 << 3, + nlink = 1 << 4, + uid = 1 << 5, + gid = 1 << 6, + rdev = 1 << 7, + atim = 1 << 8, + mtim = 1 << 9, + ctim = 1 << 10, + size = 1 << 11, + allocated = 1 << 12, + blocks = 1 << 13, + blksize = 1 << 14, + flags = 1 << 15, + gen = 1 << 16, + birthtim = 1 << 17, + sparse = 1 << 24, + compressed = 1 << 25, + reparse_point = 1 << 26, + all = static_cast<unsigned>(-1), + none = 0} QUICKCPPLIB_BITFIELD_END(want) //! Constructs a UNINITIALIZED instance i.e. full of random garbage - stat_t() {} // NOLINT CANNOT be constexpr because we are INTENTIONALLY not initialising the storage + stat_t() + { + } // NOLINT CANNOT be constexpr because we are INTENTIONALLY not initialising the storage //! Constructs a zeroed instance - constexpr explicit stat_t(std::nullptr_t) noexcept : st_dev(0), // NOLINT - st_ino(0), // NOLINT - st_type(filesystem::file_type::unknown), // NOLINT + constexpr explicit stat_t(std::nullptr_t) noexcept + : st_dev(0) // NOLINT + , st_ino(0) // NOLINT + , st_type(filesystem::file_type::unknown) // NOLINT #ifndef _WIN32 - st_perms(0), // NOLINT + , st_perms(0) // NOLINT #endif - st_nlink(0), // NOLINT + , st_nlink(0) // NOLINT #ifndef _WIN32 - st_uid(0), // NOLINT - st_gid(0), // NOLINT - st_rdev(0), // NOLINT + , st_uid(0) // NOLINT + , st_gid(0) // NOLINT + , st_rdev(0) // NOLINT #endif - st_size(0), // NOLINT - st_allocated(0), // NOLINT - st_blocks(0), // NOLINT - st_blksize(0), // NOLINT - st_flags(0), // NOLINT - st_gen(0), // NOLINT - st_sparse(0), // NOLINT - st_compressed(0), // NOLINT - st_reparse_point(0) // NOLINT + , st_atim{} // NOLINT + , st_mtim{} // NOLINT + , st_ctim{} // NOLINT + , st_size(0) // NOLINT + , st_allocated(0) // NOLINT + , st_blocks(0) // NOLINT + , st_blksize(0) // NOLINT + , st_flags(0) // NOLINT + , st_gen(0) // NOLINT + , st_birthtim{} // NOLINT + , st_sparse(0) // NOLINT + , st_compressed(0) // NOLINT + , st_reparse_point(0) // NOLINT { } #ifdef __cpp_exceptions @@ -147,6 +185,24 @@ struct LLFIO_DECL stat_t // NOLINT } } #endif + //! Equality comparison + bool operator==(const stat_t &o) const noexcept { + // This is probably bold ... + return 0 == memcmp(this, &o, sizeof(o)); + } + //! Inequality comparison + bool operator!=(const stat_t &o) const noexcept + { + // This is probably bold ... + return 0 != memcmp(this, &o, sizeof(o)); + } + //! Ordering + bool operator<(const stat_t &o) const noexcept + { + // This is probably bold ... + return memcmp(this, &o, sizeof(o)) < 0; + } + /*! Fills the structure with metadata. \return The number of items filled in. You should use a nullptr constructed structure if you wish @@ -176,7 +232,7 @@ struct LLFIO_DECL stat_t // NOLINT - `perms`, `uid`, `gid` (POSIX only) - `atim` (Windows, POSIX) - `mtim` (Windows, POSIX) - - `birthtim` (Windows, FreeBSD, OS X) + - `birthtim` (Windows, POSIX) Note that on POSIX, setting birth time involves two syscalls, the first of which temporarily sets the modified date to the birth time, which is racy. This is @@ -188,6 +244,7 @@ struct LLFIO_DECL stat_t // NOLINT */ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<want> stamp(handle &h, want wanted = want::all) noexcept; }; +static_assert(std::is_trivially_copyable<stat_t>::value, "stat_t is not trivally copyable!"); LLFIO_V2_NAMESPACE_END diff --git a/include/llfio/v2.0/symlink_handle.hpp b/include/llfio/v2.0/symlink_handle.hpp index 39706343..9190f109 100644 --- a/include/llfio/v2.0/symlink_handle.hpp +++ b/include/llfio/v2.0/symlink_handle.hpp @@ -43,9 +43,10 @@ Distributed under the Boost Software License, Version 1.0. #pragma warning(disable : 4251) // dll interface #endif -extern "C" { +extern "C" +{ struct stat; -} +} LLFIO_V2_NAMESPACE_EXPORT_BEGIN @@ -89,7 +90,8 @@ class LLFIO_DECL symlink_handle : public handle, public fs_handle #ifndef _WIN32 friend result<void> detail::stat_from_symlink(struct stat &s, const handle &h) noexcept; - LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> _create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, bool atomic_replace) noexcept; + LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> _create_symlink(const path_handle &dirh, const handle::path_type &filename, path_view target, deadline d, + bool atomic_replace, bool exists_is_ok) noexcept; #endif public: @@ -139,7 +141,7 @@ public: constexpr buffers_type() {} // NOLINT /*! Constructor - */ + */ constexpr buffers_type(path_view link, symlink_type type = symlink_type::symbolic) : _link(link) , _type(type) @@ -147,7 +149,11 @@ public: } ~buffers_type() = default; //! Move constructor - buffers_type(buffers_type &&o) noexcept : _link(o._link), _type(o._type), _kernel_buffer(std::move(o._kernel_buffer)), _kernel_buffer_size(o._kernel_buffer_size) + buffers_type(buffers_type &&o) noexcept + : _link(o._link) + , _type(o._type) + , _kernel_buffer(std::move(o._kernel_buffer)) + , _kernel_buffer_size(o._kernel_buffer_size) { o._link = {}; o._type = symlink_type::none; @@ -195,7 +201,7 @@ public: size_t _kernel_buffer_size{0}; }; /*! The constant buffers type used by this handle for writes, which is a single item sequence of `path_view`. - */ + */ struct const_buffers_type { //! Type of the pointer to the buffer. @@ -215,7 +221,9 @@ public: } ~const_buffers_type() = default; //! Move constructor - const_buffers_type(const_buffers_type &&o) noexcept : _link(o._link), _type(o._type) + const_buffers_type(const_buffers_type &&o) noexcept + : _link(o._link) + , _type(o._type) { o._link = {}; o._type = symlink_type::none; @@ -275,7 +283,10 @@ public: //! Convenience constructor constructing from anything a `span<char>` can construct from LLFIO_TEMPLATE(class... Args) LLFIO_TREQUIRES(LLFIO_TPRED(std::is_constructible<span<char>, Args...>::value)) - constexpr io_request(Args &&... args) noexcept : io_request(span<char>(static_cast<Args &&>(args)...)) {} + constexpr io_request(Args &&... args) noexcept + : io_request(span<char>(static_cast<Args &&>(args)...)) + { + } }; //! Specialisation for writing symlinks template <bool ____> struct io_request<const_buffers_type, ____> // workaround lack of nested specialisation support on older compilers @@ -291,11 +302,17 @@ public: //! Convenience constructor constructing from anything a `path_view` can construct from LLFIO_TEMPLATE(class... Args) LLFIO_TREQUIRES(LLFIO_TPRED(std::is_constructible<path_view, Args...>::value)) - constexpr io_request(Args &&... args) noexcept : buffers(path_view(static_cast<Args &&>(args)...)) {} + constexpr io_request(Args &&... args) noexcept + : buffers(path_view(static_cast<Args &&>(args)...)) + { + } //! Convenience constructor constructing a specific type of link from anything a `path_view` can construct from LLFIO_TEMPLATE(class... Args) LLFIO_TREQUIRES(LLFIO_TPRED(std::is_constructible<path_view, Args...>::value)) - constexpr io_request(symlink_type type, Args &&... args) noexcept : buffers(path_view(static_cast<Args &&>(args)...), type) {} + constexpr io_request(symlink_type type, Args &&... args) noexcept + : buffers(path_view(static_cast<Args &&>(args)...), type) + { + } }; //! Default constructor @@ -318,7 +335,8 @@ public: #if !LLFIO_SYMLINK_HANDLE_IS_FAKED constexpr #endif - explicit symlink_handle(handle &&o) noexcept : handle(std::move(o)) + explicit symlink_handle(handle &&o) noexcept + : handle(std::move(o)) { } //! Move construction permitted @@ -420,7 +438,8 @@ public: \mallocs None, unless `LLFIO_SYMLINK_HANDLE_IS_FAKED` is on, in which case one. */ LLFIO_MAKE_FREE_FUNCTION - static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink(const path_handle &base, path_view_type path, mode _mode = mode::read, creation _creation = creation::open_existing, flag flags = flag::none) noexcept; + static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink(const path_handle &base, path_view_type path, mode _mode = mode::read, + creation _creation = creation::open_existing, flag flags = flag::none) noexcept; /*! Create a symlink handle creating a uniquely named symlink on a path. The symlink is opened exclusively with `creation::only_if_not_exist` so it will never collide with nor overwrite any existing symlink. |