• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
2 // basic_archive.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 to suppress warnings
12 
13 #include <boost/assert.hpp>
14 #include <set>
15 #include <list>
16 #include <vector>
17 #include <cstddef> // size_t, NULL
18 
19 #include <boost/config.hpp>
20 #if defined(BOOST_NO_STDC_NAMESPACE)
21 namespace std{
22     using ::size_t;
23 } // namespace std
24 #endif
25 
26 #include <boost/integer_traits.hpp>
27 
28 #define BOOST_ARCHIVE_SOURCE
29 // include this to prevent linker errors when the
30 // same modules are marked export and import.
31 #define BOOST_SERIALIZATION_SOURCE
32 #include <boost/serialization/config.hpp>
33 
34 #include <boost/serialization/state_saver.hpp>
35 #include <boost/serialization/throw_exception.hpp>
36 #include <boost/serialization/tracking.hpp>
37 
38 #include <boost/archive/archive_exception.hpp>
39 #include <boost/archive/detail/decl.hpp>
40 #include <boost/archive/basic_archive.hpp>
41 #include <boost/archive/detail/basic_iserializer.hpp>
42 #include <boost/archive/detail/basic_pointer_iserializer.hpp>
43 #include <boost/archive/detail/basic_iarchive.hpp>
44 
45 #include <boost/archive/detail/auto_link_archive.hpp>
46 
47 using namespace boost::serialization;
48 
49 namespace boost {
50 namespace archive {
51 namespace detail {
52 
53 class basic_iarchive_impl {
54     friend class basic_iarchive;
55     library_version_type m_archive_library_version;
56     unsigned int m_flags;
57 
58     //////////////////////////////////////////////////////////////////////
59     // information about each serialized object loaded
60     // indexed on object_id
61     struct aobject
62     {
63         void * address;
64         bool loaded_as_pointer;
65         class_id_type class_id;
aobjectboost::archive::detail::basic_iarchive_impl::aobject66         aobject(
67             void *a,
68             class_id_type class_id_
69         ) :
70             address(a),
71             loaded_as_pointer(false),
72             class_id(class_id_)
73         {}
aobjectboost::archive::detail::basic_iarchive_impl::aobject74         aobject() :
75             address(NULL),
76             loaded_as_pointer(false),
77             class_id(-2)
78         {}
79     };
80     typedef std::vector<aobject> object_id_vector_type;
81     object_id_vector_type object_id_vector;
82 
83     //////////////////////////////////////////////////////////////////////
84     // used to implement the reset_object_address operation.
85     struct moveable_objects {
86         object_id_type start;
87         object_id_type end;
88         object_id_type recent;
89         bool is_pointer;
moveable_objectsboost::archive::detail::basic_iarchive_impl::moveable_objects90         moveable_objects() :
91             start(0),
92             end(0),
93             recent(0),
94             is_pointer(false)
95         {}
96     } m_moveable_objects;
97 
98     void reset_object_address(
99         const void * new_address,
100         const void *old_address
101     );
102 
103     //////////////////////////////////////////////////////////////////////
104     // used by load object to look up class id given basic_serializer
105     struct cobject_type
106     {
107         const basic_iserializer * m_bis;
108         const class_id_type m_class_id;
cobject_typeboost::archive::detail::basic_iarchive_impl::cobject_type109         cobject_type(
110             std::size_t class_id,
111             const basic_iserializer & bis
112         ) :
113             m_bis(& bis),
114             m_class_id(class_id)
115         {}
cobject_typeboost::archive::detail::basic_iarchive_impl::cobject_type116         cobject_type(const cobject_type & rhs) :
117             m_bis(rhs.m_bis),
118             m_class_id(rhs.m_class_id)
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         cobject_type & operator=(const cobject_type & rhs);
operator <boost::archive::detail::basic_iarchive_impl::cobject_type124         bool operator<(const cobject_type &rhs) const
125         {
126             return *m_bis < *(rhs.m_bis);
127         }
128     };
129     typedef std::set<cobject_type> cobject_info_set_type;
130     cobject_info_set_type cobject_info_set;
131 
132     //////////////////////////////////////////////////////////////////////
133     // information about each serialized class indexed on class_id
134     class cobject_id
135     {
136     public:
operator =(const cobject_id & rhs)137         cobject_id & operator=(const cobject_id & rhs){
138             bis_ptr = rhs.bis_ptr;
139             bpis_ptr = rhs.bpis_ptr;
140             file_version = rhs.file_version;
141             tracking_level = rhs.tracking_level;
142             initialized = rhs.initialized;
143             return *this;
144         }
145         const basic_iserializer * bis_ptr;
146         const basic_pointer_iserializer * bpis_ptr;
147         version_type file_version;
148         tracking_type tracking_level;
149         bool initialized;
150 
cobject_id(const basic_iserializer & bis_)151         cobject_id(const basic_iserializer & bis_) :
152             bis_ptr(& bis_),
153             bpis_ptr(NULL),
154             file_version(0),
155             tracking_level(track_never),
156             initialized(false)
157         {}
cobject_id(const cobject_id & rhs)158         cobject_id(const cobject_id &rhs):
159             bis_ptr(rhs.bis_ptr),
160             bpis_ptr(rhs.bpis_ptr),
161             file_version(rhs.file_version),
162             tracking_level(rhs.tracking_level),
163             initialized(rhs.initialized)
164         {}
165     };
166     typedef std::vector<cobject_id> cobject_id_vector_type;
167     cobject_id_vector_type cobject_id_vector;
168 
169     //////////////////////////////////////////////////////////////////////
170     // address of the most recent object serialized as a poiner
171     // whose data itself is now pending serialization
172     struct pending {
173         void * object;
174         const basic_iserializer * bis;
175         version_type version;
pendingboost::archive::detail::basic_iarchive_impl::pending176         pending() :
177             object(NULL),
178             bis(NULL),
179             version(0)
180         {}
181     } m_pending;
182 
basic_iarchive_impl(unsigned int flags)183     basic_iarchive_impl(unsigned int flags) :
184         m_archive_library_version(BOOST_ARCHIVE_VERSION()),
185         m_flags(flags)
186     {}
set_library_version(library_version_type archive_library_version)187     void set_library_version(library_version_type archive_library_version){
188         m_archive_library_version = archive_library_version;
189     }
190     bool
191     track(
192         basic_iarchive & ar,
193         void * & t
194     );
195     void
196     load_preamble(
197         basic_iarchive & ar,
198         cobject_id & co
199     );
200     class_id_type register_type(
201         const basic_iserializer & bis
202     );
203 
204     // redirect through virtual functions to load functions for this archive
205     template<class T>
load(basic_iarchive & ar,T & t)206     void load(basic_iarchive & ar, T & t){
207         ar.vload(t);
208     }
209 
210 //public:
211     void
next_object_pointer(void * t)212     next_object_pointer(void * t){
213         m_pending.object = t;
214     }
215     void delete_created_pointers();
216     class_id_type register_type(
217         const basic_pointer_iserializer & bpis
218     );
219     void load_object(
220         basic_iarchive & ar,
221         void * t,
222         const basic_iserializer & bis
223     );
224     const basic_pointer_iserializer * load_pointer(
225         basic_iarchive & ar,
226         void * & t,
227         const basic_pointer_iserializer * bpis,
228         const basic_pointer_iserializer * (*finder)(
229             const boost::serialization::extended_type_info & type
230         )
231     );
232 };
233 
234 inline void
reset_object_address(void const * const new_address,void const * const old_address)235 basic_iarchive_impl::reset_object_address(
236     void const * const new_address,
237     void const * const old_address
238 ){
239     if(m_moveable_objects.is_pointer)
240         return;
241 
242     // this code handles a couple of situations.
243     // a) where reset_object_address is applied to an untracked object.
244     //    In such a case the call is really superfluous and its really an
245     //    an error.  But we don't have access to the types here so we can't
246     //    know that.  However, this code will effectively turn this situation
247     //    into a no-op and every thing will work fine - albeat with a small
248     //    execution time penalty.
249     // b) where the call to reset_object_address doesn't immediatly follow
250     //    the << operator to which it corresponds.  This would be a bad idea
251     //    but the code may work anyway.  Naturally, a bad practice on the part
252     //    of the programmer but we can't detect it - as above.  So maybe we
253     //    can save a few more people from themselves as above.
254     object_id_type i = m_moveable_objects.recent;
255     for(; i < m_moveable_objects.end; ++i){
256         if(old_address == object_id_vector[i].address)
257             break;
258     }
259     for(; i < m_moveable_objects.end; ++i){
260         const aobject & ao = object_id_vector[i];
261         if(ao.loaded_as_pointer)
262             continue;
263         void const * const this_address = ao.address;
264         // calculate displacement from this level
265         // warning - pointer arithmetic on void * is in herently non-portable
266         // but expected to work on all platforms in current usage
267         if(this_address > old_address){
268             std::size_t member_displacement
269                 = reinterpret_cast<std::size_t>(this_address)
270                 - reinterpret_cast<std::size_t>(old_address);
271             object_id_vector[i].address = reinterpret_cast<void *>(
272                 reinterpret_cast<std::size_t>(new_address) + member_displacement
273             );
274         }
275         else{
276             std::size_t member_displacement
277                 = reinterpret_cast<std::size_t>(old_address)
278                 - reinterpret_cast<std::size_t>(this_address);
279             object_id_vector[i].address = reinterpret_cast<void *>(
280                 reinterpret_cast<std::size_t>(new_address) - member_displacement
281             );
282        }
283     }
284 }
285 
286 inline void
delete_created_pointers()287 basic_iarchive_impl::delete_created_pointers()
288 {
289     object_id_vector_type::iterator i;
290     for(
291         i = object_id_vector.begin();
292         i != object_id_vector.end();
293         ++i
294     ){
295         if(i->loaded_as_pointer){
296             // borland complains without this minor hack
297             const int j = i->class_id;
298             const cobject_id & co = cobject_id_vector[j];
299             //const cobject_id & co = cobject_id_vector[i->class_id];
300             // with the appropriate input serializer,
301             // delete the indicated object
302             co.bis_ptr->destroy(i->address);
303         }
304     }
305 }
306 
307 inline class_id_type
register_type(const basic_iserializer & bis)308 basic_iarchive_impl::register_type(
309     const basic_iserializer & bis
310 ){
311     class_id_type cid(cobject_info_set.size());
312     cobject_type co(cid, bis);
313     std::pair<cobject_info_set_type::const_iterator, bool>
314         result = cobject_info_set.insert(co);
315 
316     if(result.second){
317         cobject_id_vector.push_back(cobject_id(bis));
318         BOOST_ASSERT(cobject_info_set.size() == cobject_id_vector.size());
319     }
320     cid = result.first->m_class_id;
321     // borland complains without this minor hack
322     const int tid = cid;
323     cobject_id & coid = cobject_id_vector[tid];
324     coid.bpis_ptr = bis.get_bpis_ptr();
325     return cid;
326 }
327 
328 void
load_preamble(basic_iarchive & ar,cobject_id & co)329 basic_iarchive_impl::load_preamble(
330     basic_iarchive & ar,
331     cobject_id & co
332 ){
333     if(! co.initialized){
334         if(co.bis_ptr->class_info()){
335             class_id_optional_type cid(class_id_type(0));
336             load(ar, cid);    // to be thrown away
337             load(ar, co.tracking_level);
338             load(ar, co.file_version);
339         }
340         else{
341             // override tracking with indicator from class information
342             co.tracking_level = co.bis_ptr->tracking(m_flags);
343             co.file_version = version_type(
344                 co.bis_ptr->version()
345             );
346         }
347         co.initialized = true;
348     }
349 }
350 
351 bool
track(basic_iarchive & ar,void * & t)352 basic_iarchive_impl::track(
353     basic_iarchive & ar,
354     void * & t
355 ){
356     object_id_type oid;
357     load(ar, oid);
358 
359     // if its a reference to a old object
360     if(object_id_type(object_id_vector.size()) > oid){
361         // we're done
362         t = object_id_vector[oid].address;
363         return false;
364     }
365     return true;
366 }
367 
368 inline void
load_object(basic_iarchive & ar,void * t,const basic_iserializer & bis)369 basic_iarchive_impl::load_object(
370     basic_iarchive & ar,
371     void * t,
372     const basic_iserializer & bis
373 ){
374     m_moveable_objects.is_pointer = false;
375     serialization::state_saver<bool> ss_is_pointer(m_moveable_objects.is_pointer);
376     // if its been serialized through a pointer and the preamble's been done
377     if(t == m_pending.object && & bis == m_pending.bis){
378         // read data
379         (bis.load_object_data)(ar, t, m_pending.version);
380         return;
381     }
382 
383     const class_id_type cid = register_type(bis);
384     const int i = cid;
385     cobject_id & co = cobject_id_vector[i];
386 
387     load_preamble(ar, co);
388 
389     // save the current move stack position in case we want to truncate it
390     boost::serialization::state_saver<object_id_type> ss_start(m_moveable_objects.start);
391 
392     // note: extra line used to evade borland issue
393     const bool tracking = co.tracking_level;
394 
395     object_id_type this_id;
396     m_moveable_objects.start =
397     this_id = object_id_type(object_id_vector.size());
398 
399     // if we tracked this object when the archive was saved
400     if(tracking){
401         // if it was already read
402         if(!track(ar, t))
403             // we're done
404             return;
405         // add a new enty into the tracking list
406         object_id_vector.push_back(aobject(t, cid));
407         // and add an entry for this object
408         m_moveable_objects.end = object_id_type(object_id_vector.size());
409     }
410     // read data
411     (bis.load_object_data)(ar, t, co.file_version);
412     m_moveable_objects.recent = this_id;
413 }
414 
415 inline const basic_pointer_iserializer *
load_pointer(basic_iarchive & ar,void * & t,const basic_pointer_iserializer * bpis_ptr,const basic_pointer_iserializer * (* finder)(const boost::serialization::extended_type_info & type_))416 basic_iarchive_impl::load_pointer(
417     basic_iarchive &ar,
418     void * & t,
419     const basic_pointer_iserializer * bpis_ptr,
420     const basic_pointer_iserializer * (*finder)(
421         const boost::serialization::extended_type_info & type_
422     )
423 ){
424     m_moveable_objects.is_pointer = true;
425     serialization::state_saver<bool> w(m_moveable_objects.is_pointer);
426 
427     class_id_type cid;
428     load(ar, cid);
429 
430     if(BOOST_SERIALIZATION_NULL_POINTER_TAG == cid){
431         t = NULL;
432         return bpis_ptr;
433     }
434 
435     // if its a new class type - i.e. never been registered
436     if(class_id_type(cobject_info_set.size()) <= cid){
437         // if its either abstract
438         if(NULL == bpis_ptr
439         // or polymorphic
440         || bpis_ptr->get_basic_serializer().is_polymorphic()){
441             // is must have been exported
442             char key[BOOST_SERIALIZATION_MAX_KEY_SIZE];
443             class_name_type class_name(key);
444             load(ar, class_name);
445             // if it has a class name
446             const serialization::extended_type_info *eti = NULL;
447             if(0 != key[0])
448                 eti = serialization::extended_type_info::find(key);
449             if(NULL == eti)
450                 boost::serialization::throw_exception(
451                     archive_exception(archive_exception::unregistered_class)
452                 );
453             bpis_ptr = (*finder)(*eti);
454         }
455         BOOST_ASSERT(NULL != bpis_ptr);
456         // class_id_type new_cid = register_type(bpis_ptr->get_basic_serializer());
457         BOOST_VERIFY(register_type(bpis_ptr->get_basic_serializer()) == cid);
458         int i = cid;
459         cobject_id_vector[i].bpis_ptr = bpis_ptr;
460     }
461     int i = cid;
462     cobject_id & co = cobject_id_vector[i];
463     bpis_ptr = co.bpis_ptr;
464 
465     if (bpis_ptr == NULL) {
466         boost::serialization::throw_exception(
467             archive_exception(archive_exception::unregistered_class)
468         );
469     }
470 
471     load_preamble(ar, co);
472 
473     // extra line to evade borland issue
474     const bool tracking = co.tracking_level;
475     // if we're tracking and the pointer has already been read
476     if(tracking && ! track(ar, t))
477         // we're done
478         return bpis_ptr;
479 
480     // save state
481     serialization::state_saver<object_id_type> w_start(m_moveable_objects.start);
482 
483     // allocate space on the heap for the object - to be constructed later
484     t = bpis_ptr->heap_allocation();
485     BOOST_ASSERT(NULL != t);
486 
487     if(! tracking){
488         bpis_ptr->load_object_ptr(ar, t, co.file_version);
489     }
490     else{
491         serialization::state_saver<void *> x(m_pending.object);
492         serialization::state_saver<const basic_iserializer *> y(m_pending.bis);
493         serialization::state_saver<version_type> z(m_pending.version);
494 
495         m_pending.bis = & bpis_ptr->get_basic_serializer();
496         m_pending.version = co.file_version;
497 
498         // predict next object id to be created
499         const size_t ui = object_id_vector.size();
500 
501         serialization::state_saver<object_id_type> w_end(m_moveable_objects.end);
502 
503         // add to list of serialized objects so that we can properly handle
504         // cyclic strucures
505         object_id_vector.push_back(aobject(t, cid));
506 
507         // remember that that the address of these elements could change
508         // when we make another call so don't use the address
509         bpis_ptr->load_object_ptr(
510             ar,
511             t,
512             m_pending.version
513         );
514         object_id_vector[ui].loaded_as_pointer = true;
515     }
516 
517     return bpis_ptr;
518 }
519 
520 } // namespace detail
521 } // namespace archive
522 } // namespace boost
523 
524 //////////////////////////////////////////////////////////////////////
525 // implementation of basic_iarchive functions
526 namespace boost {
527 namespace archive {
528 namespace detail {
529 
530 BOOST_ARCHIVE_DECL void
next_object_pointer(void * t)531 basic_iarchive::next_object_pointer(void *t){
532     pimpl->next_object_pointer(t);
533 }
534 
535 BOOST_ARCHIVE_DECL
basic_iarchive(unsigned int flags)536 basic_iarchive::basic_iarchive(unsigned int flags) :
537     pimpl(new basic_iarchive_impl(flags))
538 {}
539 
540 BOOST_ARCHIVE_DECL
~basic_iarchive()541 basic_iarchive::~basic_iarchive()
542 {}
543 
544 BOOST_ARCHIVE_DECL void
set_library_version(library_version_type archive_library_version)545 basic_iarchive::set_library_version(library_version_type archive_library_version){
546     pimpl->set_library_version(archive_library_version);
547 }
548 
549 BOOST_ARCHIVE_DECL void
reset_object_address(const void * new_address,const void * old_address)550 basic_iarchive::reset_object_address(
551     const void * new_address,
552     const void * old_address
553 ){
554     pimpl->reset_object_address(new_address, old_address);
555 }
556 
557 BOOST_ARCHIVE_DECL void
load_object(void * t,const basic_iserializer & bis)558 basic_iarchive::load_object(
559     void *t,
560     const basic_iserializer & bis
561 ){
562     pimpl->load_object(*this, t, bis);
563 }
564 
565 // load a pointer object
566 BOOST_ARCHIVE_DECL const basic_pointer_iserializer *
load_pointer(void * & t,const basic_pointer_iserializer * bpis_ptr,const basic_pointer_iserializer * (* finder)(const boost::serialization::extended_type_info & type_))567 basic_iarchive::load_pointer(
568     void * &t,
569     const basic_pointer_iserializer * bpis_ptr,
570     const basic_pointer_iserializer * (*finder)(
571         const boost::serialization::extended_type_info & type_
572     )
573 
574 ){
575     return pimpl->load_pointer(*this, t, bpis_ptr, finder);
576 }
577 
578 BOOST_ARCHIVE_DECL void
register_basic_serializer(const basic_iserializer & bis)579 basic_iarchive::register_basic_serializer(const basic_iserializer & bis){
580     pimpl->register_type(bis);
581 }
582 
583 BOOST_ARCHIVE_DECL void
delete_created_pointers()584 basic_iarchive::delete_created_pointers()
585 {
586     pimpl->delete_created_pointers();
587 }
588 
589 BOOST_ARCHIVE_DECL boost::serialization::library_version_type
get_library_version() const590 basic_iarchive::get_library_version() const{
591     return pimpl->m_archive_library_version;
592 }
593 
594 BOOST_ARCHIVE_DECL unsigned int
get_flags() const595 basic_iarchive::get_flags() const{
596     return pimpl->m_flags;
597 }
598 
599 } // namespace detail
600 } // namespace archive
601 } // namespace boost
602