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