IDZEBRA 2.2.8
cfile.c
Go to the documentation of this file.
1/* This file is part of the Zebra server.
2 Copyright (C) Index Data
3
4Zebra is free software; you can redistribute it and/or modify it under
5the terms of the GNU General Public License as published by the Free
6Software Foundation; either version 2, or (at your option) any later
7version.
8
9Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or
11FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
18*/
19
20#if HAVE_CONFIG_H
21#include <config.h>
22#endif
23#include <assert.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <yaz/yaz-util.h>
28#include <yaz/snprintf.h>
29#include <idzebra/util.h>
30#include "mfile.h"
31#include "cfile.h"
32
34#define EXTRA_CHECK 0
35
36static int write_head(CFile cf)
37{
38 int left = cf->head.hash_size * sizeof(zint);
39 int bno = 1;
40 int r = 0;
41 const char *tab = (char*) cf->array;
42
43 if (!tab)
44 return 0;
45 while (left >= (int) HASH_BSIZE)
46 {
47 r = mf_write(cf->hash_mf, bno++, 0, 0, tab);
48 if (r)
49 return r;
50 tab += HASH_BSIZE;
51 left -= HASH_BSIZE;
52 }
53 if (left > 0)
54 r = mf_write(cf->hash_mf, bno, 0, left, tab);
55 return r;
56}
57
58static int read_head(CFile cf)
59{
60 int left = cf->head.hash_size * sizeof(zint);
61 int bno = 1;
62 char *tab = (char*) cf->array;
63
64 if (!tab)
65 return 0;
66 while (left >= (int) HASH_BSIZE)
67 {
68 if (mf_read(cf->hash_mf, bno++, 0, 0, tab) == -1)
69 return -1;
70 tab += HASH_BSIZE;
71 left -= HASH_BSIZE;
72 }
73 if (left > 0)
74 {
75 if (mf_read(cf->hash_mf, bno, 0, left, tab) == -1)
76 return -1;
77 }
78 return 1;
79}
80
81
82CFile cf_open(MFile mf, MFile_area area, const char *fname,
83 int block_size, int wflag, int *firstp)
84{
85 char path[1024];
86 int i, ret;
87 CFile cf = (CFile) xmalloc(sizeof(*cf));
88 int hash_bytes;
89
90 /* avoid valgrind warnings, but set to something nasty */
91 memset(cf, 'Z', sizeof(*cf));
92
93 yaz_log(YLOG_DEBUG, "cf: open %s %s", fname, wflag ? "rdwr" : "rd");
94
95 cf->block_mf = 0;
96 cf->hash_mf = 0;
97 cf->rmf = mf;
98
99 assert(firstp);
100
101 cf->bucket_lru_front = cf->bucket_lru_back = NULL;
102 cf->bucket_in_memory = 0;
103 cf->max_bucket_in_memory = 100;
104 cf->dirty = 0;
105 cf->iobuf = (char *) xmalloc(block_size);
106 memset(cf->iobuf, 0, block_size);
107 cf->no_hits = 0;
108 cf->no_miss = 0;
109 cf->parray = 0;
110 cf->array = 0;
111 cf->block_mf = 0;
112 cf->hash_mf = 0;
113
115
116 yaz_snprintf(path, sizeof(path), "%s-b", fname);
117 if (!(cf->block_mf = mf_open(area, path, block_size, wflag)))
118 {
119 cf_close(cf);
120 return 0;
121 }
122 yaz_snprintf(path, sizeof(path), "%s-i", fname);
123 if (!(cf->hash_mf = mf_open(area, path, HASH_BSIZE, wflag)))
124 {
125 cf_close(cf);
126 return 0;
127 }
128 ret = mf_read(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
129
130 if (ret == -1)
131 {
132 cf_close(cf);
133 return 0;
134 }
135 if (ret == 0 || !cf->head.state)
136 {
137 *firstp = 1;
139 cf->head.block_size = block_size;
140 cf->head.hash_size = 199;
141 hash_bytes = cf->head.hash_size * sizeof(zint);
143 (hash_bytes+sizeof(cf->head))/HASH_BSIZE + 2;
144 cf->head.next_block = 1;
145 cf->array = (zint *) xmalloc(hash_bytes);
146 for (i = 0; i<cf->head.hash_size; i++)
147 cf->array[i] = 0;
148 if (wflag)
149 {
150 if (mf_write(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head))
151 {
152 cf_close(cf);
153 return 0;
154 }
155 if (write_head(cf))
156 {
157 cf_close(cf);
158 return 0;
159 }
160 }
161 }
162 else
163 {
164 *firstp = 0;
165 assert(cf->head.block_size == block_size);
166 assert(cf->head.hash_size > 2);
167 hash_bytes = cf->head.hash_size * sizeof(zint);
168 assert(cf->head.next_bucket > 0);
169 assert(cf->head.next_block > 0);
170 if (cf->head.state == CFILE_STATE_HASH)
171 cf->array = (zint *) xmalloc(hash_bytes);
172 else
173 cf->array = NULL;
174 if (read_head(cf) == -1)
175 {
176 cf_close(cf);
177 return 0;
178 }
179 }
180 if (cf->head.state == CFILE_STATE_HASH)
181 {
182 cf->parray = (struct CFile_hash_bucket **)
183 xmalloc(cf->head.hash_size * sizeof(*cf->parray));
184 for (i = 0; i<cf->head.hash_size; i++)
185 cf->parray[i] = NULL;
186 }
187 return cf;
188}
189
190static int cf_hash(CFile cf, zint no)
191{
192 return (int) (((no >> 3) % cf->head.hash_size));
193}
194
195static void release_bucket(CFile cf, struct CFile_hash_bucket *p)
196{
197 if (p->lru_prev)
198 p->lru_prev->lru_next = p->lru_next;
199 else
200 cf->bucket_lru_back = p->lru_next;
201 if (p->lru_next)
202 p->lru_next->lru_prev = p->lru_prev;
203 else
204 cf->bucket_lru_front = p->lru_prev;
205
206 *p->h_prev = p->h_next;
207 if (p->h_next)
208 p->h_next->h_prev = p->h_prev;
209
210 --(cf->bucket_in_memory);
211 xfree(p);
212}
213
214static int flush_bucket(CFile cf, int no_to_flush)
215{
216 int i;
217 int ret = 0;
218 struct CFile_hash_bucket *p;
219
220 for (i = 0; i != no_to_flush; i++)
221 {
222 p = cf->bucket_lru_back;
223 if (!p)
224 break;
225 if (p->dirty)
226 {
227 if (ret == 0)
228 {
229 if (mf_write(cf->hash_mf, p->ph.this_bucket, 0, 0, &p->ph))
230 ret = -1;
231 }
232 cf->dirty = 1;
233 }
234 release_bucket(cf, p);
235 }
236 return ret;
237}
238
239static struct CFile_hash_bucket *alloc_bucket(CFile cf, zint block_no, int hno)
240{
241 struct CFile_hash_bucket *p, **pp;
242
244 {
245 if (flush_bucket(cf, 1))
246 return 0;
247 }
248 assert(cf->bucket_in_memory < cf->max_bucket_in_memory);
249 ++(cf->bucket_in_memory);
250 p = (struct CFile_hash_bucket *) xmalloc(sizeof(*p));
251
252 p->lru_next = NULL;
253 p->lru_prev = cf->bucket_lru_front;
254 if (cf->bucket_lru_front)
255 cf->bucket_lru_front->lru_next = p;
256 else
257 cf->bucket_lru_back = p;
258 cf->bucket_lru_front = p;
259
260 pp = cf->parray + hno;
261 p->h_next = *pp;
262 p->h_prev = pp;
263 if (*pp)
264 (*pp)->h_prev = &p->h_next;
265 *pp = p;
266 return p;
267}
268
269static struct CFile_hash_bucket *get_bucket(CFile cf, zint block_no, int hno)
270{
271 struct CFile_hash_bucket *p;
272
273 p = alloc_bucket(cf, block_no, hno);
274 if (!p)
275 return 0;
276 p->dirty = 0;
277 if (mf_read(cf->hash_mf, block_no, 0, 0, &p->ph) != 1)
278 {
279 yaz_log(YLOG_FATAL, "read get_bucket");
280 release_bucket(cf, p);
281 return 0;
282 }
283 assert(p->ph.this_bucket == block_no);
284 return p;
285}
286
287static struct CFile_hash_bucket *new_bucket(CFile cf, zint *block_nop, int hno)
288{
289 struct CFile_hash_bucket *p;
290 int i;
291 zint block_no;
292
293 block_no = *block_nop = cf->head.next_bucket++;
294 p = alloc_bucket(cf, block_no, hno);
295 if (!p)
296 return 0;
297 p->dirty = 1;
298
299 for (i = 0; i<HASH_BUCKET; i++)
300 {
301 p->ph.vno[i] = 0;
302 p->ph.no[i] = 0;
303 }
304 p->ph.next_bucket = 0;
305 p->ph.this_bucket = block_no;
306 return p;
307}
308
309static int cf_lookup_flat(CFile cf, zint no, zint *vno)
310{
311 zint hno = (no*sizeof(zint))/HASH_BSIZE;
312 int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
313
314 *vno = 0;
315 if (mf_read(cf->hash_mf, hno+cf->head.next_bucket, off, sizeof(zint), vno)
316 == -1)
317 return -1;
318 if (*vno)
319 return 1;
320 return 0;
321}
322
323static int cf_lookup_hash(CFile cf, zint no, zint *vno)
324{
325 int hno = cf_hash(cf, no);
326 struct CFile_hash_bucket *hb;
327 zint block_no;
328 int i;
329
330 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
331 {
332 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
333 if (hb->ph.no[i] == no)
334 {
335 (cf->no_hits)++;
336 *vno = hb->ph.vno[i];
337 return 1;
338 }
339 }
340 for (block_no = cf->array[hno]; block_no; block_no = hb->ph.next_bucket)
341 {
342 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
343 {
344 if (hb->ph.this_bucket == block_no)
345 break;
346 }
347 if (hb)
348 continue;
349#if EXTRA_CHECK
350 for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
351 {
352 if (hb->ph.this_bucket == block_no)
353 {
354 yaz_log(YLOG_FATAL, "Found hash bucket on other chain(1)");
355 return -1;
356 }
357 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
358 if (hb->ph.no[i] == no)
359 {
360 yaz_log(YLOG_FATAL, "Found hash bucket on other chain (2)");
361 return -1;
362 }
363 }
364#endif
365 (cf->no_miss)++;
366 hb = get_bucket(cf, block_no, hno);
367 if (!hb)
368 return -1;
369 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
370 if (hb->ph.no[i] == no)
371 {
372 *vno = hb->ph.vno[i];
373 return 1;
374 }
375 }
376 return 0;
377}
378
379static int cf_write_flat(CFile cf, zint no, zint vno)
380{
381 zint hno = (no*sizeof(zint))/HASH_BSIZE;
382 int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
383
384 hno += cf->head.next_bucket;
385 if (hno >= cf->head.flat_bucket)
386 cf->head.flat_bucket = hno+1;
387 cf->dirty = 1;
388 return mf_write(cf->hash_mf, hno, off, sizeof(zint), &vno);
389}
390
391static int cf_moveto_flat(CFile cf)
392{
393 struct CFile_hash_bucket *p;
394 int j;
395 zint i;
396
397 yaz_log(YLOG_DEBUG, "cf: Moving to flat shadow: %s", cf->rmf->name);
398 yaz_log(YLOG_DEBUG, "cf: hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT " total="
400 cf->no_hits, cf->no_miss, cf->bucket_in_memory,
402 assert(cf->head.state == CFILE_STATE_HASH);
403 if (flush_bucket(cf, -1))
404 return -1;
405 assert(cf->bucket_in_memory == 0);
406 p = (struct CFile_hash_bucket *) xmalloc(sizeof(*p));
407 for (i = cf->head.first_bucket; i < cf->head.next_bucket; i++)
408 {
409 if (mf_read(cf->hash_mf, i, 0, 0, &p->ph) != 1)
410 {
411 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read bucket moveto flat");
412 xfree(p);
413 return -1;
414 }
415 for (j = 0; j < HASH_BUCKET && p->ph.vno[j]; j++)
416 {
417 if (cf_write_flat(cf, p->ph.no[j], p->ph.vno[j]))
418 {
419 xfree(p);
420 return -1;
421 }
422 }
423 }
424 xfree(p);
425 xfree(cf->array);
426 cf->array = NULL;
427 xfree(cf->parray);
428 cf->parray = NULL;
430 cf->dirty = 1;
431 return 0;
432}
433
434static int cf_lookup(CFile cf, zint no, zint *vno)
435{
436 if (cf->head.state > 1)
437 return cf_lookup_flat(cf, no, vno);
438 return cf_lookup_hash(cf, no, vno);
439}
440
442{
443 zint vno = (cf->head.next_block)++;
444
445 cf_write_flat(cf, no, vno);
446 return vno;
447}
448
450{
451 int hno = cf_hash(cf, no);
452 struct CFile_hash_bucket *hbprev = NULL, *hb = cf->parray[hno];
453 zint *bucketpp = &cf->array[hno];
454 int i;
455 zint vno = (cf->head.next_block)++;
456
457 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
458 if (!hb->ph.vno[HASH_BUCKET-1])
459 for (i = 0; i<HASH_BUCKET; i++)
460 if (!hb->ph.vno[i])
461 {
462 (cf->no_hits)++;
463 hb->ph.no[i] = no;
464 hb->ph.vno[i] = vno;
465 hb->dirty = 1;
466 return vno;
467 }
468
469 while (*bucketpp)
470 {
471 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
472 if (hb->ph.this_bucket == *bucketpp)
473 {
474 bucketpp = &hb->ph.next_bucket;
475 hbprev = hb;
476 break;
477 }
478 if (hb)
479 continue;
480
481#if EXTRA_CHECK
482 for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
483 {
484 if (hb->ph.this_bucket == *bucketpp)
485 {
486 yaz_log(YLOG_FATAL, "Found hash bucket on other chain");
487 return 0;
488 }
489 }
490#endif
491 (cf->no_miss)++;
492 hb = get_bucket(cf, *bucketpp, hno);
493 if (!hb)
494 return 0;
495 for (i = 0; i<HASH_BUCKET; i++)
496 if (!hb->ph.vno[i])
497 {
498 hb->ph.no[i] = no;
499 hb->ph.vno[i] = vno;
500 hb->dirty = 1;
501 return vno;
502 }
503 bucketpp = &hb->ph.next_bucket;
504 hbprev = hb;
505 }
506 if (hbprev)
507 hbprev->dirty = 1;
508 hb = new_bucket(cf, bucketpp, hno);
509 if (!hb)
510 return 0;
511
512 hb->ph.no[0] = no;
513 hb->ph.vno[0] = vno;
514 return vno;
515}
516
518{
519 if (cf->head.state > 1)
520 return cf_new_flat(cf, no);
521 if (cf->no_miss*2 > cf->no_hits)
522 {
523 if (cf_moveto_flat(cf))
524 return -1;
525 assert(cf->head.state > 1);
526 return cf_new_flat(cf, no);
527 }
528 return cf_new_hash(cf, no);
529}
530
531
542int cf_read(CFile cf, zint no, int offset, int nbytes, void *buf)
543{
544 zint block;
545 int ret;
546
547 assert(cf);
549 ret = cf_lookup(cf, no, &block);
551 if (ret == -1)
552 {
553 /* error */
554 yaz_log(YLOG_FATAL, "cf_lookup failed");
555 return -1;
556 }
557 else if (ret == 0)
558 {
559 /* block could not be read */
560 return ret;
561 }
562 else if (mf_read(cf->block_mf, block, offset, nbytes, buf) != 1)
563 {
564 yaz_log(YLOG_FATAL|YLOG_ERRNO, "mf_read no=" ZINT_FORMAT " block=" ZINT_FORMAT, no, block);
565 return -1;
566 }
567 return 1;
568}
569
579int cf_write(CFile cf, zint no, int offset, int nbytes, const void *buf)
580{
581 zint block;
582 int ret;
583
584 assert(cf);
586
587 ret = cf_lookup(cf, no, &block);
588
589 if (ret == -1)
590 {
592 return ret;
593 }
594 if (ret == 0)
595 {
596 block = cf_new(cf, no);
597 if (!block)
598 {
600 return -1;
601 }
602 if (offset || nbytes)
603 {
604 if (mf_read(cf->rmf, no, 0, 0, cf->iobuf) == -1)
605 return -1;
606 memcpy(cf->iobuf + offset, buf, nbytes);
607 buf = cf->iobuf;
608 offset = 0;
609 nbytes = 0;
610 }
611 }
613 return mf_write(cf->block_mf, block, offset, nbytes, buf);
614}
615
617{
618 int ret = 0;
619 yaz_log(YLOG_DEBUG, "cf: close hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT
620 " total=" ZINT_FORMAT,
621 cf->no_hits, cf->no_miss, cf->bucket_in_memory,
623 if (flush_bucket(cf, -1))
624 ret = -1;
625 if (cf->hash_mf)
626 {
627 if (cf->dirty)
628 {
629 if (mf_write(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head))
630 ret = -1;
631 if (write_head(cf))
632 ret = -1;
633 }
634 mf_close(cf->hash_mf);
635 }
636 if (cf->block_mf)
637 mf_close(cf->block_mf);
638 xfree(cf->array);
639 xfree(cf->parray);
640 xfree(cf->iobuf);
642 xfree(cf);
643 return ret;
644}
645
646/*
647 * Local variables:
648 * c-basic-offset: 4
649 * c-file-style: "Stroustrup"
650 * indent-tabs-mode: nil
651 * End:
652 * vim: shiftwidth=4 tabstop=8 expandtab
653 */
654
static zint cf_new_flat(CFile cf, zint no)
Definition cfile.c:441
CFile cf_open(MFile mf, MFile_area area, const char *fname, int block_size, int wflag, int *firstp)
Definition cfile.c:82
static struct CFile_hash_bucket * alloc_bucket(CFile cf, zint block_no, int hno)
Definition cfile.c:239
static int cf_lookup(CFile cf, zint no, zint *vno)
Definition cfile.c:434
static int cf_write_flat(CFile cf, zint no, zint vno)
Definition cfile.c:379
static int cf_moveto_flat(CFile cf)
Definition cfile.c:391
static int read_head(CFile cf)
Definition cfile.c:58
int cf_read(CFile cf, zint no, int offset, int nbytes, void *buf)
reads block from commit area
Definition cfile.c:542
static int cf_hash(CFile cf, zint no)
Definition cfile.c:190
static zint cf_new_hash(CFile cf, zint no)
Definition cfile.c:449
static int write_head(CFile cf)
Definition cfile.c:36
static struct CFile_hash_bucket * new_bucket(CFile cf, zint *block_nop, int hno)
Definition cfile.c:287
zint cf_new(CFile cf, zint no)
Definition cfile.c:517
static struct CFile_hash_bucket * get_bucket(CFile cf, zint block_no, int hno)
Definition cfile.c:269
static int cf_lookup_hash(CFile cf, zint no, zint *vno)
Definition cfile.c:323
int cf_write(CFile cf, zint no, int offset, int nbytes, const void *buf)
writes block to commit area
Definition cfile.c:579
int cf_close(CFile cf)
Definition cfile.c:616
static int flush_bucket(CFile cf, int no_to_flush)
Definition cfile.c:214
static int cf_lookup_flat(CFile cf, zint no, zint *vno)
Definition cfile.c:309
static void release_bucket(CFile cf, struct CFile_hash_bucket *p)
Definition cfile.c:195
struct CFile_struct * CFile
All in-memory information per CFile.
#define CFILE_STATE_HASH
state of CFile is a hash structure
Definition cfile.h:51
#define CFILE_STATE_FLAT
state of CFile is a flat file file
Definition cfile.h:54
#define HASH_BSIZE
Definition cfile.h:48
#define HASH_BUCKET
number of blocks in hash bucket
Definition cfile.h:30
int mf_write(MFile mf, zint no, int offset, int nbytes, const void *buf)
writes block to metafile
Definition mfile.c:486
int mf_read(MFile mf, zint no, int offset, int nbytes, void *buf)
reads block from metafile
Definition mfile.c:453
int mf_close(MFile mf)
closes metafile
Definition mfile.c:431
MFile mf_open(MFile_area ma, const char *name, int block_size, int wflag)
opens metafile
Definition mfile.c:353
CFile hash structure info in memory.
Definition cfile.h:41
struct CFile_hash_bucket * lru_next
Definition cfile.h:45
struct CFile_ph_bucket ph
Definition cfile.h:42
struct CFile_hash_bucket * h_next
Definition cfile.h:44
struct CFile_hash_bucket * lru_prev
Definition cfile.h:45
struct CFile_hash_bucket ** h_prev
Definition cfile.h:44
int state
Definition cfile.h:58
zint flat_bucket
Definition cfile.h:64
int block_size
Definition cfile.h:60
zint next_block
Definition cfile.h:59
int hash_size
Definition cfile.h:61
zint first_bucket
Definition cfile.h:62
zint next_bucket
Definition cfile.h:63
zint vno[HASH_BUCKET]
Definition cfile.h:35
zint this_bucket
Definition cfile.h:36
zint no[HASH_BUCKET]
Definition cfile.h:34
zint next_bucket
Definition cfile.h:37
All in-memory information per CFile.
Definition cfile.h:69
int no_hits
Definition cfile.h:83
MFile rmf
Definition cfile.h:82
struct CFile_hash_bucket ** parray
Definition cfile.h:75
struct CFile_hash_bucket * bucket_lru_back
Definition cfile.h:77
int no_miss
Definition cfile.h:84
Zebra_mutex mutex
Definition cfile.h:85
struct CFile_hash_bucket * bucket_lru_front
Definition cfile.h:76
zint max_bucket_in_memory
Definition cfile.h:80
zint * array
Definition cfile.h:74
MFile block_mf
Definition cfile.h:72
char * iobuf
Definition cfile.h:81
zint bucket_in_memory
Definition cfile.h:79
struct CFile_head head
Definition cfile.h:70
MFile hash_mf
Definition cfile.h:73
int dirty
Definition cfile.h:78
char name[FILENAME_MAX+1]
Definition mfile.h:79
long zint
Zebra integer.
Definition util.h:66
#define ZINT_FORMAT
Definition util.h:72
int zebra_mutex_unlock(Zebra_mutex *p)
Definition zebra-lock.c:74
int zebra_mutex_init(Zebra_mutex *p)
Definition zebra-lock.c:31
int zebra_mutex_lock(Zebra_mutex *p)
Definition zebra-lock.c:59
int zebra_mutex_destroy(Zebra_mutex *p)
Definition zebra-lock.c:43