File Coverage

XS.xs
Criterion Covered Total %
statement 220 277 79.4
branch 115 170 67.6
condition n/a
subroutine n/a
pod n/a
total 335 447 74.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             /* Disabled for production because adding subprocess detection
14             would entail having a separate struct for the objects, which
15             seems likely to degrade performance.
16             */
17             #define DETECT_LEAKS 0
18              
19             #define DOCUMENT_CLASS "TOML::XS::Document"
20             #define TIMESTAMP_CLASS "TOML::XS::Timestamp"
21             #define BOOLEAN_CLASS "TOML::XS"
22              
23             #define CROAK_MALFORMED_TOML_FN "TOML::XS::_croak_malformed_toml"
24             #define CROAK_POINTER_BEYOND_DATUM_FN "TOML::XS::_croak_pointer_beyond_datum"
25              
26             #define PERL_TRUE get_sv(BOOLEAN_CLASS "::true", 0)
27             #define PERL_FALSE get_sv(BOOLEAN_CLASS "::false", 0)
28              
29             #define UNUSED(x) (void)(x)
30              
31             #ifdef PL_phase
32             #define _IS_GLOBAL_DESTRUCT (PL_phase == PERL_PHASE_DESTRUCT)
33             #else
34             #define _IS_GLOBAL_DESTRUCT PL_dirty
35             #endif
36              
37             /* cheating … */
38             #define my_grok_atoUV Perl_grok_atoUV
39              
40             #define ERR_PATH_UNSHIFT(err_path_ptr, sv) STMT_START { \
41             if (NULL == *err_path_ptr) *err_path_ptr = newAV(); \
42             av_unshift(*err_path_ptr, 1); \
43             av_store(*err_path_ptr, 0, sv); \
44             } STMT_END
45              
46             #define _verify_no_null(tomlstr, tomllen) \
47             if (strchr(tomlstr, 0) != (tomlstr + tomllen)) { \
48             croak( \
49             "String contains a NUL at index %" UVf "!", \
50             (UV)(strchr(tomlstr, 0) - tomlstr) \
51             ); \
52             }
53              
54             #define _verify_valid_utf8(tomlstr, tomllen) \
55             if (!is_utf8_string( (const U8*)tomlstr, tomllen )) { \
56             U8* ep; \
57             const U8** epptr = (const U8**) &ep; \
58             is_utf8_string_loc((const U8*)tomlstr, tomllen, epptr); \
59             croak( \
60             "String contains non-UTF-8 at index %" UVf "!", \
61             (UV)((char*) ep - tomlstr) \
62             ); \
63             }
64              
65 16           static inline SV* _datum_string_to_sv( pTHX_ toml_datum_t d ) {
66             #if TOMLXS_SV_CAN_USE_EXTERNAL_STRING
67             /* More efficient: make the SV use the existing string.
68             (Would sv_usepvn() work just as well??)
69             */
70 16           SV* ret = newSV(0);
71 16 50         SvUPGRADE(ret, SVt_PV);
72 16           SvPV_set(ret, d.u.s);
73 16           SvPOK_on(ret);
74 16           SvCUR_set(ret, strlen(d.u.s));
75 16           SvLEN_set(ret, SvCUR(ret));
76 16           SvUTF8_on(ret);
77             #else
78             /* Slow but safe: copy the string into the PV. */
79             SV* ret = newSVpvn_utf8(d.u.s, strlen(d.u.s), TRUE);
80             tomlxs_free_string(d.u.s);
81             #endif
82              
83 16           return ret;
84             }
85              
86             #define _datum_boolean_to_sv(d) \
87             SvREFCNT_inc(d.u.b ? PERL_TRUE : PERL_FALSE);
88              
89             #define _datum_integer_to_sv(d) \
90             newSViv((IV)d.u.i);
91              
92             #define _datum_double_to_sv(datum) \
93             newSVnv((NV)datum.u.d);
94              
95             #define RETURN_IF_DATUM_IS_STRING(d) \
96             if (d.ok) return _datum_string_to_sv(aTHX_ d);
97              
98             #define RETURN_IF_DATUM_IS_BOOLEAN(d) \
99             if (d.ok) return _datum_boolean_to_sv(d);
100              
101             #define RETURN_IF_DATUM_IS_INTEGER(d) \
102             if (d.ok) return _datum_integer_to_sv(d);
103              
104             #define RETURN_IF_DATUM_IS_DOUBLE(d) \
105             if (d.ok) return _datum_double_to_sv(d);
106              
107             #define RETURN_IF_DATUM_IS_TIMESTAMP(d) \
108             if (d.ok) return _datum_timestamp_to_sv(aTHX_ d);
109              
110             /* ---------------------------------------------------------------------- */
111              
112 14           SV* _ptr_to_svrv(pTHX_ void* ptr, HV* stash) {
113 14           SV* referent = newSVuv( PTR2UV(ptr) );
114 14           SV* retval = newRV_noinc(referent);
115 14           sv_bless(retval, stash);
116              
117 14           return retval;
118             }
119              
120 10           static inline SV* _datum_timestamp_to_sv( pTHX_ toml_datum_t datum ) {
121 10           return _ptr_to_svrv(aTHX_ datum.u.ts, gv_stashpv(TIMESTAMP_CLASS, FALSE));
122             }
123              
124 2           static inline void _call_croaker_pv (pTHX_ const char* fn, AV* err_path) {
125 2           dSP;
126              
127 2           ENTER;
128 2           SAVETMPS;
129              
130 2 50         PUSHMARK(SP);
131 2 50         EXTEND(SP, 1);
132              
133             /* When this mortal reference is reaped it’ll decrement
134             the referent AV’s refcount. */
135 2           mPUSHs(newRV_noinc( (SV*)err_path ));
136              
137 2           PUTBACK;
138              
139 2           call_pv(fn, G_DISCARD);
140              
141             // Unneeded:
142             // FREETMPS;
143             // LEAVE;
144 0           }
145              
146 0           static inline SV* _call_pv_scalar_1_1 (pTHX_ const char* fn, SV* arg) {
147 0           dSP;
148              
149 0           ENTER;
150 0           SAVETMPS;
151              
152 0 0         PUSHMARK(SP);
153 0 0         EXTEND(SP, 1);
154              
155 0           mPUSHs(arg);
156              
157 0           PUTBACK;
158              
159 0           unsigned count = call_pv(fn, G_SCALAR);
160              
161 0           SPAGAIN;
162              
163             SV* ret;
164              
165 0 0         if (count > 0) {
166 0           ret = newSVsv(POPs);
167             }
168             else {
169 0           ret = &PL_sv_undef;
170             }
171              
172 0           PUTBACK;
173 0 0         FREETMPS;
174 0           LEAVE;
175              
176 0           return ret;
177             }
178              
179 0           static inline SV* _get_json_pointer_sv (pTHX_ SV** stack, unsigned stack_idx) {
180 0           AV* pointer = newAV();
181              
182 0 0         for (unsigned i=0; i<=stack_idx; i++) {
183 0           av_push(pointer, newSVsv(stack[i]));
184             }
185              
186 0           SV* pointer_ar = newRV_noinc( (SV*) pointer );
187              
188 0           return _call_pv_scalar_1_1(aTHX_ "TOML::XS::_BUILD_JSON_POINTER", pointer_ar);
189             }
190              
191 29           toml_table_t* _get_toml_table_from_sv(pTHX_ SV *self_sv) {
192 29           SV *referent = SvRV(self_sv);
193 29 50         return INT2PTR(toml_table_t*, SvUV(referent));
194             }
195              
196 130           toml_timestamp_t* _get_toml_timestamp_from_sv(pTHX_ SV *self_sv) {
197 130           SV *referent = SvRV(self_sv);
198 130 50         return INT2PTR(toml_timestamp_t*, SvUV(referent));
199             }
200              
201             SV* _toml_table_value_to_sv(pTHX_ toml_table_t* curtab, const char* key, AV** err_path_ptr);
202             SV* _toml_array_value_to_sv(pTHX_ toml_array_t* arr, int i, AV** err_path_ptr);
203              
204 19           SV* _toml_table_to_sv(pTHX_ toml_table_t* tab, AV** err_path_ptr) {
205             int i;
206              
207             /* Doesn’t need to be mortal since this should not throw.
208             Should that ever change this will need to be mortal then
209             de-mortalized.
210             */
211 19           HV* hv = newHV();
212              
213 19           for (i = 0; ; i++) {
214 52           const char* key = toml_key_in(tab, i);
215 52 100         if (!key) break;
216              
217 36           SV* sv = _toml_table_value_to_sv(aTHX_ tab, key, err_path_ptr);
218              
219 36 100         if (NULL == sv) {
220 3           SvREFCNT_dec((SV*)hv);
221 3           SV* piece = newSVpv(key, 0);
222 3           sv_utf8_decode(piece);
223 3 100         ERR_PATH_UNSHIFT(err_path_ptr, piece);
224 3           return NULL;
225             }
226              
227 33           hv_store(hv, key, -strlen(key), sv, 0);
228 33           }
229              
230 16           return newRV_noinc( (SV *) hv );
231             }
232              
233 9           SV* _toml_array_to_sv(pTHX_ toml_array_t* arr, AV** err_path_ptr) {
234             int i;
235              
236             /* Doesn’t need to be mortal since this should not throw.
237             Should that ever change this will need to be mortal then
238             de-mortalized.
239             */
240 9           AV* av = newAV();
241              
242 9           int size = toml_array_nelem(arr);
243              
244 9           av_extend(av, size - 1);
245              
246 38 100         for (i = 0; i
247 31           SV* sv = _toml_array_value_to_sv(aTHX_ arr, i, err_path_ptr);
248              
249 31 100         if (NULL == sv) {
250 2           SvREFCNT_dec((SV*)av);
251 2 100         ERR_PATH_UNSHIFT(err_path_ptr, newSViv(i));
252 2           return NULL;
253             }
254              
255 29           av_store(av, i, sv);
256             }
257              
258 7           return newRV_noinc( (SV *) av );
259             }
260              
261 36           SV* _toml_table_value_to_sv(pTHX_ toml_table_t* curtab, const char* key, AV** err_path_ptr) {
262             toml_array_t* arr;
263             toml_table_t* tab;
264              
265 36 100         if (0 != (arr = toml_array_in(curtab, key))) {
266 6           return _toml_array_to_sv(aTHX_ arr, err_path_ptr);
267             }
268              
269 30 100         if (0 != (tab = toml_table_in(curtab, key))) {
270 8           return _toml_table_to_sv(aTHX_ tab, err_path_ptr);
271             }
272              
273             toml_datum_t d;
274              
275 22           d = toml_string_in(curtab, key);
276 22 100         RETURN_IF_DATUM_IS_STRING(d);
277              
278 14           d = toml_bool_in(curtab, key);
279 14 100         RETURN_IF_DATUM_IS_BOOLEAN(d);
    100          
280              
281 11           d = toml_int_in(curtab, key);
282 11 100         RETURN_IF_DATUM_IS_INTEGER(d);
283              
284 9           d = toml_double_in(curtab, key);
285 9 100         RETURN_IF_DATUM_IS_DOUBLE(d);
286              
287 5           d = toml_timestamp_in(curtab, key);
288 5 100         RETURN_IF_DATUM_IS_TIMESTAMP(d);
289              
290             /* This indicates some unspecified parse error that the initial
291             parse didn’t catch.
292             */
293 36           return NULL;
294             }
295              
296 31           SV* _toml_array_value_to_sv(pTHX_ toml_array_t* curarr, int i, AV** err_path_ptr) {
297             toml_array_t* arr;
298             toml_table_t* tab;
299              
300 31 100         if (0 != (arr = toml_array_at(curarr, i))) {
301 2           return _toml_array_to_sv(aTHX_ arr, err_path_ptr);
302             }
303              
304 29 100         if (0 != (tab = toml_table_at(curarr, i))) {
305 4           return _toml_table_to_sv(aTHX_ tab, err_path_ptr);
306             }
307              
308             toml_datum_t d;
309              
310 25           d = toml_string_at(curarr, i);
311 25 100         RETURN_IF_DATUM_IS_STRING(d);
312              
313 20           d = toml_bool_at(curarr, i);
314 20 100         RETURN_IF_DATUM_IS_BOOLEAN(d);
    100          
315              
316 14           d = toml_int_at(curarr, i);
317 14 100         RETURN_IF_DATUM_IS_INTEGER(d);
318              
319 8           d = toml_double_at(curarr, i);
320 8 100         RETURN_IF_DATUM_IS_DOUBLE(d);
321              
322 4           d = toml_timestamp_at(curarr, i);
323 4 100         RETURN_IF_DATUM_IS_TIMESTAMP(d);
324              
325             /* This indicates some unspecified parse error that the initial
326             parse didn’t catch.
327             */
328 31           return NULL;
329             }
330              
331             #if DETECT_LEAKS
332             static inline void _warn_if_global_destruct_destroy( pTHX_ SV* obj ) {
333             if (_IS_GLOBAL_DESTRUCT) {
334             warn( "%" SVf " destroyed at global destruction; memory leak likely!\n", obj);
335             }
336             }
337             #endif
338              
339             /* for profiling: */
340             /*
341             #include
342              
343             void _print_timeofday(char* label) {
344             struct timeval tp;
345              
346             gettimeofday(&tp, NULL);
347             fprintf(stderr, "%s: %ld.%06d\n", label, tp.tv_sec, tp.tv_usec);
348             }
349             */
350              
351             typedef union {
352             toml_table_t* table_p;
353             toml_array_t* array_p;
354             toml_datum_t datum;
355             } entity_t;
356              
357             typedef struct {
358             entity_t entity;
359              
360             enum toml_xs_type type;
361             } toml_entity_t;
362              
363             toml_entity_t _drill_into_array(pTHX_ toml_array_t* arrin, SV** stack, unsigned stack_idx, unsigned drill_len, AV** err_path_ptr);
364              
365 17           static inline void _croak_if_datum_is_nonfinal_drill( pTHX_ SV** stack, unsigned stack_idx, unsigned drill_len) {
366 17 50         if (stack_idx != drill_len-1) {
367              
368 0           SV* jsonpointer = _get_json_pointer_sv(aTHX_ stack, stack_idx);
369              
370 0           SV* errsv = newSVpvf("Cannot descend into non-container! (JSON pointer: %" SVf ")", jsonpointer);
371 0           croak_sv(errsv);
372              
373             assert(0);
374             }
375 17           }
376              
377 0           static inline bool _table_has_key_sv(toml_table_t* tabin, const char* keystr) {
378             const char* key;
379              
380 0           for (unsigned i = 0; ; i++) {
381 0           key = toml_key_in(tabin, i);
382 0 0         if (!key) break;
383              
384 0 0         if (strEQ(key, keystr)) return true;
385 0           }
386              
387 0           return false;
388             }
389              
390 41           toml_entity_t _drill_into_table(pTHX_ toml_table_t* tabin, SV** stack, unsigned stack_idx, unsigned drill_len, AV** err_path_ptr) {
391             toml_entity_t newent;
392              
393 41           SV* key_sv = stack[stack_idx];
394              
395 41 50         if (!SvOK(key_sv)) {
    0          
    0          
396 0           croak("Uninitialized value given in pointer (#%d)!", stack_idx);
397             }
398              
399 41 100         char* key = SvPVutf8_nolen(key_sv);
400              
401 41           toml_table_t* tab = toml_table_in(tabin, key);
402              
403 41 100         if (tab) {
404 21 100         if (stack_idx == drill_len-1) {
405 1           newent.type = TOML_XS_TYPE_TABLE;
406 1           newent.entity.table_p = tab;
407 1           return newent;
408             }
409             else {
410 20           return _drill_into_table(aTHX_ tab, stack, 1 + stack_idx, drill_len, err_path_ptr);
411             }
412             }
413              
414 20           toml_array_t* arr = toml_array_in(tabin, key);
415              
416 20 100         if (arr) {
417 15 100         if (stack_idx == drill_len-1) {
418 1           newent.type = TOML_XS_TYPE_ARRAY;
419 1           newent.entity.array_p = arr;
420 1           return newent;
421             }
422             else {
423 14           return _drill_into_array(aTHX_ arr, stack, 1 + stack_idx, drill_len, err_path_ptr);
424             }
425             }
426              
427 5           _croak_if_datum_is_nonfinal_drill(aTHX_ stack, stack_idx, drill_len);
428              
429 5           newent.entity.datum = toml_string_in(tabin, key);
430              
431 5 100         if (newent.entity.datum.ok) {
432 1           newent.type = TOML_XS_TYPE_STRING;
433             }
434             else {
435 4           newent.entity.datum = toml_bool_in(tabin, key);
436              
437 4 100         if (newent.entity.datum.ok) {
438 1           newent.type = TOML_XS_TYPE_BOOLEAN;
439             }
440             else {
441 3           newent.entity.datum = toml_int_in(tabin, key);
442              
443 3 100         if (newent.entity.datum.ok) {
444 1           newent.type = TOML_XS_TYPE_INTEGER;
445             }
446             else {
447 2           newent.entity.datum = toml_double_in(tabin, key);
448              
449 2 100         if (newent.entity.datum.ok) {
450 1           newent.type = TOML_XS_TYPE_DOUBLE;
451             }
452             else {
453 1           newent.entity.datum = toml_timestamp_in(tabin, key);
454              
455 1 50         if (newent.entity.datum.ok) {
456 1           newent.type = TOML_XS_TYPE_TIMESTAMP;
457             }
458             else {
459 0           SV* json_pointer = _get_json_pointer_sv(aTHX_ stack, stack_idx);
460 0 0         if (_table_has_key_sv(tabin, key)) {
461 0           croak("Invalid table element: %" SVf, json_pointer);
462             }
463              
464 0           croak("Missing table element: %" SVf, json_pointer);
465             }
466             }
467             }
468             }
469             }
470              
471 41           return newent;
472             }
473              
474 14           toml_entity_t _drill_into_array(pTHX_ toml_array_t* arrin, SV** stack, unsigned stack_idx, unsigned drill_len, AV** err_path_ptr) {
475             toml_entity_t newent;
476              
477             int i;
478              
479 14           SV* key_sv = stack[stack_idx];
480              
481 14 50         if (SvUOK(key_sv)) {
482 0 0         i = SvUV(key_sv);
483             }
484 14 50         else if (!SvOK(key_sv)) {
    0          
    0          
485 0           croak("Undef given as pointer value (#%d)!", stack_idx);
486             }
487             else {
488             UV idx_uv;
489              
490 14 100         if (my_grok_atoUV(SvPVbyte_nolen(key_sv), &idx_uv, NULL)) {
    50          
491 14           i = idx_uv;
492             }
493             else {
494 0           SV* json_pointer = _get_json_pointer_sv(aTHX_ stack, stack_idx - 1);
495 0           sv_2mortal(json_pointer);
496 0           croak("Non-number (%" SVf ") given as index to array (JSON pointer: %" SVf ")!", key_sv, json_pointer);
497             }
498             }
499              
500 14           toml_table_t* tab = toml_table_at(arrin, i);
501              
502 14 100         if (tab) {
503 2 50         if (stack_idx == drill_len-1) {
504 2           newent.type = TOML_XS_TYPE_TABLE;
505 2           newent.entity.table_p = tab;
506 2           return newent;
507             }
508             else {
509 0           return _drill_into_table( aTHX_ tab, stack, 1 + stack_idx, drill_len, err_path_ptr);
510             }
511             }
512              
513 12           toml_array_t* arr = toml_array_at(arrin, i);
514              
515 12 50         if (arr) {
516 0 0         if (stack_idx == drill_len-1) {
517 0           newent.type = TOML_XS_TYPE_ARRAY;
518 0           newent.entity.array_p = arr;
519 0           return newent;
520             }
521             else {
522 0           return _drill_into_array( aTHX_ arr, stack, 1 + stack_idx, drill_len, err_path_ptr);
523             }
524             }
525              
526 12           _croak_if_datum_is_nonfinal_drill(aTHX_ stack, stack_idx, drill_len);
527              
528 12           newent.entity.datum = toml_string_at(arrin, i);
529              
530 12 100         if (newent.entity.datum.ok) {
531 2           newent.type = TOML_XS_TYPE_STRING;
532             }
533             else {
534 10           newent.entity.datum = toml_bool_at(arrin, i);
535              
536 10 100         if (newent.entity.datum.ok) {
537 4           newent.type = TOML_XS_TYPE_BOOLEAN;
538             }
539             else {
540 6           newent.entity.datum = toml_int_at(arrin, i);
541              
542 6 100         if (newent.entity.datum.ok) {
543 2           newent.type = TOML_XS_TYPE_INTEGER;
544             }
545             else {
546 4           newent.entity.datum = toml_double_at(arrin, i);
547              
548 4 100         if (newent.entity.datum.ok) {
549 2           newent.type = TOML_XS_TYPE_DOUBLE;
550             }
551             else {
552 2           newent.entity.datum = toml_timestamp_at(arrin, i);
553              
554 2 50         if (newent.entity.datum.ok) {
555 2           newent.type = TOML_XS_TYPE_TIMESTAMP;
556             }
557             else {
558 0           SV* json_pointer = _get_json_pointer_sv(aTHX_ stack, stack_idx);
559 0           unsigned arraylen = toml_array_nelem(arrin);
560              
561 0 0         if (i >= arraylen) {
562 0           croak("Index exceeds max array index (%d; JSON pointer: %" SVf ")", arraylen - 1, json_pointer);
563             }
564              
565 0           croak("Invalid array member (JSON pointer: %" SVf ")", json_pointer);
566             }
567             }
568             }
569             }
570             }
571              
572 14           return newent;
573             }
574              
575             /* ---------------------------------------------------------------------- */
576              
577             MODULE = TOML::XS PACKAGE = TOML::XS
578              
579             PROTOTYPES: DISABLE
580              
581             SV*
582             from_toml (SV* tomlsv)
583             CODE:
584             STRLEN tomllen;
585             char errbuf[200];
586 8 50         char* tomlstr = SvPVbyte(tomlsv, tomllen);
587              
588 8 100         _verify_no_null(tomlstr, tomllen);
589              
590 7 100         _verify_valid_utf8(tomlstr, tomllen);
591              
592 6           toml_table_t* tab = toml_parse(tomlstr, errbuf, sizeof(errbuf));
593              
594 6 100         if (tab == NULL) croak("%s", errbuf);
595              
596 4           RETVAL = _ptr_to_svrv( aTHX_ tab, gv_stashpv(DOCUMENT_CLASS, FALSE) );
597             OUTPUT:
598             RETVAL
599              
600             # ----------------------------------------------------------------------
601              
602             MODULE = TOML::XS PACKAGE = TOML::XS::Document
603              
604             PROTOTYPES: DISABLE
605              
606             SV*
607             parse (SV* docsv, ...)
608             ALIAS:
609             to_struct = 1
610             CODE:
611             UNUSED(ix);
612 25           toml_table_t* tab = _get_toml_table_from_sv(aTHX_ docsv);
613              
614 25           AV* err_path = NULL;
615              
616 25 100         if (items > 1) {
617 21           toml_entity_t root_entity = _drill_into_table(aTHX_ tab, &ST(1), 0, items-1, &err_path);
618              
619 21           switch (root_entity.type) {
620             case TOML_XS_TYPE_INVALID:
621 0           RETVAL = NULL;
622 0           break;
623             case TOML_XS_TYPE_TABLE:
624 3           RETVAL = _toml_table_to_sv(aTHX_ root_entity.entity.table_p, &err_path);
625 3           break;
626              
627             case TOML_XS_TYPE_ARRAY:
628 1           RETVAL = _toml_array_to_sv(aTHX_ root_entity.entity.array_p, &err_path);
629 1           break;
630              
631             case TOML_XS_TYPE_STRING:
632 3           RETVAL = _datum_string_to_sv(aTHX_ root_entity.entity.datum);
633 3           break;
634              
635             case TOML_XS_TYPE_BOOLEAN:
636 5 100         RETVAL = _datum_boolean_to_sv(root_entity.entity.datum);
637 5           break;
638              
639             case TOML_XS_TYPE_INTEGER:
640 3           RETVAL = _datum_integer_to_sv(root_entity.entity.datum);
641 3           break;
642              
643             case TOML_XS_TYPE_DOUBLE:
644 3           RETVAL = _datum_double_to_sv(root_entity.entity.datum);
645 3           break;
646              
647             case TOML_XS_TYPE_TIMESTAMP:
648 3           RETVAL = _datum_timestamp_to_sv(aTHX_ root_entity.entity.datum);
649 21           break;
650              
651             default:
652             assert(0);
653             }
654             }
655             else {
656 4           RETVAL = _toml_table_to_sv(aTHX_ tab, &err_path);
657             }
658              
659 25 100         if (NULL == RETVAL) {
660 2           _call_croaker_pv(aTHX_ CROAK_MALFORMED_TOML_FN, err_path);
661              
662             assert(0);
663             }
664             OUTPUT:
665             RETVAL
666              
667             void
668             DESTROY (SV* docsv)
669             CODE:
670             #if DETECT_LEAKS
671             _warn_if_global_destruct_destroy(aTHX_ docsv);
672             #endif
673              
674 4           toml_table_t* tab = _get_toml_table_from_sv(aTHX_ docsv);
675 4           toml_free(tab);
676              
677             # ----------------------------------------------------------------------
678              
679             MODULE = TOML::XS PACKAGE = TOML::XS::Timestamp
680              
681             PROTOTYPES: DISABLE
682              
683             SV*
684             to_string (SV* selfsv)
685             CODE:
686 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
687              
688 10           RETVAL = newSVpvs("");
689              
690 10 50         if (NULL != ts->year) {
691 10           sv_catpvf(
692             RETVAL,
693             "%02d-%02d-%02d",
694 30           *ts->year, *ts->month, *ts->day
695             );
696             }
697              
698 10 50         if (NULL != ts->hour) {
699 10           sv_catpvf(
700             RETVAL,
701             "T%02d:%02d:%02d",
702 30           *ts->hour, *ts->minute, *ts->second
703             );
704              
705 10 100         if (NULL != ts->millisec) {
706 1           sv_catpvf(
707             RETVAL,
708             ".%03d",
709 1           *ts->millisec
710             );
711             }
712             }
713              
714 10 50         if (NULL != ts->z) {
715 10           sv_catpv(RETVAL, ts->z);
716             }
717             OUTPUT:
718             RETVAL
719              
720             SV*
721             year (SV* selfsv)
722             CODE:
723 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
724              
725 10 50         RETVAL = ts->year ? newSViv(*ts->year) : &PL_sv_undef;
726             OUTPUT:
727             RETVAL
728              
729             SV*
730             month (SV* selfsv)
731             CODE:
732 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
733              
734 10 50         RETVAL = ts->month ? newSViv(*ts->month) : &PL_sv_undef;
735             OUTPUT:
736             RETVAL
737              
738             SV*
739             day (SV* selfsv)
740             ALIAS:
741             date = 1
742             CODE:
743             UNUSED(ix);
744 20           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
745              
746 20 50         RETVAL = ts->day ? newSViv(*ts->day) : &PL_sv_undef;
747             OUTPUT:
748             RETVAL
749              
750             SV*
751             hour (SV* selfsv)
752             ALIAS:
753             hours = 1
754             CODE:
755             UNUSED(ix);
756 20           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
757              
758 20 50         RETVAL = ts->hour ? newSViv(*ts->hour) : &PL_sv_undef;
759             OUTPUT:
760             RETVAL
761              
762             SV*
763             minute (SV* selfsv)
764             ALIAS:
765             minutes = 1
766             CODE:
767             UNUSED(ix);
768 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
769              
770 10 50         RETVAL = ts->minute ? newSViv(*ts->minute) : &PL_sv_undef;
771             OUTPUT:
772             RETVAL
773              
774             SV*
775             second (SV* selfsv)
776             ALIAS:
777             seconds = 1
778             CODE:
779             UNUSED(ix);
780 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
781              
782 10 50         RETVAL = ts->second ? newSViv(*ts->second) : &PL_sv_undef;
783             OUTPUT:
784             RETVAL
785              
786             SV*
787             millisecond (SV* selfsv)
788             ALIAS:
789             milliseconds = 1
790             CODE:
791             UNUSED(ix);
792 20           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
793              
794 20 100         RETVAL = ts->millisec ? newSViv(*ts->millisec) : &PL_sv_undef;
795             OUTPUT:
796             RETVAL
797              
798             SV*
799             timezone (SV* selfsv)
800             CODE:
801 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
802              
803 10 50         RETVAL = ts->z ? newSVpv(ts->z, 0) : &PL_sv_undef;
804             OUTPUT:
805             RETVAL
806              
807             void
808             DESTROY (SV* selfsv)
809             CODE:
810             #if DETECT_LEAKS
811             _warn_if_global_destruct_destroy(aTHX_ selfsv);
812             #endif
813              
814 10           toml_timestamp_t* ts = _get_toml_timestamp_from_sv(aTHX_ selfsv);
815 10           tomlxs_free_timestamp(ts);