]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/upas/fs/cache.c
upas/fs: replace fixed cache table with lru linked list
[plan9front.git] / sys / src / cmd / upas / fs / cache.c
1 #include "common.h"
2 #include <libsec.h>
3 #include "dat.h"
4
5 static void
6 addlru(Mcache *c, Message *m)
7 {
8         Message *l, **ll;
9
10         c->nlru++;
11         ll = &c->lru;
12         while((l = *ll) != nil){
13                 if(l == m){
14                         c->nlru--;
15                         *ll = m->lru;
16                 } else {
17                         ll = &l->lru;
18                 }
19         }
20         m->lru = nil;
21         *ll = m;
22 }
23
24 static void
25 notecache(Mailbox *mb, Message *m, long sz)
26 {
27         assert(Topmsg(mb, m));
28         assert(sz >= 0 && sz <= Maxmsg);
29         m->csize += sz;
30         mb->cached += sz;
31         addlru(mb, m);
32 }
33
34 static void
35 cachefree0(Mailbox *mb, Message *m, int force)
36 {
37         long sz, i;
38         Message *s;
39
40         if(!force && mb->fetch == nil)
41                 return;
42         for(s = m->part; s; s = s->next)
43                 cachefree(mb, s, force);
44         dprint("cachefree: %D   %p,     %p\n", m->fileid, m, m->start);
45         if(m->mallocd){
46                 free(m->start);
47                 m->mallocd = 0;
48         }
49         if(m->ballocd){
50                 free(m->body);
51                 m->ballocd = 0;
52         }
53         if(m->hallocd){
54                 free(m->header);
55                 m->hallocd = 0;
56         }
57         for(i = 0; i < nelem(m->references); i++){
58                 free(m->references[i]);
59                 m->references[i] = 0;
60         }
61         sz = m->csize;
62         m->csize = 0;
63         m->start = 0;
64         m->end = 0;
65         m->header = 0;
66         m->hend = 0;
67         m->hlen = -1;
68         m->body = 0;
69         m->bend = 0;
70         m->mheader = 0;
71         m->mhend = 0;
72         if(mb->decache)
73                 mb->decache(mb, m);
74         m->decoded = 0;
75         m->converted = 0;
76         m->badchars = 0;
77         m->cstate &= ~(Cheader|Cbody);
78         if(Topmsg(mb, m))
79                 mb->cached -= sz;
80 }
81
82 void
83 cachefree(Mailbox *mb, Message *m, int force)
84 {
85         Message **ll;
86
87         for(ll = &mb->lru; *ll != nil; ll = &((*ll)->lru)){
88                 if(*ll == m){
89                         mb->nlru--;
90                         *ll = m->lru;
91                         m->lru = nil;
92                         break;
93                 }
94         }
95         cachefree0(mb, m, force);
96 }
97
98 void
99 putcache(Mailbox *mb, Message *m)
100 {
101         int n;
102
103         addlru(mb, m);
104         while(mb->lru != nil && (mb->cached > cachetarg || mb->nlru > 10)){
105                 n = 0;
106                 while(mb->lru->refs > 0){
107                         if(++n >= mb->nlru)
108                                 return;
109                         addlru(mb, mb->lru);
110                 }
111                 cachefree(mb, mb->lru, 0);
112         }
113 }
114
115 static int
116 squeeze(Message *m, uvlong o, long l, int c)
117 {
118         char *p, *q, *e;
119         int n;
120
121         q = memchr(m->start + o, c, l);
122         if(q == nil)
123                 return 0;
124         n = 0;
125         e = m->start + o + l;
126         for(p = q; q < e; q++){
127                 if(*q == c){
128                         n++;
129                         continue;
130                 }
131                 *p++ = *q;
132         }
133         return n;
134 }
135
136 void
137 msgrealloc(Message *m, ulong l)
138 {
139         long l0, h0, m0, me, b0;
140
141         l0 = m->end - m->start;
142         m->mallocd = 1;
143         h0 = m->hend - m->start;
144         m0 = m->mheader - m->start;
145         me = m->mhend - m->start;
146         b0 = m->body - m->start;
147         assert(h0 >= 0 && m0 >= 0 && me >= 0 && b0 >= 0);
148         m->start = erealloc(m->start, l + 1);
149         m->rbody = m->start + b0;
150         m->rbend = m->end = m->start + l0;
151         if(!m->hallocd){
152                 m->header = m->start;
153                 m->hend = m->start + h0;
154         }
155         if(!m->ballocd){
156                 m->body = m->start + b0;
157                 m->bend = m->start + l0;
158         }
159         m->mheader = m->start + m0;
160         m->mhend = m->start + me;
161 }
162
163 /*
164  * the way we squeeze out bad characters is exceptionally sneaky.
165  */
166 static int
167 fetch(Mailbox *mb, Message *m, uvlong o, ulong l)
168 {
169         int expand;
170         long l0, n, sz0;
171
172 top:
173         l0 = m->end - m->start;
174         assert(l0 >= 0);
175         dprint("fetch %lud sz %lud o %llud l %lud badchars %d\n", l0, m->size, o, l, m->badchars);
176         if(l0 == m->size || o > m->size)
177                 return 0;
178         expand = 0;
179         if(o + l > m->size)
180                 l = m->size - o;
181         if(o + l == m->size)
182                 l += m->ibadchars - m->badchars;
183         if(o + l > l0){
184                 expand = 1;
185                 msgrealloc(m, o + m->badchars + l);
186         }
187         assert(l0 <= o);
188         sz0 = m->size;
189         if(mb->fetch(mb, m, o + m->badchars, l) == -1){
190                 logmsg(m, "can't fetch %D %llud %lud", m->fileid, o, l);
191                 m->deleted = Dead;
192                 return -1;
193         }
194         if(m->size - sz0)
195                 l += m->size - sz0;     /* awful botch for gmail */
196         if(expand){
197                 /* grumble.  poor planning. */
198                 if(m->badchars > 0)
199                         memmove(m->start + o, m->start + o + m->badchars, l);
200                 n = squeeze(m, o, l, 0);
201                 n += squeeze(m, o, l - n, '\r');
202                 if(n > 0){
203                         if(m->ibadchars == 0)
204                                 dprint("   %ld more badchars\n", n);
205                         l -= n;
206                         m->badchars += n;
207                         msgrealloc(m, o + l);
208                 }
209                 notecache(mb, m, l);
210                 m->bend = m->rbend = m->end = m->start + o + l;
211                 if(n)
212                 if(o + l + n == m->size && m->cstate&Cidx){
213                         dprint("   redux %llud %ld\n", o + l, n);
214                         o += l;
215                         l = n;
216                         goto top;
217                 }
218         }else
219                 eprint("unhandled case in fetch\n");
220         *m->end = 0;
221         return 0;
222 }
223
224 void
225 cachehash(Mailbox *mb, Message *m)
226 {
227         if(m->whole == m->whole->whole)
228                 henter(PATH(mb->id, Qmbox), m->name,
229                         (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
230         else
231                 henter(PATH(m->whole->id, Qdir), m->name,
232                         (Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
233         henter(PATH(m->id, Qdir), "xxx",
234                 (Qid){PATH(m->id, Qmax), 0, QTFILE}, m, mb);    /* sleezy speedup */
235 }
236
237 static char *itab[] = {
238         "idx",
239         "stale",
240         "header",
241         "body",
242         "new",
243 };
244
245 char*
246 cstate(Message *m)
247 {
248         char *p, *e;
249         int i, s;
250         static char buf[64];
251
252         s = m->cstate;
253         p = e = buf;
254         e += sizeof buf;
255         for(i = 0; i < 8; i++)
256                 if(s & 1<<i)
257                 if(i < nelem(itab))
258                         p = seprint(p, e, "%s ", itab[i]);
259         if(p > buf)
260                 p--;
261         p[0] = 0;
262         return buf;
263 }
264
265
266 static int
267 middlecache(Mailbox *mb, Message *m)
268 {
269         int y;
270
271         y = 0;
272         while(!Topmsg(mb, m)){
273                 m = m->whole;
274                 if((m->cstate & Cbody) == 0)
275                         y = 1;
276         }
277         if(y == 0)
278                 return 0;
279         dprint("middlecache %d [%D] %lud %lud\n", m->id, m->fileid, (ulong)(m->end - m->start), m->size);
280         return cachebody(mb, m);
281 }
282
283 int
284 cacheheaders(Mailbox *mb, Message *m)
285 {
286         char *p, *e;
287         int r;
288         ulong o;
289
290         if(!mb->fetch || m->cstate&Cheader)
291                 return 0;
292         if(!Topmsg(mb, m))
293                 return middlecache(mb, m);
294         dprint("cacheheaders %d %D\n", m->id, m->fileid);
295         if(m->size < 10000)
296                 r = fetch(mb, m, 0, m->size);
297         else for(r = 0; (o = m->end - m->start) < m->size; ){
298                 if((r = fetch(mb, m, o, 4096)) < 0)
299                         break;
300                 p = m->start + o;
301                 if(o)
302                         p--;
303                 for(e = m->end - 2; p < e; p++){
304                         p = memchr(p, '\n', e - p);
305                         if(p == nil)
306                                 break;
307                         if(p[1] == '\n' || (p[1] == '\r' && p[2] == '\n'))
308                                 goto found;
309                 }
310         }
311         if(r < 0)
312                 return -1;
313 found:
314         parseheaders(mb, m, mb->addfrom, 0);
315         return 0;
316 }
317
318 void
319 digestmessage(Mailbox *mb, Message *m)
320 {
321         assert(m->digest == 0);
322         m->digest = emalloc(SHA1dlen);
323         sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
324         if(mtreeisdup(mb, m)){
325                 logmsg(m, "dup detected");
326                 m->deleted = Dup;       /* no dups allowed */
327         }else
328                 mtreeadd(mb, m);
329         dprint("%d %#A\n", m->id, m->digest);
330 }
331
332 int
333 cachebody(Mailbox *mb, Message *m)
334 {
335         ulong o;
336
337         while(!Topmsg(mb, m))
338                 m = m->whole;
339         if(!mb->fetch || m->cstate&Cbody)
340                 return 0;
341         o = m->end - m->start;
342         dprint("cachebody %d [%D] %lud %lud %s", m->id, m->fileid, o, m->size, cstate(m));
343         if(o < m->size)
344         if(fetch(mb, m, o, m->size - o) < 0)
345                 return -1;
346         if((m->cstate&Cidx) == 0){
347                 assert(m->ibadchars == 0);
348                 if(m->badchars > 0)
349                         dprint("reducing size %ld %ld\n", m->size, m->size - m->badchars);
350                 m->size -= m->badchars;         /* sneaky */
351                 m->ibadchars = m->badchars;
352         }
353         if(m->digest == 0)
354                 digestmessage(mb, m);
355         if(m->lines == 0)
356                 m->lines = countlines(m);
357         parse(mb, m, mb->addfrom, 0);
358         dprint("  →%s\n", cstate(m));
359         return 0;
360 }
361
362 int
363 cacheidx(Mailbox *mb, Message *m)
364 {
365         if(m->cstate & Cidx)
366                 return 0;
367         if(cachebody(mb, m) == -1)
368                 return -1;
369         m->cstate |= Cidxstale|Cidx;
370         return 0;
371 }
372
373 static int
374 countparts(Message *m)
375 {
376         Message *p;
377
378         if(m->nparts == 0)
379                 for(p = m->part; p; p = p->next){
380                         countparts(p);
381                         m->nparts++;
382                 }
383         return m->nparts;
384 }
385
386 int
387 insurecache(Mailbox *mb, Message *m)
388 {
389         if(m->deleted || !m->inmbox)
390                 return -1;
391         msgincref(m);
392         cacheidx(mb, m);
393         if((m->cstate & Cidx) == 0){
394                 logmsg(m, "%s: can't cache: %s: %r", mb->path, m->name);
395                 msgdecref(mb, m);
396                 return -1;
397         }
398         if(m->digest == 0)
399                 sysfatal("digest?");
400         countparts(m);
401         return 0;
402 }