tdf#126109 calc slow when replacing string to number

Store SharedString inline in ScCellValue.
Shaves 10% off time.
Use std::variant to handle the complexities of correctly calling
constructor and destructor of SharedString.

Change-Id: I820de5339e31434fbdbde1a72e25abe207bf008d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135863
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/sc/inc/cellvalue.hxx b/sc/inc/cellvalue.hxx
index 1095d32..caf42d2 100644
--- a/sc/inc/cellvalue.hxx
+++ b/sc/inc/cellvalue.hxx
@@ -10,6 +10,8 @@
#pragma once

#include "global.hxx"
#include <svl/sharedstring.hxx>
#include <variant>

class ScDocument;
class ScFormulaCell;
@@ -21,12 +23,6 @@ namespace sc {
struct ColumnBlockPosition;
}

namespace svl {

class SharedString;

}

/**
 * Store arbitrary cell value of any kind.  It only stores cell value and
 * nothing else.  It creates a copy of the original cell value, and manages
@@ -34,15 +30,8 @@ class SharedString;
 */
struct SC_DLLPUBLIC ScCellValue
{
private:
    CellType meType;
public:
    union {
        double mfValue1;
        svl::SharedString* mpString;
        EditTextObject* mpEditText1;
        ScFormulaCell* mpFormula1;
    };
    /// bool is there to indicate CellType::NONE
    std::variant<bool, double, svl::SharedString, EditTextObject*, ScFormulaCell*> maData;

    ScCellValue();
    ScCellValue( const ScRefCellValue& rCell );
@@ -61,25 +50,13 @@ public:
    void set( std::unique_ptr<EditTextObject> );
    void set( ScFormulaCell* pFormula );

    CellType getType() const { return meType; }
    double getDouble() const { assert(meType == CELLTYPE_VALUE); return mfValue1; }
    svl::SharedString* getSharedString() const { assert(meType == CELLTYPE_STRING); return mpString; }
    EditTextObject* getEditText() const { assert(meType == CELLTYPE_EDIT); return mpEditText1; }
    EditTextObject* releaseEditText()
    {
        assert(meType == CELLTYPE_EDIT);
        auto p = mpEditText1;
        mpEditText1 = nullptr;
        return p;
    }
    ScFormulaCell* getFormula() const { assert(meType == CELLTYPE_FORMULA); return mpFormula1; }
    ScFormulaCell* releaseFormula()
    {
        assert(meType == CELLTYPE_FORMULA);
        auto p = mpFormula1;
        mpFormula1 = nullptr;
        return p;
    }
    CellType getType() const;
    double getDouble() const { return std::get<double>(maData); }
    ScFormulaCell* getFormula() const { return std::get<ScFormulaCell*>(maData); }
    const svl::SharedString* getSharedString() const { return &std::get<svl::SharedString>(maData); }
    EditTextObject* getEditText() const { return std::get<EditTextObject*>(maData); }
    EditTextObject* releaseEditText() { auto p = getEditText(); maData = static_cast<EditTextObject*>(nullptr); return p; }
    ScFormulaCell* releaseFormula() { auto p = getFormula(); maData = static_cast<ScFormulaCell*>(nullptr); return p; }

