blob: 8fb4d5ba419857eaf3322a8c28826255de335ef1 [file] [log] [blame]
Daniel Cheng3d1108de2022-09-07 23:42:041// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
Brett Wilsonf976d3f2017-08-18 17:23:395#ifndef BASE_CONTAINERS_ID_MAP_H_
6#define BASE_CONTAINERS_ID_MAP_H_
initial.commitd7cae122008-07-26 21:49:387
avi9b6f42932015-12-26 22:15:148#include <stddef.h>
jkarlin7bc034c2015-09-25 20:45:479#include <stdint.h>
brettw1ce49f62017-04-27 19:42:3210
David Sanders61357c82022-01-19 19:38:4311#include <iterator>
12#include <ostream>
aeliasaed214d02016-09-24 01:26:4213#include <type_traits>
brettw1ce49f62017-04-27 19:42:3214#include <unordered_map>
aeliasaed214d02016-09-24 01:26:4215#include <utility>
[email protected]9de09f82009-08-17 20:13:5316
David Sanders61357c82022-01-19 19:38:4317#include "base/check.h"
Hans Wennborg7b533712020-06-22 20:52:2718#include "base/check_op.h"
brettw1ce49f62017-04-27 19:42:3219#include "base/containers/flat_set.h"
Keishi Hattori0e45c022021-11-27 09:25:5220#include "base/memory/raw_ptr.h"
Hans Wennborg7b533712020-06-22 20:52:2721#include "base/notreached.h"
jsbell41359ee2015-11-16 19:43:3422#include "base/sequence_checker.h"
initial.commitd7cae122008-07-26 21:49:3823
Brett Wilsonf976d3f2017-08-18 17:23:3924namespace base {
25
initial.commitd7cae122008-07-26 21:49:3826// This object maintains a list of IDs that can be quickly converted to
27// pointers to objects. It is implemented as a hash table, optimized for
28// relatively small data sets (in the common case, there will be exactly one
29// item in the list).
30//
31// Items can be inserted into the container with arbitrary ID, but the caller
32// must ensure they are unique. Inserting IDs and relying on automatically
33// generated ones is not allowed because they can collide.
rlandayde24c6d2016-12-01 03:05:4034
35// The map's value type (the V param) can be any dereferenceable type, such as a
36// raw pointer or smart pointer
37template <typename V, typename K = int32_t>
38class IDMap final {
[email protected]b8b670812014-05-27 18:10:0639 public:
jkarlin7bc034c2015-09-25 20:45:4740 using KeyType = K;
[email protected]b8b670812014-05-27 18:10:0641
42 private:
rlandayde24c6d2016-12-01 03:05:4043 using T = typename std::remove_reference<decltype(*V())>::type;
brettw1ce49f62017-04-27 19:42:3244
45 using HashTable = std::unordered_map<KeyType, V>;
initial.commitd7cae122008-07-26 21:49:3846
47 public:
[email protected]9de09f82009-08-17 20:13:5348 IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
jsbell41359ee2015-11-16 19:43:3449 // A number of consumers of IDMap create it on one thread but always
50 // access it from a different, but consistent, thread (or sequence)
fdoraye2b19a12016-07-29 02:30:1651 // post-construction. The first call to CalledOnValidSequence() will re-bind
52 // it.
tzikfd754ee2018-03-28 15:49:2853 DETACH_FROM_SEQUENCE(sequence_checker_);
initial.commitd7cae122008-07-26 21:49:3854 }
55
Lei Zhang6825df462020-06-23 16:17:4356 IDMap(const IDMap&) = delete;
57 IDMap& operator=(const IDMap&) = delete;
58
[email protected]9e7e0e02010-01-25 23:25:1659 ~IDMap() {
jsbell41359ee2015-11-16 19:43:3460 // Many IDMap's are static, and hence will be destroyed on the main
61 // thread. However, all the accesses may take place on another thread (or
62 // sequence), such as the IO thread. Detaching again to clean this up.
tzikfd754ee2018-03-28 15:49:2863 DETACH_FROM_SEQUENCE(sequence_checker_);
[email protected]9e7e0e02010-01-25 23:25:1664 }
65
michaelnbfea6ec2014-12-09 23:16:1366 // Sets whether Add and Replace should DCHECK if passed in NULL data.
67 // Default is false.
[email protected]a6ed9432009-07-01 22:35:2668 void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
69
initial.commitd7cae122008-07-26 21:49:3870 // Adds a view with an automatically generated unique ID. See AddWithID.
rlanday6eada0322016-11-30 18:59:3071 KeyType Add(V data) { return AddInternal(std::move(data)); }
initial.commitd7cae122008-07-26 21:49:3872
73 // Adds a new data member with the specified ID. The ID must not be in
74 // the list. The caller either must generate all unique IDs itself and use
75 // this function, or allow this object to generate IDs and call Add. These
aeliasaed214d02016-09-24 01:26:4276 // two methods may not be mixed, or duplicate IDs may be generated.
rlanday6eada0322016-11-30 18:59:3077 void AddWithID(V data, KeyType id) { AddWithIDInternal(std::move(data), id); }
initial.commitd7cae122008-07-26 21:49:3878
[email protected]9e7e0e02010-01-25 23:25:1679 void Remove(KeyType id) {
tzikfd754ee2018-03-28 15:49:2880 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]9de09f82009-08-17 20:13:5381 typename HashTable::iterator i = data_.find(id);
tzikd1646672018-03-28 20:41:1382 if (i == data_.end() || IsRemoved(id)) {
initial.commitd7cae122008-07-26 21:49:3883 NOTREACHED() << "Attempting to remove an item not in the list";
84 return;
85 }
[email protected]9de09f82009-08-17 20:13:5386
[email protected]9e7e0e02010-01-25 23:25:1687 if (iteration_depth_ == 0) {
[email protected]9de09f82009-08-17 20:13:5388 data_.erase(i);
[email protected]9e7e0e02010-01-25 23:25:1689 } else {
[email protected]9de09f82009-08-17 20:13:5390 removed_ids_.insert(id);
[email protected]9e7e0e02010-01-25 23:25:1691 }
initial.commitd7cae122008-07-26 21:49:3892 }
93
sungmann.cho82e601f92017-02-17 04:43:1394 // Replaces the value for |id| with |new_data| and returns the existing value.
95 // Should only be called with an already added id.
aeliasaed214d02016-09-24 01:26:4296 V Replace(KeyType id, V new_data) {
tzikfd754ee2018-03-28 15:49:2897 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
michaelnbfea6ec2014-12-09 23:16:1398 DCHECK(!check_on_null_data_ || new_data);
99 typename HashTable::iterator i = data_.find(id);
aeliasaed214d02016-09-24 01:26:42100 DCHECK(i != data_.end());
tzikd1646672018-03-28 20:41:13101 DCHECK(!IsRemoved(id));
michaelnbfea6ec2014-12-09 23:16:13102
tzikd1646672018-03-28 20:41:13103 using std::swap;
104 swap(i->second, new_data);
aeliasaed214d02016-09-24 01:26:42105 return new_data;
michaelnbfea6ec2014-12-09 23:16:13106 }
107
[email protected]fea0b9612012-10-29 21:36:22108 void Clear() {
tzikfd754ee2018-03-28 15:49:28109 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]fea0b9612012-10-29 21:36:22110 if (iteration_depth_ == 0) {
aeliasaed214d02016-09-24 01:26:42111 data_.clear();
[email protected]fea0b9612012-10-29 21:36:22112 } else {
tzikc5837e22018-03-29 05:31:15113 removed_ids_.reserve(data_.size());
114 removed_ids_.insert(KeyIterator(data_.begin()), KeyIterator(data_.end()));
[email protected]fea0b9612012-10-29 21:36:22115 }
116 }
117
initial.commitd7cae122008-07-26 21:49:38118 bool IsEmpty() const {
tzikfd754ee2018-03-28 15:49:28119 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]2de069e2010-02-16 09:15:38120 return size() == 0u;
initial.commitd7cae122008-07-26 21:49:38121 }
122
[email protected]9e7e0e02010-01-25 23:25:16123 T* Lookup(KeyType id) const {
tzikfd754ee2018-03-28 15:49:28124 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]9de09f82009-08-17 20:13:53125 typename HashTable::const_iterator i = data_.find(id);
tzikd1646672018-03-28 20:41:13126 if (i == data_.end() || !i->second || IsRemoved(id))
aeliasaed214d02016-09-24 01:26:42127 return nullptr;
128 return &*i->second;
initial.commitd7cae122008-07-26 21:49:38129 }
130
131 size_t size() const {
tzikfd754ee2018-03-28 15:49:28132 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]2de069e2010-02-16 09:15:38133 return data_.size() - removed_ids_.size();
initial.commitd7cae122008-07-26 21:49:38134 }
135
[email protected]0c8b8942012-10-27 01:03:01136#if defined(UNIT_TEST)
137 int iteration_depth() const {
138 return iteration_depth_;
139 }
140#endif // defined(UNIT_TEST)
141
[email protected]9de09f82009-08-17 20:13:53142 // It is safe to remove elements from the map during iteration. All iterators
143 // will remain valid.
144 template<class ReturnType>
145 class Iterator {
146 public:
rlandayde24c6d2016-12-01 03:05:40147 Iterator(IDMap<V, K>* map) : map_(map), iter_(map_->data_.begin()) {
[email protected]0c8b8942012-10-27 01:03:01148 Init();
149 }
150
151 Iterator(const Iterator& iter)
152 : map_(iter.map_),
153 iter_(iter.iter_) {
154 Init();
155 }
156
157 const Iterator& operator=(const Iterator& iter) {
158 map_ = iter.map;
159 iter_ = iter.iter;
160 Init();
161 return *this;
[email protected]9de09f82009-08-17 20:13:53162 }
163
164 ~Iterator() {
tzikfd754ee2018-03-28 15:49:28165 DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
[email protected]0c8b8942012-10-27 01:03:01166
167 // We're going to decrement iteration depth. Make sure it's greater than
168 // zero so that it doesn't become negative.
169 DCHECK_LT(0, map_->iteration_depth_);
170
[email protected]9de09f82009-08-17 20:13:53171 if (--map_->iteration_depth_ == 0)
172 map_->Compact();
173 }
174
175 bool IsAtEnd() const {
tzikfd754ee2018-03-28 15:49:28176 DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
[email protected]9de09f82009-08-17 20:13:53177 return iter_ == map_->data_.end();
178 }
179
[email protected]9e7e0e02010-01-25 23:25:16180 KeyType GetCurrentKey() const {
tzikfd754ee2018-03-28 15:49:28181 DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
[email protected]9de09f82009-08-17 20:13:53182 return iter_->first;
183 }
184
185 ReturnType* GetCurrentValue() const {
tzikfd754ee2018-03-28 15:49:28186 DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
tzikd1646672018-03-28 20:41:13187 if (!iter_->second || map_->IsRemoved(iter_->first))
188 return nullptr;
aeliasaed214d02016-09-24 01:26:42189 return &*iter_->second;
[email protected]9de09f82009-08-17 20:13:53190 }
191
192 void Advance() {
tzikfd754ee2018-03-28 15:49:28193 DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
[email protected]9de09f82009-08-17 20:13:53194 ++iter_;
195 SkipRemovedEntries();
196 }
197
198 private:
[email protected]0c8b8942012-10-27 01:03:01199 void Init() {
tzikfd754ee2018-03-28 15:49:28200 DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_);
[email protected]0c8b8942012-10-27 01:03:01201 ++map_->iteration_depth_;
202 SkipRemovedEntries();
203 }
204
[email protected]9de09f82009-08-17 20:13:53205 void SkipRemovedEntries() {
tzikd1646672018-03-28 20:41:13206 while (iter_ != map_->data_.end() && map_->IsRemoved(iter_->first))
[email protected]9de09f82009-08-17 20:13:53207 ++iter_;
[email protected]9de09f82009-08-17 20:13:53208 }
209
Keishi Hattori0e45c022021-11-27 09:25:52210 raw_ptr<IDMap<V, K>> map_;
[email protected]9de09f82009-08-17 20:13:53211 typename HashTable::const_iterator iter_;
212 };
213
214 typedef Iterator<T> iterator;
215 typedef Iterator<const T> const_iterator;
216
217 private:
tzikc5837e22018-03-29 05:31:15218 // Transforms a map iterator to an iterator on the keys of the map.
219 // Used by Clear() to populate |removed_ids_| in bulk.
Peter Kasting0acb7f5ef2022-05-05 23:57:27220 struct KeyIterator {
221 using iterator_category = std::forward_iterator_tag;
222 using value_type = KeyType;
223 using difference_type = std::ptrdiff_t;
224 using pointer = KeyType*;
225 using reference = KeyType&;
226
tzikc5837e22018-03-29 05:31:15227 using inner_iterator = typename HashTable::iterator;
228 inner_iterator iter_;
229
230 KeyIterator(inner_iterator iter) : iter_(iter) {}
231 KeyType operator*() const { return iter_->first; }
232 KeyIterator& operator++() {
233 ++iter_;
234 return *this;
235 }
236 KeyIterator operator++(int) { return KeyIterator(iter_++); }
237 bool operator==(const KeyIterator& other) const {
238 return iter_ == other.iter_;
239 }
240 bool operator!=(const KeyIterator& other) const {
241 return iter_ != other.iter_;
242 }
243 };
244
aeliasaed214d02016-09-24 01:26:42245 KeyType AddInternal(V data) {
tzikfd754ee2018-03-28 15:49:28246 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
aeliasaed214d02016-09-24 01:26:42247 DCHECK(!check_on_null_data_ || data);
248 KeyType this_id = next_id_;
249 DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
250 data_[this_id] = std::move(data);
251 next_id_++;
252 return this_id;
253 }
[email protected]9e7e0e02010-01-25 23:25:16254
aeliasaed214d02016-09-24 01:26:42255 void AddWithIDInternal(V data, KeyType id) {
tzikfd754ee2018-03-28 15:49:28256 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
aeliasaed214d02016-09-24 01:26:42257 DCHECK(!check_on_null_data_ || data);
tzikd1646672018-03-28 20:41:13258 if (IsRemoved(id)) {
259 removed_ids_.erase(id);
260 } else {
261 DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
262 }
aeliasaed214d02016-09-24 01:26:42263 data_[id] = std::move(data);
264 }
[email protected]9e7e0e02010-01-25 23:25:16265
tzikd1646672018-03-28 20:41:13266 bool IsRemoved(KeyType key) const {
267 return removed_ids_.find(key) != removed_ids_.end();
268 }
269
[email protected]9de09f82009-08-17 20:13:53270 void Compact() {
271 DCHECK_EQ(0, iteration_depth_);
jkarlin7bc034c2015-09-25 20:45:47272 for (const auto& i : removed_ids_)
tzikd1646672018-03-28 20:41:13273 data_.erase(i);
[email protected]9de09f82009-08-17 20:13:53274 removed_ids_.clear();
275 }
276
277 // Keep track of how many iterators are currently iterating on us to safely
278 // handle removing items during iteration.
279 int iteration_depth_;
280
281 // Keep set of IDs that should be removed after the outermost iteration has
282 // finished. This way we manage to not invalidate the iterator when an element
283 // is removed.
brettw1ce49f62017-04-27 19:42:32284 base::flat_set<KeyType> removed_ids_;
[email protected]9de09f82009-08-17 20:13:53285
initial.commitd7cae122008-07-26 21:49:38286 // The next ID that we will return from Add()
[email protected]9e7e0e02010-01-25 23:25:16287 KeyType next_id_;
initial.commitd7cae122008-07-26 21:49:38288
289 HashTable data_;
[email protected]a6ed9432009-07-01 22:35:26290
[email protected]a6ed9432009-07-01 22:35:26291 // See description above setter.
292 bool check_on_null_data_;
[email protected]9de09f82009-08-17 20:13:53293
tzikfd754ee2018-03-28 15:49:28294 SEQUENCE_CHECKER(sequence_checker_);
initial.commitd7cae122008-07-26 21:49:38295};
296
Brett Wilsonf976d3f2017-08-18 17:23:39297} // namespace base
298
299#endif // BASE_CONTAINERS_ID_MAP_H_