Diff for /parser3/src/main/pa_string.C between versions 1.236 and 1.267

version 1.236, 2010/12/29 12:53:33 version 1.267, 2021/12/28 15:58:31
Line 1 Line 1
 /** @file  /** @file
         Parser: string class. @see untalength_t.C.          Parser: string class. @see untalength_t.C.
   
         Copyright (c) 2001-2009 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001-2020 Art. Lebedev Studio (http://www.artlebedev.com)
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
 */  */
   
 static const char * const IDENT_STRING_C="$Date$";  
   
 #include "pa_string.h"  #include "pa_string.h"
 #include "pa_exception.h"  #include "pa_exception.h"
 #include "pa_table.h"  #include "pa_table.h"
Line 14  static const char * const IDENT_STRING_C Line 12  static const char * const IDENT_STRING_C
 #include "pa_charset.h"  #include "pa_charset.h"
 #include "pa_vregex.h"  #include "pa_vregex.h"
   
   volatile const char * IDENT_PA_STRING_C="$Id$" IDENT_PA_STRING_H;
   
 const String String::Empty;  const String String::Empty;
   
 int pa_atoi(const char* str, const String* problem_source) {  #define COMPILE_ASSERT(x) extern int assert_checker[(x) ? 1 : -1]
   COMPILE_ASSERT(sizeof(String::Languages) == sizeof(CORD));
   
   // pa_atoui is based on Manuel Novoa III _strto_l for uClibc
   
   template<typename T> inline T pa_ato_any(const char *str, int base, const String* problem_source,const T max){
           T result = 0;
           const char *pos = str;
   
           while (isspace(*pos)) /* skip leading whitespace */
                   ++pos;
   
           if (base == 16 && *pos == '0') { /* handle option prefix */
                   ++pos;
                   if (*pos == 'x' || *pos == 'X') {
                           ++pos;
                   }
           }
   
           if (base == 0) { /* dynamic base */
                   base = 10; /* default is 10 */
                   if (*pos == '0') {
                           ++pos;
                           if (*pos == 'x' || *pos == 'X'){
                                   ++pos;
                                   base=16;
                           }
                   }
           }
   
           if (base < 2 || base > 16) { /* illegal base */
                   throw Exception(PARSER_RUNTIME, 0, "base to must be an integer from 2 to 16");
           }
   
           T cutoff = max / base;
           int cutoff_digit = (int)(max - cutoff * base);
   
           while(true) {
                   int digit;
                   
                   if ((*pos >= '0') && (*pos <= '9')) {
                           digit = (*pos - '0');
                   } else if (*pos >= 'a') {
                           digit = (*pos - 'a' + 10);
                   } else if (*pos >= 'A') {
                           digit = (*pos - 'A' + 10);
                   } else break;
   
                   if (digit >= base) {
                           break;
                   }
                   
                   ++pos;
                   
                   /* adjust number, with overflow check */
                   if ((result > cutoff) || ((result == cutoff) && (digit > cutoff_digit))) {
                           throw Exception("number.format", problem_source, problem_source ? "out of range (int)" : "'%s' is out of range (int)", str);
                   } else {
                           result  = result * base + digit;
                   }
           }
   
           while(char c=*pos++)
                   if(!isspace(c))
                           throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is invalid number (int)", str);
   
           return result;
   }
   
   unsigned int pa_atoui(const char *str, int base, const String* problem_source){
           if(!str)
                   return 0;
   
           return pa_ato_any<unsigned int>(str, base, problem_source, UINT_MAX);
   }
   
   uint64_t pa_atoul(const char *str, int base, const String* problem_source){
           if(!str)
                   return 0;
   
           return pa_ato_any<uint64_t>(str, base, problem_source, ULLONG_MAX);
   }
   
   int pa_atoi(const char* str, int base, const String* problem_source) {
         if(!str)          if(!str)
                 return 0;                  return 0;
   
         while(*str && isspace((unsigned char)*str))          while(isspace(*str))
                 str++;                  str++;
   
         if(!*str)          if(!*str)
                 return 0;                  return 0;
   
         int result;  
         char *error_pos;  
         bool negative=false;          bool negative=false;
         if(str[0]=='-') {          if(str[0]=='-') {
                 negative=true;                  negative=true;
Line 34  int pa_atoi(const char* str, const Strin Line 116  int pa_atoi(const char* str, const Strin
         } else if(str[0]=='+') {          } else if(str[0]=='+') {
                 str++;                  str++;
         }          }
         // 0xABC  
         if(str[0]=='0')  
                 if(str[1]=='x' || str[1]=='X')  
                         result=(int)(unsigned long)strtol(str, &error_pos, 0);  
                 else {  
                          // skip leading 0000, to disable octal interpretation  
                         do str++; while(*str=='0');                               
                         result=(int)strtol(str, &error_pos, 0);  
                 }  
         else  
                 result=(int)strtol(str, &error_pos, 0);  
         if(negative)  
                 result=-result;  
   
         while(char c=*error_pos++)          unsigned int result=pa_atoui(str, base, problem_source);
                 if(!isspace((unsigned char)c))  
                         throw Exception("number.format",  
                                 problem_source,  
                                 problem_source?"invalid number (int)": "'%s' is invalid number (int)", str);  
   
         return result;          if(negative && result <= ((unsigned int)(-(1+INT_MIN)))+1)
                   return -(int)result;
           
           if(result<=INT_MAX)
                   return (int)result;
           
           throw Exception("number.format", problem_source, problem_source ? "out of range (int)" : "'%s' is out of range (int)", str);
 }  }
   
 double pa_atod(const char* str, const String* problem_source) {  double pa_atod(const char* str, const String* problem_source) {
         if(!str)          if(!str)
                 return 0;                  return 0;
   
         while(*str && isspace((unsigned char)*str))          while(isspace(*str))
                 str++;                  str++;
   
         if(!*str)          if(!*str)
                 return 0;                  return 0;
   
         double result;  
         char *error_pos;  
         bool negative=false;          bool negative=false;
         if(str[0]=='-') {          if(str[0]=='-') {
                 negative=true;                  negative=true;
Line 75  double pa_atod(const char* str, const St Line 145  double pa_atod(const char* str, const St
         } else if(str[0]=='+') {          } else if(str[0]=='+') {
                 str++;                  str++;
         }          }
         // 0xABC  
         if(str[0]=='0')          double result;
                 if(str[1]=='x' || str[1]=='X')          if(str[0]=='0') {
                         result=(double)(unsigned long)strtol(str, &error_pos, 0);                  if(str[1]=='x' || str[1]=='X') {
                 else {                          // 0xABC
                           result=(double)pa_atoul(str, 0, problem_source);
                           return negative ? -result : result;
                   } else {
                          // skip leading 0000, to disable octal interpretation                           // skip leading 0000, to disable octal interpretation
                         do str++; while(*str=='0');                                                       do str++; while(*str=='0');
                         result=(double)strtod(str, &error_pos);  
                 }                  }
         else          }
                 result=(double)strtod(str, &error_pos);  
         if(negative)          char *error_pos;
                 result=-result;          result=strtod(str, &error_pos);
   
         while(char c=*error_pos++)          while(char c=*error_pos++)
                 if(!isspace((unsigned char)c))                  if(!isspace((unsigned char)c))
                         throw Exception("number.format",                          throw Exception("number.format", problem_source, problem_source ? "invalid number (double)" : "'%s' is invalid number (double)", str);
                                 problem_source,  
                                 problem_source?"invalid number (double)": "'%s' is invalid number (double)", str);  
   
         return result;          return negative ? -result : result;
 }  }
   
 // cord lib extension  // cord lib extension
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 typedef struct {  typedef struct {
     ssize_t countdown;          ssize_t countdown;
     int target; /* Character we're looking for  */          int target;     /* Character we're looking for  */
 } chr_data;  } chr_data;
 #endif  #endif
   
 static int CORD_range_contains_chr_greater_then_proc(char c, size_t size, void* client_data)  static int CORD_range_contains_chr_greater_then_proc(char c, size_t size, void* client_data)
 {  {
     register chr_data * d = (chr_data *)client_data;          register chr_data * d = (chr_data *)client_data;
       
     if (d -> countdown<=0) return(2);          if (d -> countdown<=0) return(2);
     d -> countdown -= size;          d -> countdown -= size;
     if (c > d -> target) return(1);          if (c > d -> target) return(1);
     return(0);          return(0);
 }  }
   
 int CORD_range_contains_chr_greater_then(CORD x, size_t i, size_t n, int c)  int CORD_range_contains_chr_greater_then(CORD x, size_t i, size_t n, int c)
 {  {
     chr_data d;          chr_data d;
   
     d.countdown = n;          d.countdown = n;
     d.target = c;          d.target = c;
     return(CORD_block_iter(x, i, CORD_range_contains_chr_greater_then_proc, &d) == 1/*alternatives: 0 normally ended, 2=struck 'n'*/);          return(CORD_block_iter(x, i, CORD_range_contains_chr_greater_then_proc, &d) == 1/*alternatives: 0 normally ended, 2=struck 'n'*/);
 }  }
   
 static int CORD_block_count_proc(char /*c*/, size_t /*size*/, void* client_data)  static int CORD_block_count_proc(char /*c*/, size_t /*size*/, void* client_data)
 {  {
     int* result=(int*)client_data;          int* result=(int*)client_data;
     (*result)++;          (*result)++;
     return(0); // 0=continue          return(0); // 0=continue
 }  }
   
 size_t CORD_block_count(CORD x)  size_t CORD_block_count(CORD x)
 {  {
         size_t result=0;          size_t result=0;
         CORD_block_iter(x, 0, CORD_block_count_proc, &result);          CORD_block_iter(x, 0, CORD_block_count_proc, &result);
     return result;          return result;
 }  }
   
 // helpers  // helpers
Line 165  String::Body String::Body::Format(int va Line 238  String::Body String::Body::Format(int va
         return String::Body(pa_strdup(local, length));          return String::Body(pa_strdup(local, length));
 }  }
   
 String::Body String::Body::trim(String::Trim_kind kind, const char* chars,   String::Body String::Body::trim(String::Trim_kind kind, const char* chars, size_t* out_start, size_t* out_length, Charset* source_charset) const {
                                                                 size_t* out_start, size_t* out_length, Charset* source_charset) const {  
         size_t our_length=length();          size_t our_length=length();
         if(!our_length)          if(!our_length)
                 return *this;                  return *this;
Line 288  static int CORD_batched_iter_fn_generic_ Line 360  static int CORD_batched_iter_fn_generic_
         generic_hash_code(result, c);          generic_hash_code(result, c);
         return 0;          return 0;
 }  }
   
 static int CORD_batched_iter_fn_generic_hash_code(const char*  s, void * client_data) {  static int CORD_batched_iter_fn_generic_hash_code(const char*  s, void * client_data) {
         uint& result=*static_cast<uint*>(client_data);          uint& result=*static_cast<uint*>(client_data);
         generic_hash_code(result, s);          generic_hash_code(result, s);
         return 0;          return 0;
 };  }
   
 uint String::Body::get_hash_code() const {  uint String::Body::get_hash_code() const {
 #ifdef HASH_CODE_CACHING  #ifdef HASH_CODE_CACHING
         if(hash_code)          if(hash_code)
Line 301  uint String::Body::get_hash_code() const Line 375  uint String::Body::get_hash_code() const
         uint hash_code=0;          uint hash_code=0;
 #endif  #endif
         if (body && CORD_IS_STRING(body)){          if (body && CORD_IS_STRING(body)){
                 generic_hash_code(hash_code, body);                  generic_hash_code(hash_code, (const char *)body);
         } else {          } else {
                 CORD_iter5(body, 0,                  CORD_iter5(body, 0,
                         CORD_batched_iter_fn_generic_hash_code,                           CORD_batched_iter_fn_generic_hash_code, 
Line 310  uint String::Body::get_hash_code() const Line 384  uint String::Body::get_hash_code() const
         return hash_code;          return hash_code;
 }  }
   
   struct CORD_pos_info {
           const char* chars;
           size_t left;
           size_t pos;
   };
   
   // can be called only for IS_FUNCTION(CORD) which is used in String::Body::strrpbrk
   static int CORD_iter_fn_rpos(char c, CORD_pos_info* info) {
           if(info->pos < info->left){
                   info->pos=STRING_NOT_FOUND;
                   return 1;
           }
           if(strchr(info->chars, c))
                   return 1;
           --(info->pos);
           return 0;
   }
   
   size_t String::Body::strrpbrk(const char* chars, size_t left, size_t right) const {
           if(is_empty() || !chars || !strlen(chars))
                   return STRING_NOT_FOUND;
           CORD_pos_info info={chars, left, right};
           if(CORD_riter4(body, right, (CORD_iter_fn)CORD_iter_fn_rpos, &info))
                   return info.pos;
           else
                   return STRING_NOT_FOUND;
   }
   
   
   // can be called only for IS_FUNCTION(CORD) which is used in String::Body::rskipchars
   static int CORD_iter_fn_rskip(char c, CORD_pos_info* info) {
           if(info->pos < info->left) {
                   info->pos=STRING_NOT_FOUND;
                   return 1;
           }
           if(!strchr(info->chars, c))
                   return 1;
           --(info->pos);
           return 0;
   }
   
   size_t String::Body::rskipchars(const char* chars, size_t left, size_t right) const {
           if(is_empty() || !chars || !strlen(chars))
                   return STRING_NOT_FOUND;
           CORD_pos_info info={chars, left, right};
           if(CORD_riter4(body, right, (CORD_iter_fn)CORD_iter_fn_rskip, &info))
                   return info.pos;
           else
                   return STRING_NOT_FOUND;
   }
   
 // String methods  // String methods
   
 String& String::append_know_length(const char* str, size_t known_length, Language lang) {  String& String::append_know_length(const char* str, size_t known_length, Language lang) {
Line 324  String& String::append_know_length(const Line 449  String& String::append_know_length(const
         ASSERT_STRING_INVARIANT(*this);          ASSERT_STRING_INVARIANT(*this);
         return *this;          return *this;
 }  }
   
 String& String::append_help_length(const char* str, size_t helper_length, Language lang) {  String& String::append_help_length(const char* str, size_t helper_length, Language lang) {
         if(!str)          if(!str)
                 return *this;                  return *this;
Line 333  String& String::append_help_length(const Line 459  String& String::append_help_length(const
   
         return append_know_length(str, known_length, lang);          return append_know_length(str, known_length, lang);
 }  }
 String::String(int value, char *format) : langs(L_CLEAN){  
   String::String(int value, const char *format) : langs(L_CLEAN){
         char buf[MAX_NUMBER];          char buf[MAX_NUMBER];
         body.append_strdup_know_length(buf, snprintf(buf, MAX_NUMBER, format, value));          body.append_strdup_know_length(buf, snprintf(buf, MAX_NUMBER, format, value));
 }  }
   
 String& String::append_strdup(const char* str, size_t helper_length, Language lang) {  String& String::append_strdup(const char* str, size_t helper_length, Language lang) {
         size_t known_length=helper_length?helper_length:strlen(str);          size_t known_length=helper_length?helper_length:strlen(str);
         if(!known_length)          if(!known_length)
Line 357  struct CORD_length_info { Line 485  struct CORD_length_info {
 };  };
   
 int CORD_batched_len(const char* s, CORD_length_info* info){  int CORD_batched_len(const char* s, CORD_length_info* info){
         info->len += lengthUTF8( (const XMLByte *)s, (const XMLByte *)s+strlen(s));     return 0;          info->len += lengthUTF8( (const XMLByte *)s, (const XMLByte *)s+strlen(s));
           return 0;
 }  }
   
 // can be called only for IS_FUNCTION(CORD) which are used in large String::Body::mid  // can be called only for IS_FUNCTION(CORD) which are used in large String::Body::mid
Line 404  String& String::mid(size_t substr_begin, Line 533  String& String::mid(size_t substr_begin,
 String& String::mid(Charset& charset, size_t from, size_t to, size_t helper_length) const {  String& String::mid(Charset& charset, size_t from, size_t to, size_t helper_length) const {
         String& result=*new String;          String& result=*new String;
   
         size_t self_length=(helper_length)?helper_length:length(charset);          size_t self_length=helper_length ? helper_length : length(charset);
   
         if(!self_length)          if(!self_length)
                 return result;                  return result;
Line 452  size_t String::pos(const String::Body su Line 581  size_t String::pos(const String::Body su
         }          }
 }  }
   
 size_t String::pos(const String& substr,   size_t String::pos(const String& substr, size_t this_offset, Language lang) const {
                                 size_t this_offset, Language lang) const {  
         return pos(substr.body, this_offset, lang);          return pos(substr.body, this_offset, lang);
 }  }
   
 size_t String::pos(Charset& charset, const String& substr,   size_t String::pos(Charset& charset, const String& substr, size_t this_offset, Language lang) const {
                                 size_t this_offset, Language lang) const {  
   
         if(charset.isUTF8()){          if(charset.isUTF8()){
                 const XMLByte* srcPtr=(const XMLByte*)cstr();                  const XMLByte* srcPtr=(const XMLByte*)cstr();
Line 479  size_t String::pos(Charset& charset, con Line 606  size_t String::pos(Charset& charset, con
         }          }
 }  }
   
 void String::split(ArrayString& result,   void String::split(ArrayString& result, size_t pos_after, const char* delim, Language lang) const {
                    size_t& pos_after,           if(is_empty())
                    const char* delim,                   return;
                    Language lang, int limit) const {  
         size_t self_length=length();          size_t self_length=length();
         if(size_t delim_length=strlen(delim) && !is_empty()) {          if(size_t delim_length=strlen(delim)) {
                 size_t pos_before;                  size_t pos_before;
                 // while we have 'delim'...                  // while we have 'delim'...
                 for(; (pos_before=pos(delim, pos_after, lang))!=STRING_NOT_FOUND && limit; limit--) {                  while((pos_before=pos(delim, pos_after, lang))!=STRING_NOT_FOUND) {
                         result+=&mid(pos_after, pos_before);                          result+=&mid(pos_after, pos_before);
                         pos_after=pos_before+delim_length;                          pos_after=pos_before+delim_length;
                 }                  }
                 // last piece                  // last piece
                 if(pos_after<self_length && limit) {                  if(pos_after<self_length)
                         result+=&mid(pos_after, self_length);                          result+=&mid(pos_after, self_length);
                         pos_after=self_length;  
                 }  
         } else { // empty delim          } else { // empty delim
                 result+=this;                  result+=this;
                 pos_after+=self_length;  
         }          }
 }  }
   
 void String::split(ArrayString& result,   void String::split(ArrayString& result, size_t pos_after, const String& delim, Language lang) const {
                    size_t& pos_after,           if(is_empty())
                    const String& delim, Language lang,                   return;
                    int limit) const {          if(!delim.is_empty()) {
         if(!delim.is_empty() && !is_empty()) {  
                 size_t pos_before;                  size_t pos_before;
                 // while we have 'delim'...                  // while we have 'delim'...
                 for(; (pos_before=pos(delim, pos_after, lang))!=STRING_NOT_FOUND && limit; limit--) {                  while((pos_before=pos(delim, pos_after, lang))!=STRING_NOT_FOUND) {
                         result+=&mid(pos_after, pos_before);                          result+=&mid(pos_after, pos_before);
                         pos_after=pos_before+delim.length();                          pos_after=pos_before+delim.length();
                 }                  }
                 // last piece                  // last piece
                 if(pos_after<length() && limit) {                  if(pos_after<length())
                         result+=&mid(pos_after, length());                          result+=&mid(pos_after, length());
                         pos_after=length();  
                 }  
         } else { // empty delim          } else { // empty delim
                 result+=this;                  result+=this;
                 pos_after+=length();  
         }          }
 }  }
   
 Table* String::match(VRegex* vregex,  Table* String::match(VRegex* vregex, Row_action row_action, void *info, int& matches_count) const {
                         Row_action row_action, void *info,  
                         int& matches_count) const {   
   
         // vregex->info(); // I have no idea what does it for?          // vregex->info(); // I have no idea what does it for?
   
Line 544  Table* String::match(VRegex* vregex, Line 661  Table* String::match(VRegex* vregex,
         int prestart=0;          int prestart=0;
         int poststart=0;          int poststart=0;
         int postfinish=length();          int postfinish=length();
           int action_was_executed=-1;
         while(true) {          while(true) {
                 int exec_result=vregex->exec(subject, subject_length, ovector, ovector_size, prestart);                  int exec_result=vregex->exec(subject, subject_length, ovector, ovector_size, prestart);
   
Line 553  Table* String::match(VRegex* vregex, Line 671  Table* String::match(VRegex* vregex,
                 int prefinish=ovector[0];                  int prefinish=ovector[0];
                 poststart=ovector[1];                  poststart=ovector[1];
   
                 if (prestart==poststart && subject[poststart]=='\n'){                  if (prestart==poststart && action_was_executed==1){
                         prestart++;                          prestart++;
                           action_was_executed=0;
                         continue;                          continue;
                 }                  }
   
Line 575  Table* String::match(VRegex* vregex, Line 694  Table* String::match(VRegex* vregex,
                 }                  }
                                   
                 matches_count++;                  matches_count++;
                 row_action(table, row, prestart, prefinish, poststart, postfinish, info);                  row_action(table, row, prestart - !action_was_executed, prefinish, poststart, postfinish, info);
   
                 if(!global || prestart==poststart) // last step                  if(!global) // last step
                         break;                          break;
   
                 prestart=poststart;                  prestart=poststart;
                   action_was_executed=1;
         }          }
   
         row_action(table, 0/*last time, no raw*/, 0, 0, poststart, postfinish, info);          row_action(table, 0/*last time, no raw*/, 0, 0, poststart, postfinish, info);
Line 651  const String& String::escape(Charset& so Line 771  const String& String::escape(Charset& so
         return Charset::escape(*this, source_charset);          return Charset::escape(*this, source_charset);
 }  }
   
   #define STRING_APPEND(result, from_cstr, langs, langs_offset, length) \
                           result.langs.append(result.body, langs, langs_offset, length); \
                           result.body.append_strdup_know_length(from_cstr, length);
   
 const String& String::replace(const Dictionary& dict) const {  const String& String::replace(const Dictionary& dict) const {
           if(!dict.count() || is_empty())
                   return *this;
   
         String& result=*new String();          String& result=*new String();
         const char* old_cstr=cstr();          const char* old_cstr=cstr();
         const char* prematch_begin=old_cstr;          const char* prematch_begin=old_cstr;
   
         const char* current=old_cstr;          if(dict.count()==1) {
         while(*current) {                  // optimized simple case
                 if(Dictionary::Subst subst=dict.first_that_begins(current)) {  
                   Dictionary::Subst subst=dict.get(0);
                   while(const char* p=strstr(prematch_begin, subst.from)) {
                         // prematch                          // prematch
                         if(size_t prematch_length=current-prematch_begin) {                          if(size_t prematch_length=p-prematch_begin) {
                                 result.langs.append(result.body, langs, prematch_begin-old_cstr, prematch_length);                                  STRING_APPEND(result, prematch_begin, langs, prematch_begin-old_cstr, prematch_length)
                                 result.body.append_strdup_know_length(prematch_begin, prematch_length);  
                         }                          }
   
                         // match                          // match
                         // skip 'a' in 'current'; move prematch_begin                          prematch_begin=p+subst.from_length;
                         current+=subst.from_length; prematch_begin=current;  
   
                         if(const String* b=subst.to) // are there any b?                          if(const String* b=subst.to) // are there any b?
                                 result<<*b;                                  result<<*b;
                 } else // simply advance                  }
                         current++;   
           } else {
   
                   const char* current=old_cstr;
                   while(*current) {
                           if(Dictionary::Subst subst=dict.first_that_begins(current)) {
                                   // prematch
                                   if(size_t prematch_length=current-prematch_begin) {
                                           STRING_APPEND(result, prematch_begin, langs, prematch_begin-old_cstr, prematch_length)
                                   }
   
                                   // match
                                   // skip 'a' in 'current'; move prematch_begin
                                   current+=subst.from_length; prematch_begin=current;
   
                                   if(const String* b=subst.to) // are there any b?
                                           result<<*b;
                           } else // simply advance
                                   current++; 
                   }
   
         }          }
   
           if(prematch_begin==old_cstr) // not modified
                   return *this;
   
         // postmatch          // postmatch
         if(size_t postmatch_length=current-prematch_begin) {          if(size_t postmatch_length=old_cstr+length()-prematch_begin) {
                 result.langs.append(result.body, langs, prematch_begin-old_cstr, postmatch_length);                  STRING_APPEND(result, prematch_begin, langs, prematch_begin-old_cstr, postmatch_length)
                 result.body.append_strdup_know_length(prematch_begin, postmatch_length);  
         }          }
   
         ASSERT_STRING_INVARIANT(result);          ASSERT_STRING_INVARIANT(result);
Line 688  const String& String::replace(const Dict Line 837  const String& String::replace(const Dict
 static int serialize_body_char(char c, char** cur) {  static int serialize_body_char(char c, char** cur) {
         *((*cur)++)=c;          *((*cur)++)=c;
         return 0; // 0=continue          return 0; // 0=continue
 };  }
   
 static int serialize_body_piece(const char* s, char** cur) {  static int serialize_body_piece(const char* s, char** cur) {
         size_t length=strlen(s);          size_t length=strlen(s);
         memcpy(*cur, s, length);  *cur+=length;          memcpy(*cur, s, length);  *cur+=length;
         return 0; // 0=continue          return 0; // 0=continue
 };  }
   
 static int serialize_lang_piece(char alang, size_t asize, char** cur) {  static int serialize_lang_piece(char alang, size_t asize, char** cur) {
         // lang          // lang
         **cur=alang; (*cur)++;          **cur=alang; (*cur)++;
Line 702  static int serialize_lang_piece(char ala Line 853  static int serialize_lang_piece(char ala
   
         return 0; // 0=continue          return 0; // 0=continue
 }  }
   
 String::Cm String::serialize(size_t prolog_length) const {  String::Cm String::serialize(size_t prolog_length) const {
         size_t fragments_count=langs.count();          size_t fragments_count=langs.count();
         size_t body_length=body.length();          size_t body_length=body.length();
Line 730  String::Cm String::serialize(size_t prol Line 882  String::Cm String::serialize(size_t prol
   
         return result;          return result;
 }  }
   
 bool String::deserialize(size_t prolog_size, void *buf, size_t buf_size) {  bool String::deserialize(size_t prolog_size, void *buf, size_t buf_size) {
         size_t in_buf=buf_size;          size_t in_buf=buf_size;
         if(in_buf<=prolog_size)          if(in_buf<=prolog_size)
Line 753  bool String::deserialize(size_t prolog_s Line 906  bool String::deserialize(size_t prolog_s
         if(cur[body_length] != 0) // in place?          if(cur[body_length] != 0) // in place?
                 return false;                  return false;
         // 3: letters          // 3: letters
         body=String::Body(*cur?cur:0);          body=String::Body(String::C(cur, body_length));
 #ifdef STRING_LENGTH_CACHING  
         body.set_length(body_length);  
 #endif  
         cur+=body_length+1;          cur+=body_length+1;
         in_buf-=body_length+1;          in_buf-=body_length+1;
   
Line 802  bool String::deserialize(size_t prolog_s Line 952  bool String::deserialize(size_t prolog_s
         return true;          return true;
 }  }
   
 const char* String::Body::v() const {  
         return CORD_to_const_char_star(body, length());  
 }  
 void String::Body::dump() const {  void String::Body::dump() const {
         CORD_dump(body);          CORD_dump(body);
 }  }
   
 const char* String::Languages::v() const {  const char* String::Languages::visualize() const {
         if(opt.is_not_just_lang)          if(opt.is_not_just_lang)
                 return CORD_to_const_char_star(langs, 0);                  return CORD_to_const_char_star(langs, 0);
         else          else
                 return (const char*)&langs;                  return 0;
 }  }
   
 void String::Languages::dump() const {  void String::Languages::dump() const {
         if(opt.is_not_just_lang)          if(opt.is_not_just_lang)
                 CORD_dump(langs);                  CORD_dump(langs);
         else          else
                 puts((const char*)&langs);                  puts((const char*)&langs);
 }  }
 const char* String::v() const {  
         const uint LIMIT_VIEW=20;  
         char* buf=(char*)malloc(MAX_STRING);  
         const char*body_view=body.v();  
         const char*langs_view=langs.v();  
         snprintf(buf, MAX_STRING,   
                 "%d:%.*s%s}   "  
                 "{%d:%s",  
                 langs.count(), LIMIT_VIEW, langs_view, strlen(langs_view)>LIMIT_VIEW?"...":"",  
                 strlen(body_view), body_view  
         );  
   
         return buf;  
 }  
   
 void String::dump() const {  void String::dump() const {
         body.dump();          body.dump();
         langs.dump();          langs.dump();
 }  }
   
   static char *n_chars(char c, size_t length){
           char *result=(char *)pa_malloc_atomic(length+1);
           memset(result, c, length);
           result[length] = '\0';
           return result;
   }
   
   char* String::visualize_langs() const {
           return is_not_just_lang() ? pa_strdup(langs.visualize()) : n_chars((char)just_lang(), length());
   }
   
 const String& String::trim(String::Trim_kind kind, const char* chars, Charset* source_charset) const {  const String& String::trim(String::Trim_kind kind, const char* chars, Charset* source_charset) const {
         if(is_empty())          if(is_empty())
                 return *this;                  return *this;

Removed from v.1.236  
changed lines
  Added in v.1.267


E-mail: