Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / base / containers / small_map.h
blobdf3d22ae9ab26a184d7ead489e98b5c1d09c7911
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef BASE_CONTAINERS_SMALL_MAP_H_
6 #define BASE_CONTAINERS_SMALL_MAP_H_
8 #include <map>
9 #include <string>
10 #include <utility>
12 #include "base/basictypes.h"
13 #include "base/containers/hash_tables.h"
14 #include "base/logging.h"
15 #include "base/memory/manual_constructor.h"
17 namespace base {
19 // An STL-like associative container which starts out backed by a simple
20 // array but switches to some other container type if it grows beyond a
21 // fixed size.
23 // WHAT TYPE OF MAP SHOULD YOU USE?
24 // --------------------------------
26 // - std::map should be the default if you're not sure, since it's the most
27 // difficult to mess up. Generally this is backed by a red-black tree. It
28 // will generate a lot of code (if you use a common key type like int or
29 // string the linker will probably emiminate the duplicates). It will
30 // do heap allocations for each element.
32 // - If you only ever keep a couple of items and have very simple usage,
33 // consider whether a using a vector and brute-force searching it will be
34 // the most efficient. It's not a lot of generated code (less then a
35 // red-black tree if your key is "weird" and not eliminated as duplicate of
36 // something else) and will probably be faster and do fewer heap allocations
37 // than std::map if you have just a couple of items.
39 // - base::hash_map should be used if you need O(1) lookups. It may waste
40 // space in the hash table, and it can be easy to write correct-looking
41 // code with the default hash function being wrong or poorly-behaving.
43 // - SmallMap combines the performance benefits of the brute-force-searched
44 // vector for small cases (no extra heap allocations), but can efficiently
45 // fall back if you end up adding many items. It will generate more code
46 // than std::map (at least 160 bytes for operator[]) which is bad if you
47 // have a "weird" key where map functions can't be
48 // duplicate-code-eliminated. If you have a one-off key and aren't in
49 // performance-critical code, this bloat may negate some of the benefits and
50 // you should consider on of the other options.
52 // SmallMap will pick up the comparator from the underlying map type. In
53 // std::map (and in MSVC additionally hash_map) only a "less" operator is
54 // defined, which requires us to do two comparisons per element when doing the
55 // brute-force search in the simple array.
57 // We define default overrides for the common map types to avoid this
58 // double-compare, but you should be aware of this if you use your own
59 // operator< for your map and supply yor own version of == to the SmallMap.
60 // You can use regular operator== by just doing:
62 // base::SmallMap<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey> >
65 // USAGE
66 // -----
68 // NormalMap: The map type to fall back to. This also defines the key
69 // and value types for the SmallMap.
70 // kArraySize: The size of the initial array of results. This will be
71 // allocated with the SmallMap object rather than separately on
72 // the heap. Once the map grows beyond this size, the map type
73 // will be used instead.
74 // EqualKey: A functor which tests two keys for equality. If the wrapped
75 // map type has a "key_equal" member (hash_map does), then that will
76 // be used by default. If the wrapped map type has a strict weak
77 // ordering "key_compare" (std::map does), that will be used to
78 // implement equality by default.
79 // MapInit: A functor that takes a ManualConstructor<NormalMap>* and uses it to
80 // initialize the map. This functor will be called at most once per
81 // SmallMap, when the map exceeds the threshold of kArraySize and we
82 // are about to copy values from the array to the map. The functor
83 // *must* call one of the Init() methods provided by
84 // ManualConstructor, since after it runs we assume that the NormalMap
85 // has been initialized.
87 // example:
88 // base::SmallMap< std::map<string, int> > days;
89 // days["sunday" ] = 0;
90 // days["monday" ] = 1;
91 // days["tuesday" ] = 2;
92 // days["wednesday"] = 3;
93 // days["thursday" ] = 4;
94 // days["friday" ] = 5;
95 // days["saturday" ] = 6;
97 // You should assume that SmallMap might invalidate all the iterators
98 // on any call to erase(), insert() and operator[].
100 namespace internal {
102 template <typename NormalMap>
103 class SmallMapDefaultInit {
104 public:
105 void operator()(ManualConstructor<NormalMap>* map) const {
106 map->Init();
110 // has_key_equal<M>::value is true iff there exists a type M::key_equal. This is
111 // used to dispatch to one of the select_equal_key<> metafunctions below.
112 template <typename M>
113 struct has_key_equal {
114 typedef char sml; // "small" is sometimes #defined so we use an abbreviation.
115 typedef struct { char dummy[2]; } big;
116 // Two functions, one accepts types that have a key_equal member, and one that
117 // accepts anything. They each return a value of a different size, so we can
118 // determine at compile-time which function would have been called.
119 template <typename U> static big test(typename U::key_equal*);
120 template <typename> static sml test(...);
121 // Determines if M::key_equal exists by looking at the size of the return
122 // type of the compiler-chosen test() function.
123 static const bool value = (sizeof(test<M>(0)) == sizeof(big));
125 template <typename M> const bool has_key_equal<M>::value;
127 // Base template used for map types that do NOT have an M::key_equal member,
128 // e.g., std::map<>. These maps have a strict weak ordering comparator rather
129 // than an equality functor, so equality will be implemented in terms of that
130 // comparator.
132 // There's a partial specialization of this template below for map types that do
133 // have an M::key_equal member.
134 template <typename M, bool has_key_equal_value>
135 struct select_equal_key {
136 struct equal_key {
137 bool operator()(const typename M::key_type& left,
138 const typename M::key_type& right) {
139 // Implements equality in terms of a strict weak ordering comparator.
140 typename M::key_compare comp;
141 return !comp(left, right) && !comp(right, left);
146 // Provide overrides to use operator== for key compare for the "normal" map and
147 // hash map types. If you override the default comparator or allocator for a
148 // map or hash_map, or use another type of map, this won't get used.
150 // If we switch to using std::unordered_map for base::hash_map, then the
151 // hash_map specialization can be removed.
152 template <typename KeyType, typename ValueType>
153 struct select_equal_key< std::map<KeyType, ValueType>, false> {
154 struct equal_key {
155 bool operator()(const KeyType& left, const KeyType& right) {
156 return left == right;
160 template <typename KeyType, typename ValueType>
161 struct select_equal_key< base::hash_map<KeyType, ValueType>, false> {
162 struct equal_key {
163 bool operator()(const KeyType& left, const KeyType& right) {
164 return left == right;
169 // Partial template specialization handles case where M::key_equal exists, e.g.,
170 // hash_map<>.
171 template <typename M>
172 struct select_equal_key<M, true> {
173 typedef typename M::key_equal equal_key;
176 } // namespace internal
178 template <typename NormalMap,
179 int kArraySize = 4,
180 typename EqualKey =
181 typename internal::select_equal_key<
182 NormalMap,
183 internal::has_key_equal<NormalMap>::value>::equal_key,
184 typename MapInit = internal::SmallMapDefaultInit<NormalMap> >
185 class SmallMap {
186 // We cannot rely on the compiler to reject array of size 0. In
187 // particular, gcc 2.95.3 does it but later versions allow 0-length
188 // arrays. Therefore, we explicitly reject non-positive kArraySize
189 // here.
190 COMPILE_ASSERT(kArraySize > 0, default_initial_size_should_be_positive);
192 public:
193 typedef typename NormalMap::key_type key_type;
194 typedef typename NormalMap::mapped_type data_type;
195 typedef typename NormalMap::mapped_type mapped_type;
196 typedef typename NormalMap::value_type value_type;
197 typedef EqualKey key_equal;
199 SmallMap() : size_(0), functor_(MapInit()) {}
201 explicit SmallMap(const MapInit& functor) : size_(0), functor_(functor) {}
203 // Allow copy-constructor and assignment, since STL allows them too.
204 SmallMap(const SmallMap& src) {
205 // size_ and functor_ are initted in InitFrom()
206 InitFrom(src);
208 void operator=(const SmallMap& src) {
209 if (&src == this) return;
211 // This is not optimal. If src and dest are both using the small
212 // array, we could skip the teardown and reconstruct. One problem
213 // to be resolved is that the value_type itself is pair<const K,
214 // V>, and const K is not assignable.
215 Destroy();
216 InitFrom(src);
218 ~SmallMap() {
219 Destroy();
222 class const_iterator;
224 class iterator {
225 public:
226 typedef typename NormalMap::iterator::iterator_category iterator_category;
227 typedef typename NormalMap::iterator::value_type value_type;
228 typedef typename NormalMap::iterator::difference_type difference_type;
229 typedef typename NormalMap::iterator::pointer pointer;
230 typedef typename NormalMap::iterator::reference reference;
232 inline iterator(): array_iter_(NULL) {}
234 inline iterator& operator++() {
235 if (array_iter_ != NULL) {
236 ++array_iter_;
237 } else {
238 ++hash_iter_;
240 return *this;
242 inline iterator operator++(int /*unused*/) {
243 iterator result(*this);
244 ++(*this);
245 return result;
247 inline iterator& operator--() {
248 if (array_iter_ != NULL) {
249 --array_iter_;
250 } else {
251 --hash_iter_;
253 return *this;
255 inline iterator operator--(int /*unused*/) {
256 iterator result(*this);
257 --(*this);
258 return result;
260 inline value_type* operator->() const {
261 if (array_iter_ != NULL) {
262 return array_iter_->get();
263 } else {
264 return hash_iter_.operator->();
268 inline value_type& operator*() const {
269 if (array_iter_ != NULL) {
270 return *array_iter_->get();
271 } else {
272 return *hash_iter_;
276 inline bool operator==(const iterator& other) const {
277 if (array_iter_ != NULL) {
278 return array_iter_ == other.array_iter_;
279 } else {
280 return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_;
284 inline bool operator!=(const iterator& other) const {
285 return !(*this == other);
288 bool operator==(const const_iterator& other) const;
289 bool operator!=(const const_iterator& other) const;
291 private:
292 friend class SmallMap;
293 friend class const_iterator;
294 inline explicit iterator(ManualConstructor<value_type>* init)
295 : array_iter_(init) {}
296 inline explicit iterator(const typename NormalMap::iterator& init)
297 : array_iter_(NULL), hash_iter_(init) {}
299 ManualConstructor<value_type>* array_iter_;
300 typename NormalMap::iterator hash_iter_;
303 class const_iterator {
304 public:
305 typedef typename NormalMap::const_iterator::iterator_category
306 iterator_category;
307 typedef typename NormalMap::const_iterator::value_type value_type;
308 typedef typename NormalMap::const_iterator::difference_type difference_type;
309 typedef typename NormalMap::const_iterator::pointer pointer;
310 typedef typename NormalMap::const_iterator::reference reference;
312 inline const_iterator(): array_iter_(NULL) {}
313 // Non-explicit ctor lets us convert regular iterators to const iterators
314 inline const_iterator(const iterator& other)
315 : array_iter_(other.array_iter_), hash_iter_(other.hash_iter_) {}
317 inline const_iterator& operator++() {
318 if (array_iter_ != NULL) {
319 ++array_iter_;
320 } else {
321 ++hash_iter_;
323 return *this;
325 inline const_iterator operator++(int /*unused*/) {
326 const_iterator result(*this);
327 ++(*this);
328 return result;
331 inline const_iterator& operator--() {
332 if (array_iter_ != NULL) {
333 --array_iter_;
334 } else {
335 --hash_iter_;
337 return *this;
339 inline const_iterator operator--(int /*unused*/) {
340 const_iterator result(*this);
341 --(*this);
342 return result;
345 inline const value_type* operator->() const {
346 if (array_iter_ != NULL) {
347 return array_iter_->get();
348 } else {
349 return hash_iter_.operator->();
353 inline const value_type& operator*() const {
354 if (array_iter_ != NULL) {
355 return *array_iter_->get();
356 } else {
357 return *hash_iter_;
361 inline bool operator==(const const_iterator& other) const {
362 if (array_iter_ != NULL) {
363 return array_iter_ == other.array_iter_;
364 } else {
365 return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_;
369 inline bool operator!=(const const_iterator& other) const {
370 return !(*this == other);
373 private:
374 friend class SmallMap;
375 inline explicit const_iterator(
376 const ManualConstructor<value_type>* init)
377 : array_iter_(init) {}
378 inline explicit const_iterator(
379 const typename NormalMap::const_iterator& init)
380 : array_iter_(NULL), hash_iter_(init) {}
382 const ManualConstructor<value_type>* array_iter_;
383 typename NormalMap::const_iterator hash_iter_;
386 iterator find(const key_type& key) {
387 key_equal compare;
388 if (size_ >= 0) {
389 for (int i = 0; i < size_; i++) {
390 if (compare(array_[i]->first, key)) {
391 return iterator(array_ + i);
394 return iterator(array_ + size_);
395 } else {
396 return iterator(map()->find(key));
400 const_iterator find(const key_type& key) const {
401 key_equal compare;
402 if (size_ >= 0) {
403 for (int i = 0; i < size_; i++) {
404 if (compare(array_[i]->first, key)) {
405 return const_iterator(array_ + i);
408 return const_iterator(array_ + size_);
409 } else {
410 return const_iterator(map()->find(key));
414 // Invalidates iterators.
415 data_type& operator[](const key_type& key) {
416 key_equal compare;
418 if (size_ >= 0) {
419 // operator[] searches backwards, favoring recently-added
420 // elements.
421 for (int i = size_-1; i >= 0; --i) {
422 if (compare(array_[i]->first, key)) {
423 return array_[i]->second;
426 if (size_ == kArraySize) {
427 ConvertToRealMap();
428 return (*map_)[key];
429 } else {
430 array_[size_].Init(key, data_type());
431 return array_[size_++]->second;
433 } else {
434 return (*map_)[key];
438 // Invalidates iterators.
439 std::pair<iterator, bool> insert(const value_type& x) {
440 key_equal compare;
442 if (size_ >= 0) {
443 for (int i = 0; i < size_; i++) {
444 if (compare(array_[i]->first, x.first)) {
445 return std::make_pair(iterator(array_ + i), false);
448 if (size_ == kArraySize) {
449 ConvertToRealMap(); // Invalidates all iterators!
450 std::pair<typename NormalMap::iterator, bool> ret = map_->insert(x);
451 return std::make_pair(iterator(ret.first), ret.second);
452 } else {
453 array_[size_].Init(x);
454 return std::make_pair(iterator(array_ + size_++), true);
456 } else {
457 std::pair<typename NormalMap::iterator, bool> ret = map_->insert(x);
458 return std::make_pair(iterator(ret.first), ret.second);
462 // Invalidates iterators.
463 template <class InputIterator>
464 void insert(InputIterator f, InputIterator l) {
465 while (f != l) {
466 insert(*f);
467 ++f;
471 iterator begin() {
472 if (size_ >= 0) {
473 return iterator(array_);
474 } else {
475 return iterator(map_->begin());
478 const_iterator begin() const {
479 if (size_ >= 0) {
480 return const_iterator(array_);
481 } else {
482 return const_iterator(map_->begin());
486 iterator end() {
487 if (size_ >= 0) {
488 return iterator(array_ + size_);
489 } else {
490 return iterator(map_->end());
493 const_iterator end() const {
494 if (size_ >= 0) {
495 return const_iterator(array_ + size_);
496 } else {
497 return const_iterator(map_->end());
501 void clear() {
502 if (size_ >= 0) {
503 for (int i = 0; i < size_; i++) {
504 array_[i].Destroy();
506 } else {
507 map_.Destroy();
509 size_ = 0;
512 // Invalidates iterators.
513 void erase(const iterator& position) {
514 if (size_ >= 0) {
515 int i = position.array_iter_ - array_;
516 array_[i].Destroy();
517 --size_;
518 if (i != size_) {
519 array_[i].Init(*array_[size_]);
520 array_[size_].Destroy();
522 } else {
523 map_->erase(position.hash_iter_);
527 size_t erase(const key_type& key) {
528 iterator iter = find(key);
529 if (iter == end()) return 0u;
530 erase(iter);
531 return 1u;
534 size_t count(const key_type& key) const {
535 return (find(key) == end()) ? 0 : 1;
538 size_t size() const {
539 if (size_ >= 0) {
540 return static_cast<size_t>(size_);
541 } else {
542 return map_->size();
546 bool empty() const {
547 if (size_ >= 0) {
548 return (size_ == 0);
549 } else {
550 return map_->empty();
554 // Returns true if we have fallen back to using the underlying map
555 // representation.
556 bool UsingFullMap() const {
557 return size_ < 0;
560 inline NormalMap* map() {
561 CHECK(UsingFullMap());
562 return map_.get();
564 inline const NormalMap* map() const {
565 CHECK(UsingFullMap());
566 return map_.get();
569 private:
570 int size_; // negative = using hash_map
572 MapInit functor_;
574 // We want to call constructors and destructors manually, but we don't
575 // want to allocate and deallocate the memory used for them separately.
576 // So, we use this crazy ManualConstructor class.
578 // Since array_ and map_ are mutually exclusive, we'll put them in a
579 // union, too. We add in a dummy_ value which quiets MSVC from otherwise
580 // giving an erroneous "union member has copy constructor" error message
581 // (C2621). This dummy member has to come before array_ to quiet the
582 // compiler.
584 // TODO(brettw) remove this and use C++11 unions when we require C++11.
585 union {
586 ManualConstructor<value_type> dummy_;
587 ManualConstructor<value_type> array_[kArraySize];
588 ManualConstructor<NormalMap> map_;
591 void ConvertToRealMap() {
592 // Move the current elements into a temporary array.
593 ManualConstructor<value_type> temp_array[kArraySize];
595 for (int i = 0; i < kArraySize; i++) {
596 temp_array[i].Init(*array_[i]);
597 array_[i].Destroy();
600 // Initialize the map.
601 size_ = -1;
602 functor_(&map_);
604 // Insert elements into it.
605 for (int i = 0; i < kArraySize; i++) {
606 map_->insert(*temp_array[i]);
607 temp_array[i].Destroy();
611 // Helpers for constructors and destructors.
612 void InitFrom(const SmallMap& src) {
613 functor_ = src.functor_;
614 size_ = src.size_;
615 if (src.size_ >= 0) {
616 for (int i = 0; i < size_; i++) {
617 array_[i].Init(*src.array_[i]);
619 } else {
620 functor_(&map_);
621 (*map_.get()) = (*src.map_.get());
624 void Destroy() {
625 if (size_ >= 0) {
626 for (int i = 0; i < size_; i++) {
627 array_[i].Destroy();
629 } else {
630 map_.Destroy();
635 template <typename NormalMap, int kArraySize, typename EqualKey,
636 typename Functor>
637 inline bool SmallMap<NormalMap, kArraySize, EqualKey,
638 Functor>::iterator::operator==(
639 const const_iterator& other) const {
640 return other == *this;
642 template <typename NormalMap, int kArraySize, typename EqualKey,
643 typename Functor>
644 inline bool SmallMap<NormalMap, kArraySize, EqualKey,
645 Functor>::iterator::operator!=(
646 const const_iterator& other) const {
647 return other != *this;
650 } // namespace base
652 #endif // BASE_CONTAINERS_SMALL_MAP_H_