1 /* This file is part of the Zebra server.
2 Copyright (C) 1994-2011 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
24 #include <idzebra/util.h>
25 #include <yaz/yaz-util.h>
29 /** \brief set to 1 if extra commit/shadow check is to be performed */
32 static int write_head(CFile cf)
34 int left = cf->head.hash_size * sizeof(zint);
37 const char *tab = (char*) cf->array;
41 while (left >= (int) HASH_BSIZE)
43 r = mf_write(cf->hash_mf, bno++, 0, 0, tab);
50 r = mf_write(cf->hash_mf, bno, 0, left, tab);
54 static int read_head(CFile cf)
56 int left = cf->head.hash_size * sizeof(zint);
58 char *tab = (char*) cf->array;
62 while (left >= (int) HASH_BSIZE)
64 if (mf_read(cf->hash_mf, bno++, 0, 0, tab) == -1)
71 if (mf_read(cf->hash_mf, bno, 0, left, tab) == -1)
78 CFile cf_open(MFile mf, MFile_area area, const char *fname,
79 int block_size, int wflag, int *firstp)
83 CFile cf = (CFile) xmalloc(sizeof(*cf));
86 /* avoid valgrind warnings, but set to something nasty */
87 memset(cf, 'Z', sizeof(*cf));
89 yaz_log(YLOG_DEBUG, "cf: open %s %s", fname, wflag ? "rdwr" : "rd");
97 cf->bucket_lru_front = cf->bucket_lru_back = NULL;
98 cf->bucket_in_memory = 0;
99 cf->max_bucket_in_memory = 100;
101 cf->iobuf = (char *) xmalloc(block_size);
102 memset(cf->iobuf, 0, block_size);
110 zebra_mutex_init(&cf->mutex);
112 sprintf(path, "%s-b", fname);
113 if (!(cf->block_mf = mf_open(area, path, block_size, wflag)))
118 sprintf(path, "%s-i", fname);
119 if (!(cf->hash_mf = mf_open(area, path, HASH_BSIZE, wflag)))
124 ret = mf_read(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head);
131 if (ret == 0 || !cf->head.state)
134 cf->head.state = CFILE_STATE_HASH;
135 cf->head.block_size = block_size;
136 cf->head.hash_size = 199;
137 hash_bytes = cf->head.hash_size * sizeof(zint);
138 cf->head.flat_bucket = cf->head.next_bucket = cf->head.first_bucket =
139 (hash_bytes+sizeof(cf->head))/HASH_BSIZE + 2;
140 cf->head.next_block = 1;
141 cf->array = (zint *) xmalloc(hash_bytes);
142 for (i = 0; i<cf->head.hash_size; i++)
146 if (mf_write(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head))
161 assert(cf->head.block_size == block_size);
162 assert(cf->head.hash_size > 2);
163 hash_bytes = cf->head.hash_size * sizeof(zint);
164 assert(cf->head.next_bucket > 0);
165 assert(cf->head.next_block > 0);
166 if (cf->head.state == CFILE_STATE_HASH)
167 cf->array = (zint *) xmalloc(hash_bytes);
170 if (read_head(cf) == -1)
176 if (cf->head.state == CFILE_STATE_HASH)
178 cf->parray = (struct CFile_hash_bucket **)
179 xmalloc(cf->head.hash_size * sizeof(*cf->parray));
180 for (i = 0; i<cf->head.hash_size; i++)
181 cf->parray[i] = NULL;
186 static int cf_hash(CFile cf, zint no)
188 return (int) (((no >> 3) % cf->head.hash_size));
191 static void release_bucket(CFile cf, struct CFile_hash_bucket *p)
194 p->lru_prev->lru_next = p->lru_next;
196 cf->bucket_lru_back = p->lru_next;
198 p->lru_next->lru_prev = p->lru_prev;
200 cf->bucket_lru_front = p->lru_prev;
202 *p->h_prev = p->h_next;
204 p->h_next->h_prev = p->h_prev;
206 --(cf->bucket_in_memory);
210 static int flush_bucket(CFile cf, int no_to_flush)
214 struct CFile_hash_bucket *p;
216 for (i = 0; i != no_to_flush; i++)
218 p = cf->bucket_lru_back;
225 if (mf_write(cf->hash_mf, p->ph.this_bucket, 0, 0, &p->ph))
230 release_bucket(cf, p);
235 static struct CFile_hash_bucket *alloc_bucket(CFile cf, zint block_no, int hno)
237 struct CFile_hash_bucket *p, **pp;
239 if (cf->bucket_in_memory == cf->max_bucket_in_memory)
241 if (flush_bucket(cf, 1))
244 assert(cf->bucket_in_memory < cf->max_bucket_in_memory);
245 ++(cf->bucket_in_memory);
246 p = (struct CFile_hash_bucket *) xmalloc(sizeof(*p));
249 p->lru_prev = cf->bucket_lru_front;
250 if (cf->bucket_lru_front)
251 cf->bucket_lru_front->lru_next = p;
253 cf->bucket_lru_back = p;
254 cf->bucket_lru_front = p;
256 pp = cf->parray + hno;
260 (*pp)->h_prev = &p->h_next;
265 static struct CFile_hash_bucket *get_bucket(CFile cf, zint block_no, int hno)
267 struct CFile_hash_bucket *p;
269 p = alloc_bucket(cf, block_no, hno);
273 if (mf_read(cf->hash_mf, block_no, 0, 0, &p->ph) != 1)
275 yaz_log(YLOG_FATAL, "read get_bucket");
276 release_bucket(cf, p);
279 assert(p->ph.this_bucket == block_no);
283 static struct CFile_hash_bucket *new_bucket(CFile cf, zint *block_nop, int hno)
285 struct CFile_hash_bucket *p;
289 block_no = *block_nop = cf->head.next_bucket++;
290 p = alloc_bucket(cf, block_no, hno);
295 for (i = 0; i<HASH_BUCKET; i++)
300 p->ph.next_bucket = 0;
301 p->ph.this_bucket = block_no;
305 static int cf_lookup_flat(CFile cf, zint no, zint *vno)
307 zint hno = (no*sizeof(zint))/HASH_BSIZE;
308 int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
311 if (mf_read(cf->hash_mf, hno+cf->head.next_bucket, off, sizeof(zint), vno)
319 static int cf_lookup_hash(CFile cf, zint no, zint *vno)
321 int hno = cf_hash(cf, no);
322 struct CFile_hash_bucket *hb;
326 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
328 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
329 if (hb->ph.no[i] == no)
332 *vno = hb->ph.vno[i];
336 for (block_no = cf->array[hno]; block_no; block_no = hb->ph.next_bucket)
338 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
340 if (hb->ph.this_bucket == block_no)
346 for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
348 if (hb->ph.this_bucket == block_no)
350 yaz_log(YLOG_FATAL, "Found hash bucket on other chain(1)");
353 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
354 if (hb->ph.no[i] == no)
356 yaz_log(YLOG_FATAL, "Found hash bucket on other chain (2)");
362 hb = get_bucket(cf, block_no, hno);
365 for (i = 0; i<HASH_BUCKET && hb->ph.vno[i]; i++)
366 if (hb->ph.no[i] == no)
368 *vno = hb->ph.vno[i];
375 static int cf_write_flat(CFile cf, zint no, zint vno)
377 zint hno = (no*sizeof(zint))/HASH_BSIZE;
378 int off = (int) ((no*sizeof(zint)) - hno*HASH_BSIZE);
380 hno += cf->head.next_bucket;
381 if (hno >= cf->head.flat_bucket)
382 cf->head.flat_bucket = hno+1;
384 return mf_write(cf->hash_mf, hno, off, sizeof(zint), &vno);
387 static int cf_moveto_flat(CFile cf)
389 struct CFile_hash_bucket *p;
393 yaz_log(YLOG_DEBUG, "cf: Moving to flat shadow: %s", cf->rmf->name);
394 yaz_log(YLOG_DEBUG, "cf: hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT " total="
396 cf->no_hits, cf->no_miss, cf->bucket_in_memory,
397 cf->head.next_bucket - cf->head.first_bucket);
398 assert(cf->head.state == CFILE_STATE_HASH);
399 if (flush_bucket(cf, -1))
401 assert(cf->bucket_in_memory == 0);
402 p = (struct CFile_hash_bucket *) xmalloc(sizeof(*p));
403 for (i = cf->head.first_bucket; i < cf->head.next_bucket; i++)
405 if (mf_read(cf->hash_mf, i, 0, 0, &p->ph) != 1)
407 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read bucket moveto flat");
411 for (j = 0; j < HASH_BUCKET && p->ph.vno[j]; j++)
413 if (cf_write_flat(cf, p->ph.no[j], p->ph.vno[j]))
425 cf->head.state = CFILE_STATE_FLAT;
430 static int cf_lookup(CFile cf, zint no, zint *vno)
432 if (cf->head.state > 1)
433 return cf_lookup_flat(cf, no, vno);
434 return cf_lookup_hash(cf, no, vno);
437 static zint cf_new_flat(CFile cf, zint no)
439 zint vno = (cf->head.next_block)++;
441 cf_write_flat(cf, no, vno);
445 static zint cf_new_hash(CFile cf, zint no)
447 int hno = cf_hash(cf, no);
448 struct CFile_hash_bucket *hbprev = NULL, *hb = cf->parray[hno];
449 zint *bucketpp = &cf->array[hno];
451 zint vno = (cf->head.next_block)++;
453 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
454 if (!hb->ph.vno[HASH_BUCKET-1])
455 for (i = 0; i<HASH_BUCKET; i++)
467 for (hb = cf->parray[hno]; hb; hb = hb->h_next)
468 if (hb->ph.this_bucket == *bucketpp)
470 bucketpp = &hb->ph.next_bucket;
478 for (hb = cf->bucket_lru_back; hb; hb = hb->lru_next)
480 if (hb->ph.this_bucket == *bucketpp)
482 yaz_log(YLOG_FATAL, "Found hash bucket on other chain");
488 hb = get_bucket(cf, *bucketpp, hno);
491 for (i = 0; i<HASH_BUCKET; i++)
499 bucketpp = &hb->ph.next_bucket;
504 hb = new_bucket(cf, bucketpp, hno);
513 zint cf_new(CFile cf, zint no)
515 if (cf->head.state > 1)
516 return cf_new_flat(cf, no);
517 if (cf->no_miss*2 > cf->no_hits)
519 if (cf_moveto_flat(cf))
521 assert(cf->head.state > 1);
522 return cf_new_flat(cf, no);
524 return cf_new_hash(cf, no);
528 /** \brief reads block from commit area
529 \param cf commit file
530 \param no block number
531 \param offset offset in block
532 \param nbytes number of bytes to read
533 \param buf buffer for content (if read was succesful)
534 \retval 0 block could not be fully read
535 \retval 1 block could be read
538 int cf_read(CFile cf, zint no, int offset, int nbytes, void *buf)
544 zebra_mutex_lock(&cf->mutex);
545 ret = cf_lookup(cf, no, &block);
546 zebra_mutex_unlock(&cf->mutex);
550 yaz_log(YLOG_FATAL, "cf_lookup failed");
555 /* block could not be read */
558 else if (mf_read(cf->block_mf, block, offset, nbytes, buf) != 1)
560 yaz_log(YLOG_FATAL|YLOG_ERRNO, "mf_read no=" ZINT_FORMAT " block=" ZINT_FORMAT, no, block);
566 /** \brief writes block to commit area
567 \param cf commit file
568 \param no block number
569 \param offset offset in block
570 \param nbytes number of bytes to be written
571 \param buf buffer to be written
572 \retval 0 block written
575 int cf_write(CFile cf, zint no, int offset, int nbytes, const void *buf)
581 zebra_mutex_lock(&cf->mutex);
583 ret = cf_lookup(cf, no, &block);
587 zebra_mutex_unlock(&cf->mutex);
592 block = cf_new(cf, no);
595 zebra_mutex_unlock(&cf->mutex);
598 if (offset || nbytes)
600 if (mf_read(cf->rmf, no, 0, 0, cf->iobuf) == -1)
602 memcpy(cf->iobuf + offset, buf, nbytes);
608 zebra_mutex_unlock(&cf->mutex);
609 return mf_write(cf->block_mf, block, offset, nbytes, buf);
612 int cf_close(CFile cf)
615 yaz_log(YLOG_DEBUG, "cf: close hits=%d miss=%d bucket_in_memory=" ZINT_FORMAT
616 " total=" ZINT_FORMAT,
617 cf->no_hits, cf->no_miss, cf->bucket_in_memory,
618 cf->head.next_bucket - cf->head.first_bucket);
619 if (flush_bucket(cf, -1))
625 if (mf_write(cf->hash_mf, 0, 0, sizeof(cf->head), &cf->head))
630 mf_close(cf->hash_mf);
633 mf_close(cf->block_mf);
637 zebra_mutex_destroy(&cf->mutex);
645 * c-file-style: "Stroustrup"
646 * indent-tabs-mode: nil
648 * vim: shiftwidth=4 tabstop=8 expandtab