Some more string_view use, add o3tl::starts/ends_with

...until those C++20 string_view member functions are generally available (the
fallback implementations are taken directly from the C++20 spec).

(In ParseMathMLAttributeLengthValue in starmath/source/mathml/mathmlattr.cxx,
returning nIdx + 2 instead of nIdx + 1 for the single-character u'%' case was
presumably a typo, but which was harmless as the return value was only checked
for <= 0, and has now been turned into a bool.)

Change-Id: Ib441e474c515f016a4d81bb39f7821dfe0356499
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122322
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
diff --git a/include/o3tl/string_view.hxx b/include/o3tl/string_view.hxx
index 5ebbb0b..b66ba11 100644
--- a/include/o3tl/string_view.hxx
+++ b/include/o3tl/string_view.hxx
@@ -13,6 +13,7 @@

#include <cassert>
#include <cstddef>
#include <string>
#include <string_view>

#include <rtl/ustring.h>
@@ -46,6 +47,66 @@ inline std::string_view getToken(std::string_view sv, char delimiter, std::size_
    }
    return t;
}

// Implementations of C++20 std::basic_string_view::starts_with and
// std::basic_string_view::ends_with, until we can use those directly on all platforms:
template <typename charT, typename traits = std::char_traits<charT>>
constexpr bool starts_with(std::basic_string_view<charT, traits> sv,
                           std::basic_string_view<charT, traits> x) noexcept
{
#if defined __cpp_lib_starts_ends_with
    return sv.starts_with(x);
#else
    return sv.substr(0, x.size()) == x;
#endif
}
template <typename charT, typename traits = std::char_traits<charT>>
constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT x) noexcept
{
#if defined __cpp_lib_starts_ends_with
    return sv.starts_with(x);
#else
    return !sv.empty() && traits::eq(sv.front(), x);
#endif
}
template <typename charT, typename traits = std::char_traits<charT>>
constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT const* x)
{
#if defined __cpp_lib_starts_ends_with
    return sv.starts_with(x);
#else
    return starts_with(sv, std::basic_string_view<charT, traits>(x));
#endif
}
template <typename charT, typename traits = std::char_traits<charT>>
constexpr bool ends_with(std::basic_string_view<charT, traits> sv,
                         std::basic_string_view<charT, traits> x) noexcept
{
#if defined __cpp_lib_ends_ends_with
    return sv.ends_with(x);
#else
    return sv.size() >= x.size()
           && sv.compare(sv.size() - x.size(), std::basic_string_view<charT, traits>::npos, x) == 0;
#endif
}
template <typename charT, typename traits = std::char_traits<charT>>
constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT x) noexcept
{
#if defined __cpp_lib_ends_ends_with
    return sv.ends_with(x);
#else
    return !sv.empty() && traits::eq(sv.back(), x);
#endif
}
template <typename charT, typename traits = std::char_traits<charT>>
constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT const* x)
{
#if defined __cpp_lib_ends_ends_with
    return sv.ends_with(x);
#else
    return ends_with(sv, std::basic_string_view<charT, traits>(x));
#endif
}
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/inc/mathml/mathmlattr.hxx b/starmath/inc/mathml/mathmlattr.hxx
index f999f28..16161fb 100644
--- a/starmath/inc/mathml/mathmlattr.hxx
+++ b/starmath/inc/mathml/mathmlattr.hxx
@@ -9,6 +9,10 @@

#pragma once

#include <sal/config.h>

#include <string_view>

#include <rtl/ustring.hxx>
#include <sal/types.h>
#include <tools/fract.hxx>
@@ -44,7 +48,7 @@ struct MathMLAttributeLengthValue
    }
};

sal_Int32 ParseMathMLAttributeLengthValue(const OUString& rStr, MathMLAttributeLengthValue& rV);
bool ParseMathMLAttributeLengthValue(std::u16string_view rStr, MathMLAttributeLengthValue& rV);

// MathML 3: 3.2.2 Mathematics style attributes common to token elements
// <https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt>
diff --git a/starmath/source/mathml/mathmlattr.cxx b/starmath/source/mathml/mathmlattr.cxx
index 7da7f94..ce28ec7 100644
--- a/starmath/source/mathml/mathmlattr.cxx
+++ b/starmath/source/mathml/mathmlattr.cxx
@@ -10,14 +10,18 @@
#include <mathmlattr.hxx>

#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <rtl/math.h>

#include <cstddef>
#include <string_view>
#include <unordered_map>

static sal_Int32 ParseMathMLUnsignedNumber(const OUString& rStr, Fraction& rUN)
static std::size_t ParseMathMLUnsignedNumber(std::u16string_view rStr, Fraction& rUN)
{
    auto nLen = rStr.getLength();
    sal_Int32 nDecimalPoint = -1;
    sal_Int32 nIdx;
    auto nLen = rStr.length();
    std::size_t nDecimalPoint = std::u16string_view::npos;
    std::size_t nIdx;
    sal_Int64 nom = 0;
    sal_Int64 den = 1;
    bool validNomDen = true;
@@ -26,8 +30,8 @@ static sal_Int32 ParseMathMLUnsignedNumber(const OUString& rStr, Fraction& rUN)
        auto cD = rStr[nIdx];
        if (cD == u'.')
        {
            if (nDecimalPoint >= 0)
                return -1;
            if (nDecimalPoint != std::u16string_view::npos)
                return std::u16string_view::npos;
            nDecimalPoint = nIdx;
            continue;
        }
@@ -36,13 +40,14 @@ static sal_Int32 ParseMathMLUnsignedNumber(const OUString& rStr, Fraction& rUN)
        if (validNomDen
            && (o3tl::checked_multiply(nom, sal_Int64(10), nom)
                || o3tl::checked_add(nom, sal_Int64(cD - u'0'), nom)
                || (nDecimalPoint >= 0 && o3tl::checked_multiply(den, sal_Int64(10), den))))
                || (nDecimalPoint != std::u16string_view::npos
                    && o3tl::checked_multiply(den, sal_Int64(10), den))))
        {
            validNomDen = false;
        }
    }
    if (nIdx == 0 || (nIdx == 1 && nDecimalPoint == 0))
        return -1;
        return std::u16string_view::npos;

    // If the input "xx.yyy" can be represented with nom = xx*10^n + yyy and den = 10^n in sal_Int64
    // (where n is the length of "yyy"), then use that to create an accurate Fraction (and TODO: we
