diff --git a/README.md b/README.md index 63ca8d62..aa523ced 100644 --- a/README.md +++ b/README.md @@ -5709,6 +5709,14 @@ It returns -1 in case of error in decoding the UTF8 string. Return the position in the stream of the iterator 'it'. +##### void string\_it\_set\_ref(string\_it\_t it, string\_t str, string\_unicode\_t u) + +Replace the unicode code point referenced by 'it' to the unicode code point 'u' +in the string 'str'. +If 'u' is 0, then it will resize the string to the size of the referenced iterator +only keeping the unicode code points before the iterator. +'it' shall be a valid iterator of 'str'. + ##### bool string\_it\_equal\_p (const string\_it\_t it1, const string\_it\_t it2) Return true if both iterators reference the same position, false otherwise. diff --git a/m-string.h b/m-string.h index 0d608861..783e1c88 100644 --- a/m-string.h +++ b/m-string.h @@ -1863,6 +1863,56 @@ m_string_cref (m_string_it_t it) return &it->u; } +/* Update the value referenced by the iterator to the given value */ +static inline void +m_string_it_set_ref(m_string_it_t it, m_string_t s, m_string_unicode_t new_u) +{ + M_STR1NG_IT_CONTRACT(it); + M_STR1NG_CONTRACT(s); + M_ASSERT(s == it->string); + char *ptr = m_str1ng_get_cstr(s); + M_ASSUME( it->ptr >= ptr); + size_t offset = (size_t) (it->ptr - ptr); + // Special case if the unicode codepoint is 0 + if (new_u == 0) { + // Null the string + m_str1ng_set_size(s, offset); + ptr[offset] = 0; + M_STR1NG_IT_CONTRACT(it); + M_STR1NG_CONTRACT(s); + return; + } + // Encode the new unicode code point & compute its size + char buffer[4+1]; + m_str1ng_utf8_encode(buffer, new_u); + size_t new_u_size = strlen(buffer); + // Compute the size of the old unicode code point + m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; + m_string_unicode_t old_u = 0; + const char *str = it->ptr; + do { + m_str1ng_utf8_decode(*str, &state, &old_u); + str++; + } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR && *str != 0); + M_ASSUME( str > it->ptr); + size_t old_u_size = (size_t) (str - it->ptr); + // We need to replace old_u by new_u. Both are variable length + size_t str_size = m_string_size(s); + m_str1ng_fit2size(s, str_size + new_u_size - old_u_size + 1); + ptr = m_str1ng_get_cstr(s); // ptr may be reallocated! + if (new_u_size != old_u_size) { + M_ASSUME( str_size+1 > (offset + old_u_size) ); + memmove(&ptr[offset+new_u_size], &ptr[offset + old_u_size], + str_size+1 - offset - old_u_size); + } + memcpy(&ptr[offset], &buffer[0], new_u_size); + m_str1ng_set_size(s, str_size + new_u_size - old_u_size); + it->ptr = &ptr[offset]; + M_STR1NG_IT_CONTRACT(it); + M_STR1NG_CONTRACT(s); + return; +} + /* Push unicode code point into string, encoding it in UTF8 */ static inline void m_string_push_u (m_string_t str, m_string_unicode_t u) @@ -2714,6 +2764,7 @@ namespace m_lib { #define string_it_pos m_string_it_pos #define string_it_get_pos m_string_it_get_pos #define string_previous m_string_previous +#define string_it_set_ref m_string_it_set_ref #define STRING_CTE M_STRING_CTE #define STRING_DECL_INIT M_STRING_DECL_INIT diff --git a/tests/test-mstring.c b/tests/test-mstring.c index 9bdc4949..9158a711 100644 --- a/tests/test-mstring.c +++ b/tests/test-mstring.c @@ -172,6 +172,52 @@ static void test_utf8_it(void) assert(!string_it_equal_p(it, it2)); string_clear(s); + + /* Test replacing of unicode */ + string_init(s); + for (string_unicode_t u = 1; u < 0x10ffff; u+=3) { + if (u < 0xD800U || u > 0xDFFFU) { + for(int c1 = 0 ; c1 <= 3; c1+=2) { + for(int c2 = 0 ; c2 <= 20; c2+=5) { + const string_unicode_t u_tab[] = { 0, 'B', 300, 45215, 0x10fffe}; + for(int c3 = 0 ; c3 < 5; c3++) { + // Generate string + string_reset(s); + for(int j = 0; j < c1; j++) + string_push_back(s, 'A'); + string_push_u(s, u); + for(int j = 0; j < c2; j++) + string_push_back(s, 'C'); + // Replace iterator in string + string_it(it, s); + for(int j = 0; j < c1; j++) + string_next(it); + assert( string_get_cref(it) == u); + string_it_set_ref(it, s, u_tab[c3]); + if (u_tab[c3] != 0) { + assert( string_get_cref(it) == u_tab[c3]); + } + // Check constructed string + string_it(it, s); + for(int j = 0; j < c1; j++) { + assert( string_get_cref(it) == 'A'); + string_next(it); + } + if (u_tab[c3] != 0) { + assert( *string_cref(it) == u_tab[c3]); + string_next(it); + for(int j = 0; j < c2; j++) { + assert( string_get_cref(it) == 'C'); + string_next(it); + } + } + assert(string_end_p(it)); + } + } + } + } + } + string_clear(s); } static void call_string_cat_vprintf(string_t s, const char format[], ...)