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