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