From: Peter Hutterer Date: Fri, 11 Oct 2024 05:52:04 +0000 (+1000) Subject: util: add a generic stringbuffer with a few helper functions X-Git-Tag: 1.27.0~48 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4fa5ea268ebee187364a67a28968c55ffd8b1c48;p=platform%2Fupstream%2Flibinput.git util: add a generic stringbuffer with a few helper functions Part-of: --- diff --git a/meson.build b/meson.build index aac0ea9b..65da6806 100644 --- a/meson.build +++ b/meson.build @@ -264,6 +264,7 @@ util_headers = [ 'util-matrix.h', 'util-prop-parsers.h', 'util-ratelimit.h', + 'util-stringbuf.h', 'util-strings.h', 'util-time.h', ] diff --git a/src/util-stringbuf.h b/src/util-stringbuf.h new file mode 100644 index 00000000..a11d7f28 --- /dev/null +++ b/src/util-stringbuf.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2024 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "util-macros.h" + +struct stringbuf { + char *data; + size_t len; /* len of log */ + size_t sz; /* allocated size */ +}; + +static inline void +stringbuf_init(struct stringbuf *b) +{ + b->len = 0; + b->sz = 64; + b->data = calloc(1, b->sz); +} + +static inline bool +stringbuf_is_empty(struct stringbuf *b) +{ + return b->len == 0; +} + +static inline void +stringbuf_reset(struct stringbuf *b) +{ + free(b->data); + b->data = NULL; + b->sz = 0; + b->len = 0; + +} + +static inline struct stringbuf * +stringbuf_new(void) +{ + struct stringbuf *b = calloc(1, sizeof(*b)); + stringbuf_init(b); + return b; +} + +static inline void +stringbuf_destroy(struct stringbuf *b) +{ + stringbuf_reset(b); + free(b); +} + +static inline char * +stringbuf_steal_destroy(struct stringbuf *b) +{ + char *str = b->data; + b->data = NULL; + stringbuf_destroy(b); + return str; +} + +static inline char * +stringbuf_steal(struct stringbuf *b) +{ + char *str = b->data; + b->data = NULL; + stringbuf_reset(b); + return str; +} + +/** + * Ensure the stringbuf has space for at least sz bytes + */ +static inline int +stringbuf_ensure_size(struct stringbuf *b, size_t sz) +{ + if (b->sz < sz) { + char *tmp = realloc(b->data, sz); + if (!tmp) + return -errno; + b->data = tmp; + memset(&b->data[b->len], 0, sz - b->len); + b->sz = sz; + } + return 0; +} + +/** + * Ensure the stringbuf has space for at least sz *more* bytes + */ +static inline int +stringbuf_ensure_space(struct stringbuf *b, size_t sz) +{ + return stringbuf_ensure_size(b, b->len + sz); +} + +/** + * Append the the data from the fd to the string buffer. + */ +static inline int +stringbuf_append_from_fd(struct stringbuf *b, int fd, size_t maxlen) +{ + while (1) { + size_t inc = maxlen ? maxlen : 1024; + do { + int r = stringbuf_ensure_space(b, inc); + if (r < 0) + return r; + + r = read(fd, &b->data[b->len], min(b->sz - b->len, inc)); + if (r <= 0) { + if (r < 0 && errno != EAGAIN) + return -errno; + return 0; + } + b->len += r; + } while (maxlen > 0); + } + + return 0; +} + +static inline int +stringbuf_append_string(struct stringbuf *b, const char *msg) +{ + int r = stringbuf_ensure_space(b, strlen(msg) + 1); + if (r < 0) + return r; + + size_t slen = strlen(msg); + + if (!b->data) /* cannot happen, but let's make scan-build happy */ + abort(); + + memcpy(b->data + b->len, msg, slen); + b->len += slen; + + return 0; +} diff --git a/test/test-utils.c b/test/test-utils.c index 73a82208..10751fed 100644 --- a/test/test-utils.c +++ b/test/test-utils.c @@ -27,6 +27,9 @@ #include +#include +#include + #include "util-list.h" #include "util-strings.h" #include "util-time.h" @@ -35,6 +38,7 @@ #include "util-bits.h" #include "util-range.h" #include "util-ratelimit.h" +#include "util-stringbuf.h" #include "util-matrix.h" #include "util-input-event.h" @@ -1721,6 +1725,107 @@ START_TEST(range_test) } END_TEST +START_TEST(stringbuf_test) +{ + struct stringbuf buf; + struct stringbuf *b = &buf; + int rc; + + stringbuf_init(b); + ck_assert_int_eq(b->len, 0u); + + rc = stringbuf_append_string(b, "foo"); + ck_assert_int_ne(rc, -1); + rc = stringbuf_append_string(b, "bar"); + ck_assert_int_ne(rc, -1); + rc = stringbuf_append_string(b, "baz"); + ck_assert_int_ne(rc, -1); + ck_assert_str_eq(b->data, "foobarbaz"); + ck_assert_int_eq(b->len, strlen("foobarbaz")); + + rc = stringbuf_ensure_space(b, 500); + ck_assert_int_ne(rc, -1); + ck_assert_int_ge(b->sz, 500u); + + rc = stringbuf_ensure_size(b, 0); + ck_assert_int_ne(rc, -1); + rc = stringbuf_ensure_size(b, 1024); + ck_assert_int_ne(rc, -1); + ck_assert_int_ge(b->sz, 1024u); + + char *data = stringbuf_steal(b); + ck_assert_str_eq(data, "foobarbaz"); + ck_assert_int_eq(b->sz, 0u); + ck_assert_int_eq(b->len, 0u); + ck_assert_ptr_null(b->data); + free(data); + + const char *str = "1234567890"; + rc = stringbuf_append_string(b, str); + ck_assert_int_ne(rc, -1); + ck_assert_str_eq(b->data, str); + ck_assert_int_eq(b->len, 10u); + stringbuf_reset(b); + + /* intentional double-reset */ + stringbuf_reset(b); + + int pipefd[2]; + rc = pipe2(pipefd, O_CLOEXEC | O_NONBLOCK); + ck_assert_int_ne(rc, -1); + + str = "foo bar baz"; + char *compare = NULL; + for (int i = 0; i < 100; i++) { + rc = write(pipefd[1], str, strlen(str)); + ck_assert_int_ne(rc, -1); + + rc = stringbuf_append_from_fd(b, pipefd[0], 64); + ck_assert_int_ne(rc, -1); + + char *expected; + rc = xasprintf(&expected, "%s%s", compare ? compare : "", str); + ck_assert_int_ne(rc, -1); + ck_assert_str_eq(b->data, expected); + + free(compare); + compare = expected; + } + free(compare); + close(pipefd[0]); + close(pipefd[1]); + stringbuf_reset(b); + + rc = pipe2(pipefd, O_CLOEXEC | O_NONBLOCK); + ck_assert_int_ne(rc, -1); + ck_assert_int_ne(rc, -1); + + const size_t stride = 256; + const size_t maxsize = 4096; + + for (size_t i = 0; i < maxsize; i += stride) { + char buf[stride]; + memset(buf, i/stride, sizeof(buf)); + rc = write(pipefd[1], buf, sizeof(buf)); + ck_assert_int_ne(rc, -1); + } + + stringbuf_append_from_fd(b, pipefd[0], 0); + ck_assert_int_eq(b->len, maxsize); + ck_assert_int_ge(b->sz, maxsize); + + for (size_t i = 0; i < maxsize; i+= stride) { + char buf[stride]; + memset(buf, i/stride, sizeof(buf)); + ck_assert_int_eq(memcmp(buf, b->data + i, sizeof(buf)), 0); + } + + close(pipefd[0]); + close(pipefd[1]); + stringbuf_reset(b); +} +END_TEST + static Suite * litest_utils_suite(void) { @@ -1778,6 +1883,7 @@ litest_utils_suite(void) tcase_add_test(tc, absinfo_normalize_value_test); tcase_add_test(tc, range_test); + tcase_add_test(tc, stringbuf_test); suite_add_tcase(s, tc);