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