diff options
author | Mike Kaganski <mike.kaganski@collabora.com> | 2022-03-02 13:11:18 +0300 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2022-03-02 22:11:23 +0100 |
commit | 3cb30369d699a4836043d434833ba4c3d3d257c3 (patch) | |
tree | 99a92a7b1ad3186bfa27f8920ae1ea50a3fe191b /sal/rtl/strtmpl.hxx | |
parent | aa54ea23233a54e32450138a07f2d6d2257149d9 (diff) |
Unify normal/shortened, null-terminated/with-length comparisons
Change-Id: Ie154efd1e0d9b49601200ac896d5d5dd0422d504
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130832
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'sal/rtl/strtmpl.hxx')
-rw-r--r-- | sal/rtl/strtmpl.hxx | 185 |
1 files changed, 70 insertions, 115 deletions
diff --git a/sal/rtl/strtmpl.hxx b/sal/rtl/strtmpl.hxx index 6988865e8f11..945aaa54a6e2 100644 --- a/sal/rtl/strtmpl.hxx +++ b/sal/rtl/strtmpl.hxx @@ -30,6 +30,7 @@ #include "strimp.hxx" +#include <o3tl/safeint.hxx> #include <osl/diagnose.h> #include <sal/log.hxx> #include <rtl/character.hxx> @@ -44,6 +45,35 @@ namespace rtl::str { template <typename C> auto IMPL_RTL_USTRCODE(C c) { return std::make_unsigned_t<C>(c); } +// Wrappers around null-terminated/known-length strings, that allow to generalize algorithms +// without overhead (e.g., without need to get length of null-terminated strings). + +template <typename C> struct null_terminated +{ + C* p; + null_terminated(C* pStr) + : p(pStr) + { + assert(pStr); + } + auto getIter() const { return p; } + static auto getEndDetector() { return [](C* iter) { return *iter == 0; }; } +}; + +template <typename C> struct with_length +{ + C* p; + sal_Int32 len; + with_length(C* pStr, sal_Int32 nLength) + : p(pStr) + , len(nLength) + { + assert(len >= 0); + } + auto getIter() const { return p; } + auto getEndDetector() const { return [pEnd = p + len](C* iter) { return iter == pEnd; }; } +}; + template <typename C> void Copy(C* _pDest, const C* _pSrc, sal_Int32 _nCount) { // take advantage of builtin optimisations @@ -124,16 +154,33 @@ struct CompareIgnoreAsciiCase /* ----------------------------------------------------------------------- */ -template <typename C1, typename C2, class Compare> -sal_Int32 compare(const C1* pStr1, const C2* pStr2, Compare) +struct NoShortening +{ + constexpr bool operator>=(int) { return true; } // for assert + constexpr bool operator==(int) { return false; } // for loop break check + constexpr void operator--() {} // for decrement in loop +} constexpr noShortening; + +template <class S1, class S2, class Compare, typename Shorten_t> +sal_Int32 compare(S1 s1, S2 s2, Compare, Shorten_t shortenedLength) { - assert(pStr1); - assert(pStr2); + static_assert(std::is_same_v<Shorten_t, NoShortening> || std::is_same_v<Shorten_t, sal_Int32>); + assert(shortenedLength >= 0); + auto pStr1 = s1.getIter(); + const auto atEnd1 = s1.getEndDetector(); + auto pStr2 = s2.getIter(); + const auto atEnd2 = s2.getEndDetector(); for (;;) { - const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2); - if (!(nRet == 0 && *pStr2)) + if (shortenedLength == 0) + return 0; + if (atEnd2(pStr2)) + return atEnd1(pStr1) ? 0 : 1; + if (atEnd1(pStr1)) + return -1; + if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2)) return nRet; + --shortenedLength; ++pStr1; ++pStr2; } @@ -141,115 +188,28 @@ sal_Int32 compare(const C1* pStr1, const C2* pStr2, Compare) // take advantage of builtin optimisations template <typename C, std::enable_if_t<sizeof(C) == sizeof(wchar_t), int> = 0> -sal_Int32 compare(const C* pStr1, const C* pStr2, CompareNormal) -{ - assert(pStr1); - assert(pStr2); - return wcscmp(reinterpret_cast<wchar_t const*>(pStr1), reinterpret_cast<wchar_t const*>(pStr2)); -} -inline sal_Int32 compare(const char* pStr1, const char* pStr2, CompareNormal) +sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening) { - assert(pStr1); - assert(pStr2); - return strcmp(pStr1, pStr2); + return wcscmp(reinterpret_cast<wchar_t const*>(s1.p), reinterpret_cast<wchar_t const*>(s2.p)); } - -/* ----------------------------------------------------------------------- */ - -template <typename C1, typename C2, class Compare> -sal_Int32 compare_WithLength(const C1* pStr1, sal_Int32 nStr1Len, const C2* pStr2, Compare) +template <typename C, std::enable_if_t<sizeof(C) == sizeof(char), int> = 0> +sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening) { - assert(pStr1 || nStr1Len == 0); - assert(nStr1Len >= 0); - assert(pStr2); - for (;;) - { - if (*pStr2 == '\0') - return nStr1Len; - if (nStr1Len == 0) - return -1; - if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2)) - return nRet; - pStr1++; - pStr2++; - nStr1Len--; - } + return strcmp(reinterpret_cast<char const*>(s1.p), reinterpret_cast<char const*>(s2.p)); } - -/* ----------------------------------------------------------------------- */ - -template <typename C1, typename C2, class Compare> -sal_Int32 compare_WithLengths(const C1* pStr1, sal_Int32 nStr1Len, - const C2* pStr2, sal_Int32 nStr2Len, Compare) -{ - assert(pStr1 || nStr1Len == 0); - assert(nStr1Len >= 0); - assert(pStr2 || nStr2Len == 0); - assert(nStr2Len >= 0); - // TODO: use std::lexicographical_compare_three_way when C++20 is available - const C1* pStr1End = pStr1 + nStr1Len; - const C2* pStr2End = pStr2 + nStr2Len; - while ((pStr1 < pStr1End) && (pStr2 < pStr2End)) - { - if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2)) - return nRet; - pStr1++; - pStr2++; - } - - return nStr1Len - nStr2Len; -} - template <typename C> -sal_Int32 compare_WithLengths(const C* pStr1, sal_Int32 nStr1Len, - const C* pStr2, sal_Int32 nStr2Len, CompareNormal) +sal_Int32 compare(with_length<C> s1, with_length<C> s2, CompareNormal, NoShortening) { - assert(pStr1 || nStr1Len == 0); - assert(nStr1Len >= 0); - assert(pStr2 || nStr2Len == 0); - assert(nStr2Len >= 0); - // take advantage of builtin optimisations - std::basic_string_view<C> aView1(pStr1, nStr1Len); - std::basic_string_view<C> aView2(pStr2, nStr2Len); - return aView1.compare(aView2); + std::basic_string_view sv1(s1.p, s1.len); + return sv1.compare(std::basic_string_view(s2.p, s2.len)); } - -/* ----------------------------------------------------------------------- */ - template <typename C1, typename C2, class Compare> -sal_Int32 shortenedCompare_WithLength(const C1* pStr1, sal_Int32 nStr1Len, const C2* pStr2, - sal_Int32 nShortenedLength, Compare) +sal_Int32 compare(with_length<C1> s1, with_length<C2> s2, Compare cf, sal_Int32 nShortenedLength) { - assert(pStr1); - assert(nStr1Len >= 0); - assert(pStr2); assert(nShortenedLength >= 0); - const C1* pStr1End = pStr1 + nStr1Len; - for (;;) - { - if (nShortenedLength == 0) - return 0; - if (*pStr2 == '\0') - return pStr1End - pStr1; - if (pStr1 == pStr1End) - return -1; // first is a substring of the second string => less (negative value) - if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2)) - return nRet; - nShortenedLength--; - pStr1++; - pStr2++; - } -} - -/* ----------------------------------------------------------------------- */ - -template <typename C1, typename C2, class Compare> -sal_Int32 shortenedCompare_WithLengths(const C1* pStr1, sal_Int32 nStr1Len, - const C2* pStr2, sal_Int32 nStr2Len, - sal_Int32 nShortenedLength, Compare cf) -{ - return compare_WithLengths(pStr1, std::min(nStr1Len, nShortenedLength), - pStr2, std::min(nStr2Len, nShortenedLength), cf); + s1.len = std::min(s1.len, nShortenedLength); + s2.len = std::min(s2.len, nShortenedLength); + return compare(s1, s2, cf, noShortening); } /* ----------------------------------------------------------------------- */ @@ -786,12 +746,6 @@ template <typename T> std::pair<T, sal_Int16> DivMod(sal_Int16 nRadix, [[maybe_u return { std::numeric_limits<T>::max() / nRadix, std::numeric_limits<T>::max() % nRadix }; } -template <class SV> auto getIter(const SV& sv) { return sv.begin(); } -template <typename C> auto getIter(const C* pStr) { return pStr; } - -template <class SV> auto good(typename SV::iterator iter, const SV& sv) { return iter != sv.end(); } -template <typename C> auto good(const C* pStr, const C*) { return *pStr != 0; } - template <typename T, class S> T toInt(S str, sal_Int16 nRadix) { assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); @@ -799,12 +753,13 @@ template <typename T, class S> T toInt(S str, sal_Int16 nRadix) if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) nRadix = 10; - auto pStr = getIter(str); + auto pStr = str.getIter(); + const auto atEnd = str.getEndDetector(); /* Skip whitespaces */ - while (good(pStr, str) && rtl_ImplIsWhitespace(IMPL_RTL_USTRCODE(*pStr))) + while (!atEnd(pStr) && rtl_ImplIsWhitespace(IMPL_RTL_USTRCODE(*pStr))) pStr++; - if (!good(pStr, str)) + if (atEnd(pStr)) return 0; const bool bNeg = HandleSignChar<T>(pStr); @@ -812,7 +767,7 @@ template <typename T, class S> T toInt(S str, sal_Int16 nRadix) assert(nDiv > 0); std::make_unsigned_t<T> n = 0; - while (good(pStr, str)) + while (!atEnd(pStr)) { sal_Int16 nDigit = rtl_ImplGetDigit(IMPL_RTL_USTRCODE(*pStr), nRadix); if ( nDigit < 0 ) |