tdf#63130 reduce large memory copies when reading from BinaryDataContainer

rather than writing a bunch more code, extract the common part from
comphelper::SequenceInputStream into a new base class

Change-Id: I0d3561e3ca2e748b904128e3b5955e27196d7170
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151943
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/comphelper/source/streaming/seqstream.cxx b/comphelper/source/streaming/seqstream.cxx
index 9626484..49eebc1 100644
--- a/comphelper/source/streaming/seqstream.cxx
+++ b/comphelper/source/streaming/seqstream.cxx
@@ -36,26 +36,27 @@ using namespace ::osl;



SequenceInputStream::SequenceInputStream(
    css::uno::Sequence<sal_Int8> const & rData)
:   m_aData(rData)
MemoryInputStream::MemoryInputStream(
    const sal_Int8* pData, sal_Int32 nDataLength)
:   m_pMemoryData(pData)
,   m_nMemoryDataLength(nDataLength)
,   m_nPos(0)
{
}

// checks if closed, returns available size, not mutex-protected

inline sal_Int32 SequenceInputStream::avail()
inline sal_Int32 MemoryInputStream::avail()
{
    if (m_nPos == -1)
        throw NotConnectedException(OUString(), *this);

    return m_aData.getLength() - m_nPos;
    return m_nMemoryDataLength - m_nPos;
}

// css::io::XInputStream

sal_Int32 SAL_CALL SequenceInputStream::readBytes( Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead )
sal_Int32 SAL_CALL MemoryInputStream::readBytes( Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead )
{
    if (nBytesToRead < 0)
        throw BufferSizeExceededException(OUString(),*this);
@@ -68,13 +69,13 @@ sal_Int32 SAL_CALL SequenceInputStream::readBytes( Sequence<sal_Int8>& aData, sa
        nBytesToRead = nAvail;

    aData.realloc(nBytesToRead);
    memcpy(aData.getArray(), m_aData.getConstArray() + m_nPos, nBytesToRead);
    memcpy(aData.getArray(), m_pMemoryData + m_nPos, nBytesToRead);
    m_nPos += nBytesToRead;

    return nBytesToRead;
}

sal_Int32 SequenceInputStream::readSomeBytes( sal_Int8* pData, sal_Int32 nBytesToRead )
sal_Int32 MemoryInputStream::readSomeBytes( sal_Int8* pData, sal_Int32 nBytesToRead )
{
    if (nBytesToRead < 0)
        throw BufferSizeExceededException(OUString(),*this);
@@ -86,20 +87,20 @@ sal_Int32 SequenceInputStream::readSomeBytes( sal_Int8* pData, sal_Int32 nBytesT
    if (nAvail < nBytesToRead)
        nBytesToRead = nAvail;

    memcpy(pData, m_aData.getConstArray() + m_nPos, nBytesToRead);
    memcpy(pData, m_pMemoryData + m_nPos, nBytesToRead);
    m_nPos += nBytesToRead;

    return nBytesToRead;
}

sal_Int32 SAL_CALL SequenceInputStream::readSomeBytes( Sequence<sal_Int8>& aData, sal_Int32 nMaxBytesToRead )
sal_Int32 SAL_CALL MemoryInputStream::readSomeBytes( Sequence<sal_Int8>& aData, sal_Int32 nMaxBytesToRead )
{
    // all data is available at once
    return readBytes(aData, nMaxBytesToRead);
}


void SAL_CALL SequenceInputStream::skipBytes( sal_Int32 nBytesToSkip )
void SAL_CALL MemoryInputStream::skipBytes( sal_Int32 nBytesToSkip )
{
    if (nBytesToSkip < 0)
        throw BufferSizeExceededException(OUString(),*this);
@@ -115,7 +116,7 @@ void SAL_CALL SequenceInputStream::skipBytes( sal_Int32 nBytesToSkip )
}


sal_Int32 SAL_CALL SequenceInputStream::available(  )
sal_Int32 SAL_CALL MemoryInputStream::available(  )
{
    std::scoped_lock aGuard( m_aMutex );

@@ -123,7 +124,7 @@ sal_Int32 SAL_CALL SequenceInputStream::available(  )
}


void SAL_CALL SequenceInputStream::closeInput(  )
void SAL_CALL MemoryInputStream::closeInput(  )
{
    std::scoped_lock aGuard( m_aMutex );

@@ -133,24 +134,32 @@ void SAL_CALL SequenceInputStream::closeInput(  )
    m_nPos = -1;
}

void SAL_CALL SequenceInputStream::seek( sal_Int64 location )
void SAL_CALL MemoryInputStream::seek( sal_Int64 location )
{
    if ( location > m_aData.getLength() || location < 0 || location > SAL_MAX_INT32 )
    if ( location > m_nMemoryDataLength || location < 0 || location > SAL_MAX_INT32 )
        throw IllegalArgumentException("bad location", static_cast<cppu::OWeakObject*>(this), 1);
    std::scoped_lock aGuard( m_aMutex );
    m_nPos = static_cast<sal_Int32>(location);
}

sal_Int64 SAL_CALL SequenceInputStream::getPosition()
sal_Int64 SAL_CALL MemoryInputStream::getPosition()
{
    std::scoped_lock aGuard( m_aMutex );
    return m_nPos;
}

sal_Int64 SAL_CALL SequenceInputStream::getLength(  )
sal_Int64 SAL_CALL MemoryInputStream::getLength(  )
{
    std::scoped_lock aGuard( m_aMutex );
    return m_aData.getLength();
    return m_nMemoryDataLength;
}


SequenceInputStream::SequenceInputStream(
    css::uno::Sequence<sal_Int8> const & rData)
:   MemoryInputStream(rData.getConstArray(), rData.getLength())
,   m_aData(rData)
{
}


diff --git a/include/comphelper/seqstream.hxx b/include/comphelper/seqstream.hxx
index 1f1db11..1fd695b 100644
--- a/include/comphelper/seqstream.hxx
+++ b/include/comphelper/seqstream.hxx
@@ -33,21 +33,18 @@
namespace comphelper
{


// SequenceInputStream
// stream for reading data from a sequence of bytes


class COMPHELPER_DLLPUBLIC SequenceInputStream final
/** Base class for wrappers around memory data that want to be exposed as an XInputStream */
class COMPHELPER_DLLPUBLIC MemoryInputStream
    : public ::cppu::WeakImplHelper< css::io::XInputStream, css::io::XSeekable >,
      public comphelper::ByteReader
{
    std::mutex    m_aMutex;
    css::uno::Sequence<sal_Int8> const m_aData;
    const sal_Int8* m_pMemoryData;
    sal_Int32       m_nMemoryDataLength;
    sal_Int32       m_nPos;

public:
    SequenceInputStream(css::uno::Sequence<sal_Int8> const & rData);
    MemoryInputStream(const sal_Int8* pData, sal_Int32 nDataLength);

// css::io::XInputStream
    virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence<sal_Int8>& aData, sal_Int32 nBytesToRead ) override;
@@ -71,6 +68,17 @@ private:
    sal_Int32 avail();
};


// Stream for reading data from a sequence of bytes
class COMPHELPER_DLLPUBLIC SequenceInputStream final
    : public MemoryInputStream
{
    css::uno::Sequence<sal_Int8> const m_aData;

public:
    SequenceInputStream(css::uno::Sequence<sal_Int8> const & rData);
};

// don't export to avoid duplicate WeakImplHelper definitions with MSVC
class SAL_DLLPUBLIC_TEMPLATE OSequenceOutputStream_Base
    : public ::cppu::WeakImplHelper< css::io::XOutputStream >
diff --git a/include/vcl/BinaryDataContainer.hxx b/include/vcl/BinaryDataContainer.hxx
index f6f07f0..72bd852 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 <com/sun/star/io/XInputStream.hpp>
#include <unotools/tempfile.hxx>
#include <tools/stream.hxx>
#include <vcl/dllapi.h>
@@ -53,6 +54,9 @@ public:
    // Returns the data as a readonly stream open for reading
    std::shared_ptr<SvStream> getAsStream();

    // Returns the data as a readonly stream open for reading
    css::uno::Reference<css::io::XInputStream> getAsXInputStream();

    /// writes the contents to the given stream
    std::size_t writeToStream(SvStream& rStream) const;

diff --git a/vcl/source/gdi/vectorgraphicdata.cxx b/vcl/source/gdi/vectorgraphicdata.cxx
index 979fd4f..7dc5107 100644
--- a/vcl/source/gdi/vectorgraphicdata.cxx
+++ b/vcl/source/gdi/vectorgraphicdata.cxx
@@ -196,8 +196,7 @@ void VectorGraphicData::ensureSequenceAndRange()
    {
        case VectorGraphicDataType::Svg:
        {
            css::uno::Sequence<sal_Int8> aDataSequence = maDataContainer.getCopyAsByteSequence();
            const uno::Reference<io::XInputStream> xInputStream(new comphelper::SequenceInputStream(aDataSequence));
            const uno::Reference<io::XInputStream> xInputStream = maDataContainer.getAsXInputStream();

            const uno::Reference< graphic::XSvgParser > xSvgParser = graphic::SvgTools::create(xContext);

@@ -211,8 +210,7 @@ void VectorGraphicData::ensureSequenceAndRange()
        {
            const uno::Reference< graphic::XEmfParser > xEmfParser = graphic::EmfTools::create(xContext);

            css::uno::Sequence<sal_Int8> aDataSequence = maDataContainer.getCopyAsByteSequence();
            const uno::Reference<io::XInputStream> xInputStream(new comphelper::SequenceInputStream(aDataSequence));
            const uno::Reference<io::XInputStream> xInputStream = maDataContainer.getAsXInputStream();

            if (xInputStream.is())
            {
diff --git a/vcl/source/graphic/BinaryDataContainer.cxx b/vcl/source/graphic/BinaryDataContainer.cxx
index f395497f..83cacb4 100644
--- a/vcl/source/graphic/BinaryDataContainer.cxx
+++ b/vcl/source/graphic/BinaryDataContainer.cxx
@@ -12,6 +12,7 @@
#include <o3tl/hash_combine.hxx>
#include <unotools/tempfile.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/seqstream.hxx>
#include <sal/log.hxx>

struct BinaryDataContainer::Impl
@@ -115,6 +116,19 @@ public:
    {
    }
};

class ReferencedXInputStream : public comphelper::MemoryInputStream
{
    std::shared_ptr<std::vector<sal_uInt8>> mpData;

public:
    ReferencedXInputStream(const std::shared_ptr<std::vector<sal_uInt8>>& pData)
        : comphelper::MemoryInputStream(reinterpret_cast<const sal_Int8*>(pData->data()),
                                        pData->size())
        , mpData(pData)
    {
    }
};
}

std::shared_ptr<SvStream> BinaryDataContainer::getAsStream()
@@ -123,6 +137,12 @@ std::shared_ptr<SvStream> BinaryDataContainer::getAsStream()
    return std::make_shared<ReferencedMemoryStream>(mpImpl->mpData);
}

css::uno::Reference<css::io::XInputStream> BinaryDataContainer::getAsXInputStream()
{
    ensureSwappedIn(); // TODO: transfer in streamed chunks
    return new ReferencedXInputStream(mpImpl->mpData);
}

std::size_t BinaryDataContainer::writeToStream(SvStream& rStream) const
{
    ensureSwappedIn(); // TODO: transfer in streamed chunks