summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2020-10-03 01:04:14 +0000
committerB Stack <bgstack15@gmail.com>2020-10-03 01:04:14 +0000
commit0576c1a2ab5ff534348c879ea03bb9c9d9f7ac4c (patch)
treed8a89392817379e3036c42eedebf33d4fb372dfd /zen
parentMerge branch '11.1' into 'master' (diff)
parentadd upstream 11.2 (diff)
downloadFreeFileSync-0576c1a2ab5ff534348c879ea03bb9c9d9f7ac4c.tar.gz
FreeFileSync-0576c1a2ab5ff534348c879ea03bb9c9d9f7ac4c.tar.bz2
FreeFileSync-0576c1a2ab5ff534348c879ea03bb9c9d9f7ac4c.zip
Merge branch '11.2' into 'master'11.2
add upstream 11.2 See merge request opensource-tracking/FreeFileSync!26
Diffstat (limited to 'zen')
-rw-r--r--zen/basic_math.h9
-rw-r--r--zen/build_info.h9
-rw-r--r--zen/file_access.cpp4
-rw-r--r--zen/file_io.cpp8
-rw-r--r--zen/file_io.h4
-rw-r--r--zen/format_unit.cpp39
-rw-r--r--zen/format_unit.h15
-rw-r--r--zen/legacy_compiler.cpp4
-rw-r--r--zen/legacy_compiler.h25
-rw-r--r--zen/open_ssl.cpp5
-rw-r--r--zen/recycler.cpp71
-rw-r--r--zen/socket.h1
-rw-r--r--zen/stl_tools.h10
-rw-r--r--zen/string_base.h4
-rw-r--r--zen/string_tools.h106
-rw-r--r--zen/sys_version.cpp21
-rw-r--r--zen/zstring.cpp67
-rw-r--r--zen/zstring.h24
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)); } };
//------------------------------------------------------------------------------------------
bgstack15