tdf#117066 Saving ODT document with ~1500 bookmarks is slow, part 5

Individually, these don't make much difference, but they add up
to a halving the time to save on my machine.

OStorageImpl is spending time iterating over its m_aChildrenVector to
find stuff by name, so just use a std::unordered_map.
Also return iterator from FindElement, so we can avoid searching the map
twice.
This was probably the biggest win.

Change-Id: I30776bad5377d14144fc7a47e86527e2cdb62a83
Reviewed-on: https://gerrit.libreoffice.org/70313
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx
index fe12788..c963820 100644
--- a/package/source/xstor/xstorage.cxx
+++ b/package/source/xstor/xstorage.cxx
@@ -322,8 +322,9 @@
        m_pParent = nullptr;
    }

    std::for_each(m_aChildrenVector.begin(), m_aChildrenVector.end(), std::default_delete<SotElement_Impl>());
    m_aChildrenVector.clear();
    for (auto & pair : m_aChildrenMap)
        delete pair.second;
    m_aChildrenMap.clear();

    std::for_each(m_aDeletedVector.begin(), m_aDeletedVector.end(), std::default_delete<SotElement_Impl>());
    m_aDeletedVector.clear();
@@ -485,12 +486,12 @@
        throw embed::InvalidStorageException( THROW_WHERE );
}

SotElementVector_Impl& OStorage_Impl::GetChildrenVector()
bool OStorage_Impl::HasChildren()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    ReadContents();
    return m_aChildrenVector;
    return !m_aChildrenMap.empty();
}

void OStorage_Impl::GetStorageProperties()
@@ -612,7 +613,8 @@
                    xNewElement->m_bIsRemoved = true;
                }

                m_aChildrenVector.push_back(xNewElement.release());
                OUString name = xNewElement->m_aName; // because we're calling release in the next line
                m_aChildrenMap.emplace(name, xNewElement.release());
            }
        }
        catch( const container::NoSuchElementException& rNoSuchElementException )
@@ -652,8 +654,9 @@
    if ( !m_xPackageFolder.is() )
        throw embed::InvalidStorageException( THROW_WHERE );

    for ( auto& pElement : m_aChildrenVector )
    for ( auto& pair : m_aChildrenMap )
    {
        auto & pElement = pair.second;
        if ( !pElement->m_bIsRemoved )
            CopyStorageElement( pElement, xDest, pElement->m_aName, bDirect );
    }
@@ -1021,34 +1024,34 @@
    m_aDeletedVector.clear();

    // remove removed elements
    SotElementVector_Impl::iterator pElementIter = m_aChildrenVector.begin();
    while (  pElementIter != m_aChildrenVector.end() )
    auto mapIter = m_aChildrenMap.begin();
    while (  mapIter != m_aChildrenMap.end() )
    {
        // renamed and inserted elements must be really inserted to package later
        // since thay can conflict with removed elements

        if ( (*pElementIter)->m_bIsRemoved )
        auto & pElement = mapIter->second;
        if ( pElement->m_bIsRemoved )
        {
            if ( m_nStorageType == embed::StorageFormats::OFOPXML && !(*pElementIter)->m_bIsStorage )
                RemoveStreamRelInfo( (*pElementIter)->m_aOriginalName );
            if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage )
                RemoveStreamRelInfo( pElement->m_aOriginalName );

            // the removed elements are not in new temporary storage
            if ( m_bCommited || m_bIsRoot )
                xNewPackageFolder->removeByName( (*pElementIter)->m_aOriginalName );
                xNewPackageFolder->removeByName( pElement->m_aOriginalName );

            delete *pElementIter;
            pElementIter = m_aChildrenVector.erase(pElementIter);
            delete pElement;
            mapIter = m_aChildrenMap.erase(mapIter);
        }
        else
            ++pElementIter;
            ++mapIter;
    }

    // there should be no more deleted elements
    for ( auto& pElement : m_aChildrenVector )
    for ( auto& pair : m_aChildrenMap )
    {
        // if it is a 'duplicate commit' inserted elements must be really inserted to package later
        // since thay can conflict with renamed elements

        auto & pElement = pair.second;
        if ( !pElement->m_bIsInserted )
        {
            // for now stream is opened in direct mode that means that in case
@@ -1117,8 +1120,9 @@
        }
    }

    for ( auto& pElement : m_aChildrenVector )
    for ( auto& pair : m_aChildrenMap )
    {
        auto & pElement = pair.second;
        // now inserted elements can be inserted to the package
        if ( pElement->m_bIsInserted )
        {
@@ -1212,29 +1216,35 @@
    // all the children must be removed
    // they will be created later on demand

    SotElementVector_Impl::iterator pElementIter = m_aChildrenVector.begin();
    while (  pElementIter != m_aChildrenVector.end() )
    // rebuild the map - cannot do it in-place, because we're changing some of the key values
    std::unordered_map<OUString, SotElement_Impl*> oldMap;
    std::swap(oldMap, m_aChildrenMap);

    auto pElementIter = oldMap.begin();
    while (  pElementIter != oldMap.end() )
    {
        if ( (*pElementIter)->m_bIsInserted )
        auto & pElement = pElementIter->second;
        if ( pElement->m_bIsInserted )
        {
            delete *pElementIter;
            pElementIter = m_aChildrenVector.erase(pElementIter);
            delete pElement;
        }
        else
        {
            ClearElement( *pElementIter );
            ClearElement( pElement );

            (*pElementIter)->m_aName = (*pElementIter)->m_aOriginalName;
            (*pElementIter)->m_bIsRemoved = false;
            pElement->m_aName = pElement->m_aOriginalName;
            pElement->m_bIsRemoved = false;

            ++pElementIter;
            m_aChildrenMap.emplace(pElement->m_aName, pElement);
        }
        ++pElementIter;
    }

    // return replaced removed elements
    for ( auto& pDeleted : m_aDeletedVector )
    {
        m_aChildrenVector.push_back( pDeleted );
        // use original name as key, because we're updating m_aName below
        m_aChildrenMap.emplace( pDeleted->m_aOriginalName, pDeleted );

        ClearElement( pDeleted );

@@ -1282,18 +1292,27 @@

SotElement_Impl* OStorage_Impl::FindElement( const OUString& rName )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    auto it = FindElementIt(rName);
    return it == m_aChildrenMap.end() ? nullptr : it->second;
}

std::unordered_map<OUString, SotElement_Impl*>::iterator OStorage_Impl::FindElementIt( const OUString& rName )
{
    SAL_WARN_IF( rName.isEmpty(), "package.xstor", "Name is empty!" );

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    ReadContents();

    auto pElementIter = std::find_if(m_aChildrenVector.begin(), m_aChildrenVector.end(),
        [&rName](const SotElement_Impl* pElement) { return pElement->m_aName == rName && !pElement->m_bIsRemoved; });
    if (pElementIter != m_aChildrenVector.end())
        return *pElementIter;
    auto pElementIter = m_aChildrenMap.find(rName);
    if (pElementIter == m_aChildrenMap.end())
        return m_aChildrenMap.end();
    if (pElementIter->second->m_bIsRemoved)
        return m_aChildrenMap.end();

    return nullptr;
    return pElementIter;
}

SotElement_Impl* OStorage_Impl::InsertStream( const OUString& aName, bool bEncr )
@@ -1321,7 +1340,7 @@
    SotElement_Impl* pNewElement = InsertElement( aName, false );
    pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, bEncr, m_nStorageType, true));

    m_aChildrenVector.push_back( pNewElement );
    m_aChildrenMap.emplace( pNewElement->m_aName, pNewElement );
    m_bIsModified = true;
    m_bBroadcastModified = true;

@@ -1360,7 +1379,7 @@
    // the stream is inserted and must be treated as a committed one
    pNewElement->m_xStream->SetToBeCommited();

    m_aChildrenVector.push_back( pNewElement );
    m_aChildrenMap.emplace( pNewElement->m_aName, pNewElement );
    m_bIsModified = true;
    m_bBroadcastModified = true;
}
@@ -1394,30 +1413,26 @@

    pNewElement->m_xStorage = CreateNewStorageImpl(nStorageMode);

    m_aChildrenVector.push_back( pNewElement );
    m_aChildrenMap.emplace( pNewElement->m_aName, pNewElement );

    return pNewElement;
}

