google_hash 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. data/README +61 -27
  2. data/Rakefile +4 -1
  3. data/TODO +5 -0
  4. data/VERSION +1 -1
  5. data/changelog +3 -0
  6. data/ext/extconf.rb +10 -5
  7. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/AUTHORS +0 -0
  8. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/COPYING +0 -0
  9. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/ChangeLog +47 -0
  10. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/INSTALL +0 -0
  11. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/Makefile.am +29 -14
  12. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/Makefile.in +77 -42
  13. data/ext/sparsehash-1.8.1/NEWS +71 -0
  14. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/README +0 -0
  15. data/ext/{sparsehash-1.5.2/README.windows → sparsehash-1.8.1/README_windows.txt} +25 -25
  16. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/TODO +0 -0
  17. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/aclocal.m4 +0 -0
  18. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/compile +0 -0
  19. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/config.guess +0 -0
  20. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/config.sub +0 -0
  21. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/configure +3690 -4560
  22. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/configure.ac +1 -1
  23. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/depcomp +0 -0
  24. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/dense_hash_map.html +65 -5
  25. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/dense_hash_set.html +65 -5
  26. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/designstyle.css +0 -0
  27. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/implementation.html +11 -5
  28. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/index.html +0 -0
  29. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/performance.html +0 -0
  30. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/sparse_hash_map.html +65 -5
  31. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/sparse_hash_set.html +65 -5
  32. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/sparsetable.html +0 -0
  33. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/Makefile +0 -0
  34. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/README +0 -0
  35. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/example.c +0 -0
  36. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/libchash.c +0 -0
  37. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/libchash.h +0 -0
  38. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/google-sparsehash.sln +17 -1
  39. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/install-sh +0 -0
  40. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/acx_pthread.m4 +0 -0
  41. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/google_namespace.m4 +0 -0
  42. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/namespaces.m4 +0 -0
  43. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/stl_hash.m4 +0 -0
  44. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/stl_hash_fun.m4 +0 -0
  45. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/stl_namespace.m4 +0 -0
  46. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/missing +0 -0
  47. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/mkinstalldirs +0 -0
  48. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb.sh +0 -0
  49. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/README +0 -0
  50. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/changelog +24 -0
  51. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/compat +0 -0
  52. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/control +1 -1
  53. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/copyright +0 -0
  54. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/docs +0 -0
  55. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/rules +0 -0
  56. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/sparsehash.dirs +0 -0
  57. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/sparsehash.install +0 -0
  58. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/rpm.sh +0 -0
  59. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/rpm/rpm.spec +1 -1
  60. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/config.h.in +3 -0
  61. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/config.h.include +0 -0
  62. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/dense_hash_map +43 -27
  63. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/dense_hash_set +40 -19
  64. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparse_hash_map +32 -23
  65. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparse_hash_set +31 -21
  66. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparsehash/densehashtable.h +481 -298
  67. data/ext/sparsehash-1.8.1/src/google/sparsehash/hashtable-common.h +178 -0
  68. data/ext/sparsehash-1.8.1/src/google/sparsehash/libc_allocator_with_realloc.h +121 -0
  69. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparsehash/sparsehashtable.h +404 -233
  70. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparsetable +173 -83
  71. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/type_traits.h +3 -29
  72. data/ext/sparsehash-1.8.1/src/hash_test_interface.h +1011 -0
  73. data/ext/sparsehash-1.8.1/src/hashtable_test.cc +1733 -0
  74. data/ext/sparsehash-1.8.1/src/libc_allocator_with_realloc_test.cc +129 -0
  75. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/simple_test.cc +1 -1
  76. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/sparsetable_unittest.cc +202 -6
  77. data/ext/sparsehash-1.8.1/src/testutil.h +251 -0
  78. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/time_hash_map.cc +128 -54
  79. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/type_traits_unittest.cc +30 -20
  80. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/config.h +0 -0
  81. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/google/sparsehash/sparseconfig.h +0 -0
  82. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/port.cc +0 -0
  83. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/port.h +0 -0
  84. data/ext/sparsehash-1.8.1/vsprojects/hashtable_test/hashtable_test.vcproj +197 -0
  85. data/ext/{sparsehash-1.5.2/vsprojects/hashtable_unittest/hashtable_unittest.vcproj → sparsehash-1.8.1/vsprojects/simple_test/simple_test.vcproj} +9 -8
  86. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/vsprojects/sparsetable_unittest/sparsetable_unittest.vcproj +0 -2
  87. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/vsprojects/time_hash_map/time_hash_map.vcproj +3 -2
  88. data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/vsprojects/type_traits_unittest/type_traits_unittest.vcproj +0 -2
  89. data/ext/template/google_hash.cpp.erb +2 -1
  90. data/ext/template/main.cpp.erb +1 -1
  91. data/results.txt +6 -22
  92. data/spec/benchmark.rb +57 -0
  93. data/spec/spec.google_hash.rb +1 -8
  94. metadata +140 -130
  95. data/ext/benchmark.rb +0 -47
  96. data/ext/sparsehash-1.5.2/NEWS +0 -0
  97. data/ext/sparsehash-1.5.2/src/hashtable_unittest.cc +0 -1375
  98. data/ext/sparsehash-1.5.2/src/words +0 -8944
  99. data/types.txt +0 -18
