diff --git a/README.md b/README.md index 09f935e0..63ca8d62 100644 --- a/README.md +++ b/README.md @@ -5718,6 +5718,13 @@ Return true if both iterators reference the same position, false otherwise. Push the unicode code point 'u' into the string 'str' encoding it as a variable UTF8 encoded code point. +##### bool string\_pop\_u (string\_unicode\_t *u, string\_t str) + +Pop the last unicode code point from the string 'str' +encoded as a variable UTF8 encoded code point +and store in '*u' the popped unicode code point if 'u' is not NULL. +It returns true in case of succes or false otherwise (no character to pop or no valid UTF8). + ##### size\_t string\_length\_u(string\_t str) Return the number of UTF8 encoded code point in the string. diff --git a/m-string.h b/m-string.h index 6c3b1158..0d608861 100644 --- a/m-string.h +++ b/m-string.h @@ -63,7 +63,7 @@ typedef enum { M_STR1NG_UTF8_STARTING = 0, M_STR1NG_UTF8_DECODING_1 = 8, M_STR1NG_UTF8_DECODING_2 = 16, - M_STR1NG_UTF8_DOCODING_3 = 24, + M_STR1NG_UTF8_DECODING_3 = 24, M_STR1NG_UTF8_ERROR = 32 } m_str1ng_utf8_state_e; @@ -1873,6 +1873,38 @@ m_string_push_u (m_string_t str, m_string_unicode_t u) m_string_cat_cstr(str, buffer); } +/* Pop last unicode code point into string, encoding it in UTF8 */ +static inline bool +m_string_pop_u(m_string_unicode_t *u, m_string_t str) +{ + M_STR1NG_CONTRACT(str); + char *org = m_str1ng_get_cstr(str); + size_t len = m_string_size(str); + // From the last byte in the string + while (len > 0) { + len--; + // Test if it is a start of an UTF8 code point + if (m_str1ng_utf8_start_p((unsigned char) org[len])) { + // Yes, so decode the UTF8 + m_str1ng_utf8_state_e state = M_STR1NG_UTF8_STARTING; + const char *tmp = &org[len]; + // Support of NULL pointer + m_string_unicode_t u_tmp; + m_string_unicode_t *u_ptr = u == NULL ? &u_tmp : u; + do { + m_str1ng_utf8_decode(*tmp++, &state, u_ptr); + } while (state != M_STR1NG_UTF8_STARTING && state != M_STR1NG_UTF8_ERROR); + // Final null char for the string + org[len] = 0; + m_str1ng_set_size(str, len); + M_STR1NG_CONTRACT(str); + return true; + } + } + // Fail to pop a unicode code + return false; +} + /* Compute the length in UTF8 code points in the string */ static inline size_t m_string_length_u(m_string_t str) @@ -2674,6 +2706,7 @@ namespace m_lib { #define string_get_cref m_string_get_cref #define string_cref m_string_cref #define string_push_u m_string_push_u +#define string_pop_u m_string_pop_u #define string_length_u m_string_length_u #define string_utf8_p m_string_utf8_p #define string_set_ui m_string_set_ui diff --git a/tests/test-mstring.c b/tests/test-mstring.c index 966a2272..9bdc4949 100644 --- a/tests/test-mstring.c +++ b/tests/test-mstring.c @@ -55,6 +55,37 @@ static void test_utf8_basic(void) string_push_u(s, i); assert(string_utf8_p(s) == true); assert(string_length_u(s) == 1); + string_unicode_t j; + bool b = string_pop_u(&j, s); + assert(b); + assert(i== j); + assert(string_utf8_p(s) == true); + assert(string_length_u(s) == 0); + assert(string_size(s) == 0); + string_push_u(s, 'A'); + string_push_u(s, i); + size_t len = string_size(s); + string_push_u(s, i); + assert(string_utf8_p(s) == true); + assert(string_length_u(s) == 3); + b = string_pop_u(&j, s); + assert(b); + assert(i== j); + assert(string_utf8_p(s) == true); + assert(string_length_u(s) == 2); + assert(string_size(s) == len); + b = string_pop_u(NULL, s); + assert(b); + assert(string_utf8_p(s) == true); + assert(string_length_u(s) == 1); + assert(string_size(s) == 1); + b = string_pop_u(NULL, s); + assert(b); + assert(string_utf8_p(s) == true); + assert(string_length_u(s) == 0); + assert(string_size(s) == 0); + b = string_pop_u(NULL, s); + assert(!b); } }