1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include "oox/xls/biffoutputstream.hxx" 25 26 namespace oox { 27 namespace xls { 28 29 // ============================================================================ 30 31 namespace prv { 32 33 BiffOutputRecordBuffer::BiffOutputRecordBuffer( BinaryOutputStream& rOutStrm, sal_uInt16 nMaxRecSize ) : 34 mrOutStrm( rOutStrm ), 35 mnMaxRecSize( nMaxRecSize ), 36 mnRecId( BIFF_ID_UNKNOWN ), 37 mbInRec( false ) 38 { 39 OSL_ENSURE( mrOutStrm.isSeekable(), "BiffOutputRecordBuffer::BiffOutputRecordBuffer - stream must be seekable" ); 40 maData.reserve( SAL_MAX_UINT16 ); 41 } 42 43 void BiffOutputRecordBuffer::startRecord( sal_uInt16 nRecId ) 44 { 45 OSL_ENSURE( !mbInRec, "BiffOutputRecordBuffer::startRecord - another record still open" ); 46 mnRecId = nRecId; 47 maData.clear(); 48 mbInRec = true; 49 } 50 51 void BiffOutputRecordBuffer::endRecord() 52 { 53 OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::endRecord - no record open" ); 54 sal_uInt16 nRecSize = getLimitedValue< sal_uInt16, size_t >( maData.size(), 0, SAL_MAX_UINT16 ); 55 mrOutStrm.seekToEnd(); 56 mrOutStrm << mnRecId << nRecSize; 57 if( nRecSize > 0 ) 58 mrOutStrm.writeMemory( &maData.front(), nRecSize ); 59 mbInRec = false; 60 } 61 62 void BiffOutputRecordBuffer::write( const void* pData, sal_uInt16 nBytes ) 63 { 64 OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" ); 65 OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" ); 66 OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" ); 67 maData.resize( maData.size() + nBytes ); 68 memcpy( &*(maData.end() - nBytes), pData, nBytes ); 69 } 70 71 void BiffOutputRecordBuffer::fill( sal_uInt8 nValue, sal_uInt16 nBytes ) 72 { 73 OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" ); 74 OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" ); 75 OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" ); 76 maData.resize( maData.size() + nBytes, nValue ); 77 } 78 79 } // namespace prv 80 81 // ============================================================================ 82 83 BiffOutputStream::BiffOutputStream( BinaryOutputStream& rOutStream, sal_uInt16 nMaxRecSize ) : 84 BinaryStreamBase( true ), 85 maRecBuffer( rOutStream, nMaxRecSize ), 86 mnPortionSize( 0 ), 87 mnPortionPos( 0 ) 88 { 89 } 90 91 // record control ------------------------------------------------------------- 92 93 void BiffOutputStream::startRecord( sal_uInt16 nRecId ) 94 { 95 maRecBuffer.startRecord( nRecId ); 96 setPortionSize( 1 ); 97 } 98 99 void BiffOutputStream::endRecord() 100 { 101 setPortionSize( 1 ); 102 maRecBuffer.endRecord(); 103 } 104 105 void BiffOutputStream::setPortionSize( sal_uInt8 nSize ) 106 { 107 OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::setPortionSize - block operation inside portion" ); 108 mnPortionSize = ::std::max< sal_uInt8 >( nSize, 1 ); 109 mnPortionPos = 0; 110 } 111 112 // BinaryStreamBase interface (seeking) --------------------------------------- 113 114 sal_Int64 BiffOutputStream::tellBase() const 115 { 116 return maRecBuffer.getBaseStream().tell(); 117 } 118 119 sal_Int64 BiffOutputStream::sizeBase() const 120 { 121 return maRecBuffer.getBaseStream().size(); 122 } 123 124 // BinaryOutputStream interface (stream write access) ------------------------- 125 126 void BiffOutputStream::writeData( const StreamDataSequence& rData, size_t nAtomSize ) 127 { 128 if( rData.hasElements() ) 129 writeMemory( rData.getConstArray(), rData.getLength(), nAtomSize ); 130 } 131 132 void BiffOutputStream::writeMemory( const void* pMem, sal_Int32 nBytes, size_t nAtomSize ) 133 { 134 if( pMem && (nBytes > 0) ) 135 { 136 const sal_uInt8* pnBuffer = reinterpret_cast< const sal_uInt8* >( pMem ); 137 sal_Int32 nBytesLeft = nBytes; 138 while( nBytesLeft > 0 ) 139 { 140 sal_uInt16 nBlockSize = prepareWriteBlock( nBytesLeft, nAtomSize ); 141 maRecBuffer.write( pnBuffer, nBlockSize ); 142 pnBuffer += nBlockSize; 143 nBytesLeft -= nBlockSize; 144 } 145 } 146 } 147 148 void BiffOutputStream::fill( sal_uInt8 nValue, sal_Int32 nBytes, size_t nAtomSize ) 149 { 150 sal_Int32 nBytesLeft = nBytes; 151 while( nBytesLeft > 0 ) 152 { 153 sal_uInt16 nBlockSize = prepareWriteBlock( nBytesLeft, nAtomSize ); 154 maRecBuffer.fill( nValue, nBlockSize ); 155 nBytesLeft -= nBlockSize; 156 } 157 } 158 159 // private -------------------------------------------------------------------- 160 161 sal_uInt16 BiffOutputStream::prepareWriteBlock( sal_Int32 nTotalSize, size_t nAtomSize ) 162 { 163 sal_uInt16 nRecLeft = maRecBuffer.getRecLeft(); 164 if( mnPortionSize <= 1 ) 165 { 166 // no portions: restrict remaining record size to entire atoms 167 nRecLeft = static_cast< sal_uInt16 >( (nRecLeft / nAtomSize) * nAtomSize ); 168 } 169 else 170 { 171 sal_Int32 nPortionLeft = mnPortionSize - mnPortionPos; 172 if( nTotalSize <= nPortionLeft ) 173 { 174 // block fits into the current portion 175 OSL_ENSURE( nPortionLeft <= nRecLeft, "BiffOutputStream::prepareWriteBlock - portion exceeds record" ); 176 mnPortionPos = static_cast< sal_uInt8 >( (mnPortionPos + nTotalSize) % mnPortionSize ); 177 } 178 else 179 { 180 // restrict remaining record size to entire portions 181 OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::prepareWriteBlock - writing over multiple portions starts inside portion" ); 182 mnPortionPos = 0; 183 // check that atom size matches portion size 184 OSL_ENSURE( mnPortionSize % nAtomSize == 0, "BiffOutputStream::prepareWriteBlock - atom size does not match portion size" ); 185 sal_uInt8 nPortionSize = static_cast< sal_uInt8 >( (mnPortionSize / nAtomSize) * nAtomSize ); 186 // restrict remaining record size to entire portions 187 nRecLeft = (nRecLeft / nPortionSize) * nPortionSize; 188 } 189 } 190 191 // current record has space for some data: return size of available space 192 if( nRecLeft > 0 ) 193 return getLimitedValue< sal_uInt16, sal_Int32 >( nTotalSize, 0, nRecLeft ); 194 195 // finish current record and start a new CONTINUE record 196 maRecBuffer.endRecord(); 197 maRecBuffer.startRecord( BIFF_ID_CONT ); 198 mnPortionPos = 0; 199 return prepareWriteBlock( nTotalSize, nAtomSize ); 200 } 201 202 // ============================================================================ 203 204 } // namespace xls 205 } // namespace oox 206