1 /* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef API_BASE_CONTAINERS_UNORDERED_MAP_H 17 #define API_BASE_CONTAINERS_UNORDERED_MAP_H 18 19 #include <cstddef> 20 #include <cstdint> 21 22 #include <base/containers/iterator.h> 23 #include <base/containers/pair.h> 24 #include <base/containers/string.h> 25 #include <base/containers/string_view.h> 26 #include <base/containers/vector.h> 27 #include <base/namespace.h> 28 #include <base/util/log.h> 29 30 BASE_BEGIN_NAMESPACE() 31 template<class T> 32 class unordered_map_iterator; 33 34 template<class T> 35 class const_unordered_map_iterator; 36 37 #ifdef BASE_STD_COMPATIBILITY 38 using forward_iterator_tag = std::forward_iterator_tag; // yeah. we need this for std compatibility. 39 #else 40 struct forward_iterator_tag {}; 41 #endif 42 43 template<class T> 44 class const_unordered_map_iterator { 45 public: 46 using base_container = T; 47 48 using iterator_category = forward_iterator_tag; 49 using value_type = typename base_container::value_type; 50 using difference_type = ptrdiff_t; 51 using pointer = typename base_container::const_pointer; 52 using reference = typename base_container::const_reference; 53 54 using node_type = typename base_container::list_node; 55 using iterator = typename base_container::const_iterator; 56 57 constexpr const_unordered_map_iterator() noexcept = default; const_unordered_map_iterator(const typename base_container::iterator & other)58 constexpr const_unordered_map_iterator(const typename base_container::iterator& other) noexcept 59 : owner_ { other.owner_ }, it_ { other.it_ } 60 {} 61 62 constexpr reference operator*() const noexcept 63 { 64 BASE_ASSERT(owner_ && it_); 65 return it_->data; 66 } 67 constexpr pointer operator->() const noexcept 68 { 69 BASE_ASSERT(owner_ && it_); 70 return &it_->data; 71 } 72 73 constexpr bool operator==(const iterator& other) const noexcept 74 { 75 return ((owner_ == other.owner_) && (it_ == other.it_)); 76 } 77 constexpr bool operator!=(const iterator& other) const noexcept 78 { 79 return ((owner_ != other.owner_) || (it_ != other.it_)); 80 } 81 82 constexpr bool operator==(const typename base_container::iterator& other) const noexcept 83 { 84 return ((owner_ == other.owner_) && (it_ == other.it_)); 85 } 86 constexpr bool operator!=(const typename base_container::iterator& other) const noexcept 87 { 88 return ((owner_ != other.owner_) || (it_ != other.it_)); 89 } 90 constexpr iterator& operator++() noexcept 91 { 92 it_ = owner_->advance(*this); 93 return *this; 94 } 95 96 protected: const_unordered_map_iterator(const T & owner,const node_type * ptr)97 constexpr explicit const_unordered_map_iterator(const T& owner, const node_type* ptr) noexcept 98 : owner_(&owner), it_ { ptr } 99 {} 100 friend T; 101 friend class unordered_map_iterator<T>; 102 const T* owner_ { nullptr }; 103 const node_type* it_ { nullptr }; 104 }; 105 106 template<class T> 107 class unordered_map_iterator { 108 public: 109 using base_container = T; 110 111 using iterator_category = forward_iterator_tag; 112 using value_type = typename base_container::value_type; 113 using difference_type = ptrdiff_t; 114 using pointer = typename base_container::pointer; 115 using reference = typename base_container::reference; 116 117 using node_type = typename base_container::list_node; 118 using iterator = typename base_container::iterator; 119 using const_iterator = typename base_container::const_iterator; 120 121 constexpr unordered_map_iterator() noexcept = default; 122 123 constexpr reference operator*() const noexcept 124 { 125 BASE_ASSERT(owner_ && it_); 126 return it_->data; 127 } 128 constexpr pointer operator->() const noexcept 129 { 130 BASE_ASSERT(owner_ && it_); 131 return &it_->data; 132 } 133 constexpr reference operator*() noexcept 134 { 135 BASE_ASSERT(owner_ && it_); 136 return it_->data; 137 } 138 constexpr pointer operator->() noexcept 139 { 140 BASE_ASSERT(owner_ && it_); 141 return &it_->data; 142 } 143 144 constexpr bool operator==(const iterator& other) const noexcept 145 { 146 return ((owner_ == other.owner_) && (it_ == other.it_)); 147 } 148 constexpr bool operator!=(const iterator& other) const noexcept 149 { 150 return ((owner_ != other.owner_) || (it_ != other.it_)); 151 } 152 153 constexpr bool operator==(const typename base_container::const_iterator& other) const noexcept 154 { 155 return ((owner_ == other.owner_) && (it_ == other.it_)); 156 } 157 constexpr bool operator!=(const typename base_container::const_iterator& other) const noexcept 158 { 159 return ((owner_ != other.owner_) || (it_ != other.it_)); 160 } 161 162 constexpr iterator& operator++() noexcept 163 { 164 it_ = owner_->advance(*this); 165 return *this; 166 } 167 168 protected: unordered_map_iterator(T & owner,node_type * ptr)169 constexpr explicit unordered_map_iterator(T& owner, node_type* ptr) noexcept : owner_ { &owner }, it_ { ptr } {} 170 friend T; 171 friend const_unordered_map_iterator<T>; 172 T* owner_ { nullptr }; 173 node_type* it_ { nullptr }; 174 }; 175 176 template<class data_type> 177 struct node_handle { 178 using key_type = typename data_type::value_type::first_type; 179 using mapped_type = typename data_type::value_type::second_type; 180 using value_type = data_type; 181 emptynode_handle182 [[nodiscard]] bool empty() const noexcept 183 { 184 return pointer == nullptr; 185 } 186 187 explicit operator bool() const noexcept 188 { 189 return !empty(); 190 } 191 keynode_handle192 key_type& key() const 193 { 194 return pointer->data.first; 195 } 196 mappednode_handle197 mapped_type& mapped() const 198 { 199 return pointer->data.second; 200 } 201 ~node_handlenode_handle202 ~node_handle() 203 { 204 if (pointer) { 205 if constexpr (!__is_trivially_destructible(value_type)) { 206 pointer->~data_type(); 207 } 208 allocator_.free(pointer); 209 } 210 } 211 node_handlenode_handle212 node_handle() : allocator_ { default_allocator() } {} node_handlenode_handle213 node_handle(data_type* pointer, allocator& alloc) : pointer { pointer }, allocator_ { alloc } {} 214 node_handlenode_handle215 node_handle(node_handle&& other) noexcept 216 : pointer { exchange(other.pointer, nullptr) }, allocator_ { other.allocator_ } 217 {} 218 node_handle& operator=(node_handle&& other) noexcept 219 { 220 if (&other != this) { 221 if (pointer) { 222 if constexpr (!__is_trivially_destructible(value_type)) { 223 pointer->~data_type(); 224 } 225 allocator_.free(pointer); 226 } 227 pointer = exchange(other.pointer, nullptr); 228 allocator_ = other.allocator_; 229 } 230 return *this; 231 } 232 233 node_handle(const node_handle&) = delete; 234 node_handle& operator=(const node_handle&) = delete; 235 236 data_type* pointer { nullptr }; 237 238 private: 239 // Wrapper to create a "re-seatable" reference. 240 class Wrapper { 241 public: Wrappernode_handle242 inline Wrapper(allocator& a) : allocator_(&a) {} 243 244 inline Wrapper& operator=(allocator& a) 245 { 246 allocator_ = &a; 247 return *this; 248 } 249 allocnode_handle250 inline void* alloc(allocator::size_type size) 251 { 252 if ((allocator_) && (allocator_->alloc)) { 253 return allocator_->alloc(allocator_->instance, size); 254 } 255 return nullptr; 256 } 257 freenode_handle258 inline void free(void* ptr) 259 { 260 if ((allocator_) && (allocator_->free)) { 261 allocator_->free(allocator_->instance, ptr); 262 } 263 } 264 getnode_handle265 allocator& get() 266 { 267 BASE_ASSERT(allocator_ != nullptr); 268 return *allocator_; 269 } 270 getnode_handle271 const allocator& get() const 272 { 273 BASE_ASSERT(allocator_ != nullptr); 274 return *allocator_; 275 } 276 277 private: 278 allocator* allocator_ { nullptr }; 279 } allocator_; 280 }; 281 282 template<class Key, class T> 283 class unordered_map_base { 284 constexpr static uint32_t DEFAULT_SHIFT_AMOUNT = 4U; // 1<<4 (16) initial buckets, seems to match ms stl. get_sa(size_t const count,uint32_t const sa)285 constexpr uint32_t get_sa(size_t const count, uint32_t const sa) 286 { 287 uint32_t ret = sa; 288 while ((size_t(1) << ret) < count) { 289 ret++; 290 } 291 return ret; 292 } 293 294 public: 295 using key_type = Key; 296 using mapped_type = T; 297 using value_type = pair<const Key, T>; 298 using reference = value_type&; 299 using const_reference = const value_type&; 300 using pointer = value_type*; 301 using const_pointer = const value_type*; 302 using iterator = BASE_NS::unordered_map_iterator<unordered_map_base<Key, T>>; 303 using const_iterator = BASE_NS::const_unordered_map_iterator<unordered_map_base<Key, T>>; 304 struct list_node { 305 using value_type = pair<const Key, T>; 306 using first_type = typename value_type::first_type; 307 using second_type = typename value_type::second_type; 308 value_type data {}; 309 struct list_node* prev { nullptr }; 310 struct list_node* next { nullptr }; 311 list_nodelist_node312 list_node(const Key& x, const T& y) : data { x, y } {} list_nodelist_node313 list_node(Key&& x, const T& y) : data { BASE_NS::forward<Key>(x), y } {} list_nodelist_node314 list_node(Key&& x, T&& y) : data { BASE_NS::forward<Key>(x), BASE_NS::forward<T>(y) } {} list_nodelist_node315 list_node(const Key& x, T&& y) : data { x, BASE_NS::forward<T>(y) } {} list_nodelist_node316 explicit list_node(value_type&& val) : data { BASE_NS::forward<value_type>(val) } {} list_nodelist_node317 explicit list_node(const value_type& val) : data { val } {} 318 }; 319 using node_type = node_handle<list_node>; 320 321 unordered_map_base() = default; unordered_map_base(size_t bucket_count)322 explicit unordered_map_base(size_t bucket_count) 323 : shift_amount_ { get_sa(bucket_count, DEFAULT_SHIFT_AMOUNT) }, buckets_ { 1ull << shift_amount_ } 324 {} ~unordered_map_base()325 ~unordered_map_base() 326 { 327 clear(); 328 } 329 330 // The only way to get initializer_lists is to use std::initializer_list. 331 // Also initializer_lists are bad since they cause copies. please avoid them. unordered_map_base(std::initializer_list<value_type> init)332 unordered_map_base(std::initializer_list<value_type> init) 333 : shift_amount_ { get_sa(init.size(), DEFAULT_SHIFT_AMOUNT) }, buckets_ { 1ull << shift_amount_ } 334 { 335 for (auto&& value : init) { 336 insert(value); 337 } 338 } 339 340 unordered_map_base& operator=(std::initializer_list<T> init) 341 { 342 clear(); 343 reserve(init.size()); 344 for (auto&& value : init) { 345 insert(value); 346 } 347 return *this; 348 } 349 reserve(size_t count)350 void reserve(size_t count) 351 { 352 const uint32_t new_shift_amount = get_sa(count, DEFAULT_SHIFT_AMOUNT); 353 if (new_shift_amount > shift_amount_) { 354 shift_amount_ = new_shift_amount - 1; 355 rehash(); 356 } 357 } clear()358 void clear() 359 { 360 if (!empty()) { 361 for (auto t : buckets_) { 362 auto next = t; 363 for (; next != nullptr;) { 364 auto b = next; 365 next = b->next; 366 release(b); 367 } 368 } 369 buckets_.clear(); 370 buckets_.resize(1ull << shift_amount_); 371 size_ = 0; 372 } 373 } unordered_map_base(unordered_map_base && other)374 unordered_map_base(unordered_map_base&& other) noexcept 375 : size_ { BASE_NS::exchange(other.size_, 0U) }, 376 shift_amount_ { BASE_NS::exchange(other.shift_amount_, 0U) }, buckets_ { BASE_NS::move(other.buckets_) } 377 {} unordered_map_base(const unordered_map_base & other)378 unordered_map_base(const unordered_map_base& other) 379 : shift_amount_ { other.shift_amount_ }, buckets_ { 1ull << shift_amount_ } 380 { 381 // copy. 382 for (auto b : other.buckets_) { 383 list_node* last = nullptr; 384 if (b) { 385 auto ind = index(b->data.first); 386 for (; b != nullptr; b = b->next) { 387 auto nb = allocate(b->data); 388 if (last == nullptr) { 389 last = buckets_[ind] = nb; 390 } else { 391 nb->prev = last; 392 last = last->next = nb; 393 } 394 size_++; 395 } 396 } 397 } 398 } empty()399 bool empty() const 400 { 401 return size_ == 0; 402 } size()403 size_t size() const 404 { 405 return size_; 406 } erase(const_iterator pos)407 iterator erase(const_iterator pos) 408 { 409 if (pos.owner_ != this || !pos.it_) { 410 return end(); 411 } 412 list_node* node = nullptr; 413 const auto ind = index(pos.it_->data.first); 414 415 auto next = pos.it_->next; 416 // link prev to next or set bucket start to next if this was the first node. 417 // if this was the last prev-next will be null or bucket will be empty. 418 if (pos.it_->prev) { 419 BASE_ASSERT(pos.it_ == pos.it_->prev->next); 420 node = pos.it_->prev->next; 421 pos.it_->prev->next = next; 422 } else { 423 BASE_ASSERT(pos.it_ == buckets_[ind]); 424 node = buckets_[ind]; 425 buckets_[ind] = next; 426 } 427 428 // link next to prev or if this was the last node look or the next bucket with items. 429 // if this was the first next-prev will be null 430 if (next) { 431 next->prev = node->prev; 432 } else { 433 // okay, advance to next bucket.. 434 for (auto i = ind + 1; i < buckets_.size(); ++i) { 435 next = buckets_[i]; 436 if (next) { 437 break; 438 } 439 } 440 } 441 --size_; 442 release(node); 443 444 return iterator { *this, next }; 445 } erase(const key_type & key)446 size_t erase(const key_type& key) 447 { 448 if (auto entry = detach_entry(key)) { 449 release(entry); 450 return 1u; 451 } 452 return 0u; 453 } extract(const key_type & key)454 node_type extract(const key_type& key) 455 { 456 return node_type { detach_entry(key), buckets_.getAllocator() }; 457 } insert(value_type && v)458 pair<iterator, bool> insert(value_type&& v) 459 { 460 const auto ind = index(v.first); 461 auto entry = get_entry(ind, v.first); 462 if (entry) { 463 return { iterator { *this, entry }, false }; 464 } 465 return { iterator { *this, create_entry(ind, BASE_NS::forward<value_type>(v)) }, true }; 466 } insert(const value_type & v)467 pair<iterator, bool> insert(const value_type& v) 468 { 469 const auto ind = index(v.first); 470 auto entry = get_entry(ind, v.first); 471 if (entry) { 472 return { iterator { *this, entry }, false }; 473 } 474 return { iterator { *this, create_entry(ind, v.first, v.second) }, true }; 475 } insert(node_type && nh)476 auto insert(node_type&& nh) 477 { 478 struct insert_return_type { 479 iterator position; 480 bool inserted; 481 node_type node; 482 }; 483 if (nh.empty()) { 484 return insert_return_type { end(), false, BASE_NS::move(nh) }; 485 } 486 const auto& key = nh.pointer->data.first; 487 const auto ind = index(key); 488 auto res = get_entry(ind, key); 489 if (res) { 490 return insert_return_type { iterator { *this, res }, false, BASE_NS::move(nh) }; 491 } 492 auto nl = nh.pointer; 493 nh.pointer = nullptr; 494 return insert_return_type { iterator { *this, create_entry(ind, nl) }, true, BASE_NS::move(nh) }; 495 } 496 template<class M> insert_or_assign(const key_type & key,M && value)497 pair<iterator, bool> insert_or_assign(const key_type& key, M&& value) 498 { 499 const auto ind = index(key); 500 auto res = get_entry(ind, key); 501 if (res) { 502 res->data.second = BASE_NS::forward<M>(value); 503 return { iterator { *this, res }, false }; 504 } 505 auto nl = allocate(key, BASE_NS::forward<M>(value)); 506 return { iterator { *this, create_entry(ind, nl) }, true }; 507 } begin()508 iterator begin() noexcept 509 { 510 for (auto t : buckets_) { 511 if (t) { 512 return iterator { *this, t }; 513 } 514 } 515 return end(); 516 } begin()517 const_iterator begin() const noexcept 518 { 519 for (auto t : buckets_) { 520 if (t) { 521 return const_iterator { *this, t }; 522 } 523 } 524 return end(); 525 } cbegin()526 const_iterator cbegin() const noexcept 527 { 528 return begin(); 529 } 530 end()531 iterator end() noexcept 532 { 533 return iterator { *this, nullptr }; 534 } end()535 const_iterator end() const noexcept 536 { 537 return const_iterator { *this, nullptr }; 538 } cend()539 const_iterator cend() const noexcept 540 { 541 return end(); 542 } find(const key_type & key)543 iterator find(const key_type& key) 544 { 545 return iterator { *this, get_entry(index(key), key) }; 546 } find(const key_type & key)547 const_iterator find(const key_type& key) const 548 { 549 return const_iterator { *this, get_entry(index(key), key) }; 550 } contains(const key_type & key)551 bool contains(const key_type& key) const 552 { 553 return (get_entry(index(key), key) != nullptr); 554 } count(const key_type & key)555 size_t count(const key_type& key) const 556 { 557 return contains(key) ? 1U : 0U; 558 } 559 mapped_type& operator[](const key_type& key) 560 { 561 const auto ind = index(key); 562 auto b = get_entry(ind, key); 563 if (b) { 564 return b->data.second; 565 } 566 return create_entry(ind, key)->data.second; 567 } 568 mapped_type& operator[](key_type&& key) 569 { 570 const auto ind = index(key); 571 auto b = get_entry(ind, key); 572 if (b) { 573 return b->data.second; 574 } 575 return create_entry(ind, BASE_NS::forward<key_type>(key))->data.second; 576 } 577 578 unordered_map_base& operator=(unordered_map_base&& other) noexcept 579 { 580 if (&other != this) { 581 // move.. 582 clear(); 583 size_ = BASE_NS::exchange(other.size_, 0U); 584 shift_amount_ = BASE_NS::exchange(other.shift_amount_, 0U); 585 buckets_ = BASE_NS::move(other.buckets_); 586 } 587 return *this; 588 } 589 unordered_map_base& operator=(const unordered_map_base& other) 590 { 591 if (&other != this) { 592 // copy. 593 clear(); 594 shift_amount_ = other.shift_amount_; 595 buckets_.resize(1ull << shift_amount_); 596 for (auto* b : other.buckets_) { 597 if (!b) { 598 continue; 599 } 600 list_node* last = nullptr; 601 const uint32_t ind = index(b->data.first); 602 for (; b != nullptr; b = b->next) { 603 auto* nb = allocate(b->data); 604 if (last == nullptr) { 605 last = buckets_[ind] = nb; 606 } else { 607 nb->prev = last; 608 last = last->next = nb; 609 } 610 size_++; 611 } 612 } 613 } 614 return *this; 615 } 616 617 protected: 618 friend iterator; 619 friend const_iterator; 620 621 // helpers for the iterators. (perhaps link the nodes across buckets?) advance(const const_iterator & it)622 list_node* advance(const const_iterator& it) const 623 { 624 list_node* next = nullptr; 625 if (it.it_->next) { 626 next = it.it_->next; 627 } else { 628 // okay, advance to next bucket.. 629 for (uint32_t ind = index(it.it_->data.first) + 1U; (ind < buckets_.size()) && (!next); ++ind) { 630 next = buckets_[ind]; 631 } 632 } 633 return next; 634 } 635 create_entry(uint32_t ind,const key_type & key)636 list_node* create_entry(uint32_t ind, const key_type& key) 637 { 638 return create_entry(ind, allocate(key, mapped_type {})); 639 } create_entry(uint32_t ind,key_type && key)640 list_node* create_entry(uint32_t ind, key_type&& key) 641 { 642 return create_entry(ind, allocate(BASE_NS::forward<key_type>(key), mapped_type {})); 643 } create_entry(uint32_t ind,const key_type & key,const mapped_type & value)644 list_node* create_entry(uint32_t ind, const key_type& key, const mapped_type& value) 645 { 646 return create_entry(ind, allocate(key, value)); 647 } create_entry(uint32_t ind,key_type && key,mapped_type && value)648 list_node* create_entry(uint32_t ind, key_type&& key, mapped_type&& value) 649 { 650 return create_entry(ind, allocate(BASE_NS::forward<key_type>(key), BASE_NS::forward<mapped_type>(value))); 651 } create_entry(uint32_t ind,value_type && v)652 list_node* create_entry(uint32_t ind, value_type&& v) 653 { 654 return create_entry(ind, allocate(BASE_NS::forward<value_type>(v))); 655 } 656 657 template<class... Args> allocate(Args &&...args)658 list_node* allocate(Args&&... args) 659 { 660 auto alloc = buckets_.getAllocator(); 661 auto node = static_cast<list_node*>(alloc.alloc(alloc.instance, sizeof(list_node))); 662 ::new (node) list_node { BASE_NS::forward<Args>(args)... }; 663 return node; 664 } 665 release(list_node * node)666 void release(list_node* node) 667 { 668 if constexpr (!__is_trivially_destructible(list_node)) { 669 node->~list_node(); 670 } 671 auto alloc = buckets_.getAllocator(); 672 alloc.free(alloc.instance, node); 673 } 674 create_entry(uint32_t ind,list_node * nh)675 list_node* create_entry(uint32_t ind, list_node* nh) 676 { 677 // check load level.. and resize/rehash if needed 678 bool resize = false; 679 if (buckets_.empty()) { 680 resize = true; 681 } else { 682 const float load = size_ / static_cast<float>(buckets_.size()); 683 resize = (load > 0.7f); 684 } 685 if (resize) { 686 rehash(); 687 // need to recalculate/rehash the index 688 ind = index(nh->data.first); 689 } 690 // okay. so add it as first.. 691 auto old = buckets_[ind]; 692 buckets_[ind] = nh; 693 buckets_[ind]->next = old; 694 if (old) { 695 old->prev = buckets_[ind]; 696 } 697 size_++; 698 return buckets_[ind]; 699 } 700 template<class k> get_entry(uint32_t index,const k & key)701 list_node* get_entry(uint32_t index, const k& key) const 702 { 703 if (index >= buckets_.size()) { 704 // invalid! 705 return nullptr; 706 } 707 auto b = buckets_[index]; 708 while (b) { 709 if (b->data.first == key) { 710 return b; 711 } 712 b = b->next; 713 } 714 // invalid! 715 return nullptr; 716 } 717 // maps key to bucket index. 718 // first uses the hash function to convert key to 64 bit hash. 719 // and then uses "fibonacci hashing"/"Knuth’s multiplicative method" 720 // with additional magic to fix high bits 721 // and to map the hash to 0 - buckets_.size() range. 722 // see: 723 // https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/ 724 template<class k> index(const k & key)725 uint32_t index(const k& key) const 726 { 727 if (shift_amount_) { 728 const uint64_t GOLDEN_RATIO_64 = 0x9E3779B97F4A7C15ull; 729 const uint64_t shift = 64u - shift_amount_; 730 uint64_t h = hash(key); 731 h ^= h >> shift; 732 return static_cast<uint32_t>(((GOLDEN_RATIO_64 * h) >> shift)); 733 } 734 return 0u; 735 } 736 737 template<class k> detach_entry(const k & key)738 list_node* detach_entry(const k& key) 739 { 740 const auto ind = index(key); 741 auto entry = get_entry(ind, key); 742 if (entry) { 743 if (entry->prev) { 744 entry->prev->next = entry->next; 745 } else { 746 buckets_[ind] = entry->next; 747 } 748 if (entry->next) { 749 entry->next->prev = entry->prev; 750 } 751 entry->prev = nullptr; 752 entry->next = nullptr; 753 --size_; 754 } 755 return entry; 756 } 757 rehash()758 void rehash() 759 { 760 BASE_NS::vector<list_node*> tmp(BASE_NS::move(buckets_)); 761 shift_amount_++; 762 if (shift_amount_ < DEFAULT_SHIFT_AMOUNT) { 763 // make sure that we always have atleast 1<<DEFAULT_SHIFT_AMOUNT buckets. 764 shift_amount_ = DEFAULT_SHIFT_AMOUNT; 765 } 766 buckets_.resize(1ull << shift_amount_); 767 for (auto b : tmp) { 768 for (; b != nullptr;) { 769 auto next = b->next; 770 const uint32_t ind = index(b->data.first); 771 b->next = buckets_[ind]; 772 buckets_[ind] = b; 773 if (buckets_[ind]->next) { 774 buckets_[ind]->next->prev = buckets_[ind]; 775 } 776 b->prev = nullptr; 777 b = next; 778 } 779 } 780 } 781 782 // linked list in buckets_... 783 size_t size_ { 0 }; 784 uint32_t shift_amount_ { 0 }; 785 BASE_NS::vector<list_node*> buckets_; make_iterator(list_node * node)786 iterator make_iterator(list_node* node) 787 { 788 return iterator { *this, node }; 789 } make_const_iterator(const list_node * node)790 const_iterator make_const_iterator(const list_node* node) const 791 { 792 return const_iterator { *this, node }; 793 } 794 }; 795 796 template<class Key, class T> 797 class unordered_map : public unordered_map_base<Key, T> { 798 public: 799 using unordered_map_base<Key, T>::unordered_map_base; 800 }; 801 802 template<class T> 803 class unordered_map<BASE_NS::string, T> : public unordered_map_base<BASE_NS::string, T> { 804 // Specialization of unordered_map with string key but 805 // accepts string_view as key in certain basic operations.. 806 using base = unordered_map_base<BASE_NS::string, T>; 807 808 public: 809 using unordered_map_base<BASE_NS::string, T>::unordered_map_base; find(const string_view & key)810 auto find(const string_view& key) 811 { 812 return base::make_iterator(base::get_entry(base::index(key), key)); 813 } find(const string_view & key)814 auto find(const string_view& key) const 815 { 816 return base::make_const_iterator(base::get_entry(base::index(key), key)); 817 } contains(const string_view & key)818 bool contains(const string_view& key) const 819 { 820 return (base::get_entry(base::index(key), key) != nullptr); 821 } count(const string_view & key)822 size_t count(const string_view& key) const 823 { 824 return contains(key) ? 1U : 0U; 825 } 826 827 // expose string overloads as well 828 using base::operator[]; 829 using base::erase; 830 831 auto& operator[](const char* const key) 832 { 833 return operator[](string_view(key)); 834 } 835 auto& operator[](const string_view& key) 836 { 837 const auto ind = base::index(key); 838 if (auto b = base::get_entry(ind, key)) { 839 return b->data.second; 840 } 841 return base::create_entry(ind, typename base::key_type(key))->data.second; 842 } 843 erase(const string_view & key)844 size_t erase(const string_view& key) 845 { 846 if (auto* entry = base::detach_entry(key)) { 847 base::release(entry); 848 return 1u; 849 } 850 return 0u; 851 } 852 insert(pair<string_view,T> && v)853 pair<typename base::iterator, bool> insert(pair<string_view, T>&& v) 854 { 855 const auto ind = base::index(v.first); 856 auto entry = base::get_entry(ind, v.first); 857 if (entry) { 858 return { base::make_iterator(entry), false }; 859 } 860 entry = base::create_entry( 861 ind, typename base::key_type(v.first), BASE_NS::forward<typename base::mapped_type>(v.second)); 862 return { base::make_iterator(entry), true }; 863 } 864 insert(const pair<string_view,T> & v)865 pair<typename base::iterator, bool> insert(const pair<string_view, T>& v) 866 { 867 const auto ind = base::index(v.first); 868 auto entry = base::get_entry(ind, v.first); 869 if (entry) { 870 return { base::make_iterator(entry), false }; 871 } 872 entry = base::create_entry(ind, typename base::key_type(v.first), v.second); 873 return { base::make_iterator(entry), true }; 874 } 875 876 template<class M> insert_or_assign(const string_view & key,M && value)877 pair<typename base::iterator, bool> insert_or_assign(const string_view& key, M&& value) 878 { 879 const auto ind = base::index(key); 880 auto entry = base::get_entry(ind, key); 881 if (entry) { 882 entry->data.second = BASE_NS::forward<M>(value); 883 return { base::make_iterator(entry), false }; 884 } 885 entry = base::create_entry(ind, typename base::key_type(key), BASE_NS::forward<M>(value)); 886 return { base::make_iterator(entry), true }; 887 } 888 extract(const string_view & key)889 auto extract(const string_view& key) 890 { 891 return typename base::node_type { base::detach_entry(key), base::buckets_.getAllocator() }; 892 } 893 }; 894 BASE_END_NAMESPACE() 895 896 #endif // API_BASE_CONTAINERS_UNORDERED_MAP_H 897