blob: 5aea98e82dfe98666ca77f699d21cbac82fad189 [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_
thakis@chromium.org01d14522010-07-27 08:08:24 +09007#pragma once
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +09008
9#include <set>
10
11#include "base/basictypes.h"
12#include "base/hash_tables.h"
13#include "base/logging.h"
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090014#include "base/threading/non_thread_safe.h"
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090015
neb@chromium.orged1f8d72010-01-26 08:25:16 +090016// Ownership semantics - own pointer means the pointer is deleted in Remove()
17// & during destruction
18enum IDMapOwnershipSemantics {
19 IDMapExternalPointer,
20 IDMapOwnPointer
21};
22
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090023// This object maintains a list of IDs that can be quickly converted to
24// pointers to objects. It is implemented as a hash table, optimized for
25// relatively small data sets (in the common case, there will be exactly one
26// item in the list).
27//
28// Items can be inserted into the container with arbitrary ID, but the caller
29// must ensure they are unique. Inserting IDs and relying on automatically
30// generated ones is not allowed because they can collide.
neb@chromium.orged1f8d72010-01-26 08:25:16 +090031//
32// This class does not have a virtual destructor, do not inherit from it when
33// ownership semantics are set to own because pointers will leak.
34template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer>
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090035class IDMap : public base::NonThreadSafe {
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090036 private:
neb@chromium.orged1f8d72010-01-26 08:25:16 +090037 typedef int32 KeyType;
38 typedef base::hash_map<KeyType, T*> HashTable;
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) {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090042 // A number of consumers of IDMap create it on one thread but always access
43 // it from a different, but consitent, thread post-construction.
44 DetachFromThread();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090045 }
46
neb@chromium.orged1f8d72010-01-26 08:25:16 +090047 ~IDMap() {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090048 // Many IDMap's are static, and hence will be destroyed on the main thread.
49 // However, all the accesses may take place on another thread, such as the
50 // IO thread. Detaching again to clean this up.
51 DetachFromThread();
neb@chromium.orged1f8d72010-01-26 08:25:16 +090052 Releaser<OS, 0>::release_all(&data_);
53 }
54
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090055 // Sets whether Add should CHECK if passed in NULL data. Default is false.
56 void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
57
58 // Adds a view with an automatically generated unique ID. See AddWithID.
neb@chromium.orged1f8d72010-01-26 08:25:16 +090059 KeyType Add(T* data) {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090060 DCHECK(CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090061 CHECK(!check_on_null_data_ || data);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090062 KeyType this_id = next_id_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090063 DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
64 data_[this_id] = data;
65 next_id_++;
66 return this_id;
67 }
68
69 // Adds a new data member with the specified ID. The ID must not be in
70 // the list. The caller either must generate all unique IDs itself and use
71 // this function, or allow this object to generate IDs and call Add. These
72 // two methods may not be mixed, or duplicate IDs may be generated
neb@chromium.orged1f8d72010-01-26 08:25:16 +090073 void AddWithID(T* data, KeyType id) {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090074 DCHECK(CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090075 CHECK(!check_on_null_data_ || data);
76 DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
77 data_[id] = data;
78 }
79
neb@chromium.orged1f8d72010-01-26 08:25:16 +090080 void Remove(KeyType id) {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090081 DCHECK(CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090082 typename HashTable::iterator i = data_.find(id);
83 if (i == data_.end()) {
84 NOTREACHED() << "Attempting to remove an item not in the list";
85 return;
86 }
87
neb@chromium.orged1f8d72010-01-26 08:25:16 +090088 if (iteration_depth_ == 0) {
89 Releaser<OS, 0>::release(i->second);
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090090 data_.erase(i);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090091 } else {
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090092 removed_ids_.insert(id);
neb@chromium.orged1f8d72010-01-26 08:25:16 +090093 }
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090094 }
95
96 bool IsEmpty() const {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +090097 DCHECK(CalledOnValidThread());
phajdan.jr@chromium.org61544a02010-02-16 18:15:38 +090098 return size() == 0u;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +090099 }
100
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900101 T* Lookup(KeyType id) const {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900102 DCHECK(CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900103 typename HashTable::const_iterator i = data_.find(id);
104 if (i == data_.end())
105 return NULL;
106 return i->second;
107 }
108
109 size_t size() const {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900110 DCHECK(CalledOnValidThread());
phajdan.jr@chromium.org61544a02010-02-16 18:15:38 +0900111 return data_.size() - removed_ids_.size();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900112 }
113
114 // It is safe to remove elements from the map during iteration. All iterators
115 // will remain valid.
116 template<class ReturnType>
117 class Iterator {
118 public:
brettw@chromium.org5808ed32010-03-06 11:53:28 +0900119 Iterator(IDMap<T, OS>* map)
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900120 : map_(map),
121 iter_(map_->data_.begin()) {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900122 DCHECK(map->CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900123 ++map_->iteration_depth_;
124 SkipRemovedEntries();
125 }
126
127 ~Iterator() {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900128 DCHECK(map_->CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900129 if (--map_->iteration_depth_ == 0)
130 map_->Compact();
131 }
132
133 bool IsAtEnd() const {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900134 DCHECK(map_->CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900135 return iter_ == map_->data_.end();
136 }
137
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900138 KeyType GetCurrentKey() const {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900139 DCHECK(map_->CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900140 return iter_->first;
141 }
142
143 ReturnType* GetCurrentValue() const {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900144 DCHECK(map_->CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900145 return iter_->second;
146 }
147
148 void Advance() {
cbentzel@chromium.org95cb7512011-03-08 11:07:29 +0900149 DCHECK(map_->CalledOnValidThread());
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900150 ++iter_;
151 SkipRemovedEntries();
152 }
153
154 private:
155 void SkipRemovedEntries() {
156 while (iter_ != map_->data_.end() &&
157 map_->removed_ids_.find(iter_->first) !=
158 map_->removed_ids_.end()) {
159 ++iter_;
160 }
161 }
162
brettw@chromium.org5808ed32010-03-06 11:53:28 +0900163 IDMap<T, OS>* map_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900164 typename HashTable::const_iterator iter_;
165 };
166
167 typedef Iterator<T> iterator;
168 typedef Iterator<const T> const_iterator;
169
170 private:
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900171
172 // The dummy parameter is there because C++ standard does not allow
173 // explicitly specialized templates inside classes
174 template<IDMapOwnershipSemantics OI, int dummy> struct Releaser {
175 static inline void release(T* ptr) {}
176 static inline void release_all(HashTable* table) {}
177 };
178
179 template<int dummy> struct Releaser<IDMapOwnPointer, dummy> {
180 static inline void release(T* ptr) { delete ptr;}
181 static inline void release_all(HashTable* table) {
182 for (typename HashTable::iterator i = table->begin();
183 i != table->end(); ++i) {
184 delete i->second;
185 }
186 table->clear();
187 }
188 };
189
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900190 void Compact() {
191 DCHECK_EQ(0, iteration_depth_);
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900192 for (std::set<KeyType>::const_iterator i = removed_ids_.begin();
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900193 i != removed_ids_.end(); ++i) {
194 Remove(*i);
195 }
196 removed_ids_.clear();
197 }
198
199 // Keep track of how many iterators are currently iterating on us to safely
200 // handle removing items during iteration.
201 int iteration_depth_;
202
203 // Keep set of IDs that should be removed after the outermost iteration has
204 // finished. This way we manage to not invalidate the iterator when an element
205 // is removed.
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900206 std::set<KeyType> removed_ids_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900207
208 // The next ID that we will return from Add()
neb@chromium.orged1f8d72010-01-26 08:25:16 +0900209 KeyType next_id_;
phajdan.jr@chromium.orga12746c2009-08-19 23:56:38 +0900210
211 HashTable data_;
212
213 // See description above setter.
214 bool check_on_null_data_;
215
216 DISALLOW_COPY_AND_ASSIGN(IDMap);
217};
218
219#endif // BASE_ID_MAP_H_