1 #include <nchan_module.h>
2 #include "nchan_rbtree.h"
3 #include <assert.h>
4 
5 //#define DEBUG_LEVEL NGX_LOG_WARN
6 #define DEBUG_LEVEL NGX_LOG_DEBUG
7 #define DBG(fmt, arg...) ngx_log_error(DEBUG_LEVEL, ngx_cycle->log, 0, "RBTREE:" fmt, ##arg)
8 #define ERR(fmt, arg...) ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "RBTREE:" fmt, ##arg)
9 
rbtree_hash_crc32(void * str)10 static uint32_t rbtree_hash_crc32(void *str) {
11   return ngx_crc32_short(((ngx_str_t *)str)->data, ((ngx_str_t *)str)->len);
12 }
rbtree_compare_str(void * id1,void * id2)13 static ngx_int_t rbtree_compare_str(void *id1, void *id2) {
14   return ngx_memn2cmp(((ngx_str_t *)id1)->data, ((ngx_str_t *)id2)->data, ((ngx_str_t *)id1)->len, ((ngx_str_t *)id2)->len);
15 }
16 
17 /*
18 static ngx_int_t rbtree_validate_node(rbtree_seed_t *seed, ngx_rbtree_node_t *node) {
19   if (node == &seed->sentinel) {
20     return 1;
21   }
22 
23   ngx_int_t    i, max, valid;
24   ngx_rbtree_node_t *cur;
25   valid = 0;
26   max = sizeof(seed->actives)/sizeof(cur);
27 
28   for(i=0; i<max; i++){
29     if(seed->actives[i] == node) {
30       valid = 1;
31       break;
32     }
33   }
34   return valid;
35 
36 }
37 */
38 
39 /*
40 static ngx_int_t rbtree_validate_nodes_reachable(rbtree_seed_t *seed) {
41   ngx_int_t    i, max;
42   ngx_rbtree_node_t *cur, *match;
43   max = sizeof(seed->actives)/sizeof(cur);
44 
45   for(i=0; i<max; i++){
46     cur = seed->actives[i];
47     if(cur != NULL) {
48       match = rbtree_find_node(seed, seed->id(rbtree_data_from_node(cur)));
49       assert(match == cur);
50     }
51   }
52   return 1;
53 }
54 */
55 
rbtree_find_node_generic(rbtree_seed_t * seed,void * id,uint32_t hash,ngx_rbtree_node_t ** last_parent,ngx_int_t * last_compare)56 static ngx_rbtree_node_t * rbtree_find_node_generic(rbtree_seed_t *seed, void *id, uint32_t hash, ngx_rbtree_node_t **last_parent, ngx_int_t *last_compare) {
57   ngx_rbtree_node_t              *root = seed->tree.root;
58   ngx_rbtree_node_t              *node = root;
59   ngx_rbtree_node_t              *sentinel = seed->tree.sentinel;
60   ngx_int_t                       rc;
61 
62   while (node != sentinel) {
63     if (hash < node->key) {
64       node = node->left;
65       continue;
66     }
67     if (hash > node->key) {
68       node = node->right;
69       continue;
70     }
71 
72     /* hash == node->key */
73     rc = seed->compare(id, seed->id(rbtree_data_from_node(node)));
74     if (rc == 0) {
75       return node;
76     }
77     node = (rc < 0) ? node->left : node->right;
78   }
79   /* not found */
80 
81   return NULL;
82 }
83 
rbtree_find_node(rbtree_seed_t * seed,void * id)84 ngx_rbtree_node_t *rbtree_find_node(rbtree_seed_t *seed, void *id) {
85   ngx_rbtree_node_t *found;
86   found = rbtree_find_node_generic(seed, id, seed->hash(id), NULL, NULL);
87   if(found) {
88     DBG("found node %p", found);
89   }
90   else {
91     DBG("node not found");
92   }
93   return found;
94 }
95 
rbtree_insert_generic(ngx_rbtree_node_t * temp,ngx_rbtree_node_t * node,ngx_rbtree_node_t * sentinel)96 static void rbtree_insert_generic(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) {
97   ngx_int_t         offset = offsetof(rbtree_seed_t, sentinel);
98   rbtree_seed_t    *seed = (rbtree_seed_t *)((char *)sentinel - offset);
99   ngx_int_t         rc;
100   void             *id = seed->id(rbtree_data_from_node(node));
101 
102   ngx_rbtree_node_t  **p;
103 
104   for ( ;; ) {
105 
106     if (node->key != temp->key) {
107       p = (node->key < temp->key) ? &temp->left : &temp->right;
108     }
109     else {
110       rc = seed->compare(id, seed->id(rbtree_data_from_node(temp)));
111       p = (rc < 0) ? &temp->left : &temp->right;
112     }
113 
114     if (*p == sentinel) {
115       break;
116     }
117 
118     temp = *p;
119   }
120 
121   *p = node;
122   node->parent = temp;
123   node->left = sentinel;
124   node->right = sentinel;
125   ngx_rbt_red(node);
126 }
127 
rbtree_create_node(rbtree_seed_t * seed,size_t data)128 ngx_rbtree_node_t *rbtree_create_node(rbtree_seed_t *seed, size_t data) {
129   ngx_rbtree_node_t *node = ngx_alloc(sizeof(ngx_rbtree_node_t) + data, ngx_cycle->log);
130   if(node) {
131     node->left = NULL;
132     node->right = NULL;
133     node->parent = NULL;
134     seed->allocd_nodes++;
135   }
136   DBG("created node %p", node);
137   return node;
138 }
139 
rbtree_destroy_node(rbtree_seed_t * seed,ngx_rbtree_node_t * node)140 ngx_int_t rbtree_destroy_node(rbtree_seed_t *seed, ngx_rbtree_node_t *node) {
141   seed->allocd_nodes--;
142 #if NCHAN_RBTREE_DBG
143   ngx_memset(node, 0x67, sizeof(*node));
144 #endif
145   DBG("Destroyed node %p", node);
146   ngx_free(node);
147 
148   return NGX_OK;
149 }
150 
rbtree_insert_node(rbtree_seed_t * seed,ngx_rbtree_node_t * node)151 ngx_int_t rbtree_insert_node(rbtree_seed_t *seed, ngx_rbtree_node_t *node) {
152   void  *id = seed->id(rbtree_data_from_node(node));
153   node->key = seed->hash(id);
154   ngx_rbtree_insert(&seed->tree, node);
155   seed->active_nodes++;
156 
157 #if NCHAN_RBTREE_DBG
158 
159   //assert(rbtree_find_node(seed, seed->id(rbtree_data_from_node(node))) == node);
160 
161   //super-heavy debugging
162   /*
163   ngx_int_t    i, max, inserted = 0;
164   ngx_rbtree_node_t *cur;
165   max = sizeof(seed->actives)/sizeof(cur);
166 
167   assert(seed->active_nodes < max);
168 
169   for(i=0; i<max; i++){
170     if(seed->actives[i] == NULL) {
171       seed->actives[i]=node;
172       inserted = 1;
173       break;
174     }
175   }
176   assert(inserted);
177   */
178 
179 #endif
180   DBG("inserted node %p", node);
181   return NGX_OK;
182 }
183 
rbtree_remove_node(rbtree_seed_t * seed,ngx_rbtree_node_t * node)184 ngx_int_t rbtree_remove_node(rbtree_seed_t *seed, ngx_rbtree_node_t *node) {
185 
186   ngx_rbtree_delete(&seed->tree, node);
187   DBG("Removed node %p", node);
188   seed->active_nodes--;
189 
190 #if NCHAN_RBTREE_DBG
191   //assert(rbtree_find_node(seed, seed->id(rbtree_data_from_node(node))) == NULL);
192   ngx_memset(node, 0x65, sizeof(*node));
193 
194 
195   //super-heavy debugging
196   /*
197   ngx_int_t    i, max, removed = 0;
198   ngx_rbtree_node_t *cur;
199   max = sizeof(seed->actives)/sizeof(cur);
200 
201   assert(seed->active_nodes < max);
202 
203   for(i=0; i<max; i++){
204     if(seed->actives[i] == node) {
205       seed->actives[i]=NULL;
206       removed = 1;
207       break;
208     }
209   }
210   assert(removed);
211 
212   */
213 
214 #endif
215   return NGX_OK;
216 }
217 
rbtree_walk_real(rbtree_seed_t * seed,ngx_rbtree_node_t * node,ngx_rbtree_node_t * sentinel,rbtree_walk_callback_pt callback,void * data)218 static ngx_inline void rbtree_walk_real(rbtree_seed_t *seed, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, rbtree_walk_callback_pt callback, void *data) {
219   ngx_rbtree_node_t  *left, *right;
220   if(node == sentinel || node == NULL) {
221     return;
222   }
223   left = node->left;
224   right = node->right;
225   rbtree_walk_real(seed, left, sentinel, callback, data);
226   rbtree_walk_real(seed, right, sentinel, callback, data);
227   callback(seed, rbtree_data_from_node(node), data);
228 }
229 
rbtree_walk_ordered_incr_real(rbtree_seed_t * seed,ngx_rbtree_node_t * node,ngx_rbtree_node_t * sentinel,rbtree_walk_callback_pt callback,void * data)230 static ngx_inline void rbtree_walk_ordered_incr_real(rbtree_seed_t *seed, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, rbtree_walk_callback_pt callback, void *data) {
231   ngx_rbtree_node_t  *left, *right;
232   if(node == sentinel || node == NULL) {
233     return;
234   }
235   left = node->left;
236   right = node->right;
237   rbtree_walk_real(seed, left, sentinel, callback, data);
238   callback(seed, rbtree_data_from_node(node), data);
239   rbtree_walk_real(seed, right, sentinel, callback, data);
240 }
241 
rbtree_walk_ordered_decr_real(rbtree_seed_t * seed,ngx_rbtree_node_t * node,ngx_rbtree_node_t * sentinel,rbtree_walk_callback_pt callback,void * data)242 static ngx_inline void rbtree_walk_ordered_decr_real(rbtree_seed_t *seed, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, rbtree_walk_callback_pt callback, void *data) {
243   ngx_rbtree_node_t  *left, *right;
244   if(node == sentinel || node == NULL) {
245     return;
246   }
247   left = node->left;
248   right = node->right;
249   rbtree_walk_real(seed, right, sentinel, callback, data);
250   callback(seed, rbtree_data_from_node(node), data);
251   rbtree_walk_real(seed, left, sentinel, callback, data);
252 }
253 
rbtree_walk(rbtree_seed_t * seed,rbtree_walk_callback_pt callback,void * data)254 ngx_int_t rbtree_walk(rbtree_seed_t *seed, rbtree_walk_callback_pt callback, void *data) {
255   rbtree_walk_real(seed, seed->tree.root, seed->tree.sentinel, callback, data);
256   return NGX_OK;
257 }
258 
rbtree_walk_incr(rbtree_seed_t * seed,rbtree_walk_callback_pt callback,void * data)259 ngx_int_t rbtree_walk_incr(rbtree_seed_t *seed, rbtree_walk_callback_pt callback, void *data) {
260   rbtree_walk_ordered_incr_real(seed, seed->tree.root, seed->tree.sentinel, callback, data);
261   return NGX_OK;
262 }
rbtree_walk_decr(rbtree_seed_t * seed,rbtree_walk_callback_pt callback,void * data)263 ngx_int_t rbtree_walk_decr(rbtree_seed_t *seed, rbtree_walk_callback_pt callback, void *data) {
264   rbtree_walk_ordered_decr_real(seed, seed->tree.root, seed->tree.sentinel, callback, data);
265   return NGX_OK;
266 }
267 
268 typedef struct {
269   void                **els;
270   int                 (*include)(void *);
271   int                   n;
272 } rbtree_walk_writesafe_data_t;
273 
rbtree_walk_writesafe_callback(rbtree_seed_t * seed,void * node_data,rbtree_walk_writesafe_data_t * d)274 static ngx_int_t rbtree_walk_writesafe_callback(rbtree_seed_t *seed, void *node_data, rbtree_walk_writesafe_data_t *d) {
275   if(d->include(node_data)) {
276     d->els[d->n++]=node_data;
277   }
278   return NGX_OK;
279 }
280 
rbtree_walk_writesafe(rbtree_seed_t * seed,int (* include)(void *),rbtree_walk_callback_pt callback,void * data)281 ngx_int_t rbtree_walk_writesafe(rbtree_seed_t *seed, int (*include)(void *), rbtree_walk_callback_pt callback, void *data) {
282   void                         *els_static[32];
283   int                           allocd;
284   int                           i;
285   rbtree_walk_writesafe_data_t  d;
286   if(seed->active_nodes > 32) {
287     d.els = ngx_alloc(sizeof(void *) * seed->active_nodes, ngx_cycle->log);
288     allocd = 1;
289   }
290   else {
291     d.els = els_static;
292     allocd = 0;
293   }
294   d.include = include;
295   d.n = 0;
296   rbtree_walk(seed, (rbtree_walk_callback_pt )rbtree_walk_writesafe_callback, &d);
297 
298   for(i=0; i<d.n; i++) {
299     callback(seed, d.els[i], data);
300   }
301 
302   if(allocd) ngx_free(d.els);
303   return NGX_OK;
304 }
305 
rbtree_empty(rbtree_seed_t * seed,rbtree_walk_callback_pt callback,void * data)306 unsigned rbtree_empty(rbtree_seed_t *seed, rbtree_walk_callback_pt callback, void *data) {
307   ngx_rbtree_t         *tree = &seed->tree;
308   ngx_rbtree_node_t    *cur, *sentinel = tree->sentinel;
309   unsigned int          n = 0;
310 
311   for(cur = tree->root; cur != NULL && cur != sentinel; cur = tree->root) {
312     if(callback) {
313       callback(seed, rbtree_data_from_node(cur), data);
314     }
315     rbtree_remove_node(seed, cur);
316     rbtree_destroy_node(seed, cur);
317     n++;
318   }
319   return n;
320 }
321 
322 
rbtree_conditional_walk_real(rbtree_seed_t * seed,ngx_rbtree_node_t * node,ngx_rbtree_node_t * sentinel,rbtree_walk_conditional_callback_pt callback,void * data)323 static ngx_inline void rbtree_conditional_walk_real(rbtree_seed_t *seed, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, rbtree_walk_conditional_callback_pt callback, void *data) {
324   rbtree_walk_direction_t    direction;
325 
326   if(node == sentinel || node == NULL) {
327     return;
328   }
329 
330   direction = callback(seed, rbtree_data_from_node(node), data);
331   switch(direction) {
332     case RBTREE_WALK_LEFT:
333       rbtree_conditional_walk_real(seed, node->left, sentinel, callback, data);
334       break;
335 
336     case RBTREE_WALK_RIGHT:
337       rbtree_conditional_walk_real(seed, node->right, sentinel, callback, data);
338       break;
339 
340     case RBTREE_WALK_LEFT_RIGHT:
341       rbtree_conditional_walk_real(seed, node->left, sentinel, callback, data);
342       rbtree_conditional_walk_real(seed, node->right, sentinel, callback, data);
343       break;
344 
345     case RBTREE_WALK_STOP:
346       //no more
347       break;
348   }
349 }
350 
rbtree_conditional_walk(rbtree_seed_t * seed,rbtree_walk_conditional_callback_pt callback,void * data)351 ngx_int_t rbtree_conditional_walk(rbtree_seed_t *seed, rbtree_walk_conditional_callback_pt callback, void *data) {
352   rbtree_conditional_walk_real(seed, seed->tree.root, seed->tree.sentinel, callback, data);
353   return NGX_OK;
354 }
355 
rbtree_init(rbtree_seed_t * seed,char * name,void * (* id)(void *),uint32_t (* hash)(void *),ngx_int_t (* compare)(void *,void *))356 ngx_int_t rbtree_init(rbtree_seed_t *seed, char *name, void *(*id)(void *), uint32_t (*hash)(void *), ngx_int_t (*compare)(void *, void *)) {
357   seed->name=name;
358   assert(id != NULL);
359   if(hash==NULL) {
360     hash = &rbtree_hash_crc32;
361   }
362   if(compare == NULL) {
363     compare = &rbtree_compare_str;
364   }
365   seed->id = id;
366   seed->hash = hash;
367   seed->compare = compare;
368   seed->active_nodes = 0;
369   seed->allocd_nodes = 0;
370 #if NCHAN_RBTREE_DBG
371   /*
372   //super-heavy debugging setup
373   ngx_int_t max = sizeof(seed->actives);
374   ngx_memzero(seed->actives, sizeof(seed->actives));
375   */
376 #endif
377   ngx_rbtree_init(&seed->tree, &seed->sentinel, &rbtree_insert_generic);
378   return NGX_OK;
379 }
380