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 <svl/filerec.hxx>
21 #include <osl/endian.h>
23 //========================================================================
25 /* Die folgenden Makros extrahieren Teilbereiche aus einem sal_uInt32 Wert.
26 Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
30 #define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
31 #define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
32 #define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
33 #define SFX_REC_VER(n) ( ((n) & 0x0000FF00) >> 8 )
34 #define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )
36 #define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
37 #define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
39 //-------------------------------------------------------------------------
41 /* Die folgenden Makros setzen Teilbereiche zu einem sal_uInt32 Wert zusammen.
42 Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
46 #define SFX_REC_MINI_HEADER(nPreTag,nStartPos,nEndPos) \
47 ( sal_uInt32(nPreTag) | \
48 sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 )
50 #define SFX_REC_HEADER(nRecType,nContentTag,nContentVer) \
51 ( sal_uInt32(nRecType) | \
52 ( sal_uInt32(nContentVer) << 8 ) | \
53 ( sal_uInt32(nContentTag) << 16 ) )
55 #define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
56 ( sal_uInt32(nContentVer) | \
57 sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )
59 //=========================================================================
61 sal_uInt32
SfxMiniRecordWriter::Close
63 bool bSeekToEndOfRec
/* true (default)
64 Der Stream wird an das Ende des Records
68 Der Stream wird an den Anfang des
69 Contents (also hinter den Header)
76 Diese Methode schlie\st den Record. Dabei wird haupts"achlich der
79 Wurde der Header bereits geschrieben, hat der Aufruf keine Wirkung.
85 Position im Stream, die direkt hinter dem Record liegt.
86 'bSeekToEndOfRecord==sal_True'
87 => R"uckgabewert == aktuelle Stream-Position nach Aufruf
90 Der Header war bereits geschrieben worden.
94 // wurde der Header noch nicht geschrieben?
97 // Header an den Anfang des Records schreiben
98 sal_uInt32 nEndPos
= _pStream
->Tell();
99 _pStream
->Seek( _nStartPos
);
100 *_pStream
<< SFX_REC_MINI_HEADER( _nPreTag
, _nStartPos
, nEndPos
);
102 // je nachdem ans Ende des Records seeken oder hinter Header bleiben
103 if ( bSeekToEndOfRec
)
104 _pStream
->Seek( nEndPos
);
106 // Header wurde JETZT geschrieben
111 // Record war bereits geschlossen
115 bool SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader
)
119 Interne Methode zum nachtr"aglichen Verarbeiten eines extern gelesenen
120 Headers. Falls der Header eine End-Of-Records-Kennung darstellt,
121 wird am Stream ein Errorcode gesetzt und sal_False zur"uckgeliefert. Im
122 Fehlerfall wird der Stream jedoch nicht auf den Record-Anfang zur"uck-
129 // Record-Ende und Pre-Tag aus dem Header ermitteln
130 _nEofRec
= _pStream
->Tell() + SFX_REC_OFS(nHeader
);
131 _nPreTag
= sal::static_int_cast
< sal_uInt8
>(SFX_REC_PRE(nHeader
));
133 // wenn End-Of-Record-Kennung, dann Fehler
134 if ( _nPreTag
== SFX_REC_PRETAG_EOR
)
136 _pStream
->SetError( ERRCODE_IO_WRONGFORMAT
);
142 SfxMiniRecordReader::SfxMiniRecordReader
144 SvStream
* pStream
, /* <SvStream>, an dessen aktueller
145 Position sich ein <SfxMiniRecord>
148 sal_uInt8 nTag
// Pre-Tag des gew"unschten Records
153 Dieser Ctor interpretiert 'pStream' ab der aktuellen Position als
154 eine l"uckenlose Folge von, von dieser Klassen-Gruppe interpretierbaren,
155 Records. Der in dieser Folge erste als <SfxMiniRecord> interpretierbare
156 (also ggf. auch ein extended-Record) mit dem PreTag 'nTag' wird ge"offnet
157 und durch diese Instanz repr"asentiert.
159 Wird das Ende des Streams oder die Kennung SFX_REC_PRETAG_EOR
160 erreicht, bevor ein Record mit dem ge"unschten Pre-Tag gefunden wird,
161 ist die erzeugte Instanz ung"ultig ('IsValid() == sal_False'). Ein ent-
162 sprechender Error-Code (ERRCODE_IO_EOF bzw. ERRCODE_IO_WRONGFORMAT)
163 ist dann am Stream gesetzt, dessen Position ist dann au\serdem unver-
166 Bei 'nTag==SFX_FILEREC_PRETAG_EOR' wird nicht versucht, einen Record
167 zu lesen, es wird sofort 'IsValid()' auf sal_False gesetzt und kein Error-Code
168 am Stream gesetzt. Dies ist dauzu gedacht, ohne 'new' und 'delete'
169 abw"rtskompatibel SfxMiniRecords einbauen zu k"onnen. Siehe dazu
170 <SfxItemSet::Load()>.
173 [Anwendungsvorschlag]
175 Wird dieser Ctor in einer bereits ausgelieferten Programmversion
176 verwendet, k"onnen in das File-Format jeweils davor kompatibel neue
177 Records mit einer anderen Kennung eingef"ugt werden. Diese werden
178 schlie\slich automatisch "uberlesen. Erkauft wird diese M"oglichkeit
179 allerdings mit etwas schlechterem Laufzeitverhalten im Vergleich mit
180 direktem 'drauf-los-lesen', der sich jedoch auf einen Vergleich zweier
181 Bytes reduziert, falls der gesuchte Record der erste in der Folge ist.
184 : _pStream( pStream
),
185 _bSkipped( nTag
== SFX_REC_PRETAG_EOR
)
187 // ggf. ignorieren (s.o.)
194 // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
195 sal_uInt32 nStartPos
= pStream
->Tell();
197 // passenden Record suchen
201 DBG( DbgOutf( "SfxFileRec: searching record at %ul", pStream
->Tell() ) );
205 // Headerdaten von Basisklasse extrahieren lassen
206 SetHeader_Impl( nHeader
);
208 // ggf. Fehler behandeln
209 if ( pStream
->IsEof() )
210 _nPreTag
= SFX_REC_PRETAG_EOR
;
211 else if ( _nPreTag
== SFX_REC_PRETAG_EOR
)
212 pStream
->SetError( ERRCODE_IO_WRONGFORMAT
);
215 // wenn gefunden, dann Schleife abbrechen
216 if ( _nPreTag
== nTag
)
219 // sonst skippen und weitersuchen
220 pStream
->Seek( _nEofRec
);
224 // Fehler => zur"uck-seeken
225 pStream
->Seek( nStartPos
);
230 //=========================================================================
232 SfxSingleRecordWriter::SfxSingleRecordWriter
234 sal_uInt8 nRecordType
, // f"ur Subklassen
235 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
236 sal_uInt16 nContentTag
, // Inhalts-Art-Kennung
237 sal_uInt8 nContentVer
// Inhalts-Versions-Kennung
242 Interner Ctor f"ur Subklassen.
245 : SfxMiniRecordWriter( pStream
, SFX_REC_PRETAG_EXT
)
247 // Erweiterten Header hiner den des SfxMiniRec schreiben
248 *pStream
<< SFX_REC_HEADER(nRecordType
, nContentTag
, nContentVer
);
251 //=========================================================================
253 inline bool SfxSingleRecordReader::ReadHeader_Impl( sal_uInt16 nTypes
)
257 Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
258 die Basisklasse bereits initialisiert und deren Header gelesen ist.
259 Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
260 nicht zur"uckge-seekt.
266 // Basisklassen-Header einlesen
267 sal_uInt32 nHeader
=0;
268 *_pStream
>> nHeader
;
269 if ( !SetHeader_Impl( nHeader
) )
273 // eigenen Header einlesen
274 *_pStream
>> nHeader
;
275 _nRecordVer
= sal::static_int_cast
< sal_uInt8
>(SFX_REC_VER(nHeader
));
276 _nRecordTag
= sal::static_int_cast
< sal_uInt16
>(SFX_REC_TAG(nHeader
));
278 // falscher Record-Typ?
279 _nRecordType
= sal::static_int_cast
< sal_uInt8
>(SFX_REC_TYP(nHeader
));
280 bRet
= 0 != ( nTypes
& _nRecordType
);
285 //-------------------------------------------------------------------------
287 bool SfxSingleRecordReader::FindHeader_Impl
289 sal_uInt16 nTypes
, // arithm. Veroderung erlaubter Record-Typen
290 sal_uInt16 nTag
// zu findende Record-Art-Kennung
295 Interne Methode zum lesen des Headers des ersten Record, der einem
296 der Typen in 'nTypes' entspricht und mit der Art-Kennung 'nTag'
299 Kann ein solcher Record nicht gefunden werden, wird am Stream ein
300 Errorcode gesetzt, zur"uck-geseekt und sal_False zur"uckgeliefert.
304 // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
305 sal_uInt32 nStartPos
= _pStream
->Tell();
307 // richtigen Record suchen
308 while ( !_pStream
->IsEof() )
312 DBG( DbgOutf( "SfxFileRec: searching record at %ul", _pStream
->Tell() ) );
313 *_pStream
>> nHeader
;
314 if ( !SetHeader_Impl( nHeader
) )
315 // EOR => Such-Schleife abbreichen
318 // Extended Record gefunden?
319 if ( _nPreTag
== SFX_REC_PRETAG_EXT
)
321 // Extended Header lesen
322 *_pStream
>> nHeader
;
323 _nRecordTag
= sal::static_int_cast
< sal_uInt16
>(SFX_REC_TAG(nHeader
));
325 // richtigen Record gefunden?
326 if ( _nRecordTag
== nTag
)
328 // gefundener Record-Typ passend?
329 _nRecordType
= sal::static_int_cast
< sal_uInt8
>(
330 SFX_REC_TYP(nHeader
));
331 if ( nTypes
& _nRecordType
)
335 // error => Such-Schleife abbrechen
341 if ( !_pStream
->IsEof() )
342 _pStream
->Seek( _nEofRec
);
345 // Fehler setzen und zur"uck-seeken
346 _pStream
->SetError( ERRCODE_IO_WRONGFORMAT
);
347 _pStream
->Seek( nStartPos
);
351 //=========================================================================
353 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
355 sal_uInt8 nRecordType
, // Subklassen Record-Kennung
356 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
357 sal_uInt16 nContentTag
, // Content-Art-Kennung
358 sal_uInt8 nContentVer
// Content-Versions-Kennung
363 Interne Methode f"ur Subklassen.
366 : SfxSingleRecordWriter( nRecordType
, pStream
, nContentTag
, nContentVer
),
369 // Platz f"ur eigenen Header
370 pStream
->SeekRel( + SFX_REC_HEADERSIZE_MULTI
);
373 //------------------------------------------------------------------------
375 sal_uInt32
SfxMultiFixRecordWriter::Close( bool bSeekToEndOfRec
)
377 // siehe <SfxMiniRecordWriter>
380 // Header noch nicht geschrieben?
383 // Position hinter Record merken, um sie restaurieren zu k"onnen
384 sal_uInt32 nEndPos
= SfxSingleRecordWriter::Close( sal_False
);
386 // gegen"uber SfxSingleRecord erweiterten Header schreiben
387 *_pStream
<< _nContentCount
;
388 *_pStream
<< _nContentSize
;
390 // je nachdem ans Ende des Records seeken oder hinter Header bleiben
391 if ( bSeekToEndOfRec
)
392 _pStream
->Seek(nEndPos
);
396 // Record war bereits geschlossen
400 //=========================================================================
402 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
404 sal_uInt8 nRecordType
, // Record-Kennung der Subklasse
405 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
406 sal_uInt16 nRecordTag
, // Gesamt-Art-Kennung
407 sal_uInt8 nRecordVer
// Gesamt-Versions-Kennung
412 Interner Ctor f"ur Subklassen.
415 : SfxMultiFixRecordWriter( nRecordType
, pStream
, nRecordTag
, nRecordVer
),
420 //-------------------------------------------------------------------------
422 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
424 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
425 sal_uInt16 nRecordTag
, // Gesamt-Art-Kennung
426 sal_uInt8 nRecordVer
// Gesamt-Versions-Kennung
431 Legt in 'pStream' einen 'SfxMultiVarRecord' an, dessen Content-Gr"o\sen
432 weder bekannt sind noch identisch sein m"ussen, sondern jeweils nach dem
433 Streamen jedes einzelnen Contents errechnet werden sollen.
438 Diese Methode ist nicht inline, da f"ur die Initialisierung eines
439 <SvULongs>-Members zu viel Code generiert werden w"urde.
442 : SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE
,
443 pStream
, nRecordTag
, nRecordVer
),
448 //-------------------------------------------------------------------------
450 SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()
454 Der Dtor der Klasse <SfxMultiVarRecordWriter> schlie\st den Record
455 automatisch, falls <SfxMultiVarRecordWriter::Close()> nicht bereits
456 explizit gerufen wurde.
460 // wurde der Header noch nicht geschrieben oder mu\s er gepr"uft werden
465 //-------------------------------------------------------------------------
467 void SfxMultiVarRecordWriter::FlushContent_Impl()
471 Interne Methode zum Abschlie\sen eines einzelnen Contents.
475 // Versions-Kennung und Positions-Offset des aktuellen Contents merken;
476 // das Positions-Offset ist relativ zur Startposition des ersten Contents
477 assert(_aContentOfs
.size() == static_cast<size_t>(_nContentCount
)-1);
478 _aContentOfs
.resize(_nContentCount
-1);
479 _aContentOfs
.push_back(
480 SFX_REC_CONTENT_HEADER(_nContentVer
,_nStartPos
,_nContentStartPos
));
483 //-------------------------------------------------------------------------
485 void SfxMultiVarRecordWriter::NewContent()
487 // siehe <SfxMultiFixRecordWriter>
490 // schon ein Content geschrieben?
491 if ( _nContentCount
)
494 // neuen Content beginnen
495 _nContentStartPos
= _pStream
->Tell();
499 //-------------------------------------------------------------------------
501 sal_uInt32
SfxMultiVarRecordWriter::Close( bool bSeekToEndOfRec
)
503 // siehe <SfxMiniRecordWriter>
506 // Header noch nicht geschrieben?
509 // ggf. letzten Content abschlie\sen
510 if ( _nContentCount
)
513 // Content-Offset-Tabelle schreiben
514 sal_uInt32 nContentOfsPos
= _pStream
->Tell();
515 //! darf man das so einr"ucken?
516 for ( sal_uInt16 n
= 0; n
< _nContentCount
; ++n
)
517 *_pStream
<< _aContentOfs
[n
];
519 // SfxMultiFixRecordWriter::Close() "uberspringen!
520 sal_uInt32 nEndPos
= SfxSingleRecordWriter::Close( sal_False
);
522 // eigenen Header schreiben
523 *_pStream
<< _nContentCount
;
524 if ( SFX_REC_TYPE_VARSIZE_RELOC
== _nPreTag
||
525 SFX_REC_TYPE_MIXTAGS_RELOC
== _nPreTag
)
526 *_pStream
<< static_cast<sal_uInt32
>(nContentOfsPos
- ( _pStream
->Tell() + sizeof(sal_uInt32
) ));
528 *_pStream
<< nContentOfsPos
;
530 // ans Ende des Records seeken bzw. am Ende des Headers bleiben
531 if ( bSeekToEndOfRec
)
532 _pStream
->Seek(nEndPos
);
536 // Record war bereits vorher geschlossen
540 //=========================================================================
542 void SfxMultiMixRecordWriter::NewContent
544 sal_uInt16 nContentTag
, // Kennung f"ur die Art des Contents
545 sal_uInt8 nContentVer
// Kennung f"ur die Version des Contents
550 Mit dieser Methode wird in den Record ein neuer Content eingef"ugt
551 und dessen Content-Tag sowie dessen Content-Version angegeben. Jeder,
552 auch der 1. Record mu\s durch Aufruf dieser Methode eingeleitet werden.
556 // ggf. vorherigen Record abschlie\sen
557 if ( _nContentCount
)
560 // Tag vor den Content schreiben, Version und Startposition merken
561 _nContentStartPos
= _pStream
->Tell();
563 *_pStream
<< nContentTag
;
564 _nContentVer
= nContentVer
;
567 //=========================================================================
569 bool SfxMultiRecordReader::ReadHeader_Impl()
573 Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
574 die Basisklasse bereits initialisiert und deren Header gelesen ist.
575 Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
576 nicht zur"uckge-seekt.
580 // eigenen Header lesen
581 *_pStream
>> _nContentCount
;
582 *_pStream
>> _nContentSize
; // Fix: jedes einzelnen, Var|Mix: Tabellen-Pos.
584 // mu\s noch eine Tabelle mit Content-Offsets geladen werden?
585 if ( _nRecordType
!= SFX_REC_TYPE_FIXSIZE
)
587 // Tabelle aus dem Stream einlesen
588 sal_uInt32 nContentPos
= _pStream
->Tell();
589 if ( _nRecordType
== SFX_REC_TYPE_VARSIZE_RELOC
||
590 _nRecordType
== SFX_REC_TYPE_MIXTAGS_RELOC
)
591 _pStream
->SeekRel( + _nContentSize
);
593 _pStream
->Seek( _nContentSize
);
594 _pContentOfs
= new sal_uInt32
[_nContentCount
];
595 memset(_pContentOfs
, 0, _nContentCount
*sizeof(sal_uInt32
));
596 //! darf man jetzt so einr"ucken
597 #if defined(OSL_LITENDIAN)
598 _pStream
->Read( _pContentOfs
, sizeof(sal_uInt32
)*_nContentCount
);
600 for ( sal_uInt16 n
= 0; n
< _nContentCount
; ++n
)
601 *_pStream
>> _pContentOfs
[n
];
603 _pStream
->Seek( nContentPos
);
606 // Header konnte gelesen werden, wenn am Stream kein Error gesetzt ist
607 return !_pStream
->GetError();
610 //-------------------------------------------------------------------------
612 SfxMultiRecordReader::SfxMultiRecordReader( SvStream
*pStream
, sal_uInt16 nTag
)
618 // Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
619 _nStartPos
= pStream
->Tell();
621 // passenden Record suchen und Basisklasse initialisieren
622 SfxSingleRecordReader::Construct_Impl( pStream
);
623 if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE
|
624 SFX_REC_TYPE_VARSIZE
| SFX_REC_TYPE_VARSIZE_RELOC
|
625 SFX_REC_TYPE_MIXTAGS
| SFX_REC_TYPE_MIXTAGS_RELOC
,
628 // eigenen Header dazu-lesen
629 if ( !ReadHeader_Impl() )
630 // nicht lesbar => als ung"ultig markieren und zur"uck-seeken
631 SetInvalid_Impl( _nStartPos
);
635 //-------------------------------------------------------------------------
637 SfxMultiRecordReader::~SfxMultiRecordReader()
639 delete[] _pContentOfs
;
642 //-------------------------------------------------------------------------
644 bool SfxMultiRecordReader::GetContent()
648 Positioniert den Stream an den Anfang des n"chsten bzw. beim 1. Aufruf
649 auf den Anfang des ersten Contents im Record und liest ggf. dessen
652 Liegt laut Record-Header kein Content mehr vor, wird sal_False zur"uck-
653 gegeben. Trotz einem sal_True-Returnwert kann am Stream ein Fehlercode
654 gesetzt sein, z.B. falls er unvorhergesehenerweise (kaputtes File)
659 // noch ein Content vorhanden?
660 if ( _nContentNo
< _nContentCount
)
662 // den Stream an den Anfang des Contents positionieren
663 sal_uInt32 nOffset
= _nRecordType
== SFX_REC_TYPE_FIXSIZE
664 ? _nContentNo
* _nContentSize
665 : SFX_REC_CONTENT_OFS(_pContentOfs
[_nContentNo
]);
666 sal_uInt32 nNewPos
= _nStartPos
+ nOffset
;
667 DBG_ASSERT( nNewPos
>= _pStream
->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );
669 // #99366#: correct stream pos in every case;
670 // the if clause was added by MT a long time ago,
671 // maybe to 'repair' other corrupt documents; but this
672 // gives errors when writing with 5.1 and reading with current
673 // versions, so we decided to remove the if clause (KA-05/17/2002)
674 // if ( nNewPos > _pStream->Tell() )
675 _pStream
->Seek( nNewPos
);
677 // ggf. Content-Header lesen
678 if ( _nRecordType
== SFX_REC_TYPE_MIXTAGS
||
679 _nRecordType
== SFX_REC_TYPE_MIXTAGS_RELOC
)
681 _nContentVer
= sal::static_int_cast
< sal_uInt8
>(
682 SFX_REC_CONTENT_VER(_pContentOfs
[_nContentNo
]));
683 *_pStream
>> _nContentTag
;
686 // ContentNo weiterz"ahlen
695 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */