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