summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2022-11-22 08:54:34 -0500
committerB. Stack <bgstack15@gmail.com>2022-11-22 08:54:34 -0500
commita034cfca98d4408b175938740628a54f57eb7614 (patch)
tree501fd78c6276c0be8be8d2c671a58dd0598060b5 /zen
parentadd upstream 11.27 (diff)
downloadFreeFileSync-a034cfca98d4408b175938740628a54f57eb7614.tar.gz
FreeFileSync-a034cfca98d4408b175938740628a54f57eb7614.tar.bz2
FreeFileSync-a034cfca98d4408b175938740628a54f57eb7614.zip
add upstream 11.2811.28
Diffstat (limited to 'zen')
-rw-r--r--zen/basic_math.h8
-rw-r--r--zen/crc.h8
-rw-r--r--zen/file_access.cpp3
-rw-r--r--zen/file_path.cpp6
-rw-r--r--zen/format_unit.cpp21
-rw-r--r--zen/format_unit.h2
-rw-r--r--zen/http.cpp54
-rw-r--r--zen/http.h4
-rw-r--r--zen/legacy_compiler.h11
-rw-r--r--zen/open_ssl.cpp19
-rw-r--r--zen/process_exec.cpp9
-rw-r--r--zen/serialize.h2
-rw-r--r--zen/stl_tools.h69
-rw-r--r--zen/string_base.h91
-rw-r--r--zen/string_tools.h71
-rw-r--r--zen/sys_info.cpp39
-rw-r--r--zen/sys_version.cpp9
-rw-r--r--zen/zstring.cpp52
-rw-r--r--zen/zstring.h4
19 files changed, 305 insertions, 177 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index c8a06b78..7258128f 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -20,7 +20,7 @@ template <class T> int sign(T value); //returns one of {-1, 0, 1}
template <class T> bool isNull(T value); //...definitively fishy...
template <class T, class InputIterator> //precondition: range must be sorted!
-auto nearMatch(const T& val, InputIterator first, InputIterator last);
+auto roundToGrid(T val, InputIterator first, InputIterator last);
template <class N, class D> auto intDivRound(N numerator, D denominator);
template <class N, class D> auto intDivCeil (N numerator, D denominator);
@@ -122,12 +122,12 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input
*/
template <class T, class InputIterator> inline
-auto nearMatch(const T& val, InputIterator first, InputIterator last)
+auto roundToGrid(T val, InputIterator first, InputIterator last)
{
+ assert(std::is_sorted(first, last));
if (first == last)
- return static_cast<decltype(*first)>(0);
+ return static_cast<decltype(*first)>(val);
- assert(std::is_sorted(first, last));
InputIterator it = std::lower_bound(first, last, val);
if (it == last)
return *--last;
diff --git a/zen/crc.h b/zen/crc.h
index c65ce2e7..009ff9da 100644
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -12,8 +12,8 @@
namespace zen
{
-uint16_t getCrc16(const std::string& str);
-uint32_t getCrc32(const std::string& str);
+uint16_t getCrc16(const std::string_view& str);
+uint32_t getCrc32(const std::string_view& str);
template <class ByteIterator> uint16_t getCrc16(ByteIterator first, ByteIterator last);
template <class ByteIterator> uint32_t getCrc32(ByteIterator first, ByteIterator last);
@@ -21,8 +21,8 @@ template <class ByteIterator> uint32_t getCrc32(ByteIterator first, ByteIterator
//------------------------- implementation -------------------------------
-inline uint16_t getCrc16(const std::string& str) { return getCrc16(str.begin(), str.end()); }
-inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(), str.end()); }
+inline uint16_t getCrc16(const std::string_view& str) { return getCrc16(str.begin(), str.end()); }
+inline uint32_t getCrc32(const std::string_view& str) { return getCrc32(str.begin(), str.end()); }
template <class ByteIterator> inline
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index a52ea9b8..01dda68c 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -126,9 +126,6 @@ namespace
int64_t zen::getFreeDiskSpace(const Zstring& folderPath) //throw FileError
{
const auto& [existingPath, existingType] = getExistingPath(folderPath); //throw FileError
-
- warn_static("what if existingType is symlink?")
-
try
{
struct statfs info = {};
diff --git a/zen/file_path.cpp b/zen/file_path.cpp
index 912d5a37..73a3e923 100644
--- a/zen/file_path.cpp
+++ b/zen/file_path.cpp
@@ -77,6 +77,8 @@ std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
Zstring zen::appendSeparator(Zstring path) //support rvalue references!
{
+ assert(!endsWith(path, FILE_NAME_SEPARATOR == Zstr('/') ? Zstr('\\' ) : Zstr('/' )));
+
if (!endsWith(path, FILE_NAME_SEPARATOR))
path += FILE_NAME_SEPARATOR;
return path; //returning a by-value parameter => RVO if possible, r-value otherwise!
@@ -156,8 +158,8 @@ Zstring zen::getFileExtension(const Zstring& filePath)
std::weak_ordering zen::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); //
+ assert(!contains(lhs, Zchar('\0'))); //don't expect embedded nulls!
+ assert(!contains(rhs, Zchar('\0'))); //
return lhs <=> rhs;
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 8b3fccfe..a5dd5152 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -111,8 +111,8 @@ std::wstring roundToBlock(double timeInHigh,
const double granularity = 0.1;
const double timeInLow = timeInHigh * unitLowPerHigh;
const int blockSizeLow = granularity * timeInHigh < 1 ?
- numeric::nearMatch(granularity * timeInLow, std::begin(stepsLow), std::end(stepsLow)):
- numeric::nearMatch(granularity * timeInHigh, std::begin(stepsHigh), std::end(stepsHigh)) * unitLowPerHigh;
+ numeric::roundToGrid(granularity * timeInLow, std::begin(stepsLow), std::end(stepsLow)):
+ numeric::roundToGrid(granularity * timeInHigh, std::begin(stepsHigh), std::end(stepsHigh)) * unitLowPerHigh;
const int roundedtimeInLow = std::lround(timeInLow / blockSizeLow) * blockSizeLow;
std::wstring output = formatUnitTime(roundedtimeInLow / unitLowPerHigh, unitHigh);
@@ -149,10 +149,21 @@ std::wstring zen::formatRemainingTime(double timeInSec)
}
-std::wstring zen::formatPercent0(double fraction)
+std::wstring zen::formatProgressPercent(double fraction, int decPlaces)
{
- return numberTo<std::wstring>(std::lround(fraction * 100)) + L'%'; //need to localize percent!?
- //return printNumber<std::wstring>(L"%.2f", fraction * 100) + L'%';
+#if 0 //special case for perf!?
+ if (decPlaces == 0)
+ return numberTo<std::wstring>(static_cast<int>(fraction * 100)) + L'%';
+#endif
+ //round down! don't show 100% when not actually done: https://freefilesync.org/forum/viewtopic.php?t=9781
+ const double blocks = std::pow(10, decPlaces);
+ const double percent = std::floor(fraction * 100 * blocks) / blocks;
+
+ assert(0 <= decPlaces && decPlaces <= 9);
+ wchar_t format[] = L"%.0f" L"%%" /*literal %: need to localize?*/;
+ format[2] += static_cast<wchar_t>(std::clamp(decPlaces, 0, 9));
+
+ return printNumber<std::wstring>(format, percent);
}
diff --git a/zen/format_unit.h b/zen/format_unit.h
index d1ebc28c..5498e8c2 100644
--- a/zen/format_unit.h
+++ b/zen/format_unit.h
@@ -18,7 +18,7 @@ namespace zen
const int bytesPerKilo = 1000;
std::wstring formatFilesizeShort(int64_t filesize);
std::wstring formatRemainingTime(double timeInSec);
-std::wstring formatPercent0(double fraction /*within [0, 1]*/); //zero decimal places
+std::wstring formatProgressPercent(double fraction /*[0, 1]*/, int decPlaces = 0 /*[0, 9]*/); //rounded down!
std::wstring formatUtcToLocalTime(time_t utcTime); //like Windows Explorer would...
std::wstring formatTwoDigitPrecision (double value); //format with fixed number of digits
diff --git a/zen/http.cpp b/zen/http.cpp
index 5054ef3f..540b4ef6 100644
--- a/zen/http.cpp
+++ b/zen/http.cpp
@@ -90,10 +90,10 @@ public:
readRequest = [&, postBufStream{MemoryStreamIn(*postBuf)}](std::span<char> buf) mutable
{
const size_t bytesRead = postBufStream.read(buf.data(), buf.size());
- *postBytesSent += bytesRead;
+ * postBytesSent += bytesRead;
return bytesRead;
};
- extraOptions.emplace_back(CURLOPT_POST, 1);
+ extraOptions.emplace_back(CURLOPT_POST, 1);
extraOptions.emplace_back(CURLOPT_POSTFIELDSIZE_LARGE, postBuf->size()); //avoid HTTP chunked transfer encoding?
}
@@ -162,19 +162,21 @@ public:
const std::string headBuf = futHeader.get(); //throw SysError
//parse header: https://www.w3.org/Protocols/HTTP/1.0/spec.html#Request-Line
- const std::string& statusBuf = beforeFirst(headBuf, "\r\n", IfNotFoundReturn::all);
- const std::string& headersBuf = afterFirst (headBuf, "\r\n", IfNotFoundReturn::none);
+ const std::string_view& statusBuf = beforeFirst<std::string_view>(headBuf, "\r\n", IfNotFoundReturn::all);
+ const std::string_view& headersBuf = afterFirst <std::string_view>(headBuf, "\r\n", IfNotFoundReturn::none);
- const std::vector<std::string> statusItems = split(statusBuf, ' ', SplitOnEmpty::allow); //HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ const std::vector<std::string_view> statusItems = splitCpy(statusBuf, ' ', SplitOnEmpty::allow); //HTTP-Version SP Status-Code SP Reason-Phrase CRLF
if (statusItems.size() < 2 || !startsWith(statusItems[0], "HTTP/"))
throw SysError(L"Invalid HTTP response: \"" + utfTo<std::wstring>(statusBuf) + L'"');
statusCode_ = stringTo<int>(statusItems[1]);
- for (const std::string& line : split(headersBuf, '\n', SplitOnEmpty::skip)) //careful: actual line separator is "\r\n"!
- responseHeaders_[trimCpy(beforeFirst(line, ':', IfNotFoundReturn::all))] =
- /**/ trimCpy(afterFirst (line, ':', IfNotFoundReturn::none));
-
+ split(headersBuf, '\n', [&](const std::string_view line)
+ {
+ if (!line.empty()) //careful: actual line separator is "\r\n"!
+ responseHeaders_.emplace(trimCpy(beforeFirst(line, ':', IfNotFoundReturn::all)),
+ trimCpy(afterFirst (line, ':', IfNotFoundReturn::none)));
+ });
/* let's NOT consider "Content-Length" header:
- may be unavailable ("Transfer-Encoding: chunked")
- may refer to compressed data size ("Content-Encoding: gzip") */
@@ -274,7 +276,7 @@ std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url,
//encode for "application/x-www-form-urlencoded"
-std::string urlencode(const std::string& str)
+std::string urlencode(const std::string_view& str)
{
std::string output;
for (const char c : str) //follow PHP spec: https://github.com/php/php-src/blob/e99d5d39239c611e1e7304e79e88545c4e71a073/ext/standard/url.c#L455
@@ -296,7 +298,7 @@ std::string urlencode(const std::string& str)
}
-std::string urldecode(const std::string& str)
+std::string urldecode(const std::string_view& str)
{
std::string output;
for (size_t i = 0; i < str.size(); ++i)
@@ -331,13 +333,16 @@ std::string zen::xWwwFormUrlEncode(const std::vector<std::pair<std::string, std:
}
-std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const std::string& str)
+std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const std::string_view str)
{
std::vector<std::pair<std::string, std::string>> output;
- for (const std::string& nvPair : split(str, '&', SplitOnEmpty::skip))
- output.emplace_back(urldecode(beforeFirst(nvPair, '=', IfNotFoundReturn::all)),
- urldecode(afterFirst (nvPair, '=', IfNotFoundReturn::none)));
+ split(str, '&', [&](const std::string_view nvPair)
+ {
+ if (!nvPair.empty())
+ output.emplace_back(urldecode(beforeFirst(nvPair, '=', IfNotFoundReturn::all)),
+ urldecode(afterFirst (nvPair, '=', IfNotFoundReturn::none)));
+ });
return output;
}
@@ -469,16 +474,16 @@ std::wstring zen::formatHttpError(int sc)
}
-bool zen::isValidEmail(const std::string& email)
+bool zen::isValidEmail(const std::string_view& email)
{
//https://en.wikipedia.org/wiki/Email_address#Syntax
//https://tools.ietf.org/html/rfc3696 => note errata! https://www.rfc-editor.org/errata_search.php?rfc=3696
//https://tools.ietf.org/html/rfc5321
- std::string local = beforeLast(email, '@', IfNotFoundReturn::none);
- std::string domain = afterLast(email, '@', IfNotFoundReturn::none);
+ std::string_view local = beforeLast(email, '@', IfNotFoundReturn::none);
+ std::string_view domain = afterLast(email, '@', IfNotFoundReturn::none);
//consider: "t@st"@email.com t\@st@email.com"
- auto stripComments = [](std::string& part)
+ auto stripComments = [](std::string_view& part)
{
if (startsWith(part, '('))
part = afterFirst(part, ')', IfNotFoundReturn::none);
@@ -494,15 +499,16 @@ bool zen::isValidEmail(const std::string& email)
return false;
//---------------------------------------------------------------------
+ //not going to parse and validate this!
const bool quoted = (startsWith(local, '"') && endsWith(local, '"')) ||
contains(local, '\\'); //e.g. "t\@st@email.com"
- if (!quoted) //I'm not going to parse and validate this!
- for (const std::string& comp : split(local, '.', SplitOnEmpty::allow))
+ if (!quoted)
+ for (const std::string_view& comp : splitCpy(local, '.', SplitOnEmpty::allow))
if (comp.empty() || !std::all_of(comp.begin(), comp.end(), [](char c)
{
- const char printable[] = "!#$%&'*+-/=?^_`{|}~";
+ constexpr std::string_view printable("!#$%&'*+-/=?^_`{|}~");
return isAsciiAlpha(c) || isDigit(c) || !isAsciiChar(c) ||
- std::find(std::begin(printable), std::end(printable), c) != std::end(printable);
+ contains(printable, c);
}))
return false;
//---------------------------------------------------------------------
@@ -514,7 +520,7 @@ bool zen::isValidEmail(const std::string& email)
if (!contains(domain, '.'))
return false;
- for (const std::string& comp : split(domain, '.', SplitOnEmpty::allow))
+ for (const std::string_view& comp : splitCpy(domain, '.', SplitOnEmpty::allow))
if (comp.empty() || comp.size() > 63 ||
!std::all_of(comp.begin(), comp.end(), [](char c) { return isAsciiAlpha(c) ||isDigit(c) || !isAsciiChar(c) || c == '-'; }))
return false;
diff --git a/zen/http.h b/zen/http.h
index ebbb8bd8..1943b129 100644
--- a/zen/http.h
+++ b/zen/http.h
@@ -50,11 +50,11 @@ HttpInputStream sendHttpPost(const Zstring& url,
bool internetIsAlive(); //noexcept
std::wstring formatHttpError(int httpStatus);
-bool isValidEmail(const std::string& email);
+bool isValidEmail(const std::string_view& email);
std::string htmlSpecialChars(const std::string_view& str);
std::string xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs);
-std::vector<std::pair<std::string, std::string>> xWwwFormUrlDecode(const std::string& str);
+std::vector<std::pair<std::string, std::string>> xWwwFormUrlDecode(const std::string_view str);
}
#endif //HTTP_H_879083425703425702
diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h
index 33394de3..8f93153e 100644
--- a/zen/legacy_compiler.h
+++ b/zen/legacy_compiler.h
@@ -8,6 +8,7 @@
#define LEGACY_COMPILER_H_839567308565656789
#include <version> //contains all __cpp_lib_<feature> macros
+#include <string>
/* C++ standard conformance:
https://en.cppreference.com/w/cpp/feature_test
@@ -25,6 +26,16 @@
namespace std
{
+
+
+//W(hy)TF is this not standard? https://stackoverflow.com/a/47735624
+template <class Char, class Traits, class Alloc> inline
+basic_string<Char, Traits, Alloc> operator+(basic_string<Char, Traits, Alloc>&& lhs, const basic_string_view<Char> rhs)
+{ return move(lhs.append(rhs.begin(), rhs.end())); } //the move *is* needed!!!
+
+//template <class Char> inline
+//basic_string<Char> operator+(const basic_string<Char>& lhs, const basic_string_view<Char>& rhs) { return basic_string<Char>(lhs) + rhs; }
+//-> somewhat inefficient: enable + optimize when needed
}
//---------------------------------------------------------------------------------
diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp
index 6a5be3e8..1f556656 100644
--- a/zen/open_ssl.cpp
+++ b/zen/open_ssl.cpp
@@ -419,30 +419,29 @@ bool zen::isPuttyKeyStream(const std::string& keyStream)
std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::string& passphrase) //throw SysError
{
- std::vector<std::string> lines;
+ std::vector<std::string_view> lines;
- split2(keyStream, isLineBreak<char>,
- [&lines](const char* blockFirst, const char* blockLast)
+ split2(keyStream, isLineBreak<char>, [&lines](const std::string_view block)
{
- if (blockFirst != blockLast) //consider Windows' <CR><LF>
- lines.emplace_back(blockFirst, blockLast);
+ if (!block.empty()) //consider Windows' <CR><LF>
+ lines.push_back(block);
});
//----------- parse PuTTY ppk structure ----------------------------------
auto itLine = lines.begin();
if (itLine == lines.end() || !startsWith(*itLine, "PuTTY-User-Key-File-2: "))
throw SysError(L"Unknown key file format");
- const std::string algorithm = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
+ const std::string_view algorithm = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
++itLine;
if (itLine == lines.end() || !startsWith(*itLine, "Encryption: "))
throw SysError(L"Unknown key encryption");
- const std::string keyEncryption = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
+ const std::string_view keyEncryption = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
++itLine;
if (itLine == lines.end() || !startsWith(*itLine, "Comment: "))
throw SysError(L"Invalid key comment");
- const std::string comment = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
+ const std::string_view comment = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
++itLine;
if (itLine == lines.end() || !startsWith(*itLine, "Public-Lines: "))
@@ -471,7 +470,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
if (itLine == lines.end() || !startsWith(*itLine, "Private-MAC: "))
throw SysError(L"Invalid key: MAC missing");
- const std::string macHex = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
+ const std::string_view macHex = afterFirst(*itLine, ' ', IfNotFoundReturn::none);
++itLine;
//----------- unpack key file elements ---------------------
@@ -784,7 +783,7 @@ std::string zen::convertPuttyKeyToPkix(const std::string& keyStream, const std::
algorithm == "ecdsa-sha2-nistp384" ||
algorithm == "ecdsa-sha2-nistp521")
{
- const std::string algoShort = afterLast(algorithm, '-', IfNotFoundReturn::none);
+ const std::string_view algoShort = afterLast(algorithm, '-', IfNotFoundReturn::none);
if (extractStringPub() != algoShort)
throw SysError(L"Invalid public key stream (header)");
diff --git a/zen/process_exec.cpp b/zen/process_exec.cpp
index a2c02eb0..ffc90b4f 100644
--- a/zen/process_exec.cpp
+++ b/zen/process_exec.cpp
@@ -21,19 +21,20 @@ Zstring zen::escapeCommandArg(const Zstring& arg)
{
//*INDENT-OFF* if not put exactly here, Astyle will seriously mess this .cpp file up!
Zstring output;
- for (const Zchar c : arg)
+ for (const char c : arg)
switch (c)
{
+ //case ' ': output += "\\ "; break; -> maybe nicer to use quotes instead?
case '"': output += "\\\""; break; //Windows: not needed; " cannot be used as file name
case '\\': output += "\\\\"; break; //Windows: path separator! => don't escape
case '`': output += "\\`"; break; //yes, used in some paths => Windows: no escaping required
default: output += c; break;
}
-//*INDENT-ON*
- if (contains(output, Zstr(' ')))
- output = Zstr('"') + output + Zstr('"'); //Windows: escaping a single blank instead would not work
+ if (contains(arg, ' '))
+ output = '"' + output + '"'; //caveat: single-quotes not working on macOS if string contains escaped chars! no such issue on Linux
return output;
+//*INDENT-ON*
}
diff --git a/zen/serialize.h b/zen/serialize.h
index 53a6fc62..dd393422 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -79,7 +79,7 @@ template < class BufferedInputStream> void readArray (BufferedInputSt
struct IOCallbackDivider
{
IOCallbackDivider(const IoCallback& notifyUnbufferedIO, int64_t& totalBytesNotified) :
- totalBytesNotified_(totalBytesNotified),
+ totalBytesNotified_(totalBytesNotified),
notifyUnbufferedIO_(notifyUnbufferedIO) { assert(totalBytesNotified == 0); }
void operator()(int64_t bytesDelta) //throw X!
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 66af8551..bb005e34 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -58,14 +58,22 @@ void removeDuplicatesStable(std::vector<T, Alloc>& v);
template <class BidirectionalIterator, class T>
BidirectionalIterator findLast(BidirectionalIterator first, BidirectionalIterator last, const T& value);
+template <class RandomAccessIterator1, class RandomAccessIterator2> inline
+RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last,
+ const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast);
+
+template <class RandomAccessIterator1, class RandomAccessIterator2, class IsEq> inline
+RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last,
+ const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast, IsEq isEqual);
+
//replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name)
-template <class BidirectionalIterator1, class BidirectionalIterator2>
-BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
- BidirectionalIterator2 first2, BidirectionalIterator2 last2);
+template <class RandomAccessIterator1, class RandomAccessIterator2>
+RandomAccessIterator1 searchLast(RandomAccessIterator1 first, RandomAccessIterator1 last,
+ RandomAccessIterator2 needleFirst, RandomAccessIterator2 needleLast);
//binary search returning an iterator
-template <class Iterator, class T, class CompLess>
-Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less);
+template <class RandomAccessIterator, class T, class CompLess>
+RandomAccessIterator binarySearch(RandomAccessIterator first, RandomAccessIterator last, const T& value, CompLess less);
//read-only variant of std::merge; input: two sorted ranges
template <class Iterator, class FunctionLeftOnly, class FunctionBoth, class FunctionRightOnly, class Compare>
@@ -199,10 +207,10 @@ void removeDuplicatesStable(std::vector<T, Alloc>& v)
}
-template <class Iterator, class T, class CompLess> inline
-Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less)
+template <class RandomAccessIterator, class T, class CompLess> inline
+RandomAccessIterator binarySearch(RandomAccessIterator first, RandomAccessIterator last, const T& value, CompLess less)
{
- static_assert(std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>);
+ static_assert(std::is_same_v<typename std::iterator_traits<RandomAccessIterator>::iterator_category, std::random_access_iterator_tag>);
first = std::lower_bound(first, last, value, less); //alternative: std::partition_point
if (first != last && !less(value, *first))
@@ -226,29 +234,56 @@ BidirectionalIterator findLast(const BidirectionalIterator first, const Bidirect
}
-template <class BidirectionalIterator1, class BidirectionalIterator2> inline
-BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
- const BidirectionalIterator2 first2, const BidirectionalIterator2 last2)
+template <class RandomAccessIterator1, class RandomAccessIterator2> inline
+RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last,
+ const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast)
+{
+ if (needleLast - needleFirst == 1) //don't use expensive std::search unless required!
+ return std::find(first, last, *needleFirst);
+
+ return std::search(first, last,
+ needleFirst, needleLast);
+}
+
+
+template <class RandomAccessIterator1, class RandomAccessIterator2, class IsEq> inline
+RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last,
+ const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast, IsEq isEqual)
{
- const BidirectionalIterator1 itNotFound = last1;
+ if (needleLast - needleFirst == 1) //don't use expensive std::search unless required!
+ return std::find_if(first, last, [needleFirst, isEqual](const auto c){ return isEqual(*needleFirst, c); });
+
+ return std::search(first, last,
+ needleFirst, needleLast, isEqual);
+}
+
+
+template <class RandomAccessIterator1, class RandomAccessIterator2> inline
+RandomAccessIterator1 searchLast(const RandomAccessIterator1 first, RandomAccessIterator1 last,
+ const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast)
+{
+ if (needleLast - needleFirst == 1) //fast-path
+ return findLast(first, last, *needleFirst);
+
+ const RandomAccessIterator1 itNotFound = last;
//reverse iteration: 1. check 2. decrement 3. evaluate
for (;;)
{
- BidirectionalIterator1 it1 = last1;
- BidirectionalIterator2 it2 = last2;
+ RandomAccessIterator1 it1 = last;
+ RandomAccessIterator2 it2 = needleLast;
for (;;)
{
- if (it2 == first2) return it1;
- if (it1 == first1) return itNotFound;
+ if (it2 == needleFirst) return it1;
+ if (it1 == first) return itNotFound;
--it1;
--it2;
if (*it1 != *it2) break;
}
- --last1;
+ --last;
}
}
diff --git a/zen/string_base.h b/zen/string_base.h
index a87827e6..3ebf848a 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -92,7 +92,7 @@ protected:
//this needs to be checked before writing to "ptr"
static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; }
- static size_t length(const Char* ptr) { return descr(ptr)->length; }
+ static size_t size(const Char* ptr) { return descr(ptr)->length; }
static void setLength(Char* ptr, size_t newLength)
{
@@ -174,7 +174,7 @@ protected:
return d->refCount == 1 && minCapacity <= d->capacity;
}
- static size_t length(const Char* ptr) { return descr(ptr)->length; }
+ static size_t size(const Char* ptr) { return descr(ptr)->length; }
static void setLength(Char* ptr, size_t newLength)
{
@@ -228,17 +228,21 @@ public:
Zbase();
Zbase(const Char* str) : Zbase(str, str + strLength(str)) {} //implicit conversion from a C-string!
Zbase(const Char* str, size_t len) : Zbase(str, str + len) {}
+ explicit Zbase(const std::basic_string_view<Char> view) : Zbase(view.begin(), view.end()) {}
Zbase(size_t count, Char fillChar);
- Zbase(const Zbase& str);
- Zbase(Zbase&& tmp) noexcept;
template <class InputIterator>
Zbase(InputIterator first, InputIterator last);
+ Zbase(const Zbase& str);
+ Zbase(Zbase&& tmp) noexcept;
//explicit Zbase(Char ch); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! //-> non-standard extension!!!
~Zbase();
//operator const Char* () const; //NO implicit conversion to a C-string!! Many problems... one of them: if we forget to provide operator overloads, it'll just work with a Char*...
+ operator std::basic_string_view<Char>() const& noexcept { return {data(), size()}; }
+ operator std::basic_string_view<Char>() const&& = delete; //=> probably a bug!
+
//STL accessors
using iterator = Char*;
using const_iterator = const Char*;
@@ -250,25 +254,28 @@ public:
iterator end ();
const_iterator begin () const { return rawStr_; }
- const_iterator end () const { return rawStr_ + length(); }
+ const_iterator end () const { return rawStr_ + size(); }
const_iterator cbegin() const { return begin(); }
const_iterator cend () const { return end (); }
//std::string functions
- size_t length() const;
- size_t size () const { return length(); }
+ size_t length() const { return size(); }
+ size_t size () const;
const Char* c_str() const { return rawStr_; } //C-string format with 0-termination
- /**/ Char* data() { return &*begin(); }
+ const Char* data() const { return &*begin(); }
+ /**/ Char* data() { return &*begin(); }
const Char& operator[](size_t pos) const;
/**/ Char& operator[](size_t pos);
- bool empty() const { return length() == 0; }
+ bool empty() const { return size() == 0; }
void clear();
+#if 0 //avoid redundant std::string API bloat!
size_t find (const Zbase& str, size_t pos = 0) const; //
size_t find (const Char* str, size_t pos = 0) const; //
size_t find (Char ch, size_t pos = 0) const; //returns "npos" if not found
size_t rfind(Char ch, size_t pos = npos) const; //
size_t rfind(const Char* str, size_t pos = npos) const; //
+#endif
//Zbase& replace(size_t pos1, size_t n1, const Zbase& str);
void reserve(size_t minCapacity);
Zbase& assign(const Char* str, size_t len) { return assign(str, str + len); }
@@ -286,7 +293,7 @@ public:
Zbase& operator=(const Zbase& str);
Zbase& operator=(const Char* str) { return assign(str, strLength(str)); }
Zbase& operator=(Char ch) { return assign(&ch, 1); }
- Zbase& operator+=(const Zbase& str) { return append(str.c_str(), str.length()); }
+ Zbase& operator+=(const Zbase& str) { return append(str.c_str(), str.size()); }
Zbase& operator+=(const Char* str) { return append(str, strLength(str)); }
Zbase& operator+=(Char ch) { return append(&ch, 1); }
@@ -355,7 +362,7 @@ template <class Char, template <class> class SP>
template <class InputIterator> inline
Zbase<Char, SP>::Zbase(InputIterator first, InputIterator last)
{
- rawStr_ = this->create(std::distance(first, last));
+ rawStr_ = this->create(last - first);
*std::copy(first, last, rawStr_) = 0;
}
@@ -394,35 +401,36 @@ Zbase<Char, SP>::~Zbase()
}
+#if 0 //avoid redundant std::string API bloat!
template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::find(const Zbase& str, size_t pos) const
+size_t Zbase<Char, SP>::find(const Zbase& str, size_t pos) const //returns "npos" if not found
{
- assert(pos <= length());
- const size_t len = length();
+ assert(pos <= size());
+ const size_t len = size();
const Char* thisEnd = begin() + len; //respect embedded 0
- const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
+ const Char* it = searchFirst(begin() + std::min(pos, len), thisEnd,
str.begin(), str.end());
return it == thisEnd ? npos : it - begin();
}
template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::find(const Char* str, size_t pos) const
+size_t Zbase<Char, SP>::find(const Char* str, size_t pos) const //returns "npos" if not found
{
- assert(pos <= length());
- const size_t len = length();
+ assert(pos <= size());
+ const size_t len = size();
const Char* thisEnd = begin() + len; //respect embedded 0
- const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
+ const Char* it = searchFirst(begin() + std::min(pos, len), thisEnd,
str, str + strLength(str));
return it == thisEnd ? npos : it - begin();
}
template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::find(Char ch, size_t pos) const
+size_t Zbase<Char, SP>::find(Char ch, size_t pos) const //returns "npos" if not found
{
- assert(pos <= length());
- const size_t len = length();
+ assert(pos <= size());
+ const size_t len = size();
const Char* thisEnd = begin() + len; //respect embedded 0
const Char* it = std::find(begin() + std::min(pos, len), thisEnd, ch);
return it == thisEnd ? npos : it - begin();
@@ -430,10 +438,10 @@ size_t Zbase<Char, SP>::find(Char ch, size_t pos) const
template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const
+size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const //returns "npos" if not found
{
- assert(pos == npos || pos <= length());
- const size_t len = length();
+ assert(pos == npos || pos <= size());
+ const size_t len = size();
const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + 1, len));
const Char* it = findLast(begin(), currEnd, ch);
return it == currEnd ? npos : it - begin();
@@ -441,22 +449,23 @@ size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const
template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const
+size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const //returns "npos" if not found
{
- assert(pos == npos || pos <= length());
+ assert(pos == npos || pos <= size());
const size_t strLen = strLength(str);
- const size_t len = length();
+ const size_t len = size();
const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len));
const Char* it = searchLast(begin(), currEnd,
str, str + strLen);
return it == currEnd ? npos : it - begin();
}
+#endif
template <class Char, template <class> class SP> inline
void Zbase<Char, SP>::resize(size_t newSize, Char fillChar)
{
- const size_t oldSize = length();
+ const size_t oldSize = size();
if (this->canWrite(rawStr_, newSize))
{
if (oldSize < newSize)
@@ -485,28 +494,28 @@ void Zbase<Char, SP>::resize(size_t newSize, Char fillChar)
template <class Char, template <class> class SP> inline
bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
{
- return lhs.length() == rhs.length() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); //respect embedded 0
+ return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); //respect embedded 0
}
template <class Char, template <class> class SP> inline
bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs)
{
- return lhs.length() == strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0
+ return lhs.size() == strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0
}
template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::length() const
+size_t Zbase<Char, SP>::size() const
{
- return SP<Char>::length(rawStr_);
+ return SP<Char>::size(rawStr_);
}
template <class Char, template <class> class SP> inline
const Char& Zbase<Char, SP>::operator[](size_t pos) const
{
- assert(pos < length()); //design by contract! no runtime check!
+ assert(pos < size()); //design by contract! no runtime check!
return rawStr_[pos];
}
@@ -514,8 +523,8 @@ const Char& Zbase<Char, SP>::operator[](size_t pos) const
template <class Char, template <class> class SP> inline
Char& Zbase<Char, SP>::operator[](size_t pos)
{
- reserve(length()); //make unshared!
- assert(pos < length()); //design by contract! no runtime check!
+ reserve(size()); //make unshared!
+ assert(pos < size()); //design by contract! no runtime check!
return rawStr_[pos];
}
@@ -523,7 +532,7 @@ Char& Zbase<Char, SP>::operator[](size_t pos)
template <class Char, template <class> class SP> inline
auto Zbase<Char, SP>::begin() -> iterator
{
- reserve(length()); //make unshared!
+ reserve(size()); //make unshared!
return rawStr_;
}
@@ -531,7 +540,7 @@ auto Zbase<Char, SP>::begin() -> iterator
template <class Char, template <class> class SP> inline
auto Zbase<Char, SP>::end() -> iterator
{
- return begin() + length();
+ return begin() + size();
}
@@ -557,7 +566,7 @@ void Zbase<Char, SP>::reserve(size_t minCapacity) //make unshared and check capa
if (!this->canWrite(rawStr_, minCapacity))
{
//allocate a new string
- const size_t len = length();
+ const size_t len = size();
Char* newStr = this->create(len, std::max(len, minCapacity)); //reserve() must NEVER shrink the string: logical const!
std::copy(rawStr_, rawStr_ + len + 1 /*0-termination*/, newStr);
@@ -591,7 +600,7 @@ Zbase<Char, SP>& Zbase<Char, SP>::append(InputIterator first, InputIterator last
const size_t len = std::distance(first, last);
if (len > 0) //avoid making this string unshared for no reason
{
- const size_t thisLen = length();
+ const size_t thisLen = size();
reserve(thisLen + len); //make unshared and check capacity
*std::copy(first, last, rawStr_ + thisLen) = 0;
@@ -624,7 +633,7 @@ Zbase<Char, SP>& Zbase<Char, SP>::operator=(Zbase<Char, SP>&& tmp) noexcept
template <class Char, template <class> class SP> inline
void Zbase<Char, SP>::pop_back()
{
- const size_t len = length();
+ const size_t len = size();
assert(len > 0);
if (len > 0)
resize(len - 1);
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 364a9a26..3975cc92 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -70,11 +70,11 @@ enum class SplitOnEmpty
allow,
skip
};
-template <class S, class Char> [[nodiscard]] std::vector<S> split(const S& str, Char delimiter, SplitOnEmpty soe);
+template <class S, class Char, class Function> void split(const S& str, Char delimiter, Function onStringPart);
template <class S, class Function1, class Function2> void split2(const S& str, Function1 isDelimiter, Function2 onStringPart);
+template <class S, class Char> [[nodiscard]] std::vector<S> splitCpy(const S& str, Char delimiter, SplitOnEmpty soe);
-template <class S> [[nodiscard]] S trimCpy(S str, bool fromLeft = true, bool fromRight = true);
-template <class Char, class Function> [[nodiscard]] std::pair<Char*, Char*> trimCpy(Char* first, Char* last, bool fromLeft, bool fromRight, Function trimThisChar);
+template <class S> [[nodiscard]] S trimCpy(const S& str, bool fromLeft = true, bool fromRight = true);
template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true);
template <class S, class Function> void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar);
@@ -315,8 +315,8 @@ bool contains(const S& str, const T& term)
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLen;
const auto* const termFirst = strBegin(term);
-
- return std::search(strFirst, strLast,
+
+ return searchFirst(strFirst, strLast,
termFirst, termFirst + termLen) != strLast;
}
@@ -331,7 +331,7 @@ S afterLast(const S& str, const T& term, IfNotFoundReturn infr)
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
-
+
const auto* it = searchLast(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
@@ -348,7 +348,7 @@ S beforeLast(const S& str, const T& term, IfNotFoundReturn infr)
static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t termLen = strLength(term);
assert(termLen > 0);
-
+
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
@@ -372,8 +372,8 @@ S afterFirst(const S& str, const T& term, IfNotFoundReturn infr)
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
-
- const auto* it = std::search(strFirst, strLast,
+
+ const auto* it = searchFirst(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
return infr == IfNotFoundReturn::all ? str : S();
@@ -393,8 +393,8 @@ S beforeFirst(const S& str, const T& term, IfNotFoundReturn infr)
const auto* const strFirst = strBegin(str);
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
-
- auto it = std::search(strFirst, strLast,
+
+ auto it = searchFirst(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
return infr == IfNotFoundReturn::all ? str : S();
@@ -412,7 +412,7 @@ void split2(const S& str, Function1 isDelimiter, Function2 onStringPart)
for (;;)
{
const auto* const blockLast = std::find_if(blockFirst, strEnd, isDelimiter);
- onStringPart(blockFirst, blockLast);
+ onStringPart(makeStringView(blockFirst, blockLast));
if (blockLast == strEnd)
return;
@@ -422,16 +422,24 @@ void split2(const S& str, Function1 isDelimiter, Function2 onStringPart)
}
+template <class S, class Char, class Function> inline
+void split(const S& str, Char delimiter, Function onStringPart)
+{
+ static_assert(std::is_same_v<GetCharTypeT<S>, Char>);
+ split2(str, [delimiter](Char c) { return c == delimiter; }, onStringPart);
+}
+
+
template <class S, class Char> inline
-std::vector<S> split(const S& str, Char delimiter, SplitOnEmpty soe)
+std::vector<S> splitCpy(const S& str, Char delimiter, SplitOnEmpty soe)
{
static_assert(std::is_same_v<GetCharTypeT<S>, Char>);
std::vector<S> output;
- split2(str, [delimiter](Char c) { return c == delimiter; }, [&](const Char* blockFirst, const Char* blockLast)
+ split2(str, [delimiter](Char c) { return c == delimiter; }, [&, soe](std::basic_string_view<Char> block)
{
- if (blockFirst != blockLast || soe == SplitOnEmpty::allow)
- output.emplace_back(blockFirst, blockLast);
+ if (!block.empty() || soe == SplitOnEmpty::allow)
+ output.emplace_back(block.data(), block.size());
});
return output;
}
@@ -474,7 +482,7 @@ void replace(S& str, const T& oldTerm, const U& newTerm, CharEq charEqual)
auto* it = strBegin(str); //don't use str.begin() or wxString will return this wxUni* nonsense!
auto* const strEnd = it + strLength(str);
- auto itFound = std::search(it, strEnd,
+ auto itFound = searchFirst(it, strEnd,
oldBegin, oldEnd, charEqual);
if (itFound == strEnd)
return; //optimize "oldTerm not found"
@@ -489,7 +497,7 @@ void replace(S& str, const T& oldTerm, const U& newTerm, CharEq charEqual)
itFound = strEnd;
else
#endif
- itFound = std::search(it, strEnd,
+ itFound = searchFirst(it, strEnd,
oldBegin, oldEnd, charEqual);
impl::stringAppend(output, it, itFound);
@@ -531,8 +539,9 @@ S replaceCpyAsciiNoCase(S str, const T& oldTerm, const U& newTerm)
}
-template <class Char, class Function> inline
-std::pair<Char*, Char*> trimCpy(Char* first, Char* last, bool fromLeft, bool fromRight, Function trimThisChar)
+template <class Char, class Function>
+[[nodiscard]] inline
+std::pair<Char*, Char*> trimCpy2(Char* first, Char* last, bool fromLeft, bool fromRight, Function trimThisChar)
{
assert(fromLeft || fromRight);
@@ -554,7 +563,7 @@ void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar)
assert(fromLeft || fromRight);
const auto* const oldBegin = strBegin(str);
- const auto& [newBegin, newEnd] = trimCpy(oldBegin, oldBegin + strLength(str), fromLeft, fromRight, trimThisChar);
+ const auto& [newBegin, newEnd] = trimCpy2(oldBegin, oldBegin + strLength(str), fromLeft, fromRight, trimThisChar);
if (newBegin != oldBegin)
str = S(newBegin, newEnd); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only
@@ -572,11 +581,18 @@ void trim(S& str, bool fromLeft, bool fromRight)
template <class S> inline
-S trimCpy(S str, bool fromLeft, bool fromRight)
+S trimCpy(const S& str, bool fromLeft, bool fromRight)
{
- //implementing trimCpy() in terms of trim(), instead of the other way round, avoids memory allocations when trimming from right!
- trim(str, fromLeft, fromRight);
- return str;
+ using CharType = GetCharTypeT<S>;
+ const auto* const oldBegin = strBegin(str);
+ const auto* const oldEnd = oldBegin + strLength(str);
+
+ const auto& [newBegin, newEnd] = trimCpy2(oldBegin, oldEnd, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); });
+
+ if (newBegin == oldBegin && newEnd == oldEnd)
+ return str;
+ else
+ return S(newBegin, newEnd - newBegin);
}
@@ -626,6 +642,7 @@ S printNumber(const T& format, const Num& number) //format a single number using
#error refactor?
#endif
static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
+ assert(strBegin(format)[strLength(format)] == 0); //format must be null-terminated!
S buf(128, static_cast<GetCharTypeT<S>>('0'));
const int charsWritten = impl::saferPrintf(buf.data(), buf.size(), strBegin(format), number);
@@ -634,7 +651,7 @@ S printNumber(const T& format, const Num& number) //format a single number using
return S();
buf.resize(charsWritten);
- return buf;
+ return buf;
}
@@ -849,7 +866,7 @@ Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::unsign
if (hasMinusSign)
{
assert(false);
- return 0U;
+ return -makeSigned(number); //at least make some noise
}
return number;
}
diff --git a/zen/sys_info.cpp b/zen/sys_info.cpp
index 87e07f91..5a044c3a 100644
--- a/zen/sys_info.cpp
+++ b/zen/sys_info.cpp
@@ -131,10 +131,28 @@ ComputerModel zen::getComputerModel() //throw FileError
cm.model = beforeFirst(cm.model, L'\u00ff', IfNotFoundReturn::all); //fix broken BIOS entries:
cm.vendor = beforeFirst(cm.vendor, L'\u00ff', IfNotFoundReturn::all); //0xff can be considered 0
+ trim(cm.model, false, true, [](wchar_t c) { return c == L'_'; }); //e.g. "CBX3___" or just "_"
+ trim(cm.vendor, false, true, [](wchar_t c) { return c == L'_'; }); //e.g. "DELL__" or just "_"
+
for (const char* dummyModel :
{
- "To Be Filled By O.E.M.", "Default string", "$(DEFAULT_STRING)", "Undefined", "empty", "O.E.M", "OEM", "NA",
- "System Product Name", "Please change product name", "INVALID",
+ "Please change product name",
+ "SYSTEM_PRODUCT_NAME",
+ "System Product Name",
+ "To Be Filled By O.E.M.",
+ "Default string",
+ "$(DEFAULT_STRING)",
+ "<null string>",
+ "Product Name",
+ "Undefined",
+ "INVALID",
+ "Unknow",
+ "empty",
+ "O.E.M.",
+ "O.E.M",
+ "OEM",
+ "NA",
+ ".",
})
if (equalAsciiNoCase(cm.model, dummyModel))
{
@@ -144,8 +162,21 @@ ComputerModel zen::getComputerModel() //throw FileError
for (const char* dummyVendor :
{
- "To Be Filled By O.E.M.", "Default string", "$(DEFAULT_STRING)", "Undefined", "empty", "O.E.M", "OEM", "NA",
- "System manufacturer", "OEM Manufacturer",
+ "OEM Manufacturer",
+ "SYSTEM_MANUFACTURER",
+ "System manufacturer",
+ "System Manufacter",
+ "To Be Filled By O.E.M.",
+ "Default string",
+ "$(DEFAULT_STRING)",
+ "Undefined",
+ "Unknow",
+ "empty",
+ "O.E.M.",
+ "O.E.M",
+ "OEM",
+ "NA",
+ ".",
})
if (equalAsciiNoCase(cm.vendor, dummyVendor))
{
diff --git a/zen/sys_version.cpp b/zen/sys_version.cpp
index d187e0f0..b123ba07 100644
--- a/zen/sys_version.cpp
+++ b/zen/sys_version.cpp
@@ -47,13 +47,14 @@ OsVersionDetail zen::getOsVersionDetail() //throw SysError
}
catch (const FileError& e) { throw SysError(replaceCpy(e.toString(), L"\n\n", L'\n')); } //errors should be further enriched by context info => SysError
- for (const std::string& line : split(releaseInfo, '\n', SplitOnEmpty::skip))
+ split(releaseInfo, '\n', [&](const std::string_view line)
+ {
if (startsWith(line, "NAME="))
osName = utfTo<std::wstring>(afterFirst(line, '=', IfNotFoundReturn::none));
else if (startsWith(line, "VERSION_ID="))
osVersion = utfTo<std::wstring>(afterFirst(line, '=', IfNotFoundReturn::none));
- //PRETTY_NAME? too wordy! e.g. "Fedora 17 (Beefy Miracle)"
-
+ //PRETTY_NAME? too wordy! e.g. "Fedora 17 (Beefy Miracle)"
+ });
trim(osName, true, true, [](char c) { return c == L'"' || c == L'\''; });
trim(osVersion, true, true, [](char c) { return c == L'"' || c == L'\''; });
}
@@ -64,7 +65,7 @@ OsVersionDetail zen::getOsVersionDetail() //throw SysError
// lsb_release Release is "rolling"
// etc/os-release: VERSION_ID is missing
- std::vector<std::wstring> verDigits = split<std::wstring>(osVersion, L'.', SplitOnEmpty::allow); //e.g. "7.7.1908"
+ std::vector<std::wstring_view> verDigits = splitCpy<std::wstring_view>(osVersion, L'.', SplitOnEmpty::allow); //e.g. "7.7.1908"
verDigits.resize(2);
return OsVersionDetail
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 73f18cd1..59e90956 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -18,7 +18,7 @@ Zstring getUnicodeNormalForm_NonAsciiValidUtf(const Zstring& str, UnicodeNormalF
//Example: const char* decomposed = "\x6f\xcc\x81"; //ó
// const char* precomposed = "\xc3\xb3"; //ó
assert(!isAsciiString(str)); //includes "not-empty" check
- assert(str.find(Zchar('\0')) == Zstring::npos); //don't expect embedded nulls!
+ assert(!contains(str, Zchar('\0'))); //don't expect embedded nulls!
try
{
@@ -75,6 +75,17 @@ Zstring getUnicodeNormalFormNonAscii(const Zstring& str, UnicodeNormalForm form)
}
+Zstring getUpperCaseAscii(const Zstring& str)
+{
+ assert(isAsciiString(str));
+
+ Zstring output = str;
+ for (Zchar& c : output) //identical to LCMapStringEx(), g_unichar_toupper(), CFStringUppercase() [verified!]
+ c = asciiToUpper(c); //
+ return output;
+}
+
+
Zstring getUpperCaseNonAscii(const Zstring& str)
{
Zstring strNorm = getUnicodeNormalFormNonAscii(str, UnicodeNormalForm::native);
@@ -102,27 +113,20 @@ Zstring getUpperCaseNonAscii(const Zstring& str)
Zstring getUnicodeNormalForm(const Zstring& str, UnicodeNormalForm form)
{
- //fast pre-check:
- if (isAsciiString(str)) //perf: in the range of 3.5ns
- return str;
static_assert(std::is_same_v<decltype(str), const Zbase<Zchar>&>, "god bless our ref-counting! => save needless memory allocation!");
- return getUnicodeNormalFormNonAscii(str, form);
+ if (isAsciiString(str)) //fast path: in the range of 3.5ns
+ return str;
+
+ return getUnicodeNormalFormNonAscii(str, form); //slow path
}
Zstring getUpperCase(const Zstring& str)
{
- if (isAsciiString(str)) //fast path: in the range of 3.5ns
- {
- Zstring output = str;
- for (Zchar& c : output) //identical to LCMapStringEx(), g_unichar_toupper(), CFStringUppercase() [verified!]
- c = asciiToUpper(c); //
- return output;
- }
- //else: slow path --------------------------------------
-
- return getUpperCaseNonAscii(str);
+ return isAsciiString(str) ? //fast path: in the range of 3.5ns
+ getUpperCaseAscii(str) :
+ getUpperCaseNonAscii(str); //slow path
}
@@ -252,8 +256,11 @@ std::weak_ordering compareNatural(const Zstring& lhs, const Zstring& rhs)
std::weak_ordering compareNoCase(const Zstring& lhs, const Zstring& rhs)
{
+ const bool isAsciiL = isAsciiString(lhs);
+ const bool isAsciiR = isAsciiString(rhs);
+
//fast path: no memory allocations => ~ 6x speedup
- if (isAsciiString(lhs) && isAsciiString(rhs))
+ if (isAsciiL && isAsciiR)
{
const size_t minSize = std::min(lhs.size(), rhs.size());
for (size_t i = 0; i < minSize; ++i)
@@ -271,19 +278,19 @@ std::weak_ordering compareNoCase(const Zstring& lhs, const Zstring& rhs)
//can't we instead skip isAsciiString() and compare chars as long as isAsciiChar()?
// => NOPE! e.g. decomposed Unicode! A seemingly single isAsciiChar() might be followed by a combining character!!!
- return getUpperCase(lhs) <=> getUpperCase(rhs);
+ return (isAsciiL ? getUpperCaseAscii(lhs) : getUpperCaseNonAscii(lhs)) <=>
+ (isAsciiR ? getUpperCaseAscii(rhs) : getUpperCaseNonAscii(rhs));
}
bool equalNoCase(const Zstring& lhs, const Zstring& rhs)
{
- //fast-path: no need for extra memory allocations
const bool isAsciiL = isAsciiString(lhs);
const bool isAsciiR = isAsciiString(rhs);
- if (isAsciiL != isAsciiR)
- return false;
- if (isAsciiL)
+ //fast-path: no extra memory allocations
+ //caveat: ASCII-char and non-ASCII Unicode *can* compare case-insensitive equal!!! e.g. i and ı https://freefilesync.org/forum/viewtopic.php?t=9718
+ if (isAsciiL && isAsciiR)
{
if (lhs.size() != rhs.size())
return false;
@@ -295,5 +302,6 @@ bool equalNoCase(const Zstring& lhs, const Zstring& rhs)
return true;
}
- return getUpperCaseNonAscii(lhs) == getUpperCaseNonAscii(rhs);
+ return (isAsciiL ? getUpperCaseAscii(lhs) : getUpperCaseNonAscii(lhs)) ==
+ (isAsciiR ? getUpperCaseAscii(rhs) : getUpperCaseNonAscii(rhs));
}
diff --git a/zen/zstring.h b/zen/zstring.h
index d0a8eb4c..5bcc8b34 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -20,13 +20,13 @@
//a high-performance string for interfacing with native OS APIs in multithreaded contexts
using Zstring = zen::Zbase<Zchar>;
+using ZstringView = std::basic_string_view<Zchar>;
+
//for special UI-contexts: guaranteed exponential growth + ref-counting + COW + no SSO overhead
using Zstringc = zen::Zbase<char>;
//using Zstringw = zen::Zbase<wchar_t>;
-//Windows, Linux: precomposed
-//macOS: decomposed
enum class UnicodeNormalForm
{
nfc, //precomposed
bgstack15