@@ -0,0 +1,178 @@
1
+ // Copyright (c) 2005, 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: Giao Nguyen
32
+
33
+ #ifndef UTIL_GTL_HASHTABLE_COMMON_H_
34
+ #define UTIL_GTL_HASHTABLE_COMMON_H_
35
+
36
+ #include <assert.h>
37
+
38
+ // Settings contains parameters for growing and shrinking the table.
39
+ // It also packages zero-size functor (ie. hasher).
40
+
41
+ template<typename Key, typename HashFunc,
42
+ typename SizeType, int HT_MIN_BUCKETS>
43
+ class sh_hashtable_settings : public HashFunc {
44
+ public:
45
+ typedef Key key_type;
46
+ typedef HashFunc hasher;
47
+ typedef SizeType size_type;
48
+
49
+ public:
50
+ sh_hashtable_settings(const hasher& hf,
51
+ const float ht_occupancy_flt,
52
+ const float ht_empty_flt)
53
+ : hasher(hf),
54
+ enlarge_threshold_(0),
55
+ shrink_threshold_(0),
56
+ consider_shrink_(false),
57
+ use_empty_(false),
58
+ use_deleted_(false),
59
+ num_ht_copies_(0) {
60
+ set_enlarge_factor(ht_occupancy_flt);
61
+ set_shrink_factor(ht_empty_flt);
62
+ }
63
+
64
+ size_type hash(const key_type& v) const {
65
+ return hasher::operator()(v);
66
+ }
67
+
68
+ float enlarge_factor() const {
69
+ return enlarge_factor_;
70
+ }
71
+ void set_enlarge_factor(float f) {
72
+ enlarge_factor_ = f;
73
+ }
74
+ float shrink_factor() const {
75
+ return shrink_factor_;
76
+ }
77
+ void set_shrink_factor(float f) {
78
+ shrink_factor_ = f;
79
+ }
80
+
81
+ size_type enlarge_threshold() const {
82
+ return enlarge_threshold_;
83
+ }
84
+ void set_enlarge_threshold(size_type t) {
85
+ enlarge_threshold_ = t;
86
+ }
87
+ size_type shrink_threshold() const {
88
+ return shrink_threshold_;
89
+ }
90
+ void set_shrink_threshold(size_type t) {
91
+ shrink_threshold_ = t;
92
+ }
93
+
94
+ size_type enlarge_size(size_type x) const {
95
+ return static_cast<size_type>(x * enlarge_factor_);
96
+ }
97
+ size_type shrink_size(size_type x) const {
98
+ return static_cast<size_type>(x * shrink_factor_);
99
+ }
100
+
101
+ bool consider_shrink() const {
102
+ return consider_shrink_;
103
+ }
104
+ void set_consider_shrink(bool t) {
105
+ consider_shrink_ = t;
106
+ }
107
+
108
+ bool use_empty() const {
109
+ return use_empty_;
110
+ }
111
+ void set_use_empty(bool t) {
112
+ use_empty_ = t;
113
+ }
114
+
115
+ bool use_deleted() const {
116
+ return use_deleted_;
117
+ }
118
+ void set_use_deleted(bool t) {
119
+ use_deleted_ = t;
120
+ }
121
+
122
+ size_type num_ht_copies() const {
123
+ return static_cast<size_type>(num_ht_copies_);
124
+ }
125
+ void inc_num_ht_copies() {
126
+ ++num_ht_copies_;
127
+ }
128
+
129
+ // Reset the enlarge and shrink thresholds
130
+ void reset_thresholds(int num_buckets) {
131
+ set_enlarge_threshold(enlarge_size(num_buckets));
132
+ set_shrink_threshold(shrink_size(num_buckets));
133
+ // whatever caused us to reset already considered
134
+ set_consider_shrink(false);
135
+ }
136
+
137
+ // Caller is resposible for calling reset_threshold right after
138
+ // set_resizing_parameters.
139
+ void set_resizing_parameters(float shrink, float grow) {
140
+ assert(shrink >= 0.0);
141
+ assert(grow <= 1.0);
142
+ if (shrink > grow/2.0f)
143
+ shrink = grow / 2.0f; // otherwise we thrash hashtable size
144
+ set_shrink_factor(shrink);
145
+ set_enlarge_factor(grow);
146
+ }
147
+
148
+ // This is the smallest size a hashtable can be without being too crowded
149
+ // If you like, you can give a min #buckets as well as a min #elts
150
+ size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) {
151
+ float enlarge = enlarge_factor();
152
+ size_type sz = HT_MIN_BUCKETS; // min buckets allowed
153
+ while ( sz < min_buckets_wanted ||
154
+ num_elts >= static_cast<size_type>(sz * enlarge) ) {
155
+ // This just prevents overflowing size_type, since sz can exceed
156
+ // max_size() here.
157
+ if (static_cast<size_type>(sz * 2) < sz) {
158
+ throw std::length_error("resize overflow"); // protect against overflow
159
+ }
160
+ sz *= 2;
161
+ }
162
+ return sz;
163
+ }
164
+
165
+ private:
166
+ size_type enlarge_threshold_; // table.size() * enlarge_factor
167
+ size_type shrink_threshold_; // table.size() * shrink_factor
168
+ float enlarge_factor_; // how full before resize
169
+ float shrink_factor_; // how empty before resize
170
+ // consider_shrink=true if we should try to shrink before next insert
171
+ bool consider_shrink_;
172
+ bool use_empty_; // used only by densehashtable, not sparsehashtable
173
+ bool use_deleted_; // false until delkey has been set
174
+ // num_ht_copies is a counter incremented every Copy/Move
175
+ unsigned int num_ht_copies_;
176
+ };
177
+
178
+ #endif // UTIL_GTL_HASHTABLE_COMMON_H_
@@ -0,0 +1,121 @@
1
+ // Copyright (c) 2010, 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: Guilin Chen
32
+
33
+ #ifndef UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_
34
+ #define UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_
35
+
36
+ #include <google/sparsehash/sparseconfig.h>
37
+
38
+ #include <stdlib.h> // for malloc/realloc/free
39
+ #include <stddef.h> // for ptrdiff_t
40
+
41
+
42
+ _START_GOOGLE_NAMESPACE_
43
+
44
+ template<class T>
45
+ class libc_allocator_with_realloc {
46
+ public:
47
+ typedef T value_type;
48
+ typedef size_t size_type;
49
+ typedef ptrdiff_t difference_type;
50
+
51
+ typedef T* pointer;
52
+ typedef const T* const_pointer;
53
+ typedef T& reference;
54
+ typedef const T& const_reference;
55
+
56
+ libc_allocator_with_realloc() {}
57
+ libc_allocator_with_realloc(const libc_allocator_with_realloc&) {}
58
+ ~libc_allocator_with_realloc() {}
59
+
60
+ pointer address(reference r) const { return &r; }
61
+ const_pointer address(const_reference r) const { return &r; }
62
+
63
+ pointer allocate(size_type n, const_pointer = 0) {
64
+ return static_cast<pointer>(malloc(n * sizeof(value_type)));
65
+ }
66
+ void deallocate(pointer p, size_type) {
67
+ free(p);
68
+ }
69
+ pointer reallocate(pointer p, size_type n) {
70
+ return static_cast<pointer>(realloc(p, n * sizeof(value_type)));
71
+ }
72
+
73
+ size_type max_size() const {
74
+ return static_cast<size_type>(-1) / sizeof(value_type);
75
+ }
76
+
77
+ void construct(pointer p, const value_type& val) {
78
+ new(p) value_type(val);
79
+ }
80
+ void destroy(pointer p) { p->~value_type(); }
81
+
82
+ template <class U>
83
+ libc_allocator_with_realloc(const libc_allocator_with_realloc<U>&) {}
84
+
85
+ template<class U>
86
+ struct rebind {
87
+ typedef libc_allocator_with_realloc<U> other;
88
+ };
89
+ };
90
+
91
+ // libc_allocator_with_realloc<void> specialization.
92
+ template<>
93
+ class libc_allocator_with_realloc<void> {
94
+ public:
95
+ typedef void value_type;
96
+ typedef size_t size_type;
97
+ typedef ptrdiff_t difference_type;
98
+ typedef void* pointer;
99
+ typedef const void* const_pointer;
100
+
101
+ template<class U>
102
+ struct rebind {
103
+ typedef libc_allocator_with_realloc<U> other;
104
+ };
105
+ };
106
+
107
+ template<class T>
108
+ inline bool operator==(const libc_allocator_with_realloc<T>&,
109
+ const libc_allocator_with_realloc<T>&) {
110
+ return true;
111
+ }
112
+
113
+ template<class T>
114
+ inline bool operator!=(const libc_allocator_with_realloc<T>&,
115
+ const libc_allocator_with_realloc<T>&) {
116
+ return false;
117
+ }
118
+
119
+ _END_GOOGLE_NAMESPACE_
120
+
121
+ #endif // UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_
@@ -59,20 +59,20 @@
59
59
  // <google/sparse_hash_table> or <google/sparse_hash_set> instead.
60
60
  //
61
61
  // You can modify the following, below:
62
- // HT_OCCUPANCY_FLT -- how full before we double size
63
- // HT_EMPTY_FLT -- how empty before we halve size
62
+ // HT_OCCUPANCY_PCT -- how full before we double size
63
+ // HT_EMPTY_PCT -- how empty before we halve size
64
64
  // HT_MIN_BUCKETS -- smallest bucket size
65
65
  // HT_DEFAULT_STARTING_BUCKETS -- default bucket size at construct-time
66
66
  //
67
- // You can also change enlarge_resize_percent (which defaults to
68
- // HT_OCCUPANCY_FLT), and shrink_resize_percent (which defaults to
69
- // HT_EMPTY_FLT) with set_resizing_parameters().
67
+ // You can also change enlarge_factor (which defaults to
68
+ // HT_OCCUPANCY_PCT), and shrink_factor (which defaults to
69
+ // HT_EMPTY_PCT) with set_resizing_parameters().
70
70
  //
71
71
  // How to decide what values to use?
72
- // shrink_resize_percent's default of .4 * OCCUPANCY_FLT, is probably good.
72
+ // shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good.
73
73
  // HT_MIN_BUCKETS is probably unnecessary since you can specify
74
74
  // (indirectly) the starting number of buckets at construct-time.
75
- // For enlarge_resize_percent, you can use this chart to try to trade-off
75
+ // For enlarge_factor, you can use this chart to try to trade-off
76
76
  // expected lookup time to the space taken up. By default, this
77
77
  // code uses quadratic probing, though you can change it to linear
78
78
  // via _JUMP below if you really want to.
@@ -82,7 +82,7 @@
82
82
  // Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L)
83
83
  // Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2
84
84
  //
85
- // -- enlarge_resize_percent -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
85
+ // -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
86
86
  // QUADRATIC COLLISION RES.
87
87
  // probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11
88
88
  // probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6
@@ -103,21 +103,29 @@
103
103
  // The probing method
104
104
  // Linear probing
105
105
  // #define JUMP_(key, num_probes) ( 1 )
