File Coverage

XS.xs
Criterion Covered Total %
statement 223 281 79.3
branch 116 172 67.4
condition n/a
subroutine n/a
pod n/a
total 339 453 74.8


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