summaryrefslogtreecommitdiff
path: root/sal/rtl/strtmpl.hxx
diff options
context:
space:
mode:
authorMike Kaganski <mike.kaganski@collabora.com>2022-03-02 13:11:18 +0300
committerMike Kaganski <mike.kaganski@collabora.com>2022-03-02 22:11:23 +0100
commit3cb30369d699a4836043d434833ba4c3d3d257c3 (patch)
tree99a92a7b1ad3186bfa27f8920ae1ea50a3fe191b /sal/rtl/strtmpl.hxx
parentaa54ea23233a54e32450138a07f2d6d2257149d9 (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.hxx185
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 )