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