File Coverage

qtbase.c
Criterion Covered Total %
statement 165 174 94.8
branch 77 88 87.5
condition n/a
subroutine n/a
pod n/a
total 242 262 92.3


line stmt bran cond sub pod time code
1             #include "qtbase.h"
2              
3             #define CHILDREN_PER_NODE 4
4             #define MAX_SIZE_INITIAL 4
5             #define MAX_SIZE_GROWTH 2
6             #define MAX_SIZE_CLEAR 32
7              
8 53           Shape* create_shape()
9             {
10 53           Shape *s = malloc(sizeof *s);
11 53           return s;
12             }
13              
14 53           void destroy_shape(Shape *s)
15             {
16 53           free(s);
17 53           }
18              
19 58           DynArr* create_array()
20             {
21 58           DynArr *arr = malloc(sizeof *arr);
22 58           arr->count = 0;
23 58           arr->max_size = 0;
24              
25 58           return arr;
26             }
27              
28 58           void destroy_array(DynArr* arr)
29             {
30 58 100         if (arr->max_size > 0) {
31 42           free(arr->ptr);
32             }
33              
34 58           free(arr);
35 58           }
36              
37 48           void clear_array(DynArr *arr)
38             {
39 48           arr->count = 0;
40              
41 48 100         if (arr->max_size >= MAX_SIZE_CLEAR) {
42 1           arr->max_size = 0;
43 1           free(arr->ptr);
44             }
45 48           }
46              
47 143           void push_array(DynArr *arr, void *ptr)
48             {
49 143 100         if (arr->count == arr->max_size) {
50 51 100         if (arr->max_size == 0) {
51 43           arr->max_size = MAX_SIZE_INITIAL;
52 43           arr->ptr = malloc(arr->max_size * sizeof *arr->ptr);
53             }
54             else {
55 8           arr->max_size *= MAX_SIZE_GROWTH;
56              
57 8           void *enlarged = realloc(arr->ptr, arr->max_size * sizeof *arr->ptr);
58             assert(enlarged != NULL);
59              
60 8           arr->ptr = enlarged;
61             }
62             }
63              
64 143           arr->ptr[arr->count] = ptr;
65 143           arr->count += 1;
66 143           }
67              
68 53           void adopt_object (QuadTreeRootNode *root, SV *value, Shape *s)
69             {
70 53           push_array(root->objects, value);
71 53           SvREFCNT_inc(value);
72 53           hv_store_ent(root->backref, value, newSViv((uintptr_t) s), 0);
73 53           }
74              
75 1           void disown_object (QuadTreeRootNode *root, SV *value)
76             {
77             int i;
78 1           DynArr* new_list = create_array();
79 33 100         for(i = 0; i < root->objects->count; ++i) {
80 32           SV *fetched = (SV*) root->objects->ptr[i];
81 32 100         if (!sv_eq(fetched, value)) {
82 31           push_array(new_list, fetched);
83             }
84             else {
85 1           SvREFCNT_dec(fetched);
86             }
87             }
88              
89 1           destroy_array(root->objects);
90 1           root->objects = new_list;
91              
92             /* NOTE: no shape destruction here, since "adopt_object" does not create it */
93 1           hv_delete_ent(root->backref, value, 0, 0);
94 1           }
95              
96 20           QuadTreeNode* create_nodes(int count, QuadTreeNode *parent)
97             {
98 20           QuadTreeNode *node = malloc(count * sizeof *node);
99              
100             int i;
101 88 100         for (i = 0; i < count; ++i) {
102 68           node[i].values = NULL;
103 68           node[i].children = NULL;
104 68           node[i].parent = parent;
105 68           node[i].has_objects = false;
106             }
107              
108 20           return node;
109             }
110              
111             /* NOTE: does not actually free the node, but frees its children nodes */
112 68           void destroy_node(QuadTreeNode *node)
113             {
114 68 100         if (node->values != NULL) {
115 52           destroy_array(node->values);
116             }
117             else {
118             int i;
119 80 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
120 64           destroy_node(&node->children[i]);
121             }
122              
123 16           free(node->children);
124             }
125 68           }
126              
127 0           void clear_has_objects (QuadTreeNode *node)
128             {
129 0 0         if (node->values == NULL) {
130             int i;
131 0 0         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
132 0 0         if (node->children[i].has_objects) return;
133             }
134             }
135              
136 0           node->has_objects = false;
137 0 0         if (node->parent != NULL) {
138 0           clear_has_objects(node->parent);
139             }
140             }
141              
142 4           QuadTreeRootNode* create_root()
143             {
144 4           QuadTreeRootNode *root = malloc(sizeof *root);
145 4           root->node = create_nodes(1, NULL);
146 4           root->backref = newHV();
147 4           root->objects = create_array();
148              
149 4           return root;
150             }
151              
152 68           void node_add_level(QuadTreeNode* node, double xmin, double ymin, double xmax, double ymax, int depth)
153             {
154 68           bool last = --depth == 0;
155              
156 68           node->xmin = xmin;
157 68           node->ymin = ymin;
158 68           node->xmax = xmax;
159 68           node->ymax = ymax;
160              
161 68 100         if (last) {
162 52           node->values = create_array();
163             }
164             else {
165 16           node->children = create_nodes(CHILDREN_PER_NODE, node);
166 16           double xmid = xmin + (xmax - xmin) / 2;
167 16           double ymid = ymin + (ymax - ymin) / 2;
168              
169 16           node_add_level(&node->children[0], xmin, ymin, xmid, ymid, depth);
170 16           node_add_level(&node->children[1], xmin, ymid, xmid, ymax, depth);
171 16           node_add_level(&node->children[2], xmid, ymin, xmax, ymid, depth);
172 16           node_add_level(&node->children[3], xmid, ymid, xmax, ymax, depth);
173             }
174 68           }
175              
176 1062           bool is_within_node(QuadTreeNode *node, Shape *s)
177             {
178 1062           switch (s->type) {
179 1023           case shape_rectangle: {
180 827 100         return (s->x <= node->xmax && s->x2 >= node->xmin)
181 1850 100         && (s->y <= node->ymax && s->y2 >= node->ymin);
    100          
    100          
182             }
183              
184 39           case shape_circle: {
185 78           double check_x = s->x < node->xmin
186 14           ? node->xmin - s->x
187 64 100         : s->x > node->xmax
188 3           ? node->xmax - s->x
189 25 100         : 0
190             ;
191              
192 78           double check_y = s->y < node->ymin
193 14           ? node->ymin - s->y
194 64 100         : s->y > node->ymax
195 3           ? node->ymax - s->y
196 25 100         : 0
197             ;
198              
199 39           return check_x * check_x + check_y * check_y <= s->radius_sq;
200             }
201             }
202 0           }
203              
204 608           void find_nodes(QuadTreeNode *node, HV *ret, Shape *param)
205             {
206 608 100         if (!node->has_objects || !is_within_node(node, param)) return;
    100          
207              
208             int i;
209              
210 270 100         if (node->values != NULL) {
211 284 100         for (i = 0; i < node->values->count; ++i) {
212 150           SV *fetched = (SV*) node->values->ptr[i];
213 150           SvREFCNT_inc(fetched);
214 150           hv_store_ent(ret, fetched, fetched, 0);
215             }
216             }
217             else {
218 680 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
219 544           find_nodes(&node->children[i], ret, param);
220             }
221             }
222             }
223              
224 465           bool fill_nodes (QuadTreeNode *node, SV *value, Shape *param)
225             {
226 465 100         if (!is_within_node(node, param)) return false;
227              
228 161 100         if (node->values != NULL) {
229 58           push_array(node->values, value);
230             }
231             else {
232             int i;
233 515 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
234 412           fill_nodes(&node->children[i], value, param);
235             }
236             }
237              
238             /* NOTE: only first level result is important, since if the object fits in
239             * the tree area at all, it must fit into one of the leaves */
240 161           node->has_objects = true;
241 161           return true;
242             }
243              
244 9           void delete_nodes(QuadTreeNode *node, SV *value, Shape *param)
245             {
246 9 50         if (!node->has_objects || !is_within_node(node, param)) return;
    100          
247              
248             int i;
249              
250 3 100         if (node->values != NULL) {
251 1           DynArr* new_list = create_array();
252              
253 3 100         for (i = 0; i < node->values->count; ++i) {
254 2           SV *fetched = (SV*) node->values->ptr[i];
255 2 100         if (!sv_eq(fetched, value)) {
256 1           push_array(new_list, fetched);
257             }
258             }
259              
260 1           destroy_array(node->values);
261 1           node->values = new_list;
262 1 50         if (new_list->count == 0) clear_has_objects(node);
263             }
264             else {
265 10 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
266 8           delete_nodes(&node->children[i], value, param);
267             }
268             }
269             }
270              
271 67           void clear_node(QuadTreeNode *node)
272             {
273 67 100         if (!node->has_objects) return;
274 56           node->has_objects = false;
275              
276 56 100         if (node->values != NULL) {
277 41           clear_array(node->values);
278             }
279             else {
280             int i;
281 75 100         for (i = 0; i < CHILDREN_PER_NODE; ++i) {
282 60           clear_node(&node->children[i]);
283             }
284             }
285             }
286              
287 7           void clear_tree(QuadTreeRootNode *root)
288             {
289 7           clear_node(root->node);
290              
291             int i;
292             char *key;
293             I32 retlen;
294             SV *value;
295              
296 7           hv_iterinit(root->backref);
297 59 100         while ((value = hv_iternextsv(root->backref, &key, &retlen)) != NULL) {
298 52           destroy_shape((Shape*) SvIV(value));
299             }
300              
301 59 100         for (i = 0; i < root->objects->count; ++i) {
302 52           SvREFCNT_dec((SV*) root->objects->ptr[i]);
303             }
304              
305 7           hv_clear(root->backref);
306 7           clear_array(root->objects);
307 7           }
308              
309             /* XS helpers */
310              
311 20           SV* get_hash_key (HV* hash, const char* key)
312             {
313 20           SV **value = hv_fetch(hash, key, strlen(key), 0);
314              
315             assert(value != NULL);
316 20           return *value;
317             }
318              
319 125           QuadTreeRootNode* get_root_from_perl(SV *self)
320             {
321 125           SV **value = hv_fetch((HV*) SvRV(self), "ROOT", 4, 0);
322 125 50         if (value == NULL)
323 0           croak("quad tree root node is undefined");
324              
325 125           return (QuadTreeRootNode*) SvIV(*value);
326             }
327              
328 64           AV* get_hash_values (HV* hash)
329             {
330 64           AV *ret = newAV();
331             HE *he;
332              
333 64           hv_iterinit(hash);
334 214 100         while ((he = hv_iternext(hash)) != NULL) {
335 150           SV *fetched = HeVAL(he);
336 150           SvREFCNT_inc(fetched);
337 150           av_push(ret, fetched);
338             }
339              
340 64           return ret;
341             }
342