flat_map.h (15362B)
1 // Copyright 2017 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_CONTAINERS_FLAT_MAP_H_ 6 #define BASE_CONTAINERS_FLAT_MAP_H_ 7 8 #include <functional> 9 #include <tuple> 10 #include <type_traits> 11 #include <utility> 12 #include <vector> 13 14 #include "base/check.h" 15 #include "base/containers/flat_tree.h" 16 #include "base/template_util.h" 17 18 namespace base { 19 20 namespace internal { 21 22 // An implementation of the flat_tree GetKeyFromValue template parameter that 23 // extracts the key as the first element of a pair. 24 struct GetFirst { 25 template <class Key, class Mapped> 26 constexpr const Key& operator()(const std::pair<Key, Mapped>& p) const { 27 return p.first; 28 } 29 }; 30 31 } // namespace internal 32 33 // flat_map is a container with a std::map-like interface that stores its 34 // contents in a sorted container, by default a vector. 35 // 36 // Its implementation mostly tracks the corresponding standardization proposal 37 // https://wg21.link/P0429, except that the storage of keys and values is not 38 // split. 39 // 40 // Please see //base/containers/README.md for an overview of which container 41 // to select. 42 // 43 // PROS 44 // 45 // - Good memory locality. 46 // - Low overhead, especially for smaller maps. 47 // - Performance is good for more workloads than you might expect (see 48 // overview link above). 49 // - Supports C++14 map interface. 50 // 51 // CONS 52 // 53 // - Inserts and removals are O(n). 54 // 55 // IMPORTANT NOTES 56 // 57 // - Iterators are invalidated across mutations. This means that the following 58 // line of code has undefined behavior since adding a new element could 59 // resize the container, invalidating all iterators: 60 // container["new element"] = it.second; 61 // - If possible, construct a flat_map in one operation by inserting into 62 // a container and moving that container into the flat_map constructor. 63 // 64 // QUICK REFERENCE 65 // 66 // Most of the core functionality is inherited from flat_tree. Please see 67 // flat_tree.h for more details for most of these functions. As a quick 68 // reference, the functions available are: 69 // 70 // Constructors (inputs need not be sorted): 71 // flat_map(const flat_map&); 72 // flat_map(flat_map&&); 73 // flat_map(InputIterator first, InputIterator last, 74 // const Compare& compare = Compare()); 75 // flat_map(const container_type& items, 76 // const Compare& compare = Compare()); 77 // flat_map(container_type&& items, 78 // const Compare& compare = Compare()); // Re-use storage. 79 // flat_map(std::initializer_list<value_type> ilist, 80 // const Compare& comp = Compare()); 81 // 82 // Constructors (inputs need to be sorted): 83 // flat_map(sorted_unique_t, 84 // InputIterator first, InputIterator last, 85 // const Compare& compare = Compare()); 86 // flat_map(sorted_unique_t, 87 // const container_type& items, 88 // const Compare& compare = Compare()); 89 // flat_map(sorted_unique_t, 90 // container_type&& items, 91 // const Compare& compare = Compare()); // Re-use storage. 92 // flat_map(sorted_unique_t, 93 // std::initializer_list<value_type> ilist, 94 // const Compare& comp = Compare()); 95 // 96 // Assignment functions: 97 // flat_map& operator=(const flat_map&); 98 // flat_map& operator=(flat_map&&); 99 // flat_map& operator=(initializer_list<value_type>); 100 // 101 // Memory management functions: 102 // void reserve(size_t); 103 // size_t capacity() const; 104 // void shrink_to_fit(); 105 // 106 // Size management functions: 107 // void clear(); 108 // size_t size() const; 109 // size_t max_size() const; 110 // bool empty() const; 111 // 112 // Iterator functions: 113 // iterator begin(); 114 // const_iterator begin() const; 115 // const_iterator cbegin() const; 116 // iterator end(); 117 // const_iterator end() const; 118 // const_iterator cend() const; 119 // reverse_iterator rbegin(); 120 // const reverse_iterator rbegin() const; 121 // const_reverse_iterator crbegin() const; 122 // reverse_iterator rend(); 123 // const_reverse_iterator rend() const; 124 // const_reverse_iterator crend() const; 125 // 126 // Insert and accessor functions: 127 // mapped_type& operator[](const key_type&); 128 // mapped_type& operator[](key_type&&); 129 // mapped_type& at(const K&); 130 // const mapped_type& at(const K&) const; 131 // pair<iterator, bool> insert(const value_type&); 132 // pair<iterator, bool> insert(value_type&&); 133 // iterator insert(const_iterator hint, const value_type&); 134 // iterator insert(const_iterator hint, value_type&&); 135 // void insert(InputIterator first, InputIterator last); 136 // pair<iterator, bool> insert_or_assign(K&&, M&&); 137 // iterator insert_or_assign(const_iterator hint, K&&, M&&); 138 // pair<iterator, bool> emplace(Args&&...); 139 // iterator emplace_hint(const_iterator, Args&&...); 140 // pair<iterator, bool> try_emplace(K&&, Args&&...); 141 // iterator try_emplace(const_iterator hint, K&&, Args&&...); 142 143 // Underlying type functions: 144 // container_type extract() &&; 145 // void replace(container_type&&); 146 // 147 // Erase functions: 148 // iterator erase(iterator); 149 // iterator erase(const_iterator); 150 // iterator erase(const_iterator first, const_iterator& last); 151 // template <class K> size_t erase(const K& key); 152 // 153 // Comparators (see std::map documentation). 154 // key_compare key_comp() const; 155 // value_compare value_comp() const; 156 // 157 // Search functions: 158 // template <typename K> size_t count(const K&) const; 159 // template <typename K> iterator find(const K&); 160 // template <typename K> const_iterator find(const K&) const; 161 // template <typename K> bool contains(const K&) const; 162 // template <typename K> pair<iterator, iterator> equal_range(const K&); 163 // template <typename K> iterator lower_bound(const K&); 164 // template <typename K> const_iterator lower_bound(const K&) const; 165 // template <typename K> iterator upper_bound(const K&); 166 // template <typename K> const_iterator upper_bound(const K&) const; 167 // 168 // General functions: 169 // void swap(flat_map&); 170 // 171 // Non-member operators: 172 // bool operator==(const flat_map&, const flat_map); 173 // bool operator!=(const flat_map&, const flat_map); 174 // bool operator<(const flat_map&, const flat_map); 175 // bool operator>(const flat_map&, const flat_map); 176 // bool operator>=(const flat_map&, const flat_map); 177 // bool operator<=(const flat_map&, const flat_map); 178 // 179 template <class Key, 180 class Mapped, 181 class Compare = std::less<>, 182 class Container = std::vector<std::pair<Key, Mapped>>> 183 class flat_map : public ::base::internal:: 184 flat_tree<Key, internal::GetFirst, Compare, Container> { 185 private: 186 using tree = typename ::base::internal:: 187 flat_tree<Key, internal::GetFirst, Compare, Container>; 188 189 public: 190 using key_type = typename tree::key_type; 191 using mapped_type = Mapped; 192 using value_type = typename tree::value_type; 193 using reference = typename Container::reference; 194 using const_reference = typename Container::const_reference; 195 using size_type = typename Container::size_type; 196 using difference_type = typename Container::difference_type; 197 using iterator = typename tree::iterator; 198 using const_iterator = typename tree::const_iterator; 199 using reverse_iterator = typename tree::reverse_iterator; 200 using const_reverse_iterator = typename tree::const_reverse_iterator; 201 using container_type = typename tree::container_type; 202 203 // -------------------------------------------------------------------------- 204 // Lifetime and assignments. 205 // 206 // Note: we explicitly bring operator= in because otherwise 207 // flat_map<...> x; 208 // x = {...}; 209 // Would first create a flat_map and then move assign it. This most likely 210 // would be optimized away but still affects our debug builds. 211 212 using tree::tree; 213 using tree::operator=; 214 215 // Out-of-bound calls to at() will CHECK. 216 template <class K> 217 mapped_type& at(const K& key); 218 template <class K> 219 const mapped_type& at(const K& key) const; 220 221 // -------------------------------------------------------------------------- 222 // Map-specific insert operations. 223 // 224 // Normal insert() functions are inherited from flat_tree. 225 // 226 // Assume that every operation invalidates iterators and references. 227 // Insertion of one element can take O(size). 228 229 mapped_type& operator[](const key_type& key); 230 mapped_type& operator[](key_type&& key); 231 232 template <class K, class M> 233 std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj); 234 template <class K, class M> 235 iterator insert_or_assign(const_iterator hint, K&& key, M&& obj); 236 237 template <class K, class... Args> 238 std::enable_if_t<std::is_constructible_v<key_type, K&&>, 239 std::pair<iterator, bool>> 240 try_emplace(K&& key, Args&&... args); 241 242 template <class K, class... Args> 243 std::enable_if_t<std::is_constructible_v<key_type, K&&>, iterator> 244 try_emplace(const_iterator hint, K&& key, Args&&... args); 245 246 // -------------------------------------------------------------------------- 247 // General operations. 248 // 249 // Assume that swap invalidates iterators and references. 250 251 void swap(flat_map& other) noexcept; 252 253 friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); } 254 }; 255 256 // ---------------------------------------------------------------------------- 257 // Lookups. 258 259 template <class Key, class Mapped, class Compare, class Container> 260 template <class K> 261 auto flat_map<Key, Mapped, Compare, Container>::at(const K& key) 262 -> mapped_type& { 263 iterator found = tree::find(key); 264 CHECK(found != tree::end()); 265 return found->second; 266 } 267 268 template <class Key, class Mapped, class Compare, class Container> 269 template <class K> 270 auto flat_map<Key, Mapped, Compare, Container>::at(const K& key) const 271 -> const mapped_type& { 272 const_iterator found = tree::find(key); 273 CHECK(found != tree::cend()); 274 return found->second; 275 } 276 277 // ---------------------------------------------------------------------------- 278 // Insert operations. 279 280 template <class Key, class Mapped, class Compare, class Container> 281 auto flat_map<Key, Mapped, Compare, Container>::operator[](const key_type& key) 282 -> mapped_type& { 283 iterator found = tree::lower_bound(key); 284 if (found == tree::end() || tree::key_comp()(key, found->first)) 285 found = tree::unsafe_emplace(found, key, mapped_type()); 286 return found->second; 287 } 288 289 template <class Key, class Mapped, class Compare, class Container> 290 auto flat_map<Key, Mapped, Compare, Container>::operator[](key_type&& key) 291 -> mapped_type& { 292 iterator found = tree::lower_bound(key); 293 if (found == tree::end() || tree::key_comp()(key, found->first)) 294 found = tree::unsafe_emplace(found, std::move(key), mapped_type()); 295 return found->second; 296 } 297 298 template <class Key, class Mapped, class Compare, class Container> 299 template <class K, class M> 300 auto flat_map<Key, Mapped, Compare, Container>::insert_or_assign(K&& key, 301 M&& obj) 302 -> std::pair<iterator, bool> { 303 auto result = 304 tree::emplace_key_args(key, std::forward<K>(key), std::forward<M>(obj)); 305 if (!result.second) 306 result.first->second = std::forward<M>(obj); 307 return result; 308 } 309 310 template <class Key, class Mapped, class Compare, class Container> 311 template <class K, class M> 312 auto flat_map<Key, Mapped, Compare, Container>::insert_or_assign( 313 const_iterator hint, 314 K&& key, 315 M&& obj) -> iterator { 316 auto result = tree::emplace_hint_key_args(hint, key, std::forward<K>(key), 317 std::forward<M>(obj)); 318 if (!result.second) 319 result.first->second = std::forward<M>(obj); 320 return result.first; 321 } 322 323 template <class Key, class Mapped, class Compare, class Container> 324 template <class K, class... Args> 325 auto flat_map<Key, Mapped, Compare, Container>::try_emplace(K&& key, 326 Args&&... args) 327 -> std::enable_if_t<std::is_constructible_v<key_type, K&&>, 328 std::pair<iterator, bool>> { 329 return tree::emplace_key_args( 330 key, std::piecewise_construct, 331 std::forward_as_tuple(std::forward<K>(key)), 332 std::forward_as_tuple(std::forward<Args>(args)...)); 333 } 334 335 template <class Key, class Mapped, class Compare, class Container> 336 template <class K, class... Args> 337 auto flat_map<Key, Mapped, Compare, Container>::try_emplace(const_iterator hint, 338 K&& key, 339 Args&&... args) 340 -> std::enable_if_t<std::is_constructible_v<key_type, K&&>, iterator> { 341 return tree::emplace_hint_key_args( 342 hint, key, std::piecewise_construct, 343 std::forward_as_tuple(std::forward<K>(key)), 344 std::forward_as_tuple(std::forward<Args>(args)...)) 345 .first; 346 } 347 348 // ---------------------------------------------------------------------------- 349 // General operations. 350 351 template <class Key, class Mapped, class Compare, class Container> 352 void flat_map<Key, Mapped, Compare, Container>::swap(flat_map& other) noexcept { 353 tree::swap(other); 354 } 355 356 // ---------------------------------------------------------------------------- 357 // Utility functions. 358 359 // Utility function to simplify constructing a flat_set from a fixed list of 360 // keys and values. The key/value pairs are obtained by applying |proj| to the 361 // |unprojected_elements|. The map's keys are sorted by |comp|. 362 // 363 // Example usage (creates a set {{16, "4"}, {9, "3"}, {4, "2"}, {1, "1"}}): 364 // auto map = base::MakeFlatMap<int, std::string>( 365 // std::vector<int>{1, 2, 3, 4}, 366 // [](int i, int j) { return i > j; }, 367 // [](int i) { return std::make_pair(i * i, base::NumberToString(i)); }); 368 template <class Key, 369 class Mapped, 370 class KeyCompare = std::less<>, 371 class Container = std::vector<std::pair<Key, Mapped>>, 372 class InputContainer, 373 class Projection = base::identity> 374 constexpr flat_map<Key, Mapped, KeyCompare, Container> MakeFlatMap( 375 const InputContainer& unprojected_elements, 376 const KeyCompare& comp = KeyCompare(), 377 const Projection& proj = Projection()) { 378 Container elements; 379 internal::ReserveIfSupported(elements, unprojected_elements); 380 base::ranges::transform(unprojected_elements, std::back_inserter(elements), 381 proj); 382 return flat_map<Key, Mapped, KeyCompare, Container>(std::move(elements), 383 comp); 384 } 385 386 // Deduction guide to construct a flat_map from a Container of std::pair<Key, 387 // Mapped> elements. The container does not have to be sorted or contain only 388 // unique keys; construction will automatically discard duplicate keys, keeping 389 // only the first. 390 template < 391 class Container, 392 class Compare = std::less<>, 393 class Key = typename std::decay_t<Container>::value_type::first_type, 394 class Mapped = typename std::decay_t<Container>::value_type::second_type> 395 flat_map(Container&&, Compare comp = {}) 396 -> flat_map<Key, Mapped, Compare, std::decay_t<Container>>; 397 398 } // namespace base 399 400 #endif // BASE_CONTAINERS_FLAT_MAP_H_