diff --git a/src/lib/eina/eina_binbuf.h b/src/lib/eina/eina_binbuf.h index f887b9b811..23f27986b1 100644 --- a/src/lib/eina/eina_binbuf.h +++ b/src/lib/eina/eina_binbuf.h @@ -64,6 +64,26 @@ EAPI Eina_Binbuf *eina_binbuf_new(void) EINA_MALLOC EINA_WARN_UNUSED_RESULT; */ EAPI Eina_Binbuf *eina_binbuf_manage_new_length(unsigned char *str, size_t length) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +/** + * @brief Create a new string buffer using the passed string. The passed + * string is used directly as the buffer, it's somehow the opposite function of + * @ref eina_binbuf_string_steal . The passed string will not be touched. + * + * @param str the string to start from + * @param length the length of the string. + * @return Newly allocated string buffer instance. + * + * This function creates a new string buffer. On error, @c NULL is + * returned. To free the resources, use eina_binbuf_free(). It will + * not touch the internal buffer. Any changing operation will + * create a fresh new memory, copy old data there and starting modifying + * that one. + * + * @see eina_binbuf_manage_new() + * @since 1.9.0 + */ +EAPI Eina_Binbuf *eina_binbuf_manage_read_only_new_length(const unsigned char *str, size_t length) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + /** * @brief Free a string buffer. * @@ -120,6 +140,7 @@ EAPI Eina_Bool eina_binbuf_append_length(Eina_Binbuf *buf, const unsigned char * * @see eina_binbuf_append() * @see eina_binbuf_append_n() * @see eina_binbuf_append_length() + * @since 1.9.0 */ EAPI Eina_Bool eina_binbuf_append_buffer(Eina_Binbuf *buf, const Eina_Binbuf *data) EINA_ARG_NONNULL(1, 2); diff --git a/src/lib/eina/eina_binbuf_template_c.x b/src/lib/eina/eina_binbuf_template_c.x index 84541cf746..b9f5c5abf3 100644 --- a/src/lib/eina/eina_binbuf_template_c.x +++ b/src/lib/eina/eina_binbuf_template_c.x @@ -74,6 +74,15 @@ _FUNC_EXPAND(manage_new_length)(_STRBUF_DATA_TYPE *str, size_t length) return buf; } +EAPI _STRBUF_STRUCT_NAME * +_FUNC_EXPAND(manage_read_only_new_length)(const _STRBUF_DATA_TYPE *str, size_t length) +{ + _STRBUF_STRUCT_NAME *buf = + eina_strbuf_common_manage_ro_new(_STRBUF_CSIZE, (void *) str, length); + EINA_MAGIC_SET(buf, _STRBUF_MAGIC); + return buf; +} + EAPI void _FUNC_EXPAND(free)(_STRBUF_STRUCT_NAME *buf) { diff --git a/src/lib/eina/eina_strbuf.h b/src/lib/eina/eina_strbuf.h index 45e0bd355d..1a628b9fef 100644 --- a/src/lib/eina/eina_strbuf.h +++ b/src/lib/eina/eina_strbuf.h @@ -113,6 +113,23 @@ EAPI Eina_Strbuf *eina_strbuf_manage_new(char *str) EINA_MALLOC EINA_WARN_UNUSED */ EAPI Eina_Strbuf *eina_strbuf_manage_new_length(char *str, size_t length) EINA_MALLOC EINA_WARN_UNUSED_RESULT; +/** + * @brief Create a new string buffer using the passed string. The passed + * string is used directly as the buffer, it's somehow the opposite function of + * @ref eina_strbuf_string_steal . The passed string must be malloced. + * + * @param str the string to manage + * @param length the length of the string. + * @return Newly allocated string buffer instance. + * + * This function creates a new string buffer. On error, @c NULL is + * returned. To free the resources, use eina_strbuf_free(). + * + * @see eina_strbuf_manage_new() + * @since 1.9.0 + */ +EAPI Eina_Strbuf *eina_strbuf_manage_read_only_new_length(const char *str, size_t length) EINA_MALLOC EINA_WARN_UNUSED_RESULT; + /** * @brief Free a string buffer. * @@ -223,6 +240,7 @@ EAPI Eina_Bool eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, size * @see eina_strbuf_append() * @see eina_strbuf_append_n() * @see eina_strbuf_append_length() + * @since 1.9.0 */ EAPI Eina_Bool eina_strbuf_append_buffer(Eina_Strbuf *buf, const Eina_Strbuf *data) EINA_ARG_NONNULL(1, 2); diff --git a/src/lib/eina/eina_strbuf_common.c b/src/lib/eina/eina_strbuf_common.c index e0faf49145..9c401835b9 100644 --- a/src/lib/eina/eina_strbuf_common.c +++ b/src/lib/eina/eina_strbuf_common.c @@ -83,10 +83,11 @@ eina_strbuf_common_shutdown(void) static Eina_Bool _eina_strbuf_common_init(size_t csize, Eina_Strbuf *buf) { + buf->ro = EINA_FALSE; buf->len = 0; buf->size = EINA_STRBUF_INIT_SIZE; buf->step = EINA_STRBUF_INIT_STEP; - + buf->buf = calloc(csize, buf->size); if (EINA_UNLIKELY(!buf->buf)) return EINA_FALSE; return EINA_TRUE; @@ -131,6 +132,7 @@ _eina_strbuf_common_resize(size_t csize, Eina_Strbuf *buf, size_t size) { size_t new_size, new_step, delta; void *buffer; + void *copy; size += 1; // Add extra space for '\0' @@ -150,14 +152,30 @@ _eina_strbuf_common_resize(size_t csize, Eina_Strbuf *buf, size_t size) new_size = (((size / new_step) + 1) * new_step); + if (buf->size == new_size * csize) return EINA_TRUE; + + copy = buf->buf; + if (EINA_UNLIKELY(buf->ro)) + buf->buf = NULL; + /* reallocate the buffer to the new size */ buffer = realloc(buf->buf, new_size * csize); - if (EINA_UNLIKELY(!buffer)) return EINA_FALSE; + if (EINA_UNLIKELY(!buffer)) goto on_error; + + if (EINA_UNLIKELY(buf->ro)) + { + memcpy(buffer, copy, buf->len); + buf->ro = EINA_FALSE; + } buf->buf = buffer; buf->size = new_size; buf->step = new_step; return EINA_TRUE; + + on_error: + if (buf->ro) buf->buf = copy; + return EINA_FALSE; } /** @@ -238,7 +256,7 @@ eina_strbuf_common_new(size_t csize) { Eina_Strbuf *buf; - buf = malloc(sizeof(Eina_Strbuf)); + buf = calloc(1, sizeof(Eina_Strbuf)); if (EINA_UNLIKELY(!buf)) return NULL; if (EINA_UNLIKELY(!_eina_strbuf_common_init(csize, buf))) { @@ -272,16 +290,52 @@ eina_strbuf_common_manage_new(size_t csize, { Eina_Strbuf *buf; - buf = malloc(sizeof(Eina_Strbuf)); + buf = calloc(1, sizeof(Eina_Strbuf)); if (EINA_UNLIKELY(!buf)) return NULL; if (EINA_UNLIKELY(!_eina_strbuf_common_manage_init(csize, buf, str, len))) - { + { eina_strbuf_common_free(buf); return NULL; } return buf; } +/** + * @internal + * @brief Create a new string buffer managing read only str. + * + * @param csize the character size + * @param str the read only string to manage + * @param len the length of the string to manage + * @return Newly allocated string buffer instance. + * + * This function creates a new string buffer. On error, @c NULL is + * returned. To free the resources, use eina_strbuf_common_free(). + * + * @see eina_strbuf_common_free() + * @see eina_strbuf_common_append() + * @see eina_strbuf_common_string_get() + * @since 1.1.0 + */ +Eina_Strbuf * +eina_strbuf_common_manage_ro_new(size_t csize, + const void *str, + size_t len) +{ + Eina_Strbuf *buf; + + buf = calloc(1, sizeof(Eina_Strbuf)); + if (EINA_UNLIKELY(!buf)) return NULL; + if (EINA_UNLIKELY(!_eina_strbuf_common_manage_init(csize, buf, + (void*) str, len))) + { + eina_strbuf_common_free(buf); + return NULL; + } + buf->ro = EINA_TRUE; + return buf; +} + /** * @internal * @brief Free a string buffer. @@ -294,7 +348,7 @@ eina_strbuf_common_manage_new(size_t csize, void eina_strbuf_common_free(Eina_Strbuf *buf) { - free(buf->buf); + if (!buf->ro) free(buf->buf); free(buf); } @@ -311,6 +365,14 @@ eina_strbuf_common_free(Eina_Strbuf *buf) void eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf) { + /* This is a read only buffer which need change to be made */ + if (buf->ro) + { + _eina_strbuf_common_init(csize, buf); + buf->ro = EINA_FALSE; + return ; + } + buf->len = 0; buf->step = EINA_STRBUF_INIT_STEP; memset(buf->buf, 0, csize); @@ -608,6 +670,17 @@ eina_strbuf_common_remove(size_t csize, if (end >= buf->len) end = buf->len; if (end <= start) return EINA_TRUE; + /* This is a read only buffer which need change to be made */ + if (buf->ro) + { + char *dest; + + dest = malloc(buf->size); + if (!dest) return 0; + memcpy(dest, buf->buf, buf->len); + buf->buf = dest; + } + remove_len = end - start; if (remove_len == buf->len) { @@ -663,6 +736,19 @@ eina_strbuf_common_string_steal(size_t csize, Eina_Strbuf *buf) { void *ret; + // If the buffer is ro, the caller would have to do additional + // test to detect if it is the same string or not. Let's make + // life for everyone easy. + if (buf->ro) + { + char *dest; + + dest = malloc(buf->size); + if (!dest) return 0; + memcpy(dest, buf->buf, buf->len); + buf->buf = dest; + } + ret = buf->buf; // TODO: Check return value and do something clever _eina_strbuf_common_init(csize, buf); @@ -753,6 +839,17 @@ eina_strbuf_replace(Eina_Strbuf *buf, if (n) spos++; } + /* This is a read only buffer which need change to be made */ + if (buf->ro) + { + char *dest; + + dest = malloc(buf->size); + if (!dest) return 0; + memcpy(dest, buf->buf, buf->len); + buf->buf = dest; + } + pos = spos - (const char *)buf->buf; len1 = strlen(str); len2 = strlen(with); @@ -789,6 +886,18 @@ eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with) spos = strstr(buf->buf, str); if (!spos || *spos == '\0') return 0; + + /* This is a read only buffer which need change to be made */ + if (buf->ro) + { + char *dest; + + dest = malloc(buf->size); + if (!dest) return 0; + memcpy(dest, buf->buf, buf->len); + buf->buf = dest; + } + len1 = strlen(str); len2 = strlen(with); /* if the size of the two string is equal, it is fairly easy to replace them diff --git a/src/lib/eina/eina_strbuf_common.h b/src/lib/eina/eina_strbuf_common.h index 06a13533ff..47fbe9e3aa 100644 --- a/src/lib/eina/eina_strbuf_common.h +++ b/src/lib/eina/eina_strbuf_common.h @@ -19,6 +19,8 @@ struct _Eina_Strbuf size_t step; EINA_MAGIC + + Eina_Bool ro : 1; }; #define EINA_MAGIC_CHECK_STRBUF(d, ...) \ @@ -41,6 +43,11 @@ Eina_Strbuf * eina_strbuf_common_manage_new(size_t csize, void *str, size_t len); +Eina_Strbuf * +eina_strbuf_common_manage_ro_new(size_t csize, + const void *str, + size_t len); + void eina_strbuf_common_free(Eina_Strbuf *buf); void diff --git a/src/tests/eina/eina_test_binbuf.c b/src/tests/eina/eina_test_binbuf.c index d62072ebab..b016bc6f3c 100644 --- a/src/tests/eina/eina_test_binbuf.c +++ b/src/tests/eina/eina_test_binbuf.c @@ -125,6 +125,36 @@ START_TEST(binbuf_manage_simple) } END_TEST +START_TEST(binbuf_manage_read_only_simple) +{ + Eina_Binbuf *buf; + const char *_cbuf = "12\0 456 78\0 abcthis is some more random junk here!"; + const unsigned char *cbuf = (const unsigned char *) _cbuf; + size_t size = sizeof(cbuf) - 1; /* We don't care about the real NULL */ + + eina_init(); + + buf = eina_binbuf_manage_read_only_new_length(_cbuf, size); + fail_if(!buf); + + eina_binbuf_free(buf); + + buf = eina_binbuf_manage_read_only_new_length(_cbuf, size); + fail_if(!buf); + + fail_if(eina_binbuf_string_get(buf) != cbuf); + fail_if(size != eina_binbuf_length_get(buf)); + eina_binbuf_append_length(buf, cbuf, size); + fail_if(memcmp(eina_binbuf_string_get(buf), cbuf, size)); + fail_if(memcmp(eina_binbuf_string_get(buf) + size, cbuf, size)); + fail_if(2 * size != eina_binbuf_length_get(buf)); + + eina_binbuf_free(buf); + + eina_shutdown(); +} +END_TEST + START_TEST(binbuf_insert) { #if 0 @@ -260,4 +290,5 @@ eina_test_binbuf(TCase *tc) tcase_add_test(tc, binbuf_insert); tcase_add_test(tc, binbuf_realloc); tcase_add_test(tc, binbuf_manage_simple); + tcase_add_test(tc, binbuf_manage_read_only_simple); }