5817946 [rkeene@sledge /home/rkeene/tmp]$ cat -n hat.c
  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
5817947 [rkeene@sledge /home/rkeene/tmp]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 2012-01-05 16:21:36