/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svl.hxx"

#include <functional> // needed under Solaris when including <algorithm>...

#include <algorithm>
#include <limits>
#include <set>
#include <rtl/alloc.h>
#include <rtl/memory.h>
#include <svl/instrm.hxx>
#include <svl/outstrm.hxx>
#include <svl/strmadpt.hxx>

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];
	};

	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_nPageSize;
	sal_uInt32 m_nMinPages;
	sal_uInt32 m_nMaxPages;
	sal_uInt32 m_nPages;
	bool m_bEOF;

	bool remove(Page * pPage);

public:
	inline SvDataPipe_Impl(sal_uInt32 nThePageSize = 1000,
						   sal_uInt32 nTheMinPages = 100,
						   sal_uInt32 nTheMaxPages
						       = std::numeric_limits< sal_uInt32 >::max());

	~SvDataPipe_Impl();

	inline void setReadBuffer(sal_Int8 * pBuffer, sal_uInt32 nSize);

	sal_uInt32 read();

	void clearReadBuffer() { m_pReadBuffer = 0; }

	sal_uInt32 write(sal_Int8 const * pBuffer, sal_uInt32 nSize);

	void setEOF() { m_bEOF = true; }

	inline bool isEOF() const;

	bool addMark(sal_uInt32 nPosition);

	bool removeMark(sal_uInt32 nPosition);

	inline sal_uInt32 getReadPosition() const;

	SeekResult setReadPosition(sal_uInt32 nPosition);
};

SvDataPipe_Impl::SvDataPipe_Impl(sal_uInt32 nThePageSize,
								 sal_uInt32 nTheMinPages,
								 sal_uInt32 nTheMaxPages):
	m_pFirstPage(0),
	m_pReadPage(0),
	m_pWritePage(0),
	m_pReadBuffer(0),
	m_nPageSize(std::min< sal_uInt32 >(
		            std::max< sal_uInt32 >(nThePageSize, sal_uInt32(1)),
					sal_uInt32(std::numeric_limits< sal_uInt32 >::max()
							       - sizeof (Page) + 1))),
	m_nMinPages(std::max< sal_uInt32 >(nTheMinPages, sal_uInt32(1))),
	m_nMaxPages(std::max< sal_uInt32 >(nTheMaxPages, sal_uInt32(1))),
	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);
}

inline sal_uInt32 SvDataPipe_Impl::getReadPosition() const
{
	return m_pReadPage == 0 ? 0 :
		                      m_pReadPage->m_nOffset
		                          + (m_pReadPage->m_pRead
									     - m_pReadPage->m_aBuffer);
}

//============================================================================
//
//  SvOutputStreamOpenLockBytes
//
//============================================================================

TYPEINIT1(SvOutputStreamOpenLockBytes, SvOpenLockBytes)

//============================================================================
// virtual
ErrCode SvOutputStreamOpenLockBytes::ReadAt(sal_uLong, void *, sal_uLong, sal_uLong *)
	const
{
	return ERRCODE_IO_CANTREAD;
}

//============================================================================
// virtual
ErrCode SvOutputStreamOpenLockBytes::WriteAt(sal_uLong nPos, void const * pBuffer,
											 sal_uLong nCount, sal_uLong * pWritten)
{
	if (nPos != m_nPosition)
		return ERRCODE_IO_CANTWRITE;
	return FillAppend(pBuffer, nCount, pWritten);
}

//============================================================================
// virtual
ErrCode SvOutputStreamOpenLockBytes::Flush() const
{
	if (!m_xOutputStream.is())
		return ERRCODE_IO_CANTWRITE;
	try
	{
		m_xOutputStream->flush();
	}
	catch (io::IOException)
	{
		return ERRCODE_IO_CANTWRITE;
	}
	return ERRCODE_NONE;
}

//============================================================================
// virtual
ErrCode SvOutputStreamOpenLockBytes::SetSize(sal_uLong)
{
	return ERRCODE_IO_NOTSUPPORTED;
}

