1 /* Released Into the Public Domain By: Roy Keene, 2012 */ 2 3 #include <assert.h> 4 #include <stdlib.h> 5 #include <limits.h> 6 7 #ifndef HAT_DEFAULT_PAGESIZE 8 #define HAT_DEFAULT_PAGESIZE 4096 9 #endif 10 11 struct hat { 12 unsigned int baselen; 13 unsigned int numleaves; 14 void ***leaves; 15 }; 16 17 /* Compute the leaf index a given index is under */ 18 static unsigned int hat_compute_leaf(unsigned int baselen, unsigned int idx) { 19 unsigned int leaf; 20 unsigned int base; 21 22 /* This computes the leaf node using integer addition and multiplication */ 23 for (base = leaf = 0; base <= idx; leaf++) { 24 base += baselen; 25 baselen *= 2; 26 } 27 leaf--; 28 29 return(leaf); 30 } 31 32 /* Compute the number of items in a given leaf index */ 33 static unsigned int hat_compute_numitems_leaf(unsigned int baselen, unsigned int leaf) { 34 unsigned int numitemsthisleaf; 35 36 if (leaf == 0) { 37 numitemsthisleaf = baselen; 38 } else { 39 numitemsthisleaf = (baselen * (2 << (leaf - 1))); 40 } 41 42 return(numitemsthisleaf); 43 } 44 45 /* Returns the leaf index and item index for a given linear index */ 46 static void hat_get_idx(unsigned int baselen, unsigned int idx, unsigned int *leaf_p, unsigned int *leaf_idx_p) { 47 unsigned int leaf, leaf_idx; 48 49 assert(leaf_p != NULL); 50 assert(leaf_idx_p != NULL); 51 52 leaf = hat_compute_leaf(baselen, idx); 53 54 if (leaf == 0) { 55 leaf_idx = idx; 56 } else { 57 leaf_idx = idx - (baselen * ((2 << (leaf - 1)) - 1)); 58 } 59 60 assert(leaf <= idx); 61 assert(leaf_idx <= idx); 62 63 *leaf_p = leaf; 64 *leaf_idx_p = leaf_idx; 65 66 return; 67 } 68 69 /* Grows the structure such that it will be have "newnumleaves" number of leaf indexes */ 70 static void hat_alloc_leaves(struct hat *hat, unsigned int newnumleaves) { 71 unsigned int oldnumleaves, numitemsthisleaf, idx; 72 size_t leaves_new_size; 73 struct hat tmp_hat; 74 75 assert(hat != NULL); 76 assert(newnumleaves >= hat->numleaves); 77 78 oldnumleaves = hat->numleaves; 79 leaves_new_size = sizeof(*(hat->leaves)) * newnumleaves; 80 81 if (hat->leaves == NULL) { 82 tmp_hat.leaves = malloc(leaves_new_size); 83 } else { 84 tmp_hat.leaves = realloc(hat->leaves, leaves_new_size); 85 } 86 87 if (tmp_hat.leaves == NULL) { 88 return; 89 } 90 91 hat->leaves = tmp_hat.leaves; 92 93 for (idx = oldnumleaves; idx < newnumleaves; idx++) { 94 tmp_hat.leaves[idx] = NULL; 95 } 96 97 for (idx = oldnumleaves; idx < newnumleaves; idx++) { 98 numitemsthisleaf = hat_compute_numitems_leaf(hat->baselen, idx); 99 100 tmp_hat.leaves[idx] = malloc(sizeof(**(tmp_hat.leaves)) * numitemsthisleaf); 101 102 if (tmp_hat.leaves[idx] == NULL) { 103 for (idx = oldnumleaves; idx < newnumleaves; idx++) { 104 if (tmp_hat.leaves[idx] != NULL) { 105 free(tmp_hat.leaves[idx]); 106 } 107 } 108 109 return; 110 } 111 } 112 113 hat->numleaves = newnumleaves; 114 115 return; 116 } 117 118 /* Determine system page size */ 119 #ifdef HAT_FEATURE_USE_GETPAGESIZE 120 #include <unistd.h> 121 #endif 122 static unsigned long hat_getpagesize(void) { 123 #ifdef HAT_FEATURE_USE_GETPAGESIZE 124 static long pagesize = -1; 125 126 if (pagesize != -1) { 127 return(pagesize); 128 } 129 130 pagesize = sysconf(_SC_PAGESIZE); 131 132 if (pagesize > 0) { 133 return(pagesize); 134 } 135 #endif 136 137 return(HAT_DEFAULT_PAGESIZE); 138 } 139 140 /* Determine optimal baselen size for a given page size */ 141 static unsigned int hat_get_baselen(void) { 142 unsigned long pagesize; 143 unsigned int baselen; 144 145 /* Get OS page size */ 146 pagesize = hat_getpagesize(); 147 148 /* Compute the number of entries that will fit in a page */ 149 baselen = pagesize / sizeof(void *); 150 151 /* Leave some room in the page for "buddy info" */ 152 baselen -= 2; 153 154 return(baselen); 155 } 156 157 static unsigned int hat_get_baselen_from_max(unsigned int maxidx) { 158 unsigned long best_baselen_size, best_baselen; 159 unsigned long pagesize, baselen_size; 160 unsigned int baselen; 161 unsigned int leaf; 162 163 /* Get OS page size */ 164 pagesize = hat_getpagesize(); 165 pagesize -= sizeof(void *) * 2; 166 167 /* 168 * Really we are finding the start of the block we hope not to have to 169 * allocate, rather than the end of the current block 170 */ 171 maxidx++; 172 173 best_baselen_size = 0; 174 for (leaf = 2; leaf < 64; leaf++) { 175 baselen = (maxidx / ((2 << (leaf - 1)) - 1)); 176 baselen_size = baselen * sizeof(void *); 177 baselen_size = baselen_size % pagesize; 178 179 /* Don't create too large of a baselen to inhibit growth rate (and waste) for the average case */ 180 if (baselen > 16384) { 181 continue; 182 } 183 184 if (baselen < 2) { 185 break; 186 } 187 188 if (baselen_size == 0) { 189 baselen_size = pagesize; 190 } 191 192 if (baselen_size >= best_baselen_size) { 193 best_baselen_size = baselen_size; 194 best_baselen = baselen; 195 } 196 } 197 198 if (best_baselen_size != 0) { 199 return(best_baselen + 1); 200 } 201 202 return(hat_get_baselen()); 203 } 204 205 /* Initialize a "hat" structure */ 206 int hat_init(struct hat *hat, unsigned int baselen, unsigned int numleaves, unsigned int maxidx) { 207 assert(hat != NULL); 208 209 if (baselen == 0) { 210 if (maxidx == 0) { 211 baselen = hat_get_baselen(); 212 } else { 213 baselen = hat_get_baselen_from_max(maxidx); 214 } 215 } 216 217 if (numleaves == 0) { 218 numleaves = 1; 219 } 220 221 hat->baselen = baselen; 222 hat->numleaves = 0; 223 hat->leaves = NULL; 224 225 hat_alloc_leaves(hat, numleaves); 226 227 return(0); 228 } 229 230 /* De-initialize and free a "hat" structure */ 231 void hat_free(struct hat *hat) { 232 unsigned int leaf; 233 234 assert(hat != NULL); 235 236 if (hat->leaves == NULL) { 237 assert(hat->numleaves == 0); 238 239 return; 240 } 241 242 for (leaf = 0; leaf < hat->numleaves; leaf++) { 243 assert(hat->leaves[leaf] != NULL); 244 245 free(hat->leaves[leaf]); 246 } 247 248 free(hat->leaves); 249 250 return; 251 } 252 253 /* Set an item */ 254 int hat_set(struct hat *hat, unsigned int idx, void *value) { 255 unsigned int leaf, leaf_idx; 256 257 assert(hat != NULL); 258 259 hat_get_idx(hat->baselen, idx, &leaf, &leaf_idx); 260 261 if (leaf >= hat->numleaves) { 262 hat_alloc_leaves(hat, leaf + 1); 263 264 if (leaf >= hat->numleaves) { 265 return(-1); 266 } 267 } 268 269 hat->leaves[leaf][leaf_idx] = value; 270 271 return(0); 272 } 273 274 /* Get an item */ 275 int hat_get(struct hat *hat, unsigned int idx, void **value_p) { 276 unsigned int leaf, leaf_idx; 277 278 assert(hat != NULL); 279 assert(value_p != NULL); 280 281 hat_get_idx(hat->baselen, idx, &leaf, &leaf_idx); 282 283 if (leaf >= hat->numleaves) { 284 return(-1); 285 } 286 287 *value_p = hat->leaves[leaf][leaf_idx]; 288 289 return(0); 290 } 291 292 #ifdef test_driver 293 /* Test and example code on how to use this implementation */ 294 295 #include <stdio.h> 296 297 int main(int argc, char **argv) { 298 struct hat hat; 299 unsigned int idx; 300 char *word; 301 int chk_rv; 302 303 chk_rv = hat_init(&hat, 1022, 1, 0); 304 if (chk_rv != 0) { 305 fprintf(stderr, "hat_init() failed\n"); 306 307 return(1); 308 } 309 310 /* Test immediate destruction */ 311 hat_free(&hat); 312 313 /* Re-create for further testing */ 314 hat_init(&hat, 0, 1, 268435455); 315 316 /* Test setting 5000 initial values */ 317 for (idx = 0; idx < 5000; idx++) { 318 chk_rv = hat_set(&hat, idx, NULL); 319 320 if (chk_rv != 0) { 321 fprintf(stderr, "hat_set() failed\n"); 322 323 return(1); 324 } 325 } 326 327 /* Test setting to specific values and then retreiving them */ 328 hat_set(&hat, 0, "This"); 329 hat_set(&hat, 1, "is"); 330 hat_set(&hat, 2, "a"); 331 hat_set(&hat, 3, "test"); 332 hat_set(&hat, 4, "bob"); 333 hat_set(&hat, 10, "ten"); 334 hat_set(&hat, 20, "twenty"); 335 hat_set(&hat, 40, "fourty"); 336 hat_set(&hat, 80, "eighty"); 337 hat_set(&hat, 160, "one hundred sixty"); 338 hat_set(&hat, 320, "three hundred twenty"); 339 hat_set(&hat, 321, "three hundred twenty-one"); 340 hat_set(&hat, 640, "six hundred fourty"); 341 chk_rv = hat_set(&hat, 268435455, "big"); 342 if (chk_rv != 0) { 343 fprintf(stderr, "hat_set(..., 268435455, ...) failed\n"); 344 } 345 346 for (idx = 0; idx <= 641; idx++) { 347 if (idx == 5) idx = 10; 348 if (idx == 11) idx = 20; 349 if (idx == 21) idx = 40; 350 if (idx == 41) idx = 80; 351 if (idx == 81) idx = 160; 352 if (idx == 161) idx = 320; 353 if (idx == 322) idx = 640; 354 if (idx == 641) idx = 268435455; 355 356 chk_rv = hat_get(&hat, idx, (void **) &word); 357 if (chk_rv != 0) { 358 fprintf(stderr, "hat_get() failed\n"); 359 360 continue; 361 } 362 363 printf("hat[%u] = %s\n", idx, word); 364 } 365 366 /* Test destruction */ 367 hat_free(&hat); 368 369 return(0); 370 371 /* NOTREACHED */ 372 argc = argc; 373 argv = argv; 374 } 375 #endif |