1 2 // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. 3 // Copyright (C) 2005-2011 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_UNIQUE_HPP_INCLUDED 8 #define BOOST_UNORDERED_DETAIL_UNIQUE_HPP_INCLUDED 9 10 #include <boost/config.hpp> 11 #if defined(BOOST_HAS_PRAGMA_ONCE) 12 #pragma once 13 #endif 14 15 #include <boost/unordered/detail/table.hpp> 16 #include <boost/unordered/detail/extract_key.hpp> 17 #include <boost/throw_exception.hpp> 18 #include <stdexcept> 19 20 namespace boost { namespace unordered { namespace detail { 21 22 template <typename A, typename T> struct unique_node; 23 template <typename T> struct ptr_node; 24 template <typename Types> struct table_impl; 25 26 template <typename A, typename T> 27 struct unique_node : 28 boost::unordered::detail::value_base<T> 29 { 30 typedef typename ::boost::unordered::detail::rebind_wrap< 31 A, unique_node<A, T> >::type allocator; 32 typedef typename ::boost::unordered::detail:: 33 allocator_traits<allocator>::pointer node_pointer; 34 typedef node_pointer link_pointer; 35 36 link_pointer next_; 37 std::size_t hash_; 38 unique_nodeboost::unordered::detail::unique_node39 unique_node() : 40 next_(), 41 hash_(0) 42 {} 43 initboost::unordered::detail::unique_node44 void init(node_pointer) 45 { 46 } 47 48 private: 49 unique_node& operator=(unique_node const&); 50 }; 51 52 template <typename T> 53 struct ptr_node : 54 boost::unordered::detail::ptr_bucket 55 { 56 typedef T value_type; 57 typedef boost::unordered::detail::ptr_bucket bucket_base; 58 typedef ptr_node<T>* node_pointer; 59 typedef ptr_bucket* link_pointer; 60 61 std::size_t hash_; 62 boost::unordered::detail::value_base<T> value_base_; 63 ptr_nodeboost::unordered::detail::ptr_node64 ptr_node() : 65 bucket_base(), 66 hash_(0) 67 {} 68 initboost::unordered::detail::ptr_node69 void init(node_pointer) 70 { 71 } 72 addressboost::unordered::detail::ptr_node73 void* address() { return value_base_.address(); } valueboost::unordered::detail::ptr_node74 value_type& value() { return value_base_.value(); } value_ptrboost::unordered::detail::ptr_node75 value_type* value_ptr() { return value_base_.value_ptr(); } 76 77 private: 78 ptr_node& operator=(ptr_node const&); 79 }; 80 81 // If the allocator uses raw pointers use ptr_node 82 // Otherwise use node. 83 84 template <typename A, typename T, typename NodePtr, typename BucketPtr> 85 struct pick_node2 86 { 87 typedef boost::unordered::detail::unique_node<A, T> node; 88 89 typedef typename boost::unordered::detail::allocator_traits< 90 typename boost::unordered::detail::rebind_wrap<A, node>::type 91 >::pointer node_pointer; 92 93 typedef boost::unordered::detail::bucket<node_pointer> bucket; 94 typedef node_pointer link_pointer; 95 }; 96 97 template <typename A, typename T> 98 struct pick_node2<A, T, 99 boost::unordered::detail::ptr_node<T>*, 100 boost::unordered::detail::ptr_bucket*> 101 { 102 typedef boost::unordered::detail::ptr_node<T> node; 103 typedef boost::unordered::detail::ptr_bucket bucket; 104 typedef bucket* link_pointer; 105 }; 106 107 template <typename A, typename T> 108 struct pick_node 109 { 110 typedef boost::unordered::detail::allocator_traits< 111 typename boost::unordered::detail::rebind_wrap<A, 112 boost::unordered::detail::ptr_node<T> >::type 113 > tentative_node_traits; 114 115 typedef boost::unordered::detail::allocator_traits< 116 typename boost::unordered::detail::rebind_wrap<A, 117 boost::unordered::detail::ptr_bucket >::type 118 > tentative_bucket_traits; 119 120 typedef pick_node2<A, T, 121 typename tentative_node_traits::pointer, 122 typename tentative_bucket_traits::pointer> pick; 123 124 typedef typename pick::node node; 125 typedef typename pick::bucket bucket; 126 typedef typename pick::link_pointer link_pointer; 127 }; 128 129 template <typename A, typename T, typename H, typename P> 130 struct set 131 { 132 typedef boost::unordered::detail::set<A, T, H, P> types; 133 134 typedef A allocator; 135 typedef T value_type; 136 typedef H hasher; 137 typedef P key_equal; 138 typedef T key_type; 139 140 typedef boost::unordered::detail::allocator_traits<allocator> traits; 141 typedef boost::unordered::detail::pick_node<allocator, value_type> pick; 142 typedef typename pick::node node; 143 typedef typename pick::bucket bucket; 144 typedef typename pick::link_pointer link_pointer; 145 146 typedef boost::unordered::detail::table_impl<types> table; 147 typedef boost::unordered::detail::set_extractor<value_type> extractor; 148 149 typedef typename boost::unordered::detail::pick_policy<T>::type policy; 150 }; 151 152 template <typename A, typename K, typename M, typename H, typename P> 153 struct map 154 { 155 typedef boost::unordered::detail::map<A, K, M, H, P> types; 156 157 typedef A allocator; 158 typedef std::pair<K const, M> value_type; 159 typedef H hasher; 160 typedef P key_equal; 161 typedef K key_type; 162 163 typedef boost::unordered::detail::allocator_traits<allocator> 164 traits; 165 typedef boost::unordered::detail::pick_node<allocator, value_type> pick; 166 typedef typename pick::node node; 167 typedef typename pick::bucket bucket; 168 typedef typename pick::link_pointer link_pointer; 169 170 typedef boost::unordered::detail::table_impl<types> table; 171 typedef boost::unordered::detail::map_extractor<key_type, value_type> 172 extractor; 173 174 typedef typename boost::unordered::detail::pick_policy<K>::type policy; 175 }; 176 177 template <typename Types> 178 struct table_impl : boost::unordered::detail::table<Types> 179 { 180 typedef boost::unordered::detail::table<Types> table; 181 typedef typename table::value_type value_type; 182 typedef typename table::bucket bucket; 183 typedef typename table::policy policy; 184 typedef typename table::node_pointer node_pointer; 185 typedef typename table::node_allocator node_allocator; 186 typedef typename table::node_allocator_traits node_allocator_traits; 187 typedef typename table::bucket_pointer bucket_pointer; 188 typedef typename table::link_pointer link_pointer; 189 typedef typename table::hasher hasher; 190 typedef typename table::key_equal key_equal; 191 typedef typename table::key_type key_type; 192 typedef typename table::node_constructor node_constructor; 193 typedef typename table::extractor extractor; 194 typedef typename table::iterator iterator; 195 typedef typename table::c_iterator c_iterator; 196 197 typedef std::pair<iterator, bool> emplace_return; 198 199 // Constructors 200 table_implboost::unordered::detail::table_impl201 table_impl(std::size_t n, 202 hasher const& hf, 203 key_equal const& eq, 204 node_allocator const& a) 205 : table(n, hf, eq, a) 206 {} 207 table_implboost::unordered::detail::table_impl208 table_impl(table_impl const& x) 209 : table(x, node_allocator_traits:: 210 select_on_container_copy_construction(x.node_alloc())) 211 { 212 this->init(x); 213 } 214 table_implboost::unordered::detail::table_impl215 table_impl(table_impl const& x, 216 node_allocator const& a) 217 : table(x, a) 218 { 219 this->init(x); 220 } 221 table_implboost::unordered::detail::table_impl222 table_impl(table_impl& x, 223 boost::unordered::detail::move_tag m) 224 : table(x, m) 225 {} 226 table_implboost::unordered::detail::table_impl227 table_impl(table_impl& x, 228 node_allocator const& a, 229 boost::unordered::detail::move_tag m) 230 : table(x, a, m) 231 { 232 this->move_init(x); 233 } 234 235 // Accessors 236 237 template <class Key, class Pred> find_node_implboost::unordered::detail::table_impl238 iterator find_node_impl( 239 std::size_t key_hash, 240 Key const& k, 241 Pred const& eq) const 242 { 243 std::size_t bucket_index = this->hash_to_bucket(key_hash); 244 iterator n = this->begin(bucket_index); 245 246 for (;;) 247 { 248 if (!n.node_) return n; 249 250 std::size_t node_hash = n.node_->hash_; 251 if (key_hash == node_hash) 252 { 253 if (eq(k, this->get_key(*n))) 254 return n; 255 } 256 else 257 { 258 if (this->hash_to_bucket(node_hash) != bucket_index) 259 return iterator(); 260 } 261 262 ++n; 263 } 264 } 265 countboost::unordered::detail::table_impl266 std::size_t count(key_type const& k) const 267 { 268 return this->find_node(k).node_ ? 1 : 0; 269 } 270 atboost::unordered::detail::table_impl271 value_type& at(key_type const& k) const 272 { 273 if (this->size_) { 274 iterator it = this->find_node(k); 275 if (it.node_) return *it; 276 } 277 278 boost::throw_exception( 279 std::out_of_range("Unable to find key in unordered_map.")); 280 } 281 282 std::pair<iterator, iterator> equal_rangeboost::unordered::detail::table_impl283 equal_range(key_type const& k) const 284 { 285 iterator n = this->find_node(k); 286 iterator n2 = n; 287 if (n2.node_) ++n2; 288 return std::make_pair(n, n2); 289 } 290 291 // equals 292 equalsboost::unordered::detail::table_impl293 bool equals(table_impl const& other) const 294 { 295 if(this->size_ != other.size_) return false; 296 297 for(iterator n1 = this->begin(); n1.node_; ++n1) 298 { 299 iterator n2 = other.find_matching_node(n1); 300 301 if (!n2.node_ || *n1 != *n2) 302 return false; 303 } 304 305 return true; 306 } 307 308 // Emplace/Insert 309 add_nodeboost::unordered::detail::table_impl310 inline iterator add_node( 311 node_constructor& a, 312 std::size_t key_hash) 313 { 314 node_pointer n = a.release(); 315 n->hash_ = key_hash; 316 317 bucket_pointer b = this->get_bucket(this->hash_to_bucket(key_hash)); 318 319 if (!b->next_) 320 { 321 link_pointer start_node = this->get_previous_start(); 322 323 if (start_node->next_) { 324 this->get_bucket(this->hash_to_bucket( 325 static_cast<node_pointer>(start_node->next_)->hash_) 326 )->next_ = n; 327 } 328 329 b->next_ = start_node; 330 n->next_ = start_node->next_; 331 start_node->next_ = n; 332 } 333 else 334 { 335 n->next_ = b->next_->next_; 336 b->next_->next_ = n; 337 } 338 339 ++this->size_; 340 return iterator(n); 341 } 342 operator []boost::unordered::detail::table_impl343 value_type& operator[](key_type const& k) 344 { 345 std::size_t key_hash = this->hash(k); 346 iterator pos = this->find_node(key_hash, k); 347 348 if (pos.node_) return *pos; 349 350 // Create the node before rehashing in case it throws an 351 // exception (need strong safety in such a case). 352 node_constructor a(this->node_alloc()); 353 a.construct_with_value(BOOST_UNORDERED_EMPLACE_ARGS3( 354 boost::unordered::piecewise_construct, 355 boost::make_tuple(k), 356 boost::make_tuple())); 357 358 this->reserve_for_insert(this->size_ + 1); 359 return *add_node(a, key_hash); 360 } 361 362 #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 363 # if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) emplaceboost::unordered::detail::table_impl364 emplace_return emplace(boost::unordered::detail::emplace_args1< 365 boost::unordered::detail::please_ignore_this_overload> const&) 366 { 367 BOOST_ASSERT(false); 368 return emplace_return(this->begin(), false); 369 } 370 # else emplaceboost::unordered::detail::table_impl371 emplace_return emplace( 372 boost::unordered::detail::please_ignore_this_overload const&) 373 { 374 BOOST_ASSERT(false); 375 return emplace_return(this->begin(), false); 376 } 377 # endif 378 #endif 379 380 template <BOOST_UNORDERED_EMPLACE_TEMPLATE> emplaceboost::unordered::detail::table_impl381 emplace_return emplace(BOOST_UNORDERED_EMPLACE_ARGS) 382 { 383 #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) 384 return emplace_impl( 385 extractor::extract(BOOST_UNORDERED_EMPLACE_FORWARD), 386 BOOST_UNORDERED_EMPLACE_FORWARD); 387 #else 388 return emplace_impl( 389 extractor::extract(args.a0, args.a1), 390 BOOST_UNORDERED_EMPLACE_FORWARD); 391 #endif 392 } 393 394 #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) 395 template <typename A0> emplaceboost::unordered::detail::table_impl396 emplace_return emplace( 397 boost::unordered::detail::emplace_args1<A0> const& args) 398 { 399 return emplace_impl(extractor::extract(args.a0), args); 400 } 401 #endif 402 403 template <BOOST_UNORDERED_EMPLACE_TEMPLATE> emplace_implboost::unordered::detail::table_impl404 emplace_return emplace_impl(key_type const& k, 405 BOOST_UNORDERED_EMPLACE_ARGS) 406 { 407 std::size_t key_hash = this->hash(k); 408 iterator pos = this->find_node(key_hash, k); 409 410 if (pos.node_) return emplace_return(pos, false); 411 412 // Create the node before rehashing in case it throws an 413 // exception (need strong safety in such a case). 414 node_constructor a(this->node_alloc()); 415 a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD); 416 417 // reserve has basic exception safety if the hash function 418 // throws, strong otherwise. 419 this->reserve_for_insert(this->size_ + 1); 420 return emplace_return(this->add_node(a, key_hash), true); 421 } 422 emplace_impl_with_nodeboost::unordered::detail::table_impl423 emplace_return emplace_impl_with_node(node_constructor& a) 424 { 425 key_type const& k = this->get_key(a.value()); 426 std::size_t key_hash = this->hash(k); 427 iterator pos = this->find_node(key_hash, k); 428 429 if (pos.node_) return emplace_return(pos, false); 430 431 // reserve has basic exception safety if the hash function 432 // throws, strong otherwise. 433 this->reserve_for_insert(this->size_ + 1); 434 return emplace_return(this->add_node(a, key_hash), true); 435 } 436 437 template <BOOST_UNORDERED_EMPLACE_TEMPLATE> emplace_implboost::unordered::detail::table_impl438 emplace_return emplace_impl(no_key, BOOST_UNORDERED_EMPLACE_ARGS) 439 { 440 // Don't have a key, so construct the node first in order 441 // to be able to lookup the position. 442 node_constructor a(this->node_alloc()); 443 a.construct_with_value(BOOST_UNORDERED_EMPLACE_FORWARD); 444 return emplace_impl_with_node(a); 445 } 446 447 //////////////////////////////////////////////////////////////////////// 448 // Insert range methods 449 // 450 // if hash function throws, or inserting > 1 element, basic exception 451 // safety strong otherwise 452 453 template <class InputIt> insert_rangeboost::unordered::detail::table_impl454 void insert_range(InputIt i, InputIt j) 455 { 456 if(i != j) 457 return insert_range_impl(extractor::extract(*i), i, j); 458 } 459 460 template <class InputIt> insert_range_implboost::unordered::detail::table_impl461 void insert_range_impl(key_type const& k, InputIt i, InputIt j) 462 { 463 node_constructor a(this->node_alloc()); 464 465 insert_range_impl2(a, k, i, j); 466 467 while(++i != j) { 468 // Note: can't use get_key as '*i' might not be value_type - it 469 // could be a pair with first_types as key_type without const or 470 // a different second_type. 471 // 472 // TODO: Might be worth storing the value_type instead of the 473 // key here. Could be more efficient if '*i' is expensive. Could 474 // be less efficient if copying the full value_type is 475 // expensive. 476 insert_range_impl2(a, extractor::extract(*i), i, j); 477 } 478 } 479 480 template <class InputIt> insert_range_impl2boost::unordered::detail::table_impl481 void insert_range_impl2(node_constructor& a, key_type const& k, 482 InputIt i, InputIt j) 483 { 484 // No side effects in this initial code 485 std::size_t key_hash = this->hash(k); 486 iterator pos = this->find_node(key_hash, k); 487 488 if (!pos.node_) { 489 a.construct_with_value2(*i); 490 if(this->size_ + 1 > this->max_load_) 491 this->reserve_for_insert(this->size_ + 492 boost::unordered::detail::insert_size(i, j)); 493 494 // Nothing after this point can throw. 495 this->add_node(a, key_hash); 496 } 497 } 498 499 template <class InputIt> insert_range_implboost::unordered::detail::table_impl500 void insert_range_impl(no_key, InputIt i, InputIt j) 501 { 502 node_constructor a(this->node_alloc()); 503 504 do { 505 a.construct_with_value2(*i); 506 emplace_impl_with_node(a); 507 } while(++i != j); 508 } 509 510 //////////////////////////////////////////////////////////////////////// 511 // Erase 512 // 513 // no throw 514 erase_keyboost::unordered::detail::table_impl515 std::size_t erase_key(key_type const& k) 516 { 517 if(!this->size_) return 0; 518 519 std::size_t key_hash = this->hash(k); 520 std::size_t bucket_index = this->hash_to_bucket(key_hash); 521 link_pointer prev = this->get_previous_start(bucket_index); 522 if (!prev) return 0; 523 524 for (;;) 525 { 526 if (!prev->next_) return 0; 527 std::size_t node_hash = 528 static_cast<node_pointer>(prev->next_)->hash_; 529 if (this->hash_to_bucket(node_hash) != bucket_index) 530 return 0; 531 if (node_hash == key_hash && 532 this->key_eq()(k, this->get_key( 533 static_cast<node_pointer>(prev->next_)->value()))) 534 break; 535 prev = prev->next_; 536 } 537 538 link_pointer end = static_cast<node_pointer>(prev->next_)->next_; 539 540 std::size_t deleted_count = this->delete_nodes(prev, end); 541 this->fix_bucket(bucket_index, prev); 542 return deleted_count; 543 } 544 eraseboost::unordered::detail::table_impl545 iterator erase(c_iterator r) 546 { 547 BOOST_ASSERT(r.node_); 548 iterator next(r.node_); 549 ++next; 550 erase_nodes(r.node_, next.node_); 551 return next; 552 } 553 erase_rangeboost::unordered::detail::table_impl554 iterator erase_range(c_iterator r1, c_iterator r2) 555 { 556 if (r1 == r2) return iterator(r2.node_); 557 erase_nodes(r1.node_, r2.node_); 558 return iterator(r2.node_); 559 } 560 erase_nodesboost::unordered::detail::table_impl561 void erase_nodes(node_pointer i, node_pointer j) 562 { 563 std::size_t bucket_index = this->hash_to_bucket(i->hash_); 564 565 // Find the node before i. 566 link_pointer prev = this->get_previous_start(bucket_index); 567 while(prev->next_ != i) prev = prev->next_; 568 569 // Delete the nodes. 570 do { 571 this->delete_node(prev); 572 bucket_index = this->fix_bucket(bucket_index, prev); 573 } while (prev->next_ != j); 574 } 575 576 //////////////////////////////////////////////////////////////////////// 577 // fill_buckets 578 579 template <class NodeCreator> fill_bucketsboost::unordered::detail::table_impl580 static void fill_buckets(iterator n, table& dst, 581 NodeCreator& creator) 582 { 583 link_pointer prev = dst.get_previous_start(); 584 585 while (n.node_) { 586 node_pointer node = creator.create(*n); 587 node->hash_ = n.node_->hash_; 588 prev->next_ = node; 589 ++dst.size_; 590 ++n; 591 592 prev = place_in_bucket(dst, prev); 593 } 594 } 595 596 // strong otherwise exception safety rehash_implboost::unordered::detail::table_impl597 void rehash_impl(std::size_t num_buckets) 598 { 599 BOOST_ASSERT(this->buckets_); 600 601 this->create_buckets(num_buckets); 602 link_pointer prev = this->get_previous_start(); 603 while (prev->next_) 604 prev = place_in_bucket(*this, prev); 605 } 606 607 // Iterate through the nodes placing them in the correct buckets. 608 // pre: prev->next_ is not null. place_in_bucketboost::unordered::detail::table_impl609 static link_pointer place_in_bucket(table& dst, link_pointer prev) 610 { 611 node_pointer n = static_cast<node_pointer>(prev->next_); 612 bucket_pointer b = dst.get_bucket(dst.hash_to_bucket(n->hash_)); 613 614 if (!b->next_) { 615 b->next_ = prev; 616 return n; 617 } 618 else { 619 prev->next_ = n->next_; 620 n->next_ = b->next_->next_; 621 b->next_->next_ = n; 622 return prev; 623 } 624 } 625 }; 626 }}} 627 628 #endif 629