1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file contains a system for loading and saving persistent data in a robust manor
19 The system is hierarchical and all data elements are tagged.
21 The system supports saving to text files and to binary files
23 NOTE: The text file format is similar to XML but is not currently XML feature-complete
27 #ifndef PERSISTENT_DATA_H
28 #define PERSISTENT_DATA_H
30 //-----------------------------------------------------------------------------
32 //-----------------------------------------------------------------------------
34 #include "nel/misc/types_nl.h"
35 #include "nel/misc/stream.h"
36 #include "nel/misc/mem_stream.h"
37 #include "nel/misc/entity_id.h"
38 #include "nel/misc/sheet_id.h"
39 #include "nel/misc/sstring.h"
40 #include "nel/misc/common.h"
47 //-----------------------------------------------------------------------------
49 //-----------------------------------------------------------------------------
51 // The following macros define methods that are instantiated using the
52 // 'persistent_data_template.h' header file
54 #define DECLARE_PERSISTENCE_APPLY_METHOD\
55 void apply(CPersistentDataRecord &pdr);
57 #define DECLARE_VIRTUAL_PERSISTENCE_APPLY_METHOD\
58 virtual void apply(CPersistentDataRecord &pdr);
60 #define DECLARE_PURE_VIRTUAL_PERSISTENCE_APPLY_METHOD\
61 virtual void apply(CPersistentDataRecord &pdr) =0;
64 #define DECLARE_PERSISTENCE_STORE_METHOD\
65 void store(CPersistentDataRecord &pdr) const;
67 #define DECLARE_VIRTUAL_PERSISTENCE_STORE_METHOD\
68 virtual void store(CPersistentDataRecord &pdr) const;
70 #define DECLARE_PURE_VIRTUAL_PERSISTENCE_STORE_METHOD\
71 virtual void store(CPersistentDataRecord &pdr) const =0;
74 #define DECLARE_PERSISTENCE_METHODS\
75 DECLARE_PERSISTENCE_APPLY_METHOD\
76 DECLARE_PERSISTENCE_STORE_METHOD
78 #define DECLARE_VIRTUAL_PERSISTENCE_METHODS\
79 DECLARE_VIRTUAL_PERSISTENCE_APPLY_METHOD\
80 DECLARE_VIRTUAL_PERSISTENCE_STORE_METHOD
82 #define DECLARE_PURE_VIRTUAL_PERSISTENCE_METHODS\
83 DECLARE_PURE_VIRTUAL_PERSISTENCE_APPLY_METHOD\
84 DECLARE_PURE_VIRTUAL_PERSISTENCE_STORE_METHOD
87 #define DECLARE_PERSISTENCE_METHODS_WITH_STORE_ARG(arg)\
88 void store(CPersistentDataRecord &pdr,arg) const;\
89 void apply(CPersistentDataRecord &pdr);
91 #define DECLARE_PERSISTENCE_METHODS_WITH_APPLY_ARG(arg)\
92 void store(CPersistentDataRecord &pdr) const;\
93 void apply(CPersistentDataRecord &pdr,arg);
95 #define DECLARE_PERSISTENCE_METHODS_WITH_ARG(arg)\
96 void store(CPersistentDataRecord &pdr,arg) const;\
97 void apply(CPersistentDataRecord &pdr,arg);
100 #define DECLARE_PERSISTENCE_METHODS_WITH_TARGET(arg)\
101 void store(CPersistentDataRecord &pdr,const arg) const;\
102 void apply(CPersistentDataRecord &pdr,arg);
105 // A friendly macro to place before each inclusion of persistent_data_template.h to ease debug
106 #define PERSISTENT_GENERATION_MESSAGE \
107 NL_LOC_MSG "generating persistence code for " NL_MACRO_TO_STR(PERSISTENT_CLASS)
110 //-----------------------------------------------------------------------------
111 // forward declarations
112 //-----------------------------------------------------------------------------
114 class CPersistentDataRecord
;
117 #pragma pack(push , 4)
120 //-----------------------------------------------------------------------------
121 // class CPersistentDataRecord
122 //-----------------------------------------------------------------------------
124 class CPersistentDataRecord
127 //-------------------------------------------------------------------------
128 // public data structures
129 //-------------------------------------------------------------------------
131 typedef uint16 TToken
;
132 typedef std::vector
<NLMISC::CSString
> TStringTable
;
136 //-------------------------------------------------------------------------
137 // private data structures
138 //-------------------------------------------------------------------------
142 /*** NOTE: Tokens are coded on 3 bits so there should never be more than 8 ***/
175 ET_64_BIT_EXTENDED_TYPES
= 0x80000000,
176 ET_ENTITY_ID
= ET_64_BIT_EXTENDED_TYPES
208 NLMISC::CSString _String
;
212 CArg(const std::string
& type
,const std::string
& value
,CPersistentDataRecord
& pdr
);
214 uint64
asUint() const;
215 sint64
asSint() const;
216 float asFloat() const;
217 double asDouble() const;
218 NLMISC::CSheetId
asSheetId() const;
219 NLMISC::CEntityId
asEntityId() const;
220 NLMISC::CSString
asString() const;
221 ucstring
asUCString() const;
222 NLMISC::CSString
typeName() const;
224 bool setType(const std::string
&typeName
);
225 void setType(CArg::TType value
);
226 bool isExtended() const;
229 void push(TToken token
, std::vector
<TToken
>& tokenTable
, std::vector
<uint32
>& argTable
) const;
231 static CArg
EntityId(NLMISC::CEntityId val
);
232 static CArg
SheetId(NLMISC::CSheetId val
);
233 static CArg
Int32(sint32 val
);
234 static CArg
Int32(uint32 val
);
235 static CArg
Int64(sint64 val
);
236 static CArg
Int64(uint64 val
);
237 static CArg
Float32(float val
);
238 static CArg
Float64(double val
);
239 static CArg
String(const std::string
& value
,CPersistentDataRecord
& pdr
);
240 static CArg
UCString(const ucstring
& value
,CPersistentDataRecord
& pdr
);
243 static TType
token2Type(uint32 token
,bool extend
=false);
244 static TToken
type2Token(uint32 type
);
245 static bool isTypeExtended(uint32 type
);
249 //-------------------------------------------------------------------------
251 //-------------------------------------------------------------------------
254 CPersistentDataRecord(const std::string
& tokenFamily
=std::string());
257 //-------------------------------------------------------------------------
258 // set of methods for storing data in a CPersistentDataRecord
259 //-------------------------------------------------------------------------
261 // clear out all data from the record
264 // accessors for the string table
265 // note that the second variant of addString() is often faster as it checks
266 // whether the value of 'result' is already correct before looking further...
267 // note that the tokenFamily is used to setup the initial state of the string table (at init time)
268 uint16
addString(const std::string
& name
);
269 void addString(const std::string
& name
,uint16
&result
);
270 void addString(const char* name
,uint16
&result
);
271 const NLMISC::CSString
& lookupString(uint32 idx
) const;
272 const NLMISC::CSString
& getTokenFamily() const;
274 // deal with the next data element (checking that the token matches)
276 void push(TToken token
,const CArg
& arg
);
277 void push(TToken token
,bool val
);
279 void push(TToken token
,sint8 val
);
280 void push(TToken token
,sint16 val
);
281 void push(TToken token
,sint32 val
);
282 void push(TToken token
,sint64 val
);
284 void push(TToken token
,uint8 val
);
285 void push(TToken token
,uint16 val
);
286 void push(TToken token
,uint32 val
);
287 void push(TToken token
,uint64 val
);
289 void push(TToken token
,float val
);
290 void push(TToken token
,double val
);
291 void push(TToken token
,const std::string
& val
);
292 void push(TToken token
,const ucstring
& val
);
294 void push(TToken token
,NLMISC::CSheetId val
);
295 void push(TToken token
,const NLMISC::CEntityId
& val
);
297 void push(TToken token
); // for flag token that takes no args
300 // deal with start and end of a 'structure' data block
302 void pushStructBegin(TToken token
);
303 void pushStructEnd(TToken token
);
306 //-------------------------------------------------------------------------
307 // set of methods for retrieving data from a CPersistentDataRecord
308 //-------------------------------------------------------------------------
310 // reset the read pointer to the start of the input data
313 bool isEndOfData() const;
314 bool isEndOfStruct() const;
315 bool isStartOfStruct() const;
316 bool isTokenWithNoData() const;
318 TToken
peekNextToken() const;
319 const NLMISC::CSString
& peekNextTokenName() const;
320 CArg::TType
peekNextTokenType() const;
322 void peekNextArg(CArg
& result
) const;
323 const CArg
& peekNextArg() const;
325 void popNextArg(TToken token
,CArg
& result
);
326 const CArg
& popNextArg(TToken token
);
328 void pop(TToken token
,bool& result
);
330 void pop(TToken token
,sint8
& result
);
331 void pop(TToken token
,sint16
& result
);
332 void pop(TToken token
,sint32
& result
);
333 void pop(TToken token
,sint64
& result
);
335 void pop(TToken token
,uint8
& result
);
336 void pop(TToken token
,uint16
& result
);
337 void pop(TToken token
,uint32
& result
);
338 void pop(TToken token
,uint64
& result
);
340 void pop(TToken token
,float& result
);
341 void pop(TToken token
,double& result
);
342 void pop(TToken token
,std::string
& result
);
343 void pop(TToken token
,ucstring
& result
);
345 void pop(TToken token
,NLMISC::CSheetId
& result
);
346 void pop(TToken token
,NLMISC::CEntityId
& result
);
348 void pop(TToken token
); // for flag token that takes no args
350 void popStructBegin(TToken token
);
351 void popStructEnd(TToken token
);
352 void skipStruct(); // Added by Ben, will skip a whole struct in record
353 void skipData(); // Added by Ben, will skip next record, a struct as well as a simple token
355 // accessors for the _LookupTbls container
356 CPDRLookupTbl
* getLookupTbl(uint32 id
) const;
357 void setLookupTbl(uint32 id
, CPDRLookupTbl
* tbl
);
359 // compare two persistent data records (verify that their contents are eqivalent)
360 bool operator==(const CPersistentDataRecord
& other
) const;
363 //-------------------------------------------------------------------------
364 // debug methods for retrieving info from pdr records
365 //-------------------------------------------------------------------------
367 // get info as a single text string including field names and values in a man-readable format
368 NLMISC::CSString
getInfo() const;
369 // get info as a string of comma separated values
370 NLMISC::CSString
getInfoAsCSV() const;
371 // get the header row that correspond to the comma separated values in getInfoAsCSV() - note that this method is static
372 static const NLMISC::CSString
& getCSVHeaderLine();
373 // get the number of values in a pdr (a value is a string, a uint32, uint64, CEntitiyId, flag or other such primitive)
374 uint32
getNumValues() const;
377 //-------------------------------------------------------------------------
379 //-------------------------------------------------------------------------
381 enum TFileFormat
{ BINARY_FILE
, XML_FILE
, LINES_FILE
, ANY_FILE
};
382 enum TStringFormat
{ XML_STRING
=XML_FILE
, LINES_STRING
=LINES_FILE
};
385 //-------------------------------------------------------------------------
386 // set of methods for storing a data record to various destinations
387 //-------------------------------------------------------------------------
389 uint32
totalDataSize() const;
390 uint32
stringDataSize() const;
392 // the following routines save the data in different ways
393 // NOTE: they are all inclined to rewind the 'read pointer'
395 // write the contents of the pdr to a stream
396 bool toStream(NLMISC::IStream
& dest
);
398 // write data to a binary buffer - the size required for the buffer is totalDataSize()
399 bool toBuffer(char *dest
,uint32 sizeLimit
);
401 // write data to a text buffer in one of several txt formats
402 bool toString(std::string
& result
,TStringFormat stringFormat
=XML_STRING
);
404 // write data to a text buffer in an xml format
405 bool toXML(std::string
& result
);
407 // write data to a text buffer in line-based txt format
408 bool toLines(std::string
& result
);
410 // perform a toBuffer() and write the result to a binary file
411 bool writeToBinFile(const std::string
&fileName
);
413 // perform a toString() and write the result to a text file
414 bool writeToTxtFile(const std::string
&fileName
, TStringFormat stringFormat
=XML_STRING
);
416 // if the format is set to 'ANY_FILE' then use the extension provided in the 'fileName' argument to
417 // determine the file type. In this case 'txt' and 'xml' have specific meanings
418 // returns writeToTxtFile(...) or writeToBinFile(...) depending on the file format
419 bool writeToFile(const std::string
&fileName
, TFileFormat fileFormat
=ANY_FILE
);
422 //-------------------------------------------------------------------------
423 // set of methods for retrieving a data record from various sources
424 //-------------------------------------------------------------------------
426 // read from a CMemStream (maybe either binary or text data)
427 bool fromBuffer(NLMISC::IStream
& stream
);
429 // read from a binary data buffer
430 // if the input data looks like text then calls fromString()
431 bool fromBuffer(const char *src
, uint32 bufferSize
);
433 // read from a text string (either xml or lines)
434 // note 1: This routine is not at all optimised
435 // note 2: The content of s is destroyed by the routine
436 bool fromString(const std::string
& s
);
438 // read from an xml string
439 // note 1: This routine is not at all optimised
440 // note 2: The content of s is destroyed by the routine
441 bool fromXML(const std::string
& s
);
443 // read from a lines string
444 // note 1: This routine is not at all optimised
445 // note 2: The content of s is destroyed by the routine
446 bool fromLines(const std::string
& s
);
448 // read from a binary file
449 bool readFromBinFile(const std::string
&fileName
);
451 // read from a text file
452 bool readFromTxtFile(const std::string
&fileName
);
454 // read a file and determine whether it's a binary or text file from it's
455 // content - then behave like readFromBinFile() or readFromTxtFile()
456 bool readFromFile(const std::string
&fileName
);
461 bool fromStream(NLMISC::IStream
& stream
, uint32 size
);
463 //-------------------------------------------------------------------------
464 // private persistent data
465 //-------------------------------------------------------------------------
467 TStringTable _StringTable
;
468 std::vector
<uint32
> _ArgTable
;
469 std::vector
<TToken
> _TokenTable
;
472 //-------------------------------------------------------------------------
473 // private work data - for writing
474 //-------------------------------------------------------------------------
476 NLMISC::CSString _TokenFamily
;
477 std::vector
<TToken
> _WritingStructStack
;
480 //-------------------------------------------------------------------------
481 // private work data - for reading
482 //-------------------------------------------------------------------------
484 mutable uint32 _ArgOffset
;
485 mutable uint32 _TokenOffset
;
487 typedef std::vector
<TToken
> TReadingStructStack
;
488 mutable TReadingStructStack _ReadingStructStack
;
490 typedef std::vector
< NLMISC::CSmartPtr
<CPDRLookupTbl
> > TLookupTbls
;
491 TLookupTbls _LookupTbls
;
494 //-------------------------------------------------------------------------
496 //-------------------------------------------------------------------------
501 //-----------------------------------------------------------------------------
502 // class CPersistentDataRecordRyzomStore
503 //-----------------------------------------------------------------------------
504 // This is just a specialisation of the class that register it into the "RyzomTokenFamily"
505 // Use it only to store (else useless copy of default string table at clear())
507 class CPersistentDataRecordRyzomStore
: public CPersistentDataRecord
510 CPersistentDataRecordRyzomStore() : CPersistentDataRecord("RyzomTokenFamily") {}
514 //-----------------------------------------------------------------------------
515 // class CPDRLookupTbl
516 //-----------------------------------------------------------------------------
518 class CPDRLookupTbl
: public NLMISC::CRefCount
521 // the data type used to represent enum values
522 typedef sint16 TEnumValue
;
524 // the [] operator is used to lookup entries in the lookup table
525 // in debug mode if the index 'idx' falls outside the table we assert
526 // in release mode if the index 'idx' falls outside the table we return -1; (This is the 'Bad String' value)
527 TEnumValue
operator[](uint32 idx
) const;
529 // get the number of lookup table classes registered so far
530 static uint32
getNumLookupTableClasses();
533 // a static used by derived classes to setup unique ids
534 static uint32 _NextLookupTblClassId
;
536 // table mapping from pdr string table indices to enum values
537 typedef std::vector
<TEnumValue
> TTheTbl
;
542 //-----------------------------------------------------------------------------
543 // class CPdrTokenRegistry
544 //-----------------------------------------------------------------------------
545 // This is a singleton that can be used to setup families of tokens that
546 // are copied directly into a new pdr's string table at init time
549 class CPdrTokenRegistry
552 // singleton instantiation
553 static CPdrTokenRegistry
* getInstance();
556 static void releaseInstance();
558 // adding a token to the string table
559 uint16
addToken(const std::string
& family
,const std::string
& token
);
561 // get hold of the string table for a given family
562 const CPersistentDataRecord::TStringTable
& getStringTable(const std::string
& family
);
566 typedef std::map
<NLMISC::CSString
,CPersistentDataRecord::TStringTable
> TRegistry
;
570 static CPdrTokenRegistry
*_Instance
;
571 // this is a singleton so prohibit creation
578 #include "persistent_data_inline.h"
580 //-----------------------------------------------------------------------------