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,782 @@
1
+ /* marshal.c — Ruby Marshal 4.8 emission (TODO.restructure/21).
2
+ *
3
+ * Two passes over the value records (the emitter discipline): sizing
4
+ * computes the exact buffer, then emission is linear writes — both
5
+ * runs execute the SAME walker with the writer no-op'd, so they
6
+ * cannot drift. Per-open element counts come from one linear
7
+ * pre-pass; registration ids are assigned deterministically in
8
+ * record order.
9
+ *
10
+ * Format facts are empirical, pinned against Ruby 3.4 Marshal.dump /
11
+ * Marshal.load byte streams:
12
+ * - w_long small form: x==0 -> 0x00; |x| <= 122 -> sign*(|x|+5);
13
+ * size form: tag +-k (k = minimal magnitude bytes), payload =
14
+ * LE(m) for positives, LE(2^(8k)-m) for negatives.
15
+ * - integer 'i' while |x| < 2^30 (the dumper's boundary; the loader
16
+ * accepts wider, but byte-matching the dumper keeps streams
17
+ * diffable). Beyond: bignum 'l' + sign + w_long(limbs) + LE
18
+ * 16-bit limbs.
19
+ * - float 'f' + w_long(repr_len) + repr; inf/nan spell out.
20
+ * - UTF-8 string: 'I' '"' w_long(len) bytes 0x06 ':' 0x06 'E' 'T'.
21
+ * - object links '@' + w_long(index): the loader registers arrays,
22
+ * hashes, strings and FLOATS (0-based, stream order); integers,
23
+ * bignums, symbols and immediates never register.
24
+ *
25
+ * Semantics mirror the binding record walks exactly (the ':sym' plain
26
+ * scan, single-char y/n, dot-required floats); aliases to registered
27
+ * constructs become object links (identity preserved), aliases to
28
+ * immediates re-emit the value. Merge keys ('<<') and timestamps
29
+ * return YEPTRIS_ERROR_UNSUPPORTED — the host falls back to the walk.
30
+ */
31
+
32
+ #include <math.h>
33
+ #include <stdlib.h>
34
+ #include <string.h>
35
+
36
+ #include "common/error.h"
37
+ #include "doc.h"
38
+ #include "emit/float/api.h"
39
+ #include "events/values_priv.h"
40
+
41
+ #include <yeptris/error.h>
42
+ #include <yeptris/marshal.h>
43
+ #include <yeptris/resolve.h> /* YEPTRIS_TAG_BINARY (#168) */
44
+
45
+ typedef struct {
46
+ const char* name; /* into the value arena */
47
+ uint32_t len;
48
+ int has_obj; /* 1: registered construct -> obj_id */
49
+ uint64_t obj_id;
50
+ int is_scalar; /* copy for immediate re-emits */
51
+ YeptrisValue scalar;
52
+ } yep_anchor;
53
+
54
+ typedef struct {
55
+ const yep_value_ctx* c;
56
+ int compat; /* Psych float quirks apply ONLY under 1.1 compat
57
+ * (TODO.restructure/32): the core schema's float
58
+ * regexp has an OPTIONAL dot - 1e3 IS a core float */
59
+ char* buf;
60
+ size_t len;
61
+ size_t nreg;
62
+ int sizing; /* 1: count only (writer no-op) */
63
+ int oom;
64
+ int unsupported;
65
+ uint64_t last_reg; /* id of the most recently completed value's
66
+ * registration, if any */
67
+ int last_was_reg;
68
+ /* anchors: rebuilt identically each pass (deterministic order) */
69
+ yep_anchor* anchors;
70
+ size_t nanchors, anchors_cap;
71
+ /* per-open element counts (document order of OPEN records) */
72
+ const uint64_t* counts;
73
+ size_t open_seq;
74
+ } E;
75
+
76
+ static void e_put(E* e, const void* p, size_t n) {
77
+ if (!e->sizing && n != 0) {
78
+ memcpy(e->buf + e->len, p, n);
79
+ }
80
+ e->len += n;
81
+ }
82
+
83
+ static void e_byte(E* e, unsigned char b) {
84
+ e_put(e, &b, 1);
85
+ }
86
+
87
+ static void e_long(E* e, int64_t x) {
88
+ if (x == 0) {
89
+ e_byte(e, 0);
90
+ return;
91
+ }
92
+ int64_t m = x < 0 ? -x : x;
93
+ if (m <= 122) {
94
+ e_byte(e, (unsigned char)(x < 0 ? -(m + 5) : m + 5));
95
+ return;
96
+ }
97
+ /* width types: unsigned long is 32-bit on Windows (LLP64) and
98
+ truncates every magnitude >= 2^32 (limb-count bug: a 19-digit
99
+ int materialized as its low 32 bits). */
100
+ uint64_t um = (uint64_t)m;
101
+ int k = 0;
102
+ while (um != 0) {
103
+ um >>= 8;
104
+ k++;
105
+ }
106
+ e_byte(e, (unsigned char)(x < 0 ? -k : k));
107
+ uint64_t payload =
108
+ x < 0 ? ((k == 8 ? UINT64_C(0) : UINT64_C(1) << (8 * k)) - (uint64_t)m) : (uint64_t)m;
109
+ unsigned char b[8];
110
+ for (int i = 0; i < k; i++) {
111
+ b[i] = (unsigned char)(payload & 0xff);
112
+ payload >>= 8;
113
+ }
114
+ e_put(e, b, (size_t)k);
115
+ }
116
+
117
+ static void e_reg(E* e, uint64_t* id_out) {
118
+ uint64_t id = e->nreg++;
119
+ if (id_out != NULL) {
120
+ *id_out = id;
121
+ }
122
+ }
123
+
124
+ static void e_int(E* e, int64_t x) {
125
+ e->last_was_reg = 0;
126
+ if (x == INT64_MIN) { /* |x| overflows int64 arithmetic */
127
+ e_byte(e, 'l');
128
+ e_byte(e, '-');
129
+ e_long(e, 4); /* 2^63: 64 bits -> 4 16-bit limbs */
130
+ static const unsigned char limbs[8] = {0, 0, 0, 0, 0, 0, 0, 0x80};
131
+ e_put(e, limbs, 8);
132
+ return;
133
+ }
134
+ int64_t m = x < 0 ? -x : x;
135
+ if (m < (int64_t)1 << 30) {
136
+ e_byte(e, 'i');
137
+ e_long(e, x);
138
+ return;
139
+ }
140
+ /* bignum: sign + limb count + LE 16-bit limbs */
141
+ e_byte(e, 'l');
142
+ e_byte(e, x < 0 ? '-' : '+');
143
+ uint64_t um = (uint64_t)m;
144
+ int bits = 0;
145
+ while (um != 0) {
146
+ um >>= 1;
147
+ bits++;
148
+ }
149
+ int limbs = (bits + 15) / 16;
150
+ e_long(e, limbs);
151
+ unsigned char b[8] = {0};
152
+ uint64_t v = (uint64_t)m;
153
+ for (int i = 0; i < limbs * 2; i += 2) {
154
+ b[i] = (unsigned char)(v & 0xff);
155
+ b[i + 1] = (unsigned char)((v >> 8) & 0xff);
156
+ v >>= 16;
157
+ }
158
+ e_put(e, b, (size_t)(limbs * 2));
159
+ }
160
+
161
+ static void e_str(E* e, const char* p, uint32_t len) {
162
+ uint64_t id;
163
+ e_reg(e, &id); /* strings register at their '"' token */
164
+ e_byte(e, 'I');
165
+ e_byte(e, '"');
166
+ e_long(e, (int64_t)len);
167
+ e_put(e, p, len);
168
+ e_byte(e, 0x06); /* 1 ivar */
169
+ e_byte(e, ':');
170
+ e_byte(e, 0x06); /* :E — 1-byte symbol */
171
+ e_byte(e, 'E');
172
+ e_byte(e, 'T'); /* true: UTF-8 */
173
+ e->last_reg = id;
174
+ e->last_was_reg = 1;
175
+ }
176
+
177
+ static void e_sym(E* e, const char* p, uint32_t len) {
178
+ e->last_was_reg = 0;
179
+ e_byte(e, ':');
180
+ e_long(e, (int64_t)len);
181
+ e_put(e, p, len);
182
+ }
183
+
184
+ static void e_float(E* e, double d) {
185
+ uint64_t id;
186
+ e_reg(e, &id); /* the loader registers floats (dump links them) */
187
+ char repr[40];
188
+ const char* s = repr;
189
+ uint32_t rl;
190
+ if (isnan(d)) {
191
+ s = "nan";
192
+ rl = 3;
193
+ } else if (isinf(d)) {
194
+ s = d < 0 ? "-inf" : "inf";
195
+ rl = d < 0 ? 4 : 3;
196
+ } else {
197
+ int n = yep_d2s_shortest(d, repr);
198
+ repr[n] = '\0';
199
+ rl = (uint32_t)n;
200
+ }
201
+ e_byte(e, 'f');
202
+ e_long(e, (int64_t)rl);
203
+ e_put(e, s, rl);
204
+ e->last_reg = id;
205
+ e->last_was_reg = 1;
206
+ }
207
+
208
+ static void e_null(E* e) {
209
+ e->last_was_reg = 0;
210
+ e_byte(e, '0');
211
+ }
212
+
213
+ static void e_bool(E* e, int truthy) {
214
+ e->last_was_reg = 0;
215
+ e_byte(e, truthy ? 'T' : 'F');
216
+ }
217
+
218
+ /* Psych's plain-scalar symbol scan: implicit ':name' (not '::'). */
219
+ static int sym_scan(const char* p, uint32_t len) {
220
+ return len > 1 && p[0] == ':' && p[1] != ':';
221
+ }
222
+
223
+ /* The float dot-quirk, exactly as the walks decide it: without '.',
224
+ * ':' or a leading '.', Psych leaves the scalar a String. */
225
+ static int float_text_ok(const E* e, const char* p, uint32_t len) {
226
+ if (!e->compat) {
227
+ return 1; /* core_12: the resolver's verdict is the spec's */
228
+ }
229
+ if (len == 0 || p[0] == '.') {
230
+ return len != 0;
231
+ }
232
+ return memchr(p, '.', len) != NULL || memchr(p, ':', len) != NULL;
233
+ }
234
+
235
+ static const char* e_text(const E* e, const YeptrisValue* v) {
236
+ return v->len == 0 ? "" : e->c->arena + v->off;
237
+ }
238
+
239
+ static yep_anchor* anchor_find(E* e, const char* name, uint32_t len) {
240
+ /* newest binding wins: YAML lets a later &anchor shadow an
241
+ * earlier one of the same name (libyaml snapshot 3GZX) */
242
+ for (size_t i = e->nanchors; i > 0; i--) {
243
+ yep_anchor* a = &e->anchors[i - 1];
244
+ if (a->len == len && memcmp(a->name, name, len) == 0) {
245
+ return a;
246
+ }
247
+ }
248
+ return NULL;
249
+ }
250
+
251
+ static int anchor_add(E* e, const char* name, uint32_t len) {
252
+ if (e->nanchors == e->anchors_cap) {
253
+ size_t cap = e->anchors_cap ? e->anchors_cap * 2 : 16;
254
+ yep_anchor* na = realloc(e->anchors, cap * sizeof(*na));
255
+ if (na == NULL) {
256
+ return -1;
257
+ }
258
+ e->anchors = na;
259
+ e->anchors_cap = cap;
260
+ }
261
+ yep_anchor* a = &e->anchors[e->nanchors++];
262
+ memset(a, 0, sizeof(*a));
263
+ a->name = name;
264
+ a->len = len;
265
+ return 0;
266
+ }
267
+
268
+ /* Psych's integer shape (scalar_scanner): optional sign, no
269
+ * leading zero, digits with '_' / ',' separators only BETWEEN
270
+ * digits. Ints within int64 arrive as V_INT; this rebuilds the
271
+ * BEYOND-int64 texts Psych materializes as Integer (issue #31,
272
+ * TODO.restructure/43). */
273
+ static int psych_int_text(const char* p, uint32_t len) {
274
+ if (len == 0)
275
+ return 0;
276
+ if (p[0] == '-' || p[0] == '+') {
277
+ p++;
278
+ len--;
279
+ }
280
+ if (len == 0 || p[0] == '0')
281
+ return 0;
282
+ for (uint32_t i = 0; i < len; i++) {
283
+ char c = p[i];
284
+ if (c >= '0' && c <= '9')
285
+ continue;
286
+ if ((c == '_' || c == ',') && i + 1 < len && p[i + 1] >= '0' && p[i + 1] <= '9')
287
+ continue;
288
+ return 0;
289
+ }
290
+ return 1;
291
+ }
292
+
293
+ /* decimal text -> bignum limbs (base 2^16 LE), then Marshal's 'l':
294
+ * sign + w_long(limb count) + limbs. 128 limbs = 2048 bits covers
295
+ * ~616 decimal digits; longer texts stay Strings (pathological). */
296
+ static void e_bignum_text(E* e, const char* p, uint32_t len) {
297
+ int neg = 0;
298
+ if (p[0] == '-' || p[0] == '+') {
299
+ neg = p[0] == '-';
300
+ p++;
301
+ len--;
302
+ }
303
+ uint16_t limbs[128];
304
+ memset(limbs, 0, sizeof(limbs));
305
+ int nl = 1;
306
+ for (uint32_t i = 0; i < len; i++) {
307
+ char c = p[i];
308
+ if (c == '_' || c == ',')
309
+ continue;
310
+ uint32_t carry = (uint32_t)(c - '0');
311
+ for (int j = 0; j < nl; j++) {
312
+ uint32_t t = (uint32_t)limbs[j] * 10u + carry;
313
+ limbs[j] = (uint16_t)(t & 0xffffu);
314
+ carry = t >> 16;
315
+ }
316
+ while (carry != 0 && nl < 128) {
317
+ limbs[nl++] = (uint16_t)(carry & 0xffffu);
318
+ carry >>= 16;
319
+ }
320
+ }
321
+ while (nl > 1 && limbs[nl - 1] == 0)
322
+ nl--;
323
+ e->last_was_reg = 0;
324
+ e_byte(e, 'l');
325
+ e_byte(e, neg ? '-' : '+');
326
+ e_long(e, nl);
327
+ unsigned char b[256];
328
+ for (int i = 0; i < nl; i++) {
329
+ b[2 * i] = (unsigned char)(limbs[i] & 0xff);
330
+ b[2 * i + 1] = (unsigned char)(limbs[i] >> 8);
331
+ }
332
+ e_put(e, b, (size_t)nl * 2);
333
+ }
334
+
335
+ static int e_scalar(E* e, const YeptrisValue* v) {
336
+ const char* p = e_text(e, v);
337
+ switch (v->kind) {
338
+ case YEP_V_NULL:
339
+ e_null(e);
340
+ return 0;
341
+ case YEP_V_BOOL:
342
+ if (v->len == 1) { /* Psych: single-char y/n stay Strings */
343
+ e_str(e, p, v->len);
344
+ } else {
345
+ e_bool(e, v->b);
346
+ }
347
+ return 0;
348
+ case YEP_V_INT:
349
+ e_int(e, (int64_t)v->p);
350
+ return 0;
351
+ case YEP_V_FLOAT:
352
+ if (float_text_ok(e, p, v->len)) {
353
+ double d;
354
+ memcpy(&d, &v->p, sizeof(d));
355
+ e_float(e, d);
356
+ } else {
357
+ e_str(e, p, v->len);
358
+ }
359
+ return 0;
360
+ case YEP_V_STR:
361
+ if (v->b == 1 && v->len < 1300 && v->len > 18 && psych_int_text(p, v->len)) {
362
+ e_bignum_text(e, p, v->len);
363
+ return 0;
364
+ }
365
+ if (v->b == 1 && sym_scan(p, v->len)) {
366
+ e_sym(e, p + 1, v->len - 1);
367
+ } else {
368
+ e_str(e, p, v->len);
369
+ }
370
+ return 0;
371
+ default:
372
+ return -1; /* unreachable: the pre-scan rejects timestamps */
373
+ }
374
+ }
375
+
376
+ static int e_alias(E* e, const YeptrisValue* v) {
377
+ const char* p = e_text(e, v);
378
+ yep_anchor* a = anchor_find(e, p, v->len);
379
+ if (a == NULL) {
380
+ e->unsupported = 1; /* forward reference: impossible in YAML */
381
+ return -1;
382
+ }
383
+ if (a->has_obj) {
384
+ e->last_was_reg = 0;
385
+ e_byte(e, '@');
386
+ e_long(e, (int64_t)a->obj_id);
387
+ return 0;
388
+ }
389
+ /* immediate target: re-emit the value (floats re-register, which
390
+ * matches the loader's own registration discipline) */
391
+ return e_scalar(e, &a->scalar);
392
+ }
393
+
394
+ /* One value at cursor i: scalar, alias, or OPEN..CLOSE subtree.
395
+ * Returns the cursor after the value, or -1 on bail. */
396
+ long e_value(E* e, size_t i, size_t end);
397
+
398
+ static long e_body(E* e, size_t i, size_t end) {
399
+ const YeptrisValue* v = &e->c->vals[i];
400
+ switch (v->kind) {
401
+ case YEP_V_SEQ_OPEN:
402
+ case YEP_V_MAP_OPEN: {
403
+ uint64_t id;
404
+ e_reg(e, &id);
405
+ e_byte(e, v->kind == YEP_V_SEQ_OPEN ? '[' : '{');
406
+ e_long(e, (int64_t)e->counts[e->open_seq++]); /* pre-computed */
407
+ size_t j = i + 1;
408
+ while (j < end) {
409
+ if (e->c->vals[j].kind == YEP_V_CLOSE) {
410
+ break;
411
+ }
412
+ long nx = e_value(e, j, end);
413
+ if (nx < 0) {
414
+ return nx;
415
+ }
416
+ j = (size_t)nx;
417
+ }
418
+ if (j >= end) {
419
+ return -1; /* unterminated container */
420
+ }
421
+ e->last_reg = id;
422
+ e->last_was_reg = 1;
423
+ return (long)j + 1;
424
+ }
425
+ case YEP_V_CLOSE:
426
+ case YEP_V_DOC:
427
+ return -1; /* only the driver emits around these */
428
+ case YEP_V_ALIAS:
429
+ return e_alias(e, v) == 0 ? (long)i + 1 : -1;
430
+ default:
431
+ return e_scalar(e, v) == 0 ? (long)i + 1 : -1;
432
+ }
433
+ }
434
+
435
+ /* ANCHOR decorates the value that follows: bind after it completes. */
436
+ long e_value(E* e, size_t i, size_t end) {
437
+ const YeptrisValue* v = &e->c->vals[i];
438
+ if (v->kind != YEP_V_ANCHOR) {
439
+ return e_body(e, i, end);
440
+ }
441
+ if (anchor_add(e, e->c->arena + v->off, v->len) != 0) {
442
+ e->oom = 1;
443
+ return -1;
444
+ }
445
+ /* the index is a LOCAL: the decorated value can itself contain
446
+ * anchors (e.g. &a [&b x]), and the recursion's own bindings must
447
+ * not clobber ours (ASAN: anchors[-1] write on the snapshots) */
448
+ size_t pend_idx = e->nanchors - 1;
449
+ long nx = e_value(e, i + 1, end);
450
+ if (nx >= 0) {
451
+ yep_anchor* a = &e->anchors[pend_idx];
452
+ if (e->last_was_reg) {
453
+ a->has_obj = 1;
454
+ a->obj_id = e->last_reg;
455
+ } else {
456
+ a->is_scalar = 1;
457
+ a->scalar = e->c->vals[i + 1];
458
+ }
459
+ }
460
+ e->last_was_reg = 0;
461
+ return nx;
462
+ }
463
+
464
+ /* ---- the driver ---- */
465
+
466
+ typedef struct {
467
+ size_t from, to;
468
+ } yep_seg;
469
+
470
+ /* One linear scan: rejection scan (timestamps, merge keys), the
471
+ * per-open counts, and the DOC segmentation. Returns 0 ok, -1 oom,
472
+ * -2 unsupported. */
473
+ static int pre_scan(const yep_value_ctx* c, uint64_t** counts_out, size_t* nopens_out,
474
+ yep_seg** segs_out, size_t* nsegs_out) {
475
+ const YeptrisValue* vals = c->vals;
476
+ size_t n = c->n;
477
+ size_t opens = 0;
478
+ size_t ndocs = 0;
479
+ for (size_t i = 0; i < n; i++) {
480
+ switch (vals[i].kind) {
481
+ case YEP_V_SEQ_OPEN:
482
+ case YEP_V_MAP_OPEN:
483
+ opens++;
484
+ break;
485
+ case YEP_V_DOC:
486
+ ndocs++;
487
+ break;
488
+ case YEP_V_TIMESTAMP:
489
+ return -2;
490
+ case YEP_V_STR:
491
+ if (vals[i].tag_id == YEPTRIS_TAG_BINARY) {
492
+ return -2; /* !binary: the walk base64-decodes (#168) */
493
+ }
494
+ if (vals[i].is_key == 1 && vals[i].len == 2 && c->arena[vals[i].off] == '<' &&
495
+ c->arena[vals[i].off + 1] == '<') {
496
+ return -2; /* merge key: the walk resolves these */
497
+ }
498
+ break;
499
+ default:
500
+ break;
501
+ }
502
+ }
503
+ uint64_t* counts = NULL;
504
+ if (opens > 0) {
505
+ counts = calloc(opens, sizeof(*counts));
506
+ if (counts == NULL) {
507
+ return -1;
508
+ }
509
+ }
510
+ /* counts: one stack pass — every value record credits its
511
+ * innermost open (sequences count elements, maps count pairs) */
512
+ size_t* idxs = NULL;
513
+ unsigned char* maps = NULL;
514
+ size_t sp = 0, cap = 0;
515
+ size_t oi = 0;
516
+ /* documents: content AFTER each DOC marker ([d+1, next d); no
517
+ * markers at all = one implicit document [0, n) — node subtrees
518
+ * take that path too) */
519
+ size_t* doc_pos = NULL;
520
+ size_t ndoc_seen = 0;
521
+ if (ndocs > 0) {
522
+ doc_pos = calloc(ndocs, sizeof(*doc_pos));
523
+ if (doc_pos == NULL) {
524
+ free(counts);
525
+ return -1;
526
+ }
527
+ }
528
+ yep_seg* segs = NULL;
529
+ size_t nsegs = 0;
530
+ int bad = 0;
531
+ {
532
+ size_t scap = (ndocs > 0 ? ndocs : 1);
533
+ segs = calloc(scap, sizeof(*segs));
534
+ if (segs == NULL) {
535
+ free(counts);
536
+ free(doc_pos);
537
+ return -1;
538
+ }
539
+ }
540
+ for (size_t i = 0; i < n && !bad; i++) {
541
+ unsigned char k = vals[i].kind;
542
+ if (k == YEP_V_SEQ_OPEN || k == YEP_V_MAP_OPEN) {
543
+ /* the open itself is a slot in its parent (complex keys
544
+ * aside, this is how sequences nest) */
545
+ if (sp > 0 && (maps[sp - 1] == 0 || vals[i].is_key == 1)) {
546
+ counts[idxs[sp - 1]]++;
547
+ }
548
+ if (sp == cap) {
549
+ size_t ncap = cap ? cap * 2 : 64;
550
+ size_t* ni = realloc(idxs, ncap * sizeof(*ni));
551
+ unsigned char* nm = realloc(maps, ncap);
552
+ if (ni == NULL || nm == NULL) {
553
+ free(ni);
554
+ free(nm);
555
+ bad = 1;
556
+ break;
557
+ }
558
+ idxs = ni;
559
+ maps = nm;
560
+ cap = ncap;
561
+ }
562
+ maps[sp] = (k == YEP_V_MAP_OPEN);
563
+ idxs[sp] = oi++;
564
+ counts[idxs[sp]] = 0;
565
+ sp++;
566
+ } else if (k == YEP_V_CLOSE) {
567
+ if (sp > 0) {
568
+ sp--;
569
+ }
570
+ } else if (k == YEP_V_ANCHOR) {
571
+ /* decorates the next record: no slot */
572
+ } else if (k == YEP_V_DOC) {
573
+ doc_pos[ndoc_seen++] = i;
574
+ } else if (sp > 0) {
575
+ if (maps[sp - 1] == 0 || vals[i].is_key == 1) {
576
+ counts[idxs[sp - 1]]++;
577
+ }
578
+ }
579
+ }
580
+ free(idxs);
581
+ free(maps);
582
+ if (!bad) {
583
+ if (ndocs == 0) {
584
+ segs[nsegs].from = 0;
585
+ segs[nsegs].to = n;
586
+ nsegs = 1;
587
+ } else {
588
+ for (size_t i = 0; i < ndocs; i++) {
589
+ segs[i].from = doc_pos[i] + 1;
590
+ segs[i].to = (i + 1 < ndocs) ? doc_pos[i + 1] : n;
591
+ }
592
+ nsegs = ndocs;
593
+ }
594
+ }
595
+ free(doc_pos);
596
+ if (bad) {
597
+ free(counts);
598
+ free(segs);
599
+ return -1;
600
+ }
601
+ *counts_out = counts;
602
+ *nopens_out = opens;
603
+ *segs_out = segs;
604
+ *nsegs_out = nsegs;
605
+ return 0;
606
+ }
607
+
608
+ static void e_init(E* e, const yep_value_ctx* c, int compat, const uint64_t* counts, char* buf,
609
+ int sizing) {
610
+ memset(e, 0, sizeof(*e));
611
+ e->c = c;
612
+ e->compat = compat;
613
+ e->counts = counts;
614
+ e->buf = buf;
615
+ e->sizing = sizing;
616
+ }
617
+
618
+ /* Runs the walker over one segment set per `mode`. Returns 0 ok,
619
+ * -1 oom, -2 unsupported, -3 drift (a bug). */
620
+ static int run_pass(E* e, YeptrisMarshalMode mode, int node_mode, const yep_seg* segs,
621
+ size_t nsegs) {
622
+ static const unsigned char hdr[2] = {0x04, 0x08};
623
+ e_put(e, hdr, 2);
624
+ if (node_mode) {
625
+ long nx = e_value(e, segs[0].from, segs[0].to);
626
+ return nx < 0 ? (e->unsupported ? -2 : (e->oom ? -1 : -3)) : 0;
627
+ }
628
+ if (mode == YEPTRIS_MARSHAL_FIRST_DOC) {
629
+ if (nsegs == 0 || segs[0].from == segs[0].to) {
630
+ e_null(e); /* empty stream / empty first document */
631
+ return 0;
632
+ }
633
+ long nx = e_value(e, segs[0].from, segs[0].to);
634
+ return nx < 0 ? (e->unsupported ? -2 : (e->oom ? -1 : -3)) : 0;
635
+ }
636
+ /* ALL_DOCS: one array of every document's root */
637
+ size_t ndocs = nsegs;
638
+ uint64_t id;
639
+ e_reg(e, &id);
640
+ e_byte(e, '[');
641
+ e_long(e, (int64_t)ndocs);
642
+ for (size_t i = 0; i < nsegs; i++) {
643
+ if (segs[i].from == segs[i].to) {
644
+ e_null(e); /* an empty document */
645
+ continue;
646
+ }
647
+ long nx = e_value(e, segs[i].from, segs[i].to);
648
+ if (nx < 0) {
649
+ return e->unsupported ? -2 : (e->oom ? -1 : -3);
650
+ }
651
+ }
652
+ return 0;
653
+ }
654
+
655
+ static YeptrisStatus map_fail(int rc) {
656
+ return rc == -1 ? YEPTRIS_ERROR_MEMORY : YEPTRIS_ERROR_UNSUPPORTED;
657
+ }
658
+
659
+ static YeptrisStatus marshal_records(const yep_value_ctx* c, int compat, YeptrisMarshalMode mode,
660
+ int node_mode, char** out, size_t* out_len) {
661
+ *out = NULL;
662
+ *out_len = 0;
663
+ uint64_t* counts = NULL;
664
+ yep_seg* segs = NULL;
665
+ size_t opens = 0, nsegs = 0;
666
+ int prc = pre_scan(c, &counts, &opens, &segs, &nsegs);
667
+ if (prc != 0) {
668
+ return map_fail(prc);
669
+ }
670
+ E e;
671
+ e_init(&e, c, compat, counts, NULL, 1);
672
+ int rc = run_pass(&e, mode, node_mode, segs, nsegs);
673
+ size_t total = e.len;
674
+ free(e.anchors);
675
+ if (rc != 0) {
676
+ free(counts);
677
+ free(segs);
678
+ return map_fail(rc);
679
+ }
680
+ char* buf = malloc(total > 0 ? total : 1);
681
+ if (buf == NULL) {
682
+ free(counts);
683
+ free(segs);
684
+ return YEPTRIS_ERROR_MEMORY;
685
+ }
686
+ e_init(&e, c, compat, counts, buf, 0);
687
+ rc = run_pass(&e, mode, node_mode, segs, nsegs);
688
+ free(e.anchors);
689
+ free(counts);
690
+ free(segs);
691
+ if (rc != 0 || e.len != total) { /* drift between passes is a bug */
692
+ free(buf);
693
+ return rc != 0 ? map_fail(rc) : YEPTRIS_ERROR_INTERNAL;
694
+ }
695
+ *out = buf;
696
+ *out_len = total;
697
+ return YEPTRIS_OK;
698
+ }
699
+
700
+ YEPTRIS_API YeptrisStatus yeptris_marshal(const char* data, size_t len, YeptrisSchema schema,
701
+ YeptrisMarshalMode mode, char** out, size_t* out_len) {
702
+ if (out == NULL || out_len == NULL || (data == NULL && len != 0) ||
703
+ (mode != YEPTRIS_MARSHAL_ALL_DOCS && mode != YEPTRIS_MARSHAL_FIRST_DOC)) {
704
+ return YEPTRIS_ERROR_ARG;
705
+ }
706
+ *out = NULL;
707
+ *out_len = 0;
708
+ yep_value_ctx* c = NULL;
709
+ int rc = yep_values_from_input(data, len, schema == YEPTRIS_SCHEMA_11_COMPAT, &c);
710
+ if (rc != 0) {
711
+ return rc == -1 ? YEPTRIS_ERROR_MEMORY : YEPTRIS_ERROR_PARSE;
712
+ }
713
+ YeptrisStatus st =
714
+ marshal_records(c, schema == YEPTRIS_SCHEMA_11_COMPAT, mode, 0, out, out_len);
715
+ if (st == YEPTRIS_ERROR_UNSUPPORTED) {
716
+ yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
717
+ "marshal: merge key, timestamp or forward alias not expressible; "
718
+ "fall back to the value walk");
719
+ }
720
+ yep_value_ctx_free(c);
721
+ return st;
722
+ }
723
+
724
+ /* #178 (ruby #168's fast path): the value records carry the resolver's
725
+ * verdict, NOT the source's explicit tag — a `!ruby/object` mapping
726
+ * would marshal as a plain hash, silently dropping the tag (psych's
727
+ * visitor revives it). One cheap pre-walk: any explicitly tagged node
728
+ * in the subtree → UNSUPPORTED, the host walks. Explicit CORE tags
729
+ * (!!str and friends) ride the same bail — correct, just conservative;
730
+ * bulk data is untagged. */
731
+ static int dom_subtree_tagged(const yep_dom* d, uint32_t id) {
732
+ const yep_dnode* n = yep_dom_node(d, id);
733
+ if (n == NULL) {
734
+ return 0;
735
+ }
736
+ if (n->tag.len != 0) {
737
+ return 1;
738
+ }
739
+ for (uint32_t c = n->first_child; c != UINT32_MAX;) {
740
+ const yep_dnode* cn = yep_dom_node(d, c);
741
+ if (cn == NULL) {
742
+ break;
743
+ }
744
+ if (dom_subtree_tagged(d, c) != 0) {
745
+ return 1;
746
+ }
747
+ c = cn->next_sibling;
748
+ }
749
+ return 0;
750
+ }
751
+
752
+ YEPTRIS_API YeptrisStatus yeptris_marshal_node(YeptrisNode node, char** out, size_t* out_len) {
753
+ if (node == NULL || out == NULL || out_len == NULL) {
754
+ return YEPTRIS_ERROR_ARG;
755
+ }
756
+ *out = NULL;
757
+ *out_len = 0;
758
+ yeptris_node* h = (yeptris_node*)node;
759
+ if (dom_subtree_tagged(h->doc->dom, h->id) != 0) {
760
+ yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
761
+ "marshal: explicitly tagged node not expressible; "
762
+ "fall back to the value walk");
763
+ return YEPTRIS_ERROR_UNSUPPORTED;
764
+ }
765
+ yep_value_ctx* c = NULL;
766
+ if (yep_values_from_dom(h->doc->dom, h->id, 0, &c) != 0) {
767
+ return YEPTRIS_ERROR_MEMORY;
768
+ }
769
+ YeptrisStatus st = marshal_records(c, h->doc->schema == YEPTRIS_SCHEMA_11_COMPAT,
770
+ YEPTRIS_MARSHAL_ALL_DOCS, 1, out, out_len);
771
+ if (st == YEPTRIS_ERROR_UNSUPPORTED) {
772
+ yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, 0,
773
+ "marshal: merge key, timestamp or forward alias not expressible; "
774
+ "fall back to the value walk");
775
+ }
776
+ yep_value_ctx_free(c);
777
+ return st;
778
+ }
779
+
780
+ YEPTRIS_API void yeptris_marshal_free(char* out) {
781
+ free(out);
782
+ }