• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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