google_hash 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,47 +0,0 @@
1
- require './google_hash'
2
- require 'benchmark'
3
- require 'hitimes'
4
-
5
- def measure
6
- Hitimes::Interval.measure { yield }
7
- end
8
-
9
- def meas string
10
- puts "% -23s" % string + measure { yield }.to_s
11
- end
12
-
13
- def go num
14
- puts num
15
- # get all existing
16
- all = [Hash] + Object.constants.grep(/Goog/).reject{|n| n == :GoogleHash}.map{|n| eval n}
17
-
18
- for name in all do
19
- GC.start
20
- subject = name.new
21
- puts
22
- puts name
23
-
24
- subject = name.new
25
- meas( "populate string ") { num.times {|n| subject['abc'] = 4 } } rescue nil
26
- subject = name.new
27
- meas( "populate symbol") { num.times {|n| subject[:abc] = 4} } rescue nil
28
-
29
- meas( "populate int") { num.times {|n| subject[n] = 4}}
30
- meas("each") { subject.each{|k, v| } }
31
-
32
- begin
33
- subject = name.new
34
- subject[3] = 4
35
- meas("lookup int") { num.times {|n| subject[3]}}
36
- subject['abc'] = 3
37
- subject[:abc] = 3
38
-
39
- meas("lookup string") { num.times {|n| subject['abc']}}
40
- meas( "lookup symbol" ) { num.times {|n| subject[:abc]}}
41
- rescue
42
- end
43
- end
44
- end
45
-
46
- num = 200_000
47
- go num if $0 ==__FILE__
File without changes
@@ -1,1375 +0,0 @@
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: Craig Silverstein
32
- //
33
- // This tests <google/sparsehash/densehashtable.h>
34
- // This tests <google/dense_hash_set>
35
- // This tests <google/dense_hash_map>
36
- // This tests <google/sparsehash/sparsehashtable.h>
37
- // This tests <google/sparse_hash_set>
38
- // This tests <google/sparse_hash_map>
39
-
40
- // Since {dense,sparse}hashtable is templatized, it's important that
41
- // we test every function in every class in this file -- not just to
42
- // see if it works, but even if it compiles.
43
-
44
- #include "config.h"
45
- #include <stdio.h>
46
- #include <sys/stat.h> // for stat()
47
- #ifdef HAVE_UNISTD_H
48
- #include <unistd.h> // for unlink()
49
- #endif
50
- #include <string.h>
51
- #include <time.h> // for silly random-number-seed generator
52
- #include <math.h> // for sqrt()
53
- #include <map>
54
- #include <set>
55
- #include <iterator> // for insert_iterator
56
- #include <iostream>
57
- #include <iomanip> // for setprecision()
58
- #include <string>
59
- #include HASH_FUN_H // defined in config.h
60
- #include <google/type_traits.h>
61
- #include <google/dense_hash_map>
62
- #include <google/dense_hash_set>
63
- #include <google/sparsehash/densehashtable.h>
64
- #include <google/sparse_hash_map>
65
- #include <google/sparse_hash_set>
66
- #include <google/sparsehash/sparsehashtable.h>
67
-
68
- // Otherwise, VC++7 warns about size_t -> int in the cout logging lines
69
- #ifdef _MSC_VER
70
- #pragma warning(disable:4267)
71
- #endif
72
-
73
- using GOOGLE_NAMESPACE::sparse_hash_map;
74
- using GOOGLE_NAMESPACE::dense_hash_map;
75
- using GOOGLE_NAMESPACE::sparse_hash_set;
76
- using GOOGLE_NAMESPACE::dense_hash_set;
77
- using GOOGLE_NAMESPACE::sparse_hashtable;
78
- using GOOGLE_NAMESPACE::dense_hashtable;
79
- using STL_NAMESPACE::map;
80
- using STL_NAMESPACE::set;
81
- using STL_NAMESPACE::pair;
82
- using STL_NAMESPACE::make_pair;
83
- using STL_NAMESPACE::string;
84
- using STL_NAMESPACE::insert_iterator;
85
- using STL_NAMESPACE::allocator;
86
- using STL_NAMESPACE::equal_to;
87
- using STL_NAMESPACE::ostream;
88
-
89
- #define LOGF STL_NAMESPACE::cout // where we log to; LOGF is a historical name
90
-
91
- #define CHECK(cond) do { \
92
- if (!(cond)) { \
93
- LOGF << "Test failed: " #cond "\n"; \
94
- exit(1); \
95
- } \
96
- } while (0)
97
-
98
- #define CHECK_EQ(a, b) CHECK((a) == (b))
99
- #define CHECK_LT(a, b) CHECK((a) < (b))
100
- #define CHECK_GE(a, b) CHECK((a) >= (b))
101
-
102
- #ifndef _MSC_VER
103
- static string TmpFile(const char* basename) {
104
- return string("/tmp/") + basename;
105
- }
106
- #endif
107
-
108
- const char *words[] = {"Baffin\n", // in /usr/dict/words
109
- "Boffin\n", // not in
110
- "baffin\n", // not in
111
- "genial\n", // last word in
112
- "Aarhus\n", // first word alphabetically
113
- "Zurich\n", // last word alphabetically
114
- "Getty\n",
115
- };
116
-
117
- const char *nwords[] = {"Boffin\n",
118
- "baffin\n",
119
- };
120
-
121
- const char *default_dict[] = {"Aarhus\n",
122
- "aback\n",
123
- "abandon\n",
124
- "Baffin\n",
125
- "baffle\n",
126
- "bagged\n",
127
- "congenial\n",
128
- "genial\n",
129
- "Getty\n",
130
- "indiscreet\n",
131
- "linens\n",
132
- "pence\n",
133
- "reassure\n",
134
- "sequel\n",
135
- "zoning\n",
136
- "zoo\n",
137
- "Zurich\n",
138
- };
139
-
140
- // Apparently identity is not stl-standard, so we define our own
141
- template<class Value>
142
- struct Identity {
143
- Value& operator()(Value& v) const { return v; }
144
- const Value& operator()(const Value& v) const { return v; }
145
- };
146
-
147
- // Likewise, it's not standard to hash a string pre-tr1. Luckily, it is a char*
148
- #ifdef HAVE_UNORDERED_MAP
149
- typedef SPARSEHASH_HASH<string> StrHash;
150
- struct CharStarHash {
151
- size_t operator()(const char* s) const {
152
- return StrHash()(string(s));
153
- }
154
- };
155
- #else
156
- typedef SPARSEHASH_HASH<const char*> CharStarHash;
157
- struct StrHash {
158
- size_t operator()(const string& s) const {
159
- return SPARSEHASH_HASH<const char*>()(s.c_str());
160
- }
161
- };
162
- #endif
163
-
164
- // Let us log the pairs that make up a hash_map
165
- template<class P1, class P2>
166
- ostream& operator<<(ostream& s, const pair<P1, P2>& p) {
167
- s << "pair(" << p.first << ", " << p.second << ")";
168
- return s;
169
- }
170
-
171
- struct strcmp_fnc {
172
- bool operator()(const char* s1, const char* s2) const {
173
- return ((s1 == 0 && s2 == 0) ||
174
- (s1 && s2 && *s1 == *s2 && strcmp(s1, s2) == 0));
175
- }
176
- };
177
-
178
- namespace {
179
-
180
- template <class T, class H, class I, class S, class C, class A>
181
- void set_empty_key(sparse_hashtable<T,T,H,I,S,C,A> *ht, T val) {
182
- }
183
-
184
- template <class T, class H, class C>
185
- void set_empty_key(sparse_hash_set<T,H,C> *ht, T val) {
186
- }
187
-
188
- template <class K, class V, class H, class C>
189
- void set_empty_key(sparse_hash_map<K,V,H,C> *ht, K val) {
190
- }
191
-
192
- template <class T, class H, class I, class S, class C, class A>
193
- void set_empty_key(dense_hashtable<T,T,H,I,S,C,A> *ht, T val) {
194
- ht->set_empty_key(val);
195
- }
196
-
197
- template <class T, class H, class C>
198
- void set_empty_key(dense_hash_set<T,H,C> *ht, T val) {
199
- ht->set_empty_key(val);
200
- }
201
-
202
- template <class K, class V, class H, class C>
203
- void set_empty_key(dense_hash_map<K,V,H,C> *ht, K val) {
204
- ht->set_empty_key(val);
205
- }
206
-
207
- template <class T, class H, class I, class S, class C, class A>
208
- bool clear_no_resize(sparse_hashtable<T,T,H,I,S,C,A> *ht) {
209
- return false;
210
- }
211
-
212
- template <class T, class H, class C>
213
- bool clear_no_resize(sparse_hash_set<T,H,C> *ht) {
214
- return false;
215
- }
216
-
217
- template <class K, class V, class H, class C>
218
- bool clear_no_resize(sparse_hash_map<K,V,H,C> *ht) {
219
- return false;
220
- }
221
-
222
- template <class T, class H, class I, class S, class C, class A>
223
- bool clear_no_resize(dense_hashtable<T,T,H,I,S,C,A> *ht) {
224
- ht->clear_no_resize();
225
- return true;
226
- }
227
-
228
- template <class T, class H, class C>
229
- bool clear_no_resize(dense_hash_set<T,H,C> *ht) {
230
- ht->clear_no_resize();
231
- return true;
232
- }
233
-
234
- template <class K, class V, class H, class C>
235
- bool clear_no_resize(dense_hash_map<K,V,H,C> *ht) {
236
- ht->clear_no_resize();
237
- return true;
238
- }
239
-
240
- template <class T, class H, class I, class S, class C, class A>
241
- void insert(dense_hashtable<T,T,H,I,S,C,A> *ht, T val) {
242
- ht->insert(val);
243
- }
244
-
245
- template <class T, class H, class C>
246
- void insert(dense_hash_set<T,H,C> *ht, T val) {
247
- ht->insert(val);
248
- }
249
-
250
- template <class K, class V, class H, class C>
251
- void insert(dense_hash_map<K,V,H,C> *ht, K val) {
252
- ht->insert(pair<K,V>(val,V()));
253
- }
254
-
255
- template <class T, class H, class I, class S, class C, class A>
256
- void insert(sparse_hashtable<T,T,H,I,S,C,A> *ht, T val) {
257
- ht->insert(val);
258
- }
259
-
260
- template <class T, class H, class C>
261
- void insert(sparse_hash_set<T,H,C> *ht, T val) {
262
- ht->insert(val);
263
- }
264
-
265
- template <class K, class V, class H, class C>
266
- void insert(sparse_hash_map<K,V,H,C> *ht, K val) {
267
- ht->insert(pair<K,V>(val,V()));
268
- }
269
-
270
- template <class HT, class Iterator>
271
- void insert(HT *ht, Iterator begin, Iterator end) {
272
- ht->insert(begin, end);
273
- }
274
-
275
- // For hashtable's and hash_set's, the iterator insert works fine (and
276
- // is used). But for the hash_map's, the iterator insert expects the
277
- // iterators to point to pair's. So by looping over and calling insert
278
- // on each element individually, the code below automatically expands
279
- // into inserting a pair.
280
- template <class K, class V, class H, class C, class Iterator>
281
- void insert(dense_hash_map<K,V,H,C> *ht, Iterator begin, Iterator end) {
282
- while (begin != end) {
283
- insert(ht, *begin);
284
- ++begin;
285
- }
286
- }
287
-
288
- template <class K, class V, class H, class C, class Iterator>
289
- void insert(sparse_hash_map<K,V,H,C> *ht, Iterator begin, Iterator end) {
290
- while (begin != end) {
291
- insert(ht, *begin);
292
- ++begin;
293
- }
294
- }
295
-
296
- // A version of insert that uses the insert_iterator. But insert_iterator
297
- // isn't defined for the low level hashtable classes, so we just punt to insert.
298
-
299
- template <class T, class H, class I, class S, class C, class A>
300
- void iterator_insert(dense_hashtable<T,T,H,I,S,C,A>* ht, T val,
301
- insert_iterator<dense_hashtable<T,T,H,I,S,C,A> >* ) {
302
- ht->insert(val);
303
- }
304
-
305
- template <class T, class H, class C>
306
- void iterator_insert(dense_hash_set<T,H,C>* , T val,
307
- insert_iterator<dense_hash_set<T,H,C> >* ii) {
308
- *(*ii)++ = val;
309
- }
310
-
311
- template <class K, class V, class H, class C>
312
- void iterator_insert(dense_hash_map<K,V,H,C>* , K val,
313
- insert_iterator<dense_hash_map<K,V,H,C> >* ii) {
314
- *(*ii)++ = pair<K,V>(val,V());
315
- }
316
-
317
- template <class T, class H, class I, class S, class C, class A>
318
- void iterator_insert(sparse_hashtable<T,T,H,I,S,C,A>* ht, T val,
319
- insert_iterator<sparse_hashtable<T,T,H,I,S,C,A> >* ) {
320
- ht->insert(val);
321
- }
322
-
323
- template <class T, class H, class C>
324
- void iterator_insert(sparse_hash_set<T,H,C>* , T val,
325
- insert_iterator<sparse_hash_set<T,H,C> >* ii) {
326
- *(*ii)++ = val;
327
- }
328
-
329
- template <class K, class V, class H, class C>
330
- void iterator_insert(sparse_hash_map<K,V,H,C> *, K val,
331
- insert_iterator<sparse_hash_map<K,V,H,C> >* ii) {
332
- *(*ii)++ = pair<K,V>(val,V());
333
- }
334
-
335
-
336
- void write_item(FILE *fp, const char *val) {
337
- fwrite(val, strlen(val), 1, fp); // \n serves to separate
338
- }
339
-
340
- // The weird 'const' declarations are desired by the compiler. Yucko.
341
- void write_item(FILE *fp, const pair<char*const,int> &val) {
342
- fwrite(val.first, strlen(val.first), 1, fp);
343
- }
344
-
345
- void write_item(FILE *fp, const string &val) {
346
- fwrite(val.data(), val.length(), 1, fp); // \n serves to separate
347
- }
348
-
349
- // The weird 'const' declarations are desired by the compiler. Yucko.
350
- void write_item(FILE *fp, const pair<const string,int> &val) {
351
- fwrite(val.first.data(), val.first.length(), 1, fp);
352
- }
353
-
354
- char* read_line(FILE* fp, char* line, int linesize) {
355
- if ( fgets(line, linesize, fp) == NULL )
356
- return NULL;
357
- // normalize windows files :-(
358
- const size_t linelen = strlen(line);
359
- if ( linelen >= 2 && line[linelen-2] == '\r' && line[linelen-1] == '\n' ) {
360
- line[linelen-2] = '\n';
361
- line[linelen-1] = '\0';
362
- }
363
- return line;
364
- }
365
-
366
- void read_item(FILE *fp, char*const* val) {
367
- char line[1024];
368
- read_line(fp, line, sizeof(line));
369
- char **p = const_cast<char**>(val);
370
- *p = strdup(line);
371
- }
372
-
373
- void read_item(FILE *fp, pair<char*const,int> *val) {
374
- char line[1024];
375
- read_line(fp, line, sizeof(line));
376
- char **p = const_cast<char**>(&val->first);
377
- *p = strdup(line);
378
- }
379
-
380
- void read_item(FILE *fp, const string* val) {
381
- char line[1024];
382
- read_line(fp, line, sizeof(line));
383
- new(const_cast<string*>(val)) string(line); // need to use placement new
384
- }
385
-
386
- void read_item(FILE *fp, pair<const string,int> *val) {
387
- char line[1024];
388
- read_line(fp, line, sizeof(line));
389
- new(const_cast<string*>(&val->first)) string(line);
390
- }
391
-
392
- void free_item(char*const* val) {
393
- free(*val);
394
- }
395
-
396
- void free_item(pair<char*const,int> *val) {
397
- free(val->first);
398
- }
399
-
400
- int get_int_item(int int_item) {
401
- return int_item;
402
- }
403
-
404
- int get_int_item(pair<int, int> val) {
405
- return val.first;
406
- }
407
-
408
- int getintkey(int i) { return i; }
409
-
410
- int getintkey(const pair<int, int> &p) { return p.first; }
411
-
412
- } // end anonymous namespace
413
-
414
- // Performs tests where the hashtable's value type is assumed to be int.
415
- template <class htint>
416
- void test_int() {
417
- htint x;
418
- htint y(1000);
419
- htint z(64);
420
- set_empty_key(&x, 0xefefef);
421
- set_empty_key(&y, 0xefefef);
422
- set_empty_key(&z, 0xefefef);
423
-
424
- CHECK(y.empty());
425
- insert(&y, 1);
426
- CHECK(!y.empty());
427
- insert(&y, 11);
428
- insert(&y, 111);
429
- insert(&y, 1111);
430
- insert(&y, 11111);
431
- insert(&y, 111111);
432
- insert(&y, 1111111); // 1M, more or less
433
- insert(&y, 11111111);
434
- insert(&y, 111111111);
435
- insert(&y, 1111111111); // 1B, more or less
436
- for ( int i = 0; i < 64; ++i )
437
- insert(&z, i);
438
- // test the second half of the insert with an insert_iterator
439
- insert_iterator<htint> insert_iter(z, z.begin());
440
- for ( int i = 32; i < 64; ++i )
441
- iterator_insert(&z, i, &insert_iter);
442
-
443
- // only perform the following CHECKs for
444
- // dense{hashtable, _hash_set, _hash_map}
445
- if (clear_no_resize(&x)) {
446
- // make sure x has to increase its number of buckets
447
- typename htint::size_type empty_bucket_count = x.bucket_count();
448
- int last_element = 0;
449
- while (x.bucket_count() == empty_bucket_count) {
450
- insert(&x, last_element);
451
- ++last_element;
452
- }
453
- // if clear_no_resize is supported (i.e. htint is a
454
- // dense{hashtable,_hash_set,_hash_map}), it should leave the bucket_count
455
- // as is.
456
- typename htint::size_type last_bucket_count = x.bucket_count();
457
- clear_no_resize(&x);
458
- CHECK(last_bucket_count == x.bucket_count());
459
- CHECK(x.empty());
460
- LOGF << "x has " << x.bucket_count() << " buckets\n";
461
- LOGF << "x size " << x.size() << "\n";
462
- // when inserting the same number of elements again, no resize should be
463
- // necessary
464
- for (int i = 0; i < last_element; ++i) {
465
- insert(&x, i);
466
- CHECK(x.bucket_count() == last_bucket_count);
467
- }
468
- }
469
-
470
- for ( typename htint::const_iterator it = y.begin(); it != y.end(); ++it )
471
- LOGF << "y: " << get_int_item(*it) << "\n";
472
- z.insert(y.begin(), y.end());
473
- swap(y,z);
474
- for ( typename htint::iterator it = y.begin(); it != y.end(); ++it )
475
- LOGF << "y+z: " << get_int_item(*it) << "\n";
476
- LOGF << "z has " << z.bucket_count() << " buckets\n";
477
- LOGF << "y has " << y.bucket_count() << " buckets\n";
478
- LOGF << "z size: " << z.size() << "\n";
479
-
480
- for (int i = 0; i < 64; ++i)
481
- CHECK(y.find(i) != y.end());
482
-
483
- CHECK(z.size() == 10);
484
- z.set_deleted_key(1010101010); // an unused value
485
- z.erase(11111);
486
- CHECK(z.size() == 9);
487
- insert(&z, 11111); // should retake deleted value
488
- CHECK(z.size() == 10);
489
- // Do the delete/insert again. Last time we probably resized; this time no
490
- z.erase(11111);
491
- insert(&z, 11111); // should retake deleted value
492
- CHECK(z.size() == 10);
493
-
494
- z.erase(-11111); // shouldn't do anything
495
- CHECK(z.size() == 10);
496
- z.erase(1);
497
- CHECK(z.size() == 9);
498
-
499
- typename htint::iterator itdel = z.find(1111);
500
- pair<typename htint::iterator,typename htint::iterator> itdel2
501
- = z.equal_range(1111);
502
- CHECK(itdel2.first != z.end());
503
- CHECK(&*itdel2.first == &*itdel); // while we're here, check equal_range()
504
- CHECK(itdel2.second == ++itdel2.first);
505
- pair<typename htint::const_iterator,typename htint::const_iterator> itdel3
506
- = const_cast<const htint*>(&z)->equal_range(1111);
507
- CHECK(itdel3.first != z.end());
508
- CHECK(&*itdel3.first == &*itdel);
509
- CHECK(itdel3.second == ++itdel3.first);
510
-
511
- z.erase(itdel);
512
- CHECK(z.size() == 8);
513
- itdel2 = z.equal_range(1111);
514
- CHECK(itdel2.first == z.end());
515
- CHECK(itdel2.second == itdel2.first);
516
- itdel3 = const_cast<const htint*>(&z)->equal_range(1111);
517
- CHECK(itdel3.first == z.end());
518
- CHECK(itdel3.second == itdel3.first);
519
-
520
- itdel = z.find(2222); // should be end()
521
- z.erase(itdel); // shouldn't do anything
522
- CHECK(z.size() == 8);
523
- for ( typename htint::const_iterator it = z.begin(); it != z.end(); ++it )
524
- LOGF << "y: " << get_int_item(*it) << "\n";
525
- z.set_deleted_key(1010101011); // a different unused value
526
- for ( typename htint::const_iterator it = z.begin(); it != z.end(); ++it )
527
- LOGF << "y: " << get_int_item(*it) << "\n";
528
- LOGF << "That's " << z.size() << " elements\n";
529
- z.erase(z.begin(), z.end());
530
- CHECK(z.empty());
531
-
532
- y.clear();
533
- CHECK(y.empty());
534
- LOGF << "y has " << y.bucket_count() << " buckets\n";
535
- }
536
-
537
- // Performs tests where the hashtable's value type is assumed to be char*.
538
- // The read_write parameters specifies whether the read/write tests
539
- // should be performed. Note that densehashtable::write_metdata is not
540
- // implemented, so we only do the read/write tests for the
541
- // sparsehashtable varieties.
542
- template <class ht>
543
- void test_charptr(bool read_write) {
544
- ht w;
545
- set_empty_key(&w, (char*) NULL);
546
- insert(&w, const_cast<char **>(nwords),
547
- const_cast<char **>(nwords) + sizeof(nwords) / sizeof(*nwords));
548
- LOGF << "w has " << w.size() << " items\n";
549
- CHECK(w.size() == 2);
550
- CHECK(w == w);
551
-
552
- ht x;
553
- set_empty_key(&x, (char*) NULL);
554
- long dict_size = 1; // for size stats -- can't be 0 'cause of division
555
-
556
- map<string, int> counts;
557
- // Hash the dictionary
558
- {
559
- // automake says 'look for all data files in $srcdir.' OK.
560
- string filestr = (string(getenv("srcdir") ? getenv("srcdir") : ".") +
561
- "/src/words");
562
- const char* file = filestr.c_str();
563
- FILE *fp = fopen(file, "rb");
564
- if ( fp == NULL ) {
565
- LOGF << "Can't open " << file << ", using small, built-in dict...\n";
566
- for (int i = 0; i < sizeof(default_dict)/sizeof(*default_dict); ++i) {
567
- insert(&x, strdup(default_dict[i]));
568
- counts[default_dict[i]] = 0;
569
- }
570
- } else {
571
- char line[1024];
572
- while ( read_line(fp, line, sizeof(line)) ) {
573
- insert(&x, strdup(line));
574
- counts[line] = 0;
575
- }
576
- LOGF << "Read " << x.size() << " words from " << file << "\n";
577
- fclose(fp);
578
- struct stat buf;
579
- stat(file, &buf);
580
- dict_size = buf.st_size;
581
- LOGF << "Size of " << file << ": " << buf.st_size << " bytes\n";
582
- }
583
- for (char **word = const_cast<char **>(words);
584
- word < const_cast<char **>(words) + sizeof(words) / sizeof(*words);
585
- ++word ) {
586
- if (x.find(*word) == x.end()) {
587
- CHECK(w.find(*word) != w.end());
588
- } else {
589
- CHECK(w.find(*word) == w.end());
590
- }
591
- }
592
- }
593
- CHECK(counts.size() == x.size());
594
-
595
- // Save the hashtable.
596
- if (read_write) {
597
- const string file_string = TmpFile("#hashtable_unittest_dicthash");
598
- const char* file = file_string.c_str();
599
- FILE *fp = fopen(file, "wb");
600
- if ( fp == NULL ) {
601
- // maybe we can't write to /tmp/. Try the current directory
602
- file = "#hashtable_unittest_dicthash";
603
- fp = fopen(file, "wb");
604
- }
605
- if ( fp == NULL ) {
606
- LOGF << "Can't open " << file << " skipping hashtable save...\n";
607
- } else {
608
- x.write_metadata(fp); // this only writes meta-information
609
- int write_count = 0;
610
- for ( typename ht::iterator it = x.begin(); it != x.end(); ++it ) {
611
- write_item(fp, *it);
612
- free_item(&(*it));
613
- ++write_count;
614
- }
615
- LOGF << "Wrote " << write_count << " words to " << file << "\n";
616
- fclose(fp);
617
- struct stat buf;
618
- stat(file, &buf);
619
- LOGF << "Size of " << file << ": " << buf.st_size << " bytes\n";
620
- LOGF << STL_NAMESPACE::setprecision(3)
621
- << "Hashtable overhead "
622
- << (buf.st_size - dict_size) * 100.0 / dict_size
623
- << "% ("
624
- << (buf.st_size - dict_size) * 8.0 / write_count
625
- << " bits/entry)\n";
626
- x.clear();
627
-
628
- // Load the hashtable
629
- fp = fopen(file, "rb");
630
- if ( fp == NULL ) {
631
- LOGF << "Can't open " << file << " skipping hashtable reload...\n";
632
- } else {
633
- x.read_metadata(fp); // reads metainformation
634
- LOGF << "Hashtable size: " << x.size() << "\n";
635
- int read_count = 0;
636
- for ( typename ht::iterator it = x.begin(); it != x.end(); ++it ) {
637
- read_item(fp, &(*it));
638
- ++read_count;
639
- }
640
- LOGF << "Read " << read_count << " words from " << file << "\n";
641
- fclose(fp);
642
- unlink(file);
643
- for ( char **word = const_cast<char **>(words);
644
- word < const_cast<char **>(words) + sizeof(words) / sizeof(*words);
645
- ++word ) {
646
- if (x.find(*word) == x.end()) {
647
- CHECK(w.find(*word) != w.end());
648
- } else {
649
- CHECK(w.find(*word) == w.end());
650
- }
651
- }
652
- }
653
- }
654
- }
655
- for ( typename ht::iterator it = x.begin(); it != x.end(); ++it ) {
656
- free_item(&(*it));
657
- }
658
- }
659
-
660
- // Perform tests where the hashtable's value type is assumed to
661
- // be string.
662
- // TODO(austern): factor out the bulk of test_charptr and test_string
663
- // into a common function.
664
- template <class ht>
665
- void test_string(bool read_write) {
666
- ht w;
667
- set_empty_key(&w, string("-*- empty key -*-"));
668
- const int N = sizeof(nwords) / sizeof(*nwords);
669
- string* nwords1 = new string[N];
670
- for (int i = 0; i < N; ++i)
671
- nwords1[i] = nwords[i];
672
- insert(&w, nwords1, nwords1 + N);
673
- delete[] nwords1;
674
- LOGF << "w has " << w.size() << " items\n";
675
- CHECK(w.size() == 2);
676
- CHECK(w == w);
677
-
678
- ht x;
679
- set_empty_key(&x, string("-*- empty key -*-"));
680
- long dict_size = 1; // for size stats -- can't be 0 'cause of division
681
-
682
- map<string, int> counts;
683
- // Hash the dictionary
684
- {
685
- // automake says 'look for all data files in $srcdir.' OK.
686
- string filestr = (string(getenv("srcdir") ? getenv("srcdir") : ".") +
687
- "/src/words");
688
- const char* file = filestr.c_str();
689
- FILE *fp = fopen(file, "rb");
690
- if ( fp == NULL ) {
691
- LOGF << "Can't open " << file << ", using small, built-in dict...\n";
692
- for (int i = 0; i < sizeof(default_dict)/sizeof(*default_dict); ++i) {
693
- insert(&x, string(default_dict[i]));
694
- counts[default_dict[i]] = 0;
695
- }
696
- } else {
697
- char line[1024];
698
- while ( fgets(line, sizeof(line), fp) ) {
699
- insert(&x, string(line));
700
- counts[line] = 0;
701
- }
702
- LOGF << "Read " << x.size() << " words from " << file << "\n";
703
- fclose(fp);
704
- struct stat buf;
705
- stat(file, &buf);
706
- dict_size = buf.st_size;
707
- LOGF << "Size of " << file << ": " << buf.st_size << " bytes\n";
708
- }
709
- for ( const char* const* word = words;
710
- word < words + sizeof(words) / sizeof(*words);
711
- ++word ) {
712
- if (x.find(*word) == x.end()) {
713
- CHECK(w.find(*word) != w.end());
714
- } else {
715
- CHECK(w.find(*word) == w.end());
716
- }
717
- }
718
- }
719
- CHECK(counts.size() == x.size());
720
- {
721
- // verify that size() works correctly
722
- int xcount = 0;
723
- for ( typename ht::iterator it = x.begin(); it != x.end(); ++it ) {
724
- ++xcount;
725
- }
726
- CHECK(x.size() == xcount);
727
- }
728
-
729
- // Save the hashtable.
730
- if (read_write) {
731
- const string file_string = TmpFile("#hashtable_unittest_dicthash_str");
732
- const char* file = file_string.c_str();
733
- FILE *fp = fopen(file, "wb");
734
- if ( fp == NULL ) {
735
- // maybe we can't write to /tmp/. Try the current directory
736
- file = "#hashtable_unittest_dicthash_str";
737
- fp = fopen(file, "wb");
738
- }
739
- if ( fp == NULL ) {
740
- LOGF << "Can't open " << file << " skipping hashtable save...\n";
741
- } else {
742
- x.write_metadata(fp); // this only writes meta-information
743
- int write_count = 0;
744
- for ( typename ht::iterator it = x.begin(); it != x.end(); ++it ) {
745
- write_item(fp, *it);
746
- ++write_count;
747
- }
748
- LOGF << "Wrote " << write_count << " words to " << file << "\n";
749
- fclose(fp);
750
- struct stat buf;
751
- stat(file, &buf);
752
- LOGF << "Size of " << file << ": " << buf.st_size << " bytes\n";
753
- LOGF << STL_NAMESPACE::setprecision(3)
754
- << "Hashtable overhead "
755
- << (buf.st_size - dict_size) * 100.0 / dict_size
756
- << "% ("
757
- << (buf.st_size - dict_size) * 8.0 / write_count
758
- << " bits/entry)\n";
759
- x.clear();
760
-
761
- // Load the hashtable
762
- fp = fopen(file, "rb");
763
- if ( fp == NULL ) {
764
- LOGF << "Can't open " << file << " skipping hashtable reload...\n";
765
- } else {
766
- x.read_metadata(fp); // reads metainformation
767
- LOGF << "Hashtable size: " << x.size() << "\n";
768
- int count = 0;
769
- for ( typename ht::iterator it = x.begin(); it != x.end(); ++it ) {
770
- read_item(fp, &(*it));
771
- ++count;
772
- }
773
- LOGF << "Read " << count << " words from " << file << "\n";
774
- fclose(fp);
775
- unlink(file);
776
- for ( const char* const* word = words;
777
- word < words + sizeof(words) / sizeof(*words);
778
- ++word ) {
779
- if (x.find(*word) == x.end()) {
780
- CHECK(w.find(*word) != w.end());
781
- } else {
782
- CHECK(w.find(*word) == w.end());
783
- }
784
- }
785
- }
786
- }
787
- }
788
-
789
- // ensure that destruction is done properly in clear_no_resize()
790
- if (!clear_no_resize(&w)) w.clear();
791
- }
792
-
793
- // The read_write parameters specifies whether the read/write tests
794
- // should be performed. Note that densehashtable::write_metdata is not
795
- // implemented, so we only do the read/write tests for the
796
- // sparsehashtable varieties.
797
- template<class ht, class htstr, class htint>
798
- void test(bool read_write) {
799
- test_int<htint>();
800
- test_string<htstr>(read_write);
801
- test_charptr<ht>(read_write);
802
- }
803
-
804
- // For data types with trivial copy-constructors and destructors, we
805
- // should use an optimized routine for data-copying, that involves
806
- // memmove. We test this by keeping count of how many times the
807
- // copy-constructor is called; it should be much less with the
808
- // optimized code.
809
-
810
- class Memmove {
811
- public:
812
- Memmove(): i_(0) {}
813
- explicit Memmove(int i): i_(i) {}
814
- Memmove(const Memmove& that) {
815
- this->i_ = that.i_;
816
- num_copies_++;
817
- }
818
-
819
- int i_;
820
- static int num_copies_;
821
- };
822
- int Memmove::num_copies_ = 0;
823
-
824
-
825
- // This is what tells the hashtable code it can use memmove for this class:
826
- _START_GOOGLE_NAMESPACE_
827
- template<> struct has_trivial_copy<Memmove> : true_type { };
828
- template<> struct has_trivial_destructor<Memmove> : true_type { };
829
- _END_GOOGLE_NAMESPACE_
830
-
831
- class NoMemmove {
832
- public:
833
- NoMemmove(): i_(0) {}
834
- explicit NoMemmove(int i): i_(i) {}
835
- NoMemmove(const NoMemmove& that) {
836
- this->i_ = that.i_;
837
- num_copies_++;
838
- }
839
-
840
- int i_;
841
- static int num_copies_;
842
- };
843
- int NoMemmove::num_copies_ = 0;
844
-
845
- void TestSimpleDataTypeOptimizations() {
846
- {
847
- sparse_hash_map<int, Memmove> memmove;
848
- sparse_hash_map<int, NoMemmove> nomemmove;
849
-
850
- Memmove::num_copies_ = 0; // reset
851
- NoMemmove::num_copies_ = 0; // reset
852
- for (int i = 10000; i > 0; i--) {
853
- memmove[i] = Memmove(i);
854
- }
855
- for (int i = 10000; i > 0; i--) {
856
- nomemmove[i] = NoMemmove(i);
857
- }
858
- LOGF << "sparse_hash_map copies for unoptimized/optimized cases: "
859
- << NoMemmove::num_copies_ << "/" << Memmove::num_copies_ << "\n";
860
- CHECK(NoMemmove::num_copies_ > Memmove::num_copies_);
861
- }
862
- // Same should hold true for dense_hash_map
863
- {
864
- dense_hash_map<int, Memmove> memmove;
865
- dense_hash_map<int, NoMemmove> nomemmove;
866
- memmove.set_empty_key(0);
867
- nomemmove.set_empty_key(0);
868
-
869
- Memmove::num_copies_ = 0; // reset
870
- NoMemmove::num_copies_ = 0; // reset
871
- for (int i = 10000; i > 0; i--) {
872
- memmove[i] = Memmove(i);
873
- }
874
- for (int i = 10000; i > 0; i--) {
875
- nomemmove[i] = NoMemmove(i);
876
- }
877
- LOGF << "dense_hash_map copies for unoptimized/optimized cases: "
878
- << NoMemmove::num_copies_ << "/" << Memmove::num_copies_ << "\n";
879
- CHECK(NoMemmove::num_copies_ > Memmove::num_copies_);
880
- }
881
- }
882
-
883
- void TestShrinking() {
884
- // We want to make sure that when we create a hashtable, and then
885
- // add and delete one element, the size of the hashtable doesn't
886
- // change.
887
- {
888
- sparse_hash_set<int> s;
889
- s.set_deleted_key(0);
890
- const int old_bucket_count = s.bucket_count();
891
- s.insert(4);
892
- s.erase(4);
893
- s.insert(4);
894
- s.erase(4);
895
- CHECK_EQ(old_bucket_count, s.bucket_count());
896
- }
897
- {
898
- dense_hash_set<int> s;
899
- s.set_deleted_key(0);
900
- s.set_empty_key(1);
901
- const int old_bucket_count = s.bucket_count();
902
- s.insert(4);
903
- s.erase(4);
904
- s.insert(4);
905
- s.erase(4);
906
- CHECK_EQ(old_bucket_count, s.bucket_count());
907
- }
908
- {
909
- sparse_hash_set<int> s(2); // start small: only expects 2 items
910
- CHECK_LT(s.bucket_count(), 32); // verify we actually do start small
911
- s.set_deleted_key(0);
912
- const int old_bucket_count = s.bucket_count();
913
- s.insert(4);
914
- s.erase(4);
915
- s.insert(4);
916
- s.erase(4);
917
- CHECK_EQ(old_bucket_count, s.bucket_count());
918
- }
919
- {
920
- dense_hash_set<int> s(2); // start small: only expects 2 items
921
- CHECK_LT(s.bucket_count(), 32); // verify we actually do start small
922
- s.set_deleted_key(0);
923
- s.set_empty_key(1);
924
- const int old_bucket_count = s.bucket_count();
925
- s.insert(4);
926
- s.erase(4);
927
- s.insert(4);
928
- s.erase(4);
929
- CHECK_EQ(old_bucket_count, s.bucket_count());
930
- }
931
- }
932
-
933
- class TestHashFcn : public SPARSEHASH_HASH<int> {
934
- public:
935
- explicit TestHashFcn(int i)
936
- : id_(i) {
937
- }
938
-
939
- int id() const {
940
- return id_;
941
- }
942
-
943
- private:
944
- int id_;
945
- };
946
-
947
- class TestEqualTo : public equal_to<int> {
948
- public:
949
- explicit TestEqualTo(int i)
950
- : id_(i) {
951
- }
952
-
953
- int id() const {
954
- return id_;
955
- }
956
-
957
- private:
958
- int id_;
959
- };
960
-
961
- template <template <class V, class H, class E, class A> class Hash>
962
- void TestHash() {
963
- typedef Hash<int, TestHashFcn, TestEqualTo, allocator<int> > TheHash;
964
- const TestHashFcn fcn(1);
965
- const TestEqualTo eqt(2);
966
- {
967
- const TheHash simple(0, fcn, eqt);
968
- CHECK(fcn.id() == simple.hash_funct().id());
969
- CHECK(eqt.id() == simple.key_eq().id());
970
- }
971
- {
972
- const set<int> input;
973
- const TheHash iterated(input.begin(), input.end(), 0, fcn, eqt);
974
- CHECK(fcn.id() == iterated.hash_funct().id());
975
- CHECK(eqt.id() == iterated.key_eq().id());
976
- }
977
- }
978
-
979
- static void TestHashes() {
980
- TestHash<sparse_hash_set>();
981
- TestHash<dense_hash_set>();
982
- }
983
-
984
- template <template <class K, class T, class H, class E, class A> class Map>
985
- void TestMap() {
986
- typedef Map<int, int, TestHashFcn, TestEqualTo, allocator<int> > TheMap;
987
- const TestHashFcn fcn(1);
988
- const TestEqualTo eqt(2);
989
- {
990
- const TheMap simple(0, fcn, eqt);
991
- CHECK(fcn.id() == simple.hash_funct().id());
992
- CHECK(eqt.id() == simple.key_eq().id());
993
- }
994
- {
995
- const map<int, int> input;
996
- const TheMap iterated(input.begin(), input.end(), 0, fcn, eqt);
997
- CHECK(fcn.id() == iterated.hash_funct().id());
998
- CHECK(eqt.id() == iterated.key_eq().id());
999
- }
1000
- }
1001
-
1002
- static void TestMaps() {
1003
- TestMap<sparse_hash_map>();
1004
- TestMap<dense_hash_map>();
1005
- }
1006
-
1007
- static void TestOperatorEquals() {
1008
- {
1009
- dense_hash_set<int> sa, sb;
1010
- sa.set_empty_key(-1);
1011
- sb.set_empty_key(-1);
1012
- sa.set_deleted_key(-2);
1013
- sb.set_deleted_key(-2);
1014
- CHECK(sa == sb);
1015
- sa.insert(1);
1016
- CHECK(sa != sb);
1017
- sa.insert(2);
1018
- CHECK(sa != sb);
1019
- sb.insert(2);
1020
- CHECK(sa != sb);
1021
- sb.insert(1);
1022
- CHECK(sa == sb);
1023
- sb.erase(1);
1024
- CHECK(sa != sb);
1025
- }
1026
- {
1027
- dense_hash_map<int, string> sa, sb;
1028
- sa.set_empty_key(-1);
1029
- sb.set_empty_key(-1);
1030
- sa.set_deleted_key(-2);
1031
- sb.set_deleted_key(-2);
1032
- CHECK(sa == sb);
1033
- sa.insert(make_pair(1, "a"));
1034
- CHECK(sa != sb);
1035
- sa.insert(make_pair(2, "b"));
1036
- CHECK(sa != sb);
1037
- sb.insert(make_pair(2, "b"));
1038
- CHECK(sa != sb);
1039
- sb.insert(make_pair(1, "a"));
1040
- CHECK(sa == sb);
1041
- sa[1] = "goodbye";
1042
- CHECK(sa != sb);
1043
- sb.erase(1);
1044
- CHECK(sa != sb);
1045
- }
1046
- }
1047
-
1048
- // Test the interface for setting the resize parameters in a
1049
- // sparse_hash_set or dense_hash_set. If use_tr1_api is true,
1050
- // we use the newer tr1-inspired functions to set resize_parameters,
1051
- // rather than my old, home-grown API
1052
- template<class HS, bool USE_TR1_API>
1053
- static void TestResizingParameters() {
1054
- const int kSize = 16536;
1055
- // Check growing past various thresholds and then shrinking below
1056
- // them.
1057
- for (float grow_threshold = 0.2f;
1058
- grow_threshold <= 0.8f;
1059
- grow_threshold += 0.2f) {
1060
- HS hs;
1061
- hs.set_deleted_key(-1);
1062
- set_empty_key(&hs, -2);
1063
- if (USE_TR1_API) {
1064
- hs.max_load_factor(grow_threshold);
1065
- hs.min_load_factor(0.0);
1066
- } else {
1067
- hs.set_resizing_parameters(0.0, grow_threshold);
1068
- }
1069
- hs.resize(kSize);
1070
- size_t bucket_count = hs.bucket_count();
1071
- // Erase and insert an element to set consider_shrink = true,
1072
- // which should not cause a shrink because the threshold is 0.0.
1073
- insert(&hs, 1);
1074
- hs.erase(1);
1075
- for (int i = 0;; ++i) {
1076
- insert(&hs, i);
1077
- if (static_cast<float>(hs.size())/bucket_count < grow_threshold) {
1078
- CHECK(hs.bucket_count() == bucket_count);
1079
- } else {
1080
- CHECK(hs.bucket_count() > bucket_count);
1081
- break;
1082
- }
1083
- }
1084
- // Now set a shrink threshold 1% below the current size and remove
1085
- // items until the size falls below that.
1086
- const float shrink_threshold = static_cast<float>(hs.size()) /
1087
- hs.bucket_count() - 0.01f;
1088
- if (USE_TR1_API) {
1089
- hs.max_load_factor(1.0);
1090
- hs.min_load_factor(shrink_threshold);
1091
- } else {
1092
- hs.set_resizing_parameters(shrink_threshold, 1.0);
1093
- }
1094
- bucket_count = hs.bucket_count();
1095
- for (int i = 0;; ++i) {
1096
- hs.erase(i);
1097
- // A resize is only triggered by an insert, so add and remove a
1098
- // value every iteration to trigger the shrink as soon as the
1099
- // threshold is passed.
1100
- hs.erase(i+1);
1101
- insert(&hs, i+1);
1102
- if (static_cast<float>(hs.size())/bucket_count > shrink_threshold) {
1103
- CHECK(hs.bucket_count() == bucket_count);
1104
- } else {
1105
- CHECK(hs.bucket_count() < bucket_count);
1106
- break;
1107
- }
1108
- }
1109
- }
1110
- }
1111
-
1112
- // Tests the some of the tr1-inspired API features.
1113
- template<class HS>
1114
- static void TestTR1API() {
1115
- HS hs;
1116
- hs.set_deleted_key(-1);
1117
- set_empty_key(&hs, -2);
1118
-
1119
- typename HS::size_type expected_bucknum = hs.bucket(1);
1120
- insert(&hs, 1);
1121
- typename HS::size_type bucknum = hs.bucket(1);
1122
- CHECK(expected_bucknum == bucknum);
1123
- typename HS::const_local_iterator b = hs.begin(bucknum);
1124
- typename HS::const_local_iterator e = hs.end(bucknum);
1125
- CHECK(b != e);
1126
- CHECK(getintkey(*b) == 1);
1127
- b++;
1128
- CHECK(b == e);
1129
-
1130
- hs.erase(1);
1131
- bucknum = hs.bucket(1);
1132
- CHECK(expected_bucknum == bucknum);
1133
- b = hs.begin(bucknum);
1134
- e = hs.end(bucknum);
1135
- CHECK(b == e);
1136
-
1137
- // For very small sets, the min-bucket-size gets in the way, so
1138
- // let's make our hash_set bigger.
1139
- for (int i = 0; i < 10000; i++)
1140
- insert(&hs, i);
1141
- float f = hs.load_factor();
1142
- CHECK(f >= hs.min_load_factor());
1143
- CHECK(f <= hs.max_load_factor());
1144
- }
1145
-
1146
- class MemUsingKey {
1147
- public:
1148
- // TODO(csilvers): nix this when requirement for zero-arg keys goes away
1149
- MemUsingKey() : data_(new int) {
1150
- net_allocations_++;
1151
- }
1152
- MemUsingKey(int i) : data_(new int(i)) {
1153
- net_allocations_++;
1154
- }
1155
- MemUsingKey(const MemUsingKey& that) : data_(new int(*that.data_)) {
1156
- net_allocations_++;
1157
- }
1158
- ~MemUsingKey() {
1159
- delete data_;
1160
- net_allocations_--;
1161
- CHECK_GE(net_allocations_, 0);
1162
- }
1163
- MemUsingKey& operator=(const MemUsingKey& that) {
1164
- delete data_;
1165
- data_ = new int(*that.data_);
1166
- return *this;
1167
- }
1168
- struct Hash {
1169
- size_t operator()(const MemUsingKey& x) const { return *x.data_; }
1170
- };
1171
- struct Equal {
1172
- bool operator()(const MemUsingKey& x, const MemUsingKey& y) const {
1173
- return *x.data_ == *y.data_;
1174
- }
1175
- };
1176
- static int net_allocations() { return net_allocations_; }
1177
- private:
1178
- int* data_;
1179
- static int net_allocations_;
1180
- };
1181
-
1182
- class MemUsingValue {
1183
- public:
1184
- // This also tests that value does not need to have a zero-arg constructor
1185
- explicit MemUsingValue(const char* data) : data_(NULL) {
1186
- Strcpy(data);
1187
- }
1188
- MemUsingValue(const MemUsingValue& that) : data_(NULL) {
1189
- Strcpy(that.data_);
1190
- }
1191
- ~MemUsingValue() {
1192
- if (data_) {
1193
- free(data_);
1194
- net_allocations_--;
1195
- CHECK_GE(net_allocations_, 0);
1196
- }
1197
- }
1198
- MemUsingValue& operator=(const MemUsingValue& that) {
1199
- if (data_) {
1200
- free(data_);
1201
- net_allocations_--;
1202
- CHECK_GE(net_allocations_, 0);
1203
- }
1204
- Strcpy(that.data_);
1205
- return *this;
1206
- }
1207
- static int net_allocations() { return net_allocations_; }
1208
- private:
1209
- void Strcpy(const char* data) {
1210
- if (data) {
1211
- data_ = (char*)malloc(strlen(data) + 1); // use malloc this time
1212
- strcpy(data_, data); // strdup isn't so portable
1213
- net_allocations_++;
1214
- } else {
1215
- data_ = NULL;
1216
- }
1217
- }
1218
- char* data_;
1219
- static int net_allocations_;
1220
- };
1221
-
1222
- // TODO(csilvers): nix this when set_empty_key doesn't require zero-arg value
1223
- class MemUsingValueWithZeroArgConstructor : public MemUsingValue {
1224
- public:
1225
- MemUsingValueWithZeroArgConstructor(const char* data=NULL)
1226
- : MemUsingValue(data) { }
1227
- MemUsingValueWithZeroArgConstructor(
1228
- const MemUsingValueWithZeroArgConstructor& that)
1229
- : MemUsingValue(that) { }
1230
- MemUsingValueWithZeroArgConstructor& operator=(
1231
- const MemUsingValueWithZeroArgConstructor& that) {
1232
- *static_cast<MemUsingValue*>(this)
1233
- = *static_cast<const MemUsingValue*>(&that);
1234
- return *this;
1235
- }
1236
- };
1237
-
1238
- int MemUsingKey::net_allocations_ = 0;
1239
- int MemUsingValue::net_allocations_ = 0;
1240
-
1241
-
1242
- void TestMemoryManagement() {
1243
- MemUsingKey deleted_key(-1);
1244
- MemUsingKey empty_key(-2);
1245
-
1246
- {
1247
- // TODO(csilvers): fix sparsetable to allow missing zero-arg value ctor
1248
- sparse_hash_map<MemUsingKey, MemUsingValueWithZeroArgConstructor,
1249
- MemUsingKey::Hash, MemUsingKey::Equal> ht;
1250
- ht.set_deleted_key(deleted_key);
1251
- for (int i = 0; i < 1000; i++) {
1252
- ht.insert(pair<MemUsingKey,MemUsingValueWithZeroArgConstructor>(
1253
- i, MemUsingValueWithZeroArgConstructor("hello!")));
1254
- ht.erase(i);
1255
- CHECK_EQ(0, MemUsingValue::net_allocations());
1256
- }
1257
- }
1258
- // Various copies of deleted_key will be hanging around until the
1259
- // hashtable is destroyed, so it's only safe to do this test now.
1260
- CHECK_EQ(2, MemUsingKey::net_allocations()); // for deleted+empty_key
1261
-
1262
- {
1263
- dense_hash_map<MemUsingKey, MemUsingValueWithZeroArgConstructor,
1264
- MemUsingKey::Hash, MemUsingKey::Equal> ht;
1265
- ht.set_empty_key(empty_key);
1266
- ht.set_deleted_key(deleted_key);
1267
- for (int i = 0; i < 1000; i++) {
1268
- // As long as we have a zero-arg constructor for the value anyway,
1269
- // use operator[] rather than the more verbose insert().
1270
- ht[i] = MemUsingValueWithZeroArgConstructor("hello!");
1271
- ht.erase(i);
1272
- CHECK_EQ(0, MemUsingValue::net_allocations());
1273
- }
1274
- }
1275
- CHECK_EQ(2, MemUsingKey::net_allocations()); // for deleted+empty_key
1276
- }
1277
-
1278
- template<class Key>
1279
- struct SetKey {
1280
- void operator()(Key* key, const Key& new_key) const {
1281
- *key = new_key;
1282
- }
1283
- };
1284
-
1285
- int main(int argc, char **argv) {
1286
- TestOperatorEquals();
1287
-
1288
- // SPARSEHASH_HASH is defined in sparseconfig.h. It resolves to the
1289
- // system hash function (usually, but not always, named "hash") on
1290
- // whatever system we're on.
1291
-
1292
- // First try with the low-level hashtable interface
1293
- LOGF << "\n\nTEST WITH DENSE_HASHTABLE\n\n";
1294
- test<dense_hashtable<char *, char *, CharStarHash,
1295
- Identity<char *>, SetKey<char *>, strcmp_fnc,
1296
- allocator<char *> >,
1297
- dense_hashtable<string, string, StrHash,
1298
- Identity<string>, SetKey<string>,
1299
- equal_to<string>,
1300
- allocator<string> >,
1301
- dense_hashtable<int, int, SPARSEHASH_HASH<int>,
1302
- Identity<int>, SetKey<int>, equal_to<int>,
1303
- allocator<int> > >(
1304
- false);
1305
-
1306
- // Now try with hash_set, which should be equivalent
1307
- LOGF << "\n\nTEST WITH DENSE_HASH_SET\n\n";
1308
- test<dense_hash_set<char *, CharStarHash, strcmp_fnc>,
1309
- dense_hash_set<string, StrHash>,
1310
- dense_hash_set<int> >(false);
1311
-
1312
- TestResizingParameters<dense_hash_set<int>, true>(); // use tr1 API
1313
- TestResizingParameters<dense_hash_set<int>, false>(); // use older API
1314
-
1315
- // Now try with hash_map, which differs only in insert()
1316
- LOGF << "\n\nTEST WITH DENSE_HASH_MAP\n\n";
1317
- test<dense_hash_map<char *, int, CharStarHash, strcmp_fnc>,
1318
- dense_hash_map<string, int, StrHash>,
1319
- dense_hash_map<int, int> >(false);
1320
-
1321
- // First try with the low-level hashtable interface
1322
- LOGF << "\n\nTEST WITH SPARSE_HASHTABLE\n\n";
1323
- test<sparse_hashtable<char *, char *, CharStarHash,
1324
- Identity<char *>, SetKey<char *>, strcmp_fnc,
1325
- allocator<char *> >,
1326
- sparse_hashtable<string, string, StrHash,
1327
- Identity<string>, SetKey<string>, equal_to<string>,
1328
- allocator<string> >,
1329
- sparse_hashtable<int, int, SPARSEHASH_HASH<int>,
1330
- Identity<int>, SetKey<int>, equal_to<int>,
1331
- allocator<int> > >(
1332
- true);
1333
-
1334
- // Now try with hash_set, which should be equivalent
1335
- LOGF << "\n\nTEST WITH SPARSE_HASH_SET\n\n";
1336
- test<sparse_hash_set<char *, CharStarHash, strcmp_fnc>,
1337
- sparse_hash_set<string, StrHash>,
1338
- sparse_hash_set<int> >(true);
1339
-
1340
- TestResizingParameters<sparse_hash_set<int>, true>();
1341
- TestResizingParameters<sparse_hash_set<int>, false>();
1342
-
1343
- // Now try with hash_map, which differs only in insert()
1344
- LOGF << "\n\nTEST WITH SPARSE_HASH_MAP\n\n";
1345
- test<sparse_hash_map<char *, int, CharStarHash, strcmp_fnc>,
1346
- sparse_hash_map<string, int, StrHash>,
1347
- sparse_hash_map<int, int> >(true);
1348
-
1349
- // Test that we use the optimized routines for simple data types
1350
- LOGF << "\n\nTesting simple-data-type optimizations\n";
1351
- TestSimpleDataTypeOptimizations();
1352
-
1353
- // Test shrinking to very small sizes
1354
- LOGF << "\n\nTesting shrinking behavior";
1355
- TestShrinking();
1356
-
1357
- // Test that the hashers and key_equals are used properly in hash tables and
1358
- // hash maps.
1359
- LOGF << "\n\nTesting hashers and key_equals\n";
1360
- TestHashes();
1361
- TestMaps();
1362
-
1363
- LOGF << "\n\nTesting tr1 API\n";
1364
- TestTR1API<sparse_hash_map<int, int> >();
1365
- TestTR1API<dense_hash_map<int, int> >();
1366
- TestTR1API<sparse_hash_set<int> >();
1367
- TestTR1API<dense_hash_set<int> >();
1368
-
1369
- // Test memory management when the keys and values are non-trivial
1370
- LOGF << "\n\nTesting memory management\n";
1371
- TestMemoryManagement();
1372
-
1373
- LOGF << "\nAll tests pass.\n";
1374
- return 0;
1375
- }