Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / include / svl / filerec.hxx
blob7088e378e9fb8a8e4f4299043087ce46f3f9c3c7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 #ifndef INCLUDED_SVL_FILEREC_HXX
21 #define INCLUDED_SVL_FILEREC_HXX
23 #include <sal/config.h>
25 #include <sal/log.hxx>
26 #include <svl/svldllapi.h>
27 #include <tools/debug.hxx>
28 #include <tools/stream.hxx>
29 #include <osl/diagnose.h>
31 #include <vector>
33 #define SFX_REC_PRETAG_EXT sal_uInt8(0x00) // Pre-Tag for Extended-Records
34 #define SFX_REC_PRETAG_EOR sal_uInt8(0xFF) // Pre-Tag for End-Of-Records
36 #define SFX_REC_TYPE_SINGLE sal_uInt8(0x01) // Single-Content-Record
37 #define SFX_REC_TYPE_FIXSIZE sal_uInt8(0x02) // Fix-Size-Multi-Content-Record
38 #define SFX_REC_TYPE_VARSIZE_RELOC sal_uInt8(0x03) // variable Rec-Size
39 #define SFX_REC_TYPE_VARSIZE sal_uInt8(0x04) // old (not movable)
40 #define SFX_REC_TYPE_MIXTAGS_RELOC sal_uInt8(0x07) // Mixed Tag Content-Record
41 #define SFX_REC_TYPE_MIXTAGS sal_uInt8(0x08) // old (not movable)
43 #define SFX_REC_HEADERSIZE_MINI 4 // size of the Mini-Record-Header
44 #define SFX_REC_HEADERSIZE_SINGLE 4 // additional HEADERSIZE_MINI => 8
45 #define SFX_REC_HEADERSIZE_MULTI 6 // additional HEADERSIZE_SINGLE => 14
48 // General file format: documented at class SfxMiniRecordReader below
50 /** Writes simple records in a stream
52 * An instance of this class can write a simple record into a stream. It identifies itself
53 * with a sal_uInt8 and stores its own size. This allows it to be skipped with old versions or
54 * readers if they do not know the record type (= tag). No version number will be stored.
56 * One can either provide the size or the latter will be automatically calculated based on the
57 * difference of Tell() before and after streaming the content.
59 * @par File format
61 * 1* sal_uInt8 Content-Tag (!= 0)
62 * 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
63 * SizeOfContent* sal_uInt8 Content
65 * @par Example
66 * @code
67 * {
68 * SfxMiniRecordWriter aRecord( pStream, MY_TAG_X );
69 * *aRecord << aMember1;
70 * *aRecord << aMember2;
71 * }
72 * @endcode
74 * @note To ensure up- and downwards compatibility, new versions need to include
75 * the data of the older ones and are only allowed to add data afterwards.
76 * @see SfxMiniRecordReader
78 class SVL_DLLPUBLIC SfxMiniRecordWriter
80 protected:
81 SvStream* _pStream; // <SvStream> with the record
82 sal_uInt32 _nStartPos; // starting position of the total record in the stream
83 bool _bHeaderOk; /* TRUE, if header already written */
84 sal_uInt8 _nPreTag; // 'pre-Tag' to write to header
86 public:
87 inline SfxMiniRecordWriter( SvStream *pStream, sal_uInt8 nTag );
88 inline ~SfxMiniRecordWriter();
90 sal_uInt32 Close( bool bSeekToEndOfRec = true );
92 private:
93 SfxMiniRecordWriter( const SfxMiniRecordWriter& ) = delete;
94 SfxMiniRecordWriter& operator=(const SfxMiniRecordWriter&) = delete;
97 /** Reads simple record from a stream
99 * An instance of this class allows to read a simple record from a stream that was written by
100 * SfxMiniRecordWriter. It is also possible to skip a record, even without knowing its internal
101 * format.
103 * @par Example
104 * @code
106 * SfxMiniRecordReader aRecord( pStream );
107 * switch ( aRecord.GetTag() )
109 * case MY_TAG_X:
110 * *aRecord >> aMember1;
111 * *aRecord >> aMember2;
112 * break;
114 * ...
117 * @endcode
119 * @par General file format
121 * Each record begins with one byte, the so called 'Pre-Tag'.
123 * If this 'Pre-Tag' == 0x00, then the record is a extended record,
124 * whose type is further determined by another byte at position 5:
126 * 0x01: SfxSingleRecord
127 * 0x02: SfxMultiFixRecord
128 * 0x03+0x04: SfxMultiVarRecord
129 * 0x07+0x08: SfxMultiMixRecord
130 * (All other possible record types are reserved.)
132 * Normally, if only for performance reasons, the file formats are
133 * constructed in such a way, that when loading the record type
134 * is predetermined. So the record type serves mainly for checks
135 * and for file viewers that do not know the exact file format.
137 * For that purpose 'SfxMiniRecordReader' provides a static method
138 * 'ScanRecordType()', with which it is possible to find out what
139 * Record type can be found in the stream that was passed.
141 * A 'Pre-Tag' with value 0xFF is reserved for a terminator.
142 * Terminators are used to stop looking for a particular record,
143 * i.e. if it was not found until then the search will not be continued.
145 * For all other values of the 'Pre-Tag' (so 0x01 to 0xFE) the record
146 * is one that is compatible with SW3, called 'SfxMiniRecord' here,
147 * and therefore it can be read with an <SfxMiniRecordReader>.
149 * If the record starts with 0x44 it could be a Drawing-Engine-Record.
150 * This is the case if the following 3 bytes spell 'RMD' or 'RVW'
151 * (which together with 'D'==0x44 form an abbreviation for 'DRaw-MoDel'
152 * or 'DRaw-VieW'). Records of this type cannot be read by the classes
153 * represented here, nor interpreted in any way. Only the
154 * 'ScanRecordType()' method can recognise it - but further processing
155 * is impossible.
157 * The 3 bytes in position 2 to 4 normally contain the size of the
158 * record without the pre-tag and the size field itself,
159 * so the remaining size after the 4 byte header.
161 * Structure of the Mini-Records:
163 * 1 sal_uInt8 Pre-Tag
164 * 3 sal_uInt8 OffsetToEndOfRec
165 * OffsetToEndOfRec* 1 sal_uInt8 Content
167 * For Extended-Records the 4 byte header is followed by an extended header,
168 * which contains first the record type, than a version number
169 * and a tag, which indicates the kind of content.
171 * Structure of the extended record:
173 * 1 sal_uInt8 Pre-Tag (==0x00)
174 * 3 sal_uInt8 OffsetToEndOfRec
175 * OffsetToEndOfRec* 1 sal_uInt8 Content
176 * 1 sal_uInt8 Record-Type
177 * 1 sal_uInt8 Version
178 * 2 sal_uInt8 Tag
179 * ContentSize* 1 sal_uInt8 Content
181 * (ContentSize = OffsetToEndOfRec - 8)
183 * @note
184 * The reason for the structure of the record is as follows:
186 * The SW record type came first, and so had to be kept 1:1.
187 * Fortunately some record tags had not been used (like 0x00 and 0xFF).
188 * <BR>
189 * => 1st byte 0x00 can be used to identify extended records
190 * <BR>
191 * => 1st byte 0xFF can be used for special purposes.
193 * Whatever record type is present, it should be possible to recognise
194 * the type, read the header and skip the record without having to
195 * seek back or read superfluous data.
196 * <BR>
197 * => Bytes 2-4 are interpreted as the offset to the end of the record
198 * whatever the record, so that the total record size is equal
199 * to sizeof(sal_uInt32) + OffsetToEndOfRec
201 * The records should be easy to parse and constructed uniformly
202 * <BR>
203 * => They build on each, for instance the SfxMiniRecord is contained
204 * in all others
206 * It should be possible to distinguish the record from Drawing Engine
207 * ones. These start with 'DRMD' und 'DRVW'.
208 * <BR>
209 * => Mini-Records with Pre-Tag 'D' can only be up to 4MB in size,
210 * to avoid confusion.
212 * @par Extensions
213 * Plans are to extend the file format in such a way that the high nibble
214 * of the record type has special duties. For instance it is planned
215 * to mark Record-Contents als 'consisting only of Records'. That way
216 * a file viewer could automatically parse these structures without
217 * risking encountering data that looks like records, but actually is
218 * flat data. Those further extensions are prepared to the extent
219 * that in type comparisons the high nibble is not taken into account.
221 * @see SfxMiniRecordWriter
223 class SVL_DLLPUBLIC SfxMiniRecordReader
225 protected:
226 SvStream* _pStream; // <SvStream> to read from
227 sal_uInt32 _nEofRec; // Position direcly after the record
228 bool _bSkipped; // TRUE: the record was skipped explicitly
229 sal_uInt8 _nPreTag; // Pre-Tag read from the heather
231 // three phase constructor for sub-classes
232 SfxMiniRecordReader()
233 : _pStream(nullptr)
234 , _nEofRec(0)
235 , _bSkipped(false)
236 , _nPreTag(0)
239 void Construct_Impl( SvStream *pStream )
241 _pStream = pStream;
242 _bSkipped = false;
243 _nPreTag = SFX_REC_PRETAG_EXT;
245 inline bool SetHeader_Impl( sal_uInt32 nHeader );
247 // mark as invalid and seek back
248 void SetInvalid_Impl( sal_uInt32 nRecordStartPos )
250 _nPreTag = SFX_REC_PRETAG_EOR;
251 _pStream->Seek( nRecordStartPos );
254 public:
255 SfxMiniRecordReader( SvStream *pStream, sal_uInt8 nTag );
256 inline ~SfxMiniRecordReader();
258 inline void Skip();
260 private:
261 SfxMiniRecordReader( const SfxMiniRecordReader& ) = delete;
262 SfxMiniRecordReader& operator=(const SfxMiniRecordReader&) = delete;
267 * With instances of this class a record ban be written to a stream,
268 * whose only contents is identified by a sal_uInt16 tag and a
269 * sal_uInt8 version number. Also the length of the record is stored
270 * so that older versions or readers that do not known the
271 * record type (tag) can skip it.
273 * The size can be given directly or calculated automatically from
274 * the difference between the tell() return values before and
275 * after streaming the contents.
277 * To allow for forward and backward compatibility, newer versions
278 * of the data must always include the older versions completely,
279 * it is only allowed to append new data!
281 * @par File Format
283 * 1* sal_uInt8 Pre-Tag (!= 0)
284 * 1* 3-sal_uInt8 OffsetToEndOfRec in bytes
285 * 1* sal_uInt8 Record-Type (==SFX_REC_TYPE_SINGLE)
286 * 1* sal_uInt8 Content-Version
287 * 1* sal_uInt16 Content-Tag
288 * SizeOfContent* sal_uInt8 Content
290 class SVL_DLLPUBLIC SfxSingleRecordWriter: public SfxMiniRecordWriter
292 protected:
293 SfxSingleRecordWriter( sal_uInt8 nRecordType,
294 SvStream *pStream,
295 sal_uInt16 nTag, sal_uInt8 nCurVer );
297 public:
298 sal_uInt32 Close();
303 * With instances of this class simple records can be read from a stream,
304 * that were written with class <SfxSingleRecordWriter>.
306 * It is also possible to skip the record without knowing the internal format.
308 class SVL_DLLPUBLIC SfxSingleRecordReader: public SfxMiniRecordReader
310 protected:
311 sal_uInt16 _nRecordTag; // type of the complete contents
312 sal_uInt8 _nRecordType; // Record Type from the header
314 // Three phase constructor for derived classes
315 SfxSingleRecordReader()
316 : _nRecordTag(0)
317 , _nRecordType(0)
320 void Construct_Impl( SvStream *pStream )
322 SfxMiniRecordReader::Construct_Impl( pStream );
324 bool FindHeader_Impl( sal_uInt16 nTypes, sal_uInt16 nTag );
329 * Instances of this class can be used to write a record to a stream,
330 * which stores its own length so that it can be skipped by
331 * older versions and readers that do not known the record type (tag).
333 * It contains multiple contents of the same type (tag) and the same
334 * version, which have been identified once and for all in the
335 * header of the record. All contents have a length which is
336 * known in advance and identical.
338 * To be able to guarantee forward and backwards compatibility,
339 * newer versions of the that must always completely contain
340 * the old version, so it is only allowed to append data!
341 * Obviously, only the data of the individual contents are meant,
342 * the number of contents is naturally variable, and should be
343 * treated as such by the reading application.
345 * @par File format
347 * 1* sal_uInt8 Pre-Tag (==0)
348 * 1* 3-sal_uInt8 OffsetToEndOfRec in bytes
349 * 1* sal_uInt8 Record-Type (==SFX_REC_TYPE_FIXSIZE)
350 * 1* sal_uInt8 Content-Version
351 * 1* sal_uInt16 Content-Tag
352 * 1* sal_uInt16 NumberOfContents
353 * 1* sal_uInt32 SizeOfEachContent
354 * NumberOfContents* (
355 * SizeOfEachContent sal_uInt8 Content
358 * @par Example
359 * @code
361 * SfxMultiFixRecordWriter aRecord( pStream, MY_TAG_X, MY_VERSION );
362 * for ( sal_uInt16 n = 0; n < Count(); ++n )
364 * aRecord.NewContent();
365 * *aRecord << aMember1[n];
366 * *aRecord << aMember2[n];
369 * @endcode
371 class SVL_DLLPUBLIC SfxMultiFixRecordWriter: public SfxSingleRecordWriter
373 protected:
374 sal_uInt32 _nContentStartPos; /* start position of respective
375 content - only with DBG_UTIL
376 and for subclasses */
377 sal_uInt32 _nContentSize; // size of each content
378 sal_uInt16 _nContentCount; // number of contents
380 SfxMultiFixRecordWriter( sal_uInt8 nRecordType,
381 SvStream *pStream,
382 sal_uInt16 nTag,
383 sal_uInt8 nCurVer );
385 public:
386 inline ~SfxMultiFixRecordWriter();
388 sal_uInt32 Close();
391 /** write record with multiple content items
393 * Write a record into a stream that stores its own size. This allows it to be
394 * skipped with old versions or readers if they do not know the record type (= tag).
396 * It contains multiple content items of the same tag and version, that are both
397 * stored in the header of the record. The size of each content will be calculated
398 * automatically and stored so that single content items can be skipped without
399 * having to read them.
401 * @par File Format
403 * 1* sal_uInt8 Pre-Tag (==0)
404 * 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
405 * 1* sal_uInt8 Record-Type (==SFX_FILETYPE_TYPE_VARSIZE)
406 * 1* sal_uInt8 Content-Version
407 * 1* sal_uInt16 Content-Tag
408 * 1* sal_uInt16 NumberOfContents
409 * 1* sal_uInt32 OffsetToOfsTable
410 * NumberOfContents* (
411 * ContentSize* sal_uInt8 Content
413 * NumberOfContents* sal_uInt32 ContentOfs (shifted each <<8)
415 * @par Example
416 * @code
418 * SfxMultiVarRecordWriter aRecord( pStream, MY_TAG_X, MY_VERSION );
419 * for ( sal_uInt16 n = 0; n < Count(); ++n )
421 * aRecord.NewContent();
422 * *aRecord << aMember1[n];
423 * *aRecord << aMember2[n];
426 * @endcode
428 * @note To ensure up- and downwards compatibility, new versions need to include
429 * the data of the older ones and are only allowed to add data afterwards.
431 class SVL_DLLPUBLIC SfxMultiVarRecordWriter: public SfxMultiFixRecordWriter
433 protected:
434 std::vector<sal_uInt32> _aContentOfs;
435 sal_uInt16 _nContentVer; // only for SfxMultiMixRecordWriter
437 SfxMultiVarRecordWriter( sal_uInt8 nRecordType,
438 SvStream *pStream,
439 sal_uInt16 nRecordTag );
441 void FlushContent_Impl();
443 public:
444 SfxMultiVarRecordWriter( SvStream *pStream,
445 sal_uInt16 nRecordTag );
446 virtual ~SfxMultiVarRecordWriter();
448 void NewContent();
450 sal_uInt32 Close();
453 /** write record with multiple content items with identical size
455 * Write a record into a stream that stores its own size. This allows it to be
456 * skipped with old versions or readers if they do not know the record type (= tag).
458 * It contains multiple content items of the same tag and version, that are both
459 * stored in the header of the record. All content items have a known identical
460 * size.
462 * @par File Format
464 * 1* sal_uInt8 Pre-Tag (==0)
465 * 1* 3-sal_uInt8 OffsetToEndOfRec in Bytes
466 * 1* sal_uInt8 record type (==SFX_REC_TYPE_MIXTAGS)
467 * 1* sal_uInt8 content version
468 * 1* sal_uInt16 record tag
469 * 1* sal_uInt16 NumberOfContents
470 * 1* sal_uInt32 OffsetToOfsTable
471 * NumberOfContents* (
472 * 1* sal_uInt16 content tag
473 * ContentSize* sal_uInt8 content
475 * NumberOfContents* sal_uInt32 ( ContentOfs << 8 + Version )
477 * @note To ensure up- and downwards compatibility, new versions need to include
478 * the data of the older ones and are only allowed to add data afterwards.
480 class SVL_DLLPUBLIC SfxMultiMixRecordWriter: public SfxMultiVarRecordWriter
482 public:
483 inline SfxMultiMixRecordWriter( SvStream *pStream,
484 sal_uInt16 nRecordTag );
486 void NewContent( sal_uInt16 nTag, sal_uInt8 nVersion );
489 /** Read multiple content items of an existing record
491 * Instances of this class allow to read multiple content items of a record
492 * that was written with
493 * - SfxMultiFixRecordWriter
494 * - SfxMultiVarRecordWriter
495 * - SfxMultiMixRecordWriter
497 * It is possible to skip single content or the whole record without knowing
498 * its internal format.
500 * @par Example
501 * @code
503 * SfxMultiRecordReader aRecord( pStream );
504 * for ( sal_uInt16 nRecNo = 0; aRecord.GetContent(); ++nRecNo )
506 * switch ( aRecord.GetTag() )
508 * case MY_TAG_X:
509 * X *pObj = new X;
510 * *aRecord >> pObj.>aMember1;
511 * if ( aRecord.HasVersion(2) )
512 * *aRecord >> pObj->aMember2;
513 * Append( pObj );
514 * break;
516 * ...
520 * @endcode
522 class SVL_DLLPUBLIC SfxMultiRecordReader: public SfxSingleRecordReader
524 sal_uInt32 _nStartPos; // start position of this record
525 sal_uInt32* _pContentOfs; // offsets of the start positions
526 sal_uInt32 _nContentSize; // size of each record or table position
527 sal_uInt16 _nContentCount; // number of content items
528 sal_uInt16 _nContentNo; /* the index of the current content
529 contains the next content's index
530 for GetContent() */
531 sal_uInt16 _nContentTag; // tag of the current content
532 sal_uInt8 _nContentVer; // version of the current content
534 bool ReadHeader_Impl();
536 public:
537 SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag );
538 ~SfxMultiRecordReader();
540 bool GetContent();
541 inline sal_uInt16 GetContentTag();
542 inline sal_uInt8 GetContentVersion() const;
545 /** create a mini record
547 * The content size is calculated automatically after streaming.
549 * @param pStream the stream that will contain the record
550 * @param nTag a record tag between 0x01 and 0xFE
552 inline SfxMiniRecordWriter::SfxMiniRecordWriter( SvStream* pStream, sal_uInt8 nTag )
553 : _pStream( pStream ),
554 _nStartPos( pStream->Tell() ),
555 _bHeaderOk(false),
556 _nPreTag( nTag )
558 DBG_ASSERT( _nPreTag != 0xFF, "invalid Tag" );
559 SAL_INFO("svl", "SfxFileRec: writing record to " << pStream->Tell());
561 pStream->SeekRel( + SFX_REC_HEADERSIZE_MINI );
564 /** The destructor closes the record automatically if not done earlier */
565 inline SfxMiniRecordWriter::~SfxMiniRecordWriter()
567 // the header was not written, yet, or needs to be checked
568 if ( !_bHeaderOk )
569 Close();
572 /** The dtor moves the stream automatically to the position directly behind the record */
573 inline SfxMiniRecordReader::~SfxMiniRecordReader()
575 if ( !_bSkipped )
576 Skip();
579 /** position the stream directly behind the record's end */
580 inline void SfxMiniRecordReader::Skip()
582 _pStream->Seek(_nEofRec);
583 _bSkipped = true;
586 /// @see SfxMiniRecordWriter::Close()
587 inline sal_uInt32 SfxSingleRecordWriter::Close()
589 sal_uInt32 nRet = 0;
591 // was the header already written?
592 if ( !_bHeaderOk )
594 // write base class header
595 sal_uInt32 nEndPos = SfxMiniRecordWriter::Close( false/*bSeekToEndOfRec*/ );
597 // seek the end of the own header if needed or stay behind the record
598 _pStream->SeekRel( SFX_REC_HEADERSIZE_SINGLE );
599 nRet = nEndPos;
601 #ifdef DBG_UTIL
602 else
603 // check base class header
604 SfxMiniRecordWriter::Close( false/*bSeekToEndOfRec*/ );
605 #endif
607 return nRet;
611 /** The destructor closes the record automatically if not done earlier */
612 inline SfxMultiFixRecordWriter::~SfxMultiFixRecordWriter()
614 // the header was not written, yet, or needs to be checked
615 if ( !_bHeaderOk )
616 Close();
620 * Creates a SfxMultiMixRecord in the given stream with a separate tags and
621 * versions of its content parts. The sizes of each part are calculated
622 * automatically.
624 * @param pStream target stream in which the record will be created
625 * @param nRecordTag tag for the total record
627 inline SfxMultiMixRecordWriter::SfxMultiMixRecordWriter( SvStream* pStream,
628 sal_uInt16 nRecordTag )
629 : SfxMultiVarRecordWriter( SFX_REC_TYPE_MIXTAGS, pStream, nRecordTag )
633 /** @returns the tag of the last opened content
634 * @see SfxMultiRecordReder::GetContent()
636 inline sal_uInt16 SfxMultiRecordReader::GetContentTag()
638 return _nContentTag;
641 /** @returns the version of the last opened content
642 * @see SfxMultiRecordReder::GetContent()
644 inline sal_uInt8 SfxMultiRecordReader::GetContentVersion() const
646 return _nContentVer;
649 #endif
651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */