1*38e8c45fSAndroid Build Coastguard Worker /* 2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2020 The Android Open Source Project 3*38e8c45fSAndroid Build Coastguard Worker * 4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*38e8c45fSAndroid Build Coastguard Worker * 8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*38e8c45fSAndroid Build Coastguard Worker * 10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License. 15*38e8c45fSAndroid Build Coastguard Worker */ 16*38e8c45fSAndroid Build Coastguard Worker 17*38e8c45fSAndroid Build Coastguard Worker #pragma once 18*38e8c45fSAndroid Build Coastguard Worker 19*38e8c45fSAndroid Build Coastguard Worker #include <ftl/initializer_list.h> 20*38e8c45fSAndroid Build Coastguard Worker #include <ftl/optional.h> 21*38e8c45fSAndroid Build Coastguard Worker #include <ftl/small_vector.h> 22*38e8c45fSAndroid Build Coastguard Worker 23*38e8c45fSAndroid Build Coastguard Worker #include <algorithm> 24*38e8c45fSAndroid Build Coastguard Worker #include <functional> 25*38e8c45fSAndroid Build Coastguard Worker #include <type_traits> 26*38e8c45fSAndroid Build Coastguard Worker #include <utility> 27*38e8c45fSAndroid Build Coastguard Worker 28*38e8c45fSAndroid Build Coastguard Worker namespace android::ftl { 29*38e8c45fSAndroid Build Coastguard Worker 30*38e8c45fSAndroid Build Coastguard Worker // Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are 31*38e8c45fSAndroid Build Coastguard Worker // stored in contiguous storage for cache efficiency. The map is allocated statically until its size 32*38e8c45fSAndroid Build Coastguard Worker // exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has 33*38e8c45fSAndroid Build Coastguard Worker // a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place 34*38e8c45fSAndroid Build Coastguard Worker // counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator, 35*38e8c45fSAndroid Build Coastguard Worker // but immutable getters that can optionally transform the value. 36*38e8c45fSAndroid Build Coastguard Worker // 37*38e8c45fSAndroid Build Coastguard Worker // SmallMap<K, V, 0> unconditionally allocates on the heap. 38*38e8c45fSAndroid Build Coastguard Worker // 39*38e8c45fSAndroid Build Coastguard Worker // Example usage: 40*38e8c45fSAndroid Build Coastguard Worker // 41*38e8c45fSAndroid Build Coastguard Worker // ftl::SmallMap<int, std::string, 3> map; 42*38e8c45fSAndroid Build Coastguard Worker // assert(map.empty()); 43*38e8c45fSAndroid Build Coastguard Worker // assert(!map.dynamic()); 44*38e8c45fSAndroid Build Coastguard Worker // 45*38e8c45fSAndroid Build Coastguard Worker // map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); 46*38e8c45fSAndroid Build Coastguard Worker // assert(map.size() == 3u); 47*38e8c45fSAndroid Build Coastguard Worker // assert(!map.dynamic()); 48*38e8c45fSAndroid Build Coastguard Worker // 49*38e8c45fSAndroid Build Coastguard Worker // assert(map.contains(123)); 50*38e8c45fSAndroid Build Coastguard Worker // assert(map.get(42).transform([](const std::string& s) { return s.size(); }) == 3u); 51*38e8c45fSAndroid Build Coastguard Worker // 52*38e8c45fSAndroid Build Coastguard Worker // const auto opt = map.get(-1); 53*38e8c45fSAndroid Build Coastguard Worker // assert(opt); 54*38e8c45fSAndroid Build Coastguard Worker // 55*38e8c45fSAndroid Build Coastguard Worker // std::string& ref = *opt; 56*38e8c45fSAndroid Build Coastguard Worker // assert(ref.empty()); 57*38e8c45fSAndroid Build Coastguard Worker // ref = "xyz"; 58*38e8c45fSAndroid Build Coastguard Worker // 59*38e8c45fSAndroid Build Coastguard Worker // map.emplace_or_replace(0, "vanilla", 2u, 3u); 60*38e8c45fSAndroid Build Coastguard Worker // assert(map.dynamic()); 61*38e8c45fSAndroid Build Coastguard Worker // 62*38e8c45fSAndroid Build Coastguard Worker // assert(map == SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv))); 63*38e8c45fSAndroid Build Coastguard Worker // 64*38e8c45fSAndroid Build Coastguard Worker template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>> 65*38e8c45fSAndroid Build Coastguard Worker class SmallMap final { 66*38e8c45fSAndroid Build Coastguard Worker using Map = SmallVector<std::pair<const K, V>, N>; 67*38e8c45fSAndroid Build Coastguard Worker 68*38e8c45fSAndroid Build Coastguard Worker template <typename, typename, std::size_t, typename> 69*38e8c45fSAndroid Build Coastguard Worker friend class SmallMap; 70*38e8c45fSAndroid Build Coastguard Worker 71*38e8c45fSAndroid Build Coastguard Worker public: 72*38e8c45fSAndroid Build Coastguard Worker using key_type = K; 73*38e8c45fSAndroid Build Coastguard Worker using mapped_type = V; 74*38e8c45fSAndroid Build Coastguard Worker 75*38e8c45fSAndroid Build Coastguard Worker using value_type = typename Map::value_type; 76*38e8c45fSAndroid Build Coastguard Worker using size_type = typename Map::size_type; 77*38e8c45fSAndroid Build Coastguard Worker using difference_type = typename Map::difference_type; 78*38e8c45fSAndroid Build Coastguard Worker 79*38e8c45fSAndroid Build Coastguard Worker using reference = typename Map::reference; 80*38e8c45fSAndroid Build Coastguard Worker using iterator = typename Map::iterator; 81*38e8c45fSAndroid Build Coastguard Worker 82*38e8c45fSAndroid Build Coastguard Worker using const_reference = typename Map::const_reference; 83*38e8c45fSAndroid Build Coastguard Worker using const_iterator = typename Map::const_iterator; 84*38e8c45fSAndroid Build Coastguard Worker 85*38e8c45fSAndroid Build Coastguard Worker // Creates an empty map. 86*38e8c45fSAndroid Build Coastguard Worker SmallMap() = default; 87*38e8c45fSAndroid Build Coastguard Worker 88*38e8c45fSAndroid Build Coastguard Worker // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments. 89*38e8c45fSAndroid Build Coastguard Worker // The template arguments K, V, and N are inferred using the deduction guide defined below. 90*38e8c45fSAndroid Build Coastguard Worker // The syntax for listing pairs is as follows: 91*38e8c45fSAndroid Build Coastguard Worker // 92*38e8c45fSAndroid Build Coastguard Worker // ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?'); 93*38e8c45fSAndroid Build Coastguard Worker // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>); 94*38e8c45fSAndroid Build Coastguard Worker // 95*38e8c45fSAndroid Build Coastguard Worker // The types of the key and value are deduced if the first pair contains exactly two arguments: 96*38e8c45fSAndroid Build Coastguard Worker // 97*38e8c45fSAndroid Build Coastguard Worker // ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); 98*38e8c45fSAndroid Build Coastguard Worker // static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, char, 3>>); 99*38e8c45fSAndroid Build Coastguard Worker // 100*38e8c45fSAndroid Build Coastguard Worker template <typename U, std::size_t... Sizes, typename... Types> SmallMap(InitializerList<U,std::index_sequence<Sizes...>,Types...> && list)101*38e8c45fSAndroid Build Coastguard Worker SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list) 102*38e8c45fSAndroid Build Coastguard Worker : map_(std::move(list)) { 103*38e8c45fSAndroid Build Coastguard Worker deduplicate(); 104*38e8c45fSAndroid Build Coastguard Worker } 105*38e8c45fSAndroid Build Coastguard Worker 106*38e8c45fSAndroid Build Coastguard Worker // Copies or moves key-value pairs from a convertible map. 107*38e8c45fSAndroid Build Coastguard Worker template <typename Q, typename W, std::size_t M, typename E> SmallMap(SmallMap<Q,W,M,E> other)108*38e8c45fSAndroid Build Coastguard Worker SmallMap(SmallMap<Q, W, M, E> other) : map_(std::move(other.map_)) {} 109*38e8c45fSAndroid Build Coastguard Worker static_capacity()110*38e8c45fSAndroid Build Coastguard Worker static constexpr size_type static_capacity() { return N; } 111*38e8c45fSAndroid Build Coastguard Worker max_size()112*38e8c45fSAndroid Build Coastguard Worker size_type max_size() const { return map_.max_size(); } size()113*38e8c45fSAndroid Build Coastguard Worker size_type size() const { return map_.size(); } empty()114*38e8c45fSAndroid Build Coastguard Worker bool empty() const { return map_.empty(); } 115*38e8c45fSAndroid Build Coastguard Worker 116*38e8c45fSAndroid Build Coastguard Worker // Returns whether the map is backed by static or dynamic storage. dynamic()117*38e8c45fSAndroid Build Coastguard Worker bool dynamic() const { 118*38e8c45fSAndroid Build Coastguard Worker if constexpr (static_capacity() > 0) { 119*38e8c45fSAndroid Build Coastguard Worker return map_.dynamic(); 120*38e8c45fSAndroid Build Coastguard Worker } else { 121*38e8c45fSAndroid Build Coastguard Worker return true; 122*38e8c45fSAndroid Build Coastguard Worker } 123*38e8c45fSAndroid Build Coastguard Worker } 124*38e8c45fSAndroid Build Coastguard Worker begin()125*38e8c45fSAndroid Build Coastguard Worker iterator begin() { return map_.begin(); } begin()126*38e8c45fSAndroid Build Coastguard Worker const_iterator begin() const { return cbegin(); } cbegin()127*38e8c45fSAndroid Build Coastguard Worker const_iterator cbegin() const { return map_.cbegin(); } 128*38e8c45fSAndroid Build Coastguard Worker end()129*38e8c45fSAndroid Build Coastguard Worker iterator end() { return map_.end(); } end()130*38e8c45fSAndroid Build Coastguard Worker const_iterator end() const { return cend(); } cend()131*38e8c45fSAndroid Build Coastguard Worker const_iterator cend() const { return map_.cend(); } 132*38e8c45fSAndroid Build Coastguard Worker 133*38e8c45fSAndroid Build Coastguard Worker // Returns whether a mapping exists for the given key. contains(const key_type & key)134*38e8c45fSAndroid Build Coastguard Worker bool contains(const key_type& key) const { return get(key).has_value(); } 135*38e8c45fSAndroid Build Coastguard Worker 136*38e8c45fSAndroid Build Coastguard Worker // Returns a reference to the value for the given key, or std::nullopt if the key was not found. 137*38e8c45fSAndroid Build Coastguard Worker // 138*38e8c45fSAndroid Build Coastguard Worker // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); 139*38e8c45fSAndroid Build Coastguard Worker // 140*38e8c45fSAndroid Build Coastguard Worker // const auto opt = map.get('c'); 141*38e8c45fSAndroid Build Coastguard Worker // assert(opt == 'C'); 142*38e8c45fSAndroid Build Coastguard Worker // 143*38e8c45fSAndroid Build Coastguard Worker // char d = 'd'; 144*38e8c45fSAndroid Build Coastguard Worker // const auto ref = map.get('d').value_or(std::ref(d)); 145*38e8c45fSAndroid Build Coastguard Worker // ref.get() = 'D'; 146*38e8c45fSAndroid Build Coastguard Worker // assert(d == 'D'); 147*38e8c45fSAndroid Build Coastguard Worker // 148*38e8c45fSAndroid Build Coastguard Worker auto get(const key_type& key) const -> Optional<std::reference_wrapper<const mapped_type>> { 149*38e8c45fSAndroid Build Coastguard Worker for (const auto& [k, v] : *this) { 150*38e8c45fSAndroid Build Coastguard Worker if (KeyEqual{}(k, key)) { 151*38e8c45fSAndroid Build Coastguard Worker return std::cref(v); 152*38e8c45fSAndroid Build Coastguard Worker } 153*38e8c45fSAndroid Build Coastguard Worker } 154*38e8c45fSAndroid Build Coastguard Worker return {}; 155*38e8c45fSAndroid Build Coastguard Worker } 156*38e8c45fSAndroid Build Coastguard Worker 157*38e8c45fSAndroid Build Coastguard Worker auto get(const key_type& key) -> Optional<std::reference_wrapper<mapped_type>> { 158*38e8c45fSAndroid Build Coastguard Worker for (auto& [k, v] : *this) { 159*38e8c45fSAndroid Build Coastguard Worker if (KeyEqual{}(k, key)) { 160*38e8c45fSAndroid Build Coastguard Worker return std::ref(v); 161*38e8c45fSAndroid Build Coastguard Worker } 162*38e8c45fSAndroid Build Coastguard Worker } 163*38e8c45fSAndroid Build Coastguard Worker return {}; 164*38e8c45fSAndroid Build Coastguard Worker } 165*38e8c45fSAndroid Build Coastguard Worker 166*38e8c45fSAndroid Build Coastguard Worker // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise. find(const key_type & key)167*38e8c45fSAndroid Build Coastguard Worker const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); } find(const key_type & key)168*38e8c45fSAndroid Build Coastguard Worker iterator find(const key_type& key) { return find(key, begin()); } 169*38e8c45fSAndroid Build Coastguard Worker 170*38e8c45fSAndroid Build Coastguard Worker // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping, 171*38e8c45fSAndroid Build Coastguard Worker // and whether the mapping was inserted. 172*38e8c45fSAndroid Build Coastguard Worker // 173*38e8c45fSAndroid Build Coastguard Worker // On emplace, if the map reaches its static or dynamic capacity, then all iterators are 174*38e8c45fSAndroid Build Coastguard Worker // invalidated. Otherwise, only the end() iterator is invalidated. 175*38e8c45fSAndroid Build Coastguard Worker // 176*38e8c45fSAndroid Build Coastguard Worker template <typename... Args> try_emplace(const key_type & key,Args &&...args)177*38e8c45fSAndroid Build Coastguard Worker std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) { 178*38e8c45fSAndroid Build Coastguard Worker if (const auto it = find(key); it != end()) { 179*38e8c45fSAndroid Build Coastguard Worker return {it, false}; 180*38e8c45fSAndroid Build Coastguard Worker } 181*38e8c45fSAndroid Build Coastguard Worker 182*38e8c45fSAndroid Build Coastguard Worker decltype(auto) ref_or_it = 183*38e8c45fSAndroid Build Coastguard Worker map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), 184*38e8c45fSAndroid Build Coastguard Worker std::forward_as_tuple(std::forward<Args>(args)...)); 185*38e8c45fSAndroid Build Coastguard Worker 186*38e8c45fSAndroid Build Coastguard Worker if constexpr (static_capacity() > 0) { 187*38e8c45fSAndroid Build Coastguard Worker return {&ref_or_it, true}; 188*38e8c45fSAndroid Build Coastguard Worker } else { 189*38e8c45fSAndroid Build Coastguard Worker return {ref_or_it, true}; 190*38e8c45fSAndroid Build Coastguard Worker } 191*38e8c45fSAndroid Build Coastguard Worker } 192*38e8c45fSAndroid Build Coastguard Worker 193*38e8c45fSAndroid Build Coastguard Worker // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator 194*38e8c45fSAndroid Build Coastguard Worker // otherwise. 195*38e8c45fSAndroid Build Coastguard Worker // 196*38e8c45fSAndroid Build Coastguard Worker // The value is replaced via move constructor, so type V does not need to define copy/move 197*38e8c45fSAndroid Build Coastguard Worker // assignment, e.g. its data members may be const. 198*38e8c45fSAndroid Build Coastguard Worker // 199*38e8c45fSAndroid Build Coastguard Worker // The arguments may directly or indirectly refer to the mapping being replaced. 200*38e8c45fSAndroid Build Coastguard Worker // 201*38e8c45fSAndroid Build Coastguard Worker // Iterators to the replaced mapping point to its replacement, and others remain valid. 202*38e8c45fSAndroid Build Coastguard Worker // 203*38e8c45fSAndroid Build Coastguard Worker template <typename... Args> try_replace(const key_type & key,Args &&...args)204*38e8c45fSAndroid Build Coastguard Worker iterator try_replace(const key_type& key, Args&&... args) { 205*38e8c45fSAndroid Build Coastguard Worker const auto it = find(key); 206*38e8c45fSAndroid Build Coastguard Worker if (it == end()) return it; 207*38e8c45fSAndroid Build Coastguard Worker map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), 208*38e8c45fSAndroid Build Coastguard Worker std::forward_as_tuple(std::forward<Args>(args)...)); 209*38e8c45fSAndroid Build Coastguard Worker return it; 210*38e8c45fSAndroid Build Coastguard Worker } 211*38e8c45fSAndroid Build Coastguard Worker 212*38e8c45fSAndroid Build Coastguard Worker // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or 213*38e8c45fSAndroid Build Coastguard Worker // false on replace. 214*38e8c45fSAndroid Build Coastguard Worker // 215*38e8c45fSAndroid Build Coastguard Worker // The value is emplaced and replaced via move constructor, so type V does not need to define 216*38e8c45fSAndroid Build Coastguard Worker // copy/move assignment, e.g. its data members may be const. 217*38e8c45fSAndroid Build Coastguard Worker // 218*38e8c45fSAndroid Build Coastguard Worker // On emplace, if the map reaches its static or dynamic capacity, then all iterators are 219*38e8c45fSAndroid Build Coastguard Worker // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators 220*38e8c45fSAndroid Build Coastguard Worker // to the replaced mapping point to its replacement, and others remain valid. 221*38e8c45fSAndroid Build Coastguard Worker // 222*38e8c45fSAndroid Build Coastguard Worker template <typename... Args> emplace_or_replace(const key_type & key,Args &&...args)223*38e8c45fSAndroid Build Coastguard Worker std::pair<iterator, bool> emplace_or_replace(const key_type& key, Args&&... args) { 224*38e8c45fSAndroid Build Coastguard Worker const auto [it, ok] = try_emplace(key, std::forward<Args>(args)...); 225*38e8c45fSAndroid Build Coastguard Worker if (ok) return {it, ok}; 226*38e8c45fSAndroid Build Coastguard Worker map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), 227*38e8c45fSAndroid Build Coastguard Worker std::forward_as_tuple(std::forward<Args>(args)...)); 228*38e8c45fSAndroid Build Coastguard Worker return {it, ok}; 229*38e8c45fSAndroid Build Coastguard Worker } 230*38e8c45fSAndroid Build Coastguard Worker 231*38e8c45fSAndroid Build Coastguard Worker // Removes a mapping if it exists, and returns whether it did. 232*38e8c45fSAndroid Build Coastguard Worker // 233*38e8c45fSAndroid Build Coastguard Worker // The last() and end() iterators, as well as those to the erased mapping, are invalidated. 234*38e8c45fSAndroid Build Coastguard Worker // erase(const key_type & key)235*38e8c45fSAndroid Build Coastguard Worker bool erase(const key_type& key) { return erase(key, begin()); } 236*38e8c45fSAndroid Build Coastguard Worker 237*38e8c45fSAndroid Build Coastguard Worker // Removes all mappings. 238*38e8c45fSAndroid Build Coastguard Worker // 239*38e8c45fSAndroid Build Coastguard Worker // All iterators are invalidated. 240*38e8c45fSAndroid Build Coastguard Worker // clear()241*38e8c45fSAndroid Build Coastguard Worker void clear() { map_.clear(); } 242*38e8c45fSAndroid Build Coastguard Worker 243*38e8c45fSAndroid Build Coastguard Worker private: find(const key_type & key,iterator first)244*38e8c45fSAndroid Build Coastguard Worker iterator find(const key_type& key, iterator first) { 245*38e8c45fSAndroid Build Coastguard Worker return std::find_if(first, end(), 246*38e8c45fSAndroid Build Coastguard Worker [&key](const auto& pair) { return KeyEqual{}(pair.first, key); }); 247*38e8c45fSAndroid Build Coastguard Worker } 248*38e8c45fSAndroid Build Coastguard Worker erase(const key_type & key,iterator first)249*38e8c45fSAndroid Build Coastguard Worker bool erase(const key_type& key, iterator first) { 250*38e8c45fSAndroid Build Coastguard Worker const auto it = find(key, first); 251*38e8c45fSAndroid Build Coastguard Worker if (it == end()) return false; 252*38e8c45fSAndroid Build Coastguard Worker map_.unstable_erase(it); 253*38e8c45fSAndroid Build Coastguard Worker return true; 254*38e8c45fSAndroid Build Coastguard Worker } 255*38e8c45fSAndroid Build Coastguard Worker deduplicate()256*38e8c45fSAndroid Build Coastguard Worker void deduplicate() { 257*38e8c45fSAndroid Build Coastguard Worker for (auto it = begin(); it != end();) { 258*38e8c45fSAndroid Build Coastguard Worker if (const auto key = it->first; ++it != end()) { 259*38e8c45fSAndroid Build Coastguard Worker while (erase(key, it)); 260*38e8c45fSAndroid Build Coastguard Worker } 261*38e8c45fSAndroid Build Coastguard Worker } 262*38e8c45fSAndroid Build Coastguard Worker } 263*38e8c45fSAndroid Build Coastguard Worker 264*38e8c45fSAndroid Build Coastguard Worker Map map_; 265*38e8c45fSAndroid Build Coastguard Worker }; 266*38e8c45fSAndroid Build Coastguard Worker 267*38e8c45fSAndroid Build Coastguard Worker // Deduction guide for in-place constructor. 268*38e8c45fSAndroid Build Coastguard Worker template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types> 269*38e8c45fSAndroid Build Coastguard Worker SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&) 270*38e8c45fSAndroid Build Coastguard Worker -> SmallMap<K, V, sizeof...(Sizes), E>; 271*38e8c45fSAndroid Build Coastguard Worker 272*38e8c45fSAndroid Build Coastguard Worker // Returns whether the key-value pairs of two maps are equal. 273*38e8c45fSAndroid Build Coastguard Worker template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> 274*38e8c45fSAndroid Build Coastguard Worker bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { 275*38e8c45fSAndroid Build Coastguard Worker if (lhs.size() != rhs.size()) return false; 276*38e8c45fSAndroid Build Coastguard Worker 277*38e8c45fSAndroid Build Coastguard Worker for (const auto& [k, v] : lhs) { 278*38e8c45fSAndroid Build Coastguard Worker const auto& lv = v; 279*38e8c45fSAndroid Build Coastguard Worker if (!rhs.get(k).transform([&lv](const W& rv) { return lv == rv; }).value_or(false)) { 280*38e8c45fSAndroid Build Coastguard Worker return false; 281*38e8c45fSAndroid Build Coastguard Worker } 282*38e8c45fSAndroid Build Coastguard Worker } 283*38e8c45fSAndroid Build Coastguard Worker 284*38e8c45fSAndroid Build Coastguard Worker return true; 285*38e8c45fSAndroid Build Coastguard Worker } 286*38e8c45fSAndroid Build Coastguard Worker 287*38e8c45fSAndroid Build Coastguard Worker // TODO: Remove in C++20. 288*38e8c45fSAndroid Build Coastguard Worker template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E> 289*38e8c45fSAndroid Build Coastguard Worker inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) { 290*38e8c45fSAndroid Build Coastguard Worker return !(lhs == rhs); 291*38e8c45fSAndroid Build Coastguard Worker } 292*38e8c45fSAndroid Build Coastguard Worker 293*38e8c45fSAndroid Build Coastguard Worker } // namespace android::ftl 294