/*------------------------------------------------------------------------------ Author: Andy Rushton Copyright: (c) Andy Rushton, 2004 License: BSD License, see ../docs/license.html ------------------------------------------------------------------------------*/ #include "stringio.hpp" #include "fileio.hpp" //////////////////////////////////////////////////////////////////////////////// // enumeration types template void dump_enum(dump_context& context, const T& data) throw(persistent_dump_failed) { dump(context, (unsigned)data); } template void restore_enum(restore_context& context, T& data) throw(persistent_restore_failed) { unsigned value = 0; restore(context, value); data = (T)value; } //////////////////////////////////////////////////////////////////////////////// // STL strings template void dump_basic_string(dump_context& context, const std::basic_string& data) throw(persistent_dump_failed) { dump(context, data.size()); for (size_t i = 0; i < data.size(); i++) dump(context,data[i]); } template void restore_basic_string(restore_context& context, std::basic_string& data) throw(persistent_restore_failed) { data.erase(); size_t size = 0; restore(context, size); for (size_t i = 0; i < size; i++) { charT ch; restore(context,ch); data += ch; } } //////////////////////////////////////////////////////////////////////////////// // Pointers // format: magic_key [ data ] template void dump_pointer(dump_context& context, const T* const data) throw(persistent_dump_failed) { // register the address and get the magic key for it std::pair mapping = context.pointer_map(data); dump(context,mapping.second); // if the address is null, then that is all that we need to do // however, if it is non-null and this is the first sight of the address, dump the contents if (data && !mapping.first) dump(context,*data); } template void restore_pointer(restore_context& context, T*& data) throw(persistent_restore_failed) { if (data) { delete data; data = 0; } // get the magic key unsigned magic = 0; restore(context,magic); // now lookup the magic key to see if this pointer has already been restored // null pointers are always flagged as already restored std::pair address = context.pointer_map(magic); if (address.first) { // seen before, so simply assign the old address data = (T*)address.second; } else { // this pointer has never been seen before and is non-null data = new T(); restore(context,*data); // add this pointer to the set of already seen objects context.pointer_add(magic,data); } } //////////////////////////////////////////////////////////////////////////////// // Cross-reference pointers // format: magic_key [ data ] template void dump_xref(dump_context& context, const T* const data) throw(persistent_dump_failed) { // register the address and get the magic key for it std::pair mapping = context.pointer_map(data); // if this is the first view of this pointer, simply throw an exception if (!mapping.first) throw persistent_dump_failed("tried to dump a cross-reference not seen before"); // otherwise, just dump the magic key dump(context,mapping.second); } template void restore_xref(restore_context& context, T*& data) throw(persistent_restore_failed) { // Note: I do not try to delete the old data because this is a cross-reference // get the magic key unsigned magic = 0; restore(context,magic); // now lookup the magic key to see if this pointer has already been restored // null pointers are always flagged as already restored std::pair address = context.pointer_map(magic); // if this is the first view of this pointer, simply throw an exception if (!address.first) throw persistent_restore_failed("tried to restore a cross-reference not seen before"); // seen before, so simply assign the old address data = (T*)address.second; } //////////////////////////////////////////////////////////////////////////////// // Polymorphous classes using the callback approach // format: address [ key data ] template void dump_polymorph(dump_context& context, const T* data) throw(persistent_dump_failed) { try { // register the address and get the magic key for it std::pair mapping = context.pointer_map(data); dump(context,mapping.second); // if the address is null, then that is all that we need to do // however, if it is non-null and this is the first sight of the address, dump the contents if (data && !mapping.first) { // callback method - get the callback data and perform the dump // this will throw persistent_illegal_type if not recognised, thus the try block dump_context::callback_data callback = context.lookup_type(typeid(*data)); // dump the magic key for the type dump(context, callback.first); // now call the callback that dumps the subclass callback.second(context,data); } } catch (const persistent_illegal_type& except) { // convert this to a simpler dump failed exception throw persistent_dump_failed(except.what()); } } template void restore_polymorph(restore_context& context, T*& data) throw(persistent_restore_failed) { try { // first delete any previous object pointed to since the restore creates the object of the right subclass if (data) { delete data; data = 0; } // get the magic key unsigned magic = 0; restore(context,magic); // now lookup the magic key to see if this pointer has already been restored // null pointers are always flagged as already restored std::pair address = context.pointer_map(magic); if (address.first) { // seen before, so simply map it to the existing address data = (T*)address.second; } else { // now restore the magic key that denotes the particular subclass unsigned short key = 0; restore(context, key); // callback approach // call the create callback to create an object of the right type // then call the restore callback to get the contents // this will throw persistent_illegal_type if not recognised - this is caught below restore_context::callback_data callbacks = context.lookup_type(key); void* void_data = callbacks.first(); data = (T*)void_data; try { callbacks.second(context,void_data); } catch(...) { // tidy up any memory allocated in this function if (data) { delete data; data = 0; } throw; } // add this pointer to the set of already seen objects context.pointer_add(magic,data); } } catch (const persistent_illegal_type& exception) { // convert this to a simpler dump failed exception throw persistent_restore_failed(exception.what()); } } //////////////////////////////////////////////////////////////////////////////// // Polymorphous classes using the interface approach // format: address [ key data ] template void dump_interface(dump_context& context, const T* data) throw(persistent_dump_failed) { try { // register the address and get the magic key for it std::pair mapping = context.pointer_map(data); dump(context,mapping.second); // if the address is null, then that is all that we need to do // however, if it is non-null and this is the first sight of the address, dump the contents if (data && !mapping.first) { // interface method // the lookup just finds the magic key and the type has a dump method // this will throw persistent_illegal_type if the type is not registered unsigned short key = context.lookup_interface(typeid(*data)); // dump the magic key for the type dump(context, key); // now call the dump method defined by the interface data->dump(context); } } catch (const persistent_illegal_type& exception) { // convert this to a simpler dump failed exception throw persistent_dump_failed(exception.what()); } } template void restore_interface(restore_context& context, T*& data) throw(persistent_restore_failed) { try { // first delete any previous object pointed to since the restore creates the object of the right subclass if (data) { delete data; data = 0; } // get the magic key unsigned magic = 0; restore(context,magic); // now lookup the magic key to see if this pointer has already been restored // null pointers are always flagged as already restored std::pair address = context.pointer_map(magic); if (address.first) { // seen this address before, so simply assign it data = (T*)address.second; } else { // now restore the magic key that denotes the particular subclass unsigned short key = 0; restore(context, key); // interface approach // first clone the sample object stored in the map - lookup_interface can throw persistent_illegal_type data = (T*)(context.lookup_interface(key).clone()); // now restore the contents using the object's method try { data->restore(context); } catch(...) { if (data) { delete data; data = 0; } throw; } // add this pointer to the set of already seen objects context.pointer_add(magic,data); } } catch (const persistent_illegal_type& exception) { // convert this to a simpler dump failed exception throw persistent_restore_failed(exception.what()); } } //////////////////////////////////////////////////////////////////////////////// // format: data msB first, packed into bytes with lowest index at the byte's lsb // Note: the interface does not provide access to the internal storage and yet // to be efficient the std::bitset must be packed as bytes. Thus I have to do it the // hard way. template void dump_bitset(dump_context& context, const std::bitset& data) throw(persistent_dump_failed) { size_t bits = data.size(); size_t bytes = (bits+7)/8; for (size_t B = bytes; B--; ) { unsigned char ch = 0; for (size_t b = 0; b < 8; b++) { size_t bit = B*8+b; if (bit < bits && data[bit]) ch |= (0x01 << b); } dump(context,ch); } } template void restore_bitset(restore_context& context, std::bitset& data) throw(persistent_restore_failed) { size_t bits = data.size(); size_t bytes = (bits+7)/8; for (size_t B = bytes; B--; ) { unsigned char ch = 0; restore(context,ch); for (size_t b = 0; b < 8; b++) { size_t bit = B*8+b; if (bit >= bits) break; data.set(bit, ch & (0x01 << b) ? true : false); } } } //////////////////////////////////////////////////////////////////////////////// // complex template void dump_complex(dump_context& context, const std::complex& data) throw(persistent_dump_failed) { dump(context,data.real()); dump(context,data.imag()); } template void restore_complex(restore_context& context, std::complex& data) throw(persistent_restore_failed) { T re, im; restore(context,re); restore(context,im); data = std::complex(re, im); } //////////////////////////////////////////////////////////////////////////////// // deque template void dump_deque(dump_context& context, const std::deque& data) throw(persistent_dump_failed) { dump(context,data.size()); for (typename std::deque::const_iterator i = data.begin(); i != data.end(); i++) dump(context,*i); } template void restore_deque(restore_context& context, std::deque& data) throw(persistent_restore_failed) { data.clear(); size_t size = 0; restore(context,size); for (size_t i = 0; i < size; i++) { data.push_back(T()); restore(context,data.back()); } } //////////////////////////////////////////////////////////////////////////////// // list template void dump_list(dump_context& context, const std::list& data) throw(persistent_dump_failed) { dump(context,data.size()); for (typename std::list::const_iterator i = data.begin(); i != data.end(); i++) dump(context,*i); } template void restore_list(restore_context& context, std::list& data) throw(persistent_restore_failed) { data.clear(); size_t size = 0; restore(context,size); for (size_t i = 0; i < size; i++) { data.push_back(T()); restore(context,data.back()); } } //////////////////////////////////////////////////////////////////////////////// // pair template void dump_pair(dump_context& context, const std::pair& data) throw(persistent_dump_failed) { dump(context,data.first); dump(context,data.second); } template void restore_pair(restore_context& context, std::pair& data) throw(persistent_restore_failed) { restore(context,data.first); restore(context,data.second); } //////////////////////////////////////////////////////////////////////////////// // Map template void dump_map(dump_context& context, const std::map& data) throw(persistent_dump_failed) { dump(context,data.size()); for (typename std::map::const_iterator i = data.begin(); i != data.end(); i++) { dump(context,i->first); dump(context,i->second); } } template void restore_map(restore_context& context, std::map& data) throw(persistent_restore_failed) { data.clear(); size_t size = 0; restore(context,size); for (size_t j = 0; j < size; j++) { K key; restore(context,key); restore(context,data[key]); } } //////////////////////////////////////////////////////////////////////////////// // Multimap template void dump_multimap(dump_context& context, const std::multimap& data) throw(persistent_dump_failed) { dump(context,data.size()); for (typename std::multimap::const_iterator i = data.begin(); i != data.end(); i++) { dump(context,i->first); dump(context,i->second); } } template void restore_multimap(restore_context& context, std::multimap& data) throw(persistent_restore_failed) { data.clear(); size_t size = 0; restore(context,size); for (size_t j = 0; j < size; j++) { K key; restore(context,key); typename std::multimap::iterator v = data.insert(std::pair(key,T())); restore(context,v->second); } } //////////////////////////////////////////////////////////////////////////////// // Set template void dump_set(dump_context& context, const std::set& data) throw(persistent_dump_failed) { dump(context,data.size()); for (typename std::set::const_iterator i = data.begin(); i != data.end(); i++) dump(context,*i); } template void restore_set(restore_context& context, std::set& data) throw(persistent_restore_failed) { data.clear(); size_t size = 0; restore(context,size); typename std::set::iterator i = data.begin(); for (size_t j = 0; j < size; j++) { K key; restore(context,key); // inserting using an iterator is O(n) rather than O(n*log(n)) data.insert(i, key); } } //////////////////////////////////////////////////////////////////////////////// // Multiset // Shame that a multiset is not just a derivative of set since it has the same interface template void dump_multiset(dump_context& context, const std::multiset& data) throw(persistent_dump_failed) { dump(context,data.size()); for (typename std::multiset::const_iterator i = data.begin(); i != data.end(); i++) dump(context,*i); } template void restore_multiset(restore_context& context, std::multiset& data) throw(persistent_restore_failed) { data.clear(); size_t size = 0; restore(context,size); typename std::multiset::iterator i = data.begin(); for (size_t j = 0; j < size; j++) { K key; restore(context,key); // inserting using an iterator is O(n) rather than O(n*log(n)) data.insert(i, key); } } //////////////////////////////////////////////////////////////////////////////// //vector template void dump_vector(dump_context& context, const std::vector& data) throw(persistent_dump_failed) { dump(context,data.size()); for (size_t i = 0; i < data.size(); i++) dump(context,data[i]); } template void restore_vector(restore_context& context, std::vector& data) throw(persistent_restore_failed) { size_t size = 0; restore(context,size); data.resize(size); for (size_t i = 0; i < size; i++) restore(context,data[i]); } //////////////////////////////////////////////////////////////////////////////// template void dump_to_device(const T& source, otext& result, dump_context::installer installer) throw(persistent_dump_failed) { dump_context context(result); context.register_all(installer); dump(context, source); } template void restore_from_device(itext& source, T& result, restore_context::installer installer) throw(persistent_restore_failed) { restore_context context(source); context.register_all(installer); restore(context, result); } template void dump_to_string(const T& source, std::string& result, dump_context::installer installer) throw(persistent_dump_failed) { ostext output; dump_to_device(source, output, installer); result = output.get_string(); } template void restore_from_string(const std::string& source, T& result, restore_context::installer installer) throw(persistent_restore_failed) { istext input(source); restore_from_device(input, result, installer); } template void dump_to_file(const T& source, const std::string& filename, dump_context::installer installer) throw(persistent_dump_failed) { oftext output(filename); dump_to_device(source, output, installer); } template void restore_from_file(const std::string& filename, T& result, restore_context::installer installer) throw(persistent_restore_failed) { iftext input(filename); restore_from_device(input, result, installer); } ////////////////////////////////////////////////////////////////////////////////