File Coverage

src/String.c
Criterion Covered Total %
statement 16 16 100.0
branch 5 6 83.3
condition n/a
subroutine n/a
pod n/a
total 21 22 95.4


line stmt bran cond sub pod time code
1             enum StringSource {
2             STR_SRC_INVOCANT,
3             STR_SRC_DEREF_SCALAR,
4             STR_SRC_DEREF_ARRAY,
5             STR_SRC_DEREF_HASH,
6             STR_SRC_CALL_METHOD,
7             };
8              
9             typedef struct {
10             char* name;
11             enum StringSource str_source;
12             char* str_source_string;
13             char* str_source_fallback;
14             I32 str_source_index;
15             bool has_curried_sv;
16             SV* curried_sv;
17             enum ReturnPattern method_return_pattern;
18             char* method_return_class;
19             char* method_return_constructor;
20             } shvxs_string_CMP_SIG;
21              
22             typedef struct {
23             char* name;
24             enum StringSource str_source;
25             char* str_source_string;
26             char* str_source_fallback;
27             I32 str_source_index;
28             bool has_curried_sv;
29             SV* curried_sv;
30             I32 type;
31             CV* type_cv;
32             SV* type_tiny;
33             bool has_type_tiny;
34             CV* coercion_cv;
35             enum ReturnPattern method_return_pattern;
36             char* method_return_class;
37             char* method_return_constructor;
38             } shvxs_string_SETTER_SIG;
39              
40             #define GET_STRING_FROM_SOURCE \
41             SV *string = NULL; /* var for incoming string */ \
42             SV *val = NULL; /* var for outgoing value */ \
43             STMT_START { \
44             SV *tmp; \
45             \
46             switch (sig->str_source) { \
47             case STR_SRC_INVOCANT: \
48             string = invocant; \
49             break; \
50             \
51             case STR_SRC_DEREF_SCALAR: \
52             if (!SvROK(invocant)) \
53             croak("Invocant is not a scalar reference"); \
54             string = SvRV(invocant); \
55             break; \
56             \
57             case STR_SRC_DEREF_ARRAY: { \
58             if (!SvROK(invocant) || \
59             SvTYPE(SvRV(invocant)) != SVt_PVAV) \
60             croak("Invocant is not an array reference"); \
61             SV **svp = av_fetch((AV *)SvRV(invocant), \
62             sig->str_source_index, 1); \
63             if (!svp) \
64             croak("Failed to create array element"); \
65             string = *svp; /* alias */ \
66             break; \
67             } \
68             \
69             case STR_SRC_DEREF_HASH: { \
70             if (!SvROK(invocant) || \
71             SvTYPE(SvRV(invocant)) != SVt_PVHV) \
72             croak("Invocant is not a hash reference"); \
73             \
74             if (sig->str_source_fallback && \
75             !hv_exists((HV *)SvRV(invocant), \
76             sig->str_source_string, \
77             (I32)strlen(sig->str_source_string))) { \
78             ENTER; \
79             SAVETMPS; \
80             PUSHMARK(SP); \
81             XPUSHs(invocant); \
82             PUTBACK; \
83             call_method(sig->str_source_fallback, \
84             G_VOID | G_DISCARD); \
85             SPAGAIN; \
86             FREETMPS; \
87             LEAVE; \
88             } \
89             \
90             SV **svp = hv_fetch((HV *)SvRV(invocant), \
91             sig->str_source_string, \
92             (I32)strlen(sig->str_source_string), \
93             1); \
94             if (!svp) \
95             croak("Failed to create hash value"); \
96             string = *svp; /* alias */ \
97             break; \
98             } \
99             \
100             case STR_SRC_CALL_METHOD: { \
101             ENTER; \
102             SAVETMPS; \
103             PUSHMARK(SP); \
104             XPUSHs(invocant); \
105             PUTBACK; \
106             I32 count = call_method(sig->str_source_string, G_SCALAR); \
107             SPAGAIN; \
108             tmp = (count > 0) ? POPs : &PL_sv_undef; \
109             PUTBACK; \
110             string = newSVsv(tmp); \
111             FREETMPS; \
112             LEAVE; \
113             break; \
114             } \
115             \
116             default: \
117             croak("Unknown string source"); \
118             } \
119             } STMT_END
120              
121             #define SET_STRING(newsv) \
122             STMT_START { \
123             SV *ns = newsv; \
124             if (!ns) ns = &PL_sv_undef; \
125             \
126             /* For non-method sources, `string` aliases original storage, \
127             so this updates the original location automatically. */ \
128             if (sig->str_source != STR_SRC_CALL_METHOD) { \
129             sv_setsv(string, ns); \
130             } \
131             else { \
132             /* Keep local copy in sync too */ \
133             if (string) \
134             sv_setsv(string, ns); \
135             \
136             ENTER; \
137             SAVETMPS; \
138             PUSHMARK(SP); \
139             XPUSHs(invocant); \
140             XPUSHs(ns); \
141             PUTBACK; \
142             call_method(sig->str_source_string, G_VOID | G_DISCARD); \
143             SPAGAIN; \
144             FREETMPS; \
145             LEAVE; \
146             } \
147             } STMT_END
148              
149              
150             #define RETURN_STRING_EXPECTATION \
151             STMT_START { \
152             switch (sig->method_return_pattern) { \
153             case SHOULD_RETURN_NOTHING: { \
154             if (GIMME_V == G_SCALAR) { \
155             XSRETURN_UNDEF; \
156             } \
157             XSRETURN_EMPTY; \
158             } \
159             \
160             case SHOULD_RETURN_UNDEF: \
161             XSRETURN_UNDEF; \
162             \
163             case SHOULD_RETURN_TRUE: \
164             XSRETURN_YES; \
165             \
166             case SHOULD_RETURN_FALSE: \
167             XSRETURN_NO; \
168             \
169             case SHOULD_RETURN_INVOCANT: \
170             ST(0) = sv_2mortal(newSVsv(invocant)); \
171             XSRETURN(1); \
172             \
173             case SHOULD_RETURN_STRING: \
174             ST(0) = sv_2mortal(newSVsv(string)); \
175             XSRETURN(1); \
176             \
177             case SHOULD_RETURN_VAL: \
178             ST(0) = val; \
179             XSRETURN(1); \
180             \
181             case SHOULD_RETURN_OTHER: \
182             default: \
183             break; \
184             } \
185             } STMT_END
186              
187             SV*
188 90           sv_lower(pTHX_ SV* sv) {
189             STRLEN len;
190 90           char *s = SvPV_force(sv, len);
191 228 100         for (; len--; s++)
192 138 100         *s = toLOWER(*s);
193 90           return sv;
194             }
195              
196             static I32
197 45           sv_cmp_ci(pTHX_ SV *a, SV *b)
198             {
199             I32 c;
200              
201 45           ENTER;
202 45           SAVETMPS;
203              
204 45           SV *ta = sv_2mortal(newSVsv(a));
205 45           SV *tb = sv_2mortal(newSVsv(b));
206              
207 45           sv_lower(aTHX_ ta);
208 45           sv_lower(aTHX_ tb);
209              
210 45           c = sv_cmp(ta, tb);
211              
212 45 50         FREETMPS;
213 45           LEAVE;
214              
215 45           return c;
216             }
217              
218             #define SV_CMP_CI(a, b) sv_cmp_ci(aTHX_ (a), (b))
219              
220             #define CHECK_CURRIED_SV_IS_STRING \
221             bool ok = SvOK(curried_sv) && !SvROK(curried_sv) && !isGV(curried_sv); \
222             if (!ok && has_curried_sv) { \
223             type_error(curried_sv, "$curried", 0, TYPE_BASE_STR, NULL); \
224             } \
225             else if (!ok) { \
226             type_error(curried_sv, "$_", 1, TYPE_BASE_STR, NULL); \
227             }
228              
229             #define COMMON_STRING_PARAM_COUNT_CHECK \
230             if (has_curried_sv && items!=1) { \
231             croak(WRONG_NUMBER_OF_PARAMETERS); \
232             } \
233             else if(!has_curried_sv && items!=2) { \
234             croak(WRONG_NUMBER_OF_PARAMETERS); \
235             }