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