source: dict.c @ 7cd5878

release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 7cd5878 was 7cd5878, checked in by David Benjamin <davidben@mit.edu>, 12 years ago
Implement binary search in dict.c Just because there's a TODO and it was asking for it. Signed-off-by: David Benjamin <davidben@mit.edu>
  • Property mode set to 100644
File size: 5.5 KB
Line 
1/* Dictionary data abstraction. 
2 * Maps from strings to pointers.
3 * Stores as a sorted list of key/value pairs.
4 * O(n) on inserts and deletes.
5 * O(log n) on searches
6 */
7
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include "owl.h"
12
13
14#define INITSIZE 30
15#define GROWAT 2
16#define GROWBY 1.5
17
18int owl_dict_create(owl_dict *d) {
19  d->size=0;
20  d->els=owl_malloc(INITSIZE*sizeof(owl_dict_el));
21  d->avail=INITSIZE;
22  if (d->els==NULL) return(-1);
23  return(0);
24}
25
26int owl_dict_get_size(owl_dict *d) {
27  return(d->size);
28}
29
30/* Finds the position of an element with key k, or of the element where
31 * this element would logically go, and stores the index in pos.
32 * Returns 1 if found, else 0. */
33int _owl_dict_find_pos(owl_dict *d, char *k, int *pos) {
34  int lo, hi, mid, cmp;
35  lo = 0;
36  hi = d->size;
37  while (lo < hi) {
38    mid = (lo+hi)/2; // lo goes up and we can't hit hi, so no +1
39    cmp = strcmp(k, d->els[mid].k);
40    if (cmp == 0) {
41      lo = hi = mid;
42    } else if (cmp < 0) {
43      hi = mid;
44    } else { // cmp > 0
45      lo = mid+1;
46    }
47  }
48  *pos = lo;
49  return !!(lo < d->size && !strcmp(k, d->els[lo].k));
50}
51
52/* returns the value corresponding to key k */
53void *owl_dict_find_element(owl_dict *d, char *k) {
54  int found, pos;
55  found = _owl_dict_find_pos(d, k, &pos);
56  if (!found) {
57    return(NULL);
58  }
59  return(d->els[pos].v);
60}
61
62/* creates a list and fills it in with keys.  duplicates the keys,
63 * so they will need to be freed by the caller. */
64int owl_dict_get_keys(owl_dict *d, owl_list *l) {
65  int i;
66  char *dupk;
67  if (owl_list_create(l)) return(-1);
68  for (i=0; i<d->size; i++) {
69    if ((dupk = owl_strdup(d->els[i].k)) == NULL) return(-1);
70    owl_list_append_element(l, dupk);
71  }
72  return(0);
73}
74
75void owl_dict_noop_free(void *x) {
76  return;
77}
78
79/* Returns 0 on success.  Will copy the key but make
80   a reference to the value.  Will clobber an existing
81   entry with the same key iff free_on_replace!=NULL,
82   and will run free_on_replace on the old element.
83   Will return -2 if replace=NULL and match was found.
84*/
85int owl_dict_insert_element(owl_dict *d, char *k, void *v, void (*free_on_replace)(void *old)) {
86  int pos, found;
87  char *dupk;
88  found = _owl_dict_find_pos(d, k, &pos);
89  if (found && free_on_replace) {
90    free_on_replace(d->els[pos].v);
91    d->els[pos].v = v;
92    return(0);
93  } else if (found && !free_on_replace) {
94    return(-2);
95  } else {
96    if ((d->size+1) > (d->avail/GROWAT)) {
97      d->els=owl_realloc(d->els, d->avail*GROWBY*sizeof(void *));
98      d->avail=d->avail*GROWBY;
99      if (d->els==NULL) return(-1);
100    }
101    if ((dupk = owl_strdup(k)) == NULL) return(-1);
102    if (pos!=d->size) {
103      /* shift forward to leave us a slot */
104      memmove(d->els+pos+1, d->els+pos, 
105              sizeof(owl_dict_el)*(d->size-pos));
106    }
107    d->size++;
108    d->els[pos].k = dupk;
109    d->els[pos].v = v;   
110    return(0);
111  }
112}
113
114/* Doesn't free the value of the element, but does
115 * return it so the caller can free it. */
116void *owl_dict_remove_element(owl_dict *d, char *k) {
117  int i;
118  int pos, found;
119  void *v;
120  found = _owl_dict_find_pos(d, k, &pos);
121  if (!found) return(NULL);
122  owl_free(d->els[pos].k);
123  v = d->els[pos].v;
124  for (i=pos; i<d->size-1; i++) {
125    d->els[i]=d->els[i+1];
126  }
127  d->size--;
128  return(v);
129}
130
131/* elefree should free the value as well */
132void owl_dict_free_all(owl_dict *d, void (*elefree)(void *)) {
133  int i;
134
135  for (i=0; i<d->size; i++) {
136    owl_free(d->els[i].k);
137    if (elefree) (elefree)(d->els[i].v);
138  }
139  if (d->els) owl_free(d->els);
140}
141
142void owl_dict_free_simple(owl_dict *d) {
143  owl_dict_free_all(d, NULL);
144}
145
146
147
148/************* REGRESSION TESTS **************/
149#ifdef OWL_INCLUDE_REG_TESTS
150
151#include "test.h"
152
153int owl_dict_regtest(void) {
154  owl_dict d;
155  owl_list l;
156  int numfailed=0;
157  char *av="aval", *bv="bval", *cv="cval", *dv="dval";
158
159  printf("# BEGIN testing owl_dict\n");
160  FAIL_UNLESS("create", 0==owl_dict_create(&d));
161  FAIL_UNLESS("insert b", 0==owl_dict_insert_element(&d, "b", bv, owl_dict_noop_free));
162  FAIL_UNLESS("insert d", 0==owl_dict_insert_element(&d, "d", dv, owl_dict_noop_free));
163  FAIL_UNLESS("insert a", 0==owl_dict_insert_element(&d, "a", av, owl_dict_noop_free));
164  FAIL_UNLESS("insert c", 0==owl_dict_insert_element(&d, "c", cv, owl_dict_noop_free));
165  FAIL_UNLESS("reinsert d (no replace)", -2==owl_dict_insert_element(&d, "d", dv, 0));
166  FAIL_UNLESS("find a", av==owl_dict_find_element(&d, "a"));
167  FAIL_UNLESS("find b", bv==owl_dict_find_element(&d, "b"));
168  FAIL_UNLESS("find c", cv==owl_dict_find_element(&d, "c"));
169  FAIL_UNLESS("find d", dv==owl_dict_find_element(&d, "d"));
170  FAIL_UNLESS("find e (non-existent)", NULL==owl_dict_find_element(&d, "e"));
171  FAIL_UNLESS("remove d", dv==owl_dict_remove_element(&d, "d"));
172  FAIL_UNLESS("find d (post-removal)", NULL==owl_dict_find_element(&d, "d"));
173
174  FAIL_UNLESS("get_size", 3==owl_dict_get_size(&d));
175  FAIL_UNLESS("get_keys", 0==owl_dict_get_keys(&d, &l));
176  FAIL_UNLESS("get_keys result size", 3==owl_list_get_size(&l));
177 
178  /* these assume the returned keys are sorted */
179  FAIL_UNLESS("get_keys result val",0==strcmp("a",owl_list_get_element(&l,0)));
180  FAIL_UNLESS("get_keys result val",0==strcmp("b",owl_list_get_element(&l,1)));
181  FAIL_UNLESS("get_keys result val",0==strcmp("c",owl_list_get_element(&l,2)));
182
183  owl_list_free_all(&l, owl_free);
184  owl_dict_free_all(&d, NULL);
185
186  /*  if (numfailed) printf("*** WARNING: failures encountered with owl_dict\n"); */
187  printf("# END testing owl_dict (%d failures)\n", numfailed);
188  return(numfailed);
189}
190
191#endif /* OWL_INCLUDE_REG_TESTS */
Note: See TracBrowser for help on using the repository browser.