--- parser3/src/main/pa_string.C 2002/04/19 11:59:44 1.156 +++ parser3/src/main/pa_string.C 2003/01/24 08:19:10 1.172.2.3 @@ -1,38 +1,51 @@ /** @file Parser: string class. @see untasize_t.C. - Copyright (c) 2001, 2002 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) - - $Id: pa_string.C,v 1.156 2002/04/19 11:59:44 paf Exp $ */ +static const char* IDENT_STRING_C="$Date: 2003/01/24 08:19:10 $"; + #include "pcre.h" #include "pa_pool.h" #include "pa_string.h" -#include "pa_hash.h" +//#include "pa_hash.h" #include "pa_exception.h" -#include "pa_common.h" +//#include "pa_common.h" #include "pa_array.h" -#include "pa_globals.h" +//#include "pa_globals.h" #include "pa_table.h" #include "pa_dictionary.h" #include "pa_charset.h" -#define DEBUG_STRING_APPENDS_VS_EXPANDS +// helpers +/// String::match uses this as replace & global search table columns +const int MAX_STRING_MATCH_TABLE_COLUMNS=100; -#ifdef DEBUG_STRING_APPENDS_VS_EXPANDS -ulong string_piece_appends=0; -#endif +class String_match_table_template_columns: public Array> { + char cn_cstr[MAX_STRING_MATCH_TABLE_COLUMNS][3/*strlen("100")*/+1/*terminating 0*/]; + String cn[MAX_STRING_MATCH_TABLE_COLUMNS]; +public: + string_match_table_template_columns_class() { + *this+=smart_ptr(new String("prematch")); + *this+=smart_ptr(new String("match")); + *this+=smart_ptr(new String("postmatch")); + for(int i=0; iitem.size; - ); - return result; -} - -/// @todo not very optimal -uint String::used_rows() const { - uint result=0; - STRING_FOREACH_ROW( - result++; - ); - return result; -} -void String::expand() { - uint new_chunk_count=last_chunk->count+CR_GROW_COUNT; - if(new_chunk_count>max_integral(Chunk::count_type)) - new_chunk_count=max_integral(Chunk::count_type); - - Chunk *new_chunk=static_cast(malloc( - sizeof(Chunk)// count+interpadding(?)+rows[CR_PREALLOCATED_COUNT]+tailpadding(??) - -sizeof(Chunk::rows_type) // PREALLOCATED rows - +sizeof(Chunk::Row)*new_chunk_count // neaded rows - +sizeof(Chunk *) // link size - , 10)); - new_chunk->rows[new_chunk->count=new_chunk_count].link=0; - last_chunk->rows[last_chunk->count].link=new_chunk; - - last_chunk=new_chunk; - append_here=last_chunk->rows; +String::String(const String& src): Array(src.count()) { + append(src, UL_PASS_APPENDED); } String& String::real_append(STRING_APPEND_PARAMS) { - if(!last_chunk) // growth stopped [we're appended as string to somebody] - throw Exception(0, - this, - "string growth stopped (append cstr)"); - if(!src) return *this; if(!size) @@ -96,52 +65,21 @@ String& String::real_append(STRING_APPEN if(!size) 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()) - expand(); + if(is_full()) + expand(fdelta); - append_here->item.ptr=src; - append_here->item.size=size; - append_here->item.lang=lang; + String_fragment *fragment=felements[fused++]; + fragment->item.ptr=src; + fragment->item.size=size; + fragment->item.lang=lang; #ifndef NO_STRING_ORIGIN - append_here->item.origin.file=file; - append_here->item.origin.line=line; + fragment->item.origin.file=file; + fragment->item.origin.line=line; #endif - append_here++; return *this; } -char String::first_char() const { - if(is_empty()) - throw Exception(0, - this, - "getting first char of empty string"); - - return *head.chunk.rows[0].item.ptr; -} - uint String::hash_code() const { uint result=0; STRING_FOREACH_ROW( @@ -329,11 +267,11 @@ const Origin& String::origin() const { } #endif -String& String::mid(size_t start, size_t finish) const { - String& result=*NEW String(pool()); +StringPtr String::mid(size_t start, size_t finish) const { + StringPtr result(new String()); - start=max(size_t(0), start); - finish=min(size(), finish); + start=min(start, size()); + finish=max(start, finish); if(start==finish) return result; @@ -457,7 +395,7 @@ static void regex_options(const String * {"'", 0, 0, 0, 0, &need_pre_post_match}, {0} }; - result[0]=PCRE_EXTRA | PCRE_DOTALL; + result[0]=PCRE_EXTRA | PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; result[1]=0; if(options) @@ -474,7 +412,7 @@ static void regex_options(const String * } /// @todo make replacement Table stacked -bool String::match(const char *acstr, +bool String::match(Charset& source_charset, const String *aorigin, const String& regexp, const String *options, @@ -496,7 +434,7 @@ bool String::match(const char *acstr, *was_global=option_bits[1]!=0; pcre *code=pcre_compile(pattern, option_bits[0], &errptr, &erroffset, - pool().get_source_charset().pcre_tables); + source_charset.pcre_tables); if(!code) throw Exception(0, @@ -512,13 +450,13 @@ bool String::match(const char *acstr, info_substrings); } - const char *subject=acstr?acstr:cstr(); + const char *subject=cstr(); int length=strlen(subject); const int ovecsize=(1/*match*/+MAX_STRING_MATCH_TABLE_COLUMNS)*3; int ovector[ovecsize]; // create table - *table=NEW Table(pool(), 0, string_match_table_columns); + *table=new Table(*string_match_table_template); int exec_option_bits=0; int prestart=0; @@ -545,7 +483,7 @@ bool String::match(const char *acstr, int prefinish=ovector[0]; poststart=ovector[1]; - Array& row=*NEW Array(pool()); + Array& row=*new Array(); row+=need_pre_post_match?&mid(0, prefinish):0; // .prematch column value row+=need_pre_post_match?&mid(prefinish, poststart):0; // .match row+=need_pre_post_match?&mid(poststart, postfinish):0; // .postmatch @@ -571,10 +509,9 @@ bool String::match(const char *acstr, } } -String& String::change_case(Pool& pool, - Change_case_kind kind) const { - const unsigned char *tables=pool.get_source_charset().pcre_tables; - String& result=*new(pool) String(pool); +String& String::change_case(Charset& source_charset, Change_case_kind kind) const { + const unsigned char *tables=source_charset.pcre_tables; + String& result=*new String(); const unsigned char *a; const unsigned char *b; @@ -597,7 +534,7 @@ String& String::change_case(Pool& pool, } STRING_FOREACH_ROW( - char *new_cstr=(char *)pool.malloc(row->item.size, 12); + char *new_cstr=(char *)pa_malloc(row->item.size); char *dest=new_cstr; const char *src=row->item.ptr; for(int size=row->item.size; size--; src++) { @@ -617,67 +554,62 @@ String& String::change_case(Pool& pool, } /// @test if in some piece were found no dict words, append it, not it's duplicate -String& String::replace(Pool& pool, Dictionary& dict) const { -// return reconstruct(pool).replace_in_reconstructed(pool, dict); - String& result=*new(pool) String(pool); +String& String::replace(Dictionary& dict) const { + char *lcstr=cstr(); + const char *current=lcstr; + String& result=*new String(); STRING_FOREACH_ROW( - const char *src=row->item.ptr; - size_t src_size=row->item.size; - char *new_cstr=(char *)pool.malloc((size_t)ceil(src_size*dict.max_ratio()), 14); +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; + + char *new_cstr=(char *)pa_malloc((size_t)ceil(joined_size*dict.max_ratio())); 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)) { + while(joined_size) { + // there is a row where first column starts 'joined_ptr' + if(Table::Item *item=dict.first_that_starts(joined_ptr, joined_size)) { // get a=>b values const String& a=*static_cast(item)->get_string(0); const String& b=*static_cast(item)->get_string(1); - // skip 'a' in 'src' && reduce work size - src+=a.size(); src_size-=a.size(); + // skip 'a' in 'joined_ptr' && reduce work size + joined_ptr+=a.size(); joined_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--; + *dest++=*joined_ptr++; joined_size--; } } - result.APPEND(new_cstr, dest-new_cstr, row->item.lang, - row->item.origin.file, row->item.origin.line); + result.APPEND(new_cstr, dest-new_cstr, joined_lang, + joined_origin_file, joined_origin_line); ); - return result; -} - -/// @test real! -bool String::is_join_chains_profitable() const { - // mimimum actually sizeof(String), - // but one must also consider CPU time optimize would eat - const int minimum_economy=sizeof(String)*2; - - size_t wasted=0; - STRING_FOREACH_ROW( - uchar joined_lang=row->item.lang; - STRING_PREPARED_FOREACH_ROW(*this, - if(row->item.lang==joined_lang) { - wasted+=sizeof(String::Chunk::rows_type); - if(wasted>minimum_economy) - return true; - } else - break; // before non-ours - ); - // pointers are after joined piece - // & one step back, see STRING_FOREACH_ROW - --row; ++countdown; - ); - return false; + return result; } -String& String::join_chains(Pool& pool, char** acstr) const { +String& String::join_chains(char** acstr) const { char *lcstr=cstr(); const char *current=lcstr; - String& result=*new(pool) String(pool); + String& result=*new String(); STRING_FOREACH_ROW( IFNDEF_NO_STRING_ORIGIN( const char *joined_origin_file=row->item.origin.file; @@ -719,6 +651,11 @@ double String::as_double() const { cstr=buf; } else cstr=this->cstr(); + while(*cstr && isspace(*cstr)) + cstr++; + if(!*cstr) + return 0; + char *error_pos; // 0xABC if(cstr[0]=='0') @@ -729,10 +666,11 @@ double String::as_double() const { else result=(double)strtod(cstr, &error_pos); - if(*error_pos/*not EOS*/) - throw Exception("number.format", - this, - "invalid number (double)"); + while(char c=*error_pos++) + if(!isspace(c)) + throw Exception("number.format", + this, + "invalid number (double)"); return result; } @@ -741,12 +679,17 @@ int String::as_int() const { const char *cstr; char buf[MAX_NUMBER]; if(head.chunk.rows+1==append_here) { - int size=min(head.chunk.rows[0].item.size, MAX_NUMBER-1); + size_t size=min(head.chunk.rows[0].item.size, MAX_NUMBER-1); memcpy(buf, head.chunk.rows[0].item.ptr, size); buf[size]=0; cstr=buf; } else cstr=this->cstr(); + while(*cstr && isspace(*cstr)) + cstr++; + if(!*cstr) + return 0; + char *error_pos; // 0xABC if(cstr[0]=='0') @@ -757,10 +700,11 @@ int String::as_int() const { else result=(int)strtol(cstr, &error_pos, 0); - if(*error_pos/*not EOS*/) - throw Exception("number.format", - this, - "invalid number (int)"); + while(char c=*error_pos++) + if(!isspace(c)) + throw Exception("number.format", + this, + "invalid number (int)"); return result; } @@ -782,7 +726,7 @@ void String::serialize(size_t prolog_siz prolog_size +used_rows()*(sizeof(uchar)+sizeof(ushort)) +size(); - buf=malloc(buf_size,15); + buf=pa_malloc(buf_size); char *cur=(char *)buf+prolog_size; STRING_FOREACH_ROW(