yeptris 0.6.7.2-aarch64-linux → 0.6.7.4-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/lib/yeptris.rb +1 -1
  3. data/vendor/libyeptris/CMakeLists.txt +254 -0
  4. data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
  5. data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
  6. data/vendor/libyeptris/src/CMakeLists.txt +167 -0
  7. data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
  8. data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
  9. data/vendor/libyeptris/src/include/yeptris/dom.h +181 -0
  10. data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
  11. data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
  12. data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
  13. data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
  14. data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
  15. data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
  16. data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
  17. data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
  18. data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
  19. data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
  20. data/vendor/libyeptris/src/include/yeptris/schema.h +119 -0
  21. data/vendor/libyeptris/src/include/yeptris/tape.h +99 -0
  22. data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
  23. data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
  24. data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
  25. data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
  26. data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
  27. data/vendor/libyeptris/src/include/yeptris.h +23 -0
  28. data/vendor/libyeptris/src/yeptris/build.c +371 -0
  29. data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
  30. data/vendor/libyeptris/src/yeptris/cbor/decode.c +846 -0
  31. data/vendor/libyeptris/src/yeptris/cbor/encode.c +821 -0
  32. data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
  33. data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
  34. data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
  35. data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
  36. data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
  37. data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
  38. data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
  39. data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
  40. data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
  41. data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
  42. data/vendor/libyeptris/src/yeptris/common/simd_text.c +46 -0
  43. data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
  44. data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
  45. data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
  46. data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
  47. data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
  48. data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
  49. data/vendor/libyeptris/src/yeptris/doc.h +37 -0
  50. data/vendor/libyeptris/src/yeptris/dom/dom.c +973 -0
  51. data/vendor/libyeptris/src/yeptris/dom/dom.h +290 -0
  52. data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
  53. data/vendor/libyeptris/src/yeptris/dom/mapindex.c +166 -0
  54. data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
  55. data/vendor/libyeptris/src/yeptris/dom/mutate.c +429 -0
  56. data/vendor/libyeptris/src/yeptris/emit/emit.c +349 -0
  57. data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
  58. data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
  59. data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +196 -0
  60. data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
  61. data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
  62. data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
  63. data/vendor/libyeptris/src/yeptris/emit/writer.c +746 -0
  64. data/vendor/libyeptris/src/yeptris/emit/writer.h +61 -0
  65. data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
  66. data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
  67. data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
  68. data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
  69. data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
  70. data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
  71. data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
  72. data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
  73. data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
  74. data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
  75. data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
  76. data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
  77. data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
  78. data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
  79. data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
  80. data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
  81. data/vendor/libyeptris/src/yeptris/marshal.c +744 -0
  82. data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
  83. data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
  84. data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
  85. data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
  86. data/vendor/libyeptris/src/yeptris/memory/pool.c +115 -0
  87. data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
  88. data/vendor/libyeptris/src/yeptris/parse/engine.c +3689 -0
  89. data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
  90. data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
  91. data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
  92. data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
  93. data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
  94. data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
  95. data/vendor/libyeptris/src/yeptris/parse.c +650 -0
  96. data/vendor/libyeptris/src/yeptris/plan.c +817 -0
  97. data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
  98. data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
  99. data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
  100. data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
  101. data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
  102. data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
  103. data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
  104. data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
  105. data/vendor/libyeptris/src/yeptris/schema.c +238 -0
  106. data/vendor/libyeptris/src/yeptris/tape.c +1292 -0
  107. data/vendor/libyeptris/src/yeptris/version.c +7 -0
  108. data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
  109. data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
  110. data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
  111. metadata +111 -4
