From: Lennart Poettering Date: Tue, 18 May 2021 20:30:10 +0000 (+0200) Subject: alloc-util: add MALLOC_ELEMENTSOF() helper X-Git-Tag: v249-rc1~197^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=99480504d47c0a6bbb1ac230cc33df12bbd36c54;p=thirdparty%2Fsystemd.git alloc-util: add MALLOC_ELEMENTSOF() helper This is a wrapper around malloc_usable_size() but is typesafe, and divides by the element size. A test it is also added ensuring what it does it does correcly. --- diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 66bee6cb871..195795285bb 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -175,3 +175,11 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); * case. */ #define MALLOC_SIZEOF_SAFE(x) \ MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) + +/* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items + * that fit into the specified memory block */ +#define MALLOC_ELEMENTSOF(x) \ + (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ + MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \ + VOID_0)) diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c index b4319f9c7da..3b77dd98ca4 100644 --- a/src/test/test-alloc-util.c +++ b/src/test/test-alloc-util.c @@ -150,6 +150,44 @@ static void test_auto_erase_memory(void) { assert_se(p1[i] == p2[i]); } +#define TEST_SIZES(f, n) \ + do { \ + log_debug("requested=%zu vs. malloc_size=%zu vs. gcc_size=%zu", \ + n * sizeof(*f), \ + malloc_usable_size(f), \ + __builtin_object_size(f, 0)); \ + assert_se(MALLOC_ELEMENTSOF(f) >= n); \ + assert_se(MALLOC_SIZEOF_SAFE(f) >= sizeof(*f) * n); \ + assert_se(malloc_usable_size(f) >= sizeof(*f) * n); \ + assert_se(__builtin_object_size(f, 0) >= sizeof(*f) * n); \ + } while(false) + +static void test_malloc_size_safe(void) { + _cleanup_free_ uint32_t *f = NULL; + size_t n = 4711; + + /* Let's check the macros and built-ins work on NULL and return the expected values */ + assert_se(MALLOC_ELEMENTSOF((float*) NULL) == 0); + assert_se(MALLOC_SIZEOF_SAFE((float*) NULL) == 0); + assert_se(malloc_usable_size(NULL) == 0); /* as per man page, this is safe and defined */ + assert_se(__builtin_object_size(NULL, 0) == SIZE_MAX); /* as per docs SIZE_MAX is returned for pointers where the size isn't known */ + + /* Then, let's try these macros once with contant size values, so that __builtin_object_size() + * definitely can work (as long as -O2 is used when compiling) */ + assert_se(f = new(uint32_t, n)); + TEST_SIZES(f, n); + + /* Finally, let's use some dynamically sized allocations, to make sure this doesn't deteriorate */ + for (unsigned i = 0; i < 50; i++) { + _cleanup_free_ uint64_t *g = NULL; + size_t m; + + m = random_u64_range(16*1024); + assert_se(g = new(uint64_t, m)); + TEST_SIZES(g, m); + } +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -159,6 +197,7 @@ int main(int argc, char *argv[]) { test_bool_assign(); test_cleanup_order(); test_auto_erase_memory(); + test_malloc_size_safe(); return 0; }