iodine 0.8.2 → 0.8.3
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/CHANGELOG.md +12 -0
- data/ext/iodine/fio-stl.h +6101 -3276
- data/ext/iodine/fio-stl.md +5155 -3213
- data/ext/iodine/iodine.c +1 -0
- data/ext/iodine/iodine.h +9 -4
- data/ext/iodine/iodine_connection.h +20 -1
- data/ext/iodine/iodine_json.h +469 -160
- data/ext/iodine/iodine_params.h +1362 -0
- data/lib/iodine/documentation.rb +121 -0
- data/lib/iodine/version.rb +2 -1
- metadata +3 -2
data/ext/iodine/iodine_json.h
CHANGED
|
@@ -33,7 +33,268 @@ Ruby API (Iodine::JSON):
|
|
|
33
33
|
JSON Stringifier - Ruby to JSON String Conversion
|
|
34
34
|
***************************************************************************** */
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/** Minimal-overhead fio_bstr writer for the JSON stringifier.
|
|
37
|
+
*
|
|
38
|
+
* Mirrors the bstr's len/capa, so the hot path is an inline capacity check
|
|
39
|
+
* followed by a raw write - avoiding the per-call overhead of the generic
|
|
40
|
+
* fio_bstr_write API (which validates capacity on every single write).
|
|
41
|
+
* Growth goes through fio_bstr_reserve with added slack (capa/4) for
|
|
42
|
+
* geometric, amortized O(1) appends.
|
|
43
|
+
*
|
|
44
|
+
* NOTE: all writes MUST go through these helpers - len/capa are mirrors.
|
|
45
|
+
*/
|
|
46
|
+
typedef struct {
|
|
47
|
+
char *s; /* the fio_bstr (may be NULL) */
|
|
48
|
+
size_t len; /* mirrors fio_bstr_len(s) */
|
|
49
|
+
size_t capa; /* mirrors fio_bstr_info(s).capa (0 when s is NULL) */
|
|
50
|
+
} iodine_json_w_s;
|
|
51
|
+
|
|
52
|
+
/** Ensures room for `add` more bytes (plus the NUL terminator).
|
|
53
|
+
* Returns non-zero on success, zero on allocation failure (write skipped,
|
|
54
|
+
* truncating the output - same semantics as fio_bstr_write). */
|
|
55
|
+
FIO_IFUNC int iodine_json_w_need(iodine_json_w_s *w, size_t add) {
|
|
56
|
+
if (FIO_LIKELY(w->capa - w->len > add))
|
|
57
|
+
return 1;
|
|
58
|
+
/* sync the bstr metadata (our len is a mirror) before reserve reads it */
|
|
59
|
+
w->s = fio_bstr_len_set(w->s, w->len);
|
|
60
|
+
/* grow with geometric slack (capa/4) - amortized O(1) appends */
|
|
61
|
+
char *n = fio_bstr_reserve(w->s, add + (w->capa >> 2) + 1);
|
|
62
|
+
fio_str_info_s i = fio_bstr_info(n);
|
|
63
|
+
if (FIO_UNLIKELY(i.capa - i.len <= add))
|
|
64
|
+
return 0; /* allocation failed */
|
|
65
|
+
w->s = n;
|
|
66
|
+
w->capa = i.capa; /* i.len == w->len (mirrors) */
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
FIO_IFUNC void iodine_json_w_write(iodine_json_w_s *w,
|
|
71
|
+
const void *src,
|
|
72
|
+
size_t len) {
|
|
73
|
+
if (FIO_LIKELY(iodine_json_w_need(w, len))) {
|
|
74
|
+
FIO_MEMCPY(w->s + w->len, src, len);
|
|
75
|
+
w->len += len;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
FIO_IFUNC void iodine_json_w_ch(iodine_json_w_s *w, const char c) {
|
|
80
|
+
if (FIO_LIKELY(iodine_json_w_need(w, 1)))
|
|
81
|
+
w->s[w->len++] = c;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
FIO_IFUNC void iodine_json_w_i(iodine_json_w_s *w, int64_t num) {
|
|
85
|
+
/* write the number directly into the buffer (max 20 digits + sign + NUL) */
|
|
86
|
+
if (FIO_LIKELY(iodine_json_w_need(w, 24)))
|
|
87
|
+
w->len += fio_ltoa(w->s + w->len, num, 10);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Writes `num` to `dest` (up to 39 bytes + NUL), matching the output of
|
|
91
|
+
* fio_ftoa(dest, num, 10) - i.e., snprintf("%g") with the ".0" fixup -
|
|
92
|
+
* without the snprintf overhead.
|
|
93
|
+
*
|
|
94
|
+
* Falls back to fio_ftoa for non-finite values, extreme exponents, and
|
|
95
|
+
* values landing on a rounding boundary (where the exact snprintf
|
|
96
|
+
* half-to-even rounding of the exact decimal expansion is required).
|
|
97
|
+
*
|
|
98
|
+
* NOTE: uses the C STL's internal fio___aton_dec2d for correctly-rounded
|
|
99
|
+
* powers of ten. If a future fio-stl.h sync renames it, this breaks loudly
|
|
100
|
+
* at compile time (safe). */
|
|
101
|
+
static size_t iodine_json_ftoa(char *dest, double num) {
|
|
102
|
+
if (FIO_UNLIKELY(!isfinite(num)))
|
|
103
|
+
return fio_ftoa(dest, num, 10); /* "NaN" / "Infinity" / "-Infinity" */
|
|
104
|
+
char *p = dest;
|
|
105
|
+
double a = num;
|
|
106
|
+
uint64_t bits;
|
|
107
|
+
memcpy(&bits, &num, 8);
|
|
108
|
+
if (bits >> 63) {
|
|
109
|
+
*p++ = '-';
|
|
110
|
+
a = -a;
|
|
111
|
+
}
|
|
112
|
+
if (a == 0) { /* %g prints "0" (or "-0"), fio_ftoa adds ".0" */
|
|
113
|
+
memcpy(p, "0.0", 4);
|
|
114
|
+
return (size_t)(p + 3 - dest);
|
|
115
|
+
}
|
|
116
|
+
/* decimal exponent estimate: a = m * 2^e2, m in [0.5, 1) */
|
|
117
|
+
int e2;
|
|
118
|
+
frexp(a, &e2);
|
|
119
|
+
int e10 = (int)(((int64_t)e2 * 78913) >> 18); /* ~floor(log10(a)) +/- 1 */
|
|
120
|
+
if (FIO_UNLIKELY(e10 < -302 || e10 > 302))
|
|
121
|
+
return fio_ftoa(dest, num, 10); /* pow10 out of table range */
|
|
122
|
+
/* scale to a 6 digit mantissa (q ~= a * 10^(5-e10) in [1e5, 1e6)) */
|
|
123
|
+
double q = a * fio___aton_dec2d(1, 5 - e10);
|
|
124
|
+
if (q >= 1e6 || q < 1e5) { /* correct the estimate (off by one at most) */
|
|
125
|
+
e10 += (q >= 1e6) ? 1 : -1;
|
|
126
|
+
q = a * fio___aton_dec2d(1, 5 - e10);
|
|
127
|
+
}
|
|
128
|
+
double fl = floor(q);
|
|
129
|
+
if (FIO_UNLIKELY(q - fl > 0.4999999 && q - fl < 0.5000001))
|
|
130
|
+
return fio_ftoa(dest, num, 10); /* rounding boundary - exact tie break */
|
|
131
|
+
uint64_t r = (uint64_t)fl + (q - fl >= 0.5);
|
|
132
|
+
if (FIO_UNLIKELY(r == 1000000)) { /* rounded up to the next power of 10 */
|
|
133
|
+
r = 100000;
|
|
134
|
+
++e10;
|
|
135
|
+
}
|
|
136
|
+
/* the 6 digits of r (d.ddddd * 10^e10) */
|
|
137
|
+
char dig[6];
|
|
138
|
+
dig[5] = (char)('0' + (r % 10));
|
|
139
|
+
r /= 10;
|
|
140
|
+
dig[4] = (char)('0' + (r % 10));
|
|
141
|
+
r /= 10;
|
|
142
|
+
dig[3] = (char)('0' + (r % 10));
|
|
143
|
+
r /= 10;
|
|
144
|
+
dig[2] = (char)('0' + (r % 10));
|
|
145
|
+
r /= 10;
|
|
146
|
+
dig[1] = (char)('0' + (r % 10));
|
|
147
|
+
r /= 10;
|
|
148
|
+
dig[0] = (char)('0' + r);
|
|
149
|
+
if (e10 >= -4 && e10 < 6) {
|
|
150
|
+
/* %f style (trimming trailing zeros after the decimal point) */
|
|
151
|
+
if (e10 >= 0) {
|
|
152
|
+
const int idig = e10 + 1; /* integer digits (1..6) */
|
|
153
|
+
memcpy(p, dig, (size_t)idig);
|
|
154
|
+
p += idig;
|
|
155
|
+
int f = 5;
|
|
156
|
+
while (f >= idig && dig[f] == '0')
|
|
157
|
+
--f;
|
|
158
|
+
if (f >= idig) {
|
|
159
|
+
*p++ = '.';
|
|
160
|
+
memcpy(p, dig + idig, (size_t)(f - idig + 1));
|
|
161
|
+
p += f - idig + 1;
|
|
162
|
+
} else {
|
|
163
|
+
memcpy(p, ".0", 3); /* fio_ftoa's ".0" fixup */
|
|
164
|
+
p += 2;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
memcpy(p, "0.", 2);
|
|
168
|
+
p += 2;
|
|
169
|
+
for (int z = -e10 - 1; z--;)
|
|
170
|
+
*p++ = '0';
|
|
171
|
+
int f = 5;
|
|
172
|
+
while (f > 0 && dig[f] == '0')
|
|
173
|
+
--f;
|
|
174
|
+
memcpy(p, dig, (size_t)(f + 1));
|
|
175
|
+
p += f + 1;
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
/* %e style: d[.ddddd]e+XX (trimming trailing zeros in the mantissa) */
|
|
179
|
+
*p++ = dig[0];
|
|
180
|
+
int f = 5;
|
|
181
|
+
while (f > 0 && dig[f] == '0')
|
|
182
|
+
--f;
|
|
183
|
+
if (f) {
|
|
184
|
+
*p++ = '.';
|
|
185
|
+
memcpy(p, dig + 1, (size_t)f);
|
|
186
|
+
p += f;
|
|
187
|
+
}
|
|
188
|
+
*p++ = 'e';
|
|
189
|
+
*p++ = (e10 < 0) ? '-' : '+';
|
|
190
|
+
int x = (e10 < 0) ? -e10 : e10;
|
|
191
|
+
if (x >= 100)
|
|
192
|
+
*p++ = (char)('0' + (x / 100));
|
|
193
|
+
*p++ = (char)('0' + ((x / 10) % 10));
|
|
194
|
+
*p++ = (char)('0' + (x % 10));
|
|
195
|
+
}
|
|
196
|
+
*p = 0;
|
|
197
|
+
return (size_t)(p - dest);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
FIO_IFUNC void iodine_json_w_f(iodine_json_w_s *w, double num) {
|
|
201
|
+
/* write the float directly into the buffer (max 39 bytes + NUL) */
|
|
202
|
+
if (FIO_LIKELY(iodine_json_w_need(w, 40)))
|
|
203
|
+
w->len += iodine_json_ftoa(w->s + w->len, num);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** SWAR escape detection: returns non-zero if any byte in the word needs
|
|
207
|
+
* escaping (< 0x20, '"', '\\', 0x7F) or UTF-8 validation (>= 0x80).
|
|
208
|
+
* Byte-order independent (mask is only tested against zero). */
|
|
209
|
+
FIO_IFUNC uint64_t iodine_json_swar_dirty(uint64_t v) {
|
|
210
|
+
const uint64_t hi = 0x8080808080808080ULL;
|
|
211
|
+
uint64_t dirty = (v - 0x2020202020202020ULL) & ~v & hi; /* bytes < 0x20 */
|
|
212
|
+
uint64_t x = v ^ 0x2222222222222222ULL; /* '"' */
|
|
213
|
+
dirty |= (x - 0x0101010101010101ULL) & ~x & hi;
|
|
214
|
+
x = v ^ 0x5C5C5C5C5C5C5C5CULL; /* '\\' */
|
|
215
|
+
dirty |= (x - 0x0101010101010101ULL) & ~x & hi;
|
|
216
|
+
x = v ^ 0x7F7F7F7F7F7F7F7FULL; /* 0x7F */
|
|
217
|
+
dirty |= (x - 0x0101010101010101ULL) & ~x & hi;
|
|
218
|
+
return dirty | (v & hi); /* bytes >= 0x80 */
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Writes a quoted JSON string, escaping as needed (single pass).
|
|
222
|
+
* Escape semantics identical to fio_bstr_write_escape: simple escapes for
|
|
223
|
+
* \b \f \n \r \t \\ \", \u00XX for control characters (incl. 0x7F),
|
|
224
|
+
* valid UTF-8 sequences copied as-is, invalid bytes escaped as \xXX. */
|
|
225
|
+
static void iodine_json_w_string(iodine_json_w_s *w,
|
|
226
|
+
const char *src,
|
|
227
|
+
size_t len) {
|
|
228
|
+
const uint8_t *s = (const uint8_t *)src;
|
|
229
|
+
const uint8_t *e = s + len;
|
|
230
|
+
const uint8_t *run = s;
|
|
231
|
+
const uint8_t *p = s;
|
|
232
|
+
iodine_json_w_ch(w, '"');
|
|
233
|
+
while (p < e) {
|
|
234
|
+
/* SWAR fast scan: skip 16 clean bytes at a time */
|
|
235
|
+
while ((size_t)(e - p) >= 16) {
|
|
236
|
+
uint64_t v0, v1;
|
|
237
|
+
memcpy(&v0, p, 8);
|
|
238
|
+
memcpy(&v1, p + 8, 8);
|
|
239
|
+
if (iodine_json_swar_dirty(v0) | iodine_json_swar_dirty(v1))
|
|
240
|
+
break;
|
|
241
|
+
p += 16;
|
|
242
|
+
}
|
|
243
|
+
/* byte-scan the dirty block (or the tail) */
|
|
244
|
+
const uint8_t *block_end = ((size_t)(e - p) >= 16) ? p + 16 : e;
|
|
245
|
+
while (p < block_end) {
|
|
246
|
+
const uint8_t c = *p;
|
|
247
|
+
/* fast path: printable ASCII (0x20..0x7E), except '"' and '\\' */
|
|
248
|
+
if (FIO_LIKELY((uint8_t)(c - 0x20) < (uint8_t)0x5F && c != '"' &&
|
|
249
|
+
c != '\\')) {
|
|
250
|
+
++p;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const size_t utf8_len = fio_utf8_char_len(p);
|
|
254
|
+
if (utf8_len > 1 && (size_t)(e - p) >= utf8_len) {
|
|
255
|
+
p += utf8_len; /* valid UTF-8 sequence - copied as-is with the run */
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
/* (a truncated sequence at the end of the input is escaped, byte-wise) */
|
|
259
|
+
/* flush the clean run and escape this byte */
|
|
260
|
+
iodine_json_w_write(w, run, (size_t)(p - run));
|
|
261
|
+
++p;
|
|
262
|
+
run = p;
|
|
263
|
+
switch (c) {
|
|
264
|
+
case '\b': iodine_json_w_write(w, "\\b", 2); break;
|
|
265
|
+
case '\f': iodine_json_w_write(w, "\\f", 2); break;
|
|
266
|
+
case '\n': iodine_json_w_write(w, "\\n", 2); break;
|
|
267
|
+
case '\r': iodine_json_w_write(w, "\\r", 2); break;
|
|
268
|
+
case '\t': iodine_json_w_write(w, "\\t", 2); break;
|
|
269
|
+
case '\\': iodine_json_w_write(w, "\\\\", 2); break;
|
|
270
|
+
case '"': iodine_json_w_write(w, "\\\"", 2); break;
|
|
271
|
+
default: {
|
|
272
|
+
/* control characters: \u00XX - invalid UTF-8 bytes: \xXX */
|
|
273
|
+
char buf[6] = {'\\',
|
|
274
|
+
'u',
|
|
275
|
+
'0',
|
|
276
|
+
'0',
|
|
277
|
+
(char)fio_i2c((unsigned char)(c >> 4)),
|
|
278
|
+
(char)fio_i2c((unsigned char)(c & 15))};
|
|
279
|
+
if (c & 128) {
|
|
280
|
+
buf[1] = 'x';
|
|
281
|
+
buf[2] = buf[4];
|
|
282
|
+
buf[3] = buf[5];
|
|
283
|
+
iodine_json_w_write(w, buf, 4);
|
|
284
|
+
} else {
|
|
285
|
+
iodine_json_w_write(w, buf, 6);
|
|
286
|
+
}
|
|
287
|
+
} break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
iodine_json_w_write(w, run, (size_t)(e - run));
|
|
292
|
+
iodine_json_w_ch(w, '"');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static void iodine_json_w_value(iodine_json_w_s *w, VALUE o);
|
|
296
|
+
|
|
297
|
+
static void iodine_json_w_key(iodine_json_w_s *w, VALUE tmp) {
|
|
37
298
|
/* Note: keys MUST be Strings */
|
|
38
299
|
switch (rb_type(tmp)) {
|
|
39
300
|
case RUBY_T_BIGNUM: /* Bignum (Windows: Integers >= 2^30) */
|
|
@@ -42,126 +303,130 @@ static char *iodine_json_stringify_key(char *dest, VALUE tmp) {
|
|
|
42
303
|
case RUBY_T_SYMBOL: tmp = rb_sym2str(tmp); /* fall through */
|
|
43
304
|
case RUBY_T_STRING:
|
|
44
305
|
string_key:
|
|
45
|
-
|
|
46
|
-
dest =
|
|
47
|
-
fio_bstr_write_escape(dest, RSTRING_PTR(tmp), (size_t)RSTRING_LEN(tmp));
|
|
48
|
-
dest = fio_bstr_write(dest, "\"", 1);
|
|
306
|
+
iodine_json_w_string(w, RSTRING_PTR(tmp), (size_t)RSTRING_LEN(tmp));
|
|
49
307
|
break;
|
|
50
308
|
case RUBY_T_FIXNUM:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
FIO_STRING_WRITE_STR2("\"", 1));
|
|
309
|
+
iodine_json_w_ch(w, '"');
|
|
310
|
+
iodine_json_w_i(w, NUM2LL(tmp));
|
|
311
|
+
iodine_json_w_ch(w, '"');
|
|
55
312
|
break;
|
|
56
|
-
case RUBY_T_TRUE:
|
|
57
|
-
case RUBY_T_FALSE:
|
|
58
|
-
case RUBY_T_NIL:
|
|
313
|
+
case RUBY_T_TRUE: iodine_json_w_write(w, "\"true\"", 6); break;
|
|
314
|
+
case RUBY_T_FALSE: iodine_json_w_write(w, "\"false\"", 7); break;
|
|
315
|
+
case RUBY_T_NIL: iodine_json_w_write(w, "\"null\"", 6); break;
|
|
59
316
|
default:
|
|
60
317
|
tmp = rb_any_to_s(tmp);
|
|
61
318
|
if (RB_TYPE_P(tmp, RUBY_T_STRING))
|
|
62
319
|
goto string_key;
|
|
63
|
-
|
|
320
|
+
iodine_json_w_write(w, "\"error\"", 7);
|
|
64
321
|
}
|
|
65
|
-
return dest;
|
|
66
322
|
}
|
|
67
323
|
|
|
68
|
-
static
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*pdest = iodine_json_stringify2bstr(*pdest, v);
|
|
75
|
-
*pdest = fio_bstr_write(*pdest, ",", 1);
|
|
324
|
+
static int iodine_json_hash_each_callback(VALUE k, VALUE v, VALUE w_) {
|
|
325
|
+
iodine_json_w_s *w = (iodine_json_w_s *)w_;
|
|
326
|
+
iodine_json_w_key(w, k);
|
|
327
|
+
iodine_json_w_ch(w, ':');
|
|
328
|
+
iodine_json_w_value(w, v);
|
|
329
|
+
iodine_json_w_ch(w, ',');
|
|
76
330
|
return ST_CONTINUE;
|
|
77
331
|
}
|
|
78
332
|
|
|
79
|
-
/* Converts a VALUE to
|
|
80
|
-
static
|
|
333
|
+
/* Converts a VALUE to JSON, appending to the writer's bstr. */
|
|
334
|
+
static void iodine_json_w_value(iodine_json_w_s *w, VALUE o) {
|
|
81
335
|
size_t tmp;
|
|
82
336
|
switch (rb_type(o)) {
|
|
83
|
-
case RUBY_T_NIL:
|
|
84
|
-
case RUBY_T_TRUE:
|
|
85
|
-
case RUBY_T_FALSE:
|
|
337
|
+
case RUBY_T_NIL: iodine_json_w_write(w, "null", 4); return;
|
|
338
|
+
case RUBY_T_TRUE: iodine_json_w_write(w, "true", 4); return;
|
|
339
|
+
case RUBY_T_FALSE: iodine_json_w_write(w, "false", 5); return;
|
|
86
340
|
case RUBY_T_ARRAY:
|
|
87
341
|
tmp = rb_array_len(o);
|
|
88
342
|
if (!tmp) {
|
|
89
|
-
|
|
90
|
-
return
|
|
343
|
+
iodine_json_w_write(w, "[]", 2);
|
|
344
|
+
return;
|
|
91
345
|
}
|
|
92
|
-
|
|
346
|
+
iodine_json_w_ch(w, '[');
|
|
93
347
|
for (size_t i = 0; i < tmp; ++i) {
|
|
94
|
-
|
|
95
|
-
|
|
348
|
+
iodine_json_w_value(w, RARRAY_PTR(o)[i]);
|
|
349
|
+
iodine_json_w_ch(w, ',');
|
|
96
350
|
}
|
|
97
|
-
|
|
98
|
-
|
|
351
|
+
if (FIO_LIKELY(w->len && w->s[w->len - 1] == ','))
|
|
352
|
+
w->s[w->len - 1] = ']';
|
|
353
|
+
else
|
|
354
|
+
iodine_json_w_ch(w, ']');
|
|
355
|
+
return;
|
|
99
356
|
case RUBY_T_HASH:
|
|
100
357
|
tmp = rb_hash_size_num(o);
|
|
101
358
|
if (!tmp) {
|
|
102
|
-
|
|
103
|
-
return
|
|
359
|
+
iodine_json_w_write(w, "{}", 2);
|
|
360
|
+
return;
|
|
104
361
|
}
|
|
105
|
-
|
|
106
|
-
rb_hash_foreach(o, iodine_json_hash_each_callback, (VALUE)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
362
|
+
iodine_json_w_ch(w, '{');
|
|
363
|
+
rb_hash_foreach(o, iodine_json_hash_each_callback, (VALUE)w);
|
|
364
|
+
if (FIO_LIKELY(w->len && w->s[w->len - 1] == ','))
|
|
365
|
+
w->s[w->len - 1] = '}';
|
|
366
|
+
else
|
|
367
|
+
iodine_json_w_ch(w, '}');
|
|
368
|
+
return;
|
|
369
|
+
case RUBY_T_FIXNUM: iodine_json_w_i(w, RB_NUM2LL(o)); return;
|
|
110
370
|
case RUBY_T_BIGNUM: /* Bignum (Windows: Integers >= 2^30) */
|
|
111
371
|
o = rb_big2str(o, 10);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
case RUBY_T_FLOAT:
|
|
115
|
-
FIO_STR_INFO_TMP_VAR(buf, 232);
|
|
116
|
-
buf.len = fio_ftoa(buf.buf, RFLOAT_VALUE(o), 10);
|
|
117
|
-
dest = fio_bstr_write(dest, buf.buf, buf.len);
|
|
118
|
-
return dest;
|
|
119
|
-
}
|
|
372
|
+
iodine_json_w_write(w, RSTRING_PTR(o), (size_t)RSTRING_LEN(o));
|
|
373
|
+
return;
|
|
374
|
+
case RUBY_T_FLOAT: iodine_json_w_f(w, RFLOAT_VALUE(o)); return;
|
|
120
375
|
case RUBY_T_SYMBOL: o = rb_sym2str(o); /* fall through */
|
|
121
376
|
case RUBY_T_STRING:
|
|
122
377
|
add_string:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
dest = fio_bstr_write(dest, "\"", 1);
|
|
126
|
-
return dest;
|
|
378
|
+
iodine_json_w_string(w, RSTRING_PTR(o), (size_t)RSTRING_LEN(o));
|
|
379
|
+
return;
|
|
127
380
|
default:
|
|
128
381
|
if (1) {
|
|
129
382
|
VALUE tmp = rb_check_funcall(o, IODINE_TO_JSON_ID, 0, NULL);
|
|
130
|
-
if (tmp != RUBY_Qundef && RB_TYPE_P(tmp, RUBY_T_STRING))
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
383
|
+
if (tmp != RUBY_Qundef && RB_TYPE_P(tmp, RUBY_T_STRING)) {
|
|
384
|
+
iodine_json_w_write(w, RSTRING_PTR(tmp), (size_t)RSTRING_LEN(tmp));
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
134
387
|
o = rb_funcallv(o, IODINE_TO_S_ID, 0, NULL);
|
|
135
388
|
}
|
|
136
389
|
if (!RB_TYPE_P(o, RUBY_T_STRING)) {
|
|
137
390
|
FIO_LOG_ERROR("Iodine::JSON.stringify called with an object that doesn't "
|
|
138
391
|
"respond to #to_s.");
|
|
139
|
-
|
|
392
|
+
iodine_json_w_write(w, "null", 4);
|
|
393
|
+
return;
|
|
140
394
|
}
|
|
141
395
|
goto add_string;
|
|
142
396
|
}
|
|
143
397
|
}
|
|
144
398
|
|
|
399
|
+
/* Converts a VALUE to a fio_bstr */
|
|
400
|
+
static char *iodine_json_stringify2bstr(char *dest, VALUE o) {
|
|
401
|
+
iodine_json_w_s w = {.s = dest, .len = 0, .capa = 0};
|
|
402
|
+
if (dest) {
|
|
403
|
+
fio_str_info_s i = fio_bstr_info(dest);
|
|
404
|
+
w.len = i.len;
|
|
405
|
+
w.capa = i.capa;
|
|
406
|
+
}
|
|
407
|
+
iodine_json_w_value(&w, o);
|
|
408
|
+
return fio_bstr_len_set(w.s, w.len);
|
|
409
|
+
}
|
|
410
|
+
|
|
145
411
|
/* *****************************************************************************
|
|
146
412
|
JSON Beautifier - Pretty-Printed JSON Output
|
|
147
413
|
***************************************************************************** */
|
|
148
414
|
|
|
149
415
|
typedef struct {
|
|
150
|
-
|
|
416
|
+
iodine_json_w_s w;
|
|
151
417
|
size_t t;
|
|
152
418
|
} iodine_json_beautify2bstr_s;
|
|
153
419
|
|
|
154
|
-
static
|
|
155
|
-
VALUE o);
|
|
420
|
+
static void iodine_json_beautify2bstr(iodine_json_beautify2bstr_s *d, VALUE o);
|
|
156
421
|
|
|
157
422
|
FIO_IFUNC void iodine_json_beautify2bstr_pad(iodine_json_beautify2bstr_s *d) {
|
|
158
423
|
if (!d->t)
|
|
159
424
|
return;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
425
|
+
iodine_json_w_ch(&d->w, '\n');
|
|
426
|
+
if (iodine_json_w_need(&d->w, d->t)) {
|
|
427
|
+
FIO_MEMSET(d->w.s + d->w.len, '\t', d->t);
|
|
428
|
+
d->w.len += d->t;
|
|
429
|
+
}
|
|
165
430
|
}
|
|
166
431
|
|
|
167
432
|
static int iodine_json_hash_each_beautify_callback(VALUE k,
|
|
@@ -169,85 +434,81 @@ static int iodine_json_hash_each_beautify_callback(VALUE k,
|
|
|
169
434
|
VALUE dest_) {
|
|
170
435
|
iodine_json_beautify2bstr_s *d = (iodine_json_beautify2bstr_s *)dest_;
|
|
171
436
|
iodine_json_beautify2bstr_pad(d);
|
|
172
|
-
|
|
173
|
-
|
|
437
|
+
iodine_json_w_key(&d->w, k);
|
|
438
|
+
iodine_json_w_write(&d->w, ": ", 2);
|
|
174
439
|
iodine_json_beautify2bstr(d, v);
|
|
175
|
-
|
|
440
|
+
iodine_json_w_ch(&d->w, ',');
|
|
176
441
|
return ST_CONTINUE;
|
|
177
442
|
}
|
|
178
443
|
|
|
179
444
|
/* Converts a VALUE to a fio_bstr */
|
|
180
|
-
static
|
|
181
|
-
|
|
445
|
+
static void iodine_json_beautify2bstr(iodine_json_beautify2bstr_s *d, VALUE o) {
|
|
446
|
+
iodine_json_w_s *w = &d->w;
|
|
182
447
|
size_t tmp;
|
|
183
448
|
switch (rb_type(o)) {
|
|
184
|
-
case RUBY_T_NIL:
|
|
185
|
-
case RUBY_T_TRUE:
|
|
186
|
-
case RUBY_T_FALSE:
|
|
449
|
+
case RUBY_T_NIL: iodine_json_w_write(w, "null", 4); return;
|
|
450
|
+
case RUBY_T_TRUE: iodine_json_w_write(w, "true", 4); return;
|
|
451
|
+
case RUBY_T_FALSE: iodine_json_w_write(w, "false", 5); return;
|
|
187
452
|
case RUBY_T_ARRAY:
|
|
188
453
|
tmp = rb_array_len(o);
|
|
189
454
|
if (!tmp) {
|
|
190
|
-
|
|
191
|
-
return
|
|
455
|
+
iodine_json_w_write(w, "[]", 2);
|
|
456
|
+
return;
|
|
192
457
|
}
|
|
193
|
-
|
|
458
|
+
iodine_json_w_ch(w, '[');
|
|
194
459
|
++d->t;
|
|
195
460
|
for (size_t i = 0; i < tmp; ++i) {
|
|
196
461
|
iodine_json_beautify2bstr_pad(d);
|
|
197
462
|
iodine_json_beautify2bstr(d, RARRAY_PTR(o)[i]);
|
|
198
|
-
|
|
463
|
+
iodine_json_w_ch(w, ',');
|
|
199
464
|
}
|
|
200
|
-
|
|
465
|
+
if (FIO_LIKELY(w->len && w->s[w->len - 1] == ','))
|
|
466
|
+
w->s[w->len - 1] = '\n';
|
|
201
467
|
--d->t;
|
|
202
468
|
iodine_json_beautify2bstr_pad(d);
|
|
203
|
-
|
|
204
|
-
return
|
|
469
|
+
iodine_json_w_ch(w, ']');
|
|
470
|
+
return;
|
|
205
471
|
case RUBY_T_HASH:
|
|
206
472
|
tmp = rb_hash_size_num(o);
|
|
207
473
|
if (!tmp) {
|
|
208
|
-
|
|
209
|
-
return
|
|
474
|
+
iodine_json_w_write(w, "{}", 2);
|
|
475
|
+
return;
|
|
210
476
|
}
|
|
211
|
-
|
|
477
|
+
iodine_json_w_ch(w, '{');
|
|
212
478
|
++d->t;
|
|
213
|
-
rb_hash_foreach(o, iodine_json_hash_each_beautify_callback, (VALUE)
|
|
214
|
-
|
|
479
|
+
rb_hash_foreach(o, iodine_json_hash_each_beautify_callback, (VALUE)d);
|
|
480
|
+
if (FIO_LIKELY(w->len && w->s[w->len - 1] == ','))
|
|
481
|
+
w->s[w->len - 1] = '\n';
|
|
215
482
|
--d->t;
|
|
216
483
|
iodine_json_beautify2bstr_pad(d);
|
|
217
|
-
|
|
218
|
-
return
|
|
219
|
-
case RUBY_T_FIXNUM:
|
|
484
|
+
iodine_json_w_ch(w, '}');
|
|
485
|
+
return;
|
|
486
|
+
case RUBY_T_FIXNUM: iodine_json_w_i(w, RB_NUM2LL(o)); return;
|
|
220
487
|
case RUBY_T_BIGNUM: /* Bignum (Windows: Integers >= 2^30) */
|
|
221
488
|
o = rb_big2str(o, 10);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
case RUBY_T_FLOAT:
|
|
225
|
-
FIO_STR_INFO_TMP_VAR(buf, 232);
|
|
226
|
-
buf.len = fio_ftoa(buf.buf, RFLOAT_VALUE(o), 10);
|
|
227
|
-
d->o = fio_bstr_write(d->o, buf.buf, buf.len);
|
|
228
|
-
return d->o;
|
|
229
|
-
}
|
|
489
|
+
iodine_json_w_write(w, RSTRING_PTR(o), (size_t)RSTRING_LEN(o));
|
|
490
|
+
return;
|
|
491
|
+
case RUBY_T_FLOAT: iodine_json_w_f(w, RFLOAT_VALUE(o)); return;
|
|
230
492
|
case RUBY_T_SYMBOL: o = rb_sym2str(o); /* fall through */
|
|
231
493
|
case RUBY_T_STRING:
|
|
232
494
|
add_string:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
d->o = fio_bstr_write(d->o, "\"", 1);
|
|
236
|
-
return d->o;
|
|
495
|
+
iodine_json_w_string(w, RSTRING_PTR(o), (size_t)RSTRING_LEN(o));
|
|
496
|
+
return;
|
|
237
497
|
default:
|
|
238
498
|
if (1) {
|
|
239
499
|
VALUE tmp = rb_check_funcall(o, IODINE_TO_JSON_ID, 0, NULL);
|
|
240
|
-
if (tmp != RUBY_Qundef && RB_TYPE_P(tmp, RUBY_T_STRING))
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
500
|
+
if (tmp != RUBY_Qundef && RB_TYPE_P(tmp, RUBY_T_STRING)) {
|
|
501
|
+
iodine_json_w_write(w, RSTRING_PTR(tmp), (size_t)RSTRING_LEN(tmp));
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
244
504
|
o = rb_funcallv(o, IODINE_TO_S_ID, 0, NULL);
|
|
245
505
|
if (RB_TYPE_P(o, RUBY_T_STRING))
|
|
246
506
|
goto add_string;
|
|
247
507
|
}
|
|
248
508
|
FIO_LOG_ERROR("Iodine::JSON.stringify called with an object that doesn't "
|
|
249
509
|
"respond to neither #to_json nor #to_s.");
|
|
250
|
-
|
|
510
|
+
iodine_json_w_write(w, "null", 4);
|
|
511
|
+
return;
|
|
251
512
|
}
|
|
252
513
|
}
|
|
253
514
|
|
|
@@ -440,35 +701,37 @@ This is the faster parsing path used by Iodine::JSON.parse().
|
|
|
440
701
|
|
|
441
702
|
/** JSON parser callback implementations for direct Ruby object creation */
|
|
442
703
|
/** NULL object was detected. Returns new object as `void *`. */
|
|
443
|
-
FIO_SFUNC
|
|
704
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_on_null(void *udata) {
|
|
444
705
|
(void)udata;
|
|
445
|
-
return (void *)Qnil;
|
|
706
|
+
return (fio_json_obj_result_s){.obj = (void *)Qnil};
|
|
446
707
|
}
|
|
447
708
|
/** TRUE object was detected. Returns new object as `void *`. */
|
|
448
|
-
FIO_SFUNC
|
|
709
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_on_true(void *udata) {
|
|
449
710
|
(void)udata;
|
|
450
|
-
return (void *)Qtrue;
|
|
711
|
+
return (fio_json_obj_result_s){.obj = (void *)Qtrue};
|
|
451
712
|
}
|
|
452
713
|
/** FALSE object was detected. Returns new object as `void *`. */
|
|
453
|
-
FIO_SFUNC
|
|
714
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_on_false(void *udata) {
|
|
454
715
|
(void)udata;
|
|
455
|
-
return (void *)Qfalse;
|
|
716
|
+
return (fio_json_obj_result_s){.obj = (void *)Qfalse};
|
|
456
717
|
}
|
|
457
718
|
/** Number was detected (long long). Returns new object as `void *`. */
|
|
458
|
-
FIO_SFUNC
|
|
719
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_on_number(void *udata,
|
|
720
|
+
int64_t i) {
|
|
459
721
|
(void)udata;
|
|
460
|
-
return (void *)
|
|
722
|
+
return (fio_json_obj_result_s){.obj = (void *)LL2NUM(((long long)i))};
|
|
461
723
|
}
|
|
462
724
|
/** Float was detected (double).Returns new object as `void *`. */
|
|
463
|
-
FIO_SFUNC
|
|
725
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_on_float(void *udata, double f) {
|
|
464
726
|
(void)udata;
|
|
465
|
-
return (void *)DBL2NUM(f);
|
|
727
|
+
return (fio_json_obj_result_s){.obj = (void *)DBL2NUM(f)};
|
|
466
728
|
}
|
|
467
729
|
/** String was detected (int / float). update `pos` to point at ending */
|
|
468
|
-
FIO_SFUNC
|
|
469
|
-
|
|
470
|
-
size_t len) {
|
|
730
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_on_string(void *udata,
|
|
731
|
+
fio_buf_info_s input) {
|
|
471
732
|
(void)udata;
|
|
733
|
+
const void *start = input.buf;
|
|
734
|
+
size_t len = input.len;
|
|
472
735
|
VALUE str;
|
|
473
736
|
if (len < 4096) {
|
|
474
737
|
FIO_STR_INFO_TMP_VAR(buf, 4096);
|
|
@@ -479,35 +742,72 @@ FIO_SFUNC void *iodine___json_on_string(void *udata,
|
|
|
479
742
|
str = rb_str_new(tmp, fio_bstr_len(tmp));
|
|
480
743
|
fio_bstr_free(tmp);
|
|
481
744
|
}
|
|
482
|
-
return (void *)str;
|
|
745
|
+
return (fio_json_obj_result_s){.obj = (void *)str};
|
|
483
746
|
}
|
|
484
|
-
FIO_SFUNC
|
|
485
|
-
|
|
486
|
-
size_t len) {
|
|
747
|
+
FIO_SFUNC fio_json_obj_result_s
|
|
748
|
+
iodine___json_on_string_simple(void *udata, fio_buf_info_s input) {
|
|
487
749
|
(void)udata;
|
|
488
|
-
VALUE str = rb_str_new(
|
|
489
|
-
return (void *)str;
|
|
490
|
-
}
|
|
491
|
-
/** Dictionary
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
(void)udata, (void)ctx, (void)at;
|
|
500
|
-
VALUE ary = rb_ary_new();
|
|
501
|
-
return (void *)ary;
|
|
502
|
-
}
|
|
503
|
-
/** Map entry detected. Returns non-zero on error. */
|
|
504
|
-
FIO_SFUNC int iodine___json_map_push(void *udata,
|
|
505
|
-
void *ctx,
|
|
506
|
-
void *key,
|
|
507
|
-
void *val) {
|
|
750
|
+
VALUE str = rb_str_new(input.buf, input.len);
|
|
751
|
+
return (fio_json_obj_result_s){.obj = (void *)str};
|
|
752
|
+
}
|
|
753
|
+
/** Dictionary opens at `key` in `ctx` (NULL ctx = owned root). Places the
|
|
754
|
+
* map in its parent, reusing an existing Hash at `key` when present
|
|
755
|
+
* (duplicate map keys merge, matching the FIOBJ parser). JSON parsing keeps
|
|
756
|
+
* last-wins semantics: replaced values are NOT reported through `old` -
|
|
757
|
+
* conflict resolution is reserved for the HTTP params layer. */
|
|
758
|
+
FIO_SFUNC fio_json_push_result_s iodine___json_on_map(void *udata,
|
|
759
|
+
void *ctx,
|
|
760
|
+
fio_buf_info_s key) {
|
|
508
761
|
(void)udata;
|
|
509
|
-
|
|
510
|
-
|
|
762
|
+
VALUE map;
|
|
763
|
+
if (!ctx)
|
|
764
|
+
return (fio_json_push_result_s){.obj = (void *)rb_hash_new()};
|
|
765
|
+
if (!key.buf) { /* array parent: append */
|
|
766
|
+
map = rb_hash_new();
|
|
767
|
+
rb_ary_push((VALUE)ctx, map);
|
|
768
|
+
return (fio_json_push_result_s){.obj = (void *)map};
|
|
769
|
+
}
|
|
770
|
+
VALUE k = rb_str_new(key.buf, (long)key.len);
|
|
771
|
+
VALUE cur = rb_hash_lookup2((VALUE)ctx, k, Qundef);
|
|
772
|
+
if (cur != Qundef && RB_TYPE_P(cur, RUBY_T_HASH))
|
|
773
|
+
return (fio_json_push_result_s){
|
|
774
|
+
.obj = (void *)cur}; /* reuse matching map (merge) */
|
|
775
|
+
map = rb_hash_new();
|
|
776
|
+
rb_hash_aset((VALUE)ctx, k, map);
|
|
777
|
+
return (fio_json_push_result_s){.obj = (void *)map};
|
|
778
|
+
}
|
|
779
|
+
/** Array opens at `key` in `ctx` (NULL ctx = owned root). Same placement
|
|
780
|
+
* and reuse contract as iodine___json_on_map (last-wins, `old` unused). */
|
|
781
|
+
FIO_SFUNC fio_json_push_result_s iodine___json_on_array(void *udata,
|
|
782
|
+
void *ctx,
|
|
783
|
+
fio_buf_info_s key) {
|
|
784
|
+
(void)udata;
|
|
785
|
+
VALUE ary;
|
|
786
|
+
if (!ctx)
|
|
787
|
+
return (fio_json_push_result_s){.obj = (void *)rb_ary_new()};
|
|
788
|
+
if (!key.buf) { /* array parent: append */
|
|
789
|
+
ary = rb_ary_new();
|
|
790
|
+
rb_ary_push((VALUE)ctx, ary);
|
|
791
|
+
return (fio_json_push_result_s){.obj = (void *)ary};
|
|
792
|
+
}
|
|
793
|
+
VALUE k = rb_str_new(key.buf, (long)key.len);
|
|
794
|
+
VALUE cur = rb_hash_lookup2((VALUE)ctx, k, Qundef);
|
|
795
|
+
if (cur != Qundef && RB_TYPE_P(cur, RUBY_T_ARRAY))
|
|
796
|
+
return (fio_json_push_result_s){.obj =
|
|
797
|
+
(void *)cur}; /* reuse matching array */
|
|
798
|
+
ary = rb_ary_new();
|
|
799
|
+
rb_hash_aset((VALUE)ctx, k, ary);
|
|
800
|
+
return (fio_json_push_result_s){.obj = (void *)ary};
|
|
801
|
+
}
|
|
802
|
+
/** Map entry detected (`key` is borrowed). Last value wins, matching the
|
|
803
|
+
* FIOBJ parser. Returns no replaced object to the parser. */
|
|
804
|
+
FIO_SFUNC fio_json_obj_result_s iodine___json_map_push(void *udata,
|
|
805
|
+
void *map,
|
|
806
|
+
fio_buf_info_s key,
|
|
807
|
+
void *val) {
|
|
808
|
+
(void)udata;
|
|
809
|
+
rb_hash_aset((VALUE)map, rb_str_new(key.buf, (long)key.len), (VALUE)val);
|
|
810
|
+
return (fio_json_obj_result_s){0};
|
|
511
811
|
}
|
|
512
812
|
/** Array entry detected. Returns non-zero on error. */
|
|
513
813
|
FIO_SFUNC int iodine___json_array_push(void *udata, void *ctx, void *val) {
|
|
@@ -515,16 +815,19 @@ FIO_SFUNC int iodine___json_array_push(void *udata, void *ctx, void *val) {
|
|
|
515
815
|
rb_ary_push((VALUE)ctx, (VALUE)val);
|
|
516
816
|
return 0;
|
|
517
817
|
}
|
|
518
|
-
/**
|
|
519
|
-
FIO_SFUNC
|
|
520
|
-
(void)udata
|
|
818
|
+
/** Conflict-resolution type checks (values are Ruby objects). */
|
|
819
|
+
FIO_SFUNC int iodine___json_is_array(void *udata, void *ctx) {
|
|
820
|
+
(void)udata;
|
|
821
|
+
return RB_TYPE_P((VALUE)ctx, RUBY_T_ARRAY);
|
|
822
|
+
}
|
|
823
|
+
FIO_SFUNC int iodine___json_is_map(void *udata, void *ctx) {
|
|
824
|
+
(void)udata;
|
|
825
|
+
return RB_TYPE_P((VALUE)ctx, RUBY_T_HASH);
|
|
521
826
|
}
|
|
522
|
-
/**
|
|
523
|
-
FIO_SFUNC void
|
|
524
|
-
(void)udata, (void)ctx;
|
|
525
|
-
return (void *)Qnil;
|
|
827
|
+
/** Ruby owns values; no manual cleanup is required on parser errors. */
|
|
828
|
+
FIO_SFUNC void iodine___json_free_unused_object(void *udata, void *ctx) {
|
|
829
|
+
(void)udata, (void)ctx; /* Ruby objects are GC managed */
|
|
526
830
|
}
|
|
527
|
-
|
|
528
831
|
static fio_json_parser_callbacks_s IODINE_JSON_PARSER_CALLBACKS = {
|
|
529
832
|
.on_null = iodine___json_on_null,
|
|
530
833
|
.on_true = iodine___json_on_true,
|
|
@@ -537,8 +840,9 @@ static fio_json_parser_callbacks_s IODINE_JSON_PARSER_CALLBACKS = {
|
|
|
537
840
|
.on_array = iodine___json_on_array,
|
|
538
841
|
.map_push = iodine___json_map_push,
|
|
539
842
|
.array_push = iodine___json_array_push,
|
|
843
|
+
.is_array = iodine___json_is_array,
|
|
844
|
+
.is_map = iodine___json_is_map,
|
|
540
845
|
.free_unused_object = iodine___json_free_unused_object,
|
|
541
|
-
.on_error = iodine___json_on_error,
|
|
542
846
|
};
|
|
543
847
|
|
|
544
848
|
/* *****************************************************************************
|
|
@@ -562,11 +866,12 @@ static VALUE iodine_json_parse(VALUE self, VALUE rstr) {
|
|
|
562
866
|
rb_check_type(rstr, RUBY_T_STRING);
|
|
563
867
|
STORE.gc_stop();
|
|
564
868
|
fio_json_result_s result = fio_json_parse(&IODINE_JSON_PARSER_CALLBACKS,
|
|
869
|
+
NULL,
|
|
565
870
|
NULL,
|
|
566
871
|
RSTRING_PTR(rstr),
|
|
567
872
|
RSTRING_LEN(rstr));
|
|
568
873
|
STORE.gc_start();
|
|
569
|
-
r = (VALUE)
|
|
874
|
+
r = result.err ? Qnil : (VALUE)result.ctx;
|
|
570
875
|
STORE.cache(r);
|
|
571
876
|
rb_str_buf_new(1); /* force GC to run if Ruby needs it */
|
|
572
877
|
return r;
|
|
@@ -630,12 +935,16 @@ static VALUE iodine_json_pretty(VALUE self, VALUE object) {
|
|
|
630
935
|
*/
|
|
631
936
|
static VALUE iodine_json_beautify(VALUE self, VALUE object) {
|
|
632
937
|
VALUE r = Qnil;
|
|
633
|
-
iodine_json_beautify2bstr_s dest = {
|
|
634
|
-
|
|
635
|
-
|
|
938
|
+
iodine_json_beautify2bstr_s dest = {.w = {.s = NULL, .len = 0, .capa = 0},
|
|
939
|
+
.t = 0};
|
|
940
|
+
dest.w.s = fio_bstr_reserve(NULL, ((size_t)1 << 12) - 64);
|
|
941
|
+
fio_str_info_s i = fio_bstr_info(dest.w.s);
|
|
942
|
+
dest.w.len = i.len;
|
|
943
|
+
dest.w.capa = i.capa;
|
|
636
944
|
iodine_json_beautify2bstr(&dest, object);
|
|
637
|
-
|
|
638
|
-
|
|
945
|
+
dest.w.s = fio_bstr_len_set(dest.w.s, dest.w.len);
|
|
946
|
+
r = rb_str_new(dest.w.s, (long)fio_bstr_len(dest.w.s));
|
|
947
|
+
fio_bstr_free(dest.w.s);
|
|
639
948
|
return r;
|
|
640
949
|
}
|
|
641
950
|
|