Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2020-10-08 14:04:31 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2020-10-08 14:04:31 +0300
commit04826c2e67404ec63782a0025b929c7d717ea1fd (patch)
treeb52837073da8c390ead9ef0e8399da17f245bc47
parente8f25ff3646124c4090a3413bfb43751b680a3ff (diff)
Long, long round of refactoring path_view to match latest P1030 draft normative wording. Not done yet however, so this is a wip commit.
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/detail/impl/cached_parent_handle_adapter.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/posix/directory_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/file_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/posix/fs_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/path_handle.ipp25
-rw-r--r--include/llfio/v2.0/detail/impl/posix/process_handle.ipp8
-rw-r--r--include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp4
-rw-r--r--include/llfio/v2.0/detail/impl/reduce.ipp8
-rw-r--r--include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/traverse.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/windows/directory_handle.ipp10
-rw-r--r--include/llfio/v2.0/detail/impl/windows/file_handle.ipp4
-rw-r--r--include/llfio/v2.0/detail/impl/windows/fs_handle.ipp14
-rw-r--r--include/llfio/v2.0/detail/impl/windows/import.hpp28
-rw-r--r--include/llfio/v2.0/detail/impl/windows/path_handle.ipp51
-rw-r--r--include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/windows/process_handle.ipp4
-rw-r--r--include/llfio/v2.0/detail/impl/windows/storage_profile.ipp3
-rw-r--r--include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp14
-rw-r--r--include/llfio/v2.0/path_handle.hpp11
-rw-r--r--include/llfio/v2.0/path_view.hpp898
-rw-r--r--programs/illegal-codepoints/main.cpp2
-rw-r--r--test/tests/clone_extents.cpp4
-rw-r--r--test/tests/path_view.cpp10
-rw-r--r--test/tests/reduce.cpp6
-rw-r--r--test/tests/traverse.cpp4
27 files changed, 676 insertions, 464 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index e9513d03..66c7a389 100644
--- a/include/llfio/revision.hpp
+++ b/include/llfio/revision.hpp
@@ -1,4 +1,4 @@
// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time
-#define LLFIO_PREVIOUS_COMMIT_REF e5fc567e29066d836db8286d3d86c8e4eef49b54
-#define LLFIO_PREVIOUS_COMMIT_DATE "2020-09-04 21:41:07 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE e5fc567e
+#define LLFIO_PREVIOUS_COMMIT_REF e8f25ff3646124c4090a3413bfb43751b680a3ff
+#define LLFIO_PREVIOUS_COMMIT_DATE "2020-10-06 14:57:42 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE e8f25ff3
diff --git a/include/llfio/v2.0/detail/impl/cached_parent_handle_adapter.ipp b/include/llfio/v2.0/detail/impl/cached_parent_handle_adapter.ipp
index 09f7eb36..95ad940c 100644
--- a/include/llfio/v2.0/detail/impl/cached_parent_handle_adapter.ipp
+++ b/include/llfio/v2.0/detail/impl/cached_parent_handle_adapter.ipp
@@ -77,7 +77,7 @@ namespace algorithm
filesystem::path dirpath;
if(base.is_valid())
{
- dirpath = base.current_path().value() / path.path();
+ dirpath = base.current_path().value() / path;
}
else
{
diff --git a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
index e597ba60..9ea737c0 100644
--- a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp
@@ -79,7 +79,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
// really ought to be cloning the handle. But let's humour him.
path = ".";
}
- path_view::c_str<> zpath(path);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
auto rename_random_dir_over_existing_dir = [_mode, _caching, flags](const path_handle &base, path_view_type path) -> result<directory_handle> {
// Take a path handle to the directory containing the file
auto path_parent = path.parent_path();
@@ -266,7 +266,7 @@ result<directory_handle::buffers_type> directory_handle::read(io_request<buffers
return std::move(req.buffers);
}
// Is glob a single entry match? If so, this is really a stat call
- path_view_type::c_str<> zglob(req.glob);
+ path_view_type::c_str<> zglob(req.glob, path_view::zero_terminated);
if(!req.glob.empty() && !req.glob.contains_glob())
{
struct stat s
@@ -431,7 +431,7 @@ result<directory_handle::buffers_type> directory_handle::read(io_request<buffers
goto cont;
}
directory_entry &item = req.buffers[n];
- item.leafname = path_view(dent->d_name, length, true);
+ item.leafname = path_view(dent->d_name, length, path_view::zero_terminated);
item.stat = stat_t(nullptr);
item.stat.st_ino = dent->d_ino;
char d_type = dent->d_type;
diff --git a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
index 96eb3a40..a02a73b0 100644
--- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
@@ -51,7 +51,7 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
OUTCOME_TRY(auto &&attribs, attribs_from_handle_mode_caching_and_flags(nativeh, _mode, _creation, _caching, flags));
attribs &= ~O_NONBLOCK;
nativeh.behaviour &= ~native_handle_type::disposition::nonblocking;
- path_view::c_str<> zpath(path);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
if(base.is_valid())
{
nativeh.fd = ::openat(base.native_handle().fd, zpath.buffer, attribs, 0x1b0 /*660*/);
diff --git a/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp b/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp
index 8c8d2c14..f13ade7d 100644
--- a/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/fs_handle.ipp
@@ -128,7 +128,7 @@ namespace detail
return success(std::move(currentdirh));
}
// stat the same file name, and compare dev and inode
- path_view::c_str<> zpath(filename);
+ path_view::c_str<> zpath(filename, path_view::zero_terminated);
struct stat s
{
};
@@ -188,7 +188,7 @@ result<void> fs_handle::relink(const path_handle &base, path_view_type path, boo
{
LLFIO_LOG_FUNCTION_CALL(this);
auto &h = const_cast<handle &>(_get_handle());
- path_view::c_str<> zpath(path);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
#ifdef O_TMPFILE
// If the handle was created with O_TMPFILE, we need a different approach
if(h.flags() & handle::flag::anonymous_inode)
@@ -327,7 +327,7 @@ result<void> fs_handle::link(const path_handle &base, path_view_type path, deadl
{
LLFIO_LOG_FUNCTION_CALL(this);
auto &h = const_cast<handle &>(_get_handle());
- path_view::c_str<> zpath(path);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
#ifdef AT_EMPTY_PATH
// Try to use the fd linking syscall
if(-1 != ::linkat(h.native_handle().fd, "", base.is_valid() ? base.native_handle().fd : AT_FDCWD, zpath.buffer, AT_EMPTY_PATH))
diff --git a/include/llfio/v2.0/detail/impl/posix/path_handle.ipp b/include/llfio/v2.0/detail/impl/posix/path_handle.ipp
index 99867ecf..c349cca1 100644
--- a/include/llfio/v2.0/detail/impl/posix/path_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/path_handle.ipp
@@ -28,6 +28,29 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_BEGIN
+result<bool> path_handle::exists(path_view_type path) const noexcept {
+ try
+ {
+ LLFIO_LOG_FUNCTION_CALL(this);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
+ int x = ::faccessat(native_handle().fd, zpath.buffer, F_OK, AT_SYMLINK_NOFOLLOW);
+ if(x < 0)
+ {
+ auto ret = posix_error();
+ if(ret == errc::no_such_file_or_directory)
+ {
+ return false;
+ }
+ return failure(std::move(ret));
+ }
+ return (x == F_OK);
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+}
+
result<path_handle> path_handle::path(const path_handle &base, path_handle::path_view_type path) noexcept
{
result<path_handle> ret(in_place_type<path_handle>);
@@ -43,7 +66,7 @@ result<path_handle> path_handle::path(const path_handle &base, path_handle::path
// Linux provides this extension opening a super light weight fd to just an anchor on the filing system
attribs |= O_PATH;
#endif
- path_view::c_str<> zpath(path);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
if(base.is_valid())
{
nativeh.fd = ::openat(base.native_handle().fd, zpath.buffer, attribs);
diff --git a/include/llfio/v2.0/detail/impl/posix/process_handle.ipp b/include/llfio/v2.0/detail/impl/posix/process_handle.ipp
index 2679ed25..63070099 100644
--- a/include/llfio/v2.0/detail/impl/posix/process_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/process_handle.ipp
@@ -143,7 +143,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC std::unique_ptr<span<path_view_component>, proce
{
size_t len = strlen(*e);
memcpy(after, *e, len + 1);
- *arraye++ = path_view_component(after, len, true);
+ *arraye++ = path_view_component(after, len, path_view::zero_terminated);
out = {array, arraye};
after += len + 1;
}
@@ -247,11 +247,11 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<process_handle> process_handle::launch_pr
std::vector<const char *> argptrs(args.size() + 2);
std::vector<small_path_view_c_str> _args;
_args.reserve(args.size() + 1);
- _args.emplace_back(path);
+ _args.emplace_back(path, path_view::zero_terminated);
argptrs[0] = _args[0].buffer;
for(size_t n = 0; n < args.size(); ++n)
{
- _args.emplace_back(args[n]);
+ _args.emplace_back(args[n], path_view::zero_terminated);
argptrs[n + 1] = _args[n + 1].buffer;
}
std::vector<small_path_view_c_str> _envs;
@@ -260,7 +260,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<process_handle> process_handle::launch_pr
envptrs.reserve(env.size() + 1);
for(const auto &i : env)
{
- _envs.emplace_back(i);
+ _envs.emplace_back(i, path_view::zero_terminated);
envptrs.push_back(_envs.back().buffer);
}
envptrs.push_back(nullptr);
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 9a013611..a234c668 100644
--- a/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/symlink_handle.ipp
@@ -63,7 +63,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> symlink_handle::_create_symlink(con
end_utc = d.to_time_point();
}
}
- path_view::c_str<> zpath(target);
+ path_view::c_str<> zpath(target, path_view::zero_terminated);
try
{
if(atomic_replace)
@@ -434,7 +434,7 @@ result<symlink_handle::buffers_type> symlink_handle::read(symlink_handle::io_req
}
// We know we can null terminate as read < bytes
buffer[read] = 0;
- tofill._link = path_view(buffer, read, true);
+ tofill._link = path_view(buffer, read, path_view::zero_terminated);
tofill._type = symlink_type::symbolic;
return {std::move(tofill)};
}
diff --git a/include/llfio/v2.0/detail/impl/reduce.ipp b/include/llfio/v2.0/detail/impl/reduce.ipp
index 214ccdca..5ce27e3b 100644
--- a/include/llfio/v2.0/detail/impl/reduce.ipp
+++ b/include/llfio/v2.0/detail/impl/reduce.ipp
@@ -67,7 +67,7 @@ namespace algorithm
const DWORD renamefile_ntflags = 0x20 /*FILE_SYNCHRONOUS_IO_NONALERT*/ | 0x00200000 /*FILE_OPEN_REPARSE_POINT*/ | 0x040 /*FILE_NON_DIRECTORY_FILE*/;
const DWORD renamedir_ntflags = 0x20 /*FILE_SYNCHRONOUS_IO_NONALERT*/ | 0x00200000 /*FILE_OPEN_REPARSE_POINT*/ | 0x01 /*FILE_DIRECTORY_FILE*/;
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(leafname, true);
+ path_view::c_str<> zpath(leafname, path_view::zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -142,7 +142,7 @@ namespace algorithm
}
return ntkernel_error(ntstat);
#else
- path_view::c_str<> zpath(leafname);
+ path_view::c_str<> zpath(leafname, path_view::zero_terminated);
errno = 0;
if(is_dir || -1 == ::unlinkat(dirh.native_handle().fd, zpath.buffer, 0))
{
@@ -179,7 +179,7 @@ namespace algorithm
const DWORD renamefile_ntflags = 0x20 /*FILE_SYNCHRONOUS_IO_NONALERT*/ | 0x00200000 /*FILE_OPEN_REPARSE_POINT*/ | 0x040 /*FILE_NON_DIRECTORY_FILE*/;
const DWORD renamedir_ntflags = 0x20 /*FILE_SYNCHRONOUS_IO_NONALERT*/ | 0x00200000 /*FILE_OPEN_REPARSE_POINT*/ | 0x01 /*FILE_DIRECTORY_FILE*/;
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(leafname, true);
+ path_view::c_str<> zpath(leafname, path_view::zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -251,7 +251,7 @@ namespace algorithm
return success();
#else
(void) is_dir;
- path_view::c_str<> zpath(leafname);
+ path_view::c_str<> zpath(leafname, path_view::zero_terminated);
if(dirh.unique_id() != topdirh.unique_id())
{
// Try renaming it into topdirh
diff --git a/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp b/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp
index a224148e..17eaf0fb 100644
--- a/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp
+++ b/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp
@@ -379,7 +379,7 @@ namespace algorithm
{
try
{
- path_view::c_str<> zpath(lockfile);
+ path_view::c_str<> zpath(lockfile, path_view::zero_terminated);
struct stat s
{
};
diff --git a/include/llfio/v2.0/detail/impl/traverse.ipp b/include/llfio/v2.0/detail/impl/traverse.ipp
index f69801a0..7f7bf4df 100644
--- a/include/llfio/v2.0/detail/impl/traverse.ipp
+++ b/include/llfio/v2.0/detail/impl/traverse.ipp
@@ -165,7 +165,7 @@ namespace algorithm
new(this) workitem(std::move(o));
return *this;
}
- path_view leaf() const noexcept { return using_sso ? path_view(_sso, _sso_length, true) : path_view(_alloc); }
+ path_view leaf() const noexcept { return using_sso ? path_view(_sso, _sso_length, path_view::zero_terminated) : path_view(_alloc); }
};
#endif
std::vector<std::list<workitem>> workqueue;
@@ -261,7 +261,7 @@ namespace algorithm
{
struct ::stat stat;
memset(&stat, 0, sizeof(stat));
- path_view::c_str<> zpath(entry.leafname);
+ path_view::c_str<> zpath(entry.leafname, path_view::zero_terminated);
if(::fstatat(mydirh->native_handle().fd, zpath.buffer, &stat, AT_SYMLINK_NOFOLLOW) >= 0)
{
entry.stat.st_type = [](uint16_t mode) {
@@ -318,7 +318,7 @@ namespace algorithm
{
if(use_slow_path)
{
- newwork.push_back(state_t::workitem(topdirh, mywork.leaf().path() / entry.leafname.path()));
+ newwork.push_back(state_t::workitem(topdirh, mywork.leaf() / entry.leafname));
}
else
{
diff --git a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
index 9bd50277..10fcf849 100644
--- a/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/directory_handle.ipp
@@ -84,7 +84,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
ntflags |= 0x01 /*FILE_DIRECTORY_FILE*/; // required to open a directory
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -159,7 +159,7 @@ result<directory_handle> directory_handle::directory(const path_handle &base, pa
break;
}
attribs |= FILE_FLAG_BACKUP_SEMANTICS; // required to open a directory
- path_view::c_str<> zpath(path, false);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFileW_(zpath.buffer, access, fileshare, nullptr, creation, attribs, nullptr, true))) // NOLINT
{
DWORD errcode = GetLastError();
@@ -298,7 +298,7 @@ result<directory_handle::buffers_type> directory_handle::read(io_request<buffers
}
UNICODE_STRING _glob{};
memset(&_glob, 0, sizeof(_glob));
- path_view_type::c_str<> zglob(req.glob, true);
+ path_view_type::c_str<> zglob(req.glob, path_view::not_zero_terminated);
if(!req.glob.empty())
{
_glob.Buffer = const_cast<wchar_t *>(zglob.buffer);
@@ -413,11 +413,11 @@ result<directory_handle::buffers_type> directory_handle::read(io_request<buffers
if(reinterpret_cast<uintptr_t>(ffdi->FileName + length) + sizeof(wchar_t) <= reinterpret_cast<uintptr_t>(ffdi) + ffdi->NextEntryOffset)
{
ffdi->FileName[length] = 0;
- item.leafname = path_view_type(ffdi->FileName, length, true);
+ item.leafname = path_view_type(ffdi->FileName, length, path_view::zero_terminated);
}
else
{
- item.leafname = path_view_type(ffdi->FileName, length, false);
+ item.leafname = path_view_type(ffdi->FileName, length, path_view::not_zero_terminated);
}
if(req.filtering == filter::fastdeleted && item.leafname.is_llfio_deleted())
{
diff --git a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
index cfd4a387..43bbf4df 100644
--- a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp
@@ -66,7 +66,7 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
ntflags |= 0x040 /*FILE_NON_DIRECTORY_FILE*/; // do not open a directory
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -134,7 +134,7 @@ result<file_handle> file_handle::file(const path_handle &base, file_handle::path
creation = CREATE_ALWAYS;
break;
}
- path_view::c_str<> zpath(path, false);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFileW_(zpath.buffer, access, fileshare, nullptr, creation, attribs, nullptr))) // NOLINT
{
DWORD errcode = GetLastError();
diff --git a/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp b/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp
index 6e80627e..c08a3a89 100644
--- a/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/fs_handle.ipp
@@ -83,7 +83,7 @@ result<path_handle> fs_handle::parent_path_handle(deadline d) const noexcept
DWORD fileshare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(filename, true);
+ path_view::c_str<> zpath(filename, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -139,7 +139,7 @@ result<void> fs_handle::relink(const path_handle &base, path_view_type path, boo
// If the target is a win32 path, we need to convert to NT path and call ourselves
if(!base.is_valid() && !path.is_ntpath())
{
- path_view::c_str<> zpath(path, false);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
UNICODE_STRING NtPath{};
if(RtlDosPathNameToNtPathName_U(zpath.buffer, &NtPath, nullptr, nullptr) == 0u)
{
@@ -152,7 +152,7 @@ result<void> fs_handle::relink(const path_handle &base, path_view_type path, boo
}
});
// RtlDosPathNameToNtPathName_U outputs \??\path, so path.is_ntpath() will be false.
- return relink(base, path_view_type(NtPath.Buffer, NtPath.Length / sizeof(wchar_t), false), atomic_replace, d);
+ return relink(base, path_view_type(NtPath.Buffer, NtPath.Length / sizeof(wchar_t), path_view::zero_terminated), atomic_replace, d);
}
HANDLE duph = INVALID_HANDLE_VALUE;
@@ -188,7 +188,7 @@ result<void> fs_handle::relink(const path_handle &base, path_view_type path, boo
duph = h.native_handle().h;
}
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -227,7 +227,7 @@ result<void> fs_handle::link(const path_handle &base, path_view_type path, deadl
// If the target is a win32 path, we need to convert to NT path and call ourselves
if(!base.is_valid() && !path.is_ntpath())
{
- path_view::c_str<> zpath(path, false);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
UNICODE_STRING NtPath{};
if(RtlDosPathNameToNtPathName_U(zpath.buffer, &NtPath, nullptr, nullptr) == 0u)
{
@@ -240,12 +240,12 @@ result<void> fs_handle::link(const path_handle &base, path_view_type path, deadl
}
});
// RtlDosPathNameToNtPathName_U outputs \??\path, so path.is_ntpath() will be false.
- return link(base, path_view_type(NtPath.Buffer, NtPath.Length / sizeof(wchar_t), false), d);
+ return link(base, path_view_type(NtPath.Buffer, NtPath.Length / sizeof(wchar_t), path_view::zero_terminated), d);
}
HANDLE duph = h.native_handle().h;
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
diff --git a/include/llfio/v2.0/detail/impl/windows/import.hpp b/include/llfio/v2.0/detail/impl/windows/import.hpp
index b1c538c7..a511e509 100644
--- a/include/llfio/v2.0/detail/impl/windows/import.hpp
+++ b/include/llfio/v2.0/detail/impl/windows/import.hpp
@@ -278,6 +278,15 @@ namespace windows_nt_kernel
LARGE_INTEGER MaximumSize;
} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
+ typedef struct _FILE_BASIC_INFORMATION // NOLINT
+ {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ ULONG FileAttributes;
+ } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
// From https://msdn.microsoft.com/en-us/library/bb432383%28v=vs.85%29.aspx
using NtQueryObject_t = NTSTATUS(NTAPI *)(_In_opt_ HANDLE Handle, _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, _Out_opt_ PVOID ObjectInformation,
_In_ ULONG ObjectInformationLength, _Out_opt_ PULONG ReturnLength);
@@ -294,6 +303,8 @@ namespace windows_nt_kernel
// From http://msdn.microsoft.com/en-us/library/windows/hardware/ff566492(v=vs.85).aspx
using NtOpenDirectoryObject_t = NTSTATUS(NTAPI *)(_Out_ PHANDLE DirectoryHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes);
+ // From https://docs.microsoft.com/en-us/windows/win32/devnotes/ntqueryattributesfile
+ using NtQueryAttributesFile_t = NTSTATUS(NTAPI *)(_In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PFILE_BASIC_INFORMATION FileInformation);
// From http://msdn.microsoft.com/en-us/library/windows/hardware/ff567011(v=vs.85).aspx
using NtOpenFile_t = NTSTATUS(NTAPI *)(_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes,
@@ -554,15 +565,6 @@ namespace windows_nt_kernel
using RtlFreeUnicodeString_t = NTSTATUS(NTAPI *)(PUNICODE_STRING String);
- typedef struct _FILE_BASIC_INFORMATION // NOLINT
- {
- LARGE_INTEGER CreationTime;
- LARGE_INTEGER LastAccessTime;
- LARGE_INTEGER LastWriteTime;
- LARGE_INTEGER ChangeTime;
- ULONG FileAttributes;
- } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
-
typedef struct _FILE_STANDARD_INFORMATION // NOLINT
{
LARGE_INTEGER AllocationSize;
@@ -920,6 +922,7 @@ namespace windows_nt_kernel
static NtQueryInformationFile_t NtQueryInformationFile;
static NtQueryVolumeInformationFile_t NtQueryVolumeInformationFile;
static NtOpenDirectoryObject_t NtOpenDirectoryObject;
+ static NtQueryAttributesFile_t NtQueryAttributesFile;
static NtOpenFile_t NtOpenFile;
static NtCreateFile_t NtCreateFile;
static NtReadFile_t NtReadFile;
@@ -1016,6 +1019,13 @@ namespace windows_nt_kernel
abort();
}
}
+ if(NtQueryAttributesFile == nullptr)
+ {
+ if((NtQueryAttributesFile = reinterpret_cast<NtQueryAttributesFile_t>(GetProcAddress(ntdllh, "NtQueryAttributesFile"))) == nullptr)
+ {
+ abort();
+ }
+ }
if(NtOpenFile == nullptr)
{
if((NtOpenFile = reinterpret_cast<NtOpenFile_t>(GetProcAddress(ntdllh, "NtOpenFile"))) == nullptr)
diff --git a/include/llfio/v2.0/detail/impl/windows/path_handle.ipp b/include/llfio/v2.0/detail/impl/windows/path_handle.ipp
index 07cec50d..bc938ad9 100644
--- a/include/llfio/v2.0/detail/impl/windows/path_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/path_handle.ipp
@@ -27,6 +27,53 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_BEGIN
+result<bool> path_handle::exists(path_view_type path) const noexcept
+{
+ try
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ LLFIO_LOG_FUNCTION_CALL(this);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
+ UNICODE_STRING _path{};
+ _path.Buffer = const_cast<wchar_t *>(zpath.buffer);
+ _path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
+ if(zpath.length >= 4 && _path.Buffer[0] == '\\' && _path.Buffer[1] == '!' && _path.Buffer[2] == '!' && _path.Buffer[3] == '\\')
+ {
+ _path.Buffer += 3;
+ _path.Length -= 3 * sizeof(wchar_t);
+ _path.MaximumLength -= 3 * sizeof(wchar_t);
+ }
+
+ OBJECT_ATTRIBUTES oa{};
+ memset(&oa, 0, sizeof(oa));
+ oa.Length = sizeof(OBJECT_ATTRIBUTES);
+ oa.ObjectName = &_path;
+ oa.RootDirectory = native_handle().h;
+ oa.Attributes = 0; // 0x40 /*OBJ_CASE_INSENSITIVE*/;
+ // if(!!(flags & file_flags::int_opening_link))
+ // oa.Attributes|=0x100/*OBJ_OPENLINK*/;
+
+ FILE_BASIC_INFORMATION fbi;
+ memset(&fbi, 0, sizeof(fbi));
+ NTSTATUS ntstat = NtQueryAttributesFile(&oa, &fbi);
+ if(ntstat < 0)
+ {
+ auto ret = ntkernel_error(ntstat);
+ if(ret == errc::no_such_file_or_directory)
+ {
+ return false;
+ }
+ return failure(std::move(ret));
+ }
+ return true;
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
+}
+
result<path_handle> path_handle::path(const path_handle &base, path_handle::path_view_type path) noexcept
{
windows_nt_kernel::init();
@@ -52,7 +99,7 @@ result<path_handle> path_handle::path(const path_handle &base, path_handle::path
ntflags |= 0x01 /*FILE_DIRECTORY_FILE*/; // required to open a directory
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -88,7 +135,7 @@ result<path_handle> path_handle::path(const path_handle &base, path_handle::path
{
DWORD creation = OPEN_EXISTING;
attribs |= FILE_FLAG_BACKUP_SEMANTICS; // required to open a directory
- path_view::c_str<> zpath(path, false);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFileW_(zpath.buffer, access, fileshare, nullptr, creation, attribs, nullptr))) // NOLINT
{
DWORD errcode = GetLastError();
diff --git a/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp b/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp
index 502b2a66..6f8c5d1f 100644
--- a/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/pipe_handle.ipp
@@ -72,7 +72,7 @@ result<pipe_handle> pipe_handle::pipe(pipe_handle::path_view_type path, pipe_han
ntflags &= ~0x00000008 /*FILE_NO_INTERMEDIATE_BUFFERING*/; // pipes always buffer
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
if(path.empty())
{
diff --git a/include/llfio/v2.0/detail/impl/windows/process_handle.ipp b/include/llfio/v2.0/detail/impl/windows/process_handle.ipp
index ef104231..ebc63d00 100644
--- a/include/llfio/v2.0/detail/impl/windows/process_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/process_handle.ipp
@@ -118,7 +118,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC std::unique_ptr<span<path_view_component>, proce
}
if(*s != '=')
{
- *arraye++ = path_view_component(s, e, true);
+ *arraye++ = path_view_component(s, e, path_view::zero_terminated);
out = {array, arraye};
}
}
@@ -268,7 +268,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<process_handle> process_handle::launch_pr
}));
}
*envbuffere = 0;
- path_view::c_str<> zpath(path);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
PROCESS_INFORMATION pi;
if(!CreateProcessW(zpath.buffer, argsbuffer, nullptr, nullptr, true, CREATE_UNICODE_ENVIRONMENT, envbuffer, nullptr, &si, &pi))
return win32_error();
diff --git a/include/llfio/v2.0/detail/impl/windows/storage_profile.ipp b/include/llfio/v2.0/detail/impl/windows/storage_profile.ipp
index 044b7d18..407e06a9 100644
--- a/include/llfio/v2.0/detail/impl/windows/storage_profile.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/storage_profile.ipp
@@ -355,7 +355,8 @@ namespace storage_profile
}
*e++ = '0' + (vde->Extents[0].DiskNumber % 10);
*e = 0;
- OUTCOME_TRY(auto &&diskh, file_handle::file({}, path_view(physicaldrivename, e - physicaldrivename, true), handle::mode::none, handle::creation::open_existing, handle::caching::only_metadata));
+ OUTCOME_TRY(auto &&diskh, file_handle::file({}, path_view(physicaldrivename, e - physicaldrivename, path_view::zero_terminated),
+ handle::mode::none, handle::creation::open_existing, handle::caching::only_metadata));
memset(&spq, 0, sizeof(spq));
spq.PropertyId = StorageDeviceProperty;
spq.QueryType = PropertyStandardQuery;
diff --git a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
index d36eda1b..54ec1daf 100644
--- a/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/windows/symlink_handle.ipp
@@ -107,7 +107,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
ntflags |= 0x040 /*FILE_NON_DIRECTORY_FILE*/; // do not open a directory
IO_STATUS_BLOCK isb = make_iostatus();
- path_view::c_str<> zpath(path, true);
+ path_view::c_str<> zpath(path, path_view::not_zero_terminated);
UNICODE_STRING _path{};
_path.Buffer = const_cast<wchar_t *>(zpath.buffer);
_path.MaximumLength = (_path.Length = static_cast<USHORT>(zpath.length * sizeof(wchar_t))) + sizeof(wchar_t);
@@ -161,7 +161,7 @@ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<symlink_handle> symlink_handle::symlink(c
}
// required to open a symlink
attribs |= FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
- path_view::c_str<> zpath(path, false);
+ path_view::c_str<> zpath(path, path_view::zero_terminated);
if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFileW_(zpath.buffer, access, fileshare, nullptr, creation, attribs, nullptr))) // NOLINT
{
DWORD errcode = GetLastError();
@@ -219,11 +219,15 @@ result<symlink_handle::buffers_type> symlink_handle::read(symlink_handle::io_req
switch(rpd->ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
- tofill._link = path_view(rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.SubstituteNameOffset / sizeof(rpd->MountPointReparseBuffer.PathBuffer[0]), rpd->MountPointReparseBuffer.SubstituteNameLength / sizeof(rpd->MountPointReparseBuffer.PathBuffer[0]), true);
+ tofill._link = path_view(
+ rpd->MountPointReparseBuffer.PathBuffer + rpd->MountPointReparseBuffer.SubstituteNameOffset / sizeof(rpd->MountPointReparseBuffer.PathBuffer[0]),
+ rpd->MountPointReparseBuffer.SubstituteNameLength / sizeof(rpd->MountPointReparseBuffer.PathBuffer[0]), path_view::zero_terminated);
tofill._type = symlink_type::win_junction;
return std::move(tofill);
case IO_REPARSE_TAG_SYMLINK:
- tofill._link = path_view(rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(rpd->SymbolicLinkReparseBuffer.PathBuffer[0]), rpd->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(rpd->SymbolicLinkReparseBuffer.PathBuffer[0]), true);
+ tofill._link = path_view(
+ rpd->SymbolicLinkReparseBuffer.PathBuffer + rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(rpd->SymbolicLinkReparseBuffer.PathBuffer[0]),
+ rpd->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(rpd->SymbolicLinkReparseBuffer.PathBuffer[0]), path_view::zero_terminated);
tofill._type = symlink_type::symbolic;
return std::move(tofill);
}
@@ -247,7 +251,7 @@ result<symlink_handle::const_buffers_type> symlink_handle::write(symlink_handle:
auto *buffer = req.kernelbuffer.empty() ? alloca(buffersize) : req.kernelbuffer.data();
memset(buffer, 0, sizeof(REPARSE_DATA_BUFFER));
auto *rpd = (REPARSE_DATA_BUFFER *) buffer;
- path_view::c_str<> zpath(req.buffers.path(), true);
+ path_view::c_str<> zpath(req.buffers.path(), path_view::zero_terminated);
switch(req.buffers.type())
{
case symlink_type::none:
diff --git a/include/llfio/v2.0/path_handle.hpp b/include/llfio/v2.0/path_handle.hpp
index a931bd42..e1df68d5 100644
--- a/include/llfio/v2.0/path_handle.hpp
+++ b/include/llfio/v2.0/path_handle.hpp
@@ -107,6 +107,17 @@ public:
return path_handle(std::move(newh));
}
+ /*! Returns whether a file entry exists more efficiently that opening and
+ closing a `file_handle`. Note that this can be a rich source of TOCTOU
+ security attacks! Be aware that symbolic links are NOT dereferenced, so
+ a subsequent file handle open may fail.
+ */
+ LLFIO_MAKE_FREE_FUNCTION
+ result<bool> exists(path_view_type path) const noexcept;
+ //! \overload
+ LLFIO_MAKE_FREE_FUNCTION
+ static inline result<bool> exists(const path_handle &base, path_view_type path) noexcept { return base.exists(path); }
+
/*! Create a path handle opening access to some location on the filing system.
Some operating systems provide a particularly lightweight method of doing this
(Linux: `O_PATH`, Windows: no access perms) which is much faster than opening
diff --git a/include/llfio/v2.0/path_view.hpp b/include/llfio/v2.0/path_view.hpp
index d296b9c4..d4ee5fb9 100644
--- a/include/llfio/v2.0/path_view.hpp
+++ b/include/llfio/v2.0/path_view.hpp
@@ -69,6 +69,14 @@ namespace detail
}
return e - s;
}
+ constexpr inline size_t constexpr_strlen(const byte *s) noexcept
+ {
+ const byte *e = s;
+ for(; *e != to_byte(0); e++)
+ {
+ }
+ return e - s;
+ }
#if LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED
#ifdef _MSC_VER // MSVC's standard library refuses any basic_string_view<T> where T is not an unsigned type
@@ -125,26 +133,26 @@ 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);
+ const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ const LLFIO_V2_NAMESPACE::byte *src_buffer, size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
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);
+ size_t src_buffer_length, const std::locale *loc);
class path_view_iterator;
} // namespace detail
@@ -153,11 +161,10 @@ class path_view_component;
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept;
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator!=(path_view_component x, path_view_component y) noexcept;
template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view_component view, F &&f);
+template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(F &&f, path_view_component view);
inline std::ostream &operator<<(std::ostream &s, const path_view_component &v);
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view x, path_view y) noexcept;
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator!=(path_view x, path_view y) noexcept;
-template <class F> inline LLFIO_PATH_VIEW_CONSTEXPR auto visit(path_view view, F &&f);
-inline std::ostream &operator<<(std::ostream &s, const path_view &v);
/*! \class path_view_component
\brief An iterated part of a `path_view`.
@@ -169,10 +176,12 @@ class LLFIO_DECL path_view_component
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator==(path_view_component x, path_view_component y) noexcept;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator!=(path_view_component x, path_view_component y) noexcept;
template <class F> friend inline LLFIO_PATH_VIEW_CONSTEXPR auto LLFIO_V2_NAMESPACE::visit(path_view_component view, F &&f);
- template <class F> friend inline LLFIO_PATH_VIEW_CONSTEXPR auto LLFIO_V2_NAMESPACE::visit(path_view view, F &&f);
+ template <class F> friend inline LLFIO_PATH_VIEW_CONSTEXPR auto LLFIO_V2_NAMESPACE::visit(F &&f, path_view_component view);
friend inline std::ostream &LLFIO_V2_NAMESPACE::operator<<(std::ostream &s, const path_view_component &v);
public:
+ //! The size type
+ using size_type = filesystem::path::string_type::size_type;
//! The preferred separator type
static constexpr auto preferred_separator = filesystem::path::preferred_separator;
@@ -196,6 +205,26 @@ public:
//! The default internal buffer size used by `c_str`.
static constexpr size_t default_internal_buffer_size = 1024; // 2Kb for wchar_t, 1Kb for char
+ //! How to interpret separators
+ enum format : uint8_t
+ {
+ unknown,
+ native_format, //!< Separate at the native path separator only.
+ generic_format, //!< Separate at the generic path separator only.
+ auto_format, //!< Separate at both the native and generic path separators.
+ binary_format //!< Do not separate at any path separator.
+ };
+
+ //! The zero termination to use
+ enum zero_termination
+ {
+ zero_terminated, //!< The input is zero terminated, or requested output ought to be zero terminated.
+ not_zero_terminated, //!< The input is not zero terminated, or requested output ought to not be zero terminated.
+ };
+
+ //! The default deleter to use
+ template <class T> using default_deleter = std::default_delete<T>;
+
private:
static constexpr auto _npos = string_view::npos;
union
@@ -207,89 +236,191 @@ private:
const char16_t *_char16str;
};
size_t _length{0}; // in characters, excluding any zero terminator
- unsigned _zero_terminated : 1;
- unsigned _passthrough : 1;
- unsigned _char : 1;
- unsigned _wchar : 1;
- unsigned _utf8 : 1;
- unsigned _utf16 : 1;
+ uint16_t _zero_terminated : 1;
+ uint16_t _passthrough : 1;
+ uint16_t _char : 1;
+ uint16_t _wchar : 1;
+ uint16_t _utf8 : 1;
+ uint16_t _utf16 : 1;
+ uint16_t _reserved1 : 10;
+ format _format{format::unknown};
+ uint8_t _reserved2{0};
public:
- //! Constructs an empty path view component
- constexpr path_view_component()
+ //! Constructs an empty path view component (DEVIATES from P1030, is not trivial due to C++ 14 compatibility)
+ constexpr path_view_component() noexcept
: _zero_terminated(false)
, _passthrough(false)
, _char(false)
, _wchar(false)
, _utf8(false)
, _utf16(false)
+ , _reserved1(0)
{
} // NOLINT
- constexpr path_view_component(const byte *b, size_t l, bool zt)
- : _bytestr((l == 0) ? nullptr : b)
- , _length((l == 0) ? 0 : l)
- , _zero_terminated((l == 0) ? false : zt)
- , _passthrough((l == 0) ? false : true)
- , _char(false)
- , _wchar(false)
- , _utf8(false)
- , _utf16(false)
+ //! Implicitly constructs a path view from a path. The input path MUST continue to exist for this view to be valid.
+ path_view_component(const filesystem::path &v) noexcept // NOLINT
+ : path_view_component(v.native().c_str(), v.native().size(), zero_terminated, native_format)
{
}
- constexpr path_view_component(const char *b, size_t l, bool zt)
+ /*! Constructs from a basic string if the character type is one of
+ `char`, `wchar_t`, `char8_t` or `char16_t`.
+ */
+ LLFIO_TEMPLATE(class Char)
+ LLFIO_TREQUIRES(LLFIO_TPRED(is_source_chartype_acceptable<Char>))
+ constexpr path_view_component(const std::basic_string<Char> &v, format fmt = auto_format) noexcept // NOLINT
+ : path_view_component(v.data(), v.size(), zero_terminated, fmt)
+ {
+ }
+ /*! Constructs from a lengthed array of one of `char`, `wchar_t`, `char8_t` or `char16_t`. The input
+ string MUST continue to exist for this view to be valid.
+ */
+ constexpr path_view_component(const char *b, size_t l, enum zero_termination zt, format fmt = auto_format) noexcept
: _charstr((l == 0) ? nullptr : b)
, _length((l == 0) ? 0 : l)
- , _zero_terminated((l == 0) ? false : zt)
+ , _zero_terminated((l == 0) ? false : (zt == zero_terminated))
, _passthrough(false)
, _char((l == 0) ? false : true)
, _wchar(false)
, _utf8(false)
, _utf16(false)
+ , _reserved1(0)
+ , _format(fmt)
{
}
- constexpr path_view_component(const wchar_t *b, size_t l, bool zt)
+ /*! Constructs from a lengthed array of one of `char`, `wchar_t`, `char8_t` or `char16_t`. The input
+ string MUST continue to exist for this view to be valid.
+ */
+ constexpr path_view_component(const wchar_t *b, size_t l, enum zero_termination zt, format fmt = auto_format) noexcept
: _wcharstr((l == 0) ? nullptr : b)
, _length((l == 0) ? 0 : l)
- , _zero_terminated((l == 0) ? false : zt)
+ , _zero_terminated((l == 0) ? false : (zt == zero_terminated))
, _passthrough(false)
, _char(false)
, _wchar((l == 0) ? false : true)
, _utf8(false)
, _utf16(false)
+ , _reserved1(0)
+ , _format(fmt)
{
}
- constexpr path_view_component(const char8_t *b, size_t l, bool zt)
+ /*! Constructs from a lengthed array of one of `char`, `wchar_t`, `char8_t` or `char16_t`. The input
+ string MUST continue to exist for this view to be valid.
+ */
+ constexpr path_view_component(const char8_t *b, size_t l, enum zero_termination zt, format fmt = auto_format) noexcept
: _char8str((l == 0) ? nullptr : b)
, _length((l == 0) ? 0 : l)
- , _zero_terminated((l == 0) ? false : zt)
+ , _zero_terminated((l == 0) ? false : (zt == zero_terminated))
, _passthrough(false)
, _char(false)
, _wchar(false)
, _utf8((l == 0) ? false : true)
, _utf16(false)
+ , _reserved1(0)
+ , _format(fmt)
{
}
- constexpr path_view_component(const char16_t *b, size_t l, bool zt)
+ /*! Constructs from a lengthed array of one of `char`, `wchar_t`, `char8_t` or `char16_t`. The input
+ string MUST continue to exist for this view to be valid.
+ */
+ constexpr path_view_component(const char16_t *b, size_t l, enum zero_termination zt, format fmt = auto_format) noexcept
: _char16str((l == 0) ? nullptr : b)
, _length((l == 0) ? 0 : l)
- , _zero_terminated((l == 0) ? false : zt)
+ , _zero_terminated((l == 0) ? false : (zt == zero_terminated))
, _passthrough(false)
, _char(false)
, _wchar(false)
, _utf8(false)
, _utf16((l == 0) ? false : true)
+ , _reserved1(0)
+ , _format(fmt)
+ {
+ }
+ /*! Constructs from a lengthed array of `byte`. The input
+ array MUST continue to exist for this view to be valid.
+ */
+ constexpr path_view_component(const byte *b, size_t l, enum zero_termination zt) noexcept
+ : _bytestr((l == 0) ? nullptr : b)
+ , _length((l == 0) ? 0 : l)
+ , _zero_terminated((l == 0) ? false : (zt == zero_terminated))
+ , _passthrough((l == 0) ? false : true)
+ , _char(false)
+ , _wchar(false)
+ , _utf8(false)
+ , _utf16(false)
+ , _reserved1(0)
+ , _format(binary_format)
{
}
+
+ /*! Implicitly constructs a path view from a zero terminated pointer to a
+ character array, which must be one of `char`, `wchar_t`, `char8_t` or `char16_t`.
+ The input string MUST continue to exist for this view to be valid.
+ */
LLFIO_TEMPLATE(class Char)
- LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_acceptable<Char>))
- constexpr path_view_component(const Char *s)
- : path_view_component(s, detail::constexpr_strlen(s), true)
+ LLFIO_TREQUIRES(LLFIO_TPRED(is_source_chartype_acceptable<Char>))
+ constexpr path_view_component(const Char *s, format fmt = auto_format) noexcept
+ : path_view_component(s, detail::constexpr_strlen(s), zero_terminated, fmt)
{
}
+ /*! Implicitly constructs a path view from a zero terminated pointer to byte array.
+ The input array MUST continue to exist for this view to be valid.
+ */
+ constexpr path_view_component(const byte *s) noexcept
+ : path_view_component(s, detail::constexpr_strlen(s), zero_terminated)
+ {
+ }
+
+ /*! Constructs from a basic string view if the character type is one of
+ `char`, `wchar_t`, `char8_t` or `char16_t`.
+ */
LLFIO_TEMPLATE(class Char)
- LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_acceptable<Char>))
- constexpr path_view_component(const Char *s, const Char *e, bool zt)
- : path_view_component(s, e - s, zt)
+ LLFIO_TREQUIRES(LLFIO_TPRED(is_source_chartype_acceptable<Char>))
+ constexpr path_view_component(basic_string_view<Char> v, enum zero_termination zt, format fmt = auto_format) noexcept
+ : path_view_component(v.data(), v.size(), zt, fmt)
+ {
+ }
+ /*! Constructs from a `span<const byte>`.
+ */
+ constexpr path_view_component(span<const byte> v, enum zero_termination zt) noexcept
+ : path_view_component(v.data(), v.size(), zt)
+ {
+ }
+
+ /*! Constructs from an iterator sequence if the iterator is contiguous, if its
+ value type is one of `char`, `wchar_t`, `char8_t` or `char16_t`.
+
+ (DEVIATES from P1030, cannot detect contiguous iterator in SFINAE in C++ 14)
+ */
+ LLFIO_TEMPLATE(class It, class End)
+ LLFIO_TREQUIRES(LLFIO_TPRED(is_source_chartype_acceptable<typename It::value_type>), LLFIO_TPRED(is_source_chartype_acceptable<typename End::value_type>))
+ constexpr path_view_component(It b, End e, enum zero_termination zt, format fmt = auto_format) noexcept
+ : path_view_component(addressof(*b), e - b, zt, fmt)
+ {
+ }
+ //! \overload
+ LLFIO_TEMPLATE(class It, class End)
+ LLFIO_TREQUIRES(LLFIO_TPRED(is_source_chartype_acceptable<std::decay_t<It>>), LLFIO_TPRED(is_source_chartype_acceptable<std::decay_t<End>>))
+ constexpr path_view_component(It *b, End *e, enum zero_termination zt, format fmt = auto_format) noexcept
+ : path_view_component(b, e - b, zt, fmt)
+ {
+ }
+ /*! Constructs from an iterator sequence if the iterator is contiguous, if its
+ value type is `byte`.
+
+ (DEVIATES from P1030, cannot detect contiguous iterator in SFINAE in C++ 14)
+ */
+ LLFIO_TEMPLATE(class It, class End)
+ LLFIO_TREQUIRES(LLFIO_TPRED(std::is_same<typename It::value_type, byte>::value), LLFIO_TPRED(std::is_same<typename End::value_type, byte>::value))
+ constexpr path_view_component(It b, End e, enum zero_termination zt) noexcept
+ : path_view_component(addressof(*b), e - b, zt, binary_format)
+ {
+ }
+ //! \overload
+ LLFIO_TEMPLATE(class It, class End)
+ LLFIO_TREQUIRES(LLFIO_TPRED(std::is_same<std::decay_t<It>, byte>::value), LLFIO_TPRED(std::is_same<std::decay_t<End>, byte>::value))
+ constexpr path_view_component(It *b, End *e, enum zero_termination zt) noexcept
+ : path_view_component(b, e - b, zt, binary_format)
{
}
@@ -308,44 +439,90 @@ private:
constexpr auto _find_first_sep(size_t startidx = 0) const noexcept
{
using LLFIO_V2_NAMESPACE::basic_string_view;
+ switch(_format)
+ {
+ case format::binary_format:
+ return _npos;
#ifdef _WIN32
- return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find_first_of((const char8_t *) "/\\", startidx) //
- :
- (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find_first_of((const char16_t *) L"/\\", startidx) //
- :
- (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find_first_of(L"/\\", startidx) //
- :
- basic_string_view<char>((const char *) _bytestr, _length).find_first_of((const char *) "/\\", startidx)));
+ case format::auto_format:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find_first_of((const char8_t *) "/\\", startidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find_first_of((const char16_t *) L"/\\", startidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find_first_of(L"/\\", startidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).find_first_of((const char *) "/\\", startidx)));
+ case format::native_format:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find(preferred_separator, startidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find(preferred_separator, startidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find(preferred_separator, startidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).find(preferred_separator, startidx)));
+ case format::generic_format:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find('/', startidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find('/', startidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find('/', startidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).find('/', startidx)));
#else
- return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find(preferred_separator, startidx) //
- :
- (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find(preferred_separator, startidx) //
- :
- (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find(preferred_separator, startidx) //
- :
- basic_string_view<char>((const char *) _bytestr, _length).find(preferred_separator, startidx)));
+ default:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find(preferred_separator, startidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find(preferred_separator, startidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find(preferred_separator, startidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).find(preferred_separator, startidx)));
#endif
+ }
}
constexpr auto _find_last_sep(size_t endidx = _npos) const noexcept
{
using LLFIO_V2_NAMESPACE::basic_string_view;
+ switch(_format)
+ {
+ case format::binary_format:
+ return _npos;
#ifdef _WIN32
- return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find_last_of((const char8_t *) "/\\", endidx) //
- :
- (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find_last_of((const char16_t *) L"/\\", endidx) //
- :
- (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find_last_of(L"/\\", endidx) //
- :
- basic_string_view<char>((const char *) _bytestr, _length).find_last_of("/\\", endidx)));
+ case format::auto_format:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).find_last_of((const char8_t *) "/\\", endidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).find_last_of((const char16_t *) L"/\\", endidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).find_last_of(L"/\\", endidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).find_last_of("/\\", endidx)));
+ case format::native_format:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).rfind(preferred_separator, endidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).rfind(preferred_separator, endidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).rfind(preferred_separator, endidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).rfind(preferred_separator, endidx)));
+ case format::generic_format:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).rfind('/', endidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).rfind('/', endidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).rfind('/', endidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).rfind('/', endidx)));
#else
- return _utf8 ? basic_string_view<char8_t>(_char8str, _length).rfind(preferred_separator, endidx) //
- :
- (_utf16 ? basic_string_view<char16_t>(_char16str, _length).rfind(preferred_separator, endidx) //
- :
- (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).rfind(preferred_separator, endidx) //
- :
- basic_string_view<char>((const char *) _bytestr, _length).rfind(preferred_separator, endidx)));
+ default:
+ return _utf8 ? basic_string_view<char8_t>(_char8str, _length).rfind(preferred_separator, endidx) //
+ :
+ (_utf16 ? basic_string_view<char16_t>(_char16str, _length).rfind(preferred_separator, endidx) //
+ :
+ (_wchar ? basic_string_view<wchar_t>(_wcharstr, _length).rfind(preferred_separator, endidx) //
+ :
+ basic_string_view<char>((const char *) _bytestr, _length).rfind(preferred_separator, endidx)));
#endif
+ }
}
LLFIO_PATH_VIEW_CONSTEXPR path_view_component _filename() const noexcept
{
@@ -354,7 +531,7 @@ private:
{
return *this;
}
- return _invoke([sep_idx, this](const auto &v) { return path_view_component(v.data() + sep_idx + 1, v.size() - sep_idx - 1, _zero_terminated); });
+ return _invoke([sep_idx, this](const auto &v) { return path_view_component(v.data() + sep_idx + 1, v.size() - sep_idx - 1, zero_termination()); });
}
public:
@@ -366,6 +543,14 @@ public:
const byte *_raw_data() const noexcept { return _bytestr; }
+ //! Swap the view with another
+ constexpr void swap(path_view_component &o) noexcept
+ {
+ path_view_component x = *this;
+ *this = o;
+ o = x;
+ }
+
//! True if empty
LLFIO_NODISCARD constexpr bool empty() const noexcept { return _length == 0; }
@@ -375,15 +560,20 @@ public:
return _invoke([](const auto &v) { return v.size(); });
}
- //! Swap the view with another
- constexpr void swap(path_view_component &o) noexcept
- {
- path_view_component x = *this;
- *this = o;
- o = x;
- }
+ //! How path separators shall be interpreted
+ constexpr format formatting() const noexcept { return _format; }
+
+ //! True if input is declared to be zero terminated
+ constexpr bool has_zero_termination() const noexcept { return _zero_terminated; }
+ //! The zero termination during construction
+ constexpr enum zero_termination zero_termination() const noexcept { return _zero_terminated ? zero_terminated : not_zero_terminated; }
- // True if the view contains any of the characters `*`, `?`, (POSIX only: `[` or `]`).
+ //! True if `stem()` returns a non-empty path.
+ LLFIO_PATH_VIEW_CONSTEXPR bool has_stem() const noexcept { return !stem().empty(); }
+ //! True if `extension()` returns a non-empty path.
+ LLFIO_PATH_VIEW_CONSTEXPR bool has_extension() const noexcept { return !extension().empty(); }
+
+ //! True if the view contains any of the characters `*`, `?`, (POSIX only: `[` or `]`).
LLFIO_PATH_VIEW_CONSTEXPR bool contains_glob() const noexcept
{
return _invoke([](const auto &v) {
@@ -407,7 +597,7 @@ public:
{
return self;
}
- return path_view_component(v.data(), dot_idx, false);
+ return path_view_component(v.data(), dot_idx, not_zero_terminated);
});
}
//! Returns a view of the file extension part of this view
@@ -420,7 +610,7 @@ public:
{
return path_view_component();
}
- return path_view_component(v.data() + dot_idx, v.size() - dot_idx, _zero_terminated);
+ return path_view_component(v.data() + dot_idx, v.size() - dot_idx, zero_termination());
});
}
@@ -455,16 +645,20 @@ private:
}
// 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
+ static int _compare(basic_string_view<CharT> a, enum zero_termination /*unused*/, basic_string_view<CharT> b, enum zero_termination /*unused*/,
+ const std::locale * /*unused*/) 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
+ static int _compare(basic_string_view<Char1T> a, enum zero_termination a_zt, basic_string_view<Char2T> b, enum zero_termination b_zt,
+ const std::locale *loc) 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);
+ path_view_component _a_(a.data(), a.size(), a_zt);
+ path_view_component _b_(b.data(), b.size(), b_zt);
+ c_str<DestT, Deleter, _internal_buffer_size> _a(_a_, not_zero_terminated, loc);
+ c_str<DestT, Deleter, _internal_buffer_size> _b(_b_, not_zero_terminated, loc);
if(_a.length < _b.length)
{
return -1;
@@ -499,27 +693,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[]>,
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_deleter<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
+ constexpr int compare(path_view_component p, const std::locale &loc) 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([&](const auto &self) {
+ return p._invoke(
+ [&](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, zero_termination(), other, p.zero_termination(), &loc); });
+ });
}
//! \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_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T> &&is_source_acceptable<Char>))
- constexpr int compare(const Char *s) const noexcept
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_deleter<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 compare<T, Deleter, _internal_buffer_size>(path_view_component(s, detail::constexpr_strlen(s), true));
+ return _invoke([&](const auto &self) {
+ return p._invoke(
+ [&](const auto &other) { return _compare<T, Deleter, _internal_buffer_size>(self, zero_termination(), other, p.zero_termination(), nullptr); });
+ });
}
- //! \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_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_component` to get a path suitable for feeding to other code.
\tparam T The destination encoding required.
@@ -542,12 +737,15 @@ 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[]>,
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_deleter<T[]>,
size_t _internal_buffer_size = default_internal_buffer_size)
LLFIO_TREQUIRES(LLFIO_TPRED(is_source_acceptable<T>))
struct c_str
{
static_assert(is_source_acceptable<T>, "path_view_component::c_str<T> does not have a T which is one of byte, char, wchar_t, char8_t nor char16_t");
+ template <class DestT, class _Deleter, size_t _internal_buffer_size_, class Char1T, class Char2T>
+ friend int path_view_component::_compare(basic_string_view<Char1T> a, enum zero_termination a_zt, basic_string_view<Char2T> b, enum zero_termination b_zt,
+ const std::locale *loc) noexcept;
//! Type of the value type
using value_type = T;
@@ -556,24 +754,25 @@ public:
//! The size of the internal temporary buffer
static constexpr size_t internal_buffer_size = (_internal_buffer_size == 0) ? 1 : _internal_buffer_size;
- //! Number of characters, excluding zero terminating char, at buffer
- size_t length{0};
//! Pointer to the possibly-converted path
const value_type *buffer{nullptr};
+ //! Number of characters, excluding zero terminating char, at buffer
+ size_t length{0};
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> void _make_passthrough(path_view_component view, bool no_zero_terminate, U &allocate, const value_type *source)
+ template <class U> void _make_passthrough(path_view_component view, enum zero_termination output_zero_termination, 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 != '/') &&
+ (view.formatting() == format::auto_format || view.formatting() == format::generic_format) &&
+ view._invoke([](auto sv) { return sv.find('/') != _npos; });
length = view._length;
- if(!needs_slash_translation && (no_zero_terminate || view._zero_terminated))
+ if(!needs_slash_translation && (output_zero_termination == not_zero_terminated || view._zero_terminated))
{
buffer = source;
}
@@ -631,20 +830,15 @@ public:
#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant
#endif
- /*! Construct, performing any reencoding or memory copying required.
- \param view The path component view to use as source.
- \param no_zero_terminate Set to true if zero termination is not required.
- \param allocate A callable with prototype `value_type *(size_t length)` which
- is defaulted to `return new value_type[length];`. You can return `nullptr` if
- you wish, the consumer of `c_str` will see a `buffer` set to `nullptr`.
-
- If an error occurs during any conversion from UTF-8 or UTF-16, an exception of
- `system_error(errc::illegal_byte_sequence)` is thrown.
- This is because if you tell `path_view` that its source is UTF-8 or UTF-16, then that
- must be **valid** UTF. If you wish to supply UTF-invalid paths (which are legal
- on most filesystems), use native narrow or wide encoded source, or binary.
- */
- template <class U> c_str(path_view_component view, bool no_zero_terminate, U &&allocate)
+ private:
+ struct _internal_construct_tag
+ {
+ };
+ LLFIO_TEMPLATE(class U, class V)
+ LLFIO_TREQUIRES(LLFIO_TEXPR(std::declval<U>()((size_t) 1)), LLFIO_TEXPR(std::declval<V>()((value_type *) nullptr)))
+ c_str(_internal_construct_tag /*unused*/, path_view_component view, enum zero_termination output_zero_termination, const std::locale *loc, U &&allocate,
+ V &&deleter)
+ : _deleter(static_cast<V &&>(deleter))
{
if(0 == view._length)
{
@@ -661,36 +855,36 @@ public:
}
if(std::is_same<T, char>::value && view._char)
{
- _make_passthrough(view, no_zero_terminate, allocate, view._charstr);
+ _make_passthrough(view, output_zero_termination, allocate, view._charstr);
return;
}
if(std::is_same<T, wchar_t>::value && view._wchar)
{
- _make_passthrough(view, no_zero_terminate, allocate, view._wcharstr);
+ _make_passthrough(view, output_zero_termination, allocate, view._wcharstr);
return;
}
if(std::is_same<T, char8_t>::value && view._utf8)
{
- _make_passthrough(view, no_zero_terminate, allocate, view._char8str);
+ _make_passthrough(view, output_zero_termination, allocate, view._char8str);
return;
}
if(std::is_same<T, char16_t>::value && view._utf16)
{
- _make_passthrough(view, no_zero_terminate, allocate, view._char16str);
+ _make_passthrough(view, output_zero_termination, allocate, view._char16str);
return;
}
#ifdef _WIN32
// On Windows, consider char16_t input equivalent to wchar_t
if(std::is_same<T, wchar_t>::value && view._utf16)
{
- _make_passthrough(view, no_zero_terminate, allocate, view._wcharstr);
+ _make_passthrough(view, output_zero_termination, allocate, view._wcharstr);
return;
}
#else
// On POSIX, consider char8_t input equivalent to char
if(std::is_same<T, char>::value && view._utf8)
{
- _make_passthrough(view, no_zero_terminate, allocate, view._charstr);
+ _make_passthrough(view, output_zero_termination, allocate, view._charstr);
return;
}
#endif
@@ -700,23 +894,23 @@ public:
value_type *end = nullptr;
if(view._passthrough)
{
- end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._bytestr, view._length);
+ end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._bytestr, view._length, loc);
}
else if(view._char)
{
- end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._charstr, view._length);
+ end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._charstr, view._length, loc);
}
else if(view._wchar)
{
- end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._wcharstr, view._length);
+ end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._wcharstr, view._length, loc);
}
else if(view._utf8)
{
- end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._char8str, view._length);
+ end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._char8str, view._length, loc);
}
else if(view._utf16)
{
- end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._char16str, view._length);
+ end = detail::reencode_path_to(required_length, _buffer, _internal_buffer_size, view._char16str, view._length, loc);
}
else
{
@@ -743,23 +937,23 @@ public:
end = allocated_buffer + (end - _buffer);
if(view._passthrough)
{
- end = detail::reencode_path_to(length, end, required_length, view._bytestr, view._length);
+ end = detail::reencode_path_to(length, end, required_length, view._bytestr, view._length, loc);
}
else if(view._char)
{
- end = detail::reencode_path_to(length, end, required_length, view._charstr, view._length);
+ end = detail::reencode_path_to(length, end, required_length, view._charstr, view._length, loc);
}
else if(view._wchar)
{
- end = detail::reencode_path_to(length, end, required_length, view._wcharstr, view._length);
+ end = detail::reencode_path_to(length, end, required_length, view._wcharstr, view._length, loc);
}
else if(view._utf8)
{
- end = detail::reencode_path_to(length, end, required_length, view._char8str, view._length);
+ end = detail::reencode_path_to(length, end, required_length, view._char8str, view._length, loc);
}
else if(view._utf16)
{
- end = detail::reencode_path_to(length, end, required_length, view._char16str, view._length);
+ end = detail::reencode_path_to(length, end, required_length, view._char16str, view._length, loc);
}
else
{
@@ -772,9 +966,53 @@ public:
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+ c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale *loc)
+ : c_str(
+ _internal_construct_tag(), view, output_zero_termination, loc,
+ [](size_t length) { return static_cast<value_type *>(::operator new[](length * sizeof(value_type))); }, deleter_type())
+ {
+ }
+
+ public:
+ /*! Construct, performing any reencoding or memory copying required.
+ \param view The path component view to use as source.
+ \param output_zero_termination The zero termination in the output desired
+ \param loc The locale to use to perform reencoding.
+ \param allocate A callable with prototype `value_type *(size_t length)` which
+ is defaulted to `return static_cast<value_type *>(::operator new[](length * sizeof(value_type)));`.
+ You can return `nullptr` if you wish, the consumer of `c_str` will
+ see a `buffer` set to `nullptr`.
+
+ If `loc` is defaulted, and an error occurs during any conversion from UTF-8 or UTF-16, an exception of
+ `system_error(errc::illegal_byte_sequence)` is thrown.
+ This is because if you tell `path_view` that its source is UTF-8 or UTF-16, then that
+ must be **valid** UTF. If you wish to supply UTF-invalid paths (which are legal
+ on most filesystems), use native narrow or wide encoded source, or binary.
+ */
+ template <class U, class V>
+ c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale &loc, U &&allocate, V &&deleter = deleter_type())
+ : c_str(_internal_construct_tag(), view, output_zero_termination, &loc, static_cast<U &&>(allocate), static_cast<V &&>(deleter))
+ {
+ }
+ //! \overload
+ template <class U, class V>
+ c_str(path_view_component view, enum zero_termination output_zero_termination, U &&allocate, V &&deleter = deleter_type())
+ : c_str(_internal_construct_tag(), view, output_zero_termination, (const std::locale *) nullptr, static_cast<U &&>(allocate),
+ static_cast<V &&>(deleter))
+ {
+ }
+ //! \overload
+ c_str(path_view_component view, enum zero_termination output_zero_termination, const std::locale &loc)
+ : c_str(
+ _internal_construct_tag(), view, output_zero_termination, &loc,
+ [](size_t length) { return static_cast<value_type *>(::operator new[](length * sizeof(value_type))); }, deleter_type())
+ {
+ }
//! \overload
- c_str(path_view_component view, bool no_zero_terminate = false)
- : c_str(view, no_zero_terminate, [](size_t length) { return new value_type[length]; })
+ c_str(path_view_component view, enum zero_termination output_zero_termination)
+ : c_str(
+ _internal_construct_tag(), view, output_zero_termination, (const std::locale *) nullptr,
+ [](size_t length) { return static_cast<value_type *>(::operator new[](length * sizeof(value_type))); }, deleter_type())
{
}
~c_str()
@@ -786,8 +1024,8 @@ public:
}
c_str(const c_str &) = delete;
c_str(c_str &&o) noexcept
- : length(o.length)
- , buffer(o.buffer)
+ : buffer(o.buffer)
+ , length(o.length)
, _call_deleter(o._call_deleter)
, _deleter(std::move(o._deleter))
{
@@ -827,6 +1065,8 @@ public:
#endif
friend struct c_str;
};
+static_assert(std::is_trivially_copyable<path_view_component>::value, "path_view_component is not trivially copyable!");
+static_assert(sizeof(path_view_component) == 3 * sizeof(void *), "path_view_component is not three pointers in size!");
inline LLFIO_PATH_VIEW_CONSTEXPR bool operator==(path_view_component x, path_view_component y) noexcept
{
if(x.native_size() != y.native_size())
@@ -1063,13 +1303,11 @@ routine.
If however you are taking input from some external piece of code, then for
maximum compatibility you should still use the Win32 API.
*/
-class path_view
+class path_view : public path_view_component
{
friend class detail::path_view_iterator;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator==(path_view x, path_view y) noexcept;
friend inline LLFIO_PATH_VIEW_CONSTEXPR bool LLFIO_V2_NAMESPACE::operator!=(path_view x, path_view y) noexcept;
- template <class F> friend inline LLFIO_PATH_VIEW_CONSTEXPR auto LLFIO_V2_NAMESPACE::visit(path_view view, F &&f);
- friend inline std::ostream &LLFIO_V2_NAMESPACE::operator<<(std::ostream &s, const path_view &v);
public:
//! Const iterator type
@@ -1081,120 +1319,22 @@ public:
//! Const reverse iterator
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
//! Size type
- using size_type = std::size_t;
+ using size_type = path_view_component::size_type;
//! Difference type
using difference_type = std::ptrdiff_t;
- //! The preferred separator type
- static constexpr auto preferred_separator = filesystem::path::preferred_separator;
-
- //! Character type for passthrough input
- using byte = LLFIO_V2_NAMESPACE::byte;
-#if LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED
- using char8_t = detail::char8_t;
-#endif
-#if !defined(__CHAR16_TYPE__) && !defined(_MSC_VER) // VS2015 onwards has built in char16_t
- enum class char16_t : unsigned short
- {
- };
-#endif
-
- //! True if path views can be constructed from this character type.
- //! i.e. is one of `char`, `wchar_t`, `char8_t`, `char16_t`
- template <class Char> static constexpr bool is_source_chartype_acceptable = path_view_component::is_source_chartype_acceptable<Char>;
-
- //! True if path views can be constructed from this source.
- //! i.e. `is_source_chartype_acceptable`, or is `byte`
- template <class Char> static constexpr bool is_source_acceptable = path_view_component::is_source_acceptable<Char>;
-
- //! The default internal buffer size used by `c_str`.
- static constexpr size_t default_internal_buffer_size = path_view_component::default_internal_buffer_size; // 2Kb for wchar_t, 1Kb for char
-
-private:
- static constexpr auto _npos = string_view::npos;
-
- path_view_component _state;
-
public:
//! Constructs an empty path view
constexpr path_view() {} // NOLINT
~path_view() = default;
- //! Implicitly constructs a path view from a path. The input path MUST continue to exist for this view to be valid.
- path_view(const filesystem::path &v) noexcept // NOLINT
- : _state(v.native().c_str(), v.native().size(), true)
- {
- }
+ using path_view_component::path_view_component;
+
//! Implicitly constructs a path view from a path view component. The input path MUST continue to exist for this view to be valid.
constexpr path_view(path_view_component v) noexcept // NOLINT
- : _state(v)
- {
- }
-
- //! 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.
- constexpr path_view(const wchar_t *v) noexcept // NOLINT
- : _state(v, detail::constexpr_strlen(v), true)
+ : path_view_component(v)
{
}
- //! 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.
- constexpr path_view(const char16_t *v) noexcept // NOLINT
- : _state(v, detail::constexpr_strlen(v), true)
- {
- }
-
- /*! Constructs a path view from a lengthed array of one of
- `byte`, `char`, `wchar_t`, `char8_t` or `char16_t`. The input
- string MUST continue to exist for this view to be valid.
- */
- LLFIO_TEMPLATE(class Char)
- LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_acceptable<Char>))
- constexpr path_view(const Char *v, size_t len, bool is_zero_terminated) noexcept
- : _state(v, len, is_zero_terminated)
- {
- }
- /*! Constructs a path view from a pair of pointers of one of
- `byte`, `char`, `wchar_t`, `char8_t` or `char16_t`. The input
- string MUST continue to exist for this view to be valid.
- */
- LLFIO_TEMPLATE(class Char)
- LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_acceptable<Char>))
- constexpr path_view(const Char *s, const Char *e, bool zt)
- : path_view(s, e - s, zt)
- {
- }
- /*! Constructs from a basic string if the character type is one of
- `char`, `wchar_t`, `char8_t` or `char16_t`.
- */
- LLFIO_TEMPLATE(class Char)
- LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_chartype_acceptable<Char>))
- constexpr path_view(const std::basic_string<Char> &v) noexcept // NOLINT
- : path_view(v.data(), v.size(), true)
- {
- }
- /*! Constructs from a basic string view if the character type is one of
- `char`, `wchar_t`, `char8_t` or `char16_t`.
- */
- LLFIO_TEMPLATE(class Char)
- LLFIO_TREQUIRES(LLFIO_TPRED(path_view_component::is_source_chartype_acceptable<Char>))
- constexpr path_view(basic_string_view<Char> v, bool is_zero_terminated) noexcept // NOLINT
- : path_view(v.data(), v.size(), is_zero_terminated)
- {
- }
-
//! Default copy constructor
path_view(const path_view &) = default;
//! Default move constructor
@@ -1204,24 +1344,22 @@ public:
//! Default move assignment
path_view &operator=(path_view &&p) noexcept = default;
- //! Swap the view with another
- constexpr void swap(path_view &o) noexcept { _state.swap(o._state); }
-
- const byte *_raw_data() const noexcept { return _state._bytestr; }
-
- //! True if empty
- LLFIO_NODISCARD LLFIO_PATH_VIEW_CONSTEXPR bool empty() const noexcept { return _state.empty(); }
+ //! True if has root path
LLFIO_PATH_VIEW_CONSTEXPR bool has_root_path() const noexcept { return !root_path().empty(); }
+ //! True if has root name
LLFIO_PATH_VIEW_CONSTEXPR bool has_root_name() const noexcept { return !root_name().empty(); }
+ //! True if has root directory
LLFIO_PATH_VIEW_CONSTEXPR bool has_root_directory() const noexcept { return !root_directory().empty(); }
+ //! True if has relative path
LLFIO_PATH_VIEW_CONSTEXPR bool has_relative_path() const noexcept { return !relative_path().empty(); }
+ //! True if has parent path
LLFIO_PATH_VIEW_CONSTEXPR bool has_parent_path() const noexcept { return !parent_path().empty(); }
+ //! True if has filename
LLFIO_PATH_VIEW_CONSTEXPR bool has_filename() const noexcept { return !filename().empty(); }
- LLFIO_PATH_VIEW_CONSTEXPR bool has_stem() const noexcept { return !stem().empty(); }
- LLFIO_PATH_VIEW_CONSTEXPR bool has_extension() const noexcept { return !extension().empty(); }
+ //! True if absolute
constexpr bool is_absolute() const noexcept
{
- auto sep_idx = _state._find_first_sep();
+ auto sep_idx = this->_find_first_sep();
if(_npos == sep_idx)
{
return false;
@@ -1229,7 +1367,7 @@ public:
#ifdef _WIN32
if(is_ntpath())
return true;
- return _state._invoke([sep_idx](const auto &v) {
+ return this->_invoke([sep_idx](const auto &v) {
if(sep_idx == 0)
{
if(v[sep_idx + 1] == preferred_separator) // double separator at front
@@ -1242,14 +1380,13 @@ public:
return sep_idx == 0;
#endif
}
+ //! True if relative
constexpr bool is_relative() const noexcept { return !is_absolute(); }
- // True if the path view contains any of the characters `*`, `?`, (POSIX only: `[` or `]`).
- LLFIO_PATH_VIEW_CONSTEXPR bool contains_glob() const noexcept { return _state.contains_glob(); }
#ifdef _WIN32
// True if the path view is a NT kernel path starting with `\!!\` or `\??\`
constexpr bool is_ntpath() const noexcept
{
- return _state._invoke([](const auto &v) {
+ return this->_invoke([](const auto &v) {
if(v.size() < 4)
{
return false;
@@ -1269,7 +1406,7 @@ public:
// True if the path view is a UNC path starting with `\\`
constexpr bool is_uncpath() const noexcept
{
- return _state._invoke([](const auto &v) {
+ return this->_invoke([](const auto &v) {
if(v.size() < 2)
{
return false;
@@ -1285,7 +1422,7 @@ public:
// True if the path view matches the format of an LLFIO deleted file
constexpr bool is_llfio_deleted() const noexcept
{
- return filename()._state._invoke([](const auto &v) {
+ return filename()._invoke([](const auto &v) {
if(v.size() == 64 + 8)
{
// Could be one of our "deleted" files, is he all hex + ".deleted"?
@@ -1320,44 +1457,44 @@ public:
//! Returns a copy of this view with the end adjusted to match the final separator.
LLFIO_PATH_VIEW_CONSTEXPR path_view remove_filename() const noexcept
{
- auto sep_idx = _state._find_last_sep();
+ auto sep_idx = this->_find_last_sep();
if(_npos == sep_idx)
{
return path_view();
}
- return _state._invoke([sep_idx](auto v) { return path_view(v.data(), sep_idx, false); });
+ return this->_invoke([sep_idx](auto v) { return path_view(v.data(), sep_idx, not_zero_terminated); });
}
//! Returns the size of the view in characters.
- LLFIO_PATH_VIEW_CONSTEXPR size_t native_size() const noexcept { return _state.native_size(); }
+ LLFIO_PATH_VIEW_CONSTEXPR size_t native_size() const noexcept { return this->native_size(); }
//! Returns a view of the root name part of this view e.g. C:
LLFIO_PATH_VIEW_CONSTEXPR path_view root_name() const noexcept
{
- auto sep_idx = _state._find_first_sep();
+ auto sep_idx = this->_find_first_sep();
if(_npos == sep_idx)
{
return path_view();
}
- return _state._invoke([sep_idx](const auto &v) { return path_view(v.data(), sep_idx, false); });
+ return this->_invoke([sep_idx](const auto &v) { return path_view(v.data(), sep_idx, not_zero_terminated); });
}
//! Returns a view of the root directory, if there is one e.g. /
LLFIO_PATH_VIEW_CONSTEXPR path_view root_directory() const noexcept
{
- auto sep_idx = _state._find_first_sep();
+ auto sep_idx = this->_find_first_sep();
if(_npos == sep_idx)
{
return path_view();
}
- return _state._invoke([sep_idx](const auto &v) {
+ return this->_invoke([sep_idx](const auto &v) {
#ifdef _WIN32
auto colon_idx = v.find(':');
if(colon_idx < sep_idx)
{
- return path_view(v.data() + sep_idx, 1, false);
+ return path_view(v.data() + sep_idx, 1, not_zero_terminated);
}
#endif
if(sep_idx == 0)
{
- return path_view(v.data(), 1, false);
+ return path_view(v.data(), 1, not_zero_terminated);
}
return path_view();
});
@@ -1365,40 +1502,40 @@ public:
//! Returns, if any, a view of the root path part of this view e.g. C:/
LLFIO_PATH_VIEW_CONSTEXPR path_view root_path() const noexcept
{
- auto sep_idx = _state._find_first_sep();
+ auto sep_idx = this->_find_first_sep();
if(_npos == sep_idx)
{
return path_view();
}
#ifdef _WIN32
- return _state._invoke([this, sep_idx](const auto &v) mutable {
+ return this->_invoke([this, sep_idx](const auto &v) mutable {
// Special case \\.\ and \\?\ to match filesystem::path
if(is_ntpath() || (v.size() >= 4 && sep_idx == 0 && v[1] == '\\' && (v[2] == '.' || v[2] == '?') && v[3] == '\\'))
{
- return path_view(v.data() + 0, 4, false);
+ return path_view(v.data() + 0, 4, not_zero_terminated);
}
// Is there a drive letter before the first separator?
auto colon_idx = v.find(':');
if(colon_idx < sep_idx)
{
- return path_view(v.data(), sep_idx + 1, false);
+ return path_view(v.data(), sep_idx + 1, not_zero_terminated);
}
// UNC paths return the server name as the root path
if(is_uncpath())
{
- sep_idx = _state._find_first_sep(2);
+ sep_idx = this->_find_first_sep(2);
if(_npos == sep_idx)
{
return *this;
}
- return path_view(v.data(), sep_idx + 1, false);
+ return path_view(v.data(), sep_idx + 1, not_zero_terminated);
}
#else
- return _state._invoke([sep_idx](const auto &v) {
+ return this->_invoke([sep_idx](const auto &v) {
#endif
if(sep_idx == 0)
{
- return path_view(v.data(), 1, false);
+ return path_view(v.data(), 1, not_zero_terminated);
}
return path_view();
});
@@ -1406,40 +1543,40 @@ public:
//! Returns a view of everything after the root path
LLFIO_PATH_VIEW_CONSTEXPR path_view relative_path() const noexcept
{
- auto sep_idx = _state._find_first_sep();
+ auto sep_idx = this->_find_first_sep();
if(_npos == sep_idx)
{
return *this;
}
#ifdef _WIN32
- return _state._invoke([this, sep_idx](const auto &v) mutable {
+ return this->_invoke([this, sep_idx](const auto &v) mutable {
// Special case \\.\ and \\?\ to match filesystem::path
if(is_ntpath() || (v.size() >= 4 && sep_idx == 0 && v[1] == '\\' && (v[2] == '.' || v[2] == '?') && v[3] == '\\'))
{
- return path_view(v.data() + 4, v.size() - 4, _state._zero_terminated);
+ return path_view(v.data() + 4, v.size() - 4, this->zero_termination());
}
// Is there a drive letter before the first separator?
auto colon_idx = v.find(':');
if(colon_idx < sep_idx)
{
- return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1, _state._zero_terminated);
+ return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1, this->zero_termination());
}
// UNC paths return the server name as the root path
if(is_uncpath())
{
- sep_idx = _state._find_first_sep(2);
+ sep_idx = this->_find_first_sep(2);
if(_npos == sep_idx)
{
return path_view();
}
- return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1, _state._zero_terminated);
+ return path_view(v.data() + sep_idx + 1, v.size() - sep_idx - 1, this->zero_termination());
}
#else
- return _state._invoke([this, sep_idx](const auto &v) {
+ return this->_invoke([this, sep_idx](const auto &v) {
#endif
if(sep_idx == 0)
{
- return path_view(v.data() + 1, v.size() - 1, _state._zero_terminated);
+ return path_view(v.data() + 1, v.size() - 1, this->zero_termination());
}
return *this;
});
@@ -1447,37 +1584,30 @@ public:
//! Returns a view of the everything apart from the filename part of this view
LLFIO_PATH_VIEW_CONSTEXPR path_view parent_path() const noexcept
{
- auto sep_idx = _state._find_last_sep();
+ auto sep_idx = this->_find_last_sep();
if(_npos == sep_idx)
{
return path_view();
}
#ifdef _WIN32
- return _state._invoke([this, sep_idx](const auto &v) {
+ return this->_invoke([this, sep_idx](const auto &v) {
// UNC paths return a trailing slash if the parent path is the server name
if(is_uncpath())
{
- auto sep_idx2 = _state._find_first_sep(2);
+ auto sep_idx2 = this->_find_first_sep(2);
if(sep_idx2 == sep_idx)
{
- return path_view(v.data(), sep_idx + 1, false);
+ return path_view(v.data(), sep_idx + 1, not_zero_terminated);
}
}
- return path_view(v.data(), sep_idx, false);
+ return path_view(v.data(), sep_idx, not_zero_terminated);
});
#else
- return _state._invoke([sep_idx](const auto &v) { return path_view(v.data(), sep_idx, false); });
+ return this->_invoke([sep_idx](const auto &v) { return path_view(v.data(), sep_idx, not_zero_terminated); });
#endif
}
//! Returns a view of the filename part of this view.
- LLFIO_PATH_VIEW_CONSTEXPR path_view filename() const noexcept { return _state._filename(); }
- //! Returns a view of the filename without any file extension
- LLFIO_PATH_VIEW_CONSTEXPR path_view_component stem() const noexcept { return _state.stem(); }
- //! Returns a view of the file extension part of this view
- LLFIO_PATH_VIEW_CONSTEXPR path_view_component extension() const noexcept { return _state.extension(); }
-
- //! Return the path view as a path. Allocates and copies memory!
- filesystem::path path() const { return _state.path(); }
+ LLFIO_PATH_VIEW_CONSTEXPR path_view filename() const noexcept { return this->_filename(); }
/*! Compares the two path views for equivalence or ordering using `T`
as the destination encoding, if necessary.
@@ -1495,77 +1625,16 @@ 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[]>,
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_deleter<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_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 inline int compare(path_view p, const std::locale &loc) 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_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[]>,
+ LLFIO_TEMPLATE(class T = typename filesystem::path::value_type, class Deleter = default_deleter<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>
- {
- //! Number of characters, excluding zero terminating char, at buffer
- using _base = path_view_component::c_str<T, Deleter, _internal_buffer_size>;
- c_str() = default;
- c_str(const c_str &) = delete;
- c_str(c_str &&) = default;
- c_str &operator=(const c_str &) = delete;
- c_str &operator=(c_str &&) = default;
- ~c_str() = default;
-
- /*! See constructor for `path_view_component::c_str`.
- */
- template <class U>
- c_str(path_view view, bool no_zero_terminate, U &&allocate)
- : _base(view._state, no_zero_terminate, static_cast<U &&>(allocate))
- {
- }
- //! \overload
- c_str(path_view view, bool no_zero_terminate = false)
- : _base(view._state, no_zero_terminate)
- {
- }
- };
-#ifdef __cpp_concepts
- template <class T, class Deleter, size_t _internal_buffer_size>
- requires(is_source_acceptable<T>)
-#elif defined(_MSC_VER)
- template <class T, class Deleter, size_t _internal_buffer_size, class>
-#else
-template <class T, class Deleter, size_t _internal_buffer_size, typename std::enable_if<(is_source_acceptable<T>), bool>::type>
-#endif
- friend struct c_str;
+ LLFIO_TREQUIRES(LLFIO_TPRED(path_view::is_source_acceptable<T>))
+ constexpr inline int compare(path_view p) const;
};
-//! \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;
-}
namespace detail
{
@@ -1611,16 +1680,17 @@ namespace detail
LLFIO_PATH_VIEW_CONSTEXPR value_type _get() const noexcept
{
assert(_parent != nullptr);
- return _parent->_state._invoke([this](const auto &v) {
+ return _parent->_invoke([this](const auto &v) {
assert(_end <= v.size());
assert(_begin <= _end);
- return path_view_component(v.data() + _begin, _end - _begin, (_end == v.size()) ? _parent->_state._zero_terminated : false);
+ return path_view_component(v.data() + _begin, _end - _begin,
+ (_end == v.size()) ? _parent->zero_termination() : path_view_component::not_zero_terminated);
});
}
LLFIO_PATH_VIEW_CONSTEXPR void _inc() noexcept
{
_begin = (_end > 0 && !_special) ? (_end + 1) : _end;
- _end = _parent->_state._find_first_sep(_begin);
+ _end = _parent->_find_first_sep(_begin);
if(0 == _end)
{
// Path has a beginning /
@@ -1656,7 +1726,7 @@ namespace detail
_end = 1;
return;
}
- _begin = _parent->_state._find_last_sep(_end - 1);
+ _begin = _parent->_find_last_sep(_end - 1);
if(is_end && _begin == _end - 1)
{
// Path has a trailing /
@@ -1835,6 +1905,35 @@ template <class T, class Deleter, size_t _internal_buffer_size, class>
#else
template <class T, class Deleter, size_t _internal_buffer_size, typename std::enable_if<(path_view::is_source_acceptable<T>), bool>::type>
#endif
+constexpr inline int path_view::compare(path_view o, const std::locale &loc) const
+{
+ auto it1 = begin(), it2 = o.begin();
+ for(; it1 != end() && it2 != o.end(); ++it1, ++it2)
+ {
+ int res = it1->compare<T, Deleter, _internal_buffer_size>(*it2, loc);
+ if(res != 0)
+ {
+ return res;
+ }
+ }
+ if(it1 == end() && it2 != o.end())
+ {
+ return -1;
+ }
+ if(it1 != end() && it2 == o.end())
+ {
+ return 1;
+ }
+ return 0; // identical
+}
+#ifdef __cpp_concepts
+template <class T, class Deleter, size_t _internal_buffer_size>
+requires(path_view::is_source_acceptable<T>)
+#elif defined(_MSC_VER)
+template <class T, class Deleter, size_t _internal_buffer_size, class>
+#else
+template <class T, class Deleter, size_t _internal_buffer_size, typename std::enable_if<(path_view::is_source_acceptable<T>), bool>::type>
+#endif
constexpr inline int path_view::compare(path_view o) const
{
auto it1 = begin(), it2 = o.begin();
@@ -1860,40 +1959,55 @@ constexpr inline int path_view::compare(path_view o) const
//! Append a path view component to a path
inline filesystem::path &operator+=(filesystem::path &a, path_view_component b)
{
- path_view_component::c_str<> zpath(b);
- a.concat(zpath.buffer);
+ path_view_component::c_str<> zpath(b, path_view_component::not_zero_terminated);
+ basic_string_view<filesystem::path::value_type> _(zpath.buffer, zpath.length);
+ a.concat(_.begin(), _.end());
return a;
}
//! Append a path view component to a path
inline filesystem::path &operator/=(filesystem::path &a, path_view_component b)
{
- path_view_component::c_str<> zpath(b);
- a.append(zpath.buffer);
+ path_view_component::c_str<> zpath(b, path_view_component::not_zero_terminated);
+ basic_string_view<filesystem::path::value_type> _(zpath.buffer, zpath.length);
+ a.append(_.begin(), _.end());
return a;
}
//! Append a path view component to a path
-inline filesystem::path operator/(filesystem::path a, path_view_component b)
+inline filesystem::path operator/(const filesystem::path &a, path_view_component b)
{
- return a /= b;
+ filesystem::path ret(a);
+ return ret /= b;
}
-//! Append a path view to a path
-inline filesystem::path &operator+=(filesystem::path &a, path_view b)
+//! Append a path view component to a path
+inline filesystem::path operator/(filesystem::path &&a, path_view_component b)
{
- path_view::c_str<> zpath(b);
- a.concat(zpath.buffer);
- return a;
+ filesystem::path ret(std::move(a));
+ return ret /= b;
}
-//! Append a path view to a path
-inline filesystem::path &operator/=(filesystem::path &a, path_view b)
+namespace detail
{
- path_view::c_str<> zpath(b);
- a.append(zpath.buffer);
- return a;
-}
-//! Append a path view to a path
-inline filesystem::path operator/(filesystem::path a, path_view b)
+ struct path_view_component_operator_slash_visitor
+ {
+ filesystem::path ret;
+ template <class CharT, class Traits> void operator()(basic_string_view<CharT, Traits> sv) { ret.assign(sv.begin(), sv.end()); }
+#if LLFIO_PATH_VIEW_CHAR8_TYPE_EMULATED
+ template <class Traits> void operator()(basic_string_view<path_view_component::char8_t, Traits> sv)
+ {
+ basic_string_view<char, Traits> _((const char *) sv.data(), sv.size());
+ ret.assign(_.begin(), _.end());
+ }
+#endif
+ template <class T> void operator()(span<T> sv) { throw std::logic_error("filesystem::path cannot be constructed from a byte input."); }
+ };
+} // namespace detail
+//! Append a path view component to a path view component
+LLFIO_TEMPLATE(class T)
+LLFIO_TREQUIRES(LLFIO_TPRED(std::is_same<T, path_view_component>::value || std::is_same<T, path_view>::value))
+inline filesystem::path operator/(T a, path_view_component b)
{
- return a /= b;
+ detail::path_view_component_operator_slash_visitor x;
+ visit(x, a);
+ return x.ret /= b;
}
diff --git a/programs/illegal-codepoints/main.cpp b/programs/illegal-codepoints/main.cpp
index 9babcf16..0b75f1c4 100644
--- a/programs/illegal-codepoints/main.cpp
+++ b/programs/illegal-codepoints/main.cpp
@@ -6170,7 +6170,7 @@ int main()
auto &entry = entries[n];
if(entry.second == status::created)
{
- if(item.leafname == llfio::path_view(entry.first.data(), 3, true))
+ if(item.leafname == llfio::path_view(entry.first.data(), 3, llfio::path_view::zero_terminated))
{
entry.second = status::found;
break;
diff --git a/test/tests/clone_extents.cpp b/test/tests/clone_extents.cpp
index 315b3427..2d8dfc87 100644
--- a/test/tests/clone_extents.cpp
+++ b/test/tests/clone_extents.cpp
@@ -260,7 +260,7 @@ static inline void TestCloneOrCopyTree()
to_hex_string(buffer + 1, 8, (const char *) &c, 4);
buffer[9] = 0;
}
- auto h = directory_handle::directory(dirh, path_view(buffer, 9, true), directory_handle::mode::write, directory_handle::creation::if_needed).value();
+ auto h = directory_handle::directory(dirh, path_view(buffer, 9, path_view::zero_terminated), directory_handle::mode::write, directory_handle::creation::if_needed).value();
entries_created++;
for(size_t n = 0; n < file_entries; n++)
{
@@ -268,7 +268,7 @@ static inline void TestCloneOrCopyTree()
buffer[0] = 'f';
to_hex_string(buffer + 1, 2, (const char *) &c, 1);
buffer[3] = 0;
- file_handle::file(h, path_view(buffer, 3, true), file_handle::mode::write, file_handle::creation::if_needed).value();
+ file_handle::file(h, path_view(buffer, 3, path_view::zero_terminated), file_handle::mode::write, file_handle::creation::if_needed).value();
entries_created++;
}
dirhs.emplace_back(std::move(h));
diff --git a/test/tests/path_view.cpp b/test/tests/path_view.cpp
index f8f40da2..f983b734 100644
--- a/test/tests/path_view.cpp
+++ b/test/tests/path_view.cpp
@@ -118,9 +118,9 @@ static inline void TestPathView()
BOOST_CHECK(0 == f.compare<>("0"));
#ifndef _WIN32
// cstr
- llfio::path_view::c_str<> g(e);
+ llfio::path_view::c_str<> g(e, llfio::path_view::zero_terminated);
BOOST_CHECK(g.buffer != p); // NOLINT
- llfio::path_view::c_str<> h(f);
+ llfio::path_view::c_str<> h(f, llfio::path_view::zero_terminated);
BOOST_CHECK(h.buffer == p + 70); // NOLINT
#endif
CheckPathView("/mnt/c/Users/ned/Documents/boostish/afio/programs/build_posix/testdir");
@@ -162,11 +162,11 @@ static inline void TestPathView()
BOOST_CHECK(0 == g.compare<>("\\mnt\\c\\Users\\ned\\Documents\\boostish\\afio\\programs\\build_posix\\testdir"));
BOOST_CHECK(0 == h.compare<>("0"));
// cstr
- llfio::path_view::c_str<> i(g, false);
+ llfio::path_view::c_str<> i(g, llfio::path_view::not_zero_terminated);
BOOST_CHECK(i.buffer != p2);
- llfio::path_view::c_str<> j(g, true);
+ llfio::path_view::c_str<> j(g, llfio::path_view::zero_terminated);
BOOST_CHECK(j.buffer == p2);
- llfio::path_view::c_str<> k(h, false);
+ llfio::path_view::c_str<> k(h, llfio::path_view::not_zero_terminated);
BOOST_CHECK(k.buffer == p2 + 70);
CheckPathView(L"\\mnt\\c\\Users\\ned\\Documents\\boostish\\afio\\programs\\build_posix\\testdir\\0");
diff --git a/test/tests/reduce.cpp b/test/tests/reduce.cpp
index 88aad648..c46745a1 100644
--- a/test/tests/reduce.cpp
+++ b/test/tests/reduce.cpp
@@ -78,7 +78,9 @@ static inline void TestReduce()
to_hex_string(buffer + 1, 8, (const char *) &c, 4);
buffer[9] = 0;
}
- auto h = directory_handle::directory(dirh, path_view(buffer, 9, true), directory_handle::mode::write, directory_handle::creation::if_needed).value();
+ auto h =
+ directory_handle::directory(dirh, path_view(buffer, 9, path_view::zero_terminated), directory_handle::mode::write, directory_handle::creation::if_needed)
+ .value();
entries_created++;
for(size_t n = 0; n < file_entries; n++)
{
@@ -86,7 +88,7 @@ static inline void TestReduce()
buffer[0] = 'f';
to_hex_string(buffer + 1, 2, (const char *) &c, 1);
buffer[3] = 0;
- file_handle::file(h, path_view(buffer, 3, true), file_handle::mode::write, file_handle::creation::if_needed).value();
+ file_handle::file(h, path_view(buffer, 3, path_view::zero_terminated), file_handle::mode::write, file_handle::creation::if_needed).value();
entries_created++;
}
dirhs.emplace_back(std::move(h));
diff --git a/test/tests/traverse.cpp b/test/tests/traverse.cpp
index a019ff3f..57d5a667 100644
--- a/test/tests/traverse.cpp
+++ b/test/tests/traverse.cpp
@@ -146,14 +146,14 @@ static inline void TestTraverse()
to_hex_string(buffer + 1, 8, (const char *) &c, 4);
buffer[9] = 0;
}
- auto h = directory_handle::directory(dirh, path_view(buffer, 9, true), directory_handle::mode::write, directory_handle::creation::if_needed).value();
+ auto h = directory_handle::directory(dirh, path_view(buffer, 9, path_view::zero_terminated), directory_handle::mode::write, directory_handle::creation::if_needed).value();
for(size_t n = 0; n < file_entries; n++)
{
auto c = (uint8_t) n;
buffer[0] = 'f';
to_hex_string(buffer + 1, 2, (const char *) &c, 1);
buffer[3] = 0;
- file_handle::file(h, path_view(buffer, 3, true), file_handle::mode::write, file_handle::creation::if_needed).value();
+ file_handle::file(h, path_view(buffer, 3, path_view::zero_terminated), file_handle::mode::write, file_handle::creation::if_needed).value();
entries_created++;
}
entries_created++;