//============================================================================
// virtual
ErrCode SvOutputStreamOpenLockBytes::Stat(SvLockBytesStat * pStat,
										  SvLockBytesStatFlag) const
{
	if (pStat)
		pStat->nSize = m_nPosition;
	return ERRCODE_NONE;
}

//============================================================================
// virtual
ErrCode SvOutputStreamOpenLockBytes::FillAppend(void const * pBuffer,
												sal_uLong nCount,
												sal_uLong * pWritten)
{
	if (!m_xOutputStream.is())
		return ERRCODE_IO_CANTWRITE;
	if (nCount > 0
		&& nCount > std::numeric_limits< sal_uLong >::max() - m_nPosition)
	{
		nCount = std::numeric_limits< sal_uLong >::max() - m_nPosition;
		if (nCount == 0)
			return ERRCODE_IO_CANTWRITE;
	}
	try
	{
		m_xOutputStream->
			writeBytes(uno::Sequence< sal_Int8 >(
				           static_cast< sal_Int8 const * >(pBuffer), nCount));
	}
	catch (io::IOException)
	{
		return ERRCODE_IO_CANTWRITE;
	}
	m_nPosition += nCount;
	if (pWritten)
		*pWritten = nCount;
	return ERRCODE_NONE;
}

//============================================================================
// virtual
sal_uLong SvOutputStreamOpenLockBytes::Tell() const
{
	return m_nPosition;
}

//============================================================================
// virtual
sal_uLong SvOutputStreamOpenLockBytes::Seek(sal_uLong)
{
	return m_nPosition;
}

//============================================================================
// virtual
void SvOutputStreamOpenLockBytes::Terminate()
{
	if (m_xOutputStream.is())
		try
		{
			m_xOutputStream->closeOutput();
		}
		catch (io::IOException) {}
}

//============================================================================
//
//  SvLockBytesInputStream
//
//============================================================================

// virtual
uno::Any SAL_CALL SvLockBytesInputStream::queryInterface(uno::Type const &
														     rType)
	throw (uno::RuntimeException)
{
	uno::Any
		aReturn(cppu::queryInterface(rType,
									 static_cast< io::XInputStream * >(this),
									 static_cast< io::XSeekable * >(this)));
	return aReturn.hasValue() ? aReturn : OWeakObject::queryInterface(rType);
}

//============================================================================
// virtual
void SAL_CALL SvLockBytesInputStream::acquire()	throw ()
{
	OWeakObject::acquire();
}

//============================================================================
// virtual
void SAL_CALL SvLockBytesInputStream::release() throw ()
{
	OWeakObject::release();
}

//============================================================================
// virtual
sal_Int32 SAL_CALL
SvLockBytesInputStream::readBytes(uno::Sequence< sal_Int8 > & rData,
								  sal_Int32 nBytesToRead)
	throw (io::IOException, uno::RuntimeException)
{
    OSL_ASSERT(m_nPosition >= 0);
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	if (
         nBytesToRead < 0 ||
         (
          static_cast<sal_uInt64>(m_nPosition) > SAL_MAX_SIZE &&
          nBytesToRead > 0
         )
       )
    {
		throw io::IOException();
    }
	rData.realloc(nBytesToRead);
	sal_Int32 nSize = 0;
	while (nSize < nBytesToRead)
	{
		sal_Size nCount;
		ErrCode nError = m_xLockBytes->ReadAt(static_cast<sal_Size>(
                                                  m_nPosition),
											  rData.getArray() + nSize,
											  nBytesToRead - nSize, &nCount);
		if (nError != ERRCODE_NONE && nError != ERRCODE_IO_PENDING)
			throw io::IOException();
		m_nPosition += nCount;
		nSize += nCount;
		if (nError == ERRCODE_NONE && nCount == 0)
			break;
	}
	rData.realloc(nSize);
	return nSize;
}

