From a084e2bc8066ce2fed852bf04e5168e289bf834b Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 19 Aug 2023 12:43:39 +0200 Subject: Libunit: added macros that enhance type safety. nxt_min() nxt_max() Return the minimum/maximum of two values. nxt_swap() Swap the values of two variables passed by their addresses. nxt_sizeof_array() Return the size (in bytes) of an array. nxt_nitems() Return the number of elements in an array. nxt_memberof() Expand to a member of a structure. It uses a compound literal for the object. nxt_sizeof_incomplete() Calculate the size of an incomplete type, as if sizeof() could be applied to it. nxt_sizeof_fam0() Calculate the size of each element of a FAM of a structure. nxt_sizeof_fam() Calculate the size of a FAM of a structure. nxt_offsetof_fam() Calculate the offset of the nth element of the FAM from the start of the containing structure. nxt_sizeof_struct() Calculate the total size of a structure containing a FAM. This value is the one that should be used for allocating the structure. Suggested-by: Andrew Clayton nxt_is_near_end() Evaluate to true if the member is near the end of a structure. This is only designed to be used with FAMs, to make sure that the FAM is near the end of the structure (a zero-length array near the end of the structure would still pass this test, but it's a reasonable assertion to do. Suggested-by: David Laight nxt_is_zero_sizeof() Evaluate to true if the size of 'x' is 0. nxt_is_same_type() Evaluate to true if the both arguments are compatible types. nxt_is_same_typeof() Evaluate to true if the types of both arguments are compatible. nxt_is_array() Evaluate to true if the argument is an array. nxt_must_be() It's like static_assert(3), but implemented as an expression. It's necessary for writing the must_be_array() macro. It's always evaluates to (int) 0. nxt_must_be_array() Statically assert that the argument is an array. It is an expression that always evaluates to (int) 0. nxt_must_be_zero_sizeof() Statically assert that the argument has a size of 0. nxt_must_be_near_end() Statically assert that a member of a structure is near the end of it. Suggested-by: David Laight nxt_must_be_fam() Statically assert that the argument is a flexible array member (FAM). It's an expression that always evaluates to (int) 0. Link: Link: Link: Link: Link: Cc: Andrew Clayton Cc: Zhidao Hong Signed-off-by: Alejandro Colomar Signed-off-by: Alejandro Colomar --- auto/make | 2 ++ src/nxt_clang.h | 17 +++------- src/nxt_unit.h | 1 + src/nxt_unit_cdefs.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 src/nxt_unit_cdefs.h diff --git a/auto/make b/auto/make index abfd41ad..88c4fb2b 100644 --- a/auto/make +++ b/auto/make @@ -406,6 +406,7 @@ libunit-install: $NXT_BUILD_DIR/lib/$NXT_LIB_UNIT_STATIC test -d \$(DESTDIR)$NXT_INCLUDEDIR \ || install -d \$(DESTDIR)$NXT_INCLUDEDIR install -p -m u=rw,go=r src/nxt_unit.h \ + src/nxt_unit_cdefs.h \ src/nxt_unit_field.h \ src/nxt_unit_request.h \ src/nxt_unit_response.h \ @@ -423,6 +424,7 @@ libunit-uninstall: rm -f \$(DESTDIR)$NXT_PKGCONFIGDIR/unit.pc @rmdir -p \$(DESTDIR)$NXT_PKGCONFIGDIR 2>/dev/null || true rm -f \$(DESTDIR)$NXT_INCLUDEDIR/nxt_unit.h \ + \$(DESTDIR)$NXT_INCLUDEDIR/nxt_unit_cdefs.h \ \$(DESTDIR)$NXT_INCLUDEDIR/nxt_unit_field.h \ \$(DESTDIR)$NXT_INCLUDEDIR/nxt_unit_request.h \ \$(DESTDIR)$NXT_INCLUDEDIR/nxt_unit_response.h \ diff --git a/src/nxt_clang.h b/src/nxt_clang.h index 94638346..10ed6687 100644 --- a/src/nxt_clang.h +++ b/src/nxt_clang.h @@ -8,6 +8,9 @@ #define _NXT_CLANG_H_INCLUDED_ +#include + + #define nxt_inline static inline __attribute__((always_inline)) #define nxt_noinline __attribute__((noinline)) #define nxt_cdecl @@ -210,24 +213,12 @@ nxt_popcount(unsigned int x) *(type *) ((u_char *) p + offset) -#define nxt_nitems(x) \ - (sizeof(x) / sizeof((x)[0])) - - /* GCC and Clang use __builtin_abs() instead of libc abs(). */ #define nxt_abs(val) \ abs(val) -#define nxt_max(val1, val2) \ - ((val1 < val2) ? (val2) : (val1)) - - -#define nxt_min(val1, val2) \ - ((val1 > val2) ? (val2) : (val1)) - - #define nxt_bswap32(val) \ ( ((val) >> 24) \ | (((val) & 0x00FF0000) >> 8) \ @@ -252,7 +243,7 @@ nxt_popcount(unsigned int x) #define nxt_length(s) \ - (sizeof(s) - 1) + (sizeof(s) - 1 + nxt_must_be_array(s)) #endif /* _NXT_CLANG_H_INCLUDED_ */ diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 35f9fa55..97e0e9df 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -15,6 +15,7 @@ #include "nxt_auto_config.h" #include "nxt_version.h" #include "nxt_unit_typedefs.h" +#include enum { diff --git a/src/nxt_unit_cdefs.h b/src/nxt_unit_cdefs.h new file mode 100644 index 00000000..48389117 --- /dev/null +++ b/src/nxt_unit_cdefs.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019-2023, Alejandro Colomar + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_UNIT_CDEFS_H_INCLUDED_ +#define _NXT_UNIT_CDEFS_H_INCLUDED_ + + +#include + + +#define nxt_max(a, b) (((a) > (b)) ? (a) : (b)) +#define nxt_min(a, b) (((a) < (b)) ? (a) : (b)) + + +#define nxt_swap(ap, bp) \ + do { \ + __auto_type ap_ = (ap); \ + __auto_type bp_ = (bp); \ + typeof(*ap_) tmp_; \ + \ + _Static_assert(nxt_is_same_typeof(ap_, bp_), ""); \ + \ + tmp_ = *ap_; \ + *ap_ = *bp_; \ + *bp_ = tmp_; \ + } while (0) + + +#define nxt_sizeof_array(a) (sizeof(a) + nxt_must_be_array(a)) +#define nxt_nitems(a) (nxt_sizeof_array(a) / sizeof((a)[0])) +#define nxt_memberof(T, member) ((T){}.member) + +#define nxt_sizeof_incomplete(x) \ + ( \ + sizeof( \ + struct { \ + max_align_t a; \ + typeof(x) inc; \ + } \ + ) \ + - sizeof(max_align_t) \ + ) + +#define nxt_sizeof_fam0(T, fam) \ + (sizeof(nxt_memberof(T, fam[0])) + nxt_must_be_fam(T, fam)) + +#define nxt_sizeof_fam(T, fam, n) \ + (nxt_sizeof_fam0(T, fam) * (n)) + +#define nxt_offsetof_fam(T, fam, n) \ + (offsetof(T, fam) + nxt_sizeof_fam(T, fam, n)) + +#define nxt_sizeof_struct(T, fam, n) \ + nxt_max(sizeof(T), nxt_offsetof_fam(T, fam, n)) + + +#define nxt_is_near_end(T, m) (offsetof(T, m) > (sizeof(T) - _Alignof(T))) +#define nxt_is_zero_sizeof(z) (nxt_sizeof_incomplete(z) == 0) +#define nxt_is_same_type(a, b) __builtin_types_compatible_p(a, b) +#define nxt_is_same_typeof(a, b) nxt_is_same_type(typeof(a), typeof(b)) +#define nxt_is_array(a) (!nxt_is_same_typeof(a, &(a)[0])) + + +#define nxt_must_be(e) \ + ( \ + 0 * (int) sizeof( \ + struct { \ + _Static_assert(e, ""); \ + int ISO_C_forbids_a_struct_with_no_members_; \ + } \ + ) \ + ) + + +#define nxt_must_be_array(a) nxt_must_be(nxt_is_array(a)) +#define nxt_must_be_zero_sizeof(z) nxt_must_be(nxt_is_zero_sizeof(z)) +#define nxt_must_be_near_end(T, m) nxt_must_be(nxt_is_near_end(T, m)) + +#define nxt_must_be_fam(T, fam) \ + (nxt_must_be_array(nxt_memberof(T, fam)) \ + + nxt_must_be_zero_sizeof(nxt_memberof(T, fam)) \ + + nxt_must_be_near_end(T, fam)) + + +#endif /* _NXT_UNIT_CDEFS_H_INCLUDED_ */ -- cgit