Annotation of parser3/src/main/pa_string.C, revision 1.62

1.45      paf         1: /** @file
1.55      paf         2:        Parser: string class. @see untasize_t.C.
1.46      paf         3: 
1.36      paf         4:        Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)
1.46      paf         5: 
1.37      paf         6:        Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)
1.36      paf         7: 
1.62    ! paf         8:        $Id: pa_string.C,v 1.61 2001/04/03 08:23:08 paf Exp $
1.4       paf         9: */
                     10: 
1.48      paf        11: #include "pa_config_includes.h"
1.1       paf        12: 
1.62    ! paf        13: #include <locale.h>
        !            14: 
1.13      paf        15: #include "pa_pool.h"
1.12      paf        16: #include "pa_string.h"
1.5       paf        17: #include "pa_hash.h"
1.22      paf        18: #include "pa_exception.h"
1.53      paf        19: #include "pa_common.h"
1.60      paf        20: #include "pa_array.h"
                     21: #include "pa_globals.h"
1.61      paf        22: #include "pa_table.h"
1.62    ! paf        23: #include "pa_threads.h"
        !            24: 
        !            25: #include "pcre.h"
1.60      paf        26: 
                     27: //#include "pa_sapi.h"
1.1       paf        28: 
1.62    ! paf        29: // consts
        !            30: 
        !            31: const int MAX_MATCH_COLUMNS=20;
        !            32: 
1.18      paf        33: // String
                     34: 
1.55      paf        35: String::String(Pool& apool, const char *src, bool tasize_ted) :
1.17      paf        36:        Pooled(apool) {
1.28      paf        37:        last_chunk=&head;
                     38:        head.count=CR_PREALLOCATED_COUNT;
1.5       paf        39:        append_here=head.rows;
1.2       paf        40:        head.preallocated_link=0;
1.28      paf        41:        link_row=&head.rows[head.count];
1.8       paf        42:        fused_rows=fsize=0;
1.41      paf        43: 
                     44:        if(src)
1.55      paf        45:                if(tasize_ted)
1.41      paf        46:                        APPEND_TAINTED(src, 0, 0, 0);
                     47:                else
1.53      paf        48:                        APPEND_CONST(src);
1.1       paf        49: }
                     50: 
                     51: void String::expand() {
1.55      paf        52:        size_t new_chunk_count=last_chunk->count+last_chunk->count*CR_GROW_PERCENT/100;
1.28      paf        53:        last_chunk=static_cast<Chunk *>(
1.55      paf        54:                malloc(sizeof(size_t)+sizeof(Chunk::Row)*new_chunk_count+sizeof(Chunk *)));
1.28      paf        55:        last_chunk->count=new_chunk_count;
                     56:        link_row->link=last_chunk;
                     57:        append_here=last_chunk->rows;
                     58:        link_row=&last_chunk->rows[last_chunk->count];
1.8       paf        59:        link_row->link=0;
1.1       paf        60: }
                     61: 
