Diff for /parser3/src/main/pa_string.C between versions 1.126 and 1.170

version 1.126, 2001/11/23 10:38:53 version 1.170, 2002/09/17 14:22:29
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://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         $Id$  
 */  */
   
 #include "pa_config_includes.h"  static const char* IDENT_STRING_C="$Date$";
   
 #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::OnPool(Pool& apool, const char *local_src, size_t src_size, bool tainted) {
           if(local_src && *local_src) {
                   if(src_size==0)
                           src_size=strlen(local_src);
                   
                   char *pooled_src=(char *)apool.malloc(src_size);
                   memcpy(pooled_src, local_src, src_size);
                   return *new(apool) String(apool, pooled_src, src_size, tainted);
           } else
                   return *new(apool) String(apool);
   }
 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) {
         last_chunk=&head;          last_chunk=&head.chunk;
         head.count=CR_PREALLOCATED_COUNT;          head.chunk.count=CR_PREALLOCATED_COUNT;
         append_here=head.rows;          append_here=head.chunk.rows;
         head_link=0;  
         link_row=&head.rows[head.count];  
   
         if(src)          if(src)
                 if(tainted)                  if(tainted)
Line 39  String::String(Pool& apool, const char * Line 53  String::String(Pool& apool, const char *
   
 String::String(const String& src) :       String::String(const String& src) :     
         Pooled(src.pool()) {          Pooled(src.pool()) {
         head.count=CR_PREALLOCATED_COUNT;          last_chunk=&head.chunk;
                   head.chunk.count=CR_PREALLOCATED_COUNT;
         uint src_used_rows=src.used_rows();          append_here=head.chunk.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_link=last_chunk;  
                 append_here=link_row=&last_chunk->rows[last_chunk->count];  
   
                 Chunk *old_chunk=src.head_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_PASS_APPENDED);
                         } else {  
                                 // the last source chunk  
                                 // taking only those rows of chunk that _left_to_copy  
                                 memcpy(new_rows, old_chunk->rows, sizeof(Chunk::Row)*rows_left_to_copy);  
                                 break;  
                         }  
                 }  
         }  
         link_row->link=0;  
         src_used_rows;  
 }  }
   
 size_t  String::size() const {  size_t  String::size() const {
Line 100  size_t  String::size() const { Line 65  size_t  String::size() const {
         STRING_FOREACH_ROW(          STRING_FOREACH_ROW(
                         result+=row->item.size;                          result+=row->item.size;
         );          );
 break2:  
         return result;          return result;
 }  }
   
Line 110  uint String::used_rows() const { Line 74  uint String::used_rows() const {
         STRING_FOREACH_ROW(          STRING_FOREACH_ROW(
                 result++;                  result++;
         );          );
 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;
         if(new_chunk_count>MAX_USHORT)          if(new_chunk_count>max_integral(Chunk::count_type))
                 new_chunk_count=MAX_USHORT;                  new_chunk_count=max_integral(Chunk::count_type);
   
         last_chunk=static_cast<Chunk *>(          Chunk *new_chunk=static_cast<Chunk *>(malloc(
                 malloc(sizeof(uint)+sizeof(Chunk::Row)*new_chunk_count+sizeof(Chunk *), 10));                          sizeof(Chunk)// count+interpadding(?)+rows[CR_PREALLOCATED_COUNT]+tailpadding(??)
         last_chunk->count=new_chunk_count;                          -sizeof(Chunk::rows_type) // PREALLOCATED rows
         link_row->link=last_chunk;                          +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;          append_here=last_chunk->rows;
         link_row=&last_chunk->rows[last_chunk->count];  
         link_row->link=0;  
 }  }
   
 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 135  String& String::real_append(STRING_APPEN Line 107  String& String::real_append(STRING_APPEN
         if(!size)          if(!size)
                 return *this;                  return *this;
   
         while(size>MAX_USHORT) {  #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())                  if(chunk_is_full())
                         expand();                          expand();
   
                 append_here->item.ptr=src;                  append_here->item.ptr=src;
                 append_here->item.size=MAX_USHORT;                  append_here->item.size=max_integral(Chunk::Row::item_size_type);
                 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 148  String& String::real_append(STRING_APPEN Line 125  String& String::real_append(STRING_APPEN
 #endif  #endif
                 append_here++;                  append_here++;
   
                 src+=MAX_USHORT;                  src+=max_integral(Chunk::Row::item_size_type);
                 size-=MAX_USHORT;                  size-=max_integral(Chunk::Row::item_size_type);
         }          }
   
         if(chunk_is_full())          if(chunk_is_full())
Line 167  String& String::real_append(STRING_APPEN Line 144  String& String::real_append(STRING_APPEN
         return *this;          return *this;
 }  }
   
 char String::first_char() const {  
         if(!used_rows())  
                 throw Exception(0, 0,  
                         this,  
                         "getting first char of empty string");  
   
         return *head.rows[0].item.ptr;  
 }  
   
 uint String::hash_code() const {  uint String::hash_code() const {
         uint result=0;          uint result=0;
         STRING_FOREACH_ROW(          STRING_FOREACH_ROW(
                         result=Hash::generic_code(result, row->item.ptr, row->item.size);                          result=Hash::generic_code(result, row->item.ptr, row->item.size);
         );          );
 break2:  
         return result;          return result;
 }  }
   
Line 192  int String::cmp(int& partial, const Stri Line 159  int String::cmp(int& partial, const Stri
         size_t a_size=size();          size_t a_size=size();
         this_offset=min(this_offset, a_size-1);          this_offset=min(this_offset, a_size-1);
   
         const Chunk *a_chunk=&head;          const Chunk *a_chunk=&head.chunk;
         const Chunk *b_chunk=&src.head;          const Chunk *b_chunk=&src.head.chunk;
         const Chunk::Row *a_row=a_chunk->rows;          const Chunk::Row *a_row=a_chunk->rows;
         const Chunk::Row *b_row=b_chunk->rows;          const Chunk::Row *b_row=b_chunk->rows;
         size_t a_offset=this_offset;          size_t a_offset=this_offset;
Line 206  int String::cmp(int& partial, const Stri Line 173  int String::cmp(int& partial, const Stri
         size_t pos=0;           size_t pos=0; 
   
         bool a_break=a_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 282  int String::cmp(int& partial, const char Line 249  int String::cmp(int& partial, const char
         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, a_size-1);          this_offset=min(this_offset, a_size-1);
   
         const Chunk *a_chunk=&head;          const Chunk *a_chunk=&head.chunk;
         const Chunk::Row *a_row=a_chunk->rows;          const Chunk::Row *a_row=a_chunk->rows;
         size_t a_offset=this_offset;          size_t a_offset=this_offset;
         size_t b_offset=0;          size_t b_offset=0;
Line 294  int String::cmp(int& partial, const char Line 261  int String::cmp(int& partial, const char
         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 348  int String::cmp(int& partial, const char Line 315  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(!used_rows()) {          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.chunk.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=min(start, size());
         finish=min(size(), finish);          finish=max(start, finish);
         if(start==finish)          if(start==finish)
                 return result;                  return result;
   
Line 376  String& String::mid(size_t start, size_t Line 343  String& String::mid(size_t start, size_t
         STRING_FOREACH_ROW(          STRING_FOREACH_ROW(
                 size_t item_finish=pos+row->item.size;                  size_t item_finish=pos+row->item.size;
                 if(item_finish > start) { // started now or already?                  if(item_finish > start) { // started now or already?
                         bool started=result.size()==0; // started now?                          bool started=result.is_empty(); // started now?
                         bool finished=finish <= item_finish; // finished now?                          bool finished=finish <= item_finish; // finished now?
                         size_t offset=started?start-pos:0;                          size_t offset=started?start-pos:0;
                         size_t size=finished?finish-pos:row->item.size;                          size_t size=finished?finish-pos:row->item.size;
Line 398  break2: Line 365  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 {
         size_t self_size=size();          size_t self_size=size();
         for(; result<self_size; result++) {          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 412  int String::pos(const String& substr, Line 379  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 {
         size_t self_size=size();          size_t self_size=size();
         for(; result<self_size; result++) {          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 454  void String::split(Array& result, Line 421  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 476  void String::split(Array& result, Line 443  void String::split(Array& result,
         }          }
 }  }
   
 static void regex_options(char *options, int *result){  static void regex_options(const String *options, int *result, bool& need_pre_post_match){
     struct Regex_option {      struct Regex_option {
                 char key;                  const char *keyL;
                   const char *keyU;
                 int clear, set;                  int clear, set;
                 int *result;                  int *result;
                   bool *flag;
     } regex_option[]={      } regex_option[]={
                 {'i', 0, PCRE_CASELESS, result}, // a=A                  {"i", "I", 0, PCRE_CASELESS, result}, // a=A
                 {'s', 0, PCRE_DOTALL, result}, // \n\n$ [default]                  {"s", "S", 0, PCRE_DOTALL, result}, // \n\n$ [default]
                 {'x', 0, PCRE_EXTENDED, result}, // whitespace in regex ignored                  {"x", "U", 0, PCRE_EXTENDED, result}, // whitespace in regex ignored
                 {'m', PCRE_DOTALL, PCRE_MULTILINE, result}, // ^aaa\n$^bbb\n$                  {"m", "M", PCRE_DOTALL, PCRE_MULTILINE, result}, // ^aaa\n$^bbb\n$
                 {'g', 0, true, result+1}, // many rows                  {"g", "G", 0, true, result+1}, // many rows
                 {0},                  {"'", 0, 0, 0, 0, &need_pre_post_match},
                   {0}
     };      };
         result[0]=PCRE_EXTRA | PCRE_DOTALL;          result[0]=PCRE_EXTRA | PCRE_DOTALL;
         result[1]=0;          result[1]=0;
   
     if(options)       if(options) 
                 for(Regex_option *o=regex_option; o->key; o++)                   for(Regex_option *o=regex_option; o->keyL; o++) 
                         if(                          if(options->pos(o->keyL)>=0
                                 strchr(options, o->key) ||                                   || (o->keyU && options->pos(o->keyU)>=0)) {
                                 strchr(options, toupper(o->key))) {                                  if(o->flag)
                                 *(o->result)&=~o->clear;                                          *o->flag=true;
                                 *(o->result)|=o->set;                                  else { // result
                                           *o->result &= ~o->clear;
                                           *o->result |= o->set;
                                   }
                         }                          }
 }  }
   
 /// @todo maybe need speedup: some option to remove pre/match/post string generation  /// @todo make replacement Table stacked
 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 511  bool String::match(const unsigned char * Line 484  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();          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);      bool need_pre_post_match=false;
           int option_bits[2];  regex_options(options, option_bits, need_pre_post_match);
         if(was_global)          if(was_global)
                 *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;  
         const char *subject=cstr();          const char *subject=cstr();
         int length=strlen(subject);          int length=strlen(subject);
         int ovecsize;          const int ovecsize=(1/*match*/+MAX_STRING_MATCH_TABLE_COLUMNS)*3;
         int *ovector=(int *)malloc(sizeof(int)*          int ovector[ovecsize];
                 (ovecsize=(1/*match*/+info_substrings)*3), 11);  
           // create table
         { // create table          *table=NEW Table(pool(), *string_match_table_template);
                 Array& columns=*NEW Array(pool());  
                 columns+=string_pre_match_name;  
                 columns+=string_match_name;  
                 columns+=string_post_match_name;  
                 for(int i=1; i<=info_substrings; i++) {  
                         char *column=(char *)malloc(MAX_NUMBER);  
                         snprintf(column, MAX_NUMBER, "%d", i);  
                         columns+=NEW String(pool(), column); // .i column name  
                 }  
                 *table=NEW Table(pool(), aorigin, &columns);  
         }  
   
         int exec_option_bits=0;          int exec_option_bits=0;
           int prestart=0;
           int poststart=0;
           int postfinish=size();
         while(true) {          while(true) {
                 int exec_substrings=pcre_exec(code, 0,                  int exec_substrings=pcre_exec(code, 0,
                         subject, length, startoffset,                          subject, length, prestart,
                         exec_option_bits, ovector, ovecsize);                          exec_option_bits, ovector, ovecsize);
                                   
                 if(exec_substrings==PCRE_ERROR_NOMATCH) {                  if(exec_substrings==PCRE_ERROR_NOMATCH) {
                         pcre_free(code);                          pcre_free(code);
                         row_action(**table, 0/*last time, no row*/, 0, 0, info);                          row_action(**table, 0/*last time, no row*/, 0, 0, poststart, postfinish, info);
                         return option_bits[1]!=0; // global=true+table, not global=false                          return option_bits[1]!=0; // global=true+table, not global=false
                 }                  }
   
                 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);
                 }                  }
   
                   int prefinish=ovector[0];
                   poststart=ovector[1];
                 Array& row=*NEW Array(pool());                  Array& row=*NEW Array(pool());
                 row+=&mid(0, ovector[0]); // .prematch column value                  row+=need_pre_post_match?&mid(0, prefinish):0; // .prematch column value
                 row+=&mid(ovector[0], ovector[1]); // .match                  row+=need_pre_post_match?&mid(prefinish, poststart):0; // .match
                 row+=&mid(ovector[1], size()); // .postmatch                  row+=need_pre_post_match?&mid(poststart, postfinish):0; // .postmatch
                                   
                 for(int i=1; i<exec_substrings; i++) {                  for(int i=1; i<exec_substrings; i++) {
                         // -1:-1 case handled peacefully by mid() itself                          // -1:-1 case handled peacefully by mid() itself
                         row+=&mid(ovector[i*2+0], ovector[i*2+1]); // .i column value                          row+=&mid(ovector[i*2+0], ovector[i*2+1]); // .i column value
                 }                  }
                                   
                 row_action(**table, &row, startoffset, ovector[0], info);                  row_action(**table, &row, prestart, prefinish, poststart, postfinish, info);
   
                 if(!option_bits[1] || startoffset==ovector[1]) { // not global | going to hang                  if(!option_bits[1] || prestart==poststart) { // not global | going to hang
                         pcre_free(code);                          pcre_free(code);
                         row_action(**table, 0/*last time, no row*/, 0, 0, info);                          row_action(**table, 0/*last time, no row*/, 0, 0, poststart, postfinish, info);
                         return true;                          return true;
                 }                  }
                 startoffset=ovector[1];                  prestart=poststart;
   
 /*  /*
                 if(option_bits[0] & PCRE_MULTILINE)                  if(option_bits[0] & PCRE_MULTILINE)
Line 605  bool String::match(const unsigned char * Line 573  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 621  String& String::change_case(Pool& pool, Line 590  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 629  String& String::change_case(Pool& pool, Line 598  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)
                         char *new_cstr=(char *)pool.malloc(row->item.size, 12);                                  c=b[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;                          *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,   /// @test if in some piece were found no dict words, append it, not it's duplicate
                                            uint& ai, const Chunk*& achunk, const Chunk::Row*& arow,  String& String::replace(Pool& pool, Dictionary& dict) const {
                                            uchar& joined_lang, const char *& joined_ptr, size_t& joined_size) const {          char *lcstr=cstr();
         joined_lang=arow->item.lang;          const char *current=lcstr;
           
         // calc size  
         joined_size=0;  
         {  
                 uint start_i=ai;  
                 const Chunk::Row *start_row=arow;  
                 const Chunk *chunk=achunk;  
                 do {  
                         const Chunk::Row *row=start_row;  
                         for(uint i=start_i; i<chunk->count; i++, row++) {  
                                 if(row==append_here)  
                                         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;  
                 } while(true);  
 break21:;  
         }  
   
         // if one row, return simply itself  
         if(joined_size==arow->item.size) {  
                 joined_ptr=arow->item.ptr;  
                 ai++; arow++;  
                 if(ai==achunk->count)  
                         achunk=arow->link;                
         } else {  
                 // join adjacent rows  
                 char *ptr=(char *)pool.malloc(joined_size,13);  
                 joined_ptr=ptr;  
                 uint start_i=ai;  
                 const Chunk::Row *start_row=arow;  
                 const Chunk *chunk=achunk;  
                 uint i;  
                 const Chunk::Row *row;  
                 do {  
                         row=start_row;  
                         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  
                                 break;  
                 } while(true);  
 break22:;  
                   
                 // return joined rows  
                 ai=i;  
                 arow=row;  
                 achunk=chunk;  
         }  
 }  
   
 String& String::reconstruct(Pool& pool) const {  
         //_asm int 3;  
         String& result=*new(pool) String(pool);  
         const Chunk *chunk=&head;   
         do {  
                 const Chunk::Row *row=chunk->rows;  
                 for(uint i=0; i<chunk->count; ) {  
                         if(row==append_here)  
                                 goto break2;  
   
                         uchar joined_lang;  
                         const char *joined_ptr;  
                         size_t joined_size;  
                         join_chain(pool, i, chunk, row,  
                                 joined_lang, joined_ptr, joined_size);  
   
                         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;  
 };  
   
 String& String::replace_in_reconstructed(Pool& pool, Dictionary& dict) const {  
         //_asm int 3;  
         String& result=*new(pool) String(pool);          String& result=*new(pool) String(pool);
         STRING_FOREACH_ROW(          STRING_FOREACH_ROW(
                 const char *src=row->item.ptr;   IFNDEF_NO_STRING_ORIGIN(
                 size_t src_size=row->item.size;                  const char *joined_origin_file=row->item.origin.file;
                 char *new_cstr=(char *)pool.malloc((size_t)ceil(src_size*dict.max_ratio()), 14);                  const size_t joined_origin_line=row->item.origin.line;
   );
                   uchar joined_lang=row->item.lang;
                   const char *joined_ptr=current;
                   // calc size
                   size_t joined_size=0;
                   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_FOREACH_ROW
                   --row;  ++countdown;
                   
                   char *new_cstr=(char *)pool.malloc((size_t)ceil(joined_size*dict.max_ratio()), 14);
                 char *dest=new_cstr;                  char *dest=new_cstr;
                 while(src_size) {                  while(joined_size) {
                         // there is a row where first column starts 'src'                          // there is a row where first column starts 'joined_ptr'
                         if(Table::Item *item=dict.first_that_starts(src, src_size)) {                          if(Table::Item *item=dict.first_that_starts(joined_ptr, joined_size)) {
                                 // get a=>b values                                  // get a=>b values
                                 const String& a=*static_cast<Array *>(item)->get_string(0);                                  const String& a=*static_cast<Array *>(item)->get_string(0);
                                 const String& b=*static_cast<Array *>(item)->get_string(1);                                  const String& b=*static_cast<Array *>(item)->get_string(1);
                                 // skip 'a' in 'src' && reduce work size                                  // skip 'a' in 'joined_ptr' && reduce work size
                                 src+=a.size();  src_size-=a.size();                                  joined_ptr+=a.size();  joined_size-=a.size();
                                 // write 'b' to 'dest' && skip 'b' in 'dest'                                  // write 'b' to 'dest' && skip 'b' in 'dest'
                                 b.store_to(dest);  dest+=b.size();                                  b.store_to(dest);  dest+=b.size();
                         } else {                          } else {
                                 // write a char to b && reduce work size                                  // write a char to b && reduce work size
                                 *dest++=*src++;  src_size--;                                  *dest++=*joined_ptr++;  joined_size--;
                         }                          }
                 }                  }
   
                 result.APPEND(new_cstr, dest-new_cstr,                   result.APPEND(new_cstr, dest-new_cstr, joined_lang,
                         row->item.lang,                          joined_origin_file, joined_origin_line);
                         row->item.origin.file, row->item.origin.line);  
         );          );
 break2:  
         return result;          return result;
 }  }
   
 String& String::replace(Pool& pool, Dictionary& dict) const {  String& String::join_chains(Pool& pool, char** acstr) const {
         return reconstruct(pool).replace_in_reconstructed(pool, dict);          char *lcstr=cstr();
           const char *current=lcstr;
   
           String& result=*new(pool) String(pool);
           STRING_FOREACH_ROW(
   IFNDEF_NO_STRING_ORIGIN(
                   const char *joined_origin_file=row->item.origin.file;
                   const size_t joined_origin_line=row->item.origin.line;
   );
                   uchar joined_lang=row->item.lang;
                   const char *joined_ptr=current;
                   // calc size
                   size_t joined_size=0;
                   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_FOREACH_ROW
                   --row;  ++countdown;
                   
                   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 String::as_double() const { 
         double result;          double result;
         const char *cstr;          const char *cstr;
         char buf[MAX_NUMBER];          char buf[MAX_NUMBER];
         if(head.rows+1==append_here) {          if(head.chunk.rows+1==append_here) {
                 int size=min(head.rows[0].item.size, MAX_NUMBER-1);                  int size=min(head.chunk.rows[0].item.size, MAX_NUMBER-1);
                 memcpy(buf, head.rows[0].item.ptr, size);                  memcpy(buf, head.chunk.rows[0].item.ptr, size);
                 buf[size]=0;                  buf[size]=0;
                 cstr=buf;                  cstr=buf;
         } else          } else
                 cstr=this->cstr();                  cstr=this->cstr();
           while(*cstr && isspace(*cstr))
                   cstr++;
           if(!*cstr)
                   return 0;
   
         char *error_pos;          char *error_pos;
         // 0xABC          // 0xABC
         if(cstr[0]=='0')          if(cstr[0]=='0')
Line 816  double String::as_double() const { Line 731  double String::as_double() const {
         else          else
                 result=(double)strtod(cstr, &error_pos);                  result=(double)strtod(cstr, &error_pos);
   
         if(*error_pos/*not EOS*/)          while(char c=*error_pos++)
                 throw Exception(0, 0,                  if(!isspace(c))
                         this,                          throw Exception("number.format",
                         "invalid number (double)");                                  this,
                                   "invalid number (double)");
   
         return result;          return result;
 }  }
Line 827  int String::as_int() const { Line 743  int String::as_int() const {
         int result;          int result;
         const char *cstr;          const char *cstr;
         char buf[MAX_NUMBER];          char buf[MAX_NUMBER];
         if(head.rows+1==append_here) {          if(head.chunk.rows+1==append_here) {
                 int size=min(head.rows[0].item.size, MAX_NUMBER-1);                  size_t size=min(head.chunk.rows[0].item.size, MAX_NUMBER-1);
                 memcpy(buf, head.rows[0].item.ptr, size);                  memcpy(buf, head.chunk.rows[0].item.ptr, size);
                 buf[size]=0;                  buf[size]=0;
                 cstr=buf;                  cstr=buf;
         } else          } else
                 cstr=this->cstr();                  cstr=this->cstr();
           while(*cstr && isspace(*cstr))
                   cstr++;
           if(!*cstr)
                   return 0;
   
         char *error_pos;          char *error_pos;
         // 0xABC          // 0xABC
         if(cstr[0]=='0')          if(cstr[0]=='0')
Line 844  int String::as_int() const { Line 765  int String::as_int() const {
         else          else
                 result=(int)strtol(cstr, &error_pos, 0);                  result=(int)strtol(cstr, &error_pos, 0);
   
         if(*error_pos/*not EOS*/)          while(char c=*error_pos++)
                 throw Exception(0, 0,                  if(!isspace(c))
                         this,                          throw Exception("number.format",
                         "invalid number (int)");                                  this,
                                   "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 870  void String::serialize(size_t prolog_siz Line 799  void String::serialize(size_t prolog_siz
                 memcpy(cur, &row->item.lang, sizeof(uchar));                  memcpy(cur, &row->item.lang, sizeof(uchar));
                 cur+=sizeof(uchar);                  cur+=sizeof(uchar);
                 // size                  // size
                 memcpy(cur, &row->item.size, sizeof(ushort));                  uchar byte1; uchar byte2;
                 cur+=sizeof(ushort);                  ushort2uchars(row->item.size, byte1, byte2);
                   memcpy(cur, &byte1, sizeof(uchar)); cur+=sizeof(uchar);
                   memcpy(cur, &byte2, sizeof(uchar)); cur+=sizeof(uchar);
                 // bytes                  // bytes
                 memcpy(cur, row->item.ptr, row->item.size);                  memcpy(cur, row->item.ptr, row->item.size);
                 cur+=row->item.size;                  cur+=row->item.size;
         );          );
 break2:  
         ;  
 }  }
 void String::deserialize(size_t prolog_size, void *buf, size_t buf_size, const char *file) {  bool String::deserialize(size_t prolog_size, void *buf, size_t buf_size, const char *file) {
           if(buf_size<=prolog_size)
                   return false;
   
         char *cur=(char *)buf+prolog_size;          char *cur=(char *)buf+prolog_size;
         buf_size-=prolog_size;          buf_size-=prolog_size;
   
         while(buf_size) {          while(buf_size) {
                 uchar lang=*(const uchar*)((const char*)cur+0);                  if(sizeof(uchar)+sizeof(ushort)>buf_size) // lang+size
                 ushort size=*(const ushort*)((const char*)cur+sizeof(uchar));                          return false;
                 const char *ptr=(const char*)cur+sizeof(uchar)+sizeof(ushort);  
                 APPEND(ptr, size, lang, file, 0);                  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;                  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);
   
                 cur+=piece_size;                  cur+=piece_size;
                 buf_size-=piece_size;                  buf_size-=piece_size;
         }          }
           return true;
 }  }

Removed from v.1.126  
changed lines
  Added in v.1.170


E-mail: