gout

A static git page generator
git clone https://git.bracken.jp/gout.git
Log | Files | Refs | README | LICENSE

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:
Msrc/git/commit.c | 10+++-------
Msrc/git/commit.h | 6++++--
Msrc/git/commit_tests.c | 11++++++-----
Asrc/git/constants.h | 12++++++++++++
Msrc/git/file.c | 14++++++++++----
Msrc/git/file.h | 6++++--
Msrc/git/git.c | 11++++++-----
Msrc/git/git.h | 2--
Msrc/git/internal.h | 1-
Msrc/git/repo.c | 5+++--
Msrc/writer/atom/atom.c | 2+-
Msrc/writer/cache/cache.c | 8++++----
Msrc/writer/gopher/commit_tests.c | 3++-
Msrc/writer/html/commit.c | 3+--
Msrc/writer/html/commit.h | 3+--
Msrc/writer/html/commit_tests.c | 3++-
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 = {