diff options
Diffstat (limited to 'debian/htdig/htdig-3.2.0b6/db/mp_cmpr.c')
-rw-r--r-- | debian/htdig/htdig-3.2.0b6/db/mp_cmpr.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/debian/htdig/htdig-3.2.0b6/db/mp_cmpr.c b/debian/htdig/htdig-3.2.0b6/db/mp_cmpr.c new file mode 100644 index 00000000..d595ef76 --- /dev/null +++ b/debian/htdig/htdig-3.2.0b6/db/mp_cmpr.c @@ -0,0 +1,1136 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Loic Dachary. All rights reserved. + * + * Overview of the code (by Lachlan Andrew, lha@users.sourceforge.net): + * + * This code compresses pages on-the-fly, either using a built-in algorithm, + * or using the zlib library. The compressed page is stored in pages of + * size CMPR_MULTIPLY(db_io->pagesize) -- a fixed multiple of the true + * page size, db_io->pagesize. If the compressed page requires multiple + * pages, extra pages are allocated at the end of the file, and "chained" + * on to the original page. The chain is specified as an array in the first + * page (not a linked list). If a subsequent write of the page requires + * a shorter chain, the spare pages are recorded as "free" and listed in + * the weak-compression database (with suffix given by DB_CMPR_SUFFIX). + * + * When writing a compressed page, extra memory may need to be allocated if + * chaining occurs. This can cause recursive calls to CDB___memp_alloc(), + * since the latter may write dirty cache pages to satisfy the request. + * There is currently an explicit check for recursive calls, both in + * CDB___memp_alloc() and CDB___memp_cmpr_write(), but a more elegant + * solution would be nice. + * + * There also seems to be an issue with the memory allocation for the chain + * array. The small allocations seem to cause fragmentation in the memory + * pool (seen as very many small clean blocks, which don't go away). + * + * + * TODO: + * Keith Bostic says: + * The only change I'd probably think about is if + * we should merge the call to CDB___memp_pg and CDB___memp_cmpr -- kind + * of a stack of page modification routines, that sits on top of + * CDB___os_io. That's a bigger change, but it's probably cleaner + * in the long-run. + * + * Pending questions: + * + * The CMPR structure contains binary data. Should we store them in network order ? + * How is this related to DB_AM_SWAP ? + * + * The calls to cmpr_open/cmpr_close in memp_pgread/pgwrite are probably not + * at the right place / most logical place in the function. I have troubles + * finding out where to put them. They work, that's not the problem. Just don't + * know if they should be before or after. + * + * When opening weakcmpr DB, the DB_THREAD flag is not available. Should it be + * set if the main DB has this flag set ? + * + * In CDB___memp_cmpr, niop is always multiplied by compression factor for page 0. + * I see no problems with this but it's a bit awkward. + * + * In CDB___memp_cmpr_page, the page built fills some fields of the PAGE structure + * others are set to 0. I'm not 100% sure this is enough. It should only impact + * utilities that read pages by incrementing pgno. Only stat does this an it's + * enough for it. I've not found any other context where these fake pages are + * used. + * + */ + +#include "db_config.h" + +#ifndef lint +static const char sccsid[] = "@(#)mp_cmpr.c 1.1 (Senga) 01/08/99"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#include <errno.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "shqueue.h" +#include "db_shash.h" +#include "mp.h" +#include "db_page.h" +#include "common_ext.h" + +#ifdef DEBUG +#include "WordMonitor.h" +#endif /* DEBUG */ + +#if 0 +#define DEBUG_CMPR 1 +#endif +#if 0 +#define DEBUG_CMPR_ALLOC 1 +#endif + +#ifdef HAVE_LIBZ +#include "zlib.h" +#endif /* HAVE_LIBZ */ +static int memp_cmpr_zlib_level = -1; + +/* + * Helpers declarations. + */ +static int CDB___memp_cmpr_page __P((DB_MPOOLFILE *, CMPR *, DB_IO *, ssize_t *)); + +/* + * Maximum chain length + * Cast to signed, as -1 used as a flag, which compares bigger on some systems + */ +#define CMPR_MAX (int)(dbenv->mp_cmpr_info->max_npages) + +#define CMPR_MULTIPLY(n) ((n) << (dbenv->mp_cmpr_info->coefficient)) +#define CMPR_DIVIDE(n) ((n) >> (dbenv->mp_cmpr_info->coefficient)) + +#ifdef HAVE_LIBZ +static DB_CMPR_INFO default_cmpr_info = { + CDB___memp_cmpr_deflate, + CDB___memp_cmpr_inflate, + 3, /* reduce page size by factor of 1<<3 = 8 */ + 9, /* allow 9 reduced pages, in case "compression" expands data */ + 6, /* zlib compression level */ + NULL +}; +#else /* HAVE_LIBZ */ +static DB_CMPR_INFO default_cmpr_info = { + 0, + 0, + 0, + 0, + 0, + NULL +}; +#endif /* HAVE_LIBZ */ + +/* + * Entry point. Functionaly equivalent to CDB___os_io. + * Compress/uncompress pages before returning them or writing them to disk. + */ + +/* + * CDB___memp_cmpr -- + * Transparent compression read/write + * + * PUBLIC: int CDB___memp_cmpr __P((DB_MPOOLFILE *, BH *, DB_IO *, int, ssize_t *)); + */ +int +CDB___memp_cmpr(dbmfp, bhp, db_io, flag, niop) + DB_MPOOLFILE *dbmfp; + BH *bhp; + DB_IO *db_io; + int flag; + ssize_t *niop; +{ + size_t orig_pagesize = db_io->pagesize; + db_pgno_t orig_pgno = db_io->pgno; + size_t orig_bytes = db_io->bytes; + DB_ENV *dbenv = dbmfp->dbmp->dbenv; + DB_CMPR_INFO *cmpr_info = dbenv->mp_cmpr_info; + int ret = 0; + + db_io->pagesize = CMPR_DIVIDE(db_io->pagesize); + db_io->bytes = CMPR_DIVIDE(db_io->bytes); + +#ifdef HAVE_LIBZ + if(memp_cmpr_zlib_level == -1) + { + memp_cmpr_zlib_level = cmpr_info->zlib_flags; + if(memp_cmpr_zlib_level == -1) + memp_cmpr_zlib_level = Z_DEFAULT_COMPRESSION; + } +#endif + + /* + * Page 0 is a special case. It contains the metadata information (at most 512 bytes) + * and must not be compressed because it is read with CDB___os_read and not CDB___os_io. + */ + switch (flag) { + case DB_IO_READ: + if(db_io->pgno == 0) { + ret = CDB___os_io(db_io, DB_IO_READ, niop); + *niop = CMPR_MULTIPLY(*niop); + } else + ret = CDB___memp_cmpr_read(dbmfp, bhp, db_io, niop); + break; + case DB_IO_WRITE: + if(db_io->pgno == 0) { + ret = CDB___os_io(db_io, DB_IO_WRITE, niop); + *niop = CMPR_MULTIPLY(*niop); + } else + ret = CDB___memp_cmpr_write(dbmfp, bhp, db_io, niop); + break; + } + + db_io->pgno = orig_pgno; + db_io->pagesize = orig_pagesize; + db_io->bytes = orig_bytes; + + return ret; +} + +/* + * CDB___memp_cmpr_read -- + * Transparent compression read + * + * PUBLIC: int CDB___memp_cmpr_read __P((DB_MPOOLFILE *, BH *, DB_IO *, ssize_t *)); + */ +int +CDB___memp_cmpr_read(dbmfp, bhp, db_io, niop) + DB_MPOOLFILE *dbmfp; + BH *bhp; + DB_IO *db_io; + ssize_t *niop; +{ + CMPR cmpr; + int ret; + int chain = 0; + u_int8_t *buffcmpr = 0; + int buffcmpr_length = 0; + int chain_length = 0; + db_pgno_t first_pgno = db_io->pgno; + DB_ENV *dbenv = dbmfp->dbmp->dbenv; + DB_CMPR_INFO *cmpr_info = dbenv->mp_cmpr_info; + /* + * By default the compression does not use too much space, + * hence the chain is empty. + */ + F_CLR(bhp, BH_CMPR); + + /* + * Read first page (if no overflow, this is the only one) + */ + ret = CDB___os_io(db_io, DB_IO_READ, niop); + + /* + * An error or partial read on the first page means that we're not + * going anywhere. + */ + if(ret || *niop < db_io->pagesize) + goto err; + + /* + * Read the cmpr header from page. + */ + memcpy(&cmpr, db_io->buf, sizeof(CMPR)); + + /* + * If not at the beginning of compressed page chain, build + * a fake page. + */ + if(F_ISSET(&cmpr, DB_CMPR_FREE) || F_ISSET(&cmpr, DB_CMPR_INTERNAL)) { + ret = CDB___memp_cmpr_page(dbmfp, &cmpr, db_io, niop); + goto err; + } + + /* + * Sanity check. Happens if file corrupted. + */ + if(!F_ISSET(&cmpr, DB_CMPR_FIRST)) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_read: expected DB_CMPR_FIRST flag set at pgno = %ld", db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, EINVAL); + goto err; + } + + if((ret = CDB___os_malloc(db_io->pagesize * CMPR_MAX, NULL, &buffcmpr)) != 0) + goto err; + + do { + /* + * Read the first part of the compressed data from page. + */ + memcpy(buffcmpr + buffcmpr_length, DB_CMPR_DATA(db_io), DB_CMPR_PAGESIZE(db_io)); + buffcmpr_length += DB_CMPR_PAGESIZE(db_io); + + /* + * Flag must only contain FIRST|INTERNAL and/or CHAIN. If other bits are + * set, the data is corrupted. Removing the FIRST|INTERNAL bits and checking + * the CHAIN bit with == instead of F_ISSET verify this. + */ + F_CLR(&cmpr, DB_CMPR_FIRST | DB_CMPR_INTERNAL); + chain = cmpr.flags; + + if(chain == DB_CMPR_CHAIN) { + /* + * Overflow Case. Continue reading data from extra pages. + */ + + chain_length++; + if(chain_length >= CMPR_MAX) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_read: compression chain too long at pgno = %ld", db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, EINVAL); + goto err; + } + + if(cmpr.next == 0) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_read: cmpr.next is null at pgno = %ld", chain, db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, EINVAL); + goto err; + } + /* + * Keep the chain in buffer header. + * Freed when bhp freed in CDB___memp_bhfree(). + */ + CDB___memp_cmpr_alloc_chain(dbmfp->dbmp, bhp, BH_CMPR_POOL); + + bhp->chain[chain_length - 1] = cmpr.next; + db_io->pgno = cmpr.next; + /* + * Read data from extra page. + */ + if((ret = CDB___os_io(db_io, DB_IO_READ, niop)) != 0 || + *niop != db_io->pagesize) { + ret = EIO; + goto err; + } + /* + * Read the cmpr header from this extra page + */ + memcpy(&cmpr, db_io->buf, sizeof(CMPR)); + } else if(chain != 0) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_read: unexpected compression flag value 0x%x at pgno = %ld", chain, db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, ret); + goto err; + } else if(cmpr.next != 0) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_read: cmpr.next is not null at pgno = %ld", chain, db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, ret); + goto err; + } + } while(chain); + + /* + * We gathered all the compressed data in buffcmpr, inflate it. + */ + + if(cmpr_info->zlib_flags != 0) + ret = CDB___memp_cmpr_inflate(buffcmpr, buffcmpr_length, db_io->buf, CMPR_MULTIPLY(db_io->pagesize), cmpr_info->user_data); + else + ret = (*cmpr_info->uncompress)(buffcmpr, buffcmpr_length, db_io->buf, CMPR_MULTIPLY(db_io->pagesize), cmpr_info->user_data); + + if(ret != 0) + { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_read: unable to uncompress page at pgno = %ld", first_pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, ret); + goto err; + } +#ifdef DEBUG + { + int ratio = buffcmpr_length > 0 ? (CMPR_MULTIPLY(db_io->pagesize) / buffcmpr_length) : 0; + if(ratio > 10) ratio = 10; + word_monitor_add(WORD_MONITOR_COMPRESS_01 + ratio, 1); + } +#endif /* DEBUG */ + + *niop = CMPR_MULTIPLY(db_io->pagesize); + + err: +#ifdef DEBUG_CMPR + if(chain_length > 0) { + int i; + fprintf(stderr,"CDB___memp_cmpr_read:: chain_length (number of overflow pages):%2d\n",chain_length); + fprintf(stderr,"CDB___memp_cmpr_read:: chain "); + for(i = 0; i < chain_length; i++) + fprintf(stderr, "%d, ", bhp->chain[i]); + fprintf(stderr, "\n"); + } +#endif + if(buffcmpr) CDB___os_free(buffcmpr, 0); + return ret; +} + +/* + * CDB___memp_cmpr_write -- + * Transparent compression write + * + * PUBLIC: int CDB___memp_cmpr_write __P((DB_MPOOLFILE *, BH *, DB_IO *, ssize_t *)); + */ +int +CDB___memp_cmpr_write(dbmfp, bhp, db_io, niop) + DB_MPOOLFILE *dbmfp; + BH *bhp; + DB_IO *db_io; + ssize_t *niop; +{ + CMPR cmpr; + int chain_length = 0; + int first_nonreused_chain_pos = 0; + int ret; + u_int8_t *buffcmpr = 0; + u_int8_t *buffp; + unsigned int buffcmpr_length; + u_int8_t *orig_buff = db_io->buf; + DB_ENV *dbenv = dbmfp->dbmp->dbenv; + DB_CMPR_INFO *cmpr_info = dbenv->mp_cmpr_info; + + if((ret = CDB___os_malloc(CMPR_MULTIPLY(db_io->bytes), NULL, &db_io->buf)) != 0) + goto err; + + + if(cmpr_info->zlib_flags != 0) + ret = CDB___memp_cmpr_deflate(orig_buff, CMPR_MULTIPLY(db_io->pagesize), &buffcmpr, &buffcmpr_length, cmpr_info->user_data); + else + ret = (*cmpr_info->compress)(orig_buff, CMPR_MULTIPLY(db_io->pagesize), &buffcmpr, &buffcmpr_length, cmpr_info->user_data); + + if(ret != 0) + { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_write: unable to compress page at pgno = %ld", db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, ret); + goto err; + } +#ifdef DEBUG + { + int ratio = buffcmpr_length > 0 ? (CMPR_MULTIPLY(db_io->pagesize) / buffcmpr_length) : 0; + if(ratio > 10) ratio = 10; + word_monitor_add(WORD_MONITOR_COMPRESS_01 + ratio, 1); + } +#endif /* DEBUG */ + + /* + * This can never happen. + */ + if(buffcmpr_length > DB_CMPR_PAGESIZE(db_io) * CMPR_MAX) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_write: compressed data is too big at pgno = %ld", db_io->pgno); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, EINVAL); + goto err; + } + + buffp = buffcmpr; + cmpr.flags = DB_CMPR_FIRST; + cmpr.next = 0; + + /* write pages until the whole compressed data is written */ + do { + unsigned int length = buffcmpr_length - (buffp - buffcmpr); + unsigned int copy_length = length > DB_CMPR_PAGESIZE(db_io) ? DB_CMPR_PAGESIZE(db_io) : length; + /* + * We handle serious compression stuff only if we need to. + * overflow! the compressed buffer is too big -> get extra page + */ + if(length > copy_length) { + if (dbmfp->dbmp->recursion_level >= 2 ) { + fprintf(stderr,"CDB___memp_cmpr_write: Wanted %d > %d bytes\n", length, copy_length); + fprintf(stderr,"Reducing wordlist_cache_dirty_level may help.\n"); + ret = EBUSY; + goto err; + } + chain_length++; + if(chain_length >= CMPR_MAX) { + CDB___db_err(dbmfp->dbmp->dbenv, "CDB___memp_cmpr_write: chain_length overflow"); + ret = CDB___db_panic(dbmfp->dbmp->dbenv, EINVAL); + goto err; + } + F_SET(&cmpr, DB_CMPR_CHAIN); + if((ret = CDB___memp_cmpr_alloc(dbmfp, &cmpr.next, bhp, &first_nonreused_chain_pos)) != 0) + goto err; + CDB___memp_cmpr_alloc_chain(dbmfp->dbmp, bhp, BH_CMPR_OS); + bhp->chain[chain_length - 1] = cmpr.next; + } + /* write in the cmpr header */ + memcpy(db_io->buf, &cmpr, DB_CMPR_OVERHEAD); + /* write in what's left of the compressed buffer (and that also fits in) */ + memcpy(db_io->buf + DB_CMPR_OVERHEAD, buffp, copy_length); + buffp += copy_length; + /* actual output */ + if((ret = CDB___os_io(db_io, DB_IO_WRITE, niop)) != 0 || + *niop != db_io->pagesize) { + ret = EIO; + goto err; + } + db_io->pgno = cmpr.next; + cmpr.flags = DB_CMPR_INTERNAL; + cmpr.next = 0; + } while((unsigned int)(buffp - buffcmpr) < buffcmpr_length); + +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_write:: chain_length (number of overflow pages):%2d\n",chain_length); + if(chain_length > 0) { + int i; + fprintf(stderr,"CDB___memp_cmpr_write:: chain "); + for(i = 0; i < chain_length; i++) + fprintf(stderr, "%d, ", bhp->chain[i]); + fprintf(stderr, "\n"); + } +#endif + /* + * If the chain was not completely reused, free the remaining pages (the page compression + * rate is better). + */ + if(F_ISSET(bhp, BH_CMPR) && first_nonreused_chain_pos >= 0) { + int i; + CMPR cmpr; + cmpr.flags = DB_CMPR_FREE; + cmpr.next = 0; + memcpy(db_io->buf, &cmpr, sizeof(CMPR)); + for(i = first_nonreused_chain_pos; i < (CMPR_MAX - 1) && bhp->chain[i]; i++) { + if((ret = CDB___memp_cmpr_free(dbmfp, bhp->chain[i])) != 0) + goto err; + /* + * Mark the page as free for recovery. + */ + db_io->pgno = bhp->chain[i]; + if((ret = CDB___os_io(db_io, DB_IO_WRITE, niop)) != 0 || + *niop != db_io->pagesize) { + ret = EIO; + goto err; + } + bhp->chain[i] = 0; + } + } + + CDB___memp_cmpr_free_chain(dbmfp->dbmp, bhp); + + /* + * In case of success, always pretend that we exactly wrote the + * all bytes of the original pagesize. + */ + *niop = CMPR_MULTIPLY(db_io->pagesize); + + err: + CDB___os_free(db_io->buf, 0); + db_io->buf = orig_buff; + if(buffcmpr) CDB___os_free(buffcmpr, 0); + + return ret; +} + +/* + * Helpers + */ + +/* + * CDB___memp_cmpr_page -- + * Build a fake page. This function is a CDB___memp_cmpr_read helper. + * + */ +static int +CDB___memp_cmpr_page(dbmfp, cmpr, db_io, niop) + DB_MPOOLFILE *dbmfp; + CMPR *cmpr; + DB_IO *db_io; + ssize_t *niop; +{ + DB_ENV *dbenv = dbmfp->dbmp->dbenv; + int ret = 0; + PAGE page; + + memset((char*)&page, '\0', sizeof(PAGE)); + + page.pgno = db_io->pgno; + page.type = F_ISSET(cmpr, DB_CMPR_FREE) ? P_CMPR_FREE : P_CMPR_INTERNAL; + + /* + * Sanity check + */ + if(db_io->pagesize < sizeof(PAGE)) { + ret = ENOMEM; + goto err; + } + + memcpy(db_io->buf, (char*)&page, sizeof(PAGE)); + + *niop = CMPR_MULTIPLY(db_io->pagesize); + + err: + + return ret; +} + +/* + * CDB___memp_cmpr_inflate -- + * Decompress buffer + * + * PUBLIC: int CDB___memp_cmpr_inflate __P((const u_int8_t *, int, u_int8_t *, int, void *)); + */ +int +CDB___memp_cmpr_inflate(inbuff, inbuff_length, outbuff, outbuff_length, user_data) + const u_int8_t* inbuff; + int inbuff_length; + u_int8_t* outbuff; + int outbuff_length; + void *user_data; +{ +#ifdef HAVE_LIBZ + int ret = 0; + z_stream c_stream; + + c_stream.zalloc=(alloc_func)0; + c_stream.zfree=(free_func)0; + c_stream.opaque=(voidpf)0; + c_stream.next_in = (Bytef*)inbuff; + c_stream.avail_in = inbuff_length; + c_stream.next_out = outbuff; + c_stream.avail_out = outbuff_length; + + if(inflateInit(&c_stream) != Z_OK || + inflate(&c_stream, Z_FINISH) != Z_STREAM_END || + inflateEnd(&c_stream) != Z_OK) + ret = EIO; + + /* + * The uncompressed data must *exactly* fill outbuff_length. + */ + if(c_stream.avail_out != 0) + ret = EIO; + + return ret; +#else /* HAVE_LIBZ */ + return EINVAL; +#endif /* HAVE_LIBZ */ +} + + + +/* + * CDB___memp_cmpr_deflate -- + * Compress buffer + * + * PUBLIC: int CDB___memp_cmpr_deflate __P((const u_int8_t *, int, u_int8_t **, int*, void *)); + */ +int +CDB___memp_cmpr_deflate(inbuff, inbuff_length, outbuffp, outbuff_lengthp, user_data) + const u_int8_t* inbuff; + int inbuff_length; + u_int8_t** outbuffp; + int* outbuff_lengthp; + void *user_data; +{ +#ifdef HAVE_LIBZ + int ret = 0; + int r; + int off = 0; + int freesp = 0; + z_stream c_stream; + u_int8_t* outbuff; + + /* + * Z_FINISH can be used immediately after deflateInit if all the compression + * is to be done in a single step. In this case, avail_out must be at least + * 0.1% larger than avail_in plus 12 bytes. If deflate does not return + * Z_STREAM_END, then it must be called again as described above. + * + * !!! + * In order to avoid division by 1000, divide by 512 (2^9) using shift. + * That is, make the buffer 0.2% larger. + */ + int outbuff_length = inbuff_length + (inbuff_length >> 9) + 12; + + *outbuffp = 0; + *outbuff_lengthp = 0; + + if(CDB___os_malloc(outbuff_length, NULL, &outbuff) != 0) { + ret = ENOMEM; + goto err; + } + + /* + * Clear possible garbage in the page + */ + { + PAGE* pg = (PAGE*)inbuff; + switch(TYPE(pg)) { + case P_IBTREE: + case P_LBTREE: + off = LOFFSET(pg); + freesp = P_FREESPACE(pg); + memset((char*)(inbuff + off), 0, freesp); + break; + } + } + + c_stream.zalloc=(alloc_func)0; + c_stream.zfree=(free_func)0; + c_stream.opaque=(voidpf)0; + + if(deflateInit(&c_stream, memp_cmpr_zlib_level) != Z_OK) + { + ret = EIO; + goto err; + } + + c_stream.next_in = (Bytef*)inbuff; + c_stream.avail_in = inbuff_length; + c_stream.next_out = outbuff; + c_stream.avail_out = outbuff_length; + + while((r = deflate(&c_stream, Z_FINISH)) != Z_STREAM_END && r == Z_OK) + ; + + if(r != Z_STREAM_END) + ret = EIO; + + if(deflateEnd(&c_stream) != Z_OK) + ret = EIO; + + if(ret == 0) { + *outbuffp = outbuff; + *outbuff_lengthp = outbuff_length - c_stream.avail_out; + } else { + CDB___os_free(outbuff, outbuff_length); + } +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_deflate:: compress %d bytes to %d \n", inbuff_length, *outbuff_lengthp); +#endif + + err: + return ret; +#else /* HAVE_LIBZ */ + return EINVAL; +#endif /* HAVE_LIBZ */ +} + + + +/* + * CDB___memp_cmpr_info_valid -- + * Compute compressed page size + */ +static int +CDB___memp_cmpr_info_valid(dbenv,cmpr_info) + DB_ENV *dbenv; + DB_CMPR_INFO *cmpr_info; +{ + int ret = 0; + if(!cmpr_info ) { + CDB___db_err(dbenv, "CDB___memp_cmpr_info_valid: cmpr_info == NULL"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + if(!cmpr_info->compress ) { + CDB___db_err(dbenv, "CDB___memp_cmpr_info_valid: compress == NULL!"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + if(!cmpr_info->uncompress ) { + CDB___db_err(dbenv, "CDB___memp_cmpr_info_valid: uncompress == NULL!"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + if(cmpr_info->coefficient == 0 || cmpr_info->coefficient > 5 ) { + CDB___db_err(dbenv, "CDB___memp_cmpr_info_valid: coefficient should be > 0 and < 5 coefficient=%d ", cmpr_info->coefficient); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + if(cmpr_info->max_npages == 0 || cmpr_info->max_npages > 128 ) { + CDB___db_err(dbenv, "CDB___memp_cmpr_info_valid: max_npages should be > 0 and < 128 max_npages=%d ", cmpr_info->max_npages); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } +err: + return ret; +} + +/* + * __memp_cmpr_pagesize -- + * Compute compressed page size + * + * PUBLIC: u_int8_t CDB___memp_cmpr_coefficient __P((DB_ENV *dbenv)); + */ +u_int8_t +CDB___memp_cmpr_coefficient(dbenv) + DB_ENV *dbenv; +{ + u_int8_t ret = 0; + + if(!dbenv || !dbenv->mp_cmpr_info) { + ret = default_cmpr_info.coefficient; + } else { + CDB___memp_cmpr_info_valid(dbenv, dbenv->mp_cmpr_info); + ret = dbenv->mp_cmpr_info->coefficient; + } + + return (ret); +} +/* + * Initialisation of page compression + */ + +/* + * CDB___memp_cmpr_open -- + * Open the db that contains the free compression pages. + * + * PUBLIC: int CDB___memp_cmpr_open __P((const char *, DB_ENV *, CMPR_CONTEXT *)); + */ +int +CDB___memp_cmpr_open(dbenv, path, flags, mode, cmpr_context) + DB_ENV *dbenv; + const char *path; + int flags; + int mode; + CMPR_CONTEXT *cmpr_context; +{ + int ret; + char* tmp = 0; + int tmp_length = strlen(path) + strlen(DB_CMPR_SUFFIX) + 1; + + /* + * Management of pages containing data when the compression does not achieve + * the expected compression ratio. + */ + { + DB *dbp; + if((ret = CDB___os_malloc(tmp_length, NULL, &tmp)) != 0) + goto err; + sprintf(tmp, "%s%s", path, DB_CMPR_SUFFIX); + + /* Use *standalone* database, to prevent recursion when writing pages */ + /* from the cache, shared with other members of the environment */ + if(CDB_db_create(&dbp, NULL, 0) != 0) + goto err; + + cmpr_context->weakcmpr = dbp; + + (dbp->set_flags)(dbp, DB_RECNUM); + + LF_CLR(DB_COMPRESS); + if(!LF_ISSET(DB_RDONLY)) LF_SET(DB_CREATE); + if((ret = (dbp->open)(dbp, tmp, NULL, DB_BTREE, flags, mode)) != 0) + goto err; + } + + /* + * Initialisation of cmpr_context + */ + if(!dbenv->mp_cmpr_info) { + if(default_cmpr_info.compress == 0) { + CDB___db_err(dbenv, "CDB___memp_cmpr_open: zlib compression not available, re-compile --with-zlib=DIR"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + dbenv->mp_cmpr_info = &default_cmpr_info; + } + /* + * Check if cmpr_info is sane + */ + if((ret = CDB___memp_cmpr_info_valid(dbenv, dbenv->mp_cmpr_info))) + goto err; + + err: + if(tmp) CDB___os_free(tmp, tmp_length); + return ret; +} + +/* + * CDB___memp_cmpr_close -- + * Close the db that contains the free compression pages. + * + * PUBLIC: int CDB___memp_cmpr_close __P((CMPR_CONTEXT *)); + */ +int +CDB___memp_cmpr_close(cmpr_context) + CMPR_CONTEXT *cmpr_context; +{ + int ret = 0; + + if(cmpr_context->weakcmpr == 0) { + ret = EINVAL; + goto err; + } + + if((ret = cmpr_context->weakcmpr->close(cmpr_context->weakcmpr, 0)) != 0) + goto err; + cmpr_context->weakcmpr = 0; + + err: + return ret; +} + +/* + * CDB___memp_cmpr_alloc -- + * Get a new free page to store weak compression data. + * + * PUBLIC: int CDB___memp_cmpr_alloc __P((DB_MPOOLFILE *, db_pgno_t *, BH *, int *)); + */ +int +CDB___memp_cmpr_alloc(dbmfp, pgnop, bhp, first_nonreused_chain_posp) + DB_MPOOLFILE *dbmfp; + db_pgno_t *pgnop; + BH *bhp; + int *first_nonreused_chain_posp; +{ + DB_ENV *dbenv = dbmfp->dbmp->dbenv; + int ret = 0; + +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_alloc:: bhp:%8x bhp->chain:%8x first_nonreused_chain_posp:%2d\n", bhp, bhp->chain, *first_nonreused_chain_posp); +#endif + if(F_ISSET(bhp, BH_CMPR) && bhp->chain == NULL) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: BH_CMPR set and bhp->chain == NULL"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + if((*first_nonreused_chain_posp) >= (CMPR_MAX - 1)) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: first_nonreused_chain_pos >= (CMPR_MAX - 1)"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + /* + * If possible reuse an existing chain. + */ + if((*first_nonreused_chain_posp) >= 0 && F_ISSET(bhp, BH_CMPR) && bhp->chain[*first_nonreused_chain_posp]) { + *pgnop = bhp->chain[*first_nonreused_chain_posp]; + (*first_nonreused_chain_posp)++; +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_alloc:: reusing page in chain \n"); +#endif + } else { + DB *db = dbmfp->cmpr_context.weakcmpr; + DBT key; + DBT data; + db_recno_t recno = 1; + + /* all pages in bhp->chain are now reused */ + (*first_nonreused_chain_posp) = -1; +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_alloc:: no more reusable pages in chain\n"); +#endif + + if(db == 0) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: dbmfp->cmpr_context.weakcmpr is null"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + /* + * If the existing chain is too short, pop a free page from + * the free pages database. + */ + memset(&key, '\0', sizeof(DBT)); + memset(&data, '\0', sizeof(DBT)); + + key.data = &recno; + key.size = sizeof(recno); + + if((ret = db->get(db, NULL, &key, &data, DB_SET_RECNO)) != 0) { + /* + * If the free list is empty, create a new page. + */ +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_alloc:: weakcmpr free page pool empty, allocating\n"); +#endif + if(ret == DB_NOTFOUND) { + DB_MPOOL *dbmp = dbmfp->dbmp; + ret = 0; + R_LOCK(dbenv, &dbmp->reginfo); + ++dbmfp->mfp->last_pgno; +#ifdef DEBUG + word_monitor_set(WORD_MONITOR_PGNO, dbmfp->mfp->last_pgno); +#endif /* DEBUG */ + *pgnop = dbmfp->mfp->last_pgno; + R_UNLOCK(dbenv, &dbmp->reginfo); + ret = 0; + } else { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: unexpected error from weakcmpr base"); + ret = CDB___db_panic(dbenv, ret); + } + } else { + if(key.size != sizeof(db_pgno_t)) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: unexpected key size from weakcmpr base (%d instead of %d)", key.size, sizeof(db_pgno_t)); + ret = CDB___db_panic(dbenv, ret); + } else { + memcpy((char*)pgnop, (char*)key.data, key.size); + if((ret = db->del(db, NULL, &key, 0)) != 0) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: del error, got pgno %d", *pgnop); + ret = CDB___db_panic(dbenv, ret); + } + if(*pgnop == 0) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc: unexpected pgno == 0"); + ret = CDB___db_panic(dbenv, ret); + } + } +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_alloc:: reuse free page %d from weakcmpr\n", *pgnop); +#endif + } + } + + err: + return ret; +} + +/* + * CDB___memp_cmpr_free -- + * Release a page used to store weak compression data. + * + * PUBLIC: int CDB___memp_cmpr_free __P((DB_MPOOLFILE *, db_pgno_t)); + */ +int +CDB___memp_cmpr_free(dbmfp, pgno) + DB_MPOOLFILE *dbmfp; + db_pgno_t pgno; +{ + int ret = 0; + DB_ENV *dbenv = dbmfp->dbmp->dbenv; + DB *db = dbmfp->cmpr_context.weakcmpr; + DBT key; + DBT data; + +#ifdef DEBUG_CMPR + fprintf(stderr,"CDB___memp_cmpr_free:: freeing page (inserting into weakcmpr):%3d \n",pgno); +#endif + if(db == 0) { + CDB___db_err(dbenv, "CDB___memp_cmpr_free: dbmfp->cmpr_context.weakcmpr is null"); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + + memset(&key, '\0', sizeof(DBT)); + memset(&data, '\0', sizeof(DBT)); + + key.data = &pgno; + key.size = sizeof(db_pgno_t); + + data.data = " "; + data.size = 1; + + if((ret = db->put(db, 0, &key, &data, DB_NOOVERWRITE)) != 0) { + CDB___db_err(dbenv, "CDB___memp_cmpr_free: put failed for pgno = %d", pgno); + ret = CDB___db_panic(dbenv, ret); + goto err; + } + + err: + return ret; +} + + +/* + * CDB___memp_cmpr_alloc_chain -- + * Allocate chain entry in BH + * + * PUBLIC: int CDB___memp_cmpr_alloc_chain __P((DB_MPOOL *, BH *)); + */ + +int +CDB___memp_cmpr_alloc_chain(dbmp, bhp, alloc_type) + DB_MPOOL *dbmp; + BH *bhp; + int alloc_type; +{ + DB_ENV *dbenv = dbmp->dbenv; + int ret = 0; + if(!bhp->chain) { + int alloc_ret; + int alloc_length = sizeof(db_pgno_t)*(CMPR_MAX-1); + switch(alloc_type) { + case BH_CMPR_POOL: + { + MPOOL *mp = dbmp->reginfo.primary; + int n_cache = NCACHE(mp, bhp->pgno); + alloc_ret = CDB___memp_alloc(dbmp, &dbmp->c_reginfo[n_cache], NULL, alloc_length, NULL, (void *)(&bhp->chain)); + F_SET(bhp, BH_CMPR_POOL); + } + break; + case BH_CMPR_OS: + alloc_ret = CDB___os_malloc(alloc_length, NULL, &bhp->chain); + F_SET(bhp, BH_CMPR_OS); + break; + default: + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc_chain: unknown alloc type :%d", alloc_type); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + break; + } + + if(alloc_ret) { + CDB___db_err(dbenv, "CDB___memp_cmpr_alloc_chain: memp_alloc %d bytes failed:%d", alloc_length, alloc_ret); + ret = CDB___db_panic(dbenv, EINVAL); + goto err; + } + memset((void *)bhp->chain, 0, alloc_length); +#if defined(DEBUG_CMPR) || defined(DEBUG_CMPR_ALLOC) + fprintf(stderr, "CDB___memp_cmpr_alloc_chain:: allocate chain in %s :%8x\n", (alloc_type == BH_CMPR_OS ? "malloc" : "shalloc"), bhp->chain); +#endif + } else { +#ifdef DEBUG_CMPR + fprintf(stderr, "CDB___memp_cmpr_alloc_chain:: existing chain:%8x\n", bhp->chain); +#endif + } + F_SET(bhp, BH_CMPR); + err: + return ret; +} + +/* + * CDB___memp_cmpr_free_chain -- + * Free chain entry in BH + * + * PUBLIC: int CDB___memp_cmpr_free_chain __P((DB_MPOOL *, BH *)); + */ + +int +CDB___memp_cmpr_free_chain(dbmp, bhp) + DB_MPOOL *dbmp; + BH *bhp; +{ + DB_ENV *dbenv = dbmp->dbenv; + + if(F_ISSET(bhp, BH_CMPR)) { + if(bhp->chain) { + int alloc_length = sizeof(db_pgno_t)*(CMPR_MAX-1); + int alloc_type = bhp->flags & (BH_CMPR_POOL|BH_CMPR_OS); + switch(alloc_type) { + case BH_CMPR_POOL: + { + MPOOL *mp = dbmp->reginfo.primary; + int n_cache = NCACHE(mp, bhp->pgno); + CDB___db_shalloc_free(dbmp->c_reginfo[n_cache].addr, bhp->chain); + } + break; + case BH_CMPR_OS: + CDB___os_free(bhp->chain, alloc_length); + break; + default: + CDB___db_err(dbenv, "CDB___memp_cmpr_free_chain: unknown alloc type :%d", alloc_type); + return CDB___db_panic(dbenv, EINVAL); + break; + } +#if defined(DEBUG_CMPR) || defined(DEBUG_CMPR_ALLOC) + fprintf(stderr, "CDB___memp_cmpr_free_chain:: free chain in %s :%8x\n", (alloc_type == BH_CMPR_OS ? "malloc" : "shalloc"), bhp->chain); +#endif + bhp->chain = NULL; + } else { + CDB___db_err(dbenv, "CDB___memp_cmpr_free_chain: BH_CMPR set but null bhp->chain"); + return CDB___db_panic(dbenv, EINVAL); + } + } else if(bhp->chain) { + CDB___db_err(dbenv, "CDB___memp_cmpr_free_chain: BH_CMPR not set but bhp->chain not null"); + return CDB___db_panic(dbenv, EINVAL); + } + + F_CLR(bhp, BH_CMPR | BH_CMPR_OS | BH_CMPR_POOL); + + return 0; +} |