diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /tools/source/stream/stream.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/source/stream/stream.cxx')
-rw-r--r-- | tools/source/stream/stream.cxx | 2012 |
1 files changed, 2012 insertions, 0 deletions
diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx new file mode 100644 index 000000000..bb84dd468 --- /dev/null +++ b/tools/source/stream/stream.cxx @@ -0,0 +1,2012 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +// TODO: Read->RefreshBuffer-> React to changes from m_nBufActualLen + +#include <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <memory> + +#include <string.h> + +#include <o3tl/safeint.hxx> +#include <osl/endian.h> +#include <osl/diagnose.h> +#include <rtl/strbuf.hxx> +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <tools/long.hxx> + +#include <comphelper/fileformat.h> +#include <comphelper/fileurl.hxx> + +static void swapNibbles(unsigned char &c) +{ + unsigned char nSwapTmp=c; + nSwapTmp <<= 4; + c >>= 4; + c |= nSwapTmp; +} + +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <osl/thread.h> +#include <algorithm> + +// !!! Do not inline if already the operators <<,>> are inline +template <typename T, std::enable_if_t<std::is_integral_v<T> && sizeof(T) == 2, int> = 0> +static void SwapNumber(T& r) + { r = OSL_SWAPWORD(r); } +template <typename T, std::enable_if_t<std::is_integral_v<T> && sizeof(T) == 4, int> = 0> +static void SwapNumber(T& r) + { r = OSL_SWAPDWORD(r); } +template <typename T, std::enable_if_t<std::is_integral_v<T> && sizeof(T) == 8, int> = 0> +static void SwapNumber(T& r) + { + union + { + T n; + sal_uInt32 c[2]; + } s; + + s.n = r; + std::swap(s.c[0], s.c[1]); // swap the 32 bit words + // swap the bytes in the words + s.c[0] = OSL_SWAPDWORD(s.c[0]); + s.c[1] = OSL_SWAPDWORD(s.c[1]); + r = s.n; + } + +#ifdef UNX +static void SwapFloat( float& r ) + { + union + { + float f; + sal_uInt32 c; + } s; + + s.f = r; + s.c = OSL_SWAPDWORD( s.c ); + r = s.f; + } + +static void SwapDouble( double& r ) + { + if( sizeof(double) != 8 ) + { + SAL_WARN( "tools.stream", "Can only swap 8-Byte-doubles" ); + } + else + { + union + { + double d; + sal_uInt32 c[2]; + } s; + + s.d = r; + s.c[0] ^= s.c[1]; // swap 32-bit values in situ + s.c[1] ^= s.c[0]; + s.c[0] ^= s.c[1]; + s.c[0] = OSL_SWAPDWORD(s.c[0]); // swap dword itself in situ + s.c[1] = OSL_SWAPDWORD(s.c[1]); + r = s.d; + } + } +#endif + +//SDO + +void SvStream::readNumberWithoutSwap_(void * pDataDest, int nDataSize) +{ + if (m_isIoRead && nDataSize <= m_nBufFree) + { + for (int i = 0; i < nDataSize; i++) + static_cast<char*>(pDataDest)[i] = m_pBufPos[i]; + m_nBufActualPos += nDataSize; + m_pBufPos += nDataSize; + m_nBufFree -= nDataSize; + } + else + { + ReadBytes( pDataDest, nDataSize ); + } +} + + +void SvStream::writeNumberWithoutSwap_(const void * pDataSrc, int nDataSize) +{ + if (m_isIoWrite && nDataSize <= m_nBufFree) + { + for (int i = 0; i < nDataSize; i++) + m_pBufPos[i] = static_cast<const char*>(pDataSrc)[i]; + m_nBufFree -= nDataSize; + m_nBufActualPos += nDataSize; + if (m_nBufActualPos > m_nBufActualLen) + m_nBufActualLen = m_nBufActualPos; + m_pBufPos += nDataSize; + m_isDirty = true; + } + else + { + WriteBytes( pDataSrc, nDataSize ); + } +} + + +void SvLockBytes::close() +{ + if (m_bOwner) + delete m_pStream; + m_pStream = nullptr; +} + + +// virtual +ErrCode SvLockBytes::ReadAt(sal_uInt64 const nPos, void * pBuffer, std::size_t nCount, + std::size_t * pRead) const +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::ReadAt(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Seek(nPos); + std::size_t nTheRead = m_pStream->ReadBytes(pBuffer, nCount); + if (pRead) + *pRead = nTheRead; + return m_pStream->GetErrorCode(); +} + +// virtual +ErrCode SvLockBytes::WriteAt(sal_uInt64 const nPos, const void * pBuffer, std::size_t nCount, + std::size_t * pWritten) +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::WriteAt(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Seek(nPos); + std::size_t nTheWritten = m_pStream->WriteBytes(pBuffer, nCount); + if (pWritten) + *pWritten = nTheWritten; + return m_pStream->GetErrorCode(); +} + +// virtual +ErrCode SvLockBytes::Flush() const +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::Flush(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Flush(); + return m_pStream->GetErrorCode(); +} + +// virtual +ErrCode SvLockBytes::SetSize(sal_uInt64 const nSize) +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::SetSize(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->SetStreamSize(nSize); + return m_pStream->GetErrorCode(); +} + +ErrCode SvLockBytes::Stat(SvLockBytesStat * pStat) const +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::Stat(): Bad stream"); + return ERRCODE_NONE; + } + + if (pStat) + pStat->nSize = m_pStream->TellEnd(); + return ERRCODE_NONE; +} + + +std::size_t SvStream::GetData( void* pData, std::size_t nSize ) +{ + if( !GetError() ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + std::size_t nRet(0); + m_nError = m_xLockBytes->ReadAt(m_nActPos, pData, nSize, &nRet); + m_nActPos += nRet; + return nRet; + } + else return 0; +} + +std::size_t SvStream::PutData( const void* pData, std::size_t nSize ) +{ + if( !GetError() ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + std::size_t nRet(0); + m_nError = m_xLockBytes->WriteAt(m_nActPos, pData, nSize, &nRet); + m_nActPos += nRet; + return nRet; + } + else return 0; +} + +sal_uInt64 SvStream::SeekPos(sal_uInt64 const nPos) +{ + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nPos != SAL_MAX_UINT32); + if( !GetError() && nPos == STREAM_SEEK_TO_END ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + SvLockBytesStat aStat; + m_xLockBytes->Stat( &aStat ); + m_nActPos = aStat.nSize; + } + else + m_nActPos = nPos; + return m_nActPos; +} + +void SvStream::FlushData() +{ + if( !GetError() ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + m_nError = m_xLockBytes->Flush(); + } +} + +void SvStream::SetSize(sal_uInt64 const nSize) +{ + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + m_nError = m_xLockBytes->SetSize( nSize ); +} + +SvStream::SvStream() : + m_nActPos(0) + + , m_pBufPos(nullptr) + , m_nBufSize(0) + , m_nBufActualLen(0) + , m_nBufActualPos(0) + , m_nBufFree(0) + , m_isIoRead(false) + , m_isIoWrite(false) + + , m_isDirty(false) + , m_isEof(false) + + , m_nCompressMode(SvStreamCompressFlags::NONE) +#if defined UNX + , m_eLineDelimiter(LINEEND_LF) // UNIX-Format +#else + , m_eLineDelimiter(LINEEND_CRLF) // DOS-Format +#endif + , m_eStreamCharSet(osl_getThreadTextEncoding()) + + , m_nCryptMask(0) + + , m_nVersion(0) + + , m_nBufFilePos(0) + , m_eStreamMode(StreamMode::NONE) + , m_isWritable(true) + +{ + SetEndian( SvStreamEndian::LITTLE ); + + ClearError(); +} + +SvStream::SvStream( SvLockBytes* pLockBytesP ) : SvStream() +{ + m_xLockBytes = pLockBytesP; + if( pLockBytesP ) { + const SvStream* pStrm = pLockBytesP->GetStream(); + if( pStrm ) { + SetError( pStrm->GetErrorCode() ); + } + } + SetBufferSize( 256 ); +} + +SvStream::~SvStream() +{ + if (m_xLockBytes.is()) + Flush(); +} + +void SvStream::ClearError() +{ + m_isEof = false; + m_nError = ERRCODE_NONE; +} + +void SvStream::SetError( ErrCode nErrorCode ) +{ + if (m_nError == ERRCODE_NONE) + m_nError = nErrorCode; +} + +void SvStream::SetEndian( SvStreamEndian nNewFormat ) +{ +#ifdef OSL_BIGENDIAN + m_isSwap = nNewFormat == SvStreamEndian::LITTLE; +#else + m_isSwap = nNewFormat == SvStreamEndian::BIG; +#endif +} + +SvStreamEndian SvStream::GetEndian() const +{ +#ifdef OSL_BIGENDIAN + return m_isSwap ? SvStreamEndian::LITTLE : SvStreamEndian::BIG; +#else + return m_isSwap ? SvStreamEndian::BIG : SvStreamEndian::LITTLE; +#endif +} + +void SvStream::SetBufferSize( sal_uInt16 nBufferSize ) +{ + sal_uInt64 const nActualFilePos = Tell(); + bool bDontSeek = (m_pRWBuf == nullptr); + + if (m_isDirty && m_isWritable) // due to Windows NT: Access denied + FlushBuffer(); + + if (m_nBufSize) + { + m_pRWBuf.reset(); + m_nBufFilePos += m_nBufActualPos; + } + + m_pRWBuf = nullptr; + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_nBufSize = nBufferSize; + if (m_nBufSize) + m_pRWBuf.reset(new sal_uInt8[ m_nBufSize ]); + m_pBufPos = m_pRWBuf.get(); + m_isIoRead = m_isIoWrite = false; + if( !bDontSeek ) + SeekPos( nActualFilePos ); +} + +void SvStream::ClearBuffer() +{ + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_nBufFilePos = 0; + m_pBufPos = m_pRWBuf.get(); + m_isDirty = false; + m_isIoRead = m_isIoWrite = false; + + m_isEof = false; +} + +void SvStream::ResetError() +{ + ClearError(); +} + +bool SvStream::ReadByteStringLine( OUString& rStr, rtl_TextEncoding eSrcCharSet, + sal_Int32 nMaxBytesToRead ) +{ + OString aStr; + bool bRet = ReadLine( aStr, nMaxBytesToRead); + rStr = OStringToOUString(aStr, eSrcCharSet); + return bRet; +} + +bool SvStream::ReadLine( OString& rStr, sal_Int32 nMaxBytesToRead ) +{ + OStringBuffer aBuf(4096); + bool rv = ReadLine(aBuf, nMaxBytesToRead); + rStr = aBuf.makeStringAndClear(); + return rv; +} + +bool SvStream::ReadLine( OStringBuffer& aBuf, sal_Int32 nMaxBytesToRead ) +{ + char buf[256+1]; + bool bEnd = false; + sal_uInt64 nOldFilePos = Tell(); + char c = 0; + std::size_t nTotalLen = 0; + + aBuf.setLength(0); + while( !bEnd && !GetError() ) // Don't test for EOF as we + // are reading block-wise! + { + sal_uInt16 nLen = static_cast<sal_uInt16>(ReadBytes(buf, sizeof(buf)-1)); + if ( !nLen ) + { + if ( aBuf.isEmpty() ) + { + // Exit on first block-read error + m_isEof = true; + aBuf.setLength(0); + return false; + } + else + break; + } + + sal_uInt16 j, n; + for( j = n = 0; j < nLen ; ++j ) + { + c = buf[j]; + if ( c == '\n' || c == '\r' ) + { + bEnd = true; + break; + } + if ( n < j ) + buf[n] = c; + ++n; + } + nTotalLen += j; + if (nTotalLen > o3tl::make_unsigned(nMaxBytesToRead)) + { + n -= nTotalLen - nMaxBytesToRead; + nTotalLen = nMaxBytesToRead; + bEnd = true; + } + if ( n ) + aBuf.append(buf, n); + } + + if ( !bEnd && !GetError() && !aBuf.isEmpty() ) + bEnd = true; + + nOldFilePos += nTotalLen; + if( Tell() > nOldFilePos ) + nOldFilePos++; + Seek( nOldFilePos ); // Seek pointer due to BlockRead above + + if ( bEnd && (c=='\r' || c=='\n') ) // Special treatment for DOS files + { + char cTemp; + std::size_t nLen = ReadBytes(&cTemp, sizeof(cTemp)); + if ( nLen ) { + if( cTemp == c || (cTemp != '\n' && cTemp != '\r') ) + Seek( nOldFilePos ); + } + } + + if ( bEnd ) + m_isEof = false; + return bEnd; +} + +bool SvStream::ReadUniStringLine( OUString& rStr, sal_Int32 nMaxCodepointsToRead ) +{ + sal_Unicode buf[256+1]; + bool bEnd = false; + sal_uInt64 nOldFilePos = Tell(); + sal_Unicode c = 0; + std::size_t nTotalLen = 0; + + DBG_ASSERT( sizeof(sal_Unicode) == sizeof(sal_uInt16), "ReadUniStringLine: swapping sizeof(sal_Unicode) not implemented" ); + + OUStringBuffer aBuf(4096); + while( !bEnd && !GetError() ) // Don't test for EOF as we + // are reading block-wise! + { + sal_uInt16 nLen = static_cast<sal_uInt16>(ReadBytes( buf, sizeof(buf)-sizeof(sal_Unicode))); + nLen /= sizeof(sal_Unicode); + if ( !nLen ) + { + if ( aBuf.isEmpty() ) + { + // exit on first BlockRead error + m_isEof = true; + rStr.clear(); + return false; + } + else + break; + } + + sal_uInt16 j, n; + for( j = n = 0; j < nLen ; ++j ) + { + if (m_isSwap) + SwapNumber( buf[n] ); + c = buf[j]; + if ( c == '\n' || c == '\r' ) + { + bEnd = true; + break; + } + // erAck 26.02.01: Old behavior was no special treatment of '\0' + // character here, but a following rStr+=c did ignore it. Is this + // really intended? Or should a '\0' better terminate a line? + // The nOldFilePos stuff wasn't correct then anyways. + if ( c ) + { + if ( n < j ) + buf[n] = c; + ++n; + } + } + nTotalLen += j; + if (nTotalLen > o3tl::make_unsigned(nMaxCodepointsToRead)) + { + n -= nTotalLen - nMaxCodepointsToRead; + nTotalLen = nMaxCodepointsToRead; + bEnd = true; + } + if ( n ) + aBuf.append( buf, n ); + } + + if ( !bEnd && !GetError() && !aBuf.isEmpty() ) + bEnd = true; + + nOldFilePos += nTotalLen * sizeof(sal_Unicode); + if( Tell() > nOldFilePos ) + nOldFilePos += sizeof(sal_Unicode); + Seek( nOldFilePos ); // seek due to BlockRead above + + if ( bEnd && (c=='\r' || c=='\n') ) // special treatment for DOS files + { + sal_Unicode cTemp; + ReadBytes( &cTemp, sizeof(cTemp) ); + if (m_isSwap) + SwapNumber( cTemp ); + if( cTemp == c || (cTemp != '\n' && cTemp != '\r') ) + Seek( nOldFilePos ); + } + + if ( bEnd ) + m_isEof = false; + rStr = aBuf.makeStringAndClear(); + return bEnd; +} + +bool SvStream::ReadUniOrByteStringLine( OUString& rStr, rtl_TextEncoding eSrcCharSet, + sal_Int32 nMaxCodepointsToRead ) +{ + if ( eSrcCharSet == RTL_TEXTENCODING_UNICODE ) + return ReadUniStringLine( rStr, nMaxCodepointsToRead ); + else + return ReadByteStringLine( rStr, eSrcCharSet, nMaxCodepointsToRead ); +} + +OString read_zeroTerminated_uInt8s_ToOString(SvStream& rStream) +{ + OStringBuffer aOutput(256); + + char buf[ 256 + 1 ]; + bool bEnd = false; + sal_uInt64 nFilePos = rStream.Tell(); + + while( !bEnd && !rStream.GetError() ) + { + std::size_t nLen = rStream.ReadBytes(buf, sizeof(buf)-1); + if (!nLen) + break; + + std::size_t nReallyRead = nLen; + const char* pPtr = buf; + while (nLen && *pPtr) + { + ++pPtr; + --nLen; + } + + bEnd = ( nReallyRead < sizeof(buf)-1 ) // read less than attempted to read + || ( ( nLen > 0 ) // OR it is inside the block we read + && ( 0 == *pPtr ) // AND found a string terminator + ); + + aOutput.append(buf, pPtr - buf); + } + + nFilePos += aOutput.getLength(); + if (rStream.Tell() > nFilePos) + rStream.Seek(nFilePos+1); // seek due to FileRead above + return aOutput.makeStringAndClear(); +} + +OUString read_zeroTerminated_uInt8s_ToOUString(SvStream& rStream, rtl_TextEncoding eEnc) +{ + return OStringToOUString( + read_zeroTerminated_uInt8s_ToOString(rStream), eEnc); +} + +/** Attempt to write a prefixed sequence of nUnits 16bit units from an OUString, + returned value is number of bytes written */ +std::size_t write_uInt16s_FromOUString(SvStream& rStrm, std::u16string_view rStr, + std::size_t nUnits) +{ + DBG_ASSERT( sizeof(sal_Unicode) == sizeof(sal_uInt16), "write_uInt16s_FromOUString: swapping sizeof(sal_Unicode) not implemented" ); + std::size_t nWritten; + if (!rStrm.IsEndianSwap()) + nWritten = rStrm.WriteBytes(rStr.data(), nUnits * sizeof(sal_Unicode)); + else + { + std::size_t nLen = nUnits; + sal_Unicode aBuf[384]; + sal_Unicode* const pTmp = ( nLen > 384 ? new sal_Unicode[nLen] : aBuf); + memcpy( pTmp, rStr.data(), nLen * sizeof(sal_Unicode) ); + sal_Unicode* p = pTmp; + const sal_Unicode* const pStop = pTmp + nLen; + while ( p < pStop ) + { + SwapNumber( *p ); + p++; + } + nWritten = rStrm.WriteBytes( pTmp, nLen * sizeof(sal_Unicode) ); + if ( pTmp != aBuf ) + delete [] pTmp; + } + return nWritten; +} + +bool SvStream::WriteUnicodeOrByteText( std::u16string_view rStr, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + { + write_uInt16s_FromOUString(*this, rStr, rStr.size()); + return m_nError == ERRCODE_NONE; + } + else + { + OString aStr(OUStringToOString(rStr, eDestCharSet)); + write_uInt8s_FromOString(*this, aStr, aStr.getLength()); + return m_nError == ERRCODE_NONE; + } +} + +bool SvStream::WriteByteStringLine( std::u16string_view rStr, rtl_TextEncoding eDestCharSet ) +{ + return WriteLine(OUStringToOString(rStr, eDestCharSet)); +} + +bool SvStream::WriteLine(std::string_view rStr) +{ + WriteBytes(rStr.data(), rStr.size()); + endl(*this); + return m_nError == ERRCODE_NONE; +} + +bool SvStream::WriteUniOrByteChar( sal_Unicode ch, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + WriteUnicode(ch); + else + { + OString aStr(&ch, 1, eDestCharSet); + WriteBytes(aStr.getStr(), aStr.getLength()); + } + return m_nError == ERRCODE_NONE; +} + +void SvStream::StartWritingUnicodeText() +{ + m_isSwap = false; // Switch to no endian swapping + // BOM, Byte Order Mark, U+FEFF, see + // http://www.unicode.org/faq/utf_bom.html#BOM + // Upon read: 0xfeff(-257) => no swap; 0xfffe(-2) => swap + WriteUInt16(0xfeff); +} + +void SvStream::StartReadingUnicodeText( rtl_TextEncoding eReadBomCharSet ) +{ + if (!( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UNICODE || + eReadBomCharSet == RTL_TEXTENCODING_UTF8)) + return; // nothing to read + + const sal_uInt64 nOldPos = Tell(); + bool bGetBack = true; + unsigned char nFlag(0); + ReadUChar( nFlag ); + switch ( nFlag ) + { + case 0xfe: // UTF-16BE? + if ( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UNICODE) + { + ReadUChar(nFlag); + if (nFlag == 0xff) + { + SetEndian(SvStreamEndian::BIG); + bGetBack = false; + } + } + break; + case 0xff: // UTF-16LE? + if ( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UNICODE) + { + ReadUChar(nFlag); + if (nFlag == 0xfe) + { + SetEndian(SvStreamEndian::LITTLE); + bGetBack = false; + } + } + break; + case 0xef: // UTF-8? + if ( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UTF8) + { + ReadUChar(nFlag); + if (nFlag == 0xbb) + { + ReadUChar(nFlag); + if (nFlag == 0xbf) + bGetBack = false; // it is UTF-8 + } + } + break; + default: + ; // nothing + } + if (bGetBack) + Seek(nOldPos); // no BOM, pure data +} + +sal_uInt64 SvStream::SeekRel(sal_Int64 const nPos) +{ + sal_uInt64 nActualPos = Tell(); + + if ( nPos >= 0 ) + { + if (SAL_MAX_UINT64 - nActualPos > o3tl::make_unsigned(nPos)) + nActualPos += nPos; + } + else + { + sal_uInt64 const nAbsPos = static_cast<sal_uInt64>(-nPos); + if ( nActualPos >= nAbsPos ) + nActualPos -= nAbsPos; + } + + assert((m_pBufPos != nullptr) == bool(m_pRWBuf)); + if (m_pRWBuf) + { + m_pBufPos = m_pRWBuf.get() + nActualPos; + } + return Seek( nActualPos ); +} + +template <typename T> SvStream& SvStream::ReadNumber(T& r) +{ + T n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapNumber(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadUInt16(sal_uInt16& r) { return ReadNumber(r); } +SvStream& SvStream::ReadUInt32(sal_uInt32& r) { return ReadNumber(r); } +SvStream& SvStream::ReadUInt64(sal_uInt64& r) { return ReadNumber(r); } +SvStream& SvStream::ReadInt16(sal_Int16& r) { return ReadNumber(r); } +SvStream& SvStream::ReadInt32(sal_Int32& r) { return ReadNumber(r); } +SvStream& SvStream::ReadInt64(sal_Int64& r) { return ReadNumber(r); } + +SvStream& SvStream::ReadSChar( signed char& r ) +{ + if (m_isIoRead && sizeof(signed char) <= m_nBufFree) + { + r = *m_pBufPos; + m_nBufActualPos += sizeof(signed char); + m_pBufPos += sizeof(signed char); + m_nBufFree -= sizeof(signed char); + } + else + ReadBytes( &r, sizeof(signed char) ); + return *this; +} + +// Special treatment for Chars due to PutBack + +SvStream& SvStream::ReadChar( char& r ) +{ + if (m_isIoRead && sizeof(char) <= m_nBufFree) + { + r = *m_pBufPos; + m_nBufActualPos += sizeof(char); + m_pBufPos += sizeof(char); + m_nBufFree -= sizeof(char); + } + else + ReadBytes( &r, sizeof(char) ); + return *this; +} + +SvStream& SvStream::ReadUChar( unsigned char& r ) +{ + if (m_isIoRead && sizeof(char) <= m_nBufFree) + { + r = *m_pBufPos; + m_nBufActualPos += sizeof(char); + m_pBufPos += sizeof(char); + m_nBufFree -= sizeof(char); + } + else + ReadBytes( &r, sizeof(char) ); + return *this; +} + +SvStream& SvStream::ReadUtf16(sal_Unicode& r) { return ReadNumber(r); } + +SvStream& SvStream::ReadCharAsBool( bool& r ) +{ + if (m_isIoRead && sizeof(char) <= m_nBufFree) + { + SAL_WARN_IF( + *m_pBufPos > 1, "tools.stream", unsigned(*m_pBufPos) << " not 0/1"); + r = *m_pBufPos != 0; + m_nBufActualPos += sizeof(char); + m_pBufPos += sizeof(char); + m_nBufFree -= sizeof(char); + } + else + { + unsigned char c; + if (ReadBytes(&c, 1) == 1) + { + SAL_WARN_IF(c > 1, "tools.stream", unsigned(c) << " not 0/1"); + r = c != 0; + } + } + return *this; +} + +SvStream& SvStream::ReadFloat(float& r) +{ + float n = 0; + readNumberWithoutSwap(n); + if (good()) + { +#if defined UNX + if (m_isSwap) + SwapFloat(n); +#endif + r = n; + } + return *this; +} + +SvStream& SvStream::ReadDouble(double& r) +{ + double n = 0; + readNumberWithoutSwap(n); + if (good()) + { +#if defined UNX + if (m_isSwap) + SwapDouble(n); +#endif + r = n; + } + return *this; +} + +SvStream& SvStream::ReadStream( SvStream& rStream ) +{ + const sal_uInt32 cBufLen = 0x8000; + std::unique_ptr<char[]> pBuf( new char[ cBufLen ] ); + + sal_uInt32 nCount; + do { + nCount = ReadBytes( pBuf.get(), cBufLen ); + rStream.WriteBytes( pBuf.get(), nCount ); + } while( nCount == cBufLen ); + + return *this; +} + +template <typename T> SvStream& SvStream::WriteNumber(T n) +{ + if (m_isSwap) + SwapNumber(n); + writeNumberWithoutSwap(n); + return *this; +} + +SvStream& SvStream::WriteUInt16(sal_uInt16 v) { return WriteNumber(v); } +SvStream& SvStream::WriteUInt32(sal_uInt32 v) { return WriteNumber(v); } +SvStream& SvStream::WriteUInt64(sal_uInt64 v) { return WriteNumber(v); } +SvStream& SvStream::WriteInt16(sal_Int16 v) { return WriteNumber(v); } +SvStream& SvStream::WriteInt32(sal_Int32 v) { return WriteNumber(v); } +SvStream& SvStream::WriteInt64(sal_Int64 v) { return WriteNumber(v); } + +SvStream& SvStream::WriteSChar( signed char v ) +{ + //SDO + if (m_isIoWrite && sizeof(signed char) <= m_nBufFree) + { + *m_pBufPos = v; + m_pBufPos++; // sizeof(char); + m_nBufActualPos++; + if (m_nBufActualPos > m_nBufActualLen) // Append ? + m_nBufActualLen = m_nBufActualPos; + m_nBufFree--; // = sizeof(char); + m_isDirty = true; + } + else + WriteBytes( &v, sizeof(signed char) ); + return *this; +} + +// Special treatment for Chars due to PutBack + +SvStream& SvStream::WriteChar( char v ) +{ + //SDO + if (m_isIoWrite && sizeof(char) <= m_nBufFree) + { + *m_pBufPos = v; + m_pBufPos++; // sizeof(char); + m_nBufActualPos++; + if (m_nBufActualPos > m_nBufActualLen) // Append ? + m_nBufActualLen = m_nBufActualPos; + m_nBufFree--; // = sizeof(char); + m_isDirty = true; + } + else + WriteBytes( &v, sizeof(char) ); + return *this; +} + +SvStream& SvStream::WriteUChar( unsigned char v ) +{ +//SDO + if (m_isIoWrite && sizeof(char) <= m_nBufFree) + { + *reinterpret_cast<unsigned char*>(m_pBufPos) = v; + m_pBufPos++; // = sizeof(char); + m_nBufActualPos++; // = sizeof(char); + if (m_nBufActualPos > m_nBufActualLen) // Append ? + m_nBufActualLen = m_nBufActualPos; + m_nBufFree--; + m_isDirty = true; + } + else + WriteBytes( &v, sizeof(char) ); + return *this; +} + +SvStream& SvStream::WriteUInt8( sal_uInt8 v ) +{ + return WriteUChar(v); +} + +SvStream& SvStream::WriteUnicode( sal_Unicode v ) +{ + return WriteUInt16(v); +} + +SvStream& SvStream::WriteFloat( float v ) +{ +#ifdef UNX + if (m_isSwap) + SwapFloat(v); +#endif + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteDouble ( const double& r ) +{ +#if defined UNX + if (m_isSwap) + { + double nHelp = r; + SwapDouble(nHelp); + writeNumberWithoutSwap(nHelp); + return *this; + } + else +#endif + { + writeNumberWithoutSwap(r); + } + return *this; +} + +SvStream& SvStream::WriteCharPtr( const char* pBuf ) +{ + WriteBytes( pBuf, strlen(pBuf) ); + return *this; +} + +SvStream& SvStream::WriteStream( SvStream& rStream ) +{ + const sal_uInt32 cBufLen = 0x8000; + std::unique_ptr<char[]> pBuf( new char[ cBufLen ] ); + sal_uInt32 nCount; + do { + nCount = rStream.ReadBytes( pBuf.get(), cBufLen ); + WriteBytes( pBuf.get(), nCount ); + } while( nCount == cBufLen ); + + return *this; +} + +sal_uInt64 SvStream::WriteStream( SvStream& rStream, sal_uInt64 nSize ) +{ + const sal_uInt32 cBufLen = 0x8000; + std::unique_ptr<char[]> pBuf( new char[ cBufLen ] ); + sal_uInt32 nCurBufLen = cBufLen; + sal_uInt32 nCount; + sal_uInt64 nWriteSize = nSize; + + do + { + nCurBufLen = std::min<sal_uInt64>(nCurBufLen, nWriteSize); + nCount = rStream.ReadBytes(pBuf.get(), nCurBufLen); + WriteBytes( pBuf.get(), nCount ); + nWriteSize -= nCount; + } + while( nWriteSize && nCount == nCurBufLen ); + + return nSize - nWriteSize; +} + +OUString SvStream::ReadUniOrByteString( rtl_TextEncoding eSrcCharSet ) +{ + // read UTF-16 string directly from stream ? + if (eSrcCharSet == RTL_TEXTENCODING_UNICODE) + return read_uInt32_lenPrefixed_uInt16s_ToOUString(*this); + return read_uInt16_lenPrefixed_uInt8s_ToOUString(*this, eSrcCharSet); +} + +SvStream& SvStream::WriteUniOrByteString( std::u16string_view rStr, rtl_TextEncoding eDestCharSet ) +{ + // write UTF-16 string directly into stream ? + if (eDestCharSet == RTL_TEXTENCODING_UNICODE) + write_uInt32_lenPrefixed_uInt16s_FromOUString(*this, rStr); + else + write_uInt16_lenPrefixed_uInt8s_FromOUString(*this, rStr, eDestCharSet); + return *this; +} + +void SvStream::FlushBuffer() +{ + if (m_isDirty) // Does stream require a flush? + { + SeekPos(m_nBufFilePos); + if (m_nCryptMask) + CryptAndWriteBuffer(m_pRWBuf.get(), m_nBufActualLen); + else if (PutData(m_pRWBuf.get(), m_nBufActualLen) != m_nBufActualLen) + SetError(SVSTREAM_WRITE_ERROR); + m_isDirty = false; + } +} + +std::size_t SvStream::ReadBytes( void* pData, std::size_t nCount ) +{ + std::size_t nSaveCount = nCount; + + if (!m_pRWBuf) + { + nCount = GetData( pData,nCount); + if (m_nCryptMask) + EncryptBuffer(pData, nCount); + m_nBufFilePos += nCount; + } + else + { + // check if block is completely within buffer + m_isIoRead = true; + m_isIoWrite = false; + if (nCount <= o3tl::make_unsigned(m_nBufActualLen - m_nBufActualPos)) + { + // => yes + if (nCount != 0) + memcpy(pData, m_pBufPos, nCount); + m_nBufActualPos = m_nBufActualPos + static_cast<sal_uInt16>(nCount); + m_pBufPos += nCount; + m_nBufFree = m_nBufFree - static_cast<sal_uInt16>(nCount); + } + else + { + FlushBuffer(); + + // Does data block fit into buffer? + if (nCount > m_nBufSize) + { + // => No! Thus read directly + // into target area without using the buffer + + m_isIoRead = false; + + SeekPos(m_nBufFilePos + m_nBufActualPos); + m_nBufActualLen = 0; + m_pBufPos = m_pRWBuf.get(); + nCount = GetData( pData, nCount ); + if (m_nCryptMask) + EncryptBuffer(pData, nCount); + m_nBufFilePos += nCount; + m_nBufFilePos += m_nBufActualPos; + m_nBufActualPos = 0; + } + else + { + // => Yes. Fill buffer first, then copy to target area + + m_nBufFilePos += m_nBufActualPos; + SeekPos(m_nBufFilePos); + + // TODO: Typecast before GetData, sal_uInt16 nCountTmp + std::size_t nCountTmp = GetData( m_pRWBuf.get(), m_nBufSize ); + if (m_nCryptMask) + EncryptBuffer(m_pRWBuf.get(), nCountTmp); + m_nBufActualLen = static_cast<sal_uInt16>(nCountTmp); + if( nCount > nCountTmp ) + { + nCount = nCountTmp; // trim count back, EOF see below + } + memcpy( pData, m_pRWBuf.get(), nCount ); + m_nBufActualPos = static_cast<sal_uInt16>(nCount); + m_pBufPos = m_pRWBuf.get() + nCount; + } + } + } + m_isEof = false; + m_nBufFree = m_nBufActualLen - m_nBufActualPos; + if (nCount != nSaveCount && m_nError != ERRCODE_IO_PENDING) + m_isEof = true; + if (nCount == nSaveCount && m_nError == ERRCODE_IO_PENDING) + m_nError = ERRCODE_NONE; + return nCount; +} + +std::size_t SvStream::WriteBytes( const void* pData, std::size_t nCount ) +{ + if( !nCount ) + return 0; + + if (!m_isWritable) + { + SetError( ERRCODE_IO_CANTWRITE ); + return 0; + } + + if (!m_pRWBuf) + { + if (m_nCryptMask) + nCount = CryptAndWriteBuffer( pData, nCount ); + else + nCount = PutData( pData, nCount ); + m_nBufFilePos += nCount; + return nCount; + } + + m_isIoRead = false; + m_isIoWrite = true; + if (nCount <= o3tl::make_unsigned(m_nBufSize - m_nBufActualPos)) + { + memcpy( m_pBufPos, pData, nCount ); + m_nBufActualPos = m_nBufActualPos + static_cast<sal_uInt16>(nCount); + // Update length if buffer was updated + if (m_nBufActualPos > m_nBufActualLen) + m_nBufActualLen = m_nBufActualPos; + + m_pBufPos += nCount; + m_isDirty = true; + } + else + { + FlushBuffer(); + + // Does data block fit into buffer? + if (nCount > m_nBufSize) + { + m_isIoWrite = false; + m_nBufFilePos += m_nBufActualPos; + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_pBufPos = m_pRWBuf.get(); + SeekPos(m_nBufFilePos); + if (m_nCryptMask) + nCount = CryptAndWriteBuffer( pData, nCount ); + else + nCount = PutData( pData, nCount ); + m_nBufFilePos += nCount; + } + else + { + // Copy block to buffer + memcpy( m_pRWBuf.get(), pData, nCount ); + + // Mind the order! + m_nBufFilePos += m_nBufActualPos; + m_nBufActualPos = static_cast<sal_uInt16>(nCount); + m_pBufPos = m_pRWBuf.get() + nCount; + m_nBufActualLen = static_cast<sal_uInt16>(nCount); + m_isDirty = true; + } + } + m_nBufFree = m_nBufSize - m_nBufActualPos; + return nCount; +} + +sal_uInt64 SvStream::Seek(sal_uInt64 const nFilePos) +{ + m_isIoRead = m_isIoWrite = false; + m_isEof = false; + if (!m_pRWBuf) + { + m_nBufFilePos = SeekPos( nFilePos ); + DBG_ASSERT(Tell() == m_nBufFilePos,"Out Of Sync!"); + return m_nBufFilePos; + } + + // Is seek position within buffer? + if (nFilePos >= m_nBufFilePos && nFilePos <= (m_nBufFilePos + m_nBufActualLen)) + { + m_nBufActualPos = static_cast<sal_uInt16>(nFilePos - m_nBufFilePos); + m_pBufPos = m_pRWBuf.get() + m_nBufActualPos; + // Update m_nBufFree to avoid crash upon PutBack + m_nBufFree = m_nBufActualLen - m_nBufActualPos; + } + else + { + FlushBuffer(); + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_pBufPos = m_pRWBuf.get(); + m_nBufFilePos = SeekPos( nFilePos ); + } + return m_nBufFilePos + m_nBufActualPos; +} + +bool checkSeek(SvStream &rSt, sal_uInt64 nOffset) +{ + const sal_uInt64 nMaxSeek = rSt.TellEnd(); + return (nOffset <= nMaxSeek && rSt.Seek(nOffset) == nOffset); +} + +namespace tools +{ +bool isEmptyFileUrl(const OUString& rUrl) +{ + if (!comphelper::isFileUrl(rUrl)) + { + return false; + } + + SvFileStream aStream(rUrl, StreamMode::READ); + if (!aStream.IsOpen()) + { + return false; + } + + return aStream.remainingSize() == 0; +} +} + +//STREAM_SEEK_TO_END in some of the Seek backends is special cased to be +//efficient, in others e.g. SotStorageStream it's really horribly slow, and in +//those this should be overridden +sal_uInt64 SvStream::remainingSize() +{ + sal_uInt64 const nCurr = Tell(); + sal_uInt64 const nEnd = TellEnd(); + sal_uInt64 nMaxAvailable = nEnd > nCurr ? (nEnd-nCurr) : 0; + return nMaxAvailable; +} + +sal_uInt64 SvStream::TellEnd() +{ + FlushBuffer(); + sal_uInt64 const nCurr = Tell(); + sal_uInt64 const nEnd = Seek(STREAM_SEEK_TO_END); + Seek(nCurr); + return nEnd; +} + +void SvStream::Flush() +{ + FlushBuffer(); + if (m_isWritable) + FlushData(); +} + +void SvStream::RefreshBuffer() +{ + FlushBuffer(); + SeekPos(m_nBufFilePos); + m_nBufActualLen = static_cast<sal_uInt16>(GetData( m_pRWBuf.get(), m_nBufSize )); + if (m_nBufActualLen && m_nError == ERRCODE_IO_PENDING) + m_nError = ERRCODE_NONE; + if (m_nCryptMask) + EncryptBuffer(m_pRWBuf.get(), static_cast<std::size_t>(m_nBufActualLen)); + m_isIoRead = m_isIoWrite = false; +} + +SvStream& SvStream::WriteInt32AsString(sal_Int32 nInt32) +{ + auto const buffer = OString::number(nInt32); + WriteBytes(buffer.getStr(), buffer.length); + return *this; +} + +SvStream& SvStream::WriteUInt32AsString(sal_uInt32 nUInt32) +{ + auto const buffer = OString::number(nUInt32); + WriteBytes(buffer.getStr(), buffer.length); + return *this; +} + +#define CRYPT_BUFSIZE 1024 + +/// Encrypt and write +std::size_t SvStream::CryptAndWriteBuffer( const void* pStart, std::size_t nLen) +{ + unsigned char pTemp[CRYPT_BUFSIZE]; + unsigned char const * pDataPtr = static_cast<unsigned char const *>(pStart); + std::size_t nCount = 0; + std::size_t nBufCount; + unsigned char nMask = m_nCryptMask; + do + { + if( nLen >= CRYPT_BUFSIZE ) + nBufCount = CRYPT_BUFSIZE; + else + nBufCount = nLen; + nLen -= nBufCount; + memcpy( pTemp, pDataPtr, static_cast<sal_uInt16>(nBufCount) ); + // ******** Encrypt ******** + for (unsigned char & rn : pTemp) + { + unsigned char aCh = rn; + aCh ^= nMask; + swapNibbles(aCh); + rn = aCh; + } + // ************************* + nCount += PutData( pTemp, nBufCount ); + pDataPtr += nBufCount; + } + while ( nLen ); + return nCount; +} + +void SvStream::EncryptBuffer(void* pStart, std::size_t nLen) const +{ + unsigned char* pTemp = static_cast<unsigned char*>(pStart); + unsigned char nMask = m_nCryptMask; + + for ( std::size_t n=0; n < nLen; n++, pTemp++ ) + { + unsigned char aCh = *pTemp; + swapNibbles(aCh); + aCh ^= nMask; + *pTemp = aCh; + } +} + +static unsigned char implGetCryptMask(const char* pStr, sal_Int32 nLen, tools::Long nVersion) +{ + unsigned char nCryptMask = 0; + + if (!nLen) + return nCryptMask; + + if( nVersion <= SOFFICE_FILEFORMAT_31 ) + { + while( nLen ) + { + nCryptMask ^= *pStr; + pStr++; + nLen--; + } + } + else // BugFix #25888# + { + for( sal_Int32 i = 0; i < nLen; i++ ) { + nCryptMask ^= pStr[i]; + if( nCryptMask & 0x80 ) { + nCryptMask <<= 1; + nCryptMask++; + } + else + nCryptMask <<= 1; + } + } + + if( !nCryptMask ) + nCryptMask = 67; + + return nCryptMask; +} + +void SvStream::SetCryptMaskKey(const OString& rCryptMaskKey) +{ + m_aCryptMaskKey = rCryptMaskKey; + m_nCryptMask = implGetCryptMask(m_aCryptMaskKey.getStr(), + m_aCryptMaskKey.getLength(), GetVersion()); +} + +bool SvStream::SetStreamSize(sal_uInt64 const nSize) +{ +#ifdef DBG_UTIL + sal_uInt64 nFPos = Tell(); +#endif + sal_uInt16 nBuf = m_nBufSize; + SetBufferSize( 0 ); + SetSize( nSize ); + if (nSize < m_nBufFilePos) + { + m_nBufFilePos = nSize; + } + SetBufferSize( nBuf ); +#ifdef DBG_UTIL + DBG_ASSERT(Tell()==nFPos,"SetStreamSize failed"); +#endif + return (m_nError == ERRCODE_NONE); +} + +SvStream& endl( SvStream& rStr ) +{ + LineEnd eDelim = rStr.GetLineDelimiter(); + if ( eDelim == LINEEND_CR ) + rStr.WriteChar('\r'); + else if( eDelim == LINEEND_LF ) + rStr.WriteChar('\n'); + else + rStr.WriteChar('\r').WriteChar('\n'); + return rStr; +} + +SvStream& endlu( SvStream& rStrm ) +{ + switch ( rStrm.GetLineDelimiter() ) + { + case LINEEND_CR : + rStrm.WriteUnicode('\r'); + break; + case LINEEND_LF : + rStrm.WriteUnicode('\n'); + break; + default: + rStrm.WriteUnicode('\r').WriteUnicode('\n'); + } + return rStrm; +} + +SvStream& endlub( SvStream& rStrm ) +{ + if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE ) + return endlu( rStrm ); + else + return endl( rStrm ); +} + +SvMemoryStream::SvMemoryStream( void* pBuffer, std::size_t bufSize, + StreamMode eMode ) +{ + if( eMode & StreamMode::WRITE ) + m_isWritable = true; + else + m_isWritable = false; + nEndOfData = bufSize; + bOwnsData = false; + pBuf = static_cast<sal_uInt8 *>(pBuffer); + nResize = 0; + nSize = bufSize; + nPos = 0; + SetBufferSize( 0 ); +} + +SvMemoryStream::SvMemoryStream( std::size_t nInitSize, std::size_t nResizeOffset ) +{ + m_isWritable = true; + bOwnsData = true; + nEndOfData = 0; + nResize = nResizeOffset; + nPos = 0; + pBuf = nullptr; + if( nResize != 0 && nResize < 16 ) + nResize = 16; + if( nInitSize ) + AllocateMemory( nInitSize ); + nSize = nInitSize; + SetBufferSize( 64 ); +} + +SvMemoryStream::~SvMemoryStream() +{ + if( pBuf ) + { + if( bOwnsData ) + FreeMemory(); + else + FlushBuffer(); + } +} + +void SvMemoryStream::SetBuffer( void* pNewBuf, std::size_t nCount, + std::size_t nEOF ) +{ + SetBufferSize( 0 ); // Init buffering in the base class + Seek( 0 ); + if( bOwnsData && pNewBuf != pBuf ) + FreeMemory(); + + pBuf = static_cast<sal_uInt8 *>(pNewBuf); + nPos = 0; + nSize = nCount; + nResize = 0; + bOwnsData = false; + + if( nEOF > nCount ) + nEOF = nCount; + nEndOfData = nEOF; + + ResetError(); +} + +std::size_t SvMemoryStream::GetData( void* pData, std::size_t nCount ) +{ + std::size_t nMaxCount = nEndOfData-nPos; + if( nCount > nMaxCount ) + nCount = nMaxCount; + if (nCount != 0) + { + memcpy( pData, pBuf+nPos, nCount ); + } + nPos += nCount; + return nCount; +} + +std::size_t SvMemoryStream::PutData( const void* pData, std::size_t nCount ) +{ + if( GetError() ) + return 0; + + std::size_t nMaxCount = nSize-nPos; + + // check for overflow + if( nCount > nMaxCount ) + { + if( nResize == 0 ) + { + // copy as much as possible + nCount = nMaxCount; + SetError( SVSTREAM_OUTOFMEMORY ); + } + else + { + tools::Long nNewResize; + if( nSize && nSize > nResize ) + nNewResize = nSize; + else + nNewResize = nResize; + + if( (nCount-nMaxCount) < nResize ) + { + // lacking memory is smaller than nResize, + // resize accordingly + if( !ReAllocateMemory( nNewResize) ) + { + nCount = 0; + SetError( SVSTREAM_WRITE_ERROR ); + } + } + else + { + // lacking memory is larger than nResize, + // resize by (nCount-nMaxCount) + resize offset + if( !ReAllocateMemory( nCount-nMaxCount+nNewResize ) ) + { + nCount = 0; + SetError( SVSTREAM_WRITE_ERROR ); + } + } + } + } + assert(pBuf && "Possibly Reallocate failed"); + memcpy( pBuf+nPos, pData, nCount); + + nPos += nCount; + if( nPos > nEndOfData ) + nEndOfData = nPos; + return nCount; +} + +sal_uInt64 SvMemoryStream::SeekPos(sal_uInt64 const nNewPos) +{ + // nEndOfData: First position in stream not allowed to read from + // nSize: Size of allocated buffer + + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nNewPos != SAL_MAX_UINT32); + if( nNewPos < nEndOfData ) + nPos = nNewPos; + else if( nNewPos == STREAM_SEEK_TO_END ) + nPos = nEndOfData; + else + { + if( nNewPos >= nSize ) // Does buffer need extension? + { + if( nResize ) // Is extension possible? + { + tools::Long nDiff = static_cast<tools::Long>(nNewPos - nSize + 1); + nDiff += static_cast<tools::Long>(nResize); + ReAllocateMemory( nDiff ); + nPos = nNewPos; + nEndOfData = nNewPos; + } + else // Extension not possible, set pos to end of data + { + // SetError( SVSTREAM_OUTOFMEMORY ); + nPos = nEndOfData; + } + } + else // Expand buffer size + { + nPos = nNewPos; + nEndOfData = nNewPos; + } + } + return nPos; +} + +void SvMemoryStream::FlushData() +{ +} + +void SvMemoryStream::ResetError() +{ + SvStream::ClearError(); +} + +void SvMemoryStream::AllocateMemory( std::size_t nNewSize ) +{ + pBuf = new sal_uInt8[nNewSize]; +} + +// (using Bozo algorithm) +bool SvMemoryStream::ReAllocateMemory( tools::Long nDiff ) +{ + if (!m_isWritable || !bOwnsData) + return false; + + bool bRetVal = false; + tools::Long nTemp = static_cast<tools::Long>(nSize); + nTemp += nDiff; + std::size_t nNewSize = static_cast<std::size_t>(nTemp); + + if( nNewSize ) + { + sal_uInt8* pNewBuf = new sal_uInt8[nNewSize]; + + bRetVal = true; // Success! + if( nNewSize < nSize ) // Are we shrinking? + { + memcpy( pNewBuf, pBuf, nNewSize ); + if( nPos > nNewSize ) + nPos = 0; + if( nEndOfData >= nNewSize ) + nEndOfData = nNewSize-1; + } + else + { + if (nSize != 0) + { + memcpy( pNewBuf, pBuf, nSize ); + } + memset(pNewBuf + nSize, 0x00, nNewSize - nSize); + } + + FreeMemory(); + + pBuf = pNewBuf; + nSize = nNewSize; + } + else + { + bRetVal = true; + FreeMemory(); + pBuf = nullptr; + nSize = 0; + nEndOfData = 0; + nPos = 0; + } + + return bRetVal; +} + +void SvMemoryStream::FreeMemory() +{ + assert(bOwnsData); + if (bOwnsData) + { + delete[] pBuf; + pBuf = nullptr; + } +} + +void* SvMemoryStream::SwitchBuffer() +{ + FlushBuffer(); + if( !bOwnsData ) + return nullptr; + Seek( STREAM_SEEK_TO_BEGIN ); + + void* pRetVal = pBuf; + pBuf = nullptr; + nEndOfData = 0; + nResize = 64; + nPos = 0; + + ResetError(); + + std::size_t nInitSize = 512; + AllocateMemory(nInitSize); + nSize = nInitSize; + + SetBufferSize( 64 ); + return pRetVal; +} + +void SvMemoryStream::SetSize(sal_uInt64 const nNewSize) +{ + if (!m_isWritable) + { + SetError(SVSTREAM_INVALID_HANDLE); + return; + } + + tools::Long nDiff = static_cast<tools::Long>(nNewSize) - static_cast<tools::Long>(nSize); + ReAllocateMemory( nDiff ); +} + +// Create an OString of nLen bytes from rStream +// coverity[ +taint_sanitize ] +OString read_uInt8s_ToOString(SvStream& rStrm, std::size_t nLen) +{ + rtl_String *pStr = nullptr; + if (nLen) + { + nLen = std::min<std::size_t>(nLen, SAL_MAX_INT32); + //limit allocation to size of file, but + 1 to set eof state + nLen = std::min<sal_uInt64>(nLen, rStrm.remainingSize() + 1); + //alloc a (ref-count 1) rtl_String of the desired length. + //rtl_String's buffer is uninitialized, except for null termination + pStr = rtl_string_alloc(sal::static_int_cast<sal_Int32>(nLen)); + SAL_WARN_IF(!pStr, "tools.stream", "allocation failed"); + if (pStr) + { + std::size_t nWasRead = rStrm.ReadBytes(pStr->buffer, nLen); + if (nWasRead != nLen) + { + //on (typically unlikely) short read set length to what we could + //read, and null terminate. Excess buffer capacity remains of + //course, could create a (true) replacement OString if it matters. + pStr->length = sal::static_int_cast<sal_Int32>(nWasRead); + pStr->buffer[pStr->length] = 0; + } + } + } + + //take ownership of buffer and return, otherwise return empty string + return pStr ? OString(pStr, SAL_NO_ACQUIRE) : OString(); +} + +// Create an OUString of nLen sal_Unicode code units from rStream +// coverity[ +taint_sanitize ] +OUString read_uInt16s_ToOUString(SvStream& rStrm, std::size_t nLen) +{ + rtl_uString *pStr = nullptr; + if (nLen) + { + nLen = std::min<std::size_t>(nLen, SAL_MAX_INT32); + //limit allocation to size of file, but + 1 to set eof state + nLen = o3tl::sanitizing_min<sal_uInt64>(nLen, (rStrm.remainingSize() + 2) / 2); + //alloc a (ref-count 1) rtl_uString of the desired length. + //rtl_String's buffer is uninitialized, except for null termination + pStr = rtl_uString_alloc(sal::static_int_cast<sal_Int32>(nLen)); + SAL_WARN_IF(!pStr, "tools.stream", "allocation failed"); + if (pStr) + { + std::size_t nWasRead = rStrm.ReadBytes(pStr->buffer, nLen*2)/2; + if (nWasRead != nLen) + { + //on (typically unlikely) short read set length to what we could + //read, and null terminate. Excess buffer capacity remains of + //course, could create a (true) replacement OUString if it matters. + pStr->length = sal::static_int_cast<sal_Int32>(nWasRead); + pStr->buffer[pStr->length] = 0; + } + if (rStrm.IsEndianSwap()) + { + for (sal_Int32 i = 0; i < pStr->length; ++i) + pStr->buffer[i] = OSL_SWAPWORD(pStr->buffer[i]); + } + } + } + + // take ownership of buffer and return, otherwise return empty string + return pStr ? OUString(pStr, SAL_NO_ACQUIRE) : OUString(); +} + +namespace +{ + template <typename T, typename O> T tmpl_convertLineEnd(const T &rIn, LineEnd eLineEnd) + { + // Determine linebreaks and compute length + bool bConvert = false; // Needs conversion + sal_Int32 nStrLen = rIn.getLength(); + sal_Int32 nLineEndLen = (eLineEnd == LINEEND_CRLF) ? 2 : 1; + sal_Int32 nLen = 0; // Target length + sal_Int32 i = 0; // Source counter + + while (i < nStrLen) + { + // \r or \n causes linebreak + if ( (rIn[i] == '\r') || (rIn[i] == '\n') ) + { + nLen = nLen + nLineEndLen; + + // If set already, skip expensive test + if ( !bConvert ) + { + // Do we need to convert? + if ( ((eLineEnd != LINEEND_LF) && (rIn[i] == '\n')) || + ((eLineEnd == LINEEND_CRLF) && (i+1) < nStrLen && (rIn[i+1] != '\n')) || + ((eLineEnd == LINEEND_LF) && + ((rIn[i] == '\r') || ((i+1) < nStrLen && rIn[i+1] == '\r'))) || + ((eLineEnd == LINEEND_CR) && + ((rIn[i] == '\n') || ((i+1) < nStrLen && rIn[i+1] == '\n'))) ) + bConvert = true; + } + + // skip char if \r\n or \n\r + if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) && + (rIn[i] != rIn[i+1]) ) + ++i; + } + else + ++nLen; + ++i; + } + + if (!bConvert) + return rIn; + + // convert linebreaks, insert string + O aNewData(nLen); + i = 0; + while (i < nStrLen) + { + // \r or \n causes linebreak + if ( (rIn[i] == '\r') || (rIn[i] == '\n') ) + { + if ( eLineEnd == LINEEND_CRLF ) + { + aNewData.append('\r'); + aNewData.append('\n'); + } + else + { + if ( eLineEnd == LINEEND_CR ) + aNewData.append('\r'); + else + aNewData.append('\n'); + } + + if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) && + (rIn[i] != rIn[i+1]) ) + ++i; + } + else + { + aNewData.append(rIn[i]); + } + + ++i; + } + + return aNewData.makeStringAndClear(); + } +} + +OString convertLineEnd(const OString &rIn, LineEnd eLineEnd) +{ + return tmpl_convertLineEnd<OString, OStringBuffer>(rIn, eLineEnd); +} + +OUString convertLineEnd(const OUString &rIn, LineEnd eLineEnd) +{ + return tmpl_convertLineEnd<OUString, OUStringBuffer>(rIn, eLineEnd); +} + +std::size_t write_uInt32_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + std::u16string_view rStr) +{ + std::size_t nWritten = 0; + sal_uInt32 nUnits = std::min<std::size_t>(rStr.size(), std::numeric_limits<sal_uInt32>::max()); + SAL_WARN_IF(static_cast<std::size_t>(nUnits) != static_cast<std::size_t>(rStr.size()), + "tools.stream", + "string too long for prefix count to fit in output type"); + rStrm.WriteUInt32(nUnits); + if (rStrm.good()) + { + nWritten += sizeof(sal_uInt32); + nWritten += write_uInt16s_FromOUString(rStrm, rStr, nUnits); + } + return nWritten; +} + +std::size_t write_uInt16_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + std::u16string_view rStr) +{ + std::size_t nWritten = 0; + sal_uInt16 nUnits = std::min<std::size_t>(rStr.size(), std::numeric_limits<sal_uInt16>::max()); + SAL_WARN_IF(nUnits != rStr.size(), + "tools.stream", + "string too long for prefix count to fit in output type"); + rStrm.WriteUInt16(nUnits); + if (rStrm.good()) + { + nWritten += sizeof(nUnits); + nWritten += write_uInt16s_FromOUString(rStrm, rStr, nUnits); + } + return nWritten; +} + +std::size_t write_uInt16_lenPrefixed_uInt8s_FromOString(SvStream& rStrm, + std::string_view rStr) +{ + std::size_t nWritten = 0; + sal_uInt16 nUnits = std::min<std::size_t>(rStr.size(), std::numeric_limits<sal_uInt16>::max()); + SAL_WARN_IF(static_cast<std::size_t>(nUnits) != static_cast<std::size_t>(rStr.size()), + "tools.stream", + "string too long for sal_uInt16 count to fit in output type"); + rStrm.WriteUInt16( nUnits ); + if (rStrm.good()) + { + nWritten += sizeof(sal_uInt16); + nWritten += write_uInt8s_FromOString(rStrm, rStr, nUnits); + } + return nWritten; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |