if (strcmp(argv[1], "depends") == 0) {
printf("ccan/likely\n");
+ printf("ccan/str\n");
return 0;
}
/* CC0 (Public domain) - see LICENSE file for details */
#include <ccan/take/take.h>
#include <ccan/likely/likely.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const void **takenarr;
+static const char **labelarr;
static size_t max_taken, num_taken;
static size_t allocfail;
static void (*allocfailfn)(const void *p);
-void *take_(const void *p)
+void *take_(const void *p, const char *label)
{
+ /* Overallocate: it's better than risking calloc returning NULL! */
+ if (unlikely(label && !labelarr))
+ labelarr = calloc(max_taken+1, sizeof(*labelarr));
+
if (unlikely(num_taken == max_taken)) {
const void **new;
return (void *)p;
}
takenarr = new;
+ /* Once labelarr is set, we maintain it. */
+ if (labelarr)
+ labelarr = realloc(labelarr,
+ sizeof(*labelarr) * (max_taken+1));
max_taken++;
}
+ if (unlikely(labelarr))
+ labelarr[num_taken] = label;
takenarr[num_taken++] = p;
+
return (void *)p;
}
return find_taken(p) > 0;
}
-bool taken_any(void)
+const char *taken_any(void)
{
- return num_taken != 0;
+ static char pointer_buf[32];
+
+ if (num_taken == 0)
+ return NULL;
+
+ /* We're *allowed* to have some with labels, some without. */
+ if (labelarr) {
+ size_t i;
+ for (i = 0; i < num_taken; i++)
+ if (labelarr[i])
+ return labelarr[i];
+ }
+
+ sprintf(pointer_buf, "%p", takenarr[0]);
+ return pointer_buf;
}
void take_cleanup(void)
max_taken = num_taken = 0;
free(takenarr);
takenarr = NULL;
+ free(labelarr);
+ labelarr = NULL;
}
void take_allocfail(void (*fn)(const void *p))
#define CCAN_TAKE_H
#include "config.h"
#include <stdbool.h>
+#include <ccan/str/str.h>
+
+#ifdef CCAN_TAKE_DEBUG
+#define TAKE_LABEL(p) __FILE__ ":" stringify(__LINE__) ":" stringify(p)
+#else
+#define TAKE_LABEL(p) NULL
+#endif
/**
* take - record a pointer to be consumed by the function its handed to.
* which is extremely useful for chaining functions. It works on
* NULL, for pass-through error handling.
*/
-#define take(p) (take_typeof(p) take_((p)))
+#define take(p) (take_typeof(p) take_((p), TAKE_LABEL(p)))
/**
* taken - check (and un-take) a pointer was passed with take()
/**
* taken_any - are there any taken pointers?
*
- * Mainly useful for debugging take() leaks.
+ * Mainly useful for debugging take() leaks. With CCAN_TAKE_DEBUG, returns
+ * the label where the pointer was passed to take(), otherwise returns
+ * a static char buffer with the pointer value in it. NULL if none are taken.
*
* Example:
* static void cleanup(void)
* assert(!taken_any());
* }
*/
-bool taken_any(void);
+const char *taken_any(void);
/**
* take_cleanup - remove all taken pointers from list.
#define take_typeof(ptr)
#endif
-void *take_(const void *p);
+void *take_(const void *p, const char *label);
#endif /* CCAN_TAKE_H */
--- /dev/null
+#include <stdlib.h>
+#include <stdbool.h>
+
+#define CCAN_TAKE_DEBUG 1
+#include <ccan/take/take.h>
+#include <ccan/take/take.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ const char *p = "hi";
+
+ plan_tests(14);
+
+ /* We can take NULL. */
+ ok1(take(NULL) == NULL);
+ ok1(is_taken(NULL));
+ ok1(strstr(taken_any(), "run-debug.c:16:"));
+ ok1(taken(NULL)); /* Undoes take() */
+ ok1(!is_taken(NULL));
+ ok1(!taken(NULL));
+ ok1(!taken_any());
+
+ /* We can take a real pointer. */
+ ok1(take(p) == p);
+ ok1(is_taken(p));
+ ok1(strends(taken_any(), "run-debug.c:25:p"));
+ ok1(taken(p)); /* Undoes take() */
+ ok1(!is_taken(p));
+ ok1(!taken(p));
+ ok1(!taken_any());
+
+ return exit_status();
+}