SotElement_Impl* OStorage_Impl::InsertElement( const OUString& aName, bool bIsStorage )
{
    OSL_ENSURE( FindElement( aName ) == nullptr, "Should not try to insert existing element" );

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SotElement_Impl* pDeletedElm = nullptr;

    for ( const auto& pElement : m_aChildrenVector )
    auto it = m_aChildrenMap.find(aName);
    if (it != m_aChildrenMap.end())
    {
        if ( pElement->m_aName == aName )
        auto pElement = it->second;
        SAL_WARN_IF( !pElement->m_bIsRemoved, "package.xstor", "Try to insert an element instead of existing one!" );
        if ( pElement->m_bIsRemoved )
        {
            SAL_WARN_IF( !pElement->m_bIsRemoved, "package.xstor", "Try to insert an element instead of existing one!" );
            if ( pElement->m_bIsRemoved )
            {
                SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted elements must be deleted immediately!" );
                pDeletedElm = pElement;
                break;
            }
            SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted elements must be deleted immediately!" );
            pDeletedElm = pElement;
        }
    }

@@ -1428,9 +1443,7 @@
        else
            OpenSubStream( pDeletedElm );

        m_aChildrenVector.erase(
            std::remove(m_aChildrenVector.begin(), m_aChildrenVector.end(), pDeletedElm),
            m_aChildrenVector.end());
        m_aChildrenMap.erase(it);
        m_aDeletedVector.push_back( pDeletedElm );
    }

@@ -1488,12 +1501,13 @@

    ReadContents();

    sal_uInt32 nSize = m_aChildrenVector.size();
    sal_uInt32 nSize = m_aChildrenMap.size();
    uno::Sequence< OUString > aElementNames( nSize );

    sal_uInt32 nInd = 0;
    for ( const auto& pElement : m_aChildrenVector )
    for ( const auto& pair : m_aChildrenMap )
    {
        auto pElement = pair.second;
        if ( !pElement->m_bIsRemoved )
            aElementNames[nInd++] = pElement->m_aName;
    }
@@ -1502,12 +1516,9 @@
    return aElementNames;
}

void OStorage_Impl::RemoveElement( SotElement_Impl* pElement )
std::unordered_map<OUString, SotElement_Impl*>::iterator OStorage_Impl::RemoveElement( std::unordered_map<OUString, SotElement_Impl*>::iterator pElementIter )
{
    SAL_WARN_IF( !pElement, "package.xstor", "Element must be provided!" );

    if ( !pElement )
        return;
    auto pElement = pElementIter->second;

    if ( (pElement->m_xStorage && ( pElement->m_xStorage->m_pAntiImpl || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty() ))
      || (pElement->m_xStream && ( pElement->m_xStream->m_pAntiImpl || !pElement->m_xStream->m_aInputStreamsVector.empty() )) )
@@ -1515,13 +1526,15 @@

    if ( pElement->m_bIsInserted )
    {
        delete pElement;
        m_aChildrenVector.erase(std::remove(m_aChildrenVector.begin(), m_aChildrenVector.end(), pElement), m_aChildrenVector.end());
        delete pElementIter->second;
        return m_aChildrenMap.erase(pElementIter);
    }
    else
    {
        pElement->m_bIsRemoved = true;
        ClearElement( pElement );
        ++pElementIter;
        return pElementIter;
    }

    // TODO/OFOPXML: the rel stream should be removed as well
@@ -2384,10 +2397,9 @@

                if ( nStorageMode & embed::ElementModes::TRUNCATE )
                {
                    for ( SotElement_Impl* pElementToDel : pElement->m_xStorage->m_aChildrenVector )
                    {
                        m_pImpl->RemoveElement( pElementToDel );
                    }
                    auto pElementToDelIter = pElement->m_xStorage->m_aChildrenMap.begin();
                    while (pElementToDelIter != pElement->m_xStorage->m_aChildrenMap.end())
                        pElementToDelIter = m_pImpl->RemoveElement( pElementToDelIter );
                }
            }
        }