@@ -54,83 +59,74 @@ static sal_Int32 ParseMathMLUnsignedNumber(const OUString& rStr, Fraction& rUN)
    }
    else
    {
        rUN = Fraction(rStr.copy(0, nIdx).toDouble());
        rUN = Fraction(
            rtl_math_uStringToDouble(rStr.data(), rStr.data() + nIdx, '.', 0, nullptr, nullptr));
    }

    return nIdx;
}

static sal_Int32 ParseMathMLNumber(const OUString& rStr, Fraction& rN)
static std::size_t ParseMathMLNumber(std::u16string_view rStr, Fraction& rN)
{
    if (rStr.isEmpty())
        return -1;
    if (rStr.empty())
        return std::u16string_view::npos;
    bool bNegative = (rStr[0] == '-');
    sal_Int32 nOffset = bNegative ? 1 : 0;
    auto nIdx = ParseMathMLUnsignedNumber(rStr.copy(nOffset), rN);
    if (nIdx <= 0 || !rN.IsValid())
        return -1;
    std::size_t nOffset = bNegative ? 1 : 0;
    auto nIdx = ParseMathMLUnsignedNumber(rStr.substr(nOffset), rN);
    if (nIdx == std::u16string_view::npos || !rN.IsValid())
        return std::u16string_view::npos;
    if (bNegative)
        rN *= -1;
    return nOffset + nIdx;
}

sal_Int32 ParseMathMLAttributeLengthValue(const OUString& rStr, MathMLAttributeLengthValue& rV)
bool ParseMathMLAttributeLengthValue(std::u16string_view rStr, MathMLAttributeLengthValue& rV)
{
    auto nIdx = ParseMathMLNumber(rStr, rV.aNumber);
    if (nIdx <= 0)
        return -1;
    OUString sRest = rStr.copy(nIdx);
    if (sRest.isEmpty())
    if (nIdx == std::u16string_view::npos)
        return false;
    std::u16string_view sRest = rStr.substr(nIdx);
    if (sRest.empty())
    {
        rV.eUnit = MathMLLengthUnit::None;
        return nIdx;
    }
    if (sRest.startsWith("em"))
    if (o3tl::starts_with(sRest, u"em"))
    {
        rV.eUnit = MathMLLengthUnit::Em;
        return nIdx + 2;
    }
    if (sRest.startsWith("ex"))
    if (o3tl::starts_with(sRest, u"ex"))
    {
        rV.eUnit = MathMLLengthUnit::Ex;
        return nIdx + 2;
    }
    if (sRest.startsWith("px"))
    if (o3tl::starts_with(sRest, u"px"))
    {
        rV.eUnit = MathMLLengthUnit::Px;
        return nIdx + 2;
    }
    if (sRest.startsWith("in"))
    if (o3tl::starts_with(sRest, u"in"))
    {
        rV.eUnit = MathMLLengthUnit::In;
        return nIdx + 2;
    }
    if (sRest.startsWith("cm"))
    if (o3tl::starts_with(sRest, u"cm"))
    {
        rV.eUnit = MathMLLengthUnit::Cm;
        return nIdx + 2;
    }
    if (sRest.startsWith("mm"))
    if (o3tl::starts_with(sRest, u"mm"))
    {
        rV.eUnit = MathMLLengthUnit::Mm;
        return nIdx + 2;
    }
    if (sRest.startsWith("pt"))
    if (o3tl::starts_with(sRest, u"pt"))
    {
        rV.eUnit = MathMLLengthUnit::Pt;
        return nIdx + 2;
    }
    if (sRest.startsWith("pc"))
    if (o3tl::starts_with(sRest, u"pc"))
    {
        rV.eUnit = MathMLLengthUnit::Pc;
        return nIdx + 2;
    }
    if (sRest[0] == u'%')
    {
        rV.eUnit = MathMLLengthUnit::Percent;
        return nIdx + 2;
    }
    return nIdx;
    return true;
}

bool GetMathMLMathvariantValue(const OUString& rStr, MathMLMathvariantValue& rV)
diff --git a/starmath/source/mathml/mathmlimport.cxx b/starmath/source/mathml/mathmlimport.cxx
index e2db20a..7315c96 100644
--- a/starmath/source/mathml/mathmlimport.cxx
+++ b/starmath/source/mathml/mathmlimport.cxx
@@ -1522,7 +1522,7 @@ void SmXMLSpaceContext_Impl::startFastElement(
        switch (aIter.getToken())
        {
            case XML_WIDTH:
                if (ParseMathMLAttributeLengthValue(sValue.trim(), aLV) <= 0
                if (!ParseMathMLAttributeLengthValue(sValue.trim(), aLV)
                    || !lcl_CountBlanks(aLV, &nWide, &nNarrow))
                    SAL_WARN("starmath", "ignore mspace's width: " << sValue);
                break;