diff options
Diffstat (limited to 'zen/zstring.h')
-rwxr-xr-x[-rw-r--r--] | zen/zstring.h | 491 |
1 files changed, 222 insertions, 269 deletions
diff --git a/zen/zstring.h b/zen/zstring.h index 902da80e..12bda29f 100644..100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -1,269 +1,222 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#ifndef ZSTRING_H_73425873425789 -#define ZSTRING_H_73425873425789 - -#include "string_base.h" - - -#ifdef ZEN_WIN //Windows encodes Unicode as UTF-16 wchar_t - using Zchar = wchar_t; - #define Zstr(x) L ## x - const Zchar FILE_NAME_SEPARATOR = L'\\'; - -#elif defined ZEN_LINUX || defined ZEN_MAC //Linux uses UTF-8 - using Zchar = char; - #define Zstr(x) x - const Zchar FILE_NAME_SEPARATOR = '/'; -#endif - -//"The reason for all the fuss above" - Loki/SmartPtr -//a high-performance string for interfacing with native OS APIs in multithreaded contexts -using Zstring = zen::Zbase<Zchar>; - - -int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); -#if defined ZEN_LINUX || defined ZEN_MAC - int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); -#endif - -template <class S, class T> inline -bool equalNoCase(const S& lhs, const T& rhs) { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } - -template <class S> -S makeUpperCopy(S str); - - -//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES -int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); -#if defined ZEN_LINUX || defined ZEN_MAC - int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); -#endif - -template <class S, class T> inline -bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } - -struct LessFilePath -{ - template <class S, class T> - bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; } -}; - - - -inline -Zstring appendSeparator(Zstring path) //support rvalue references! -{ - return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); //returning a by-value parameter implicitly converts to r-value! -} - - -inline -Zstring getFileExtension(const Zstring& filePath) -{ - const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL); - return afterLast(shortName, Zchar('.'), zen::IF_MISSING_RETURN_NONE); -} - - -template <class S, class T> inline -bool pathStartsWith(const S& str, const T& prefix) -{ - using namespace zen; - const size_t pfLen = strLength(prefix); - if (strLength(str) < pfLen) - return false; - - return cmpFilePath(strBegin(str), pfLen, strBegin(prefix), pfLen) == 0; -} - - -template <class S, class T> inline -bool pathEndsWith(const S& str, const T& postfix) -{ - using namespace zen; - const size_t strLen = strLength(str); - const size_t pfLen = strLength(postfix); - if (strLen < pfLen) - return false; - - return cmpFilePath(strBegin(str) + strLen - pfLen, pfLen, strBegin(postfix), pfLen) == 0; -} - - -template <class S, class T, class U> -S pathReplaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); - - - - -//################################# inline implementation ######################################## -#ifdef ZEN_WIN -void makeUpperInPlace(wchar_t* str, size_t strLen); - -#elif defined ZEN_LINUX || defined ZEN_MAC -inline -void makeUpperInPlace(wchar_t* str, size_t strLen) -{ - std::for_each(str, str + strLen, [](wchar_t& c) { c = std::towupper(c); }); //locale-dependent! -} - - -inline -void makeUpperInPlace(char* str, size_t strLen) -{ - std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent! - //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! - //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! -} - - -inline -int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) -{ - assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! - assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // - - const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! - if (rv != 0) - return rv; - return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); -} - - -inline -int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) -{ - assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! - assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // - - const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! - if (rv != 0) - return rv; - return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); -} -#endif - - -template <class S> inline -S makeUpperCopy(S str) -{ - const size_t len = str.length(); //we assert S is a string type! - if (len > 0) - makeUpperInPlace(&*str.begin(), len); - - return std::move(str); //"str" is an l-value parameter => no copy elision! -} - - -inline -int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) -{ -#if defined ZEN_WIN || defined ZEN_MAC - return cmpStringNoCase(lhs, lhsLen, rhs, rhsLen); - -#elif defined ZEN_LINUX - assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! - assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // - - const int rv = std::wcsncmp(lhs, rhs, std::min(lhsLen, rhsLen)); - if (rv != 0) - return rv; - return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); -#endif -} - - -#if defined ZEN_LINUX || defined ZEN_MAC -inline -int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) -{ -#if defined ZEN_MAC - return cmpStringNoCase(lhs, lhsLen, rhs, rhsLen); - -#elif defined ZEN_LINUX - assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! - assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // - - const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen)); - if (rv != 0) - return rv; - return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); -#endif -} -#endif - - -template <class S, class T, class U> inline -S pathReplaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) -{ - assert(!contains(str, Zchar('\0'))); - -#if defined ZEN_WIN || defined ZEN_MAC - using namespace zen; - - S strU = makeUpperCopy(str); //S required to be a string class - S oldTermU = makeUpperCopy<S>(oldTerm); //[!] T not required to be a string class - assert(strLength(strU ) == strLength(str )); - assert(strLength(oldTermU) == strLength(oldTerm)); - - replace(strU, oldTermU, Zchar('\0'), replaceAll); - - S output; - - size_t i = 0; - for (auto c : strU) - if (c == Zchar('\0')) - { - output += newTerm; - i += oldTermU.size(); - } - else - output += str[i++]; - - return output; - -#elif defined ZEN_LINUX - return replaceCpy(str, oldTerm, newTerm, replaceAll); -#endif -} - - -//--------------------------------------------------------------------------- -//ZEN macro consistency checks: -#ifdef ZEN_WIN - #if defined ZEN_LINUX || defined ZEN_MAC - #error more than one target platform defined - #endif - - #ifdef ZEN_WIN_VISTA_AND_LATER - #ifdef ZEN_WIN_PRE_VISTA - #error choose only one of the two variants - #endif - #elif defined ZEN_WIN_PRE_VISTA - #ifdef ZEN_WIN_VISTA_AND_LATER - #error choose only one of the two variants - #endif - #else - #error choose one of the two variants - #endif - -#elif defined ZEN_LINUX - #if defined ZEN_WIN || defined ZEN_MAC - #error more than one target platform defined - #endif - -#elif defined ZEN_MAC - #if defined ZEN_WIN || defined ZEN_LINUX - #error more than one target platform defined - #endif - -#else - #error no target platform defined -#endif - -#endif //ZSTRING_H_73425873425789 +// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef ZSTRING_H_73425873425789
+#define ZSTRING_H_73425873425789
+
+#include "string_base.h"
+
+
+ using Zchar = char;
+ #define Zstr(x) x
+ const Zchar FILE_NAME_SEPARATOR = '/';
+
+//"The reason for all the fuss above" - Loki/SmartPtr
+//a high-performance string for interfacing with native OS APIs in multithreaded contexts
+using Zstring = zen::Zbase<Zchar>;
+
+
+int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen);
+ int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+
+template <class S>
+S makeUpperCopy(S str);
+
+
+//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES
+int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen);
+ int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+
+
+template <class S, class T> inline
+bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; }
+
+struct LessFilePath
+{
+ template <class S, class T>
+ bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+};
+
+
+struct LessNoCase
+{
+ template <class S, class T>
+ bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+};
+
+
+inline
+Zstring appendSeparator(Zstring path) //support rvalue references!
+{
+ return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); //returning a by-value parameter implicitly converts to r-value!
+}
+
+
+inline
+Zstring getFileExtension(const Zstring& filePath)
+{
+ const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
+ return afterLast(shortName, Zchar('.'), zen::IF_MISSING_RETURN_NONE);
+}
+
+
+template <class S, class T> inline
+bool ciEqual(const S& lhs, const T& rhs) { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; }
+
+
+template <class S, class T> inline
+bool ciStartsWith(const S& str, const T& prefix)
+{
+ using namespace zen;
+ const size_t pfLen = strLength(prefix);
+ if (strLength(str) < pfLen)
+ return false;
+
+ return cmpStringNoCase(strBegin(str), pfLen, strBegin(prefix), pfLen) == 0;
+}
+
+
+template <class S, class T> inline
+bool ciEndsWith(const S& str, const T& postfix)
+{
+ using namespace zen;
+ const size_t strLen = strLength(str);
+ const size_t pfLen = strLength(postfix);
+ if (strLen < pfLen)
+ return false;
+
+ return cmpStringNoCase(strBegin(str) + strLen - pfLen, pfLen, strBegin(postfix), pfLen) == 0;
+}
+
+
+template <class S, class T, class U>
+S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm);
+
+
+
+
+//################################# inline implementation ########################################
+inline
+void makeUpperInPlace(wchar_t* str, size_t strLen)
+{
+ std::for_each(str, str + strLen, [](wchar_t& c) { c = std::towupper(c); }); //locale-dependent!
+}
+
+
+inline
+void makeUpperInPlace(char* str, size_t strLen)
+{
+ std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent!
+ //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness!
+ //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves!
+}
+
+
+inline
+int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen)
+{
+ assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
+ assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+
+ const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+inline
+int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+{
+ assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
+ assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+
+ const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent!
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+template <class S> inline
+S makeUpperCopy(S str)
+{
+ const size_t len = str.length(); //we assert S is a string type!
+ if (len > 0)
+ makeUpperInPlace(&*str.begin(), len);
+
+ return std::move(str); //"str" is an l-value parameter => no copy elision!
+}
+
+
+inline
+int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen)
+{
+ assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
+ assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+
+ const int rv = std::wcsncmp(lhs, rhs, std::min(lhsLen, rhsLen));
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+inline
+int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+{
+ assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
+ assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+
+ const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen));
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+template <class S, class T, class U> inline
+S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm)
+{
+ using namespace zen;
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
+ const size_t oldLen = strLength(oldTerm);
+ if (oldLen == 0)
+ return str;
+
+ const S strU = makeUpperCopy(str); //S required to be a string class
+ const S oldU = makeUpperCopy<S>(oldTerm); //[!] T not required to be a string class
+ assert(strLength(strU) == strLength(str ));
+ assert(strLength(oldU) == strLength(oldTerm));
+
+ const auto* const newBegin = strBegin(newTerm);
+ const auto* const newEnd = newBegin + strLength(newTerm);
+
+ S output;
+
+ for (size_t pos = 0;;)
+ {
+ const auto itFound = std::search(strU.begin() + pos, strU.end(),
+ oldU.begin(), oldU.end());
+ if (itFound == strU.end() && pos == 0)
+ return str; //optimize "oldTerm not found": return ref-counted copy
+
+ impl::stringAppend(output, str.begin() + pos, str.begin() + (itFound - strU.begin()));
+ if (itFound == strU.end())
+ return output;
+
+ impl::stringAppend(output, newBegin, newEnd);
+ pos = (itFound - strU.begin()) + oldLen;
+ }
+}
+
+
+//---------------------------------------------------------------------------
+//ZEN macro consistency checks:
+
+
+#endif //ZSTRING_H_73425873425789
|