From 5c96031834179913948a278843b7f67de13fbdf9 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 16 Mar 2024 13:35:12 +0100 Subject: [PATCH] feat: add auto-expanding buffer implementation for html writer --- include/mrk/common.h | 8 +++++ src/_include/mrk/buf.h | 43 ++++++++++++++++++++++++++ src/buf.c | 70 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 src/_include/mrk/buf.h create mode 100644 src/buf.c diff --git a/include/mrk/common.h b/include/mrk/common.h index 3a8094c..b75eff7 100644 --- a/include/mrk/common.h +++ b/include/mrk/common.h @@ -11,6 +11,14 @@ *out = temp; \ } +#define MRK_MALLOC(out, size) \ + { \ + void *temp = malloc(size); \ + if (temp == NULL) \ + return mrk_err_failed_alloc; \ + *out = temp; \ + } + #define MRK_RES(x) \ { \ mrk_err res = x; \ diff --git a/src/_include/mrk/buf.h b/src/_include/mrk/buf.h new file mode 100644 index 0000000..6463028 --- /dev/null +++ b/src/_include/mrk/buf.h @@ -0,0 +1,43 @@ +#ifndef MRK_BUF +#define MRK_BUF + +#include + +#include "mrk/common.h" + +/** + * Wrapper around a nul-terminated char buffer that grows as needed + */ +typedef struct mrk_buf { + char *s; + size_t len; + size_t cap; +} mrk_buf; + +/** + * Initialize a (usually stack-allocated) buffer + */ +mrk_err mrk_buf_init(mrk_buf *buf); + +/** + * Double the buffer's internal capacity until it has enough + * room for the additional elements. + */ +mrk_err mrk_buf_expand(mrk_buf *buf, size_t required); + +/** + * Shrink the internal buffer so that its capacity equals the stored length. + */ +mrk_err mrk_buf_compact(mrk_buf *buf); + +/** + * Append the given nul-terminated string to the stored string. + */ +mrk_err mrk_buf_append(mrk_buf *buf, const char *s); + +/** + * Append the char buffer with the given length to the stored string. + */ +mrk_err mrk_buf_append_n(mrk_buf *buf, const char *s, size_t len); + +#endif diff --git a/src/buf.c b/src/buf.c new file mode 100644 index 0000000..78f64ef --- /dev/null +++ b/src/buf.c @@ -0,0 +1,70 @@ +#include + +#include "mrk/buf.h" + +mrk_err mrk_buf_init(mrk_buf *buf) { + MRK_MALLOC(&buf->s, 2); + + // 2 ^ 0 + buf->cap = 1; + buf->len = 0; + buf->s[buf->len] = '\0'; + + return mrk_err_ok; +} + +mrk_err mrk_buf_expand(mrk_buf *buf, size_t required) { + size_t new_cap = buf->cap; + + while (buf->len + required >= new_cap) { + // Grow buffer size in powers of two + new_cap <<= 1; + } + + // No need to reallocate if there's already enough space + if (new_cap == buf->cap) { + return mrk_err_ok; + } + + char *new_buf = realloc(buf->s, new_cap + 1); + + if (new_buf == NULL) { + return mrk_err_failed_alloc; + } + + buf->s = new_buf; + buf->cap = new_cap; + + return mrk_err_ok; +} + +mrk_err mrk_buf_compact(mrk_buf *buf) { + char *new_buf = realloc(buf->s, buf->len + 1); + + if (new_buf == NULL) { + return mrk_err_failed_alloc; + } + + buf->s = new_buf; + buf->cap = buf->len; + + return mrk_err_ok; +} + +mrk_err mrk_buf_append(mrk_buf *buf, const char *s) { + size_t len = strlen(s); + + return mrk_buf_append_n(buf, s, len); +} + +mrk_err mrk_buf_append_n(mrk_buf *buf, const char *s, size_t len) { + MRK_RES(mrk_buf_expand(buf, len)); + + memcpy(&buf->s[buf->len], s, len); + buf->len += len; + + // Keep the buffer nul-terminated + buf->s[buf->len] = '\0'; + + return mrk_err_ok; +}