LibreOffice Module oox (master) 1
vbainputstream.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
21#include <osl/diagnose.h>
22
23namespace oox::ole {
24
25namespace {
26
27const sal_uInt8 VBASTREAM_SIGNATURE = 1;
28
29const sal_uInt16 VBACHUNK_SIGMASK = 0x7000;
30const sal_uInt16 VBACHUNK_SIG = 0x3000;
31const sal_uInt16 VBACHUNK_COMPRESSED = 0x8000;
32const sal_uInt16 VBACHUNK_LENMASK = 0x0FFF;
33
34} // namespace
35
37 BinaryStreamBase( false ),
38 mpInStrm( &rInStrm ),
39 mnChunkPos( 0 )
40{
41 maChunk.reserve( 4096 );
42
43 sal_uInt8 nSig = rInStrm.readuInt8();
44 OSL_ENSURE( nSig == VBASTREAM_SIGNATURE, "VbaInputStream::VbaInputStream - wrong signature" );
45 mbEof = mbEof || rInStrm.isEof() || (nSig != VBASTREAM_SIGNATURE);
46}
47
48sal_Int64 VbaInputStream::size() const
49{
50 return -1;
51}
52
53sal_Int64 VbaInputStream::tell() const
54{
55 return -1;
56}
57
58void VbaInputStream::seek( sal_Int64 )
59{
60}
61
63{
64 mpInStrm = nullptr;
65 mbEof = true;
66}
67
68sal_Int32 VbaInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize )
69{
70 sal_Int32 nRet = 0;
71 if( !mbEof )
72 {
73 orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) );
74 if( nBytes > 0 )
75 {
76 nRet = readMemory( orData.getArray(), nBytes, nAtomSize );
77 if( nRet < nBytes )
78 orData.realloc( nRet );
79 }
80 }
81 return nRet;
82}
83
84sal_Int32 VbaInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t /*nAtomSize*/ )
85{
86 sal_Int32 nRet = 0;
87 sal_uInt8* opnMem = static_cast< sal_uInt8* >( opMem );
88 while( (nBytes > 0) && updateChunk() )
89 {
90 sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos );
91 sal_Int32 nReadBytes = ::std::min( nBytes, nChunkLeft );
92 memcpy( opnMem, &*(maChunk.begin() + mnChunkPos), nReadBytes );
93 opnMem += nReadBytes;
94 mnChunkPos += static_cast< size_t >( nReadBytes );
95 nBytes -= nReadBytes;
96 nRet += nReadBytes;
97 }
98 return nRet;
99}
100
101void VbaInputStream::skip( sal_Int32 nBytes, size_t /*nAtomSize*/ )
102{
103 while( (nBytes > 0) && updateChunk() )
104 {
105 sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos );
106 sal_Int32 nSkipBytes = ::std::min( nBytes, nChunkLeft );
107 mnChunkPos += static_cast< size_t >( nSkipBytes );
108 nBytes -= nSkipBytes;
109 }
110}
111
112// private --------------------------------------------------------------------
113
115{
116 if( mbEof || (mnChunkPos < maChunk.size()) ) return !mbEof;
117 // try to read next chunk header, this may trigger EOF
118 sal_uInt16 nHeader = mpInStrm->readuInt16();
119
120 mbEof = mpInStrm->isEof();
121 if( mbEof ) return false;
122
123 // check header signature
124 bool bIgnoreBrokenSig = ( (nHeader & VBACHUNK_SIGMASK) != VBACHUNK_SIG );
125
126 // decode length of chunk data and compression flag
127 bool bCompressed = getFlag( nHeader, VBACHUNK_COMPRESSED );
128 sal_uInt16 nChunkLen = (nHeader & VBACHUNK_LENMASK) + 1;
129 OSL_ENSURE( bCompressed || (nChunkLen == 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" );
130
131 // From the amazing bit detective work of Valek Filippov<frob@gnome.org>
132 // this tweak and the one at the bottom of the method to seek to the
133 // start of the next chunk we can read those strange broken
134 // ( I guess from a MSO bug ) compressed streams > 4k
135
136 if ( bIgnoreBrokenSig )
137 {
138 bCompressed = true;
139 nChunkLen = 4094;
140 }
141
142 sal_Int64 target = mpInStrm->tell() + nChunkLen;
143 if( bCompressed )
144 {
145 maChunk.clear();
147 sal_uInt16 nChunkPos = 0;
148 while( !mbEof && !mpInStrm->isEof() && (nChunkPos < nChunkLen) )
149 {
150 sal_uInt8 nTokenFlags = mpInStrm->readuInt8();
151 ++nChunkPos;
152 for( int nBit = 0; !mbEof && !mpInStrm->isEof() && (nBit < 8) && (nChunkPos < nChunkLen); ++nBit, nTokenFlags >>= 1 )
153 {
154 if( nTokenFlags & 1 )
155 {
156 sal_uInt16 nCopyToken = mpInStrm->readuInt16();
157 nChunkPos = nChunkPos + 2;
158 // update bit count used for offset/length in the token
159 while( ( static_cast<size_t>(1) << nBitCount ) < maChunk.size() ) ++nBitCount;
160 // extract length from lower (16-nBitCount) bits, plus 3
161 sal_uInt16 nLength = extractValue< sal_uInt16 >( nCopyToken, 0, 16 - nBitCount ) + 3;
162 // extract offset from high nBitCount bits, plus 1
163 sal_uInt16 nOffset = extractValue< sal_uInt16 >( nCopyToken, 16 - nBitCount, nBitCount ) + 1;
164 mbEof = (nOffset > maChunk.size()) || (maChunk.size() + nLength > 4096);
165 OSL_ENSURE( !mbEof, "VbaInputStream::updateChunk - invalid offset or size in copy token" );
166 if( !mbEof )
167 {
168 // append data to buffer
169 maChunk.resize( maChunk.size() + nLength );
170 sal_uInt8* pnTo = &*(maChunk.end() - nLength);
171 const sal_uInt8* pnEnd = pnTo + nLength;
172 const sal_uInt8* pnFrom = pnTo - nOffset;
173 // offset may be less than length, effectively duplicating source data several times
174 size_t nRunLen = ::std::min< size_t >( nLength, nOffset );
175 while( pnTo < pnEnd )
176 {
177 size_t nStepLen = ::std::min< size_t >( nRunLen, pnEnd - pnTo );
178 memcpy( pnTo, pnFrom, nStepLen );
179 pnTo += nStepLen;
180 }
181 }
182 }
183 // we suspect this will never be called
184 else
185 {
186 maChunk.emplace_back();
187 maChunk.back() = mpInStrm->readuChar();
188 ++nChunkPos;
189 }
190 }
191 }
192 }
193 else
194 {
195 maChunk.resize( nChunkLen );
196 mpInStrm->readMemory(maChunk.data(), nChunkLen);
197 }
198 // decompression sometimes leaves the stream pos offset 1 place ( at
199 // least ) past or before the expected stream pos.
200 // here we make sure we are on the chunk boundary
201 mpInStrm->seek( target );
202 mnChunkPos = 0;
203 return !mbEof;
204}
205
206} // namespace oox::ole
207
208/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Interface for binary input stream classes.
virtual sal_Int32 readMemory(void *opMem, sal_Int32 nBytes, size_t nAtomSize=1)=0
Derived classes implement reading nBytes bytes to the (preallocated!) memory buffer opMem.
Base class for binary stream classes.
bool isEof() const
Returns true, if the stream position is invalid (EOF).
virtual sal_Int64 tell() const =0
Implementations return the current stream position, if possible.
virtual void seek(sal_Int64 nPos)=0
Implementations seek the stream to the passed position, if the stream is seekable.
bool mbEof
End of stream flag.
VbaInputStream(BinaryInputStream &rInStrm)
virtual sal_Int64 tell() const override
Returns -1, stream position is not tracked.
virtual void close() override
Closes the input stream but not the wrapped stream.
virtual void seek(sal_Int64 nPos) override
Does nothing, stream is not seekable.
virtual sal_Int32 readMemory(void *opMem, sal_Int32 nBytes, size_t nAtomSize=1) override
Reads nBytes bytes to the (existing) buffer opMem.
virtual sal_Int64 size() const override
Returns -1, stream size is not determinable.
BinaryInputStream * mpInStrm
virtual sal_Int32 readData(StreamDataSequence &orData, sal_Int32 nBytes, size_t nAtomSize=1) override
Reads nBytes bytes to the passed sequence.
virtual void skip(sal_Int32 nBytes, size_t nAtomSize=1) override
Seeks the stream forward by the passed number of bytes.
bool updateChunk()
If no data left in chunk buffer, reads the next chunk from stream.
short nBitCount
sal_Int16 nBit
css::uno::Sequence< sal_Int8 > StreamDataSequence
bool getFlag(Type nBitField, Type nMask)
Returns true, if at least one of the bits set in nMask is set in nBitField.
Definition: helper.hxx:137
unsigned char sal_uInt8
sal_Int32 nLength