1.40      paf        62: String::String(const String& src) :    Pooled(src.pool()) {
1.8       paf        63:        head.count=CR_PREALLOCATED_COUNT;
                     64:        
1.55      paf        65:        size_t src_used_rows=src.fused_rows;
1.8       paf        66:        if(src_used_rows<=head.count) {
1.55      paf        67:                // all new rows fit size_to preallocated area
                     68:                size_t curr_chunk_rows=head.count;
1.8       paf        69:                memcpy(head.rows, src.head.rows, sizeof(Chunk::Row)*src_used_rows);
                     70:                append_here=&head.rows[src_used_rows];
                     71:                link_row=&head.rows[curr_chunk_rows];
                     72:        } else {
                     73:                // warning: 
1.10      paf        74:                //   heavily relies on the fact 
                     75:                //   "preallocated area is the same for all strings"
1.8       paf        76:                //
                     77:                // info:
                     78:                //   allocating only enough mem to fit src string rows
                     79:                //   next append would allocate a new chunk
                     80:                //
1.55      paf        81:                // new rows don't fit size_to preallocated area: splitting size_to two chunks
1.8       paf        82:                // preallocated chunk src to constructing head
                     83:                memcpy(head.rows, src.head.rows, sizeof(Chunk::Row)*head.count);
1.55      paf        84:                // remaining rows size_to new_chunk
                     85:                size_t curr_chunk_rows=src_used_rows-head.count;
1.8       paf        86:                Chunk *new_chunk=static_cast<Chunk *>(
1.55      paf        87:                        malloc(sizeof(size_t)+sizeof(Chunk::Row)*curr_chunk_rows+sizeof(Chunk *)));
1.8       paf        88:                new_chunk->count=curr_chunk_rows;
                     89:                head.preallocated_link=new_chunk;
1.28      paf        90:                append_here=link_row=&new_chunk->rows[new_chunk->count];
1.8       paf        91: 
                     92:                Chunk *old_chunk=src.head.preallocated_link; 
                     93:                Chunk::Row *new_rows=new_chunk->rows;
1.55      paf        94:                size_t rows_left_to_copy=new_chunk->count;
1.8       paf        95:                while(true) {
1.55      paf        96:                        size_t old_count=old_chunk->count;
1.8       paf        97:                        Chunk *next_chunk=old_chunk->rows[old_count].link;
                     98:                        if(next_chunk) {
                     99:                                // not last source chunk
                    100:                                // taking it all
                    101:                                memcpy(new_rows, old_chunk->rows, sizeof(Chunk::Row)*old_count);
                    102:                                new_rows+=old_count;
                    103:                                rows_left_to_copy-=old_count;
                    104: 
                    105:                                old_chunk=next_chunk;
                    106:                        } else {
                    107:                                // the last source chunk
                    108:                                // taking only those rows of chunk that _left_to_copy
                    109:                                memcpy(new_rows, old_chunk->rows, sizeof(Chunk::Row)*rows_left_to_copy);
                    110:                                break;
                    111:                        }
                    112:                }
1.5       paf       113:        }
1.8       paf       114:        link_row->link=0;
                    115:        fused_rows=src_used_rows;
                    116:        fsize=src.fsize;
1.5       paf       117: }
1.28      paf       118: 
1.42      paf       119: String& String::append(const String& src, Untaint_lang lang, bool forced) {
1.60      paf       120:        const Chunk *chunk=&src.head; 
1.40      paf       121:        do {
1.60      paf       122:                const Chunk::Row *row=chunk->rows;
                    123:                for(size_t i=0; i<chunk->count; i++, row++) {
                    124:                        if(row==src.append_here)
1.40      paf       125:                                goto break2;
1.60      paf       126:                        
                    127:                        APPEND(row->item.ptr, row->item.size, 
                    128:                                (lang!=UL_PASS_APPENDED && (row->item.lang==UL_TAINTED || forced))?lang:row->item.lang,
                    129:                                row->item.origin.file, row->item.origin.line);
1.40      paf       130:                }
                    131:                chunk=row->link;
                    132:        } while(chunk);
                    133: break2:
1.60      paf       134:        return *this;
1.34      paf       135: }
1.60      paf       136: 
1.13      paf       137: String& String::real_append(STRING_APPEND_PARAMS) {
1.9       paf       138:        if(!src)
                    139:                return *this;
1.26      paf       140:        if(!size)
                    141:                size=strlen(src);
                    142:        if(!size)
1.9       paf       143:                return *this;
                    144: 
1.1       paf       145:        if(chunk_is_full())
                    146:                expand();
                    147: 
                    148:        append_here->item.ptr=src;
1.26      paf       149:        fsize+=append_here->item.size=size;
1.52      paf       150:        append_here->item.lang=lang;
1.13      paf       151: #ifndef NO_STRING_ORIGIN
1.14      paf       152:        append_here->item.origin.file=file;
                    153:        append_here->item.origin.line=line;
1.13      paf       154: #endif
1.8       paf       155:        append_here++; fused_rows++;
1.1       paf       156: 
                    157:        return *this;
                    158: }
                    159: 
1.16      paf       160: uint String::hash_code() const {
1.7       paf       161:        uint result=0;
1.5       paf       162: 
1.16      paf       163:        const Chunk *chunk=&head; 
1.5       paf       164:        do {
1.16      paf       165:                const Chunk::Row *row=chunk->rows;
1.55      paf       166:                for(size_t i=0; i<chunk->count; i++) {
1.5       paf       167:                        if(row==append_here)
                    168:                                goto break2;
                    169: 
1.6       paf       170:                        result=Hash::generic_code(result, row->item.ptr, row->item.size);
1.5       paf       171:                        row++;
                    172:                }
                    173:                chunk=row->link;
                    174:        } while(chunk);
                    175: break2:
                    176:        return result;
                    177: }
                    178: 
