/* -*- 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()
{
(void)this;
}
// virtual
ErrCode SvLockBytes::ReadAt(sal_uInt64 const , void * , std::size_t ,
std::size_t * ) const
{
OSL_FAIL("SvLockBytes::ReadAt(): Bad stream");
return ERRCODE_NONE;
}
// virtual
ErrCode SvLockBytes::WriteAt(sal_uInt64 const , const void * , std::size_t ,
std::size_t * )
{
OSL_FAIL("SvLockBytes::WriteAt(): Bad stream");
return ERRCODE_NONE;
}
// virtual
ErrCode SvLockBytes::Flush() const
{
OSL_FAIL("SvLockBytes::Flush(): Bad stream");
return ERRCODE_NONE;
}
// virtual
ErrCode SvLockBytes::SetSize(sal_uInt64)
{
OSL_FAIL("SvLockBytes::SetSize(): Bad stream");
return ERRCODE_NONE;
}
ErrCode SvLockBytes::Stat(SvLockBytesStat *) const
{
OSL_FAIL("SvLockBytes::Stat(): Bad stream");
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;
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.IsWarning() && nErrorCode.IsError()))
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 sequence of nUnits 16bit units from an OUString,
returned value is number of bytes written */
static 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, bool bZero)
{
if ( eDestCharSet == RTL_TEXTENCODING_UNICODE )
{
write_uInt16s_FromOUString(*this, rStr, rStr.size());
if (bZero)
WriteUnicode(0);
}
else
{
OString aStr(OUStringToOString(rStr, eDestCharSet));
WriteBytes(aStr.getStr(), aStr.getLength());
if (bZero)
WriteChar(0);
}
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 ( double v )
{
#if defined UNX
if (m_isSwap)
SwapDouble(v);
#endif
writeNumberWithoutSwap(v);
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;
}
#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 );
}
void SvMemoryStream::MakeReadOnly()
{
FlushBuffer();
m_isWritable = false;
nResize = 0;
SetBufferSize( 0 );
}
// 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
// coverity[tainted_data] - unhelpful untrusted loop bound
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 += rStrm.WriteBytes(rStr.data(), nUnits);
}
return nWritten;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V1053 Calling the 'SeekPos' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'stream.cxx:296', 'stream.cxx:341', 'stream.cxx:1055', 'stream.hxx:183'.
↑ V1053 Calling the 'SeekPos' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'stream.cxx:1464', 'stream.cxx:341', 'stream.cxx:1055', 'stream.hxx:613'.
↑ V1053 Calling the 'SeekPos' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'stream.cxx:1480', 'stream.cxx:341', 'stream.cxx:1055', 'stream.hxx:613'.
↑ V1051 Consider checking for misprints. It's possible that the 'nActualPos' should be checked here.