1 /*******************************************************************************
2 
3     Class implementing a set of hashes. The set is built on top of an efficient
4     bucket algorithm, allowing for fast look up of hashes in the set.
5 
6     Usage example:
7 
8     ---
9 
10         import ocean.util.container.map.HashSet;
11 
12         // A set of hash_t's
13         auto set = new HashSet;
14 
15         hash_t hash = 232323;
16 
17         // Add a hash
18         set.put(hash);
19 
20         // Check if a hash exists in the set (null if not found)
21         auto exists = hash in set;
22 
23         // Remove a hash from the set
24         set.remove(hash);
25 
26         // Clear the set
27         set.clear();
28 
29     ---
30 
31     Copyright:
32         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
33         All rights reserved.
34 
35     License:
36         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
37         Alternatively, this file may be distributed under the terms of the Tango
38         3-Clause BSD License (see LICENSE_BSD.txt for details).
39 
40 *******************************************************************************/
41 
42 module ocean.util.container.map.HashSet;
43 
44 
45 
46 
47 import ocean.util.container.map.Set;
48 
49 version (UnitTestVerbose) import ocean.io.Stdout;
50 version (unittest) import ocean.core.Test;
51 
52 
53 
54 /*******************************************************************************
55 
56     Debug switch for verbose unittest output (uncomment if desired)
57 
58 *******************************************************************************/
59 
60 //version = UnitTestVerbose;
61 
62 version ( UnitTestVerbose )
63 {
64     import ocean.io.Stdout;
65 }
66 
67 /*******************************************************************************
68 
69     HashSet class. Manages a set of hash_t's with fast lookup.
70 
71 *******************************************************************************/
72 
73 public class HashSet : Set!(hash_t)
74 {
75     /***************************************************************************
76 
77         Constructor, sets the number of buckets to n * load_factor
78 
79         Params:
80             n = expected number of elements
81             load_factor = ratio of n to the number of internal buckets. The
82                 desired (approximate) number of elements per bucket. For
83                 example, 0.5 sets the number of buckets to double n; for 2 the
84                 number of buckets is the half of n. load_factor must be greater
85                 than 0. The load factor is basically a trade-off between memory
86                 usage (number of buckets) and search time (number of elements
87                 per bucket).
88 
89     ***************************************************************************/
90 
91     public this ( size_t n, float load_factor = 0.75 )
92     {
93         super(n, load_factor);
94     }
95 
96     /***************************************************************************
97 
98         Constructor.
99 
100         Params:
101             allocator = custom bucket elements allocator
102             n = expected number of elements in mapping
103             load_factor = ratio of n to the number of internal buckets. The
104                 desired (approximate) number of elements per bucket. For
105                 example, 0.5 sets the number of buckets to double n; for 2 the
106                 number of buckets is the half of n. load_factor must be greater
107                 than 0. The load factor is basically a trade-off between memory
108                 usage (number of buckets) and search time (number of elements
109                 per bucket).
110 
111     ***************************************************************************/
112 
113     public this ( IAllocator allocator, size_t n, float load_factor = 0.75 )
114     {
115         super(allocator, n, load_factor);
116     }
117 
118     /***************************************************************************
119 
120         Calculates the hash value from key. Uses the identity since key is
121         expected to be a suitable hash value.
122 
123         Params:
124             key = key to hash
125 
126         Returns:
127             the hash value that corresponds to key, which is key itself.
128 
129     ***************************************************************************/
130 
131     public override hash_t toHash ( in hash_t key )
132     {
133         return key;
134     }
135 }
136 
137 /***************************************************************************
138 
139     HashSet unittest.
140 
141 ***************************************************************************/
142 
143 unittest
144 {
145     version ( UnitTestVerbose )
146     {
147         Stdout.formatln("{} unittest ---------------",
148             typeof(this).stringof);
149         scope ( exit ) Stdout.formatln("{} unittest ---------------",
150            typeof(this).stringof);
151     }
152 
153     scope set = new HashSet(10);
154 
155     version ( UnitTestVerbose ) void printState ( )
156     {
157         Stdout.formatln("  ::  len={}, load={}, max_load={}, pool={} ({} busy)",
158             set.length, set.load, set.max_load,
159             set.bucket_elements.length, set.bucket_elements.num_busy);
160     }
161 
162     bool lengthIs ( size_t expected )
163     {
164         test(set.length == expected);
165 
166         int c;
167         foreach ( k; set )
168         {
169             c++;
170         }
171         return c == expected;
172     }
173 
174     void put ( hash_t key, bool should_exist )
175     {
176         auto len = set.length;
177 
178         test(!!(key in set) == should_exist);
179 
180         auto e = set.put(key);
181         version ( UnitTestVerbose )
182         {
183             Stdout.format("put {}: {}", key, e);
184             printState();
185         }
186 
187         test(key in set);
188         test(lengthIs(len + (should_exist ? 0 : 1)));
189     }
190 
191     void remove ( hash_t key, bool should_exist )
192     {
193         auto len = set.length;
194         auto pool_len = set.bucket_info.num_buckets;
195 
196         test(!!(key in set) == should_exist);
197 
198         auto e = set.remove(key);
199         version ( UnitTestVerbose )
200         {
201             Stdout.format("remove {}: {}", key, e);
202             printState();
203         }
204 
205         test(!(key in set));
206         test(lengthIs(len - (should_exist ? 1 : 0)));
207         test(pool_len == set.bucket_info.num_buckets);
208     }
209 
210     void clear ( )
211     {
212         auto pool_len = set.bucket_info.num_buckets;
213 
214         set.clear();
215         version ( UnitTestVerbose )
216         {
217             Stdout.format("clear");
218             printState();
219         }
220 
221         test(lengthIs(0));
222 
223         test(pool_len == set.bucket_info.num_buckets);
224     }
225 
226     put(4711, false);   // put
227     put(4711, true);    // double put
228     put(23, false);     // put
229     put(12, false);     // put
230     remove(23, true);   // remove
231     remove(23, false);  // double remove
232     put(23, false);     // put
233     put(23, true);      // double put
234 
235     clear();
236 
237     put(4711, false);   // put
238     put(11, false);     // put
239     put(11, true);      // double put
240     put(12, false);     // put
241     remove(23, false);  // remove
242     put(23, false);     // put
243     put(23, true);      // double put
244 
245     clear();
246 }