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, |