teptris 0.3.3.1-arm-linux → 0.3.6.1-arm-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.
- checksums.yaml +4 -4
- data/ext/teptris_ext/engine/README.md +1 -1
- data/ext/teptris_ext/engine/src/include/teptris/teptris.h +17 -0
- data/ext/teptris_ext/engine/src/include/teptris/version.h +1 -1
- data/ext/teptris_ext/engine/src/teptris/emit/emit_shared.h +419 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.c +1 -404
- data/ext/teptris_ext/engine/src/teptris/emit/writer.c +575 -0
- data/ext/teptris_ext/engine/src/teptris/plan/plan.c +0 -2
- data/ext/teptris_ext/materialize.c +67 -1
- data/lib/teptris/3.3/teptris_ext.so +0 -0
- data/lib/teptris/3.4/teptris_ext.so +0 -0
- data/lib/teptris/4.0/teptris_ext.so +0 -0
- data/lib/teptris/toml.rb +18 -0
- data/lib/teptris/version.rb +1 -1
- data/sig/teptris.rbs +6 -0
- metadata +4 -2
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/* Streaming TOML writer: an op-stream in, TOML text out.
|
|
2
|
+
*
|
|
3
|
+
* Fuses the builder's DOM construction and the emitter's tree walk
|
|
4
|
+
* into one pass: bindings that walk host objects (Python dicts, JS
|
|
5
|
+
* objects) encode an op-stream instead of calling builder_put_* per
|
|
6
|
+
* value and re-emitting from the DOM. Sections emit as
|
|
7
|
+
* [dotted.headers] / [[aot.headers]] with full paths; arrays and
|
|
8
|
+
* inline tables render inline.
|
|
9
|
+
*
|
|
10
|
+
* Op grammar (little-endian, mirroring the flatten payload styles):
|
|
11
|
+
* 0x00 END-OF-CHUNK
|
|
12
|
+
* 0x01 KEY u8 klen <bytes> (klen 0 clears the pending key)
|
|
13
|
+
* 0x02 STRING u32 len <bytes>
|
|
14
|
+
* 0x03 INT i64
|
|
15
|
+
* 0x04 FLOAT f64
|
|
16
|
+
* 0x05 FALSE
|
|
17
|
+
* 0x06 TRUE
|
|
18
|
+
* 0x07 DATETIME u8 kind(4..7) <25 B, same layout as flatten>
|
|
19
|
+
* 0x08 BEGIN-TABLE section header ([k] / [a.b])
|
|
20
|
+
* 0x09 BEGIN-ARRAY inline array as a value
|
|
21
|
+
* 0x0A BEGIN-AOT array-of-tables ([[k]], elements via BEGIN-TABLE)
|
|
22
|
+
* 0x0B BEGIN-INLINE-TABLE {inline table: array element or keyed value}
|
|
23
|
+
* 0x0C CLOSE
|
|
24
|
+
* 0x0D EMPTY-TABLE (value {})
|
|
25
|
+
* 0x0E EMPTY-ARRAY (value [])
|
|
26
|
+
*
|
|
27
|
+
* Caller contract: within each table, feed every scalar BEFORE any
|
|
28
|
+
* nested table/AOT - TOML forbids keys after a sub-table header, and
|
|
29
|
+
* the writer enforces that with ERR_STATE instead of emitting invalid
|
|
30
|
+
* TOML. */
|
|
31
|
+
#include <stdlib.h>
|
|
32
|
+
#include <string.h>
|
|
33
|
+
|
|
34
|
+
#include "emit_shared.h"
|
|
35
|
+
#include "teptris/teptris.h"
|
|
36
|
+
|
|
37
|
+
enum {
|
|
38
|
+
W_END = 0x00,
|
|
39
|
+
W_KEY = 0x01,
|
|
40
|
+
W_STRING = 0x02,
|
|
41
|
+
W_INT = 0x03,
|
|
42
|
+
W_FLOAT = 0x04,
|
|
43
|
+
W_FALSE = 0x05,
|
|
44
|
+
W_TRUE = 0x06,
|
|
45
|
+
W_DATETIME = 0x07,
|
|
46
|
+
W_BEGIN_TABLE = 0x08,
|
|
47
|
+
W_BEGIN_ARRAY = 0x09,
|
|
48
|
+
W_BEGIN_AOT = 0x0A,
|
|
49
|
+
W_BEGIN_INLINE_TABLE = 0x0B,
|
|
50
|
+
W_CLOSE = 0x0C,
|
|
51
|
+
W_EMPTY_TABLE = 0x0D,
|
|
52
|
+
W_EMPTY_ARRAY = 0x0E
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
enum {
|
|
56
|
+
F_TABLE, /* section table (root or [dotted]) or an AOT element */
|
|
57
|
+
F_ARRAY, /* inline array */
|
|
58
|
+
F_AOT, /* the AOT itself: contributes its key to element paths */
|
|
59
|
+
F_ITAB /* inline table */
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
typedef struct wframe {
|
|
63
|
+
struct wframe *down;
|
|
64
|
+
int kind;
|
|
65
|
+
char *key; /* TBL section component; AOT key; else NULL */
|
|
66
|
+
size_t key_len;
|
|
67
|
+
int first; /* inline separator state */
|
|
68
|
+
int saw_subtable; /* TBL: a nested section was opened under us */
|
|
69
|
+
} wframe;
|
|
70
|
+
|
|
71
|
+
struct teptris_writer {
|
|
72
|
+
ebuf out;
|
|
73
|
+
char *key; /* pending key */
|
|
74
|
+
size_t key_len;
|
|
75
|
+
wframe *top;
|
|
76
|
+
int finished;
|
|
77
|
+
teptris_status st;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
static wframe *w_push(teptris_writer *w, int kind, const char *k,
|
|
81
|
+
size_t klen);
|
|
82
|
+
|
|
83
|
+
teptris_writer *teptris_writer_new(void)
|
|
84
|
+
{
|
|
85
|
+
teptris_writer *w = calloc(1, sizeof(*w));
|
|
86
|
+
if (w != NULL) {
|
|
87
|
+
/* implicit root table: tracks root-level value/sub-table
|
|
88
|
+
* ordering; never closed by CLOSE */
|
|
89
|
+
w_push(w, F_TABLE, NULL, 0);
|
|
90
|
+
w->st = TEPTRIS_OK;
|
|
91
|
+
}
|
|
92
|
+
return w;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void teptris_writer_free(teptris_writer *w)
|
|
96
|
+
{
|
|
97
|
+
if (w == NULL) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
while (w->top != NULL) {
|
|
101
|
+
wframe *f = w->top;
|
|
102
|
+
w->top = f->down;
|
|
103
|
+
free(f->key);
|
|
104
|
+
free(f);
|
|
105
|
+
}
|
|
106
|
+
free(w->key);
|
|
107
|
+
free(w->out.p);
|
|
108
|
+
free(w);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static void w_take_key(teptris_writer *w, const char *k, size_t klen)
|
|
112
|
+
{
|
|
113
|
+
free(w->key);
|
|
114
|
+
w->key = NULL;
|
|
115
|
+
w->key_len = 0;
|
|
116
|
+
if (klen > 0) {
|
|
117
|
+
w->key = malloc(klen);
|
|
118
|
+
if (w->key == NULL) {
|
|
119
|
+
w->st = TEPTRIS_ERR_ALLOC;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
memcpy(w->key, k, klen);
|
|
123
|
+
w->key_len = klen;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static void w_clear_key(teptris_writer *w)
|
|
128
|
+
{
|
|
129
|
+
free(w->key);
|
|
130
|
+
w->key = NULL;
|
|
131
|
+
w->key_len = 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* push with an OWNED key (transfers the malloc from the caller) */
|
|
135
|
+
static wframe *w_push_steal(teptris_writer *w, int kind, char *k,
|
|
136
|
+
size_t klen)
|
|
137
|
+
{
|
|
138
|
+
wframe *f = calloc(1, sizeof(*f));
|
|
139
|
+
if (f == NULL) {
|
|
140
|
+
free(k);
|
|
141
|
+
w->st = TEPTRIS_ERR_ALLOC;
|
|
142
|
+
return NULL;
|
|
143
|
+
}
|
|
144
|
+
f->down = w->top;
|
|
145
|
+
f->kind = kind;
|
|
146
|
+
f->key = k;
|
|
147
|
+
f->key_len = klen;
|
|
148
|
+
w->top = f;
|
|
149
|
+
return f;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static wframe *w_push(teptris_writer *w, int kind, const char *k,
|
|
153
|
+
size_t klen)
|
|
154
|
+
{
|
|
155
|
+
wframe *f = calloc(1, sizeof(*f));
|
|
156
|
+
if (f == NULL) {
|
|
157
|
+
w->st = TEPTRIS_ERR_ALLOC;
|
|
158
|
+
return NULL;
|
|
159
|
+
}
|
|
160
|
+
f->down = w->top;
|
|
161
|
+
f->kind = kind;
|
|
162
|
+
if (klen > 0) {
|
|
163
|
+
f->key = malloc(klen);
|
|
164
|
+
if (f->key == NULL) {
|
|
165
|
+
free(f);
|
|
166
|
+
w->st = TEPTRIS_ERR_ALLOC;
|
|
167
|
+
return NULL;
|
|
168
|
+
}
|
|
169
|
+
memcpy(f->key, k, klen);
|
|
170
|
+
f->key_len = klen;
|
|
171
|
+
}
|
|
172
|
+
w->top = f;
|
|
173
|
+
return f;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* dotted path of all keyed frames down the stack, outermost first;
|
|
177
|
+
* with_tail appends the pending key as the last component */
|
|
178
|
+
static void w_path(teptris_writer *w, int with_tail)
|
|
179
|
+
{
|
|
180
|
+
wframe *stack[600];
|
|
181
|
+
int n = 0;
|
|
182
|
+
for (wframe *f = w->top; f != NULL && n < 600; f = f->down) {
|
|
183
|
+
stack[n++] = f;
|
|
184
|
+
}
|
|
185
|
+
int first = 1;
|
|
186
|
+
for (int i = n - 1; i >= 0; i--) {
|
|
187
|
+
wframe *f = stack[i];
|
|
188
|
+
if ((f->kind == F_TABLE || f->kind == F_AOT) && f->key != NULL) {
|
|
189
|
+
if (!first) {
|
|
190
|
+
eb_c(&w->out, '.');
|
|
191
|
+
}
|
|
192
|
+
emit_key(&w->out, (teptris_view){f->key, f->key_len});
|
|
193
|
+
first = 0;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (with_tail && w->key != NULL) {
|
|
197
|
+
if (!first) {
|
|
198
|
+
eb_c(&w->out, '.');
|
|
199
|
+
}
|
|
200
|
+
emit_key(&w->out, (teptris_view){w->key, w->key_len});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* section headers follow the previous line directly (emitter style) */
|
|
205
|
+
static void w_sep(teptris_writer *w)
|
|
206
|
+
{
|
|
207
|
+
(void)w;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static void w_sep_val(teptris_writer *w, wframe *f)
|
|
211
|
+
{
|
|
212
|
+
if (f->first) {
|
|
213
|
+
eb_put(&w->out, ", ", 2);
|
|
214
|
+
}
|
|
215
|
+
f->first = 1;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* emit "key = " in a section/inline-table context; arrays take bare
|
|
219
|
+
* values with separators */
|
|
220
|
+
static void w_keyed_prefix(teptris_writer *w)
|
|
221
|
+
{
|
|
222
|
+
wframe *f = w->top;
|
|
223
|
+
if (f != NULL && f->kind == F_ARRAY) {
|
|
224
|
+
w_sep_val(w, f);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (w->key == NULL) {
|
|
228
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (f != NULL && f->saw_subtable) {
|
|
232
|
+
w->st = TEPTRIS_ERR_STATE; /* value after a nested table header */
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
emit_key(&w->out, (teptris_view){w->key, w->key_len});
|
|
236
|
+
eb_put(&w->out, " = ", 3);
|
|
237
|
+
w_clear_key(w);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static void w_end_line(teptris_writer *w)
|
|
241
|
+
{
|
|
242
|
+
wframe *f = w->top;
|
|
243
|
+
if (f == NULL || f->kind == F_TABLE) {
|
|
244
|
+
eb_c(&w->out, '\n');
|
|
245
|
+
}
|
|
246
|
+
/* inside inline arrays/tables the separator or close comes next */
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
teptris_status teptris_writer_feed(teptris_writer *w, const uint8_t *ops,
|
|
250
|
+
size_t len)
|
|
251
|
+
{
|
|
252
|
+
if (w == NULL || (ops == NULL && len > 0)) {
|
|
253
|
+
return TEPTRIS_ERR_ARG;
|
|
254
|
+
}
|
|
255
|
+
if (w->finished) {
|
|
256
|
+
return TEPTRIS_ERR_STATE;
|
|
257
|
+
}
|
|
258
|
+
size_t i = 0;
|
|
259
|
+
while (i < len && w->st == TEPTRIS_OK) {
|
|
260
|
+
uint8_t op = ops[i++];
|
|
261
|
+
switch (op) {
|
|
262
|
+
case W_END:
|
|
263
|
+
return w->st;
|
|
264
|
+
case W_KEY: {
|
|
265
|
+
if (i >= len) {
|
|
266
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
uint8_t klen = ops[i++];
|
|
270
|
+
if (i + klen > len) {
|
|
271
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
w_take_key(w, (const char *)ops + i, klen);
|
|
275
|
+
i += klen;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case W_STRING: {
|
|
279
|
+
if (i + 4 > len) {
|
|
280
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
uint32_t vlen = (uint32_t)ops[i] |
|
|
284
|
+
((uint32_t)ops[i + 1] << 8) |
|
|
285
|
+
((uint32_t)ops[i + 2] << 16) |
|
|
286
|
+
((uint32_t)ops[i + 3] << 24);
|
|
287
|
+
i += 4;
|
|
288
|
+
if (i + vlen > len) {
|
|
289
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
w_keyed_prefix(w);
|
|
293
|
+
if (w->st != TEPTRIS_OK) {
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
emit_string_value(&w->out,
|
|
297
|
+
(teptris_view){(const char *)ops + i, vlen});
|
|
298
|
+
i += vlen;
|
|
299
|
+
w_end_line(w);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case W_INT: {
|
|
303
|
+
if (i + 8 > len) {
|
|
304
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
uint64_t u = 0;
|
|
308
|
+
for (int b = 0; b < 8; b++) {
|
|
309
|
+
u |= (uint64_t)ops[i + b] << (8 * b); /* little-endian */
|
|
310
|
+
}
|
|
311
|
+
int64_t v;
|
|
312
|
+
memcpy(&v, &u, 8);
|
|
313
|
+
i += 8;
|
|
314
|
+
w_keyed_prefix(w);
|
|
315
|
+
if (w->st != TEPTRIS_OK) {
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
emit_i64(&w->out, v);
|
|
319
|
+
w_end_line(w);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
case W_FLOAT: {
|
|
323
|
+
if (i + 8 > len) {
|
|
324
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
uint64_t u = 0;
|
|
328
|
+
for (int b = 0; b < 8; b++) {
|
|
329
|
+
u |= (uint64_t)ops[i + b] << (8 * b); /* little-endian */
|
|
330
|
+
}
|
|
331
|
+
double d;
|
|
332
|
+
memcpy(&d, &u, 8);
|
|
333
|
+
i += 8;
|
|
334
|
+
w_keyed_prefix(w);
|
|
335
|
+
if (w->st != TEPTRIS_OK) {
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
emit_float(&w->out, d);
|
|
339
|
+
w_end_line(w);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case W_FALSE:
|
|
343
|
+
case W_TRUE: {
|
|
344
|
+
w_keyed_prefix(w);
|
|
345
|
+
if (w->st != TEPTRIS_OK) {
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (op == W_TRUE) {
|
|
349
|
+
eb_put(&w->out, "true", 4);
|
|
350
|
+
} else {
|
|
351
|
+
eb_put(&w->out, "false", 5);
|
|
352
|
+
}
|
|
353
|
+
w_end_line(w);
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case W_DATETIME: {
|
|
357
|
+
if (i + 26 > len) { /* kind + i64 year + 5 + u32 ns + i64 off */
|
|
358
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
uint8_t kb = ops[i];
|
|
362
|
+
if (kb < 4 || kb > 7) {
|
|
363
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
teptris_datetime dt;
|
|
367
|
+
uint64_t yr = 0, off = 0, ns = 0;
|
|
368
|
+
for (int b = 0; b < 8; b++) {
|
|
369
|
+
yr |= (uint64_t)ops[i + 1 + b] << (8 * b); /* LE */
|
|
370
|
+
off |= (uint64_t)ops[i + 18 + b] << (8 * b);
|
|
371
|
+
}
|
|
372
|
+
for (int b = 0; b < 4; b++) {
|
|
373
|
+
ns |= (uint64_t)ops[i + 14 + b] << (8 * b);
|
|
374
|
+
}
|
|
375
|
+
dt.year = (int32_t)yr;
|
|
376
|
+
dt.month = ops[i + 9];
|
|
377
|
+
dt.day = ops[i + 10];
|
|
378
|
+
dt.hour = ops[i + 11];
|
|
379
|
+
dt.minute = ops[i + 12];
|
|
380
|
+
dt.second = ops[i + 13];
|
|
381
|
+
memcpy(&dt.nanosecond, &ns, 4);
|
|
382
|
+
dt.offset_seconds = (int32_t)off;
|
|
383
|
+
i += 26;
|
|
384
|
+
w_keyed_prefix(w);
|
|
385
|
+
if (w->st != TEPTRIS_OK) {
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
emit_dt(&w->out, &dt, (teptris_kind)kb);
|
|
389
|
+
w_end_line(w);
|
|
390
|
+
break;
|
|
391
|
+
if (w->st != TEPTRIS_OK) {
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
emit_dt(&w->out, &dt, (teptris_kind)kb);
|
|
395
|
+
w_end_line(w);
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case W_BEGIN_TABLE: {
|
|
399
|
+
wframe *t = w->top;
|
|
400
|
+
if (t != NULL && (t->kind == F_ARRAY || t->kind == F_ITAB)) {
|
|
401
|
+
/* sections cannot live inside inline containers; use
|
|
402
|
+
* BEGIN_INLINE_TABLE there */
|
|
403
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
if (t != NULL && t->kind == F_AOT) {
|
|
407
|
+
/* element: [[path]] header, keyless frame */
|
|
408
|
+
w_sep(w);
|
|
409
|
+
eb_put(&w->out, "[[", 2);
|
|
410
|
+
w_path(w, 0);
|
|
411
|
+
eb_put(&w->out, "]]\n", 3);
|
|
412
|
+
w_clear_key(w);
|
|
413
|
+
w_push(w, F_TABLE, NULL, 0);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
if (w->key == NULL) {
|
|
417
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
if (t != NULL) {
|
|
421
|
+
t->saw_subtable = 1;
|
|
422
|
+
}
|
|
423
|
+
w_sep(w);
|
|
424
|
+
eb_c(&w->out, '[');
|
|
425
|
+
w_path(w, 1);
|
|
426
|
+
eb_put(&w->out, "]\n", 2);
|
|
427
|
+
char *k = w->key; /* frame takes ownership */
|
|
428
|
+
size_t klen = w->key_len;
|
|
429
|
+
w->key = NULL;
|
|
430
|
+
w->key_len = 0;
|
|
431
|
+
w_push_steal(w, F_TABLE, k, klen);
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case W_BEGIN_AOT: {
|
|
435
|
+
if (w->key == NULL) {
|
|
436
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
wframe *t = w->top;
|
|
440
|
+
if (t != NULL && t->kind != F_TABLE) {
|
|
441
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
if (t != NULL) {
|
|
445
|
+
t->saw_subtable = 1;
|
|
446
|
+
}
|
|
447
|
+
char *k = w->key; /* frame takes ownership */
|
|
448
|
+
size_t klen = w->key_len;
|
|
449
|
+
w->key = NULL;
|
|
450
|
+
w->key_len = 0;
|
|
451
|
+
w_push_steal(w, F_AOT, k, klen);
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
case W_BEGIN_ARRAY: {
|
|
455
|
+
wframe *t = w->top;
|
|
456
|
+
if (t != NULL && t->kind == F_AOT) {
|
|
457
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
if (t == NULL || t->kind == F_TABLE || t->kind == F_ITAB) {
|
|
461
|
+
if (w->key == NULL) {
|
|
462
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
if (t != NULL && t->saw_subtable) {
|
|
466
|
+
w->st = TEPTRIS_ERR_STATE;
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
emit_key(&w->out, (teptris_view){w->key, w->key_len});
|
|
470
|
+
eb_put(&w->out, " = [", 4);
|
|
471
|
+
w_clear_key(w);
|
|
472
|
+
} else { /* F_ARRAY: nested inline array */
|
|
473
|
+
w_sep_val(w, t);
|
|
474
|
+
eb_c(&w->out, '[');
|
|
475
|
+
}
|
|
476
|
+
w_push(w, F_ARRAY, NULL, 0);
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
case W_BEGIN_INLINE_TABLE: {
|
|
480
|
+
wframe *t = w->top;
|
|
481
|
+
if (t != NULL && t->kind == F_ARRAY) {
|
|
482
|
+
if (w->key != NULL) {
|
|
483
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
w_sep_val(w, t);
|
|
487
|
+
} else {
|
|
488
|
+
if (w->key == NULL) {
|
|
489
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
if (t != NULL && t->saw_subtable) {
|
|
493
|
+
w->st = TEPTRIS_ERR_STATE;
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
emit_key(&w->out, (teptris_view){w->key, w->key_len});
|
|
497
|
+
eb_put(&w->out, " = ", 3);
|
|
498
|
+
w_clear_key(w);
|
|
499
|
+
}
|
|
500
|
+
eb_c(&w->out, '{');
|
|
501
|
+
w_push(w, F_ITAB, NULL, 0);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case W_CLOSE: {
|
|
505
|
+
wframe *f = w->top;
|
|
506
|
+
if (f == NULL || f->down == NULL) {
|
|
507
|
+
w->st = TEPTRIS_ERR_ARG; /* nothing (or only the root) open */
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
int kind = f->kind;
|
|
511
|
+
w->top = f->down;
|
|
512
|
+
free(f->key);
|
|
513
|
+
free(f);
|
|
514
|
+
if (kind == F_ARRAY) {
|
|
515
|
+
eb_c(&w->out, ']');
|
|
516
|
+
w_end_line(w);
|
|
517
|
+
} else if (kind == F_ITAB) {
|
|
518
|
+
eb_c(&w->out, '}');
|
|
519
|
+
w_end_line(w);
|
|
520
|
+
}
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case W_EMPTY_TABLE:
|
|
524
|
+
case W_EMPTY_ARRAY: {
|
|
525
|
+
w_keyed_prefix(w);
|
|
526
|
+
if (w->st != TEPTRIS_OK) {
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
if (op == W_EMPTY_TABLE) {
|
|
530
|
+
eb_put(&w->out, "{}", 2);
|
|
531
|
+
} else {
|
|
532
|
+
eb_put(&w->out, "[]", 2);
|
|
533
|
+
}
|
|
534
|
+
w_end_line(w);
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
default:
|
|
538
|
+
w->st = TEPTRIS_ERR_ARG;
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return w->st;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
teptris_status teptris_writer_finish(teptris_writer *w, char **buf,
|
|
546
|
+
size_t *len)
|
|
547
|
+
{
|
|
548
|
+
if (w == NULL || buf == NULL || len == NULL) {
|
|
549
|
+
return TEPTRIS_ERR_ARG;
|
|
550
|
+
}
|
|
551
|
+
*buf = NULL;
|
|
552
|
+
*len = 0;
|
|
553
|
+
if (w->top == NULL || w->top->down != NULL) {
|
|
554
|
+
return TEPTRIS_ERR_STATE; /* unclosed containers */
|
|
555
|
+
}
|
|
556
|
+
if (w->st != TEPTRIS_OK) {
|
|
557
|
+
return w->st;
|
|
558
|
+
}
|
|
559
|
+
w->finished = 1;
|
|
560
|
+
if (w->out.p == NULL) {
|
|
561
|
+
char *empty = malloc(1);
|
|
562
|
+
if (empty == NULL) {
|
|
563
|
+
return TEPTRIS_ERR_ALLOC;
|
|
564
|
+
}
|
|
565
|
+
empty[0] = '\0';
|
|
566
|
+
*buf = empty;
|
|
567
|
+
*len = 0;
|
|
568
|
+
return TEPTRIS_OK;
|
|
569
|
+
}
|
|
570
|
+
w->out.p[w->out.len] = '\0';
|
|
571
|
+
*buf = w->out.p;
|
|
572
|
+
*len = w->out.len;
|
|
573
|
+
w->out.p = NULL; /* ownership transferred */
|
|
574
|
+
return TEPTRIS_OK;
|
|
575
|
+
}
|
|
@@ -115,8 +115,6 @@ teptris_plan *teptris_plan_build(const teptris_plan_spec *spec,
|
|
|
115
115
|
}
|
|
116
116
|
int too_deep = 0;
|
|
117
117
|
for (uint32_t pi = 0; pi < p->plan_count; pi++) {
|
|
118
|
-
fprintf(stderr, "DBG depths[%u]=%u first=%u\n", pi, depths[pi],
|
|
119
|
-
p->first_row[pi]);
|
|
120
118
|
if (depths[pi] > TEPTRIS_PLAN_MAX_DEPTH) {
|
|
121
119
|
too_deep = 1;
|
|
122
120
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#define FMT_FORBID_TIME 2
|
|
10
10
|
#define FMT_FORBID_DATE 4
|
|
11
11
|
|
|
12
|
-
static VALUE eParseError, eError, cDate;
|
|
12
|
+
static VALUE eParseError, eError, cDate, cDoc;
|
|
13
13
|
|
|
14
14
|
/* civil date -> days since epoch (Hinnant, inverse of the dump side) */
|
|
15
15
|
static int64_t days_from_civil(int32_t y, uint8_t m, uint8_t d) {
|
|
@@ -297,6 +297,66 @@ static VALUE ext_load(int argc, VALUE *argv, VALUE self) {
|
|
|
297
297
|
return out;
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/* ---------------- native-doc API: the retained DOM ----------------
|
|
301
|
+
* load_doc parses into an engine document the wrapper OWNS, together
|
|
302
|
+
* with a private copy of the input - the doc's keys/values are
|
|
303
|
+
* zero-copy views into that copy, so both live and die together.
|
|
304
|
+
* dump_doc serializes with the C emitter: the roundtrip
|
|
305
|
+
* dump_doc(load_doc(s)) never materializes Ruby objects. */
|
|
306
|
+
|
|
307
|
+
static void dump_check(teptris_status st);
|
|
308
|
+
|
|
309
|
+
typedef struct {
|
|
310
|
+
teptris_document *doc;
|
|
311
|
+
char *buf;
|
|
312
|
+
} doc_owner;
|
|
313
|
+
|
|
314
|
+
static void doc_owner_free(void *p) {
|
|
315
|
+
doc_owner *o = (doc_owner *)p;
|
|
316
|
+
if (o->doc != NULL) teptris_document_free(o->doc);
|
|
317
|
+
if (o->buf != NULL) xfree(o->buf);
|
|
318
|
+
xfree(o);
|
|
319
|
+
}
|
|
320
|
+
static size_t doc_owner_size(const void *p) { return sizeof(doc_owner); }
|
|
321
|
+
static const rb_data_type_t doc_owner_type = {
|
|
322
|
+
"TeptrisExt/Doc",
|
|
323
|
+
{0, doc_owner_free, doc_owner_size, NULL, 0},
|
|
324
|
+
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY};
|
|
325
|
+
|
|
326
|
+
static VALUE ext_load_doc(VALUE self, VALUE str) {
|
|
327
|
+
StringValue(str);
|
|
328
|
+
long len = RSTRING_LEN(str);
|
|
329
|
+
char *cbuf = ALLOC_N(char, (size_t)len + 1);
|
|
330
|
+
memcpy(cbuf, RSTRING_PTR(str), (size_t)len);
|
|
331
|
+
teptris_document *doc = NULL;
|
|
332
|
+
struct gvl_parse_arg arg = {cbuf, (size_t)len, &doc, TEPTRIS_OK};
|
|
333
|
+
rb_thread_call_without_gvl(parse_without_gvl, &arg, RUBY_UBF_IO, NULL);
|
|
334
|
+
if (arg.st != TEPTRIS_OK) {
|
|
335
|
+
const teptris_error *e = teptris_document_error(doc);
|
|
336
|
+
VALUE ex = rb_exc_new(eParseError, e->message, (long)strlen(e->message));
|
|
337
|
+
rb_iv_set(ex, "@line", SIZET2NUM(e->line));
|
|
338
|
+
rb_iv_set(ex, "@column", SIZET2NUM(e->column));
|
|
339
|
+
xfree(cbuf);
|
|
340
|
+
teptris_document_free(doc);
|
|
341
|
+
rb_exc_raise(ex);
|
|
342
|
+
}
|
|
343
|
+
doc_owner *o = ALLOC(doc_owner);
|
|
344
|
+
o->doc = doc;
|
|
345
|
+
o->buf = cbuf;
|
|
346
|
+
return TypedData_Wrap_Struct(cDoc, &doc_owner_type, o);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
static VALUE ext_dump_doc(VALUE self, VALUE rbdoc) {
|
|
350
|
+
doc_owner *o;
|
|
351
|
+
TypedData_Get_Struct(rbdoc, doc_owner, &doc_owner_type, o);
|
|
352
|
+
char *buf = NULL;
|
|
353
|
+
size_t len = 0;
|
|
354
|
+
dump_check(teptris_document_emit(o->doc, &buf, &len));
|
|
355
|
+
VALUE out = rb_utf8_str_new(buf, (long)len);
|
|
356
|
+
free(buf);
|
|
357
|
+
return out;
|
|
358
|
+
}
|
|
359
|
+
|
|
300
360
|
static VALUE ext_version(VALUE self) {
|
|
301
361
|
return rb_str_new_cstr(teptris_version_string());
|
|
302
362
|
}
|
|
@@ -1209,7 +1269,13 @@ void Init_teptris_ext(void) {
|
|
|
1209
1269
|
* call state (key cache, arenas, types) is per-object or per-call */
|
|
1210
1270
|
rb_ext_ractor_safe(true);
|
|
1211
1271
|
VALUE m = rb_define_module("TeptrisExt");
|
|
1272
|
+
cDoc = rb_define_class_under(m, "Doc", rb_cObject);
|
|
1273
|
+
/* opaque handle: no Ruby-side allocation (silences the
|
|
1274
|
+
* allocator-undefine warning on reload) */
|
|
1275
|
+
rb_undef_alloc_func(cDoc);
|
|
1212
1276
|
rb_define_module_function(m, "load", ext_load, -1);
|
|
1277
|
+
rb_define_module_function(m, "load_doc", ext_load_doc, 1);
|
|
1278
|
+
rb_define_module_function(m, "dump_doc", ext_dump_doc, 1);
|
|
1213
1279
|
rb_define_module_function(m, "load_lazy", ext_load_lazy, 1);
|
|
1214
1280
|
rb_define_module_function(m, "load_batch", ext_load_batch, -1);
|
|
1215
1281
|
rb_define_module_function(m, "load_slice", ext_load_slice, -1);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/teptris/toml.rb
CHANGED
|
@@ -18,6 +18,24 @@ module Teptris::TOML
|
|
|
18
18
|
TeptrisExt.load(toml)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# Parse into a RETAINED engine document (an opaque TeptrisExt::Doc
|
|
22
|
+
# handle; no Ruby objects are materialized). Pair with dump_doc for
|
|
23
|
+
# a roundtrip that runs entirely at C speed - reserialization of
|
|
24
|
+
# an unchanged document skips both Ruby-object walks. The doc owns
|
|
25
|
+
# a private copy of the input: its values are views into it, so
|
|
26
|
+
# mutating the source string afterwards is invisible (and the doc
|
|
27
|
+
# keeps the copy alive).
|
|
28
|
+
def load_doc(toml)
|
|
29
|
+
raise ArgumentError, "input must be a String" unless toml.is_a?(String)
|
|
30
|
+
|
|
31
|
+
TeptrisExt.load_doc(toml)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Emit a load_doc() document at C speed (canonical TOML).
|
|
35
|
+
def dump_doc(doc)
|
|
36
|
+
TeptrisExt.dump_doc(doc)
|
|
37
|
+
end
|
|
38
|
+
|
|
21
39
|
# Safe-load shaped entry point for frameworks: permitted_classes
|
|
22
40
|
# restricts datetime materialization (raise when a value would
|
|
23
41
|
# materialize an unpermitted class); datetime_policy :string keeps
|
data/lib/teptris/version.rb
CHANGED