-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathflat_map.h
640 lines (561 loc) · 30.2 KB
/
flat_map.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Date: Wed Nov 27 12:59:20 CST 2013
// This closed addressing hash-map puts first linked node in bucket array
// directly to save an extra memory indirection. As a result, this map yields
// close performance to raw array on nearly all operations, probably being the
// fastest hashmap for small-sized key/value ever.
//
// Performance comparisons between several maps:
// [ value = 8 bytes ]
// Sequentially inserting 100 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/20/300/290/2010/210/230
// Sequentially erasing 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/20/1700/150/160/170/250
// Sequentially inserting 1000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 16/15/360/342/206/195/219
// Sequentially erasing 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 15/18/178/159/149/159/149
// Sequentially inserting 10000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 15/15/415/410/200/192/235
// Sequentially erasing 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 14/17/201/181/151/181/154
// [ value = 32 bytes ]
// Sequentially inserting 100 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/10/280/280/250/200/230
// Sequentially erasing 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/10/2070/150/160/160/160
// Sequentially inserting 1000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 17/17/330/329/207/185/212
// Sequentially erasing 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/17/172/163/146/157/148
// Sequentially inserting 10000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 17/17/405/406/197/185/215
// Sequentially erasing 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/16/206/188/158/168/159
// [ value = 128 bytes ]
// Sequentially inserting 100 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/30/290/290/420/220/250
// Sequentially erasing 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/10/180/150/160/160/160
// Sequentially inserting 1000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 22/25/352/349/213/193/222
// Sequentially erasing 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/17/170/165/160/171/157
// Sequentially inserting 10000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 21/24/416/422/210/206/242
// Sequentially erasing 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 16/16/213/190/163/171/159
// [ value = 8 bytes ]
// Randomly inserting 100 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/20/290/260/250/220/220
// Randomly erasing 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/20/240/220/170/170/180
// Randomly inserting 1000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 16/15/315/309/206/191/215
// Randomly erasing 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 15/17/258/240/155/193/156
// Randomly inserting 10000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 15/16/378/363/208/191/210
// Randomly erasing 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 14/16/311/290/162/187/169
// [ value = 32 bytes ]
// Randomly inserting 100 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/20/280/270/240/230/220
// Randomly erasing 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 10/20/250/220/170/180/160
// Randomly inserting 1000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 18/18/310/304/209/192/209
// Randomly erasing 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/16/255/247/155/175/152
// Randomly inserting 10000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 17/17/381/367/209/197/214
// Randomly erasing 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 15/17/310/296/163/188/168
// [ value = 128 bytes ]
// Randomly inserting 100 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 30/40/300/290/280/230/230
// Randomly erasing 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/20/230/220/160/180/170
// Randomly inserting 1000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 29/33/327/329/219/197/220
// Randomly erasing 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 14/16/257/247/159/182/156
// Randomly inserting 10000 into FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 35/39/398/400/220/213/246
// Randomly erasing 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 35/36/330/319/221/224/200
// [ value = 8 bytes ]
// Seeking 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 10/20/140/130/60/70/50
// Seeking 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/172/161/77/54/46
// Seeking 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/16/216/211/73/56/51
// [ value = 32 bytes ]
// Seeking 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 10/20/130/130/70/60/50
// Seeking 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/174/163/73/54/49
// Seeking 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/218/217/75/58/52
// [ value = 128 bytes ]
// Seeking 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/10/140/130/80/50/60
// Seeking 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/173/171/73/55/49
// Seeking 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 26/22/238/252/107/89/91
// [ value = 8 bytes ]
// Seeking 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/10/140/130/70/50/60
// Seeking 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/14/180/160/68/57/47
// Seeking 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/221/210/72/57/51
// [ value = 32 bytes ]
// Seeking 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 20/10/140/130/70/60/50
// Seeking 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/167/160/69/53/50
// Seeking 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 15/14/224/219/75/59/52
// [ value = 128 bytes ]
// Seeking 100 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 10/10/140/130/80/50/60
// Seeking 1000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 13/13/170/167/70/54/52
// Seeking 10000 from FlatMap/MultiFlatMap/std::map/butil::PooledMap/std::unordered_map/std::unordered_multimap/butil::hash_map takes 26/22/238/240/85/70/67
#ifndef BUTIL_FLAT_MAP_H
#define BUTIL_FLAT_MAP_H
#include <stdint.h>
#include <cstddef>
#include <functional>
#include <iostream> // std::ostream
#include <type_traits> // std::aligned_storage
#include "butil/type_traits.h"
#include "butil/logging.h"
#include "butil/find_cstr.h"
#include "butil/single_threaded_pool.h" // SingleThreadedPool
#include "butil/containers/hash_tables.h" // hash<>
#include "butil/bit_array.h" // bit_array_*
#include "butil/strings/string_piece.h" // StringPiece
#include "butil/memory/scope_guard.h"
#include "butil/containers/optional.h"
namespace butil {
template <typename _Map, typename _Element> class FlatMapIterator;
template <typename _Map, typename _Element> class SparseFlatMapIterator;
template <typename K, typename T> class FlatMapElement;
struct FlatMapVoid {}; // Replace void which is not constructible.
template <typename K> struct DefaultHasher;
template <typename K> struct DefaultEqualTo;
struct BucketInfo {
size_t longest_length;
double average_length;
};
#ifndef BRPC_FLATMAP_DEFAULT_NBUCKET
#ifdef FLAT_MAP_ROUND_BUCKET_BY_USE_NEXT_PRIME
#define BRPC_FLATMAP_DEFAULT_NBUCKET 29U
#else
#define BRPC_FLATMAP_DEFAULT_NBUCKET 16U
#endif
#endif // BRPC_FLATMAP_DEFAULT_NBUCKET
// NOTE: Objects stored in FlatMap MUST be copyable.
template <typename _K, typename _T,
// Compute hash code from key.
// Use src/butil/third_party/murmurhash3 to make better distributions.
typename _Hash = DefaultHasher<_K>,
// Test equivalence between stored-key and passed-key.
// stored-key is always on LHS, passed-key is always on RHS.
typename _Equal = DefaultEqualTo<_K>,
bool _Sparse = false,
typename _Alloc = PtAllocator,
// If `_Multi' is true, allow containing multiple copies of each key value.
bool _Multi = false>
class FlatMap {
public:
typedef _K key_type;
typedef _T mapped_type;
typedef _Alloc allocator_type;
typedef FlatMapElement<_K, _T> Element;
typedef typename Element::value_type value_type;
typedef typename conditional<
_Sparse, SparseFlatMapIterator<FlatMap, value_type>,
FlatMapIterator<FlatMap, value_type> >::type iterator;
typedef typename conditional<
_Sparse, SparseFlatMapIterator<FlatMap, const value_type>,
FlatMapIterator<FlatMap, const value_type> >::type const_iterator;
typedef _Hash hasher;
typedef _Equal key_equal;
static constexpr size_t DEFAULT_NBUCKET = BRPC_FLATMAP_DEFAULT_NBUCKET;
struct PositionHint {
size_t nbucket;
size_t offset;
bool at_entry;
key_type key;
};
explicit FlatMap(const hasher& hashfn = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
FlatMap(const FlatMap& rhs);
~FlatMap();
FlatMap& operator=(const FlatMap& rhs);
void swap(FlatMap & rhs);
// FlatMap will be automatically initialized with small FlatMap optimization,
// so this function only needs to be call when a large initial number of
// buckets or non-default `load_factor' is required.
// Returns 0 on success, -1 on error, but FlatMap can still be used normally.
// `nbucket' is the initial number of buckets. `load_factor' is the
// maximum value of size()*100/nbucket, if the value is reached, nbucket
// will be doubled and all items stored will be rehashed which is costly.
// Choosing proper values for these 2 parameters reduces costs.
int init(size_t nbucket, u_int load_factor = 80);
// Insert a pair of |key| and |value|. If size()*100/bucket_count() is
// more than load_factor(), a resize() will be done.
// Returns address of the inserted value, NULL on error.
mapped_type* insert(const key_type& key, const mapped_type& value);
// Insert a pair of {key, value}. If size()*100/bucket_count() is
// more than load_factor(), a resize() will be done.
// Returns address of the inserted value, NULL on error.
mapped_type* insert(const std::pair<key_type, mapped_type>& kv);
// For `_Multi=false'. (Default)
// Remove |key| and all associated value
// Returns: 1 on erased, 0 otherwise.
template <typename K2, bool Multi = _Multi>
typename std::enable_if<!Multi, size_t >::type
erase(const K2& key, mapped_type* old_value = NULL);
// For `_Multi=true'.
// Returns: num of value on erased, 0 otherwise.
template <typename K2, bool Multi = _Multi>
typename std::enable_if<Multi, size_t >::type
erase(const K2& key, std::vector<mapped_type>* old_values = NULL);
// Remove all items. Allocated spaces are NOT returned by system.
void clear();
// Remove all items and return all allocated spaces to system.
void clear_and_reset_pool();
// Search for the value associated with |key|.
// If `_Multi=false', Search for any of multiple values associated with |key|.
// Returns: address of the value.
template <typename K2> mapped_type* seek(const K2& key) const;
template <typename K2> std::vector<mapped_type*> seek_all(const K2& key) const;
// For `_Multi=false'. (Default)
// Get the value associated with |key|. If |key| does not exist,
// insert with a default-constructed value. If size()*100/bucket_count()
// is more than load_factor, a resize will be done.
// Returns reference of the value
template<bool Multi = _Multi>
typename std::enable_if<!Multi, mapped_type&>::type operator[](const key_type& key);
// Resize this map. This is optional because resizing will be triggered by
// insert() or operator[] if there're too many items.
// Returns successful or not.
bool resize(size_t nbucket);
// Iterators
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
// Iterate FlatMap inconsistently in more-than-one passes. This is used
// in multi-threaded environment to divide the critical sections of
// iterating big maps into smaller ones. "inconsistently" means that:
// * elements added during iteration may be missed.
// * some elements may be iterated more than once.
// * iteration is restarted at beginning when the map is resized.
// Example: (copying all keys in multi-threaded environment)
// LOCK;
// size_t n = 0;
// for (Map::const_iterator it = map.begin(); it != map.end(); ++it) {
// if (++n >= 256/*max iterated one pass*/) {
// Map::PositionHint hint;
// map.save_iterator(it, &hint);
// n = 0;
// UNLOCK;
// LOCK;
// it = map.restore_iterator(hint);
// if (it == map.begin()) { // resized
// keys->clear();
// }
// if (it == map.end()) {
// break;
// }
// }
// keys->push_back(it->first);
// }
// UNLOCK;
void save_iterator(const const_iterator&, PositionHint*) const;
const_iterator restore_iterator(const PositionHint&) const;
// Always returns true.
bool initialized() const { return _buckets != NULL; }
bool empty() const { return _size == 0; }
size_t size() const { return _size; }
size_t bucket_count() const { return _nbucket; }
u_int load_factor () const { return _load_factor; }
// Returns #nodes of longest bucket in this map. This scans all buckets.
BucketInfo bucket_info() const;
struct Bucket {
Bucket() : next((Bucket*)-1UL) {}
explicit Bucket(const _K& k) : next(NULL) {
element_space_.Init(k);
}
Bucket(const Bucket& other) : next(NULL) {
element_space_.Init(other.element());
}
bool is_valid() const { return next != (const Bucket*)-1UL; }
void set_invalid() { next = (Bucket*)-1UL; }
// NOTE: Only be called when is_valid() is true.
Element& element() { return *element_space_; }
const Element& element() const { return *element_space_; }
void destroy_element() { element_space_.Destroy(); }
void swap(Bucket& rhs) {
if (!is_valid() && !rhs.is_valid()) {
return;
} else if (is_valid() && !rhs.is_valid()) {
rhs.element_space_.Init(std::move(element()));
destroy_element();
} else if (!is_valid() && rhs.is_valid()) {
element_space_.Init(std::move(rhs.element()));
rhs.destroy_element();
} else {
element().swap(rhs.element());
}
std::swap(next, rhs.next);
}
Bucket* next;
private:
ManualConstructor<Element> element_space_;
};
private:
template <typename _Map, typename _Element> friend class FlatMapIterator;
template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
struct NewBucketsInfo {
NewBucketsInfo()
: buckets(NULL), thumbnail(NULL), nbucket(0) {}
NewBucketsInfo(Bucket* b, uint64_t* t, size_t n)
: buckets(b), thumbnail(t), nbucket(n) {}
Bucket* buckets;
uint64_t* thumbnail;
size_t nbucket;
};
optional<NewBucketsInfo> new_buckets_and_thumbnail(size_t size, size_t new_nbucket);
// For `_Multi=true'.
// Insert a new default-constructed associated with |key| always.
// If size()*100/bucket_count() is more than load_factor,
// a resize will be done.
// Returns reference of the value
template<bool Multi = _Multi>
typename std::enable_if<Multi, mapped_type&>::type operator[](const key_type& key);
allocator_type& get_allocator() { return _pool.get_allocator(); }
allocator_type get_allocator() const { return _pool.get_allocator(); }
// True if buckets need to be resized before holding `size' elements.
bool is_too_crowded(size_t size) const {
return is_too_crowded(size, _nbucket, _load_factor);
}
static bool is_too_crowded(size_t size, size_t nbucket, u_int load_factor) {
return size * 100 >= nbucket * load_factor;
}
void init_load_factor(u_int load_factor) {
if (_is_default_load_factor) {
_is_default_load_factor = false;
_load_factor = load_factor;
}
}
// True if using default buckets.
bool is_default_buckets() const {
return _buckets == (Bucket*)(&_default_buckets);
}
static void init_buckets_and_thumbnail(Bucket* buckets,
uint64_t* thumbnail,
size_t nbucket) {
for (size_t i = 0; i < nbucket; ++i) {
buckets[i].set_invalid();
}
buckets[nbucket].next = NULL;
if (_Sparse) {
bit_array_clear(thumbnail, nbucket);
}
}
static const size_t default_nthumbnail = BIT_ARRAY_LEN(DEFAULT_NBUCKET);
// Note: need an extra bucket to let iterator know where buckets end.
// Small map optimization.
Bucket _default_buckets[DEFAULT_NBUCKET + 1];
uint64_t _default_thumbnail[default_nthumbnail];
size_t _size;
size_t _nbucket;
Bucket* _buckets;
uint64_t* _thumbnail;
u_int _load_factor;
bool _is_default_load_factor;
hasher _hashfn;
key_equal _eql;
SingleThreadedPool<sizeof(Bucket), 1024, 16, allocator_type> _pool;
};
template <typename _K, typename _T,
typename _Hash = DefaultHasher<_K>,
typename _Equal = DefaultEqualTo<_K>,
bool _Sparse = false,
typename _Alloc = PtAllocator>
using MultiFlatMap = FlatMap<
_K, _T, _Hash, _Equal, _Sparse, _Alloc, true>;
template <typename _K,
typename _Hash = DefaultHasher<_K>,
typename _Equal = DefaultEqualTo<_K>,
bool _Sparse = false,
typename _Alloc = PtAllocator>
class FlatSet {
public:
typedef FlatMap<_K, FlatMapVoid, _Hash, _Equal, _Sparse,
_Alloc, false> Map;
typedef typename Map::key_type key_type;
typedef typename Map::value_type value_type;
typedef typename Map::Bucket Bucket;
typedef typename Map::iterator iterator;
typedef typename Map::const_iterator const_iterator;
typedef typename Map::hasher hasher;
typedef typename Map::key_equal key_equal;
typedef typename Map::allocator_type allocator_type;
explicit FlatSet(const hasher& hashfn = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: _map(hashfn, eql, alloc) {}
void swap(FlatSet & rhs) { _map.swap(rhs._map); }
int init(size_t nbucket, u_int load_factor = 80)
{ return _map.init(nbucket, load_factor); }
const void* insert(const key_type& key)
{ return _map.insert(key, FlatMapVoid()); }
template <typename K2>
size_t erase(const K2& key) { return _map.erase(key, NULL); }
void clear() { return _map.clear(); }
void clear_and_reset_pool() { return _map.clear_and_reset_pool(); }
template <typename K2>
const void* seek(const K2& key) const { return _map.seek(key); }
bool resize(size_t nbucket) { return _map.resize(nbucket); }
iterator begin() { return _map.begin(); }
iterator end() { return _map.end(); }
const_iterator begin() const { return _map.begin(); }
const_iterator end() const { return _map.end(); }
bool initialized() const { return _map.initialized(); }
bool empty() const { return _map.empty(); }
size_t size() const { return _map.size(); }
size_t bucket_count() const { return _map.bucket_count(); }
u_int load_factor () const { return _map.load_factor(); }
BucketInfo bucket_info() const { return _map.bucket_info(); }
private:
Map _map;
};
template <typename _K, typename _T,
typename _Hash = DefaultHasher<_K>,
typename _Equal = DefaultEqualTo<_K>,
typename _Alloc = PtAllocator,
bool _Multi = false>
class SparseFlatMap : public FlatMap<
_K, _T, _Hash, _Equal, true, _Alloc, _Multi> {};
template <typename _K,
typename _Hash = DefaultHasher<_K>,
typename _Equal = DefaultEqualTo<_K>,
typename _Alloc = PtAllocator>
class SparseFlatSet : public FlatSet<
_K, _Hash, _Equal, true, _Alloc> {};
// Implement FlatMapElement
template <typename K, typename T>
class FlatMapElement {
public:
typedef std::pair<const K, T> value_type;
// NOTE: Have to initialize _value in this way which is treated by GCC
// specially that _value is zeroized(POD) or constructed(non-POD). Other
// methods do not work. For example, if we put _value into the std::pair
// and do initialization by calling _pair(k, T()), _value will be copy
// constructed from the defaultly constructed instance(not zeroized for
// POD) which is wrong generally.
explicit FlatMapElement(const K& k) : _key(k), _value(T()) {}
// ^^^^^^^^^^^
FlatMapElement(const FlatMapElement& rhs)
: _key(rhs._key), _value(rhs._value) {}
FlatMapElement(FlatMapElement&& rhs) noexcept
: _key(std::move(rhs._key)), _value(std::move(rhs._value)) {}
const K& first_ref() const { return _key; }
T& second_ref() { return _value; }
T&& second_movable_ref() { return std::move(_value); }
value_type& value_ref() { return *reinterpret_cast<value_type*>(this); }
inline static const K& first_ref_from_value(const value_type& v)
{ return v.first; }
inline static const T& second_ref_from_value(const value_type& v)
{ return v.second; }
inline static T&& second_movable_ref_from_value(value_type& v)
{ return std::move(v.second); }
void swap(FlatMapElement& rhs) {
std::swap(_key, rhs._key);
std::swap(_value, rhs._value);
}
private:
K _key;
T _value;
};
template <typename K>
class FlatMapElement<K, FlatMapVoid> {
public:
typedef const K value_type;
// See the comment in the above FlatMapElement.
explicit FlatMapElement(const K& k) : _key(k) {}
FlatMapElement(const FlatMapElement& rhs) : _key(rhs._key) {}
FlatMapElement(FlatMapElement&& rhs) noexcept : _key(std::move(rhs._key)) {}
const K& first_ref() const { return _key; }
FlatMapVoid& second_ref() { return second_ref_from_value(_key); }
FlatMapVoid& second_movable_ref() { return second_ref(); }
value_type& value_ref() { return _key; }
inline static const K& first_ref_from_value(value_type& v) { return v; }
inline static FlatMapVoid& second_ref_from_value(value_type&) {
static FlatMapVoid dummy;
return dummy;
}
inline static const FlatMapVoid& second_movable_ref_from_value(value_type& v) {
return second_ref_from_value(v);
}
private:
K _key;
};
// Implement DefaultHasher and DefaultEqualTo
template <typename K>
struct DefaultHasher : public BUTIL_HASH_NAMESPACE::hash<K> {};
template <>
struct DefaultHasher<std::string> {
std::size_t operator()(const butil::StringPiece& s) const {
std::size_t result = 0;
for (butil::StringPiece::const_iterator
i = s.begin(); i != s.end(); ++i) {
result = result * 101 + *i;
}
return result;
}
std::size_t operator()(const char* s) const {
std::size_t result = 0;
for (; *s; ++s) {
result = result * 101 + *s;
}
return result;
}
std::size_t operator()(const std::string& s) const {
std::size_t result = 0;
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) {
result = result * 101 + *i;
}
return result;
}
};
template <typename K>
struct DefaultEqualTo : public std::equal_to<K> {
};
template <>
struct DefaultEqualTo<std::string> {
bool operator()(const std::string& s1, const std::string& s2) const
{ return s1 == s2; }
bool operator()(const std::string& s1, const butil::StringPiece& s2) const
{ return s1 == s2; }
bool operator()(const std::string& s1, const char* s2) const
{ return s1 == s2; }
};
// find_cstr and find_lowered_cstr
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
const _T* find_cstr(const FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key) {
return m.seek(key);
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
_T* find_cstr(FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key) {
return m.seek(key);
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
const _T* find_cstr(const FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key, size_t length) {
return m.seek(butil::StringPiece(key, length));
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
_T* find_cstr(FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key, size_t length) {
return m.seek(butil::StringPiece(key, length));
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
const _T* find_lowered_cstr(
const FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key) {
return m.seek(*tls_stringmap_temp.get_lowered_string(key));
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
_T* find_lowered_cstr(FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key) {
return m.seek(*tls_stringmap_temp.get_lowered_string(key));
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
const _T* find_lowered_cstr(
const FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key, size_t length) {
return m.seek(*tls_stringmap_temp.get_lowered_string(key, length));
}
template <typename _T, typename _Hash, typename _Equal, bool _Sparse>
_T* find_lowered_cstr(FlatMap<std::string, _T, _Hash, _Equal, _Sparse>& m,
const char* key, size_t length) {
return m.seek(*tls_stringmap_temp.get_lowered_string(key, length));
}
} // namespace butil
#include "butil/containers/flat_map_inl.h"
#endif //BUTIL_FLAT_MAP_H