teptris 0.2.37-aarch64-linux → 0.2.39-aarch64-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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/ext/teptris_ext/engine/LICENSE.md +21 -0
  3. data/ext/teptris_ext/engine/src/include/teptris/plan.h +123 -0
  4. data/ext/teptris_ext/engine/src/include/teptris/teptris.h +208 -0
  5. data/ext/teptris_ext/engine/src/include/teptris/version.h +9 -0
  6. data/ext/teptris_ext/engine/src/teptris/api.c +362 -0
  7. data/ext/teptris_ext/engine/src/teptris/build.c +355 -0
  8. data/ext/teptris_ext/engine/src/teptris/common/chartype.c +70 -0
  9. data/ext/teptris_ext/engine/src/teptris/common/chartype.h +41 -0
  10. data/ext/teptris_ext/engine/src/teptris/common/ryu/common.h +114 -0
  11. data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s.c +496 -0
  12. data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_full_table.h +367 -0
  13. data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_intrinsics.h +357 -0
  14. data/ext/teptris_ext/engine/src/teptris/common/ryu/digit_table.h +35 -0
  15. data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_d2s.h +39 -0
  16. data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_parse.h +45 -0
  17. data/ext/teptris_ext/engine/src/teptris/common/ryu/s2d.c +259 -0
  18. data/ext/teptris_ext/engine/src/teptris/common/simd.h +119 -0
  19. data/ext/teptris_ext/engine/src/teptris/dom/dom.c +192 -0
  20. data/ext/teptris_ext/engine/src/teptris/dom/dom.h +124 -0
  21. data/ext/teptris_ext/engine/src/teptris/emit/emitter.c +718 -0
  22. data/ext/teptris_ext/engine/src/teptris/emit/emitter.h +11 -0
  23. data/ext/teptris_ext/engine/src/teptris/memory/arena.c +104 -0
  24. data/ext/teptris_ext/engine/src/teptris/memory/arena.h +53 -0
  25. data/ext/teptris_ext/engine/src/teptris/parse/datetime.c +218 -0
  26. data/ext/teptris_ext/engine/src/teptris/parse/keys.c +121 -0
  27. data/ext/teptris_ext/engine/src/teptris/parse/parse.h +76 -0
  28. data/ext/teptris_ext/engine/src/teptris/parse/parser.c +931 -0
  29. data/ext/teptris_ext/engine/src/teptris/parse/scalars.c +940 -0
  30. data/ext/teptris_ext/engine/src/teptris/plan/plan.c +472 -0
  31. data/ext/teptris_ext/engine/src/teptris/version.c +6 -0
  32. data/ext/teptris_ext/extconf.rb +61 -0
  33. data/ext/teptris_ext/materialize.c +608 -0
  34. data/lib/{teptris_ext.so → teptris/3.3/teptris_ext.so} +0 -0
  35. data/lib/teptris/3.4/teptris_ext.so +0 -0
  36. data/lib/teptris/4.0/teptris_ext.so +0 -0
  37. data/lib/teptris/version.rb +1 -1
  38. data/lib/teptris.rb +19 -11
  39. metadata +37 -3
