diff options
author | Thomas Haller <thaller@redhat.com> | 2023-03-03 13:40:38 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2023-03-03 13:40:38 +0100 |
commit | e94e759f3adb03d1782239f86eb350451e54b60c (patch) | |
tree | c857980a2f17a72a28ef03a9f7719ddf104b7e67 | |
parent | aa6647214655230b8ec514fd40e4b9187814aa8d (diff) | |
parent | a6ead14f3361e2ff8be6d1e3779c56257948fcc0 (diff) | |
download | NetworkManager-e94e759f3adb03d1782239f86eb350451e54b60c.tar.gz |
c-stdaux: re-import git-subtree for 'src/c-stdaux'
git subtree pull --prefix src/c-stdaux git@github.com:c-util/c-stdaux.git main --squash
-rw-r--r-- | src/c-stdaux/NEWS.md | 17 | ||||
-rw-r--r-- | src/c-stdaux/meson.build | 2 | ||||
-rw-r--r-- | src/c-stdaux/src/c-stdaux-generic.h | 273 | ||||
-rw-r--r-- | src/c-stdaux/src/test-api.c | 27 | ||||
-rw-r--r-- | src/c-stdaux/src/test-basic.c | 27 |
5 files changed, 345 insertions, 1 deletions
diff --git a/src/c-stdaux/NEWS.md b/src/c-stdaux/NEWS.md index 0cae9c5671..8e7b3c710a 100644 --- a/src/c-stdaux/NEWS.md +++ b/src/c-stdaux/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/src/c-stdaux/meson.build b/src/c-stdaux/meson.build index e41fd5f3e1..6af822be6f 100644 --- a/src/c-stdaux/meson.build +++ b/src/c-stdaux/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/src/c-stdaux-generic.h b/src/c-stdaux/src/c-stdaux-generic.h index 4848617a79..6f87fd4aca 100644 --- a/src/c-stdaux/src/c-stdaux-generic.h +++ b/src/c-stdaux/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/c-stdaux/src/test-api.c b/src/c-stdaux/src/test-api.c index ca78c602d4..e52d42c29c 100644 --- a/src/c-stdaux/src/test-api.c +++ b/src/c-stdaux/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/c-stdaux/src/test-basic.c b/src/c-stdaux/src/test-basic.c index ffb4bf6086..1d16a82859 100644 --- a/src/c-stdaux/src/test-basic.c +++ b/src/c-stdaux/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 */ |