1 /* This file is part of the Zebra server.
2 Copyright (C) 2004-2013 Index Data
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <idzebra/util.h>
28 #include <yaz/yaz-util.h>
32 /** \brief set to 1 if extra commit/shadow check is to be performed */
35 static int write_head(CFile cf)
37 int left = cf->head.hash_size * sizeof(zint);
40 const char *tab = (char*) cf->array;
44 while (left >= (int) HASH_BSIZE)
46 r = mf_write(cf->hash_mf, bno++, 0, 0, tab);
53 r = mf_write(cf->hash_mf, bno, 0, left, tab);
57 static int read_head(CFile cf)
59 int left = cf->head.hash_size * sizeof(zint);
61 char *tab = (char*) cf->array;
65 while (left >= (int) HASH_BSIZE)
67 if (mf_read(cf->hash_mf, bno++, 0, 0, tab) == -1)
74 if (mf_read(cf->hash_mf, bno, 0, left, tab) == -1)
81 CFile cf_open(MFile mf, MFile_area area, const char *fname,
82 int block_size, int wflag, int *firstp)
86 CFile cf = (CFile) xmalloc(sizeof(*cf));
89 /* avoid valgrind warnings, but set to something nasty */
90 memset(cf, 'Z', sizeof(*cf));
92 yaz_log(YLOG_DEBUG, "cf: open %s %s", fname, wflag ? "rdwr" : "rd");
100 cf->bucket_lru_front = cf->bucket_lru_back = NULL;
101 cf->bucket_in_memory = 0;
102 cf->max_bucket_in_memory = 100;
104 cf->iobuf = (char *) xmalloc(block_size);
105 memset(cf->iobuf, 0, block_size);
113 zebra_mutex_init(&cf->mutex);
115 sprintf(path, "%s-b", fname);
116 if (!(cf->block_mf = mf_open(area, path, block_size, wflag)))
121 sprintf(path, "%s-i", fname);
122 if (!(cf->hash_mf = mf_open(area, path, HASH_BSIZE, wflag)))
127 ret = mf_read(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
134 if (ret == 0 || !cf->head.state)
137 cf->head.state = CFILE_STATE_HASH;
138 cf->head.block_size = block_size;
139 cf->head.hash_size = 199;
140 hash_bytes = cf->head.hash_size * sizeof(zint);
141 cf->head.flat_bucket = cf->head.next_bucket = cf->head.first_bucket =
142 (hash_bytes+sizeof(cf->head))/HASH_BSIZE + 2;
143 cf->head.next_block = 1;
144 cf->array = (zint *) xmalloc(hash_bytes);
145 for (i = 0; i<cf->head.hash_size; i++)
149 if (mf_write(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head))
164 assert(cf->head.block_size == block_size);
165 assert(cf->head.hash_size > 2);
166 hash_bytes = cf->head.hash_size * sizeof(zint);
167 assert(cf->head.next_bucket > 0);
168 assert(cf->head.next_block > 0);
169 if (cf->head.state == CFILE_STATE_HASH)
170 cf->array = (zint *) xmalloc(hash_bytes);
173 if (read_head(cf) == -1)
179 if (cf->head.state == CFILE_STATE_HASH)
181 cf->parray = (struct CFile_hash_bucket **)
182 xmalloc(cf->head.hash_size * sizeof(*cf->parray));
183 for (i = 0; i<cf->head.hash_size; i++)
184 cf->parray[i] = NULL;
189 static int cf_hash(CFile cf, zint no)
191 return (int) (((no >> 3) % cf->head.hash_size));
194 static void release_bucket(CFile cf, struct CFile_hash_bucket *p)
197 p->lru_prev->lru_next = p->lru_next;
199 cf->bucket_lru_back = p->lru_next;
201 p->lru_next->lru_prev = p->lru_prev;
203 cf->bucket_lru_front = p->lru_prev;
205 *p->h_prev = p->h_next;
207 p->h_next->h_prev = p->h_prev;
209 --(cf->bucket_in_memory);
213 static int flush_bucket(CFile cf, int no_to_flush)
217 struct CFile_hash_bucket *p;
219 for (i = 0; i != no_to_flush; i++)
221 p = cf->bucket_lru_back;
228 if (mf_write(cf->hash_mf, p->ph.this_bucket, 0, 0, &p->ph))
233 release_bucket(cf, p);
238 static struct CFile_hash_bucket *alloc_bucket(CFile cf, zint block_no, int hno)
240 struct CFile_hash_bucket *p, **pp;
242 if (cf->bucket_in_memory == cf->max_bucket_in_memory)
244 if (flush_bucket(cf, 1))
247 assert(cf->bucket_in_memory < cf->max_bucket_in_memory);
248 ++(cf->bucket_in_memory);
249 p = (struct CFile_hash_bucket *) xmalloc(sizeof(*p));
252 p->lru_prev = cf->bucket_lru_front;
253 if (cf->bucket_lru_front)
254 cf->bucket_lru_front->lru_next = p;
256 cf->bucket_lru_back = p;
257 cf->bucket_lru_front = p;
259 pp = cf->parray + hno;
263 (*pp)->h_prev = &p->h_next;
268 static struct CFile_hash_bucket *get_bucket(CFile cf, zint block_no, int hno)
270 struct CFile_hash_bucket *p;
272 p = alloc_bucket(cf, block_no, hno);
276 if (mf_read(cf->hash_mf, block_no, 0, 0, &p->ph) != 1)
278 yaz_log(YLOG_FATAL, "read get_bucket");
279 release_bucket(cf, p);
282 assert(p->ph.this_bucket == block_no);
286 static struct CFile_hash_bucket *new_bucket(CFile cf, zint *block_nop, int hno)
288 struct CFile_hash_bucket *p;
292 block_no = *block_nop = cf->head.next_bucket++;
293 p = alloc_bucket(cf, block_no, hno);
298 for (i = 0; i<HASH_BUCKET; i++)
303 p->ph.next_bucket = 0;
304 p->ph.this_bucket = block_no;
308 static int cf_lookup_flat(CFile cf, zint no, zint *vno)
310 zint hno = (no*sizeof(zint))/HASH_BSIZE;
311 int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
314 if (mf_read(cf->hash_mf, hno+cf->head.next_bucket, off, sizeof(zint), vno)
322 static int cf_lookup_hash(CFile cf, zint no, zint *vno)
324 int hno = cf_hash(cf, no);
325 struct CFile_hash_bucket *hb;
329 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
331 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
332 if (hb->ph.no[i] == no)
335 *vno = hb->ph.vno[i];
339 for (block_no = cf->array[hno]; block_no; block_no = hb->ph.next_bucket)
341 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
343 if (hb->ph.this_bucket == block_no)
349 for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
351 if (hb->ph.this_bucket == block_no)
353 yaz_log(YLOG_FATAL, "Found hash bucket on other chain(1)");
356 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
357 if (hb->ph.no[i] == no)
359 yaz_log(YLOG_FATAL, "Found hash bucket on other chain (2)");
365 hb = get_bucket(cf, block_no, hno);
368 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
369 if (hb->ph.no[i] == no)
371 *vno = hb->ph.vno[i];
378 static int cf_write_flat(CFile cf, zint no, zint vno)
380 zint hno = (no*sizeof(zint))/HASH_BSIZE;
381 int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
383 hno += cf->head.next_bucket;
384 if (hno >= cf->head.flat_bucket)
385 cf->head.flat_bucket = hno+1;
387 return mf_write(cf->hash_mf, hno, off, sizeof(zint), &vno);
390 static int cf_moveto_flat(CFile cf)
392 struct CFile_hash_bucket *p;
396 yaz_log(YLOG_DEBUG, "cf: Moving to flat shadow: %s", cf->rmf->name);
397 yaz_log(YLOG_DEBUG, "cf: hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT " total="
399 cf->no_hits, cf->no_miss, cf->bucket_in_memory,
400 cf->head.next_bucket - cf->head.first_bucket);
401 assert(cf->head.state == CFILE_STATE_HASH);
402 if (flush_bucket(cf, -1))
404 assert(cf->bucket_in_memory == 0);
405 p = (struct CFile_hash_bucket *) xmalloc(sizeof(*p));
406 for (i = cf->head.first_bucket; i < cf->head.next_bucket; i++)
408 if (mf_read(cf->hash_mf, i, 0, 0, &p->ph) != 1)
410 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read bucket moveto flat");
414 for (j = 0; j < HASH_BUCKET && p->ph.vno[j]; j++)
416 if (cf_write_flat(cf, p->ph.no[j], p->ph.vno[j]))
428 cf->head.state = CFILE_STATE_FLAT;
433 static int cf_lookup(CFile cf, zint no, zint *vno)
435 if (cf->head.state > 1)
436 return cf_lookup_flat(cf, no, vno);
437 return cf_lookup_hash(cf, no, vno);
440 static zint cf_new_flat(CFile cf, zint no)
442 zint vno = (cf->head.next_block)++;
444 cf_write_flat(cf, no, vno);
448 static zint cf_new_hash(CFile cf, zint no)
450 int hno = cf_hash(cf, no);
451 struct CFile_hash_bucket *hbprev = NULL, *hb = cf->parray[hno];
452 zint *bucketpp = &cf->array[hno];
454 zint vno = (cf->head.next_block)++;
456 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
457 if (!hb->ph.vno[HASH_BUCKET-1])
458 for (i = 0; i<HASH_BUCKET; i++)
470 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
471 if (hb->ph.this_bucket == *bucketpp)
473 bucketpp = &hb->ph.next_bucket;
481 for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
483 if (hb->ph.this_bucket == *bucketpp)
485 yaz_log(YLOG_FATAL, "Found hash bucket on other chain");
491 hb = get_bucket(cf, *bucketpp, hno);
494 for (i = 0; i<HASH_BUCKET; i++)
502 bucketpp = &hb->ph.next_bucket;
507 hb = new_bucket(cf, bucketpp, hno);
516 zint cf_new(CFile cf, zint no)
518 if (cf->head.state > 1)
519 return cf_new_flat(cf, no);
520 if (cf->no_miss*2 > cf->no_hits)
522 if (cf_moveto_flat(cf))
524 assert(cf->head.state > 1);
525 return cf_new_flat(cf, no);
527 return cf_new_hash(cf, no);
531 /** \brief reads block from commit area
532 \param cf commit file
533 \param no block number
534 \param offset offset in block
535 \param nbytes number of bytes to read
536 \param buf buffer for content (if read was succesful)
537 \retval 0 block could not be fully read
538 \retval 1 block could be read
541 int cf_read(CFile cf, zint no, int offset, int nbytes, void *buf)
547 zebra_mutex_lock(&cf->mutex);
548 ret = cf_lookup(cf, no, &block);
549 zebra_mutex_unlock(&cf->mutex);
553 yaz_log(YLOG_FATAL, "cf_lookup failed");
558 /* block could not be read */
561 else if (mf_read(cf->block_mf, block, offset, nbytes, buf) != 1)
563 yaz_log(YLOG_FATAL|YLOG_ERRNO, "mf_read no=" ZINT_FORMAT " block=" ZINT_FORMAT, no, block);
569 /** \brief writes block to commit area
570 \param cf commit file
571 \param no block number
572 \param offset offset in block
573 \param nbytes number of bytes to be written
574 \param buf buffer to be written
575 \retval 0 block written
578 int cf_write(CFile cf, zint no, int offset, int nbytes, const void *buf)
584 zebra_mutex_lock(&cf->mutex);
586 ret = cf_lookup(cf, no, &block);
590 zebra_mutex_unlock(&cf->mutex);
595 block = cf_new(cf, no);
598 zebra_mutex_unlock(&cf->mutex);
601 if (offset || nbytes)
603 if (mf_read(cf->rmf, no, 0, 0, cf->iobuf) == -1)
605 memcpy(cf->iobuf + offset, buf, nbytes);
611 zebra_mutex_unlock(&cf->mutex);
612 return mf_write(cf->block_mf, block, offset, nbytes, buf);
615 int cf_close(CFile cf)
618 yaz_log(YLOG_DEBUG, "cf: close hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT
619 " total=" ZINT_FORMAT,
620 cf->no_hits, cf->no_miss, cf->bucket_in_memory,
621 cf->head.next_bucket - cf->head.first_bucket);
622 if (flush_bucket(cf, -1))
628 if (mf_write(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head))
633 mf_close(cf->hash_mf);
636 mf_close(cf->block_mf);
640 zebra_mutex_destroy(&cf->mutex);
648 * c-file-style: "Stroustrup"
649 * indent-tabs-mode: nil
651 * vim: shiftwidth=4 tabstop=8 expandtab