Author: David Rheinsberg <david.rheinsberg@gmail.com>
Origin: backport, https://github.com/c-util/c-stdaux/commit/1257244f886a4799a1ed739aa2c632e9eb033b8d
Description: add c_memset()
 The memset(3) function causes UB if its area pointer is NULL, even if
 the area is 0-bytes in length. This is very unfortunate and requires
 unnecessary guards in most callers. We really want to be able to call
 memset(3) with NULL pointers on empty areas to avoid needless branching
 and complexity.
 .
 Provide c_memset() which is exactly like memset(3) for non-NULL areas,
 but a no-op for empty areas.
--- a/subprojects/c-stdaux/src/c-stdaux.h
+++ b/subprojects/c-stdaux/src/c-stdaux.h
@@ -470,6 +470,24 @@
         return _c_likely_(errno > 0) ? errno : ENOTRECOVERABLE;
 }
 
+/**
+ * c_memset() - fill memory region with constant byte
+ * @p:          pointer to memory region, if non-empty
+ * @c:          value to fill with
+ * @n:          size of the memory region in bytes
+ *
+ * This function works like `memset(3)` if @n is non-zero. If @n is zero, this
+ * function is a no-op. Therefore, unlike `memset(3)` it is safe to call this
+ * function with NULL as @p if @n is 0.
+ *
+ * Return: @p is returned.
+ */
+static inline void *c_memset(void *p, int c, size_t n) {
+        if (n > 0)
+                memset(p, c, n);
+        return p;
+}
+
 /*
  * Common Destructors
  *
--- a/subprojects/c-stdaux/src/test-api.c
+++ b/subprojects/c-stdaux/src/test-api.c
@@ -187,6 +187,7 @@
 static void test_api_functions(void) {
         void *fns[] = {
                 (void *)c_errno,
+                (void *)c_memset,
                 (void *)c_free,
                 (void *)c_close,
                 (void *)c_fclose,
--- a/subprojects/c-stdaux/src/test-basic.c
+++ b/subprojects/c-stdaux/src/test-basic.c
@@ -304,6 +304,34 @@
                 errno = 0;
                 c_assert(c_errno() != errno);
         }
+
+        /*
+         * Test c_memset(). Simply verify its most basic behavior, as well as
+         * calling it on empty regions.
+         */
+        {
+                uint64_t v = (uint64_t)-1;
+                size_t n;
+                void *p;
+
+                /* try filling with 0 and 0xff */
+                c_assert(v == (uint64_t)-1);
+                c_memset(&v, 0, sizeof(v));
+                c_assert(v == (uint64_t)0);
+                c_memset(&v, 0xff, sizeof(v));
+                c_assert(v == (uint64_t)-1);
+
+                /*
+                 * Try tricking the optimizer into thinking @p cannot be NULL,
+                 * as normal `memset(3)` would allow.
+                 */
+                p = NULL;
+                n = 0;
+                c_memset(p, 0, n);
+                if (p)
+                        abort();
+                c_assert(p == NULL);
+        }
 }
 
 /*
