include/vcl/BinaryDataContainer.hxx        |   15 ++-
 include/vcl/gfxlink.hxx                    |    3 
 vcl/source/gdi/impgraph.cxx                |    5 +
 vcl/source/graphic/BinaryDataContainer.cxx |  121 +++++++++++++++++++++++++----
 4 files changed, 127 insertions(+), 17 deletions(-)

New commits:
commit 3200e73aee4b954ddf4fa1b017efc871a9c08f28
Author:     Michael Meeks <michael.me...@collabora.com>
AuthorDate: Mon Apr 3 09:34:54 2023 +0100
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Mon May 8 08:32:36 2023 +0200

    BinaryDataContainer swap out implementation.
    
    We can easily accumulate a large number of in-memory graphic
    objects, and swapping these as well as the un-compressed
    images can become important.
    
    For now we swap out the container when the image is swapped
    out, however it seems unlikely it will ever need to be swapped
    in again.
    
    Despite the shared pImpl, we retained the reference counting
    on the underling vector to keep this immutable while we hand
    out a MemoryStream reference to it.
    
    Change-Id: Ib7ca45afb8499460b1852461f7c11afca3f3cdfa
    Signed-off-by: Michael Meeks <michael.me...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151436
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/include/vcl/BinaryDataContainer.hxx 
b/include/vcl/BinaryDataContainer.hxx
index 3e5b4395a61a..4011c8685487 100644
--- a/include/vcl/BinaryDataContainer.hxx
+++ b/include/vcl/BinaryDataContainer.hxx
@@ -13,6 +13,7 @@
 #include <sal/config.h>
 
 #include <com/sun/star/uno/Sequence.hxx>
+#include <unotools/tempfile.hxx>
 #include <tools/stream.hxx>
 #include <vcl/dllapi.h>
 
@@ -26,9 +27,11 @@
  */
 class VCL_DLLPUBLIC BinaryDataContainer final
 {
-private:
-    // the binary data
-    std::shared_ptr<std::vector<sal_uInt8>> mpData;
+    struct Impl;
+
+    std::shared_ptr<Impl> mpImpl;
+
+    void ensureSwappedIn() const;
 
 public:
     BinaryDataContainer() = default;
@@ -53,6 +56,12 @@ public:
     /// writes the contents to the given stream
     std::size_t writeToStream(SvStream& rStream) const;
 
+    /// return the in-memory size in bytes as of now.
+    std::size_t getSizeBytes() const;
+
+    /// swap out to disk for now
+    void swapOut() const;
+
     size_t calculateHash() const;
 };
 
diff --git a/include/vcl/gfxlink.hxx b/include/vcl/gfxlink.hxx
index 8c0f5fd32b05..531633b3f738 100644
--- a/include/vcl/gfxlink.hxx
+++ b/include/vcl/gfxlink.hxx
@@ -85,6 +85,9 @@ public:
     sal_uInt32          GetDataSize() const { return 
maDataContainer.getSize(); }
     const sal_uInt8*    GetData() const;
 
+    /// return the in-memory size as of now.
+    size_t              getSizeBytes() const { return 
maDataContainer.getSizeBytes(); }
+
     const BinaryDataContainer& getDataContainer() const
     {
         return maDataContainer;
diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx
index a4569f95e200..3b06fbe94787 100644
--- a/vcl/source/gdi/impgraph.cxx
+++ b/vcl/source/gdi/impgraph.cxx
@@ -1250,6 +1250,9 @@ bool ImpGraphic::swapOutGraphic(SvStream& rStream)
             break;
     }
 
+    if (mpGfxLink)
+        mpGfxLink->getDataContainer().swapOut();
+
     return true;
 }
 
@@ -1434,6 +1437,8 @@ void ImpGraphic::dumpState(rtl::OStringBuffer &rState)
     rState.append(static_cast<sal_Int32>(meType));
     rState.append("\tsize:\t");
     rState.append(static_cast<sal_Int64>(mnSizeBytes));
+    rState.append("\tgfxl:\t");
+    rState.append(static_cast<sal_Int64>(mpGfxLink ? mpGfxLink->getSizeBytes() 
: -1));
     rState.append("\t");
     rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Width()));
     rState.append("x");
diff --git a/vcl/source/graphic/BinaryDataContainer.cxx 
b/vcl/source/graphic/BinaryDataContainer.cxx
index 0eaaa75e380c..cfbd4560a8d5 100644
--- a/vcl/source/graphic/BinaryDataContainer.cxx
+++ b/vcl/source/graphic/BinaryDataContainer.cxx
@@ -10,20 +10,78 @@
 
 #include <vcl/BinaryDataContainer.hxx>
 #include <o3tl/hash_combine.hxx>
