summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2018-11-13 06:58:56 -0500
committerB Stack <bgstack15@gmail.com>2018-11-13 06:58:56 -0500
commit076498028ff511afd88d93e7b0bf1d1a81093b3d (patch)
tree30bf08d782d58174a0ca212b2e4b172fabd9c42c /zen
parentMerge branch '10.5' into 'master' (diff)
downloadFreeFileSync-076498028ff511afd88d93e7b0bf1d1a81093b3d.tar.gz
FreeFileSync-076498028ff511afd88d93e7b0bf1d1a81093b3d.tar.bz2
FreeFileSync-076498028ff511afd88d93e7b0bf1d1a81093b3d.zip
10.6
Diffstat (limited to 'zen')
-rwxr-xr-xzen/basic_math.h51
-rwxr-xr-xzen/dir_watcher.cpp10
-rwxr-xr-xzen/file_access.cpp142
-rwxr-xr-xzen/file_access.h20
-rwxr-xr-xzen/file_io.cpp2
-rwxr-xr-xzen/legacy_compiler.h36
-rwxr-xr-xzen/recycler.cpp2
-rwxr-xr-xzen/ring_buffer.h55
-rwxr-xr-xzen/serialize.h18
-rwxr-xr-xzen/stl_tools.h63
-rwxr-xr-xzen/string_base.h4
-rwxr-xr-xzen/string_tools.h29
-rwxr-xr-xzen/string_traits.h2
-rwxr-xr-xzen/thread.h21
-rwxr-xr-xzen/zstring.cpp22
-rwxr-xr-xzen/zstring.h64
16 files changed, 281 insertions, 260 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 0d08f6a6..75f5d3b8 100755
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -21,14 +21,8 @@ namespace numeric
template <class T> T abs(T value);
template <class T> auto dist(T a, T b);
template <class T> int sign(T value); //returns one of {-1, 0, 1}
-template <class T> T min(T a, T b, T c);
-template <class T> T max(T a, T b, T c);
template <class T> bool isNull(T value);
-template <class T> void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal
-template <class T> T clampCpy(T val, T minVal, T maxVal);
-//std::clamp() available with C++17
-
template <class T, class InputIterator> //precondition: range must be sorted!
auto nearMatch(const T& val, InputIterator first, InputIterator last);
@@ -106,51 +100,6 @@ int sign(T value) //returns one of {-1, 0, 1}
return value < 0 ? -1 : (value > 0 ? 1 : 0);
}
-
-template <class T> inline
-T min(T a, T b, T c) //don't follow std::min's "const T&(const T&, const T&)" API
-{
- if (a < b)
- return a < c ? a : c;
- else
- return b < c ? b : c;
- //return std::min(std::min(a, b), c);
-}
-
-
-template <class T> inline
-T max(T a, T b, T c)
-{
- if (a > b)
- return a > c ? a : c;
- else
- return b > c ? b : c;
- //return std::max(std::max(a, b), c);
-}
-
-
-template <class T> inline
-T clampCpy(T val, T minVal, T maxVal)
-{
- assert(minVal <= maxVal);
- if (val < minVal)
- return minVal;
- else if (val > maxVal)
- return maxVal;
- return val;
-}
-
-template <class T> inline
-void clamp(T& val, T minVal, T maxVal)
-{
- assert(minVal <= maxVal);
- if (val < minVal)
- val = minVal;
- else if (val > maxVal)
- val = maxVal;
-}
-
-
/*
part of C++11 now!
template <class InputIterator, class Compare> inline
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 2bb3fd26..f5ed0488 100755
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -25,7 +25,7 @@ using namespace zen;
struct DirWatcher::Impl
{
int notifDescr = 0;
- std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr"
+ std::map<int, Zstring> watchedPaths; //watch descriptor and (sub-)directory paths -> owned by "notifDescr"
};
@@ -90,7 +90,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec));
}
- pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath));
+ pimpl_->watchedPaths.emplace(wd, subDirPath);
}
}
@@ -130,12 +130,12 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch
{
- auto it = pimpl_->watchDescrs.find(evt.wd);
- if (it != pimpl_->watchDescrs.end())
+ auto it = pimpl_->watchedPaths.find(evt.wd);
+ if (it != pimpl_->watchedPaths.end())
{
//Note: evt.len is NOT the size of the evt.name c-string, but the array size including all padding 0 characters!
//It may be even 0 in which case evt.name must not be used!
- const Zstring itemPath = it->second + evt.name;
+ const Zstring itemPath = appendSeparator(it->second) + evt.name;
if ((evt.mask & IN_CREATE) ||
(evt.mask & IN_MOVED_TO))
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 88b70b14..82c78760 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -50,32 +50,32 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
return {};
};
- if (startsWith(itemPath, "/"))
- {
- if (startsWith(itemPath, "/media/"))
- {
- //Ubuntu: e.g. /media/zenju/DEVICE_NAME
- if (const char* username = ::getenv("USER"))
- if (startsWith(itemPath, std::string("/media/") + username + "/"))
- return doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ std::optional<PathComponents> pc; //"/media/zenju/" and "/Volumes/" should not fail to parse
- //Ubuntu: e.g. /media/cdrom0
- return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- }
+ if (!pc && startsWith(itemPath, "/mnt/")) //e.g. /mnt/DEVICE_NAME
+ pc = doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME
- if (const char* username = ::getenv("USER"))
- if (startsWith(itemPath, std::string("/run/media/") + username + "/"))
- return doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ if (!pc && startsWith(itemPath, "/media/")) //Ubuntu: e.g. /media/zenju/DEVICE_NAME
+ if (const char* username = ::getenv("USER"))
+ if (startsWith(itemPath, std::string("/media/") + username + "/"))
+ pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- return doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/);
- }
+ if (!pc && startsWith(itemPath, "/run/media/")) //Centos, Suse: e.g. /run/media/zenju/DEVICE_NAME
+ if (const char* username = ::getenv("USER"))
+ if (startsWith(itemPath, std::string("/run/media/") + username + "/"))
+ pc = doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- //we do NOT support relative paths!
- return {};
-}
+ if (!pc && startsWith(itemPath, "/run/user/")) //Ubuntu, e.g.: /run/user/1000/gvfs/smb-share:server=192.168.62.145,share=folder
+ if (startsWith(itemPath, "/run/user/" + numberTo<std::string>(::getuid()) + "/gvfs/")) //::getuid() never fails
+ pc = doParse(6 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ if (!pc && startsWith(itemPath, "/"))
+ pc = doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/);
+
+ return pc;
+}
+
std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
{
@@ -108,49 +108,40 @@ ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
}
-PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError
+std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw FileError
{
- const std::optional<Zstring> parentPath = getParentFolderPath(itemPath);
try
{
- return { getItemType(itemPath), itemPath, {} }; //throw FileError
+ return getItemType(itemPath); //throw FileError
}
- catch (FileError&)
+ catch (const FileError& e) //not existing or access error
{
+ const std::optional<Zstring> parentPath = getParentFolderPath(itemPath);
if (!parentPath) //device root
throw;
//else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes:
// ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE,
// ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable
- }
- const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
- assert(!itemName.empty());
-
- PathStatus ps = getPathStatus(*parentPath); //throw FileError
- if (ps.relPath.empty() &&
- ps.existingType != ItemType::FILE) //obscure, but possible (and not an error)
- try
- {
- traverseFolder(*parentPath,
- [&](const FileInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FILE; },
- [&](const FolderInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FOLDER; },
- [&](const SymlinkInfo& si) { if (equalFilePath(si.itemName, itemName)) throw ItemType::SYMLINK; },
- [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
- }
- catch (const ItemType& type) { return { type, itemPath, {} }; } //yes, exceptions for control-flow are bad design... but, but...
- //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found)
-
- ps.relPath.push_back(itemName);
- return ps;
-}
+ const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ assert(!itemName.empty());
-std::optional<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
-{
- const PathStatus ps = getPathStatus(itemPath); //throw FileError
- if (ps.relPath.empty())
- return ps.existingType;
- return {};
+ const std::optional<ItemType> parentType = itemStillExists(*parentPath); //throw FileError
+ if (parentType && *parentType != ItemType::FILE /*obscure, but possible (and not an error)*/)
+ try
+ {
+ traverseFolder(*parentPath,
+ [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; },
+ [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; },
+ [&](const SymlinkInfo& si) { if (si.itemName == itemName) throw ItemType::SYMLINK; },
+ [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
+ }
+ catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional
+ {
+ throw e; //yes, slicing
+ }
+ return {};
+ }
}
@@ -174,16 +165,6 @@ bool zen::dirAvailable(const Zstring& dirPath) //noexcept
}
-bool zen::itemNotExisting(const Zstring& itemPath)
-{
- try
- {
- return !getItemTypeIfExists(itemPath); //throw FileError
- }
- catch (FileError&) { return false; }
-}
-
-
namespace
{
}
@@ -209,13 +190,13 @@ uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0
}
-VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
+FileId /*optional*/ zen::getFileId(const Zstring& itemPath) //throw FileError
{
struct ::stat fileInfo = {};
if (::stat(itemPath.c_str(), &fileInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat");
- return fileInfo.st_dev;
+ return extractFileId(fileInfo);
}
@@ -381,7 +362,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr
const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
const Zstring parentPathTrg = beforeLast(pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
//some (broken) devices may fail to rename case directly:
- if (equalLocalPath(parentPathSrc, parentPathTrg))
+ if (equalNativePath(parentPathSrc, parentPathTrg))
{
if (fileNameSrc == fileNameTrg)
return; //non-sensical request
@@ -567,8 +548,18 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget
void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError
{
- if (!getParentFolderPath(dirPath)) //device root
- return static_cast<void>(/*ItemType =*/ getItemType(dirPath)); //throw FileError
+ const std::optional<Zstring> parentPath = getParentFolderPath(dirPath);
+ if (!parentPath) //device root
+ return;
+
+ try //generally we expect that path already exists (see: ffs_paths.cpp) => check first
+ {
+ if (getItemType(dirPath) != ItemType::FILE) //throw FileError
+ return;
+ }
+ catch (FileError&) {} //not yet existing or access error? let's find out...
+
+ createDirectoryIfMissingRecursion(*parentPath); //throw FileError
try
{
@@ -576,18 +567,15 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File
}
catch (FileError&)
{
- const PathStatus ps = getPathStatus(dirPath); //throw FileError
- if (ps.existingType == ItemType::FILE)
- throw;
+ try
+ {
+ if (getItemType(dirPath) != ItemType::FILE) //throw FileError
+ return; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel
+ }
+ catch (FileError&) {} //not yet existing or access error
+ //catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } -> details needed???
- //ps.relPath.size() == 1 => same createDirectory() call from above? Maybe parent folder was created by parallel thread shortly after failure!
- Zstring intermediatePath = ps.existingPath;
- for (const Zstring& itemName : ps.relPath)
- try
- {
- createDirectory(intermediatePath = appendSeparator(intermediatePath) + itemName); //throw FileError, ErrorTargetExisting
- }
- catch (ErrorTargetExisting&) {} //possible, if createDirectoryIfMissingRecursion() is run in parallel
+ throw;
}
}
diff --git a/zen/file_access.h b/zen/file_access.h
index 916f23f5..514d798e 100755
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -30,8 +30,6 @@ std::optional<Zstring> getParentFolderPath(const Zstring& itemPath);
//POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar
bool fileAvailable(const Zstring& filePath); //noexcept
bool dirAvailable (const Zstring& dirPath ); //
-//NEGATIVE existence checks; if false: 1. item existing 2.device access error or similar
-bool itemNotExisting(const Zstring& itemPath);
enum class ItemType
{
@@ -40,17 +38,12 @@ enum class ItemType
SYMLINK,
};
//(hopefully) fast: does not distinguish between error/not existing
-ItemType getItemType (const Zstring& itemPath); //throw FileError
+ItemType getItemType(const Zstring& itemPath); //throw FileError
//execute potentially SLOW folder traversal but distinguish error/not existing
-std::optional<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError
-
-struct PathStatus
-{
- ItemType existingType;
- Zstring existingPath; //itemPath =: existingPath + relPath
- std::vector<Zstring> relPath; //
-};
-PathStatus getPathStatus(const Zstring& itemPath); //throw FileError
+// assumes: - base path still exists
+// - all child item path parts must correspond to folder traversal
+// => we can conclude whether an item is *not* existing anymore by doing a *case-sensitive* name search => potentially SLOW!
+std::optional<ItemType> itemStillExists(const Zstring& itemPath); //throw FileError
enum class ProcSymlink
{
@@ -62,7 +55,8 @@ void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); /
//symlink handling: always evaluate target
uint64_t getFileSize(const Zstring& filePath); //throw FileError
uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available
-VolumeId getVolumeId(const Zstring& itemPath); //throw FileError
+FileId /*optional*/ getFileId(const Zstring& itemPath); //throw FileError
+
//get per-user directory designated for temporary files:
Zstring getTempFolderPath(); //throw FileError
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index df47e4c5..80fb3153 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -128,7 +128,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E
return bytesRead; //"zero indicates end of file"
}
-
+
size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
{
/*
diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h
index 16d87c53..bebf5a05 100755
--- a/zen/legacy_compiler.h
+++ b/zen/legacy_compiler.h
@@ -7,11 +7,47 @@
#ifndef LEGACY_COMPILER_H_839567308565656789
#define LEGACY_COMPILER_H_839567308565656789
+//#include <span> //requires C++20
+
+
namespace std
{
//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html
//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
+
+
+//requires C++20! until then, this should suffice...
+template <class T>
+class span
+{
+public:
+ template <class Iterator>
+ span(Iterator first, Iterator last) : size_(last - first), data_(first != last ? &*first : nullptr) {}
+
+ template <class Container>
+ span(Container& cont) : span(cont.begin(), cont.end()) {}
+
+ using iterator = T*;
+ using const_iterator = const T*;
+
+ iterator begin() { return data_; }
+ iterator end () { return data_ + size_; }
+
+ const_iterator begin() const { return data_; }
+ const_iterator end () const { return data_ + size_; }
+
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend () const { return end (); }
+
+ T* data() const { return data_; }
+ size_t size() const { return size_; }
+ bool empty() const { return size_ == 0; }
+
+private:
+ const size_t size_;
+ T* const data_;
+};
}
#endif //LEGACY_COMPILER_H_839567308565656789
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 8b4389a7..8d34f262 100755
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -27,7 +27,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError
if (!::g_file_trash(file, nullptr, &error))
{
- const std::optional<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError
+ const std::optional<ItemType> type = itemStillExists(itemPath); //throw FileError
if (!type)
return false;
diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h
index 232e17da..e3dbd55f 100755
--- a/zen/ring_buffer.h
+++ b/zen/ring_buffer.h
@@ -8,10 +8,7 @@
#define RING_BUFFER_H_01238467085684139453534
#include <cassert>
-#include <vector>
-#include <stdexcept>
#include "scope_guard.h"
-#include "string_tools.h"
namespace zen
@@ -40,13 +37,24 @@ public:
~RingBuffer() { clear(); }
- reference front() { return getBufPtr()[bufStart_]; }
- const_reference front() const { return getBufPtr()[bufStart_]; }
+ reference front() { checkInvariants(); assert(!empty()); return getBufPtr()[bufStart_]; }
+ const_reference front() const { checkInvariants(); assert(!empty()); return getBufPtr()[bufStart_]; }
+
+ reference back() { checkInvariants(); assert(!empty()); return getBufPtr()[getBufPos(size_ - 1)]; }
+ const_reference back() const { checkInvariants(); assert(!empty()); return getBufPtr()[getBufPos(size_ - 1)]; }
+
+ template <class U>
+ void push_front(U&& value)
+ {
+ reserve(size_ + 1); //throw ?
+ ::new (getBufPtr() + getBufPos(capacity_ - 1)) T(std::forward<U>(value)); //throw ?
+ ++size_;
+ bufStart_ = getBufPos(capacity_ - 1);
+ }
template <class U>
void push_back(U&& value)
{
- checkInvariants();
reserve(size_ + 1); //throw ?
::new (getBufPtr() + getBufPos(size_)) T(std::forward<U>(value)); //throw ?
++size_;
@@ -54,15 +62,21 @@ public:
void pop_front()
{
- checkInvariants();
- if (empty())
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
front().~T();
- ++bufStart_;
--size_;
- if (size_ == 0 || bufStart_ == capacity_)
+ if (size_ == 0)
+ bufStart_ = 0;
+ else
+ bufStart_ = getBufPos(1);
+ }
+
+ void pop_back()
+ {
+ back().~T();
+ --size_;
+
+ if (size_ == 0)
bufStart_ = 0;
}
@@ -80,8 +94,6 @@ public:
template <class Iterator>
void insert_back(Iterator first, Iterator last) //throw ? (strong exception-safety!)
{
- checkInvariants();
-
const size_t len = last - first;
reserve(size_ + len); //throw ?
@@ -100,10 +112,8 @@ public:
void extract_front(Iterator first, Iterator last) //throw ? strongly exception-safe! (but only basic exception safety for [first, last) range)
{
checkInvariants();
-
const size_t len = last - first;
- if (size_ < len)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ assert(size_ >= len);
const size_t frontSize = std::min(len, capacity_ - bufStart_);
@@ -113,13 +123,12 @@ public:
std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize);
std::destroy(getBufPtr(), getBufPtr() + len - frontSize);
- bufStart_ += len;
- size_ -= len;
+ size_ -= len;
if (size_ == 0)
bufStart_ = 0;
- else if (bufStart_ >= capacity_)
- bufStart_ -= capacity_;
+ else
+ bufStart_ = getBufPos(len);
}
void swap(RingBuffer& other)
@@ -132,6 +141,8 @@ public:
void reserve(size_t minSize) //throw ? (strong exception-safety!)
{
+ checkInvariants();
+
if (minSize > capacity_)
{
const size_t newCapacity = std::max(minSize + minSize / 2, minSize); //no minimum capacity: just like std::vector<> implementation
@@ -205,7 +216,7 @@ private:
struct FreeStoreDelete { void operator()(std::byte* p) const { ::operator delete (p); } };
- T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); }
+ /**/ T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); }
const T* getBufPtr() const { return reinterpret_cast<T*>(rawMem_.get()); }
//unlike pure std::uninitialized_move, this one allows for strong exception-safety!
diff --git a/zen/serialize.h b/zen/serialize.h
index d34b61b2..8b4c58ea 100755
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -36,20 +36,20 @@ public:
using iterator = std::vector<std::byte>::iterator;
using const_iterator = std::vector<std::byte>::const_iterator;
- iterator begin() { return buffer_->begin(); }
- iterator end () { return buffer_->end (); }
+ iterator begin() { return buffer_.ref().begin(); }
+ iterator end () { return buffer_.ref().end (); }
- const_iterator begin() const { return buffer_->begin(); }
- const_iterator end () const { return buffer_->end (); }
+ const_iterator begin() const { return buffer_.ref().begin(); }
+ const_iterator end () const { return buffer_.ref().end (); }
- void resize(size_t len) { buffer_->resize(len); }
- size_t size() const { return buffer_->size(); }
- bool empty() const { return buffer_->empty(); }
+ void resize(size_t len) { buffer_.ref().resize(len); }
+ size_t size() const { return buffer_.ref().size(); }
+ bool empty() const { return buffer_.ref().empty(); }
- inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; }
+ inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return lhs.buffer_.ref() == rhs.buffer_.ref(); }
private:
- std::shared_ptr<std::vector<std::byte>> buffer_ = std::make_shared<std::vector<std::byte>>(); //always bound!
+ SharedRef<std::vector<std::byte>> buffer_ = makeSharedRef<std::vector<std::byte>>();
//perf: shared_ptr indirection irrelevant: less than 1% slower!
};
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index c3a9bf8f..d8bda888 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -11,10 +11,11 @@
#include <map>
#include <vector>
#include <memory>
+#include <cassert>
#include <algorithm>
#include <optional>
#include "string_traits.h"
-#include "build_info.h"
+//#include "build_info.h"
//enhancements for <algorithm>
@@ -22,13 +23,13 @@ namespace zen
{
//erase selected elements from any container:
template <class T, class Alloc, class Predicate>
-void erase_if(std::vector<T, Alloc>& v, Predicate p);
+void eraseIf(std::vector<T, Alloc>& v, Predicate p);
template <class T, class LessType, class Alloc, class Predicate>
-void erase_if(std::set<T, LessType, Alloc>& s, Predicate p);
+void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p);
template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate>
-void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p);
+void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p);
//append STL containers
template <class T, class Alloc, class C>
@@ -48,14 +49,14 @@ void removeDuplicates(std::vector<T, Alloc>& v, CompLess less);
//binary search returning an iterator
template <class Iterator, class T, class CompLess>
-Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less);
+Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less);
template <class BidirectionalIterator, class T>
-BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
+BidirectionalIterator findLast(BidirectionalIterator first, BidirectionalIterator last, const T& value);
//replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name)
template <class BidirectionalIterator1, class BidirectionalIterator2>
-BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
+BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
BidirectionalIterator2 first2, BidirectionalIterator2 last2);
template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last);
@@ -74,17 +75,49 @@ struct StringHash
};
-//why, oh wy is there no std::optional<T>::get()???
+//why, oh why is there no std::optional<T>::get()???
template <class T> inline T* get( std::optional<T>& opt) { return opt ? &*opt : nullptr; }
template <class T> inline const T* get(const std::optional<T>& opt) { return opt ? &*opt : nullptr; }
+//===========================================================================
+template <class T> class SharedRef;
+template <class T, class... Args> SharedRef<T> makeSharedRef(Args&& ... args);
+
+template <class T>
+class SharedRef //why is there no std::shared_ref???
+{
+public:
+ SharedRef() = delete; //no suprise memory allocations => always construct with makeSharedRef()
+
+ template <class U>
+ SharedRef(const SharedRef<U>& other) : ref_(other.ref_) {}
+
+ /**/ T& ref() { return *ref_; };
+ const T& ref() const { return *ref_; };
+
+ std::shared_ptr<T> ptr() { return ref_; };
+
+private:
+ explicit SharedRef(std::shared_ptr<T>&& ptr) : ref_(std::move(ptr)) { assert(ref_); }
+
+ template <class U, class... Args> friend SharedRef<U> makeSharedRef(Args&& ... args);
+ template <class U> friend class SharedRef;
+
+ std::shared_ptr<T> ref_; //always bound
+};
+
+template <class T, class... Args> inline
+SharedRef<T> makeSharedRef(Args&&... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); }
+//===========================================================================
+
+
//######################## implementation ########################
template <class T, class Alloc, class Predicate> inline
-void erase_if(std::vector<T, Alloc>& v, Predicate p)
+void eraseIf(std::vector<T, Alloc>& v, Predicate p)
{
v.erase(std::remove_if(v.begin(), v.end(), p), v.end());
}
@@ -93,7 +126,7 @@ void erase_if(std::vector<T, Alloc>& v, Predicate p)
namespace impl
{
template <class S, class Predicate> inline
-void set_or_map_erase_if(S& s, Predicate p)
+void setOrMapEraseIf(S& s, Predicate p)
{
for (auto it = s.begin(); it != s.end();)
if (p(*it))
@@ -105,11 +138,11 @@ void set_or_map_erase_if(S& s, Predicate p)
template <class T, class LessType, class Alloc, class Predicate> inline
-void erase_if(std::set<T, LessType, Alloc>& s, Predicate p) { impl::set_or_map_erase_if(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!!
+void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p) { impl::setOrMapEraseIf(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!!
template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> inline
-void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::set_or_map_erase_if(m, p); }
+void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::setOrMapEraseIf(m, p); }
template <class T, class Alloc, class C> inline
@@ -147,7 +180,7 @@ void removeDuplicates(std::vector<T, Alloc>& v)
template <class Iterator, class T, class CompLess> inline
-Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less)
+Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less)
{
static_assert(std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>);
@@ -160,7 +193,7 @@ Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess l
template <class BidirectionalIterator, class T> inline
-BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
+BidirectionalIterator findLast(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
{
for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate
{
@@ -174,7 +207,7 @@ BidirectionalIterator find_last(const BidirectionalIterator first, const Bidirec
template <class BidirectionalIterator1, class BidirectionalIterator2> inline
-BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
+BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
const BidirectionalIterator2 first2, const BidirectionalIterator2 last2)
{
const BidirectionalIterator1 itNotFound = last1;
diff --git a/zen/string_base.h b/zen/string_base.h
index 9632eba4..91a6d5bd 100755
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -410,7 +410,7 @@ size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const
assert(pos == npos || pos <= length());
const size_t len = length();
const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + 1, len));
- const Char* it = find_last(begin(), currEnd, ch);
+ const Char* it = findLast(begin(), currEnd, ch);
return it == currEnd ? npos : it - begin();
}
@@ -422,7 +422,7 @@ size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const
const size_t strLen = strLength(str);
const size_t len = length();
const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len));
- const Char* it = search_last(begin(), currEnd,
+ const Char* it = searchLast(begin(), currEnd,
str, str + strLen);
return it == currEnd ? npos : it - begin();
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 657c70d5..8579a460 100755
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -104,26 +104,19 @@ template <class T, class S> T copyStringTo(S&& str);
//---------------------- implementation ----------------------
-template <> inline
-bool isWhiteSpace(char c)
-{
- assert(c != 0); //std C++ does not consider 0 as white space
- //caveat 1: std::isspace() takes an int, but expects an unsigned char
- //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
- return static_cast<unsigned char>(c) < 128 &&
- std::isspace(static_cast<unsigned char>(c)) != 0;
-}
-
-template <> inline
-bool isWhiteSpace(wchar_t c)
+template <class Char> inline
+bool isWhiteSpace(Char c)
{
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
assert(c != 0); //std C++ does not consider 0 as white space
- return std::iswspace(c) != 0;
+ return c == static_cast<Char>(' ') || (static_cast<Char>('\t') <= c && c <= static_cast<Char>('\r'));
+ //following std::isspace() for default locale but without the interface insanity:
+ // - std::isspace() takes an int, but expects an unsigned char
+ // - some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
}
-
template <class Char> inline
-bool isDigit(Char c) //similar to implmenetation of std::isdigit()!
+bool isDigit(Char c) //similar to implementation of std::isdigit()!
{
static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
return static_cast<Char>('0') <= c && c <= static_cast<Char>('9');
@@ -306,7 +299,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv)
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* it = search_last(strFirst, strLast,
+ const auto* it = searchLast(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
return rv == IF_MISSING_RETURN_ALL ? str : S();
@@ -327,7 +320,7 @@ S beforeLast(const S& str, const T& term, FailureReturnVal rv)
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* it = search_last(strFirst, strLast,
+ const auto* it = searchLast(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
return rv == IF_MISSING_RETURN_ALL ? str : S();
@@ -725,7 +718,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i
number += c - static_cast<CharType>('0');
}
else //rest of string should contain whitespace only, it's NOT a bug if there is something else!
- break; //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation
+ break; //assert(std::all_of(iter, last, isWhiteSpace<CharType>)); -> this is NO assert situation
}
return number;
}
diff --git a/zen/string_traits.h b/zen/string_traits.h
index cd7dbf1b..93cfd81c 100755
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -53,7 +53,7 @@ public:
private:
const size_t len_;
- Char* str_;
+ Char* const str_;
};
diff --git a/zen/thread.h b/zen/thread.h
index 7f3d216c..809bc771 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -90,10 +90,10 @@ bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0
//wait until first job is successful or all failed: substitute until std::when_any is available
template <class T>
-class GetFirstResult
+class AsyncFirstResult
{
public:
- GetFirstResult();
+ AsyncFirstResult();
template <class Fun>
void addJob(Fun&& f); //f must return a std::optional<T> containing a value if successful
@@ -161,12 +161,15 @@ public:
ThreadGroup& operator=(ThreadGroup&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!!
//context of controlling OR worker thread, non-blocking:
- void run(Function&& wi /*should throw ThreadInterruption when needed*/)
+ void run(Function&& wi /*should throw ThreadInterruption when needed*/, bool insertFront = false)
{
{
std::lock_guard<std::mutex> dummy(workLoad_->lock);
- workLoad_->tasks.push_back(std::move(wi));
+ if (insertFront)
+ workLoad_->tasks.push_front(std::move(wi));
+ else
+ workLoad_->tasks.push_back(std::move(wi));
const size_t tasksPending = ++(workLoad_->tasksPending);
if (worker_.size() < std::min(tasksPending, threadCountMax_))
@@ -318,7 +321,7 @@ bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration&
template <class T>
-class GetFirstResult<T>::AsyncResult
+class AsyncFirstResult<T>::AsyncResult
{
public:
//context: worker threads
@@ -361,12 +364,12 @@ private:
template <class T> inline
-GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
+AsyncFirstResult<T>::AsyncFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
template <class T>
template <class Fun> inline
-void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success
+void AsyncFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success
{
std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); });
++jobsTotal_;
@@ -376,11 +379,11 @@ void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> conta
template <class T>
template <class Duration> inline
-bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
+bool AsyncFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
template <class T> inline
-std::optional<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
+std::optional<T> AsyncFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
//------------------------------------------------------------------------------------------
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 68609030..f8a34045 100755
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -77,22 +77,19 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const
{
if (oldTerm.empty())
return str;
-
- Zstring strU = str;
- Zstring oldU = oldTerm;
-
- for (Zchar& c : strU) c = asciiToUpper(c); //can't use makeUpperCopy(): input/output sizes may differ!
- for (Zchar& c : oldU) c = asciiToUpper(c); //
Zstring output;
for (size_t pos = 0;;)
{
- const size_t posFound = strU.find(oldU, pos);
- if (posFound == Zstring::npos)
+ const size_t posFound = std::search(str.begin() + pos, str.end(), //can't use makeUpperCopy(): input/output sizes may differ!
+ oldTerm.begin(), oldTerm.end(),
+ [](Zchar charL, Zchar charR) { return asciiToUpper(charL) == asciiToUpper(charR); }) - str.begin();
+
+ if (posFound == str.size())
{
if (pos == 0) //optimize "oldTerm not found": return ref-counted copy
- return str;
+ return str;
output.append(str.begin() + pos, str.end());
return output;
}
@@ -126,7 +123,7 @@ OS X (UTF8 char)
________________________
time per call | function
*/
-int compareLocalPath(const Zstring& lhs, const Zstring& rhs)
+int compareNativePath(const Zstring& lhs, const Zstring& rhs)
{
assert(lhs.find(Zchar('\0')) == Zstring::npos); //don't expect embedded nulls!
assert(rhs.find(Zchar('\0')) == Zstring::npos); //
@@ -250,8 +247,3 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs)
}
}
-
-
-warn_static("clean up implementation of these two:")
-//template <> inline bool isWhiteSpace(char c)
-//template <> inline bool isWhiteSpace(wchar_t c)
diff --git a/zen/zstring.h b/zen/zstring.h
index 20cf968d..9fecdce3 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -35,24 +35,18 @@ Zstring getUnicodeNormalForm(const Zstring& str);
Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const Zstring& newTerm);
+struct LessUnicodeNormal { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return getUnicodeNormalForm(lhs) < getUnicodeNormalForm(rhs);} };
+
//------------------------------------------------------------------------------------------
-//inline
-//int compareNoCase(const Zstring& lhs, const Zstring& rhs)
-//{
-// return zen::compareString(makeUpperCopy(lhs), makeUpperCopy(rhs));
-// //avoid eager optimization bugs: e.g. "if (isAsciiString()) compareAsciiNoCase()" might model a different order!
-//}
inline bool equalNoCase(const Zstring& lhs, const Zstring& rhs) { return makeUpperCopy(lhs) == makeUpperCopy(rhs); }
struct ZstringNoCase //use as STL container key: avoid needless upper-case conversions during std::map<>::find()
{
- ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {}
- Zstring upperCase;
+ ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {}
+ Zstring upperCase;
};
-inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { return lhs.upperCase < rhs.upperCase; }
-
-//struct LessNoCase { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNoCase(lhs, rhs) < 0; } };
+inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { return lhs.upperCase < rhs.upperCase; }
//------------------------------------------------------------------------------------------
@@ -60,11 +54,11 @@ inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { retu
// Windows: igore case
// Linux: byte-wise comparison
// macOS: igore case + Unicode normalization forms
-int compareLocalPath(const Zstring& lhs, const Zstring& rhs);
+int compareNativePath(const Zstring& lhs, const Zstring& rhs);
-inline bool equalLocalPath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs) == 0; }
+inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; }
-struct LessLocalPath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareLocalPath(lhs, rhs) < 0; } };
+struct LessNativePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNativePath(lhs, rhs) < 0; } };
//------------------------------------------------------------------------------------------
int compareNatural(const Zstring& lhs, const Zstring& rhs);
@@ -73,11 +67,7 @@ struct LessNaturalSort { bool operator()(const Zstring& lhs, const Zstring rhs)
//------------------------------------------------------------------------------------------
warn_static("get rid:")
-inline int compareFilePath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs); }
-
-inline bool equalFilePath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs) == 0; }
-
-struct LessFilePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareLocalPath(lhs, rhs) < 0; } };
+inline bool equalFilePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; }
//------------------------------------------------------------------------------------------
@@ -92,18 +82,50 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
inline
+Zstring appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep)
+{
+ using namespace zen;
+
+ assert(!startsWith(relPath, pathSep) && !endsWith(relPath, pathSep));
+ if (relPath.empty())
+ return basePath;
+ if (basePath.empty())
+ return relPath;
+
+ if (startsWith(relPath, pathSep))
+ {
+ if (relPath.size() == 1)
+ return basePath;
+
+ if (endsWith(basePath, pathSep))
+ return basePath + (relPath.c_str() + 1);
+ }
+ else if (!endsWith(basePath, pathSep))
+ {
+ Zstring output = basePath;
+ output.reserve(basePath.size() + 1 + relPath.size()); //append all three strings using a single memory allocation
+ return std::move(output) + pathSep + relPath; //
+ }
+
+ return basePath + relPath;
+}
+
+inline Zstring nativeAppendPaths(const Zstring& basePath, const Zstring& relPath) { return appendPaths(basePath, relPath, FILE_NAME_SEPARATOR); }
+
+
+inline
Zstring getFileExtension(const Zstring& filePath)
{
//const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
//return afterLast(fileName, Zstr('.'), zen::IF_MISSING_RETURN_NONE);
- auto it = zen::find_last(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
+ auto it = zen::findLast(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
if (it == filePath.end())
it = filePath.begin();
else
++it;
- auto it2 = zen::find_last(it, filePath.end(), Zstr('.'));
+ auto it2 = zen::findLast(it, filePath.end(), Zstr('.'));
if (it2 != filePath.end())
++it2;
bgstack15