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