blob: f3473b9a3a6903137eb64b6658086507f684b72e [file] [log] [blame]
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// basic_oarchive.cpp:
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org for updates, documentation, and revision history.
#include <boost/config.hpp> // msvc 6.0 needs this for warning suppression
#include <cassert>
#include <set>
#include <cstddef> // NULL
#include <boost/limits.hpp>
#include <boost/serialization/state_saver.hpp>
#include <boost/serialization/throw_exception.hpp>
// including this here to work around an ICC in intel 7.0
// normally this would be part of basic_oarchive.hpp below.
#define BOOST_ARCHIVE_SOURCE
// include this to prevent linker errors when the
// same modules are marked export and import.
#define BOOST_SERIALIZATION_SOURCE
#include <boost/archive/detail/decl.hpp>
#include <boost/archive/basic_archive.hpp>
#include <boost/archive/detail/basic_oserializer.hpp>
#include <boost/archive/detail/basic_pointer_oserializer.hpp>
#include <boost/archive/detail/basic_oarchive.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/serialization/extended_type_info.hpp>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4251 4231 4660 4275)
#endif
using namespace boost::serialization;
namespace boost {
namespace archive {
namespace detail {
class basic_oarchive_impl {
friend class basic_oarchive;
unsigned int m_flags;
//////////////////////////////////////////////////////////////////////
// information about each serialized object saved
// keyed on address, class_id
struct aobject
{
const void * address;
class_id_type class_id;
object_id_type object_id;
bool operator<(const aobject &rhs) const
{
assert(NULL != address);
assert(NULL != rhs.address);
if( address < rhs.address )
return true;
if( address > rhs.address )
return false;
return class_id < rhs.class_id;
}
aobject & operator=(const aobject & rhs)
{
address = rhs.address;
class_id = rhs.class_id;
object_id = rhs.object_id;
return *this;
}
aobject(
const void *a,
class_id_type class_id_,
object_id_type object_id_
) :
address(a),
class_id(class_id_),
object_id(object_id_)
{}
aobject() : address(NULL){}
};
// keyed on class_id, address
typedef std::set<aobject> object_set_type;
object_set_type object_set;
//////////////////////////////////////////////////////////////////////
// information about each serialized class saved
// keyed on type_info
struct cobject_type
{
const basic_oserializer * m_bos_ptr;
const class_id_type m_class_id;
bool m_initialized;
cobject_type(
std::size_t class_id,
const basic_oserializer & bos
) :
m_bos_ptr(& bos),
m_class_id(class_id),
m_initialized(false)
{}
cobject_type(const basic_oserializer & bos)
: m_bos_ptr(& bos)
{}
cobject_type(
const cobject_type & rhs
) :
m_bos_ptr(rhs.m_bos_ptr),
m_class_id(rhs.m_class_id),
m_initialized(rhs.m_initialized)
{}
// the following cannot be defined because of the const
// member. This will generate a link error if an attempt
// is made to assign. This should never be necessary
// use this only for lookup argument
cobject_type & operator=(const cobject_type &rhs);
bool operator<(const cobject_type &rhs) const {
return *m_bos_ptr < *(rhs.m_bos_ptr);
}
};
// keyed on type_info
typedef std::set<cobject_type> cobject_info_set_type;
cobject_info_set_type cobject_info_set;
// list of objects initially stored as pointers - used to detect errors
// keyed on object id
std::set<object_id_type> stored_pointers;
// address of the most recent object serialized as a poiner
// whose data itself is now pending serialization
const void * pending_object;
const basic_oserializer * pending_bos;
basic_oarchive_impl(unsigned int flags) :
m_flags(flags),
pending_object(NULL),
pending_bos(NULL)
{}
const cobject_type &
find(const basic_oserializer & bos);
const basic_oserializer *
find(const serialization::extended_type_info &ti) const;
//public:
const cobject_type &
register_type(const basic_oserializer & bos);
void save_object(
basic_oarchive & ar,
const void *t,
const basic_oserializer & bos
);
void save_pointer(
basic_oarchive & ar,
const void * t,
const basic_pointer_oserializer * bpos
);
};
//////////////////////////////////////////////////////////////////////
// basic_oarchive implementation functions
// given a type_info - find its bos
// return NULL if not found
inline const basic_oserializer *
basic_oarchive_impl::find(const serialization::extended_type_info & ti) const {
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4511 4512)
#endif
class bosarg :
public basic_oserializer
{
bool class_info() const {
assert(false);
return false;
}
// returns true if objects should be tracked
bool tracking(const unsigned int) const {
assert(false);
return false;
}
// returns class version
version_type version() const {
assert(false);
return version_type(0);
}
// returns true if this class is polymorphic
bool is_polymorphic() const{
assert(false);
return false;
}
void save_object_data(
basic_oarchive & /*ar*/, const void * /*x*/
) const {
assert(false);
}
public:
bosarg(const serialization::extended_type_info & eti) :
boost::archive::detail::basic_oserializer(eti)
{}
};
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
bosarg bos(ti);
cobject_info_set_type::const_iterator cit
= cobject_info_set.find(cobject_type(bos));
// it should already have been "registered" - see below
if(cit == cobject_info_set.end()){
// if an entry is not found in the table it is because a pointer
// of a derived class has been serialized through its base class
// but the derived class hasn't been "registered"
return NULL;
}
// return pointer to the real class
return cit->m_bos_ptr;
}
inline const basic_oarchive_impl::cobject_type &
basic_oarchive_impl::find(const basic_oserializer & bos)
{
std::pair<cobject_info_set_type::iterator, bool> cresult =
cobject_info_set.insert(cobject_type(cobject_info_set.size(), bos));
return *(cresult.first);
}
inline const basic_oarchive_impl::cobject_type &
basic_oarchive_impl::register_type(
const basic_oserializer & bos
){
cobject_type co(cobject_info_set.size(), bos);
std::pair<cobject_info_set_type::const_iterator, bool>
result = cobject_info_set.insert(co);
return *(result.first);
}
inline void
basic_oarchive_impl::save_object(
basic_oarchive & ar,
const void *t,
const basic_oserializer & bos
){
// if its been serialized through a pointer and the preamble's been done
if(t == pending_object && pending_bos == & bos){
// just save the object data
ar.end_preamble();
(bos.save_object_data)(ar, t);
return;
}
// get class information for this object
const cobject_type & co = register_type(bos);
if(bos.class_info()){
if( ! co.m_initialized){
ar.vsave(class_id_optional_type(co.m_class_id));
ar.vsave(tracking_type(bos.tracking(m_flags)));
ar.vsave(version_type(bos.version()));
(const_cast<cobject_type &>(co)).m_initialized = true;
}
}
// we're not tracking this type of object
if(! bos.tracking(m_flags)){
// just windup the preamble - no object id to write
ar.end_preamble();
// and save the data
(bos.save_object_data)(ar, t);
return;
}
// look for an existing object id
object_id_type oid(object_set.size());
// lookup to see if this object has already been written to the archive
basic_oarchive_impl::aobject ao(t, co.m_class_id, oid);
std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool>
aresult = object_set.insert(ao);
oid = aresult.first->object_id;
// if its a new object
if(aresult.second){
// write out the object id
ar.vsave(oid);
ar.end_preamble();
// and data
(bos.save_object_data)(ar, t);
return;
}
// check that it wasn't originally stored through a pointer
if(stored_pointers.end() != stored_pointers.find(oid)){
// this has to be a user error. loading such an archive
// would create duplicate objects
boost::serialization::throw_exception(
archive_exception(archive_exception::pointer_conflict)
);
}
// just save the object id
ar.vsave(object_reference_type(oid));
ar.end_preamble();
return;
}
// save a pointer to an object instance
inline void
basic_oarchive_impl::save_pointer(
basic_oarchive & ar,
const void * t,
const basic_pointer_oserializer * bpos_ptr
){
const basic_oserializer & bos = bpos_ptr->get_basic_serializer();
std::size_t original_count = cobject_info_set.size();
const cobject_type & co = register_type(bos);
if(! co.m_initialized){
ar.vsave(co.m_class_id);
// if its a previously unregistered class
if((cobject_info_set.size() > original_count)){
if(bos.is_polymorphic()){
const serialization::extended_type_info *eti = & bos.get_eti();
const char * key = NULL;
if(NULL != eti)
key = eti->get_key();
if(NULL != key){
// the following is required by IBM C++ compiler which
// makes a copy when passing a non-const to a const. This
// is permitted by the standard but rarely seen in practice
const class_name_type cn(key);
// write out the external class identifier
ar.vsave(cn);
}
else
// without an external class name
// we won't be able to de-serialize it so bail now
boost::serialization::throw_exception(
archive_exception(archive_exception::unregistered_class)
);
}
}
if(bos.class_info()){
ar.vsave(tracking_type(bos.tracking(m_flags)));
ar.vsave(version_type(bos.version()));
}
(const_cast<cobject_type &>(co)).m_initialized = true;
}
else{
ar.vsave(class_id_reference_type(co.m_class_id));
}
// if we're not tracking
if(! bos.tracking(m_flags)){
// just save the data itself
ar.end_preamble();
serialization::state_saver<const void *> x(pending_object);
serialization::state_saver<const basic_oserializer *> y(pending_bos);
pending_object = t;
pending_bos = & bpos_ptr->get_basic_serializer();
bpos_ptr->save_object_ptr(ar, t);
return;
}
object_id_type oid(object_set.size());
// lookup to see if this object has already been written to the archive
basic_oarchive_impl::aobject ao(t, co.m_class_id, oid);
std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool>
aresult = object_set.insert(ao);
oid = aresult.first->object_id;
// if the saved object already exists
if(! aresult.second){
// append the object id to he preamble
ar.vsave(object_reference_type(oid));
// and windup.
ar.end_preamble();
return;
}
// append id of this object to preamble
ar.vsave(oid);
ar.end_preamble();
// and save the object itself
serialization::state_saver<const void *> x(pending_object);
serialization::state_saver<const basic_oserializer *> y(pending_bos);
pending_object = t;
pending_bos = & bpos_ptr->get_basic_serializer();
bpos_ptr->save_object_ptr(ar, t);
// add to the set of object initially stored through pointers
stored_pointers.insert(oid);
}
} // namespace detail
} // namespace archive
} // namespace boost
//////////////////////////////////////////////////////////////////////
// implementation of basic_oarchive functions
namespace boost {
namespace archive {
namespace detail {
BOOST_ARCHIVE_DECL(BOOST_PP_EMPTY())
basic_oarchive::basic_oarchive(unsigned int flags)
: pimpl(new basic_oarchive_impl(flags))
{}
BOOST_ARCHIVE_DECL(BOOST_PP_EMPTY())
basic_oarchive::~basic_oarchive()
{
delete pimpl;
}
BOOST_ARCHIVE_DECL(void)
basic_oarchive::save_object(
const void *x,
const basic_oserializer & bos
){
pimpl->save_object(*this, x, bos);
}
BOOST_ARCHIVE_DECL(void)
basic_oarchive::save_pointer(
const void * t,
const basic_pointer_oserializer * bpos_ptr
){
pimpl->save_pointer(*this, t, bpos_ptr);
}
BOOST_ARCHIVE_DECL(void)
basic_oarchive::register_basic_serializer(const basic_oserializer & bos){
pimpl->register_type(bos);
}
BOOST_ARCHIVE_DECL(library_version_type)
basic_oarchive::get_library_version() const{
return BOOST_ARCHIVE_VERSION();
}
BOOST_ARCHIVE_DECL(unsigned int)
basic_oarchive::get_flags() const{
return pimpl->m_flags;
}
BOOST_ARCHIVE_DECL(void)
basic_oarchive::end_preamble(){
}
} // namespace detail
} // namespace archive
} // namespace boost
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif