File Coverage

lib/MaxMind/DB/Writer/Tree.xs
Criterion Covered Total %
statement 90 94 95.7
branch 29 38 76.3
condition n/a
subroutine n/a
pod n/a
total 119 132 90.1


line stmt bran cond sub pod time code
1             /* *INDENT-ON* */
2             #ifdef __cplusplus
3             extern "C" {
4             #endif
5              
6             #include "tree.h"
7              
8             #ifdef __cplusplus
9             }
10             #endif
11              
12             typedef struct perl_iterator_args_s {
13             SV *empty_method;
14             SV *node_method;
15             SV *data_method;
16             SV *receiver;
17             } perl_iterator_args_s;
18              
19 121210           MMDBW_tree_s *tree_from_self(SV *self) {
20             /* This is a bit wrong since we're looking in the $self hash
21             rather than calling a method. I couldn't get method calling
22             to work. */
23 121210           return *(MMDBW_tree_s **)SvPV_nolen(
24             *(hv_fetchs((HV *)SvRV(self), "_tree", 0)));
25             }
26              
27 11826           void call_iteration_method(MMDBW_tree_s *tree,
28             perl_iterator_args_s *args,
29             SV *method,
30             const uint64_t node_number,
31             MMDBW_record_s *record,
32             const uint128_t node_ip_num,
33             const uint8_t node_prefix_length,
34             const uint128_t record_ip_num,
35             const uint8_t record_prefix_length,
36             const bool is_right) {
37 11826           dSP;
38              
39 11826           ENTER;
40 11826           SAVETMPS;
41              
42 23652           int stack_size = MMDBW_RECORD_TYPE_EMPTY == record->type ||
43             MMDBW_RECORD_TYPE_FIXED_EMPTY == record->type
44             ? 7
45 11826 100         : 8;
46              
47 11826 50         PUSHMARK(SP);
48 11826 50         EXTEND(SP, stack_size);
49 11826           PUSHs((SV *)args->receiver);
50 11826           mPUSHs(newSVu64(node_number));
51 11826           mPUSHi((int)is_right);
52 11826           mPUSHs(newSVu128(node_ip_num));
53 11826           mPUSHi(node_prefix_length);
54 11826           mPUSHs(newSVu128(record_ip_num));
55 11826           mPUSHi(record_prefix_length);
56 11826 100         if (MMDBW_RECORD_TYPE_DATA == record->type) {
57 1129           mPUSHs(newSVsv(data_for_key(tree, record->value.key)));
58 10697           } else if (MMDBW_RECORD_TYPE_NODE == record->type ||
59 10697 100         MMDBW_RECORD_TYPE_FIXED_NODE == record->type ||
60             MMDBW_RECORD_TYPE_ALIAS == record->type) {
61 5761           mPUSHi(record->value.node->number);
62             }
63 11826           PUTBACK;
64              
65 11826           int count = call_sv(method, G_VOID);
66              
67 11826           SPAGAIN;
68              
69 11826 50         if (count != 0) {
70 0           croak("Expected no items back from ->%s() call", SvPV_nolen(method));
71             }
72              
73 11826           PUTBACK;
74 11826 50         FREETMPS;
75 11826           LEAVE;
76              
77 11826           return;
78             }
79              
80 12164           SV *method_for_record_type(perl_iterator_args_s *args,
81             const MMDBW_record_type record_type) {
82 12164           switch (record_type) {
83 4936           case MMDBW_RECORD_TYPE_EMPTY:
84             case MMDBW_RECORD_TYPE_FIXED_EMPTY:
85 4936           return args->empty_method;
86 1141           break;
87 1141           case MMDBW_RECORD_TYPE_DATA:
88 1141           return args->data_method;
89 6087           break;
90 6087           case MMDBW_RECORD_TYPE_NODE:
91             case MMDBW_RECORD_TYPE_FIXED_NODE:
92             case MMDBW_RECORD_TYPE_ALIAS:
93 6087           return args->node_method;
94 0           break;
95             }
96              
97             // This croak is probably okay. It should not happen unless we're adding a
98             // new record type and missed this spot.
99 0           croak("unexpected record type");
100             return NULL;
101             }
102              
103 6082           void call_perl_object(MMDBW_tree_s *tree,
104             MMDBW_node_s *node,
105             const uint128_t node_ip_num,
106             const uint8_t node_prefix_length,
107             void *void_args) {
108 6082           perl_iterator_args_s *args = (perl_iterator_args_s *)void_args;
109              
110 6082           SV *left_method = method_for_record_type(args, node->left_record.type);
111              
112 6082 100         if (NULL != left_method) {
113 5860           call_iteration_method(tree,
114             args,
115             left_method,
116 5860           node->number,
117             &(node->left_record),
118             node_ip_num,
119             node_prefix_length,
120             node_ip_num,
121 5860           node_prefix_length + 1,
122             false);
123             }
124              
125 6082           SV *right_method = method_for_record_type(args, node->right_record.type);
126 6082 100         if (NULL != right_method) {
127 11932           call_iteration_method(
128             tree,
129             args,
130             right_method,
131 5966           node->number,
132             &(node->right_record),
133             node_ip_num,
134             node_prefix_length,
135             flip_network_bit(tree, node_ip_num, node_prefix_length),
136 5966           node_prefix_length + 1,
137             true);
138             }
139 6082           return;
140             }
141              
142             /* It'd be nice to return the CV instead but there's no exposed API for
143             * calling a CV directly. */
144 36           SV *maybe_method(HV *package, const char *const method) {
145 36           GV *gv = gv_fetchmethod_autoload(package, method, 1);
146 36 100         if (NULL != gv) {
147 26           CV *cv = GvCV(gv);
148 26 50         if (NULL != cv) {
149 26           return newRV_noinc((SV *)cv);
150             }
151             }
152              
153             return NULL;
154             }
155              
156             // clang-format off
157             /* XXX - it'd be nice to find a way to get the tree from the XS code so we
158             * don't have to pass it in all over place - it'd also let us remove at least
159             * a few shim methods on the Perl code. */
160              
161             MODULE = MaxMind::DB::Writer::Tree PACKAGE = MaxMind::DB::Writer::Tree
162              
163             #include <stdint.h>
164              
165             BOOT:
166 18 50         PERL_MATH_INT128_LOAD_OR_CROAK;
167              
168             MMDBW_tree_s *
169             _create_tree(ip_version, record_size, merge_strategy, alias_ipv6, remove_reserved_networks)
170             uint8_t ip_version;
171             uint8_t record_size;
172             MMDBW_merge_strategy merge_strategy;
173             bool alias_ipv6;
174             bool remove_reserved_networks;
175              
176             CODE:
177 174           RETVAL = new_tree(ip_version, record_size, merge_strategy, alias_ipv6, remove_reserved_networks);
178              
179             OUTPUT:
180             RETVAL
181              
182             void
183             _insert_network(self, ip_address, prefix_length, key, data, merge_strategy)
184             SV *self;
185             char *ip_address;
186             uint8_t prefix_length;
187             SV *key;
188             SV *data;
189             MMDBW_merge_strategy merge_strategy;
190              
191             CODE:
192 105268           MMDBW_tree_s *tree = tree_from_self(self);
193 105268           insert_network(tree, ip_address, prefix_length, key, data, merge_strategy);
194              
195             void
196             _insert_range(self, start_ip_address, end_ip_address, key, data, merge_strategy)
197             SV *self;
198             char *start_ip_address;
199             char *end_ip_address;
200             SV *key;
201             SV *data;
202             MMDBW_merge_strategy merge_strategy;
203              
204             CODE:
205 849           insert_range(tree_from_self(self), start_ip_address, end_ip_address, key, data, merge_strategy);
206              
207             void
208             _remove_network(self, ip_address, prefix_length)
209             SV *self;
210             char *ip_address;
211             uint8_t prefix_length;
212              
213             CODE:
214 2           remove_network(tree_from_self(self), ip_address, prefix_length);
215              
216             void
217             _write_search_tree(self, output, root_data_type, serializer)
218             SV *self;
219             SV *output;
220             SV *root_data_type;
221             SV *serializer;
222              
223             CODE:
224 37           write_search_tree(tree_from_self(self), output, root_data_type, serializer);
225              
226             uint32_t
227             node_count(self)
228             SV * self;
229              
230             CODE:
231 57           MMDBW_tree_s *tree = tree_from_self(self);
232 57           assign_node_numbers(tree);
233 57 50         if (tree->node_count > max_record_value(tree)) {
234 0           croak("Node count of %u exceeds record size limit of %u bits",
235             tree->node_count, tree->record_size);
236             }
237 57 100         RETVAL = tree->node_count;
238              
239             OUTPUT:
240             RETVAL
241              
242             void
243             iterate(self, object)
244             SV *self;
245             SV *object;
246              
247             CODE:
248 15           MMDBW_tree_s *tree = tree_from_self(self);
249 15           assign_node_numbers(tree);
250 13           HV *package;
251             /* It's a blessed object */
252 13 100         if (sv_isobject(object)) {
253 10           package = SvSTASH(SvRV(object));
254             /* It's a package name */
255 3 100         } else if (SvPOK(object) && !SvROK(object)) {
256 2           package = gv_stashsv(object, 0);
257             } else {
258 1           croak("The argument passed to iterate (%s) is not an object or class name", SvPV_nolen(object));
259             }
260              
261 48           perl_iterator_args_s args = {
262 12           .empty_method = maybe_method(package, "process_empty_record"),
263 12           .node_method = maybe_method(package, "process_node_record"),
264 12           .data_method = maybe_method(package, "process_data_record"),
265             .receiver = object
266             };
267 14 100         if (!(NULL != args.empty_method
    50          
268 2 50         || NULL != args.node_method
269             || NULL != args.data_method)) {
270              
271 2           croak("The object or class passed to iterate must implement "
272             "at least one method of process_empty_record, "
273             "process_node_record, or process_data_record");
274             }
275              
276 10           start_iteration(tree, true, (void *)&args, &call_perl_object);
277              
278             SV *
279             lookup_ip_address(self, address)
280             SV *self;
281             char *address;
282              
283             CODE:
284 14749           RETVAL = lookup_ip_address(tree_from_self(self), address);
285              
286             OUTPUT:
287             RETVAL
288              
289             void
290             _freeze_tree(self, filename, frozen_params, frozen_params_size)
291             SV *self;
292             char *filename;
293             char *frozen_params;
294             int frozen_params_size;
295              
296             CODE:
297 30           freeze_tree(tree_from_self(self), filename, frozen_params, frozen_params_size);
298              
299             MMDBW_tree_s *
300             _thaw_tree(filename, initial_offset, ip_version, record_size, merge_strategy, alias_ipv6, remove_reserved_networks)
301             char *filename;
302             int initial_offset;
303             int ip_version;
304             int record_size;
305             MMDBW_merge_strategy merge_strategy;
306             bool alias_ipv6;
307             bool remove_reserved_networks;
308              
309             CODE:
310 29           RETVAL = thaw_tree(filename, initial_offset, ip_version, record_size, merge_strategy, alias_ipv6, remove_reserved_networks);
311              
312             OUTPUT:
313             RETVAL
314              
315             void
316             _free_tree(self)
317             SV *self;
318              
319             CODE:
320 203           free_tree(tree_from_self(self));