@@ -0,0 +1,746 @@
1
+ /* writer.c — the single writer engine (TODO.impl/13).
2
+ *
3
+ * One recursive emitter drives both sizing (dry: counts only) and
4
+ * output (wet): the size query and the bytes agree by construction.
5
+ *
6
+ * Layout contract (indent = the CONTENT column of the node):
7
+ * block map value: "key: <inline>" or "key:\n<indent+2>…"
8
+ * block seq item: "- <inline>" (compact: nested block collections
9
+ * start on the dash line at indent+2)
10
+ * flow collections re-emit compact: {k: v, k2: v2} / [a, b]
11
+ * scalars re-emit their recorded style; plain values that are not
12
+ * plain-safe in position fall back to double quotes; any multiline
13
+ * scalar emits as a literal block (deterministic upgrade: parse of
14
+ * the upgrade records literal, so byte stability holds)
15
+ * anchors: "&name " before the node; aliases: "*name"
16
+ * multi-document streams mark every document with "---"
17
+ */
18
+
19
+ #include "writer.h"
20
+
21
+ #include <string.h>
22
+
23
+ #include "style.h"
24
+
25
+ #include <stdio.h>
26
+ #include <stdlib.h>
27
+
28
+ #include "../../include/yeptris/resolve.h"
29
+ #include "float/api.h"
30
+ #include "resolve/resolver.h"
31
+
32
+ /* Decodes a compact node string for the writer's configured run. */
33
+ static yep_view wv(const yep_writer* w, yep_sview sv) {
34
+ yep_view v = {NULL, 0};
35
+ if (sv.len == 0) {
36
+ return v;
37
+ }
38
+ v.len = sv.len;
39
+ v.p = (sv.off & YEP_SV_INPUT ? w->sv_arena : w->sv_input) + (sv.off & YEP_SV_OFF);
40
+ return v;
41
+ }
42
+
43
+ /* Streaming flush (13C): append-only output means any high-water
44
+ * crossing is a safe cut point. */
45
+ static void wr_maybe_flush(yep_writer* w) {
46
+ if (w->sink == NULL || w->dry || w->len < w->watermark) {
47
+ return;
48
+ }
49
+ if (w->sink(w->sink_ctx, w->p, w->len) != 0) {
50
+ w->sink_aborted = 1;
51
+ return;
52
+ }
53
+ w->flushed += w->len;
54
+ w->len = 0;
55
+ }
56
+
57
+ static void wr_col_update(yep_writer* w, const char* p, uint32_t n) {
58
+ for (uint32_t i = n; i > 0; i--) { /* last newline wins */
59
+ if (p[i - 1] == '\n') {
60
+ w->col = (int)(n - i);
61
+ return;
62
+ }
63
+ }
64
+ w->col += (int)n;
65
+ }
66
+
67
+ static void wr_put(yep_writer* w, const char* p, uint32_t n) {
68
+ if (n > 0) {
69
+ w->last = p[n - 1];
70
+ }
71
+ wr_col_update(w, p, n);
72
+ if (w->dry) {
73
+ w->len += n;
74
+ return;
75
+ }
76
+ memcpy(w->p + w->len, p, n);
77
+ w->len += n;
78
+ wr_maybe_flush(w);
79
+ }
80
+
81
+ static void wr_byte(yep_writer* w, char c) {
82
+ w->last = c;
83
+ if (c == '\n') {
84
+ w->col = 0;
85
+ } else {
86
+ w->col++;
87
+ }
88
+ if (w->dry) {
89
+ w->len++;
90
+ return;
91
+ }
92
+ w->p[w->len] = c;
93
+ w->len++;
94
+ wr_maybe_flush(w);
95
+ }
96
+
97
+ static void wr_indent(yep_writer* w, int depth) {
98
+ for (int i = 0; i < depth; i++) {
99
+ wr_byte(w, ' ');
100
+ }
101
+ }
102
+
103
+ static void emit_dq(yep_writer* w, const char* p, uint32_t n) {
104
+ wr_byte(w, '"');
105
+ for (uint32_t i = 0; i < n; i++) {
106
+ unsigned char c = (unsigned char)p[i];
107
+ if (c == '"' || c == '\\') {
108
+ wr_byte(w, '\\');
109
+ wr_byte(w, (char)c);
110
+ } else if (c == '\n') {
111
+ wr_put(w, "\\n", 2);
112
+ } else if (c == '\r') {
113
+ wr_put(w, "\\r", 2);
114
+ } else if (c == '\t') {
115
+ wr_put(w, "\\t", 2);
116
+ } else if (c < 0x20 || c == 0x7f) {
117
+ static const char* named[6] = {"\\0", "\\a", "\\b", "\\v", "\\f", "\\e"};
118
+ static const unsigned char code[6] = {0x00, 0x07, 0x08, 0x0b, 0x0c, 0x1b};
119
+ int k;
120
+ for (k = 0; k < 6; k++) {
121
+ if (code[k] == c) {
122
+ break;
123
+ }
124
+ }
125
+ if (k < 6) {
126
+ wr_put(w, named[k], 2);
127
+ } else {
128
+ static const char hd[] = "0123456789abcdef";
129
+ char hex[4] = {'\\', 'x', hd[(c >> 4) & 0xf], hd[c & 0xf]};
130
+ wr_put(w, hex, 4);
131
+ }
132
+ } else {
133
+ wr_byte(w, (char)c);
134
+ }
135
+ }
136
+ wr_byte(w, '"');
137
+ }
138
+
139
+ static void emit_sq(yep_writer* w, const char* p, uint32_t n) {
140
+ wr_byte(w, '\'');
141
+ for (uint32_t i = 0; i < n; i++) {
142
+ if (p[i] == '\'') {
143
+ wr_put(w, "''", 2);
144
+ } else {
145
+ wr_byte(w, p[i]);
146
+ }
147
+ }
148
+ wr_byte(w, '\'');
149
+ }
150
+
151
+ /* JSON string escapes: \" \\ \b \f \n \r \t and \uXXXX for other
152
+ * controls; raw UTF-8 passes through (RFC 8259). */
153
+ static void emit_dq_json(yep_writer* w, const char* p, uint32_t n) {
154
+ wr_byte(w, '"');
155
+ static const char* named[32] = {
156
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\b", "\\t", "\\n",
157
+ NULL, "\\f", "\\r", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
158
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
159
+ };
160
+ for (uint32_t i = 0; i < n; i++) {
161
+ unsigned char c = (unsigned char)p[i];
162
+ if (c < 32 && named[c] != NULL) {
163
+ wr_put(w, named[c], 2);
164
+ } else if (c == '"' || c == '\\') {
165
+ wr_byte(w, '\\');
166
+ wr_byte(w, (char)c);
167
+ } else if (c < 0x20) {
168
+ static const char hd[] = "0123456789abcdef";
169
+ char hex[6] = {'\\', 'u', '0', '0', hd[(c >> 4) & 0xf], hd[c & 0xf]};
170
+ wr_put(w, hex, 6);
171
+ } else {
172
+ wr_byte(w, (char)c);
173
+ }
174
+ }
175
+ wr_byte(w, '"');
176
+ }
177
+
178
+ /* Literal block: header at the cursor, body lines at parent_col+2.
179
+ * The explicit indicator is always '2' because the pad is defined as
180
+ * parent_col+2 everywhere (the contract that keeps it valid). */
181
+ static void emit_literal(yep_writer* w, const yep_dnode* n, int parent_col) {
182
+ yep_view vdec = wv(w, n->value);
183
+ const char* p = (const char*)vdec.p;
184
+ uint32_t len = vdec.len;
185
+ uint32_t trail = 0;
186
+ while (trail < len && p[len - 1 - trail] == '\n') {
187
+ trail++;
188
+ }
189
+ uint32_t body = len - trail;
190
+ wr_byte(w, '|');
191
+ if (trail == 0) {
192
+ wr_byte(w, '-');
193
+ } else if (trail > 1) {
194
+ wr_byte(w, '+');
195
+ }
196
+ /* libyaml's indicator rules (#290 family 4): the explicit indent
197
+ * appears when the first body line starts with a space or is
198
+ * blank — the base indent is unambiguous otherwise (the pad is
199
+ * parent_col+2 by contract; F6MC's leading-blank folded bodies) */
200
+ if (body > 0 && (p[0] == ' ' || p[0] == '\n')) {
201
+ wr_byte(w, '2');
202
+ }
203
+ wr_byte(w, '\n');
204
+ int pad = parent_col + 2;
205
+ wr_indent(w, pad);
206
+ for (uint32_t i = 0; i < body; i++) {
207
+ wr_byte(w, p[i]);
208
+ if (p[i] == '\n' && i + 1 < body) {
209
+ if (p[i + 1] == '\n') {
210
+ continue; /* blank interior lines ride unpadded (libyaml) */
211
+ }
212
+ wr_indent(w, pad);
213
+ }
214
+ }
215
+ for (uint32_t i = 0; i < trail; i++) {
216
+ wr_byte(w, '\n');
217
+ }
218
+ }
219
+
220
+ /* Canonical scalar (13B): typed words and numbers plain, everything
221
+ * else double-quoted; floats re-print through the shortest printer so
222
+ * canonical output is a fixed point (parse of canonical re-prints
223
+ * identically — the byte-stability gate). */
224
+ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
225
+ yep_view vdec = wv(w, n->value);
226
+ const char* p = (const char*)vdec.p;
227
+ uint32_t len = vdec.len;
228
+ if (n->tag.len == 0) {
229
+ switch (n->tag_id) {
230
+ case YEPTRIS_TAG_NULL:
231
+ if (w->json) {
232
+ wr_put(w, "null", 4);
233
+ } else {
234
+ wr_put(w, "~", 1);
235
+ }
236
+ return;
237
+ case YEPTRIS_TAG_BOOL:
238
+ if (len == 4) { /* true/TRUE/True/null-ish variants */
239
+ wr_put(w, (p[0] == 't' || p[0] == 'T') ? "true" : "null", 4);
240
+ } else { /* false family (5 chars) */
241
+ wr_put(w, "false", 5);
242
+ }
243
+ return;
244
+ case YEPTRIS_TAG_FLOAT: {
245
+ const char* word = yep_tag_float_word(p, len);
246
+ if (word != NULL) {
247
+ wr_put(w, word, (uint32_t)strlen(word));
248
+ return;
249
+ }
250
+ char buf[40];
251
+ char* end = NULL;
252
+ char tmp[40];
253
+ uint32_t tl = len < sizeof(tmp) - 1 ? len : (uint32_t)sizeof(tmp) - 1;
254
+ memcpy(tmp, p, tl);
255
+ tmp[tl] = '\0';
256
+ double d = strtod(tmp, &end);
257
+ if (end != NULL && *end == '\0' && end != tmp) {
258
+ int wl = yep_d2s_shortest(d, buf);
259
+ wr_put(w, buf, (uint32_t)wl);
260
+ return;
261
+ }
262
+ break; /* unparseable view: fall through to quoting */
263
+ }
264
+ case YEPTRIS_TAG_INT:
265
+ wr_put(w, p, len);
266
+ return;
267
+ default:
268
+ break;
269
+ }
270
+ }
271
+ if (w->json) {
272
+ emit_dq_json(w, p, len);
273
+ } else {
274
+ emit_dq(w, p, len);
275
+ }
276
+ }
277
+
278
+ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int as_key) {
279
+ yep_view vdec = wv(w, n->value);
280
+ const char* p = (const char*)vdec.p;
281
+ uint32_t len = vdec.len;
282
+ if (w->canonical || w->json) {
283
+ emit_canonical_scalar(w, n);
284
+ return;
285
+ }
286
+ /* p may be NULL for an empty view; memchr(NULL, .., 0) is UB by
287
+ * the nonnull attribute even though it reads nothing */
288
+ int multiline = (len > 0 && memchr(p, '\n', len) != NULL);
289
+ int blockable = 0;
290
+ if (multiline && !as_key) {
291
+ /* A block whose body is all whitespace parses back empty, and a
292
+ * raw CR re-parses as a break — both emit double-quoted. */
293
+ int allws = 1;
294
+ int has_cr = 0;
295
+ for (uint32_t i = 0; i < len; i++) {
296
+ if (p[i] != ' ' && p[i] != '\t' && p[i] != '\n' && p[i] != '\r') {
297
+ allws = 0;
298
+ }
299
+ if (p[i] == '\r') {
300
+ has_cr = 1;
301
+ }
302
+ }
303
+ blockable = !allws && !has_cr;
304
+ }
305
+ int sty = n->style;
306
+ if (sty == 4 || sty == 5) {
307
+ if (multiline && as_key) {
308
+ emit_dq(w, p, len); /* keys stay inline: no block scalars */
309
+ return;
310
+ }
311
+ if (multiline && blockable) {
312
+ emit_literal(w, n, parent_col);
313
+ return;
314
+ }
315
+ if (multiline) {
316
+ emit_dq(w, p, len);
317
+ return;
318
+ }
319
+ if (len == 0) {
320
+ emit_dq(w, p, len); /* an explicitly-empty block scalar is
321
+ the empty STRING — a bare empty would
322
+ re-read as null (2G84#3, K858) */
323
+ return;
324
+ }
325
+ sty = 1; /* a chomp-stripped block is plain bytes: re-emit plain */
326
+ }
327
+ switch (sty) {
328
+ case 1: /* plain */
329
+ if (len == 0) {
330
+ if (as_key) {
331
+ emit_dq(w, p, len); /* empty keys must stay visible */
332
+ }
333
+ break; /* an empty plain value emits as nothing (libyaml's
334
+ null rendering, issue #290) */
335
+ }
336
+ if ((as_key ? yep_style_plain_key_safe(p, len) : yep_style_plain_safe(p, len))) {
337
+ wr_put(w, p, len);
338
+ } else if (!multiline) {
339
+ /* libyaml parity: a scalar that must be quoted but needs no
340
+ * escapes (digit-leading strings like "5014", indicator
341
+ * leads) single-quotes — double quotes are for text that
342
+ * actually requires escapes (issue #95's matrix) */
343
+ int clean = 1;
344
+ for (uint32_t i = 0; i < len; i++) {
345
+ if ((unsigned char)p[i] < 0x20 || (unsigned char)p[i] == 0x7f) {
346
+ clean = 0;
347
+ break;
348
+ }
349
+ }
350
+ if (clean) {
351
+ emit_sq(w, p, len);
352
+ } else {
353
+ emit_dq(w, p, len);
354
+ }
355
+ } else {
356
+ emit_dq(w, p, len);
357
+ }
358
+ break;
359
+ case 2: /* single */
360
+ if (!multiline) {
361
+ emit_sq(w, p, len);
362
+ } else {
363
+ emit_dq(w, p, len);
364
+ }
365
+ break;
366
+ default: /* double, literal-as-key, folded-as-key, any */
367
+ emit_dq(w, p, len);
368
+ break;
369
+ }
370
+ }
371
+
372
+ /* Canonical mode renames anchors to generated names (a0, a1, ...):
373
+ * exotic source names (flow indicators inside them) cannot survive
374
+ * flow context raw, and renaming preserves the binding semantics.
375
+ * Deterministic by definition order -> byte-stable across roundtrips.
376
+ * The emitter's nametab lives in yep_emitter (owner: yep_emit_run). */
377
+ static void emit_anchor_ex(yep_emitter* em, const yep_dnode* n) {
378
+ yep_writer* w = &em->w;
379
+ if (n->anchor.len == 0) {
380
+ return;
381
+ }
382
+ wr_byte(w, '&');
383
+ yep_view adec = wv(w, n->anchor);
384
+ if (w->canonical) {
385
+ uint32_t idx = yep_nametab_get(&em->canon_names, adec);
386
+ if (idx == YEP_NAMETAB_NIL) {
387
+ idx = em->canon_names.count;
388
+ yep_nametab_set(&em->canon_names, adec, idx);
389
+ }
390
+ char name[24];
391
+ int nl = snprintf(name, sizeof(name), "a%u", idx);
392
+ wr_put(w, name, (uint32_t)nl);
393
+ } else {
394
+ wr_put(w, (const char*)adec.p, adec.len);
395
+ }
396
+ wr_byte(w, ' ');
397
+ }
398
+
399
+ static void emit_alias_ex(yep_emitter* em, const yep_dnode* n) {
400
+ yep_writer* w = &em->w;
401
+ yep_view name_v = wv(w, n->value);
402
+ wr_byte(w, '*');
403
+ if (w->canonical) {
404
+ uint32_t idx = yep_nametab_get(&em->canon_names, name_v);
405
+ char name[24];
406
+ int nl;
407
+ if (idx == YEP_NAMETAB_NIL) {
408
+ nl = snprintf(name, sizeof(name), "%.*s", (int)name_v.len, (const char*)name_v.p);
409
+ } else {
410
+ nl = snprintf(name, sizeof(name), "a%u", idx);
411
+ }
412
+ wr_put(w, name, (uint32_t)nl);
413
+ } else {
414
+ wr_put(w, (const char*)name_v.p, name_v.len);
415
+ }
416
+ }
417
+
418
+ /* Explicit tag, verbatim URI form ("!<tag:…> "). Never skipped: a
419
+ * recorded tag always re-emits (implied-by-style skipping made the
420
+ * roundtrip byte-unstable: plain+!!str and ""+!!str collided). */
421
+ static void emit_tag(yep_writer* w, const yep_dnode* n) {
422
+ if (n->tag.len == 0) {
423
+ return;
424
+ }
425
+ yep_view tdec = wv(w, n->tag);
426
+ if (tdec.len > 0 && tdec.p[0] == '!') {
427
+ /* already a shorthand ("!ruby/object:K", "!foo"): verbatim —
428
+ * the !<...> wrapper is for verbatim URIs only */
429
+ wr_put(w, (const char*)tdec.p, tdec.len);
430
+ wr_byte(w, ' ');
431
+ return;
432
+ }
433
+ wr_byte(w, '!');
434
+ wr_byte(w, '<');
435
+ wr_put(w, (const char*)tdec.p, tdec.len);
436
+ wr_byte(w, '>');
437
+ wr_byte(w, ' ');
438
+ }
439
+
440
+ /* Line-tail properties for block collections: "!<t> &a" after
441
+ * "key:"/"-" — properties may not precede a dash entry directly (the
442
+ * suite's SY6V rule). */
443
+ static void emit_props_tail(yep_writer* w, const yep_dnode* n) {
444
+ if (n->tag.len > 0) {
445
+ wr_byte(w, ' ');
446
+ yep_view tdec2 = wv(w, n->tag);
447
+ wr_byte(w, '!');
448
+ wr_byte(w, '<');
449
+ wr_put(w, (const char*)tdec2.p, tdec2.len);
450
+ wr_byte(w, '>');
451
+ }
452
+ if (n->anchor.len > 0) {
453
+ wr_byte(w, ' ');
454
+ yep_view adec2 = wv(w, n->anchor);
455
+ wr_byte(w, '&');
456
+ wr_put(w, (const char*)adec2.p, adec2.len);
457
+ }
458
+ }
459
+
460
+ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key);
461
+
462
+ static void emit_flow(yep_emitter* em, uint32_t id) {
463
+ yep_writer* w = &em->w;
464
+ const yep_dom* d = em->doc->dom;
465
+ const yep_dnode* n = yep_dom_node(d, id);
466
+ emit_tag(w, n);
467
+ emit_anchor_ex(em, n);
468
+ int map = (n->kind == 2);
469
+ wr_byte(w, map ? '{' : '[');
470
+ int flow_indent = w->col;
471
+ if (w->json_pretty && n->count > 0) {
472
+ w->pretty_depth++;
473
+ flow_indent = w->pretty_depth * 2; /* 2 spaces per level */
474
+ wr_byte(w, '\n');
475
+ wr_indent(w, flow_indent);
476
+ }
477
+ uint32_t child = n->first_child;
478
+ uint32_t idx = 0;
479
+ while (child != UINT32_MAX) {
480
+ const yep_dnode* cn = yep_dom_node(d, child);
481
+ if (idx > 0) {
482
+ /* 13B: past best_width, break AFTER the previous value
483
+ * (value boundaries only, never inside a scalar) and
484
+ * re-indent to the opening bracket's column */
485
+ if (w->json_pretty) {
486
+ wr_put(w, ",\n", 2);
487
+ wr_indent(w, w->pretty_depth * 2);
488
+ } else if (w->json_compact) {
489
+ wr_byte(w, ',');
490
+ } else if (w->best_width > 0 && w->col > w->best_width) {
491
+ wr_byte(w, ',');
492
+ wr_byte(w, '\n');
493
+ wr_indent(w, flow_indent);
494
+ } else {
495
+ wr_put(w, ", ", 2);
496
+ }
497
+ }
498
+ if (map) {
499
+ int explicit_key = cn->anchor.len > 0 || cn->tag.len > 0 || cn->kind != 0;
500
+ if (explicit_key) {
501
+ wr_put(w, "? ", 2);
502
+ emit_node(em, child, 0, 1);
503
+ wr_put(w, " : ", 3);
504
+ } else {
505
+ emit_node(em, child, 0, 1);
506
+ if (w->json_pretty) {
507
+ wr_put(w, ": ", 2);
508
+ } else if (w->json_compact) {
509
+ wr_byte(w, ':');
510
+ } else {
511
+ wr_put(w, ": ", 2);
512
+ }
513
+ }
514
+ emit_node(em, cn->next_sibling, 0, 0);
515
+ child = cn->next_sibling;
516
+ cn = yep_dom_node(d, child);
517
+ } else {
518
+ emit_node(em, child, 0, 0);
519
+ }
520
+ idx++;
521
+ child = cn->next_sibling;
522
+ }
523
+ if (w->json_pretty && idx > 0) {
524
+ wr_byte(w, '\n');
525
+ wr_indent(w, (w->pretty_depth - 1) * 2);
526
+ w->pretty_depth--;
527
+ }
528
+ wr_byte(w, map ? '}' : ']');
529
+ }
530
+
531
+ /* Block map: first key at the cursor (the caller placed it at
532
+ * content_col = parent_col+2); following keys on new lines there. */
533
+ static void emit_block_map(yep_emitter* em, uint32_t id, int content_col) {
534
+ yep_writer* w = &em->w;
535
+ const yep_dom* d = em->doc->dom;
536
+ const yep_dnode* n = yep_dom_node(d, id);
537
+ uint32_t child = n->first_child;
538
+ uint32_t pair = 0;
539
+ while (child != UINT32_MAX) {
540
+ const yep_dnode* kn = yep_dom_node(d, child);
541
+ const yep_dnode* vn = yep_dom_node(d, kn->next_sibling);
542
+ if (pair > 0) {
543
+ if (w->last != '\n') {
544
+ wr_byte(w, '\n'); /* keep-chomped bodies own their break */
545
+ }
546
+ wr_indent(w, content_col);
547
+ }
548
+ int complex_key = (kn->kind != 0);
549
+ if (complex_key) {
550
+ w->force_flow = 1; /* collections as flow keys: compact and
551
+ free of the block '?' layout edges */
552
+ wr_put(w, "? ", 2);
553
+ emit_props_tail(w, kn); /* the key's props ride "? " */
554
+ if (kn->anchor.len > 0 || kn->tag.len > 0) {
555
+ wr_byte(w, '\n');
556
+ wr_indent(w, content_col + 2);
557
+ }
558
+ emit_node(em, child, content_col, 1);
559
+ wr_byte(w, '\n');
560
+ wr_indent(w, content_col);
561
+ wr_put(w, ": ", 2);
562
+ w->force_flow = 0;
563
+ emit_node(em, kn->next_sibling, content_col, 0);
564
+ } else {
565
+ emit_tag(w, kn);
566
+ emit_anchor_ex(em, kn);
567
+ emit_scalar(w, kn, content_col, 1);
568
+ wr_byte(w, ':');
569
+ /* empty collections ride the key line flow (k: [] / k: {})
570
+ * like libyaml — a block-empty has no valid next-line form at
571
+ * the seq indent (an empty [] at the key's column is not a
572
+ * legal value; pyyaml rejects it, issue #52's differential).
573
+ * An implicit-empty plain value rides bare (k:) with no
574
+ * trailing space — libyaml's null rendering (#290) */
575
+ if (vn->kind == 0 || vn->kind == 3 || vn->flow || vn->count == 0) {
576
+ int empty_plain = vn->kind == 0 && vn->style == 1 && vn->anchor.len == 0 &&
577
+ vn->tag.len == 0 && wv(w, vn->value).len == 0 && !w->canonical &&
578
+ !w->json;
579
+ if (!empty_plain) {
580
+ wr_byte(w, ' ');
581
+ }
582
+ emit_node(em, kn->next_sibling, content_col, 0);
583
+ } else {
584
+ /* nested block collection: next line. libyaml parity:
585
+ * a SEQUENCE value sits at the key's column (zero
586
+ * extra indent); mappings indent +2 (issue #95) */
587
+ int seq_val = (vn->kind == 1 && !vn->flow);
588
+ int val_col = seq_val ? content_col : content_col + 2;
589
+ emit_props_tail(w, vn);
590
+ wr_byte(w, '\n');
591
+ wr_indent(w, val_col);
592
+ /* emit_block_seq dashes at parent_col+2: pass val_col-2
593
+ * so the dashes land at val_col exactly */
594
+ emit_node(em, kn->next_sibling, val_col - 2, 0);
595
+ }
596
+ }
597
+ pair++;
598
+ child = vn->next_sibling;
599
+ }
600
+ }
601
+
602
+ /* Block seq: "- item" lines at content_col = parent_col+2. */
603
+ static void emit_block_seq(yep_emitter* em, uint32_t id, int content_col) {
604
+ yep_writer* w = &em->w;
605
+ const yep_dom* d = em->doc->dom;
606
+ const yep_dnode* n = yep_dom_node(d, id);
607
+ uint32_t child = n->first_child;
608
+ uint32_t idx = 0;
609
+ while (child != UINT32_MAX) {
610
+ const yep_dnode* cn = yep_dom_node(d, child);
611
+ if (idx > 0) {
612
+ if (w->last != '\n') {
613
+ wr_byte(w, '\n'); /* keep-chomped bodies own their break */
614
+ }
615
+ wr_indent(w, content_col);
616
+ }
617
+ if (cn->kind == 0 && cn->style == 1 && cn->anchor.len == 0 && cn->tag.len == 0 &&
618
+ wv(w, cn->value).len == 0 && !w->canonical && !w->json) {
619
+ wr_put(w, "-\n", 2); /* libyaml: a null item is a bare dash */
620
+ idx++;
621
+ child = cn->next_sibling;
622
+ continue;
623
+ }
624
+ wr_put(w, "- ", 2);
625
+ if (cn->kind != 0 && cn->kind != 3 && !cn->flow &&
626
+ (cn->anchor.len > 0 || cn->tag.len > 0)) {
627
+ /* props may not precede a dash: they ride the dash line and
628
+ * the collection starts on the next line (SY6V) */
629
+ emit_props_tail(w, cn);
630
+ wr_byte(w, '\n');
631
+ wr_indent(w, content_col + 2);
632
+ emit_node(em, child, content_col, 0);
633
+ } else {
634
+ /* compact: a block collection starts right after "- " */
635
+ emit_node(em, child, content_col, 0);
636
+ }
637
+ idx++;
638
+ child = cn->next_sibling;
639
+ }
640
+ }
641
+
642
+ static void emit_node(yep_emitter* em, uint32_t id, int parent_col, int as_key) {
643
+ yep_writer* w = &em->w;
644
+ const yep_dom* d = em->doc->dom;
645
+ const yep_dnode* n = yep_dom_node(d, id);
646
+ if (n == NULL) {
647
+ return;
648
+ }
649
+ if (n->kind == 3) { /* alias */
650
+ emit_alias_ex(em, n);
651
+ return;
652
+ }
653
+ if ((n->kind == 1 || n->kind == 2) && (n->flow || w->canonical || w->json || w->force_flow)) {
654
+ /* force_flow holds through the subtree: a complex key's nested
655
+ * collections all render flow (scalars are never collections) */
656
+ emit_flow(em, id);
657
+ return;
658
+ }
659
+ if (n->kind == 2) { /* mapping */
660
+ if (n->count == 0) {
661
+ emit_tag(w, n);
662
+ emit_anchor_ex(em, n);
663
+ wr_put(w, "{}", 2);
664
+ } else {
665
+ emit_block_map(em, id, parent_col + 2);
666
+ }
667
+ return;
668
+ }
669
+ if (n->kind == 1) { /* sequence */
670
+ if (n->count == 0) {
671
+ emit_tag(w, n);
672
+ emit_anchor_ex(em, n);
673
+ wr_put(w, "[]", 2);
674
+ } else {
675
+ emit_block_seq(em, id, parent_col + 2);
676
+ }
677
+ return;
678
+ }
679
+ emit_tag(w, n);
680
+ emit_anchor_ex(em, n);
681
+ if (parent_col < 0) {
682
+ parent_col = 0; /* root scalar: block body at column 2 */
683
+ }
684
+ emit_scalar(w, n, parent_col, as_key);
685
+ }
686
+
687
+ size_t yep_emit_run(yep_emitter* em, int dry) {
688
+ yep_writer* w = &em->w;
689
+ const yep_dom* d = em->doc->dom;
690
+ w->dry = dry;
691
+ w->len = 0;
692
+ w->last = 0;
693
+ w->force_flow = 0;
694
+ w->flushed = 0;
695
+ w->sink_aborted = 0;
696
+ w->col = 0;
697
+ yep_nametab_clear(&em->canon_names);
698
+ if (w->json) {
699
+ /* JSON has no multi-document streams and no document marker */
700
+ if (d->dcount == 1) {
701
+ const yep_dnode* root = yep_dom_node(d, d->docs[0]);
702
+ emit_node(em, d->docs[0], -2, 0);
703
+ if (root != NULL && root->kind != 0 && w->last != '\n') {
704
+ wr_byte(w, '\n');
705
+ }
706
+ }
707
+ return w->flushed + w->len;
708
+ }
709
+ int multi = (d->dcount > 1) || w->canonical;
710
+ for (uint32_t i = 0; i < d->dcount; i++) {
711
+ const yep_dnode* root = yep_dom_node(d, d->docs[i]);
712
+ if (multi || w->explicit_doc_start) {
713
+ /* libyaml parity: a scalar root rides the marker line
714
+ * ("--- 42"); collections take the marker alone */
715
+ wr_put(w,
716
+ (w->explicit_doc_start && root != NULL && root->kind == YEP_DOM_SCALAR &&
717
+ !root->flow)
718
+ ? "--- "
719
+ : "---\n",
720
+ 4);
721
+ }
722
+ if (root != NULL && (root->kind == 1 || root->kind == 2) && !root->flow && !w->canonical &&
723
+ (root->tag.len > 0 || root->anchor.len > 0)) {
724
+ /* root block-collection props ride their own leading line;
725
+ * scalars and flow collections carry them inline */
726
+ if (root->tag.len > 0) {
727
+ yep_view tdec3 = wv(w, root->tag);
728
+ wr_byte(w, '!');
729
+ wr_byte(w, '<');
730
+ wr_put(w, (const char*)tdec3.p, tdec3.len);
731
+ wr_byte(w, '>');
732
+ }
733
+ if (root->anchor.len > 0) {
734
+ yep_view adec3 = wv(w, root->anchor);
735
+ wr_byte(w, '&');
736
+ wr_put(w, (const char*)adec3.p, adec3.len);
737
+ }
738
+ wr_byte(w, '\n');
739
+ }
740
+ emit_node(em, d->docs[i], -2, 0); /* root content at column 0 */
741
+ if (w->last != '\n') {
742
+ wr_byte(w, '\n');
743
+ }
744
+ }
745
+ return w->flushed + w->len;
746
+ }