1 /* 2 * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 */ 22 module dchip.cpHashSet; 23 24 import dchip.chipmunk; 25 import dchip.chipmunk_private; 26 import dchip.chipmunk_types; 27 import dchip.cpArray; 28 import dchip.prime; 29 30 struct cpHashSetBin 31 { 32 void* elt; 33 cpHashValue hash; 34 cpHashSetBin* next; 35 } 36 37 struct cpHashSet 38 { 39 uint entries, size; 40 41 cpHashSetEqlFunc eql; 42 void* default_value; 43 44 cpHashSetBin** table; 45 cpHashSetBin* pooledBins; 46 47 cpArray* allocatedBuffers; 48 } 49 50 void cpHashSetFree(cpHashSet* set) 51 { 52 if (set) 53 { 54 cpfree(set.table); 55 56 cpArrayFreeEach(set.allocatedBuffers, &cpfree); 57 cpArrayFree(set.allocatedBuffers); 58 59 cpfree(set); 60 } 61 } 62 63 cpHashSet* cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc) 64 { 65 cpHashSet* set = cast(cpHashSet*)cpcalloc(1, cpHashSet.sizeof); 66 67 set.size = next_prime(size); 68 set.entries = 0; 69 70 set.eql = eqlFunc; 71 set.default_value = null; 72 73 set.table = cast(cpHashSetBin**)cpcalloc(set.size, (cpHashSetBin*).sizeof); 74 set.pooledBins = null; 75 76 set.allocatedBuffers = cpArrayNew(0); 77 78 return set; 79 } 80 81 void cpHashSetSetDefaultValue(cpHashSet* set, void* default_value) 82 { 83 set.default_value = default_value; 84 } 85 86 int setIsFull(cpHashSet* set) 87 { 88 return (set.entries >= set.size); 89 } 90 91 void cpHashSetResize(cpHashSet* set) 92 { 93 // Get the next approximate doubled prime. 94 uint newSize = next_prime(set.size + 1); 95 96 // Allocate a new table. 97 cpHashSetBin** newTable = cast(cpHashSetBin**)cpcalloc(newSize, (cpHashSetBin*).sizeof); 98 99 // Iterate over the chains. 100 for (uint i = 0; i < set.size; i++) 101 { 102 // Rehash the bins into the new table. 103 cpHashSetBin* bin = set.table[i]; 104 105 while (bin) 106 { 107 cpHashSetBin* next = bin.next; 108 109 cpHashValue idx = bin.hash % newSize; 110 bin.next = newTable[idx]; 111 newTable[idx] = bin; 112 113 bin = next; 114 } 115 } 116 117 cpfree(set.table); 118 119 set.table = newTable; 120 set.size = newSize; 121 } 122 123 void recycleBin(cpHashSet* set, cpHashSetBin* bin) 124 { 125 bin.next = set.pooledBins; 126 set.pooledBins = bin; 127 bin.elt = null; 128 } 129 130 cpHashSetBin* getUnusedBin(cpHashSet* set) 131 { 132 cpHashSetBin* bin = set.pooledBins; 133 134 if (bin) 135 { 136 set.pooledBins = bin.next; 137 return bin; 138 } 139 else 140 { 141 // Pool is exhausted, make more 142 int count = CP_BUFFER_BYTES / cpHashSetBin.sizeof; 143 cpAssertHard(count, "Internal Error: Buffer size is too small."); 144 145 cpHashSetBin* buffer = cast(cpHashSetBin*)cpcalloc(1, CP_BUFFER_BYTES); 146 cpArrayPush(set.allocatedBuffers, buffer); 147 148 // push all but the first one, return it instead 149 for (int i = 1; i < count; i++) 150 recycleBin(set, buffer + i); 151 152 return buffer; 153 } 154 } 155 156 int cpHashSetCount(cpHashSet* set) 157 { 158 return set.entries; 159 } 160 161 void* cpHashSetInsert(cpHashSet* set, cpHashValue hash, void* ptr, void* data, cpHashSetTransFunc trans) 162 { 163 cpHashValue idx = hash % set.size; 164 165 // Find the bin with the matching element. 166 cpHashSetBin* bin = set.table[idx]; 167 168 while (bin && !set.eql(ptr, bin.elt)) 169 bin = bin.next; 170 171 // Create it if necessary. 172 if (!bin) 173 { 174 bin = getUnusedBin(set); 175 bin.hash = hash; 176 bin.elt = (trans ? trans(ptr, data) : data); 177 178 bin.next = set.table[idx]; 179 set.table[idx] = bin; 180 181 set.entries++; 182 183 if (setIsFull(set)) 184 cpHashSetResize(set); 185 } 186 187 return bin.elt; 188 } 189 190 void* cpHashSetRemove(cpHashSet* set, cpHashValue hash, void* ptr) 191 { 192 cpHashValue idx = hash % set.size; 193 194 cpHashSetBin** prev_ptr = &set.table[idx]; 195 cpHashSetBin * bin = set.table[idx]; 196 197 // Find the bin 198 while (bin && !set.eql(ptr, bin.elt)) 199 { 200 prev_ptr = &bin.next; 201 bin = bin.next; 202 } 203 204 // Remove it if it exists. 205 if (bin) 206 { 207 // Update the previous linked list pointer 208 (*prev_ptr) = bin.next; 209 set.entries--; 210 211 void* elt = bin.elt; 212 recycleBin(set, bin); 213 214 return elt; 215 } 216 217 return null; 218 } 219 220 void* cpHashSetFind(cpHashSet* set, cpHashValue hash, void* ptr) 221 { 222 cpHashValue idx = hash % set.size; 223 cpHashSetBin* bin = set.table[idx]; 224 225 while (bin && !set.eql(ptr, bin.elt)) 226 bin = bin.next; 227 228 return (bin ? bin.elt : set.default_value); 229 } 230 231 void cpHashSetEach(cpHashSet* set, cpHashSetIteratorFunc func, void* data) 232 { 233 for (uint i = 0; i < set.size; i++) 234 { 235 cpHashSetBin* bin = set.table[i]; 236 237 while (bin) 238 { 239 cpHashSetBin* next = bin.next; 240 func(bin.elt, data); 241 bin = next; 242 } 243 } 244 } 245 246 void cpHashSetFilter(cpHashSet* set, cpHashSetFilterFunc func, void* data) 247 { 248 for (uint i = 0; i < set.size; i++) 249 { 250 // The rest works similarly to cpHashSetRemove() above. 251 cpHashSetBin** prev_ptr = &set.table[i]; 252 cpHashSetBin * bin = set.table[i]; 253 254 while (bin) 255 { 256 cpHashSetBin* next = bin.next; 257 258 if (func(bin.elt, data)) 259 { 260 prev_ptr = &bin.next; 261 } 262 else 263 { 264 (*prev_ptr) = next; 265 266 set.entries--; 267 recycleBin(set, bin); 268 } 269 270 bin = next; 271 } 272 } 273 }