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