eina_str speedups.

* eina_str_split() now does the minimum number of passes and
   allocations. The first pass figures out the string size (strlen())
   and number of delimiters, so we can allocate the exact number of
   elements in array. The second repeats the loop copying elements to
   string and also setting them to the result array.

 * eina_str_split_full() is a variation of eina_str_split() that
   returns also the number of elements in array, in the case you need
   to pre-allocate another array to copy.

 * eina_strlen_bounded() is introduced to limit strlen() results, this
   is used in has_prefix and has_suffix, but possibly other use cases
   where string must be of a maximum size as we don't do useless
   iterations;



SVN revision: 46547
This commit is contained in:
Gustavo Sverzut Barbieri 2010-02-27 03:42:27 +00:00
parent b2816b08e9
commit 08127ecc3e
8 changed files with 378 additions and 40 deletions

View File

@ -44,6 +44,7 @@ eina_hamster.h \
eina_matrixsparse.h \
eina_inline_tiler.x \
eina_str.h \
eina_inline_str.x \
eina_strbuf.h
installed_mainheaderdir = $(includedir)/eina-@VMAJ@

View File

@ -0,0 +1,52 @@
/* EINA - EFL data type library
* Copyright (C) 2002-2008 Gustavo Sverzut Barbieri
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library;
* if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EINA_STR_INLINE_H_
#define EINA_STR_INLINE_H_
/**
* @addtogroup Eina_Str_Group Str
*
* @{
*/
/**
* strlen() that will count up to maxlen bytes.
*
* If one wants to know the size of @a str, but it should not be
* greater than @a maxlen, then use this function and avoid needless
* iterations after that size.
*
* @param str the string pointer, must be valid and not @c NULL.
* @param maxlen the maximum length to allow.
* @return the string size or (size_t)-1 if greater than @a maxlen.
*/
static inline size_t
eina_strlen_bounded(const char *str, size_t maxlen)
{
const char *itr, *str_maxend = str + maxlen;
for (itr = str; *itr != '\0'; itr++)
if (itr == str_maxend) return (size_t)-1;
return itr - str;
}
/**
* @}
*/
#endif /* EINA_STR_INLINE_H_ */

View File

@ -12,24 +12,26 @@
*/
/* strlcpy implementation for libc's lacking it */
EAPI size_t eina_strlcpy(char *dst, const char *src, size_t siz);
EAPI size_t eina_strlcat(char *dst, const char *src, size_t siz);
EAPI size_t eina_strlcpy(char *dst, const char *src, size_t siz) EINA_ARG_NONNULL(1, 2);
EAPI size_t eina_strlcat(char *dst, const char *src, size_t siz) EINA_ARG_NONNULL(1, 2);
EAPI Eina_Bool eina_str_has_prefix(const char *str, const char *prefix);
EAPI Eina_Bool eina_str_has_prefix(const char *str, const char *prefix) EINA_PURE EINA_ARG_NONNULL(1, 2);
EAPI Eina_Bool eina_str_has_suffix(const char *str, const char *suffix);
EAPI Eina_Bool eina_str_has_extension(const char *str, const char *ext);
EAPI Eina_Bool eina_str_has_suffix(const char *str, const char *suffix) EINA_PURE EINA_ARG_NONNULL(1, 2);
EAPI Eina_Bool eina_str_has_extension(const char *str, const char *ext) EINA_PURE EINA_ARG_NONNULL(1, 2);
EAPI char **eina_str_split(const char *string, const char *delimiter,
int max_tokens);
EAPI char **eina_str_split(const char *string, const char *delimiter, int max_tokens) EINA_ARG_NONNULL(1, 2);
EAPI char **eina_str_split_full(const char *string, const char *delimiter, int max_tokens, unsigned int *elements) EINA_ARG_NONNULL(1, 2, 3);
EAPI size_t eina_str_join_len(char *dst, size_t size, char sep, const char *a, size_t a_len, const char *b, size_t b_len);
EAPI size_t eina_str_join_len(char *dst, size_t size, char sep, const char *a, size_t a_len, const char *b, size_t b_len) EINA_ARG_NONNULL(1, 4, 6);
EAPI char *eina_str_convert(const char *enc_from, const char *enc_to, const char *text);
EAPI char *eina_str_convert(const char *enc_from, const char *enc_to, const char *text) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_ARG_NONNULL(1, 2, 3);
EAPI char *eina_str_escape(const char *str);
EAPI char *eina_str_escape(const char *str) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_ARG_NONNULL(1);
static inline size_t eina_str_join(char *dst, size_t size, char sep, const char *a, const char *b) EINA_ARG_NONNULL(1, 4, 5);
/**
* @brief Join two strings of known length.
*
@ -69,4 +71,8 @@ static inline size_t eina_str_join(char *dst, size_t size, char sep, const char
*/
#define eina_str_join_static(dst, sep, a, b) eina_str_join_len(dst, sizeof(dst), sep, a, (sizeof(a) > 0) ? sizeof(a) - 1 : 0, b, (sizeof(b) > 0) ? sizeof(b) - 1 : 0)
static inline size_t eina_strlen_bounded(const char *str, size_t maxlen) EINA_PURE EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
#include "eina_inline_str.x"
#endif /* EINA_STR_H */