1.60      paf       179: /// @todo move 'lang' skipping to pos
                    180: int String::cmp(int& partial, const String& src, 
                    181:                                size_t this_offset, Untaint_lang lang) const {
1.59      paf       182:        partial=-1;
1.55      paf       183:        this_offset=min(this_offset, size()-1);
                    184: 
1.16      paf       185:        const Chunk *a_chunk=&head;
                    186:        const Chunk *b_chunk=&src.head;
                    187:        const Chunk::Row *a_row=a_chunk->rows;
                    188:        const Chunk::Row *b_row=b_chunk->rows;
1.55      paf       189:        size_t a_offset=this_offset;
                    190:        size_t b_offset=0;
1.9       paf       191:        Chunk::Row *a_end=append_here;
                    192:        Chunk::Row *b_end=src.append_here;
1.55      paf       193:        size_t a_countdown=a_chunk->count;
                    194:        size_t b_countdown=b_chunk->count;
1.9       paf       195:        bool a_break=false;
                    196:        bool b_break=false;
1.55      paf       197:        size_t result;
1.60      paf       198:        size_t pos=0; 
                    199:        while(true) {
1.33      paf       200:                a_break=a_row==a_end;
                    201:                b_break=b_row==b_end;
                    202:                if(a_break || b_break)
                    203:                        break;
                    204: 
1.55      paf       205:                if(pos+a_row->item.size > this_offset) {
1.60      paf       206:                        if(lang!=UL_UNKNOWN && a_row->item.lang!=lang) 
                    207:                                return -1; // wrong lang -- bail out
                    208: 
1.55      paf       209:                        int size_diff=
                    210:                                (a_row->item.size-a_offset)-
                    211:                                (b_row->item.size-b_offset);
                    212:                        
                    213:                        if(size_diff==0) { // a has same size as b
1.60      paf       214:                                result=memcmp(a_row->item.ptr+a_offset, b_row->item.ptr+b_offset, 
                    215:                                        a_row->item.size-a_offset);
1.55      paf       216:                                if(result)
                    217:                                        return result;
1.60      paf       218:                                pos+=a_row->item.size;
1.55      paf       219:                                a_row++; a_countdown--; a_offset=0;
                    220:                                b_row++; b_countdown--; b_offset=0;
                    221:                        } else if (size_diff>0) { // a longer
1.60      paf       222:                                result=memcmp(a_row->item.ptr+a_offset, b_row->item.ptr+b_offset, 
                    223:                                        b_row->item.size-b_offset);
1.55      paf       224:                                if(result)
                    225:                                        return result;
                    226:                                a_offset+=b_row->item.size-b_offset;
                    227:                                b_row++; b_countdown--; b_offset=0;
                    228:                        } else { // b longer
1.60      paf       229:                                result=memcmp(a_row->item.ptr+a_offset, b_row->item.ptr+b_offset, 
                    230:                                        a_row->item.size-a_offset);
1.55      paf       231:                                if(result)
                    232:                                        return result;
                    233:                                b_offset+=a_row->item.size-a_offset;
1.60      paf       234:                                pos+=a_row->item.size;
1.55      paf       235:                                a_row++; a_countdown--; a_offset=0;
                    236:                        }
1.60      paf       237:                        
1.55      paf       238:                        if(!b_countdown) {
                    239:                                b_chunk=b_row->link;
                    240:                                b_row=b_chunk->rows;
                    241:                                b_countdown=b_chunk->count;
                    242:                        }
                    243:                } else {
1.60      paf       244:                        a_offset-=a_row->item.size;
                    245:                        pos+=a_row->item.size;
                    246:                        a_row++; a_countdown--; 
1.9       paf       247:                }
                    248: 
1.11      paf       249:                if(!a_countdown) {
1.9       paf       250:                        a_chunk=a_row->link;
                    251:                        a_row=a_chunk->rows;
1.11      paf       252:                        a_countdown=a_chunk->count;
1.9       paf       253:                }
1.27      paf       254:        }
1.55      paf       255:        if(a_break==b_break) { // ended simultaneously
                    256:                partial=0; return 0;
                    257:        } else if(a_break) { // first bytes equal, but a ended before b
                    258:                partial=1; return -1;
                    259:        } else {
                    260:                partial=2; return +1;
                    261:        }
1.27      paf       262: }
                    263: 
