take: add labels when CCAN_TAKE_DEBUG set, return in taken_any().
[ccan] / ccan / take / take.c
1 /* CC0 (Public domain) - see LICENSE file for details */
2 #include <ccan/take/take.h>
3 #include <ccan/likely/likely.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 static const void **takenarr;
9 static const char **labelarr;
10 static size_t max_taken, num_taken;
11 static size_t allocfail;
12 static void (*allocfailfn)(const void *p);
13
14 void *take_(const void *p, const char *label)
15 {
16         /* Overallocate: it's better than risking calloc returning NULL! */
17         if (unlikely(label && !labelarr))
18                 labelarr = calloc(max_taken+1, sizeof(*labelarr));
19
20         if (unlikely(num_taken == max_taken)) {
21                 const void **new;
22
23                 new = realloc(takenarr, sizeof(*takenarr) * (max_taken+1));
24                 if (unlikely(!new)) {
25                         if (allocfailfn) {
26                                 allocfail++;
27                                 allocfailfn(p);
28                                 return NULL;
29                         }
30                         /* Otherwise we leak p. */
31                         return (void *)p;
32                 }
33                 takenarr = new;
34                 /* Once labelarr is set, we maintain it. */
35                 if (labelarr)
36                         labelarr = realloc(labelarr,
37                                            sizeof(*labelarr) * (max_taken+1));
38                 max_taken++;
39         }
40         if (unlikely(labelarr))
41                 labelarr[num_taken] = label;
42         takenarr[num_taken++] = p;
43
44         return (void *)p;
45 }
46
47 static size_t find_taken(const void *p)
48 {
49         size_t i;
50
51         for (i = 0; i < num_taken; i++) {
52                 if (takenarr[i] == p)
53                         return i+1;
54         }
55         return 0;
56 }
57
58 bool taken(const void *p)
59 {
60         size_t i;
61
62         if (!p && unlikely(allocfail)) {
63                 allocfail--;
64                 return true;
65         }
66
67         i = find_taken(p);
68         if (!i)
69                 return false;
70
71         memmove(&takenarr[i-1], &takenarr[i],
72                 (--num_taken - (i - 1))*sizeof(takenarr[0]));
73         return true;
74 }
75
76 bool is_taken(const void *p)
77 {
78         if (!p && unlikely(allocfail))
79                 return true;
80
81         return find_taken(p) > 0;
82 }
83
84 const char *taken_any(void)
85 {
86         static char pointer_buf[32];
87
88         if (num_taken == 0)
89                 return NULL;
90
91         /* We're *allowed* to have some with labels, some without. */
92         if (labelarr) {
93                 size_t i;
94                 for (i = 0; i < num_taken; i++)
95                         if (labelarr[i])
96                                 return labelarr[i];
97         }
98
99         sprintf(pointer_buf, "%p", takenarr[0]);
100         return pointer_buf;
101 }
102
103 void take_cleanup(void)
104 {
105         max_taken = num_taken = 0;
106         free(takenarr);
107         takenarr = NULL;
108         free(labelarr);
109         labelarr = NULL;
110 }
111
112 void take_allocfail(void (*fn)(const void *p))
113 {
114         allocfailfn = fn;
115 }