106
- // Quadratic-ish probing
106
+ // Quadratic probing
107
107
  #define JUMP_(key, num_probes) ( num_probes )
108
108
 
109
-
110
109
  #include <google/sparsehash/sparseconfig.h>
111
110
  #include <assert.h>
112
111
  #include <algorithm> // For swap(), eg
112
+ #include <stdexcept> // For length_error
113
113
  #include <iterator> // for facts about iterator tags
114
+ #include <limits> // for numeric_limits<>
114
115
  #include <utility> // for pair<>
116
+ #include <google/sparsehash/hashtable-common.h>
115
117
  #include <google/sparsetable> // Since that's basically what we are
116
118
 
117
119
  _START_GOOGLE_NAMESPACE_
118
120
 
119
121
  using STL_NAMESPACE::pair;
120
122
 
123
+ // The smaller this is, the faster lookup is (because the group bitmap is
124
+ // smaller) and the faster insert is, because there's less to move.
125
+ // On the other hand, there are more groups. Since group::size_type is
126
+ // a short, this number should be of the form 32*x + 16 to avoid waste.
127
+ static const u_int16_t DEFAULT_GROUP_SIZE = 48; // fits in 1.5 words
128
+
121
129
  // Hashtable class, used to implement the hashed associative containers
122
130
  // hash_set and hash_map.
123
131
  //
@@ -131,7 +139,7 @@ using STL_NAMESPACE::pair;
131
139
  // with key == deleted_key.
132
140
  // EqualKey: Given two Keys, says whether they are the same (that is,
133
141
  // if they are both associated with the same Value).
134
- // Alloc: STL allocator to use to allocate memory. Currently ignored.
142
+ // Alloc: STL allocator to use to allocate memory.
135
143
 
136
144
  template <class Value, class Key, class HashFcn,
137
145
  class ExtractKey, class SetKey, class EqualKey, class Alloc>
@@ -147,17 +155,21 @@ struct sparse_hashtable_const_iterator;
147
155
  // that skips over deleted elements.
148
156
  template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
