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