View File

@ -28,6 +28,7 @@
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
@ -52,7 +53,7 @@
* Internal helper function used by eina_str_has_suffix() and
* eina_str_has_extension()
*/
static Eina_Bool
static inline Eina_Bool
eina_str_has_suffix_helper(const char *str,
const char *suffix,
int (*cmp)(const char *, const char *))
@ -61,13 +62,102 @@ eina_str_has_suffix_helper(const char *str,
size_t suffix_len;
str_len = strlen(str);
suffix_len = strlen(suffix);
if (suffix_len > str_len)
suffix_len = eina_strlen_bounded(suffix, str_len);
if (suffix_len == (size_t)-1)
return EINA_FALSE;
return cmp(str + str_len - suffix_len, suffix) == 0;
}
static inline char **
eina_str_split_full_helper(const char *str, const char *delim, int max_tokens, unsigned int *elements)
{
char *s, **str_array;
const char *src;
size_t len, dlen;
unsigned int tokens;
dlen = strlen(delim);
if (dlen == 0)
{
if (elements) *elements = 0;
return NULL;
}
tokens = 0;
src = str;
/* count tokens and check strlen(str) */
while (*src != '\0')
{
const char *d = delim, *d_end = d + dlen;
const char *tmp = src;
for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
{
if (EINA_LIKELY(*d != *tmp))
break;
}
if (EINA_UNLIKELY(d == d_end))
{
src = tmp;
tokens++;
}
else
src++;
}
len = src - str;
if ((max_tokens > 0) && (tokens > (unsigned int)max_tokens))
tokens = max_tokens;
str_array = malloc(sizeof(char *) * (tokens + 2));
if (!str_array)
{
if (elements) *elements = 0;
return NULL;
}
s = malloc(len + 1);
if (!s)
{
free(str_array);
if (elements) *elements = 0;
return NULL;
}
/* copy tokens and string */
tokens = 0;
str_array[0] = s;
src = str;
while (*src != '\0')
{
const char *d = delim, *d_end = d + dlen;
const char *tmp = src;
for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
{
if (EINA_LIKELY(*d != *tmp))
break;
}
if (EINA_UNLIKELY(d == d_end))
{
src = tmp;
*s = '\0';
s += dlen;
tokens++;
str_array[tokens] = s;
}
else
{
*s = *src;
s++;
src++;
}
}
*s = '\0';
str_array[tokens + 1] = NULL;
if (elements) *elements = (tokens + 1);
return str_array;
}
/**
* @endcond
*/
@ -189,8 +279,8 @@ eina_str_has_prefix(const char *str, const char *prefix)
size_t prefix_len;
str_len = strlen(str);
prefix_len = strlen(prefix);
if (prefix_len > str_len)
prefix_len = eina_strlen_bounded(prefix, str_len);
if (prefix_len == (size_t)-1)
return EINA_FALSE;
return (strncmp(str, prefix, prefix_len) == 0);
@ -235,6 +325,34 @@ eina_str_has_extension(const char *str, const char *ext)
return eina_str_has_suffix_helper(str, ext, strcasecmp);
}
/**
* @brief Split a string using a delimiter and returns number of elements.
*
* @param str The string to split.
* @param delim The string which specifies the places at which to split the string.
* @param max_tokens The maximum number of strings to split string into.
* @param elements Where to return the number of elements in returned
* array (not counting the terminating @c NULL). May be @c NULL.
* @return A newly-allocated NULL-terminated array of strings.
*
* This functin splits @p str into a maximum of @p max_tokens pieces,
* using the given delimiter @p delim. @p delim is not included in any
* of the resulting strings, unless @p max_tokens is reached. If
* @p max_tokens is less than @c 1, the string is splitted completely. If
* @p max_tokens is reached, the last string in the returned string
* array contains the remainder of string. The returned value is a
* newly allocated NUL-terminated array of string. To free it, free
* the first element of the array and the array itself.
*
* @see eina_str_split()
*/
EAPI char **
eina_str_split_full(const char *str, const char *delim, int max_tokens, unsigned int *elements)
{
return eina_str_split_full_helper(str, delim, max_tokens, elements);
}
/**
* @brief Split a string using a delimiter.
*
@ -255,30 +373,7 @@ eina_str_has_extension(const char *str, const char *ext)
EAPI char **
eina_str_split(const char *str, const char *delim, int max_tokens)
{
char *s, *sep, **str_array;
size_t len, dlen;
int i;
if (*delim == '\0')
return NULL;
max_tokens = ((max_tokens <= 0) ? (INT_MAX) : (max_tokens - 1));
len = strlen(str);
dlen = strlen(delim);
s = strdup(str);
str_array = malloc(sizeof(char *) * (len + 1));
for (i = 0; (i < max_tokens) && (sep = strstr(s, delim)); i++)
{
str_array[i] = s;
s = sep + dlen;
*sep = 0;
}
str_array[i++] = s;
str_array = realloc(str_array, sizeof(char *) * (i + 1));
str_array[i] = NULL;
return str_array;
return eina_str_split_full_helper(str, delim, max_tokens, NULL);
}
/**

View File

@ -54,7 +54,8 @@ eina_test_rectangle.c \
eina_test_list.c \
eina_test_matrixsparse.c \
eina_test_tiler.c \
eina_test_strbuf.c
eina_test_strbuf.c \
eina_test_str.c
eina_suite_LDADD = @CHECK_LIBS@ $(top_builddir)/src/lib/libeina.la

View File

@ -56,6 +56,7 @@ static const Eina_Test_Case etc[] = {
{ "Matrix Sparse", eina_test_matrixsparse },
{ "Eina Tiler", eina_test_tiler },
{ "Eina Strbuf", eina_test_strbuf },
{ "String", eina_test_str },
{ NULL, NULL }
};

View File

@ -44,5 +44,6 @@ void eina_test_rectangle(TCase *tc);
void eina_test_matrixsparse(TCase *tc);
void eina_test_tiler(TCase *tc);
void eina_test_strbuf(TCase *tc);
void eina_test_str(TCase *tc);
#endif /* EINA_SUITE_H_ */

