summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2023-03-03 13:40:38 +0100
committerThomas Haller <thaller@redhat.com>2023-03-03 13:40:38 +0100
commita6ead14f3361e2ff8be6d1e3779c56257948fcc0 (patch)
treebccb4e5ffdb2477732b3918ce2682b61bb6cdeca
parent54c68e129014a5bb0ad7e8c301adce9569fb62c4 (diff)
downloadNetworkManager-a6ead14f3361e2ff8be6d1e3779c56257948fcc0.tar.gz
Squashed 'src/c-stdaux/' changes from eceefe959250..699c20de4e81
699c20de4e81 c-stdaux: workaround warning "-Wunused-value" in c_internal_assume_aligned() 193444c22c09 c-stdaux: workaround compiler error with clang 3.4 and __builtin_assume_aligned() ed5fee49a3ec build: prepare v1.4.0 615c52daed67 Merge pull request #14 from dvdhrm/pr/load 32462ddc2ea5 c-stdaux: add c_load*() helpers 5878375d81ba c-stdaux: add c_assume_aligned() git-subtree-dir: src/c-stdaux git-subtree-split: 699c20de4e81f4b15786cb170340a87f69483f3d
-rw-r--r--NEWS.md17
-rw-r--r--meson.build2
-rw-r--r--src/c-stdaux-generic.h273
-rw-r--r--src/test-api.c27
-rw-r--r--src/test-basic.c27
5 files changed, 345 insertions, 1 deletions
diff --git a/NEWS.md b/NEWS.md
index 0cae9c5671..8e7b3c710a 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,22 @@
# c-stdaux - Auxiliary macros and functions for the C standard library
+## CHANGES WITH 1.4.0:
+
+ * New compiler-builtin c_assume_aligned() allows hinting alignment
+ to the compiler and thus improving code generation. For targets
+ without such builtins, the function will be a no-op.
+
+ * A new set of memory-load operations is added: c_load_*()
+ This includes support for reading unaligned & aligned memory,
+ big-endian & little-endian data, and various standard sizes.
+ The helpers are basically a pointer cast to `uintX_t*` and a
+ dereference operation, but they guarantee that strict aliasing
+ rules, as well as alignment requirements are followed.
+
+ Contributions from: David Rheinsberg, Jan Engelhardt, Tom Gundersen
+
+ - Dußlingen, 2023-01-12
+
## CHANGES WITH 1.3.0:
* Microsoft Windows is now supported as a target platform.
diff --git a/meson.build b/meson.build
index e41fd5f3e1..6af822be6f 100644
--- a/meson.build
+++ b/meson.build
@@ -10,7 +10,7 @@ project(
],
license: 'Apache',
meson_version: '>=0.60.0',
- version: '1.3.0',
+ version: '1.4.0',
)
major = meson.project_version().split('.')[0]
project_description = 'Auxiliary macros and functions for the C standard library'
diff --git a/src/c-stdaux-generic.h b/src/c-stdaux-generic.h
index 4848617a79..6f87fd4aca 100644
--- a/src/c-stdaux-generic.h
+++ b/src/c-stdaux-generic.h
@@ -290,6 +290,27 @@ extern "C" {
/**/
/**
+ * c_assume_aligned() - Hint alignment to compiler
+ * @_ptr: Pointer to provide alignment hint for
+ * @_alignment: Alignment in bytes
+ * @_offset: Misalignment offset
+ *
+ * This hints to the compiler that `_ptr - _offset` is aligned to the alignment
+ * specified in `_alignment`.
+ *
+ * On platforms without support for `__builtin_assume_aligned()` this is a
+ * no-op.
+ *
+ * Return: `_ptr` is returned.
+ */
+#define c_assume_aligned(_ptr, _alignment, _offset) c_internal_assume_aligned((_ptr), (_alignment), (_offset))
+#if (defined(C_COMPILER_GNUC) && __GNUC__ > 5) || (defined(C_COMPILER_CLANG) && __clang_major__ > 3)
+# define c_internal_assume_aligned(_ptr, _alignment, _offset) __builtin_assume_aligned((_ptr), (_alignment), (_offset))
+#else
+# define c_internal_assume_aligned(_ptr, _alignment, _offset) ((void)(_alignment), (void)(_offset), (_ptr))
+#endif
+
+/**
* c_assert() - Runtime assertions
* @_x: Result of an expression
*
@@ -401,6 +422,258 @@ static inline int c_memcmp(const void *s1, const void *s2, size_t n) {
}
/**
+ * DOC: Memory Access
+ *
+ * This section provides helpers to read and write arbitrary memory locations.
+ * They are carefully designed to follow all language restrictions and thus
+ * work with strict-aliasing and alignment rules.
+ *
+ * The C language does not allow aliasing an object with a pointer of an
+ * incompatible type (with few exceptions). Furthermore, memory access must be
+ * aligned. This function uses exceptions in the language to circumvent both
+ * restrictions.
+ *
+ * Note that pointer-offset calculations should avoid exceeding the extents of
+ * the object, even if the object is surrounded by other objects. That is,
+ * `ptr+offset` should point to the same object as `ptr`. Otherwise, pointer
+ * provenance will have to be considered.
+ */
+/**/
+
+/**
+ * c_load_8() - Read a u8 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unsigned 8-bit integer at the offset of the specified memory
+ * location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint8_t c_load_8(const void *memory, size_t offset) {
+ return ((const uint8_t *)memory)[offset];
+}
+
+/**
+ * c_load_16be_unaligned() - Read an unaligned big-endian u16 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unaligned big-endian unsigned 16-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint16_t c_load_16be_unaligned(const void *memory, size_t offset) {
+ const uint8_t *m = (const uint8_t *)memory + offset;
+ return ((uint16_t)m[1] << 0) | ((uint16_t)m[0] << 8);
+}
+
+/**
+ * c_load_16be_aligned() - Read an aligned big-endian u16 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an aligned big-endian unsigned 16-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint16_t c_load_16be_aligned(const void *memory, size_t offset) {
+ const uint8_t *m = c_assume_aligned((const uint8_t *)memory + offset, 2, 0);
+ return ((uint16_t)m[1] << 0) | ((uint16_t)m[0] << 8);
+}
+
+/**
+ * c_load_16le_unaligned() - Read an unaligned little-endian u16 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unaligned little-endian unsigned 16-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint16_t c_load_16le_unaligned(const void *memory, size_t offset) {
+ const uint8_t *m = (const uint8_t *)memory + offset;
+ return ((uint16_t)m[0] << 0) | ((uint16_t)m[1] << 8);
+}
+
+/**
+ * c_load_16le_aligned() - Read an aligned little-endian u16 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an aligned little-endian unsigned 16-bit integer at the offset of
+ * the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint16_t c_load_16le_aligned(const void *memory, size_t offset) {
+ const uint8_t *m = c_assume_aligned((const uint8_t *)memory + offset, 2, 0);
+ return ((uint16_t)m[0] << 0) | ((uint16_t)m[1] << 8);
+}
+
+/**
+ * c_load_32be_unaligned() - Read an unaligned big-endian u32 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unaligned big-endian unsigned 32-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint32_t c_load_32be_unaligned(const void *memory, size_t offset) {
+ const uint8_t *m = (const uint8_t *)memory + offset;
+ return ((uint32_t)m[3] << 0) | ((uint32_t)m[2] << 8) |
+ ((uint32_t)m[1] << 16) | ((uint32_t)m[0] << 24);
+}
+
+/**
+ * c_load_32be_aligned() - Read an aligned big-endian u32 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an aligned big-endian unsigned 32-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint32_t c_load_32be_aligned(const void *memory, size_t offset) {
+ const uint8_t *m = c_assume_aligned((const uint8_t *)memory + offset, 4, 0);
+ return ((uint32_t)m[3] << 0) | ((uint32_t)m[2] << 8) |
+ ((uint32_t)m[1] << 16) | ((uint32_t)m[0] << 24);
+}
+
+/**
+ * c_load_32le_unaligned() - Read an unaligned little-endian u32 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unaligned little-endian unsigned 32-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint32_t c_load_32le_unaligned(const void *memory, size_t offset) {
+ const uint8_t *m = (const uint8_t *)memory + offset;
+ return ((uint32_t)m[0] << 0) | ((uint32_t)m[1] << 8) |
+ ((uint32_t)m[2] << 16) | ((uint32_t)m[3] << 24);
+}
+
+/**
+ * c_load_32le_aligned() - Read an aligned little-endian u32 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an aligned little-endian unsigned 32-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint32_t c_load_32le_aligned(const void *memory, size_t offset) {
+ const uint8_t *m = c_assume_aligned((const uint8_t *)memory + offset, 4, 0);
+ return ((uint32_t)m[0] << 0) | ((uint32_t)m[1] << 8) |
+ ((uint32_t)m[2] << 16) | ((uint32_t)m[3] << 24);
+}
+
+/**
+ * c_load_64be_unaligned() - Read an unaligned big-endian u64 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unaligned big-endian unsigned 64-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint64_t c_load_64be_unaligned(const void *memory, size_t offset) {
+ const uint8_t *m = (const uint8_t *)memory + offset;
+ return ((uint64_t)m[7] << 0) | ((uint64_t)m[6] << 8) |
+ ((uint64_t)m[5] << 16) | ((uint64_t)m[4] << 24) |
+ ((uint64_t)m[3] << 32) | ((uint64_t)m[2] << 40) |
+ ((uint64_t)m[1] << 48) | ((uint64_t)m[0] << 56);
+}
+
+/**
+ * c_load_64be_aligned() - Read an aligned big-endian u64 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an aligned big-endian unsigned 64-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint64_t c_load_64be_aligned(const void *memory, size_t offset) {
+ const uint8_t *m = c_assume_aligned((const uint8_t *)memory + offset, 8, 0);
+ return ((uint64_t)m[7] << 0) | ((uint64_t)m[6] << 8) |
+ ((uint64_t)m[5] << 16) | ((uint64_t)m[4] << 24) |
+ ((uint64_t)m[3] << 32) | ((uint64_t)m[2] << 40) |
+ ((uint64_t)m[1] << 48) | ((uint64_t)m[0] << 56);
+}
+
+/**
+ * c_load_64le_unaligned() - Read an unaligned little-endian u64 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an unaligned little-endian unsigned 64-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint64_t c_load_64le_unaligned(const void *memory, size_t offset) {
+ const uint8_t *m = (const uint8_t *)memory + offset;
+ return ((uint64_t)m[0] << 0) | ((uint64_t)m[1] << 8) |
+ ((uint64_t)m[2] << 16) | ((uint64_t)m[3] << 24) |
+ ((uint64_t)m[4] << 32) | ((uint64_t)m[5] << 40) |
+ ((uint64_t)m[6] << 48) | ((uint64_t)m[7] << 56);
+}
+
+/**
+ * c_load_64le_aligned() - Read an aligned little-endian u64 from memory
+ * @memory: Memory location to operate on
+ * @offset: Offset in bytes from the pointed memory location
+ *
+ * This reads an aligned little-endian unsigned 64-bit integer at the offset
+ * of the specified memory location.
+ *
+ * Return: The read value is returned.
+ */
+static inline uint64_t c_load_64le_aligned(const void *memory, size_t offset) {
+ const uint8_t *m = c_assume_aligned((const uint8_t *)memory + offset, 8, 0);
+ return ((uint64_t)m[0] << 0) | ((uint64_t)m[1] << 8) |
+ ((uint64_t)m[2] << 16) | ((uint64_t)m[3] << 24) |
+ ((uint64_t)m[4] << 32) | ((uint64_t)m[5] << 40) |
+ ((uint64_t)m[6] << 48) | ((uint64_t)m[7] << 56);
+}
+
+/**
+ * c_load() - Read from memory
+ * @_type: Datatype to read
+ * @_endian: Endianness
+ * @_aligned: Aligned or unaligned access
+ * @_memory: Memory location to operate on
+ * @_offset: Offset in bytes from the pointed memory location
+ *
+ * This reads a value of the same size as `_type` at the offset of the
+ * specified memory location. `_endian` must be either `be` or `le`, `_aligned`
+ * must be either `aligned` or `unaligned`.
+ *
+ * This is a generic macro that maps to the respective `c_load_*()` function.
+ *
+ * Return: The read value is returned.
+ */
+#define c_load(_type, _endian, _aligned, _memory, _offset) \
+ (_Generic((_type){ 0 }, \
+ uint16_t: c_load_16 ## _endian ## _ ## _aligned ((_memory), (_offset)), \
+ uint32_t: c_load_32 ## _endian ## _ ## _aligned ((_memory), (_offset)), \
+ uint64_t: c_load_64 ## _endian ## _ ## _aligned ((_memory), (_offset)) \
+ ))
+
+/**
* DOC: Generic Destructors
*
* A set of destructors is provided which extends standard library destructors
diff --git a/src/test-api.c b/src/test-api.c
index ca78c602d4..e52d42c29c 100644
--- a/src/test-api.c
+++ b/src/test-api.c
@@ -98,11 +98,25 @@ static void test_api_generic(void) {
int C_VAR = 0; c_assert(!C_VAR); /* must be on the same line */
}
+ /* c_assume_aligned */
+ {
+ _Alignas(16) uint8_t data[8] = { 0 };
+
+ c_assert(c_assume_aligned(data, 16, 0));
+ }
+
/* c_assert */
{
c_assert(true);
}
+ /* c_load */
+ {
+ uint64_t data[128] = { 0 };
+
+ c_assert(c_load(uint64_t, le, aligned, data, 0) == 0);
+ }
+
/* C_DEFINE_CLEANUP / C_DEFINE_DIRECT_CLEANUP */
{
int v = 0;
@@ -118,6 +132,19 @@ static void test_api_generic(void) {
(void *)c_memset,
(void *)c_memzero,
(void *)c_memcpy,
+ (void *)c_load_8,
+ (void *)c_load_16be_unaligned,
+ (void *)c_load_16be_aligned,
+ (void *)c_load_16le_unaligned,
+ (void *)c_load_16le_aligned,
+ (void *)c_load_32be_unaligned,
+ (void *)c_load_32be_aligned,
+ (void *)c_load_32le_unaligned,
+ (void *)c_load_32le_aligned,
+ (void *)c_load_64be_unaligned,
+ (void *)c_load_64be_aligned,
+ (void *)c_load_64le_unaligned,
+ (void *)c_load_64le_aligned,
(void *)c_free,
(void *)c_fclose,
(void *)c_freep,
diff --git a/src/test-basic.c b/src/test-basic.c
index ffb4bf6086..1d16a82859 100644
--- a/src/test-basic.c
+++ b/src/test-basic.c
@@ -321,6 +321,33 @@ static void test_basic_generic(int non_constant_expr) {
c_assert(c_memcmp(&v1, &v2, 0) == 0);
c_assert(c_memcmp(&v1, &v2, 8) != 0);
}
+
+ /*
+ * Test c_load*() and its mapping to c_load_*() functions.
+ */
+ {
+ _Alignas(8) uint8_t data[16] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ };
+
+ c_assert(c_load_8(data, 7) == 0);
+ c_assert(c_load_8(data, 8) == 1);
+ c_assert(c_load(uint16_t, be, unaligned, data, 7) == UINT16_C(0x0001));
+ c_assert(c_load(uint16_t, be, aligned, data, 8) == UINT16_C(0x0102));
+ c_assert(c_load(uint16_t, le, unaligned, data, 7) == UINT16_C(0x0100));
+ c_assert(c_load(uint16_t, le, aligned, data, 8) == UINT16_C(0x0201));
+ c_assert(c_load(uint32_t, be, unaligned, data, 7) == UINT32_C(0x00010203));
+ c_assert(c_load(uint32_t, be, aligned, data, 8) == UINT32_C(0x01020304));
+ c_assert(c_load(uint32_t, le, unaligned, data, 7) == UINT32_C(0x03020100));
+ c_assert(c_load(uint32_t, le, aligned, data, 8) == UINT32_C(0x04030201));
+ c_assert(c_load(uint64_t, be, unaligned, data, 7) == UINT64_C(0x0001020304050607));
+ c_assert(c_load(uint64_t, be, aligned, data, 8) == UINT64_C(0x0102030405060708));
+ c_assert(c_load(uint64_t, le, unaligned, data, 7) == UINT64_C(0x0706050403020100));
+ c_assert(c_load(uint64_t, le, aligned, data, 8) == UINT64_C(0x0807060504030201));
+ }
}
#else /* C_MODULE_GENERIC */