[ccan] [PATCHv2] ptrint: Module for encoding integers into void * pointers

David Gibson david at gibson.dropbear.id.au
Tue May 26 22:57:47 AEST 2015


For callbacks which need a void * context pointer in the general case,
there are often simpler cases where an integer would suffice.  These are
often handled by casting the integer into a pointer, rather than having
to allocate that integer somewhere.

This adds a module with some helpers for this.  It has some advantages over
direct casts:
  * It uses pointer arithmetic against NULL instead of casts which should
    make it more portable, even to weird platforms with odd representations
    for NULL.  I don't know the C standard well enough to know if it's
    totally portable though.
  * In particular it means that the truth value of the pointer
    representation matches that of the encoded integer.
  * The conversion functions are inlines providing more type safety than
    raw casts.
  * It uses a ptrint_t * type which can be used to mark such pointer
    encoded integers.  ptrint_t is a deliberately incomplete type so such
    pointers can never be dereferenced.

Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
---
 ccan/ptrint/LICENSE    |  1 +
 ccan/ptrint/_info      | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
 ccan/ptrint/ptrint.h   | 34 +++++++++++++++++++++++++++++
 ccan/ptrint/test/run.c | 29 +++++++++++++++++++++++++
 4 files changed, 123 insertions(+)
 create mode 120000 ccan/ptrint/LICENSE
 create mode 100644 ccan/ptrint/_info
 create mode 100644 ccan/ptrint/ptrint.h
 create mode 100644 ccan/ptrint/test/run.c

diff --git a/ccan/ptrint/LICENSE b/ccan/ptrint/LICENSE
new file mode 120000
index 0000000..b7951da
--- /dev/null
+++ b/ccan/ptrint/LICENSE
@@ -0,0 +1 @@
+../../licenses/CC0
\ No newline at end of file
diff --git a/ccan/ptrint/_info b/ccan/ptrint/_info
new file mode 100644
index 0000000..8135d1e
--- /dev/null
+++ b/ccan/ptrint/_info
@@ -0,0 +1,59 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * ptrint - Encoding integers in pointer values
+ *
+ * Library (standard or ccan) functions which take user supplied
+ * callbacks usually have the callback supplied with a void * context
+ * pointer.  For simple cases, it's sometimes sufficient to pass a
+ * simple integer cast into a void *, rather than having to allocate a
+ * context structure.  This module provides some helper macros to do
+ * this relatively safely and portably.
+ *
+ * The key characteristics of these functions are:
+ *	ptr2int(int2ptr(val)) == val
+ * and
+ *      !int2ptr(val) == !val
+ * (i.e. the transformation preserves truth value).
+ *
+ * Example:
+ *	#include <ccan/ptrint/ptrint.h>
+ *
+ *	static void callback(void *opaque)
+ *	{
+ *		int val = ptr2int(opaque);
+ *		printf("Value is %d\n", val);
+ *	}
+ *
+ *	void (*cb)(void *opaque) = callback;
+ *
+ *	int main(int argc, char *argv[])
+ *	{
+ *		int val = 17;
+ *
+ *		(*cb)(int2ptr(val));
+ *		exit(0);
+ *	}
+ *
+ * License: CC0 (Public domain)
+ * Author: David Gibson <david at gibson.dropbear.id.au>
+ */
+int main(int argc, char *argv[])
+{
+	/* Expect exactly one argument */
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		printf("ccan/build_assert\n");
+		return 0;
+	}
+	if (strcmp(argv[1], "testdepends") == 0) {
+		printf("ccan/array_size\n");
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/ccan/ptrint/ptrint.h b/ccan/ptrint/ptrint.h
new file mode 100644
index 0000000..992e4b1
--- /dev/null
+++ b/ccan/ptrint/ptrint.h
@@ -0,0 +1,34 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#ifndef CCAN_PTRINT_H
+#define CCAN_PTRINT_H
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include <ccan/build_assert/build_assert.h>
+
+/*
+ * This is a deliberately incomplete type, because it should never be
+ * dereferenced - instead it marks pointer values which are actually
+ * encoding integers
+ */
+typedef struct ptrint ptrint_t;
+
+static inline ptrdiff_t ptr2int(const ptrint_t *p)
+{
+	/*
+	 * ptrdiff_t is the right size by definition, but to avoid
+	 * surprises we want a warning if the user can't fit at least
+	 * a regular int in there
+	 */
+	BUILD_ASSERT(sizeof(int) <= sizeof(ptrdiff_t));
+	return (const char *)p - (const char *)NULL;
+}
+
+static inline ptrint_t *int2ptr(ptrdiff_t i)
+{
+	return (ptrint_t *)((char *)NULL + i);
+}
+
+#endif /* CCAN_PTRINT_H */
diff --git a/ccan/ptrint/test/run.c b/ccan/ptrint/test/run.c
new file mode 100644
index 0000000..7d6f934
--- /dev/null
+++ b/ccan/ptrint/test/run.c
@@ -0,0 +1,29 @@
+#include <limits.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <ccan/ptrint/ptrint.h>
+#include <ccan/tap/tap.h>
+
+static ptrdiff_t testvals[] = {
+	-INT_MAX, -1, 0, 1, 2, 17, INT_MAX,
+};
+
+int main(void)
+{
+	int i;
+
+	/* This is how many tests you plan to run */
+	plan_tests(2 * ARRAY_SIZE(testvals));
+
+	for (i = 0; i < ARRAY_SIZE(testvals); i++) {
+		ptrdiff_t val = testvals[i];
+		void *ptr = int2ptr(val);
+
+		ok1(ptr2int(ptr) == val);
+		ok1(!val == !ptr);
+	}
+
+	/* This exits depending on whether all tests passed */
+	return exit_status();
+}
-- 
2.1.0



More information about the ccan mailing list