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