diff options
Diffstat (limited to 'zen')
-rwxr-xr-x | zen/deprecate.h | 4 | ||||
-rwxr-xr-x | zen/file_access.cpp | 69 | ||||
-rwxr-xr-x | zen/file_traverser.cpp | 25 | ||||
-rwxr-xr-x | zen/guid.h | 4 | ||||
-rwxr-xr-x | zen/scope_guard.h | 2 | ||||
-rwxr-xr-x | zen/stl_tools.h | 44 | ||||
-rwxr-xr-x | zen/thread.h | 72 | ||||
-rwxr-xr-x | zen/type_traits.h | 6 | ||||
-rwxr-xr-x | zen/warn_static.h | 2 | ||||
-rwxr-xr-x | zen/xml_io.cpp | 2 | ||||
-rwxr-xr-x | zen/xml_io.h | 2 | ||||
-rwxr-xr-x | zen/zstring.cpp | 6 | ||||
-rwxr-xr-x | zen/zstring.h | 4 |
13 files changed, 168 insertions, 74 deletions
diff --git a/zen/deprecate.h b/zen/deprecate.h index 2a6bcb0d..1f4e6ab4 100755 --- a/zen/deprecate.h +++ b/zen/deprecate.h @@ -8,7 +8,11 @@ #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/file_access.cpp b/zen/file_access.cpp index 1711e934..18c0ed26 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -32,13 +32,45 @@ using namespace zen; 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 + int sepCount = 0; + for (auto it = itemPathFmt.begin(); it != itemPathFmt.end(); ++it) + if (*it == FILE_NAME_SEPARATOR) + if (++sepCount == sepCountVolumeRoot) + { + Zstring rootPath(itemPathFmt.begin(), rootWithSep ? it + 1 : it); + + Zstring relPath(it + 1, itemPathFmt.end()); + trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; }); + + return PathComponents({ rootPath, relPath }); + } + return NoValue(); + }; + if (startsWith(itemPath, "/")) { - Zstring relPath(itemPath.c_str() + 1); - if (endsWith(relPath, FILE_NAME_SEPARATOR)) - relPath.pop_back(); - return PathComponents({ "/", relPath }); + 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 + return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + } + + 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*/); + + return doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/); } + //we do NOT support relative paths! return NoValue(); } @@ -115,9 +147,9 @@ PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError { - const PathStatus pd = getPathStatus(itemPath); //throw FileError - if (pd.relPath.empty()) - return pd.existingType; + const PathStatus ps = getPathStatus(itemPath); //throw FileError + if (ps.relPath.empty()) + return ps.existingType; return NoValue(); } @@ -541,23 +573,18 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File } catch (FileError&) { - Opt<PathStatus> pd; - try { pd = getPathStatus(dirPath); /*throw FileError*/ } - catch (FileError&) {} //previous exception is more relevant + const PathStatus ps = getPathStatus(dirPath); //throw FileError + if (ps.existingType == ItemType::FILE) + throw; - if (pd && - pd->existingType != ItemType::FILE && - pd->relPath.size() != 1) //don't repeat the very same createDirectory() call from above! - { - Zstring intermediatePath = pd->existingPath; - for (const Zstring& itemName : pd->relPath) + //ps.relPath.size() == 1 => same createDirectory() call from above? Maybe parent folder was created by parallel thread shortly after failure! + Zstring intermediatePath = ps.existingPath; + for (const Zstring& itemName : ps.relPath) + try { - intermediatePath = appendSeparator(intermediatePath) + itemName; - createDirectory(intermediatePath); //throw FileError, (ErrorTargetExisting) + createDirectory(intermediatePath = appendSeparator(intermediatePath) + itemName); //throw FileError, ErrorTargetExisting } - return; - } - throw; + catch (ErrorTargetExisting&) {} //possible, if createDirectoryIfMissingRecursion() is run in parallel } } diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index bc53f206..e342c8ec 100755 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -24,14 +24,6 @@ void zen::traverseFolder(const Zstring& dirPath, { try { - /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede - that field within the dirent structure, portable applications that use readdir_r() should allocate - the buffer whose address is passed in entry as follows: - len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 - entryp = malloc(len); */ - const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) - std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1); - DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" if (!folder) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); @@ -39,13 +31,16 @@ void zen::traverseFolder(const Zstring& dirPath, for (;;) { - struct ::dirent* dirEntry = nullptr; - if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); - //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/ + errno = 0; + const struct ::dirent* dirEntry = ::readdir(folder); //don't use readdir_r(), see comment in native.cpp + if (!dirEntry) + { + if (errno == 0) //errno left unchanged => no more items + return; - if (!dirEntry) //no more items - return; + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir"); + //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/ + } //don't return "." and ".." const char* itemNameRaw = dirEntry->d_name; @@ -56,7 +51,7 @@ void zen::traverseFolder(const Zstring& dirPath, const Zstring& itemName = itemNameRaw; if (itemName.empty()) //checks result of normalizeUtfForPosix, too! - throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); + throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir: Data corruption; item with empty name."); const Zstring& itemPath = appendSeparator(dirPath) + itemName; @@ -9,10 +9,14 @@ #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 namespace zen diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 328e2caa..6a732c9f 100755 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -13,7 +13,7 @@ //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__ <= 0))), "check std::uncaught_exceptions support"); + static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); namespace __cxxabiv1 { diff --git a/zen/stl_tools.h b/zen/stl_tools.h index ba4a6c89..f09639e1 100755 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -58,9 +58,8 @@ template <class InputIterator1, class InputIterator2> bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2); -template <class ByteIterator> size_t hashBytes (ByteIterator first, ByteIterator last); -template <class ByteIterator> size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last); - +template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last); +template <class Num, class ByteIterator> Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last); //support for custom string classes in std::unordered_set/map struct StringHash @@ -69,7 +68,7 @@ struct StringHash size_t operator()(const String& str) const { const auto* strFirst = strBegin(str); - return hashBytes(reinterpret_cast<const char*>(strFirst), + return hashBytes<size_t>(reinterpret_cast<const char*>(strFirst), reinterpret_cast<const char*>(strFirst + strLength(str))); } }; @@ -190,34 +189,29 @@ bool equal(InputIterator1 first1, InputIterator1 last1, } + - -template <class ByteIterator> inline -size_t hashBytes(ByteIterator first, ByteIterator last) +//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) { - //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function -#ifdef ZEN_BUILD_32BIT - const size_t basis = 2166136261U; -#elif defined ZEN_BUILD_64BIT - const size_t basis = 14695981039346656037ULL; -#endif - return hashBytesAppend(basis, first, 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; + + return hashBytesAppend(base, first, last); } -template <class ByteIterator> inline -size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last) +template <class Num, class ByteIterator> inline +Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last) { -#ifdef ZEN_BUILD_32BIT - const size_t prime = 16777619U; -#elif defined ZEN_BUILD_64BIT - const size_t prime = 1099511628211ULL; -#endif - static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, ""); - - for (; first != last; ++first) + static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, ""); + const Num prime = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL; + + for (; first != last; ++first) { - hashVal ^= static_cast<size_t>(*first); + hashVal ^= static_cast<Num>(*first); hashVal *= prime; } return hashVal; diff --git a/zen/thread.h b/zen/thread.h index 3721b3c7..ed61e06b 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -71,8 +71,8 @@ std::async replacement without crappy semantics: 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor Example: - Zstring dirpath = ... - auto ft = zen::runAsync([=]{ return zen::dirExists(dirpath); }); + Zstring dirPath = ... + auto ft = zen::runAsync([=]{ return zen::dirExists(dirPath); }); if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get()) //dir exising */ @@ -120,10 +120,10 @@ public: Protected(const T& value) : value_(value) {} template <class Function> - void access(Function fun) + auto access(Function fun) //-> decltype(fun(std::declval<T&>())) { std::lock_guard<std::mutex> dummy(lockValue_); - fun(value_); + return fun(value_); } private: @@ -134,6 +134,60 @@ private: T value_{}; }; +//------------------------------------------------------------------------------------------ + +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() + { + for (InterruptibleThread& w : worker_) w.interrupt(); //interrupt all first, then join + for (InterruptibleThread& w : worker_) w.join(); + } + + //context of controlling thread, non-blocking: + void run(Function&& wi) + { + assert(!worker_.empty()); + { + std::lock_guard<std::mutex> dummy(lockWork_); + workItems_.push_back(std::move(wi)); + } + conditionNewWork_.notify_all(); + } + +private: + ThreadGroup (const ThreadGroup&) = delete; + ThreadGroup& operator=(const ThreadGroup&) = delete; + + //context of worker threads, blocking: + Function getNextWorkItem() //throw ThreadInterruption + { + std::unique_lock<std::mutex> dummy(lockWork_); + + interruptibleWait(conditionNewWork_, dummy, [this] { return !workItems_.empty(); }); //throw ThreadInterruption + warn_static("implement FIFO!?") + + Function wi = std::move(workItems_. back()); // + /**/ workItems_.pop_back(); //noexcept thanks to move + return wi; // + } + std::vector<InterruptibleThread> worker_; + std::mutex lockWork_; + std::vector<Function> workItems_; + std::condition_variable conditionNewWork_; +}; @@ -222,10 +276,9 @@ public: private: bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished_ >= jobsTotal); } //call while locked! - std::mutex lockResult_; size_t jobsFinished_ = 0; // - Opt<T> result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal" + Opt<T> result_; //our condition is: "have result" or "jobsFinished_ == jobsTotal" std::condition_variable conditionJobDone_; }; @@ -256,7 +309,11 @@ 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 {}; @@ -296,7 +353,8 @@ public: setConditionVar(&cv); ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr)); - //"interrupted_" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out! + //"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! while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted_ || pred(); })) ; diff --git a/zen/type_traits.h b/zen/type_traits.h index f0f96b43..83a74d1e 100755 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -37,6 +37,12 @@ struct ResultType using Type = T; }; +template<class T, class...> +struct GetFirstOf +{ + using Type = T; +}; + //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); } template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); } diff --git a/zen/warn_static.h b/zen/warn_static.h index 5b9a4fee..e4931c08 100755 --- a/zen/warn_static.h +++ b/zen/warn_static.h @@ -14,11 +14,13 @@ 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 warn_static(TXT) \ typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \ enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) }; +#endif #endif //WARN_STATIC_H_08724567834560832745 diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp index a618f27c..dd116ef0 100755 --- a/zen/xml_io.cpp +++ b/zen/xml_io.cpp @@ -57,7 +57,7 @@ void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filePath) //throw Fi { const std::string stream = serialize(doc); //noexcept - //only update xml file if there are real changes + //only update XML file if there are real changes try { if (getFileSize(filePath) == stream.size()) //throw FileError diff --git a/zen/xml_io.h b/zen/xml_io.h index a53a7edb..81b45aa1 100755 --- a/zen/xml_io.h +++ b/zen/xml_io.h @@ -11,7 +11,7 @@ #include "file_error.h" -//combine zen::Xml and zen file i/o +//combine zen::Xml and zen file I/O //-> loadXmlDocument vs loadStream: //1. better error reporting //2. quick exit if (potentially large) input file is not an XML diff --git a/zen/zstring.cpp b/zen/zstring.cpp index ce94fe56..afa62c93 100755 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -54,7 +54,9 @@ 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); - 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 if (charL != charR) @@ -65,7 +67,7 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh } -int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) +int cmpStringNaturalLinuxTest(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) { const char* const lhsEnd = lhs + lhsLen; const char* const rhsEnd = rhs + rhsLen; diff --git a/zen/zstring.h b/zen/zstring.h index 258603dc..5a6ecbdd 100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -163,7 +163,9 @@ S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm) } } - int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); +//expose for unit tests +int cmpStringNaturalLinuxTest(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); +inline int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) { return cmpStringNaturalLinuxTest(lhs, lhsLen, rhs, rhsLen); } //--------------------------------------------------------------------------- //ZEN macro consistency checks: |