/**
 * @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)))
      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;
}
EAPI Eina_Bool
eina_strbuf_replace(Eina_Strbuf *buf,
                    const char *str,
                    const char *with,
                    unsigned int n)
{
   size_t len1, len2;
   char *spos;
   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);
   if (n == 0) return EINA_FALSE;

   spos = buf->buf;
   while (n--)
     {
        spos = strstr(spos, str);
        if (!spos || *spos == '\0') return EINA_FALSE;
        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);
   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;
}
/**
 * @brief Append a string to a buffer, reallocating as necessary.
 *
 * @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
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)))
		return EINA_FALSE;

	memcpy(buf->buf + (buf->len * csize), str, (len + 1) * csize);
	buf->len += len;
	return EINA_TRUE;
}
/**
 * @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
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)))
      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;
}
/**
 * @internal
 *
 * insert string of known length at random within existing strbuf limits.
 *
 * @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
_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)))
		return EINA_FALSE;

	/* move the existing text */
	memmove(buf->buf + ((len + pos) * csize), buf->buf + (pos * csize),
		(buf->len - pos) * csize);

	/* and now insert the given string */
	memcpy(buf->buf + (pos * csize), str, len * csize);

	buf->len += len;
	memset(buf->buf + (buf->len * csize), 0, csize);
	return EINA_TRUE;
}
/**
 * @brief Replace all strings with an other string.

 * @param buf the string buffer to work with.
 * @param str The string to replace.
 * @param with The replaceing string.
 * @return How often the string was replaced.
 *
 * This function replaces all the occurrences of @p str in @ buf with
 * the string @p with. This function returns the number of times @p str
 * has been replaced. On failure, it returns 0.
 */
EAPI 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;

	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);
	if (!spos || *spos == '\0')
		return 0;

	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) {
		while (spos) {
			memcpy(spos, with, len2);
			spos = strstr(spos + len2, str);
			n++;
		}
		return n;
	}

	pos = pos_tmp = spos - (const char *) buf->buf;
	tmp_buf = buf->buf;
	buf->buf = malloc(buf->size);
	if (EINA_UNLIKELY(!buf->buf)) {
		buf->buf = tmp_buf;
		return 0;
	}

	start = start_tmp = 0;
	len = buf->len;

	while (spos) {
		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 */
		memcpy(buf->buf + start, tmp_buf + start_tmp, pos - start);
		/* copy the new string */
		memcpy(buf->buf + pos, with, len2);

		/* 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 */
	memcpy(buf->buf + start, tmp_buf + start_tmp, len - start);
	buf->len = len;
	memset((char *) buf->buf + buf->len, 0, 1);

	free(tmp_buf);

	return n;
}