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