File Coverage

AVLTree.xs
Criterion Covered Total %
statement 88 103 85.4
branch 91 244 37.3
condition n/a
subroutine n/a
pod n/a
total 179 347 51.5


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4              
5             /*
6             From http://blogs.perl.org/users/nick_wellnhofer/2015/03/writing-xs-like-a-pro---perl-no-get-context-and-static-functions.html
7             The perlxs man page recommends to define the PERL_NO_GET_CONTEXT macro before including EXTERN.h, perl.h, and XSUB.h.
8             If this macro is defined, it is assumed that the interpreter context is passed as a parameter to every function.
9             If it's undefined, the context will typically be fetched from thread-local storage when calling the Perl API, which
10             incurs a performance overhead.
11            
12             WARNING:
13            
14             setting this macro involves additional changes to the XS code. For example, if the XS file has static functions that
15             call into the Perl API, you'll get somewhat cryptic error messages like the following:
16              
17             /usr/lib/i386-linux-gnu/perl/5.20/CORE/perl.h:155:16: error: ‘my_perl’ undeclared (first use in this function)
18             # define aTHX my_perl
19              
20             See http://perldoc.perl.org/perlguts.html#How-do-I-use-all-this-in-extensions? for ways in which to avoid these
21             errors when using the macro.
22              
23             One way is to begin each static function that invoke the perl API with the dTHX macro to fetch context. This is
24             used in the following static functions.
25             Another more efficient approach is to prepend pTHX_ to the argument list in the declaration of each static
26             function and aTHX_ when each of these functions are invoked. This is used directly in the AVL tree library
27             source code.
28             */
29             #define PERL_NO_GET_CONTEXT
30            
31             #ifdef ENABLE_DEBUG
32             #define TRACEME(x) do { \
33             if (SvTRUE(perl_get_sv("AVLTree::ENABLE_DEBUG", TRUE))) \
34             { PerlIO_stdoutf (x); PerlIO_stdoutf ("\n"); } \
35             } while (0)
36             #else
37             #define TRACEME(x)
38             #endif
39            
40             #include "EXTERN.h"
41             #include "perl.h"
42             #include "XSUB.h"
43            
44             #include "ppport.h"
45            
46             #include "avltree.h"
47            
48             #ifdef __cplusplus
49             }
50             #endif
51              
52             typedef avltree_t AVLTree;
53             typedef avltrav_t AVLTrav;
54              
55             /* C-level callbacks required by the AVL tree library */
56              
57             static SV* callback = (SV*)NULL;
58              
59 464           static int svcompare(SV *p1, SV *p2) {
60             /*
61             This is one way to avoid the above mentioned error when
62             declaring the PERL_NO_GET_CONTEXT macro
63             */
64             dTHX;
65            
66             int cmp;
67            
68 464           dSP;
69             int count;
70              
71             //ENTER;
72             //SAVETMPS;
73            
74 464 50         PUSHMARK(SP);
75 464 50         XPUSHs(sv_2mortal(newSVsv(p1)));
76 464 50         XPUSHs(sv_2mortal(newSVsv(p2)));
77 464           PUTBACK;
78            
79             /* Call the Perl sub to process the callback */
80 464           count = call_sv(callback, G_SCALAR);
81              
82 464           SPAGAIN;
83              
84 464 50         if(count != 1)
85 0           croak("Did not return a value\n");
86            
87 464 50         cmp = POPi;
88 464           PUTBACK;
89              
90             //FREETMPS;
91             //LEAVE;
92              
93 464           return cmp;
94             }
95              
96 96           static SV* svclone(SV* p) {
97             dTHX; /* fetch context */
98            
99 96           return newSVsv(p);
100             }
101              
102 96           void svdestroy(SV* p) {
103             dTHX; /* fetch context */
104            
105 96           SvREFCNT_dec(p);
106 96           }
107              
108             /*====================================================================
109             * XS SECTION
110             *====================================================================*/
111              
112             MODULE = AVLTree PACKAGE = AVLTree
113              
114             void
115             new ( class, cmp_fn )
116             char* class
117             SV* cmp_fn
118             PROTOTYPE: $$
119             PREINIT:
120             AVLTree* tree;
121             AVLTrav* trav;
122             PPCODE:
123             {
124             SV* self;
125 20           HV* hash = newHV();
126              
127 20 50         TRACEME("Registering callback for comparison");
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
128 20 100         if(callback == (SV*)NULL)
129 3           callback = newSVsv(cmp_fn);
130             else
131 17 50         SvSetSV(callback, cmp_fn);
132            
133 20 50         TRACEME("Allocating AVL tree");
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
134 20           tree = avltree_new(svcompare, svclone, svdestroy);
135 20 50         if(tree == NULL)
136 0           croak("Unable to allocate AVL tree");
137 20           hv_store(hash, "tree", 4, newSViv(PTR2IV(tree)), 0);
138              
139 20 50         TRACEME("Allocating AVL tree traversal");
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
140 20           trav = avltnew();
141 20 50         if(trav == NULL)
142 0           croak("Unable to allocate AVL tree traversal");
143 20           hv_store(hash, "trav", 4, newSViv(PTR2IV(trav)), 0);
144            
145 20           self = newRV_noinc((SV*)hash);;
146 20           sv_2mortal(self);
147 20           sv_bless(self, gv_stashpv(class, FALSE));
148            
149 20           PUSHs(self);
150 20           XSRETURN(1);
151             }
152              
153             SV*
154             find(self, ...)
155             SV* self
156             PREINIT:
157             AVLTree* tree;
158             INIT:
159 14 100         if(items < 2 || !SvOK(ST(1)) || SvTYPE(ST(1)) == SVt_NULL) {
    100          
    50          
    50          
    50          
160 4           XSRETURN_UNDEF;
161             }
162             CODE:
163             // get tree pointer
164 10           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
165 10 50         if(svp == NULL)
166 0           croak("Unable to access tree\n");
167 10 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
168              
169 10           SV* result = avltree_find(aTHX_ tree, ST(1));
170 10 100         if(SvOK(result) && SvTYPE(result) != SVt_NULL) {
    50          
    50          
    50          
171             /* WARN: if it's mortalised e.g. sv_2mortal(...)? returns "Attempt to free unreferenced scalar: SV" */
172 6           RETVAL = newSVsv(result);
173             } else
174 4           XSRETURN_UNDEF;
175             OUTPUT:
176             RETVAL
177              
178             int
179             insert(self, item)
180             SV* self
181             SV* item
182             PROTOTYPE: $$
183             PREINIT:
184             AVLTree* tree;
185             CODE:
186 96           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
187 96 50         if(svp == NULL)
188 0           croak("Unable to access tree\n");
189 96 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
190            
191 96           RETVAL = avltree_insert(tree, item);
192              
193             OUTPUT:
194             RETVAL
195              
196             int
197             remove(self, item)
198             SV* self
199             SV* item
200             PROTOTYPE: $$
201             PREINIT:
202             AVLTree* tree;
203             CODE:
204 12           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
205 12 50         if(svp == NULL)
206 0           croak("Unable to access tree\n");
207 12 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
208              
209 12           RETVAL = avltree_erase(tree, item);
210              
211             OUTPUT:
212             RETVAL
213              
214             int
215             size(self)
216             SV* self
217             PROTOTYPE: $
218             PREINIT:
219             AVLTree* tree;
220             CODE:
221 8           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
222 8 50         if(svp == NULL)
223 0           croak("Unable to access tree\n");
224 8 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
225            
226 8           RETVAL = avltree_size(tree);
227             OUTPUT:
228             RETVAL
229              
230             SV*
231             first(self)
232             SV* self
233             PROTOTYPE: $
234             PREINIT:
235             AVLTree* tree;
236             AVLTrav* trav;
237             CODE:
238 5           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
239 5 50         if(svp == NULL)
240 0           croak("Unable to access tree\n");
241 5 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
242 5           svp = hv_fetch((HV*)SvRV(self), "trav", 4, 0);
243 5 50         if(svp == NULL)
244 0           croak("Unable to access tree traversal\n");
245 5 50         trav = INT2PTR(AVLTrav*, SvIV(*svp));
246              
247 5           RETVAL = newSVsv(avltfirst(aTHX_ trav, tree));
248              
249             OUTPUT:
250             RETVAL
251              
252             SV*
253             last(self)
254             SV* self
255             PROTOTYPE: $
256             PREINIT:
257             AVLTree* tree;
258             AVLTrav* trav;
259             CODE:
260 3           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
261 3 50         if(svp == NULL)
262 0           croak("Unable to access tree\n");
263 3 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
264 3           svp = hv_fetch((HV*)SvRV(self), "trav", 4, 0);
265 3 50         if(svp == NULL)
266 0           croak("Unable to access tree traversal\n");
267 3 50         trav = INT2PTR(AVLTrav*, SvIV(*svp));
268              
269 3           RETVAL = newSVsv(avltlast(aTHX_ trav, tree));
270              
271             OUTPUT:
272             RETVAL
273              
274             SV*
275             next(self)
276             SV* self
277             PROTOTYPE: $
278             PREINIT:
279             AVLTree* tree;
280             AVLTrav* trav;
281             CODE:
282 22           SV** svp = hv_fetch((HV*)SvRV(self), "trav", 4, 0);
283 22 50         if(svp == NULL)
284 0           croak("Unable to access tree traversal\n");
285 22 50         trav = INT2PTR(AVLTrav*, SvIV(*svp));
286              
287 22           RETVAL = newSVsv(avltnext(aTHX_ trav));
288              
289             OUTPUT:
290             RETVAL
291              
292             SV*
293             prev(self)
294             SV* self
295             PROTOTYPE: $
296             PREINIT:
297             AVLTree* tree;
298             AVLTrav* trav;
299             CODE:
300 10           SV** svp = hv_fetch((HV*)SvRV(self), "trav", 4, 0);
301 10 50         if(svp == NULL)
302 0           croak("Unable to access tree traversal\n");
303 10 50         trav = INT2PTR(AVLTrav*, SvIV(*svp));
304              
305 10           RETVAL = newSVsv(avltprev(aTHX_ trav));
306              
307             OUTPUT:
308             RETVAL
309              
310             void DESTROY(self)
311             SV* self
312             PROTOTYPE: $
313             PREINIT:
314             AVLTree* tree;
315             AVLTrav* trav;
316             CODE:
317 20 50         TRACEME("Deleting AVL tree");
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
318 20           SV** svp = hv_fetch((HV*)SvRV(self), "tree", 4, 0);
319 20 50         if(svp == NULL)
320 0           croak("Unable to access tree\n");
321 20 50         tree = INT2PTR(AVLTree*, SvIV(*svp));
322 20           avltree_delete(tree);
323              
324 20 50         TRACEME("Deleting AVL tree traversal");
    50          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
325 20           svp = hv_fetch((HV*)SvRV(self), "trav", 4, 0);
326 20 50         if(svp == NULL)
327 0           croak("Unable to access tree traversal\n");
328 20 50         trav = INT2PTR(AVLTrav*, SvIV(*svp));
329 20           avltdelete(trav);
330              
331              
332              
333