1 /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
2 // basic_oarchive.cpp:
3
4 // (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
5 // Use, modification and distribution is subject to the Boost Software
6 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8
9 // See http://www.boost.org for updates, documentation, and revision history.
10
11 #include <boost/config.hpp> // msvc 6.0 needs this for warning suppression
12
13 #include <boost/assert.hpp>
14 #include <set>
15 #include <cstddef> // NULL
16
17 #include <boost/limits.hpp>
18 #include <boost/serialization/state_saver.hpp>
19 #include <boost/serialization/throw_exception.hpp>
20
21 // including this here to work around an ICC in intel 7.0
22 // normally this would be part of basic_oarchive.hpp below.
23 #define BOOST_ARCHIVE_SOURCE
24 // include this to prevent linker errors when the
25 // same modules are marked export and import.
26 #define BOOST_SERIALIZATION_SOURCE
27
28 #include <boost/archive/detail/decl.hpp>
29 #include <boost/archive/basic_archive.hpp>
30 #include <boost/archive/detail/basic_oserializer.hpp>
31 #include <boost/archive/detail/basic_pointer_oserializer.hpp>
32 #include <boost/archive/detail/basic_oarchive.hpp>
33 #include <boost/archive/archive_exception.hpp>
34 #include <boost/serialization/extended_type_info.hpp>
35
36 #ifdef BOOST_MSVC
37 # pragma warning(push)
38 # pragma warning(disable : 4251 4231 4660 4275)
39 #endif
40
41 using namespace boost::serialization;
42
43 namespace boost {
44 namespace archive {
45 namespace detail {
46
47 class basic_oarchive_impl {
48 friend class basic_oarchive;
49 unsigned int m_flags;
50
51 //////////////////////////////////////////////////////////////////////
52 // information about each serialized object saved
53 // keyed on address, class_id
54 struct aobject
55 {
56 const void * address;
57 class_id_type class_id;
58 object_id_type object_id;
59
operator <boost::archive::detail::basic_oarchive_impl::aobject60 bool operator<(const aobject &rhs) const
61 {
62 BOOST_ASSERT(NULL != address);
63 BOOST_ASSERT(NULL != rhs.address);
64 if( address < rhs.address )
65 return true;
66 if( address > rhs.address )
67 return false;
68 return class_id < rhs.class_id;
69 }
operator =boost::archive::detail::basic_oarchive_impl::aobject70 aobject & operator=(const aobject & rhs)
71 {
72 address = rhs.address;
73 class_id = rhs.class_id;
74 object_id = rhs.object_id;
75 return *this;
76 }
aobjectboost::archive::detail::basic_oarchive_impl::aobject77 aobject(
78 const void *a,
79 class_id_type class_id_,
80 object_id_type object_id_
81 ) :
82 address(a),
83 class_id(class_id_),
84 object_id(object_id_)
85 {}
aobjectboost::archive::detail::basic_oarchive_impl::aobject86 aobject() : address(NULL){}
87 };
88 // keyed on class_id, address
89 typedef std::set<aobject> object_set_type;
90 object_set_type object_set;
91
92 //////////////////////////////////////////////////////////////////////
93 // information about each serialized class saved
94 // keyed on type_info
95 struct cobject_type
96 {
97 const basic_oserializer * m_bos_ptr;
98 const class_id_type m_class_id;
99 bool m_initialized;
cobject_typeboost::archive::detail::basic_oarchive_impl::cobject_type100 cobject_type(
101 std::size_t class_id,
102 const basic_oserializer & bos
103 ) :
104 m_bos_ptr(& bos),
105 m_class_id(class_id),
106 m_initialized(false)
107 {}
cobject_typeboost::archive::detail::basic_oarchive_impl::cobject_type108 cobject_type(const basic_oserializer & bos)
109 : m_bos_ptr(& bos)
110 {}
cobject_typeboost::archive::detail::basic_oarchive_impl::cobject_type111 cobject_type(
112 const cobject_type & rhs
113 ) :
114 m_bos_ptr(rhs.m_bos_ptr),
115 m_class_id(rhs.m_class_id),
116 m_initialized(rhs.m_initialized)
117 {}
118 // the following cannot be defined because of the const
119 // member. This will generate a link error if an attempt
120 // is made to assign. This should never be necessary
121 // use this only for lookup argument
122 cobject_type & operator=(const cobject_type &rhs);
operator <boost::archive::detail::basic_oarchive_impl::cobject_type123 bool operator<(const cobject_type &rhs) const {
124 return *m_bos_ptr < *(rhs.m_bos_ptr);
125 }
126 };
127 // keyed on type_info
128 typedef std::set<cobject_type> cobject_info_set_type;
129 cobject_info_set_type cobject_info_set;
130
131 // list of objects initially stored as pointers - used to detect errors
132 // keyed on object id
133 std::set<object_id_type> stored_pointers;
134
135 // address of the most recent object serialized as a poiner
136 // whose data itself is now pending serialization
137 const void * pending_object;
138 const basic_oserializer * pending_bos;
139
basic_oarchive_impl(unsigned int flags)140 basic_oarchive_impl(unsigned int flags) :
141 m_flags(flags),
142 pending_object(NULL),
143 pending_bos(NULL)
144 {}
145
146 const cobject_type &
147 find(const basic_oserializer & bos);
148 const basic_oserializer *
149 find(const serialization::extended_type_info &ti) const;
150
151 //public:
152 const cobject_type &
153 register_type(const basic_oserializer & bos);
154 void save_object(
155 basic_oarchive & ar,
156 const void *t,
157 const basic_oserializer & bos
158 );
159 void save_pointer(
160 basic_oarchive & ar,
161 const void * t,
162 const basic_pointer_oserializer * bpos
163 );
164 };
165
166 //////////////////////////////////////////////////////////////////////
167 // basic_oarchive implementation functions
168
169 // given a type_info - find its bos
170 // return NULL if not found
171 inline const basic_oserializer *
find(const serialization::extended_type_info & ti) const172 basic_oarchive_impl::find(const serialization::extended_type_info & ti) const {
173 #ifdef BOOST_MSVC
174 # pragma warning(push)
175 # pragma warning(disable : 4511 4512)
176 #endif
177 class bosarg :
178 public basic_oserializer
179 {
180 bool class_info() const {
181 BOOST_ASSERT(false);
182 return false;
183 }
184 // returns true if objects should be tracked
185 bool tracking(const unsigned int) const {
186 BOOST_ASSERT(false);
187 return false;
188 }
189 // returns class version
190 version_type version() const {
191 BOOST_ASSERT(false);
192 return version_type(0);
193 }
194 // returns true if this class is polymorphic
195 bool is_polymorphic() const{
196 BOOST_ASSERT(false);
197 return false;
198 }
199 void save_object_data(
200 basic_oarchive & /*ar*/, const void * /*x*/
201 ) const {
202 BOOST_ASSERT(false);
203 }
204 public:
205 bosarg(const serialization::extended_type_info & eti) :
206 boost::archive::detail::basic_oserializer(eti)
207 {}
208 };
209 #ifdef BOOST_MSVC
210 #pragma warning(pop)
211 #endif
212 bosarg bos(ti);
213 cobject_info_set_type::const_iterator cit
214 = cobject_info_set.find(cobject_type(bos));
215 // it should already have been "registered" - see below
216 if(cit == cobject_info_set.end()){
217 // if an entry is not found in the table it is because a pointer
218 // of a derived class has been serialized through its base class
219 // but the derived class hasn't been "registered"
220 return NULL;
221 }
222 // return pointer to the real class
223 return cit->m_bos_ptr;
224 }
225
226 inline const basic_oarchive_impl::cobject_type &
find(const basic_oserializer & bos)227 basic_oarchive_impl::find(const basic_oserializer & bos)
228 {
229 std::pair<cobject_info_set_type::iterator, bool> cresult =
230 cobject_info_set.insert(cobject_type(cobject_info_set.size(), bos));
231 return *(cresult.first);
232 }
233
234 inline const basic_oarchive_impl::cobject_type &
register_type(const basic_oserializer & bos)235 basic_oarchive_impl::register_type(
236 const basic_oserializer & bos
237 ){
238 cobject_type co(cobject_info_set.size(), bos);
239 std::pair<cobject_info_set_type::const_iterator, bool>
240 result = cobject_info_set.insert(co);
241 return *(result.first);
242 }
243
244 inline void
save_object(basic_oarchive & ar,const void * t,const basic_oserializer & bos)245 basic_oarchive_impl::save_object(
246 basic_oarchive & ar,
247 const void *t,
248 const basic_oserializer & bos
249 ){
250 // if its been serialized through a pointer and the preamble's been done
251 if(t == pending_object && pending_bos == & bos){
252 // just save the object data
253 ar.end_preamble();
254 (bos.save_object_data)(ar, t);
255 return;
256 }
257
258 // get class information for this object
259 const cobject_type & co = register_type(bos);
260 if(bos.class_info()){
261 if( ! co.m_initialized){
262 ar.vsave(class_id_optional_type(co.m_class_id));
263 ar.vsave(tracking_type(bos.tracking(m_flags)));
264 ar.vsave(version_type(bos.version()));
265 (const_cast<cobject_type &>(co)).m_initialized = true;
266 }
267 }
268
269 // we're not tracking this type of object
270 if(! bos.tracking(m_flags)){
271 // just windup the preamble - no object id to write
272 ar.end_preamble();
273 // and save the data
274 (bos.save_object_data)(ar, t);
275 return;
276 }
277
278 // look for an existing object id
279 object_id_type oid(object_set.size());
280 // lookup to see if this object has already been written to the archive
281 basic_oarchive_impl::aobject ao(t, co.m_class_id, oid);
282 std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool>
283 aresult = object_set.insert(ao);
284 oid = aresult.first->object_id;
285
286 // if its a new object
287 if(aresult.second){
288 // write out the object id
289 ar.vsave(oid);
290 ar.end_preamble();
291 // and data
292 (bos.save_object_data)(ar, t);
293 return;
294 }
295
296 // check that it wasn't originally stored through a pointer
297 if(stored_pointers.end() != stored_pointers.find(oid)){
298 // this has to be a user error. loading such an archive
299 // would create duplicate objects
300 boost::serialization::throw_exception(
301 archive_exception(archive_exception::pointer_conflict)
302 );
303 }
304 // just save the object id
305 ar.vsave(object_reference_type(oid));
306 ar.end_preamble();
307 return;
308 }
309
310 // save a pointer to an object instance
311 inline void
save_pointer(basic_oarchive & ar,const void * t,const basic_pointer_oserializer * bpos_ptr)312 basic_oarchive_impl::save_pointer(
313 basic_oarchive & ar,
314 const void * t,
315 const basic_pointer_oserializer * bpos_ptr
316 ){
317 const basic_oserializer & bos = bpos_ptr->get_basic_serializer();
318 std::size_t original_count = cobject_info_set.size();
319 const cobject_type & co = register_type(bos);
320 if(! co.m_initialized){
321 ar.vsave(co.m_class_id);
322 // if its a previously unregistered class
323 if((cobject_info_set.size() > original_count)){
324 if(bos.is_polymorphic()){
325 const serialization::extended_type_info *eti = & bos.get_eti();
326 const char * key = NULL;
327 if(NULL != eti)
328 key = eti->get_key();
329 if(NULL != key){
330 // the following is required by IBM C++ compiler which
331 // makes a copy when passing a non-const to a const. This
332 // is permitted by the standard but rarely seen in practice
333 const class_name_type cn(key);
334 if(cn.size() > (BOOST_SERIALIZATION_MAX_KEY_SIZE - 1))
335 boost::serialization::throw_exception(
336 boost::archive::archive_exception(
337 boost::archive::archive_exception::
338 invalid_class_name)
339 );
340 // write out the external class identifier
341 ar.vsave(cn);
342 }
343 else
344 // without an external class name
345 // we won't be able to de-serialize it so bail now
346 boost::serialization::throw_exception(
347 archive_exception(archive_exception::unregistered_class)
348 );
349 }
350 }
351 if(bos.class_info()){
352 ar.vsave(tracking_type(bos.tracking(m_flags)));
353 ar.vsave(version_type(bos.version()));
354 }
355 (const_cast<cobject_type &>(co)).m_initialized = true;
356 }
357 else{
358 ar.vsave(class_id_reference_type(co.m_class_id));
359 }
360
361 // if we're not tracking
362 if(! bos.tracking(m_flags)){
363 // just save the data itself
364 ar.end_preamble();
365 serialization::state_saver<const void *> x(pending_object);
366 serialization::state_saver<const basic_oserializer *> y(pending_bos);
367 pending_object = t;
368 pending_bos = & bpos_ptr->get_basic_serializer();
369 bpos_ptr->save_object_ptr(ar, t);
370 return;
371 }
372
373 object_id_type oid(object_set.size());
374 // lookup to see if this object has already been written to the archive
375 basic_oarchive_impl::aobject ao(t, co.m_class_id, oid);
376 std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool>
377 aresult = object_set.insert(ao);
378 oid = aresult.first->object_id;
379 // if the saved object already exists
380 if(! aresult.second){
381 // append the object id to he preamble
382 ar.vsave(object_reference_type(oid));
383 // and windup.
384 ar.end_preamble();
385 return;
386 }
387
388 // append id of this object to preamble
389 ar.vsave(oid);
390 ar.end_preamble();
391
392 // and save the object itself
393 serialization::state_saver<const void *> x(pending_object);
394 serialization::state_saver<const basic_oserializer *> y(pending_bos);
395 pending_object = t;
396 pending_bos = & bpos_ptr->get_basic_serializer();
397 bpos_ptr->save_object_ptr(ar, t);
398 // add to the set of object initially stored through pointers
399 stored_pointers.insert(oid);
400 }
401
402 } // namespace detail
403 } // namespace archive
404 } // namespace boost
405
406 //////////////////////////////////////////////////////////////////////
407 // implementation of basic_oarchive functions
408
409 namespace boost {
410 namespace archive {
411 namespace detail {
412
413 BOOST_ARCHIVE_DECL
basic_oarchive(unsigned int flags)414 basic_oarchive::basic_oarchive(unsigned int flags)
415 : pimpl(new basic_oarchive_impl(flags))
416 {}
417
418 BOOST_ARCHIVE_DECL
~basic_oarchive()419 basic_oarchive::~basic_oarchive()
420 {}
421
422 BOOST_ARCHIVE_DECL void
save_object(const void * x,const basic_oserializer & bos)423 basic_oarchive::save_object(
424 const void *x,
425 const basic_oserializer & bos
426 ){
427 pimpl->save_object(*this, x, bos);
428 }
429
430 BOOST_ARCHIVE_DECL void
save_pointer(const void * t,const basic_pointer_oserializer * bpos_ptr)431 basic_oarchive::save_pointer(
432 const void * t,
433 const basic_pointer_oserializer * bpos_ptr
434 ){
435 pimpl->save_pointer(*this, t, bpos_ptr);
436 }
437
438 BOOST_ARCHIVE_DECL void
register_basic_serializer(const basic_oserializer & bos)439 basic_oarchive::register_basic_serializer(const basic_oserializer & bos){
440 pimpl->register_type(bos);
441 }
442
443 BOOST_ARCHIVE_DECL library_version_type
get_library_version() const444 basic_oarchive::get_library_version() const{
445 return BOOST_ARCHIVE_VERSION();
446 }
447
448 BOOST_ARCHIVE_DECL unsigned int
get_flags() const449 basic_oarchive::get_flags() const{
450 return pimpl->m_flags;
451 }
452
453 BOOST_ARCHIVE_DECL void
end_preamble()454 basic_oarchive::end_preamble(){
455 }
456
457 } // namespace detail
458 } // namespace archive
459 } // namespace boost
460
461 #ifdef BOOST_MSVC
462 #pragma warning(pop)
463 #endif
464