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