diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2023-05-11 10:38:23 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2023-05-11 13:14:13 -0400 |
commit | 7e26443627926bb3bcc88bd790190d5e4e0eda98 (patch) | |
tree | 02844b8dac09348f39fcb599f1033807cb4b66e6 | |
parent | 59f87eed2b930af2f31fd1d1fb2589f80f426ee0 (diff) | |
download | bfs-7e26443627926bb3bcc88bd790190d5e4e0eda98.tar.xz |
config: Properly align flex_sizeof()
-rw-r--r-- | src/config.h | 31 | ||||
-rw-r--r-- | tests/bfstd.c | 13 |
2 files changed, 34 insertions, 10 deletions
diff --git a/src/config.h b/src/config.h index 4408feb..c6dcc1e 100644 --- a/src/config.h +++ b/src/config.h @@ -140,11 +140,12 @@ */ #define countof(array) (sizeof(array) / sizeof(0[array])) -// Lower bound on flex_sizeof() -#define BFS_FLEX_LB(type, member, length) (offsetof(type, member) + sizeof(((type *)NULL)->member[0]) * (length)) - -// Maximum macro for BFS_FLEX_SIZE() -#define BFS_FLEX_MAX(a, b) ((a) > (b) ? (a) : (b)) +/** + * Round up to a multiple of an alignment. + */ +static inline size_t align_ceil(size_t align, size_t size) { + return (size + align - 1) & ~(align - 1); +} /** * Computes the size of a struct containing a flexible array member of the given @@ -154,13 +155,23 @@ * The type of the struct containing the flexible array. * @param member * The name of the flexible array member. - * @param length + * @param count * The length of the flexible array. */ -#define flex_sizeof(type, member, length) \ - (sizeof(type) <= BFS_FLEX_LB(type, member, 0) \ - ? BFS_FLEX_LB(type, member, length) \ - : BFS_FLEX_MAX(sizeof(type), BFS_FLEX_LB(type, member, length))) +#define flex_sizeof(type, member, count) \ + flex_sizeof_impl(alignof(type), sizeof(type), offsetof(type, member), sizeof(((type *)NULL)->member[0]), count) + +static inline size_t flex_sizeof_impl(size_t align, size_t min, size_t offset, size_t size, size_t count) { + size_t ret = align_ceil(align, offset + size * count); + + // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the + // type has more padding than necessary for alignment + if (min > align_ceil(align, offset) && ret < min) { + ret = min; + } + + return ret; +} /** * Initialize a variable, unless sanitizers would detect uninitialized uses. diff --git a/tests/bfstd.c b/tests/bfstd.c index 8c61072..c6a9e9f 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,8 +1,12 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD +#undef NDEBUG #include "../src/bfstd.h" +#include "../src/config.h" +#include <assert.h> #include <stdio.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -30,6 +34,15 @@ static void check_base_dir(const char *path, const char *dir, const char *base) } int main(void) { + // Check flex_sizeof() + struct flexible { + alignas(64) int foo; + int bar[]; + }; + assert(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); + assert(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); + assert(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); + // From man 3p basename check_base_dir("usr", ".", "usr"); check_base_dir("usr/", ".", "usr"); |