1 
2 // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
3 // Copyright (C) 2005-2009 Daniel James
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 
7 #ifndef BOOST_UNORDERED_DETAIL_ALL_HPP_INCLUDED
8 #define BOOST_UNORDERED_DETAIL_ALL_HPP_INCLUDED
9 
10 #include <cstddef>
11 #include <stdexcept>
12 #include <algorithm>
13 #include <boost/config/no_tr1/cmath.hpp>
14 #include <boost/iterator/iterator_categories.hpp>
15 #include <boost/throw_exception.hpp>
16 
17 #include <boost/unordered/detail/buckets.hpp>
18 
19 namespace boost { namespace unordered_detail {
20 
21     ////////////////////////////////////////////////////////////////////////////
22     // Helper methods
23 
24     // strong exception safety, no side effects
25     template <class T>
equal(key_type const & k,value_type const & v) const26     inline bool hash_table<T>::equal(
27         key_type const& k, value_type const& v) const
28     {
29         return this->key_eq()(k, get_key(v));
30     }
31 
32     // strong exception safety, no side effects
33     template <class T>
34     template <class Key, class Pred>
35     inline BOOST_DEDUCED_TYPENAME T::node_ptr
find_iterator(bucket_ptr bucket,Key const & k,Pred const & eq) const36         hash_table<T>::find_iterator(bucket_ptr bucket, Key const& k,
37             Pred const& eq) const
38     {
39         node_ptr it = bucket->next_;
40         while (BOOST_UNORDERED_BORLAND_BOOL(it) &&
41             !eq(k, get_key(node::get_value(it))))
42         {
43             it = node::next_group(it);
44         }
45 
46         return it;
47     }
48 
49     // strong exception safety, no side effects
50     template <class T>
51     inline BOOST_DEDUCED_TYPENAME T::node_ptr
find_iterator(bucket_ptr bucket,key_type const & k) const52         hash_table<T>::find_iterator(
53             bucket_ptr bucket, key_type const& k) const
54     {
55         node_ptr it = bucket->next_;
56         while (BOOST_UNORDERED_BORLAND_BOOL(it) &&
57             !equal(k, node::get_value(it)))
58         {
59             it = node::next_group(it);
60         }
61 
62         return it;
63     }
64 
65     // strong exception safety, no side effects
66     // pre: this->buckets_
67     template <class T>
68     inline BOOST_DEDUCED_TYPENAME T::node_ptr
find_iterator(key_type const & k) const69         hash_table<T>::find_iterator(key_type const& k) const
70     {
71         return find_iterator(this->get_bucket(this->bucket_index(k)), k);
72     }
73 
74     // strong exception safety, no side effects
75     template <class T>
76     inline BOOST_DEDUCED_TYPENAME T::node_ptr*
find_for_erase(bucket_ptr bucket,key_type const & k) const77         hash_table<T>::find_for_erase(
78             bucket_ptr bucket, key_type const& k) const
79     {
80         node_ptr* it = &bucket->next_;
81         while(BOOST_UNORDERED_BORLAND_BOOL(*it) &&
82             !equal(k, node::get_value(*it)))
83         {
84             it = &node::next_group(*it);
85         }
86 
87         return it;
88     }
89 
90     ////////////////////////////////////////////////////////////////////////////
91     // Load methods
92 
93     // no throw
94     template <class T>
max_size() const95     std::size_t hash_table<T>::max_size() const
96     {
97         using namespace std;
98 
99         // size < mlf_ * count
100         return double_to_size_t(ceil(
101                 (double) this->mlf_ * this->max_bucket_count())) - 1;
102     }
103 
104     // strong safety
105     template <class T>
bucket_index(key_type const & k) const106     inline std::size_t hash_table<T>::bucket_index(
107         key_type const& k) const
108     {
109         // hash_function can throw:
110         return this->hash_function()(k) % this->bucket_count_;
111     }
112 
113 
114     // no throw
115     template <class T>
calculate_max_load()116     inline std::size_t hash_table<T>::calculate_max_load()
117     {
118         using namespace std;
119 
120         // From 6.3.1/13:
121         // Only resize when size >= mlf_ * count
122         return double_to_size_t(ceil((double) mlf_ * this->bucket_count_));
123     }
124 
125     template <class T>
max_load_factor(float z)126     void hash_table<T>::max_load_factor(float z)
127     {
128         BOOST_ASSERT(z > 0);
129         mlf_ = (std::max)(z, minimum_max_load_factor);
130         this->max_load_ = this->calculate_max_load();
131     }
132 
133     // no throw
134     template <class T>
min_buckets_for_size(std::size_t size) const135     inline std::size_t hash_table<T>::min_buckets_for_size(
136         std::size_t size) const
137     {
138         BOOST_ASSERT(this->mlf_ != 0);
139 
140         using namespace std;
141 
142         // From 6.3.1/13:
143         // size < mlf_ * count
144         // => count > size / mlf_
145         //
146         // Or from rehash post-condition:
147         // count > size / mlf_
148         return next_prime(double_to_size_t(floor(size / (double) mlf_)) + 1);
149     }
150 
151     ////////////////////////////////////////////////////////////////////////////
152     // recompute_begin_bucket
153 
154     // init_buckets
155 
156     template <class T>
init_buckets()157     inline void hash_table<T>::init_buckets()
158     {
159         if (this->size_) {
160             this->cached_begin_bucket_ = this->buckets_;
161             while (!this->cached_begin_bucket_->next_)
162                 ++this->cached_begin_bucket_;
163         } else {
164             this->cached_begin_bucket_ = this->get_bucket(this->bucket_count_);
165         }
166         this->max_load_ = calculate_max_load();
167     }
168 
169     // After an erase cached_begin_bucket_ might be left pointing to
170     // an empty bucket, so this is called to update it
171     //
172     // no throw
173 
174     template <class T>
recompute_begin_bucket(bucket_ptr b)175     inline void hash_table<T>::recompute_begin_bucket(bucket_ptr b)
176     {
177         BOOST_ASSERT(!(b < this->cached_begin_bucket_));
178 
179         if(b == this->cached_begin_bucket_)
180         {
181             if (this->size_ != 0) {
182                 while (!this->cached_begin_bucket_->next_)
183                     ++this->cached_begin_bucket_;
184             } else {
185                 this->cached_begin_bucket_ =
186                     this->get_bucket(this->bucket_count_);
187             }
188         }
189     }
190 
191     // This is called when a range has been erased
192     //
193     // no throw
194 
195     template <class T>
recompute_begin_bucket(bucket_ptr b1,bucket_ptr b2)196     inline void hash_table<T>::recompute_begin_bucket(
197         bucket_ptr b1, bucket_ptr b2)
198     {
199         BOOST_ASSERT(!(b1 < this->cached_begin_bucket_) && !(b2 < b1));
200         BOOST_ASSERT(BOOST_UNORDERED_BORLAND_BOOL(b2->next_));
201 
202         if(b1 == this->cached_begin_bucket_ && !b1->next_)
203             this->cached_begin_bucket_ = b2;
204     }
205 
206     // no throw
207     template <class T>
load_factor() const208     inline float hash_table<T>::load_factor() const
209     {
210         BOOST_ASSERT(this->bucket_count_ != 0);
211         return static_cast<float>(this->size_)
212             / static_cast<float>(this->bucket_count_);
213     }
214 
215     ////////////////////////////////////////////////////////////////////////////
216     // Constructors
217 
218     template <class T>
hash_table(std::size_t num_buckets,hasher const & hf,key_equal const & eq,node_allocator const & a)219     hash_table<T>::hash_table(std::size_t num_buckets,
220         hasher const& hf, key_equal const& eq, node_allocator const& a)
221       : buckets(a, next_prime(num_buckets)),
222         base(hf, eq),
223         size_(),
224         mlf_(1.0f),
225         cached_begin_bucket_(),
226         max_load_(0)
227     {
228     }
229 
230     // Copy Construct with allocator
231 
232     template <class T>
hash_table(hash_table const & x,node_allocator const & a)233     hash_table<T>::hash_table(hash_table const& x,
234         node_allocator const& a)
235       : buckets(a, x.min_buckets_for_size(x.size_)),
236         base(x),
237         size_(x.size_),
238         mlf_(x.mlf_),
239         cached_begin_bucket_(),
240         max_load_(0)
241     {
242         if(x.size_) {
243             x.copy_buckets_to(*this);
244             this->init_buckets();
245         }
246     }
247 
248     // Move Construct
249 
250     template <class T>
hash_table(hash_table & x,move_tag)251     hash_table<T>::hash_table(hash_table& x, move_tag)
252       : buckets(x.node_alloc(), x.bucket_count_),
253         base(x),
254         size_(0),
255         mlf_(1.0f),
256         cached_begin_bucket_(),
257         max_load_(0)
258     {
259         this->partial_swap(x);
260     }
261 
262     template <class T>
hash_table(hash_table & x,node_allocator const & a,move_tag)263     hash_table<T>::hash_table(hash_table& x,
264         node_allocator const& a, move_tag)
265       : buckets(a, x.bucket_count_),
266         base(x),
267         size_(0),
268         mlf_(x.mlf_),
269         cached_begin_bucket_(),
270         max_load_(0)
271     {
272         if(a == x.node_alloc()) {
273             this->partial_swap(x);
274         }
275         else if(x.size_) {
276             x.copy_buckets_to(*this);
277             this->size_ = x.size_;
278             this->init_buckets();
279         }
280     }
281 
282     template <class T>
operator =(hash_table const & x)283     hash_table<T>& hash_table<T>::operator=(
284         hash_table const& x)
285     {
286         hash_table tmp(x, this->node_alloc());
287         this->fast_swap(tmp);
288         return *this;
289     }
290 
291     ////////////////////////////////////////////////////////////////////////////
292     // Swap & Move
293 
294     // Swap
295     //
296     // Strong exception safety
297     //
298     // Can throw if hash or predicate object's copy constructor throws
299     // or if allocators are unequal.
300 
301     template <class T>
partial_swap(hash_table & x)302     inline void hash_table<T>::partial_swap(hash_table& x)
303     {
304         this->buckets::swap(x); // No throw
305         std::swap(this->size_, x.size_);
306         std::swap(this->mlf_, x.mlf_);
307         std::swap(this->cached_begin_bucket_, x.cached_begin_bucket_);
308         std::swap(this->max_load_, x.max_load_);
309     }
310 
311     template <class T>
fast_swap(hash_table & x)312     inline void hash_table<T>::fast_swap(hash_table& x)
313     {
314         // These can throw, but they only affect the function objects
315         // that aren't in use so it is strongly exception safe, via.
316         // double buffering.
317         {
318             set_hash_functions<hasher, key_equal> op1(*this, x);
319             set_hash_functions<hasher, key_equal> op2(x, *this);
320             op1.commit();
321             op2.commit();
322         }
323         this->buckets::swap(x); // No throw
324         std::swap(this->size_, x.size_);
325         std::swap(this->mlf_, x.mlf_);
326         std::swap(this->cached_begin_bucket_, x.cached_begin_bucket_);
327         std::swap(this->max_load_, x.max_load_);
328     }
329 
330     template <class T>
slow_swap(hash_table & x)331     inline void hash_table<T>::slow_swap(hash_table& x)
332     {
333         if(this == &x) return;
334 
335         {
336             // These can throw, but they only affect the function objects
337             // that aren't in use so it is strongly exception safe, via.
338             // double buffering.
339             set_hash_functions<hasher, key_equal> op1(*this, x);
340             set_hash_functions<hasher, key_equal> op2(x, *this);
341 
342             // Create new buckets in separate hash_buckets objects
343             // which will clean up if anything throws an exception.
344             // (all can throw, but with no effect as these are new objects).
345 
346             buckets b1(this->node_alloc(), x.min_buckets_for_size(x.size_));
347             if(x.size_) x.copy_buckets_to(b1);
348 
349             buckets b2(x.node_alloc(), this->min_buckets_for_size(this->size_));
350             if(this->size_) copy_buckets_to(b2);
351 
352             // Modifying the data, so no throw from now on.
353 
354             b1.swap(*this);
355             b2.swap(x);
356             op1.commit();
357             op2.commit();
358         }
359 
360         std::swap(this->size_, x.size_);
361 
362         if(this->buckets_) this->init_buckets();
363         if(x.buckets_) x.init_buckets();
364     }
365 
366     template <class T>
swap(hash_table & x)367     void hash_table<T>::swap(hash_table& x)
368     {
369         if(this->node_alloc() == x.node_alloc()) {
370             if(this != &x) this->fast_swap(x);
371         }
372         else {
373             this->slow_swap(x);
374         }
375     }
376 
377 
378     // Move
379     //
380     // Strong exception safety (might change unused function objects)
381     //
382     // Can throw if hash or predicate object's copy constructor throws
383     // or if allocators are unequal.
384 
385     template <class T>
move(hash_table & x)386     void hash_table<T>::move(hash_table& x)
387     {
388         // This can throw, but it only affects the function objects
389         // that aren't in use so it is strongly exception safe, via.
390         // double buffering.
391         set_hash_functions<hasher, key_equal> new_func_this(*this, x);
392 
393         if(this->node_alloc() == x.node_alloc()) {
394             this->buckets::move(x); // no throw
395             this->size_ = x.size_;
396             this->cached_begin_bucket_ = x.cached_begin_bucket_;
397             this->max_load_ = x.max_load_;
398             x.size_ = 0;
399         }
400         else {
401             // Create new buckets in separate HASH_TABLE_DATA objects
402             // which will clean up if anything throws an exception.
403             // (all can throw, but with no effect as these are new objects).
404 
405             buckets b(this->node_alloc(), x.min_buckets_for_size(x.size_));
406             if(x.size_) x.copy_buckets_to(b);
407 
408             // Start updating the data here, no throw from now on.
409             this->size_ = x.size_;
410             b.swap(*this);
411             this->init_buckets();
412         }
413 
414         // We've made it, the rest is no throw.
415         this->mlf_ = x.mlf_;
416         new_func_this.commit();
417     }
418 
419     ////////////////////////////////////////////////////////////////////////////
420     // Reserve & Rehash
421 
422     // basic exception safety
423     template <class T>
create_for_insert(std::size_t size)424     inline void hash_table<T>::create_for_insert(std::size_t size)
425     {
426         this->bucket_count_ = (std::max)(this->bucket_count_,
427             this->min_buckets_for_size(size));
428         this->create_buckets();
429         this->init_buckets();
430     }
431 
432     // basic exception safety
433     template <class T>
reserve_for_insert(std::size_t size)434     inline bool hash_table<T>::reserve_for_insert(std::size_t size)
435     {
436         if(size >= max_load_) {
437             std::size_t num_buckets
438                 = this->min_buckets_for_size((std::max)(size,
439                     this->size_ + (this->size_ >> 1)));
440             if(num_buckets != this->bucket_count_) {
441                 rehash_impl(num_buckets);
442                 return true;
443             }
444         }
445 
446         return false;
447     }
448 
449     // if hash function throws, basic exception safety
450     // strong otherwise.
451 
452     template <class T>
rehash(std::size_t min_buckets)453     inline void hash_table<T>::rehash(std::size_t min_buckets)
454     {
455         using namespace std;
456 
457         if(!this->size_) {
458             if(this->buckets_) this->delete_buckets();
459             this->bucket_count_ = next_prime(min_buckets);
460         }
461         else {
462             // no throw:
463             min_buckets = next_prime((std::max)(min_buckets,
464                     double_to_size_t(floor(this->size_ / (double) mlf_)) + 1));
465             if(min_buckets != this->bucket_count_) rehash_impl(min_buckets);
466         }
467     }
468 
469     // if hash function throws, basic exception safety
470     // strong otherwise
471 
472     template <class T>
473     void hash_table<T>
rehash_impl(std::size_t num_buckets)474         ::rehash_impl(std::size_t num_buckets)
475     {
476         hasher const& hf = this->hash_function();
477         std::size_t size = this->size_;
478         bucket_ptr end = this->get_bucket(this->bucket_count_);
479 
480         buckets dst(this->node_alloc(), num_buckets);
481         dst.create_buckets();
482 
483         buckets src(this->node_alloc(), this->bucket_count_);
484         src.swap(*this);
485         this->size_ = 0;
486 
487         for(bucket_ptr bucket = this->cached_begin_bucket_;
488             bucket != end; ++bucket)
489         {
490             node_ptr group = bucket->next_;
491             while(group) {
492                 // Move the first group of equivalent nodes in bucket to dst.
493 
494                 // This next line throws iff the hash function throws.
495                 bucket_ptr dst_bucket = dst.bucket_ptr_from_hash(
496                     hf(get_key_from_ptr(group)));
497 
498                 node_ptr& next_group = node::next_group(group);
499                 bucket->next_ = next_group;
500                 next_group = dst_bucket->next_;
501                 dst_bucket->next_ = group;
502                 group = bucket->next_;
503             }
504         }
505 
506         // Swap the new nodes back into the container and setup the local
507         // variables.
508         this->size_ = size;
509         dst.swap(*this);                        // no throw
510         this->init_buckets();
511     }
512 
513     ////////////////////////////////////////////////////////////////////////////
514     // copy_buckets_to
515 
516     // copy_buckets_to
517     //
518     // basic excpetion safety. If an exception is thrown this will
519     // leave dst partially filled.
520 
521     template <class T>
522     void hash_table<T>
copy_buckets_to(buckets & dst) const523         ::copy_buckets_to(buckets& dst) const
524     {
525         BOOST_ASSERT(this->buckets_ && !dst.buckets_);
526 
527         hasher const& hf = this->hash_function();
528         bucket_ptr end = this->get_bucket(this->bucket_count_);
529 
530         node_constructor a(dst);
531         dst.create_buckets();
532 
533         // no throw:
534         for(bucket_ptr i = this->cached_begin_bucket_; i != end; ++i) {
535             // no throw:
536             for(node_ptr it = i->next_; it;) {
537                 // hash function can throw.
538                 bucket_ptr dst_bucket = dst.bucket_ptr_from_hash(
539                     hf(get_key_from_ptr(it)));
540                 // throws, strong
541 
542                 node_ptr group_end = node::next_group(it);
543 
544                 a.construct(node::get_value(it));
545                 node_ptr n = a.release();
546                 node::add_to_bucket(n, *dst_bucket);
547 
548                 for(it = it->next_; it != group_end; it = it->next_) {
549                     a.construct(node::get_value(it));
550                     node::add_after_node(a.release(), n);
551                 }
552             }
553         }
554     }
555 
556     ////////////////////////////////////////////////////////////////////////////
557     // Misc. key methods
558 
559     // strong exception safety
560 
561     // count
562     //
563     // strong exception safety, no side effects
564 
565     template <class T>
count(key_type const & k) const566     std::size_t hash_table<T>::count(key_type const& k) const
567     {
568         if(!this->size_) return 0;
569         node_ptr it = find_iterator(k); // throws, strong
570         return BOOST_UNORDERED_BORLAND_BOOL(it) ? node::group_count(it) : 0;
571     }
572 
573     // find
574     //
575     // strong exception safety, no side effects
576     template <class T>
577     BOOST_DEDUCED_TYPENAME T::iterator_base
find(key_type const & k) const578         hash_table<T>::find(key_type const& k) const
579     {
580         if(!this->size_) return this->end();
581 
582         bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
583         node_ptr it = find_iterator(bucket, k);
584 
585         if (BOOST_UNORDERED_BORLAND_BOOL(it))
586             return iterator_base(bucket, it);
587         else
588             return this->end();
589     }
590 
591     template <class T>
592     template <class Key, class Hash, class Pred>
find(Key const & k,Hash const & h,Pred const & eq) const593     BOOST_DEDUCED_TYPENAME T::iterator_base hash_table<T>::find(Key const& k,
594         Hash const& h, Pred const& eq) const
595     {
596         if(!this->size_) return this->end();
597 
598         bucket_ptr bucket = this->get_bucket(h(k) % this->bucket_count_);
599         node_ptr it = find_iterator(bucket, k, eq);
600 
601         if (BOOST_UNORDERED_BORLAND_BOOL(it))
602             return iterator_base(bucket, it);
603         else
604             return this->end();
605     }
606 
607     template <class T>
608     BOOST_DEDUCED_TYPENAME T::value_type&
at(key_type const & k) const609         hash_table<T>::at(key_type const& k) const
610     {
611         if(!this->size_)
612             boost::throw_exception(std::out_of_range("Unable to find key in unordered_map."));
613 
614         bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
615         node_ptr it = find_iterator(bucket, k);
616 
617         if (!it)
618             boost::throw_exception(std::out_of_range("Unable to find key in unordered_map."));
619 
620         return node::get_value(it);
621     }
622 
623     // equal_range
624     //
625     // strong exception safety, no side effects
626     template <class T>
627     BOOST_DEDUCED_TYPENAME T::iterator_pair
equal_range(key_type const & k) const628         hash_table<T>::equal_range(key_type const& k) const
629     {
630         if(!this->size_)
631             return iterator_pair(this->end(), this->end());
632 
633         bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
634         node_ptr it = find_iterator(bucket, k);
635         if (BOOST_UNORDERED_BORLAND_BOOL(it)) {
636             iterator_base first(iterator_base(bucket, it));
637             iterator_base second(first);
638             second.increment_bucket(node::next_group(second.node_));
639             return iterator_pair(first, second);
640         }
641         else {
642             return iterator_pair(this->end(), this->end());
643         }
644     }
645 
646     ////////////////////////////////////////////////////////////////////////////
647     // Erase methods
648 
649     template <class T>
clear()650     void hash_table<T>::clear()
651     {
652         if(!this->size_) return;
653 
654         bucket_ptr end = this->get_bucket(this->bucket_count_);
655         for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
656             this->clear_bucket(begin);
657         }
658 
659         this->size_ = 0;
660         this->cached_begin_bucket_ = end;
661     }
662 
663     template <class T>
erase_group(node_ptr * it,bucket_ptr bucket)664     inline std::size_t hash_table<T>::erase_group(
665         node_ptr* it, bucket_ptr bucket)
666     {
667         node_ptr pos = *it;
668         node_ptr end = node::next_group(pos);
669         *it = end;
670         std::size_t count = this->delete_nodes(pos, end);
671         this->size_ -= count;
672         this->recompute_begin_bucket(bucket);
673         return count;
674     }
675 
676     template <class T>
erase_key(key_type const & k)677     std::size_t hash_table<T>::erase_key(key_type const& k)
678     {
679         if(!this->size_) return 0;
680 
681         // No side effects in initial section
682         bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
683         node_ptr* it = this->find_for_erase(bucket, k);
684 
685         // No throw.
686         return *it ? this->erase_group(it, bucket) : 0;
687     }
688 
689     template <class T>
erase(iterator_base r)690     void hash_table<T>::erase(iterator_base r)
691     {
692         BOOST_ASSERT(r.node_);
693         --this->size_;
694         node::unlink_node(*r.bucket_, r.node_);
695         this->delete_node(r.node_);
696         // r has been invalidated but its bucket is still valid
697         this->recompute_begin_bucket(r.bucket_);
698     }
699 
700     template <class T>
701     BOOST_DEDUCED_TYPENAME T::iterator_base
erase_return_iterator(iterator_base r)702         hash_table<T>::erase_return_iterator(iterator_base r)
703     {
704         BOOST_ASSERT(r.node_);
705         iterator_base next = r;
706         next.increment();
707         --this->size_;
708         node::unlink_node(*r.bucket_, r.node_);
709         this->delete_node(r.node_);
710         // r has been invalidated but its bucket is still valid
711         this->recompute_begin_bucket(r.bucket_, next.bucket_);
712         return next;
713     }
714 
715     template <class T>
716     BOOST_DEDUCED_TYPENAME T::iterator_base
erase_range(iterator_base r1,iterator_base r2)717         hash_table<T>::erase_range(
718             iterator_base r1, iterator_base r2)
719     {
720         if(r1 != r2)
721         {
722             BOOST_ASSERT(r1.node_);
723             if (r1.bucket_ == r2.bucket_) {
724                 node::unlink_nodes(*r1.bucket_, r1.node_, r2.node_);
725                 this->size_ -= this->delete_nodes(r1.node_, r2.node_);
726 
727                 // No need to call recompute_begin_bucket because
728                 // the nodes are only deleted from one bucket, which
729                 // still contains r2 after the erase.
730                  BOOST_ASSERT(r1.bucket_->next_);
731             }
732             else {
733                 bucket_ptr end_bucket = r2.node_ ?
734                     r2.bucket_ : this->get_bucket(this->bucket_count_);
735                 BOOST_ASSERT(r1.bucket_ < end_bucket);
736                 node::unlink_nodes(*r1.bucket_, r1.node_, node_ptr());
737                 this->size_ -= this->delete_nodes(r1.node_, node_ptr());
738 
739                 bucket_ptr i = r1.bucket_;
740                 for(++i; i != end_bucket; ++i) {
741                     this->size_ -= this->delete_nodes(i->next_, node_ptr());
742                     i->next_ = node_ptr();
743                 }
744 
745                 if(r2.node_) {
746                     node_ptr first = r2.bucket_->next_;
747                     node::unlink_nodes(*r2.bucket_, r2.node_);
748                     this->size_ -= this->delete_nodes(first, r2.node_);
749                 }
750 
751                 // r1 has been invalidated but its bucket is still
752                 // valid.
753                 this->recompute_begin_bucket(r1.bucket_, end_bucket);
754             }
755         }
756 
757         return r2;
758     }
759 
760     template <class T>
761     BOOST_DEDUCED_TYPENAME hash_table<T>::iterator_base
emplace_empty_impl_with_node(node_constructor & a,std::size_t size)762         hash_table<T>::emplace_empty_impl_with_node(
763             node_constructor& a, std::size_t size)
764     {
765         key_type const& k = get_key(a.value());
766         std::size_t hash_value = this->hash_function()(k);
767         if(this->buckets_) this->reserve_for_insert(size);
768         else this->create_for_insert(size);
769         bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
770         node_ptr n = a.release();
771         node::add_to_bucket(n, *bucket);
772         ++this->size_;
773         this->cached_begin_bucket_ = bucket;
774         return iterator_base(bucket, n);
775     }
776 }}
777 
778 #endif
779