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