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_
12 #include "base/basictypes.h"
13 #include "base/containers/hash_tables.h"
14 #include "base/logging.h"
15 #include "base/memory/manual_constructor.h"
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
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> >
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.
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[].
102 template <typename NormalMap
>
103 class SmallMapDefaultInit
{
105 void operator()(ManualConstructor
<NormalMap
>* map
) const {
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
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
{
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> {
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> {
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.,
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
,
181 typename
internal::select_equal_key
<
183 internal::has_key_equal
<NormalMap
>::value
>::equal_key
,
184 typename MapInit
= internal::SmallMapDefaultInit
<NormalMap
> >
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
190 COMPILE_ASSERT(kArraySize
> 0, default_initial_size_should_be_positive
);
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()
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.
222 class const_iterator
;
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
) {
242 inline iterator
operator++(int /*unused*/) {
243 iterator
result(*this);
247 inline iterator
& operator--() {
248 if (array_iter_
!= NULL
) {
255 inline iterator
operator--(int /*unused*/) {
256 iterator
result(*this);
260 inline value_type
* operator->() const {
261 if (array_iter_
!= NULL
) {
262 return array_iter_
->get();
264 return hash_iter_
.operator->();
268 inline value_type
& operator*() const {
269 if (array_iter_
!= NULL
) {
270 return *array_iter_
->get();
276 inline bool operator==(const iterator
& other
) const {
277 if (array_iter_
!= NULL
) {
278 return array_iter_
== other
.array_iter_
;
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;
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
{
305 typedef typename
NormalMap::const_iterator::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
) {
325 inline const_iterator
operator++(int /*unused*/) {
326 const_iterator
result(*this);
331 inline const_iterator
& operator--() {
332 if (array_iter_
!= NULL
) {
339 inline const_iterator
operator--(int /*unused*/) {
340 const_iterator
result(*this);
345 inline const value_type
* operator->() const {
346 if (array_iter_
!= NULL
) {
347 return array_iter_
->get();
349 return hash_iter_
.operator->();
353 inline const value_type
& operator*() const {
354 if (array_iter_
!= NULL
) {
355 return *array_iter_
->get();
361 inline bool operator==(const const_iterator
& other
) const {
362 if (array_iter_
!= NULL
) {
363 return array_iter_
== other
.array_iter_
;
365 return other
.array_iter_
== NULL
&& hash_iter_
== other
.hash_iter_
;
369 inline bool operator!=(const const_iterator
& other
) const {
370 return !(*this == other
);
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
) {
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_
);
396 return iterator(map()->find(key
));
400 const_iterator
find(const key_type
& key
) const {
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_
);
410 return const_iterator(map()->find(key
));
414 // Invalidates iterators.
415 data_type
& operator[](const key_type
& key
) {
419 // operator[] searches backwards, favoring recently-added
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
) {
430 array_
[size_
].Init(key
, data_type());
431 return array_
[size_
++]->second
;
438 // Invalidates iterators.
439 std::pair
<iterator
, bool> insert(const value_type
& x
) {
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
);
453 array_
[size_
].Init(x
);
454 return std::make_pair(iterator(array_
+ size_
++), true);
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
) {
473 return iterator(array_
);
475 return iterator(map_
->begin());
478 const_iterator
begin() const {
480 return const_iterator(array_
);
482 return const_iterator(map_
->begin());
488 return iterator(array_
+ size_
);
490 return iterator(map_
->end());
493 const_iterator
end() const {
495 return const_iterator(array_
+ size_
);
497 return const_iterator(map_
->end());
503 for (int i
= 0; i
< size_
; i
++) {
512 // Invalidates iterators.
513 void erase(const iterator
& position
) {
515 int i
= position
.array_iter_
- array_
;
519 array_
[i
].Init(*array_
[size_
]);
520 array_
[size_
].Destroy();
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;
534 size_t count(const key_type
& key
) const {
535 return (find(key
) == end()) ? 0 : 1;
538 size_t size() const {
540 return static_cast<size_t>(size_
);
550 return map_
->empty();
554 // Returns true if we have fallen back to using the underlying map
556 bool UsingFullMap() const {
560 inline NormalMap
* map() {
561 CHECK(UsingFullMap());
564 inline const NormalMap
* map() const {
565 CHECK(UsingFullMap());
570 int size_
; // negative = using hash_map
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
584 // TODO(brettw) remove this and use C++11 unions when we require C++11.
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
]);
600 // Initialize the 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_
;
615 if (src
.size_
>= 0) {
616 for (int i
= 0; i
< size_
; i
++) {
617 array_
[i
].Init(*src
.array_
[i
]);
621 (*map_
.get()) = (*src
.map_
.get());
626 for (int i
= 0; i
< size_
; i
++) {
635 template <typename NormalMap
, int kArraySize
, typename EqualKey
,
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
,
644 inline bool SmallMap
<NormalMap
, kArraySize
, EqualKey
,
645 Functor
>::iterator::operator!=(
646 const const_iterator
& other
) const {
647 return other
!= *this;
652 #endif // BASE_CONTAINERS_SMALL_MAP_H_