File Coverage

XS.xs
Criterion Covered Total %
statement 121 121 100.0
branch 73 90 81.1
condition n/a
subroutine n/a
pod n/a
total 194 211 91.9


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             #include
9              
10             #include "toml.h"
11             #include "tomlxs.h"
12              
13             #define DOCUMENT_CLASS "TOML::XS::Document"
14             #define TIMESTAMP_CLASS "TOML::XS::Timestamp"
15             #define BOOLEAN_CLASS "TOML::XS"
16              
17             #define CROAK_ERR_MSG_FN "TOML::XS::_croak_err_message_from_pieces"
18              
19             #define PERL_TRUE get_sv(BOOLEAN_CLASS "::true", 0)
20             #define PERL_FALSE get_sv(BOOLEAN_CLASS "::false", 0)
21              
22             #define UNUSED(x) (void)(x)
23              
24             #define ERR_PATH_UNSHIFT(err_path_ptr, sv) STMT_START { \
25             if (NULL == *err_path_ptr) *err_path_ptr = newAV(); \
26             av_unshift(*err_path_ptr, 1); \
27             av_store(*err_path_ptr, 0, sv); \
28             } STMT_END
29              
30             #define _timestamp_to_sv(ts) _ptr_to_svrv(aTHX_ ts, gv_stashpv(TIMESTAMP_CLASS, FALSE))
31              
32             #define _verify_no_null(tomlstr, tomllen) \
33             if (strchr(tomlstr, 0) != (tomlstr + tomllen)) { \
34             croak( \
35             "String contains a NUL at index %" UVf "!", \
36             (UV)(strchr(tomlstr, 0) - tomlstr) \
37             ); \
38             }
39              
40             #define _verify_valid_utf8(tomlstr, tomllen) \
41             if (!is_utf8_string( (const U8*)tomlstr, tomllen )) { \
42             U8* ep; \
43             const U8** epptr = (const U8**) &ep; \
44             is_utf8_string_loc((const U8*)tomlstr, tomllen, epptr); \
45             croak( \
46             "String contains non-UTF-8 at index %" UVf "!", \
47             (UV)((char*) ep - tomlstr) \
48             ); \
49             }
50              
51             #if TOMLXS_SV_CAN_USE_EXTERNAL_STRING
52             /* More efficient: make the SV use the existing string.
53             (Would sv_usepvn() work just as well??)
54             */
55             #define RETURN_IF_DATUM_IS_STRING(d) \
56             if (d.ok) { \
57             SV* ret = newSV(0); \
58             SvUPGRADE(ret, SVt_PV); \
59             SvPV_set(ret, d.u.s); \
60             SvPOK_on(ret); \
61             SvCUR_set(ret, strlen(d.u.s)); \
62             SvLEN_set(ret, SvCUR(ret)); \
63             SvUTF8_on(ret); \
64             return ret; \
65             }
66             #else
67             /* Slow but safe: copy the string into the PV. */
68             #define RETURN_IF_DATUM_IS_STRING(d) \
69             if (d.ok) { \
70             SV* ret = newSVpvn_utf8(d.u.s, strlen(d.u.s), TRUE); \
71             tomlxs_free_string(d.u.s); \
72             return ret; \
73             }
74             #endif
75              
76             #define RETURN_IF_DATUM_IS_BOOLEAN(d) \
77             if (d.ok) { \
78             return SvREFCNT_inc(d.u.b ? PERL_TRUE : PERL_FALSE); \
79             }
80              
81             #define RETURN_IF_DATUM_IS_INTEGER(d) \
82             if (d.ok) { \
83             return newSViv((IV)d.u.i); \
84             }
85              
86             #define RETURN_IF_DATUM_IS_DOUBLE(d) \
87             if (d.ok) { \
88             return newSVnv((NV)d.u.d); \
89             }
90              
91             #define RETURN_IF_DATUM_IS_TIMESTAMP(d) \
92             if (d.ok) { \
93             return _timestamp_to_sv(d.u.ts); \
94             }
95              
96             /* ---------------------------------------------------------------------- */
97              
98 8           SV* _ptr_to_svrv(pTHX_ void* ptr, HV* stash) {
99 8           SV* referent = newSVuv( PTR2UV(ptr) );
100 8           SV* retval = newRV_noinc(referent);
101 8           sv_bless(retval, stash);
102              
103 8           return retval;
104             }
105              
106 8           toml_table_t* _get_toml_table_from_sv(pTHX_ SV *self_sv) {
107 8           SV *referent = SvRV(self_sv);
108 8 50         return INT2PTR(toml_table_t*, SvUV(referent));
109             }
110              
111 52           toml_timestamp_t* _get_toml_timestamp_from_sv(pTHX_ SV *self_sv) {
112 52           SV *referent = SvRV(self_sv);
113 52 50         return INT2PTR(toml_timestamp_t*, SvUV(referent));
114             }
115              
116             SV* _toml_table_value_to_sv(pTHX_ toml_table_t* curtab, const char* key, AV** err_path_ptr);
117             SV* _toml_array_value_to_sv(pTHX_ toml_array_t* arr, int i, AV** err_path_ptr);
118              
119 14           SV* _toml_table_to_sv(pTHX_ toml_table_t* tab, AV** err_path_ptr) {
120             int i;
121              
122             /* Doesn’t need to be mortal since this should not throw.
123             Should that ever change this will need to be mortal then
124             de-mortalized.
125             */
126 14           HV* hv = newHV();
127              
128 14           for (i = 0; ; i++) {
129 41           const char* key = toml_key_in(tab, i);
130 41 100         if (!key) break;
131              
132 30           SV* sv = _toml_table_value_to_sv(aTHX_ tab, key, err_path_ptr);
133              
134 30 100         if (NULL == sv) {
135 3           SvREFCNT_dec((SV*)hv);
136 3           SV* piece = newSVpv(key, 0);
137 3           sv_utf8_decode(piece);
138 3 100         ERR_PATH_UNSHIFT(err_path_ptr, piece);
139 3           return NULL;
140             }
141              
142 27           hv_store(hv, key, strlen(key), sv, 0);
143 27           }
144              
145 11           return newRV_noinc( (SV *) hv );
146             }
147              
148 7           SV* _toml_array_to_sv(pTHX_ toml_array_t* arr, AV** err_path_ptr) {
149             int i;
150              
151             /* Doesn’t need to be mortal since this should not throw.
152             Should that ever change this will need to be mortal then
153             de-mortalized.
154             */
155 7           AV* av = newAV();
156              
157 7           int size = toml_array_nelem(arr);
158              
159 7           av_extend(av, size - 1);
160              
161 22 100         for (i = 0; i
162 17           SV* sv = _toml_array_value_to_sv(aTHX_ arr, i, err_path_ptr);
163              
164 17 100         if (NULL == sv) {
165 2           SvREFCNT_dec((SV*)av);
166 2 100         ERR_PATH_UNSHIFT(err_path_ptr, newSViv(i));
167 2           return NULL;
168             }
169              
170 15           av_store(av, i, sv);
171             }
172              
173 5           return newRV( (SV *) av );
174             }
175              
176 30           SV* _toml_table_value_to_sv(pTHX_ toml_table_t* curtab, const char* key, AV** err_path_ptr) {
177             toml_array_t* arr;
178             toml_table_t* tab;
179              
180 30 100         if (0 != (arr = toml_array_in(curtab, key))) {
181 5           return _toml_array_to_sv(aTHX_ arr, err_path_ptr);
182             }
183              
184 25 100         if (0 != (tab = toml_table_in(curtab, key))) {
185 8           return _toml_table_to_sv(aTHX_ tab, err_path_ptr);
186             }
187              
188             toml_datum_t d;
189              
190 17           d = toml_string_in(curtab, key);
191 17 100         RETURN_IF_DATUM_IS_STRING(d);
    50          
192              
193 10           d = toml_bool_in(curtab, key);
194 10 100         RETURN_IF_DATUM_IS_BOOLEAN(d);
    100          
195              
196 8           d = toml_int_in(curtab, key);
197 8 100         RETURN_IF_DATUM_IS_INTEGER(d);
198              
199 7           d = toml_double_in(curtab, key);
200 7 100         RETURN_IF_DATUM_IS_DOUBLE(d);
201              
202 4           d = toml_timestamp_in(curtab, key);
203 4 100         RETURN_IF_DATUM_IS_TIMESTAMP(d);
204              
205             /* This indicates some unspecified parse error that the initial
206             parse didn’t catch.
207             */
208 30           return NULL;
209             }
210              
211 17           SV* _toml_array_value_to_sv(pTHX_ toml_array_t* curarr, int i, AV** err_path_ptr) {
212             toml_array_t* arr;
213             toml_table_t* tab;
214              
215 17 100         if (0 != (arr = toml_array_at(curarr, i))) {
216 2           return _toml_array_to_sv(aTHX_ arr, err_path_ptr);
217             }
218              
219 15 100         if (0 != (tab = toml_table_at(curarr, i))) {
220 2           return _toml_table_to_sv(aTHX_ tab, err_path_ptr);
221             }
222              
223             toml_datum_t d;
224              
225 13           d = toml_string_at(curarr, i);
226 13 100         RETURN_IF_DATUM_IS_STRING(d);
    50          
227              
228 10           d = toml_bool_at(curarr, i);
229 10 100         RETURN_IF_DATUM_IS_BOOLEAN(d);
    100          
230              
231 8           d = toml_int_at(curarr, i);
232 8 100         RETURN_IF_DATUM_IS_INTEGER(d);
233              
234 4           d = toml_double_at(curarr, i);
235 4 100         RETURN_IF_DATUM_IS_DOUBLE(d);
236              
237 2           d = toml_timestamp_at(curarr, i);
238 2 100         RETURN_IF_DATUM_IS_TIMESTAMP(d);
239              
240             /* This indicates some unspecified parse error that the initial
241             parse didn’t catch.
242             */
243 17           return NULL;
244             }
245              
246             /* for profiling: */
247             /*
248             #include
249              
250             void _print_timeofday(char* label) {
251             struct timeval tp;
252              
253             gettimeofday(&tp, NULL);
254             fprintf(stderr, "%s: %ld.%06d\n", label, tp.tv_sec, tp.tv_usec);
255             }
256             */
257              
258             /* ---------------------------------------------------------------------- */
259              
260             MODULE = TOML::XS PACKAGE = TOML::XS
261              
262             PROTOTYPES: DISABLE
263              
264             SV*
265             from_toml (SV* tomlsv)
266             CODE:
267             STRLEN tomllen;
268             char errbuf[200];
269 8 50         char* tomlstr = SvPVbyte(tomlsv, tomllen);
270              
271 8 100         _verify_no_null(tomlstr, tomllen);
272              
273 7 100         _verify_valid_utf8(tomlstr, tomllen);
274              
275 6           toml_table_t* tab = toml_parse(tomlstr, errbuf, sizeof(errbuf));
276              
277 6 100         if (tab == NULL) croak("%s", errbuf);
278              
279 4           RETVAL = _ptr_to_svrv( aTHX_ tab, gv_stashpv(DOCUMENT_CLASS, FALSE) );
280             OUTPUT:
281             RETVAL
282              
283             # ----------------------------------------------------------------------
284              
285             MODULE = TOML::XS PACKAGE = TOML::XS::Document
286              
287             PROTOTYPES: DISABLE
288              
289             SV*
290             to_struct (SV* docsv)
291             CODE:
292 4           toml_table_t* tab = _get_toml_table_from_sv(aTHX_ docsv);
293              
294 4           AV* err_path = NULL;
295              
296 4           RETVAL = _toml_table_to_sv(aTHX_ tab, &err_path);
297              
298 4 100         if (NULL == RETVAL) {
299 2           dSP;
300              
301 2           ENTER;
302 2           SAVETMPS;
303              
304 2 50         PUSHMARK(SP);
305 2 50         EXTEND(SP, 1);
306              
307             /* When this mortal reference is reaped it’ll decrement
308             the referent AV’s refcount. */
309 2           mPUSHs(newRV_noinc( (SV*)err_path ));
310              
311 2           PUTBACK;
312              
313 2           call_pv(CROAK_ERR_MSG_FN, G_DISCARD);
314              
315             // Unneeded:
316             // FREETMPS;
317             // LEAVE;
318              
319             assert(0);
320             }
321             OUTPUT:
322             RETVAL
323              
324             void
325             DESTROY (SV* docsv)
326             CODE:
327 4           toml_table_t* tab = _get_toml_table_from_sv(aTHX_ docsv);
328 4           toml_free(tab);
329              
330             # ----------------------------------------------------------------------
331              
332             MODULE = TOML::XS PACKAGE = TOML::XS::Timestamp
333              
334             PROTOTYPES: DISABLE
335              
336             SV*
337             to_string (SV* selfsv)
338             CODE:
339 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
340              
341 4           RETVAL = newSVpvs("");
342              
343 4 50         if (NULL != ts->year) {
344 4           sv_catpvf(
345             RETVAL,
346             "%02d-%02d-%02d",
347 12           *ts->year, *ts->month, *ts->day
348             );
349             }
350              
351 4 50         if (NULL != ts->hour) {
352 4           sv_catpvf(
353             RETVAL,
354             "T%02d:%02d:%02d",
355 12           *ts->hour, *ts->minute, *ts->second
356             );
357              
358 4 100         if (NULL != ts->millisec) {
359 1           sv_catpvf(
360             RETVAL,
361             ".%03d",
362 1           *ts->millisec
363             );
364             }
365             }
366              
367 4 50         if (NULL != ts->z) {
368 4           sv_catpv(RETVAL, ts->z);
369             }
370             OUTPUT:
371             RETVAL
372              
373             SV*
374             year (SV* selfsv)
375             CODE:
376 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
377              
378 4 50         RETVAL = ts->year ? newSViv(*ts->year) : &PL_sv_undef;
379             OUTPUT:
380             RETVAL
381              
382             SV*
383             month (SV* selfsv)
384             CODE:
385 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
386              
387 4 50         RETVAL = ts->month ? newSViv(*ts->month) : &PL_sv_undef;
388             OUTPUT:
389             RETVAL
390              
391             SV*
392             day (SV* selfsv)
393             ALIAS:
394             date = 1
395             CODE:
396             UNUSED(ix);
397 8           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
398              
399 8 50         RETVAL = ts->day ? newSViv(*ts->day) : &PL_sv_undef;
400             OUTPUT:
401             RETVAL
402              
403             SV*
404             hour (SV* selfsv)
405             ALIAS:
406             hours = 1
407             CODE:
408             UNUSED(ix);
409 8           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
410              
411 8 50         RETVAL = ts->hour ? newSViv(*ts->hour) : &PL_sv_undef;
412             OUTPUT:
413             RETVAL
414              
415             SV*
416             minute (SV* selfsv)
417             ALIAS:
418             minutes = 1
419             CODE:
420             UNUSED(ix);
421 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
422              
423 4 50         RETVAL = ts->minute ? newSViv(*ts->minute) : &PL_sv_undef;
424             OUTPUT:
425             RETVAL
426              
427             SV*
428             second (SV* selfsv)
429             ALIAS:
430             seconds = 1
431             CODE:
432             UNUSED(ix);
433 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
434              
435 4 50         RETVAL = ts->second ? newSViv(*ts->second) : &PL_sv_undef;
436             OUTPUT:
437             RETVAL
438              
439             SV*
440             millisecond (SV* selfsv)
441             ALIAS:
442             milliseconds = 1
443             CODE:
444             UNUSED(ix);
445 8           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
446              
447 8 100         RETVAL = ts->millisec ? newSViv(*ts->millisec) : &PL_sv_undef;
448             OUTPUT:
449             RETVAL
450              
451             SV*
452             timezone (SV* selfsv)
453             CODE:
454 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
455              
456 4 50         RETVAL = ts->z ? newSVpv(ts->z, 0) : &PL_sv_undef;
457             OUTPUT:
458             RETVAL
459              
460             void
461             DESTROY (SV* selfsv)
462             CODE:
463 4           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
464 4           tomlxs_free_timestamp(ts);