Ruby: ext/psych/psych_emitter.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

psych_emitter.c
Go to the documentation of this file.
1#include <psych.h>
2
3#if !defined(RARRAY_CONST_PTR)
4#define RARRAY_CONST_PTR(s) (const VALUE *)RARRAY_PTR(s)
5#endif
6#if !defined(RARRAY_AREF)
7#define RARRAY_AREF(a, i) RARRAY_CONST_PTR(a)[i]
8#endif
9
10 VALUE cPsychEmitter;
11static ID id_io;
12static ID id_write;
13static ID id_line_width;
14static ID id_indentation;
15static ID id_canonical;
16
17static void emit(yaml_emitter_t * emitter, yaml_event_t * event)
18{
19 if(!yaml_emitter_emit(emitter, event))
20 rb_raise(rb_eRuntimeError, "%s", emitter->problem);
21}
22
23static int writer(void *ctx, unsigned char *buffer, size_t size)
24{
25 VALUE self = (VALUE)ctx, io = rb_attr_get(self, id_io);
26 VALUE str = rb_enc_str_new((const char *)buffer, (long)size, rb_utf8_encoding());
27 VALUE wrote = rb_funcall(io, id_write, 1, str);
28 return (int)NUM2INT(wrote);
29}
30
31static void dealloc(void * ptr)
32{
33 yaml_emitter_t * emitter;
34
35 emitter = (yaml_emitter_t *)ptr;
36 yaml_emitter_delete(emitter);
37 xfree(emitter);
38}
39
40#if 0
41static size_t memsize(const void *ptr)
42{
43 const yaml_emitter_t *emitter = ptr;
44 /* TODO: calculate emitter's size */
45 return 0;
46}
47#endif
48
49static const rb_data_type_t psych_emitter_type = {
50 "Psych/emitter",
51 {0, dealloc, 0,},
52 0, 0,
53#ifdef RUBY_TYPED_FREE_IMMEDIATELY
54 RUBY_TYPED_FREE_IMMEDIATELY,
55#endif
56};
57
58static VALUE allocate(VALUE klass)
59{
60 yaml_emitter_t * emitter;
61 VALUE obj = TypedData_Make_Struct(klass, yaml_emitter_t, &psych_emitter_type, emitter);
62
63 yaml_emitter_initialize(emitter);
64 yaml_emitter_set_unicode(emitter, 1);
65 yaml_emitter_set_indent(emitter, 2);
66
67 return obj;
68}
69
70/* call-seq: Psych::Emitter.new(io, options = Psych::Emitter::OPTIONS)
71 *
72 * Create a new Psych::Emitter that writes to +io+.
73 */
74static VALUE initialize(int argc, VALUE *argv, VALUE self)
75{
76 yaml_emitter_t * emitter;
77 VALUE io, options;
78 VALUE line_width;
79 VALUE indent;
80 VALUE canonical;
81
82 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
83
84 if (rb_scan_args(argc, argv, "11", &io, &options) == 2) {
85 line_width = rb_funcall(options, id_line_width, 0);
86 indent = rb_funcall(options, id_indentation, 0);
87 canonical = rb_funcall(options, id_canonical, 0);
88
89 yaml_emitter_set_width(emitter, NUM2INT(line_width));
90 yaml_emitter_set_indent(emitter, NUM2INT(indent));
91 yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0);
92 }
93
94 rb_ivar_set(self, id_io, io);
95 yaml_emitter_set_output(emitter, writer, (void *)self);
96
97 return self;
98}
99
100/* call-seq: emitter.start_stream(encoding)
101 *
102 * Start a stream emission with +encoding+
103 *
104 * See Psych::Handler#start_stream
105 */
106static VALUE start_stream(VALUE self, VALUE encoding)
107{
108 yaml_emitter_t * emitter;
109 yaml_event_t event;
110 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
111 Check_Type(encoding, T_FIXNUM);
112
113 yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding));
114
115 emit(emitter, &event);
116
117 return self;
118}
119
120/* call-seq: emitter.end_stream
121 *
122 * End a stream emission
123 *
124 * See Psych::Handler#end_stream
125 */
126static VALUE end_stream(VALUE self)
127{
128 yaml_emitter_t * emitter;
129 yaml_event_t event;
130 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
131
132 yaml_stream_end_event_initialize(&event);
133
134 emit(emitter, &event);
135
136 return self;
137}
138
139/* call-seq: emitter.start_document(version, tags, implicit)
140 *
141 * Start a document emission with YAML +version+, +tags+, and an +implicit+
142 * start.
143 *
144 * See Psych::Handler#start_document
145 */
146static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
147{
148 yaml_emitter_t * emitter;
149 yaml_tag_directive_t * head = NULL;
150 yaml_tag_directive_t * tail = NULL;
151 yaml_event_t event;
152 yaml_version_directive_t version_directive;
153 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
154
155
156 Check_Type(version, T_ARRAY);
157
158 if(RARRAY_LEN(version) > 0) {
159 VALUE major = rb_ary_entry(version, (long)0);
160 VALUE minor = rb_ary_entry(version, (long)1);
161
162 version_directive.major = NUM2INT(major);
163 version_directive.minor = NUM2INT(minor);
164 }
165
166 if(RTEST(tags)) {
167 long i = 0;
168 long len;
169 rb_encoding * encoding = rb_utf8_encoding();
170
171 Check_Type(tags, T_ARRAY);
172
173 len = RARRAY_LEN(tags);
174 head = xcalloc((size_t)len, sizeof(yaml_tag_directive_t));
175 tail = head;
176
177 for(i = 0; i < len && i < RARRAY_LEN(tags); i++) {
178 VALUE tuple = RARRAY_AREF(tags, i);
179 VALUE name;
180 VALUE value;
181
182 Check_Type(tuple, T_ARRAY);
183
184 if(RARRAY_LEN(tuple) < 2) {
185 xfree(head);
186 rb_raise(rb_eRuntimeError, "tag tuple must be of length 2");
187 }
188 name = RARRAY_AREF(tuple, 0);
189 value = RARRAY_AREF(tuple, 1);
190 StringValue(name);
191 StringValue(value);
192 name = rb_str_export_to_enc(name, encoding);
193 value = rb_str_export_to_enc(value, encoding);
194
195 tail->handle = (yaml_char_t *)StringValueCStr(name);
196 tail->prefix = (yaml_char_t *)StringValueCStr(value);
197
198 tail++;
199 }
200 }
201
202 yaml_document_start_event_initialize(
203 &event,
204 (RARRAY_LEN(version) > 0) ? &version_directive : NULL,
205 head,
206 tail,
207 imp ? 1 : 0
208 );
209
210 emit(emitter, &event);
211
212 if(head) xfree(head);
213
214 return self;
215}
216
217/* call-seq: emitter.end_document(implicit)
218 *
219 * End a document emission with an +implicit+ ending.
220 *
221 * See Psych::Handler#end_document
222 */
223static VALUE end_document(VALUE self, VALUE imp)
224{
225 yaml_emitter_t * emitter;
226 yaml_event_t event;
227 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
228
229 yaml_document_end_event_initialize(&event, imp ? 1 : 0);
230
231 emit(emitter, &event);
232
233 return self;
234}
235
236/* call-seq: emitter.scalar(value, anchor, tag, plain, quoted, style)
237 *
238 * Emit a scalar with +value+, +anchor+, +tag+, and a +plain+ or +quoted+
239 * string type with +style+.
240 *
241 * See Psych::Handler#scalar
242 */
243static VALUE scalar(
244 VALUE self,
245 VALUE value,
246 VALUE anchor,
247 VALUE tag,
248 VALUE plain,
249 VALUE quoted,
250 VALUE style
251 ) {
252 yaml_emitter_t * emitter;
253 yaml_event_t event;
254 rb_encoding *encoding;
255 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
256
257 Check_Type(value, T_STRING);
258
259 encoding = rb_utf8_encoding();
260
261 value = rb_str_export_to_enc(value, encoding);
262
263 if(!NIL_P(anchor)) {
264 Check_Type(anchor, T_STRING);
265 anchor = rb_str_export_to_enc(anchor, encoding);
266 }
267
268 if(!NIL_P(tag)) {
269 Check_Type(tag, T_STRING);
270 tag = rb_str_export_to_enc(tag, encoding);
271 }
272
273 yaml_scalar_event_initialize(
274 &event,
275 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
276 (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
277 (yaml_char_t*)StringValuePtr(value),
278 (int)RSTRING_LEN(value),
279 plain ? 1 : 0,
280 quoted ? 1 : 0,
281 (yaml_scalar_style_t)NUM2INT(style)
282 );
283
284 emit(emitter, &event);
285
286 return self;
287}
288
289/* call-seq: emitter.start_sequence(anchor, tag, implicit, style)
290 *
291 * Start emitting a sequence with +anchor+, a +tag+, +implicit+ sequence
292 * start and end, along with +style+.
293 *
294 * See Psych::Handler#start_sequence
295 */
296static VALUE start_sequence(
297 VALUE self,
298 VALUE anchor,
299 VALUE tag,
300 VALUE implicit,
301 VALUE style
302 ) {
303 yaml_emitter_t * emitter;
304 yaml_event_t event;
305
306 rb_encoding * encoding = rb_utf8_encoding();
307
308 if(!NIL_P(anchor)) {
309 Check_Type(anchor, T_STRING);
310 anchor = rb_str_export_to_enc(anchor, encoding);
311 }
312
313 if(!NIL_P(tag)) {
314 Check_Type(tag, T_STRING);
315 tag = rb_str_export_to_enc(tag, encoding);
316 }
317
318 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
319
320 yaml_sequence_start_event_initialize(
321 &event,
322 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
323 (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
324 implicit ? 1 : 0,
325 (yaml_sequence_style_t)NUM2INT(style)
326 );
327
328 emit(emitter, &event);
329
330 return self;
331}
332
333/* call-seq: emitter.end_sequence
334 *
335 * End sequence emission.
336 *
337 * See Psych::Handler#end_sequence
338 */
339static VALUE end_sequence(VALUE self)
340{
341 yaml_emitter_t * emitter;
342 yaml_event_t event;
343 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
344
345 yaml_sequence_end_event_initialize(&event);
346
347 emit(emitter, &event);
348
349 return self;
350}
351
352/* call-seq: emitter.start_mapping(anchor, tag, implicit, style)
353 *
354 * Start emitting a YAML map with +anchor+, +tag+, an +implicit+ start
355 * and end, and +style+.
356 *
357 * See Psych::Handler#start_mapping
358 */
359static VALUE start_mapping(
360 VALUE self,
361 VALUE anchor,
362 VALUE tag,
363 VALUE implicit,
364 VALUE style
365 ) {
366 yaml_emitter_t * emitter;
367 yaml_event_t event;
368 rb_encoding *encoding;
369
370 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
371
372 encoding = rb_utf8_encoding();
373
374 if(!NIL_P(anchor)) {
375 Check_Type(anchor, T_STRING);
376 anchor = rb_str_export_to_enc(anchor, encoding);
377 }
378
379 if(!NIL_P(tag)) {
380 Check_Type(tag, T_STRING);
381 tag = rb_str_export_to_enc(tag, encoding);
382 }
383
384 yaml_mapping_start_event_initialize(
385 &event,
386 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
387 (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
388 implicit ? 1 : 0,
389 (yaml_mapping_style_t)NUM2INT(style)
390 );
391
392 emit(emitter, &event);
393
394 return self;
395}
396
397/* call-seq: emitter.end_mapping
398 *
399 * Emit the end of a mapping.
400 *
401 * See Psych::Handler#end_mapping
402 */
403static VALUE end_mapping(VALUE self)
404{
405 yaml_emitter_t * emitter;
406 yaml_event_t event;
407 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
408
409 yaml_mapping_end_event_initialize(&event);
410
411 emit(emitter, &event);
412
413 return self;
414}
415
416/* call-seq: emitter.alias(anchor)
417 *
418 * Emit an alias with +anchor+.
419 *
420 * See Psych::Handler#alias
421 */
422static VALUE alias(VALUE self, VALUE anchor)
423{
424 yaml_emitter_t * emitter;
425 yaml_event_t event;
426 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
427
428 if(!NIL_P(anchor)) {
429 Check_Type(anchor, T_STRING);
430 anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding());
431 }
432
433 yaml_alias_event_initialize(
434 &event,
435 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor))
436 );
437
438 emit(emitter, &event);
439
440 return self;
441}
442
443/* call-seq: emitter.canonical = true
444 *
445 * Set the output style to canonical, or not.
446 */
447static VALUE set_canonical(VALUE self, VALUE style)
448{
449 yaml_emitter_t * emitter;
450 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
451
452 yaml_emitter_set_canonical(emitter, Qtrue == style ? 1 : 0);
453
454 return style;
455}
456
457/* call-seq: emitter.canonical
458 *
459 * Get the output style, canonical or not.
460 */
461static VALUE canonical(VALUE self)
462{
463 yaml_emitter_t * emitter;
464 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
465
466 return (emitter->canonical == 0) ? Qfalse : Qtrue;
467}
468
469/* call-seq: emitter.indentation = level
470 *
471 * Set the indentation level to +level+. The level must be less than 10 and
472 * greater than 1.
473 */
474static VALUE set_indentation(VALUE self, VALUE level)
475{
476 yaml_emitter_t * emitter;
477 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
478
479 yaml_emitter_set_indent(emitter, NUM2INT(level));
480
481 return level;
482}
483
484/* call-seq: emitter.indentation
485 *
486 * Get the indentation level.
487 */
488static VALUE indentation(VALUE self)
489{
490 yaml_emitter_t * emitter;
491 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
492
493 return INT2NUM(emitter->best_indent);
494}
495
496/* call-seq: emitter.line_width
497 *
498 * Get the preferred line width.
499 */
500static VALUE line_width(VALUE self)
501{
502 yaml_emitter_t * emitter;
503 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
504
505 return INT2NUM(emitter->best_width);
506}
507
508/* call-seq: emitter.line_width = width
509 *
510 * Set the preferred line with to +width+.
511 */
512static VALUE set_line_width(VALUE self, VALUE width)
513{
514 yaml_emitter_t * emitter;
515 TypedData_Get_Struct(self, yaml_emitter_t, &psych_emitter_type, emitter);
516
517 yaml_emitter_set_width(emitter, NUM2INT(width));
518
519 return width;
520}
521
522 void Init_psych_emitter(void)
523{
524#undef rb_intern
525 VALUE psych = rb_define_module("Psych");
526 VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject);
527 cPsychEmitter = rb_define_class_under(psych, "Emitter", handler);
528
529 rb_define_alloc_func(cPsychEmitter, allocate);
530
531 rb_define_method(cPsychEmitter, "initialize", initialize, -1);
532 rb_define_method(cPsychEmitter, "start_stream", start_stream, 1);
533 rb_define_method(cPsychEmitter, "end_stream", end_stream, 0);
534 rb_define_method(cPsychEmitter, "start_document", start_document, 3);
535 rb_define_method(cPsychEmitter, "end_document", end_document, 1);
536 rb_define_method(cPsychEmitter, "scalar", scalar, 6);
537 rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4);
538 rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0);
539 rb_define_method(cPsychEmitter, "start_mapping", start_mapping, 4);
540 rb_define_method(cPsychEmitter, "end_mapping", end_mapping, 0);
541 rb_define_method(cPsychEmitter, "alias", alias, 1);
542 rb_define_method(cPsychEmitter, "canonical", canonical, 0);
543 rb_define_method(cPsychEmitter, "canonical=", set_canonical, 1);
544 rb_define_method(cPsychEmitter, "indentation", indentation, 0);
545 rb_define_method(cPsychEmitter, "indentation=", set_indentation, 1);
546 rb_define_method(cPsychEmitter, "line_width", line_width, 0);
547 rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1);
548
549 id_io = rb_intern("io");
550 id_write = rb_intern("write");
551 id_line_width = rb_intern("line_width");
552 id_indentation = rb_intern("indentation");
553 id_canonical = rb_intern("canonical");
554}
555/* vim: set noet sws=4 sw=4: */
rb_ary_entry
VALUE rb_ary_entry(VALUE ary, long offset)
Definition: array.c:1672
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
ptr
struct RIMemo * ptr
Definition: debug.c:88
rb_utf8_encoding
rb_encoding * rb_utf8_encoding(void)
Definition: encoding.c:1537
len
uint8_t len
Definition: escape.c:17
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
RSTRING_LEN
#define RSTRING_LEN(string)
Definition: fbuffer.h:22
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:797
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:871
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2296
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:1055
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:49
rb_enc_str_new
VALUE rb_enc_str_new(const char *, long, rb_encoding *)
Definition: string.c:857
rb_str_export_to_enc
VALUE rb_str_export_to_enc(VALUE, rb_encoding *)
Definition: string.c:1205
rb_funcall
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
rb_attr_get
VALUE rb_attr_get(VALUE, ID)
Definition: variable.c:1242
rb_ivar_set
VALUE rb_ivar_set(VALUE, ID, VALUE)
Definition: variable.c:1493
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_intern
ID rb_intern(const char *)
Definition: symbol.c:785
NUM2INT
#define NUM2INT
Definition: int.h:44
INT2NUM
#define INT2NUM
Definition: int.h:43
size
voidpf void uLong size
Definition: ioapi.h:138
name
const char * name
Definition: nkf.c:208
alias
const char * alias
Definition: nkf.c:1159
psych.h
Init_psych_emitter
void Init_psych_emitter(void)
Definition: psych_emitter.c:522
cPsychEmitter
VALUE cPsychEmitter
Definition: psych_emitter.c:10
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: psych_emitter.c:7
RARRAY_LEN
#define RARRAY_LEN
Definition: rarray.h:52
NULL
#define NULL
Definition: regenc.h:69
StringValue
#define StringValue(v)
Definition: rstring.h:50
StringValuePtr
#define StringValuePtr(v)
Definition: rstring.h:51
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
RUBY_TYPED_FREE_IMMEDIATELY
@ RUBY_TYPED_FREE_IMMEDIATELY
Definition: rtypeddata.h:62
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
argc
int argc
Definition: ruby.c:240
argv
char ** argv
Definition: ruby.c:241
Qtrue
#define Qtrue
Definition: special_consts.h:52
RTEST
#define RTEST
Definition: special_consts.h:42
Qfalse
#define Qfalse
Definition: special_consts.h:50
NIL_P
#define NIL_P
Definition: special_consts.h:46
OnigEncodingTypeST
Definition: onigmo.h:160
rb_data_type_struct
Definition: rtypeddata.h:70
VALUE
unsigned long VALUE
Definition: value.h:38
ID
unsigned long ID
Definition: value.h:39
T_STRING
#define T_STRING
Definition: value_type.h:77
T_FIXNUM
#define T_FIXNUM
Definition: value_type.h:62
T_ARRAY
#define T_ARRAY
Definition: value_type.h:55
xfree
#define xfree
Definition: xmalloc.h:49
xcalloc
#define xcalloc
Definition: xmalloc.h:46