1*635a8641SAndroid Build Coastguard Worker // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2*635a8641SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*635a8641SAndroid Build Coastguard Worker // found in the LICENSE file. 4*635a8641SAndroid Build Coastguard Worker 5*635a8641SAndroid Build Coastguard Worker #ifndef BASE_CONTAINERS_ID_MAP_H_ 6*635a8641SAndroid Build Coastguard Worker #define BASE_CONTAINERS_ID_MAP_H_ 7*635a8641SAndroid Build Coastguard Worker 8*635a8641SAndroid Build Coastguard Worker #include <stddef.h> 9*635a8641SAndroid Build Coastguard Worker #include <stdint.h> 10*635a8641SAndroid Build Coastguard Worker 11*635a8641SAndroid Build Coastguard Worker #include <memory> 12*635a8641SAndroid Build Coastguard Worker #include <set> 13*635a8641SAndroid Build Coastguard Worker #include <type_traits> 14*635a8641SAndroid Build Coastguard Worker #include <unordered_map> 15*635a8641SAndroid Build Coastguard Worker #include <utility> 16*635a8641SAndroid Build Coastguard Worker 17*635a8641SAndroid Build Coastguard Worker #include "base/containers/flat_set.h" 18*635a8641SAndroid Build Coastguard Worker #include "base/logging.h" 19*635a8641SAndroid Build Coastguard Worker #include "base/macros.h" 20*635a8641SAndroid Build Coastguard Worker #include "base/sequence_checker.h" 21*635a8641SAndroid Build Coastguard Worker 22*635a8641SAndroid Build Coastguard Worker namespace base { 23*635a8641SAndroid Build Coastguard Worker 24*635a8641SAndroid Build Coastguard Worker // This object maintains a list of IDs that can be quickly converted to 25*635a8641SAndroid Build Coastguard Worker // pointers to objects. It is implemented as a hash table, optimized for 26*635a8641SAndroid Build Coastguard Worker // relatively small data sets (in the common case, there will be exactly one 27*635a8641SAndroid Build Coastguard Worker // item in the list). 28*635a8641SAndroid Build Coastguard Worker // 29*635a8641SAndroid Build Coastguard Worker // Items can be inserted into the container with arbitrary ID, but the caller 30*635a8641SAndroid Build Coastguard Worker // must ensure they are unique. Inserting IDs and relying on automatically 31*635a8641SAndroid Build Coastguard Worker // generated ones is not allowed because they can collide. 32*635a8641SAndroid Build Coastguard Worker 33*635a8641SAndroid Build Coastguard Worker // The map's value type (the V param) can be any dereferenceable type, such as a 34*635a8641SAndroid Build Coastguard Worker // raw pointer or smart pointer 35*635a8641SAndroid Build Coastguard Worker template <typename V, typename K = int32_t> 36*635a8641SAndroid Build Coastguard Worker class IDMap final { 37*635a8641SAndroid Build Coastguard Worker public: 38*635a8641SAndroid Build Coastguard Worker using KeyType = K; 39*635a8641SAndroid Build Coastguard Worker 40*635a8641SAndroid Build Coastguard Worker private: 41*635a8641SAndroid Build Coastguard Worker using T = typename std::remove_reference<decltype(*V())>::type; 42*635a8641SAndroid Build Coastguard Worker 43*635a8641SAndroid Build Coastguard Worker using HashTable = std::unordered_map<KeyType, V>; 44*635a8641SAndroid Build Coastguard Worker 45*635a8641SAndroid Build Coastguard Worker public: IDMap()46*635a8641SAndroid Build Coastguard Worker IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) { 47*635a8641SAndroid Build Coastguard Worker // A number of consumers of IDMap create it on one thread but always 48*635a8641SAndroid Build Coastguard Worker // access it from a different, but consistent, thread (or sequence) 49*635a8641SAndroid Build Coastguard Worker // post-construction. The first call to CalledOnValidSequence() will re-bind 50*635a8641SAndroid Build Coastguard Worker // it. 51*635a8641SAndroid Build Coastguard Worker DETACH_FROM_SEQUENCE(sequence_checker_); 52*635a8641SAndroid Build Coastguard Worker } 53*635a8641SAndroid Build Coastguard Worker ~IDMap()54*635a8641SAndroid Build Coastguard Worker ~IDMap() { 55*635a8641SAndroid Build Coastguard Worker // Many IDMap's are static, and hence will be destroyed on the main 56*635a8641SAndroid Build Coastguard Worker // thread. However, all the accesses may take place on another thread (or 57*635a8641SAndroid Build Coastguard Worker // sequence), such as the IO thread. Detaching again to clean this up. 58*635a8641SAndroid Build Coastguard Worker DETACH_FROM_SEQUENCE(sequence_checker_); 59*635a8641SAndroid Build Coastguard Worker } 60*635a8641SAndroid Build Coastguard Worker 61*635a8641SAndroid Build Coastguard Worker // Sets whether Add and Replace should DCHECK if passed in NULL data. 62*635a8641SAndroid Build Coastguard Worker // Default is false. set_check_on_null_data(bool value)63*635a8641SAndroid Build Coastguard Worker void set_check_on_null_data(bool value) { check_on_null_data_ = value; } 64*635a8641SAndroid Build Coastguard Worker 65*635a8641SAndroid Build Coastguard Worker // Adds a view with an automatically generated unique ID. See AddWithID. Add(V data)66*635a8641SAndroid Build Coastguard Worker KeyType Add(V data) { return AddInternal(std::move(data)); } 67*635a8641SAndroid Build Coastguard Worker 68*635a8641SAndroid Build Coastguard Worker // Adds a new data member with the specified ID. The ID must not be in 69*635a8641SAndroid Build Coastguard Worker // the list. The caller either must generate all unique IDs itself and use 70*635a8641SAndroid Build Coastguard Worker // this function, or allow this object to generate IDs and call Add. These 71*635a8641SAndroid Build Coastguard Worker // two methods may not be mixed, or duplicate IDs may be generated. AddWithID(V data,KeyType id)72*635a8641SAndroid Build Coastguard Worker void AddWithID(V data, KeyType id) { AddWithIDInternal(std::move(data), id); } 73*635a8641SAndroid Build Coastguard Worker Remove(KeyType id)74*635a8641SAndroid Build Coastguard Worker void Remove(KeyType id) { 75*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 76*635a8641SAndroid Build Coastguard Worker typename HashTable::iterator i = data_.find(id); 77*635a8641SAndroid Build Coastguard Worker if (i == data_.end() || IsRemoved(id)) { 78*635a8641SAndroid Build Coastguard Worker NOTREACHED() << "Attempting to remove an item not in the list"; 79*635a8641SAndroid Build Coastguard Worker return; 80*635a8641SAndroid Build Coastguard Worker } 81*635a8641SAndroid Build Coastguard Worker 82*635a8641SAndroid Build Coastguard Worker if (iteration_depth_ == 0) { 83*635a8641SAndroid Build Coastguard Worker data_.erase(i); 84*635a8641SAndroid Build Coastguard Worker } else { 85*635a8641SAndroid Build Coastguard Worker removed_ids_.insert(id); 86*635a8641SAndroid Build Coastguard Worker } 87*635a8641SAndroid Build Coastguard Worker } 88*635a8641SAndroid Build Coastguard Worker 89*635a8641SAndroid Build Coastguard Worker // Replaces the value for |id| with |new_data| and returns the existing value. 90*635a8641SAndroid Build Coastguard Worker // Should only be called with an already added id. Replace(KeyType id,V new_data)91*635a8641SAndroid Build Coastguard Worker V Replace(KeyType id, V new_data) { 92*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 93*635a8641SAndroid Build Coastguard Worker DCHECK(!check_on_null_data_ || new_data); 94*635a8641SAndroid Build Coastguard Worker typename HashTable::iterator i = data_.find(id); 95*635a8641SAndroid Build Coastguard Worker DCHECK(i != data_.end()); 96*635a8641SAndroid Build Coastguard Worker DCHECK(!IsRemoved(id)); 97*635a8641SAndroid Build Coastguard Worker 98*635a8641SAndroid Build Coastguard Worker using std::swap; 99*635a8641SAndroid Build Coastguard Worker swap(i->second, new_data); 100*635a8641SAndroid Build Coastguard Worker return new_data; 101*635a8641SAndroid Build Coastguard Worker } 102*635a8641SAndroid Build Coastguard Worker Clear()103*635a8641SAndroid Build Coastguard Worker void Clear() { 104*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 105*635a8641SAndroid Build Coastguard Worker if (iteration_depth_ == 0) { 106*635a8641SAndroid Build Coastguard Worker data_.clear(); 107*635a8641SAndroid Build Coastguard Worker } else { 108*635a8641SAndroid Build Coastguard Worker removed_ids_.reserve(data_.size()); 109*635a8641SAndroid Build Coastguard Worker removed_ids_.insert(KeyIterator(data_.begin()), KeyIterator(data_.end())); 110*635a8641SAndroid Build Coastguard Worker } 111*635a8641SAndroid Build Coastguard Worker } 112*635a8641SAndroid Build Coastguard Worker IsEmpty()113*635a8641SAndroid Build Coastguard Worker bool IsEmpty() const { 114*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 115*635a8641SAndroid Build Coastguard Worker return size() == 0u; 116*635a8641SAndroid Build Coastguard Worker } 117*635a8641SAndroid Build Coastguard Worker Lookup(KeyType id)118*635a8641SAndroid Build Coastguard Worker T* Lookup(KeyType id) const { 119*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 120*635a8641SAndroid Build Coastguard Worker typename HashTable::const_iterator i = data_.find(id); 121*635a8641SAndroid Build Coastguard Worker if (i == data_.end() || !i->second || IsRemoved(id)) 122*635a8641SAndroid Build Coastguard Worker return nullptr; 123*635a8641SAndroid Build Coastguard Worker return &*i->second; 124*635a8641SAndroid Build Coastguard Worker } 125*635a8641SAndroid Build Coastguard Worker size()126*635a8641SAndroid Build Coastguard Worker size_t size() const { 127*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 128*635a8641SAndroid Build Coastguard Worker return data_.size() - removed_ids_.size(); 129*635a8641SAndroid Build Coastguard Worker } 130*635a8641SAndroid Build Coastguard Worker 131*635a8641SAndroid Build Coastguard Worker #if defined(UNIT_TEST) iteration_depth()132*635a8641SAndroid Build Coastguard Worker int iteration_depth() const { 133*635a8641SAndroid Build Coastguard Worker return iteration_depth_; 134*635a8641SAndroid Build Coastguard Worker } 135*635a8641SAndroid Build Coastguard Worker #endif // defined(UNIT_TEST) 136*635a8641SAndroid Build Coastguard Worker 137*635a8641SAndroid Build Coastguard Worker // It is safe to remove elements from the map during iteration. All iterators 138*635a8641SAndroid Build Coastguard Worker // will remain valid. 139*635a8641SAndroid Build Coastguard Worker template<class ReturnType> 140*635a8641SAndroid Build Coastguard Worker class Iterator { 141*635a8641SAndroid Build Coastguard Worker public: Iterator(IDMap<V,K> * map)142*635a8641SAndroid Build Coastguard Worker Iterator(IDMap<V, K>* map) : map_(map), iter_(map_->data_.begin()) { 143*635a8641SAndroid Build Coastguard Worker Init(); 144*635a8641SAndroid Build Coastguard Worker } 145*635a8641SAndroid Build Coastguard Worker Iterator(const Iterator & iter)146*635a8641SAndroid Build Coastguard Worker Iterator(const Iterator& iter) 147*635a8641SAndroid Build Coastguard Worker : map_(iter.map_), 148*635a8641SAndroid Build Coastguard Worker iter_(iter.iter_) { 149*635a8641SAndroid Build Coastguard Worker Init(); 150*635a8641SAndroid Build Coastguard Worker } 151*635a8641SAndroid Build Coastguard Worker 152*635a8641SAndroid Build Coastguard Worker const Iterator& operator=(const Iterator& iter) { 153*635a8641SAndroid Build Coastguard Worker map_ = iter.map; 154*635a8641SAndroid Build Coastguard Worker iter_ = iter.iter; 155*635a8641SAndroid Build Coastguard Worker Init(); 156*635a8641SAndroid Build Coastguard Worker return *this; 157*635a8641SAndroid Build Coastguard Worker } 158*635a8641SAndroid Build Coastguard Worker ~Iterator()159*635a8641SAndroid Build Coastguard Worker ~Iterator() { 160*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); 161*635a8641SAndroid Build Coastguard Worker 162*635a8641SAndroid Build Coastguard Worker // We're going to decrement iteration depth. Make sure it's greater than 163*635a8641SAndroid Build Coastguard Worker // zero so that it doesn't become negative. 164*635a8641SAndroid Build Coastguard Worker DCHECK_LT(0, map_->iteration_depth_); 165*635a8641SAndroid Build Coastguard Worker 166*635a8641SAndroid Build Coastguard Worker if (--map_->iteration_depth_ == 0) 167*635a8641SAndroid Build Coastguard Worker map_->Compact(); 168*635a8641SAndroid Build Coastguard Worker } 169*635a8641SAndroid Build Coastguard Worker IsAtEnd()170*635a8641SAndroid Build Coastguard Worker bool IsAtEnd() const { 171*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); 172*635a8641SAndroid Build Coastguard Worker return iter_ == map_->data_.end(); 173*635a8641SAndroid Build Coastguard Worker } 174*635a8641SAndroid Build Coastguard Worker GetCurrentKey()175*635a8641SAndroid Build Coastguard Worker KeyType GetCurrentKey() const { 176*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); 177*635a8641SAndroid Build Coastguard Worker return iter_->first; 178*635a8641SAndroid Build Coastguard Worker } 179*635a8641SAndroid Build Coastguard Worker GetCurrentValue()180*635a8641SAndroid Build Coastguard Worker ReturnType* GetCurrentValue() const { 181*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); 182*635a8641SAndroid Build Coastguard Worker if (!iter_->second || map_->IsRemoved(iter_->first)) 183*635a8641SAndroid Build Coastguard Worker return nullptr; 184*635a8641SAndroid Build Coastguard Worker return &*iter_->second; 185*635a8641SAndroid Build Coastguard Worker } 186*635a8641SAndroid Build Coastguard Worker Advance()187*635a8641SAndroid Build Coastguard Worker void Advance() { 188*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); 189*635a8641SAndroid Build Coastguard Worker ++iter_; 190*635a8641SAndroid Build Coastguard Worker SkipRemovedEntries(); 191*635a8641SAndroid Build Coastguard Worker } 192*635a8641SAndroid Build Coastguard Worker 193*635a8641SAndroid Build Coastguard Worker private: Init()194*635a8641SAndroid Build Coastguard Worker void Init() { 195*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); 196*635a8641SAndroid Build Coastguard Worker ++map_->iteration_depth_; 197*635a8641SAndroid Build Coastguard Worker SkipRemovedEntries(); 198*635a8641SAndroid Build Coastguard Worker } 199*635a8641SAndroid Build Coastguard Worker SkipRemovedEntries()200*635a8641SAndroid Build Coastguard Worker void SkipRemovedEntries() { 201*635a8641SAndroid Build Coastguard Worker while (iter_ != map_->data_.end() && map_->IsRemoved(iter_->first)) 202*635a8641SAndroid Build Coastguard Worker ++iter_; 203*635a8641SAndroid Build Coastguard Worker } 204*635a8641SAndroid Build Coastguard Worker 205*635a8641SAndroid Build Coastguard Worker IDMap<V, K>* map_; 206*635a8641SAndroid Build Coastguard Worker typename HashTable::const_iterator iter_; 207*635a8641SAndroid Build Coastguard Worker }; 208*635a8641SAndroid Build Coastguard Worker 209*635a8641SAndroid Build Coastguard Worker typedef Iterator<T> iterator; 210*635a8641SAndroid Build Coastguard Worker typedef Iterator<const T> const_iterator; 211*635a8641SAndroid Build Coastguard Worker 212*635a8641SAndroid Build Coastguard Worker private: 213*635a8641SAndroid Build Coastguard Worker // Transforms a map iterator to an iterator on the keys of the map. 214*635a8641SAndroid Build Coastguard Worker // Used by Clear() to populate |removed_ids_| in bulk. 215*635a8641SAndroid Build Coastguard Worker struct KeyIterator : std::iterator<std::forward_iterator_tag, KeyType> { 216*635a8641SAndroid Build Coastguard Worker using inner_iterator = typename HashTable::iterator; 217*635a8641SAndroid Build Coastguard Worker inner_iterator iter_; 218*635a8641SAndroid Build Coastguard Worker KeyIteratorKeyIterator219*635a8641SAndroid Build Coastguard Worker KeyIterator(inner_iterator iter) : iter_(iter) {} 220*635a8641SAndroid Build Coastguard Worker KeyType operator*() const { return iter_->first; } 221*635a8641SAndroid Build Coastguard Worker KeyIterator& operator++() { 222*635a8641SAndroid Build Coastguard Worker ++iter_; 223*635a8641SAndroid Build Coastguard Worker return *this; 224*635a8641SAndroid Build Coastguard Worker } 225*635a8641SAndroid Build Coastguard Worker KeyIterator operator++(int) { return KeyIterator(iter_++); } 226*635a8641SAndroid Build Coastguard Worker bool operator==(const KeyIterator& other) const { 227*635a8641SAndroid Build Coastguard Worker return iter_ == other.iter_; 228*635a8641SAndroid Build Coastguard Worker } 229*635a8641SAndroid Build Coastguard Worker bool operator!=(const KeyIterator& other) const { 230*635a8641SAndroid Build Coastguard Worker return iter_ != other.iter_; 231*635a8641SAndroid Build Coastguard Worker } 232*635a8641SAndroid Build Coastguard Worker }; 233*635a8641SAndroid Build Coastguard Worker AddInternal(V data)234*635a8641SAndroid Build Coastguard Worker KeyType AddInternal(V data) { 235*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 236*635a8641SAndroid Build Coastguard Worker DCHECK(!check_on_null_data_ || data); 237*635a8641SAndroid Build Coastguard Worker KeyType this_id = next_id_; 238*635a8641SAndroid Build Coastguard Worker DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; 239*635a8641SAndroid Build Coastguard Worker data_[this_id] = std::move(data); 240*635a8641SAndroid Build Coastguard Worker next_id_++; 241*635a8641SAndroid Build Coastguard Worker return this_id; 242*635a8641SAndroid Build Coastguard Worker } 243*635a8641SAndroid Build Coastguard Worker AddWithIDInternal(V data,KeyType id)244*635a8641SAndroid Build Coastguard Worker void AddWithIDInternal(V data, KeyType id) { 245*635a8641SAndroid Build Coastguard Worker DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 246*635a8641SAndroid Build Coastguard Worker DCHECK(!check_on_null_data_ || data); 247*635a8641SAndroid Build Coastguard Worker if (IsRemoved(id)) { 248*635a8641SAndroid Build Coastguard Worker removed_ids_.erase(id); 249*635a8641SAndroid Build Coastguard Worker } else { 250*635a8641SAndroid Build Coastguard Worker DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; 251*635a8641SAndroid Build Coastguard Worker } 252*635a8641SAndroid Build Coastguard Worker data_[id] = std::move(data); 253*635a8641SAndroid Build Coastguard Worker } 254*635a8641SAndroid Build Coastguard Worker IsRemoved(KeyType key)255*635a8641SAndroid Build Coastguard Worker bool IsRemoved(KeyType key) const { 256*635a8641SAndroid Build Coastguard Worker return removed_ids_.find(key) != removed_ids_.end(); 257*635a8641SAndroid Build Coastguard Worker } 258*635a8641SAndroid Build Coastguard Worker Compact()259*635a8641SAndroid Build Coastguard Worker void Compact() { 260*635a8641SAndroid Build Coastguard Worker DCHECK_EQ(0, iteration_depth_); 261*635a8641SAndroid Build Coastguard Worker for (const auto& i : removed_ids_) 262*635a8641SAndroid Build Coastguard Worker data_.erase(i); 263*635a8641SAndroid Build Coastguard Worker removed_ids_.clear(); 264*635a8641SAndroid Build Coastguard Worker } 265*635a8641SAndroid Build Coastguard Worker 266*635a8641SAndroid Build Coastguard Worker // Keep track of how many iterators are currently iterating on us to safely 267*635a8641SAndroid Build Coastguard Worker // handle removing items during iteration. 268*635a8641SAndroid Build Coastguard Worker int iteration_depth_; 269*635a8641SAndroid Build Coastguard Worker 270*635a8641SAndroid Build Coastguard Worker // Keep set of IDs that should be removed after the outermost iteration has 271*635a8641SAndroid Build Coastguard Worker // finished. This way we manage to not invalidate the iterator when an element 272*635a8641SAndroid Build Coastguard Worker // is removed. 273*635a8641SAndroid Build Coastguard Worker base::flat_set<KeyType> removed_ids_; 274*635a8641SAndroid Build Coastguard Worker 275*635a8641SAndroid Build Coastguard Worker // The next ID that we will return from Add() 276*635a8641SAndroid Build Coastguard Worker KeyType next_id_; 277*635a8641SAndroid Build Coastguard Worker 278*635a8641SAndroid Build Coastguard Worker HashTable data_; 279*635a8641SAndroid Build Coastguard Worker 280*635a8641SAndroid Build Coastguard Worker // See description above setter. 281*635a8641SAndroid Build Coastguard Worker bool check_on_null_data_; 282*635a8641SAndroid Build Coastguard Worker 283*635a8641SAndroid Build Coastguard Worker SEQUENCE_CHECKER(sequence_checker_); 284*635a8641SAndroid Build Coastguard Worker 285*635a8641SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(IDMap); 286*635a8641SAndroid Build Coastguard Worker }; 287*635a8641SAndroid Build Coastguard Worker 288*635a8641SAndroid Build Coastguard Worker } // namespace base 289*635a8641SAndroid Build Coastguard Worker 290*635a8641SAndroid Build Coastguard Worker #endif // BASE_CONTAINERS_ID_MAP_H_ 291