From bba70aef2e059fd29c1e04d5556742cf5f162efd Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Fri, 26 Feb 2010 21:33:17 +0000 Subject: [PATCH] eina_strbuf_insert variants and fix leak with eina_strbuf_append_escaped() SVN revision: 46521 --- legacy/eina/src/include/eina_strbuf.h | 27 ++-- legacy/eina/src/lib/eina_strbuf.c | 185 ++++++++++++++++++++--- legacy/eina/src/tests/eina_test_strbuf.c | 90 +++++++++++ 3 files changed, 275 insertions(+), 27 deletions(-) diff --git a/legacy/eina/src/include/eina_strbuf.h b/legacy/eina/src/include/eina_strbuf.h index 6a1f2bab2b..5915f249a8 100644 --- a/legacy/eina/src/include/eina_strbuf.h +++ b/legacy/eina/src/include/eina_strbuf.h @@ -10,23 +10,32 @@ typedef struct _Eina_Strbuf Eina_Strbuf; EAPI Eina_Strbuf *eina_strbuf_new(void); EAPI void eina_strbuf_free(Eina_Strbuf *buf) EINA_ARG_NONNULL(1); EAPI void eina_strbuf_reset(Eina_Strbuf *buf) EINA_ARG_NONNULL(1); + EAPI Eina_Bool eina_strbuf_append(Eina_Strbuf *buf, const char *str) EINA_ARG_NONNULL(1, 2); EAPI Eina_Bool eina_strbuf_append_escaped(Eina_Strbuf *buf, const char *str) EINA_ARG_NONNULL(1, 2); -EAPI Eina_Bool eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, unsigned int maxlen) EINA_ARG_NONNULL(1, 2); -EAPI Eina_Bool eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, unsigned int length) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, size_t maxlen) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, size_t length) EINA_ARG_NONNULL(1, 2); EAPI Eina_Bool eina_strbuf_append_char(Eina_Strbuf *buf, char c) EINA_ARG_NONNULL(1); -EAPI Eina_Bool eina_strbuf_insert(Eina_Strbuf *buf, const char *str, - size_t pos) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool eina_strbuf_insert(Eina_Strbuf *buf, const char *str, size_t pos) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_strbuf_insert_escaped(Eina_Strbuf *buf, const char *str, size_t pos) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_strbuf_insert_n(Eina_Strbuf *buf, const char *str, size_t maxlen, size_t pos) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_strbuf_insert_length(Eina_Strbuf *buf, const char *str, size_t length, size_t pos) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_strbuf_insert_char(Eina_Strbuf *buf, char c, size_t pos) EINA_ARG_NONNULL(1); + #define eina_strbuf_prepend(buf, str) eina_strbuf_insert(buf, str, 0) -EAPI Eina_Bool eina_strbuf_remove(Eina_Strbuf *buf, unsigned int start, unsigned int end) EINA_ARG_NONNULL(1); +#define eina_strbuf_prepend_n(buf, str, maxlen) eina_strbuf_insert_n(buf, str, maxlen, 0) +#define eina_strbuf_prepend_length(buf, str, length) eina_strbuf_insert_length(buf, str, length, 0) +#define eina_strbuf_prepend_char(buf, c) eina_strbuf_insert_char(buf, c, 0) + + +EAPI Eina_Bool eina_strbuf_remove(Eina_Strbuf *buf, size_t start, size_t end) EINA_ARG_NONNULL(1); EAPI const char *eina_strbuf_string_get(const Eina_Strbuf *buf) EINA_ARG_NONNULL(1); EAPI char *eina_strbuf_string_steal(Eina_Strbuf *buf) EINA_ARG_NONNULL(1); EAPI size_t eina_strbuf_length_get(const Eina_Strbuf *buf) EINA_ARG_NONNULL(1); -EAPI Eina_Bool eina_strbuf_replace(Eina_Strbuf *buf, const char *str, - const char *with, unsigned int n) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_strbuf_replace(Eina_Strbuf *buf, const char *str, const char *with, unsigned int n) EINA_ARG_NONNULL(1, 2, 3); #define eina_strbuf_replace_first(buf, str, with) \ eina_strbuf_replace(buf, str, with, 1) -EAPI int eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, - const char *with) EINA_ARG_NONNULL(1, 2, 3); +EAPI int eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with) EINA_ARG_NONNULL(1, 2, 3); #endif /* EINA_STRBUF_H */ diff --git a/legacy/eina/src/lib/eina_strbuf.c b/legacy/eina/src/lib/eina_strbuf.c index c279204eec..e55a09c709 100644 --- a/legacy/eina/src/lib/eina_strbuf.c +++ b/legacy/eina/src/lib/eina_strbuf.c @@ -44,6 +44,8 @@ struct _Eina_Strbuf static Eina_Bool _eina_strbuf_init(Eina_Strbuf *buf); static Eina_Bool _eina_strbuf_resize(Eina_Strbuf *buf, size_t size); +static inline Eina_Bool _eina_strbuf_insert_length(Eina_Strbuf *buf, const char *str, size_t len, size_t pos); + #define _eina_strbuf_grow(_buf, _size) \ ((((_size) + 1) > (_buf)->size) ? _eina_strbuf_resize((_buf), (_size)) : EINA_TRUE) @@ -195,7 +197,10 @@ eina_strbuf_append_escaped(Eina_Strbuf *buf, const char *str) return EINA_FALSE; len = strlen(esc); if (EINA_UNLIKELY(!_eina_strbuf_grow(buf, buf->len + len))) - return EINA_FALSE; + { + free(esc); + return EINA_FALSE; + } memcpy(buf->buf + buf->len, esc, len + 1); buf->len += len; free(esc); @@ -204,6 +209,12 @@ eina_strbuf_append_escaped(Eina_Strbuf *buf, const char *str) /** * Append a string to a buffer, reallocating as necessary. Limited by maxlen. + * + * This is slightly slower than eina_strbuf_append_length(), as it + * needs to strlen() the given pointer. If you know the size + * beforehand, consider using that variant (of course, you need to + * check if the maxlen is greater than the string size yourself). + * * @param buf the Eina_Strbuf to append to * @param str the string to append * @param maxlen maximum number of chars to append @@ -214,7 +225,7 @@ eina_strbuf_append_escaped(Eina_Strbuf *buf, const char *str) * @see eina_strbuf_append_length() */ EAPI Eina_Bool -eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, unsigned int maxlen) +eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, size_t maxlen) { size_t len; @@ -250,7 +261,7 @@ eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, unsigned int maxlen) * @see eina_strbuf_append_n() */ EAPI Eina_Bool -eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, unsigned int length) +eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, size_t length) { EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); @@ -266,6 +277,11 @@ eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, unsigned int length /** * Insert a string to a buffer, reallocating as necessary. + * + * This is slightly slower than eina_strbuf_insert_length(), as it + * needs to strlen() the given pointer. If you know the size + * beforehand, consider using that variant. + * * @param buf the Eina_Strbuf to insert * @param str the string to insert * @param pos the position to insert the string @@ -283,19 +299,93 @@ eina_strbuf_insert(Eina_Strbuf *buf, const char *str, size_t pos) if (pos >= buf->len) return eina_strbuf_append(buf, str); - /* - * resize the buffer if necessary - */ len = strlen(str); - if (EINA_UNLIKELY(!_eina_strbuf_grow(buf, buf->len + len))) + return _eina_strbuf_insert_length(buf, str, len, pos); +} + +/** + * Insert an escaped string to a buffer, reallocating as necessary. + * @param buf the Eina_Strbuf to insert to + * @param str the string to insert + * @param pos the position to insert the string + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +EAPI Eina_Bool +eina_strbuf_insert_escaped(Eina_Strbuf *buf, const char *str, size_t pos) +{ + Eina_Bool ret; + size_t len; + char *esc; + + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + + esc = eina_str_escape(str); + if (EINA_UNLIKELY(!esc)) return EINA_FALSE; - /* move the existing text */ - memmove(buf->buf + len + pos, buf->buf + pos, buf->len - pos); - /* and now insert the given string */ - memcpy(buf->buf + pos, str, len); - buf->len += len; - buf->buf[buf->len] = 0; - return EINA_TRUE; + len = strlen(esc); + ret = _eina_strbuf_insert_length(buf, esc, len, pos); + free(esc); + return ret; +} + +/** + * Insert a string to a buffer, reallocating as necessary. Limited by maxlen. + * + * This is slightly slower than eina_strbuf_insert_length(), as it + * needs to strlen() the given pointer. If you know the size + * beforehand, consider using that variant (of course, you need to + * check if the maxlen is greater than the string size yourself). + * + * @param buf the Eina_Strbuf to insert + * @param str the string to insert + * @param maxlen maximum number of chars to insert + * @param pos the position to insert the string + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +EAPI Eina_Bool +eina_strbuf_insert_n(Eina_Strbuf *buf, const char *str, size_t maxlen, size_t pos) +{ + size_t len; + + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + + if (pos >= buf->len) + return eina_strbuf_append_n(buf, str, maxlen); + + len = strlen(str); + if (len > maxlen) len = maxlen; + return _eina_strbuf_insert_length(buf, str, len, pos); +} + +/** + * Insert a string of exact length to a buffer, reallocating as necessary. + * + * This is a slightly faster version that does not need to strlen() + * the whole string to know its size. Useful when dealing with strings + * of known size, such as eina_stringshare, see + * eina_stringshare_length(). + * + * @param buf the Eina_Strbuf to insert + * @param str the string to insert + * @param length the exact length to use. + * @param pos the position to insert the string + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +EAPI Eina_Bool +eina_strbuf_insert_length(Eina_Strbuf *buf, const char *str, size_t length, size_t pos) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + + if (pos >= buf->len) + return eina_strbuf_append_length(buf, str, length); + + return _eina_strbuf_insert_length(buf, str, length, pos); } /** @@ -317,10 +407,40 @@ eina_strbuf_append_char(Eina_Strbuf *buf, char c) return EINA_TRUE; } +/** + * Insert a character to a string buffer, reallocating as necessary. + * @param buf the Eina_Strbuf to insert to + * @param c the char to insert + * @param pos the position to insert the char + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ EAPI Eina_Bool -eina_strbuf_remove(Eina_Strbuf *buf, unsigned int start, unsigned int end) +eina_strbuf_insert_char(Eina_Strbuf *buf, char c, size_t pos) { - unsigned int remove_len, tail_len; + EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); + + if (pos >= buf->len) + return eina_strbuf_append_char(buf, c); + + return _eina_strbuf_insert_length(buf, &c, 1, pos); +} + +/** + * Remove slice of string buffer. + * + * @param buf the Eina_Strbuf to remove a slice. + * @param start the initial (inclusive) slice position to start + * removing, in bytes. + * @param end the final (non-inclusive) slice position to finish + * removing, in bytes. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +EAPI Eina_Bool +eina_strbuf_remove(Eina_Strbuf *buf, size_t start, size_t end) +{ + size_t remove_len, tail_len; EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE); @@ -410,8 +530,7 @@ eina_strbuf_length_get(const Eina_Strbuf *buf) * @return #EINA_TRUE on success, #EINA_FALSE on failure. */ EAPI Eina_Bool -eina_strbuf_replace(Eina_Strbuf *buf, const char *str, const char *with, - unsigned int n) +eina_strbuf_replace(Eina_Strbuf *buf, const char *str, const char *with, size_t n) { EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(with, EINA_FALSE); @@ -638,3 +757,33 @@ _eina_strbuf_resize(Eina_Strbuf *buf, size_t size) buf->step = new_step; return EINA_TRUE; } + +/** + * @internal + * + * insert string of known length at random within existing strbuf limits. + * + * @param buf the buffer to resize, must be valid. + * @param str the string to copy, must be valid (!NULL and smaller than @a len) + * @param len the amount of bytes in @a str to copy, must be valid. + * @param pos the position inside buffer to insert, must be valid (smaller + * than eina_strbuf_length_get()) + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + */ +static inline Eina_Bool +_eina_strbuf_insert_length(Eina_Strbuf *buf, const char *str, size_t len, size_t pos) +{ + if (EINA_UNLIKELY(!_eina_strbuf_grow(buf, buf->len + len))) + return EINA_FALSE; + + /* move the existing text */ + memmove(buf->buf + len + pos, buf->buf + pos, buf->len - pos); + + /* and now insert the given string */ + memcpy(buf->buf + pos, str, len); + + buf->len += len; + buf->buf[buf->len] = '\0'; + return EINA_TRUE; +} diff --git a/legacy/eina/src/tests/eina_test_strbuf.c b/legacy/eina/src/tests/eina_test_strbuf.c index 06d2cf374a..9c8ae59807 100644 --- a/legacy/eina/src/tests/eina_test_strbuf.c +++ b/legacy/eina/src/tests/eina_test_strbuf.c @@ -180,6 +180,24 @@ START_TEST(strbuf_insert) fail_if(strlen(eina_strbuf_string_get(buf)) != eina_strbuf_length_get(buf)); fail_if(strcmp(eina_strbuf_string_get(buf), "1xyz23abcxyz")); + eina_strbuf_insert_n(buf, "ABCDEF", 2, 1); + fail_if(strlen(eina_strbuf_string_get(buf)) != eina_strbuf_length_get(buf)); + fail_if(strcmp(eina_strbuf_string_get(buf), "1ABxyz23abcxyz")); + + eina_strbuf_insert_n(buf, "EINA", 2, 3); + fail_if(strlen(eina_strbuf_string_get(buf)) != eina_strbuf_length_get(buf)); + fail_if(strcmp(eina_strbuf_string_get(buf), "1ABEIxyz23abcxyz")); + + eina_strbuf_insert_escaped(buf, "678", 3); + fail_if(strlen(eina_strbuf_string_get(buf)) != eina_strbuf_length_get(buf)); + fail_if(strncmp(eina_strbuf_string_get(buf) + 3, "678", 3)); + + eina_strbuf_insert_escaped(buf, "089 '\\", 9); + fail_if(strlen(eina_strbuf_string_get(buf)) != eina_strbuf_length_get(buf)); + fail_if(strncmp(eina_strbuf_string_get(buf) + 9, "089\\ \\'\\\\", + strlen("089\\ \\'\\\\"))); + eina_strbuf_reset(buf); + eina_strbuf_free(buf); eina_shutdown(); @@ -234,6 +252,76 @@ START_TEST(strbuf_replace) } END_TEST +START_TEST(strbuf_append_realloc) +{ + Eina_Strbuf *buf; + const size_t runs = 40960; + const char target_pattern[] = "stringstrsstr"; + const char *str; + size_t i, target_pattern_size; + + eina_init(); + + buf = eina_strbuf_new(); + fail_if(!buf); + + for (i = 0; i < runs; i++) + { + fail_if(!eina_strbuf_append(buf, "string")); + fail_if(!eina_strbuf_append_n(buf, "string", 3)); + fail_if(!eina_strbuf_append_char(buf, 's')); + fail_if(!eina_strbuf_append_length(buf, "string", 3)); + } + + target_pattern_size = strlen(target_pattern); + fail_if(eina_strbuf_length_get(buf) != (runs * target_pattern_size)); + + str = eina_strbuf_string_get(buf); + fail_if(str == NULL); + for (i = 0; i < runs; i++, str += target_pattern_size) + fail_if(memcmp(str, target_pattern, target_pattern_size)); + + eina_strbuf_free(buf); + + eina_shutdown(); +} +END_TEST + +START_TEST(strbuf_prepend_realloc) +{ + Eina_Strbuf *buf; + const size_t runs = 40960; + const char target_pattern[] = "strsstrstring"; + const char *str; + size_t i, target_pattern_size; + + eina_init(); + + buf = eina_strbuf_new(); + fail_if(!buf); + + for (i = 0; i < runs; i++) + { + fail_if(!eina_strbuf_prepend(buf, "string")); + fail_if(!eina_strbuf_prepend_n(buf, "string", 3)); + fail_if(!eina_strbuf_prepend_char(buf, 's')); + fail_if(!eina_strbuf_prepend_length(buf, "string", 3)); + } + + target_pattern_size = strlen(target_pattern); + fail_if(eina_strbuf_length_get(buf) != (runs * target_pattern_size)); + + str = eina_strbuf_string_get(buf); + fail_if(str == NULL); + for (i = 0; i < runs; i++, str += target_pattern_size) + fail_if(memcmp(str, target_pattern, target_pattern_size)); + + eina_strbuf_free(buf); + + eina_shutdown(); +} +END_TEST + void eina_test_strbuf(TCase *tc) { @@ -242,4 +330,6 @@ eina_test_strbuf(TCase *tc) tcase_add_test(tc, strbuf_append); tcase_add_test(tc, strbuf_insert); tcase_add_test(tc, strbuf_replace); + tcase_add_test(tc, strbuf_append_realloc); + tcase_add_test(tc, strbuf_prepend_realloc); }