/*- * 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 #include #include #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; }