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