1.60      paf       264: /// @todo move 'lang' skipping to pos
1.59      paf       265: int String::cmp(int& partial, const char* b_ptr, size_t src_size, 
1.60      paf       266:                                size_t this_offset, Untaint_lang lang) const {
1.59      paf       267:        partial=-1;
1.50      paf       268:        size_t b_size=src_size?src_size:b_ptr?strlen(b_ptr):0;
1.59      paf       269:        this_offset=min(this_offset, size()-1);
1.27      paf       270: 
                    271:        const Chunk *a_chunk=&head;
                    272:        const Chunk::Row *a_row=a_chunk->rows;
1.59      paf       273:        size_t a_offset=this_offset;
1.55      paf       274:        size_t b_offset=0;
1.27      paf       275:        Chunk::Row *a_end=append_here;
1.55      paf       276:        size_t a_countdown=a_chunk->count;
1.27      paf       277:        bool a_break=false;
                    278:        bool b_break=false;
1.60      paf       279:        size_t pos=0;
                    280:        while(true) {
1.52      paf       281:                a_break=a_row==a_end;
                    282:                if(a_break || b_break)
                    283:                        break;
                    284: 
1.59      paf       285:                if(pos+a_row->item.size > this_offset) {
1.60      paf       286:                        if(lang!=UL_UNKNOWN && a_row->item.lang!=lang) 
                    287:                                return -1; // wrong lang -- bail out
                    288: 
1.59      paf       289:                        int size_diff=
                    290:                                (a_row->item.size-a_offset)-
                    291:                                (b_size-b_offset);
                    292:                        
                    293:                        if(size_diff==0) { // a has same size as b
                    294:                                if(size_t result=memcmp(a_row->item.ptr+a_offset, b_ptr+b_offset, 
                    295:                                        a_row->item.size-a_offset)!=0)
                    296:                                        return result;
1.60      paf       297:                                pos+=a_row->item.size;
1.59      paf       298:                                a_row++; a_countdown--; a_offset=0;
                    299:                                b_break=true;
                    300:                        } else if (size_diff>0) { // a longer
                    301:                                if(size_t result=memcmp(a_row->item.ptr+a_offset, b_ptr+b_offset, 
                    302:                                        b_size-b_offset)!=0)
                    303:                                        return result;
                    304:                                a_offset+=b_size-b_offset;
                    305:                                b_break=true;
                    306:                        } else { // b longer
                    307:                                if(size_t result=memcmp(a_row->item.ptr+a_offset, b_ptr+b_offset, 
                    308:                                        a_row->item.size-a_offset)!=0)
                    309:                                        return result;
                    310:                                b_offset+=a_row->item.size-a_offset;
1.60      paf       311:                                pos+=a_row->item.size;
1.59      paf       312:                                a_row++; a_countdown--; a_offset=0;
                    313:                        }
                    314:                } else {
1.60      paf       315:                        a_offset-=a_row->item.size; 
                    316:                        pos+=a_row->item.size;
                    317:                        a_row++; a_countdown--; 
1.27      paf       318:                }
                    319: 
                    320:                if(!a_countdown) {
                    321:                        a_chunk=a_row->link;
                    322:                        a_row=a_chunk->rows;
                    323:                        a_countdown=a_chunk->count;
1.9       paf       324:                }
                    325:        }
1.55      paf       326:        if(a_break==b_break) { // ended simultaneously
                    327:                partial=0; return 0;
                    328:        } else if(a_break) { // first bytes equal, but a ended before b
                    329:                partial=1; return -1;
                    330:        } else {
                    331:                partial=2; return +1;
                    332:        }
1.5       paf       333: }
1.46      paf       334: 
                    335: #ifndef NO_STRING_ORIGIN
                    336: const Origin& String::origin() const { 
                    337:        if(!fused_rows)
                    338:                THROW(0, 0, 
1.50      paf       339:                        0,
                    340:                        "String::origin() of empty string called");
1.46      paf       341:        
1.49      paf       342:        // determining origin by last appended piece
1.50      paf       343:        // because first one frequently constant. 
                    344:        // ex: ^load[/file] "document_root" + "/file"
1.49      paf       345:        return append_here[-1].item.origin; 
1.46      paf       346: }
                    347: #endif
