Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2051)

Side by Side Diff: base/containers/small_map.h

Issue 2825853002: Improvements to uses of base::SmallMap (Closed)
Patch Set: Unit tests Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698