/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void swapNibbles(unsigned char &c) { unsigned char nSwapTmp=c; nSwapTmp <<= 4; c >>= 4; c |= nSwapTmp; } #include #include #include #include // !!! Do not inline if already the operators <<,>> are inline template && sizeof(T) == 2, int> = 0> static void SwapNumber(T& r) { r = OSL_SWAPWORD(r); } template && sizeof(T) == 4, int> = 0> static void SwapNumber(T& r) { r = OSL_SWAPDWORD(r); } template && 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(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(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(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(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(-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 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 pBuf( new char[ cBufLen ] ); sal_uInt32 nCount; do { nCount = ReadBytes( pBuf.get(), cBufLen ); rStream.WriteBytes( pBuf.get(), nCount ); } while( nCount == cBufLen ); return *this; } template 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(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::WriteStream( SvStream& rStream ) { const sal_uInt32 cBufLen = 0x8000; std::unique_ptr 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 pBuf( new char[ cBufLen ] ); sal_uInt32 nCurBufLen = cBufLen; sal_uInt32 nCount; sal_uInt64 nWriteSize = nSize; do { nCurBufLen = std::min(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(nCount); m_pBufPos += nCount; m_nBufFree = m_nBufFree - static_cast(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(nCountTmp); if( nCount > nCountTmp ) { nCount = nCountTmp; // trim count back, EOF see below } memcpy( pData, m_pRWBuf.get(), nCount ); m_nBufActualPos = static_cast(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(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(nCount); m_pBufPos = m_pRWBuf.get() + nCount; m_nBufActualLen = static_cast(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(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(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(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(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(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(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(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(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(nNewPos - nSize + 1); nDiff += static_cast(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(nSize); nTemp += nDiff; std::size_t nNewSize = static_cast(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(nNewSize) - static_cast(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(nLen, SAL_MAX_INT32); //limit allocation to size of file, but + 1 to set eof state nLen = std::min(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(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(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(nLen, SAL_MAX_INT32); //limit allocation to size of file, but + 1 to set eof state nLen = o3tl::sanitizing_min(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(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(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 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(rIn, eLineEnd); } OUString convertLineEnd(const OUString &rIn, LineEnd eLineEnd) { return tmpl_convertLineEnd(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(rStr.size(), std::numeric_limits::max()); SAL_WARN_IF(static_cast(nUnits) != static_cast(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(rStr.size(), std::numeric_limits::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(rStr.size(), std::numeric_limits::max()); SAL_WARN_IF(static_cast(nUnits) != static_cast(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: */