json 2.13.2 → 2.19.5
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 +114 -8
- data/LEGAL +12 -0
- data/README.md +19 -1
- data/ext/json/ext/fbuffer/fbuffer.h +47 -66
- data/ext/json/ext/generator/extconf.rb +3 -1
- data/ext/json/ext/generator/generator.c +393 -563
- data/ext/json/ext/json.h +116 -0
- data/ext/json/ext/parser/extconf.rb +6 -1
- data/ext/json/ext/parser/parser.c +665 -502
- data/ext/json/ext/simd/simd.h +42 -22
- 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 +101 -33
- data/lib/json/ext/generator/state.rb +11 -14
- data/lib/json/generic_object.rb +0 -8
- data/lib/json/truffle_ruby/generator.rb +133 -71
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +58 -3
- 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
|
|
39
|
+
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
33
40
|
|
|
34
|
-
static
|
|
35
|
-
|
|
36
|
-
static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
|
|
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);
|
|
@@ -66,9 +74,6 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
|
66
74
|
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
67
75
|
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
68
76
|
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
69
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
70
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
71
|
-
#endif
|
|
72
77
|
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
73
78
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
74
79
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -76,23 +81,18 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
76
81
|
|
|
77
82
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
|
78
83
|
|
|
79
|
-
|
|
80
|
-
RBIMPL_ATTR_NORETURN()
|
|
81
|
-
#endif
|
|
82
|
-
static void raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
84
|
+
NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
83
85
|
{
|
|
86
|
+
rb_enc_associate_index(str, utf8_encindex);
|
|
84
87
|
VALUE exc = rb_exc_new_str(eGeneratorError, str);
|
|
85
88
|
rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
|
|
86
89
|
rb_exc_raise(exc);
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
#ifdef RBIMPL_ATTR_NORETURN
|
|
90
|
-
RBIMPL_ATTR_NORETURN()
|
|
91
|
-
#endif
|
|
92
92
|
#ifdef RBIMPL_ATTR_FORMAT
|
|
93
93
|
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
|
|
94
94
|
#endif
|
|
95
|
-
static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
95
|
+
NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
96
96
|
{
|
|
97
97
|
va_list args;
|
|
98
98
|
va_start(args, fmt);
|
|
@@ -127,18 +127,12 @@ typedef struct _search_state {
|
|
|
127
127
|
#endif /* HAVE_SIMD */
|
|
128
128
|
} search_state;
|
|
129
129
|
|
|
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)
|
|
130
|
+
ALWAYS_INLINE(static) void search_flush(search_state *search)
|
|
137
131
|
{
|
|
138
132
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
|
139
133
|
// 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
|
|
134
|
+
// For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
|
|
135
|
+
// will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
|
|
142
136
|
// consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
|
|
143
137
|
// nothing needs to be flushed, we can save a few memory references with this conditional.
|
|
144
138
|
if (search->ptr > search->cursor) {
|
|
@@ -160,8 +154,6 @@ static const unsigned char escape_table_basic[256] = {
|
|
|
160
154
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
161
155
|
};
|
|
162
156
|
|
|
163
|
-
static unsigned char (*search_escape_basic_impl)(search_state *);
|
|
164
|
-
|
|
165
157
|
static inline unsigned char search_escape_basic(search_state *search)
|
|
166
158
|
{
|
|
167
159
|
while (search->ptr < search->end) {
|
|
@@ -176,7 +168,7 @@ static inline unsigned char search_escape_basic(search_state *search)
|
|
|
176
168
|
return 0;
|
|
177
169
|
}
|
|
178
170
|
|
|
179
|
-
static
|
|
171
|
+
ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
180
172
|
{
|
|
181
173
|
const unsigned char ch = (unsigned char)*search->ptr;
|
|
182
174
|
switch (ch) {
|
|
@@ -217,11 +209,39 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
|
|
|
217
209
|
* Everything else (should be UTF-8) is just passed through and
|
|
218
210
|
* appended to the result.
|
|
219
211
|
*/
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
#if defined(HAVE_SIMD_NEON)
|
|
215
|
+
static inline unsigned char search_escape_basic_neon(search_state *search);
|
|
216
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
217
|
+
static inline unsigned char search_escape_basic_sse2(search_state *search);
|
|
218
|
+
#endif
|
|
219
|
+
|
|
220
|
+
static inline unsigned char search_escape_basic(search_state *search);
|
|
221
|
+
|
|
220
222
|
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
221
223
|
{
|
|
222
|
-
|
|
224
|
+
#ifdef HAVE_SIMD
|
|
225
|
+
#if defined(HAVE_SIMD_NEON)
|
|
226
|
+
while (search_escape_basic_neon(search)) {
|
|
227
|
+
escape_UTF8_char_basic(search);
|
|
228
|
+
}
|
|
229
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
230
|
+
if (simd_impl == SIMD_SSE2) {
|
|
231
|
+
while (search_escape_basic_sse2(search)) {
|
|
232
|
+
escape_UTF8_char_basic(search);
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
while (search_escape_basic(search)) {
|
|
223
237
|
escape_UTF8_char_basic(search);
|
|
224
238
|
}
|
|
239
|
+
#endif
|
|
240
|
+
#else
|
|
241
|
+
while (search_escape_basic(search)) {
|
|
242
|
+
escape_UTF8_char_basic(search);
|
|
243
|
+
}
|
|
244
|
+
#endif /* HAVE_SIMD */
|
|
225
245
|
}
|
|
226
246
|
|
|
227
247
|
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
@@ -263,8 +283,10 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
263
283
|
|
|
264
284
|
#ifdef HAVE_SIMD
|
|
265
285
|
|
|
266
|
-
static
|
|
286
|
+
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
267
287
|
{
|
|
288
|
+
RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
|
|
289
|
+
|
|
268
290
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
269
291
|
search_flush(search);
|
|
270
292
|
|
|
@@ -274,19 +296,25 @@ static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsi
|
|
|
274
296
|
char *s = (buf->ptr + buf->len);
|
|
275
297
|
|
|
276
298
|
// Pad the buffer with dummy characters that won't need escaping.
|
|
277
|
-
// This seem
|
|
278
|
-
|
|
299
|
+
// This seem wasteful at first sight, but memset of vector length is very fast.
|
|
300
|
+
// This is a space as it can be directly represented as an immediate on AArch64.
|
|
301
|
+
memset(s, ' ', vec_len);
|
|
279
302
|
|
|
280
303
|
// Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
|
|
281
304
|
// to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
|
|
282
|
-
|
|
305
|
+
if (vec_len == 16) {
|
|
306
|
+
RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
|
|
307
|
+
json_fast_memcpy16(s, search->ptr, len);
|
|
308
|
+
} else {
|
|
309
|
+
MEMCPY(s, search->ptr, char, len);
|
|
310
|
+
}
|
|
283
311
|
|
|
284
312
|
return s;
|
|
285
313
|
}
|
|
286
314
|
|
|
287
315
|
#ifdef HAVE_SIMD_NEON
|
|
288
316
|
|
|
289
|
-
static
|
|
317
|
+
ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
|
|
290
318
|
{
|
|
291
319
|
uint64_t mask = search->matches_mask;
|
|
292
320
|
uint32_t index = trailing_zeros64(mask) >> 2;
|
|
@@ -400,7 +428,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
|
|
|
400
428
|
|
|
401
429
|
#ifdef HAVE_SIMD_SSE2
|
|
402
430
|
|
|
403
|
-
static
|
|
431
|
+
ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
|
|
404
432
|
{
|
|
405
433
|
int mask = search->matches_mask;
|
|
406
434
|
int index = trailing_zeros(mask);
|
|
@@ -424,7 +452,7 @@ static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
|
|
|
424
452
|
#define TARGET_SSE2
|
|
425
453
|
#endif
|
|
426
454
|
|
|
427
|
-
static
|
|
455
|
+
ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
|
|
428
456
|
{
|
|
429
457
|
if (RB_UNLIKELY(search->has_matches)) {
|
|
430
458
|
// There are more matches if search->matches_mask > 0.
|
|
@@ -672,290 +700,6 @@ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned
|
|
|
672
700
|
}
|
|
673
701
|
}
|
|
674
702
|
|
|
675
|
-
/*
|
|
676
|
-
* Document-module: JSON::Ext::Generator
|
|
677
|
-
*
|
|
678
|
-
* This is the JSON generator implemented as a C extension. It can be
|
|
679
|
-
* configured to be used by setting
|
|
680
|
-
*
|
|
681
|
-
* JSON.generator = JSON::Ext::Generator
|
|
682
|
-
*
|
|
683
|
-
* with the method generator= in JSON.
|
|
684
|
-
*
|
|
685
|
-
*/
|
|
686
|
-
|
|
687
|
-
/* Explanation of the following: that's the only way to not pollute
|
|
688
|
-
* standard library's docs with GeneratorMethods::<ClassName> which
|
|
689
|
-
* are uninformative and take a large place in a list of classes
|
|
690
|
-
*/
|
|
691
|
-
|
|
692
|
-
/*
|
|
693
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods
|
|
694
|
-
* :nodoc:
|
|
695
|
-
*/
|
|
696
|
-
|
|
697
|
-
/*
|
|
698
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Array
|
|
699
|
-
* :nodoc:
|
|
700
|
-
*/
|
|
701
|
-
|
|
702
|
-
/*
|
|
703
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
|
|
704
|
-
* :nodoc:
|
|
705
|
-
*/
|
|
706
|
-
|
|
707
|
-
/*
|
|
708
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
|
|
709
|
-
* :nodoc:
|
|
710
|
-
*/
|
|
711
|
-
|
|
712
|
-
/*
|
|
713
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
|
|
714
|
-
* :nodoc:
|
|
715
|
-
*/
|
|
716
|
-
|
|
717
|
-
/*
|
|
718
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Float
|
|
719
|
-
* :nodoc:
|
|
720
|
-
*/
|
|
721
|
-
|
|
722
|
-
/*
|
|
723
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
|
|
724
|
-
* :nodoc:
|
|
725
|
-
*/
|
|
726
|
-
|
|
727
|
-
/*
|
|
728
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
|
|
729
|
-
* :nodoc:
|
|
730
|
-
*/
|
|
731
|
-
|
|
732
|
-
/*
|
|
733
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
|
|
734
|
-
* :nodoc:
|
|
735
|
-
*/
|
|
736
|
-
|
|
737
|
-
/*
|
|
738
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Object
|
|
739
|
-
* :nodoc:
|
|
740
|
-
*/
|
|
741
|
-
|
|
742
|
-
/*
|
|
743
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::String
|
|
744
|
-
* :nodoc:
|
|
745
|
-
*/
|
|
746
|
-
|
|
747
|
-
/*
|
|
748
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
|
|
749
|
-
* :nodoc:
|
|
750
|
-
*/
|
|
751
|
-
|
|
752
|
-
/*
|
|
753
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
|
|
754
|
-
* :nodoc:
|
|
755
|
-
*/
|
|
756
|
-
|
|
757
|
-
/*
|
|
758
|
-
* call-seq: to_json(state = nil)
|
|
759
|
-
*
|
|
760
|
-
* Returns a JSON string containing a JSON object, that is generated from
|
|
761
|
-
* this Hash instance.
|
|
762
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
763
|
-
* produced JSON string output further.
|
|
764
|
-
*/
|
|
765
|
-
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
|
|
766
|
-
{
|
|
767
|
-
rb_check_arity(argc, 0, 1);
|
|
768
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
769
|
-
return cState_partial_generate(Vstate, self, generate_json_object, Qfalse);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/*
|
|
773
|
-
* call-seq: to_json(state = nil)
|
|
774
|
-
*
|
|
775
|
-
* Returns a JSON string containing a JSON array, that is generated from
|
|
776
|
-
* this Array instance.
|
|
777
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
778
|
-
* produced JSON string output further.
|
|
779
|
-
*/
|
|
780
|
-
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self)
|
|
781
|
-
{
|
|
782
|
-
rb_check_arity(argc, 0, 1);
|
|
783
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
784
|
-
return cState_partial_generate(Vstate, self, generate_json_array, Qfalse);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
788
|
-
/*
|
|
789
|
-
* call-seq: to_json(*)
|
|
790
|
-
*
|
|
791
|
-
* Returns a JSON string representation for this Integer number.
|
|
792
|
-
*/
|
|
793
|
-
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
|
|
794
|
-
{
|
|
795
|
-
rb_check_arity(argc, 0, 1);
|
|
796
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
797
|
-
return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
#else
|
|
801
|
-
/*
|
|
802
|
-
* call-seq: to_json(*)
|
|
803
|
-
*
|
|
804
|
-
* Returns a JSON string representation for this Integer number.
|
|
805
|
-
*/
|
|
806
|
-
static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
|
|
807
|
-
{
|
|
808
|
-
rb_check_arity(argc, 0, 1);
|
|
809
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
810
|
-
return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
/*
|
|
814
|
-
* call-seq: to_json(*)
|
|
815
|
-
*
|
|
816
|
-
* Returns a JSON string representation for this Integer number.
|
|
817
|
-
*/
|
|
818
|
-
static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
|
|
819
|
-
{
|
|
820
|
-
rb_check_arity(argc, 0, 1);
|
|
821
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
822
|
-
return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse);
|
|
823
|
-
}
|
|
824
|
-
#endif
|
|
825
|
-
|
|
826
|
-
/*
|
|
827
|
-
* call-seq: to_json(*)
|
|
828
|
-
*
|
|
829
|
-
* Returns a JSON string representation for this Float number.
|
|
830
|
-
*/
|
|
831
|
-
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
832
|
-
{
|
|
833
|
-
rb_check_arity(argc, 0, 1);
|
|
834
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
835
|
-
return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
|
|
836
|
-
}
|
|
837
|
-
|
|
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
|
-
/*
|
|
851
|
-
* call-seq: to_json(*)
|
|
852
|
-
*
|
|
853
|
-
* This string should be encoded with UTF-8 A call to this method
|
|
854
|
-
* returns a JSON string encoded with UTF16 big endian characters as
|
|
855
|
-
* \u????.
|
|
856
|
-
*/
|
|
857
|
-
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
|
858
|
-
{
|
|
859
|
-
rb_check_arity(argc, 0, 1);
|
|
860
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
861
|
-
return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
|
|
862
|
-
}
|
|
863
|
-
|
|
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
|
-
/*
|
|
910
|
-
* call-seq: to_json(*)
|
|
911
|
-
*
|
|
912
|
-
* Returns a JSON string for true: 'true'.
|
|
913
|
-
*/
|
|
914
|
-
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
915
|
-
{
|
|
916
|
-
rb_check_arity(argc, 0, 1);
|
|
917
|
-
return rb_utf8_str_new("true", 4);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
/*
|
|
921
|
-
* call-seq: to_json(*)
|
|
922
|
-
*
|
|
923
|
-
* Returns a JSON string for false: 'false'.
|
|
924
|
-
*/
|
|
925
|
-
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
926
|
-
{
|
|
927
|
-
rb_check_arity(argc, 0, 1);
|
|
928
|
-
return rb_utf8_str_new("false", 5);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
/*
|
|
932
|
-
* call-seq: to_json(*)
|
|
933
|
-
*
|
|
934
|
-
* Returns a JSON string for nil: 'null'.
|
|
935
|
-
*/
|
|
936
|
-
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
937
|
-
{
|
|
938
|
-
rb_check_arity(argc, 0, 1);
|
|
939
|
-
return rb_utf8_str_new("null", 4);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
/*
|
|
943
|
-
* call-seq: to_json(*)
|
|
944
|
-
*
|
|
945
|
-
* Converts this object to a string (calling #to_s), converts
|
|
946
|
-
* it to a JSON string, and returns the result. This is a fallback, if no
|
|
947
|
-
* special method #to_json was defined for some object.
|
|
948
|
-
*/
|
|
949
|
-
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
|
|
950
|
-
{
|
|
951
|
-
VALUE state;
|
|
952
|
-
VALUE string = rb_funcall(self, i_to_s, 0);
|
|
953
|
-
rb_scan_args(argc, argv, "01", &state);
|
|
954
|
-
Check_Type(string, T_STRING);
|
|
955
|
-
state = cState_from_state_s(cState, state);
|
|
956
|
-
return cState_partial_generate(state, string, generate_json_string, Qfalse);
|
|
957
|
-
}
|
|
958
|
-
|
|
959
703
|
static void State_mark(void *ptr)
|
|
960
704
|
{
|
|
961
705
|
JSON_Generator_State *state = ptr;
|
|
@@ -978,32 +722,20 @@ static void State_compact(void *ptr)
|
|
|
978
722
|
state->as_json = rb_gc_location(state->as_json);
|
|
979
723
|
}
|
|
980
724
|
|
|
981
|
-
static void State_free(void *ptr)
|
|
982
|
-
{
|
|
983
|
-
JSON_Generator_State *state = ptr;
|
|
984
|
-
ruby_xfree(state);
|
|
985
|
-
}
|
|
986
|
-
|
|
987
725
|
static size_t State_memsize(const void *ptr)
|
|
988
726
|
{
|
|
989
727
|
return sizeof(JSON_Generator_State);
|
|
990
728
|
}
|
|
991
729
|
|
|
992
|
-
#ifndef HAVE_RB_EXT_RACTOR_SAFE
|
|
993
|
-
# undef RUBY_TYPED_FROZEN_SHAREABLE
|
|
994
|
-
# define RUBY_TYPED_FROZEN_SHAREABLE 0
|
|
995
|
-
#endif
|
|
996
|
-
|
|
997
730
|
static const rb_data_type_t JSON_Generator_State_type = {
|
|
998
|
-
"JSON/Generator/State",
|
|
999
|
-
{
|
|
731
|
+
.wrap_struct_name = "JSON/Generator/State",
|
|
732
|
+
.function = {
|
|
1000
733
|
.dmark = State_mark,
|
|
1001
|
-
.dfree =
|
|
734
|
+
.dfree = RUBY_DEFAULT_FREE,
|
|
1002
735
|
.dsize = State_memsize,
|
|
1003
736
|
.dcompact = State_compact,
|
|
1004
737
|
},
|
|
1005
|
-
|
|
1006
|
-
RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
738
|
+
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
|
|
1007
739
|
};
|
|
1008
740
|
|
|
1009
741
|
static void state_init(JSON_Generator_State *state)
|
|
@@ -1035,18 +767,24 @@ static void vstate_spill(struct generate_json_data *data)
|
|
|
1035
767
|
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
|
1036
768
|
}
|
|
1037
769
|
|
|
1038
|
-
static inline VALUE
|
|
770
|
+
static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
|
|
1039
771
|
{
|
|
1040
772
|
if (RB_UNLIKELY(!data->vstate)) {
|
|
1041
773
|
vstate_spill(data);
|
|
1042
774
|
}
|
|
1043
|
-
|
|
775
|
+
GET_STATE(data->vstate);
|
|
776
|
+
state->depth = data->depth;
|
|
777
|
+
VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
|
|
778
|
+
// no need to restore state->depth, vstate is just a temporary State
|
|
779
|
+
return tmp;
|
|
1044
780
|
}
|
|
1045
781
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
};
|
|
782
|
+
static VALUE
|
|
783
|
+
json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
|
|
784
|
+
{
|
|
785
|
+
VALUE proc_args[2] = {object, is_key};
|
|
786
|
+
return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
|
|
787
|
+
}
|
|
1050
788
|
|
|
1051
789
|
static VALUE
|
|
1052
790
|
convert_string_subclass(VALUE key)
|
|
@@ -1063,6 +801,159 @@ convert_string_subclass(VALUE key)
|
|
|
1063
801
|
return key_to_s;
|
|
1064
802
|
}
|
|
1065
803
|
|
|
804
|
+
static bool enc_utf8_compatible_p(int enc_idx)
|
|
805
|
+
{
|
|
806
|
+
if (enc_idx == usascii_encindex) return true;
|
|
807
|
+
if (enc_idx == utf8_encindex) return true;
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
static VALUE encode_json_string_try(VALUE str)
|
|
812
|
+
{
|
|
813
|
+
return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
817
|
+
{
|
|
818
|
+
raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
|
|
819
|
+
return Qundef;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
static inline int json_str_coderange(VALUE str) {
|
|
823
|
+
int coderange = RB_ENC_CODERANGE(str);
|
|
824
|
+
if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
|
|
825
|
+
coderange = rb_enc_str_coderange(str);
|
|
826
|
+
}
|
|
827
|
+
return coderange;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
static inline bool valid_json_string_p(VALUE str)
|
|
831
|
+
{
|
|
832
|
+
int coderange = json_str_coderange(str);
|
|
833
|
+
|
|
834
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
|
|
839
|
+
return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
846
|
+
{
|
|
847
|
+
if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
|
|
848
|
+
VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
|
|
849
|
+
if (coerced_str != str) {
|
|
850
|
+
if (RB_TYPE_P(coerced_str, T_STRING)) {
|
|
851
|
+
if (!valid_json_string_p(coerced_str)) {
|
|
852
|
+
raise_generator_error(str, "source sequence is illegal/malformed utf-8");
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
// as_json could return another type than T_STRING
|
|
856
|
+
if (is_key) {
|
|
857
|
+
raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return coerced_str;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
|
|
866
|
+
VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
|
|
867
|
+
switch (rb_enc_str_coderange(utf8_string)) {
|
|
868
|
+
case ENC_CODERANGE_7BIT:
|
|
869
|
+
return utf8_string;
|
|
870
|
+
case ENC_CODERANGE_VALID:
|
|
871
|
+
// For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
|
|
872
|
+
// TODO: Raise in 3.0.0
|
|
873
|
+
rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
|
|
874
|
+
return utf8_string;
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
883
|
+
{
|
|
884
|
+
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
885
|
+
return str;
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
return convert_invalid_encoding(data, str, as_json_called, is_key);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
893
|
+
{
|
|
894
|
+
fbuffer_append_char(buffer, '"');
|
|
895
|
+
|
|
896
|
+
long len;
|
|
897
|
+
search_state search;
|
|
898
|
+
search.buffer = buffer;
|
|
899
|
+
RSTRING_GETMEM(obj, search.ptr, len);
|
|
900
|
+
search.cursor = search.ptr;
|
|
901
|
+
search.end = search.ptr + len;
|
|
902
|
+
|
|
903
|
+
#ifdef HAVE_SIMD
|
|
904
|
+
search.matches_mask = 0;
|
|
905
|
+
search.has_matches = false;
|
|
906
|
+
search.chunk_base = NULL;
|
|
907
|
+
search.chunk_end = NULL;
|
|
908
|
+
#endif /* HAVE_SIMD */
|
|
909
|
+
|
|
910
|
+
switch (json_str_coderange(obj)) {
|
|
911
|
+
case ENC_CODERANGE_7BIT:
|
|
912
|
+
case ENC_CODERANGE_VALID:
|
|
913
|
+
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
914
|
+
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
|
915
|
+
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
|
916
|
+
convert_UTF8_to_script_safe_JSON(&search);
|
|
917
|
+
} else {
|
|
918
|
+
convert_UTF8_to_JSON(&search);
|
|
919
|
+
}
|
|
920
|
+
break;
|
|
921
|
+
default:
|
|
922
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
fbuffer_append_char(buffer, '"');
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
929
|
+
{
|
|
930
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
931
|
+
raw_generate_json_string(buffer, data, obj);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
struct hash_foreach_arg {
|
|
935
|
+
VALUE hash;
|
|
936
|
+
struct generate_json_data *data;
|
|
937
|
+
int first_key_type;
|
|
938
|
+
bool first;
|
|
939
|
+
bool mixed_keys_encountered;
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
NOINLINE(static) void
|
|
943
|
+
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
944
|
+
{
|
|
945
|
+
if (arg->mixed_keys_encountered) {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
arg->mixed_keys_encountered = true;
|
|
949
|
+
|
|
950
|
+
JSON_Generator_State *state = arg->data->state;
|
|
951
|
+
if (state->on_duplicate_key != JSON_IGNORE) {
|
|
952
|
+
VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
|
|
953
|
+
rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
1066
957
|
static int
|
|
1067
958
|
json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1068
959
|
{
|
|
@@ -1072,22 +963,34 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1072
963
|
FBuffer *buffer = data->buffer;
|
|
1073
964
|
JSON_Generator_State *state = data->state;
|
|
1074
965
|
|
|
1075
|
-
long depth =
|
|
1076
|
-
int
|
|
966
|
+
long depth = data->depth;
|
|
967
|
+
int key_type = rb_type(key);
|
|
968
|
+
|
|
969
|
+
if (arg->first) {
|
|
970
|
+
arg->first = false;
|
|
971
|
+
arg->first_key_type = key_type;
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
fbuffer_append_char(buffer, ',');
|
|
975
|
+
}
|
|
1077
976
|
|
|
1078
|
-
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
|
|
1079
977
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1080
978
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1081
979
|
}
|
|
1082
980
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1083
|
-
|
|
1084
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1085
|
-
}
|
|
981
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1086
982
|
}
|
|
1087
983
|
|
|
1088
984
|
VALUE key_to_s;
|
|
1089
|
-
|
|
985
|
+
bool as_json_called = false;
|
|
986
|
+
|
|
987
|
+
start:
|
|
988
|
+
switch (key_type) {
|
|
1090
989
|
case T_STRING:
|
|
990
|
+
if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
|
|
991
|
+
json_inspect_hash_with_mixed_keys(arg);
|
|
992
|
+
}
|
|
993
|
+
|
|
1091
994
|
if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
|
|
1092
995
|
key_to_s = key;
|
|
1093
996
|
} else {
|
|
@@ -1095,15 +998,31 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1095
998
|
}
|
|
1096
999
|
break;
|
|
1097
1000
|
case T_SYMBOL:
|
|
1001
|
+
if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
|
|
1002
|
+
json_inspect_hash_with_mixed_keys(arg);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1098
1005
|
key_to_s = rb_sym2str(key);
|
|
1099
1006
|
break;
|
|
1100
1007
|
default:
|
|
1008
|
+
if (data->state->strict) {
|
|
1009
|
+
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1010
|
+
key = json_call_as_json(data->state, key, Qtrue);
|
|
1011
|
+
key_type = rb_type(key);
|
|
1012
|
+
as_json_called = true;
|
|
1013
|
+
goto start;
|
|
1014
|
+
} else {
|
|
1015
|
+
raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1101
1018
|
key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
|
|
1102
1019
|
break;
|
|
1103
1020
|
}
|
|
1104
1021
|
|
|
1022
|
+
key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
|
|
1023
|
+
|
|
1105
1024
|
if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
|
|
1106
|
-
|
|
1025
|
+
raw_generate_json_string(buffer, data, key_to_s);
|
|
1107
1026
|
} else {
|
|
1108
1027
|
generate_json(buffer, data, key_to_s);
|
|
1109
1028
|
}
|
|
@@ -1112,46 +1031,43 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1112
1031
|
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
|
|
1113
1032
|
generate_json(buffer, data, val);
|
|
1114
1033
|
|
|
1115
|
-
arg->iter++;
|
|
1116
1034
|
return ST_CONTINUE;
|
|
1117
1035
|
}
|
|
1118
1036
|
|
|
1119
1037
|
static inline long increase_depth(struct generate_json_data *data)
|
|
1120
1038
|
{
|
|
1121
1039
|
JSON_Generator_State *state = data->state;
|
|
1122
|
-
long depth = ++
|
|
1040
|
+
long depth = ++data->depth;
|
|
1123
1041
|
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
|
1124
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", --
|
|
1042
|
+
rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
|
|
1125
1043
|
}
|
|
1126
1044
|
return depth;
|
|
1127
1045
|
}
|
|
1128
1046
|
|
|
1129
1047
|
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1130
1048
|
{
|
|
1131
|
-
int j;
|
|
1132
1049
|
long depth = increase_depth(data);
|
|
1133
1050
|
|
|
1134
1051
|
if (RHASH_SIZE(obj) == 0) {
|
|
1135
1052
|
fbuffer_append(buffer, "{}", 2);
|
|
1136
|
-
--data->
|
|
1053
|
+
--data->depth;
|
|
1137
1054
|
return;
|
|
1138
1055
|
}
|
|
1139
1056
|
|
|
1140
1057
|
fbuffer_append_char(buffer, '{');
|
|
1141
1058
|
|
|
1142
1059
|
struct hash_foreach_arg arg = {
|
|
1060
|
+
.hash = obj,
|
|
1143
1061
|
.data = data,
|
|
1144
|
-
.
|
|
1062
|
+
.first = true,
|
|
1145
1063
|
};
|
|
1146
1064
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
1147
1065
|
|
|
1148
|
-
depth = --data->
|
|
1066
|
+
depth = --data->depth;
|
|
1149
1067
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1150
1068
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1151
1069
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1152
|
-
|
|
1153
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1154
|
-
}
|
|
1070
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1155
1071
|
}
|
|
1156
1072
|
}
|
|
1157
1073
|
fbuffer_append_char(buffer, '}');
|
|
@@ -1159,125 +1075,41 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1159
1075
|
|
|
1160
1076
|
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1161
1077
|
{
|
|
1162
|
-
int i, j;
|
|
1163
1078
|
long depth = increase_depth(data);
|
|
1164
1079
|
|
|
1165
1080
|
if (RARRAY_LEN(obj) == 0) {
|
|
1166
1081
|
fbuffer_append(buffer, "[]", 2);
|
|
1167
|
-
--data->
|
|
1082
|
+
--data->depth;
|
|
1168
1083
|
return;
|
|
1169
1084
|
}
|
|
1170
1085
|
|
|
1171
1086
|
fbuffer_append_char(buffer, '[');
|
|
1172
1087
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1173
|
-
for (i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1088
|
+
for (int i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1174
1089
|
if (i > 0) {
|
|
1175
1090
|
fbuffer_append_char(buffer, ',');
|
|
1176
1091
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1177
1092
|
}
|
|
1178
1093
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1179
|
-
|
|
1180
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1181
|
-
}
|
|
1094
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1182
1095
|
}
|
|
1183
1096
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
1184
1097
|
}
|
|
1185
|
-
data->
|
|
1098
|
+
data->depth = --depth;
|
|
1186
1099
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
|
1187
1100
|
fbuffer_append_str(buffer, data->state->array_nl);
|
|
1188
1101
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1189
|
-
|
|
1190
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1191
|
-
}
|
|
1102
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1192
1103
|
}
|
|
1193
1104
|
}
|
|
1194
1105
|
fbuffer_append_char(buffer, ']');
|
|
1195
1106
|
}
|
|
1196
1107
|
|
|
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
1108
|
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1277
1109
|
{
|
|
1278
1110
|
VALUE tmp;
|
|
1279
1111
|
if (rb_respond_to(obj, i_to_json)) {
|
|
1280
|
-
tmp =
|
|
1112
|
+
tmp = json_call_to_json(data, obj);
|
|
1281
1113
|
Check_Type(tmp, T_STRING);
|
|
1282
1114
|
fbuffer_append_str(buffer, tmp);
|
|
1283
1115
|
} else {
|
|
@@ -1319,19 +1151,9 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1319
1151
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1320
1152
|
{
|
|
1321
1153
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1322
|
-
fbuffer_append_str(buffer, tmp);
|
|
1154
|
+
fbuffer_append_str(buffer, StringValue(tmp));
|
|
1323
1155
|
}
|
|
1324
1156
|
|
|
1325
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
1326
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1327
|
-
{
|
|
1328
|
-
if (FIXNUM_P(obj))
|
|
1329
|
-
generate_json_fixnum(buffer, data, obj);
|
|
1330
|
-
else
|
|
1331
|
-
generate_json_bignum(buffer, data, obj);
|
|
1332
|
-
}
|
|
1333
|
-
#endif
|
|
1334
|
-
|
|
1335
1157
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1336
1158
|
{
|
|
1337
1159
|
double value = RFLOAT_VALUE(obj);
|
|
@@ -1340,11 +1162,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1340
1162
|
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
|
|
1341
1163
|
if (!allow_nan) {
|
|
1342
1164
|
if (data->state->strict && data->state->as_json) {
|
|
1343
|
-
VALUE casted_obj =
|
|
1165
|
+
VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1344
1166
|
if (casted_obj != obj) {
|
|
1345
1167
|
increase_depth(data);
|
|
1346
1168
|
generate_json(buffer, data, casted_obj);
|
|
1347
|
-
data->
|
|
1169
|
+
data->depth--;
|
|
1348
1170
|
return;
|
|
1349
1171
|
}
|
|
1350
1172
|
}
|
|
@@ -1357,12 +1179,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1357
1179
|
}
|
|
1358
1180
|
|
|
1359
1181
|
/* This implementation writes directly into the buffer. We reserve
|
|
1360
|
-
* the
|
|
1182
|
+
* the 32 characters that fpconv_dtoa states as its maximum.
|
|
1361
1183
|
*/
|
|
1362
|
-
fbuffer_inc_capa(buffer,
|
|
1184
|
+
fbuffer_inc_capa(buffer, 32);
|
|
1363
1185
|
char* d = buffer->ptr + buffer->len;
|
|
1364
1186
|
int len = fpconv_dtoa(value, d);
|
|
1365
|
-
|
|
1366
1187
|
/* fpconv_dtoa converts a float to its shortest string representation,
|
|
1367
1188
|
* but it adds a ".0" if this is a plain integer.
|
|
1368
1189
|
*/
|
|
@@ -1376,7 +1197,7 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
1376
1197
|
fbuffer_append_str(buffer, fragment);
|
|
1377
1198
|
}
|
|
1378
1199
|
|
|
1379
|
-
static void
|
|
1200
|
+
static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
|
|
1380
1201
|
{
|
|
1381
1202
|
bool as_json_called = false;
|
|
1382
1203
|
start:
|
|
@@ -1403,22 +1224,31 @@ start:
|
|
|
1403
1224
|
generate_json_bignum(buffer, data, obj);
|
|
1404
1225
|
break;
|
|
1405
1226
|
case T_HASH:
|
|
1406
|
-
if (klass != rb_cHash) goto general;
|
|
1227
|
+
if (fallback && klass != rb_cHash) goto general;
|
|
1407
1228
|
generate_json_object(buffer, data, obj);
|
|
1408
1229
|
break;
|
|
1409
1230
|
case T_ARRAY:
|
|
1410
|
-
if (klass != rb_cArray) goto general;
|
|
1231
|
+
if (fallback && klass != rb_cArray) goto general;
|
|
1411
1232
|
generate_json_array(buffer, data, obj);
|
|
1412
1233
|
break;
|
|
1413
1234
|
case T_STRING:
|
|
1414
|
-
if (klass != rb_cString) goto general;
|
|
1415
|
-
|
|
1235
|
+
if (fallback && klass != rb_cString) goto general;
|
|
1236
|
+
|
|
1237
|
+
if (RB_LIKELY(valid_json_string_p(obj))) {
|
|
1238
|
+
raw_generate_json_string(buffer, data, obj);
|
|
1239
|
+
} else if (as_json_called) {
|
|
1240
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1241
|
+
} else {
|
|
1242
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
1243
|
+
as_json_called = true;
|
|
1244
|
+
goto start;
|
|
1245
|
+
}
|
|
1416
1246
|
break;
|
|
1417
1247
|
case T_SYMBOL:
|
|
1418
1248
|
generate_json_symbol(buffer, data, obj);
|
|
1419
1249
|
break;
|
|
1420
1250
|
case T_FLOAT:
|
|
1421
|
-
if (klass != rb_cFloat) goto general;
|
|
1251
|
+
if (fallback && klass != rb_cFloat) goto general;
|
|
1422
1252
|
generate_json_float(buffer, data, obj);
|
|
1423
1253
|
break;
|
|
1424
1254
|
case T_STRUCT:
|
|
@@ -1429,7 +1259,7 @@ start:
|
|
|
1429
1259
|
general:
|
|
1430
1260
|
if (data->state->strict) {
|
|
1431
1261
|
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1432
|
-
obj =
|
|
1262
|
+
obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1433
1263
|
as_json_called = true;
|
|
1434
1264
|
goto start;
|
|
1435
1265
|
} else {
|
|
@@ -1442,26 +1272,34 @@ start:
|
|
|
1442
1272
|
}
|
|
1443
1273
|
}
|
|
1444
1274
|
|
|
1275
|
+
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1276
|
+
{
|
|
1277
|
+
generate_json_general(buffer, data, obj, true);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1281
|
+
{
|
|
1282
|
+
generate_json_general(buffer, data, obj, false);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1445
1285
|
static VALUE generate_json_try(VALUE d)
|
|
1446
1286
|
{
|
|
1447
1287
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1448
1288
|
|
|
1449
1289
|
data->func(data->buffer, data, data->obj);
|
|
1450
1290
|
|
|
1451
|
-
return
|
|
1291
|
+
return fbuffer_finalize(data->buffer);
|
|
1452
1292
|
}
|
|
1453
1293
|
|
|
1454
|
-
static VALUE
|
|
1294
|
+
static VALUE generate_json_ensure(VALUE d)
|
|
1455
1295
|
{
|
|
1456
1296
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1457
1297
|
fbuffer_free(data->buffer);
|
|
1458
1298
|
|
|
1459
|
-
rb_exc_raise(exc);
|
|
1460
|
-
|
|
1461
1299
|
return Qundef;
|
|
1462
1300
|
}
|
|
1463
1301
|
|
|
1464
|
-
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
|
|
1302
|
+
static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
|
|
1465
1303
|
{
|
|
1466
1304
|
GET_STATE(self);
|
|
1467
1305
|
|
|
@@ -1473,14 +1311,13 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
|
1473
1311
|
|
|
1474
1312
|
struct generate_json_data data = {
|
|
1475
1313
|
.buffer = &buffer,
|
|
1476
|
-
.vstate = self
|
|
1314
|
+
.vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
|
|
1477
1315
|
.state = state,
|
|
1316
|
+
.depth = state->depth,
|
|
1478
1317
|
.obj = obj,
|
|
1479
1318
|
.func = func
|
|
1480
1319
|
};
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
return fbuffer_finalize(&buffer);
|
|
1320
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1484
1321
|
}
|
|
1485
1322
|
|
|
1486
1323
|
/* call-seq:
|
|
@@ -1496,10 +1333,16 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1496
1333
|
rb_check_arity(argc, 1, 2);
|
|
1497
1334
|
VALUE obj = argv[0];
|
|
1498
1335
|
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1336
|
+
return cState_partial_generate(self, obj, generate_json, io);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/* :nodoc: */
|
|
1340
|
+
static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
|
|
1341
|
+
{
|
|
1342
|
+
rb_check_arity(argc, 1, 2);
|
|
1343
|
+
VALUE obj = argv[0];
|
|
1344
|
+
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1345
|
+
return cState_partial_generate(self, obj, generate_json_no_fallback, io);
|
|
1503
1346
|
}
|
|
1504
1347
|
|
|
1505
1348
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
@@ -1580,6 +1423,7 @@ static VALUE string_config(VALUE config)
|
|
|
1580
1423
|
*/
|
|
1581
1424
|
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
1582
1425
|
{
|
|
1426
|
+
rb_check_frozen(self);
|
|
1583
1427
|
GET_STATE(self);
|
|
1584
1428
|
RB_OBJ_WRITE(self, &state->indent, string_config(indent));
|
|
1585
1429
|
return Qnil;
|
|
@@ -1605,6 +1449,7 @@ static VALUE cState_space(VALUE self)
|
|
|
1605
1449
|
*/
|
|
1606
1450
|
static VALUE cState_space_set(VALUE self, VALUE space)
|
|
1607
1451
|
{
|
|
1452
|
+
rb_check_frozen(self);
|
|
1608
1453
|
GET_STATE(self);
|
|
1609
1454
|
RB_OBJ_WRITE(self, &state->space, string_config(space));
|
|
1610
1455
|
return Qnil;
|
|
@@ -1628,6 +1473,7 @@ static VALUE cState_space_before(VALUE self)
|
|
|
1628
1473
|
*/
|
|
1629
1474
|
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
1630
1475
|
{
|
|
1476
|
+
rb_check_frozen(self);
|
|
1631
1477
|
GET_STATE(self);
|
|
1632
1478
|
RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
|
|
1633
1479
|
return Qnil;
|
|
@@ -1653,6 +1499,7 @@ static VALUE cState_object_nl(VALUE self)
|
|
|
1653
1499
|
*/
|
|
1654
1500
|
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
1655
1501
|
{
|
|
1502
|
+
rb_check_frozen(self);
|
|
1656
1503
|
GET_STATE(self);
|
|
1657
1504
|
RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
|
|
1658
1505
|
return Qnil;
|
|
@@ -1676,6 +1523,7 @@ static VALUE cState_array_nl(VALUE self)
|
|
|
1676
1523
|
*/
|
|
1677
1524
|
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
1678
1525
|
{
|
|
1526
|
+
rb_check_frozen(self);
|
|
1679
1527
|
GET_STATE(self);
|
|
1680
1528
|
RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
|
|
1681
1529
|
return Qnil;
|
|
@@ -1699,6 +1547,7 @@ static VALUE cState_as_json(VALUE self)
|
|
|
1699
1547
|
*/
|
|
1700
1548
|
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
|
1701
1549
|
{
|
|
1550
|
+
rb_check_frozen(self);
|
|
1702
1551
|
GET_STATE(self);
|
|
1703
1552
|
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
|
1704
1553
|
return Qnil;
|
|
@@ -1733,6 +1582,17 @@ static long long_config(VALUE num)
|
|
|
1733
1582
|
return RTEST(num) ? FIX2LONG(num) : 0;
|
|
1734
1583
|
}
|
|
1735
1584
|
|
|
1585
|
+
// depth must never be negative; reject early with a clear error.
|
|
1586
|
+
static long depth_config(VALUE num)
|
|
1587
|
+
{
|
|
1588
|
+
if (!RTEST(num)) return 0;
|
|
1589
|
+
long d = NUM2LONG(num);
|
|
1590
|
+
if (RB_UNLIKELY(d < 0)) {
|
|
1591
|
+
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1592
|
+
}
|
|
1593
|
+
return d;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1736
1596
|
/*
|
|
1737
1597
|
* call-seq: max_nesting=(depth)
|
|
1738
1598
|
*
|
|
@@ -1741,6 +1601,7 @@ static long long_config(VALUE num)
|
|
|
1741
1601
|
*/
|
|
1742
1602
|
static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
|
1743
1603
|
{
|
|
1604
|
+
rb_check_frozen(self);
|
|
1744
1605
|
GET_STATE(self);
|
|
1745
1606
|
state->max_nesting = long_config(depth);
|
|
1746
1607
|
return Qnil;
|
|
@@ -1766,6 +1627,7 @@ static VALUE cState_script_safe(VALUE self)
|
|
|
1766
1627
|
*/
|
|
1767
1628
|
static VALUE cState_script_safe_set(VALUE self, VALUE enable)
|
|
1768
1629
|
{
|
|
1630
|
+
rb_check_frozen(self);
|
|
1769
1631
|
GET_STATE(self);
|
|
1770
1632
|
state->script_safe = RTEST(enable);
|
|
1771
1633
|
return Qnil;
|
|
@@ -1797,6 +1659,7 @@ static VALUE cState_strict(VALUE self)
|
|
|
1797
1659
|
*/
|
|
1798
1660
|
static VALUE cState_strict_set(VALUE self, VALUE enable)
|
|
1799
1661
|
{
|
|
1662
|
+
rb_check_frozen(self);
|
|
1800
1663
|
GET_STATE(self);
|
|
1801
1664
|
state->strict = RTEST(enable);
|
|
1802
1665
|
return Qnil;
|
|
@@ -1821,6 +1684,7 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
1821
1684
|
*/
|
|
1822
1685
|
static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
|
|
1823
1686
|
{
|
|
1687
|
+
rb_check_frozen(self);
|
|
1824
1688
|
GET_STATE(self);
|
|
1825
1689
|
state->allow_nan = RTEST(enable);
|
|
1826
1690
|
return Qnil;
|
|
@@ -1845,11 +1709,25 @@ static VALUE cState_ascii_only_p(VALUE self)
|
|
|
1845
1709
|
*/
|
|
1846
1710
|
static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
1847
1711
|
{
|
|
1712
|
+
rb_check_frozen(self);
|
|
1848
1713
|
GET_STATE(self);
|
|
1849
1714
|
state->ascii_only = RTEST(enable);
|
|
1850
1715
|
return Qnil;
|
|
1851
1716
|
}
|
|
1852
1717
|
|
|
1718
|
+
static VALUE cState_allow_duplicate_key_p(VALUE self)
|
|
1719
|
+
{
|
|
1720
|
+
GET_STATE(self);
|
|
1721
|
+
switch (state->on_duplicate_key) {
|
|
1722
|
+
case JSON_IGNORE:
|
|
1723
|
+
return Qtrue;
|
|
1724
|
+
case JSON_DEPRECATED:
|
|
1725
|
+
return Qnil;
|
|
1726
|
+
default:
|
|
1727
|
+
return Qfalse;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1853
1731
|
/*
|
|
1854
1732
|
* call-seq: depth
|
|
1855
1733
|
*
|
|
@@ -1869,8 +1747,9 @@ static VALUE cState_depth(VALUE self)
|
|
|
1869
1747
|
*/
|
|
1870
1748
|
static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
1871
1749
|
{
|
|
1750
|
+
rb_check_frozen(self);
|
|
1872
1751
|
GET_STATE(self);
|
|
1873
|
-
state->depth =
|
|
1752
|
+
state->depth = depth_config(depth);
|
|
1874
1753
|
return Qnil;
|
|
1875
1754
|
}
|
|
1876
1755
|
|
|
@@ -1902,6 +1781,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
|
|
|
1902
1781
|
*/
|
|
1903
1782
|
static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
|
|
1904
1783
|
{
|
|
1784
|
+
rb_check_frozen(self);
|
|
1905
1785
|
GET_STATE(self);
|
|
1906
1786
|
buffer_initial_length_set(state, buffer_initial_length);
|
|
1907
1787
|
return Qnil;
|
|
@@ -1934,13 +1814,15 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1934
1814
|
else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
|
|
1935
1815
|
else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
|
|
1936
1816
|
else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
|
|
1937
|
-
else if (key == sym_depth) { state->depth =
|
|
1817
|
+
else if (key == sym_depth) { state->depth = depth_config(val); }
|
|
1938
1818
|
else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
|
|
1939
1819
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1940
1820
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
1941
1821
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
|
1822
|
+
else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
|
|
1942
1823
|
else if (key == sym_as_json) {
|
|
1943
1824
|
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
|
|
1825
|
+
state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
|
|
1944
1826
|
state_write_value(data, &state->as_json, proc);
|
|
1945
1827
|
}
|
|
1946
1828
|
return ST_CONTINUE;
|
|
@@ -1966,12 +1848,13 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
|
|
|
1966
1848
|
|
|
1967
1849
|
static VALUE cState_configure(VALUE self, VALUE opts)
|
|
1968
1850
|
{
|
|
1851
|
+
rb_check_frozen(self);
|
|
1969
1852
|
GET_STATE(self);
|
|
1970
1853
|
configure_state(state, self, opts);
|
|
1971
1854
|
return self;
|
|
1972
1855
|
}
|
|
1973
1856
|
|
|
1974
|
-
static VALUE
|
|
1857
|
+
static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
|
|
1975
1858
|
{
|
|
1976
1859
|
JSON_Generator_State state = {0};
|
|
1977
1860
|
state_init(&state);
|
|
@@ -1987,17 +1870,23 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
|
1987
1870
|
.buffer = &buffer,
|
|
1988
1871
|
.vstate = Qfalse,
|
|
1989
1872
|
.state = &state,
|
|
1873
|
+
.depth = state.depth,
|
|
1990
1874
|
.obj = obj,
|
|
1991
|
-
.func =
|
|
1875
|
+
.func = func,
|
|
1992
1876
|
};
|
|
1993
|
-
|
|
1877
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1878
|
+
}
|
|
1994
1879
|
|
|
1995
|
-
|
|
1880
|
+
static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1881
|
+
{
|
|
1882
|
+
return cState_m_do_generate(klass, obj, opts, io, generate_json);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1886
|
+
{
|
|
1887
|
+
return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
|
|
1996
1888
|
}
|
|
1997
1889
|
|
|
1998
|
-
/*
|
|
1999
|
-
*
|
|
2000
|
-
*/
|
|
2001
1890
|
void Init_generator(void)
|
|
2002
1891
|
{
|
|
2003
1892
|
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
@@ -2062,51 +1951,12 @@ void Init_generator(void)
|
|
|
2062
1951
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2063
1952
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2064
1953
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
2065
|
-
|
|
1954
|
+
rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
|
|
2066
1955
|
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
|
|
2070
|
-
|
|
2071
|
-
VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object");
|
|
2072
|
-
rb_define_method(mObject, "to_json", mObject_to_json, -1);
|
|
2073
|
-
|
|
2074
|
-
VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash");
|
|
2075
|
-
rb_define_method(mHash, "to_json", mHash_to_json, -1);
|
|
2076
|
-
|
|
2077
|
-
VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array");
|
|
2078
|
-
rb_define_method(mArray, "to_json", mArray_to_json, -1);
|
|
2079
|
-
|
|
2080
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
2081
|
-
VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
|
|
2082
|
-
rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
|
|
2083
|
-
#else
|
|
2084
|
-
VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
|
|
2085
|
-
rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
|
|
2086
|
-
|
|
2087
|
-
VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
|
|
2088
|
-
rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
|
|
2089
|
-
#endif
|
|
2090
|
-
VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float");
|
|
2091
|
-
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
|
2092
|
-
|
|
2093
|
-
VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
|
|
2094
|
-
rb_define_singleton_method(mString, "included", mString_included_s, 1);
|
|
2095
|
-
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
|
-
|
|
2102
|
-
VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
|
2103
|
-
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
|
2104
|
-
|
|
2105
|
-
VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
|
|
2106
|
-
rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
|
|
1956
|
+
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2107
1957
|
|
|
2108
|
-
|
|
2109
|
-
|
|
1958
|
+
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
1959
|
+
rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
|
|
2110
1960
|
|
|
2111
1961
|
rb_global_variable(&Encoding_UTF_8);
|
|
2112
1962
|
Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
|
|
@@ -2114,10 +1964,6 @@ void Init_generator(void)
|
|
|
2114
1964
|
i_to_s = rb_intern("to_s");
|
|
2115
1965
|
i_to_json = rb_intern("to_json");
|
|
2116
1966
|
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
1967
|
i_encode = rb_intern("encode");
|
|
2122
1968
|
|
|
2123
1969
|
sym_indent = ID2SYM(rb_intern("indent"));
|
|
@@ -2134,6 +1980,7 @@ void Init_generator(void)
|
|
|
2134
1980
|
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
|
2135
1981
|
sym_strict = ID2SYM(rb_intern("strict"));
|
|
2136
1982
|
sym_as_json = ID2SYM(rb_intern("as_json"));
|
|
1983
|
+
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
|
2137
1984
|
|
|
2138
1985
|
usascii_encindex = rb_usascii_encindex();
|
|
2139
1986
|
utf8_encindex = rb_utf8_encindex();
|
|
@@ -2141,22 +1988,5 @@ void Init_generator(void)
|
|
|
2141
1988
|
|
|
2142
1989
|
rb_require("json/ext/generator/state");
|
|
2143
1990
|
|
|
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
|
-
}
|
|
1991
|
+
simd_impl = find_simd_implementation();
|
|
2162
1992
|
}
|