File Coverage

Patricia.xs
Criterion Covered Total %
statement 177 248 71.3
branch 135 224 60.2
condition n/a
subroutine n/a
pod n/a
total 312 472 66.1


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             #include
12             #include
13             #include
14             #include "libpatricia/patricia.h"
15              
16             /* frozen stuff is frozen in network byte order */
17             struct frozen_node
18             {
19             int32_t l_index;
20             int32_t r_index;
21             int32_t d_index;
22             uint16_t bitlen; /* bit 0x8000 indicates presence of prefix */
23             uint16_t family;
24             uint8_t address[16];
25             } __attribute__((__packed__));
26              
27             struct frozen_header
28             {
29             uint32_t magic;
30             #define FROZEN_MAGIC 0x4E655061 /* NePa */
31             uint8_t major;
32             #define FROZEN_MAJOR 0
33             uint8_t minor;
34             #define FROZEN_MINOR 0
35             uint16_t maxbits;
36             int32_t num_total_node;
37             int32_t num_active_node;
38             } __attribute__((__packed__));
39              
40             struct frozen_patricia
41             {
42             struct frozen_header header;
43             struct frozen_node node[1];
44             } __attribute__((__packed__));
45              
46             static int
47 0           not_here(s)
48             char *s;
49             {
50 0           croak("%s not implemented on this architecture", s);
51             return -1;
52             }
53              
54             static double
55 0           constant(name, arg)
56             char *name;
57             int arg;
58             {
59 0           errno = 0;
60 0           switch (*name) {
61             case 'A':
62 0           break;
63             case 'B':
64 0           break;
65             case 'C':
66 0           break;
67             case 'D':
68 0           break;
69             case 'E':
70 0           break;
71             case 'F':
72 0           break;
73             case 'G':
74 0           break;
75             case 'H':
76 0           break;
77             case 'I':
78 0           break;
79             case 'J':
80 0           break;
81             case 'K':
82 0           break;
83             case 'L':
84 0           break;
85             case 'M':
86 0           break;
87             case 'N':
88 0           break;
89             case 'O':
90 0           break;
91             case 'P':
92 0           break;
93             case 'Q':
94 0           break;
95             case 'R':
96 0           break;
97             case 'S':
98 0           break;
99             case 'T':
100 0           break;
101             case 'U':
102 0           break;
103             case 'V':
104 0           break;
105             case 'W':
106 0           break;
107             case 'X':
108 0           break;
109             case 'Y':
110 0           break;
111             case 'Z':
112 0           break;
113             case 'a':
114 0           break;
115             case 'b':
116 0           break;
117             case 'c':
118 0           break;
119             case 'd':
120 0           break;
121             case 'e':
122 0           break;
123             case 'f':
124 0           break;
125             case 'g':
126 0           break;
127             case 'h':
128 0           break;
129             case 'i':
130 0           break;
131             case 'j':
132 0           break;
133             case 'k':
134 0           break;
135             case 'l':
136 0           break;
137             case 'm':
138 0           break;
139             case 'n':
140 0           break;
141             case 'o':
142 0           break;
143             case 'p':
144 0           break;
145             case 'q':
146 0           break;
147             case 'r':
148 0           break;
149             case 's':
150 0           break;
151             case 't':
152 0           break;
153             case 'u':
154 0           break;
155             case 'v':
156 0           break;
157             case 'w':
158 0           break;
159             case 'x':
160 0           break;
161             case 'y':
162 0           break;
163             case 'z':
164 0           break;
165             }
166 0           errno = EINVAL;
167 0           return 0;
168              
169             not_there:
170             errno = ENOENT;
171             return 0;
172             }
173              
174             #define Fill_Prefix(p,f,a,b,mb) \
175             do { \
176             if (b < 0 || b > mb) \
177             croak("invalid key"); \
178             memcpy(&p.add.sin, a, (mb+7)/8); \
179             p.family = f; \
180             p.bitlen = b; \
181             p.ref_count = 0; \
182             } while (0)
183              
184 68           static void deref_data(SV *data) {
185 68           SvREFCNT_dec(data);
186 68           data = NULL;
187 68           }
188              
189             static size_t
190 32           patricia_walk_inorder_perl(patricia_node_t *node, SV *coderef) {
191 32           dSP;
192 32           size_t n = 0;
193              
194 32 100         if (node->l) {
195 19           n += patricia_walk_inorder_perl(node->l, coderef);
196             }
197              
198 32 100         if (node->prefix) {
199 25 50         if (NULL != coderef) {
200 25 50         PUSHMARK(SP);
201 25 50         XPUSHs(sv_mortalcopy((SV *)node->data));
202 25 100         if (node->prefix->family == AF_INET) {
203 21 50         XPUSHs(sv_2mortal(
204             newSVpvf("%s/%d",
205             inet_ntoa(node->prefix->add.sin),
206             node->prefix->bitlen
207             )
208             ));
209             #ifdef HAVE_IPV6
210             } else {
211             char buff[40]; // Longest possible IPv6
212 4           buff[39] = '\0';
213 4           inet_ntop(node->prefix->family, &node->prefix->add.sin6, buff, 40);
214 4 50         XPUSHs(sv_2mortal(newSVpvf("%s/%d", buff, node->prefix->bitlen)));
215             #endif
216             }
217 25           PUTBACK;
218 25           perl_call_sv(coderef, G_VOID|G_DISCARD);
219 25           SPAGAIN;
220             }
221 25           n++;
222             }
223            
224 32 100         if (node->r) {
225 9           n += patricia_walk_inorder_perl(node->r, coderef);
226             }
227              
228 32           return n;
229             }
230              
231             typedef patricia_tree_t *JCM__Net__Patricia;
232             typedef patricia_node_t *JCM__Net__PatriciaNode;
233              
234             MODULE = JCM::Net::Patricia PACKAGE = JCM::Net::Patricia
235              
236             PROTOTYPES: ENABLE
237              
238             double
239             constant(name,arg)
240             char * name
241             int arg
242              
243             int
244             have_ipv6()
245             CODE:
246             #ifdef HAVE_IPV6
247 6           RETVAL = 1;
248             #else
249             RETVAL = 0;
250             #endif
251             OUTPUT:
252             RETVAL
253              
254             JCM::Net::Patricia
255             _new(size)
256             int size
257             CODE:
258 7           RETVAL = New_Patricia(size);
259             OUTPUT:
260             RETVAL
261              
262             void
263             _add(tree, family, addr, bits, data)
264             JCM::Net::Patricia tree
265             int family
266             char * addr
267             int bits
268             SV * data
269             PROTOTYPE: $$$$$
270             PREINIT:
271             prefix_t prefix;
272             JCM__Net__PatriciaNode node;
273             PPCODE:
274 37 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
275 37           node = patricia_lookup(tree, &prefix);
276 37 50         if (NULL != node) {
277             /* { */
278 37 50         if (node->data) {
279 0           deref_data(node->data);
280             }
281 37           node->data = newSVsv(data);
282             /* } */
283 37           PUSHs(data);
284             } else {
285 0           XSRETURN_UNDEF;
286             }
287              
288             void
289             _match(tree, family, addr, bits)
290             JCM::Net::Patricia tree
291             int family
292             char * addr
293             int bits
294             PROTOTYPE: $$$$
295             PREINIT:
296             prefix_t prefix;
297             JCM__Net__PatriciaNode node;
298             PPCODE:
299 23 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
300 23           node = patricia_search_best(tree, &prefix);
301 23 100         if (NULL != node) {
302 18 50         XPUSHs((SV *)node->data);
303             } else {
304 5           XSRETURN_UNDEF;
305             }
306              
307             void
308             _matching_prefix(tree, family, addr, bits)
309             JCM::Net::Patricia tree
310             int family
311             char * addr
312             int bits
313             PROTOTYPE: $$$$
314             PREINIT:
315             prefix_t prefix;
316             JCM__Net__PatriciaNode node;
317             PPCODE:
318 8 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
319 8           node = patricia_search_best(tree, &prefix);
320 8 100         if (NULL != node) {
321 7 100         if (node->prefix->family == AF_INET) {
322 3 50         XPUSHs(sv_2mortal(
323             newSVpvf("%s/%d",
324             inet_ntoa(node->prefix->add.sin),
325             node->prefix->bitlen
326             )
327             ));
328             #ifdef HAVE_IPV6
329             } else {
330             char buff[40]; // Longest possible IPv6
331 4           inet_ntop(node->prefix->family, &node->prefix->add.sin6, buff, 40);
332 7 50         XPUSHs(sv_2mortal(newSVpvf("%s/%d", buff, node->prefix->bitlen)));
333             #endif
334             }
335             } else {
336 1           XSRETURN_UNDEF;
337             }
338              
339              
340             void
341             _exact(tree, family, addr, bits)
342             JCM::Net::Patricia tree
343             int family
344             char * addr
345             int bits
346             PROTOTYPE: $$$$
347             PREINIT:
348             prefix_t prefix;
349             JCM__Net__PatriciaNode node;
350             PPCODE:
351 8 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
352 8           node = patricia_search_exact(tree, &prefix);
353 8 100         if (NULL != node) {
354 4 50         XPUSHs((SV *)node->data);
355             } else {
356 4           XSRETURN_UNDEF;
357             }
358              
359              
360             void
361             _remove(tree, family, addr, bits)
362             JCM::Net::Patricia tree
363             int family
364             char * addr
365             int bits
366             PROTOTYPE: $$$$
367             PREINIT:
368             prefix_t prefix;
369             JCM__Net__PatriciaNode node;
370             PPCODE:
371 4 50         Fill_Prefix(prefix, family, addr, bits, tree->maxbits);
    50          
