summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-05-11 10:38:23 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-05-11 13:14:13 -0400
commit7e26443627926bb3bcc88bd790190d5e4e0eda98 (patch)
tree02844b8dac09348f39fcb599f1033807cb4b66e6
parent59f87eed2b930af2f31fd1d1fb2589f80f426ee0 (diff)
downloadbfs-7e26443627926bb3bcc88bd790190d5e4e0eda98.tar.xz
config: Properly align flex_sizeof()
-rw-r--r--src/config.h31
-rw-r--r--tests/bfstd.c13
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");