yeptris 0.6.10.1 → 0.6.11.1
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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fab04bdd5f0236d59b5d139d410c095d9c99c8aacccaa18c464324c58b0eef33
|
|
4
|
+
data.tar.gz: 9ba0ca96dd534843e8238921f3de634c7f18b051f90b3bde901be74b950a6403
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2281939709069aaf555ed5df307cc613b304ad236d7c3fc13cca8a4fc28a0ee8ca35f41d7dd3b46c9db71a8a088d90565e4ccfd7aa25c4512a83d46b41bddd79
|
|
7
|
+
data.tar.gz: 9731da1240594c945e1eba4f7eef86e3ec10556c23ab0c84617b2109c17c2c08e08d2c64c5cd558103c07abe2ef93da00735ca6c42703e61fbae4eac7a0ad64c
|
data/lib/yeptris.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Yeptris
|
|
4
4
|
# The gem's version lives in the parent namespace's file — the last
|
|
5
5
|
# internal require (yeptris/version) retired with it.
|
|
6
|
-
VERSION = "0.6.
|
|
6
|
+
VERSION = "0.6.11.1".freeze
|
|
7
7
|
# The error hierarchy lives in THIS file (the parent namespace's
|
|
8
8
|
# own file): nested constants do not trigger a parent-constant
|
|
9
9
|
# autoload, and the law forbids internal requires — defining the
|
|
@@ -39,6 +39,11 @@ YEPTRIS_API size_t yeptris_serialize_into_ex(YeptrisDocument handle,
|
|
|
39
39
|
return 0;
|
|
40
40
|
}
|
|
41
41
|
yep_emitter em;
|
|
42
|
+
em.w.sc_dec = NULL;
|
|
43
|
+
em.w.sc_i = 0;
|
|
44
|
+
em.w.cap = 0;
|
|
45
|
+
em.w.grow = 0;
|
|
46
|
+
em.w.oom = 0;
|
|
42
47
|
em.doc = (const yeptris_document*)handle;
|
|
43
48
|
em.w.p = NULL;
|
|
44
49
|
em.w.last = 0;
|
|
@@ -60,13 +65,20 @@ YEPTRIS_API size_t yeptris_serialize_into_ex(YeptrisDocument handle,
|
|
|
60
65
|
if (!yep_nametab_init(&em.canon_names, yep_system_allocator())) {
|
|
61
66
|
return 0;
|
|
62
67
|
}
|
|
68
|
+
em.w.sc_dec = (uint8_t*)malloc(em.doc->dom->ncount > 0 ? em.doc->dom->ncount : 1);
|
|
69
|
+
if (em.w.sc_dec == NULL) {
|
|
70
|
+
yep_nametab_free(&em.canon_names);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
63
73
|
size_t need = yep_emit_run(&em, 1);
|
|
64
74
|
if (buf == NULL || cap < need + 1) {
|
|
75
|
+
free(em.w.sc_dec);
|
|
65
76
|
yep_nametab_free(&em.canon_names);
|
|
66
77
|
return need;
|
|
67
78
|
}
|
|
68
79
|
em.w.p = buf;
|
|
69
80
|
size_t wrote = yep_emit_run(&em, 0);
|
|
81
|
+
free(em.w.sc_dec);
|
|
70
82
|
yep_nametab_free(&em.canon_names);
|
|
71
83
|
buf[wrote] = '\0';
|
|
72
84
|
return wrote;
|
|
@@ -82,6 +94,11 @@ YEPTRIS_API char* yeptris_serialize_ex(YeptrisDocument handle, const yeptris_emi
|
|
|
82
94
|
return NULL;
|
|
83
95
|
}
|
|
84
96
|
yep_emitter em;
|
|
97
|
+
em.w.sc_dec = NULL;
|
|
98
|
+
em.w.sc_i = 0;
|
|
99
|
+
em.w.cap = 0;
|
|
100
|
+
em.w.grow = 0;
|
|
101
|
+
em.w.oom = 0;
|
|
85
102
|
em.doc = (const yeptris_document*)handle;
|
|
86
103
|
em.w.p = NULL;
|
|
87
104
|
em.w.last = 0;
|
|
@@ -103,13 +120,41 @@ YEPTRIS_API char* yeptris_serialize_ex(YeptrisDocument handle, const yeptris_emi
|
|
|
103
120
|
if (!yep_nametab_init(&em.canon_names, yep_system_allocator())) {
|
|
104
121
|
return NULL;
|
|
105
122
|
}
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
/* #352 slice 2: ONE wet pass into a growable buffer — the dry
|
|
124
|
+
* exact-sizing pass measured 0.54-0.73x of this route on CI-fresh
|
|
125
|
+
* runners (the emit-split table's 2p/1p column). The estimate
|
|
126
|
+
* starts at the input size (+64) and doubles on demand; a final
|
|
127
|
+
* shrink-to-fit keeps the returned allocation tight. No dry pass,
|
|
128
|
+
* so no decisions table. */
|
|
129
|
+
size_t est = em.doc->dom->input_len + 64;
|
|
130
|
+
if (est < 256) {
|
|
131
|
+
est = 256;
|
|
132
|
+
}
|
|
133
|
+
char* out = (char*)malloc(est);
|
|
108
134
|
if (out == NULL) {
|
|
135
|
+
yep_nametab_free(&em.canon_names);
|
|
109
136
|
return NULL;
|
|
110
137
|
}
|
|
111
138
|
em.w.p = out;
|
|
139
|
+
em.w.cap = est;
|
|
140
|
+
em.w.grow = 1;
|
|
112
141
|
size_t wrote = yep_emit_run(&em, 0);
|
|
142
|
+
em.w.grow = 0;
|
|
143
|
+
/* wr_grow reallocs through w->p — the local `out` is stale after
|
|
144
|
+
* any growth; every post-run touch goes through the writer's
|
|
145
|
+
* pointer (the first round's abort was exactly this aliasing) */
|
|
146
|
+
out = em.w.p;
|
|
147
|
+
if (em.w.oom) {
|
|
148
|
+
free(out);
|
|
149
|
+
yep_nametab_free(&em.canon_names);
|
|
150
|
+
return NULL;
|
|
151
|
+
}
|
|
152
|
+
if (wrote + 1 < em.w.cap) {
|
|
153
|
+
char* tight = (char*)realloc(out, wrote + 1);
|
|
154
|
+
if (tight != NULL) {
|
|
155
|
+
out = tight;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
113
158
|
yep_nametab_free(&em.canon_names);
|
|
114
159
|
out[wrote] = '\0';
|
|
115
160
|
if (len != NULL) {
|
|
@@ -123,6 +168,11 @@ YEPTRIS_API char* yeptris_serialize_json(YeptrisDocument handle, size_t* len) {
|
|
|
123
168
|
return NULL;
|
|
124
169
|
}
|
|
125
170
|
yep_emitter em;
|
|
171
|
+
em.w.sc_dec = NULL;
|
|
172
|
+
em.w.sc_i = 0;
|
|
173
|
+
em.w.cap = 0;
|
|
174
|
+
em.w.grow = 0;
|
|
175
|
+
em.w.oom = 0;
|
|
126
176
|
em.doc = (const yeptris_document*)handle;
|
|
127
177
|
em.w.p = NULL;
|
|
128
178
|
em.w.last = 0;
|
|
@@ -168,6 +218,11 @@ YEPTRIS_API char* yeptris_serialize_json_ex(YeptrisDocument handle, size_t* len,
|
|
|
168
218
|
return NULL;
|
|
169
219
|
}
|
|
170
220
|
yep_emitter em;
|
|
221
|
+
em.w.sc_dec = NULL;
|
|
222
|
+
em.w.sc_i = 0;
|
|
223
|
+
em.w.cap = 0;
|
|
224
|
+
em.w.grow = 0;
|
|
225
|
+
em.w.oom = 0;
|
|
171
226
|
em.doc = (const yeptris_document*)handle;
|
|
172
227
|
em.w.p = NULL;
|
|
173
228
|
em.w.last = 0;
|
|
@@ -210,6 +265,11 @@ char* yep_serialize_json_compact(const yeptris_document* doc, size_t* len) {
|
|
|
210
265
|
return NULL;
|
|
211
266
|
}
|
|
212
267
|
yep_emitter em;
|
|
268
|
+
em.w.sc_dec = NULL;
|
|
269
|
+
em.w.sc_i = 0;
|
|
270
|
+
em.w.cap = 0;
|
|
271
|
+
em.w.grow = 0;
|
|
272
|
+
em.w.oom = 0;
|
|
213
273
|
em.doc = doc;
|
|
214
274
|
em.w.p = NULL;
|
|
215
275
|
em.w.last = 0;
|
|
@@ -252,6 +312,11 @@ char* yep_serialize_json_pretty(const yeptris_document* doc, size_t* len) {
|
|
|
252
312
|
return NULL;
|
|
253
313
|
}
|
|
254
314
|
yep_emitter em;
|
|
315
|
+
em.w.sc_dec = NULL;
|
|
316
|
+
em.w.sc_i = 0;
|
|
317
|
+
em.w.cap = 0;
|
|
318
|
+
em.w.grow = 0;
|
|
319
|
+
em.w.oom = 0;
|
|
255
320
|
em.doc = doc;
|
|
256
321
|
em.w.p = NULL;
|
|
257
322
|
em.w.last = 0;
|
|
@@ -307,6 +372,11 @@ YEPTRIS_API size_t yeptris_serialize_stream(YeptrisDocument handle,
|
|
|
307
372
|
return 0;
|
|
308
373
|
}
|
|
309
374
|
yep_emitter em;
|
|
375
|
+
em.w.sc_dec = NULL;
|
|
376
|
+
em.w.sc_i = 0;
|
|
377
|
+
em.w.cap = 0;
|
|
378
|
+
em.w.grow = 0;
|
|
379
|
+
em.w.oom = 0;
|
|
310
380
|
em.doc = (const yeptris_document*)handle;
|
|
311
381
|
em.w.p = NULL;
|
|
312
382
|
em.w.last = 0;
|
|
@@ -64,6 +64,24 @@ static void wr_col_update(yep_writer* w, const char* p, uint32_t n) {
|
|
|
64
64
|
w->col += (int)n;
|
|
65
65
|
}
|
|
66
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
|
+
|
|
67
85
|
static void wr_put(yep_writer* w, const char* p, uint32_t n) {
|
|
68
86
|
if (n > 0) {
|
|
69
87
|
w->last = p[n - 1];
|
|
@@ -73,6 +91,13 @@ static void wr_put(yep_writer* w, const char* p, uint32_t n) {
|
|
|
73
91
|
w->len += n;
|
|
74
92
|
return;
|
|
75
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
|
+
}
|
|
76
101
|
memcpy(w->p + w->len, p, n);
|
|
77
102
|
w->len += n;
|
|
78
103
|
wr_maybe_flush(w);
|
|
@@ -89,6 +114,13 @@ static void wr_byte(yep_writer* w, char c) {
|
|
|
89
114
|
w->len++;
|
|
90
115
|
return;
|
|
91
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
|
+
}
|
|
92
124
|
w->p[w->len] = c;
|
|
93
125
|
w->len++;
|
|
94
126
|
wr_maybe_flush(w);
|
|
@@ -275,21 +307,16 @@ static void emit_canonical_scalar(yep_writer* w, const yep_dnode* n) {
|
|
|
275
307
|
}
|
|
276
308
|
}
|
|
277
309
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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 */
|
|
310
|
+
/* The fidelity-mode emission route for one scalar (#352): the dry
|
|
311
|
+
* pass derives it exactly once; the wet pass consumes the recorded
|
|
312
|
+
* byte and skips the multiline/blockable/safety analysis entirely. */
|
|
313
|
+
enum { YEP_SC_NOTHING = 0, YEP_SC_PLAIN, YEP_SC_SQ, YEP_SC_DQ, YEP_SC_LITERAL };
|
|
314
|
+
#define YEP_SC_MEMO_MIN 32u /* bytes; derive-below, memoize-above */
|
|
315
|
+
|
|
316
|
+
static uint8_t sc_route(yep_writer* w, const char* p, uint32_t len, int as_key, uint8_t sty_in) {
|
|
288
317
|
int multiline = (len > 0 && memchr(p, '\n', len) != NULL);
|
|
289
318
|
int blockable = 0;
|
|
290
319
|
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
320
|
int allws = 1;
|
|
294
321
|
int has_cr = 0;
|
|
295
322
|
for (uint32_t i = 0; i < len; i++) {
|
|
@@ -302,68 +329,88 @@ static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int a
|
|
|
302
329
|
}
|
|
303
330
|
blockable = !allws && !has_cr;
|
|
304
331
|
}
|
|
305
|
-
|
|
332
|
+
uint8_t sty = sty_in;
|
|
306
333
|
if (sty == 4 || sty == 5) {
|
|
307
334
|
if (multiline && as_key) {
|
|
308
|
-
|
|
309
|
-
return;
|
|
335
|
+
return YEP_SC_DQ;
|
|
310
336
|
}
|
|
311
337
|
if (multiline && blockable) {
|
|
312
|
-
|
|
313
|
-
return;
|
|
338
|
+
return YEP_SC_LITERAL;
|
|
314
339
|
}
|
|
315
340
|
if (multiline) {
|
|
316
|
-
|
|
317
|
-
return;
|
|
341
|
+
return YEP_SC_DQ;
|
|
318
342
|
}
|
|
319
343
|
if (len == 0) {
|
|
320
|
-
|
|
321
|
-
the empty STRING — a bare empty would
|
|
322
|
-
re-read as null (2G84#3, K858) */
|
|
323
|
-
return;
|
|
344
|
+
return YEP_SC_DQ;
|
|
324
345
|
}
|
|
325
|
-
sty = 1;
|
|
346
|
+
sty = 1;
|
|
326
347
|
}
|
|
327
348
|
switch (sty) {
|
|
328
|
-
case 1:
|
|
349
|
+
case 1:
|
|
329
350
|
if (len == 0) {
|
|
330
|
-
|
|
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) */
|
|
351
|
+
return as_key ? YEP_SC_DQ : YEP_SC_NOTHING;
|
|
335
352
|
}
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
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;
|
|
353
|
+
if (as_key ? yep_style_plain_key_safe(p, len) : yep_style_plain_safe(p, len)) {
|
|
354
|
+
return YEP_SC_PLAIN;
|
|
355
|
+
}
|
|
356
|
+
if (!multiline) {
|
|
344
357
|
for (uint32_t i = 0; i < len; i++) {
|
|
345
358
|
if ((unsigned char)p[i] < 0x20 || (unsigned char)p[i] == 0x7f) {
|
|
346
|
-
|
|
347
|
-
break;
|
|
359
|
+
return YEP_SC_DQ;
|
|
348
360
|
}
|
|
349
361
|
}
|
|
350
|
-
|
|
351
|
-
emit_sq(w, p, len);
|
|
352
|
-
} else {
|
|
353
|
-
emit_dq(w, p, len);
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
emit_dq(w, p, len);
|
|
362
|
+
return YEP_SC_SQ;
|
|
357
363
|
}
|
|
358
|
-
|
|
359
|
-
case 2:
|
|
360
|
-
|
|
361
|
-
|
|
364
|
+
return YEP_SC_DQ;
|
|
365
|
+
case 2:
|
|
366
|
+
return multiline ? YEP_SC_DQ : YEP_SC_SQ;
|
|
367
|
+
default:
|
|
368
|
+
return YEP_SC_DQ;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
static void emit_scalar(yep_writer* w, const yep_dnode* n, int parent_col, int as_key) {
|
|
373
|
+
yep_view vdec = wv(w, n->value);
|
|
374
|
+
const char* p = (const char*)vdec.p;
|
|
375
|
+
uint32_t len = vdec.len;
|
|
376
|
+
if (w->canonical || w->json) {
|
|
377
|
+
emit_canonical_scalar(w, n);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
(void)0;
|
|
381
|
+
uint8_t route;
|
|
382
|
+
/* The memo pays only for LONG scalars: the derive cost scales with
|
|
383
|
+
* len (safety walk, quote scans) while the memo is a fixed store +
|
|
384
|
+
* cold load — on the short scalars of table-shaped documents the
|
|
385
|
+
* memo measured SLOWER than re-deriving (the #357 first artifact:
|
|
386
|
+
* json-users -36%, scalar-heavy +24%). Below the threshold both
|
|
387
|
+
* passes derive; the consumption-order index covers only the
|
|
388
|
+
* memoized long scalars, in visit order either way. */
|
|
389
|
+
if (w->sc_dec != NULL && len >= YEP_SC_MEMO_MIN) {
|
|
390
|
+
if (w->dry) {
|
|
391
|
+
route = sc_route(w, p, len, as_key, n->style);
|
|
392
|
+
w->sc_dec[w->sc_i] = route;
|
|
362
393
|
} else {
|
|
363
|
-
|
|
394
|
+
route = w->sc_dec[w->sc_i];
|
|
364
395
|
}
|
|
396
|
+
w->sc_i++;
|
|
397
|
+
} else {
|
|
398
|
+
route = sc_route(w, p, len, as_key, n->style);
|
|
399
|
+
}
|
|
400
|
+
switch (route) {
|
|
401
|
+
case YEP_SC_NOTHING:
|
|
402
|
+
break; /* an empty plain value emits as nothing (libyaml's null
|
|
403
|
+
rendering, issue #290) */
|
|
404
|
+
case YEP_SC_PLAIN:
|
|
405
|
+
wr_put(w, p, len);
|
|
406
|
+
break;
|
|
407
|
+
case YEP_SC_SQ:
|
|
408
|
+
emit_sq(w, p, len);
|
|
409
|
+
break;
|
|
410
|
+
case YEP_SC_LITERAL:
|
|
411
|
+
emit_literal(w, n, parent_col);
|
|
365
412
|
break;
|
|
366
|
-
default:
|
|
413
|
+
default:
|
|
367
414
|
emit_dq(w, p, len);
|
|
368
415
|
break;
|
|
369
416
|
}
|
|
@@ -694,6 +741,7 @@ size_t yep_emit_run(yep_emitter* em, int dry) {
|
|
|
694
741
|
w->flushed = 0;
|
|
695
742
|
w->sink_aborted = 0;
|
|
696
743
|
w->col = 0;
|
|
744
|
+
w->sc_i = 0;
|
|
697
745
|
yep_nametab_clear(&em->canon_names);
|
|
698
746
|
if (w->json) {
|
|
699
747
|
/* JSON has no multi-document streams and no document marker */
|
|
@@ -40,6 +40,21 @@ typedef struct yep_writer {
|
|
|
40
40
|
int col; /* current output column (wr_* maintains) */
|
|
41
41
|
const char* sv_input; /* compact-view decode bases (dom regions) */
|
|
42
42
|
const char* sv_arena;
|
|
43
|
+
/* #352: per-scalar route decisions, derived once in the dry
|
|
44
|
+
* sizing pass and consumed by the wet write pass (one byte per
|
|
45
|
+
* scalar, indexed by consumption order; NULL = derive per pass) */
|
|
46
|
+
uint8_t* sc_dec;
|
|
47
|
+
uint32_t sc_i;
|
|
48
|
+
/* #352 slice 2: the growable single-pass route — the exact-sizing
|
|
49
|
+
* dry pass measured 0.54-0.73x of the one-pass stream route on
|
|
50
|
+
* CI-fresh runners, so serialize_ex writes wet into a buffer that
|
|
51
|
+
* doubles on demand (cap/grow), like the streaming window. The
|
|
52
|
+
* fixed-buffer routes keep grow=0 and today's trust-the-count
|
|
53
|
+
* behavior. oom marks a failed growth: writes stop touching memory
|
|
54
|
+
* but the byte count keeps running so callers can fail cleanly. */
|
|
55
|
+
size_t cap;
|
|
56
|
+
int grow;
|
|
57
|
+
int oom;
|
|
43
58
|
} yep_writer;
|
|
44
59
|
|
|
45
60
|
typedef struct yep_emitter {
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yeptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.11.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|