From 90ded13e589b0089167ef25ca3d26be599dfec9b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 19 Jun 2023 12:11:36 -0400 Subject: alloc: New header for memory allocation utilities --- src/alloc.h | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/alloc.h (limited to 'src/alloc.h') diff --git a/src/alloc.h b/src/alloc.h new file mode 100644 index 0000000..899a4ec --- /dev/null +++ b/src/alloc.h @@ -0,0 +1,149 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Memory allocation. + */ + +#ifndef BFS_ALLOC_H +#define BFS_ALLOC_H + +#include "config.h" +#include + +/** Round down to a multiple of an alignment. */ +static inline size_t align_floor(size_t align, size_t size) { + return size & ~(align - 1); +} + +/** Round up to a multiple of an alignment. */ +static inline size_t align_ceil(size_t align, size_t size) { + return align_floor(align, size + align - 1); +} + +/** + * Saturating array size. + * + * @param align + * Array element alignment. + * @param size + * Array element size. + * @param count + * Array element count. + * @return + * size * count, saturating to the maximum aligned value on overflow. + */ +static inline size_t array_size(size_t align, size_t size, size_t count) { + size_t ret = size * count; + return ret / size == count ? ret : ~(align - 1); +} + +/** Saturating array sizeof. */ +#define sizeof_array(type, count) \ + array_size(alignof(type), sizeof(type), count) + +/** Size of a struct/union field. */ +#define sizeof_member(type, member) \ + sizeof(((type *)NULL)->member) + +/** + * Saturating flexible struct size. + * + * @param align + * Struct alignment. + * @param min + * Minimum struct size. + * @param offset + * Flexible array member offset. + * @param size + * Flexible array element size. + * @param count + * Flexible array element count. + * @return + * The size of the struct with count flexible array elements. Saturates + * to the maximum aligned value on overflow. + */ +static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t size, size_t count) { + size_t ret = size * count; + size_t overflow = ret / size != count; + + size_t extra = offset + align - 1; + ret += extra; + overflow |= ret < extra; + ret |= -overflow; + ret = align_floor(align, ret); + + // 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 = ret < min ? min : ret; + } + + return ret; +} + +/** + * Computes the size of a flexible struct. + * + * @param type + * The type of the struct containing the flexible array. + * @param member + * The name of the flexible array member. + * @param count + * The length of the flexible array. + * @return + * The size of the struct with count flexible array elements. Saturates + * to the maximum aligned value on overflow. + */ +#define sizeof_flex(type, member, count) \ + flex_size(alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0]), count) + +/** + * General memory allocator. + * + * @param align + * The required alignment. + * @param size + * The size of the allocation. + * @return + * The allocated memory, or NULL on failure. + */ +void *alloc(size_t align, size_t size); + +/** + * Zero-initialized memory allocator. + * + * @param align + * The required alignment. + * @param size + * The size of the allocation. + * @return + * The allocated memory, or NULL on failure. + */ +void *zalloc(size_t align, size_t size); + +/** Allocate memory for the given type. */ +#define ALLOC(type) \ + (type *)alloc(alignof(type), sizeof(type)) + +/** Allocate zeroed memory for the given type. */ +#define ZALLOC(type) \ + (type *)zalloc(alignof(type), sizeof(type)) + +/** Allocate memory for an array. */ +#define ALLOC_ARRAY(type, count) \ + (type *)alloc(alignof(type), sizeof_array(type, count)); + +/** Allocate zeroed memory for an array. */ +#define ZALLOC_ARRAY(type, count) \ + (type *)zalloc(alignof(type), sizeof_array(type, count)); + +/** Allocate memory for a flexible struct. */ +#define ALLOC_FLEX(type, member, count) \ + (type *)alloc(alignof(type), sizeof_flex(type, member, count)) + +/** Allocate zeroed memory for a flexible struct. */ +#define ZALLOC_FLEX(type, member, count) \ + (type *)zalloc(alignof(type), sizeof_flex(type, member, count)) + +#endif // BFS_ALLOC_H -- cgit v1.2.3 From eb83f2a91a615c5fa3788d41c3ec80b43bf5ed28 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 19 Jun 2023 13:43:46 -0400 Subject: alloc: Implement an arena allocator --- src/alloc.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/alloc.h | 44 ++++++++++++++++++++++++ 2 files changed, 155 insertions(+) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index 0003108..b437975 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -4,6 +4,7 @@ #include "alloc.h" #include "bit.h" #include "diag.h" +#include "sanity.h" #include #include #include @@ -48,3 +49,113 @@ void *zalloc(size_t align, size_t size) { } return ret; } + +/** + * An arena allocator chunk. + */ +union chunk { + /** + * Free chunks are stored in a singly linked list. The pointer to the + * next chunk is represented by an offset from the chunk immediately + * after this one in memory, so that zalloc() correctly initializes a + * linked list of chunks (except for the last one). + */ + uintptr_t next; + + // char object[]; +}; + +/** Decode the next chunk. */ +static union chunk *chunk_next(const struct arena *arena, const union chunk *chunk) { + uintptr_t base = (uintptr_t)chunk + arena->size; + return (union chunk *)(base + chunk->next); +} + +/** Encode the next chunk. */ +static void chunk_set_next(const struct arena *arena, union chunk *chunk, union chunk *next) { + uintptr_t base = (uintptr_t)chunk + arena->size; + chunk->next = (uintptr_t)next - base; +} + +void arena_init(struct arena *arena, size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert((size & (align - 1)) == 0); + + if (align < alignof(union chunk)) { + align = alignof(union chunk); + } + if (size < sizeof(union chunk)) { + size = sizeof(union chunk); + } + bfs_assert((size & (align - 1)) == 0); + + arena->chunks = NULL; + arena->nslabs = 0; + arena->slabs = NULL; + arena->align = align; + arena->size = size; +} + +/** Allocate a new slab. */ +static int slab_alloc(struct arena *arena) { + void **slabs = realloc(arena->slabs, sizeof_array(void *, arena->nslabs + 1)); + if (!slabs) { + return -1; + } + arena->slabs = slabs; + + // Make the initial allocation size ~4K + size_t size = 4096; + if (size < arena->size) { + size = arena->size; + } + // Trim off the excess + size -= size % arena->size; + // Double the size for every slab + size <<= arena->nslabs; + + // Allocate the slab + void *slab = zalloc(arena->align, size); + if (!slab) { + return -1; + } + + // Fix the last chunk->next offset + void *last = (char *)slab + size - arena->size; + chunk_set_next(arena, last, arena->chunks); + + // We can rely on zero-initialized slabs, but others shouldn't + sanitize_uninit(slab, size); + + arena->chunks = arena->slabs[arena->nslabs++] = slab; + return 0; +} + +void *arena_alloc(struct arena *arena) { + if (!arena->chunks && slab_alloc(arena) != 0) { + return NULL; + } + + union chunk *chunk = arena->chunks; + sanitize_alloc(chunk, arena->size); + + sanitize_init(chunk); + arena->chunks = chunk_next(arena, chunk); + sanitize_uninit(chunk, arena->size); + + return chunk; +} + +void arena_free(struct arena *arena, void *ptr) { + union chunk *chunk = ptr; + chunk_set_next(arena, chunk, arena->chunks); + arena->chunks = chunk; + sanitize_free(chunk, arena->size); +} + +void arena_destroy(struct arena *arena) { + for (size_t i = 0; i < arena->nslabs; ++i) { + free(arena->slabs[i]); + } + sanitize_uninit(arena); +} diff --git a/src/alloc.h b/src/alloc.h index 899a4ec..65edb92 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -146,4 +146,48 @@ void *zalloc(size_t align, size_t size); #define ZALLOC_FLEX(type, member, count) \ (type *)zalloc(alignof(type), sizeof_flex(type, member, count)) +/** + * An arena allocator for fixed-size types. + * + * Arena allocators are intentionally not thread safe. + */ +struct arena { + /** The list of free chunks. */ + void *chunks; + /** The number of allocated slabs. */ + size_t nslabs; + /** The array of slabs. */ + void **slabs; + /** Chunk alignment. */ + size_t align; + /** Chunk size. */ + size_t size; +}; + +/** + * Initialize an arena for chunks of the given size and alignment. + */ +void arena_init(struct arena *arena, size_t align, size_t size); + +/** + * Initialize an arena for the given type. + */ +#define ARENA_INIT(arena, type) \ + arena_init((arena), alignof(type), sizeof(type)) + +/** + * Allocate an object out of the arena. + */ +void *arena_alloc(struct arena *arena); + +/** + * Free an object from the arena. + */ +void arena_free(struct arena *arena, void *ptr); + +/** + * Destroy an arena, freeing all allocations. + */ +void arena_destroy(struct arena *arena); + #endif // BFS_ALLOC_H -- cgit v1.2.3 From cbe2c473c9aee71f5f770dc7f41973d2ef4c273c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 19 Jun 2023 16:58:24 -0400 Subject: alloc: Implement an arena for flexible structs --- src/alloc.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/alloc.h | 92 +++++++++++++++++++++++++++++++++++++++++++++ tests/alloc.c | 14 ++++++- 3 files changed, 223 insertions(+), 1 deletion(-) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index b437975..a6910ce 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -157,5 +157,123 @@ void arena_destroy(struct arena *arena) { for (size_t i = 0; i < arena->nslabs; ++i) { free(arena->slabs[i]); } + free(arena->slabs); sanitize_uninit(arena); } + +void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, size_t size) { + varena->align = align; + varena->offset = offset; + varena->size = size; + varena->narenas = 0; + varena->arenas = NULL; + + // The smallest size class is at least as many as fit in the smallest + // aligned allocation size + size_t min_count = (flex_size(align, min, offset, size, 1) - offset + size - 1) / size; + varena->shift = bit_width(min_count - 1); +} + +/** Get the size class for the given array length. */ +static size_t varena_size_class(struct varena *varena, size_t count) { + // Since powers of two are common array lengths, make them the + // (inclusive) upper bound for each size class + return bit_width((count - !!count) >> varena->shift); +} + +/** Get the exact size of a flexible struct. */ +static size_t varena_exact_size(const struct varena *varena, size_t count) { + return flex_size(varena->align, 0, varena->offset, varena->size, count); +} + +/** Get the arena for the given array length. */ +static struct arena *varena_get(struct varena *varena, size_t count) { + size_t i = varena_size_class(varena, count); + + if (i >= varena->narenas) { + size_t narenas = i + 1; + struct arena *arenas = realloc(varena->arenas, sizeof_array(struct arena, narenas)); + if (!arenas) { + return NULL; + } + + for (size_t j = varena->narenas; j < narenas; ++j) { + size_t shift = j + varena->shift; + size_t size = varena_exact_size(varena, (size_t)1 << shift); + arena_init(&arenas[j], varena->align, size); + } + + varena->narenas = narenas; + varena->arenas = arenas; + } + + return &varena->arenas[i]; +} + +void *varena_alloc(struct varena *varena, size_t count) { + struct arena *arena = varena_get(varena, count); + if (!arena) { + return NULL; + } + + void *ret = arena_alloc(arena); + if (!ret) { + return NULL; + } + + // Tell the sanitizers the exact size of the allocated struct + sanitize_free(ret, arena->size); + sanitize_alloc(ret, varena_exact_size(varena, count)); + + return ret; +} + +void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t new_count) { + struct arena *new_arena = varena_get(varena, new_count); + struct arena *old_arena = varena_get(varena, old_count); + if (!new_arena) { + return NULL; + } + + size_t new_exact_size = varena_exact_size(varena, new_count); + size_t old_exact_size = varena_exact_size(varena, old_count); + + if (new_arena == old_arena) { + if (new_count < old_count) { + sanitize_free((char *)ptr + new_exact_size, old_exact_size - new_exact_size); + } else if (new_count > old_count) { + sanitize_alloc((char *)ptr + old_exact_size, new_exact_size - old_exact_size); + } + return ptr; + } + + void *ret = arena_alloc(new_arena); + if (!ret) { + return NULL; + } + + size_t old_size = old_arena->size; + sanitize_alloc((char *)ptr + old_exact_size, old_size - old_exact_size); + + size_t new_size = new_arena->size; + size_t min_size = new_size < old_size ? new_size : old_size; + memcpy(ret, ptr, min_size); + + arena_free(old_arena, ptr); + sanitize_free((char *)ret + new_exact_size, new_size - new_exact_size); + + return ret; +} + +void varena_free(struct varena *varena, void *ptr, size_t count) { + struct arena *arena = varena_get(varena, count); + arena_free(arena, ptr); +} + +void varena_destroy(struct varena *varena) { + for (size_t i = 0; i < varena->narenas; ++i) { + arena_destroy(&varena->arenas[i]); + } + free(varena->arenas); + sanitize_uninit(varena); +} diff --git a/src/alloc.h b/src/alloc.h index 65edb92..c2ea09b 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -190,4 +190,96 @@ void arena_free(struct arena *arena, void *ptr); */ void arena_destroy(struct arena *arena); +/** + * An arena allocator for flexibly-sized types. + */ +struct varena { + /** The alignment of the struct. */ + size_t align; + /** The offset of the flexible array. */ + size_t offset; + /** The size of the flexible array elements. */ + size_t size; + /** Shift amount for the smallest size class. */ + size_t shift; + /** The number of arenas of different sizes. */ + size_t narenas; + /** The array of differently-sized arenas. */ + struct arena *arenas; +}; + +/** + * Initialize a varena for a struct with the given layout. + * + * @param varena + * The varena to initialize. + * @param align + * alignof(type) + * @param min + * sizeof(type) + * @param offset + * offsetof(type, flexible_array) + * @param size + * sizeof(flexible_array[i]) + */ +void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, size_t size); + +/** + * Initialize a varena for the given type and flexible array. + * + * @param varena + * The varena to initialize. + * @param type + * A struct type containing a flexible array. + * @param member + * The name of the flexible array member. + */ +#define VARENA_INIT(varena, type, member) \ + varena_init(varena, alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0])) + +/** + * Arena-allocate a flexible struct. + * + * @param varena + * The varena to allocate from. + * @param count + * The length of the flexible array. + * @return + * The allocated struct, or NULL on failure. + */ +void *varena_alloc(struct varena *varena, size_t count); + +/** + * Resize a flexible struct. + * + * @param varena + * The varena to allocate from. + * @param ptr + * The object to resize. + * @param old_count + * The old array lenth. + * @param new_count + * The new array length. + * @return + * The resized struct, or NULL on failure. + */ +void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t new_count); + +/** + * Free an arena-allocated flexible struct. + * + * @param varena + * The that allocated the object. + * @param ptr + * The object to free. + * @param count + * The length of the flexible array. + */ +void varena_free(struct varena *varena, void *ptr, size_t count); + +/** + * Destroy a varena, freeing all allocations. + */ +void varena_destroy(struct varena *varena); + #endif // BFS_ALLOC_H diff --git a/tests/alloc.c b/tests/alloc.c index 91b1b43..9e6e892 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -8,7 +8,7 @@ int main(void) { // Check sizeof_flex() struct flexible { - alignas(64) int foo; + alignas(64) int foo[8]; int bar[]; }; bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); @@ -20,5 +20,17 @@ int main(void) { // Doesn't happen in typical ABIs bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + // varena tests + struct varena varena; + VARENA_INIT(&varena, struct flexible, bar); + + for (size_t i = 0; i < 256; ++i) { + bfs_verify(varena_alloc(&varena, i)); + struct arena *arena = &varena.arenas[varena.narenas - 1]; + bfs_verify(arena->size >= sizeof_flex(struct flexible, bar, i)); + } + + varena_destroy(&varena); + return EXIT_SUCCESS; } -- cgit v1.2.3 From 5f736eada55f84c7499103faf7c181b80eb7092f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 31 Aug 2023 10:16:15 -0400 Subject: alloc: New [v]arena_clear() functions --- src/alloc.c | 16 +++++++++++++++- src/alloc.h | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index 56d8763..8a4dc3a 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -163,11 +163,19 @@ void arena_free(struct arena *arena, void *ptr) { sanitize_free(chunk, arena->size); } -void arena_destroy(struct arena *arena) { +void arena_clear(struct arena *arena) { for (size_t i = 0; i < arena->nslabs; ++i) { free(arena->slabs[i]); } free(arena->slabs); + + arena->chunks = NULL; + arena->nslabs = 0; + arena->slabs = NULL; +} + +void arena_destroy(struct arena *arena) { + arena_clear(arena); sanitize_uninit(arena); } @@ -280,6 +288,12 @@ void varena_free(struct varena *varena, void *ptr, size_t count) { arena_free(arena, ptr); } +void varena_clear(struct varena *varena) { + for (size_t i = 0; i < varena->narenas; ++i) { + arena_clear(&varena->arenas[i]); + } +} + void varena_destroy(struct varena *varena) { for (size_t i = 0; i < varena->narenas; ++i) { arena_destroy(&varena->arenas[i]); diff --git a/src/alloc.h b/src/alloc.h index c2ea09b..b5dfa68 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -185,6 +185,11 @@ void *arena_alloc(struct arena *arena); */ void arena_free(struct arena *arena, void *ptr); +/** + * Free all allocations from an arena. + */ +void arena_clear(struct arena *arena); + /** * Destroy an arena, freeing all allocations. */ @@ -277,6 +282,11 @@ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t */ void varena_free(struct varena *varena, void *ptr, size_t count); +/** + * Free all allocations from a varena. + */ +void varena_clear(struct varena *varena); + /** * Destroy a varena, freeing all allocations. */ -- cgit v1.2.3 From 1bb2b6ccfc11d4aec61287441379cb6584b8fb5f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 31 Aug 2023 10:42:42 -0400 Subject: alloc: New varena_grow() function --- src/alloc.c | 15 +++++++++++++++ src/alloc.h | 14 ++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index 8a4dc3a..0b978ba 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -283,6 +283,21 @@ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t return ret; } +void *varena_grow(struct varena *varena, void *ptr, size_t *count) { + size_t old_count = *count; + + // Round up to the limit of the current size class. If we're already at + // the limit, go to the next size class. + size_t new_shift = varena_size_class(varena, old_count + 1) + varena->shift; + size_t new_count = (size_t)1 << new_shift; + + ptr = varena_realloc(varena, ptr, old_count, new_count); + if (ptr) { + *count = new_count; + } + return ptr; +} + void varena_free(struct varena *varena, void *ptr, size_t count) { struct arena *arena = varena_get(varena, count); arena_free(arena, ptr); diff --git a/src/alloc.h b/src/alloc.h index b5dfa68..5f0c423 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -270,6 +270,20 @@ void *varena_alloc(struct varena *varena, size_t count); */ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t new_count); +/** + * Grow a flexible struct by an arbitrary amount. + * + * @param varena + * The varena to allocate from. + * @param ptr + * The object to resize. + * @param count + * Pointer to the flexible array length. + * @return + * The resized struct, or NULL on failure. + */ +void *varena_grow(struct varena *varena, void *ptr, size_t *count); + /** * Free an arena-allocated flexible struct. * -- cgit v1.2.3 From 054ef7f719ce6fd2167f1c1b4433feaa438bebfc Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 6 Oct 2023 12:37:01 -0400 Subject: alloc: Test allocation size overflows --- src/alloc.h | 4 ++-- tests/alloc.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.h b/src/alloc.h index 5f0c423..fd3e5f0 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -132,11 +132,11 @@ void *zalloc(size_t align, size_t size); /** Allocate memory for an array. */ #define ALLOC_ARRAY(type, count) \ - (type *)alloc(alignof(type), sizeof_array(type, count)); + (type *)alloc(alignof(type), sizeof_array(type, count)) /** Allocate zeroed memory for an array. */ #define ZALLOC_ARRAY(type, count) \ - (type *)zalloc(alignof(type), sizeof_array(type, count)); + (type *)zalloc(alignof(type), sizeof_array(type, count)) /** Allocate memory for a flexible struct. */ #define ALLOC_FLEX(type, member, count) \ diff --git a/tests/alloc.c b/tests/alloc.c index 9e6e892..382131f 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "../src/alloc.h" #include "../src/diag.h" +#include #include int main(void) { @@ -13,13 +14,20 @@ int main(void) { }; bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); bfs_verify(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); - bfs_verify(sizeof_flex(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) - == align_floor(alignof(struct flexible), SIZE_MAX)); + + size_t too_many = SIZE_MAX / sizeof(int) + 1; + bfs_verify(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) // Doesn't happen in typical ABIs bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + // Make sure we detect allocation size overflows + bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + bfs_verify(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + // varena tests struct varena varena; VARENA_INIT(&varena, struct flexible, bar); -- cgit v1.2.3 From e25261a90222de75781726a93ab809c660208afd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 9 Nov 2023 15:16:04 -0500 Subject: config: Add (de)allocator attributes --- src/alloc.h | 40 +++++++++++++++++++++++----------------- src/config.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dstring.h | 21 +++++++++++++-------- tests/alloc.c | 4 ++++ 4 files changed, 89 insertions(+), 25 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.h b/src/alloc.h index fd3e5f0..34f6949 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -9,7 +9,7 @@ #define BFS_ALLOC_H #include "config.h" -#include +#include /** Round down to a multiple of an alignment. */ static inline size_t align_floor(size_t align, size_t size) { @@ -108,6 +108,8 @@ static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t s * @return * The allocated memory, or NULL on failure. */ +attr_malloc(free, 1) +attr_aligned_alloc(1, 2) void *alloc(size_t align, size_t size); /** @@ -120,6 +122,8 @@ void *alloc(size_t align, size_t size); * @return * The allocated memory, or NULL on failure. */ +attr_malloc(free, 1) +attr_aligned_alloc(1, 2) void *zalloc(size_t align, size_t size); /** Allocate memory for the given type. */ @@ -176,14 +180,15 @@ void arena_init(struct arena *arena, size_t align, size_t size); arena_init((arena), alignof(type), sizeof(type)) /** - * Allocate an object out of the arena. + * Free an object from the arena. */ -void *arena_alloc(struct arena *arena); +void arena_free(struct arena *arena, void *ptr); /** - * Free an object from the arena. + * Allocate an object out of the arena. */ -void arena_free(struct arena *arena, void *ptr); +attr_malloc(arena_free, 2) +void *arena_alloc(struct arena *arena); /** * Free all allocations from an arena. @@ -242,6 +247,18 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, #define VARENA_INIT(varena, type, member) \ varena_init(varena, alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0])) +/** + * Free an arena-allocated flexible struct. + * + * @param varena + * The that allocated the object. + * @param ptr + * The object to free. + * @param count + * The length of the flexible array. + */ +void varena_free(struct varena *varena, void *ptr, size_t count); + /** * Arena-allocate a flexible struct. * @@ -252,6 +269,7 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, * @return * The allocated struct, or NULL on failure. */ +attr_malloc(varena_free, 2) void *varena_alloc(struct varena *varena, size_t count); /** @@ -284,18 +302,6 @@ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t */ void *varena_grow(struct varena *varena, void *ptr, size_t *count); -/** - * Free an arena-allocated flexible struct. - * - * @param varena - * The that allocated the object. - * @param ptr - * The object to free. - * @param count - * The length of the flexible array. - */ -void varena_free(struct varena *varena, void *ptr, size_t count); - /** * Free all allocations from a varena. */ diff --git a/src/config.h b/src/config.h index b95abaa..db62ef8 100644 --- a/src/config.h +++ b/src/config.h @@ -184,6 +184,17 @@ # define fallthru ((void)0) #endif +/** + * Warn if a value is unused. + */ +#if __has_c_attribute(nodiscard) +# define attr_nodiscard [[nodiscard]] +#elif __has_attribute(nodiscard) +# define attr_nodiscard __attribute__((nodiscard)) +#else +# define attr_nodiscard +#endif + /** * Hint to avoid inlining a function. */ @@ -211,6 +222,44 @@ # define attr_format(fmt, args) #endif +/** + * Annotates allocator-like functions. + */ +#if __has_attribute(malloc) +# if __clang__ +# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) +# else +# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) +# endif +#else +# define attr_malloc(...) attr_nodiscard +#endif + +/** + * Specifies that a function returns allocations with a given alignment. + */ +#if __has_attribute(alloc_align) +# define attr_alloc_align(param) __attribute__((alloc_align(param))) +#else +# define attr_alloc_align(param) +#endif + +/** + * Specifies that a function returns allocations with a given size. + */ +#if __has_attribute(alloc_size) +# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else +# define attr_alloc_size(...) +#endif + +/** + * Shorthand for attr_alloc_align() and attr_alloc_size(). + */ +#define attr_aligned_alloc(align, ...) \ + attr_alloc_align(align) \ + attr_alloc_size(__VA_ARGS__) + /** * Check if function multiversioning via GNU indirect functions (ifunc) is supported. */ diff --git a/src/dstring.h b/src/dstring.h index fd98df8..07b4ee9 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -27,12 +27,21 @@ typedef __attribute__((aligned(alignof(size_t)))) char dchar; typedef char dchar; #endif +/** + * Free a dynamic string. + * + * @param dstr + * The string to free. + */ +void dstrfree(dchar *dstr); + /** * Allocate a dynamic string. * * @param capacity * The initial capacity of the string. */ +attr_malloc(dstrfree, 1) dchar *dstralloc(size_t capacity); /** @@ -41,6 +50,7 @@ dchar *dstralloc(size_t capacity); * @param str * The NUL-terminated string to copy. */ +attr_malloc(dstrfree, 1) dchar *dstrdup(const char *str); /** @@ -51,6 +61,7 @@ dchar *dstrdup(const char *str); * @param n * The maximum number of characters to copy from str. */ +attr_malloc(dstrfree, 1) dchar *dstrndup(const char *str, size_t n); /** @@ -59,6 +70,7 @@ dchar *dstrndup(const char *str, size_t n); * @param dstr * The dynamic string to copy. */ +attr_malloc(dstrfree, 1) dchar *dstrddup(const dchar *dstr); /** @@ -69,6 +81,7 @@ dchar *dstrddup(const dchar *dstr); * @param len * The length of the string, which may include internal NUL bytes. */ +attr_malloc(dstrfree, 1) dchar *dstrxdup(const char *str, size_t len); /** @@ -306,12 +319,4 @@ int dstrescat(dchar **dest, const char *str, enum wesc_flags flags); */ int dstrnescat(dchar **dest, const char *str, size_t n, enum wesc_flags flags); -/** - * Free a dynamic string. - * - * @param dstr - * The string to free. - */ -void dstrfree(dchar *dstr); - #endif // BFS_DSTRING_H diff --git a/tests/alloc.c b/tests/alloc.c index 2334241..37b70bf 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -24,6 +24,10 @@ int main(void) { bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); // Make sure we detect allocation size overflows +#if __GNUC__ && !__clang__ +# pragma GCC diagnostic ignored "-Walloc-size-larger-than=" +#endif + bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); -- cgit v1.2.3 From 3fe1e7ca64bcd3724b00cf399ed9ae6f60e0a008 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 22 Nov 2023 16:52:33 -0500 Subject: alloc: New is_aligned() helper --- src/alloc.c | 10 +++++----- src/alloc.h | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index ff3ec6d..97f90bc 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -14,7 +14,7 @@ static void *xmemalign(size_t align, size_t size) { bfs_assert(has_single_bit(align)); bfs_assert(align >= sizeof(void *)); - bfs_assert((size & (align - 1)) == 0); + bfs_assert(is_aligned(align, size)); #if __APPLE__ void *ptr = NULL; @@ -27,7 +27,7 @@ static void *xmemalign(size_t align, size_t size) { void *alloc(size_t align, size_t size) { bfs_assert(has_single_bit(align)); - bfs_assert((size & (align - 1)) == 0); + bfs_assert(is_aligned(align, size)); if (size >> (SIZE_WIDTH - 1)) { errno = EOVERFLOW; @@ -43,7 +43,7 @@ void *alloc(size_t align, size_t size) { void *zalloc(size_t align, size_t size) { bfs_assert(has_single_bit(align)); - bfs_assert((size & (align - 1)) == 0); + bfs_assert(is_aligned(align, size)); if (size >> (SIZE_WIDTH - 1)) { errno = EOVERFLOW; @@ -90,7 +90,7 @@ static void chunk_set_next(const struct arena *arena, union chunk *chunk, union void arena_init(struct arena *arena, size_t align, size_t size) { bfs_assert(has_single_bit(align)); - bfs_assert((size & (align - 1)) == 0); + bfs_assert(is_aligned(align, size)); if (align < alignof(union chunk)) { align = alignof(union chunk); @@ -98,7 +98,7 @@ void arena_init(struct arena *arena, size_t align, size_t size) { if (size < sizeof(union chunk)) { size = sizeof(union chunk); } - bfs_assert((size & (align - 1)) == 0); + bfs_assert(is_aligned(align, size)); arena->chunks = NULL; arena->nslabs = 0; diff --git a/src/alloc.h b/src/alloc.h index 34f6949..15e4983 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -11,6 +11,11 @@ #include "config.h" #include +/** Check if a size is properly aligned. */ +static inline bool is_aligned(size_t align, size_t size) { + return (size & (align - 1)) == 0; +} + /** Round down to a multiple of an alignment. */ static inline size_t align_floor(size_t align, size_t size) { return size & ~(align - 1); -- cgit v1.2.3 From ae18c20d5a585ae4bc1e9ee6859230fee7f73ed8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 Nov 2023 13:08:04 -0500 Subject: alloc: New helpers for aligned reallocation There is no aligned_realloc(), so the new xrealloc() function emulates it by manually reallocating and copying for over-aligned types. The new REALLOC_ARRAY() and REALLOC_FLEX() macros wrap xrealloc(). --- src/alloc.c | 42 ++++++++++++++++++++++++++++++++++++++---- src/alloc.h | 24 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index 8c88813..e83b273 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -69,6 +69,38 @@ void *zalloc(size_t align, size_t size) { return ret; } +void *xrealloc(void *ptr, size_t align, size_t old_size, size_t new_size) { + bfs_assert(has_single_bit(align)); + bfs_assert(is_aligned(align, old_size)); + bfs_assert(is_aligned(align, new_size)); + + if (new_size == 0) { + free(ptr); + return NULL; + } else if (new_size > ALLOC_MAX) { + errno = EOVERFLOW; + return NULL; + } + + if (align <= alignof(max_align_t)) { + return realloc(ptr, new_size); + } + + // There is no aligned_realloc(), so reallocate and copy manually + void *ret = xmemalign(align, new_size); + if (!ret) { + return NULL; + } + + size_t min_size = old_size < new_size ? old_size : new_size; + if (min_size) { + memcpy(ret, ptr, min_size); + } + + free(ptr); + return ret; +} + /** * An arena allocator chunk. */ @@ -118,7 +150,8 @@ void arena_init(struct arena *arena, size_t align, size_t size) { /** Allocate a new slab. */ attr_cold static int slab_alloc(struct arena *arena) { - void **slabs = realloc(arena->slabs, sizeof_array(void *, arena->nslabs + 1)); + size_t nslabs = arena->nslabs; + void **slabs = REALLOC_ARRAY(void *, arena->slabs, nslabs, nslabs + 1); if (!slabs) { return -1; } @@ -132,7 +165,7 @@ static int slab_alloc(struct arena *arena) { // Trim off the excess size -= size % arena->size; // Double the size for every slab - size <<= arena->nslabs; + size <<= nslabs; // Allocate the slab void *slab = zalloc(arena->align, size); @@ -147,7 +180,8 @@ static int slab_alloc(struct arena *arena) { // We can rely on zero-initialized slabs, but others shouldn't sanitize_uninit(slab, size); - arena->chunks = arena->slabs[arena->nslabs++] = slab; + arena->chunks = arena->slabs[nslabs] = slab; + ++arena->nslabs; return 0; } @@ -220,7 +254,7 @@ static struct arena *varena_get(struct varena *varena, size_t count) { if (i >= varena->narenas) { size_t narenas = i + 1; - struct arena *arenas = realloc(varena->arenas, sizeof_array(struct arena, narenas)); + struct arena *arenas = REALLOC_ARRAY(struct arena, varena->arenas, varena->narenas, narenas); if (!arenas) { return NULL; } diff --git a/src/alloc.h b/src/alloc.h index 15e4983..a6dee99 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -155,6 +155,30 @@ void *zalloc(size_t align, size_t size); #define ZALLOC_FLEX(type, member, count) \ (type *)zalloc(alignof(type), sizeof_flex(type, member, count)) +/** + * Alignment-aware realloc(). + * + * @param ptr + * The pointer to reallocate. + * @param align + * The required alignment. + * @param old_size + * The previous allocation size. + * @param new_size + * The new allocation size. + * @return + * The reallocated memory, or NULL on failure. + */ +void *xrealloc(void *ptr, size_t align, size_t old_size, size_t new_size); + +/** Reallocate memory for an array. */ +#define REALLOC_ARRAY(type, ptr, old_count, new_count) \ + (type *)xrealloc((ptr), alignof(type), sizeof_array(type, old_count), sizeof_array(type, new_count)) + +/** Reallocate memory for a flexible struct. */ +#define REALLOC_FLEX(type, member, ptr, old_count, new_count) \ + (type *)xrealloc((ptr), alignof(type), sizeof_flex(type, member, old_count), sizeof_flex(type, member, new_count)) + /** * An arena allocator for fixed-size types. * -- cgit v1.2.3 From 9032d107ab759450c21fea8f82865ff48c743132 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 Nov 2023 13:31:33 -0500 Subject: alloc: New helpers for growing dynamic arrays --- src/alloc.c | 66 ++++++++++++++++++++++++++++++++++++++++--------------------- src/alloc.h | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 22 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index e83b273..467f0f0 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -101,6 +101,34 @@ void *xrealloc(void *ptr, size_t align, size_t old_size, size_t new_size) { return ret; } +void *reserve(void *ptr, size_t align, size_t size, size_t count) { + // No need to overflow-check the current size + size_t old_size = size * count; + + // Capacity is doubled every power of two, from 0→1, 1→2, 2→4, etc. + // If we stayed within the same size class, re-use ptr. + if (count & (count - 1)) { + // Tell sanitizers about the new array element + sanitize_alloc((char *)ptr + old_size, size); + errno = 0; + return ptr; + } + + // No need to overflow-check; xrealloc() will fail before we overflow + size_t new_size = count ? 2 * old_size : size; + void *ret = xrealloc(ptr, align, old_size, new_size); + if (!ret) { + // errno is used to communicate success/failure to the RESERVE() macro + bfs_assert(errno != 0); + return ptr; + } + + // Pretend we only allocated one more element + sanitize_free((char *)ret + old_size + size, new_size - old_size - size); + errno = 0; + return ret; +} + /** * An arena allocator chunk. */ @@ -150,13 +178,6 @@ void arena_init(struct arena *arena, size_t align, size_t size) { /** Allocate a new slab. */ attr_cold static int slab_alloc(struct arena *arena) { - size_t nslabs = arena->nslabs; - void **slabs = REALLOC_ARRAY(void *, arena->slabs, nslabs, nslabs + 1); - if (!slabs) { - return -1; - } - arena->slabs = slabs; - // Make the initial allocation size ~4K size_t size = 4096; if (size < arena->size) { @@ -165,7 +186,7 @@ static int slab_alloc(struct arena *arena) { // Trim off the excess size -= size % arena->size; // Double the size for every slab - size <<= nslabs; + size <<= arena->nslabs; // Allocate the slab void *slab = zalloc(arena->align, size); @@ -173,6 +194,13 @@ static int slab_alloc(struct arena *arena) { return -1; } + // Grow the slab array + void **pslab = RESERVE(void *, &arena->slabs, &arena->nslabs); + if (!pslab) { + free(slab); + return -1; + } + // Fix the last chunk->next offset void *last = (char *)slab + size - arena->size; chunk_set_next(arena, last, arena->chunks); @@ -180,8 +208,7 @@ static int slab_alloc(struct arena *arena) { // We can rely on zero-initialized slabs, but others shouldn't sanitize_uninit(slab, size); - arena->chunks = arena->slabs[nslabs] = slab; - ++arena->nslabs; + arena->chunks = *pslab = slab; return 0; } @@ -252,21 +279,16 @@ static size_t varena_exact_size(const struct varena *varena, size_t count) { static struct arena *varena_get(struct varena *varena, size_t count) { size_t i = varena_size_class(varena, count); - if (i >= varena->narenas) { - size_t narenas = i + 1; - struct arena *arenas = REALLOC_ARRAY(struct arena, varena->arenas, varena->narenas, narenas); - if (!arenas) { + while (i >= varena->narenas) { + size_t j = varena->narenas; + struct arena *arena = RESERVE(struct arena, &varena->arenas, &varena->narenas); + if (!arena) { return NULL; } - for (size_t j = varena->narenas; j < narenas; ++j) { - size_t shift = j + varena->shift; - size_t size = varena_exact_size(varena, (size_t)1 << shift); - arena_init(&arenas[j], varena->align, size); - } - - varena->narenas = narenas; - varena->arenas = arenas; + size_t shift = j + varena->shift; + size_t size = varena_exact_size(varena, (size_t)1 << shift); + arena_init(arena, varena->align, size); } return &varena->arenas[i]; diff --git a/src/alloc.h b/src/alloc.h index a6dee99..7132470 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -9,6 +9,7 @@ #define BFS_ALLOC_H #include "config.h" +#include #include /** Check if a size is properly aligned. */ @@ -179,6 +180,41 @@ void *xrealloc(void *ptr, size_t align, size_t old_size, size_t new_size); #define REALLOC_FLEX(type, member, ptr, old_count, new_count) \ (type *)xrealloc((ptr), alignof(type), sizeof_flex(type, member, old_count), sizeof_flex(type, member, new_count)) +/** + * Reserve space for one more element in a dynamic array. + * + * @param ptr + * The pointer to reallocate. + * @param align + * The required alignment. + * @param count + * The current size of the array. + * @return + * The reallocated memory, on both success *and* failure. On success, + * errno will be set to zero, and the returned pointer will have room + * for (count + 1) elements. On failure, errno will be non-zero, and + * ptr will returned unchanged. + */ +void *reserve(void *ptr, size_t align, size_t size, size_t count); + +/** + * Convenience macro to grow a dynamic array. + * + * @param type + * The array element type. + * @param type **ptr + * A pointer to the array. + * @param size_t *count + * A pointer to the array's size. + * @return + * On success, a pointer to the newly reserved array element, i.e. + * `*ptr + *count++`. On failure, NULL is returned, and both *ptr and + * *count remain unchanged. + */ +#define RESERVE(type, ptr, count) \ + ((*ptr) = reserve((*ptr), alignof(type), sizeof(type), (*count)), \ + errno ? NULL : (*ptr) + (*count)++) + /** * An arena allocator for fixed-size types. * -- cgit v1.2.3 From 5b38f658ee42bef05cecb6cadec65b25d9e94993 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 16 Nov 2023 12:25:09 -0500 Subject: config: New variadic attr(...) macro --- src/alloc.c | 2 +- src/alloc.h | 10 ++++---- src/bar.c | 2 +- src/color.c | 4 ++-- src/color.h | 4 ++-- src/config.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/diag.h | 37 ++++++++++++------------------ src/dstring.h | 18 +++++++-------- src/eval.c | 2 +- src/exec.c | 2 +- src/fsade.c | 6 ++--- src/ioq.c | 4 ++-- src/mtab.c | 2 +- src/opt.c | 4 ++-- src/parse.c | 14 +++++------ src/trie.c | 2 +- src/xspawn.c | 2 +- 17 files changed, 127 insertions(+), 62 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index 467f0f0..b65d0c5 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -176,7 +176,7 @@ void arena_init(struct arena *arena, size_t align, size_t size) { } /** Allocate a new slab. */ -attr_cold +attr(cold) static int slab_alloc(struct arena *arena) { // Make the initial allocation size ~4K size_t size = 4096; diff --git a/src/alloc.h b/src/alloc.h index 7132470..f21ddb9 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -114,8 +114,7 @@ static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t s * @return * The allocated memory, or NULL on failure. */ -attr_malloc(free, 1) -attr_aligned_alloc(1, 2) +attr(malloc(free, 1), aligned_alloc(1, 2)) void *alloc(size_t align, size_t size); /** @@ -128,8 +127,7 @@ void *alloc(size_t align, size_t size); * @return * The allocated memory, or NULL on failure. */ -attr_malloc(free, 1) -attr_aligned_alloc(1, 2) +attr(malloc(free, 1), aligned_alloc(1, 2)) void *zalloc(size_t align, size_t size); /** Allocate memory for the given type. */ @@ -252,7 +250,7 @@ void arena_free(struct arena *arena, void *ptr); /** * Allocate an object out of the arena. */ -attr_malloc(arena_free, 2) +attr(malloc(arena_free, 2)) void *arena_alloc(struct arena *arena); /** @@ -334,7 +332,7 @@ void varena_free(struct varena *varena, void *ptr, size_t count); * @return * The allocated struct, or NULL on failure. */ -attr_malloc(varena_free, 2) +attr(malloc(varena_free, 2)) void *varena_alloc(struct varena *varena, size_t count); /** diff --git a/src/bar.c b/src/bar.c index 2e50dbe..babadc3 100644 --- a/src/bar.c +++ b/src/bar.c @@ -127,7 +127,7 @@ static void reset_before_death_by(int sig) { } /** printf() to the status bar with a single write(). */ -attr_format(2, 3) +attr(format(2, 3)) static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { va_list args; va_start(args, format); diff --git a/src/color.c b/src/color.c index 05ba1a3..4c2c8ca 100644 --- a/src/color.c +++ b/src/color.c @@ -1105,7 +1105,7 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { } /** Format some colored output to the buffer. */ -attr_format(2, 3) +attr(format(2, 3)) static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ @@ -1177,7 +1177,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { return 0; } -attr_format(2, 0) +attr(format(2, 0)) static int cvbuff(CFILE *cfile, const char *format, va_list args) { const struct colors *colors = cfile->colors; int error = errno; diff --git a/src/color.h b/src/color.h index 8a81573..81f0e2a 100644 --- a/src/color.h +++ b/src/color.h @@ -100,13 +100,13 @@ int cfclose(CFILE *cfile); * @return * 0 on success, -1 on failure. */ -attr_format(2, 3) +attr(format(2, 3)) int cfprintf(CFILE *cfile, const char *format, ...); /** * cfprintf() variant that takes a va_list. */ -attr_format(2, 0) +attr(format(2, 0)) int cvfprintf(CFILE *cfile, const char *format, va_list args); #endif // BFS_COLOR_H diff --git a/src/config.h b/src/config.h index 9f95674..aa03552 100644 --- a/src/config.h +++ b/src/config.h @@ -301,4 +301,78 @@ typedef long double max_align_t; # define attr_target_clones(...) #endif +/** + * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to + * + * attr_a + * attr_b(c) + * attr_d + */ +#define attr(...) \ + attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none) + +/** + * attr() helper. For exposition, pretend we support only 2 args, instead of 9. + * There are a few cases: + * + * attr() + * => attr__(attr_, none, none) + * => attr_ => + * attr_none => + * attr_too_many_none() => + * + * attr(a) + * => attr__(attr_a, none, none) + * => attr_a => __attribute__((a)) + * attr_none => + * attr_too_many_none() => + * + * attr(a, b(c)) + * => attr__(attr_a, b(c), none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_none(none) => + * + * attr(a, b(c), d) + * => attr__(attr_a, b(c), d, none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_d(none, none) => error + * + * Some attribute names are the same as standard library functions, e.g. printf. + * Standard libraries are permitted to define these functions as macros, like + * + * #define printf(...) __builtin_printf(__VA_ARGS__) + * + * The token paste in + * + * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) + * + * is necessary to prevent macro expansion before evaluating attr__(). + * Otherwise, we could get + * + * attr(printf(1, 2)) + * => attr__(__builtin_printf(1, 2), none, none) + * => attr____builtin_printf(1, 2) + * => error + */ +#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ + a1 \ + attr_##a2 \ + attr_##a3 \ + attr_##a4 \ + attr_##a5 \ + attr_##a6 \ + attr_##a7 \ + attr_##a8 \ + attr_##a9 \ + attr_too_many_##none(__VA_ARGS__) + +// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) +#define attr_none +// Ignore `attr_` from expanding 0-argument attr() +#define attr_ +// Only trigger an error on more than 9 arguments +#define attr_too_many_none(...) + #endif // BFS_CONFIG_H diff --git a/src/diag.h b/src/diag.h index 981419e..aa6a44f 100644 --- a/src/diag.h +++ b/src/diag.h @@ -44,8 +44,7 @@ struct bfs_loc { /** * Print a message to standard error and abort. */ -attr_cold -attr_format(2, 3) +attr(cold, format(2, 3)) noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); /** @@ -117,14 +116,13 @@ const char *debug_flag_name(enum debug_flags flag); /** * Like perror(), but decorated like bfs_error(). */ -attr_cold +attr(cold) void bfs_perror(const struct bfs_ctx *ctx, const char *str); /** * Shorthand for printing error messages. */ -attr_cold -attr_format(2, 3) +attr(cold, format(2, 3)) void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -132,8 +130,7 @@ void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a warning was printed. */ -attr_cold -attr_format(2, 3) +attr(cold, format(2, 3)) bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -141,71 +138,67 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a debug message was printed. */ -attr_cold -attr_format(3, 4) +attr(cold, format(3, 4)) bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...); /** * bfs_error() variant that takes a va_list. */ -attr_cold -attr_format(2, 0) +attr(cold, format(2, 0)) void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_warning() variant that takes a va_list. */ -attr_cold -attr_format(2, 0) +attr(cold, format(2, 0)) bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_debug() variant that takes a va_list. */ -attr_cold -attr_format(3, 0) +attr(cold, format(3, 0)) bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args); /** * Print the error message prefix. */ -attr_cold +attr(cold) void bfs_error_prefix(const struct bfs_ctx *ctx); /** * Print the warning message prefix. */ -attr_cold +attr(cold) bool bfs_warning_prefix(const struct bfs_ctx *ctx); /** * Print the debug message prefix. */ -attr_cold +attr(cold) bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag); /** * Highlight parts of the command line in an error message. */ -attr_cold +attr(cold) void bfs_argv_error(const struct bfs_ctx *ctx, const bool args[]); /** * Highlight parts of an expression in an error message. */ -attr_cold +attr(cold) void bfs_expr_error(const struct bfs_ctx *ctx, const struct bfs_expr *expr); /** * Highlight parts of the command line in a warning message. */ -attr_cold +attr(cold) bool bfs_argv_warning(const struct bfs_ctx *ctx, const bool args[]); /** * Highlight parts of an expression in a warning message. */ -attr_cold +attr(cold) bool bfs_expr_warning(const struct bfs_ctx *ctx, const struct bfs_expr *expr); #endif // BFS_DIAG_H diff --git a/src/dstring.h b/src/dstring.h index 2a94438..1be1185 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -41,7 +41,7 @@ void dstrfree(dchar *dstr); * @param cap * The initial capacity of the string. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstralloc(size_t cap); /** @@ -50,7 +50,7 @@ dchar *dstralloc(size_t cap); * @param str * The NUL-terminated string to copy. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrdup(const char *str); /** @@ -61,7 +61,7 @@ dchar *dstrdup(const char *str); * @param n * The maximum number of characters to copy from str. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrndup(const char *str, size_t n); /** @@ -70,7 +70,7 @@ dchar *dstrndup(const char *str, size_t n); * @param dstr * The dynamic string to copy. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrddup(const dchar *dstr); /** @@ -81,7 +81,7 @@ dchar *dstrddup(const dchar *dstr); * @param len * The length of the string, which may include internal NUL bytes. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrxdup(const char *str, size_t len); /** @@ -243,7 +243,7 @@ int dstrxcpy(dchar **dest, const char *str, size_t len); * @return * The created string, or NULL on failure. */ -attr_format(1, 2) +attr(format(1, 2)) char *dstrprintf(const char *format, ...); /** @@ -256,7 +256,7 @@ char *dstrprintf(const char *format, ...); * @return * The created string, or NULL on failure. */ -attr_format(1, 0) +attr(format(1, 0)) char *dstrvprintf(const char *format, va_list args); /** @@ -271,7 +271,7 @@ char *dstrvprintf(const char *format, va_list args); * @return * 0 on success, -1 on failure. */ -attr_format(2, 3) +attr(format(2, 3)) int dstrcatf(dchar **str, const char *format, ...); /** @@ -286,7 +286,7 @@ int dstrcatf(dchar **str, const char *format, ...); * @return * 0 on success, -1 on failure. */ -attr_format(2, 0) +attr(format(2, 0)) int dstrvcatf(dchar **str, const char *format, va_list args); /** diff --git a/src/eval.c b/src/eval.c index a990fd4..859ad7e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -57,7 +57,7 @@ struct bfs_eval { /** * Print an error message. */ -attr_format(2, 3) +attr(format(2, 3)) static void eval_error(struct bfs_eval *state, const char *format, ...) { // By POSIX, any errors should be accompanied by a non-zero exit status *state->ret = EXIT_FAILURE; diff --git a/src/exec.c b/src/exec.c index 90b3598..ba2fec8 100644 --- a/src/exec.c +++ b/src/exec.c @@ -22,7 +22,7 @@ #include /** Print some debugging info. */ -attr_format(2, 3) +attr(format(2, 3)) static void bfs_exec_debug(const struct bfs_exec *execbuf, const char *format, ...) { const struct bfs_ctx *ctx = execbuf->ctx; diff --git a/src/fsade.c b/src/fsade.c index 4d22d99..a48eeb0 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -32,7 +32,7 @@ * Many of the APIs used here don't have *at() variants, but we can try to * emulate something similar if /proc/self/fd is available. */ -attr_maybe_unused +attr(maybe_unused) static const char *fake_at(const struct BFTW *ftwbuf) { static atomic int proc_works = -1; @@ -66,7 +66,7 @@ fail: return ftwbuf->path; } -attr_maybe_unused +attr(maybe_unused) static void free_fake_at(const struct BFTW *ftwbuf, const char *path) { if (path != ftwbuf->path) { dstrfree((dchar *)path); @@ -76,7 +76,7 @@ static void free_fake_at(const struct BFTW *ftwbuf, const char *path) { /** * Check if an error was caused by the absence of support or data for a feature. */ -attr_maybe_unused +attr(maybe_unused) static bool is_absence_error(int error) { // If the OS doesn't support the feature, it's obviously not enabled for // any files diff --git a/src/ioq.c b/src/ioq.c index bd8d111..2739338 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -273,7 +273,7 @@ static struct ioq_monitor *ioq_slot_monitor(struct ioqq *ioqq, ioq_slot *slot) { } /** Atomically wait for a slot to change. */ -attr_noinline +attr(noinline) static uintptr_t ioq_slot_wait(struct ioqq *ioqq, ioq_slot *slot, uintptr_t value) { struct ioq_monitor *monitor = ioq_slot_monitor(ioqq, slot); mutex_lock(&monitor->mutex); @@ -303,7 +303,7 @@ done: } /** Wake up any threads waiting on a slot. */ -attr_noinline +attr(noinline) static void ioq_slot_wake(struct ioqq *ioqq, ioq_slot *slot) { struct ioq_monitor *monitor = ioq_slot_monitor(ioqq, slot); diff --git a/src/mtab.c b/src/mtab.c index 082150c..cc726a2 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -61,7 +61,7 @@ struct bfs_mtab { /** * Add an entry to the mount table. */ -attr_maybe_unused +attr(maybe_unused) static int bfs_mtab_add(struct bfs_mtab *mtab, const char *path, const char *type) { struct bfs_mount *mount = RESERVE(struct bfs_mount, &mtab->mounts, &mtab->nmounts); if (!mount) { diff --git a/src/opt.c b/src/opt.c index 5da73d8..3ee5e81 100644 --- a/src/opt.c +++ b/src/opt.c @@ -305,7 +305,7 @@ struct opt_state { }; /** Log an optimization. */ -attr_format(3, 4) +attr(format(3, 4)) static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { bfs_assert(state->ctx->optlevel >= level); @@ -321,7 +321,7 @@ static bool opt_debug(const struct opt_state *state, int level, const char *form } /** Warn about an expression. */ -attr_format(3, 4) +attr(format(3, 4)) static void opt_warning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, ...) { if (bfs_expr_warning(state->ctx, expr)) { va_list args; diff --git a/src/parse.c b/src/parse.c index 778fc68..35d22fb 100644 --- a/src/parse.c +++ b/src/parse.c @@ -246,7 +246,7 @@ static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, /** * Print an error message during parsing. */ -attr_format(2, 3) +attr(format(2, 3)) static void parse_error(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -266,7 +266,7 @@ static void parse_error(const struct parser_state *state, const char *format, .. /** * Print an error about some command line arguments. */ -attr_format(4, 5) +attr(format(4, 5)) static void parse_argv_error(const struct parser_state *state, char **argv, size_t argc, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -286,7 +286,7 @@ static void parse_argv_error(const struct parser_state *state, char **argv, size /** * Print an error about conflicting command line arguments. */ -attr_format(6, 7) +attr(format(6, 7)) static void parse_conflict_error(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -307,7 +307,7 @@ static void parse_conflict_error(const struct parser_state *state, char **argv1, /** * Print an error about an expression. */ -attr_format(3, 4) +attr(format(3, 4)) static void parse_expr_error(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -324,7 +324,7 @@ static void parse_expr_error(const struct parser_state *state, const struct bfs_ /** * Print a warning message during parsing. */ -attr_format(2, 3) +attr(format(2, 3)) static bool parse_warning(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -347,7 +347,7 @@ static bool parse_warning(const struct parser_state *state, const char *format, /** * Print a warning about conflicting command line arguments. */ -attr_format(6, 7) +attr(format(6, 7)) static bool parse_conflict_warning(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -371,7 +371,7 @@ static bool parse_conflict_warning(const struct parser_state *state, char **argv /** * Print a warning about an expression. */ -attr_format(3, 4) +attr(format(3, 4)) static bool parse_expr_warning(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; diff --git a/src/trie.c b/src/trie.c index cf55cee..bd5300d 100644 --- a/src/trie.c +++ b/src/trie.c @@ -95,7 +95,7 @@ bfs_static_assert(CHAR_WIDTH == 8); #if __i386__ || __x86_64__ -# define trie_clones attr_target_clones("popcnt", "default") +# define trie_clones attr(target_clones("popcnt", "default")) #else # define trie_clones #endif diff --git a/src/xspawn.c b/src/xspawn.c index 03287c3..40115a1 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -83,7 +83,7 @@ int bfs_spawn_destroy(struct bfs_spawn *ctx) { #if _POSIX_SPAWN > 0 /** Set some posix_spawnattr flags. */ -attr_maybe_unused +attr(maybe_unused) static int bfs_spawn_addflags(struct bfs_spawn *ctx, short flags) { short prev; errno = posix_spawnattr_getflags(&ctx->attr, &prev); -- cgit v1.2.3 From cbbcb340c974fca7312713d2515e3b32bacb176f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Dec 2023 18:35:58 -0500 Subject: alloc: Add attr(nodiscard) to a few functions --- src/alloc.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/alloc.h') diff --git a/src/alloc.h b/src/alloc.h index f21ddb9..60dd738 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -168,6 +168,7 @@ void *zalloc(size_t align, size_t size); * @return * The reallocated memory, or NULL on failure. */ +attr(nodiscard, aligned_alloc(2, 4)) void *xrealloc(void *ptr, size_t align, size_t old_size, size_t new_size); /** Reallocate memory for an array. */ @@ -193,6 +194,7 @@ void *xrealloc(void *ptr, size_t align, size_t old_size, size_t new_size); * for (count + 1) elements. On failure, errno will be non-zero, and * ptr will returned unchanged. */ +attr(nodiscard) void *reserve(void *ptr, size_t align, size_t size, size_t count); /** @@ -349,6 +351,7 @@ void *varena_alloc(struct varena *varena, size_t count); * @return * The resized struct, or NULL on failure. */ +attr(nodiscard) void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t new_count); /** @@ -363,6 +366,7 @@ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t * @return * The resized struct, or NULL on failure. */ +attr(nodiscard) void *varena_grow(struct varena *varena, void *ptr, size_t *count); /** -- cgit v1.2.3 From 2c3ef3a06ee1f951f6d68be6d0d3f6a1822b05b7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 11 Mar 2024 09:51:03 -0400 Subject: Re-run include-what-you-use --- src/alloc.h | 1 + src/bfstd.c | 6 +++--- src/bftw.c | 1 + src/color.h | 1 - src/ctx.c | 1 + src/ctx.h | 2 ++ src/diag.c | 2 +- src/dir.h | 4 ++-- src/dstring.c | 2 ++ src/eval.c | 4 ++-- src/expr.c | 4 ++-- src/expr.h | 1 - src/ioq.c | 5 +++-- src/main.c | 1 + src/opt.c | 3 ++- src/parse.c | 3 +-- src/printf.c | 3 ++- src/trie.c | 2 -- src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 1 - tests/alloc.c | 1 + tests/bfstd.c | 3 --- tests/bit.c | 2 +- tests/ioq.c | 2 ++ tests/main.c | 3 --- tests/xtime.c | 4 ++-- tests/xtouch.c | 1 + 28 files changed, 33 insertions(+), 32 deletions(-) (limited to 'src/alloc.h') diff --git a/src/alloc.h b/src/alloc.h index 60dd738..ae055bc 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include /** Check if a size is properly aligned. */ diff --git a/src/bfstd.c b/src/bfstd.c index ce4aa49..c6c2e7f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -2,18 +2,19 @@ // SPDX-License-Identifier: 0BSD #include "bfstd.h" -#include "bit.h" #include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" #include "xregex.h" -#include #include #include #include +#include #include #include +#include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #if BFS_USE_SYS_SYSMACROS_H # include diff --git a/src/bftw.c b/src/bftw.c index 6f52299..50b8b02 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -35,6 +35,7 @@ #include #include #include +#include /** Initialize a bftw_stat cache. */ static void bftw_stat_init(struct bftw_stat *bufs, struct bfs_stat *stat_buf, struct bfs_stat *lstat_buf) { diff --git a/src/color.h b/src/color.h index 85633a4..e3e7973 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include "dstring.h" -#include #include /** diff --git a/src/ctx.c b/src/ctx.c index 6c84f75..f5b28c7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -6,6 +6,7 @@ #include "color.h" #include "diag.h" #include "expr.h" +#include "list.h" #include "mtab.h" #include "pwcache.h" #include "stat.h" diff --git a/src/ctx.h b/src/ctx.h index aa91f2c..e14db21 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -18,6 +18,8 @@ #include #include +struct CFILE; + /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index 656fa89..cb27b92 100644 --- a/src/diag.c +++ b/src/diag.c @@ -11,8 +11,8 @@ #include "expr.h" #include #include +#include #include -#include /** bfs_diagf() implementation. */ attr(printf(2, 0)) diff --git a/src/dir.h b/src/dir.h index b11d454..18d907e 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,8 +8,6 @@ #ifndef BFS_DIR_H #define BFS_DIR_H -#include "alloc.h" -#include "config.h" #include /** @@ -78,6 +76,8 @@ struct bfs_dirent { */ struct bfs_dir *bfs_allocdir(void); +struct arena; + /** * Initialize an arena for directories. * diff --git a/src/dstring.c b/src/dstring.c index bc18308..10b0fad 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -7,6 +7,8 @@ #include "config.h" #include "diag.h" #include +#include +#include #include #include #include diff --git a/src/eval.c b/src/eval.c index 1711001..9e55964 100644 --- a/src/eval.c +++ b/src/eval.c @@ -25,7 +25,6 @@ #include "stat.h" #include "trie.h" #include "xregex.h" -#include "xtime.h" #include #include #include @@ -36,8 +35,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/src/expr.c b/src/expr.c index 3e0033f..5784220 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4,12 +4,12 @@ #include "expr.h" #include "alloc.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "exec.h" +#include "list.h" #include "printf.h" #include "xregex.h" -#include -#include #include struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { diff --git a/src/expr.h b/src/expr.h index 957b04a..75cb5fd 100644 --- a/src/expr.h +++ b/src/expr.h @@ -12,7 +12,6 @@ #include "config.h" #include "eval.h" #include "stat.h" -#include #include #include diff --git a/src/ioq.c b/src/ioq.c index 00c3b86..37eed7d 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -126,13 +126,14 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "sanity.h" #include "stat.h" #include "thread.h" -#include #include +#include #include +#include #include +#include #if BFS_USE_LIBURING # include diff --git a/src/main.c b/src/main.c index 16a2576..e120f03 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,7 @@ #include "bfstd.h" #include "config.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "parse.h" #include diff --git a/src/opt.c b/src/opt.c index 74145ac..76965de 100644 --- a/src/opt.c +++ b/src/opt.c @@ -26,11 +26,13 @@ */ #include "opt.h" +#include "bftw.h" #include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" #include "diag.h" +#include "dir.h" #include "eval.h" #include "exec.h" #include "expr.h" @@ -40,7 +42,6 @@ #include #include #include -#include #include static char *fake_and_arg = "-and"; diff --git a/src/parse.c b/src/parse.c index b26a50f..3b7386d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -42,8 +42,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/printf.c b/src/printf.c index 34ed606..487f039 100644 --- a/src/printf.c +++ b/src/printf.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "printf.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -14,10 +15,10 @@ #include "mtab.h" #include "pwcache.h" #include "stat.h" -#include "xtime.h" #include #include #include +#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bd5300d..1ffb23a 100644 --- a/src/trie.c +++ b/src/trie.c @@ -87,9 +87,7 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include -#include #include bfs_static_assert(CHAR_WIDTH == 8); diff --git a/src/trie.h b/src/trie.h index 02088f1..4288d76 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "alloc.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 8d6108b..6a94d3d 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #if BFS_USE_PATHS_H diff --git a/src/xtime.c b/src/xtime.c index 05f0e1a..5b259ab 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -7,7 +7,6 @@ #include "diag.h" #include #include -#include #include #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 4ce23d4..9f08111 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "tests.h" #include "../src/alloc.h" +#include "../src/config.h" #include "../src/diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 0ded5de..26abdb6 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,9 +7,6 @@ #include "../src/diag.h" #include #include -#include -#include -#include #include #include diff --git a/tests/bit.c b/tests/bit.c index b944748..3d66ce3 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -3,10 +3,10 @@ #include "tests.h" #include "../src/bit.h" +#include "../src/config.h" #include "../src/diag.h" #include #include -#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); diff --git a/tests/ioq.c b/tests/ioq.c index 56e1886..1ce8f75 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -4,10 +4,12 @@ #include "tests.h" #include "../src/ioq.h" #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/diag.h" #include "../src/dir.h" #include #include +#include /** * Test for blocking within ioq_slot_push(). diff --git a/tests/main.c b/tests/main.c index 38438b2..8849e8c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,11 +6,8 @@ */ #include "tests.h" -#include "../src/bfstd.h" #include "../src/color.h" #include "../src/config.h" -#include "../src/diag.h" -#include #include #include #include diff --git a/tests/xtime.c b/tests/xtime.c index 3f1fec2..c8dc00b 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -5,10 +5,10 @@ #include "../src/xtime.h" #include "../src/bfstd.h" #include "../src/config.h" +#include "../src/diag.h" #include +#include #include -#include -#include #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { diff --git a/tests/xtouch.c b/tests/xtouch.c index 8c5c5f3..fad272f 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From c66379749f423413913b406609dfe9311ba6e555 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 Apr 2024 14:53:56 -0400 Subject: Rename config.h to prelude.h --- src/alloc.c | 2 +- src/alloc.h | 2 +- src/bar.c | 2 +- src/bfstd.c | 2 +- src/bfstd.h | 2 +- src/bftw.c | 2 +- src/bit.h | 2 +- src/color.c | 2 +- src/color.h | 2 +- src/config.h | 377 --------------------------------------------------------- src/ctx.h | 2 +- src/diag.c | 2 +- src/diag.h | 2 +- src/dir.c | 2 +- src/dstring.c | 2 +- src/dstring.h | 2 +- src/eval.c | 2 +- src/eval.h | 2 +- src/exec.c | 2 +- src/expr.h | 2 +- src/fsade.c | 2 +- src/fsade.h | 2 +- src/ioq.c | 2 +- src/ioq.h | 2 +- src/main.c | 4 +- src/mtab.c | 2 +- src/mtab.h | 2 +- src/opt.c | 2 +- src/parse.c | 2 +- src/prelude.h | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/printf.c | 2 +- src/pwcache.c | 2 +- src/sanity.h | 2 +- src/stat.c | 2 +- src/stat.h | 2 +- src/thread.c | 2 +- src/thread.h | 2 +- src/trie.c | 2 +- src/xregex.c | 2 +- src/xspawn.c | 2 +- src/xspawn.h | 2 +- src/xtime.c | 2 +- tests/alloc.c | 2 +- tests/bfstd.c | 2 +- tests/bit.c | 2 +- tests/ioq.c | 2 +- tests/main.c | 2 +- tests/tests.h | 2 +- tests/trie.c | 2 +- tests/xspawn.c | 2 +- tests/xtime.c | 2 +- tests/xtouch.c | 2 +- 52 files changed, 428 insertions(+), 428 deletions(-) delete mode 100644 src/config.h create mode 100644 src/prelude.h (limited to 'src/alloc.h') diff --git a/src/alloc.c b/src/alloc.c index b65d0c5..ec8608f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include diff --git a/src/alloc.h b/src/alloc.h index ae055bc..095134a 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -8,7 +8,7 @@ #ifndef BFS_ALLOC_H #define BFS_ALLOC_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/bar.c b/src/bar.c index 8ab4112..184d9a0 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bar.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "dstring.h" #include #include diff --git a/src/bfstd.c b/src/bfstd.c index 2499f00..e1b4804 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/bfstd.h b/src/bfstd.h index fc22971..42f5d5b 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -8,7 +8,7 @@ #ifndef BFS_BFSTD_H #define BFS_BFSTD_H -#include "config.h" +#include "prelude.h" #include "sanity.h" #include diff --git a/src/bftw.c b/src/bftw.c index 6130c44..c4d3c17 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -18,10 +18,10 @@ * various helper functions to take fewer parameters. */ +#include "prelude.h" #include "bftw.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/bit.h b/src/bit.h index 69df21e..17cfbcf 100644 --- a/src/bit.h +++ b/src/bit.h @@ -8,7 +8,7 @@ #ifndef BFS_BIT_H #define BFS_BIT_H -#include "config.h" +#include "prelude.h" #include #include diff --git a/src/color.c b/src/color.c index 8c32a68..f004bf2 100644 --- a/src/color.c +++ b/src/color.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "color.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/color.h b/src/color.h index e3e7973..3278cd6 100644 --- a/src/color.h +++ b/src/color.h @@ -8,7 +8,7 @@ #ifndef BFS_COLOR_H #define BFS_COLOR_H -#include "config.h" +#include "prelude.h" #include "dstring.h" #include diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 2eff1fc..0000000 --- a/src/config.h +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -/** - * Configuration and feature/platform detection. - */ - -#ifndef BFS_CONFIG_H -#define BFS_CONFIG_H - -// Possible __STDC_VERSION__ values - -#define C95 199409L -#define C99 199901L -#define C11 201112L -#define C17 201710L -#define C23 202311L - -#include - -#if __STDC_VERSION__ < C23 -# include -# include -# include -#endif - -// bfs packaging configuration - -#ifndef BFS_COMMAND -# define BFS_COMMAND "bfs" -#endif -#ifndef BFS_HOMEPAGE -# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" -#endif - -// This is a symbol instead of a literal so we don't have to rebuild everything -// when the version number changes -extern const char bfs_version[]; - -// Check for system headers - -#ifdef __has_include - -#if __has_include() -# define BFS_HAS_MNTENT_H true -#endif -#if __has_include() -# define BFS_HAS_PATHS_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_ACL_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_CAPABILITY_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_EXTATTR_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_MKDEV_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_PARAM_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_SYSMACROS_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_XATTR_H true -#endif -#if __has_include() -# define BFS_HAS_THREADS_H true -#endif -#if __has_include() -# define BFS_HAS_UTIL_H true -#endif - -#else // !__has_include - -#define BFS_HAS_MNTENT_H __GLIBC__ -#define BFS_HAS_PATHS_H true -#define BFS_HAS_SYS_ACL_H true -#define BFS_HAS_SYS_CAPABILITY_H __linux__ -#define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ -#define BFS_HAS_SYS_MKDEV_H false -#define BFS_HAS_SYS_PARAM_H true -#define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ -#define BFS_HAS_SYS_XATTR_H __linux__ -#define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) -#define BFS_HAS_UTIL_H __NetBSD__ - -#endif // !__has_include - -#ifndef BFS_USE_MNTENT_H -# define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H -#endif -#ifndef BFS_USE_PATHS_H -# define BFS_USE_PATHS_H BFS_HAS_PATHS_H -#endif -#ifndef BFS_USE_SYS_ACL_H -# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) -#endif -#ifndef BFS_USE_SYS_CAPABILITY_H -# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) -#endif -#ifndef BFS_USE_SYS_EXTATTR_H -# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) -#endif -#ifndef BFS_USE_SYS_MKDEV_H -# define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H -#endif -#ifndef BFS_USE_SYS_PARAM_H -# define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H -#endif -#ifndef BFS_USE_SYS_SYSMACROS_H -# define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H -#endif -#ifndef BFS_USE_SYS_XATTR_H -# define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H -#endif -#ifndef BFS_USE_THREADS_H -# define BFS_USE_THREADS_H BFS_HAS_THREADS_H -#endif -#ifndef BFS_USE_UTIL_H -# define BFS_USE_UTIL_H BFS_HAS_UTIL_H -#endif - -// Stub out feature detection on old/incompatible compilers - -#ifndef __has_feature -# define __has_feature(feat) false -#endif - -#ifndef __has_c_attribute -# define __has_c_attribute(attr) false -#endif - -#ifndef __has_attribute -# define __has_attribute(attr) false -#endif - -// Platform detection - -// Get the definition of BSD if available -#if BFS_USE_SYS_PARAM_H -# include -#endif - -#ifndef __GLIBC_PREREQ -# define __GLIBC_PREREQ(maj, min) false -#endif - -#ifndef __NetBSD_Prereq__ -# define __NetBSD_Prereq__(maj, min, patch) false -#endif - -// Fundamental utilities - -/** - * Get the length of an array. - */ -#define countof(array) (sizeof(array) / sizeof(0[array])) - -/** - * False sharing/destructive interference/largest cache line size. - */ -#ifdef __GCC_DESTRUCTIVE_SIZE -# define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE -#else -# define FALSE_SHARING_SIZE 64 -#endif - -/** - * True sharing/constructive interference/smallest cache line size. - */ -#ifdef __GCC_CONSTRUCTIVE_SIZE -# define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE -#else -# define TRUE_SHARING_SIZE 64 -#endif - -/** - * Alignment specifier that avoids false sharing. - */ -#define cache_align alignas(FALSE_SHARING_SIZE) - -#if __COSMOPOLITAN__ -typedef long double max_align_t; -#endif - -// Wrappers for attributes - -/** - * Silence warnings about switch/case fall-throughs. - */ -#if __has_attribute(fallthrough) -# define fallthru __attribute__((fallthrough)) -#else -# define fallthru ((void)0) -#endif - -/** - * Silence warnings about unused declarations. - */ -#if __has_attribute(unused) -# define attr_maybe_unused __attribute__((unused)) -#else -# define attr_maybe_unused -#endif - -/** - * Warn if a value is unused. - */ -#if __has_attribute(warn_unused_result) -# define attr_nodiscard __attribute__((warn_unused_result)) -#else -# define attr_nodiscard -#endif - -/** - * Hint to avoid inlining a function. - */ -#if __has_attribute(noinline) -# define attr_noinline __attribute__((noinline)) -#else -# define attr_noinline -#endif - -/** - * Hint that a function is unlikely to be called. - */ -#if __has_attribute(cold) -# define attr_cold attr_noinline __attribute__((cold)) -#else -# define attr_cold attr_noinline -#endif - -/** - * Adds compiler warnings for bad printf()-style function calls, if supported. - */ -#if __has_attribute(format) -# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) -#else -# define attr_printf(fmt, args) -#endif - -/** - * Annotates allocator-like functions. - */ -#if __has_attribute(malloc) -# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC -# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) -# else -# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) -# endif -#else -# define attr_malloc(...) attr_nodiscard -#endif - -/** - * Specifies that a function returns allocations with a given alignment. - */ -#if __has_attribute(alloc_align) -# define attr_alloc_align(param) __attribute__((alloc_align(param))) -#else -# define attr_alloc_align(param) -#endif - -/** - * Specifies that a function returns allocations with a given size. - */ -#if __has_attribute(alloc_size) -# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) -#else -# define attr_alloc_size(...) -#endif - -/** - * Shorthand for attr_alloc_align() and attr_alloc_size(). - */ -#define attr_aligned_alloc(align, ...) \ - attr_alloc_align(align) \ - attr_alloc_size(__VA_ARGS__) - -/** - * Check if function multiversioning via GNU indirect functions (ifunc) is supported. - */ -#ifndef BFS_USE_TARGET_CLONES -# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) -# define BFS_USE_TARGET_CLONES true -# endif -#endif - -/** - * Apply the target_clones attribute, if available. - */ -#if BFS_USE_TARGET_CLONES -# define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) -#else -# define attr_target_clones(...) -#endif - -/** - * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to - * - * attr_a - * attr_b(c) - * attr_d - */ -#define attr(...) \ - attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) - -/** - * attr() helper. For exposition, pretend we support only 2 args, instead of 9. - * There are a few cases: - * - * attr() - * => attr__(attr_, none, none) - * => attr_ => - * attr_none => - * attr_too_many_none() => - * - * attr(a) - * => attr__(attr_a, none, none) - * => attr_a => __attribute__((a)) - * attr_none => - * attr_too_many_none() => - * - * attr(a, b(c)) - * => attr__(attr_a, b(c), none, none) - * => attr_a => __attribute__((a)) - * attr_b(c) => __attribute__((b(c))) - * attr_too_many_none(none) => - * - * attr(a, b(c), d) - * => attr__(attr_a, b(c), d, none, none) - * => attr_a => __attribute__((a)) - * attr_b(c) => __attribute__((b(c))) - * attr_too_many_d(none, none) => error - * - * Some attribute names are the same as standard library functions, e.g. printf. - * Standard libraries are permitted to define these functions as macros, like - * - * #define printf(...) __builtin_printf(__VA_ARGS__) - * - * The token paste in - * - * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) - * - * is necessary to prevent macro expansion before evaluating attr__(). - * Otherwise, we could get - * - * attr(printf(1, 2)) - * => attr__(__builtin_printf(1, 2), none, none) - * => attr____builtin_printf(1, 2) - * => error - */ -#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ - a1 \ - attr_##a2 \ - attr_##a3 \ - attr_##a4 \ - attr_##a5 \ - attr_##a6 \ - attr_##a7 \ - attr_##a8 \ - attr_##a9 \ - attr_too_many_##none(__VA_ARGS__) - -// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) -#define attr_none -// Ignore `attr_` from expanding 0-argument attr() -#define attr_ -// Only trigger an error on more than 9 arguments -#define attr_too_many_none(...) - -#endif // BFS_CONFIG_H diff --git a/src/ctx.h b/src/ctx.h index e14db21..fc3020c 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -8,9 +8,9 @@ #ifndef BFS_CTX_H #define BFS_CTX_H +#include "prelude.h" #include "alloc.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "expr.h" #include "trie.h" diff --git a/src/diag.c b/src/diag.c index cb27b92..deb6f26 100644 --- a/src/diag.c +++ b/src/diag.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "diag.h" #include "alloc.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "dstring.h" #include "expr.h" diff --git a/src/diag.h b/src/diag.h index 4054c48..2b13609 100644 --- a/src/diag.h +++ b/src/diag.h @@ -8,7 +8,7 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "config.h" +#include "prelude.h" #include /** diff --git a/src/dir.c b/src/dir.c index 98518f2..53c9be3 100644 --- a/src/dir.c +++ b/src/dir.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dir.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "trie.h" diff --git a/src/dstring.c b/src/dstring.c index 10b0fad..913dda8 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dstring.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include #include diff --git a/src/dstring.h b/src/dstring.h index 6006199..9ea7eb9 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -8,8 +8,8 @@ #ifndef BFS_DSTRING_H #define BFS_DSTRING_H +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include #include diff --git a/src/eval.c b/src/eval.c index d0112c2..b103912 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5,12 +5,12 @@ * Implementation of all the primary expressions. */ +#include "prelude.h" #include "eval.h" #include "bar.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/eval.h b/src/eval.h index ae43628..4dd7996 100644 --- a/src/eval.h +++ b/src/eval.h @@ -9,7 +9,7 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H -#include "config.h" +#include "prelude.h" struct bfs_ctx; struct bfs_expr; diff --git a/src/exec.c b/src/exec.c index 60bfd28..e782d49 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "exec.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dstring.h" diff --git a/src/expr.h b/src/expr.h index 75cb5fd..7bcace7 100644 --- a/src/expr.h +++ b/src/expr.h @@ -8,8 +8,8 @@ #ifndef BFS_EXPR_H #define BFS_EXPR_H +#include "prelude.h" #include "color.h" -#include "config.h" #include "eval.h" #include "stat.h" #include diff --git a/src/fsade.c b/src/fsade.c index 0810c7f..34a4d57 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "fsade.h" #include "atomic.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" diff --git a/src/fsade.h b/src/fsade.h index 1f1dbfc..6300852 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -9,7 +9,7 @@ #ifndef BFS_FSADE_H #define BFS_FSADE_H -#include "config.h" +#include "prelude.h" #define BFS_CAN_CHECK_ACL BFS_USE_SYS_ACL_H diff --git a/src/ioq.c b/src/ioq.c index b936681..189bdac 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -118,12 +118,12 @@ * [1]: https://arxiv.org/abs/2201.02179 */ +#include "prelude.h" #include "ioq.h" #include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "stat.h" diff --git a/src/ioq.h b/src/ioq.h index 818eea6..d8e1573 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -8,7 +8,7 @@ #ifndef BFS_IOQ_H #define BFS_IOQ_H -#include "config.h" +#include "prelude.h" #include "dir.h" #include "stat.h" #include diff --git a/src/main.c b/src/main.c index e120f03..9d8b206 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ * - bit.h (bit manipulation) * - bfstd.[ch] (standard library wrappers/polyfills) * - color.[ch] (for pretty terminal colors) - * - config.h (configuration and feature/platform detection) + * - prelude.h (configuration and feature/platform detection) * - diag.[ch] (formats diagnostic messages) * - dir.[ch] (a directory API facade) * - dstring.[ch] (a dynamic string library) @@ -45,8 +45,8 @@ * - xtime.[ch] (date/time handling utilities) */ +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "eval.h" diff --git a/src/mtab.c b/src/mtab.c index 86ae151..7905d14 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "mtab.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "stat.h" #include "trie.h" #include diff --git a/src/mtab.h b/src/mtab.h index d99d78f..67290c2 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -8,7 +8,7 @@ #ifndef BFS_MTAB_H #define BFS_MTAB_H -#include "config.h" +#include "prelude.h" struct bfs_stat; diff --git a/src/opt.c b/src/opt.c index b74b4e1..ffc795b 100644 --- a/src/opt.c +++ b/src/opt.c @@ -25,11 +25,11 @@ * effects are reachable at all, skipping the traversal if not. */ +#include "prelude.h" #include "opt.h" #include "bftw.h" #include "bit.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/parse.c b/src/parse.c index a3e32fe..c2ae58f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,12 +8,12 @@ * flags like always-true options, and skipping over paths wherever they appear. */ +#include "prelude.h" #include "parse.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/prelude.h b/src/prelude.h new file mode 100644 index 0000000..c3a0752 --- /dev/null +++ b/src/prelude.h @@ -0,0 +1,377 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Configuration and feature/platform detection. + */ + +#ifndef BFS_PRELUDE_H +#define BFS_PRELUDE_H + +// Possible __STDC_VERSION__ values + +#define C95 199409L +#define C99 199901L +#define C11 201112L +#define C17 201710L +#define C23 202311L + +#include + +#if __STDC_VERSION__ < C23 +# include +# include +# include +#endif + +// bfs packaging configuration + +#ifndef BFS_COMMAND +# define BFS_COMMAND "bfs" +#endif +#ifndef BFS_HOMEPAGE +# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" +#endif + +// This is a symbol instead of a literal so we don't have to rebuild everything +// when the version number changes +extern const char bfs_version[]; + +// Check for system headers + +#ifdef __has_include + +#if __has_include() +# define BFS_HAS_MNTENT_H true +#endif +#if __has_include() +# define BFS_HAS_PATHS_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_ACL_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_CAPABILITY_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_EXTATTR_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_MKDEV_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_PARAM_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_SYSMACROS_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_XATTR_H true +#endif +#if __has_include() +# define BFS_HAS_THREADS_H true +#endif +#if __has_include() +# define BFS_HAS_UTIL_H true +#endif + +#else // !__has_include + +#define BFS_HAS_MNTENT_H __GLIBC__ +#define BFS_HAS_PATHS_H true +#define BFS_HAS_SYS_ACL_H true +#define BFS_HAS_SYS_CAPABILITY_H __linux__ +#define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ +#define BFS_HAS_SYS_MKDEV_H false +#define BFS_HAS_SYS_PARAM_H true +#define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ +#define BFS_HAS_SYS_XATTR_H __linux__ +#define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) +#define BFS_HAS_UTIL_H __NetBSD__ + +#endif // !__has_include + +#ifndef BFS_USE_MNTENT_H +# define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H +#endif +#ifndef BFS_USE_PATHS_H +# define BFS_USE_PATHS_H BFS_HAS_PATHS_H +#endif +#ifndef BFS_USE_SYS_ACL_H +# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) +#endif +#ifndef BFS_USE_SYS_CAPABILITY_H +# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) +#endif +#ifndef BFS_USE_SYS_EXTATTR_H +# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) +#endif +#ifndef BFS_USE_SYS_MKDEV_H +# define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H +#endif +#ifndef BFS_USE_SYS_PARAM_H +# define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H +#endif +#ifndef BFS_USE_SYS_SYSMACROS_H +# define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H +#endif +#ifndef BFS_USE_SYS_XATTR_H +# define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H +#endif +#ifndef BFS_USE_THREADS_H +# define BFS_USE_THREADS_H BFS_HAS_THREADS_H +#endif +#ifndef BFS_USE_UTIL_H +# define BFS_USE_UTIL_H BFS_HAS_UTIL_H +#endif + +// Stub out feature detection on old/incompatible compilers + +#ifndef __has_feature +# define __has_feature(feat) false +#endif + +#ifndef __has_c_attribute +# define __has_c_attribute(attr) false +#endif + +#ifndef __has_attribute +# define __has_attribute(attr) false +#endif + +// Platform detection + +// Get the definition of BSD if available +#if BFS_USE_SYS_PARAM_H +# include +#endif + +#ifndef __GLIBC_PREREQ +# define __GLIBC_PREREQ(maj, min) false +#endif + +#ifndef __NetBSD_Prereq__ +# define __NetBSD_Prereq__(maj, min, patch) false +#endif + +// Fundamental utilities + +/** + * Get the length of an array. + */ +#define countof(array) (sizeof(array) / sizeof(0[array])) + +/** + * False sharing/destructive interference/largest cache line size. + */ +#ifdef __GCC_DESTRUCTIVE_SIZE +# define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE +#else +# define FALSE_SHARING_SIZE 64 +#endif + +/** + * True sharing/constructive interference/smallest cache line size. + */ +#ifdef __GCC_CONSTRUCTIVE_SIZE +# define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE +#else +# define TRUE_SHARING_SIZE 64 +#endif + +/** + * Alignment specifier that avoids false sharing. + */ +#define cache_align alignas(FALSE_SHARING_SIZE) + +#if __COSMOPOLITAN__ +typedef long double max_align_t; +#endif + +// Wrappers for attributes + +/** + * Silence warnings about switch/case fall-throughs. + */ +#if __has_attribute(fallthrough) +# define fallthru __attribute__((fallthrough)) +#else +# define fallthru ((void)0) +#endif + +/** + * Silence warnings about unused declarations. + */ +#if __has_attribute(unused) +# define attr_maybe_unused __attribute__((unused)) +#else +# define attr_maybe_unused +#endif + +/** + * Warn if a value is unused. + */ +#if __has_attribute(warn_unused_result) +# define attr_nodiscard __attribute__((warn_unused_result)) +#else +# define attr_nodiscard +#endif + +/** + * Hint to avoid inlining a function. + */ +#if __has_attribute(noinline) +# define attr_noinline __attribute__((noinline)) +#else +# define attr_noinline +#endif + +/** + * Hint that a function is unlikely to be called. + */ +#if __has_attribute(cold) +# define attr_cold attr_noinline __attribute__((cold)) +#else +# define attr_cold attr_noinline +#endif + +/** + * Adds compiler warnings for bad printf()-style function calls, if supported. + */ +#if __has_attribute(format) +# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) +#else +# define attr_printf(fmt, args) +#endif + +/** + * Annotates allocator-like functions. + */ +#if __has_attribute(malloc) +# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC +# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) +# else +# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) +# endif +#else +# define attr_malloc(...) attr_nodiscard +#endif + +/** + * Specifies that a function returns allocations with a given alignment. + */ +#if __has_attribute(alloc_align) +# define attr_alloc_align(param) __attribute__((alloc_align(param))) +#else +# define attr_alloc_align(param) +#endif + +/** + * Specifies that a function returns allocations with a given size. + */ +#if __has_attribute(alloc_size) +# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else +# define attr_alloc_size(...) +#endif + +/** + * Shorthand for attr_alloc_align() and attr_alloc_size(). + */ +#define attr_aligned_alloc(align, ...) \ + attr_alloc_align(align) \ + attr_alloc_size(__VA_ARGS__) + +/** + * Check if function multiversioning via GNU indirect functions (ifunc) is supported. + */ +#ifndef BFS_USE_TARGET_CLONES +# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) +# define BFS_USE_TARGET_CLONES true +# endif +#endif + +/** + * Apply the target_clones attribute, if available. + */ +#if BFS_USE_TARGET_CLONES +# define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) +#else +# define attr_target_clones(...) +#endif + +/** + * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to + * + * attr_a + * attr_b(c) + * attr_d + */ +#define attr(...) \ + attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) + +/** + * attr() helper. For exposition, pretend we support only 2 args, instead of 9. + * There are a few cases: + * + * attr() + * => attr__(attr_, none, none) + * => attr_ => + * attr_none => + * attr_too_many_none() => + * + * attr(a) + * => attr__(attr_a, none, none) + * => attr_a => __attribute__((a)) + * attr_none => + * attr_too_many_none() => + * + * attr(a, b(c)) + * => attr__(attr_a, b(c), none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_none(none) => + * + * attr(a, b(c), d) + * => attr__(attr_a, b(c), d, none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_d(none, none) => error + * + * Some attribute names are the same as standard library functions, e.g. printf. + * Standard libraries are permitted to define these functions as macros, like + * + * #define printf(...) __builtin_printf(__VA_ARGS__) + * + * The token paste in + * + * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) + * + * is necessary to prevent macro expansion before evaluating attr__(). + * Otherwise, we could get + * + * attr(printf(1, 2)) + * => attr__(__builtin_printf(1, 2), none, none) + * => attr____builtin_printf(1, 2) + * => error + */ +#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ + a1 \ + attr_##a2 \ + attr_##a3 \ + attr_##a4 \ + attr_##a5 \ + attr_##a6 \ + attr_##a7 \ + attr_##a8 \ + attr_##a9 \ + attr_too_many_##none(__VA_ARGS__) + +// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) +#define attr_none +// Ignore `attr_` from expanding 0-argument attr() +#define attr_ +// Only trigger an error on more than 9 arguments +#define attr_too_many_none(...) + +#endif // BFS_PRELUDE_H diff --git a/src/printf.c b/src/printf.c index 3b8269e..4df399b 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "printf.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/pwcache.c b/src/pwcache.c index 79437d8..af8c237 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "pwcache.h" #include "alloc.h" -#include "config.h" #include "trie.h" #include #include diff --git a/src/sanity.h b/src/sanity.h index 423e6ff..e168b8f 100644 --- a/src/sanity.h +++ b/src/sanity.h @@ -8,7 +8,7 @@ #ifndef BFS_SANITY_H #define BFS_SANITY_H -#include "config.h" +#include "prelude.h" #include #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) diff --git a/src/stat.c b/src/stat.c index 2f2743b..eca5bab 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "stat.h" #include "atomic.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include diff --git a/src/stat.h b/src/stat.h index 856a2ca..1fdd263 100644 --- a/src/stat.h +++ b/src/stat.h @@ -12,7 +12,7 @@ #ifndef BFS_STAT_H #define BFS_STAT_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/thread.c b/src/thread.c index 200d8c3..3793896 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "thread.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/src/thread.h b/src/thread.h index 8174fe4..db11bd8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,7 +8,7 @@ #ifndef BFS_THREAD_H #define BFS_THREAD_H -#include "config.h" +#include "prelude.h" #include #if __STDC_VERSION__ < C23 && !defined(thread_local) diff --git a/src/trie.c b/src/trie.c index f275064..808953e 100644 --- a/src/trie.c +++ b/src/trie.c @@ -81,10 +81,10 @@ * and insert intermediate singleton "jump" nodes when necessary. */ +#include "prelude.h" #include "trie.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "list.h" #include diff --git a/src/xregex.c b/src/xregex.c index 3df27f0..c2711bc 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xregex.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/xspawn.c b/src/xspawn.c index 347625d..113d7ec 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xspawn.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.h b/src/xspawn.h index a20cbd0..6a8f54a 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -8,7 +8,7 @@ #ifndef BFS_XSPAWN_H #define BFS_XSPAWN_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/xtime.c b/src/xtime.c index bcf6dd3..91ed915 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 54b84ba..6c0defd 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "alloc.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 5e408ca..07b68b0 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/bit.c b/tests/bit.c index b444e50..674d1b2 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "bit.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/ioq.c b/tests/ioq.c index a69f2bf..ef5ee3b 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "ioq.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include diff --git a/tests/main.c b/tests/main.c index 281c417..429772b 100644 --- a/tests/main.c +++ b/tests/main.c @@ -5,10 +5,10 @@ * Entry point for unit tests. */ +#include "prelude.h" #include "tests.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include #include #include diff --git a/tests/tests.h b/tests/tests.h index d61ffd7..9078938 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -8,7 +8,7 @@ #ifndef BFS_TESTS_H #define BFS_TESTS_H -#include "config.h" +#include "prelude.h" #include "diag.h" /** Unit test function type. */ diff --git a/tests/trie.c b/tests/trie.c index 2a6eb48..4667322 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "trie.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/xspawn.c b/tests/xspawn.c index fd8362e..7362aa5 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "dstring.h" #include "xspawn.h" #include diff --git a/tests/xtime.c b/tests/xtime.c index fd7aa0f..a7c63d2 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/xtouch.c b/tests/xtouch.c index b1daec7..82d749d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,8 +1,8 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "sanity.h" #include "xtime.h" #include -- cgit v1.2.3