efl/src/lib/eina/eina_strbuf_common.c

1126 lines
32 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "eina_private.h"
#include "eina_str.h"
#include "eina_magic.h"
#include "eina_safety_checks.h"
#include "eina_strbuf.h"
#include "eina_strbuf_common.h"
/*============================================================================*
2010-07-27 19:37:05 -07:00
* Local *
*============================================================================*/
/**
* @cond LOCAL
*/
#define EINA_STRBUF_INIT_SIZE 32
#define EINA_STRBUF_INIT_STEP 32
#define EINA_STRBUF_MAX_STEP 4096
2010-08-16 23:58:26 -07:00
/**
* @endcond
*/
/*============================================================================*
* Global *
*============================================================================*/
/**
* @internal
* @brief Initialize the strbuf module.
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function sets up the strbuf module of Eina. It is called by
* eina_init().
*
* @see eina_init()
*/
Eina_Bool
eina_strbuf_common_init(void)
{
return EINA_TRUE;
}
/**
* @internal
* @brief Shut down the strbuf module.
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function shuts down the strbuf module set up by
* eina_strbuf_common_init(). It is called by eina_shutdown().
*
* @see eina_shutdown()
*/
Eina_Bool
eina_strbuf_common_shutdown(void)
{
return EINA_TRUE;
}
/**
* @internal
*
* init the buffer
* @param csize the character size
* @param buf the buffer to init
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*/
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;
}
/**
* @internal
*
* init the buffer without allocating the actual string (for managed)
* @param csize the character size
* @param buf the buffer to init
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*/
static Eina_Bool
_eina_strbuf_common_manage_init(size_t csize EINA_UNUSED,
Eina_Strbuf *buf,
void *str,
size_t len)
{
buf->len = len;
buf->size = len + 1;
buf->step = EINA_STRBUF_INIT_STEP;
buf->buf = str;
return EINA_TRUE;
}
/**
* @internal
*
* resize the buffer
* @param csize the character size
* @param buf the buffer to resize
* @param size the minimum size of the buffer
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*/
static inline Eina_Bool
_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'
2010-12-10 20:05:52 -08:00
/* nothing to do */
if (size == buf->size) return EINA_TRUE;
else if (size > buf->size) delta = size - buf->size;
else delta = buf->size - size;
/* check if should keep the same step (just used while growing) */
2010-12-10 20:05:52 -08:00
if ((delta <= buf->step) && (size > buf->size)) new_step = buf->step;
else
{
2010-07-27 19:37:05 -07:00
new_step = (((delta / EINA_STRBUF_INIT_STEP) + 1)
* EINA_STRBUF_INIT_STEP);
2010-12-10 20:05:52 -08:00
if (new_step > EINA_STRBUF_MAX_STEP) new_step = EINA_STRBUF_MAX_STEP;
}
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)) 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;
}
/**
* @internal
*
* If required, enlarge the buffer to fit the new size.
*
* @param csize the character size
* @param buf the buffer to resize
* @param size the minimum size of the buffer
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*/
Eina_Bool
_eina_strbuf_common_grow(size_t csize, Eina_Strbuf *buf, size_t size)
{
2010-12-10 20:05:52 -08:00
if ((size + 1) < buf->size) return EINA_TRUE;
return _eina_strbuf_common_resize(csize, buf, size);
}
/**
* @internal
*
* insert string of known length at random within existing strbuf limits.
*
* @param csize the character size
* @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_common_length_get())
*
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*/
static inline Eina_Bool
2010-07-27 19:37:05 -07:00
_eina_strbuf_common_insert_length(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t len,
size_t pos)
{
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len)))
2010-07-27 19:37:05 -07:00
return EINA_FALSE;
/* move the existing text */
memmove(((unsigned char *)(buf->buf)) + ((len + pos) * csize),
((unsigned char *)(buf->buf)) + (pos * csize),
2010-07-27 19:37:05 -07:00
(buf->len - pos) * csize);
/* and now insert the given string */
2010-12-10 23:09:07 -08:00
memcpy((unsigned char *)buf->buf + (pos * csize), str, len * csize);
buf->len += len;
memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize);
return EINA_TRUE;
}
/*============================================================================*
2010-07-27 19:37:05 -07:00
* API *
*============================================================================*/
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Create a new string buffer.
*
* @param csize the character size
* @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()
*/
Eina_Strbuf *
eina_strbuf_common_new(size_t csize)
{
Eina_Strbuf *buf;
buf = calloc(1, sizeof(Eina_Strbuf));
if (EINA_UNLIKELY(!buf)) return NULL;
if (EINA_UNLIKELY(!_eina_strbuf_common_init(csize, buf)))
{
2010-07-27 19:37:05 -07:00
eina_strbuf_common_free(buf);
return NULL;
}
return buf;
}
/**
* @internal
* @brief Create a new string buffer managing str.
*
* @param csize the character size
* @param str the 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_new(size_t csize,
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, 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;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Free a string buffer.
*
* @param buf The string buffer to free.
*
* This function frees the memory of @p buf. @p buf must have been
* created by eina_strbuf_common_new().
*/
void
eina_strbuf_common_free(Eina_Strbuf *buf)
{
if (!buf->ro) free(buf->buf);
free(buf);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Reset a string buffer.
*
* @param csize the character size
* @param buf The string buffer to reset.
*
* This function reset @p buf: the buffer len is set to 0, and the
* string is set to '\\0'. No memory is free'd.
*/
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);
}
/**
* @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;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Append a string to a buffer, reallocating as necessary.
*
* @param csize the character size
* @param buf The string buffer to append to.
* @param str The string to append.
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function appends @p str to @p buf. It computes the length of
* @p str, so is slightly slower than eina_strbuf_common_append_length(). If
* the length is known beforehand, consider using that variant. If
* @p buf can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
* returned.
*
* @see eina_strbuf_common_append()
* @see eina_strbuf_common_append_length()
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_append(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t len)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len)))
2010-07-27 19:37:05 -07:00
return EINA_FALSE;
memcpy(((unsigned char *)(buf->buf)) + (buf->len * csize), str,
(len + 1) * csize);
buf->len += len;
return EINA_TRUE;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Append a string to a buffer, reallocating as necessary,
* limited by the given length.
*
* @param csize the character size
* @param buf The string buffer to append to.
* @param str The string to append.
* @param maxlen The maximum number of characters to append.
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function appends at most @p maxlen characters of @p str to
* @p buf. It can't appends more than the length of @p str. It
* computes the length of @p str, so is slightly slower than
* eina_strbuf_common_append_length(). If the length is known beforehand,
* consider using that variant (@p maxlen should then be checked so
* that it is greater than the size of @p str). If @p str can not be
* appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is
* returned.
*
* @see eina_strbuf_common_append()
* @see eina_strbuf_common_append_length()
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_append_n(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t len,
size_t maxlen)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
2010-12-10 20:05:52 -08:00
if (len > maxlen) len = maxlen;
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len)))
2010-07-27 19:37:05 -07:00
return EINA_FALSE;
memcpy(((unsigned char *)(buf->buf)) + (buf->len * csize), str,
len * csize);
buf->len += len;
memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize);
return EINA_TRUE;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Append a string of exact length to a buffer, reallocating as necessary.
*
* @param csize the character size
* @param buf The string buffer to append to.
* @param str The string to append.
* @param length The exact length to use.
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function appends @p str to @p buf. @p str must be of size at
* most @p length. It is slightly faster than eina_strbuf_common_append() as
* it does not compute the size of @p str. It is useful when dealing
* with strings of known size, such as eina_strngshare. If @p buf
* can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
* returned.
*
* @see eina_stringshare_length()
* @see eina_strbuf_common_append()
* @see eina_strbuf_common_append_n()
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_append_length(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t length)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + length)))
2010-07-27 19:37:05 -07:00
return EINA_FALSE;
memcpy(((unsigned char *)(buf->buf)) + (buf->len * csize), str,
length * csize);
buf->len += length;
memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize);
return EINA_TRUE;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Insert a string to a buffer, reallocating as necessary.
*
* @param csize the character size
* @param buf The string buffer to insert.
* @param str The string to insert.
* @param pos The position to insert the string.
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function inserts @p str to @p buf at position @p pos. It
* computes the length of @p str, so is slightly slower than
* eina_strbuf_common_insert_length(). If the length is known beforehand,
* consider using that variant. If @p buf can't insert it, #EINA_FALSE
* is returned, otherwise #EINA_TRUE is returned.
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_insert(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t len,
size_t pos)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
2010-12-10 20:05:52 -08:00
if (pos >= buf->len) return eina_strbuf_common_append(csize, buf, str, len);
return _eina_strbuf_common_insert_length(csize, buf, str, len, pos);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen.
*
* @param csize the character size
* @param buf The string buffer to insert to.
* @param str The string to insert.
* @param maxlen The maximum number of chars to insert.
* @param pos The position to insert the string.
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function inserts @p str ot @p buf at position @p pos, with at
* most @p maxlen bytes. The number of inserted characters can not be
* greater than the length of @p str. It computes the length of
* @p str, so is slightly slower than eina_strbuf_common_insert_length(). If the
* length is known beforehand, consider using that variant (@p maxlen
* should then be checked so that it is greater than the size of
* @p str). If @p str can not be inserted, #EINA_FALSE is returned,
* otherwise, #EINA_TRUE is returned.
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_insert_n(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t len,
size_t maxlen,
size_t pos)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
if (pos >= buf->len)
2010-07-27 19:37:05 -07:00
return eina_strbuf_common_append_n(csize, buf, str, len, maxlen);
2010-12-10 20:05:52 -08:00
if (len > maxlen) len = maxlen;
return _eina_strbuf_common_insert_length(csize, buf, str, len, pos);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Insert a string of exact length to a buffer, reallocating as necessary.
*
* @param csize the character size
* @param buf The string buffer to insert to.
* @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.
*
* This function inserts @p str to @p buf. @p str must be of size at
* most @p length. It is slightly faster than eina_strbuf_common_insert() as
* it does not compute the size of @p str. It is useful when dealing
* with strings of known size, such as eina_strngshare. If @p buf
* can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
* returned.
*
* @see eina_stringshare_length()
* @see eina_strbuf_common_insert()
* @see eina_strbuf_common_insert_n()
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_insert_length(size_t csize,
Eina_Strbuf *buf,
const void *str,
size_t length,
size_t pos)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
if (pos >= buf->len)
2010-07-27 19:37:05 -07:00
return eina_strbuf_common_append_length(csize, buf, str, length);
return _eina_strbuf_common_insert_length(csize, buf, str, length, pos);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Append a character to a string buffer, reallocating as
* necessary.
*
* @param csize the character size
* @param buf The string buffer to append to.
* @param c The char to append.
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
*
* This function inserts @p c to @p buf. If it can not insert it, #EINA_FALSE
* is returned, otherwise #EINA_TRUE is returned.
*/
Eina_Bool
eina_strbuf_common_append_char(size_t csize, Eina_Strbuf *buf, const void *c)
{
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + 1)))
2010-07-27 19:37:05 -07:00
return EINA_FALSE;
memcpy(((unsigned char *)(buf->buf)) + ((buf->len)++ *csize), c, csize);
memset(((unsigned char *)(buf->buf)) + (buf->len * csize), 0, csize);
return EINA_TRUE;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Insert a character to a string buffer, reallocating as
* necessary.
*
* @param csize the character size
* @param buf The string buffer 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.
*
* This function inserts @p c to @p buf at position @p pos. If @p buf
* can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
* returned.
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_insert_char(size_t csize,
Eina_Strbuf *buf,
const void *c,
size_t pos)
{
if (pos >= buf->len)
2010-07-27 19:37:05 -07:00
return eina_strbuf_common_append_char(csize, buf, c);
return _eina_strbuf_common_insert_length(csize, buf, c, 1, pos);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Remove a slice of the given string buffer.
*
* @param csize the character size
* @param buf The string buffer 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.
*
* This function removes a slice of @p buf, starting at @p start
* (inclusive) and ending at @p end (non-inclusive). Both values are
* in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise.
*/
Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_common_remove(size_t csize,
Eina_Strbuf *buf,
size_t start,
size_t end)
{
size_t remove_len, tail_len;
2010-12-10 20:05:52 -08:00
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;
buf->ro = EINA_FALSE;
}
remove_len = end - start;
if (remove_len == buf->len)
{
2010-07-27 19:37:05 -07:00
free(buf->buf);
return _eina_strbuf_common_init(csize, buf);
}
tail_len = buf->len - end + 1; /* includes '\0' */
memmove(((unsigned char *)(buf->buf)) + (start * csize),
((unsigned char *)(buf->buf)) + (end * csize),
2010-07-27 19:37:05 -07:00
tail_len * csize);
buf->len -= remove_len;
return _eina_strbuf_common_resize(csize, buf, buf->len);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Retrieve a pointer to the contents of a string buffer
*
* @param buf The string buffer.
* @return The current string in the string buffer.
*
* This function returns the string contained in @p buf. The returned
* value must not be modified and will no longer be valid if @p buf is
* modified. In other words, any eina_strbuf_common_append() or similar will
* make that pointer invalid.
*
* @see eina_strbuf_common_string_steal()
*/
const void *
eina_strbuf_common_string_get(const Eina_Strbuf *buf)
{
return buf->buf;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Steal the contents of a string buffer.
*
* @param csize the character size
* @param buf The string buffer to steal.
* @return The current string in the string buffer.
*
* This function returns the string contained in @p buf. @p buf is
* then initialized and does not own the returned string anymore. The
* caller must release the memory of the returned string by calling
2010-07-27 19:37:05 -07:00
* free().
*
* @see eina_strbuf_common_string_get()
*/
void *
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;
buf->ro = EINA_FALSE;
}
ret = buf->buf;
// TODO: Check return value and do something clever
_eina_strbuf_common_init(csize, buf);
return ret;
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Free the contents of a string buffer but not the buffer.
*
* @param csize the character size
* @param buf The string buffer to free the string of.
2010-07-27 19:37:05 -07:00
*
* This function frees the string contained in @p buf without freeing
* @p buf.
*/
void
eina_strbuf_common_string_free(size_t csize, Eina_Strbuf *buf)
{
free(buf->buf);
_eina_strbuf_common_init(csize, buf);
}
/**
2011-04-07 06:08:48 -07:00
* @internal
* @brief Retrieve the length of the string buffer content.
*
* @param buf The string buffer.
* @return The current length of the string, in bytes.
*
* This function returns the length of @p buf.
*/
size_t
eina_strbuf_common_length_get(const Eina_Strbuf *buf)
{
return buf->len;
}
/**
* @internal
* @brief Get a read-only slice representing the current strbuf contents.
*
* @param buf The string buffer.
* @return The current memory read-only slice of the string, in bytes,
* without the trailing null byte.
*/
Eina_Slice
eina_strbuf_common_slice_get(const Eina_Strbuf *buf)
{
Eina_Slice ret;
ret.len = buf->len;
ret.mem = buf->buf;
return ret;
}
/**
* @internal
* @brief Get a read-write slice representing the current strbuf contents.
*
* @param buf The string buffer.
* @return The current memory read-write slice of the string, in
* bytes, without the trailing null byte.
*/
Eina_Rw_Slice
eina_strbuf_common_rw_slice_get(const Eina_Strbuf *buf)
{
Eina_Rw_Slice ret;
ret.len = buf->len;
ret.mem = buf->buf;
return ret;
}
2010-08-16 23:58:26 -07:00
/**
* @cond LOCAL
*/
/*FIXME: Implementing them here is a hack! */
#ifdef _STRBUF_CSIZE
# undef _STRBUF_CSIZE
#endif
#ifdef _STRBUF_MAGIC
# undef _STRBUF_MAGIC
#endif
#define _STRBUF_CSIZE 1
#define _STRBUF_MAGIC EINA_MAGIC_STRBUF
2010-08-16 23:58:26 -07:00
/**
* @endcond
*/
eina: Rename EAPI macro to EINA_API in Eina library Summary: Patch from a series of patches to rename EAPI symbols to specific library DSOs. EAPI was designed to be able to pass `__attribute__ ((visibility ("default")))` for symbols with GCC, which would mean that even if -fvisibility=hidden was used when compiling the library, the needed symbols would get exported. MSVC __almost__ works like GCC (or mingw) in which you can declare everything as export and it will just work (slower, but it will work). But there's a caveat: global variables will not work the same way for MSVC, but works for mingw and GCC. For global variables (as opposed to functions), MSVC requires correct DSO visibility for MSVC: instead of declaring a symbol as export for everything, you need to declare it as import when importing from another DSO and export when defining it locally. With current EAPI definitions, we get the following example working in mingw and MSVC (observe it doesn't define any global variables as exported symbols). Example 1: dll1: ``` EAPI void foo(void); EAPI void bar() { foo(); } ``` dll2: ``` EAPI void foo() { printf ("foo\n"); } ``` This works fine with API defined as __declspec(dllexport) in both cases and for gcc defining as `__atttribute__((visibility("default")))` However, the following: Example 2: dll1: ``` EAPI extern int foo; EAPI void foobar(void); EAPI void bar() { foo = 5; foobar(); } ``` dll2: ``` EAPI int foo = 0; EAPI void foobar() { printf ("foo %d\n", foo); } ``` This will work on mingw but will not work for MSVC. And that's why EAPI is the only solution that worked for MSVC. Co-authored-by: João Paulo Taylor Ienczak Zanette <jpaulotiz@gmail.com> Co-authored-by: Ricardo Campos <ricardo.campos@expertise.dev> Co-authored-by: Lucas Cavalcante de Sousa <lucks.sousa@gmail.com> Reviewers: jptiz, lucas, woohyun, vtorri, raster Reviewed By: jptiz, lucas, vtorri Subscribers: ProhtMeyhet, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12188
2020-11-25 04:35:48 -08:00
EINA_API Eina_Bool
2010-07-27 19:37:05 -07:00
eina_strbuf_replace(Eina_Strbuf *buf,
const char *str,
const char *with,
unsigned int n)
{
size_t len1, len2;
char *spos;
size_t pos;
2010-07-27 19:37:05 -07:00
EINA_SAFETY_ON_NULL_RETURN_VAL( str, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(with, EINA_FALSE);
EINA_MAGIC_CHECK_STRBUF(buf, 0);
2010-12-10 20:05:52 -08:00
if (n == 0) return EINA_FALSE;
spos = buf->buf;
while (n--)
{
2010-07-27 19:37:05 -07:00
spos = strstr(spos, str);
2010-12-10 20:05:52 -08:00
if (!spos || *spos == '\0') return EINA_FALSE;
if (n) spos++;
}
pos = spos - (const char *)buf->buf;
len1 = strlen(str);
len2 = strlen(with);
/* 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;
buf->ro = EINA_FALSE;
}
if (len1 != len2)
{
2010-07-27 19:37:05 -07:00
/* resize the buffer if necessary */
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf,
buf->len - len1 + len2)))
2010-12-10 20:05:52 -08:00
return EINA_FALSE; /* move the existing text */
memmove(((unsigned char *)(buf->buf)) + pos + len2,
((unsigned char *)(buf->buf)) + pos + len1,
2010-07-27 19:37:05 -07:00
buf->len - pos - len1);
}
/* and now insert the given string */
memcpy(((unsigned char *)(buf->buf)) + pos, with, len2);
buf->len += len2 - len1;
memset(((unsigned char *)(buf->buf)) + buf->len, 0, 1);
return EINA_TRUE;
}
eina: Rename EAPI macro to EINA_API in Eina library Summary: Patch from a series of patches to rename EAPI symbols to specific library DSOs. EAPI was designed to be able to pass `__attribute__ ((visibility ("default")))` for symbols with GCC, which would mean that even if -fvisibility=hidden was used when compiling the library, the needed symbols would get exported. MSVC __almost__ works like GCC (or mingw) in which you can declare everything as export and it will just work (slower, but it will work). But there's a caveat: global variables will not work the same way for MSVC, but works for mingw and GCC. For global variables (as opposed to functions), MSVC requires correct DSO visibility for MSVC: instead of declaring a symbol as export for everything, you need to declare it as import when importing from another DSO and export when defining it locally. With current EAPI definitions, we get the following example working in mingw and MSVC (observe it doesn't define any global variables as exported symbols). Example 1: dll1: ``` EAPI void foo(void); EAPI void bar() { foo(); } ``` dll2: ``` EAPI void foo() { printf ("foo\n"); } ``` This works fine with API defined as __declspec(dllexport) in both cases and for gcc defining as `__atttribute__((visibility("default")))` However, the following: Example 2: dll1: ``` EAPI extern int foo; EAPI void foobar(void); EAPI void bar() { foo = 5; foobar(); } ``` dll2: ``` EAPI int foo = 0; EAPI void foobar() { printf ("foo %d\n", foo); } ``` This will work on mingw but will not work for MSVC. And that's why EAPI is the only solution that worked for MSVC. Co-authored-by: João Paulo Taylor Ienczak Zanette <jpaulotiz@gmail.com> Co-authored-by: Ricardo Campos <ricardo.campos@expertise.dev> Co-authored-by: Lucas Cavalcante de Sousa <lucks.sousa@gmail.com> Reviewers: jptiz, lucas, woohyun, vtorri, raster Reviewed By: jptiz, lucas, vtorri Subscribers: ProhtMeyhet, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12188
2020-11-25 04:35:48 -08:00
EINA_API Eina_Bool
eina_strbuf_replace_last(Eina_Strbuf *buf,
const char *str,
const char *with)
{
size_t len1, len2;
char *spos, *spos_next;
size_t pos;
EINA_SAFETY_ON_NULL_RETURN_VAL( str, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(with, EINA_FALSE);
EINA_MAGIC_CHECK_STRBUF(buf, 0);
spos = NULL;
spos_next = strstr(buf->buf, str);
while (spos_next && *spos_next)
{
spos = spos_next;
spos_next = strstr(spos + 1, str);
}
if (!spos) return EINA_FALSE;
pos = spos - (const char *)buf->buf;
len1 = strlen(str);
len2 = strlen(with);
/* 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;
}
if (len1 != len2)
{
/* resize the buffer if necessary */
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf,
buf->len - len1 + len2)))
return EINA_FALSE; /* move the existing text */
memmove(((unsigned char *)(buf->buf)) + pos + len2,
((unsigned char *)(buf->buf)) + pos + len1,
buf->len - pos - len1);
}
/* and now insert the given string */
memcpy(((unsigned char *)(buf->buf)) + pos, with, len2);
buf->len += len2 - len1;
memset(((unsigned char *)(buf->buf)) + buf->len, 0, 1);
return EINA_TRUE;
}
eina: Rename EAPI macro to EINA_API in Eina library Summary: Patch from a series of patches to rename EAPI symbols to specific library DSOs. EAPI was designed to be able to pass `__attribute__ ((visibility ("default")))` for symbols with GCC, which would mean that even if -fvisibility=hidden was used when compiling the library, the needed symbols would get exported. MSVC __almost__ works like GCC (or mingw) in which you can declare everything as export and it will just work (slower, but it will work). But there's a caveat: global variables will not work the same way for MSVC, but works for mingw and GCC. For global variables (as opposed to functions), MSVC requires correct DSO visibility for MSVC: instead of declaring a symbol as export for everything, you need to declare it as import when importing from another DSO and export when defining it locally. With current EAPI definitions, we get the following example working in mingw and MSVC (observe it doesn't define any global variables as exported symbols). Example 1: dll1: ``` EAPI void foo(void); EAPI void bar() { foo(); } ``` dll2: ``` EAPI void foo() { printf ("foo\n"); } ``` This works fine with API defined as __declspec(dllexport) in both cases and for gcc defining as `__atttribute__((visibility("default")))` However, the following: Example 2: dll1: ``` EAPI extern int foo; EAPI void foobar(void); EAPI void bar() { foo = 5; foobar(); } ``` dll2: ``` EAPI int foo = 0; EAPI void foobar() { printf ("foo %d\n", foo); } ``` This will work on mingw but will not work for MSVC. And that's why EAPI is the only solution that worked for MSVC. Co-authored-by: João Paulo Taylor Ienczak Zanette <jpaulotiz@gmail.com> Co-authored-by: Ricardo Campos <ricardo.campos@expertise.dev> Co-authored-by: Lucas Cavalcante de Sousa <lucks.sousa@gmail.com> Reviewers: jptiz, lucas, woohyun, vtorri, raster Reviewed By: jptiz, lucas, vtorri Subscribers: ProhtMeyhet, cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12188
2020-11-25 04:35:48 -08:00
EINA_API int
eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with)
{
size_t len1, len2, len;
char *tmp_buf = NULL;
char *spos;
size_t pos, start;
size_t pos_tmp, start_tmp;
int n = 0;
2010-07-27 19:37:05 -07:00
EINA_SAFETY_ON_NULL_RETURN_VAL( str, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(with, 0);
EINA_MAGIC_CHECK_STRBUF(buf, 0);
spos = strstr(buf->buf, str);
2010-12-10 20:05:52 -08:00
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;
buf->ro = EINA_FALSE;
}
len1 = strlen(str);
len2 = strlen(with);
/* if the size of the two string is equal, it is fairly easy to replace them
* we don't need to resize the buffer or doing other calculations */
if (len1 == len2)
{
2010-07-27 19:37:05 -07:00
while (spos)
{
memcpy(spos, with, len2);
spos = strstr(spos + len2, str);
n++;
}
return n;
}
2010-07-27 19:37:05 -07:00
pos = pos_tmp = spos - (const char *)buf->buf;
tmp_buf = buf->buf;
buf->buf = malloc(buf->size);
if (EINA_UNLIKELY(!buf->buf))
{
2010-07-27 19:37:05 -07:00
buf->buf = tmp_buf;
return 0;
}
start = start_tmp = 0;
len = buf->len;
while (spos)
{
2010-07-27 19:37:05 -07:00
n++;
len = (len + len2) - len1;
/* resize the buffer if necessary */
if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf, len)))
{
/* we have to stop replacing here, because we haven't enough
* memory to go on */
len = (len + len1) - len2;
break;
}
/* copy the untouched text */
2010-12-10 20:05:52 -08:00
memcpy(((unsigned char *)(buf->buf)) + start, tmp_buf + start_tmp,
pos - start);
2010-07-27 19:37:05 -07:00
/* copy the new string */
2010-12-10 20:05:52 -08:00
memcpy(((unsigned char *)(buf->buf)) + pos, with, len2);
2010-07-27 19:37:05 -07:00
/* calculate the next positions */
start_tmp = pos_tmp + len1;
start = pos + len2;
spos = strstr(tmp_buf + start_tmp, str);
/* this calculations don't make sense if spos == NULL, but the
* calculated values won't be used, because the loop will stop
* then */
pos_tmp = spos - tmp_buf;
pos = start + pos_tmp - start_tmp;
}
/* and now copy the rest of the text */
2010-12-10 20:05:52 -08:00
memcpy(((unsigned char *)(buf->buf)) + start, tmp_buf + start_tmp,
len - start);
buf->len = len;
2010-12-10 20:05:52 -08:00
memset(((unsigned char *)(buf->buf)) + buf->len, 0, 1);
free(tmp_buf);
return n;
}