1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <tools/pstm.hxx>
22 #include <rtl/strbuf.hxx>
24 #define STOR_NO_OPTIMIZE
26 void SvClassManager::Register( sal_Int32 nClassId
, SvCreateInstancePersist pFunc
)
29 SvCreateInstancePersist p
;
31 DBG_ASSERT( !p
|| p
== pFunc
, "register class with same id" );
33 aAssocTable
.insert(Map::value_type(nClassId
, pFunc
));
36 SvCreateInstancePersist
SvClassManager::Get( sal_Int32 nClassId
)
38 Map::const_iterator
i(aAssocTable
.find(nClassId
));
39 return i
== aAssocTable
.end() ? 0 : i
->second
;
43 TYPEINIT0( SvRttiBase
);
45 // SvPersistBaseMemberList
47 #define PERSIST_LIST_VER (sal_uInt8)0
48 #define PERSIST_LIST_DBGUTIL (sal_uInt8)0x80
50 void TOOLS_DLLPUBLIC
WritePersistListObjects(const SvPersistListWriteable
& rList
, SvPersistStream
& rStm
, bool bOnlyStreamed
)
52 #ifdef STOR_NO_OPTIMIZE
53 rStm
<< (sal_uInt8
)(PERSIST_LIST_VER
| PERSIST_LIST_DBGUTIL
);
54 sal_uInt32 nObjPos
= rStm
.WriteDummyLen();
56 sal_uInt8 bTmp
= PERSIST_LIST_VER
;
59 sal_uInt32 nCountMember
= rList
.size();
60 sal_uIntPtr nCountPos
= rStm
.Tell();
61 sal_uInt32 nWriteCount
= 0;
63 // Don't change the list, as it causes side-effects while saving
64 for( sal_uIntPtr n
= 0; n
< nCountMember
; n
++ )
66 SvPersistBase
* pObj
= rList
.GetPersistBase( n
);
67 if( !bOnlyStreamed
|| rStm
.IsStreamed( pObj
) )
68 { // Object should be stored
73 if( nWriteCount
!= nCountMember
)
75 // Didn't write all members, adjust count
76 sal_uIntPtr nPos
= rStm
.Tell();
77 rStm
.Seek( nCountPos
);
81 #ifdef STOR_NO_OPTIMIZE
82 rStm
.WriteLen( nObjPos
);
86 void TOOLS_DLLPUBLIC
ReadObjects( SvPersistListReadable
& rLst
, SvPersistStream
& rStm
)
91 if( (nVer
& ~PERSIST_LIST_DBGUTIL
) != PERSIST_LIST_VER
)
93 rStm
.SetError( SVSTREAM_GENERALERROR
);
94 OSL_FAIL( "persist list, false version" );
97 sal_uInt32
nObjLen(0), nObjPos(0);
98 if( nVer
& PERSIST_LIST_DBGUTIL
)
99 nObjLen
= rStm
.ReadLen( &nObjPos
);
103 for( sal_uIntPtr n
= 0; n
< nCount
&& rStm
.GetError() == SVSTREAM_OK
; n
++ )
105 SvPersistBase
* pObj
;
108 rLst
.push_back( pObj
);
111 if( nObjLen
+ nObjPos
!= rStm
.Tell() )
113 OStringBuffer
aStr("false list len: read = ");
114 aStr
.append(static_cast<sal_Int64
>(rStm
.Tell() - nObjPos
));
115 aStr
.append(", should = ");
116 aStr
.append(static_cast<sal_Int64
>(nObjLen
));
117 OSL_FAIL(aStr
.getStr());
126 @param rMgr Stores factories for objects that can persisted
127 @param pStream This stream is used as the medium for PersistStream
128 @param nStartIdxP Start value for object identifier (set > 0 )
130 @warning Objects rMgr and pStream must not be manipulated while used in
131 SvPersistStream. An Exception to this is pvStream
132 (cf. <SvPersistStream::SetStream>).
133 @see SvPersistStream::SetStream
135 SvPersistStream::SvPersistStream( SvClassManager
& rMgr
, SvStream
* pStream
, sal_uInt32 nStartIdxP
)
138 , aPUIdx( nStartIdxP
)
139 , nStartIdx( nStartIdxP
)
143 DBG_ASSERT( nStartIdx
!= 0, "zero index not allowed" );
144 bIsWritable
= sal_True
;
147 SetVersion( pStm
->GetVersion() );
148 SetError( pStm
->GetError() );
149 SyncSvStream( pStm
->Tell() );
153 SvPersistStream::~SvPersistStream()
160 @param pStream This stream is used as the medium for PersistStream
162 @warning pStream is used as the medium for PersistStream.
163 It must not be manipulated while used in SvPersistStream.
164 An Exception to this is pvStream (cf. <SvPersistStream::SetStream>).
166 void SvPersistStream::SetStream( SvStream
* pStream
)
168 if( pStm
!= pStream
)
173 pStm
->SetError( GetError() );
179 SetVersion( pStm
->GetVersion() );
180 SetError( pStm
->GetError() );
181 SyncSvStream( pStm
->Tell() );
185 /** Returns the identifier of this stream class.
187 @return ID_PERSISTSTREAM
191 sal_uInt16
SvPersistStream::IsA() const
193 return ID_PERSISTSTREAM
;
196 void SvPersistStream::ResetError()
198 SvStream::ResetError();
199 DBG_ASSERT( pStm
, "stream not set" );
203 sal_uIntPtr
SvPersistStream::GetData( void* pData
, sal_uIntPtr nSize
)
205 DBG_ASSERT( pStm
, "stream not set" );
206 sal_uIntPtr nRet
= pStm
->Read( pData
, nSize
);
207 SetError( pStm
->GetError() );
211 sal_uIntPtr
SvPersistStream::PutData( const void* pData
, sal_uIntPtr nSize
)
213 DBG_ASSERT( pStm
, "stream not set" );
214 sal_uIntPtr nRet
= pStm
->Write( pData
, nSize
);
215 SetError( pStm
->GetError() );
219 sal_uIntPtr
SvPersistStream::SeekPos( sal_uIntPtr nPos
)
221 DBG_ASSERT( pStm
, "stream not set" );
222 sal_uIntPtr nRet
= pStm
->Seek( nPos
);
223 SetError( pStm
->GetError() );
227 void SvPersistStream::FlushData()
231 sal_uIntPtr
SvPersistStream::GetIndex( SvPersistBase
* pObj
) const
233 PersistBaseMap::const_iterator it
= aPTable
.find( pObj
);
234 if( it
== aPTable
.end() )
237 return pRefStm
->GetIndex( pObj
);
244 SvPersistBase
* SvPersistStream::GetObject( sal_uIntPtr nIdx
) const
246 if( nIdx
>= nStartIdx
)
247 return aPUIdx
.Get( nIdx
);
249 return pRefStm
->GetObject( nIdx
);
258 /** Reads a compressed word from the stream.
260 For details on what format is used for compression, see
261 <SvPersistStream::WriteCompressed>.
263 @param rStm Source to read compressed data from
265 @return Uncompressed word
266 @see SvPersistStream::WriteCompressed
268 sal_uInt32
SvPersistStream::ReadCompressed( SvStream
& rStm
)
274 nRet
= ~LEN_1
& nMask
;
275 else if( nMask
& LEN_2
)
277 nRet
= ~LEN_2
& nMask
;
282 else if( nMask
& LEN_4
)
284 nRet
= ~LEN_4
& nMask
;
293 else if( nMask
& LEN_5
)
297 rStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
298 OSL_FAIL( "format error" );
304 rStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
305 OSL_FAIL( "format error" );
310 /** Writes compressed stream
312 @param rStm Source for writing compressed data
313 @param nVal This value will be compressed and written
315 nVal is compressed and written to stream using the following algorithm
316 nVal < 0x80 => 0x80 + nVal of size 1 Byte.
317 nVal < 0x4000 => 0x4000 + nVal of size 2 Byte.
318 nVal < 0x20000000 => 0x20000000 + nVal of size 4 Byte.
319 nVal > 0x1FFFFFFF => 0x1000000000+ nVal of size 5 Byte.
321 @see SvPersistStream::ReadCompressed
323 void SvPersistStream::WriteCompressed( SvStream
& rStm
, sal_uInt32 nVal
)
325 #ifdef STOR_NO_OPTIMIZE
327 rStm
<< (sal_uInt8
)(LEN_1
| nVal
);
328 else if( nVal
< 0x4000 )
330 rStm
<< (sal_uInt8
)(LEN_2
| (nVal
>> 8));
331 rStm
<< (sal_uInt8
)nVal
;
333 else if( nVal
< 0x20000000 )
336 rStm
<< (sal_uInt8
)(LEN_4
| (nVal
>> 24));
337 // 2nd highest sal_uInt8
338 rStm
<< (sal_uInt8
)(nVal
>> 16);
339 rStm
<< (sal_uInt16
)(nVal
);
344 rStm
<< (sal_uInt8
)LEN_5
;
349 /** This method writes length value of 4 Bytes to the stream and returns the
354 sal_uInt32 nObjPos = rStm.WriteDummyLen();
358 rStm.WriteLen( nObjPos );
361 @return Position of stream behind length value
363 @see SvPersistStream::ReadLen
364 @see SvPersistStream::WriteLen
366 sal_uInt32
SvPersistStream::WriteDummyLen()
369 sal_uInt32 nPos
= Tell();
372 *this << n0
; // Because of Sun sp
373 // Don't assert on stream error
374 DBG_ASSERT( GetError() != SVSTREAM_OK
375 || (sizeof( sal_uInt32
) == Tell() -nPos
),
376 "No 4 byte as length parameter" );
380 /** Write difference between current position and nObjPos
381 as sal_uInt32 to position nObjPos-4 in the stream.
383 Afterwards, reset stream to old position.
385 Example: Difference does not contain length value
387 sal_uInt32 nObjPos = rStm.WriteDummyLen();
391 rStm.WriteLen( nObjPos );
395 @param nObjPos Position+4, on which length is written to
397 @see SvPersistStream::ReadLen
398 @see SvPersistStream::WriteDummyLen
400 void SvPersistStream::WriteLen( sal_uInt32 nObjPos
)
402 sal_uInt32 nPos
= Tell();
403 sal_uInt32 nLen
= nPos
- nObjPos
;
404 // Length in stream must be 4 Bytes
405 Seek( nObjPos
- sizeof( sal_uInt32
) );
411 /** Read a length value written to stream
413 @param pTestPos Position of the stream after reading length. May be NULL.
415 @see SvPersistStream::WriteDummyLen
416 @see SvPersistStream::WriteLen
418 sal_uInt32
SvPersistStream::ReadLen( sal_uInt32
* pTestPos
)
427 // File format backward-compatible
428 #ifdef STOR_NO_OPTIMIZE
429 #define P_VER (sal_uInt8)0x00
431 #define P_VER (sal_uInt8)0x01
433 #define P_VER_MASK (sal_uInt8)0x0F
434 #define P_ID_0 (sal_uInt8)0x80
435 #define P_OBJ (sal_uInt8)0x40
436 #define P_DBGUTIL (sal_uInt8)0x20
437 #define P_ID (sal_uInt8)0x10
438 #ifdef STOR_NO_OPTIMIZE
439 #define P_STD P_DBGUTIL
452 #ifdef STOR_NO_OPTIMIZE
458 if( (nHdr
& P_OBJ
) || nId
!= 0 )
459 { // Id set only for pointers or DBGUTIL
460 rStm
<< (sal_uInt8
)(nHdr
);
461 SvPersistStream::WriteCompressed( rStm
, nId
);
465 rStm
<< (sal_uInt8
)(nHdr
| P_ID_0
);
472 if( (nHdr
& P_DBGUTIL
) || (nHdr
& P_OBJ
) )
473 // Objects always have a class id
474 // Pointers only for DBG_UTIL and != NULL
475 SvPersistStream::WriteCompressed( rStm
, nClassId
);
483 sal_uInt16
& nClassId
492 if( (nHdr
& P_VER_MASK
) == 0 )
494 if( (nHdr
& P_DBGUTIL
) || !(nHdr
& P_OBJ
) )
495 nId
= SvPersistStream::ReadCompressed( rStm
);
499 else if( nHdr
& P_ID
)
500 nId
= SvPersistStream::ReadCompressed( rStm
);
502 if( (nHdr
& P_DBGUTIL
) || (nHdr
& P_OBJ
) )
503 nClassId
= (sal_uInt16
)SvPersistStream::ReadCompressed( rStm
);
507 void SvPersistStream::WriteObj
513 #ifdef STOR_NO_OPTIMIZE
514 sal_uInt32 nObjPos
= 0;
515 if( nHdr
& P_DBGUTIL
)
516 // remember position for length value
517 nObjPos
= WriteDummyLen();
520 #ifdef STOR_NO_OPTIMIZE
521 if( nHdr
& P_DBGUTIL
)
526 SvPersistStream
& SvPersistStream::WritePointer
531 sal_uInt8 nP
= P_STD
;
535 sal_uIntPtr nId
= GetIndex( pObj
);
540 nId
= aPUIdx
.Insert( pObj
);
541 aPTable
[ pObj
] = nId
;
544 WriteId( *this, nP
, nId
, pObj
->GetClassId() );
546 WriteObj( nP
, pObj
);
550 WriteId( *this, nP
| P_ID
, 0, 0 );
555 sal_uInt32
SvPersistStream::ReadObj
557 SvPersistBase
* & rpObj
,
565 rpObj
= NULL
; // specification: 0 in case of error
566 ReadId( *this, nHdr
, nId
, nClassId
);
568 // get version nummer through masking
569 if( P_VER
< (nHdr
& P_VER_MASK
) )
571 SetError( SVSTREAM_FILEFORMAT_ERROR
);
572 OSL_FAIL( "false version" );
575 if( !(nHdr
& P_ID_0
) && GetError() == SVSTREAM_OK
)
578 { // read object, nId only set for P_DBGUTIL
579 DBG_ASSERT( !(nHdr
& P_DBGUTIL
) || NULL
== aPUIdx
.Get( nId
),
580 "object already exist" );
581 SvCreateInstancePersist pFunc
= rClassMgr
.Get( nClassId
);
583 sal_uInt32
nObjLen(0), nObjPos(0);
584 if( nHdr
& P_DBGUTIL
)
585 nObjLen
= ReadLen( &nObjPos
);
589 OStringBuffer
aStr("no class with id: " );
590 aStr
.append(static_cast<sal_Int32
>(nClassId
));
591 aStr
.append(" registered");
592 DBG_WARNING(aStr
.getStr());
596 SetError( ERRCODE_IO_NOFACTORY
);
606 sal_uIntPtr nNewId
= aPUIdx
.Insert( rpObj
);
607 // in order to restore state after saving
608 aPTable
[ rpObj
] = nNewId
;
609 DBG_ASSERT( !(nHdr
& P_DBGUTIL
) || nId
== nNewId
,
610 "read write id conflict: not the same" );
612 rpObj
->Load( *this );
614 if( nObjLen
+ nObjPos
!= Tell() )
616 OStringBuffer
aStr("false object len: read = ");
617 aStr
.append(static_cast<sal_Int64
>((long)(Tell() - nObjPos
)));
618 aStr
.append(", should = ");
619 aStr
.append(static_cast<sal_Int32
>(nObjLen
));
620 OSL_FAIL(aStr
.getStr());
623 rpObj
->RestoreNoDelete();
628 rpObj
= GetObject( nId
);
629 DBG_ASSERT( rpObj
!= NULL
, "object does not exist" );
630 DBG_ASSERT( rpObj
->GetClassId() == nClassId
, "class mismatch" );
636 SvPersistStream
& SvPersistStream::ReadPointer
638 SvPersistBase
* & rpObj
641 ReadObj( rpObj
, sal_True
);
645 SvPersistStream
& operator <<
647 SvPersistStream
& rStm
,
651 return rStm
.WritePointer( pObj
);
654 SvPersistStream
& operator >>
656 SvPersistStream
& rStm
,
657 SvPersistBase
* & rpObj
660 return rStm
.ReadPointer( rpObj
);
663 SvStream
& operator <<
666 SvPersistStream
& rThis
669 SvStream
* pOldStm
= rThis
.GetStream();
670 rThis
.SetStream( &rStm
);
673 rThis
<< bTmp
; // Version
674 sal_uInt32 nCount
= (sal_uInt32
)rThis
.aPUIdx
.Count();
676 sal_uIntPtr aIndex
= rThis
.aPUIdx
.FirstIndex();
677 for( sal_uInt32 i
= 0; i
< nCount
; i
++ )
679 SvPersistBase
* pEle
= rThis
.aPUIdx
.Get(aIndex
);
680 sal_uInt8 nP
= P_OBJ
| P_ID
| P_STD
;
681 WriteId( rThis
, nP
, aIndex
, pEle
->GetClassId() );
682 rThis
.WriteObj( nP
, pEle
);
683 aIndex
= rThis
.aPUIdx
.NextIndex( aIndex
);
685 rThis
.SetStream( pOldStm
);
689 SvStream
& operator >>
692 SvPersistStream
& rThis
695 SvStream
* pOldStm
= rThis
.GetStream();
696 rThis
.SetStream( &rStm
);
699 rThis
>> nVers
; // Version
702 sal_uInt32 nCount
= 0;
704 for( sal_uInt32 i
= 0; i
< nCount
; i
++ )
706 SvPersistBase
* pEle
;
707 // read, but don't insert into table
708 sal_uIntPtr nId
= rThis
.ReadObj( pEle
, sal_False
);
709 if( rThis
.GetError() )
712 // Id of object is never modified
713 rThis
.aPUIdx
.Insert( nId
, pEle
);
714 rThis
.aPTable
[ pEle
] = nId
;
718 rThis
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
720 rThis
.SetStream( pOldStm
);
724 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */