]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 & 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 | } | |
78 | 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 | {} | |
87 | 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; | |
101 | 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 | {} | |
b32b8144 FG |
109 | cobject_type(const basic_oserializer & bos) : |
110 | m_bos_ptr(& bos), | |
111 | m_initialized(false) | |
7c673cae FG |
112 | {} |
113 | 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); | |
125 | 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 poiner | |
138 | // whose data itself is now pending serialization | |
139 | const void * pending_object; | |
140 | const basic_oserializer * pending_bos; | |
141 | ||
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 * | |
174 | 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 { | |
183 | BOOST_ASSERT(false); | |
184 | return false; | |
185 | } | |
186 | // returns true if objects should be tracked | |
187 | bool tracking(const unsigned int) const { | |
188 | BOOST_ASSERT(false); | |
189 | return false; | |
190 | } | |
191 | // returns class version | |
192 | version_type version() const { | |
193 | BOOST_ASSERT(false); | |
194 | return version_type(0); | |
195 | } | |
196 | // returns true if this class is polymorphic | |
197 | bool is_polymorphic() const{ | |
198 | BOOST_ASSERT(false); | |
199 | return false; | |
200 | } | |
201 | void save_object_data( | |
202 | basic_oarchive & /*ar*/, const void * /*x*/ | |
203 | ) const { | |
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 & | |
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 & | |
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 | |
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 | return; | |
310 | } | |
311 | ||
312 | // save a pointer to an object instance | |
313 | inline void | |
314 | basic_oarchive_impl::save_pointer( | |
315 | basic_oarchive & ar, | |
316 | const void * t, | |
317 | const basic_pointer_oserializer * bpos_ptr | |
318 | ){ | |
319 | const basic_oserializer & bos = bpos_ptr->get_basic_serializer(); | |
320 | std::size_t original_count = cobject_info_set.size(); | |
321 | const cobject_type & co = register_type(bos); | |
322 | if(! co.m_initialized){ | |
323 | ar.vsave(co.m_class_id); | |
324 | // if its a previously unregistered class | |
325 | if((cobject_info_set.size() > original_count)){ | |
326 | if(bos.is_polymorphic()){ | |
327 | const serialization::extended_type_info *eti = & bos.get_eti(); | |
328 | const char * key = NULL; | |
329 | if(NULL != eti) | |
330 | key = eti->get_key(); | |
331 | if(NULL != key){ | |
332 | // the following is required by IBM C++ compiler which | |
333 | // makes a copy when passing a non-const to a const. This | |
334 | // is permitted by the standard but rarely seen in practice | |
335 | const class_name_type cn(key); | |
336 | if(cn.size() > (BOOST_SERIALIZATION_MAX_KEY_SIZE - 1)) | |
337 | boost::serialization::throw_exception( | |
338 | boost::archive::archive_exception( | |
339 | boost::archive::archive_exception:: | |
340 | invalid_class_name) | |
341 | ); | |
342 | // write out the external class identifier | |
343 | ar.vsave(cn); | |
344 | } | |
345 | else | |
346 | // without an external class name | |
347 | // we won't be able to de-serialize it so bail now | |
348 | boost::serialization::throw_exception( | |
349 | archive_exception(archive_exception::unregistered_class) | |
350 | ); | |
351 | } | |
352 | } | |
353 | if(bos.class_info()){ | |
354 | ar.vsave(tracking_type(bos.tracking(m_flags))); | |
355 | ar.vsave(version_type(bos.version())); | |
356 | } | |
357 | (const_cast<cobject_type &>(co)).m_initialized = true; | |
358 | } | |
359 | else{ | |
360 | ar.vsave(class_id_reference_type(co.m_class_id)); | |
361 | } | |
362 | ||
363 | // if we're not tracking | |
364 | if(! bos.tracking(m_flags)){ | |
365 | // just save the data itself | |
366 | ar.end_preamble(); | |
367 | serialization::state_saver<const void *> x(pending_object); | |
368 | serialization::state_saver<const basic_oserializer *> y(pending_bos); | |
369 | pending_object = t; | |
370 | pending_bos = & bpos_ptr->get_basic_serializer(); | |
371 | bpos_ptr->save_object_ptr(ar, t); | |
372 | return; | |
373 | } | |
374 | ||
375 | object_id_type oid(object_set.size()); | |
376 | // lookup to see if this object has already been written to the archive | |
377 | basic_oarchive_impl::aobject ao(t, co.m_class_id, oid); | |
378 | std::pair<basic_oarchive_impl::object_set_type::const_iterator, bool> | |
379 | aresult = object_set.insert(ao); | |
380 | oid = aresult.first->object_id; | |
381 | // if the saved object already exists | |
382 | if(! aresult.second){ | |
383 | // append the object id to he preamble | |
384 | ar.vsave(object_reference_type(oid)); | |
385 | // and windup. | |
386 | ar.end_preamble(); | |
387 | return; | |
388 | } | |
389 | ||
390 | // append id of this object to preamble | |
391 | ar.vsave(oid); | |
392 | ar.end_preamble(); | |
393 | ||
394 | // and save the object itself | |
395 | serialization::state_saver<const void *> x(pending_object); | |
396 | serialization::state_saver<const basic_oserializer *> y(pending_bos); | |
397 | pending_object = t; | |
398 | pending_bos = & bpos_ptr->get_basic_serializer(); | |
399 | bpos_ptr->save_object_ptr(ar, t); | |
400 | // add to the set of object initially stored through pointers | |
401 | stored_pointers.insert(oid); | |
402 | } | |
403 | ||
404 | } // namespace detail | |
405 | } // namespace archive | |
406 | } // namespace boost | |
407 | ||
408 | ////////////////////////////////////////////////////////////////////// | |
409 | // implementation of basic_oarchive functions | |
410 | ||
411 | namespace boost { | |
412 | namespace archive { | |
413 | namespace detail { | |
414 | ||
415 | BOOST_ARCHIVE_DECL | |
416 | basic_oarchive::basic_oarchive(unsigned int flags) | |
417 | : pimpl(new basic_oarchive_impl(flags)) | |
418 | {} | |
419 | ||
420 | BOOST_ARCHIVE_DECL | |
421 | basic_oarchive::~basic_oarchive() | |
422 | {} | |
423 | ||
424 | BOOST_ARCHIVE_DECL void | |
425 | basic_oarchive::save_object( | |
426 | const void *x, | |
427 | const basic_oserializer & bos | |
428 | ){ | |
429 | pimpl->save_object(*this, x, bos); | |
430 | } | |
431 | ||
432 | BOOST_ARCHIVE_DECL void | |
433 | basic_oarchive::save_pointer( | |
434 | const void * t, | |
435 | const basic_pointer_oserializer * bpos_ptr | |
436 | ){ | |
437 | pimpl->save_pointer(*this, t, bpos_ptr); | |
438 | } | |
439 | ||
440 | BOOST_ARCHIVE_DECL void | |
441 | basic_oarchive::register_basic_serializer(const basic_oserializer & bos){ | |
442 | pimpl->register_type(bos); | |
443 | } | |
444 | ||
445 | BOOST_ARCHIVE_DECL library_version_type | |
446 | basic_oarchive::get_library_version() const{ | |
447 | return BOOST_ARCHIVE_VERSION(); | |
448 | } | |
449 | ||
450 | BOOST_ARCHIVE_DECL unsigned int | |
451 | basic_oarchive::get_flags() const{ | |
452 | return pimpl->m_flags; | |
453 | } | |
454 | ||
455 | BOOST_ARCHIVE_DECL void | |
456 | basic_oarchive::end_preamble(){ | |
457 | } | |
458 | ||
459 | BOOST_ARCHIVE_DECL helper_collection & | |
460 | basic_oarchive::get_helper_collection(){ | |
461 | return *this; | |
462 | } | |
463 | ||
464 | } // namespace detail | |
465 | } // namespace archive | |
466 | } // namespace boost | |
467 | ||
468 | #ifdef BOOST_MSVC | |
469 | #pragma warning(pop) | |
470 | #endif |