1dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifndef BASE_ID_MAP_H_ 6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#define BASE_ID_MAP_H_ 73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#pragma once 8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <set> 10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/basictypes.h" 12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/hash_tables.h" 13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h" 14dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/threading/non_thread_safe.h" 15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Ownership semantics - own pointer means the pointer is deleted in Remove() 17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// & during destruction 18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottenum IDMapOwnershipSemantics { 19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott IDMapExternalPointer, 20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott IDMapOwnPointer 21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}; 22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// This object maintains a list of IDs that can be quickly converted to 24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// pointers to objects. It is implemented as a hash table, optimized for 25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// relatively small data sets (in the common case, there will be exactly one 26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// item in the list). 27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Items can be inserted into the container with arbitrary ID, but the caller 29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// must ensure they are unique. Inserting IDs and relying on automatically 30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// generated ones is not allowed because they can collide. 31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// This class does not have a virtual destructor, do not inherit from it when 33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// ownership semantics are set to own because pointers will leak. 34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer> 35dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenclass IDMap : public base::NonThreadSafe { 36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott private: 37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typedef int32 KeyType; 38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typedef base::hash_map<KeyType, T*> HashTable; 39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott public: 41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) { 42dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // A number of consumers of IDMap create it on one thread but always access 43dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // it from a different, but consitent, thread post-construction. 44dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DetachFromThread(); 45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ~IDMap() { 48dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Many IDMap's are static, and hence will be destroyed on the main thread. 49dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // However, all the accesses may take place on another thread, such as the 50dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // IO thread. Detaching again to clean this up. 51dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DetachFromThread(); 52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott Releaser<OS, 0>::release_all(&data_); 53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Sets whether Add should CHECK if passed in NULL data. Default is false. 56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void set_check_on_null_data(bool value) { check_on_null_data_ = value; } 57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Adds a view with an automatically generated unique ID. See AddWithID. 59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott KeyType Add(T* data) { 60dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(CalledOnValidThread()); 61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott CHECK(!check_on_null_data_ || data); 62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott KeyType this_id = next_id_; 63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; 64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott data_[this_id] = data; 65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_id_++; 66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return this_id; 67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Adds a new data member with the specified ID. The ID must not be in 70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // the list. The caller either must generate all unique IDs itself and use 71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // this function, or allow this object to generate IDs and call Add. These 72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // two methods may not be mixed, or duplicate IDs may be generated 73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void AddWithID(T* data, KeyType id) { 74dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(CalledOnValidThread()); 75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott CHECK(!check_on_null_data_ || data); 76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; 77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott data_[id] = data; 78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void Remove(KeyType id) { 81dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(CalledOnValidThread()); 82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typename HashTable::iterator i = data_.find(id); 83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (i == data_.end()) { 84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott NOTREACHED() << "Attempting to remove an item not in the list"; 85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (iteration_depth_ == 0) { 89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott Releaser<OS, 0>::release(i->second); 90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott data_.erase(i); 91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott removed_ids_.insert(id); 93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott bool IsEmpty() const { 97dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(CalledOnValidThread()); 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return size() == 0u; 99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott T* Lookup(KeyType id) const { 102dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(CalledOnValidThread()); 103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typename HashTable::const_iterator i = data_.find(id); 104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (i == data_.end()) 105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return NULL; 106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return i->second; 107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott size_t size() const { 110dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(CalledOnValidThread()); 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return data_.size() - removed_ids_.size(); 112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // It is safe to remove elements from the map during iteration. All iterators 115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // will remain valid. 116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott template<class ReturnType> 117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott class Iterator { 118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott public: 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Iterator(IDMap<T, OS>* map) 120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott : map_(map), 121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott iter_(map_->data_.begin()) { 122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(map->CalledOnValidThread()); 123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ++map_->iteration_depth_; 124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SkipRemovedEntries(); 125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ~Iterator() { 128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(map_->CalledOnValidThread()); 129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (--map_->iteration_depth_ == 0) 130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott map_->Compact(); 131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott bool IsAtEnd() const { 134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(map_->CalledOnValidThread()); 135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return iter_ == map_->data_.end(); 136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott KeyType GetCurrentKey() const { 139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(map_->CalledOnValidThread()); 140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return iter_->first; 141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ReturnType* GetCurrentValue() const { 144dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(map_->CalledOnValidThread()); 145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return iter_->second; 146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void Advance() { 149dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(map_->CalledOnValidThread()); 150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ++iter_; 151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SkipRemovedEntries(); 152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott private: 155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void SkipRemovedEntries() { 156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott while (iter_ != map_->data_.end() && 157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott map_->removed_ids_.find(iter_->first) != 158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott map_->removed_ids_.end()) { 159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ++iter_; 160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IDMap<T, OS>* map_; 164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typename HashTable::const_iterator iter_; 165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott }; 166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typedef Iterator<T> iterator; 168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott typedef Iterator<const T> const_iterator; 169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott private: 171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // The dummy parameter is there because C++ standard does not allow 173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // explicitly specialized templates inside classes 174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott template<IDMapOwnershipSemantics OI, int dummy> struct Releaser { 175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott static inline void release(T* ptr) {} 176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott static inline void release_all(HashTable* table) {} 177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott }; 178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott template<int dummy> struct Releaser<IDMapOwnPointer, dummy> { 180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott static inline void release(T* ptr) { delete ptr;} 181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott static inline void release_all(HashTable* table) { 182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (typename HashTable::iterator i = table->begin(); 183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott i != table->end(); ++i) { 184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott delete i->second; 185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott table->clear(); 187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott }; 189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void Compact() { 191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_EQ(0, iteration_depth_); 192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (std::set<KeyType>::const_iterator i = removed_ids_.begin(); 193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott i != removed_ids_.end(); ++i) { 194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott Remove(*i); 195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott removed_ids_.clear(); 197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Keep track of how many iterators are currently iterating on us to safely 200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // handle removing items during iteration. 201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott int iteration_depth_; 202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Keep set of IDs that should be removed after the outermost iteration has 204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // finished. This way we manage to not invalidate the iterator when an element 205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // is removed. 206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott std::set<KeyType> removed_ids_; 207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // The next ID that we will return from Add() 209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott KeyType next_id_; 210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott HashTable data_; 212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // See description above setter. 214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott bool check_on_null_data_; 215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DISALLOW_COPY_AND_ASSIGN(IDMap); 217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}; 218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif // BASE_ID_MAP_H_ 220