+#include <unotools/tempfile.hxx>
+#include <comphelper/lok.hxx>
+#include <sal/log.hxx>
+
+struct BinaryDataContainer::Impl
+{
+    // temp file to store the data out of RAM if necessary
+    std::unique_ptr<utl::TempFileNamed> mpFile;
+    // the binary data
+    std::shared_ptr<std::vector<sal_uInt8>> mpData;
+
+    Impl(SvStream& stream, size_t size) { readData(stream, size); }
+
+    /// Populate mpData from the stream
+    void readData(SvStream& stream, size_t size)
+    {
+        auto pData = std::make_shared<std::vector<sal_uInt8>>(size);
+        if (stream.ReadBytes(pData->data(), pData->size()) == size)
+            mpData = std::move(pData);
+    }
+
+    /// ensure the data is in-RAM
+    void ensureSwappedIn()
+    {
+        if (mpData || !mpFile)
+            return;
+
+        auto pStream = mpFile->GetStream(StreamMode::READ);
+        pStream->Seek(0);
+        readData(*pStream, pStream->remainingSize());
+
+        // Horrifying data loss ...
+        SAL_WARN_IF(pStream->GetError(), "vcl",
+                    "Inconsistent system - failed to swap image back in");
+        SAL_DEBUG("Swap in: " << pStream->GetError());
+    }
+
+    void swapOut()
+    {
+        if (mpFile)
+        {
+            // we already have it swapped out.
+            mpData.reset();
+            return;
+        }
+
+        if (!mpData || mpData->empty())
+            return;
+
+        mpFile.reset(new utl::TempFileNamed());
+        auto pStream = mpFile->GetStream(StreamMode::READWRITE);
+
+        pStream->WriteBytes(mpData->data(), mpData->size());
+
+        mpData.reset();
+    }
+};
 
 BinaryDataContainer::BinaryDataContainer(SvStream& stream, size_t size)
+    : mpImpl(new Impl(stream, size))
 {
-    auto pBuffer = std::make_shared<std::vector<sal_uInt8>>(size);
-    if (stream.ReadBytes(pBuffer->data(), pBuffer->size()) == size)
-        mpData = std::move(pBuffer);
 }
 
 size_t BinaryDataContainer::calculateHash() const
 {
     size_t nSeed = 0;
-    o3tl::hash_combine(nSeed, getSize());
-    for (sal_uInt8 const& rByte : *mpData)
-        o3tl::hash_combine(nSeed, rByte);
+    if (mpImpl && mpImpl->mpData && !mpImpl->mpData->empty())
+    {
+        o3tl::hash_combine(nSeed, getSize());
+        for (sal_uInt8 const& rByte : *mpImpl->mpData)
+            o3tl::hash_combine(nSeed, rByte);
+    }
     return nSeed;
 }
 
@@ -31,10 +89,11 @@ css::uno::Sequence<sal_Int8> 
BinaryDataContainer::getAsSequence() const
 {
     if (isEmpty())
         return css::uno::Sequence<sal_Int8>();
+    assert(mpImpl);
 
     css::uno::Sequence<sal_Int8> aData(getSize());
 
-    std::copy(mpData->cbegin(), mpData->cend(), aData.getArray());
+    std::copy(mpImpl->mpData->cbegin(), mpImpl->mpData->cend(), 
aData.getArray());
 
     return aData;
 }
@@ -48,27 +107,61 @@ class ReferencedMemoryStream : public SvMemoryStream
     std::shared_ptr<std::vector<sal_uInt8>> mpData;
 
 public:
-    ReferencedMemoryStream(const std::shared_ptr<std::vector<sal_uInt8>>& 
rData)
-        : SvMemoryStream(rData ? rData->data() : nullptr, rData->size(), 
StreamMode::READ)
-        , mpData(rData)
+    ReferencedMemoryStream(const std::shared_ptr<std::vector<sal_uInt8>>& 
pData)
+        : SvMemoryStream(pData->data(), pData->size(), StreamMode::READ)
+        , mpData(pData)
     {
     }
 };
 
 std::shared_ptr<SvStream> BinaryDataContainer::getAsStream()
 {
-    return std::make_shared<ReferencedMemoryStream>(mpData);
+    ensureSwappedIn(); // TODO: transfer in streamed chunks
+    return std::make_shared<ReferencedMemoryStream>(mpImpl->mpData);
 }
 
 std::size_t BinaryDataContainer::writeToStream(SvStream& rStream) const
 {
+    ensureSwappedIn(); // TODO: transfer in streamed chunks
     return rStream.WriteBytes(getData(), getSize());
 }
 
-size_t BinaryDataContainer::getSize() const { return mpData ? mpData->size() : 
0; }
+size_t BinaryDataContainer::getSize() const
+{
+    ensureSwappedIn();
+    return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0;
+}
+
+size_t BinaryDataContainer::getSizeBytes() const
+{
+    return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0;
+}
 
-bool BinaryDataContainer::isEmpty() const { return !mpData || mpData->empty(); 
}
+bool BinaryDataContainer::isEmpty() const
+{
+    ensureSwappedIn();
+    return !mpImpl || !mpImpl->mpData || mpImpl->mpData->empty();
+}
 
-const sal_uInt8* BinaryDataContainer::getData() const { return mpData ? 
mpData->data() : nullptr; }
+const sal_uInt8* BinaryDataContainer::getData() const
+{
+    ensureSwappedIn();
+    return mpImpl && mpImpl->mpData ? mpImpl->mpData->data() : nullptr;
+}
+
+void BinaryDataContainer::ensureSwappedIn() const
+{
+    if (mpImpl)
+        mpImpl->ensureSwappedIn();
+}
+
+void BinaryDataContainer::swapOut() const
+{
+    // Only bother reducing memory footprint in kit mode - for mobile/online 
etc.
+    if (!mpImpl || !comphelper::LibreOfficeKit::isActive())
+        return;
+
+    mpImpl->swapOut();
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to