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