diff options
Diffstat (limited to 'zen')
37 files changed, 1275 insertions, 755 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h index 16f69bde..0d08f6a6 100755 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -13,6 +13,7 @@ #include <cmath> #include <functional> #include <cassert> +#include "type_traits.h" namespace numeric @@ -65,6 +66,7 @@ const double sqrt2 = 1.41421356237309504880; const double ln2 = 0.693147180559945309417; //static_assert(pi + e + sqrt2 + ln2 == 7.9672352249818781, "whoopsie"); + //---------------------------------------------------------------------------------- @@ -83,7 +85,7 @@ const double ln2 = 0.693147180559945309417; template <class T> inline T abs(T value) { - //static_assert(std::is_signed<T>::value, ""); + //static_assert(std::is_signed_v<T>); if (value < 0) return -value; //operator "?:" caveat: may be different type than "value" else @@ -100,7 +102,7 @@ auto dist(T a, T b) //return type might be different than T, e.g. std::chrono::d template <class T> inline int sign(T value) //returns one of {-1, 0, 1} { - static_assert(std::is_signed<T>::value, ""); + static_assert(std::is_signed_v<T>); return value < 0 ? -1 : (value > 0 ? 1 : 0); } @@ -231,8 +233,8 @@ int64_t round(double d) template <class N, class D> inline auto integerDivideRoundUp(N numerator, D denominator) { - static_assert(std::is_integral<N>::value, ""); - static_assert(std::is_integral<D>::value, ""); + static_assert(zen::IsInteger<N>::value); + static_assert(zen::IsInteger<D>::value); assert(numerator > 0 && denominator > 0); return (numerator + denominator - 1) / denominator; } diff --git a/zen/build_info.h b/zen/build_info.h index 9b8b7fc0..e80f3721 100755 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -16,11 +16,11 @@ #endif #ifdef ZEN_BUILD_32BIT - static_assert(sizeof(void*) == 4, ""); + static_assert(sizeof(void*) == 4); #endif #ifdef ZEN_BUILD_64BIT - static_assert(sizeof(void*) == 8, ""); + static_assert(sizeof(void*) == 8); #endif #endif //BUILD_INFO_H_5928539285603428657 @@ -7,6 +7,7 @@ #ifndef CRC_H_23489275827847235 #define CRC_H_23489275827847235 +//boost, clean this mess up! #include <boost/crc.hpp> @@ -28,12 +29,12 @@ inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(), template <class ByteIterator> inline uint16_t getCrc16(ByteIterator first, ByteIterator last) { - static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, ""); + static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1); boost::crc_16_type result; if (first != last) result.process_bytes(&*first, last - first); auto rv = result.checksum(); - static_assert(sizeof(rv) == sizeof(uint16_t), ""); + static_assert(sizeof(rv) == sizeof(uint16_t)); return rv; } @@ -41,12 +42,12 @@ uint16_t getCrc16(ByteIterator first, ByteIterator last) template <class ByteIterator> inline uint32_t getCrc32(ByteIterator first, ByteIterator last) { - static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, ""); + static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1); boost::crc_32_type result; if (first != last) result.process_bytes(&*first, last - first); auto rv = result.checksum(); - static_assert(sizeof(rv) == sizeof(uint32_t), ""); + static_assert(sizeof(rv) == sizeof(uint32_t)); return rv; } } diff --git a/zen/deprecate.h b/zen/deprecate.h deleted file mode 100755 index 1f4e6ab4..00000000 --- a/zen/deprecate.h +++ /dev/null @@ -1,18 +0,0 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef DEPRECATE_H_234897087787348 -#define DEPRECATE_H_234897087787348 - -//compiler macros: http://predef.sourceforge.net/precomp.html -#ifdef __GNUC__ - #define ZEN_DEPRECATE __attribute__ ((deprecated)) - -#else - #error add your platform here! -#endif - -#endif //DEPRECATE_H_234897087787348 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 0cbde150..2bb3fd26 100755 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -103,7 +103,7 @@ DirWatcher::~DirWatcher() std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>& requestUiRefresh, std::chrono::milliseconds cbInterval) //throw FileError { - std::vector<char> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1)); + std::vector<std::byte> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1)); ssize_t bytesRead = 0; do diff --git a/zen/error_log.h b/zen/error_log.h index b6660850..0de88856 100755 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -13,7 +13,7 @@ #include <string> #include "time.h" #include "i18n.h" -#include "string_base.h" +#include "zstring.h" namespace zen @@ -26,13 +26,11 @@ enum MessageType MSG_TYPE_FATAL_ERROR = 0x8, }; -using MsgString = Zbase<wchar_t>; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items) - struct LogEntry { time_t time = 0; MessageType type = MSG_TYPE_FATAL_ERROR; - MsgString message; + Zstringw message; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items) }; template <class String> @@ -69,7 +67,7 @@ private: template <class String> inline void ErrorLog::logMsg(const String& text, MessageType type) { - entries_.push_back({ std::time(nullptr), type, copyStringTo<MsgString>(text) }); + entries_.push_back({ std::time(nullptr), type, copyStringTo<Zstringw>(text) }); } diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 18c0ed26..fca1e3d8 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -52,18 +52,18 @@ Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath) if (startsWith(itemPath, "/")) { - if (startsWith(itemPath, "/media/")) + if (startsWith(itemPath, "/media/")) { //Ubuntu: e.g. /media/zenju/DEVICE_NAME if (const char* username = ::getenv("USER")) if (startsWith(itemPath, std::string("/media/") + username + "/")) return doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - //Ubuntu: e.g. /media/cdrom0 + //Ubuntu: e.g. /media/cdrom0 return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); } - if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME + if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME if (const char* username = ::getenv("USER")) if (startsWith(itemPath, std::string("/run/media/") + username + "/")) return doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/); @@ -372,7 +372,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr } catch (ErrorTargetExisting&) { -#if 0 //"Work around pen drive failing to change file name case" => enable if needed: https://www.freefilesync.org/forum/viewtopic.php?t=4279 +#if 0 //"Work around pen drive failing to change file name case" => enable if needed: https://freefilesync.org/forum/viewtopic.php?t=4279 const Zstring fileNameSrc = afterLast (pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); const Zstring fileNameTrg = afterLast (pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); @@ -411,17 +411,17 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"! */ struct ::timespec newTimes[2] = {}; - newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://www.freefilesync.org/forum/viewtopic.php?t=1701 + newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://freefilesync.org/forum/viewtopic.php?t=1701 newTimes[1] = modTime; //modification time if (procSl == ProcSymlink::FOLLOW) { //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP: - //https://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP) + //https://freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP) if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0) return; - //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: https://www.freefilesync.org/forum/viewtopic.php?t=387 + //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: https://freefilesync.org/forum/viewtopic.php?t=387 const int fdFile = ::open(itemPath.c_str(), O_WRONLY | O_APPEND); //2017-07-04: O_WRONLY | O_APPEND seems to avoid EOPNOTSUPP on gvfs SFTP! if (fdFile == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open"); @@ -676,7 +676,7 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, //this triggers bugs on samba shares where the modification time is set to current time instead. //Linux: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236 // http://comments.gmane.org/gmane.linux.file-systems.cifs/2854 - //OS X: https://www.freefilesync.org/forum/viewtopic.php?t=356 + //OS X: https://freefilesync.org/forum/viewtopic.php?t=356 setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError } catch (const FileError& e) diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 1cbd970b..25dc93ce 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -132,7 +132,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! { /* - FFS 8.9-9.5 perf issues on macOS: https://www.freefilesync.org/forum/viewtopic.php?t=4808 + FFS 8.9-9.5 perf issues on macOS: https://freefilesync.org/forum/viewtopic.php?t=4808 app-level buffering is essential to optimize random data sizes; e.g. "export file list": => big perf improvement on Windows, Linux. No significant improvement on macOS in tests impact on stream-based file copy: @@ -148,8 +148,8 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, Erro assert(memBuf_.size() >= blockSize); assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size()); - char* it = static_cast<char*>(buffer); - char* const itEnd = it + bytesToRead; + auto it = static_cast<std::byte*>(buffer); + const auto itEnd = it + bytesToRead; for (;;) { const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_); @@ -169,7 +169,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, Erro if (bytesRead == 0) //end of file break; } - return it - static_cast<char*>(buffer); + return it - static_cast<std::byte*>(buffer); } //---------------------------------------------------------------------------------------------------- @@ -253,8 +253,8 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro assert(memBuf_.size() >= blockSize); assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size()); - const char* it = static_cast<const char*>(buffer); - const char* const itEnd = it + bytesToWrite; + auto it = static_cast<const std::byte*>(buffer); + const auto itEnd = it + bytesToWrite; for (;;) { if (memBuf_.size() - bufPos_ < blockSize) //support memBuf_.size() > blockSize to reduce memmove()s, but perf test shows: not really needed! diff --git a/zen/file_io.h b/zen/file_io.h index 5b0b8cb0..d05d97db 100755 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -66,7 +66,7 @@ private: const IOCallback notifyUnbufferedIO_; //throw X - std::vector<char> memBuf_ = std::vector<char>(getBlockSize()); + std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize()); size_t bufPos_ = 0; size_t bufPosEnd_= 0; }; @@ -95,7 +95,7 @@ private: IOCallback notifyUnbufferedIO_; //throw X - std::vector<char> memBuf_ = std::vector<char>(getBlockSize()); + std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize()); size_t bufPos_ = 0; size_t bufPosEnd_ = 0; }; diff --git a/zen/fixed_list.h b/zen/fixed_list.h deleted file mode 100755 index 10b66233..00000000 --- a/zen/fixed_list.h +++ /dev/null @@ -1,240 +0,0 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef FIXED_LIST_H_01238467085684139453534 -#define FIXED_LIST_H_01238467085684139453534 - -#include <cassert> -#include <iterator> -#include "stl_tools.h" - - -namespace zen -{ -//std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types -//-> no iterator invalidation after emplace_back() - -template <class T> -class FixedList -{ - struct Node - { - template <class... Args> - Node(Args&& ... args) : val(std::forward<Args>(args)...) {} - - Node* next = nullptr; //singly-linked list is sufficient - T val; - }; - -public: - FixedList() {} - - ~FixedList() { clear(); } - - template <class NodeT, class U> - class FixedIterator : public std::iterator<std::forward_iterator_tag, U> - { - public: - FixedIterator(NodeT* it = nullptr) : it_(it) {} - FixedIterator& operator++() { it_ = it_->next; return *this; } - inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; } - inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); } - U& operator* () const { return it_->val; } - U* operator->() const { return &it_->val; } - private: - NodeT* it_; - }; - - using value_type = T; - using iterator = FixedIterator< Node, T>; - using const_iterator = FixedIterator<const Node, const T>; - using reference = T&; - using const_reference = const T&; - - iterator begin() { return firstInsert_; } - iterator end () { return iterator(); } - - const_iterator begin() const { return firstInsert_; } - const_iterator end () const { return const_iterator(); } - - //const_iterator cbegin() const { return firstInsert_; } - //const_iterator cend () const { return const_iterator(); } - - reference front() { return firstInsert_->val; } - const_reference front() const { return firstInsert_->val; } - - reference& back() { return lastInsert_->val; } - const_reference& back() const { return lastInsert_->val; } - - template <class... Args> - void emplace_back(Args&& ... args) - { - Node* newNode = new Node(std::forward<Args>(args)...); - - if (!lastInsert_) - { - assert(!firstInsert_ && sz_ == 0); - firstInsert_ = lastInsert_ = newNode; - } - else - { - assert(lastInsert_->next == nullptr); - lastInsert_->next = newNode; - lastInsert_ = newNode; - } - ++sz_; - } - - template <class Predicate> - void remove_if(Predicate pred) - { - Node* prev = nullptr; - Node* ptr = firstInsert_; - - while (ptr) - if (pred(ptr->val)) - { - Node* next = ptr->next; - - delete ptr; - assert(sz_ > 0); - --sz_; - - ptr = next; - - if (prev) - prev->next = next; - else - firstInsert_ = next; - if (!next) - lastInsert_ = prev; - } - else - { - prev = ptr; - ptr = ptr->next; - } - } - - void clear() - { - Node* ptr = firstInsert_; - while (ptr) - { - Node* next = ptr->next; - delete ptr; - ptr = next; - } - - sz_ = 0; - firstInsert_ = lastInsert_ = nullptr; - } - - bool empty() const { return sz_ == 0; } - - size_t size() const { return sz_; } - - void swap(FixedList& other) - { - std::swap(firstInsert_, other.firstInsert_); - std::swap(lastInsert_, other.lastInsert_); - std::swap(sz_, other.sz_); - } - -private: - FixedList (const FixedList&) = delete; - FixedList& operator=(const FixedList&) = delete; - - Node* firstInsert_ = nullptr; - Node* lastInsert_ = nullptr; //point to last insertion; required by efficient emplace_back() - size_t sz_ = 0; -}; - - -//just as fast as FixedList, but simpler, more CPU-cache-friendly => superseeds FixedList! -template <class T> -class FixedVector -{ -public: - FixedVector() {} - - /* - class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back() - - template <class V> - class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this random-access if needed - { - public: - FixedIterator(std::vector<std::unique_ptr<T>>& cont, size_t pos) : cont_(cont), pos_(pos) {} - FixedIterator& operator++() { ++pos_; return *this; } - inline friend bool operator==(const FixedIterator& lhs, EndIterator) { return lhs.pos_ == lhs.cont_.size(); } - inline friend bool operator!=(const FixedIterator& lhs, EndIterator) { return !(lhs == EndIterator()); } - V& operator* () const { return *cont_[pos_]; } - V* operator->() const { return &*cont_[pos_]; } - private: - std::vector<std::unique_ptr<T>>& cont_; - size_t pos_ = 0; - }; - */ - - template <class IterImpl, class V> - class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this bidirectional if needed - { - public: - FixedIterator(IterImpl it) : it_(it) {} - FixedIterator& operator++() { ++it_; return *this; } - inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; } - inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); } - V& operator* () const { return **it_; } - V* operator->() const { return &** it_; } - private: - IterImpl it_; //TODO: avoid iterator invalidation after emplace_back(); caveat: end() must not store old length! - }; - - using value_type = T; - using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator, T>; - using const_iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::const_iterator, const T>; - using reference = T&; - using const_reference = const T&; - - iterator begin() { return items_.begin(); } - iterator end () { return items_.end (); } - - const_iterator begin() const { return items_.begin(); } - const_iterator end () const { return items_.end (); } - - reference front() { return *items_.front(); } - const_reference front() const { return *items_.front(); } - - reference& back() { return *items_.back(); } - const_reference& back() const { return *items_.back(); } - - template <class... Args> - void emplace_back(Args&& ... args) - { - items_.push_back(std::make_unique<T>(std::forward<Args>(args)...)); - } - - template <class Predicate> - void remove_if(Predicate pred) - { - erase_if(items_, [&](const std::unique_ptr<T>& p) { return pred(*p); }); - } - - void clear() { items_.clear(); } - bool empty() const { return items_.empty(); } - size_t size () const { return items_.size(); } - void swap(FixedVector& other) { items_.swap(other.items_); } - -private: - FixedVector (const FixedVector&) = delete; - FixedVector& operator=(const FixedVector&) = delete; - - std::vector<std::unique_ptr<T>> items_; -}; -} - -#endif //FIXED_LIST_H_01238467085684139453534 diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 931a29be..3e75278b 100755 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -193,7 +193,7 @@ std::wstring zen::formatUtcToLocalTime(time_t utcTime) auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; }; TimeComp loc = getLocalTime(utcTime); - + std::wstring dateString = formatTime<std::wstring>(L"%x %X", loc); return !dateString.empty() ? dateString : errorMsg(); } diff --git a/zen/globals.h b/zen/globals.h index 2066c380..10975414 100755 --- a/zen/globals.h +++ b/zen/globals.h @@ -21,10 +21,12 @@ class Global public: Global() { - static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever"); + static_assert(std::is_trivially_destructible_v<Pod>, "this memory needs to live forever"); assert(!pod_.inst && !pod_.spinLock); //we depend on static zero-initialization! } + explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); } + ~Global() { set(nullptr); } std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!) @@ -60,7 +62,6 @@ private: //serialize access; can't use std::mutex: has non-trival destructor } pod_; }; - } #endif //GLOBALS_H_8013740213748021573485 @@ -8,16 +8,7 @@ #define GUID_H_80425780237502345 #include <string> - -#ifdef __GNUC__ //boost should clean this mess up - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif -#include <boost/uuid/uuid_generators.hpp> -#ifdef __GNUC__ - #pragma GCC diagnostic pop -#endif - + #include <boost/uuid/uuid_generators.hpp> namespace zen { @@ -28,6 +19,7 @@ std::string generateGUID() //creates a 16-byte GUID // retrieve GUID: 0.13µs per call //generator is only thread-safe like an int => keep thread-local thread_local boost::uuids::random_generator gen; + static_assert(boost::uuids::uuid::static_size() == 16); const boost::uuids::uuid nativeRep = gen(); return std::string(nativeRep.begin(), nativeRep.end()); } diff --git a/zen/http.cpp b/zen/http.cpp new file mode 100755 index 00000000..d06d3309 --- /dev/null +++ b/zen/http.cpp @@ -0,0 +1,376 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#include "http.h" + + #include "socket.h" + +using namespace zen; + + +class HttpInputStream::Impl +{ +public: + Impl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError + const std::vector<std::pair<std::string, std::string>>* postParams) : //issue POST if bound, GET otherwise + notifyUnbufferedIO_(notifyUnbufferedIO) + { + ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ ); + + const Zstring urlFmt = afterFirst(url, Zstr("://"), IF_MISSING_RETURN_NONE); + const Zstring server = beforeFirst(urlFmt, Zstr('/'), IF_MISSING_RETURN_ALL); + const Zstring page = Zstr('/') + afterFirst(urlFmt, Zstr('/'), IF_MISSING_RETURN_NONE); + + const bool useTls = [&] + { + if (startsWith(url, Zstr("http://"), CmpAsciiNoCase())) + return false; + if (startsWith(url, Zstr("https://"), CmpAsciiNoCase())) + return true; + throw SysError(L"URL uses unexpected protocol."); + }(); + + assert(!useTls); //not supported by our plain socket! + (void)useTls; + + socket_ = std::make_unique<Socket>(server, Zstr("http")); //throw SysError + //HTTP default port: 80, see %WINDIR%\system32\drivers\etc\services + + std::map<std::string, std::string, LessAsciiNoCase> headers; + headers["Host" ] = utfTo<std::string>(server); //only required for HTTP/1.1 + headers["User-Agent"] = utfTo<std::string>(userAgent); + headers["Accept" ] = "*/*"; //won't hurt? + + const std::string postBuf = postParams ? xWwwFormUrlEncode(*postParams) : ""; + + if (!postParams) //HTTP GET + headers["Pragma"] = "no-cache"; //HTTP 1.0 only! superseeded by "Cache-Control" + //consider internetIsAlive() test; not relevant for POST (= never cached) + else //HTTP POST + { + headers["Content-type"] = "application/x-www-form-urlencoded"; + headers["Content-Length"] = numberTo<std::string>(postBuf.size()); + } + + //https://www.w3.org/Protocols/HTTP/1.0/spec.html#Request-Line + std::string msg = (postParams ? "POST " : "GET ") + utfTo<std::string>(page) + " HTTP/1.0\r\n"; + for (const auto& item : headers) + msg += item.first + ": " + item.second + "\r\n"; + msg += "\r\n"; + msg += postBuf; + + //send request + for (size_t bytesToSend = msg.size(); bytesToSend > 0;) + { + int bytesSent = 0; + for (;;) + { + bytesSent = ::send(socket_->get(), //_In_ SOCKET s, + &*(msg.end() - bytesToSend), //_In_ const char *buf, + static_cast<int>(bytesToSend), //_In_ int len, + 0); //_In_ int flags + if (bytesSent >= 0 || errno != EINTR) + break; + } + if (bytesSent < 0) + THROW_LAST_SYS_ERROR_WSA(L"send"); + if (bytesSent > static_cast<int>(bytesToSend)) + throw SysError(L"send: buffer overflow."); + if (bytesSent == 0) + throw SysError(L"send: zero bytes processed"); + + bytesToSend -= bytesSent; + } + if (::shutdown(socket_->get(), SHUT_WR) != 0) + THROW_LAST_SYS_ERROR_WSA(L"shutdown"); + + //receive response: + std::string headBuf; + const std::string headerDelim = "\r\n\r\n"; + for (std::string buf;;) + { + const size_t blockSize = std::min(static_cast<size_t>(1024), memBuf_.size()); //smaller block size: try to only read header part + buf.resize(buf.size() + blockSize); + const size_t bytesReceived = tryRead(&*(buf.end() - blockSize), blockSize); //throw SysError + buf.resize(buf.size() - blockSize + bytesReceived); //caveat: unsigned arithmetics + + if (contains(buf, headerDelim)) + { + headBuf = beforeFirst(buf, headerDelim, IF_MISSING_RETURN_NONE); + const std::string bodyBuf = afterFirst (buf, headerDelim, IF_MISSING_RETURN_NONE); + //put excess bytes into instance buffer for body retrieval + assert(bufPos_ == 0 && bufPosEnd_ == 0); + bufPosEnd_ = bodyBuf.size(); + std::copy(bodyBuf.begin(), bodyBuf.end(), reinterpret_cast<char*>(&memBuf_[0])); + break; + } + if (bytesReceived == 0) + break; + } + //parse header + const std::string statusBuf = beforeFirst(headBuf, "\r\n", IF_MISSING_RETURN_ALL); + const std::string headersBuf = afterFirst (headBuf, "\r\n", IF_MISSING_RETURN_NONE); + + const std::vector<std::string> statusItems = split(statusBuf, ' ', SplitType::ALLOW_EMPTY); //HTTP-Version SP Status-Code SP Reason-Phrase CRLF + if (statusItems.size() < 2 || !startsWith(statusItems[0], "HTTP/")) + throw SysError(L"Invalid HTTP response: \"" + utfTo<std::wstring>(statusBuf) + L"\""); + + statusCode_ = stringTo<int>(statusItems[1]); + + for (const std::string& line : split(headersBuf, "\r\n", SplitType::SKIP_EMPTY)) + responseHeaders_[trimCpy(beforeFirst(line, ":", IF_MISSING_RETURN_ALL))] = + /**/ trimCpy(afterFirst (line, ":", IF_MISSING_RETURN_NONE)); + + //try to get "Content-Length" header if available + if (const std::string* value = getHeader("Content-Length")) + contentRemaining_ = stringTo<int64_t>(*value) - (bufPosEnd_ - bufPos_); + } + + ~Impl() { cleanup(); } + + + const int getStatusCode() const { return statusCode_; } + + const std::string* getHeader(const std::string& name) const + { + auto it = responseHeaders_.find(name); + return it != responseHeaders_.end() ? &it->second : nullptr; + } + + size_t read(void* buffer, size_t bytesToRead) //throw SysError, X; return "bytesToRead" bytes unless end of stream! + { + const size_t blockSize = getBlockSize(); + assert(memBuf_.size() >= blockSize); + assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size()); + + auto it = static_cast<std::byte*>(buffer); + const auto itEnd = it + bytesToRead; + for (;;) + { + const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_); + std::memcpy(it, &memBuf_[0] + bufPos_, junkSize); + bufPos_ += junkSize; + it += junkSize; + + if (it == itEnd) + break; + //-------------------------------------------------------------------- + const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 + bufPos_ = 0; + bufPosEnd_ = bytesRead; + + if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X + + if (bytesRead == 0) //end of file + break; + } + return it - static_cast<std::byte*>(buffer); + } + + size_t getBlockSize() const { return 64 * 1024; } + +private: + size_t tryRead(void* buffer, size_t bytesToRead) //throw SysError; may return short, only 0 means EOF! + { + if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check! + throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + assert(bytesToRead <= getBlockSize()); //block size might be 1000 while reading HTTP header + + if (contentRemaining_ >= 0) + { + if (contentRemaining_ == 0) + return 0; + bytesToRead = static_cast<size_t>(std::min(static_cast<int64_t>(bytesToRead), contentRemaining_)); //[!] contentRemaining_ > 4 GB possible! + } + int bytesReceived = 0; + for (;;) + { + bytesReceived = ::recv(socket_->get(), //_In_ SOCKET s, + static_cast<char*>(buffer), //_Out_ char *buf, + static_cast<int>(bytesToRead), //_In_ int len, + 0); //_In_ int flags + if (bytesReceived >= 0 || errno != EINTR) + break; + } + if (bytesReceived < 0) + THROW_LAST_SYS_ERROR_WSA(L"recv"); + if (static_cast<size_t>(bytesReceived) > bytesToRead) //better safe than sorry + throw SysError(L"HttpInputStream::tryRead: buffer overflow."); + + if (contentRemaining_ >= 0) + contentRemaining_ -= bytesReceived; + + if (bytesReceived == 0 && contentRemaining_ > 0) + throw SysError(replaceCpy<std::wstring>(L"HttpInputStream::tryRead: incomplete server response; %x more bytes expected.", L"%x", numberTo<std::wstring>(contentRemaining_))); + + return bytesReceived; //"zero indicates end of file" + } + + Impl (const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + void cleanup() + { + } + + std::unique_ptr<Socket> socket_; //*bound* after constructor has run + int statusCode_ = 0; + std::map<std::string, std::string, LessAsciiNoCase> responseHeaders_; + + int64_t contentRemaining_ = -1; //consider "Content-Length" if available + + const IOCallback notifyUnbufferedIO_; //throw X + + std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize()); + size_t bufPos_ = 0; //buffered I/O; see file_io.cpp + size_t bufPosEnd_ = 0; // +}; + + +HttpInputStream::HttpInputStream(std::unique_ptr<Impl>&& pimpl) : pimpl_(std::move(pimpl)) {} + +HttpInputStream::~HttpInputStream() {} + +size_t HttpInputStream::read(void* buffer, size_t bytesToRead) { return pimpl_->read(buffer, bytesToRead); } //throw SysError, X; return "bytesToRead" bytes unless end of stream! + +size_t HttpInputStream::getBlockSize() const { return pimpl_->getBlockSize(); } + +std::string HttpInputStream::readAll() { return bufferedLoad<std::string>(*pimpl_); } //throw SysError, X; + + +namespace +{ +std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError + const std::vector<std::pair<std::string, std::string>>* postParams) //issue POST if bound, GET otherwise +{ + Zstring urlRed = url; + //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop." + for (int redirects = 0; redirects < 6; ++redirects) + { + auto response = std::make_unique<HttpInputStream::Impl>(urlRed, userAgent, notifyUnbufferedIO, postParams); //throw SysError + + //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection + const int statusCode = response->getStatusCode(); + if (statusCode / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too! + { + const std::string* value = response->getHeader("Location"); + if (!value || value->empty()) + throw SysError(L"Unresolvable redirect. No target Location."); + + urlRed = utfTo<Zstring>(*value); + } + else + { + if (statusCode != 200) //HTTP_STATUS_OK + throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(statusCode))); + //e.g. 404 - HTTP_STATUS_NOT_FOUND + + return response; + } + } + throw SysError(L"Too many redirects."); +} + + +//encode into "application/x-www-form-urlencoded" +std::string urlencode(const std::string& str) +{ + std::string out; + for (const char c : str) //follow PHP spec: https://github.com/php/php-src/blob/master/ext/standard/url.c#L500 + if (c == ' ') + out += '+'; + else if (('0' <= c && c <= '9') || + ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'z') || + c == '-' || c == '.' || c == '_') //note: "~" is encoded by PHP! + out += c; + else + { + const std::pair<char, char> hex = hexify(c); + + out += '%'; + out += hex.first; + out += hex.second; + } + return out; +} + + +std::string urldecode(const std::string& str) +{ + std::string out; + for (size_t i = 0; i < str.size(); ++i) + { + const char c = str[i]; + if (c == '+') + out += ' '; + else if (c == '%' && str.size() - i >= 3 && + isHexDigit(str[i + 1]) && + isHexDigit(str[i + 2])) + { + out += unhexify(str[i + 1], str[i + 2]); + i += 2; + } + else + out += c; + } + return out; +} +} + + +std::string zen::xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs) +{ + std::string output; + for (const auto& pair : paramPairs) + output += urlencode(pair.first) + '=' + urlencode(pair.second) + '&'; + //encode both key and value: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 + if (!output.empty()) + output.pop_back(); + return output; +} + + +std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const std::string& str) +{ + std::vector<std::pair<std::string, std::string>> output; + + for (const std::string& nvPair : split(str, '&', SplitType::SKIP_EMPTY)) + output.emplace_back(urldecode(beforeFirst(nvPair, '=', IF_MISSING_RETURN_ALL)), + urldecode(afterFirst (nvPair, '=', IF_MISSING_RETURN_NONE))); + return output; +} + + +HttpInputStream zen::sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, + const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError +{ + return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, &postParams); //throw SysError +} + + +HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO) //throw SysError +{ + return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, nullptr); //throw SysError +} + + +bool zen::internetIsAlive() //noexcept +{ + try + { + auto response = std::make_unique<HttpInputStream::Impl>(Zstr("http://www.google.com/"), + Zstr("FreeFileSync"), + nullptr /*notifyUnbufferedIO*/, + nullptr /*postParams*/); //throw SysError + const int statusCode = response->getStatusCode(); + + //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!! + return statusCode / 100 == 2 || //e.g. 200 + statusCode / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive! + } + catch (SysError&) { return false; } +} diff --git a/zen/http.h b/zen/http.h new file mode 100755 index 00000000..5d84be2c --- /dev/null +++ b/zen/http.h @@ -0,0 +1,48 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef HTTP_H_879083425703425702 +#define HTTP_H_879083425703425702 + +#include <zen/zstring.h> +#include <zen/sys_error.h> +#include <zen/serialize.h> + +namespace zen +{ +/* + - thread-safe! (Window/Linux/macOS) + - HTTPS supported only for Windows +*/ +class HttpInputStream +{ +public: + //support zen/serialize.h buffered input stream concept + size_t read(void* buffer, size_t bytesToRead); //throw SysError, X; return "bytesToRead" bytes unless end of stream! + std::string readAll(); //throw SysError, X + + size_t getBlockSize() const; + + class Impl; + HttpInputStream(std::unique_ptr<Impl>&& pimpl); + HttpInputStream(HttpInputStream&&) noexcept = default; + ~HttpInputStream(); + +private: + std::unique_ptr<Impl> pimpl_; +}; + + +HttpInputStream sendHttpGet (const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError +HttpInputStream sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/, // + const std::vector<std::pair<std::string, std::string>>& postParams); +bool internetIsAlive(); //noexcept + +std::string xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs); +std::vector<std::pair<std::string, std::string>> xWwwFormUrlDecode(const std::string& str); +} + +#endif //HTTP_H_879083425703425702 @@ -95,7 +95,7 @@ std::wstring translate(const std::wstring& text) template <class T> inline std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n) { - static_assert(sizeof(n) <= sizeof(int64_t), ""); + static_assert(sizeof(n) <= sizeof(int64_t)); const auto n64 = static_cast<int64_t>(n); assert(contains(plural, L"%x")); diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h new file mode 100755 index 00000000..5a0013b3 --- /dev/null +++ b/zen/legacy_compiler.h @@ -0,0 +1,89 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef LEGACY_COMPILER_H_839567308565656789 +#define LEGACY_COMPILER_H_839567308565656789 + + #include <memory> + #include <cstddef> //std::byte + + +namespace std +{ +//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html +//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations + +#ifndef __cpp_lib_type_trait_variable_templates //GCC 7.1 + template<class T, class U> constexpr bool is_same_v = is_same<T, U>::value; + template<class T> constexpr bool is_const_v = is_const<T>::value; + template<class T> constexpr bool is_signed_v = is_signed<T>::value; + template<class T> constexpr bool is_trivially_destructible_v = is_trivially_destructible<T>::value; +#endif + +#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //GCC doesn't define __cpp_lib_raw_memory_algorithms +template<class T> void destroy_at(T* p) { p->~T(); } + +template< class ForwardIt > +void destroy(ForwardIt first, ForwardIt last) +{ + for (; first != last; ++first) + std::destroy_at(std::addressof(*first)); +} + +template<class InputIt, class ForwardIt> +ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt trg_first) +{ + typedef typename std::iterator_traits<ForwardIt>::value_type Value; + ForwardIt current = trg_first; + try + { + for (; first != last; ++first, ++current) + ::new (static_cast<void*>(std::addressof(*current))) Value(std::move(*first)); + return current; + } + catch (...) + { + for (; trg_first != current; ++trg_first) + trg_first->~Value(); + throw; + } +} +#endif + +#ifndef __cpp_lib_apply //GCC 7.1 +template <class F, class T0, class T1, class T2> +constexpr decltype(auto) apply(F&& f, std::tuple<T0, T1, T2>& t) { return f(std::get<0>(t), std::get<1>(t), std::get<2>(t)); } +#endif + +#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //__cpp_lib_byte not defined before GCC 7.3 but supported earlier + typedef unsigned char byte; +#endif + +#ifndef __cpp_lib_bool_constant //GCC 6.1 + template<bool B> using bool_constant = integral_constant<bool, B>; +#endif + +//================================================================================ + +} +namespace __cxxabiv1 +{ +struct __cxa_eh_globals; +extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; +} +namespace std +{ +inline int uncaught_exceptions_legacy_hack() noexcept +{ + return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); +} +#ifndef __cpp_lib_uncaught_exceptions //GCC 6.1 +inline int uncaught_exceptions() noexcept { return uncaught_exceptions_legacy_hack(); } +#endif + +} + +#endif //LEGACY_COMPILER_H_839567308565656789 diff --git a/zen/optional.h b/zen/optional.h index 88928ac0..e4605c5f 100755 --- a/zen/optional.h +++ b/zen/optional.h @@ -7,7 +7,6 @@ #ifndef OPTIONAL_H_2857428578342203589 #define OPTIONAL_H_2857428578342203589 -//#include <cassert> #include <type_traits> @@ -51,16 +50,6 @@ public: ~Opt() { if (T* val = get()) val->~T(); } - Opt& operator=(NoValue) //support assignment to Opt<const T> - { - if (T* val = get()) - { - valid_ = false; - val->~T(); - } - return *this; - } - Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe { if (T* val = get()) @@ -81,6 +70,16 @@ public: return *this; } + Opt& operator=(NoValue) //support assignment to Opt<const T> + { + if (T* val = get()) + { + valid_ = false; + val->~T(); + } + return *this; + } + explicit operator bool() const { return valid_; } //thank you, C++11!!! const T* get() const { return valid_ ? reinterpret_cast<const T*>(&rawMem_) : nullptr; } @@ -8,8 +8,8 @@ #define PERF_H_83947184145342652456 #include <chrono> -#include "deprecate.h" #include "scope_guard.h" +#include "string_tools.h" #include <iostream> @@ -31,7 +31,7 @@ namespace zen class PerfTimer { public: - ZEN_DEPRECATE PerfTimer() {} + [[deprecated]] PerfTimer() {} ~PerfTimer() { if (!resultShown_) showResult(); } @@ -75,7 +75,8 @@ public: if (wasRunning) pause(); //don't include call to MessageBox()! ZEN_ON_SCOPE_EXIT(if (wasRunning) resume()); - std::clog << "Perf: duration: " << timeMs() << " ms\n"; + const std::string msg = numberTo<std::string>(timeMs()) + " ms"; + std::clog << "Perf: duration: " << msg << "\n"; resultShown_ = true; } diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h new file mode 100755 index 00000000..1a67c452 --- /dev/null +++ b/zen/ring_buffer.h @@ -0,0 +1,230 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef RING_BUFFER_H_01238467085684139453534 +#define RING_BUFFER_H_01238467085684139453534 + +#include <cassert> +#include <vector> +#include <stdexcept> +#include "type_traits.h" +#include "scope_guard.h" + + +namespace zen +{ +//basically a std::deque<> but with a non-garbage implementation => circular buffer with std::vector<>-like exponential growth! +//https://stackoverflow.com/questions/39324192/why-is-an-stl-deque-not-implemented-as-just-a-circular-vector + +template <class T> +class RingBuffer +{ +public: + RingBuffer() {} + + RingBuffer(RingBuffer&& tmp) noexcept : rawMem_(std::move(tmp.rawMem_)), capacity_(tmp.capacity_), bufStart_(tmp.bufStart_), size_(tmp.size_) + { + tmp.capacity_ = tmp.bufStart_ = tmp.size_ = 0; + } + RingBuffer& operator=(RingBuffer&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!! + + using value_type = T; + using reference = T&; + using const_reference = const T&; + + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + + ~RingBuffer() { clear(); } + + reference front() { return getBufPtr()[bufStart_]; } + const_reference front() const { return getBufPtr()[bufStart_]; } + + template <class U> + void push_back(U&& value) + { + checkInvariants(); + reserve(size_ + 1); //throw ? + ::new (getBufPtr() + getBufPos(size_)) T(std::forward<U>(value)); //throw ? + ++size_; + } + + void pop_front() + { + checkInvariants(); + if (empty()) + throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + + front().~T(); + ++bufStart_; + --size_; + + if (size_ == 0 || bufStart_ == capacity_) + bufStart_ = 0; + } + + void clear() + { + checkInvariants(); + + const size_t frontSize = std::min(size_, capacity_ - bufStart_); + + std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize); + std::destroy(getBufPtr(), getBufPtr() + size_ - frontSize); + bufStart_ = size_ = 0; + } + + template <class Iterator> + void insert_back(Iterator first, Iterator last) //throw ? (strong exception-safety!) + { + checkInvariants(); + + const size_t len = last - first; + reserve(size_ + len); //throw ? + + const size_t endPos = getBufPos(size_); + const size_t tailSize = std::min(len, capacity_ - endPos); + + std::uninitialized_copy(first, first + tailSize, getBufPtr() + endPos); //throw ? + ZEN_ON_SCOPE_FAIL(std::destroy(first, first + tailSize)); + std::uninitialized_copy(first + tailSize, last, getBufPtr()); //throw ? + + size_ += len; + } + + //contract: last - first <= size() + template <class Iterator> + void extract_front(Iterator first, Iterator last) //throw ? strongly exception-safe! (but only basic exception safety for [first, last) range) + { + checkInvariants(); + + const size_t len = last - first; + if (size_ < len) + throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + + const size_t frontSize = std::min(len, capacity_ - bufStart_); + + auto itTrg = std::copy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize, first); //throw ? + /**/ std::copy(getBufPtr(), getBufPtr() + len - frontSize, itTrg); // + + std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize); + std::destroy(getBufPtr(), getBufPtr() + len - frontSize); + + bufStart_ += len; + size_ -= len; + + if (size_ == 0) + bufStart_ = 0; + else if (bufStart_ >= capacity_) + bufStart_ -= capacity_; + } + + void swap(RingBuffer& other) + { + std::swap(rawMem_, other.rawMem_); + std::swap(capacity_, other.capacity_); + std::swap(bufStart_, other.bufStart_); + std::swap(size_, other.size_); + } + + void reserve(size_t minSize) //throw ? (strong exception-safety!) + { + if (minSize > capacity_) + { + const size_t newCapacity = std::max(minSize + minSize / 2, minSize); //no minimum capacity: just like std::vector<> implementation + + RingBuffer newBuf(newCapacity); //throw ? + + T* itTrg = reinterpret_cast<T*>(newBuf.rawMem_.get()); + + const size_t frontSize = std::min(size_, capacity_ - bufStart_); + + itTrg = uninitializedMoveIfNoexcept(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize, itTrg); //throw ? + newBuf.size_ = frontSize; //pass ownership + /**/ uninitializedMoveIfNoexcept(getBufPtr(), getBufPtr() + size_ - frontSize, itTrg); //throw ? + newBuf.size_ = size_; // + + newBuf.swap(*this); + } + } + + const T& operator[](size_t offset) const + { + assert(offset < size()); //design by contract! no runtime check! + return getBufPtr()[getBufPos(offset)]; + } + + T& operator[](size_t offset) { return const_cast<T&>(static_cast<const RingBuffer*>(this)->operator[](offset)); } + + template <class Container, class Value> + class Iterator + { + public: + Iterator(Container& container, size_t offset) : container_(&container), offset_(offset) {} + Iterator& operator++() { ++offset_; return *this; } + inline friend bool operator==(const Iterator& lhs, const Iterator& rhs) { assert(lhs.container_ == rhs.container_); return lhs.offset_ == rhs.offset_; } + inline friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return !(lhs == rhs); } + Value& operator* () const { return (*container_)[offset_]; } + Value* operator->() const { return &(*container_)[offset_]; } + private: + Container* container_ = nullptr; + size_t offset_ = 0; + }; + using iterator = Iterator< RingBuffer, T>; + using const_iterator = Iterator<const RingBuffer, const T>; + + iterator begin() { return { *this, 0 }; } + iterator end () { return { *this, size_ }; } + + const_iterator begin() const { return { *this, 0 }; } + const_iterator end () const { return { *this, size_ }; } + + const_iterator cbegin() const { return begin(); } + const_iterator cend () const { return end (); } + +private: + RingBuffer (const RingBuffer&) = delete; //wait until there is a reason to copy a RingBuffer + RingBuffer& operator=(const RingBuffer&) = delete; // + + RingBuffer(size_t capacity) : + rawMem_(static_cast<std::byte*>(::operator new (capacity * sizeof(T)))), //throw std::bad_alloc + capacity_(capacity) {} + + struct FreeStoreDelete { void operator()(std::byte* p) const { ::operator delete (p); } }; + + T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); } + const T* getBufPtr() const { return reinterpret_cast<T*>(rawMem_.get()); } + + //unlike pure std::uninitialized_move, this one allows for strong exception-safety! + static T* uninitializedMoveIfNoexcept(T* first, T* last, T* firstTrg) + { + return uninitializedMoveIfNoexcept(first, last, firstTrg, std::is_nothrow_move_constructible<T>()); + } + static T* uninitializedMoveIfNoexcept(T* first, T* last, T* firstTrg, std::true_type ) { return std::uninitialized_move(first, last, firstTrg); } + static T* uninitializedMoveIfNoexcept(T* first, T* last, T* firstTrg, std::false_type) { return std::uninitialized_copy(first, last, firstTrg); } //throw ? + + void checkInvariants() + { + assert(bufStart_ == 0 || bufStart_ < capacity_); + assert(size_ <= capacity_); + } + + size_t getBufPos(size_t offset) const //< capacity_ + { + size_t bufPos = bufStart_ + offset; + if (bufPos >= capacity_) + bufPos -= capacity_; + return bufPos; + } + + std::unique_ptr<std::byte, FreeStoreDelete> rawMem_; + size_t capacity_ = 0; //as number of T + size_t bufStart_ = 0; //< capacity_ + size_t size_ = 0; //<= capacity_ +}; +} + +#endif //RING_BUFFER_H_01238467085684139453534 diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 6a732c9f..d056bb2a 100755 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -9,23 +9,7 @@ #include <cassert> #include <exception> -#include "type_tools.h" - - -//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP - static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); - -namespace __cxxabiv1 -{ -struct __cxa_eh_globals; -extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; -} - -inline -int getUncaughtExceptionCount() -{ - return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); -} +#include "type_traits.h" //best of Zen, Loki and C++17 @@ -53,7 +37,7 @@ enum class ScopeGuardRunMode //partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant" template <typename F> inline -void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>) +void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>) { try { fun(); } catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! @@ -61,18 +45,18 @@ void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeG template <typename F> inline -void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>) +void runScopeGuardDestructor(F& fun, int exeptionCountOld, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>) { - const bool failed = getUncaughtExceptionCount() > exeptionCountOld; + const bool failed = std::uncaught_exceptions() > exeptionCountOld; if (!failed) fun(); //throw X } template <typename F> inline -void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>) +void runScopeGuardDestructor(F& fun, int exeptionCountOld, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>) { - const bool failed = getUncaughtExceptionCount() > exeptionCountOld; + const bool failed = std::uncaught_exceptions() > exeptionCountOld; if (failed) try { fun(); } catch (...) { assert(false); } @@ -93,7 +77,7 @@ public: ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS) { if (!dismissed_) - runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>()); + runScopeGuardDestructor(fun_, exeptionCount_, std::integral_constant<ScopeGuardRunMode, runMode>()); } void dismiss() { dismissed_ = true; } @@ -103,7 +87,7 @@ private: ScopeGuard& operator=(const ScopeGuard&) = delete; F fun_; - const int exeptionCount_ = getUncaughtExceptionCount(); + const int exeptionCount_ = std::uncaught_exceptions(); bool dismissed_ = false; }; diff --git a/zen/serialize.h b/zen/serialize.h index 381d102a..16375cff 100755 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -21,7 +21,7 @@ namespace zen -------------------------- |Binary Container Concept| -------------------------- -binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<char>, std::string, Zbase<char>) +binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<std::byte>, std::string, Zbase<char>) */ //binary container reference implementations @@ -29,12 +29,12 @@ using Utf8String = Zbase<char>; //ref-counted + COW text stream + guaranteed per class ByteArray; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?) -class ByteArray //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite +class ByteArray //essentially a std::vector<std::byte> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite { public: - using value_type = std::vector<char>::value_type; - using iterator = std::vector<char>::iterator; - using const_iterator = std::vector<char>::const_iterator; + using value_type = std::vector<std::byte>::value_type; + using iterator = std::vector<std::byte>::iterator; + using const_iterator = std::vector<std::byte>::const_iterator; iterator begin() { return buffer_->begin(); } iterator end () { return buffer_->end (); } @@ -49,7 +49,7 @@ public: inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; } private: - std::shared_ptr<std::vector<char>> buffer_ { std::make_shared<std::vector<char>>() }; //always bound! + std::shared_ptr<std::vector<std::byte>> buffer_ = std::make_shared<std::vector<std::byte>>(); //always bound! //perf: shared_ptr indirection irrelevant: less than 1% slower! }; @@ -122,10 +122,11 @@ struct MemoryStreamIn size_t read(void* buffer, size_t bytesToRead) //return "bytesToRead" bytes unless end of stream! { - static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes + using Byte = typename BinContainer::value_type; + static_assert(sizeof(Byte) == 1); const size_t bytesRead = std::min(bytesToRead, buffer_.size() - pos_); auto itFirst = buffer_.begin() + pos_; - std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(buffer)); + std::copy(itFirst, itFirst + bytesRead, static_cast<Byte*>(buffer)); pos_ += bytesRead; return bytesRead; } @@ -147,9 +148,11 @@ struct MemoryStreamOut void write(const void* buffer, size_t bytesToWrite) { - static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes + using Byte = typename BinContainer::value_type; + static_assert(sizeof(Byte) == 1); buffer_.resize(buffer_.size() + bytesToWrite); - std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.end() - bytesToWrite); + const auto it = static_cast<const Byte*>(buffer); + std::copy(it, it + bytesToWrite, buffer_.end() - bytesToWrite); } const BinContainer& ref() const { return buffer_; } @@ -177,7 +180,7 @@ void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X if (blockSize == 0) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - std::vector<char> buffer(blockSize); + std::vector<std::byte> buffer(blockSize); for (;;) { const size_t bytesRead = streamIn.read(&buffer[0], blockSize); //throw X; return "bytesToRead" bytes unless end of stream! @@ -192,7 +195,7 @@ void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X template <class BinContainer, class BufferedInputStream> inline BinContainer bufferedLoad(BufferedInputStream& streamIn) //throw X { - static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes + static_assert(sizeof(typename BinContainer::value_type) == 1); //expect: bytes const size_t blockSize = streamIn.getBlockSize(); if (blockSize == 0) @@ -221,7 +224,7 @@ void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len) template <class N, class BufferedOutputStream> inline void writeNumber(BufferedOutputStream& stream, const N& num) { - static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "not a number!"); + static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool>); writeArray(stream, &num, sizeof(N)); } @@ -249,7 +252,7 @@ void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw Un template <class N, class BufferedInputStream> inline N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError { - static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, ""); + static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool>); N num = 0; readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError return num; diff --git a/zen/socket.h b/zen/socket.h new file mode 100755 index 00000000..c92071e2 --- /dev/null +++ b/zen/socket.h @@ -0,0 +1,84 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef SOCKET_H_23498325972583947678456437 +#define SOCKET_H_23498325972583947678456437 + +#include <zen/zstring.h> +#include "sys_error.h" + #include <unistd.h> //close + #include <sys/socket.h> + #include <netdb.h> //getaddrinfo + + +namespace zen +{ +#define THROW_LAST_SYS_ERROR_WSA(functionName) \ + do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false) + + +//Winsock needs to be initialized before calling any of these functions! (WSAStartup/WSACleanup) + +class Socket //throw SysError +{ +public: + Socket(const Zstring& server, const Zstring& serviceName) //throw SysError + { + ::addrinfo hints = {}; + hints.ai_socktype = SOCK_STREAM; //we *do* care about this one! + hints.ai_flags = AI_ADDRCONFIG; //save a AAAA lookup on machines that can't use the returned data anyhow + + ::addrinfo* servinfo = nullptr; + ZEN_ON_SCOPE_EXIT(if (servinfo) ::freeaddrinfo(servinfo)); + + const int rcGai = ::getaddrinfo(server.c_str(), serviceName.c_str(), &hints, &servinfo); + if (rcGai != 0) + throw SysError(formatSystemError(L"getaddrinfo", replaceCpy(_("Error Code %x"), L"%x", numberTo<std::wstring>(rcGai)), utfTo<std::wstring>(::gai_strerror(rcGai)))); + if (!servinfo) + throw SysError(L"getaddrinfo: empty server info"); + + auto getConnectedSocket = [&](const auto& /*::addrinfo*/ ai) + { + SocketType testSocket = ::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); + if (testSocket == invalidSocket) + THROW_LAST_SYS_ERROR_WSA(L"socket"); + ZEN_ON_SCOPE_FAIL(closeSocket(testSocket)); + + if (::connect(testSocket, ai.ai_addr, static_cast<int>(ai.ai_addrlen)) != 0) + THROW_LAST_SYS_ERROR_WSA(L"connect"); + + return testSocket; + }; + + Opt<SysError> firstError; + for (const auto* /*::addrinfo*/ si = servinfo; si; si = si->ai_next) + try + { + socket_ = getConnectedSocket(*si); //throw SysError; pass ownership + return; + } + catch (const SysError& e) { if (!firstError) firstError = e; } + + throw* firstError; //list was not empty, so there must have been an error! + } + + ~Socket() { closeSocket(socket_); } + + using SocketType = int; + SocketType get() const { return socket_; } + +private: + Socket (const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + static const SocketType invalidSocket = -1; + static void closeSocket(SocketType s) { ::close(s); } + + SocketType socket_ = invalidSocket; +}; +} + +#endif //SOCKET_H_23498325972583947678456437 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index f09639e1..2ce2cf33 100755 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -12,7 +12,7 @@ #include <vector> #include <memory> #include <algorithm> -#include "type_tools.h" +#include "type_traits.h" #include "build_info.h" @@ -54,10 +54,6 @@ template <class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1, BidirectionalIterator2 first2, BidirectionalIterator2 last2); -template <class InputIterator1, class InputIterator2> -bool equal(InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2); - template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last); template <class Num, class ByteIterator> Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last); @@ -69,7 +65,7 @@ struct StringHash { const auto* strFirst = strBegin(str); return hashBytes<size_t>(reinterpret_cast<const char*>(strFirst), - reinterpret_cast<const char*>(strFirst + strLength(str))); + reinterpret_cast<const char*>(strFirst + strLength(str))); } }; @@ -181,35 +177,25 @@ BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, Bi } -template <class InputIterator1, class InputIterator2> inline -bool equal(InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2) -{ - return last1 - first1 == last2 - first2 && std::equal(first1, last1, first2); -} - - - - //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function template <class Num, class ByteIterator> inline -Num hashBytes(ByteIterator first, ByteIterator last) +Num hashBytes(ByteIterator first, ByteIterator last) { - static_assert(std::is_integral<Num>::value, ""); - static_assert(sizeof(Num) == 4 || sizeof(Num) == 8, ""); //macOS: size_t is "unsigned long" - const Num base = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL; + static_assert(IsInteger<Num>::value); + static_assert(sizeof(Num) == 4 || sizeof(Num) == 8); //macOS: size_t is "unsigned long" + constexpr Num base = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL; - return hashBytesAppend(base, first, last); + return hashBytesAppend(base, first, last); } template <class Num, class ByteIterator> inline Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last) { - static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, ""); - const Num prime = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL; + static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1); + constexpr Num prime = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL; - for (; first != last; ++first) + for (; first != last; ++first) { hashVal ^= static_cast<Num>(*first); hashVal *= prime; diff --git a/zen/string_base.h b/zen/string_base.h index 2d043d4f..9632eba4 100755 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -27,9 +27,9 @@ Allocator Policy: class AllocatorOptimalSpeed //exponential growth + min size { protected: - //::operator new/ ::operator delete show same performance characterisics like malloc()/free()! - static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc - static void deallocate(void* ptr) { ::free(ptr); } + //::operator new/delete show same performance characterisics like malloc()/free()! + static void* allocate(size_t size) { return ::operator new (size); } //throw std::bad_alloc + static void deallocate(void* ptr) { ::operator delete (ptr); } static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); } //- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length! //- any growth rate should not exceed golden ratio: 1.618033989 @@ -39,8 +39,8 @@ protected: class AllocatorOptimalMemory //no wasted memory, but more reallocations required when manipulating string { protected: - static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc - static void deallocate(void* ptr) { ::free(ptr); } + static void* allocate(size_t size) { return ::operator new (size); } //throw std::bad_alloc + static void deallocate(void* ptr) { ::operator delete (ptr); } static size_t calcCapacity(size_t length) { return length; } }; @@ -114,7 +114,7 @@ private: capacity(static_cast<uint32_t>(cap)) {} uint32_t length; - uint32_t capacity; //allocated size without null-termination + const uint32_t capacity; //allocated size without null-termination }; static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } @@ -187,11 +187,15 @@ private: { Descriptor(size_t len, size_t cap) : length (static_cast<uint32_t>(len)), - capacity(static_cast<uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "The atomic type is always lock-free" + capacity(static_cast<uint32_t>(cap)) + { + static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free" + //static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3) + } std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default! uint32_t length; - uint32_t capacity; //allocated size without null-termination + const uint32_t capacity; //allocated size without null-termination }; static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } @@ -234,8 +238,10 @@ public: iterator begin(); iterator end (); + const_iterator begin () const { return rawStr_; } const_iterator end () const { return rawStr_ + length(); } + const_iterator cbegin() const { return begin(); } const_iterator cend () const { return end (); } @@ -357,7 +363,7 @@ Zbase<Char, SP>::Zbase(Zbase<Char, SP>&& tmp) noexcept template <class Char, template <class> class SP> inline Zbase<Char, SP>::~Zbase() { - static_assert(noexcept(this->~Zbase()), ""); //has exception spec of compiler-generated destructor by default + static_assert(noexcept(this->~Zbase())); //has exception spec of compiler-generated destructor by default this->destroy(rawStr_); //rawStr_ may be nullptr; see move constructor! } diff --git a/zen/string_tools.h b/zen/string_tools.h index 7734b6f0..58cb6ea6 100755 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -127,7 +127,7 @@ bool isWhiteSpace(wchar_t c) template <class Char> inline bool isDigit(Char c) //similar to implmenetation of std::isdigit()! { - static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, ""); + static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>); return static_cast<Char>('0') <= c && c <= static_cast<Char>('9'); } @@ -135,7 +135,7 @@ bool isDigit(Char c) //similar to implmenetation of std::isdigit()! template <class Char> inline bool isHexDigit(Char c) { - static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, ""); + static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>); return (static_cast<Char>('0') <= c && c <= static_cast<Char>('9')) || (static_cast<Char>('A') <= c && c <= static_cast<Char>('F')) || (static_cast<Char>('a') <= c && c <= static_cast<Char>('f')); @@ -145,7 +145,7 @@ bool isHexDigit(Char c) template <class Char> inline bool isAsciiAlpha(Char c) { - static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, ""); + static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>); return (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) || (static_cast<Char>('a') <= c && c <= static_cast<Char>('z')); } @@ -209,7 +209,7 @@ template <class S, class T> inline bool strEqual (const S& lhs, const T& rhs template <class S, class T> inline bool contains(const S& str, const T& term) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const size_t strLen = strLength(str); const size_t termLen = strLength(term); if (strLen < termLen) @@ -227,7 +227,7 @@ bool contains(const S& str, const T& term) template <class S, class T> inline S afterLast(const S& str, const T& term, FailureReturnVal rv) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const size_t termLen = strLength(term); assert(termLen > 0); @@ -248,7 +248,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv) template <class S, class T> inline S beforeLast(const S& str, const T& term, FailureReturnVal rv) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const size_t termLen = strLength(term); assert(termLen > 0); @@ -268,7 +268,7 @@ S beforeLast(const S& str, const T& term, FailureReturnVal rv) template <class S, class T> inline S afterFirst(const S& str, const T& term, FailureReturnVal rv) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const size_t termLen = strLength(term); assert(termLen > 0); @@ -289,7 +289,7 @@ S afterFirst(const S& str, const T& term, FailureReturnVal rv) template <class S, class T> inline S beforeFirst(const S& str, const T& term, FailureReturnVal rv) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const size_t termLen = strLength(term); assert(termLen > 0); @@ -309,7 +309,7 @@ S beforeFirst(const S& str, const T& term, FailureReturnVal rv) template <class S, class T> inline std::vector<S> split(const S& str, const T& delimiter, SplitType st) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const size_t delimLen = strLength(delimiter); assert(delimLen > 0); if (delimLen == 0) @@ -346,18 +346,18 @@ ZEN_INIT_DETECT_MEMBER(append); //either call operator+=(S(str, len)) or append(str, len) template <class S, class InputIterator> inline -typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); } +std::enable_if_t<HasMember_append<S>::value> stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); } template <class S, class InputIterator> inline -typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); } +std::enable_if_t<!HasMember_append<S>::value> stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); } } template <class S, class T, class U> inline S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); - static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); + static_assert(std::is_same_v<GetCharTypeT<T>, GetCharTypeT<U>>); const size_t oldLen = strLength(oldTerm); if (oldLen == 0) return str; @@ -427,7 +427,7 @@ void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar) template <class S> inline void trim(S& str, bool fromLeft, bool fromRight) { - using CharType = typename GetCharType<S>::Type; + using CharType = GetCharTypeT<S>; trim(str, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); }); } @@ -509,11 +509,10 @@ 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 { - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); - using CharType = typename GetCharType<S>::Type; + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); const int BUFFER_SIZE = 128; - CharType buffer[BUFFER_SIZE]; //zero-initialize? + GetCharTypeT<S> buffer[BUFFER_SIZE]; //zero-initialize? const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number); return 0 < charsWritten && charsWritten < BUFFER_SIZE ? S(buffer, charsWritten) : S(); @@ -522,21 +521,19 @@ S printNumber(const T& format, const Num& number) //format a single number using namespace impl { -enum NumberType +enum class NumberType { - NUM_TYPE_SIGNED_INT, - NUM_TYPE_UNSIGNED_INT, - NUM_TYPE_FLOATING_POINT, - NUM_TYPE_OTHER, + SIGNED_INT, + UNSIGNED_INT, + FLOATING_POINT, + OTHER, }; template <class S, class Num> inline -S numberTo(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, std::integral_constant<NumberType, NumberType::OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) { - using CharType = typename GetCharType<S>::Type; - - std::basic_ostringstream<CharType> ss; + std::basic_ostringstream<GetCharTypeT<S>> ss; ss << number; return copyStringTo<S>(ss.str()); } @@ -546,9 +543,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 numberTo(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::FLOATING_POINT>) { - return floatToString<S>(number, typename GetCharType<S>::Type()); + return floatToString<S>(number, GetCharTypeT<S>()); } @@ -591,10 +588,9 @@ void formatPositiveInteger(Num n, OutputIterator& it) template <class S, class Num> inline -S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::SIGNED_INT>) { - using CharType = typename GetCharType<S>::Type; - CharType buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize? + GetCharTypeT<S> buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize? //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1! // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41) @@ -612,10 +608,9 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) template <class S, class Num> inline -S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::UNSIGNED_INT>) { - using CharType = typename GetCharType<S>::Type; - CharType buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize? + GetCharTypeT<S> buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize? //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41) auto it = std::end(buffer); @@ -628,9 +623,9 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) //-------------------------------------------------------------------------------- template <class Num, class S> inline -Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) //default string to number conversion using streams: convenient, but SLOW { - using CharType = typename GetCharType<S>::Type; + using CharType = GetCharTypeT<S>; Num number = 0; std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number; return number; @@ -641,7 +636,7 @@ template <class Num> inline Num stringToFloat(const char* str) { return std:: template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); } template <class Num, class S> inline -Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::FLOATING_POINT>) { return stringToFloat<Num>(strBegin(str)); } @@ -649,7 +644,7 @@ Num stringTo(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 { - using CharType = typename GetCharType<S>::Type; + using CharType = GetCharTypeT<S>; const CharType* first = strBegin(str); const CharType* last = first + strLength(str); @@ -687,7 +682,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i template <class Num, class S> inline -Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>) +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::SIGNED_INT>) { bool hasMinusSign = false; //handle minus sign const Num number = extractInteger<Num>(str, hasMinusSign); @@ -696,7 +691,7 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>) template <class Num, class S> inline -Num stringTo(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, std::integral_constant<NumberType, NumberType::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); @@ -713,11 +708,11 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversi template <class S, class Num> inline S numberTo(const Num& number) { - using TypeTag = Int2Type< - IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT : - impl::NUM_TYPE_OTHER>; + using TypeTag = std::integral_constant<impl::NumberType, + IsSignedInt <Num>::value ? impl::NumberType::SIGNED_INT : + IsUnsignedInt<Num>::value ? impl::NumberType::UNSIGNED_INT : + IsFloat <Num>::value ? impl::NumberType::FLOATING_POINT : + impl::NumberType::OTHER>; return impl::numberTo<S>(number, TypeTag()); } @@ -726,11 +721,11 @@ S numberTo(const Num& number) template <class Num, class S> inline Num stringTo(const S& str) { - using TypeTag = Int2Type< - IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT : - impl::NUM_TYPE_OTHER>; + using TypeTag = std::integral_constant<impl::NumberType, + IsSignedInt <Num>::value ? impl::NumberType::SIGNED_INT : + IsUnsignedInt<Num>::value ? impl::NumberType::UNSIGNED_INT : + IsFloat <Num>::value ? impl::NumberType::FLOATING_POINT : + impl::NumberType::OTHER>; return impl::stringTo<Num>(str, TypeTag()); } diff --git a/zen/string_traits.h b/zen/string_traits.h index 805db46d..8187126d 100755 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -8,20 +8,20 @@ #define STRING_TRAITS_H_813274321443234 #include <cstring> //strlen -#include "type_tools.h" +#include "type_traits.h" //uniform access to string-like types, both classes and character arrays namespace zen { /* -IsStringLike<>::value: - IsStringLike<const wchar_t*>::value; //equals "true" - IsStringLike<const int*> ::value; //equals "false" +IsStringLikeV<>: + IsStringLikeV<const wchar_t*> //equals "true" + IsStringLikeV<const int*> //equals "false" -GetCharType<>::Type: - GetCharType<std::wstring>::Type //equals wchar_t - GetCharType<wchar_t[5]> ::Type //equals wchar_t +GetCharTypeT<>: + GetCharTypeT<std::wstring> //equals wchar_t + GetCharTypeT<wchar_t[5]> //equals wchar_t strLength(): strLength(str); //equals str.length() @@ -34,6 +34,7 @@ strBegin(): -> not null-terminated! -> may be nullptr if length is 0! strBegin(array); //returns array */ + //reference a sub-string for consumption by zen string_tools template <class Char> class StringRef @@ -79,15 +80,14 @@ public: }; -template <class S, bool isStringClass> struct GetCharTypeImpl : ResultType<NullType> {}; +template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; }; 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> +struct GetCharTypeImpl<S, true> { + using Type = std::conditional_t<HasConversion<S, wchar_t>::value, wchar_t, + /**/ std::conditional_t<HasConversion<S, char >::value, char, void>>; + //using Type = typename S::value_type; /*DON'T use S::value_type: 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*" @@ -96,13 +96,13 @@ struct GetCharTypeImpl<S, true> : */ }; -template <> struct GetCharTypeImpl<char, false> : ResultType<char > {}; -template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {}; +template <> struct GetCharTypeImpl<char, false> { using Type = char; }; +template <> struct GetCharTypeImpl<wchar_t, false> { using Type = wchar_t; }; -template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {}; -template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> : ResultType<wchar_t> {}; -template <> struct GetCharTypeImpl<StringRef<const char >, false> : ResultType<char > {}; -template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> : ResultType<wchar_t> {}; +template <> struct GetCharTypeImpl<StringRef<char >, false> { using Type = char; }; +template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> { using Type = wchar_t; }; +template <> struct GetCharTypeImpl<StringRef<const char >, false> { using Type = char; }; +template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> { using Type = wchar_t; }; ZEN_INIT_DETECT_MEMBER_TYPE(value_type); @@ -112,35 +112,42 @@ ZEN_INIT_DETECT_MEMBER(length); // template <class S> class StringTraits { - using NonRefType = typename RemoveRef <S >::Type; - using NonConstType = typename RemoveConst <NonRefType >::Type; - using NonArrayType = typename RemoveArray <NonConstType>::Type; - using NonPtrType = typename RemovePointer<NonArrayType>::Type; - using UndecoratedType = typename RemoveConst <NonPtrType >::Type ; //handle "const char* const" + using CleanType = std::remove_cv_t<std::remove_reference_t<S>>; //std::remove_cvref requires C++20 + using NonArrayType = std::remove_extent_t <CleanType>; + using NonPtrType = std::remove_pointer_t<NonArrayType>; + using UndecoratedType = std::remove_cv_t <NonPtrType>; //handle "const char* const" public: enum { - isStringClass = HasMemberType_value_type<NonConstType>::value && - HasMember_c_str <NonConstType>::value && - HasMember_length <NonConstType>::value + isStringClass = HasMemberType_value_type<CleanType>::value && + HasMember_c_str <CleanType>::value && + HasMember_length <CleanType>::value }; using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type; enum { - isStringLike = IsSameType<CharType, char>::value || - IsSameType<CharType, wchar_t>::value + isStringLike = std::is_same_v<CharType, char> || + std::is_same_v<CharType, wchar_t> }; }; } template <class T> -struct IsStringLike : StaticBool<impl::StringTraits<T>::isStringLike> {}; +struct IsStringLike : std::bool_constant<impl::StringTraits<T>::isStringLike> {}; template <class T> -struct GetCharType : ResultType<typename impl::StringTraits<T>::CharType> {}; +struct GetCharType { using Type = typename impl::StringTraits<T>::CharType; }; + + +//template alias helpers: +template<class T> +constexpr bool IsStringLikeV = IsStringLike<T>::value; + +template<class T> +using GetCharTypeT = typename GetCharType<T>::Type; namespace impl @@ -154,7 +161,7 @@ inline size_t cStringLength(const wchar_t* str) { return std::wcslen(str); } template <class C> inline size_t cStringLength(const C* str) { - static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, ""); + static_assert(std::is_same_v<C, char> || std::is_same_v<C, wchar_t>); size_t len = 0; while (*str++ != 0) ++len; @@ -162,8 +169,8 @@ size_t cStringLength(const C* str) } #endif -template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline -const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string" +template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline +const GetCharTypeT<S>* strBegin(const S& str) //SFINAE: T must be a "string" { return str.c_str(); } @@ -179,7 +186,7 @@ inline const char* strBegin(const StringRef<const char >& ref) { return ref inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); } -template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline +template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline size_t strLength(const S& str) //SFINAE: T must be a "string" { return str.length(); @@ -198,9 +205,9 @@ inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length template <class S> inline -auto strBegin(S&& str) -> const typename GetCharType<S>::Type* +auto strBegin(S&& str) -> const GetCharTypeT<S>* { - static_assert(IsStringLike<S>::value, ""); + static_assert(IsStringLikeV<S>); return impl::strBegin(std::forward<S>(str)); } @@ -208,7 +215,7 @@ auto strBegin(S&& str) -> const typename GetCharType<S>::Type* template <class S> inline size_t strLength(S&& str) { - static_assert(IsStringLike<S>::value, ""); + static_assert(IsStringLikeV<S>); return impl::strLength(std::forward<S>(str)); } } diff --git a/zen/sys_error.h b/zen/sys_error.h index c179ec8a..a087172f 100755 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -81,7 +81,6 @@ std::wstring formatSystemError(const std::wstring& functionName, long long lastE inline std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec) { - //static_assert(sizeof(ec) == sizeof(int), ""); //const std::wstring errorCode = printNumber<std::wstring>(L"0x%08x", static_cast<int>(ec)); const std::wstring errorCode = numberTo<std::wstring>(ec); diff --git a/zen/thread.cpp b/zen/thread.cpp new file mode 100755 index 00000000..8016d4a9 --- /dev/null +++ b/zen/thread.cpp @@ -0,0 +1,55 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#include "thread.h" + #include <sys/prctl.h> + #include <unistd.h> + #include <sys/syscall.h> + +using namespace zen; + + + + +void zen::setCurrentThreadName(const char* threadName) +{ + ::prctl(PR_SET_NAME, threadName, 0, 0, 0); + +} + + +namespace +{ +uint64_t getThreadIdNative() +{ + const pid_t tid = ::syscall(SYS_gettid); //no-fail + //"Invalid thread and process IDs": https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503 + //if (tid == 0) -> not sure this holds on Linux, too! + // throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] Failed to get thread ID."); + static_assert(sizeof(uint64_t) >= sizeof(tid)); + return tid; +} + + +struct InitMainThreadIdOnStartup +{ + InitMainThreadIdOnStartup() { getMainThreadId(); } +} startupInitMainThreadId; +} + + +uint64_t zen::getThreadId() +{ + thread_local const uint64_t tid = getThreadIdNative(); //buffer to get predictable perf characteristics + return tid; +} + + +uint64_t zen::getMainThreadId() +{ + static const uint64_t mainThreadId = getThreadId(); + return mainThreadId; +} diff --git a/zen/thread.h b/zen/thread.h index ed61e06b..ee36f305 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -10,9 +10,9 @@ #include <thread> #include <future> #include "scope_guard.h" -#include "type_traits.h" +#include "ring_buffer.h" #include "optional.h" - #include <sys/prctl.h> +#include "string_tools.h" namespace zen @@ -23,8 +23,8 @@ class InterruptibleThread { public: InterruptibleThread() {} - InterruptibleThread (InterruptibleThread&& tmp) = default; - InterruptibleThread& operator=(InterruptibleThread&& tmp) = default; + InterruptibleThread (InterruptibleThread&&) noexcept = default; + InterruptibleThread& operator=(InterruptibleThread&&) noexcept = default; template <class Function> InterruptibleThread(Function&& f); @@ -46,7 +46,7 @@ public: private: std::thread stdThread_; - std::shared_ptr<InterruptionStatus> intStatus_{ std::make_shared<InterruptionStatus>() }; + std::shared_ptr<InterruptionStatus> intStatus_ = std::make_shared<InterruptionStatus>(); std::future<void> threadCompleted_; }; @@ -62,7 +62,9 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //th void setCurrentThreadName(const char* threadName); uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754 +uint64_t getMainThreadId(); +inline bool runningMainThread() { return getThreadId() == getMainThreadId(); } //------------------------------------------------------------------------------------------ /* @@ -118,6 +120,7 @@ class Protected public: Protected() {} Protected(const T& value) : value_(value) {} + //Protected( T&& tmp ) : value_(std::move(tmp)) {} <- wait until needed template <class Function> auto access(Function fun) //-> decltype(fun(std::declval<T&>())) @@ -140,53 +143,101 @@ template <class Function> class ThreadGroup { public: - ThreadGroup(size_t threadCount, const std::string& groupName) - { - for (size_t i = 0; i < threadCount; ++i) - worker_.emplace_back([this, groupName, i, threadCount] - { - setCurrentThreadName((groupName + "[" + numberTo<std::string>(i + 1) + "/" + numberTo<std::string>(threadCount) + "]").c_str()); - for (;;) - getNextWorkItem()(); //throw ThreadInterruption - }); - } + ThreadGroup(size_t threadCountMax, const std::string& groupName) : threadCountMax_(threadCountMax), groupName_(groupName) + { if (threadCountMax == 0) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); } + ~ThreadGroup() { for (InterruptibleThread& w : worker_) w.interrupt(); //interrupt all first, then join - for (InterruptibleThread& w : worker_) w.join(); + for (InterruptibleThread& w : worker_) detach_ ? w.detach() : w.join(); } + ThreadGroup(ThreadGroup&& tmp) noexcept : + worker_ (std::move(tmp.worker_)), + workLoad_ (std::move(tmp.workLoad_)), + detach_ (tmp.detach_), + threadCountMax_(tmp.threadCountMax_), + groupName_ (std::move(tmp.groupName_)) { tmp.worker_.clear(); /*just in case: make sure destructor is no-op!*/ } + + ThreadGroup& operator=(ThreadGroup&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!! + //context of controlling thread, non-blocking: void run(Function&& wi) { - assert(!worker_.empty()); + size_t tasksPending = 0; { - std::lock_guard<std::mutex> dummy(lockWork_); - workItems_.push_back(std::move(wi)); + std::lock_guard<std::mutex> dummy(workLoad_->lock); + workLoad_->tasks.push_back(std::move(wi)); + tasksPending = ++(workLoad_->tasksPending); } - conditionNewWork_.notify_all(); + workLoad_->conditionNewTask.notify_all(); + + if (worker_.size() < std::min(tasksPending, threadCountMax_)) + addWorkerThread(); } + //context of controlling thread, blocking: + void wait() + { + std::unique_lock<std::mutex> dummy(workLoad_->lock); + workLoad_->conditionTasksDone.wait(dummy, [&tasksPending = workLoad_->tasksPending] { return tasksPending == 0; }); + } + + void detach() { detach_ = true; } //not expected to also interrupt! + private: ThreadGroup (const ThreadGroup&) = delete; ThreadGroup& operator=(const ThreadGroup&) = delete; - //context of worker threads, blocking: - Function getNextWorkItem() //throw ThreadInterruption + void addWorkerThread() { - std::unique_lock<std::mutex> dummy(lockWork_); + std::string threadName = groupName_ + '[' + numberTo<std::string>(worker_.size() + 1) + '/' + numberTo<std::string>(threadCountMax_) + ']'; + + worker_.emplace_back([wl = workLoad_, threadName = std::move(threadName)] //don't capture "this"! consider detach() and swap() + { + setCurrentThreadName(threadName.c_str()); + + std::unique_lock<std::mutex> dummy(wl->lock); + for (;;) + { + interruptibleWait(wl->conditionNewTask, dummy, [&tasks = wl->tasks] { return !tasks.empty(); }); //throw ThreadInterruption + + Function task = std::move(wl->tasks. front()); //noexcept thanks to move + /**/ wl->tasks.pop_front(); // + dummy.unlock(); - interruptibleWait(conditionNewWork_, dummy, [this] { return !workItems_.empty(); }); //throw ThreadInterruption - warn_static("implement FIFO!?") + task(); - Function wi = std::move(workItems_. back()); // - /**/ workItems_.pop_back(); //noexcept thanks to move - return wi; // + dummy.lock(); + if (--(wl->tasksPending) == 0) + wl->conditionTasksDone.notify_all(); //too difficult to notify outside the lock + } + }); } + + void swap(ThreadGroup& other) + { + std::swap(worker_, other.worker_); + std::swap(workLoad_, other.workLoad_); + std::swap(detach_, other.detach_); + std::swap(threadCountMax_, other.threadCountMax_); + std::swap(groupName_, other.groupName_); + } + + struct WorkLoad + { + std::mutex lock; + RingBuffer<Function> tasks; //FIFO! :) + size_t tasksPending = 0; + std::condition_variable conditionNewTask; + std::condition_variable conditionTasksDone; + }; + std::vector<InterruptibleThread> worker_; - std::mutex lockWork_; - std::vector<Function> workItems_; - std::condition_variable conditionNewWork_; + std::shared_ptr<WorkLoad> workLoad_ = std::make_shared<WorkLoad>(); + bool detach_ = false; + size_t threadCountMax_; + std::string groupName_; }; @@ -201,7 +252,7 @@ private: namespace impl { template <class Function> inline -auto runAsync(Function&& fun, TrueType /*copy-constructible*/) +auto runAsync(Function&& fun, std::true_type /*copy-constructible*/) { using ResultType = decltype(fun()); @@ -214,11 +265,11 @@ auto runAsync(Function&& fun, TrueType /*copy-constructible*/) template <class Function> inline -auto runAsync(Function&& fun, FalseType /*copy-constructible*/) +auto runAsync(Function&& fun, std::false_type /*copy-constructible*/) { //support move-only function objects! auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun)); - return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType()); + return runAsync([sharedFun] { return (*sharedFun)(); }, std::true_type()); } } @@ -226,7 +277,7 @@ auto runAsync(Function&& fun, FalseType /*copy-constructible*/) template <class Function> inline auto runAsync(Function&& fun) { - return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>()); + return impl::runAsync(std::forward<Function>(fun), std::is_copy_constructible<Function>()); } @@ -308,14 +359,6 @@ Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal //------------------------------------------------------------------------------------------ -//thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine: -#if defined __GNUC__ || defined __clang__ - #define ZEN_THREAD_LOCAL_SPECIFIER __thread -#else - #error "Game over!" -#endif - - class ThreadInterruption {}; @@ -354,7 +397,7 @@ public: ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr)); //"interrupted_" is not protected by cv's mutex => signal may get lost!!! e.g. after condition was checked but before the wait begins - //=> add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out! + //=> add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out! while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted_ || pred(); })) ; @@ -393,7 +436,8 @@ namespace impl inline InterruptionStatus*& refThreadLocalInterruptionStatus() { - static ZEN_THREAD_LOCAL_SPECIFIER InterruptionStatus* threadLocalInterruptionStatus = nullptr; + //thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine: + thread_local InterruptionStatus* threadLocalInterruptionStatus = nullptr; return threadLocalInterruptionStatus; } } @@ -457,26 +501,6 @@ InterruptibleThread::InterruptibleThread(Function&& f) inline void InterruptibleThread::interrupt() { intStatus_->interrupt(); } - - - - -inline -void setCurrentThreadName(const char* threadName) -{ - ::prctl(PR_SET_NAME, threadName, 0, 0, 0); - -} - - -inline -uint64_t getThreadId() -{ - //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach: - static_assert(sizeof(uint64_t) >= sizeof(void*), ""); - return reinterpret_cast<uint64_t>(static_cast<void*>(&errno)); - -} } #endif //THREAD_H_7896323423432235246427 @@ -217,12 +217,11 @@ struct PredefinedFormatTag {}; template <class String, class String2> inline String formatTime(const String2& format, const TimeComp& tc, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure { - using CharType = typename GetCharType<String>::Type; std::tm ctc = toClibTimeComponents(tc); 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] = {}; + GetCharTypeT<String> buffer[256] = {}; const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc); return String(buffer, charsWritten); } @@ -231,8 +230,7 @@ String formatTime(const String2& format, const TimeComp& tc, UserDefinedFormatTa template <class String, class FormatType> inline String formatTime(FormatType, const TimeComp& tc, PredefinedFormatTag) { - using CharType = typename GetCharType<String>::Type; - return formatTime<String>(GetFormat<FormatType>().format(CharType()), tc, UserDefinedFormatTag()); + return formatTime<String>(GetFormat<FormatType>().format(GetCharTypeT<String>()), tc, UserDefinedFormatTag()); } } @@ -306,13 +304,13 @@ String formatTime(const String2& format, const TimeComp& tc) if (tc == TimeComp()) //failure code from getLocalTime() return String(); - using FormatTag = typename SelectIf< - IsSameType<String2, FormatDateTag >::value || - IsSameType<String2, FormatTimeTag >::value || - IsSameType<String2, FormatDateTimeTag >::value || - IsSameType<String2, FormatIsoDateTag >::value || - IsSameType<String2, FormatIsoTimeTag >::value || - IsSameType<String2, FormatIsoDateTimeTag>::value, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>::Type; + using FormatTag = std::conditional_t< + std::is_same_v<String2, FormatDateTag > || + std::is_same_v<String2, FormatTimeTag > || + std::is_same_v<String2, FormatDateTimeTag > || + std::is_same_v<String2, FormatIsoDateTag > || + std::is_same_v<String2, FormatIsoTimeTag > || + std::is_same_v<String2, FormatIsoDateTimeTag>, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>; return impl::formatTime<String>(format, tc, FormatTag()); } @@ -323,8 +321,8 @@ namespace impl template <class String, class String2> TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTag) { - using CharType = typename GetCharType<String>::Type; - static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, ""); + using CharType = GetCharTypeT<String>; + static_assert(std::is_same_v<CharType, GetCharTypeT<String2>>); const CharType* itStr = strBegin(str); const CharType* const strLast = itStr + strLength(str); @@ -429,8 +427,7 @@ TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTa template <class FormatType, class String> inline TimeComp parseTime(FormatType, const String& str, PredefinedFormatTag) { - using CharType = typename GetCharType<String>::Type; - return parseTime(GetFormat<FormatType>().format(CharType()), str, UserDefinedFormatTag()); + return parseTime(GetFormat<FormatType>().format(GetCharTypeT<String>()), str, UserDefinedFormatTag()); } } @@ -438,10 +435,10 @@ TimeComp parseTime(FormatType, const String& str, PredefinedFormatTag) template <class String, class String2> inline TimeComp parseTime(const String& format, const String2& str) { - using FormatTag = typename SelectIf< - IsSameType<String, FormatIsoDateTag >::value || - IsSameType<String, FormatIsoTimeTag >::value || - IsSameType<String, FormatIsoDateTimeTag>::value, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>::Type; + using FormatTag = std::conditional_t< + std::is_same_v<String, FormatIsoDateTag > || + std::is_same_v<String, FormatIsoTimeTag > || + std::is_same_v<String, FormatIsoDateTimeTag>, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>; return impl::parseTime(format, str, FormatTag()); } diff --git a/zen/type_tools.h b/zen/type_tools.h deleted file mode 100755 index a705d9bd..00000000 --- a/zen/type_tools.h +++ /dev/null @@ -1,103 +0,0 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef TYPE_TOOLS_H_45237590734254545 -#define TYPE_TOOLS_H_45237590734254545 - -#include "type_traits.h" - - -namespace zen -{ -//########## Strawman Classes ########################## -struct NullType {}; //:= no type here - -//########## Type Mapping ############################## -template <int n> -struct Int2Type {}; -//------------------------------------------------------ -template <class T> -struct Type2Type {}; - -//########## Control Structures ######################## -template <bool flag, class T, class U> -struct SelectIf : ResultType<T> {}; - -template <class T, class U> -struct SelectIf<false, T, U> : ResultType<U> {}; -//------------------------------------------------------ -template <class T, class U> -struct IsSameType : FalseType {}; - -template <class T> -struct IsSameType<T, T> : TrueType {}; - -//------------------------------------------------------ -template <bool, class T = void> -struct EnableIf {}; - -template <class T> -struct EnableIf<true, T> : ResultType<T> {}; -//########## Type Cleanup ############################## -template <class T> -struct RemoveRef : ResultType<T> {}; - -template <class T> -struct RemoveRef<T&> : ResultType<T> {}; - -template <class T> -struct RemoveRef<T&&> : ResultType<T> {}; -//------------------------------------------------------ -template <class T> -struct RemoveConst : ResultType<T> {}; - -template <class T> -struct RemoveConst<const T> : ResultType<T> {}; -//------------------------------------------------------ -template <class T> -struct RemovePointer : ResultType<T> {}; - -template <class T> -struct RemovePointer<T*> : ResultType<T> {}; -//------------------------------------------------------ -template <class T> -struct RemoveArray : ResultType<T> {}; - -template <class T, int N> -struct RemoveArray<T[N]> : ResultType<T> {}; - -//########## Sorting ############################## -/* -Generate a descending binary predicate at compile time! - -Usage: - static const bool ascending = ... - makeSortDirection(old binary predicate, Int2Type<ascending>()) -> new binary predicate - -or directly; - makeDescending(old binary predicate) -> new binary predicate -*/ - -template <class Predicate> -struct LessDescending -{ - LessDescending(Predicate lessThan) : lessThan_(lessThan) {} - template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); } -private: - Predicate lessThan_; -}; - -template <class Predicate> inline -/**/ Predicate makeSortDirection(Predicate pred, Int2Type<true>) { return pred; } - -template <class Predicate> inline -LessDescending<Predicate> makeSortDirection(Predicate pred, Int2Type<false>) { return pred; } - -template <class Predicate> inline -LessDescending<Predicate> makeDescending(Predicate pred) { return pred; } -} - -#endif //TYPE_TOOLS_H_45237590734254545 diff --git a/zen/type_traits.h b/zen/type_traits.h index 83a74d1e..2d4e7a97 100755 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -7,41 +7,42 @@ #ifndef TYPE_TRAITS_H_3425628658765467 #define TYPE_TRAITS_H_3425628658765467 -#include <type_traits> //all we need is std::is_class!! +#include <type_traits> +#include "legacy_compiler.h" +//http://en.cppreference.com/w/cpp/header/type_traits namespace zen { -//################# TMP compile time return values: "inherit to return compile-time result" ############## -template <int i> -struct StaticInt +template<class T, class...> +struct GetFirstOf { - enum { value = i }; + using Type = T; }; +template<class... T> using GetFirstOfT = typename GetFirstOf<T...>::Type; -template <bool b> -struct StaticBool : StaticInt<b> {}; - -using TrueType = StaticBool<true>; -using FalseType = StaticBool<false>; - -template <class EnumType, EnumType val> -struct StaticEnum +template <class F> +class FunctionReturnType { - static const EnumType value = val; -}; -//--------------------------------------------------------- -template <class T> -struct ResultType -{ - using Type = T; + template <class R, class... Args> static R dummyFun(R(*)(Args...)); +public: + using Type = decltype(dummyFun(F())); }; +template<class F> using FunctionReturnTypeT = typename FunctionReturnType<F>::Type; -template<class T, class...> -struct GetFirstOf +//============================================================================= + +template<class T, size_t N> +constexpr size_t arraySize(T (&)[N]) { return N; } + +template<class S, class T, size_t N> +constexpr S arrayAccumulate(T (&arr)[N]) { - using Type = T; -}; + S sum = 0; + for (size_t i = 0; i < N; ++i) + sum += arr[i]; + return sum; +} //Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/ template<class T> inline auto makeSigned (T t) { return static_cast<std::make_signed_t <T>>(t); } @@ -50,16 +51,19 @@ template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_u //################# Built-in Types ######################## //Example: "IsSignedInt<int>::value" evaluates to "true" +//unfortunate standardized nonsense: std::is_integral<> includes bool, char, wchar_t! => roll our own: template <class T> struct IsUnsignedInt; template <class T> struct IsSignedInt; -template <class T> struct IsFloat; -template <class T> struct IsInteger; //IsSignedInt or IsUnsignedInt -template <class T> struct IsArithmetic; //IsInteger or IsFloat +template <class T> using IsFloat = std::is_floating_point<T>; +template <class T> using IsInteger = std::bool_constant<IsUnsignedInt<T>::value || IsSignedInt<T>::value>; +template <class T> using IsArithmetic = std::bool_constant<IsInteger <T>::value || IsFloat <T>::value>; + //remaining non-arithmetic types: bool, char, wchar_t + //optional: specialize new types like: -//template <> struct IsUnsignedInt<UInt64> : TrueType {}; +//template <> struct IsUnsignedInt<UInt64> : std::true_type {}; //################# Class Members ######################## @@ -80,13 +84,29 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat 2. HasMemberType_value_type<T>::value -> use as boolean */ +//########## Sorting ############################## +/* +Generate a descending binary predicate at compile time! +Usage: + static const bool ascending = ... + makeSortDirection(old binary predicate, std::bool_constant<ascending>()) -> new binary predicate +*/ +template <class Predicate> +struct LessDescending +{ + LessDescending(Predicate lessThan) : lessThan_(lessThan) {} + template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); } +private: + Predicate lessThan_; +}; +template <class Predicate> inline +/**/ Predicate makeSortDirection(Predicate pred, std::true_type) { return pred; } - - - +template <class Predicate> inline +LessDescending<Predicate> makeSortDirection(Predicate pred, std::false_type) { return pred; } @@ -95,43 +115,23 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //################ implementation ###################### -#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : TrueType {}; - template <class T> -struct IsUnsignedInt : FalseType {}; +struct IsUnsignedInt : std::false_type {}; -ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char); -ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int); -ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned int); -ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long int); -ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 - same type as unsigned __int64 in VS2010 -//------------------------------------------------------ +template <> struct IsUnsignedInt<unsigned char > : std::true_type {}; +template <> struct IsUnsignedInt<unsigned short int > : std::true_type {}; +template <> struct IsUnsignedInt<unsigned int > : std::true_type {}; +template <> struct IsUnsignedInt<unsigned long int > : std::true_type {}; +template <> struct IsUnsignedInt<unsigned long long int> : std::true_type {}; template <class T> -struct IsSignedInt : FalseType {}; - -ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char); -ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int); -ZEN_SPECIALIZE_TRAIT(IsSignedInt, int); -ZEN_SPECIALIZE_TRAIT(IsSignedInt, long int); -ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type as __int64 in VS2010 -//------------------------------------------------------ +struct IsSignedInt : std::false_type {}; -template <class T> -struct IsFloat : FalseType {}; - -ZEN_SPECIALIZE_TRAIT(IsFloat, float); -ZEN_SPECIALIZE_TRAIT(IsFloat, double); -ZEN_SPECIALIZE_TRAIT(IsFloat, long double); -//------------------------------------------------------ - -#undef ZEN_SPECIALIZE_TRAIT - -template <class T> -struct IsInteger : StaticBool<IsUnsignedInt<T>::value || IsSignedInt<T>::value> {}; - -template <class T> -struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; +template <> struct IsSignedInt<signed char > : std::true_type {}; +template <> struct IsSignedInt<short int > : std::true_type {}; +template <> struct IsSignedInt<int > : std::true_type {}; +template <> struct IsSignedInt<long int > : std::true_type {}; +template <> struct IsSignedInt<long long int> : std::true_type {}; //#################################################################### #define ZEN_INIT_DETECT_MEMBER(NAME) \ @@ -145,6 +145,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; \ template <typename U, U t> \ class Helper {}; \ + \ struct Fallback { int NAME; }; \ \ template <class U> \ @@ -156,11 +157,11 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \ }; \ \ - template<class T> \ - struct HasMemberImpl_##NAME<false, T> : FalseType {}; \ + template<class T> \ + struct HasMemberImpl_##NAME<false, T> : std::false_type {}; \ \ - template<typename T> \ - struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {}; + template<typename T> \ + struct HasMember_##NAME : std::bool_constant<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {}; //#################################################################### @@ -53,6 +53,8 @@ const CodePoint TRAIL_SURROGATE_MAX = 0xdfff; const CodePoint REPLACEMENT_CHAR = 0xfffd; const CodePoint CODE_POINT_MAX = 0x10ffff; +static_assert(LEAD_SURROGATE + TRAIL_SURROGATE + TRAIL_SURROGATE_MAX + REPLACEMENT_CHAR + CODE_POINT_MAX == 1348603); + template <class Function> inline void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char16 @@ -245,14 +247,14 @@ private: //---------------------------------------------------------------------------------------------------------------- -template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char -template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t -template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<4>) { writeOutput(cp); } //other OS: UTF32-wchar_t +template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, std::integral_constant<int, 1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char +template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, std::integral_constant<int, 2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t +template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, std::integral_constant<int, 4>) { writeOutput(cp); } //other OS: UTF32-wchar_t template <class CharType, class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a CharType { - return codePointToUtf(cp, writeOutput, Int2Type<sizeof(CharType)>()); + return codePointToUtf(cp, writeOutput, std::integral_constant<int, sizeof(CharType)>()); } //---------------------------------------------------------------------------------------------------------------- @@ -311,7 +313,7 @@ bool isValidUtf(const UtfString& str) { using namespace impl; - UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str)); + UtfDecoder<GetCharTypeT<UtfString>> decoder(strBegin(str), strLength(str)); while (Opt<CodePoint> cp = decoder.getNext()) if (*cp == REPLACEMENT_CHAR) return false; @@ -324,7 +326,7 @@ template <class UtfString> inline size_t unicodeLength(const UtfString& str) //return number of code points (+ correctly handle broken UTF encoding) { size_t uniLen = 0; - impl::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str)); + impl::UtfDecoder<GetCharTypeT<UtfString>> decoder(strBegin(str), strLength(str)); while (decoder.getNext()) ++uniLen; return uniLen; @@ -336,7 +338,7 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u { assert(uniPosFirst <= uniPosLast && uniPosLast <= unicodeLength(str)); using namespace impl; - using CharType = typename GetCharType<UtfString>::Type; + using CharType = GetCharTypeT<UtfString>; UtfString output; if (uniPosFirst >= uniPosLast) //optimize for empty range return output; @@ -357,11 +359,11 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u namespace impl { template <class TargetString, class SourceString> inline -TargetString utfTo(const SourceString& str, FalseType) +TargetString utfTo(const SourceString& str, std::false_type) { - using CharSrc = typename GetCharType<SourceString>::Type; - using CharTrg = typename GetCharType<TargetString>::Type; - static_assert(sizeof(CharSrc) != sizeof(CharTrg), "no UTF-conversion needed"); + using CharSrc = GetCharTypeT<SourceString>; + using CharTrg = GetCharTypeT<TargetString>; + static_assert(sizeof(CharSrc) != sizeof(CharTrg)); TargetString output; @@ -374,14 +376,14 @@ TargetString utfTo(const SourceString& str, FalseType) template <class TargetString, class SourceString> inline -TargetString utfTo(const SourceString& str, TrueType) { return copyStringTo<TargetString>(str); } +TargetString utfTo(const SourceString& str, std::true_type) { return copyStringTo<TargetString>(str); } } template <class TargetString, class SourceString> inline TargetString utfTo(const SourceString& str) { - return impl::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>()); + return impl::utfTo<TargetString>(str, std::bool_constant<sizeof(GetCharTypeT<SourceString>) == sizeof(GetCharTypeT<TargetString>)>()); } } diff --git a/zen/warn_static.h b/zen/warn_static.h index e4931c08..6f0a2691 100755 --- a/zen/warn_static.h +++ b/zen/warn_static.h @@ -14,13 +14,11 @@ Usage: warn_static("my message") */ -#if defined __GNUC__ -#define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y -#define STATIC_WARNING_CONCAT(X, Y) STATIC_WARNING_CONCAT_SUB(X, Y) +#define ZEN_STATIC_WARNING_STRINGIZE(NUM) #NUM -#define warn_static(TXT) \ - typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \ - enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) }; +#if defined __GNUC__ //Clang also defines __GNUC__! +#define warn_static(MSG) \ + _Pragma(ZEN_STATIC_WARNING_STRINGIZE(GCC warning MSG)) #endif #endif //WARN_STATIC_H_08724567834560832745 diff --git a/zen/zstring.cpp b/zen/zstring.cpp index afa62c93..1fe31eaf 100755 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -54,8 +54,8 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh if (!cpL || !cpR) return static_cast<int>(!cpR) - static_cast<int>(!cpL); -//support unit-testing on Windows: CodePoint is truncated to wchar_t -static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint), ""); + //support unit-testing on Windows: CodePoint is truncated to wchar_t + static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint)); const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper() const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale diff --git a/zen/zstring.h b/zen/zstring.h index 5a6ecbdd..cb19318c 100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -18,6 +18,9 @@ //a high-performance string for interfacing with native OS APIs in multithreaded contexts using Zstring = zen::Zbase<Zchar>; + //for special UI-contexts: guaranteed exponential growth + ref-counting +using Zstringw = zen::Zbase<wchar_t>; + //Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES struct CmpFilePath @@ -131,8 +134,8 @@ template <class S, class T, class U> inline S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm) { using namespace zen; - static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); - static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, ""); + static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); + static_assert(std::is_same_v<GetCharTypeT<T>, GetCharTypeT<U>>); const size_t oldLen = strLength(oldTerm); if (oldLen == 0) return str; |