Several bug fixes. Commit system runs now.
[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.2  1995-11-30 17:00:49  adam
8  * Several bug fixes. Commit system runs now.
9  *
10  * Revision 1.1  1995/11/30  08:33:11  adam
11  * Started work on commit facility.
12  *
13  */
14
15 #include <assert.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include <alexutil.h>
22 #include <mfile.h>
23 #include "cfile.h"
24
25 static int hash_write (CFile cf, const void *buf, size_t bytes)
26 {
27     int r;
28
29     r = write (cf->hash_fd, buf, bytes);
30     if (r == bytes)
31         return bytes;
32     if (r == -1)
33         logf (LOG_FATAL|LOG_ERRNO, "write in commit hash file");
34     else
35         logf (LOG_FATAL, "write in commit hash file. "
36                      "%d bytes instead of %d bytes", r, bytes);
37     exit (1);
38     return 0;
39 }
40
41 static int hash_read (CFile cf, void *buf, size_t bytes)
42 {
43     int r;
44
45     r = read (cf->hash_fd, buf, bytes);
46     if (r == bytes)
47         return bytes;
48     if (r == -1)
49         logf (LOG_FATAL|LOG_ERRNO, "read in commit hash file");
50     else
51         logf (LOG_FATAL, "read in commit hash file. "
52                      "%d bytes instead of %d bytes", r, bytes);
53     abort ();
54     return 0;
55 }
56
57 CFile cf_open (MFile mf, const char *cname, const char *fname,
58                int block_size, int wflag, int *firstp)
59 {
60     char path[256];
61     int r, i;
62     CFile cf = xmalloc (sizeof(*cf));
63     int hash_bytes;
64    
65     cf->mf = mf; 
66     sprintf (path, "%s.%s.b", cname, fname);
67     if ((cf->block_fd = 
68         open (path, wflag ? O_RDWR|O_CREAT : O_RDONLY, 0666)) < 0)
69     {
70         logf (LOG_FATAL|LOG_ERRNO, "Failed to open %s", path);
71         exit (1);
72     }
73     sprintf (path, "%s.%s.h", cname, fname);
74     if ((cf->hash_fd = 
75         open (path, wflag ? O_RDWR|O_CREAT : O_RDONLY, 0666)) < 0)
76     {
77         logf (LOG_FATAL|LOG_ERRNO, "Failed to open %s", path);
78         exit (1);
79     }
80     r = read (cf->hash_fd, &cf->head, sizeof(cf->head));
81     if (r != sizeof(cf->head))
82     {
83         *firstp = 1;
84         if (r == -1)
85         {
86             logf (LOG_FATAL|LOG_ERRNO, "read head of %s", path);
87             exit (1);
88         }
89         if (r != 0)
90         {
91             logf (LOG_FATAL, "illegal head of %s", path);
92             exit (1);
93         }
94         cf->head.block_size = block_size;
95         cf->head.hash_size = 401;
96         hash_bytes = cf->head.hash_size * sizeof(int);
97         cf->head.next_bucket =
98             (hash_bytes+sizeof(cf->head))/HASH_BSIZE + 2;
99         cf->head.next_block = 1;
100         if (wflag)
101             hash_write (cf, &cf->head, sizeof(cf->head));
102         cf->array = xmalloc (hash_bytes);
103         for (i = 0; i<cf->head.hash_size; i++)
104             cf->array[i] = 0;
105         if (wflag)
106             hash_write (cf, cf->array, hash_bytes);
107     }
108     else
109     {
110         *firstp = 0;
111         assert (cf->head.block_size == block_size);
112         assert (cf->head.hash_size > 2 && cf->head.hash_size < 200000);
113         hash_bytes = cf->head.hash_size * sizeof(int);
114         assert (cf->head.next_bucket > 0);
115         cf->array = xmalloc (hash_bytes);
116         hash_read (cf, cf->array, hash_bytes);
117     }
118     cf->parray = xmalloc (cf->head.hash_size * sizeof(*cf->parray));
119     for (i = 0; i<cf->head.hash_size; i++)
120         cf->parray[i] = NULL;
121     cf->bucket_lru_front = cf->bucket_lru_back = NULL;
122     cf->bucket_in_memory = 0;
123     cf->max_bucket_in_memory = 200;
124     cf->dirty = 0;
125     cf->iobuf = xmalloc (cf->head.block_size);
126     memset (cf->iobuf, 0, cf->head.block_size);
127     return cf;
128 }
129
130 static int cf_hash (CFile cf, int no)
131 {
132     return (no>>3) % cf->head.hash_size;
133 }
134
135 static void release_bucket (CFile cf, struct CFile_hash_bucket *p)
136 {
137     if (p->lru_prev)
138         p->lru_prev->lru_next = p->lru_next;
139     else
140         cf->bucket_lru_back = p->lru_next;
141     if (p->lru_next)
142         p->lru_next->lru_prev = p->lru_prev;
143     else
144         cf->bucket_lru_front = p->lru_prev;
145
146     *p->h_prev = p->h_next;
147     if (p->h_next)
148         p->h_next->h_prev = p->h_prev;
149     
150     --(cf->bucket_in_memory);
151     xfree (p);
152 }
153
154 static void flush_bucket (CFile cf, int no_to_flush)
155 {
156     int i;
157     struct CFile_hash_bucket *p;
158
159     for (i = 0; i != no_to_flush; i++)
160     {
161         p = cf->bucket_lru_back;
162         if (!p)
163             break;
164         if (p->dirty)
165         {
166             if (lseek (cf->hash_fd, p->ph.this_bucket*HASH_BSIZE, SEEK_SET) < 0)
167             {
168                 logf (LOG_FATAL|LOG_ERRNO, "lseek in flush_bucket");
169                 exit (1);
170             }
171             hash_write (cf, &p->ph, HASH_BSIZE);
172             cf->dirty = 1;
173         }
174         release_bucket (cf, p);
175     }
176 }
177
178 static struct CFile_hash_bucket *alloc_bucket (CFile cf, int block_no, int hno)
179 {
180     struct CFile_hash_bucket *p, **pp;
181
182     if (cf->bucket_in_memory == cf->max_bucket_in_memory)
183         flush_bucket (cf, 1);
184     assert (cf->bucket_in_memory < cf->max_bucket_in_memory);
185     ++(cf->bucket_in_memory);
186     p = xmalloc (sizeof(*p));
187
188     p->lru_next = NULL;
189     p->lru_prev = cf->bucket_lru_front;
190     if (cf->bucket_lru_front)
191         cf->bucket_lru_front->lru_next = p;
192     else
193         cf->bucket_lru_back = p;
194     cf->bucket_lru_front = p; 
195
196     pp = cf->parray + hno;
197     p->h_next = *pp;
198     p->h_prev = pp;
199     if (*pp)
200         (*pp)->h_prev = &p->h_next;
201     *pp = p;
202     return p;
203 }
204
205 static struct CFile_hash_bucket *get_bucket (CFile cf, int block_no, int hno)
206 {
207     struct CFile_hash_bucket *p;
208
209     p = alloc_bucket (cf, block_no, hno);
210
211     if (lseek (cf->hash_fd, block_no * HASH_BSIZE, SEEK_SET) < 0)
212     {
213         logf (LOG_FATAL|LOG_ERRNO, "lseek in get_bucket");
214         exit (1);
215     }
216     hash_read (cf, &p->ph, HASH_BSIZE);
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 tor, block, r;
324     
325     assert (cf);
326     if (!(block = cf_lookup (cf, no)))
327         return -1;
328     if (lseek (cf->block_fd, cf->head.block_size * block + offset,
329          SEEK_SET) < 0)
330     {
331         logf (LOG_FATAL|LOG_ERRNO, "cf_read, lseek no=%d, block=%d",
332               no, block);
333         exit (1);
334     }
335     tor = num ? num : cf->head.block_size;
336     r = read (cf->block_fd, buf, tor);
337     if (r != tor)
338     {
339         logf (LOG_FATAL|LOG_ERRNO, "cf_read, read no=%d, block=%d",
340               no, block);
341         exit (1);
342     }
343     return 1;
344 }
345
346 int cf_write (CFile cf, int no, int offset, int num, const void *buf)
347 {
348     int block, r, tow;
349
350     assert (cf);
351     if (!(block = cf_lookup (cf, no)))
352     {
353         block = cf_new (cf, no);
354         if (offset || num)
355         {
356             mf_read (cf->mf, no, 0, 0, cf->iobuf);
357             memcpy (cf->iobuf + offset, buf, num);
358             buf = cf->iobuf;
359             offset = 0;
360             num = 0;
361         }
362     }
363     if (lseek (cf->block_fd, cf->head.block_size * block + offset,
364                SEEK_SET) < 0)
365     {
366         logf (LOG_FATAL|LOG_ERRNO, "cf_write, lseek no=%d, block=%d",
367               no, block);
368         exit (1);
369     }
370
371     tow = num ? num : cf->head.block_size;
372     r = write (cf->block_fd, buf, tow);
373     if (r != tow)
374     {
375         logf (LOG_FATAL|LOG_ERRNO, "cf_write, read no=%d, block=%d",
376               no, block);
377         exit (1);
378     }
379     return 0;
380 }
381
382 int cf_close (CFile cf)
383 {
384     flush_bucket (cf, -1);
385     if (cf->dirty)
386     {
387         int hash_bytes = cf->head.hash_size * sizeof(int);
388         if (lseek (cf->hash_fd, 0L, SEEK_SET) < 0)
389         {
390             logf (LOG_FATAL|LOG_ERRNO, "seek in hash fd");
391             exit (1);
392         }
393         hash_write (cf, &cf->head, sizeof(cf->head));
394         hash_write (cf, cf->array, hash_bytes);
395     }
396     if (close (cf->hash_fd) < 0)
397     {
398         logf (LOG_FATAL|LOG_ERRNO, "close hash fd");
399         exit (1);
400     }
401     if (close (cf->block_fd) < 0)
402     {
403         logf (LOG_FATAL|LOG_ERRNO, "close block fd");
404         exit (1);
405     }
406     xfree (cf->array);
407     xfree (cf->parray);
408     xfree (cf->iobuf);
409     xfree (cf);
410     return 0;
411 }
412