1 /* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 #include "chipmunk/chipmunk_private.h"
25 typedef struct cpHashSetBin {
28 struct cpHashSetBin *next;
32 unsigned int entries, size;
38 cpHashSetBin *pooledBins;
40 cpArray *allocatedBuffers;
44 cpHashSetFree(cpHashSet *set)
49 cpArrayFreeEach(set->allocatedBuffers, cpfree);
50 cpArrayFree(set->allocatedBuffers);
57 cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc)
59 cpHashSet *set = (cpHashSet *)cpcalloc(1, sizeof(cpHashSet));
61 set->size = next_prime(size);
65 set->default_value = NULL;
67 set->table = (cpHashSetBin **)cpcalloc(set->size, sizeof(cpHashSetBin *));
68 set->pooledBins = NULL;
70 set->allocatedBuffers = cpArrayNew(0);
76 cpHashSetSetDefaultValue(cpHashSet *set, void *default_value)
78 set->default_value = default_value;
82 setIsFull(cpHashSet *set)
84 return (set->entries >= set->size);
88 cpHashSetResize(cpHashSet *set)
90 // Get the next approximate doubled prime.
91 unsigned int newSize = next_prime(set->size + 1);
92 // Allocate a new table.
93 cpHashSetBin **newTable = (cpHashSetBin **)cpcalloc(newSize, sizeof(cpHashSetBin *));
95 // Iterate over the chains.
96 for(unsigned int i=0; i<set->size; i++){
97 // Rehash the bins into the new table.
98 cpHashSetBin *bin = set->table[i];
100 cpHashSetBin *next = bin->next;
102 cpHashValue idx = bin->hash%newSize;
103 bin->next = newTable[idx];
112 set->table = newTable;
117 recycleBin(cpHashSet *set, cpHashSetBin *bin)
119 bin->next = set->pooledBins;
120 set->pooledBins = bin;
124 static cpHashSetBin *
125 getUnusedBin(cpHashSet *set)
127 cpHashSetBin *bin = set->pooledBins;
130 set->pooledBins = bin->next;
133 // Pool is exhausted, make more
134 int count = CP_BUFFER_BYTES/sizeof(cpHashSetBin);
135 cpAssertHard(count, "Internal Error: Buffer size is too small.");
137 cpHashSetBin *buffer = (cpHashSetBin *)cpcalloc(1, CP_BUFFER_BYTES);
138 cpArrayPush(set->allocatedBuffers, buffer);
140 // push all but the first one, return it instead
141 for(int i=1; i<count; i++) recycleBin(set, buffer + i);
147 cpHashSetCount(cpHashSet *set)
153 cpHashSetInsert(cpHashSet *set, cpHashValue hash, const void *ptr, cpHashSetTransFunc trans, void *data)
155 cpHashValue idx = hash%set->size;
157 // Find the bin with the matching element.
158 cpHashSetBin *bin = set->table[idx];
159 while(bin && !set->eql(ptr, bin->elt))
162 // Create it if necessary.
164 bin = getUnusedBin(set);
166 bin->elt = (trans ? trans(ptr, data) : data);
168 bin->next = set->table[idx];
169 set->table[idx] = bin;
172 if(setIsFull(set)) cpHashSetResize(set);
179 cpHashSetRemove(cpHashSet *set, cpHashValue hash, const void *ptr)
181 cpHashValue idx = hash%set->size;
183 cpHashSetBin **prev_ptr = &set->table[idx];
184 cpHashSetBin *bin = set->table[idx];
187 while(bin && !set->eql(ptr, bin->elt)){
188 prev_ptr = &bin->next;
192 // Remove it if it exists.
194 // Update the previous linked list pointer
195 (*prev_ptr) = bin->next;
198 const void *elt = bin->elt;
199 recycleBin(set, bin);
208 cpHashSetFind(cpHashSet *set, cpHashValue hash, const void *ptr)
210 cpHashValue idx = hash%set->size;
211 cpHashSetBin *bin = set->table[idx];
212 while(bin && !set->eql(ptr, bin->elt))
215 return (bin ? bin->elt : set->default_value);
219 cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data)
221 for(unsigned int i=0; i<set->size; i++){
222 cpHashSetBin *bin = set->table[i];
224 cpHashSetBin *next = bin->next;
225 func(bin->elt, data);
232 cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data)
234 for(unsigned int i=0; i<set->size; i++){
235 // The rest works similarly to cpHashSetRemove() above.
236 cpHashSetBin **prev_ptr = &set->table[i];
237 cpHashSetBin *bin = set->table[i];
239 cpHashSetBin *next = bin->next;
241 if(func(bin->elt, data)){
242 prev_ptr = &bin->next;
247 recycleBin(set, bin);