diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:19:14 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:19:14 +0200 |
commit | 01eb8253196672c969a39587e90b49321a182428 (patch) | |
tree | 4a3b71d7913de519744466c9227fda6461c4f0b5 /zen | |
parent | 5.0 (diff) | |
download | FreeFileSync-01eb8253196672c969a39587e90b49321a182428.tar.gz FreeFileSync-01eb8253196672c969a39587e90b49321a182428.tar.bz2 FreeFileSync-01eb8253196672c969a39587e90b49321a182428.zip |
5.1
Diffstat (limited to 'zen')
40 files changed, 1035 insertions, 932 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp index 70076aa2..46eb956c 100644 --- a/zen/FindFilePlus/find_file_plus.cpp +++ b/zen/FindFilePlus/find_file_plus.cpp @@ -129,10 +129,10 @@ private: FileSearcher::FileSearcher(const wchar_t* dirname) : - hDir(NULL), + hDir(nullptr), nextEntryOffset(0) { - dirnameNt.Buffer = NULL; + dirnameNt.Buffer = nullptr; dirnameNt.Length = 0; dirnameNt.MaximumLength = 0; @@ -148,17 +148,17 @@ FileSearcher::FileSearcher(const wchar_t* dirname) : // RtlDosPathNameToRelativeNtPathName_U: used by Win7/Win8 available with OS version 5.2 (Windows Server 2003) and higher if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName, &dirnameNt, //__out ntFileName, - NULL, //__out_optFilePart, - NULL)) //__out_opt relativeName - empty if dosFileName is absolute + nullptr, //__out_optFilePart, + nullptr)) //__out_opt relativeName - empty if dosFileName is absolute throw NtFileError(STATUS_OBJECT_PATH_NOT_FOUND); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx() OBJECT_ATTRIBUTES objAttr = {}; - InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, - &dirnameNt, //[in] PUNICODE_STRING objectName, + InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes, + &dirnameNt, //[in] PUNICODE_STRING objectName, OBJ_CASE_INSENSITIVE, //[in] ULONG attributes, - NULL, //[in] HANDLE rootDirectory, - NULL); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor + nullptr, //[in] HANDLE rootDirectory, + nullptr); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor { IO_STATUS_BLOCK status = {}; NTSTATUS rv = ntOpenFile(&hDir, //__out PHANDLE FileHandle, @@ -236,15 +236,15 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError /* corresponding first access in ::FindFirstFileW() NTSTATUS rv = ntQueryDirectoryFile(hDir, //__in HANDLE fileHandle, - NULL, //__in_opt HANDLE event, - NULL, //__in_opt PIO_APC_ROUTINE apcRoutine, - NULL, //__in_opt PVOID apcContext, + nullptr, //__in_opt HANDLE event, + nullptr, //__in_opt PIO_APC_ROUTINE apcRoutine, + nullptr, //__in_opt PVOID apcContext, &status, //__out PIO_STATUS_BLOCK ioStatusBlock, &buffer, //__out_bcount(Length) PVOID fileInformation, BUFFER_SIZE, //__in ULONG length, ::FindFirstFileW() on all XP/Win7/Win8 uses sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(TCHAR) * MAX_PATH == 0x268 FileIdBothDirectoryInformation, //__in FILE_INFORMATION_CLASS fileInformationClass - all XP/Win7/Win8 use "FileBothDirectoryInformation" true, //__in BOOLEAN returnSingleEntry, - NULL, //__in_opt PUNICODE_STRING mask, + nullptr, //__in_opt PUNICODE_STRING mask, false); //__in BOOLEAN restartScan */ @@ -253,15 +253,15 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError { IO_STATUS_BLOCK status = {}; NTSTATUS rv = ntQueryDirectoryFile(hDir, //__in HANDLE fileHandle, - NULL, //__in_opt HANDLE event, - NULL, //__in_opt PIO_APC_ROUTINE apcRoutine, - NULL, //__in_opt PVOID apcContext, + nullptr, //__in_opt HANDLE event, + nullptr, //__in_opt PIO_APC_ROUTINE apcRoutine, + nullptr, //__in_opt PVOID apcContext, &status, //__out PIO_STATUS_BLOCK ioStatusBlock, &buffer, //__out_bcount(Length) PVOID fileInformation, BUFFER_SIZE, //__in ULONG length, ::FindNextFileW() on all XP/Win7/Win8 uses sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(TCHAR) * 2000 == 0x1000 QueryPolicy::fileInformationClass, //__in FILE_INFORMATION_CLASS fileInformationClass - all XP/Win7/Win8 use "FileBothDirectoryInformation" false, //__in BOOLEAN returnSingleEntry, - NULL, //__in_opt PUNICODE_STRING mask, + nullptr, //__in_opt PUNICODE_STRING mask, false); //__in BOOLEAN restartScan if (!NT_SUCCESS(rv)) { diff --git a/zen/FindFilePlus/find_file_plus.h b/zen/FindFilePlus/find_file_plus.h index cf1174eb..7306c32e 100644 --- a/zen/FindFilePlus/find_file_plus.h +++ b/zen/FindFilePlus/find_file_plus.h @@ -44,7 +44,7 @@ class FileSearcher; typedef FileSearcher* FindHandle; DLL_FUNCTION_DECLARATION -FindHandle openDir(const wchar_t* dirname); //returns NULL on error, call ::GetLastError() +FindHandle openDir(const wchar_t* dirname); //returns nullptr on error, call ::GetLastError() //note: do NOT place an asterisk at end, e.g. C:\SomeDir\*, as one would do for ::FindFirstFile() DLL_FUNCTION_DECLARATION diff --git a/zen/basic_math.h b/zen/basic_math.h index e9ab1a2f..dbc2d922 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -12,6 +12,7 @@ #include <iterator> #include <limits> #include <functional> +#include <cassert> namespace numeric @@ -32,7 +33,7 @@ template <class T> const T& max(const T& a, const T& b, const T& c); template <class T> -void restrict(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal +void confine(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal template <class InputIterator> std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last); @@ -57,13 +58,12 @@ template <class RandomAccessIterator> double median(RandomAccessIterator first, RandomAccessIterator last); //note: invalidates input range! template <class InputIterator> -double stdDeviation(InputIterator first, InputIterator last, double* mean = NULL); //estimate standard deviation (and thereby arithmetic mean) +double stdDeviation(InputIterator first, InputIterator last, double* mean = nullptr); //estimate standard deviation (and thereby arithmetic mean) //median absolute deviation: "mad / 0.6745" is a robust measure for standard deviation of a normal distribution template <class RandomAccessIterator> double mad(RandomAccessIterator first, RandomAccessIterator last); //note: invalidates input range! - template <class InputIterator> double norm2(InputIterator first, InputIterator last); @@ -99,7 +99,10 @@ const double ln2 = 0.693147180559945309417; template <class T> inline T abs(T value) { - return value < 0 ? -1 * value : value; + if (value < 0) + return -value; // operator "?:" caveat: may be different type than "value" + else + return value; } template <class T> inline @@ -131,7 +134,7 @@ const T& max(const T& a, const T& b, const T& c) template <class T> inline -void restrict(T& val, const T& minVal, const T& maxVal) +void confine(T& val, const T& minVal, const T& maxVal) { assert(minVal <= maxVal); if (val < minVal) @@ -142,19 +145,36 @@ void restrict(T& val, const T& minVal, const T& maxVal) template <class InputIterator, class Compare> inline -std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare comp) +std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare compLess) { + //by factor 1.5 to 3 faster than boost::minmax_element (=two-step algorithm) for built-in types! + InputIterator lowest = first; InputIterator largest = first; if (first != last) - while (++first != last) + { + auto minVal = *lowest; //nice speedup on 64 bit! + auto maxVal = *largest; // + for (;;) { - if (comp(*largest, *first)) // or: if (comp(*largest,*lowest)) for the comp version + ++first; + if (first == last) + break; + const auto val = *first; + + if (compLess(maxVal, val)) + { largest = first; - else if (comp(*first, *lowest)) + maxVal = val; + } + else if (compLess(val, minVal)) + { lowest = first; + minVal = val; + } } + } return std::make_pair(lowest, largest); } @@ -207,10 +227,10 @@ template <class T> struct PowerImpl<10, T>; //not defined: invalidates power<N> for N >= 10 } -template <size_t N, class T> inline +template <size_t n, class T> inline T power(const T& value) { - return PowerImpl<N, T>::result(value); + return PowerImpl<n, T>::result(value); } @@ -231,8 +251,7 @@ double degToRad(double degree) template <class InputIterator> inline double arithmeticMean(InputIterator first, InputIterator last) { - //low level implementation to avoid random-access requirement on iterator - size_t n = 0; + size_t n = 0; //avoid random-access requirement for iterator! double sum_xi = 0; for (; first != last; ++first, ++n) @@ -280,10 +299,7 @@ double mad(RandomAccessIterator first, RandomAccessIterator last) //note: invali if (n % 2 != 0) return midVal; else //n is even and >= 2 in this context: return mean of two middle values - { - const double midVal2 = abs(*std::max_element(first, first + n / 2, lessMedAbs) - m); - return 0.5 * (midVal2 + midVal); - } + return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal); } return 0; } diff --git a/zen/com_error.h b/zen/com_error.h index 7f967f7c..c67c4193 100644 --- a/zen/com_error.h +++ b/zen/com_error.h @@ -63,11 +63,11 @@ Equivalent to: std::wstring formatWin32Msg(DWORD dwMessageId) //return empty string on error { std::wstring output; - LPWSTR buffer = NULL; + 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, NULL, dwMessageId, 0, reinterpret_cast<LPWSTR>(&buffer), 0, NULL) != 0) + FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, dwMessageId, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0) { if (buffer) //just to be sure { diff --git a/zen/com_ptr.h b/zen/com_ptr.h index b55d873e..98963cd1 100644 --- a/zen/com_ptr.h +++ b/zen/com_ptr.h @@ -20,7 +20,7 @@ Example: -------- ComPtr<IUPnPDeviceFinder> devFinder; if (FAILED(::CoCreateInstance(CLSID_UPnPDeviceFinder, - NULL, + nullptr, CLSCTX_ALL, IID_PPV_ARGS(devFinder.init())))) return -1; diff --git a/zen/com_util.h b/zen/com_util.h index 2845e352..fe02eadd 100644 --- a/zen/com_util.h +++ b/zen/com_util.h @@ -82,7 +82,7 @@ std::vector<ComPtr<T> > convertEnum(const ComPtr<U>& enumObj) if (enumUnknown) { ComPtr<IUnknown> itemTmp; - while (enumUnknown->Next(1, itemTmp.init(), NULL) == S_OK) //returns S_FALSE == 1 when finished! Don't use SUCCEEDED()!!! + while (enumUnknown->Next(1, itemTmp.init(), nullptr) == S_OK) //returns S_FALSE == 1 when finished! Don't use SUCCEEDED()!!! { ComPtr<T> itemNew = com_dynamic_cast<T>(itemTmp); if (itemNew) @@ -103,11 +103,11 @@ std::wstring getText(ComPtr<T> comObj, MemFun memFun) if (!comObj) return std::wstring(); - BSTR bstr = NULL; + BSTR bstr = nullptr; if (FAILED((comObj.get()->*memFun)(&bstr))) return std::wstring(); - if (bstr) //NULL means "no text" + if (bstr) //nullptr means "no text" { text = std::wstring(bstr, ::SysStringLen(bstr)); //correctly copy 0-characters ::SysFreeString(bstr); diff --git a/zen/debug_new.cpp b/zen/debug_new.cpp index 9992f0b3..6cc0e2da 100644 --- a/zen/debug_new.cpp +++ b/zen/debug_new.cpp @@ -15,14 +15,14 @@ namespace { LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo) { - HANDLE hFile = ::CreateFile(L"exception.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = ::CreateFile(L"exception.dmp", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != INVALID_HANDLE_VALUE) { MINIDUMP_EXCEPTION_INFORMATION exInfo = {}; exInfo.ThreadId = ::GetCurrentThreadId(); exInfo.ExceptionPointers = pExceptionInfo; - MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : NULL; + MINIDUMP_EXCEPTION_INFORMATION* exceptParam = pExceptionInfo ? &exInfo : nullptr; /*bool rv = */ ::MiniDumpWriteDump(::GetCurrentProcess(), //__in HANDLE hProcess, @@ -30,8 +30,8 @@ LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo) hFile, //__in HANDLE hFile, MiniDumpWithDataSegs, //__in MINIDUMP_TYPE DumpType, ->Standard: MiniDumpNormal, Medium: MiniDumpWithDataSegs, Full: MiniDumpWithFullMemory exceptParam, //__in PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - NULL, //__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - NULL); //__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam + nullptr, //__in PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + nullptr); //__in PMINIDUMP_CALLBACK_INFORMATION CallbackParam ::CloseHandle(hFile); } @@ -45,5 +45,5 @@ struct Dummy { Dummy() { ::SetUnhandledExceptionFilter(writeDumpOnException); }} void mem_check::writeMinidump() { - writeDumpOnException(NULL); + writeDumpOnException(nullptr); } diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 6ab56100..ed07a1e4 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -28,15 +28,6 @@ using namespace zen; #ifdef FFS_WIN namespace { -inline -bool errorCodeForNotExisting(const DWORD lastError) -{ - return lastError == ERROR_PATH_NOT_FOUND || - lastError == ERROR_BAD_NETPATH || - lastError == ERROR_NETNAME_DELETED; -} - - class SharedData { public: @@ -152,13 +143,13 @@ public: hDir = ::CreateFile(applyLongPathPrefix(dirname.c_str()).c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, - NULL); + nullptr); if (hDir == INVALID_HANDLE_VALUE) { - const std::wstring errorMsg = _("Could not initialize directory monitoring:") + L"\n\"" + dirname + L"\"" + L"\n\n" + zen::getLastErrorFormatted(); + const std::wstring errorMsg = _("Could not initialize directory monitoring:") + L"\n\"" + dirname + L"\"" L"\n\n" + zen::getLastErrorFormatted(); if (errorCodeForNotExisting(::GetLastError())) throw ErrorNotExisting(errorMsg); throw FileError(errorMsg); @@ -185,30 +176,30 @@ public: //actual work OVERLAPPED overlapped = {}; - overlapped.hEvent = ::CreateEvent(NULL, //__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, - true, //__in BOOL bManualReset, - false, //__in BOOL bInitialState, - NULL); //__in_opt LPCTSTR lpName - if (overlapped.hEvent == NULL) - return shared_->reportError(_("Error when monitoring directories.") + L" (CreateEvent)" + L"\n\n" + getLastErrorFormatted(), ::GetLastError()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(overlapped.hEvent)); + overlapped.hEvent = ::CreateEvent(nullptr, //__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, + true, //__in BOOL bManualReset, + false, //__in BOOL bInitialState, + nullptr); //__in_opt LPCTSTR lpName + if (overlapped.hEvent == nullptr) + return shared_->reportError(_("Error when monitoring directories.") + L" (CreateEvent)" L"\n\n" + getLastErrorFormatted(), ::GetLastError()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent)); //asynchronous variant: runs on this thread's APC queue! - if (!::ReadDirectoryChangesW(hDir, // __in HANDLE hDirectory, - &buffer[0], // __out LPVOID lpBuffer, + if (!::ReadDirectoryChangesW(hDir, // __in HANDLE hDirectory, + &buffer[0], // __out LPVOID lpBuffer, static_cast<DWORD>(buffer.size()), // __in DWORD nBufferLength, - true, // __in BOOL bWatchSubtree, + true, // __in BOOL bWatchSubtree, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, // __in DWORD dwNotifyFilter, - NULL, // __out_opt LPDWORD lpBytesReturned, + nullptr, // __out_opt LPDWORD lpBytesReturned, &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped, - NULL)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine - return shared_->reportError(_("Error when monitoring directories.") + L" (ReadDirectoryChangesW)" + L"\n\n" + getLastErrorFormatted(), ::GetLastError()); + nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine + return shared_->reportError(_("Error when monitoring directories.") + L" (ReadDirectoryChangesW)" L"\n\n" + getLastErrorFormatted(), ::GetLastError()); //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! - zen::ScopeGuard lockAio = zen::makeGuard([&]() + zen::ScopeGuard guardAio = zen::makeGuard([&] { //http://msdn.microsoft.com/en-us/library/aa363789(v=vs.85).aspx if (::CancelIo(hDir) == TRUE) //cancel all async I/O related to this handle and thread @@ -218,16 +209,15 @@ public: } }); - DWORD bytesWritten = 0; - //wait for results + DWORD bytesWritten = 0; while (!::GetOverlappedResult(hDir, //__in HANDLE hFile, &overlapped, //__in LPOVERLAPPED lpOverlapped, &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred, false)) //__in BOOL bWait { if (::GetLastError() != ERROR_IO_INCOMPLETE) - return shared_->reportError(_("Error when monitoring directories.") + L" (GetOverlappedResult)" + L"\n\n" + getLastErrorFormatted(), ::GetLastError()); + return shared_->reportError(_("Error when monitoring directories.") + L" (GetOverlappedResult)" L"\n\n" + getLastErrorFormatted(), ::GetLastError()); //execute asynchronous procedure calls (APC) queued on this thread ::SleepEx(50, // __in DWORD dwMilliseconds, @@ -235,7 +225,7 @@ public: boost::this_thread::interruption_point(); } - lockAio.dismiss(); + guardAio.dismiss(); shared_->addChanges(&buffer[0], bytesWritten, dirname); //throw () } @@ -370,8 +360,8 @@ public: DirsOnlyTraverser(std::vector<Zstring>& dirs, const std::shared_ptr<TraverseCallback>& otherMe) : otherMe_(otherMe), dirs_(dirs) {} - virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {} - virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) {} + virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) {} + virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) {} virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName) { dirs_.push_back(fullName); @@ -21,7 +21,7 @@ Manage DLL function and library ownership Usage: typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process); - const zen::DllFun<IsWow64ProcessFun> isWow64Process(L"kernel32.dll", "IsWow64Process"); + const zen::SysDllFun<IsWow64ProcessFun> isWow64Process(L"kernel32.dll", "IsWow64Process"); if (isWow64Process) ... use function ptr ... */ @@ -29,11 +29,11 @@ template <class Func> class DllFun { public: - DllFun() : fun(NULL) {} + DllFun() : fun(nullptr) {} DllFun(const wchar_t* libraryName, const char* functionName) : hLibRef(new HMODULE(::LoadLibrary(libraryName)), deleter), - fun(*hLibRef ? reinterpret_cast<Func>(::GetProcAddress(*hLibRef, functionName)) : NULL) {} + fun(*hLibRef ? reinterpret_cast<Func>(::GetProcAddress(*hLibRef, functionName)) : nullptr) {} operator Func() const { return fun; } @@ -50,10 +50,13 @@ template <class Func> class SysDllFun { public: - SysDllFun() : fun(NULL) {} + SysDllFun() : fun(nullptr) {} - SysDllFun(const wchar_t* systemLibrary, const char* functionName) : - fun(reinterpret_cast<Func>(::GetProcAddress(::GetModuleHandle(systemLibrary), functionName))) {} + SysDllFun(const wchar_t* systemLibrary, const char* functionName) + { + HMODULE mod = ::GetModuleHandle(systemLibrary); + fun = mod ? reinterpret_cast<Func>(::GetProcAddress(mod, functionName)) : nullptr; + } operator Func() const { return fun; } @@ -88,32 +91,24 @@ std::string getResourceStream(const std::wstring& libraryName, size_t resourceId -//---------------Inline Implementation--------------------------------------------------- +//--------------- implementation--------------------------------------------------- inline std::string getResourceStream(const wchar_t* libraryName, size_t resourceId) { - std::string output; - HMODULE module = ::LoadLibrary(libraryName); - if (module) + if (HMODULE module = ::LoadLibrary(libraryName)) { - ZEN_ON_BLOCK_EXIT(::FreeLibrary(module)); + ZEN_ON_SCOPE_EXIT(::FreeLibrary(module)); - const HRSRC res = ::FindResource(module, MAKEINTRESOURCE(resourceId), RT_RCDATA); - if (res != NULL) + if (HRSRC res = ::FindResource(module, MAKEINTRESOURCE(resourceId), RT_RCDATA)) { - const HGLOBAL resHandle = ::LoadResource(module, res); - if (resHandle != NULL) + if (HGLOBAL resHandle = ::LoadResource(module, res)) { - const char* stream = static_cast<const char*>(::LockResource(resHandle)); - if (stream) - { - const DWORD streamSize = ::SizeofResource(module, res); - output.assign(stream, streamSize); - } + if (const char* stream = static_cast<const char*>(::LockResource(resHandle))) + return std::string(stream, static_cast<size_t>(::SizeofResource(module, res))); //size is 0 on error } } } - return output; + return std::string(); } } diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp index 9fe3a550..3ecfd8e9 100644 --- a/zen/dst_hack.cpp +++ b/zen/dst_hack.cpp @@ -17,9 +17,9 @@ namespace Zstring getVolumeName(const Zstring& filename) { //this call is expensive: ~1.5 ms! - // if (!::GetVolumePathName(applyLongPathPrefix(filename).c_str(), //__in LPCTSTR lpszFileName, - // fsName, //__out LPTSTR lpszVolumePathName, - // BUFFER_SIZE)) //__in DWORD cchBufferLength + // if (!::GetVolumePathName(filename.c_str(), //__in LPCTSTR lpszFileName, + // fsName, //__out LPTSTR lpszVolumePathName, + // BUFFER_SIZE)) //__in DWORD cchBufferLength // ... // Zstring volumePath = fsName; // if (!volumePath.EndsWith(FILE_NAME_SEPARATOR)) //a trailing backslash is required @@ -65,11 +65,11 @@ bool dst::isFatDrive(const Zstring& fileName) //throw() //suprisingly fast: ca. 0.03 ms per call! if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - NULL, //__out LPTSTR lpVolumeNameBuffer, + nullptr, //__out LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, fsName, //__out LPTSTR lpFileSystemNameBuffer, BUFFER_SIZE)) //__in DWORD nFileSystemNameSize { @@ -105,11 +105,11 @@ bool dst::isFatDrive(HANDLE hFile) //throw() wchar_t fsName[BUFFER_SIZE]; if (!getVolumeInformationByHandle(hFile, //__in HANDLE hFile, - NULL, //__out LPTSTR lpVolumeNameBuffer, + nullptr, //__out LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, fsName, //__out LPTSTR lpFileSystemNameBuffer, BUFFER_SIZE)) //__in DWORD nFileSystemNameSize { @@ -158,6 +158,7 @@ Int64 toInt64(const FILETIME& fileTime) } +inline FILETIME utcToLocal(const FILETIME& utcTime) //throw (std::runtime_error) { //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: @@ -167,14 +168,15 @@ FILETIME utcToLocal(const FILETIME& utcTime) //throw (std::runtime_error) &localTime)) //__out LPFILETIME lpLocalFileTime { const std::wstring errorMessage = _("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" + - L"High: " + toString<std::wstring>(utcTime.dwHighDateTime) + L" " + - L"Low: " + toString<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); + L"High: " + numberTo<std::wstring>(utcTime.dwHighDateTime) + L" " + + L"Low: " + numberTo<std::wstring>(utcTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); } return localTime; } +inline FILETIME localToUtc(const FILETIME& localTime) //throw (std::runtime_error) { //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: @@ -184,8 +186,8 @@ FILETIME localToUtc(const FILETIME& localTime) //throw (std::runtime_error) &utcTime)) //__out LPFILETIME lpFileTime { const std::wstring errorMessage = _("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" + - L"High: " + toString<std::wstring>(localTime.dwHighDateTime) + L" " + - L"Low: " + toString<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); + L"High: " + numberTo<std::wstring>(localTime.dwHighDateTime) + L" " + + L"Low: " + numberTo<std::wstring>(localTime.dwLowDateTime) + L") " + L"\n\n" + getLastErrorFormatted(); throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); } return utcTime; @@ -214,7 +216,7 @@ const size_t UTC_LOCAL_OFFSET_BITS = 7; const size_t WRITE_TIME_HASH_BITS = CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS - UTC_LOCAL_OFFSET_BITS; -template <size_t precision> +template <size_t precision> inline FILETIME encodeRawInformation(UInt64 rawInfo) { rawInfo *= precision; @@ -225,7 +227,7 @@ FILETIME encodeRawInformation(UInt64 rawInfo) } -template <size_t precision> +template <size_t precision> inline UInt64 extractRawInformation(const FILETIME& createTime) { assert(toUInt64(FAT_MIN_TIME) <= toUInt64(createTime)); @@ -243,6 +245,7 @@ UInt64 extractRawInformation(const FILETIME& createTime) //convert write time to it's minimal representation (no restriction to FAT range "1980 - 2107") +inline UInt64 extractRawWriteTime(const FILETIME& writeTime) { UInt64 rawInfo = toUInt64(writeTime); @@ -253,6 +256,7 @@ UInt64 extractRawWriteTime(const FILETIME& writeTime) //files with different resolution than 2 seconds are rounded up when written to FAT +inline FILETIME roundToFatWriteTime(const FILETIME& writeTime) { UInt64 rawData = toUInt64(writeTime); @@ -284,7 +288,7 @@ std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift() timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity! { const std::wstring errorMessage = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + - L"(" + toString<std::wstring>(timeShiftSec) + L") " + L"\n\n" + getLastErrorFormatted(); + L"(" + numberTo<std::wstring>(timeShiftSec) + L") " + L"\n\n" + getLastErrorFormatted(); throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); } diff --git a/zen/error_log.h b/zen/error_log.h new file mode 100644 index 00000000..f3f67233 --- /dev/null +++ b/zen/error_log.h @@ -0,0 +1,133 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ERRORLOGGING_H_INCLUDED +#define ERRORLOGGING_H_INCLUDED + +#include <algorithm> +#include <vector> +#include <string> +#include <zen/time.h> +#include <zen/i18n.h> + +namespace zen +{ +enum MessageType +{ + TYPE_INFO = 1, + TYPE_WARNING = 2, + TYPE_ERROR = 4, + TYPE_FATAL_ERROR = 8, +}; + +struct LogEntry +{ + time_t time; + MessageType type; + std::wstring message; +}; + +std::wstring formatMessage(const LogEntry& msg); + + +class ErrorLog +{ +public: + void logMsg(const std::wstring& message, MessageType type); + + int getItemCount(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const; + + const std::vector<LogEntry>& getEntries() const { return logEntries; } + +private: + std::vector<LogEntry> logEntries; //list of non-resolved errors and warnings +}; + + + + + + + + + + + + + + + + + + + +//######################## implementation ########################## + +inline +void ErrorLog::logMsg(const std::wstring& message, zen::MessageType type) +{ + const LogEntry newEntry = { std::time(nullptr), type, message }; + logEntries.push_back(newEntry); +} + + +inline +int ErrorLog::getItemCount(int typeFilter) const +{ + return static_cast<int>(std::count_if(logEntries.begin(), logEntries.end(), [&](const LogEntry& e) { return e.type & typeFilter; })); +} + + +namespace +{ +std::wstring formatMessageImpl(const LogEntry& entry) //internal linkage +{ + auto getTypeName = [&]() -> std::wstring + { + switch (entry.type) + { + case TYPE_INFO: + return _("Info"); + case TYPE_WARNING: + return _("Warning"); + case TYPE_ERROR: + return _("Error"); + case TYPE_FATAL_ERROR: + return _("Fatal Error"); + } + return std::wstring(); + }; + + std::wstring formattedText = L"[" + formatTime<std::wstring>(FORMAT_TIME, localTime(entry.time)) + L"] " + getTypeName() + L": "; + const size_t prefixLen = formattedText.size(); + + for (auto iter = entry.message.begin(); iter != entry.message.end(); ) + if (*iter == L'\n') + { + formattedText += L'\n'; + + std::wstring blanks; + blanks.resize(prefixLen, L' '); + formattedText += blanks; + + do //skip duplicate newlines + { + ++iter; + } + while (iter != entry.message.end() && *iter == L'\n'); + } + else + formattedText += *iter++; + + return formattedText; +} +} + +inline std::wstring formatMessage(const LogEntry& entry) { return formatMessageImpl(entry); } + +} + +#endif // ERRORLOGGING_H_INCLUDED diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index dd5276a4..a81b3a80 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -31,7 +31,6 @@ #include <sys/stat.h> #include <time.h> #include <utime.h> -#include <cerrno> #include <sys/time.h> #ifdef HAVE_SELINUX @@ -89,7 +88,8 @@ bool zen::symlinkExists(const Zstring& objname) bool zen::somethingExists(const Zstring& objname) //throw() check whether any object with this name exists { #ifdef FFS_WIN - return ::GetFileAttributes(applyLongPathPrefix(objname).c_str()) != INVALID_FILE_ATTRIBUTES; + const DWORD rv = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); + return rv != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys" #elif defined FFS_LINUX struct stat fileInfo = {}; @@ -142,10 +142,10 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); + nullptr); if (hFile == INVALID_HANDLE_VALUE) throw FileError(_("Error reading file attributes:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hFile)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) @@ -211,12 +211,12 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error! DWORD volumeSerial = 0; if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - NULL, //__out LPTSTR lpVolumeNameBuffer, + nullptr, //__out LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, - NULL, //__out LPTSTR lpFileSystemNameBuffer, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize return 0; @@ -260,34 +260,27 @@ zen::ResponseSame zen::onSameVolume(const Zstring& folderLeft, const Zstring& fo bool zen::removeFile(const Zstring& filename) //throw FileError; { #ifdef FFS_WIN - //remove file, support for \\?\-prefix - const Zstring filenameFmt = applyLongPathPrefix(filename); + const Zstring& filenameFmt = applyLongPathPrefix(filename); if (!::DeleteFile(filenameFmt.c_str())) #elif defined FFS_LINUX if (::unlink(filename.c_str()) != 0) #endif { + ErrorCode lastError = getLastError(); + if (errorCodeForNotExisting(lastError)) //no error situation if file is not existing! manual deletion relies on it! + return false; #ifdef FFS_WIN - //perf: apply ONLY when necessary! - if (::GetLastError() == ERROR_ACCESS_DENIED) //function fails if file is read-only + else if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only { - //(try to) normalize file attributes - ::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL); + ::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes - //now try again... - if (::DeleteFile(filenameFmt.c_str())) + if (::DeleteFile(filenameFmt.c_str())) //now try again... return true; + lastError = ::GetLastError(); } - //eval error code before next call - DWORD lastError = ::GetLastError(); -#elif defined FFS_LINUX - int lastError = errno; #endif - - //no error situation if file is not existing! manual deletion relies on it! - //perf: check is placed in error handling block - //warning: this call changes error code!! - if (!somethingExists(filename)) + //after "lastError" evaluation it *may* be redundant to check existence again, but better be safe than sorry: + if (!somethingExists(filename)) //warning: changes global error code!! return false; //neither file nor any other object (e.g. broken symlink) with that name existing throw FileError(_("Error deleting file:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted(lastError)); @@ -383,7 +376,7 @@ Zstring getFilenameFmt(const Zstring& filename, Function fun) //throw(); returns { const Zstring filenameFmt = applyLongPathPrefix(filename); - const DWORD bufferSize = fun(filenameFmt.c_str(), NULL, 0); + const DWORD bufferSize = fun(filenameFmt.c_str(), nullptr, 0); if (bufferSize == 0) return Zstring(); @@ -407,11 +400,12 @@ Zstring findUnused8Dot3Name(const Zstring& filename) //find a unique 8.3 short n Zstring extension = afterLast(afterLast(filename, FILE_NAME_SEPARATOR), Zchar('.')); //extension needn't contain reasonable data if (extension.empty()) extension = Zstr("FFS"); - truncate(extension, 3); + else if (extension.length() > 3) + extension.resize(3); for (int index = 0; index < 100000000; ++index) //filename must be representable by <= 8 characters { - const Zstring output = pathPrefix + toString<Zstring>(index) + Zchar('.') + extension; + const Zstring output = pathPrefix + numberTo<Zstring>(index) + Zchar('.') + extension; if (!somethingExists(output)) //ensure uniqueness return output; } @@ -559,7 +553,7 @@ void zen::moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ig copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions else { - std::unique_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(sourceFile, targetFile, *callback) : NULL); + std::unique_ptr<CopyCallbackImpl> copyCallback(callback ? new CopyCallbackImpl(sourceFile, targetFile, *callback) : nullptr); copyFile(sourceFile, targetFile, false, true, copyCallback.get()); //throw FileError; } @@ -687,7 +681,7 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool } //delete source - std::unique_ptr<RemoveCallbackImpl> removeCallback(callback != NULL ? new RemoveCallbackImpl(*callback) : NULL); + std::unique_ptr<RemoveCallbackImpl> removeCallback(callback ? new RemoveCallbackImpl(*callback) : nullptr); removeDirectory(sourceDir, removeCallback.get()); //throw FileError; if (callback) callback->objectProcessed(); @@ -779,32 +773,32 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) std::vector<Zstring> fileList; std::vector<Zstring> dirList; + { + //get all files and directories from current directory (WITHOUT subdirectories!) + FilesDirsOnlyTraverser traverser(fileList, dirList); + traverseFolder(directory, false, traverser); //don't follow symlinks + } - //get all files and directories from current directory (WITHOUT subdirectories!) - FilesDirsOnlyTraverser traverser(fileList, dirList); - traverseFolder(directory, false, traverser); //don't follow symlinks + //delete directories recursively + for (auto iter = dirList.begin(); iter != dirList.end(); ++iter) + removeDirectory(*iter, callback); //call recursively to correctly handle symbolic links //delete files - for (std::vector<Zstring>::const_iterator i = fileList.begin(); i != fileList.end(); ++i) + for (auto iter = fileList.begin(); iter != fileList.end(); ++iter) { - const bool workDone = removeFile(*i); + const bool workDone = removeFile(*iter); if (callback && workDone) - callback->notifyFileDeletion(*i); //call once per file + callback->notifyFileDeletion(*iter); //call once per file } - //delete directories recursively - for (std::vector<Zstring>::const_iterator i = dirList.begin(); i != dirList.end(); ++i) - removeDirectory(*i, callback); //call recursively to correctly handle symbolic links - //parent directory is deleted last #ifdef FFS_WIN - if (!::RemoveDirectory(directoryFmt.c_str())) //remove directory, support for \\?\-prefix + if (!::RemoveDirectory(directoryFmt.c_str())) #else if (::rmdir(directory.c_str()) != 0) #endif - { throw FileError(_("Error deleting directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + getLastErrorFormatted()); - } + if (callback) callback->notifyDirDeletion(directory); //and once per folder } @@ -848,7 +842,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //process symlinks - NULL); + nullptr); }); if (targetHandle.get() == INVALID_HANDLE_VALUE) @@ -865,8 +859,8 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr auto isNullTime = [](const FILETIME & ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; }; if (!::SetFileTime(targetHandle.get(), - isNullTime(creationTime) ? NULL : &creationTime, - NULL, + isNullTime(creationTime) ? nullptr : &creationTime, + nullptr, &lastWriteTime)) throw FileError(_("Error changing modification time:") + L"\n\"" + filename + L"\"" + L"\n\n" + getLastErrorFormatted()); @@ -887,7 +881,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr if (procSl == SYMLINK_FOLLOW) { struct utimbuf newTimes = {}; - newTimes.actime = ::time(NULL); + newTimes.actime = ::time(nullptr); newTimes.modtime = to<time_t>(modificationTime); // set new "last write time" @@ -897,7 +891,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr else { struct timeval newTimes[2] = {}; - newTimes[0].tv_sec = ::time(NULL); /* seconds */ + newTimes[0].tv_sec = ::time(nullptr); /* seconds */ newTimes[0].tv_usec = 0; /* microseconds */ newTimes[1].tv_sec = to<time_t>(modificationTime); @@ -913,43 +907,65 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr bool zen::supportsPermissions(const Zstring& dirname) //throw FileError { #ifdef FFS_WIN - const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(dirname).c_str(), - 0, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, // | FILE_FLAG_OPEN_REPARSE_POINT -> follow symlinks - NULL); - if (hDir == INVALID_HANDLE_VALUE) + std::vector<wchar_t> buffer(MAX_PATH + 1); + if (!::GetVolumePathName(dirname.c_str(), //__in LPCTSTR lpszFileName, + &buffer[0], //__out LPTSTR lpszVolumePathName, + static_cast<DWORD>(buffer.size()))) //__in DWORD cchBufferLength throw FileError(_("Error reading file attributes:") + L"\n\"" + dirname + L"\"" + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir)); - - //dynamically load windows API function (existing since Windows XP) - typedef BOOL (WINAPI* GetVolumeInformationByHandleWFun)(HANDLE hFile, - LPWSTR lpVolumeNameBuffer, - DWORD nVolumeNameSize, - LPDWORD lpVolumeSerialNumber, - LPDWORD lpMaximumComponentLength, - LPDWORD lpFileSystemFlags, - LPWSTR lpFileSystemNameBuffer, - DWORD nFileSystemNameSize); - - const SysDllFun<GetVolumeInformationByHandleWFun> getVolumeInformationByHandleW(L"kernel32.dll", "GetVolumeInformationByHandleW"); - if (!getVolumeInformationByHandleW) - throw FileError(_("Error loading library function:") + L"\n\"" + L"GetVolumeInformationByHandleW" + L"\""); - - DWORD fileSystemFlags = 0; - if (!getVolumeInformationByHandleW(hDir, //__in HANDLE hFile, - NULL, //__out_opt LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - &fileSystemFlags, //__out_opt LPDWORD lpFileSystemFlags, - NULL, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize + + DWORD fsFlags = 0; + if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName, + nullptr, //__out LPTSTR lpVolumeNameBuffer, + 0, //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize throw FileError(_("Error reading file attributes:") + L"\n\"" + dirname + L"\"" + L"\n\n" + getLastErrorFormatted()); - return (fileSystemFlags & FILE_PERSISTENT_ACLS) != 0; + return (fsFlags & FILE_PERSISTENT_ACLS) != 0; + + + // -> the following approach is *only* working since Windows Vista: + // const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(dirname).c_str(), + // 0, + // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + // nullptr, + // OPEN_EXISTING, + // FILE_FLAG_BACKUP_SEMANTICS, // | FILE_FLAG_OPEN_REPARSE_POINT -> follow symlinks + // nullptr); + // if (hDir == INVALID_HANDLE_VALUE) + // throw FileError(_("Error reading file attributes:") + L"\n\"" + dirname + L"\"" + L"\n\n" + getLastErrorFormatted()); + // ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + // + // //dynamically load windows API function (existing since Windows XP) + // typedef BOOL (WINAPI* GetVolumeInformationByHandleWFun)(HANDLE hFile, + // LPWSTR lpVolumeNameBuffer, + // DWORD nVolumeNameSize, + // LPDWORD lpVolumeSerialNumber, + // LPDWORD lpMaximumComponentLength, + // LPDWORD lpFileSystemFlags, + // LPWSTR lpFileSystemNameBuffer, + // DWORD nFileSystemNameSize); + // + // const SysDllFun<GetVolumeInformationByHandleWFun> getVolumeInformationByHandleW(L"kernel32.dll", "GetVolumeInformationByHandleW"); //available since Windows Vista + // if (!getVolumeInformationByHandleW) + // return true; //Windows XP, 2000 -> do not show this error message + // //throw FileError(rror loading library function+ L"\n\"" + L"GetVolumeInformationByHandleW" + L"\""); + // + // DWORD fileSystemFlags = 0; + // if (!getVolumeInformationByHandleW(hDir, //__in HANDLE hFile, + // nullptr, //__out_opt LPTSTR lpVolumeNameBuffer, + // 0, //__in DWORD nVolumeNameSize, + // nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + // nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + // &fileSystemFlags, //__out_opt LPDWORD lpFileSystemFlags, + // nullptr, //__out LPTSTR lpFileSystemNameBuffer, + // 0)) //__in DWORD nFileSystemNameSize + // throw FileError(_("Error reading file attributes:") + L"\n\"" + dirname + L"\"" + L"\n\n" + getLastErrorFormatted()); + // + // return (fileSystemFlags & FILE_PERSISTENT_ACLS) != 0; #elif defined FFS_LINUX return true; @@ -969,10 +985,10 @@ Zstring getSymlinkTargetPath(const Zstring& symlink) //throw FileError 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory - NULL); + nullptr); if (hDir == INVALID_HANDLE_VALUE) throw FileError(_("Error resolving symbolic link:") + L"\n\"" + symlink + L"\"" + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); //dynamically load windows API function typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, @@ -1006,7 +1022,7 @@ Zstring getSymlinkTargetPath(const Zstring& symlink) //throw FileError //copy SELinux security context void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError { - security_context_t contextSource = NULL; + security_context_t contextSource = nullptr; const int rv = procSl == SYMLINK_FOLLOW ? ::getfilecon(source.c_str(), &contextSource) : ::lgetfilecon(source.c_str(), &contextSource); @@ -1018,10 +1034,10 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli throw FileError(_("Error reading security context:") + L"\n\"" + source + L"\"" + L"\n\n" + getLastErrorFormatted()); } - ZEN_ON_BLOCK_EXIT(::freecon(contextSource)); + ZEN_ON_SCOPE_EXIT(::freecon(contextSource)); { - security_context_t contextTarget = NULL; + security_context_t contextTarget = nullptr; const int rv2 = procSl == SYMLINK_FOLLOW ? ::getfilecon(target.c_str(), &contextTarget) : ::lgetfilecon(target.c_str(), &contextTarget); @@ -1033,7 +1049,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli } else { - ZEN_ON_BLOCK_EXIT(::freecon(contextTarget)); + ZEN_ON_SCOPE_EXIT(::freecon(contextTarget)); if (::strcmp(contextSource, contextTarget) == 0) //nothing to do return; @@ -1121,11 +1137,11 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym throw FileError(_("Error copying file permissions:") + L"\n\"" + sourceResolved + L"\" ->\n\"" + targetResolved + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (W)"); /* - PSECURITY_DESCRIPTOR buffer = NULL; - PSID owner = NULL; - PSID group = NULL; - PACL dacl = NULL; - PACL sacl = NULL; + PSECURITY_DESCRIPTOR buffer = nullptr; + PSID owner = nullptr; + PSID group = nullptr; + PACL dacl = nullptr; + PACL sacl = nullptr; //File Security and Access Rights: http://msdn.microsoft.com/en-us/library/aa364399(v=VS.85).aspx //SECURITY_INFORMATION Access Rights: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379573(v=vs.85).aspx @@ -1135,10 +1151,10 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory - NULL); + nullptr); if (hSource == INVALID_HANDLE_VALUE) throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted() + L" (OR)"); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hSource)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource)); // DWORD rc = ::GetNamedSecurityInfo(const_cast<WCHAR*>(applyLongPathPrefix(source).c_str()), -> does NOT dereference symlinks! DWORD rc = ::GetSecurityInfo(hSource, //__in LPTSTR pObjectName, @@ -1152,7 +1168,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym &buffer); //__out_opt PSECURITY_DESCRIPTOR *ppSecurityDescriptor if (rc != ERROR_SUCCESS) throw FileError(_("Error copying file permissions:") + L"\n\"" + source + L"\" ->\n\"" + target + L"\"" + L"\n\n" + getLastErrorFormatted(rc) + L" (R)"); - ZEN_ON_BLOCK_EXIT(::LocalFree(buffer)); + ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; { @@ -1172,7 +1188,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym 0, // lpSecurityAttributes OPEN_EXISTING, // dwCreationDisposition FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), // dwFlagsAndAttributes - NULL); // hTemplateFile + nullptr); // hTemplateFile }); if (targetHandle.get() == INVALID_HANDLE_VALUE) @@ -1233,7 +1249,7 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!) - if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), NULL)) + if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr)) #elif defined FFS_LINUX if (::mkdir(directory.c_str(), 0755) != 0) #endif @@ -1282,10 +1298,10 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); + nullptr); if (hDir != INVALID_HANDLE_VALUE) { - ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; @@ -1294,16 +1310,16 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD FSCTL_SET_COMPRESSION, //dwIoControlCode &cmpState, //input buffer sizeof(cmpState), //size of input buffer - NULL, //lpOutBuffer + nullptr, //lpOutBuffer 0, //OutBufferSize &bytesReturned, //number of bytes returned - NULL); //OVERLAPPED structure + nullptr); //OVERLAPPED structure } } } } #endif - zen::ScopeGuard guardNewDir = zen::makeGuard([&]() { removeDirectory(directory); }); //ensure cleanup: + zen::ScopeGuard guardNewDir = zen::makeGuard([&] { removeDirectory(directory); }); //ensure cleanup: //enforce copying file permissions: it's advertized on GUI... if (copyFilePermissions) @@ -1333,7 +1349,7 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat if (dirExists(directory)) return; #endif - else //if "somethingExists" we needn't create the parent directory + else //if "not somethingExists" we need to create the parent directory { //try to create parent folders first const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR); @@ -1399,7 +1415,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool throw FileError(_("Error copying symbolic link:") + L"\n\"" + sourceLink + L"\" ->\n\"" + targetLink + L"\"" + L"\n\n" + getLastErrorFormatted()); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist - zen::ScopeGuard guardNewDir = zen::makeGuard([&]() + zen::ScopeGuard guardNewDir = zen::makeGuard([&] { #ifdef FFS_WIN if (isDirLink) @@ -1430,7 +1446,7 @@ Zstring createTempName(const Zstring& filename) //ensure uniqueness for (int i = 1; somethingExists(output); ++i) - output = filename + Zchar('_') + toString<Zstring>(i) + zen::TEMP_FILE_ENDING; + output = filename + Zchar('_') + numberTo<Zstring>(i) + zen::TEMP_FILE_ENDING; return output; } @@ -1439,20 +1455,17 @@ Zstring createTempName(const Zstring& filename) class CallbackData { public: - CallbackData(CallbackCopyFile* cb, //may be NULL + CallbackData(CallbackCopyFile* cb, //may be nullptr const Zstring& sourceFile, - const Zstring& targetFile, - bool osIsvistaOrLater) : + const Zstring& targetFile) : userCallback(cb), sourceFile_(sourceFile), targetFile_(targetFile), - osIsvistaOrLater_(osIsvistaOrLater), exceptionInUserCallback(false) {} CallbackCopyFile* userCallback; //optional! const Zstring& sourceFile_; const Zstring& targetFile_; - const bool osIsvistaOrLater_; //there is mixed responsibility in this class, pure read-only data and abstraction for error reporting //however we need to keep it together as ::CopyFileEx() requires! @@ -1526,7 +1539,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, CallbackData& cbd = *static_cast<CallbackData*>(lpData); if (dwCallbackReason == CALLBACK_STREAM_SWITCH && //called up-front for every file (even if 0-sized) - dwStreamNumber == 1) //ADS! + dwStreamNumber == 1) //consider ADS! { //#################### return source file attributes ################################ BY_HANDLE_FILE_INFORMATION fileInfoSrc = {}; @@ -1556,14 +1569,14 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, if (!::GetFileTime(hSourceFile, //__in HANDLE hFile, &creationTime, //__out_opt LPFILETIME lpCreationTime, - NULL, //__out_opt LPFILETIME lpLastAccessTime, - NULL)) //__out_opt LPFILETIME lpLastWriteTime + nullptr, //__out_opt LPFILETIME lpLastAccessTime, + nullptr)) //__out_opt LPFILETIME lpLastWriteTime { cbd.reportError(_("Error reading file attributes:") + L"\n\"" + cbd.sourceFile_ + L"\"" + L"\n\n" + getLastErrorFormatted()); return PROGRESS_CANCEL; } - ::SetFileTime(hDestinationFile, &creationTime, NULL, NULL); //no error handling! + ::SetFileTime(hDestinationFile, &creationTime, nullptr, nullptr); //no error handling! //############################################################################## } @@ -1574,11 +1587,11 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, { //some odd check for some possible(?) error condition if (totalBytesTransferred.QuadPart < 0) //let's see if someone answers the call... - ::MessageBox(NULL, L"You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\ + ::MessageBox(nullptr, L"You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\ Please write a mail to the author of FreeFileSync at zhnmju123@gmx.de and simply state that\n\ \"totalBytesTransferred.HighPart can be below zero\"!\n\n\ This will then be handled in future versions of FreeFileSync.\n\nThanks -ZenJu", - NULL, 0); + nullptr, 0); try { cbd.userCallback->updateCopyStatus(UInt64(totalBytesTransferred.QuadPart)); @@ -1604,7 +1617,8 @@ void rawCopyWinApi_sub(const Zstring& sourceFile, CallbackCopyFile* callback, FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked { - zen::ScopeGuard guardTarget = zen::makeGuard([&]() { removeFile(targetFile); }); //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we ;) + zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); + //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;) DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; @@ -1612,25 +1626,19 @@ void rawCopyWinApi_sub(const Zstring& sourceFile, static bool nonEncSupported = false; { static boost::once_flag initNonEncOnce = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010! - boost::call_once(initNonEncOnce, []() { nonEncSupported = winXpOrLater(); }); //encrypted destination is not supported with Windows 2000 + boost::call_once(initNonEncOnce, [] { nonEncSupported = winXpOrLater(); }); //encrypted destination is not supported with Windows 2000 } if (nonEncSupported) copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; - static bool osIsvistaOrLater = false; - { - static boost::once_flag initVistaLaterOnce = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010! - boost::call_once(initVistaLaterOnce, []() { osIsvistaOrLater = vistaOrLater(); }); - } - - CallbackData cbd(callback, sourceFile, targetFile, osIsvistaOrLater); + CallbackData cbd(callback, sourceFile, targetFile); const bool success = ::CopyFileEx( //same performance like CopyFile() applyLongPathPrefix(sourceFile).c_str(), applyLongPathPrefix(targetFile).c_str(), copyCallbackInternal, &cbd, - NULL, + nullptr, copyFlags) == TRUE; //silence x64 perf warning cbd.evaluateErrors(); //throw ?, process errors in callback first! @@ -1725,8 +1733,8 @@ void rawCopyWinApi(const Zstring& sourceFile, // /* // BackupRead() FileRead() CopyFileEx() // -------------------------------------------- -// Attributes NO NO YES -// create time NO NO NO +// Attributes NO NO YES +// create time NO NO NO // ADS YES NO YES // Encrypted NO(silent fail) NO YES // Compressed NO NO NO @@ -1737,18 +1745,20 @@ void rawCopyWinApi(const Zstring& sourceFile, // compatible with: BackupRead() FileRead() // */ // +//FILE_FLAG_BACKUP_SEMANTICS ?????? +// // //open sourceFile for reading // HANDLE hFileIn = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), // GENERIC_READ, // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //all shared modes are required to read files that are open in other applications -// 0, +// nullptr, // OPEN_EXISTING, // FILE_FLAG_SEQUENTIAL_SCAN, -// NULL); +// nullptr); // if (hFileIn == INVALID_HANDLE_VALUE) // { // const DWORD lastError = ::GetLastError(); -// const std::wstring& errorMessage = _("Error opening file:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted(lastError); +// const std::wstring& errorMessage = Error opening file: + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted(lastError); // // //if file is locked (try to) use Windows Volume Shadow Copy Service // if (lastError == ERROR_SHARING_VIOLATION || @@ -1757,12 +1767,12 @@ void rawCopyWinApi(const Zstring& sourceFile, // // throw FileError(errorMessage); // } -// ZEN_ON_BLOCK_EXIT(::CloseHandle, hFileIn); +// ZEN_ON_SCOPE_EXIT(::CloseHandle, hFileIn); // // // BY_HANDLE_FILE_INFORMATION infoFileIn = {}; // if (!::GetFileInformationByHandle(hFileIn, &infoFileIn)) -// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); +// throw FileError(Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); // // //####################################### DST hack ########################################### // if (dst::isFatDrive(sourceFile)) //throw() @@ -1794,15 +1804,14 @@ void rawCopyWinApi(const Zstring& sourceFile, // HANDLE hFileOut = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), // GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, -// 0, +// nullptr, // CREATE_NEW, // (infoFileIn.dwFileAttributes & validAttribs) | FILE_FLAG_SEQUENTIAL_SCAN, -// NULL); +// nullptr); // if (hFileOut == INVALID_HANDLE_VALUE) // { // const DWORD lastError = ::GetLastError(); -// const std::wstring& errorMessage = _("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + getLastErrorFormatted(lastError); +// const std::wstring& errorMessage = // // if (lastError == ERROR_FILE_EXISTS) // throw ErrorTargetExisting(errorMessage); @@ -1814,7 +1823,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // } // Loki::ScopeGuard guardTarget = Loki::MakeGuard(&removeFile, targetFile); //transactional behavior: guard just after opening target and before managing hFileOut // -// ZEN_ON_BLOCK_EXIT(::CloseHandle, hFileOut); +// ZEN_ON_SCOPE_EXIT(::CloseHandle, hFileOut); // // //#ifndef _MSC_VER @@ -1822,14 +1831,14 @@ void rawCopyWinApi(const Zstring& sourceFile, //#endif // DWORD fsFlags = 0; // if (!GetVolumeInformationByHandleW(hFileOut, //__in HANDLE hFile, -// NULL, //__out_opt LPTSTR lpVolumeNameBuffer, +// nullptr, //__out_opt LPTSTR lpVolumeNameBuffer, // 0, //__in DWORD nVolumeNameSize, -// NULL, //__out_opt LPDWORD lpVolumeSerialNumber, -// NULL, //__out_opt LPDWORD lpMaximumComponentLength, +// nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, +// nullptr, //__out_opt LPDWORD lpMaximumComponentLength, // &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, -// NULL, //__out LPTSTR lpFileSystemNameBuffer, +// nullptr, //__out LPTSTR lpFileSystemNameBuffer, // 0)) //__in DWORD nFileSystemNameSize -// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); +// throw FileError(Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); // // const bool sourceIsEncrypted = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; // const bool sourceIsCompressed = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; @@ -1851,12 +1860,11 @@ void rawCopyWinApi(const Zstring& sourceFile, // FSCTL_SET_COMPRESSION, //dwIoControlCode // &cmpState, //input buffer // sizeof(cmpState), //size of input buffer -// NULL, //lpOutBuffer +// nullptr, //lpOutBuffer // 0, //OutBufferSize // &bytesReturned, //number of bytes returned -// NULL)) //OVERLAPPED structure -// throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + getLastErrorFormatted() + +// nullptr)) //OVERLAPPED structure +// throw FileError( ddd + // "\nFailed to write NTFS compressed attribute!"); // } // @@ -1868,14 +1876,13 @@ void rawCopyWinApi(const Zstring& sourceFile, // DWORD bytesReturned = 0; // if (!DeviceIoControl(hFileOut, //handle to file // FSCTL_SET_SPARSE, //dwIoControlCode -// NULL, //input buffer +// nullptr, //input buffer // 0, //size of input buffer -// NULL, //lpOutBuffer +// nullptr, //lpOutBuffer // 0, //OutBufferSize // &bytesReturned, //number of bytes returned -// NULL)) //OVERLAPPED structure -// throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + getLastErrorFormatted() + +// nullptr)) //OVERLAPPED structure +// throw FileError(dddd // "\nFailed to write NTFS sparse attribute!"); // } // } @@ -1889,13 +1896,13 @@ void rawCopyWinApi(const Zstring& sourceFile, // // struct ManageCtxt //manage context for BackupRead()/BackupWrite() // { -// ManageCtxt() : read(NULL), write(NULL) {} +// ManageCtxt() : read(nullptr), write(nullptr) {} // ~ManageCtxt() // { -// if (read != NULL) -// ::BackupRead (0, NULL, 0, NULL, true, false, &read); -// if (write != NULL) -// ::BackupWrite(0, NULL, 0, NULL, true, false, &write); +// if (read != nullptr) +// ::BackupRead (0, nullptr, 0, nullptr, true, false, &read); +// if (write != nullptr) +// ::BackupWrite(0, nullptr, 0, nullptr, true, false, &write); // } // // LPVOID read; @@ -1919,19 +1926,19 @@ void rawCopyWinApi(const Zstring& sourceFile, // false, //__in BOOL bAbort, // false, //__in BOOL bProcessSecurity, // &context.read)) //__out LPVOID *lpContext -// throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + +// throw FileError(Error reading file:") + "\n\"" + sourceFile + "\"" + // "\n\n" + getLastErrorFormatted()); // } // else if (!::ReadFile(hFileIn, //__in HANDLE hFile, // &buffer[0], //__out LPVOID lpBuffer, // BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, // &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, -// NULL)) //__inout_opt LPOVERLAPPED lpOverlapped -// throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + +// nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped +// throw FileError(Error reading file:") + "\n\"" + sourceFile + "\"" + // "\n\n" + getLastErrorFormatted()); // // if (bytesRead > BUFFER_SIZE) -// throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + +// throw FileError(Error reading file:") + "\n\"" + sourceFile + "\"" + // "\n\n" + "buffer overflow"); // // if (bytesRead < BUFFER_SIZE) @@ -1948,19 +1955,17 @@ void rawCopyWinApi(const Zstring& sourceFile, // false, //__in BOOL bAbort, // false, //__in BOOL bProcessSecurity, // &context.write)) //__out LPVOID *lpContext -// throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! +// throw FileError(ddd" (w)"); //w -> distinguish from fopen error message! // } // else if (!::WriteFile(hFileOut, //__in HANDLE hFile, // &buffer[0], //__out LPVOID lpBuffer, // bytesRead, //__in DWORD nNumberOfBytesToWrite, // &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, -// NULL)) //__inout_opt LPOVERLAPPED lpOverlapped -// throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! +// nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped +// throw FileError(ddd" (w)"); //w -> distinguish from fopen error message! // // if (bytesWritten != bytesRead) -// throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + "\n\n" + "incomplete write"); +// throw FileError(ddd + "incomplete write"); // // totalBytesTransferred += bytesRead; // @@ -1969,15 +1974,15 @@ void rawCopyWinApi(const Zstring& sourceFile, //#endif // // //invoke callback method to update progress indicators -// if (callback != NULL) +// if (callback != nullptr) // switch (callback->updateCopyStatus(totalBytesTransferred)) // { // case CallbackCopyFile::CONTINUE: // break; // // case CallbackCopyFile::CANCEL: //a user aborted operation IS an error condition! -// throw FileError(_("Error copying file:") + "\n\"" + sourceFile + "\" ->\n\"" + -// targetFile + "\"\n\n" + _("Operation aborted!")); +// throw FileError(Error copying file:") + "\n\"" + sourceFile + "\" ->\n\"" + +// targetFile + "\"\n\n" + Operation aborted!")); // } // } // while (!eof); @@ -1987,18 +1992,18 @@ void rawCopyWinApi(const Zstring& sourceFile, // { // LARGE_INTEGER inputSize = {}; // if (!::GetFileSizeEx(hFileIn, &inputSize)) -// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); +// throw FileError(Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); // // if (inputSize.QuadPart != 0) -// throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + "\n\n" + "unknown error"); +// throw FileError(Error reading file:") + "\n\"" + sourceFile + "\"" + "\n\n" + "unknown error"); // } // // //time needs to be set at the end: BackupWrite() changes file time // if (!::SetFileTime(hFileOut, // &infoFileIn.ftCreationTime, -// NULL, +// nullptr, // &infoFileIn.ftLastWriteTime)) -// throw FileError(_("Error changing modification time:") + "\n\"" + targetFile + "\"" + "\n\n" + getLastErrorFormatted()); +// throw FileError(Error changing modification time:") + "\n\"" + targetFile + "\"" + "\n\n" + getLastErrorFormatted()); // // //#ifndef NDEBUG //dst hack: verify data written @@ -2024,15 +2029,15 @@ void rawCopyWinApi(const Zstring& sourceFile, // 0, // CREATE_NEW, // FILE_FLAG_SEQUENTIAL_SCAN, -// NULL); +// nullptr); // DWORD br = 0; -// if (!::DeviceIoControl(hSparse, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &br,NULL)) +// if (!::DeviceIoControl(hSparse, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &br,nullptr)) // throw 1; // // LARGE_INTEGER liDistanceToMove = {}; // liDistanceToMove.QuadPart = 1024 * 1024 * 1024; //create 5 TB sparse file // liDistanceToMove.QuadPart *= 5 * 1024; // -// if (!::SetFilePointerEx(hSparse, liDistanceToMove, NULL, FILE_BEGIN)) +// if (!::SetFilePointerEx(hSparse, liDistanceToMove, nullptr, FILE_BEGIN)) // throw 1; // // if (!SetEndOfFile(hSparse)) @@ -2040,7 +2045,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // // FILE_ZERO_DATA_INFORMATION zeroInfo = {}; // zeroInfo.BeyondFinalZero.QuadPart = liDistanceToMove.QuadPart; -// if (!::DeviceIoControl(hSparse, FSCTL_SET_ZERO_DATA, &zeroInfo, sizeof(zeroInfo), NULL, 0, &br, NULL)) +// if (!::DeviceIoControl(hSparse, FSCTL_SET_ZERO_DATA, &zeroInfo, sizeof(zeroInfo), nullptr, 0, &br, nullptr)) // throw 1; // // ::CloseHandle(hSparse); @@ -2054,7 +2059,7 @@ void rawCopyStream(const Zstring& sourceFile, CallbackCopyFile* callback, FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting { - zen::ScopeGuard guardTarget = zen::makeGuard([&] { removeFile(targetFile); }); //transactional behavior: place guard before lifetime of FileOutput + zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput try { //open sourceFile for reading diff --git a/zen/file_handling.h b/zen/file_handling.h index 80350731..b0d97a6c 100644 --- a/zen/file_handling.h +++ b/zen/file_handling.h @@ -48,7 +48,7 @@ UInt64 getFilesize(const Zstring& filename); //throw FileError //file handling bool removeFile(const Zstring& filename); //return "true" if file was actually deleted; throw FileError -void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = NULL); //throw FileError +void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = nullptr); //throw FileError //rename file or directory: no copying!!! @@ -80,8 +80,8 @@ void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissi const Zstring& targetFile, //symlink handling: dereference source bool copyFilePermissions, bool transactionalCopy, - CallbackCopyFile* callback, //may be NULL - FileAttrib* newAttrib = NULL); //return current attributes at the time of copy + CallbackCopyFile* callback, //may be nullptr + FileAttrib* newAttrib = nullptr); //return current attributes at the time of copy //Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop. // => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: const Zstring TEMP_FILE_ENDING = Zstr(".ffs_tmp"); diff --git a/zen/file_id.cpp b/zen/file_id.cpp index 4f9e3600..40efa373 100644 --- a/zen/file_id.cpp +++ b/zen/file_id.cpp @@ -27,13 +27,13 @@ zen::FileId zen::getFileID(const Zstring& filename) const HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(filename).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory - NULL); + nullptr); if (hFile != INVALID_HANDLE_VALUE) { - ZEN_ON_BLOCK_EXIT(::CloseHandle(hFile)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfo = {}; if (::GetFileInformationByHandle(hFile, &fileInfo)) diff --git a/zen/file_id_def.h b/zen/file_id_def.h index b65496be..c51a0ecc 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -21,7 +21,7 @@ namespace zen { #ifdef FFS_WIN -typedef std::pair<decltype(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), decltype(ULARGE_INTEGER().QuadPart)> FileId; //(volume serial number, file ID) +typedef std::pair<DWORD, ULONGLONG> FileId; //(volume serial number, file ID) inline FileId extractFileID(const BY_HANDLE_FILE_INFORMATION& fileInfo) @@ -41,16 +41,10 @@ FileId extractFileID(DWORD dwVolumeSerialNumber, ULARGE_INTEGER fileId) FileId(dwVolumeSerialNumber, fileId.QuadPart) : FileId(); } -namespace impl -{ -inline -void validate(const FileId& id, const BY_HANDLE_FILE_INFORMATION& fileInfo) -{ - assert_static(sizeof(id.second) == sizeof(fileInfo.nFileIndexHigh) + sizeof(fileInfo.nFileIndexLow)); - assert_static(sizeof(id.first ) == sizeof(DWORD)); - assert_static(sizeof(id.second) == sizeof(ULARGE_INTEGER)); -} -} +assert_static(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber)); +assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow)); +assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER)); + #elif defined FFS_LINUX namespace impl { typedef struct ::stat StatDummy; } //sigh... @@ -58,7 +52,7 @@ namespace impl { typedef struct ::stat StatDummy; } //sigh... typedef std::pair<decltype(impl::StatDummy::st_dev), decltype(impl::StatDummy::st_ino)> FileId; //(device id, inode) inline -FileId extractFileID(const struct stat& fileInfo) +FileId extractFileID(const struct ::stat& fileInfo) { return fileInfo.st_dev != 0 && fileInfo.st_ino != 0 ? FileId(fileInfo.st_dev, fileInfo.st_ino) : FileId(); diff --git a/zen/file_io.cpp b/zen/file_io.cpp index ad1ecd6b..4c38bb22 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -8,9 +8,6 @@ #ifdef FFS_WIN #include "long_path_prefix.h" - -#elif defined FFS_LINUX -#include <cerrno> #endif using namespace zen; @@ -57,36 +54,21 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis for FFS most comparisons are probably between different disks => let's use FILE_FLAG_SEQUENTIAL_SCAN */ - NULL); + nullptr); if (fileHandle == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); - - std::wstring errorMessage = _("Error opening file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); - - if (lastError == ERROR_FILE_NOT_FOUND || - lastError == ERROR_PATH_NOT_FOUND || - lastError == ERROR_BAD_NETPATH || - lastError == ERROR_NETNAME_DELETED) - throw ErrorNotExisting(errorMessage); - - throw FileError(errorMessage); - } - #elif defined FFS_LINUX fileHandle = ::fopen(filename.c_str(), "r,type=record,noseek"); //utilize UTF-8 filename - if (fileHandle == NULL) + if (!fileHandle) +#endif { - const int lastError = errno; - - std::wstring errorMessage = _("Error opening file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); + const ErrorCode lastError = getLastError(); + std::wstring errorMessage = _("Error reading file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError) + L" (open)"; - if (lastError == ENOENT) + if (errorCodeForNotExisting(lastError)) throw ErrorNotExisting(errorMessage); throw FileError(errorMessage); } -#endif } @@ -95,7 +77,7 @@ FileInput::~FileInput() #ifdef FFS_WIN ::CloseHandle(fileHandle); #elif defined FFS_LINUX - ::fclose(fileHandle); //NEVER allow passing NULL to fclose! -> crash!; fileHandle != NULL in this context! + ::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash!; fileHandle != nullptr in this context! #endif } @@ -108,15 +90,15 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number buffer, //__out LPVOID lpBuffer, static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead, &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, - NULL)) //__inout_opt LPOVERLAPPED lpOverlapped + nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped #elif defined FFS_LINUX const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle); if (::ferror(fileHandle) != 0) #endif - throw FileError(_("Error reading file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted() + L" (r)"); + throw FileError(_("Error reading file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted() + L" (read)"); #ifdef FFS_WIN - if (bytesRead < bytesToRead) //falsify only! + if (bytesRead < bytesToRead) //verify only! #elif defined FFS_LINUX if (::feof(fileHandle) != 0) #endif @@ -153,11 +135,11 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil 0, access == ACC_OVERWRITE ? CREATE_ALWAYS : CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - NULL); + nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { const DWORD lastError = ::GetLastError(); - std::wstring errorMessage = _("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error writing file:") + L"\n\"" + filename_ + L"\"" L"\n\n" + zen::getLastErrorFormatted(lastError); if (lastError == ERROR_FILE_EXISTS) throw ErrorTargetExisting(errorMessage); @@ -172,10 +154,10 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil fileHandle = ::fopen(filename.c_str(), //GNU extension: https://www.securecoding.cert.org/confluence/display/cplusplus/FIO03-CPP.+Do+not+make+assumptions+about+fopen()+and+file+creation access == ACC_OVERWRITE ? "w,type=record,noseek" : "wx,type=record,noseek"); - if (fileHandle == NULL) + if (!fileHandle) { const int lastError = errno; - std::wstring errorMessage = _("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error writing file:") + L"\n\"" + filename_ + L"\"" L"\n\n" + zen::getLastErrorFormatted(lastError); if (lastError == EEXIST) throw ErrorTargetExisting(errorMessage); @@ -193,7 +175,7 @@ FileOutput::~FileOutput() #ifdef FFS_WIN ::CloseHandle(fileHandle); #elif defined FFS_LINUX - ::fclose(fileHandle); //NEVER allow passing NULL to fclose! -> crash! + ::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash! #endif } @@ -206,13 +188,13 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro buffer, //__out LPVOID lpBuffer, static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite, &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, - NULL)) //__inout_opt LPOVERLAPPED lpOverlapped + nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped #elif defined FFS_LINUX const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle); if (::ferror(fileHandle) != 0) #endif - throw FileError(_("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + zen::getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message! + throw FileError(_("Error writing file:") + L"\n\"" + filename_ + L"\"" L"\n\n" + zen::getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message! if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! - throw FileError(_("Error writing file:") + L"\n\"" + filename_ + L"\"" + L"\n\n" + L"incomplete write"); + throw FileError(_("Error writing file:") + L"\n\"" + filename_ + L"\"" L"\n\n" + L"incomplete write"); } diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index a0979c49..c3536825 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -21,7 +21,6 @@ #elif defined FFS_LINUX #include <sys/stat.h> #include <dirent.h> -#include <cerrno> #endif using namespace zen; @@ -67,10 +66,10 @@ bool extractFileInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback:: 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); + nullptr); if (hFile == INVALID_HANDLE_VALUE) return false; - ZEN_ON_BLOCK_EXIT(::CloseHandle(hFile)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); BY_HANDLE_FILE_INFORMATION fileInfoByHandle = {}; if (!::GetFileInformationByHandle(hFile, &fileInfoByHandle)) @@ -92,11 +91,10 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s //- indirection: subst S: %USERPROFILE% // -> GetVolumePathName() on the other hand resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try... - //dynamically load windows API function (existing since Windows XP) typedef BOOL (WINAPI* GetFileInformationByHandleFunc)(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation); - const SysDllFun<GetFileInformationByHandleFunc> getFileInformationByHandle(L"kernel32.dll", "GetFileInformationByHandle"); + const SysDllFun<GetFileInformationByHandleFunc> getFileInformationByHandle(L"kernel32.dll", "GetFileInformationByHandle"); //available since Windows XP if (!getFileInformationByHandle) { assert(false); @@ -106,13 +104,13 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS /*needed to open a directory*/ /*| FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!*/ , - NULL); + nullptr); if (hDir == INVALID_HANDLE_VALUE) return 0; - ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); BY_HANDLE_FILE_INFORMATION fileInfo = {}; if (!getFileInformationByHandle(hDir, //__in HANDLE hFile, @@ -143,12 +141,12 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error! DWORD volumeSerial = 0; if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - NULL, //__out LPTSTR lpVolumeNameBuffer, + nullptr, //__out LPTSTR lpVolumeNameBuffer, 0, //__in DWORD nVolumeNameSize, &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, - NULL, //__out LPTSTR lpFileSystemNameBuffer, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize return 0; @@ -194,7 +192,7 @@ struct Win32Traverser { struct DirHandle { - DirHandle() : searchHandle(NULL), firstRead(true) {} + DirHandle() : searchHandle(nullptr), firstRead(true) {} HANDLE searchHandle; bool firstRead; @@ -276,7 +274,7 @@ struct FilePlusTraverser { struct DirHandle { - DirHandle() : searchHandle(NULL) {} + DirHandle() : searchHandle(nullptr) {} findplus::FindHandle searchHandle; }; @@ -286,7 +284,7 @@ struct FilePlusTraverser static void create(const Zstring& directory, DirHandle& hnd) //throw FileError { hnd.searchHandle = ::openDir(applyLongPathPrefix(directory).c_str()); - if (hnd.searchHandle == NULL) + if (hnd.searchHandle == nullptr) throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted()); } @@ -315,10 +313,12 @@ struct FilePlusTraverser STATUS_UNSUCCESSFUL | ERROR_GEN_FAILURE STATUS_ACCESS_VIOLATION | ERROR_NOACCESS ->FileIdBothDirectoryInformation on XP accessing UDF */ + if (lastError == ERROR_INVALID_LEVEL || lastError == ERROR_NOT_SUPPORTED || lastError == ERROR_INVALID_PARAMETER || lastError == ERROR_BAD_NET_RESP || + lastError == ERROR_UNEXP_NET_ERR || //traverse network drive hosted by Win98 lastError == ERROR_GEN_FAILURE || lastError == ERROR_NOACCESS) { @@ -407,7 +407,7 @@ private: if (!openSuccess) return; //ignored error - ZEN_ON_BLOCK_EXIT(typedef Trav Trav; Trav::destroy(searchHandle)); + ZEN_ON_SCOPE_EXIT(typedef Trav Trav; Trav::destroy(searchHandle)); typename Trav::FindData fileInfo = {}; @@ -456,8 +456,7 @@ private: } else if (Trav::isDirectory(fileInfo)) //a directory... or symlink that needs to be followed (for directory symlinks this flag is set too!) { - const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName); - if (rv) + if (const std::shared_ptr<TraverseCallback> rv = sink.onDir(shortName, fullName)) traverse<Trav>(fullName, *rv, level + 1); } else //a file or symlink that is followed... @@ -471,7 +470,7 @@ private: } else { - Trav::extractFileInfo(fileInfo, volumeSerial, details); //make optional character of volumeSerial explicit in the interface + Trav::extractFileInfo(fileInfo, volumeSerial, details); //####################################### DST hack ########################################### if (isFatFileSystem) @@ -508,15 +507,15 @@ private: const dst::RawTime encodedTime = dst::fatEncodeUtcTime(i->second); //throw (std::runtime_error) { //may need to remove the readonly-attribute (e.g. FAT usb drives) - FileUpdateHandle updateHandle(i->first, [=]() + FileUpdateHandle updateHandle(i->first, [=] { return ::CreateFile(zen::applyLongPathPrefix(i->first).c_str(), - GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL); + GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr); }); if (updateHandle.get() == INVALID_HANDLE_VALUE) { @@ -527,7 +526,7 @@ private: if (!::SetFileTime(updateHandle.get(), &encodedTime.createTimeRaw, - NULL, + nullptr, &encodedTime.writeTimeRaw)) { ++failedAttempts; @@ -604,27 +603,27 @@ private: }, sink); - DIR* dirObj = NULL; + DIR* dirObj = nullptr; tryReportingError([&] { dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/" - if (dirObj == NULL) + if (!dirObj) throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted()); }, sink); - if (dirObj == NULL) + if (!dirObj) return; //ignored error - ZEN_ON_BLOCK_EXIT(::closedir(dirObj)); //never close NULL handles! -> crash + ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash while (true) { - struct ::dirent* dirEntry = NULL; + struct ::dirent* dirEntry = nullptr; tryReportingError([&] { if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted()); }, sink); - if (dirEntry == NULL) //no more items or ignore error + if (!dirEntry) //no more items or ignore error return; diff --git a/zen/file_traverser.h b/zen/file_traverser.h index d6b69f86..ab46621f 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -66,7 +66,7 @@ struct DstHackCallback; //DST hack not required on Linux void traverseFolder(const Zstring& directory, //throw(); bool followSymlinks, TraverseCallback& sink, - DstHackCallback* dstCallback = NULL); //apply DST hack if callback is supplied + DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied //followSymlinks: //"true": Symlinks dereferenced and reported via onFile() and onDir() => onSymlink not used! //"false": Symlinks directly reported via onSymlink(), directory symlinks are not followed diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 1b2af5bf..eedbfab9 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -18,13 +18,13 @@ class FixedList { struct Node { - Node() : next(NULL), val() {} - template <class A> Node(A&& a) : next(NULL), val(a) {} - template <class A, class B> Node(A&& a, B&& b) : next(NULL), val(a, b) {} - template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(NULL), val(a, b, c) {} - template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(NULL), val(a, b, c, d) {} - template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(NULL), val(a, b, c, d, e) {} - template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(NULL), val(a, b, c, d, e, f) {} + Node() : next(nullptr), val() {} + template <class A> Node(A&& a) : next(nullptr), val(a) {} + template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(a, b) {} + template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(a, b, c) {} + template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(a, b, c, d) {} + template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(a, b, c, d, e) {} + template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(a, b, c, d, e, f) {} Node* next; //singly linked list is sufficient T val; @@ -32,8 +32,8 @@ class FixedList public: FixedList() : - first(NULL), - lastInsert(NULL), + first(nullptr), + lastInsert(nullptr), sz(0) {} ~FixedList() @@ -51,7 +51,7 @@ public: class ListIterator : public std::iterator<std::forward_iterator_tag, U> { public: - ListIterator(NodeT* it = NULL) : iter(it) {} + ListIterator(NodeT* it = nullptr) : iter(it) {} ListIterator& operator++() { iter = iter->next; return *this; } inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.iter == rhs.iter; } inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); } @@ -90,7 +90,7 @@ public: template <class Predicate> void remove_if(Predicate pred) { - Node* prev = NULL; + Node* prev = nullptr; Node* ptr = first; while (ptr) @@ -102,7 +102,7 @@ public: prev->next = ptr = tmp; else first = ptr = tmp; - if (tmp == NULL) + if (!tmp) lastInsert = prev; } else @@ -113,7 +113,7 @@ public: } void clear() { remove_if([](T&) { return true; }); } - bool empty() const { return first == NULL; } + bool empty() const { return first == nullptr; } size_t size() const { return sz; } private: @@ -123,14 +123,14 @@ private: void pushNode(Node* newNode) { ++sz; - if (lastInsert == NULL) + if (lastInsert == nullptr) { - assert(first == NULL); + assert(first == nullptr); first = lastInsert = newNode; } else { - assert(lastInsert->next == NULL); + assert(lastInsert->next == nullptr); lastInsert->next = newNode; lastInsert = newNode; } @@ -33,7 +33,7 @@ struct TranslationHandler virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n) = 0; }; -void setTranslator(TranslationHandler* newHandler = NULL); //takes ownership +void setTranslator(TranslationHandler* newHandler = nullptr); //takes ownership TranslationHandler* getTranslator(); std::wstring getThousandsSeparator(); diff --git a/zen/int64.h b/zen/int64.h index a5140ffd..ea51b23b 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -49,13 +49,15 @@ class Int64 { public: //safe implicit conversions - Int64() : value(0) {} - Int64(const Int64& rhs) : value(rhs.value) {} - template <class T> - Int64(T rhs, typename EnableIf<IsSignedInt<T>::result && sizeof(T) <= sizeof(std::int64_t)>::Result* = NULL) : value(static_cast<std::int64_t>(rhs)) {} + Int64() : value(0) {} + Int64(const Int64& rhs) : value(rhs.value) {} + template <class T> + Int64(T rhs, typename EnableIf<IsSignedInt<T>::value && sizeof(T) <= sizeof(std::int64_t)>::Type* = nullptr) : + value(static_cast<std::int64_t>(rhs)) {} //unsafe explicit but checked conversion for all other integer types - template <class T> explicit Int64(T rhs, typename EnableIf<!(IsSignedInt<T>::result && sizeof(T) <= sizeof(std::int64_t))>::Result* = NULL) : value(static_cast<std::int64_t>(rhs)) { checkRange<std::int64_t>(rhs); } + template <class T> explicit Int64(T rhs, typename EnableIf<!(IsSignedInt<T>::value && sizeof(T) <= sizeof(std::int64_t))>::Type* = nullptr) : + value(static_cast<std::int64_t>(rhs)) { checkRange<std::int64_t>(rhs); } Int64& operator=(const Int64& rhs) { value = rhs.value; return *this; } @@ -126,13 +128,15 @@ class UInt64 { public: //safe implicit conversions - UInt64() : value(0) {} - UInt64(const UInt64& rhs) : value(rhs.value) {} - template <class T> - UInt64(T rhs, typename EnableIf<IsUnsignedInt<T>::result && sizeof(T) <= sizeof(std::uint64_t)>::Result* = NULL) : value(static_cast<std::uint64_t>(rhs)) {} + UInt64() : value(0) {} + UInt64(const UInt64& rhs) : value(rhs.value) {} + template <class T> + UInt64(T rhs, typename EnableIf<IsUnsignedInt<T>::value && sizeof(T) <= sizeof(std::uint64_t)>::Type* = nullptr) : + value(static_cast<std::uint64_t>(rhs)) {} //unsafe explicit but checked conversion for all other integer types - template <class T> explicit UInt64(T rhs, typename EnableIf<!(IsUnsignedInt<T>::result && sizeof(T) <= sizeof(std::uint64_t))>::Result* = NULL) : value(static_cast<std::uint64_t>(rhs)) { checkRange<std::uint64_t>(rhs); } + template <class T> explicit UInt64(T rhs, typename EnableIf<!(IsUnsignedInt<T>::value && sizeof(T) <= sizeof(std::uint64_t))>::Type* = nullptr) : + value(static_cast<std::uint64_t>(rhs)) { checkRange<std::uint64_t>(rhs); } UInt64& operator=(const UInt64& rhs) { value = rhs.value; return *this; } @@ -248,8 +252,8 @@ public: //specialize zen type trait namespace zen -> we cannot mix signed/unsigned in general arithmetic operations -> we'll use the ostream-approach { -template <> struct IsUnsignedInt<UInt64> { enum { result = true }; }; -template <> struct IsSignedInt <Int64> { enum { result = true }; }; +template <> struct IsUnsignedInt<UInt64> : StaticBool<true> {}; +template <> struct IsSignedInt <Int64> : StaticBool<true> {}; } */ #endif //FFS_LARGE_64_BIT_INTEGER_H_INCLUDED diff --git a/zen/last_error.h b/zen/last_error.h index 6f701992..28982b63 100644 --- a/zen/last_error.h +++ b/zen/last_error.h @@ -20,16 +20,25 @@ #endif - namespace zen { //evaluate GetLastError()/errno and assemble specific error message #ifdef FFS_WIN -std::wstring getLastErrorFormatted(DWORD lastError = 0); +typedef DWORD ErrorCode; #elif defined FFS_LINUX -std::wstring getLastErrorFormatted(int lastError = 0); +typedef int ErrorCode; #endif +std::wstring getLastErrorFormatted(ErrorCode lastError = 0); +ErrorCode getLastError(); + +bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" aliases + + + + + + @@ -56,24 +65,37 @@ std::wstring getLastErrorFormatted(int lastError = 0); -//######################## Implementation ######################## + + + +//######################## implementation ######################## +inline +ErrorCode getLastError() +{ #ifdef FFS_WIN + return ::GetLastError(); +#elif defined FFS_LINUX + return errno; +#endif +} + inline -std::wstring getLastErrorFormatted(DWORD lastError) //try to get additional Windows error information +std::wstring getLastErrorFormatted(ErrorCode lastError) { +#ifdef FFS_WIN //determine error code if none was specified if (lastError == 0) lastError = ::GetLastError(); std::wstring output = _("Windows Error Code %x:"); - replace(output, L"%x", toString<std::wstring>(lastError)); + replace(output, L"%x", numberTo<std::wstring>(lastError)); - LPWSTR buffer = NULL; + 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, NULL, lastError, 0, reinterpret_cast<LPWSTR>(&buffer), 0, NULL) != 0) + FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, lastError, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0) { if (buffer) //just to be sure { @@ -84,27 +106,37 @@ std::wstring getLastErrorFormatted(DWORD lastError) //try to get additional Wind } ::SetLastError(lastError); //restore last error return output; -} #elif defined FFS_LINUX -inline -std::wstring getLastErrorFormatted(int lastError) //try to get additional Linux error information -{ //determine error code if none was specified if (lastError == 0) lastError = errno; //don't use "::", errno is a macro! std::wstring output = _("Linux Error Code %x:"); - replace(output, L"%x", toString<std::wstring>(lastError)); + replace(output, L"%x", numberTo<std::wstring>(lastError)); output += L" "; output += utf8CvrtTo<std::wstring>(::strerror(lastError)); errno = lastError; //restore errno return output; -} #endif } +inline +bool errorCodeForNotExisting(ErrorCode lastError) +{ +#ifdef FFS_WIN + return lastError == ERROR_FILE_NOT_FOUND || + lastError == ERROR_PATH_NOT_FOUND || + lastError == ERROR_BAD_NETPATH || + lastError == ERROR_NETNAME_DELETED; + +#elif defined FFS_LINUX + return lastError == ENOENT; +#endif +} +} + #endif // SYSTEMFUNCTIONS_H_INCLUDED diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index d6255b85..83643906 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -55,7 +55,7 @@ template <size_t max_path> inline Zstring applyLongPathPrefixImpl(const Zstring& path) { assert(!path.empty()); //nicely check almost all WinAPI accesses! - assert(!zen::cStringIsWhiteSpace(path[0])); + assert(!zen::isWhiteSpace(path[0])); if (path.length() >= max_path && //maximum allowed path length without prefix is (MAX_PATH - 1) !zen::startsWith(path, LONG_PATH_PREFIX)) diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp index 661822cb..d8657ea2 100644 --- a/zen/notify_removal.cpp +++ b/zen/notify_removal.cpp @@ -91,10 +91,10 @@ LRESULT CALLBACK topWndProc(HWND hwnd, //handle to window MessageProvider::MessageProvider() : - process(::GetModuleHandle(NULL)), //get program's module handle - windowHandle(NULL) + process(::GetModuleHandle(nullptr)), //get program's module handle + windowHandle(nullptr) { - if (process == NULL) + if (!process) throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (GetModuleHandle)"); //register the main window class @@ -110,17 +110,17 @@ MessageProvider::MessageProvider() : //create dummy-window windowHandle = ::CreateWindow(WINDOW_NAME, //LPCTSTR lpClassName OR ATOM in low-order word! - NULL, //LPCTSTR lpWindowName, + nullptr, //LPCTSTR lpWindowName, 0, //DWORD dwStyle, 0, //int x, 0, //int y, 0, //int nWidth, 0, //int nHeight, 0, //note: we need a toplevel window to receive device arrival events, not a message-window (HWND_MESSAGE)! - NULL, //HMENU hMenu, - process, //HINSTANCE hInstance, - NULL); //LPVOID lpParam - if (windowHandle == NULL) + nullptr, //HMENU hMenu, + process, //HINSTANCE hInstance, + nullptr); //LPVOID lpParam + if (!windowHandle) throw zen::FileError(std::wstring(L"Could not start monitoring window notifications:") + L"\n\n" + getLastErrorFormatted() + L" (CreateWindow)"); guardClass.dismiss(); @@ -161,7 +161,7 @@ public: hNotification = ::RegisterDeviceNotification(MessageProvider::instance().getWnd(), //__in HANDLE hRecipient, &filter, //__in LPVOID NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); //__in DWORD Flags - if (hNotification == NULL) + if (!hNotification) { const DWORD lastError = ::GetLastError(); if (lastError != ERROR_CALL_NOT_IMPLEMENTED && //fail on SAMBA share: this shouldn't be a showstopper! diff --git a/zen/optional.h b/zen/optional.h index 1e04f52c..2bd272ae 100644 --- a/zen/optional.h +++ b/zen/optional.h @@ -34,9 +34,13 @@ template <class T> class Opt { public: - Opt() : valid(false), value() {} - Opt(NoValue) : valid(false), value() {} - Opt(const T& val) : valid(true ), value(val) {} + Opt() : value() , valid(false) {} + Opt(NoValue) : value() , valid(false) {} + Opt(const T& val) : value(val), valid(true ) {} + + //rvalue optimization: only basic exception safety: + Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {} + Opt& operator=(const Opt& tmp) { value = std::move(tmp.value); valid = tmp.valid; } #ifdef _MSC_VER private: @@ -52,9 +56,11 @@ public: const T* operator->() const { return &value; } /**/ T* operator->() { return &value; } + + void reset() { valid = false; } private: - const bool valid; T value; + bool valid; }; } @@ -7,14 +7,21 @@ #ifndef DEBUG_PERF_HEADER #define DEBUG_PERF_HEADER -#include <sstream> #include "deprecate.h" + +#ifdef FFS_WIN +#include <sstream> #include "win.h" //includes "windows.h" +#else +#include <iostream> +#include <time.h> +#endif //two macros for quick performance measurements #define PERF_START CpuTimer perfTest; #define PERF_STOP perfTest.showResult(); +#ifdef FFS_WIN class CpuTimer { public: @@ -24,8 +31,10 @@ public: CpuTimer() : frequency(), startTime(), resultShown(false) { SetThreadAffinity dummy; - if (!::QueryPerformanceFrequency(&frequency)) throw TimerError(); - if (!::QueryPerformanceCounter (&startTime)) throw TimerError(); + if (!::QueryPerformanceFrequency(&frequency)) + throw TimerError(); + if (!::QueryPerformanceCounter (&startTime)) + throw TimerError(); } ~CpuTimer() @@ -38,16 +47,18 @@ public: { SetThreadAffinity dummy; LARGE_INTEGER currentTime = {}; - if (!::QueryPerformanceCounter(¤tTime)) throw TimerError(); + if (!::QueryPerformanceCounter(¤tTime)) + throw TimerError(); - const long delta = static_cast<long>(1000.0 * (currentTime.QuadPart - startTime.QuadPart) / frequency.QuadPart); + const auto delta = static_cast<long>(1000.0 * (currentTime.QuadPart - startTime.QuadPart) / frequency.QuadPart); std::ostringstream ss; ss << delta << " ms"; - ::MessageBoxA(NULL, ss.str().c_str(), "Timer", 0); + ::MessageBoxA(nullptr, ss.str().c_str(), "Timer", 0); resultShown = true; - if (!::QueryPerformanceCounter(&startTime)) throw TimerError(); //don't include call to MessageBox()! + if (!::QueryPerformanceCounter(&startTime)) + throw TimerError(); //don't include call to MessageBox()! } private: @@ -57,6 +68,8 @@ private: SetThreadAffinity() : oldmask(::SetThreadAffinityMask(::GetCurrentThread(), 1)) { if (oldmask == 0) throw TimerError(); } ~SetThreadAffinity() { ::SetThreadAffinityMask(::GetCurrentThread(), oldmask); } private: + SetThreadAffinity(const SetThreadAffinity&); + SetThreadAffinity& operator=(const SetThreadAffinity&); const DWORD_PTR oldmask; }; @@ -65,4 +78,46 @@ private: bool resultShown; }; + +#else +class CpuTimer +{ +public: + class TimerError {}; + + ZEN_DEPRECATE + CpuTimer() : startTime(), resultShown(false) + { + //clock() seems to give grossly incorrect results: multi core issue? + //gettimeofday() seems fine but is deprecated + if (::clock_gettime(CLOCK_MONOTONIC_RAW, &startTime) != 0) //CLOCK_MONOTONIC measures time reliably across processors! + throw TimerError(); + } + + ~CpuTimer() + { + if (!resultShown) + showResult(); + } + + void showResult() + { + timespec currentTime = {}; + if (::clock_gettime(CLOCK_MONOTONIC_RAW, ¤tTime) != 0) + throw TimerError(); + + const auto delta = static_cast<long>((currentTime.tv_sec - startTime.tv_sec) * 1000.0 + (currentTime.tv_nsec - startTime.tv_nsec) / 1000000.0); + std::clog << "Perf: duration: " << delta << " ms\n"; + resultShown = true; + + if (::clock_gettime(CLOCK_MONOTONIC_RAW, &startTime) != 0) + throw TimerError(); + } + +private: + timespec startTime; + bool resultShown; +}; +#endif + #endif //DEBUG_PERF_HEADER diff --git a/zen/privilege.cpp b/zen/privilege.cpp index 3b7e9cc5..23a55bd8 100644 --- a/zen/privilege.cpp +++ b/zen/privilege.cpp @@ -11,15 +11,15 @@ namespace { bool privilegeIsActive(LPCTSTR privilege) //throw FileError { - HANDLE hToken = NULL; + HANDLE hToken = nullptr; if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, TOKEN_QUERY, //__in DWORD DesiredAccess, &hToken)) //__out PHANDLE TokenHandle throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hToken)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); LUID luid = {}; - if (!::LookupPrivilegeValue(NULL, //__in_opt LPCTSTR lpSystemName, + if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, privilege, //__in LPCTSTR lpName, &luid )) //__out PLUID lpLuid throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); @@ -42,15 +42,15 @@ bool privilegeIsActive(LPCTSTR privilege) //throw FileError void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError { - HANDLE hToken = NULL; + HANDLE hToken = nullptr; if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess, &hToken)) //__out PHANDLE TokenHandle throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hToken)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); LUID luid = {}; - if (!::LookupPrivilegeValue(NULL, //__in_opt LPCTSTR lpSystemName, + if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, privilege, //__in LPCTSTR lpName, &luid )) //__out PLUID lpLuid throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); @@ -60,12 +60,12 @@ void setPrivilege(LPCTSTR privilege, bool enable) //throw FileError tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; - if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle, - false, //__in BOOL DisableAllPrivileges, - &tp, //__in_opt PTOKEN_PRIVILEGES NewState, - 0, //__in DWORD BufferLength, - NULL, //__out_opt PTOKEN_PRIVILEGES PreviousState, - NULL)) //__out_opt PDWORD ReturnLength + if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle, + false, //__in BOOL DisableAllPrivileges, + &tp, //__in_opt PTOKEN_PRIVILEGES NewState, + 0, //__in DWORD BufferLength, + nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState, + nullptr)) //__out_opt PDWORD ReturnLength throw FileError(_("Error setting privilege:") + L" \"" + privilege + L"\"" + L"\n\n" + getLastErrorFormatted()); if (::GetLastError() == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! diff --git a/zen/privilege.h b/zen/privilege.h index 97d0d201..b940279a 100644 --- a/zen/privilege.h +++ b/zen/privilege.h @@ -15,5 +15,4 @@ namespace zen void activatePrivilege(LPCTSTR privilege); //throw FileError; thread-safe!!! } - #endif // PRIVILEGE_H_INCLUDED diff --git a/zen/scope_guard.h b/zen/scope_guard.h index cc2f31e6..86d22c91 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -8,6 +8,8 @@ #ifndef ZEN_SCOPEGUARD_8971632487321434 #define ZEN_SCOPEGUARD_8971632487321434 +#include <cassert> + //best of Zen, Loki and C++11 namespace zen @@ -21,7 +23,7 @@ namespace zen //Scope Exit /* - ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); */ class ScopeGuardBase @@ -56,7 +58,7 @@ public: { fun_(); } - catch (...) {} + catch (...) { assert(false); } } private: @@ -72,6 +74,6 @@ ScopeGuardImpl<F> makeGuard(F fun) { return ScopeGuardImpl<F>(fun); } #define ZEN_CONCAT_SUB(X, Y) X ## Y #define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y) -#define ZEN_ON_BLOCK_EXIT(X) zen::ScopeGuard ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard([&](){X;}); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_EXIT(X) zen::ScopeGuard ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); #endif //ZEN_SCOPEGUARD_8971632487321434 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 03a10f96..e9fe149a 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -9,6 +9,7 @@ #define STL_TOOLS_HEADER_84567184321434 #include <memory> +#include <algorithm> #if defined _MSC_VER && _MSC_VER <= 1600 #include <set> #include <map> @@ -111,17 +112,14 @@ ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const template <class BidirectionalIterator, class T> inline BidirectionalIterator find_last(const BidirectionalIterator first, BidirectionalIterator last, const T& value) { - //reverse iteration: 1. check 2. decrement 3. evaluate - const BidirectionalIterator iterNotFound = last; - for (;;) //VS 2010 doesn't like "while (true)" + for (BidirectionalIterator iter = last; iter != first;) //reverse iteration: 1. check 2. decrement 3. evaluate { - if (last == first) - return iterNotFound; - --last; + --iter; // - if (*last == value) - return last; + if (*iter == value) + return iter; } + return last; } diff --git a/zen/string_base.h b/zen/string_base.h index ef0d9059..82793a49 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -118,11 +118,7 @@ class StorageRefCountThreadSafe : public AP protected: ~StorageRefCountThreadSafe() {} - static Char* create(size_t size) - { - return create(size, size); - } - + static Char* create(size_t size) { return create(size, size); } static Char* create(size_t size, size_t minCapacity) { const size_t newCapacity = AP::calcCapacity(minCapacity); @@ -157,23 +153,24 @@ protected: return descr(ptr)->refCount == 1 && minCapacity <= descr(ptr)->capacity; } - static size_t length(const Char* ptr) - { - return descr(ptr)->length; - } + static size_t length(const Char* ptr) { return descr(ptr)->length; } static void setLength(Char* ptr, size_t newLength) { assert(canWrite(ptr, newLength)); - descr(ptr)->length = newLength; + descr(ptr)->length = static_cast<std::uint32_t>(newLength); } private: struct Descriptor { - Descriptor(long rc, size_t len, size_t cap) : refCount(rc), length(len), capacity(cap) {} + Descriptor(long rc, size_t len, size_t cap) : + refCount(rc), + length(static_cast<std::uint32_t>(len)), + capacity(static_cast<std::uint32_t>(cap)) {} boost::detail::atomic_count refCount; //practically no perf loss: ~0.2%! (FFS comparison) + //replace by #include <atomic> std::atomic_int when finally getting rid of VS2010 std::uint32_t length; std::uint32_t capacity; //allocated size without null-termination }; @@ -197,7 +194,7 @@ public: Zbase(const Char* source, size_t length); Zbase(const Zbase& source); Zbase(Zbase&& tmp); - explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; Zbase name = buffer; ups... + explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... //allow explicit construction from different string type, prevent ambiguity via SFINAE template <class S> explicit Zbase(const S& other, typename S::value_type = 0); ~Zbase(); @@ -217,15 +214,15 @@ public: //std::string functions size_t length() const; - size_t size() const; - const Char* c_str() const; //C-string format with NULL-termination - const Char* data() const; //internal representation, NULL-termination not guaranteed + 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 operator[](size_t pos) const; - bool empty() const; + bool empty() const { return length() == 0; } void clear(); size_t find (const Zbase& str, size_t pos = 0) const; // - size_t find (const Char* str, size_t pos = 0) const; //returns "npos" if not found - size_t find (Char ch, size_t pos = 0) const; // + size_t find (const Char* str, size_t pos = 0) const; // + size_t find (Char ch, size_t pos = 0) const; //returns "npos" if not found size_t rfind(Char ch, size_t pos = npos) const; // size_t rfind(const Char* str, size_t pos = npos) const; // Zbase& replace(size_t pos1, size_t n1, const Zbase& str); @@ -234,7 +231,7 @@ public: Zbase& append(const Char* source, size_t len); void resize(size_t newSize, Char fillChar = 0); void swap(Zbase& other); - void push_back(Char val); //STL access + void push_back(Char val) { operator+=(val); } //STL access Zbase& operator=(Zbase source); Zbase& operator=(const Char* source); @@ -254,27 +251,24 @@ private: 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> bool operator==(const Char* 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 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> 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> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs); -template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, const Char* rhs); -template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs); -template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs); -template <class Char, template <class, class> class SP, class AP> const Zbase<Char, SP, AP> operator+(const Zbase<Char, SP, AP>& lhs, Char 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> inline bool operator==(const Char* lhs, const Zbase<Char, SP, AP>& 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> 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); +//rvalue references: unified first argument! +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 lhs += rhs; } +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 lhs += rhs; } +template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP> lhs, Char rhs) { return lhs += rhs; } +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) += 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; } @@ -302,13 +296,7 @@ template <class Char, template <class, class> class SP, class AP> const Zbase<Ch - - - - - - -//################################# inline implementation ######################################## +//################################# implementation ######################################## template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::Zbase() { @@ -355,7 +343,16 @@ Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source) template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp) { - rawStr = this->clone(tmp.rawStr); //for a ref-counting string there probably isn't a faster way, even with r-value references + //rawStr = this->clone(tmp.rawStr); NO! do not increment ref-count of a potentially unshared string! We'd lose optimization opportunity of reusing it! + //instead create a dummy string and swap: + if (canWrite(tmp.rawStr, 0)) //perf: this check saves about 4% + { + rawStr = this->create(0); //no perf issue! see comment in default constructor + rawStr[0] = 0; + swap(tmp); + } + else //shared representation: yet another "add ref" won't hurt + rawStr = this->clone(tmp.rawStr); } @@ -524,34 +521,6 @@ 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> 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> class SP, class AP> inline bool operator<(const Zbase<Char, SP, AP>& lhs, const Zbase<Char, SP, AP>& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0 @@ -583,27 +552,6 @@ size_t Zbase<Char, SP, AP>::length() const template <class Char, template <class, class> class SP, class AP> inline -size_t Zbase<Char, SP, AP>::size() const -{ - return length(); -} - - -template <class Char, template <class, class> class SP, class AP> inline -const Char* Zbase<Char, SP, AP>::c_str() const -{ - return rawStr; -} - - -template <class Char, template <class, class> class SP, class AP> inline -const Char* Zbase<Char, SP, AP>::data() const -{ - return rawStr; -} - - -template <class Char, template <class, class> class SP, class AP> inline const Char Zbase<Char, SP, AP>::operator[](size_t pos) const { assert(pos < length()); @@ -641,20 +589,6 @@ Char* Zbase<Char, SP, AP>::end() template <class Char, template <class, class> class SP, class AP> inline -void Zbase<Char, SP, AP>::push_back(Char val) -{ - operator+=(val); -} - - -template <class Char, template <class, class> class SP, class AP> inline -bool Zbase<Char, SP, AP>::empty() const -{ - return length() == 0; -} - - -template <class Char, template <class, class> class SP, class AP> inline void Zbase<Char, SP, AP>::clear() { if (!empty()) @@ -671,41 +605,6 @@ void Zbase<Char, SP, AP>::clear() template <class Char, template <class, class> class SP, class AP> inline -const 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 -const 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 -const 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> class SP, class AP> inline -const Zbase<Char, SP, AP> operator+(Char 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 -const 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> class SP, class AP> inline void Zbase<Char, SP, AP>::swap(Zbase<Char, SP, AP>& other) { std::swap(rawStr, other.rawStr); @@ -719,7 +618,7 @@ void Zbase<Char, SP, AP>::reserve(size_t minCapacity) //make unshared and check { //allocate a new string Char* newStr = create(length(), std::max(minCapacity, length())); //reserve() must NEVER shrink the string: logical const! - std::copy(rawStr, rawStr + length() + 1, newStr); //include NULL-termination + std::copy(rawStr, rawStr + length() + 1, newStr); //include 0-termination destroy(rawStr); rawStr = newStr; diff --git a/zen/string_tools.h b/zen/string_tools.h index 602c4258..85eef5df 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -23,9 +23,8 @@ //enhance arbitray string class with useful non-member functions: namespace zen { -template <class Char> bool cStringIsWhiteSpace(Char ch); -template <class Char> bool cStringIsDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only! - +template <class Char> bool isWhiteSpace(Char ch); +template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only! template <class S, class T> bool startsWith(const S& str, const T& prefix); //both S and T can be strings or char/wchar_t arrays or simple char/wchar_t template <class S, class T> bool endsWith (const S& str, const T& postfix); // @@ -36,7 +35,6 @@ template <class S, class T> S afterFirst (const S& str, const T& ch); //returns template <class S, class T> S beforeFirst(const S& str, const T& ch); //returns the whole string if ch not found template <class S, class T> std::vector<S> split(const S& str, const T& delimiter); -template <class S> void truncate(S& str, size_t newLen); template <class S> void trim(S& str, bool fromLeft = true, bool fromRight = true); template <class S, class T, class U> void replace ( S& str, const T& oldOne, const U& newOne, bool replaceAll = true); template <class S, class T, class U> S replaceCpy(const S& str, const T& oldOne, const U& newOne, bool replaceAll = true); @@ -44,8 +42,8 @@ template <class S, class T, class U> S replaceCpy(const S& str, const T& oldO //high-performance conversion between numbers and strings template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf() -template <class S, class Num> S toString(const Num& number); -template <class Num, class S > Num toNumber(const S& str); +template <class S, class Num> S numberTo(const Num& number); +template <class Num, class S > Num stringTo(const S& str); //string to string conversion: converts string-like type into char-compatible target string class template <class T, class S> T copyStringTo(const S& str); @@ -83,7 +81,7 @@ template <class T, class S> T copyStringTo(const S& str); //---------------------- implementation ---------------------- template <> inline -bool cStringIsWhiteSpace(char ch) +bool isWhiteSpace(char ch) { //caveat 1: std::isspace() takes an int, but expects an unsigned char //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC) @@ -91,13 +89,13 @@ bool cStringIsWhiteSpace(char ch) std::isspace(static_cast<unsigned char>(ch)) != 0; } -template <> inline bool cStringIsWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } +template <> inline bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } template <class Char> inline -bool cStringIsDigit(Char ch) //similar to implmenetation of std::::isdigit()! +bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! { - assert_static((IsSameType<Char, char>::result || IsSameType<Char, wchar_t>::result)); + assert_static((IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value)); return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9'); } @@ -105,9 +103,8 @@ bool cStringIsDigit(Char ch) //similar to implmenetation of std::::isdigit()! template <class S, class T> inline bool startsWith(const S& str, const T& prefix) { - assert_static(IsStringLike<S>::result); - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<S>::value && IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; const size_t pfLength = strLength(prefix); if (strLength(str) < pfLength) @@ -122,9 +119,8 @@ bool startsWith(const S& str, const T& prefix) template <class S, class T> inline bool endsWith(const S& str, const T& postfix) { - assert_static(IsStringLike<S>::result); - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<S>::value && IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; const size_t strLen = strLength(str); const size_t pfLen = strLength(postfix); @@ -141,8 +137,8 @@ bool endsWith(const S& str, const T& postfix) template <class S, class T> inline S afterLast(const S& str, const T& ch) { - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; const size_t chLen = strLength(ch); @@ -164,8 +160,8 @@ S afterLast(const S& str, const T& ch) template <class S, class T> inline S beforeLast(const S& str, const T& ch) { - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; const CharType* const strFirst = strBegin(str); const CharType* const strLast = strFirst + strLength(str); @@ -184,8 +180,8 @@ S beforeLast(const S& str, const T& ch) template <class S, class T> inline S afterFirst(const S& str, const T& ch) { - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; const size_t chLen = strLength(ch); const CharType* const strFirst = strBegin(str); @@ -206,8 +202,8 @@ S afterFirst(const S& str, const T& ch) template <class S, class T> inline S beforeFirst(const S& str, const T& ch) { - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; const CharType* const strFirst = strBegin(str); const CharType* const chFirst = strBegin(ch); @@ -220,8 +216,8 @@ S beforeFirst(const S& str, const T& ch) template <class S, class T> inline std::vector<S> split(const S& str, const T& delimiter) { - assert_static(IsStringLike<T>::result); - typedef typename GetCharType<S>::Result CharType; + assert_static(IsStringLike<T>::value); + typedef typename GetCharType<S>::Type CharType; std::vector<S> output; @@ -252,34 +248,25 @@ std::vector<S> split(const S& str, const T& delimiter) } -template <class S> inline -void truncate(S& str, size_t newLen) -{ - if (newLen < str.length()) - str.resize(newLen); -} - - namespace implementation { 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>::result>::Result stringAppend(S& str, const Char* other, size_t len) { str.append(other, len); } +typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, const Char* other, size_t len) { str.append(other, len); } template <class S, class Char> inline -typename EnableIf<!HasMember_append<S>::result>::Result stringAppend(S& str, const Char* other, size_t len) { str += S(other, len); } +typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, const Char* other, size_t len) { str += S(other, len); } } template <class S, class T, class U> inline S replaceCpy(const S& str, const T& oldOne, const U& newOne, bool replaceAll) { - assert_static(IsStringLike<T>::result); - assert_static(IsStringLike<U>::result); + assert_static(IsStringLike<T>::value && IsStringLike<U>::value); - typedef typename GetCharType<S>::Result CharType; + typedef typename GetCharType<S>::Type CharType; const size_t oldLen = strLength(oldOne); const size_t newLen = strLength(newOne); @@ -324,29 +311,24 @@ template <class S> inline void trim(S& str, bool fromLeft, bool fromRight) { assert(fromLeft || fromRight); - - typedef typename GetCharType<S>::Result CharType; //don't use value_type! (wxString, Glib::ustring) + typedef typename GetCharType<S>::Type CharType; //don't use value_type! (wxString, Glib::ustring) const CharType* const oldBegin = str.c_str(); const CharType* newBegin = oldBegin; const CharType* newEnd = oldBegin + str.length(); if (fromRight) - while (newBegin != newEnd && cStringIsWhiteSpace(newEnd[-1])) + while (newBegin != newEnd && isWhiteSpace(newEnd[-1])) --newEnd; if (fromLeft) - while (newBegin != newEnd && cStringIsWhiteSpace(*newBegin)) + while (newBegin != newEnd && isWhiteSpace(*newBegin)) ++newBegin; - const size_t newLength = newEnd - newBegin; - if (newLength != str.length()) - { - if (newBegin != oldBegin) - str = S(newBegin, newLength); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only - else - str.resize(newLength); - } + if (newBegin != oldBegin) + str = S(newBegin, newEnd - newBegin); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only + else + str.resize(newEnd - newBegin); } @@ -374,8 +356,8 @@ namespace implementation template <class Num> inline int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;) { -#ifdef _MSC_VER - return ::_snprintf(buffer, bufferSize, format, number); //VS2010 doesn't respect ISO C +#if defined _MSC_VER || defined __MINGW32__ + return ::_snprintf(buffer, bufferSize, format, number); //by factor 10 faster than "std::snprintf" on Mingw and on par with std::sprintf()!!! #else return std::snprintf(buffer, bufferSize, format, number); //C99 #endif @@ -385,7 +367,7 @@ template <class Num> inline int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const Num& number) { #ifdef __MINGW32__ - return ::snwprintf(buffer, bufferSize, format, number); //MinGW doesn't respect ISO C + return ::_snwprintf(buffer, bufferSize, format, number); //MinGW doesn't respect ISO C #else return std::swprintf(buffer, bufferSize, format, number); //C99 #endif @@ -395,12 +377,12 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const template <class S, class T, class Num> inline S printNumber(const T& format, const Num& number) //format a single number using ::sprintf { - assert_static(IsStringLike<T>::result); + assert_static(IsStringLike<T>::value); assert_static((IsSameType< - typename GetCharType<S>::Result, - typename GetCharType<T>::Result>::result)); + typename GetCharType<S>::Type, + typename GetCharType<T>::Type>::value)); - typedef typename GetCharType<S>::Result CharType; + typedef typename GetCharType<S>::Type CharType; const int BUFFER_SIZE = 128; CharType buffer[BUFFER_SIZE]; @@ -422,9 +404,9 @@ enum NumberType template <class S, class Num> inline -S toString(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) +S numberTo(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) { - typedef typename GetCharType<S>::Result CharType; + typedef typename GetCharType<S>::Type CharType; std::basic_ostringstream<CharType> ss; ss << number; @@ -436,9 +418,9 @@ template <class S, class Num> inline S floatToString(const Num& number, char ) template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); } template <class S, class Num> inline -S toString(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>) +S numberTo(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>) { - return floatToString<S>(number, typename GetCharType<S>::Result()); + return floatToString<S>(number, typename GetCharType<S>::Type()); } @@ -452,32 +434,37 @@ perf: integer to string: (executed 10 mio. times) template <class S, class Num> inline S formatInteger(Num n, bool hasMinus) { - typedef typename GetCharType<S>::Result CharType; - assert(n >= 0); - S output; + typedef typename GetCharType<S>::Type CharType; + + const size_t bufferSize = 100; //sufficient for signed 256-bit numbers + CharType buffer[bufferSize]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency + assert_static(2 + 5 * sizeof(n) / 2 <= bufferSize); + //minimum required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.5) + + size_t startPos = bufferSize; do { - output += static_cast<CharType>('0' + n % 10); + buffer[--startPos] = static_cast<char>('0' + n % 10); n /= 10; } while (n != 0); + if (hasMinus) - output += static_cast<CharType>('-'); + buffer[--startPos] = static_cast<CharType>('-'); - std::reverse(output.begin(), output.end()); - return output; + return S(buffer + startPos, bufferSize - startPos); } template <class S, class Num> inline -S toString(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) +S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) { return formatInteger<S>(number < 0 ? -number : number, number < 0); } template <class S, class Num> inline -S toString(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) +S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) { return formatInteger<S>(number, false); } @@ -486,20 +473,20 @@ S toString(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) template <class Num, class S> inline -Num toNumber(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW +Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW { - typedef typename GetCharType<S>::Result CharType; + typedef typename GetCharType<S>::Type CharType; Num number = 0; std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType> >(str)) >> number; return number; } -template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, NULL); } -template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, NULL); } +template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, nullptr); } +template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); } template <class Num, class S> inline -Num toNumber(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) +Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) { return stringToFloat<Num>(strBegin(str)); } @@ -507,12 +494,12 @@ Num toNumber(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) template <class Num, class S> Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { - typedef typename GetCharType<S>::Result CharType; + typedef typename GetCharType<S>::Type CharType; const CharType* first = strBegin(str); const CharType* last = first + strLength(str); - while (first != last && cStringIsWhiteSpace(*first)) //skip leading whitespace + while (first != last && isWhiteSpace(*first)) //skip leading whitespace ++first; //handle minus sign @@ -540,7 +527,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i else { //rest of string should contain whitespace only - //assert(std::find_if(iter, last, std::not1(std::ptr_fun(&cStringIsWhiteSpace<CharType>))) == last); -> this is NO assert situation + //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation break; } } @@ -549,7 +536,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i template <class Num, class S> inline -Num toNumber(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>) +Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>) { bool hasMinusSign = false; //handle minus sign const Num number = extractInteger<Num>(str, hasMinusSign); @@ -558,7 +545,7 @@ Num toNumber(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>) template <class Num, class S> inline -Num toNumber(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic +Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { bool hasMinusSign = false; //handle minus sign const Num number = extractInteger<Num>(str, hasMinusSign); @@ -573,28 +560,28 @@ Num toNumber(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversi template <class S, class Num> inline -S toString(const Num& number) //convert number to string the C++ way +S numberTo(const Num& number) { typedef Int2Type< - IsSignedInt <Num>::result ? implementation::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::result ? implementation::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::result ? implementation::NUM_TYPE_FLOATING_POINT : + IsSignedInt <Num>::value ? implementation::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::value ? implementation::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::value ? implementation::NUM_TYPE_FLOATING_POINT : implementation::NUM_TYPE_OTHER> TypeTag; - return implementation::toString<S>(number, TypeTag()); + return implementation::numberTo<S>(number, TypeTag()); } template <class Num, class S> inline -Num toNumber(const S& str) //convert string to number the C++ way +Num stringTo(const S& str) { typedef Int2Type< - IsSignedInt <Num>::result ? implementation::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::result ? implementation::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::result ? implementation::NUM_TYPE_FLOATING_POINT : + IsSignedInt <Num>::value ? implementation::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::value ? implementation::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::value ? implementation::NUM_TYPE_FLOATING_POINT : implementation::NUM_TYPE_OTHER> TypeTag; - return implementation::toNumber<Num>(str, TypeTag()); + return implementation::stringTo<Num>(str, TypeTag()); } } diff --git a/zen/string_traits.h b/zen/string_traits.h index c06aa6e3..cfcf78bb 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -9,7 +9,6 @@ #define STRING_TRAITS_HEADER_813274321443234 #include "type_tools.h" -#include "type_traits.h" #include "assert_static.h" @@ -17,13 +16,13 @@ namespace zen { /* -IsStringLike<>::result: - IsStringLike<const wchar_t*>::result; //equals "true" - IsStringLike<const int*> ::result; //equals "false" +IsStringLike<>::value: + IsStringLike<const wchar_t*>::value; //equals "true" + IsStringLike<const int*> ::value; //equals "false" -GetCharType<>::Result: - GetCharType<std::wstring>::Result //equals wchar_t - GetCharType<wchar_t[5]> ::Result //equals wchar_t +GetCharType<>::Type: + GetCharType<std::wstring>::Type //equals wchar_t + GetCharType<wchar_t[5]> ::Type //equals wchar_t strBegin(): std::wstring str(L"dummy"); @@ -67,21 +66,6 @@ private: //---------------------- implementation ---------------------- namespace implementation { -ZEN_INIT_DETECT_MEMBER(c_str) //we don't know the exact declaration of the member attribute and it may be in a base class! -ZEN_INIT_DETECT_MEMBER(length) // - -template<typename T, bool isClassType> -struct HasStringMembers { enum { result = false }; }; - -template<typename T> -struct HasStringMembers<T, true> //Note: we can apply non-typed member-check on class types only! -{ - enum { result = HasMember_c_str <T>::result && - HasMember_length<T>::result - }; -}; - - template<class S, class Char> //test if result of S::c_str() can convert to const Char* class HasConversion { @@ -91,63 +75,69 @@ class HasConversion static Yes& hasConversion(const Char*); static No& hasConversion(...); - static S createInstance(); + static S& createInstance(); public: - enum { result = sizeof(hasConversion(createInstance().c_str())) == sizeof(Yes) }; + enum { value = sizeof(hasConversion(createInstance().c_str())) == sizeof(Yes) }; }; -template <class S, bool isStringClass> struct GetCharTypeImpl { typedef EmptyType Result; }; -template <class S> struct GetCharTypeImpl<S, true > +template <class S, bool isStringClass> struct GetCharTypeImpl : ResultType<NullType> {}; +template <class S> +struct GetCharTypeImpl<S, true> : + ResultType< + typename SelectIf<HasConversion<S, wchar_t>::value, wchar_t, + typename SelectIf<HasConversion<S, char >::value, char, NullType>::Type + >::Type> { - //typedef typename S::value_type Result; + //typedef typename S::value_type Type; /*DON'T use S::value_type: 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*" 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which is implicitly convertible to *both* "const char*" and "const wchar_t*" while wxString::value_type is a wrapper around an unsigned int */ - typedef typename SelectIf<HasConversion<S, wchar_t>::result, wchar_t, - typename SelectIf<HasConversion<S, char>::result, char, EmptyType>::Result - >::Result Result; }; - -template <> struct GetCharTypeImpl<char, false> { typedef char Result; }; -template <> struct GetCharTypeImpl<wchar_t, false> { typedef wchar_t Result; }; +template <> struct GetCharTypeImpl<char, false> : ResultType<char > {}; +template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {}; ZEN_INIT_DETECT_MEMBER_TYPE(value_type); +ZEN_INIT_DETECT_MEMBER(c_str); //we don't know the exact declaration of the member attribute and it may be in a base class! +ZEN_INIT_DETECT_MEMBER(length); // template <class S> class StringTraits { - typedef typename RemoveRef <S >::Result NonRefType; - typedef typename RemoveConst <NonRefType >::Result NonConstType; - typedef typename RemoveArray <NonConstType>::Result NonArrayType; - typedef typename RemovePointer<NonArrayType>::Result NonPtrType; - typedef typename RemoveConst <NonPtrType >::Result UndecoratedType; //handle "const char* const" + typedef typename RemoveRef <S >::Type NonRefType; + typedef typename RemoveConst <NonRefType >::Type NonConstType; + typedef typename RemoveArray <NonConstType>::Type NonArrayType; + typedef typename RemovePointer<NonArrayType>::Type NonPtrType; + typedef typename RemoveConst <NonPtrType >::Type UndecoratedType; //handle "const char* const" public: enum { - isStringClass = HasStringMembers<NonConstType, HasMemberType_value_type<NonConstType>::result>::result + isStringClass = HasMemberType_value_type<NonConstType>::value && + HasMember_c_str <NonConstType>::value && + HasMember_length <NonConstType>::value }; - typedef typename GetCharTypeImpl<UndecoratedType, isStringClass>::Result CharType; + typedef typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type CharType; enum { - isStringLike = IsSameType<CharType, char>::result || - IsSameType<CharType, wchar_t>::result + isStringLike = + IsSameType<CharType, char >::value || + IsSameType<CharType, wchar_t>::value }; }; } template <class T> -struct IsStringLike { enum { result = implementation::StringTraits<T>::isStringLike }; }; +struct IsStringLike : StaticBool<implementation::StringTraits<T>::isStringLike> {}; template <class T> -struct GetCharType { typedef typename implementation::StringTraits<T>::CharType Result; }; +struct GetCharType : ResultType<typename implementation::StringTraits<T>::CharType> {}; namespace implementation @@ -155,7 +145,7 @@ namespace implementation template <class C> inline size_t cStringLength(const C* str) //strlen() { - assert_static((IsSameType<C, char>::result || IsSameType<C, wchar_t>::result)); + assert_static((IsSameType<C, char>::value || IsSameType<C, wchar_t>::value)); size_t len = 0; while (*str++ != 0) ++len; @@ -165,7 +155,7 @@ size_t cStringLength(const C* str) //strlen() template <class S> inline -const typename GetCharType<S>::Result* strBegin(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Result* = NULL) //SFINAE: T must be a "string" +const typename GetCharType<S>::Type* strBegin(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type* = nullptr) //SFINAE: T must be a "string" { return str.c_str(); } @@ -177,7 +167,7 @@ inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } template <class S> inline -size_t strLength(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Result* = NULL) //SFINAE: T must be a "string" +size_t strLength(const S& str, typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type* = nullptr) //SFINAE: T must be a "string" { return str.length(); } diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 20433b43..006e4ea0 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -76,10 +76,10 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, - NULL); + nullptr); if (hLink == INVALID_HANDLE_VALUE) throw FileError(_("Error resolving symbolic link:") + L"\n\"" + linkPath + L"\"" + L"\n\n" + getLastErrorFormatted()); - ZEN_ON_BLOCK_EXIT(::CloseHandle(hLink)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hLink)); //respect alignment issues... const DWORD bufferSize = REPARSE_DATA_BUFFER_HEADER_SIZE + MAXIMUM_REPARSE_DATA_BUFFER_SIZE; @@ -88,12 +88,12 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError DWORD bytesReturned; //dummy value required by FSCTL_GET_REPARSE_POINT! if (!::DeviceIoControl(hLink, //__in HANDLE hDevice, FSCTL_GET_REPARSE_POINT, //__in DWORD dwIoControlCode, - NULL, //__in_opt LPVOID lpInBuffer, + nullptr, //__in_opt LPVOID lpInBuffer, 0, //__in DWORD nInBufferSize, &buffer[0], //__out_opt LPVOID lpOutBuffer, bufferSize, //__in DWORD nOutBufferSize, &bytesReturned, //__out_opt LPDWORD lpBytesReturned, - NULL)) //__inout_opt LPOVERLAPPED lpOverlapped + nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped throw FileError(_("Error resolving symbolic link:") + L"\n\"" + linkPath + L"\"" + L"\n\n" + getLastErrorFormatted()); REPARSE_DATA_BUFFER& reparseData = *reinterpret_cast<REPARSE_DATA_BUFFER*>(&buffer[0]); //REPARSE_DATA_BUFFER needs to be artificially enlarged! @@ -25,16 +25,16 @@ struct TimeComp //replaces "struct std::tm" and SYSTEMTIME int second; //0-61 }; -TimeComp localTime (time_t utc = std::time(NULL)); //convert time_t (UTC) to local time components +TimeComp localTime (time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error //---------------------------------------------------------------------------------------------------------------------------------- /* format (current) date and time; example: - formatTime<std::wstring>(L"%Y*%m*%d"); -> "2011*10*29" - formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" - formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" + formatTime<std::wstring>(L"%Y*%m*%d"); -> "2011*10*29" + formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" + formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" */ template <class String, class String2> String formatTime(const String2& format, const TimeComp& comp = localTime()); //format as specified by "std::strftime", returns empty string on failure @@ -82,6 +82,12 @@ namespace implementation inline struct std::tm toClibTimeComponents(const TimeComp& comp) { + assert(1 <= comp.month && comp.month <= 12 && + 1 <= comp.day && comp.day <= 31 && + 0 <= comp.hour && comp.hour <= 23 && + 0 <= comp.minute && comp.minute <= 59 && + 0 <= comp.second && comp.second <= 61); + struct std::tm ctc = {}; ctc.tm_year = comp.year - 1900; //years since 1900 ctc.tm_mon = comp.month - 1; //0-11 @@ -90,6 +96,7 @@ struct std::tm toClibTimeComponents(const TimeComp& comp) ctc.tm_min = comp.minute; //0-59 ctc.tm_sec = comp.second; //0-61 ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available + return ctc; } @@ -132,14 +139,14 @@ struct GetFormat<FormatDateTimeTag> //%c - locale dependent date and time: }; template <> -struct GetFormat<FormatIsoDateTag> //%Y-%m-%d - e.g. 2001-08-23 +struct GetFormat<FormatIsoDateTag> //%Y-%m-%d - e.g. 2001-08-23 { const char* format(char) const { return "%Y-%m-%d"; } const wchar_t* format(wchar_t) const { return L"%Y-%m-%d"; } }; template <> -struct GetFormat<FormatIsoTimeTag> //%H:%M:%S - e.g. 14:55:02 +struct GetFormat<FormatIsoTimeTag> //%H:%M:%S - e.g. 14:55:02 { const char* format(char) const { return "%H:%M:%S"; } const wchar_t* format(wchar_t) const { return L"%H:%M:%S"; } @@ -173,10 +180,10 @@ struct PredefinedFormatTag {}; template <class String, class String2> inline String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure { - typedef typename GetCharType<String>::Result CharType; + typedef typename GetCharType<String>::Type CharType; struct std::tm ctc = toClibTimeComponents(comp); - std::mktime (&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday + std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent CharType buffer[256]; @@ -187,7 +194,7 @@ String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormat template <class String, class FormatType> inline String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) { - typedef typename GetCharType<String>::Result CharType; + typedef typename GetCharType<String>::Type CharType; return formatTime<String>(GetFormat<FormatType>().format(CharType()), comp, UserDefinedFormatTag()); } } @@ -196,7 +203,14 @@ String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) inline TimeComp localTime(time_t utc) { - return implementation::toZenTimeComponents(*std::localtime (&utc)); +#ifdef _MSC_VER + struct tm lt = {}; + /*errno_t rv = */ + ::localtime_s(<, &utc); //more secure? + return implementation::toZenTimeComponents(lt); +#else + return implementation::toZenTimeComponents(*std::localtime(&utc)); +#endif } @@ -212,12 +226,12 @@ template <class String, class String2> inline String formatTime(const String2& format, const TimeComp& comp) { typedef typename SelectIf< - IsSameType<String2, FormatDateTag >::result || - IsSameType<String2, FormatTimeTag >::result || - IsSameType<String2, FormatDateTimeTag >::result || - IsSameType<String2, FormatIsoDateTag >::result || - IsSameType<String2, FormatIsoTimeTag >::result || - IsSameType<String2, FormatIsoDateTimeTag>::result, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Result FormatTag; + 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 FormatTag; return implementation::formatTime<String>(format, comp, FormatTag()); } @@ -226,7 +240,7 @@ String formatTime(const String2& format, const TimeComp& comp) template <class String> bool parseTime(const String& format, const String& str, TimeComp& comp) //return true on success { - typedef typename GetCharType<String>::Result CharType; + typedef typename GetCharType<String>::Type CharType; const CharType* iterFmt = strBegin(format); const CharType* const fmtLast = iterFmt + strLength(format); @@ -239,10 +253,10 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return if (strLast - iterStr < digitCount) return false; - if (std::find_if(iterStr, iterStr + digitCount, [](CharType c) { return !cStringIsDigit(c); }) != str.end()) + if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); })) return false; - result = zen::toNumber<int>(StringProxy<CharType>(iterStr, digitCount)); + result = zen::stringTo<int>(StringProxy<CharType>(iterStr, digitCount)); iterStr += digitCount; return true; }; @@ -287,9 +301,9 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return return false; } } - else if (cStringIsWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars + else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars { - while (iterStr != strLast && cStringIsWhiteSpace(*iterStr)) + while (iterStr != strLast && isWhiteSpace(*iterStr)) ++iterStr; } else diff --git a/zen/type_tools.h b/zen/type_tools.h index 03105ac8..b221954c 100644 --- a/zen/type_tools.h +++ b/zen/type_tools.h @@ -8,11 +8,12 @@ #ifndef TYPE_TOOLS_HEADER_45237590734254545 #define TYPE_TOOLS_HEADER_45237590734254545 +#include "type_traits.h" + namespace zen { //########## Strawman Classes ########################## -struct EmptyType {}; -struct NullType {}; +struct NullType {}; //:= no type here //########## Type Mapping ############################## template <int n> @@ -23,61 +24,47 @@ struct Type2Type {}; //########## Control Structures ######################## template <bool flag, class T, class U> -struct SelectIf -{ - typedef T Result; -}; +struct SelectIf : ResultType<T> {}; + template <class T, class U> -struct SelectIf<false, T, U> -{ - typedef U Result; -}; +struct SelectIf<false, T, U> : ResultType<U> {}; //------------------------------------------------------ template <class T, class U> -struct IsSameType -{ - enum { result = false }; -}; +struct IsSameType : StaticBool<false> {}; template <class T> -struct IsSameType<T, T> -{ - enum { result = true }; -}; +struct IsSameType<T, T> : StaticBool<true> {}; //------------------------------------------------------ template <bool, class T = void> struct EnableIf {}; -template <class T> -struct EnableIf<true, T> -{ - typedef T Result; -}; +template <class T> +struct EnableIf<true, T> : ResultType<T> {}; //########## Type Cleanup ############################## template <class T> -struct RemoveRef { typedef T Result; }; +struct RemoveRef : ResultType<T> {}; template <class T> -struct RemoveRef<T&> { typedef T Result; }; +struct RemoveRef<T&> : ResultType<T> {}; //------------------------------------------------------ template <class T> -struct RemoveConst { typedef T Result; }; +struct RemoveConst : ResultType<T> {}; template <class T> -struct RemoveConst<const T> { typedef T Result; }; +struct RemoveConst<const T> : ResultType<T> {}; //------------------------------------------------------ template <class T> -struct RemovePointer { typedef T Result; }; +struct RemovePointer : ResultType<T> {}; template <class T> -struct RemovePointer<T*> { typedef T Result; }; +struct RemovePointer<T*> : ResultType<T> {}; //------------------------------------------------------ template <class T> -struct RemoveArray { typedef T Result; }; +struct RemoveArray : ResultType<T> {}; template <class T, int N> -struct RemoveArray<T[N]> { typedef T Result; }; +struct RemoveArray<T[N]> : ResultType<T> {}; //########## Sorting ############################## /* diff --git a/zen/type_traits.h b/zen/type_traits.h index d9b28525..0b15eef1 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -8,10 +8,34 @@ #ifndef TYPE_TRAITS_HEADER_3425628658765467 #define TYPE_TRAITS_HEADER_3425628658765467 +#include <type_traits> //all we need is std::is_class!! + namespace zen { +//################# TMP compile time return values: "inherit to return compile-time result" ############## +template <int i> +struct StaticInt +{ + enum { value = i }; +}; + +template <bool b> +struct StaticBool : StaticInt<b> {}; + +template <class EnumType, EnumType val> +struct StaticEnum +{ + static const EnumType value = val; +}; +//--------------------------------------------------------- +template <class T> +struct ResultType +{ + typedef T Type; +}; + //################# Built-in Types ######################## -//Example: "IsSignedInt<int>::result" evaluates to "true" +//Example: "IsSignedInt<int>::value" evaluates to "true" template <class T> struct IsUnsignedInt; template <class T> struct IsSignedInt; @@ -22,27 +46,25 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //remaining non-arithmetic types: bool, char, wchar_t //optional: specialize new types like: -//template <> struct IsUnsignedInt<UInt64> { enum { result = true }; }; +//template <> struct IsUnsignedInt<UInt64> : StaticBool<true> {}; //################# Class Members ######################## /* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_ - !!! Note: this may ONLY be used for class types: fails to compile for non-class types !!! - Example: 1. ZEN_INIT_DETECT_MEMBER(c_str); - 2. HasMember_c_str<T>::result -> use as boolean + 2. HasMember_c_str<T>::value -> use as boolean */ -/* Detect data or function members of a class by name and type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ +/* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const); - 2. HasMember_size<T>::result -> use as boolean + 2. HasMember_size<T>::value -> use as boolean */ /* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_ Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type); - 2. HasMemberType_value_type<T>::result -> use as boolean + 2. HasMemberType_value_type<T>::value -> use as boolean */ @@ -65,10 +87,10 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //################ implementation ###################### -#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> { enum { result = true }; }; +#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : StaticBool<true> {}; template <class T> -struct IsUnsignedInt { enum { result = false }; }; +struct IsUnsignedInt : StaticBool<false> {}; ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char); ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int); @@ -78,7 +100,7 @@ ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 - //------------------------------------------------------ template <class T> -struct IsSignedInt { enum { result = false }; }; +struct IsSignedInt : StaticBool<false> {}; ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char); ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int); @@ -88,7 +110,7 @@ ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type a //------------------------------------------------------ template <class T> -struct IsFloat { enum { result = false }; }; +struct IsFloat : StaticBool<false> {}; ZEN_SPECIALIZE_TRAIT(IsFloat, float); ZEN_SPECIALIZE_TRAIT(IsFloat, double); @@ -98,31 +120,40 @@ ZEN_SPECIALIZE_TRAIT(IsFloat, long double); #undef ZEN_SPECIALIZE_TRAIT template <class T> -struct IsInteger { enum { result = IsUnsignedInt<T>::result || IsSignedInt<T>::result }; }; +struct IsInteger : StaticBool<IsUnsignedInt<T>::value || IsSignedInt<T>::value> {}; template <class T> -struct IsArithmetic { enum { result = IsInteger<T>::result || IsFloat<T>::result }; }; +struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; //#################################################################### #define ZEN_INIT_DETECT_MEMBER(NAME) \ \ - template<typename T> \ - class HasMember_##NAME \ - { \ + template<bool isClass, class T> \ + struct HasMemberImpl_##NAME \ + { \ + private: \ typedef char Yes[1]; \ typedef char No [2]; \ \ - template <typename U, U t> class Helper {}; \ - struct Fallback { int NAME; }; \ + template <typename U, U t> \ + class Helper {}; \ + struct Fallback { int NAME; }; \ \ - template <class U> \ - struct Helper2 : public U, public Fallback {}; \ + template <class U> \ + struct Helper2 : public U, public Fallback {}; /*this works only for class types!!!*/ \ \ template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \ template <class U> static Yes& hasMember(...); \ public: \ - enum { result = sizeof(hasMember<T>(NULL)) == sizeof(Yes) }; \ - }; + enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \ + }; \ + \ + template<class T> \ + struct HasMemberImpl_##NAME<false, T> : StaticBool<false> {}; \ + \ + template<typename T> \ + struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {}; + //#################################################################### #define ZEN_INIT_DETECT_MEMBER2(NAME, TYPE) \ @@ -138,7 +169,7 @@ struct IsArithmetic { enum { result = IsInteger<T>::result || IsFloat<T>::result template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \ template <class T> static No& hasMember(...); \ public: \ - enum { result = sizeof(hasMember<U>(NULL)) == sizeof(Yes) }; \ + enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \ }; //#################################################################### @@ -155,7 +186,7 @@ struct IsArithmetic { enum { result = IsInteger<T>::result || IsFloat<T>::result template <class U> static Yes& hasMemberType(Helper<typename U::TYPENAME>*); \ template <class U> static No& hasMemberType(...); \ public: \ - enum { result = sizeof(hasMemberType<T>(NULL)) == sizeof(Yes) }; \ + enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \ }; } @@ -8,13 +8,15 @@ #ifndef STRING_UTF8_HEADER_01832479146991573473545 #define STRING_UTF8_HEADER_01832479146991573473545 +#include <cstdint> #include <iterator> #include "string_tools.h" //copyStringTo namespace zen { //convert any(!) "string-like" object into target string by applying a UTF8 conversion (but only if necessary!) -template <class TargetString, class SourceString> TargetString utf8CvrtTo(const SourceString& str); +template <class TargetString, class SourceString> +TargetString utf8CvrtTo(const SourceString& str); //convert wide to utf8 string; example: std::string tmp = toUtf8<std::string>(L"abc"); template <class CharString, class WideString> @@ -62,7 +64,9 @@ const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF"; //----------------------- implementation ---------------------------------- namespace implementation { -typedef unsigned int CodePoint; //must be at least four bytes +typedef std::uint_fast32_t CodePoint; //must be at least four bytes +typedef std::uint_fast16_t Char16; //we need an unsigned type +typedef unsigned char Char8; const CodePoint CODE_POINT_MAX = 0x10ffff; @@ -73,31 +77,28 @@ const CodePoint LOW_SURROGATE = 0xdc00; const CodePoint LOW_SURROGATE_MAX = 0xdfff; -template <class OutputIterator> inline -OutputIterator codePointToUtf16(CodePoint cp, OutputIterator result) //http://en.wikipedia.org/wiki/UTF-16 +template <class Function> inline +void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char16 { - typedef unsigned short Char16; //this isn't necessarily 16 bit, but all we need is an unsigned type - + //http://en.wikipedia.org/wiki/UTF-16 assert(cp < HIGH_SURROGATE || LOW_SURROGATE_MAX < cp); //code points [0xd800, 0xdfff] are not allowed for UTF-16 assert(cp <= CODE_POINT_MAX); if (cp < 0x10000) - *result++ = static_cast<Char16>(cp); + writeOutput(static_cast<Char16>(cp)); else { cp -= 0x10000; - *result++ = static_cast<Char16>((cp >> 10) + HIGH_SURROGATE); - *result++ = static_cast<Char16>((cp & 0x3ff) + LOW_SURROGATE); + writeOutput(static_cast<Char16>((cp >> 10) + HIGH_SURROGATE)); + writeOutput(static_cast<Char16>((cp & 0x3ff) + LOW_SURROGATE)); } - return result; } template <class CharIterator, class Function> inline -Function utf16ToCodePoint(CharIterator first, CharIterator last, Function f) //f is a unary function taking a CodePoint as single parameter +void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { assert_static(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 2); - typedef unsigned short Char16; //this isn't necessarily 16 bit, but all we need is an unsigned type for ( ; first != last; ++first) { @@ -107,7 +108,7 @@ Function utf16ToCodePoint(CharIterator first, CharIterator last, Function f) //f if (++first == last) { assert(false); //low surrogate expected - break; + return; } assert(LOW_SURROGATE <= static_cast<Char16>(*first) && static_cast<Char16>(*first) <= LOW_SURROGATE_MAX); //low surrogate expected cp = ((cp - HIGH_SURROGATE) << 10) + static_cast<Char16>(*first) - LOW_SURROGATE + 0x10000; @@ -115,40 +116,37 @@ Function utf16ToCodePoint(CharIterator first, CharIterator last, Function f) //f else assert(cp < LOW_SURROGATE || LOW_SURROGATE_MAX < cp); //NO low surrogate expected - f(cp); + writeOutput(cp); } - return f; } -template <class OutputIterator> inline -OutputIterator codePointToUtf8(CodePoint cp, OutputIterator result) //http://en.wikipedia.org/wiki/UTF-8 +template <class Function> inline +void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char8 { - typedef unsigned char Char8; - - assert(cp <= CODE_POINT_MAX); + //http://en.wikipedia.org/wiki/UTF-8 if (cp < 0x80) - *result++ = static_cast<Char8>(cp); + writeOutput(static_cast<Char8>(cp)); else if (cp < 0x800) { - *result++ = static_cast<Char8>((cp >> 6 ) | 0xc0); - *result++ = static_cast<Char8>((cp & 0x3f) | 0x80); + writeOutput(static_cast<Char8>((cp >> 6 ) | 0xc0)); + writeOutput(static_cast<Char8>((cp & 0x3f) | 0x80)); } else if (cp < 0x10000) { - *result++ = static_cast<Char8>((cp >> 12 ) | 0xe0); - *result++ = static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80); - *result++ = static_cast<Char8>((cp & 0x3f ) | 0x80); + writeOutput(static_cast<Char8>((cp >> 12 ) | 0xe0)); + writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80)); + writeOutput(static_cast<Char8>((cp & 0x3f ) | 0x80)); } else { - *result++ = static_cast<Char8>((cp >> 18 ) | 0xf0); - *result++ = static_cast<Char8>(((cp >> 12) & 0x3f) | 0x80); - *result++ = static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80); - *result++ = static_cast<Char8>((cp & 0x3f ) | 0x80); + assert(cp <= CODE_POINT_MAX); + writeOutput(static_cast<Char8>((cp >> 18 ) | 0xf0)); + writeOutput(static_cast<Char8>(((cp >> 12) & 0x3f) | 0x80)); + writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80)); + writeOutput(static_cast<Char8>((cp & 0x3f ) | 0x80)); } - return result; } @@ -170,14 +168,13 @@ size_t getUtf8Len(unsigned char ch) template <class CharIterator, class Function> inline -Function utf8ToCodePoint(CharIterator first, CharIterator last, Function f) //f is a unary function taking a CodePoint as single parameter +void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { assert_static(sizeof(typename std::iterator_traits<CharIterator>::value_type) == 1); - typedef unsigned char Char8; for ( ; first != last; ++first) { - auto getChar = [&](Char8 & ch) -> bool + auto getChar = [&](Char8& ch) -> bool { if (++first == last) { @@ -189,71 +186,55 @@ Function utf8ToCodePoint(CharIterator first, CharIterator last, Function f) //f return true; }; - CodePoint cp = static_cast<Char8>(*first); - switch (getUtf8Len(static_cast<Char8>(cp))) + Char8 ch = static_cast<Char8>(*first); + switch (getUtf8Len(ch)) { case 1: + writeOutput(ch); break; case 2: { - cp = (cp & 0x1f) << 6; - Char8 ch; - if (!getChar(ch)) continue; + CodePoint cp = (ch & 0x1f) << 6; + if (!getChar(ch)) return; cp += ch & 0x3f; + writeOutput(cp); } break; case 3: { - cp = (cp & 0xf) << 12; - Char8 ch; - if (!getChar(ch)) continue; + CodePoint cp = (ch & 0xf) << 12; + if (!getChar(ch)) return; cp += (ch & 0x3f) << 6; - if (!getChar(ch)) continue; + if (!getChar(ch)) return; cp += ch & 0x3f; - + writeOutput(cp); } break; case 4: { - cp = (cp & 0x7) << 18; - Char8 ch; - if (!getChar(ch)) continue; + CodePoint cp = (ch & 0x7) << 18; + if (!getChar(ch)) return; cp += (ch & 0x3f) << 12; - if (!getChar(ch)) continue; + if (!getChar(ch)) return; cp += (ch & 0x3f) << 6; - if (!getChar(ch)) continue; + if (!getChar(ch)) return; cp += ch & 0x3f; + writeOutput(cp); } break; default: assert(false); } - f(cp); } - return f; } -template <class String> -class AppendStringIterator: public std::iterator<std::output_iterator_tag, void, void, void, void> -{ -public: - explicit AppendStringIterator (String& x) : str(&x) {} - AppendStringIterator& operator= (typename String::value_type value) { *str += value; return *this; } - AppendStringIterator& operator* () { return *this; } - AppendStringIterator& operator++ () { return *this; } - AppendStringIterator operator++ (int) { return *this; } -private: - String* str; -}; - - template <class WideString, class CharString> inline WideString utf8ToWide(const CharString& str, Int2Type<2>) //windows: convert utf8 to utf16 wchar_t { WideString output; utf8ToCodePoint(strBegin(str), strBegin(str) + strLength(str), - [&](CodePoint cp) { codePointToUtf16(cp, AppendStringIterator<WideString>(output)); }); + [&](CodePoint cp) { codePointToUtf16(cp, [&](Char16 c) { output += static_cast<wchar_t>(c); }); }); return output; } @@ -273,7 +254,7 @@ CharString wideToUtf8(const WideString& str, Int2Type<2>) //windows: convert utf { CharString output; utf16ToCodePoint(strBegin(str), strBegin(str) + strLength(str), - [&](CodePoint cp) { codePointToUtf8(cp, AppendStringIterator<CharString>(output)); }); + [&](CodePoint cp) { codePointToUtf8(cp, [&](char c) { output += c; }); }); return output; } @@ -283,7 +264,7 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut { CharString output; std::for_each(strBegin(str), strBegin(str) + strLength(str), - [&](CodePoint cp) { codePointToUtf8(cp, AppendStringIterator<CharString>(output)); }); + [&](CodePoint cp) { codePointToUtf8(cp, [&](char c) { output += c; }); }); return output; } } @@ -292,8 +273,8 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut template <class WideString, class CharString> inline WideString utf8ToWide(const CharString& str) { - assert_static((IsSameType<typename GetCharType<CharString>::Result, char >::result)); - assert_static((IsSameType<typename GetCharType<WideString>::Result, wchar_t>::result)); + assert_static((IsSameType<typename GetCharType<CharString>::Type, char >::value)); + assert_static((IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value)); return implementation::utf8ToWide<WideString>(str, Int2Type<sizeof(wchar_t)>()); } @@ -302,8 +283,8 @@ WideString utf8ToWide(const CharString& str) template <class CharString, class WideString> inline CharString wideToUtf8(const WideString& str) { - assert_static((IsSameType<typename GetCharType<CharString>::Result, char >::result)); - assert_static((IsSameType<typename GetCharType<WideString>::Result, wchar_t>::result)); + assert_static((IsSameType<typename GetCharType<CharString>::Type, char >::value)); + assert_static((IsSameType<typename GetCharType<WideString>::Type, wchar_t>::value)); return implementation::wideToUtf8<CharString>(str, Int2Type<sizeof(wchar_t)>()); } @@ -326,8 +307,8 @@ template <class TargetString, class SourceString> inline TargetString utf8CvrtTo(const SourceString& str) { return utf8CvrtTo<TargetString>(str, - typename GetCharType<SourceString>::Result(), - typename GetCharType<TargetString>::Result()); + typename GetCharType<SourceString>::Type(), + typename GetCharType<TargetString>::Type()); } } diff --git a/zen/zstring.cpp b/zen/zstring.cpp index d17e860c..d37fa522 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -35,7 +35,7 @@ LeakChecker::~LeakChecker() const std::string message = std::string("Memory leak detected!") + "\n\n" + "Candidates:\n" + leakingStrings; #ifdef FFS_WIN - MessageBoxA(NULL, message.c_str(), "Error", 0); + MessageBoxA(nullptr, message.c_str(), "Error", 0); #else std::cerr << message; std::abort(); @@ -70,7 +70,7 @@ std::string LeakChecker::rawMemToString(const void* ptr, size_t size) void LeakChecker::reportProblem(const std::string& message) //throw (std::logic_error) { #ifdef FFS_WIN - ::MessageBoxA(NULL, message.c_str(), "Error", 0); + ::MessageBoxA(nullptr, message.c_str(), "Error", 0); #else std::cerr << message; #endif @@ -100,20 +100,20 @@ typedef int (WINAPI* CompareStringOrdinalFunc)(LPCWSTR lpString1, LPCWSTR lpString2, int cchCount2, BOOL bIgnoreCase); -const SysDllFun<CompareStringOrdinalFunc> ordinalCompare = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); +const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); } -int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB) +int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs) { //caveat: function scope static initialization is not thread-safe in VS 2010! - if (ordinalCompare) //this additional test has no noticeable performance impact + if (compareStringOrdinal) //this additional test has no noticeable performance impact { - const int rv = ordinalCompare(a, //pointer to first string - static_cast<int>(sizeA), //size, in bytes or characters, of first string - b, //pointer to second string - static_cast<int>(sizeB), //size, in bytes or characters, of second string - true); //ignore case + const int rv = compareStringOrdinal(lhs, //__in LPCWSTR lpString1, + static_cast<int>(sizeLhs), //__in int cchCount1, + rhs, //__in LPCWSTR lpString2, + static_cast<int>(sizeRhs), //__in int cchCount2, + true); //__in BOOL bIgnoreCase if (rv == 0) throw std::runtime_error("Error comparing strings (ordinal)!"); else @@ -124,26 +124,26 @@ int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA //do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!! //the only reliable way to compare filenames (with XP) is to call "CharUpper" or "LCMapString": - const int minSize = std::min<int>(sizeA, sizeB); + const auto minSize = static_cast<unsigned int>(std::min(sizeLhs, sizeRhs)); int rv = 0; - if (minSize != 0) //LCMapString does not allow input sizes of 0! + if (minSize >= 0) //LCMapString does not allow input sizes of 0! { - if (minSize <= 5000) //performance optimization: stack + if (minSize <= MAX_PATH) //performance optimization: stack { - wchar_t bufferA[5000]; - wchar_t bufferB[5000]; + wchar_t bufferA[MAX_PATH]; + wchar_t bufferB[MAX_PATH]; //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString() if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, LCMAP_UPPERCASE, //__in DWORD dwMapFlags, - a, //__in LPCTSTR lpSrcStr, + lhs, //__in LPCTSTR lpSrcStr, minSize, //__in int cchSrc, bufferA, //__out LPTSTR lpDestStr, - 5000) == 0) //__in int cchDest + MAX_PATH) == 0) //__in int cchDest throw std::runtime_error("Error comparing strings! (LCMapString)"); - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, minSize, bufferB, 5000) == 0) + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, rhs, minSize, bufferB, MAX_PATH) == 0) throw std::runtime_error("Error comparing strings! (LCMapString)"); rv = ::wmemcmp(bufferA, bufferB, minSize); @@ -153,10 +153,10 @@ int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA std::vector<wchar_t> bufferA(minSize); std::vector<wchar_t> bufferB(minSize); - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, a, minSize, &bufferA[0], minSize) == 0) + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, lhs, minSize, &bufferA[0], minSize) == 0) throw std::runtime_error("Error comparing strings! (LCMapString: FS)"); - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, minSize, &bufferB[0], minSize) == 0) + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, rhs, minSize, &bufferB[0], minSize) == 0) throw std::runtime_error("Error comparing strings! (LCMapString: FS)"); rv = ::wmemcmp(&bufferA[0], &bufferB[0], minSize); @@ -164,7 +164,7 @@ int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA } return rv == 0 ? - static_cast<int>(sizeA) - static_cast<int>(sizeB) : + static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs) : rv; } diff --git a/zen/zstring.h b/zen/zstring.h index a53c1bb0..cb6974e5 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -151,7 +151,7 @@ typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, AllocatorFreeStoreChec #if defined(FFS_WIN) || defined(FFS_LINUX) namespace z_impl { -int compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB); +int compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs); void makeUpperCaseWin(wchar_t* str, size_t size); } |