xref: /aosp_15_r20/frameworks/native/include/ftl/small_map.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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