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 
BiffOutputRecordBuffer(BinaryOutputStream & rOutStrm,sal_uInt16 nMaxRecSize)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 
startRecord(sal_uInt16 nRecId)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 
endRecord()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 
write(const void * pData,sal_uInt16 nBytes)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 
fill(sal_uInt8 nValue,sal_uInt16 nBytes)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 
BiffOutputStream(BinaryOutputStream & rOutStream,sal_uInt16 nMaxRecSize)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 
startRecord(sal_uInt16 nRecId)93 void BiffOutputStream::startRecord( sal_uInt16 nRecId )
94 {
95     maRecBuffer.startRecord( nRecId );
96     setPortionSize( 1 );
97 }
98 
endRecord()99 void BiffOutputStream::endRecord()
100 {
101     setPortionSize( 1 );
102     maRecBuffer.endRecord();
103 }
104 
setPortionSize(sal_uInt8 nSize)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 
tellBase() const114 sal_Int64 BiffOutputStream::tellBase() const
115 {
116     return maRecBuffer.getBaseStream().tell();
117 }
118 
sizeBase() const119 sal_Int64 BiffOutputStream::sizeBase() const
120 {
121     return maRecBuffer.getBaseStream().size();
122 }
123 
124 // BinaryOutputStream interface (stream write access) -------------------------
125 
writeData(const StreamDataSequence & rData,size_t nAtomSize)126 void BiffOutputStream::writeData( const StreamDataSequence& rData, size_t nAtomSize )
127 {
128     if( rData.hasElements() )
129         writeMemory( rData.getConstArray(), rData.getLength(), nAtomSize );
130 }
131 
writeMemory(const void * pMem,sal_Int32 nBytes,size_t nAtomSize)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 
fill(sal_uInt8 nValue,sal_Int32 nBytes,size_t nAtomSize)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 
prepareWriteBlock(sal_Int32 nTotalSize,size_t nAtomSize)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