Diff for /parser3/src/main/pa_string.C between versions 1.116 and 1.149

version 1.116, 2001/10/29 16:29:08 version 1.149, 2002/03/27 15:30:37
Line 1 Line 1
 /** @file  /** @file
         Parser: string class. @see untasize_t.C.          Parser: string class. @see untasize_t.C.
   
         Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com)
         Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         $Id$          $Id$
 */  */
   
 #include "pa_config_includes.h"  
   
 #include "pcre.h"  #include "pcre.h"
 #include "internal.h"  
   
 #include "pa_pool.h"  #include "pa_pool.h"
 #include "pa_string.h"  #include "pa_string.h"
Line 21 Line 18
 #include "pa_globals.h"  #include "pa_globals.h"
 #include "pa_table.h"  #include "pa_table.h"
 #include "pa_dictionary.h"  #include "pa_dictionary.h"
   #include "pa_charset.h"
   
   #define DEBUG_STRING_APPENDS_VS_EXPANDS
   
   
   #ifdef DEBUG_STRING_APPENDS_VS_EXPANDS
   ulong string_piece_appends=0;
   #endif
   
 String::String(Pool& apool, const char *src, size_t src_size, bool tainted) :  String::String(Pool& apool, const char *src, size_t src_size, bool tainted) :
         Pooled(apool),          Pooled(apool) {
         forigins_mode(false) {  
         last_chunk=&head;          last_chunk=&head;
         head.count=CR_PREALLOCATED_COUNT;          head.count=CR_PREALLOCATED_COUNT;
         append_here=head.rows;          append_here=head.rows;
         head.preallocated_link=0;  
         link_row=&head.rows[head.count];  
         fsize=0;  
   
         if(src)          if(src)
                 if(tainted)                  if(tainted)
Line 40  String::String(Pool& apool, const char * Line 41  String::String(Pool& apool, const char *
 }  }
   
 String::String(const String& src) :       String::String(const String& src) :     
         Pooled(src.pool()),          Pooled(src.pool()) {
         forigins_mode(false) {          last_chunk=&head;
         head.count=CR_PREALLOCATED_COUNT;          head.count=CR_PREALLOCATED_COUNT;
                   append_here=head.rows;
         uint src_used_rows=src.used_rows();  
         if(src_used_rows<=head.count) {  
                 // all new rows fit size_to preallocated area  
                 last_chunk=&head;  
                 uint curr_chunk_rows=head.count;  
                 memcpy(head.rows, src.head.rows, sizeof(Chunk::Row)*src_used_rows);  
                 append_here=&head.rows[src_used_rows];  
                 link_row=&head.rows[curr_chunk_rows];  
         } else {  
                 // warning:   
                 //   heavily relies on the fact   
                 //   "preallocated area is the same for all strings"  
                 //  
                 // info:  
                 //   allocating only enough mem to fit src string rows  
                 //   next append would allocate a new chunk  
                 //  
                 // new rows don't fit size_to preallocated area: splitting size_to two chunks  
                 // preallocated chunk src to constructing head  
                 memcpy(head.rows, src.head.rows, sizeof(Chunk::Row)*head.count);  
                 // remaining rows size_to new_chunk  
                 uint curr_chunk_rows=src_used_rows-head.count;  
                 last_chunk=static_cast<Chunk *>(  
                         malloc(sizeof(uint)+sizeof(Chunk::Row)*curr_chunk_rows+sizeof(Chunk *), 9));  
                 last_chunk->count=curr_chunk_rows;  
                 head.preallocated_link=last_chunk;  
                 append_here=link_row=&last_chunk->rows[last_chunk->count];  
   
                 Chunk *old_chunk=src.head.preallocated_link;   
                 Chunk::Row *new_rows=last_chunk->rows;  
                 uint rows_left_to_copy=last_chunk->count;  
                 while(true) {  
                         uint old_count=old_chunk->count;  
                         Chunk *next_chunk=old_chunk->rows[old_count].link;  
                         if(next_chunk) {  
                                 // not last source chunk  
                                 // taking it all  
                                 memcpy(new_rows, old_chunk->rows, sizeof(Chunk::Row)*old_count);  
                                 new_rows+=old_count;  
                                 rows_left_to_copy-=old_count;  
   
                                 old_chunk=next_chunk;          append(src, UL_UNSPECIFIED);
                         } else {  }
                                 // the last source chunk  
                                 // taking only those rows of chunk that _left_to_copy  size_t  String::size() const {
                                 memcpy(new_rows, old_chunk->rows, sizeof(Chunk::Row)*rows_left_to_copy);          size_t result=0;
                                 break;          STRING_FOREACH_ROW(
                         }                          result+=row->item.size;
                 }          );
         }          return result;
         link_row->link=0;  
         src_used_rows;  
         fsize=src.fsize;  
 }  }
   
 /// @todo not very optimal  /// @todo not very optimal
 uint String::used_rows() const {  uint String::used_rows() const {
         uint result=0;          uint result=0;
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {                  result++;
                 const Chunk::Row *row=chunk->rows;          );
                 for(uint i=0; i<chunk->count; i++, row++) {  
                         if(row==append_here)  
                                 goto break2;  
   
                         result++;  
                 }  
                 chunk=row->link;  
         } while(chunk);  
   
 break2:  
         return result;          return result;
 }  }
 void String::expand() {  void String::expand() {
         uint new_chunk_count=last_chunk->count+CR_GROW_COUNT;          uint new_chunk_count=last_chunk->count+CR_GROW_COUNT;
         last_chunk=static_cast<Chunk *>(          if(new_chunk_count>max_integral(Chunk::count_type))
                 malloc(sizeof(uint)+sizeof(Chunk::Row)*new_chunk_count+sizeof(Chunk *), 10));                  new_chunk_count=max_integral(Chunk::count_type);
         last_chunk->count=new_chunk_count;  
         link_row->link=last_chunk;  
         append_here=last_chunk->rows;  
         link_row=&last_chunk->rows[last_chunk->count];  
         link_row->link=0;  
 }  
   
 String& String::append(const String& src, Untaint_lang lang, bool forced) {          Chunk *new_chunk=static_cast<Chunk *>(
         const Chunk *chunk=&src.head;                   malloc(sizeof(Chunk::count_type)+sizeof(Chunk::Row)*new_chunk_count+sizeof(Chunk *), 10));
         do {          new_chunk->rows[new_chunk->count=new_chunk_count].link=0;
                 const Chunk::Row *row=chunk->rows;          last_chunk->rows[last_chunk->count].link=new_chunk;
                 for(uint i=0; i<chunk->count; i++, row++) {          
                         if(row==src.append_here)          last_chunk=new_chunk;
                                 goto break2;          append_here=last_chunk->rows;
                           
                         APPEND(row->item.ptr, row->item.size,   
                                 (lang!=UL_PASS_APPENDED && (row->item.lang==UL_TAINTED || forced))?lang:row->item.lang,  
                                 row->item.origin.file, row->item.origin.line);  
                 }  
                 chunk=row->link;  
         } while(chunk);  
 break2:  
         return *this;  
 }  }
   
 String& String::real_append(STRING_APPEND_PARAMS) {  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)          if(!src)
                 return *this;                  return *this;
         if(!size)          if(!size)
