blob: c0976b0557d9d15a5e51e8eae5b31afc7fd0f733 [file] [log] [blame]
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +09001// Copyright (c) 2011 The Chromium Authors. All rights reserved.
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef BASE_ID_MAP_H_
6#define BASE_ID_MAP_H_
7
avia6a6a682015-12-27 07:15:14 +09008#include <stddef.h>
jkarlina5762912015-09-26 05:45:47 +09009#include <stdint.h>
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090010#include <set>
11
brettw@chromium.org30230a82013-06-12 02:52:44 +090012#include "base/containers/hash_tables.h"
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090013#include "base/logging.h"
avia6a6a682015-12-27 07:15:14 +090014#include "base/macros.h"
jsbell5c49d682015-11-17 04:43:34 +090015#include "base/sequence_checker.h"
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090016
neb@chromium.orged1f8d72010-01-26 08:25:16 +090017// Ownership semantics - own pointer means the pointer is deleted in Remove()
18// & during destruction
19enum IDMapOwnershipSemantics {
20 IDMapExternalPointer,
21 IDMapOwnPointer
22};
23
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090024// 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.
neb@chromium.orged1f8d72010-01-26 08:25:16 +090032//
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.
jkarlina5762912015-09-26 05:45:47 +090035template <typename T,
36 IDMapOwnershipSemantics OS = IDMapExternalPointer,
37 typename K = int32_t>
jsbell5c49d682015-11-17 04:43:34 +090038class IDMap {
mnaganov@chromium.org0f3c4e62014-05-28 03:10:06 +090039 public:
jkarlina5762912015-09-26 05:45:47 +090040 using KeyType = K;
mnaganov@chromium.org0f3c4e62014-05-28 03:10:06 +090041
42 private:
neb@chromium.orged1f8d72010-01-26 08:25:16 +090043 typedef base::hash_map<KeyType, T*> HashTable;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090044
45 public:
46 IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
jsbell5c49d682015-11-17 04:43:34 +090047 // A number of consumers of IDMap create it on one thread but always
48 // access it from a different, but consistent, thread (or sequence)
fdoraye4a58272016-07-29 11:30:16 +090049 // post-construction. The first call to CalledOnValidSequence() will re-bind
50 // it.
jsbell5c49d682015-11-17 04:43:34 +090051 sequence_checker_.DetachFromSequence();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090052 }
53
neb@chromium.orged1f8d72010-01-26 08:25:16 +090054 ~IDMap() {
jsbell5c49d682015-11-17 04:43:34 +090055 // 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();
neb@chromium.orged1f8d72010-01-26 08:25:16 +090059 Releaser<OS, 0>::release_all(&data_);
60 }
61
michaeln699d8f52014-12-10 08:16:13 +090062 // Sets whether Add and Replace should DCHECK if passed in NULL data.
63 // Default is false.
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090064 void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
65
66 // Adds a view with an automatically generated unique ID. See AddWithID.
neb@chromium.orged1f8d72010-01-26 08:25:16 +090067 KeyType Add(T* data) {
fdoraye4a58272016-07-29 11:30:16 +090068 DCHECK(sequence_checker_.CalledOnValidSequence());
michaeln699d8f52014-12-10 08:16:13 +090069 DCHECK(!check_on_null_data_ || data);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090070 KeyType this_id = next_id_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090071 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
neb@chromium.orged1f8d72010-01-26 08:25:16 +090081 void AddWithID(T* data, KeyType id) {
fdoraye4a58272016-07-29 11:30:16 +090082 DCHECK(sequence_checker_.CalledOnValidSequence());
michaeln699d8f52014-12-10 08:16:13 +090083 DCHECK(!check_on_null_data_ || data);
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090084 DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
85 data_[id] = data;
86 }
87
neb@chromium.orged1f8d72010-01-26 08:25:16 +090088 void Remove(KeyType id) {
fdoraye4a58272016-07-29 11:30:16 +090089 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090090 typename HashTable::iterator i = data_.find(id);
91 if (i == data_.end()) {
92 NOTREACHED() << "Attempting to remove an item not in the list";
93 return;
94 }
95
neb@chromium.orged1f8d72010-01-26 08:25:16 +090096 if (iteration_depth_ == 0) {
97 Releaser<OS, 0>::release(i->second);
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090098 data_.erase(i);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090099 } else {
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900100 removed_ids_.insert(id);
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900101 }
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900102 }
103
michaeln699d8f52014-12-10 08:16:13 +0900104 // 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) {
fdoraye4a58272016-07-29 11:30:16 +0900110 DCHECK(sequence_checker_.CalledOnValidSequence());
michaeln699d8f52014-12-10 08:16:13 +0900111 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
jsbell@chromium.org8fe2a8f2012-10-30 06:36:22 +0900123 void Clear() {
fdoraye4a58272016-07-29 11:30:16 +0900124 DCHECK(sequence_checker_.CalledOnValidSequence());
jsbell@chromium.org8fe2a8f2012-10-30 06:36:22 +0900125 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
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900134 bool IsEmpty() const {
fdoraye4a58272016-07-29 11:30:16 +0900135 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org61544a02010-02-16 18:15:38 +0900136 return size() == 0u;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900137 }
138
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900139 T* Lookup(KeyType id) const {
fdoraye4a58272016-07-29 11:30:16 +0900140 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900141 typename HashTable::const_iterator i = data_.find(id);
142 if (i == data_.end())
143 return NULL;
144 return i->second;
145 }
146
147 size_t size() const {
fdoraye4a58272016-07-29 11:30:16 +0900148 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org61544a02010-02-16 18:15:38 +0900149 return data_.size() - removed_ids_.size();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900150 }
151
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900152#if defined(UNIT_TEST)
153 int iteration_depth() const {
154 return iteration_depth_;
155 }
156#endif // defined(UNIT_TEST)
157
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900158 // 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:
bcwhite0ac12b02016-02-23 07:16:10 +0900163 Iterator(IDMap<T, OS, K>* map)
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900164 : map_(map),
165 iter_(map_->data_.begin()) {
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900166 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;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900180 }
181
182 ~Iterator() {
fdoraye4a58272016-07-29 11:30:16 +0900183 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900184
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
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900189 if (--map_->iteration_depth_ == 0)
190 map_->Compact();
191 }
192
193 bool IsAtEnd() const {
fdoraye4a58272016-07-29 11:30:16 +0900194 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900195 return iter_ == map_->data_.end();
196 }
197
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900198 KeyType GetCurrentKey() const {
fdoraye4a58272016-07-29 11:30:16 +0900199 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900200 return iter_->first;
201 }
202
203 ReturnType* GetCurrentValue() const {
fdoraye4a58272016-07-29 11:30:16 +0900204 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900205 return iter_->second;
206 }
207
208 void Advance() {
fdoraye4a58272016-07-29 11:30:16 +0900209 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900210 ++iter_;
211 SkipRemovedEntries();
212 }
213
214 private:
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900215 void Init() {
fdoraye4a58272016-07-29 11:30:16 +0900216 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900217 ++map_->iteration_depth_;
218 SkipRemovedEntries();
219 }
220
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900221 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
bcwhite0ac12b02016-02-23 07:16:10 +0900229 IDMap<T, OS, K>* map_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900230 typename HashTable::const_iterator iter_;
231 };
232
233 typedef Iterator<T> iterator;
234 typedef Iterator<const T> const_iterator;
235
236 private:
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900237
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
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900256 void Compact() {
257 DCHECK_EQ(0, iteration_depth_);
jkarlina5762912015-09-26 05:45:47 +0900258 for (const auto& i : removed_ids_)
259 Remove(i);
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900260 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.
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900270 std::set<KeyType> removed_ids_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900271
272 // The next ID that we will return from Add()
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900273 KeyType next_id_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900274
275 HashTable data_;
276
277 // See description above setter.
278 bool check_on_null_data_;
279
jsbell5c49d682015-11-17 04:43:34 +0900280 base::SequenceChecker sequence_checker_;
281
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900282 DISALLOW_COPY_AND_ASSIGN(IDMap);
283};
284
285#endif // BASE_ID_MAP_H_