View File

@ -0,0 +1,181 @@
/* EINA - EFL data type library
* Copyright (C) 2010 Gustavo Sverzut Barbieri
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library;
* if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include "eina_suite.h"
#include "Eina.h"
START_TEST(str_simple)
{
eina_init();
fail_if(!eina_str_has_prefix("", ""));
fail_if(!eina_str_has_prefix("x", "x"));
fail_if(!eina_str_has_prefix("xab", "x"));
fail_if(!eina_str_has_prefix("xab", "xab"));
fail_if(eina_str_has_prefix("x", "xab"));
fail_if(eina_str_has_prefix("xab", "xyz"));
fail_if(eina_str_has_prefix("", "x"));
fail_if(eina_str_has_prefix("X", "x"));
fail_if(eina_str_has_prefix("xAb", "X"));
fail_if(eina_str_has_prefix("xAb", "xab"));
fail_if(!eina_str_has_suffix("", ""));
fail_if(!eina_str_has_suffix("x", "x"));
fail_if(!eina_str_has_suffix("abx", "x"));
fail_if(!eina_str_has_suffix("xab", "xab"));
fail_if(eina_str_has_suffix("x", "xab"));
fail_if(eina_str_has_suffix("xab", "xyz"));
fail_if(eina_str_has_suffix("", "x"));
fail_if(eina_str_has_suffix("X", "x"));
fail_if(eina_str_has_suffix("aBx", "X"));
fail_if(eina_str_has_suffix("xaB", "Xab"));
fail_if(!eina_str_has_extension("", ""));
fail_if(!eina_str_has_extension("x", "x"));
fail_if(!eina_str_has_extension("abx", "x"));
fail_if(!eina_str_has_extension("xab", "xab"));
fail_if(!eina_str_has_extension("x", "X"));
fail_if(!eina_str_has_extension("abx", "X"));
fail_if(!eina_str_has_extension("xab", "Xab"));
fail_if(!eina_str_has_extension("X", "X"));
fail_if(!eina_str_has_extension("aBx", "X"));
fail_if(!eina_str_has_extension("xaB", "Xab"));
fail_if(eina_str_has_extension("x", "xab"));
fail_if(eina_str_has_extension("xab", "xyz"));
fail_if(eina_str_has_extension("", "x"));
fail_if(eina_str_has_extension("x", "xAb"));
fail_if(eina_str_has_extension("xab", "xYz"));
fail_if(eina_str_has_extension("", "x"));
fail_if(eina_strlen_bounded("abc", 1024) != strlen("abc"));
fail_if(eina_strlen_bounded("abc", 2) != (size_t)-1);
eina_shutdown();
}
END_TEST
START_TEST(str_split)
{
char **result;
unsigned int elements;
eina_init();
result = eina_str_split_full("nomatch", "", -1, &elements);
fail_if(result != NULL);
fail_if(elements != 0);
result = eina_str_split_full("nomatch", "x", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 1);
fail_if(strcmp(result[0], "nomatch") != 0);
free(result[0]);
free(result);
result = eina_str_split_full("nomatch", "xyz", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 1);
fail_if(strcmp(result[0], "nomatch") != 0);
free(result[0]);
free(result);
result = eina_str_split_full("match:match:match", ":", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 3);
while (elements >= 1)
{
elements--;
fail_if(strcmp(result[elements], "match") != 0);
}
free(result[0]);
free(result);
result = eina_str_split_full("a:b:c", ":", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 3);
fail_if(strcmp(result[0], "a") != 0);
fail_if(strcmp(result[1], "b") != 0);
fail_if(strcmp(result[2], "c") != 0);
free(result[0]);
free(result);
result = eina_str_split_full("a:b:", ":", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 3);
fail_if(strcmp(result[0], "a") != 0);
fail_if(strcmp(result[1], "b") != 0);
fail_if(strcmp(result[2], "") != 0);
free(result[0]);
free(result);
result = eina_str_split_full(":b:c", ":", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 3);
fail_if(strcmp(result[0], "") != 0);
fail_if(strcmp(result[1], "b") != 0);
fail_if(strcmp(result[2], "c") != 0);
free(result[0]);
free(result);
result = eina_str_split_full(":", ":", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 2);
fail_if(strcmp(result[0], "") != 0);
fail_if(strcmp(result[1], "") != 0);
free(result[0]);
free(result);
result = eina_str_split_full("a", "!!!!!!!!!", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 1);
fail_if(strcmp(result[0], "a") != 0);
free(result[0]);
free(result);
result = eina_str_split_full("aaba", "ab", -1, &elements);
fail_if(result == NULL);
fail_if(elements != 2);
fail_if(strcmp(result[0], "a") != 0);
fail_if(strcmp(result[1], "a") != 0);
free(result[0]);
free(result);
eina_shutdown();
}
END_TEST
void
eina_test_str(TCase *tc)
{
tcase_add_test(tc, str_simple);
tcase_add_test(tc, str_split);
}