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