Could have sworn I did this originally, somehow it got lost.
Also fix a thinko in tools::ResId I introduced in commit
2b70fb58be
"Use o3tl::strong_int on RESOURCE_TYPE"
Change-Id: Id3b39962255297010cd1feaaca6d822168311081
Reviewed-on: https://gerrit.libreoffice.org/35108
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
530 lines
13 KiB
C++
530 lines
13 KiB
C++
/* -*- 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 .
|
|
*/
|
|
|
|
#include <tools/debug.hxx>
|
|
#include <tools/pstm.hxx>
|
|
#include <rtl/strbuf.hxx>
|
|
#include <osl/diagnose.h>
|
|
|
|
#define STOR_NO_OPTIMIZE
|
|
|
|
void SvClassManager::Register( sal_Int32 nClassId, SvCreateInstancePersist pFunc )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
SvCreateInstancePersist p;
|
|
p = Get( nClassId );
|
|
DBG_ASSERT( !p || p == pFunc, "register class with same id" );
|
|
#endif
|
|
aAssocTable.insert(Map::value_type(nClassId, pFunc));
|
|
}
|
|
|
|
SvCreateInstancePersist SvClassManager::Get( sal_Int32 nClassId )
|
|
{
|
|
Map::const_iterator i(aAssocTable.find(nClassId));
|
|
return i == aAssocTable.end() ? nullptr : i->second;
|
|
}
|
|
|
|
/** Constructor
|
|
|
|
@param rMgr Stores factories for objects that can persisted
|
|
@param pStream This stream is used as the medium for PersistStream
|
|
@param nStartIdxP Start value for object identifier (set > 0 )
|
|
|
|
@warning Objects rMgr and pStream must not be manipulated while used in
|
|
SvPersistStream. An Exception to this is pvStream
|
|
(cf. <SvPersistStream::SetStream>).
|
|
@see SvPersistStream::SetStream
|
|
*/
|
|
SvPersistStream::SvPersistStream( SvClassManager & rMgr, SvStream * pStream )
|
|
: rClassMgr( rMgr )
|
|
, pStm( pStream )
|
|
, aPUIdx( UniqueIndex<SvPersistBase>::Index(1) )
|
|
, nStartIdx( 1 )
|
|
{
|
|
DBG_ASSERT( nStartIdx != Index(0), "zero index not allowed" );
|
|
m_isWritable = true;
|
|
if( pStm )
|
|
{
|
|
SetVersion( pStm->GetVersion() );
|
|
SetError( pStm->GetError() );
|
|
SyncSvStream( pStm->Tell() );
|
|
}
|
|
}
|
|
|
|
SvPersistStream::~SvPersistStream()
|
|
{
|
|
if( pStm != nullptr )
|
|
{
|
|
SyncSysStream();
|
|
pStm->SetError( GetError() );
|
|
pStm = nullptr;
|
|
}
|
|
}
|
|
|
|
void SvPersistStream::ResetError()
|
|
{
|
|
SvStream::ResetError();
|
|
DBG_ASSERT( pStm, "stream not set" );
|
|
pStm->ResetError();
|
|
}
|
|
|
|
std::size_t SvPersistStream::GetData( void* pData, std::size_t nSize )
|
|
{
|
|
DBG_ASSERT( pStm, "stream not set" );
|
|
std::size_t const nRet = pStm->ReadBytes( pData, nSize );
|
|
SetError( pStm->GetError() );
|
|
return nRet;
|
|
}
|
|
|
|
std::size_t SvPersistStream::PutData( const void* pData, std::size_t nSize )
|
|
{
|
|
DBG_ASSERT( pStm, "stream not set" );
|
|
std::size_t const nRet = pStm->WriteBytes( pData, nSize );
|
|
SetError( pStm->GetError() );
|
|
return nRet;
|
|
}
|
|
|
|
sal_uInt64 SvPersistStream::SeekPos(sal_uInt64 const nPos)
|
|
{
|
|
DBG_ASSERT( pStm, "stream not set" );
|
|
sal_uInt64 nRet = pStm->Seek( nPos );
|
|
SetError( pStm->GetError() );
|
|
return nRet;
|
|
}
|
|
|
|
void SvPersistStream::FlushData()
|
|
{
|
|
}
|
|
|
|
SvPersistStream::Index SvPersistStream::GetIndex( SvPersistBase * pObj ) const
|
|
{
|
|
PersistBaseMap::const_iterator it = aPTable.find( pObj );
|
|
if( it == aPTable.end() )
|
|
{
|
|
return Index(0);
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
SvPersistBase * SvPersistStream::GetObject( Index nIdx ) const
|
|
{
|
|
if( nIdx >= nStartIdx )
|
|
return aPUIdx.Get( nIdx );
|
|
return nullptr;
|
|
}
|
|
|
|
#define LEN_1 0x80
|
|
#define LEN_2 0x40
|
|
#define LEN_4 0x20
|
|
#define LEN_5 0x10
|
|
|
|
/** Reads a compressed word from the stream.
|
|
|
|
For details on what format is used for compression, see
|
|
<SvPersistStream::WriteCompressed>.
|
|
|
|
@param rStm Source to read compressed data from
|
|
|
|
@return Uncompressed word
|
|
@see SvPersistStream::WriteCompressed
|
|
*/
|
|
sal_uInt32 SvPersistStream::ReadCompressed( SvStream & rStm )
|
|
{
|
|
sal_uInt32 nRet(0);
|
|
sal_uInt8 nMask;
|
|
rStm.ReadUChar( nMask );
|
|
if( nMask & LEN_1 )
|
|
nRet = ~LEN_1 & nMask;
|
|
else if( nMask & LEN_2 )
|
|
{
|
|
nRet = ~LEN_2 & nMask;
|
|
nRet <<= 8;
|
|
rStm.ReadUChar( nMask );
|
|
nRet |= nMask;
|
|
}
|
|
else if( nMask & LEN_4 )
|
|
{
|
|
nRet = ~LEN_4 & nMask;
|
|
nRet <<= 8;
|
|
rStm.ReadUChar( nMask );
|
|
nRet |= nMask;
|
|
nRet <<= 16;
|
|
sal_uInt16 n;
|
|
rStm.ReadUInt16( n );
|
|
nRet |= n;
|
|
}
|
|
else if( nMask & LEN_5 )
|
|
{
|
|
if( nMask & 0x0F )
|
|
{
|
|
rStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
|
|
OSL_FAIL( "format error" );
|
|
}
|
|
rStm.ReadUInt32( nRet );
|
|
}
|
|
else
|
|
{
|
|
rStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
|
|
OSL_FAIL( "format error" );
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
/** Writes compressed stream
|
|
|
|
@param rStm Source for writing compressed data
|
|
@param nVal This value will be compressed and written
|
|
|
|
nVal is compressed and written to stream using the following algorithm
|
|
nVal < 0x80 => 0x80 + nVal of size 1 Byte.
|
|
nVal < 0x4000 => 0x4000 + nVal of size 2 Byte.
|
|
nVal < 0x20000000 => 0x20000000 + nVal of size 4 Byte.
|
|
nVal > 0x1FFFFFFF => 0x1000000000+ nVal of size 5 Byte.
|
|
|
|
@see SvPersistStream::ReadCompressed
|
|
*/
|
|
void SvPersistStream::WriteCompressed( SvStream & rStm, sal_uInt32 nVal )
|
|
{
|
|
#ifdef STOR_NO_OPTIMIZE
|
|
if( nVal < 0x80 )
|
|
rStm.WriteUChar( LEN_1 | nVal );
|
|
else if( nVal < 0x4000 )
|
|
{
|
|
rStm.WriteUChar( LEN_2 | (nVal >> 8) );
|
|
rStm.WriteUChar( nVal );
|
|
}
|
|
else if( nVal < 0x20000000 )
|
|
{
|
|
// highest sal_uInt8
|
|
rStm.WriteUChar( LEN_4 | (nVal >> 24) );
|
|
// 2nd highest sal_uInt8
|
|
rStm.WriteUChar( nVal >> 16 );
|
|
rStm.WriteUInt16( nVal );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
rStm.WriteUChar( LEN_5 );
|
|
rStm.WriteUInt32( nVal );
|
|
}
|
|
}
|
|
|
|
/** This method writes length value of 4 Bytes to the stream and returns the
|
|
stream position.
|
|
|
|
Example:
|
|
@code
|
|
sal_uInt32 nObjPos = rStm.WriteDummyLen();
|
|
...
|
|
// write data
|
|
...
|
|
rStm.WriteLen( nObjPos );
|
|
@endcode
|
|
|
|
@return Position of stream behind length value
|
|
|
|
@see SvPersistStream::ReadLen
|
|
@see SvPersistStream::WriteLen
|
|
*/
|
|
sal_uInt32 SvPersistStream::WriteDummyLen()
|
|
{
|
|
WriteUInt32( 0 );
|
|
return Tell();
|
|
}
|
|
|
|
/** Write difference between current position and nObjPos
|
|
as sal_uInt32 to position nObjPos-4 in the stream.
|
|
|
|
Afterwards, reset stream to old position.
|
|
|
|
Example: Difference does not contain length value
|
|
@code
|
|
sal_uInt32 nObjPos = rStm.WriteDummyLen();
|
|
...
|
|
// write data
|
|
...
|
|
rStm.WriteLen( nObjPos );
|
|
// write more data
|
|
@endcode
|
|
|
|
@param nObjPos Position+4, on which length is written to
|
|
|
|
@see SvPersistStream::ReadLen
|
|
@see SvPersistStream::WriteDummyLen
|
|
*/
|
|
void SvPersistStream::WriteLen( sal_uInt32 nObjPos )
|
|
{
|
|
sal_uInt32 nPos = Tell();
|
|
sal_uInt32 nLen = nPos - nObjPos;
|
|
// Length in stream must be 4 Bytes
|
|
Seek( nObjPos - sizeof( sal_uInt32 ) );
|
|
// write length
|
|
WriteUInt32( nLen );
|
|
Seek( nPos );
|
|
}
|
|
|
|
/** Read a length value written to stream
|
|
|
|
@param pTestPos Position of the stream after reading length. May be NULL.
|
|
|
|
@see SvPersistStream::WriteDummyLen
|
|
@see SvPersistStream::WriteLen
|
|
*/
|
|
sal_uInt32 SvPersistStream::ReadLen( sal_uInt32 * pTestPos )
|
|
{
|
|
sal_uInt32 nLen;
|
|
ReadUInt32( nLen );
|
|
if( pTestPos )
|
|
*pTestPos = Tell();
|
|
return nLen;
|
|
}
|
|
|
|
// File format backward-compatible
|
|
#ifdef STOR_NO_OPTIMIZE
|
|
#define P_VER (sal_uInt8)0x00
|
|
#else
|
|
#define P_VER (sal_uInt8)0x01
|
|
#endif
|
|
#define P_VER_MASK (sal_uInt8)0x0F
|
|
#define P_ID_0 (sal_uInt8)0x80
|
|
#define P_OBJ (sal_uInt8)0x40
|
|
#define P_DBGUTIL (sal_uInt8)0x20
|
|
#define P_ID (sal_uInt8)0x10
|
|
#ifdef STOR_NO_OPTIMIZE
|
|
#define P_STD P_DBGUTIL
|
|
#else
|
|
#define P_STD 0
|
|
#endif
|
|
|
|
static void WriteId
|
|
(
|
|
SvStream & rStm,
|
|
sal_uInt8 nHdr,
|
|
sal_uInt32 nId,
|
|
sal_uInt16 nClassId
|
|
)
|
|
{
|
|
#ifdef STOR_NO_OPTIMIZE
|
|
nHdr |= P_ID;
|
|
#endif
|
|
nHdr |= P_VER;
|
|
if( nHdr & P_ID )
|
|
{
|
|
if( (nHdr & P_OBJ) || nId != 0 )
|
|
{ // Id set only for pointers or DBGUTIL
|
|
rStm.WriteUChar( nHdr );
|
|
SvPersistStream::WriteCompressed( rStm, nId );
|
|
}
|
|
else
|
|
{ // NULL Pointer
|
|
rStm.WriteUChar( nHdr | P_ID_0 );
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
rStm.WriteUChar( nHdr );
|
|
|
|
if( (nHdr & P_DBGUTIL) || (nHdr & P_OBJ) )
|
|
// Objects always have a class id
|
|
// Pointers only for DBG_UTIL and != NULL
|
|
SvPersistStream::WriteCompressed( rStm, nClassId );
|
|
}
|
|
|
|
static void ReadId
|
|
(
|
|
SvStream & rStm,
|
|
sal_uInt8 & nHdr,
|
|
SvPersistStream::Index & nId,
|
|
sal_uInt16 & nClassId
|
|
)
|
|
{
|
|
nClassId = 0;
|
|
rStm.ReadUChar( nHdr );
|
|
if( nHdr & P_ID_0 )
|
|
nId = SvPersistStream::Index(0);
|
|
else
|
|
{
|
|
if( (nHdr & P_VER_MASK) == 0 )
|
|
{
|
|
if( (nHdr & P_DBGUTIL) || !(nHdr & P_OBJ) )
|
|
nId = SvPersistStream::Index(SvPersistStream::ReadCompressed( rStm ));
|
|
else
|
|
nId = SvPersistStream::Index(0);
|
|
}
|
|
else if( nHdr & P_ID )
|
|
nId = SvPersistStream::Index(SvPersistStream::ReadCompressed( rStm ));
|
|
|
|
if( (nHdr & P_DBGUTIL) || (nHdr & P_OBJ) )
|
|
nClassId = (sal_uInt16)SvPersistStream::ReadCompressed( rStm );
|
|
}
|
|
}
|
|
|
|
void SvPersistStream::WriteObj
|
|
(
|
|
sal_uInt8 nHdr,
|
|
SvPersistBase * pObj
|
|
)
|
|
{
|
|
#ifdef STOR_NO_OPTIMIZE
|
|
sal_uInt32 nObjPos = 0;
|
|
if( nHdr & P_DBGUTIL )
|
|
// remember position for length value
|
|
nObjPos = WriteDummyLen();
|
|
#endif
|
|
pObj->Save( *this );
|
|
#ifdef STOR_NO_OPTIMIZE
|
|
if( nHdr & P_DBGUTIL )
|
|
WriteLen( nObjPos );
|
|
#endif
|
|
}
|
|
|
|
SvPersistStream& SvPersistStream::WritePointer
|
|
(
|
|
SvPersistBase * pObj
|
|
)
|
|
{
|
|
sal_uInt8 nP = P_STD;
|
|
|
|
if( pObj )
|
|
{
|
|
Index nId = GetIndex( pObj );
|
|
if( nId )
|
|
nP |= P_ID;
|
|
else
|
|
{
|
|
nId = aPUIdx.Insert( pObj );
|
|
aPTable[ pObj ] = nId;
|
|
nP |= P_OBJ;
|
|
}
|
|
WriteId( *this, nP, (sal_uInt32)nId, pObj->GetClassId() );
|
|
if( nP & P_OBJ )
|
|
WriteObj( nP, pObj );
|
|
}
|
|
else
|
|
{ // NULL Pointer
|
|
WriteId( *this, nP | P_ID, 0, 0 );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void SvPersistStream::ReadObj
|
|
(
|
|
SvPersistBase * & rpObj
|
|
)
|
|
{
|
|
sal_uInt8 nHdr;
|
|
Index nId(0);
|
|
sal_uInt16 nClassId;
|
|
|
|
rpObj = nullptr; // specification: 0 in case of error
|
|
ReadId( *this, nHdr, nId, nClassId );
|
|
|
|
// get version number through masking
|
|
if( P_VER < (nHdr & P_VER_MASK) )
|
|
{
|
|
SetError( SVSTREAM_FILEFORMAT_ERROR );
|
|
OSL_FAIL( "false version" );
|
|
}
|
|
|
|
if( !(nHdr & P_ID_0) && GetError() == SVSTREAM_OK )
|
|
{
|
|
if( P_OBJ & nHdr )
|
|
{ // read object, nId only set for P_DBGUTIL
|
|
DBG_ASSERT( !(nHdr & P_DBGUTIL) || nullptr == aPUIdx.Get( nId ),
|
|
"object already exist" );
|
|
SvCreateInstancePersist pFunc = rClassMgr.Get( nClassId );
|
|
|
|
sal_uInt32 nObjLen(0), nObjPos(0);
|
|
if( nHdr & P_DBGUTIL )
|
|
nObjLen = ReadLen( &nObjPos );
|
|
if( !pFunc )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
OStringBuffer aStr("no class with id: " );
|
|
aStr.append(static_cast<sal_Int32>(nClassId));
|
|
aStr.append(" registered");
|
|
SAL_INFO("tools", aStr.getStr());
|
|
#else
|
|
(void)nObjLen;
|
|
#endif
|
|
SetError( ERRCODE_IO_NOFACTORY );
|
|
return;
|
|
}
|
|
pFunc( &rpObj );
|
|
// Save reference
|
|
rpObj->AddFirstRef();
|
|
|
|
// insert into table
|
|
const Index nNewId = aPUIdx.Insert( rpObj );
|
|
// in order to restore state after saving
|
|
aPTable[ rpObj ] = nNewId;
|
|
DBG_ASSERT( !(nHdr & P_DBGUTIL) || nId == nNewId,
|
|
"read write id conflict: not the same" );
|
|
rpObj->Load( *this );
|
|
#ifdef DBG_UTIL
|
|
if( nObjLen + nObjPos != Tell() )
|
|
{
|
|
OStringBuffer aStr("false object len: read = ");
|
|
aStr.append(static_cast<sal_Int64>((long)(Tell() - nObjPos)));
|
|
aStr.append(", should = ");
|
|
aStr.append(static_cast<sal_Int32>(nObjLen));
|
|
OSL_FAIL(aStr.getStr());
|
|
}
|
|
#endif
|
|
rpObj->RestoreNoDelete();
|
|
rpObj->ReleaseRef();
|
|
}
|
|
else
|
|
{
|
|
rpObj = GetObject( nId );
|
|
DBG_ASSERT( rpObj != nullptr, "object does not exist" );
|
|
DBG_ASSERT( rpObj->GetClassId() == nClassId, "class mismatch" );
|
|
}
|
|
}
|
|
}
|
|
|
|
SvPersistStream& SvPersistStream::ReadPointer
|
|
(
|
|
SvPersistBase * & rpObj
|
|
)
|
|
{
|
|
ReadObj( rpObj );
|
|
return *this;
|
|
}
|
|
|
|
SvPersistStream& WriteSvPersistBase
|
|
(
|
|
SvPersistStream & rStm,
|
|
SvPersistBase * pObj
|
|
)
|
|
{
|
|
return rStm.WritePointer( pObj );
|
|
}
|
|
|
|
SvPersistStream& operator >>
|
|
(
|
|
SvPersistStream & rStm,
|
|
SvPersistBase * & rpObj
|
|
)
|
|
{
|
|
return rStm.ReadPointer( rpObj );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|