145
|
1 /* Copyright (C) 2012-2020 Free Software Foundation, Inc.
|
111
|
2
|
|
3 This file is part of GCC.
|
|
4
|
|
5 GCC is free software; you can redistribute it and/or modify it
|
|
6 under the terms of the GNU General Public License as published by
|
|
7 the Free Software Foundation; either version 3, or (at your option)
|
|
8 any later version.
|
|
9
|
|
10 GCC is distributed in the hope that it will be useful, but WITHOUT
|
|
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
12 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
13 License for more details.
|
|
14
|
|
15 Under Section 7 of GPL version 3, you are granted additional
|
|
16 permissions described in the GCC Runtime Library Exception, version
|
|
17 3.1, as published by the Free Software Foundation.
|
|
18
|
|
19 You should have received a copy of the GNU General Public License
|
|
20 and a copy of the GCC Runtime Library Exception along with this
|
|
21 program; see the files COPYING3 and COPYING.RUNTIME respectively.
|
|
22 If not, see <http://www.gnu.org/licenses/>. */
|
|
23
|
|
24 #ifndef _VTV_SET_H
|
|
25 #define _VTV_SET_H 1
|
|
26
|
|
27 /* Code in this file manages a collection of insert-only sets. We
|
|
28 have only tested the case where Key is uintptr_t, though it
|
|
29 theoretically should work for some other cases. All odd keys are
|
|
30 reserved, and must not be inserted into any of the sets. This code
|
|
31 is intended primarily for sets of pointers, and the code is
|
|
32 optimized for small sets (including size 0 and 1), but regardless
|
|
33 of the set size, insert() and contains() have close to O(1) speed
|
|
34 in practice.
|
|
35
|
|
36 TODO(gpike): fix this comment.
|
|
37
|
|
38 Recommended multithreaded use of a set:
|
|
39
|
|
40 For speed, we want to use a lock-free test for set membership. The
|
|
41 code handles simultaneous reads and inserts, as long as at most one
|
|
42 insertion is in progress at a time. After an insert, other threads
|
|
43 may not immediately "see" the inserted key if they perform a
|
|
44 lock-free read, so we recommend retrying, as explained below.
|
|
45
|
|
46 Also, to make data corruption less likely, we recommend using a
|
|
47 "normal" RW page as well as one or pages that are typically RO
|
|
48 but that can be switched to RW and back as needed. The latter
|
|
49 pages should contain sets. The former should contain a lock, L,
|
|
50 and an int or similar, num_writers. Then, to insert, something
|
|
51 like this would be safe:
|
|
52 o Acquire L.
|
|
53 o Increment num_writers; if that made it 1, change pages to RW.
|
|
54 o Release L.
|
|
55 o while (there are insertions to do in some set, S) {
|
|
56 acquire L;
|
|
57 do some insertions in S;
|
|
58 release L;
|
|
59 }
|
|
60 o Acquire L.
|
|
61 o Decrement num_writers; if that made it 0, change pages to RO.
|
|
62 o Release L.
|
|
63
|
|
64 And to check if the set contains some key, one could use
|
|
65 set.contains(key) ||
|
|
66 ({ Acquire L; bool b = set.contains(key); Release L; b; })
|
|
67
|
|
68 In this scheme, the number of threads with reads in progress isn't
|
|
69 tracked, so old sets can never be deleted. In addition, on some
|
|
70 architectures the intentionally racy reads might cause contains()
|
|
71 to return true when it should have returned false. This should be
|
|
72 no problem on x86, and most other machines, where reading or
|
|
73 writing an aligned uintptr_t is atomic. E.g., on those machines,
|
|
74 if *p is 0 and one thread does *p = x while another reads *p, the
|
|
75 read will see either 0 or x.
|
|
76
|
|
77 To make the above easier, the insert_only_hash_sets class provides
|
|
78 an interface to manipulate any number of hash sets. One shouldn't
|
|
79 create objects of that class, as it has no member data and its
|
|
80 methods are static.
|
|
81
|
|
82 So the recommended model is to have a single lock, a single
|
|
83 num_writers variable, and some number of sets. If lock contention
|
|
84 becomes a problem then the sets can be divided into k groups, each
|
|
85 of which has a lock and a num_writers variable; or each set can be
|
|
86 represented as a set of values that equal 0 mod m, a set of values
|
|
87 that equal 1 mod m, ..., plus a set of values that equal m-1 mod m.
|
|
88
|
|
89 However, we expect most or all uses of this code to call contains()
|
|
90 much more frequently than anything else, so lock contention is
|
|
91 likely to be low. */
|
|
92
|
|
93 #include <algorithm>
|
|
94
|
|
95 #ifndef HASHTABLE_STATS
|
|
96 #define HASHTABLE_STATS 0
|
|
97 #endif
|
|
98
|
|
99 #ifndef HASHTABLE_STATS_ATOMIC
|
|
100 #define HASHTABLE_STATS_ATOMIC 0
|
|
101 #endif
|
|
102
|
|
103 #if HASHTABLE_STATS
|
|
104 #if HASHTABLE_STATS_ATOMIC
|
|
105 /* Stat counters, with atomics. */
|
|
106 #include <bits/atomic_word.h>
|
|
107
|
|
108 typedef _Atomic_word _AtomicStatCounter;
|
|
109
|
|
110 void
|
|
111 inc_by (_AtomicStatCounter &stat, int amount)
|
|
112 {
|
|
113 __atomic_add_fetch (&stat, amount, __ATOMIC_ACQ_REL);
|
|
114 }
|
|
115
|
|
116 #else
|
|
117
|
|
118 /* Stat counters, but without atomics. */
|
|
119 typedef int _AtomicStatCounter;
|
|
120
|
|
121 void
|
|
122 inc_by (_AtomicStatCounter& stat, int amount)
|
|
123 {
|
|
124 stat += amount;
|
|
125 }
|
|
126
|
|
127 #endif
|
|
128
|
|
129
|
|
130 /* Number of calls to contains(), insert(), etc. */
|
|
131 extern _AtomicStatCounter stat_insert;
|
|
132 extern _AtomicStatCounter stat_contains;
|
|
133 extern _AtomicStatCounter stat_resize;
|
|
134 extern _AtomicStatCounter stat_create;
|
|
135
|
|
136 /* Sum of set size over all calls to contains(). */
|
|
137 extern _AtomicStatCounter stat_contains_sizes;
|
|
138
|
|
139 /* contains() calls in a set whose capacity is more than 1. */
|
|
140 extern _AtomicStatCounter stat_contains_in_non_trivial_set;
|
|
141
|
|
142 /* Probes in a set whose capacity is more than 1. Ideally, this will
|
|
143 be pretty close to stat_contains_in_non_trivial_set. That will
|
|
144 happen if our hash function is good and/or important keys were
|
|
145 inserted before unimportant keys. */
|
|
146 extern _AtomicStatCounter stat_probes_in_non_trivial_set;
|
|
147
|
|
148 /* number of calls to contains() with size=0, 1, etc. */
|
|
149 extern _AtomicStatCounter stat_contains_size0;
|
|
150 extern _AtomicStatCounter stat_contains_size1;
|
|
151 extern _AtomicStatCounter stat_contains_size2;
|
|
152 extern _AtomicStatCounter stat_contains_size3;
|
|
153 extern _AtomicStatCounter stat_contains_size4;
|
|
154 extern _AtomicStatCounter stat_contains_size5;
|
|
155 extern _AtomicStatCounter stat_contains_size6;
|
|
156 extern _AtomicStatCounter stat_contains_size7;
|
|
157 extern _AtomicStatCounter stat_contains_size8;
|
|
158 extern _AtomicStatCounter stat_contains_size9;
|
|
159 extern _AtomicStatCounter stat_contains_size10;
|
|
160 extern _AtomicStatCounter stat_contains_size11;
|
|
161 extern _AtomicStatCounter stat_contains_size12;
|
|
162 extern _AtomicStatCounter stat_contains_size13_or_more;
|
|
163 extern _AtomicStatCounter stat_grow_from_size0_to_1;
|
|
164 extern _AtomicStatCounter stat_grow_from_size1_to_2;
|
|
165 extern _AtomicStatCounter stat_double_the_number_of_buckets;
|
|
166 extern _AtomicStatCounter stat_insert_key_that_was_already_present;
|
|
167
|
|
168 /* Hash collisions detected during insert_no_resize(). Only counts
|
|
169 hasher(k) == hasher(k'); hasher(k) % tablesize == hasher(k') %
|
|
170 tablesize is not sufficient. Will count collisions that are
|
|
171 detected during table resizes etc., so the same two keys may add to
|
|
172 this stat multiple times. */
|
|
173 extern _AtomicStatCounter stat_insert_found_hash_collision;
|
|
174
|
|
175 #include <string>
|
|
176
|
|
177 struct insert_only_hash_sets_logger
|
|
178 {
|
|
179 static char *
|
|
180 log (char c, char *buf)
|
|
181 {
|
|
182 *buf++ = c;
|
|
183 return buf;
|
|
184 }
|
|
185
|
|
186 static char *
|
|
187 log (const char *s, char *buf)
|
|
188 { return strcpy (buf, s) + strlen (s); }
|
|
189
|
|
190 static char *
|
|
191 log (_AtomicStatCounter i, char *buf)
|
|
192 {
|
|
193 if (i < 10)
|
|
194 return log ((char) ('0' + i), buf);
|
|
195 else
|
|
196 return log ((char) ('0' + i % 10), log (i / 10, buf));
|
|
197 }
|
|
198
|
|
199 static char *
|
|
200 log (const char *label, _AtomicStatCounter i, char *buf)
|
|
201 {
|
|
202 buf = log (label, buf);
|
|
203 buf = log (": ", buf);
|
|
204 buf = log (i, buf);
|
|
205 return log ('\n', buf);
|
|
206 }
|
|
207 };
|
|
208
|
|
209 // Write stats to the given buffer, which should be at least 4000 bytes.
|
|
210 static inline void
|
|
211 insert_only_hash_tables_stats (char *buf)
|
|
212 {
|
|
213 buf = insert_only_hash_sets_logger::log ("insert", stat_insert, buf);
|
|
214 buf = insert_only_hash_sets_logger::log ("contains", stat_contains, buf);
|
|
215 buf = insert_only_hash_sets_logger::log ("resize", stat_resize, buf);
|
|
216 buf = insert_only_hash_sets_logger::log ("create", stat_create, buf);
|
|
217 buf = insert_only_hash_sets_logger::log ("insert_key_that_was_already_"
|
|
218 "present",
|
|
219 stat_insert_key_that_was_already_present,
|
|
220 buf);
|
|
221 buf = insert_only_hash_sets_logger::log ("contains_sizes",
|
|
222 stat_contains_sizes, buf);
|
|
223 buf = insert_only_hash_sets_logger::log ("contains_in_non_trivial_set",
|
|
224 stat_contains_in_non_trivial_set,
|
|
225 buf);
|
|
226 buf = insert_only_hash_sets_logger::log ("probes_in_non_trivial_set",
|
|
227 stat_probes_in_non_trivial_set,
|
|
228 buf);
|
|
229 buf = insert_only_hash_sets_logger::log ("contains_size0",
|
|
230 stat_contains_size0, buf);
|
|
231 buf = insert_only_hash_sets_logger::log ("contains_size1",
|
|
232 stat_contains_size1, buf);
|
|
233 buf = insert_only_hash_sets_logger::log ("contains_size2",
|
|
234 stat_contains_size2, buf);
|
|
235 buf = insert_only_hash_sets_logger::log ("contains_size3",
|
|
236 stat_contains_size3, buf);
|
|
237 buf = insert_only_hash_sets_logger::log ("contains_size4",
|
|
238 stat_contains_size4, buf);
|
|
239 buf = insert_only_hash_sets_logger::log ("contains_size5",
|
|
240 stat_contains_size5, buf);
|
|
241 buf = insert_only_hash_sets_logger::log ("contains_size6",
|
|
242 stat_contains_size6, buf);
|
|
243 buf = insert_only_hash_sets_logger::log ("contains_size7",
|
|
244 stat_contains_size7, buf);
|
|
245 buf = insert_only_hash_sets_logger::log ("contains_size8",
|
|
246 stat_contains_size8, buf);
|
|
247 buf = insert_only_hash_sets_logger::log ("contains_size9",
|
|
248 stat_contains_size9, buf);
|
|
249 buf = insert_only_hash_sets_logger::log ("contains_size10",
|
|
250 stat_contains_size10, buf);
|
|
251 buf = insert_only_hash_sets_logger::log ("contains_size11",
|
|
252 stat_contains_size11, buf);
|
|
253 buf = insert_only_hash_sets_logger::log ("contains_size12",
|
|
254 stat_contains_size12, buf);
|
|
255 buf = insert_only_hash_sets_logger::log ("contains_size13_or_more",
|
|
256 stat_contains_size13_or_more, buf);
|
|
257 buf = insert_only_hash_sets_logger::log ("grow_from_size0_to_1",
|
|
258 stat_grow_from_size0_to_1, buf);
|
|
259 buf = insert_only_hash_sets_logger::log ("grow_from_size1_to_2",
|
|
260 stat_grow_from_size1_to_2, buf);
|
|
261 buf = insert_only_hash_sets_logger::log ("insert_found_hash_collision",
|
|
262 stat_insert_found_hash_collision,
|
|
263 buf);
|
|
264 buf = insert_only_hash_sets_logger::log ("double_the_number_of_buckets",
|
|
265 stat_double_the_number_of_buckets,
|
|
266 buf);
|
|
267 *buf = '\0';
|
|
268 }
|
|
269
|
|
270 #else
|
|
271
|
|
272 /* No stats. */
|
|
273 #define inc_by(statname, amount) do { } while (false && (amount))
|
|
274
|
|
275 #endif
|
|
276
|
|
277 #define inc(statname) inc_by (statname, 1)
|
|
278
|
|
279 template <typename Key, class HashFcn, class Alloc>
|
|
280 class insert_only_hash_sets
|
|
281 {
|
|
282 public:
|
|
283 typedef Key key_type;
|
|
284 typedef size_t size_type;
|
|
285 typedef Alloc alloc_type;
|
|
286 enum { illegal_key = 1 };
|
|
287 enum { min_capacity = 4 };
|
|
288 #if HASHTABLE_STATS
|
|
289 enum { stats = true };
|
|
290 #else
|
|
291 enum { stats = false };
|
|
292 #endif
|
|
293
|
|
294 /* Do not directly use insert_only_hash_set. Instead, use the
|
|
295 static methods below to create and manipulate objects of the
|
|
296 following class.
|
|
297
|
|
298 Implementation details: each set is represented by a pointer
|
|
299 plus, perhaps, out-of-line data, which would be an object of type
|
|
300 insert_only_hash_set. For a pointer, s, the interpretation is: s
|
|
301 == NULL means empty set, lsb(s) == 1 means a set with one
|
|
302 element, which is (uintptr_t)s - 1, and otherwise s is a pointer
|
|
303 of type insert_only_hash_set*. So, to increase the size of a set
|
|
304 we have to change s and/or *s. To check if a set contains some
|
|
305 key we have to examine s and possibly *s. */
|
|
306 class insert_only_hash_set
|
|
307 {
|
|
308 public:
|
|
309 /* Insert a key. The key must not be a reserved key. */
|
|
310 static inline insert_only_hash_set *insert (key_type key,
|
|
311 insert_only_hash_set *s);
|
|
312
|
|
313
|
|
314 /* Create an empty set. */
|
|
315 static inline insert_only_hash_set *create (size_type capacity);
|
|
316
|
|
317 /* Return whether the given key is present. If key is illegal_key
|
|
318 then either true or false may be returned, but for all other
|
|
319 reserved keys false will be returned. */
|
|
320 static bool
|
|
321 contains (key_type key, const insert_only_hash_set *s)
|
|
322 {
|
|
323 if (stats)
|
|
324 {
|
|
325 inc (stat_contains);
|
|
326 switch (size (s))
|
|
327 {
|
|
328 case 0: inc (stat_contains_size0); break;
|
|
329 case 1: inc (stat_contains_size1); break;
|
|
330 case 2: inc (stat_contains_size2); break;
|
|
331 case 3: inc (stat_contains_size3); break;
|
|
332 case 4: inc (stat_contains_size4); break;
|
|
333 case 5: inc (stat_contains_size5); break;
|
|
334 case 6: inc (stat_contains_size6); break;
|
|
335 case 7: inc (stat_contains_size7); break;
|
|
336 case 8: inc (stat_contains_size8); break;
|
|
337 case 9: inc (stat_contains_size9); break;
|
|
338 case 10: inc (stat_contains_size10); break;
|
|
339 case 11: inc (stat_contains_size11); break;
|
|
340 case 12: inc (stat_contains_size12); break;
|
|
341 default: inc (stat_contains_size13_or_more); break;
|
|
342 }
|
|
343 inc_by (stat_contains_sizes, size (s));
|
|
344 }
|
|
345
|
|
346 return (singleton (s) ?
|
|
347 singleton_key (key) == s :
|
|
348 ((s != NULL) && s->contains (key)));
|
|
349 }
|
|
350
|
|
351 /* Return a set's size. */
|
|
352 static size_type
|
|
353 size (const insert_only_hash_set *s)
|
|
354 { return (s == NULL) ? 0 : (singleton (s) ? 1 : s->num_entries); }
|
|
355
|
|
356 static inline insert_only_hash_set *resize (size_type target_num_buckets,
|
|
357 insert_only_hash_set *s);
|
|
358
|
|
359
|
|
360 private:
|
|
361 /* Return whether a set has size 1. */
|
|
362 static bool
|
|
363 singleton (const insert_only_hash_set *s)
|
|
364 { return (uintptr_t) s & 1; }
|
|
365
|
|
366 /* Return the representation of a singleton set containing the
|
|
367 given key. */
|
|
368 static insert_only_hash_set *
|
|
369 singleton_key (key_type key)
|
|
370 { return (insert_only_hash_set *) ((uintptr_t) key + 1); }
|
|
371
|
|
372 /* Given a singleton set, what key does it contain? */
|
|
373 static key_type
|
|
374 extract_singleton_key (const insert_only_hash_set *s)
|
|
375 {
|
|
376 VTV_DEBUG_ASSERT (singleton (s));
|
|
377 return (key_type) ((uintptr_t) s - 1);
|
|
378 }
|
|
379
|
|
380 volatile key_type &
|
|
381 key_at_index (size_type index)
|
|
382 { return buckets[index]; }
|
|
383
|
|
384 key_type
|
|
385 key_at_index (size_type index) const
|
|
386 { return buckets[index]; }
|
|
387
|
|
388 size_type
|
|
389 next_index (size_type index, size_type indices_examined) const
|
|
390 { return (index + indices_examined) & (num_buckets - 1); }
|
|
391
|
|
392 inline void insert_no_resize (key_type key);
|
|
393
|
|
394 inline bool contains (key_type key) const;
|
|
395
|
|
396 inline insert_only_hash_set *resize_if_necessary (void);
|
|
397
|
|
398 size_type num_buckets; /* Must be a power of 2 not less than
|
|
399 min_capacity. */
|
|
400 volatile size_type num_entries;
|
|
401 volatile key_type buckets[0]; /* Actual array size is num_buckets. */
|
|
402 };
|
|
403
|
|
404 /* Create an empty set with the given capacity. Requires that n be
|
|
405 0 or a power of 2. If 1 < n < min_capacity then treat n as
|
|
406 min_capacity. Sets *handle. Returns true unless the allocator
|
|
407 fails. Subsequent operations on this set should use the same
|
|
408 handle. */
|
|
409
|
|
410 static inline bool create (size_type n, insert_only_hash_set **handle);
|
|
411
|
|
412 /* Force the capacity of a set to be n, unless it was more than n
|
|
413 already. Requires that n be 0 or a power of 2. Sets *handle
|
|
414 unless the current capacity is n or more. Returns true unless
|
|
415 the allocator fails. */
|
|
416
|
|
417 static inline bool resize (size_type n, insert_only_hash_set **handle);
|
|
418
|
|
419 /* Insert a key. *handle is unmodified unless (1) a resize occurs,
|
|
420 or (2) the set was initially empty. Returns true unless the
|
|
421 allocator fails during a resize. If the allocator fails during a
|
|
422 resize then the set is reset to be the empty set. The key must
|
|
423 not be a reserved key. */
|
|
424
|
|
425 static inline bool insert (key_type key, insert_only_hash_set **handle);
|
|
426
|
|
427 /* Check for the presence of a key. If key is illegal_key then
|
|
428 either true or false may be returned, but for all other reserved
|
|
429 keys false will be returned. */
|
|
430
|
|
431 static inline bool
|
|
432 contains (key_type key, /* const */ insert_only_hash_set **handle)
|
|
433 { return insert_only_hash_set::contains (key, *handle); }
|
|
434
|
|
435 /* Return the size of the given set. */
|
|
436 static size_type
|
|
437 size (const insert_only_hash_set **handle)
|
|
438 { return insert_only_hash_set::size (*handle); }
|
|
439
|
|
440 static bool
|
|
441 is_reserved_key (key_type key)
|
|
442 { return ((uintptr_t) key % 2) == 1; }
|
|
443 };
|
|
444
|
|
445 template <typename Key, class HashFcn, class Alloc>
|
|
446 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
|
|
447 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::resize
|
|
448 (size_type n, insert_only_hash_set *s)
|
|
449 {
|
|
450 if (s == NULL)
|
|
451 return create (n);
|
|
452
|
|
453 size_type capacity = singleton (s) ? 1 : s->num_buckets;
|
|
454
|
|
455 if (n <= capacity)
|
|
456 return s;
|
|
457
|
|
458 insert_only_hash_set *result =
|
|
459 create (std::max<size_type> (n, min_capacity));
|
|
460 if (result != NULL)
|
|
461 {
|
|
462 if (singleton (s))
|
|
463 {
|
|
464 result->insert_no_resize (extract_singleton_key (s));
|
|
465 }
|
|
466 else
|
|
467 {
|
|
468 for (size_type i = 0; i < s->num_buckets; i++)
|
|
469 if (s->buckets[i] != (key_type) illegal_key)
|
|
470 result->insert_no_resize (s->buckets[i]);
|
|
471 }
|
|
472 VTV_DEBUG_ASSERT (size (result) == size (s));
|
|
473 }
|
|
474 return result;
|
|
475 }
|
|
476
|
|
477 template <typename Key, class HashFcn, class Alloc>
|
|
478 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
|
|
479 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::insert
|
|
480 (key_type key, insert_only_hash_set *s)
|
|
481 {
|
|
482 VTV_DEBUG_ASSERT (!is_reserved_key (key));
|
|
483
|
|
484 inc_by (stat_grow_from_size0_to_1, s == NULL);
|
|
485
|
|
486 if (s == NULL)
|
|
487 return singleton_key (key);
|
|
488
|
|
489 if (singleton (s))
|
|
490 {
|
|
491 const key_type old_key = extract_singleton_key (s);
|
|
492 if (old_key == key)
|
|
493 return s;
|
|
494
|
|
495 /* Grow from size 1 to size 2. */
|
|
496 inc (stat_grow_from_size1_to_2);
|
|
497 s = create (2);
|
|
498 if (s == NULL)
|
|
499 return NULL;
|
|
500
|
|
501 s->insert_no_resize (old_key);
|
|
502 s->insert_no_resize (key);
|
|
503 VTV_DEBUG_ASSERT (size (s) == 2);
|
|
504 return s;
|
|
505 }
|
|
506 s = s->resize_if_necessary();
|
|
507 if (s != NULL)
|
|
508 s->insert_no_resize (key);
|
|
509 return s;
|
|
510 }
|
|
511
|
|
512 template <typename Key, class HashFcn, class Alloc>
|
|
513 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
|
|
514 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::create
|
|
515 (size_type capacity)
|
|
516 {
|
|
517 if (capacity <= 1)
|
|
518 return NULL;
|
|
519
|
|
520 VTV_DEBUG_ASSERT (capacity > 1 && (capacity & (capacity - 1)) == 0);
|
|
521 VTV_DEBUG_ASSERT (sizeof (insert_only_hash_set) == 2 * sizeof (size_type));
|
|
522 capacity = std::max <size_type> (capacity, min_capacity);
|
|
523 const size_t num_bytes = sizeof (insert_only_hash_set) +
|
|
524 sizeof (key_type) * capacity;
|
|
525 alloc_type alloc;
|
|
526 insert_only_hash_set *result = (insert_only_hash_set *) alloc (num_bytes);
|
|
527 result->num_buckets = capacity;
|
|
528 result->num_entries = 0;
|
|
529 for (size_type i = 0; i < capacity; i++)
|
|
530 result->buckets[i] = (key_type) illegal_key;
|
|
531 return result;
|
|
532 }
|
|
533
|
|
534 template <typename Key, class HashFcn, class Alloc>
|
|
535 void
|
|
536 insert_only_hash_sets<Key, HashFcn,
|
|
537 Alloc>::insert_only_hash_set::insert_no_resize
|
|
538 (key_type key)
|
|
539 {
|
|
540 HashFcn hasher;
|
|
541 const size_type capacity = num_buckets;
|
|
542 VTV_DEBUG_ASSERT (capacity >= min_capacity);
|
|
543 VTV_DEBUG_ASSERT (!is_reserved_key (key));
|
|
544 size_type index = hasher (key) & (capacity - 1);
|
|
545 key_type k = key_at_index (index);
|
|
546 size_type indices_examined = 0;
|
|
547 while (k != key)
|
|
548 {
|
|
549 ++indices_examined;
|
|
550 if (k == (key_type) illegal_key)
|
|
551 {
|
|
552 key_at_index (index) = key;
|
|
553 ++num_entries;
|
|
554 return;
|
|
555 }
|
|
556 else
|
|
557 {
|
|
558 inc_by (stat_insert_found_hash_collision,
|
|
559 hasher (k) == hasher (key));
|
|
560 }
|
|
561 VTV_DEBUG_ASSERT (indices_examined < capacity);
|
|
562 index = next_index (index, indices_examined);
|
|
563 k = key_at_index (index);
|
|
564 }
|
|
565 }
|
|
566
|
|
567 template<typename Key, class HashFcn, class Alloc>
|
|
568 bool
|
|
569 insert_only_hash_sets<Key, HashFcn, Alloc>::insert_only_hash_set::contains
|
|
570 (key_type key) const
|
|
571 {
|
|
572 inc (stat_contains_in_non_trivial_set);
|
|
573 HashFcn hasher;
|
|
574 const size_type capacity = num_buckets;
|
|
575 size_type index = hasher (key) & (capacity - 1);
|
|
576 key_type k = key_at_index (index);
|
|
577 size_type indices_examined = 0;
|
|
578 inc (stat_probes_in_non_trivial_set);
|
|
579 while (k != key)
|
|
580 {
|
|
581 ++indices_examined;
|
|
582 if (/*UNLIKELY*/(k == (key_type) illegal_key
|
|
583 || indices_examined == capacity))
|
|
584 return false;
|
|
585
|
|
586 index = next_index (index, indices_examined);
|
|
587 k = key_at_index (index);
|
|
588 inc (stat_probes_in_non_trivial_set);
|
|
589 }
|
|
590 return true;
|
|
591 }
|
|
592
|
|
593 template <typename Key, class HashFcn, class Alloc>
|
|
594 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
|
|
595 insert_only_hash_sets<Key, HashFcn,
|
|
596 Alloc>::insert_only_hash_set::resize_if_necessary (void)
|
|
597 {
|
|
598 VTV_DEBUG_ASSERT (num_buckets >= min_capacity);
|
|
599 size_type unused = num_buckets - num_entries;
|
|
600 if (unused < (num_buckets >> 2))
|
|
601 {
|
|
602 inc (stat_double_the_number_of_buckets);
|
|
603 size_type new_num_buckets = num_buckets * 2;
|
|
604 insert_only_hash_set *s = create (new_num_buckets);
|
|
605 for (size_type i = 0; i < num_buckets; i++)
|
|
606 if (buckets[i] != (key_type) illegal_key)
|
|
607 s->insert_no_resize (buckets[i]);
|
|
608 VTV_DEBUG_ASSERT (size (this) == size (s));
|
|
609 return s;
|
|
610 }
|
|
611 else
|
|
612 return this;
|
|
613 }
|
|
614
|
|
615 template<typename Key, class HashFcn, class Alloc>
|
|
616 bool
|
|
617 insert_only_hash_sets<Key, HashFcn, Alloc>::create (size_type n,
|
|
618 insert_only_hash_set **handle)
|
|
619
|
|
620 {
|
|
621 inc (stat_create);
|
|
622 *handle = insert_only_hash_set::create (n);
|
|
623 return (n <= 1) || (*handle != NULL);
|
|
624 }
|
|
625
|
|
626 template<typename Key, class HashFcn, class Alloc>
|
|
627 bool
|
|
628 insert_only_hash_sets<Key, HashFcn, Alloc>::resize (size_type n,
|
|
629 insert_only_hash_set **handle)
|
|
630 {
|
|
631 inc (stat_resize);
|
|
632 *handle = insert_only_hash_set::resize (n, *handle);
|
|
633 return (n <= 1) || (*handle != NULL);
|
|
634 }
|
|
635
|
|
636 template<typename Key, class HashFcn, class Alloc>
|
|
637 bool
|
|
638 insert_only_hash_sets<Key, HashFcn, Alloc>::insert (key_type key,
|
|
639 insert_only_hash_set **handle)
|
|
640 {
|
|
641 inc (stat_insert);
|
|
642 const size_type old_size = insert_only_hash_set::size (*handle);
|
|
643 *handle = insert_only_hash_set::insert (key, *handle);
|
|
644 if (*handle != NULL)
|
|
645 {
|
|
646 const size_type delta = insert_only_hash_set::size (*handle) - old_size;
|
|
647 inc_by (stat_insert_key_that_was_already_present, delta == 0);
|
|
648 }
|
|
649 return *handle != NULL;
|
|
650 }
|
|
651
|
|
652 #endif /* VTV_SET_H */
|