From 952bf223e050fdd576eb573dd099875fbb3518c2 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Tue, 12 Nov 2024 12:07:39 -0600 Subject: [PATCH] strbuf: Add strbuf_reserve_extra() To accomplish the same task as scratchbuf_alloc() does: make sure the buffer has enough space for next operations. One difference is that ensures **extra** space, not the total space. If needed in future, a strbuf_reserve(), that resembles C++'s std::vector::reserve(), can be added on top. Signed-off-by: Lucas De Marchi Reviewed-by: Emil Velikov Link: https://github.com/kmod-project/kmod/pull/239 --- shared/strbuf.c | 19 +++++++++++-------- shared/strbuf.h | 5 +++++ testsuite/test-strbuf.c | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/shared/strbuf.c b/shared/strbuf.c index 836101c8..5a25976c 100644 --- a/shared/strbuf.c +++ b/shared/strbuf.c @@ -34,15 +34,18 @@ static bool buf_realloc(struct strbuf *buf, size_t sz) return true; } -static bool buf_grow(struct strbuf *buf, size_t newsize) +bool strbuf_reserve_extra(struct strbuf *buf, size_t n) { - if (newsize <= buf->size) + if (uaddsz_overflow(buf->used, n, &n)) + return false; + + if (n <= buf->size) return true; - if (newsize % BUF_STEP) - newsize = ((newsize / BUF_STEP) + 1) * BUF_STEP; + if (n % BUF_STEP) + n = ((n / BUF_STEP) + 1) * BUF_STEP; - return buf_realloc(buf, newsize); + return buf_realloc(buf, n); } void strbuf_init(struct strbuf *buf) @@ -75,7 +78,7 @@ char *strbuf_steal(struct strbuf *buf) const char *strbuf_str(struct strbuf *buf) { - if (!buf_grow(buf, buf->used + 1)) + if (!strbuf_reserve_extra(buf, 1)) return NULL; buf->bytes[buf->used] = '\0'; return buf->bytes; @@ -83,7 +86,7 @@ const char *strbuf_str(struct strbuf *buf) bool strbuf_pushchar(struct strbuf *buf, char ch) { - if (!buf_grow(buf, buf->used + 1)) + if (!strbuf_reserve_extra(buf, 1)) return false; buf->bytes[buf->used] = ch; buf->used++; @@ -99,7 +102,7 @@ size_t strbuf_pushchars(struct strbuf *buf, const char *str) len = strlen(str); - if (!buf_grow(buf, buf->used + len)) + if (!strbuf_reserve_extra(buf, len)) return 0; memcpy(buf->bytes + buf->used, str, len); diff --git a/shared/strbuf.h b/shared/strbuf.h index b2aa946e..d916d0af 100644 --- a/shared/strbuf.h +++ b/shared/strbuf.h @@ -62,6 +62,11 @@ char *strbuf_steal(struct strbuf *buf); */ const char *strbuf_str(struct strbuf *buf); +/* + * Reserve enough space for @n bytes, ensuring additional pushes up to @n bytes + * don't cause re-allocations + */ +bool strbuf_reserve_extra(struct strbuf *buf, size_t n); bool strbuf_pushchar(struct strbuf *buf, char ch); size_t strbuf_pushchars(struct strbuf *buf, const char *str); diff --git a/testsuite/test-strbuf.c b/testsuite/test-strbuf.c index 1c8f6040..45c9eec7 100644 --- a/testsuite/test-strbuf.c +++ b/testsuite/test-strbuf.c @@ -163,4 +163,26 @@ static int test_strbuf_with_heap(const struct test *t) } DEFINE_TEST(test_strbuf_with_heap, .description = "test strbuf with heap only"); +static int test_strbuf_reserve_extra(const struct test *t) +{ + _cleanup_strbuf_ struct strbuf buf; + size_t size; + + strbuf_init(&buf); + strbuf_reserve_extra(&buf, strlen(TEXT) + 1); + size = buf.size; + assert_return(size >= strlen(TEXT) + 1, EXIT_FAILURE); + + strbuf_pushchars(&buf, TEXT); + strbuf_pushchar(&buf, '\0'); + assert_return(size == buf.size, EXIT_FAILURE); + + strbuf_clear(&buf); + strbuf_pushchars(&buf, TEXT); + assert_return(size == buf.size, EXIT_FAILURE); + + return 0; +} +DEFINE_TEST(test_strbuf_reserve_extra, .description = "test strbuf_reserve_extra"); + TESTSUITE_MAIN(); -- 2.47.2