summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rwxr-xr-xzen/deprecate.h4
-rwxr-xr-xzen/file_access.cpp69
-rwxr-xr-xzen/file_traverser.cpp25
-rwxr-xr-xzen/guid.h4
-rwxr-xr-xzen/scope_guard.h2
-rwxr-xr-xzen/stl_tools.h44
-rwxr-xr-xzen/thread.h72
-rwxr-xr-xzen/type_traits.h6
-rwxr-xr-xzen/warn_static.h2
-rwxr-xr-xzen/xml_io.cpp2
-rwxr-xr-xzen/xml_io.h2
-rwxr-xr-xzen/zstring.cpp6
-rwxr-xr-xzen/zstring.h4
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;
diff --git a/zen/guid.h b/zen/guid.h
index b2ada48c..6cffc708 100755
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -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:
bgstack15