json 2.13.2 → 2.18.1
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 +81 -8
- data/LEGAL +12 -0
- data/README.md +19 -1
- data/ext/json/ext/fbuffer/fbuffer.h +35 -56
- data/ext/json/ext/generator/extconf.rb +1 -1
- data/ext/json/ext/generator/generator.c +326 -264
- data/ext/json/ext/json.h +101 -0
- data/ext/json/ext/parser/extconf.rb +2 -1
- data/ext/json/ext/parser/parser.c +564 -444
- data/ext/json/ext/simd/simd.h +42 -12
- data/ext/json/ext/vendor/fpconv.c +13 -12
- data/ext/json/ext/vendor/ryu.h +819 -0
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/string.rb +35 -0
- data/lib/json/common.rb +60 -23
- data/lib/json/ext/generator/state.rb +11 -14
- data/lib/json/generic_object.rb +0 -8
- data/lib/json/truffle_ruby/generator.rb +113 -63
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +44 -1
- metadata +6 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#include "
|
|
1
|
+
#include "../json.h"
|
|
2
2
|
#include "../fbuffer/fbuffer.h"
|
|
3
3
|
#include "../vendor/fpconv.c"
|
|
4
4
|
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
/* ruby api and some helpers */
|
|
11
11
|
|
|
12
|
+
enum duplicate_key_action {
|
|
13
|
+
JSON_DEPRECATED = 0,
|
|
14
|
+
JSON_IGNORE,
|
|
15
|
+
JSON_RAISE,
|
|
16
|
+
};
|
|
17
|
+
|
|
12
18
|
typedef struct JSON_Generator_StateStruct {
|
|
13
19
|
VALUE indent;
|
|
14
20
|
VALUE space;
|
|
@@ -21,20 +27,19 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
21
27
|
long depth;
|
|
22
28
|
long buffer_initial_length;
|
|
23
29
|
|
|
30
|
+
enum duplicate_key_action on_duplicate_key;
|
|
31
|
+
|
|
32
|
+
bool as_json_single_arg;
|
|
24
33
|
bool allow_nan;
|
|
25
34
|
bool ascii_only;
|
|
26
35
|
bool script_safe;
|
|
27
36
|
bool strict;
|
|
28
37
|
} JSON_Generator_State;
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
#define RB_UNLIKELY(cond) (cond)
|
|
32
|
-
#endif
|
|
33
|
-
|
|
34
|
-
static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
39
|
+
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
35
40
|
|
|
36
|
-
static ID i_to_s, i_to_json, i_new,
|
|
37
|
-
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
|
|
41
|
+
static ID i_to_s, i_to_json, i_new, i_encode;
|
|
42
|
+
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key,
|
|
38
43
|
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
|
39
44
|
|
|
40
45
|
|
|
@@ -55,8 +60,11 @@ struct generate_json_data {
|
|
|
55
60
|
JSON_Generator_State *state;
|
|
56
61
|
VALUE obj;
|
|
57
62
|
generator_func func;
|
|
63
|
+
long depth;
|
|
58
64
|
};
|
|
59
65
|
|
|
66
|
+
static SIMD_Implementation simd_impl;
|
|
67
|
+
|
|
60
68
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
61
69
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
|
62
70
|
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -76,23 +84,18 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
76
84
|
|
|
77
85
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
RBIMPL_ATTR_NORETURN()
|
|
81
|
-
#endif
|
|
82
|
-
static void raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
87
|
+
NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
83
88
|
{
|
|
89
|
+
rb_enc_associate_index(str, utf8_encindex);
|
|
84
90
|
VALUE exc = rb_exc_new_str(eGeneratorError, str);
|
|
85
91
|
rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
|
|
86
92
|
rb_exc_raise(exc);
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
#ifdef RBIMPL_ATTR_NORETURN
|
|
90
|
-
RBIMPL_ATTR_NORETURN()
|
|
91
|
-
#endif
|
|
92
95
|
#ifdef RBIMPL_ATTR_FORMAT
|
|
93
96
|
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
|
|
94
97
|
#endif
|
|
95
|
-
static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
98
|
+
NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
96
99
|
{
|
|
97
100
|
va_list args;
|
|
98
101
|
va_start(args, fmt);
|
|
@@ -127,18 +130,12 @@ typedef struct _search_state {
|
|
|
127
130
|
#endif /* HAVE_SIMD */
|
|
128
131
|
} search_state;
|
|
129
132
|
|
|
130
|
-
|
|
131
|
-
#define FORCE_INLINE __attribute__((always_inline))
|
|
132
|
-
#else
|
|
133
|
-
#define FORCE_INLINE
|
|
134
|
-
#endif
|
|
135
|
-
|
|
136
|
-
static inline FORCE_INLINE void search_flush(search_state *search)
|
|
133
|
+
ALWAYS_INLINE(static) void search_flush(search_state *search)
|
|
137
134
|
{
|
|
138
135
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
|
139
136
|
// escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
|
|
140
|
-
// For back-to-back characters that need to be escaped,
|
|
141
|
-
// will be called just before calling escape_UTF8_char_basic. There will be no
|
|
137
|
+
// For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
|
|
138
|
+
// will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
|
|
142
139
|
// consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
|
|
143
140
|
// nothing needs to be flushed, we can save a few memory references with this conditional.
|
|
144
141
|
if (search->ptr > search->cursor) {
|
|
@@ -160,8 +157,6 @@ static const unsigned char escape_table_basic[256] = {
|
|
|
160
157
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
161
158
|
};
|
|
162
159
|
|
|
163
|
-
static unsigned char (*search_escape_basic_impl)(search_state *);
|
|
164
|
-
|
|
165
160
|
static inline unsigned char search_escape_basic(search_state *search)
|
|
166
161
|
{
|
|
167
162
|
while (search->ptr < search->end) {
|
|
@@ -176,7 +171,7 @@ static inline unsigned char search_escape_basic(search_state *search)
|
|
|
176
171
|
return 0;
|
|
177
172
|
}
|
|
178
173
|
|
|
179
|
-
static
|
|
174
|
+
ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
180
175
|
{
|
|
181
176
|
const unsigned char ch = (unsigned char)*search->ptr;
|
|
182
177
|
switch (ch) {
|
|
@@ -217,11 +212,39 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
|
|
|
217
212
|
* Everything else (should be UTF-8) is just passed through and
|
|
218
213
|
* appended to the result.
|
|
219
214
|
*/
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
#if defined(HAVE_SIMD_NEON)
|
|
218
|
+
static inline unsigned char search_escape_basic_neon(search_state *search);
|
|
219
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
220
|
+
static inline unsigned char search_escape_basic_sse2(search_state *search);
|
|
221
|
+
#endif
|
|
222
|
+
|
|
223
|
+
static inline unsigned char search_escape_basic(search_state *search);
|
|
224
|
+
|
|
220
225
|
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
221
226
|
{
|
|
222
|
-
|
|
227
|
+
#ifdef HAVE_SIMD
|
|
228
|
+
#if defined(HAVE_SIMD_NEON)
|
|
229
|
+
while (search_escape_basic_neon(search)) {
|
|
223
230
|
escape_UTF8_char_basic(search);
|
|
224
231
|
}
|
|
232
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
233
|
+
if (simd_impl == SIMD_SSE2) {
|
|
234
|
+
while (search_escape_basic_sse2(search)) {
|
|
235
|
+
escape_UTF8_char_basic(search);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
while (search_escape_basic(search)) {
|
|
240
|
+
escape_UTF8_char_basic(search);
|
|
241
|
+
}
|
|
242
|
+
#endif
|
|
243
|
+
#else
|
|
244
|
+
while (search_escape_basic(search)) {
|
|
245
|
+
escape_UTF8_char_basic(search);
|
|
246
|
+
}
|
|
247
|
+
#endif /* HAVE_SIMD */
|
|
225
248
|
}
|
|
226
249
|
|
|
227
250
|
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
@@ -263,8 +286,10 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
263
286
|
|
|
264
287
|
#ifdef HAVE_SIMD
|
|
265
288
|
|
|
266
|
-
static
|
|
289
|
+
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
267
290
|
{
|
|
291
|
+
RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
|
|
292
|
+
|
|
268
293
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
269
294
|
search_flush(search);
|
|
270
295
|
|
|
@@ -274,19 +299,25 @@ static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsi
|
|
|
274
299
|
char *s = (buf->ptr + buf->len);
|
|
275
300
|
|
|
276
301
|
// Pad the buffer with dummy characters that won't need escaping.
|
|
277
|
-
// This seem
|
|
278
|
-
|
|
302
|
+
// This seem wasteful at first sight, but memset of vector length is very fast.
|
|
303
|
+
// This is a space as it can be directly represented as an immediate on AArch64.
|
|
304
|
+
memset(s, ' ', vec_len);
|
|
279
305
|
|
|
280
306
|
// Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
|
|
281
307
|
// to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
|
|
282
|
-
|
|
308
|
+
if (vec_len == 16) {
|
|
309
|
+
RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
|
|
310
|
+
json_fast_memcpy16(s, search->ptr, len);
|
|
311
|
+
} else {
|
|
312
|
+
MEMCPY(s, search->ptr, char, len);
|
|
313
|
+
}
|
|
283
314
|
|
|
284
315
|
return s;
|
|
285
316
|
}
|
|
286
317
|
|
|
287
318
|
#ifdef HAVE_SIMD_NEON
|
|
288
319
|
|
|
289
|
-
static
|
|
320
|
+
ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
|
|
290
321
|
{
|
|
291
322
|
uint64_t mask = search->matches_mask;
|
|
292
323
|
uint32_t index = trailing_zeros64(mask) >> 2;
|
|
@@ -400,7 +431,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
|
|
|
400
431
|
|
|
401
432
|
#ifdef HAVE_SIMD_SSE2
|
|
402
433
|
|
|
403
|
-
static
|
|
434
|
+
ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
|
|
404
435
|
{
|
|
405
436
|
int mask = search->matches_mask;
|
|
406
437
|
int index = trailing_zeros(mask);
|
|
@@ -424,7 +455,7 @@ static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
|
|
|
424
455
|
#define TARGET_SSE2
|
|
425
456
|
#endif
|
|
426
457
|
|
|
427
|
-
static
|
|
458
|
+
ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
|
|
428
459
|
{
|
|
429
460
|
if (RB_UNLIKELY(search->has_matches)) {
|
|
430
461
|
// There are more matches if search->matches_mask > 0.
|
|
@@ -835,18 +866,6 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
835
866
|
return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
|
|
836
867
|
}
|
|
837
868
|
|
|
838
|
-
/*
|
|
839
|
-
* call-seq: String.included(modul)
|
|
840
|
-
*
|
|
841
|
-
* Extends _modul_ with the String::Extend module.
|
|
842
|
-
*/
|
|
843
|
-
static VALUE mString_included_s(VALUE self, VALUE modul)
|
|
844
|
-
{
|
|
845
|
-
VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
|
|
846
|
-
rb_call_super(1, &modul);
|
|
847
|
-
return result;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
869
|
/*
|
|
851
870
|
* call-seq: to_json(*)
|
|
852
871
|
*
|
|
@@ -861,51 +880,6 @@ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
861
880
|
return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
|
|
862
881
|
}
|
|
863
882
|
|
|
864
|
-
/*
|
|
865
|
-
* call-seq: to_json_raw_object()
|
|
866
|
-
*
|
|
867
|
-
* This method creates a raw object hash, that can be nested into
|
|
868
|
-
* other data structures and will be generated as a raw string. This
|
|
869
|
-
* method should be used, if you want to convert raw strings to JSON
|
|
870
|
-
* instead of UTF-8 strings, e. g. binary data.
|
|
871
|
-
*/
|
|
872
|
-
static VALUE mString_to_json_raw_object(VALUE self)
|
|
873
|
-
{
|
|
874
|
-
VALUE ary;
|
|
875
|
-
VALUE result = rb_hash_new();
|
|
876
|
-
rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
|
|
877
|
-
ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
|
|
878
|
-
rb_hash_aset(result, rb_utf8_str_new_lit("raw"), ary);
|
|
879
|
-
return result;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/*
|
|
883
|
-
* call-seq: to_json_raw(*args)
|
|
884
|
-
*
|
|
885
|
-
* This method creates a JSON text from the result of a call to
|
|
886
|
-
* to_json_raw_object of this String.
|
|
887
|
-
*/
|
|
888
|
-
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
|
|
889
|
-
{
|
|
890
|
-
VALUE obj = mString_to_json_raw_object(self);
|
|
891
|
-
Check_Type(obj, T_HASH);
|
|
892
|
-
return mHash_to_json(argc, argv, obj);
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
/*
|
|
896
|
-
* call-seq: json_create(o)
|
|
897
|
-
*
|
|
898
|
-
* Raw Strings are JSON Objects (the raw bytes are stored in an array for the
|
|
899
|
-
* key "raw"). The Ruby String can be created by this module method.
|
|
900
|
-
*/
|
|
901
|
-
static VALUE mString_Extend_json_create(VALUE self, VALUE o)
|
|
902
|
-
{
|
|
903
|
-
VALUE ary;
|
|
904
|
-
Check_Type(o, T_HASH);
|
|
905
|
-
ary = rb_hash_aref(o, rb_str_new2("raw"));
|
|
906
|
-
return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
|
|
907
|
-
}
|
|
908
|
-
|
|
909
883
|
/*
|
|
910
884
|
* call-seq: to_json(*)
|
|
911
885
|
*
|
|
@@ -989,11 +963,6 @@ static size_t State_memsize(const void *ptr)
|
|
|
989
963
|
return sizeof(JSON_Generator_State);
|
|
990
964
|
}
|
|
991
965
|
|
|
992
|
-
#ifndef HAVE_RB_EXT_RACTOR_SAFE
|
|
993
|
-
# undef RUBY_TYPED_FROZEN_SHAREABLE
|
|
994
|
-
# define RUBY_TYPED_FROZEN_SHAREABLE 0
|
|
995
|
-
#endif
|
|
996
|
-
|
|
997
966
|
static const rb_data_type_t JSON_Generator_State_type = {
|
|
998
967
|
"JSON/Generator/State",
|
|
999
968
|
{
|
|
@@ -1035,18 +1004,24 @@ static void vstate_spill(struct generate_json_data *data)
|
|
|
1035
1004
|
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
|
1036
1005
|
}
|
|
1037
1006
|
|
|
1038
|
-
static inline VALUE
|
|
1007
|
+
static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
|
|
1039
1008
|
{
|
|
1040
1009
|
if (RB_UNLIKELY(!data->vstate)) {
|
|
1041
1010
|
vstate_spill(data);
|
|
1042
1011
|
}
|
|
1043
|
-
|
|
1012
|
+
GET_STATE(data->vstate);
|
|
1013
|
+
state->depth = data->depth;
|
|
1014
|
+
VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
|
|
1015
|
+
// no need to restore state->depth, vstate is just a temporary State
|
|
1016
|
+
return tmp;
|
|
1044
1017
|
}
|
|
1045
1018
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
};
|
|
1019
|
+
static VALUE
|
|
1020
|
+
json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
|
|
1021
|
+
{
|
|
1022
|
+
VALUE proc_args[2] = {object, is_key};
|
|
1023
|
+
return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
|
|
1024
|
+
}
|
|
1050
1025
|
|
|
1051
1026
|
static VALUE
|
|
1052
1027
|
convert_string_subclass(VALUE key)
|
|
@@ -1063,6 +1038,145 @@ convert_string_subclass(VALUE key)
|
|
|
1063
1038
|
return key_to_s;
|
|
1064
1039
|
}
|
|
1065
1040
|
|
|
1041
|
+
static bool enc_utf8_compatible_p(int enc_idx)
|
|
1042
|
+
{
|
|
1043
|
+
if (enc_idx == usascii_encindex) return true;
|
|
1044
|
+
if (enc_idx == utf8_encindex) return true;
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
static VALUE encode_json_string_try(VALUE str)
|
|
1049
|
+
{
|
|
1050
|
+
return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
1054
|
+
{
|
|
1055
|
+
raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
|
|
1056
|
+
return Qundef;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
static inline bool valid_json_string_p(VALUE str)
|
|
1060
|
+
{
|
|
1061
|
+
int coderange = rb_enc_str_coderange(str);
|
|
1062
|
+
|
|
1063
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
|
|
1068
|
+
return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
return false;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
1075
|
+
{
|
|
1076
|
+
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
1077
|
+
return str;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
|
|
1081
|
+
VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
|
|
1082
|
+
if (coerced_str != str) {
|
|
1083
|
+
if (RB_TYPE_P(coerced_str, T_STRING)) {
|
|
1084
|
+
if (!valid_json_string_p(coerced_str)) {
|
|
1085
|
+
raise_generator_error(str, "source sequence is illegal/malformed utf-8");
|
|
1086
|
+
}
|
|
1087
|
+
} else {
|
|
1088
|
+
// as_json could return another type than T_STRING
|
|
1089
|
+
if (is_key) {
|
|
1090
|
+
raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return coerced_str;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
|
|
1099
|
+
VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
|
|
1100
|
+
switch (rb_enc_str_coderange(utf8_string)) {
|
|
1101
|
+
case ENC_CODERANGE_7BIT:
|
|
1102
|
+
return utf8_string;
|
|
1103
|
+
case ENC_CODERANGE_VALID:
|
|
1104
|
+
// For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
|
|
1105
|
+
// TODO: Raise in 3.0.0
|
|
1106
|
+
rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
|
|
1107
|
+
return utf8_string;
|
|
1108
|
+
break;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1116
|
+
{
|
|
1117
|
+
fbuffer_append_char(buffer, '"');
|
|
1118
|
+
|
|
1119
|
+
long len;
|
|
1120
|
+
search_state search;
|
|
1121
|
+
search.buffer = buffer;
|
|
1122
|
+
RSTRING_GETMEM(obj, search.ptr, len);
|
|
1123
|
+
search.cursor = search.ptr;
|
|
1124
|
+
search.end = search.ptr + len;
|
|
1125
|
+
|
|
1126
|
+
#ifdef HAVE_SIMD
|
|
1127
|
+
search.matches_mask = 0;
|
|
1128
|
+
search.has_matches = false;
|
|
1129
|
+
search.chunk_base = NULL;
|
|
1130
|
+
search.chunk_end = NULL;
|
|
1131
|
+
#endif /* HAVE_SIMD */
|
|
1132
|
+
|
|
1133
|
+
switch (rb_enc_str_coderange(obj)) {
|
|
1134
|
+
case ENC_CODERANGE_7BIT:
|
|
1135
|
+
case ENC_CODERANGE_VALID:
|
|
1136
|
+
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
1137
|
+
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
|
1138
|
+
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
|
1139
|
+
convert_UTF8_to_script_safe_JSON(&search);
|
|
1140
|
+
} else {
|
|
1141
|
+
convert_UTF8_to_JSON(&search);
|
|
1142
|
+
}
|
|
1143
|
+
break;
|
|
1144
|
+
default:
|
|
1145
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
fbuffer_append_char(buffer, '"');
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1152
|
+
{
|
|
1153
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
1154
|
+
raw_generate_json_string(buffer, data, obj);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
struct hash_foreach_arg {
|
|
1158
|
+
VALUE hash;
|
|
1159
|
+
struct generate_json_data *data;
|
|
1160
|
+
int first_key_type;
|
|
1161
|
+
bool first;
|
|
1162
|
+
bool mixed_keys_encountered;
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
NOINLINE(static) void
|
|
1166
|
+
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
1167
|
+
{
|
|
1168
|
+
if (arg->mixed_keys_encountered) {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
arg->mixed_keys_encountered = true;
|
|
1172
|
+
|
|
1173
|
+
JSON_Generator_State *state = arg->data->state;
|
|
1174
|
+
if (state->on_duplicate_key != JSON_IGNORE) {
|
|
1175
|
+
VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
|
|
1176
|
+
rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1066
1180
|
static int
|
|
1067
1181
|
json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1068
1182
|
{
|
|
@@ -1072,22 +1186,34 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1072
1186
|
FBuffer *buffer = data->buffer;
|
|
1073
1187
|
JSON_Generator_State *state = data->state;
|
|
1074
1188
|
|
|
1075
|
-
long depth =
|
|
1076
|
-
int
|
|
1189
|
+
long depth = data->depth;
|
|
1190
|
+
int key_type = rb_type(key);
|
|
1191
|
+
|
|
1192
|
+
if (arg->first) {
|
|
1193
|
+
arg->first = false;
|
|
1194
|
+
arg->first_key_type = key_type;
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
fbuffer_append_char(buffer, ',');
|
|
1198
|
+
}
|
|
1077
1199
|
|
|
1078
|
-
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
|
|
1079
1200
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1080
1201
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1081
1202
|
}
|
|
1082
1203
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1083
|
-
|
|
1084
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1085
|
-
}
|
|
1204
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1086
1205
|
}
|
|
1087
1206
|
|
|
1088
1207
|
VALUE key_to_s;
|
|
1089
|
-
|
|
1208
|
+
bool as_json_called = false;
|
|
1209
|
+
|
|
1210
|
+
start:
|
|
1211
|
+
switch (key_type) {
|
|
1090
1212
|
case T_STRING:
|
|
1213
|
+
if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
|
|
1214
|
+
json_inspect_hash_with_mixed_keys(arg);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1091
1217
|
if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
|
|
1092
1218
|
key_to_s = key;
|
|
1093
1219
|
} else {
|
|
@@ -1095,15 +1221,31 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1095
1221
|
}
|
|
1096
1222
|
break;
|
|
1097
1223
|
case T_SYMBOL:
|
|
1224
|
+
if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
|
|
1225
|
+
json_inspect_hash_with_mixed_keys(arg);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1098
1228
|
key_to_s = rb_sym2str(key);
|
|
1099
1229
|
break;
|
|
1100
1230
|
default:
|
|
1231
|
+
if (data->state->strict) {
|
|
1232
|
+
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1233
|
+
key = json_call_as_json(data->state, key, Qtrue);
|
|
1234
|
+
key_type = rb_type(key);
|
|
1235
|
+
as_json_called = true;
|
|
1236
|
+
goto start;
|
|
1237
|
+
} else {
|
|
1238
|
+
raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1101
1241
|
key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
|
|
1102
1242
|
break;
|
|
1103
1243
|
}
|
|
1104
1244
|
|
|
1245
|
+
key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
|
|
1246
|
+
|
|
1105
1247
|
if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
|
|
1106
|
-
|
|
1248
|
+
raw_generate_json_string(buffer, data, key_to_s);
|
|
1107
1249
|
} else {
|
|
1108
1250
|
generate_json(buffer, data, key_to_s);
|
|
1109
1251
|
}
|
|
@@ -1112,46 +1254,43 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1112
1254
|
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
|
|
1113
1255
|
generate_json(buffer, data, val);
|
|
1114
1256
|
|
|
1115
|
-
arg->iter++;
|
|
1116
1257
|
return ST_CONTINUE;
|
|
1117
1258
|
}
|
|
1118
1259
|
|
|
1119
1260
|
static inline long increase_depth(struct generate_json_data *data)
|
|
1120
1261
|
{
|
|
1121
1262
|
JSON_Generator_State *state = data->state;
|
|
1122
|
-
long depth = ++
|
|
1263
|
+
long depth = ++data->depth;
|
|
1123
1264
|
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
|
1124
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", --
|
|
1265
|
+
rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
|
|
1125
1266
|
}
|
|
1126
1267
|
return depth;
|
|
1127
1268
|
}
|
|
1128
1269
|
|
|
1129
1270
|
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1130
1271
|
{
|
|
1131
|
-
int j;
|
|
1132
1272
|
long depth = increase_depth(data);
|
|
1133
1273
|
|
|
1134
1274
|
if (RHASH_SIZE(obj) == 0) {
|
|
1135
1275
|
fbuffer_append(buffer, "{}", 2);
|
|
1136
|
-
--data->
|
|
1276
|
+
--data->depth;
|
|
1137
1277
|
return;
|
|
1138
1278
|
}
|
|
1139
1279
|
|
|
1140
1280
|
fbuffer_append_char(buffer, '{');
|
|
1141
1281
|
|
|
1142
1282
|
struct hash_foreach_arg arg = {
|
|
1283
|
+
.hash = obj,
|
|
1143
1284
|
.data = data,
|
|
1144
|
-
.
|
|
1285
|
+
.first = true,
|
|
1145
1286
|
};
|
|
1146
1287
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
1147
1288
|
|
|
1148
|
-
depth = --data->
|
|
1289
|
+
depth = --data->depth;
|
|
1149
1290
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1150
1291
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1151
1292
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1152
|
-
|
|
1153
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1154
|
-
}
|
|
1293
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1155
1294
|
}
|
|
1156
1295
|
}
|
|
1157
1296
|
fbuffer_append_char(buffer, '}');
|
|
@@ -1159,125 +1298,41 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1159
1298
|
|
|
1160
1299
|
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1161
1300
|
{
|
|
1162
|
-
int i, j;
|
|
1163
1301
|
long depth = increase_depth(data);
|
|
1164
1302
|
|
|
1165
1303
|
if (RARRAY_LEN(obj) == 0) {
|
|
1166
1304
|
fbuffer_append(buffer, "[]", 2);
|
|
1167
|
-
--data->
|
|
1305
|
+
--data->depth;
|
|
1168
1306
|
return;
|
|
1169
1307
|
}
|
|
1170
1308
|
|
|
1171
1309
|
fbuffer_append_char(buffer, '[');
|
|
1172
1310
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1173
|
-
for (i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1311
|
+
for (int i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1174
1312
|
if (i > 0) {
|
|
1175
1313
|
fbuffer_append_char(buffer, ',');
|
|
1176
1314
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1177
1315
|
}
|
|
1178
1316
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1179
|
-
|
|
1180
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1181
|
-
}
|
|
1317
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1182
1318
|
}
|
|
1183
1319
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
1184
1320
|
}
|
|
1185
|
-
data->
|
|
1321
|
+
data->depth = --depth;
|
|
1186
1322
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
|
1187
1323
|
fbuffer_append_str(buffer, data->state->array_nl);
|
|
1188
1324
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1189
|
-
|
|
1190
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1191
|
-
}
|
|
1325
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1192
1326
|
}
|
|
1193
1327
|
}
|
|
1194
1328
|
fbuffer_append_char(buffer, ']');
|
|
1195
1329
|
}
|
|
1196
1330
|
|
|
1197
|
-
static inline int enc_utf8_compatible_p(int enc_idx)
|
|
1198
|
-
{
|
|
1199
|
-
if (enc_idx == usascii_encindex) return 1;
|
|
1200
|
-
if (enc_idx == utf8_encindex) return 1;
|
|
1201
|
-
return 0;
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
static VALUE encode_json_string_try(VALUE str)
|
|
1205
|
-
{
|
|
1206
|
-
return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
1210
|
-
{
|
|
1211
|
-
raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
|
|
1212
|
-
return Qundef;
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
static inline VALUE ensure_valid_encoding(VALUE str)
|
|
1216
|
-
{
|
|
1217
|
-
int encindex = RB_ENCODING_GET(str);
|
|
1218
|
-
VALUE utf8_string;
|
|
1219
|
-
if (RB_UNLIKELY(!enc_utf8_compatible_p(encindex))) {
|
|
1220
|
-
if (encindex == binary_encindex) {
|
|
1221
|
-
utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
|
|
1222
|
-
switch (rb_enc_str_coderange(utf8_string)) {
|
|
1223
|
-
case ENC_CODERANGE_7BIT:
|
|
1224
|
-
return utf8_string;
|
|
1225
|
-
case ENC_CODERANGE_VALID:
|
|
1226
|
-
// For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
|
|
1227
|
-
// TODO: Raise in 3.0.0
|
|
1228
|
-
rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
|
|
1229
|
-
return utf8_string;
|
|
1230
|
-
break;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
1235
|
-
}
|
|
1236
|
-
return str;
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1240
|
-
{
|
|
1241
|
-
obj = ensure_valid_encoding(obj);
|
|
1242
|
-
|
|
1243
|
-
fbuffer_append_char(buffer, '"');
|
|
1244
|
-
|
|
1245
|
-
long len;
|
|
1246
|
-
search_state search;
|
|
1247
|
-
search.buffer = buffer;
|
|
1248
|
-
RSTRING_GETMEM(obj, search.ptr, len);
|
|
1249
|
-
search.cursor = search.ptr;
|
|
1250
|
-
search.end = search.ptr + len;
|
|
1251
|
-
|
|
1252
|
-
#ifdef HAVE_SIMD
|
|
1253
|
-
search.matches_mask = 0;
|
|
1254
|
-
search.has_matches = false;
|
|
1255
|
-
search.chunk_base = NULL;
|
|
1256
|
-
#endif /* HAVE_SIMD */
|
|
1257
|
-
|
|
1258
|
-
switch (rb_enc_str_coderange(obj)) {
|
|
1259
|
-
case ENC_CODERANGE_7BIT:
|
|
1260
|
-
case ENC_CODERANGE_VALID:
|
|
1261
|
-
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
1262
|
-
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
|
1263
|
-
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
|
1264
|
-
convert_UTF8_to_script_safe_JSON(&search);
|
|
1265
|
-
} else {
|
|
1266
|
-
convert_UTF8_to_JSON(&search);
|
|
1267
|
-
}
|
|
1268
|
-
break;
|
|
1269
|
-
default:
|
|
1270
|
-
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1271
|
-
break;
|
|
1272
|
-
}
|
|
1273
|
-
fbuffer_append_char(buffer, '"');
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
1331
|
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1277
1332
|
{
|
|
1278
1333
|
VALUE tmp;
|
|
1279
1334
|
if (rb_respond_to(obj, i_to_json)) {
|
|
1280
|
-
tmp =
|
|
1335
|
+
tmp = json_call_to_json(data, obj);
|
|
1281
1336
|
Check_Type(tmp, T_STRING);
|
|
1282
1337
|
fbuffer_append_str(buffer, tmp);
|
|
1283
1338
|
} else {
|
|
@@ -1319,7 +1374,7 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1319
1374
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1320
1375
|
{
|
|
1321
1376
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1322
|
-
fbuffer_append_str(buffer, tmp);
|
|
1377
|
+
fbuffer_append_str(buffer, StringValue(tmp));
|
|
1323
1378
|
}
|
|
1324
1379
|
|
|
1325
1380
|
#ifdef RUBY_INTEGER_UNIFICATION
|
|
@@ -1340,11 +1395,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1340
1395
|
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
|
|
1341
1396
|
if (!allow_nan) {
|
|
1342
1397
|
if (data->state->strict && data->state->as_json) {
|
|
1343
|
-
VALUE casted_obj =
|
|
1398
|
+
VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1344
1399
|
if (casted_obj != obj) {
|
|
1345
1400
|
increase_depth(data);
|
|
1346
1401
|
generate_json(buffer, data, casted_obj);
|
|
1347
|
-
data->
|
|
1402
|
+
data->depth--;
|
|
1348
1403
|
return;
|
|
1349
1404
|
}
|
|
1350
1405
|
}
|
|
@@ -1357,12 +1412,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1357
1412
|
}
|
|
1358
1413
|
|
|
1359
1414
|
/* This implementation writes directly into the buffer. We reserve
|
|
1360
|
-
* the
|
|
1415
|
+
* the 32 characters that fpconv_dtoa states as its maximum.
|
|
1361
1416
|
*/
|
|
1362
|
-
fbuffer_inc_capa(buffer,
|
|
1417
|
+
fbuffer_inc_capa(buffer, 32);
|
|
1363
1418
|
char* d = buffer->ptr + buffer->len;
|
|
1364
1419
|
int len = fpconv_dtoa(value, d);
|
|
1365
|
-
|
|
1366
1420
|
/* fpconv_dtoa converts a float to its shortest string representation,
|
|
1367
1421
|
* but it adds a ".0" if this is a plain integer.
|
|
1368
1422
|
*/
|
|
@@ -1412,7 +1466,16 @@ start:
|
|
|
1412
1466
|
break;
|
|
1413
1467
|
case T_STRING:
|
|
1414
1468
|
if (klass != rb_cString) goto general;
|
|
1415
|
-
|
|
1469
|
+
|
|
1470
|
+
if (RB_LIKELY(valid_json_string_p(obj))) {
|
|
1471
|
+
raw_generate_json_string(buffer, data, obj);
|
|
1472
|
+
} else if (as_json_called) {
|
|
1473
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1474
|
+
} else {
|
|
1475
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
1476
|
+
as_json_called = true;
|
|
1477
|
+
goto start;
|
|
1478
|
+
}
|
|
1416
1479
|
break;
|
|
1417
1480
|
case T_SYMBOL:
|
|
1418
1481
|
generate_json_symbol(buffer, data, obj);
|
|
@@ -1429,7 +1492,7 @@ start:
|
|
|
1429
1492
|
general:
|
|
1430
1493
|
if (data->state->strict) {
|
|
1431
1494
|
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1432
|
-
obj =
|
|
1495
|
+
obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1433
1496
|
as_json_called = true;
|
|
1434
1497
|
goto start;
|
|
1435
1498
|
} else {
|
|
@@ -1448,16 +1511,14 @@ static VALUE generate_json_try(VALUE d)
|
|
|
1448
1511
|
|
|
1449
1512
|
data->func(data->buffer, data, data->obj);
|
|
1450
1513
|
|
|
1451
|
-
return
|
|
1514
|
+
return fbuffer_finalize(data->buffer);
|
|
1452
1515
|
}
|
|
1453
1516
|
|
|
1454
|
-
static VALUE
|
|
1517
|
+
static VALUE generate_json_ensure(VALUE d)
|
|
1455
1518
|
{
|
|
1456
1519
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1457
1520
|
fbuffer_free(data->buffer);
|
|
1458
1521
|
|
|
1459
|
-
rb_exc_raise(exc);
|
|
1460
|
-
|
|
1461
1522
|
return Qundef;
|
|
1462
1523
|
}
|
|
1463
1524
|
|
|
@@ -1473,14 +1534,15 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
|
1473
1534
|
|
|
1474
1535
|
struct generate_json_data data = {
|
|
1475
1536
|
.buffer = &buffer,
|
|
1476
|
-
.vstate = self
|
|
1537
|
+
.vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
|
|
1477
1538
|
.state = state,
|
|
1539
|
+
.depth = state->depth,
|
|
1478
1540
|
.obj = obj,
|
|
1479
1541
|
.func = func
|
|
1480
1542
|
};
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
return
|
|
1543
|
+
VALUE result = rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1544
|
+
RB_GC_GUARD(self);
|
|
1545
|
+
return result;
|
|
1484
1546
|
}
|
|
1485
1547
|
|
|
1486
1548
|
/* call-seq:
|
|
@@ -1496,10 +1558,7 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1496
1558
|
rb_check_arity(argc, 1, 2);
|
|
1497
1559
|
VALUE obj = argv[0];
|
|
1498
1560
|
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1499
|
-
|
|
1500
|
-
GET_STATE(self);
|
|
1501
|
-
(void)state;
|
|
1502
|
-
return result;
|
|
1561
|
+
return cState_partial_generate(self, obj, generate_json, io);
|
|
1503
1562
|
}
|
|
1504
1563
|
|
|
1505
1564
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
@@ -1580,6 +1639,7 @@ static VALUE string_config(VALUE config)
|
|
|
1580
1639
|
*/
|
|
1581
1640
|
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
1582
1641
|
{
|
|
1642
|
+
rb_check_frozen(self);
|
|
1583
1643
|
GET_STATE(self);
|
|
1584
1644
|
RB_OBJ_WRITE(self, &state->indent, string_config(indent));
|
|
1585
1645
|
return Qnil;
|
|
@@ -1605,6 +1665,7 @@ static VALUE cState_space(VALUE self)
|
|
|
1605
1665
|
*/
|
|
1606
1666
|
static VALUE cState_space_set(VALUE self, VALUE space)
|
|
1607
1667
|
{
|
|
1668
|
+
rb_check_frozen(self);
|
|
1608
1669
|
GET_STATE(self);
|
|
1609
1670
|
RB_OBJ_WRITE(self, &state->space, string_config(space));
|
|
1610
1671
|
return Qnil;
|
|
@@ -1628,6 +1689,7 @@ static VALUE cState_space_before(VALUE self)
|
|
|
1628
1689
|
*/
|
|
1629
1690
|
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
1630
1691
|
{
|
|
1692
|
+
rb_check_frozen(self);
|
|
1631
1693
|
GET_STATE(self);
|
|
1632
1694
|
RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
|
|
1633
1695
|
return Qnil;
|
|
@@ -1653,6 +1715,7 @@ static VALUE cState_object_nl(VALUE self)
|
|
|
1653
1715
|
*/
|
|
1654
1716
|
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
1655
1717
|
{
|
|
1718
|
+
rb_check_frozen(self);
|
|
1656
1719
|
GET_STATE(self);
|
|
1657
1720
|
RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
|
|
1658
1721
|
return Qnil;
|
|
@@ -1676,6 +1739,7 @@ static VALUE cState_array_nl(VALUE self)
|
|
|
1676
1739
|
*/
|
|
1677
1740
|
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
1678
1741
|
{
|
|
1742
|
+
rb_check_frozen(self);
|
|
1679
1743
|
GET_STATE(self);
|
|
1680
1744
|
RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
|
|
1681
1745
|
return Qnil;
|
|
@@ -1699,6 +1763,7 @@ static VALUE cState_as_json(VALUE self)
|
|
|
1699
1763
|
*/
|
|
1700
1764
|
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
|
1701
1765
|
{
|
|
1766
|
+
rb_check_frozen(self);
|
|
1702
1767
|
GET_STATE(self);
|
|
1703
1768
|
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
|
1704
1769
|
return Qnil;
|
|
@@ -1741,6 +1806,7 @@ static long long_config(VALUE num)
|
|
|
1741
1806
|
*/
|
|
1742
1807
|
static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
|
1743
1808
|
{
|
|
1809
|
+
rb_check_frozen(self);
|
|
1744
1810
|
GET_STATE(self);
|
|
1745
1811
|
state->max_nesting = long_config(depth);
|
|
1746
1812
|
return Qnil;
|
|
@@ -1766,6 +1832,7 @@ static VALUE cState_script_safe(VALUE self)
|
|
|
1766
1832
|
*/
|
|
1767
1833
|
static VALUE cState_script_safe_set(VALUE self, VALUE enable)
|
|
1768
1834
|
{
|
|
1835
|
+
rb_check_frozen(self);
|
|
1769
1836
|
GET_STATE(self);
|
|
1770
1837
|
state->script_safe = RTEST(enable);
|
|
1771
1838
|
return Qnil;
|
|
@@ -1797,6 +1864,7 @@ static VALUE cState_strict(VALUE self)
|
|
|
1797
1864
|
*/
|
|
1798
1865
|
static VALUE cState_strict_set(VALUE self, VALUE enable)
|
|
1799
1866
|
{
|
|
1867
|
+
rb_check_frozen(self);
|
|
1800
1868
|
GET_STATE(self);
|
|
1801
1869
|
state->strict = RTEST(enable);
|
|
1802
1870
|
return Qnil;
|
|
@@ -1821,6 +1889,7 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
1821
1889
|
*/
|
|
1822
1890
|
static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
|
|
1823
1891
|
{
|
|
1892
|
+
rb_check_frozen(self);
|
|
1824
1893
|
GET_STATE(self);
|
|
1825
1894
|
state->allow_nan = RTEST(enable);
|
|
1826
1895
|
return Qnil;
|
|
@@ -1845,11 +1914,25 @@ static VALUE cState_ascii_only_p(VALUE self)
|
|
|
1845
1914
|
*/
|
|
1846
1915
|
static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
1847
1916
|
{
|
|
1917
|
+
rb_check_frozen(self);
|
|
1848
1918
|
GET_STATE(self);
|
|
1849
1919
|
state->ascii_only = RTEST(enable);
|
|
1850
1920
|
return Qnil;
|
|
1851
1921
|
}
|
|
1852
1922
|
|
|
1923
|
+
static VALUE cState_allow_duplicate_key_p(VALUE self)
|
|
1924
|
+
{
|
|
1925
|
+
GET_STATE(self);
|
|
1926
|
+
switch (state->on_duplicate_key) {
|
|
1927
|
+
case JSON_IGNORE:
|
|
1928
|
+
return Qtrue;
|
|
1929
|
+
case JSON_DEPRECATED:
|
|
1930
|
+
return Qnil;
|
|
1931
|
+
default:
|
|
1932
|
+
return Qfalse;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1853
1936
|
/*
|
|
1854
1937
|
* call-seq: depth
|
|
1855
1938
|
*
|
|
@@ -1869,6 +1952,7 @@ static VALUE cState_depth(VALUE self)
|
|
|
1869
1952
|
*/
|
|
1870
1953
|
static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
1871
1954
|
{
|
|
1955
|
+
rb_check_frozen(self);
|
|
1872
1956
|
GET_STATE(self);
|
|
1873
1957
|
state->depth = long_config(depth);
|
|
1874
1958
|
return Qnil;
|
|
@@ -1902,6 +1986,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
|
|
|
1902
1986
|
*/
|
|
1903
1987
|
static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
|
|
1904
1988
|
{
|
|
1989
|
+
rb_check_frozen(self);
|
|
1905
1990
|
GET_STATE(self);
|
|
1906
1991
|
buffer_initial_length_set(state, buffer_initial_length);
|
|
1907
1992
|
return Qnil;
|
|
@@ -1939,8 +2024,10 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1939
2024
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1940
2025
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
1941
2026
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
|
2027
|
+
else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1942
2028
|
else if (key == sym_as_json) {
|
|
1943
2029
|
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
|
|
2030
|
+
state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
|
|
1944
2031
|
state_write_value(data, &state->as_json, proc);
|
|
1945
2032
|
}
|
|
1946
2033
|
return ST_CONTINUE;
|
|
@@ -1966,6 +2053,7 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
|
|
|
1966
2053
|
|
|
1967
2054
|
static VALUE cState_configure(VALUE self, VALUE opts)
|
|
1968
2055
|
{
|
|
2056
|
+
rb_check_frozen(self);
|
|
1969
2057
|
GET_STATE(self);
|
|
1970
2058
|
configure_state(state, self, opts);
|
|
1971
2059
|
return self;
|
|
@@ -1987,12 +2075,11 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
|
1987
2075
|
.buffer = &buffer,
|
|
1988
2076
|
.vstate = Qfalse,
|
|
1989
2077
|
.state = &state,
|
|
2078
|
+
.depth = state.depth,
|
|
1990
2079
|
.obj = obj,
|
|
1991
2080
|
.func = generate_json,
|
|
1992
2081
|
};
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
return fbuffer_finalize(&buffer);
|
|
2082
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1996
2083
|
}
|
|
1997
2084
|
|
|
1998
2085
|
/*
|
|
@@ -2062,7 +2149,8 @@ void Init_generator(void)
|
|
|
2062
2149
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2063
2150
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2064
2151
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
2065
|
-
|
|
2152
|
+
|
|
2153
|
+
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2066
2154
|
|
|
2067
2155
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
2068
2156
|
|
|
@@ -2091,13 +2179,7 @@ void Init_generator(void)
|
|
|
2091
2179
|
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
|
2092
2180
|
|
|
2093
2181
|
VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
|
|
2094
|
-
rb_define_singleton_method(mString, "included", mString_included_s, 1);
|
|
2095
2182
|
rb_define_method(mString, "to_json", mString_to_json, -1);
|
|
2096
|
-
rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
|
|
2097
|
-
rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
|
|
2098
|
-
|
|
2099
|
-
mString_Extend = rb_define_module_under(mString, "Extend");
|
|
2100
|
-
rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
|
|
2101
2183
|
|
|
2102
2184
|
VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
|
2103
2185
|
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
|
@@ -2114,10 +2196,6 @@ void Init_generator(void)
|
|
|
2114
2196
|
i_to_s = rb_intern("to_s");
|
|
2115
2197
|
i_to_json = rb_intern("to_json");
|
|
2116
2198
|
i_new = rb_intern("new");
|
|
2117
|
-
i_pack = rb_intern("pack");
|
|
2118
|
-
i_unpack = rb_intern("unpack");
|
|
2119
|
-
i_create_id = rb_intern("create_id");
|
|
2120
|
-
i_extend = rb_intern("extend");
|
|
2121
2199
|
i_encode = rb_intern("encode");
|
|
2122
2200
|
|
|
2123
2201
|
sym_indent = ID2SYM(rb_intern("indent"));
|
|
@@ -2134,6 +2212,7 @@ void Init_generator(void)
|
|
|
2134
2212
|
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
|
2135
2213
|
sym_strict = ID2SYM(rb_intern("strict"));
|
|
2136
2214
|
sym_as_json = ID2SYM(rb_intern("as_json"));
|
|
2215
|
+
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
|
2137
2216
|
|
|
2138
2217
|
usascii_encindex = rb_usascii_encindex();
|
|
2139
2218
|
utf8_encindex = rb_utf8_encindex();
|
|
@@ -2141,22 +2220,5 @@ void Init_generator(void)
|
|
|
2141
2220
|
|
|
2142
2221
|
rb_require("json/ext/generator/state");
|
|
2143
2222
|
|
|
2144
|
-
|
|
2145
|
-
switch (find_simd_implementation()) {
|
|
2146
|
-
#ifdef HAVE_SIMD
|
|
2147
|
-
#ifdef HAVE_SIMD_NEON
|
|
2148
|
-
case SIMD_NEON:
|
|
2149
|
-
search_escape_basic_impl = search_escape_basic_neon;
|
|
2150
|
-
break;
|
|
2151
|
-
#endif /* HAVE_SIMD_NEON */
|
|
2152
|
-
#ifdef HAVE_SIMD_SSE2
|
|
2153
|
-
case SIMD_SSE2:
|
|
2154
|
-
search_escape_basic_impl = search_escape_basic_sse2;
|
|
2155
|
-
break;
|
|
2156
|
-
#endif /* HAVE_SIMD_SSE2 */
|
|
2157
|
-
#endif /* HAVE_SIMD */
|
|
2158
|
-
default:
|
|
2159
|
-
search_escape_basic_impl = search_escape_basic;
|
|
2160
|
-
break;
|
|
2161
|
-
}
|
|
2223
|
+
simd_impl = find_simd_implementation();
|
|
2162
2224
|
}
|