yeptris 0.6.18.2-arm-linux-eabihf

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