372 4           node = patricia_search_exact(tree, &prefix);
373 4 100         if (NULL != node) {
374 3 50         XPUSHs(sv_mortalcopy((SV *)node->data));
375 3           deref_data(node->data);
376 3           patricia_remove(tree, node);
377             } else {
378 1           XSRETURN_UNDEF;
379             }
380              
381             size_t
382             climb(tree, ...)
383             JCM::Net::Patricia tree
384             PREINIT:
385 4           patricia_node_t *node = NULL;
386 4           size_t n = 0;
387 4           SV *func = NULL;
388             CODE:
389 4 100         if (2 == items) {
390 2           func = ST(1);
391 2 50         } else if (2 < items) {
392 0           croak("Usage: JCM::Net::Patricia::climb(tree[,CODEREF])");
393             }
394 36 100         PATRICIA_WALK (tree->head, node) {
    100          
395 25 100         if (NULL != func) {
396 13 50         PUSHMARK(SP);
397 13 50         XPUSHs(sv_mortalcopy((SV *)node->data));
398 13 100         if (node->prefix->family == AF_INET) {
399 9 50         XPUSHs(sv_2mortal(
400             newSVpvf("%s/%d",
401             inet_ntoa(node->prefix->add.sin),
402             node->prefix->bitlen
403             )
404             ));
405             #ifdef HAVE_IPV6
406             } else {
407             char buff[40]; // Longest possible IPv6
408 4           inet_ntop(node->prefix->family, &node->prefix->add.sin6, buff, 40);
409 4 50         XPUSHs(sv_2mortal(newSVpvf("%s/%d", buff, node->prefix->bitlen)));
410             #endif
411             }
412 13           PUTBACK;
413 13           perl_call_sv(func, G_VOID|G_DISCARD);
414 13           SPAGAIN;
415             }
416 25           n++;
417 32 100         } PATRICIA_WALK_END;
    100          
    50          
    100          
