*/
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.
*
* @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);
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)
{
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.
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,
}
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)
{
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);
}