File Coverage

XS.xs
Criterion Covered Total %
statement 220 286 76.9
branch 96 172 55.8
condition n/a
subroutine n/a
pod n/a
total 316 458 69.0


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             #include "ppport.h"
6              
7             #include
8              
9             #include
10             #include
11              
12             /* ------------------------------------------------------------------------- */
13             /* Wrapper structs */
14             /* ------------------------------------------------------------------------- */
15              
16             typedef struct {
17             kdl_parser* parser;
18             SV* read_cb; /* Perl coderef, or NULL */
19             SV* keepalive; /* string SV holding the input buffer, or NULL */
20             } ptkx_parser;
21              
22             typedef struct {
23             kdl_emitter* emitter;
24             } ptkx_emitter;
25              
26             /* ------------------------------------------------------------------------- */
27             /* Helpers: build a Text::KDL::XS::Value SV from a kdl_value */
28             /* ------------------------------------------------------------------------- */
29              
30             static SV*
31 61           ptkx_make_value_sv(pTHX_ const kdl_value* v)
32             {
33 61           HV* hv = newHV();
34              
35             /* type_annotation: string or undef */
36 61 100         if (v->type_annotation.data != NULL) {
37 1           SV* ann = newSVpvn(v->type_annotation.data, v->type_annotation.len);
38 1           SvUTF8_on(ann);
39 1           (void) hv_stores(hv, "type_annotation", ann);
40             }
41             else {
42 60           (void) hv_stores(hv, "type_annotation", &PL_sv_undef);
43             }
44              
45 61           switch (v->type) {
46 2           case KDL_TYPE_NULL:
47 2           (void) hv_stores(hv, "type", newSVpvs("null"));
48 2           (void) hv_stores(hv, "value", &PL_sv_undef);
49 2           break;
50 4           case KDL_TYPE_BOOLEAN:
51 4           (void) hv_stores(hv, "type", newSVpvs("bool"));
52 4           (void) hv_stores(hv, "value", newSViv(v->boolean ? 1 : 0));
53 4           break;
54 38           case KDL_TYPE_STRING: {
55 38           SV* sv = newSVpvn(v->string.data, v->string.len);
56 38           SvUTF8_on(sv);
57 38           (void) hv_stores(hv, "type", newSVpvs("string"));
58 38           (void) hv_stores(hv, "value", sv);
59 38           break;
60             }
61 17           case KDL_TYPE_NUMBER: {
62 17           (void) hv_stores(hv, "type", newSVpvs("number"));
63 17           switch (v->number.type) {
64 13           case KDL_NUMBER_TYPE_INTEGER:
65 13           (void) hv_stores(hv, "kind", newSVpvs("integer"));
66 13           (void) hv_stores(hv, "value", newSViv((IV) v->number.integer));
67 13           break;
68 3           case KDL_NUMBER_TYPE_FLOATING_POINT:
69 3           (void) hv_stores(hv, "kind", newSVpvs("float"));
70 3           (void) hv_stores(hv, "value", newSVnv(v->number.floating_point));
71 3           break;
72 1           case KDL_NUMBER_TYPE_STRING_ENCODED: {
73 1           SV* sv = newSVpvn(v->number.string.data, v->number.string.len);
74 1           (void) hv_stores(hv, "kind", newSVpvs("string"));
75 1           (void) hv_stores(hv, "value", sv);
76 1           break;
77             }
78             }
79 17           break;
80             }
81             }
82              
83 61           SV* rv = newRV_noinc((SV*) hv);
84 61           HV* stash = gv_stashpv("Text::KDL::XS::Value", GV_ADD);
85 61           sv_bless(rv, stash);
86 61           return rv;
87             }
88              
89             /* ------------------------------------------------------------------------- */
90             /* Stream-source trampoline: invoke a Perl coderef returning chunks */
91             /* ------------------------------------------------------------------------- */
92              
93             static size_t
94 15           ptkx_read_thunk(void* user_data, char* buf, size_t bufsize)
95             {
96             dTHX;
97 15           ptkx_parser* self = (ptkx_parser*) user_data;
98 15           SV* cb = self->read_cb;
99 15 50         if (!cb) return 0;
100              
101 15           dSP;
102 15           ENTER;
103 15           SAVETMPS;
104 15 50         PUSHMARK(SP);
105 15 50         XPUSHs(sv_2mortal(newSVuv((UV) bufsize)));
106 15           PUTBACK;
107              
108 15           int n = call_sv(cb, G_SCALAR | G_EVAL);
109              
110 15           SPAGAIN;
111 15           size_t got = 0;
112 15 50         if (SvTRUE(ERRSV)) {
    50          
113             /* Swallow the error to a zero-byte read; the parser will treat it
114             * as EOF. The original error is preserved on $@ for the caller. */
115 0           (void) POPs;
116             }
117 15 50         else if (n >= 1) {
118 15           SV* chunk = POPs;
119 15 50         if (SvOK(chunk)) {
120             STRLEN len;
121 15           const char* data = SvPVbyte(chunk, len);
122 15 50         if (len > bufsize) len = bufsize;
123 15 100         if (len > 0) memcpy(buf, data, len);
124 15           got = len;
125             }
126             }
127 15           PUTBACK;
128 15 50         FREETMPS;
129 15           LEAVE;
130 15           return got;
131             }
132              
133             /* ------------------------------------------------------------------------- */
134             /* Parser construction helper */
135             /* ------------------------------------------------------------------------- */
136              
137             static SV*
138 0           ptkx_bless_parser(pTHX_ ptkx_parser* p)
139             {
140 0           SV* obj = newSV(0);
141 0           HV* stash = gv_stashpv("Text::KDL::XS::Parser", GV_ADD);
142 0           sv_setref_pv(obj, "Text::KDL::XS::Parser", (void*) p);
143             (void) stash;
144 0           return obj;
145             }
146              
147             /* ------------------------------------------------------------------------- */
148             /* XS bindings */
149             /* ------------------------------------------------------------------------- */
150              
151             MODULE = Text::KDL::XS PACKAGE = Text::KDL::XS
152              
153             PROTOTYPES: DISABLE
154              
155             BOOT:
156             /* Export option constants as package globals for Perl to consume. */
157 7           HV* opt_stash = gv_stashpv("Text::KDL::XS", GV_ADD);
158             (void) opt_stash;
159              
160             # --- Constants -------------------------------------------------------------
161              
162             int
163             _OPT_DETECT()
164             CODE:
165 16 100         RETVAL = (int) KDL_DETECT_VERSION;
166             OUTPUT:
167             RETVAL
168              
169             int
170             _OPT_V1()
171             CODE:
172 0 0         RETVAL = (int) KDL_READ_VERSION_1;
173             OUTPUT:
174             RETVAL
175              
176             int
177             _OPT_V2()
178             CODE:
179 3 100         RETVAL = (int) KDL_READ_VERSION_2;
180             OUTPUT:
181             RETVAL
182              
183             int
184             _OPT_EMIT_COMMENTS()
185             CODE:
186 0 0         RETVAL = (int) KDL_EMIT_COMMENTS;
187             OUTPUT:
188             RETVAL
189              
190              
191             MODULE = Text::KDL::XS PACKAGE = Text::KDL::XS::Parser
192              
193             # --- Parser construction ---------------------------------------------------
194              
195             SV*
196             _new_string_parser(klass, doc_sv, opts)
197             SV* klass
198             SV* doc_sv
199             int opts
200             PREINIT:
201             STRLEN len;
202             const char* data;
203             ptkx_parser* p;
204             kdl_str doc;
205             CODE:
206             (void) klass;
207 17 50         if (!SvOK(doc_sv))
208 0           croak("Text::KDL::XS::Parser: document must be a defined string");
209 17           data = SvPVbyte(doc_sv, len);
210              
211 17           Newxz(p, 1, ptkx_parser);
212             /* Keep a reference to the input SV alive - ckdl's string parser
213             * holds onto the buffer until kdl_destroy_parser. */
214 17           p->keepalive = newSVsv(doc_sv);
215 17           SvPV_force_nomg(p->keepalive, len);
216 17           data = SvPVbyte_nolen(p->keepalive);
217 17           len = SvCUR(p->keepalive);
218              
219 17           doc.data = data;
220 17           doc.len = len;
221 17           p->parser = kdl_create_string_parser(doc, (kdl_parse_option) opts);
222 17 50         if (!p->parser) {
223 0           SvREFCNT_dec(p->keepalive);
224 0           Safefree(p);
225 0           croak("kdl_create_string_parser failed");
226             }
227 17           RETVAL = sv_newmortal();
228 17           sv_setref_pv(RETVAL, "Text::KDL::XS::Parser", (void*) p);
229 17           SvREFCNT_inc(RETVAL);
230             OUTPUT:
231             RETVAL
232              
233             SV*
234             _new_stream_parser(klass, cb_sv, opts)
235             SV* klass
236             SV* cb_sv
237             int opts
238             PREINIT:
239             ptkx_parser* p;
240             CODE:
241             (void) klass;
242 2 50         if (!SvROK(cb_sv) || SvTYPE(SvRV(cb_sv)) != SVt_PVCV)
    50          
