Diff for /parser3/src/main/pa_string.C between versions 1.121 and 1.168

version 1.121, 2001/11/16 13:58:28 version 1.168, 2002/08/08 08:43:38
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.preallocated_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.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  
                                 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 {
         size_t result=0;          size_t result=0;
         const Chunk *chunk=&head;           STRING_FOREACH_ROW(
         do {  
                 const Chunk::Row *row=chunk->rows;  
                 for(uint i=0; i<chunk->count; i++, row++) {  
                         if(row==append_here)  
                                 goto break2;  
   
                         result+=row->item.size;                          result+=row->item.size;
                 }          );
                 chunk=row->link;  
         } while(chunk);  
   
 break2:  
         return result;          return result;
 }  }
   
 /// @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 *>(malloc(
         const Chunk *chunk=&src.head;                           sizeof(Chunk)// count+interpadding(?)+rows[CR_PREALLOCATED_COUNT]+tailpadding(??)
         do {                          -sizeof(Chunk::rows_type) // PREALLOCATED rows
                 const Chunk::Row *row=chunk->rows;                          +sizeof(Chunk::Row)*new_chunk_count // neaded rows
                 for(uint i=0; i<chunk->count; i++, row++) {                          +sizeof(Chunk *) // link size
                         if(row==src.append_here)                  , 10));
                                 goto break2;          new_chunk->rows[new_chunk->count=new_chunk_count].link=0;
                                   last_chunk->rows[last_chunk->count].link=new_chunk;
                         APPEND(row->item.ptr, row->item.size,           
                                 (lang!=UL_PASS_APPENDED && (row->item.lang==UL_TAINTED || forced))?lang:(Untaint_lang)row->item.lang,          last_chunk=new_chunk;
                                 row->item.origin.file, row->item.origin.line);          append_here=last_chunk->rows;
                 }  
                 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 168  String& String::real_append(STRING_APPEN Line 107  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();
   
Line 183  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(
         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 215  break2: Line 156  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.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 230  int String::cmp(int& partial, const Stri Line 172  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 303  int String::cmp(int& partial, const Stri Line 245  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.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 314  int String::cmp(int& partial, const char Line 257  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 372  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;
   
         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,   
                                         (Untaint_lang)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 428  break2: Line 364  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 441  int String::pos(const String& substr, Line 378  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 456  void String::split(Array& result, Line 394  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 465  void String::split(Array& result, Line 404  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 482  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 504  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 539  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 633  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 649  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 657  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,   
                                 (Untaint_lang)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 {
                                            Untaint_lang& joined_lang, const char *& joined_ptr, size_t& joined_size) const {  //      return reconstruct(pool).replace_in_reconstructed(pool, dict);
         joined_lang=(Untaint_lang)arow->item.lang;  
           
         // 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);          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;  
   
                         Untaint_lang joined_lang;          STRING_FOREACH_ROW(
                         const char *joined_ptr;                  const char *src=row->item.ptr; 
                         size_t joined_size;                  size_t src_size=row->item.size;
                         join_chain(pool, i, chunk, row,                  char *new_cstr=(char *)pool.malloc((size_t)ceil(src_size*dict.max_ratio()), 14);
                                 joined_lang, joined_ptr, joined_size);                  char *dest=new_cstr;
                   while(src_size) {
                         result.APPEND(joined_ptr, joined_size,                           // there is a row where first column starts 'src'
                                 joined_lang,                          if(Table::Item *item=dict.first_that_starts(src, src_size)) {
                                 row->item.origin.file, row->item.origin.line);                                  // get a=>b values
                         if(!chunk)                                  const String& a=*static_cast<Array *>(item)->get_string(0);
                                 goto break2;                                  const String& b=*static_cast<Array *>(item)->get_string(1);
                                   // skip 'a' in 'src' && reduce work 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--;
                           }
                 }                  }
         } while(true);  
 break2:  
   
                   result.APPEND(new_cstr, dest-new_cstr, row->item.lang,
                           row->item.origin.file, row->item.origin.line);
           );
         return result;          return result;
 };  }
   
 String& String::replace_in_reconstructed(Pool& pool, Dictionary& dict) 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; i++, row++) {  
                         if(row==append_here)  
                                 goto break2;  
   
                         const char *src=row->item.ptr;   String& String::join_chains(Pool& pool, char** acstr) const {
                         size_t src_size=row->item.size;          char *lcstr=cstr();
                         char *new_cstr=(char *)pool.malloc((size_t)ceil(src_size*dict.max_ratio()), 14);          const char *current=lcstr;
                         char *dest=new_cstr;  
                         while(src_size) {  
                                 // there is a row where first column starts 'src'  
                                 if(Table::Item *item=dict.first_that_starts(src, src_size)) {  
                                         // get a=>b values  
                                         const String& a=*static_cast<Array *>(item)->get_string(0);  
                                         const String& b=*static_cast<Array *>(item)->get_string(1);  
                                         // skip 'a' in 'src' && reduce work 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,           String& result=*new(pool) String(pool);
                                 (Untaint_lang)row->item.lang,          STRING_FOREACH_ROW(
                                 row->item.origin.file, row->item.origin.line);  IFNDEF_NO_STRING_ORIGIN(
                 }                  const char *joined_origin_file=row->item.origin.file;
                 chunk=row->link;                  const size_t joined_origin_line=row->item.origin.line;
         } while(chunk);  );
                   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);
           );
   
 break2:          if(acstr)
                   *acstr=lcstr;
         return result;          return result;
 }  }
   
 String& String::replace(Pool& pool, Dictionary& dict) const {  
         return reconstruct(pool).replace_in_reconstructed(pool, dict);  
 }  
   
 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 853  double String::as_double() const { Line 711  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 864  int String::as_int() const { Line 723  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 881  int String::as_int() const { Line 745  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 897  int String::as_int() const { Line 769  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++;          char *cur=(char *)buf+prolog_size;
                 }  
                 chunk=row->link;  
         } while(chunk);  
 break2:  
         ;  
 }  
   
 /* @todo maybe network order worth spending some effort?  
         don't bothering myself with network byte order,  
         am not planning to be able to move resulting file across platforms  
         for now  
 */  
 #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.121  
changed lines
  Added in v.1.168


E-mail: