File Coverage

Patricia.xs
Criterion Covered Total %
statement 154 172 89.5
branch 106 150 70.6
condition n/a
subroutine n/a
pod n/a
total 260 322 80.7


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4             #include "EXTERN.h"
5             #include "perl.h"
6             #include "XSUB.h"
7             #ifdef __cplusplus
8             }
9             #endif
10              
11             /* memory for libpatricia must not be managed with redefined calloc() and
12             * free() routines from XSUB.h. Otherwise, DESTROY crashes on Perl releases
13             * such as Strawberry Perl that use their own memory management. */
14             #undef calloc
15             #undef free
16              
17             #include
18             #include
19             #include "libpatricia/patricia.h"
20              
21             /* frozen stuff is frozen in network byte order */
22             struct frozen_node
23             {
24             int32_t l_index;
25             int32_t r_index;
26             int32_t d_index;
27             uint16_t bitlen; /* bit 0x8000 indicates presence of prefix */
28             uint16_t family;
29             uint8_t address[16];
30             } __attribute__((__packed__));
31              
32             struct frozen_header
33             {
34             uint32_t magic;
35             #define FROZEN_MAGIC 0x4E655061 /* NePa */
36             uint8_t major;
37             #define FROZEN_MAJOR 0
38             uint8_t minor;
39             #define FROZEN_MINOR 0
40             uint16_t maxbits;
41             int32_t num_total_node;
42             int32_t num_active_node;
43             } __attribute__((__packed__));
44              
45             struct frozen_patricia
46             {
47             struct frozen_header header;
48             struct frozen_node node[1];
49             } __attribute__((__packed__));
50              
51             #define Fill_Prefix(p,f,a,b,mb) \
52             do { \
53             if (b < 0 || b > mb) \
54             croak("invalid key"); \
55             memcpy(&p.add.sin, a, (mb+7)/8); \
56             p.family = f; \
57             p.bitlen = b; \
58             p.ref_count = 0; \
59             } while (0)
60              
61 42           static void deref_data(SV *data) {
62 42           SvREFCNT_dec(data);
63 42           data = NULL;
64 42           }
65              
66             static size_t
67 16           patricia_walk_inorder_perl(patricia_node_t *node, SV *coderef) {
68 16           dSP;
69 16           size_t n = 0;
70              
71 16 100         if (node->l) {
72 10           n += patricia_walk_inorder_perl(node->l, coderef);
73             }
74              
75 16 100         if (node->prefix) {
76 12 50         if (NULL != coderef) {
77 12 50         PUSHMARK(SP);
78 12 50         XPUSHs(sv_mortalcopy((SV *)node->data));
79 12           PUTBACK;
80 12           perl_call_sv(coderef, G_VOID|G_DISCARD);
81 12           SPAGAIN;
82             }
83 12           n++;
84             }
85            
86 16 100         if (node->r) {
87 4           n += patricia_walk_inorder_perl(node->r, coderef);
88             }
89              
90 16           return n;
91             }
92              
93             typedef patricia_tree_t *Net__Patricia;
94             typedef patricia_node_t *Net__PatriciaNode;
95              
96             MODULE = Net::Patricia PACKAGE = Net::Patricia
97              
98             PROTOTYPES: ENABLE
99              
100             Net::Patricia
101             _new(size)
102             int size
103             CODE:
104 2           RETVAL = New_Patricia(size);
105             OUTPUT:
106             RETVAL
107              
108             void
109             _add(tree, family, addr, bits, data)
110             Net::Patricia tree
111             int family
112             char * addr
113             int bits
114             SV * data
115             PROTOTYPE: $$$$$
116             PREINIT:
117             prefix_t prefix;
118             Net__PatriciaNode node;
119             PPCODE:
120 11 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
121 11           node = patricia_lookup(tree, &prefix);
122 11 50         if (NULL != node) {
123             /* { */
124 11 50         if (node->data) {
125 0           deref_data(node->data);
126             }
127 11           node->data = newSVsv(data);
128             /* } */
129 11           PUSHs(data);
130             } else {
131 0           XSRETURN_UNDEF;
132             }
133              
134             void
135             _match(tree, family, addr, bits)
136             Net::Patricia tree
137             int family
138             char * addr
139             int bits
140             PROTOTYPE: $$$$
141             PREINIT:
142             prefix_t prefix;
143             Net__PatriciaNode node;
144             PPCODE:
145 22 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
146 22           node = patricia_search_best(tree, &prefix);
147 22 100         if (NULL != node) {
148 18 50         XPUSHs((SV *)node->data);
149             } else {
150 4           XSRETURN_UNDEF;
151             }
152              
153             void
154             _exact(tree, family, addr, bits)
155             Net::Patricia tree
156             int family
157             char * addr
158             int bits
159             PROTOTYPE: $$$$
160             PREINIT:
161             prefix_t prefix;
162             Net__PatriciaNode node;
163             PPCODE:
164 8 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
165 8           node = patricia_search_exact(tree, &prefix);
166 8 100         if (NULL != node) {
167 4 50         XPUSHs((SV *)node->data);
168             } else {
169 4           XSRETURN_UNDEF;
170             }
171              
172              
173             void
174             _remove(tree, family, addr, bits)
175             Net::Patricia tree
176             int family
177             char * addr
178             int bits
179             PROTOTYPE: $$$$
180             PREINIT:
181             prefix_t prefix;
182             Net__PatriciaNode node;
183             PPCODE:
184 4 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
185 4           node = patricia_search_exact(tree, &prefix);
186 4 100         if (NULL != node) {
187 3 50         XPUSHs(sv_mortalcopy((SV *)node->data));
188 3           deref_data(node->data);
189 3           patricia_remove(tree, node);
190             } else {
191 1           XSRETURN_UNDEF;
192             }
193              
194             size_t
195             climb(tree, ...)
196             Net::Patricia tree
197             PREINIT:
198 2           patricia_node_t *node = NULL;
199 2           size_t n = 0;
200 2 50         SV *func = NULL;
201             CODE:
202 2 50         if (2 == items) {
203 0           func = ST(1);
204 2 50         } else if (2 < items) {
205 0           croak("Usage: Net::Patricia::climb(tree[,CODEREF])");
206             }
207 18 100         PATRICIA_WALK (tree->head, node) {
    100          
208 12 50         if (NULL != func) {
209 0 0         PUSHMARK(SP);
210 0 0         XPUSHs(sv_mortalcopy((SV *)node->data));
211 0           PUTBACK;
212 0           perl_call_sv(func, G_VOID|G_DISCARD);
213 0           SPAGAIN;
214             }
215 12           n++;
216 16 100         } PATRICIA_WALK_END;
    100          
    50          
    100          
