1 /* Copyright (C) 2012-2021 Free Software Foundation, Inc.
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
inc_by(_AtomicStatCounter & stat,int amount)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
inc_by(_AtomicStatCounter & stat,int amount)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 *
loginsert_only_hash_sets_logger180 log (char c, char *buf)
181 {
182 *buf++ = c;
183 return buf;
184 }
185
186 static char *
loginsert_only_hash_sets_logger187 log (const char *s, char *buf)
188 { return strcpy (buf, s) + strlen (s); }
189
190 static char *
loginsert_only_hash_sets_logger191 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 *
loginsert_only_hash_sets_logger200 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
insert_only_hash_tables_stats(char * buf)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
contains(key_type key,const insert_only_hash_set * s)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
size(const insert_only_hash_set * s)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
singleton(const insert_only_hash_set * s)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 *
singleton_key(key_type key)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
extract_singleton_key(const insert_only_hash_set * s)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 &
key_at_index(size_type index)381 key_at_index (size_type index)
382 { return buckets[index]; }
383
384 key_type
key_at_index(size_type index)385 key_at_index (size_type index) const
386 { return buckets[index]; }
387
388 size_type
next_index(size_type index,size_type indices_examined)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
contains(key_type key,insert_only_hash_set ** handle)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
size(const insert_only_hash_set ** handle)437 size (const insert_only_hash_set **handle)
438 { return insert_only_hash_set::size (*handle); }
439
440 static bool
is_reserved_key(key_type key)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 *
resize(size_type n,insert_only_hash_set * s)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 *
insert(key_type key,insert_only_hash_set * s)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 *
create(size_type capacity)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,
insert_no_resize(key_type key)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
contains(key_type key)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,
resize_if_necessary(void)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
create(size_type n,insert_only_hash_set ** handle)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
resize(size_type n,insert_only_hash_set ** handle)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
insert(key_type key,insert_only_hash_set ** handle)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 */
653