Line 154  String& String::real_append(STRING_APPEN Line 92  String& String::real_append(STRING_APPEN
         if(!size)          if(!size)
                 return *this;                  return *this;
   
   #ifdef DEBUG_STRING_APPENDS_VS_EXPANDS
           string_piece_appends++;
   #endif
   
           // 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
                   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())          if(chunk_is_full())
                 expand();                  expand();
   
         append_here->item.ptr=src;          append_here->item.ptr=src;
         fsize+=append_here->item.size=size;          append_here->item.size=size;
         append_here->item.lang=lang;          append_here->item.lang=lang;
 #ifndef NO_STRING_ORIGIN  #ifndef NO_STRING_ORIGIN
         append_here->item.origin.file=file;          append_here->item.origin.file=file;
Line 170  String& String::real_append(STRING_APPEN Line 130  String& String::real_append(STRING_APPEN
 }  }
   
 char String::first_char() const {  char String::first_char() const {
         if(!fsize)          if(is_empty())
                 throw Exception(0, 0,                  throw Exception(0,
                         this,                          this,
                         "getting first char of empty string");                          "getting first char of empty string");
   
Line 180  char String::first_char() const { Line 140  char String::first_char() const {
   
 uint String::hash_code() const {  uint String::hash_code() const {
         uint result=0;          uint result=0;
           STRING_FOREACH_ROW(
         const Chunk *chunk=&head;   
         do {  
                 const Chunk::Row *row=chunk->rows;  
                 for(uint i=0; i<chunk->count; i++) {  
                         if(row==append_here)  
                                 goto break2;  
   
                         result=Hash::generic_code(result, row->item.ptr, row->item.size);                          result=Hash::generic_code(result, row->item.ptr, row->item.size);
                         row++;          );
                 }  
                 chunk=row->link;  
         } while(chunk);  
 break2:  
         return result;          return result;
 }  }
   
Line 201  break2: Line 150  break2:
 int String::cmp(int& partial, const String& src,   int String::cmp(int& partial, const String& src, 
                                 size_t this_offset, Untaint_lang lang) const {                                  size_t this_offset, Untaint_lang lang) const {
         partial=-1;          partial=-1;
         this_offset=min(this_offset, size()-1);          size_t a_size=size();
           this_offset=min(this_offset, a_size-1);
   
         const Chunk *a_chunk=&head;          const Chunk *a_chunk=&head;
         const Chunk *b_chunk=&src.head;          const Chunk *b_chunk=&src.head;
Line 216  int String::cmp(int& partial, const Stri Line 166  int String::cmp(int& partial, const Stri
         int result;          int result;
         size_t pos=0;           size_t pos=0; 
   
         bool a_break=size()==0;          bool a_break=a_size==0;
         bool b_break=src.size()==0;          bool b_break=src.is_empty();
         if(!(a_break || b_break)) while(true) {          if(!(a_break || b_break)) while(true) {
                 if(pos+a_row->item.size > this_offset) {                  if(pos+a_row->item.size > this_offset) {
                         if(lang!=UL_UNSPECIFIED && a_row->item.lang!=lang)                           if(lang!=UL_UNSPECIFIED && a_row->item.lang>lang) 
                                 return -1; // wrong lang -- bail out                                  return -1; // wrong lang -- bail out
   
                         int size_diff=                          int size_diff=
Line 289  int String::cmp(int& partial, const Stri Line 239  int String::cmp(int& partial, const Stri
 int String::cmp(int& partial, const char* b_ptr, size_t src_size,   int String::cmp(int& partial, const char* b_ptr, size_t src_size, 
                                 size_t this_offset, Untaint_lang lang) const {                                  size_t this_offset, Untaint_lang lang) const {
         partial=-1;          partial=-1;
           size_t a_size=size();
         size_t b_size=src_size?src_size:b_ptr?strlen(b_ptr):0;          size_t b_size=src_size?src_size:b_ptr?strlen(b_ptr):0;
         this_offset=min(this_offset, size()-1);          this_offset=min(this_offset, a_size-1);
   
         const Chunk *a_chunk=&head;          const Chunk *a_chunk=&head;
         const Chunk::Row *a_row=a_chunk->rows;          const Chunk::Row *a_row=a_chunk->rows;
Line 300  int String::cmp(int& partial, const char Line 251  int String::cmp(int& partial, const char
         uint a_countdown=a_chunk->count;          uint a_countdown=a_chunk->count;
         size_t pos=0;          size_t pos=0;
   
         bool a_break=size()==0;          bool a_break=a_size==0;
         bool b_break=b_size==0;          bool b_break=b_size==0;
         if(!(a_break || b_break)) while(true) {          if(!(a_break || b_break)) while(true) {
                 if(pos+a_row->item.size > this_offset) {                  if(pos+a_row->item.size > this_offset) {
                         if(lang!=UL_UNSPECIFIED && a_row->item.lang!=lang)                           if(lang!=UL_UNSPECIFIED && a_row->item.lang>lang) 
                                 return -1; // wrong lang -- bail out                                  return -1; // wrong lang -- bail out
   
                         int size_diff=                          int size_diff=
Line 358  int String::cmp(int& partial, const char Line 309  int String::cmp(int& partial, const char
   
 #ifndef NO_STRING_ORIGIN  #ifndef NO_STRING_ORIGIN
 const Origin& String::origin() const {   const Origin& String::origin() const { 
         if(!fsize) {          if(is_empty()) {
                 static const Origin empty_origin={"empty string"};                  static const Origin empty_origin={"empty string"};
                 return empty_origin;                  return empty_origin;
         }          }
                   
         // determining origin by last appended piece          // determining origin by first piece or last appended piece
         // because first one frequently constant.           // because any of them can be constant=without origin: 
         // ex: ^load[/file] "document_root" + "/file"          // ex: ^load[/file] "document_root" + "/file"
         // when last peice is constant,           // when last peice is constant, 
         // ex: parser_root_auto_path{dynamic} / auto.p{const}          // ex: parser_root_auto_path{dynamic} / auto.p{const}
         // using first piece          // using first piece
         Origin& last_origin=append_here[-1].item.origin;          Origin& first_origin=head.rows[0].item.origin;
         return last_origin.file ? last_origin : head.rows[0].item.origin;          return first_origin.file ? first_origin : append_here[-1].item.origin;
 }  }
 #endif  #endif
   
 String& String::mid(size_t start, size_t finish) const {  String& String::mid(size_t start, size_t finish) const {
         String& result=*NEW String(pool());          String& result=*NEW String(pool());
   
         start=max(0, start);          start=max(size_t(0), start);
         finish=min(size(), finish);          finish=min(size(), finish);
         if(start==finish)          if(start==finish)
                 return result;                  return result;
   
         size_t pos=0;          size_t pos=0;
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {                  size_t item_finish=pos+row->item.size;
                 const Chunk::Row *row=chunk->rows;                  if(item_finish > start) { // started now or already?
                 for(uint i=0; i<chunk->count; pos+=row->item.size, i++, row++) {                          bool started=result.is_empty(); // started now?
                         if(row==append_here)                          bool finished=finish <= item_finish; // finished now?
                           size_t offset=started?start-pos:0;
                           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;                                  goto break2;
   
                         size_t item_finish=pos+row->item.size;  
                         if(item_finish > start) { // started now or already?  
                                 bool started=result.size()==0; // started now?  
                                 bool finished=finish <= item_finish; // finished now?  
                                 size_t offset=started?start-pos:0;  
                                 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;  
                         }  
                 }                  }
                 chunk=row->link;                  pos+=row->item.size;
         } while(chunk);          );
 break2:  break2:
 //      SAPI::log(pool(), "piece of '%s' from %d to %d is '%s'",  //      SAPI::log(pool(), "piece of '%s' from %d to %d is '%s'",
                 //cstr(), start, finish, result.cstr());                  //cstr(), start, finish, result.cstr());
Line 414  break2: Line 358  break2:
   
 int String::pos(const String& substr,   int String::pos(const String& substr, 
                                 int result, Untaint_lang lang) const {                                  int result, Untaint_lang lang) const {
         for(; result<size(); result++) {          size_t self_size=size();
           for(; size_t(result)<self_size; result++) {
                 int partial; cmp(partial, substr, result, lang);                  int partial; cmp(partial, substr, result, lang);
                 if(                  if(
                         partial==0 || // full match                          partial==0 || // full match
Line 427  int String::pos(const String& substr, Line 372  int String::pos(const String& substr,
   
 int String::pos(const char *substr, size_t substr_size,   int String::pos(const char *substr, size_t substr_size, 
                                 int result, Untaint_lang lang) const {                                  int result, Untaint_lang lang) const {
         for(; result<size(); result++) {          size_t self_size=size();
           for(; size_t(result)<self_size; result++) {
                 int partial; cmp(partial, substr, substr_size, result, lang);                  int partial; cmp(partial, substr, substr_size, result, lang);
                 if(                  if(
                         partial==0 || // full match                          partial==0 || // full match
Line 442  void String::split(Array& result, Line 388  void String::split(Array& result,
                                    size_t* pos_after_ref,                                      size_t* pos_after_ref, 
                                    const char *delim, size_t delim_size,                                      const char *delim, size_t delim_size, 
                                    Untaint_lang lang, int limit) const {                                     Untaint_lang lang, int limit) const {
           size_t self_size=size();
         if(delim_size) {          if(delim_size) {
                 size_t pos_after=pos_after_ref?*pos_after_ref:0;                  size_t pos_after=pos_after_ref?*pos_after_ref:0;
                 int pos_before;                  int pos_before;
Line 451  void String::split(Array& result, Line 398  void String::split(Array& result,
                         pos_after=pos_before+delim_size;                          pos_after=pos_before+delim_size;
                 }                  }
                 // last piece                  // last piece
                 if(pos_after<size() && limit) {                  if(pos_after<self_size && limit) {
                         result+=&mid(pos_after, size());                          result+=&mid(pos_after, self_size);
                         pos_after=size();                          pos_after=self_size;
                 }                  }
                 if(pos_after_ref)                  if(pos_after_ref)
                         *pos_after_ref=pos_after;                          *pos_after_ref=pos_after;
         } else { // empty delim          } else { // empty delim
                 result+=this;                  result+=this;
                 if(pos_after_ref)                  if(pos_after_ref)
                         *pos_after_ref+=size();                          *pos_after_ref+=self_size;
         }          }
 }  }
   
Line 468  void String::split(Array& result, Line 415  void String::split(Array& result,
                                    size_t* pos_after_ref,                                      size_t* pos_after_ref, 
                                    const String& delim, Untaint_lang lang,                                      const String& delim, Untaint_lang lang, 
                                    int limit) const {                                     int limit) const {
         if(delim.size()) {          if(!delim.is_empty()) {
                 size_t pos_after=pos_after_ref?*pos_after_ref:0;                  size_t pos_after=pos_after_ref?*pos_after_ref:0;
                 int pos_before;                  int pos_before;
                 // while we have 'delim'...                  // while we have 'delim'...
Line 517  static void regex_options(char *options, Line 464  static void regex_options(char *options,
 }  }
   
 /// @todo maybe need speedup: some option to remove pre/match/post string generation  /// @todo maybe need speedup: some option to remove pre/match/post string generation
 bool String::match(const unsigned char *pcre_tables,  bool String::match(
                                    const String *aorigin,                                     const String *aorigin,
                                    const String& regexp,                                      const String& regexp, 
                                    const String *options,                                     const String *options,
Line 525  bool String::match(const unsigned char * Line 472  bool String::match(const unsigned char *
                                    Row_action row_action, void *info,                                     Row_action row_action, void *info,
                                    bool *was_global) const {                                      bool *was_global) const { 
   
         if(!regexp.size())          if(regexp.is_empty())
                 throw Exception(0, 0,                  throw Exception(0,
                         aorigin,                          aorigin,
                         "regexp is empty");                          "regexp is empty");
         const char *pattern=regexp.cstr(UL_AS_IS);          const char *pattern=regexp.cstr();
         const char *errptr;          const char *errptr;
         int erroffset;          int erroffset;
     int option_bits[2];  regex_options(options?options->cstr():0, option_bits);      int option_bits[2];  regex_options(options?options->cstr():0, option_bits);
Line 537  bool String::match(const unsigned char * Line 484  bool String::match(const unsigned char *
                 *was_global=option_bits[1]!=0;                  *was_global=option_bits[1]!=0;
         pcre *code=pcre_compile(pattern, option_bits[0],           pcre *code=pcre_compile(pattern, option_bits[0], 
                 &errptr, &erroffset,                  &errptr, &erroffset,
                 pcre_tables);                  pool().get_source_charset().pcre_tables);
   
         if(!code)          if(!code)
                 throw Exception(0, 0,                  throw Exception(0,
                         &regexp.mid(erroffset, regexp.size()),                          &regexp.mid(erroffset, regexp.size()),
                         "regular expression syntax error - %s", errptr);                          "regular expression syntax error - %s", errptr);
                   
         int info_substrings=pcre_info(code, 0, 0);          int info_substrings=pcre_info(code, 0, 0);
         if(info_substrings<0) {          if(info_substrings<0) {
                 pcre_free(code);                  pcre_free(code);
                 throw Exception(0, 0,                  throw Exception(0,
                         aorigin,                          aorigin,
                         "pcre_info error (%d)",                           "pcre_info error (%d)", 
                                 info_substrings);                                  info_substrings);
         }          }
   
         int startoffset=0;          int startoffset=0;
         const char *subject=cstr(UL_AS_IS);          const char *subject=cstr();
         int length=strlen(subject);          int length=strlen(subject);
         int ovecsize;          int ovecsize;
         int *ovector=(int *)malloc(sizeof(int)*          int *ovector=(int *)malloc(sizeof(int)*
Line 587  bool String::match(const unsigned char * Line 534  bool String::match(const unsigned char *
   
                 if(exec_substrings<0) {                  if(exec_substrings<0) {
                         pcre_free(code);                          pcre_free(code);
                         throw Exception(0, 0,                          throw Exception(0,
                                 aorigin,                                  aorigin,
                                 "regular expression execute error (%d)",                                   "regular expression execute error (%d)", 
                                         exec_substrings);                                          exec_substrings);
Line 619  bool String::match(const unsigned char * Line 566  bool String::match(const unsigned char *
         }          }
 }  }
   
 String& String::change_case(Pool& pool, const unsigned char *tables,   String& String::change_case(Pool& pool, 
                                                         Change_case_kind kind) const {                                                          Change_case_kind kind) const {
           const unsigned char *tables=pool.get_source_charset().pcre_tables;
         String& result=*new(pool) String(pool);          String& result=*new(pool) String(pool);
   
         const unsigned char *a;          const unsigned char *a;
Line 635  String& String::change_case(Pool& pool, Line 583  String& String::change_case(Pool& pool,
                 b=0;                  b=0;
                 break;                  break;
         default:          default:
                 throw Exception(0, 0,                   throw Exception(0, 
                         this,                           this, 
                         "unknown change case kind #%d",                           "unknown change case kind #%d", 
                                 static_cast<int>(kind)); // never                                  static_cast<int>(kind)); // never
Line 643  String& String::change_case(Pool& pool, Line 591  String& String::change_case(Pool& pool,
                 break; // never                  break; // never
         }                 }       
   
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {                  char *new_cstr=(char *)pool.malloc(row->item.size, 12);
                 const Chunk::Row *row=chunk->rows;                  char *dest=new_cstr;
                 for(uint i=0; i<chunk->count; i++, row++) {                  const char *src=row->item.ptr; 
                         if(row==append_here)                  for(int size=row->item.size; size--; src++) {
                                 goto break2;                          unsigned char c=a[(unsigned char)*src];
                           if(b)
                                   c=b[c];
   
                         char *new_cstr=(char *)pool.malloc(row->item.size, 12);                          *dest++=(char)c;
                         char *dest=new_cstr;  
                         const char *src=row->item.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, row->item.size,   
                                 row->item.lang,  
                                 row->item.origin.file, row->item.origin.line);  
                 }                  }
                 chunk=row->link;                  
         } while(chunk);                  result.APPEND(new_cstr, row->item.size, 
 break2:                          row->item.lang,
                           row->item.origin.file, row->item.origin.line);
           );
   
         return result;          return result;
 }  }
   
 void String::join_chain(Pool& pool,   void String::join_chain(Pool& pool, 
                                            uint& ai, const Chunk*& achunk, const Chunk::Row*& arow,                                             const Chunk*& achunk, const Chunk::Row*& arow, uint& acountdown, 
                                            Untaint_lang& joined_lang, const char *& joined_ptr, size_t& joined_size) const {                                             uchar& joined_lang, const char *& joined_ptr, size_t& joined_size) const {
         joined_lang=arow->item.lang;          joined_lang=arow->item.lang;
                   
         // calc size          // calc size
         joined_size=0;          joined_size=0;
         {          {
                 uint start_i=ai;                  const Chunk* chunk=achunk;
                 const Chunk::Row *start_row=arow;                  const Chunk::Row* row=arow;
                 const Chunk *chunk=achunk;                  uint countdown=acountdown;
                 do {                  STRING_PREPARED_FOREACH_ROW(*this, 
                         const Chunk::Row *row=start_row;                          if(row->item.lang==joined_lang)
                         for(uint i=start_i; i<chunk->count; i++, row++) {                                  joined_size+=row->item.size;
                                 if(row==append_here)                          else
                                         goto break21;  
                                   
                                 if(row->item.lang==joined_lang)  
                                         joined_size+=row->item.size;  
                                 else  
                                         break;  
                         }  
                         if(chunk=row->link) {  
                                 start_i=0;  
                                 start_row=chunk->rows;  
                         } else  
                                 break;                                  break;
                 } while(true);                  );
 break21:;  
         }          }
   
         // if one row, return simply itself          // if one row, return simply itself
         if(joined_size==arow->item.size) {          if(joined_size==arow->item.size) {
                 joined_ptr=arow->item.ptr;                  joined_ptr=arow->item.ptr;
                 ai++; arow++;  
                 if(ai==achunk->count)  
                         achunk=arow->link;                
         } else {          } else {
                 // join adjacent rows                  // join adjacent rows
                 char *ptr=(char *)pool.malloc(joined_size,13);                  char *ptr=(char *)pool.malloc(joined_size,13);  
                 joined_ptr=ptr;                  joined_ptr=ptr;
                 uint start_i=ai;  
                 const Chunk::Row *start_row=arow;                  const Chunk* chunk=achunk;
                 const Chunk *chunk=achunk;                  const Chunk::Row* row=arow;
                 uint i;                  uint countdown=acountdown;
                 const Chunk::Row *row;                  STRING_PREPARED_FOREACH_ROW(*this, 
                 do {                          if(row->item.lang==joined_lang) {
                         row=start_row;                                  memcpy(ptr, row->item.ptr, row->item.size);  ptr+=row->item.size;
                         for(i=start_i; i<chunk->count; i++, row++) {  
                                 if(row==append_here)  
                                         goto break22;  
                                   
                                 if(row->item.lang==joined_lang) {  
                                         memcpy(ptr, row->item.ptr, row->item.size);  
                                         ptr+=row->item.size;  
                                 } else  
                                         break;  
                         }  
                         if(chunk=row->link) {  
                                 start_i=0;  
                                 start_row=chunk->rows;  
                         } else                          } else
                                 break;                                  break; // before non-ours
                 } while(true);                  );
 break22:;  
                                   
                 // return joined rows                  // set pointers after joined piece
                 ai=i;                  achunk=chunk;  arow=row;  acountdown=countdown;
                 arow=row;                  // & one step back, see String::reconstruct
                 achunk=chunk;                  --arow;  ++acountdown;
         }          }
 }  }
   
 String& String::reconstruct(Pool& pool) const {  String& String::reconstruct(Pool& pool) const {
         //_asm int 3;          //_asm int 3;
         String& result=*new(pool) String(pool);          String& result=*new(pool) String(pool);
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {                  uchar joined_lang;
                 const Chunk::Row *row=chunk->rows;                  const char *joined_ptr;
                 for(uint i=0; i<chunk->count; ) {                  size_t joined_size;
                         if(row==append_here)  IFNDEF_NO_STRING_ORIGIN(
                                 goto break2;                  const char *joined_origin_file=row->item.origin.file;
                   const size_t joined_origin_line=row->item.origin.line;
                         Untaint_lang joined_lang;  );
                         const char *joined_ptr;                  join_chain(pool, chunk, row, countdown,
                         size_t joined_size;                          joined_lang, joined_ptr, joined_size);
                         join_chain(pool, i, chunk, row,  
                                 joined_lang, joined_ptr, joined_size);                  result.APPEND(joined_ptr, joined_size, joined_lang,
                           joined_origin_file, joined_origin_line);
                         result.APPEND(joined_ptr, joined_size,           );
                                 joined_lang,  
                                 row->item.origin.file, row->item.origin.line);  
                         if(!chunk)  
                                 goto break2;  
                 }  
         } while(true);  
 break2:  
   
         return result;          return result;
 };  };
Line 776  break2: Line 679  break2:
 String& String::replace_in_reconstructed(Pool& pool, Dictionary& dict) const {  String& String::replace_in_reconstructed(Pool& pool, Dictionary& dict) const {
         //_asm int 3;          //_asm int 3;
         String& result=*new(pool) String(pool);          String& result=*new(pool) String(pool);
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {                  const char *src=row->item.ptr; 
                 const Chunk::Row *row=chunk->rows;                  size_t src_size=row->item.size;
                 for(uint i=0; i<chunk->count; i++, row++) {                  char *new_cstr=(char *)pool.malloc((size_t)ceil(src_size*dict.max_ratio()), 14);
                         if(row==append_here)                  char *dest=new_cstr;
                                 goto break2;                  while(src_size) {
                           // there is a row where first column starts 'src'
                         const char *src=row->item.ptr;                           if(Table::Item *item=dict.first_that_starts(src, src_size)) {
                         size_t src_size=row->item.size;                                  // get a=>b values
                         char *new_cstr=(char *)pool.malloc((size_t)ceil(src_size*dict.max_ratio()), 14);                                  const String& a=*static_cast<Array *>(item)->get_string(0);
                         char *dest=new_cstr;                                  const String& b=*static_cast<Array *>(item)->get_string(1);
                         while(src_size) {                                  // skip 'a' in 'src' && reduce work size
                                 // there is a row where first column starts 'src'                                  src+=a.size();  src_size-=a.size();
                                 if(Table::Item *item=dict.first_that_starts(src, src_size)) {                                  // write 'b' to 'dest' && skip 'b' in 'dest'
                                         // get a=>b values                                  b.store_to(dest);  dest+=b.size();
                                         const String& a=*static_cast<Array *>(item)->get_string(0);                          } else {
                                         const String& b=*static_cast<Array *>(item)->get_string(1);                                  // write a char to b && reduce work size
                                         // skip 'a' in 'src' && reduce work size                                  *dest++=*src++;  src_size--;
                                         src+=a.size();  src_size-=a.size();  
                                         // write 'b' to 'dest' && skip 'b' in 'dest'  
                                         b.store_to(dest);  dest+=b.size();  
                                 } else {  
                                         // write a char to b && reduce work size  
                                         *dest++=*src++;  src_size--;  
                                 }  
                         }                          }
   
                         result.APPEND(new_cstr, dest-new_cstr,   
                                 row->item.lang,  
                                 row->item.origin.file, row->item.origin.line);  
                 }                  }
                 chunk=row->link;  
         } while(chunk);  
   
 break2:                  result.APPEND(new_cstr, dest-new_cstr, 
                           row->item.lang,
                           row->item.origin.file, row->item.origin.line);
           );
         return result;          return result;
 }  }
   
Line 840  double String::as_double() const { Line 733  double String::as_double() const {
                 result=(double)strtod(cstr, &error_pos);                  result=(double)strtod(cstr, &error_pos);
   
         if(*error_pos/*not EOS*/)          if(*error_pos/*not EOS*/)
                 throw Exception(0, 0,                  throw Exception("number.format",
                         this,                          this,
                         "invalid number (double)");                          "invalid number (double)");
   
Line 868  int String::as_int() const { Line 761  int String::as_int() const {
                 result=(int)strtol(cstr, &error_pos, 0);                  result=(int)strtol(cstr, &error_pos, 0);
   
         if(*error_pos/*not EOS*/)          if(*error_pos/*not EOS*/)
                 throw Exception(0, 0,                  throw Exception("number.format",
                         this,                          this,
                         "invalid number (int)");                          "invalid number (int)");
   
         return result;          return result;
 }  }
   
   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?  /* @todo maybe network order worth spending some effort?
         don't bothering myself with network byte order,          don't bothering myself with network byte order,
         am not planning to be able to move resulting file across platforms          am not planning to be able to move resulting file across platforms
Line 883  int String::as_int() const { Line 783  int String::as_int() const {
 void String::serialize(size_t prolog_size, void *& buf, size_t& buf_size) const {  void String::serialize(size_t prolog_size, void *& buf, size_t& buf_size) const {
         buf_size=          buf_size=
                 prolog_size                  prolog_size
                 +used_rows()*(sizeof(Untaint_lang)+sizeof(size_t))                  +used_rows()*(sizeof(uchar)+sizeof(ushort))
                 +size();                  +size();
         buf=malloc(buf_size,15);          buf=malloc(buf_size,15);
         char *cur=(char *)buf+prolog_size;          char *cur=(char *)buf+prolog_size;
   
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {                  // lang
                 const Chunk::Row *row=chunk->rows;                  memcpy(cur, &row->item.lang, sizeof(uchar));
                 for(uint i=0; i<chunk->count; i++) {                  cur+=sizeof(uchar);
                         if(row==append_here)                  // size
                                 goto break2;                  uchar byte1; uchar byte2;
                   ushort2uchars(row->item.size, byte1, byte2);
                         // lang                  memcpy(cur, &byte1, sizeof(uchar)); cur+=sizeof(uchar);
                         memcpy(cur, &row->item.lang, sizeof(Untaint_lang));                  memcpy(cur, &byte2, sizeof(uchar)); cur+=sizeof(uchar);
                         cur+=sizeof(Untaint_lang);                  // bytes
                         // size                  memcpy(cur, row->item.ptr, row->item.size);
                         memcpy(cur, &row->item.size, sizeof(size_t));                  cur+=row->item.size;
                         cur+=sizeof(size_t);          );
                         // bytes  }
                         memcpy(cur, row->item.ptr, row->item.size);  bool String::deserialize(size_t prolog_size, void *buf, size_t buf_size, const char *file) {
                         cur+=row->item.size;          if(buf_size<=prolog_size)
                   return false;
                         row++;  
                 }  
                 chunk=row->link;  
         } while(chunk);  
 break2:  
         ;  
 }  
   
 /* @todo maybe network order worth spending some effort?          char *cur=(char *)buf+prolog_size;
         don't bothering myself with network byte order,  
         am not planning to be able to move resulting file across platforms  
         for now  
 */  
 #ifndef DOXYGEN  
 struct Serialized_piece {  
         String::Untaint_lang lang;  
         size_t size;  
         char ptr[1];  
 };  
 #endif  
   
 void String::deserialize(size_t prolog_size, void *buf, size_t buf_size, const char *file) {  
         char *cur=((char *)buf)+prolog_size;  
         buf_size-=prolog_size;          buf_size-=prolog_size;
   
         while(buf_size) {          while(buf_size) {
                 Serialized_piece& p=*(Serialized_piece *)cur;                  if(sizeof(uchar)+sizeof(ushort)>buf_size) // lang+size
                 APPEND(p.ptr, p.size, p.lang, file, 0);                          return false;
   
                   uchar lang=*(uchar *)(cur);             
                   ushort size=uchars2ushort(
                           *(uchar*)(cur+sizeof(uchar)*1),
                           *(uchar*)(cur+sizeof(uchar)*2)
                   );
   
                   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)*3); 
                   APPEND(ptr, size, lang, file, 0);
   
                 size_t piece_size=sizeof(p.lang)+sizeof(p.size)+p.size;  
                 cur+=piece_size;                  cur+=piece_size;
                 buf_size-=piece_size;                  buf_size-=piece_size;
         }          }
           return true;
 }  }
   

Removed from v.1.116  
changed lines
  Added in v.1.149


E-mail: