diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2020-10-08 14:04:31 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2020-10-08 14:04:31 +0300 |
commit | 04826c2e67404ec63782a0025b929c7d717ea1fd (patch) | |
tree | b52837073da8c390ead9ef0e8399da17f245bc47 /include/llfio/v2.0 | |
parent | e8f25ff3646124c4090a3413bfb43751b680a3ff (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.
Diffstat (limited to 'include/llfio/v2.0')
21 files changed, 659 insertions, 449 deletions
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; } |