//============================================================================
// virtual
sal_Int32 SAL_CALL
SvLockBytesInputStream::readSomeBytes(uno::Sequence< sal_Int8 > & rData,
									  sal_Int32 nMaxBytesToRead)
	throw (io::IOException, uno::RuntimeException)
{
    OSL_ASSERT(m_nPosition >= 0);
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	if (static_cast<sal_uInt64>(m_nPosition) > SAL_MAX_SIZE
		&& nMaxBytesToRead > 0)
		throw io::IOException();
	rData.realloc(nMaxBytesToRead);
	sal_Size nCount = 0;
	if (nMaxBytesToRead > 0)
	{
		ErrCode nError;
		do
		{
			nError = m_xLockBytes->ReadAt(static_cast<sal_Size>(m_nPosition),
										  rData.getArray(),
										  nMaxBytesToRead < 0 ?
										      0 : nMaxBytesToRead,
										  &nCount);
			if (nError != ERRCODE_NONE && nError != ERRCODE_IO_PENDING)
				throw io::IOException();
			m_nPosition += nCount;
		}
		while (nCount == 0 && nError == ERRCODE_IO_PENDING);
	}
	rData.realloc(sal_Int32(nCount));
	return sal_Int32(nCount);
}

//============================================================================
// virtual
void SAL_CALL SvLockBytesInputStream::skipBytes(sal_Int32 nBytesToSkip)
	throw (io::IOException, uno::RuntimeException)
{
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	if (nBytesToSkip < 0)
		throw io::IOException();
	if (nBytesToSkip > SAL_MAX_INT64 - m_nPosition)
		throw io::BufferSizeExceededException();
	m_nPosition += nBytesToSkip;
}

//============================================================================
// virtual
sal_Int32 SAL_CALL SvLockBytesInputStream::available()
	throw (io::IOException, uno::RuntimeException)
{
    OSL_ASSERT(m_nPosition >= 0);
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	SvLockBytesStat aStat;
	if (m_xLockBytes->Stat(&aStat, SVSTATFLAG_DEFAULT) != ERRCODE_NONE)
		throw io::IOException();
	return aStat.nSize <= static_cast<sal_uInt64>(m_nPosition) ?
		       0 :
           static_cast<sal_Size>(aStat.nSize - m_nPosition) <=
                   static_cast<sal_uInt32>(SAL_MAX_INT32) ?
               static_cast<sal_Int32>(aStat.nSize - m_nPosition) :
               SAL_MAX_INT32;
}

//============================================================================
// virtual
void SAL_CALL SvLockBytesInputStream::closeInput()
	throw (io::IOException, uno::RuntimeException)
{
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	m_xLockBytes = 0;
}

//============================================================================
// virtual
void SAL_CALL SvLockBytesInputStream::seek(sal_Int64 nLocation)
	throw (lang::IllegalArgumentException, io::IOException,
		   uno::RuntimeException)
{
	if (nLocation < 0)
		throw lang::IllegalArgumentException();
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	m_nPosition = nLocation;
}

//============================================================================
// virtual
sal_Int64 SAL_CALL SvLockBytesInputStream::getPosition()
	throw (io::IOException, uno::RuntimeException)
{
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	return m_nPosition;
}

//============================================================================
// virtual
sal_Int64 SAL_CALL SvLockBytesInputStream::getLength()
	throw (io::IOException, uno::RuntimeException)
{
	if (!m_xLockBytes.Is())
		throw io::NotConnectedException();
	SvLockBytesStat aStat;
	if (m_xLockBytes->Stat(&aStat, SVSTATFLAG_DEFAULT) != ERRCODE_NONE)
		throw io::IOException();
#if SAL_TYPES_SIZEOFPOINTER > 4 // avoid warnings if sal_Size < sal_Int64
    if (aStat.nSize > static_cast<sal_uInt64>(SAL_MAX_INT64))
		throw io::IOException();
#endif
	return aStat.nSize;
}

//============================================================================
//
//  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
			= uno::Reference< io::XSeekable >(m_xStream, uno::UNO_QUERY);
		if (!m_xSeekable.is())
			m_pPipe = new SvDataPipe_Impl;
	}
	return true;
}