418 4           RETVAL = n;
419             OUTPUT:
420             RETVAL
421              
422             size_t
423             climb_inorder(tree, ...)
424             JCM::Net::Patricia tree
425             PREINIT:
426 4           size_t n = 0;
427 4           SV *func = NULL;
428             CODE:
429 4           func = NULL;
430 4 50         if (2 == items) {
431 4           func = ST(1);
432 0 0         } else if (2 < items) {
433 0           croak("Usage: JCM::Net::Patricia::climb_inorder(tree[,CODEREF])");
434             }
435 4           n = patricia_walk_inorder_perl(tree->head, func);
436 4           RETVAL = n;
437             OUTPUT:
438             RETVAL
439              
440             void
441             STORABLE_freeze(tree, cloning)
442             JCM::Net::Patricia tree
443             SV * cloning
444             PREINIT:
445 6           patricia_node_t *node = NULL;
446             struct frozen_header frozen_header;
447             struct frozen_node *frozen_nodes, *frozen_node;
448 6           size_t n = 0, i = 0, nd = 0;
449             SV *frozen_patricia;
450             PPCODE:
451 6 50         if (SvTRUE(cloning))
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
452 0           XSRETURN_UNDEF;
453              
454             /* I do not know enough of patricia.c to
455             * decide whether inactive nodes can
456             * be present in a tree, and whether such
457             * nodes can be skipped while copying,
458             * so we copy everything and do not use
459             * num_active_node here. */
460 46 100         PATRICIA_WALK_ALL (tree->head, node) {
461 40           n++;
462 40 100         } PATRICIA_WALK_END;
    100          
    50          
    100          
463              
464 6 50         if (n > 2147483646)
465 0           croak("JCM::Net::Patricia::STORABLE_freeze: too many nodes");
466              
467 6           frozen_header.magic = htonl(FROZEN_MAGIC);
468 6           frozen_header.major = FROZEN_MAJOR;
469 6           frozen_header.minor = FROZEN_MINOR;
470 6           frozen_header.maxbits = htons((uint16_t)tree->maxbits);
471 6           frozen_header.num_total_node = htonl(n);
472 6           frozen_header.num_active_node = htonl(tree->num_active_node);
473              
474 6           frozen_patricia = newSVpv((char *)&frozen_header, sizeof(frozen_header));
475 6 50         XPUSHs(frozen_patricia);
476              
477 6           frozen_nodes = calloc(n, sizeof(struct frozen_node));
478              
479             /* We use user1 field to store the index of each node
480             * in the frozen_node array; it is okay since JCM::Net::Patricia
481             * is not using it for anything anywhere else */
482 46 100         PATRICIA_WALK_ALL (tree->head, node) {
483 40           node->user1 = (void *)(IV)i;
484              
485 40           frozen_node = &frozen_nodes[i];
486 40           frozen_node->l_index = htonl(-1);
487 40           frozen_node->r_index = htonl(-1);
488 40           frozen_node->bitlen = node->bit;
489 40 100         if (node->prefix) {
490 31           frozen_node->bitlen |= 0x8000;
491 31           frozen_node->family = htons(node->prefix->family);
492 31 100         if (tree->maxbits == 32)
493 30           memcpy(&frozen_node->address, &node->prefix->add, 4);
494             else
495 1           memcpy(&frozen_node->address, &node->prefix->add, 16);
496             }
497 40           frozen_node->bitlen = htons(frozen_node->bitlen);
498              
499 40 100         if (node->data) {
500 31           frozen_node->d_index = htonl(nd);
501 31           nd++;
502 31 50         XPUSHs(sv_2mortal(newRV_inc((SV *)node->data)));
503             } else {
504 9           frozen_node->d_index = htonl(-1);
505             }
506 40 100         if (node->parent && node->parent->l == node) {
    100          
507 24           frozen_nodes[(IV)node->parent->user1].l_index = htonl(i);
508 16 100         } else if (node->parent && node->parent->r == node) {
    50          
509 10           frozen_nodes[(IV)node->parent->user1].r_index = htonl(i);
510             }
511 40           i++;
512 40 100         } PATRICIA_WALK_END;
    100          
    50          
    100          