217 2 100         RETVAL = n;
218             OUTPUT:
219             RETVAL
220              
221             size_t
222             climb_inorder(tree, ...)
223             Net::Patricia tree
224             PREINIT:
225 2           size_t n = 0;
226 2 50         SV *func = NULL;
227             CODE:
228 2           func = NULL;
229 2 50         if (2 == items) {
230 2           func = ST(1);
231 0 0         } else if (2 < items) {
232 0           croak("Usage: Net::Patricia::climb_inorder(tree[,CODEREF])");
233             }
234 2 50         if (NULL != tree->head) {
235 2           n = patricia_walk_inorder_perl(tree->head, func);
236             }
237 2 100         RETVAL = n;
238             OUTPUT:
239             RETVAL
240              
241             void
242             STORABLE_freeze(tree, cloning)
243             Net::Patricia tree
244             SV * cloning
245             PREINIT:
246 6           patricia_node_t *node = NULL;
247             struct frozen_header frozen_header;
248             struct frozen_node *frozen_nodes, *frozen_node;
249 6 50         size_t n = 0, i = 0, nd = 0;
250             SV *frozen_patricia;
251             PPCODE:
252 6 50         if (SvTRUE(cloning))
253 0           XSRETURN_UNDEF;
254              
255             /* I do not know enough of patricia.c to
256             * decide whether inactive nodes can
257             * be present in a tree, and whether such
258             * nodes can be skipped while copying,
259             * so we copy everything and do not use
260             * num_active_node here. */
261 46 100         PATRICIA_WALK_ALL (tree->head, node) {
262 40           n++;
263 40 100         } PATRICIA_WALK_END;
    100          
    50          
    100          
264              
265 6 50         if (n > 2147483646)
266 0           croak("Net::Patricia::STORABLE_freeze: too many nodes");
267              
268 6           frozen_header.magic = htonl(FROZEN_MAGIC);
269 6           frozen_header.major = FROZEN_MAJOR;
270 6           frozen_header.minor = FROZEN_MINOR;
271 6           frozen_header.maxbits = htons((uint16_t)tree->maxbits);
272 6           frozen_header.num_total_node = htonl(n);
273 6           frozen_header.num_active_node = htonl(tree->num_active_node);
274              
275 6           frozen_patricia = newSVpv((char *)&frozen_header, sizeof(frozen_header));
276 6 50         XPUSHs(frozen_patricia);
277              
278 6           frozen_nodes = calloc(n, sizeof(struct frozen_node));
279              
280             /* We use user1 field to store the index of each node
281             * in the frozen_node array; it is okay since Net::Patricia
282             * is not using it for anything anywhere else */
283 46 100         PATRICIA_WALK_ALL (tree->head, node) {
284 40           node->user1 = (void *)(IV)i;
285              
286 40           frozen_node = &frozen_nodes[i];
287 40           frozen_node->l_index = htonl(-1);
288 40           frozen_node->r_index = htonl(-1);
289 40           frozen_node->bitlen = node->bit;
290 40 100         if (node->prefix) {
291 31           frozen_node->bitlen |= 0x8000;
292 31           frozen_node->family = htons(node->prefix->family);
293 31 100         if (tree->maxbits == 32)
294 30           memcpy(&frozen_node->address, &node->prefix->add, 4);
295             else
296 1           memcpy(&frozen_node->address, &node->prefix->add, 16);
297             }
298 40           frozen_node->bitlen = htons(frozen_node->bitlen);
299              
300 40 100         if (node->data) {
301 31           frozen_node->d_index = htonl(nd);
302 31           nd++;
303 31 50         XPUSHs(sv_2mortal(newRV_inc((SV *)node->data)));
304             } else {
305 9           frozen_node->d_index = htonl(-1);
306             }
307 40 100         if (node->parent && node->parent->l == node) {
    100          
308 24           frozen_nodes[(IV)node->parent->user1].l_index = htonl(i);
309 16 100         } else if (node->parent && node->parent->r == node) {
    50          
310 10           frozen_nodes[(IV)node->parent->user1].r_index = htonl(i);
311             }
312 40           i++;
313 40 100         } PATRICIA_WALK_END;
    100          
    50          
    100          
