91fa2ad723207474829ebf281259c13d10ab87c4
[idzebra-moved-to-github.git] / bfile / cfile.c
1 /*
2  * Copyright (C) 1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: cfile.c,v $
7  * Revision 1.12  1996-04-09 06:47:28  adam
8  * Function scan_areadef doesn't use sscanf (%n fails on this Linux).
9  *
10  * Revision 1.11  1996/03/26 15:59:05  adam
11  * The directory of the shadow table file can be specified by the new
12  * bf_lockDir call.
13  *
14  * Revision 1.10  1996/02/07  14:03:46  adam
15  * Work on flat indexed shadow files.
16  *
17  * Revision 1.9  1996/02/07  10:08:43  adam
18  * Work on flat shadow (not finished yet).
19  *
20  * Revision 1.8  1995/12/15  12:36:52  adam
21  * Moved hash file information to union.
22  * Renamed commit files.
23  *
24  * Revision 1.7  1995/12/15  10:35:07  adam
25  * Changed names of commit files.
26  *
27  * Revision 1.6  1995/12/11  09:03:53  adam
28  * New function: cf_unlink.
29  * New member of commit file head: state (0) deleted, (1) hash file.
30  *
31  * Revision 1.5  1995/12/08  16:21:14  adam
32  * Work on commit/update.
33  *
34  * Revision 1.4  1995/12/01  16:24:28  adam
35  * Commit files use separate meta file area.
36  *
37  * Revision 1.3  1995/12/01  11:37:22  adam
38  * Cached/commit files implemented as meta-files.
39  *
40  * Revision 1.2  1995/11/30  17:00:49  adam
41  * Several bug fixes. Commit system runs now.
42  *
43  * Revision 1.1  1995/11/30  08:33:11  adam
44  * Started work on commit facility.
45  *
46  */
47
48 #include <assert.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include <alexutil.h>
53 #include <mfile.h>
54 #include "cfile.h"
55
56 static int write_head (CFile cf)
57 {
58     int left = cf->head.hash_size * sizeof(int);
59     int bno = 1;
60     const char *tab = (char*) cf->array;
61
62     if (!tab)
63         return 0;
64     while (left >= HASH_BSIZE)
65     {
66         mf_write (cf->hash_mf, bno++, 0, 0, tab);
67         tab += HASH_BSIZE;
68         left -= HASH_BSIZE;
69     }
70     if (left > 0)
71         mf_write (cf->hash_mf, bno, 0, left, tab);
72     return 0;
73 }
74
75 static int read_head (CFile cf)
76 {
77     int left = cf->head.hash_size * sizeof(int);
78     int bno = 1;
79     char *tab = (char*) cf->array;
80
81     if (!tab)
82         return 0;
83     while (left >= HASH_BSIZE)
84     {
85         mf_read (cf->hash_mf, bno++, 0, 0, tab);
86         tab += HASH_BSIZE;
87         left -= HASH_BSIZE;
88     }
89     if (left > 0)
90         mf_read (cf->hash_mf, bno, 0, left, tab);
91     return 0;
92 }
93
94
95 CFile cf_open (MFile mf, MFile_area area, const char *fname,
96                int block_size, int wflag, int *firstp)
97 {
98     char path[1024];
99     int i;
100     CFile cf = xmalloc (sizeof(*cf));
101     int hash_bytes;
102    
103     cf->rmf = mf; 
104     logf (LOG_LOG, "cf_open %s", cf->rmf->name);
105     sprintf (path, "%s-b", fname);
106     if (!(cf->block_mf = mf_open (area, path, block_size, wflag)))
107     {
108         logf (LOG_FATAL|LOG_ERRNO, "Failed to open %s", path);
109         exit (1);
110     }
111     sprintf (path, "%s-i", fname);
112     if (!(cf->hash_mf = mf_open (area, path, HASH_BSIZE, wflag)))
113     {
114         logf (LOG_FATAL|LOG_ERRNO, "Failed to open %s", path);
115         exit (1);
116     }
117     assert (firstp);
118     if (!mf_read (cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head) ||
119         !cf->head.state)
120     {
121         *firstp = 1;
122         cf->head.state = 1;
123         cf->head.block_size = block_size;
124         cf->head.hash_size = 199;
125         hash_bytes = cf->head.hash_size * sizeof(int);
126         cf->head.flat_bucket = cf->head.next_bucket = cf->head.first_bucket = 
127             (hash_bytes+sizeof(cf->head))/HASH_BSIZE + 2;
128         cf->head.next_block = 1;
129         if (wflag)
130             mf_write (cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
131         cf->array = xmalloc (hash_bytes);
132         for (i = 0; i<cf->head.hash_size; i++)
133             cf->array[i] = 0;
134         if (wflag)
135             write_head (cf);
136     }
137     else
138     {
139         *firstp = 0;
140         assert (cf->head.block_size == block_size);
141         assert (cf->head.hash_size > 2);
142         hash_bytes = cf->head.hash_size * sizeof(int);
143         assert (cf->head.next_bucket > 0);
144         if (cf->head.state == 1)
145             cf->array = xmalloc (hash_bytes);
146         else
147             cf->array = NULL;
148         read_head (cf);
149     }
150     if (cf->head.state == 1)
151     {
152         cf->parray = xmalloc (cf->head.hash_size * sizeof(*cf->parray));
153         for (i = 0; i<cf->head.hash_size; i++)
154             cf->parray[i] = NULL;
155     }
156     else
157         cf->parray = NULL;
158     cf->bucket_lru_front = cf->bucket_lru_back = NULL;
159     cf->bucket_in_memory = 0;
160     cf->max_bucket_in_memory = 100;
161     cf->dirty = 0;
162     cf->iobuf = xmalloc (cf->head.block_size);
163     memset (cf->iobuf, 0, cf->head.block_size);
164     cf->no_hits = 0;
165     cf->no_miss = 0;
166     return cf;
167 }
168
169 static int cf_hash (CFile cf, int no)
170 {
171     return (no>>3) % cf->head.hash_size;
172 }
173
174 static void release_bucket (CFile cf, struct CFile_hash_bucket *p)
175 {
176     if (p->lru_prev)
177         p->lru_prev->lru_next = p->lru_next;
178     else
179         cf->bucket_lru_back = p->lru_next;
180     if (p->lru_next)
181         p->lru_next->lru_prev = p->lru_prev;
182     else
183         cf->bucket_lru_front = p->lru_prev;
184
185     *p->h_prev = p->h_next;
186     if (p->h_next)
187         p->h_next->h_prev = p->h_prev;
188     
189     --(cf->bucket_in_memory);
190     xfree (p);
191 }
192
193 static void flush_bucket (CFile cf, int no_to_flush)
194 {
195     int i;
196     struct CFile_hash_bucket *p;
197
198     for (i = 0; i != no_to_flush; i++)
199     {
200         p = cf->bucket_lru_back;
201         if (!p)
202             break;
203         if (p->dirty)
204         {
205             mf_write (cf->hash_mf, p->ph.this_bucket, 0, 0, &p->ph);
206             cf->dirty = 1;
207         }
208         release_bucket (cf, p);
209     }
210 }
211
212 static struct CFile_hash_bucket *alloc_bucket (CFile cf, int block_no, int hno)
213 {
214     struct CFile_hash_bucket *p, **pp;
215
216     if (cf->bucket_in_memory == cf->max_bucket_in_memory)
217         flush_bucket (cf, 1);
218     assert (cf->bucket_in_memory < cf->max_bucket_in_memory);
219     ++(cf->bucket_in_memory);
220     p = xmalloc (sizeof(*p));
221
222     p->lru_next = NULL;
223     p->lru_prev = cf->bucket_lru_front;
224     if (cf->bucket_lru_front)
225         cf->bucket_lru_front->lru_next = p;
226     else
227         cf->bucket_lru_back = p;
228     cf->bucket_lru_front = p; 
229
230     pp = cf->parray + hno;
231     p->h_next = *pp;
232     p->h_prev = pp;
233     if (*pp)
234         (*pp)->h_prev = &p->h_next;
235     *pp = p;
236     return p;
237 }
238
239 static struct CFile_hash_bucket *get_bucket (CFile cf, int block_no, int hno)
240 {
241     struct CFile_hash_bucket *p;
242
243     p = alloc_bucket (cf, block_no, hno);
244     if (!mf_read (cf->hash_mf, block_no, 0, 0, &p->ph))
245     {
246         logf (LOG_FATAL|LOG_ERRNO, "read get_bucket");
247         exit (1);
248     }
249     assert (p->ph.this_bucket == block_no);
250     p->dirty = 0;
251     return p;
252 }
253
254 static struct CFile_hash_bucket *new_bucket (CFile cf, int *block_no, int hno)
255 {
256     struct CFile_hash_bucket *p;
257     int i;
258
259     *block_no = cf->head.next_bucket++;
260     p = alloc_bucket (cf, *block_no, hno);
261
262     for (i = 0; i<HASH_BUCKET; i++)
263     {
264         p->ph.vno[i] = 0;
265         p->ph.no[i] = 0;
266     }
267     p->ph.next_bucket = 0;
268     p->ph.this_bucket = *block_no;
269     p->dirty = 1;
270     return p;
271 }
272
273 static int cf_lookup_flat (CFile cf, int no)
274 {
275     int hno = (no*sizeof(int))/HASH_BSIZE;
276     int off = (no*sizeof(int)) - hno*sizeof(HASH_BSIZE);
277     int vno = 0;
278
279     mf_read (cf->hash_mf, hno+cf->head.next_bucket, off, sizeof(int), &vno);
280     return vno;
281 }
282
283 static int cf_lookup_hash (CFile cf, int no)
284 {
285     int hno = cf_hash (cf, no);
286     struct CFile_hash_bucket *hb;
287     int block_no, i;
288
289     for (hb = cf->parray[hno]; hb; hb = hb->h_next)
290     {
291         for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
292             if (hb->ph.no[i] == no)
293             {
294                 (cf->no_hits)++;
295                 return hb->ph.vno[i];
296             }
297     }
298     for (block_no = cf->array[hno]; block_no; block_no = hb->ph.next_bucket)
299     {
300         for (hb = cf->parray[hno]; hb; hb = hb->h_next)
301         {
302             if (hb->ph.this_bucket == block_no)
303                 break;
304         }
305         if (hb)
306             continue;
307         (cf->no_miss)++;
308         hb = get_bucket (cf, block_no, hno);
309         for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
310             if (hb->ph.no[i] == no)
311                 return hb->ph.vno[i];
312     }
313     return 0;
314 }
315
316 static void cf_write_flat (CFile cf, int no, int vno)
317 {
318     int hno = (no*sizeof(int))/HASH_BSIZE;
319     int off = (no*sizeof(int)) - hno*sizeof(HASH_BSIZE);
320
321     hno += cf->head.next_bucket;
322     if (hno >= cf->head.flat_bucket)
323         cf->head.flat_bucket = hno+1;
324     mf_write (cf->hash_mf, hno, off, sizeof(int), &vno);
325 }
326
327 static void cf_moveto_flat (CFile cf)
328 {
329     struct CFile_hash_bucket *p;
330     int i, j;
331
332     logf (LOG_LOG, "Moving to flat shadow: %s", cf->rmf->name);
333     logf (LOG_LOG, "hits=%d miss=%d bucket_in_memory=%d total=%d",
334         cf->no_hits, cf->no_miss, cf->bucket_in_memory, 
335         cf->head.next_bucket - cf->head.first_bucket);
336     assert (cf->head.state == 1);
337     flush_bucket (cf, -1);
338     assert (cf->bucket_in_memory == 0);
339     p = xmalloc (sizeof(*p));
340     for (i = cf->head.first_bucket; i < cf->head.next_bucket; i++)
341     {
342         if (!mf_read (cf->hash_mf, i, 0, 0, &p->ph))
343         {
344             logf (LOG_FATAL|LOG_ERRNO, "read bucket moveto flat");
345             exit (1);
346         }
347         for (j = 0; j < HASH_BUCKET && p->ph.vno[j]; j++)
348             cf_write_flat (cf, p->ph.no[j], p->ph.vno[j]);
349     }
350     xfree (p);
351     xfree (cf->array);
352     cf->array = NULL;
353     xfree (cf->parray);
354     cf->parray = NULL;
355     cf->head.state = 2;
356 }
357
358 static int cf_lookup (CFile cf, int no)
359 {
360     if (cf->head.state > 1)
361         return cf_lookup_flat (cf, no);
362     return cf_lookup_hash (cf, no);
363 }
364
365 static int cf_new_flat (CFile cf, int no)
366 {
367     int vno = (cf->head.next_block)++;
368
369     cf_write_flat (cf, no, vno);
370     return vno;
371 }
372
373 static int cf_new_hash (CFile cf, int no)
374 {
375     int hno = cf_hash (cf, no);
376     struct CFile_hash_bucket *hbprev = NULL, *hb = cf->parray[hno];
377     int *bucketpp = &cf->array[hno]; 
378     int i, vno = (cf->head.next_block)++;
379   
380     for (hb = cf->parray[hno]; hb; hb = hb->h_next)
381         if (!hb->ph.vno[HASH_BUCKET-1])
382             for (i = 0; i<HASH_BUCKET; i++)
383                 if (!hb->ph.vno[i])
384                 {
385                     (cf->no_hits)++;
386                     hb->ph.no[i] = no;
387                     hb->ph.vno[i] = vno;
388                     hb->dirty = 1;
389                     return vno;
390                 }
391
392     while (*bucketpp)
393     {
394         for (hb = cf->parray[hno]; hb; hb = hb->h_next)
395             if (hb->ph.this_bucket == *bucketpp)
396             {
397                 bucketpp = &hb->ph.next_bucket;
398                 hbprev = hb;
399                 break;
400             }
401         if (hb)
402             continue;
403         (cf->no_miss)++;
404         hb = get_bucket (cf, *bucketpp, hno);
405         assert (hb);
406         for (i = 0; i<HASH_BUCKET; i++)
407             if (!hb->ph.vno[i])
408             {
409                 hb->ph.no[i] = no;
410                 hb->ph.vno[i] = vno;
411                 hb->dirty = 1;
412                 return vno;
413             }
414         bucketpp = &hb->ph.next_bucket;
415         hbprev = hb;
416     }
417     if (hbprev)
418         hbprev->dirty = 1;
419     hb = new_bucket (cf, bucketpp, hno);
420     hb->ph.no[0] = no;
421     hb->ph.vno[0] = vno;
422     return vno;
423 }
424
425 int cf_new (CFile cf, int no)
426 {
427     if (cf->head.state > 1)
428         return cf_new_flat (cf, no);
429     if (cf->no_miss*5 > cf->no_hits)
430     {
431         cf_moveto_flat (cf);
432         assert (cf->head.state > 1);
433         return cf_new_flat (cf, no);
434     }
435     return cf_new_hash (cf, no);
436 }
437
438
439 int cf_read (CFile cf, int no, int offset, int num, void *buf)
440 {
441     int block;
442     
443     assert (cf);
444     if (!(block = cf_lookup (cf, no)))
445         return -1;
446     if (!mf_read (cf->block_mf, block, offset, num, buf))
447     {
448         logf (LOG_FATAL|LOG_ERRNO, "cf_read no=%d, block=%d", no, block);
449         exit (1);
450     }
451     return 1;
452 }
453
454 int cf_write (CFile cf, int no, int offset, int num, const void *buf)
455 {
456     int block;
457
458     assert (cf);
459     if (!(block = cf_lookup (cf, no)))
460     {
461         block = cf_new (cf, no);
462         if (offset || num)
463         {
464             mf_read (cf->rmf, no, 0, 0, cf->iobuf);
465             memcpy (cf->iobuf + offset, buf, num);
466             buf = cf->iobuf;
467             offset = 0;
468             num = 0;
469         }
470     }
471     if (mf_write (cf->block_mf, block, offset, num, buf))
472     {
473         logf (LOG_FATAL|LOG_ERRNO, "cf_write no=%d, block=%d", no, block);
474         exit (1);
475     }
476     return 0;
477 }
478
479 int cf_close (CFile cf)
480 {
481     logf (LOG_LOG, "cf_close %s", cf->rmf->name);
482     logf (LOG_LOG, "hits=%d miss=%d bucket_in_memory=%d total=%d",
483           cf->no_hits, cf->no_miss, cf->bucket_in_memory,
484           cf->head.next_bucket - cf->head.first_bucket);
485     flush_bucket (cf, -1);
486     if (cf->dirty)
487     {
488         mf_write (cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
489         write_head (cf);
490     }
491     mf_close (cf->hash_mf);
492     mf_close (cf->block_mf);
493     xfree (cf->array);
494     xfree (cf->parray);
495     xfree (cf->iobuf);
496     xfree (cf);
497     return 0;
498 }
499