//============================================================================
// virtual
sal_uLong SvInputStream::GetData(void * pData, sal_uLong nSize)
{
	if (!open())
	{
		SetError(ERRCODE_IO_CANTREAD);
		return 0;
	}
	sal_uInt32 nRead = 0;
	if (m_xSeekable.is())
	{
		if (m_nSeekedFrom != STREAM_SEEK_TO_END)
		{
			try
			{
				m_xSeekable->seek(m_nSeekedFrom);
			}
			catch (io::IOException)
			{
				SetError(ERRCODE_IO_CANTREAD);
				return 0;
			}
			m_nSeekedFrom = STREAM_SEEK_TO_END;
		}
		for (;;)
		{
			sal_Int32 nRemain
				= sal_Int32(
					std::min(sal_uLong(nSize - nRead),
							 sal_uLong(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 (io::IOException)
			{
				SetError(ERRCODE_IO_CANTREAD);
				return nRead;
			}
			rtl_copyMemory(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(
							sal_uLong(nSize - nRead),
							sal_uLong(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 (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
sal_uLong SvInputStream::PutData(void const *, sal_uLong)
{
	SetError(ERRCODE_IO_NOTSUPPORTED);
	return 0;
}

//============================================================================
// virtual
void SvInputStream::FlushData()
{}

//============================================================================
// virtual
sal_uLong SvInputStream::SeekPos(sal_uLong nPos)
{
	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 (static_cast<sal_uInt64>(nLength)
                            < STREAM_SEEK_TO_END)
						{
							m_nSeekedFrom = Tell();
							return sal_uLong(nLength);
						}
					}
					catch (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 (io::IOException) {}
		else if (m_pPipe->setReadPosition(nPos) == SvDataPipe_Impl::SEEK_OK)
		{
			m_nSeekedFrom = STREAM_SEEK_TO_END;
			return nPos;
		}
	}
	SetError(ERRCODE_IO_CANTSEEK);
	return Tell();
}

//============================================================================
// virtual
void SvInputStream::SetSize(sal_uLong)
{
	SetError(ERRCODE_IO_NOTSUPPORTED);
}

//============================================================================
SvInputStream::SvInputStream(
		com::sun::star::uno::Reference< com::sun::star::io::XInputStream >
				const &
			rTheStream):
	m_xStream(rTheStream),
	m_pPipe(0),
	m_nSeekedFrom(STREAM_SEEK_TO_END)
{
	SetBufferSize(0);
}

//============================================================================
// virtual
SvInputStream::~SvInputStream()
{
	if (m_xStream.is())
		try
		{
			m_xStream->closeInput();
		}
		catch (io::IOException) {}
	delete m_pPipe;
}

//============================================================================
// virtual
sal_uInt16 SvInputStream::IsA() const
{
	return 0;
}

//============================================================================
// virtual
void SvInputStream::AddMark(sal_uLong nPos)
{
	if (open() && m_pPipe)
		m_pPipe->addMark(nPos);
}

//============================================================================
// virtual
void SvInputStream::RemoveMark(sal_uLong nPos)
{
	if (open() && m_pPipe)
		m_pPipe->removeMark(nPos);
}

//============================================================================
//
//  SvOutputStream
//
//============================================================================

// virtual
sal_uLong SvOutputStream::GetData(void *, sal_uLong)
{
	SetError(ERRCODE_IO_NOTSUPPORTED);
	return 0;
}

//============================================================================
// virtual
sal_uLong SvOutputStream::PutData(void const * pData, sal_uLong nSize)
{
	if (!m_xStream.is())
	{
		SetError(ERRCODE_IO_CANTWRITE);
		return 0;
	}
	sal_uLong nWritten = 0;
	for (;;)
	{
		sal_Int32 nRemain
			= sal_Int32(
				std::min(sal_uLong(nSize - nWritten),
						 sal_uLong(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 (io::IOException)
		{
			SetError(ERRCODE_IO_CANTWRITE);
			break;
		}
		nWritten += nRemain;
	}
	return nWritten;
}

//============================================================================
// virtual
sal_uLong SvOutputStream::SeekPos(sal_uLong)
{
	SetError(ERRCODE_IO_NOTSUPPORTED);
	return 0;
}

//============================================================================
// virtual
void SvOutputStream::FlushData()
{
	if (!m_xStream.is())
	{
		SetError(ERRCODE_IO_INVALIDDEVICE);
		return;
	}
	try
	{
		m_xStream->flush();
	}
	catch (io::IOException) {}
}

//============================================================================
// virtual
void SvOutputStream::SetSize(sal_uLong)
{
	SetError(ERRCODE_IO_NOTSUPPORTED);
}

//============================================================================
SvOutputStream::SvOutputStream(uno::Reference< io::XOutputStream > const &
							       rTheStream):
	m_xStream(rTheStream)
{
	SetBufferSize(0);
}

//============================================================================
// virtual
SvOutputStream::~SvOutputStream()
{
	if (m_xStream.is())
		try
		{
			m_xStream->closeOutput();
		}
		catch (io::IOException) {}
}

//============================================================================
// virtual
sal_uInt16 SvOutputStream::IsA() const
{
	return 0;
}

//============================================================================
//
//  SvDataPipe_Impl
//
//============================================================================

bool 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 false;
    }

	m_pFirstPage = m_pFirstPage->m_pNext;

	if (m_nPages <= m_nMinPages)
		return true;

	pPage->m_pPrev->m_pNext = pPage->m_pNext;
	pPage->m_pNext->m_pPrev = pPage->m_pPrev;
	rtl_freeMemory(pPage);
	--m_nPages;

	return true;
}

//============================================================================
SvDataPipe_Impl::~SvDataPipe_Impl()
{
	if (m_pFirstPage != 0)
		for (Page * pPage = m_pFirstPage;;)
		{
			Page * pNext = pPage->m_pNext;
			rtl_freeMemory(pPage);
			if (pNext == m_pFirstPage)
				break;
			pPage = pNext;
		}
}

//============================================================================
sal_uInt32 SvDataPipe_Impl::read()
{
	if (m_pReadBuffer == 0 || m_nReadBufferSize == 0 || m_pReadPage == 0)
		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);
		rtl_copyMemory(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;
}

//============================================================================
sal_uInt32 SvDataPipe_Impl::write(sal_Int8 const * pBuffer, sal_uInt32 nSize)
{
	if (nSize == 0)
		return 0;

	if (m_pWritePage == 0)
	{
		m_pFirstPage
			= static_cast< Page * >(rtl_allocateMemory(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 != 0 && 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)
		{
			rtl_copyMemory(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)
		for (;;)
		{
			sal_uInt32 nBlock
				= std::min(sal_uInt32(m_pWritePage->m_aBuffer + m_nPageSize
									      - m_pWritePage->m_pEnd),
						   nRemain);
			rtl_copyMemory(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 == m_nMaxPages)
					break;

				Page * pNew
					= static_cast< Page * >(rtl_allocateMemory(
						                        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;
		}

	return nSize - nRemain;
}

//============================================================================
bool SvDataPipe_Impl::addMark(sal_uInt32 nPosition)
{
	if (m_pFirstPage != 0 && m_pFirstPage->m_nOffset > nPosition)
		return false;
	m_aMarks.insert(nPosition);
	return true;
}

//============================================================================
bool SvDataPipe_Impl::removeMark(sal_uInt32 nPosition)
{
	std::multiset< sal_uInt32 >::iterator t = m_aMarks.find(nPosition);
	if (t == m_aMarks.end())
		return false;
	m_aMarks.erase(t);
	while (remove(m_pFirstPage)) ;
	return true;
}

//============================================================================
SvDataPipe_Impl::SeekResult SvDataPipe_Impl::setReadPosition(sal_uInt32
															     nPosition)
{
	if (m_pFirstPage == 0)
		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;
}