314              
315 6           sv_catpvn(frozen_patricia, (char*)frozen_nodes, n*sizeof(struct frozen_node));
316 6           free(frozen_nodes);
317              
318             void
319             STORABLE_thaw(tobj, cloning, serialized, ...)
320             SV * tobj
321             SV * cloning
322             SV * serialized;
323             PREINIT:
324             struct frozen_patricia *frozen_patricia;
325             struct frozen_node *frozen_node;
326             struct _patricia_tree_t *tree;
327 6           patricia_node_t *node = NULL, *child, **fixup;
328             int n, n_calculated, i, d_index, l_index, r_index;
329             STRLEN len;
330             PPCODE:
331 6 50         if (SvTRUE(cloning))
332 0           XSRETURN_UNDEF;
333              
334 6           tree = calloc(1, sizeof(*tree));
335 6           frozen_patricia = (struct frozen_patricia*)SvPV(serialized, len);
336              
337 6 50         if (ntohl(frozen_patricia->header.magic) != FROZEN_MAGIC)
338 0           croak("Net::Patricia::STORABLE_thaw: magic mismatch");
339 6 50         if (frozen_patricia->header.major != FROZEN_MAJOR)
340 0           croak("Net::Patricia::STORABLE_thaw: major mismatch");
341 6 50         if (frozen_patricia->header.minor != FROZEN_MINOR)
342 0           croak("Net::Patricia::STORABLE_thaw: minor mismatch");
343              
344 6           tree->maxbits = ntohs(frozen_patricia->header.maxbits);
345 6           tree->num_active_node = ntohl(frozen_patricia->header.num_active_node);
346 6           tree->head = NULL;
347              
348 6           n = ntohl(frozen_patricia->header.num_total_node);
349 6           n_calculated = (len - sizeof(frozen_patricia->header)) / sizeof(struct frozen_node);
350 6 50         if (n_calculated < n)
351 0           croak("Net::Patricia::STORABLE_thaw: size mismatch");
352 6           fixup = calloc(n, sizeof(patricia_node_t *));
353              
354 46 100         for (i = 0; i < n; i++) {
355 40           node = calloc(1, sizeof(*node));
356 40           memset(node, 0, sizeof(*node));
357 40           frozen_node = &frozen_patricia->node[i];
358              
359 40           node->bit = ntohs(frozen_node->bitlen) & ~0x8000;
360 40           d_index = ntohl(frozen_node->d_index);
361 40 100         if (d_index >= 0)
362 31           node->data = newSVsv(SvRV(ST(3+d_index)));
363              
364 40 100         if (ntohs(frozen_node->bitlen) & 0x8000) {
365 31           node->prefix = calloc(1, sizeof(*node->prefix));
366 31           node->prefix->bitlen = node->bit;
367 31           node->prefix->family = ntohs(frozen_node->family);
368             #ifndef HAVE_IPV6
369             if (tree->maxbits > 32)
370             croak("Net::Patricia::STORABLE_thaw: IPv6 is not supported by Net::Patricia on this machine");
371             #endif
372 31 100         if (tree->maxbits == 32) {
373 30           memcpy(&node->prefix->add, &frozen_node->address, 4);
374             } else
375 1           memcpy(&node->prefix->add, &frozen_node->address, 16);
376 31           node->prefix->ref_count = 1;
377             }
378 40           fixup[i] = node;
379             }
380              
381             /* Fix pointers up. */
382 6 50         if (n)
383 6           tree->head = fixup[0];
384 46 100         for (i = 0; i < n; i++) {
385 40           frozen_node = &frozen_patricia->node[i];
386 40           node = fixup[i];
387              
388 40           l_index = ntohl(frozen_node->l_index);
389 40 100         if (l_index >= 0) {
390 24           child = fixup[l_index];
391 24           child->parent = node;
392 24           node->l = child;
393             }
394              
395 40           r_index = ntohl(frozen_node->r_index);
396 40 100         if (r_index >= 0) {
397 10           child = fixup[r_index];
398 10           child->parent = node;
399 10           node->r = child;
400             }
401             }
402              
403 6           free(fixup);
404 6           sv_setiv((SV*)SvRV(tobj), PTR2IV(tree));
405 6           XSRETURN_EMPTY;
406              
407             void
408             DESTROY(tree)
409             Net::Patricia tree
410             CODE:
411 8           Destroy_Patricia(tree, (void_fn_t)deref_data);