diff options
Diffstat (limited to 'zen')
-rwxr-xr-x | zen/basic_math.h | 53 | ||||
-rwxr-xr-x | zen/file_access.cpp | 11 | ||||
-rwxr-xr-x | zen/file_io.cpp | 6 | ||||
-rwxr-xr-x | zen/file_io.h | 5 | ||||
-rwxr-xr-x | zen/i18n.h | 68 | ||||
-rwxr-xr-x | zen/scope_guard.h | 2 | ||||
-rwxr-xr-x | zen/stl_tools.h | 17 | ||||
-rwxr-xr-x | zen/string_tools.h | 48 | ||||
-rwxr-xr-x | zen/time.h | 43 | ||||
-rwxr-xr-x | zen/xml_io.cpp | 2 |
10 files changed, 139 insertions, 116 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h index 722722a5..22f29ca7 100755 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -24,10 +24,9 @@ template <class T> T min(T a, T b, T c); template <class T> T max(T a, T b, T c); template <class T> bool isNull(T value); -template <class T> -void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal -template <class T> -T clampCpy(T val, T minVal, T maxVal); +template <class T> void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal +template <class T> T clampCpy(T val, T minVal, T maxVal); +//std::clamp() available with C++17 template <class T, class InputIterator> //precondition: range must be sorted! auto nearMatch(const T& val, InputIterator first, InputIterator last); @@ -64,6 +63,8 @@ const double pi = 3.14159265358979323846; const double e = 2.71828182845904523536; const double sqrt2 = 1.41421356237309504880; const double ln2 = 0.693147180559945309417; + +//static_assert(pi + e + sqrt2 + ln2 == 7.9672352249818781, "whoopsie"); //---------------------------------------------------------------------------------- @@ -286,17 +287,16 @@ template <class RandomAccessIterator> inline double median(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range! { const size_t n = last - first; - if (n > 0) - { - std::nth_element(first, first + n / 2, last); //complexity: O(n) - const double midVal = *(first + n / 2); + if (n == 0) + return 0; - if (n % 2 != 0) - return midVal; - else //n is even and >= 2 in this context: return mean of two middle values - return 0.5 * (*std::max_element(first, first + n / 2) + midVal); //this operation is the reason why median() CANNOT support a comparison predicate!!! - } - return 0; + std::nth_element(first, first + n / 2, last); //complexity: O(n) + const double midVal = *(first + n / 2); + + if (n % 2 != 0) + return midVal; + else //n is even and >= 2 in this context: return mean of two middle values + return 0.5 * (*std::max_element(first, first + n / 2) + midVal); //this operation is the reason why median() CANNOT support a comparison predicate!!! } @@ -304,25 +304,22 @@ template <class RandomAccessIterator> inline double mad(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range! { //http://en.wikipedia.org/wiki/Median_absolute_deviation - const size_t n = last - first; - if (n > 0) - { - const double m = median(first, last); + if (n == 0) + return 0; - //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision! + const double m = median(first, last); - auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); }; + //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision! + auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); }; - std::nth_element(first, first + n / 2, last, lessMedAbs); //complexity: O(n) - const double midVal = abs(*(first + n / 2) - m); + std::nth_element(first, first + n / 2, last, lessMedAbs); //complexity: O(n) + const double midVal = abs(*(first + n / 2) - m); - if (n % 2 != 0) - return midVal; - else //n is even and >= 2 in this context: return mean of two middle values - return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal); - } - return 0; + if (n % 2 != 0) + return midVal; + else //n is even and >= 2 in this context: return mean of two middle values + return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal); } diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 69b6c388..e7467325 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" -#include "guid.h" +#include "crc.h" //boost dependency! +#include "guid.h" // #include <sys/vfs.h> //statfs #include <sys/time.h> //lutimes @@ -627,10 +627,13 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, //=> don't delete file that existed previously!!! FileOutput fileOut(fdTarget, targetFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //pass ownership - bufferedStreamCopy(fileIn, fileOut); //throw FileError, X + //fileOut.preAllocateSpaceBestEffort(sourceInfo.st_size); //throw FileError + //=> perf: seems like no real benefit... + + bufferedStreamCopy(fileIn, fileOut); //throw FileError, (ErrorFileLocked), X //flush intermediate buffers before fiddling with the raw file handle - fileOut.flushBuffers(); //throw FileError, X + fileOut.flushBuffers(); //throw FileError, X struct ::stat targetInfo = {}; if (::fstat(fileOut.getHandle(), &targetInfo) != 0) diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 60023849..68b41bcb 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -103,7 +103,7 @@ FileInput::FileInput(const Zstring& filePath, const IOCallback& notifyUnbuffered } -size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; may return short, only 0 means EOF! +size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! { if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check! throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); @@ -127,7 +127,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m } -size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; return "bytesToRead" bytes unless end of stream! +size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! { const size_t blockSize = getBlockSize(); assert(memBuf_.size() <= blockSize); @@ -144,7 +144,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; r break; //-------------------------------------------------------------------- memBuf_.resize(blockSize); - const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 + const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 memBuf_.resize(bytesRead); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X diff --git a/zen/file_io.h b/zen/file_io.h index 6b0665a1..c2071500 100755 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -33,6 +33,7 @@ public: //Windows: use 64kB ?? https://technet.microsoft.com/en-us/library/cc938632 //Linux: use st_blksize? + //macOS: use f_iosize? static size_t getBlockSize() { return 128 * 1024; }; protected: @@ -58,10 +59,10 @@ public: FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorFileLocked FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership! - size_t read(void* buffer, size_t bytesToRead); //throw FileError, X; return "bytesToRead" bytes unless end of stream! + size_t read(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! private: - size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0! + size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0! std::vector<char> memBuf_; const IOCallback notifyUnbufferedIO_; //throw X @@ -16,8 +16,8 @@ //minimal layer enabling text translation - without platform/library dependencies! #define ZEN_TRANS_CONCAT_SUB(X, Y) X ## Y -#define _(s) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s)) -#define _P(s, p, n) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n) +#define _(s) zen::translate(ZEN_TRANS_CONCAT_SUB(L, s)) +#define _P(s, p, n) zen::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n) //source and translation are required to use %x as number placeholder //for plural form, which will be substituted automatically!!! @@ -58,61 +58,55 @@ std::shared_ptr<const TranslationHandler> getTranslator(); namespace implementation { inline -std::wstring translate(const std::wstring& text) +Global<const TranslationHandler>& refGlobalTranslationHandler() { - if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface! - return t->translate(text); - return text; + //getTranslator() may be called even after static objects of this translation unit are destroyed! + static Global<const TranslationHandler> inst; //external linkage even in header! + return inst; +} } - -//translate plural forms: "%x day" "%x days" -//returns "1 day" if n == 1; "123 days" if n == 123 for english language inline -std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n) +std::shared_ptr<const TranslationHandler> getTranslator() { - assert(contains(plural, L"%x")); - - if (std::shared_ptr<const TranslationHandler> t = getTranslator()) - { - std::wstring translation = t->translate(singular, plural, n); - assert(!contains(translation, L"%x")); - return translation; - } - - return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); + return implementation::refGlobalTranslationHandler().get(); } -template <class T> inline -std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n) +inline +void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler) { - static_assert(sizeof(n) <= sizeof(int64_t), ""); - return translate(singular, plural, static_cast<int64_t>(n)); + implementation::refGlobalTranslationHandler().set(std::move(newHandler)); } inline -Global<const TranslationHandler>& refGlobalTranslationHandler() +std::wstring translate(const std::wstring& text) { - //getTranslator() may be called even after static objects of this translation unit are destroyed! - static Global<const TranslationHandler> inst; //external linkage even in header! - return inst; -} + if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface! + return t->translate(text); + return text; } -inline -void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler) +//translate plural forms: "%x day" "%x days" +//returns "1 day" if n == 1; "123 days" if n == 123 for english language +template <class T> inline +std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n) { - implementation::refGlobalTranslationHandler().set(std::move(newHandler)); -} + static_assert(sizeof(n) <= sizeof(int64_t), ""); + const auto n64 = static_cast<int64_t>(n); + assert(contains(plural, L"%x")); -inline -std::shared_ptr<const TranslationHandler> getTranslator() -{ - return implementation::refGlobalTranslationHandler().get(); + if (std::shared_ptr<const TranslationHandler> t = getTranslator()) + { + std::wstring translation = t->translate(singular, plural, n64); + assert(!contains(translation, L"%x")); + return translation; + } + //fallback: + return replaceCpy(std::abs(n64) == 1 ? singular : plural, L"%x", toGuiString(n)); } } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index f4ffc92b..ccc6db0c 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__ < 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); + static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); namespace __cxxabiv1 { diff --git a/zen/stl_tools.h b/zen/stl_tools.h index b03d7533..2fcecd11 100755 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -39,9 +39,6 @@ void append(std::set<T, LessType, Alloc>& s, const C& c); template <class KeyType, class ValueType, class LessType, class Alloc, class C> void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c); -template <class M, class K, class V> -V& map_add_or_update(M& map, const K& key, const V& value); //efficient add or update without "default-constructible" requirement (Effective STL, item 24) - template <class T, class Alloc> void removeDuplicates(std::vector<T, Alloc>& v); @@ -125,20 +122,6 @@ template <class KeyType, class ValueType, class LessType, class Alloc, class C> void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.insert(c.begin(), c.end()); } -template <class M, class K, class V> inline -V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or update without "default-constructible" requirement (Effective STL, item 24) -{ - auto it = map.lower_bound(key); - if (it != map.end() && !(map.key_comp()(key, it->first))) - { - it->second = value; - return it->second; - } - else - return map.insert(it, typename M::value_type(key, value))->second; -} - - template <class T, class Alloc> inline void removeDuplicates(std::vector<T, Alloc>& v) { diff --git a/zen/string_tools.h b/zen/string_tools.h index bfa14257..0a24ab2a 100755 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -22,10 +22,11 @@ //enhance arbitray string class with useful non-member functions: namespace zen { -template <class Char> bool isWhiteSpace(Char ch); -template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only! -template <class Char> bool isHexDigit (Char ch); -template <class Char> bool isAsciiAlpha(Char ch); +template <class Char> bool isWhiteSpace(Char c); +template <class Char> bool isDigit (Char c); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only! +template <class Char> bool isHexDigit (Char c); +template <class Char> bool isAsciiAlpha(Char c); +template <class Char> Char asciiToLower(Char c); //case-sensitive comparison (compile-time correctness: use different number of arguments as STL comparison predicates!) struct CmpBinary { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; }; @@ -105,28 +106,28 @@ template <class T, class S> T copyStringTo(S&& str); //---------------------- implementation ---------------------- template <> inline -bool isWhiteSpace(char ch) +bool isWhiteSpace(char c) { - assert(ch != 0); //std C++ does not consider 0 as white space + assert(c != 0); //std C++ does not consider 0 as white space //caveat 1: std::isspace() takes an int, but expects an unsigned char //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC) - return static_cast<unsigned char>(ch) < 128 && - std::isspace(static_cast<unsigned char>(ch)) != 0; + return static_cast<unsigned char>(c) < 128 && + std::isspace(static_cast<unsigned char>(c)) != 0; } template <> inline -bool isWhiteSpace(wchar_t ch) +bool isWhiteSpace(wchar_t c) { - assert(ch != 0); //std C++ does not consider 0 as white space - return std::iswspace(ch) != 0; + assert(c != 0); //std C++ does not consider 0 as white space + return std::iswspace(c) != 0; } template <class Char> inline -bool isDigit(Char ch) //similar to implmenetation of std::isdigit()! +bool isDigit(Char c) //similar to implmenetation of std::isdigit()! { static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, ""); - return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9'); + return static_cast<Char>('0') <= c && c <= static_cast<Char>('9'); } @@ -149,6 +150,15 @@ bool isAsciiAlpha(Char c) } +template <class Char> inline +Char asciiToLower(Char c) +{ + if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) + return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a')); + return c; +} + + template <class S, class T, class Function> inline bool startsWith(const S& str, const T& prefix, Function cmpStringFun) { @@ -458,19 +468,11 @@ int CmpBinary::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_ template <class Char> inline int CmpAsciiNoCase::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const { - auto asciiToLower = [](Char c) //ordering: lower-case chars have higher code points than uppper-case - { - if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) - return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a')); - return c; - }; - const auto* const lhsLast = lhs + std::min(lhsLen, rhsLen); - while (lhs != lhsLast) { - const Char charL = asciiToLower(*lhs++); - const Char charR = asciiToLower(*rhs++); + const Char charL = asciiToLower(*lhs++); //ordering: lower-case chars have higher code points than uppper-case + const Char charR = asciiToLower(*rhs++); // if (charL != charR) return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention! //unsigned underflow is well-defined! @@ -23,12 +23,19 @@ struct TimeComp //replaces std::tm and SYSTEMTIME int second = 0; //0-60 (including leap second) }; +inline bool operator==(const TimeComp& lhs, const TimeComp& rhs) +{ + return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second; +} + TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components time_t utcToTimeT(const TimeComp& comp); //convert UTC time components to time_t (UTC), returns -1 on error +TimeComp getCompileTime(); + //---------------------------------------------------------------------------------------------------------------------------------- /* @@ -268,6 +275,23 @@ time_t utcToTimeT(const TimeComp& comp) //returns -1 on error } +inline +TimeComp getCompileTime() +{ + //https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html + char compileTime[] = __DATE__ " " __TIME__; //e.g. "Aug 1 2017 01:32:26" + if (compileTime[4] == ' ') //day is space-padded, but %d expects zero-padding + compileTime[4] = '0'; + + TimeComp tc = {}; + if (parseTime("%b %d %Y %H:%M:%S", compileTime, tc)) + return tc; + + assert(false); + return TimeComp(); +} + + template <class String, class String2> inline String formatTime(const String2& format, const TimeComp& comp) { @@ -328,6 +352,25 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur if (!extractNumber(comp.month, 2)) return false; break; + case 'b': //abbreviated month name: Jan-Dec + { + if (strLast - itStr < 3) + return false; + + const char* months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + auto itMonth = std::find_if(std::begin(months), std::end(months), [&](const char* name) + { + return asciiToLower(itStr[0]) == name[0] && + asciiToLower(itStr[1]) == name[1] && + asciiToLower(itStr[2]) == name[2]; + }); + if (itMonth == std::end(months)) + return false; + + comp.month = 1 + static_cast<int>(itMonth - std::begin(months)); + itStr += 3; + } + break; case 'd': if (!extractNumber(comp.day, 2)) return false; diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp index 0a1c8505..8192d6e6 100755 --- a/zen/xml_io.cpp +++ b/zen/xml_io.cpp @@ -22,7 +22,7 @@ XmlDoc zen::loadXmlDocument(const Zstring& filePath) //throw FileError for (;;) { buffer.resize(buffer.size() + blockSize); - const size_t bytesRead = fileIn.read(&*(buffer.end() - blockSize), blockSize); //throw FileError, (X); return "bytesToRead" bytes unless end of stream! + const size_t bytesRead = fileIn.read(&*(buffer.end() - blockSize), blockSize); //throw FileError, ErrorFileLocked, (X); return "bytesToRead" bytes unless end of stream! buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics //quick test whether input is an XML: avoid loading large binary files up front! |