foreach: fix case where iterators are not on the stack.
[ccan] / ccan / foreach / foreach.c
1 #include <ccan/foreach/foreach.h>
2 #if !HAVE_COMPOUND_LITERALS || !HAVE_FOR_LOOP_DECLARATION
3 #include <ccan/list/list.h>
4 #include <stdint.h>
5 #include <stdlib.h>
6 #include <stdarg.h>
7
8 /* This list is normally very short. */
9 static LIST_HEAD(iters);
10
11 struct iter_info {
12         struct list_node list;
13         const void *index;
14         unsigned int i, num;
15         bool onstack;
16 };
17
18 /* Is pointer still downstack from some other onstack var? */
19 static bool on_stack(const void *ptr, const void *onstack)
20 {
21 #if HAVE_STACK_GROWS_UPWARDS
22         return ptr < onstack;
23 #else
24         return ptr > onstack;
25 #endif
26 }
27
28 static void free_old_iters(const void *index)
29 {
30         struct iter_info *i, *next;
31
32         list_for_each_safe(&iters, i, next, list) {
33                 /* If we're re-using an index, free the old one.
34                  * Otherwise, discard if it's passed off stack. */
35                 if (i->index == index
36                     || (i->onstack && !on_stack(i->index, &i))) {
37                         list_del(&i->list);
38                         free(i);
39                 }
40         }
41 }
42
43 static struct iter_info *find_iter(const void *index)
44 {
45         struct iter_info *i;
46
47         list_for_each(&iters, i, list) {
48                 if (i->index == index)
49                         return i;
50         }
51         abort();
52 }
53
54 static struct iter_info *new_iter(const void *index)
55 {
56         struct iter_info *info = malloc(sizeof *info);
57         info->index = index;
58         info->i = info->num = 0;
59         info->onstack = on_stack(index, &info);
60         list_add(&iters, &info->list);
61         return info;
62 };
63
64 #if HAVE_COMPOUND_LITERALS
65 void _foreach_iter_init(const void *i)
66 {
67         free_old_iters(i);
68         new_iter(i);
69 }
70
71 unsigned int _foreach_iter(const void *i)
72 {
73         struct iter_info *info = find_iter(i);
74         return info->i;
75 }
76
77 unsigned int _foreach_iter_inc(const void *i)
78 {
79         struct iter_info *info = find_iter(i);
80         return ++info->i;
81 }
82 #else /* Don't have compound literals... */
83 int _foreach_term = 0x42430199;
84
85 /* We count values at beginning, and every time around the loop.  We change
86  * the terminator each time, so we don't get fooled in case it really appears
87  * in the list. */
88 static unsigned int count_vals(struct iter_info *info, va_list *ap)
89 {
90         unsigned int i;
91         int val = 0;
92
93         for (i = 0; i < info->num || val != _foreach_term; i++) {
94                 val = va_arg(*ap, int);
95         }
96         _foreach_term++;
97         return i;
98 }
99
100 int _foreach_intval_init(const void *i, int val, ...)
101 {
102         va_list ap;
103         struct iter_info *info;
104
105         free_old_iters(i);
106         info = new_iter(i);
107
108         va_start(ap, val);
109         info->num = count_vals(info, &ap);
110         va_end(ap);
111
112         return val;
113 }
114
115 bool _foreach_intval_done(const void *i)
116 {
117         struct iter_info *info = find_iter(i);
118         return info->i == info->num;
119 }
120         
121 int _foreach_intval_next(const void *i, int val, ...)
122 {
123         struct iter_info *info = find_iter(i);
124         va_list ap;
125         unsigned int num;
126
127         va_start(ap, val);
128         info->num = count_vals(info, &ap);
129         va_end(ap);
130
131         info->i++;
132         assert(info->i <= info->num);
133         if (info->i == info->num)
134                 return 0;
135
136         va_start(ap, val);
137         for (num = 0; num < info->i; num++)
138                 val = va_arg(ap, int);
139
140         va_end(ap);
141         return val;
142 }
143
144 void *_foreach_ptrval_init(const void *i, const void *val, ...)
145 {
146         struct iter_info *info;
147
148         free_old_iters(i);
149         info = new_iter(i);
150
151         return (void *)val;
152 }
153
154 void *_foreach_ptrval_next(const void *i, const void *val, ...)
155 {
156         struct iter_info *info = find_iter(i);
157         va_list ap;
158         unsigned int num;
159
160         info->i++;
161         va_start(ap, val);
162         for (num = 0; num < info->i; num++)
163                 val = va_arg(ap, void *);
164         va_end(ap);
165         return (void *)val;
166 }
167 #endif /* !HAVE_COMPOUND_LITERALS */
168 #endif /* !HAVE_COMPOUND_LITERALS || !HAVE_FOR_LOOP_DECLARATION */