File Coverage

src/Array.c
Criterion Covered Total %
statement 44 51 86.2
branch 16 42 38.1
condition n/a
subroutine n/a
pod n/a
total 60 93 64.5


line stmt bran cond sub pod time code
1             enum ArraySource {
2             ARRAY_SRC_INVOCANT,
3             ARRAY_SRC_DEREF_SCALAR,
4             ARRAY_SRC_DEREF_ARRAY,
5             ARRAY_SRC_DEREF_HASH,
6             ARRAY_SRC_CALL_METHOD,
7             };
8              
9             /* SIMPLE SIG: all, count, pop, shift, etc */
10             typedef struct {
11             char* name;
12             enum ArraySource arr_source;
13             char* arr_source_string;
14             char* arr_source_fallback;
15             I32 arr_source_index;
16             enum ReturnPattern method_return_pattern;
17             char* method_return_class;
18             char* method_return_constructor;
19             } shvxs_array_SIMPLE_SIG;
20              
21             /* NEW ELEMS SIG: push, unshift, etc (add new elements to arrayref, so need a type) */
22             typedef struct {
23             char* name;
24             enum ArraySource arr_source;
25             char* arr_source_string;
26             char* arr_source_fallback;
27             I32 arr_source_index;
28             I32 element_type;
29             CV* element_type_cv;
30             SV* element_type_tiny;
31             bool has_element_type_tiny;
32             CV* element_coercion_cv;
33             enum ReturnPattern method_return_pattern;
34             char* method_return_class;
35             char* method_return_constructor;
36             } shvxs_array_NEW_ELEMS_SIG;
37              
38             /* CODEREF SIG: for_each, map, grep, etc (methods which accept a potentially curried coderef) */
39             typedef struct {
40             char* name;
41             enum ArraySource arr_source;
42             char* arr_source_string;
43             char* arr_source_fallback;
44             I32 arr_source_index;
45             CV* callback;
46             enum ReturnPattern method_return_pattern;
47             char* method_return_class;
48             char* method_return_constructor;
49             } shvxs_array_CALLBACK_SIG;
50              
51             /* INDEX SIG: get, etc (methods which accept a potentially curried index) */
52             typedef struct {
53             char* name;
54             enum ArraySource arr_source;
55             char* arr_source_string;
56             char* arr_source_fallback;
57             I32 arr_source_index;
58             bool has_index;
59             I32 index;
60             enum ReturnPattern method_return_pattern;
61             char* method_return_class;
62             char* method_return_constructor;
63             } shvxs_array_INDEX_SIG;
64              
65             /* SV SIG: join, etc (methods which accept a potentially curried scalar) */
66             typedef struct {
67             char* name;
68             enum ArraySource arr_source;
69             char* arr_source_string;
70             char* arr_source_fallback;
71             I32 arr_source_index;
72             bool has_curried_sv;
73             SV* curried_sv;
74             enum ReturnPattern method_return_pattern;
75             char* method_return_class;
76             char* method_return_constructor;
77             } shvxs_array_SV_SIG;
78              
79             /* SETTER SIG: accessor, set (combination of NEW_ELEMS, INDEX, and SV) */
80             typedef struct {
81             char* name;
82             enum ArraySource arr_source;
83             char* arr_source_string;
84             char* arr_source_fallback;
85             I32 arr_source_index;
86             bool has_index;
87             I32 index;
88             bool has_curried_sv;
89             SV* curried_sv;
90             I32 element_type;
91             CV* element_type_cv;
92             SV* element_type_tiny;
93             bool has_element_type_tiny;
94             CV* element_coercion_cv;
95             enum ReturnPattern method_return_pattern;
96             char* method_return_class;
97             char* method_return_constructor;
98             } shvxs_array_SETTER_SIG;
99              
100             #define GET_ARRAY_FROM_SOURCE \
101             AV *array = NULL; \
102             AV *out = NULL; \
103             SV *val = NULL; \
104             STMT_START { \
105             SV *tmp; \
106             \
107             switch (sig->arr_source) { \
108             case ARRAY_SRC_INVOCANT: \
109             if (!SvROK(invocant) || \
110             SvTYPE(SvRV(invocant)) != SVt_PVAV) \
111             croak("Invocant is not an array reference"); \
112             array = (AV *)SvRV(invocant); \
113             break; \
114             \
115             case ARRAY_SRC_DEREF_SCALAR: \
116             if (!SvROK(invocant) || \
117             !SvROK(SvRV(invocant)) || \
118             SvTYPE(SvRV(SvRV(invocant))) != SVt_PVAV) \
119             croak("Invocant is not a scalar ref to array"); \
120             array = (AV *)SvRV(SvRV(invocant)); \
121             break; \
122             \
123             case ARRAY_SRC_DEREF_ARRAY: \
124             if (!SvROK(invocant) || \
125             SvTYPE(SvRV(invocant)) != SVt_PVAV) \
126             croak("Invocant is not an array reference"); \
127             tmp = *av_fetch((AV *)SvRV(invocant), \
128             sig->arr_source_index, 0); \
129             if (!tmp || !SvROK(tmp) || \
130             SvTYPE(SvRV(tmp)) != SVt_PVAV) \
131             croak("Array element is not an array ref"); \
132             array = (AV *)SvRV(tmp); \
133             break; \
134             \
135             case ARRAY_SRC_DEREF_HASH: { \
136             if (!SvROK(invocant) || \
137             SvTYPE(SvRV(invocant)) != SVt_PVHV) \
138             croak("Invocant is not a hash reference"); \
139             if (sig->arr_source_fallback && \
140             !hv_exists((HV *)SvRV(invocant), \
141             sig->arr_source_string, \
142             strlen(sig->arr_source_string))) { \
143             dSP; \
144             ENTER; \
145             SAVETMPS; \
146             PUSHMARK(SP); \
147             XPUSHs(invocant); \
148             PUTBACK; \
149             call_method(sig->arr_source_fallback, G_VOID|G_DISCARD);\
150             FREETMPS; \
151             LEAVE; \
152             } \
153             tmp = *hv_fetch((HV *)SvRV(invocant), \
154             sig->arr_source_string, \
155             strlen(sig->arr_source_string), 0); \
156             if (!tmp || !SvROK(tmp) || \
157             SvTYPE(SvRV(tmp)) != SVt_PVAV) \
158             croak("Hash value is not an array ref"); \
159             array = (AV *)SvRV(tmp); \
160             break; \
161             } \
162             \
163             case ARRAY_SRC_CALL_METHOD: { \
164             dSP; \
165             ENTER; \
166             SAVETMPS; \
167             PUSHMARK(SP); \
168             XPUSHs(invocant); \
169             PUTBACK; \
170             call_method(sig->arr_source_string, G_SCALAR); \
171             SPAGAIN; \
172             tmp = POPs; \
173             PUTBACK; \
174             if (!SvROK(tmp) || \
175             SvTYPE(SvRV(tmp)) != SVt_PVAV) \
176             croak("Method did not return an array ref"); \
177             array = (AV *)SvRV(tmp); \
178             FREETMPS; \
179             LEAVE; \
180             break; \
181             } \
182             \
183             default: \
184             croak("Unknown array source"); \
185             } \
186             } STMT_END
187              
188             SV*
189 5           shvxs_array_return_sv_object(
190             SV* invocant,
191             AV* array,
192             char* return_class,
193             char* return_constructor,
194             enum ArraySource arr_source,
195             char* arr_source_string,
196             I32 arr_source_index
197             ) {
198             dTHX;
199              
200 5           SV *array_ref = newRV_noinc((SV *)array);
201             SV *obj;
202              
203 5           SV *return_class_sv = NULL;
204 5           HV *return_class_stash = NULL;
205              
206 5 50         if (return_class && strcmp(return_class, "1") == 0) {
    50          
207 0 0         if (!SvROK(invocant) || !SvOBJECT(SvRV(invocant)))
    0          
208 0           croak("Invocant is not an object");
209 0           return_class_stash = SvSTASH(SvRV(invocant));
210 0 0         return_class_sv = newSVpv(HvNAME(return_class_stash), 0);
    0          
    0          
    0          
    0          
    0          
211             }
212 5 50         else if (return_class) {
213 5           return_class_sv = newSVpv(return_class, 0);
214 5           return_class_stash = gv_stashpv(return_class, GV_ADD);
215             }
216              
217             /* Case 1: call constructor */
218 5 100         if (return_constructor) {
219 1           dSP;
220              
221 1           ENTER;
222 1           SAVETMPS;
223              
224 1 50         PUSHMARK(SP);
225 1 50         XPUSHs(sv_2mortal(return_class_sv));
226 1 50         XPUSHs(array_ref);
227 1           PUTBACK;
228              
229 1           call_method(return_constructor, G_SCALAR);
230              
231 1           SPAGAIN;
232 1           obj = SvREFCNT_inc(POPs);
233 1           PUTBACK;
234              
235 1 50         FREETMPS;
236 1           LEAVE;
237              
238 1           return obj;
239             }
240              
241             /* Case 2: bless(\@array, class) */
242 4 100         if (arr_source == ARRAY_SRC_INVOCANT) {
243 1           obj = sv_bless(array_ref, return_class_stash);
244 1           return SvREFCNT_inc(obj);
245             }
246              
247             /* Case 3: bless(\$tmp, class) */
248 3 100         if (arr_source == ARRAY_SRC_DEREF_SCALAR) {
249 1           SV *tmp = newSV(0);
250 1           sv_setsv(tmp, array_ref);
251              
252 1           SV *tmp_ref = newRV_noinc(tmp);
253 1           obj = sv_bless(tmp_ref, return_class_stash);
254              
255 1           return SvREFCNT_inc(obj);
256             }
257              
258             /* Case 4: bless(\@tmp, class) */
259 2 100         if (arr_source == ARRAY_SRC_DEREF_ARRAY) {
260 1           AV *tmp = newAV();
261 1           av_store(tmp, arr_source_index, SvREFCNT_inc(array_ref));
262              
263 1           SV *tmp_ref = newRV_noinc((SV *)tmp);
264 1           obj = sv_bless(tmp_ref, return_class_stash);
265              
266 1           return SvREFCNT_inc(obj);
267             }
268              
269             /* Case 5: bless(\%tmp, class) */
270 1 50         if (arr_source == ARRAY_SRC_DEREF_HASH) {
271 1           HV *tmp = newHV();
272 1           hv_store(
273             tmp,
274             arr_source_string,
275             strlen(arr_source_string),
276             SvREFCNT_inc(array_ref),
277             0
278             );
279              
280 1           SV *tmp_ref = newRV_noinc((SV *)tmp);
281 1           obj = sv_bless(tmp_ref, return_class_stash);
282              
283 1           return SvREFCNT_inc(obj);
284             }
285              
286             /* Case 6: forbidden */
287 0 0         if (arr_source == ARRAY_SRC_CALL_METHOD) {
288 0           croak("ARRAY_SRC_CALL_METHOD not permitted for return object construction");
289             }
290              
291 0           croak("Invalid ArraySource value");
292             }
293              
294             #define RETURN_ARRAY_EXPECTATION \
295             STMT_START { \
296             switch (sig->method_return_pattern) { \
297             case SHOULD_RETURN_NOTHING: { \
298             if (GIMME_V == G_SCALAR) { \
299             XSRETURN_UNDEF; \
300             } \
301             XSRETURN_EMPTY; \
302             } \
303             \
304             case SHOULD_RETURN_UNDEF: \
305             XSRETURN_UNDEF; \
306             \
307             case SHOULD_RETURN_TRUE: \
308             XSRETURN_YES; \
309             \
310             case SHOULD_RETURN_FALSE: \
311             XSRETURN_NO; \
312             \
313             case SHOULD_RETURN_INVOCANT: \
314             ST(0) = sv_2mortal(newSVsv(invocant)); \
315             XSRETURN(1); \
316             \
317             case SHOULD_RETURN_VAL: \
318             ST(0) = val; \
319             XSRETURN(1); \
320             \
321             case SHOULD_RETURN_COUNT: \
322             I32 n = av_len(array) + 1; \
323             ST(0) = sv_2mortal(newSViv(n)); \
324             XSRETURN(1); \
325             \
326             case SHOULD_RETURN_ARRAY: \
327             case SHOULD_RETURN_ARRAYBLESS: { \
328             I32 n = av_len(array) + 1; \
329             \
330             if (GIMME_V == G_SCALAR) { \
331             enum ReturnPattern rp = sig->method_return_pattern; \
332             if ( rp == SHOULD_RETURN_ARRAY ) { \
333             ST(0) = sv_2mortal(newSViv(n)); \
334             XSRETURN(1); \
335             } \
336             else { \
337             ST(0) = shvxs_array_return_sv_object( \
338             invocant, \
339             array, \
340             sig->method_return_class, \
341             sig->method_return_constructor, \
342             sig->arr_source, \
343             sig->arr_source_string, \
344             sig->arr_source_index \
345             ); \
346             XSRETURN(1); \
347             } \
348             } \
349             \
350             if (n > 0) { \
351             I32 i; \
352             SV **svp; \
353             SP = MARK; \
354             EXTEND(SP, n); \
355             for (i = 0; i < n; i++) { \
356             svp = av_fetch(array, i, 0); \
357             PUSHs(svp ? sv_2mortal(newSVsv(*svp)) \
358             : &PL_sv_undef); \
359             } \
360             } \
361             XSRETURN(n); \
362             } \
363             \
364             case SHOULD_RETURN_OUT: \
365             case SHOULD_RETURN_OUTBLESS: { \
366             I32 n = av_len(out) + 1; \
367             \
368             if (GIMME_V == G_SCALAR) { \
369             enum ReturnPattern rp = sig->method_return_pattern; \
370             if ( rp == SHOULD_RETURN_OUT ) { \
371             ST(0) = sv_2mortal(newSViv(n)); \
372             XSRETURN(1); \
373             } \
374             else { \
375             ST(0) = shvxs_array_return_sv_object( \
376             invocant, \
377             out, \
378             sig->method_return_class, \
379             sig->method_return_constructor, \
380             sig->arr_source, \
381             sig->arr_source_string, \
382             sig->arr_source_index \
383             ); \
384             XSRETURN(1); \
385             } \
386             } \
387             \
388             if (n > 0) { \
389             I32 i; \
390             SV **svp; \
391             SP = MARK; \
392             EXTEND(SP, n); \
393             for (i = 0; i < n; i++) { \
394             svp = av_fetch(out, i, 0); \
395             PUSHs(svp ? sv_2mortal(newSVsv(*svp)) \
396             : &PL_sv_undef); \
397             } \
398             } \
399             XSRETURN(n); \
400             } \
401             \
402             case SHOULD_RETURN_OTHER: \
403             default: \
404             break; \
405             } \
406             } STMT_END