243 0           croak("Text::KDL::XS::Parser: stream source must be a CODE ref");
244              
245 2           Newxz(p, 1, ptkx_parser);
246 2           p->read_cb = newSVsv(cb_sv);
247 2           p->parser = kdl_create_stream_parser(ptkx_read_thunk, (void*) p,
248             (kdl_parse_option) opts);
249 2 50         if (!p->parser) {
250 0           SvREFCNT_dec(p->read_cb);
251 0           Safefree(p);
252 0           croak("kdl_create_stream_parser failed");
253             }
254 2           RETVAL = sv_newmortal();
255 2           sv_setref_pv(RETVAL, "Text::KDL::XS::Parser", (void*) p);
256 2           SvREFCNT_inc(RETVAL);
257             OUTPUT:
258             RETVAL
259              
260             # --- Parser events ---------------------------------------------------------
261              
262             SV*
263             _next_event(self_sv)
264             SV* self_sv
265             PREINIT:
266             ptkx_parser* p;
267             kdl_event_data* ev;
268             int real_event;
269             int commented;
270             CODE:
271 195 50         if (!sv_isa(self_sv, "Text::KDL::XS::Parser"))
272 0           croak("not a Text::KDL::XS::Parser");
273 195           p = INT2PTR(ptkx_parser*, SvIV(SvRV(self_sv)));
274              
275 195           ev = kdl_parser_next_event(p->parser);
276 195 50         if (!ev) croak("kdl_parser_next_event returned NULL");
277              
278 195 100         if (ev->event == KDL_EVENT_EOF) {
279 17           RETVAL = &PL_sv_undef;
280             }
281 178 100         else if (ev->event == KDL_EVENT_PARSE_ERROR) {
282 2           croak("KDL parse error");
283             }
284             else {
285 176           HV* h = newHV();
286 176           real_event = (int)(ev->event & ~KDL_EVENT_COMMENT);
287 176           commented = (ev->event & KDL_EVENT_COMMENT) ? 1 : 0;
288              
289             const char* name;
290 176           switch (real_event) {
291 58           case KDL_EVENT_START_NODE: name = "start_node"; break;
292 57           case KDL_EVENT_END_NODE: name = "end_node"; break;
293 57           case KDL_EVENT_ARGUMENT: name = "argument"; break;
294 4           case KDL_EVENT_PROPERTY: name = "property"; break;
295 0           case 0: name = "comment"; break;
296 0           default: name = "unknown"; break;
297             }
298 176           (void) hv_stores(h, "event", newSVpv(name, 0));
299 176           (void) hv_stores(h, "commented", newSViv(commented));
300              
301 176 100         if (ev->name.data != NULL) {
302 62           SV* nm = newSVpvn(ev->name.data, ev->name.len);
303 62           SvUTF8_on(nm);
304 62           (void) hv_stores(h, "name", nm);
305             }
306              
307 176 100         if (real_event == KDL_EVENT_START_NODE) {
308             /* type annotation lives on the value for nodes */
309 58 50         if (ev->value.type_annotation.data != NULL) {
310 0           SV* ann = newSVpvn(ev->value.type_annotation.data,
311             ev->value.type_annotation.len);
312 0           SvUTF8_on(ann);
313 0           (void) hv_stores(h, "type", ann);
314             }
315             }
316 118 100         else if (real_event == KDL_EVENT_ARGUMENT
317 61 100         || real_event == KDL_EVENT_PROPERTY) {
318 61           (void) hv_stores(h, "value",
319             ptkx_make_value_sv(aTHX_ &ev->value));
320             }
321              
322 176           RETVAL = newRV_noinc((SV*) h);
323             }
324             OUTPUT:
325             RETVAL
326              
327             void
328             DESTROY(self_sv)
329             SV* self_sv
330             PREINIT:
331             ptkx_parser* p;
332             CODE:
333 19 50         if (!sv_isa(self_sv, "Text::KDL::XS::Parser")) return;
334 19           p = INT2PTR(ptkx_parser*, SvIV(SvRV(self_sv)));
335 19 50         if (!p) return;
336 19 50         if (p->parser) kdl_destroy_parser(p->parser);
337 19 100         if (p->read_cb) SvREFCNT_dec(p->read_cb);
338 19 100         if (p->keepalive) SvREFCNT_dec(p->keepalive);
339 19           Safefree(p);
340              
341              
342             MODULE = Text::KDL::XS PACKAGE = Text::KDL::XS::Emitter
343              
344             # --- Emitter ---------------------------------------------------------------
345              
346             SV*
347             _new(klass, version_int, indent, escape_mode, identifier_mode)
348             SV* klass
349             int version_int
350             int indent
351             int escape_mode
352             int identifier_mode
353             PREINIT:
354             ptkx_emitter* e;
355             kdl_emitter_options opts;
356             CODE:
357             (void) klass;
358 13           opts = KDL_DEFAULT_EMITTER_OPTIONS;
359 13 50         if (version_int == 1) opts.version = KDL_VERSION_1;
360 13 100         else if (version_int == 2) opts.version = KDL_VERSION_2;
361 13 50         if (indent >= 0) opts.indent = indent;
362 13 50         if (escape_mode >= 0) opts.escape_mode = (kdl_escape_mode) escape_mode;
363 13 50         if (identifier_mode >= 0)
364 0           opts.identifier_mode = (kdl_identifier_emission_mode) identifier_mode;
365              
366 13           Newxz(e, 1, ptkx_emitter);
367 13           e->emitter = kdl_create_buffering_emitter(&opts);
368 13 50         if (!e->emitter) {
369 0           Safefree(e);
370 0           croak("kdl_create_buffering_emitter failed");
371             }
372 13           RETVAL = sv_newmortal();
373 13           sv_setref_pv(RETVAL, "Text::KDL::XS::Emitter", (void*) e);
374 13           SvREFCNT_inc(RETVAL);
375             OUTPUT:
376             RETVAL
377              
378             bool
379             _emit_node(self_sv, name_sv, type_sv)
380             SV* self_sv
381             SV* name_sv
382             SV* type_sv
383             PREINIT:
384             ptkx_emitter* e;
385             kdl_str name;
386             kdl_str type;
387             STRLEN len;
388             const char* data;
389             CODE:
390 39 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
391 0           croak("not a Text::KDL::XS::Emitter");
392 39           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
393              
394 39           data = SvPVutf8(name_sv, len);
395 39           name.data = data; name.len = len;
396              
397 39 50         if (SvOK(type_sv)) {
398 0           data = SvPVutf8(type_sv, len);
399 0           type.data = data; type.len = len;
400 0           RETVAL = kdl_emit_node_with_type(e->emitter, type, name);
401             }
402             else {
403 39           RETVAL = kdl_emit_node(e->emitter, name);
404             }
405             OUTPUT:
406             RETVAL
407              
408             bool
409             _emit_arg(self_sv, value_hv_ref)
410             SV* self_sv
411             SV* value_hv_ref
412             PREINIT:
413             ptkx_emitter* e;
414             kdl_value v;
415             CODE:
416 33 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
417 0           croak("not a Text::KDL::XS::Emitter");
418 33           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
419              
420             /* Caller has already populated kdl_value-ish hash with C-friendly
421             * values. We translate here to keep the hot path tight. */
422 33 50         if (!SvROK(value_hv_ref) || SvTYPE(SvRV(value_hv_ref)) != SVt_PVHV)
    50          