1.53      paf       348: 
                    349: String& String::piece(size_t start, size_t finish) const {
                    350:        start=max(0, start);
                    351:        finish=min(size(), finish);
1.60      paf       352:        if(start==finish)
                    353:                return *empty_string;
1.53      paf       354: 
                    355:        String& result=*NEW String(pool());
                    356: 
                    357:        size_t pos=0;
                    358:        const Chunk *chunk=&head; 
                    359:        do {
                    360:                const Chunk::Row *row=chunk->rows;
1.55      paf       361:                for(size_t i=0; i<chunk->count; pos+=row->item.size, i++, row++) {
1.53      paf       362:                        if(row==append_here)
                    363:                                goto break2;
                    364: 
1.60      paf       365:                        size_t item_finish=pos+row->item.size;
                    366:                        if(item_finish > start) { // started now or already?
                    367:                                bool started=result.size()==0; // started now?
                    368:                                bool finished=finish <= item_finish; // finished now?
1.53      paf       369:                                size_t offset=started?start-pos:0;
                    370:                                size_t size=finished?finish-pos:row->item.size;
                    371:                                result.APPEND(
                    372:                                        row->item.ptr+offset, size-offset, 
                    373:                                        row->item.lang,
                    374:                                        row->item.origin.file, row->item.origin.line);
                    375:                                if(finished)
                    376:                                        goto break2;
                    377:                        }
                    378:                }
                    379:                chunk=row->link;
                    380:        } while(chunk);
                    381: break2:
1.60      paf       382: //     SAPI::log(pool(), "piece of '%s' from %d to %d is '%s'",
                    383:                //cstr(), start, finish, result.cstr());
1.53      paf       384:        return result;
1.54      paf       385: }
                    386: 
1.60      paf       387: int String::pos(const String& substr, 
                    388:                                size_t result, Untaint_lang lang) const {
1.58      paf       389:        for(; result<size(); result++) {
1.60      paf       390:                int partial; cmp(partial, substr, result, lang);
1.58      paf       391:                if(
                    392:                        partial==0 || // full match
                    393:                        partial==2) // 'substr' starts 'this'+'result'
                    394:                        return result;
                    395:        }
                    396:        
                    397:        return -1;
                    398: }
                    399: 
1.60      paf       400: int String::pos(const char *substr, size_t substr_size, 
                    401:                                size_t result, Untaint_lang lang) const {
1.57      paf       402:        for(; result<size(); result++) {
1.60      paf       403:                int partial; cmp(partial, substr, substr_size, result, lang);
1.55      paf       404:                if(
                    405:                        partial==0 || // full match
                    406:                        partial==2) // 'substr' starts 'this'+'result'
                    407:                        return result;
                    408:        }
                    409:        
                    410:        return -1;
1.60      paf       411: }
                    412: 
                    413: void String::split(Array& result, 
                    414:                                   size_t* pos_after_ref, 
                    415:                                   const char *delim, size_t delim_size, 
                    416:                                   Untaint_lang lang, int limit) const {
                    417:        if(delim_size) {
                    418:                size_t pos_after=pos_after_ref?*pos_after_ref:0;
                    419:                int pos_before;
                    420:                // while we have 'delim'...
                    421:                for(; (pos_before=pos(delim, delim_size, pos_after, lang))>=0 && limit; limit--) {
                    422:                        result+=&piece(pos_after, pos_before);
                    423:                        pos_after=pos_before+delim_size;
                    424:                }
                    425:                // last piece
                    426:                if(pos_after<size() && limit) {
                    427:                        result+=&piece(pos_after, size());
                    428:                        pos_after=size();
                    429:                }
                    430:                if(pos_after_ref)
                    431:                        *pos_after_ref=pos_after;
                    432:        } else { // empty delim
                    433:                result+=this;
                    434:                if(pos_after_ref)
                    435:                        *pos_after_ref+=size();
                    436:        }
                    437: }
                    438: 
                    439: void String::split(Array& result, 
                    440:                                   size_t* pos_after_ref, 
                    441:                                   const String& delim, Untaint_lang lang, 
                    442:                                   int limit) const {
                    443:        if(delim.size()) {
                    444:                size_t pos_after=pos_after_ref?*pos_after_ref:0;
                    445:                int pos_before;
                    446:                // while we have 'delim'...
                    447:                for(; (pos_before=pos(delim, pos_after, lang))>=0 && limit; limit--) {
                    448:                        result+=&piece(pos_after, pos_before);
                    449:                        pos_after=pos_before+delim.size();
                    450:                }
                    451:                // last piece
                    452:                if(pos_after<size() && limit) {
                    453:                        result+=&piece(pos_after, size());
                    454:                        pos_after=size();
                    455:                }
                    456:                if(pos_after_ref)
                    457:                        *pos_after_ref=pos_after;
                    458:        } else { // empty delim
                    459:                result+=this;
                    460:                if(pos_after_ref)
                    461:                        *pos_after_ref+=size();
                    462:        }
1.61      paf       463: }
                    464: 
