summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2016-10-29 11:41:53 +0200
committerDaniel Wilhelm <shieldwed@outlook.com>2016-10-29 11:41:53 +0200
commit7302bb4484d517a72cdffbd13ec7a9f2324cde01 (patch)
tree17d2964c6768d49510206836a496fb1802a63e08 /zen
parent8.5 (diff)
downloadFreeFileSync-7302bb4484d517a72cdffbd13ec7a9f2324cde01.tar.gz
FreeFileSync-7302bb4484d517a72cdffbd13ec7a9f2324cde01.tar.bz2
FreeFileSync-7302bb4484d517a72cdffbd13ec7a9f2324cde01.zip
8.6
Diffstat (limited to 'zen')
-rw-r--r--zen/basic_math.h93
-rw-r--r--zen/crc.h2
-rw-r--r--zen/dir_watcher.cpp16
-rw-r--r--zen/dir_watcher.h4
-rw-r--r--zen/file_access.cpp326
-rw-r--r--zen/file_io.cpp58
-rw-r--r--zen/file_io.h8
-rw-r--r--zen/fixed_list.h185
-rw-r--r--zen/format_unit.cpp4
-rw-r--r--zen/globals.h28
-rw-r--r--zen/i18n.h2
-rw-r--r--zen/process_priority.cpp8
-rw-r--r--zen/process_priority.h8
-rw-r--r--zen/scope_guard.h14
-rw-r--r--zen/shell_execute.h2
-rw-r--r--zen/stl_tools.h25
-rw-r--r--zen/string_base.h378
-rw-r--r--zen/string_tools.h52
-rw-r--r--zen/sys_error.h48
-rw-r--r--zen/thread.h27
-rw-r--r--zen/time.h12
-rw-r--r--zen/utf.h16
-rw-r--r--zen/warn_static.h33
-rw-r--r--zen/zstring.h40
24 files changed, 744 insertions, 645 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 4dc9b43b..e9e17466 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -17,39 +17,28 @@
namespace numeric
{
-template <class T>
-T abs(T value);
-
-template <class T>
-T dist(T a, T b);
-
-template <class T>
-int sign(T value); //returns -1/0/1
-
-template <class T>
-T min(T a, T b, T c);
+template <class T> T abs(T value);
+template <class T> T 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>
-T max(T a, T b, T c);
-
-template <class T>
-void clamp(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal
+void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal
template <class T>
-T clampCpy(const T& val, const T& minVal, const T& maxVal);
+T clampCpy(T val, T minVal, T maxVal);
template <class T, class InputIterator> //precondition: range must be sorted!
auto nearMatch(const T& val, InputIterator first, InputIterator last);
-template <class T>
-bool isNull(T value);
-
int round(double d); //"little rounding function"
-template <class N>
-N integerDivideRoundUp(N numerator, N denominator);
+template <class N, class D>
+auto integerDivideRoundUp(N numerator, D denominator);
template <size_t N, class T>
-T power(const T& value);
+T power(T value);
double radToDeg(double rad); //convert unit [rad] into [°]
double degToRad(double degree); //convert unit [°] into [rad]
@@ -108,8 +97,9 @@ T dist(T a, T b)
template <class T> inline
-int sign(T value) //returns -1/0/1
+int sign(T value) //returns one of {-1, 0, 1}
{
+ static_assert(std::is_signed<T>::value, "");
return value < 0 ? -1 : (value > 0 ? 1 : 0);
}
@@ -117,19 +107,27 @@ int sign(T value) //returns -1/0/1
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
{
- return std::min(std::min(a, b), c);
+ 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)
{
- return std::max(std::max(a, b), 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(const T& val, const T& minVal, const T& maxVal)
+T clampCpy(T val, T minVal, T maxVal)
{
assert(minVal <= maxVal);
if (val < minVal)
@@ -140,7 +138,7 @@ T clampCpy(const T& val, const T& minVal, const T& maxVal)
}
template <class T> inline
-void clamp(T& val, const T& minVal, const T& maxVal)
+void clamp(T& val, T minVal, T maxVal)
{
assert(minVal <= maxVal);
if (val < minVal)
@@ -229,10 +227,11 @@ int round(double d)
}
-template <class N> inline
-N integerDivideRoundUp(N numerator, N denominator)
+template <class N, class D> inline
+auto integerDivideRoundUp(N numerator, D denominator)
{
- static_assert(std::is_unsigned<N>::value, "");
+ static_assert(std::is_integral<N>::value && std::is_unsigned<N>::value, "");
+ static_assert(std::is_integral<D>::value && std::is_unsigned<D>::value, "");
assert(denominator > 0);
return (numerator + denominator - 1) / denominator;
}
@@ -240,33 +239,17 @@ N integerDivideRoundUp(N numerator, N denominator)
namespace
{
-template <size_t N, class T>
-struct PowerImpl
-{
- static T result(const T& value)
- {
- return PowerImpl<N - 1, T>::result(value) * value;
- }
-};
-
-template <class T>
-struct PowerImpl<2, T>
-{
- static T result(const T& value)
- {
- return value * value;
- }
-};
-
-template <class T>
-struct PowerImpl<0, T>; //not defined: invalidates power<0> and power<1>
-
-template <class T>
-struct PowerImpl<10, T>; //not defined: invalidates power<N> for N >= 10
+template <size_t N, class T> struct PowerImpl;
+/*
+ template <size_t N, class T> -> let's use non-recursive specializations to help the compiler
+ struct PowerImpl { static T result(const T& value) { return PowerImpl<N - 1, T>::result(value) * value; } };
+*/
+template <class T> struct PowerImpl<2, T> { static T result(T value) { return value * value; } };
+template <class T> struct PowerImpl<3, T> { static T result(T value) { return value * value * value; } };
}
template <size_t n, class T> inline
-T power(const T& value)
+T power(T value)
{
return PowerImpl<n, T>::result(value);
}
@@ -327,7 +310,7 @@ double mad(RandomAccessIterator first, RandomAccessIterator last) //note: invali
{
const double m = median(first, last);
- //the second median needs to operate on absolute residuals => avoid transforming input range as it may decrease precision!
+ //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision!
auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); };
diff --git a/zen/crc.h b/zen/crc.h
index a11158d7..b617bdd0 100644
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -38,7 +38,7 @@ uint32_t getCrc32(ByteIterator first, ByteIterator last)
static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
boost::crc_32_type result;
if (first != last)
- result.process_bytes(&*first, last - first);
+ result.process_bytes(&*first, last - first);
auto rv = result.checksum();
static_assert(sizeof(rv) == sizeof(uint32_t), "");
return rv;
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 769aa4f2..12a6a9f4 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -194,7 +194,7 @@ public:
true, //__in BOOL bManualReset,
false, //__in BOOL bInitialState,
nullptr); //__in_opt LPCTSTR lpName
- if (overlapped.hEvent == nullptr)
+ if (!overlapped.hEvent)
{
const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(dirPathPf)), formatSystemError(L"CreateEvent", ec));
@@ -319,7 +319,7 @@ private:
}
-struct DirWatcher::Pimpl
+struct DirWatcher::Impl
{
InterruptibleThread worker;
std::shared_ptr<SharedData> shared;
@@ -329,7 +329,7 @@ struct DirWatcher::Pimpl
DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
baseDirPath(dirPath),
- pimpl_(std::make_unique<Pimpl>())
+ pimpl_(std::make_unique<Impl>())
{
pimpl_->shared = std::make_shared<SharedData>();
@@ -345,7 +345,7 @@ DirWatcher::~DirWatcher()
{
pimpl_->worker.interrupt();
pimpl_->worker.detach(); //we don't have time to wait... would take ~50ms
- //Windows caveat: exitting the app will kill the thread and leak memory!
+ //Windows caveat: exitting the app will kill the thread and leak memory!
}
}
@@ -375,7 +375,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
#elif defined ZEN_LINUX
-struct DirWatcher::Pimpl
+struct DirWatcher::Impl
{
int notifDescr = 0;
std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr"
@@ -384,7 +384,7 @@ struct DirWatcher::Pimpl
DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
baseDirPath(dirPath),
- pimpl_(std::make_unique<Pimpl>())
+ pimpl_(std::make_unique<Impl>())
{
//get all subdirectories
std::vector<Zstring> fullFolderList { baseDirPath };
@@ -553,7 +553,7 @@ void eventCallback(ConstFSEventStreamRef streamRef,
}
-struct DirWatcher::Pimpl
+struct DirWatcher::Impl
{
FSEventStreamRef eventStream = nullptr;
std::vector<DirWatcher::Entry> changedFiles;
@@ -562,7 +562,7 @@ struct DirWatcher::Pimpl
DirWatcher::DirWatcher(const Zstring& dirPath) :
baseDirPath(dirPath),
- pimpl_(std::make_unique<Pimpl>())
+ pimpl_(std::make_unique<Impl>())
{
CFStringRef dirpathCf = nullptr;
try
diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h
index 28396ae3..5676555f 100644
--- a/zen/dir_watcher.h
+++ b/zen/dir_watcher.h
@@ -66,8 +66,8 @@ private:
const Zstring baseDirPath;
- struct Pimpl;
- std::unique_ptr<Pimpl> pimpl_;
+ struct Impl;
+ const std::unique_ptr<Impl> pimpl_;
};
}
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 2e9d93f8..bad1b60d 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -730,7 +730,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError
if (creationTime)
basicInfo.CreationTime = toLargeInteger(*creationTime);
- //set file time + attributes
+ //set file time + attributes
if (!::SetFileInformationByHandle(hFile, //__in HANDLE hFile,
FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&basicInfo, //__in LPVOID lpFileInformation,
@@ -906,21 +906,21 @@ void setWriteTimeNative(const Zstring& itemPath,
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
#if 0 //waiting for user feedback...
-#ifdef ZEN_WIN_VISTA_AND_LATER
- //bugs, bugs, bugs.... on "SharePoint" SetFileAttributes() seems to affect file modification time: http://www.freefilesync.org/forum/viewtopic.php?t=3699
- //on Vista we can avoid reopening the file (and the SharePoint bug)
-ZEN_ON_SCOPE_EXIT(
- if (attribsToRestore != INVALID_FILE_ATTRIBUTES)
- {
- FILE_BASIC_INFO basicInfo = {};
- basicInfo.FileAttributes = attribsToRestore;
- ::SetFileInformationByHandle(hFile, //__in HANDLE hFile,
- FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
- &basicInfo, //__in LPVOID lpFileInformation,
- sizeof(basicInfo)); //__in DWORD dwBufferSize
- attribsToRestore = INVALID_FILE_ATTRIBUTES;
- }
- );
+#ifdef ZEN_WIN_VISTA_AND_LATER
+ //bugs, bugs, bugs.... on "SharePoint" SetFileAttributes() seems to affect file modification time: http://www.freefilesync.org/forum/viewtopic.php?t=3699
+ //on Vista we can avoid reopening the file (and the SharePoint bug)
+ ZEN_ON_SCOPE_EXIT(
+ if (attribsToRestore != INVALID_FILE_ATTRIBUTES)
+ {
+ FILE_BASIC_INFO basicInfo = {};
+ basicInfo.FileAttributes = attribsToRestore;
+ ::SetFileInformationByHandle(hFile, //__in HANDLE hFile,
+ FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
+ &basicInfo, //__in LPVOID lpFileInformation,
+ sizeof(basicInfo)); //__in DWORD dwBufferSize
+ attribsToRestore = INVALID_FILE_ATTRIBUTES;
+ }
+ );
#endif
#endif
@@ -1407,26 +1407,26 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
}
-void makeDirectoryRecursivelyImpl(const Zstring& directory) //FileError
+void makeDirectoryRecursivelyImpl(const Zstring& dirPath) //FileError
{
- assert(!endsWith(directory, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input!
+ assert(!endsWith(dirPath, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input!
try
{
- copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
+ copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
}
catch (const ErrorTargetExisting&) {} //*something* existing: folder or FILE!
catch (const ErrorTargetPathMissing&)
{
//we need to create parent directories first
- const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
- if (!dirParent.empty())
+ const Zstring parentPath = beforeLast(dirPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ if (!parentPath.empty())
{
//recurse...
- makeDirectoryRecursivelyImpl(dirParent); //throw FileError
+ makeDirectoryRecursivelyImpl(parentPath); //throw FileError
//now try again...
- copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing)
+ copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing)
return;
}
throw;
@@ -1586,7 +1586,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
nullptr); //_Inout_opt_ LPOVERLAPPED lpOverlapped
}
- //(try to) set creation and modification time
+ //(try to) set creation (and modification) time
/*bool rv = */::SetFileTime(hDirTrg, //_In_ HANDLE hFile,
&dirInfo.ftCreationTime, //_Out_opt_ LPFILETIME lpCreationTime,
nullptr, //_Out_opt_ LPFILETIME lpLastAccessTime,
@@ -1598,6 +1598,16 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
#elif defined ZEN_MAC
if (hasNativeSupportForExtendedAtrributes(targetPath)) //throw FileError
::copyfile(sourcePath.c_str(), targetPath.c_str(), nullptr, COPYFILE_XATTR); //ignore errors, see related comments in copyFileOsSpecific()
+
+ //(try to) set creation (and modification) time
+ if (dirInfo.st_birthtimespec.tv_sec != 0)
+ try
+ {
+ setWriteTimeNative(targetPath, dirInfo.st_mtimespec, &dirInfo.st_birthtimespec, ProcSymlink::FOLLOW); //throw FileError
+ //dirInfo.st_birthtime; -> only seconds-precision
+ //dirInfo.st_mtime; ->
+ }
+ catch (FileError&) {}
#endif
ZEN_ON_SCOPE_FAIL(try { removeDirectorySimple(targetPath); }
@@ -1691,7 +1701,7 @@ namespace
{
#ifdef ZEN_WIN
/*
- CopyFileEx() BackupRead() FileRead()
+ CopyFileEx() BackupRead() ReadFile()
--------------------------------------------
Attributes YES NO NO
create time NO NO NO
@@ -1702,7 +1712,7 @@ Sparse NO YES NO
Nonstandard FS YES UNKNOWN -> error writing ADS to Samba, issues reading from NAS, error copying files having "blocked" state... ect.
PERF - 6% faster
-Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and FileRead()
+Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and ReadFile()
Current support for combinations of NTFS extended attributes:
@@ -1710,9 +1720,9 @@ Current support for combinations of NTFS extended attributes:
source attr | tf normal | tf compressed | tf encrypted | handled by
============|==================================================================
--- | --- -C- E-- copyFileWindowsDefault
- --S | --S -CS E-S copyFileWindowsBackupStream
+ --S | --S -CS E-S copyFileWindowsStream
-C- | -C- -C- E-- copyFileWindowsDefault
- -CS | -CS -CS E-S copyFileWindowsBackupStream
+ -CS | -CS -CS E-S copyFileWindowsStream
E-- | E-- E-- E-- copyFileWindowsDefault
E-S | E-- (NOK) E-- (NOK) E-- (NOK) copyFileWindowsDefault -> may fail with ERROR_DISK_FULL for large sparse files!!
@@ -1810,18 +1820,26 @@ bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //thr
return false;
ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource));
- BY_HANDLE_FILE_INFORMATION fileInfoSource = {};
- if (!::GetFileInformationByHandle(hSource, &fileInfoSource))
+ BY_HANDLE_FILE_INFORMATION sourceInfo = {};
+ if (!::GetFileInformationByHandle(hSource, &sourceInfo))
return false;
- return canCopyAsSparse(fileInfoSource.dwFileAttributes, targetFile); //throw ()
+ return canCopyAsSparse(sourceInfo.dwFileAttributes, targetFile); //throw ()
}
//=============================================================================================
-InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked
- const Zstring& targetFile,
- const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
+enum class StreamCopyType
+{
+ READ_FILE,
+ BACKUP_READ,
+};
+
+
+InSyncAttributes copyFileWindowsStream(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked
+ const Zstring& targetFile,
+ StreamCopyType scType,
+ const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
{
//try to get backup read and write privileges: help solve most "access denied" errors with FILE_FLAG_BACKUP_SEMANTICS:
//http://www.freefilesync.org/forum/viewtopic.php?t=1714
@@ -1830,7 +1848,6 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
try { activatePrivilege(PrivilegeName::RESTORE); }
catch (const FileError&) {}
- //open sourceFile for reading
HANDLE hFileSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName,
GENERIC_READ, //_In_ DWORD dwDesiredAccess,
FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
@@ -1862,16 +1879,18 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
throw FileError(errorMsg, errorDescr);
}
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileSource));
+ FileInput fileIn(hFileSource, sourceFile); //pass ownership
+
+ if (notifyProgress) notifyProgress(0); //throw X!
//----------------------------------------------------------------------
- BY_HANDLE_FILE_INFORMATION fileInfoSource = {};
- if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource))
+ BY_HANDLE_FILE_INFORMATION sourceInfo = {};
+ if (!::GetFileInformationByHandle(fileIn.getHandle(), &sourceInfo))
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"GetFileInformationByHandle");
//encrypted files cannot be read with BackupRead which would fail silently!
- const bool sourceIsEncrypted = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
- if (sourceIsEncrypted)
+ const bool sourceIsEncrypted = (sourceInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
+ if (sourceIsEncrypted && scType == StreamCopyType::BACKUP_READ)
throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: Source file is encrypted.");
//----------------------------------------------------------------------
@@ -1883,7 +1902,6 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; //
//FILE_ATTRIBUTE_ENCRYPTED -> no!
- //create targetFile and open it for writing
HANDLE hFileTarget = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), //_In_ LPCTSTR lpFileName,
GENERIC_READ | GENERIC_WRITE | DELETE, //_In_ DWORD dwDesiredAccess,
//GENERIC_READ required for FSCTL_SET_COMPRESSION, DELETE for ::SetFileInformationByHandle(),FileDispositionInfo
@@ -1892,7 +1910,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
CREATE_NEW, //_In_ DWORD dwCreationDisposition,
//FILE_FLAG_OVERLAPPED must not be used! FILE_FLAG_NO_BUFFERING should not be used!
- (fileInfoSource.dwFileAttributes & validAttribs) |
+ (sourceInfo.dwFileAttributes & validAttribs) |
FILE_FLAG_SEQUENTIAL_SCAN | //_In_ DWORD dwFlagsAndAttributes,
FILE_FLAG_BACKUP_SEMANTICS, //-> also required by FSCTL_SET_SPARSE
nullptr); //_In_opt_ HANDLE hTemplateFile
@@ -1911,14 +1929,14 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
throw FileError(errorMsg, errorDescr);
}
#ifdef ZEN_WIN_VISTA_AND_LATER
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget));
+ FileOutput fileOut(hFileTarget, targetFile); //pass ownership
//no need for ::DeleteFile(), we already have an open handle! Maybe this also prevents needless buffer-flushing in ::CloseHandle()??? Anyway, same behavior like ::CopyFileEx()
ZEN_ON_SCOPE_FAIL
(
FILE_DISPOSITION_INFO di = {};
di.DeleteFile = true;
- if (!::SetFileInformationByHandle(hFileTarget, //_In_ HANDLE hFile,
+ if (!::SetFileInformationByHandle(fileOut.getHandle(), //_In_ HANDLE hFile,
FileDispositionInfo, //_In_ FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&di, //_In_ LPVOID lpFileInformation,
sizeof(di))) //_In_ DWORD dwBufferSize
@@ -1928,29 +1946,30 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); }
catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget));
+ FileOutput fileOut(hFileTarget, targetFile); //pass ownership
#endif
+ if (notifyProgress) notifyProgress(0); //throw X!
//----------------------------------------------------------------------
- BY_HANDLE_FILE_INFORMATION fileInfoTarget = {};
- if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget))
+ BY_HANDLE_FILE_INFORMATION targetInfo = {};
+ if (!::GetFileInformationByHandle(fileOut.getHandle(), &targetInfo))
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle");
//return up-to-date file attributes
InSyncAttributes newAttrib;
- newAttrib.fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh);
- newAttrib.modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime);
- newAttrib.sourceFileId = extractFileId(fileInfoSource);
- newAttrib.targetFileId = extractFileId(fileInfoTarget);
+ newAttrib.fileSize = get64BitUInt(sourceInfo.nFileSizeLow, sourceInfo.nFileSizeHigh);
+ newAttrib.modificationTime = filetimeToTimeT(sourceInfo.ftLastWriteTime);
+ newAttrib.sourceFileId = extractFileId(sourceInfo);
+ newAttrib.targetFileId = extractFileId(targetInfo);
//#################### copy NTFS compressed attribute #########################
- const bool sourceIsCompressed = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0;
- const bool targetIsCompressed = (fileInfoTarget.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CreateFile if target parent folder is compressed!
+ const bool sourceIsCompressed = (sourceInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0;
+ const bool targetIsCompressed = (targetInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CreateFile if target parent folder is compressed!
if (sourceIsCompressed && !targetIsCompressed)
{
USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice,
+ if (!::DeviceIoControl(fileOut.getHandle(), //_In_ HANDLE hDevice,
FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode,
&cmpState, //_In_opt_ LPVOID lpInBuffer,
sizeof(cmpState), //_In_ DWORD nInBufferSize,
@@ -1969,91 +1988,97 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
//The application should retrieve the attributes by using GetFileAttributes prior to creating a backup with BackupRead.
//If a file originally had the sparse attribute (FILE_ATTRIBUTE_SPARSE_FILE), the backup utility must explicitly set the
//attribute on the restored file.
-
+ if (scType == StreamCopyType::BACKUP_READ)
#ifdef ZEN_WIN_VISTA_AND_LATER
- if (canCopyAsSparse(fileInfoSource.dwFileAttributes, hFileTarget)) //throw ()
+ if (canCopyAsSparse(sourceInfo.dwFileAttributes, fileOut.getHandle())) //throw ()
#else
- if (canCopyAsSparse(fileInfoSource.dwFileAttributes, targetFile)) //throw ()
+ if (canCopyAsSparse(sourceInfo.dwFileAttributes, targetFile)) //throw ()
#endif
- {
- DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice,
- FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode,
- nullptr, //_In_opt_ LPVOID lpInBuffer,
- 0, //_In_ DWORD nInBufferSize,
- nullptr, //_Out_opt_ LPVOID lpOutBuffer,
- 0, //_In_ DWORD nOutBufferSize,
- &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
- nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE");
- }
+ {
+ DWORD bytesReturned = 0;
+ if (!::DeviceIoControl(fileOut.getHandle(), //_In_ HANDLE hDevice,
+ FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode,
+ nullptr, //_In_opt_ LPVOID lpInBuffer,
+ 0, //_In_ DWORD nInBufferSize,
+ nullptr, //_Out_opt_ LPVOID lpOutBuffer,
+ 0, //_In_ DWORD nOutBufferSize,
+ &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
+ nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE");
+ }
//----------------------------------------------------------------------
- const DWORD BUFFER_SIZE = std::max(128 * 1024, static_cast<int>(sizeof(WIN32_STREAM_ID))); //must be greater than sizeof(WIN32_STREAM_ID)!
- std::vector<BYTE> buffer(BUFFER_SIZE);
+ if (scType == StreamCopyType::READ_FILE)
+ unbufferedStreamCopy(fileIn, fileOut, notifyProgress); //throw FileError, X
+ else
+ {
+ const DWORD BUFFER_SIZE = std::max(128 * 1024, static_cast<int>(sizeof(WIN32_STREAM_ID))); //must be greater than sizeof(WIN32_STREAM_ID)!
+ std::vector<BYTE> buffer(BUFFER_SIZE);
- LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite()
- LPVOID contextWrite = nullptr; //
+ LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite()
+ LPVOID contextWrite = nullptr; //
- ZEN_ON_SCOPE_EXIT(
- if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //MSDN: "lpContext must be passed [...] all other parameters are ignored."
- if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); //
+ ZEN_ON_SCOPE_EXIT(
+ if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //MSDN: "lpContext must be passed [...] all other parameters are ignored."
+ if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); //
- //stream-copy sourceFile to targetFile
- bool eof = false;
- bool someBytesRead = false; //try to detect failure reading encrypted files
- do
- {
- DWORD bytesRead = 0;
- if (!::BackupRead(hFileSource, //__in HANDLE hFile,
- &buffer[0], //__out LPBYTE lpBuffer,
- BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead,
- &bytesRead, //__out LPDWORD lpNumberOfBytesRead,
- false, //__in BOOL bAbort,
- false, //__in BOOL bProcessSecurity,
- &contextRead)) //__out LPVOID *lpContext
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead"); //better use fine-granular error messages "reading/writing"!
-
- if (bytesRead > BUFFER_SIZE)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this
-
- if (bytesRead < BUFFER_SIZE)
- eof = true;
-
- DWORD bytesWritten = 0;
- if (!::BackupWrite(hFileTarget, //__in HANDLE hFile,
- &buffer[0], //__in LPBYTE lpBuffer,
- bytesRead, //__in DWORD nNumberOfBytesToWrite,
- &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten,
- false, //__in BOOL bAbort,
- false, //__in BOOL bProcessSecurity,
- &contextWrite)) //__out LPVOID *lpContext
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite");
-
- if (bytesWritten != bytesRead)
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite: incomplete write."); //user should never see this
-
- //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)!
- if (notifyProgress) notifyProgress(bytesRead); //throw X!
-
- if (bytesRead > 0)
- someBytesRead = true;
- }
- while (!eof);
+ //stream-copy sourceFile to targetFile
+ bool eof = false;
+ bool someBytesRead = false; //try to detect failure reading encrypted files
+ do
+ {
+ DWORD bytesRead = 0;
+ if (!::BackupRead(fileIn.getHandle(), //__in HANDLE hFile,
+ &buffer[0], //__out LPBYTE lpBuffer,
+ BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead,
+ &bytesRead, //__out LPDWORD lpNumberOfBytesRead,
+ false, //__in BOOL bAbort,
+ false, //__in BOOL bProcessSecurity,
+ &contextRead)) //__out LPVOID *lpContext
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead"); //better use fine-granular error messages "reading/writing"!
+
+ if (bytesRead > BUFFER_SIZE)
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this
+
+ if (bytesRead < BUFFER_SIZE)
+ eof = true;
+
+ DWORD bytesWritten = 0;
+ if (!::BackupWrite(fileOut.getHandle(), //__in HANDLE hFile,
+ &buffer[0], //__in LPBYTE lpBuffer,
+ bytesRead, //__in DWORD nNumberOfBytesToWrite,
+ &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten,
+ false, //__in BOOL bAbort,
+ false, //__in BOOL bProcessSecurity,
+ &contextWrite)) //__out LPVOID *lpContext
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite");
+
+ if (bytesWritten != bytesRead)
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite: incomplete write."); //user should never see this
+
+ //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)!
+ if (notifyProgress) notifyProgress(bytesRead); //throw X!
+
+ if (bytesRead > 0)
+ someBytesRead = true;
+ }
+ while (!eof);
- //::BackupRead() silently fails reading encrypted files -> double check!
- if (!someBytesRead && get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U)
- //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)!
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()"
+ //::BackupRead() silently fails reading encrypted files -> double check!
+ if (!someBytesRead && get64BitUInt(sourceInfo.nFileSizeLow, sourceInfo.nFileSizeHigh) != 0U)
+ //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)!
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()"
+ }
- //time needs to be set at the end: BackupWrite() changes modification time
- setFileTimeByHandle(hFileTarget, &fileInfoSource.ftCreationTime,fileInfoSource.ftLastWriteTime, targetFile); //throw FileError
+ //time needs to be set at the end: WriteFile/BackupWrite() change modification time
+ setFileTimeByHandle(fileOut.getHandle(), &sourceInfo.ftCreationTime,sourceInfo.ftLastWriteTime, targetFile); //throw FileError
return newAttrib;
}
-DEFINE_NEW_FILE_ERROR(ErrorFallbackToCopyAsBackupStream);
+DEFINE_NEW_FILE_ERROR(ErrorFallbackToCopyViaBackupRead);
+DEFINE_NEW_FILE_ERROR(ErrorFallbackToCopyViaReadFile);
struct CallbackData
@@ -2127,7 +2152,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
#else
if (canCopyAsSparse(cbd.fileInfoSrc.dwFileAttributes, cbd.targetFile_)) //throw ()
#endif
- throw ErrorFallbackToCopyAsBackupStream(L"sparse, callback"); //use a different copy routine!
+ throw ErrorFallbackToCopyViaBackupRead(L"sparse, callback"); //use a different copy routine!
//#################### copy file creation time ################################
::SetFileTime(hDestinationFile, &cbd.fileInfoSrc.ftCreationTime, nullptr, nullptr); //no error handling!
@@ -2170,8 +2195,8 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
}
-InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyAsBackupStream
- const Zstring& targetFile,
+InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked,
+ const Zstring& targetFile, // ErrorFallbackToCopyViaReadFile, ErrorFallbackToCopyViaBackupRead
const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
{
//try to get backup read and write privileges: may help solve some "access denied" errors
@@ -2205,27 +2230,28 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE
nullptr, //__in_opt LPBOOL pbCancel,
copyFlags) != FALSE; //__in DWORD dwCopyFlags
if (cbd.exception)
- std::rethrow_exception(cbd.exception); //throw ?, process errors in callback first!
+ std::rethrow_exception(cbd.exception); //throw X, process errors in callback first!
if (!success)
{
const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
- //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition!
+ //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user-aborted operation IS an error condition!
//trying to copy huge sparse files may directly fail with ERROR_DISK_FULL before entering the callback function
if (canCopyAsSparse(sourceFile, targetFile)) //noexcept
- throw ErrorFallbackToCopyAsBackupStream(L"sparse, copy failure");
+ throw ErrorFallbackToCopyViaBackupRead(L"sparse, copy failure");
if (ec == ERROR_ACCESS_DENIED && backupPrivilegesActive)
- //chances are good this will work with copyFileWindowsBackupStream: http://www.freefilesync.org/forum/viewtopic.php?t=1714
- throw ErrorFallbackToCopyAsBackupStream(L"access denied");
+ //chances are good this will work with copyFileWindowsStream: http://www.freefilesync.org/forum/viewtopic.php?t=1714
+ throw ErrorFallbackToCopyViaReadFile(L"access denied");
- //copying ADS may incorrectly fail with ERROR_FILE_NOT_FOUND: http://www.freefilesync.org/forum/viewtopic.php?t=446
+ //- copying ADS may incorrectly fail with ERROR_FILE_NOT_FOUND: http://www.freefilesync.org/forum/viewtopic.php?t=446
+ //- even BackupWrite may fail => use ReadFile: http://www.freefilesync.org/forum/viewtopic.php?t=2321
if (ec == ERROR_FILE_NOT_FOUND &&
cbd.fileInfoSrc.nNumberOfLinks > 0 &&
cbd.fileInfoTrg.nNumberOfLinks > 0)
- throw ErrorFallbackToCopyAsBackupStream(L"bogus file not found");
+ throw ErrorFallbackToCopyViaReadFile(L"bogus file not found");
//assemble error message...
const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile));
@@ -2288,11 +2314,15 @@ InSyncAttributes copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Z
{
try
{
- return copyFileWindowsDefault(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyAsBackupStream
+ return copyFileWindowsDefault(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyViaReadFile, ErrorFallbackToCopyViaBackupRead
+ }
+ catch (ErrorFallbackToCopyViaReadFile&)
+ {
+ return copyFileWindowsStream(sourceFile, targetFile, StreamCopyType::READ_FILE, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
- catch (ErrorFallbackToCopyAsBackupStream&)
+ catch (ErrorFallbackToCopyViaBackupRead&)
{
- return copyFileWindowsBackupStream(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked
+ return copyFileWindowsStream(sourceFile, targetFile, StreamCopyType::BACKUP_READ, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
}
@@ -2325,7 +2355,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
const Zstring& targetFile,
const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
{
- FileInput fileIn(sourceFile); //throw FileError
+ FileInput fileIn(sourceFile); //throw FileError, (ErrorFileLocked -> Windows-only)
if (notifyProgress) notifyProgress(0); //throw X!
struct ::stat sourceInfo = {};
@@ -2409,16 +2439,16 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
#endif
/*
- ------------------
- |File Copy Layers|
- ------------------
- copyNewFile
- |
- copyFileOsSpecific (solve 8.3 issue on Windows)
- |
- copyFileWindowsSelectRoutine
- / \
-copyFileWindowsDefault(::CopyFileEx) copyFileWindowsBackupStream(::BackupRead/::BackupWrite)
+ ------------------
+ |File Copy Layers|
+ ------------------
+ copyNewFile
+ |
+ copyFileOsSpecific (solve 8.3 issue on Windows)
+ |
+ copyFileWindowsSelectRoutine
+ / \
+copyFileWindowsDefault(::CopyFileEx) copyFileWindowsStream(::BackupRead/::BackupWrite)
*/
}
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 6243980b..440a5d0d 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -26,13 +26,13 @@ using namespace zen;
namespace
{
#if defined ZEN_LINUX || defined ZEN_MAC
-//- "filepath" could be a named pipe which *blocks* forever for open()!
+//- "filePath" could be a named pipe which *blocks* forever for open()!
//- open() with O_NONBLOCK avoids the block, but opens successfully
//- create sample pipe: "sudo mkfifo named_pipe"
-void checkForUnsupportedType(const Zstring& filepath) //throw FileError
+void checkForUnsupportedType(const Zstring& filePath) //throw FileError
{
struct ::stat fileInfo = {};
- if (::stat(filepath.c_str(), &fileInfo) != 0) //follows symlinks
+ if (::stat(filePath.c_str(), &fileInfo) != 0) //follows symlinks
return; //let the caller handle errors like "not existing"
if (!S_ISREG(fileInfo.st_mode) &&
@@ -49,7 +49,7 @@ void checkForUnsupportedType(const Zstring& filepath) //throw FileError
const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & S_IFMT);
return name ? numFmt + L", " + name : numFmt;
};
- throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtPath(filepath)) + L" " + getTypeName(fileInfo.st_mode));
+ throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtPath(filePath)) + L" " + getTypeName(fileInfo.st_mode));
}
}
#endif
@@ -66,11 +66,11 @@ FileHandle getInvalidHandle()
}
-FileInput::FileInput(FileHandle handle, const Zstring& filepath) : FileBase(filepath), fileHandle(handle) {}
+FileInput::FileInput(FileHandle handle, const Zstring& filePath) : FileBase(filePath), fileHandle(handle) {}
-FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLocked
- FileBase(filepath), fileHandle(getInvalidHandle())
+FileInput::FileInput(const Zstring& filePath) : //throw FileError, ErrorFileLocked
+ FileBase(filePath), fileHandle(getInvalidHandle())
{
#ifdef ZEN_WIN
try { activatePrivilege(PrivilegeName::BACKUP); }
@@ -78,12 +78,12 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock
auto createHandle = [&](DWORD dwShareMode)
{
- return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName,
- GENERIC_READ, //_In_ DWORD dwDesiredAccess,
- dwShareMode, //_In_ DWORD dwShareMode,
- nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
- FILE_FLAG_SEQUENTIAL_SCAN | //_In_ DWORD dwFlagsAndAttributes,
+ return ::CreateFile(applyLongPathPrefix(filePath).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_READ, //_In_ DWORD dwDesiredAccess,
+ dwShareMode, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_SEQUENTIAL_SCAN | //_In_ DWORD dwFlagsAndAttributes,
/* possible values: (Reference https://msdn.microsoft.com/en-us/library/aa363858#caching_behavior)
FILE_FLAG_NO_BUFFERING
FILE_FLAG_RANDOM_ACCESS
@@ -122,14 +122,14 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock
if (fileHandle == INVALID_HANDLE_VALUE)
{
const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filePath));
std::wstring errorDescr = formatSystemError(L"CreateFile", ec);
if (ec == ERROR_SHARING_VIOLATION || //-> enhance error message!
ec == ERROR_LOCK_VIOLATION)
{
#ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message
- const std::wstring procList = vista::getLockingProcesses(filepath); //noexcept
+ const std::wstring procList = vista::getLockingProcesses(filePath); //noexcept
if (!procList.empty())
errorDescr = _("The file is locked by another process:") + L"\n" + procList;
#endif
@@ -140,12 +140,12 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock
}
#elif defined ZEN_LINUX || defined ZEN_MAC
- checkForUnsupportedType(filepath); //throw FileError; opening a named pipe would block forever!
+ checkForUnsupportedType(filePath); //throw FileError; opening a named pipe would block forever!
//don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html
- fileHandle = ::open(filepath.c_str(), O_RDONLY);
+ fileHandle = ::open(filePath.c_str(), O_RDONLY);
if (fileHandle == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath)), L"open");
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filePath)), L"open");
#endif
//------------------------------------------------------------------------------------------------------
@@ -160,7 +160,7 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock
#ifdef ZEN_LINUX //handle still un-owned => need constructor guard
//optimize read-ahead on input file:
if (::posix_fadvise(fileHandle, 0, 0, POSIX_FADV_SEQUENTIAL) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filepath)), L"posix_fadvise");
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filePath)), L"posix_fadvise");
#elif defined ZEN_MAC
//"dtruss" doesn't show use of "fcntl() F_RDAHEAD/F_RDADVISE" for "cp")
@@ -215,11 +215,11 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m
//----------------------------------------------------------------------------------------------------
-FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileBase(filepath), fileHandle(handle) {}
+FileOutput::FileOutput(FileHandle handle, const Zstring& filePath) : FileBase(filePath), fileHandle(handle) {}
-FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetExisting
- FileBase(filepath), fileHandle(getInvalidHandle())
+FileOutput::FileOutput(const Zstring& filePath, AccessFlag access) : //throw FileError, ErrorTargetExisting
+ FileBase(filePath), fileHandle(getInvalidHandle())
{
#ifdef ZEN_WIN
try { activatePrivilege(PrivilegeName::BACKUP); }
@@ -231,7 +231,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
auto createHandle = [&](DWORD dwFlagsAndAttributes)
{
- return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName,
+ return ::CreateFile(applyLongPathPrefix(filePath).c_str(), //_In_ LPCTSTR lpFileName,
GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess,
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858#files
quote: When an application creates a file across a network, it is better
@@ -256,7 +256,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
//CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system": https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858#files
if (ec == ERROR_ACCESS_DENIED && dwCreationDisposition == CREATE_ALWAYS)
{
- const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str());
+ const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filePath).c_str());
if (attrib != INVALID_FILE_ATTRIBUTES)
{
fileHandle = createHandle(attrib); //retry: alas this may still fail for hidden file, e.g. accessing shared folder in XP as Virtual Box guest!
@@ -267,14 +267,14 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
//begin of "regular" error reporting
if (fileHandle == INVALID_HANDLE_VALUE)
{
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filePath));
std::wstring errorDescr = formatSystemError(L"CreateFile", ec);
#ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message
if (ec == ERROR_SHARING_VIOLATION || //-> enhance error message!
ec == ERROR_LOCK_VIOLATION)
{
- const std::wstring procList = vista::getLockingProcesses(filepath); //noexcept
+ const std::wstring procList = vista::getLockingProcesses(filePath); //noexcept
if (!procList.empty())
errorDescr = _("The file is locked by another process:") + L"\n" + procList;
}
@@ -289,14 +289,14 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
}
#elif defined ZEN_LINUX || defined ZEN_MAC
- //checkForUnsupportedType(filepath); -> not needed, open() + O_WRONLY should fail fast
+ //checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast
- fileHandle = ::open(filepath.c_str(), O_WRONLY | O_CREAT | (access == FileOutput::ACC_CREATE_NEW ? O_EXCL : O_TRUNC),
+ fileHandle = ::open(filePath.c_str(), O_WRONLY | O_CREAT | (access == FileOutput::ACC_CREATE_NEW ? O_EXCL : O_TRUNC),
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //0666
if (fileHandle == -1)
{
const int ec = errno; //copy before making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filepath));
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filePath));
const std::wstring errorDescr = formatSystemError(L"open", ec);
if (ec == EEXIST)
diff --git a/zen/file_io.h b/zen/file_io.h
index 89cf77d5..4a135150 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -51,8 +51,8 @@ private:
class FileInput : public FileBase
{
public:
- FileInput(const Zstring& filepath); //throw FileError, ErrorFileLocked
- FileInput(FileHandle handle, const Zstring& filepath); //takes ownership!
+ FileInput(const Zstring& filePath); //throw FileError, ErrorFileLocked
+ FileInput(FileHandle handle, const Zstring& filePath); //takes ownership!
~FileInput();
//Windows: better use 64kB ?? https://technet.microsoft.com/en-us/library/cc938632
@@ -76,8 +76,8 @@ public:
ACC_CREATE_NEW
};
- FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetExisting
- FileOutput(FileHandle handle, const Zstring& filepath); //takes ownership!
+ FileOutput(const Zstring& filePath, AccessFlag access); //throw FileError, ErrorTargetExisting
+ FileOutput(FileHandle handle, const Zstring& filePath); //takes ownership!
~FileOutput();
FileOutput(FileOutput&& tmp);
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
index dba0996e..4376c13f 100644
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -9,12 +9,13 @@
#include <cassert>
#include <iterator>
-
+#include "stl_tools.h"
namespace zen
{
-//std::list(C++11)-like class for inplace element construction supporting non-copyable/movable types
-//may be replaced by C++11 std::list when available...or never...
+//std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types
+//-> no iterator invalidation after emplace_back()
+
template <class T>
class FixedList
{
@@ -23,7 +24,7 @@ class FixedList
template <class... Args>
Node(Args&& ... args) : val(std::forward<Args>(args)...) {}
- Node* next = nullptr; //singly linked list is sufficient
+ Node* next = nullptr; //singly-linked list is sufficient
T val;
};
@@ -33,13 +34,13 @@ public:
~FixedList() { clear(); }
template <class NodeT, class U>
- class ListIterator : public std::iterator<std::forward_iterator_tag, U>
+ class FixedIterator : public std::iterator<std::forward_iterator_tag, U>
{
public:
- ListIterator(NodeT* it = nullptr) : it_(it) {}
- ListIterator& operator++() { it_ = it_->next; return *this; }
- inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.it_ == rhs.it_; }
- inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); }
+ FixedIterator(NodeT* it = nullptr) : it_(it) {}
+ FixedIterator& operator++() { it_ = it_->next; return *this; }
+ inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
+ inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
U& operator* () const { return it_->val; }
U* operator->() const { return &it_->val; }
private:
@@ -47,48 +48,68 @@ public:
};
using value_type = T;
- using iterator = ListIterator< Node, T>;
- using const_iterator = ListIterator<const Node, const T>;
+ using iterator = FixedIterator< Node, T>;
+ using const_iterator = FixedIterator<const Node, const T>;
using reference = T&;
using const_reference = const T&;
- iterator begin() { return firstInsert; }
+ iterator begin() { return firstInsert_; }
iterator end () { return iterator(); }
- const_iterator begin() const { return firstInsert; }
+ const_iterator begin() const { return firstInsert_; }
const_iterator end () const { return const_iterator(); }
- //const_iterator cbegin() const { return firstInsert; }
+ //const_iterator cbegin() const { return firstInsert_; }
//const_iterator cend () const { return const_iterator(); }
- reference front() { return firstInsert->val; }
- const_reference front() const { return firstInsert->val; }
+ reference front() { return firstInsert_->val; }
+ const_reference front() const { return firstInsert_->val; }
- reference& back() { return lastInsert->val; }
- const_reference& back() const { return lastInsert->val; }
+ reference& back() { return lastInsert_->val; }
+ const_reference& back() const { return lastInsert_->val; }
template <class... Args>
- void emplace_back(Args&& ... args) { pushNode(new Node(std::forward<Args>(args)...)); }
+ void emplace_back(Args&&... args)
+ {
+ Node* newNode = new Node(std::forward<Args>(args)...);
+
+ if (!lastInsert_)
+ {
+ assert(!firstInsert_ && sz_ == 0);
+ firstInsert_ = lastInsert_ = newNode;
+ }
+ else
+ {
+ assert(lastInsert_->next == nullptr);
+ lastInsert_->next = newNode;
+ lastInsert_ = newNode;
+ }
+ ++sz_;
+ }
template <class Predicate>
void remove_if(Predicate pred)
{
Node* prev = nullptr;
- Node* ptr = firstInsert;
+ Node* ptr = firstInsert_;
while (ptr)
if (pred(ptr->val))
{
Node* next = ptr->next;
- deleteNode(ptr);
+
+ delete ptr;
+ assert(sz_ > 0);
+ --sz_;
+
ptr = next;
if (prev)
prev->next = next;
else
- firstInsert = next;
+ firstInsert_ = next;
if (!next)
- lastInsert = prev;
+ lastInsert_ = prev;
}
else
{
@@ -99,59 +120,119 @@ public:
void clear()
{
- Node* ptr = firstInsert;
+ Node* ptr = firstInsert_;
while (ptr)
{
Node* next = ptr->next;
- deleteNode(ptr);
+ delete ptr;
ptr = next;
}
- firstInsert = lastInsert = nullptr;
- assert(sz == 0);
+ sz_ = 0;
+ firstInsert_ = lastInsert_ = nullptr;
}
- bool empty() const { return firstInsert == nullptr; }
+ bool empty() const { return sz_ == 0; }
- size_t size() const { return sz; }
+ size_t size() const { return sz_; }
void swap(FixedList& other)
{
- std::swap(firstInsert, other.firstInsert);
- std::swap(lastInsert , other.lastInsert);
- std::swap(sz , other.sz);
+ std::swap(firstInsert_, other.firstInsert_);
+ std::swap(lastInsert_ , other.lastInsert_);
+ std::swap(sz_ , other.sz_);
}
private:
FixedList (const FixedList&) = delete;
FixedList& operator=(const FixedList&) = delete;
- void pushNode(Node* newNode) //throw()
+ Node* firstInsert_ = nullptr;
+ Node* lastInsert_ = nullptr; //point to last insertion; required by efficient emplace_back()
+ size_t sz_ = 0;
+};
+
+
+//just as fast as FixedList, but simpler, more CPU-cache-friendly => superseeds FixedList!
+template <class T>
+class FixedVector
+{
+public:
+ FixedVector() {}
+
+ /*
+ class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back()
+
+ template <class V>
+ class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this random-access if needed
{
- if (lastInsert == nullptr)
- {
- assert(firstInsert == nullptr && sz == 0);
- firstInsert = lastInsert = newNode;
- }
- else
- {
- assert(lastInsert->next == nullptr);
- lastInsert->next = newNode;
- lastInsert = newNode;
- }
- ++sz;
+ public:
+ FixedIterator(std::vector<std::unique_ptr<T>>& cont, size_t pos) : cont_(cont), pos_(pos) {}
+ FixedIterator& operator++() { ++pos_; return *this; }
+ inline friend bool operator==(const FixedIterator& lhs, EndIterator) { return lhs.pos_ == lhs.cont_.size(); }
+ inline friend bool operator!=(const FixedIterator& lhs, EndIterator) { return !(lhs == EndIterator()); }
+ V& operator* () const { return *cont_[pos_]; }
+ V* operator->() const { return &*cont_[pos_]; }
+ private:
+ std::vector<std::unique_ptr<T>>& cont_;
+ size_t pos_ = 0;
+ };
+ */
+
+ template <class IterImpl, class V>
+ class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this bidirectional if needed
+ {
+ public:
+ FixedIterator(IterImpl it) : it_(it) {}
+ FixedIterator& operator++() { ++it_; return *this; }
+ inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
+ inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
+ V& operator* () const { return **it_; }
+ V* operator->() const { return &**it_; }
+ private:
+ IterImpl it_; //TODO: avoid iterator invalidation after emplace_back(); caveat: end() must not store old length!
+ };
+
+ using value_type = T;
+ using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator , T>;
+ using const_iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::const_iterator, const T>;
+ using reference = T&;
+ using const_reference = const T&;
+
+ iterator begin() { return items_.begin(); }
+ iterator end () { return items_.end (); }
+
+ const_iterator begin() const { return items_.begin(); }
+ const_iterator end () const { return items_.end (); }
+
+ reference front() { return *items_.front(); }
+ const_reference front() const { return *items_.front(); }
+
+ reference& back() { return *items_.back(); }
+ const_reference& back() const { return *items_.back(); }
+
+ template <class... Args>
+ void emplace_back(Args&&... args)
+ {
+ items_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
}
- void deleteNode(Node* oldNode)
+ template <class Predicate>
+ void remove_if(Predicate pred)
{
- assert(sz > 0);
- --sz;
- delete oldNode;
+ erase_if(items_, [&](const std::unique_ptr<T>& p){ return pred(*p); });
}
- Node* firstInsert = nullptr;
- Node* lastInsert = nullptr; //point to last insertion; required by efficient emplace_back()
- size_t sz = 0;
+ void clear() { items_.clear(); }
+ bool empty() const { return items_.empty(); }
+ size_t size () const { return items_.size(); }
+ void swap(FixedVector& other) { items_.swap(other.items_); }
+
+private:
+ FixedVector (const FixedVector&) = delete;
+ FixedVector& operator=(const FixedVector&) = delete;
+
+ std::vector<std::unique_ptr<T>> items_;
};
}
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 71bb8688..d87a1643 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -214,7 +214,7 @@ private:
reinterpret_cast<LPTSTR>(&setting), //__out LPTSTR lpLCData,
sizeof(setting) / sizeof(TCHAR)) > 0; //__in int cchData
}
-
+
static bool getUserSetting(LCTYPE lt, std::wstring& setting)
{
const int bufferSize = ::GetLocaleInfo(LOCALE_USER_DEFAULT, lt, nullptr, 0);
@@ -261,7 +261,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
return &buffer[0]; //GetNumberFormat() returns char count *including* 0-termination!
}
}
- assert(false); //what's the problem?
+ assert(false); //what's the problem?
return number;
#elif defined ZEN_LINUX || defined ZEN_MAC
diff --git a/zen/globals.h b/zen/globals.h
index ff8c890d..123028c7 100644
--- a/zen/globals.h
+++ b/zen/globals.h
@@ -18,10 +18,10 @@ template <class T>
class Global
{
public:
- Global() { static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever"); }
- explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }
- ~Global() { set(nullptr); }
-
+ Global() { static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever"); }
+ explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }
+ ~Global() { set(nullptr); }
+
std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!)
{
while (pod.spinLock.exchange(true)) ;
@@ -45,19 +45,19 @@ public:
}
private:
- //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown
- //e.g. _("") used by message in debug_minidump.cpp or by some detached thread assembling an error message!
- //=> use trivially-destructible POD only!!!
- struct Pod
- {
- std::shared_ptr<T>* inst = nullptr;
- //serialize access; can't use std::mutex: has non-trival destructor
- std::atomic<bool> spinLock { false };
- } pod;
+ //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown
+ //e.g. _("") used by message in debug_minidump.cpp or by some detached thread assembling an error message!
+ //=> use trivially-destructible POD only!!!
+ struct Pod
+ {
+ std::shared_ptr<T>* inst = nullptr;
+ //serialize access; can't use std::mutex: has non-trival destructor
+ std::atomic<bool> spinLock { false };
+ } pod;
};
#if defined _MSC_VER && _MSC_VER < 1900
-#error function scope static initialization is not yet thread-safe!
+ #error function scope static initialization is not yet thread-safe!
#endif
}
diff --git a/zen/i18n.h b/zen/i18n.h
index 32b6ed80..4f62fd4c 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -100,7 +100,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural,
inline
Global<const TranslationHandler>& getGlobalTranslationHandler()
{
- //getTranslator() may be called even after static objects of this translation unit are destroyed!
+ //getTranslator() may be called even after static objects of this translation unit are destroyed!
static Global<const TranslationHandler> inst; //external linkage even in header!
return inst;
}
diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp
index 7ef7bc0f..826cf857 100644
--- a/zen/process_priority.cpp
+++ b/zen/process_priority.cpp
@@ -15,7 +15,7 @@ using namespace zen;
#ifdef ZEN_WIN
-struct PreventStandby::Pimpl {};
+struct PreventStandby::Impl {};
PreventStandby::PreventStandby()
{
@@ -30,7 +30,7 @@ PreventStandby::~PreventStandby()
}
-struct ScheduleForBackgroundProcessing::Pimpl {};
+struct ScheduleForBackgroundProcessing::Impl {};
ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing()
@@ -46,13 +46,13 @@ ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing()
}
#elif defined ZEN_LINUX
-struct PreventStandby::Pimpl {};
+struct PreventStandby::Impl {};
PreventStandby::PreventStandby() {}
PreventStandby::~PreventStandby() {}
//solution for GNOME?: http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit
-struct ScheduleForBackgroundProcessing::Pimpl {};
+struct ScheduleForBackgroundProcessing::Impl {};
ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {};
ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {};
diff --git a/zen/process_priority.h b/zen/process_priority.h
index ce2d0157..07679b0c 100644
--- a/zen/process_priority.h
+++ b/zen/process_priority.h
@@ -19,8 +19,8 @@ public:
PreventStandby(); //throw FileError
~PreventStandby();
private:
- struct Pimpl;
- std::unique_ptr<Pimpl> pimpl;
+ struct Impl;
+ const std::unique_ptr<Impl> pimpl;
};
//lower CPU and file I/O priorities
@@ -30,8 +30,8 @@ public:
ScheduleForBackgroundProcessing(); //throw FileError
~ScheduleForBackgroundProcessing();
private:
- struct Pimpl;
- std::unique_ptr<Pimpl> pimpl;
+ struct Impl;
+ const std::unique_ptr<Impl> pimpl;
};
}
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 8ab58901..853f51b9 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -95,24 +95,24 @@ public:
explicit ScopeGuard( F&& fun) : fun_(std::move(fun)) {}
ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)),
- exeptionCount(other.exeptionCount),
- dismissed(other.dismissed) { other.dismissed = true; }
+ exeptionCount_(other.exeptionCount_),
+ dismissed_(other.dismissed_) { other.dismissed_ = true; }
~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
{
- if (!dismissed)
- runScopeGuardDestructor(fun_, exeptionCount, StaticEnum<ScopeGuardRunMode, runMode>());
+ if (!dismissed_)
+ runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>());
}
- void dismiss() { dismissed = true; }
+ void dismiss() { dismissed_ = true; }
private:
ScopeGuard (const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
F fun_;
- const int exeptionCount = getUncaughtExceptionCount();
- bool dismissed = false;
+ const int exeptionCount_ = getUncaughtExceptionCount();
+ bool dismissed_ = false;
};
diff --git a/zen/shell_execute.h b/zen/shell_execute.h
index 5ebdd04d..ee8203c3 100644
--- a/zen/shell_execute.h
+++ b/zen/shell_execute.h
@@ -82,7 +82,7 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
trim(commandTmp, true, false); //CommandLineToArgvW() does not like leading spaces
std::vector<Zstring> argv;
- if (!commandTmp.empty()) //::CommandLineToArgvW returns the path to the current executable if empty string is passed
+ if (!commandTmp.empty()) //::CommandLineToArgvW returns the path to the current executable if empty string is passed
{
int argc = 0;
LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc);
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 07925981..064d5b51 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -61,8 +61,8 @@ template <class InputIterator1, class InputIterator2>
bool equal(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2);
-size_t hashBytes(const unsigned char* ptr, size_t len);
-size_t hashBytesAppend(size_t hashVal, const unsigned char* ptr, size_t len);
+template <class ByteIterator> size_t hashBytes (ByteIterator first, ByteIterator last);
+template <class ByteIterator> size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last);
//support for custom string classes in std::unordered_set/map
@@ -72,7 +72,8 @@ struct StringHash
size_t operator()(const String& str) const
{
const auto* strFirst = strBegin(str);
- return hashBytes(reinterpret_cast<const unsigned char*>(strFirst), strLength(str) * sizeof(strFirst[0]));
+ return hashBytes(reinterpret_cast<const char*>(strFirst),
+ reinterpret_cast<const char*>(strFirst + strLength(str)));
}
};
@@ -211,36 +212,36 @@ bool equal(InputIterator1 first1, InputIterator1 last1,
#endif
-inline
-size_t hashBytes(const unsigned char* ptr, size_t len)
+template <class ByteIterator> inline
+size_t hashBytes(ByteIterator first, ByteIterator last)
{
- //http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+ //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
#ifdef ZEN_BUILD_32BIT
const size_t basis = 2166136261U;
#elif defined ZEN_BUILD_64BIT
const size_t basis = 14695981039346656037ULL;
#endif
- return hashBytesAppend(basis, ptr, len);
+ return hashBytesAppend(basis, first, last);
}
-inline
-size_t hashBytesAppend(size_t hashVal, const unsigned char* ptr, size_t len)
+template <class ByteIterator> inline
+size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last)
{
#ifdef ZEN_BUILD_32BIT
const size_t prime = 16777619U;
#elif defined ZEN_BUILD_64BIT
const size_t prime = 1099511628211ULL;
#endif
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
- for (size_t i = 0; i < len; ++i)
+ for (; first != last; ++first)
{
- hashVal ^= static_cast<size_t>(ptr[i]);
+ hashVal ^= static_cast<size_t>(*first);
hashVal *= prime;
}
return hashVal;
}
-
}
#endif //STL_TOOLS_H_84567184321434
diff --git a/zen/string_base.h b/zen/string_base.h
index b54c3b2e..1ee37563 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -26,7 +26,7 @@ Allocator Policy:
*/
class AllocatorOptimalSpeed //exponential growth + min size
{
-public:
+protected:
//::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
static void deallocate(void* ptr) { ::free(ptr); }
@@ -38,7 +38,7 @@ public:
class AllocatorOptimalMemory //no wasted memory, but more reallocations required when manipulating string
{
-public:
+protected:
static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
static void deallocate(void* ptr) { ::free(ptr); }
static size_t calcCapacity(size_t length) { return length; }
@@ -81,8 +81,9 @@ protected:
Char* clone(Char* ptr)
{
- Char* newData = create(length(ptr)); //throw std::bad_alloc
- std::copy(ptr, ptr + length(ptr) + 1, newData);
+ const size_t len = length(ptr);
+ Char* newData = create(len); //throw std::bad_alloc
+ std::copy(ptr, ptr + len + 1, newData);
return newData;
}
@@ -207,25 +208,28 @@ private:
static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; }
};
+
+template <class Char>
+using DefaultStoragePolicy = StorageRefCountThreadSafe<Char, AllocatorOptimalSpeed>;
+
+
//################################################################################################################################################################
//perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison
-template <class Char, //Character Type
- template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy
- class AP = AllocatorOptimalSpeed> //Allocator Policy
-class Zbase : public SP<Char, AP>
+template <class Char, //Character Type
+ template <class> class SP = DefaultStoragePolicy> //Storage Policy
+class Zbase : public SP<Char>
{
public:
Zbase();
- Zbase(const Char* source); //implicit conversion from a C-string
- Zbase(const Char* source, size_t length);
- Zbase(const Zbase& source);
+ Zbase(const Char* str) : Zbase(str, str + strLength(str)) {} //implicit conversion from a C-string!
+ Zbase(const Char* str, size_t len) : Zbase(str, str + len) {}
+ Zbase(const Zbase& str);
Zbase(Zbase&& tmp) noexcept;
- //explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! //-> non-standard extension!!!
-
- //allow explicit construction from different string type, prevent ambiguity via SFINAE
- //template <class S> explicit Zbase(const S& other, typename S::value_type = 0);
+ template <class InputIterator>
+ Zbase(InputIterator first, InputIterator last);
+ //explicit Zbase(Char ch); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! //-> non-standard extension!!!
~Zbase();
@@ -238,19 +242,17 @@ public:
using const_reference = const Char&;
using value_type = Char;
- Zbase(const_iterator first, const_iterator last);
- Char* begin();
- Char* end ();
- const Char* begin() const;
- const Char* end () const;
- const Char* cbegin() const { return begin(); }
- const Char* cend () const { return end(); }
+ iterator begin();
+ iterator end ();
+ const_iterator begin () const { return rawStr_; }
+ const_iterator end () const { return rawStr_ + length(); }
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend () const { return end (); }
//std::string functions
size_t length() const;
size_t size () const { return length(); }
- const Char* c_str() const { return rawStr; } //C-string format with 0-termination
- const Char* data() const { return rawStr; } //internal representation, 0-termination not guaranteed
+ const Char* c_str() const { return rawStr_; } //C-string format with 0-termination
const Char operator[](size_t pos) const;
bool empty() const { return length() == 0; }
void clear();
@@ -261,20 +263,24 @@ public:
size_t rfind(const Char* str, size_t pos = npos) const; //
//Zbase& replace(size_t pos1, size_t n1, const Zbase& str);
void reserve(size_t minCapacity);
- Zbase& assign(const Char* source, size_t len);
- Zbase& append(const Char* source, size_t len);
+ Zbase& assign(const Char* str, size_t len) { return assign(str, str + len); }
+ Zbase& append(const Char* str, size_t len) { return append(str, str + len); }
+
+ template <class InputIterator> Zbase& assign(InputIterator first, InputIterator last);
+ template <class InputIterator> Zbase& append(InputIterator first, InputIterator last);
+
void resize(size_t newSize, Char fillChar = 0);
- void swap(Zbase& other);
+ void swap(Zbase& str) { std::swap(rawStr_, str.rawStr_); }
void push_back(Char val) { operator+=(val); } //STL access
void pop_back();
- Zbase& operator=(const Zbase& source);
+ Zbase& operator=(const Zbase& str);
Zbase& operator=(Zbase&& tmp) noexcept;
- Zbase& operator=(const Char* source);
- Zbase& operator=(Char source);
- Zbase& operator+=(const Zbase& other);
- Zbase& operator+=(const Char* other);
- Zbase& operator+=(Char ch);
+ Zbase& operator=(const Char* str) { return assign(str, strLength(str)); }
+ Zbase& operator=(Char ch) { return assign(&ch, 1); }
+ Zbase& operator+=(const Zbase& str) { return append(str.c_str(), str.length()); }
+ Zbase& operator+=(const Char* str) { return append(str, strLength(str)); }
+ Zbase& operator+=(Char ch) { return append(&ch, 1); }
static const size_t npos = static_cast<size_t>(-1);
@@ -284,32 +290,32 @@ private:
Zbase& operator+=(int) = delete; //
void push_back (int) = delete; //
- Char* rawStr;
+ Char* rawStr_;
};
-template <class Char, template <class, class> class SP, class AP> bool operator==(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs);
-template <class Char, template <class, class> class SP, class AP> bool operator==(const Zbase<Char, SP, AP>& lhs, const Char* rhs);
-template <class Char, template <class, class> class SP, class AP> inline bool operator==(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return operator==(rhs, lhs); }
+template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
+template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs);
+template <class Char, template <class> class SP> inline bool operator==(const Char* lhs, const Zbase<Char, SP>& rhs) { return operator==(rhs, lhs); }
-template <class Char, template <class, class> class SP, class AP> inline bool operator!=(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return !operator==(lhs, rhs); }
-template <class Char, template <class, class> class SP, class AP> inline bool operator!=(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { return !operator==(lhs, rhs); }
-template <class Char, template <class, class> class SP, class AP> inline bool operator!=(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return !operator==(lhs, rhs); }
+template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); }
+template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Char* rhs) { return !operator==(lhs, rhs); }
+template <class Char, template <class> class SP> inline bool operator!=(const Char* lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); }
-template <class Char, template <class, class> class SP, class AP> bool operator<(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs);
-template <class Char, template <class, class> class SP, class AP> bool operator<(const Zbase<Char, SP, AP>& lhs, const Char* rhs);
-template <class Char, template <class, class> class SP, class AP> bool operator<(const Char* lhs, const Zbase<Char, SP, AP>& rhs);
+template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
+template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs);
+template <class Char, template <class> class SP> bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs);
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Char* rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, Char rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Char* rhs) { return Zbase<Char, SP>(lhs) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, Char rhs) { return Zbase<Char, SP>(lhs) += rhs; }
//don't use unified first argument but save one move-construction in the r-value case instead!
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Zbase<Char, SP, AP>& rhs) { return std::move(lhs += rhs); } //the move *is* needed!!!
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-value parameter...
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, const Zbase<Char, SP>& rhs) { return std::move(lhs += rhs); } //the move *is* needed!!!
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-value parameter...
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(&lhs, 1) += rhs; }
-template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs ) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+( Char lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(&lhs, 1) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Char* lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs ) += rhs; }
@@ -324,82 +330,51 @@ template <class Char, template <class, class> class SP, class AP> inline Zbase<C
//################################# implementation ########################################
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::Zbase()
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::Zbase()
{
//resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues!
- rawStr = this->create(0);
- rawStr[0] = 0;
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::Zbase(const Char* source)
-{
- const size_t sourceLen = strLength(source);
- rawStr = this->create(sourceLen);
- std::copy(source, source + sourceLen + 1, rawStr); //include null-termination
+ rawStr_ = this->create(0);
+ rawStr_[0] = 0;
}
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::Zbase(const Char* source, size_t sourceLen)
+template <class Char, template <class> class SP>
+template <class InputIterator> inline
+Zbase<Char, SP>::Zbase(InputIterator first, InputIterator last)
{
- rawStr = this->create(sourceLen);
- std::copy(source, source + sourceLen, rawStr);
- rawStr[sourceLen] = 0;
+ rawStr_ = this->create(std::distance(first, last));
+ *std::copy(first, last, rawStr_) = 0;
}
-template <class Char, template <class, class> class SP, class AP>
-Zbase<Char, SP, AP>::Zbase(const_iterator first, const_iterator last)
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::Zbase(const Zbase<Char, SP>& str)
{
- assert(first <= last);
- const size_t sourceLen = last - first;
- rawStr = this->create(sourceLen);
- std::copy(first, last, rawStr);
- rawStr[sourceLen] = 0;
+ rawStr_ = this->clone(str.rawStr_);
}
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source)
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::Zbase(Zbase<Char, SP>&& tmp) noexcept
{
- rawStr = this->clone(source.rawStr);
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp) noexcept
-{
- rawStr = tmp.rawStr;
- tmp.rawStr = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor!
+ rawStr_ = tmp.rawStr_;
+ tmp.rawStr_ = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor!
//caveat: do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
}
-/*
-template <class Char, template <class, class> class SP, class AP>
-template <class S> inline
-Zbase<Char, SP, AP>::Zbase(const S& other, typename S::value_type)
-{
- const size_t sourceLen = other.size();
- rawStr = this->create(sourceLen);
- std::copy(other.c_str(), other.c_str() + sourceLen, rawStr);
- rawStr[sourceLen] = 0;
-}
-*/
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::~Zbase()
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::~Zbase()
{
static_assert(noexcept(this->~Zbase()), ""); //has exception spec of compiler-generated destructor by default
- this->destroy(rawStr); //rawStr may be nullptr; see move constructor!
+ this->destroy(rawStr_); //rawStr_ may be nullptr; see move constructor!
}
-template <class Char, template <class, class> class SP, class AP> inline
-size_t Zbase<Char, SP, AP>::find(const Zbase& str, size_t pos) const
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::find(const Zbase& str, size_t pos) const
{
assert(pos <= length());
const size_t len = length();
@@ -410,8 +385,8 @@ size_t Zbase<Char, SP, AP>::find(const Zbase& str, size_t pos) const
}
-template <class Char, template <class, class> class SP, class AP> inline
-size_t Zbase<Char, SP, AP>::find(const Char* str, size_t pos) const
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::find(const Char* str, size_t pos) const
{
assert(pos <= length());
const size_t len = length();
@@ -422,8 +397,8 @@ size_t Zbase<Char, SP, AP>::find(const Char* str, size_t pos) const
}
-template <class Char, template <class, class> class SP, class AP> inline
-size_t Zbase<Char, SP, AP>::find(Char ch, size_t pos) const
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::find(Char ch, size_t pos) const
{
assert(pos <= length());
const size_t len = length();
@@ -433,8 +408,8 @@ size_t Zbase<Char, SP, AP>::find(Char ch, size_t pos) const
}
-template <class Char, template <class, class> class SP, class AP> inline
-size_t Zbase<Char, SP, AP>::rfind(Char ch, size_t pos) const
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const
{
assert(pos == npos || pos <= length());
const size_t len = length();
@@ -444,8 +419,8 @@ size_t Zbase<Char, SP, AP>::rfind(Char ch, size_t pos) const
}
-template <class Char, template <class, class> class SP, class AP> inline
-size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const
{
assert(pos == npos || pos <= length());
const size_t strLen = strLength(str);
@@ -457,126 +432,112 @@ size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const
}
-template <class Char, template <class, class> class SP, class AP> inline
-void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar)
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::resize(size_t newSize, Char fillChar)
{
const size_t oldSize = length();
- if (this->canWrite(rawStr, newSize))
+ if (this->canWrite(rawStr_, newSize))
{
if (oldSize < newSize)
- std::fill(rawStr + oldSize, rawStr + newSize, fillChar);
- rawStr[newSize] = 0;
- this->setLength(rawStr, newSize);
+ std::fill(rawStr_ + oldSize, rawStr_ + newSize, fillChar);
+ rawStr_[newSize] = 0;
+ this->setLength(rawStr_, newSize);
}
else
{
Char* newStr = this->create(newSize);
if (oldSize < newSize)
{
- std::copy(rawStr, rawStr + oldSize, newStr);
+ std::copy(rawStr_, rawStr_ + oldSize, newStr);
std::fill(newStr + oldSize, newStr + newSize, fillChar);
}
else
- std::copy(rawStr, rawStr + newSize, newStr);
+ std::copy(rawStr_, rawStr_ + newSize, newStr);
newStr[newSize] = 0;
- this->destroy(rawStr);
- rawStr = newStr;
+ this->destroy(rawStr_);
+ rawStr_ = newStr;
}
}
-template <class Char, template <class, class> class SP, class AP> inline
-bool operator==(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs)
+template <class Char, template <class> class SP> inline
+bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
{
return lhs.length() == rhs.length() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); //respect embedded 0
}
-template <class Char, template <class, class> class SP, class AP> inline
-bool operator==(const Zbase<Char, SP, AP>& lhs, const Char* rhs)
+template <class Char, template <class> class SP> inline
+bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs)
{
return lhs.length() == strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0
}
-template <class Char, template <class, class> class SP, class AP> inline
-bool operator<(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs)
+template <class Char, template <class> class SP> inline
+bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
{
return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
rhs.begin(), rhs.end());
}
-template <class Char, template <class, class> class SP, class AP> inline
-bool operator<(const Zbase<Char, SP, AP>& lhs, const Char* rhs)
+template <class Char, template <class> class SP> inline
+bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs)
{
return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
rhs, rhs + strLength(rhs));
}
-template <class Char, template <class, class> class SP, class AP> inline
-bool operator<(const Char* lhs, const Zbase<Char, SP, AP>& rhs)
+template <class Char, template <class> class SP> inline
+bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs)
{
return std::lexicographical_compare(lhs, lhs + strLength(lhs), //respect embedded 0
rhs.begin(), rhs.end());
}
-template <class Char, template <class, class> class SP, class AP> inline
-size_t Zbase<Char, SP, AP>::length() const
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::length() const
{
- return SP<Char, AP>::length(rawStr);
+ return SP<Char>::length(rawStr_);
}
-template <class Char, template <class, class> class SP, class AP> inline
-const Char Zbase<Char, SP, AP>::operator[](size_t pos) const
+template <class Char, template <class> class SP> inline
+const Char Zbase<Char, SP>::operator[](size_t pos) const
{
assert(pos < length()); //design by contract! no runtime check!
- return rawStr[pos];
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-const Char* Zbase<Char, SP, AP>::begin() const
-{
- return rawStr;
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-const Char* Zbase<Char, SP, AP>::end() const
-{
- return rawStr + length();
+ return rawStr_[pos];
}
-template <class Char, template <class, class> class SP, class AP> inline
-Char* Zbase<Char, SP, AP>::begin()
+template <class Char, template <class> class SP> inline
+auto Zbase<Char, SP>::begin() -> iterator
{
reserve(length()); //make unshared!
- return rawStr;
+ return rawStr_;
}
-template <class Char, template <class, class> class SP, class AP> inline
-Char* Zbase<Char, SP, AP>::end()
+template <class Char, template <class> class SP> inline
+auto Zbase<Char, SP>::end() -> iterator
{
return begin() + length();
}
-template <class Char, template <class, class> class SP, class AP> inline
-void Zbase<Char, SP, AP>::clear()
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::clear()
{
if (!empty())
{
- if (this->canWrite(rawStr, 0))
+ if (this->canWrite(rawStr_, 0))
{
- rawStr[0] = 0; //keep allocated memory
- this->setLength(rawStr, 0); //
+ rawStr_[0] = 0; //keep allocated memory
+ this->setLength(rawStr_, 0); //
}
else
*this = Zbase();
@@ -584,111 +545,70 @@ void Zbase<Char, SP, AP>::clear()
}
-template <class Char, template <class, class> class SP, class AP> inline
-void Zbase<Char, SP, AP>::swap(Zbase<Char, SP, AP>& other)
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::reserve(size_t minCapacity) //make unshared and check capacity
{
- std::swap(rawStr, other.rawStr);
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-void Zbase<Char, SP, AP>::reserve(size_t minCapacity) //make unshared and check capacity
-{
- if (!this->canWrite(rawStr, minCapacity))
+ if (!this->canWrite(rawStr_, minCapacity))
{
//allocate a new string
const size_t len = length();
Char* newStr = this->create(len, std::max(len, minCapacity)); //reserve() must NEVER shrink the string: logical const!
- std::copy(rawStr, rawStr + len + 1, newStr); //include 0-termination
+ std::copy(rawStr_, rawStr_ + len + 1, newStr); //include 0-termination
- this->destroy(rawStr);
- rawStr = newStr;
+ this->destroy(rawStr_);
+ rawStr_ = newStr;
}
}
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::assign(const Char* source, size_t len)
+template <class Char, template <class> class SP>
+template <class InputIterator> inline
+Zbase<Char, SP>& Zbase<Char, SP>::assign(InputIterator first, InputIterator last)
{
- if (this->canWrite(rawStr, len))
+ const size_t len = std::distance(first, last);
+ if (this->canWrite(rawStr_, len))
{
- std::copy(source, source + len, rawStr);
- rawStr[len] = 0; //include null-termination
- this->setLength(rawStr, len);
+ *std::copy(first, last, rawStr_) = 0;
+ this->setLength(rawStr_, len);
}
else
- *this = Zbase(source, len);
+ *this = Zbase(first, last);
return *this;
}
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::append(const Char* source, size_t len)
+template <class Char, template <class> class SP>
+template <class InputIterator> inline
+Zbase<Char, SP>& Zbase<Char, SP>::append(InputIterator first, InputIterator last)
{
+ const size_t len = std::distance(first, last);
const size_t thisLen = length();
reserve(thisLen + len); //make unshared and check capacity
- std::copy(source, source + len, rawStr + thisLen);
- rawStr[thisLen + len] = 0;
- this->setLength(rawStr, thisLen + len);
+ *std::copy(first, last, rawStr_ + thisLen) = 0;
+ this->setLength(rawStr_, thisLen + len);
return *this;
}
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(const Zbase<Char, SP, AP>& other)
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>& Zbase<Char, SP>::operator=(const Zbase<Char, SP>& str)
{
- Zbase<Char, SP, AP>(other).swap(*this);
+ Zbase<Char, SP>(str).swap(*this);
return *this;
}
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Zbase<Char, SP, AP>&& tmp) noexcept
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>& Zbase<Char, SP>::operator=(Zbase<Char, SP>&& tmp) noexcept
{
swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead!
return *this;
}
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(const Char* source)
-{
- return assign(source, strLength(source));
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Char ch)
-{
- return assign(&ch, 1);
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(const Zbase<Char, SP, AP>& other)
-{
- return append(other.c_str(), other.length());
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(const Char* other)
-{
- return append(other, strLength(other));
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(Char ch)
-{
- return append(&ch, 1);
-}
-
-
-template <class Char, template <class, class> class SP, class AP> inline
-void Zbase<Char, SP, AP>::pop_back()
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::pop_back()
{
const size_t len = length();
assert(len > 0);
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 525227d6..9b8e7328 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -56,7 +56,8 @@ template <class S, class T, class Num> S printNumber(const T& format, const Num&
//string to string conversion: converts string-like type into char-compatible target string class
template <class T, class S> T copyStringTo(S&& str);
-
+//case-sensitive comparison
+template <class S, class T> int cmpString(const S& lhs, const T& rhs);
@@ -233,7 +234,7 @@ std::vector<S> split(const S& str, const T& delimiter)
const size_t delimLen = strLength(delimiter);
if (delimLen == 0)
- return { str };
+ return { str };
else
{
const auto* const delimFirst = strBegin(delimiter);
@@ -241,8 +242,8 @@ std::vector<S> split(const S& str, const T& delimiter)
const auto* blockStart = strBegin(str);
const auto* const strLast = blockStart + strLength(str);
-
- std::vector<S> output;
+
+ std::vector<S> output;
for (;;)
{
@@ -251,7 +252,7 @@ std::vector<S> split(const S& str, const T& delimiter)
output.emplace_back(blockStart, blockEnd - blockStart);
if (blockEnd == strLast) //clients expect: if delimiter not found, return str
- return output;
+ return output;
blockStart = blockEnd + delimLen;
}
}
@@ -263,11 +264,11 @@ namespace impl
ZEN_INIT_DETECT_MEMBER(append);
//either call operator+=(S(str, len)) or append(str, len)
-template <class S, class Char> inline
-typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, const Char* other, size_t len) { str.append(other, len); }
+template <class S, class InputIterator> inline
+typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); }
-template <class S, class Char> inline
-typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, const Char* other, size_t len) { str += S(other, len); }
+template <class S, class InputIterator> inline
+typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); }
}
@@ -289,20 +290,20 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
const auto* const oldBegin = strBegin(oldTerm);
const auto* const oldEnd = oldBegin + oldLen;
- //optimize "oldTerm not found"
+ //optimize "oldTerm not found": return ref-counted copy
const auto* strMatch = std::search(strPos, strEnd,
oldBegin, oldEnd);
if (strMatch == strEnd)
return str;
- const size_t newLen = strLength(newTerm);
const auto* const newBegin = strBegin(newTerm);
+ const auto* const newEnd = newBegin + strLength(newTerm);
S output;
for (;;)
{
- impl::stringAppend(output, strPos, strMatch - strPos);
- impl::stringAppend(output, newBegin, newLen);
+ impl::stringAppend(output, strPos, strMatch);
+ impl::stringAppend(output, newBegin, newEnd);
strPos = strMatch + oldLen;
@@ -314,7 +315,7 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
if (strMatch == strEnd)
break;
}
- impl::stringAppend(output, strPos, strEnd - strPos);
+ impl::stringAppend(output, strPos, strEnd);
return output;
}
@@ -380,6 +381,28 @@ template <class T, class S> inline
T copyStringTo(S&& str) { return impl::CopyStringToString<std::decay_t<S>, T>().copy(std::forward<S>(str)); }
+template <class S, class T> inline
+int cmpString(const S& lhs, const T& rhs)
+{
+ const size_t lenL = strLength(lhs);
+ const size_t lenR = strLength(rhs);
+
+ const auto* strPosL = strBegin(lhs);
+ const auto* strPosR = strBegin(rhs);
+
+ const auto* const strPosLLast = strPosL + std::min(lenL, lenR);
+
+ while (strPosL != strPosLLast)
+ {
+ const auto charL = static_cast<unsigned int>(*strPosL++); //unsigned char-comparison is the convention!
+ const auto charR = static_cast<unsigned int>(*strPosR++);
+ if (charL != charR)
+ return static_cast<int>(charL) - static_cast<int>(charR);
+ }
+ return static_cast<int>(lenL) - static_cast<int>(lenR);
+}
+
+
namespace impl
{
template <class Num> inline
@@ -635,7 +658,6 @@ Num stringTo(const S& str)
return impl::stringTo<Num>(str, TypeTag());
}
-
}
#endif //STRING_TOOLS_H_213458973046
diff --git a/zen/sys_error.h b/zen/sys_error.h
index 5897b413..4798b959 100644
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -13,8 +13,8 @@
#include "scope_guard.h"
#ifdef ZEN_WIN
- #include "win.h" //includes "windows.h"
-
+ #include "win.h" //tame WinINet.h include
+ #include <WinINet.h>
#elif defined ZEN_LINUX || defined ZEN_MAC
#include <cstring>
#include <cerrno>
@@ -50,6 +50,22 @@ private:
+#ifdef _MSC_VER
+#define THROW_LAST_SYS_ERROR(functionName) \
+ do \
+ { \
+ const ErrorCode ecInternal = getLastError(); \
+ throw SysError(formatSystemError(functionName, ecInternal)); \
+ \
+ __pragma(warning(suppress: 4127)) /*"conditional expression is constant"*/ \
+ } while (false)
+
+#else //same thing witout "__pragma":
+#define THROW_LAST_SYS_ERROR(functionName) \
+ do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
+#endif
+
+
@@ -78,10 +94,26 @@ std::wstring formatSystemErrorRaw(ErrorCode ec) //return empty string on error
ZEN_ON_SCOPE_EXIT(::SetLastError(currentError)); //this function must not change active system error variable!
LPWSTR buffer = nullptr;
- if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_MAX_WIDTH_MASK |
- FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders
- FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, ec, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0)
+ const DWORD rv = [&]
+ {
+ if (INTERNET_ERROR_BASE <= ec && ec <= INTERNET_ERROR_LAST)
+ return ::FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER, ::GetModuleHandle(L"WinINet.dll"), ec, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr);
+ else
+ return ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK |
+ FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders
+ FORMAT_MESSAGE_ALLOCATE_BUFFER, //_In_ DWORD dwFlags,
+ nullptr, //_In_opt_ LPCVOID lpSource,
+ ec, //_In_ DWORD dwMessageId,
+ 0, //_In_ DWORD dwLanguageId,
+ reinterpret_cast<LPWSTR>(&buffer), //_Out_ LPTSTR lpBuffer,
+ 0, //_In_ DWORD nSize,
+ nullptr); //_In_opt_ va_list *Arguments
+ }();
+ if (rv != 0)
if (buffer) //"don't trust nobody"
{
ZEN_ON_SCOPE_EXIT(::LocalFree(buffer));
@@ -102,9 +134,9 @@ std::wstring formatSystemErrorRaw(ErrorCode ec) //return empty string on error
std::wstring formatSystemError(const std::wstring& functionName, long long lastError) = delete; //intentional overload ambiguity to catch usage errors with HRESULT!
inline
-std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec)
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec)
{
- return formatSystemError(functionName, numberTo<std::wstring>(ec), formatSystemErrorRaw(ec));
+ return formatSystemError(functionName, numberTo<std::wstring>(ec), formatSystemErrorRaw(ec));
}
diff --git a/zen/thread.h b/zen/thread.h
index fd9dc76d..ac94da6a 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -65,6 +65,9 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //th
#ifdef ZEN_WIN
void setCurrentThreadName(const char* threadName);
#endif
+
+std::uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
+
//------------------------------------------------------------------------------------------
/*
@@ -335,7 +338,7 @@ private:
}
std::atomic<bool> interrupted{ false }; //std:atomic is uninitialized by default!!!
- //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
+ //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
std::condition_variable* activeCondition = nullptr;
std::mutex lockConditionPtr; //serialize pointer access (only!)
@@ -447,6 +450,28 @@ void setCurrentThreadName(const char* threadName)
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
#endif
+
+
+inline
+std::uint64_t getThreadId()
+{
+#ifdef ZEN_WIN
+ static_assert(sizeof(std::uint64_t) >= sizeof(DWORD), "");
+ return ::GetCurrentThreadId(); //no-fail
+
+#elif defined ZEN_LINUX
+ //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
+ static_assert(sizeof(std::uint64_t) >= sizeof(void*), "");
+ return reinterpret_cast<std::uint64_t>(static_cast<void*>(&errno));
+
+#elif defined ZEN_MAC
+ uint64_t tid = 0;
+ const int rv = ::pthread_threadid_np(nullptr, &tid); //yeah, theoretically no-fail, too :> http://opensource.apple.com//source/Libc/Libc-583/pthreads/pthread.c
+ assert(rv == 0);
+ (void)rv;
+ return tid;
+#endif
+}
}
#endif //THREAD_H_7896323423432235246427
diff --git a/zen/time.h b/zen/time.h
index 04d841b5..a4613408 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -261,12 +261,12 @@ template <class String, class String2> inline
String formatTime(const String2& format, const TimeComp& comp)
{
using FormatTag = typename SelectIf<
- IsSameType<String2, FormatDateTag >::value ||
- IsSameType<String2, FormatTimeTag >::value ||
- IsSameType<String2, FormatDateTimeTag >::value ||
- IsSameType<String2, FormatIsoDateTag >::value ||
- IsSameType<String2, FormatIsoTimeTag >::value ||
- IsSameType<String2, FormatIsoDateTimeTag>::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type;
+ IsSameType<String2, FormatDateTag >::value ||
+ IsSameType<String2, FormatTimeTag >::value ||
+ IsSameType<String2, FormatDateTimeTag >::value ||
+ IsSameType<String2, FormatIsoDateTag >::value ||
+ IsSameType<String2, FormatIsoTimeTag >::value ||
+ IsSameType<String2, FormatIsoDateTimeTag>::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type;
return implementation::formatTime<String>(format, comp, FormatTag());
}
diff --git a/zen/utf.h b/zen/utf.h
index 16136349..1544c9ab 100644
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -417,16 +417,16 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut
template <class CharString> inline
-bool isValidUtf8(const CharString& str)
+bool isValidUtf8(const CharString& str)
{
- using namespace implementation;
- bool valid = true;
+ using namespace implementation;
+ bool valid = true;
utf8ToCodePoint(strBegin(str), strBegin(str) + strLength(str),
- [&](CodePoint cp)
- {
- if (cp == REPLACEMENT_CHAR)
- valid = false; //perf: should we use an (expensive) exception for iteration break?
- });
+ [&](CodePoint cp)
+ {
+ if (cp == REPLACEMENT_CHAR)
+ valid = false; //perf: should we use an (expensive) exception for iteration break?
+ });
return valid;
}
diff --git a/zen/warn_static.h b/zen/warn_static.h
deleted file mode 100644
index 44d7bd73..00000000
--- a/zen/warn_static.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef WARN_STATIC_H_08724567834560832745
-#define WARN_STATIC_H_08724567834560832745
-
-/*
-Portable Compile-Time Warning
------------------------------
-Usage:
- warn_static("my message")
-*/
-
-#ifdef _MSC_VER
-#define STATIC_WARNING_MAKE_STRINGIZE_SUB(NUM) #NUM
-#define STATIC_WARNING_MAKE_STRINGIZE(NUM) STATIC_WARNING_MAKE_STRINGIZE_SUB(NUM)
-
-#define warn_static(TXT) \
- __pragma(message(__FILE__ "(" STATIC_WARNING_MAKE_STRINGIZE(__LINE__) "): Warning: " ## TXT))
-
-#elif defined __GNUC__
-#define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y
-#define STATIC_WARNING_CONCAT(X, Y) STATIC_WARNING_CONCAT_SUB(X, Y)
-
-#define warn_static(TXT) \
- typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \
- enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) };
-#endif
-
-#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/zstring.h b/zen/zstring.h
index 792b92db..902da80e 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -23,7 +23,7 @@
//"The reason for all the fuss above" - Loki/SmartPtr
//a high-performance string for interfacing with native OS APIs in multithreaded contexts
-using Zstring = zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, zen::AllocatorOptimalSpeed>;
+using Zstring = zen::Zbase<Zchar>;
int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen);
@@ -95,6 +95,9 @@ bool pathEndsWith(const S& str, const T& postfix)
}
+template <class S, class T, class U>
+S pathReplaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true);
+
@@ -195,6 +198,41 @@ int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
#endif
+template <class S, class T, class U> inline
+S pathReplaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
+{
+ assert(!contains(str, Zchar('\0')));
+
+#if defined ZEN_WIN || defined ZEN_MAC
+ using namespace zen;
+
+ S strU = makeUpperCopy(str); //S required to be a string class
+ S oldTermU = makeUpperCopy<S>(oldTerm); //[!] T not required to be a string class
+ assert(strLength(strU ) == strLength(str ));
+ assert(strLength(oldTermU) == strLength(oldTerm));
+
+ replace(strU, oldTermU, Zchar('\0'), replaceAll);
+
+ S output;
+
+ size_t i = 0;
+ for (auto c : strU)
+ if (c == Zchar('\0'))
+ {
+ output += newTerm;
+ i += oldTermU.size();
+ }
+ else
+ output += str[i++];
+
+ return output;
+
+#elif defined ZEN_LINUX
+ return replaceCpy(str, oldTerm, newTerm, replaceAll);
+#endif
+}
+
+
//---------------------------------------------------------------------------
//ZEN macro consistency checks:
#ifdef ZEN_WIN
bgstack15