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.
- data/README +61 -27
- data/Rakefile +4 -1
- data/TODO +5 -0
- data/VERSION +1 -1
- data/changelog +3 -0
- data/ext/extconf.rb +10 -5
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/AUTHORS +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/COPYING +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/ChangeLog +47 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/INSTALL +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/Makefile.am +29 -14
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/Makefile.in +77 -42
- data/ext/sparsehash-1.8.1/NEWS +71 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/README +0 -0
- data/ext/{sparsehash-1.5.2/README.windows → sparsehash-1.8.1/README_windows.txt} +25 -25
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/TODO +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/aclocal.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/compile +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/config.guess +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/config.sub +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/configure +3690 -4560
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/configure.ac +1 -1
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/depcomp +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/dense_hash_map.html +65 -5
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/dense_hash_set.html +65 -5
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/designstyle.css +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/implementation.html +11 -5
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/index.html +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/performance.html +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/sparse_hash_map.html +65 -5
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/sparse_hash_set.html +65 -5
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/doc/sparsetable.html +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/Makefile +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/README +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/example.c +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/libchash.c +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/experimental/libchash.h +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/google-sparsehash.sln +17 -1
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/install-sh +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/acx_pthread.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/google_namespace.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/namespaces.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/stl_hash.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/stl_hash_fun.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/m4/stl_namespace.m4 +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/missing +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/mkinstalldirs +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb.sh +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/README +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/changelog +24 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/compat +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/control +1 -1
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/copyright +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/docs +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/rules +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/sparsehash.dirs +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/deb/sparsehash.install +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/rpm.sh +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/packages/rpm/rpm.spec +1 -1
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/config.h.in +3 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/config.h.include +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/dense_hash_map +43 -27
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/dense_hash_set +40 -19
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparse_hash_map +32 -23
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparse_hash_set +31 -21
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparsehash/densehashtable.h +481 -298
- data/ext/sparsehash-1.8.1/src/google/sparsehash/hashtable-common.h +178 -0
- data/ext/sparsehash-1.8.1/src/google/sparsehash/libc_allocator_with_realloc.h +121 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparsehash/sparsehashtable.h +404 -233
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/sparsetable +173 -83
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/google/type_traits.h +3 -29
- data/ext/sparsehash-1.8.1/src/hash_test_interface.h +1011 -0
- data/ext/sparsehash-1.8.1/src/hashtable_test.cc +1733 -0
- data/ext/sparsehash-1.8.1/src/libc_allocator_with_realloc_test.cc +129 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/simple_test.cc +1 -1
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/sparsetable_unittest.cc +202 -6
- data/ext/sparsehash-1.8.1/src/testutil.h +251 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/time_hash_map.cc +128 -54
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/type_traits_unittest.cc +30 -20
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/config.h +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/google/sparsehash/sparseconfig.h +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/port.cc +0 -0
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/src/windows/port.h +0 -0
- data/ext/sparsehash-1.8.1/vsprojects/hashtable_test/hashtable_test.vcproj +197 -0
- data/ext/{sparsehash-1.5.2/vsprojects/hashtable_unittest/hashtable_unittest.vcproj → sparsehash-1.8.1/vsprojects/simple_test/simple_test.vcproj} +9 -8
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/vsprojects/sparsetable_unittest/sparsetable_unittest.vcproj +0 -2
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/vsprojects/time_hash_map/time_hash_map.vcproj +3 -2
- data/ext/{sparsehash-1.5.2 → sparsehash-1.8.1}/vsprojects/type_traits_unittest/type_traits_unittest.vcproj +0 -2
- data/ext/template/google_hash.cpp.erb +2 -1
- data/ext/template/main.cpp.erb +1 -1
- data/results.txt +6 -22
- data/spec/benchmark.rb +57 -0
- data/spec/spec.google_hash.rb +1 -8
- metadata +140 -130
- data/ext/benchmark.rb +0 -47
- data/ext/sparsehash-1.5.2/NEWS +0 -0
- data/ext/sparsehash-1.5.2/src/hashtable_unittest.cc +0 -1375
- data/ext/sparsehash-1.5.2/src/words +0 -8944
- data/types.txt +0 -18
|
@@ -0,0 +1,1733 @@
|
|
|
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: Craig Silverstein
|
|
32
|
+
//
|
|
33
|
+
// This tests common/densehashtable.h
|
|
34
|
+
// This tests common/dense_hash_set.h
|
|
35
|
+
// This tests common/dense_hash_map.h
|
|
36
|
+
// This tests common/sparsehashtable.h
|
|
37
|
+
// This tests common/sparse_hash_set.h
|
|
38
|
+
// This tests common/sparse_hash_map.h
|
|
39
|
+
//
|
|
40
|
+
// This test replacess hashtable_unittest.cc, which was becoming
|
|
41
|
+
// unreadable.
|
|
42
|
+
//
|
|
43
|
+
// Note that since all these classes are templatized, it's important
|
|
44
|
+
// to call every public method on the class: not just to make sure
|
|
45
|
+
// they work, but to make sure they even compile.
|
|
46
|
+
|
|
47
|
+
#ifdef _MSC_VER
|
|
48
|
+
// Below, we purposefully test having a very small allocator size.
|
|
49
|
+
// This causes some "type conversion too small" errors when using this
|
|
50
|
+
// allocator with sparsetable buckets. We're testing to make sure we
|
|
51
|
+
// handle that situation ok, so we don't need the compiler warnings.
|
|
52
|
+
#pragma warning(disable:4244)
|
|
53
|
+
#endif
|
|
54
|
+
|
|
55
|
+
#include "config.h"
|
|
56
|
+
#include <math.h>
|
|
57
|
+
#include <stdlib.h>
|
|
58
|
+
#include <string.h>
|
|
59
|
+
#include <iostream>
|
|
60
|
+
#include <set>
|
|
61
|
+
#include <vector>
|
|
62
|
+
#include <google/type_traits.h>
|
|
63
|
+
#include <google/sparsetable>
|
|
64
|
+
#include "hash_test_interface.h"
|
|
65
|
+
#include "testutil.h"
|
|
66
|
+
|
|
67
|
+
using GOOGLE_NAMESPACE::sparsetable;
|
|
68
|
+
using GOOGLE_NAMESPACE::sparse_hash_map;
|
|
69
|
+
using GOOGLE_NAMESPACE::sparse_hash_set;
|
|
70
|
+
using GOOGLE_NAMESPACE::dense_hash_map;
|
|
71
|
+
using GOOGLE_NAMESPACE::dense_hash_set;
|
|
72
|
+
using GOOGLE_NAMESPACE::HashtableInterface_SparseHashMap;
|
|
73
|
+
using GOOGLE_NAMESPACE::HashtableInterface_SparseHashSet;
|
|
74
|
+
using GOOGLE_NAMESPACE::HashtableInterface_SparseHashtable;
|
|
75
|
+
using GOOGLE_NAMESPACE::HashtableInterface_DenseHashMap;
|
|
76
|
+
using GOOGLE_NAMESPACE::HashtableInterface_DenseHashSet;
|
|
77
|
+
using GOOGLE_NAMESPACE::HashtableInterface_DenseHashtable;
|
|
78
|
+
namespace testing = GOOGLE_NAMESPACE::testing;
|
|
79
|
+
using STL_NAMESPACE::cout;
|
|
80
|
+
using STL_NAMESPACE::pair;
|
|
81
|
+
using STL_NAMESPACE::set;
|
|
82
|
+
using STL_NAMESPACE::string;
|
|
83
|
+
using STL_NAMESPACE::vector;
|
|
84
|
+
|
|
85
|
+
namespace {
|
|
86
|
+
|
|
87
|
+
#ifndef _MSC_VER // windows defines its own version
|
|
88
|
+
static string TmpFile(const char* basename) {
|
|
89
|
+
return string("/tmp/") + basename;
|
|
90
|
+
}
|
|
91
|
+
#endif
|
|
92
|
+
|
|
93
|
+
typedef unsigned char uint8;
|
|
94
|
+
|
|
95
|
+
// Used as a value in some of the hashtable tests. It's just some
|
|
96
|
+
// arbitrary user-defined type with non-trivial memory management.
|
|
97
|
+
struct ValueType {
|
|
98
|
+
public:
|
|
99
|
+
ValueType() : s_(kDefault) { }
|
|
100
|
+
ValueType(const char* init_s) : s_(kDefault) { set_s(init_s); }
|
|
101
|
+
~ValueType() { set_s(NULL); }
|
|
102
|
+
ValueType(const ValueType& that) : s_(kDefault) { operator=(that); }
|
|
103
|
+
void operator=(const ValueType& that) { set_s(that.s_); }
|
|
104
|
+
bool operator==(const ValueType& that) const {
|
|
105
|
+
return strcmp(this->s(), that.s()) == 0;
|
|
106
|
+
}
|
|
107
|
+
void set_s(const char* new_s) {
|
|
108
|
+
if (s_ != kDefault)
|
|
109
|
+
free(const_cast<char*>(s_));
|
|
110
|
+
s_ = (new_s == NULL ? kDefault : reinterpret_cast<char*>(strdup(new_s)));
|
|
111
|
+
}
|
|
112
|
+
const char* s() const { return s_; }
|
|
113
|
+
private:
|
|
114
|
+
const char* s_;
|
|
115
|
+
static const char* const kDefault;
|
|
116
|
+
};
|
|
117
|
+
const char* const ValueType::kDefault = "hi";
|
|
118
|
+
|
|
119
|
+
// This is used by the low-level sparse/dense_hashtable classes,
|
|
120
|
+
// which support the most general relationship between keys and
|
|
121
|
+
// values: the key is derived from the value through some arbitrary
|
|
122
|
+
// function. (For classes like sparse_hash_map, the 'value' is a
|
|
123
|
+
// key/data pair, and the function to derive the key is
|
|
124
|
+
// FirstElementOfPair.) KeyToValue is the inverse of this function,
|
|
125
|
+
// so GetKey(KeyToValue(key)) == key. To keep the tests a bit
|
|
126
|
+
// simpler, we've chosen to make the key and value actually be the
|
|
127
|
+
// same type, which is why we need only one template argument for the
|
|
128
|
+
// types, rather than two (one for the key and one for the value).
|
|
129
|
+
template<class KeyAndValueT, class KeyToValue>
|
|
130
|
+
struct SetKey {
|
|
131
|
+
void operator()(KeyAndValueT* value, const KeyAndValueT& new_key) const {
|
|
132
|
+
*value = KeyToValue()(new_key);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// A hash function that keeps track of how often it's called. We use
|
|
137
|
+
// a simple djb-hash so we don't depend on how STL hashes. We use
|
|
138
|
+
// this same method to do the key-comparison, so we can keep track
|
|
139
|
+
// of comparison-counts too.
|
|
140
|
+
struct Hasher {
|
|
141
|
+
explicit Hasher(int i=0) : id_(i), num_hashes_(0), num_compares_(0) { }
|
|
142
|
+
int id() const { return id_; }
|
|
143
|
+
int num_hashes() const { return num_hashes_; }
|
|
144
|
+
int num_compares() const { return num_compares_; }
|
|
145
|
+
|
|
146
|
+
size_t operator()(int a) const {
|
|
147
|
+
num_hashes_++;
|
|
148
|
+
return static_cast<size_t>(a);
|
|
149
|
+
}
|
|
150
|
+
size_t operator()(const char* a) const {
|
|
151
|
+
num_hashes_++;
|
|
152
|
+
size_t hash = 0;
|
|
153
|
+
for (size_t i = 0; a[i]; i++ )
|
|
154
|
+
hash = 33 * hash + a[i];
|
|
155
|
+
return hash;
|
|
156
|
+
}
|
|
157
|
+
size_t operator()(const string& a) const {
|
|
158
|
+
num_hashes_++;
|
|
159
|
+
size_t hash = 0;
|
|
160
|
+
for (size_t i = 0; i < a.length(); i++ )
|
|
161
|
+
hash = 33 * hash + a[i];
|
|
162
|
+
return hash;
|
|
163
|
+
}
|
|
164
|
+
bool operator()(int a, int b) const {
|
|
165
|
+
num_compares_++;
|
|
166
|
+
return a == b;
|
|
167
|
+
}
|
|
168
|
+
bool operator()(const string& a, const string& b) const {
|
|
169
|
+
num_compares_++;
|
|
170
|
+
return a == b;
|
|
171
|
+
}
|
|
172
|
+
bool operator()(const char* a, const char* b) const {
|
|
173
|
+
num_compares_++;
|
|
174
|
+
// The 'a == b' test is necessary, in case a and b are both NULL.
|
|
175
|
+
return (a == b || (a && b && strcmp(a, b) == 0));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private:
|
|
179
|
+
mutable int id_;
|
|
180
|
+
mutable int num_hashes_;
|
|
181
|
+
mutable int num_compares_;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Allocator that allows controlling its size in various ways, to test
|
|
185
|
+
// allocator overflow. Because we use this allocator in a vector, we
|
|
186
|
+
// need to define != and swap for gcc.
|
|
187
|
+
template<typename T,
|
|
188
|
+
typename SizeT = size_t, SizeT MAX_SIZE = static_cast<SizeT>(~0)>
|
|
189
|
+
struct Alloc {
|
|
190
|
+
typedef T value_type;
|
|
191
|
+
typedef SizeT size_type;
|
|
192
|
+
typedef ptrdiff_t difference_type;
|
|
193
|
+
typedef T* pointer;
|
|
194
|
+
typedef const T* const_pointer;
|
|
195
|
+
typedef T& reference;
|
|
196
|
+
typedef const T& const_reference;
|
|
197
|
+
|
|
198
|
+
explicit Alloc(int i=0, int* count=NULL) : id_(i), count_(count) {}
|
|
199
|
+
~Alloc() {}
|
|
200
|
+
pointer address(reference r) const { return &r; }
|
|
201
|
+
const_pointer address(const_reference r) const { return &r; }
|
|
202
|
+
pointer allocate(size_type n, const_pointer = 0) {
|
|
203
|
+
if (count_) ++(*count_);
|
|
204
|
+
return static_cast<pointer>(malloc(n * sizeof(value_type)));
|
|
205
|
+
}
|
|
206
|
+
void deallocate(pointer p, size_type) {
|
|
207
|
+
free(p);
|
|
208
|
+
}
|
|
209
|
+
pointer reallocate(pointer p, size_type n) {
|
|
210
|
+
if (count_) ++(*count_);
|
|
211
|
+
return static_cast<pointer>(realloc(p, n * sizeof(value_type)));
|
|
212
|
+
}
|
|
213
|
+
size_type max_size() const {
|
|
214
|
+
return static_cast<size_type>(MAX_SIZE);
|
|
215
|
+
}
|
|
216
|
+
void construct(pointer p, const value_type& val) {
|
|
217
|
+
new(p) value_type(val);
|
|
218
|
+
}
|
|
219
|
+
void destroy(pointer p) { p->~value_type(); }
|
|
220
|
+
|
|
221
|
+
bool is_custom_alloc() const { return true; }
|
|
222
|
+
|
|
223
|
+
template <class U>
|
|
224
|
+
Alloc(const Alloc<U, SizeT, MAX_SIZE>& that)
|
|
225
|
+
: id_(that.id_), count_(that.count_) {
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
template <class U>
|
|
229
|
+
struct rebind {
|
|
230
|
+
typedef Alloc<U, SizeT, MAX_SIZE> other;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
bool operator==(const Alloc<T,SizeT,MAX_SIZE>& that) {
|
|
234
|
+
return this->id_ == that.id_ && this->count_ == that.count_;
|
|
235
|
+
}
|
|
236
|
+
bool operator!=(const Alloc<T,SizeT,MAX_SIZE>& that) {
|
|
237
|
+
return !this->operator==(that);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
int id() const { return id_; }
|
|
241
|
+
|
|
242
|
+
// I have to make these public so the constructor used for rebinding
|
|
243
|
+
// can see them. Normally, I'd just make them private and say:
|
|
244
|
+
// template<typename U, typename U_SizeT, U_SizeT U_MAX_SIZE> friend struct Alloc;
|
|
245
|
+
// but MSVC 7.1 barfs on that. So public it is. But no peeking!
|
|
246
|
+
public:
|
|
247
|
+
int id_;
|
|
248
|
+
int* count_;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
// Below are a few fun routines that convert a value into a key, used
|
|
253
|
+
// for dense_hashtable and sparse_hashtable. It's our responsibility
|
|
254
|
+
// to make sure, when we insert values into these objects, that the
|
|
255
|
+
// values match the keys we insert them under. To allow us to use
|
|
256
|
+
// these routines for SetKey as well, we require all these functions
|
|
257
|
+
// be their own inverse: f(f(x)) == x.
|
|
258
|
+
template<class Value>
|
|
259
|
+
struct Negation {
|
|
260
|
+
Value operator()(Value& v) { return -v; }
|
|
261
|
+
const Value operator()(const Value& v) const { return -v; }
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
struct Capital {
|
|
265
|
+
string operator()(string& s) {
|
|
266
|
+
return string(1, s[0] ^ 32) + s.substr(1);
|
|
267
|
+
}
|
|
268
|
+
const string operator()(const string& s) const {
|
|
269
|
+
return string(1, s[0] ^ 32) + s.substr(1);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
struct Identity { // lame, I know, but an important case to test.
|
|
274
|
+
const char* operator()(const char* s) const {
|
|
275
|
+
return s;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// This is just to avoid memory leaks -- it's a global pointer to
|
|
280
|
+
// all the memory allocated by UniqueObjectHelper. We'll use it
|
|
281
|
+
// to semi-test sparsetable as well. :-)
|
|
282
|
+
sparsetable<char*> g_unique_charstar_objects(16);
|
|
283
|
+
|
|
284
|
+
// This is an object-generator: pass in an index, and it will return a
|
|
285
|
+
// unique object of type ItemType. We provide specializations for the
|
|
286
|
+
// types we actually support.
|
|
287
|
+
template <typename ItemType> ItemType UniqueObjectHelper(int index);
|
|
288
|
+
template<> int UniqueObjectHelper(int index) {
|
|
289
|
+
return index;
|
|
290
|
+
}
|
|
291
|
+
template<> string UniqueObjectHelper(int index) {
|
|
292
|
+
char buffer[64];
|
|
293
|
+
snprintf(buffer, sizeof(buffer), "%d", index);
|
|
294
|
+
return buffer;
|
|
295
|
+
}
|
|
296
|
+
template<> char* UniqueObjectHelper(int index) {
|
|
297
|
+
// First grow the table if need be.
|
|
298
|
+
sparsetable<char*>::size_type table_size = g_unique_charstar_objects.size();
|
|
299
|
+
while (index >= static_cast<int>(table_size)) {
|
|
300
|
+
assert(table_size * 2 > table_size); // avoid overflow problems
|
|
301
|
+
table_size *= 2;
|
|
302
|
+
}
|
|
303
|
+
if (table_size > g_unique_charstar_objects.size())
|
|
304
|
+
g_unique_charstar_objects.resize(table_size);
|
|
305
|
+
|
|
306
|
+
if (!g_unique_charstar_objects.test(index)) {
|
|
307
|
+
char buffer[64];
|
|
308
|
+
snprintf(buffer, sizeof(buffer), "%d", index);
|
|
309
|
+
g_unique_charstar_objects[index] = strdup(buffer);
|
|
310
|
+
}
|
|
311
|
+
return g_unique_charstar_objects.get(index);
|
|
312
|
+
}
|
|
313
|
+
template<> const char* UniqueObjectHelper(int index) {
|
|
314
|
+
return UniqueObjectHelper<char*>(index);
|
|
315
|
+
}
|
|
316
|
+
template<> ValueType UniqueObjectHelper(int index) {
|
|
317
|
+
return ValueType(UniqueObjectHelper<string>(index).c_str());
|
|
318
|
+
}
|
|
319
|
+
template<> pair<const int, int> UniqueObjectHelper(int index) {
|
|
320
|
+
return pair<const int,int>(index, index + 1);
|
|
321
|
+
}
|
|
322
|
+
template<> pair<const string, string> UniqueObjectHelper(int index) {
|
|
323
|
+
return pair<const string,string>(
|
|
324
|
+
UniqueObjectHelper<string>(index), UniqueObjectHelper<string>(index + 1));
|
|
325
|
+
}
|
|
326
|
+
template<> pair<const char* const,ValueType> UniqueObjectHelper(int index) {
|
|
327
|
+
return pair<const char* const,ValueType>(
|
|
328
|
+
UniqueObjectHelper<char*>(index), UniqueObjectHelper<ValueType>(index+1));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
template <typename HashtableType>
|
|
332
|
+
class HashtableTest {
|
|
333
|
+
public:
|
|
334
|
+
HashtableTest() : ht_() { }
|
|
335
|
+
// Give syntactically-prettier access to UniqueObjectHelper.
|
|
336
|
+
typename HashtableType::value_type UniqueObject(int index) {
|
|
337
|
+
return UniqueObjectHelper<typename HashtableType::value_type>(index);
|
|
338
|
+
}
|
|
339
|
+
typename HashtableType::key_type UniqueKey(int index) {
|
|
340
|
+
return this->ht_.get_key(this->UniqueObject(index));
|
|
341
|
+
}
|
|
342
|
+
protected:
|
|
343
|
+
HashtableType ht_;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// These are used to specify the empty key and deleted key in some
|
|
349
|
+
// contexts. They can't be in the unnamed namespace, or static,
|
|
350
|
+
// because the template code requires external linkage.
|
|
351
|
+
extern const string kEmptyString("--empty string--");
|
|
352
|
+
extern const string kDeletedString("--deleted string--");
|
|
353
|
+
extern const int kEmptyInt = 0;
|
|
354
|
+
extern const int kDeletedInt = -1234676543; // an unlikely-to-pick int
|
|
355
|
+
extern const char* const kEmptyCharStar = "--empty char*--";
|
|
356
|
+
extern const char* const kDeletedCharStar = "--deleted char*--";
|
|
357
|
+
|
|
358
|
+
namespace {
|
|
359
|
+
|
|
360
|
+
#define INT_HASHTABLES \
|
|
361
|
+
HashtableInterface_SparseHashMap<int, int, Hasher, Hasher, \
|
|
362
|
+
Alloc<int> >, \
|
|
363
|
+
HashtableInterface_SparseHashSet<int, Hasher, Hasher, \
|
|
364
|
+
Alloc<int> >, \
|
|
365
|
+
/* This is a table where the key associated with a value is -value */ \
|
|
366
|
+
HashtableInterface_SparseHashtable<int, int, Hasher, Negation<int>, \
|
|
367
|
+
SetKey<int, Negation<int> >, \
|
|
368
|
+
Hasher, Alloc<int> >, \
|
|
369
|
+
HashtableInterface_DenseHashMap<int, int, kEmptyInt, Hasher, Hasher, \
|
|
370
|
+
Alloc<int> >, \
|
|
371
|
+
HashtableInterface_DenseHashSet<int, kEmptyInt, Hasher, Hasher, \
|
|
372
|
+
Alloc<int> >, \
|
|
373
|
+
HashtableInterface_DenseHashtable<int, int, kEmptyInt, \
|
|
374
|
+
Hasher, Negation<int>, \
|
|
375
|
+
SetKey<int, Negation<int> >, \
|
|
376
|
+
Hasher, Alloc<int> >
|
|
377
|
+
|
|
378
|
+
#define STRING_HASHTABLES \
|
|
379
|
+
HashtableInterface_SparseHashMap<string, string, Hasher, Hasher, \
|
|
380
|
+
Alloc<string> >, \
|
|
381
|
+
HashtableInterface_SparseHashSet<string, Hasher, Hasher, \
|
|
382
|
+
Alloc<string> >, \
|
|
383
|
+
/* This is a table where the key associated with a value is Cap(value) */ \
|
|
384
|
+
HashtableInterface_SparseHashtable<string, string, Hasher, Capital, \
|
|
385
|
+
SetKey<string, Capital>, \
|
|
386
|
+
Hasher, Alloc<string> >, \
|
|
387
|
+
HashtableInterface_DenseHashMap<string, string, kEmptyString, \
|
|
388
|
+
Hasher, Hasher, Alloc<string> >, \
|
|
389
|
+
HashtableInterface_DenseHashSet<string, kEmptyString, Hasher, Hasher, \
|
|
390
|
+
Alloc<string> >, \
|
|
391
|
+
HashtableInterface_DenseHashtable<string, string, kEmptyString, \
|
|
392
|
+
Hasher, Capital, \
|
|
393
|
+
SetKey<string, Capital>, \
|
|
394
|
+
Hasher, Alloc<string> >
|
|
395
|
+
|
|
396
|
+
// I'd like to use ValueType keys for SparseHashtable<> and
|
|
397
|
+
// DenseHashtable<> but I can't due to memory-management woes (nobody
|
|
398
|
+
// really owns the char* involved). So instead I do something simpler.
|
|
399
|
+
#define CHARSTAR_HASHTABLES \
|
|
400
|
+
HashtableInterface_SparseHashMap<const char*, ValueType, \
|
|
401
|
+
Hasher, Hasher, Alloc<const char*> >, \
|
|
402
|
+
HashtableInterface_SparseHashSet<const char*, Hasher, Hasher, \
|
|
403
|
+
Alloc<const char*> >, \
|
|
404
|
+
/* This is a table where each value is its own key. */ \
|
|
405
|
+
HashtableInterface_SparseHashtable<const char*, const char*, \
|
|
406
|
+
Hasher, Identity, \
|
|
407
|
+
SetKey<const char*, Identity>, \
|
|
408
|
+
Hasher, Alloc<const char*> >, \
|
|
409
|
+
HashtableInterface_DenseHashMap<const char*, ValueType, kEmptyCharStar, \
|
|
410
|
+
Hasher, Hasher, Alloc<const char*> >, \
|
|
411
|
+
HashtableInterface_DenseHashSet<const char*, kEmptyCharStar, \
|
|
412
|
+
Hasher, Hasher, Alloc<const char*> >, \
|
|
413
|
+
HashtableInterface_DenseHashtable<const char*, const char*, kEmptyCharStar, \
|
|
414
|
+
Hasher, Identity, \
|
|
415
|
+
SetKey<const char*, Identity>, \
|
|
416
|
+
Hasher, Alloc<ValueType> >
|
|
417
|
+
|
|
418
|
+
// This is the list of types we run each test against.
|
|
419
|
+
// We need to define the same class 4 times due to limitations in the
|
|
420
|
+
// testing framework. Basically, we associate each class below with
|
|
421
|
+
// the set of types we want to run tests on it with.
|
|
422
|
+
typedef testing::TypeList6<INT_HASHTABLES> IntHashtables;
|
|
423
|
+
template <typename HashtableType> class HashtableIntTest
|
|
424
|
+
: public HashtableTest<HashtableType> { };
|
|
425
|
+
TYPED_TEST_CASE_6(HashtableIntTest, IntHashtables);
|
|
426
|
+
|
|
427
|
+
typedef testing::TypeList6<STRING_HASHTABLES> StringHashtables;
|
|
428
|
+
template <typename HashtableType> class HashtableStringTest
|
|
429
|
+
: public HashtableTest<HashtableType> { };
|
|
430
|
+
TYPED_TEST_CASE_6(HashtableStringTest, StringHashtables);
|
|
431
|
+
|
|
432
|
+
typedef testing::TypeList6<CHARSTAR_HASHTABLES> CharStarHashtables;
|
|
433
|
+
template <typename HashtableType> class HashtableCharStarTest
|
|
434
|
+
: public HashtableTest<HashtableType> { };
|
|
435
|
+
TYPED_TEST_CASE_6(HashtableCharStarTest, CharStarHashtables);
|
|
436
|
+
|
|
437
|
+
typedef testing::TypeList18<INT_HASHTABLES, STRING_HASHTABLES,
|
|
438
|
+
CHARSTAR_HASHTABLES> AllHashtables;
|
|
439
|
+
template <typename HashtableType> class HashtableAllTest
|
|
440
|
+
: public HashtableTest<HashtableType> { };
|
|
441
|
+
TYPED_TEST_CASE_18(HashtableAllTest, AllHashtables);
|
|
442
|
+
|
|
443
|
+
// ------------------------------------------------------------------------
|
|
444
|
+
// If the first arg to TYPED_TEST is HashtableIntTest, it will run
|
|
445
|
+
// this test on all the hashtable types, with key=int and value=int.
|
|
446
|
+
// Likewise, HashtableStringTest will have string key/values, and
|
|
447
|
+
// HashtableCharStarTest will have char* keys and -- just to mix it up
|
|
448
|
+
// a little -- ValueType values. HashtableAllTest will run all three
|
|
449
|
+
// key/value types on all 6 hashtables types, for 18 test-runs total
|
|
450
|
+
// per test.
|
|
451
|
+
//
|
|
452
|
+
// In addition, TYPED_TEST makes available the magic keyword
|
|
453
|
+
// TypeParam, which is the type being used for the current test.
|
|
454
|
+
|
|
455
|
+
// This first set of tests just tests the public API, going through
|
|
456
|
+
// the public typedefs and methods in turn. It goes approximately
|
|
457
|
+
// in the definition-order in sparse_hash_map.h.
|
|
458
|
+
|
|
459
|
+
TYPED_TEST(HashtableIntTest, Typedefs) {
|
|
460
|
+
// Make sure all the standard STL-y typedefs are defined. The exact
|
|
461
|
+
// key/value types don't matter here, so we only bother testing on
|
|
462
|
+
// the int tables. This is just a compile-time "test"; nothing here
|
|
463
|
+
// can fail at runtime.
|
|
464
|
+
this->ht_.set_deleted_key(-2); // just so deleted_key succeeds
|
|
465
|
+
typename TypeParam::key_type kt;
|
|
466
|
+
typename TypeParam::value_type vt;
|
|
467
|
+
typename TypeParam::hasher h;
|
|
468
|
+
typename TypeParam::key_equal ke;
|
|
469
|
+
typename TypeParam::allocator_type at;
|
|
470
|
+
|
|
471
|
+
typename TypeParam::size_type st;
|
|
472
|
+
typename TypeParam::difference_type dt;
|
|
473
|
+
typename TypeParam::pointer p;
|
|
474
|
+
typename TypeParam::const_pointer cp;
|
|
475
|
+
// I can't declare variables of reference-type, since I have nothing
|
|
476
|
+
// to point them to, so I just make sure that these types exist.
|
|
477
|
+
typedef typename TypeParam::reference r;
|
|
478
|
+
typedef typename TypeParam::const_reference cf;
|
|
479
|
+
|
|
480
|
+
typename TypeParam::iterator i;
|
|
481
|
+
typename TypeParam::const_iterator ci;
|
|
482
|
+
typename TypeParam::local_iterator li;
|
|
483
|
+
typename TypeParam::const_local_iterator cli;
|
|
484
|
+
|
|
485
|
+
// Now make sure the variables are used, so the compiler doesn't
|
|
486
|
+
// complain. Where possible, I "use" the variable by calling the
|
|
487
|
+
// method that's supposed to return the unique instance of the
|
|
488
|
+
// relevant type (eg. get_allocator()). Otherwise, I try to call a
|
|
489
|
+
// different, arbitrary function that returns the type. Sometimes
|
|
490
|
+
// the type isn't used at all, and there's no good way to use the
|
|
491
|
+
// variable.
|
|
492
|
+
kt = this->ht_.deleted_key();
|
|
493
|
+
(void)vt; // value_type may not be copyable. Easiest not to try.
|
|
494
|
+
h = this->ht_.hash_funct();
|
|
495
|
+
ke = this->ht_.key_eq();
|
|
496
|
+
at = this->ht_.get_allocator();
|
|
497
|
+
st = this->ht_.size();
|
|
498
|
+
(void)dt;
|
|
499
|
+
(void)p;
|
|
500
|
+
(void)cp;
|
|
501
|
+
i = this->ht_.begin();
|
|
502
|
+
ci = this->ht_.begin();
|
|
503
|
+
li = this->ht_.begin(0);
|
|
504
|
+
cli = this->ht_.begin(0);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
TYPED_TEST(HashtableAllTest, NormalIterators) {
|
|
508
|
+
EXPECT_TRUE(this->ht_.begin() == this->ht_.end());
|
|
509
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
510
|
+
{
|
|
511
|
+
typename TypeParam::iterator it = this->ht_.begin();
|
|
512
|
+
EXPECT_TRUE(it != this->ht_.end());
|
|
513
|
+
++it;
|
|
514
|
+
EXPECT_TRUE(it == this->ht_.end());
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
TEST(HashtableTest, ModifyViaIterator) {
|
|
519
|
+
// This only works for hash-maps, since only they have non-const values.
|
|
520
|
+
{
|
|
521
|
+
sparse_hash_map<int, int> ht;
|
|
522
|
+
ht[1] = 2;
|
|
523
|
+
sparse_hash_map<int, int>::iterator it = ht.find(1);
|
|
524
|
+
EXPECT_TRUE(it != ht.end());
|
|
525
|
+
EXPECT_EQ(1, it->first);
|
|
526
|
+
EXPECT_EQ(2, it->second);
|
|
527
|
+
it->second = 5;
|
|
528
|
+
it = ht.find(1);
|
|
529
|
+
EXPECT_TRUE(it != ht.end());
|
|
530
|
+
EXPECT_EQ(5, it->second);
|
|
531
|
+
}
|
|
532
|
+
{
|
|
533
|
+
dense_hash_map<int, int> ht;
|
|
534
|
+
ht.set_empty_key(0);
|
|
535
|
+
ht[1] = 2;
|
|
536
|
+
dense_hash_map<int, int>::iterator it = ht.find(1);
|
|
537
|
+
EXPECT_TRUE(it != ht.end());
|
|
538
|
+
EXPECT_EQ(1, it->first);
|
|
539
|
+
EXPECT_EQ(2, it->second);
|
|
540
|
+
it->second = 5;
|
|
541
|
+
it = ht.find(1);
|
|
542
|
+
EXPECT_TRUE(it != ht.end());
|
|
543
|
+
EXPECT_EQ(5, it->second);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
TYPED_TEST(HashtableAllTest, ConstIterators) {
|
|
548
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
549
|
+
typename TypeParam::const_iterator it = this->ht_.begin();
|
|
550
|
+
EXPECT_TRUE(it != this->ht_.end());
|
|
551
|
+
++it;
|
|
552
|
+
EXPECT_TRUE(it == this->ht_.end());
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
TYPED_TEST(HashtableAllTest, LocalIterators) {
|
|
556
|
+
// Now, tr1 begin/end (the local iterator that takes a bucket-number).
|
|
557
|
+
// ht::bucket() returns the bucket that this key would be inserted in.
|
|
558
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
559
|
+
const typename TypeParam::size_type bucknum =
|
|
560
|
+
this->ht_.bucket(this->UniqueKey(1));
|
|
561
|
+
typename TypeParam::local_iterator b = this->ht_.begin(bucknum);
|
|
562
|
+
typename TypeParam::local_iterator e = this->ht_.end(bucknum);
|
|
563
|
+
EXPECT_TRUE(b != e);
|
|
564
|
+
b++;
|
|
565
|
+
EXPECT_TRUE(b == e);
|
|
566
|
+
|
|
567
|
+
// Check an empty bucket. We can just xor the bottom bit and be sure
|
|
568
|
+
// of getting a legal bucket, since #buckets is always a power of 2.
|
|
569
|
+
EXPECT_TRUE(this->ht_.begin(bucknum ^ 1) == this->ht_.end(bucknum ^ 1));
|
|
570
|
+
// Another test, this time making sure we're using the right types.
|
|
571
|
+
typename TypeParam::local_iterator b2 = this->ht_.begin(bucknum ^ 1);
|
|
572
|
+
typename TypeParam::local_iterator e2 = this->ht_.end(bucknum ^ 1);
|
|
573
|
+
EXPECT_TRUE(b2 == e2);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
TYPED_TEST(HashtableAllTest, ConstLocalIterators) {
|
|
577
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
578
|
+
const typename TypeParam::size_type bucknum =
|
|
579
|
+
this->ht_.bucket(this->UniqueKey(1));
|
|
580
|
+
typename TypeParam::const_local_iterator b = this->ht_.begin(bucknum);
|
|
581
|
+
typename TypeParam::const_local_iterator e = this->ht_.end(bucknum);
|
|
582
|
+
EXPECT_TRUE(b != e);
|
|
583
|
+
b++;
|
|
584
|
+
EXPECT_TRUE(b == e);
|
|
585
|
+
typename TypeParam::const_local_iterator b2 = this->ht_.begin(bucknum ^ 1);
|
|
586
|
+
typename TypeParam::const_local_iterator e2 = this->ht_.end(bucknum ^ 1);
|
|
587
|
+
EXPECT_TRUE(b2 == e2);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
TYPED_TEST(HashtableAllTest, Iterating) {
|
|
591
|
+
// Test a bit more iterating than just one ++.
|
|
592
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
593
|
+
this->ht_.insert(this->UniqueObject(11));
|
|
594
|
+
this->ht_.insert(this->UniqueObject(111));
|
|
595
|
+
this->ht_.insert(this->UniqueObject(1111));
|
|
596
|
+
this->ht_.insert(this->UniqueObject(11111));
|
|
597
|
+
this->ht_.insert(this->UniqueObject(111111));
|
|
598
|
+
this->ht_.insert(this->UniqueObject(1111111));
|
|
599
|
+
this->ht_.insert(this->UniqueObject(11111111));
|
|
600
|
+
this->ht_.insert(this->UniqueObject(111111111));
|
|
601
|
+
typename TypeParam::iterator it = this->ht_.begin();
|
|
602
|
+
for (int i = 1; i <= 9; i++) { // start at 1 so i is never 0
|
|
603
|
+
// && here makes it easier to tell what loop iteration the test failed on.
|
|
604
|
+
EXPECT_TRUE(i && (it++ != this->ht_.end()));
|
|
605
|
+
}
|
|
606
|
+
EXPECT_TRUE(it == this->ht_.end());
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
TYPED_TEST(HashtableIntTest, Constructors) {
|
|
610
|
+
// The key/value types don't matter here, so I just test on one set
|
|
611
|
+
// of tables, the ones with int keys, which can easily handle the
|
|
612
|
+
// placement-news we have to do below.
|
|
613
|
+
Hasher hasher(1); // 1 is a unique id
|
|
614
|
+
int alloc_count = 0;
|
|
615
|
+
Alloc<typename TypeParam::key_type> alloc(2, &alloc_count);
|
|
616
|
+
|
|
617
|
+
TypeParam ht_noarg;
|
|
618
|
+
TypeParam ht_onearg(100);
|
|
619
|
+
TypeParam ht_twoarg(100, hasher);
|
|
620
|
+
TypeParam ht_threearg(100, hasher, hasher); // hasher serves as key_equal too
|
|
621
|
+
TypeParam ht_fourarg(100, hasher, hasher, alloc);
|
|
622
|
+
|
|
623
|
+
// The allocator should have been called at most once, for the last ht.
|
|
624
|
+
EXPECT_LE(1, alloc_count);
|
|
625
|
+
int old_alloc_count = alloc_count;
|
|
626
|
+
|
|
627
|
+
vector<typename TypeParam::value_type> input(4);
|
|
628
|
+
// We have to use placement-new because value_type may be const.
|
|
629
|
+
new(&input[0]) typename TypeParam::value_type(this->UniqueObject(1));
|
|
630
|
+
new(&input[1]) typename TypeParam::value_type(this->UniqueObject(2));
|
|
631
|
+
new(&input[2]) typename TypeParam::value_type(this->UniqueObject(4));
|
|
632
|
+
new(&input[3]) typename TypeParam::value_type(this->UniqueObject(8));
|
|
633
|
+
TypeParam ht_iter_noarg(input.begin(), input.end());
|
|
634
|
+
TypeParam ht_iter_onearg(input.begin(), input.end(), 100);
|
|
635
|
+
TypeParam ht_iter_twoarg(input.begin(), input.end(), 100, hasher);
|
|
636
|
+
TypeParam ht_iter_threearg(input.begin(), input.end(), 100, hasher, hasher);
|
|
637
|
+
TypeParam ht_iter_fourarg(input.begin(), input.end(), 100, hasher, hasher,
|
|
638
|
+
alloc);
|
|
639
|
+
// Now the allocator should have been called more.
|
|
640
|
+
EXPECT_GT(alloc_count, old_alloc_count);
|
|
641
|
+
old_alloc_count = alloc_count;
|
|
642
|
+
|
|
643
|
+
// Let's do a lot more inserting and make sure the alloc-count goes up
|
|
644
|
+
for (int i = 2; i < 2000; i++)
|
|
645
|
+
ht_fourarg.insert(this->UniqueObject(i));
|
|
646
|
+
EXPECT_GT(alloc_count, old_alloc_count);
|
|
647
|
+
|
|
648
|
+
EXPECT_LT(ht_noarg.bucket_count(), 100u);
|
|
649
|
+
EXPECT_GE(ht_onearg.bucket_count(), 100u);
|
|
650
|
+
EXPECT_GE(ht_twoarg.bucket_count(), 100u);
|
|
651
|
+
EXPECT_GE(ht_threearg.bucket_count(), 100u);
|
|
652
|
+
EXPECT_GE(ht_fourarg.bucket_count(), 100u);
|
|
653
|
+
EXPECT_GE(ht_iter_onearg.bucket_count(), 100u);
|
|
654
|
+
|
|
655
|
+
// When we pass in a hasher -- it can serve both as the hash-function
|
|
656
|
+
// and the key-equal function -- its id should be 1. Where we don't
|
|
657
|
+
// pass it in and use the default Hasher object, the id should be 0.
|
|
658
|
+
EXPECT_EQ(0, ht_noarg.hash_funct().id());
|
|
659
|
+
EXPECT_EQ(0, ht_noarg.key_eq().id());
|
|
660
|
+
EXPECT_EQ(0, ht_onearg.hash_funct().id());
|
|
661
|
+
EXPECT_EQ(0, ht_onearg.key_eq().id());
|
|
662
|
+
EXPECT_EQ(1, ht_twoarg.hash_funct().id());
|
|
663
|
+
EXPECT_EQ(0, ht_twoarg.key_eq().id());
|
|
664
|
+
EXPECT_EQ(1, ht_threearg.hash_funct().id());
|
|
665
|
+
EXPECT_EQ(1, ht_threearg.key_eq().id());
|
|
666
|
+
|
|
667
|
+
EXPECT_EQ(0, ht_iter_noarg.hash_funct().id());
|
|
668
|
+
EXPECT_EQ(0, ht_iter_noarg.key_eq().id());
|
|
669
|
+
EXPECT_EQ(0, ht_iter_onearg.hash_funct().id());
|
|
670
|
+
EXPECT_EQ(0, ht_iter_onearg.key_eq().id());
|
|
671
|
+
EXPECT_EQ(1, ht_iter_twoarg.hash_funct().id());
|
|
672
|
+
EXPECT_EQ(0, ht_iter_twoarg.key_eq().id());
|
|
673
|
+
EXPECT_EQ(1, ht_iter_threearg.hash_funct().id());
|
|
674
|
+
EXPECT_EQ(1, ht_iter_threearg.key_eq().id());
|
|
675
|
+
|
|
676
|
+
// Likewise for the allocator
|
|
677
|
+
EXPECT_EQ(0, ht_threearg.get_allocator().id());
|
|
678
|
+
EXPECT_EQ(0, ht_iter_threearg.get_allocator().id());
|
|
679
|
+
EXPECT_EQ(2, ht_fourarg.get_allocator().id());
|
|
680
|
+
EXPECT_EQ(2, ht_iter_fourarg.get_allocator().id());
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
TYPED_TEST(HashtableAllTest, OperatorEquals) {
|
|
684
|
+
{
|
|
685
|
+
TypeParam ht1, ht2;
|
|
686
|
+
ht1.set_deleted_key(this->UniqueKey(1));
|
|
687
|
+
ht2.set_deleted_key(this->UniqueKey(2));
|
|
688
|
+
|
|
689
|
+
ht1.insert(this->UniqueObject(10));
|
|
690
|
+
ht2.insert(this->UniqueObject(20));
|
|
691
|
+
EXPECT_FALSE(ht1 == ht2);
|
|
692
|
+
ht1 = ht2;
|
|
693
|
+
EXPECT_TRUE(ht1 == ht2);
|
|
694
|
+
}
|
|
695
|
+
{
|
|
696
|
+
TypeParam ht1, ht2;
|
|
697
|
+
ht1.insert(this->UniqueObject(30));
|
|
698
|
+
ht1 = ht2;
|
|
699
|
+
EXPECT_EQ(0u, ht1.size());
|
|
700
|
+
}
|
|
701
|
+
{
|
|
702
|
+
TypeParam ht1, ht2;
|
|
703
|
+
ht1.set_deleted_key(this->UniqueKey(1));
|
|
704
|
+
ht2.insert(this->UniqueObject(1)); // has same key as ht1.delkey
|
|
705
|
+
ht1 = ht2; // should reset deleted-key to 'unset'
|
|
706
|
+
EXPECT_EQ(1u, ht1.size());
|
|
707
|
+
EXPECT_EQ(1u, ht1.count(this->UniqueKey(1)));
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
TYPED_TEST(HashtableAllTest, Clear) {
|
|
712
|
+
for (int i = 1; i < 200; i++) {
|
|
713
|
+
this->ht_.insert(this->UniqueObject(i));
|
|
714
|
+
}
|
|
715
|
+
this->ht_.clear();
|
|
716
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
717
|
+
// TODO(csilvers): do we want to enforce that the hashtable has or
|
|
718
|
+
// has not shrunk? It does for dense_* but not sparse_*.
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
TYPED_TEST(HashtableAllTest, ClearNoResize) {
|
|
722
|
+
if (!this->ht_.supports_clear_no_resize())
|
|
723
|
+
return;
|
|
724
|
+
typename TypeParam::size_type empty_bucket_count = this->ht_.bucket_count();
|
|
725
|
+
int last_element = 1;
|
|
726
|
+
while (this->ht_.bucket_count() == empty_bucket_count) {
|
|
727
|
+
this->ht_.insert(this->UniqueObject(last_element));
|
|
728
|
+
++last_element;
|
|
729
|
+
}
|
|
730
|
+
typename TypeParam::size_type last_bucket_count = this->ht_.bucket_count();
|
|
731
|
+
this->ht_.clear_no_resize();
|
|
732
|
+
EXPECT_EQ(last_bucket_count, this->ht_.bucket_count());
|
|
733
|
+
EXPECT_TRUE(this->ht_.empty());
|
|
734
|
+
|
|
735
|
+
// When inserting the same number of elements again, no resize
|
|
736
|
+
// should be necessary.
|
|
737
|
+
for (int i = 1; i < last_element; ++i) {
|
|
738
|
+
this->ht_.insert(this->UniqueObject(last_element + i));
|
|
739
|
+
EXPECT_EQ(last_bucket_count, this->ht_.bucket_count());
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
TYPED_TEST(HashtableAllTest, Swap) {
|
|
744
|
+
// Let's make a second hashtable with its own hasher, key_equal, etc.
|
|
745
|
+
Hasher hasher(1); // 1 is a unique id
|
|
746
|
+
TypeParam other_ht(200, hasher, hasher);
|
|
747
|
+
|
|
748
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
749
|
+
other_ht.set_deleted_key(this->UniqueKey(2));
|
|
750
|
+
|
|
751
|
+
for (int i = 3; i < 2000; i++) {
|
|
752
|
+
this->ht_.insert(this->UniqueObject(i));
|
|
753
|
+
}
|
|
754
|
+
this->ht_.erase(this->UniqueKey(1000));
|
|
755
|
+
other_ht.insert(this->UniqueObject(2001));
|
|
756
|
+
typename TypeParam::size_type expected_buckets = other_ht.bucket_count();
|
|
757
|
+
|
|
758
|
+
this->ht_.swap(other_ht);
|
|
759
|
+
|
|
760
|
+
EXPECT_EQ(this->UniqueKey(2), this->ht_.deleted_key());
|
|
761
|
+
EXPECT_EQ(this->UniqueKey(1), other_ht.deleted_key());
|
|
762
|
+
|
|
763
|
+
EXPECT_EQ(1, this->ht_.hash_funct().id());
|
|
764
|
+
EXPECT_EQ(0, other_ht.hash_funct().id());
|
|
765
|
+
|
|
766
|
+
EXPECT_EQ(1, this->ht_.key_eq().id());
|
|
767
|
+
EXPECT_EQ(0, other_ht.key_eq().id());
|
|
768
|
+
|
|
769
|
+
EXPECT_EQ(expected_buckets, this->ht_.bucket_count());
|
|
770
|
+
EXPECT_GT(other_ht.bucket_count(), 200u);
|
|
771
|
+
|
|
772
|
+
EXPECT_EQ(1u, this->ht_.size());
|
|
773
|
+
EXPECT_EQ(1996u, other_ht.size()); // because we erased 1000
|
|
774
|
+
|
|
775
|
+
EXPECT_EQ(0u, this->ht_.count(this->UniqueKey(111)));
|
|
776
|
+
EXPECT_EQ(1u, other_ht.count(this->UniqueKey(111)));
|
|
777
|
+
EXPECT_EQ(1u, this->ht_.count(this->UniqueKey(2001)));
|
|
778
|
+
EXPECT_EQ(0u, other_ht.count(this->UniqueKey(2001)));
|
|
779
|
+
EXPECT_EQ(0u, this->ht_.count(this->UniqueKey(1000)));
|
|
780
|
+
EXPECT_EQ(0u, other_ht.count(this->UniqueKey(1000)));
|
|
781
|
+
|
|
782
|
+
// We purposefully don't swap allocs -- they're not necessarily swappable.
|
|
783
|
+
|
|
784
|
+
// Now swap back, using the free-function swap.
|
|
785
|
+
// NOTE: MSVC seems to have trouble with this free swap, not quite
|
|
786
|
+
// sure why. I've given up trying to fix it though.
|
|
787
|
+
#ifdef _MSC_VER
|
|
788
|
+
other_ht.swap(this->ht_);
|
|
789
|
+
#else
|
|
790
|
+
swap(this->ht_, other_ht);
|
|
791
|
+
#endif
|
|
792
|
+
|
|
793
|
+
EXPECT_EQ(this->UniqueKey(1), this->ht_.deleted_key());
|
|
794
|
+
EXPECT_EQ(this->UniqueKey(2), other_ht.deleted_key());
|
|
795
|
+
EXPECT_EQ(0, this->ht_.hash_funct().id());
|
|
796
|
+
EXPECT_EQ(1, other_ht.hash_funct().id());
|
|
797
|
+
EXPECT_EQ(1996u, this->ht_.size());
|
|
798
|
+
EXPECT_EQ(1u, other_ht.size());
|
|
799
|
+
EXPECT_EQ(1u, this->ht_.count(this->UniqueKey(111)));
|
|
800
|
+
EXPECT_EQ(0u, other_ht.count(this->UniqueKey(111)));
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
TYPED_TEST(HashtableAllTest, Size) {
|
|
804
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
805
|
+
for (int i = 1; i < 1000; i++) { // go through some resizes
|
|
806
|
+
this->ht_.insert(this->UniqueObject(i));
|
|
807
|
+
EXPECT_EQ(static_cast<typename TypeParam::size_type>(i), this->ht_.size());
|
|
808
|
+
}
|
|
809
|
+
this->ht_.clear();
|
|
810
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
811
|
+
|
|
812
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
813
|
+
EXPECT_EQ(0u, this->ht_.size()); // deleted key doesn't count
|
|
814
|
+
for (int i = 2; i < 1000; i++) { // go through some resizes
|
|
815
|
+
this->ht_.insert(this->UniqueObject(i));
|
|
816
|
+
this->ht_.erase(this->UniqueKey(i));
|
|
817
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
TEST(HashtableTest, MaxSizeAndMaxBucketCount) {
|
|
822
|
+
// The max size depends on the allocator. So we can't use the
|
|
823
|
+
// built-in allocator type; instead, we make our own types.
|
|
824
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int> > ht_default;
|
|
825
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int, unsigned char> > ht_char;
|
|
826
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int, unsigned char, 104> > ht_104;
|
|
827
|
+
|
|
828
|
+
EXPECT_GE(ht_default.max_size(), 256u);
|
|
829
|
+
EXPECT_EQ(255u, ht_char.max_size());
|
|
830
|
+
EXPECT_EQ(104u, ht_104.max_size());
|
|
831
|
+
|
|
832
|
+
// In our implementations, MaxBucketCount == MaxSize.
|
|
833
|
+
EXPECT_EQ(ht_default.max_size(), ht_default.max_bucket_count());
|
|
834
|
+
EXPECT_EQ(ht_char.max_size(), ht_char.max_bucket_count());
|
|
835
|
+
EXPECT_EQ(ht_104.max_size(), ht_104.max_bucket_count());
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
TYPED_TEST(HashtableAllTest, Empty) {
|
|
839
|
+
EXPECT_TRUE(this->ht_.empty());
|
|
840
|
+
|
|
841
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
842
|
+
EXPECT_FALSE(this->ht_.empty());
|
|
843
|
+
|
|
844
|
+
this->ht_.clear();
|
|
845
|
+
EXPECT_TRUE(this->ht_.empty());
|
|
846
|
+
|
|
847
|
+
TypeParam empty_ht;
|
|
848
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
849
|
+
this->ht_.swap(empty_ht);
|
|
850
|
+
EXPECT_TRUE(this->ht_.empty());
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
TYPED_TEST(HashtableAllTest, BucketCount) {
|
|
854
|
+
TypeParam ht(100);
|
|
855
|
+
// constructor arg is number of *items* to be inserted, not the
|
|
856
|
+
// number of buckets, so we expect more buckets.
|
|
857
|
+
EXPECT_GT(ht.bucket_count(), 100u);
|
|
858
|
+
for (int i = 1; i < 200; i++) {
|
|
859
|
+
ht.insert(this->UniqueObject(i));
|
|
860
|
+
}
|
|
861
|
+
EXPECT_GT(ht.bucket_count(), 200u);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
TYPED_TEST(HashtableAllTest, BucketAndBucketSize) {
|
|
865
|
+
const typename TypeParam::size_type expected_bucknum = this->ht_.bucket(
|
|
866
|
+
this->UniqueKey(1));
|
|
867
|
+
EXPECT_EQ(0u, this->ht_.bucket_size(expected_bucknum));
|
|
868
|
+
|
|
869
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
870
|
+
EXPECT_EQ(expected_bucknum, this->ht_.bucket(this->UniqueKey(1)));
|
|
871
|
+
EXPECT_EQ(1u, this->ht_.bucket_size(expected_bucknum));
|
|
872
|
+
|
|
873
|
+
// Check that a bucket we didn't insert into, has a 0 size. Since
|
|
874
|
+
// we have an even number of buckets, bucknum^1 is guaranteed in range.
|
|
875
|
+
EXPECT_EQ(0u, this->ht_.bucket_size(expected_bucknum ^ 1));
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
TYPED_TEST(HashtableAllTest, LoadFactor) {
|
|
879
|
+
const typename TypeParam::size_type kSize = 16536;
|
|
880
|
+
// Check growing past various thresholds and then shrinking below
|
|
881
|
+
// them.
|
|
882
|
+
for (float grow_threshold = 0.2f;
|
|
883
|
+
grow_threshold <= 0.8f;
|
|
884
|
+
grow_threshold += 0.2f) {
|
|
885
|
+
TypeParam ht;
|
|
886
|
+
ht.set_deleted_key(this->UniqueKey(1));
|
|
887
|
+
ht.max_load_factor(grow_threshold);
|
|
888
|
+
ht.min_load_factor(0.0);
|
|
889
|
+
EXPECT_EQ(grow_threshold, ht.max_load_factor());
|
|
890
|
+
EXPECT_EQ(0.0, ht.min_load_factor());
|
|
891
|
+
|
|
892
|
+
ht.resize(kSize);
|
|
893
|
+
size_t bucket_count = ht.bucket_count();
|
|
894
|
+
// Erase and insert an element to set consider_shrink = true,
|
|
895
|
+
// which should not cause a shrink because the threshold is 0.0.
|
|
896
|
+
ht.insert(this->UniqueObject(2));
|
|
897
|
+
ht.erase(this->UniqueKey(2));
|
|
898
|
+
for (int i = 2;; ++i) {
|
|
899
|
+
ht.insert(this->UniqueObject(i));
|
|
900
|
+
if (static_cast<float>(ht.size())/bucket_count < grow_threshold) {
|
|
901
|
+
EXPECT_EQ(bucket_count, ht.bucket_count());
|
|
902
|
+
} else {
|
|
903
|
+
EXPECT_GT(ht.bucket_count(), bucket_count);
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
// Now set a shrink threshold 1% below the current size and remove
|
|
908
|
+
// items until the size falls below that.
|
|
909
|
+
const float shrink_threshold = static_cast<float>(ht.size()) /
|
|
910
|
+
ht.bucket_count() - 0.01f;
|
|
911
|
+
|
|
912
|
+
// This time around, check the old set_resizing_parameters interface.
|
|
913
|
+
ht.set_resizing_parameters(shrink_threshold, 1.0);
|
|
914
|
+
EXPECT_EQ(1.0, ht.max_load_factor());
|
|
915
|
+
EXPECT_EQ(shrink_threshold, ht.min_load_factor());
|
|
916
|
+
|
|
917
|
+
bucket_count = ht.bucket_count();
|
|
918
|
+
for (int i = 2;; ++i) {
|
|
919
|
+
ht.erase(this->UniqueKey(i));
|
|
920
|
+
// A resize is only triggered by an insert, so add and remove a
|
|
921
|
+
// value every iteration to trigger the shrink as soon as the
|
|
922
|
+
// threshold is passed.
|
|
923
|
+
ht.erase(this->UniqueKey(i+1));
|
|
924
|
+
ht.insert(this->UniqueObject(i+1));
|
|
925
|
+
if (static_cast<float>(ht.size())/bucket_count > shrink_threshold) {
|
|
926
|
+
EXPECT_EQ(bucket_count, ht.bucket_count());
|
|
927
|
+
} else {
|
|
928
|
+
EXPECT_LT(ht.bucket_count(), bucket_count);
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
TYPED_TEST(HashtableAllTest, ResizeAndRehash) {
|
|
936
|
+
// resize() and rehash() are synonyms. rehash() is the tr1 name.
|
|
937
|
+
TypeParam ht(10000);
|
|
938
|
+
ht.max_load_factor(0.8f); // for consistency's sake
|
|
939
|
+
|
|
940
|
+
for (int i = 1; i < 100; ++i)
|
|
941
|
+
ht.insert(this->UniqueObject(i));
|
|
942
|
+
ht.resize(0);
|
|
943
|
+
// Now ht should be as small as possible.
|
|
944
|
+
EXPECT_LT(ht.bucket_count(), 300u);
|
|
945
|
+
|
|
946
|
+
ht.rehash(9000); // use the 'rehash' version of the name.
|
|
947
|
+
// Bucket count should be next power of 2, after considering max_load_factor.
|
|
948
|
+
EXPECT_EQ(16384u, ht.bucket_count());
|
|
949
|
+
for (int i = 101; i < 200; ++i)
|
|
950
|
+
ht.insert(this->UniqueObject(i));
|
|
951
|
+
// Adding a few hundred buckets shouldn't have caused a resize yet.
|
|
952
|
+
EXPECT_EQ(ht.bucket_count(), 16384u);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
TYPED_TEST(HashtableAllTest, FindAndCountAndEqualRange) {
|
|
956
|
+
pair<typename TypeParam::iterator, typename TypeParam::iterator> eq_pair;
|
|
957
|
+
pair<typename TypeParam::const_iterator,
|
|
958
|
+
typename TypeParam::const_iterator> const_eq_pair;
|
|
959
|
+
|
|
960
|
+
EXPECT_TRUE(this->ht_.empty());
|
|
961
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(1)) == this->ht_.end());
|
|
962
|
+
EXPECT_EQ(0u, this->ht_.count(this->UniqueKey(1)));
|
|
963
|
+
eq_pair = this->ht_.equal_range(this->UniqueKey(1));
|
|
964
|
+
EXPECT_TRUE(eq_pair.first == eq_pair.second);
|
|
965
|
+
|
|
966
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
967
|
+
EXPECT_FALSE(this->ht_.empty());
|
|
968
|
+
this->ht_.insert(this->UniqueObject(11));
|
|
969
|
+
this->ht_.insert(this->UniqueObject(111));
|
|
970
|
+
this->ht_.insert(this->UniqueObject(1111));
|
|
971
|
+
this->ht_.insert(this->UniqueObject(11111));
|
|
972
|
+
this->ht_.insert(this->UniqueObject(111111));
|
|
973
|
+
this->ht_.insert(this->UniqueObject(1111111));
|
|
974
|
+
this->ht_.insert(this->UniqueObject(11111111));
|
|
975
|
+
this->ht_.insert(this->UniqueObject(111111111));
|
|
976
|
+
EXPECT_EQ(9u, this->ht_.size());
|
|
977
|
+
typename TypeParam::const_iterator it = this->ht_.find(this->UniqueKey(1));
|
|
978
|
+
EXPECT_EQ(it.key(), this->UniqueKey(1));
|
|
979
|
+
|
|
980
|
+
// Allow testing the const version of the methods as well.
|
|
981
|
+
const TypeParam ht = this->ht_;
|
|
982
|
+
|
|
983
|
+
// Some successful lookups (via find, count, and equal_range).
|
|
984
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(1)) != this->ht_.end());
|
|
985
|
+
EXPECT_EQ(1u, this->ht_.count(this->UniqueKey(1)));
|
|
986
|
+
eq_pair = this->ht_.equal_range(this->UniqueKey(1));
|
|
987
|
+
EXPECT_TRUE(eq_pair.first != eq_pair.second);
|
|
988
|
+
EXPECT_EQ(eq_pair.first.key(), this->UniqueKey(1));
|
|
989
|
+
++eq_pair.first;
|
|
990
|
+
EXPECT_TRUE(eq_pair.first == eq_pair.second);
|
|
991
|
+
|
|
992
|
+
EXPECT_TRUE(ht.find(this->UniqueKey(1)) != ht.end());
|
|
993
|
+
EXPECT_EQ(1u, ht.count(this->UniqueKey(1)));
|
|
994
|
+
const_eq_pair = ht.equal_range(this->UniqueKey(1));
|
|
995
|
+
EXPECT_TRUE(const_eq_pair.first != const_eq_pair.second);
|
|
996
|
+
EXPECT_EQ(const_eq_pair.first.key(), this->UniqueKey(1));
|
|
997
|
+
++const_eq_pair.first;
|
|
998
|
+
EXPECT_TRUE(const_eq_pair.first == const_eq_pair.second);
|
|
999
|
+
|
|
1000
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(11111)) != this->ht_.end());
|
|
1001
|
+
EXPECT_EQ(1u, this->ht_.count(this->UniqueKey(11111)));
|
|
1002
|
+
eq_pair = this->ht_.equal_range(this->UniqueKey(11111));
|
|
1003
|
+
EXPECT_TRUE(eq_pair.first != eq_pair.second);
|
|
1004
|
+
EXPECT_EQ(eq_pair.first.key(), this->UniqueKey(11111));
|
|
1005
|
+
++eq_pair.first;
|
|
1006
|
+
EXPECT_TRUE(eq_pair.first == eq_pair.second);
|
|
1007
|
+
|
|
1008
|
+
EXPECT_TRUE(ht.find(this->UniqueKey(11111)) != ht.end());
|
|
1009
|
+
EXPECT_EQ(1u, ht.count(this->UniqueKey(11111)));
|
|
1010
|
+
const_eq_pair = ht.equal_range(this->UniqueKey(11111));
|
|
1011
|
+
EXPECT_TRUE(const_eq_pair.first != const_eq_pair.second);
|
|
1012
|
+
EXPECT_EQ(const_eq_pair.first.key(), this->UniqueKey(11111));
|
|
1013
|
+
++const_eq_pair.first;
|
|
1014
|
+
EXPECT_TRUE(const_eq_pair.first == const_eq_pair.second);
|
|
1015
|
+
|
|
1016
|
+
// Some unsuccessful lookups (via find, count, and equal_range).
|
|
1017
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(11112)) == this->ht_.end());
|
|
1018
|
+
EXPECT_EQ(0u, this->ht_.count(this->UniqueKey(11112)));
|
|
1019
|
+
eq_pair = this->ht_.equal_range(this->UniqueKey(11112));
|
|
1020
|
+
EXPECT_TRUE(eq_pair.first == eq_pair.second);
|
|
1021
|
+
|
|
1022
|
+
EXPECT_TRUE(ht.find(this->UniqueKey(11112)) == ht.end());
|
|
1023
|
+
EXPECT_EQ(0u, ht.count(this->UniqueKey(11112)));
|
|
1024
|
+
const_eq_pair = ht.equal_range(this->UniqueKey(11112));
|
|
1025
|
+
EXPECT_TRUE(const_eq_pair.first == const_eq_pair.second);
|
|
1026
|
+
|
|
1027
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(11110)) == this->ht_.end());
|
|
1028
|
+
EXPECT_EQ(0u, this->ht_.count(this->UniqueKey(11110)));
|
|
1029
|
+
eq_pair = this->ht_.equal_range(this->UniqueKey(11110));
|
|
1030
|
+
EXPECT_TRUE(eq_pair.first == eq_pair.second);
|
|
1031
|
+
|
|
1032
|
+
EXPECT_TRUE(ht.find(this->UniqueKey(11110)) == ht.end());
|
|
1033
|
+
EXPECT_EQ(0u, ht.count(this->UniqueKey(11110)));
|
|
1034
|
+
const_eq_pair = ht.equal_range(this->UniqueKey(11110));
|
|
1035
|
+
EXPECT_TRUE(const_eq_pair.first == const_eq_pair.second);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
TYPED_TEST(HashtableAllTest, BracketInsert) {
|
|
1039
|
+
// tests operator[], for those types that support it.
|
|
1040
|
+
if (!this->ht_.supports_brackets())
|
|
1041
|
+
return;
|
|
1042
|
+
|
|
1043
|
+
// bracket_equal is equivalent to ht_[a] == b. It should insert a if
|
|
1044
|
+
// it doesn't already exist.
|
|
1045
|
+
EXPECT_TRUE(this->ht_.bracket_equal(this->UniqueKey(1),
|
|
1046
|
+
this->ht_.default_data()));
|
|
1047
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(1)) != this->ht_.end());
|
|
1048
|
+
|
|
1049
|
+
// bracket_assign is equivalent to ht_[a] = b.
|
|
1050
|
+
this->ht_.bracket_assign(this->UniqueKey(2),
|
|
1051
|
+
this->ht_.get_data(this->UniqueObject(4)));
|
|
1052
|
+
EXPECT_TRUE(this->ht_.find(this->UniqueKey(2)) != this->ht_.end());
|
|
1053
|
+
EXPECT_TRUE(this->ht_.bracket_equal(
|
|
1054
|
+
this->UniqueKey(2), this->ht_.get_data(this->UniqueObject(4))));
|
|
1055
|
+
|
|
1056
|
+
this->ht_.bracket_assign(
|
|
1057
|
+
this->UniqueKey(2), this->ht_.get_data(this->UniqueObject(6)));
|
|
1058
|
+
EXPECT_TRUE(this->ht_.bracket_equal(
|
|
1059
|
+
this->UniqueKey(2), this->ht_.get_data(this->UniqueObject(6))));
|
|
1060
|
+
// bracket_equal shouldn't have modified the value.
|
|
1061
|
+
EXPECT_TRUE(this->ht_.bracket_equal(
|
|
1062
|
+
this->UniqueKey(2), this->ht_.get_data(this->UniqueObject(6))));
|
|
1063
|
+
|
|
1064
|
+
// Verify that an operator[] that doesn't cause a resize, also
|
|
1065
|
+
// doesn't require an extra rehash.
|
|
1066
|
+
TypeParam ht(100);
|
|
1067
|
+
EXPECT_EQ(0, ht.hash_funct().num_hashes());
|
|
1068
|
+
ht.bracket_assign(this->UniqueKey(2), ht.get_data(this->UniqueObject(2)));
|
|
1069
|
+
EXPECT_EQ(1, ht.hash_funct().num_hashes());
|
|
1070
|
+
|
|
1071
|
+
// And overwriting, likewise, should only cause one extra hash.
|
|
1072
|
+
ht.bracket_assign(this->UniqueKey(2), ht.get_data(this->UniqueObject(2)));
|
|
1073
|
+
EXPECT_EQ(2, ht.hash_funct().num_hashes());
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
TYPED_TEST(HashtableAllTest, InsertValue) {
|
|
1077
|
+
// First, try some straightforward insertions.
|
|
1078
|
+
EXPECT_TRUE(this->ht_.empty());
|
|
1079
|
+
this->ht_.insert(this->UniqueObject(1));
|
|
1080
|
+
EXPECT_FALSE(this->ht_.empty());
|
|
1081
|
+
this->ht_.insert(this->UniqueObject(11));
|
|
1082
|
+
this->ht_.insert(this->UniqueObject(111));
|
|
1083
|
+
this->ht_.insert(this->UniqueObject(1111));
|
|
1084
|
+
this->ht_.insert(this->UniqueObject(11111));
|
|
1085
|
+
this->ht_.insert(this->UniqueObject(111111));
|
|
1086
|
+
this->ht_.insert(this->UniqueObject(1111111));
|
|
1087
|
+
this->ht_.insert(this->UniqueObject(11111111));
|
|
1088
|
+
this->ht_.insert(this->UniqueObject(111111111));
|
|
1089
|
+
EXPECT_EQ(9u, this->ht_.size());
|
|
1090
|
+
EXPECT_EQ(1u, this->ht_.count(this->UniqueKey(1)));
|
|
1091
|
+
EXPECT_EQ(1u, this->ht_.count(this->UniqueKey(1111)));
|
|
1092
|
+
|
|
1093
|
+
// Check the return type.
|
|
1094
|
+
pair<typename TypeParam::iterator, bool> insert_it;
|
|
1095
|
+
insert_it = this->ht_.insert(this->UniqueObject(1));
|
|
1096
|
+
EXPECT_EQ(false, insert_it.second); // false: already present
|
|
1097
|
+
EXPECT_TRUE(*insert_it.first == this->UniqueObject(1));
|
|
1098
|
+
|
|
1099
|
+
insert_it = this->ht_.insert(this->UniqueObject(2));
|
|
1100
|
+
EXPECT_EQ(true, insert_it.second); // true: not already present
|
|
1101
|
+
EXPECT_TRUE(*insert_it.first == this->UniqueObject(2));
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
TYPED_TEST(HashtableIntTest, InsertRange) {
|
|
1105
|
+
// We just test the ints here, to make the placement-new easier.
|
|
1106
|
+
TypeParam ht_source;
|
|
1107
|
+
ht_source.insert(this->UniqueObject(10));
|
|
1108
|
+
ht_source.insert(this->UniqueObject(100));
|
|
1109
|
+
ht_source.insert(this->UniqueObject(1000));
|
|
1110
|
+
ht_source.insert(this->UniqueObject(10000));
|
|
1111
|
+
ht_source.insert(this->UniqueObject(100000));
|
|
1112
|
+
ht_source.insert(this->UniqueObject(1000000));
|
|
1113
|
+
|
|
1114
|
+
vector<typename TypeParam::value_type> input(4);
|
|
1115
|
+
// We have to use placement-new because value_type may be const.
|
|
1116
|
+
// This is a copy of the first element in ht_source.
|
|
1117
|
+
new(&input[0]) typename TypeParam::value_type(*ht_source.begin());
|
|
1118
|
+
new(&input[1]) typename TypeParam::value_type(this->UniqueObject(2));
|
|
1119
|
+
new(&input[2]) typename TypeParam::value_type(this->UniqueObject(4));
|
|
1120
|
+
new(&input[3]) typename TypeParam::value_type(this->UniqueObject(8));
|
|
1121
|
+
|
|
1122
|
+
set<typename TypeParam::value_type> set_input;
|
|
1123
|
+
set_input.insert(this->UniqueObject(1111111));
|
|
1124
|
+
set_input.insert(this->UniqueObject(111111));
|
|
1125
|
+
set_input.insert(this->UniqueObject(11111));
|
|
1126
|
+
set_input.insert(this->UniqueObject(1111));
|
|
1127
|
+
set_input.insert(this->UniqueObject(111));
|
|
1128
|
+
set_input.insert(this->UniqueObject(11));
|
|
1129
|
+
|
|
1130
|
+
// Insert from ht_source, an iterator of the same type as us.
|
|
1131
|
+
typename TypeParam::const_iterator begin = ht_source.begin();
|
|
1132
|
+
typename TypeParam::const_iterator end = begin;
|
|
1133
|
+
std::advance(end, 3);
|
|
1134
|
+
this->ht_.insert(begin, end); // insert 3 elements from ht_source
|
|
1135
|
+
EXPECT_EQ(3u, this->ht_.size());
|
|
1136
|
+
EXPECT_TRUE(*this->ht_.begin() == this->UniqueObject(10) ||
|
|
1137
|
+
*this->ht_.begin() == this->UniqueObject(100) ||
|
|
1138
|
+
*this->ht_.begin() == this->UniqueObject(1000) ||
|
|
1139
|
+
*this->ht_.begin() == this->UniqueObject(10000) ||
|
|
1140
|
+
*this->ht_.begin() == this->UniqueObject(100000) ||
|
|
1141
|
+
*this->ht_.begin() == this->UniqueObject(1000000));
|
|
1142
|
+
|
|
1143
|
+
// And insert from set_input, a separate, non-random-access iterator.
|
|
1144
|
+
typename set<typename TypeParam::value_type>::const_iterator set_begin;
|
|
1145
|
+
typename set<typename TypeParam::value_type>::const_iterator set_end;
|
|
1146
|
+
set_begin = set_input.begin();
|
|
1147
|
+
set_end = set_begin;
|
|
1148
|
+
std::advance(set_end, 3);
|
|
1149
|
+
this->ht_.insert(set_begin, set_end);
|
|
1150
|
+
EXPECT_EQ(6u, this->ht_.size());
|
|
1151
|
+
|
|
1152
|
+
// Insert from input as well, a separate, random-access iterator.
|
|
1153
|
+
// The first element of input overlaps with an existing element
|
|
1154
|
+
// of ht_, so this should only up the size by 2.
|
|
1155
|
+
this->ht_.insert(input.begin(), input.begin() + 3);
|
|
1156
|
+
EXPECT_EQ(8u, this->ht_.size());
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
TEST(HashtableTest, InsertValueToMap) {
|
|
1160
|
+
// For the maps in particular, ensure that inserting doesn't change
|
|
1161
|
+
// the value.
|
|
1162
|
+
sparse_hash_map<int, int> shm;
|
|
1163
|
+
pair<sparse_hash_map<int,int>::iterator, bool> shm_it;
|
|
1164
|
+
shm[1] = 2; // test a different method of inserting
|
|
1165
|
+
shm_it = shm.insert(pair<int, int>(1, 3));
|
|
1166
|
+
EXPECT_EQ(false, shm_it.second);
|
|
1167
|
+
EXPECT_EQ(1, shm_it.first->first);
|
|
1168
|
+
EXPECT_EQ(2, shm_it.first->second);
|
|
1169
|
+
shm_it.first->second = 20;
|
|
1170
|
+
EXPECT_EQ(20, shm[1]);
|
|
1171
|
+
|
|
1172
|
+
shm_it = shm.insert(pair<int, int>(2, 4));
|
|
1173
|
+
EXPECT_EQ(true, shm_it.second);
|
|
1174
|
+
EXPECT_EQ(2, shm_it.first->first);
|
|
1175
|
+
EXPECT_EQ(4, shm_it.first->second);
|
|
1176
|
+
EXPECT_EQ(4, shm[2]);
|
|
1177
|
+
|
|
1178
|
+
// Do it all again, with dense_hash_map.
|
|
1179
|
+
dense_hash_map<int, int> dhm;
|
|
1180
|
+
dhm.set_empty_key(0);
|
|
1181
|
+
pair<dense_hash_map<int,int>::iterator, bool> dhm_it;
|
|
1182
|
+
dhm[1] = 2; // test a different method of inserting
|
|
1183
|
+
dhm_it = dhm.insert(pair<const int, int>(1, 3));
|
|
1184
|
+
EXPECT_EQ(false, dhm_it.second);
|
|
1185
|
+
EXPECT_EQ(1, dhm_it.first->first);
|
|
1186
|
+
EXPECT_EQ(2, dhm_it.first->second);
|
|
1187
|
+
dhm_it.first->second = 20;
|
|
1188
|
+
EXPECT_EQ(20, dhm[1]);
|
|
1189
|
+
|
|
1190
|
+
dhm_it = dhm.insert(pair<const int, int>(2, 4));
|
|
1191
|
+
EXPECT_EQ(true, dhm_it.second);
|
|
1192
|
+
EXPECT_EQ(2, dhm_it.first->first);
|
|
1193
|
+
EXPECT_EQ(4, dhm_it.first->second);
|
|
1194
|
+
EXPECT_EQ(4, dhm[2]);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
TYPED_TEST(HashtableStringTest, EmptyKey) {
|
|
1198
|
+
// Only run the string tests, to make it easier to know what the
|
|
1199
|
+
// empty key should be.
|
|
1200
|
+
if (!this->ht_.supports_empty_key())
|
|
1201
|
+
return;
|
|
1202
|
+
EXPECT_EQ(kEmptyString, this->ht_.empty_key());
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
TYPED_TEST(HashtableAllTest, DeletedKey) {
|
|
1206
|
+
if (!this->ht_.supports_deleted_key())
|
|
1207
|
+
return;
|
|
1208
|
+
this->ht_.insert(this->UniqueObject(10));
|
|
1209
|
+
this->ht_.insert(this->UniqueObject(20));
|
|
1210
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
1211
|
+
EXPECT_EQ(this->ht_.deleted_key(), this->UniqueKey(1));
|
|
1212
|
+
EXPECT_EQ(2u, this->ht_.size());
|
|
1213
|
+
this->ht_.erase(this->UniqueKey(20));
|
|
1214
|
+
EXPECT_EQ(1u, this->ht_.size());
|
|
1215
|
+
|
|
1216
|
+
// Changing the deleted key is fine.
|
|
1217
|
+
this->ht_.set_deleted_key(this->UniqueKey(2));
|
|
1218
|
+
EXPECT_EQ(this->ht_.deleted_key(), this->UniqueKey(2));
|
|
1219
|
+
EXPECT_EQ(1u, this->ht_.size());
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
TYPED_TEST(HashtableAllTest, Erase) {
|
|
1223
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
1224
|
+
EXPECT_EQ(0u, this->ht_.erase(this->UniqueKey(20)));
|
|
1225
|
+
this->ht_.insert(this->UniqueObject(10));
|
|
1226
|
+
this->ht_.insert(this->UniqueObject(20));
|
|
1227
|
+
EXPECT_EQ(1u, this->ht_.erase(this->UniqueKey(20)));
|
|
1228
|
+
EXPECT_EQ(1u, this->ht_.size());
|
|
1229
|
+
EXPECT_EQ(0u, this->ht_.erase(this->UniqueKey(20)));
|
|
1230
|
+
EXPECT_EQ(1u, this->ht_.size());
|
|
1231
|
+
EXPECT_EQ(0u, this->ht_.erase(this->UniqueKey(19)));
|
|
1232
|
+
EXPECT_EQ(1u, this->ht_.size());
|
|
1233
|
+
|
|
1234
|
+
typename TypeParam::iterator it = this->ht_.find(this->UniqueKey(10));
|
|
1235
|
+
EXPECT_TRUE(it != this->ht_.end());
|
|
1236
|
+
this->ht_.erase(it);
|
|
1237
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
1238
|
+
|
|
1239
|
+
for (int i = 10; i < 100; i++)
|
|
1240
|
+
this->ht_.insert(this->UniqueObject(i));
|
|
1241
|
+
EXPECT_EQ(90u, this->ht_.size());
|
|
1242
|
+
this->ht_.erase(this->ht_.begin(), this->ht_.end());
|
|
1243
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
TYPED_TEST(HashtableAllTest, EraseDoesNotResize) {
|
|
1247
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
1248
|
+
for (int i = 10; i < 2000; i++) {
|
|
1249
|
+
this->ht_.insert(this->UniqueObject(i));
|
|
1250
|
+
}
|
|
1251
|
+
const typename TypeParam::size_type old_count = this->ht_.bucket_count();
|
|
1252
|
+
for (int i = 10; i < 1000; i++) { // erase half one at a time
|
|
1253
|
+
EXPECT_EQ(1u, this->ht_.erase(this->UniqueKey(i)));
|
|
1254
|
+
}
|
|
1255
|
+
this->ht_.erase(this->ht_.begin(), this->ht_.end()); // and the rest at once
|
|
1256
|
+
EXPECT_EQ(0u, this->ht_.size());
|
|
1257
|
+
EXPECT_EQ(old_count, this->ht_.bucket_count());
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
TYPED_TEST(HashtableAllTest, Equals) {
|
|
1261
|
+
// The real test here is whether two hashtables are equal if they
|
|
1262
|
+
// have the same items but in a different order.
|
|
1263
|
+
TypeParam ht1;
|
|
1264
|
+
TypeParam ht2;
|
|
1265
|
+
|
|
1266
|
+
EXPECT_TRUE(ht1 == ht1);
|
|
1267
|
+
EXPECT_FALSE(ht1 != ht1);
|
|
1268
|
+
EXPECT_TRUE(ht1 == ht2);
|
|
1269
|
+
EXPECT_FALSE(ht1 != ht2);
|
|
1270
|
+
ht1.set_deleted_key(this->UniqueKey(1));
|
|
1271
|
+
// Only the contents affect equality, not things like deleted-key.
|
|
1272
|
+
EXPECT_TRUE(ht1 == ht2);
|
|
1273
|
+
EXPECT_FALSE(ht1 != ht2);
|
|
1274
|
+
ht1.resize(2000);
|
|
1275
|
+
EXPECT_TRUE(ht1 == ht2);
|
|
1276
|
+
|
|
1277
|
+
// The choice of allocator/etc doesn't matter either.
|
|
1278
|
+
Hasher hasher(1);
|
|
1279
|
+
Alloc<typename TypeParam::key_type> alloc(2, NULL);
|
|
1280
|
+
TypeParam ht3(5, hasher, hasher, alloc);
|
|
1281
|
+
EXPECT_TRUE(ht1 == ht3);
|
|
1282
|
+
EXPECT_FALSE(ht1 != ht3);
|
|
1283
|
+
|
|
1284
|
+
ht1.insert(this->UniqueObject(2));
|
|
1285
|
+
EXPECT_TRUE(ht1 != ht2);
|
|
1286
|
+
EXPECT_FALSE(ht1 == ht2); // this should hold as well!
|
|
1287
|
+
|
|
1288
|
+
ht2.insert(this->UniqueObject(2));
|
|
1289
|
+
EXPECT_TRUE(ht1 == ht2);
|
|
1290
|
+
|
|
1291
|
+
for (int i = 3; i <= 2000; i++) {
|
|
1292
|
+
ht1.insert(this->UniqueObject(i));
|
|
1293
|
+
}
|
|
1294
|
+
for (int i = 2000; i >= 3; i--) {
|
|
1295
|
+
ht2.insert(this->UniqueObject(i));
|
|
1296
|
+
}
|
|
1297
|
+
EXPECT_TRUE(ht1 == ht2);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
TEST(HashtableTest, IntIO) {
|
|
1301
|
+
// Since dense_hash_* doesn't support IO yet, and the set case is
|
|
1302
|
+
// just a special (easier) case than the map case, I just test on
|
|
1303
|
+
// sparse_hash_map. This handles the easy case where we can use the
|
|
1304
|
+
// standard reader and writer.
|
|
1305
|
+
sparse_hash_map<int, int> ht_out;
|
|
1306
|
+
ht_out.set_deleted_key(0);
|
|
1307
|
+
for (int i = 1; i < 1000; i++) {
|
|
1308
|
+
ht_out[i] = i * i;
|
|
1309
|
+
}
|
|
1310
|
+
ht_out.erase(563); // just to test having some erased keys when we write.
|
|
1311
|
+
ht_out.erase(22);
|
|
1312
|
+
|
|
1313
|
+
string file(TmpFile("/intio"));
|
|
1314
|
+
FILE* fp = fopen(file.c_str(), "wb");
|
|
1315
|
+
EXPECT_TRUE(fp != NULL);
|
|
1316
|
+
EXPECT_TRUE(ht_out.write_metadata(fp));
|
|
1317
|
+
EXPECT_TRUE(ht_out.write_nopointer_data(fp));
|
|
1318
|
+
fclose(fp);
|
|
1319
|
+
|
|
1320
|
+
sparse_hash_map<int, int> ht_in;
|
|
1321
|
+
fp = fopen(file.c_str(), "rb");
|
|
1322
|
+
EXPECT_TRUE(fp != NULL);
|
|
1323
|
+
EXPECT_TRUE(ht_in.read_metadata(fp));
|
|
1324
|
+
EXPECT_TRUE(ht_in.read_nopointer_data(fp));
|
|
1325
|
+
fclose(fp);
|
|
1326
|
+
|
|
1327
|
+
EXPECT_EQ(1, ht_in[1]);
|
|
1328
|
+
EXPECT_EQ(998001, ht_in[999]);
|
|
1329
|
+
EXPECT_EQ(100, ht_in[10]);
|
|
1330
|
+
EXPECT_EQ(441, ht_in[21]);
|
|
1331
|
+
EXPECT_EQ(0, ht_in[22]); // should not have been saved
|
|
1332
|
+
EXPECT_EQ(0, ht_in[563]);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
TEST(HashtableTest, StringIO) {
|
|
1336
|
+
// Since dense_hash_* doesn't support IO yet, and the set case is
|
|
1337
|
+
// just a special (easier) case than the map case, I just test on
|
|
1338
|
+
// sparse_hash_map. This handles the difficult case where we have
|
|
1339
|
+
// to write our own custom reader/writer for the data.
|
|
1340
|
+
sparse_hash_map<string, string, Hasher, Hasher> ht_out;
|
|
1341
|
+
ht_out.set_deleted_key(string(""));
|
|
1342
|
+
for (int i = 32; i < 128; i++) {
|
|
1343
|
+
// This maps 'a' to 32 a's, 'b' to 33 b's, etc.
|
|
1344
|
+
ht_out[string(1, i)] = string(i, i);
|
|
1345
|
+
}
|
|
1346
|
+
ht_out.erase("c"); // just to test having some erased keys when we write.
|
|
1347
|
+
ht_out.erase("y");
|
|
1348
|
+
|
|
1349
|
+
string file(TmpFile("/stringio"));
|
|
1350
|
+
FILE* fp = fopen(file.c_str(), "wb");
|
|
1351
|
+
EXPECT_TRUE(fp != NULL);
|
|
1352
|
+
EXPECT_TRUE(ht_out.write_metadata(fp));
|
|
1353
|
+
for (sparse_hash_map<string, string, Hasher, Hasher>::const_iterator
|
|
1354
|
+
it = ht_out.begin(); it != ht_out.end(); ++it) {
|
|
1355
|
+
const string::size_type first_size = it->first.length();
|
|
1356
|
+
fwrite(&first_size, sizeof(first_size), 1, fp); // ignore endianness issues
|
|
1357
|
+
fwrite(it->first.c_str(), first_size, 1, fp);
|
|
1358
|
+
|
|
1359
|
+
const string::size_type second_size = it->second.length();
|
|
1360
|
+
fwrite(&second_size, sizeof(second_size), 1, fp);
|
|
1361
|
+
fwrite(it->second.c_str(), second_size, 1, fp);
|
|
1362
|
+
}
|
|
1363
|
+
fclose(fp);
|
|
1364
|
+
|
|
1365
|
+
sparse_hash_map<string, string, Hasher, Hasher> ht_in;
|
|
1366
|
+
fp = fopen(file.c_str(), "rb");
|
|
1367
|
+
EXPECT_TRUE(fp != NULL);
|
|
1368
|
+
EXPECT_TRUE(ht_in.read_metadata(fp));
|
|
1369
|
+
for (sparse_hash_map<string, string, Hasher, Hasher>::iterator
|
|
1370
|
+
it = ht_in.begin(); it != ht_in.end(); ++it) {
|
|
1371
|
+
string::size_type first_size;
|
|
1372
|
+
fread(&first_size, sizeof(first_size), 1, fp);
|
|
1373
|
+
char* first = new char[first_size];
|
|
1374
|
+
fread(first, first_size, 1, fp);
|
|
1375
|
+
|
|
1376
|
+
string::size_type second_size;
|
|
1377
|
+
fread(&second_size, sizeof(second_size), 1, fp);
|
|
1378
|
+
char* second = new char[second_size];
|
|
1379
|
+
fread(second, second_size, 1, fp);
|
|
1380
|
+
|
|
1381
|
+
// it points to garbage, so we have to use placement-new to initialize.
|
|
1382
|
+
// We also have to use const-cast since it->first is const.
|
|
1383
|
+
new(const_cast<string*>(&it->first)) string(first, first_size);
|
|
1384
|
+
new(&it->second) string(second, second_size);
|
|
1385
|
+
delete[] first;
|
|
1386
|
+
delete[] second;
|
|
1387
|
+
}
|
|
1388
|
+
fclose(fp);
|
|
1389
|
+
|
|
1390
|
+
EXPECT_EQ(string(" "), ht_in[" "]);
|
|
1391
|
+
EXPECT_EQ(string("+++++++++++++++++++++++++++++++++++++++++++"), ht_in["+"]);
|
|
1392
|
+
EXPECT_EQ(string(""), ht_in["c"]); // should not have been saved
|
|
1393
|
+
EXPECT_EQ(string(""), ht_in["y"]);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// ------------------------------------------------------------------------
|
|
1397
|
+
// The above tests test the general API for correctness. These tests
|
|
1398
|
+
// test a few corner cases that have tripped us up in the past, and
|
|
1399
|
+
// more general, cross-API issues like memory management.
|
|
1400
|
+
|
|
1401
|
+
TYPED_TEST(HashtableAllTest, BracketOperatorCrashing) {
|
|
1402
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
1403
|
+
for (int iters = 0; iters < 10; iters++) {
|
|
1404
|
+
// We start at 33 because after shrinking, we'll be at 32 buckets.
|
|
1405
|
+
for (int i = 33; i < 133; i++) {
|
|
1406
|
+
this->ht_.bracket_assign(this->UniqueKey(i),
|
|
1407
|
+
this->ht_.get_data(this->UniqueObject(i)));
|
|
1408
|
+
}
|
|
1409
|
+
this->ht_.clear_no_resize();
|
|
1410
|
+
// This will force a shrink on the next insert, which we want to test.
|
|
1411
|
+
this->ht_.bracket_assign(this->UniqueKey(2),
|
|
1412
|
+
this->ht_.get_data(this->UniqueObject(2)));
|
|
1413
|
+
this->ht_.erase(this->UniqueKey(2));
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// For data types with trivial copy-constructors and destructors, we
|
|
1418
|
+
// should use an optimized routine for data-copying, that involves
|
|
1419
|
+
// memmove. We test this by keeping count of how many times the
|
|
1420
|
+
// copy-constructor is called; it should be much less with the
|
|
1421
|
+
// optimized code.
|
|
1422
|
+
struct Memmove {
|
|
1423
|
+
public:
|
|
1424
|
+
Memmove(): i(0) {}
|
|
1425
|
+
explicit Memmove(int ival): i(ival) {}
|
|
1426
|
+
Memmove(const Memmove& that) { this->i = that.i; num_copies++; }
|
|
1427
|
+
int i;
|
|
1428
|
+
static int num_copies;
|
|
1429
|
+
};
|
|
1430
|
+
int Memmove::num_copies = 0;
|
|
1431
|
+
|
|
1432
|
+
struct NoMemmove {
|
|
1433
|
+
public:
|
|
1434
|
+
NoMemmove(): i(0) {}
|
|
1435
|
+
explicit NoMemmove(int ival): i(ival) {}
|
|
1436
|
+
NoMemmove(const NoMemmove& that) { this->i = that.i; num_copies++; }
|
|
1437
|
+
int i;
|
|
1438
|
+
static int num_copies;
|
|
1439
|
+
};
|
|
1440
|
+
int NoMemmove::num_copies = 0;
|
|
1441
|
+
|
|
1442
|
+
} // unnamed namespace
|
|
1443
|
+
|
|
1444
|
+
// This is what tells the hashtable code it can use memmove for this class:
|
|
1445
|
+
_START_GOOGLE_NAMESPACE_
|
|
1446
|
+
template<> struct has_trivial_copy<Memmove> : true_type { };
|
|
1447
|
+
template<> struct has_trivial_destructor<Memmove> : true_type { };
|
|
1448
|
+
_END_GOOGLE_NAMESPACE_
|
|
1449
|
+
|
|
1450
|
+
namespace {
|
|
1451
|
+
|
|
1452
|
+
TEST(HashtableTest, SimpleDataTypeOptimizations) {
|
|
1453
|
+
// Only sparsehashtable optimizes moves in this way.
|
|
1454
|
+
sparse_hash_map<int, Memmove, Hasher, Hasher> memmove;
|
|
1455
|
+
sparse_hash_map<int, NoMemmove, Hasher, Hasher> nomemmove;
|
|
1456
|
+
sparse_hash_map<int, Memmove, Hasher, Hasher, Alloc<int> >
|
|
1457
|
+
memmove_nonstandard_alloc;
|
|
1458
|
+
|
|
1459
|
+
Memmove::num_copies = 0;
|
|
1460
|
+
for (int i = 10000; i > 0; i--) {
|
|
1461
|
+
memmove[i] = Memmove(i);
|
|
1462
|
+
}
|
|
1463
|
+
const int memmove_copies = Memmove::num_copies;
|
|
1464
|
+
|
|
1465
|
+
NoMemmove::num_copies = 0;
|
|
1466
|
+
for (int i = 10000; i > 0; i--) {
|
|
1467
|
+
nomemmove[i] = NoMemmove(i);
|
|
1468
|
+
}
|
|
1469
|
+
const int nomemmove_copies = NoMemmove::num_copies;
|
|
1470
|
+
|
|
1471
|
+
Memmove::num_copies = 0;
|
|
1472
|
+
for (int i = 10000; i > 0; i--) {
|
|
1473
|
+
memmove_nonstandard_alloc[i] = Memmove(i);
|
|
1474
|
+
}
|
|
1475
|
+
const int memmove_nonstandard_alloc_copies = Memmove::num_copies;
|
|
1476
|
+
|
|
1477
|
+
EXPECT_GT(nomemmove_copies, memmove_copies);
|
|
1478
|
+
EXPECT_EQ(nomemmove_copies, memmove_nonstandard_alloc_copies);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
TYPED_TEST(HashtableAllTest, ResizeHysteresis) {
|
|
1482
|
+
// We want to make sure that when we create a hashtable, and then
|
|
1483
|
+
// add and delete one element, the size of the hashtable doesn't
|
|
1484
|
+
// change.
|
|
1485
|
+
this->ht_.set_deleted_key(this->UniqueKey(1));
|
|
1486
|
+
typename TypeParam::size_type old_bucket_count = this->ht_.bucket_count();
|
|
1487
|
+
this->ht_.insert(this->UniqueObject(4));
|
|
1488
|
+
this->ht_.erase(this->UniqueKey(4));
|
|
1489
|
+
this->ht_.insert(this->UniqueObject(4));
|
|
1490
|
+
this->ht_.erase(this->UniqueKey(4));
|
|
1491
|
+
EXPECT_EQ(old_bucket_count, this->ht_.bucket_count());
|
|
1492
|
+
|
|
1493
|
+
// Try it again, but with a hashtable that starts very small
|
|
1494
|
+
TypeParam ht(2);
|
|
1495
|
+
EXPECT_LT(ht.bucket_count(), 32u); // verify we really do start small
|
|
1496
|
+
ht.set_deleted_key(this->UniqueKey(1));
|
|
1497
|
+
old_bucket_count = ht.bucket_count();
|
|
1498
|
+
ht.insert(this->UniqueObject(4));
|
|
1499
|
+
ht.erase(this->UniqueKey(4));
|
|
1500
|
+
ht.insert(this->UniqueObject(4));
|
|
1501
|
+
ht.erase(this->UniqueKey(4));
|
|
1502
|
+
EXPECT_EQ(old_bucket_count, ht.bucket_count());
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
TEST(HashtableTest, ConstKey) {
|
|
1506
|
+
// Sometimes people write hash_map<const int, int>, even though the
|
|
1507
|
+
// const isn't necessary. Make sure we handle this cleanly.
|
|
1508
|
+
sparse_hash_map<const int, int, Hasher, Hasher> shm;
|
|
1509
|
+
shm.set_deleted_key(1);
|
|
1510
|
+
shm[10] = 20;
|
|
1511
|
+
|
|
1512
|
+
dense_hash_map<const int, int, Hasher, Hasher> dhm;
|
|
1513
|
+
dhm.set_empty_key(1);
|
|
1514
|
+
dhm.set_deleted_key(2);
|
|
1515
|
+
dhm[10] = 20;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
TYPED_TEST(HashtableAllTest, ResizeActuallyResizes) {
|
|
1519
|
+
// This tests for a problem we had where we could repeatedly "resize"
|
|
1520
|
+
// a hashtable to the same size it was before, on every insert.
|
|
1521
|
+
const typename TypeParam::size_type kSize = 1<<10; // Pick any power of 2
|
|
1522
|
+
const float kResize = 0.8f; // anything between 0.5 and 1 is fine.
|
|
1523
|
+
const int kThreshold = static_cast<int>(kSize * kResize - 1);
|
|
1524
|
+
this->ht_.set_resizing_parameters(0, kResize);
|
|
1525
|
+
this->ht_.set_deleted_key(this->UniqueKey(kThreshold + 100));
|
|
1526
|
+
|
|
1527
|
+
// Get right up to the resizing threshold.
|
|
1528
|
+
for (int i = 0; i <= kThreshold; i++) {
|
|
1529
|
+
this->ht_.insert(this->UniqueObject(i+1));
|
|
1530
|
+
}
|
|
1531
|
+
// The bucket count should equal kSize.
|
|
1532
|
+
EXPECT_EQ(kSize, this->ht_.bucket_count());
|
|
1533
|
+
|
|
1534
|
+
// Now start doing erase+insert pairs. This should cause us to
|
|
1535
|
+
// copy the hashtable at most once.
|
|
1536
|
+
const int pre_copies = this->ht_.num_table_copies();
|
|
1537
|
+
for (int i = 0; i < static_cast<int>(kSize); i++) {
|
|
1538
|
+
this->ht_.erase(this->UniqueKey(kThreshold));
|
|
1539
|
+
this->ht_.insert(this->UniqueObject(kThreshold));
|
|
1540
|
+
}
|
|
1541
|
+
EXPECT_LT(this->ht_.num_table_copies(), pre_copies + 2);
|
|
1542
|
+
|
|
1543
|
+
// Now create a hashtable where we go right to the threshold, then
|
|
1544
|
+
// delete everything and do one insert. Even though our hashtable
|
|
1545
|
+
// is now tiny, we should still have at least kSize buckets, because
|
|
1546
|
+
// our shrink threshhold is 0.
|
|
1547
|
+
TypeParam ht2;
|
|
1548
|
+
ht2.set_deleted_key(this->UniqueKey(kThreshold + 100));
|
|
1549
|
+
ht2.set_resizing_parameters(0, kResize);
|
|
1550
|
+
EXPECT_LT(ht2.bucket_count(), kSize);
|
|
1551
|
+
for (int i = 0; i <= kThreshold; i++) {
|
|
1552
|
+
ht2.insert(this->UniqueObject(i+1));
|
|
1553
|
+
}
|
|
1554
|
+
EXPECT_EQ(ht2.bucket_count(), kSize);
|
|
1555
|
+
for (int i = 0; i <= kThreshold; i++) {
|
|
1556
|
+
ht2.erase(this->UniqueKey(i+1));
|
|
1557
|
+
EXPECT_EQ(ht2.bucket_count(), kSize);
|
|
1558
|
+
}
|
|
1559
|
+
ht2.insert(this->UniqueObject(kThreshold+2));
|
|
1560
|
+
EXPECT_GE(ht2.bucket_count(), kSize);
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
template<typename T> class DenseIntMap : public dense_hash_map<int, T> {
|
|
1564
|
+
public:
|
|
1565
|
+
DenseIntMap() { this->set_empty_key(0); }
|
|
1566
|
+
};
|
|
1567
|
+
|
|
1568
|
+
class DenseStringSet : public dense_hash_set<string, Hasher, Hasher> {
|
|
1569
|
+
public:
|
|
1570
|
+
DenseStringSet() { this->set_empty_key(string("")); }
|
|
1571
|
+
};
|
|
1572
|
+
|
|
1573
|
+
TEST(HashtableTest, NestedHashtables) {
|
|
1574
|
+
// People can do better than to have a hash_map of hash_maps, but we
|
|
1575
|
+
// should still support it. I try a few different mappings.
|
|
1576
|
+
sparse_hash_map<string, sparse_hash_map<int, string>, Hasher, Hasher> ht1;
|
|
1577
|
+
sparse_hash_map<string, DenseStringSet, Hasher, Hasher> ht2;
|
|
1578
|
+
dense_hash_map<int, DenseIntMap<int>, Hasher, Hasher> ht3;
|
|
1579
|
+
ht3.set_empty_key(0);
|
|
1580
|
+
|
|
1581
|
+
ht1["hi"]; // create a sub-ht with the default values
|
|
1582
|
+
ht1["lo"][1] = "there";
|
|
1583
|
+
sparse_hash_map<string, sparse_hash_map<int, string>, Hasher, Hasher>
|
|
1584
|
+
ht1copy = ht1;
|
|
1585
|
+
|
|
1586
|
+
ht2["hi"];
|
|
1587
|
+
ht2["hi"].insert("lo");
|
|
1588
|
+
sparse_hash_map<string, DenseStringSet, Hasher, Hasher> ht2copy = ht2;
|
|
1589
|
+
|
|
1590
|
+
ht3[1];
|
|
1591
|
+
ht3[2][3] = 4;
|
|
1592
|
+
dense_hash_map<int, DenseIntMap<int>, Hasher, Hasher> ht3copy = ht3;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
TEST(HashtableDeathTest, ResizeOverflow) {
|
|
1596
|
+
dense_hash_map<int, int> ht;
|
|
1597
|
+
try {
|
|
1598
|
+
ht.resize(static_cast<size_t>(-1));
|
|
1599
|
+
EXPECT_TRUE(false && "dense_hash_map reszie should have failed");
|
|
1600
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1601
|
+
// Good, the resize failed.
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
sparse_hash_map<int, int> ht2;
|
|
1605
|
+
try {
|
|
1606
|
+
ht2.resize(static_cast<size_t>(-1));
|
|
1607
|
+
EXPECT_TRUE(false && "sparse_hash_map reszie should have failed");
|
|
1608
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1609
|
+
// Good, the resize failed.
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
TEST(HashtableDeathTest, InsertSizeTypeOverflow) {
|
|
1614
|
+
static const int kMax = 256;
|
|
1615
|
+
vector<int> test_data(kMax);
|
|
1616
|
+
for (int i = 0; i < kMax; ++i) {
|
|
1617
|
+
test_data[i] = i+1000;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 10> > shs;
|
|
1621
|
+
dense_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 10> > dhs;
|
|
1622
|
+
dhs.set_empty_key(-1);
|
|
1623
|
+
|
|
1624
|
+
// Test we are using the correct allocator
|
|
1625
|
+
EXPECT_TRUE(shs.get_allocator().is_custom_alloc());
|
|
1626
|
+
EXPECT_TRUE(dhs.get_allocator().is_custom_alloc());
|
|
1627
|
+
|
|
1628
|
+
// Test size_type overflow in insert(it, it)
|
|
1629
|
+
try {
|
|
1630
|
+
dhs.insert(test_data.begin(), test_data.end());
|
|
1631
|
+
EXPECT_TRUE(false && "dense_hash_map::insert(it,it) should have overflown");
|
|
1632
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1633
|
+
// Good, the operation failed.
|
|
1634
|
+
}
|
|
1635
|
+
try {
|
|
1636
|
+
shs.insert(test_data.begin(), test_data.end());
|
|
1637
|
+
EXPECT_TRUE(false && "sparse_hash_map::insert(it,it) should have overflown");
|
|
1638
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1639
|
+
// Good, the operation failed.
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
TEST(HashtableDeathTest, InsertMaxSizeOverflow) {
|
|
1644
|
+
static const int kMax = 256;
|
|
1645
|
+
vector<int> test_data(kMax);
|
|
1646
|
+
for (int i = 0; i < kMax; ++i) {
|
|
1647
|
+
test_data[i] = i+1000;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 10> > shs;
|
|
1651
|
+
dense_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 10> > dhs;
|
|
1652
|
+
dhs.set_empty_key(-1);
|
|
1653
|
+
|
|
1654
|
+
// Test max_size overflow
|
|
1655
|
+
try {
|
|
1656
|
+
dhs.insert(test_data.begin(), test_data.begin() + 11);
|
|
1657
|
+
EXPECT_TRUE(false && "dense_hash_map max_size check should have failed");
|
|
1658
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1659
|
+
// Good, the operation failed.
|
|
1660
|
+
}
|
|
1661
|
+
try {
|
|
1662
|
+
shs.insert(test_data.begin(), test_data.begin() + 11);
|
|
1663
|
+
EXPECT_TRUE(false && "sparse_hash_map max_size check should have failed");
|
|
1664
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1665
|
+
// Good, the operation failed.
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
TEST(HashtableDeathTest, ResizeSizeTypeOverflow) {
|
|
1670
|
+
// Test min-buckets overflow, when we want to resize too close to size_type
|
|
1671
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 10> > shs;
|
|
1672
|
+
dense_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 10> > dhs;
|
|
1673
|
+
dhs.set_empty_key(-1);
|
|
1674
|
+
|
|
1675
|
+
try {
|
|
1676
|
+
dhs.resize(250); // 9+250 > 25
|
|
1677
|
+
EXPECT_TRUE(false && "dense_hash_map resize should have seen overflow");
|
|
1678
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1679
|
+
// Good, the operation failed.
|
|
1680
|
+
}
|
|
1681
|
+
try {
|
|
1682
|
+
shs.resize(250);
|
|
1683
|
+
EXPECT_TRUE(false && "sparse_hash_map resize should have seen overflow");
|
|
1684
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1685
|
+
// Good, the operation failed.
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
TEST(HashtableDeathTest, ResizeDeltaOverflow) {
|
|
1690
|
+
static const int kMax = 256;
|
|
1691
|
+
vector<int> test_data(kMax);
|
|
1692
|
+
for (int i = 0; i < kMax; ++i) {
|
|
1693
|
+
test_data[i] = i+1000;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
sparse_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 255> > shs;
|
|
1697
|
+
dense_hash_set<int, Hasher, Hasher, Alloc<int, uint8, 255> > dhs;
|
|
1698
|
+
dhs.set_empty_key(-1);
|
|
1699
|
+
for (int i = 0; i < 9; i++) {
|
|
1700
|
+
dhs.insert(i);
|
|
1701
|
+
shs.insert(i);
|
|
1702
|
+
}
|
|
1703
|
+
try {
|
|
1704
|
+
dhs.insert(test_data.begin(), test_data.begin() + 250);
|
|
1705
|
+
EXPECT_TRUE(false && "dense_hash_map big insert should have overflowed");
|
|
1706
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1707
|
+
// Good, the operation failed.
|
|
1708
|
+
}
|
|
1709
|
+
try {
|
|
1710
|
+
shs.insert(test_data.begin(), test_data.begin() + 250);
|
|
1711
|
+
EXPECT_TRUE(false && "sparse_hash_map big insert should have overflowed");
|
|
1712
|
+
} catch (const STL_NAMESPACE::length_error&) {
|
|
1713
|
+
// Good, the operation failed.
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// ------------------------------------------------------------------------
|
|
1718
|
+
// This informational "test" comes last so it's easy to see.
|
|
1719
|
+
// Also, benchmarks.
|
|
1720
|
+
|
|
1721
|
+
TYPED_TEST(HashtableAllTest, ClassSizes) {
|
|
1722
|
+
cout << "sizeof(" << typeid(TypeParam).name() << "): "
|
|
1723
|
+
<< sizeof(this->ht_) << "\n";
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
} // unnamed namespace
|
|
1727
|
+
|
|
1728
|
+
int main(int, char **) {
|
|
1729
|
+
// All the work is done in the static constructors. If they don't
|
|
1730
|
+
// die, the tests have all passed.
|
|
1731
|
+
cout << "PASS\n";
|
|
1732
|
+
return 0;
|
|
1733
|
+
}
|