File Coverage

XS.xs
Criterion Covered Total %
statement 141 178 79.2
branch 98 180 54.4
condition n/a
subroutine n/a
pod n/a
total 239 358 66.7


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 "ppport.h"
7              
8             #ifndef NOT_REACHED
9             # define NOT_REACHED assert(0)
10             #endif
11              
12             #define iParent(i) (((i)-1) / 2)
13             #define iLeftChild(i) ((2*(i)) + 1)
14             #define iRightChild(i) ((2*(i)) + 2)
15              
16             #define HAVE_PERL_SV_NUMCMP \
17             (PERL_REVISION > 5 || (PERL_REVISION == 5 && (PERL_VERSION > 43 || (PERL_VERSION == 43 && PERL_SUBVERSION >= 8))))
18              
19             #define OUT_OF_ORDER(a,child,parent,is_min) \
20             ( ( (is_min & 2) \
21             ? my_sv_string_gt(aTHX_ (a)[(child)], (a)[(parent)]) \
22             : my_sv_num_gt(aTHX_ (a)[(child)], (a)[(parent)])) \
23             ? !(is_min & 1) : (is_min & 1) )
24              
25             #define FORCE_SCALAR(fakeop) \
26             STMT_START { \
27             SAVEOP(); \
28             Copy(PL_op, &fakeop, 1, OP); \
29             fakeop.op_flags = OPf_WANT_SCALAR; \
30             PL_op = &fakeop; \
31             } STMT_END
32              
33              
34             #ifdef Perl_do_ncmp
35             #define my_Perl_do_ncmp Perl_do_ncmp
36             #else
37             /* compare left and right SVs. Returns:
38             * -1: <
39             * 0: ==
40             * 1: >
41             * 2: left or right was a NaN
42             */
43             I32
44 198           my_Perl_do_ncmp(pTHX_ SV* const left, SV * const right)
45             {
46             PERL_ARGS_ASSERT_DO_NCMP;
47             /* Fortunately it seems NaN isn't IOK */
48 198 50         if (SvIV_please_nomg(right) && SvIV_please_nomg(left)) {
    0          
    100          
    100          
    50          
    100          
49 191 100         if (!SvIsUV(left)) {
50 117           const IV leftiv = SvIVX(left);
51 117 50         if (!SvIsUV(right)) {
52             /* ## IV <=> IV ## */
53 117           const IV rightiv = SvIVX(right);
54 117           return (leftiv > rightiv) - (leftiv < rightiv);
55             }
56             /* ## IV <=> UV ## */
57 0 0         if (leftiv < 0)
58             /* As (b) is a UV, it's >=0, so it must be < */
59 0           return -1;
60             {
61 0           const UV rightuv = SvUVX(right);
62 0           return ((UV)leftiv > rightuv) - ((UV)leftiv < rightuv);
63             }
64             }
65              
66 74 50         if (SvIsUV(right)) {
67             /* ## UV <=> UV ## */
68 74           const UV leftuv = SvUVX(left);
69 74           const UV rightuv = SvUVX(right);
70 74           return (leftuv > rightuv) - (leftuv < rightuv);
71             }
72             /* ## UV <=> IV ## */
73             {
74 0           const IV rightiv = SvIVX(right);
75 0 0         if (rightiv < 0)
76             /* As (a) is a UV, it's >=0, so it cannot be < */
77 0           return 1;
78             {
79 0           const UV leftuv = SvUVX(left);
80 0           return (leftuv > (UV)rightiv) - (leftuv < (UV)rightiv);
81             }
82             }
83             NOT_REACHED; /* NOTREACHED */
84             }
85             {
86 7           NV const lnv = SvNV_nomg(left);
87 7           NV const rnv = SvNV_nomg(right);
88              
89             #if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
90             if (Perl_isnan(lnv) || Perl_isnan(rnv)) {
91             return 2;
92             }
93             return (lnv > rnv) - (lnv < rnv);
94             #else
95 7 100         if (lnv < rnv)
96 5           return -1;
97 2 50         if (lnv > rnv)
98 2           return 1;
99 0 0         if (lnv == rnv)
100 0           return 0;
101 0           return 2;
102             #endif
103             }
104             }
105             #endif
106              
107             static bool
108 841193           my_has_real_overload_method(pTHX_ SV *sv, const char *name, STRLEN len)
109             {
110             GV *gv;
111             CV *cv;
112             GV *cvgv;
113             HV *stash;
114             const HEK *gvhek;
115             const HEK *stashek;
116              
117 841193 50         if (!SvAMAGIC(sv) || !SvROK(sv)) {
    50          
    50          
    50          
118 0           return FALSE;
119             }
120              
121 841193           stash = SvSTASH(SvRV(sv));
122 841193 50         if (!stash) {
123 0           return FALSE;
124             }
125              
126 841193           gv = gv_fetchmeth_pvn(stash, name, len, -1, 0);
127 841193 100         if (!gv) {
128 560894           return FALSE;
129             }
130              
131 280299           cv = GvCV(gv);
132 280299 50         if (!cv) {
133 0           return FALSE;
134             }
135              
136 280299           cvgv = CvGV(cv);
137 280299 50         if (!cvgv) {
138 0           return TRUE;
139             }
140              
141 280299           gvhek = GvNAME_HEK(cvgv);
142 280299 50         stashek = HvNAME_HEK(GvSTASH(cvgv));
    50          
    50          
143 280299 50         if (!gvhek || !stashek) {
    50          
144 0           return TRUE;
145             }
146              
147 560598 50         return !(stashek
148 280299 50         && memEQs(HEK_KEY(gvhek), HEK_LEN(gvhek), "nil")
    0          
149 0 0         && memEQs(HEK_KEY(stashek), HEK_LEN(stashek), "overload"));
    0          
150             }
151              
152             static SV *
153 544           my_sv_2num(pTHX_ SV *sv)
154             {
155 544 100         if (!SvROK(sv)) {
156 396           return sv;
157             }
158              
159 148 50         if (SvAMAGIC(sv)) {
    50          
    50          
160 148           SV *tmpsv = AMG_CALLunary(sv, numer_amg);
161 148 50         if (tmpsv && (!SvROK(tmpsv) || SvRV(tmpsv) != SvRV(sv))) {
    50          
    0          
162 148           return my_sv_2num(aTHX_ tmpsv);
163             }
164             }
165              
166 0           return sv_2mortal(newSVuv(PTR2UV(SvRV(sv))));
167             }
168              
169             static bool
170 69           my_sv_string_gt(pTHX_ SV *left, SV *right)
171             {
172 69           SV *tmpsv = NULL;
173              
174 69 50         if (SvAMAGIC(left) || SvAMAGIC(right)) {
    50          
    50          
    0          
    0          
    0          
175 69 50         if (my_has_real_overload_method(aTHX_ left, "(gt", 3)
176 69 50         || my_has_real_overload_method(aTHX_ right, "(gt", 3)) {
177 0           tmpsv = amagic_call(left, right, sgt_amg, 0);
178 0 0         if (tmpsv) {
179 0           return SvTRUE(tmpsv);
180             }
181             }
182 69 50         if (my_has_real_overload_method(aTHX_ left, "(cmp", 4)
183 0 0         || my_has_real_overload_method(aTHX_ right, "(cmp", 4)) {
184 69           tmpsv = amagic_call(left, right, scmp_amg, 0);
185 69 50         if (tmpsv) {
186 69           return SvIV(tmpsv) > 0;
187             }
188             }
189             }
190              
191 0           return Perl_sv_cmp(aTHX_ left, right) > 0;
192             }
193              
194             static bool
195 280428           my_sv_num_gt(pTHX_ SV *left, SV *right)
196             {
197             #if HAVE_PERL_SV_NUMCMP
198             return sv_numcmp(left, right) > 0;
199             #else
200 280428 100         if (SvAMAGIC(left) || SvAMAGIC(right)) {
    50          
    50          
    50          
    0          
    0          
201 280304           SV *tmpsv = NULL;
202              
203 280304 50         if (my_has_real_overload_method(aTHX_ left, "(>", 2)
204 280304 50         || my_has_real_overload_method(aTHX_ right, "(>", 2)) {
205 0           tmpsv = amagic_call(left, right, gt_amg, 0);
206 0 0         if (tmpsv) {
207 0           return SvTRUE(tmpsv);
208             }
209             }
210              
211 280304 100         if (my_has_real_overload_method(aTHX_ left, "(<=>", 4)
212 74 50         || my_has_real_overload_method(aTHX_ right, "(<=>", 4)) {
213 280230           tmpsv = amagic_call(left, right, ncmp_amg, 0);
214 280230 50         if (tmpsv) {
215 280230           return SvIV(tmpsv) > 0;
216             }
217             }
218              
219             }
220              
221 198           left = my_sv_2num(aTHX_ left);
222 198           right = my_sv_2num(aTHX_ right);
223              
224 198           return my_Perl_do_ncmp(aTHX_ left, right) > 0;
225             #endif
226             }
227              
228              
229 16           I32 sift_up(pTHX_ SV **a, ssize_t start, ssize_t end, I32 is_min) {
230             /*start represents the limit of how far up the heap to sift.
231             end is the node to sift up. */
232 16           ssize_t child = end;
233 16           I32 swapped = 0;
234 16           SvGETMAGIC(a[child]);
235              
236 43 100         while (child > start) {
237 33           ssize_t parent = iParent(child);
238 33           SvGETMAGIC(a[parent]);
239 33 100         if ( OUT_OF_ORDER(a,child,parent,is_min) ) {
    100          
    100          
240 27           SV *swap_tmp= a[parent];
241 27           a[parent]= a[child];
242 27           a[child]= swap_tmp;
243              
244 27           child = parent; /* repeat to continue sifting up the parent now */
245 27           swapped++;
246             }
247             else {
248 6           return swapped;
249             }
250             }
251 10           return swapped;
252             }
253              
254             /*Repair the heap whose root element is at index 'start', assuming the heaps rooted at its children are valid*/
255 75534           I32 sift_down(pTHX_ SV **a, ssize_t start, ssize_t end, I32 is_min) {
256 75534           ssize_t root = start;
257 75534           I32 swapped = 0;
258              
259 186381 100         while (iLeftChild(root) <= end) { /* While the root has at least one child */
260 140548           ssize_t child = iLeftChild(root); /* Left child of root */
261 140548           ssize_t swap = root; /* Keeps track of child to swap with */
262              
263             /* if the root is smaller than the left child
264             * then the swap is with the left child */
265 140548 100         if ( OUT_OF_ORDER(a,child,swap,is_min) ) {
    100          
    100          
266 94333           swap = child;
267             }
268             /* if there is a right child and the right child is larger than the root or the left child
269             * then the swap is with the right child */
270 140548 100         if (child+1 <= end) {
271 139916 100         if ( OUT_OF_ORDER(a,child+1,swap,is_min) ) {
    100          
    100          
272 54889           swap = child + 1;
273             }
274             }
275             /* check if we need to swap or if this tree is in heap-order */
276 140548 100         if (swap == root) {
277             /* The root is larger than both children, and as we assume the heaps rooted at the children are valid
278             * then we know we can stop. */
279 29701           return swapped;
280             } else {
281             /* swap the root with the largest child */
282 110847           SV *tmp= a[root];
283 110847           a[root]= a[swap];
284 110847           a[swap]= tmp;
285             /* continue sifting down the child by setting the root to the chosen child
286             * effectively we sink down the tree towards the leafs */
287 110847           root = swap;
288 110847           swapped++;
289             }
290             }
291 45833           return swapped;
292             }
293              
294             /* this is O(N log N) */
295 0           void heapify_with_sift_up(pTHX_ SV **a, ssize_t count, I32 is_min) {
296 0           ssize_t end = 1; /* end is assigned the index of the first (left) child of the root */
297              
298 0 0         while (end < count) {
299             /*sift up the node at index end to the proper place such that all nodes above
300             the end index are in heap order */
301 0           (void)sift_up(aTHX_ a, 0, end, is_min);
302 0           end++;
303             }
304             /* after sifting up the last node all nodes are in heap order */
305 0           }
306              
307             /* this is O(N) */
308 324           void heapify_with_sift_down(pTHX_ SV **a, ssize_t count, I32 is_min) {
309             /*start is assigned the index in 'a' of the last parent node
310             the last element in a 0-based array is at index count-1; find the parent of that element */
311 324           ssize_t start = iParent(count-1);
312              
313 75508 100         while (start >= 0) {
314             /* sift down the node at index 'start' to the proper place such that all nodes below
315             the start index are in heap order */
316 75184           (void)sift_down(aTHX_ a, start, count - 1, is_min);
317             /* go to the next parent node */
318 75184           start--;
319             }
320             /* after sifting down the root all nodes/elements are in heap order */
321 324           }
322              
323              
324             MODULE = Algorithm::Heapify::XS PACKAGE = Algorithm::Heapify::XS
325              
326             void
327             max_heapify(av)
328             AV *av
329             PROTOTYPE: \@
330             ALIAS:
331             max_heapify = 0
332             min_heapify = 1
333             maxstr_heapify = 2
334             minstr_heapify = 3
335             PREINIT:
336             OP fakeop;
337             I32 count;
338             PPCODE:
339 324           FORCE_SCALAR(fakeop);
340 324 50         count = av_top_index(av)+1;
341 324 50         if ( count ) {
342 324           heapify_with_sift_down(aTHX_ AvARRAY(av),count,ix);
343 324           ST(0)= AvARRAY(av)[0];
344 324           XSRETURN(1);
345             }
346             else {
347 0           XSRETURN(0);
348             }
349              
350             void
351             max_heap_shift(av)
352             AV *av
353             PROTOTYPE: \@
354             ALIAS:
355             max_heap_shift = 0
356             min_heap_shift = 1
357             maxstr_heap_shift = 2
358             minstr_heap_shift = 3
359             PREINIT:
360             OP fakeop;
361             I32 top;
362             I32 count;
363             PPCODE:
364 390           FORCE_SCALAR(fakeop);
365 390 50         top= av_top_index(av);
366 390           count= top+1;
367 390 50         if (count) {
368 390           SV *tmp= AvARRAY(av)[0];
369 390           AvARRAY(av)[0]= AvARRAY(av)[top];
370 390           AvARRAY(av)[top]= tmp;
371 390           ST(0)= av_pop(av);
372 390 100         if (count > 2)
373 342           sift_down(aTHX_ AvARRAY(av),0,top-1,ix);
374 390           XSRETURN(1);
375             }
376             else {
377 0           XSRETURN(0);
378             }
379              
380             void
381             max_heap_push(av,sv)
382             AV *av
383             SV *sv
384             PROTOTYPE: \@$
385             ALIAS:
386             max_heap_push = 0
387             min_heap_push = 1
388             maxstr_heap_push = 2
389             minstr_heap_push = 3
390             PREINIT:
391             OP fakeop;
392             I32 top;
393             I32 count;
394             PPCODE:
395 8           FORCE_SCALAR(fakeop);
396 8           av_push(av,newSVsv(sv));
397 8 50         top= av_top_index(av);
398 8           count= top+1;
399 8           sift_up(aTHX_ AvARRAY(av),0,top,ix);
400 8           ST(0)= AvARRAY(av)[0];
401 8           XSRETURN(1);
402              
403             void
404             max_heap_adjust_top(av)
405             AV *av
406             PROTOTYPE: \@
407             ALIAS:
408             max_heap_adjust_top = 0
409             min_heap_adjust_top = 1
410             maxstr_heap_adjust_top = 2
411             minstr_heap_adjust_top = 3
412             PREINIT:
413             OP fakeop;
414             I32 top;
415             I32 count;
416             PPCODE:
417 6           FORCE_SCALAR(fakeop);
418 6 50         top= av_top_index(av);
419 6           count= top+1;
420 6 50         if ( count ) {
421 6           (void)sift_down(aTHX_ AvARRAY(av),0,top,ix);
422 6           ST(0)= AvARRAY(av)[0];
423 6           XSRETURN(1);
424             } else {
425 0           XSRETURN(0);
426             }
427              
428             void
429             max_heap_adjust_item(av,idx=0)
430             AV *av
431             I32 idx;
432             PROTOTYPE: \@;$
433             ALIAS:
434             max_heap_adjust_item = 0
435             min_heap_adjust_item = 1
436             maxstr_heap_adjust_item = 2
437             minstr_heap_adjust_item = 3
438             PREINIT:
439             OP fakeop;
440             I32 top;
441             I32 count;
442             PPCODE:
443 8           FORCE_SCALAR(fakeop);
444 8 50         top= av_top_index(av);
445 8           count= top+1;
446 8 50         if ( idx < count ) {
447 8 50         if (!idx || !sift_up(aTHX_ AvARRAY(av),0,idx,ix))
    100          
448 2           (void)sift_down(aTHX_ AvARRAY(av),idx,top,ix);
449 8           ST(0)= AvARRAY(av)[0];
450 8           XSRETURN(1);
451             } else {
452 0           XSRETURN(0);
453             }