| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "oox/ole/vbainputstream.hxx" |
| #include <osl/diagnose.h> |
| |
| namespace oox { |
| namespace ole { |
| |
| // ============================================================================ |
| |
| namespace { |
| |
| const sal_uInt8 VBASTREAM_SIGNATURE = 1; |
| |
| const sal_uInt16 VBACHUNK_SIGMASK = 0x7000; |
| const sal_uInt16 VBACHUNK_SIG = 0x3000; |
| const sal_uInt16 VBACHUNK_COMPRESSED = 0x8000; |
| const sal_uInt16 VBACHUNK_LENMASK = 0x0FFF; |
| |
| } // namespace |
| |
| // ============================================================================ |
| |
| VbaInputStream::VbaInputStream( BinaryInputStream& rInStrm ) : |
| BinaryStreamBase( false ), |
| mpInStrm( &rInStrm ), |
| mnChunkPos( 0 ) |
| { |
| maChunk.reserve( 4096 ); |
| |
| sal_uInt8 nSig = rInStrm.readuInt8(); |
| OSL_ENSURE( nSig == VBASTREAM_SIGNATURE, "VbaInputStream::VbaInputStream - wrong signature" ); |
| mbEof = mbEof || rInStrm.isEof() || (nSig != VBASTREAM_SIGNATURE); |
| } |
| |
| sal_Int64 VbaInputStream::size() const |
| { |
| return -1; |
| } |
| |
| sal_Int64 VbaInputStream::tell() const |
| { |
| return -1; |
| } |
| |
| void VbaInputStream::seek( sal_Int64 ) |
| { |
| } |
| |
| void VbaInputStream::close() |
| { |
| mpInStrm = 0; |
| mbEof = true; |
| } |
| |
| sal_Int32 VbaInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize ) |
| { |
| sal_Int32 nRet = 0; |
| if( !mbEof ) |
| { |
| orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) ); |
| if( nBytes > 0 ) |
| { |
| nRet = readMemory( orData.getArray(), nBytes, nAtomSize ); |
| if( nRet < nBytes ) |
| orData.realloc( nRet ); |
| } |
| } |
| return nRet; |
| } |
| |
| sal_Int32 VbaInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t /*nAtomSize*/ ) |
| { |
| sal_Int32 nRet = 0; |
| sal_uInt8* opnMem = reinterpret_cast< sal_uInt8* >( opMem ); |
| while( (nBytes > 0) && updateChunk() ) |
| { |
| sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); |
| sal_Int32 nReadBytes = ::std::min( nBytes, nChunkLeft ); |
| memcpy( opnMem, &*(maChunk.begin() + mnChunkPos), nReadBytes ); |
| opnMem += nReadBytes; |
| mnChunkPos += static_cast< size_t >( nReadBytes ); |
| nBytes -= nReadBytes; |
| nRet += nReadBytes; |
| } |
| return nRet; |
| } |
| |
| void VbaInputStream::skip( sal_Int32 nBytes, size_t /*nAtomSize*/ ) |
| { |
| while( (nBytes > 0) && updateChunk() ) |
| { |
| sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); |
| sal_Int32 nSkipBytes = ::std::min( nBytes, nChunkLeft ); |
| mnChunkPos += static_cast< size_t >( nSkipBytes ); |
| nBytes -= nSkipBytes; |
| } |
| } |
| |
| // private -------------------------------------------------------------------- |
| |
| bool VbaInputStream::updateChunk() |
| { |
| if( mbEof || (mnChunkPos < maChunk.size()) ) return !mbEof; |
| |
| // try to read next chunk header, this may trigger EOF |
| sal_uInt16 nHeader = mpInStrm->readuInt16(); |
| mbEof = mpInStrm->isEof(); |
| if( mbEof ) return false; |
| |
| // check header signature |
| OSL_ENSURE( (nHeader & VBACHUNK_SIGMASK) == VBACHUNK_SIG, "VbaInputStream::updateChunk - invalid chunk signature" ); |
| mbEof = (nHeader & VBACHUNK_SIGMASK) != VBACHUNK_SIG; |
| if( mbEof ) return false; |
| |
| // decode length of chunk data and compression flag |
| bool bCompressed = getFlag( nHeader, VBACHUNK_COMPRESSED ); |
| sal_uInt16 nChunkLen = (nHeader & VBACHUNK_LENMASK) + 1; |
| OSL_ENSURE( bCompressed || (nChunkLen == 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" ); |
| if( bCompressed ) |
| { |
| maChunk.clear(); |
| sal_uInt8 nBitCount = 4; |
| sal_uInt16 nChunkPos = 0; |
| while( !mbEof && !mpInStrm->isEof() && (nChunkPos < nChunkLen) ) |
| { |
| sal_uInt8 nTokenFlags = mpInStrm->readuInt8(); |
| ++nChunkPos; |
| for( int nBit = 0; !mbEof && !mpInStrm->isEof() && (nBit < 8) && (nChunkPos < nChunkLen); ++nBit, nTokenFlags >>= 1 ) |
| { |
| if( nTokenFlags & 1 ) |
| { |
| sal_uInt16 nCopyToken = mpInStrm->readuInt16(); |
| nChunkPos = nChunkPos + 2; |
| // update bit count used for offset/length in the token |
| while( static_cast< size_t >( 1 << nBitCount ) < maChunk.size() ) ++nBitCount; |
| // extract length from lower (16-nBitCount) bits, plus 3 |
| sal_uInt16 nLength = extractValue< sal_uInt16 >( nCopyToken, 0, 16 - nBitCount ) + 3; |
| // extract offset from high nBitCount bits, plus 1 |
| sal_uInt16 nOffset = extractValue< sal_uInt16 >( nCopyToken, 16 - nBitCount, nBitCount ) + 1; |
| mbEof = (nOffset > maChunk.size()) || (maChunk.size() + nLength > 4096); |
| OSL_ENSURE( !mbEof, "VbaInputStream::updateChunk - invalid offset or size in copy token" ); |
| if( !mbEof ) |
| { |
| // append data to buffer |
| maChunk.resize( maChunk.size() + nLength ); |
| sal_uInt8* pnTo = &*(maChunk.end() - nLength); |
| const sal_uInt8* pnEnd = pnTo + nLength; |
| const sal_uInt8* pnFrom = pnTo - nOffset; |
| // offset may be less than length, effectively duplicating source data several times |
| size_t nRunLen = ::std::min< size_t >( nLength, nOffset ); |
| while( pnTo < pnEnd ) |
| { |
| size_t nStepLen = ::std::min< size_t >( nRunLen, pnEnd - pnTo ); |
| memcpy( pnTo, pnFrom, nStepLen ); |
| pnTo += nStepLen; |
| } |
| } |
| } |
| else |
| { |
| maChunk.resize( maChunk.size() + 1 ); |
| *mpInStrm >> maChunk.back(); |
| ++nChunkPos; |
| } |
| } |
| } |
| } |
| else |
| { |
| maChunk.resize( nChunkLen ); |
| mpInStrm->readMemory( &maChunk.front(), nChunkLen ); |
| } |
| |
| mnChunkPos = 0; |
| return !mbEof; |
| } |
| |
| // ============================================================================ |
| |
| } // namespace ole |
| } // namespace oox |
| |