--- parser3/src/main/pa_string.C 2003/01/24 14:36:11 1.172.2.5 +++ parser3/src/main/pa_string.C 2003/03/17 14:48:06 1.172.4.1 @@ -5,46 +5,45 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char* IDENT_STRING_C="$Date: 2003/01/24 14:36:11 $"; +static const char* IDENT_STRING_C="$Date: 2003/03/17 14:48:06 $"; #include "pcre.h" #include "pa_pool.h" #include "pa_string.h" +#include "pa_hash.h" #include "pa_exception.h" +#include "pa_common.h" #include "pa_array.h" +#include "pa_globals.h" #include "pa_table.h" #include "pa_dictionary.h" #include "pa_charset.h" -// helpers +#define DEBUG_STRING_APPENDS_VS_EXPANDS -/// String::match uses this as replace & global search table columns -const int MAX_MATCH_GROUPS=100; - -class String_match_table_template_columns: public Array { - Pool pool; -public: - String_match_table_template_columns() { - *this+=ConstStringPtr(new String("prematch")); - *this+=ConstStringPtr(new String("match")); - *this+=ConstStringPtr(new String("postmatch")); - for(int i=0; i(1), fsize(0) { if(src) if(tainted) APPEND_TAINTED(src, src_size, 0, 0); @@ -52,11 +51,55 @@ String::String(const char *src, size_t s APPEND_CLEAN(src, src_size, 0, 0); } -String::String(const String& src): Array(src.count()) { +String::String(const String& src) : + Pooled(src.pool()) { + last_chunk=&head.chunk; + head.chunk.count=CR_PREALLOCATED_COUNT; + append_here=head.chunk.rows; + append(src, UL_PASS_APPENDED); } +size_t String::size() const { + size_t result=0; + STRING_FOREACH_ROW( + result+=row->item.size; + ); + return result; +} + +/// @todo not very optimal +uint String::used_rows() const { + uint result=0; + STRING_FOREACH_ROW( + result++; + ); + return result; +} +void String::expand() { + uint new_chunk_count=last_chunk->count+CR_GROW_COUNT; + if(new_chunk_count>max_integral(Chunk::count_type)) + new_chunk_count=max_integral(Chunk::count_type); + + Chunk *new_chunk=static_cast(malloc( + sizeof(Chunk)// count+interpadding(?)+rows[CR_PREALLOCATED_COUNT]+tailpadding(??) + -sizeof(Chunk::rows_type) // PREALLOCATED rows + +sizeof(Chunk::Row)*new_chunk_count // neaded rows + +sizeof(Chunk *) // link size + , 10)); + new_chunk->rows[new_chunk->count=new_chunk_count].link=0; + last_chunk->rows[last_chunk->count].link=new_chunk; + + last_chunk=new_chunk; + append_here=last_chunk->rows; +} + String& String::real_append(STRING_APPEND_PARAMS) { + if(!last_chunk) // growth stopped [we're appended as string to somebody] + throw Exception(0, + this, + "string growth stopped (append cstr)"); + if(!src) return *this; if(!size) @@ -64,25 +107,47 @@ String& String::real_append(STRING_APPEN if(!size) return *this; - if(is_full()) - expand(fdelta); +#ifdef DEBUG_STRING_APPENDS_VS_EXPANDS + string_piece_appends++; +#endif - String_fragment *fragment=&felements[fused++]; - fragment->ptr=src; - fragment->size=size; - fragment->lang=lang; + // manually unrolled to avoid extra check + while(size>max_integral(Chunk::Row::item_size_type)) { + if(chunk_is_full()) + expand(); + + append_here->item.ptr=src; + append_here->item.size=max_integral(Chunk::Row::item_size_type); + append_here->item.lang=lang; #ifndef NO_STRING_ORIGIN - fragment->origin.file=file; - fragment->origin.line=line; + append_here->item.origin.file=file; + append_here->item.origin.line=line; #endif + append_here++; + + src+=max_integral(Chunk::Row::item_size_type); + size-=max_integral(Chunk::Row::item_size_type); + } + + if(chunk_is_full()) + expand(); + + append_here->item.ptr=src; + append_here->item.size=size; + append_here->item.lang=lang; +#ifndef NO_STRING_ORIGIN + append_here->item.origin.file=file; + append_here->item.origin.line=line; +#endif + append_here++; return *this; } uint String::hash_code() const { uint result=0; - STRING_FOREACH_FRAGMENT( - result=generic_hash_code(result, fragment->ptr, fragment->size); + STRING_FOREACH_ROW( + result=Hash::generic_code(result, row->item.ptr, row->item.size); ); return result; } @@ -94,56 +159,77 @@ int String::cmp(int& partial, const Stri size_t a_size=size(); this_offset=min(this_offset, a_size-1); - const String_fragment *a_current=felements; - const String_fragment *b_current=src.felements; + const Chunk *a_chunk=&head.chunk; + const Chunk *b_chunk=&src.head.chunk; + const Chunk::Row *a_row=a_chunk->rows; + const Chunk::Row *b_row=b_chunk->rows; size_t a_offset=this_offset; size_t b_offset=0; - String_fragment *a_end=felements+fused; - String_fragment *b_end=src.felements+src.fused; + Chunk::Row *a_end=append_here; + Chunk::Row *b_end=src.append_here; + uint a_countdown=a_chunk->count; + uint b_countdown=b_chunk->count; int result; size_t pos=0; - bool a_break; - bool b_break; - while(!( - (a_break=(a_current==a_end)) - || (b_break=(b_current==b_end)))) { - if(pos+a_current->size > this_offset) { - if(lang!=UL_UNSPECIFIED && a_current->lang>lang) + bool a_break=a_size==0; + bool b_break=src.is_empty(); + if(!(a_break || b_break)) while(true) { + if(pos+a_row->item.size > this_offset) { + if(lang!=UL_UNSPECIFIED && a_row->item.lang>lang) return -1; // wrong lang -- bail out int size_diff= - (a_current->size-a_offset)- - (b_current->size-b_offset); + (a_row->item.size-a_offset)- + (b_row->item.size-b_offset); if(size_diff==0) { // a has same size as b - result=memcmp(a_current->ptr+a_offset, b_current->ptr+b_offset, - a_current->size-a_offset); + result=memcmp(a_row->item.ptr+a_offset, b_row->item.ptr+b_offset, + a_row->item.size-a_offset); if(result) return result; - pos+=a_current->size; - a_current++; a_offset=0; - b_current++; b_offset=0; - } else if(size_diff>0) { // a longer - result=memcmp(a_current->ptr+a_offset, b_current->ptr+b_offset, - b_current->size-b_offset); + pos+=a_row->item.size; + a_row++; a_countdown--; a_offset=0; + b_row++; b_countdown--; b_offset=0; + } else if (size_diff>0) { // a longer + result=memcmp(a_row->item.ptr+a_offset, b_row->item.ptr+b_offset, + b_row->item.size-b_offset); if(result) return result; - a_offset+=b_current->size-b_offset; - b_current++; b_offset=0; + a_offset+=b_row->item.size-b_offset; + b_row++; b_countdown--; b_offset=0; } else { // b longer - result=memcmp(a_current->ptr+a_offset, b_current->ptr+b_offset, - a_current->size-a_offset); + result=memcmp(a_row->item.ptr+a_offset, b_row->item.ptr+b_offset, + a_row->item.size-a_offset); if(result) return result; - b_offset+=a_current->size-a_offset; - pos+=a_current->size; - a_current++; a_offset=0; + b_offset+=a_row->item.size-a_offset; + pos+=a_row->item.size; + a_row++; a_countdown--; a_offset=0; + } + if(b_break=b_row==b_end) { + a_break=a_row==a_end; + break; + } + if(!b_countdown) { + b_chunk=b_row->link; + b_row=b_chunk->rows; + b_countdown=b_chunk->count; } } else { - a_offset-=a_current->size; - pos+=a_current->size; - a_current++; + a_offset-=a_row->item.size; + pos+=a_row->item.size; + a_row++; a_countdown--; + } + + if(a_break=a_row==a_end) { + b_break=b_row==b_end; + break; + } + if(!a_countdown) { + a_chunk=a_row->link; + a_row=a_chunk->rows; + a_countdown=a_chunk->count; } } if(a_break==b_break) { // ended simultaneously @@ -163,50 +249,59 @@ int String::cmp(int& partial, const char size_t b_size=src_size?src_size:b_ptr?strlen(b_ptr):0; this_offset=min(this_offset, a_size-1); - const String_fragment *a_current=felements; + const Chunk *a_chunk=&head.chunk; + const Chunk::Row *a_row=a_chunk->rows; size_t a_offset=this_offset; size_t b_offset=0; - String_fragment *a_end=felements+fused; + Chunk::Row *a_end=append_here; + uint a_countdown=a_chunk->count; size_t pos=0; - bool a_break; + bool a_break=a_size==0; bool b_break=b_size==0; - while(!( - (a_break=(a_current==a_end)) - || b_break)) { - if(pos+a_current->size > this_offset) { - if(lang!=UL_UNSPECIFIED && a_current->lang>lang) + if(!(a_break || b_break)) while(true) { + if(pos+a_row->item.size > this_offset) { + if(lang!=UL_UNSPECIFIED && a_row->item.lang>lang) return -1; // wrong lang -- bail out int size_diff= - (a_current->size-a_offset)- + (a_row->item.size-a_offset)- (b_size-b_offset); if(size_diff==0) { // a has same size as b - if(int result=memcmp(a_current->ptr+a_offset, b_ptr+b_offset, - a_current->size-a_offset)!=0) + if(int result=memcmp(a_row->item.ptr+a_offset, b_ptr+b_offset, + a_row->item.size-a_offset)!=0) return result; - pos+=a_current->size; - a_current++; a_offset=0; + pos+=a_row->item.size; + a_row++; a_countdown--; a_offset=0; b_break=true; } else if (size_diff>0) { // a longer - if(int result=memcmp(a_current->ptr+a_offset, b_ptr+b_offset, + if(int result=memcmp(a_row->item.ptr+a_offset, b_ptr+b_offset, b_size-b_offset)!=0) return result; a_offset+=b_size-b_offset; b_break=true; } else { // b longer - if(int result=memcmp(a_current->ptr+a_offset, b_ptr+b_offset, - a_current->size-a_offset)!=0) + if(int result=memcmp(a_row->item.ptr+a_offset, b_ptr+b_offset, + a_row->item.size-a_offset)!=0) return result; - b_offset+=a_current->size-a_offset; - pos+=a_current->size; - a_current++; a_offset=0; + b_offset+=a_row->item.size-a_offset; + pos+=a_row->item.size; + a_row++; a_countdown--; a_offset=0; } } else { - a_offset-=a_current->size; - pos+=a_current->size; - a_current++; + a_offset-=a_row->item.size; + pos+=a_row->item.size; + a_row++; a_countdown--; + } + + a_break=a_row==a_end; + if(a_break || b_break) + break; + if(!a_countdown) { + a_chunk=a_row->link; + a_row=a_chunk->rows; + a_countdown=a_chunk->count; } } if(a_break==b_break) { // ended simultaneously @@ -219,9 +314,9 @@ int String::cmp(int& partial, const char } #ifndef NO_STRING_ORIGIN -const String_fragment::Origin& String::origin() const { +const Origin& String::origin() const { if(is_empty()) { - static const String_fragment::Origin empty_origin={"empty string"}; + static const Origin empty_origin={"empty string"}; return empty_origin; } @@ -231,13 +326,13 @@ const String_fragment::Origin& String::o // when last peice is constant, // ex: parser_root_auto_path{dynamic} / auto.p{const} // using first piece - String_fragment::Origin& first_origin=felements[0].origin; - return first_origin.file ? first_origin : felements[fused-1].origin; + Origin& first_origin=head.chunk.rows[0].item.origin; + return first_origin.file ? first_origin : append_here[-1].item.origin; } #endif -StringPtr String::mid(size_t start, size_t finish) const { - StringPtr result(new String()); +String& String::mid(size_t start, size_t finish) const { + String& result=*NEW String(pool()); start=min(start, size()); finish=max(start, finish); @@ -245,21 +340,21 @@ StringPtr String::mid(size_t start, size return result; size_t pos=0; - STRING_FOREACH_FRAGMENT( - size_t item_finish=pos+fragment->size; + STRING_FOREACH_ROW( + size_t item_finish=pos+row->item.size; if(item_finish > start) { // started now or already? - bool started=result->is_empty(); // started now? + bool started=result.is_empty(); // started now? bool finished=finish <= item_finish; // finished now? size_t offset=started?start-pos:0; - size_t size=finished?finish-pos:fragment->size; - result->APPEND( - fragment->ptr+offset, size-offset, - fragment->lang, - fragment->origin.file, fragment->origin.line); + size_t size=finished?finish-pos:row->item.size; + result.APPEND( + row->item.ptr+offset, size-offset, + row->item.lang, + row->item.origin.file, row->item.origin.line); if(finished) goto break2; } - pos+=fragment->size; + pos+=row->item.size; ); break2: // SAPI::log(pool(), "piece of '%s' from %d to %d is '%s'", @@ -295,7 +390,7 @@ int String::pos(const char *substr, size return -1; } -void String::split(Array& result, +void String::split(Array& result, size_t* pos_after_ref, const char *delim, size_t delim_size, Untaint_lang lang, int limit) const { @@ -305,24 +400,24 @@ void String::split(Array int pos_before; // while we have 'delim'... for(; (pos_before=pos(delim, delim_size, pos_after, lang))>=0 && limit; limit--) { - result+=mid(pos_after, pos_before); + result+=&mid(pos_after, pos_before); pos_after=pos_before+delim_size; } // last piece if(pos_after& result, +void String::split(Array& result, size_t* pos_after_ref, const String& delim, Untaint_lang lang, int limit) const { @@ -331,24 +426,24 @@ void String::split(Array int pos_before; // while we have 'delim'... for(; (pos_before=pos(delim, pos_after, lang))>=0 && limit; limit--) { - result+=mid(pos_after, pos_before); + result+=&mid(pos_after, pos_before); pos_after=pos_before+delim.size(); } // last piece if(pos_after > row; - if(need_pre_post_match) { - *row+=mid(0, prefinish); // .prematch column value - *row+=mid(prefinish, poststart); // .match - *row+=mid(poststart, postfinish); // .postmatch - } else { - *row+=ConstStringPtrZero; // .prematch column value - *row+=ConstStringPtrZero; // .match - *row+=ConstStringPtrZero; // .postmatch - } + Array& row=*NEW Array(pool()); + row+=need_pre_post_match?&mid(0, prefinish):0; // .prematch column value + row+=need_pre_post_match?&mid(prefinish, poststart):0; // .match + row+=need_pre_post_match?&mid(poststart, postfinish):0; // .postmatch for(int i=1; i(kind)); // never a=b=0; // calm, compiler break; // never } - STRING_FOREACH_FRAGMENT( - char *new_cstr=new(pool) char[fragment->size]; + STRING_FOREACH_ROW( + char *new_cstr=(char *)pool.malloc_atomic(row->item.size, 12); char *dest=new_cstr; - const char *end=fragment->ptr+fragment->size; - for(const char *current=fragment->ptr; currentitem.ptr; + for(int size=row->item.size; size--; src++) { + unsigned char c=a[(unsigned char)*src]; if(b) c=b[c]; *dest++=(char)c; } - result->APPEND(new_cstr, fragment->size, - fragment->lang, - fragment->origin.file, fragment->origin.line); + result.APPEND(new_cstr, row->item.size, + row->item.lang, + row->item.origin.file, row->item.origin.line); ); return result; } /// @test if in some piece were found no dict words, append it, not it's duplicate -StringPtr String::replace(Pool& pool, Dictionary& dict) const { - StringPtr result(new String()); - CharPtr lcstr(cstr()); - const char *current=lcstr.get(); +String& String::replace(Pool& pool, Dictionary& dict) const { + char *lcstr=cstr(); + const char *current=lcstr; - STRING_FOREACH_FRAGMENT( + String& result=*new(pool) String(pool); + STRING_FOREACH_ROW( IFNDEF_NO_STRING_ORIGIN( - const char *joined_origin_file=fragment->origin.file; - const size_t joined_origin_line=fragment->origin.line; + const char *joined_origin_file=row->item.origin.file; + const size_t joined_origin_line=row->item.origin.line; ); - uchar joined_lang=fragment->lang; + uchar joined_lang=row->item.lang; const char *joined_ptr=current; // calc size size_t joined_size=0; - STRING_PREPARED_FOREACH_FRAGMENT( - if(fragment->lang==joined_lang) - joined_size+=fragment->size; + STRING_PREPARED_FOREACH_ROW(*this, + if(row->item.lang==joined_lang) + joined_size+=row->item.size; else break; // before non-ours ); current+=joined_size; // pointers are after joined piece - // & one step back, see STRING_PREPARED_FOREACH_FRAGMENT - --fragment; + // & one step back, see STRING_FOREACH_ROW + --row; ++countdown; - char *new_cstr=new(pool) char[(size_t)ceil(joined_size*dict.max_ratio())]; + char *new_cstr=(char *)pool.malloc_atomic((size_t)ceil(joined_size*dict.max_ratio()), 14); char *dest=new_cstr; while(joined_size) { - // there is a fragment where first column starts 'joined_ptr' - if(Table::element_type row=dict.first_that_starts(joined_ptr, joined_size)) { + // there is a row where first column starts 'joined_ptr' + if(Table::Item *item=dict.first_that_starts(joined_ptr, joined_size)) { // get a=>b values - ConstStringPtr a=row->get(0); - ConstStringPtr b=row->get(1); + const String& a=*static_cast(item)->get_string(0); + const String& b=*static_cast(item)->get_string(1); // skip 'a' in 'joined_ptr' && reduce work size - joined_ptr+=a->size(); joined_size-=a->size(); + joined_ptr+=a.size(); joined_size-=a.size(); // write 'b' to 'dest' && skip 'b' in 'dest' - b->store_to(dest, String::UL_AS_IS); dest+=b->size(); + b.store_to(dest); dest+=b.size(); } else { // write a char to b && reduce work size *dest++=*joined_ptr++; joined_size--; } } - result->APPEND(new_cstr, dest-new_cstr, joined_lang, + result.APPEND(new_cstr, dest-new_cstr, joined_lang, joined_origin_file, joined_origin_line); ); return result; } -StringPtr String::join_chains(Pool& pool) const { - StringPtr result(new String()); - - char *pooled_buf=new(pool) char[cstr_bufsize()]; - store_to(pooled_buf, String::UL_AS_IS); - const char *current=pooled_buf; +String& String::join_chains(Pool& pool, char** acstr) const { + char *lcstr=cstr(); + const char *current=lcstr; - STRING_FOREACH_FRAGMENT( + String& result=*new(pool) String(pool); + STRING_FOREACH_ROW( IFNDEF_NO_STRING_ORIGIN( - const char *joined_origin_file=fragment->origin.file; - const size_t joined_origin_line=fragment->origin.line; + const char *joined_origin_file=row->item.origin.file; + const size_t joined_origin_line=row->item.origin.line; ); - uchar joined_lang=fragment->lang; + uchar joined_lang=row->item.lang; const char *joined_ptr=current; // calc size size_t joined_size=0; - STRING_PREPARED_FOREACH_FRAGMENT( - if(fragment->lang==joined_lang) - joined_size+=fragment->size; + STRING_PREPARED_FOREACH_ROW(*this, + if(row->item.lang==joined_lang) + joined_size+=row->item.size; else break; // before non-ours ); current+=joined_size; // pointers are after joined piece - // & one step back, see STRING_PREPARED_FOREACH_FRAGMENT - --fragment; + // & one step back, see STRING_FOREACH_ROW + --row; ++countdown; - result->APPEND(joined_ptr, joined_size, joined_lang, + result.APPEND(joined_ptr, joined_size, joined_lang, joined_origin_file, joined_origin_line); ); + if(acstr) + *acstr=lcstr; return result; } double String::as_double() const { double result; - char buf[MAX_STRING]; - if(size()>MAX_STRING-1) - throw Exception("number.format", - ConstStringPtr(this), - "invalid number too long a string (%u>%u)", size(), MAX_STRING-1); - char *eol=store_to(buf, String::UL_AS_IS); *eol=0; - const char *cstr=buf; - + const char *cstr; + char buf[MAX_NUMBER]; + if(head.chunk.rows+1==append_here) { + int size=min(head.chunk.rows[0].item.size, MAX_NUMBER-1); + memcpy(buf, head.chunk.rows[0].item.ptr, size); + buf[size]=0; + cstr=buf; + } else + cstr=this->cstr(); while(*cstr && isspace(*cstr)) cstr++; if(!*cstr) @@ -645,21 +734,22 @@ double String::as_double() const { while(char c=*error_pos++) if(!isspace(c)) throw Exception("number.format", - ConstStringPtr(this), + this, "invalid number (double)"); return result; } int String::as_int() const { int result; - char buf[MAX_STRING]; - if(size()>MAX_STRING-1) - throw Exception("number.format", - ConstStringPtr(this), - "invalid number too long a string (%u>%u)", size(), MAX_STRING-1); - char *eol=store_to(buf, String::UL_AS_IS); *eol=0; - const char *cstr=buf; - + const char *cstr; + char buf[MAX_NUMBER]; + if(head.chunk.rows+1==append_here) { + size_t size=min(head.chunk.rows[0].item.size, MAX_NUMBER-1); + memcpy(buf, head.chunk.rows[0].item.ptr, size); + buf[size]=0; + cstr=buf; + } else + cstr=this->cstr(); while(*cstr && isspace(*cstr)) cstr++; if(!*cstr) @@ -678,46 +768,44 @@ int String::as_int() const { while(char c=*error_pos++) if(!isspace(c)) throw Exception("number.format", - ConstStringPtr(this), + this, "invalid number (int)"); return result; } -inline void uint2uchars(uint word, uchar *bytes) { - bytes[0]=word&0xFF; - bytes[1]=(word>>8)&0xFF; - bytes[2]=(word>>16)&0xFF; - bytes[3]=(word>>24)&0xFF; -} -inline uint uchars2uint(uchar *bytes) { - return bytes[3]<<24 - | bytes[2]<<16 - | bytes[1]<<8 - | bytes[0]; -} - -void String::serialize(Pool& pool, size_t prolog_size, char *& buf, size_t& buf_size) const { +inline void ushort2uchars(ushort word, uchar& byte1, uchar& byte2) { + byte1=word&0xFF; + byte2=word>>8; +} +inline ushort uchars2ushort(uchar byte1, uchar byte2) { + return (byte2<<8) | byte1; +} +/* @todo maybe network order worth spending some effort? + don't bothering myself with network byte order, + am not planning to be able to move resulting file across platforms + for now +*/ +void String::serialize(size_t prolog_size, void *& buf, size_t& buf_size) const { buf_size= prolog_size - +fused*(sizeof(uchar)+sizeof(size_t)) + +used_rows()*(sizeof(uchar)+sizeof(ushort)) +size(); - buf=new(pool) char[buf_size]; - char *cur=buf+prolog_size; + buf=malloc_atomic(buf_size,15); + char *cur=(char *)buf+prolog_size; - STRING_FOREACH_FRAGMENT( + STRING_FOREACH_ROW( // lang - memcpy(cur, &fragment->lang, sizeof(fragment->lang)); - cur+=sizeof(fragment->lang); + memcpy(cur, &row->item.lang, sizeof(uchar)); + cur+=sizeof(uchar); // size - // bug on some sparc platform [you can't work with integers on odd pointers] - // forces us to use byte array instead - uchar bytes[4]; - uint2uchars(fragment->size, bytes); - memcpy(cur, &bytes, sizeof(bytes)); cur+=sizeof(bytes); + uchar byte1; uchar byte2; + ushort2uchars(row->item.size, byte1, byte2); + memcpy(cur, &byte1, sizeof(uchar)); cur+=sizeof(uchar); + memcpy(cur, &byte2, sizeof(uchar)); cur+=sizeof(uchar); // bytes - memcpy(cur, fragment->ptr, fragment->size); - cur+=fragment->size; + memcpy(cur, row->item.ptr, row->item.size); + cur+=row->item.size; ); } bool String::deserialize(size_t prolog_size, void *buf, size_t buf_size, const char *file) { @@ -728,17 +816,20 @@ bool String::deserialize(size_t prolog_s buf_size-=prolog_size; while(buf_size) { - if(sizeof(uchar)+sizeof(size_t)>buf_size) // lang+size + if(sizeof(uchar)+sizeof(ushort)>buf_size) // lang+size return false; uchar lang=*(uchar *)(cur); - size_t size=uchars2uint((uchar *)cur); + ushort size=uchars2ushort( + *(uchar*)(cur+sizeof(uchar)*1), + *(uchar*)(cur+sizeof(uchar)*2) + ); - size_t piece_size=sizeof(uchar)+sizeof(size_t)+size; + size_t piece_size=sizeof(uchar)+sizeof(ushort)+size; if(piece_size>buf_size) // buffer overrun, can be on incomplete cache files return false; - const char *ptr=(const char*)(cur+sizeof(uchar)+sizeof(size_t)); + const char *ptr=(const char*)(cur+sizeof(uchar)*3); APPEND(ptr, size, lang, file, 0); cur+=piece_size;