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 is just a very thin wrapper over densehashtable.h, just |
34 |
// like sgi stl's stl_hash_map is a very thin wrapper over |
35 |
// stl_hashtable. The major thing we define is operator[], because |
36 |
// we have a concept of a data_type which stl_hashtable doesn't |
37 |
// (it only has a key and a value). |
38 |
// |
39 |
// NOTE: this is exactly like sparse_hash_map.h, with the word |
40 |
// "sparse" replaced by "dense", except for the addition of |
41 |
// set_empty_key(). |
42 |
// |
43 |
// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. |
44 |
// |
45 |
// Otherwise your program will die in mysterious ways. |
46 |
// |
47 |
// In other respects, we adhere mostly to the STL semantics for |
48 |
// hash-map. One important exception is that insert() invalidates |
49 |
// iterators entirely. On the plus side, though, erase() doesn't |
50 |
// invalidate iterators at all, or even change the ordering of elements. |
51 |
// |
52 |
// Here are a few "power user" tips: |
53 |
// |
54 |
// 1) set_deleted_key(): |
55 |
// If you want to use erase() you must call set_deleted_key(), |
56 |
// in addition to set_empty_key(), after construction. |
57 |
// The deleted and empty keys must differ. |
58 |
// |
59 |
// 2) resize(0): |
60 |
// When an item is deleted, its memory isn't freed right |
61 |
// away. This allows you to iterate over a hashtable, |
62 |
// and call erase(), without invalidating the iterator. |
63 |
// To force the memory to be freed, call resize(0). |
64 |
// |
65 |
// 3) set_resizing_parameters(0.0, 0.8): |
66 |
// Setting the shrink_resize_percent to 0.0 guarantees |
67 |
// that the hash table will never shrink. |
68 |
// |
69 |
// Guide to what kind of hash_map to use: |
70 |
// (1) dense_hash_map: fastest, uses the most memory |
71 |
// (2) sparse_hash_map: slowest, uses the least memory |
72 |
// (3) hash_map (STL): in the middle |
73 |
// Typically I use sparse_hash_map when I care about space and/or when |
74 |
// I need to save the hashtable on disk. I use hash_map otherwise. I |
75 |
// don't personally use dense_hash_map ever; the only use of |
76 |
// dense_hash_map I know of is to work around malloc() bugs in some |
77 |
// systems (dense_hash_map has a particularly simple allocation scheme). |
78 |
// |
79 |
// - dense_hash_map has, typically, a factor of 2 memory overhead (if your |
80 |
// data takes up X bytes, the hash_map uses X more bytes in overhead). |
81 |
// - sparse_hash_map has about 2 bits overhead per entry. |
82 |
// - sparse_hash_map can be 3-7 times slower than the others for lookup and, |
83 |
// especially, inserts. See time_hash_map.cc for details. |
84 |
// |
85 |
// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_map.html |
86 |
// for information about how to use this class. |
87 |
|
88 |
#ifndef _DENSE_HASH_MAP_H_ |
89 |
#define _DENSE_HASH_MAP_H_ |
90 |
|
91 |
#include <google/sparsehash/sparseconfig.h> |
92 |
#include <stdio.h> // for FILE * in read()/write() |
93 |
#include <algorithm> // for the default template args |
94 |
#include <functional> // for equal_to |
95 |
#include <memory> // for alloc<> |
96 |
#include <utility> // for pair<> |
97 |
#include HASH_FUN_H // defined in config.h |
98 |
#include <google/sparsehash/densehashtable.h> |
99 |
|
100 |
|
101 |
_START_GOOGLE_NAMESPACE_ |
102 |
|
103 |
using STL_NAMESPACE::pair; |
104 |
|
105 |
template <class Key, class T, |
106 |
class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h |
107 |
class EqualKey = STL_NAMESPACE::equal_to<Key>, |
108 |
class Alloc = STL_NAMESPACE::allocator<T> > |
109 |
class dense_hash_map { |
110 |
private: |
111 |
// Apparently select1st is not stl-standard, so we define our own |
112 |
struct SelectKey { |
113 |
const Key& operator()(const pair<const Key, T>& p) const { |
114 |
return p.first; |
115 |
} |
116 |
}; |
117 |
|
118 |
// The actual data |
119 |
typedef dense_hashtable<pair<const Key, T>, Key, HashFcn, |
120 |
SelectKey, EqualKey, Alloc> ht; |
121 |
ht rep; |
122 |
|
123 |
public: |
124 |
typedef typename ht::key_type key_type; |
125 |
typedef T data_type; |
126 |
typedef T mapped_type; |
127 |
typedef typename ht::value_type value_type; |
128 |
typedef typename ht::hasher hasher; |
129 |
typedef typename ht::key_equal key_equal; |
130 |
|
131 |
typedef typename ht::size_type size_type; |
132 |
typedef typename ht::difference_type difference_type; |
133 |
typedef typename ht::pointer pointer; |
134 |
typedef typename ht::const_pointer const_pointer; |
135 |
typedef typename ht::reference reference; |
136 |
typedef typename ht::const_reference const_reference; |
137 |
|
138 |
typedef typename ht::iterator iterator; |
139 |
typedef typename ht::const_iterator const_iterator; |
140 |
|
141 |
// Iterator functions |
142 |
iterator begin() { return rep.begin(); } |
143 |
iterator end() { return rep.end(); } |
144 |
const_iterator begin() const { return rep.begin(); } |
145 |
const_iterator end() const { return rep.end(); } |
146 |
|
147 |
|
148 |
// Accessor functions |
149 |
hasher hash_funct() const { return rep.hash_funct(); } |
150 |
key_equal key_eq() const { return rep.key_eq(); } |
151 |
|
152 |
|
153 |
// Constructors |
154 |
explicit dense_hash_map(size_type expected_max_items_in_table = 0, |
155 |
const hasher& hf = hasher(), |
156 |
const key_equal& eql = key_equal()) |
157 |
: rep(expected_max_items_in_table, hf, eql) { } |
158 |
|
159 |
template <class InputIterator> |
160 |
dense_hash_map(InputIterator f, InputIterator l, |
161 |
size_type expected_max_items_in_table = 0, |
162 |
const hasher& hf = hasher(), |
163 |
const key_equal& eql = key_equal()) |
164 |
: rep(expected_max_items_in_table, hf, eql) { |
165 |
rep.insert(f, l); |
166 |
} |
167 |
// We use the default copy constructor |
168 |
// We use the default operator=() |
169 |
// We use the default destructor |
170 |
|
171 |
void clear() { rep.clear(); } |
172 |
// This clears the hash map without resizing it down to the minimum |
173 |
// bucket count, but rather keeps the number of buckets constant |
174 |
void clear_no_resize() { rep.clear_no_resize(); } |
175 |
void swap(dense_hash_map& hs) { rep.swap(hs.rep); } |
176 |
|
177 |
|
178 |
// Functions concerning size |
179 |
size_type size() const { return rep.size(); } |
180 |
size_type max_size() const { return rep.max_size(); } |
181 |
bool empty() const { return rep.empty(); } |
182 |
size_type bucket_count() const { return rep.bucket_count(); } |
183 |
size_type max_bucket_count() const { return rep.max_bucket_count(); } |
184 |
|
185 |
void resize(size_type hint) { rep.resize(hint); } |
186 |
|
187 |
void set_resizing_parameters(float shrink, float grow) { |
188 |
return rep.set_resizing_parameters(shrink, grow); |
189 |
} |
190 |
|
191 |
// Lookup routines |
192 |
iterator find(const key_type& key) { return rep.find(key); } |
193 |
const_iterator find(const key_type& key) const { return rep.find(key); } |
194 |
|
195 |
data_type& operator[](const key_type& key) { // This is our value-add! |
196 |
iterator it = find(key); |
197 |
if (it != end()) { |
198 |
return it->second; |
199 |
} else { |
200 |
return insert(value_type(key, data_type())).first->second; |
201 |
} |
202 |
} |
203 |
|
204 |
size_type count(const key_type& key) const { return rep.count(key); } |
205 |
|
206 |
pair<iterator, iterator> equal_range(const key_type& key) { |
207 |
return rep.equal_range(key); |
208 |
} |
209 |
pair<const_iterator, const_iterator> equal_range(const key_type& key) const { |
210 |
return rep.equal_range(key); |
211 |
} |
212 |
|
213 |
// Insertion routines |
214 |
pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); } |
215 |
template <class InputIterator> |
216 |
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } |
217 |
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } |
218 |
// required for std::insert_iterator; the passed-in iterator is ignored |
219 |
iterator insert(iterator, const value_type& obj) { return insert(obj).first; } |
220 |
|
221 |
|
222 |
// Deletion and empty routines |
223 |
// THESE ARE NON-STANDARD! I make you specify an "impossible" key |
224 |
// value to identify deleted and empty buckets. You can change the |
225 |
// deleted key as time goes on, or get rid of it entirely to be insert-only. |
226 |
void set_empty_key(const key_type& key) { // YOU MUST CALL THIS! |
227 |
rep.set_empty_key(value_type(key, data_type())); // rep wants a value |
228 |
} |
229 |
void set_deleted_key(const key_type& key) { |
230 |
rep.set_deleted_key(value_type(key, data_type())); // rep wants a value |
231 |
} |
232 |
void clear_deleted_key() { rep.clear_deleted_key(); } |
233 |
|
234 |
// These are standard |
235 |
size_type erase(const key_type& key) { return rep.erase(key); } |
236 |
void erase(iterator it) { rep.erase(it); } |
237 |
void erase(iterator f, iterator l) { rep.erase(f, l); } |
238 |
|
239 |
|
240 |
// Comparison |
241 |
bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; } |
242 |
bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; } |
243 |
|
244 |
|
245 |
// I/O -- this is an add-on for writing metainformation to disk |
246 |
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } |
247 |
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } |
248 |
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } |
249 |
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } |
250 |
}; |
251 |
|
252 |
// We need a global swap as well |
253 |
template <class Key, class T, class HashFcn, class EqualKey, class Alloc> |
254 |
inline void swap(dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1, |
255 |
dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) { |
256 |
hm1.swap(hm2); |
257 |
} |
258 |
|
259 |
_END_GOOGLE_NAMESPACE_ |
260 |
|
261 |
#endif /* _DENSE_HASH_MAP_H_ */ |