Files
libreoffice/tools/source/ref/pstm.cxx
Noel Grandin b24a9f8b35 make o3tl::strong_int constructor explicit
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>
2017-03-14 06:08:20 +00:00

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: */