File: wxSerialize.h

package info (click to toggle)
codelite 14.0+dfsg-1
 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620 ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // copyright : (C) 2008 by Eran Ifrah // file name : wxSerialize.h // // ------------------------------------------------------------------------- // A // _____ _ _ _ _ // / __ \ | | | | (_) | // | / \/ ___ __| | ___| | _| |_ ___ // | | / _ \ / _ |/ _ \ | | | __/ _ ) // | \__/\ (_) | (_| | __/ |___| | || __/ // \____/\___/ \__,_|\___\_____/_|\__\___| // // F i l e // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- // $RCSfile: wxSerialize.h$ // $Source: include/wx/wxSerialize.h$ // $Revision: 1.16$ // $Date: 7-sep-2007 11:29:08$ //--------------------------------------------------------------------------- // Author: Jorgen Bodde // Christian Buhtz // Copyright: (c) Jorgen Bodde // License: see LICENSE for details //--------------------------------------------------------------------------- #ifndef _WXSERIALIZE_H_ #define _WXSERIALIZE_H_ #include #include #include #include #include // for wxSize, wxPoint #define WXSERIALIZE_MAJOR_VERSION 1 #define WXSERIALIZE_MINOR_VERSION 1 /** \author Jorgen Bodde \mainpage This is the wxSerialize documentation guide. wxSerialize is a serializing class meant for serializing / de-serializing a complete (or partial) object model to a stream. The streaming is made type safe and reliable. This means when an error occurs (wrong data read back or unexpected end of file) the wxSerialize class logs an error, and refuses to read from the stream, leaving all variables that follow the error untouched, or in a default state. With the use of a version number and a header, the stream is checked when reading it back. When, for example you wrote a stream with version 1000, and upon reading you expect version 900, the wxSerialize class logs an error that the expected version is lower then the encountered version, avoiding a mis-read of data that is not supported in the code. With the use of a header string, you can double check if the stream offered is really recognised by the application. Because wxSerialize uses wxInputStream and wxOutputStream for reading and writing, the contents of the serialization can be written to a memory based stream, a compression stream (like wxZLibOutputStream), a tcp/ip socket, from an wxURL (from the internet, handy for updates), or any other stream based upon wxInputStream / wxOutputStream. The streaming is binary compatible on Linux / Windows and MacOSX which means that if you read back the binary data from disk, the same results are read back, the values are properly converted from little to big endian. \section serializing_exactly Ok but what is serializing exactly? Serializing is putting the contents of a class to an output file. Microsoft's MFC used CArchive (which I attempted to keep as API compatible as possible) and boost now also has a binary serializer. So, how does it work? Well it is really simple. The power of the serializing lies in the fact that the classes themselves are responsible for serializing their data. It is very short in code, and the destination of the serialization stream doesn't really matter. \section how_work So how does it work? Consider the following class: \code class MyDataClass { public: // the serialization class, that streams from and to the // specified stream. Serialize(wxSerialize &a); private: wxString m_name; wxString m_address; wxUint8 m_age; wxArrayString m_siblings; }; \endcode How does the serializing work .. We simply implement the Serialize method: \code bool MyDataClass::Serialize(wxSerialize &a) { if(a.IsStoring()) { a << m_name << m_address << m_age; a << m_siblings; } else { a >> m_name >> m_address >> m_age; a >> m_siblings; } // return false when the archive encountered an error return a.IsOk(); } \endcode This is all there is to it! This small piece of code, serializes the 4 used member vars to the stream, on disk or where it needs to go. The following code shows how the class is serialized. First when it is saved to disk: \code wxFileOutputStream file("somefile.dat"); wxSerialize a(file); m_ourClass->Serialize(a); \endcode That's all! When the file and archive are out of scope, the file is closed and the object is persistent on disk. Now to read the data back: \code wxFileInputStream file("somefile.dat"); wxSerialize a(file); // let's say we just create it and initialize it m_ourClass = new MyDataClass(); m_ourClass->Serialize(a); \endcode Nearly identical code isn't it ? Even the new MyDataClass() is not needed, it is simply there to illustrate how newly created objects can be serialized back into. Only one method is needed, I called it Serialize() but the name does not matter. \section why_using So why not write using wxConfig, wxFFile, XML or use XTI? This serializer class was designed, keeping in mind a small footprint in memory and disk. When serializing to XML you gain flexibility and even can hand-edit it, but the overhead in the class is enormous. You have to figure out how the data needs to be stored, attribute names, nested hierarchies, handle errors, etc. XML is basically better to write to, if user editing or portability is a big issue. Serializing to a binary file can be more useful in the following situations: - Writing cache data (e.g. an object model that represents a state in memory). By writing this in a serialized stream to disk, the object model can be quickly read back between sessions, or heavy preprocessing (e.g storing a directory structure to disk) can be avoided. - Undo / Redo mechanism. Usually undo / redo mechanisms require a lot of overhead in the object model. For example when the user deletes a data item in a GUI window, this item should not be really deleted, only taken apart from the object model. When using a serializer class, the object can be removed, and later on re-created back on it's rightful place. The good thing is that the serialize methods can both handle undo / redo, but also save the data to disk. - Small applications that do not require overhead of XML, SQL, or use wxConfig because these all need name / value conventions. With very small applications that only perform minor tasks, the developer is more distracted by the writing to disk then it's really needed. - Writing config. This sounds odd, but when you have a global config object, and it is not really necessary to keep all that data in a text file, serializing the object containing the config is rather easy. So when is it not useful? - Ofcourse when people want to edit the data afterwards. - When the data needs to be exchanged between applications using a standard format - When the application needs to process huge data, or store entered data on disk to be queried later on (SQL or XML). \section what_dp What about data flexibility? People who spent some time serializing data, know that when for example a binary serialized file is written to disk and the object is extended with more data, you have a problem with serializing it back. Why? The data in the stream is only compatible with the old data makeup, and when adding new member vars how are you going to read them back? They are not in the stream to begin with, and you don't even know if they are or not. Well I deviced two ways of doing this. Let's for example extend our rather trivial MyDataClass class: \code class MyDataClass { public: // the serialization class, that streams from and to the // specified stream. Serialize(wxSerialize &a); private: wxString m_name; wxString m_address; wxUint8 m_age; wxArrayString m_siblings; // NEW DATA ADDED, THIS NEEDS SERIALIZING TOO! wxUint32 m_monthlyPay; bool m_healthInsurance; }; \endcode \subsection version_number Using a version number The version number you can give to the stream when you write it back. This version number is stored and checked upon reading. When creating the stream you specify the version of the stream. You increment this version when there will be binary incompatibility with older streams: \code wxFileOutputStream file("somefile.dat"); wxSerialize a(file, 1001); // latest version is 1001 (was 1000) m_ourClass->Serialize(a); \endcode Now in the code we can take this in account when reading. When we save, we simply save our new data back. And as you guessed the drawback, older applications still in the field cannot read the newer stream: \code bool MyDataClass::Serialize(wxSerialize &a) { if(a.IsStoring()) { // simply write all a << m_name << m_address << m_age; a << m_siblings; // always append new data here, so reading back goes in // the same exact order as it is written a << m_monthlyPay << m_healthInsurance; } else { a >> m_name >> m_address >> m_age; a >> m_siblings; // reading back poses a problem. We can have the data but in // an older stream we have not. So we need the version check: if(a.GetVersion() > 1000) { a >> m_monthlyPay >> m_healthInsurance; } } // return false when the archive encountered an error return a.IsOk(); } \endcode As you might have guessed, this can be some overhead. While developing the app, this can happen a lot as the classes grow. So the disadvantages are: - Older releases you wrote (and are in the field) cannot read the newer data back. So when you use serialization as part of packages or updates, people are obliged to update, or you must provide multiple versions) - All the newer data require a version check at read time, and will bloat your application as you must take in account all older streams. A small overhead, but lots of changes will simply bloat your code with GetVersion() checks which is not really elegant. \subsection enter_leave Using EnterObject() and LeaveObject() To reduce the code overhead, and maintain binary read compatiblity between older and newer releases of your application, I deviced a way which allows you to add member data to your serializing routines and not break the reading of older or even newer streams. This is how it works; \code bool MyDataClass::Serialize(wxSerialize &a) { a.EnterObject(); if(a.IsStoring()) { // simply write all a << m_name << m_address << m_age; a << m_siblings; a << m_monthlyPay << m_healthInsurance; } else { a >> m_name >> m_address >> m_age; a >> m_siblings; a >> m_monthlyPay >> m_healthInsurance; } a.LeaveObject(); return a.IsOk(); } \endcode Done! These two powerful boundaries (EnterObject() and LeaveObject()) tackle two situations: - Reading an old stream in a newer application. So what happens, when for example the older stream does not contain the m_monthlyPay and m_healthInsurance, the LeaveObject() boundary was already written in the old code. The stream recognises this, and refuses to read data into m_monthlyPay and m_healthInsurance. In these cases the default value will be kept in these members. Data that follows after the boundary when LeaveObject was not called, will be kept default. - Reading a newer stream with an older application. When the application you released in the field is older then the stream being written by the newer release, more data is present in the stream then the old application knows about. Here the same situation applies. The LeaveObject() in the old code comes sooner then the expected leave boundary in the stream, so wxSerialize will go look for the boundary and ignore all data that is newer. The EnterObject() and LeaveObject() construction keeps the stream in sync. The only important thing to remember is that you MUST USE these boundaries from the first moment and not when you are adding new member objects later on. It is a small sacrifice to gain a lot of flexibility! The overhead in the stream is also very small, because the footprint of the boundaries is exactly 1 byte for the enter and 1 byte for the leave marker. EnterObject() and LeaveObject() can be used multiple times after eachother, and also nested. So when you serialize a class hierarchy and in one of your child objects also data changes, this will be possible. The only drawback that is left with this approach (but is also present in all data writing when older applications write back data that contained newer information) is that newer data is being overwritten. You must take special caution (for example using the version tag or wxSerializeStatus object) in your application to warn the user that the data being written will destroy newer information in the serializing stream. The newer release of your application can still read this stream, but newer data is defaulted again. Also another caveat is that when you replace data being read back it will not work, not with EnterObject() / LeaveObject() but also not with a versioned stream. Consider this example: \code bool MyDataClass::Serialize(wxSerialize &a) { a.EnterObject(); if(a.IsStoring()) { // simply write all a << m_name << m_address << m_age; a << m_siblings; //a << m_monthlyPay << m_healthInsurance; // REPLACED NOW with: a << m_surName << m_middleName; } else { a >> m_name >> m_address >> m_age; a >> m_siblings; // WON'T WORK! In older versions the two vars m_monthlypay and m_healthInsurance // were stored here, instead we read back this. If the stream we read back do not // contain these new replaced variables yet, we are hitting an error state. a >> m_surName >> m_middleName; } a.LeaveObject(); return a.IsOk(); } \endcode \subsection archive_status wxSerializeStatus Helper Class This class is especially designed to keep the status of the stream between reading and writing. It contains the last version being written or read, the header, and more important, an NewDataLoss() flag. This flag will be set when the stream being read back is newer then the data model it's beaing serialized into. Writing the data back using the wxSerializeStatus object earlier retrieved when reading will prevent the write if the flag NewDataLoss() is not reset using OverwriteNew() flag. How is the flag determined? Well, if one time in the data serializing process the LeaveObject() method needs to search for the marker (thus skipping newer data in the stream) this flag is set. This wxSerializeStatus class will also remove the need of writing the same header back as being specified when reading, and also the version will be preserved. The member wxSerializeStatus::SetVersion() can be used to specify a newer version. Here is an example, first when reading we store the last status and keep it: \code wxFileInputStream file("somefile.dat"); wxSerialize a(file); // let's say we just create it and initialize it m_ourClass = new MyDataClass(); m_ourClass->Serialize(a); // store the status (copy class) m_ourStatus = a.GetArchiveStatus(); \endcode When writing back, and this application is older then the newer application, thus an overwrite will result in newer information loss: \code // if the last time we read a newer stream, tell the user if(m_ourStatus.NewDataLoss()) { if(::wxMessageDialog( ... "ask if we should overwrite, it will result in dataloss" ...) == wxID_YES) m_ourStatus.OverwriteNew(); // reset the flag } // if the flag is reset we continue else we skip if(!m_ourStatus.NewDataLoss()) { wxFileOutputStream file("somefile.dat"); wxSerialize a(file, m_ourStatus.GetVersion(), m_ourStatus.GetHeader()); // serialize data m_ourClass->Serialize(a); } else { wxLogError("Data is not saved!"); } \endcode This construction prevents the application of accidentally writing data back and destroying newer serialized data. The user must explicitly agree upon this, or the developer must provide a mechanism to backup the old file so that newer data is not lost when reading it back in the newer application. */ /** \class wxSerializeStatus \brief A simple helper class that keeps track of the current version of the archive, the header and more important, when at reading time it appears the stream contains newer data then expected. It will help the developer in asking the user if the stream might be saved back or not. See \ref archive_status overview for more information. */ class wxSerializeStatus { public: /** Constructor of the wxSerializeStatus object. This is the default constructor so you can define it in the class itself without having to do anything for it */ wxSerializeStatus() : m_version(0) , m_header(wxT("")) , m_newDataLoss(false) { }; /** Constructor initializer of the wxSerializeStatus object. Used by wxSerialize to fill in the goods so they can be used later */ wxSerializeStatus(size_t version, const wxString &header) : m_version(version) , m_header(header) , m_newDataLoss(false) { }; ~wxSerializeStatus() { }; /** Copy operator to get the latest copy of it from the wxSerialize class, and assign it to your copy */ void operator=(const wxSerializeStatus &s) { m_version = s.m_version; m_header = s.m_header; m_newDataLoss = s.m_newDataLoss; }; /** With this method you tell wxSerialize that it is ok to overwrite the newer data stream with an older version of it. This needs to be set implicitly. Check out \ref archive_status for more information. */ void OverwriteNew() { m_newDataLoss = false; }; /** This method returns the flag of the current NewDataLoss status. It will be set when the wxSerialize encounters an out of sync where there is more data then can be read back. See \ref enter_leave */ bool NewDataLoss() const { return m_newDataLoss; }; /** Sets the NewDataLoss flag again. This is used by wxSerialize only, or you can use it to prevent other parts of the your code to be executed later when new data loss still occurs. The counterpart of this flag is OverwriteNew() */ void SetNewDataLoss() { m_newDataLoss = true; }; /** Returns the version of the last read archive */ size_t GetVersion() const { return m_version; }; /** Returns the header of the last read archive */ const wxString &GetHeader() const { return m_header; }; private: size_t m_version; wxString m_header; bool m_newDataLoss; }; /** \class wxSerialize \brief A serializer class that can serialize the contents of your class to or from a stream. It uses wxInputStream and wxOutputStream derived classes for input and output. See the \ref mainpage for more information */ class wxSerialize { public: /** Construct an input stream archive. The stream will be used to retrieve the data from, which can be any derived stream like a file, zip stream etc. When constructed as input, the wxSerialize object cannot be written to. The first integer and string are always read from the stream. This contains the version number (or 0 when not used) and the header which is empty if not used. When setting the version parameter, wxSerialize will check if the version read back is at least the expected version. For example, when version argument is set to 1000, wxSerialize will accept versions 0 .. 1000 for reading but will give an error if version 1001 and up is read from the stream. You can use this to set the version to at least the supported version for your code. When extending the serialization routines after your app has been deployed, the version info can be used to dynamically skip parts while loading from an older stream, and warn users that the version of the read stream is older which means incompatibility with the older application will occur. This is not the recommended method though, see EnterObject() and LeaveObject() for a more flexible solution. The header tag will also be compared when it is not empty. When an empty header is passed as parameter, the read back header tag is ignored. When the header argument is set, and the read back header is empty or different, an error will occur and the stream cannot be read back from. This can provide an extra check if the stream will contain the data needed. The partialMode flag will do nothing else but setting a flag (IsFullSerialize()) which can be used in serializing to skip some relational hierarchies and stuff not needed when the object is only partially serialized to the stream (handy for undo / redo mechanisms). When set to true, IsFullSerialize() will return false. \code // open and write wxFileInputStream in_stream("somefile.dat"); wxSerialize a(in_stream, 1000, _T("My Stream")); if(a.IsOk()) { a >> m_name; a >> m_age; a >> m_string >> m_count; } else wxLogError(wxT("Archive cannot be opened because : ") + a.GetLastErrorString()); // end the archive a.Close(); \endcode See also \ref enter_leave and read about data flexibility. \sa IsFullSerialize(), wxSerializeStatus */ wxSerialize(wxInputStream &stream, size_t version = 0, const wxString &header = wxEmptyString, bool partialMode = false); /** Construct an output stream object. The stream will be used to save the data to, which can be any derived stream like a file, zip stream etc. When constructed as output, the wxSerialize object cannot be read from. \code // open and write wxFileOutputStream out_file("somefile.dat"); wxSerialize a(out_file, 1000, _T("My Stream")); a << m_name; a << m_age; a << m_string << m_count; // end all writing (does not close the stream) a.Close(); \endcode The version (1000 in example) is saved in the stream, also the header. At reading time these two are checked against what the expected version and header are, so that you can be sure the stream is recognised and supported. Always save your latest supported version here. This means when you extended your application's serialization, increment the version. This will allow you to read back older streams and save them with newer information. See \ref enter_leave for more ways to make your serialization more flexible. */ wxSerialize(wxOutputStream &stream, size_t version = 1, const wxString &header = wxEmptyString, bool partialMode = false); /** Destructor. The passed stream ombject will not be destroyed, because it is used by reference */ virtual ~wxSerialize(); /** Closes the archive. This means all writing or reading done to it will not affect the archive or variables passed by reference. A closed archive will not produce any errors when read from and will not advance in searching. A closed archive can be re-opened with Open() to allow reading or writing again. */ void Close() { m_opened = false; }; /** Re-opens the archive. But only when the archive is in OK state (IsOk() == true). By default the archive is opened when the constructor is created and everything went ok */ void Open() { // it can be as simple as this m_opened = IsOk(); }; /** In reading mode, it returns true when the stream we are reading from returns true on Eof(). It means that it returns true AFTER an attempt has been made to read past the end of the stream. In writing mode Eof() returns always false. When the stream is closed, true is always returned, we can't read or write anymore in that case anyway. Reading more from a stream that is at it's end will produce errors. */ bool Eof(); /** Returns true when the wxSerialize is in OK condition. This means no error occurred. Errors that can occur are for example: - The underlying stream reports that it is not OK (not able to write, not able to read) - Reading an unexpected type of variable (e.g. expecting int and reading back string) - Reading from a wxSerialize with a higher version then supported - Reading from a wxSerialize with a wrong header - Writing to the wxSerialize when we are in reading mode - Reading from the wxSerialize when we are in writing mode See GetLastError() to retrieve the error, and GetLastErrorString() for an explanation. When the wxSerialize encountered errors, it will remain in an error state, subsequent reads and writes will not have effect to either the member variables to read to, or write anything to the stream when writing, for example: \code wxSerialize a(in_stream); // .. some code if(a.IsStoring()) { // when wxSerialize is not ok, this does nothing // so we can do this safely, and report Ok status below a << m_name << m_address << m_age; // when we are not ok, we can use a.IsOk() to skip // the count loop right away (we cannot write and it saves time) a << GetCount(); for(int i = 0; i < GetCount() && a.IsOk(); i++) a << m_value[i]; } else { // reset your object, in case we // get errors, all vars will be initialized ResetDefaults(); // reading back from not ok stream also does not affect // the passed member vars they will remain default a >> m_name >> m_address >> m_age; // be careful here, because count is not set by default // so we set it to 0. Now the for loop will quit right away int count = 0; a >> count; for(int i = 0; i < count; i++) a >> m_value; } // return status to lower part return a.IsOk(); \endcode */ bool IsOk() { bool noerr = (m_errorCode == wxSERIALIZE_ERR_OK); if(m_writeMode) return m_odstr.IsOk() && noerr; else return m_idstr.IsOk() && noerr; }; /** Returns the last error set in this string. */ const wxString &GetLastErrorStr() const { return m_errMsg; }; /** This method allows you to specify regions of an object that wxSerialize can identify. This allows you to read back old streams, and still serialize all data back from the stream. All the data that is not present in the stream reading back is skipped. This will reduce the problem that old streams do not contain all data that is needed, it will simply be skipped. See \ref enter_leave for a bigger explanation, basically when using this, you ensure older applications can read newer streams, and newer applications can also read older streams. The only restriction is that you keep the data in the same order. This means the new added data always needs to be read and written at the end of the original data. The method returns false when the object entering did not work (always returns true in write mode but can return false plus an error when the current level could not be found. If this is the case the wxSerialize class stays in an error state so it won't be harmful trying to read or write anyway. For example: \code wxSerialize a(in_stream); if(a.EnterObject()) { // we succesfully entered the object level if(a.IsLoading()) { a >> m_name; a >> m_age; // these vars are not present in the older stream. At this point, // all serializing into the variables will not take place. They will // be left default, so initializing before the construction gives them // a default value, but the stream reader does not give errors, here // it is best to use the ReadInt and ReadString directly with a default // specifier: a.ReadInt(m_peopeCount, 0); // default no people when not present a.ReadUint8(m_numberOfPersons, 1); // default 1 person } else { // save the stream back, now all vars can be read back. The interesting // thing is that older programs can still read the streams back as they // scan for the LeaveObject() marker at the end of their serializing round a << m_name; a << m_age; a << m_peopeCount; a << m_numberOfPersons; } a.LeaveObject(); } \endcode */ bool EnterObject(); /** This the leave method that belongs to the EnterObject() method. Please always close the section with the LeaveObject() when an EnterObject() is used. These two constructions can be nested also. See \ref enter_leave for more information. \sa EnterObject() */ bool LeaveObject(); /** Indicates current state is writing. This flag will always be true if wxSerialize is constructed with a wxOutputStream. The IsStoring() does not check wether the state of the stream is ok or not, to prevent problems do: \code wxSerialize a(stream); // without the a.IsOk() it will work too, but serializing will // go on writing to dev/null without aborting when an error occurred. if(a.IsOk()) { if(a.IsStoring()) { // write contents } else { // read contents } } \endcode */ bool IsStoring() const { return m_writeMode; }; /** Indicates current state is reading. This flag will always be true if wxSerialize is constructed with a wxInputStream. The IsLoading() does not check wether the state of the stream is ok or not, to prevent problems do: \code wxSerialize a(stream); // without the a.IsOk() it will work too, but serializing will // go on without aborting when an error occurred. if(a.IsOk()) { if(a.IsLoading()) { // read contents } else { // write contents } } \endcode */ bool IsLoading() const { return !m_writeMode; }; /** This is an extra flag independent of IsStoring() or IsLoading(). It can be set to indicate that certain parts of the serialized objects must be omitted, can be useful when not the complete hierarchy needs serializing, but only a single object, for example to a temporary stream (undo / redo mechanism). See the argument 'partialMode' at the constructors for this class. \code bool SomeObject::Serialize(wxSerialize &a) { if(a.IsStoring()) { // store our data a << m_name << m_address << m_age; // in partial mode, the following will return false, not saving // any hierachie information of objects if(a.IsFullSerialize()) { a << m_items.Count(); for(size_t i = 0 ; a.IsOk() && i < m_items.Count(); i++) m_items[a]->Serialize(a); } } else { // load our data a >> m_name >> m_address >> m_age; // in partial mode, the following will return false, not restoring // any hierachie information of objects if(a.IsFullSerialize()) { size_t count = 0; a >> count; m_items.DeleteAll(); for(size_t i = 0 ; a.IsOk() && i < count; i++) { SomeChild *child = new SomeChild(); child->Serialize(a); m_items.Add(child); } } } } \endcode */ bool IsFullSerialize() const { return !m_partialMode; }; /** Returns true when the stream is open. When opened, reading and writing can take place. When closed, no reading and writing can occur, but also no error will be reported when tried anyway */ bool IsOpen() const { return m_opened; }; // -- saving serializers -- /** Convenience operator for serializing. See WriteUInt8() */ virtual wxSerialize &operator <<(wxUint8 value) { WriteUint8(value); return *this; }; /** Convenience operator for serializing. See WriteUInt16() */ virtual wxSerialize &operator <<(wxUint16 value) { WriteUint16(value); return *this; }; /** Convenience operator for serializing. See WriteUInt32() */ virtual wxSerialize &operator <<(wxUint32 value) { WriteUint32(value); return *this; }; /** Convenience operator for serializing. See WriteUInt64() */ virtual wxSerialize &operator <<(wxUint64 value) { WriteUint64(value); return *this; }; /** Convenience operator for serializing. See WriteInt() */ virtual wxSerialize &operator <<(int value) { WriteInt(value); return *this; }; /** Convenience operator for serializing. See WriteString() */ virtual wxSerialize &operator <<(const wxString& value) { WriteString(value); return *this; }; /** Convenience operator for serializing. See WriteArrayString() */ virtual wxSerialize &operator <<(const wxArrayString& value) { WriteArrayString(value); return *this; }; /** Convenience operator for serializing. See WriteString() */ virtual wxSerialize &operator <<(const wxChar* pvalue) { wxString value(pvalue); WriteString(value); return *this; }; /** Convenience operator for serializing. See WriteBool() */ virtual wxSerialize &operator <<(bool value) { WriteBool(value); return *this; }; /** Convenience operator for serializing. See WriteDouble() */ virtual wxSerialize &operator <<(wxFloat64 value) { WriteDouble(value); return *this; }; /** Convenience operator for serializing. See WriteDateTime() */ virtual wxSerialize &operator <<(const wxDateTime &value) { WriteDateTime(value); return *this; }; /** Convenience operator for serializing. See WriteIntInt() */ virtual wxSerialize &operator <<(const wxSize& value) { WriteIntInt(value.GetWidth(), value.GetHeight()); return *this; }; /** Convenience operator for serializing. See WriteIntInt() */ virtual wxSerialize &operator <<(const wxPoint& value) { WriteIntInt(value.x, value.y); return *this; }; // -- loading serializers -- /** Convenience operator for serializing. See ReadUint8() */ virtual wxSerialize &operator >>(wxUint8& value) { ReadUint8(value); return *this; }; /** Convenience operator for serializing. See ReadUint16() */ virtual wxSerialize &operator >>(wxUint16& value) { ReadUint16(value); return *this; }; /** Convenience operator for serializing. See ReadUInt32() */ virtual wxSerialize &operator >>(wxUint32& value) { ReadUint32(value); return *this; }; /** Convenience operator for serializing. See ReadUInt64() */ virtual wxSerialize &operator >>(wxUint64& value) { ReadUint64(value); return *this; }; /** Convenience operator for serializing. See ReadInt() */ virtual wxSerialize &operator >>(int &value) { ReadInt(value); return *this; }; /** Convenience operator for serializing. See ReadString() */ virtual wxSerialize &operator >>(wxString& value) { ReadString(value); return *this; }; /** Convenience operator for serializing. See ReadArrayString() */ virtual wxSerialize &operator >>(wxArrayString& value) { ReadArrayString(value); return *this; }; /** Convenience operator for serializing. See ReadBool() */ virtual wxSerialize &operator >>(bool& value) { ReadBool(value); return *this; }; /** Convenience operator for serializing. See ReadDouble() */ virtual wxSerialize &operator >>(wxFloat64& value) { ReadDouble(value); return *this; }; /** Convenience operator for serializing. See ReadDateTime() */ virtual wxSerialize &operator >>(wxDateTime& value) { ReadDateTime(value); return *this; }; /** Convenience operator for serializing. See ReadIntInt() */ virtual wxSerialize &operator >>(wxSize& value) { int iWidth, iHeight; if (ReadIntInt(iWidth, iHeight)) { value.SetWidth(iWidth); value.SetHeight(iHeight); } return *this; }; /** Convenience operator for serializing. See ReadIntInt() */ virtual wxSerialize &operator >>(wxPoint& value) { ReadIntInt(value.x, value.y); return *this; }; /** Loads a boolean from stream. When the next record to be read is not a bool, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. */ bool ReadBool(bool& value); /** Variant of ReadBool() that will provide a default if the read was not succesful */ bool ReadBool(bool& value, bool defval) { bool val = ReadBool(value); if(!val) value = defval; return val; }; /** Loads a wxUint8 from stream. When the next record to be read is not a wxUint8, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. */ bool ReadUint8(wxUint8& value); /** Variant of ReadUint8() that will provide a default if the read was not succesful */ bool ReadUint8(wxUint8& value, wxUint8 defval) { bool val = ReadUint8(value); if(!val) value = defval; return val; }; /** Loads a wxUint16 from stream. When the next record to be read is not a wxUint16, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. */ bool ReadUint16(wxUint16& value); /** Variant of ReadUint16() that will provide a default if the read was not succesful */ bool ReadUint16(wxUint16& value, wxUint16 defval) { bool val = ReadUint16(value); if(!val) value = defval; return val; }; /** Loads a wxUint32 from stream. When the next record to be read is not a wxUint32, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. */ bool ReadUint32(wxUint32& value); /** Variant of ReadUint32() that will provide a default if the read was not succesful */ bool ReadUint32(wxUint32& value, wxUint32 defval) { bool val = ReadUint32(value); if(!val) value = defval; return val; }; /** Loads a wxUint64 from stream. When the next record to be read is not a wxUint64, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. */ bool ReadUint64(wxUint64& value); /** Variant of ReadUint64() that will provide a default if the read was not succesful */ bool ReadUint64(wxUint64& value, wxUint64 defval) { bool val = ReadUint64(value); if(!val) value = defval; return val; }; /** Loads an int from stream. When the next record to be read is not an int, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN: Reading an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is using the wxUint16, wxUint32, wxUint64 data types. However, this method is made as safe as possible by also storing the size of the int on the stream. This means reading a stored integer of 4 bytes (32 bits) it will be read back as 32 bits, and casted to a 64 bits integer. In theory, this should work as the sign flag for a 32 bits container will be extended to match the 64 bits container. The only worst case scenario that exists is when an 64 bits int is written, and it needs to be casted to a 32 bits integer, problems can occur because the destination container may be too small. No error will be logged so be careful storing extreme values that might not fit in smaller integers. */ bool ReadInt(int& value); /** Loads an pair of ints from stream. When the next record to be read is not an int, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN: Reading an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is using the wxUint16, wxUint32, wxUint64 data types. However, this method is made as safe as possible by also storing the size of the int on the stream. This means reading a stored integer of 4 bytes (32 bits) it will be read back as 32 bits, and casted to a 64 bits integer. In theory, this should work as the sign flag for a 32 bits container will be extended to match the 64 bits container. The only worst case scenario that exists is when an 64 bits int is written, and it needs to be casted to a 32 bits integer, problems can occur because the destination container may be too small. No error will be logged so be careful storing extreme values that might not fit in smaller integers. */ bool ReadIntInt(int& value1, int& value2); /** Variant of ReadIntInt() that will provide a default if the read was not succesful */ bool ReadIntInt(int& value1, int& value2, int defval1, int defval2) { bool val = ReadIntInt(value1, value2); if(!val) { value1 = defval1; value2 = defval2; } return val; }; /** Variant of ReadInt() that will provide a default if the read was not succesful */ bool ReadInt(int& value, int defval) { bool val = ReadInt(value); if(!val) value = defval; return val; }; /** Loads a wxFloat64 from stream. When the next record to be read is not a wxFloat64, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. The wxFloat64 is stored as a IEEE converted standard wxFloat64, and is x-platform so it can be shared between other os'es */ bool ReadDouble(wxFloat64& value); /** Variant of ReadDouble() that will provide a default if the read was not succesful */ bool ReadDouble(wxFloat64& value, wxFloat64 defval) { bool val = ReadDouble(value); if(!val) value = defval; return val; }; /** Loads a wxString from stream. When the next record to be read is not a wxString, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. For the string first the count is stored and then the string itself. When storing an empty string, only a 0 count is stored. */ bool ReadString(wxString& value); /** Variant of ReadString() that will provide a default if the read was not succesful */ bool ReadString(wxString& value, const wxString &defval) { bool val = ReadString(value); if(!val) value = defval; return val; }; /** Loads a wxArrayString from stream. When the next record to be read is not a wxArrayString, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. For the string first the count is stored and then the string itself. When storing an empty string, only a 0 count is stored. */ bool ReadArrayString(wxArrayString& value); /** Variant of ReadArrayString() that will provide a default if the read was not succesful */ bool ReadString(wxArrayString& value, const wxArrayString &defval) { bool val = ReadArrayString(value); if(!val) value = defval; return val; }; /** Loads a wxDateTime from stream. When the next record to be read is not a wxDateTime, an error will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is in an error state, the value is not touched. Please note that when an error occurs, all subsequent loading from the stream will result in an error. This is to provide a save mechanism out of a read error. So instead of checking every value you read back yourself, they can be default and won't produce erratic behaviour. The value returned is true when the read was succesful, false when nothing was read. For the string first the count is stored and then the string itself. When storing an empty string, only a 0 count is stored. */ bool ReadDateTime(wxDateTime& value); /** Reads a record from the stream. The wxMemoryBuffer is a growable buffer class, which will contain the data being read. The length of the buffer will be the length present so there is no need to define a pre-allocated buffer. When the referenced buffer runs out of scope in your code, it is destroyed safely. The read buffer can be up to 4 Gb (count is 32 bits). The buffer will be considered an atomic element so when the read fails for the specified number of counts, the buffer is corrupted. This will ofcourse only happen when the stream unexpectedly terminates and will also occur with other atomic values like reading an integer etc. NOTICE FOR PLATFORM BINARY COMPATIBILITY: No conversion takes place when writing the buffer. It is written as a sequence of bytes to the stream. So be careful storing the whole data back into a structure (little to big endian) */ bool Read(wxMemoryBuffer &buffer); /** Saves a bool to stream. It writes a header type first, and after that the value. This means when the boolean is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. */ bool WriteBool(bool value); /** Saves a double as a wxFloat64 to stream. It writes a header type first, and after that the value. This means when the wxFloat64 is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxFloat64 is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. */ bool WriteDouble(wxFloat64 value); /** Saves a wxUint8 to stream. It writes a header type first, and after that the value. This means when the wxUint8 is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxUint8 is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. */ bool WriteUint8(wxUint8 value); /** Saves a wxUint16 to stream. It writes a header type first, and after that the value. This means when the wxUint16 is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxUint16 is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. */ bool WriteUint16(wxUint16 value); /** Saves a wxUint32 to stream. It writes a header type first, and after that the value. This means when the wxUint32 is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxUint32 is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. */ bool WriteUint32(wxUint32 value); /** Saves a wxUint64 to stream. It writes a header type first, and after that the value. This means when the wxUint64 is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxUint64 is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. */ bool WriteUint64(wxUint64 value); /** Saves an int to stream. It writes a header type first, and after that the value. This means when the int is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN: Writing an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is using the wxUint16, wxUint32, wxUint64 data types. See ReadInt() for more information. */ bool WriteInt(int value); /** Saves a pair of ints to a stream. It is used for wxSize and wxPoint. It writes a header type first, and after that the value. This means when the int is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN: Writing an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is using the wxUint16, wxUint32, wxUint64 data types. See ReadInt() for more information. */ bool WriteIntInt(int value1, int value2); /** Saves a wxString to stream. It writes a header type first, and after that the value. This means when the wxString is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxString is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. First the count is stored, then the string. With an empty string only the 0 count is stored. */ bool WriteString(const wxString& value); /** Saves a wxArrayString to stream. It writes a header type first, and after that the value. This means when the wxArrayString is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxString is saved in a cross platform compatible mode, which means the streams can be interchanged between MacOS, Linux and Windows without problems. First the count is stored, then the string. With an empty string only the 0 count is stored. */ bool WriteArrayString(const wxArrayString& value); /** Saves a wxDateTime to stream. It writes a header type first, and after that the value. This means when the wxDateTime is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. The wxDateTime is saved in a cross platform compatible mode (e.g. the number of ticks). Please be aware that no timezone information or daylight saving information is stored. So if the date read back on a different PC is mis interpreted because the PC is in a different time zone you must find other means of syncing them. */ bool WriteDateTime(const wxDateTime& value); /** Saves an arbitrary amount of bytes to the stream using wxMemoryBuffer. It writes a header type first, and after that the count to be saved and the bytes in the wxMemoryBuffer. This means when the data is read back when it is not expected, an error occurs and the program can terminate gracefully because no bogus values are read back. True is returned when the value is saved properly, and false when it's not. Data can be saved up to 4Gb. This is not recommended (nor physically possible I think) but the limit is 32 bits for the count. NOTICE FOR PLATFORM BINARY COMPATIBILITY: No conversion takes place when writing the buffer. It is written as a sequence of bytes to the stream. So be careful storing the whole data back into a structure (little to big endian) */ bool Write(const wxMemoryBuffer &buffer); // -- arbitrary functions -- /** Returns the header that was read by this stream. When writing, the header string contains the passed header by the constructor. See GetArchiveStatus() for more information. */ wxString GetHeaderString() const { return m_headerStr; }; /** Returns the version of the stream currently beaing read, or else the version of the stream when it was created. This can be used to exclude parts while reading, if the stream version does not support newly added veriables like; \code // we expect version 1001 at least (lower versions are possible) wxSerialize a(in_stream, 1001); if(a.IsStoring()) { a << m_name; // added since 1001 a << m_addr; } else { a >> m_name; // only read when we are above 1000 if(a.GetVersion() > 1000) a >> m_addr; } \endcode */ size_t GetVersion() const { return m_version; }; /** Returns the wxSerializeStatus object. You usually do this when you just read a stream, to get more information about it later, like the version, header, and if the stream contained newer data which means writing it back with the older data model you might be using, you destroy information */ const wxSerializeStatus &GetArchiveStatus() const { return m_status; }; /// return the version string of the class wxSerialize static wxString GetLibVersionString (); /// return the major version number of the class wxSerialize static long GetLibVersionMajor (); /// return the minor version number of the class wxSerialize static long GetLibVersionMinor (); private: // error types enum { wxSERIALIZE_ERR_OK = 0, wxSERIALIZE_ERR_EOF = -1, wxSERIALIZE_ERR_ILL = -2, wxSERIALIZE_ERR_MEM = -3 }; // all errors that can occur enum { wxSERIALIZE_ERR_STR_HEADER_s1_s2, /// Illegal header in start of stream wxSERIALIZE_ERR_STR_WRONGVERSION_s1_s2, /// Not a proper version wxSERIALIZE_ERR_STR_BADISTREAM, /// Bad input stream wxSERIALIZE_ERR_STR_BADOSTREAM, /// Bad output stream wxSERIALIZE_ERR_STR_NOVERHDR, /// Cannot write version or header info to stream wxSERIALIZE_ERR_STR_NOHEADER_s1, /// Header expected but not found wxSERIALIZE_ERR_STR_NOVERSION, /// No version found in stream wxSERIALIZE_ERR_STR_NOWRITE, /// Cannot write in read mode wxSERIALIZE_ERR_STR_NOREAD, /// Cannot read in write mode wxSERIALIZE_ERR_STR_EOF, /// End of file error in read mode wxSERIALIZE_ERR_STR_WRONGCHUNK_s1_s2, /// Expected header x but got y wxSERIALIZE_ERR_STR_MEMORY_s1, /// Cannot allocate x bytes of memory wxSERIALIZE_ERR_STR_READSIZE, /// Read size mismatch wxSERIALIZE_ERR_STR_RINTSIZE, /// Integer size mismatch wxSERIALIZE_ERR_STR_SINTSIZE, /// Ditto wxSERIALIZE_ERR_STR_ILL_LEAVE, /// Illegal leave marker found wxSERIALIZE_ERR_STR_ILL_UNKNOWN_HDR_s1, /// Unknown header error wxSERIALIZE_ERR_STR_ILL_LEVEL /// Sync level failure! }; // headers enum { wxSERIALIZE_HDR_BOOL = 'b', wxSERIALIZE_HDR_INT8 = 'c', wxSERIALIZE_HDR_INT16 = 'w', wxSERIALIZE_HDR_INT32 = 'l', wxSERIALIZE_HDR_INT64 = 'q', wxSERIALIZE_HDR_DOUBLE = 'd', wxSERIALIZE_HDR_STRING = 's', wxSERIALIZE_HDR_RECORD = 'r', wxSERIALIZE_HDR_INT = 'i', wxSERIALIZE_HDR_ARRSTRING = 'a', wxSERIALIZE_HDR_DATETIME = 't', wxSERIALIZE_HDR_ENTER = '<', wxSERIALIZE_HDR_LEAVE = '>', wxSERIALIZE_HDR_INTINT = 'I' }; // pre-check if stream is capable of writing, returns errors // when condition is not good (like input stream is defined // instead of output) bool CanStore(); // pre-check if stream is capable of reading, returns errors // when condition is not good (like output stream is defined // instead of input) bool CanLoad(); // loads the header, and sets an error when the expected header is not the same // as the header given int LoadChunkHeader(int expheader); // writes a single char to the stream void WriteChar(wxUint8 value); // sets an error with arbitrary message using wxLogError int LogError(int err, int msgcode, const wxString &s1 = wxEmptyString, const wxString &s2 = wxEmptyString); // attempts to find the marker that belongs to this // current level. We assume the current count but increment when we // find one, decrement when we find another .. void FindCurrentEnterLevel(); // finds current leave level. This means all enter levels we encounter // we will skip, until we have the level of our object - 1 void FindCurrentLeaveLevel(); // skip the data belonging by the header void SkipData(wxUint8 hdr); // load the buffer from a stream void Load(wxMemoryBuffer &buf); // reads a string (without header) wxString LoadString(); // reads an array string without header wxArrayString LoadArrayString(); // reads a datetime without header wxDateTime LoadDateTime(); // reads a single char from the stream, returns '\0' when none can be read wxUint8 LoadChar(); // see wxUint8 LoadChar() wxUint8 LoadUint8(); // reads a wxUint16 (without header) wxUint16 LoadUint16(); // reads a wxUint32 (without header) wxUint32 LoadUint32(); // reads a wxUint64 (without header) wxUint64 LoadUint64(); // reads a wxFloat64 (without header) wxFloat64 LoadDouble(); // loads integer int LoadInt(); // loads pair of integers void LoadIntInt (int& value1, int& value2); // reads a bool (without header) bool LoadBool(); // writes a wxUint8 without header void SaveChar(wxUint8 value); // see SaveChar(wxUint8) void SaveUint8(wxUint8 value); // writes a wxUint16 (without header) void SaveUint16(wxUint16 value); // writes a wxUint32 (without header) void SaveUint32(wxUint32 value); // writes a wxUint64 (without header) void SaveUint64(wxUint64 value); // writes a string (without header) void SaveString(const wxString &str); // translates header name to something readable for errors wxString GetHeaderName(int headername); /** Sets the error to a specific condition. Valid error conditions are - wxSERIALIZE_OK - wxSERIALIZE_ERR_EOF - wxSERIALIZE_ERR_ILL - wxSERIALIZE_ERR_MEM The error will not change from the last error condition when it is already set. Meaning it will keep the last error, even when others follow. */ void SetError(int err) { // only set error when we are still ok if(m_errorCode == wxSERIALIZE_ERR_OK) m_errorCode = err; }; // initialize function void InitAll(); private: // last error code issued in wxSerialize int m_errorCode; // the last logged error message wxString m_errMsg; // the header received wxString m_headerStr; // The version written or read size_t m_version; // opened status (true if open) Close() will close it bool m_opened; // partial save or load mode bool m_partialMode; // write or read mode of the stream bool m_writeMode; // under the hood output data stream (this stream is maintained by us) wxOutputStream &m_odstr; // under the hood output data stream (this stream is maintained by us) wxInputStream &m_idstr; // object marker level. Any level above 0 means we have encountered // an EnterObject and LeaveObject. int m_objectLevel; // flag to indicate that we encountered a LEAVE or ENTER marker. This means // no read should be done until we reached the LeaveObject() that // belongs to our level. bool m_haveBoundary; // last boundary encountered unsigned char m_lastBoundary; // temp vars that have only the purpose to initialize streams that // need to be initialized by ref. By using wxStringStream variants // we can safely read and load from them when this error might occur wxString m_tmpostr, m_tmpistr; wxStringOutputStream m_otmp; wxStringInputStream m_itmp; // The archive status object wxSerializeStatus m_status; }; #endif