forked from enlightenment/efl
eina_binbuf: allow expand & usage of extra bytes.
Some code needs to read directly into eina_binbuf to avoid an extra copy from eina_binbuf_append* variants. This can be achieved by using eina_binbuf_expand(), which returns a write-able slice of the spare bytes. Once they are used, eina_binbuf_use() should be called to increment "buf->len", which is used by all other binbuf functions such as eina_binbuf_length_get() or eina_binbuf_append_slice().
This commit is contained in:
parent
3680ae0bab
commit
09825cbe5d
|
@ -129,6 +129,58 @@ EAPI void eina_binbuf_free(Eina_Binbuf *buf) EINA_ARG_NONNULL(1);
|
|||
*/
|
||||
EAPI void eina_binbuf_reset(Eina_Binbuf *buf) EINA_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* @brief Expand a buffer, making room for at least @a minimum_unused_space.
|
||||
*
|
||||
* One of the properties of the buffer is that it may overallocate
|
||||
* space, thus it may have more than eina_binbuf_length_get() bytes
|
||||
* allocated. How much depends on buffer growing logic, but this
|
||||
* function allows one to request a minimum amount of bytes to be
|
||||
* allocated at the end of the buffer.
|
||||
*
|
||||
* This is particularly useful to write directly to buffer's memory
|
||||
* (ie: a call to read(2)). After the bytes are used call
|
||||
* eina_binbuf_use() to mark them as such, so eina_binbuf_length_get()
|
||||
* will consider the new bytes.
|
||||
*
|
||||
* @param buf The Buffer to expand.
|
||||
* @param minimum_unused_space The minimum unused allocated space, in
|
||||
* bytes, at the end of the buffer. Zero can be used to query
|
||||
* the available slice of unused bytes.
|
||||
*
|
||||
* @return The slice of unused bytes. The slice length may be zero if
|
||||
* @a minimum_unused_space couldn't be allocated, otherwise it
|
||||
* will be at least @a minimum_unused_space. After bytes are used,
|
||||
* mark them as such using eina_binbuf_use().
|
||||
*
|
||||
* @see eina_binbuf_rw_slice_get()
|
||||
* @see eina_binbuf_use()
|
||||
*
|
||||
* @since 1.19
|
||||
*/
|
||||
EAPI Eina_Rw_Slice eina_binbuf_expand(Eina_Binbuf *buf, size_t minimum_unused_space) EINA_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* @brief Mark more bytes as used.
|
||||
*
|
||||
* This function should be used after eina_binbuf_expand(), marking
|
||||
* the extra bytes returned there as used, then they will be
|
||||
* considered in all other functions, such as eina_binbuf_length_get().
|
||||
*
|
||||
* @param buf The buffer to mark extra bytes as used.
|
||||
* @param extra_bytes the number of bytes to be considered used, must
|
||||
* be between zero and the length of the slice returned by
|
||||
* eina_binbuf_expand().
|
||||
*
|
||||
* @return #EINA_TRUE on success, #EINA_FALSE on failure, such as @a
|
||||
* extra_bytes is too big or @a buf is NULL.
|
||||
*
|
||||
* @see eina_binbuf_expand()
|
||||
*
|
||||
* @since 1.19
|
||||
*/
|
||||
EAPI Eina_Bool eina_binbuf_use(Eina_Binbuf *buf, size_t extra_bytes) EINA_ARG_NONNULL(1);
|
||||
|
||||
/**
|
||||
* @brief Append a string of exact length to a buffer, reallocating as necessary.
|
||||
*
|
||||
|
@ -330,6 +382,10 @@ EAPI Eina_Slice eina_binbuf_slice_get(const Eina_Binbuf *buf) EINA_WARN_UNUSED_R
|
|||
* @return a read-write slice for the current contents. It may become
|
||||
* invalid as soon as the @a buf is changed with calls such as
|
||||
* eina_binbuf_append(), eina_binbuf_remove()
|
||||
*
|
||||
* @see eina_binbuf_expand()
|
||||
*
|
||||
* @since 1.19
|
||||
*/
|
||||
EAPI Eina_Rw_Slice eina_binbuf_rw_slice_get(const Eina_Binbuf *buf) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
|
||||
|
||||
|
|
|
@ -100,6 +100,21 @@ _FUNC_EXPAND(reset)(_STRBUF_STRUCT_NAME *buf)
|
|||
eina_strbuf_common_reset(_STRBUF_CSIZE, buf);
|
||||
}
|
||||
|
||||
EAPI Eina_Rw_Slice
|
||||
_FUNC_EXPAND(expand)(_STRBUF_STRUCT_NAME *buf, size_t minimum_unused_space)
|
||||
{
|
||||
Eina_Rw_Slice ret = {.len = 0, .mem = NULL};
|
||||
EINA_MAGIC_CHECK_STRBUF(buf, ret);
|
||||
return eina_strbuf_common_expand(_STRBUF_CSIZE, buf, minimum_unused_space);
|
||||
}
|
||||
|
||||
EAPI Eina_Bool
|
||||
_FUNC_EXPAND(use)(_STRBUF_STRUCT_NAME *buf, size_t extra_bytes)
|
||||
{
|
||||
EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
|
||||
return eina_strbuf_common_use(buf, extra_bytes);
|
||||
}
|
||||
|
||||
EAPI Eina_Bool
|
||||
_FUNC_EXPAND(append_length)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t length)
|
||||
{
|
||||
|
|
|
@ -378,6 +378,88 @@ eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf)
|
|||
memset(buf->buf, 0, csize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Expand a buffer, making room for at least @a minimum_unused_space.
|
||||
*
|
||||
* One of the properties of the buffer is that it may overallocate
|
||||
* space, thus it may have more than eina_strbuf_common_length_get() bytes
|
||||
* allocated. How much depends on buffer growing logic, but this
|
||||
* function allows one to request a minimum amount of bytes to be
|
||||
* allocated at the end of the buffer.
|
||||
*
|
||||
* This is particularly useful to write directly to buffer's memory
|
||||
* (ie: a call to read(2)). After the bytes are used call
|
||||
* eina_strbuf_common_use() to mark them as such, so
|
||||
* eina_strbuf_common_length_get() will consider the new bytes.
|
||||
*
|
||||
* @param csize the character size
|
||||
* @param buf The Buffer to expand.
|
||||
* @param minimum_unused_space The minimum unused allocated space, in
|
||||
* bytes, at the end of the buffer. Zero can be used to query
|
||||
* the available slice of unused bytes.
|
||||
*
|
||||
* @return The slice of unused bytes. The slice length may be zero if
|
||||
* @a minimum_unused_space couldn't be allocated, otherwise it
|
||||
* will be at least @a minimum_unused_space. After bytes are used,
|
||||
* mark them as such using eina_strbuf_common_use().
|
||||
*
|
||||
* @see eina_strbuf_common_rw_slice_get()
|
||||
* @see eina_strbuf_common_use()
|
||||
*
|
||||
* @since 1.19
|
||||
*/
|
||||
Eina_Rw_Slice
|
||||
eina_strbuf_common_expand(size_t csize,
|
||||
Eina_Strbuf *buf,
|
||||
size_t minimum_unused_space)
|
||||
{
|
||||
Eina_Rw_Slice ret = { .mem = NULL, .len = 0 };
|
||||
|
||||
if (EINA_LIKELY(buf->len + minimum_unused_space < buf->size)) goto end;
|
||||
|
||||
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + minimum_unused_space)))
|
||||
return ret;
|
||||
|
||||
end:
|
||||
ret.mem = (unsigned char *)buf->buf + (buf->len * csize);
|
||||
ret.len = buf->size - buf->len - 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Mark more bytes as used.
|
||||
*
|
||||
* This function should be used after eina_strbuf_common_expand(),
|
||||
* marking the extra bytes returned there as used, then they will be
|
||||
* considered in all other functions, such as
|
||||
* eina_strbuf_common_length_get().
|
||||
*
|
||||
* @param csize the character size
|
||||
* @param buf The buffer to mark extra bytes as used.
|
||||
* @param extra_bytes the number of bytes to be considered used, must
|
||||
* be between zero and the length of the slice returned by
|
||||
* eina_strbuf_common_expand().
|
||||
*
|
||||
* @return #EINA_TRUE on success, #EINA_FALSE on failure, such as @a
|
||||
* extra_bytes is too big or @a buf is NULL.
|
||||
*
|
||||
* @see eina_strbuf_common_expand()
|
||||
*
|
||||
* @since 1.19
|
||||
*/
|
||||
Eina_Bool
|
||||
eina_strbuf_common_use(Eina_Strbuf *buf,
|
||||
size_t extra_bytes)
|
||||
{
|
||||
if (EINA_UNLIKELY(buf->size < buf->len + extra_bytes + 1))
|
||||
return EINA_FALSE;
|
||||
|
||||
buf->len += extra_bytes;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @brief Append a string to a buffer, reallocating as necessary.
|
||||
|
|
|
@ -52,6 +52,15 @@ void
|
|||
eina_strbuf_common_free(Eina_Strbuf *buf);
|
||||
void
|
||||
eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf);
|
||||
|
||||
Eina_Rw_Slice
|
||||
eina_strbuf_common_expand(size_t csize,
|
||||
Eina_Strbuf *buf,
|
||||
size_t minimum_unused_space);
|
||||
Eina_Bool
|
||||
eina_strbuf_common_use(Eina_Strbuf *buf,
|
||||
size_t extra_bytes);
|
||||
|
||||
Eina_Bool
|
||||
eina_strbuf_common_append(size_t csize,
|
||||
Eina_Strbuf *buf,
|
||||
|
|
|
@ -306,6 +306,74 @@ START_TEST(binbuf_realloc)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(binbuf_expand)
|
||||
{
|
||||
Eina_Binbuf *buf;
|
||||
Eina_Rw_Slice rw_slice;
|
||||
Eina_Slice ro_slice;
|
||||
Eina_Slice hello_world = EINA_SLICE_STR_LITERAL("Hello World");
|
||||
Eina_Slice hi_there = EINA_SLICE_STR_LITERAL("Hi There");
|
||||
size_t i;
|
||||
Eina_Bool r;
|
||||
|
||||
eina_init();
|
||||
|
||||
buf = eina_binbuf_new();
|
||||
fail_if(!buf);
|
||||
|
||||
rw_slice = eina_binbuf_rw_slice_get(buf);
|
||||
ck_assert_int_eq(rw_slice.len, 0);
|
||||
|
||||
/* force it to grow to 'Hello World' */
|
||||
r = eina_binbuf_append_slice(buf, hello_world);
|
||||
ck_assert_int_eq(r, EINA_TRUE);
|
||||
|
||||
ro_slice = eina_binbuf_slice_get(buf);
|
||||
ck_assert_int_eq(ro_slice.len, hello_world.len);
|
||||
ck_assert_int_eq(eina_slice_compare(ro_slice, hello_world), 0);
|
||||
|
||||
/* reset doesn't change allocated size, 'Hi there' will fit */
|
||||
eina_binbuf_reset(buf);
|
||||
rw_slice = eina_binbuf_expand(buf, hi_there.len);
|
||||
ck_assert_int_ge(rw_slice.len, hi_there.len);
|
||||
/* access bytes directly */
|
||||
rw_slice = eina_rw_slice_copy(rw_slice, hi_there);
|
||||
r = eina_binbuf_use(buf, rw_slice.len);
|
||||
ck_assert_int_eq(r, EINA_TRUE);
|
||||
ck_assert_int_eq(eina_slice_compare(eina_binbuf_slice_get(buf), hi_there), 0);
|
||||
|
||||
/* start with 'Hello World */
|
||||
eina_binbuf_reset(buf);
|
||||
r = eina_binbuf_append_slice(buf, hello_world);
|
||||
ck_assert_int_eq(r, EINA_TRUE);
|
||||
|
||||
/* force it to realloc */
|
||||
rw_slice = eina_binbuf_expand(buf, 8192);
|
||||
ck_assert_int_ge(rw_slice.len, 8192);
|
||||
ck_assert_ptr_ne(rw_slice.mem, NULL);
|
||||
|
||||
memset(rw_slice.mem, 0xfe, rw_slice.len);
|
||||
|
||||
r = eina_binbuf_use(buf, rw_slice.len);
|
||||
ck_assert_int_eq(r, EINA_TRUE);
|
||||
|
||||
r = eina_binbuf_use(buf, 1); /* would go too much */
|
||||
ck_assert_int_eq(r, EINA_FALSE);
|
||||
|
||||
ro_slice = eina_binbuf_slice_get(buf);
|
||||
ck_assert_int_eq(ro_slice.len, hello_world.len + rw_slice.len);
|
||||
ck_assert_int_eq(memcmp(ro_slice.mem, hello_world.mem, hello_world.len), 0);
|
||||
|
||||
for (i = hello_world.len; i < ro_slice.len; i++)
|
||||
ck_assert_int_eq(ro_slice.bytes[i], 0xfe);
|
||||
|
||||
eina_binbuf_free(buf);
|
||||
|
||||
eina_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
void
|
||||
eina_test_binbuf(TCase *tc)
|
||||
{
|
||||
|
@ -315,4 +383,5 @@ eina_test_binbuf(TCase *tc)
|
|||
tcase_add_test(tc, binbuf_realloc);
|
||||
tcase_add_test(tc, binbuf_manage_simple);
|
||||
tcase_add_test(tc, binbuf_manage_read_only_simple);
|
||||
tcase_add_test(tc, binbuf_expand);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue