]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: add strextendf() helper, that allows extending some allocated string...
authorLennart Poettering <lennart@poettering.net>
Wed, 5 May 2021 07:56:46 +0000 (09:56 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 7 May 2021 07:10:59 +0000 (09:10 +0200)
It's not going to be efficient if called in inner loops, but it's oh so
handy, and we have some code that does this:

   asprintf(&p, "%s…", b, …);
   free(b);
   b = TAKE_PTR(p);

which can now be replaced by the quicker and easier to read:

   strextendf(&p, "…", …);

src/basic/string-util.c
src/basic/string-util.h
src/nspawn/nspawn.c
src/test/test-string-util.c

index 058eec54ff932d6e3dc32903b4adcf85a1fc015f..60c877ce71f8412292f35271b1044c9b243c6909 100644 (file)
@@ -803,6 +803,87 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) {
         return p;
 }
 
+int strextendf(char **x, const char *format, ...) {
+        size_t m, a;
+        va_list ap;
+        int l;
+
+        /* Appends a formatted string to the specified string. Don't use this in inner loops, since then
+         * we'll spend a tonload of time in determining the length of the string passed in, over and over
+         * again. */
+
+        assert(x);
+        assert(format);
+
+        /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
+        if (*x) {
+                m = strlen(*x);
+                a = malloc_usable_size(*x);
+                assert(a >= m + 1);
+        } else
+                m = a = 0;
+
+        if (a - m < 17) { /* if there's less than 16 chars space, then enlarge the buffer first */
+                char *n;
+
+                if (_unlikely_(m > SIZE_MAX - 64)) /* overflow check */
+                        return -ENOMEM;
+
+                n = realloc(*x, m + 64);
+                if (!n)
+                        return -ENOMEM;
+
+                *x = n;
+                a = malloc_usable_size(*x);
+        }
+
+        /* Now, let's try to format the string into it */
+        va_start(ap, format);
+        l = vsnprintf(*x + m, a - m, format, ap);
+        va_end(ap);
+
+        assert(l >= 0);
+
+        if ((size_t) l < a - m) {
+                char *n;
+
+                /* Nice! This worked. We are done. But first, let's return the extra space we don't
+                 * need. This should be a cheap operation, since we only lower the allocation size here,
+                 * never increase. */
+                n = realloc(*x, m + (size_t) l + 1);
+                if (n)
+                        *x = n;
+        } else {
+                char *n;
+
+                /* Wasn't enough. Then let's allocate exactly what we need. */
+
+                if (_unlikely_((size_t) l > SIZE_MAX - 1)) /* overflow check #1 */
+                        goto oom;
+                if (_unlikely_(m > SIZE_MAX - ((size_t) l + 1))) /* overflow check #2 */
+                        goto oom;
+
+                a = m + (size_t) l + 1;
+                n = realloc(*x, a);
+                if (!n)
+                        goto oom;
+                *x = n;
+
+                va_start(ap, format);
+                l = vsnprintf(*x + m, a - m, format, ap);
+                va_end(ap);
+
+                assert((size_t) l < a - m);
+        }
+
+        return 0;
+
+oom:
+        /* truncate the bytes added after the first vsnprintf() attempt again */
+        (*x)[m] = 0;
+        return -ENOMEM;
+}
+
 char *strrep(const char *s, unsigned n) {
         char *r, *p;
         size_t l;
index cb2881b64d1bfbe8ed7031f93790dcd236ef3ae3..6364a0a55f5196ce5a36e7c2e77c6c0d0cce77b9 100644 (file)
@@ -152,6 +152,8 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) _s
 #define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL)
 #define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL)
 
+int strextendf(char **x, const char *format, ...) _printf_(2,3);
+
 char *strrep(const char *s, unsigned n);
 
 int split_pair(const char *s, const char *sep, char **l, char **r);
index ed9b31e63ba1219ae5c9d8a7edc72d099c9a0446..41ae043c6e972f36073934d57a7222f37601393d 100644 (file)
@@ -2967,19 +2967,17 @@ static int determine_names(void) {
         if (!arg_machine) {
                 if (arg_directory && path_equal(arg_directory, "/"))
                         arg_machine = gethostname_malloc();
-                else {
-                        if (arg_image) {
-                                char *e;
+                else if (arg_image) {
+                        char *e;
 
-                                arg_machine = strdup(basename(arg_image));
+                        arg_machine = strdup(basename(arg_image));
 
-                                /* Truncate suffix if there is one */
-                                e = endswith(arg_machine, ".raw");
-                                if (e)
-                                        *e = 0;
-                        } else
-                                arg_machine = strdup(basename(arg_directory));
-                }
+                        /* Truncate suffix if there is one */
+                        e = endswith(arg_machine, ".raw");
+                        if (e)
+                                *e = 0;
+                } else
+                        arg_machine = strdup(basename(arg_directory));
                 if (!arg_machine)
                         return log_oom();
 
@@ -2987,20 +2985,11 @@ static int determine_names(void) {
                 if (!hostname_is_valid(arg_machine, 0))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine machine name automatically, please use -M.");
 
-                if (arg_ephemeral) {
-                        char *b;
-
-                        /* Add a random suffix when this is an
-                         * ephemeral machine, so that we can run many
-                         * instances at once without manually having
-                         * to specify -M each time. */
-
-                        if (asprintf(&b, "%s-%016" PRIx64, arg_machine, random_u64()) < 0)
+                /* Add a random suffix when this is an ephemeral machine, so that we can run many
+                 * instances at once without manually having to specify -M each time. */
+                if (arg_ephemeral)
+                        if (strextendf(&arg_machine, "-%016" PRIx64, random_u64()) < 0)
                                 return log_oom();
-
-                        free(arg_machine);
-                        arg_machine = b;
-                }
         }
 
         return 0;
index 15ea24ae4bec2f80de03ee5beb2568cc8f85fdb6..8174b1283707dd781602deedd2a121442e20a91a 100644 (file)
@@ -970,6 +970,22 @@ static void test_strverscmp_improved(void) {
         assert_se(strverscmp_improved("123_aa2-67.89", "123aa+2-67.89") == 0);
 }
 
+static void test_strextendf(void) {
+        _cleanup_free_ char *p = NULL;
+
+        assert_se(strextendf(&p, "<%i>", 77) >= 0);
+        assert_se(streq(p, "<77>"));
+
+        assert_se(strextendf(&p, "<%i>", 99) >= 0);
+        assert_se(streq(p, "<77><99>"));
+
+        assert_se(strextendf(&p, "<%80i>", 88) >= 0);
+        assert_se(streq(p, "<77><99><                                                                              88>"));
+
+        assert_se(strextendf(&p, "<%08x>", 0x1234) >= 0);
+        assert_se(streq(p, "<77><99><                                                                              88><00001234>"));
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -1008,6 +1024,7 @@ int main(int argc, char *argv[]) {
         test_string_contains_word_strv();
         test_string_contains_word();
         test_strverscmp_improved();
+        test_strextendf();
 
         return 0;
 }