diff options
Diffstat (limited to 'zen')
-rw-r--r-- | zen/basic_math.h | 9 | ||||
-rw-r--r-- | zen/build_info.h | 9 | ||||
-rw-r--r-- | zen/file_access.cpp | 4 | ||||
-rw-r--r-- | zen/file_io.cpp | 8 | ||||
-rw-r--r-- | zen/file_io.h | 4 | ||||
-rw-r--r-- | zen/format_unit.cpp | 39 | ||||
-rw-r--r-- | zen/format_unit.h | 15 | ||||
-rw-r--r-- | zen/legacy_compiler.cpp | 4 | ||||
-rw-r--r-- | zen/legacy_compiler.h | 25 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 5 | ||||
-rw-r--r-- | zen/recycler.cpp | 71 | ||||
-rw-r--r-- | zen/socket.h | 1 | ||||
-rw-r--r-- | zen/stl_tools.h | 10 | ||||
-rw-r--r-- | zen/string_base.h | 4 | ||||
-rw-r--r-- | zen/string_tools.h | 106 | ||||
-rw-r--r-- | zen/sys_version.cpp | 21 | ||||
-rw-r--r-- | zen/zstring.cpp | 67 | ||||
-rw-r--r-- | zen/zstring.h | 24 |
18 files changed, 267 insertions, 159 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h index 0e30c276..26fb9e58 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -183,18 +183,15 @@ auto integerDivideRoundUp(N numerator, D denominator) namespace { template <size_t N, class T> struct PowerImpl; -/* - template <size_t N, class T> -> let's use non-recursive specializations to help the compiler - struct PowerImpl { static T result(const T& value) { return PowerImpl<N - 1, T>::result(value) * value; } }; -*/ +//let's use non-recursive specializations to help the compiler template <class T> struct PowerImpl<2, T> { static T result(T value) { return value * value; } }; template <class T> struct PowerImpl<3, T> { static T result(T value) { return value * value * value; } }; } -template <size_t n, class T> inline +template <size_t N, class T> inline T power(T value) { - return PowerImpl<n, T>::result(value); + return PowerImpl<N, T>::result(value); } diff --git a/zen/build_info.h b/zen/build_info.h index 01f1aeb8..adb19f86 100644 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -7,8 +7,6 @@ #ifndef BUILD_INFO_H_5928539285603428657 #define BUILD_INFO_H_5928539285603428657 - #include <bit> //std::endian - #define ZEN_ARCH_32BIT 32 #define ZEN_ARCH_64BIT 64 @@ -20,11 +18,4 @@ static_assert(ZEN_BUILD_ARCH == sizeof(void*) * 8); -//-------------------------------------------------------------------- - -constexpr bool usingLittleEndian() -{ - return std::endian::native == std::endian::little; -} - #endif //BUILD_INFO_H_5928539285603428657 diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 7d3fbfc5..3269bef4 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -82,7 +82,7 @@ std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath) if (const std::optional<PathComponents> comp = parsePathComponents(itemPath)) { if (comp->relPath.empty()) - return {}; + return std::nullopt; const Zstring parentRelPath = beforeLast(comp->relPath, FILE_NAME_SEPARATOR, IfNotFoundReturn::none); if (parentRelPath.empty()) @@ -90,7 +90,7 @@ std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath) return appendSeparator(comp->rootPath) + parentRelPath; } assert(false); - return {}; + return std::nullopt; } diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 4c6602cc..33c41fbc 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -15,7 +15,7 @@ using namespace zen; FileBase::~FileBase() { - if (hFile_ != invalidFileHandle_) + if (hFile_ != invalidFileHandle) try { close(); //throw FileError @@ -26,9 +26,9 @@ FileBase::~FileBase() void FileBase::close() //throw FileError { - if (hFile_ == invalidFileHandle_) + if (hFile_ == invalidFileHandle) throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"Contract error: close() called more than once."); - ZEN_ON_SCOPE_EXIT(hFile_ = invalidFileHandle_); + ZEN_ON_SCOPE_EXIT(hFile_ = invalidFileHandle); if (::close(hFile_) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), "close"); @@ -198,7 +198,7 @@ FileOutput::FileOutput(const Zstring& filePath, const IOCallback& notifyUnbuffer FileOutput::~FileOutput() { - if (getHandle() != invalidFileHandle_) //not finalized => clean up garbage + if (getHandle() != invalidFileHandle) //not finalized => clean up garbage { //"deleting while handle is open" == FILE_FLAG_DELETE_ON_CLOSE if (::unlink(getFilePath().c_str()) != 0) diff --git a/zen/file_io.h b/zen/file_io.h index 81e1e7cc..4210cc57 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -27,7 +27,7 @@ class FileBase { public: using FileHandle = int; - static const int invalidFileHandle_ = -1; + static const int invalidFileHandle = -1; FileHandle getHandle() { return hFile_; } @@ -48,7 +48,7 @@ private: FileBase (const FileBase&) = delete; FileBase& operator=(const FileBase&) = delete; - FileHandle hFile_ = invalidFileHandle_; + FileHandle hFile_ = invalidFileHandle; const Zstring filePath_; }; diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 4984c1d7..0d75a9d4 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -7,12 +7,16 @@ #include "format_unit.h" #include <ctime> #include <cstdio> +#include <stdexcept> #include "basic_math.h" +#include "sys_error.h" #include "i18n.h" #include "time.h" #include "globals.h" #include "utf.h" + #include <iostream> + #include <langinfo.h> #include <clocale> //thousands separator #include "utf.h" // @@ -181,3 +185,38 @@ std::wstring zen::formatUtcToLocalTime(time_t utcTime) } + + +WeekDay impl::getFirstDayOfWeekImpl() //throw SysError +{ + /* testing: change locale via command line + --------------------------------------- + LC_TIME=en_DK.utf8 => Monday + LC_TIME=en_US.utf8 => Sunday */ + + const char* firstDay = ::nl_langinfo(_NL_TIME_FIRST_WEEKDAY); //[1-Sunday, 7-Saturday] + ASSERT_SYSERROR(firstDay && 1 <= *firstDay && *firstDay <= 7); + + const int weekDayStartSunday = *firstDay; + const int weekDayStartMonday = (weekDayStartSunday - 1 + 6) % 7; //+6 == -1 in Z_7 + // [0-Monday, 6-Sunday] + return static_cast<WeekDay>(weekDayStartMonday); +} + + +WeekDay zen::getFirstDayOfWeek() +{ + static const WeekDay weekDay = [] + { + try + { + return impl::getFirstDayOfWeekImpl(); //throw SysError + } + catch (const SysError& e) + { + throw std::runtime_error(std::string(__FILE__) + '[' + numberTo<std::string>(__LINE__) + "] Failed to get first day of the week." + "\n\n" + + utfTo<std::string>(e.toString())); + } + }(); + return weekDay; +} diff --git a/zen/format_unit.h b/zen/format_unit.h index de5a0811..1c96da51 100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -24,6 +24,21 @@ std::wstring formatThreeDigitPrecision(double value); //(unless value is too lar std::wstring formatNumber(int64_t n); //format integer number including thousands separator + + +enum class WeekDay +{ + monday, + tuesday, + wednesday, + thursday, + friday, + saturday, + sunday, +}; +WeekDay getFirstDayOfWeek(); + +namespace impl { WeekDay getFirstDayOfWeekImpl(); } //throw SysError } #endif diff --git a/zen/legacy_compiler.cpp b/zen/legacy_compiler.cpp index 66125b0f..81efb4dd 100644 --- a/zen/legacy_compiler.cpp +++ b/zen/legacy_compiler.cpp @@ -9,13 +9,13 @@ #error get rid of workarounds #endif -double zen::from_chars(const char* first, const char* last) +double zen::fromChars(const char* first, const char* last) { return std::strtod(std::string(first, last).c_str(), nullptr); } -const char* zen::to_chars(char* first, char* last, double num) +const char* zen::toChars(char* first, char* last, double num) { const size_t bufSize = last - first; const int charsWritten = std::snprintf(first, bufSize, "%g", num); diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 82c404d8..ad5442fe 100644 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -7,26 +7,37 @@ #ifndef LEGACY_COMPILER_H_839567308565656789 #define LEGACY_COMPILER_H_839567308565656789 +#include <version> +/* C++ standard conformance: + https://en.cppreference.com/w/cpp/feature_test + https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros + https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations + + MSVC https://docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance + + GCC https://gcc.gnu.org/projects/cxx-status.html + libstdc++ https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html + + Clang https://clang.llvm.org/cxx_status.html#cxx20 + libc++ https://libcxx.llvm.org/cxx2a_status.html */ -//https://en.cppreference.com/w/cpp/feature_test -//https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros -//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations -//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html namespace std { + + } //--------------------------------------------------------------------------------- //constinit, consteval - #define constinit2 constinit //GCC has it + #define constinit2 constinit //GCC, clang have it #define consteval2 consteval // namespace zen { -double from_chars(const char* first, const char* last); -const char* to_chars(char* first, char* last, double num); +double fromChars(const char* first, const char* last); +const char* toChars(char* first, char* last, double num); } #endif //LEGACY_COMPILER_H_839567308565656789 diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index 1d0c4bf2..ea77db43 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -5,6 +5,7 @@ // ***************************************************************************** #include "open_ssl.h" +#include <bit> //std::endian #include <stdexcept> #include "base64.h" #include "build_info.h" @@ -774,7 +775,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: auto numToBeString = [](size_t n) -> std::string { - static_assert(usingLittleEndian()&& sizeof(n) >= 4); + static_assert(std::endian::native == std::endian::little&& sizeof(n) >= 4); const char* numStr = reinterpret_cast<const char*>(&n); return { numStr[3], numStr[2], numStr[1], numStr[0] }; //big endian! }; @@ -806,7 +807,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std:: if (itEnd - it < makeSigned(sizeof(byteCount))) throw SysError(L"String extraction failed: unexpected end of stream"); - static_assert(usingLittleEndian()); + static_assert(std::endian::native == std::endian::little); char* numStr = reinterpret_cast<char*>(&byteCount); numStr[3] = *it++; // numStr[2] = *it++; //Putty uses big endian! diff --git a/zen/recycler.cpp b/zen/recycler.cpp index b1f2c0fd..4448fd60 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -16,6 +16,7 @@ using namespace zen; +//*INDENT-OFF* bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError { GFile* file = ::g_file_new_for_path(itemPath.c_str()); //never fails according to docu @@ -32,12 +33,73 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError /* g_file_trash() can fail with different error codes/messages when trash is unavailable: Debian 8 (GLib 2.42): G_IO_ERROR_NOT_SUPPORTED: Unable to find or create trash directory - CentOS 7 (GLib 2.56): G_IO_ERROR_FAILED: Unable to find or create trash directory for file.txt + CentOS 7 (GLib 2.56): G_IO_ERROR_FAILED: Unable to find or create trash directory for file.txt => localized! >:( master (GLib 2.64): G_IO_ERROR_NOT_SUPPORTED: Trashing on system internal mounts is not supported https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glocalfile.c#L2042 */ - const bool trashUnavailable = error && - ((error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_SUPPORTED) || - startsWith(error->message, "Unable to find or create trash directory")); + const bool trashUnavailable = error && error->domain == G_IO_ERROR && + (error->code == G_IO_ERROR_NOT_SUPPORTED || + + //yes, the following is a cluster fuck, but what can you do? + (error->code == G_IO_ERROR_FAILED && [&] + { + for (const char* msgLoc : //translations from https://gitlab.gnome.org/GNOME/glib/-/tree/master/po + { + "Unable to find or create trash directory for", + "No s'ha pogut trobar o crear el directori de la paperera per", + "Nelze nalézt nebo vytvořit složku koše pro", + "Kan ikke finde eller oprette papirkurvskatalog for", + "Αδύνατη η εύρεση ή δημιουργία του καταλόγου απορριμμάτων", + "Unable to find or create wastebasket directory for", + "Ne eblas trovi aŭ krei rubujan dosierujon", + "No se pudo encontrar o crear la carpeta de la papelera para", + "Prügikasti kataloogi pole võimalik leida või luua", + "zakarrontziaren direktorioa aurkitu edo sortu", + "Roskakori kansiota ei löydy tai sitä ei voi luoda", + "Impossible de trouver ou créer le répertoire de la corbeille pour", + "Non é posíbel atopar ou crear o directorio do lixo para", + "Nisam mogao promijeniti putanju u mapu", + "Nem található vagy nem hozható létre a Kuka könyvtár ehhez:", + "Tidak bisa menemukan atau membuat direktori tong sampah bagi", + "Impossibile trovare o creare la directory cestino per", + "のゴミ箱ディレクトリが存在しないか作成できません", + "휴지통 디렉터리를 찾을 수 없거나 만들 수 없습니다", + "Nepavyko rasti ar sukurti šiukšlių aplanko", + "Nevar atrast vai izveidot miskastes mapi priekš", + "Tidak boleh mencari atau mencipta direktori tong sampah untuk", + "Kan ikke finne eller opprette mappe for papirkurv for", + "फाइल सिर्जना गर्न असफल:", + "Impossible de trobar o crear lo repertòri de l'escobilhièr per", + "ਲਈ ਰੱਦੀ ਡਾਇਰੈਕਟਰੀ ਲੱਭਣ ਜਾਂ ਬਣਾਉਣ ਲਈ ਅਸਮਰੱਥ", + "Nie można odnaleźć lub utworzyć katalogu kosza dla", + "Impossível encontrar ou criar a pasta de lixo para", + "Não é possível localizar ou criar o diretório da lixeira para", + "Nu se poate găsi sau crea directorul coșului de gunoi pentru", + "Не удалось найти или создать каталог корзины для", + "Nepodarilo sa nájsť ani vytvoriť adresár Kôš pre", + "Ni mogoče najti oziroma ustvariti mape smeti za", + "Не могу да нађем или направим директоријум смећа за", + "Ne mogu da nađem ili napravim direktorijum smeća za", + "Kunde inte hitta eller skapa papperskorgskatalog för", + "için çöp dizini bulunamıyor ya da oluşturulamıyor", + "Не вдалося знайти або створити каталог смітника для", + "หาหรือสร้างไดเรกทอรีถังขยะสำหรับ", + }) + if (contains(error->message, msgLoc)) + return true; + + for (const auto& [msgLoc1, msgLoc2] : + { + std::pair{"Papierkorb-Ordner konnte für", "nicht gefunden oder angelegt werden"}, + std::pair{"Kan prullenbakmap voor", "niet vinden of aanmaken"}, + std::pair{"无法为", "找到或创建回收站目录"}, + std::pair{"無法找到或建立", "的垃圾桶目錄"}, + }) + if (contains(error->message, msgLoc1) && contains(error->message, msgLoc2)) + return true; + + return false; + }())); + if (trashUnavailable) //implement same behavior as on Windows: if recycler is not existing, delete permanently { if (*type == ItemType::folder) @@ -52,6 +114,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError } return true; } +//*INDENT-ON* /* We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not! diff --git a/zen/socket.h b/zen/socket.h index f1d26450..62386801 100644 --- a/zen/socket.h +++ b/zen/socket.h @@ -145,7 +145,6 @@ void shutdownSocketSend(SocketType socket) //throw SysError if (::shutdown(socket, SHUT_WR) != 0) THROW_LAST_SYS_ERROR_WSA("shutdown"); } - } #endif //SOCKET_H_23498325972583947678456437 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 7d071413..495ff8d1 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -185,18 +185,8 @@ BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, Bid } } - -//--------------------------------------------------------------------------------------- -//http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0458r2.html - -template <class Container, class ValueType, typename = std::enable_if_t<!IsStringLikeV<Container>>> inline - bool contains(const Container& c, const ValueType& val, int dummy = 0 /*overload string_tools.h contains()*/) -{ - return c.find(val) != c.end(); -} //--------------------------------------------------------------------------------------- - //read-only variant of std::merge; input: two sorted ranges template <class Iterator, class FunctionLeftOnly, class FunctionBoth, class FunctionRightOnly> inline void mergeTraversal(Iterator first1, Iterator last1, diff --git a/zen/string_base.h b/zen/string_base.h index 1052de56..f0899433 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -10,7 +10,7 @@ #include <algorithm> #include <atomic> #include "string_tools.h" - +#include "legacy_compiler.h" //constinit2 //Zbase - a policy based string class optimizing performance and flexibility @@ -312,7 +312,6 @@ template <class Char, template <class> class SP> std::strong_ordering operator<= template <class Char, template <class> class SP> std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Char* rhs); template <class Char, template <class> class SP> std::strong_ordering operator<=>(const Char* lhs, const Zbase<Char, SP>& rhs); - template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs) += rhs; } template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Char* rhs) { return Zbase<Char, SP>(lhs) += rhs; } template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, Char rhs) { return Zbase<Char, SP>(lhs) += rhs; } @@ -515,7 +514,6 @@ std::strong_ordering operator<=>(const Char* lhs, const Zbase<Char, SP>& rhs) } - template <class Char, template <class> class SP> inline size_t Zbase<Char, SP>::length() const { diff --git a/zen/string_tools.h b/zen/string_tools.h index 2c33a4f8..fc715961 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -16,7 +16,7 @@ #include <vector> #include "stl_tools.h" #include "string_traits.h" -#include "legacy_compiler.h" //<charconv> (without compiler crashes) +#include "legacy_compiler.h" //<charconv> but without the compiler crashes :> //enhance *any* string class with useful non-member functions: @@ -44,12 +44,12 @@ template <class S, class T, typename = std::enable_if_t<IsStringLikeV<S>>> bool template <class S, class T> bool equalString (const S& lhs, const T& rhs); template <class S, class T> bool equalAsciiNoCase(const S& lhs, const T& rhs); - template <class S, class T> int compareString (const S& lhs, const T& rhs); - template <class S, class T> int compareAsciiNoCase(const S& lhs, const T& rhs); //basic case-insensitive comparison (considering A-Z only!) + // template <class S, class T> std::strong_ordering compareString (const S& lhs, const T& rhs); + template <class S, class T> std::weak_ordering compareAsciiNoCase(const S& lhs, const T& rhs); //basic case-insensitive comparison (considering A-Z only!) struct LessAsciiNoCase //STL container predicate { - template <class S> bool operator()(const S& lhs, const S& rhs) const { return compareAsciiNoCase(lhs, rhs) < 0; } + template <class S> bool operator()(const S& lhs, const S& rhs) const { return std::is_lt(compareAsciiNoCase(lhs, rhs)); } }; @@ -186,22 +186,22 @@ Char asciiToLower(Char c) namespace impl { -inline int strcmpWithNulls(const char* ptr1, const char* ptr2, size_t num) { return std:: memcmp(ptr1, ptr2, num); } //support embedded 0, unlike strncmp/wcsncmp! -inline int strcmpWithNulls(const wchar_t* ptr1, const wchar_t* ptr2, size_t num) { return std::wmemcmp(ptr1, ptr2, num); } // +//support embedded 0, unlike strncmp/wcsncmp: +inline std::strong_ordering strcmpWithNulls(const char* ptr1, const char* ptr2, size_t num) { return std:: memcmp(ptr1, ptr2, num) <=> 0; } +inline std::strong_ordering strcmpWithNulls(const wchar_t* ptr1, const wchar_t* ptr2, size_t num) { return std::wmemcmp(ptr1, ptr2, num) <=> 0; } template <class Char1, class Char2> inline -int strcmpAsciiNoCase(const Char1* lhs, const Char2* rhs, size_t len) +std::weak_ordering strcmpAsciiNoCase(const Char1* lhs, const Char2* rhs, size_t len) { while (len-- > 0) { const Char1 charL = asciiToLower(*lhs++); //ordering: lower-case chars have higher code points than uppper-case const Char2 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! + return makeUnsigned(charL) <=> makeUnsigned(charR); //unsigned char-comparison is the convention! } - return 0; + return std::weak_ordering::equivalent; } } @@ -210,7 +210,7 @@ template <class S, class T> inline bool startsWith(const S& str, const T& prefix) { const size_t pfLen = strLength(prefix); - return strLength(str) >= pfLen && impl::strcmpWithNulls(strBegin(str), strBegin(prefix), pfLen) == 0; + return strLength(str) >= pfLen && std::is_eq(impl::strcmpWithNulls(strBegin(str), strBegin(prefix), pfLen)); } @@ -218,7 +218,7 @@ template <class S, class T> inline bool startsWithAsciiNoCase(const S& str, const T& prefix) { const size_t pfLen = strLength(prefix); - return strLength(str) >= pfLen && impl::strcmpAsciiNoCase(strBegin(str), strBegin(prefix), pfLen) == 0; + return strLength(str) >= pfLen && std::is_eq(impl::strcmpAsciiNoCase(strBegin(str), strBegin(prefix), pfLen)); } @@ -227,7 +227,7 @@ bool endsWith(const S& str, const T& postfix) { const size_t strLen = strLength(str); const size_t pfLen = strLength(postfix); - return strLen >= pfLen && impl::strcmpWithNulls(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen) == 0; + return strLen >= pfLen && std::is_eq(impl::strcmpWithNulls(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen)); } @@ -236,7 +236,7 @@ bool endsWithAsciiNoCase(const S& str, const T& postfix) { const size_t strLen = strLength(str); const size_t pfLen = strLength(postfix); - return strLen >= pfLen && impl::strcmpAsciiNoCase(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen) == 0; + return strLen >= pfLen && std::is_eq(impl::strcmpAsciiNoCase(strBegin(str) + strLen - pfLen, strBegin(postfix), pfLen)); } @@ -244,7 +244,7 @@ template <class S, class T> inline bool equalString(const S& lhs, const T& rhs) { const size_t lhsLen = strLength(lhs); - return lhsLen == strLength(rhs) && impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), lhsLen) == 0; + return lhsLen == strLength(rhs) && std::is_eq(impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), lhsLen)); } @@ -252,34 +252,36 @@ template <class S, class T> inline bool equalAsciiNoCase(const S& lhs, const T& rhs) { const size_t lhsLen = strLength(lhs); - return lhsLen == strLength(rhs) && impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), lhsLen) == 0; + return lhsLen == strLength(rhs) && std::is_eq(impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), lhsLen)); } +#if 0 template <class S, class T> inline -int compareString(const S& lhs, const T& rhs) +std::strong_ordering compareString(const S& lhs, const T& rhs) { const size_t lhsLen = strLength(lhs); const size_t rhsLen = strLength(rhs); - //length check *after* strcmpWithNulls(): we do care about natural ordering: e.g. for "compareString(getUpperCase(lhs), getUpperCase(rhs))" - if (const int rv = impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); - rv != 0) - return rv; - return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); + //length check *after* strcmpWithNulls(): we DO care about natural ordering: e.g. for "compareString(getUpperCase(lhs), getUpperCase(rhs))" + if (const std::strong_ordering cmp = impl::strcmpWithNulls(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); + std::is_neq(cmp)) + return cmp; + return lhsLen <=> rhsLen; } +#endif template <class S, class T> inline -int compareAsciiNoCase(const S& lhs, const T& rhs) +std::weak_ordering compareAsciiNoCase(const S& lhs, const T& rhs) { const size_t lhsLen = strLength(lhs); const size_t rhsLen = strLength(rhs); - if (const int rv = impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); - rv != 0) - return rv; - return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); + if (const std::weak_ordering cmp = impl::strcmpAsciiNoCase(strBegin(lhs), strBegin(rhs), std::min(lhsLen, rhsLen)); + std::is_neq(cmp)) + return cmp; + return lhsLen <=> rhsLen; } @@ -583,17 +585,17 @@ namespace impl { enum class NumberType { - SIGNED_INT, - UNSIGNED_INT, - FLOATING_POINT, - OTHER, + signedInt, + unsignedInt, + floatingPoint, + other, }; -template <class S, class Num> S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) = delete; +template <class S, class Num> S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::other>) = delete; #if 0 //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) template <class S, class Num> inline -S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::other>) { std::basic_ostringstream<GetCharTypeT<S>> ss; ss << number; @@ -603,13 +605,13 @@ S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTH template <class S, class Num> inline -S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::FLOATING_POINT>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::floatingPoint>) { //don't use sprintf("%g"): way SLOWWWWWWER than std::to_chars() char buffer[128]; //zero-initialize? //let's give some leeway, but 24 chars should suffice: https://www.reddit.com/r/cpp/comments/dgj89g/cppcon_2019_stephan_t_lavavej_floatingpoint/f3j7d3q/ - const char* strEnd = to_chars(std::begin(buffer), std::end(buffer), number); + const char* strEnd = toChars(std::begin(buffer), std::end(buffer), number); S output; std::for_each(static_cast<const char*>(buffer), strEnd, @@ -657,7 +659,7 @@ void formatPositiveInteger(Num n, OutputIterator& it) template <class S, class Num> inline -S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::SIGNED_INT>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::signedInt>) { GetCharTypeT<S> buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize? //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency @@ -677,7 +679,7 @@ S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::SIG template <class S, class Num> inline -S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::UNSIGNED_INT>) +S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::unsignedInt>) { GetCharTypeT<S> buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize? //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41) @@ -691,10 +693,10 @@ S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::UNS //-------------------------------------------------------------------------------- -template <class Num, class S> Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) = delete; +template <class Num, class S> Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::other>) = delete; #if 0 //default string to number conversion using streams: convenient, but SLOW template <class Num, class S> inline -Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::other>) { using CharType = GetCharTypeT<S>; Num number = 0; @@ -708,7 +710,7 @@ inline double stringToFloat(const char* first, const char* last) { //don't use std::strtod(): 1. requires null-terminated string 2. SLOWER than std::from_chars() - return from_chars(first, last); + return fromChars(first, last); } @@ -718,12 +720,12 @@ double stringToFloat(const wchar_t* first, const wchar_t* last) std::string buf(last - first, '\0'); std::transform(first, last, buf.begin(), [](wchar_t c) { return static_cast<char>(c); }); - return from_chars(buf.c_str(), buf.c_str() + buf.size()); + return fromChars(buf.c_str(), buf.c_str() + buf.size()); } template <class Num, class S> inline -Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::FLOATING_POINT>) +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::floatingPoint>) { const auto* const first = strBegin(str); const auto* const last = first + strLength(str); @@ -771,7 +773,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i template <class Num, class S> inline -Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::SIGNED_INT>) +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::signedInt>) { bool hasMinusSign = false; //handle minus sign const Num number = extractInteger<Num>(str, hasMinusSign); @@ -780,7 +782,7 @@ Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::SIGNED template <class Num, class S> inline -Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic +Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::unsignedInt>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { bool hasMinusSign = false; //handle minus sign const Num number = extractInteger<Num>(str, hasMinusSign); @@ -798,10 +800,10 @@ template <class S, class Num> inline S numberTo(const Num& number) { using TypeTag = std::integral_constant<impl::NumberType, - IsSignedInt <Num>::value ? impl::NumberType::SIGNED_INT : - IsUnsignedInt<Num>::value ? impl::NumberType::UNSIGNED_INT : - IsFloat <Num>::value ? impl::NumberType::FLOATING_POINT : - impl::NumberType::OTHER>; + IsSignedInt <Num>::value ? impl::NumberType::signedInt : + IsUnsignedInt<Num>::value ? impl::NumberType::unsignedInt : + IsFloat <Num>::value ? impl::NumberType::floatingPoint : + impl::NumberType::other>; return impl::numberTo<S>(number, TypeTag()); } @@ -811,10 +813,10 @@ template <class Num, class S> inline Num stringTo(const S& str) { using TypeTag = std::integral_constant<impl::NumberType, - IsSignedInt <Num>::value ? impl::NumberType::SIGNED_INT : - IsUnsignedInt<Num>::value ? impl::NumberType::UNSIGNED_INT : - IsFloat <Num>::value ? impl::NumberType::FLOATING_POINT : - impl::NumberType::OTHER>; + IsSignedInt <Num>::value ? impl::NumberType::signedInt : + IsUnsignedInt<Num>::value ? impl::NumberType::unsignedInt : + IsFloat <Num>::value ? impl::NumberType::floatingPoint : + impl::NumberType::other>; return impl::stringTo<Num>(str, TypeTag()); } diff --git a/zen/sys_version.cpp b/zen/sys_version.cpp index 46918315..d07bbc33 100644 --- a/zen/sys_version.cpp +++ b/zen/sys_version.cpp @@ -78,14 +78,17 @@ OsVersionDetail zen::getOsVersionDetail() //throw SysError OsVersion zen::getOsVersion() { - try - { - static const OsVersionDetail verDetail = getOsVersionDetail(); //throw SysError - return verDetail.version; - } - catch (const SysError& e) + static const OsVersionDetail verDetail = [] { - std::cerr << utfTo<std::string>(e.toString()) << '\n'; - return {}; //sigh, it's a jungle out there: https://freefilesync.org/forum/viewtopic.php?t=7276 - } + try + { + return getOsVersionDetail(); //throw SysError + } + catch (const SysError& e) + { + std::cerr << utfTo<std::string>(e.toString()) << '\n'; + return OsVersionDetail{}; //sigh, it's a jungle out there: https://freefilesync.org/forum/viewtopic.php?t=7276 + } + }(); + return verDetail.version; } diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 06c839e3..c9861219 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -106,38 +106,38 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const /* https://docs.microsoft.com/de-de/windows/desktop/Intl/handling-sorting-in-your-applications -Perf test: compare strings 10 mio times; 64 bit build ------------------------------------------------------ - string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-" - string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf" - -Windows (UTF16 wchar_t) - 4 ns | wcscmp - 67 ns | CompareStringOrdinalFunc+ + bIgnoreCase -314 ns | LCMapString + wmemcmp - -OS X (UTF8 char) - 6 ns | strcmp - 98 ns | strcasecmp - 120 ns | strncasecmp + std::min(sizeLhs, sizeRhs); - 856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive) -1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive) -________________________ -time per call | function */ - -int compareNativePath(const Zstring& lhs, const Zstring& rhs) + Perf test: compare strings 10 mio times; 64 bit build + ----------------------------------------------------- + string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-" + string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf" + + Windows (UTF16 wchar_t) + 4 ns | wcscmp + 67 ns | CompareStringOrdinalFunc+ + bIgnoreCase + 314 ns | LCMapString + wmemcmp + + OS X (UTF8 char) + 6 ns | strcmp + 98 ns | strcasecmp + 120 ns | strncasecmp + std::min(sizeLhs, sizeRhs); + 856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive) + 1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive) + ________________________ + time per call | function */ + +std::weak_ordering compareNativePath(const Zstring& lhs, const Zstring& rhs) { assert(lhs.find(Zchar('\0')) == Zstring::npos); //don't expect embedded nulls! assert(rhs.find(Zchar('\0')) == Zstring::npos); // - return compareString(lhs, rhs); + return lhs <=> rhs; } namespace { -int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) +std::weak_ordering compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) { //- strncasecmp implements ASCII CI-comparsion only! => signature is broken for UTF8-input; toupper() similarly doesn't support Unicode //- wcsncasecmp: https://opensource.apple.com/source/Libc/Libc-763.12/string/wcsncasecmp-fbsd.c @@ -150,7 +150,7 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh const std::optional<impl::CodePoint> cpL = decL.getNext(); const std::optional<impl::CodePoint> cpR = decR.getNext(); if (!cpL || !cpR) - return static_cast<int>(!cpR) - static_cast<int>(!cpL); + return !cpR <=> !cpL; static_assert(sizeof(gunichar) == sizeof(impl::CodePoint)); @@ -158,14 +158,13 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh const gunichar charR = ::g_unichar_toupper(*cpR); //e.g. "Σ" (upper case) can be lower-case "ς" in the end of the word or "σ" in the middle. if (charL != charR) //ordering: "to lower" converts to higher code points than "to upper" - return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention! - //unsigned underflow is well-defined! + return makeUnsigned(charL) <=> makeUnsigned(charR); //unsigned char-comparison is the convention! } } } -int compareNatural(const Zstring& lhs, const Zstring& rhs) +std::weak_ordering compareNatural(const Zstring& lhs, const Zstring& rhs) { //Unicode normal forms: // Windows: CompareString() already ignores NFD/NFC differences: nice... @@ -192,13 +191,13 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs) for (;;) { if (strL == strEndL || strR == strEndR) - return static_cast<int>(strL != strEndL) - static_cast<int>(strR != strEndR); //"nothing" before "something" + return (strL != strEndL) <=> (strR != strEndR); //"nothing" before "something" //note: "something" never would have been condensed to "nothing" further below => can finish evaluation here const bool wsL = isWhiteSpace(*strL); const bool wsR = isWhiteSpace(*strR); if (wsL != wsR) - return static_cast<int>(!wsL) - static_cast<int>(!wsR); //whitespace before non-ws! + return !wsL <=> !wsR; //whitespace before non-ws! if (wsL) { ++strL, ++strR; @@ -210,7 +209,7 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs) const bool digitL = isDigit(*strL); const bool digitR = isDigit(*strR); if (digitL != digitR) - return static_cast<int>(!digitL) - static_cast<int>(!digitR); //number before chars! + return !digitL <=> !digitR; //numbers before chars! if (digitL) { while (strL != strEndL && *strL == '0') ++strL; @@ -222,7 +221,7 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs) const bool endL = strL == strEndL || !isDigit(*strL); const bool endR = strR == strEndR || !isDigit(*strR); if (endL != endR) - return static_cast<int>(!endL) - static_cast<int>(!endR); //more digits means bigger number + return !endL <=> !endR; //more digits means bigger number if (endL) break; //same number of digits @@ -230,7 +229,7 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs) rv = *strL - *strR; //found first digit difference comparing from left } if (rv != 0) - return rv; + return rv <=> 0; continue; } @@ -240,9 +239,9 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs) while (strL != strEndL && !isWhiteSpace(*strL) && !isDigit(*strL)) ++strL; while (strR != strEndR && !isWhiteSpace(*strR) && !isDigit(*strR)) ++strR; - const int rv = compareNoCaseUtf8(textBeginL, strL - textBeginL, textBeginR, strR - textBeginR); - if (rv != 0) - return rv; + if (const std::weak_ordering cmp = compareNoCaseUtf8(textBeginL, strL - textBeginL, textBeginR, strR - textBeginR); + std::is_neq(cmp)) + return cmp; } } diff --git a/zen/zstring.h b/zen/zstring.h index 607d3859..09aa43b9 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -33,9 +33,9 @@ Zstring getUpperCase(const Zstring& str); //Windows, Linux: precomposed //macOS: decomposed Zstring getUnicodeNormalForm(const Zstring& str); -// "In fact, Unicode declares that there is an equivalence relationship between decomposed and composed sequences, -// and conformant software should not treat canonically equivalent sequences, whether composed or decomposed or something in between, as different." -// https://www.win.tue.nl/~aeb/linux/uc/nfc_vs_nfd.html +/* "In fact, Unicode declares that there is an equivalence relationship between decomposed and composed sequences, + and conformant software should not treat canonically equivalent sequences, whether composed or decomposed or something in between, as different." + https://www.win.tue.nl/~aeb/linux/uc/nfc_vs_nfd.html */ struct LessUnicodeNormal { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return getUnicodeNormalForm(lhs) < getUnicodeNormalForm(rhs); } }; @@ -55,20 +55,20 @@ struct ZstringNoCase //use as STL container key: avoid needless upper-case conve //------------------------------------------------------------------------------------------ -//Compare *local* file paths: -// Windows: igore case -// Linux: byte-wise comparison -// macOS: ignore case + Unicode normalization forms -int compareNativePath(const Zstring& lhs, const Zstring& rhs); +/* Compare *local* file paths: + Windows: igore case + Linux: byte-wise comparison + macOS: ignore case + Unicode normalization forms */ +std::weak_ordering compareNativePath(const Zstring& lhs, const Zstring& rhs); -inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; } +inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return std::is_eq(compareNativePath(lhs, rhs)); } -struct LessNativePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNativePath(lhs, rhs) < 0; } }; +struct LessNativePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return std::is_lt(compareNativePath(lhs, rhs)); } }; //------------------------------------------------------------------------------------------ -int compareNatural(const Zstring& lhs, const Zstring& rhs); +std::weak_ordering compareNatural(const Zstring& lhs, const Zstring& rhs); -struct LessNaturalSort { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNatural(lhs, rhs) < 0; } }; +struct LessNaturalSort { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return std::is_lt(compareNatural(lhs, rhs)); } }; //------------------------------------------------------------------------------------------ |