blob: 3e17d7189eb3511f57dbb3dca0aa78938c12094f [file] [log] [blame]
Brian Silverman70325d62015-09-20 17:00:43 -04001// Copyright (c) 2006, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29// ---
30//
31// Author: kenton@google.com (Kenton Varda)
32//
33// small_map is a drop-in replacement for map or hash_map. It uses a fixed
34// array to store a certain number of elements, then reverts to using a
35// map or hash_map when it runs out of space. For maps that are typically
36// small, this can be considerably faster than using something like hash_map
37// directly, as hash_map is optimized for large data sets. Of course, in
38// order for this to be a significant win, you have to have a situation where
39// you are using lots and lots of these small maps. One such situation is
40// MessageSet: A set of search results may contain thousands of MessageSets,
41// each containing only a couple items.
42//
43// TODO(kenton): This is very minimal, and was originally written for a
44// very specific use (MessageSet). It only implements a few core methods
45// of the STL associative container interface, though you are welcome to
46// extend it.
47
48#ifndef UTIL_GTL_SMALL_MAP_H_
49#define UTIL_GTL_SMALL_MAP_H_
50
51#include <config.h>
52#include <assert.h>
53#include <utility> // for make_pair()
54#include "base/manual_constructor.h"
55
56namespace ctemplate {
57
58template <bool> struct CompileAssert { };
59#define COMPILE_ASSERT(expr, msg) \
60 typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
61
62// An STL-like associative container which starts out backed by a simple
63// array but switches to some other container type if it grows beyond a
64// fixed size.
65//
66// NormalMap: The map type to fall back to. This also defines the key
67// and value types for the small_map.
68// kArraySize: The size of the initial array of results. Once the map
69// grows beyond this size, the map type will be used instead.
70// EqualKey: A functor which tests two keys for equality. If the wrapped
71// map type has a "key_equal" member (hash_map does), then that
72// will be used by default. Otherwise you must specify this
73// manually.
74// MapInit: A functor that takes a ManualConstructor<NormalMap>* and uses it to
75// initialize the map. This functor will be called at most once per
76// small_map, when the map exceeds the threshold of kArraySize and we
77// are about to copy values from the array to the map. The functor
78// *must* call one of the Init() methods provided by
79// ManualConstructor, since after it runs we assume that the NormalMap
80// has been initialized.
81//
82// example:
83// small_map<hash_map<string, int> > days;
84// days["sunday" ] = 0;
85// days["monday" ] = 1;
86// days["tuesday" ] = 2;
87// days["wednesday"] = 3;
88// days["thursday" ] = 4;
89// days["friday" ] = 5;
90// days["saturday" ] = 6;
91//
92// You should assume that small_map might invalidate all the iterators
93// on any call to erase(), insert() and operator[].
94template <typename NormalMap>
95class small_map_default_init {
96 public:
97 void operator ()(ManualConstructor<NormalMap>* map) const {
98 map->Init();
99 }
100};
101
102template <typename NormalMap,
103 int kArraySize = 4,
104 typename EqualKey = typename NormalMap::key_equal,
105 typename MapInit = small_map_default_init<NormalMap> >
106class small_map {
107 // We cannot rely on the compiler to reject array of size 0. In
108 // particular, gcc 2.95.3 does it but later versions allow 0-length
109 // arrays. Therefore, we explicitly reject non-positive kArraySize
110 // here.
111 COMPILE_ASSERT(kArraySize > 0, default_initial_size_should_be_positive);
112
113 public:
114 typedef typename NormalMap::key_type key_type;
115 typedef typename NormalMap::mapped_type data_type;
116 typedef typename NormalMap::mapped_type mapped_type;
117 typedef typename NormalMap::value_type value_type;
118 typedef EqualKey key_equal;
119
120 small_map() : size_(0), functor_(MapInit()) {}
121
122 explicit small_map(const MapInit& functor) : size_(0), functor_(functor) {}
123
124 // Allow copy-constructor and assignment, since STL allows them too.
125 small_map(const small_map& src) {
126 // size_ and functor_ are initted in InitFrom()
127 InitFrom(src);
128 }
129 void operator=(const small_map& src) {
130 if (&src == this) return;
131
132 // This is not optimal. If src and dest are both using the small
133 // array, we could skip the teardown and reconstruct. One problem
134 // to be resolved is that the value_type itself is pair<const K,
135 // V>, and const K is not assignable.
136 Destroy();
137 InitFrom(src);
138 }
139 ~small_map() {
140 Destroy();
141 }
142
143 class const_iterator;
144
145 class iterator {
146 public:
147 typedef typename NormalMap::iterator::iterator_category iterator_category;
148 typedef typename NormalMap::iterator::value_type value_type;
149 typedef typename NormalMap::iterator::difference_type difference_type;
150 typedef typename NormalMap::iterator::pointer pointer;
151 typedef typename NormalMap::iterator::reference reference;
152
153 inline iterator(): array_iter_(NULL) {}
154
155 inline iterator& operator++() {
156 if (array_iter_ != NULL) {
157 ++array_iter_;
158 } else {
159 ++hash_iter_;
160 }
161 return *this;
162 }
163 inline iterator operator++(int) {
164 iterator result(*this);
165 ++(*this);
166 return result;
167 }
168 inline iterator& operator--() {
169 if (array_iter_ != NULL) {
170 --array_iter_;
171 } else {
172 --hash_iter_;
173 }
174 return *this;
175 }
176 inline iterator operator--(int) {
177 iterator result(*this);
178 --(*this);
179 return result;
180 }
181 inline value_type* operator->() const {
182 if (array_iter_ != NULL) {
183 return array_iter_->get();
184 } else {
185 return hash_iter_.operator->();
186 }
187 }
188
189 inline value_type& operator*() const {
190 if (array_iter_ != NULL) {
191 return *array_iter_->get();
192 } else {
193 return *hash_iter_;
194 }
195 }
196
197 inline bool operator==(const iterator& other) const {
198 if (array_iter_ != NULL) {
199 return array_iter_ == other.array_iter_;
200 } else {
201 return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_;
202 }
203 }
204
205 inline bool operator!=(const iterator& other) const {
206 return !(*this == other);
207 }
208
209 bool operator==(const const_iterator& other) const;
210 bool operator!=(const const_iterator& other) const;
211
212 private:
213 friend class small_map;
214 friend class const_iterator;
215 inline explicit iterator(ManualConstructor<value_type>* init)
216 : array_iter_(init) {}
217 inline explicit iterator(const typename NormalMap::iterator& init)
218 : array_iter_(NULL), hash_iter_(init) {}
219
220 ManualConstructor<value_type>* array_iter_;
221 typename NormalMap::iterator hash_iter_;
222 };
223
224 class const_iterator {
225 public:
226 typedef typename NormalMap::const_iterator::iterator_category iterator_category;
227 typedef typename NormalMap::const_iterator::value_type value_type;
228 typedef typename NormalMap::const_iterator::difference_type difference_type;
229 typedef typename NormalMap::const_iterator::pointer pointer;
230 typedef typename NormalMap::const_iterator::reference reference;
231
232 inline const_iterator(): array_iter_(NULL) {}
233 inline const_iterator(const iterator& other)
234 : array_iter_(other.array_iter_), hash_iter_(other.hash_iter_) {}
235
236 inline const_iterator& operator++() {
237 if (array_iter_ != NULL) {
238 ++array_iter_;
239 } else {
240 ++hash_iter_;
241 }
242 return *this;
243 }
244 inline const_iterator operator++(int) {
245 const_iterator result(*this);
246 ++(*this);
247 return result;
248 }
249
250 inline const_iterator& operator--() {
251 if (array_iter_ != NULL) {
252 --array_iter_;
253 } else {
254 --hash_iter_;
255 }
256 return *this;
257 }
258 inline const_iterator operator--(int) {
259 const_iterator result(*this);
260 --(*this);
261 return result;
262 }
263
264 inline const value_type* operator->() const {
265 if (array_iter_ != NULL) {
266 return array_iter_->get();
267 } else {
268 return hash_iter_.operator->();
269 }
270 }
271
272 inline const value_type& operator*() const {
273 if (array_iter_ != NULL) {
274 return *array_iter_->get();
275 } else {
276 return *hash_iter_;
277 }
278 }
279
280 inline bool operator==(const const_iterator& other) const {
281 if (array_iter_ != NULL) {
282 return array_iter_ == other.array_iter_;
283 } else {
284 return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_;
285 }
286 }
287
288 inline bool operator!=(const const_iterator& other) const {
289 return !(*this == other);
290 }
291
292 private:
293 friend class small_map;
294 inline explicit const_iterator(
295 const ManualConstructor<value_type>* init)
296 : array_iter_(init) {}
297 inline explicit const_iterator(
298 const typename NormalMap::const_iterator& init)
299 : array_iter_(NULL), hash_iter_(init) {}
300
301 const ManualConstructor<value_type>* array_iter_;
302 typename NormalMap::const_iterator hash_iter_;
303 };
304
305 iterator find(const key_type& key) {
306 key_equal compare;
307 if (size_ >= 0) {
308 for (int i = 0; i < size_; i++) {
309 if (compare(array_[i]->first, key)) {
310 return iterator(array_ + i);
311 }
312 }
313 return iterator(array_ + size_);
314 } else {
315 return iterator(map()->find(key));
316 }
317 }
318
319 const_iterator find(const key_type& key) const {
320 key_equal compare;
321 if (size_ >= 0) {
322 for (int i = 0; i < size_; i++) {
323 if (compare(array_[i]->first, key)) {
324 return const_iterator(array_ + i);
325 }
326 }
327 return const_iterator(array_ + size_);
328 } else {
329 return const_iterator(map()->find(key));
330 }
331 }
332
333 // Invalidates iterators.
334 data_type& operator[](const key_type& key) {
335 key_equal compare;
336
337 if (size_ >= 0) {
338 // operator[] searches backwards, favoring recently-added
339 // elements.
340 for (int i = size_-1; i >= 0; --i) {
341 if (compare(array_[i]->first, key)) {
342 return array_[i]->second;
343 }
344 }
345 if (size_ == kArraySize) {
346 ConvertToRealMap();
347 return (*map_)[key];
348 } else {
349 array_[size_].Init(key, data_type());
350 return array_[size_++]->second;
351 }
352 } else {
353 return (*map_)[key];
354 }
355 }
356
357 // Invalidates iterators.
358 std::pair<iterator, bool> insert(const value_type& x) {
359 key_equal compare;
360
361 if (size_ >= 0) {
362 for (int i = 0; i < size_; i++) {
363 if (compare(array_[i]->first, x.first)) {
364 return std::make_pair(iterator(array_ + i), false);
365 }
366 }
367 if (size_ == kArraySize) {
368 ConvertToRealMap(); // Invalidates all iterators!
369 std::pair<typename NormalMap::iterator, bool> ret = map_->insert(x);
370 return std::make_pair(iterator(ret.first), ret.second);
371 } else {
372 array_[size_].Init(x);
373 return std::make_pair(iterator(array_ + size_++), true);
374 }
375 } else {
376 std::pair<typename NormalMap::iterator, bool> ret = map_->insert(x);
377 return std::make_pair(iterator(ret.first), ret.second);
378 }
379 }
380
381 // Invalidates iterators.
382 template <class InputIterator>
383 void insert(InputIterator f, InputIterator l) {
384 while (f != l) {
385 insert(*f);
386 ++f;
387 }
388 }
389
390 iterator begin() {
391 if (size_ >= 0) {
392 return iterator(array_);
393 } else {
394 return iterator(map_->begin());
395 }
396 }
397 const_iterator begin() const {
398 if (size_ >= 0) {
399 return const_iterator(array_);
400 } else {
401 return const_iterator(map_->begin());
402 }
403 }
404
405 iterator end() {
406 if (size_ >= 0) {
407 return iterator(array_ + size_);
408 } else {
409 return iterator(map_->end());
410 }
411 }
412 const_iterator end() const {
413 if (size_ >= 0) {
414 return const_iterator(array_ + size_);
415 } else {
416 return const_iterator(map_->end());
417 }
418 }
419
420 void clear() {
421 if (size_ >= 0) {
422 for (int i = 0; i < size_; i++) {
423 array_[i].Destroy();
424 }
425 } else {
426 map_.Destroy();
427 }
428 size_ = 0;
429 }
430
431 // Invalidates iterators.
432 void erase(const iterator& position) {
433 if (size_ >= 0) {
434 int i = position.array_iter_ - array_;
435 array_[i].Destroy();
436 --size_;
437 if (i != size_) {
438 array_[i].Init(*array_[size_]);
439 array_[size_].Destroy();
440 }
441 } else {
442 map_->erase(position.hash_iter_);
443 }
444 }
445
446 int erase(const key_type& key) {
447 iterator iter = find(key);
448 if (iter == end()) return 0;
449 erase(iter);
450 return 1;
451 }
452
453 int count(const key_type& key) const {
454 return (find(key) == end()) ? 0 : 1;
455 }
456
457 int size() const {
458 if (size_ >= 0) {
459 return size_;
460 } else {
461 return map_->size();
462 }
463 }
464
465 bool empty() const {
466 if (size_ >= 0) {
467 return (size_ == 0);
468 } else {
469 return map_->empty();
470 }
471 }
472
473 // Returns true if we have fallen back to using the underlying map
474 // representation.
475 bool using_full_map() const {
476 return size_ < 0;
477 }
478
479 inline NormalMap* map() {
480 assert(using_full_map());
481 return map_.get();
482 }
483 inline const NormalMap* map() const {
484 assert(using_full_map());
485 return map_.get();
486 }
487
488 private:
489 int size_; // negative = using hash_map
490
491 MapInit functor_;
492
493 // We want to call constructors and destructors manually, but we don't
494 // want to allocate and deallocate the memory used for them separately.
495 // So, we use this crazy ManualConstructor class.
496 //
497 // Since array_ and map_ are mutually exclusive, we'll put them in a
498 // union, too. We add in a dummy_ value which quiets MSVC (both
499 // 7.1 and 8.0) from otherwise giving an erroneous "union member has
500 // copy constructor" error message (C2621). This dummy member has
501 // to come before array_ to quiet the compiler. Shrug.
502 union {
503 ManualConstructor<value_type> dummy_;
504 ManualConstructor<value_type> array_[kArraySize];
505 ManualConstructor<NormalMap> map_;
506 };
507
508 void ConvertToRealMap() {
509 // Move the current elements into a temporary array.
510 ManualConstructor<value_type> temp_array[kArraySize];
511
512 for (int i = 0; i < kArraySize; i++) {
513 temp_array[i].Init(*array_[i]);
514 array_[i].Destroy();
515 }
516
517 // Initialize the map.
518 size_ = -1;
519 functor_(&map_);
520
521 // Insert elements into it.
522 for (int i = 0; i < kArraySize; i++) {
523 map_->insert(*temp_array[i]);
524 temp_array[i].Destroy();
525 }
526 }
527
528 // Helpers for constructors and destructors.
529 void InitFrom(const small_map& src) {
530 functor_ = src.functor_;
531 size_ = src.size_;
532 if (src.size_ >= 0) {
533 for (int i = 0; i < size_; i++) {
534 array_[i].Init(*src.array_[i]);
535 }
536 } else {
537 functor_(&map_);
538 (*map_.get()) = (*src.map_.get());
539 }
540 }
541 void Destroy() {
542 if (size_ >= 0) {
543 for (int i = 0; i < size_; i++) {
544 array_[i].Destroy();
545 }
546 } else {
547 map_.Destroy();
548 }
549 }
550};
551
552template <typename NormalMap, int kArraySize, typename EqualKey,
553 typename Functor>
554inline bool small_map<NormalMap, kArraySize, EqualKey,
555 Functor>::iterator::operator==(
556 const const_iterator& other) const {
557 return other == *this;
558}
559template <typename NormalMap, int kArraySize, typename EqualKey,
560 typename Functor>
561inline bool small_map<NormalMap, kArraySize, EqualKey,
562 Functor>::iterator::operator!=(
563 const const_iterator& other) const {
564 return other != *this;
565}
566
567}
568
569#endif // UTIL_GTL_SMALL_MAP_H_