    /**
     * Take cell value from specified position in specified document.
@@ -125,6 +102,8 @@ public:
 */
struct SC_DLLPUBLIC ScRefCellValue
{
    /// bool is there to indicate CellType::NONE
    std::variant<bool, double, svl::SharedString, EditTextObject*, ScFormulaCell*> maData;
private:
    CellType meType;
public:
diff --git a/sc/source/core/data/cellvalue.cxx b/sc/source/core/data/cellvalue.cxx
index fa246b3..e378677 100644
--- a/sc/source/core/data/cellvalue.cxx
+++ b/sc/source/core/data/cellvalue.cxx
@@ -38,17 +38,17 @@ template<typename T>
OUString getString( const T& rVal )
{
    if (rVal.getType() == CELLTYPE_STRING)
        return rVal.mpString->getString();
        return rVal.getSharedString()->getString();

    if (rVal.getType() == CELLTYPE_EDIT)
    {
        OUStringBuffer aRet;
        sal_Int32 n = rVal.mpEditText1->GetParagraphCount();
        sal_Int32 n = rVal.getEditText()->GetParagraphCount();
        for (sal_Int32 i = 0; i < n; ++i)
        {
            if (i > 0)
                aRet.append('\n');
            aRet.append(rVal.mpEditText1->GetText(i));
            aRet.append(rVal.getEditText()->GetText(i));
        }
        return aRet.makeStringAndClear();
    }
@@ -107,15 +107,42 @@ bool equalsWithoutFormatImpl( const T& left, const T& right )
    return false;
}

bool equalsWithoutFormatImpl( const ScCellValue& left, const ScCellValue& right )
{
    CellType eType1 = adjustCellType(left.getType());
    CellType eType2 = adjustCellType(right.getType());
    if (eType1 != eType2)
        return false;

    switch (eType1)
    {
        case CELLTYPE_NONE:
            return true;
        case CELLTYPE_VALUE:
            return left.getDouble() == right.getDouble();
        case CELLTYPE_STRING:
        {
            OUString aStr1 = getString(left);
            OUString aStr2 = getString(right);
            return aStr1 == aStr2;
        }
        case CELLTYPE_FORMULA:
            return equalsFormulaCells(left.getFormula(), right.getFormula());
        default:
            ;
    }
    return false;
}

void commitToColumn( const ScCellValue& rCell, ScColumn& rColumn, SCROW nRow )
{
    switch (rCell.getType())
    {
        case CELLTYPE_STRING:
            rColumn.SetRawString(nRow, *rCell.mpString);
            rColumn.SetRawString(nRow, *rCell.getSharedString());
        break;
        case CELLTYPE_EDIT:
            rColumn.SetEditText(nRow, ScEditUtil::Clone(*rCell.mpEditText1, rColumn.GetDoc()));
            rColumn.SetEditText(nRow, ScEditUtil::Clone(*rCell.getEditText(), rColumn.GetDoc()));
        break;
        case CELLTYPE_VALUE:
            rColumn.SetValue(nRow, rCell.getDouble());
@@ -123,7 +150,7 @@ void commitToColumn( const ScCellValue& rCell, ScColumn& rColumn, SCROW nRow )
        case CELLTYPE_FORMULA:
        {
            ScAddress aDestPos(rColumn.GetCol(), nRow, rColumn.GetTab());
            rColumn.SetFormulaCell(nRow, new ScFormulaCell(*rCell.mpFormula1, rColumn.GetDoc(), aDestPos));
            rColumn.SetFormulaCell(nRow, new ScFormulaCell(*rCell.getFormula(), rColumn.GetDoc(), aDestPos));
        }
        break;
        default:
@@ -158,21 +185,21 @@ bool hasNumericImpl( CellType eType, ScFormulaCell* pFormula )
    }
}

template<typename CellT>
OUString getStringImpl( const CellT& rCell, const ScDocument* pDoc )
template <typename T>
OUString getStringImpl( const T& rCell, const ScDocument* pDoc )
{
    switch (rCell.getType())
    {
        case CELLTYPE_VALUE:
            return OUString::number(rCell.getDouble());
        case CELLTYPE_STRING:
            return rCell.mpString->getString();
            return rCell.getSharedString()->getString();
        case CELLTYPE_EDIT:
            if (rCell.mpEditText1)
                return ScEditUtil::GetString(*rCell.mpEditText1, pDoc);
            if (rCell.getEditText())
                return ScEditUtil::GetString(*rCell.getEditText(), pDoc);
        break;
        case CELLTYPE_FORMULA:
            return rCell.mpFormula1->GetString().getString();
            return rCell.getFormula()->GetString().getString();
        default:
            ;
    }
@@ -202,69 +229,58 @@ OUString getRawStringImpl( const CellT& rCell, const ScDocument& rDoc )

}

ScCellValue::ScCellValue() : meType(CELLTYPE_NONE), mfValue1(0.0) {}
ScCellValue::ScCellValue() {}

ScCellValue::ScCellValue( const ScRefCellValue& rCell ) : meType(rCell.getType()), mfValue1(rCell.mfValue1)
ScCellValue::ScCellValue( const ScRefCellValue& rCell )
{
    switch (rCell.getType())
    {
        case CELLTYPE_STRING:
            mpString = new svl::SharedString(*rCell.mpString);
            maData = *rCell.mpString;
        break;
        case CELLTYPE_EDIT:
            mpEditText1 = rCell.mpEditText1->Clone().release();
            maData = rCell.getEditText()->Clone().release();
        break;
        case CELLTYPE_FORMULA:
            mpFormula1 = rCell.mpFormula1->Clone();
            maData = rCell.getFormula()->Clone();
        break;
        default:
            ;
        case CELLTYPE_VALUE:
            maData = rCell.getDouble();
        break;
        default: ;
    }
}

ScCellValue::ScCellValue( double fValue ) : meType(CELLTYPE_VALUE), mfValue1(fValue) {}
ScCellValue::ScCellValue( double fValue ) : maData(fValue) {}

ScCellValue::ScCellValue( const svl::SharedString& rString ) : meType(CELLTYPE_STRING), mpString(new svl::SharedString(rString)) {}
ScCellValue::ScCellValue( const svl::SharedString& rString ) : maData(rString) {}

ScCellValue::ScCellValue( std::unique_ptr<EditTextObject> xEdit ) : meType(CELLTYPE_EDIT), mpEditText1(xEdit.release()) {}
ScCellValue::ScCellValue( std::unique_ptr<EditTextObject> xEdit ) : maData(xEdit.release()) {}

ScCellValue::ScCellValue( const ScCellValue& r ) : meType(r.meType), mfValue1(r.mfValue1)
ScCellValue::ScCellValue( const ScCellValue& r )
{
    switch (r.meType)
    switch (r.getType())
    {
        case CELLTYPE_STRING:
            mpString = new svl::SharedString(*r.mpString);
            maData = *r.getSharedString();
        break;
        case CELLTYPE_EDIT:
            mpEditText1 = r.mpEditText1->Clone().release();
            maData = r.getEditText()->Clone().release();
        break;
        case CELLTYPE_FORMULA:
            mpFormula1 = r.mpFormula1->Clone();
            maData = r.getFormula()->Clone();
        break;
        default:
            ;
        case CELLTYPE_VALUE:
            maData = r.getDouble();
        break;
        default: ;
    }
}

ScCellValue::ScCellValue(ScCellValue&& r) noexcept
    : meType(r.meType)
    , mfValue1(r.mfValue1)
    : maData(std::move(r.maData))
{
    switch (r.meType)
    {
        case CELLTYPE_STRING:
            mpString = r.mpString;
        break;
        case CELLTYPE_EDIT:
            mpEditText1 = r.mpEditText1;
        break;
        case CELLTYPE_FORMULA:
            mpFormula1 = r.mpFormula1;
        break;
        default:
            ;
    }
    r.meType = CELLTYPE_NONE;
    r.maData = true; // reset to empty;
}

ScCellValue::~ScCellValue()
@@ -272,61 +288,67 @@ ScCellValue::~ScCellValue()
    clear();
}

CellType ScCellValue::getType() const
{
    switch (maData.index())
    {
        case 0: return CELLTYPE_NONE;
        case 1: return CELLTYPE_VALUE;
        case 2: return CELLTYPE_STRING;
        case 3: return CELLTYPE_EDIT;
        case 4: return CELLTYPE_FORMULA;
        default:
            assert(false);
            return CELLTYPE_NONE;
    }
}

void ScCellValue::clear() noexcept
{
    switch (meType)
    switch (getType())
    {
        case CELLTYPE_STRING:
            delete mpString;
        break;
        case CELLTYPE_EDIT:
            delete mpEditText1;
            delete getEditText();
        break;
        case CELLTYPE_FORMULA:
            delete mpFormula1;
            delete getFormula();
        break;
        default:
            ;
    }

    // Reset to empty value.
    meType = CELLTYPE_NONE;
    mfValue1 = 0.0;
    maData = true;
}

void ScCellValue::set( double fValue )
{
    clear();
    meType = CELLTYPE_VALUE;
    mfValue1 = fValue;
    maData = fValue;
}

void ScCellValue::set( const svl::SharedString& rStr )
{
    clear();
    meType = CELLTYPE_STRING;
    mpString = new svl::SharedString(rStr);
    maData = rStr;
}

void ScCellValue::set( const EditTextObject& rEditText )
{
    clear();
    meType = CELLTYPE_EDIT;
    mpEditText1 = rEditText.Clone().release();
    maData = rEditText.Clone().release();
}

void ScCellValue::set( std::unique_ptr<EditTextObject> xEditText )
{
    clear();
    meType = CELLTYPE_EDIT;
    mpEditText1 = xEditText.release();
    maData = xEditText.release();
}

void ScCellValue::set( ScFormulaCell* pFormula )
{
    clear();
    meType = CELLTYPE_FORMULA;
    mpFormula1 = pFormula;
    maData = pFormula;
}

void ScCellValue::assign( const ScDocument& rDoc, const ScAddress& rPos )
@@ -335,24 +357,21 @@ void ScCellValue::assign( const ScDocument& rDoc, const ScAddress& rPos )

