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 32 // before an element that is put later when iterating through this map's entries. Keys must be 33 // unique. 34 // 35 // Performance: 36 // - Key look-up and modification is O(1) 37 // - Value operated by replacement, no in-place modification 38 // - Memory consumption is: 39 // O(2*capacity*sizeof(K) + capacity*(sizeof(nullptr)+sizeof(V))) 40 // - NOT THREAD SAFE 41 // 42 // Template: 43 // - Key key 44 // - T value 45 template <typename Key, typename T> 46 class ListMap { 47 public: 48 using value_type = std::pair<const Key, T>; 49 // different from c++17 node_type on purpose as we want node to be copyable 50 using node_type = std::pair<Key, T>; 51 using iterator = typename std::list<value_type>::iterator; 52 using const_iterator = typename std::list<value_type>::const_iterator; 53 54 // Constructor of the list map 55 ListMap() = default; 56 57 // for move 58 ListMap(ListMap&& other) noexcept = default; 59 ListMap& operator=(ListMap&& other) noexcept = default; 60 61 // copy-constructor 62 // iterators in key_map_ cannot be copied directly ListMap(const ListMap & other)63 ListMap(const ListMap& other) : node_list_(other.node_list_) { 64 for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) { 65 key_map_.emplace(iter->first, iter); 66 } 67 } 68 69 // copy-assignment 70 // iterators in key_map_ cannot be copied directly 71 ListMap& operator=(const ListMap& other) { 72 if (&other == this) { 73 return *this; 74 } 75 node_list_ = other.node_list_; 76 key_map_.clear(); 77 for (auto iter = node_list_.begin(); iter != node_list_.end(); iter++) { 78 key_map_.emplace(iter->first, iter); 79 } 80 return *this; 81 } 82 83 // comparison operators 84 bool operator==(const ListMap& rhs) const { return node_list_ == rhs.node_list_; } 85 bool operator!=(const ListMap& rhs) const { return !(*this == rhs); } 86 ~ListMap()87 ~ListMap() { clear(); } 88 89 // Clear the list map clear()90 void clear() { 91 key_map_.clear(); 92 node_list_.clear(); 93 } 94 95 // const version of find() find(const Key & key)96 const_iterator find(const Key& key) const { return const_cast<ListMap*>(this)->find(key); } 97 98 // Get the value of a key. Return iterator to the item if found, end() if not found find(const Key & key)99 iterator find(const Key& key) { 100 auto map_iterator = key_map_.find(key); 101 if (map_iterator == key_map_.end()) { 102 return end(); 103 } 104 return map_iterator->second; 105 } 106 107 // Check if key exist in the map. Return true if key exist in map, false if not. contains(const Key & key)108 bool contains(const Key& key) const { return find(key) != end(); } 109 110 // Try emplace an element before a specific position |pos| of the list map. If the |key| already 111 // exists, does nothing. Moved arguments won't be moved when key already exists. Return <iterator, 112 // true> when key does not exist, <iterator, false> when key exist and iterator is the position 113 // where it was placed. 114 template <class... Args> try_emplace(const_iterator pos,const Key & key,Args &&...args)115 std::pair<iterator, bool> try_emplace(const_iterator pos, const Key& key, Args&&... args) { 116 auto map_iterator = key_map_.find(key); 117 if (map_iterator != key_map_.end()) { 118 return std::make_pair(end(), false); 119 } 120 auto list_iterator = node_list_.emplace(pos, key, std::forward<Args>(args)...); 121 key_map_.emplace(key, list_iterator); 122 return std::make_pair(list_iterator, true); 123 } 124 125 // Try emplace an element before the end of the list map. If the key already exists, does nothing. 126 // Moved arguments won't be moved when key already exists return <iterator, true> when key does 127 // not exist, <iterator, false> when key exist and iterator is the position where it was placed 128 template <class... Args> try_emplace_back(const Key & key,Args &&...args)129 std::pair<iterator, bool> try_emplace_back(const Key& key, Args&&... args) { 130 return try_emplace(end(), key, std::forward<Args>(args)...); 131 } 132 133 // Put a key-value pair to the map before position. If key already exist, |pos| will be ignored 134 // and existing value will be replaced insert_or_assign(const_iterator pos,const Key & key,T value)135 void insert_or_assign(const_iterator pos, const Key& key, T value) { 136 auto map_iterator = key_map_.find(key); 137 if (map_iterator != key_map_.end()) { 138 map_iterator->second->second = std::move(value); 139 return; 140 } 141 auto list_iterator = node_list_.emplace(pos, key, std::move(value)); 142 key_map_.emplace(key, list_iterator); 143 } 144 145 // Put a key-value pair to the tail of the map or replace the current value without moving the key 146 // if key exists insert_or_assign(const Key & key,T value)147 void insert_or_assign(const Key& key, T value) { insert_or_assign(end(), key, std::move(value)); } 148 149 // STL splice, same as std::list::splice 150 // - pos: element before which the content will be inserted 151 // - other: another container to transfer the content from 152 // - it: the element to transfer from other to *this splice(const_iterator pos,ListMap<Key,T> & other,const_iterator it)153 void splice(const_iterator pos, ListMap<Key, T>& other, const_iterator it) { 154 if (&other != this) { 155 auto map_node = other.key_map_.extract(it->first); 156 key_map_.insert(std::move(map_node)); 157 } 158 node_list_.splice(pos, other.node_list_, it); 159 } 160 161 // Remove a key from the list map and return removed value if key exits, std::nullopt if not. The 162 // return value will be evaluated to true in a boolean context if a value is contained by 163 // std::optional, false otherwise. extract(const Key & key)164 std::optional<node_type> extract(const Key& key) { 165 auto map_iterator = key_map_.find(key); 166 if (map_iterator == key_map_.end()) { 167 return std::nullopt; 168 } 169 std::optional<node_type> removed_node(std::move(*map_iterator->second)); 170 node_list_.erase(map_iterator->second); 171 key_map_.erase(map_iterator); 172 return removed_node; 173 } 174 175 // Remove an iterator pointed item from the list map and return the iterator immediately after the 176 // erased item erase(const_iterator iter)177 iterator erase(const_iterator iter) { 178 key_map_.erase(iter->first); 179 return node_list_.erase(iter); 180 } 181 182 // Return size of the list map size()183 inline size_t size() const { return node_list_.size(); } 184 185 // Return iterator interface for begin begin()186 inline iterator begin() { return node_list_.begin(); } 187 188 // Iterator interface for begin, const begin()189 inline const_iterator begin() const { return node_list_.begin(); } 190 191 // Iterator interface for end end()192 inline iterator end() { return node_list_.end(); } 193 194 // Iterator interface for end, const end()195 inline const_iterator end() const { return node_list_.end(); } 196 197 private: 198 std::list<value_type> node_list_; 199 std::unordered_map<Key, iterator> key_map_; 200 }; 201 202 } // namespace common 203 } // namespace bluetooth 204