teptris 0.2.37-s390x-linux → 0.2.39-s390x-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/LICENSE.md +21 -0
- data/ext/teptris_ext/engine/src/include/teptris/plan.h +123 -0
- data/ext/teptris_ext/engine/src/include/teptris/teptris.h +208 -0
- data/ext/teptris_ext/engine/src/include/teptris/version.h +9 -0
- data/ext/teptris_ext/engine/src/teptris/api.c +362 -0
- data/ext/teptris_ext/engine/src/teptris/build.c +355 -0
- data/ext/teptris_ext/engine/src/teptris/common/chartype.c +70 -0
- data/ext/teptris_ext/engine/src/teptris/common/chartype.h +41 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/common.h +114 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s.c +496 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_full_table.h +367 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_intrinsics.h +357 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/digit_table.h +35 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_d2s.h +39 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_parse.h +45 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/s2d.c +259 -0
- data/ext/teptris_ext/engine/src/teptris/common/simd.h +119 -0
- data/ext/teptris_ext/engine/src/teptris/dom/dom.c +192 -0
- data/ext/teptris_ext/engine/src/teptris/dom/dom.h +124 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.c +718 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.h +11 -0
- data/ext/teptris_ext/engine/src/teptris/memory/arena.c +104 -0
- data/ext/teptris_ext/engine/src/teptris/memory/arena.h +53 -0
- data/ext/teptris_ext/engine/src/teptris/parse/datetime.c +218 -0
- data/ext/teptris_ext/engine/src/teptris/parse/keys.c +121 -0
- data/ext/teptris_ext/engine/src/teptris/parse/parse.h +76 -0
- data/ext/teptris_ext/engine/src/teptris/parse/parser.c +931 -0
- data/ext/teptris_ext/engine/src/teptris/parse/scalars.c +940 -0
- data/ext/teptris_ext/engine/src/teptris/plan/plan.c +472 -0
- data/ext/teptris_ext/engine/src/teptris/version.c +6 -0
- data/ext/teptris_ext/extconf.rb +61 -0
- data/ext/teptris_ext/materialize.c +608 -0
- data/lib/teptris/version.rb +1 -1
- data/lib/teptris.rb +19 -11
- metadata +35 -3
- /data/lib/{teptris_ext.so → teptris/3.2/teptris_ext.so} +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <stdlib.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
5
|
+
#include "teptris/dom/dom.h"
|
|
6
|
+
#include "teptris/emit/emitter.h"
|
|
7
|
+
#include "teptris/parse/parse.h"
|
|
8
|
+
#include "teptris/teptris.h"
|
|
9
|
+
|
|
10
|
+
teptris_status teptris_parse(const char *data, size_t len,
|
|
11
|
+
const teptris_options *opts,
|
|
12
|
+
teptris_document **out)
|
|
13
|
+
{
|
|
14
|
+
*out = NULL;
|
|
15
|
+
teptris_document *doc = calloc(1, sizeof(teptris_document));
|
|
16
|
+
if (doc == NULL) {
|
|
17
|
+
return TEPTRIS_ERR_ALLOC;
|
|
18
|
+
}
|
|
19
|
+
teptris_arena_init(&doc->arena);
|
|
20
|
+
doc->max_depth = (opts != NULL && opts->max_depth != 0) ? opts->max_depth
|
|
21
|
+
: 512;
|
|
22
|
+
doc->err.status = TEPTRIS_OK;
|
|
23
|
+
doc->err.message = "";
|
|
24
|
+
|
|
25
|
+
teptris_status st = teptris_parser_run(doc, data, len);
|
|
26
|
+
if (st != TEPTRIS_OK && doc->err.status == TEPTRIS_OK) {
|
|
27
|
+
doc->err.status = st;
|
|
28
|
+
if (st == TEPTRIS_ERR_ALLOC) {
|
|
29
|
+
snprintf(doc->err_msg, sizeof(doc->err_msg), "out of memory");
|
|
30
|
+
}
|
|
31
|
+
doc->err.message = doc->err_msg;
|
|
32
|
+
}
|
|
33
|
+
*out = doc;
|
|
34
|
+
return st;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void teptris_document_free(teptris_document *doc)
|
|
38
|
+
{
|
|
39
|
+
if (doc == NULL) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
teptris_arena_destroy(&doc->arena);
|
|
43
|
+
free(doc);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const teptris_error *teptris_document_error(const teptris_document *doc)
|
|
47
|
+
{
|
|
48
|
+
return &doc->err;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const teptris_node *teptris_document_root(const teptris_document *doc)
|
|
52
|
+
{
|
|
53
|
+
if (doc == NULL || doc->err.status != TEPTRIS_OK) {
|
|
54
|
+
return NULL;
|
|
55
|
+
}
|
|
56
|
+
return doc->root;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
teptris_kind teptris_node_kind(const teptris_node *node)
|
|
60
|
+
{
|
|
61
|
+
return (node != NULL) ? (teptris_kind)node->kind : TEPTRIS_STRING;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
teptris_status teptris_node_string(const teptris_node *node, teptris_view *out)
|
|
65
|
+
{
|
|
66
|
+
if (node == NULL || node->kind != TEPTRIS_STRING) {
|
|
67
|
+
return TEPTRIS_ERR_ARG;
|
|
68
|
+
}
|
|
69
|
+
*out = node->as.str;
|
|
70
|
+
return TEPTRIS_OK;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
teptris_status teptris_node_integer(const teptris_node *node, int64_t *out)
|
|
74
|
+
{
|
|
75
|
+
if (node == NULL || node->kind != TEPTRIS_INTEGER) {
|
|
76
|
+
return TEPTRIS_ERR_ARG;
|
|
77
|
+
}
|
|
78
|
+
*out = node->as.i;
|
|
79
|
+
return TEPTRIS_OK;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
teptris_status teptris_node_float(const teptris_node *node, double *out)
|
|
83
|
+
{
|
|
84
|
+
if (node == NULL || node->kind != TEPTRIS_FLOAT) {
|
|
85
|
+
return TEPTRIS_ERR_ARG;
|
|
86
|
+
}
|
|
87
|
+
*out = node->as.f;
|
|
88
|
+
return TEPTRIS_OK;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
teptris_status teptris_node_boolean(const teptris_node *node, bool *out)
|
|
92
|
+
{
|
|
93
|
+
if (node == NULL || node->kind != TEPTRIS_BOOLEAN) {
|
|
94
|
+
return TEPTRIS_ERR_ARG;
|
|
95
|
+
}
|
|
96
|
+
*out = node->as.b;
|
|
97
|
+
return TEPTRIS_OK;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
teptris_status teptris_node_datetime(const teptris_node *node,
|
|
101
|
+
teptris_datetime *out)
|
|
102
|
+
{
|
|
103
|
+
if (node == NULL || node->kind < TEPTRIS_DATETIME_OFFSET ||
|
|
104
|
+
node->kind > TEPTRIS_TIME_LOCAL) {
|
|
105
|
+
return TEPTRIS_ERR_ARG;
|
|
106
|
+
}
|
|
107
|
+
*out = node->as.dt;
|
|
108
|
+
return TEPTRIS_OK;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
size_t teptris_node_array_length(const teptris_node *node)
|
|
112
|
+
{
|
|
113
|
+
return (node != NULL && node->kind == TEPTRIS_ARRAY) ? node->as.array.len
|
|
114
|
+
: 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const teptris_node *teptris_node_array_at(const teptris_node *node,
|
|
118
|
+
size_t index)
|
|
119
|
+
{
|
|
120
|
+
if (node == NULL || node->kind != TEPTRIS_ARRAY ||
|
|
121
|
+
index >= node->as.array.len) {
|
|
122
|
+
return NULL;
|
|
123
|
+
}
|
|
124
|
+
return node->as.array.items[index];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
size_t teptris_node_table_length(const teptris_node *node)
|
|
128
|
+
{
|
|
129
|
+
return (node != NULL && node->kind == TEPTRIS_TABLE) ? node->as.table.len
|
|
130
|
+
: 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const teptris_node *teptris_node_table_at(const teptris_node *node,
|
|
134
|
+
size_t index, teptris_view *key_out)
|
|
135
|
+
{
|
|
136
|
+
if (node == NULL || node->kind != TEPTRIS_TABLE ||
|
|
137
|
+
index >= node->as.table.len) {
|
|
138
|
+
return NULL;
|
|
139
|
+
}
|
|
140
|
+
if (key_out != NULL) {
|
|
141
|
+
*key_out = node->as.table.entries[index].key;
|
|
142
|
+
}
|
|
143
|
+
return node->as.table.entries[index].value;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const teptris_node *teptris_node_table_get(const teptris_node *node,
|
|
147
|
+
const char *key, size_t key_len)
|
|
148
|
+
{
|
|
149
|
+
if (node == NULL || node->kind != TEPTRIS_TABLE || key == NULL) {
|
|
150
|
+
return NULL;
|
|
151
|
+
}
|
|
152
|
+
teptris_entry *e = teptris_dom_table_find(node, key, key_len);
|
|
153
|
+
return (e != NULL) ? e->value : NULL;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
teptris_status teptris_document_emit(const teptris_document *doc, char **buf,
|
|
157
|
+
size_t *len)
|
|
158
|
+
{
|
|
159
|
+
return teptris_emit_document(doc, buf, len);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
teptris_status teptris_document_emit_json(const teptris_document *doc,
|
|
163
|
+
char **buf, size_t *len)
|
|
164
|
+
{
|
|
165
|
+
return teptris_emit_document_json(doc, buf, len);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* --------------------------------------------------- bulk flatten ----- */
|
|
169
|
+
|
|
170
|
+
typedef struct {
|
|
171
|
+
uint8_t *p;
|
|
172
|
+
size_t len, cap;
|
|
173
|
+
teptris_status st;
|
|
174
|
+
} flatbuf;
|
|
175
|
+
|
|
176
|
+
static void fb_grow(flatbuf *b, size_t extra)
|
|
177
|
+
{
|
|
178
|
+
if (b->st != TEPTRIS_OK) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (b->len + extra <= b->cap) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
size_t ncap = b->cap ? b->cap : 512;
|
|
185
|
+
while (b->len + extra > ncap) {
|
|
186
|
+
ncap *= 2;
|
|
187
|
+
}
|
|
188
|
+
uint8_t *np = realloc(b->p, ncap);
|
|
189
|
+
if (np == NULL) {
|
|
190
|
+
free(b->p);
|
|
191
|
+
b->p = NULL;
|
|
192
|
+
b->st = TEPTRIS_ERR_ALLOC;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
b->p = np;
|
|
196
|
+
b->cap = ncap;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static void fb_byte(flatbuf *b, uint8_t v)
|
|
200
|
+
{
|
|
201
|
+
fb_grow(b, 1);
|
|
202
|
+
if (b->st == TEPTRIS_OK) {
|
|
203
|
+
b->p[b->len++] = v;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static void fb_u32(flatbuf *b, uint32_t v)
|
|
208
|
+
{
|
|
209
|
+
fb_grow(b, 4);
|
|
210
|
+
if (b->st == TEPTRIS_OK) {
|
|
211
|
+
b->p[b->len++] = (uint8_t)v;
|
|
212
|
+
b->p[b->len++] = (uint8_t)(v >> 8);
|
|
213
|
+
b->p[b->len++] = (uint8_t)(v >> 16);
|
|
214
|
+
b->p[b->len++] = (uint8_t)(v >> 24);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static void fb_u64(flatbuf *b, uint64_t v)
|
|
219
|
+
{
|
|
220
|
+
for (int i = 0; i < 8; i++) {
|
|
221
|
+
fb_byte(b, (uint8_t)(v >> (8 * i)));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static void fb_i64(flatbuf *b, int64_t v)
|
|
226
|
+
{
|
|
227
|
+
uint64_t u;
|
|
228
|
+
memcpy(&u, &v, 8);
|
|
229
|
+
fb_u64(b, u);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
static void fb_f64(flatbuf *b, double v)
|
|
233
|
+
{
|
|
234
|
+
uint64_t u;
|
|
235
|
+
memcpy(&u, &v, 8);
|
|
236
|
+
fb_u64(b, u);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static void fb_dt(flatbuf *b, const teptris_datetime *d)
|
|
240
|
+
{
|
|
241
|
+
fb_i64(b, d->year);
|
|
242
|
+
fb_byte(b, d->month);
|
|
243
|
+
fb_byte(b, d->day);
|
|
244
|
+
fb_byte(b, d->hour);
|
|
245
|
+
fb_byte(b, d->minute);
|
|
246
|
+
fb_byte(b, d->second);
|
|
247
|
+
fb_u32(b, d->nanosecond);
|
|
248
|
+
fb_i64(b, d->offset_seconds);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
static teptris_status flatten_node(flatbuf *b, const teptris_node *n)
|
|
252
|
+
{
|
|
253
|
+
switch (n->kind) {
|
|
254
|
+
case TEPTRIS_TABLE:
|
|
255
|
+
fb_byte(b, 0x01);
|
|
256
|
+
fb_u32(b, (uint32_t)n->as.table.len);
|
|
257
|
+
for (size_t i = 0; i < n->as.table.len; i++) {
|
|
258
|
+
fb_u32(b, (uint32_t)n->as.table.entries[i].key.len);
|
|
259
|
+
fb_grow(b, n->as.table.entries[i].key.len);
|
|
260
|
+
if (b->st != TEPTRIS_OK) {
|
|
261
|
+
return b->st;
|
|
262
|
+
}
|
|
263
|
+
memcpy(b->p + b->len, n->as.table.entries[i].key.ptr,
|
|
264
|
+
n->as.table.entries[i].key.len);
|
|
265
|
+
b->len += n->as.table.entries[i].key.len;
|
|
266
|
+
teptris_status st = flatten_node(b, n->as.table.entries[i].value);
|
|
267
|
+
if (st != TEPTRIS_OK) {
|
|
268
|
+
return st;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return TEPTRIS_OK;
|
|
272
|
+
case TEPTRIS_ARRAY:
|
|
273
|
+
fb_byte(b, 0x02);
|
|
274
|
+
fb_u32(b, (uint32_t)n->as.array.len);
|
|
275
|
+
for (size_t i = 0; i < n->as.array.len; i++) {
|
|
276
|
+
teptris_status st = flatten_node(b, n->as.array.items[i]);
|
|
277
|
+
if (st != TEPTRIS_OK) {
|
|
278
|
+
return st;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return TEPTRIS_OK;
|
|
282
|
+
case TEPTRIS_STRING:
|
|
283
|
+
fb_byte(b, 0x03);
|
|
284
|
+
fb_u32(b, (uint32_t)n->as.str.len);
|
|
285
|
+
fb_grow(b, n->as.str.len);
|
|
286
|
+
if (b->st != TEPTRIS_OK) {
|
|
287
|
+
return b->st;
|
|
288
|
+
}
|
|
289
|
+
memcpy(b->p + b->len, n->as.str.ptr, n->as.str.len);
|
|
290
|
+
b->len += n->as.str.len;
|
|
291
|
+
return TEPTRIS_OK;
|
|
292
|
+
case TEPTRIS_INTEGER:
|
|
293
|
+
fb_byte(b, 0x04);
|
|
294
|
+
fb_i64(b, n->as.i);
|
|
295
|
+
return TEPTRIS_OK;
|
|
296
|
+
case TEPTRIS_FLOAT:
|
|
297
|
+
fb_byte(b, 0x05);
|
|
298
|
+
fb_f64(b, n->as.f);
|
|
299
|
+
return TEPTRIS_OK;
|
|
300
|
+
case TEPTRIS_BOOLEAN:
|
|
301
|
+
fb_byte(b, n->as.b ? 0x07 : 0x06);
|
|
302
|
+
return TEPTRIS_OK;
|
|
303
|
+
case TEPTRIS_DATETIME_OFFSET:
|
|
304
|
+
case TEPTRIS_DATETIME_LOCAL:
|
|
305
|
+
case TEPTRIS_DATE_LOCAL:
|
|
306
|
+
case TEPTRIS_TIME_LOCAL:
|
|
307
|
+
fb_byte(b, (uint8_t)(0x08 + (n->kind - TEPTRIS_DATETIME_OFFSET)));
|
|
308
|
+
fb_dt(b, &n->as.dt);
|
|
309
|
+
return TEPTRIS_OK;
|
|
310
|
+
default:
|
|
311
|
+
return TEPTRIS_ERR_STATE;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
teptris_status teptris_document_flatten(const teptris_document *doc,
|
|
316
|
+
uint8_t **buf, size_t *len)
|
|
317
|
+
{
|
|
318
|
+
if (doc == NULL || doc->root == NULL || buf == NULL || len == NULL) {
|
|
319
|
+
return TEPTRIS_ERR_ARG;
|
|
320
|
+
}
|
|
321
|
+
*buf = NULL;
|
|
322
|
+
*len = 0;
|
|
323
|
+
flatbuf b = {NULL, 0, 0, TEPTRIS_OK};
|
|
324
|
+
fb_grow(&b, 512);
|
|
325
|
+
teptris_status st = flatten_node(&b, doc->root);
|
|
326
|
+
if (st != TEPTRIS_OK) {
|
|
327
|
+
free(b.p);
|
|
328
|
+
return st;
|
|
329
|
+
}
|
|
330
|
+
*buf = b.p;
|
|
331
|
+
*len = b.len;
|
|
332
|
+
return TEPTRIS_OK;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
void teptris_flatten_free(void *buf)
|
|
336
|
+
{
|
|
337
|
+
free(buf);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const char *teptris_status_string(teptris_status status)
|
|
341
|
+
{
|
|
342
|
+
switch (status) {
|
|
343
|
+
case TEPTRIS_OK:
|
|
344
|
+
return "ok";
|
|
345
|
+
case TEPTRIS_ERR_ALLOC:
|
|
346
|
+
return "out of memory";
|
|
347
|
+
case TEPTRIS_ERR_SYNTAX:
|
|
348
|
+
return "syntax error";
|
|
349
|
+
case TEPTRIS_ERR_SEMANTIC:
|
|
350
|
+
return "semantic error";
|
|
351
|
+
case TEPTRIS_ERR_ENCODING:
|
|
352
|
+
return "invalid encoding";
|
|
353
|
+
case TEPTRIS_ERR_DEPTH:
|
|
354
|
+
return "nesting too deep";
|
|
355
|
+
case TEPTRIS_ERR_ARG:
|
|
356
|
+
return "invalid argument";
|
|
357
|
+
case TEPTRIS_ERR_STATE:
|
|
358
|
+
return "invalid state";
|
|
359
|
+
default:
|
|
360
|
+
return "unknown status";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
#include <stdlib.h>
|
|
2
|
+
#include <string.h>
|
|
3
|
+
|
|
4
|
+
#include "teptris/dom/dom.h"
|
|
5
|
+
#include "teptris/teptris.h"
|
|
6
|
+
|
|
7
|
+
/* Dump-side document construction. The parser assembles nodes from
|
|
8
|
+
* text; the builder assembles them from materialized values so dump
|
|
9
|
+
* consumers share the emitter (single formatting source). */
|
|
10
|
+
|
|
11
|
+
#define BUILDER_MAX_DEPTH 512
|
|
12
|
+
|
|
13
|
+
enum arr_nature {
|
|
14
|
+
ARR_UNDECIDED = 0, /* no elements yet */
|
|
15
|
+
ARR_INLINE, /* scalar/inline elements: renders [..] */
|
|
16
|
+
ARR_AOT_SECTIONS /* table elements: renders [[..]] */
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
typedef struct bframe {
|
|
20
|
+
teptris_node *node;
|
|
21
|
+
bool is_array;
|
|
22
|
+
uint8_t nature; /* arr_nature when is_array */
|
|
23
|
+
} bframe;
|
|
24
|
+
|
|
25
|
+
struct teptris_builder {
|
|
26
|
+
teptris_document *doc; /* NULL once finish() transferred it */
|
|
27
|
+
bframe stack[BUILDER_MAX_DEPTH];
|
|
28
|
+
int depth; /* index of the current container */
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static teptris_status doc_strdup(teptris_document *doc, const char *src,
|
|
32
|
+
size_t len, teptris_view *out)
|
|
33
|
+
{
|
|
34
|
+
char *copy = teptris_arena_alloc(&doc->arena, len + 1);
|
|
35
|
+
if (copy == NULL) {
|
|
36
|
+
return TEPTRIS_ERR_ALLOC;
|
|
37
|
+
}
|
|
38
|
+
memcpy(copy, src, len);
|
|
39
|
+
copy[len] = '\0';
|
|
40
|
+
out->ptr = copy;
|
|
41
|
+
out->len = len;
|
|
42
|
+
return TEPTRIS_OK;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static bool datetime_ok(teptris_kind kind, const teptris_datetime *dt)
|
|
46
|
+
{
|
|
47
|
+
if (dt->hour > 23 || dt->minute > 59 || dt->second > 60 ||
|
|
48
|
+
dt->nanosecond > 999999999 ||
|
|
49
|
+
dt->offset_seconds < -86399 || dt->offset_seconds > 86399) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (kind == TEPTRIS_TIME_LOCAL) {
|
|
53
|
+
/* time of day: no date component at all */
|
|
54
|
+
return dt->year == 0 && dt->month == 0 && dt->day == 0 &&
|
|
55
|
+
dt->offset_seconds == 0;
|
|
56
|
+
}
|
|
57
|
+
if (dt->year > 9999 || dt->month < 1 || dt->month > 12 ||
|
|
58
|
+
dt->day < 1 || dt->day > 31) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (kind == TEPTRIS_DATE_LOCAL &&
|
|
62
|
+
(dt->hour != 0 || dt->minute != 0 || dt->second != 0 ||
|
|
63
|
+
dt->nanosecond != 0)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (kind != TEPTRIS_DATETIME_OFFSET && dt->offset_seconds != 0) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static const bframe *current(const teptris_builder *b)
|
|
73
|
+
{
|
|
74
|
+
return &b->stack[b->depth];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Attach node to the current container: keyed insert into a table, or
|
|
78
|
+
* element push into an array (deciding the array's nature). Returns
|
|
79
|
+
* the attached node for scalar field fills. */
|
|
80
|
+
static teptris_status attach(teptris_builder *b, const char *key,
|
|
81
|
+
size_t key_len, bool element_is_table,
|
|
82
|
+
teptris_node *n, teptris_node **attached)
|
|
83
|
+
{
|
|
84
|
+
const bframe *f = current(b);
|
|
85
|
+
if (f->is_array) {
|
|
86
|
+
if (element_is_table) {
|
|
87
|
+
if (f->nature == ARR_UNDECIDED) {
|
|
88
|
+
/* first element is a table: array of tables */
|
|
89
|
+
b->stack[b->depth].nature = ARR_AOT_SECTIONS;
|
|
90
|
+
f->node->flags |= ARR_AOT;
|
|
91
|
+
}
|
|
92
|
+
/* tables of an inline array render inline (open_table
|
|
93
|
+
* picked the flag from the array's nature) */
|
|
94
|
+
} else if (f->nature == ARR_AOT_SECTIONS) {
|
|
95
|
+
return TEPTRIS_ERR_ARG; /* [[..]] sections cannot mix values */
|
|
96
|
+
} else if (f->nature == ARR_UNDECIDED) {
|
|
97
|
+
b->stack[b->depth].nature = ARR_INLINE;
|
|
98
|
+
}
|
|
99
|
+
teptris_status st = teptris_dom_array_push(b->doc, f->node, n);
|
|
100
|
+
if (st != TEPTRIS_OK) {
|
|
101
|
+
return st;
|
|
102
|
+
}
|
|
103
|
+
*attached = n;
|
|
104
|
+
return TEPTRIS_OK;
|
|
105
|
+
}
|
|
106
|
+
if (key == NULL) {
|
|
107
|
+
return TEPTRIS_ERR_ARG; /* element push requires an array current */
|
|
108
|
+
}
|
|
109
|
+
teptris_view kview;
|
|
110
|
+
teptris_status st = doc_strdup(b->doc, key, key_len, &kview);
|
|
111
|
+
if (st != TEPTRIS_OK) {
|
|
112
|
+
return st;
|
|
113
|
+
}
|
|
114
|
+
uint64_t hash;
|
|
115
|
+
if (teptris_dom_table_find_h(f->node, kview.ptr, kview.len, &hash) != NULL) {
|
|
116
|
+
return TEPTRIS_ERR_ARG; /* duplicate key */
|
|
117
|
+
}
|
|
118
|
+
st = teptris_dom_table_insert_h(b->doc, f->node, kview, hash, n);
|
|
119
|
+
if (st != TEPTRIS_OK) {
|
|
120
|
+
return st;
|
|
121
|
+
}
|
|
122
|
+
*attached = n;
|
|
123
|
+
return TEPTRIS_OK;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static teptris_status put_scalar(teptris_builder *b, const char *key,
|
|
127
|
+
size_t key_len, teptris_kind kind,
|
|
128
|
+
teptris_node **out)
|
|
129
|
+
{
|
|
130
|
+
if (b->doc == NULL) {
|
|
131
|
+
return TEPTRIS_ERR_STATE;
|
|
132
|
+
}
|
|
133
|
+
teptris_node *n = teptris_dom_new_node(b->doc, kind);
|
|
134
|
+
if (n == NULL) {
|
|
135
|
+
return TEPTRIS_ERR_ALLOC;
|
|
136
|
+
}
|
|
137
|
+
return attach(b, key, key_len, false, n, out);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static teptris_status push_container(teptris_builder *b, const char *key,
|
|
141
|
+
size_t key_len, teptris_node *n,
|
|
142
|
+
bool n_is_array)
|
|
143
|
+
{
|
|
144
|
+
teptris_node *attached;
|
|
145
|
+
teptris_status st = attach(b, key, key_len, !n_is_array, n, &attached);
|
|
146
|
+
if (st != TEPTRIS_OK) {
|
|
147
|
+
return st;
|
|
148
|
+
}
|
|
149
|
+
if (b->depth + 1 >= BUILDER_MAX_DEPTH) {
|
|
150
|
+
return TEPTRIS_ERR_DEPTH;
|
|
151
|
+
}
|
|
152
|
+
b->depth++;
|
|
153
|
+
b->stack[b->depth].node = n;
|
|
154
|
+
b->stack[b->depth].is_array = n_is_array;
|
|
155
|
+
b->stack[b->depth].nature = ARR_UNDECIDED;
|
|
156
|
+
return TEPTRIS_OK;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
teptris_builder *teptris_builder_new(void)
|
|
160
|
+
{
|
|
161
|
+
teptris_builder *b = calloc(1, sizeof(teptris_builder));
|
|
162
|
+
if (b == NULL) {
|
|
163
|
+
return NULL;
|
|
164
|
+
}
|
|
165
|
+
teptris_document *doc = calloc(1, sizeof(teptris_document));
|
|
166
|
+
if (doc == NULL) {
|
|
167
|
+
free(b);
|
|
168
|
+
return NULL;
|
|
169
|
+
}
|
|
170
|
+
teptris_arena_init(&doc->arena);
|
|
171
|
+
doc->max_depth = BUILDER_MAX_DEPTH;
|
|
172
|
+
doc->err.status = TEPTRIS_OK;
|
|
173
|
+
doc->err.message = "";
|
|
174
|
+
doc->root = teptris_dom_new_table(doc, TBL_EXPLICIT);
|
|
175
|
+
if (doc->root == NULL) {
|
|
176
|
+
teptris_arena_destroy(&doc->arena);
|
|
177
|
+
free(doc);
|
|
178
|
+
free(b);
|
|
179
|
+
return NULL;
|
|
180
|
+
}
|
|
181
|
+
b->doc = doc;
|
|
182
|
+
b->stack[0].node = doc->root;
|
|
183
|
+
return b;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void teptris_builder_free(teptris_builder *b)
|
|
187
|
+
{
|
|
188
|
+
if (b == NULL) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
teptris_document_free(b->doc); /* NULL after finish() */
|
|
192
|
+
free(b);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
teptris_status teptris_builder_put_string(teptris_builder *b, const char *key,
|
|
196
|
+
size_t key_len, const char *val,
|
|
197
|
+
size_t val_len)
|
|
198
|
+
{
|
|
199
|
+
teptris_node *n;
|
|
200
|
+
teptris_status st = put_scalar(b, key, key_len, TEPTRIS_STRING, &n);
|
|
201
|
+
if (st != TEPTRIS_OK) {
|
|
202
|
+
return st;
|
|
203
|
+
}
|
|
204
|
+
return doc_strdup(b->doc, val, val_len, &n->as.str);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
teptris_status teptris_builder_put_integer(teptris_builder *b, const char *key,
|
|
208
|
+
size_t key_len, int64_t v)
|
|
209
|
+
{
|
|
210
|
+
teptris_node *n;
|
|
211
|
+
teptris_status st = put_scalar(b, key, key_len, TEPTRIS_INTEGER, &n);
|
|
212
|
+
if (st != TEPTRIS_OK) {
|
|
213
|
+
return st;
|
|
214
|
+
}
|
|
215
|
+
n->as.i = v;
|
|
216
|
+
return TEPTRIS_OK;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
teptris_status teptris_builder_put_float(teptris_builder *b, const char *key,
|
|
220
|
+
size_t key_len, double v)
|
|
221
|
+
{
|
|
222
|
+
teptris_node *n;
|
|
223
|
+
teptris_status st = put_scalar(b, key, key_len, TEPTRIS_FLOAT, &n);
|
|
224
|
+
if (st != TEPTRIS_OK) {
|
|
225
|
+
return st;
|
|
226
|
+
}
|
|
227
|
+
n->as.f = v;
|
|
228
|
+
return TEPTRIS_OK;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
teptris_status teptris_builder_put_boolean(teptris_builder *b, const char *key,
|
|
232
|
+
size_t key_len, bool v)
|
|
233
|
+
{
|
|
234
|
+
teptris_node *n;
|
|
235
|
+
teptris_status st = put_scalar(b, key, key_len, TEPTRIS_BOOLEAN, &n);
|
|
236
|
+
if (st != TEPTRIS_OK) {
|
|
237
|
+
return st;
|
|
238
|
+
}
|
|
239
|
+
n->as.b = v;
|
|
240
|
+
return TEPTRIS_OK;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
teptris_status teptris_builder_put_datetime(teptris_builder *b, const char *key,
|
|
244
|
+
size_t key_len, teptris_kind kind,
|
|
245
|
+
const teptris_datetime *dt)
|
|
246
|
+
{
|
|
247
|
+
if (kind != TEPTRIS_DATETIME_OFFSET && kind != TEPTRIS_DATETIME_LOCAL &&
|
|
248
|
+
kind != TEPTRIS_DATE_LOCAL && kind != TEPTRIS_TIME_LOCAL) {
|
|
249
|
+
return TEPTRIS_ERR_ARG;
|
|
250
|
+
}
|
|
251
|
+
if (!datetime_ok(kind, dt)) {
|
|
252
|
+
return TEPTRIS_ERR_ARG;
|
|
253
|
+
}
|
|
254
|
+
teptris_node *n;
|
|
255
|
+
teptris_status st = put_scalar(b, key, key_len, kind, &n);
|
|
256
|
+
if (st != TEPTRIS_OK) {
|
|
257
|
+
return st;
|
|
258
|
+
}
|
|
259
|
+
n->as.dt = *dt;
|
|
260
|
+
return TEPTRIS_OK;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
teptris_status teptris_builder_open_table(teptris_builder *b, const char *key,
|
|
264
|
+
size_t key_len)
|
|
265
|
+
{
|
|
266
|
+
if (b->doc == NULL) {
|
|
267
|
+
return TEPTRIS_ERR_STATE;
|
|
268
|
+
}
|
|
269
|
+
const bframe *f = current(b);
|
|
270
|
+
uint8_t flags;
|
|
271
|
+
if (f->is_array) {
|
|
272
|
+
/* UNDECIDED means this is the first element: attach() turns the
|
|
273
|
+
* array into an AOT and the table renders as a section. */
|
|
274
|
+
flags = (f->nature == ARR_INLINE) ? TBL_INLINE : TBL_EXPLICIT;
|
|
275
|
+
} else if ((f->node->flags & TBL_INLINE) != 0) {
|
|
276
|
+
flags = TBL_INLINE; /* tables inside inline tables stay inline */
|
|
277
|
+
} else {
|
|
278
|
+
flags = TBL_EXPLICIT;
|
|
279
|
+
}
|
|
280
|
+
teptris_node *t = teptris_dom_new_table(b->doc, flags);
|
|
281
|
+
if (t == NULL) {
|
|
282
|
+
return TEPTRIS_ERR_ALLOC;
|
|
283
|
+
}
|
|
284
|
+
return push_container(b, key, key_len, t, false);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
teptris_status teptris_builder_open_array(teptris_builder *b, const char *key,
|
|
288
|
+
size_t key_len)
|
|
289
|
+
{
|
|
290
|
+
if (b->doc == NULL) {
|
|
291
|
+
return TEPTRIS_ERR_STATE;
|
|
292
|
+
}
|
|
293
|
+
teptris_node *a = teptris_dom_new_array(b->doc, 0);
|
|
294
|
+
if (a == NULL) {
|
|
295
|
+
return TEPTRIS_ERR_ALLOC;
|
|
296
|
+
}
|
|
297
|
+
teptris_status st = push_container(b, key, key_len, a, true);
|
|
298
|
+
if (st != TEPTRIS_OK) {
|
|
299
|
+
return st;
|
|
300
|
+
}
|
|
301
|
+
/* arrays in inline territory (inside an inline array or an inline
|
|
302
|
+
* table) can never become [[..]] sections: pin them inline */
|
|
303
|
+
const bframe *parent = &b->stack[b->depth - 1];
|
|
304
|
+
bool inline_territory =
|
|
305
|
+
(parent->is_array && parent->nature == ARR_INLINE) ||
|
|
306
|
+
(!parent->is_array && (parent->node->flags & TBL_INLINE) != 0);
|
|
307
|
+
if (inline_territory) {
|
|
308
|
+
b->stack[b->depth].nature = ARR_INLINE;
|
|
309
|
+
}
|
|
310
|
+
return TEPTRIS_OK;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
teptris_status teptris_builder_open_inline_array(teptris_builder *b,
|
|
314
|
+
const char *key,
|
|
315
|
+
size_t key_len)
|
|
316
|
+
{
|
|
317
|
+
if (b->doc == NULL) {
|
|
318
|
+
return TEPTRIS_ERR_STATE;
|
|
319
|
+
}
|
|
320
|
+
teptris_node *a = teptris_dom_new_array(b->doc, 0);
|
|
321
|
+
if (a == NULL) {
|
|
322
|
+
return TEPTRIS_ERR_ALLOC;
|
|
323
|
+
}
|
|
324
|
+
teptris_status st = push_container(b, key, key_len, a, true);
|
|
325
|
+
if (st != TEPTRIS_OK) {
|
|
326
|
+
return st;
|
|
327
|
+
}
|
|
328
|
+
/* explicit inline mode: caller-side lookahead knows the array mixes
|
|
329
|
+
* tables with values (or wants [..] rendering) */
|
|
330
|
+
b->stack[b->depth].nature = ARR_INLINE;
|
|
331
|
+
return TEPTRIS_OK;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
teptris_status teptris_builder_close(teptris_builder *b)
|
|
335
|
+
{
|
|
336
|
+
if (b->doc == NULL || b->depth == 0) {
|
|
337
|
+
return TEPTRIS_ERR_STATE;
|
|
338
|
+
}
|
|
339
|
+
b->depth--;
|
|
340
|
+
return TEPTRIS_OK;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
teptris_status teptris_builder_finish(teptris_builder *b, teptris_document **out)
|
|
344
|
+
{
|
|
345
|
+
*out = NULL;
|
|
346
|
+
if (b->doc == NULL) {
|
|
347
|
+
return TEPTRIS_ERR_STATE;
|
|
348
|
+
}
|
|
349
|
+
if (b->depth != 0) {
|
|
350
|
+
return TEPTRIS_ERR_STATE; /* unclosed containers */
|
|
351
|
+
}
|
|
352
|
+
*out = b->doc;
|
|
353
|
+
b->doc = NULL;
|
|
354
|
+
return TEPTRIS_OK;
|
|
355
|
+
}
|