    ScRefCellValue aRefVal(const_cast<ScDocument&>(rDoc), rPos);

    meType = aRefVal.getType();
    switch (meType)
    switch (aRefVal.getType())
    {
        case CELLTYPE_STRING:
            mpString = new svl::SharedString(*aRefVal.mpString);
            maData = *aRefVal.mpString;
        break;
        case CELLTYPE_EDIT:
            if (aRefVal.mpEditText1)
                mpEditText1 = aRefVal.mpEditText1->Clone().release();
            maData = aRefVal.mpEditText1 ? aRefVal.mpEditText1->Clone().release() : static_cast<EditTextObject*>(nullptr);
        break;
        case CELLTYPE_VALUE:
            mfValue1 = aRefVal.mfValue1;
            maData = aRefVal.mfValue1;
        break;
        case CELLTYPE_FORMULA:
            mpFormula1 = aRefVal.mpFormula1->Clone();
            maData = aRefVal.mpFormula1->Clone();
        break;
        default:
            meType = CELLTYPE_NONE; // reset to empty.
        default: ; // leave empty
    }
}

@@ -360,66 +379,64 @@ void ScCellValue::assign(const ScCellValue& rOther, ScDocument& rDestDoc, ScClon
{
    clear();

    meType = rOther.meType;
    switch (meType)
    switch (rOther.getType())
    {
        case CELLTYPE_STRING:
            mpString = new svl::SharedString(*rOther.mpString);
            maData = rOther.maData;
        break;
        case CELLTYPE_EDIT:
        {
            // Switch to the pool of the destination document.
            ScFieldEditEngine& rEngine = rDestDoc.GetEditEngine();
            if (rOther.mpEditText1->HasOnlineSpellErrors())
            if (rOther.getEditText()->HasOnlineSpellErrors())
            {
                EEControlBits nControl = rEngine.GetControlWord();
                const EEControlBits nSpellControl = EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS;
                bool bNewControl = ((nControl & nSpellControl) != nSpellControl);
                if (bNewControl)
                    rEngine.SetControlWord(nControl | nSpellControl);
                rEngine.SetTextCurrentDefaults(*rOther.mpEditText1);
                mpEditText1 = rEngine.CreateTextObject().release();
                rEngine.SetTextCurrentDefaults(*rOther.getEditText());
                maData = rEngine.CreateTextObject().release();
                if (bNewControl)
                    rEngine.SetControlWord(nControl);
            }
            else
            {
                rEngine.SetTextCurrentDefaults(*rOther.mpEditText1);
                mpEditText1 = rEngine.CreateTextObject().release();
                rEngine.SetTextCurrentDefaults(*rOther.getEditText());
                maData = rEngine.CreateTextObject().release();
            }
        }
        break;
        case CELLTYPE_VALUE:
            mfValue1 = rOther.mfValue1;
            maData = rOther.maData;
        break;
        case CELLTYPE_FORMULA:
            // Switch to the destination document.
            mpFormula1 = new ScFormulaCell(*rOther.mpFormula1, rDestDoc, rOther.mpFormula1->aPos, nCloneFlags);
            maData = new ScFormulaCell(*rOther.getFormula(), rDestDoc, rOther.getFormula()->aPos, nCloneFlags);
        break;
        default:
            meType = CELLTYPE_NONE; // reset to empty.
        default: ; // leave empty
    }
}

void ScCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const
{
    switch (meType)
    switch (getType())
    {
        case CELLTYPE_STRING:
        {
            ScSetStringParam aParam;
            aParam.setTextInput();
            rDoc.SetString(rPos, mpString->getString(), &aParam);
            rDoc.SetString(rPos, getSharedString()->getString(), &aParam);
        }
        break;
        case CELLTYPE_EDIT:
            rDoc.SetEditText(rPos, mpEditText1->Clone());
            rDoc.SetEditText(rPos, getEditText()->Clone());
        break;
        case CELLTYPE_VALUE:
            rDoc.SetValue(rPos, mfValue1);
            rDoc.SetValue(rPos, getDouble());
        break;
        case CELLTYPE_FORMULA:
            rDoc.SetFormulaCell(rPos, mpFormula1->Clone());
            rDoc.SetFormulaCell(rPos, getFormula()->Clone());
        break;
        default:
            rDoc.SetEmptyCell(rPos);
@@ -433,64 +450,60 @@ void ScCellValue::commit( ScColumn& rColumn, SCROW nRow ) const

void ScCellValue::release( ScDocument& rDoc, const ScAddress& rPos )
{
    switch (meType)
    switch (getType())
    {
        case CELLTYPE_STRING:
        {
            // Currently, string cannot be placed without copying.
            ScSetStringParam aParam;
            aParam.setTextInput();
            rDoc.SetString(rPos, mpString->getString(), &aParam);
            delete mpString;
            rDoc.SetString(rPos, getSharedString()->getString(), &aParam);
        }
        break;
        case CELLTYPE_EDIT:
            // Cell takes the ownership of the text object.
            rDoc.SetEditText(rPos, std::unique_ptr<EditTextObject>(mpEditText1));
            rDoc.SetEditText(rPos, std::unique_ptr<EditTextObject>(getEditText()));
        break;
        case CELLTYPE_VALUE:
            rDoc.SetValue(rPos, mfValue1);
            rDoc.SetValue(rPos, getDouble());
        break;
        case CELLTYPE_FORMULA:
            // This formula cell instance is directly placed in the document without copying.
            rDoc.SetFormulaCell(rPos, mpFormula1);
            rDoc.SetFormulaCell(rPos, getFormula());
        break;
        default:
            rDoc.SetEmptyCell(rPos);
    }

    meType = CELLTYPE_NONE;
    mfValue1 = 0.0;
    maData = true; // reset to empty
}

void ScCellValue::release( ScColumn& rColumn, SCROW nRow, sc::StartListeningType eListenType )
{
    switch (meType)
    switch (getType())
    {
        case CELLTYPE_STRING:
        {
            // Currently, string cannot be placed without copying.
            rColumn.SetRawString(nRow, *mpString);
            delete mpString;
            rColumn.SetRawString(nRow, *getSharedString());
        }
        break;
        case CELLTYPE_EDIT:
            // Cell takes the ownership of the text object.
            rColumn.SetEditText(nRow, std::unique_ptr<EditTextObject>(mpEditText1));
            rColumn.SetEditText(nRow, std::unique_ptr<EditTextObject>(getEditText()));
        break;
        case CELLTYPE_VALUE:
            rColumn.SetValue(nRow, mfValue1);
            rColumn.SetValue(nRow, getDouble());
        break;
        case CELLTYPE_FORMULA:
            // This formula cell instance is directly placed in the document without copying.
            rColumn.SetFormulaCell(nRow, mpFormula1, eListenType);
            rColumn.SetFormulaCell(nRow, getFormula(), eListenType);
        break;
        default:
            rColumn.DeleteContent(nRow);
    }

    meType = CELLTYPE_NONE;
    mfValue1 = 0.0;
    maData = true; // reset to empty
}

OUString ScCellValue::getString( const ScDocument& rDoc ) const
@@ -500,7 +513,7 @@ OUString ScCellValue::getString( const ScDocument& rDoc ) const

bool ScCellValue::isEmpty() const
{
    return meType == CELLTYPE_NONE;
    return getType() == CELLTYPE_NONE;
}

bool ScCellValue::equalsWithoutFormat( const ScCellValue& r ) const
@@ -518,27 +531,8 @@ ScCellValue& ScCellValue::operator= ( const ScCellValue& r )
ScCellValue& ScCellValue::operator=(ScCellValue&& rCell) noexcept
{
    clear();

    meType = rCell.meType;
    mfValue1 = rCell.mfValue1;
    switch (rCell.meType)
    {
        case CELLTYPE_STRING:
            mpString = rCell.mpString;
        break;
        case CELLTYPE_EDIT:
            mpEditText1 = rCell.mpEditText1;
        break;
        case CELLTYPE_FORMULA:
            mpFormula1 = rCell.mpFormula1;
        break;
        default:
            ;
    }
    //we don't need to reset mpString/mpEditText1/mpFormula1 if we
    //set meType to NONE as the ScCellValue dtor keys off the meType
    rCell.meType = CELLTYPE_NONE;

    maData = std::move(rCell.maData);
    rCell.maData = true; // reset to empty;
    return *this;
}

@@ -551,11 +545,7 @@ ScCellValue& ScCellValue::operator= ( const ScRefCellValue& r )

void ScCellValue::swap( ScCellValue& r )
{
    std::swap(meType, r.meType);

    // double is 8 bytes, whereas a pointer may be 4 or 8 bytes depending on
    // the platform. Swap by double values.
    std::swap(mfValue1, r.mfValue1);
    std::swap(maData, r.maData);
}

ScRefCellValue::ScRefCellValue() : meType(CELLTYPE_NONE), mfValue1(0.0) {}
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 2c610d4..ce982e9 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -291,7 +291,7 @@ void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, 
                // same document. If not, re-intern shared strings.
                svl::SharedStringPool* pSharedStringPool = (bSameDocPool ? nullptr : &rDocument.GetSharedStringPool());
                svl::SharedString aStr = (pSharedStringPool ?
                        pSharedStringPool->intern( rSrcCell.getSharedString()-> getString()) :
                        pSharedStringPool->intern( rSrcCell.getSharedString()->getString()) :
                        *rSrcCell.getSharedString());

                std::vector<svl::SharedString> aStrs(nDestSize, aStr);
diff --git a/sc/source/core/tool/chgtrack.cxx b/sc/source/core/tool/chgtrack.cxx
index 29cc75e..0fa7951 100644
--- a/sc/source/core/tool/chgtrack.cxx
+++ b/sc/source/core/tool/chgtrack.cxx
@@ -1365,7 +1365,7 @@ void ScChangeActionContent::SetValueString(
    if ( rStr.getLength() > 1 && rStr[0] == '=' )
    {
        rValue.clear();
        rCell = ScCellValue(new ScFormulaCell(
        rCell.set(new ScFormulaCell(
            *pDoc, aBigRange.aStart.MakeAddress(*pDoc), rStr,
            pDoc->GetGrammar() ));
        rCell.getFormula()->SetInChangeTrack(true);