File Coverage

lib/Net/BART/XS.xs
Criterion Covered Total %
statement 105 113 92.9
branch 51 86 59.3
condition n/a
subroutine n/a
pod n/a
total 156 199 78.3


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include "bart.h"
7              
8             /* Helpers: increment/decrement SV refcounts for values stored in the trie */
9              
10 274           static void sv_val_dec(pTHX_ void *val) {
11 274 50         if (val) SvREFCNT_dec((SV*)val);
12 274           }
13              
14             /* Free all SV* values in a node tree (called before bart_table_free) */
15 35           static void free_node_svs(pTHX_ bart_node_t *node) {
16             int i;
17 35 50         if (!node) return;
18             /* Free prefix SVs */
19 43 100         for (i = 0; i < node->prefixes.len; i++) {
20 8           sv_val_dec(aTHX_ node->prefixes.items[i]);
21             }
22             /* Free child SVs recursively */
23 312 100         for (i = 0; i < node->children.len; i++) {
24 277           void *tagged = node->children.items[i];
25 277           int tag = ptr_tag(tagged);
26 277           void *child = untag_ptr(tagged);
27 277           switch (tag) {
28 11           case NODE_BART:
29 11           free_node_svs(aTHX_ (bart_node_t*)child);
30 11           break;
31 3           case NODE_LEAF:
32 3           sv_val_dec(aTHX_ ((leaf_node_t*)child)->value);
33 3           break;
34 263           case NODE_FRINGE:
35 263           sv_val_dec(aTHX_ ((fringe_node_t*)child)->value);
36 263           break;
37             }
38             }
39             }
40              
41             /* Parse "addr/len" or bare IP. Returns is_ipv6. */
42 280           static int parse_prefix_str(pTHX_ const char *str, uint8_t *addr, int *addr_len, int *prefix_len) {
43             char buf[256];
44             const char *slash;
45             int is_ipv6;
46              
47 280           strncpy(buf, str, sizeof(buf) - 1);
48 280           buf[sizeof(buf) - 1] = '\0';
49              
50 280           slash = strchr(buf, '/');
51 280 50         if (slash) {
52 280           buf[slash - buf] = '\0';
53 280           *prefix_len = atoi(slash + 1);
54             } else {
55 0           *prefix_len = -1; /* will be set based on address type */
56             }
57              
58 280           is_ipv6 = (strchr(buf, ':') != NULL);
59              
60 280 100         if (is_ipv6) {
61             /* Parse IPv6 using inet_pton */
62             struct in6_addr in6;
63 3 50         if (inet_pton(AF_INET6, buf, &in6) != 1) {
64 0           croak("Invalid IPv6 address: %s", buf);
65             }
66 3           memcpy(addr, &in6, 16);
67 3           *addr_len = 16;
68 3 50         if (*prefix_len < 0) *prefix_len = 128;
69             } else {
70 277 50         if (!parse_ipv4(buf, addr)) {
71 0           croak("Invalid IPv4 address: %s", buf);
72             }
73 277           memset(addr + 4, 0, 12);
74 277           *addr_len = 4;
75 277 50         if (*prefix_len < 0) *prefix_len = 32;
76             }
77              
78 280           mask_prefix(addr, *addr_len, *prefix_len);
79 280           return is_ipv6;
80             }
81              
82             /* Parse bare IP for lookup/contains. */
83 21           static int parse_ip_str(pTHX_ const char *str, uint8_t *addr) {
84 21           int is_ipv6 = (strchr(str, ':') != NULL);
85 21 100         if (is_ipv6) {
86             struct in6_addr in6;
87 3 50         if (inet_pton(AF_INET6, str, &in6) != 1) {
88 0           croak("Invalid IPv6 address: %s", str);
89             }
90 3           memcpy(addr, &in6, 16);
91             } else {
92 18 50         if (!parse_ipv4(str, addr)) {
93 0           croak("Invalid IPv4 address: %s", str);
94             }
95             }
96 21           return is_ipv6;
97             }
98              
99              
100             MODULE = Net::BART::XS PACKAGE = Net::BART::XS
101              
102             PROTOTYPES: DISABLE
103              
104             SV*
105             new(class)
106             const char *class
107             CODE:
108 12           bart_table_t *t = bart_table_new();
109 12           SV *obj = newSViv(PTR2IV(t));
110 12           SV *objref = newRV_noinc(obj);
111 12           sv_bless(objref, gv_stashpv(class, GV_ADD));
112 12           RETVAL = objref;
113             OUTPUT:
114             RETVAL
115              
116             void
117             DESTROY(self)
118             SV *self
119             CODE:
120 12           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
121 12           free_node_svs(aTHX_ t->root4);
122 12           free_node_svs(aTHX_ t->root6);
123 12           bart_table_free(t);
124              
125             int
126             insert(self, prefix_str, value)
127             SV *self
128             const char *prefix_str
129             SV *value
130             CODE:
131 276           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
132             uint8_t addr[16];
133             int addr_len, prefix_len;
134 276           int is_ipv6 = parse_prefix_str(aTHX_ prefix_str, addr, &addr_len, &prefix_len);
135 276 100         bart_node_t *root = is_ipv6 ? t->root6 : t->root4;
136              
137 276           SV *sv_copy = newSVsv(value);
138 276           void *old_val = NULL;
139 276           int is_new = bart_insert(root, addr, addr_len, prefix_len, 0, (void*)sv_copy, &old_val);
140              
141 276 100         if (!is_new && old_val) {
    50          
142 1           SvREFCNT_dec((SV*)old_val);
143             }
144 276 100         if (is_new) {
145 275 100         if (is_ipv6) t->size6++; else t->size4++;
146             }
147 276 100         RETVAL = is_new;
148             OUTPUT:
149             RETVAL
150              
151             void
152             lookup(self, ip_str)
153             SV *self
154             const char *ip_str
155             PPCODE:
156 18           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
157             uint8_t addr[16];
158 18           int is_ipv6 = parse_ip_str(aTHX_ ip_str, addr);
159 18           int found = 0;
160 18           void *val = bart_lookup(t, addr, is_ipv6, &found);
161 18 100         if (found && val) {
    50          
162 15 50         XPUSHs(sv_2mortal(newSVsv((SV*)val)));
163 15 50         XPUSHs(sv_2mortal(newSViv(1)));
164             } else {
165 3 50         XPUSHs(&PL_sv_undef);
166 3 50         XPUSHs(sv_2mortal(newSViv(0)));
167             }
168              
169             int
170             contains(self, ip_str)
171             SV *self
172             const char *ip_str
173             CODE:
174 3           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
175             uint8_t addr[16];
176 3           int is_ipv6 = parse_ip_str(aTHX_ ip_str, addr);
177 3           RETVAL = bart_contains(t, addr, is_ipv6);
178             OUTPUT:
179             RETVAL
180              
181             void
182             get(self, prefix_str)
183             SV *self
184             const char *prefix_str
185             PPCODE:
186 3           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
187             uint8_t addr[16];
188             int addr_len, prefix_len;
189 3           int is_ipv6 = parse_prefix_str(aTHX_ prefix_str, addr, &addr_len, &prefix_len);
190 3           int found = 0;
191 3           void *val = bart_get(t, addr, prefix_len, is_ipv6, &found);
192 3 100         if (found && val) {
    50          
193 2 50         XPUSHs(sv_2mortal(newSVsv((SV*)val)));
194 2 50         XPUSHs(sv_2mortal(newSViv(1)));
195             } else {
196 1 50         XPUSHs(&PL_sv_undef);
197 1 50         XPUSHs(sv_2mortal(newSViv(0)));
198             }
199              
200             void
201             delete(self, prefix_str)
202             SV *self
203             const char *prefix_str
204             PPCODE:
205 1           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
206             uint8_t addr[16];
207             int addr_len, prefix_len;
208 1           int is_ipv6 = parse_prefix_str(aTHX_ prefix_str, addr, &addr_len, &prefix_len);
209 1 50         bart_node_t *root = is_ipv6 ? t->root6 : t->root4;
210 1           int found = 0;
211 1           void *val = bart_delete(root, addr, prefix_len, 0, &found);
212 1 50         if (found) {
213 1 50         if (is_ipv6) t->size6--; else t->size4--;
214 1 50         if (val) {
215 1 50         XPUSHs(sv_2mortal(newSVsv((SV*)val)));
216 1           SvREFCNT_dec((SV*)val);
217             } else {
218 0 0         XPUSHs(&PL_sv_undef);
219             }
220 1 50         XPUSHs(sv_2mortal(newSViv(1)));
221             } else {
222 0 0         XPUSHs(&PL_sv_undef);
223 0 0         XPUSHs(sv_2mortal(newSViv(0)));
224             }
225              
226             int
227             size(self)
228             SV *self
229             CODE:
230 5           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
231 5 50         RETVAL = t->size4 + t->size6;
232             OUTPUT:
233             RETVAL
234              
235             int
236             size4(self)
237             SV *self
238             CODE:
239 1           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
240 1 50         RETVAL = t->size4;
241             OUTPUT:
242             RETVAL
243              
244             int
245             size6(self)
246             SV *self
247             CODE:
248 2           bart_table_t *t = INT2PTR(bart_table_t*, SvIV(SvRV(self)));
249 2 50         RETVAL = t->size6;
250             OUTPUT:
251             RETVAL