summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2018-07-29 21:45:13 -0700
committerDaniel Wilhelm <shieldwed@outlook.com>2018-07-29 21:45:13 -0700
commit06c99e6c91d87a91f3e54191670d23e3f4d132b9 (patch)
treea2c82149d6fe802c68365f73e11f3c241c95ef3d /zen
parent10.1 (diff)
downloadFreeFileSync-06c99e6c91d87a91f3e54191670d23e3f4d132b9.tar.gz
FreeFileSync-06c99e6c91d87a91f3e54191670d23e3f4d132b9.tar.bz2
FreeFileSync-06c99e6c91d87a91f3e54191670d23e3f4d132b9.zip
10.2
Diffstat (limited to 'zen')
-rwxr-xr-xzen/crc.h89
-rwxr-xr-xzen/file_access.cpp17
-rwxr-xr-xzen/file_io.cpp8
-rwxr-xr-xzen/guid.h55
-rwxr-xr-xzen/perf.h51
-rwxr-xr-xzen/ring_buffer.h12
-rwxr-xr-xzen/serialize.h2
-rwxr-xr-xzen/stl_tools.h10
-rwxr-xr-xzen/string_tools.h8
-rwxr-xr-xzen/string_traits.h8
-rwxr-xr-xzen/thread.h56
-rwxr-xr-xzen/warn_static.h2
-rwxr-xr-xzen/zstring.h18
13 files changed, 256 insertions, 80 deletions
diff --git a/zen/crc.h b/zen/crc.h
index ebac538f..df460a03 100755
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -7,8 +7,7 @@
#ifndef CRC_H_23489275827847235
#define CRC_H_23489275827847235
-//boost, clean this mess up!
-#include <boost/crc.hpp>
+#include "type_traits.h"
namespace zen
@@ -27,28 +26,86 @@ inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(),
template <class ByteIterator> inline
-uint16_t getCrc16(ByteIterator first, ByteIterator last)
+uint16_t getCrc16(ByteIterator first, ByteIterator last) //http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
{
+ constexpr uint16_t crcTable[] =
+ {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+ };
+ static_assert(arraySize(crcTable) == 256 && arrayAccumulate<uint32_t>(crcTable) == 8380544);
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));
- return rv;
+
+ uint16_t crc = 0;
+ std::for_each(first, last, [&](unsigned char b)
+ {
+ crc = (crc >> 8) ^ crcTable[(crc ^ b) & 0xFF];
+ });
+ return crc;
}
template <class ByteIterator> inline
-uint32_t getCrc32(ByteIterator first, ByteIterator last)
+uint32_t getCrc32(ByteIterator first, ByteIterator last) //https://en.wikipedia.org/wiki/Cyclic_redundancy_check
{
+ constexpr uint32_t crcTable[] =
+ {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+ };
+ static_assert(arraySize(crcTable) == 256 && arrayAccumulate<uint64_t>(crcTable) == 549755813760);
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));
- return rv;
+
+ uint32_t crc = 0xFFFFFFFF;
+ std::for_each(first, last, [&](unsigned char b)
+ {
+ crc = (crc >> 8) ^ crcTable[(crc ^ b) & 0xFF];
+ });
+ return crc ^ 0xFFFFFFFF;
}
}
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index fca1e3d8..9c351e25 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -14,8 +14,8 @@
#include "symlink_target.h"
#include "file_id_def.h"
#include "file_io.h"
-#include "crc.h" //boost dependency!
-#include "guid.h" //
+#include "crc.h"
+#include "guid.h"
#include <sys/vfs.h> //statfs
#include <sys/time.h> //lutimes
@@ -34,7 +34,7 @@ Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
{
auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> Opt<PathComponents>
{
- const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without seperator, e.g. \\server-name\share
+ const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without separator, e.g. \\server-name\share
int sepCount = 0;
for (auto it = itemPathFmt.begin(); it != itemPathFmt.end(); ++it)
if (*it == FILE_NAME_SEPARATOR)
@@ -347,8 +347,11 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
if (!equalFilePath(pathSource, pathTarget)) //exception for OS X: changing file name case is not an "already exists" situation!
{
- bool alreadyExists = true;
- try { /*ItemType type = */getItemType(pathTarget); } /*throw FileError*/ catch (FileError&) { alreadyExists = false; }
+ const bool alreadyExists = [&]
+ {
+ try { /*ItemType type = */getItemType(pathTarget); return true; } /*throw FileError*/
+ catch (FileError&) { return false; }
+ }();
if (alreadyExists)
throwException(EEXIST);
@@ -422,7 +425,7 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim
return;
//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!
+ const int fdFile = ::open(itemPath.c_str(), O_WRONLY | O_APPEND | O_CLOEXEC); //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");
ZEN_ON_SCOPE_EXIT(::close(fdFile));
@@ -636,7 +639,7 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError,
//it seems we don't need S_IWUSR, not even for the setFileTime() below! (tested with source file having different user/group!)
//=> need copyItemPermissions() only for "chown" and umask-agnostic permissions
- const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode);
+ const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
if (fdTarget == -1)
{
const int ec = errno; //copy before making other system calls!
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 25dc93ce..df47e4c5 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -8,8 +8,8 @@
#include "file_access.h"
#include <sys/stat.h>
- #include <fcntl.h> //open, close
- #include <unistd.h> //read, write
+ #include <fcntl.h> //open
+ #include <unistd.h> //close, read, write
using namespace zen;
@@ -80,7 +80,7 @@ FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileErro
checkForUnsupportedType(filePath); //throw FileError; opening a named pipe would block forever!
//don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html
- const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_RDONLY);
+ const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_RDONLY | O_CLOEXEC);
if (fileHandle == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filePath)), L"open");
return fileHandle; //pass ownership
@@ -180,7 +180,7 @@ FileBase::FileHandle openHandleForWrite(const Zstring& filePath, FileOutput::Acc
{
//checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast
- const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_WRONLY | O_CREAT | (access == FileOutput::ACC_CREATE_NEW ? O_EXCL : O_TRUNC),
+ const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | (access == FileOutput::ACC_CREATE_NEW ? O_EXCL : O_TRUNC),
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //0666
if (fileHandle == -1)
{
diff --git a/zen/guid.h b/zen/guid.h
index 50ca64d2..89e800b5 100755
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -7,21 +7,58 @@
#ifndef GUID_H_80425780237502345
#define GUID_H_80425780237502345
-#include <string>
- #include <boost/uuid/uuid_generators.hpp>
+ #include <fcntl.h> //open
+ #include <unistd.h> //close
+ //#include <uuid/uuid.h> -> uuid_generate(), uuid_unparse(); avoid additional dependency for "sudo apt-get install uuid-dev"
+
namespace zen
{
inline
std::string generateGUID() //creates a 16-byte GUID
{
- //perf: generator: 0.38ms per creation;
- // 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());
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25) //getentropy() requires glibc 2.25 (ldd --version) PS: Centos 7 is on 2.17
+ std::string guid(16, '\0');
+ if (::getentropy(&guid[0], 16) != 0) //"The maximum permitted value for the length argument is 256"
+ throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] Failed to generate GUID." +
+ "\n" + utfTo<std::string>(formatSystemError(L"getentropy", errno)));
+ return guid;
+
+#else
+ class RandomGeneratorPosix
+ {
+ public:
+ RandomGeneratorPosix()
+ {
+ if (fd_ == -1)
+ throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] Failed to generate GUID." +
+ "\n" + utfTo<std::string>(formatSystemError(L"open", errno)));
+ }
+
+ ~RandomGeneratorPosix() { ::close(fd_); }
+
+ void getBytes(void* buf, size_t size)
+ {
+ for (size_t offset = 0; offset < size; )
+ {
+ const ssize_t bytesRead = ::read(fd_, static_cast<char*>(buf) + offset, size - offset);
+ if (bytesRead < 1) //0 means EOF => error in this context (should check for buffer overflow, too?)
+ throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] Failed to generate GUID." +
+ "\n" + utfTo<std::string>(formatSystemError(L"read", bytesRead < 0 ? errno : EIO)));
+ offset += bytesRead;
+ assert(offset <= size);
+ }
+ }
+
+ private:
+ const int fd_ = ::open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+ };
+ thread_local RandomGeneratorPosix gen;
+ std::string guid(16, '\0');
+ gen.getBytes(&guid[0], 16);
+ return guid;
+#endif
+
}
}
diff --git a/zen/perf.h b/zen/perf.h
index 40b6533d..77251f8c 100755
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -28,19 +28,28 @@
namespace zen
{
-class PerfTimer
+
+//issue with wxStopWatch? https://freefilesync.org/forum/viewtopic.php?t=1426
+// => wxStopWatch implementation uses QueryPerformanceCounter: https://github.com/wxWidgets/wxWidgets/blob/17d72a48ffd4d8ff42eed070ac48ee2de50ceabd/src/common/stopwatch.cpp
+// => whatever the problem was, it's almost certainly not caused by QueryPerformanceCounter():
+// MSDN: "How often does QPC roll over? Not less than 100 years from the most recent system boot"
+// https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408#How_often_does_QPC_roll_over_
+//
+// => using the system clock is problematic: https://freefilesync.org/forum/viewtopic.php?t=5280
+//
+// std::chrono::system_clock wraps ::GetSystemTimePreciseAsFileTime()
+// std::chrono::steady_clock wraps ::QueryPerformanceCounter()
+class StopWatch
{
public:
- [[deprecated]] PerfTimer() {}
-
- ~PerfTimer() { if (!resultShown_) showResult(); }
+ bool isPaused() const { return paused_; }
void pause()
{
if (!paused_)
{
paused_ = true;
- elapsedUntilPause_ += std::chrono::steady_clock::now() - startTime_; //ignore potential ::QueryPerformanceCounter() wrap-around!
+ elapsedUntilPause_ += std::chrono::steady_clock::now() - startTime_;
}
}
@@ -60,31 +69,43 @@ public:
elapsedUntilPause_ = std::chrono::nanoseconds::zero();
}
- int64_t timeMs() const
+ std::chrono::nanoseconds elapsed() const
{
auto elapsedTotal = elapsedUntilPause_;
if (!paused_)
elapsedTotal += std::chrono::steady_clock::now() - startTime_;
-
- return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTotal).count();
+ return elapsedTotal;
}
+private:
+ bool paused_ = false;
+ std::chrono::steady_clock::time_point startTime_ = std::chrono::steady_clock::now();
+ std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end???
+};
+
+
+class PerfTimer
+{
+public:
+ [[deprecated]] PerfTimer() {}
+
+ ~PerfTimer() { if (!resultShown_) showResult(); }
+
void showResult()
{
- const bool wasRunning = !paused_;
- if (wasRunning) pause(); //don't include call to MessageBox()!
- ZEN_ON_SCOPE_EXIT(if (wasRunning) resume());
+ const bool wasRunning = !watch_.isPaused();
+ if (wasRunning) watch_.pause(); //don't include call to MessageBox()!
+ ZEN_ON_SCOPE_EXIT(if (wasRunning) watch_.resume());
- const std::string msg = numberTo<std::string>(timeMs()) + " ms";
+ const int64_t timeMs = std::chrono::duration_cast<std::chrono::milliseconds>(watch_.elapsed()).count();
+ const std::string msg = numberTo<std::string>(timeMs) + " ms";
std::clog << "Perf: duration: " << msg << "\n";
resultShown_ = true;
}
private:
+ StopWatch watch_;
bool resultShown_ = false;
- bool paused_ = false;
- std::chrono::steady_clock::time_point startTime_ = std::chrono::steady_clock::now(); //uses ::QueryPerformanceCounter()
- std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end???
};
}
diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h
index 1a67c452..6debd84e 100755
--- a/zen/ring_buffer.h
+++ b/zen/ring_buffer.h
@@ -163,16 +163,26 @@ public:
class Iterator
{
public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = Value;
+ using difference_type = ptrdiff_t;
+ using pointer = Value*;
+ using reference = Value&;
+
Iterator(Container& container, size_t offset) : container_(&container), offset_(offset) {}
Iterator& operator++() { ++offset_; return *this; }
+ Iterator& operator+=(ptrdiff_t offset) { offset_ += offset; }
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); }
+ inline friend ptrdiff_t operator-(const Iterator& lhs, const Iterator& rhs) { return lhs.offset_ - rhs.offset_; }
+ inline friend Iterator operator+(const Iterator& lhs, ptrdiff_t offset) { Iterator tmp(lhs); return tmp += offset; }
Value& operator* () const { return (*container_)[offset_]; }
Value* operator->() const { return &(*container_)[offset_]; }
private:
Container* container_ = nullptr;
- size_t offset_ = 0;
+ ptrdiff_t offset_ = 0;
};
+
using iterator = Iterator< RingBuffer, T>;
using const_iterator = Iterator<const RingBuffer, const T>;
diff --git a/zen/serialize.h b/zen/serialize.h
index 16375cff..d34b61b2 100755
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -90,7 +90,7 @@ template <class BinContainer, class BufferedInputStream> BinContainer
bufferedLoad(BufferedInputStream& streamIn); //throw X
template <class N, class BufferedOutputStream> void writeNumber (BufferedOutputStream& stream, const N& num); //
-template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //throw ()
+template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //noexcept
template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* buffer, size_t len); //
//----------------------------------------------------------------------
class UnexpectedEndOfStreamError {};
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 2ce2cf33..7365392f 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -43,8 +43,8 @@ template <class T, class Alloc>
void removeDuplicates(std::vector<T, Alloc>& v);
//binary search returning an iterator
-template <class ForwardIterator, class T, typename CompLess>
-ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less);
+template <class Iterator, class T, typename CompLess>
+Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less);
template <class BidirectionalIterator, class T>
BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
@@ -125,9 +125,11 @@ void removeDuplicates(std::vector<T, Alloc>& v)
}
-template <class ForwardIterator, class T, typename CompLess> inline
-ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less)
+template <class Iterator, class T, typename CompLess> inline
+Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less)
{
+ static_assert(std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>);
+
first = std::lower_bound(first, last, value, less);
if (first != last && !less(value, *first))
return first;
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 58cb6ea6..e09cb61f 100755
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -14,7 +14,7 @@
#include <algorithm>
#include <cassert>
#include <vector>
-#include <sstream>
+#include <sstream> //std::basic_ostringstream
#include "stl_tools.h"
#include "string_traits.h"
@@ -446,7 +446,11 @@ namespace impl
template <class S, class T>
struct CopyStringToString
{
- T copy(const S& src) const { return T(strBegin(src), strLength(src)); }
+ T copy(const S& src) const
+ {
+ static_assert(!std::is_same_v<std::decay_t<S>, std::decay_t<T>>);
+ return T(strBegin(src), strLength(src));
+ }
};
template <class T>
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 8187126d..cd7dbf1b 100755
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -41,10 +41,14 @@ class StringRef
{
public:
template <class Iterator>
- StringRef(Iterator first, Iterator last) : len_(last - first), str_(first != last ? &*first : nullptr) {}
+ StringRef(Iterator first, Iterator last) : len_(last - first),
+ str_(first != last ? &*first : reinterpret_cast<Char*>(this) /*Win32 APIs like CompareStringOrdinal() choke on nullptr!*/)
+ {
+ static_assert(alignof(StringRef) % alignof(Char) == 0); //even though str_ is never dereferenced, make sure the pointer value respects alignment (why? because we can)
+ }
//StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range!
- Char* data () const { return str_; } //1. no null-termination! 2. may be nullptr!
+ Char* data () const { return str_; } //no null-termination!
size_t length() const { return len_; }
private:
diff --git a/zen/thread.h b/zen/thread.h
index ee36f305..5828d07a 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -119,7 +119,7 @@ class Protected
{
public:
Protected() {}
- Protected(const T& value) : value_(value) {}
+ Protected(T& value) : value_(value) {}
//Protected( T&& tmp ) : value_(std::move(tmp)) {} <- wait until needed
template <class Function>
@@ -161,28 +161,46 @@ public:
ThreadGroup& operator=(ThreadGroup&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!!
- //context of controlling thread, non-blocking:
- void run(Function&& wi)
+ //context of controlling OR worker thread, non-blocking:
+ void run(Function&& wi /*should throw ThreadInterruption when needed*/)
{
- size_t tasksPending = 0;
{
std::lock_guard<std::mutex> dummy(workLoad_->lock);
+
workLoad_->tasks.push_back(std::move(wi));
- tasksPending = ++(workLoad_->tasksPending);
+ const size_t tasksPending = ++(workLoad_->tasksPending);
+
+ if (worker_.size() < std::min(tasksPending, threadCountMax_))
+ addWorkerThread();
}
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; });
+ //perf: no difference in xBRZ test case compared to std::condition_variable-based implementation
+ auto promiseDone = std::make_shared<std::promise<void>>(); //
+ std::future<void> allDone = promiseDone->get_future();
+
+ notifyWhenDone([promiseDone] { promiseDone->set_value(); }); //std::function doesn't support construction involving move-only types!
+ //use reference? => not guaranteed safe, e.g. promise object theoretically might be accessed inside set_value() after future gets signalled
+
+ allDone.get();
+ }
+
+ //non-blocking wait()-alternative: context of controlling thread:
+ void notifyWhenDone(const std::function<void()>& onCompletion /*noexcept! runs on worker thread!*/)
+ {
+ std::lock_guard<std::mutex> dummy(workLoad_->lock);
+
+ if (workLoad_->tasksPending == 0)
+ onCompletion();
+ else
+ workLoad_->onCompletionCallbacks.push_back(onCompletion);
}
+ //context of controlling thread:
void detach() { detach_ = true; } //not expected to also interrupt!
private:
@@ -204,13 +222,21 @@ private:
Function task = std::move(wl->tasks. front()); //noexcept thanks to move
/**/ wl->tasks.pop_front(); //
- dummy.unlock();
-
- task();
+ dummy.unlock();
+ task(); //throw ThreadInterruption?
dummy.lock();
+
if (--(wl->tasksPending) == 0)
- wl->conditionTasksDone.notify_all(); //too difficult to notify outside the lock
+ if (!wl->onCompletionCallbacks.empty())
+ {
+ std::vector<std::function<void()>> callbacks;
+ callbacks.swap(wl->onCompletionCallbacks);
+
+ dummy.unlock();
+ for (const auto& cb : callbacks) cb(); //noexcept!
+ dummy.lock();
+ }
}
});
}
@@ -230,7 +256,7 @@ private:
RingBuffer<Function> tasks; //FIFO! :)
size_t tasksPending = 0;
std::condition_variable conditionNewTask;
- std::condition_variable conditionTasksDone;
+ std::vector<std::function<void()>> onCompletionCallbacks;
};
std::vector<InterruptibleThread> worker_;
diff --git a/zen/warn_static.h b/zen/warn_static.h
index 6f0a2691..fb8fbb95 100755
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -18,7 +18,7 @@ Usage:
#if defined __GNUC__ //Clang also defines __GNUC__!
#define warn_static(MSG) \
- _Pragma(ZEN_STATIC_WARNING_STRINGIZE(GCC warning MSG))
+ _Pragma(ZEN_STATIC_WARNING_STRINGIZE(GCC warning MSG))
#endif
#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/zstring.h b/zen/zstring.h
index cb19318c..026737da 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -18,7 +18,7 @@
//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
+//for special UI-contexts: guaranteed exponential growth + ref-counting
using Zstringw = zen::Zbase<wchar_t>;
@@ -65,8 +65,20 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
inline
Zstring getFileExtension(const Zstring& filePath)
{
- const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
- return afterLast(shortName, Zchar('.'), zen::IF_MISSING_RETURN_NONE);
+ //const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
+ //return afterLast(fileName, Zstr('.'), zen::IF_MISSING_RETURN_NONE);
+
+ auto it = zen::find_last(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
+ if (it == filePath.end())
+ it = filePath.begin();
+ else
+ ++it;
+
+ auto it2 = zen::find_last(it, filePath.end(), Zstr('.'));
+ if (it2 != filePath.end())
+ ++it2;
+
+ return Zstring(it2, filePath.end());
}
bgstack15