summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rwxr-xr-xzen/basic_math.h10
-rwxr-xr-xzen/build_info.h4
-rwxr-xr-xzen/crc.h9
-rwxr-xr-xzen/deprecate.h18
-rwxr-xr-xzen/dir_watcher.cpp2
-rwxr-xr-xzen/error_log.h8
-rwxr-xr-xzen/file_access.cpp16
-rwxr-xr-xzen/file_io.cpp12
-rwxr-xr-xzen/file_io.h4
-rwxr-xr-xzen/fixed_list.h240
-rwxr-xr-xzen/format_unit.cpp2
-rwxr-xr-xzen/globals.h5
-rwxr-xr-xzen/guid.h12
-rwxr-xr-xzen/http.cpp376
-rwxr-xr-xzen/http.h48
-rwxr-xr-xzen/i18n.h2
-rwxr-xr-xzen/legacy_compiler.h89
-rwxr-xr-xzen/optional.h21
-rwxr-xr-xzen/perf.h7
-rwxr-xr-xzen/ring_buffer.h230
-rwxr-xr-xzen/scope_guard.h32
-rwxr-xr-xzen/serialize.h31
-rwxr-xr-xzen/socket.h84
-rwxr-xr-xzen/stl_tools.h34
-rwxr-xr-xzen/string_base.h24
-rwxr-xr-xzen/string_tools.h95
-rwxr-xr-xzen/string_traits.h83
-rwxr-xr-xzen/sys_error.h1
-rwxr-xr-xzen/thread.cpp55
-rwxr-xr-xzen/thread.h154
-rwxr-xr-xzen/time.h35
-rwxr-xr-xzen/type_tools.h103
-rwxr-xr-xzen/type_traits.h135
-rwxr-xr-xzen/utf.h28
-rwxr-xr-xzen/warn_static.h10
-rwxr-xr-xzen/zstring.cpp4
-rwxr-xr-xzen/zstring.h7
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
diff --git a/zen/crc.h b/zen/crc.h
index 6f6ca996..ebac538f 100755
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -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
diff --git a/zen/guid.h b/zen/guid.h
index 6cffc708..50ca64d2 100755
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -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
diff --git a/zen/i18n.h b/zen/i18n.h
index b67a3bb6..45762861 100755
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -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; }
diff --git a/zen/perf.h b/zen/perf.h
index 2006bdab..40b6533d 100755
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -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
diff --git a/zen/time.h b/zen/time.h
index 723614ef..b06d3d15 100755
--- a/zen/time.h
+++ b/zen/time.h
@@ -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> {};
//####################################################################
diff --git a/zen/utf.h b/zen/utf.h
index f8ee91d5..48269416 100755
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -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;
bgstack15