Put local variables footer in all c, h files.
[idzebra-moved-to-github.git] / bfile / cfile.c
1 /* $Id: cfile.c,v 1.36 2006-05-10 08:13:17 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <idzebra/util.h>
28 #include <yaz/yaz-util.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(zint);
35     int bno = 1;
36     const char *tab = (char*) cf->array;
37
38     if (!tab)
39         return 0;
40     while (left >= (int) HASH_BSIZE)
41     {
42         mf_write (cf->hash_mf, bno++, 0, 0, tab);
43         tab += HASH_BSIZE;
44         left -= HASH_BSIZE;
45     }
46     if (left > 0)
47         mf_write (cf->hash_mf, bno, 0, left, tab);
48     return 0;
49 }
50
51 static int read_head (CFile cf)
52 {
53     int left = cf->head.hash_size * sizeof(zint);
54     int bno = 1;
55     char *tab = (char*) cf->array;
56
57     if (!tab)
58         return 0;
59     while (left >= (int) HASH_BSIZE)
60     {
61         mf_read (cf->hash_mf, bno++, 0, 0, tab);
62         tab += HASH_BSIZE;
63         left -= HASH_BSIZE;
64     }
65     if (left > 0)
66         mf_read (cf->hash_mf, bno, 0, left, tab);
67     return 0;
68 }
69
70
71 CFile cf_open (MFile mf, MFile_area area, const char *fname,
72                int block_size, int wflag, int *firstp)
73 {
74     char path[1024];
75     int i;
76     CFile cf = (CFile) xmalloc (sizeof(*cf));
77     int hash_bytes;
78    
79     cf->rmf = mf; 
80     yaz_log (YLOG_DEBUG, "cf: open %s %s", cf->rmf->name, wflag ? "rdwr" : "rd");
81     sprintf (path, "%s-b", fname);
82     if (!(cf->block_mf = mf_open (area, path, block_size, wflag)))
83     {
84         yaz_log (YLOG_FATAL|YLOG_ERRNO, "Failed to open %s", path);
85         exit (1);
86     }
87     sprintf (path, "%s-i", fname);
88     if (!(cf->hash_mf = mf_open (area, path, HASH_BSIZE, wflag)))
89     {
90         yaz_log (YLOG_FATAL|YLOG_ERRNO, "Failed to open %s", path);
91         exit (1);
92     }
93     assert (firstp);
94     if (!mf_read (cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head) ||
95         !cf->head.state)
96     {
97         *firstp = 1;
98         cf->head.state = 1;
99         cf->head.block_size = block_size;
100         cf->head.hash_size = 199;
101         hash_bytes = cf->head.hash_size * sizeof(zint);
102         cf->head.flat_bucket = cf->head.next_bucket = cf->head.first_bucket = 
103             (hash_bytes+sizeof(cf->head))/HASH_BSIZE + 2;
104         cf->head.next_block = 1;
105         if (wflag)
106             mf_write (cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
107         cf->array = (zint *) xmalloc (hash_bytes);
108         for (i = 0; i<cf->head.hash_size; i++)
109             cf->array[i] = 0;
110         if (wflag)
111             write_head (cf);
112     }
113     else
114     {
115         *firstp = 0;
116         assert (cf->head.block_size == block_size);
117         assert (cf->head.hash_size > 2);
118         hash_bytes = cf->head.hash_size * sizeof(zint);
119         assert (cf->head.next_bucket > 0);
120         assert (cf->head.next_block > 0);
121         if (cf->head.state == 1)
122             cf->array = (zint *) xmalloc (hash_bytes);
123         else
124             cf->array = NULL;
125         read_head (cf);
126     }
127     if (cf->head.state == 1)
128     {
129         cf->parray = (struct CFile_hash_bucket **)
130             xmalloc (cf->head.hash_size * sizeof(*cf->parray));
131         for (i = 0; i<cf->head.hash_size; i++)
132             cf->parray[i] = NULL;
133     }
134     else
135         cf->parray = NULL;
136     cf->bucket_lru_front = cf->bucket_lru_back = NULL;
137     cf->bucket_in_memory = 0;
138     cf->max_bucket_in_memory = 100;
139     cf->dirty = 0;
140     cf->iobuf = (char *) xmalloc (cf->head.block_size);
141     memset (cf->iobuf, 0, cf->head.block_size);
142     cf->no_hits = 0;
143     cf->no_miss = 0;
144     zebra_mutex_init (&cf->mutex);
145     return cf;
146 }
147
148 static int cf_hash (CFile cf, zint no)
149 {
150     return (int) (((no >> 3) % cf->head.hash_size));
151 }
152
153 static void release_bucket (CFile cf, struct CFile_hash_bucket *p)
154 {
155     if (p->lru_prev)
156         p->lru_prev->lru_next = p->lru_next;
157     else
158         cf->bucket_lru_back = p->lru_next;
159     if (p->lru_next)
160         p->lru_next->lru_prev = p->lru_prev;
161     else
162         cf->bucket_lru_front = p->lru_prev;
163
164     *p->h_prev = p->h_next;
165     if (p->h_next)
166         p->h_next->h_prev = p->h_prev;
167     
168     --(cf->bucket_in_memory);
169     xfree (p);
170 }
171
172 static void flush_bucket (CFile cf, int no_to_flush)
173 {
174     int i;
175     struct CFile_hash_bucket *p;
176
177     for (i = 0; i != no_to_flush; i++)
178     {
179         p = cf->bucket_lru_back;
180         if (!p)
181             break;
182         if (p->dirty)
183         {
184             mf_write (cf->hash_mf, p->ph.this_bucket, 0, 0, &p->ph);
185             cf->dirty = 1;
186         }
187         release_bucket (cf, p);
188     }
189 }
190
191 static struct CFile_hash_bucket *alloc_bucket (CFile cf, zint block_no, int hno)
192 {
193     struct CFile_hash_bucket *p, **pp;
194
195     if (cf->bucket_in_memory == cf->max_bucket_in_memory)
196         flush_bucket (cf, 1);
197     assert (cf->bucket_in_memory < cf->max_bucket_in_memory);
198     ++(cf->bucket_in_memory);
199     p = (struct CFile_hash_bucket *) xmalloc (sizeof(*p));
200
201     p->lru_next = NULL;
202     p->lru_prev = cf->bucket_lru_front;
203     if (cf->bucket_lru_front)
204         cf->bucket_lru_front->lru_next = p;
205     else
206         cf->bucket_lru_back = p;
207     cf->bucket_lru_front = p; 
208
209     pp = cf->parray + hno;
210     p->h_next = *pp;
211     p->h_prev = pp;
212     if (*pp)
213         (*pp)->h_prev = &p->h_next;
214     *pp = p;
215     return p;
216 }
217
218 static struct CFile_hash_bucket *get_bucket (CFile cf, zint block_no, int hno)
219 {
220     struct CFile_hash_bucket *p;
221
222     p = alloc_bucket (cf, block_no, hno);
223     if (!mf_read (cf->hash_mf, block_no, 0, 0, &p->ph))
224     {
225         yaz_log (YLOG_FATAL|YLOG_ERRNO, "read get_bucket");
226         exit (1);
227     }
228     assert (p->ph.this_bucket == block_no);
229     p->dirty = 0;
230     return p;
231 }
232
233 static struct CFile_hash_bucket *new_bucket (CFile cf, zint *block_nop, int hno)
234 {
235     struct CFile_hash_bucket *p;
236     int i;
237     zint block_no;
238
239     block_no = *block_nop = cf->head.next_bucket++;
240     p = alloc_bucket (cf, block_no, hno);
241
242     for (i = 0; i<HASH_BUCKET; i++)
243     {
244         p->ph.vno[i] = 0;
245         p->ph.no[i] = 0;
246     }
247     p->ph.next_bucket = 0;
248     p->ph.this_bucket = block_no;
249     p->dirty = 1;
250     return p;
251 }
252
253 static zint cf_lookup_flat (CFile cf, zint no)
254 {
255     zint hno = (no*sizeof(zint))/HASH_BSIZE;
256     int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
257     zint vno = 0;
258
259     mf_read (cf->hash_mf, hno+cf->head.next_bucket, off, sizeof(zint), &vno);
260     return vno;
261 }
262
263 static zint cf_lookup_hash (CFile cf, zint no)
264 {
265     int hno = cf_hash (cf, no);
266     struct CFile_hash_bucket *hb;
267     zint block_no;
268     int i;
269
270     for (hb = cf->parray[hno]; hb; hb = hb->h_next)
271     {
272         for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
273             if (hb->ph.no[i] == no)
274             {
275                 (cf->no_hits)++;
276                 return hb->ph.vno[i];
277             }
278     }
279     for (block_no = cf->array[hno]; block_no; block_no = hb->ph.next_bucket)
280     {
281         for (hb = cf->parray[hno]; hb; hb = hb->h_next)
282         {
283             if (hb->ph.this_bucket == block_no)
284                 break;
285         }
286         if (hb)
287             continue;
288 #if 0
289         /* extra check ... */
290         for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
291         {
292             if (hb->ph.this_bucket == block_no)
293             {
294                 yaz_log (YLOG_FATAL, "Found hash bucket on other chain (1)");
295                 abort ();
296             }
297             for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
298                 if (hb->ph.no[i] == no)
299                 {
300                     yaz_log (YLOG_FATAL, "Found hash bucket on other chain (2)");
301                     abort ();
302                 }
303         }
304 #endif
305         (cf->no_miss)++;
306         hb = get_bucket (cf, block_no, hno);
307         for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
308             if (hb->ph.no[i] == no)
309                 return hb->ph.vno[i];
310     }
311     return 0;
312 }
313
314 static void cf_write_flat (CFile cf, zint no, zint vno)
315 {
316     zint hno = (no*sizeof(zint))/HASH_BSIZE;
317     int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
318
319     hno += cf->head.next_bucket;
320     if (hno >= cf->head.flat_bucket)
321         cf->head.flat_bucket = hno+1;
322     cf->dirty = 1;
323     mf_write (cf->hash_mf, hno, off, sizeof(zint), &vno);
324 }
325
326 static void cf_moveto_flat (CFile cf)
327 {
328     struct CFile_hash_bucket *p;
329     int j;
330     zint i;
331
332     yaz_log (YLOG_DEBUG, "cf: Moving to flat shadow: %s", cf->rmf->name);
333     yaz_log (YLOG_DEBUG, "cf: hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT " total="
334           ZINT_FORMAT,
335         cf->no_hits, cf->no_miss, cf->bucket_in_memory, 
336         cf->head.next_bucket - cf->head.first_bucket);
337     assert (cf->head.state == 1);
338     flush_bucket (cf, -1);
339     assert (cf->bucket_in_memory == 0);
340     p = (struct CFile_hash_bucket *) xmalloc (sizeof(*p));
341     for (i = cf->head.first_bucket; i < cf->head.next_bucket; i++)
342     {
343         if (!mf_read (cf->hash_mf, i, 0, 0, &p->ph))
344         {
345             yaz_log (YLOG_FATAL|YLOG_ERRNO, "read bucket moveto flat");
346             exit (1);
347         }
348         for (j = 0; j < HASH_BUCKET && p->ph.vno[j]; j++)
349             cf_write_flat (cf, p->ph.no[j], p->ph.vno[j]);
350     }
351     xfree (p);
352     xfree (cf->array);
353     cf->array = NULL;
354     xfree (cf->parray);
355     cf->parray = NULL;
356     cf->head.state = 2;
357     cf->dirty = 1;
358 }
359
360 static zint cf_lookup (CFile cf, zint no)
361 {
362     if (cf->head.state > 1)
363         return cf_lookup_flat (cf, no);
364     return cf_lookup_hash (cf, no);
365 }
366
367 static zint cf_new_flat (CFile cf, zint no)
368 {
369     zint vno = (cf->head.next_block)++;
370
371     cf_write_flat (cf, no, vno);
372     return vno;
373 }
374
375 static zint cf_new_hash (CFile cf, zint no)
376 {
377     int hno = cf_hash (cf, no);
378     struct CFile_hash_bucket *hbprev = NULL, *hb = cf->parray[hno];
379     zint *bucketpp = &cf->array[hno]; 
380     int i;
381     zint vno = (cf->head.next_block)++;
382   
383     for (hb = cf->parray[hno]; hb; hb = hb->h_next)
384         if (!hb->ph.vno[HASH_BUCKET-1])
385             for (i = 0; i<HASH_BUCKET; i++)
386                 if (!hb->ph.vno[i])
387                 {
388                     (cf->no_hits)++;
389                     hb->ph.no[i] = no;
390                     hb->ph.vno[i] = vno;
391                     hb->dirty = 1;
392                     return vno;
393                 }
394
395     while (*bucketpp)
396     {
397         for (hb = cf->parray[hno]; hb; hb = hb->h_next)
398             if (hb->ph.this_bucket == *bucketpp)
399             {
400                 bucketpp = &hb->ph.next_bucket;
401                 hbprev = hb;
402                 break;
403             }
404         if (hb)
405             continue;
406
407 #if 0
408         /* extra check ... */
409         for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
410         {
411             if (hb->ph.this_bucket == *bucketpp)
412             {
413                 yaz_log (YLOG_FATAL, "Found hash bucket on other chain");
414                 abort ();
415             }
416         }
417 #endif
418         (cf->no_miss)++;
419         hb = get_bucket (cf, *bucketpp, hno);
420         assert (hb);
421         for (i = 0; i<HASH_BUCKET; i++)
422             if (!hb->ph.vno[i])
423             {
424                 hb->ph.no[i] = no;
425                 hb->ph.vno[i] = vno;
426                 hb->dirty = 1;
427                 return vno;
428             }
429         bucketpp = &hb->ph.next_bucket;
430         hbprev = hb;
431     }
432     if (hbprev)
433         hbprev->dirty = 1;
434     hb = new_bucket (cf, bucketpp, hno);
435     hb->ph.no[0] = no;
436     hb->ph.vno[0] = vno;
437     return vno;
438 }
439
440 zint cf_new (CFile cf, zint no)
441 {
442     if (cf->head.state > 1)
443         return cf_new_flat (cf, no);
444     if (cf->no_miss*2 > cf->no_hits)
445     {
446         cf_moveto_flat (cf);
447         assert (cf->head.state > 1);
448         return cf_new_flat (cf, no);
449     }
450     return cf_new_hash (cf, no);
451 }
452
453
454 int cf_read (CFile cf, zint no, int offset, int nbytes, void *buf)
455 {
456     zint block;
457     
458     assert (cf);
459     zebra_mutex_lock (&cf->mutex);
460     if (!(block = cf_lookup (cf, no)))
461     {
462         zebra_mutex_unlock (&cf->mutex);
463         return -1;
464     }
465     zebra_mutex_unlock (&cf->mutex);
466     if (!mf_read (cf->block_mf, block, offset, nbytes, buf))
467     {
468         yaz_log (YLOG_FATAL|YLOG_ERRNO, "cf_read no=" ZINT_FORMAT " block=" ZINT_FORMAT, no, block);
469         exit (1);
470     }
471     return 1;
472 }
473
474 int cf_write (CFile cf, zint no, int offset, int nbytes, const void *buf)
475 {
476     zint block;
477
478     assert (cf);
479     zebra_mutex_lock (&cf->mutex);
480     if (!(block = cf_lookup (cf, no)))
481     {
482         block = cf_new (cf, no);
483         if (offset || nbytes)
484         {
485             mf_read (cf->rmf, no, 0, 0, cf->iobuf);
486             memcpy (cf->iobuf + offset, buf, nbytes);
487             buf = cf->iobuf;
488             offset = 0;
489             nbytes = 0;
490         }
491     }
492     zebra_mutex_unlock (&cf->mutex);
493     if (mf_write (cf->block_mf, block, offset, nbytes, buf))
494     {
495         yaz_log (YLOG_FATAL|YLOG_ERRNO, "cf_write no=" ZINT_FORMAT
496               " block=" ZINT_FORMAT, no, block);
497         exit (1);
498     }
499     return 0;
500 }
501
502 int cf_close (CFile cf)
503 {
504     yaz_log (YLOG_DEBUG, "cf: close hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT
505           " total=" ZINT_FORMAT,
506           cf->no_hits, cf->no_miss, cf->bucket_in_memory,
507           cf->head.next_bucket - cf->head.first_bucket);
508     flush_bucket (cf, -1);
509     if (cf->dirty)
510     {
511         mf_write (cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
512         write_head (cf);
513     }
514     mf_close (cf->hash_mf);
515     mf_close (cf->block_mf);
516     xfree (cf->array);
517     xfree (cf->parray);
518     xfree (cf->iobuf);
519     zebra_mutex_destroy (&cf->mutex);
520     xfree (cf);
521     return 0;
522 }
523
524 /*
525  * Local variables:
526  * c-basic-offset: 4
527  * indent-tabs-mode: nil
528  * End:
529  * vim: shiftwidth=4 tabstop=8 expandtab
530  */
531