tdf#132388: reimplement SbRtl_Replace

It should not convert strings to uppercase each loop; it should use
OUStringBuffer to avoid extra allocations.

This reduces tick count for the code in the bug from ~6000 to ~30.

Change-Id: I89ea062fc6d012464bb461b6a8ef321f8cc62fe6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/92884
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/basic/source/runtime/methods.cxx b/basic/source/runtime/methods.cxx
index df177d3..75b7b917 100644
--- a/basic/source/runtime/methods.cxx
+++ b/basic/source/runtime/methods.cxx
@@ -1219,88 +1219,91 @@ void SbRtl_Replace(StarBASIC *, SbxArray & rPar, bool)
    if ( nArgCount < 3 || nArgCount > 6 )
    {
        StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
        return;
    }

    sal_Int32 lStartPos = 1;
    if (nArgCount >= 4)
    {
        if (rPar.Get32(4)->GetType() != SbxEMPTY)
        {
            lStartPos = rPar.Get32(4)->GetLong();
        }
        if (lStartPos < 1)
        {
            StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
            return;
        }
    }

    sal_Int32 lCount = -1;
    if (nArgCount >= 5)
    {
        if (rPar.Get32(5)->GetType() != SbxEMPTY)
        {
            lCount = rPar.Get32(5)->GetLong();
        }
        if (lCount < -1)
        {
            StarBASIC::Error(ERRCODE_BASIC_BAD_ARGUMENT);
            return;
        }
    }

    bool bCaseInsensitive;
    if (nArgCount == 6)
    {
        bCaseInsensitive = rPar.Get32(6)->GetInteger();
    }
    else
    {
        OUString aExpStr = rPar.Get32(1)->GetOUString();
        OUString aFindStr = rPar.Get32(2)->GetOUString();
        OUString aReplaceStr = rPar.Get32(3)->GetOUString();

        sal_Int32 lStartPos = 1;
        if ( nArgCount >= 4 )
        {
            if( rPar.Get32(4)->GetType() != SbxEMPTY )
            {
                lStartPos = rPar.Get32(4)->GetLong();
            }
            if( lStartPos < 1)
            {
                StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
                lStartPos = 1;
            }
        }

        sal_Int32 lCount = -1;
        if( nArgCount >=5 )
        {
            if( rPar.Get32(5)->GetType() != SbxEMPTY )
            {
                lCount = rPar.Get32(5)->GetLong();
            }
            if( lCount < -1)
            {
                StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
                lCount = -1;
            }
        }

        SbiInstance* pInst = GetSbData()->pInst;
        bool bTextMode;
        bool bCompatibility = ( pInst && pInst->IsCompatibility() );
        if( bCompatibility )
        if (pInst && pInst->IsCompatibility())
        {
            SbiRuntime* pRT = pInst->pRun;
            bTextMode = pRT && pRT->IsImageFlag( SbiImageFlags::COMPARETEXT );
            bCaseInsensitive = pRT && pRT->IsImageFlag(SbiImageFlags::COMPARETEXT);
        }
        else
        {
            bTextMode = true;
            bCaseInsensitive = true;
        }
        if ( nArgCount == 6 )
        {
            bTextMode = rPar.Get32(6)->GetInteger();
        }
        sal_Int32 nExpStrLen = aExpStr.getLength();
        sal_Int32 nFindStrLen = aFindStr.getLength();
        sal_Int32 nReplaceStrLen = aReplaceStr.getLength();

        if( lStartPos <= nExpStrLen )
        {
            sal_Int32 nPos = lStartPos - 1;
            sal_Int32 nCounts = 0;
            while( lCount == -1 || lCount > nCounts )
            {
                OUString aSrcStr( aExpStr );
                if( bTextMode )
                {
                    aSrcStr = aSrcStr.toAsciiUpperCase();
                    aFindStr = aFindStr.toAsciiUpperCase();
                }
                nPos = aSrcStr.indexOf( aFindStr, nPos );
                if( nPos >= 0 )
                {
                    aExpStr = aExpStr.replaceAt( nPos, nFindStrLen, aReplaceStr );
                    nPos = nPos + nReplaceStrLen;
                    nCounts++;
                }
                else
                {
                    break;
                }
            }
        }
        rPar.Get32(0)->PutString( aExpStr.copy( lStartPos - 1 )  );
    }

    const OUString aExpStr = rPar.Get32(1)->GetOUString();
    OUString aFindStr = rPar.Get32(2)->GetOUString();
    const OUString aReplaceStr = rPar.Get32(3)->GetOUString();
    const sal_Int32 nExpStrLen = aExpStr.getLength();
    const sal_Int32 nFindStrLen = aFindStr.getLength();

    OUString aSrcStr(aExpStr);
    if (bCaseInsensitive)
    {
        // FIXME: case insensitivity should not be ASCII-only
        aSrcStr = aSrcStr.toAsciiUpperCase();
        aFindStr = aFindStr.toAsciiUpperCase();
    }

    // Note: the result starts from lStartPos, removing everything to the left. See i#94895.
    sal_Int32 nPrevPos = std::min(lStartPos - 1, nExpStrLen);
    OUStringBuffer sResult(nExpStrLen - nPrevPos);
    sal_Int32 nCounts = 0;
    while (lCount == -1 || lCount > nCounts)
    {
        sal_Int32 nPos = aSrcStr.indexOf(aFindStr, nPrevPos);
        if (nPos >= 0)
        {
            sResult.append(aExpStr.getStr() + nPrevPos, nPos - nPrevPos);
            sResult.append(aReplaceStr);
            nPrevPos = nPos + nFindStrLen;
            nCounts++;
        }
        else
        {
            break;
        }
    }
    sResult.append(aExpStr.getStr() + nPrevPos, nExpStrLen - nPrevPos);
    rPar.Get32(0)->PutString(sResult.makeStringAndClear());
}

void SbRtl_Right(StarBASIC *, SbxArray & rPar, bool)