eina: add eina_{str,bin}buf_manage_read_only_new_length().

It is sometime useful to start from a defined buffer, but to not touch it
until needed. This make life of caller more easier as they don't need to
duplicate the buffer themself as Eina will now take care of that.
This commit is contained in:
Cedric BAIL 2014-01-02 15:36:23 +09:00
parent 342dd72e37
commit 55a56fe5c0
6 changed files with 201 additions and 6 deletions

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);
}