Commit b9c41b542bba17bb3bcdd1ee0795845e086bcf4b

Stefan Sperling 2021-08-03T08:08:03

use less memory allocations when formatting log messages Rewrite got_object_commit_get_logmsg() such that only one memory allocation is made when creating a pretty version of a log message. ok naddy@

diff --git a/lib/object_parse.c b/lib/object_parse.c
index 011bee1..4c0fd4f 100644
--- a/lib/object_parse.c
+++ b/lib/object_parse.c
@@ -439,55 +439,48 @@ const struct got_error *
 got_object_commit_get_logmsg(char **logmsg, struct got_commit_object *commit)
 {
 	const struct got_error *err = NULL;
-	char *msg0, *msg, *line, *s;
+	const char *src;
+	char *dst;
 	size_t len;
-	int headers = 1;
 
-	*logmsg = NULL;
-
-	msg0 = strdup(commit->logmsg);
-	if (msg0 == NULL)
-		return got_error_from_errno("strdup");
-
-	/* Copy log message line by line to strip out unusual headers... */
-	msg = msg0;
-	do {
-		if ((line = strsep(&msg, "\n")) == NULL)
-			break;
-
-		if (headers == 1) {
-			if (line[0] != '\0' &&
-			    strncmp(line, GOT_COMMIT_LABEL_TREE,
-			        strlen(GOT_COMMIT_LABEL_TREE)) != 0 &&
-			    strncmp(line, GOT_COMMIT_LABEL_AUTHOR,
-			        strlen(GOT_COMMIT_LABEL_AUTHOR)) != 0 &&
-			    strncmp(line, GOT_COMMIT_LABEL_PARENT,
-			        strlen(GOT_COMMIT_LABEL_PARENT)) != 0 &&
-			    strncmp(line, GOT_COMMIT_LABEL_COMMITTER,
-			        strlen(GOT_COMMIT_LABEL_COMMITTER)) != 0)
-				continue;
-
-			if (line[0] == '\0')
-				headers = 0;
-		}
+	len = strlen(commit->logmsg);
+	*logmsg = malloc(len + 2); /* leave room for a trailing \n and \0 */
+	if (*logmsg == NULL)
+		return got_error_from_errno("malloc");
 
-		if (asprintf(&s, "%s%s\n",
-		    *logmsg ? *logmsg : "", line) == -1) {
-			err = got_error_from_errno("asprintf");
-			goto done;
+	/*
+	 * Strip out unusual headers. Headers are separated from the commit
+	 * message body by a single empty line.
+	 */
+	src = commit->logmsg;
+	dst = *logmsg;
+	while (*src != '\0' && *src != '\n') {
+		int copy_header = 1, eol = 0;
+		if (strncmp(src, GOT_COMMIT_LABEL_TREE,
+		    strlen(GOT_COMMIT_LABEL_TREE)) != 0 &&
+		    strncmp(src, GOT_COMMIT_LABEL_AUTHOR,
+		    strlen(GOT_COMMIT_LABEL_AUTHOR)) != 0 &&
+		    strncmp(src, GOT_COMMIT_LABEL_PARENT,
+		    strlen(GOT_COMMIT_LABEL_PARENT)) != 0 &&
+		    strncmp(src, GOT_COMMIT_LABEL_COMMITTER,
+		    strlen(GOT_COMMIT_LABEL_COMMITTER)) != 0)
+			copy_header = 0;
+
+		while (*src != '\0' && !eol) {
+			if (copy_header) {
+				*dst = *src;
+				dst++;
+			}
+			if (*src == '\n')
+				eol = 1;
+			src++;
 		}
-		free(*logmsg);
-		*logmsg = s;
-
-	} while (line);
+	}
+	*dst = '\0';
 
-	if (*logmsg == NULL) {
-		/* log message does not contain \n */
-		*logmsg = strdup(commit->logmsg);
-		if (*logmsg == NULL) {
-			err = got_error_from_errno("strdup");
-			goto done;
-		}
+	if (strlcat(*logmsg, src, len + 1) >= len + 1) {
+		err = got_error(GOT_ERR_NO_SPACE);
+		goto done;
 	}
 
 	/* Trim redundant trailing whitespace. */
@@ -497,8 +490,13 @@ got_object_commit_get_logmsg(char **logmsg, struct got_commit_object *commit)
 		(*logmsg)[len - 1] = '\0';
 		len--;
 	}
+
+	/* Append a trailing newline if missing. */
+	if (len > 0 && (*logmsg)[len - 1] != '\n') {
+		(*logmsg)[len] = '\n';
+		(*logmsg)[len + 1] = '\0';
+	}
 done:
-	free(msg0);
 	if (err) {
 		free(*logmsg);
 		*logmsg = NULL;