From 0a9939816c1d6c57200f99432e38bf1c425a77ea Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 25 Sep 2020 17:21:57 +0200 Subject: [PATCH] lib/buffer: add simple grow-able buffer The goal is to use it in libmount when generate options strings and in libsmartcols to replace libscols_buffer. Signed-off-by: Karel Zak --- include/buffer.h | 28 +++++++++ lib/Makemodule.am | 5 ++ lib/buffer.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 include/buffer.h create mode 100644 lib/buffer.c diff --git a/include/buffer.h b/include/buffer.h new file mode 100644 index 000000000..5bc7037f3 --- /dev/null +++ b/include/buffer.h @@ -0,0 +1,28 @@ +#ifndef UTIL_LINUX_BUFFER +#define UTIL_LINUX_BUFFER + +#include "c.h" + +struct ul_buffer { + char *begin; /* begin of the data */ + char *end; /* current end of data */ + + size_t sz; /* allocated space for data */ + size_t chunksize; +}; + +#define UL_INIT_BUFFER { .begin = NULL } + +void ul_buffer_reset_data(struct ul_buffer *buf); +void ul_buffer_free_data(struct ul_buffer *buf); +int ul_buffer_is_empty(struct ul_buffer *buf); +void ul_buffer_set_chunksize(struct ul_buffer *buf, size_t sz); +void ul_buffer_refer_string(struct ul_buffer *buf, char *str); +int ul_buffer_alloc_data(struct ul_buffer *buf, size_t sz); +int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz); +int ul_buffer_append_string(struct ul_buffer *buf, const char *str); +int ul_buffer_append_ntimes(struct ul_buffer *buf, size_t n, const char *str); +int ul_buffer_set_data(struct ul_buffer *buf, const char *data, size_t sz); +char *ul_buffer_get_data(struct ul_buffer *buf); + +#endif /* UTIL_LINUX_BUFFER */ diff --git a/lib/Makemodule.am b/lib/Makemodule.am index cab44480d..590f2f8c1 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES += libcommon.la libcommon_la_CFLAGS = $(AM_CFLAGS) libcommon_la_SOURCES = \ lib/blkdev.c \ + lib/buffer.c \ lib/canonicalize.c \ lib/crc32.c \ lib/crc32c.c \ @@ -76,6 +77,7 @@ dist_man_MANS += lib/terminal-colors.d.5 check_PROGRAMS += \ test_blkdev \ + test_buffer \ test_canonicalize \ test_colors \ test_fileutils \ @@ -186,3 +188,6 @@ test_pwdutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM test_remove_env_SOURCES = lib/env.c test_remove_env_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM + +test_buffer_SOURCES = lib/buffer.c +test_buffer_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_BUFFER diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 000000000..a25da0cd7 --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,154 @@ + +#include "buffer.h" + +void ul_buffer_reset_data(struct ul_buffer *buf) +{ + if (buf->begin) + buf->begin[0] = '\0'; + buf->end = buf->begin; +} + +void ul_buffer_free_data(struct ul_buffer *buf) +{ + assert(buf); + + free(buf->begin); + buf->begin = NULL; + buf->end = NULL; + buf->sz = 0; +} + +void ul_buffer_set_chunksize(struct ul_buffer *buf, size_t sz) +{ + buf->chunksize = sz; +} + +int ul_buffer_is_empty(struct ul_buffer *buf) +{ + return buf->begin == buf->end; +} + +void ul_buffer_refer_string(struct ul_buffer *buf, char *str) +{ + if (buf->sz) + ul_buffer_free_data(buf); + buf->begin = str; + buf->sz = str ? strlen(str) : 0; + buf->end = buf->begin + buf->sz; +} + +int ul_buffer_alloc_data(struct ul_buffer *buf, size_t sz) +{ + char *tmp; + size_t len = 0; + + assert(buf); + + if (sz <= buf->sz) + return 0; + + if (buf->end && buf->begin) + len = buf->end - buf->begin; + + if (buf->chunksize) + sz = ((sz + buf->chunksize) / buf->chunksize) * buf->chunksize + 1; + + tmp = realloc(buf->begin, sz); + if (!tmp) + return -ENOMEM; + + buf->begin = tmp; + buf->end = buf->begin + len; + buf->sz = sz; + + return 0; +} + +int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz) +{ + size_t maxsz = 0; + + if (!buf) + return -EINVAL; + if (!data || !*data) + return 0; + + if (buf->begin && buf->end) + maxsz = buf->sz - (buf->end - buf->begin); + + if (maxsz <= sz + 1) { + int rc = ul_buffer_alloc_data(buf, buf->sz + sz + 1); + if (rc) + return rc; + } + memcpy(buf->end, data, sz); + buf->end += sz; + *buf->end = '\0'; /* make sure it's terminated */ + return 0; +} + +int ul_buffer_append_string(struct ul_buffer *buf, const char *str) +{ + return ul_buffer_append_data(buf, str, strlen(str)); +} + +int ul_buffer_append_ntimes(struct ul_buffer *buf, size_t n, const char *str) +{ + size_t i; + size_t len = strlen(str); + + for (i = 0; len && i < n; i++) { + int rc = ul_buffer_append_data(buf, str, len); + if (rc) + return rc; + } + return 0; +} + +int ul_buffer_set_data(struct ul_buffer *buf, const char *data, size_t sz) +{ + ul_buffer_reset_data(buf); + return ul_buffer_append_data(buf, data, sz); +} + +char *ul_buffer_get_data(struct ul_buffer *buf) +{ + return buf->begin; +} + +#ifdef TEST_PROGRAM_BUFFER +int main(void) +{ + struct ul_buffer buf = UL_INIT_BUFFER; + char *str; + + ul_buffer_set_chunksize(&buf, 16); + + ul_buffer_append_string(&buf, "AAA"); + ul_buffer_append_data(&buf, "=", 1); + ul_buffer_append_string(&buf, "aaa"); + ul_buffer_append_data(&buf, ",", 1); + ul_buffer_append_string(&buf, "BBB"); + ul_buffer_append_string(&buf, "="); + ul_buffer_append_string(&buf, "bbb"); + + str = ul_buffer_get_data(&buf); + printf("data '%s'\n", str); + + ul_buffer_reset_data(&buf); + ul_buffer_append_string(&buf, "This is really long string to test the buffer function."); + ul_buffer_append_string(&buf, " YES!"); + str = ul_buffer_get_data(&buf); + printf("data '%s'\n", str); + + ul_buffer_free_data(&buf); + str = strdup("foo"); + ul_buffer_refer_string(&buf, str); + ul_buffer_append_data(&buf, ",", 1); + ul_buffer_append_string(&buf, "bar"); + str = ul_buffer_get_data(&buf); + printf("data '%s'\n", str); + + ul_buffer_free_data(&buf); +} +#endif /* TEST_PROGRAM_BUFFER */