From: David Gibson Date: Tue, 26 May 2015 12:56:50 +0000 (+1000) Subject: ptrint: Module for encoding integers into void * pointers X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=53079b9c300f1bdf1cd5695a13f3ccbe2de86ee9 ptrint: Module for encoding integers into void * pointers 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 --- diff --git a/ccan/ptrint/LICENSE b/ccan/ptrint/LICENSE new file mode 120000 index 00000000..b7951dab --- /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 00000000..8135d1ee --- /dev/null +++ b/ccan/ptrint/_info @@ -0,0 +1,59 @@ +#include "config.h" +#include +#include + +/** + * 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 + * + * 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 + */ +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 00000000..992e4b18 --- /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 + +#include + +/* + * 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 00000000..7d6f934b --- /dev/null +++ b/ccan/ptrint/test/run.c @@ -0,0 +1,29 @@ +#include + +#include + +#include +#include + +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(); +}