@@ -0,0 +1,718 @@
1
+ #include "teptris/emit/emitter.h"
2
+
3
+ #include <math.h>
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+ #include <string.h>
7
+
8
+ #include "teptris/common/chartype.h"
9
+ #include "teptris/common/ryu/ryu_d2s.h"
10
+ #include "teptris/dom/dom.h"
11
+
12
+ typedef struct {
13
+ char *p;
14
+ size_t len, cap;
15
+ teptris_status st;
16
+ } ebuf;
17
+
18
+ static void eb_reserve(ebuf *b, size_t extra)
19
+ {
20
+ if (b->st != TEPTRIS_OK) {
21
+ return;
22
+ }
23
+ if (b->len + extra + 1 <= b->cap) {
24
+ return;
25
+ }
26
+ size_t ncap = b->cap ? b->cap : 256;
27
+ while (b->len + extra + 1 > ncap) {
28
+ ncap *= 2;
29
+ }
30
+ char *np = realloc(b->p, ncap);
31
+ if (np == NULL) {
32
+ free(b->p);
33
+ b->p = NULL;
34
+ b->st = TEPTRIS_ERR_ALLOC;
35
+ return;
36
+ }
37
+ b->p = np;
38
+ b->cap = ncap;
39
+ }
40
+
41
+ static void eb_put(ebuf *b, const char *s, size_t n)
42
+ {
43
+ if (b->st != TEPTRIS_OK) {
44
+ return;
45
+ }
46
+ if (b->len + n + 1 <= b->cap) {
47
+ memcpy(b->p + b->len, s, n);
48
+ b->len += n;
49
+ return;
50
+ }
51
+ eb_reserve(b, n);
52
+ if (b->st != TEPTRIS_OK) {
53
+ return;
54
+ }
55
+ memcpy(b->p + b->len, s, n);
56
+ b->len += n;
57
+ }
58
+
59
+ static void eb_c(ebuf *b, char c)
60
+ {
61
+ if (b->st == TEPTRIS_OK && b->len + 2 <= b->cap) {
62
+ b->p[b->len++] = c;
63
+ return;
64
+ }
65
+ eb_put(b, &c, 1);
66
+ }
67
+
68
+ /* ", " — the element/entry separator — as one direct 2-byte write;
69
+ * the densest loops pay it per element */
70
+ static void eb_sep2(ebuf *b)
71
+ {
72
+ if (b->st == TEPTRIS_OK && b->len + 3 <= b->cap) {
73
+ b->p[b->len] = ',';
74
+ b->p[b->len + 1] = ' ';
75
+ b->len += 2;
76
+ return;
77
+ }
78
+ eb_put(b, ", ", 2);
79
+ }
80
+
81
+ static void eb_str(ebuf *b, const char *s)
82
+ {
83
+ eb_put(b, s, strlen(s));
84
+ }
85
+
86
+ /* ------------------------------------------------------------- scalars -- */
87
+
88
+ /* direct writer, shared by both emit views: snprintf costs a
89
+ * format-parse + locale check per number */
90
+ static inline void emit_i64(ebuf *b, int64_t v)
91
+ {
92
+ uint64_t mag;
93
+ bool neg = v < 0;
94
+ if (neg) {
95
+ mag = (uint64_t)(-(v + 1)) + 1; /* INT64_MIN safe */
96
+ } else {
97
+ mag = (uint64_t)v;
98
+ }
99
+ char tmp[32];
100
+ char *q = tmp + sizeof(tmp);
101
+ *--q = '\0';
102
+ do {
103
+ *--q = (char)('0' + (mag % 10));
104
+ mag /= 10;
105
+ } while (mag != 0);
106
+ if (neg) {
107
+ *--q = '-';
108
+ }
109
+ eb_put(b, q, (size_t)(tmp + sizeof(tmp) - 1 - q));
110
+ }
111
+
112
+ static void emit_float(ebuf *b, double f)
113
+ {
114
+ if (isnan(f)) {
115
+ eb_str(b, "nan");
116
+ return;
117
+ }
118
+ if (isinf(f)) {
119
+ eb_str(b, f < 0 ? "-inf" : "inf");
120
+ return;
121
+ }
122
+ /* ryu shortest round-trip digits ("D[.DD]E[X]"), re-rendered in TOML
123
+ * notation: fixed-point when compact, scientific otherwise. */
124
+ char tmp[32];
125
+ int n = teptris_ryu_d2s_buffered_n(f, tmp);
126
+ /* the _n ryu variant does NOT terminate: bound every later read */
127
+ tmp[n] = '\0';
128
+
129
+ size_t i = 0;
130
+ bool neg = false;
131
+ if (tmp[0] == '-') {
132
+ neg = true;
133
+ i = 1;
134
+ }
135
+ char digits[24];
136
+ size_t nd = 0;
137
+ for (; i < (size_t)n && tmp[i] != 'E'; i++) {
138
+ if (tmp[i] != '.') {
139
+ digits[nd++] = tmp[i];
140
+ }
141
+ }
142
+ long sci_exp = (i < (size_t)n) ? strtol(tmp + i + 1, NULL, 10) : 0;
143
+ long e10 = sci_exp - (long)nd + 1; /* value = digits * 10^e10 */
144
+
145
+ char out[44];
146
+ size_t o = 0;
147
+ if (neg) {
148
+ out[o++] = '-';
149
+ }
150
+ if (e10 >= 0 && e10 + (long)nd <= 16) {
151
+ memcpy(out + o, digits, nd);
152
+ o += nd;
153
+ for (long k = 0; k < e10; k++) {
154
+ out[o++] = '0';
155
+ }
156
+ out[o++] = '.';
157
+ out[o++] = '0';
158
+ } else if (e10 < 0 && -e10 <= (long)nd) {
159
+ long ip = (long)nd + e10;
160
+ if (ip > 0) {
161
+ memcpy(out + o, digits, (size_t)ip);
162
+ o += (size_t)ip;
163
+ out[o++] = '.';
164
+ memcpy(out + o, digits + ip, nd - (size_t)ip);
165
+ o += nd - (size_t)ip;
166
+ } else {
167
+ out[o++] = '0';
168
+ out[o++] = '.';
169
+ memcpy(out + o, digits, nd);
170
+ o += nd;
171
+ }
172
+ } else if (e10 < 0 && -e10 - (long)nd <= 6) {
173
+ out[o++] = '0';
174
+ out[o++] = '.';
175
+ for (long k = 0; k < -e10 - (long)nd; k++) {
176
+ out[o++] = '0';
177
+ }
178
+ memcpy(out + o, digits, nd);
179
+ o += nd;
180
+ } else {
181
+ out[o++] = digits[0];
182
+ if (nd > 1) {
183
+ out[o++] = '.';
184
+ memcpy(out + o, digits + 1, nd - 1);
185
+ o += nd - 1;
186
+ }
187
+ o += (size_t)snprintf(out + o, sizeof(out) - o, "e%ld", sci_exp);
188
+ }
189
+ eb_put(b, out, o);
190
+ }
191
+
192
+ static size_t put_digits2(char *p, unsigned v)
193
+ {
194
+ p[0] = (char)('0' + (v / 10));
195
+ p[1] = (char)('0' + (v % 10));
196
+ return 2;
197
+ }
198
+
199
+ static size_t put_digits4(char *p, unsigned v)
200
+ {
201
+ put_digits2(p, v / 100);
202
+ put_digits2(p + 2, v % 100);
203
+ return 4;
204
+ }
205
+
206
+ static void emit_dt(ebuf *b, const teptris_datetime *dt, teptris_kind k)
207
+ {
208
+ char tmp[64];
209
+ size_t n = 0;
210
+ if (k != TEPTRIS_TIME_LOCAL) {
211
+ n += put_digits4(tmp + n, (unsigned)dt->year);
212
+ tmp[n++] = '-';
213
+ n += put_digits2(tmp + n, dt->month);
214
+ tmp[n++] = '-';
215
+ n += put_digits2(tmp + n, dt->day);
216
+ if (k != TEPTRIS_DATE_LOCAL) {
217
+ tmp[n++] = 'T';
218
+ }
219
+ }
220
+ if (k != TEPTRIS_DATE_LOCAL) {
221
+ n += put_digits2(tmp + n, dt->hour);
222
+ tmp[n++] = ':';
223
+ n += put_digits2(tmp + n, dt->minute);
224
+ tmp[n++] = ':';
225
+ n += put_digits2(tmp + n, dt->second);
226
+ }
227
+ if (dt->nanosecond != 0 && k != TEPTRIS_DATE_LOCAL) {
228
+ tmp[n++] = '.';
229
+ unsigned v = dt->nanosecond;
230
+ for (int i = 8; i >= 0; i--) {
231
+ tmp[n + (size_t)i] = (char)('0' + v % 10);
232
+ v /= 10;
233
+ }
234
+ n += 9;
235
+ while (n > 0 && tmp[n - 1] == '0') {
236
+ n--; /* trim trailing fraction zeros, keep at least one digit */
237
+ }
238
+ }
239
+ if (k == TEPTRIS_DATETIME_OFFSET) {
240
+ int32_t off = dt->offset_seconds;
241
+ if (off == 0) {
242
+ tmp[n++] = 'Z';
243
+ } else {
244
+ int32_t a = (off < 0) ? -off : off;
245
+ tmp[n++] = (off < 0) ? '-' : '+';
246
+ n += put_digits2(tmp + n, (unsigned)(a / 3600));
247
+ tmp[n++] = ':';
248
+ n += put_digits2(tmp + n, (unsigned)((a % 3600) / 60));
249
+ }
250
+ }
251
+ eb_put(b, tmp, n);
252
+ }
253
+
254
+ static void emit_basic_bytes(ebuf *b, const char *s, size_t n)
255
+ {
256
+ eb_c(b, '"');
257
+ size_t i = 0;
258
+ while (i < n) {
259
+ /* span-scan to the next byte needing escape; the clean run
260
+ * goes out as one eb_put instead of per-byte eb_c */
261
+ size_t start = i;
262
+ while (i < n) {
263
+ unsigned char c = (unsigned char)s[i];
264
+ if (c == '"' || c == '\\' || c < 0x20 || c == 0x7F) {
265
+ break;
266
+ }
267
+ i++;
268
+ }
269
+ eb_put(b, s + start, i - start);
270
+ if (i == n) {
271
+ break;
272
+ }
273
+ unsigned char c = (unsigned char)s[i++];
274
+ switch (c) {
275
+ case '"':
276
+ eb_str(b, "\\\"");
277
+ break;
278
+ case '\\':
279
+ eb_str(b, "\\\\");
280
+ break;
281
+ case '\b':
282
+ eb_str(b, "\\b");
283
+ break;
284
+ case '\t':
285
+ eb_str(b, "\\t");
286
+ break;
287
+ case '\n':
288
+ eb_str(b, "\\n");
289
+ break;
290
+ case '\f':
291
+ eb_str(b, "\\f");
292
+ break;
293
+ case '\r':
294
+ eb_str(b, "\\r");
295
+ break;
296
+ default:
297
+ if (c < 0x20 || c == 0x7F) {
298
+ char esc[8];
299
+ snprintf(esc, sizeof(esc), "\\u%04X", c);
300
+ eb_str(b, esc);
301
+ }
302
+ }
303
+ }
304
+ eb_c(b, '"');
305
+ }
306
+
307
+ static void emit_string_value(ebuf *b, teptris_view s)
308
+ {
309
+ bool literal = true;
310
+ for (size_t i = 0; i < s.len; i++) {
311
+ unsigned char c = (unsigned char)s.ptr[i];
312
+ if (c == '\'' || (c < 0x20 && c != '\t') || c == 0x7F) {
313
+ literal = false;
314
+ break;
315
+ }
316
+ }
317
+ if (literal) {
318
+ eb_c(b, '\'');
319
+ eb_put(b, s.ptr, s.len);
320
+ eb_c(b, '\'');
321
+ return;
322
+ }
323
+ emit_basic_bytes(b, s.ptr, s.len);
324
+ }
325
+
326
+ /* Worst-case quoted-key length: 6 bytes per input byte (\u00XX) + 2 quotes. */
327
+ static size_t key_buf_cap(teptris_view key)
328
+ {
329
+ return key.len * 6 + 4;
330
+ }
331
+
332
+ static size_t key_to_buf(char *dst, teptris_view key)
333
+ {
334
+ bool bare = key.len > 0;
335
+ for (size_t i = 0; i < key.len; i++) {
336
+ if (!tep_is_barekey((unsigned char)key.ptr[i])) {
337
+ bare = false;
338
+ break;
339
+ }
340
+ }
341
+ if (bare) {
342
+ memcpy(dst, key.ptr, key.len);
343
+ return key.len;
344
+ }
345
+ bool literal = true;
346
+ for (size_t i = 0; i < key.len; i++) {
347
+ unsigned char c = (unsigned char)key.ptr[i];
348
+ if (c == '\'' || (c < 0x20 && c != '\t') || c == 0x7F) {
349
+ literal = false;
350
+ break;
351
+ }
352
+ }
353
+ size_t n = 0;
354
+ if (literal) {
355
+ dst[n++] = '\'';
356
+ memcpy(dst + n, key.ptr, key.len);
357
+ n += key.len;
358
+ dst[n++] = '\'';
359
+ return n;
360
+ }
361
+ dst[n++] = '"';
362
+ for (size_t i = 0; i < key.len; i++) {
363
+ unsigned char c = (unsigned char)key.ptr[i];
364
+ switch (c) {
365
+ case '"':
366
+ dst[n++] = '\\';
367
+ dst[n++] = '"';
368
+ break;
369
+ case '\\':
370
+ dst[n++] = '\\';
371
+ dst[n++] = '\\';
372
+ break;
373
+ default:
374
+ if (c < 0x20) {
375
+ static const char hex[] = "0123456789ABCDEF";
376
+ dst[n++] = '\\';
377
+ dst[n++] = 'u';
378
+ dst[n++] = '0';
379
+ dst[n++] = '0';
380
+ dst[n++] = hex[c >> 4];
381
+ dst[n++] = hex[c & 0xF];
382
+ } else {
383
+ dst[n++] = (char)c;
384
+ }
385
+ }
386
+ }
387
+ dst[n++] = '"';
388
+ return n;
389
+ }
390
+
391
+ static void emit_key(ebuf *b, teptris_view key)
392
+ {
393
+ /* keys <= ~42 bytes (every bare key, nearly all quoted) never
394
+ * touch the allocator */
395
+ char stack[256];
396
+ if (key_buf_cap(key) <= sizeof(stack)) {
397
+ size_t n = key_to_buf(stack, key);
398
+ eb_put(b, stack, n);
399
+ return;
400
+ }
401
+ char *ks = malloc(key_buf_cap(key));
402
+ if (ks == NULL) {
403
+ b->st = TEPTRIS_ERR_ALLOC;
404
+ return;
405
+ }
406
+ size_t n = key_to_buf(ks, key);
407
+ eb_put(b, ks, n);
408
+ free(ks);
409
+ }
410
+
411
+ /* --------------------------------------------------------------- values -- */
412
+
413
+ static void emit_inline_table(ebuf *b, const teptris_node *t);
414
+
415
+ static void emit_value(ebuf *b, const teptris_node *n)
416
+ {
417
+ switch (n->kind) {
418
+ case TEPTRIS_STRING:
419
+ emit_string_value(b, n->as.str);
420
+ break;
421
+ case TEPTRIS_INTEGER:
422
+ emit_i64(b, n->as.i);
423
+ break;
424
+ case TEPTRIS_FLOAT:
425
+ emit_float(b, n->as.f);
426
+ break;
427
+ case TEPTRIS_BOOLEAN:
428
+ eb_str(b, n->as.b ? "true" : "false");
429
+ break;
430
+ case TEPTRIS_DATETIME_OFFSET:
431
+ case TEPTRIS_DATETIME_LOCAL:
432
+ case TEPTRIS_DATE_LOCAL:
433
+ case TEPTRIS_TIME_LOCAL:
434
+ emit_dt(b, &n->as.dt, (teptris_kind)n->kind);
435
+ break;
436
+ case TEPTRIS_ARRAY: {
437
+ /* first element hoisted: no per-element separator branch in
438
+ * the loop (arrays are the element-densest shape) */
439
+ size_t alen = n->as.array.len;
440
+ eb_c(b, '[');
441
+ if (alen > 0) {
442
+ emit_value(b, n->as.array.items[0]);
443
+ for (size_t i = 1; i < alen; i++) {
444
+ eb_sep2(b);
445
+ emit_value(b, n->as.array.items[i]);
446
+ }
447
+ }
448
+ eb_c(b, ']');
449
+ break;
450
+ }
451
+ case TEPTRIS_TABLE:
452
+ emit_inline_table(b, n);
453
+ break;
454
+ default:
455
+ break;
456
+ }
457
+ }
458
+
459
+ static void emit_inline_table(ebuf *b, const teptris_node *t)
460
+ {
461
+ eb_c(b, '{');
462
+ for (size_t i = 0; i < t->as.table.len; i++) {
463
+ if (i > 0) {
464
+ eb_str(b, ", ");
465
+ }
466
+ emit_key(b, t->as.table.entries[i].key);
467
+ eb_str(b, " = ");
468
+ emit_value(b, t->as.table.entries[i].value);
469
+ }
470
+ eb_c(b, '}');
471
+ }
472
+
473
+ /* ------------------------------------------------------------- sections -- */
474
+
475
+ static bool is_section(const teptris_node *v)
476
+ {
477
+ if (v->kind == TEPTRIS_TABLE) {
478
+ return (v->flags & TBL_INLINE) == 0;
479
+ }
480
+ if (v->kind == TEPTRIS_ARRAY) {
481
+ return (v->flags & ARR_AOT) != 0;
482
+ }
483
+ return false;
484
+ }
485
+
486
+ static void emit_table_children(ebuf *b, const char *prefix, size_t plen,
487
+ const teptris_node *t);
488
+
489
+ static void emit_section(ebuf *b, const char *prefix, size_t plen,
490
+ teptris_view key, const teptris_node *n)
491
+ {
492
+ /* one stack buffer covers ordinary section paths (and the key is
493
+ * rendered straight into it — one copy fewer than the old
494
+ * ks + hdr pair) */
495
+ char stack[512];
496
+ size_t hcap = plen + key_buf_cap(key) + 2;
497
+ char *hdr;
498
+ if (hcap <= sizeof(stack)) {
499
+ hdr = stack;
500
+ } else {
501
+ hdr = malloc(hcap);
502
+ if (hdr == NULL) {
503
+ b->st = TEPTRIS_ERR_ALLOC;
504
+ return;
505
+ }
506
+ }
507
+ size_t hl = 0;
508
+ if (plen > 0) {
509
+ memcpy(hdr, prefix, plen);
510
+ hl = plen;
511
+ hdr[hl++] = '.';
512
+ }
513
+ hl += key_to_buf(hdr + hl, key);
514
+
515
+ if (n->kind == TEPTRIS_ARRAY) { /* array of tables */
516
+ for (size_t i = 0; i < n->as.array.len; i++) {
517
+ eb_str(b, "[[");
518
+ eb_put(b, hdr, hl);
519
+ eb_str(b, "]]\n");
520
+ emit_table_children(b, hdr, hl, n->as.array.items[i]);
521
+ }
522
+ } else {
523
+ eb_c(b, '[');
524
+ eb_put(b, hdr, hl);
525
+ eb_str(b, "]\n");
526
+ emit_table_children(b, hdr, hl, n);
527
+ }
528
+ if (hdr != stack) {
529
+ free(hdr);
530
+ }
531
+ }
532
+
533
+ static void emit_table_children(ebuf *b, const char *prefix, size_t plen,
534
+ const teptris_node *t)
535
+ {
536
+ for (size_t i = 0; i < t->as.table.len; i++) {
537
+ const teptris_node *v = t->as.table.entries[i].value;
538
+ if (!is_section(v)) {
539
+ emit_key(b, t->as.table.entries[i].key);
540
+ eb_str(b, " = ");
541
+ emit_value(b, v);
542
+ eb_c(b, '\n');
543
+ }
544
+ }
545
+ for (size_t i = 0; i < t->as.table.len; i++) {
546
+ const teptris_node *v = t->as.table.entries[i].value;
547
+ if (is_section(v)) {
548
+ emit_section(b, prefix, plen, t->as.table.entries[i].key, v);
549
+ }
550
+ }
551
+ }
552
+
553
+ teptris_status teptris_emit_document(const teptris_document *doc, char **buf,
554
+ size_t *len)
555
+ {
556
+ if (doc == NULL || doc->root == NULL) {
557
+ return TEPTRIS_ERR_ARG;
558
+ }
559
+ ebuf b = {0, 0, 0, TEPTRIS_OK};
560
+ eb_reserve(&b, 256);
561
+ emit_table_children(&b, NULL, 0, doc->root);
562
+ if (b.st != TEPTRIS_OK) {
563
+ return b.st;
564
+ }
565
+ eb_c(&b, '\0');
566
+ *buf = b.p;
567
+ *len = b.len - 1;
568
+ return TEPTRIS_OK;
569
+ }
570
+
571
+ /* ----------------------------------------------------------- typed JSON -- */
572
+
573
+ static void json_string(ebuf *b, teptris_view s)
574
+ {
575
+ eb_c(b, '"');
576
+ size_t i = 0;
577
+ while (i < s.len) {
578
+ size_t start = i;
579
+ while (i < s.len) {
580
+ unsigned char c = (unsigned char)s.ptr[i];
581
+ if (c == '"' || c == '\\' || c < 0x20) {
582
+ break;
583
+ }
584
+ i++;
585
+ }
586
+ eb_put(b, s.ptr + start, i - start);
587
+ if (i == s.len) {
588
+ break;
589
+ }
590
+ unsigned char c = (unsigned char)s.ptr[i++];
591
+ switch (c) {
592
+ case '"':
593
+ eb_str(b, "\\\"");
594
+ break;
595
+ case '\\':
596
+ eb_str(b, "\\\\");
597
+ break;
598
+ case '\b':
599
+ eb_str(b, "\\b");
600
+ break;
601
+ case '\t':
602
+ eb_str(b, "\\t");
603
+ break;
604
+ case '\n':
605
+ eb_str(b, "\\n");
606
+ break;
607
+ case '\f':
608
+ eb_str(b, "\\f");
609
+ break;
610
+ case '\r':
611
+ eb_str(b, "\\r");
612
+ break;
613
+ default:
614
+ if (c < 0x20) {
615
+ char esc[8];
616
+ snprintf(esc, sizeof(esc), "\\u%04X", c);
617
+ eb_str(b, esc);
618
+ }
619
+ }
620
+ }
621
+ eb_c(b, '"');
622
+ }
623
+
624
+ static const char *json_type_name(teptris_kind k)
625
+ {
626
+ switch (k) {
627
+ case TEPTRIS_STRING:
628
+ return "string";
629
+ case TEPTRIS_INTEGER:
630
+ return "integer";
631
+ case TEPTRIS_FLOAT:
632
+ return "float";
633
+ case TEPTRIS_BOOLEAN:
634
+ return "bool";
635
+ case TEPTRIS_DATETIME_OFFSET:
636
+ return "datetime";
637
+ case TEPTRIS_DATETIME_LOCAL:
638
+ return "datetime-local";
639
+ case TEPTRIS_DATE_LOCAL:
640
+ return "date-local";
641
+ case TEPTRIS_TIME_LOCAL:
642
+ return "time-local";
643
+ default:
644
+ return "";
645
+ }
646
+ }
647
+
648
+ static void emit_json_value(ebuf *b, const teptris_node *n)
649
+ {
650
+ switch (n->kind) {
651
+ case TEPTRIS_TABLE:
652
+ eb_c(b, '{');
653
+ for (size_t i = 0; i < n->as.table.len; i++) {
654
+ if (i > 0) {
655
+ eb_c(b, ',');
656
+ }
657
+ json_string(b, n->as.table.entries[i].key);
658
+ eb_c(b, ':');
659
+ emit_json_value(b, n->as.table.entries[i].value);
660
+ }
661
+ eb_c(b, '}');
662
+ break;
663
+ case TEPTRIS_ARRAY:
664
+ eb_c(b, '[');
665
+ for (size_t i = 0; i < n->as.array.len; i++) {
666
+ if (i > 0) {
667
+ eb_c(b, ',');
668
+ }
669
+ emit_json_value(b, n->as.array.items[i]);
670
+ }
671
+ eb_c(b, ']');
672
+ break;
673
+ case TEPTRIS_STRING:
674
+ eb_str(b, "{\"type\":\"string\",\"value\":");
675
+ json_string(b, n->as.str);
676
+ eb_c(b, '}');
677
+ break;
678
+ case TEPTRIS_INTEGER:
679
+ eb_str(b, "{\"type\":\"integer\",\"value\":\"");
680
+ emit_i64(b, n->as.i);
681
+ eb_str(b, "\"}");
682
+ break;
683
+ case TEPTRIS_FLOAT:
684
+ eb_str(b, "{\"type\":\"float\",\"value\":\"");
685
+ emit_float(b, n->as.f);
686
+ eb_str(b, "\"}");
687
+ break;
688
+ case TEPTRIS_BOOLEAN:
689
+ eb_str(b, "{\"type\":\"bool\",\"value\":\"");
690
+ eb_str(b, n->as.b ? "true" : "false");
691
+ eb_str(b, "\"}");
692
+ break;
693
+ default:
694
+ eb_str(b, "{\"type\":\"");
695
+ eb_str(b, json_type_name((teptris_kind)n->kind));
696
+ eb_str(b, "\",\"value\":\"");
697
+ emit_dt(b, &n->as.dt, (teptris_kind)n->kind);
698
+ eb_str(b, "\"}");
699
+ }
700
+ }
701
+
702
+ teptris_status teptris_emit_document_json(const teptris_document *doc,
703
+ char **buf, size_t *len)
704
+ {
705
+ if (doc == NULL || doc->root == NULL) {
706
+ return TEPTRIS_ERR_ARG;
707
+ }
708
+ ebuf b = {0, 0, 0, TEPTRIS_OK};
709
+ eb_reserve(&b, 256);
710
+ emit_json_value(&b, doc->root);
711
+ if (b.st != TEPTRIS_OK) {
712
+ return b.st;
713
+ }
714
+ eb_c(&b, '\0');
715
+ *buf = b.p;
716
+ *len = b.len - 1;
717
+ return TEPTRIS_OK;
718
+ }