Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef BASE_CONTAINERS_SMALL_MAP_H_ | 5 #ifndef BASE_CONTAINERS_SMALL_MAP_H_ |
| 6 #define BASE_CONTAINERS_SMALL_MAP_H_ | 6 #define BASE_CONTAINERS_SMALL_MAP_H_ |
| 7 | 7 |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #include <map> | 10 #include <map> |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 // WHAT TYPE OF MAP SHOULD YOU USE? | 24 // WHAT TYPE OF MAP SHOULD YOU USE? |
| 25 // -------------------------------- | 25 // -------------------------------- |
| 26 // | 26 // |
| 27 // - std::map should be the default if you're not sure, since it's the most | 27 // - std::map should be the default if you're not sure, since it's the most |
| 28 // difficult to mess up. Generally this is backed by a red-black tree. It | 28 // difficult to mess up. Generally this is backed by a red-black tree. It |
| 29 // will generate a lot of code (if you use a common key type like int or | 29 // will generate a lot of code (if you use a common key type like int or |
| 30 // string the linker will probably emiminate the duplicates). It will | 30 // string the linker will probably emiminate the duplicates). It will |
| 31 // do heap allocations for each element. | 31 // do heap allocations for each element. |
| 32 // | 32 // |
| 33 // - If you only ever keep a couple of items and have very simple usage, | 33 // - If you only ever keep a couple of items and have very simple usage, |
| 34 // consider whether a using a vector and brute-force searching it will be | 34 // use a base::flat_map. |
| 35 // the most efficient. It's not a lot of generated code (less than a | |
| 36 // red-black tree if your key is "weird" and not eliminated as duplicate of | |
| 37 // something else) and will probably be faster and do fewer heap allocations | |
| 38 // than std::map if you have just a couple of items. | |
| 39 // | 35 // |
| 40 // - base::hash_map should be used if you need O(1) lookups. It may waste | 36 // - std::unordered_map should be used if you need O(1) lookups. It may waste |
| 41 // space in the hash table, and it can be easy to write correct-looking | 37 // space in the hash table, and it can be easy to write correct-looking |
| 42 // code with the default hash function being wrong or poorly-behaving. | 38 // code with the default hash function being wrong or poorly-behaving. |
| 43 // | 39 // |
| 44 // - SmallMap combines the performance benefits of the brute-force-searched | 40 // - base::small_map combines the performance benefits of the |
| 45 // vector for small cases (no extra heap allocations), but can efficiently | 41 // brute-force-searched vector for small cases (no extra heap allocations), |
| 46 // fall back if you end up adding many items. It will generate more code | 42 // but can efficiently fall back if you end up adding many items. It will |
| 47 // than std::map (at least 160 bytes for operator[]) which is bad if you | 43 // generate more code than std::map (at least 160 bytes for operator[]) |
| 48 // have a "weird" key where map functions can't be | 44 // which is bad if you have a "weird" key where map functions can't be |
| 49 // duplicate-code-eliminated. If you have a one-off key and aren't in | 45 // duplicate-code-eliminated. If you have a one-off key and aren't in |
| 50 // performance-critical code, this bloat may negate some of the benefits and | 46 // performance-critical code, this bloat may negate some of the benefits and |
| 51 // you should consider on of the other options. | 47 // you should consider on of the other options. |
| 52 // | 48 // |
| 53 // SmallMap will pick up the comparator from the underlying map type. In | 49 // base::small_map will pick up the comparator from the underlying map type. In |
| 54 // std::map (and in MSVC additionally hash_map) only a "less" operator is | 50 // std::map (and in MSVC additionally hash_map) only a "less" operator is |
| 55 // defined, which requires us to do two comparisons per element when doing the | 51 // defined, which requires us to do two comparisons per element when doing the |
| 56 // brute-force search in the simple array. | 52 // brute-force search in the simple array. |
| 57 // | 53 // |
| 58 // We define default overrides for the common map types to avoid this | 54 // We define default overrides for the common map types to avoid this |
| 59 // double-compare, but you should be aware of this if you use your own | 55 // double-compare, but you should be aware of this if you use your own |
| 60 // operator< for your map and supply yor own version of == to the SmallMap. | 56 // operator< for your map and supply yor own version of == to the small_map. |
| 61 // You can use regular operator== by just doing: | 57 // You can use regular operator== by just doing: |
| 62 // | 58 // |
| 63 // base::SmallMap<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey> > | 59 // base::small_map<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey> > |
|
danakj
2017/04/19 17:32:16
Oh, this has an extra space in the > > too
| |
| 64 // | 60 // |
| 65 // | 61 // |
| 66 // USAGE | 62 // USAGE |
| 67 // ----- | 63 // ----- |
| 68 // | 64 // |
| 69 // NormalMap: The map type to fall back to. This also defines the key | 65 // NormalMap: The map type to fall back to. This also defines the key |
| 70 // and value types for the SmallMap. | 66 // and value types for the small_map. |
| 71 // kArraySize: The size of the initial array of results. This will be | 67 // kArraySize: The size of the initial array of results. This will be |
| 72 // allocated with the SmallMap object rather than separately on | 68 // allocated with the small_map object rather than separately on |
| 73 // the heap. Once the map grows beyond this size, the map type | 69 // the heap. Once the map grows beyond this size, the map type |
| 74 // will be used instead. | 70 // will be used instead. |
| 75 // EqualKey: A functor which tests two keys for equality. If the wrapped | 71 // EqualKey: A functor which tests two keys for equality. If the wrapped |
| 76 // map type has a "key_equal" member (hash_map does), then that will | 72 // map type has a "key_equal" member (hash_map does), then that will |
| 77 // be used by default. If the wrapped map type has a strict weak | 73 // be used by default. If the wrapped map type has a strict weak |
| 78 // ordering "key_compare" (std::map does), that will be used to | 74 // ordering "key_compare" (std::map does), that will be used to |
| 79 // implement equality by default. | 75 // implement equality by default. |
| 80 // MapInit: A functor that takes a ManualConstructor<NormalMap>* and uses it to | 76 // MapInit: A functor that takes a ManualConstructor<NormalMap>* and uses it to |
| 81 // initialize the map. This functor will be called at most once per | 77 // initialize the map. This functor will be called at most once per |
| 82 // SmallMap, when the map exceeds the threshold of kArraySize and we | 78 // small_map, when the map exceeds the threshold of kArraySize and we |
| 83 // are about to copy values from the array to the map. The functor | 79 // are about to copy values from the array to the map. The functor |
| 84 // *must* call one of the Init() methods provided by | 80 // *must* call one of the Init() methods provided by |
| 85 // ManualConstructor, since after it runs we assume that the NormalMap | 81 // ManualConstructor, since after it runs we assume that the NormalMap |
| 86 // has been initialized. | 82 // has been initialized. |
| 87 // | 83 // |
| 88 // example: | 84 // example: |
| 89 // base::SmallMap< std::map<string, int> > days; | 85 // base::small_map<std::map<string, int>> days; |
| 90 // days["sunday" ] = 0; | 86 // days["sunday" ] = 0; |
| 91 // days["monday" ] = 1; | 87 // days["monday" ] = 1; |
| 92 // days["tuesday" ] = 2; | 88 // days["tuesday" ] = 2; |
| 93 // days["wednesday"] = 3; | 89 // days["wednesday"] = 3; |
| 94 // days["thursday" ] = 4; | 90 // days["thursday" ] = 4; |
| 95 // days["friday" ] = 5; | 91 // days["friday" ] = 5; |
| 96 // days["saturday" ] = 6; | 92 // days["saturday" ] = 6; |
| 97 // | 93 // |
| 98 // You should assume that SmallMap might invalidate all the iterators | 94 // You should assume that small_map might invalidate all the iterators |
| 99 // on any call to erase(), insert() and operator[]. | 95 // on any call to erase(), insert() and operator[]. |
| 100 | 96 |
| 101 namespace internal { | 97 namespace internal { |
| 102 | 98 |
| 103 template <typename NormalMap> | 99 template <typename NormalMap> |
| 104 class SmallMapDefaultInit { | 100 class small_map_default_init { |
| 105 public: | 101 public: |
| 106 void operator()(ManualConstructor<NormalMap>* map) const { | 102 void operator()(ManualConstructor<NormalMap>* map) const { |
| 107 map->Init(); | 103 map->Init(); |
| 108 } | 104 } |
| 109 }; | 105 }; |
| 110 | 106 |
| 111 // has_key_equal<M>::value is true iff there exists a type M::key_equal. This is | 107 // has_key_equal<M>::value is true iff there exists a type M::key_equal. This is |
| 112 // used to dispatch to one of the select_equal_key<> metafunctions below. | 108 // used to dispatch to one of the select_equal_key<> metafunctions below. |
| 113 template <typename M> | 109 template <typename M> |
| 114 struct has_key_equal { | 110 struct has_key_equal { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 171 // hash_map<>. | 167 // hash_map<>. |
| 172 template <typename M> | 168 template <typename M> |
| 173 struct select_equal_key<M, true> { | 169 struct select_equal_key<M, true> { |
| 174 typedef typename M::key_equal equal_key; | 170 typedef typename M::key_equal equal_key; |
| 175 }; | 171 }; |
| 176 | 172 |
| 177 } // namespace internal | 173 } // namespace internal |
| 178 | 174 |
| 179 template <typename NormalMap, | 175 template <typename NormalMap, |
| 180 int kArraySize = 4, | 176 int kArraySize = 4, |
| 181 typename EqualKey = | 177 typename EqualKey = typename internal::select_equal_key< |
| 182 typename internal::select_equal_key< | 178 NormalMap, |
| 183 NormalMap, | 179 internal::has_key_equal<NormalMap>::value>::equal_key, |
| 184 internal::has_key_equal<NormalMap>::value>::equal_key, | 180 typename MapInit = internal::small_map_default_init<NormalMap>> |
| 185 typename MapInit = internal::SmallMapDefaultInit<NormalMap> > | 181 class small_map { |
| 186 class SmallMap { | |
| 187 // We cannot rely on the compiler to reject array of size 0. In | 182 // We cannot rely on the compiler to reject array of size 0. In |
| 188 // particular, gcc 2.95.3 does it but later versions allow 0-length | 183 // particular, gcc 2.95.3 does it but later versions allow 0-length |
| 189 // arrays. Therefore, we explicitly reject non-positive kArraySize | 184 // arrays. Therefore, we explicitly reject non-positive kArraySize |
| 190 // here. | 185 // here. |
| 191 static_assert(kArraySize > 0, "default initial size should be positive"); | 186 static_assert(kArraySize > 0, "default initial size should be positive"); |
| 192 | 187 |
| 193 public: | 188 public: |
| 194 typedef typename NormalMap::key_type key_type; | 189 typedef typename NormalMap::key_type key_type; |
| 195 typedef typename NormalMap::mapped_type data_type; | 190 typedef typename NormalMap::mapped_type data_type; |
| 196 typedef typename NormalMap::mapped_type mapped_type; | 191 typedef typename NormalMap::mapped_type mapped_type; |
| 197 typedef typename NormalMap::value_type value_type; | 192 typedef typename NormalMap::value_type value_type; |
| 198 typedef EqualKey key_equal; | 193 typedef EqualKey key_equal; |
| 199 | 194 |
| 200 SmallMap() : size_(0), functor_(MapInit()) {} | 195 small_map() : size_(0), functor_(MapInit()) {} |
| 201 | 196 |
| 202 explicit SmallMap(const MapInit& functor) : size_(0), functor_(functor) {} | 197 explicit small_map(const MapInit& functor) : size_(0), functor_(functor) {} |
| 203 | 198 |
| 204 // Allow copy-constructor and assignment, since STL allows them too. | 199 // Allow copy-constructor and assignment, since STL allows them too. |
| 205 SmallMap(const SmallMap& src) { | 200 small_map(const small_map& src) { |
| 206 // size_ and functor_ are initted in InitFrom() | 201 // size_ and functor_ are initted in InitFrom() |
| 207 InitFrom(src); | 202 InitFrom(src); |
| 208 } | 203 } |
| 209 void operator=(const SmallMap& src) { | 204 void operator=(const small_map& src) { |
| 210 if (&src == this) return; | 205 if (&src == this) return; |
| 211 | 206 |
| 212 // This is not optimal. If src and dest are both using the small | 207 // This is not optimal. If src and dest are both using the small |
| 213 // array, we could skip the teardown and reconstruct. One problem | 208 // array, we could skip the teardown and reconstruct. One problem |
| 214 // to be resolved is that the value_type itself is pair<const K, | 209 // to be resolved is that the value_type itself is pair<const K, |
| 215 // V>, and const K is not assignable. | 210 // V>, and const K is not assignable. |
| 216 Destroy(); | 211 Destroy(); |
| 217 InitFrom(src); | 212 InitFrom(src); |
| 218 } | 213 } |
| 219 ~SmallMap() { | 214 ~small_map() { Destroy(); } |
| 220 Destroy(); | |
| 221 } | |
| 222 | 215 |
| 223 class const_iterator; | 216 class const_iterator; |
| 224 | 217 |
| 225 class iterator { | 218 class iterator { |
| 226 public: | 219 public: |
| 227 typedef typename NormalMap::iterator::iterator_category iterator_category; | 220 typedef typename NormalMap::iterator::iterator_category iterator_category; |
| 228 typedef typename NormalMap::iterator::value_type value_type; | 221 typedef typename NormalMap::iterator::value_type value_type; |
| 229 typedef typename NormalMap::iterator::difference_type difference_type; | 222 typedef typename NormalMap::iterator::difference_type difference_type; |
| 230 typedef typename NormalMap::iterator::pointer pointer; | 223 typedef typename NormalMap::iterator::pointer pointer; |
| 231 typedef typename NormalMap::iterator::reference reference; | 224 typedef typename NormalMap::iterator::reference reference; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 } | 276 } |
| 284 | 277 |
| 285 inline bool operator!=(const iterator& other) const { | 278 inline bool operator!=(const iterator& other) const { |
| 286 return !(*this == other); | 279 return !(*this == other); |
| 287 } | 280 } |
| 288 | 281 |
| 289 bool operator==(const const_iterator& other) const; | 282 bool operator==(const const_iterator& other) const; |
| 290 bool operator!=(const const_iterator& other) const; | 283 bool operator!=(const const_iterator& other) const; |
| 291 | 284 |
| 292 private: | 285 private: |
| 293 friend class SmallMap; | 286 friend class small_map; |
| 294 friend class const_iterator; | 287 friend class const_iterator; |
| 295 inline explicit iterator(ManualConstructor<value_type>* init) | 288 inline explicit iterator(ManualConstructor<value_type>* init) |
| 296 : array_iter_(init) {} | 289 : array_iter_(init) {} |
| 297 inline explicit iterator(const typename NormalMap::iterator& init) | 290 inline explicit iterator(const typename NormalMap::iterator& init) |
| 298 : array_iter_(NULL), hash_iter_(init) {} | 291 : array_iter_(NULL), hash_iter_(init) {} |
| 299 | 292 |
| 300 ManualConstructor<value_type>* array_iter_; | 293 ManualConstructor<value_type>* array_iter_; |
| 301 typename NormalMap::iterator hash_iter_; | 294 typename NormalMap::iterator hash_iter_; |
| 302 }; | 295 }; |
| 303 | 296 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 365 } else { | 358 } else { |
| 366 return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_; | 359 return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_; |
| 367 } | 360 } |
| 368 } | 361 } |
| 369 | 362 |
| 370 inline bool operator!=(const const_iterator& other) const { | 363 inline bool operator!=(const const_iterator& other) const { |
| 371 return !(*this == other); | 364 return !(*this == other); |
| 372 } | 365 } |
| 373 | 366 |
| 374 private: | 367 private: |
| 375 friend class SmallMap; | 368 friend class small_map; |
| 376 inline explicit const_iterator( | 369 inline explicit const_iterator( |
| 377 const ManualConstructor<value_type>* init) | 370 const ManualConstructor<value_type>* init) |
| 378 : array_iter_(init) {} | 371 : array_iter_(init) {} |
| 379 inline explicit const_iterator( | 372 inline explicit const_iterator( |
| 380 const typename NormalMap::const_iterator& init) | 373 const typename NormalMap::const_iterator& init) |
| 381 : array_iter_(NULL), hash_iter_(init) {} | 374 : array_iter_(NULL), hash_iter_(init) {} |
| 382 | 375 |
| 383 const ManualConstructor<value_type>* array_iter_; | 376 const ManualConstructor<value_type>* array_iter_; |
| 384 typename NormalMap::const_iterator hash_iter_; | 377 typename NormalMap::const_iterator hash_iter_; |
| 385 }; | 378 }; |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 600 functor_(&map_); | 593 functor_(&map_); |
| 601 | 594 |
| 602 // Insert elements into it. | 595 // Insert elements into it. |
| 603 for (int i = 0; i < kArraySize; i++) { | 596 for (int i = 0; i < kArraySize; i++) { |
| 604 map_->insert(std::move(*temp_array[i])); | 597 map_->insert(std::move(*temp_array[i])); |
| 605 temp_array[i].Destroy(); | 598 temp_array[i].Destroy(); |
| 606 } | 599 } |
| 607 } | 600 } |
| 608 | 601 |
| 609 // Helpers for constructors and destructors. | 602 // Helpers for constructors and destructors. |
| 610 void InitFrom(const SmallMap& src) { | 603 void InitFrom(const small_map& src) { |
| 611 functor_ = src.functor_; | 604 functor_ = src.functor_; |
| 612 size_ = src.size_; | 605 size_ = src.size_; |
| 613 if (src.size_ >= 0) { | 606 if (src.size_ >= 0) { |
| 614 for (int i = 0; i < size_; i++) { | 607 for (int i = 0; i < size_; i++) { |
| 615 array_[i].Init(*src.array_[i]); | 608 array_[i].Init(*src.array_[i]); |
| 616 } | 609 } |
| 617 } else { | 610 } else { |
| 618 functor_(&map_); | 611 functor_(&map_); |
| 619 (*map_.get()) = (*src.map_.get()); | 612 (*map_.get()) = (*src.map_.get()); |
| 620 } | 613 } |
| 621 } | 614 } |
| 622 void Destroy() { | 615 void Destroy() { |
| 623 if (size_ >= 0) { | 616 if (size_ >= 0) { |
| 624 for (int i = 0; i < size_; i++) { | 617 for (int i = 0; i < size_; i++) { |
| 625 array_[i].Destroy(); | 618 array_[i].Destroy(); |
| 626 } | 619 } |
| 627 } else { | 620 } else { |
| 628 map_.Destroy(); | 621 map_.Destroy(); |
| 629 } | 622 } |
| 630 } | 623 } |
| 631 }; | 624 }; |
| 632 | 625 |
| 633 template <typename NormalMap, int kArraySize, typename EqualKey, | 626 template <typename NormalMap, |
| 627 int kArraySize, | |
| 628 typename EqualKey, | |
| 634 typename Functor> | 629 typename Functor> |
| 635 inline bool SmallMap<NormalMap, kArraySize, EqualKey, | 630 inline bool small_map<NormalMap, kArraySize, EqualKey, Functor>::iterator:: |
| 636 Functor>::iterator::operator==( | 631 operator==(const const_iterator& other) const { |
| 637 const const_iterator& other) const { | |
| 638 return other == *this; | 632 return other == *this; |
| 639 } | 633 } |
| 640 template <typename NormalMap, int kArraySize, typename EqualKey, | 634 template <typename NormalMap, |
| 635 int kArraySize, | |
| 636 typename EqualKey, | |
| 641 typename Functor> | 637 typename Functor> |
| 642 inline bool SmallMap<NormalMap, kArraySize, EqualKey, | 638 inline bool small_map<NormalMap, kArraySize, EqualKey, Functor>::iterator:: |
| 643 Functor>::iterator::operator!=( | 639 operator!=(const const_iterator& other) const { |
| 644 const const_iterator& other) const { | |
| 645 return other != *this; | 640 return other != *this; |
| 646 } | 641 } |
| 647 | 642 |
| 648 } // namespace base | 643 } // namespace base |
| 649 | 644 |
| 650 #endif // BASE_CONTAINERS_SMALL_MAP_H_ | 645 #endif // BASE_CONTAINERS_SMALL_MAP_H_ |
| OLD | NEW |