[Users] [PATCH 3/3] Avoid strlen() on inner loop in textview_make_clickable_parts*()

Igor Mammedov imammedo at redhat.com
Wed Oct 24 23:41:10 CEST 2012


If "walk" is iterated at very small increments on a big buffer, cost of calling
strcasestr()->strlen(bigbuf) increases drasticaly. Instead compute buffer length
once and then just deal with increments book-keeping.

it reduces cost of strcasestr() in textview_make_clickable_parts*() from 30% to 15%

Signed-off-by: Igor Mammedov <imammedo at redhat.com>
---
 src/common/utils.c | 10 ++++++--
 src/common/utils.h |  3 +++
 src/textview.c     | 70 ++++++++++++++++++++++++++++++++----------------------
 3 files changed, 53 insertions(+), 30 deletions(-)

diff --git a/src/common/utils.c b/src/common/utils.c
index 46d3ebe..90b71dd 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -521,9 +521,15 @@ freeout:
 /* Similar to `strstr' but this function ignores the case of both strings.  */
 gchar *strcasestr(const gchar *haystack, const gchar *needle)
 {
-	register size_t haystack_len, needle_len;
+	size_t haystack_len = strlen(haystack);
+
+	return strncasestr(haystack, haystack_len, needle);
+}
+
+gchar *strncasestr(const gchar *haystack, gint haystack_len, const gchar *needle)
+{
+	register size_t needle_len;
 
-	haystack_len = strlen(haystack);
 	needle_len   = strlen(needle);
 
 	if (haystack_len < needle_len || needle_len == 0)
diff --git a/src/common/utils.h b/src/common/utils.h
index d782199..50f5027 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -308,6 +308,9 @@ gchar *strcrchomp	(gchar		*str);
 gint file_strip_crs	(const gchar 	*file);
 gchar *strcasestr	(const gchar	*haystack,
 			 const gchar	*needle);
+gchar *strncasestr	(const gchar	*haystack,
+			 gint		 haystack_len,
+			 const gchar	*needle);
 gpointer my_memmem	(gconstpointer	 haystack,
 			 size_t		 haystacklen,
 			 gconstpointer	 needle,
diff --git a/src/textview.c b/src/textview.c
index c1f06e0..8dd8863 100644
--- a/src/textview.c
+++ b/src/textview.c
@@ -1311,7 +1311,8 @@ static void textview_make_clickable_parts(TextView *textview,
 	GtkTextView *text = GTK_TEXT_VIEW(textview->text);
 	GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
 	GtkTextIter iter;
-	gchar *mybuf = g_strdup(linebuf);
+	gint mybuf_len = strlen(linebuf);
+	gchar *mybuf = g_strndup(linebuf, mybuf_len);
 	
 	/* parse table - in order of priority */
 	struct table {
@@ -1319,6 +1320,7 @@ static void textview_make_clickable_parts(TextView *textview,
 
 		/* token search function */
 		gchar    *(*search)	(const gchar *haystack,
+					 gint  haystack_len,
 					 const gchar *needle);
 		/* part parsing function */
 		gboolean  (*parse)	(const gchar *start,
@@ -1332,14 +1334,14 @@ static void textview_make_clickable_parts(TextView *textview,
 	};
 
 	static struct table parser[] = {
-		{"http://",  strcasestr, get_uri_part,   make_uri_string},
-		{"https://", strcasestr, get_uri_part,   make_uri_string},
-		{"ftp://",   strcasestr, get_uri_part,   make_uri_string},
-		{"sftp://",  strcasestr, get_uri_part,   make_uri_string},
-		{"gopher://",strcasestr, get_uri_part,   make_uri_string},
-		{"www.",     strcasestr, get_uri_part,   make_http_string},
-		{"mailto:",  strcasestr, get_uri_part,   make_uri_string},
-		{"@",        strcasestr, get_email_part, make_email_string}
+		{"http://",  strncasestr, get_uri_part,   make_uri_string},
+		{"https://", strncasestr, get_uri_part,   make_uri_string},
+		{"ftp://",   strncasestr, get_uri_part,   make_uri_string},
+		{"sftp://",  strncasestr, get_uri_part,   make_uri_string},
+		{"gopher://",strncasestr, get_uri_part,   make_uri_string},
+		{"www.",     strncasestr, get_uri_part,   make_http_string},
+		{"mailto:",  strncasestr, get_uri_part,   make_uri_string},
+		{"@",        strncasestr, get_email_part, make_email_string}
 	};
 	const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
 
@@ -1354,25 +1356,30 @@ static void textview_make_clickable_parts(TextView *textview,
 
 	if (!g_utf8_validate(linebuf, -1, NULL)) {
 		g_free(mybuf);
-		mybuf = g_malloc(strlen(linebuf)*2 +1);
-		conv_localetodisp(mybuf, strlen(linebuf)*2 +1, linebuf);
+		mybuf = g_malloc(mybuf_len*2 +1);
+		conv_localetodisp(mybuf, mybuf_len*2 +1, linebuf);
+		mybuf_len = strlen(mybuf);
 	}
 
 	gtk_text_buffer_get_end_iter(buffer, &iter);
 
 	/* parse for clickable parts, and build a list of begin and end positions  */
 	for (n = 0; n < PARSE_ELEMS; n++) {
+		gint len = mybuf_len;
+		gint needle_len = strlen(parser[n].needle);
 		for (walk = mybuf;;) {
-			gchar *scanpos = parser[n].search(walk, parser[n].needle);
+			gchar *scanpos = parser[n].search(walk, len, parser[n].needle);
 			if (scanpos) {
 				/* check if URI can be parsed */
 				if (parser[n].parse(walk, scanpos, &bp, &ep, hdr)
-						&& (size_t) (ep - bp - 1) > strlen(parser[n].needle)) {
+						&& (size_t) (ep - bp - 1) > needle_len) {
 					ADD_TXT_POS(bp, ep, n);
+					len -= ep - walk;
 					walk = ep;
-				} else
-					walk = scanpos +
-						strlen(parser[n].needle);
+				} else {
+					len -= (scanpos + needle_len) - walk;
+					walk = scanpos + needle_len;
+				}
 			} else
 				break;
 		}
@@ -1423,6 +1430,7 @@ static void textview_make_clickable_parts_later(TextView *textview,
 	GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
 	GtkTextIter start_iter, end_iter;
 	gchar *mybuf;
+	gint mybuf_len;
 	gint offset = 0;
 	/* parse table - in order of priority */
 	struct table {
@@ -1430,6 +1438,7 @@ static void textview_make_clickable_parts_later(TextView *textview,
 
 		/* token search function */
 		gchar    *(*search)	(const gchar *haystack,
+					 gint  haystack_len,
 					 const gchar *needle);
 		/* part parsing function */
 		gboolean  (*parse)	(const gchar *start,
@@ -1443,13 +1452,13 @@ static void textview_make_clickable_parts_later(TextView *textview,
 	};
 
 	static struct table parser[] = {
-		{"http://",  strcasestr, get_uri_part,   make_uri_string},
-		{"https://", strcasestr, get_uri_part,   make_uri_string},
-		{"ftp://",   strcasestr, get_uri_part,   make_uri_string},
-		{"sftp://",  strcasestr, get_uri_part,   make_uri_string},
-		{"www.",     strcasestr, get_uri_part,   make_http_string},
-		{"mailto:",  strcasestr, get_uri_part,   make_uri_string},
-		{"@",        strcasestr, get_email_part, make_email_string}
+		{"http://",  strncasestr, get_uri_part,   make_uri_string},
+		{"https://", strncasestr, get_uri_part,   make_uri_string},
+		{"ftp://",   strncasestr, get_uri_part,   make_uri_string},
+		{"sftp://",  strncasestr, get_uri_part,   make_uri_string},
+		{"www.",     strncasestr, get_uri_part,   make_http_string},
+		{"mailto:",  strncasestr, get_uri_part,   make_uri_string},
+		{"@",        strncasestr, get_email_part, make_email_string}
 	};
 	const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
 
@@ -1468,18 +1477,23 @@ static void textview_make_clickable_parts_later(TextView *textview,
 	offset = gtk_text_iter_get_offset(&start_iter);
 
 	/* parse for clickable parts, and build a list of begin and end positions  */
+	mybuf_len = strlen(mybuf);
 	for (n = 0; n < PARSE_ELEMS; n++) {
+		gint len = mybuf_len;
+		gint needle_len = strlen(parser[n].needle);
 		for (walk = mybuf;;) {
-			gchar *scanpos = parser[n].search(walk, parser[n].needle);
+			gchar *scanpos = parser[n].search(walk, len, parser[n].needle);
 			if (scanpos) {
 				/* check if URI can be parsed */
 				if (parser[n].parse(walk, scanpos, &bp, &ep, FALSE)
-						&& (size_t) (ep - bp - 1) > strlen(parser[n].needle)) {
+						&& (size_t) (ep - bp - 1) > needle_len) {
 					ADD_TXT_POS_LATER(bp, ep, n);
+					len -= ep - walk;
 					walk = ep;
-				} else
-					walk = scanpos +
-						strlen(parser[n].needle);
+				} else {
+					len -= (scanpos + needle_len) - walk;
+					walk = scanpos + needle_len;
+				}
 			} else
 				break;
 		}
-- 
1.7.11.7




More information about the Users mailing list