blob: 26da8161850e29dceb43fa1d0574fa153c6a3259 [file] [log] [blame]
/**************************************************************
*
* 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.
*
*************************************************************/
#include "oox/xls/biffoutputstream.hxx"
namespace oox {
namespace xls {
// ============================================================================
namespace prv {
BiffOutputRecordBuffer::BiffOutputRecordBuffer( BinaryOutputStream& rOutStrm, sal_uInt16 nMaxRecSize ) :
mrOutStrm( rOutStrm ),
mnMaxRecSize( nMaxRecSize ),
mnRecId( BIFF_ID_UNKNOWN ),
mbInRec( false )
{
OSL_ENSURE( mrOutStrm.isSeekable(), "BiffOutputRecordBuffer::BiffOutputRecordBuffer - stream must be seekable" );
maData.reserve( SAL_MAX_UINT16 );
}
void BiffOutputRecordBuffer::startRecord( sal_uInt16 nRecId )
{
OSL_ENSURE( !mbInRec, "BiffOutputRecordBuffer::startRecord - another record still open" );
mnRecId = nRecId;
maData.clear();
mbInRec = true;
}
void BiffOutputRecordBuffer::endRecord()
{
OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::endRecord - no record open" );
sal_uInt16 nRecSize = getLimitedValue< sal_uInt16, size_t >( maData.size(), 0, SAL_MAX_UINT16 );
mrOutStrm.seekToEnd();
mrOutStrm << mnRecId << nRecSize;
if( nRecSize > 0 )
mrOutStrm.writeMemory( &maData.front(), nRecSize );
mbInRec = false;
}
void BiffOutputRecordBuffer::write( const void* pData, sal_uInt16 nBytes )
{
OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" );
OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" );
OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" );
maData.resize( maData.size() + nBytes );
memcpy( &*(maData.end() - nBytes), pData, nBytes );
}
void BiffOutputRecordBuffer::fill( sal_uInt8 nValue, sal_uInt16 nBytes )
{
OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" );
OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" );
OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" );
maData.resize( maData.size() + nBytes, nValue );
}
} // namespace prv
// ============================================================================
BiffOutputStream::BiffOutputStream( BinaryOutputStream& rOutStream, sal_uInt16 nMaxRecSize ) :
BinaryStreamBase( true ),
maRecBuffer( rOutStream, nMaxRecSize ),
mnPortionSize( 0 ),
mnPortionPos( 0 )
{
}
// record control -------------------------------------------------------------
void BiffOutputStream::startRecord( sal_uInt16 nRecId )
{
maRecBuffer.startRecord( nRecId );
setPortionSize( 1 );
}
void BiffOutputStream::endRecord()
{
setPortionSize( 1 );
maRecBuffer.endRecord();
}
void BiffOutputStream::setPortionSize( sal_uInt8 nSize )
{
OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::setPortionSize - block operation inside portion" );
mnPortionSize = ::std::max< sal_uInt8 >( nSize, 1 );
mnPortionPos = 0;
}
// BinaryStreamBase interface (seeking) ---------------------------------------
sal_Int64 BiffOutputStream::tellBase() const
{
return maRecBuffer.getBaseStream().tell();
}
sal_Int64 BiffOutputStream::sizeBase() const
{
return maRecBuffer.getBaseStream().size();
}
// BinaryOutputStream interface (stream write access) -------------------------
void BiffOutputStream::writeData( const StreamDataSequence& rData, size_t nAtomSize )
{
if( rData.hasElements() )
writeMemory( rData.getConstArray(), rData.getLength(), nAtomSize );
}
void BiffOutputStream::writeMemory( const void* pMem, sal_Int32 nBytes, size_t nAtomSize )
{
if( pMem && (nBytes > 0) )
{
const sal_uInt8* pnBuffer = reinterpret_cast< const sal_uInt8* >( pMem );
sal_Int32 nBytesLeft = nBytes;
while( nBytesLeft > 0 )
{
sal_uInt16 nBlockSize = prepareWriteBlock( nBytesLeft, nAtomSize );
maRecBuffer.write( pnBuffer, nBlockSize );
pnBuffer += nBlockSize;
nBytesLeft -= nBlockSize;
}
}
}
void BiffOutputStream::fill( sal_uInt8 nValue, sal_Int32 nBytes, size_t nAtomSize )
{
sal_Int32 nBytesLeft = nBytes;
while( nBytesLeft > 0 )
{
sal_uInt16 nBlockSize = prepareWriteBlock( nBytesLeft, nAtomSize );
maRecBuffer.fill( nValue, nBlockSize );
nBytesLeft -= nBlockSize;
}
}
// private --------------------------------------------------------------------
sal_uInt16 BiffOutputStream::prepareWriteBlock( sal_Int32 nTotalSize, size_t nAtomSize )
{
sal_uInt16 nRecLeft = maRecBuffer.getRecLeft();
if( mnPortionSize <= 1 )
{
// no portions: restrict remaining record size to entire atoms
nRecLeft = static_cast< sal_uInt16 >( (nRecLeft / nAtomSize) * nAtomSize );
}
else
{
sal_Int32 nPortionLeft = mnPortionSize - mnPortionPos;
if( nTotalSize <= nPortionLeft )
{
// block fits into the current portion
OSL_ENSURE( nPortionLeft <= nRecLeft, "BiffOutputStream::prepareWriteBlock - portion exceeds record" );
mnPortionPos = static_cast< sal_uInt8 >( (mnPortionPos + nTotalSize) % mnPortionSize );
}
else
{
// restrict remaining record size to entire portions
OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::prepareWriteBlock - writing over multiple portions starts inside portion" );
mnPortionPos = 0;
// check that atom size matches portion size
OSL_ENSURE( mnPortionSize % nAtomSize == 0, "BiffOutputStream::prepareWriteBlock - atom size does not match portion size" );
sal_uInt8 nPortionSize = static_cast< sal_uInt8 >( (mnPortionSize / nAtomSize) * nAtomSize );
// restrict remaining record size to entire portions
nRecLeft = (nRecLeft / nPortionSize) * nPortionSize;
}
}
// current record has space for some data: return size of available space
if( nRecLeft > 0 )
return getLimitedValue< sal_uInt16, sal_Int32 >( nTotalSize, 0, nRecLeft );
// finish current record and start a new CONTINUE record
maRecBuffer.endRecord();
maRecBuffer.startRecord( BIFF_ID_CONT );
mnPortionPos = 0;
return prepareWriteBlock( nTotalSize, nAtomSize );
}
// ============================================================================
} // namespace xls
} // namespace oox