json 2.10.1 → 2.11.0
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/CHANGES.md +29 -0
- data/ext/json/ext/fbuffer/fbuffer.h +45 -14
- data/ext/json/ext/generator/generator.c +21 -3
- data/ext/json/ext/parser/extconf.rb +0 -1
- data/ext/json/ext/parser/parser.c +73 -186
- data/ext/json/ext/vendor/fpconv.c +479 -0
- data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
- data/lib/json/common.rb +220 -173
- data/lib/json/truffle_ruby/generator.rb +1 -1
- data/lib/json/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6415f9d9c30cadde9c59fe7b2c1b6279dfe990c84184572a9d8134d52e334780
|
4
|
+
data.tar.gz: e071f46d8d893429c83b2aac3e531d28c9c332b672e9ea024de9b1a8a935e4d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de12cb0f01d32d4003f251b63b5a4303a199548b0510dc1e4a4d29eb2cf5efc2cf1af2689bc5cd3bb3f5e86eb7b312b4bb011b718e9149050d5f2a46dd153290
|
7
|
+
data.tar.gz: 11b34643b6857bec8cca28d4fcb7f60072371227aac1b5e2f324f90f69e54f2463a8c1714ce5713e286318933433b8947d3ddf532178765f4be3375b9835eeea
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,34 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
### 2025-04-24 (2.11.0)
|
4
|
+
|
5
|
+
* Optimize Integer generation to be ~1.8x faster.
|
6
|
+
* Optimize Float generation to be ~10x faster.
|
7
|
+
* Fix `JSON.load` proc argument to substitute the parsed object with the return value.
|
8
|
+
This better match `Marshal.load` behavior.
|
9
|
+
* Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
|
10
|
+
* Deprecate `JSON.load_default_options`.
|
11
|
+
* Deprecate `JSON.unsafe_load_default_options`.
|
12
|
+
* Deprecate `JSON.dump_default_options`.
|
13
|
+
* Deprecate `Kernel#j`
|
14
|
+
* Deprecate `Kernel#jj`
|
15
|
+
* Remove outdated `JSON.iconv`.
|
16
|
+
* Remove `Class#json_creatable?` monkey patch.
|
17
|
+
* Remove deprecated `JSON.restore` method.
|
18
|
+
* Remove deprecated `JSON.unparse` method.
|
19
|
+
* Remove deprecated `JSON.fast_unparse` method.
|
20
|
+
* Remove deprecated `JSON.pretty_unparse` method.
|
21
|
+
* Remove deprecated `JSON::UnparserError` constant.
|
22
|
+
* Remove outdated `JSON::MissingUnicodeSupport` constant.
|
23
|
+
|
24
|
+
### 2025-03-12 (2.10.2)
|
25
|
+
|
26
|
+
* Fix a potential crash in the C extension parser.
|
27
|
+
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` unadvertently changed it.
|
28
|
+
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
|
29
|
+
* Ensure parser error snippets are valid UTF-8.
|
30
|
+
* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
|
31
|
+
|
3
32
|
### 2025-02-10 (2.10.1)
|
4
33
|
|
5
34
|
* Fix a compatibility issue with `MultiJson.dump(obj, pretty: true)`: `no implicit conversion of false into Proc (TypeError)`.
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
#include "ruby.h"
|
5
5
|
#include "ruby/encoding.h"
|
6
|
+
#include "../vendor/jeaiii-ltoa.h"
|
6
7
|
|
7
8
|
/* shims */
|
8
9
|
/* This is the fallback definition from Ruby 3.4 */
|
@@ -150,6 +151,13 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
|
150
151
|
}
|
151
152
|
}
|
152
153
|
|
154
|
+
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
155
|
+
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
156
|
+
{
|
157
|
+
fb->ptr[fb->len] = chr;
|
158
|
+
fb->len += 1;
|
159
|
+
}
|
160
|
+
|
153
161
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
154
162
|
{
|
155
163
|
const char *newstr = StringValuePtr(str);
|
@@ -167,25 +175,48 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
|
167
175
|
fb->len++;
|
168
176
|
}
|
169
177
|
|
170
|
-
static
|
178
|
+
static inline char *fbuffer_cursor(FBuffer *fb)
|
179
|
+
{
|
180
|
+
return fb->ptr + fb->len;
|
181
|
+
}
|
182
|
+
|
183
|
+
static inline void fbuffer_advance_to(FBuffer *fb, char *end)
|
171
184
|
{
|
172
|
-
|
173
|
-
long sign = number;
|
174
|
-
char* tmp = buf;
|
175
|
-
|
176
|
-
if (sign < 0) number = -number;
|
177
|
-
do *tmp-- = digits[number % 10]; while (number /= 10);
|
178
|
-
if (sign < 0) *tmp-- = '-';
|
179
|
-
return buf - tmp;
|
185
|
+
fb->len = end - fb->ptr;
|
180
186
|
}
|
181
187
|
|
182
|
-
|
188
|
+
/*
|
189
|
+
* Appends the decimal string representation of \a number into the buffer.
|
190
|
+
*/
|
183
191
|
static void fbuffer_append_long(FBuffer *fb, long number)
|
184
192
|
{
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
193
|
+
/*
|
194
|
+
* The jeaiii_ultoa() function produces digits left-to-right,
|
195
|
+
* allowing us to write directly into the buffer, but we don't know
|
196
|
+
* the number of resulting characters.
|
197
|
+
*
|
198
|
+
* We do know, however, that the `number` argument is always in the
|
199
|
+
* range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
|
200
|
+
* -4611686018427387904 to 4611686018427387903. The max number of chars
|
201
|
+
* generated is therefore 20 (including a potential sign character).
|
202
|
+
*/
|
203
|
+
|
204
|
+
static const int MAX_CHARS_FOR_LONG = 20;
|
205
|
+
|
206
|
+
fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
|
207
|
+
|
208
|
+
if (number < 0) {
|
209
|
+
fbuffer_append_reserved_char(fb, '-');
|
210
|
+
|
211
|
+
/*
|
212
|
+
* Since number is always > LONG_MIN, `-number` will not overflow
|
213
|
+
* and is always the positive abs() value.
|
214
|
+
*/
|
215
|
+
number = -number;
|
216
|
+
}
|
217
|
+
|
218
|
+
char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
|
219
|
+
fbuffer_advance_to(fb, end);
|
189
220
|
}
|
190
221
|
|
191
222
|
static VALUE fbuffer_finalize(FBuffer *fb)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "ruby.h"
|
2
2
|
#include "../fbuffer/fbuffer.h"
|
3
|
+
#include "../vendor/fpconv.c"
|
3
4
|
|
4
5
|
#include <math.h>
|
5
6
|
#include <ctype.h>
|
@@ -1054,8 +1055,9 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
1054
1055
|
{
|
1055
1056
|
double value = RFLOAT_VALUE(obj);
|
1056
1057
|
char allow_nan = state->allow_nan;
|
1057
|
-
if (
|
1058
|
-
|
1058
|
+
if (isinf(value) || isnan(value)) {
|
1059
|
+
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
|
1060
|
+
if (!allow_nan) {
|
1059
1061
|
if (state->strict && state->as_json) {
|
1060
1062
|
VALUE casted_obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
|
1061
1063
|
if (casted_obj != obj) {
|
@@ -1067,8 +1069,24 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
1067
1069
|
}
|
1068
1070
|
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
|
1069
1071
|
}
|
1072
|
+
|
1073
|
+
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
1074
|
+
fbuffer_append_str(buffer, tmp);
|
1075
|
+
return;
|
1070
1076
|
}
|
1071
|
-
|
1077
|
+
|
1078
|
+
/* This implementation writes directly into the buffer. We reserve
|
1079
|
+
* the 24 characters that fpconv_dtoa states as its maximum, plus
|
1080
|
+
* 2 more characters for the potential ".0" suffix.
|
1081
|
+
*/
|
1082
|
+
fbuffer_inc_capa(buffer, 26);
|
1083
|
+
char* d = buffer->ptr + buffer->len;
|
1084
|
+
int len = fpconv_dtoa(value, d);
|
1085
|
+
|
1086
|
+
/* fpconv_dtoa converts a float to its shortest string representation,
|
1087
|
+
* but it adds a ".0" if this is a plain integer.
|
1088
|
+
*/
|
1089
|
+
buffer->len += len;
|
1072
1090
|
}
|
1073
1091
|
|
1074
1092
|
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, JSON_Generator_State *state, VALUE obj)
|
@@ -4,7 +4,6 @@ require 'mkmf'
|
|
4
4
|
have_func("rb_enc_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
|
5
5
|
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
|
6
6
|
have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
|
7
|
-
have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby
|
8
7
|
have_func("strnlen", "string.h") # Missing on Solaris 10
|
9
8
|
|
10
9
|
append_cflags("-std=c99")
|
@@ -31,28 +31,15 @@ typedef unsigned char _Bool;
|
|
31
31
|
static VALUE mJSON, eNestingError, Encoding_UTF_8;
|
32
32
|
static VALUE CNaN, CInfinity, CMinusInfinity;
|
33
33
|
|
34
|
-
static ID
|
35
|
-
i_chr, i_deep_const_get, i_match, i_aset, i_aref,
|
34
|
+
static ID i_chr, i_aset, i_aref,
|
36
35
|
i_leftshift, i_new, i_try_convert, i_uminus, i_encode;
|
37
36
|
|
38
37
|
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
|
39
|
-
|
40
|
-
sym_decimal_class, sym_match_string;
|
38
|
+
sym_decimal_class, sym_on_load;
|
41
39
|
|
42
40
|
static int binary_encindex;
|
43
41
|
static int utf8_encindex;
|
44
42
|
|
45
|
-
#ifdef HAVE_RB_CATEGORY_WARN
|
46
|
-
# define json_deprecated(message) rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, message)
|
47
|
-
#else
|
48
|
-
# define json_deprecated(message) rb_warn(message)
|
49
|
-
#endif
|
50
|
-
|
51
|
-
static const char deprecated_create_additions_warning[] =
|
52
|
-
"JSON.load implicit support for `create_additions: true` is deprecated "
|
53
|
-
"and will be removed in 3.0, use JSON.unsafe_load or explicitly "
|
54
|
-
"pass `create_additions: true`";
|
55
|
-
|
56
43
|
#ifndef HAVE_RB_HASH_BULK_INSERT
|
57
44
|
// For TruffleRuby
|
58
45
|
void
|
@@ -341,6 +328,44 @@ static void rvalue_stack_eagerly_release(VALUE handle)
|
|
341
328
|
}
|
342
329
|
}
|
343
330
|
|
331
|
+
|
332
|
+
#ifndef HAVE_STRNLEN
|
333
|
+
static size_t strnlen(const char *s, size_t maxlen)
|
334
|
+
{
|
335
|
+
char *p;
|
336
|
+
return ((p = memchr(s, '\0', maxlen)) ? p - s : maxlen);
|
337
|
+
}
|
338
|
+
#endif
|
339
|
+
|
340
|
+
#define PARSE_ERROR_FRAGMENT_LEN 32
|
341
|
+
#ifdef RBIMPL_ATTR_NORETURN
|
342
|
+
RBIMPL_ATTR_NORETURN()
|
343
|
+
#endif
|
344
|
+
static void raise_parse_error(const char *format, const char *start)
|
345
|
+
{
|
346
|
+
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 1];
|
347
|
+
|
348
|
+
size_t len = start ? strnlen(start, PARSE_ERROR_FRAGMENT_LEN) : 0;
|
349
|
+
const char *ptr = start;
|
350
|
+
|
351
|
+
if (len == PARSE_ERROR_FRAGMENT_LEN) {
|
352
|
+
MEMCPY(buffer, start, char, PARSE_ERROR_FRAGMENT_LEN);
|
353
|
+
|
354
|
+
while (buffer[len - 1] >= 0x80 && buffer[len - 1] < 0xC0) { // Is continuation byte
|
355
|
+
len--;
|
356
|
+
}
|
357
|
+
|
358
|
+
if (buffer[len - 1] >= 0xC0) { // multibyte character start
|
359
|
+
len--;
|
360
|
+
}
|
361
|
+
|
362
|
+
buffer[len] = '\0';
|
363
|
+
ptr = (const char *)buffer;
|
364
|
+
}
|
365
|
+
|
366
|
+
rb_enc_raise(enc_utf8, rb_path2class("JSON::ParserError"), format, ptr);
|
367
|
+
}
|
368
|
+
|
344
369
|
/* unicode */
|
345
370
|
|
346
371
|
static const signed char digit_values[256] = {
|
@@ -362,21 +387,19 @@ static const signed char digit_values[256] = {
|
|
362
387
|
|
363
388
|
static uint32_t unescape_unicode(const unsigned char *p)
|
364
389
|
{
|
365
|
-
const uint32_t replacement_char = 0xFFFD;
|
366
|
-
|
367
390
|
signed char b;
|
368
391
|
uint32_t result = 0;
|
369
392
|
b = digit_values[p[0]];
|
370
|
-
if (b < 0)
|
393
|
+
if (b < 0) raise_parse_error("incomplete unicode character escape sequence at '%s'", (char *)p - 2);
|
371
394
|
result = (result << 4) | (unsigned char)b;
|
372
395
|
b = digit_values[p[1]];
|
373
|
-
if (b < 0)
|
396
|
+
if (b < 0) raise_parse_error("incomplete unicode character escape sequence at '%s'", (char *)p - 2);
|
374
397
|
result = (result << 4) | (unsigned char)b;
|
375
398
|
b = digit_values[p[2]];
|
376
|
-
if (b < 0)
|
399
|
+
if (b < 0) raise_parse_error("incomplete unicode character escape sequence at '%s'", (char *)p - 2);
|
377
400
|
result = (result << 4) | (unsigned char)b;
|
378
401
|
b = digit_values[p[3]];
|
379
|
-
if (b < 0)
|
402
|
+
if (b < 0) raise_parse_error("incomplete unicode character escape sequence at '%s'", (char *)p - 2);
|
380
403
|
result = (result << 4) | (unsigned char)b;
|
381
404
|
return result;
|
382
405
|
}
|
@@ -408,20 +431,15 @@ static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
|
|
408
431
|
}
|
409
432
|
|
410
433
|
typedef struct JSON_ParserStruct {
|
411
|
-
VALUE
|
412
|
-
VALUE object_class;
|
413
|
-
VALUE array_class;
|
434
|
+
VALUE on_load_proc;
|
414
435
|
VALUE decimal_class;
|
415
436
|
ID decimal_method_id;
|
416
|
-
VALUE match_string;
|
417
437
|
int max_nesting;
|
418
438
|
bool allow_nan;
|
419
439
|
bool allow_trailing_comma;
|
420
440
|
bool parsing_name;
|
421
441
|
bool symbolize_names;
|
422
442
|
bool freeze;
|
423
|
-
bool create_additions;
|
424
|
-
bool deprecated_create_additions;
|
425
443
|
} JSON_ParserConfig;
|
426
444
|
|
427
445
|
typedef struct JSON_ParserStateStruct {
|
@@ -440,34 +458,6 @@ typedef struct JSON_ParserStateStruct {
|
|
440
458
|
|
441
459
|
static const rb_data_type_t JSON_ParserConfig_type;
|
442
460
|
|
443
|
-
#ifndef HAVE_STRNLEN
|
444
|
-
static size_t strnlen(const char *s, size_t maxlen)
|
445
|
-
{
|
446
|
-
char *p;
|
447
|
-
return ((p = memchr(s, '\0', maxlen)) ? p - s : maxlen);
|
448
|
-
}
|
449
|
-
#endif
|
450
|
-
|
451
|
-
#define PARSE_ERROR_FRAGMENT_LEN 32
|
452
|
-
#ifdef RBIMPL_ATTR_NORETURN
|
453
|
-
RBIMPL_ATTR_NORETURN()
|
454
|
-
#endif
|
455
|
-
static void raise_parse_error(const char *format, const char *start)
|
456
|
-
{
|
457
|
-
char buffer[PARSE_ERROR_FRAGMENT_LEN + 1];
|
458
|
-
|
459
|
-
size_t len = start ? strnlen(start, PARSE_ERROR_FRAGMENT_LEN) : 0;
|
460
|
-
const char *ptr = start;
|
461
|
-
|
462
|
-
if (len == PARSE_ERROR_FRAGMENT_LEN) {
|
463
|
-
MEMCPY(buffer, start, char, PARSE_ERROR_FRAGMENT_LEN);
|
464
|
-
buffer[PARSE_ERROR_FRAGMENT_LEN] = '\0';
|
465
|
-
ptr = buffer;
|
466
|
-
}
|
467
|
-
|
468
|
-
rb_enc_raise(enc_utf8, rb_path2class("JSON::ParserError"), format, ptr);
|
469
|
-
}
|
470
|
-
|
471
461
|
static const bool whitespace[256] = {
|
472
462
|
[' '] = 1,
|
473
463
|
['\t'] = 1,
|
@@ -600,7 +590,7 @@ static VALUE json_string_unescape(JSON_ParserState *state, const char *string, c
|
|
600
590
|
buffer = RSTRING_PTR(result);
|
601
591
|
bufferStart = buffer;
|
602
592
|
|
603
|
-
while ((pe = memchr(pe, '\\', stringEnd - pe))) {
|
593
|
+
while (pe < stringEnd && (pe = memchr(pe, '\\', stringEnd - pe))) {
|
604
594
|
unescape = (char *) "?";
|
605
595
|
unescape_len = 1;
|
606
596
|
if (pe > p) {
|
@@ -761,18 +751,7 @@ static VALUE json_decode_float(JSON_ParserConfig *config, const char *start, con
|
|
761
751
|
|
762
752
|
static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig *config, long count)
|
763
753
|
{
|
764
|
-
VALUE array;
|
765
|
-
if (RB_UNLIKELY(config->array_class)) {
|
766
|
-
array = rb_class_new_instance(0, 0, config->array_class);
|
767
|
-
VALUE *items = rvalue_stack_peek(state->stack, count);
|
768
|
-
long index;
|
769
|
-
for (index = 0; index < count; index++) {
|
770
|
-
rb_funcall(array, i_leftshift, 1, items[index]);
|
771
|
-
}
|
772
|
-
} else {
|
773
|
-
array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count));
|
774
|
-
}
|
775
|
-
|
754
|
+
VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->stack, count));
|
776
755
|
rvalue_stack_pop(state->stack, count);
|
777
756
|
|
778
757
|
if (config->freeze) {
|
@@ -784,41 +763,11 @@ static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig
|
|
784
763
|
|
785
764
|
static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, long count)
|
786
765
|
{
|
787
|
-
VALUE object;
|
788
|
-
|
789
|
-
object = rb_class_new_instance(0, 0, config->object_class);
|
790
|
-
long index = 0;
|
791
|
-
VALUE *items = rvalue_stack_peek(state->stack, count);
|
792
|
-
while (index < count) {
|
793
|
-
VALUE name = items[index++];
|
794
|
-
VALUE value = items[index++];
|
795
|
-
rb_funcall(object, i_aset, 2, name, value);
|
796
|
-
}
|
797
|
-
} else {
|
798
|
-
object = rb_hash_new_capa(count);
|
799
|
-
rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), object);
|
800
|
-
}
|
766
|
+
VALUE object = rb_hash_new_capa(count);
|
767
|
+
rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), object);
|
801
768
|
|
802
769
|
rvalue_stack_pop(state->stack, count);
|
803
770
|
|
804
|
-
if (RB_UNLIKELY(config->create_additions)) {
|
805
|
-
VALUE klassname;
|
806
|
-
if (config->object_class) {
|
807
|
-
klassname = rb_funcall(object, i_aref, 1, config->create_id);
|
808
|
-
} else {
|
809
|
-
klassname = rb_hash_aref(object, config->create_id);
|
810
|
-
}
|
811
|
-
if (!NIL_P(klassname)) {
|
812
|
-
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
|
813
|
-
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
|
814
|
-
if (config->deprecated_create_additions) {
|
815
|
-
json_deprecated(deprecated_create_additions_warning);
|
816
|
-
}
|
817
|
-
object = rb_funcall(klass, i_json_create, 1, object);
|
818
|
-
}
|
819
|
-
}
|
820
|
-
}
|
821
|
-
|
822
771
|
if (config->freeze) {
|
823
772
|
RB_OBJ_FREEZE(object);
|
824
773
|
}
|
@@ -826,17 +775,6 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
|
|
826
775
|
return object;
|
827
776
|
}
|
828
777
|
|
829
|
-
static int match_i(VALUE regexp, VALUE klass, VALUE memo)
|
830
|
-
{
|
831
|
-
if (regexp == Qundef) return ST_STOP;
|
832
|
-
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0)) &&
|
833
|
-
RTEST(rb_funcall(regexp, i_match, 1, rb_ary_entry(memo, 0)))) {
|
834
|
-
rb_ary_push(memo, klass);
|
835
|
-
return ST_STOP;
|
836
|
-
}
|
837
|
-
return ST_CONTINUE;
|
838
|
-
}
|
839
|
-
|
840
778
|
static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfig *config, const char *start, const char *end, bool escaped, bool is_name)
|
841
779
|
{
|
842
780
|
VALUE string;
|
@@ -848,21 +786,17 @@ static inline VALUE json_decode_string(JSON_ParserState *state, JSON_ParserConfi
|
|
848
786
|
string = json_string_fastpath(state, start, end, is_name, intern, symbolize);
|
849
787
|
}
|
850
788
|
|
851
|
-
if (RB_UNLIKELY(config->create_additions && RTEST(config->match_string))) {
|
852
|
-
VALUE klass;
|
853
|
-
VALUE memo = rb_ary_new2(2);
|
854
|
-
rb_ary_push(memo, string);
|
855
|
-
rb_hash_foreach(config->match_string, match_i, memo);
|
856
|
-
klass = rb_ary_entry(memo, 1);
|
857
|
-
if (RTEST(klass)) {
|
858
|
-
string = rb_funcall(klass, i_json_create, 1, string);
|
859
|
-
}
|
860
|
-
}
|
861
|
-
|
862
789
|
return string;
|
863
790
|
}
|
864
791
|
|
865
|
-
|
792
|
+
static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value)
|
793
|
+
{
|
794
|
+
if (RB_UNLIKELY(config->on_load_proc)) {
|
795
|
+
value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil);
|
796
|
+
}
|
797
|
+
rvalue_stack_push(state->stack, value, &state->stack_handle, &state->stack);
|
798
|
+
return value;
|
799
|
+
}
|
866
800
|
|
867
801
|
static const bool string_scan[256] = {
|
868
802
|
// ASCII Control Characters
|
@@ -889,7 +823,7 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig
|
|
889
823
|
case '"': {
|
890
824
|
VALUE string = json_decode_string(state, config, start, state->cursor, escaped, is_name);
|
891
825
|
state->cursor++;
|
892
|
-
return
|
826
|
+
return json_push_value(state, config, string);
|
893
827
|
}
|
894
828
|
case '\\': {
|
895
829
|
state->cursor++;
|
@@ -923,7 +857,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
923
857
|
case 'n':
|
924
858
|
if ((state->end - state->cursor >= 4) && (memcmp(state->cursor, "null", 4) == 0)) {
|
925
859
|
state->cursor += 4;
|
926
|
-
return
|
860
|
+
return json_push_value(state, config, Qnil);
|
927
861
|
}
|
928
862
|
|
929
863
|
raise_parse_error("unexpected token at '%s'", state->cursor);
|
@@ -931,7 +865,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
931
865
|
case 't':
|
932
866
|
if ((state->end - state->cursor >= 4) && (memcmp(state->cursor, "true", 4) == 0)) {
|
933
867
|
state->cursor += 4;
|
934
|
-
return
|
868
|
+
return json_push_value(state, config, Qtrue);
|
935
869
|
}
|
936
870
|
|
937
871
|
raise_parse_error("unexpected token at '%s'", state->cursor);
|
@@ -940,7 +874,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
940
874
|
// Note: memcmp with a small power of two compile to an integer comparison
|
941
875
|
if ((state->end - state->cursor >= 5) && (memcmp(state->cursor + 1, "alse", 4) == 0)) {
|
942
876
|
state->cursor += 5;
|
943
|
-
return
|
877
|
+
return json_push_value(state, config, Qfalse);
|
944
878
|
}
|
945
879
|
|
946
880
|
raise_parse_error("unexpected token at '%s'", state->cursor);
|
@@ -949,7 +883,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
949
883
|
// Note: memcmp with a small power of two compile to an integer comparison
|
950
884
|
if (config->allow_nan && (state->end - state->cursor >= 3) && (memcmp(state->cursor + 1, "aN", 2) == 0)) {
|
951
885
|
state->cursor += 3;
|
952
|
-
return
|
886
|
+
return json_push_value(state, config, CNaN);
|
953
887
|
}
|
954
888
|
|
955
889
|
raise_parse_error("unexpected token at '%s'", state->cursor);
|
@@ -957,7 +891,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
957
891
|
case 'I':
|
958
892
|
if (config->allow_nan && (state->end - state->cursor >= 8) && (memcmp(state->cursor, "Infinity", 8) == 0)) {
|
959
893
|
state->cursor += 8;
|
960
|
-
return
|
894
|
+
return json_push_value(state, config, CInfinity);
|
961
895
|
}
|
962
896
|
|
963
897
|
raise_parse_error("unexpected token at '%s'", state->cursor);
|
@@ -967,7 +901,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
967
901
|
if ((state->end - state->cursor >= 9) && (memcmp(state->cursor + 1, "Infinity", 8) == 0)) {
|
968
902
|
if (config->allow_nan) {
|
969
903
|
state->cursor += 9;
|
970
|
-
return
|
904
|
+
return json_push_value(state, config, CMinusInfinity);
|
971
905
|
} else {
|
972
906
|
raise_parse_error("unexpected token at '%s'", state->cursor);
|
973
907
|
}
|
@@ -1024,9 +958,9 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
1024
958
|
}
|
1025
959
|
|
1026
960
|
if (integer) {
|
1027
|
-
return
|
961
|
+
return json_push_value(state, config, json_decode_integer(start, state->cursor));
|
1028
962
|
}
|
1029
|
-
return
|
963
|
+
return json_push_value(state, config, json_decode_float(config, start, state->cursor));
|
1030
964
|
}
|
1031
965
|
case '"': {
|
1032
966
|
// %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"}
|
@@ -1040,7 +974,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
1040
974
|
|
1041
975
|
if ((state->cursor < state->end) && (*state->cursor == ']')) {
|
1042
976
|
state->cursor++;
|
1043
|
-
return
|
977
|
+
return json_push_value(state, config, json_decode_array(state, config, 0));
|
1044
978
|
} else {
|
1045
979
|
state->current_nesting++;
|
1046
980
|
if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) {
|
@@ -1059,7 +993,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
1059
993
|
long count = state->stack->head - stack_head;
|
1060
994
|
state->current_nesting--;
|
1061
995
|
state->in_array--;
|
1062
|
-
return
|
996
|
+
return json_push_value(state, config, json_decode_array(state, config, count));
|
1063
997
|
}
|
1064
998
|
|
1065
999
|
if (*state->cursor == ',') {
|
@@ -1086,7 +1020,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
1086
1020
|
|
1087
1021
|
if ((state->cursor < state->end) && (*state->cursor == '}')) {
|
1088
1022
|
state->cursor++;
|
1089
|
-
return
|
1023
|
+
return json_push_value(state, config, json_decode_object(state, config, 0));
|
1090
1024
|
} else {
|
1091
1025
|
state->current_nesting++;
|
1092
1026
|
if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) {
|
@@ -1115,7 +1049,7 @@ static VALUE json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config)
|
|
1115
1049
|
state->cursor++;
|
1116
1050
|
state->current_nesting--;
|
1117
1051
|
long count = state->stack->head - stack_head;
|
1118
|
-
return
|
1052
|
+
return json_push_value(state, config, json_decode_object(state, config, count));
|
1119
1053
|
}
|
1120
1054
|
|
1121
1055
|
if (*state->cursor == ',') {
|
@@ -1203,10 +1137,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
1203
1137
|
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
1204
1138
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
1205
1139
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
1206
|
-
else if (key ==
|
1207
|
-
else if (key == sym_object_class) { config->object_class = RTEST(val) ? val : Qfalse; }
|
1208
|
-
else if (key == sym_array_class) { config->array_class = RTEST(val) ? val : Qfalse; }
|
1209
|
-
else if (key == sym_match_string) { config->match_string = RTEST(val) ? val : Qfalse; }
|
1140
|
+
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
1210
1141
|
else if (key == sym_decimal_class) {
|
1211
1142
|
if (RTEST(val)) {
|
1212
1143
|
if (rb_respond_to(val, i_try_convert)) {
|
@@ -1236,15 +1167,6 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
1236
1167
|
}
|
1237
1168
|
}
|
1238
1169
|
}
|
1239
|
-
else if (key == sym_create_additions) {
|
1240
|
-
if (NIL_P(val)) {
|
1241
|
-
config->create_additions = true;
|
1242
|
-
config->deprecated_create_additions = true;
|
1243
|
-
} else {
|
1244
|
-
config->create_additions = RTEST(val);
|
1245
|
-
config->deprecated_create_additions = false;
|
1246
|
-
}
|
1247
|
-
}
|
1248
1170
|
|
1249
1171
|
return ST_CONTINUE;
|
1250
1172
|
}
|
@@ -1259,16 +1181,6 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
|
|
1259
1181
|
// We assume in most cases few keys are set so it's faster to go over
|
1260
1182
|
// the provided keys than to check all possible keys.
|
1261
1183
|
rb_hash_foreach(opts, parser_config_init_i, (VALUE)config);
|
1262
|
-
|
1263
|
-
if (config->symbolize_names && config->create_additions) {
|
1264
|
-
rb_raise(rb_eArgError,
|
1265
|
-
"options :symbolize_names and :create_additions cannot be "
|
1266
|
-
" used in conjunction");
|
1267
|
-
}
|
1268
|
-
|
1269
|
-
if (config->create_additions && !config->create_id) {
|
1270
|
-
config->create_id = rb_funcall(mJSON, i_create_id, 0);
|
1271
|
-
}
|
1272
1184
|
}
|
1273
1185
|
|
1274
1186
|
}
|
@@ -1293,15 +1205,6 @@ static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
|
|
1293
1205
|
* (keys) in a JSON object. Otherwise strings are returned, which is
|
1294
1206
|
* also the default. It's not possible to use this option in
|
1295
1207
|
* conjunction with the *create_additions* option.
|
1296
|
-
* * *create_additions*: If set to false, the Parser doesn't create
|
1297
|
-
* additions even if a matching class and create_id was found. This option
|
1298
|
-
* defaults to false.
|
1299
|
-
* * *object_class*: Defaults to Hash. If another type is provided, it will be used
|
1300
|
-
* instead of Hash to represent JSON objects. The type must respond to
|
1301
|
-
* +new+ without arguments, and return an object that respond to +[]=+.
|
1302
|
-
* * *array_class*: Defaults to Array If another type is provided, it will be used
|
1303
|
-
* instead of Hash to represent JSON arrays. The type must respond to
|
1304
|
-
* +new+ without arguments, and return an object that respond to +<<+.
|
1305
1208
|
* * *decimal_class*: Specifies which class to use instead of the default
|
1306
1209
|
* (Float) when parsing decimal numbers. This class must accept a single
|
1307
1210
|
* string argument in its constructor.
|
@@ -1312,11 +1215,7 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
|
1312
1215
|
|
1313
1216
|
parser_config_init(config, opts);
|
1314
1217
|
|
1315
|
-
RB_OBJ_WRITTEN(self, Qundef, config->create_id);
|
1316
|
-
RB_OBJ_WRITTEN(self, Qundef, config->object_class);
|
1317
|
-
RB_OBJ_WRITTEN(self, Qundef, config->array_class);
|
1318
1218
|
RB_OBJ_WRITTEN(self, Qundef, config->decimal_class);
|
1319
|
-
RB_OBJ_WRITTEN(self, Qundef, config->match_string);
|
1320
1219
|
|
1321
1220
|
return self;
|
1322
1221
|
}
|
@@ -1379,11 +1278,8 @@ static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
|
|
1379
1278
|
static void JSON_ParserConfig_mark(void *ptr)
|
1380
1279
|
{
|
1381
1280
|
JSON_ParserConfig *config = ptr;
|
1382
|
-
rb_gc_mark(config->
|
1383
|
-
rb_gc_mark(config->object_class);
|
1384
|
-
rb_gc_mark(config->array_class);
|
1281
|
+
rb_gc_mark(config->on_load_proc);
|
1385
1282
|
rb_gc_mark(config->decimal_class);
|
1386
|
-
rb_gc_mark(config->match_string);
|
1387
1283
|
}
|
1388
1284
|
|
1389
1285
|
static void JSON_ParserConfig_free(void *ptr)
|
@@ -1451,19 +1347,10 @@ void Init_parser(void)
|
|
1451
1347
|
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
|
1452
1348
|
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
1453
1349
|
sym_freeze = ID2SYM(rb_intern("freeze"));
|
1454
|
-
|
1455
|
-
sym_create_id = ID2SYM(rb_intern("create_id"));
|
1456
|
-
sym_object_class = ID2SYM(rb_intern("object_class"));
|
1457
|
-
sym_array_class = ID2SYM(rb_intern("array_class"));
|
1350
|
+
sym_on_load = ID2SYM(rb_intern("on_load"));
|
1458
1351
|
sym_decimal_class = ID2SYM(rb_intern("decimal_class"));
|
1459
|
-
sym_match_string = ID2SYM(rb_intern("match_string"));
|
1460
1352
|
|
1461
|
-
i_create_id = rb_intern("create_id");
|
1462
|
-
i_json_creatable_p = rb_intern("json_creatable?");
|
1463
|
-
i_json_create = rb_intern("json_create");
|
1464
1353
|
i_chr = rb_intern("chr");
|
1465
|
-
i_match = rb_intern("match");
|
1466
|
-
i_deep_const_get = rb_intern("deep_const_get");
|
1467
1354
|
i_aset = rb_intern("[]=");
|
1468
1355
|
i_aref = rb_intern("[]");
|
1469
1356
|
i_leftshift = rb_intern("<<");
|