From c13d0f7bd4e696d5db3345e00f763df23347b6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 18 May 2026 22:25:01 +0200 Subject: [PATCH 1/2] strbuf: use st_add3() in strbuf_grow() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify the code by calling st_add3() to do overflow checks instead of open-coding it. This changes the error message to include the offending summands, which can be helpful when tracking down the cause. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- strbuf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/strbuf.c b/strbuf.c index 3e04addc22..8610965d53 100644 --- a/strbuf.c +++ b/strbuf.c @@ -106,12 +106,10 @@ void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc) void strbuf_grow(struct strbuf *sb, size_t extra) { int new_buf = !sb->alloc; - if (unsigned_add_overflows(extra, 1) || - unsigned_add_overflows(sb->len, extra + 1)) - die("you want to use way too much memory"); + size_t new_len = st_add3(sb->len, extra, 1); if (new_buf) sb->buf = NULL; - ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); + ALLOC_GROW(sb->buf, new_len, sb->alloc); if (new_buf) sb->buf[0] = '\0'; } From 29d9fdcf1098672b2146c60eea5202e840615772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 18 May 2026 22:25:02 +0200 Subject: [PATCH 2/2] use __builtin_add_overflow() in st_add() with Clang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clang and GCC optimize away comparisons of overflow checks by checking the carry flag on x64. GCC does the same on ARM64, but Clang currently (version 22.1) doesn't. It does this optimization for overflow checks that use its builtin function __builtin_add_overflow(), though. Provide a non-generic lookalike for size_t that does the same checks as before as a fallback and use the original with Clang. Use it on all platforms for simplicity. On an Apple M1 I get a nice speedup for a command that builds lots of strings using a strbuf, which exercises the st_add3() in strbuf_grow() for every line of output: Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectname)' Time (mean ± σ): 120.4 ms ± 0.2 ms [User: 113.8 ms, System: 6.0 ms] Range (min … max): 120.1 ms … 121.1 ms 24 runs Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectname)' Time (mean ± σ): 115.5 ms ± 0.1 ms [User: 108.6 ms, System: 5.8 ms] Range (min … max): 115.2 ms … 115.8 ms 25 runs Summary ./git cat-file --batch-all-objects --batch-check='%(objectname)' ran 1.04 ± 0.00 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectname)' Suggested-by: Jeff King Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- git-compat-util.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/git-compat-util.h b/git-compat-util.h index ae1bdc90a4..5b1d15fe4f 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -614,12 +614,30 @@ static inline bool strip_suffix(const char *str, const char *suffix, int git_open_cloexec(const char *name, int flags); #define git_open(name) git_open_cloexec(name, O_RDONLY) -static inline size_t st_add(size_t a, size_t b) + +/* + * Help Clang; GCC generates the same instructions for both variants on + * x64 and aarch64. + */ +#ifdef __clang__ +#define st_add_overflow __builtin_add_overflow +#else +static inline bool st_add_overflow(size_t a, size_t b, size_t *out) { if (unsigned_add_overflows(a, b)) + return true; + *out = a + b; + return false; +} +#endif + +static inline size_t st_add(size_t a, size_t b) +{ + size_t result; + if (st_add_overflow(a, b, &result)) die("size_t overflow: %"PRIuMAX" + %"PRIuMAX, (uintmax_t)a, (uintmax_t)b); - return a + b; + return result; } #define st_add3(a,b,c) st_add(st_add((a),(b)),(c)) #define st_add4(a,b,c,d) st_add(st_add3((a),(b),(c)),(d))