File Coverage

lib/Syntax/Operator/Zip.xs
Criterion Covered Total %
statement 74 78 94.8
branch 45 60 75.0
condition n/a
subroutine n/a
pod n/a
total 119 138 86.2


line stmt bran cond sub pod time code
1             /* You may distribute under the terms of either the GNU General Public License
2             * or the Artistic License (the same terms as Perl itself)
3             *
4             * (C) Paul Evans, 2021-2024 -- leonerd@leonerd.org.uk
5             */
6             #include "EXTERN.h"
7             #include "perl.h"
8             #include "XSUB.h"
9              
10             #define HAVE_PERL_VERSION(R, V, S) \
11             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
12              
13             #include "XSParseInfix.h"
14              
15             #if !HAVE_PERL_VERSION(5, 16, 0)
16             # define true TRUE
17             # define false FALSE
18             #endif
19              
20 100010           static OP *pp_zip(pTHX)
21             {
22 100010           dSP;
23 100010 50         int nlists = (PL_op->op_flags & OPf_STACKED) ? POPu : PL_op->op_private;
24              
25             /* Most invocations will only have 2 lists. We'll account for up to 4 as
26             * local variables; anything bigger we'll allocate temporary SV buffers
27             */
28             U32 counts4[4];
29             SV **svp4[4];
30              
31             U32 maxcount = 0;
32 100010 50         U32 *counts = nlists <= 4 ? counts4 : (U32 *)SvPVX(sv_2mortal(newSV(nlists * sizeof(U32))));
33 300032 100         for(int i = nlists; i > 0; i--) {
34 200022           U32 mark = POPMARK;
35 200022           U32 count = SP - (PL_stack_base + mark);
36 200022           counts[i-1] = count;
37 200022           if(count > maxcount)
38             maxcount = count;
39              
40 200022           SP = PL_stack_base + mark;
41             }
42              
43 100010 50         if(GIMME_V == G_VOID)
44 0           return NORMAL;
45 100010 100         if(GIMME_V == G_SCALAR) {
46 3 50         EXTEND(SP, 1);
47 3           mPUSHi(maxcount);
48 3           RETURN;
49             }
50              
51             /* known G_LIST */
52              
53             /* No need to EXTEND because we know the stack will be big enough */
54 100007 50         PUSHMARK(SP);
55              
56 100007 50         if(!maxcount)
57 0           RETURN;
58              
59 100007 50         SV ***svp = nlists <= 4 ? svp4 : (SV ***)SvPVX(sv_2mortal(newSV(nlists * sizeof(SV **))));
60 100007           svp[0] = SP + 1;
61 200016 100         for(int i = 1; i < nlists; i++)
62 100009           svp[i] = svp[i-1] + counts[i-1];
63              
64             bool more = true;
65             do {
66             more = false;
67 500019           AV *av = newAV();
68 1500061 100         for(int i = 0; i < nlists; i++) {
69 1000042 100         if(counts[i]) {
70 1000040           av_push(av, newSVsv(*(svp[i])));
71 1000040           svp[i]++, counts[i]--;
72              
73 1000040 100         if(counts[i])
74             more = true;
75             }
76             else
77 2           av_push(av, &PL_sv_undef);
78             }
79 500019           mPUSHs(newRV_noinc((SV *)av));
80 500019 100         } while(more);
81              
82 100007           RETURN;
83             }
84              
85             static const struct XSParseInfixHooks infix_zip = {
86             /* Parse this at ADD precedence, so that (LIST)xCOUNT can be used on RHS */
87             .cls = XPI_CLS_ADD_MISC,
88             .flags = XPI_FLAG_LISTASSOC,
89             .lhs_flags = XPI_OPERAND_TERM_LIST|XPI_OPERAND_ONLY_LOOK,
90             .rhs_flags = XPI_OPERAND_TERM_LIST|XPI_OPERAND_ONLY_LOOK,
91             .permit_hintkey = "Syntax::Operator::Zip/Z",
92              
93             .wrapper_func_name = "Syntax::Operator::Zip::zip",
94              
95             .ppaddr = &pp_zip,
96             };
97              
98 10           static OP *pp_mesh(pTHX)
99             {
100 10           dSP;
101 10 50         int nlists = (PL_op->op_flags & OPf_STACKED) ? POPu : PL_op->op_private;
102              
103             /* Most invocations will only have 2 lists. We'll account for up to 4 as
104             * local variables; anything bigger we'll allocate temporary SV buffers
105             */
106             U32 counts4[4];
107             SV **svp4[4];
108              
109             U32 maxcount = 0;
110 10 50         U32 *counts = nlists <= 4 ? counts4 : (U32 *)SvPVX(sv_2mortal(newSV(nlists * sizeof(U32))));
111 32 100         for(int i = nlists; i > 0; i--) {
112 22           U32 mark = POPMARK;
113 22           U32 count = SP - (PL_stack_base + mark);
114 22           counts[i-1] = count;
115 22           if(count > maxcount)
116             maxcount = count;
117              
118 22           SP = PL_stack_base + mark;
119             }
120              
121 10           U32 retcount = maxcount * nlists;
122              
123 10 50         if(GIMME_V == G_VOID)
124 0           return NORMAL;
125 10 100         if(GIMME_V == G_SCALAR) {
126 3 50         EXTEND(SP, 1);
127 3           mPUSHi(retcount);
128 3           RETURN;
129             }
130              
131             /* known G_LIST */
132 7 50         EXTEND(SP, retcount);
133 7 50         PUSHMARK(SP);
134              
135 7 50         if(!retcount)
136 0           RETURN;
137              
138 7 50         SV ***svp = nlists <= 4 ? svp4 : (SV ***)SvPVX(sv_2mortal(newSV(nlists * sizeof(SV **))));
139 7           svp[0] = SP + 1;
140 16 100         for(int i = 1; i < nlists; i++)
141 9           svp[i] = svp[i-1] + counts[i-1];
142              
143             /* We can't easily do this inplace so we'll have to store the result in a
144             * temporary array
145             */
146 7           AV *tmpav = newAV();
147 7           sv_2mortal((SV *)tmpav);
148 7           av_extend(tmpav, retcount - 1);
149              
150 7           SV **result = AvARRAY(tmpav);
151              
152             bool more = true;
153             do {
154             more = false;
155 61 100         for(int i = 0; i < nlists; i++) {
156 42 100         if(counts[i]) {
157 40           *result = sv_mortalcopy(*(svp[i]));
158 40           svp[i]++, counts[i]--;
159              
160 40 100         if(counts[i])
161             more = true;
162             }
163             else
164 2           *result = &PL_sv_undef;
165 42           result++;
166             }
167 19 100         } while(more);
168              
169 7           result = AvARRAY(tmpav);
170 49 100         for(U32 i = 0; i < retcount; i++)
171 42           PUSHs(*result++);
172              
173 7           AvREAL_off(tmpav); // AV shouldn't own the SVs
174 7           RETURN;
175             }
176              
177             static const struct XSParseInfixHooks infix_mesh = {
178             .cls = XPI_CLS_ADD_MISC,
179             .flags = XPI_FLAG_LISTASSOC,
180             .lhs_flags = XPI_OPERAND_TERM_LIST|XPI_OPERAND_ONLY_LOOK,
181             .rhs_flags = XPI_OPERAND_TERM_LIST|XPI_OPERAND_ONLY_LOOK,
182             .permit_hintkey = "Syntax::Operator::Zip/M",
183              
184             .wrapper_func_name = "Syntax::Operator::Zip::mesh",
185              
186             .ppaddr = &pp_mesh,
187             };
188              
189             MODULE = Syntax::Operator::Zip PACKAGE = Syntax::Operator::Zip
190              
191             BOOT:
192 5           boot_xs_parse_infix(0.40);
193              
194 5           register_xs_parse_infix("Z", &infix_zip, NULL);
195 5           register_xs_parse_infix("M", &infix_mesh, NULL);