tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_