xref: /aosp_15_r20/external/libchrome/base/containers/id_map.h (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2*635a8641SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*635a8641SAndroid Build Coastguard Worker // found in the LICENSE file.
4*635a8641SAndroid Build Coastguard Worker 
5*635a8641SAndroid Build Coastguard Worker #ifndef BASE_CONTAINERS_ID_MAP_H_
6*635a8641SAndroid Build Coastguard Worker #define BASE_CONTAINERS_ID_MAP_H_
7*635a8641SAndroid Build Coastguard Worker 
8*635a8641SAndroid Build Coastguard Worker #include <stddef.h>
9*635a8641SAndroid Build Coastguard Worker #include <stdint.h>
10*635a8641SAndroid Build Coastguard Worker 
11*635a8641SAndroid Build Coastguard Worker #include <memory>
12*635a8641SAndroid Build Coastguard Worker #include <set>
13*635a8641SAndroid Build Coastguard Worker #include <type_traits>
14*635a8641SAndroid Build Coastguard Worker #include <unordered_map>
15*635a8641SAndroid Build Coastguard Worker #include <utility>
16*635a8641SAndroid Build Coastguard Worker 
17*635a8641SAndroid Build Coastguard Worker #include "base/containers/flat_set.h"
18*635a8641SAndroid Build Coastguard Worker #include "base/logging.h"
19*635a8641SAndroid Build Coastguard Worker #include "base/macros.h"
20*635a8641SAndroid Build Coastguard Worker #include "base/sequence_checker.h"
21*635a8641SAndroid Build Coastguard Worker 
22*635a8641SAndroid Build Coastguard Worker namespace base {
23*635a8641SAndroid Build Coastguard Worker 
24*635a8641SAndroid Build Coastguard Worker // This object maintains a list of IDs that can be quickly converted to
25*635a8641SAndroid Build Coastguard Worker // pointers to objects. It is implemented as a hash table, optimized for
26*635a8641SAndroid Build Coastguard Worker // relatively small data sets (in the common case, there will be exactly one
27*635a8641SAndroid Build Coastguard Worker // item in the list).
28*635a8641SAndroid Build Coastguard Worker //
29*635a8641SAndroid Build Coastguard Worker // Items can be inserted into the container with arbitrary ID, but the caller
30*635a8641SAndroid Build Coastguard Worker // must ensure they are unique. Inserting IDs and relying on automatically
31*635a8641SAndroid Build Coastguard Worker // generated ones is not allowed because they can collide.
32*635a8641SAndroid Build Coastguard Worker 
33*635a8641SAndroid Build Coastguard Worker // The map's value type (the V param) can be any dereferenceable type, such as a
34*635a8641SAndroid Build Coastguard Worker // raw pointer or smart pointer
35*635a8641SAndroid Build Coastguard Worker template <typename V, typename K = int32_t>
36*635a8641SAndroid Build Coastguard Worker class IDMap final {
37*635a8641SAndroid Build Coastguard Worker  public:
38*635a8641SAndroid Build Coastguard Worker   using KeyType = K;
39*635a8641SAndroid Build Coastguard Worker 
40*635a8641SAndroid Build Coastguard Worker  private:
41*635a8641SAndroid Build Coastguard Worker   using T = typename std::remove_reference<decltype(*V())>::type;
42*635a8641SAndroid Build Coastguard Worker 
43*635a8641SAndroid Build Coastguard Worker   using HashTable = std::unordered_map<KeyType, V>;
44*635a8641SAndroid Build Coastguard Worker 
45*635a8641SAndroid Build Coastguard Worker  public:
IDMap()46*635a8641SAndroid Build Coastguard Worker   IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
47*635a8641SAndroid Build Coastguard Worker     // A number of consumers of IDMap create it on one thread but always
48*635a8641SAndroid Build Coastguard Worker     // access it from a different, but consistent, thread (or sequence)
49*635a8641SAndroid Build Coastguard Worker     // post-construction. The first call to CalledOnValidSequence() will re-bind
50*635a8641SAndroid Build Coastguard Worker     // it.
51*635a8641SAndroid Build Coastguard Worker     DETACH_FROM_SEQUENCE(sequence_checker_);
52*635a8641SAndroid Build Coastguard Worker   }
53*635a8641SAndroid Build Coastguard Worker 
~IDMap()54*635a8641SAndroid Build Coastguard Worker   ~IDMap() {
55*635a8641SAndroid Build Coastguard Worker     // Many IDMap's are static, and hence will be destroyed on the main
56*635a8641SAndroid Build Coastguard Worker     // thread. However, all the accesses may take place on another thread (or
57*635a8641SAndroid Build Coastguard Worker     // sequence), such as the IO thread. Detaching again to clean this up.
58*635a8641SAndroid Build Coastguard Worker     DETACH_FROM_SEQUENCE(sequence_checker_);
59*635a8641SAndroid Build Coastguard Worker   }
60*635a8641SAndroid Build Coastguard Worker 
61*635a8641SAndroid Build Coastguard Worker   // Sets whether Add and Replace should DCHECK if passed in NULL data.
62*635a8641SAndroid Build Coastguard Worker   // Default is false.
set_check_on_null_data(bool value)63*635a8641SAndroid Build Coastguard Worker   void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
64*635a8641SAndroid Build Coastguard Worker 
65*635a8641SAndroid Build Coastguard Worker   // Adds a view with an automatically generated unique ID. See AddWithID.
Add(V data)66*635a8641SAndroid Build Coastguard Worker   KeyType Add(V data) { return AddInternal(std::move(data)); }
67*635a8641SAndroid Build Coastguard Worker 
68*635a8641SAndroid Build Coastguard Worker   // Adds a new data member with the specified ID. The ID must not be in
69*635a8641SAndroid Build Coastguard Worker   // the list. The caller either must generate all unique IDs itself and use
70*635a8641SAndroid Build Coastguard Worker   // this function, or allow this object to generate IDs and call Add. These
71*635a8641SAndroid Build Coastguard Worker   // two methods may not be mixed, or duplicate IDs may be generated.
AddWithID(V data,KeyType id)72*635a8641SAndroid Build Coastguard Worker   void AddWithID(V data, KeyType id) { AddWithIDInternal(std::move(data), id); }
73*635a8641SAndroid Build Coastguard Worker 
Remove(KeyType id)74*635a8641SAndroid Build Coastguard Worker   void Remove(KeyType id) {
75*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
76*635a8641SAndroid Build Coastguard Worker     typename HashTable::iterator i = data_.find(id);
77*635a8641SAndroid Build Coastguard Worker     if (i == data_.end() || IsRemoved(id)) {
78*635a8641SAndroid Build Coastguard Worker       NOTREACHED() << "Attempting to remove an item not in the list";
79*635a8641SAndroid Build Coastguard Worker       return;
80*635a8641SAndroid Build Coastguard Worker     }
81*635a8641SAndroid Build Coastguard Worker 
82*635a8641SAndroid Build Coastguard Worker     if (iteration_depth_ == 0) {
83*635a8641SAndroid Build Coastguard Worker       data_.erase(i);
84*635a8641SAndroid Build Coastguard Worker     } else {
85*635a8641SAndroid Build Coastguard Worker       removed_ids_.insert(id);
86*635a8641SAndroid Build Coastguard Worker     }
87*635a8641SAndroid Build Coastguard Worker   }
88*635a8641SAndroid Build Coastguard Worker 
89*635a8641SAndroid Build Coastguard Worker   // Replaces the value for |id| with |new_data| and returns the existing value.
90*635a8641SAndroid Build Coastguard Worker   // Should only be called with an already added id.
Replace(KeyType id,V new_data)91*635a8641SAndroid Build Coastguard Worker   V Replace(KeyType id, V new_data) {
92*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
93*635a8641SAndroid Build Coastguard Worker     DCHECK(!check_on_null_data_ || new_data);
94*635a8641SAndroid Build Coastguard Worker     typename HashTable::iterator i = data_.find(id);
95*635a8641SAndroid Build Coastguard Worker     DCHECK(i != data_.end());
96*635a8641SAndroid Build Coastguard Worker     DCHECK(!IsRemoved(id));
97*635a8641SAndroid Build Coastguard Worker 
98*635a8641SAndroid Build Coastguard Worker     using std::swap;
99*635a8641SAndroid Build Coastguard Worker     swap(i->second, new_data);
100*635a8641SAndroid Build Coastguard Worker     return new_data;
101*635a8641SAndroid Build Coastguard Worker   }
102*635a8641SAndroid Build Coastguard Worker 
Clear()103*635a8641SAndroid Build Coastguard Worker   void Clear() {
104*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
105*635a8641SAndroid Build Coastguard Worker     if (iteration_depth_ == 0) {
106*635a8641SAndroid Build Coastguard Worker       data_.clear();
107*635a8641SAndroid Build Coastguard Worker     } else {
108*635a8641SAndroid Build Coastguard Worker       removed_ids_.reserve(data_.size());
109*635a8641SAndroid Build Coastguard Worker       removed_ids_.insert(KeyIterator(data_.begin()), KeyIterator(data_.end()));
110*635a8641SAndroid Build Coastguard Worker     }
111*635a8641SAndroid Build Coastguard Worker   }
112*635a8641SAndroid Build Coastguard Worker 
IsEmpty()113*635a8641SAndroid Build Coastguard Worker   bool IsEmpty() const {
114*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115*635a8641SAndroid Build Coastguard Worker     return size() == 0u;
116*635a8641SAndroid Build Coastguard Worker   }
117*635a8641SAndroid Build Coastguard Worker 
Lookup(KeyType id)118*635a8641SAndroid Build Coastguard Worker   T* Lookup(KeyType id) const {
119*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
120*635a8641SAndroid Build Coastguard Worker     typename HashTable::const_iterator i = data_.find(id);
121*635a8641SAndroid Build Coastguard Worker     if (i == data_.end() || !i->second || IsRemoved(id))
122*635a8641SAndroid Build Coastguard Worker       return nullptr;
123*635a8641SAndroid Build Coastguard Worker     return &*i->second;
124*635a8641SAndroid Build Coastguard Worker   }
125*635a8641SAndroid Build Coastguard Worker 
size()126*635a8641SAndroid Build Coastguard Worker   size_t size() const {
127*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
128*635a8641SAndroid Build Coastguard Worker     return data_.size() - removed_ids_.size();
129*635a8641SAndroid Build Coastguard Worker   }
130*635a8641SAndroid Build Coastguard Worker 
131*635a8641SAndroid Build Coastguard Worker #if defined(UNIT_TEST)
iteration_depth()132*635a8641SAndroid Build Coastguard Worker   int iteration_depth() const {
133*635a8641SAndroid Build Coastguard Worker     return iteration_depth_;
134*635a8641SAndroid Build Coastguard Worker   }
135*635a8641SAndroid Build Coastguard Worker #endif  // defined(UNIT_TEST)
136*635a8641SAndroid Build Coastguard Worker 
137*635a8641SAndroid Build Coastguard Worker   // It is safe to remove elements from the map during iteration. All iterators
138*635a8641SAndroid Build Coastguard Worker   // will remain valid.
139*635a8641SAndroid Build Coastguard Worker   template<class ReturnType>
140*635a8641SAndroid Build Coastguard Worker   class Iterator {
141*635a8641SAndroid Build Coastguard Worker    public:
Iterator(IDMap<V,K> * map)142*635a8641SAndroid Build Coastguard Worker     Iterator(IDMap<V, K>* map) : map_(map), iter_(map_->data_.begin()) {
143*635a8641SAndroid Build Coastguard Worker       Init();
144*635a8641SAndroid Build Coastguard Worker     }
145*635a8641SAndroid Build Coastguard Worker 
Iterator(const Iterator & iter)146*635a8641SAndroid Build Coastguard Worker     Iterator(const Iterator& iter)
147*635a8641SAndroid Build Coastguard Worker         : map_(iter.map_),
148*635a8641SAndroid Build Coastguard Worker           iter_(iter.iter_) {
149*635a8641SAndroid Build Coastguard Worker       Init();
150*635a8641SAndroid Build Coastguard Worker     }
151*635a8641SAndroid Build Coastguard Worker 
152*635a8641SAndroid Build Coastguard Worker     const Iterator& operator=(const Iterator& iter) {
153*635a8641SAndroid Build Coastguard Worker       map_ = iter.map;
154*635a8641SAndroid Build Coastguard Worker       iter_ = iter.iter;
155*635a8641SAndroid Build Coastguard Worker       Init();
156*635a8641SAndroid Build Coastguard Worker       return *this;
157*635a8641SAndroid Build Coastguard Worker     }
158*635a8641SAndroid Build Coastguard Worker 
~Iterator()159*635a8641SAndroid Build Coastguard Worker     ~Iterator() {
160*635a8641SAndroid Build Coastguard Worker       DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
161*635a8641SAndroid Build Coastguard Worker 
162*635a8641SAndroid Build Coastguard Worker       // We're going to decrement iteration depth. Make sure it's greater than
163*635a8641SAndroid Build Coastguard Worker       // zero so that it doesn't become negative.
164*635a8641SAndroid Build Coastguard Worker       DCHECK_LT(0, map_->iteration_depth_);
165*635a8641SAndroid Build Coastguard Worker 
166*635a8641SAndroid Build Coastguard Worker       if (--map_->iteration_depth_ == 0)
167*635a8641SAndroid Build Coastguard Worker         map_->Compact();
168*635a8641SAndroid Build Coastguard Worker     }
169*635a8641SAndroid Build Coastguard Worker 
IsAtEnd()170*635a8641SAndroid Build Coastguard Worker     bool IsAtEnd() const {
171*635a8641SAndroid Build Coastguard Worker       DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
172*635a8641SAndroid Build Coastguard Worker       return iter_ == map_->data_.end();
173*635a8641SAndroid Build Coastguard Worker     }
174*635a8641SAndroid Build Coastguard Worker 
GetCurrentKey()175*635a8641SAndroid Build Coastguard Worker     KeyType GetCurrentKey() const {
176*635a8641SAndroid Build Coastguard Worker       DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
177*635a8641SAndroid Build Coastguard Worker       return iter_->first;
178*635a8641SAndroid Build Coastguard Worker     }
179*635a8641SAndroid Build Coastguard Worker 
GetCurrentValue()180*635a8641SAndroid Build Coastguard Worker     ReturnType* GetCurrentValue() const {
181*635a8641SAndroid Build Coastguard Worker       DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
182*635a8641SAndroid Build Coastguard Worker       if (!iter_->second || map_->IsRemoved(iter_->first))
183*635a8641SAndroid Build Coastguard Worker         return nullptr;
184*635a8641SAndroid Build Coastguard Worker       return &*iter_->second;
185*635a8641SAndroid Build Coastguard Worker     }
186*635a8641SAndroid Build Coastguard Worker 
Advance()187*635a8641SAndroid Build Coastguard Worker     void Advance() {
188*635a8641SAndroid Build Coastguard Worker       DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
189*635a8641SAndroid Build Coastguard Worker       ++iter_;
190*635a8641SAndroid Build Coastguard Worker       SkipRemovedEntries();
191*635a8641SAndroid Build Coastguard Worker     }
192*635a8641SAndroid Build Coastguard Worker 
193*635a8641SAndroid Build Coastguard Worker    private:
Init()194*635a8641SAndroid Build Coastguard Worker     void Init() {
195*635a8641SAndroid Build Coastguard Worker       DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
196*635a8641SAndroid Build Coastguard Worker       ++map_->iteration_depth_;
197*635a8641SAndroid Build Coastguard Worker       SkipRemovedEntries();
198*635a8641SAndroid Build Coastguard Worker     }
199*635a8641SAndroid Build Coastguard Worker 
SkipRemovedEntries()200*635a8641SAndroid Build Coastguard Worker     void SkipRemovedEntries() {
201*635a8641SAndroid Build Coastguard Worker       while (iter_ != map_->data_.end() && map_->IsRemoved(iter_->first))
202*635a8641SAndroid Build Coastguard Worker         ++iter_;
203*635a8641SAndroid Build Coastguard Worker     }
204*635a8641SAndroid Build Coastguard Worker 
205*635a8641SAndroid Build Coastguard Worker     IDMap<V, K>* map_;
206*635a8641SAndroid Build Coastguard Worker     typename HashTable::const_iterator iter_;
207*635a8641SAndroid Build Coastguard Worker   };
208*635a8641SAndroid Build Coastguard Worker 
209*635a8641SAndroid Build Coastguard Worker   typedef Iterator<T> iterator;
210*635a8641SAndroid Build Coastguard Worker   typedef Iterator<const T> const_iterator;
211*635a8641SAndroid Build Coastguard Worker 
212*635a8641SAndroid Build Coastguard Worker  private:
213*635a8641SAndroid Build Coastguard Worker   // Transforms a map iterator to an iterator on the keys of the map.
214*635a8641SAndroid Build Coastguard Worker   // Used by Clear() to populate |removed_ids_| in bulk.
215*635a8641SAndroid Build Coastguard Worker   struct KeyIterator : std::iterator<std::forward_iterator_tag, KeyType> {
216*635a8641SAndroid Build Coastguard Worker     using inner_iterator = typename HashTable::iterator;
217*635a8641SAndroid Build Coastguard Worker     inner_iterator iter_;
218*635a8641SAndroid Build Coastguard Worker 
KeyIteratorKeyIterator219*635a8641SAndroid Build Coastguard Worker     KeyIterator(inner_iterator iter) : iter_(iter) {}
220*635a8641SAndroid Build Coastguard Worker     KeyType operator*() const { return iter_->first; }
221*635a8641SAndroid Build Coastguard Worker     KeyIterator& operator++() {
222*635a8641SAndroid Build Coastguard Worker       ++iter_;
223*635a8641SAndroid Build Coastguard Worker       return *this;
224*635a8641SAndroid Build Coastguard Worker     }
225*635a8641SAndroid Build Coastguard Worker     KeyIterator operator++(int) { return KeyIterator(iter_++); }
226*635a8641SAndroid Build Coastguard Worker     bool operator==(const KeyIterator& other) const {
227*635a8641SAndroid Build Coastguard Worker       return iter_ == other.iter_;
228*635a8641SAndroid Build Coastguard Worker     }
229*635a8641SAndroid Build Coastguard Worker     bool operator!=(const KeyIterator& other) const {
230*635a8641SAndroid Build Coastguard Worker       return iter_ != other.iter_;
231*635a8641SAndroid Build Coastguard Worker     }
232*635a8641SAndroid Build Coastguard Worker   };
233*635a8641SAndroid Build Coastguard Worker 
AddInternal(V data)234*635a8641SAndroid Build Coastguard Worker   KeyType AddInternal(V data) {
235*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
236*635a8641SAndroid Build Coastguard Worker     DCHECK(!check_on_null_data_ || data);
237*635a8641SAndroid Build Coastguard Worker     KeyType this_id = next_id_;
238*635a8641SAndroid Build Coastguard Worker     DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
239*635a8641SAndroid Build Coastguard Worker     data_[this_id] = std::move(data);
240*635a8641SAndroid Build Coastguard Worker     next_id_++;
241*635a8641SAndroid Build Coastguard Worker     return this_id;
242*635a8641SAndroid Build Coastguard Worker   }
243*635a8641SAndroid Build Coastguard Worker 
AddWithIDInternal(V data,KeyType id)244*635a8641SAndroid Build Coastguard Worker   void AddWithIDInternal(V data, KeyType id) {
245*635a8641SAndroid Build Coastguard Worker     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
246*635a8641SAndroid Build Coastguard Worker     DCHECK(!check_on_null_data_ || data);
247*635a8641SAndroid Build Coastguard Worker     if (IsRemoved(id)) {
248*635a8641SAndroid Build Coastguard Worker       removed_ids_.erase(id);
249*635a8641SAndroid Build Coastguard Worker     } else {
250*635a8641SAndroid Build Coastguard Worker       DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
251*635a8641SAndroid Build Coastguard Worker     }
252*635a8641SAndroid Build Coastguard Worker     data_[id] = std::move(data);
253*635a8641SAndroid Build Coastguard Worker   }
254*635a8641SAndroid Build Coastguard Worker 
IsRemoved(KeyType key)255*635a8641SAndroid Build Coastguard Worker   bool IsRemoved(KeyType key) const {
256*635a8641SAndroid Build Coastguard Worker     return removed_ids_.find(key) != removed_ids_.end();
257*635a8641SAndroid Build Coastguard Worker   }
258*635a8641SAndroid Build Coastguard Worker 
Compact()259*635a8641SAndroid Build Coastguard Worker   void Compact() {
260*635a8641SAndroid Build Coastguard Worker     DCHECK_EQ(0, iteration_depth_);
261*635a8641SAndroid Build Coastguard Worker     for (const auto& i : removed_ids_)
262*635a8641SAndroid Build Coastguard Worker       data_.erase(i);
263*635a8641SAndroid Build Coastguard Worker     removed_ids_.clear();
264*635a8641SAndroid Build Coastguard Worker   }
265*635a8641SAndroid Build Coastguard Worker 
266*635a8641SAndroid Build Coastguard Worker   // Keep track of how many iterators are currently iterating on us to safely
267*635a8641SAndroid Build Coastguard Worker   // handle removing items during iteration.
268*635a8641SAndroid Build Coastguard Worker   int iteration_depth_;
269*635a8641SAndroid Build Coastguard Worker 
270*635a8641SAndroid Build Coastguard Worker   // Keep set of IDs that should be removed after the outermost iteration has
271*635a8641SAndroid Build Coastguard Worker   // finished. This way we manage to not invalidate the iterator when an element
272*635a8641SAndroid Build Coastguard Worker   // is removed.
273*635a8641SAndroid Build Coastguard Worker   base::flat_set<KeyType> removed_ids_;
274*635a8641SAndroid Build Coastguard Worker 
275*635a8641SAndroid Build Coastguard Worker   // The next ID that we will return from Add()
276*635a8641SAndroid Build Coastguard Worker   KeyType next_id_;
277*635a8641SAndroid Build Coastguard Worker 
278*635a8641SAndroid Build Coastguard Worker   HashTable data_;
279*635a8641SAndroid Build Coastguard Worker 
280*635a8641SAndroid Build Coastguard Worker   // See description above setter.
281*635a8641SAndroid Build Coastguard Worker   bool check_on_null_data_;
282*635a8641SAndroid Build Coastguard Worker 
283*635a8641SAndroid Build Coastguard Worker   SEQUENCE_CHECKER(sequence_checker_);
284*635a8641SAndroid Build Coastguard Worker 
285*635a8641SAndroid Build Coastguard Worker   DISALLOW_COPY_AND_ASSIGN(IDMap);
286*635a8641SAndroid Build Coastguard Worker };
287*635a8641SAndroid Build Coastguard Worker 
288*635a8641SAndroid Build Coastguard Worker }  // namespace base
289*635a8641SAndroid Build Coastguard Worker 
290*635a8641SAndroid Build Coastguard Worker #endif  // BASE_CONTAINERS_ID_MAP_H_
291