1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "oox/xls/biffoutputstream.hxx"
29 
30 namespace oox {
31 namespace xls {
32 
33 // ============================================================================
34 
35 namespace prv {
36 
37 BiffOutputRecordBuffer::BiffOutputRecordBuffer( BinaryOutputStream& rOutStrm, sal_uInt16 nMaxRecSize ) :
38     mrOutStrm( rOutStrm ),
39     mnMaxRecSize( nMaxRecSize ),
40     mnRecId( BIFF_ID_UNKNOWN ),
41     mbInRec( false )
42 {
43     OSL_ENSURE( mrOutStrm.isSeekable(), "BiffOutputRecordBuffer::BiffOutputRecordBuffer - stream must be seekable" );
44     maData.reserve( SAL_MAX_UINT16 );
45 }
46 
47 void BiffOutputRecordBuffer::startRecord( sal_uInt16 nRecId )
48 {
49     OSL_ENSURE( !mbInRec, "BiffOutputRecordBuffer::startRecord - another record still open" );
50     mnRecId = nRecId;
51     maData.clear();
52     mbInRec = true;
53 }
54 
55 void BiffOutputRecordBuffer::endRecord()
56 {
57     OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::endRecord - no record open" );
58     sal_uInt16 nRecSize = getLimitedValue< sal_uInt16, size_t >( maData.size(), 0, SAL_MAX_UINT16 );
59     mrOutStrm.seekToEnd();
60     mrOutStrm << mnRecId << nRecSize;
61     if( nRecSize > 0 )
62         mrOutStrm.writeMemory( &maData.front(), nRecSize );
63     mbInRec = false;
64 }
65 
66 void BiffOutputRecordBuffer::write( const void* pData, sal_uInt16 nBytes )
67 {
68     OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" );
69     OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" );
70     OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" );
71     maData.resize( maData.size() + nBytes );
72     memcpy( &*(maData.end() - nBytes), pData, nBytes );
73 }
74 
75 void BiffOutputRecordBuffer::fill( sal_uInt8 nValue, sal_uInt16 nBytes )
76 {
77     OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" );
78     OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" );
79     OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" );
80     maData.resize( maData.size() + nBytes, nValue );
81 }
82 
83 } // namespace prv
84 
85 // ============================================================================
86 
87 BiffOutputStream::BiffOutputStream( BinaryOutputStream& rOutStream, sal_uInt16 nMaxRecSize ) :
88     BinaryStreamBase( true ),
89     maRecBuffer( rOutStream, nMaxRecSize ),
90     mnPortionSize( 0 ),
91     mnPortionPos( 0 )
92 {
93 }
94 
95 // record control -------------------------------------------------------------
96 
97 void BiffOutputStream::startRecord( sal_uInt16 nRecId )
98 {
99     maRecBuffer.startRecord( nRecId );
100     setPortionSize( 1 );
101 }
102 
103 void BiffOutputStream::endRecord()
104 {
105     setPortionSize( 1 );
106     maRecBuffer.endRecord();
107 }
108 
109 void BiffOutputStream::setPortionSize( sal_uInt8 nSize )
110 {
111     OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::setPortionSize - block operation inside portion" );
112     mnPortionSize = ::std::max< sal_uInt8 >( nSize, 1 );
113     mnPortionPos = 0;
114 }
115 
116 // BinaryStreamBase interface (seeking) ---------------------------------------
117 
118 sal_Int64 BiffOutputStream::tellBase() const
119 {
120     return maRecBuffer.getBaseStream().tell();
121 }
122 
123 sal_Int64 BiffOutputStream::sizeBase() const
124 {
125     return maRecBuffer.getBaseStream().size();
126 }
127 
128 // BinaryOutputStream interface (stream write access) -------------------------
129 
130 void BiffOutputStream::writeData( const StreamDataSequence& rData, size_t nAtomSize )
131 {
132     if( rData.hasElements() )
133         writeMemory( rData.getConstArray(), rData.getLength(), nAtomSize );
134 }
135 
136 void BiffOutputStream::writeMemory( const void* pMem, sal_Int32 nBytes, size_t nAtomSize )
137 {
138     if( pMem && (nBytes > 0) )
139     {
140         const sal_uInt8* pnBuffer = reinterpret_cast< const sal_uInt8* >( pMem );
141         sal_Int32 nBytesLeft = nBytes;
142         while( nBytesLeft > 0 )
143         {
144             sal_uInt16 nBlockSize = prepareWriteBlock( nBytesLeft, nAtomSize );
145             maRecBuffer.write( pnBuffer, nBlockSize );
146             pnBuffer += nBlockSize;
147             nBytesLeft -= nBlockSize;
148         }
149     }
150 }
151 
152 void BiffOutputStream::fill( sal_uInt8 nValue, sal_Int32 nBytes, size_t nAtomSize )
153 {
154     sal_Int32 nBytesLeft = nBytes;
155     while( nBytesLeft > 0 )
156     {
157         sal_uInt16 nBlockSize = prepareWriteBlock( nBytesLeft, nAtomSize );
158         maRecBuffer.fill( nValue, nBlockSize );
159         nBytesLeft -= nBlockSize;
160     }
161 }
162 
163 // private --------------------------------------------------------------------
164 
165 sal_uInt16 BiffOutputStream::prepareWriteBlock( sal_Int32 nTotalSize, size_t nAtomSize )
166 {
167     sal_uInt16 nRecLeft = maRecBuffer.getRecLeft();
168     if( mnPortionSize <= 1 )
169     {
170         // no portions: restrict remaining record size to entire atoms
171         nRecLeft = static_cast< sal_uInt16 >( (nRecLeft / nAtomSize) * nAtomSize );
172     }
173     else
174     {
175         sal_Int32 nPortionLeft = mnPortionSize - mnPortionPos;
176         if( nTotalSize <= nPortionLeft )
177         {
178             // block fits into the current portion
179             OSL_ENSURE( nPortionLeft <= nRecLeft, "BiffOutputStream::prepareWriteBlock - portion exceeds record" );
180             mnPortionPos = static_cast< sal_uInt8 >( (mnPortionPos + nTotalSize) % mnPortionSize );
181         }
182         else
183         {
184             // restrict remaining record size to entire portions
185             OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::prepareWriteBlock - writing over multiple portions starts inside portion" );
186             mnPortionPos = 0;
187             // check that atom size matches portion size
188             OSL_ENSURE( mnPortionSize % nAtomSize == 0, "BiffOutputStream::prepareWriteBlock - atom size does not match portion size" );
189             sal_uInt8 nPortionSize = static_cast< sal_uInt8 >( (mnPortionSize / nAtomSize) * nAtomSize );
190             // restrict remaining record size to entire portions
191             nRecLeft = (nRecLeft / nPortionSize) * nPortionSize;
192         }
193     }
194 
195     // current record has space for some data: return size of available space
196     if( nRecLeft > 0 )
197         return getLimitedValue< sal_uInt16, sal_Int32 >( nTotalSize, 0, nRecLeft );
198 
199     // finish current record and start a new CONTINUE record
200     maRecBuffer.endRecord();
201     maRecBuffer.startRecord( BIFF_ID_CONT );
202     mnPortionPos = 0;
203     return prepareWriteBlock( nTotalSize, nAtomSize );
204 }
205 
206 // ============================================================================
207 
208 } // namespace xls
209 } // namespace oox
210