423 0           croak("emit_arg: expected HASH ref");
424              
425 33           memset(&v, 0, sizeof(v));
426 33           HV* hv = (HV*) SvRV(value_hv_ref);
427              
428 33           SV** sv_type = hv_fetchs(hv, "type", 0);
429 33           SV** sv_ann = hv_fetchs(hv, "type_annotation", 0);
430 33           SV** sv_val = hv_fetchs(hv, "value", 0);
431 33           SV** sv_kind = hv_fetchs(hv, "kind", 0);
432              
433 33           STRLEN slen = 0;
434             const char* sdata;
435              
436 33 100         if (sv_ann && *sv_ann && SvOK(*sv_ann)) {
    50          
    50          
437 0           sdata = SvPVutf8(*sv_ann, slen);
438 0           v.type_annotation.data = sdata;
439 0           v.type_annotation.len = slen;
440             }
441              
442 33 50         if (!sv_type || !*sv_type) croak("emit_arg: missing 'type'");
    50          
443 33           const char* type_str = SvPV_nolen(*sv_type);
444              
445 33 100         if (strEQ(type_str, "null")) {
446 1           v.type = KDL_TYPE_NULL;
447             }
448 32 100         else if (strEQ(type_str, "bool")) {
449 2           v.type = KDL_TYPE_BOOLEAN;
450 2 50         v.boolean = (sv_val && SvTRUE(*sv_val));
    100          
451             }
452 30 100         else if (strEQ(type_str, "string")) {
453 25           v.type = KDL_TYPE_STRING;
454 25           sdata = SvPVutf8(*sv_val, slen);
455 25           v.string.data = sdata; v.string.len = slen;
456             }
457 5 50         else if (strEQ(type_str, "number")) {
458 5           v.type = KDL_TYPE_NUMBER;
459 5 50         const char* kind = sv_kind && *sv_kind ? SvPV_nolen(*sv_kind)
460 5 50         : "integer";
461 5 100         if (strEQ(kind, "integer")) {
462 4           v.number.type = KDL_NUMBER_TYPE_INTEGER;
463 4           v.number.integer = (long long) SvIV(*sv_val);
464             }
465 1 50         else if (strEQ(kind, "float")) {
466 1           v.number.type = KDL_NUMBER_TYPE_FLOATING_POINT;
467 1           v.number.floating_point = SvNV(*sv_val);
468             }
469             else {
470 0           v.number.type = KDL_NUMBER_TYPE_STRING_ENCODED;
471 0           sdata = SvPVbyte(*sv_val, slen);
472 0           v.number.string.data = sdata;
473 0           v.number.string.len = slen;
474             }
475             }
476             else {
477 0           croak("emit_arg: unknown value type '%s'", type_str);
478             }
479              
480 33           RETVAL = kdl_emit_arg(e->emitter, &v);
481             OUTPUT:
482             RETVAL
483              
484             bool
485             _emit_property(self_sv, key_sv, value_hv_ref)
486             SV* self_sv
487             SV* key_sv
488             SV* value_hv_ref
489             PREINIT:
490             ptkx_emitter* e;
491             kdl_value v;
492             kdl_str key;
493 1           STRLEN slen = 0;
494             const char* sdata;
495             CODE:
496 1 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
497 0           croak("not a Text::KDL::XS::Emitter");
498 1           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
499              
500 1           sdata = SvPVutf8(key_sv, slen);
501 1           key.data = sdata; key.len = slen;
502              
503 1 50         if (!SvROK(value_hv_ref) || SvTYPE(SvRV(value_hv_ref)) != SVt_PVHV)
    50          