513              
514 6           sv_catpvn(frozen_patricia, (char*)frozen_nodes, n*sizeof(struct frozen_node));
515 6           free(frozen_nodes);
516              
517             void
518             STORABLE_thaw(tobj, cloning, serialized, ...)
519             SV * tobj
520             SV * cloning
521             SV * serialized;
522             PREINIT:
523             struct frozen_patricia *frozen_patricia;
524             struct frozen_node *frozen_node;
525             struct _patricia_tree_t *tree;
526 6           patricia_node_t *node = NULL, *child, **fixup;
527             int n, n_calculated, i, d_index, l_index, r_index;
528             STRLEN len;
529             PPCODE:
530 6 50         if (SvTRUE(cloning))
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
531 0           XSRETURN_UNDEF;
532              
533 6           tree = calloc(1, sizeof(*tree));
534 6 50         frozen_patricia = (struct frozen_patricia*)SvPV(serialized, len);
535              
536 6 50         if (ntohl(frozen_patricia->header.magic) != FROZEN_MAGIC)
537 0           croak("JCM::Net::Patricia::STORABLE_thaw: magic mismatch");
538 6 50         if (frozen_patricia->header.major != FROZEN_MAJOR)
539 0           croak("JCM::Net::Patricia::STORABLE_thaw: major mismatch");
540 6 50         if (frozen_patricia->header.minor != FROZEN_MINOR)
541 0           croak("JCM::Net::Patricia::STORABLE_thaw: minor mismatch");
542              
543 6           tree->maxbits = ntohs(frozen_patricia->header.maxbits);
544 6           tree->num_active_node = ntohl(frozen_patricia->header.num_active_node);
545 6           tree->head = NULL;
546              
547 6           n = ntohl(frozen_patricia->header.num_total_node);
548 6           n_calculated = (len - sizeof(frozen_patricia->header)) / sizeof(struct frozen_node);
549 6 50         if (n_calculated < n)
550 0           croak("JCM::Net::Patricia::STORABLE_thaw: size mismatch");
551 6           fixup = calloc(n, sizeof(patricia_node_t *));
552              
553 46 100         for (i = 0; i < n; i++) {
554 40           node = calloc(1, sizeof(*node));
555 40           memset(node, 0, sizeof(*node));
556 40           frozen_node = &frozen_patricia->node[i];
557              
558 40           node->bit = ntohs(frozen_node->bitlen) & ~0x8000;
559 40           d_index = ntohl(frozen_node->d_index);
560 40 100         if (d_index >= 0)
561 31           node->data = newSVsv(SvRV(ST(3+d_index)));
562              
563 40 100         if (ntohs(frozen_node->bitlen) & 0x8000) {
564 31           node->prefix = calloc(1, sizeof(*node->prefix));
565 31           node->prefix->bitlen = node->bit;
566 31           node->prefix->family = ntohs(frozen_node->family);
567             #ifndef HAVE_IPV6
568             if (tree->maxbits > 32)
569             croak("JCM::Net::Patricia::STORABLE_thaw: IPv6 is not supported by Net::Patricia on this machine");
570             #endif
571 31 100         if (tree->maxbits == 32) {
572 30           memcpy(&node->prefix->add, &frozen_node->address, 4);
573             } else
574 1           memcpy(&node->prefix->add, &frozen_node->address, 16);
575 31           node->prefix->ref_count = 1;
576             }
577 40           fixup[i] = node;
578             }
579              
580             /* Fix pointers up. */
581 6 50         if (n)
582 6           tree->head = fixup[0];
583 46 100         for (i = 0; i < n; i++) {
584 40           frozen_node = &frozen_patricia->node[i];
585 40           node = fixup[i];
586              
587 40           l_index = ntohl(frozen_node->l_index);
588 40 100         if (l_index >= 0) {
589 24           child = fixup[l_index];
590 24           child->parent = node;
591 24           node->l = child;
592             }
593              
594 40           r_index = ntohl(frozen_node->r_index);
595 40 100         if (r_index >= 0) {
596 10           child = fixup[r_index];
597 10           child->parent = node;
598 10           node->r = child;
599             }
600             }
601              
602 6           free(fixup);
603 6           sv_setiv((SV*)SvRV(tobj), PTR2IV(tree));
604 6           XSRETURN_EMPTY;
605              
606             void
607             DESTROY(tree)
608             JCM::Net::Patricia tree
609             CODE:
610 13           Destroy_Patricia(tree, deref_data);