structeq: fix case where we mark padding as unknown.
[ccan] / ccan / structeq / structeq.h
index 3af20c53938ce73276da8afa7dfa441d9f3766ed..81799539c51e45f9c2f1b9c23a1b3949b47564c1 100644 (file)
@@ -1,17 +1,46 @@
-/* CC0 (Public domain) - see LICENSE file for details */
+/* MIT (BSD) license - see LICENSE file for details */
 #ifndef CCAN_STRUCTEQ_H
 #define CCAN_STRUCTEQ_H
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/cppmagic/cppmagic.h>
 #include <string.h>
+#include <stdbool.h>
 
 /**
- * structeq - are two structures bitwise equal (including padding!)
- * @a: a pointer to a structure
- * @b: a pointer to a structure of the same type.
+ * STRUCTEQ_DEF - define an ..._eq function to compare two structures.
+ * @sname: name of the structure, and function (<sname>_eq) to define.
+ * @padbytes: number of bytes of expected padding, or negative "max".
+ * @...: name of every member of the structure.
  *
- * If you *know* a structure has no padding, you can memcmp them.  At
- * least this way, the compiler will issue a warning if the structs are
- * different types!
+ * This generates a single memcmp() call in the common case where the
+ * structure contains no padding.  Since it can't tell the difference between
+ * padding and a missing member, @padbytes can be used to assert that
+ * there isn't any, or how many we expect.  A negative number means
+ * "up to or equal to that amount of padding", as padding can be
+ * platform dependent.
  */
-#define structeq(a, b) \
-       (memcmp((a), (b), sizeof(*(a)) + 0 * sizeof((a) == (b))) == 0)
+#define STRUCTEQ_DEF(sname, padbytes, ...)                             \
+static inline bool CPPMAGIC_GLUE2(sname, _eq)(const struct sname *_a, \
+                                             const struct sname *_b) \
+{                                                                      \
+       BUILD_ASSERT(((padbytes) < 0 &&                                 \
+                     CPPMAGIC_JOIN(+, CPPMAGIC_MAP(STRUCTEQ_MEMBER_SIZE_, \
+                                                   __VA_ARGS__))       \
+                     - (padbytes) >= sizeof(*_a))                      \
+                    || CPPMAGIC_JOIN(+, CPPMAGIC_MAP(STRUCTEQ_MEMBER_SIZE_, \
+                                                     __VA_ARGS__))     \
+                    + (padbytes) == sizeof(*_a));                      \
+       if (CPPMAGIC_JOIN(+, CPPMAGIC_MAP(STRUCTEQ_MEMBER_SIZE_, __VA_ARGS__)) \
+           == sizeof(*_a))                                             \
+               return memcmp(_a, _b, sizeof(*_a)) == 0;                \
+       else                                                            \
+               return CPPMAGIC_JOIN(&&,                                \
+                                    CPPMAGIC_MAP(STRUCTEQ_MEMBER_CMP_, \
+                                                 __VA_ARGS__));        \
+}
+
+/* Helpers */
+#define STRUCTEQ_MEMBER_SIZE_(m) sizeof((_a)->m)
+#define STRUCTEQ_MEMBER_CMP_(m) memcmp(&_a->m, &_b->m, sizeof(_a->m)) == 0
+
 #endif /* CCAN_STRUCTEQ_H */