commit 017f2b62406f3ccd4c95eeb7748dfc40224987fd
parent c6e814bc389c90eef887b075d5790fc45c99f8e9
Author: Chris Bracken <chris@bracken.jp>
Date: Fri, 20 Feb 2026 20:29:52 +0900
oid: where possible avoid extra heap allocations for oids
Diffstat:
16 files changed, 59 insertions(+), 41 deletions(-)
diff --git a/src/git/commit.c b/src/git/commit.c
@@ -24,14 +24,12 @@ GitCommit* gitcommit_create(const git_oid* oid, git_repository* repo) {
}
// Get OID, parent OID.
- commit->oid = ecalloc(kOidLen, sizeof(char));
- git_oid_tostr(commit->oid, kOidLen, git_commit_id(gcommit));
+ git_oid_tostr(commit->oid, sizeof(commit->oid), git_commit_id(gcommit));
if (git_commit_parentcount(gcommit) > 0) {
- commit->parentoid = ecalloc(kOidLen, sizeof(char));
- git_oid_tostr(commit->parentoid, kOidLen,
+ git_oid_tostr(commit->parentoid, sizeof(commit->parentoid),
git_commit_parent_id(gcommit, 0));
} else {
- commit->parentoid = NULL;
+ commit->parentoid[0] = '\0';
}
// Set commit summary, message.
@@ -124,8 +122,6 @@ void gitcommit_free(GitCommit* commit) {
if (!commit) {
return;
}
- free(commit->oid);
- free(commit->parentoid);
free(commit->summary);
free(commit->message);
free(commit->author_name);
diff --git a/src/git/commit.h b/src/git/commit.h
@@ -3,10 +3,12 @@
#include <time.h>
+#include "git/constants.h"
+
/* A git commit. */
typedef struct GitCommit {
- char* oid;
- char* parentoid;
+ char oid[GOUT_OID_MAX_SIZE];
+ char parentoid[GOUT_OID_MAX_SIZE];
char* summary;
char* message;
time_t commit_time;
diff --git a/src/git/commit_tests.c b/src/git/commit_tests.c
@@ -1,12 +1,13 @@
+#include <ftw.h>
#include <git2.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+
+#include "git/commit.h"
#include "git/internal.h"
#include "utest.h"
-#include <ftw.h>
-
struct git_commit_test {
char repo_path[1024];
git_repository* repo;
@@ -57,8 +58,8 @@ UTEST_F(git_commit_test, CommitParentHandling) {
GitCommit* root = gitcommit_create(&commit_id, utest_fixture->repo);
ASSERT_NE(NULL, root);
- /* parentoid should be exactly NULL, not an empty string. */
- EXPECT_EQ(NULL, root->parentoid);
+ /* parentoid should be an empty string if there's no parent. */
+ EXPECT_STREQ("", root->parentoid);
EXPECT_STREQ("Initial commit", root->summary);
/* Create a child commit. */
@@ -70,7 +71,7 @@ UTEST_F(git_commit_test, CommitParentHandling) {
GitCommit* child = gitcommit_create(&child_id, utest_fixture->repo);
ASSERT_NE(NULL, child);
- ASSERT_NE(NULL, child->parentoid);
+ ASSERT_NE('\0', child->parentoid[0]);
char root_oid_str[GIT_OID_MAX_HEXSIZE + 1];
git_oid_tostr(root_oid_str, sizeof(root_oid_str), &commit_id);
diff --git a/src/git/constants.h b/src/git/constants.h
@@ -0,0 +1,12 @@
+#ifndef GOUT_GIT_CONSTANTS_H_
+#define GOUT_GIT_CONSTANTS_H_
+
+/* Maximum length of a hex-encoded OID string, including null terminator.
+ * (Matches GIT_OID_MAX_HEXSIZE + 1) */
+#define GOUT_OID_MAX_SIZE 41
+
+/* Maximum length of a file mode string, including null terminator.
+ * (e.g. "m---------") */
+#define GOUT_FILEMODE_MAX_SIZE 11
+
+#endif // GOUT_GIT_CONSTANTS_H_
diff --git a/src/git/file.c b/src/git/file.c
@@ -20,10 +20,18 @@ GitFile* gitfile_create(FileType type,
GitFile* file = ecalloc(1, sizeof(GitFile));
file->type = type;
- file->mode = mode ? estrdup(mode) : NULL;
+ if (mode) {
+ estrlcpy(file->mode, mode, sizeof(file->mode));
+ } else {
+ file->mode[0] = '\0';
+ }
file->display_path = display_path ? estrdup(display_path) : NULL;
file->repo_path = repo_path ? estrdup(repo_path) : NULL;
- file->commit_oid = commit_oid ? estrdup(commit_oid) : NULL;
+ if (commit_oid) {
+ estrlcpy(file->commit_oid, commit_oid, sizeof(file->commit_oid));
+ } else {
+ file->commit_oid[0] = '\0';
+ }
file->size_bytes = size_bytes;
file->size_lines = size_lines;
if (content && size_bytes >= 0) {
@@ -39,10 +47,8 @@ void gitfile_free(GitFile* file) {
if (!file) {
return;
}
- free(file->mode);
free(file->display_path);
free(file->repo_path);
- free(file->commit_oid);
free(file->content);
free(file);
}
diff --git a/src/git/file.h b/src/git/file.h
@@ -3,6 +3,8 @@
#include <sys/types.h>
+#include "git/constants.h"
+
typedef enum {
kFileTypeFile,
kFileTypeSubmodule,
@@ -10,11 +12,11 @@ typedef enum {
typedef struct GitFile {
FileType type;
- char* mode;
+ char mode[GOUT_FILEMODE_MAX_SIZE];
char* display_path;
char* repo_path;
// Submodule commit OID. Empty string for files.
- char* commit_oid;
+ char commit_oid[GOUT_OID_MAX_SIZE];
ssize_t size_bytes;
ssize_t size_lines;
char* content;
diff --git a/src/git/git.c b/src/git/git.c
@@ -12,7 +12,8 @@
#include "utils.h"
/* Global const data. */
-const size_t kOidLen = GIT_OID_MAX_HEXSIZE + 1;
+static_assert(GOUT_OID_MAX_SIZE == GIT_OID_MAX_HEXSIZE + 1,
+ "GOUT_OID_MAX_SIZE must match libgit2's OID size");
/* Maximum file size to load into memory, in bytes. */
static const ssize_t kMaxFileSizeBytes = 16 * 1024 * 1024;
@@ -288,9 +289,9 @@ static char get_filetype(git_filemode_t m) {
}
static char* format_filemode(git_filemode_t m) {
- char* mode = ecalloc(11, sizeof(char));
- memset(mode, '-', 10);
- mode[10] = '\0';
+ char* mode = ecalloc(GOUT_FILEMODE_MAX_SIZE, sizeof(char));
+ memset(mode, '-', GOUT_FILEMODE_MAX_SIZE - 1);
+ mode[GOUT_FILEMODE_MAX_SIZE - 1] = '\0';
mode[0] = get_filetype(m);
if (m & S_IRUSR) {
@@ -364,7 +365,7 @@ static bool gitrepo_walk_tree_files(git_repository* repo,
git_filemode_t mode = git_tree_entry_filemode(entry);
if (mode == GIT_FILEMODE_COMMIT) {
- char oid_str[kOidLen];
+ char oid_str[GOUT_OID_MAX_SIZE];
git_oid_tostr(oid_str, sizeof(oid_str), git_tree_entry_id(entry));
GitFile* fileinfo =
gitfile_create(kFileTypeSubmodule, "m---------", entrypath,
diff --git a/src/git/git.h b/src/git/git.h
@@ -9,8 +9,6 @@
#include "git/repo.h"
#include "utils.h"
-extern const size_t kOidLen;
-
typedef struct Git Git;
typedef void (*RepoCallback)(Git* git, void* user_data);
diff --git a/src/git/internal.h b/src/git/internal.h
@@ -2,7 +2,6 @@
#define GOUT_GIT_INTERNAL_H_
#include <git2.h>
-
#include "git/commit.h"
#include "git/delta.h"
#include "git/file.h"
diff --git a/src/git/repo.c b/src/git/repo.c
@@ -174,8 +174,9 @@ static void gitrepo_add_special_file(GitRepo* repo,
if (!path || path[0] == '\0') {
return;
}
- repo->special_files = reallocarray(
- repo->special_files, repo->special_files_len + 1, sizeof(RepoSpecialFile));
+ repo->special_files =
+ reallocarray(repo->special_files, repo->special_files_len + 1,
+ sizeof(RepoSpecialFile));
if (!repo->special_files) {
err(1, "reallocarray");
}
diff --git a/src/writer/atom/atom.c b/src/writer/atom/atom.c
@@ -72,7 +72,7 @@ void atom_add_commit(Atom* atom,
if (atom->remaining_commits == 0) {
return;
}
- if (!commit->oid || commit->oid[0] == '\0') {
+ if (commit->oid[0] == '\0') {
warnx("atom: processed commit with missing/empty object ID");
return;
}
diff --git a/src/writer/cache/cache.c b/src/writer/cache/cache.c
@@ -41,15 +41,15 @@ Cache* cache_create(FILE* cache_in,
cache->cache_out = cache_out;
// Read lastoid from previous cache, if it exists.
- cache->lastoid_in = ecalloc(kOidLen, sizeof(char));
+ cache->lastoid_in = ecalloc(GOUT_OID_MAX_SIZE, sizeof(char));
if (cache->cache_in) {
// Read into a local buffer that has extra space for \r\n.
- char line_buf[kOidLen + 2];
+ char line_buf[GOUT_OID_MAX_SIZE + 2];
char* oid_str = fgets(line_buf, sizeof(line_buf), cache->cache_in);
if (oid_str) {
// Strip trailing newline/carriage return before copying.
line_buf[strcspn(line_buf, "\r\n")] = '\0';
- estrlcpy(cache->lastoid_in, line_buf, kOidLen);
+ estrlcpy(cache->lastoid_in, line_buf, GOUT_OID_MAX_SIZE);
} else {
warnx("corrupt cachefile");
}
@@ -128,7 +128,7 @@ void cache_add_commit_row(Cache* cache, const GitCommit* commit) {
}
// If all commits are already written; do nothing.
if (!cache->can_add_commits ||
- strncmp(commit->oid, cache->lastoid_in, kOidLen - 1) == 0) {
+ strncmp(commit->oid, cache->lastoid_in, GOUT_OID_MAX_SIZE - 1) == 0) {
cache->can_add_commits = false;
return;
}
diff --git a/src/writer/gopher/commit_tests.c b/src/writer/gopher/commit_tests.c
@@ -130,7 +130,8 @@ UTEST_F(gopher_commit, too_large_diff) {
gopher_commit_create(&repo, g_fs_inmemory, "sha123", "Commit Title");
/* Set limits small to trigger suppression. */
- DiffLimits limits = {.max_files = 0, .max_deltas = 1000, .max_delta_lines = 1000};
+ DiffLimits limits = {
+ .max_files = 0, .max_deltas = 1000, .max_delta_lines = 1000};
gopher_commit_set_diff_limits(commit_writer, &limits);
GitCommit commit = {
diff --git a/src/writer/html/commit.c b/src/writer/html/commit.c
@@ -106,8 +106,7 @@ void html_commit_add_commit(HtmlCommit* commit, const GitCommit* git_commit) {
html_commit_write_diff_content(commit, git_commit);
}
-void html_commit_set_diff_limits(HtmlCommit* commit,
- const DiffLimits* limits) {
+void html_commit_set_diff_limits(HtmlCommit* commit, const DiffLimits* limits) {
assert(commit != NULL);
assert(limits != NULL);
commit->limits = *limits;
diff --git a/src/writer/html/commit.h b/src/writer/html/commit.h
@@ -16,8 +16,7 @@ HtmlCommit* html_commit_create(const GitRepo* repo,
void html_commit_free(HtmlCommit* commit);
void html_commit_begin(HtmlCommit* commit);
void html_commit_add_commit(HtmlCommit* commit, const GitCommit* git_commit);
-void html_commit_set_diff_limits(HtmlCommit* commit,
- const DiffLimits* limits);
+void html_commit_set_diff_limits(HtmlCommit* commit, const DiffLimits* limits);
void html_commit_end(HtmlCommit* commit);
#endif // GOUT_WRITER_HTML_COMMIT_H_
diff --git a/src/writer/html/commit_tests.c b/src/writer/html/commit_tests.c
@@ -130,7 +130,8 @@ UTEST_F(html_commit, too_large_diff) {
html_commit_create(&repo, g_fs_inmemory, "sha123", "Commit Title");
/* Set limits small to trigger suppression. */
- DiffLimits limits = {.max_files = 0, .max_deltas = 1000, .max_delta_lines = 1000};
+ DiffLimits limits = {
+ .max_files = 0, .max_deltas = 1000, .max_delta_lines = 1000};
html_commit_set_diff_limits(commit_writer, &limits);
GitCommit commit = {