From 06c99e6c91d87a91f3e54191670d23e3f4d132b9 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Sun, 29 Jul 2018 21:45:13 -0700 Subject: 10.2 --- zen/crc.h | 89 +++++++++++++++++++++++++++++++++++++++++++---------- zen/file_access.cpp | 17 +++++----- zen/file_io.cpp | 8 ++--- zen/guid.h | 55 +++++++++++++++++++++++++++------ zen/perf.h | 51 +++++++++++++++++++++--------- zen/ring_buffer.h | 12 +++++++- zen/serialize.h | 2 +- zen/stl_tools.h | 10 +++--- zen/string_tools.h | 8 +++-- zen/string_traits.h | 8 +++-- zen/thread.h | 56 ++++++++++++++++++++++++--------- zen/warn_static.h | 2 +- zen/zstring.h | 18 +++++++++-- 13 files changed, 256 insertions(+), 80 deletions(-) (limited to 'zen') 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 +#include "type_traits.h" namespace zen @@ -27,28 +26,86 @@ inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(), template 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(crcTable) == 8380544); static_assert(sizeof(typename std::iterator_traits::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 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(crcTable) == 549755813760); static_assert(sizeof(typename std::iterator_traits::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 //statfs #include //lutimes @@ -34,7 +34,7 @@ Opt zen::parsePathComponents(const Zstring& itemPath) { auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> Opt { - 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 - #include //open, close - #include //read, write + #include //open + #include //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 - #include + #include //open + #include //close + //#include -> 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(__LINE__) + "] Failed to generate GUID." + + "\n" + utfTo(formatSystemError(L"getentropy", errno))); + return guid; + +#else + class RandomGeneratorPosix + { + public: + RandomGeneratorPosix() + { + if (fd_ == -1) + throw std::runtime_error(std::string(__FILE__) + "[" + numberTo(__LINE__) + "] Failed to generate GUID." + + "\n" + utfTo(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(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(__LINE__) + "] Failed to generate GUID." + + "\n" + utfTo(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(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(timeMs()) + " ms"; + const int64_t timeMs = std::chrono::duration_cast(watch_.elapsed()).count(); + const std::string msg = numberTo(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; 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 BinContainer bufferedLoad(BufferedInputStream& streamIn); //throw X template void writeNumber (BufferedOutputStream& stream, const N& num); // -template void writeContainer(BufferedOutputStream& stream, const C& str); //throw () +template 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 void removeDuplicates(std::vector& v); //binary search returning an iterator -template -ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less); +template +Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less); template BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value); @@ -125,9 +125,11 @@ void removeDuplicates(std::vector& v) } -template inline -ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less) +template inline +Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less) { + static_assert(std::is_same_v::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 #include #include -#include +#include //std::basic_ostringstream #include "stl_tools.h" #include "string_traits.h" @@ -446,7 +446,11 @@ namespace impl template 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>); + return T(strBegin(src), strLength(src)); + } }; template 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 - 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(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 @@ -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 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 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::future 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& onCompletion /*noexcept! runs on worker thread!*/) + { + std::lock_guard 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> callbacks; + callbacks.swap(wl->onCompletionCallbacks); + + dummy.unlock(); + for (const auto& cb : callbacks) cb(); //noexcept! + dummy.lock(); + } } }); } @@ -230,7 +256,7 @@ private: RingBuffer tasks; //FIFO! :) size_t tasksPending = 0; std::condition_variable conditionNewTask; - std::condition_variable conditionTasksDone; + std::vector> onCompletionCallbacks; }; std::vector 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; - //for special UI-contexts: guaranteed exponential growth + ref-counting +//for special UI-contexts: guaranteed exponential growth + ref-counting using Zstringw = zen::Zbase; @@ -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()); } -- cgit