1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-31 14:50:15 +03:00

string-util: add strextendf() helper, that allows extending some allocated string via a format string

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, "…", …);
This commit is contained in:
Lennart Poettering 2021-05-05 09:56:46 +02:00 committed by Zbigniew Jędrzejewski-Szmek
parent db1ce3ea1a
commit e9b88a6d4e
4 changed files with 113 additions and 24 deletions

View 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;

View 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);

View 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;

View 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;
}