1 /* Copyright (C) 2012-2020 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