src/string.c

changeset 1300
fcb149ee60ff
parent 1296
2a2403c63439
child 1304
57e062a4bb05
equal deleted inserted replaced
1299:5dfce68057ce 1300:fcb149ee60ff
567 return strncasecmp(string.ptr + string.length - suffix.length, 567 return strncasecmp(string.ptr + string.length - suffix.length,
568 suffix.ptr, suffix.length) == 0; 568 suffix.ptr, suffix.length) == 0;
569 #endif 569 #endif
570 } 570 }
571 571
572 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
573 #define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
574 #endif
575
576 struct cx_strreplace_ibuf {
577 size_t *buf;
578 struct cx_strreplace_ibuf *next;
579 unsigned int len;
580 };
581
582 static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
583 // remember, the first data is on the stack!
584 buf = buf->next;
585 while (buf) {
586 struct cx_strreplace_ibuf *next = buf->next;
587 free(buf->buf);
588 free(buf);
589 buf = next;
590 }
591 }
592
593 cxmutstr cx_strreplacen_a( 572 cxmutstr cx_strreplacen_a(
594 const CxAllocator *allocator, 573 const CxAllocator *allocator,
595 cxstring str, 574 cxstring str,
596 cxstring search, 575 cxstring search,
597 cxstring replacement, 576 cxstring replacement,
598 size_t replmax 577 size_t replmax
599 ) { 578 ) {
600 579 // special cases
601 if (search.length == 0 || search.length > str.length || replmax == 0) 580 if (search.length == 0 || search.length > str.length || replmax == 0) {
602 return cx_strdup_a(allocator, str); 581 return cx_strdup_a(allocator, str);
603 582 }
604 // Compute expected buffer length 583
605 size_t ibufmax = str.length / search.length; 584 size_t in_len = str.length;
606 size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; 585 size_t search_len = search.length;
607 if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { 586 size_t repl_len = replacement.length;
608 ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; 587
609 } 588 // first run, count the occurrences
610 589 // and remember where the first is
611 // First index buffer can be on the stack 590 size_t occurrences = 1;
612 struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf; 591 cxstring first = cx_strstr(str, search);
613 size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE]; 592 if (first.length == 0) {
614 ibuf.buf = ibuf_sbo; 593 // special case, no replacements
615 ibuf.next = NULL; 594 return cx_strdup_a(allocator, str);
616 ibuf.len = 0; 595 }
617 596 cxstring tmp = cx_strsubs(first, search_len);
618 // Search occurrences 597 while (occurrences < replmax &&
619 cxstring searchstr = str; 598 (tmp = cx_strstr(tmp, search)).length > 0) {
620 size_t found = 0; 599 occurrences++;
621 do { 600 tmp = cx_strsubs(tmp, search_len);
622 cxstring match = cx_strstr(searchstr, search); 601 }
623 if (match.length > 0) { 602
624 // Allocate next buffer in chain, if required 603 // calculate necessary memory
625 if (curbuf->len == ibuflen) { 604 signed long long diff_len = (signed long long) repl_len - search_len;
626 struct cx_strreplace_ibuf *nextbuf = 605 size_t out_len = in_len + diff_len * occurrences;
627 calloc(1, sizeof(struct cx_strreplace_ibuf)); 606 cxmutstr out = {
628 if (!nextbuf) { 607 cxMalloc(allocator, out_len + 1),
629 cx_strrepl_free_ibuf(&ibuf); 608 out_len
630 return cx_mutstrn(NULL, 0); 609 };
631 } 610 if (out.ptr == NULL) return out;
632 nextbuf->buf = calloc(ibuflen, sizeof(size_t)); 611
633 if (!nextbuf->buf) { 612 // second run: perform the replacements
634 free(nextbuf); 613 // but start where we found the first occurrence
635 cx_strrepl_free_ibuf(&ibuf); 614 const char *inp = str.ptr;
636 return cx_mutstrn(NULL, 0); 615 tmp = first;
637 } 616 char *outp = out.ptr;
638 curbuf->next = nextbuf; 617 while (occurrences-- > 0 && (tmp = cx_strstr(tmp, search)).length > 0) {
639 curbuf = nextbuf; 618 size_t copylen = tmp.ptr - inp;
640 } 619 memcpy(outp, inp, copylen);
641 620 outp += copylen;
642 // Record match index 621 memcpy(outp, replacement.ptr, repl_len);
643 found++; 622 outp += repl_len;
644 size_t idx = match.ptr - str.ptr; 623 inp += copylen + search_len;
645 curbuf->buf[curbuf->len++] = idx; 624 tmp = cx_strsubs(tmp, search_len);
646 searchstr.ptr = match.ptr + search.length; 625 }
647 searchstr.length = str.length - idx - search.length; 626
648 } else { 627 // add the remaining string
649 break; 628 size_t copylen = in_len - (inp - str.ptr);
650 } 629 memcpy(outp, inp, copylen);
651 } while (searchstr.length > 0 && found < replmax); 630 out.ptr[out_len] = '\0';
652 631
653 // Allocate result string 632 return out;
654 cxmutstr result;
655 {
656 long long adjlen = (long long) replacement.length - (long long) search.length;
657 size_t rcount = 0;
658 curbuf = &ibuf;
659 do {
660 rcount += curbuf->len;
661 curbuf = curbuf->next;
662 } while (curbuf);
663 result.length = str.length + rcount * adjlen;
664 result.ptr = cxMalloc(allocator, result.length + 1);
665 if (!result.ptr) {
666 cx_strrepl_free_ibuf(&ibuf);
667 return cx_mutstrn(NULL, 0);
668 }
669 }
670
671 // Build result string
672 curbuf = &ibuf;
673 size_t srcidx = 0;
674 char *destptr = result.ptr;
675 do {
676 for (size_t i = 0; i < curbuf->len; i++) {
677 // Copy source part up to next match
678 size_t idx = curbuf->buf[i];
679 size_t srclen = idx - srcidx;
680 if (srclen > 0) {
681 memcpy(destptr, str.ptr + srcidx, srclen);
682 destptr += srclen;
683 srcidx += srclen;
684 }
685
686 // Copy the replacement and skip the source pattern
687 srcidx += search.length;
688 memcpy(destptr, replacement.ptr, replacement.length);
689 destptr += replacement.length;
690 }
691 curbuf = curbuf->next;
692 } while (curbuf);
693 memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
694
695 // Result is guaranteed to be zero-terminated
696 result.ptr[result.length] = '\0';
697
698 // Free index buffer
699 cx_strrepl_free_ibuf(&ibuf);
700
701 return result;
702 } 633 }
703 634
704 CxStrtokCtx cx_strtok_( 635 CxStrtokCtx cx_strtok_(
705 cxstring str, 636 cxstring str,
706 cxstring delim, 637 cxstring delim,

mercurial