summaryrefslogtreecommitdiffstats
path: root/svl/source/misc/strmadpt.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'svl/source/misc/strmadpt.cxx')
-rw-r--r--svl/source/misc/strmadpt.cxx653
1 files changed, 653 insertions, 0 deletions
diff --git a/svl/source/misc/strmadpt.cxx b/svl/source/misc/strmadpt.cxx
new file mode 100644
index 000000000..7a755d924
--- /dev/null
+++ b/svl/source/misc/strmadpt.cxx
@@ -0,0 +1,653 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <algorithm>
+#include <limits>
+#include <set>
+#include <string.h>
+
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/instrm.hxx>
+#include <svl/outstrm.hxx>
+#include <utility>
+
+using namespace com::sun::star;
+
+class SvDataPipe_Impl
+{
+public:
+ enum SeekResult { SEEK_BEFORE_MARKED, SEEK_OK, SEEK_PAST_END };
+
+private:
+ struct Page
+ {
+ Page * m_pPrev;
+ Page * m_pNext;
+ sal_Int8 * m_pStart;
+ sal_Int8 * m_pRead;
+ sal_Int8 * m_pEnd;
+ sal_uInt32 m_nOffset;
+ sal_Int8 m_aBuffer[1];
+ };
+ static const sal_uInt32 m_nPageSize = 1000;
+
+ std::multiset< sal_uInt32 > m_aMarks;
+ Page * m_pFirstPage;
+ Page * m_pReadPage;
+ Page * m_pWritePage;
+ sal_Int8 * m_pReadBuffer;
+ sal_uInt32 m_nReadBufferSize;
+ sal_uInt32 m_nReadBufferFilled;
+ sal_uInt32 m_nPages;
+ bool m_bEOF;
+
+ void remove(Page * pPage);
+
+public:
+ inline SvDataPipe_Impl();
+
+ ~SvDataPipe_Impl();
+
+ inline void setReadBuffer(sal_Int8 * pBuffer, sal_uInt32 nSize);
+
+ sal_uInt32 read();
+
+ void clearReadBuffer() { m_pReadBuffer = nullptr; }
+
+ void write(sal_Int8 const * pBuffer, sal_uInt32 nSize);
+
+ void setEOF() { m_bEOF = true; }
+
+ inline bool isEOF() const;
+
+ SeekResult setReadPosition(sal_uInt32 nPosition);
+};
+
+SvDataPipe_Impl::SvDataPipe_Impl()
+ : m_pFirstPage( nullptr )
+ , m_pReadPage( nullptr )
+ , m_pWritePage( nullptr )
+ , m_pReadBuffer( nullptr )
+ , m_nReadBufferSize( 0 )
+ , m_nReadBufferFilled( 0 )
+ , m_nPages( 0 )
+ , m_bEOF( false )
+{}
+
+inline void SvDataPipe_Impl::setReadBuffer(sal_Int8 * pBuffer,
+ sal_uInt32 nSize)
+{
+ m_pReadBuffer = pBuffer;
+ m_nReadBufferSize = nSize;
+ m_nReadBufferFilled = 0;
+}
+
+inline bool SvDataPipe_Impl::isEOF() const
+{
+ return m_bEOF && m_pReadPage == m_pWritePage
+ && (!m_pReadPage || m_pReadPage->m_pRead == m_pReadPage->m_pEnd);
+}
+
+
+
+// SvInputStream
+
+bool SvInputStream::open()
+{
+ if (GetError() != ERRCODE_NONE)
+ return false;
+ if (!(m_xSeekable.is() || m_pPipe))
+ {
+ if (!m_xStream.is())
+ {
+ SetError(ERRCODE_IO_INVALIDDEVICE);
+ return false;
+ }
+ m_xSeekable.set(m_xStream, uno::UNO_QUERY);
+ if (!m_xSeekable.is())
+ m_pPipe.reset( new SvDataPipe_Impl );
+ }
+ return true;
+}
+
+// virtual
+std::size_t SvInputStream::GetData(void * pData, std::size_t const nSize)
+{
+ if (!open())
+ {
+ SetError(ERRCODE_IO_CANTREAD);
+ return 0;
+ }
+ // check if a truncated STREAM_SEEK_TO_END was passed
+ assert(m_nSeekedFrom != SAL_MAX_UINT32);
+ sal_uInt32 nRead = 0;
+ if (m_xSeekable.is())
+ {
+ if (m_nSeekedFrom != STREAM_SEEK_TO_END)
+ {
+ try
+ {
+ m_xSeekable->seek(m_nSeekedFrom);
+ }
+ catch (const io::IOException&)
+ {
+ SetError(ERRCODE_IO_CANTREAD);
+ return 0;
+ }
+ m_nSeekedFrom = STREAM_SEEK_TO_END;
+ }
+ for (;;)
+ {
+ sal_Int32 nRemain
+ = sal_Int32(
+ std::min(std::size_t(nSize - nRead),
+ std::size_t(std::numeric_limits<sal_Int32>::max())));
+ if (nRemain == 0)
+ break;
+ uno::Sequence< sal_Int8 > aBuffer;
+ sal_Int32 nCount;
+ try
+ {
+ nCount = m_xStream->readBytes(aBuffer, nRemain);
+ }
+ catch (const io::IOException&)
+ {
+ SetError(ERRCODE_IO_CANTREAD);
+ return nRead;
+ }
+ memcpy(static_cast< sal_Int8 * >(pData) + nRead,
+ aBuffer.getConstArray(), sal_uInt32(nCount));
+ nRead += nCount;
+ if (nCount < nRemain)
+ break;
+ }
+ }
+ else
+ {
+ if (m_nSeekedFrom != STREAM_SEEK_TO_END)
+ {
+ SetError(ERRCODE_IO_CANTREAD);
+ return 0;
+ }
+ m_pPipe->setReadBuffer(static_cast< sal_Int8 * >(pData), nSize);
+ nRead = m_pPipe->read();
+ if (nRead < nSize && !m_pPipe->isEOF())
+ for (;;)
+ {
+ sal_Int32 nRemain
+ = sal_Int32(
+ std::min(
+ std::size_t(nSize - nRead),
+ std::size_t(std::numeric_limits<sal_Int32>::max())));
+ if (nRemain == 0)
+ break;
+ uno::Sequence< sal_Int8 > aBuffer;
+ sal_Int32 nCount;
+ try
+ {
+ nCount = m_xStream->readBytes(aBuffer, nRemain);
+ }
+ catch (const io::IOException&)
+ {
+ SetError(ERRCODE_IO_CANTREAD);
+ break;
+ }
+ m_pPipe->write(aBuffer.getConstArray(), sal_uInt32(nCount));
+ nRead += m_pPipe->read();
+ if (nCount < nRemain)
+ {
+ m_xStream->closeInput();
+ m_pPipe->setEOF();
+ break;
+ }
+ }
+ m_pPipe->clearReadBuffer();
+ }
+ return nRead;
+}
+
+// virtual
+std::size_t SvInputStream::PutData(void const *, std::size_t)
+{
+ SetError(ERRCODE_IO_NOTSUPPORTED);
+ return 0;
+}
+
+// virtual
+void SvInputStream::FlushData()
+{}
+
+// virtual
+sal_uInt64 SvInputStream::SeekPos(sal_uInt64 const nPos)
+{
+ // check if a truncated STREAM_SEEK_TO_END was passed
+ assert(nPos != SAL_MAX_UINT32);
+ if (open())
+ {
+ if (nPos == STREAM_SEEK_TO_END)
+ {
+ if (m_nSeekedFrom == STREAM_SEEK_TO_END)
+ {
+ if (m_xSeekable.is())
+ try
+ {
+ sal_Int64 nLength = m_xSeekable->getLength();
+ OSL_ASSERT(nLength >= 0);
+ if (o3tl::make_unsigned(nLength)
+ < STREAM_SEEK_TO_END)
+ {
+ m_nSeekedFrom = Tell();
+ return sal_uInt64(nLength);
+ }
+ }
+ catch (const io::IOException&)
+ {
+ }
+ else
+ return Tell(); //@@@
+ }
+ else
+ return Tell();
+ }
+ else if (nPos == m_nSeekedFrom)
+ {
+ m_nSeekedFrom = STREAM_SEEK_TO_END;
+ return nPos;
+ }
+ else if (m_xSeekable.is())
+ {
+ try
+ {
+ m_xSeekable->seek(nPos);
+ m_nSeekedFrom = STREAM_SEEK_TO_END;
+ return nPos;
+ }
+ catch (const io::IOException&)
+ {
+ }
+ }
+ else if (m_pPipe->setReadPosition(nPos) == SvDataPipe_Impl::SEEK_OK)
+ {
+ m_nSeekedFrom = STREAM_SEEK_TO_END;
+ return nPos;
+ }
+ else if ( nPos > Tell() )
+ {
+ // Read out the bytes
+ sal_Int32 nRead = nPos - Tell();
+ uno::Sequence< sal_Int8 > aBuffer;
+ m_xStream->readBytes( aBuffer, nRead );
+ return nPos;
+ }
+ else if ( nPos == Tell() )
+ return nPos;
+ }
+ SetError(ERRCODE_IO_CANTSEEK);
+ return Tell();
+}
+
+// virtual
+void SvInputStream::SetSize(sal_uInt64)
+{
+ SetError(ERRCODE_IO_NOTSUPPORTED);
+}
+
+SvInputStream::SvInputStream( css::uno::Reference< css::io::XInputStream > xTheStream):
+ m_xStream(std::move(xTheStream)),
+ m_nSeekedFrom(STREAM_SEEK_TO_END)
+{
+ SetBufferSize(0);
+}
+
+// virtual
+SvInputStream::~SvInputStream()
+{
+ if (m_xStream.is())
+ {
+ try
+ {
+ m_xStream->closeInput();
+ }
+ catch (const io::IOException&)
+ {
+ }
+ }
+}
+
+// SvOutputStream
+
+// virtual
+std::size_t SvOutputStream::GetData(void *, std::size_t)
+{
+ SetError(ERRCODE_IO_NOTSUPPORTED);
+ return 0;
+}
+
+// virtual
+std::size_t SvOutputStream::PutData(void const * pData, std::size_t nSize)
+{
+ if (!m_xStream.is())
+ {
+ SetError(ERRCODE_IO_CANTWRITE);
+ return 0;
+ }
+ std::size_t nWritten = 0;
+ for (;;)
+ {
+ sal_Int32 nRemain
+ = sal_Int32(
+ std::min(std::size_t(nSize - nWritten),
+ std::size_t(std::numeric_limits<sal_Int32>::max())));
+ if (nRemain == 0)
+ break;
+ try
+ {
+ m_xStream->writeBytes(uno::Sequence< sal_Int8 >(
+ static_cast<const sal_Int8 * >(pData)
+ + nWritten,
+ nRemain));
+ }
+ catch (const io::IOException&)
+ {
+ SetError(ERRCODE_IO_CANTWRITE);
+ break;
+ }
+ nWritten += nRemain;
+ }
+ return nWritten;
+}
+
+// virtual
+sal_uInt64 SvOutputStream::SeekPos(sal_uInt64)
+{
+ SetError(ERRCODE_IO_NOTSUPPORTED);
+ return 0;
+}
+
+// virtual
+void SvOutputStream::FlushData()
+{
+ if (!m_xStream.is())
+ {
+ SetError(ERRCODE_IO_INVALIDDEVICE);
+ return;
+ }
+ try
+ {
+ m_xStream->flush();
+ }
+ catch (const io::IOException&)
+ {
+ }
+}
+
+// virtual
+void SvOutputStream::SetSize(sal_uInt64)
+{
+ SetError(ERRCODE_IO_NOTSUPPORTED);
+}
+
+SvOutputStream::SvOutputStream(uno::Reference< io::XOutputStream > xTheStream):
+ m_xStream(std::move(xTheStream))
+{
+ SetBufferSize(0);
+}
+
+// virtual
+SvOutputStream::~SvOutputStream()
+{
+ if (m_xStream.is())
+ {
+ try
+ {
+ m_xStream->closeOutput();
+ }
+ catch (const io::IOException&)
+ {
+ }
+ }
+}
+
+
+// SvDataPipe_Impl
+
+
+void SvDataPipe_Impl::remove(Page * pPage)
+{
+ if (
+ pPage != m_pFirstPage ||
+ m_pReadPage == m_pFirstPage ||
+ (
+ !m_aMarks.empty() &&
+ *m_aMarks.begin() < m_pFirstPage->m_nOffset + m_nPageSize
+ )
+ )
+ {
+ return;
+ }
+
+ m_pFirstPage = m_pFirstPage->m_pNext;
+
+ if (m_nPages <= 100) // min pages
+ return;
+
+ pPage->m_pPrev->m_pNext = pPage->m_pNext;
+ pPage->m_pNext->m_pPrev = pPage->m_pPrev;
+ std::free(pPage);
+ --m_nPages;
+}
+
+SvDataPipe_Impl::~SvDataPipe_Impl()
+{
+ if (m_pFirstPage != nullptr)
+ for (Page * pPage = m_pFirstPage;;)
+ {
+ Page * pNext = pPage->m_pNext;
+ std::free(pPage);
+ if (pNext == m_pFirstPage)
+ break;
+ pPage = pNext;
+ }
+}
+
+sal_uInt32 SvDataPipe_Impl::read()
+{
+ if (m_pReadBuffer == nullptr || m_nReadBufferSize == 0 || m_pReadPage == nullptr)
+ return 0;
+
+ sal_uInt32 nSize = m_nReadBufferSize;
+ sal_uInt32 nRemain = m_nReadBufferSize - m_nReadBufferFilled;
+
+ m_pReadBuffer += m_nReadBufferFilled;
+ m_nReadBufferSize -= m_nReadBufferFilled;
+ m_nReadBufferFilled = 0;
+
+ while (nRemain > 0)
+ {
+ sal_uInt32 nBlock = std::min(sal_uInt32(m_pReadPage->m_pEnd
+ - m_pReadPage->m_pRead),
+ nRemain);
+ memcpy(m_pReadBuffer, m_pReadPage->m_pRead, nBlock);
+ m_pReadPage->m_pRead += nBlock;
+ m_pReadBuffer += nBlock;
+ m_nReadBufferSize -= nBlock;
+ m_nReadBufferFilled = 0;
+ nRemain -= nBlock;
+
+ if (m_pReadPage == m_pWritePage)
+ break;
+
+ if (m_pReadPage->m_pRead == m_pReadPage->m_pEnd)
+ {
+ Page * pRemove = m_pReadPage;
+ m_pReadPage = pRemove->m_pNext;
+ remove(pRemove);
+ }
+ }
+
+ return nSize - nRemain;
+}
+
+void SvDataPipe_Impl::write(sal_Int8 const * pBuffer, sal_uInt32 nSize)
+{
+ if (nSize == 0)
+ return;
+
+ if (m_pWritePage == nullptr)
+ {
+ m_pFirstPage
+ = static_cast< Page * >(std::malloc(sizeof (Page)
+ + m_nPageSize
+ - 1));
+ m_pFirstPage->m_pPrev = m_pFirstPage;
+ m_pFirstPage->m_pNext = m_pFirstPage;
+ m_pFirstPage->m_pStart = m_pFirstPage->m_aBuffer;
+ m_pFirstPage->m_pRead = m_pFirstPage->m_aBuffer;
+ m_pFirstPage->m_pEnd = m_pFirstPage->m_aBuffer;
+ m_pFirstPage->m_nOffset = 0;
+ m_pReadPage = m_pFirstPage;
+ m_pWritePage = m_pFirstPage;
+ ++m_nPages;
+ }
+
+ sal_uInt32 nRemain = nSize;
+
+ if (m_pReadBuffer != nullptr && m_pReadPage == m_pWritePage
+ && m_pReadPage->m_pRead == m_pWritePage->m_pEnd)
+ {
+ sal_uInt32 nBlock = std::min(nRemain,
+ sal_uInt32(m_nReadBufferSize
+ - m_nReadBufferFilled));
+ sal_uInt32 nPosition = m_pWritePage->m_nOffset
+ + (m_pWritePage->m_pEnd
+ - m_pWritePage->m_aBuffer);
+ if (!m_aMarks.empty())
+ nBlock = *m_aMarks.begin() > nPosition ?
+ std::min(nBlock, sal_uInt32(*m_aMarks.begin()
+ - nPosition)) :
+ 0;
+
+ if (nBlock > 0)
+ {
+ memcpy(m_pReadBuffer + m_nReadBufferFilled, pBuffer,
+ nBlock);
+ m_nReadBufferFilled += nBlock;
+ nRemain -= nBlock;
+
+ nPosition += nBlock;
+ m_pWritePage->m_nOffset = (nPosition / m_nPageSize) * m_nPageSize;
+ m_pWritePage->m_pStart = m_pWritePage->m_aBuffer
+ + nPosition % m_nPageSize;
+ m_pWritePage->m_pRead = m_pWritePage->m_pStart;
+ m_pWritePage->m_pEnd = m_pWritePage->m_pStart;
+ }
+ }
+
+ if (nRemain <= 0)
+ return;
+
+ for (;;)
+ {
+ sal_uInt32 nBlock
+ = std::min(sal_uInt32(m_pWritePage->m_aBuffer + m_nPageSize
+ - m_pWritePage->m_pEnd),
+ nRemain);
+ memcpy(m_pWritePage->m_pEnd, pBuffer, nBlock);
+ m_pWritePage->m_pEnd += nBlock;
+ pBuffer += nBlock;
+ nRemain -= nBlock;
+
+ if (nRemain == 0)
+ break;
+
+ if (m_pWritePage->m_pNext == m_pFirstPage)
+ {
+ if (m_nPages == std::numeric_limits< sal_uInt32 >::max())
+ break;
+
+ Page * pNew
+ = static_cast< Page * >(std::malloc(
+ sizeof (Page) + m_nPageSize
+ - 1));
+ pNew->m_pPrev = m_pWritePage;
+ pNew->m_pNext = m_pWritePage->m_pNext;
+
+ m_pWritePage->m_pNext->m_pPrev = pNew;
+ m_pWritePage->m_pNext = pNew;
+ ++m_nPages;
+ }
+
+ m_pWritePage->m_pNext->m_nOffset = m_pWritePage->m_nOffset
+ + m_nPageSize;
+ m_pWritePage = m_pWritePage->m_pNext;
+ m_pWritePage->m_pStart = m_pWritePage->m_aBuffer;
+ m_pWritePage->m_pRead = m_pWritePage->m_aBuffer;
+ m_pWritePage->m_pEnd = m_pWritePage->m_aBuffer;
+ }
+}
+
+SvDataPipe_Impl::SeekResult SvDataPipe_Impl::setReadPosition(sal_uInt32
+ nPosition)
+{
+ if (m_pFirstPage == nullptr)
+ return nPosition == 0 ? SEEK_OK : SEEK_PAST_END;
+
+ if (nPosition
+ <= m_pReadPage->m_nOffset
+ + (m_pReadPage->m_pRead - m_pReadPage->m_aBuffer))
+ {
+ if (nPosition
+ < m_pFirstPage->m_nOffset
+ + (m_pFirstPage->m_pStart - m_pFirstPage->m_aBuffer))
+ return SEEK_BEFORE_MARKED;
+
+ while (nPosition < m_pReadPage->m_nOffset)
+ {
+ m_pReadPage->m_pRead = m_pReadPage->m_pStart;
+ m_pReadPage = m_pReadPage->m_pPrev;
+ }
+ }
+ else
+ {
+ if (nPosition
+ > m_pWritePage->m_nOffset
+ + (m_pWritePage->m_pEnd - m_pWritePage->m_aBuffer))
+ return SEEK_PAST_END;
+
+ while (m_pReadPage != m_pWritePage
+ && nPosition >= m_pReadPage->m_nOffset + m_nPageSize)
+ {
+ Page * pRemove = m_pReadPage;
+ m_pReadPage = pRemove->m_pNext;
+ remove(pRemove);
+ }
+ }
+
+ m_pReadPage->m_pRead = m_pReadPage->m_aBuffer
+ + (nPosition - m_pReadPage->m_nOffset);
+ return SEEK_OK;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */