1 | #include "dthdr.h" |
2 | |
3 | /* Hash table. |
4 | ** dt: dictionary |
5 | ** obj: what to look for |
6 | ** type: type of search |
7 | ** |
8 | ** Written by Kiem-Phong Vo (05/25/96) |
9 | */ |
10 | |
11 | /* resize the hash table */ |
12 | static void dthtab(Dt_t* dt) |
13 | { |
14 | reg Dtlink_t *t, *r, *p, **s, **hs, **is, **olds; |
15 | int n, k; |
16 | |
17 | if(dt->data->minp > 0 && dt->data->ntab > 0) /* fixed table size */ |
18 | return; |
19 | dt->data->minp = 0; |
20 | |
21 | n = dt->data->ntab; |
22 | if(dt->disc && dt->disc->eventf && |
23 | (*dt->disc->eventf)(dt, DT_HASHSIZE, &n, dt->disc) > 0 ) |
24 | { if(n < 0) /* fix table size */ |
25 | { dt->data->minp = 1; |
26 | if(dt->data->ntab > 0 ) |
27 | return; |
28 | } |
29 | else /* set a particular size */ |
30 | { for(k = 2; k < n; k *= 2) |
31 | ; |
32 | n = k; |
33 | } |
34 | } |
35 | else n = 0; |
36 | |
37 | /* compute new table size */ |
38 | if(n <= 0) |
39 | { if((n = dt->data->ntab) == 0) |
40 | n = HSLOT; |
41 | while(dt->data->size > HLOAD(n)) |
42 | n = HRESIZE(n); |
43 | } |
44 | if(n == dt->data->ntab) |
45 | return; |
46 | |
47 | /* allocate new table */ |
48 | olds = dt->data->ntab == 0 ? NIL(Dtlink_t**) : dt->data->htab; |
49 | if(!(s = (Dtlink_t**)(*dt->memoryf)(dt,olds,n*sizeof(Dtlink_t*),dt->disc)) ) |
50 | return; |
51 | olds = s + dt->data->ntab; |
52 | dt->data->htab = s; |
53 | dt->data->ntab = n; |
54 | |
55 | /* rehash elements */ |
56 | for(hs = s+n-1; hs >= olds; --hs) |
57 | *hs = NIL(Dtlink_t*); |
58 | for(hs = s; hs < olds; ++hs) |
59 | { for(p = NIL(Dtlink_t*), t = *hs; t; t = r) |
60 | { r = t->right; |
61 | if((is = s + HINDEX(n,t->hash)) == hs) |
62 | p = t; |
63 | else /* move to a new chain */ |
64 | { if(p) |
65 | p->right = r; |
66 | else *hs = r; |
67 | t->right = *is; *is = t; |
68 | } |
69 | } |
70 | } |
71 | } |
72 | |
73 | static void* dthash(Dt_t* dt, reg void* obj, int type) |
74 | { |
75 | reg Dtlink_t *t, *r = NULL, *p; |
76 | reg void *k, *key; |
77 | reg uint hsh; |
78 | reg int lk, sz, ky; |
79 | reg Dtcompar_f cmpf; |
80 | reg Dtdisc_t* disc; |
81 | reg Dtlink_t **s = NULL, **ends; |
82 | |
83 | UNFLATTEN(dt); |
84 | |
85 | /* initialize discipline data */ |
86 | disc = dt->disc; _DTDSC(disc,ky,sz,lk,cmpf); |
87 | dt->type &= ~DT_FOUND; |
88 | |
89 | if(!obj) |
90 | { if(type&(DT_NEXT|DT_PREV)) |
91 | goto end_walk; |
92 | |
93 | if(dt->data->size <= 0 || !(type&(DT_CLEAR|DT_FIRST|DT_LAST)) ) |
94 | return NIL(void*); |
95 | |
96 | ends = (s = dt->data->htab) + dt->data->ntab; |
97 | if(type&DT_CLEAR) |
98 | { /* clean out all objects */ |
99 | for(; s < ends; ++s) |
100 | { t = *s; |
101 | *s = NIL(Dtlink_t*); |
102 | if(!disc->freef && disc->link >= 0) |
103 | continue; |
104 | while(t) |
105 | { r = t->right; |
106 | if(disc->freef) |
107 | (*disc->freef)(dt,_DTOBJ(t,lk),disc); |
108 | if(disc->link < 0) |
109 | (*dt->memoryf)(dt,(void*)t,0,disc); |
110 | t = r; |
111 | } |
112 | } |
113 | dt->data->here = NIL(Dtlink_t*); |
114 | dt->data->size = 0; |
115 | dt->data->loop = 0; |
116 | return NIL(void*); |
117 | } |
118 | else /* computing the first/last object */ |
119 | { t = NIL(Dtlink_t*); |
120 | while(s < ends && !t ) |
121 | t = (type&DT_LAST) ? *--ends : *s++; |
122 | if(t && (type&DT_LAST)) |
123 | for(; t->right; t = t->right) |
124 | ; |
125 | |
126 | dt->data->loop += 1; |
127 | dt->data->here = t; |
128 | return t ? _DTOBJ(t,lk) : NIL(void*); |
129 | } |
130 | } |
131 | |
132 | /* allow apps to delete an object "actually" in the dictionary */ |
133 | if(dt->meth->type == DT_BAG && (type&(DT_DELETE|DT_DETACH)) ) |
134 | { if(!dtsearch(dt,obj) ) |
135 | return NIL(void*); |
136 | |
137 | s = dt->data->htab + HINDEX(dt->data->ntab,dt->data->here->hash); |
138 | r = NIL(Dtlink_t*); |
139 | for(p = NIL(Dtlink_t*), t = *s; t; p = t, t = t->right) |
140 | { if(_DTOBJ(t,lk) == obj) /* delete this specific object */ |
141 | goto do_delete; |
142 | if(t == dt->data->here) |
143 | r = p; |
144 | } |
145 | |
146 | /* delete some matching object */ |
147 | p = r; t = dt->data->here; |
148 | goto do_delete; |
149 | } |
150 | |
151 | if(type&(DT_MATCH|DT_SEARCH|DT_INSERT|DT_ATTACH) ) |
152 | { key = (type&DT_MATCH) ? obj : _DTKEY(obj,ky,sz); |
153 | hsh = _DTHSH(dt,key,disc,sz); |
154 | goto do_search; |
155 | } |
156 | else if(type&(DT_RENEW|DT_VSEARCH) ) |
157 | { r = (Dtlink_t*)obj; |
158 | obj = _DTOBJ(r,lk); |
159 | key = _DTKEY(obj,ky,sz); |
160 | hsh = r->hash; |
161 | goto do_search; |
162 | } |
163 | else /*if(type&(DT_DELETE|DT_DETACH|DT_NEXT|DT_PREV))*/ |
164 | { if((t = dt->data->here) && _DTOBJ(t,lk) == obj) |
165 | { hsh = t->hash; |
166 | s = dt->data->htab + HINDEX(dt->data->ntab,hsh); |
167 | p = NIL(Dtlink_t*); |
168 | } |
169 | else |
170 | { key = _DTKEY(obj,ky,sz); |
171 | hsh = _DTHSH(dt,key,disc,sz); |
172 | do_search: |
173 | t = dt->data->ntab <= 0 ? NIL(Dtlink_t*) : |
174 | *(s = dt->data->htab + HINDEX(dt->data->ntab,hsh)); |
175 | for(p = NIL(Dtlink_t*); t; p = t, t = t->right) |
176 | { if(hsh == t->hash) |
177 | { k = _DTOBJ(t,lk); k = _DTKEY(k,ky,sz); |
178 | if(_DTCMP(dt,key,k,disc,cmpf,sz) == 0) |
179 | break; |
180 | } |
181 | } |
182 | } |
183 | } |
184 | |
185 | if(t) /* found matching object */ |
186 | dt->type |= DT_FOUND; |
187 | |
188 | if(type&(DT_MATCH|DT_SEARCH|DT_VSEARCH)) |
189 | { if(!t) |
190 | return NIL(void*); |
191 | if(p && (dt->data->type&DT_SET) && dt->data->loop <= 0) |
192 | { /* move-to-front heuristic */ |
193 | p->right = t->right; |
194 | t->right = *s; |
195 | *s = t; |
196 | } |
197 | dt->data->here = t; |
198 | return _DTOBJ(t,lk); |
199 | } |
200 | else if(type&(DT_INSERT|DT_ATTACH)) |
201 | { if(t && (dt->data->type&DT_SET) ) |
202 | { dt->data->here = t; |
203 | return _DTOBJ(t,lk); |
204 | } |
205 | |
206 | if(disc->makef && (type&DT_INSERT) && |
207 | !(obj = (*disc->makef)(dt,obj,disc)) ) |
208 | return NIL(void*); |
209 | if(lk >= 0) |
210 | r = _DTLNK(obj,lk); |
211 | else |
212 | { r = (Dtlink_t*)(*dt->memoryf) |
213 | (dt,NIL(void*),sizeof(Dthold_t),disc); |
214 | if(r) |
215 | ((Dthold_t*)r)->obj = obj; |
216 | else |
217 | { if(disc->makef && disc->freef && (type&DT_INSERT)) |
218 | (*disc->freef)(dt,obj,disc); |
219 | return NIL(void*); |
220 | } |
221 | } |
222 | r->hash = hsh; |
223 | |
224 | /* insert object */ |
225 | do_insert: |
226 | if((dt->data->size += 1) > HLOAD(dt->data->ntab) && dt->data->loop <= 0 ) |
227 | dthtab(dt); |
228 | if(dt->data->ntab == 0) |
229 | { dt->data->size -= 1; |
230 | if(disc->freef && (type&DT_INSERT)) |
231 | (*disc->freef)(dt,obj,disc); |
232 | if(disc->link < 0) |
233 | (*disc->memoryf)(dt,(void*)r,0,disc); |
234 | return NIL(void*); |
235 | } |
236 | s = dt->data->htab + HINDEX(dt->data->ntab,hsh); |
237 | if(t) |
238 | { r->right = t->right; |
239 | t->right = r; |
240 | } |
241 | else |
242 | { r->right = *s; |
243 | *s = r; |
244 | } |
245 | dt->data->here = r; |
246 | return obj; |
247 | } |
248 | else if(type&DT_NEXT) |
249 | { if(t && !(p = t->right) ) |
250 | { for(ends = dt->data->htab+dt->data->ntab, s += 1; s < ends; ++s) |
251 | if((p = *s) ) |
252 | break; |
253 | } |
254 | goto done_adj; |
255 | } |
256 | else if(type&DT_PREV) |
257 | { if(t && !p) |
258 | { if((p = *s) != t) |
259 | { while(p->right != t) |
260 | p = p->right; |
261 | } |
262 | else |
263 | { p = NIL(Dtlink_t*); |
264 | for(s -= 1, ends = dt->data->htab; s >= ends; --s) |
265 | { if((p = *s) ) |
266 | { while(p->right) |
267 | p = p->right; |
268 | break; |
269 | } |
270 | } |
271 | } |
272 | } |
273 | done_adj: |
274 | if(!(dt->data->here = p) ) |
275 | { end_walk: |
276 | if((dt->data->loop -= 1) < 0) |
277 | dt->data->loop = 0; |
278 | if(dt->data->size > HLOAD(dt->data->ntab) && dt->data->loop <= 0) |
279 | dthtab(dt); |
280 | return NIL(void*); |
281 | } |
282 | else |
283 | { dt->data->type |= DT_WALK; |
284 | return _DTOBJ(p,lk); |
285 | } |
286 | } |
287 | else if(type&DT_RENEW) |
288 | { if(!t || (dt->data->type&DT_BAG) ) |
289 | goto do_insert; |
290 | else |
291 | { if(disc->freef) |
292 | (*disc->freef)(dt,obj,disc); |
293 | if(disc->link < 0) |
294 | (*dt->memoryf)(dt,(void*)r,0,disc); |
295 | return t ? _DTOBJ(t,lk) : NIL(void*); |
296 | } |
297 | } |
298 | else /*if(type&(DT_DELETE|DT_DETACH))*/ |
299 | { /* take an element out of the dictionary */ |
300 | do_delete: |
301 | if(!t) |
302 | return NIL(void*); |
303 | else if(p) |
304 | p->right = t->right; |
305 | else if((p = *s) == t) |
306 | p = *s = t->right; |
307 | else |
308 | { while(p->right != t) |
309 | p = p->right; |
310 | p->right = t->right; |
311 | } |
312 | obj = _DTOBJ(t,lk); |
313 | dt->data->size -= 1; |
314 | dt->data->here = p; |
315 | if(disc->freef && (type&DT_DELETE)) |
316 | (*disc->freef)(dt,obj,disc); |
317 | if(disc->link < 0) |
318 | (*dt->memoryf)(dt,(void*)t,0,disc); |
319 | return obj; |
320 | } |
321 | } |
322 | |
323 | static Dtmethod_t _Dtset = { dthash, DT_SET }; |
324 | static Dtmethod_t _Dtbag = { dthash, DT_BAG }; |
325 | Dtmethod_t* Dtset = &_Dtset; |
326 | Dtmethod_t* Dtbag = &_Dtbag; |
327 | |
328 | #ifndef KPVDEL /* for backward compatibility - remove next time */ |
329 | Dtmethod_t _Dthash = { dthash, DT_SET }; |
330 | Dtmethod_t* Dthash = &_Dthash; |
331 | #endif |
332 | |
333 | #ifdef NoF |
334 | NoF(dthash) |
335 | #endif |
336 | |