@@ -2793,12 +2805,11 @@

        try
        {
            SotElement_Impl* pElement = m_pImpl->FindElement(aElementName);

            if (!pElement)
            auto pElementIter = m_pImpl->FindElementIt(aElementName);
            if ( pElementIter == m_pImpl->m_aChildrenMap.end() )
                throw container::NoSuchElementException(THROW_WHERE); //???

            m_pImpl->RemoveElement(pElement);
            m_pImpl->RemoveElement(pElementIter);

            m_pImpl->m_bIsModified = true;
            m_pImpl->m_bBroadcastModified = true;
@@ -2878,11 +2889,14 @@
            if (pRefElement)
                throw container::ElementExistException(THROW_WHERE); //???

            SotElement_Impl* pElement = m_pImpl->FindElement(aElementName);
            if (!pElement)
            auto pElementIt = m_pImpl->FindElementIt( aElementName );
            if ( pElementIt == m_pImpl->m_aChildrenMap.end() )
                throw container::NoSuchElementException(THROW_WHERE); //???

            auto pElement = pElementIt->second;
            pElement->m_aName = aNewName;
            m_pImpl->m_aChildrenMap.erase( pElementIt );
            m_pImpl->m_aChildrenMap.emplace(pElement->m_aName, pElement);

            m_pImpl->m_bIsModified = true;
            m_pImpl->m_bBroadcastModified = true;
@@ -3052,17 +3066,17 @@

        try
        {
            SotElement_Impl* pElement = m_pImpl->FindElement(aElementName);
            if (!pElement)
            auto pElementIter = m_pImpl->FindElementIt( aElementName );
            if ( pElementIter == m_pImpl->m_aChildrenMap.end() )
                throw container::NoSuchElementException(THROW_WHERE); //???

            uno::Reference<XNameAccess> xNameAccess(xDest, uno::UNO_QUERY_THROW);
            if (xNameAccess->hasByName(aNewName))
                throw container::ElementExistException(THROW_WHERE);

            m_pImpl->CopyStorageElement(pElement, xDest, aNewName, false);
            m_pImpl->CopyStorageElement(pElementIter->second, xDest, aNewName, false);

            m_pImpl->RemoveElement(pElement);
            m_pImpl->RemoveElement(pElementIter);

            m_pImpl->m_bIsModified = true;
            m_pImpl->m_bBroadcastModified = true;
@@ -3612,8 +3626,9 @@
        }

        bool bThrow = std::any_of(
            m_pImpl->m_aChildrenVector.begin(), m_pImpl->m_aChildrenVector.end(),
            [](const SotElement_Impl* pElement) {
            m_pImpl->m_aChildrenMap.begin(), m_pImpl->m_aChildrenMap.end(),
            [](decltype(m_pImpl->m_aChildrenMap)::value_type const & pair) {
                auto pElement = pair.second;
                return (pElement->m_xStorage
                        && (pElement->m_xStorage->m_pAntiImpl
                            || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty()))
@@ -3920,7 +3935,7 @@

    try
    {
        return ( !m_pImpl->GetChildrenVector().empty() );
        return m_pImpl->HasChildren();
    }
    catch( const uno::RuntimeException& rRuntimeException )
    {
diff --git a/package/source/xstor/xstorage.hxx b/package/source/xstor/xstorage.hxx
index a365dfd..85bb2c1 100644
--- a/package/source/xstor/xstorage.hxx
+++ b/package/source/xstor/xstorage.hxx
@@ -139,7 +139,7 @@
        return m_nModifiedListenerCount > 0 && m_pAntiImpl != nullptr;
    }

    SotElementVector_Impl                         m_aChildrenVector;
    std::unordered_map<OUString, SotElement_Impl*> m_aChildrenMap;
    SotElementVector_Impl                         m_aDeletedVector;

    css::uno::Reference< css::container::XNameContainer > m_xPackageFolder;
@@ -205,7 +205,7 @@
    void ReadContents();
    void ReadRelInfoIfNecessary();

    SotElementVector_Impl& GetChildrenVector();
    bool HasChildren();
    void GetStorageProperties();

    css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > GetAllRelationshipsIfAny();
@@ -229,6 +229,7 @@
                            bool bDirect );

    SotElement_Impl* FindElement( const OUString& rName );
    std::unordered_map<OUString, SotElement_Impl*>::iterator FindElementIt( const OUString& rName );

    SotElement_Impl* InsertStream( const OUString& aName, bool bEncr );
    void InsertRawStream( const OUString& aName, const css::uno::Reference< css::io::XInputStream >& xInStream );
@@ -242,7 +243,7 @@

    css::uno::Sequence< OUString > GetElementNames();

    void RemoveElement( SotElement_Impl* pElement );
    std::unordered_map<OUString, SotElement_Impl*>::iterator RemoveElement( std::unordered_map<OUString, SotElement_Impl*>::iterator pElement );
    static void ClearElement( SotElement_Impl* pElement );

    /// @throws css::embed::InvalidStorageException