From 3aa3e95e4c24fa0f359bf16d7df551be344ab80e Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Tue, 13 Sep 2022 00:18:15 +0200 Subject: [PATCH] Remove constraint of calling string_end_p before string_next or string_get_cref This generates slighty more code but it is much saner from user point of view. --- README.md | 4 --- m-string.h | 67 ++++++++++++++++++-------------------------- tests/test-mstring.c | 5 ---- 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index d720575c..26a65a60 100644 --- a/README.md +++ b/README.md @@ -5341,8 +5341,6 @@ false otherwise. ##### void string\_next (string\_it\_t it) Move the iterator to the next UTF8 encoded code point. -string\_end\_p shall have been called at least once -per UTF8 code point before. ##### void string\_previous (string\_it\_t it) @@ -5351,8 +5349,6 @@ Move the iterator to the previous UTF8 encoded code point. ##### string\_unicode\_t string\_get\_cref (const string\_it\_t it) Return the unicode code point associated to the iterator. -string\_end\_p shall have been called at least once -per UTF8 code point before. It returns -1 in case of error in decoding the UTF8 string. ##### size\_t string\_it\_get\_pos (const string\_it\_t it) diff --git a/m-string.h b/m-string.h index e3ed8acf..16a8ee9c 100644 --- a/m-string.h +++ b/m-string.h @@ -74,7 +74,6 @@ typedef enum { #define M_STR1NG_IT_CONTRACT(it) do { \ M_ASSERT( (it) != NULL); \ M_ASSERT( (it)->ptr != NULL); \ - M_ASSERT( 0 <= (it)->next_ptr && (it)->next_ptr <= M_STR1NG_MAX_BYTE_UTF8); \ M_ASSERT( (it)->string != NULL); \ M_ASSERT( m_string_get_cstr((it)->string) <= (it)->ptr); \ M_ASSERT( (it)->ptr <= &m_string_get_cstr((it)->string)[m_string_size((it)->string)]); \ @@ -129,7 +128,6 @@ typedef uint32_t m_string_unicode_t; /* Iterator on a string over UTF8 encoded code points */ typedef struct m_string_it_s { m_string_unicode_t u; // Decoded Unicode code point for the iterator - int next_ptr; // Offset to the next code point const char *ptr; m_string_srcptr string; } m_string_it_t[1]; @@ -1716,7 +1714,6 @@ m_string_it(m_string_it_t it, const m_string_t str) M_STR1NG_CONTRACT(str); M_ASSERT(it != NULL); it->ptr = m_string_get_cstr(str); - it->next_ptr = 0; it->u = 0; it->string = str; M_STR1NG_IT_CONTRACT(it); @@ -1731,7 +1728,6 @@ m_string_it_end(m_string_it_t it, const m_string_t str) M_STR1NG_CONTRACT(str); M_ASSERT(it != NULL); it->ptr = &m_string_get_cstr(str)[m_string_size(str)]; - it->next_ptr = 0; it->u = 0; it->string = str; M_STR1NG_IT_CONTRACT(it); @@ -1742,8 +1738,8 @@ static inline void m_string_it_set(m_string_it_t it, const m_string_it_t itsrc) { M_ASSERT(it != NULL && itsrc != NULL); + M_STR1NG_IT_CONTRACT(itsrc); it->ptr = itsrc->ptr; - it->next_ptr = itsrc->next_ptr; it->u = itsrc->u; it->string = itsrc->string; M_STR1NG_IT_CONTRACT(it); @@ -1762,7 +1758,6 @@ m_string_it_pos(m_string_it_t it, const m_string_t str, const size_t n) // The offset shall reference the first Byte of an UTF 8 Code point M_ASSERT(m_str1ng_utf8_start_p ((unsigned char)m_string_get_cstr(str)[n])); it->ptr = &m_string_get_cstr(str)[n]; - it->next_ptr = 0; it->u = 0; it->string = str; M_STR1NG_IT_CONTRACT(it); @@ -1778,32 +1773,12 @@ m_string_it_get_pos(m_string_it_t it) return (size_t) (it->ptr - m_string_get_cstr(it->string)); } -/* Test if the iterator has reached the end of the string. - As it is where we perform the UTF8 decoding, the user - needs to call this function at least once before any other - function, which is normally the case as the user shall test - if the stream is not empty before reading it. */ +/* Test if the iterator has reached the end of the string. */ static inline bool m_string_end_p (m_string_it_t it) { M_STR1NG_IT_CONTRACT(it); - if (M_UNLIKELY (*it->ptr == 0)) { - it->next_ptr = 0; // For safety, don't advance iterator over end of string - return true; - } - m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; - m_string_unicode_t u = 0; - const char *str = it->ptr; - do { - m_str1ng_utf8_decode(*str, &state, &u); - str++; - } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR && *str != 0); - // Save where the current unicode value ends in the UTF8 steam - M_ASSERT( (str > it->ptr) && (str - it->ptr) <= M_STR1NG_MAX_BYTE_UTF8); - it->next_ptr = (int) (str - it->ptr); - // Save the decoded unicode value - it->u = M_UNLIKELY (state == M_STR1NG_UTF8_ERROR) ? M_STRING_UNICODE_ERROR : u; - return false; + return it->ptr[0] == 0; } /* Test if the iterator is equal to the other one */ @@ -1812,8 +1787,6 @@ m_string_it_equal_p(const m_string_it_t it1, const m_string_it_t it2) { M_STR1NG_IT_CONTRACT(it1); M_STR1NG_IT_CONTRACT(it2); - // IT1.ptr == IT2.ptr ==> IT1 == IT2 ==> All fields are equal - M_ASSERT(it1->ptr != it2->ptr || (it1->next_ptr == it2->next_ptr && it1->u == it2->u)); return it1->ptr == it2->ptr; } @@ -1822,8 +1795,16 @@ static inline void m_string_next (m_string_it_t it) { M_STR1NG_IT_CONTRACT(it); - // We already decoded the UTF8 stream - it->ptr += it->next_ptr; + const char *ptr = it->ptr; + while (*ptr != 0) { + ptr ++; + if (m_str1ng_utf8_start_p((unsigned char) *ptr) ) { + /* Start of an UTF 8 code point */ + break; + } + } + it->ptr = ptr; + return; } /* Move the iterator to the previous code point */ @@ -1838,16 +1819,11 @@ m_string_previous(m_string_it_t it) if (m_str1ng_utf8_start_p((unsigned char) *ptr) ) { /* Start of an UTF 8 code point */ it->ptr = ptr; - /* Decode the UTF-8 code point */ - bool b = m_string_end_p(it); - M_ASSERT(!b); - (void) b; return; } } /* We reach the start of the string: mark the iterator to the end */ it->ptr = &org[m_string_size(it->string)]; - it->next_ptr = 0; M_STR1NG_IT_CONTRACT(it); } @@ -1856,14 +1832,27 @@ static inline m_string_unicode_t m_string_get_cref (const m_string_it_t it) { M_STR1NG_IT_CONTRACT(it); - return it->u; + M_ASSERT(*it->ptr != 0); + + m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; + m_string_unicode_t u = 0; + const char *str = it->ptr; + do { + m_str1ng_utf8_decode(*str, &state, &u); + str++; + } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR && *str != 0); + // Save where the current unicode value ends in the UTF8 steam + M_ASSERT( (str > it->ptr) && (str - it->ptr) <= M_STR1NG_MAX_BYTE_UTF8); + // Save the decoded unicode value + return M_UNLIKELY (state == M_STR1NG_UTF8_ERROR) ? M_STRING_UNICODE_ERROR : u; } /* Return the unicode code point associated to the iterator */ static inline const m_string_unicode_t * -m_string_cref (const m_string_it_t it) +m_string_cref (m_string_it_t it) { M_STR1NG_IT_CONTRACT(it); + it->u = m_string_get_cref(it); return &it->u; } diff --git a/tests/test-mstring.c b/tests/test-mstring.c index 0b08e8a8..b04eba69 100644 --- a/tests/test-mstring.c +++ b/tests/test-mstring.c @@ -41,15 +41,10 @@ static void test_utf8_basic(void) char *buf = m_str1ng_get_cstr(s); /* Low level access for internal testing */ it->ptr = buf; - it->next_ptr = 0; it->string = s; m_str1ng_utf8_encode (buf, i); m_str1ng_set_size(s, strlen(buf)); - bool b = string_end_p(it); - assert (b == false); - assert (it->u == i); assert (string_get_cref(it) == i); - assert (it->ptr[it->next_ptr] == 0); assert (m_str1ng_utf8_length(buf) == 1); assert (m_str1ng_utf8_valid_str_p(buf) == true); /* Higher level access */