diff --git a/README.md b/README.md index 49b2784b..93b5343f 100644 --- a/README.md +++ b/README.md @@ -5079,6 +5079,12 @@ the string 'str1' by the string 'str2' once. Returns the offset of the replacement or STRING_FAILURE if no replacement was performed. +##### size\_t string\_replace\_all\_str (string\_t v, const char str1[], const char str2[]) +##### size\_t string\_replace\_all (string\_t v, const string\_t str1, const string\_t str2) + +Replace in the string 'v' all the occurences of +the string 'str1' by the string 'str2'. + ##### void string\_replace\_at (string\_t v, size\_t pos, size\_t len, const char str2[]) Replace in the string 'v' the sub-string defined as starting from 'pos' and diff --git a/m-string.h b/m-string.h index 21976ebc..2c1d57cb 100644 --- a/m-string.h +++ b/m-string.h @@ -772,6 +772,143 @@ string_replace_at (string_t v, size_t pos, size_t len, const char str2[]) STRINGI_CONTRACT (v); } +/* Replace all occurences of str1 into str2 when strlen(str1) >= strlen(str2) */ +static inline void +stringi_replace_all_str_1ge2 (string_t v, const char str1[], size_t str1len, const char str2[], size_t str2len) +{ + STRINGI_CONTRACT (v); + M_ASSERT(str1len >= str2len); + + /* str1len < str2len so the string doesn't need to be resized */ + size_t vlen = string_size(v); + char *org = stringi_get_str(v); + char *src = org; + char *dst = org; + + // Go through all the characters of the string + while (*src != 0) { + // Get a new occurence of str1 in the v string. + char *occ = strstr(src, str1); + if (occ == NULL) { + // No new occurence + break; + } + M_ASSERT(occ >= src); + // Copy the data until the new occurence + if (src != dst) { + memmove(dst, src, (size_t) (occ - src)); + } + dst += (occ - src); + src = occ; + // Copy the replaced string + memcpy(dst, str2, str2len); + dst += str2len; + // Advance src pointer + src = occ + str1len; + } + // Finish copying the string until the end + M_ASSERT (src <= org + vlen ); + if (src != dst) { + memmove(dst, src, (size_t) (org + vlen + 1 - src) ); + } + // Update string size + stringi_set_size(v, (size_t) (dst + vlen - src) ); + STRINGI_CONTRACT (v); +} + +/* Reverse strstr from the end of the string + org is the start of the string + src is the current character pointer (shall be initialized to the end of the string) + pattern / pattern_size: the pattern to search. + */ +static inline char * +stringi_strstr_r(char org[], char src[], const char pattern[], size_t pattern_size) +{ + src -= pattern_size - 1; + while (org <= src) { + if (src[0] == pattern[0] + && src[pattern_size-1] == pattern[pattern_size-1] + && memcmp(src, pattern, pattern_size) == 0) { + return src; + } + src --; + } + return NULL; +} + +/* Replace all occurences of str1 into str2 when strlen(str1) < strlen(str2) */ +static inline void +stringi_replace_all_str_1lo2 (string_t v, const char str1[], size_t str1len, const char str2[], size_t str2len) +{ + STRINGI_CONTRACT (v); + M_ASSERT(str1len < str2len); + + /* str1len < str2len so the string may need to be resized + Worst case if when v is composed fully of str1 substrings. + Needed size : v.size / str1len * str2len + */ + size_t vlen = string_size(v); + size_t alloc = vlen / str1len * str2len; + stringi_fit2size(v, alloc); + + char *org = stringi_get_str(v); + char *src = org + vlen - 1; + char *end = org + string_capacity(v); + char *dst = end; + + // Go through all the characters of the string in reverse ! + while (src >= org) { + char *occ = stringi_strstr_r(org, src, str1, str1len); + if (occ == NULL) { + break; + } + M_ASSERT(occ + str1len - 1 <= src); + // Copy the data until the new occurence + dst -= (src - (occ + str1len - 1)); + memmove(dst, occ+str1len, (size_t) (src - (occ + str1len - 1))); + // Copy the replaced string + dst -= str2len; + memcpy(dst, str2, str2len); + // Advance src pointer + src = occ - 1; + } + // Finish moving data back to their natural place + memmove(src + 1, dst, (size_t) (end - dst) ); + // Update string size + vlen = (size_t) (src - org + end - dst + 1); + org[vlen] = 0; + stringi_set_size(v, vlen ); + STRINGI_CONTRACT (v); +} + +static inline void +string_replace_all_str (string_t v, const char str1[], const char str2[]) +{ + size_t str1_l = strlen(str1); + size_t str2_l = strlen(str2); + assert(str1_l > 0); + assert(str2_l > 0); + if (str1_l >= str2_l) { + stringi_replace_all_str_1ge2(v, str1, str1_l, str2, str2_l ); + } else { + stringi_replace_all_str_1lo2(v, str1, str1_l, str2, str2_l ); + } +} + +static inline void +string_replace_all (string_t v, const string_t str1, const string_t str2) +{ + size_t str1_l = string_size(str1); + size_t str2_l = string_size(str2); + assert(str1_l > 0); + assert(str2_l > 0); + if (str1_l >= str2_l) { + stringi_replace_all_str_1ge2(v, string_get_cstr(str1), str1_l, string_get_cstr(str2), str2_l ); + } else { + stringi_replace_all_str_1lo2(v, string_get_cstr(str1), str1_l, string_get_cstr(str2), str2_l ); + } +} + /* Format in the string the given printf format */ static inline int string_printf (string_t v, const char format[], ...) diff --git a/tests/test-mstring.c b/tests/test-mstring.c index 1643b24f..0a272d68 100644 --- a/tests/test-mstring.c +++ b/tests/test-mstring.c @@ -217,6 +217,31 @@ static void test0(void) assert (i != STRING_FAILURE); assert (string_cmp_str (s1, "Hello, Juliette!") == 0); + string_set_str(s1, "Hello world 4 life world is real another world"); + string_replace_all_str(s1, "world", "earth"); + assert( string_equal_str_p(s1, "Hello earth 4 life earth is real another earth")); + + string_set_str(s1, "Hello world 4 life world is real another world"); + string_replace_all_str(s1, "world", "Wrld"); + assert( string_equal_str_p(s1, "Hello Wrld 4 life Wrld is real another Wrld")); + + string_set_str(s1, "world Hello world 4 life world is real another planet"); + string_replace_all_str(s1, "world", "Wrld"); + assert( string_equal_str_p(s1, "Wrld Hello Wrld 4 life Wrld is real another planet")); + + string_set_str(s1, "Hello world 4 life world is real another world"); + string_replace_all_str(s1, "world", "**WORLD**"); + assert( string_equal_str_p(s1, "Hello **WORLD** 4 life **WORLD** is real another **WORLD**")); + + string_set_str(s1, "world Hello world 4 life world is real another planet"); + string_replace_all_str(s1, "world", "**WORLD**"); + assert( string_equal_str_p(s1, "**WORLD** Hello **WORLD** 4 life **WORLD** is real another planet")); + + string_set_str(s1, "HELLO"); + string_set_str(s2, "WORLD"); + string_replace_all(s1, s1, s2); + assert( string_equal_str_p(s1, "WORLD")); + string_printf (s1, ""); assert (string_cmp_str (s1, "") == 0); assert (string_size (s1) == 0);