149
157
  struct sparse_hashtable_iterator {
158
+ private:
159
+ typedef typename A::template rebind<V>::other value_alloc_type;
160
+
150
161
  public:
151
162
  typedef sparse_hashtable_iterator<V,K,HF,ExK,SetK,EqK,A> iterator;
152
163
  typedef sparse_hashtable_const_iterator<V,K,HF,ExK,SetK,EqK,A> const_iterator;
153
- typedef typename sparsetable<V>::nonempty_iterator st_iterator;
164
+ typedef typename sparsetable<V,DEFAULT_GROUP_SIZE,A>::nonempty_iterator
165
+ st_iterator;
154
166
 
155
167
  typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
156
168
  typedef V value_type;
157
- typedef ptrdiff_t difference_type;
158
- typedef size_t size_type;
159
- typedef V& reference; // Value
160
- typedef V* pointer;
169
+ typedef typename value_alloc_type::difference_type difference_type;
170
+ typedef typename value_alloc_type::size_type size_type;
171
+ typedef typename value_alloc_type::reference reference;
172
+ typedef typename value_alloc_type::pointer pointer;
161
173
 
162
174
  // "Real" constructor and default constructor
163
175
  sparse_hashtable_iterator(const sparse_hashtable<V,K,HF,ExK,SetK,EqK,A> *h,
@@ -195,17 +207,21 @@ struct sparse_hashtable_iterator {
195
207
  // Now do it all again, but with const-ness!
196
208
  template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
197
209
  struct sparse_hashtable_const_iterator {
210
+ private:
211
+ typedef typename A::template rebind<V>::other value_alloc_type;
212
+
198
213
  public:
199
214
  typedef sparse_hashtable_iterator<V,K,HF,ExK,SetK,EqK,A> iterator;
200
215
  typedef sparse_hashtable_const_iterator<V,K,HF,ExK,SetK,EqK,A> const_iterator;
201
- typedef typename sparsetable<V>::const_nonempty_iterator st_iterator;
216
+ typedef typename sparsetable<V,DEFAULT_GROUP_SIZE,A>::const_nonempty_iterator
217
+ st_iterator;
202
218
 
203
219
  typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
204
220
  typedef V value_type;
205
- typedef ptrdiff_t difference_type;
206
- typedef size_t size_type;
207
- typedef const V& reference; // Value
208
- typedef const V* pointer;
221
+ typedef typename value_alloc_type::difference_type difference_type;
222
+ typedef typename value_alloc_type::size_type size_type;
223
+ typedef typename value_alloc_type::const_reference reference;
224
+ typedef typename value_alloc_type::const_pointer pointer;
209
225
 
210
226
  // "Real" constructor and default constructor
211
227
  sparse_hashtable_const_iterator(const sparse_hashtable<V,K,HF,ExK,SetK,EqK,A> *h,
@@ -246,16 +262,20 @@ struct sparse_hashtable_const_iterator {
246
262
  // And once again, but this time freeing up memory as we iterate
247
263
  template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
248
264
  struct sparse_hashtable_destructive_iterator {
265
+ private:
266
+ typedef typename A::template rebind<V>::other value_alloc_type;
267
+
249
268
  public:
250
269
  typedef sparse_hashtable_destructive_iterator<V,K,HF,ExK,SetK,EqK,A> iterator;
251
- typedef typename sparsetable<V>::destructive_iterator st_iterator;
270
+ typedef typename sparsetable<V,DEFAULT_GROUP_SIZE,A>::destructive_iterator
271
+ st_iterator;
252
272
 
253
273
  typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
254
274
  typedef V value_type;
255
- typedef ptrdiff_t difference_type;
256
- typedef size_t size_type;
257
- typedef V& reference; // Value
258
- typedef V* pointer;
275
+ typedef typename value_alloc_type::difference_type difference_type;
276
+ typedef typename value_alloc_type::size_type size_type;
277
+ typedef typename value_alloc_type::reference reference;
278
+ typedef typename value_alloc_type::pointer pointer;
259
279
 
260
280
  // "Real" constructor and default constructor
261
281
  sparse_hashtable_destructive_iterator(const
@@ -295,18 +315,22 @@ struct sparse_hashtable_destructive_iterator {
295
315
  template <class Value, class Key, class HashFcn,
296
316
  class ExtractKey, class SetKey, class EqualKey, class Alloc>
297
317
  class sparse_hashtable {
318
+ private:
319
+ typedef typename Alloc::template rebind<Value>::other value_alloc_type;
320
+
298
321
  public:
299
322
  typedef Key key_type;
300
323
  typedef Value value_type;
301
324
  typedef HashFcn hasher;
302
325
  typedef EqualKey key_equal;
303
-
304
- typedef size_t size_type;
305
- typedef ptrdiff_t difference_type;
306
- typedef value_type* pointer;
307
- typedef const value_type* const_pointer;
308
- typedef value_type& reference;
309
- typedef const value_type& const_reference;
326
+ typedef Alloc allocator_type;
327
+
328
+ typedef typename value_alloc_type::size_type size_type;
329
+ typedef typename value_alloc_type::difference_type difference_type;
330
+ typedef typename value_alloc_type::reference reference;
331
+ typedef typename value_alloc_type::const_reference const_reference;
332
+ typedef typename value_alloc_type::pointer pointer;
333
+ typedef typename value_alloc_type::const_pointer const_pointer;
310
334
  typedef sparse_hashtable_iterator<Value, Key, HashFcn, ExtractKey,
311
335
  SetKey, EqualKey, Alloc>
312
336
  iterator;
@@ -326,22 +350,23 @@ class sparse_hashtable {
326
350
  // How full we let the table get before we resize, by default.
327
351
  // Knuth says .8 is good -- higher causes us to probe too much,
328
352
  // though it saves memory.
329
- static const float HT_OCCUPANCY_FLT; // = 0.8f;
353
+ static const int HT_OCCUPANCY_PCT; // = 80 (out of 100);
330
354
 
331
355
  // How empty we let the table get before we resize lower, by default.
332
- // It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
333
- static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT;
356
+ // (0.0 means never resize lower.)
357
+ // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing
358
+ static const int HT_EMPTY_PCT; // = 0.4 * HT_OCCUPANCY_PCT;
334
359
 
335
360
  // Minimum size we're willing to let hashtables be.
336
361
  // Must be a power of two, and at least 4.
337
- // Note, however, that for a given hashtable, the minimum size is
338
- // determined by the first constructor arg, and may be >HT_MIN_BUCKETS.
339
- static const size_t HT_MIN_BUCKETS = 4;
362
+ // Note, however, that for a given hashtable, the initial size is a
363
+ // function of the first constructor arg, and may be >HT_MIN_BUCKETS.
364
+ static const size_type HT_MIN_BUCKETS = 4;
340
365
 
341
366
  // By default, if you don't specify a hashtable size at
342
367
  // construction-time, we use this size. Must be a power of two, and
343
368
  // at least HT_MIN_BUCKETS.
344
- static const size_t HT_DEFAULT_STARTING_BUCKETS = 32;
369
+ static const size_type HT_DEFAULT_STARTING_BUCKETS = 32;
345
370
 
346
371
  // ITERATOR FUNCTIONS
347
372
  iterator begin() { return iterator(this, table.nonempty_begin(),
@@ -399,8 +424,12 @@ class sparse_hashtable {
399
424
 
400
425
 
401
426
  // ACCESSOR FUNCTIONS for the things we templatize on, basically
402
- hasher hash_funct() const { return hash; }
403
- key_equal key_eq() const { return equals; }
427
+ hasher hash_funct() const { return settings; }
428
+ key_equal key_eq() const { return key_info; }
429
+ allocator_type get_allocator() const { return table.get_allocator(); }
430
+
431
+ // Accessor function for statistics gathering.
432
+ int num_table_copies() const { return settings.num_ht_copies(); }
404
433
 
405
434
  private:
406
435
  // We need to copy values when we set the special marker for deleted
@@ -408,7 +437,7 @@ class sparse_hashtable {
408
437
  // operator because value_type might not be assignable (it's often
409
438
  // pair<const X, Y>). We use explicit destructor invocation and
410
439
  // placement new to get around this. Arg.
411
- void set_value(value_type* dst, const value_type src) {
440
+ void set_value(pointer dst, const_reference src) {
412
441
  dst->~value_type(); // delete the old value, if any
413
442
  new(dst) value_type(src);
414
443
  }
@@ -419,7 +448,6 @@ class sparse_hashtable {
419
448
  // can't do a destructive copy, we make the typename private.
420
449
  enum MoveDontCopyT {MoveDontCopy, MoveDontGrow};
421
450
 
422
-
423
451
  // DELETE HELPER FUNCTIONS
424
452
  // This lets the user describe a key that will indicate deleted
425
453
  // table entries. This key should be an "impossible" entry --
@@ -435,59 +463,88 @@ class sparse_hashtable {
435
463
  assert(num_deleted == 0);
436
464
  }
437
465
 
466
+ bool test_deleted_key(const key_type& key) const {
467
+ // The num_deleted test is crucial for read(): after read(), the ht values
468
+ // are garbage, and we don't want to think some of them are deleted.
469
+ // Invariant: !use_deleted implies num_deleted is 0.
470
+ assert(settings.use_deleted() || num_deleted == 0);
471
+ return num_deleted > 0 && equals(key_info.delkey, key);
472
+ }
473
+
438
474
  public:
439
475
  void set_deleted_key(const key_type &key) {
440
476
  // It's only safe to change what "deleted" means if we purge deleted guys
441
477
  squash_deleted();
442
- use_deleted = true;
443
- delkey = key;
478
+ settings.set_use_deleted(true);
479
+ key_info.delkey = key;
444
480
  }
445
481
  void clear_deleted_key() {
446
482
  squash_deleted();
447
- use_deleted = false;
483
+ settings.set_use_deleted(false);
484
+ }
485
+ key_type deleted_key() const {
486
+ assert(settings.use_deleted()
487
+ && "Must set deleted key before calling deleted_key");
488
+ return key_info.delkey;
448
489
  }
449
490
 
450
491
  // These are public so the iterators can use them
451
492
  // True if the item at position bucknum is "deleted" marker
452
493
  bool test_deleted(size_type bucknum) const {
453
- // The num_deleted test is crucial for read(): after read(), the ht values
454
- // are garbage, and we don't want to think some of them are deleted.
455
- return (use_deleted && num_deleted > 0 && table.test(bucknum) &&
456
- equals(delkey, get_key(table.unsafe_get(bucknum))));
494
+ if (num_deleted == 0 || !table.test(bucknum)) return false;
495
+ return test_deleted_key(get_key(table.unsafe_get(bucknum)));
457
496
  }
458
497
  bool test_deleted(const iterator &it) const {
459
- return (use_deleted && num_deleted > 0 &&
460
- equals(delkey, get_key(*it)));
498
+ if (!settings.use_deleted()) return false;
499
+ return test_deleted_key(get_key(*it));
461
500
  }
462
501
  bool test_deleted(const const_iterator &it) const {
463
- return (use_deleted && num_deleted > 0 &&
464
- equals(delkey, get_key(*it)));
502
+ if (!settings.use_deleted()) return false;
503
+ return test_deleted_key(get_key(*it));
465
504
  }
466
505
  bool test_deleted(const destructive_iterator &it) const {
467
- return (use_deleted && num_deleted > 0 &&
468
- equals(delkey, get_key(*it)));
506
+ if (!settings.use_deleted()) return false;
507
+ return test_deleted_key(get_key(*it));
508
+ }
509
+
510
+ private:
511
+ // Set it so test_deleted is true. true if object didn't used to be deleted.
512
+ // TODO(csilvers): make these private (also in densehashtable.h)
513
+ bool set_deleted(iterator &it) {
514
+ assert(settings.use_deleted());
515
+ bool retval = !test_deleted(it);
516
+ // &* converts from iterator to value-type.
517
+ set_key(&(*it), key_info.delkey);
518
+ return retval;
469
519
  }
470
- // Set it so test_deleted is true. true if object didn't used to be deleted
471
- // See below (at erase()) to explain why we allow const_iterators
520
+ // Set it so test_deleted is false. true if object used to be deleted.
521
+ bool clear_deleted(iterator &it) {
522
+ assert(settings.use_deleted());
523
+ // Happens automatically when we assign something else in its place.
524
+ return test_deleted(it);
525
+ }
526
+
527
+ // We also allow to set/clear the deleted bit on a const iterator.
528
+ // We allow a const_iterator for the same reason you can delete a
529
+ // const pointer: it's convenient, and semantically you can't use
530
+ // 'it' after it's been deleted anyway, so its const-ness doesn't
531
+ // really matter.
472
532
  bool set_deleted(const_iterator &it) {
473
- assert(use_deleted); // bad if set_deleted_key() wasn't called
533
+ assert(settings.use_deleted()); // bad if set_deleted_key() wasn't called
474
534
  bool retval = !test_deleted(it);
475
- // &* converts from iterator to value-type
476
- set_key(const_cast<value_type*>(&(*it)), delkey);
535
+ set_key(const_cast<pointer>(&(*it)), key_info.delkey);
477
536
  return retval;
478
537
  }
479
- // Set it so test_deleted is false. true if object used to be deleted
538
+ // Set it so test_deleted is false. true if object used to be deleted.
480
539
  bool clear_deleted(const_iterator &it) {
481
- assert(use_deleted); // bad if set_deleted_key() wasn't called
482
- // happens automatically when we assign something else in its place
540
+ assert(settings.use_deleted()); // bad if set_deleted_key() wasn't called
483
541
  return test_deleted(it);
484
542
  }
485
543
 
486
-
487
544
  // FUNCTIONS CONCERNING SIZE
545
+ public:
488
546
  size_type size() const { return table.num_nonempty() - num_deleted; }
489
- // Buckets are always a power of 2
490
- size_type max_size() const { return (size_type(-1) >> 1U) + 1; }
547
+ size_type max_size() const { return table.max_size(); }
491
548
  bool empty() const { return size() == 0; }
492
549
  size_type bucket_count() const { return table.size(); }
493
550
  size_type max_bucket_count() const { return max_size(); }
@@ -497,54 +554,57 @@ class sparse_hashtable {
497
554
  return begin(i) == end(i) ? 0 : 1;
498
555
  }
499
556
 
500
-
501
557
  private:
502
558
  // Because of the above, size_type(-1) is never legal; use it for errors
503
559
  static const size_type ILLEGAL_BUCKET = size_type(-1);
504
560
 
505
- private:
506
- // This is the smallest size a hashtable can be without being too crowded
507
- // If you like, you can give a min #buckets as well as a min #elts
508
- size_type min_size(size_type num_elts, size_type min_buckets_wanted) {
509
- size_type sz = HT_MIN_BUCKETS;
510
- while ( sz < min_buckets_wanted || num_elts >= sz * enlarge_resize_percent )
511
- sz *= 2;
512
- return sz;
513
- }
514
-
515
- // Used after a string of deletes
516
- void maybe_shrink() {
561
+ // Used after a string of deletes. Returns true if we actually shrunk.
562
+ // TODO(csilvers): take a delta so we can take into account inserts
563
+ // done after shrinking. Maybe make part of the Settings class?
564
+ bool maybe_shrink() {
517
565
  assert(table.num_nonempty() >= num_deleted);
518
566
  assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two
519
567
  assert(bucket_count() >= HT_MIN_BUCKETS);
568
+ bool retval = false;
520
569
 
521
570
  // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS,
522
571
  // we'll never shrink until you get relatively big, and we'll never
523
572
  // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something
524
573
  // like "dense_hash_set<int> x; x.insert(4); x.erase(4);" will
525
574
  // shrink us down to HT_MIN_BUCKETS buckets, which is too small.
526
- if (shrink_threshold > 0
527
- && (table.num_nonempty()-num_deleted) < shrink_threshold &&
528
- bucket_count() > HT_DEFAULT_STARTING_BUCKETS ) {
575
+ const size_type num_remain = table.num_nonempty() - num_deleted;
576
+ const size_type shrink_threshold = settings.shrink_threshold();
577
+ if (shrink_threshold > 0 && num_remain < shrink_threshold &&
578
+ bucket_count() > HT_DEFAULT_STARTING_BUCKETS) {
579
+ const float shrink_factor = settings.shrink_factor();
529
580
  size_type sz = bucket_count() / 2; // find how much we should shrink
530
- while ( sz > HT_DEFAULT_STARTING_BUCKETS &&
531
- (table.num_nonempty() - num_deleted) <= sz *
532
- shrink_resize_percent )
581
+ while (sz > HT_DEFAULT_STARTING_BUCKETS &&
582
+ num_remain < static_cast<size_type>(sz * shrink_factor)) {
533
583
  sz /= 2; // stay a power of 2
584
+ }
534
585
  sparse_hashtable tmp(MoveDontCopy, *this, sz);
535
586
  swap(tmp); // now we are tmp
587
+ retval = true;
536
588
  }
537
- consider_shrink = false; // because we just considered it
589
+ settings.set_consider_shrink(false); // because we just considered it
590
+ return retval;
538
591
  }
539
592
 
540
593
  // We'll let you resize a hashtable -- though this makes us copy all!
541
594
  // When you resize, you say, "make it big enough for this many more elements"
542
- void resize_delta(size_type delta) {
543
- if ( consider_shrink ) // see if lots of deletes happened
544
- maybe_shrink();
595
+ // Returns true if we actually resized, false if size was already ok.
596
+ bool resize_delta(size_type delta) {
597
+ bool did_resize = false;
598
+ if ( settings.consider_shrink() ) { // see if lots of deletes happened
599
+ if ( maybe_shrink() )
600
+ did_resize = true;
601
+ }
602
+ if (table.num_nonempty() >=
603
+ (STL_NAMESPACE::numeric_limits<size_type>::max)() - delta)
604
+ throw std::length_error("resize overflow");
545
605
  if ( bucket_count() >= HT_MIN_BUCKETS &&
546
- (table.num_nonempty() + delta) <= enlarge_threshold )
547
- return; // we're ok as we are
606
+ (table.num_nonempty() + delta) <= settings.enlarge_threshold() )
607
+ return did_resize; // we're ok as we are
548
608
 
549
609
  // Sometimes, we need to resize just to get rid of all the
550
610
  // "deleted" buckets that are clogging up the hashtable. So when
@@ -552,13 +612,34 @@ class sparse_hashtable {
552
612
  // are currently taking up room). But later, when we decide what
553
613
  // size to resize to, *don't* count deleted buckets, since they
554
614
  // get discarded during the resize.
555
- const size_type needed_size = min_size(table.num_nonempty() + delta, 0);
556
- if ( needed_size > bucket_count() ) { // we don't have enough buckets
557
- const size_type resize_to = min_size(table.num_nonempty() - num_deleted
558
- + delta, 0);
559
- sparse_hashtable tmp(MoveDontCopy, *this, resize_to);
560
- swap(tmp); // now we are tmp
615
+ const size_type needed_size =
616
+ settings.min_buckets(table.num_nonempty() + delta, 0);
617
+ if ( needed_size <= bucket_count() ) // we have enough buckets
618
+ return did_resize;
619
+
620
+ size_type resize_to =
621
+ settings.min_buckets(table.num_nonempty() - num_deleted + delta,
622
+ bucket_count());
623
+ if (resize_to < needed_size && // may double resize_to
624
+ resize_to < (STL_NAMESPACE::numeric_limits<size_type>::max)() / 2) {
625
+ // This situation means that we have enough deleted elements,
626
+ // that once we purge them, we won't actually have needed to
627
+ // grow. But we may want to grow anyway: if we just purge one
628
+ // element, say, we'll have to grow anyway next time we
629
+ // insert. Might as well grow now, since we're already going
630
+ // through the trouble of copying (in order to purge the
631
+ // deleted elements).
632
+ const size_type target =
633
+ static_cast<size_type>(settings.shrink_size(resize_to*2));
634
+ if (table.num_nonempty() - num_deleted + delta >= target) {
635
+ // Good, we won't be below the shrink threshhold even if we double.
636
+ resize_to *= 2;
637
+ }
561
638
  }
639
+
640
+ sparse_hashtable tmp(MoveDontCopy, *this, resize_to);
641
+ swap(tmp); // now we are tmp
642
+ return true;
562
643
  }
563
644
 
564
645
  // Used to actually do the rehashing when we grow/shrink a hashtable
@@ -566,16 +647,17 @@ class sparse_hashtable {
566
647
  clear(); // clear table, set num_deleted to 0
567
648
 
568
649
  // If we need to change the size of our table, do it now
569
- const size_type resize_to = min_size(ht.size(), min_buckets_wanted);
650
+ const size_type resize_to =
651
+ settings.min_buckets(ht.size(), min_buckets_wanted);
570
652
  if ( resize_to > bucket_count() ) { // we don't have enough buckets
571
653
  table.resize(resize_to); // sets the number of buckets
572
- reset_thresholds();
654
+ settings.reset_thresholds(bucket_count());
573
655
  }
574
656
 
575
657
  // We use a normal iterator to get non-deleted bcks from ht
576
658
  // We could use insert() here, but since we know there are
577
659
  // no duplicates and no deleted items, we can be more efficient
578
- assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two
660
+ assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two
579
661
  for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) {
580
662
  size_type num_probes = 0; // how many times we've probed
581
663
  size_type bucknum;
@@ -584,10 +666,12 @@ class sparse_hashtable {
584
666
  table.test(bucknum); // not empty
585
667
  bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) {
586
668
  ++num_probes;
587
- assert(num_probes < bucket_count()); // or else the hashtable is full
669
+ assert(num_probes < bucket_count()
670
+ && "Hashtable is full: an error in key_equal<> or hash<>");
588
671
  }
589
672
  table.set(bucknum, *it); // copies the value to here
590
673
  }
674
+ settings.inc_num_ht_copies();
591
675
  }
592
676
 
593
677
  // Implementation is like copy_from, but it destroys the table of the
@@ -598,14 +682,14 @@ class sparse_hashtable {
598
682
  clear(); // clear table, set num_deleted to 0
599
683
 
600
684
  // If we need to change the size of our table, do it now
601
- size_t resize_to;
685
+ size_type resize_to;
602
686
  if ( mover == MoveDontGrow )
603
687
  resize_to = ht.bucket_count(); // keep same size as old ht
604
688
  else // MoveDontCopy
605
- resize_to = min_size(ht.size(), min_buckets_wanted);
689
+ resize_to = settings.min_buckets(ht.size(), min_buckets_wanted);
606
690
  if ( resize_to > bucket_count() ) { // we don't have enough buckets
607
691
  table.resize(resize_to); // sets the number of buckets
608
- reset_thresholds();
692
+ settings.reset_thresholds(bucket_count());
609
693
  }
610
694
 
611
695
  // We use a normal iterator to get non-deleted bcks from ht
@@ -621,10 +705,12 @@ class sparse_hashtable {
621
705
  table.test(bucknum); // not empty
622
706
  bucknum = (bucknum + JUMP_(key, num_probes)) & (bucket_count()-1) ) {
623
707
  ++num_probes;
624
- assert(num_probes < bucket_count()); // or else the hashtable is full
708
+ assert(num_probes < bucket_count()
709
+ && "Hashtable is full: an error in key_equal<> or hash<>");
625
710
  }
626
711
  table.set(bucknum, *it); // copies the value to here
627
712
  }
713
+ settings.inc_num_ht_copies();
628
714
  }
629
715
 
630
716
 
@@ -634,28 +720,23 @@ class sparse_hashtable {
634
720
  // more useful as num_elements. As a special feature, calling with
635
721
  // req_elements==0 will cause us to shrink if we can, saving space.
636
722
  void resize(size_type req_elements) { // resize to this or larger
637
- if ( consider_shrink || req_elements == 0 )
723
+ if ( settings.consider_shrink() || req_elements == 0 )
638
724
  maybe_shrink();
639
725
  if ( req_elements > table.num_nonempty() ) // we only grow
640
726
  resize_delta(req_elements - table.num_nonempty());
641
727
  }
642
728
 
643
- // Get and change the value of shrink_resize_percent and
644
- // enlarge_resize_percent. The description at the beginning of this
645
- // file explains how to choose the values. Setting the shrink
646
- // parameter to 0.0 ensures that the table never shrinks.
729
+ // Get and change the value of shrink_factor and enlarge_factor. The
730
+ // description at the beginning of this file explains how to choose
731
+ // the values. Setting the shrink parameter to 0.0 ensures that the
732
+ // table never shrinks.
647
733
  void get_resizing_parameters(float* shrink, float* grow) const {
648
- *shrink = shrink_resize_percent;
649
- *grow = enlarge_resize_percent;
734
+ *shrink = settings.shrink_factor();
735
+ *grow = settings.enlarge_factor();
650
736
  }
651
737
  void set_resizing_parameters(float shrink, float grow) {
652
- assert(shrink >= 0.0);
653
- assert(grow <= 1.0);
654
- if (shrink > grow/2.0f)
655
- shrink = grow / 2.0f; // otherwise we thrash hashtable size
656
- shrink_resize_percent = shrink;
657
- enlarge_resize_percent = grow;
658
- reset_thresholds();
738
+ settings.set_resizing_parameters(shrink, grow);
739
+ settings.reset_thresholds(bucket_count());
659
740
  }
660
741
 
661
742
  // CONSTRUCTORS -- as required by the specs, we take a size,
@@ -665,15 +746,17 @@ class sparse_hashtable {
665
746
  explicit sparse_hashtable(size_type expected_max_items_in_table = 0,
666
747
  const HashFcn& hf = HashFcn(),
667
748
  const EqualKey& eql = EqualKey(),
749
+ const ExtractKey& ext = ExtractKey(),
668
750
  const SetKey& set = SetKey(),
669
- const ExtractKey& ext = ExtractKey())
670
- : hash(hf), equals(eql), get_key(ext), set_key(set), num_deleted(0),
671
- use_deleted(false), delkey(), enlarge_resize_percent(HT_OCCUPANCY_FLT),
672
- shrink_resize_percent(HT_EMPTY_FLT),
673
- table(expected_max_items_in_table == 0
674
- ? HT_DEFAULT_STARTING_BUCKETS
675
- : min_size(expected_max_items_in_table, 0)) {
676
- reset_thresholds();
751
+ const Alloc& alloc = Alloc())
752
+ : settings(hf),
753
+ key_info(ext, set, eql),
754
+ num_deleted(0),
755
+ table((expected_max_items_in_table == 0
756
+ ? HT_DEFAULT_STARTING_BUCKETS
757
+ : settings.min_buckets(expected_max_items_in_table, 0)),
758
+ alloc) {
759
+ settings.reset_thresholds(bucket_count());
677
760
  }
678
761
 
679
762
  // As a convenience for resize(), we allow an optional second argument
@@ -682,63 +765,51 @@ class sparse_hashtable {
682
765
  // into us instead of copying.
683
766
  sparse_hashtable(const sparse_hashtable& ht,
684
767
  size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS)
685
- : hash(ht.hash), equals(ht.equals),
686
- get_key(ht.get_key), set_key(ht.set_key), num_deleted(0),
687
- use_deleted(ht.use_deleted), delkey(ht.delkey),
688
- enlarge_resize_percent(ht.enlarge_resize_percent),
689
- shrink_resize_percent(ht.shrink_resize_percent),
690
- table() {
691
- reset_thresholds();
768
+ : settings(ht.settings),
769
+ key_info(ht.key_info),
770
+ num_deleted(0),
771
+ table(0, ht.get_allocator()) {
772
+ settings.reset_thresholds(bucket_count());
692
773
  copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries
693
774
  }
694
775
  sparse_hashtable(MoveDontCopyT mover, sparse_hashtable& ht,
695
776
  size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS)
696
- : hash(ht.hash), equals(ht.equals), get_key(ht.get_key),
697
- num_deleted(0), use_deleted(ht.use_deleted), delkey(ht.delkey),
698
- enlarge_resize_percent(ht.enlarge_resize_percent),
699
- shrink_resize_percent(ht.shrink_resize_percent),
700
- table() {
701
- reset_thresholds();
777
+ : settings(ht.settings),
778
+ key_info(ht.key_info),
779
+ num_deleted(0),
780
+ table(0, ht.get_allocator()) {
781
+ settings.reset_thresholds(bucket_count());
702
782
  move_from(mover, ht, min_buckets_wanted); // ignores deleted entries
703
783
  }
704
784
 
705
785
  sparse_hashtable& operator= (const sparse_hashtable& ht) {
706
786
  if (&ht == this) return *this; // don't copy onto ourselves
707
- clear();
708
- hash = ht.hash;
709
- equals = ht.equals;
710
- get_key = ht.get_key;
711
- set_key = ht.set_key;
712
- use_deleted = ht.use_deleted;
713
- delkey = ht.delkey;
714
- copy_from(ht, HT_MIN_BUCKETS); // sets num_deleted to 0 too
787
+ settings = ht.settings;
788
+ key_info = ht.key_info;
789
+ num_deleted = ht.num_deleted;
790
+ // copy_from() calls clear and sets num_deleted to 0 too
791
+ copy_from(ht, HT_MIN_BUCKETS);
792
+ // we purposefully don't copy the allocator, which may not be copyable
715
793
  return *this;
716
794
  }
717
795
 
718
796
  // Many STL algorithms use swap instead of copy constructors
719
797
  void swap(sparse_hashtable& ht) {
720
- STL_NAMESPACE::swap(hash, ht.hash);
721
- STL_NAMESPACE::swap(equals, ht.equals);
722
- STL_NAMESPACE::swap(get_key, ht.get_key);
723
- STL_NAMESPACE::swap(set_key, ht.set_key);
798
+ STL_NAMESPACE::swap(settings, ht.settings);
799
+ STL_NAMESPACE::swap(key_info, ht.key_info);
724
800
  STL_NAMESPACE::swap(num_deleted, ht.num_deleted);
725
- STL_NAMESPACE::swap(use_deleted, ht.use_deleted);
726
- STL_NAMESPACE::swap(enlarge_resize_percent, ht.enlarge_resize_percent);
727
- STL_NAMESPACE::swap(shrink_resize_percent, ht.shrink_resize_percent);
728
- STL_NAMESPACE::swap(delkey, ht.delkey);
729
801
  table.swap(ht.table);
730
- reset_thresholds();
731
- ht.reset_thresholds();
732
802
  }
733
803
 
734
804
  // It's always nice to be able to clear a table without deallocating it
735
805
  void clear() {
736
- table.clear();
737
- reset_thresholds();
806
+ if (!empty() || (num_deleted != 0)) {
807
+ table.clear();
808
+ }
809
+ settings.reset_thresholds(bucket_count());
738
810
  num_deleted = 0;
739
811
  }
740
812
 
741
-
742
813
  // LOOKUP ROUTINES
743
814
  private:
744
815
  // Returns a pair of positions: 1st where the object is, 2nd where
@@ -770,7 +841,8 @@ class sparse_hashtable {
770
841
  }
771
842
  ++num_probes; // we're doing another probe
772
843
  bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one;
773
- assert(num_probes < bucket_count()); // don't probe too many times!
844
+ assert(num_probes < bucket_count()
845
+ && "Hashtable is full: an error in key_equal<> or hash<>");
774
846
  }
775
847
  }
776
848
 
@@ -830,32 +902,58 @@ class sparse_hashtable {
830
902
 
831
903
  // INSERTION ROUTINES
832
904
  private:
905
+ // Private method used by insert_noresize and find_or_insert.
906
+ iterator insert_at(const_reference obj, size_type pos) {
907
+ if (size() >= max_size())
908
+ throw std::length_error("insert overflow");
909
+ if ( test_deleted(pos) ) { // just replace if it's been deleted
910
+ // The set() below will undelete this object. We just worry about stats
911
+ assert(num_deleted > 0);
912
+ --num_deleted; // used to be, now it isn't
913
+ }
914
+ table.set(pos, obj);
915
+ return iterator(this, table.get_iter(pos), table.nonempty_end());
916
+ }
917
+
833
918
  // If you know *this is big enough to hold obj, use this routine
834
- pair<iterator, bool> insert_noresize(const value_type& obj) {
919
+ pair<iterator, bool> insert_noresize(const_reference obj) {
835
920
  // First, double-check we're not inserting delkey
836
- assert(!use_deleted || !equals(get_key(obj), delkey));
921
+ assert((!settings.use_deleted() || !equals(get_key(obj), key_info.delkey))
922
+ && "Inserting the deleted key");
837
923
  const pair<size_type,size_type> pos = find_position(get_key(obj));
838
924
  if ( pos.first != ILLEGAL_BUCKET) { // object was already there
839
925
  return pair<iterator,bool>(iterator(this, table.get_iter(pos.first),
840
926
  table.nonempty_end()),
841
927
  false); // false: we didn't insert
842
928
  } else { // pos.second says where to put it
843
- if ( test_deleted(pos.second) ) { // just replace if it's been del.
844
- // The set() below will undelete this object. We just worry about stats
845
- assert(num_deleted > 0);
846
- --num_deleted; // used to be, now it isn't
847
- }
848
- table.set(pos.second, obj);
849
- return pair<iterator,bool>(iterator(this, table.get_iter(pos.second),
850
- table.nonempty_end()),
851
- true); // true: we did insert
929
+ return pair<iterator,bool>(insert_at(obj, pos.second), true);
852
930
  }
853
931
  }
854
932
 
933
+ // Specializations of insert(it, it) depending on the power of the iterator:
934
+ // (1) Iterator supports operator-, resize before inserting
935
+ template <class ForwardIterator>
936
+ void insert(ForwardIterator f, ForwardIterator l, STL_NAMESPACE::forward_iterator_tag) {
937
+ size_t dist = STL_NAMESPACE::distance(f, l);
938
+ if (dist >= (std::numeric_limits<size_type>::max)())
939
+ throw std::length_error("insert-range overflow");
940
+ resize_delta(static_cast<size_type>(dist));
941
+ for ( ; dist > 0; --dist, ++f) {
942
+ insert_noresize(*f);
943
+ }
944
+ }
945
+
946
+ // (2) Arbitrary iterator, can't tell how much to resize
947
+ template <class InputIterator>
948
+ void insert(InputIterator f, InputIterator l, STL_NAMESPACE::input_iterator_tag) {
949
+ for ( ; f != l; ++f)
950
+ insert(*f);
951
+ }
952
+
855
953
  public:
856
954
  // This is the normal insert routine, used by the outside world
857
- pair<iterator, bool> insert(const value_type& obj) {
858
- resize_delta(1); // adding an object, grow if need be
955
+ pair<iterator, bool> insert(const_reference obj) {
956
+ resize_delta(1); // adding an object, grow if need be
859
957
  return insert_noresize(obj);
860
958
  }
861
959
 
@@ -866,66 +964,102 @@ class sparse_hashtable {
866
964
  insert(f, l, typename STL_NAMESPACE::iterator_traits<InputIterator>::iterator_category());
867
965
  }
868
966
 
869
- // Iterator supports operator-, resize before inserting
870
- template <class ForwardIterator>
871
- void insert(ForwardIterator f, ForwardIterator l,
872
- STL_NAMESPACE::forward_iterator_tag) {
873
- size_type n = STL_NAMESPACE::distance(f, l); // TODO(csilvers): standard?
874
- resize_delta(n);
875
- for ( ; n > 0; --n, ++f)
876
- insert_noresize(*f);
877
- }
878
-
879
- // Arbitrary iterator, can't tell how much to resize
880
- template <class InputIterator>
881
- void insert(InputIterator f, InputIterator l,
882
- STL_NAMESPACE::input_iterator_tag) {
883
- for ( ; f != l; ++f)
884
- insert(*f);
967
+ // This is public only because sparse_hash_map::operator[] uses it.
968
+ // It does the minimal amount of work to implement operator[].
969
+ template <class DataType>
970
+ DataType& find_or_insert(const key_type& key) {
971
+ // First, double-check we're not inserting delkey
972
+ assert((!settings.use_deleted() || !equals(key, key_info.delkey))
973
+ && "Inserting the deleted key");
974
+ const pair<size_type,size_type> pos = find_position(key);
975
+ if ( pos.first != ILLEGAL_BUCKET) { // object was already there
976
+ return table.get_iter(pos.first)->second;
977
+ } else if (resize_delta(1)) { // needed to rehash to make room
978
+ // Since we resized, we can't use pos, so recalculate where to insert.
979
+ return insert_noresize(value_type(key, DataType())).first->second;
980
+ } else { // no need to rehash, insert right here
981
+ return insert_at(value_type(key, DataType()), pos.second)->second;
982
+ }
885
983
  }
886
984
 
887
-
888
985
  // DELETION ROUTINES
889
986
  size_type erase(const key_type& key) {
890
- // First, double-check we're not erasing delkey
891
- assert(!use_deleted || !equals(key, delkey));
987
+ // First, double-check we're not erasing delkey.
988
+ assert((!settings.use_deleted() || !equals(key, key_info.delkey))
989
+ && "Erasing the deleted key");
990
+ assert(!settings.use_deleted() || !equals(key, key_info.delkey));
892
991
  const_iterator pos = find(key); // shrug: shouldn't need to be const
893
992
  if ( pos != end() ) {
894
993
  assert(!test_deleted(pos)); // or find() shouldn't have returned it
895
994
  set_deleted(pos);
896
995
  ++num_deleted;
897
- consider_shrink = true; // will think about shrink after next insert
996
+ // will think about shrink after next insert
997
+ settings.set_consider_shrink(true);
898
998
  return 1; // because we deleted one thing
899
999
  } else {
900
1000
  return 0; // because we deleted nothing
901
1001
  }
902
1002
  }
903
1003
 
904
- // This is really evil: really it should be iterator, not const_iterator.
905
- // But...the only reason keys are const is to allow lookup.
906
- // Since that's a moot issue for deleted keys, we allow const_iterators
907
- void erase(const_iterator pos) {
1004
+ // We return the iterator past the deleted item.
1005
+ void erase(iterator pos) {
908
1006
  if ( pos == end() ) return; // sanity check
909
1007
  if ( set_deleted(pos) ) { // true if object has been newly deleted
910
1008
  ++num_deleted;
911
- consider_shrink = true; // will think about shrink after next insert
1009
+ // will think about shrink after next insert
1010
+ settings.set_consider_shrink(true);
1011
+ }
1012
+ }
1013
+
1014
+ void erase(iterator f, iterator l) {
1015
+ for ( ; f != l; ++f) {
1016
+ if ( set_deleted(f) ) // should always be true
1017
+ ++num_deleted;
912
1018
  }
1019
+ // will think about shrink after next insert
1020
+ settings.set_consider_shrink(true);
913
1021
  }
914
1022
 
1023
+ // We allow you to erase a const_iterator just like we allow you to
1024
+ // erase an iterator. This is in parallel to 'delete': you can delete
1025
+ // a const pointer just like a non-const pointer. The logic is that
1026
+ // you can't use the object after it's erased anyway, so it doesn't matter
1027
+ // if it's const or not.
1028
+ void erase(const_iterator pos) {
1029
+ if ( pos == end() ) return; // sanity check
1030
+ if ( set_deleted(pos) ) { // true if object has been newly deleted
1031
+ ++num_deleted;
1032
+ // will think about shrink after next insert
1033
+ settings.set_consider_shrink(true);
1034
+ }
1035
+ }
915
1036
  void erase(const_iterator f, const_iterator l) {
916
1037
  for ( ; f != l; ++f) {
917
1038
  if ( set_deleted(f) ) // should always be true
918
1039
  ++num_deleted;
919
1040
  }
920
- consider_shrink = true; // will think about shrink after next insert
1041
+ // will think about shrink after next insert
1042
+ settings.set_consider_shrink(true);
921
1043
  }
922
1044
 
923
1045
 
924
1046
  // COMPARISON
925
1047
  bool operator==(const sparse_hashtable& ht) const {
926
- // We really want to check that the hash functions are the same
927
- // but alas there's no way to do this. We just hope.
928
- return ( num_deleted == ht.num_deleted && table == ht.table );
1048
+ if (size() != ht.size()) {
1049
+ return false;
1050
+ } else if (this == &ht) {
1051
+ return true;
1052
+ } else {
1053
+ // Iterate through the elements in "this" and see if the
1054
+ // corresponding element is in ht
1055
+ for ( const_iterator it = begin(); it != end(); ++it ) {
1056
+ const_iterator it2 = ht.find(get_key(*it));
1057
+ if ((it2 == ht.end()) || (*it != *it2)) {
1058
+ return false;
1059
+ }
1060
+ }
1061
+ return true;
1062
+ }
929
1063
  }
930
1064
  bool operator!=(const sparse_hashtable& ht) const {
931
1065
  return !(*this == ht);
@@ -946,7 +1080,7 @@ class sparse_hashtable {
946
1080
  bool read_metadata(FILE *fp) {
947
1081
  num_deleted = 0; // since we got rid before writing
948
1082
  bool result = table.read_metadata(fp);
949
- reset_thresholds();
1083
+ settings.reset_thresholds(bucket_count());
950
1084
  return result;
951
1085
  }
952
1086
 
@@ -961,31 +1095,67 @@ class sparse_hashtable {
961
1095
  }
962
1096
 
963
1097
  private:
964
- // The actual data
965
- hasher hash; // required by hashed_associative_container
966
- key_equal equals;
967
- ExtractKey get_key;
968
- SetKey set_key;
969
- size_type num_deleted; // how many occupied buckets are marked deleted
970
- bool use_deleted; // false until delkey has been set
971
- // TODO(csilvers): make a pointer, and get rid of use_deleted (benchmark!)
972
- key_type delkey; // which key marks deleted entries
973
- float enlarge_resize_percent; // how full before resize
974
- float shrink_resize_percent; // how empty before resize
975
- size_type shrink_threshold; // table.size() * shrink_resize_percent
976
- size_type enlarge_threshold; // table.size() * enlarge_resize_percent
977
- sparsetable<value_type> table; // holds num_buckets and num_elements too
978
- bool consider_shrink; // true if we should try to shrink before next insert
979
-
980
- void reset_thresholds() {
981
- enlarge_threshold = static_cast<size_type>(table.size()
982
- * enlarge_resize_percent);
983
- shrink_threshold = static_cast<size_type>(table.size()
984
- * shrink_resize_percent);
985
- consider_shrink = false; // whatever caused us to reset already considered
1098
+ // Table is the main storage class.
1099
+ typedef sparsetable<value_type, DEFAULT_GROUP_SIZE, value_alloc_type> Table;
1100
+
1101
+ // Package templated functors with the other types to eliminate memory
1102
+ // needed for storing these zero-size operators. Since ExtractKey and
1103
+ // hasher's operator() might have the same function signature, they
1104
+ // must be packaged in different classes.
1105
+ struct Settings :
1106
+ sh_hashtable_settings<key_type, hasher, size_type, HT_MIN_BUCKETS> {
1107
+ explicit Settings(const hasher& hf)
1108
+ : sh_hashtable_settings<key_type, hasher, size_type, HT_MIN_BUCKETS>(
1109
+ hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {}
1110
+ };
1111
+
1112
+ // KeyInfo stores delete key and packages zero-size functors:
1113
+ // ExtractKey and SetKey.
1114
+ class KeyInfo : public ExtractKey, public SetKey, public key_equal {
1115
+ public:
1116
+ KeyInfo(const ExtractKey& ek, const SetKey& sk, const key_equal& eq)
1117
+ : ExtractKey(ek),
1118
+ SetKey(sk),
1119
+ key_equal(eq) {
1120
+ }
1121
+ const key_type get_key(const_reference v) const {
1122
+ return ExtractKey::operator()(v);
1123
+ }
1124
+ void set_key(pointer v, const key_type& k) const {
1125
+ SetKey::operator()(v, k);
1126
+ }
1127
+ bool equals(const key_type& a, const key_type& b) const {
1128
+ return key_equal::operator()(a, b);
1129
+ }
1130
+
1131
+ // Which key marks deleted entries.
1132
+ // TODO(csilvers): make a pointer, and get rid of use_deleted (benchmark!)
1133
+ typename remove_const<key_type>::type delkey;
1134
+ };
1135
+
1136
+ // Utility functions to access the templated operators
1137
+ size_type hash(const key_type& v) const {
1138
+ return settings.hash(v);
986
1139
  }
1140
+ bool equals(const key_type& a, const key_type& b) const {
1141
+ return key_info.equals(a, b);
1142
+ }
1143
+ const key_type get_key(const_reference v) const {
1144
+ return key_info.get_key(v);
1145
+ }
1146
+ void set_key(pointer v, const key_type& k) const {
1147
+ key_info.set_key(v, k);
1148
+ }
1149
+
1150
+ private:
1151
+ // Actual data
1152
+ Settings settings;
1153
+ KeyInfo key_info;
1154
+ size_type num_deleted; // how many occupied buckets are marked deleted
1155
+ Table table; // holds num_buckets and num_elements too
987
1156
  };
988
1157
 
1158
+
989
1159
  // We need a global swap as well
990
1160
  template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
991
1161
  inline void swap(sparse_hashtable<V,K,HF,ExK,SetK,EqK,A> &x,
@@ -1002,13 +1172,14 @@ const typename sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::size_type
1002
1172
  // How full we let the table get before we resize. Knuth says .8 is
1003
1173
  // good -- higher causes us to probe too much, though saves memory
1004
1174
  template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
1005
- const float sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_OCCUPANCY_FLT = 0.8f;
1175
+ const int sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_OCCUPANCY_PCT = 80;
1006
1176
 
1007
1177
  // How empty we let the table get before we resize lower.
1008
- // It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
1178
+ // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing
1009
1179
  template <class V, class K, class HF, class ExK, class SetK, class EqK, class A>
1010
- const float sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_EMPTY_FLT = 0.4f *
1011
- sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_OCCUPANCY_FLT;
1180
+ const int sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_EMPTY_PCT
1181
+ = static_cast<int>(0.4 *
1182
+ sparse_hashtable<V,K,HF,ExK,SetK,EqK,A>::HT_OCCUPANCY_PCT);
1012
1183
 
1013
1184
  _END_GOOGLE_NAMESPACE_
1014
1185