1.62    ! paf       465: /// @test setlocale param to auto.p  @test pcre_malloc & pcre_free substs
        !           466: bool String::match(const String *aorigin,
        !           467:                                   const String& regexp, 
        !           468:                                   const String& options,
        !           469:                                   Table **table) const { 
        !           470:        SYNCHRONIZED(true);
        !           471:        static const unsigned char *tables=0;
        !           472:        if(!tables) {
        !           473:                setlocale(LC_CTYPE, "ru");
        !           474:                tables = pcre_maketables();
        !           475:        }
        !           476:        const char *pattern=regexp.cstr();
        !           477:        const char *errptr;
        !           478:        int erroffset;
        !           479:        pcre *code=pcre_compile(pattern, 0, 
        !           480:                &errptr, &erroffset,
        !           481:                tables);
        !           482: 
        !           483:        if(!code)
        !           484:                THROW(0, 0,
        !           485:                        &regexp.piece(erroffset, regexp.size()),
        !           486:                        errptr);
        !           487:        
        !           488:        int ovecsize;
        !           489:        int *ovector=(int *)malloc(sizeof(int)*(ovecsize=(1/*.match*/+MAX_MATCH_COLUMNS)*3));
        !           490:        const char *subject=cstr();
        !           491:        int length=strlen(subject);
        !           492:        int exec_result=pcre_exec(code, 0,
        !           493:           subject, length, 0/*startoffset*/,
        !           494:           0/*options*/, ovector, ovecsize);
        !           495: 
        !           496:        if(exec_result==PCRE_ERROR_NOMATCH) {
        !           497:                *table=0;
        !           498:                return false;
        !           499:        }
        !           500: 
        !           501:        if(exec_result<0)
        !           502:                THROW(0, 0,
        !           503:                        0,
        !           504:                        "pcre_exec failed");
        !           505: 
        !           506:        if(exec_result==0)
        !           507:                THROW(0, 0,
        !           508:                        aorigin,
        !           509:                        "produced more substrings than maximum handled by Parser, which is %d",
        !           510:                                MAX_MATCH_COLUMNS);
        !           511: 
        !           512:        Array& columns=*NEW Array(pool());
        !           513:        columns+=string_match_name; // .match column name
        !           514:        Array& row=*NEW Array(pool());
        !           515:        row+=&piece(ovector[0], ovector[1]); // match column value
        !           516:        
        !           517:        for(int i=1; i<exec_result; i++) {
        !           518:                char *column=(char *)malloc(MAX_NUMBER);
        !           519:                snprintf(column, MAX_NUMBER, "%d", i);
        !           520:                columns+=NEW String(pool(), column); // .i column name
        !           521:                row+=&piece(ovector[i*2+0], ovector[i*2+1]); // .i column value
        !           522:        }
        !           523:        
        !           524:        *table=NEW Table(pool(), aorigin, &columns);
        !           525:        (**table)+=&row;
        !           526:        return true;
1.61      paf       527: }

E-mail: