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