json 2.15.1 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +46 -1
- data/LEGAL +12 -0
- data/README.md +17 -1
- data/ext/json/ext/fbuffer/fbuffer.h +30 -77
- data/ext/json/ext/generator/extconf.rb +1 -1
- data/ext/json/ext/generator/generator.c +288 -472
- data/ext/json/ext/json.h +105 -0
- data/ext/json/ext/parser/extconf.rb +2 -1
- data/ext/json/ext/parser/parser.c +597 -474
- data/ext/json/ext/simd/simd.h +42 -22
- data/ext/json/ext/vendor/fpconv.c +3 -3
- data/ext/json/ext/vendor/ryu.h +819 -0
- data/lib/json/common.rb +69 -26
- data/lib/json/ext/generator/state.rb +5 -1
- data/lib/json/truffle_ruby/generator.rb +66 -22
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +33 -0
- metadata +4 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#include "
|
|
1
|
+
#include "../json.h"
|
|
2
2
|
#include "../fbuffer/fbuffer.h"
|
|
3
3
|
#include "../vendor/fpconv.c"
|
|
4
4
|
|
|
@@ -36,13 +36,9 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
36
36
|
bool strict;
|
|
37
37
|
} JSON_Generator_State;
|
|
38
38
|
|
|
39
|
-
#ifndef RB_UNLIKELY
|
|
40
|
-
#define RB_UNLIKELY(cond) (cond)
|
|
41
|
-
#endif
|
|
42
|
-
|
|
43
39
|
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
44
40
|
|
|
45
|
-
static ID i_to_s, i_to_json, i_new,
|
|
41
|
+
static ID i_to_s, i_to_json, i_new, i_encode;
|
|
46
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,
|
|
47
43
|
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
|
48
44
|
|
|
@@ -64,8 +60,11 @@ struct generate_json_data {
|
|
|
64
60
|
JSON_Generator_State *state;
|
|
65
61
|
VALUE obj;
|
|
66
62
|
generator_func func;
|
|
63
|
+
long depth;
|
|
67
64
|
};
|
|
68
65
|
|
|
66
|
+
static SIMD_Implementation simd_impl;
|
|
67
|
+
|
|
69
68
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
70
69
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
|
71
70
|
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -75,9 +74,6 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
|
75
74
|
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
76
75
|
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
77
76
|
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
78
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
79
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
80
|
-
#endif
|
|
81
77
|
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
82
78
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
83
79
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -85,23 +81,18 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
85
81
|
|
|
86
82
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
RBIMPL_ATTR_NORETURN()
|
|
90
|
-
#endif
|
|
91
|
-
static void raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
84
|
+
NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
92
85
|
{
|
|
86
|
+
rb_enc_associate_index(str, utf8_encindex);
|
|
93
87
|
VALUE exc = rb_exc_new_str(eGeneratorError, str);
|
|
94
88
|
rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
|
|
95
89
|
rb_exc_raise(exc);
|
|
96
90
|
}
|
|
97
91
|
|
|
98
|
-
#ifdef RBIMPL_ATTR_NORETURN
|
|
99
|
-
RBIMPL_ATTR_NORETURN()
|
|
100
|
-
#endif
|
|
101
92
|
#ifdef RBIMPL_ATTR_FORMAT
|
|
102
93
|
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
|
|
103
94
|
#endif
|
|
104
|
-
static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
95
|
+
NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
105
96
|
{
|
|
106
97
|
va_list args;
|
|
107
98
|
va_start(args, fmt);
|
|
@@ -136,13 +127,7 @@ typedef struct _search_state {
|
|
|
136
127
|
#endif /* HAVE_SIMD */
|
|
137
128
|
} search_state;
|
|
138
129
|
|
|
139
|
-
|
|
140
|
-
#define FORCE_INLINE __attribute__((always_inline))
|
|
141
|
-
#else
|
|
142
|
-
#define FORCE_INLINE
|
|
143
|
-
#endif
|
|
144
|
-
|
|
145
|
-
static inline FORCE_INLINE void search_flush(search_state *search)
|
|
130
|
+
ALWAYS_INLINE(static) void search_flush(search_state *search)
|
|
146
131
|
{
|
|
147
132
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
|
148
133
|
// escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
|
|
@@ -169,8 +154,6 @@ static const unsigned char escape_table_basic[256] = {
|
|
|
169
154
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
170
155
|
};
|
|
171
156
|
|
|
172
|
-
static unsigned char (*search_escape_basic_impl)(search_state *);
|
|
173
|
-
|
|
174
157
|
static inline unsigned char search_escape_basic(search_state *search)
|
|
175
158
|
{
|
|
176
159
|
while (search->ptr < search->end) {
|
|
@@ -185,7 +168,7 @@ static inline unsigned char search_escape_basic(search_state *search)
|
|
|
185
168
|
return 0;
|
|
186
169
|
}
|
|
187
170
|
|
|
188
|
-
static
|
|
171
|
+
ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
189
172
|
{
|
|
190
173
|
const unsigned char ch = (unsigned char)*search->ptr;
|
|
191
174
|
switch (ch) {
|
|
@@ -226,11 +209,39 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
|
|
|
226
209
|
* Everything else (should be UTF-8) is just passed through and
|
|
227
210
|
* appended to the result.
|
|
228
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
|
+
|
|
229
222
|
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
230
223
|
{
|
|
231
|
-
|
|
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)) {
|
|
237
|
+
escape_UTF8_char_basic(search);
|
|
238
|
+
}
|
|
239
|
+
#endif
|
|
240
|
+
#else
|
|
241
|
+
while (search_escape_basic(search)) {
|
|
232
242
|
escape_UTF8_char_basic(search);
|
|
233
243
|
}
|
|
244
|
+
#endif /* HAVE_SIMD */
|
|
234
245
|
}
|
|
235
246
|
|
|
236
247
|
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
@@ -272,8 +283,10 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
272
283
|
|
|
273
284
|
#ifdef HAVE_SIMD
|
|
274
285
|
|
|
275
|
-
static
|
|
286
|
+
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
276
287
|
{
|
|
288
|
+
RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
|
|
289
|
+
|
|
277
290
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
278
291
|
search_flush(search);
|
|
279
292
|
|
|
@@ -283,19 +296,25 @@ static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsi
|
|
|
283
296
|
char *s = (buf->ptr + buf->len);
|
|
284
297
|
|
|
285
298
|
// Pad the buffer with dummy characters that won't need escaping.
|
|
286
|
-
// This seem
|
|
287
|
-
|
|
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);
|
|
288
302
|
|
|
289
303
|
// Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
|
|
290
304
|
// to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
|
|
291
|
-
|
|
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
|
+
}
|
|
292
311
|
|
|
293
312
|
return s;
|
|
294
313
|
}
|
|
295
314
|
|
|
296
315
|
#ifdef HAVE_SIMD_NEON
|
|
297
316
|
|
|
298
|
-
static
|
|
317
|
+
ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
|
|
299
318
|
{
|
|
300
319
|
uint64_t mask = search->matches_mask;
|
|
301
320
|
uint32_t index = trailing_zeros64(mask) >> 2;
|
|
@@ -409,7 +428,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
|
|
|
409
428
|
|
|
410
429
|
#ifdef HAVE_SIMD_SSE2
|
|
411
430
|
|
|
412
|
-
static
|
|
431
|
+
ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
|
|
413
432
|
{
|
|
414
433
|
int mask = search->matches_mask;
|
|
415
434
|
int index = trailing_zeros(mask);
|
|
@@ -433,7 +452,7 @@ static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
|
|
|
433
452
|
#define TARGET_SSE2
|
|
434
453
|
#endif
|
|
435
454
|
|
|
436
|
-
static
|
|
455
|
+
ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
|
|
437
456
|
{
|
|
438
457
|
if (RB_UNLIKELY(search->has_matches)) {
|
|
439
458
|
// There are more matches if search->matches_mask > 0.
|
|
@@ -681,233 +700,6 @@ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned
|
|
|
681
700
|
}
|
|
682
701
|
}
|
|
683
702
|
|
|
684
|
-
/*
|
|
685
|
-
* Document-module: JSON::Ext::Generator
|
|
686
|
-
*
|
|
687
|
-
* This is the JSON generator implemented as a C extension. It can be
|
|
688
|
-
* configured to be used by setting
|
|
689
|
-
*
|
|
690
|
-
* JSON.generator = JSON::Ext::Generator
|
|
691
|
-
*
|
|
692
|
-
* with the method generator= in JSON.
|
|
693
|
-
*
|
|
694
|
-
*/
|
|
695
|
-
|
|
696
|
-
/* Explanation of the following: that's the only way to not pollute
|
|
697
|
-
* standard library's docs with GeneratorMethods::<ClassName> which
|
|
698
|
-
* are uninformative and take a large place in a list of classes
|
|
699
|
-
*/
|
|
700
|
-
|
|
701
|
-
/*
|
|
702
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods
|
|
703
|
-
* :nodoc:
|
|
704
|
-
*/
|
|
705
|
-
|
|
706
|
-
/*
|
|
707
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Array
|
|
708
|
-
* :nodoc:
|
|
709
|
-
*/
|
|
710
|
-
|
|
711
|
-
/*
|
|
712
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
|
|
713
|
-
* :nodoc:
|
|
714
|
-
*/
|
|
715
|
-
|
|
716
|
-
/*
|
|
717
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
|
|
718
|
-
* :nodoc:
|
|
719
|
-
*/
|
|
720
|
-
|
|
721
|
-
/*
|
|
722
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
|
|
723
|
-
* :nodoc:
|
|
724
|
-
*/
|
|
725
|
-
|
|
726
|
-
/*
|
|
727
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Float
|
|
728
|
-
* :nodoc:
|
|
729
|
-
*/
|
|
730
|
-
|
|
731
|
-
/*
|
|
732
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
|
|
733
|
-
* :nodoc:
|
|
734
|
-
*/
|
|
735
|
-
|
|
736
|
-
/*
|
|
737
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
|
|
738
|
-
* :nodoc:
|
|
739
|
-
*/
|
|
740
|
-
|
|
741
|
-
/*
|
|
742
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
|
|
743
|
-
* :nodoc:
|
|
744
|
-
*/
|
|
745
|
-
|
|
746
|
-
/*
|
|
747
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Object
|
|
748
|
-
* :nodoc:
|
|
749
|
-
*/
|
|
750
|
-
|
|
751
|
-
/*
|
|
752
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::String
|
|
753
|
-
* :nodoc:
|
|
754
|
-
*/
|
|
755
|
-
|
|
756
|
-
/*
|
|
757
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
|
|
758
|
-
* :nodoc:
|
|
759
|
-
*/
|
|
760
|
-
|
|
761
|
-
/*
|
|
762
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
|
|
763
|
-
* :nodoc:
|
|
764
|
-
*/
|
|
765
|
-
|
|
766
|
-
/*
|
|
767
|
-
* call-seq: to_json(state = nil)
|
|
768
|
-
*
|
|
769
|
-
* Returns a JSON string containing a JSON object, that is generated from
|
|
770
|
-
* this Hash instance.
|
|
771
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
772
|
-
* produced JSON string output further.
|
|
773
|
-
*/
|
|
774
|
-
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
|
|
775
|
-
{
|
|
776
|
-
rb_check_arity(argc, 0, 1);
|
|
777
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
778
|
-
return cState_partial_generate(Vstate, self, generate_json_object, Qfalse);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
/*
|
|
782
|
-
* call-seq: to_json(state = nil)
|
|
783
|
-
*
|
|
784
|
-
* Returns a JSON string containing a JSON array, that is generated from
|
|
785
|
-
* this Array instance.
|
|
786
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
787
|
-
* produced JSON string output further.
|
|
788
|
-
*/
|
|
789
|
-
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self)
|
|
790
|
-
{
|
|
791
|
-
rb_check_arity(argc, 0, 1);
|
|
792
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
793
|
-
return cState_partial_generate(Vstate, self, generate_json_array, Qfalse);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
797
|
-
/*
|
|
798
|
-
* call-seq: to_json(*)
|
|
799
|
-
*
|
|
800
|
-
* Returns a JSON string representation for this Integer number.
|
|
801
|
-
*/
|
|
802
|
-
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
|
|
803
|
-
{
|
|
804
|
-
rb_check_arity(argc, 0, 1);
|
|
805
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
806
|
-
return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
#else
|
|
810
|
-
/*
|
|
811
|
-
* call-seq: to_json(*)
|
|
812
|
-
*
|
|
813
|
-
* Returns a JSON string representation for this Integer number.
|
|
814
|
-
*/
|
|
815
|
-
static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
|
|
816
|
-
{
|
|
817
|
-
rb_check_arity(argc, 0, 1);
|
|
818
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
819
|
-
return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/*
|
|
823
|
-
* call-seq: to_json(*)
|
|
824
|
-
*
|
|
825
|
-
* Returns a JSON string representation for this Integer number.
|
|
826
|
-
*/
|
|
827
|
-
static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
|
|
828
|
-
{
|
|
829
|
-
rb_check_arity(argc, 0, 1);
|
|
830
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
831
|
-
return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse);
|
|
832
|
-
}
|
|
833
|
-
#endif
|
|
834
|
-
|
|
835
|
-
/*
|
|
836
|
-
* call-seq: to_json(*)
|
|
837
|
-
*
|
|
838
|
-
* Returns a JSON string representation for this Float number.
|
|
839
|
-
*/
|
|
840
|
-
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
841
|
-
{
|
|
842
|
-
rb_check_arity(argc, 0, 1);
|
|
843
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
844
|
-
return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
/*
|
|
848
|
-
* call-seq: to_json(*)
|
|
849
|
-
*
|
|
850
|
-
* This string should be encoded with UTF-8 A call to this method
|
|
851
|
-
* returns a JSON string encoded with UTF16 big endian characters as
|
|
852
|
-
* \u????.
|
|
853
|
-
*/
|
|
854
|
-
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
|
855
|
-
{
|
|
856
|
-
rb_check_arity(argc, 0, 1);
|
|
857
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
858
|
-
return cState_partial_generate(Vstate, self, generate_json_string, Qfalse);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
/*
|
|
862
|
-
* call-seq: to_json(*)
|
|
863
|
-
*
|
|
864
|
-
* Returns a JSON string for true: 'true'.
|
|
865
|
-
*/
|
|
866
|
-
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
867
|
-
{
|
|
868
|
-
rb_check_arity(argc, 0, 1);
|
|
869
|
-
return rb_utf8_str_new("true", 4);
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
/*
|
|
873
|
-
* call-seq: to_json(*)
|
|
874
|
-
*
|
|
875
|
-
* Returns a JSON string for false: 'false'.
|
|
876
|
-
*/
|
|
877
|
-
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
878
|
-
{
|
|
879
|
-
rb_check_arity(argc, 0, 1);
|
|
880
|
-
return rb_utf8_str_new("false", 5);
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/*
|
|
884
|
-
* call-seq: to_json(*)
|
|
885
|
-
*
|
|
886
|
-
* Returns a JSON string for nil: 'null'.
|
|
887
|
-
*/
|
|
888
|
-
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
889
|
-
{
|
|
890
|
-
rb_check_arity(argc, 0, 1);
|
|
891
|
-
return rb_utf8_str_new("null", 4);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
/*
|
|
895
|
-
* call-seq: to_json(*)
|
|
896
|
-
*
|
|
897
|
-
* Converts this object to a string (calling #to_s), converts
|
|
898
|
-
* it to a JSON string, and returns the result. This is a fallback, if no
|
|
899
|
-
* special method #to_json was defined for some object.
|
|
900
|
-
*/
|
|
901
|
-
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
|
|
902
|
-
{
|
|
903
|
-
VALUE state;
|
|
904
|
-
VALUE string = rb_funcall(self, i_to_s, 0);
|
|
905
|
-
rb_scan_args(argc, argv, "01", &state);
|
|
906
|
-
Check_Type(string, T_STRING);
|
|
907
|
-
state = cState_from_state_s(cState, state);
|
|
908
|
-
return cState_partial_generate(state, string, generate_json_string, Qfalse);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
703
|
static void State_mark(void *ptr)
|
|
912
704
|
{
|
|
913
705
|
JSON_Generator_State *state = ptr;
|
|
@@ -941,11 +733,6 @@ static size_t State_memsize(const void *ptr)
|
|
|
941
733
|
return sizeof(JSON_Generator_State);
|
|
942
734
|
}
|
|
943
735
|
|
|
944
|
-
#ifndef HAVE_RB_EXT_RACTOR_SAFE
|
|
945
|
-
# undef RUBY_TYPED_FROZEN_SHAREABLE
|
|
946
|
-
# define RUBY_TYPED_FROZEN_SHAREABLE 0
|
|
947
|
-
#endif
|
|
948
|
-
|
|
949
736
|
static const rb_data_type_t JSON_Generator_State_type = {
|
|
950
737
|
"JSON/Generator/State",
|
|
951
738
|
{
|
|
@@ -987,21 +774,24 @@ static void vstate_spill(struct generate_json_data *data)
|
|
|
987
774
|
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
|
988
775
|
}
|
|
989
776
|
|
|
990
|
-
static inline VALUE
|
|
777
|
+
static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
|
|
991
778
|
{
|
|
992
779
|
if (RB_UNLIKELY(!data->vstate)) {
|
|
993
780
|
vstate_spill(data);
|
|
994
781
|
}
|
|
995
|
-
|
|
782
|
+
GET_STATE(data->vstate);
|
|
783
|
+
state->depth = data->depth;
|
|
784
|
+
VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
|
|
785
|
+
// no need to restore state->depth, vstate is just a temporary State
|
|
786
|
+
return tmp;
|
|
996
787
|
}
|
|
997
788
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
};
|
|
789
|
+
static VALUE
|
|
790
|
+
json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
|
|
791
|
+
{
|
|
792
|
+
VALUE proc_args[2] = {object, is_key};
|
|
793
|
+
return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
|
|
794
|
+
}
|
|
1005
795
|
|
|
1006
796
|
static VALUE
|
|
1007
797
|
convert_string_subclass(VALUE key)
|
|
@@ -1018,8 +808,131 @@ convert_string_subclass(VALUE key)
|
|
|
1018
808
|
return key_to_s;
|
|
1019
809
|
}
|
|
1020
810
|
|
|
1021
|
-
|
|
1022
|
-
|
|
811
|
+
static bool enc_utf8_compatible_p(int enc_idx)
|
|
812
|
+
{
|
|
813
|
+
if (enc_idx == usascii_encindex) return true;
|
|
814
|
+
if (enc_idx == utf8_encindex) return true;
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
static VALUE encode_json_string_try(VALUE str)
|
|
819
|
+
{
|
|
820
|
+
return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
824
|
+
{
|
|
825
|
+
raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
|
|
826
|
+
return Qundef;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
static inline bool valid_json_string_p(VALUE str)
|
|
830
|
+
{
|
|
831
|
+
int coderange = rb_enc_str_coderange(str);
|
|
832
|
+
|
|
833
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
|
|
838
|
+
return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
845
|
+
{
|
|
846
|
+
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
847
|
+
return str;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
|
|
851
|
+
VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
|
|
852
|
+
if (coerced_str != str) {
|
|
853
|
+
if (RB_TYPE_P(coerced_str, T_STRING)) {
|
|
854
|
+
if (!valid_json_string_p(coerced_str)) {
|
|
855
|
+
raise_generator_error(str, "source sequence is illegal/malformed utf-8");
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
// as_json could return another type than T_STRING
|
|
859
|
+
if (is_key) {
|
|
860
|
+
raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
return coerced_str;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
|
|
869
|
+
VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
|
|
870
|
+
switch (rb_enc_str_coderange(utf8_string)) {
|
|
871
|
+
case ENC_CODERANGE_7BIT:
|
|
872
|
+
return utf8_string;
|
|
873
|
+
case ENC_CODERANGE_VALID:
|
|
874
|
+
// For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
|
|
875
|
+
// TODO: Raise in 3.0.0
|
|
876
|
+
rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
|
|
877
|
+
return utf8_string;
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
886
|
+
{
|
|
887
|
+
fbuffer_append_char(buffer, '"');
|
|
888
|
+
|
|
889
|
+
long len;
|
|
890
|
+
search_state search;
|
|
891
|
+
search.buffer = buffer;
|
|
892
|
+
RSTRING_GETMEM(obj, search.ptr, len);
|
|
893
|
+
search.cursor = search.ptr;
|
|
894
|
+
search.end = search.ptr + len;
|
|
895
|
+
|
|
896
|
+
#ifdef HAVE_SIMD
|
|
897
|
+
search.matches_mask = 0;
|
|
898
|
+
search.has_matches = false;
|
|
899
|
+
search.chunk_base = NULL;
|
|
900
|
+
search.chunk_end = NULL;
|
|
901
|
+
#endif /* HAVE_SIMD */
|
|
902
|
+
|
|
903
|
+
switch (rb_enc_str_coderange(obj)) {
|
|
904
|
+
case ENC_CODERANGE_7BIT:
|
|
905
|
+
case ENC_CODERANGE_VALID:
|
|
906
|
+
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
907
|
+
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
|
908
|
+
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
|
909
|
+
convert_UTF8_to_script_safe_JSON(&search);
|
|
910
|
+
} else {
|
|
911
|
+
convert_UTF8_to_JSON(&search);
|
|
912
|
+
}
|
|
913
|
+
break;
|
|
914
|
+
default:
|
|
915
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
fbuffer_append_char(buffer, '"');
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
922
|
+
{
|
|
923
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
924
|
+
raw_generate_json_string(buffer, data, obj);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
struct hash_foreach_arg {
|
|
928
|
+
VALUE hash;
|
|
929
|
+
struct generate_json_data *data;
|
|
930
|
+
int first_key_type;
|
|
931
|
+
bool first;
|
|
932
|
+
bool mixed_keys_encountered;
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
NOINLINE(static) void
|
|
1023
936
|
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
1024
937
|
{
|
|
1025
938
|
if (arg->mixed_keys_encountered) {
|
|
@@ -1034,13 +947,6 @@ json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
|
1034
947
|
}
|
|
1035
948
|
}
|
|
1036
949
|
|
|
1037
|
-
static VALUE
|
|
1038
|
-
json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
|
|
1039
|
-
{
|
|
1040
|
-
VALUE proc_args[2] = {object, is_key};
|
|
1041
|
-
return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
950
|
static int
|
|
1045
951
|
json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
1046
952
|
{
|
|
@@ -1050,7 +956,7 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1050
956
|
FBuffer *buffer = data->buffer;
|
|
1051
957
|
JSON_Generator_State *state = data->state;
|
|
1052
958
|
|
|
1053
|
-
long depth =
|
|
959
|
+
long depth = data->depth;
|
|
1054
960
|
int key_type = rb_type(key);
|
|
1055
961
|
|
|
1056
962
|
if (arg->first) {
|
|
@@ -1106,8 +1012,10 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1106
1012
|
break;
|
|
1107
1013
|
}
|
|
1108
1014
|
|
|
1015
|
+
key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
|
|
1016
|
+
|
|
1109
1017
|
if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
|
|
1110
|
-
|
|
1018
|
+
raw_generate_json_string(buffer, data, key_to_s);
|
|
1111
1019
|
} else {
|
|
1112
1020
|
generate_json(buffer, data, key_to_s);
|
|
1113
1021
|
}
|
|
@@ -1122,9 +1030,9 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1122
1030
|
static inline long increase_depth(struct generate_json_data *data)
|
|
1123
1031
|
{
|
|
1124
1032
|
JSON_Generator_State *state = data->state;
|
|
1125
|
-
long depth = ++
|
|
1033
|
+
long depth = ++data->depth;
|
|
1126
1034
|
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
|
1127
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", --
|
|
1035
|
+
rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
|
|
1128
1036
|
}
|
|
1129
1037
|
return depth;
|
|
1130
1038
|
}
|
|
@@ -1135,7 +1043,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1135
1043
|
|
|
1136
1044
|
if (RHASH_SIZE(obj) == 0) {
|
|
1137
1045
|
fbuffer_append(buffer, "{}", 2);
|
|
1138
|
-
--data->
|
|
1046
|
+
--data->depth;
|
|
1139
1047
|
return;
|
|
1140
1048
|
}
|
|
1141
1049
|
|
|
@@ -1148,7 +1056,7 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1148
1056
|
};
|
|
1149
1057
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
1150
1058
|
|
|
1151
|
-
depth = --data->
|
|
1059
|
+
depth = --data->depth;
|
|
1152
1060
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1153
1061
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1154
1062
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
@@ -1164,7 +1072,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1164
1072
|
|
|
1165
1073
|
if (RARRAY_LEN(obj) == 0) {
|
|
1166
1074
|
fbuffer_append(buffer, "[]", 2);
|
|
1167
|
-
--data->
|
|
1075
|
+
--data->depth;
|
|
1168
1076
|
return;
|
|
1169
1077
|
}
|
|
1170
1078
|
|
|
@@ -1180,7 +1088,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1180
1088
|
}
|
|
1181
1089
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
1182
1090
|
}
|
|
1183
|
-
data->
|
|
1091
|
+
data->depth = --depth;
|
|
1184
1092
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
|
1185
1093
|
fbuffer_append_str(buffer, data->state->array_nl);
|
|
1186
1094
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
@@ -1190,90 +1098,11 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data
|
|
|
1190
1098
|
fbuffer_append_char(buffer, ']');
|
|
1191
1099
|
}
|
|
1192
1100
|
|
|
1193
|
-
static inline int enc_utf8_compatible_p(int enc_idx)
|
|
1194
|
-
{
|
|
1195
|
-
if (enc_idx == usascii_encindex) return 1;
|
|
1196
|
-
if (enc_idx == utf8_encindex) return 1;
|
|
1197
|
-
return 0;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
static VALUE encode_json_string_try(VALUE str)
|
|
1201
|
-
{
|
|
1202
|
-
return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
1206
|
-
{
|
|
1207
|
-
raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
|
|
1208
|
-
return Qundef;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
static inline VALUE ensure_valid_encoding(VALUE str)
|
|
1212
|
-
{
|
|
1213
|
-
int encindex = RB_ENCODING_GET(str);
|
|
1214
|
-
VALUE utf8_string;
|
|
1215
|
-
if (RB_UNLIKELY(!enc_utf8_compatible_p(encindex))) {
|
|
1216
|
-
if (encindex == binary_encindex) {
|
|
1217
|
-
utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
|
|
1218
|
-
switch (rb_enc_str_coderange(utf8_string)) {
|
|
1219
|
-
case ENC_CODERANGE_7BIT:
|
|
1220
|
-
return utf8_string;
|
|
1221
|
-
case ENC_CODERANGE_VALID:
|
|
1222
|
-
// For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
|
|
1223
|
-
// TODO: Raise in 3.0.0
|
|
1224
|
-
rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
|
|
1225
|
-
return utf8_string;
|
|
1226
|
-
break;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
str = rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
1231
|
-
}
|
|
1232
|
-
return str;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1236
|
-
{
|
|
1237
|
-
obj = ensure_valid_encoding(obj);
|
|
1238
|
-
|
|
1239
|
-
fbuffer_append_char(buffer, '"');
|
|
1240
|
-
|
|
1241
|
-
long len;
|
|
1242
|
-
search_state search;
|
|
1243
|
-
search.buffer = buffer;
|
|
1244
|
-
RSTRING_GETMEM(obj, search.ptr, len);
|
|
1245
|
-
search.cursor = search.ptr;
|
|
1246
|
-
search.end = search.ptr + len;
|
|
1247
|
-
|
|
1248
|
-
#ifdef HAVE_SIMD
|
|
1249
|
-
search.matches_mask = 0;
|
|
1250
|
-
search.has_matches = false;
|
|
1251
|
-
search.chunk_base = NULL;
|
|
1252
|
-
#endif /* HAVE_SIMD */
|
|
1253
|
-
|
|
1254
|
-
switch (rb_enc_str_coderange(obj)) {
|
|
1255
|
-
case ENC_CODERANGE_7BIT:
|
|
1256
|
-
case ENC_CODERANGE_VALID:
|
|
1257
|
-
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
1258
|
-
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
|
1259
|
-
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
|
1260
|
-
convert_UTF8_to_script_safe_JSON(&search);
|
|
1261
|
-
} else {
|
|
1262
|
-
convert_UTF8_to_JSON(&search);
|
|
1263
|
-
}
|
|
1264
|
-
break;
|
|
1265
|
-
default:
|
|
1266
|
-
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1267
|
-
break;
|
|
1268
|
-
}
|
|
1269
|
-
fbuffer_append_char(buffer, '"');
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
1101
|
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1273
1102
|
{
|
|
1274
1103
|
VALUE tmp;
|
|
1275
1104
|
if (rb_respond_to(obj, i_to_json)) {
|
|
1276
|
-
tmp =
|
|
1105
|
+
tmp = json_call_to_json(data, obj);
|
|
1277
1106
|
Check_Type(tmp, T_STRING);
|
|
1278
1107
|
fbuffer_append_str(buffer, tmp);
|
|
1279
1108
|
} else {
|
|
@@ -1315,18 +1144,8 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1315
1144
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1316
1145
|
{
|
|
1317
1146
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1318
|
-
fbuffer_append_str(buffer, tmp);
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
1322
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1323
|
-
{
|
|
1324
|
-
if (FIXNUM_P(obj))
|
|
1325
|
-
generate_json_fixnum(buffer, data, obj);
|
|
1326
|
-
else
|
|
1327
|
-
generate_json_bignum(buffer, data, obj);
|
|
1147
|
+
fbuffer_append_str(buffer, StringValue(tmp));
|
|
1328
1148
|
}
|
|
1329
|
-
#endif
|
|
1330
1149
|
|
|
1331
1150
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1332
1151
|
{
|
|
@@ -1340,7 +1159,7 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1340
1159
|
if (casted_obj != obj) {
|
|
1341
1160
|
increase_depth(data);
|
|
1342
1161
|
generate_json(buffer, data, casted_obj);
|
|
1343
|
-
data->
|
|
1162
|
+
data->depth--;
|
|
1344
1163
|
return;
|
|
1345
1164
|
}
|
|
1346
1165
|
}
|
|
@@ -1371,7 +1190,7 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
1371
1190
|
fbuffer_append_str(buffer, fragment);
|
|
1372
1191
|
}
|
|
1373
1192
|
|
|
1374
|
-
static void
|
|
1193
|
+
static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
|
|
1375
1194
|
{
|
|
1376
1195
|
bool as_json_called = false;
|
|
1377
1196
|
start:
|
|
@@ -1398,22 +1217,31 @@ start:
|
|
|
1398
1217
|
generate_json_bignum(buffer, data, obj);
|
|
1399
1218
|
break;
|
|
1400
1219
|
case T_HASH:
|
|
1401
|
-
if (klass != rb_cHash) goto general;
|
|
1220
|
+
if (fallback && klass != rb_cHash) goto general;
|
|
1402
1221
|
generate_json_object(buffer, data, obj);
|
|
1403
1222
|
break;
|
|
1404
1223
|
case T_ARRAY:
|
|
1405
|
-
if (klass != rb_cArray) goto general;
|
|
1224
|
+
if (fallback && klass != rb_cArray) goto general;
|
|
1406
1225
|
generate_json_array(buffer, data, obj);
|
|
1407
1226
|
break;
|
|
1408
1227
|
case T_STRING:
|
|
1409
|
-
if (klass != rb_cString) goto general;
|
|
1410
|
-
|
|
1228
|
+
if (fallback && klass != rb_cString) goto general;
|
|
1229
|
+
|
|
1230
|
+
if (RB_LIKELY(valid_json_string_p(obj))) {
|
|
1231
|
+
raw_generate_json_string(buffer, data, obj);
|
|
1232
|
+
} else if (as_json_called) {
|
|
1233
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1234
|
+
} else {
|
|
1235
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
1236
|
+
as_json_called = true;
|
|
1237
|
+
goto start;
|
|
1238
|
+
}
|
|
1411
1239
|
break;
|
|
1412
1240
|
case T_SYMBOL:
|
|
1413
1241
|
generate_json_symbol(buffer, data, obj);
|
|
1414
1242
|
break;
|
|
1415
1243
|
case T_FLOAT:
|
|
1416
|
-
if (klass != rb_cFloat) goto general;
|
|
1244
|
+
if (fallback && klass != rb_cFloat) goto general;
|
|
1417
1245
|
generate_json_float(buffer, data, obj);
|
|
1418
1246
|
break;
|
|
1419
1247
|
case T_STRUCT:
|
|
@@ -1437,26 +1265,34 @@ start:
|
|
|
1437
1265
|
}
|
|
1438
1266
|
}
|
|
1439
1267
|
|
|
1268
|
+
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1269
|
+
{
|
|
1270
|
+
generate_json_general(buffer, data, obj, true);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1274
|
+
{
|
|
1275
|
+
generate_json_general(buffer, data, obj, false);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1440
1278
|
static VALUE generate_json_try(VALUE d)
|
|
1441
1279
|
{
|
|
1442
1280
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1443
1281
|
|
|
1444
1282
|
data->func(data->buffer, data, data->obj);
|
|
1445
1283
|
|
|
1446
|
-
return
|
|
1284
|
+
return fbuffer_finalize(data->buffer);
|
|
1447
1285
|
}
|
|
1448
1286
|
|
|
1449
|
-
static VALUE
|
|
1287
|
+
static VALUE generate_json_ensure(VALUE d)
|
|
1450
1288
|
{
|
|
1451
1289
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1452
1290
|
fbuffer_free(data->buffer);
|
|
1453
1291
|
|
|
1454
|
-
rb_exc_raise(exc);
|
|
1455
|
-
|
|
1456
1292
|
return Qundef;
|
|
1457
1293
|
}
|
|
1458
1294
|
|
|
1459
|
-
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
|
|
1295
|
+
static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
|
|
1460
1296
|
{
|
|
1461
1297
|
GET_STATE(self);
|
|
1462
1298
|
|
|
@@ -1468,14 +1304,13 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
|
1468
1304
|
|
|
1469
1305
|
struct generate_json_data data = {
|
|
1470
1306
|
.buffer = &buffer,
|
|
1471
|
-
.vstate = self
|
|
1307
|
+
.vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
|
|
1472
1308
|
.state = state,
|
|
1309
|
+
.depth = state->depth,
|
|
1473
1310
|
.obj = obj,
|
|
1474
1311
|
.func = func
|
|
1475
1312
|
};
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
return fbuffer_finalize(&buffer);
|
|
1313
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1479
1314
|
}
|
|
1480
1315
|
|
|
1481
1316
|
/* call-seq:
|
|
@@ -1491,10 +1326,16 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1491
1326
|
rb_check_arity(argc, 1, 2);
|
|
1492
1327
|
VALUE obj = argv[0];
|
|
1493
1328
|
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1329
|
+
return cState_partial_generate(self, obj, generate_json, io);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
/* :nodoc: */
|
|
1333
|
+
static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
|
|
1334
|
+
{
|
|
1335
|
+
rb_check_arity(argc, 1, 2);
|
|
1336
|
+
VALUE obj = argv[0];
|
|
1337
|
+
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1338
|
+
return cState_partial_generate(self, obj, generate_json_no_fallback, io);
|
|
1498
1339
|
}
|
|
1499
1340
|
|
|
1500
1341
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
@@ -1575,6 +1416,7 @@ static VALUE string_config(VALUE config)
|
|
|
1575
1416
|
*/
|
|
1576
1417
|
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
1577
1418
|
{
|
|
1419
|
+
rb_check_frozen(self);
|
|
1578
1420
|
GET_STATE(self);
|
|
1579
1421
|
RB_OBJ_WRITE(self, &state->indent, string_config(indent));
|
|
1580
1422
|
return Qnil;
|
|
@@ -1600,6 +1442,7 @@ static VALUE cState_space(VALUE self)
|
|
|
1600
1442
|
*/
|
|
1601
1443
|
static VALUE cState_space_set(VALUE self, VALUE space)
|
|
1602
1444
|
{
|
|
1445
|
+
rb_check_frozen(self);
|
|
1603
1446
|
GET_STATE(self);
|
|
1604
1447
|
RB_OBJ_WRITE(self, &state->space, string_config(space));
|
|
1605
1448
|
return Qnil;
|
|
@@ -1623,6 +1466,7 @@ static VALUE cState_space_before(VALUE self)
|
|
|
1623
1466
|
*/
|
|
1624
1467
|
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
1625
1468
|
{
|
|
1469
|
+
rb_check_frozen(self);
|
|
1626
1470
|
GET_STATE(self);
|
|
1627
1471
|
RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
|
|
1628
1472
|
return Qnil;
|
|
@@ -1648,6 +1492,7 @@ static VALUE cState_object_nl(VALUE self)
|
|
|
1648
1492
|
*/
|
|
1649
1493
|
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
1650
1494
|
{
|
|
1495
|
+
rb_check_frozen(self);
|
|
1651
1496
|
GET_STATE(self);
|
|
1652
1497
|
RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
|
|
1653
1498
|
return Qnil;
|
|
@@ -1671,6 +1516,7 @@ static VALUE cState_array_nl(VALUE self)
|
|
|
1671
1516
|
*/
|
|
1672
1517
|
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
1673
1518
|
{
|
|
1519
|
+
rb_check_frozen(self);
|
|
1674
1520
|
GET_STATE(self);
|
|
1675
1521
|
RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
|
|
1676
1522
|
return Qnil;
|
|
@@ -1694,6 +1540,7 @@ static VALUE cState_as_json(VALUE self)
|
|
|
1694
1540
|
*/
|
|
1695
1541
|
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
|
1696
1542
|
{
|
|
1543
|
+
rb_check_frozen(self);
|
|
1697
1544
|
GET_STATE(self);
|
|
1698
1545
|
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
|
1699
1546
|
return Qnil;
|
|
@@ -1728,6 +1575,17 @@ static long long_config(VALUE num)
|
|
|
1728
1575
|
return RTEST(num) ? FIX2LONG(num) : 0;
|
|
1729
1576
|
}
|
|
1730
1577
|
|
|
1578
|
+
// depth must never be negative; reject early with a clear error.
|
|
1579
|
+
static long depth_config(VALUE num)
|
|
1580
|
+
{
|
|
1581
|
+
if (!RTEST(num)) return 0;
|
|
1582
|
+
long d = NUM2LONG(num);
|
|
1583
|
+
if (RB_UNLIKELY(d < 0)) {
|
|
1584
|
+
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1585
|
+
}
|
|
1586
|
+
return d;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1731
1589
|
/*
|
|
1732
1590
|
* call-seq: max_nesting=(depth)
|
|
1733
1591
|
*
|
|
@@ -1736,6 +1594,7 @@ static long long_config(VALUE num)
|
|
|
1736
1594
|
*/
|
|
1737
1595
|
static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
|
1738
1596
|
{
|
|
1597
|
+
rb_check_frozen(self);
|
|
1739
1598
|
GET_STATE(self);
|
|
1740
1599
|
state->max_nesting = long_config(depth);
|
|
1741
1600
|
return Qnil;
|
|
@@ -1761,6 +1620,7 @@ static VALUE cState_script_safe(VALUE self)
|
|
|
1761
1620
|
*/
|
|
1762
1621
|
static VALUE cState_script_safe_set(VALUE self, VALUE enable)
|
|
1763
1622
|
{
|
|
1623
|
+
rb_check_frozen(self);
|
|
1764
1624
|
GET_STATE(self);
|
|
1765
1625
|
state->script_safe = RTEST(enable);
|
|
1766
1626
|
return Qnil;
|
|
@@ -1792,6 +1652,7 @@ static VALUE cState_strict(VALUE self)
|
|
|
1792
1652
|
*/
|
|
1793
1653
|
static VALUE cState_strict_set(VALUE self, VALUE enable)
|
|
1794
1654
|
{
|
|
1655
|
+
rb_check_frozen(self);
|
|
1795
1656
|
GET_STATE(self);
|
|
1796
1657
|
state->strict = RTEST(enable);
|
|
1797
1658
|
return Qnil;
|
|
@@ -1816,6 +1677,7 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
1816
1677
|
*/
|
|
1817
1678
|
static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
|
|
1818
1679
|
{
|
|
1680
|
+
rb_check_frozen(self);
|
|
1819
1681
|
GET_STATE(self);
|
|
1820
1682
|
state->allow_nan = RTEST(enable);
|
|
1821
1683
|
return Qnil;
|
|
@@ -1840,6 +1702,7 @@ static VALUE cState_ascii_only_p(VALUE self)
|
|
|
1840
1702
|
*/
|
|
1841
1703
|
static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
1842
1704
|
{
|
|
1705
|
+
rb_check_frozen(self);
|
|
1843
1706
|
GET_STATE(self);
|
|
1844
1707
|
state->ascii_only = RTEST(enable);
|
|
1845
1708
|
return Qnil;
|
|
@@ -1877,8 +1740,9 @@ static VALUE cState_depth(VALUE self)
|
|
|
1877
1740
|
*/
|
|
1878
1741
|
static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
1879
1742
|
{
|
|
1743
|
+
rb_check_frozen(self);
|
|
1880
1744
|
GET_STATE(self);
|
|
1881
|
-
state->depth =
|
|
1745
|
+
state->depth = depth_config(depth);
|
|
1882
1746
|
return Qnil;
|
|
1883
1747
|
}
|
|
1884
1748
|
|
|
@@ -1910,6 +1774,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
|
|
|
1910
1774
|
*/
|
|
1911
1775
|
static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
|
|
1912
1776
|
{
|
|
1777
|
+
rb_check_frozen(self);
|
|
1913
1778
|
GET_STATE(self);
|
|
1914
1779
|
buffer_initial_length_set(state, buffer_initial_length);
|
|
1915
1780
|
return Qnil;
|
|
@@ -1942,7 +1807,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1942
1807
|
else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
|
|
1943
1808
|
else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
|
|
1944
1809
|
else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
|
|
1945
|
-
else if (key == sym_depth) { state->depth =
|
|
1810
|
+
else if (key == sym_depth) { state->depth = depth_config(val); }
|
|
1946
1811
|
else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
|
|
1947
1812
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1948
1813
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
@@ -1976,12 +1841,13 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
|
|
|
1976
1841
|
|
|
1977
1842
|
static VALUE cState_configure(VALUE self, VALUE opts)
|
|
1978
1843
|
{
|
|
1844
|
+
rb_check_frozen(self);
|
|
1979
1845
|
GET_STATE(self);
|
|
1980
1846
|
configure_state(state, self, opts);
|
|
1981
1847
|
return self;
|
|
1982
1848
|
}
|
|
1983
1849
|
|
|
1984
|
-
static VALUE
|
|
1850
|
+
static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
|
|
1985
1851
|
{
|
|
1986
1852
|
JSON_Generator_State state = {0};
|
|
1987
1853
|
state_init(&state);
|
|
@@ -1997,17 +1863,23 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
|
1997
1863
|
.buffer = &buffer,
|
|
1998
1864
|
.vstate = Qfalse,
|
|
1999
1865
|
.state = &state,
|
|
1866
|
+
.depth = state.depth,
|
|
2000
1867
|
.obj = obj,
|
|
2001
|
-
.func =
|
|
1868
|
+
.func = func,
|
|
2002
1869
|
};
|
|
2003
|
-
|
|
1870
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1871
|
+
}
|
|
2004
1872
|
|
|
2005
|
-
|
|
1873
|
+
static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1874
|
+
{
|
|
1875
|
+
return cState_m_do_generate(klass, obj, opts, io, generate_json);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1879
|
+
{
|
|
1880
|
+
return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
|
|
2006
1881
|
}
|
|
2007
1882
|
|
|
2008
|
-
/*
|
|
2009
|
-
*
|
|
2010
|
-
*/
|
|
2011
1883
|
void Init_generator(void)
|
|
2012
1884
|
{
|
|
2013
1885
|
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
@@ -2072,47 +1944,12 @@ void Init_generator(void)
|
|
|
2072
1944
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2073
1945
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2074
1946
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
2075
|
-
|
|
1947
|
+
rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
|
|
2076
1948
|
|
|
2077
1949
|
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2078
1950
|
|
|
2079
1951
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
2080
|
-
|
|
2081
|
-
VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
|
|
2082
|
-
|
|
2083
|
-
VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object");
|
|
2084
|
-
rb_define_method(mObject, "to_json", mObject_to_json, -1);
|
|
2085
|
-
|
|
2086
|
-
VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash");
|
|
2087
|
-
rb_define_method(mHash, "to_json", mHash_to_json, -1);
|
|
2088
|
-
|
|
2089
|
-
VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array");
|
|
2090
|
-
rb_define_method(mArray, "to_json", mArray_to_json, -1);
|
|
2091
|
-
|
|
2092
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
2093
|
-
VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
|
|
2094
|
-
rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
|
|
2095
|
-
#else
|
|
2096
|
-
VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
|
|
2097
|
-
rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
|
|
2098
|
-
|
|
2099
|
-
VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
|
|
2100
|
-
rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
|
|
2101
|
-
#endif
|
|
2102
|
-
VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float");
|
|
2103
|
-
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
|
2104
|
-
|
|
2105
|
-
VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
|
|
2106
|
-
rb_define_method(mString, "to_json", mString_to_json, -1);
|
|
2107
|
-
|
|
2108
|
-
VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
|
2109
|
-
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
|
2110
|
-
|
|
2111
|
-
VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
|
|
2112
|
-
rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
|
|
2113
|
-
|
|
2114
|
-
VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
|
|
2115
|
-
rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
|
|
1952
|
+
rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
|
|
2116
1953
|
|
|
2117
1954
|
rb_global_variable(&Encoding_UTF_8);
|
|
2118
1955
|
Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
|
|
@@ -2120,10 +1957,6 @@ void Init_generator(void)
|
|
|
2120
1957
|
i_to_s = rb_intern("to_s");
|
|
2121
1958
|
i_to_json = rb_intern("to_json");
|
|
2122
1959
|
i_new = rb_intern("new");
|
|
2123
|
-
i_pack = rb_intern("pack");
|
|
2124
|
-
i_unpack = rb_intern("unpack");
|
|
2125
|
-
i_create_id = rb_intern("create_id");
|
|
2126
|
-
i_extend = rb_intern("extend");
|
|
2127
1960
|
i_encode = rb_intern("encode");
|
|
2128
1961
|
|
|
2129
1962
|
sym_indent = ID2SYM(rb_intern("indent"));
|
|
@@ -2148,22 +1981,5 @@ void Init_generator(void)
|
|
|
2148
1981
|
|
|
2149
1982
|
rb_require("json/ext/generator/state");
|
|
2150
1983
|
|
|
2151
|
-
|
|
2152
|
-
switch (find_simd_implementation()) {
|
|
2153
|
-
#ifdef HAVE_SIMD
|
|
2154
|
-
#ifdef HAVE_SIMD_NEON
|
|
2155
|
-
case SIMD_NEON:
|
|
2156
|
-
search_escape_basic_impl = search_escape_basic_neon;
|
|
2157
|
-
break;
|
|
2158
|
-
#endif /* HAVE_SIMD_NEON */
|
|
2159
|
-
#ifdef HAVE_SIMD_SSE2
|
|
2160
|
-
case SIMD_SSE2:
|
|
2161
|
-
search_escape_basic_impl = search_escape_basic_sse2;
|
|
2162
|
-
break;
|
|
2163
|
-
#endif /* HAVE_SIMD_SSE2 */
|
|
2164
|
-
#endif /* HAVE_SIMD */
|
|
2165
|
-
default:
|
|
2166
|
-
search_escape_basic_impl = search_escape_basic;
|
|
2167
|
-
break;
|
|
2168
|
-
}
|
|
1984
|
+
simd_impl = find_simd_implementation();
|
|
2169
1985
|
}
|