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