// // WordDBPage.cc // // WordDBPage: Implements specific compression scheme for // Berkeley DB pages containing WordReferences objects. // // Part of the ht://Dig package // Copyright (c) 1999-2004 The ht://Dig Group // For copyright details, see the file COPYING in your distribution // or the GNU Library General Public License (LGPL) version 2 or later // // // $Id: WordDBPage.cc,v 1.5 2004/05/28 13:15:26 lha Exp $ // #ifdef HAVE_CONFIG_H #include "htconfig.h" #endif /* HAVE_CONFIG_H */ #include"WordDBPage.h" #include"WordDBCompress.h" #include #define NBITS_CMPRTYPE 2 #define CMPRTYPE_NORMALCOMRPESS 0 #define CMPRTYPE_BADCOMPRESS 1 // *********************************************** // ********** Compression Versions ************** // *********************************************** // never change NBITS_COMPRESS_VERSION ! (otherwise version tracking will fail) #define NBITS_COMPRESS_VERSION 11 // IMPORTANT: change these EVERY time you change something that affects the compression #define COMPRESS_VERSION 4 static const char *version_label[]={"INVALID_VERSION_0","INVALID_VERSION_1","INVALID_VERSION_2","14 Dec 1999","3 Jan 2000",NULL}; // returns the label of compression version v static const char * get_version_label(int v) { // check if version number is ok if(COMPRESS_VERSION <0 || COMPRESS_VERSION>((sizeof(version_label)/sizeof(*version_label))-1)) { errr("get_version_label: version_label[COMPRESS_VERSION] is not valid, please update version_label"); } if( v >= (int)((sizeof(version_label)/sizeof(*version_label))-1) ) { return("INVALID_VERSION"); } // return label return(version_label[v]); } // *********************************************** // ********** WordDBPage *********************** // *********************************************** // checks if compression/decompression sequence is harmless int WordDBPage::TestCompress(int debuglevel) { if(debuglevel>2){printf("ttttttttttttt WordDBPage::TestCompress BEGIN\n");} int compress_debug=debuglevel-1; // start by compressing this page Compressor *res=Compress(compress_debug); if(res) { int size=res->size(); // now uncompress into pageu WordDBPage pageu(pgsz); res->rewind(); pageu.Uncompress(res,compress_debug); // comapre this page and pageu int cmp=Compare(pageu); // show some results if(debuglevel>2)printf("TOTAL SIZE: %6d %8f\n",size,size/8.0); // argh! compare failed somthing went wrong // display the compress/decompress sequence and fail if(cmp || size>8*1024*1000000000) { if(size>8*1024) { printf("---------------------------------------------------\n"); printf("-----------overflow:%5d------------------------------\n",size/8); printf("---------------------------------------------------\n"); printf("---------------------------------------------------\n"); } printf("################### ORIGINAL #########################################\n"); show(); printf("################### REDECOMPRESSED #########################################\n"); pageu.show(); // re-compress the page verbosely Compressor *res2=Compress(2); res2->rewind(); // re-uncompress the page verbosely WordDBPage pageu2(pgsz); pageu2.Uncompress(res2,2); pageu2.show(); if(cmp){errr("Compare failed");} delete res2; } pageu.delete_page(); delete res; }else {errr("WordDBPage::TestCompress: Compress failed");} if(debuglevel>2){printf("ttttttttttttt WordDBPage::TestCompress END\n");} return OK; } // find position of first difference between 2 strings static int first_diff(const String &s1,const String &s2) { int j; for(j=0;j1){verbose=1;} if(verbose){printf("uuuuuuuuu WordDBPage::Uncompress: BEGIN\n");} // ** first check if versions are OK int read_version = pin->get_uint(NBITS_COMPRESS_VERSION,"COMPRESS_VERSION"); if(read_version != COMPRESS_VERSION) { fprintf(stderr,"WordDBPage::Uncompress: *** Compression version mismatch ***\n"); fprintf(stderr,"found version : %3d but using version : %3d\n",read_version,COMPRESS_VERSION); fprintf(stderr,"found version label: %s\n",get_version_label(read_version)); fprintf(stderr,"using version label: %s\n",get_version_label(COMPRESS_VERSION)); fprintf(stderr,"Are you sure you're not reading an old DB with a newer version of the indexer??\n"); errr("WordDBPage::Uncompress: *** Compression version mismatch ***"); exit(1); } // ** now see if this page was a normal or uncorrectly compressed page int cmprtype=pin->get_uint(NBITS_CMPRTYPE,"CMPRTYPE"); // two possible cases switch(cmprtype) { case CMPRTYPE_NORMALCOMRPESS:// this was a normaly compressed page Uncompress_main(pin); break; case CMPRTYPE_BADCOMPRESS:// this page did not compress correctly pin->get_zone((byte *)pg,pgsz*8,"INITIALBUFFER"); break; default: errr("WordDBPage::Uncompress: CMPRTYPE incoherent"); } if(verbose){printf("uuuuuuuuu WordDBPage::Uncompress: END\n");} return OK; } // ******* Uncompress Compressor into this page // normally compressed page case int WordDBPage::Uncompress_main(Compressor *pin) { if(!pin){errr("WordDBPage::Uncompress: no Compressor to uncompress from!!");} Compressor &in=*((Compressor *)pin); if(debug>0){in.set_use_tags();} int i,j; // number arrays used to reconstruct the original page unsigned int **rnums=new unsigned int *[nnums]; CHECK_MEM(rnums); // sizes of each array int *rnum_sizes=new int[nnums]; CHECK_MEM(rnum_sizes); // char differences between words byte *rworddiffs=NULL; int nrworddiffs; // *********** read header if(Uncompress_header(in)!=OK){return NOTOK;} // get first key(s): //type=5: key(0) stored seperately ... others are decompressed frome differences // //type=3: btikey(0) is particular (len=0) it is stored seperately // btikey(1) stored seperately ... others are decompressed frome differences // int nkeysleft=nk; if(nkeysleft>0) { WordDBKey key0=uncompress_key(in,0); if(type==P_LBTREE){uncompress_data(in,0,key0.RecType());} nkeysleft--; } if(nkeysleft>0 && type==P_IBTREE){uncompress_key(in,1);nkeysleft--;} if(nkeysleft>0) { // ********* read numerical fields Uncompress_vals_chaged_flags(in,&(rnums[0]),&(rnum_sizes[0])); for(j=1;jlsn.file =in.get_uint_vl( 8*sizeof(pg->lsn.file ),"page:lsn.file"); pg->lsn.offset =in.get_uint_vl( 8*sizeof(pg->lsn.offset ),"page:lsn.offset"); pg->pgno =in.get_uint_vl( 8*sizeof(pg->pgno ),"page:pgno"); pg->prev_pgno =in.get_uint_vl( 8*sizeof(pg->prev_pgno ),"page:prev_pgno"); pg->next_pgno =in.get_uint_vl( 8*sizeof(pg->next_pgno ),"page:next_pgno"); pg->entries =in.get_uint_vl( 8*sizeof(pg->entries ),"page:entries"); pg->hf_offset =in.get_uint_vl( 8*sizeof(pg->hf_offset ),"page:hf_offset"); pg->level =in.get_uint_vl( 8*sizeof(pg->level ),"page:level"); pg->type =in.get_uint_vl( 8*sizeof(pg->type ),"page:type"); init(); if(verbose) { printf("************************************\n"); printf("******** WordDBPage::Uncompress: page header ***\n"); printf("************************************\n"); printf("page size:%d\n",(int)pgsz); printf(" 00-07: Log sequence number. file : %d\n", pg->lsn.file ); printf(" 00-07: Log sequence number. offset: %d\n", pg->lsn.offset ); printf(" 08-11: Current page number. : %d\n", pg->pgno ); printf(" 12-15: Previous page number. : %d\n", pg->prev_pgno ); printf(" 16-19: Next page number. : %d\n", pg->next_pgno ); printf(" 20-21: Number of item pairs on the page. : %d\n", pg->entries ); printf(" 22-23: High free byte page offset. : %d\n", pg->hf_offset ); printf(" 24: Btree tree level. : %d\n", pg->level ); printf(" 25: Page type. : %d\n", pg->type ); } return OK; } void WordDBPage::Uncompress_rebuild(unsigned int **rnums,int *rnum_sizes,int nnums0,byte *rworddiffs,int nrworddiffs) { int irwordiffs=0; int nfields=WordKey::NFields(); int *rnum_pos=new int[ nnums0];// current index count CHECK_MEM(rnum_pos); int ii,j; for(j=0;ji0) { unsigned int flags=rnums[CNFLAGS][rnum_pos[CNFLAGS]++]; int foundfchange=0; // **** reconstruct the word if(flags&pow2(nfields-1))// check flags to see if word has changed { foundfchange=1; if(rnum_pos[CNWORDDIFFLEN]>=rnum_sizes[CNWORDDIFFLEN]){errr("WordDBPage::Uncompress read wrong num worddiffs");} // get position of first character that changes in this word int diffpos=rnums[CNWORDDIFFPOS][rnum_pos[CNWORDDIFFPOS]++]; // get size of changed part of the word int difflen=rnums[CNWORDDIFFLEN][rnum_pos[CNWORDDIFFLEN]++]; int wordlen=diffpos+difflen; char *str=new char [wordlen+1]; CHECK_MEM(str); // copy the unchanged part into str from previos key's word if(diffpos)strncpy(str,(char *)pkey.GetWord(),diffpos); // copy the changed part from coded word differences strncpy(str+diffpos,(char *)rworddiffs+irwordiffs,difflen); str[wordlen]=0; if(verbose)printf("key %3d word:\"%s\"\n",ii,str); akey.SetWord(str); irwordiffs+=difflen; delete [] str; }else{akey.SetWord(pkey.GetWord());} // **** reconstruct the numerical key fields for(j=1;j=rnum_sizes[k]){errr("WordDBPage::Uncompress read wrong num of changes in a field");} if(!foundfchange) { // this is the first field that changes in this key // so difference is coded compared to value in pevious key akey.Set(j,rnums[k][indx]+pkey.Get(j)); } else { // this is NOT the first field that changes in this key // so difference is coded from 0 akey.Set(j,rnums[k][indx]); } // we read 1 element from coded differences in this field rnum_pos[k]++; foundfchange=1; } else { // no changes found, just copy from previous key if(!foundfchange){akey.Set(j,pkey.Get(j));} else{akey.Set(j,0);} } } } // now insert key/data into page if(type==P_LBTREE) { if(ii>i0)insert_key(akey); if(ii>i0)insert_data(arec); } else { if(type!=3){errr("WordDBPage::Uncompress_rebuild: unsupported type!=3");} if(ii>i0)insert_btikey(akey,bti); } pkey=akey; } delete [] rnum_pos; } // display void WordDBPage::Uncompress_show_rebuild(unsigned int **rnums,int *rnum_sizes,int nnums0,byte *rworddiffs,int nrworddiffs) { int i,j; if(verbose) { printf("WordDBPage::Uncompress_show_rebuild: rebuilt numerical fields\n"); for(j=0;j1){verbose=1;} Compressor *res=(Compressor *)new Compressor((cmprInfo ? pgsz/(1<<(cmprInfo->coefficient)) : pgsz/4)); CHECK_MEM(res); if(debug>0){res->set_use_tags();} res->put_uint(COMPRESS_VERSION,NBITS_COMPRESS_VERSION,"COMPRESS_VERSION"); res->put_uint(CMPRTYPE_NORMALCOMRPESS,NBITS_CMPRTYPE,"CMPRTYPE"); if(verbose){printf("WordDBPage::Compress: trying normal compress\n");} int cmpr_ok=Compress_main(*((Compressor *)res)); if(cmpr_ok!=OK || res->buffsize()>pgsz) { if(verbose){printf("WordDBCompress::Compress full compress failed ... not compressing at all\n");} show(); if(res){delete res;} res=new Compressor; CHECK_MEM(res); if(debug>0){res->set_use_tags();} res->put_uint(COMPRESS_VERSION,NBITS_COMPRESS_VERSION,"COMPRESS_VERSION"); res->put_uint(CMPRTYPE_BADCOMPRESS,NBITS_CMPRTYPE,"CMPRTYPE"); res->put_zone((byte *)pg,pgsz*8,"INITIALBUFFER"); } if(verbose) { printf("WordDBPage::Compress: Final bitstream result\n"); res->show(); } return res; }; int WordDBPage::Compress_main(Compressor &out) { if(debug>1){verbose=1;} if(verbose){printf("WordDBPage::Compress_main: starting compression\n");} if(pg->type!=5 && pg->type!=3){ printf("pg->type:%3d\n",pg->type);return NOTOK;} // if(pg->type==P_IBTREE){show();} // *************** initialize data structures ************** int j; // 0 -> changed/unchanged flags : 4bits // 1..n -> numerical fields delta : ?bits (depending on field) // n+1 -> word changed size : 1 int *nums =new int[nk*nnums]; CHECK_MEM(nums); int *nums_pos=new int[ nnums]; CHECK_MEM(nums_pos); // int *cnsizes =new int[ nnums]; for(j=0;jsort[j].bits;} // cnsizes[CNFLAGS]=4; // cnsizes[CNWORDDIFFPOS ]=8; // cnsizes[CNWORDDIFFLEN ]=8; HtVector_byte worddiffs; //bmt_START; // *************** extract values and wordiffs ************** if(nk>0) { Compress_extract_vals_wordiffs(nums,nums_pos,nnums,worddiffs); if(verbose)Compress_show_extracted(nums,nums_pos,nnums,worddiffs); } // *************** init compression ************** //bmt_END;bmt_START; Compress_header(out); // *************** compress values and wordiffs ************** // compress first key(s) int nkeysleft=nk; if(nkeysleft>0) { compress_key(out,0); if(type==P_LBTREE){compress_data(out,0);} nkeysleft--; } if(nkeysleft>0 && type==P_IBTREE){compress_key(out,1);nkeysleft--;} if(nkeysleft>0) { //bmt_END;bmt_START; // compress values Compress_vals(out,nums,nums_pos,nnums); //bmt_END;bmt_START; // compress worddiffs int size=out.put_fixedbitl(worddiffs.begin(),worddiffs.size(),"WordDiffs"); if(verbose)printf("compressed wordiffs : %3d values: %4d bits %4f bytes\n",worddiffs.size(),size,size/8.0); //bmt_END; } // *************** cleanup ************** delete [] nums ; delete [] nums_pos; return OK; } void WordDBPage::Compress_extract_vals_wordiffs(int *nums,int *nums_pos,int ,HtVector_byte &worddiffs) { WordDBKey pkey; int ii,j; int i0=0; if(type==P_IBTREE){i0=1;}// internal pages have particular first key for(ii=i0;iipgno ; nums[CNBTINRECS*nk+nums_pos[CNBTINRECS]++]=btikey(ii)->nrecs; } // all that follows codes differences between succesive entries // that is: Numerical key fields, Words if(ii>i0) { // clear changed falgs int iflag=CNFLAGS*nk+nums_pos[CNFLAGS]++; nums[iflag]=0; int foundfchange=0; const String &aword=akey.GetWord(); const String &pword=pkey.GetWord(); if(!(aword==pword)){foundfchange=1;} // check numerical fields for changes // ******** sets CNFIELDS and some of CNFLAGS ************ for(j=1;j0) { out.put(1,"rep"); out.put_uint_vl(k,nbits,NULL); i+=k; } else {out.put(0,"rep");} } size=out.size()-size; if(verbose)printf("compressed flags %2d : %3d values: %4d bits %8f bytes : ended bit field pos:%6d\n",0,n,size,size/8.0,out.size()); } void WordDBPage::Compress_vals(Compressor &out,int *nums,int *nums_pos,int nnums0) { // the changed flags fields are particular Compress_vals_changed_flags(out,(unsigned int *)(nums+0*nk),nums_pos[0]); // compress the difference numbers for the numerical fields for( int j=1;jlsn.file , 8*sizeof(pg->lsn.file ),"page:lsn.file"); out.put_uint_vl(pg->lsn.offset , 8*sizeof(pg->lsn.offset ),"page:lsn.offset"); out.put_uint_vl(pg->pgno , 8*sizeof(pg->pgno ),"page:pgno"); out.put_uint_vl(pg->prev_pgno , 8*sizeof(pg->prev_pgno ),"page:prev_pgno"); out.put_uint_vl(pg->next_pgno , 8*sizeof(pg->next_pgno ),"page:next_pgno"); out.put_uint_vl(pg->entries , 8*sizeof(pg->entries ),"page:entries"); out.put_uint_vl(pg->hf_offset , 8*sizeof(pg->hf_offset ),"page:hf_offset"); out.put_uint_vl(pg->level , 8*sizeof(pg->level ),"page:level"); out.put_uint_vl(pg->type , 8*sizeof(pg->type ),"page:type"); } void WordDBPage::Compress_show_extracted(int *nums,int *nums_pos,int nnums0,HtVector_byte &worddiffs) { int i,j; int *cnindexe2=new int[ nnums0]; CHECK_MEM(cnindexe2); for(j=0;jworddiffs.size() ? nk : worddiffs.size()); for(i=0;ilsn.file != pg->lsn.file ){res++;printf("compare failed for pg->lsn.file \n");} if(other.pg->lsn.offset != pg->lsn.offset ){res++;printf("compare failed for pg->lsn.offset \n");} if(other.pg->pgno != pg->pgno ){res++;printf("compare failed for pg->pgno \n");} if(other.pg->prev_pgno != pg->prev_pgno ){res++;printf("compare failed for pg->prev_pgno \n");} if(other.pg->next_pgno != pg->next_pgno ){res++;printf("compare failed for pg->next_pgno \n");} if(other.pg->entries != pg->entries ){res++;printf("compare failed for pg->entries \n");} if(other.pg->hf_offset != pg->hf_offset ){res++;printf("compare failed for pg->hf_offset \n");} if(other.pg->level != pg->level ){res++;printf("compare failed for pg->level \n");} if(other.pg->type != pg->type ){res++;printf("compare failed for pg->type \n");} int i,k; // double check header if(memcmp((void *)pg,(void *)other.pg,sizeof(PAGE)-sizeof(db_indx_t))) { res++; printf("compare failed in some unknown place in header:\n"); for(i=0;i<(int)(sizeof(PAGE)-sizeof(db_indx_t));i++) { printf("%3d: %3x %3x\n",i,((byte *)pg)[i],((byte *)other.pg)[i]); } } // pg->type != 5 && !=3 pages are not really compressed: just memcmp if(pg->type != 5 && pg->type != 3) { if(memcmp((void *)pg,(void *)other.pg,pgsz)) { printf("compare:PAGETYPE:!=5 and memcmp failed\n"); res++; printf("compare failed\n"); } return(res); } // compare each key/data pair for(i=0;i<(type==P_LBTREE ? pg->entries/2 : pg->entries);i++) { if(pg->type==P_LBTREE) { // compare keys if(key(i)->len !=other.key(i)->len ) { printf("compare:key(%2d) len : %2d != %2d\n",i,key(i)->len ,other.key(i)->len ); res++; } if(key(i)->type!=other.key(i)->type) { printf("compare:key(%2d) type: %2d != %2d\n",i,key(i)->type,other.key(i)->type); res++; } if(memcmp(key(i)->data,other.key(i)->data,key(i)->len)) { printf("compare :key(%2d)\n",i); for(k=0;klen;k++) { int c=key(i)->data[k]; if(isalnum(c)){printf(" %c ",c);} else{printf("%02x ",c);} } printf("\n"); for(k=0;klen;k++) { int c=other.key(i)->data[k]; if(isalnum(c)){printf(" %c ",c);} else{printf("%02x ",c);} } printf("\n"); res++;printf("compare:key failed\n"); } // compare data if(data(i)->len !=other.data(i)->len ) { printf("compare:data(%2d) len : %2d != %2d\n",i,data(i)->len ,other.data(i)->len ); res++; } if(data(i)->type!=other.data(i)->type) { printf("compare:data(%2d) type: %2d != %2d\n",i,data(i)->type,other.key(i)->type); res++; } if(memcmp(data(i)->data,other.data(i)->data,data(i)->len)) { printf("compare :data(%2d)\n",i); for(k=0;klen;k++) { printf("%02x ",data(i)->data[k]); } printf("\n"); for(k=0;klen;k++) { printf("%02x ",other.data(i)->data[k]); } printf("\n"); res++;printf("compare:data failed\n"); } } else { if(type!=3){errr("WordDBPage::Compare: unsupported type!=3");} if(btikey(i)->len != other.btikey(i)->len || btikey(i)->type != other.btikey(i)->type || btikey(i)->pgno != other.btikey(i)->pgno || btikey(i)->nrecs != other.btikey(i)->nrecs ) { printf("compare:btikey(%2d) failed\n",i); printf("this :len :%4d type :%4d pgno :%4d nrecs :%4d \n",btikey(i)->len,btikey(i)->type, btikey(i)->pgno,btikey(i)->nrecs); printf("other:len :%4d type :%4d pgno :%4d nrecs :%4d \n",other.btikey(i)->len,other.btikey(i)->type, other.btikey(i)->pgno,other.btikey(i)->nrecs); res++; } if(memcmp(btikey(i)->data,other.btikey(i)->data,btikey(i)->len)) { printf("compare :btikey(%2d)\n",i); for(k=0;klen;k++) { printf("%02x ",btikey(i)->data[k]); } printf("\n"); for(k=0;klen;k++) { printf("%02x ",other.btikey(i)->data[k]); } printf("\n"); res++;printf("compare:btikey failed\n"); } } } if(pg->entries>0) { int smallestoffset=HtMaxMin::min_v(pg->inp,pg->entries); int other_smallestoffset=HtMaxMin::min_v(other.pg->inp,other.pg->entries); if(smallestoffset!=other_smallestoffset) { printf("compare fail:smallestoffset:%d other_smallestoffset:%d\n",smallestoffset,other_smallestoffset); res++; } } return(res); } // Bit stream description // | field[last] changed only | yes -> delta field[last] // // redo=0 -> // redo=1 -> oops, dont show! // redo=2 -> void WordDBPage::show() { int i,j,dd,l; printf("************************************\n"); printf("************************************\n"); printf("************************************\n"); printf("page size:%d\n",(int)pgsz); printf(" 00-07: Log sequence number. file : %d\n", pg->lsn.file ); printf(" 00-07: Log sequence number. offset: %d\n", pg->lsn.offset ); printf(" 08-11: Current page number. : %d\n", pg->pgno ); printf(" 12-15: Previous page number. : %d\n", pg->prev_pgno ); printf(" 16-19: Next page number. : %d\n", pg->next_pgno ); printf(" 20-21: Number of item pairs on the page. : %d\n", pg->entries ); printf(" 22-23: High free byte page offset. : %d\n", pg->hf_offset ); printf(" 24: Btree tree level. : %d\n", pg->level ); printf(" 25: Page type. : %d\n", pg->type ); printf("entry offsets:"); for(i=0;ientries;i++){printf("%4d ",pg->inp[i]);} printf("\n"); if(pg->type ==5) { WordRecord dud; WordKey prev; int pagecl=0; for(i=0;ientries;i++) { if( (i%2) && dud.type==WORD_RECORD_NONE){continue;} printf("\n||%c:%3d:off:%03d:invoff:%4d:len:%2d:typ:%x:",i%2 ? 'D' : 'K',i,e_offset(i),pgsz-e_offset(i),entry(i)->len,entry(i)->type); if(i>0) { l=entry(i)->len+3; dd=(int)(e_offset(i-1))-l; dd-=dd%4; printf("% 5d:: ",(e_offset(i)-dd)); } if(!(i%2)) { WordDBKey tkey(entry(i)); int fieldchanged[10]; char *wordchange=NULL; printf("\""); printf("%s",(char *)tkey.GetWord()); printf("\""); for(j=0;j<20-tkey.GetWord().length();j++){printf(" ");} printf("|"); for(j=1;jsort[j].bits;} } if(fieldchanged[0]){keycl+=3;keycl+=8*strlen(wordchange);} printf(" ::%2d %f",keycl,keycl/8.0); pagecl+=keycl; prev=tkey; } else { if(entry(i)->len>100){printf("WordDBPage::show: aaargh strange failing\n");return;} for(j=0;jlen;j++) { printf("%02x ",entry(i)->data[j]); } } } printf("\n"); } else if(1) { int nn=0; // dump hex for(i=0;;i++) { printf("%5d: ",nn); for(j=0;j<20;j++) { printf("%2x ",((byte *)pg)[nn++]); if(nn>=pgsz){break;} } printf("\n"); if(nn>=pgsz){break;} } } if(pg->type == 3) { for(i=0;ientries;i++) { BINTERNAL *bie=GET_BINTERNAL(pg,i); printf("%3d: off:%4d:len:%3d :type:%3d :pgno:%4d: nrecs:%4d:: ",i,pg->inp[i],bie->len,bie->type,bie->pgno,bie->nrecs); WordDBKey tkey(bie); for(j=0;jlen-tkey.GetWord().length();j++){printf("%2x ",bie->data[j]);} printf(" : "); for(j=1;j