504 0           croak("emit_property: expected HASH ref");
505              
506 1           memset(&v, 0, sizeof(v));
507 1           HV* hv = (HV*) SvRV(value_hv_ref);
508              
509 1           SV** sv_type = hv_fetchs(hv, "type", 0);
510 1           SV** sv_ann = hv_fetchs(hv, "type_annotation", 0);
511 1           SV** sv_val = hv_fetchs(hv, "value", 0);
512 1           SV** sv_kind = hv_fetchs(hv, "kind", 0);
513              
514 1 50         if (sv_ann && *sv_ann && SvOK(*sv_ann)) {
    50          
    50          
515 0           sdata = SvPVutf8(*sv_ann, slen);
516 0           v.type_annotation.data = sdata;
517 0           v.type_annotation.len = slen;
518             }
519              
520 1 50         if (!sv_type || !*sv_type) croak("emit_property: missing 'type'");
    50          
521 1           const char* type_str = SvPV_nolen(*sv_type);
522              
523 1 50         if (strEQ(type_str, "null")) {
524 0           v.type = KDL_TYPE_NULL;
525             }
526 1 50         else if (strEQ(type_str, "bool")) {
527 0           v.type = KDL_TYPE_BOOLEAN;
528 0 0         v.boolean = (sv_val && SvTRUE(*sv_val));
    0          
529             }
530 1 50         else if (strEQ(type_str, "string")) {
531 1           v.type = KDL_TYPE_STRING;
532 1           sdata = SvPVutf8(*sv_val, slen);
533 1           v.string.data = sdata; v.string.len = slen;
534             }
535 0 0         else if (strEQ(type_str, "number")) {
536 0           v.type = KDL_TYPE_NUMBER;
537 0 0         const char* kind = sv_kind && *sv_kind ? SvPV_nolen(*sv_kind)
538 0 0         : "integer";
539 0 0         if (strEQ(kind, "integer")) {
540 0           v.number.type = KDL_NUMBER_TYPE_INTEGER;
541 0           v.number.integer = (long long) SvIV(*sv_val);
542             }
543 0 0         else if (strEQ(kind, "float")) {
544 0           v.number.type = KDL_NUMBER_TYPE_FLOATING_POINT;
545 0           v.number.floating_point = SvNV(*sv_val);
546             }
547             else {
548 0           v.number.type = KDL_NUMBER_TYPE_STRING_ENCODED;
549 0           sdata = SvPVbyte(*sv_val, slen);
550 0           v.number.string.data = sdata;
551 0           v.number.string.len = slen;
552             }
553             }
554             else {
555 0           croak("emit_property: unknown value type '%s'", type_str);
556             }
557              
558 1           RETVAL = kdl_emit_property(e->emitter, key, &v);
559             OUTPUT:
560             RETVAL
561              
562             bool
563             _start_children(self_sv)
564             SV* self_sv
565             PREINIT:
566             ptkx_emitter* e;
567             CODE:
568 8 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
569 0           croak("not a Text::KDL::XS::Emitter");
570 8           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
571 8           RETVAL = kdl_start_emitting_children(e->emitter);
572             OUTPUT:
573             RETVAL
574              
575             bool
576             _finish_children(self_sv)
577             SV* self_sv
578             PREINIT:
579             ptkx_emitter* e;
580             CODE:
581 8 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
582 0           croak("not a Text::KDL::XS::Emitter");
583 8           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
584 8           RETVAL = kdl_finish_emitting_children(e->emitter);
585             OUTPUT:
586             RETVAL
587              
588             bool
589             _emit_end(self_sv)
590             SV* self_sv
591             PREINIT:
592             ptkx_emitter* e;
593             CODE:
594 12 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
595 0           croak("not a Text::KDL::XS::Emitter");
596 12           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
597 12           RETVAL = kdl_emit_end(e->emitter);
598             OUTPUT:
599             RETVAL
600              
601             SV*
602             _get_buffer(self_sv)
603             SV* self_sv
604             PREINIT:
605             ptkx_emitter* e;
606             kdl_str buf;
607             CODE:
608 12 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter"))
609 0           croak("not a Text::KDL::XS::Emitter");
610 12           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
611 12           buf = kdl_get_emitter_buffer(e->emitter);
612 12 50         RETVAL = newSVpvn(buf.data ? buf.data : "", buf.len);
613 12           SvUTF8_on(RETVAL);
614             OUTPUT:
615             RETVAL
616              
617             void
618             DESTROY(self_sv)
619             SV* self_sv
620             PREINIT:
621             ptkx_emitter* e;
622             CODE:
623 13 50         if (!sv_isa(self_sv, "Text::KDL::XS::Emitter")) return;
624 13           e = INT2PTR(ptkx_emitter*, SvIV(SvRV(self_sv)));
625 13 50         if (!e) return;
626 13 50         if (e->emitter) kdl_destroy_emitter(e->emitter);
627 13           Safefree(e);