22// basic_archive.cpp:
33
44// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
5+ // Copyright 2026 Gennaro Prota.
56// Distributed under the Boost Software License, Version 1.0.
67// (See accompanying file LICENSE_1_0.txt or copy at
78// http://www.boost.org/LICENSE_1_0.txt)
@@ -31,6 +32,8 @@ namespace std{
3132#define BOOST_SERIALIZATION_SOURCE
3233#include < boost/serialization/config.hpp>
3334
35+ #include < boost/core/no_exceptions_support.hpp>
36+
3437#include < boost/serialization/state_saver.hpp>
3538#include < boost/serialization/throw_exception.hpp>
3639#include < boost/serialization/tracking.hpp>
@@ -173,16 +176,44 @@ class basic_iarchive_impl {
173176 void * object;
174177 const basic_iserializer * bis;
175178 version_type version;
179+ // The object currently being loaded through a pointer: its index in
180+ // object_id_vector, whether delete_created_pointers may reclaim it
181+ // and whether its constructor has run yet.
182+ std::size_t pointer_object_id;
183+ bool pointer_reclaimable;
184+ bool pointer_constructed;
176185 pending () :
177186 object (NULL ),
178187 bis (NULL ),
179- version (0 )
188+ version (0 ),
189+ pointer_object_id (0 ),
190+ pointer_reclaimable (false ),
191+ pointer_constructed (false )
180192 {}
181193 } m_pending;
182194
195+ // Set while a created pointer is being loaded. Only the outermost such
196+ // load may be reclaimed by delete_created_pointers: anything created
197+ // below it is reachable from it, so freeing it runs the destructors of
198+ // the objects it owns.
199+ bool m_loading_created_pointer;
200+
201+ // The object which the pointer load that just finished created and
202+ // flagged for reclamation, if any. Lets an owning smart pointer take
203+ // that object over: see object_adopted().
204+ struct last_created {
205+ std::size_t object_id;
206+ bool reclaimable;
207+ last_created () :
208+ object_id (0 ),
209+ reclaimable (false )
210+ {}
211+ } m_last_created;
212+
183213 basic_iarchive_impl (unsigned int flags) :
184214 m_archive_library_version (BOOST_ARCHIVE_VERSION ()),
185- m_flags (flags)
215+ m_flags (flags),
216+ m_loading_created_pointer (false )
186217 {}
187218 void set_library_version (library_version_type archive_library_version){
188219 m_archive_library_version = archive_library_version;
@@ -212,6 +243,22 @@ class basic_iarchive_impl {
212243 next_object_pointer (void * t){
213244 m_pending.object = t;
214245 }
246+ void
247+ object_constructed (){
248+ m_pending.pointer_constructed = true ;
249+ if (m_pending.pointer_reclaimable ){
250+ object_id_vector[m_pending.pointer_object_id ].loaded_as_pointer
251+ = true ;
252+ }
253+ }
254+ void
255+ object_adopted (){
256+ if (m_last_created.reclaimable ){
257+ object_id_vector[m_last_created.object_id ].loaded_as_pointer
258+ = false ;
259+ m_last_created.reclaimable = false ;
260+ }
261+ }
215262 void delete_created_pointers ();
216263 class_id_type register_type (
217264 const basic_pointer_iserializer & bpis
@@ -424,6 +471,10 @@ basic_iarchive_impl::load_pointer(
424471 m_moveable_objects.is_pointer = true ;
425472 serialization::state_saver<bool > w (m_moveable_objects.is_pointer );
426473
474+ // An adopting smart pointer may only take over an object which this very
475+ // call creates, so forget any object the previous one left behind.
476+ m_last_created.reclaimable = false ;
477+
427478 class_id_type cid;
428479 load (ar, cid);
429480
@@ -480,39 +531,72 @@ basic_iarchive_impl::load_pointer(
480531 // save state
481532 serialization::state_saver<object_id_type> w_start (m_moveable_objects.start );
482533
534+ // An object created by an enclosing pointer load is owned by that
535+ // object, so only the outermost one is a candidate for reclamation.
536+ const bool root = ! m_loading_created_pointer;
537+ serialization::state_saver<bool > n (m_loading_created_pointer);
538+ serialization::state_saver<std::size_t > p_id (m_pending.pointer_object_id );
539+ serialization::state_saver<bool > p_rec (m_pending.pointer_reclaimable );
540+ serialization::state_saver<bool > p_con (m_pending.pointer_constructed );
541+ m_loading_created_pointer = true ;
542+ m_pending.pointer_reclaimable = false ;
543+ m_pending.pointer_constructed = false ;
544+
483545 // allocate space on the heap for the object - to be constructed later
484546 t = bpis_ptr->heap_allocation ();
485547 BOOST_ASSERT (NULL != t);
486548
487- if (! tracking){
488- bpis_ptr->load_object_ptr (ar, t, co.file_version );
549+ BOOST_TRY {
550+ if (! tracking){
551+ bpis_ptr->load_object_ptr (ar, t, co.file_version );
552+ }
553+ else {
554+ serialization::state_saver<void *> x (m_pending.object );
555+ serialization::state_saver<const basic_iserializer *> y (m_pending.bis );
556+ serialization::state_saver<version_type> z (m_pending.version );
557+
558+ m_pending.bis = & bpis_ptr->get_basic_serializer ();
559+ m_pending.version = co.file_version ;
560+
561+ // predict next object id to be created
562+ const size_t ui = object_id_vector.size ();
563+
564+ serialization::state_saver<object_id_type> w_end (m_moveable_objects.end );
565+
566+ // add to list of serialized objects so that we can properly handle
567+ // cyclic structures
568+ object_id_vector.push_back (aobject (t, cid));
569+ m_pending.pointer_object_id = ui;
570+ m_pending.pointer_reclaimable = root;
571+
572+ // remember that that the address of these elements could change
573+ // when we make another call so don't use the address. Once the
574+ // object has been constructed load_object_ptr calls back through
575+ // object_constructed(), which flags it for reclamation by
576+ // delete_created_pointers should loading its members throw.
577+ bpis_ptr->load_object_ptr (
578+ ar,
579+ t,
580+ m_pending.version
581+ );
582+ }
489583 }
490- else {
491- serialization::state_saver<void *> x (m_pending.object );
492- serialization::state_saver<const basic_iserializer *> y (m_pending.bis );
493- serialization::state_saver<version_type> z (m_pending.version );
494-
495- m_pending.bis = & bpis_ptr->get_basic_serializer ();
496- m_pending.version = co.file_version ;
497-
498- // predict next object id to be created
499- const size_t ui = object_id_vector.size ();
500-
501- serialization::state_saver<object_id_type> w_end (m_moveable_objects.end );
502-
503- // add to list of serialized objects so that we can properly handle
504- // cyclic structures
505- object_id_vector.push_back (aobject (t, cid));
506-
507- // remember that that the address of these elements could change
508- // when we make another call so don't use the address
509- bpis_ptr->load_object_ptr (
510- ar,
511- t,
512- m_pending.version
513- );
514- object_id_vector[ui].loaded_as_pointer = true ;
584+ BOOST_CATCH (...){
585+ // The constructor never ran, so load_object_ptr has freed the raw
586+ // storage. Clear the caller's pointer: otherwise the destructor of
587+ // an enclosing object would delete storage which is already gone.
588+ if (! m_pending.pointer_constructed ){
589+ t = NULL ;
590+ }
591+ BOOST_RETHROW ;
515592 }
593+ BOOST_CATCH_END
594+
595+ // The load succeeded: remember what it flagged, so that a smart pointer
596+ // adopting the object can take responsibility for freeing it.
597+ m_last_created.object_id = m_pending.pointer_object_id ;
598+ m_last_created.reclaimable = m_pending.pointer_reclaimable
599+ && m_pending.pointer_constructed ;
516600
517601 return bpis_ptr;
518602}
@@ -532,6 +616,16 @@ basic_iarchive::next_object_pointer(void *t){
532616 pimpl->next_object_pointer (t);
533617}
534618
619+ BOOST_ARCHIVE_DECL void
620+ basic_iarchive::object_constructed (){
621+ pimpl->object_constructed ();
622+ }
623+
624+ BOOST_ARCHIVE_DECL void
625+ basic_iarchive::object_adopted (){
626+ pimpl->object_adopted ();
627+ }
628+
535629BOOST_ARCHIVE_DECL
536630basic_iarchive::basic_iarchive (unsigned int flags) :
537631 pimpl (new basic_iarchive_impl(flags))
0 commit comments