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>
24 /* Die folgenden Makros extrahieren Teilbereiche aus einem sal_uInt32 Wert.
25 Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
29 #define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
30 #define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
31 #define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
32 #define SFX_REC_VER(n) ( ((n) & 0x0000FF00) >> 8 )
33 #define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )
35 #define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
36 #define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
39 /* Die folgenden Makros setzen Teilbereiche zu einem sal_uInt32 Wert zusammen.
40 Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
44 #define SFX_REC_MINI_HEADER(nPreTag,nStartPos,nEndPos) \
45 ( sal_uInt32(nPreTag) | \
46 sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 )
48 #define SFX_REC_HEADER(nRecType,nContentTag,nContentVer) \
49 ( sal_uInt32(nRecType) | \
50 ( sal_uInt32(nContentVer) << 8 ) | \
51 ( sal_uInt32(nContentTag) << 16 ) )
53 #define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
54 ( sal_uInt32(nContentVer) | \
55 sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )
58 sal_uInt32
SfxMiniRecordWriter::Close
60 bool bSeekToEndOfRec
/* true (default)
61 Der Stream wird an das Ende des Records
65 Der Stream wird an den Anfang des
66 Contents (also hinter den Header)
73 Diese Methode schlie\st den Record. Dabei wird haupts"achlich der
76 Wurde der Header bereits geschrieben, hat der Aufruf keine Wirkung.
82 Position im Stream, die direkt hinter dem Record liegt.
83 'bSeekToEndOfRecord==sal_True'
84 => R"uckgabewert == aktuelle Stream-Position nach Aufruf
87 Der Header war bereits geschrieben worden.
91 // wurde der Header noch nicht geschrieben?
94 // Header an den Anfang des Records schreiben
95 sal_uInt32 nEndPos
= _pStream
->Tell();
96 _pStream
->Seek( _nStartPos
);
97 *_pStream
<< SFX_REC_MINI_HEADER( _nPreTag
, _nStartPos
, nEndPos
);
99 // je nachdem ans Ende des Records seeken oder hinter Header bleiben
100 if ( bSeekToEndOfRec
)
101 _pStream
->Seek( nEndPos
);
103 // Header wurde JETZT geschrieben
108 // Record war bereits geschlossen
112 bool SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader
)
116 Interne Methode zum nachtr"aglichen Verarbeiten eines extern gelesenen
117 Headers. Falls der Header eine End-Of-Records-Kennung darstellt,
118 wird am Stream ein Errorcode gesetzt und sal_False zur"uckgeliefert. Im
119 Fehlerfall wird der Stream jedoch nicht auf den Record-Anfang zur"uck-
126 // Record-Ende und Pre-Tag aus dem Header ermitteln
127 _nEofRec
= _pStream
->Tell() + SFX_REC_OFS(nHeader
);
128 _nPreTag
= sal::static_int_cast
< sal_uInt8
>(SFX_REC_PRE(nHeader
));
130 // wenn End-Of-Record-Kennung, dann Fehler
131 if ( _nPreTag
== SFX_REC_PRETAG_EOR
)
133 _pStream
->SetError( ERRCODE_IO_WRONGFORMAT
);
139 SfxMiniRecordReader::SfxMiniRecordReader
141 SvStream
* pStream
, /* <SvStream>, an dessen aktueller
142 Position sich ein <SfxMiniRecord>
145 sal_uInt8 nTag
// Pre-Tag des gew"unschten Records
150 Dieser Ctor interpretiert 'pStream' ab der aktuellen Position als
151 eine l"uckenlose Folge von, von dieser Klassen-Gruppe interpretierbaren,
152 Records. Der in dieser Folge erste als <SfxMiniRecord> interpretierbare
153 (also ggf. auch ein extended-Record) mit dem PreTag 'nTag' wird ge"offnet
154 und durch diese Instanz repr"asentiert.
156 Wird das Ende des Streams oder die Kennung SFX_REC_PRETAG_EOR
157 erreicht, bevor ein Record mit dem ge"unschten Pre-Tag gefunden wird,
158 ist die erzeugte Instanz ung"ultig ('IsValid() == sal_False'). Ein ent-
159 sprechender Error-Code (ERRCODE_IO_EOF bzw. ERRCODE_IO_WRONGFORMAT)
160 ist dann am Stream gesetzt, dessen Position ist dann au\serdem unver-
163 Bei 'nTag==SFX_FILEREC_PRETAG_EOR' wird nicht versucht, einen Record
164 zu lesen, es wird sofort 'IsValid()' auf sal_False gesetzt und kein Error-Code
165 am Stream gesetzt. Dies ist dauzu gedacht, ohne 'new' und 'delete'
166 abw"rtskompatibel SfxMiniRecords einbauen zu k"onnen. Siehe dazu
167 <SfxItemSet::Load()>.
170 [Anwendungsvorschlag]
172 Wird dieser Ctor in einer bereits ausgelieferten Programmversion
173 verwendet, k"onnen in das File-Format jeweils davor kompatibel neue
174 Records mit einer anderen Kennung eingef"ugt werden. Diese werden
175 schlie\slich automatisch "uberlesen. Erkauft wird diese M"oglichkeit
176 allerdings mit etwas schlechterem Laufzeitverhalten im Vergleich mit
177 direktem 'drauf-los-lesen', der sich jedoch auf einen Vergleich zweier
178 Bytes reduziert, falls der gesuchte Record der erste in der Folge ist.
181 : _pStream( pStream
),
182 _bSkipped( nTag
== SFX_REC_PRETAG_EOR
)
184 // ggf. ignorieren (s.o.)
191 // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
192 sal_uInt32 nStartPos
= pStream
->Tell();
194 // passenden Record suchen
198 DBG( DbgOutf( "SfxFileRec: searching record at %ul", pStream
->Tell() ) );
202 // Headerdaten von Basisklasse extrahieren lassen
203 SetHeader_Impl( nHeader
);
205 // ggf. Fehler behandeln
206 if ( pStream
->IsEof() )
207 _nPreTag
= SFX_REC_PRETAG_EOR
;
208 else if ( _nPreTag
== SFX_REC_PRETAG_EOR
)
209 pStream
->SetError( ERRCODE_IO_WRONGFORMAT
);
212 // wenn gefunden, dann Schleife abbrechen
213 if ( _nPreTag
== nTag
)
216 // sonst skippen und weitersuchen
217 pStream
->Seek( _nEofRec
);
221 // Fehler => zur"uck-seeken
222 pStream
->Seek( nStartPos
);
228 SfxSingleRecordWriter::SfxSingleRecordWriter
230 sal_uInt8 nRecordType
, // f"ur Subklassen
231 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
232 sal_uInt16 nContentTag
, // Inhalts-Art-Kennung
233 sal_uInt8 nContentVer
// Inhalts-Versions-Kennung
238 Interner Ctor f"ur Subklassen.
241 : SfxMiniRecordWriter( pStream
, SFX_REC_PRETAG_EXT
)
243 // Erweiterten Header hiner den des SfxMiniRec schreiben
244 *pStream
<< SFX_REC_HEADER(nRecordType
, nContentTag
, nContentVer
);
248 inline bool SfxSingleRecordReader::ReadHeader_Impl( sal_uInt16 nTypes
)
252 Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
253 die Basisklasse bereits initialisiert und deren Header gelesen ist.
254 Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
255 nicht zur"uckge-seekt.
261 // Basisklassen-Header einlesen
262 sal_uInt32 nHeader
=0;
263 *_pStream
>> nHeader
;
264 if ( !SetHeader_Impl( nHeader
) )
268 // eigenen Header einlesen
269 *_pStream
>> nHeader
;
270 _nRecordVer
= sal::static_int_cast
< sal_uInt8
>(SFX_REC_VER(nHeader
));
271 _nRecordTag
= sal::static_int_cast
< sal_uInt16
>(SFX_REC_TAG(nHeader
));
273 // falscher Record-Typ?
274 _nRecordType
= sal::static_int_cast
< sal_uInt8
>(SFX_REC_TYP(nHeader
));
275 bRet
= 0 != ( nTypes
& _nRecordType
);
281 bool SfxSingleRecordReader::FindHeader_Impl
283 sal_uInt16 nTypes
, // arithm. Veroderung erlaubter Record-Typen
284 sal_uInt16 nTag
// zu findende Record-Art-Kennung
289 Interne Methode zum lesen des Headers des ersten Record, der einem
290 der Typen in 'nTypes' entspricht und mit der Art-Kennung 'nTag'
293 Kann ein solcher Record nicht gefunden werden, wird am Stream ein
294 Errorcode gesetzt, zur"uck-geseekt und sal_False zur"uckgeliefert.
298 // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
299 sal_uInt32 nStartPos
= _pStream
->Tell();
301 // richtigen Record suchen
302 while ( !_pStream
->IsEof() )
306 DBG( DbgOutf( "SfxFileRec: searching record at %ul", _pStream
->Tell() ) );
307 *_pStream
>> nHeader
;
308 if ( !SetHeader_Impl( nHeader
) )
309 // EOR => Such-Schleife abbreichen
312 // Extended Record gefunden?
313 if ( _nPreTag
== SFX_REC_PRETAG_EXT
)
315 // Extended Header lesen
316 *_pStream
>> nHeader
;
317 _nRecordTag
= sal::static_int_cast
< sal_uInt16
>(SFX_REC_TAG(nHeader
));
319 // richtigen Record gefunden?
320 if ( _nRecordTag
== nTag
)
322 // gefundener Record-Typ passend?
323 _nRecordType
= sal::static_int_cast
< sal_uInt8
>(
324 SFX_REC_TYP(nHeader
));
325 if ( nTypes
& _nRecordType
)
329 // error => Such-Schleife abbrechen
335 if ( !_pStream
->IsEof() )
336 _pStream
->Seek( _nEofRec
);
339 // Fehler setzen und zur"uck-seeken
340 _pStream
->SetError( ERRCODE_IO_WRONGFORMAT
);
341 _pStream
->Seek( nStartPos
);
346 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
348 sal_uInt8 nRecordType
, // Subklassen Record-Kennung
349 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
350 sal_uInt16 nContentTag
, // Content-Art-Kennung
351 sal_uInt8 nContentVer
// Content-Versions-Kennung
356 Interne Methode f"ur Subklassen.
359 : SfxSingleRecordWriter( nRecordType
, pStream
, nContentTag
, nContentVer
),
362 // Platz f"ur eigenen Header
363 pStream
->SeekRel( + SFX_REC_HEADERSIZE_MULTI
);
367 sal_uInt32
SfxMultiFixRecordWriter::Close( bool bSeekToEndOfRec
)
369 // siehe <SfxMiniRecordWriter>
372 // Header noch nicht geschrieben?
375 // Position hinter Record merken, um sie restaurieren zu k"onnen
376 sal_uInt32 nEndPos
= SfxSingleRecordWriter::Close( sal_False
);
378 // gegen"uber SfxSingleRecord erweiterten Header schreiben
379 *_pStream
<< _nContentCount
;
380 *_pStream
<< _nContentSize
;
382 // je nachdem ans Ende des Records seeken oder hinter Header bleiben
383 if ( bSeekToEndOfRec
)
384 _pStream
->Seek(nEndPos
);
388 // Record war bereits geschlossen
393 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
395 sal_uInt8 nRecordType
, // Record-Kennung der Subklasse
396 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
397 sal_uInt16 nRecordTag
, // Gesamt-Art-Kennung
398 sal_uInt8 nRecordVer
// Gesamt-Versions-Kennung
403 Interner Ctor f"ur Subklassen.
406 : SfxMultiFixRecordWriter( nRecordType
, pStream
, nRecordTag
, nRecordVer
),
412 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
414 SvStream
* pStream
, // Stream, in dem der Record angelegt wird
415 sal_uInt16 nRecordTag
, // Gesamt-Art-Kennung
416 sal_uInt8 nRecordVer
// Gesamt-Versions-Kennung
421 Legt in 'pStream' einen 'SfxMultiVarRecord' an, dessen Content-Gr"o\sen
422 weder bekannt sind noch identisch sein m"ussen, sondern jeweils nach dem
423 Streamen jedes einzelnen Contents errechnet werden sollen.
428 Diese Methode ist nicht inline, da f"ur die Initialisierung eines
429 <SvULongs>-Members zu viel Code generiert werden w"urde.
432 : SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE
,
433 pStream
, nRecordTag
, nRecordVer
),
439 SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()
443 Der Dtor der Klasse <SfxMultiVarRecordWriter> schlie\st den Record
444 automatisch, falls <SfxMultiVarRecordWriter::Close()> nicht bereits
445 explizit gerufen wurde.
449 // wurde der Header noch nicht geschrieben oder mu\s er gepr"uft werden
455 void SfxMultiVarRecordWriter::FlushContent_Impl()
459 Interne Methode zum Abschlie\sen eines einzelnen Contents.
463 // Versions-Kennung und Positions-Offset des aktuellen Contents merken;
464 // das Positions-Offset ist relativ zur Startposition des ersten Contents
465 assert(_aContentOfs
.size() == static_cast<size_t>(_nContentCount
)-1);
466 _aContentOfs
.resize(_nContentCount
-1);
467 _aContentOfs
.push_back(
468 SFX_REC_CONTENT_HEADER(_nContentVer
,_nStartPos
,_nContentStartPos
));
472 void SfxMultiVarRecordWriter::NewContent()
474 // siehe <SfxMultiFixRecordWriter>
477 // schon ein Content geschrieben?
478 if ( _nContentCount
)
481 // neuen Content beginnen
482 _nContentStartPos
= _pStream
->Tell();
487 sal_uInt32
SfxMultiVarRecordWriter::Close( bool bSeekToEndOfRec
)
489 // siehe <SfxMiniRecordWriter>
492 // Header noch nicht geschrieben?
495 // ggf. letzten Content abschlie\sen
496 if ( _nContentCount
)
499 // Content-Offset-Tabelle schreiben
500 sal_uInt32 nContentOfsPos
= _pStream
->Tell();
501 //! darf man das so einr"ucken?
502 for ( sal_uInt16 n
= 0; n
< _nContentCount
; ++n
)
503 *_pStream
<< _aContentOfs
[n
];
505 // SfxMultiFixRecordWriter::Close() "uberspringen!
506 sal_uInt32 nEndPos
= SfxSingleRecordWriter::Close( sal_False
);
508 // eigenen Header schreiben
509 *_pStream
<< _nContentCount
;
510 if ( SFX_REC_TYPE_VARSIZE_RELOC
== _nPreTag
||
511 SFX_REC_TYPE_MIXTAGS_RELOC
== _nPreTag
)
512 *_pStream
<< static_cast<sal_uInt32
>(nContentOfsPos
- ( _pStream
->Tell() + sizeof(sal_uInt32
) ));
514 *_pStream
<< nContentOfsPos
;
516 // ans Ende des Records seeken bzw. am Ende des Headers bleiben
517 if ( bSeekToEndOfRec
)
518 _pStream
->Seek(nEndPos
);
522 // Record war bereits vorher geschlossen
527 void SfxMultiMixRecordWriter::NewContent
529 sal_uInt16 nContentTag
, // Kennung f"ur die Art des Contents
530 sal_uInt8 nContentVer
// Kennung f"ur die Version des Contents
535 Mit dieser Methode wird in den Record ein neuer Content eingef"ugt
536 und dessen Content-Tag sowie dessen Content-Version angegeben. Jeder,
537 auch der 1. Record mu\s durch Aufruf dieser Methode eingeleitet werden.
541 // ggf. vorherigen Record abschlie\sen
542 if ( _nContentCount
)
545 // Tag vor den Content schreiben, Version und Startposition merken
546 _nContentStartPos
= _pStream
->Tell();
548 *_pStream
<< nContentTag
;
549 _nContentVer
= nContentVer
;
553 bool SfxMultiRecordReader::ReadHeader_Impl()
557 Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
558 die Basisklasse bereits initialisiert und deren Header gelesen ist.
559 Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
560 nicht zur"uckge-seekt.
564 // eigenen Header lesen
565 *_pStream
>> _nContentCount
;
566 *_pStream
>> _nContentSize
; // Fix: jedes einzelnen, Var|Mix: Tabellen-Pos.
568 // mu\s noch eine Tabelle mit Content-Offsets geladen werden?
569 if ( _nRecordType
!= SFX_REC_TYPE_FIXSIZE
)
571 // Tabelle aus dem Stream einlesen
572 sal_uInt32 nContentPos
= _pStream
->Tell();
573 if ( _nRecordType
== SFX_REC_TYPE_VARSIZE_RELOC
||
574 _nRecordType
== SFX_REC_TYPE_MIXTAGS_RELOC
)
575 _pStream
->SeekRel( + _nContentSize
);
577 _pStream
->Seek( _nContentSize
);
578 _pContentOfs
= new sal_uInt32
[_nContentCount
];
579 memset(_pContentOfs
, 0, _nContentCount
*sizeof(sal_uInt32
));
580 //! darf man jetzt so einr"ucken
581 #if defined(OSL_LITENDIAN)
582 _pStream
->Read( _pContentOfs
, sizeof(sal_uInt32
)*_nContentCount
);
584 for ( sal_uInt16 n
= 0; n
< _nContentCount
; ++n
)
585 *_pStream
>> _pContentOfs
[n
];
587 _pStream
->Seek( nContentPos
);
590 // Header konnte gelesen werden, wenn am Stream kein Error gesetzt ist
591 return !_pStream
->GetError();
595 SfxMultiRecordReader::SfxMultiRecordReader( SvStream
*pStream
, sal_uInt16 nTag
)
601 // Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
602 _nStartPos
= pStream
->Tell();
604 // passenden Record suchen und Basisklasse initialisieren
605 SfxSingleRecordReader::Construct_Impl( pStream
);
606 if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE
|
607 SFX_REC_TYPE_VARSIZE
| SFX_REC_TYPE_VARSIZE_RELOC
|
608 SFX_REC_TYPE_MIXTAGS
| SFX_REC_TYPE_MIXTAGS_RELOC
,
611 // eigenen Header dazu-lesen
612 if ( !ReadHeader_Impl() )
613 // nicht lesbar => als ung"ultig markieren und zur"uck-seeken
614 SetInvalid_Impl( _nStartPos
);
619 SfxMultiRecordReader::~SfxMultiRecordReader()
621 delete[] _pContentOfs
;
625 bool SfxMultiRecordReader::GetContent()
629 Positioniert den Stream an den Anfang des n"chsten bzw. beim 1. Aufruf
630 auf den Anfang des ersten Contents im Record und liest ggf. dessen
633 Liegt laut Record-Header kein Content mehr vor, wird sal_False zur"uck-
634 gegeben. Trotz einem sal_True-Returnwert kann am Stream ein Fehlercode
635 gesetzt sein, z.B. falls er unvorhergesehenerweise (kaputtes File)
640 // noch ein Content vorhanden?
641 if ( _nContentNo
< _nContentCount
)
643 // den Stream an den Anfang des Contents positionieren
644 sal_uInt32 nOffset
= _nRecordType
== SFX_REC_TYPE_FIXSIZE
645 ? _nContentNo
* _nContentSize
646 : SFX_REC_CONTENT_OFS(_pContentOfs
[_nContentNo
]);
647 sal_uInt32 nNewPos
= _nStartPos
+ nOffset
;
648 DBG_ASSERT( nNewPos
>= _pStream
->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );
650 // #99366#: correct stream pos in every case;
651 // the if clause was added by MT a long time ago,
652 // maybe to 'repair' other corrupt documents; but this
653 // gives errors when writing with 5.1 and reading with current
654 // versions, so we decided to remove the if clause (KA-05/17/2002)
655 // if ( nNewPos > _pStream->Tell() )
656 _pStream
->Seek( nNewPos
);
658 // ggf. Content-Header lesen
659 if ( _nRecordType
== SFX_REC_TYPE_MIXTAGS
||
660 _nRecordType
== SFX_REC_TYPE_MIXTAGS_RELOC
)
662 _nContentVer
= sal::static_int_cast
< sal_uInt8
>(
663 SFX_REC_CONTENT_VER(_pContentOfs
[_nContentNo
]));
664 *_pStream
>> _nContentTag
;
667 // ContentNo weiterz"ahlen
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */