blob: d171fb14c102cefc5b5ec332315e8a7303f86b9c [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>
aelias9fd25702016-09-24 10:26:42 +090010#include <memory>
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090011#include <set>
aelias9fd25702016-09-24 10:26:42 +090012#include <type_traits>
13#include <utility>
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090014
brettw@chromium.org30230a82013-06-12 02:52:44 +090015#include "base/containers/hash_tables.h"
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090016#include "base/logging.h"
avia6a6a682015-12-27 07:15:14 +090017#include "base/macros.h"
jsbell5c49d682015-11-17 04:43:34 +090018#include "base/sequence_checker.h"
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090019
20// This object maintains a list of IDs that can be quickly converted to
21// pointers to objects. It is implemented as a hash table, optimized for
22// relatively small data sets (in the common case, there will be exactly one
23// item in the list).
24//
25// Items can be inserted into the container with arbitrary ID, but the caller
26// must ensure they are unique. Inserting IDs and relying on automatically
27// generated ones is not allowed because they can collide.
rlanday644d4f42016-12-01 12:05:40 +090028
29// The map's value type (the V param) can be any dereferenceable type, such as a
30// raw pointer or smart pointer
31template <typename V, typename K = int32_t>
32class IDMap final {
mnaganov@chromium.org0f3c4e62014-05-28 03:10:06 +090033 public:
jkarlina5762912015-09-26 05:45:47 +090034 using KeyType = K;
mnaganov@chromium.org0f3c4e62014-05-28 03:10:06 +090035
36 private:
rlanday644d4f42016-12-01 12:05:40 +090037 using T = typename std::remove_reference<decltype(*V())>::type;
aelias9fd25702016-09-24 10:26:42 +090038 using HashTable = base::hash_map<KeyType, V>;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090039
40 public:
41 IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
jsbell5c49d682015-11-17 04:43:34 +090042 // A number of consumers of IDMap create it on one thread but always
43 // access it from a different, but consistent, thread (or sequence)
fdoraye4a58272016-07-29 11:30:16 +090044 // post-construction. The first call to CalledOnValidSequence() will re-bind
45 // it.
jsbell5c49d682015-11-17 04:43:34 +090046 sequence_checker_.DetachFromSequence();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090047 }
48
neb@chromium.orged1f8d72010-01-26 08:25:16 +090049 ~IDMap() {
jsbell5c49d682015-11-17 04:43:34 +090050 // Many IDMap's are static, and hence will be destroyed on the main
51 // thread. However, all the accesses may take place on another thread (or
52 // sequence), such as the IO thread. Detaching again to clean this up.
53 sequence_checker_.DetachFromSequence();
neb@chromium.orged1f8d72010-01-26 08:25:16 +090054 }
55
michaeln699d8f52014-12-10 08:16:13 +090056 // Sets whether Add and Replace should DCHECK if passed in NULL data.
57 // Default is false.
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090058 void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
59
60 // Adds a view with an automatically generated unique ID. See AddWithID.
rlandayf5b01dd2016-12-01 03:59:30 +090061 KeyType Add(V data) { return AddInternal(std::move(data)); }
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090062
63 // Adds a new data member with the specified ID. The ID must not be in
64 // the list. The caller either must generate all unique IDs itself and use
65 // this function, or allow this object to generate IDs and call Add. These
aelias9fd25702016-09-24 10:26:42 +090066 // two methods may not be mixed, or duplicate IDs may be generated.
rlandayf5b01dd2016-12-01 03:59:30 +090067 void AddWithID(V data, KeyType id) { AddWithIDInternal(std::move(data), id); }
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090068
neb@chromium.orged1f8d72010-01-26 08:25:16 +090069 void Remove(KeyType id) {
fdoraye4a58272016-07-29 11:30:16 +090070 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090071 typename HashTable::iterator i = data_.find(id);
72 if (i == data_.end()) {
73 NOTREACHED() << "Attempting to remove an item not in the list";
74 return;
75 }
76
neb@chromium.orged1f8d72010-01-26 08:25:16 +090077 if (iteration_depth_ == 0) {
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090078 data_.erase(i);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090079 } else {
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090080 removed_ids_.insert(id);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090081 }
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090082 }
83
sungmann.cho9ae032a2017-02-17 13:43:13 +090084 // Replaces the value for |id| with |new_data| and returns the existing value.
85 // Should only be called with an already added id.
aelias9fd25702016-09-24 10:26:42 +090086 V Replace(KeyType id, V new_data) {
fdoraye4a58272016-07-29 11:30:16 +090087 DCHECK(sequence_checker_.CalledOnValidSequence());
michaeln699d8f52014-12-10 08:16:13 +090088 DCHECK(!check_on_null_data_ || new_data);
89 typename HashTable::iterator i = data_.find(id);
aelias9fd25702016-09-24 10:26:42 +090090 DCHECK(i != data_.end());
michaeln699d8f52014-12-10 08:16:13 +090091
aelias9fd25702016-09-24 10:26:42 +090092 std::swap(i->second, new_data);
93 return new_data;
michaeln699d8f52014-12-10 08:16:13 +090094 }
95
jsbell@chromium.org8fe2a8f2012-10-30 06:36:22 +090096 void Clear() {
fdoraye4a58272016-07-29 11:30:16 +090097 DCHECK(sequence_checker_.CalledOnValidSequence());
jsbell@chromium.org8fe2a8f2012-10-30 06:36:22 +090098 if (iteration_depth_ == 0) {
aelias9fd25702016-09-24 10:26:42 +090099 data_.clear();
jsbell@chromium.org8fe2a8f2012-10-30 06:36:22 +0900100 } else {
101 for (typename HashTable::iterator i = data_.begin();
102 i != data_.end(); ++i)
103 removed_ids_.insert(i->first);
104 }
105 }
106
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900107 bool IsEmpty() const {
fdoraye4a58272016-07-29 11:30:16 +0900108 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org61544a02010-02-16 18:15:38 +0900109 return size() == 0u;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900110 }
111
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900112 T* Lookup(KeyType id) const {
fdoraye4a58272016-07-29 11:30:16 +0900113 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900114 typename HashTable::const_iterator i = data_.find(id);
115 if (i == data_.end())
aelias9fd25702016-09-24 10:26:42 +0900116 return nullptr;
117 return &*i->second;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900118 }
119
120 size_t size() const {
fdoraye4a58272016-07-29 11:30:16 +0900121 DCHECK(sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org61544a02010-02-16 18:15:38 +0900122 return data_.size() - removed_ids_.size();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900123 }
124
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900125#if defined(UNIT_TEST)
126 int iteration_depth() const {
127 return iteration_depth_;
128 }
129#endif // defined(UNIT_TEST)
130
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900131 // It is safe to remove elements from the map during iteration. All iterators
132 // will remain valid.
133 template<class ReturnType>
134 class Iterator {
135 public:
rlanday644d4f42016-12-01 12:05:40 +0900136 Iterator(IDMap<V, K>* map) : map_(map), iter_(map_->data_.begin()) {
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900137 Init();
138 }
139
140 Iterator(const Iterator& iter)
141 : map_(iter.map_),
142 iter_(iter.iter_) {
143 Init();
144 }
145
146 const Iterator& operator=(const Iterator& iter) {
147 map_ = iter.map;
148 iter_ = iter.iter;
149 Init();
150 return *this;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900151 }
152
153 ~Iterator() {
fdoraye4a58272016-07-29 11:30:16 +0900154 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900155
156 // We're going to decrement iteration depth. Make sure it's greater than
157 // zero so that it doesn't become negative.
158 DCHECK_LT(0, map_->iteration_depth_);
159
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900160 if (--map_->iteration_depth_ == 0)
161 map_->Compact();
162 }
163
164 bool IsAtEnd() const {
fdoraye4a58272016-07-29 11:30:16 +0900165 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900166 return iter_ == map_->data_.end();
167 }
168
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900169 KeyType GetCurrentKey() const {
fdoraye4a58272016-07-29 11:30:16 +0900170 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900171 return iter_->first;
172 }
173
174 ReturnType* GetCurrentValue() const {
fdoraye4a58272016-07-29 11:30:16 +0900175 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
aelias9fd25702016-09-24 10:26:42 +0900176 return &*iter_->second;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900177 }
178
179 void Advance() {
fdoraye4a58272016-07-29 11:30:16 +0900180 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900181 ++iter_;
182 SkipRemovedEntries();
183 }
184
185 private:
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900186 void Init() {
fdoraye4a58272016-07-29 11:30:16 +0900187 DCHECK(map_->sequence_checker_.CalledOnValidSequence());
phajdan.jr@chromium.org7c63ed82012-10-27 10:03:01 +0900188 ++map_->iteration_depth_;
189 SkipRemovedEntries();
190 }
191
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900192 void SkipRemovedEntries() {
193 while (iter_ != map_->data_.end() &&
194 map_->removed_ids_.find(iter_->first) !=
195 map_->removed_ids_.end()) {
196 ++iter_;
197 }
198 }
199
rlanday644d4f42016-12-01 12:05:40 +0900200 IDMap<V, K>* map_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900201 typename HashTable::const_iterator iter_;
202 };
203
204 typedef Iterator<T> iterator;
205 typedef Iterator<const T> const_iterator;
206
207 private:
aelias9fd25702016-09-24 10:26:42 +0900208 KeyType AddInternal(V data) {
209 DCHECK(sequence_checker_.CalledOnValidSequence());
210 DCHECK(!check_on_null_data_ || data);
211 KeyType this_id = next_id_;
212 DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
213 data_[this_id] = std::move(data);
214 next_id_++;
215 return this_id;
216 }
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900217
aelias9fd25702016-09-24 10:26:42 +0900218 void AddWithIDInternal(V data, KeyType id) {
219 DCHECK(sequence_checker_.CalledOnValidSequence());
220 DCHECK(!check_on_null_data_ || data);
221 DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
222 data_[id] = std::move(data);
223 }
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900224
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900225 void Compact() {
226 DCHECK_EQ(0, iteration_depth_);
jkarlina5762912015-09-26 05:45:47 +0900227 for (const auto& i : removed_ids_)
228 Remove(i);
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900229 removed_ids_.clear();
230 }
231
232 // Keep track of how many iterators are currently iterating on us to safely
233 // handle removing items during iteration.
234 int iteration_depth_;
235
236 // Keep set of IDs that should be removed after the outermost iteration has
237 // finished. This way we manage to not invalidate the iterator when an element
238 // is removed.
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900239 std::set<KeyType> removed_ids_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900240
241 // The next ID that we will return from Add()
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900242 KeyType next_id_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900243
244 HashTable data_;
245
246 // See description above setter.
247 bool check_on_null_data_;
248
jsbell5c49d682015-11-17 04:43:34 +0900249 base::SequenceChecker sequence_checker_;
250
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900251 DISALLOW_COPY_AND_ASSIGN(IDMap);
252};
253
254#endif // BASE_ID_MAP_H_