1 /* Copyright 2003-2020 Joaquin M Lopez Munoz.
2 * Distributed under the Boost Software License, Version 1.0.
3 * (See accompanying file LICENSE_1_0.txt or copy at
4 * http://www.boost.org/LICENSE_1_0.txt)
5 *
6 * See http://www.boost.org/libs/multi_index for library home page.
7 */
8
9 #ifndef BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP
10 #define BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP
11
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15
16 /* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL"
17 * (http://www.horstmann.com/safestl.html).
18 * In this mode, containers of type Container are derived from
19 * safe_container<Container>, and their corresponding iterators
20 * are wrapped with safe_iterator. These classes provide
21 * an internal record of which iterators are at a given moment associated
22 * to a given container, and properly mark the iterators as invalid
23 * when the container gets destroyed.
24 * Iterators are chained in a single attached list, whose header is
25 * kept by the container. More elaborate data structures would yield better
26 * performance, but I decided to keep complexity to a minimum since
27 * speed is not an issue here.
28 * Safe mode iterators automatically check that only proper operations
29 * are performed on them: for instance, an invalid iterator cannot be
30 * dereferenced. Additionally, a set of utilty macros and functions are
31 * provided that serve to implement preconditions and cooperate with
32 * the framework within the container.
33 * Iterators can also be unchecked, i.e. they do not have info about
34 * which container they belong in. This situation arises when the iterator
35 * is restored from a serialization archive: only information on the node
36 * is available, and it is not possible to determine to which container
37 * the iterator is associated to. The only sensible policy is to assume
38 * unchecked iterators are valid, though this can certainly generate false
39 * positive safe mode checks.
40 * This is not a full-fledged safe mode framework, and is only intended
41 * for use within the limits of Boost.MultiIndex.
42 */
43
44 /* Assertion macros. These resolve to no-ops if
45 * !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE).
46 */
47
48 #if !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
49 #undef BOOST_MULTI_INDEX_SAFE_MODE_ASSERT
50 #define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) ((void)0)
51 #else
52 #if !defined(BOOST_MULTI_INDEX_SAFE_MODE_ASSERT)
53 #include <boost/assert.hpp>
54 #define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) BOOST_ASSERT(expr)
55 #endif
56 #endif
57
58 #define BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it) \
59 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
60 safe_mode::check_valid_iterator(it), \
61 safe_mode::invalid_iterator);
62
63 #define BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(it) \
64 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
65 safe_mode::check_dereferenceable_iterator(it), \
66 safe_mode::not_dereferenceable_iterator);
67
68 #define BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(it) \
69 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
70 safe_mode::check_incrementable_iterator(it), \
71 safe_mode::not_incrementable_iterator);
72
73 #define BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(it) \
74 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
75 safe_mode::check_decrementable_iterator(it), \
76 safe_mode::not_decrementable_iterator);
77
78 #define BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,cont) \
79 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
80 safe_mode::check_is_owner(it,cont), \
81 safe_mode::not_owner);
82
83 #define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1) \
84 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
85 safe_mode::check_same_owner(it0,it1), \
86 safe_mode::not_same_owner);
87
88 #define BOOST_MULTI_INDEX_CHECK_VALID_RANGE(it0,it1) \
89 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
90 safe_mode::check_valid_range(it0,it1), \
91 safe_mode::invalid_range);
92
93 #define BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(it,it0,it1) \
94 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
95 safe_mode::check_outside_range(it,it0,it1), \
96 safe_mode::inside_range);
97
98 #define BOOST_MULTI_INDEX_CHECK_IN_BOUNDS(it,n) \
99 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
100 safe_mode::check_in_bounds(it,n), \
101 safe_mode::out_of_bounds);
102
103 #define BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(cont0,cont1) \
104 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
105 safe_mode::check_different_container(cont0,cont1), \
106 safe_mode::same_container);
107
108 #define BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(cont0,cont1) \
109 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
110 safe_mode::check_equal_allocators(cont0,cont1), \
111 safe_mode::unequal_allocators);
112
113 #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
114 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
115 #include <algorithm>
116 #include <boost/multi_index/detail/access_specifier.hpp>
117 #include <boost/multi_index/detail/iter_adaptor.hpp>
118 #include <boost/multi_index/safe_mode_errors.hpp>
119 #include <boost/noncopyable.hpp>
120
121 #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
122 #include <boost/serialization/split_member.hpp>
123 #include <boost/serialization/version.hpp>
124 #endif
125
126 #if defined(BOOST_HAS_THREADS)
127 #include <boost/detail/lightweight_mutex.hpp>
128 #endif
129
130 namespace boost{
131
132 namespace multi_index{
133
134 namespace safe_mode{
135
136 /* Checking routines. Assume the best for unchecked iterators
137 * (i.e. they pass the checking when there is not enough info
138 * to know.)
139 */
140
141 template<typename Iterator>
check_valid_iterator(const Iterator & it)142 inline bool check_valid_iterator(const Iterator& it)
143 {
144 return it.valid()||it.unchecked();
145 }
146
147 template<typename Iterator>
check_dereferenceable_iterator(const Iterator & it)148 inline bool check_dereferenceable_iterator(const Iterator& it)
149 {
150 return (it.valid()&&it!=it.owner()->end())||it.unchecked();
151 }
152
153 template<typename Iterator>
check_incrementable_iterator(const Iterator & it)154 inline bool check_incrementable_iterator(const Iterator& it)
155 {
156 return (it.valid()&&it!=it.owner()->end())||it.unchecked();
157 }
158
159 template<typename Iterator>
check_decrementable_iterator(const Iterator & it)160 inline bool check_decrementable_iterator(const Iterator& it)
161 {
162 return (it.valid()&&it!=it.owner()->begin())||it.unchecked();
163 }
164
165 template<typename Iterator>
check_is_owner(const Iterator & it,const typename Iterator::container_type & cont)166 inline bool check_is_owner(
167 const Iterator& it,const typename Iterator::container_type& cont)
168 {
169 return (it.valid()&&it.owner()==&cont)||it.unchecked();
170 }
171
172 template<typename Iterator>
check_same_owner(const Iterator & it0,const Iterator & it1)173 inline bool check_same_owner(const Iterator& it0,const Iterator& it1)
174 {
175 return (it0.valid()&&it1.valid()&&it0.owner()==it1.owner())||
176 it0.unchecked()||it1.unchecked();
177 }
178
179 template<typename Iterator>
check_valid_range(const Iterator & it0,const Iterator & it1)180 inline bool check_valid_range(const Iterator& it0,const Iterator& it1)
181 {
182 if(!check_same_owner(it0,it1))return false;
183
184 if(it0.valid()){
185 Iterator last=it0.owner()->end();
186 if(it1==last)return true;
187
188 for(Iterator first=it0;first!=last;++first){
189 if(first==it1)return true;
190 }
191 return false;
192 }
193 return true;
194 }
195
196 template<typename Iterator>
check_outside_range(const Iterator & it,const Iterator & it0,const Iterator & it1)197 inline bool check_outside_range(
198 const Iterator& it,const Iterator& it0,const Iterator& it1)
199 {
200 if(!check_same_owner(it0,it1))return false;
201
202 if(it0.valid()){
203 Iterator last=it0.owner()->end();
204 bool found=false;
205
206 Iterator first=it0;
207 for(;first!=last;++first){
208 if(first==it1)break;
209
210 /* crucial that this check goes after previous break */
211
212 if(first==it)found=true;
213 }
214 if(first!=it1)return false;
215 return !found;
216 }
217 return true;
218 }
219
220 template<typename Iterator,typename Difference>
check_in_bounds(const Iterator & it,Difference n)221 inline bool check_in_bounds(const Iterator& it,Difference n)
222 {
223 if(it.unchecked())return true;
224 if(!it.valid()) return false;
225 if(n>0) return it.owner()->end()-it>=n;
226 else return it.owner()->begin()-it<=n;
227 }
228
229 template<typename Container>
check_different_container(const Container & cont0,const Container & cont1)230 inline bool check_different_container(
231 const Container& cont0,const Container& cont1)
232 {
233 return &cont0!=&cont1;
234 }
235
236 template<typename Container0,typename Container1>
check_equal_allocators(const Container0 & cont0,const Container1 & cont1)237 inline bool check_equal_allocators(
238 const Container0& cont0,const Container1& cont1)
239 {
240 return cont0.get_allocator()==cont1.get_allocator();
241 }
242
243 /* Invalidates all iterators equivalent to that given. Safe containers
244 * must call this when deleting elements: the safe mode framework cannot
245 * perform this operation automatically without outside help.
246 */
247
248 template<typename Iterator>
detach_equivalent_iterators(Iterator & it)249 inline void detach_equivalent_iterators(Iterator& it)
250 {
251 if(it.valid()){
252 {
253 #if defined(BOOST_HAS_THREADS)
254 boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex);
255 #endif
256
257 Iterator *prev_,*next_;
258 for(
259 prev_=static_cast<Iterator*>(&it.cont->header);
260 (next_=static_cast<Iterator*>(prev_->next))!=0;){
261 if(next_!=&it&&*next_==it){
262 prev_->next=next_->next;
263 next_->cont=0;
264 }
265 else prev_=next_;
266 }
267 }
268 it.detach();
269 }
270 }
271
272 template<typename Container> class safe_container; /* fwd decl. */
273
274 } /* namespace multi_index::safe_mode */
275
276 namespace detail{
277
278 class safe_container_base; /* fwd decl. */
279
280 class safe_iterator_base
281 {
282 public:
valid() const283 bool valid()const{return cont!=0;}
unchecked() const284 bool unchecked()const{return unchecked_;}
285
286 inline void detach();
287
uncheck()288 void uncheck()
289 {
290 detach();
291 unchecked_=true;
292 }
293
294 protected:
safe_iterator_base()295 safe_iterator_base():cont(0),next(0),unchecked_(false){}
296
safe_iterator_base(safe_container_base * cont_)297 explicit safe_iterator_base(safe_container_base* cont_):
298 unchecked_(false)
299 {
300 attach(cont_);
301 }
302
safe_iterator_base(const safe_iterator_base & it)303 safe_iterator_base(const safe_iterator_base& it):
304 unchecked_(it.unchecked_)
305 {
306 attach(it.cont);
307 }
308
operator =(const safe_iterator_base & it)309 safe_iterator_base& operator=(const safe_iterator_base& it)
310 {
311 unchecked_=it.unchecked_;
312 safe_container_base* new_cont=it.cont;
313 if(cont!=new_cont){
314 detach();
315 attach(new_cont);
316 }
317 return *this;
318 }
319
~safe_iterator_base()320 ~safe_iterator_base()
321 {
322 detach();
323 }
324
owner() const325 const safe_container_base* owner()const{return cont;}
326
327 BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS:
328 friend class safe_container_base;
329
330 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
331 template<typename> friend class safe_mode::safe_container;
332 template<typename Iterator> friend
333 void safe_mode::detach_equivalent_iterators(Iterator&);
334 #endif
335
336 inline void attach(safe_container_base* cont_);
337
338 safe_container_base* cont;
339 safe_iterator_base* next;
340 bool unchecked_;
341 };
342
343 class safe_container_base:private noncopyable
344 {
345 public:
safe_container_base()346 safe_container_base(){}
347
348 BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
349 friend class safe_iterator_base;
350
351 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
352 template<typename Iterator> friend
353 void safe_mode::detach_equivalent_iterators(Iterator&);
354 #endif
355
~safe_container_base()356 ~safe_container_base()
357 {
358 /* Detaches all remaining iterators, which by now will
359 * be those pointing to the end of the container.
360 */
361
362 for(safe_iterator_base* it=header.next;it;it=it->next)it->cont=0;
363 header.next=0;
364 }
365
swap(safe_container_base & x)366 void swap(safe_container_base& x)
367 {
368 for(safe_iterator_base* it0=header.next;it0;it0=it0->next)it0->cont=&x;
369 for(safe_iterator_base* it1=x.header.next;it1;it1=it1->next)it1->cont=this;
370 std::swap(header.cont,x.header.cont);
371 std::swap(header.next,x.header.next);
372 }
373
374 safe_iterator_base header;
375
376 #if defined(BOOST_HAS_THREADS)
377 boost::detail::lightweight_mutex mutex;
378 #endif
379 };
380
attach(safe_container_base * cont_)381 void safe_iterator_base::attach(safe_container_base* cont_)
382 {
383 cont=cont_;
384 if(cont){
385 #if defined(BOOST_HAS_THREADS)
386 boost::detail::lightweight_mutex::scoped_lock lock(cont->mutex);
387 #endif
388
389 next=cont->header.next;
390 cont->header.next=this;
391 }
392 }
393
detach()394 void safe_iterator_base::detach()
395 {
396 if(cont){
397 #if defined(BOOST_HAS_THREADS)
398 boost::detail::lightweight_mutex::scoped_lock lock(cont->mutex);
399 #endif
400
401 safe_iterator_base *prev_,*next_;
402 for(prev_=&cont->header;(next_=prev_->next)!=this;prev_=next_){}
403 prev_->next=next;
404 cont=0;
405 }
406 }
407
408 } /* namespace multi_index::detail */
409
410 namespace safe_mode{
411
412 /* In order to enable safe mode on a container:
413 * - The container must derive from safe_container<container_type>,
414 * - iterators must be generated via safe_iterator, which adapts a
415 * preexistent unsafe iterator class.
416 */
417
418 template<typename Container>
419 class safe_container;
420
421 template<typename Iterator,typename Container>
422 class safe_iterator:
423 public detail::iter_adaptor<safe_iterator<Iterator,Container>,Iterator>,
424 public detail::safe_iterator_base
425 {
426 typedef detail::iter_adaptor<safe_iterator,Iterator> super;
427 typedef detail::safe_iterator_base safe_super;
428
429 public:
430 typedef Container container_type;
431 typedef typename Iterator::reference reference;
432 typedef typename Iterator::difference_type difference_type;
433
safe_iterator()434 safe_iterator(){}
safe_iterator(safe_container<container_type> * cont_)435 explicit safe_iterator(safe_container<container_type>* cont_):
436 safe_super(cont_){}
437 template<typename T0>
safe_iterator(const T0 & t0,safe_container<container_type> * cont_)438 safe_iterator(const T0& t0,safe_container<container_type>* cont_):
439 super(Iterator(t0)),safe_super(cont_){}
440 template<typename T0,typename T1>
safe_iterator(const T0 & t0,const T1 & t1,safe_container<container_type> * cont_)441 safe_iterator(
442 const T0& t0,const T1& t1,safe_container<container_type>* cont_):
443 super(Iterator(t0,t1)),safe_super(cont_){}
safe_iterator(const safe_iterator & x)444 safe_iterator(const safe_iterator& x):super(x),safe_super(x){}
445
operator =(const safe_iterator & x)446 safe_iterator& operator=(const safe_iterator& x)
447 {
448 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
449 this->base_reference()=x.base_reference();
450 safe_super::operator=(x);
451 return *this;
452 }
453
owner() const454 const container_type* owner()const
455 {
456 return
457 static_cast<const container_type*>(
458 static_cast<const safe_container<container_type>*>(
459 this->safe_super::owner()));
460 }
461
462 /* get_node is not to be used by the user */
463
464 typedef typename Iterator::node_type node_type;
465
get_node() const466 node_type* get_node()const{return this->base_reference().get_node();}
467
468 private:
469 friend class boost::multi_index::detail::iter_adaptor_access;
470
dereference() const471 reference dereference()const
472 {
473 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
474 BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(*this);
475 return *(this->base_reference());
476 }
477
equal(const safe_iterator & x) const478 bool equal(const safe_iterator& x)const
479 {
480 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
481 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
482 BOOST_MULTI_INDEX_CHECK_SAME_OWNER(*this,x);
483 return this->base_reference()==x.base_reference();
484 }
485
increment()486 void increment()
487 {
488 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
489 BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(*this);
490 ++(this->base_reference());
491 }
492
decrement()493 void decrement()
494 {
495 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
496 BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(*this);
497 --(this->base_reference());
498 }
499
advance(difference_type n)500 void advance(difference_type n)
501 {
502 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
503 BOOST_MULTI_INDEX_CHECK_IN_BOUNDS(*this,n);
504 this->base_reference()+=n;
505 }
506
distance_to(const safe_iterator & x) const507 difference_type distance_to(const safe_iterator& x)const
508 {
509 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
510 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
511 BOOST_MULTI_INDEX_CHECK_SAME_OWNER(*this,x);
512 return x.base_reference()-this->base_reference();
513 }
514
515 #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
516 /* Serialization. Note that Iterator::save and Iterator:load
517 * are assumed to be defined and public: at first sight it seems
518 * like we could have resorted to the public serialization interface
519 * for doing the forwarding to the adapted iterator class:
520 * ar<<base_reference();
521 * ar>>base_reference();
522 * but this would cause incompatibilities if a saving
523 * program is in safe mode and the loading program is not, or
524 * viceversa --in safe mode, the archived iterator data is one layer
525 * deeper, this is especially relevant with XML archives.
526 * It'd be nice if Boost.Serialization provided some forwarding
527 * facility for use by adaptor classes.
528 */
529
530 friend class boost::serialization::access;
531
BOOST_SERIALIZATION_SPLIT_MEMBER()532 BOOST_SERIALIZATION_SPLIT_MEMBER()
533
534 template<class Archive>
535 void save(Archive& ar,const unsigned int version)const
536 {
537 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
538 this->base_reference().save(ar,version);
539 }
540
541 template<class Archive>
load(Archive & ar,const unsigned int version)542 void load(Archive& ar,const unsigned int version)
543 {
544 this->base_reference().load(ar,version);
545 safe_super::uncheck();
546 }
547 #endif
548 };
549
550 template<typename Container>
551 class safe_container:public detail::safe_container_base
552 {
553 typedef detail::safe_container_base super;
554
555 public:
detach_dereferenceable_iterators()556 void detach_dereferenceable_iterators()
557 {
558 typedef typename Container::iterator iterator;
559
560 iterator end_=static_cast<Container*>(this)->end();
561 iterator *prev_,*next_;
562 for(
563 prev_=static_cast<iterator*>(&this->header);
564 (next_=static_cast<iterator*>(prev_->next))!=0;){
565 if(*next_!=end_){
566 prev_->next=next_->next;
567 next_->cont=0;
568 }
569 else prev_=next_;
570 }
571 }
572
swap(safe_container<Container> & x)573 void swap(safe_container<Container>& x)
574 {
575 super::swap(x);
576 }
577 };
578
579 } /* namespace multi_index::safe_mode */
580
581 } /* namespace multi_index */
582
583 #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
584 namespace serialization{
585 template<typename Iterator,typename Container>
586 struct version<
587 boost::multi_index::safe_mode::safe_iterator<Iterator,Container>
588 >
589 {
590 BOOST_STATIC_CONSTANT(
591 int,value=boost::serialization::version<Iterator>::value);
592 };
593 } /* namespace serialization */
594 #endif
595
596 } /* namespace boost */
597
598 #endif /* BOOST_MULTI_INDEX_ENABLE_SAFE_MODE */
599
600 #endif
601