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; emptynode_handle187 [[nodiscard]] bool empty() const noexcept 188 { 189 return pointer == nullptr; 190 } 191 explicit operator bool() const noexcept 192 { 193 return !empty(); 194 } keynode_handle195 key_type& key() const 196 { 197 return pointer->data.first; 198 } mappednode_handle199 mapped_type& mapped() const 200 { 201 return pointer->data.second; 202 } 203 data_type* pointer { nullptr }; 204 }; 205 206 template<class Key, class T> 207 class unordered_map_base { 208 constexpr static uint32_t DEFAULT_SHIFT_AMOUNT = 4; // 1<<4 (16) initial buckets, seems to match ms stl. get_sa(size_t const count,uint32_t const sa)209 constexpr uint32_t get_sa(size_t const count, uint32_t const sa) 210 { 211 uint32_t ret = sa; 212 while ((size_t(1) << ret) < count) { 213 ret++; 214 } 215 return ret; 216 } 217 218 public: 219 using key_type = Key; 220 using mapped_type = T; 221 using value_type = pair<const Key, T>; 222 using reference = value_type&; 223 using const_reference = const value_type&; 224 using pointer = value_type*; 225 using const_pointer = const value_type*; 226 using iterator = BASE_NS::unordered_map_iterator<unordered_map_base<Key, T>>; 227 using const_iterator = BASE_NS::const_unordered_map_iterator<unordered_map_base<Key, T>>; 228 struct list_node { 229 using value_type = pair<const Key, T>; 230 using first_type = typename value_type::first_type; 231 using second_type = typename value_type::second_type; 232 value_type data {}; 233 struct list_node* prev { nullptr }; 234 struct list_node* next { nullptr }; 235 list_nodelist_node236 list_node(const Key& x, const T& y) : data { x, y } {} list_nodelist_node237 list_node(Key&& x, const T& y) : data { BASE_NS::forward<Key>(x), y } {} list_nodelist_node238 list_node(Key&& x, T&& y) : data { BASE_NS::forward<Key>(x), BASE_NS::forward<T>(y) } {} list_nodelist_node239 list_node(const Key& x, T&& y) : data { x, BASE_NS::forward<T>(y) } {} list_nodelist_node240 explicit list_node(value_type&& val) : data { BASE_NS::forward<value_type>(val) } {} list_nodelist_node241 explicit list_node(const value_type& val) : data { val } {} 242 }; 243 using node_type = node_handle<list_node>; 244 245 unordered_map_base() = default; unordered_map_base(size_t bucket_count)246 explicit unordered_map_base(size_t bucket_count) 247 : shift_amount_ { get_sa(bucket_count, DEFAULT_SHIFT_AMOUNT) }, buckets_ { 1ull << shift_amount_ } 248 {} ~unordered_map_base()249 ~unordered_map_base() 250 { 251 clear(); 252 } reserve(size_t count)253 void reserve(size_t count) 254 { 255 const uint32_t new_shift_amount = get_sa(count, DEFAULT_SHIFT_AMOUNT); 256 if (new_shift_amount > shift_amount_) { 257 shift_amount_ = new_shift_amount - 1; 258 rehash(); 259 } 260 } clear()261 void clear() 262 { 263 if (!empty()) { 264 for (auto t : buckets_) { 265 auto next = t; 266 for (; next != nullptr;) { 267 auto b = next; 268 next = b->next; 269 release(b); 270 } 271 } 272 buckets_.clear(); 273 buckets_.resize(1ull << shift_amount_); 274 size_ = 0; 275 } 276 } unordered_map_base(unordered_map_base && other)277 unordered_map_base(unordered_map_base&& other) noexcept 278 : size_ { other.size_ }, shift_amount_ { other.shift_amount_ }, buckets_ { BASE_NS::move(other.buckets_) } 279 { 280 // move.. 281 other.size_ = 0; 282 other.shift_amount_ = 0; 283 } unordered_map_base(const unordered_map_base & other)284 unordered_map_base(const unordered_map_base& other) 285 : shift_amount_ { other.shift_amount_ }, buckets_ { 1ull << shift_amount_ } 286 { 287 // copy. 288 for (auto b : other.buckets_) { 289 list_node* last = nullptr; 290 if (b) { 291 auto ind = index(b->data.first); 292 for (; b != nullptr; b = b->next) { 293 auto nb = allocate(b->data); 294 if (last == nullptr) { 295 last = buckets_[ind] = nb; 296 } else { 297 nb->prev = last; 298 last = last->next = nb; 299 } 300 size_++; 301 } 302 } 303 } 304 } empty()305 bool empty() const 306 { 307 return size_ == 0; 308 } size()309 size_t size() const 310 { 311 return size_; 312 } erase(const_iterator pos)313 iterator erase(const_iterator pos) 314 { 315 if (pos.owner_ != this || !pos.it_) { 316 return end(); 317 } 318 list_node* node = nullptr; 319 const auto ind = index(pos.it_->data.first); 320 321 auto next = pos.it_->next; 322 // link prev to next or set bucket start to next if this was the first node. 323 // if this was the last prev-next will be null or bucket will be empty. 324 if (pos.it_->prev) { 325 BASE_ASSERT(pos.it_ == pos.it_->prev->next); 326 node = pos.it_->prev->next; 327 pos.it_->prev->next = next; 328 } else { 329 BASE_ASSERT(pos.it_ == buckets_[ind]); 330 node = buckets_[ind]; 331 buckets_[ind] = next; 332 } 333 334 // link next to prev or if this was the last node look or the next bucket with items. 335 // if this was the first next-prev will be null 336 if (next) { 337 next->prev = node->prev; 338 } else { 339 // okay, advance to next bucket.. 340 for (auto i = ind + 1; i < buckets_.size(); ++i) { 341 next = buckets_[i]; 342 if (next) { 343 break; 344 } 345 } 346 } 347 --size_; 348 release(node); 349 350 return iterator { *this, next }; 351 } erase(const key_type & key)352 size_t erase(const key_type& key) 353 { 354 if (auto entry = detach_entry(key)) { 355 release(entry); 356 return 1u; 357 } 358 return 0u; 359 } extract(const key_type & key)360 node_type extract(const key_type& key) 361 { 362 return node_type { detach_entry(key) }; 363 } insert(value_type && v)364 pair<iterator, bool> insert(value_type&& v) 365 { 366 const auto ind = index(v.first); 367 auto entry = get_entry(ind, v.first); 368 if (entry) { 369 return { iterator { *this, entry }, false }; 370 } 371 return { iterator { *this, create_entry(ind, BASE_NS::forward<value_type>(v)) }, true }; 372 } insert(const value_type & v)373 pair<iterator, bool> insert(const value_type& v) 374 { 375 const auto ind = index(v.first); 376 auto entry = get_entry(ind, v.first); 377 if (entry) { 378 return { iterator { *this, entry }, false }; 379 } 380 return { iterator { *this, create_entry(ind, v.first, v.second) }, true }; 381 } insert(node_type && nh)382 auto insert(node_type&& nh) 383 { 384 struct insert_return_type { 385 iterator position; 386 bool inserted; 387 node_type node; 388 }; 389 if (nh.empty()) { 390 return insert_return_type { end(), false }; 391 } 392 const auto& key = nh.pointer->data.first; 393 const auto ind = index(key); 394 auto res = get_entry(ind, key); 395 if (res) { 396 return insert_return_type { iterator { *this, res }, false, nh }; 397 } 398 auto nl = nh.pointer; 399 nh.pointer = nullptr; 400 return insert_return_type { iterator { *this, create_entry(ind, nl) }, true }; 401 } 402 template<class M> insert_or_assign(const key_type & key,M && value)403 pair<iterator, bool> insert_or_assign(const key_type& key, M&& value) 404 { 405 const auto ind = index(key); 406 auto res = get_entry(ind, key); 407 if (res) { 408 res->data.second = BASE_NS::forward<M>(value); 409 return { iterator { *this, res }, false }; 410 } 411 auto nl = allocate(key, BASE_NS::forward<M>(value)); 412 return { iterator { *this, create_entry(ind, nl) }, true }; 413 } begin()414 iterator begin() noexcept 415 { 416 for (auto t : buckets_) { 417 if (t) { 418 return iterator { *this, t }; 419 } 420 } 421 return end(); 422 } begin()423 const_iterator begin() const noexcept 424 { 425 for (auto t : buckets_) { 426 if (t) { 427 return const_iterator { *this, t }; 428 } 429 } 430 return end(); 431 } cbegin()432 const_iterator cbegin() const noexcept 433 { 434 return begin(); 435 } 436 end()437 iterator end() noexcept 438 { 439 return iterator { *this, nullptr }; 440 } end()441 const_iterator end() const noexcept 442 { 443 return const_iterator { *this, nullptr }; 444 } cend()445 const_iterator cend() const noexcept 446 { 447 return end(); 448 } find(const key_type & key)449 iterator find(const key_type& key) 450 { 451 return iterator { *this, get_entry(index(key), key) }; 452 } find(const key_type & key)453 const_iterator find(const key_type& key) const 454 { 455 return const_iterator { *this, get_entry(index(key), key) }; 456 } contains(const key_type & key)457 bool contains(const key_type& key) const 458 { 459 return (get_entry(index(key), key) != nullptr); 460 } count(const key_type & key)461 size_t count(const key_type& key) const 462 { 463 return contains(key) ? 1U : 0U; 464 } 465 mapped_type& operator[](const key_type& key) 466 { 467 const auto ind = index(key); 468 auto b = get_entry(ind, key); 469 if (b) { 470 return b->data.second; 471 } 472 return create_entry(ind, key)->data.second; 473 } 474 mapped_type& operator[](key_type&& key) 475 { 476 const auto ind = index(key); 477 auto b = get_entry(ind, key); 478 if (b) { 479 return b->data.second; 480 } 481 return create_entry(ind, BASE_NS::forward<key_type>(key))->data.second; 482 } 483 484 unordered_map_base& operator=(unordered_map_base&& other) noexcept 485 { 486 if (&other != this) { 487 // move.. 488 clear(); 489 size_ = other.size_; 490 other.size_ = 0; 491 shift_amount_ = other.shift_amount_; 492 buckets_ = BASE_NS::move(other.buckets_); 493 } 494 return *this; 495 } 496 unordered_map_base& operator=(const unordered_map_base& other) 497 { 498 if (&other != this) { 499 // copy. 500 clear(); 501 shift_amount_ = other.shift_amount_; 502 buckets_.resize(1ull << shift_amount_); 503 for (auto b : other.buckets_) { 504 list_node* last = nullptr; 505 if (b) { 506 uint32_t ind = index(b->data.first); 507 for (; b != nullptr; b = b->next) { 508 auto nb = allocate(b->data); 509 if (last == nullptr) { 510 last = buckets_[ind] = nb; 511 } else { 512 nb->prev = last; 513 last = last->next = nb; 514 } 515 size_++; 516 } 517 } 518 } 519 } 520 return *this; 521 } 522 523 protected: 524 friend iterator; 525 friend const_iterator; 526 527 // helpers for the iterators. (perhaps link the nodes across buckets?) 528 list_node* advance(const const_iterator& it, uint32_t count = 1) const 529 { 530 list_node* next = nullptr; 531 while (count--) { 532 if (it.it_->next) { 533 next = it.it_->next; 534 } else { 535 // okay, advance to next bucket.. 536 uint32_t ind = index(it.it_->data.first); 537 for (;;) { 538 ind++; 539 if (ind == buckets_.size()) { 540 next = nullptr; 541 break; 542 } 543 next = buckets_[ind]; 544 if (next) { 545 break; 546 } 547 } 548 } 549 } 550 return next; 551 } 552 create_entry(uint32_t ind,const key_type & key)553 list_node* create_entry(uint32_t ind, const key_type& key) 554 { 555 return create_entry(ind, allocate(key, mapped_type {})); 556 } create_entry(uint32_t ind,key_type && key)557 list_node* create_entry(uint32_t ind, key_type&& key) 558 { 559 return create_entry(ind, allocate(BASE_NS::forward<key_type>(key), mapped_type {})); 560 } create_entry(uint32_t ind,const key_type & key,const mapped_type & value)561 list_node* create_entry(uint32_t ind, const key_type& key, const mapped_type& value) 562 { 563 return create_entry(ind, allocate(key, value)); 564 } create_entry(uint32_t ind,key_type && key,mapped_type && value)565 list_node* create_entry(uint32_t ind, key_type&& key, mapped_type&& value) 566 { 567 return create_entry(ind, allocate(BASE_NS::forward<key_type>(key), BASE_NS::forward<mapped_type>(value))); 568 } create_entry(uint32_t ind,value_type && v)569 list_node* create_entry(uint32_t ind, value_type&& v) 570 { 571 return create_entry(ind, allocate(BASE_NS::forward<value_type>(v))); 572 } 573 574 template<class... Args> allocate(Args &&...args)575 list_node* allocate(Args&&... args) 576 { 577 auto alloc = buckets_.getAllocator(); 578 auto node = static_cast<list_node*>(alloc.alloc(alloc.instance, sizeof(list_node))); 579 ::new (node) list_node { BASE_NS::forward<Args>(args)... }; 580 return node; 581 } 582 release(list_node * node)583 void release(list_node* node) 584 { 585 if constexpr (!__is_trivially_destructible(list_node)) { 586 node->~list_node(); 587 } 588 auto alloc = buckets_.getAllocator(); 589 alloc.free(alloc.instance, node); 590 } 591 create_entry(uint32_t ind,list_node * nh)592 list_node* create_entry(uint32_t ind, list_node* nh) 593 { 594 // check load level.. and resize/rehash if needed 595 bool resize = false; 596 if (buckets_.empty()) { 597 resize = true; 598 } else { 599 const float load = size_ / static_cast<float>(buckets_.size()); 600 resize = (load > 0.7f); 601 } 602 if (resize) { 603 rehash(); 604 // need to recalculate/rehash the index 605 ind = index(nh->data.first); 606 } 607 // okay. so add it as first.. 608 auto old = buckets_[ind]; 609 buckets_[ind] = nh; 610 buckets_[ind]->next = old; 611 if (old) { 612 old->prev = buckets_[ind]; 613 } 614 size_++; 615 return buckets_[ind]; 616 } 617 template<class k> get_entry(uint32_t index,const k & key)618 list_node* get_entry(uint32_t index, const k& key) const 619 { 620 if (index >= buckets_.size()) { 621 // invalid! 622 return nullptr; 623 } 624 auto b = buckets_[index]; 625 while (b) { 626 if (b->data.first == key) { 627 return b; 628 } 629 b = b->next; 630 } 631 // invalid! 632 return nullptr; 633 } 634 // maps key to bucket index. 635 // first uses the hash function to convert key to 64 bit hash. 636 // and then uses "fibonacci hashing"/"Knuth’s multiplicative method" 637 // with additional magic to fix high bits 638 // and to map the hash to 0 - buckets_.size() range. 639 // see: 640 // https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/ 641 template<class k> index(const k & key)642 uint32_t index(const k& key) const 643 { 644 if (shift_amount_) { 645 const uint64_t GOLDEN_RATIO_64 = 0x9E3779B97F4A7C15ull; 646 const uint64_t shift = 64u - shift_amount_; 647 uint64_t h = hash(key); 648 h ^= h >> shift; 649 return static_cast<uint32_t>(((GOLDEN_RATIO_64 * h) >> shift)); 650 } 651 return 0u; 652 } 653 template<class k> detach_entry(const k & key)654 list_node* detach_entry(const k& key) 655 { 656 const auto ind = index(key); 657 auto entry = get_entry(ind, key); 658 if (entry) { 659 if (entry->prev) { 660 entry->prev->next = entry->next; 661 } else { 662 buckets_[ind] = entry->next; 663 } 664 if (entry->next) { 665 entry->next->prev = entry->prev; 666 } 667 entry->prev = nullptr; 668 entry->next = nullptr; 669 --size_; 670 } 671 return entry; 672 } rehash()673 void rehash() 674 { 675 BASE_NS::vector<list_node*> tmp(BASE_NS::move(buckets_)); 676 shift_amount_++; 677 if (shift_amount_ < DEFAULT_SHIFT_AMOUNT) { 678 // make sure that we always have atleast 1<<DEFAULT_SHIFT_AMOUNT buckets. 679 shift_amount_ = DEFAULT_SHIFT_AMOUNT; 680 } 681 buckets_.resize(1ull << shift_amount_); 682 for (auto b : tmp) { 683 for (; b != nullptr;) { 684 auto next = b->next; 685 const uint32_t ind = index(b->data.first); 686 b->next = buckets_[ind]; 687 buckets_[ind] = b; 688 if (buckets_[ind]->next) { 689 buckets_[ind]->next->prev = buckets_[ind]; 690 } 691 b->prev = nullptr; 692 b = next; 693 } 694 } 695 } 696 697 // linked list in buckets_... 698 size_t size_ { 0 }; 699 uint32_t shift_amount_ { 0 }; 700 BASE_NS::vector<list_node*> buckets_; make_iterator(list_node * node)701 iterator make_iterator(list_node* node) 702 { 703 return iterator { *this, node }; 704 } make_const_iterator(const list_node * node)705 const_iterator make_const_iterator(const list_node* node) const 706 { 707 return const_iterator { *this, node }; 708 } 709 }; 710 711 template<class Key, class T> 712 class unordered_map : public unordered_map_base<Key, T> { 713 public: 714 using unordered_map_base<Key, T>::unordered_map_base; 715 }; 716 717 template<class T> 718 class unordered_map<BASE_NS::string, T> : public unordered_map_base<BASE_NS::string, T> { 719 // Specialization of unordered_map with string key but 720 // accepts string_view as key in certain basic operations.. 721 using base = unordered_map_base<BASE_NS::string, T>; 722 723 public: 724 using unordered_map_base<BASE_NS::string, T>::unordered_map_base; find(const string_view & key)725 auto find(const string_view& key) 726 { 727 return base::make_iterator(base::get_entry(base::index(key), key)); 728 } find(const string_view & key)729 auto find(const string_view& key) const 730 { 731 return base::make_const_iterator(base::get_entry(base::index(key), key)); 732 } contains(const string_view & key)733 bool contains(const string_view& key) const 734 { 735 return (base::get_entry(base::index(key), key) != nullptr); 736 } count(const string_view & key)737 size_t count(const string_view& key) const 738 { 739 return contains(key) ? 1U : 0U; 740 } 741 742 // expose string overloads as well 743 using base::operator[]; 744 using base::erase; 745 746 auto& operator[](const string_view& key) 747 { 748 const auto ind = base::index(key); 749 if (auto b = base::get_entry(ind, key)) { 750 return b->data.second; 751 } 752 return base::create_entry(ind, typename base::key_type(key))->data.second; 753 } 754 erase(const string_view & key)755 size_t erase(const string_view& key) 756 { 757 if (auto* entry = base::detach_entry(key)) { 758 base::release(entry); 759 return 1u; 760 } 761 return 0u; 762 } 763 insert(pair<string_view,T> && v)764 pair<typename base::iterator, bool> insert(pair<string_view, T>&& v) 765 { 766 const auto ind = base::index(v.first); 767 auto entry = base::get_entry(ind, v.first); 768 if (entry) { 769 return { base::make_iterator(entry), false }; 770 } 771 entry = base::create_entry( 772 ind, typename base::key_type(v.first), BASE_NS::forward<typename base::mapped_type>(v.second)); 773 return { base::make_iterator(entry), true }; 774 } 775 insert(const pair<string_view,T> & v)776 pair<typename base::iterator, bool> insert(const pair<string_view, T>& v) 777 { 778 const auto ind = base::index(v.first); 779 auto entry = base::get_entry(ind, v.first); 780 if (entry) { 781 return { base::make_iterator(entry), false }; 782 } 783 entry = base::create_entry(ind, typename base::key_type(v.first), v.second); 784 return { base::make_iterator(entry), true }; 785 } 786 787 template<class M> insert_or_assign(const string_view & key,M && value)788 pair<typename base::iterator, bool> insert_or_assign(const string_view& key, M&& value) 789 { 790 const auto ind = base::index(key); 791 auto entry = base::get_entry(ind, key); 792 if (entry) { 793 entry->data.second = BASE_NS::forward<M>(value); 794 return { base::make_iterator(entry), false }; 795 } 796 entry = base::create_entry(ind, typename base::key_type(key), BASE_NS::forward<M>(value)); 797 return { base::make_iterator(entry), true }; 798 } 799 }; 800 BASE_END_NAMESPACE() 801 802 #endif // API_BASE_CONTAINERS_UNORDERED_MAP_H 803