1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <functional> 20 #include <iterator> 21 #include <list> 22 #include <mutex> 23 #include <optional> 24 #include <thread> 25 #include <type_traits> 26 #include <unordered_map> 27 28 namespace bluetooth { 29 namespace common { 30 31 // A map that maintains order of its element as a list. An element that is put earlier will appear before an element 32 // that is put later when iterating through this map's entries. Keys must be unique. 33 // 34 // Performance: 35 // - Key look-up and modification is O(1) 36 // - Value operated by replacement, no in-place modification 37 // - Memory consumption is: 38 // O(2*capacity*sizeof(K) + capacity*(sizeof(nullptr)+sizeof(V))) 39 // - NOT THREAD SAFE 40 // 41 // Template: 42 // - Key key 43 // - T value 44 template <typename Key, typename T> 45 class ListMap { 46 public: 47 using value_type = std::pair<const Key, T>; 48 // different from c++17 node_type on purpose as we want node to be copyable 49 using node_type = std::pair<Key, T>; 50 using iterator = typename std::list<value_type>::iterator; 51 using const_iterator = typename std::list<value_type>::const_iterator; 52 53 // Constructor of the list map 54 ListMap() = default; 55 56 // for move 57 ListMap(ListMap&& other) noexcept = default; 58 ListMap& operator=(ListMap&& other) noexcept = default; 59 60 // copy-constructor 61 // iterators in key_map_ cannot be copied directly ListMap(const ListMap & other)62 ListMap(const ListMap& other) : node_list_(other.node_list_) { 63 for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) { 64 key_map_.emplace(iter->first, iter); 65 } 66 } 67 68 // copy-assignment 69 // iterators in key_map_ cannot be copied directly 70 ListMap& operator=(const ListMap& other) { 71 if (&other == this) { 72 return *this; 73 } 74 node_list_ = other.node_list_; 75 key_map_.clear(); 76 for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) { 77 key_map_.emplace(iter->first, iter); 78 } 79 return *this; 80 } 81 82 // comparison operators 83 bool operator==(const ListMap& rhs) const { 84 return node_list_ == rhs.node_list_; 85 } 86 bool operator!=(const ListMap& rhs) const { 87 return !(*this == rhs); 88 } 89 ~ListMap()90 ~ListMap() { 91 clear(); 92 } 93 94 // Clear the list map clear()95 void clear() { 96 key_map_.clear(); 97 node_list_.clear(); 98 } 99 100 // const version of find() find(const Key & key)101 const_iterator find(const Key& key) const { 102 return const_cast<ListMap*>(this)->find(key); 103 } 104 105 // Get the value of a key. Return iterator to the item if found, end() if not found find(const Key & key)106 iterator find(const Key& key) { 107 auto map_iterator = key_map_.find(key); 108 if (map_iterator == key_map_.end()) { 109 return end(); 110 } 111 return map_iterator->second; 112 } 113 114 // Check if key exist in the map. Return true if key exist in map, false if not. contains(const Key & key)115 bool contains(const Key& key) const { 116 return find(key) != end(); 117 } 118 119 // Try emplace an element before a specific position |pos| of the list map. If the |key| already exists, does nothing. 120 // Moved arguments won't be moved when key already exists. Return <iterator, true> when key does not exist, <iterator, 121 // false> when key exist and iterator is the position where it was placed. 122 template <class... Args> try_emplace(const_iterator pos,const Key & key,Args &&...args)123 std::pair<iterator, bool> try_emplace(const_iterator pos, const Key& key, Args&&... args) { 124 auto map_iterator = key_map_.find(key); 125 if (map_iterator != key_map_.end()) { 126 return std::make_pair(end(), false); 127 } 128 auto list_iterator = node_list_.emplace(pos, key, std::forward<Args>(args)...); 129 key_map_.emplace(key, list_iterator); 130 return std::make_pair(list_iterator, true); 131 } 132 133 // Try emplace an element before the end of the list map. If the key already exists, does nothing. Moved arguments 134 // won't be moved when key already exists return <iterator, true> when key does not exist, <iterator, false> when key 135 // exist and iterator is the position where it was placed 136 template <class... Args> try_emplace_back(const Key & key,Args &&...args)137 std::pair<iterator, bool> try_emplace_back(const Key& key, Args&&... args) { 138 return try_emplace(end(), key, std::forward<Args>(args)...); 139 } 140 141 // Put a key-value pair to the map before position. If key already exist, |pos| will be ignored and existing value 142 // will be replaced insert_or_assign(const_iterator pos,const Key & key,T value)143 void insert_or_assign(const_iterator pos, const Key& key, T value) { 144 auto map_iterator = key_map_.find(key); 145 if (map_iterator != key_map_.end()) { 146 map_iterator->second->second = std::move(value); 147 return; 148 } 149 auto list_iterator = node_list_.emplace(pos, key, std::move(value)); 150 key_map_.emplace(key, list_iterator); 151 } 152 153 // Put a key-value pair to the tail of the map or replace the current value without moving the key if key exists insert_or_assign(const Key & key,T value)154 void insert_or_assign(const Key& key, T value) { 155 insert_or_assign(end(), key, std::move(value)); 156 } 157 158 // STL splice, same as std::list::splice 159 // - pos: element before which the content will be inserted 160 // - other: another container to transfer the content from 161 // - it: the element to transfer from other to *this splice(const_iterator pos,ListMap<Key,T> & other,const_iterator it)162 void splice(const_iterator pos, ListMap<Key, T>& other, const_iterator it) { 163 if (&other != this) { 164 auto map_node = other.key_map_.extract(it->first); 165 key_map_.insert(std::move(map_node)); 166 } 167 node_list_.splice(pos, other.node_list_, it); 168 } 169 170 // Remove a key from the list map and return removed value if key exits, std::nullopt if not. The return value will be 171 // evaluated to true in a boolean context if a value is contained by std::optional, false otherwise. extract(const Key & key)172 std::optional<node_type> extract(const Key& key) { 173 auto map_iterator = key_map_.find(key); 174 if (map_iterator == key_map_.end()) { 175 return std::nullopt; 176 } 177 std::optional<node_type> removed_node(std::move(*map_iterator->second)); 178 node_list_.erase(map_iterator->second); 179 key_map_.erase(map_iterator); 180 return removed_node; 181 } 182 183 // Remove an iterator pointed item from the list map and return the iterator immediately after the erased item erase(const_iterator iter)184 iterator erase(const_iterator iter) { 185 key_map_.erase(iter->first); 186 return node_list_.erase(iter); 187 } 188 189 // Return size of the list map size()190 inline size_t size() const { 191 return node_list_.size(); 192 } 193 194 // Return iterator interface for begin begin()195 inline iterator begin() { 196 return node_list_.begin(); 197 } 198 199 // Iterator interface for begin, const begin()200 inline const_iterator begin() const { 201 return node_list_.begin(); 202 } 203 204 // Iterator interface for end end()205 inline iterator end() { 206 return node_list_.end(); 207 } 208 209 // Iterator interface for end, const end()210 inline const_iterator end() const { 211 return node_list_.end(); 212 } 213 214 private: 215 std::list<value_type> node_list_; 216 std::unordered_map<Key, iterator> key_map_; 217 }; 218 219 } // namespace common 220 } // namespace bluetooth