Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "hash.h"
6 /***
7 * String hash tables.
8 */
10 Stringtab *tfree;
12 Stringtab*
13 taballoc(void)
14 {
15 static Stringtab *t;
16 static uint nt;
18 if(tfree){
19 Stringtab *tt = tfree;
20 tfree = tt->link;
21 return tt;
22 }
24 if(nt == 0){
25 t = malloc(64000*sizeof(Stringtab));
26 if(t == 0)
27 sysfatal("out of memory");
28 nt = 64000;
29 }
30 nt--;
31 return t++;
32 }
34 void
35 tabfree(Stringtab *tt)
36 {
37 tt->link = tfree;
38 tfree = tt;
39 }
41 char*
42 xstrdup(char *s, int len)
43 {
44 char *r;
45 static char *t;
46 static int nt;
48 if(nt < len){
49 t = malloc(512*1024+len);
50 if(t == 0)
51 sysfatal("out of memory");
52 nt = 512*1024;
53 }
54 r = t;
55 t += len;
56 nt -= len;
57 memmove(r, s, len);
58 return r;
59 }
61 static uint
62 hash(char *s, int n)
63 {
64 uint h;
65 uchar *p, *ep;
66 h = 0;
67 for(p=(uchar*)s, ep=p+n; p<ep; p++)
68 h = h*37 + *p;
69 return h;
70 }
72 static void
73 rehash(Hash *hh)
74 {
75 int h;
76 Stringtab *s;
78 if(hh->nstab == 0)
79 hh->nstab = 1024;
80 else
81 hh->nstab = hh->ntab*2;
83 free(hh->stab);
84 hh->stab = mallocz(hh->nstab*sizeof(Stringtab*), 1);
85 if(hh->stab == nil)
86 sysfatal("out of memory");
88 for(s=hh->all; s; s=s->link){
89 h = hash(s->str, s->n) % hh->nstab;
90 s->hash = hh->stab[h];
91 hh->stab[h] = s;
92 }
93 }
95 Stringtab*
96 findstab(Hash *hh, char *str, int n, int create)
97 {
98 uint h;
99 Stringtab *tab, **l;
101 if(hh->nstab == 0)
102 rehash(hh);
104 h = hash(str, n) % hh->nstab;
105 for(tab=hh->stab[h], l=&hh->stab[h]; tab; l=&tab->hash, tab=tab->hash)
106 if(n==tab->n && memcmp(str, tab->str, n) == 0){
107 *l = tab->hash;
108 tab->hash = hh->stab[h];
109 hh->stab[h] = tab;
110 return tab;
113 if(!create)
114 return nil;
116 hh->sorted = 0;
117 tab = taballoc();
118 tab->str = xstrdup(str, n);
119 tab->hash = hh->stab[h];
120 tab->link = hh->all;
121 hh->all = tab;
122 tab->n = n;
123 tab->count = 0;
124 tab->date = 0;
125 hh->stab[h] = tab;
127 hh->ntab++;
128 if(hh->ntab > 2*hh->nstab && !(hh->ntab&(hh->ntab-1)))
129 rehash(hh);
130 return tab;
133 int
134 scmp(Stringtab *a, Stringtab *b)
136 int n, x;
138 if(a == 0)
139 return 1;
140 if(b == 0)
141 return -1;
142 n = a->n;
143 if(n > b->n)
144 n = b->n;
145 x = memcmp(a->str, b->str, n);
146 if(x != 0)
147 return x;
148 if(a->n < b->n)
149 return -1;
150 if(a->n > b->n)
151 return 1;
152 return 0; /* shouldn't happen */
155 Stringtab*
156 merge(Stringtab *a, Stringtab *b)
158 Stringtab *s, **l;
160 l = &s;
161 while(a || b){
162 if(scmp(a, b) < 0){
163 *l = a;
164 l = &a->link;
165 a = a->link;
166 }else{
167 *l = b;
168 l = &b->link;
169 b = b->link;
172 *l = 0;
173 return s;
176 Stringtab*
177 mergesort(Stringtab *s)
179 Stringtab *a, *b;
180 int delay;
182 if(s == nil)
183 return nil;
184 if(s->link == nil)
185 return s;
187 a = b = s;
188 delay = 1;
189 while(a && b){
190 if(delay) /* easy way to handle 2-element list */
191 delay = 0;
192 else
193 a = a->link;
194 if(b = b->link)
195 b = b->link;
198 b = a->link;
199 a->link = nil;
201 a = mergesort(s);
202 b = mergesort(b);
204 return merge(a, b);
207 Stringtab*
208 sortstab(Hash *hh)
210 if(!hh->sorted){
211 hh->all = mergesort(hh->all);
212 hh->sorted = 1;
214 return hh->all;
217 int
218 Bwritehash(Biobuf *b, Hash *hh)
220 Stringtab *s;
221 int now;
223 now = time(0);
224 s = sortstab(hh);
225 Bprint(b, "# hash table\n");
226 for(; s; s=s->link){
227 if(s->count <= 0)
228 continue;
229 /*
230 * Entries that haven't been updated in thirty days get tossed.
231 */
232 if(s->date+30*86400 < now)
233 continue;
234 Bwrite(b, s->str, s->n);
235 Bprint(b, "\t%d %d\n", s->count, s->date);
237 if(Bflush(b) == Beof)
238 return -1;
239 return 0;
242 void
243 Breadhash(Biobuf *b, Hash *hh, int scale)
245 char *s;
246 char *t;
247 int n;
248 int date;
249 Stringtab *st;
251 s = Brdstr(b, '\n', 1);
252 if(s == nil)
253 return;
254 if(strcmp(s, "# hash table") != 0)
255 sysfatal("bad hash table format");
257 while(s = Brdline(b, '\n')){
258 s[Blinelen(b)-1] = 0;
259 t = strrchr(s, '\t');
260 if(t == nil)
261 sysfatal("bad hash table format");
262 *t++ = '\0';
263 if(*t < '0' || *t > '9')
264 sysfatal("bad hash table format");
265 n = strtol(t, &t, 10);
266 date = time(0);
267 if(*t != 0){
268 if(*t == ' '){
269 t++;
270 date = strtol(t, &t, 10);
272 if(*t != 0)
273 sysfatal("bad hash table format");
275 st = findstab(hh, s, strlen(s), 1);
276 if(date > st->date)
277 st->date = date;
278 st->count += n*scale;
282 void
283 freehash(Hash *h)
285 Stringtab *s, *next;
287 for(s=h->all; s; s=next){
288 next = s->link;
289 tabfree(s);
291 free(h->stab);
292 free(h);
295 Biobuf*
296 Bopenlock(char *file, int mode)
298 int i;
299 Biobuf *b;
300 char err[ERRMAX];
302 b = nil;
303 for(i=0; i<120; i++){
304 if((b = Bopen(file, mode)) != nil)
305 break;
306 rerrstr(err, sizeof err);
307 if(strstr(err, "file is locked")==nil && strstr(err, "exclusive lock")==nil)
308 break;
309 sleep(1000);
311 return b;