blob: c0976b0557d9d15a5e51e8eae5b31afc7fd0f733 [file] [log] [blame]
[email protected]4bc4f122011-03-08 02:07:291// 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
[email protected]925d5d602009-08-19 14:56:385#ifndef BASE_ID_MAP_H_
6#define BASE_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>
[email protected]9de09f82009-08-17 20:13:5310#include <set>
11
[email protected]14c1c232013-06-11 17:52:4412#include "base/containers/hash_tables.h"
initial.commitd7cae122008-07-26 21:49:3813#include "base/logging.h"
avi9b6f42932015-12-26 22:15:1414#include "base/macros.h"
jsbell41359ee2015-11-16 19:43:3415#include "base/sequence_checker.h"
initial.commitd7cae122008-07-26 21:49:3816
[email protected]9e7e0e02010-01-25 23:25:1617// Ownership semantics - own pointer means the pointer is deleted in Remove()
18// & during destruction
19enum IDMapOwnershipSemantics {
20 IDMapExternalPointer,
21 IDMapOwnPointer
22};
23
initial.commitd7cae122008-07-26 21:49:3824// This object maintains a list of IDs that can be quickly converted to
25// pointers to objects. It is implemented as a hash table, optimized for
26// relatively small data sets (in the common case, there will be exactly one
27// item in the list).
28//
29// Items can be inserted into the container with arbitrary ID, but the caller
30// must ensure they are unique. Inserting IDs and relying on automatically
31// generated ones is not allowed because they can collide.
[email protected]9e7e0e02010-01-25 23:25:1632//
33// This class does not have a virtual destructor, do not inherit from it when
34// ownership semantics are set to own because pointers will leak.
jkarlin7bc034c2015-09-25 20:45:4735template <typename T,
36 IDMapOwnershipSemantics OS = IDMapExternalPointer,
37 typename K = int32_t>
jsbell41359ee2015-11-16 19:43:3438class IDMap {
[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:
[email protected]9e7e0e02010-01-25 23:25:1643 typedef base::hash_map<KeyType, T*> HashTable;
initial.commitd7cae122008-07-26 21:49:3844
45 public:
[email protected]9de09f82009-08-17 20:13:5346 IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
jsbell41359ee2015-11-16 19:43:3447 // A number of consumers of IDMap create it on one thread but always
48 // access it from a different, but consistent, thread (or sequence)
fdoraye2b19a12016-07-29 02:30:1649 // post-construction. The first call to CalledOnValidSequence() will re-bind
50 // it.
jsbell41359ee2015-11-16 19:43:3451 sequence_checker_.DetachFromSequence();
initial.commitd7cae122008-07-26 21:49:3852 }
53
[email protected]9e7e0e02010-01-25 23:25:1654 ~IDMap() {
jsbell41359ee2015-11-16 19:43:3455 // Many IDMap's are static, and hence will be destroyed on the main
56 // thread. However, all the accesses may take place on another thread (or
57 // sequence), such as the IO thread. Detaching again to clean this up.
58 sequence_checker_.DetachFromSequence();
[email protected]9e7e0e02010-01-25 23:25:1659 Releaser<OS, 0>::release_all(&data_);
60 }
61
michaelnbfea6ec2014-12-09 23:16:1362 // Sets whether Add and Replace should DCHECK if passed in NULL data.
63 // Default is false.
[email protected]a6ed9432009-07-01 22:35:2664 void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
65
initial.commitd7cae122008-07-26 21:49:3866 // Adds a view with an automatically generated unique ID. See AddWithID.
[email protected]9e7e0e02010-01-25 23:25:1667 KeyType Add(T* data) {
fdoraye2b19a12016-07-29 02:30:1668 DCHECK(sequence_checker_.CalledOnValidSequence());
michaelnbfea6ec2014-12-09 23:16:1369 DCHECK(!check_on_null_data_ || data);
[email protected]9e7e0e02010-01-25 23:25:1670 KeyType this_id = next_id_;
initial.commitd7cae122008-07-26 21:49:3871 DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
72 data_[this_id] = data;
73 next_id_++;
74 return this_id;
75 }
76
77 // Adds a new data member with the specified ID. The ID must not be in
78 // the list. The caller either must generate all unique IDs itself and use
79 // this function, or allow this object to generate IDs and call Add. These
80 // two methods may not be mixed, or duplicate IDs may be generated
[email protected]9e7e0e02010-01-25 23:25:1681 void AddWithID(T* data, KeyType id) {
fdoraye2b19a12016-07-29 02:30:1682 DCHECK(sequence_checker_.CalledOnValidSequence());
michaelnbfea6ec2014-12-09 23:16:1383 DCHECK(!check_on_null_data_ || data);
initial.commitd7cae122008-07-26 21:49:3884 DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
85 data_[id] = data;
86 }
87
[email protected]9e7e0e02010-01-25 23:25:1688 void Remove(KeyType id) {
fdoraye2b19a12016-07-29 02:30:1689 DCHECK(sequence_checker_.CalledOnValidSequence());
[email protected]9de09f82009-08-17 20:13:5390 typename HashTable::iterator i = data_.find(id);
initial.commitd7cae122008-07-26 21:49:3891 if (i == data_.end()) {
92 NOTREACHED() << "Attempting to remove an item not in the list";
93 return;
94 }
[email protected]9de09f82009-08-17 20:13:5395
[email protected]9e7e0e02010-01-25 23:25:1696 if (iteration_depth_ == 0) {
97 Releaser<OS, 0>::release(i->second);
[email protected]9de09f82009-08-17 20:13:5398 data_.erase(i);
[email protected]9e7e0e02010-01-25 23:25:1699 } else {
[email protected]9de09f82009-08-17 20:13:53100 removed_ids_.insert(id);
[email protected]9e7e0e02010-01-25 23:25:16101 }
initial.commitd7cae122008-07-26 21:49:38102 }
103
michaelnbfea6ec2014-12-09 23:16:13104 // Replaces the value for |id| with |new_data| and returns a pointer to the
105 // existing value. If there is no entry for |id|, the map is not altered and
106 // nullptr is returned. The OwnershipSemantics of the map have no effect on
107 // how the existing value is treated, the IDMap does not delete the existing
108 // value being replaced.
109 T* Replace(KeyType id, T* new_data) {
fdoraye2b19a12016-07-29 02:30:16110 DCHECK(sequence_checker_.CalledOnValidSequence());
michaelnbfea6ec2014-12-09 23:16:13111 DCHECK(!check_on_null_data_ || new_data);
112 typename HashTable::iterator i = data_.find(id);
113 if (i == data_.end()) {
114 NOTREACHED() << "Attempting to replace an item not in the list";
115 return nullptr;
116 }
117
118 T* temp = i->second;
119 i->second = new_data;
120 return temp;
121 }
122
[email protected]fea0b9612012-10-29 21:36:22123 void Clear() {
fdoraye2b19a12016-07-29 02:30:16124 DCHECK(sequence_checker_.CalledOnValidSequence());
[email protected]fea0b9612012-10-29 21:36:22125 if (iteration_depth_ == 0) {
126 Releaser<OS, 0>::release_all(&data_);
127 } else {
128 for (typename HashTable::iterator i = data_.begin();
129 i != data_.end(); ++i)
130 removed_ids_.insert(i->first);
131 }
132 }
133
initial.commitd7cae122008-07-26 21:49:38134 bool IsEmpty() const {
fdoraye2b19a12016-07-29 02:30:16135 DCHECK(sequence_checker_.CalledOnValidSequence());
[email protected]2de069e2010-02-16 09:15:38136 return size() == 0u;
initial.commitd7cae122008-07-26 21:49:38137 }
138
[email protected]9e7e0e02010-01-25 23:25:16139 T* Lookup(KeyType id) const {
fdoraye2b19a12016-07-29 02:30:16140 DCHECK(sequence_checker_.CalledOnValidSequence());
[email protected]9de09f82009-08-17 20:13:53141 typename HashTable::const_iterator i = data_.find(id);
initial.commitd7cae122008-07-26 21:49:38142 if (i == data_.end())
143 return NULL;
144 return i->second;
145 }
146
147 size_t size() const {
fdoraye2b19a12016-07-29 02:30:16148 DCHECK(sequence_checker_.CalledOnValidSequence());
[email protected]2de069e2010-02-16 09:15:38149 return data_.size() - removed_ids_.size();
initial.commitd7cae122008-07-26 21:49:38150 }
151
[email protected]0c8b8942012-10-27 01:03:01152#if defined(UNIT_TEST)
153 int iteration_depth() const {
154 return iteration_depth_;
155 }
156#endif // defined(UNIT_TEST)
157
[email protected]9de09f82009-08-17 20:13:53158 // It is safe to remove elements from the map during iteration. All iterators
159 // will remain valid.
160 template<class ReturnType>
161 class Iterator {
162 public:
bcwhite9c5f4e22016-02-22 22:16:10163 Iterator(IDMap<T, OS, K>* map)
[email protected]9de09f82009-08-17 20:13:53164 : map_(map),
165 iter_(map_->data_.begin()) {
[email protected]0c8b8942012-10-27 01:03:01166 Init();
167 }
168
169 Iterator(const Iterator& iter)
170 : map_(iter.map_),
171 iter_(iter.iter_) {
172 Init();
173 }
174
175 const Iterator& operator=(const Iterator& iter) {
176 map_ = iter.map;
177 iter_ = iter.iter;
178 Init();
179 return *this;
[email protected]9de09f82009-08-17 20:13:53180 }
181
182 ~Iterator() {
fdoraye2b19a12016-07-29 02:30:16183 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
[email protected]0c8b8942012-10-27 01:03:01184
185 // We're going to decrement iteration depth. Make sure it's greater than
186 // zero so that it doesn't become negative.
187 DCHECK_LT(0, map_->iteration_depth_);
188
[email protected]9de09f82009-08-17 20:13:53189 if (--map_->iteration_depth_ == 0)
190 map_->Compact();
191 }
192
193 bool IsAtEnd() const {
fdoraye2b19a12016-07-29 02:30:16194 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
[email protected]9de09f82009-08-17 20:13:53195 return iter_ == map_->data_.end();
196 }
197
[email protected]9e7e0e02010-01-25 23:25:16198 KeyType GetCurrentKey() const {
fdoraye2b19a12016-07-29 02:30:16199 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
[email protected]9de09f82009-08-17 20:13:53200 return iter_->first;
201 }
202
203 ReturnType* GetCurrentValue() const {
fdoraye2b19a12016-07-29 02:30:16204 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
[email protected]9de09f82009-08-17 20:13:53205 return iter_->second;
206 }
207
208 void Advance() {
fdoraye2b19a12016-07-29 02:30:16209 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
[email protected]9de09f82009-08-17 20:13:53210 ++iter_;
211 SkipRemovedEntries();
212 }
213
214 private:
[email protected]0c8b8942012-10-27 01:03:01215 void Init() {
fdoraye2b19a12016-07-29 02:30:16216 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
[email protected]0c8b8942012-10-27 01:03:01217 ++map_->iteration_depth_;
218 SkipRemovedEntries();
219 }
220
[email protected]9de09f82009-08-17 20:13:53221 void SkipRemovedEntries() {
222 while (iter_ != map_->data_.end() &&
223 map_->removed_ids_.find(iter_->first) !=
224 map_->removed_ids_.end()) {
225 ++iter_;
226 }
227 }
228
bcwhite9c5f4e22016-02-22 22:16:10229 IDMap<T, OS, K>* map_;
[email protected]9de09f82009-08-17 20:13:53230 typename HashTable::const_iterator iter_;
231 };
232
233 typedef Iterator<T> iterator;
234 typedef Iterator<const T> const_iterator;
235
236 private:
[email protected]9e7e0e02010-01-25 23:25:16237
238 // The dummy parameter is there because C++ standard does not allow
239 // explicitly specialized templates inside classes
240 template<IDMapOwnershipSemantics OI, int dummy> struct Releaser {
241 static inline void release(T* ptr) {}
242 static inline void release_all(HashTable* table) {}
243 };
244
245 template<int dummy> struct Releaser<IDMapOwnPointer, dummy> {
246 static inline void release(T* ptr) { delete ptr;}
247 static inline void release_all(HashTable* table) {
248 for (typename HashTable::iterator i = table->begin();
249 i != table->end(); ++i) {
250 delete i->second;
251 }
252 table->clear();
253 }
254 };
255
[email protected]9de09f82009-08-17 20:13:53256 void Compact() {
257 DCHECK_EQ(0, iteration_depth_);
jkarlin7bc034c2015-09-25 20:45:47258 for (const auto& i : removed_ids_)
259 Remove(i);
[email protected]9de09f82009-08-17 20:13:53260 removed_ids_.clear();
261 }
262
263 // Keep track of how many iterators are currently iterating on us to safely
264 // handle removing items during iteration.
265 int iteration_depth_;
266
267 // Keep set of IDs that should be removed after the outermost iteration has
268 // finished. This way we manage to not invalidate the iterator when an element
269 // is removed.
[email protected]9e7e0e02010-01-25 23:25:16270 std::set<KeyType> removed_ids_;
[email protected]9de09f82009-08-17 20:13:53271
initial.commitd7cae122008-07-26 21:49:38272 // The next ID that we will return from Add()
[email protected]9e7e0e02010-01-25 23:25:16273 KeyType next_id_;
initial.commitd7cae122008-07-26 21:49:38274
275 HashTable data_;
[email protected]a6ed9432009-07-01 22:35:26276
[email protected]a6ed9432009-07-01 22:35:26277 // See description above setter.
278 bool check_on_null_data_;
[email protected]9de09f82009-08-17 20:13:53279
jsbell41359ee2015-11-16 19:43:34280 base::SequenceChecker sequence_checker_;
281
[email protected]9de09f82009-08-17 20:13:53282 DISALLOW_COPY_AND_ASSIGN(IDMap);
initial.commitd7cae122008-07-26 21:49:38283};
284
[email protected]925d5d602009-08-19 14:56:38285#endif // BASE_ID_MAP_H_