00001 // 00002 // WordDBCompress.cc 00003 // 00004 // Part of the ht://Dig package <http://www.htdig.org/> 00005 // Copyright (c) 1999 The ht://Dig Group 00006 // For copyright details, see the file COPYING in your distribution 00007 // or the GNU Public License version 2 or later 00008 // <http://www.gnu.org/copyleft/gpl.html> 00009 // 00010 // $Id: WordDBCompress_8cc-source.html,v 1.1 2008年06月08日 10:13:02 sebdiaz Exp $ 00011 // 00012 /* 00013 00014 BTREE original page layout in Berkeley DB 00015 00016 * +-----------------------------------+ 00017 * | lsn | pgno | prev pgno | 00018 * +-----------------------------------+ 00019 * | next pgno | entries | hf offset | 00020 * +-----------------------------------+ 00021 * | level | type | index | 00022 * +-----------------------------------+ 00023 * | index | free --> | 00024 * +-----------+-----------------------+ 00025 * | F R E E A R E A | 00026 * +-----------------------------------+ 00027 * | <-- free | item | 00028 * +-----------------------------------+ 00029 * | item | item | item | 00030 * +-----------------------------------+ 00031 */ 00032 #ifdef HAVE_CONFIG_H 00033 #include "config.h" 00034 #endif /* HAVE_CONFIG_H */ 00035 00036 #include <stdlib.h> 00037 #include <errno.h> 00038 00039 extern "C" { 00040 #include "db_int.h" 00041 #include "db_page.h" 00042 } 00043 00044 #include "lib.h" 00045 #include "WordDBCompress.h" 00046 #include "WordBitCompress.h" 00047 #include "WordKeyInfo.h" 00048 #include "WordKey.h" 00049 #include "WordRecord.h" 00050 #include "WordDB.h" 00051 #include "HtMaxMin.h" 00052 00053 /* 00054 * WordDBCompress: C-callbacks, actually called by Berkeley-DB 00055 * they just call their WordDBCompress equivalents (by using user_data) 00056 */ 00057 extern "C" 00058 { 00059 00060 int WordDBCompress_compress_c(DB_ENV*, const u_int8_t* inbuff, int inbuff_length, u_int8_t** outbuffp, int* outbuff_lengthp, void *user_data) 00061 { 00062 if(!user_data) { 00063 fprintf(stderr, "WordDBCompress_compress_c:: user_data is NULL"); 00064 return NOTOK; 00065 } 00066 return ((WordDBCompress *)user_data)->Compress((unsigned char*)inbuff, inbuff_length, (unsigned char**)outbuffp, outbuff_lengthp); 00067 } 00068 00069 int WordDBCompress_uncompress_c(DB_ENV*, const u_int8_t* inbuff, int inbuff_length, u_int8_t* outbuff, int outbuff_length, void *user_data) 00070 { 00071 if(!user_data) { 00072 fprintf(stderr, "WordDBCompress_uncompress_c:: user_data is NULL"); 00073 return NOTOK; 00074 } 00075 return ((WordDBCompress *)user_data)->Uncompress((unsigned char *)inbuff, inbuff_length, (unsigned char*)outbuff, outbuff_length); 00076 } 00077 00078 } 00079 00080 // 00081 // Symbolic names for the data in WordDBEncoded.values[..] arrays 00082 // 00083 // 00084 // Each entry in a leaf/internal page has a flag that shows how 00085 // the entry is coded. 00086 // 00087 #define WORD_CMPR_VAL_FLAGS 0 00088 // 00089 // Btree: Numerical fields of WordKey 00090 // 00091 #define WORD_CMPR_VAL_FIELDS 1 00092 // 00093 // Btree/Internal: BINTERNAL.pgno 00094 // 00095 #define WORD_CMPR_VAL_PGNO (WORD_CMPR_VAL_FIELDS + WORD_KEY_MAX_NFIELDS) 00096 // 00097 // Btree/Leaf: Length of record string 00098 // 00099 #define WORD_CMPR_VAL_RLENGTH WORD_CMPR_VAL_PGNO 00100 // 00101 // Btree/Internal: BINTERNAL.nrecs 00102 // 00103 #define WORD_CMPR_VAL_NRECS (WORD_CMPR_VAL_PGNO + 1) 00104 // 00105 // Btree/Leaf: Value of integer if record contains a single integer value 00106 // 00107 #define WORD_CMPR_VAL_RVALUE WORD_CMPR_VAL_NRECS 00108 // 00109 // Btree: length of WordKey.GetWord() prefix shared with previous entry 00110 // 00111 #define WORD_CMPR_VAL_PREFIX (WORD_CMPR_VAL_PGNO + 2) 00112 // 00113 // Btree: length of WordKey.GetWord() suffix to add to previous key 00114 // shared prefix. 00115 // 00116 #define WORD_CMPR_VAL_SUFFIX (WORD_CMPR_VAL_PGNO + 3) 00117 // 00118 // Symbolic name for the last index 00119 // 00120 #define WORD_CMPR_VAL_LAST WORD_CMPR_VAL_SUFFIX 00121 00122 #define WORD_CMPR_VAL_ARRAY_SIZE (WORD_CMPR_VAL_LAST + 1) 00123 // 00124 // Number of bits needed to encode the maximum array size value 00125 // 00126 #define WORD_CMPR_VAL_ARRAY_SIZE_BITS 8 00127 00128 // 00129 // Number of bits used in flags 00130 // 00131 #define WORD_CMPR_VAL_FLAGS_BITS (WORD_CMPR_VAL_LAST + 1) 00132 // 00133 // Values for the WORD_CMPR_VAL_FLAGS bit field 00134 // 00135 #define WORD_CMPR_VAL_FLAGS_FIELD(n) (1 << (WORD_CMPR_VAL_FIELDS + (n))) 00136 #define WORD_CMPR_VAL_FLAGS_PGNO (1 << (WORD_CMPR_VAL_PGNO)) 00137 #define WORD_CMPR_VAL_FLAGS_RLENGTH WORD_CMPR_VAL_FLAGS_PGNO 00138 #define WORD_CMPR_VAL_FLAGS_NRECS (1 << (WORD_CMPR_VAL_NRECS)) 00139 #define WORD_CMPR_VAL_FLAGS_RVALUE WORD_CMPR_VAL_FLAGS_NRECS 00140 #define WORD_CMPR_VAL_FLAGS_PREFIX (1 << (WORD_CMPR_VAL_PREFIX)) 00141 #define WORD_CMPR_VAL_FLAGS_SUFFIX (1 << (WORD_CMPR_VAL_SUFFIX)) 00142 #define WORD_CMPR_VAL_FLAGS_STRING (1 << (WORD_CMPR_VAL_LAST + 1)) 00143 #define WORD_CMPR_VAL_FLAGS_EMPTY (1 << (WORD_CMPR_VAL_LAST + 2)) 00144 #define WORD_CMPR_VAL_FLAGS_RECORD_EQ (1 << (WORD_CMPR_VAL_LAST + 3)) 00145 #define WORD_CMPR_VAL_FLAGS_RECORD_NO (1 << (WORD_CMPR_VAL_LAST + 4)) 00146 #define WORD_CMPR_VAL_FLAGS_RECORD_STR (1 << (WORD_CMPR_VAL_LAST + 5)) 00147 00148 // 00149 // Storage for values extracted from a PAGE structure 00150 // 00151 class WordDBEncoded { 00152 public: 00153 inline WordDBEncoded() { 00154 Init(); 00155 } 00156 inline ~WordDBEncoded() { 00157 free(strings); 00158 for(int i = 0; i < WORD_CMPR_VAL_ARRAY_SIZE; i++) 00159 free(values[i]); 00160 } 00161 00162 inline void Init() { 00163 strings_size = 32; 00164 strings = (unsigned char*)malloc(strings_size); 00165 00166 for(int i = 0; i < WORD_CMPR_VAL_ARRAY_SIZE; i++) { 00167 values_size[i] = 32; 00168 values[i] = (unsigned int*)malloc(values_size[i] * sizeof(unsigned int)); 00169 } 00170 00171 Clear(); 00172 } 00173 00174 inline void Clear() { 00175 strings_length = 0; 00176 strings_idx = 0; 00177 00178 for(int i = 0; i < WORD_CMPR_VAL_ARRAY_SIZE; i++) { 00179 values_length[i] = 0; 00180 values_idx[i] = 0; 00181 } 00182 } 00183 00184 inline void AllocateStrings(unsigned int size) { 00185 strings_size = size; 00186 strings = (unsigned char*)realloc(strings, strings_size); 00187 } 00188 00189 inline void CheckStrings(int index) { 00190 while(index >= strings_size) 00191 AllocateStrings(strings_size * 2); 00192 } 00193 00194 inline void AllocateValues(int what, unsigned int size) { 00195 values_size[what] = size; 00196 values[what] = (unsigned int*)realloc(values[what], values_size[what] * sizeof(unsigned int)); 00197 } 00198 00199 inline void CheckValues(int what, int index) { 00200 while(index >= values_size[what]) 00201 AllocateValues(what, values_size[what] * 2); 00202 } 00203 00204 inline void PushValue(int what, unsigned int value) { 00205 CheckValues(what, values_length[what]); 00206 values[what][values_length[what]] = value; 00207 values_length[what]++; 00208 } 00209 00210 inline void PushString(const unsigned char* string, int length) { 00211 CheckStrings(strings_length + length); 00212 memcpy(strings + strings_length, string, length); 00213 strings_length += length; 00214 } 00215 00216 inline unsigned int ShiftValue(int what) { 00217 if(values_idx[what] >= values_length[what]) { 00218 fprintf(stderr, "WordDBEncoded::ShiftValue: what = %d, (idx = %d) >= (length = %d)\n", what, values_idx[what], values_length[what]); 00219 abort(); 00220 } 00221 return values[what][values_idx[what]++]; 00222 } 00223 00224 inline unsigned char* ShiftString(int length) { 00225 if(strings_idx + length > strings_length) { 00226 fprintf(stderr, "WordDBEncoded::ShiftString: (idx + length = %d) >= (length = %d)\n", strings_idx + length, strings_length); 00227 abort(); 00228 } 00229 unsigned char* string = strings + strings_idx; 00230 strings_idx += length; 00231 return string; 00232 } 00233 00234 void Put(WordBitCompress& stream); 00235 void Get(WordBitCompress& stream); 00236 00237 unsigned int *values[WORD_CMPR_VAL_ARRAY_SIZE]; 00238 int values_length[WORD_CMPR_VAL_ARRAY_SIZE]; 00239 int values_idx[WORD_CMPR_VAL_ARRAY_SIZE]; 00240 int values_size[WORD_CMPR_VAL_ARRAY_SIZE]; 00241 00242 unsigned char *strings; 00243 int strings_length; 00244 int strings_idx; 00245 int strings_size; 00246 }; 00247 00248 void WordDBEncoded::Put(WordBitCompress& stream) 00249 { 00250 unsigned int count = 0; 00251 for(int i = 0; i < WORD_CMPR_VAL_ARRAY_SIZE; i++) { 00252 if(values_length[i] > 0) count++; 00253 } 00254 stream.WordBitStream::PutUint(count, WORD_CMPR_VAL_ARRAY_SIZE_BITS); 00255 00256 for(int i = 0; i < WORD_CMPR_VAL_ARRAY_SIZE; i++) { 00257 if(values_length[i] > 0) { 00258 stream.WordBitStream::PutUint(i, WORD_CMPR_VAL_ARRAY_SIZE_BITS); 00259 stream.PutUints(values[i], values_length[i]); 00260 } 00261 } 00262 00263 stream.PutUchars(strings, strings_length); 00264 } 00265 00266 void WordDBEncoded::Get(WordBitCompress& stream) 00267 { 00268 Clear(); 00269 00270 unsigned int count = 0; 00271 count = stream.WordBitStream::GetUint(WORD_CMPR_VAL_ARRAY_SIZE_BITS); 00272 00273 for(unsigned int i = 0; i < count; i++) { 00274 unsigned int index = stream.WordBitStream::GetUint(WORD_CMPR_VAL_ARRAY_SIZE_BITS); 00275 values_length[index] = stream.GetUints(&values[index], &values_size[index]); 00276 } 00277 00278 strings_length = stream.GetUchars(&strings, &strings_size); 00279 } 00280 00281 // 00282 // ******************** WordDBCompress implementation ************** 00283 // 00284 00285 #define WORD_CMPR_OVERHEAD ((int)sizeof(unsigned char)) 00286 00287 WordDBCompress::WordDBCompress(WordContext* ncontext) 00288 { 00289 cmprInfo = 0; 00290 context = ncontext; 00291 encoded = new WordDBEncoded(); 00292 00293 const Configuration& config = context->GetConfiguration(); 00294 debug = config.Boolean("wordlist_compress_debug", 0); 00295 verbose = config.Value("wordlist_compress_verbose", 0); 00296 } 00297 00298 WordDBCompress::~WordDBCompress() 00299 { 00300 delete encoded; 00301 } 00302 00303 int 00304 WordDBCompress::Compress(const unsigned char *inbuff, int inbuff_length, unsigned char **outbuffp, int *outbuff_lengthp) 00305 { 00306 // 00307 // Maximum output length can be WORD_CMPR_OVERHEAD more than input 00308 // 00309 int outbuff_length = inbuff_length + WORD_CMPR_OVERHEAD; 00310 unsigned char* outbuff = (unsigned char*)malloc(sizeof(unsigned char) * outbuff_length); 00311 00312 *outbuffp = 0; 00313 *outbuff_lengthp = outbuff_length; 00314 00315 if(outbuff == 0) 00316 return ENOMEM; 00317 00318 int ret = 0; 00319 PAGE* pp = (PAGE*)inbuff; 00320 00321 *outbuff = TYPE_TAGS(pp); 00322 00323 switch(TYPE_TAGS(pp)) { 00324 case P_IBTREE|WORD_DB_INDEX: 00325 case P_LBTREE|WORD_DB_INDEX: 00326 ret = CompressBtree(inbuff, inbuff_length, outbuff, &outbuff_length); 00327 break; 00328 default: 00329 memcpy((char*)(outbuff + WORD_CMPR_OVERHEAD), (char*)inbuff, inbuff_length); 00330 break; 00331 } 00332 00333 if(ret == 0) { 00334 *outbuffp = outbuff; 00335 *outbuff_lengthp = outbuff_length; 00336 } else { 00337 free(outbuff); 00338 } 00339 00340 return ret; 00341 } 00342 00343 int 00344 WordDBCompress::Uncompress(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int outbuff_length) 00345 { 00346 int ret = 0; 00347 00348 switch(*inbuff) { 00349 case P_IBTREE|WORD_DB_INDEX: 00350 case P_LBTREE|WORD_DB_INDEX: 00351 ret = UncompressBtree(inbuff, inbuff_length, outbuff, outbuff_length); 00352 break; 00353 default: 00354 memcpy((char*)outbuff, (char*)(inbuff + WORD_CMPR_OVERHEAD), outbuff_length); 00355 break; 00356 } 00357 00358 return ret; 00359 } 00360 00361 int 00362 WordDBCompress::CompressBtree(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int *outbuff_lengthp) 00363 { 00364 int ret = 0; 00365 PAGE *pp = (PAGE *)inbuff; 00366 00367 if(verbose) fprintf(stderr, "WordDBCompress::CompressBtree: page %d\n", PGNO(pp)); 00368 00369 switch(TYPE(pp)) { 00370 case P_IBTREE: 00371 ret = CompressIBtree(inbuff, inbuff_length, outbuff, outbuff_lengthp); 00372 break; 00373 case P_LBTREE: 00374 ret = CompressLBtree(inbuff, inbuff_length, outbuff, outbuff_lengthp); 00375 break; 00376 } 00377 00378 return ret; 00379 } 00380 00381 // 00382 // Return the length of the chars in b that are not common with a 00383 // a = abcdef 00384 // b = abcghi 00385 // --- 00386 // return 3 00387 // 00388 static inline unsigned int suffixlength(const String& a, const String& b) 00389 { 00390 const unsigned char* ap = (const unsigned char*)a.get(); 00391 const unsigned char* bp = (const unsigned char*)b.get(); 00392 int length = HtMIN(a.length(), b.length()); 00393 00394 int i; 00395 for(i = 0; i < length && ap[i] == bp[i]; i++) 00396 ; 00397 00398 return b.length() - i; 00399 } 00400 00401 int 00402 WordDBCompress::CompressIBtree(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int *outbuff_lengthp) 00403 { 00404 PAGE *pp = (PAGE *)inbuff; 00405 00406 if(verbose > 5) DumpPage(inbuff); 00407 00408 WordBitCompress stream(inbuff_length); 00409 00410 stream.PutUint(pp->lsn.file, sizeof(pp->lsn.file) * 8); 00411 stream.PutUint(pp->lsn.offset, sizeof(pp->lsn.file) * 8); 00412 stream.PutUint(PGNO(pp), sizeof(PGNO(pp)) * 8); 00413 stream.PutUint(NUM_ENT(pp), sizeof(NUM_ENT(pp)) * 8); 00414 stream.PutUint(LEVEL(pp), sizeof(LEVEL(pp)) * 8); 00415 00416 if(NUM_ENT(pp) > 0) { 00417 int i; 00418 WordKey key(context); 00419 WordKey previous_key(context); 00420 BINTERNAL* previous_e = 0; 00421 00422 encoded->Clear(); 00423 00424 for (i = 0; i < NUM_ENT(pp); i++) { 00425 BINTERNAL* e = GET_BINTERNAL(pp, i); 00426 if(debug) { 00427 if(e->type != B_KEYDATA) 00428 fprintf(stderr, "WordDBCompress::EncodeIBtree: unexpected type 0x%02x\n", e->type); 00429 } 00430 unsigned int changes = 0; 00431 00432 if(e->len <= 0) { 00433 changes = WORD_CMPR_VAL_FLAGS_EMPTY; 00434 // 00435 // Structural data 00436 // 00437 encoded->PushValue(WORD_CMPR_VAL_PGNO, e->pgno); 00438 encoded->PushValue(WORD_CMPR_VAL_NRECS, e->nrecs); 00439 } else { 00440 00441 key.Unpack((const char*)e->data, (int)e->len); 00442 00443 if(previous_e != 0) { 00444 int is_first_change = 1; 00445 #if 0 00446 // 00447 // String 00448 // 00449 const String& word = key.GetWord(); 00450 const String& previous_word = previous_key.GetWord(); 00451 if(word != previous_word) { 00452 unsigned int suffix_length = suffixlength(previous_word, word); 00453 unsigned int prefix_length = word.length() - suffix_length; 00454 encoded->PushValue(WORD_CMPR_VAL_PREFIX, prefix_length); 00455 encoded->PushValue(WORD_CMPR_VAL_SUFFIX, suffix_length); 00456 const char* suffix = word.get() + prefix_length; 00457 encoded->PushString((const unsigned char*)suffix, suffix_length); 00458 changes |= WORD_CMPR_VAL_FLAGS_STRING; 00459 is_first_change = 0; 00460 } 00461 #endif 00462 // 00463 // Fields 00464 // 00465 int nfields = key.NFields(); 00466 for(int i = 0; i < nfields; i++) { 00467 unsigned int value = key.Get(i); 00468 unsigned int previous_value = previous_key.Get(i); 00469 if(value != previous_value) { 00470 if(is_first_change) { 00471 value -= previous_value; 00472 is_first_change = 0; 00473 } 00474 encoded->PushValue(i + WORD_CMPR_VAL_FIELDS, value); 00475 changes |= WORD_CMPR_VAL_FLAGS_FIELD(i); 00476 } 00477 } 00478 // 00479 // Structural data 00480 // 00481 if(e->pgno != previous_e->pgno) { 00482 encoded->PushValue(WORD_CMPR_VAL_PGNO, e->pgno); 00483 changes |= WORD_CMPR_VAL_FLAGS_PGNO; 00484 } 00485 if(e->nrecs != previous_e->nrecs) { 00486 encoded->PushValue(WORD_CMPR_VAL_NRECS, e->nrecs); 00487 changes |= WORD_CMPR_VAL_FLAGS_NRECS; 00488 } 00489 } else { 00490 #if 0 00491 // 00492 // String 00493 // 00494 const String& word = key.GetWord(); 00495 encoded->PushValue(WORD_CMPR_VAL_SUFFIX, word.length()); 00496 encoded->PushString((const unsigned char*)word.get(), word.length()); 00497 #endif 00498 // 00499 // Fields 00500 // 00501 int nfields = key.NFields(); 00502 for(int i = 0; i < nfields; i++) { 00503 encoded->PushValue(i + WORD_CMPR_VAL_FIELDS, key.Get(i)); 00504 } 00505 // 00506 // Structural data 00507 // 00508 encoded->PushValue(WORD_CMPR_VAL_PGNO, e->pgno); 00509 encoded->PushValue(WORD_CMPR_VAL_NRECS, e->nrecs); 00510 } 00511 00512 previous_e = e; 00513 previous_key = key; 00514 } 00515 00516 encoded->PushValue(WORD_CMPR_VAL_FLAGS, changes); 00517 } 00518 00519 encoded->Put(stream); 00520 } 00521 00522 int outbuff_length = *outbuff_lengthp; 00523 if(stream.BuffLength() > (outbuff_length - WORD_CMPR_OVERHEAD)) { 00524 fprintf(stderr, "WordDBCompress::CompressIBtree: compressed length = %d > available length = %d\n", stream.BuffLength(), outbuff_length - WORD_CMPR_OVERHEAD); 00525 abort(); 00526 } 00527 memcpy(outbuff + WORD_CMPR_OVERHEAD, stream.Buff(), stream.BuffLength()); 00528 outbuff_length = WORD_CMPR_OVERHEAD + stream.BuffLength(); 00529 00530 if(debug) { 00531 unsigned char* tmp = (unsigned char*)malloc(inbuff_length); 00532 memset((char*)tmp, '0円', inbuff_length); 00533 UncompressIBtree(outbuff, outbuff_length, tmp, inbuff_length); 00534 if(DiffPage(inbuff, tmp)) { 00535 fprintf(stderr, "WordDBCompress::CompressIBtree: failed\n"); 00536 DumpPage(inbuff); 00537 DumpPage(tmp); 00538 exit(1); 00539 } 00540 free(tmp); 00541 } 00542 00543 *outbuff_lengthp = outbuff_length; 00544 00545 return 0; 00546 } 00547 00548 int 00549 WordDBCompress::CompressLBtree(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int *outbuff_lengthp) 00550 { 00551 PAGE *pp = (PAGE *)inbuff; 00552 00553 if(verbose > 5) DumpPage(inbuff); 00554 00555 WordBitCompress stream(inbuff_length); 00556 00557 stream.PutUint(pp->lsn.file, sizeof(pp->lsn.file) * 8); 00558 stream.PutUint(pp->lsn.offset, sizeof(pp->lsn.file) * 8); 00559 stream.PutUint(PGNO(pp), sizeof(PGNO(pp)) * 8); 00560 stream.PutUint(PREV_PGNO(pp), sizeof(PREV_PGNO(pp)) * 8); 00561 stream.PutUint(NEXT_PGNO(pp), sizeof(NEXT_PGNO(pp)) * 8); 00562 stream.PutUint(NUM_ENT(pp), sizeof(NUM_ENT(pp)) * 8); 00563 stream.PutUint(LEVEL(pp), sizeof(LEVEL(pp)) * 8); 00564 00565 if(NUM_ENT(pp) > 0) { 00566 int i; 00567 WordKey key(context); 00568 WordKey previous_key(context); 00569 WordRecord record(context); 00570 WordRecord previous_record(context); 00571 BKEYDATA* previous_key_e = 0; 00572 BKEYDATA* previous_record_e = 0; 00573 00574 encoded->Clear(); 00575 00576 for (i = 0; i < NUM_ENT(pp); i += P_INDX) { 00577 unsigned int changes = 0; 00578 00579 { 00580 BKEYDATA* key_e = GET_BKEYDATA(pp, i); 00581 if(debug) { 00582 if(key_e->type != B_KEYDATA) 00583 fprintf(stderr, "WordDBCompress::EncodeLBtree: unexpected type 0x%02x\n", key_e->type); 00584 if(key_e->len <= 0) 00585 fprintf(stderr, "WordDBCompress::EncodeLBtree: unexpected length <= 0\n"); 00586 } 00587 00588 key.Unpack((const char*)key_e->data, (int)key_e->len); 00589 00590 if(previous_key_e != 0) { 00591 int is_first_change = 1; 00592 #if 0 00593 // 00594 // String 00595 // 00596 const String& word = key.GetWord(); 00597 const String& previous_word = previous_key.GetWord(); 00598 if(word != previous_word) { 00599 unsigned int suffix_length = suffixlength(previous_word, word); 00600 unsigned int prefix_length = word.length() - suffix_length; 00601 encoded->PushValue(WORD_CMPR_VAL_PREFIX, prefix_length); 00602 encoded->PushValue(WORD_CMPR_VAL_SUFFIX, suffix_length); 00603 const char* suffix = word.get() + prefix_length; 00604 encoded->PushString((const unsigned char*)suffix, suffix_length); 00605 changes |= WORD_CMPR_VAL_FLAGS_STRING; 00606 is_first_change = 0; 00607 } 00608 #endif 00609 // 00610 // Fields 00611 // 00612 int nfields = key.NFields(); 00613 for(int i = 0; i < nfields; i++) { 00614 unsigned int value = key.Get(i); 00615 unsigned int previous_value = previous_key.Get(i); 00616 if(value != previous_value) { 00617 if(is_first_change) { 00618 value -= previous_value; 00619 is_first_change = 0; 00620 } 00621 encoded->PushValue(WORD_CMPR_VAL_FIELDS + i, value); 00622 changes |= WORD_CMPR_VAL_FLAGS_FIELD(i); 00623 } 00624 } 00625 } else { 00626 #if 0 00627 // 00628 // String 00629 // 00630 const String& word = key.GetWord(); 00631 encoded->PushValue(WORD_CMPR_VAL_SUFFIX, word.length()); 00632 encoded->PushString((const unsigned char*)word.get(), word.length()); 00633 #endif 00634 // 00635 // Fields 00636 // 00637 int nfields = key.NFields(); 00638 for(int i = 0; i < nfields; i++) { 00639 encoded->PushValue(i + WORD_CMPR_VAL_FIELDS, key.Get(i)); 00640 } 00641 } 00642 00643 previous_key_e = key_e; 00644 previous_key = key; 00645 } 00646 { 00647 BKEYDATA* record_e = GET_BKEYDATA(pp, i + 1); 00648 if(debug) { 00649 if(record_e->type != B_KEYDATA) 00650 fprintf(stderr, "WordDBCompress::EncodeLBtree: unexpected type 0x%02x\n", record_e->type); 00651 } 00652 00653 if(record_e->len <= 0) { 00654 changes |= WORD_CMPR_VAL_FLAGS_RECORD_NO; 00655 } else { 00656 record.Unpack((const char*)record_e->data, (int)record_e->len); 00657 00658 switch(record.type) { 00659 case WORD_RECORD_DATA: 00660 if(previous_record_e && 00661 record.info.data == previous_record.info.data) { 00662 changes |= WORD_CMPR_VAL_FLAGS_RECORD_EQ; 00663 } else { 00664 encoded->PushValue(WORD_CMPR_VAL_RVALUE, record.info.data); 00665 } 00666 break; 00667 case WORD_RECORD_STR: 00668 changes |= WORD_CMPR_VAL_FLAGS_RECORD_STR; 00669 if(previous_record_e && 00670 record.info.str == previous_record.info.str) { 00671 changes |= WORD_CMPR_VAL_FLAGS_RECORD_EQ; 00672 } else { 00673 const String& string = record.info.str; 00674 encoded->PushValue(WORD_CMPR_VAL_RLENGTH, string.length()); 00675 encoded->PushString((const unsigned char*)string.get(), string.length()); 00676 } 00677 break; 00678 default: 00679 fprintf(stderr, "WordDBCompress::EncodeLBtree: unexpected record.type = %d\n", record.type); 00680 break; 00681 } 00682 00683 previous_record = record; 00684 previous_record_e = record_e; 00685 } 00686 00687 } 00688 encoded->PushValue(WORD_CMPR_VAL_FLAGS, changes); 00689 } 00690 00691 encoded->Put(stream); 00692 } 00693 00694 int outbuff_length = *outbuff_lengthp; 00695 if(stream.BuffLength() > (outbuff_length - WORD_CMPR_OVERHEAD)) { 00696 fprintf(stderr, "WordDBCompress::CompressLBtree: compressed length = %d > available length = %d\n", stream.BuffLength(), outbuff_length - WORD_CMPR_OVERHEAD); 00697 abort(); 00698 } 00699 memcpy(outbuff + WORD_CMPR_OVERHEAD, stream.Buff(), stream.BuffLength()); 00700 outbuff_length = WORD_CMPR_OVERHEAD + stream.BuffLength(); 00701 00702 if(debug) { 00703 unsigned char* tmp = (unsigned char*)malloc(inbuff_length); 00704 memset((char*)tmp, '0円', inbuff_length); 00705 UncompressLBtree(outbuff, outbuff_length, tmp, inbuff_length); 00706 if(DiffPage(inbuff, tmp)) { 00707 fprintf(stderr, "WordDBCompress::CompressLBtree: failed\n"); 00708 DumpPage(inbuff); 00709 DumpPage(tmp); 00710 exit(1); 00711 } 00712 free(tmp); 00713 } 00714 00715 *outbuff_lengthp = outbuff_length; 00716 00717 return 0; 00718 } 00719 00720 int 00721 WordDBCompress::UncompressBtree(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int outbuff_length) 00722 { 00723 int ret = 0; 00724 switch((*inbuff) & TYPE_MASK) { 00725 case P_IBTREE: 00726 ret = UncompressIBtree(inbuff, inbuff_length, outbuff, outbuff_length); 00727 break; 00728 case P_LBTREE: 00729 ret = UncompressLBtree(inbuff, inbuff_length, outbuff, outbuff_length); 00730 break; 00731 } 00732 if(verbose) fprintf(stderr, "WordDBCompress::UncompressBtree: page %d\n", PGNO((PAGE*)outbuff)); 00733 return ret; 00734 } 00735 00736 /* 00737 * cdb___db_pitem -- 00738 * Put an item on a page. 00739 */ 00740 static void 00741 cdb___db_pitem(PAGE *pagep, u_int32_t indx, u_int32_t nbytes, DBT *hdr, DBT *data) 00742 { 00743 u_int8_t *p; 00744 00745 HOFFSET(pagep) -= nbytes; 00746 pagep->inp[indx] = HOFFSET(pagep); 00747 ++NUM_ENT(pagep); 00748 00749 p = P_ENTRY(pagep, indx); 00750 memcpy(p, hdr->data, hdr->size); 00751 if (data != NULL) 00752 memcpy(p + hdr->size, data->data, data->size); 00753 } 00754 00755 int 00756 WordDBCompress::UncompressIBtree(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int outbuff_length) 00757 { 00758 memset((char*)outbuff, '0円', outbuff_length); 00759 00760 WordBitCompress stream(inbuff_length); 00761 stream.BuffSet(inbuff + WORD_CMPR_OVERHEAD, inbuff_length - WORD_CMPR_OVERHEAD); 00762 00763 PAGE *pp = (PAGE *)outbuff; 00764 00765 TYPE_TAGS(pp) = *inbuff; 00766 HOFFSET(pp) = outbuff_length; 00767 short entry_count = 0; 00768 pp->lsn.file = stream.GetUint(sizeof(pp->lsn.file) * 8); 00769 pp->lsn.offset = stream.GetUint(sizeof(pp->lsn.offset) * 8); 00770 PGNO(pp) = stream.GetUint(sizeof(PGNO(pp)) * 8); 00771 entry_count = stream.GetUint(sizeof(NUM_ENT(pp)) * 8); 00772 LEVEL(pp) = stream.GetUint(sizeof(LEVEL(pp)) * 8); 00773 NEXT_PGNO(pp) = PREV_PGNO(pp) = 0; 00774 00775 if(entry_count > 0) { 00776 int i; 00777 String packed; 00778 BINTERNAL previous_bi; 00779 BINTERNAL bi; 00780 DBT hdr; 00781 hdr.data = &bi; 00782 hdr.size = SSZA(BINTERNAL, data); 00783 DBT data; 00784 WordKey key(context); 00785 WordKey previous_key(context); 00786 String word; 00787 00788 encoded->Get(stream); 00789 00790 for(i = 0; i < entry_count; i++) { 00791 unsigned int changes = encoded->ShiftValue(WORD_CMPR_VAL_FLAGS); 00792 00793 memset((char*)&bi, '0円', sizeof(BINTERNAL)); 00794 00795 if(changes & WORD_CMPR_VAL_FLAGS_EMPTY) { 00796 packed.trunc(); 00797 // 00798 // Structural data 00799 // 00800 bi.pgno = encoded->ShiftValue(WORD_CMPR_VAL_PGNO); 00801 bi.nrecs = encoded->ShiftValue(WORD_CMPR_VAL_NRECS); 00802 } else { 00803 key.Clear(); 00804 00805 if(!previous_key.Empty()) { 00806 int is_first_change = 1; 00807 #if 0 00808 // 00809 // String 00810 // 00811 if(changes & WORD_CMPR_VAL_FLAGS_STRING) { 00812 unsigned int suffix_length = encoded->ShiftValue(WORD_CMPR_VAL_SUFFIX); 00813 unsigned int prefix_length = encoded->ShiftValue(WORD_CMPR_VAL_PREFIX); 00814 unsigned char* suffix = encoded->ShiftString(suffix_length); 00815 word.set(previous_key.GetWord().get(), prefix_length); 00816 word.append((const char*)suffix, suffix_length); 00817 key.SetWord(word); 00818 is_first_change = 0; 00819 } else { 00820 key.SetWord(previous_key.GetWord()); 00821 } 00822 #endif 00823 // 00824 // Fields 00825 // 00826 int nfields = key.NFields(); 00827 for(int j = 0; j < nfields; j++) { 00828 if(changes & WORD_CMPR_VAL_FLAGS_FIELD(j)) { 00829 unsigned int value = encoded->ShiftValue(j + WORD_CMPR_VAL_FIELDS); 00830 if(is_first_change) { 00831 value += previous_key.Get(j); 00832 is_first_change = 0; 00833 } 00834 key.Set(j, value); 00835 } else { 00836 key.Set(j, previous_key.Get(j)); 00837 } 00838 } 00839 // 00840 // Structural data 00841 // 00842 if(changes & WORD_CMPR_VAL_FLAGS_PGNO) { 00843 bi.pgno = encoded->ShiftValue(WORD_CMPR_VAL_PGNO); 00844 } else { 00845 bi.pgno = previous_bi.pgno; 00846 } 00847 if(changes & WORD_CMPR_VAL_FLAGS_NRECS) { 00848 bi.nrecs = encoded->ShiftValue(WORD_CMPR_VAL_NRECS); 00849 } else { 00850 bi.nrecs = previous_bi.nrecs; 00851 } 00852 } else { 00853 #if 0 00854 // 00855 // String 00856 // 00857 unsigned int string_length = encoded->ShiftValue(WORD_CMPR_VAL_SUFFIX); 00858 unsigned char* string = encoded->ShiftString(string_length); 00859 key.SetWord((const char*)string, string_length); 00860 #endif 00861 // 00862 // Fields 00863 // 00864 int nfields = key.NFields(); 00865 for(int i = 0; i < nfields; i++) { 00866 key.Set(i, encoded->ShiftValue(i + WORD_CMPR_VAL_FIELDS)); 00867 } 00868 // 00869 // Structural data 00870 // 00871 bi.pgno = encoded->ShiftValue(WORD_CMPR_VAL_PGNO); 00872 bi.nrecs = encoded->ShiftValue(WORD_CMPR_VAL_NRECS); 00873 } 00874 00875 key.Pack(packed); 00876 } 00877 data.data = packed.get(); 00878 data.size = packed.length(); 00879 bi.len = packed.length(); 00880 bi.type = B_KEYDATA; 00881 00882 // 00883 // Insert entry in page 00884 // 00885 cdb___db_pitem(pp, i, BINTERNAL_SIZE(bi.len), &hdr, &data); 00886 00887 // 00888 // Current becomes previous 00889 // 00890 previous_bi = bi; 00891 previous_key = key; 00892 } 00893 } 00894 00895 if(debug) { 00896 if(entry_count != NUM_ENT(pp)) 00897 fprintf(stderr, "WordDBCompress::UncompressIBtree: pgno %d: NUM_ENT(pp) = %d is different than entry_count = %d\n", PGNO(pp), NUM_ENT(pp), entry_count); 00898 } 00899 return 0; 00900 } 00901 00902 int 00903 WordDBCompress::UncompressLBtree(const unsigned char *inbuff, int inbuff_length, unsigned char *outbuff, int outbuff_length) 00904 { 00905 memset((char*)outbuff, '0円', outbuff_length); 00906 00907 WordBitCompress stream(inbuff_length); 00908 stream.BuffSet(inbuff + WORD_CMPR_OVERHEAD, inbuff_length - WORD_CMPR_OVERHEAD); 00909 00910 PAGE *pp = (PAGE *)outbuff; 00911 00912 TYPE_TAGS(pp) = *inbuff; 00913 HOFFSET(pp) = outbuff_length; 00914 short entry_count = 0; 00915 pp->lsn.file = stream.GetUint(sizeof(pp->lsn.file) * 8); 00916 pp->lsn.offset = stream.GetUint(sizeof(pp->lsn.offset) * 8); 00917 PGNO(pp) = stream.GetUint(sizeof(PGNO(pp)) * 8); 00918 PREV_PGNO(pp) = stream.GetUint(sizeof(PREV_PGNO(pp)) * 8); 00919 NEXT_PGNO(pp) = stream.GetUint(sizeof(NEXT_PGNO(pp)) * 8); 00920 entry_count = stream.GetUint(sizeof(NUM_ENT(pp)) * 8); 00921 LEVEL(pp) = stream.GetUint(sizeof(LEVEL(pp)) * 8); 00922 00923 if(entry_count > 0) { 00924 int i; 00925 String packed; 00926 BKEYDATA bk; 00927 DBT hdr; 00928 hdr.data = &bk; 00929 hdr.size = SSZA(BKEYDATA, data); 00930 DBT data; 00931 WordKey key(context); 00932 WordKey previous_key(context); 00933 WordRecord record(context); 00934 WordRecord previous_record(context); 00935 int previous_record_p = 0; 00936 String word; 00937 00938 encoded->Get(stream); 00939 00940 for(i = 0; i < entry_count; i += P_INDX) { 00941 unsigned int changes = encoded->ShiftValue(WORD_CMPR_VAL_FLAGS); 00942 00943 // 00944 // Key 00945 // 00946 { 00947 memset((char*)&bk, '0円', sizeof(BKEYDATA)); 00948 00949 key.Clear(); 00950 00951 if(!previous_key.Empty()) { 00952 int is_first_change = 1; 00953 #if 0 00954 // 00955 // String 00956 // 00957 if(changes & WORD_CMPR_VAL_FLAGS_STRING) { 00958 unsigned int suffix_length = encoded->ShiftValue(WORD_CMPR_VAL_SUFFIX); 00959 unsigned int prefix_length = encoded->ShiftValue(WORD_CMPR_VAL_PREFIX); 00960 unsigned char* suffix = encoded->ShiftString(suffix_length); 00961 word.set(previous_key.GetWord().get(), prefix_length); 00962 word.append((const char*)suffix, suffix_length); 00963 key.SetWord(word); 00964 is_first_change = 0; 00965 } else { 00966 key.SetWord(previous_key.GetWord()); 00967 } 00968 #endif 00969 // 00970 // Fields 00971 // 00972 int nfields = key.NFields(); 00973 for(int j = 0; j < nfields; j++) { 00974 if(changes & WORD_CMPR_VAL_FLAGS_FIELD(j)) { 00975 unsigned int value = encoded->ShiftValue(j + WORD_CMPR_VAL_FIELDS); 00976 if(is_first_change) { 00977 value += previous_key.Get(j); 00978 is_first_change = 0; 00979 } 00980 key.Set(j, value); 00981 } else { 00982 key.Set(j, previous_key.Get(j)); 00983 } 00984 } 00985 } else { 00986 #if 0 00987 // 00988 // String 00989 // 00990 unsigned int string_length = encoded->ShiftValue(WORD_CMPR_VAL_SUFFIX); 00991 unsigned char* string = encoded->ShiftString(string_length); 00992 key.SetWord((const char*)string, string_length); 00993 #endif 00994 // 00995 // Fields 00996 // 00997 int nfields = key.NFields(); 00998 for(int i = 0; i < nfields; i++) { 00999 key.Set(i, encoded->ShiftValue(i + WORD_CMPR_VAL_FIELDS)); 01000 } 01001 } 01002 01003 key.Pack(packed); 01004 01005 data.data = packed.get(); 01006 data.size = packed.length(); 01007 bk.len = packed.length(); 01008 bk.type = B_KEYDATA; 01009 01010 // 01011 // Insert entry in page 01012 // 01013 cdb___db_pitem(pp, i, BKEYDATA_SIZE(bk.len), &hdr, &data); 01014 01015 previous_key = key; 01016 } 01017 // 01018 // Record 01019 // 01020 { 01021 memset((char*)&bk, '0円', sizeof(BKEYDATA)); 01022 01023 if(changes & WORD_CMPR_VAL_FLAGS_RECORD_NO) { 01024 packed.trunc(); 01025 } else { 01026 if(changes & WORD_CMPR_VAL_FLAGS_RECORD_STR) { 01027 record.type = WORD_RECORD_STR; 01028 if(previous_record_p && 01029 (changes & WORD_CMPR_VAL_FLAGS_RECORD_EQ)) { 01030 record.info.str = previous_record.info.str; 01031 } else { 01032 unsigned int string_length = encoded->ShiftValue(WORD_CMPR_VAL_RLENGTH); 01033 unsigned char* string = encoded->ShiftString(string_length); 01034 record.info.str.set((const char*)string, string_length); 01035 } 01036 } else { 01037 record.type = WORD_RECORD_DATA; 01038 01039 if(previous_record_p && 01040 (changes & WORD_CMPR_VAL_FLAGS_RECORD_EQ)) { 01041 record.info.data = previous_record.info.data; 01042 } else { 01043 record.info.data = encoded->ShiftValue(WORD_CMPR_VAL_RVALUE); 01044 } 01045 } 01046 01047 record.Pack(packed); 01048 01049 previous_record = record; 01050 previous_record_p = 1; 01051 } 01052 data.data = packed.get(); 01053 data.size = packed.length(); 01054 bk.len = packed.length(); 01055 bk.type = B_KEYDATA; 01056 01057 // 01058 // Insert entry in page 01059 // 01060 cdb___db_pitem(pp, i + 1, BKEYDATA_SIZE(bk.len), &hdr, &data); 01061 } 01062 } 01063 } 01064 01065 if(debug) { 01066 if(entry_count != NUM_ENT(pp)) 01067 fprintf(stderr, "WordDBCompress::UncompressLBtree: pgno %d: NUM_ENT(pp) = %d is different than entry_count = %d\n", PGNO(pp), NUM_ENT(pp), entry_count); 01068 } 01069 01070 return 0; 01071 } 01072 01073 DB_CMPR_INFO* WordDBCompress::CmprInfo() 01074 { 01075 DB_CMPR_INFO *cmpr_info = new DB_CMPR_INFO; 01076 01077 cmpr_info->user_data = (void *)this; 01078 cmpr_info->compress = WordDBCompress_compress_c; 01079 cmpr_info->uncompress = WordDBCompress_uncompress_c; 01080 cmpr_info->coefficient = 3; 01081 cmpr_info->max_npages = 9; 01082 cmprInfo = cmpr_info; 01083 01084 return cmpr_info; 01085 } 01086 01087 void 01088 WordDBCompress::DumpPage(const unsigned char* page) const 01089 { 01090 PAGE* pp = (PAGE*)page; 01091 01092 fprintf(stderr, "--------------------------------------------------\n"); 01093 fprintf(stderr, "pgno = %d ", PGNO(pp)); 01094 fprintf(stderr, "lsn.file = %d ", pp->lsn.file); 01095 fprintf(stderr, "lsn.offset = %d ", pp->lsn.offset); 01096 fprintf(stderr, "prev_pgno = %d ", PREV_PGNO(pp)); 01097 fprintf(stderr, "next_pgno = %d\n", NEXT_PGNO(pp)); 01098 fprintf(stderr, "entries = %d ", NUM_ENT(pp)); 01099 fprintf(stderr, "hf_offset = %d ", HOFFSET(pp)); 01100 fprintf(stderr, "level = %d ", LEVEL(pp)); 01101 fprintf(stderr, "type = %d\n", TYPE(pp)); 01102 fprintf(stderr, "tags = 0x%02x\n", TAGS(pp)); 01103 fprintf(stderr, "freespace = %d bytes from byte %d to byte %d\n", P_FREESPACE(pp), LOFFSET(pp), HOFFSET(pp)); 01104 01105 int i; 01106 for(i = 0; i < NUM_ENT(pp); i++) { 01107 fprintf(stderr, "index = %d, ", pp->inp[i]); 01108 unsigned char* data = 0; 01109 int data_length = 0; 01110 switch(TYPE(pp)) { 01111 case P_IBTREE: 01112 { 01113 BINTERNAL* e = GET_BINTERNAL(pp, i); 01114 fprintf(stderr, "len = %d, type = %d, pgno = %d, nrecs = %d\n", e->len, e->type, e->pgno, e->nrecs); 01115 data = (unsigned char*)e->data; 01116 data_length = e->len; 01117 } 01118 break; 01119 case P_LBTREE: 01120 { 01121 BKEYDATA* e = GET_BKEYDATA(pp, i); 01122 fprintf(stderr, "len = %d, type = %d\n", e->len, e->type); 01123 data = (unsigned char*)e->data; 01124 data_length = e->len; 01125 } 01126 break; 01127 } 01128 if(data_length > 0) { 01129 int j; 01130 for(j = 0; j < data_length; j++) { 01131 fprintf(stderr, "(%d) ", data[j]); 01132 } 01133 fprintf(stderr, "\n"); 01134 } 01135 } 01136 } 01137 01138 int 01139 WordDBCompress::DiffPage(const unsigned char* first, const unsigned char* second) const 01140 { 01141 PAGE* p1 = (PAGE*)first; 01142 PAGE* p2 = (PAGE*)second; 01143 01144 if(TAGS(p1) != TAGS(p2)) return 1; 01145 if(TYPE(p1) != TYPE(p2)) return 1; 01146 if(PGNO(p1) != PGNO(p2)) return 1; 01147 if(p1->lsn.file != p2->lsn.file) return 1; 01148 if(p1->lsn.offset != p2->lsn.offset) return 1; 01149 if(TYPE(p1) == P_LBTREE) { 01150 if(PREV_PGNO(p1) != PREV_PGNO(p2)) return 1; 01151 if(NEXT_PGNO(p1) != NEXT_PGNO(p2)) return 1; 01152 } 01153 if(NUM_ENT(p1) != NUM_ENT(p2)) return 1; 01154 if(HOFFSET(p1) != HOFFSET(p2)) return 1; 01155 if(LEVEL(p1) != LEVEL(p2)) return 1; 01156 01157 int i; 01158 for(i = 0; i < NUM_ENT(p1); i++) { 01159 unsigned char* data1 = 0; 01160 int data1_length = 0; 01161 unsigned char* data2 = 0; 01162 int data2_length = 0; 01163 switch(TYPE(p1)) { 01164 case P_IBTREE: 01165 { 01166 BINTERNAL* e1 = GET_BINTERNAL(p1, i); 01167 BINTERNAL* e2 = GET_BINTERNAL(p2, i); 01168 if(e1->len != e2->len) return 1; 01169 if(e1->type != e2->type) return 1; 01170 if(e1->pgno != e2->pgno) return 1; 01171 if(e1->nrecs != e2->nrecs) return 1; 01172 data1 = (unsigned char*)e1->data; 01173 data1_length = e1->len; 01174 data2 = (unsigned char*)e2->data; 01175 data2_length = e2->len; 01176 } 01177 break; 01178 case P_LBTREE: 01179 { 01180 BKEYDATA* e1 = GET_BKEYDATA(p1, i); 01181 BKEYDATA* e2 = GET_BKEYDATA(p2, i); 01182 if(e1->len != e2->len) return 1; 01183 if(e1->type != e2->type) return 1; 01184 data1 = (unsigned char*)e1->data; 01185 data1_length = e1->len; 01186 data2 = (unsigned char*)e2->data; 01187 data2_length = e2->len; 01188 } 01189 break; 01190 } 01191 if(data1_length > 0) { 01192 int j; 01193 for(j = 0; j < data1_length; j++) { 01194 if(data1[j] != data2[j]) return 1; 01195 } 01196 } 01197 } 01198 return 0; 01199 }