json 2.13.2 → 3.0.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 +208 -8
- data/LEGAL +12 -0
- data/README.md +34 -87
- data/ext/json/ext/fbuffer/fbuffer.h +75 -86
- data/ext/json/ext/generator/extconf.rb +4 -1
- data/ext/json/ext/generator/generator.c +494 -596
- data/ext/json/ext/json.h +196 -0
- data/ext/json/ext/parser/extconf.rb +29 -1
- data/ext/json/ext/parser/parser.c +2091 -723
- data/ext/json/ext/simd/simd.h +42 -22
- data/ext/json/ext/vendor/fast_float_parser.h +814 -0
- data/ext/json/ext/vendor/fpconv.c +13 -12
- data/lib/json/common.rb +199 -389
- data/lib/json/ext/generator/state.rb +8 -37
- data/lib/json/ext.rb +25 -1
- data/lib/json/truffle_ruby/generator.rb +186 -128
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +84 -252
- metadata +5 -18
- data/lib/json/add/bigdecimal.rb +0 -58
- data/lib/json/add/complex.rb +0 -51
- data/lib/json/add/core.rb +0 -12
- data/lib/json/add/date.rb +0 -54
- data/lib/json/add/date_time.rb +0 -67
- data/lib/json/add/exception.rb +0 -49
- data/lib/json/add/ostruct.rb +0 -54
- data/lib/json/add/range.rb +0 -54
- data/lib/json/add/rational.rb +0 -49
- data/lib/json/add/regexp.rb +0 -48
- data/lib/json/add/set.rb +0 -48
- data/lib/json/add/struct.rb +0 -52
- data/lib/json/add/symbol.rb +0 -52
- data/lib/json/add/time.rb +0 -52
- data/lib/json/generic_object.rb +0 -75
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#include "
|
|
1
|
+
#include "../json.h"
|
|
2
2
|
#include "../fbuffer/fbuffer.h"
|
|
3
3
|
#include "../vendor/fpconv.c"
|
|
4
4
|
|
|
@@ -21,21 +21,20 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
21
21
|
long depth;
|
|
22
22
|
long buffer_initial_length;
|
|
23
23
|
|
|
24
|
+
bool allow_duplicate_key;
|
|
25
|
+
bool as_json_single_arg;
|
|
24
26
|
bool allow_nan;
|
|
25
27
|
bool ascii_only;
|
|
26
28
|
bool script_safe;
|
|
27
29
|
bool strict;
|
|
30
|
+
VALUE sort_keys;
|
|
28
31
|
} JSON_Generator_State;
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
#define RB_UNLIKELY(cond) (cond)
|
|
32
|
-
#endif
|
|
33
|
-
|
|
34
|
-
static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
33
|
+
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc;
|
|
35
34
|
|
|
36
|
-
static ID i_to_s, i_to_json, i_new,
|
|
37
|
-
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
|
|
38
|
-
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe,
|
|
35
|
+
static ID i_to_s, i_to_json, i_new, i_encode;
|
|
36
|
+
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,
|
|
37
|
+
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_strict, sym_as_json, sym_sort_keys;
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
#define GET_STATE_TO(self, state) \
|
|
@@ -55,8 +54,11 @@ struct generate_json_data {
|
|
|
55
54
|
JSON_Generator_State *state;
|
|
56
55
|
VALUE obj;
|
|
57
56
|
generator_func func;
|
|
57
|
+
long depth;
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
static SIMD_Implementation simd_impl;
|
|
61
|
+
|
|
60
62
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
61
63
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
|
62
64
|
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -66,9 +68,6 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
|
66
68
|
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
67
69
|
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
68
70
|
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
71
|
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
73
72
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
74
73
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -76,23 +75,18 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
76
75
|
|
|
77
76
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
RBIMPL_ATTR_NORETURN()
|
|
81
|
-
#endif
|
|
82
|
-
static void raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
78
|
+
NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
|
|
83
79
|
{
|
|
80
|
+
rb_enc_associate_index(str, utf8_encindex);
|
|
84
81
|
VALUE exc = rb_exc_new_str(eGeneratorError, str);
|
|
85
82
|
rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
|
|
86
83
|
rb_exc_raise(exc);
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
#ifdef RBIMPL_ATTR_NORETURN
|
|
90
|
-
RBIMPL_ATTR_NORETURN()
|
|
91
|
-
#endif
|
|
92
86
|
#ifdef RBIMPL_ATTR_FORMAT
|
|
93
87
|
RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
|
|
94
88
|
#endif
|
|
95
|
-
static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
89
|
+
NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
96
90
|
{
|
|
97
91
|
va_list args;
|
|
98
92
|
va_start(args, fmt);
|
|
@@ -127,18 +121,12 @@ typedef struct _search_state {
|
|
|
127
121
|
#endif /* HAVE_SIMD */
|
|
128
122
|
} search_state;
|
|
129
123
|
|
|
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)
|
|
124
|
+
ALWAYS_INLINE(static) void search_flush(search_state *search)
|
|
137
125
|
{
|
|
138
126
|
// Do not remove this conditional without profiling, specifically escape-heavy text.
|
|
139
127
|
// 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
|
|
128
|
+
// For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
|
|
129
|
+
// will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
|
|
142
130
|
// consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
|
|
143
131
|
// nothing needs to be flushed, we can save a few memory references with this conditional.
|
|
144
132
|
if (search->ptr > search->cursor) {
|
|
@@ -160,8 +148,6 @@ static const unsigned char escape_table_basic[256] = {
|
|
|
160
148
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
161
149
|
};
|
|
162
150
|
|
|
163
|
-
static unsigned char (*search_escape_basic_impl)(search_state *);
|
|
164
|
-
|
|
165
151
|
static inline unsigned char search_escape_basic(search_state *search)
|
|
166
152
|
{
|
|
167
153
|
while (search->ptr < search->end) {
|
|
@@ -176,7 +162,7 @@ static inline unsigned char search_escape_basic(search_state *search)
|
|
|
176
162
|
return 0;
|
|
177
163
|
}
|
|
178
164
|
|
|
179
|
-
static
|
|
165
|
+
ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
180
166
|
{
|
|
181
167
|
const unsigned char ch = (unsigned char)*search->ptr;
|
|
182
168
|
switch (ch) {
|
|
@@ -217,11 +203,39 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search)
|
|
|
217
203
|
* Everything else (should be UTF-8) is just passed through and
|
|
218
204
|
* appended to the result.
|
|
219
205
|
*/
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
#if defined(HAVE_SIMD_NEON)
|
|
209
|
+
static inline unsigned char search_escape_basic_neon(search_state *search);
|
|
210
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
211
|
+
static inline unsigned char search_escape_basic_sse2(search_state *search);
|
|
212
|
+
#endif
|
|
213
|
+
|
|
214
|
+
static inline unsigned char search_escape_basic(search_state *search);
|
|
215
|
+
|
|
220
216
|
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
221
217
|
{
|
|
222
|
-
|
|
218
|
+
#ifdef HAVE_SIMD
|
|
219
|
+
#if defined(HAVE_SIMD_NEON)
|
|
220
|
+
while (search_escape_basic_neon(search)) {
|
|
221
|
+
escape_UTF8_char_basic(search);
|
|
222
|
+
}
|
|
223
|
+
#elif defined(HAVE_SIMD_SSE2)
|
|
224
|
+
if (simd_impl == SIMD_SSE2) {
|
|
225
|
+
while (search_escape_basic_sse2(search)) {
|
|
226
|
+
escape_UTF8_char_basic(search);
|
|
227
|
+
}
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
while (search_escape_basic(search)) {
|
|
231
|
+
escape_UTF8_char_basic(search);
|
|
232
|
+
}
|
|
233
|
+
#endif
|
|
234
|
+
#else
|
|
235
|
+
while (search_escape_basic(search)) {
|
|
223
236
|
escape_UTF8_char_basic(search);
|
|
224
237
|
}
|
|
238
|
+
#endif /* HAVE_SIMD */
|
|
225
239
|
}
|
|
226
240
|
|
|
227
241
|
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
@@ -263,8 +277,10 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
263
277
|
|
|
264
278
|
#ifdef HAVE_SIMD
|
|
265
279
|
|
|
266
|
-
static
|
|
280
|
+
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
267
281
|
{
|
|
282
|
+
RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
|
|
283
|
+
|
|
268
284
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
269
285
|
search_flush(search);
|
|
270
286
|
|
|
@@ -274,19 +290,25 @@ static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsi
|
|
|
274
290
|
char *s = (buf->ptr + buf->len);
|
|
275
291
|
|
|
276
292
|
// Pad the buffer with dummy characters that won't need escaping.
|
|
277
|
-
// This seem
|
|
278
|
-
|
|
293
|
+
// This seem wasteful at first sight, but memset of vector length is very fast.
|
|
294
|
+
// This is a space as it can be directly represented as an immediate on AArch64.
|
|
295
|
+
memset(s, ' ', vec_len);
|
|
279
296
|
|
|
280
297
|
// Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
|
|
281
298
|
// to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
|
|
282
|
-
|
|
299
|
+
if (vec_len == 16) {
|
|
300
|
+
RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
|
|
301
|
+
json_fast_memcpy16(s, search->ptr, len);
|
|
302
|
+
} else {
|
|
303
|
+
MEMCPY(s, search->ptr, char, len);
|
|
304
|
+
}
|
|
283
305
|
|
|
284
306
|
return s;
|
|
285
307
|
}
|
|
286
308
|
|
|
287
309
|
#ifdef HAVE_SIMD_NEON
|
|
288
310
|
|
|
289
|
-
static
|
|
311
|
+
ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
|
|
290
312
|
{
|
|
291
313
|
uint64_t mask = search->matches_mask;
|
|
292
314
|
uint32_t index = trailing_zeros64(mask) >> 2;
|
|
@@ -400,7 +422,7 @@ static inline unsigned char search_escape_basic_neon(search_state *search)
|
|
|
400
422
|
|
|
401
423
|
#ifdef HAVE_SIMD_SSE2
|
|
402
424
|
|
|
403
|
-
static
|
|
425
|
+
ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
|
|
404
426
|
{
|
|
405
427
|
int mask = search->matches_mask;
|
|
406
428
|
int index = trailing_zeros(mask);
|
|
@@ -424,7 +446,7 @@ static inline FORCE_INLINE unsigned char sse2_next_match(search_state *search)
|
|
|
424
446
|
#define TARGET_SSE2
|
|
425
447
|
#endif
|
|
426
448
|
|
|
427
|
-
static
|
|
449
|
+
ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
|
|
428
450
|
{
|
|
429
451
|
if (RB_UNLIKELY(search->has_matches)) {
|
|
430
452
|
// There are more matches if search->matches_mask > 0.
|
|
@@ -672,290 +694,6 @@ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned
|
|
|
672
694
|
}
|
|
673
695
|
}
|
|
674
696
|
|
|
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
697
|
static void State_mark(void *ptr)
|
|
960
698
|
{
|
|
961
699
|
JSON_Generator_State *state = ptr;
|
|
@@ -965,6 +703,7 @@ static void State_mark(void *ptr)
|
|
|
965
703
|
rb_gc_mark_movable(state->object_nl);
|
|
966
704
|
rb_gc_mark_movable(state->array_nl);
|
|
967
705
|
rb_gc_mark_movable(state->as_json);
|
|
706
|
+
rb_gc_mark_movable(state->sort_keys);
|
|
968
707
|
}
|
|
969
708
|
|
|
970
709
|
static void State_compact(void *ptr)
|
|
@@ -976,34 +715,27 @@ static void State_compact(void *ptr)
|
|
|
976
715
|
state->object_nl = rb_gc_location(state->object_nl);
|
|
977
716
|
state->array_nl = rb_gc_location(state->array_nl);
|
|
978
717
|
state->as_json = rb_gc_location(state->as_json);
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
static void State_free(void *ptr)
|
|
982
|
-
{
|
|
983
|
-
JSON_Generator_State *state = ptr;
|
|
984
|
-
ruby_xfree(state);
|
|
718
|
+
state->sort_keys = rb_gc_location(state->sort_keys);
|
|
985
719
|
}
|
|
986
720
|
|
|
987
721
|
static size_t State_memsize(const void *ptr)
|
|
988
722
|
{
|
|
723
|
+
#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
|
|
724
|
+
return 0;
|
|
725
|
+
#else
|
|
989
726
|
return sizeof(JSON_Generator_State);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
#ifndef HAVE_RB_EXT_RACTOR_SAFE
|
|
993
|
-
# undef RUBY_TYPED_FROZEN_SHAREABLE
|
|
994
|
-
# define RUBY_TYPED_FROZEN_SHAREABLE 0
|
|
995
727
|
#endif
|
|
728
|
+
}
|
|
996
729
|
|
|
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_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
|
|
1007
739
|
};
|
|
1008
740
|
|
|
1009
741
|
static void state_init(JSON_Generator_State *state)
|
|
@@ -1033,20 +765,27 @@ static void vstate_spill(struct generate_json_data *data)
|
|
|
1033
765
|
RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
|
|
1034
766
|
RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
|
|
1035
767
|
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
|
768
|
+
RB_OBJ_WRITTEN(vstate, Qundef, state->sort_keys);
|
|
1036
769
|
}
|
|
1037
770
|
|
|
1038
|
-
static inline VALUE
|
|
771
|
+
static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
|
|
1039
772
|
{
|
|
1040
773
|
if (RB_UNLIKELY(!data->vstate)) {
|
|
1041
774
|
vstate_spill(data);
|
|
1042
775
|
}
|
|
1043
|
-
|
|
776
|
+
GET_STATE(data->vstate);
|
|
777
|
+
state->depth = data->depth;
|
|
778
|
+
VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
|
|
779
|
+
// no need to restore state->depth, vstate is just a temporary State
|
|
780
|
+
return tmp;
|
|
1044
781
|
}
|
|
1045
782
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
};
|
|
783
|
+
static VALUE
|
|
784
|
+
json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
|
|
785
|
+
{
|
|
786
|
+
VALUE proc_args[2] = {object, is_key};
|
|
787
|
+
return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
|
|
788
|
+
}
|
|
1050
789
|
|
|
1051
790
|
static VALUE
|
|
1052
791
|
convert_string_subclass(VALUE key)
|
|
@@ -1063,6 +802,158 @@ convert_string_subclass(VALUE key)
|
|
|
1063
802
|
return key_to_s;
|
|
1064
803
|
}
|
|
1065
804
|
|
|
805
|
+
static bool enc_utf8_compatible_p(int enc_idx)
|
|
806
|
+
{
|
|
807
|
+
if (enc_idx == usascii_encindex) return true;
|
|
808
|
+
if (enc_idx == utf8_encindex) return true;
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
static VALUE encode_json_string_try(VALUE str)
|
|
813
|
+
{
|
|
814
|
+
return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
|
|
818
|
+
{
|
|
819
|
+
raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
|
|
820
|
+
return Qundef;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
static inline int json_str_coderange(VALUE str) {
|
|
824
|
+
int coderange = RB_ENC_CODERANGE(str);
|
|
825
|
+
if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
|
|
826
|
+
coderange = rb_enc_str_coderange(str);
|
|
827
|
+
}
|
|
828
|
+
return coderange;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
static inline bool valid_json_string_p(VALUE str)
|
|
832
|
+
{
|
|
833
|
+
int coderange = json_str_coderange(str);
|
|
834
|
+
|
|
835
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
|
|
840
|
+
return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
847
|
+
{
|
|
848
|
+
if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
|
|
849
|
+
VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
|
|
850
|
+
if (coerced_str != str) {
|
|
851
|
+
if (RB_TYPE_P(coerced_str, T_STRING)) {
|
|
852
|
+
if (!valid_json_string_p(coerced_str)) {
|
|
853
|
+
raise_generator_error(str, "source sequence is illegal/malformed utf-8");
|
|
854
|
+
}
|
|
855
|
+
} else {
|
|
856
|
+
// as_json could return another type than T_STRING
|
|
857
|
+
if (is_key) {
|
|
858
|
+
raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return coerced_str;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
|
|
867
|
+
VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
|
|
868
|
+
switch (rb_enc_str_coderange(utf8_string)) {
|
|
869
|
+
case ENC_CODERANGE_7BIT:
|
|
870
|
+
return utf8_string;
|
|
871
|
+
case ENC_CODERANGE_VALID:
|
|
872
|
+
// For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
|
|
873
|
+
// TODO: Raise in 3.0.0
|
|
874
|
+
rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
|
|
875
|
+
return utf8_string;
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
|
|
884
|
+
{
|
|
885
|
+
if (RB_LIKELY(valid_json_string_p(str))) {
|
|
886
|
+
return str;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
return convert_invalid_encoding(data, str, as_json_called, is_key);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
894
|
+
{
|
|
895
|
+
fbuffer_append_char(buffer, '"');
|
|
896
|
+
|
|
897
|
+
long len;
|
|
898
|
+
search_state search;
|
|
899
|
+
search.buffer = buffer;
|
|
900
|
+
RSTRING_GETMEM(obj, search.ptr, len);
|
|
901
|
+
search.cursor = search.ptr;
|
|
902
|
+
search.end = search.ptr + len;
|
|
903
|
+
|
|
904
|
+
#ifdef HAVE_SIMD
|
|
905
|
+
search.matches_mask = 0;
|
|
906
|
+
search.has_matches = false;
|
|
907
|
+
search.chunk_base = NULL;
|
|
908
|
+
search.chunk_end = NULL;
|
|
909
|
+
#endif /* HAVE_SIMD */
|
|
910
|
+
|
|
911
|
+
switch (json_str_coderange(obj)) {
|
|
912
|
+
case ENC_CODERANGE_7BIT:
|
|
913
|
+
case ENC_CODERANGE_VALID:
|
|
914
|
+
if (RB_UNLIKELY(data->state->ascii_only)) {
|
|
915
|
+
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
|
916
|
+
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
|
917
|
+
convert_UTF8_to_script_safe_JSON(&search);
|
|
918
|
+
} else {
|
|
919
|
+
convert_UTF8_to_JSON(&search);
|
|
920
|
+
}
|
|
921
|
+
break;
|
|
922
|
+
default:
|
|
923
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
fbuffer_append_char(buffer, '"');
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
930
|
+
{
|
|
931
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
932
|
+
raw_generate_json_string(buffer, data, obj);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
struct hash_foreach_arg {
|
|
936
|
+
VALUE hash;
|
|
937
|
+
struct generate_json_data *data;
|
|
938
|
+
int first_key_type;
|
|
939
|
+
bool first;
|
|
940
|
+
bool mixed_keys_encountered;
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
NOINLINE(static) void
|
|
944
|
+
json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
|
|
945
|
+
{
|
|
946
|
+
if (arg->mixed_keys_encountered) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
arg->mixed_keys_encountered = true;
|
|
950
|
+
|
|
951
|
+
JSON_Generator_State *state = arg->data->state;
|
|
952
|
+
if (!state->allow_duplicate_key) {
|
|
953
|
+
rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 1, arg->hash);
|
|
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,48 @@ 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
|
-
|
|
1049
|
+
if (RB_UNLIKELY(data->state->sort_keys)) {
|
|
1050
|
+
obj = rb_proc_call_with_block(data->state->sort_keys, 1, &obj, Qnil);
|
|
1051
|
+
Check_Type(obj, T_HASH);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1132
1054
|
long depth = increase_depth(data);
|
|
1133
1055
|
|
|
1134
1056
|
if (RHASH_SIZE(obj) == 0) {
|
|
1135
1057
|
fbuffer_append(buffer, "{}", 2);
|
|
1136
|
-
--data->
|
|
1058
|
+
--data->depth;
|
|
1137
1059
|
return;
|
|
1138
1060
|
}
|
|
1139
1061
|
|
|
1140
1062
|
fbuffer_append_char(buffer, '{');
|
|
1141
1063
|
|
|
1142
1064
|
struct hash_foreach_arg arg = {
|
|
1065
|
+
.hash = obj,
|
|
1143
1066
|
.data = data,
|
|
1144
|
-
.
|
|
1067
|
+
.first = true,
|
|
1145
1068
|
};
|
|
1146
1069
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
1147
1070
|
|
|
1148
|
-
depth = --data->
|
|
1071
|
+
depth = --data->depth;
|
|
1149
1072
|
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
1150
1073
|
fbuffer_append_str(buffer, data->state->object_nl);
|
|
1151
1074
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1152
|
-
|
|
1153
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1154
|
-
}
|
|
1075
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1155
1076
|
}
|
|
1156
1077
|
}
|
|
1157
1078
|
fbuffer_append_char(buffer, '}');
|
|
@@ -1159,125 +1080,41 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1159
1080
|
|
|
1160
1081
|
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1161
1082
|
{
|
|
1162
|
-
int i, j;
|
|
1163
1083
|
long depth = increase_depth(data);
|
|
1164
1084
|
|
|
1165
1085
|
if (RARRAY_LEN(obj) == 0) {
|
|
1166
1086
|
fbuffer_append(buffer, "[]", 2);
|
|
1167
|
-
--data->
|
|
1087
|
+
--data->depth;
|
|
1168
1088
|
return;
|
|
1169
1089
|
}
|
|
1170
1090
|
|
|
1171
1091
|
fbuffer_append_char(buffer, '[');
|
|
1172
1092
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1173
|
-
for (i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1093
|
+
for (int i = 0; i < RARRAY_LEN(obj); i++) {
|
|
1174
1094
|
if (i > 0) {
|
|
1175
1095
|
fbuffer_append_char(buffer, ',');
|
|
1176
1096
|
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
|
1177
1097
|
}
|
|
1178
1098
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1179
|
-
|
|
1180
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1181
|
-
}
|
|
1099
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1182
1100
|
}
|
|
1183
1101
|
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
1184
1102
|
}
|
|
1185
|
-
data->
|
|
1103
|
+
data->depth = --depth;
|
|
1186
1104
|
if (RB_UNLIKELY(data->state->array_nl)) {
|
|
1187
1105
|
fbuffer_append_str(buffer, data->state->array_nl);
|
|
1188
1106
|
if (RB_UNLIKELY(data->state->indent)) {
|
|
1189
|
-
|
|
1190
|
-
fbuffer_append_str(buffer, data->state->indent);
|
|
1191
|
-
}
|
|
1107
|
+
fbuffer_append_str_repeat(buffer, data->state->indent, depth);
|
|
1192
1108
|
}
|
|
1193
1109
|
}
|
|
1194
1110
|
fbuffer_append_char(buffer, ']');
|
|
1195
1111
|
}
|
|
1196
1112
|
|
|
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
1113
|
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1277
1114
|
{
|
|
1278
1115
|
VALUE tmp;
|
|
1279
1116
|
if (rb_respond_to(obj, i_to_json)) {
|
|
1280
|
-
tmp =
|
|
1117
|
+
tmp = json_call_to_json(data, obj);
|
|
1281
1118
|
Check_Type(tmp, T_STRING);
|
|
1282
1119
|
fbuffer_append_str(buffer, tmp);
|
|
1283
1120
|
} else {
|
|
@@ -1287,15 +1124,6 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d
|
|
|
1287
1124
|
}
|
|
1288
1125
|
}
|
|
1289
1126
|
|
|
1290
|
-
static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1291
|
-
{
|
|
1292
|
-
if (data->state->strict) {
|
|
1293
|
-
generate_json_string(buffer, data, rb_sym2str(obj));
|
|
1294
|
-
} else {
|
|
1295
|
-
generate_json_fallback(buffer, data, obj);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
1127
|
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1300
1128
|
{
|
|
1301
1129
|
fbuffer_append(buffer, "null", 4);
|
|
@@ -1319,19 +1147,9 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1319
1147
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1320
1148
|
{
|
|
1321
1149
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1322
|
-
fbuffer_append_str(buffer, tmp);
|
|
1150
|
+
fbuffer_append_str(buffer, StringValue(tmp));
|
|
1323
1151
|
}
|
|
1324
1152
|
|
|
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
1153
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1336
1154
|
{
|
|
1337
1155
|
double value = RFLOAT_VALUE(obj);
|
|
@@ -1340,11 +1158,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1340
1158
|
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
|
|
1341
1159
|
if (!allow_nan) {
|
|
1342
1160
|
if (data->state->strict && data->state->as_json) {
|
|
1343
|
-
VALUE casted_obj =
|
|
1161
|
+
VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1344
1162
|
if (casted_obj != obj) {
|
|
1345
1163
|
increase_depth(data);
|
|
1346
1164
|
generate_json(buffer, data, casted_obj);
|
|
1347
|
-
data->
|
|
1165
|
+
data->depth--;
|
|
1348
1166
|
return;
|
|
1349
1167
|
}
|
|
1350
1168
|
}
|
|
@@ -1357,12 +1175,11 @@ static void generate_json_float(FBuffer *buffer, struct generate_json_data *data
|
|
|
1357
1175
|
}
|
|
1358
1176
|
|
|
1359
1177
|
/* This implementation writes directly into the buffer. We reserve
|
|
1360
|
-
* the
|
|
1178
|
+
* the 32 characters that fpconv_dtoa states as its maximum.
|
|
1361
1179
|
*/
|
|
1362
|
-
fbuffer_inc_capa(buffer,
|
|
1180
|
+
fbuffer_inc_capa(buffer, 32);
|
|
1363
1181
|
char* d = buffer->ptr + buffer->len;
|
|
1364
1182
|
int len = fpconv_dtoa(value, d);
|
|
1365
|
-
|
|
1366
1183
|
/* fpconv_dtoa converts a float to its shortest string representation,
|
|
1367
1184
|
* but it adds a ".0" if this is a plain integer.
|
|
1368
1185
|
*/
|
|
@@ -1376,7 +1193,7 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
1376
1193
|
fbuffer_append_str(buffer, fragment);
|
|
1377
1194
|
}
|
|
1378
1195
|
|
|
1379
|
-
static void
|
|
1196
|
+
static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
|
|
1380
1197
|
{
|
|
1381
1198
|
bool as_json_called = false;
|
|
1382
1199
|
start:
|
|
@@ -1392,7 +1209,13 @@ start:
|
|
|
1392
1209
|
} else if (RB_FLONUM_P(obj)) {
|
|
1393
1210
|
generate_json_float(buffer, data, obj);
|
|
1394
1211
|
} else if (RB_STATIC_SYM_P(obj)) {
|
|
1395
|
-
|
|
1212
|
+
if (data->state->strict) {
|
|
1213
|
+
obj = rb_sym2str(obj);
|
|
1214
|
+
JSON_ASSERT(RBASIC_CLASS(obj) == rb_cString);
|
|
1215
|
+
goto generate_string;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
generate_json_fallback(buffer, data, obj);
|
|
1396
1219
|
} else {
|
|
1397
1220
|
goto general;
|
|
1398
1221
|
}
|
|
@@ -1403,22 +1226,38 @@ start:
|
|
|
1403
1226
|
generate_json_bignum(buffer, data, obj);
|
|
1404
1227
|
break;
|
|
1405
1228
|
case T_HASH:
|
|
1406
|
-
if (klass != rb_cHash) goto general;
|
|
1229
|
+
if (fallback && klass != rb_cHash) goto general;
|
|
1407
1230
|
generate_json_object(buffer, data, obj);
|
|
1408
1231
|
break;
|
|
1409
1232
|
case T_ARRAY:
|
|
1410
|
-
if (klass != rb_cArray) goto general;
|
|
1233
|
+
if (fallback && klass != rb_cArray) goto general;
|
|
1411
1234
|
generate_json_array(buffer, data, obj);
|
|
1412
1235
|
break;
|
|
1413
1236
|
case T_STRING:
|
|
1414
|
-
if (klass != rb_cString) goto general;
|
|
1415
|
-
|
|
1237
|
+
if (fallback && klass != rb_cString) goto general;
|
|
1238
|
+
|
|
1239
|
+
generate_string:
|
|
1240
|
+
if (RB_LIKELY(valid_json_string_p(obj))) {
|
|
1241
|
+
raw_generate_json_string(buffer, data, obj);
|
|
1242
|
+
} else if (as_json_called) {
|
|
1243
|
+
raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
|
|
1244
|
+
} else {
|
|
1245
|
+
obj = ensure_valid_encoding(data, obj, false, false);
|
|
1246
|
+
as_json_called = true;
|
|
1247
|
+
goto start;
|
|
1248
|
+
}
|
|
1416
1249
|
break;
|
|
1417
1250
|
case T_SYMBOL:
|
|
1418
|
-
|
|
1251
|
+
if (data->state->strict) {
|
|
1252
|
+
obj = rb_sym2str(obj);
|
|
1253
|
+
JSON_ASSERT(RBASIC_CLASS(obj) == rb_cString);
|
|
1254
|
+
goto generate_string;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
generate_json_fallback(buffer, data, obj);
|
|
1419
1258
|
break;
|
|
1420
1259
|
case T_FLOAT:
|
|
1421
|
-
if (klass != rb_cFloat) goto general;
|
|
1260
|
+
if (fallback && klass != rb_cFloat) goto general;
|
|
1422
1261
|
generate_json_float(buffer, data, obj);
|
|
1423
1262
|
break;
|
|
1424
1263
|
case T_STRUCT:
|
|
@@ -1429,7 +1268,7 @@ start:
|
|
|
1429
1268
|
general:
|
|
1430
1269
|
if (data->state->strict) {
|
|
1431
1270
|
if (RTEST(data->state->as_json) && !as_json_called) {
|
|
1432
|
-
obj =
|
|
1271
|
+
obj = json_call_as_json(data->state, obj, Qfalse);
|
|
1433
1272
|
as_json_called = true;
|
|
1434
1273
|
goto start;
|
|
1435
1274
|
} else {
|
|
@@ -1442,45 +1281,50 @@ start:
|
|
|
1442
1281
|
}
|
|
1443
1282
|
}
|
|
1444
1283
|
|
|
1284
|
+
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1285
|
+
{
|
|
1286
|
+
generate_json_general(buffer, data, obj, true);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1290
|
+
{
|
|
1291
|
+
generate_json_general(buffer, data, obj, false);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1445
1294
|
static VALUE generate_json_try(VALUE d)
|
|
1446
1295
|
{
|
|
1447
1296
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1448
1297
|
|
|
1449
1298
|
data->func(data->buffer, data, data->obj);
|
|
1450
1299
|
|
|
1451
|
-
return
|
|
1300
|
+
return fbuffer_finalize(data->buffer);
|
|
1452
1301
|
}
|
|
1453
1302
|
|
|
1454
|
-
static VALUE
|
|
1303
|
+
static VALUE generate_json_ensure(VALUE d)
|
|
1455
1304
|
{
|
|
1456
1305
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1457
1306
|
fbuffer_free(data->buffer);
|
|
1458
1307
|
|
|
1459
|
-
rb_exc_raise(exc);
|
|
1460
|
-
|
|
1461
1308
|
return Qundef;
|
|
1462
1309
|
}
|
|
1463
1310
|
|
|
1464
|
-
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
|
|
1311
|
+
static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
|
|
1465
1312
|
{
|
|
1466
1313
|
GET_STATE(self);
|
|
1467
1314
|
|
|
1468
1315
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1469
|
-
FBuffer buffer = {
|
|
1470
|
-
|
|
1471
|
-
};
|
|
1472
|
-
fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1316
|
+
FBuffer buffer = { 0 };
|
|
1317
|
+
fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1473
1318
|
|
|
1474
1319
|
struct generate_json_data data = {
|
|
1475
1320
|
.buffer = &buffer,
|
|
1476
|
-
.vstate = self
|
|
1321
|
+
.vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
|
|
1477
1322
|
.state = state,
|
|
1323
|
+
.depth = state->depth,
|
|
1478
1324
|
.obj = obj,
|
|
1479
1325
|
.func = func
|
|
1480
1326
|
};
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
return fbuffer_finalize(&buffer);
|
|
1327
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1484
1328
|
}
|
|
1485
1329
|
|
|
1486
1330
|
/* call-seq:
|
|
@@ -1496,10 +1340,16 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1496
1340
|
rb_check_arity(argc, 1, 2);
|
|
1497
1341
|
VALUE obj = argv[0];
|
|
1498
1342
|
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1343
|
+
return cState_partial_generate(self, obj, generate_json, io);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/* :nodoc: */
|
|
1347
|
+
static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
|
|
1348
|
+
{
|
|
1349
|
+
rb_check_arity(argc, 1, 2);
|
|
1350
|
+
VALUE obj = argv[0];
|
|
1351
|
+
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1352
|
+
return cState_partial_generate(self, obj, generate_json_no_fallback, io);
|
|
1503
1353
|
}
|
|
1504
1354
|
|
|
1505
1355
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
@@ -1524,12 +1374,15 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
|
1524
1374
|
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
|
|
1525
1375
|
|
|
1526
1376
|
MEMCPY(objState, origState, JSON_Generator_State, 1);
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1377
|
+
|
|
1378
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
|
|
1379
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space);
|
|
1380
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
|
|
1381
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
|
|
1382
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
|
|
1383
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
|
|
1384
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->sort_keys);
|
|
1385
|
+
|
|
1533
1386
|
return obj;
|
|
1534
1387
|
}
|
|
1535
1388
|
|
|
@@ -1580,6 +1433,7 @@ static VALUE string_config(VALUE config)
|
|
|
1580
1433
|
*/
|
|
1581
1434
|
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
1582
1435
|
{
|
|
1436
|
+
rb_check_frozen(self);
|
|
1583
1437
|
GET_STATE(self);
|
|
1584
1438
|
RB_OBJ_WRITE(self, &state->indent, string_config(indent));
|
|
1585
1439
|
return Qnil;
|
|
@@ -1605,6 +1459,7 @@ static VALUE cState_space(VALUE self)
|
|
|
1605
1459
|
*/
|
|
1606
1460
|
static VALUE cState_space_set(VALUE self, VALUE space)
|
|
1607
1461
|
{
|
|
1462
|
+
rb_check_frozen(self);
|
|
1608
1463
|
GET_STATE(self);
|
|
1609
1464
|
RB_OBJ_WRITE(self, &state->space, string_config(space));
|
|
1610
1465
|
return Qnil;
|
|
@@ -1628,6 +1483,7 @@ static VALUE cState_space_before(VALUE self)
|
|
|
1628
1483
|
*/
|
|
1629
1484
|
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
1630
1485
|
{
|
|
1486
|
+
rb_check_frozen(self);
|
|
1631
1487
|
GET_STATE(self);
|
|
1632
1488
|
RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
|
|
1633
1489
|
return Qnil;
|
|
@@ -1653,6 +1509,7 @@ static VALUE cState_object_nl(VALUE self)
|
|
|
1653
1509
|
*/
|
|
1654
1510
|
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
1655
1511
|
{
|
|
1512
|
+
rb_check_frozen(self);
|
|
1656
1513
|
GET_STATE(self);
|
|
1657
1514
|
RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
|
|
1658
1515
|
return Qnil;
|
|
@@ -1676,6 +1533,7 @@ static VALUE cState_array_nl(VALUE self)
|
|
|
1676
1533
|
*/
|
|
1677
1534
|
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
1678
1535
|
{
|
|
1536
|
+
rb_check_frozen(self);
|
|
1679
1537
|
GET_STATE(self);
|
|
1680
1538
|
RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
|
|
1681
1539
|
return Qnil;
|
|
@@ -1699,6 +1557,7 @@ static VALUE cState_as_json(VALUE self)
|
|
|
1699
1557
|
*/
|
|
1700
1558
|
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
|
1701
1559
|
{
|
|
1560
|
+
rb_check_frozen(self);
|
|
1702
1561
|
GET_STATE(self);
|
|
1703
1562
|
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
|
1704
1563
|
return Qnil;
|
|
@@ -1730,7 +1589,21 @@ static VALUE cState_max_nesting(VALUE self)
|
|
|
1730
1589
|
|
|
1731
1590
|
static long long_config(VALUE num)
|
|
1732
1591
|
{
|
|
1733
|
-
return RTEST(num) ?
|
|
1592
|
+
return RTEST(num) ? NUM2LONG(num) : 0;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// depth must never be negative; reject early with a clear error.
|
|
1596
|
+
static long depth_config(VALUE num)
|
|
1597
|
+
{
|
|
1598
|
+
if (!RTEST(num)) return 0;
|
|
1599
|
+
long d = NUM2LONG(num);
|
|
1600
|
+
if (RB_UNLIKELY(d < 0)) {
|
|
1601
|
+
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1602
|
+
}
|
|
1603
|
+
if (RB_UNLIKELY(d > INT_MAX)) {
|
|
1604
|
+
rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
|
|
1605
|
+
}
|
|
1606
|
+
return d;
|
|
1734
1607
|
}
|
|
1735
1608
|
|
|
1736
1609
|
/*
|
|
@@ -1741,6 +1614,7 @@ static long long_config(VALUE num)
|
|
|
1741
1614
|
*/
|
|
1742
1615
|
static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
|
1743
1616
|
{
|
|
1617
|
+
rb_check_frozen(self);
|
|
1744
1618
|
GET_STATE(self);
|
|
1745
1619
|
state->max_nesting = long_config(depth);
|
|
1746
1620
|
return Qnil;
|
|
@@ -1766,6 +1640,7 @@ static VALUE cState_script_safe(VALUE self)
|
|
|
1766
1640
|
*/
|
|
1767
1641
|
static VALUE cState_script_safe_set(VALUE self, VALUE enable)
|
|
1768
1642
|
{
|
|
1643
|
+
rb_check_frozen(self);
|
|
1769
1644
|
GET_STATE(self);
|
|
1770
1645
|
state->script_safe = RTEST(enable);
|
|
1771
1646
|
return Qnil;
|
|
@@ -1797,6 +1672,7 @@ static VALUE cState_strict(VALUE self)
|
|
|
1797
1672
|
*/
|
|
1798
1673
|
static VALUE cState_strict_set(VALUE self, VALUE enable)
|
|
1799
1674
|
{
|
|
1675
|
+
rb_check_frozen(self);
|
|
1800
1676
|
GET_STATE(self);
|
|
1801
1677
|
state->strict = RTEST(enable);
|
|
1802
1678
|
return Qnil;
|
|
@@ -1821,6 +1697,7 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
1821
1697
|
*/
|
|
1822
1698
|
static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
|
|
1823
1699
|
{
|
|
1700
|
+
rb_check_frozen(self);
|
|
1824
1701
|
GET_STATE(self);
|
|
1825
1702
|
state->allow_nan = RTEST(enable);
|
|
1826
1703
|
return Qnil;
|
|
@@ -1845,11 +1722,67 @@ static VALUE cState_ascii_only_p(VALUE self)
|
|
|
1845
1722
|
*/
|
|
1846
1723
|
static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
|
|
1847
1724
|
{
|
|
1725
|
+
rb_check_frozen(self);
|
|
1848
1726
|
GET_STATE(self);
|
|
1849
1727
|
state->ascii_only = RTEST(enable);
|
|
1850
1728
|
return Qnil;
|
|
1851
1729
|
}
|
|
1852
1730
|
|
|
1731
|
+
static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc)
|
|
1732
|
+
{
|
|
1733
|
+
if (!rb_obj_is_proc(proc)) {
|
|
1734
|
+
rb_raise(rb_eTypeError, "sort_key_proc must be a Proc");
|
|
1735
|
+
}
|
|
1736
|
+
return default_sort_keys_proc = proc;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
static VALUE normalize_sort_keys(VALUE value)
|
|
1740
|
+
{
|
|
1741
|
+
if (rb_obj_is_proc(value)) {
|
|
1742
|
+
return value;
|
|
1743
|
+
} else if (value == Qtrue) {
|
|
1744
|
+
return default_sort_keys_proc;
|
|
1745
|
+
} else if (RTEST(value)) {
|
|
1746
|
+
rb_raise(rb_eTypeError, "The `sort_keys` argument must be a boolean or a Proc");
|
|
1747
|
+
} else {
|
|
1748
|
+
return Qfalse;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/*
|
|
1753
|
+
* call-seq: sort_keys
|
|
1754
|
+
*
|
|
1755
|
+
* Get the value of sort_keys.
|
|
1756
|
+
*/
|
|
1757
|
+
static VALUE cState_sort_keys_p(VALUE self)
|
|
1758
|
+
{
|
|
1759
|
+
GET_STATE(self);
|
|
1760
|
+
return state->sort_keys;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/*
|
|
1764
|
+
* call-seq: sort_keys=(value)
|
|
1765
|
+
*
|
|
1766
|
+
* value is a boolean or a proc. If the value is the boolean true, object keys
|
|
1767
|
+
* will be sorted lexicographically in ascending order.
|
|
1768
|
+
*
|
|
1769
|
+
* If the value is a proc, it receives the entire Hash and must return a Hash
|
|
1770
|
+
* with its pairs in the desired order, allowing for arbitrary sorting.
|
|
1771
|
+
*/
|
|
1772
|
+
static VALUE cState_sort_keys_set(VALUE self, VALUE value)
|
|
1773
|
+
{
|
|
1774
|
+
rb_check_frozen(self);
|
|
1775
|
+
GET_STATE(self);
|
|
1776
|
+
RB_OBJ_WRITE(self, &state->sort_keys, normalize_sort_keys(value));
|
|
1777
|
+
return Qnil;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
static VALUE cState_allow_duplicate_key_p(VALUE self)
|
|
1781
|
+
{
|
|
1782
|
+
GET_STATE(self);
|
|
1783
|
+
return state->allow_duplicate_key ? Qtrue : Qfalse;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1853
1786
|
/*
|
|
1854
1787
|
* call-seq: depth
|
|
1855
1788
|
*
|
|
@@ -1869,8 +1802,9 @@ static VALUE cState_depth(VALUE self)
|
|
|
1869
1802
|
*/
|
|
1870
1803
|
static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
1871
1804
|
{
|
|
1805
|
+
rb_check_frozen(self);
|
|
1872
1806
|
GET_STATE(self);
|
|
1873
|
-
state->depth =
|
|
1807
|
+
state->depth = depth_config(depth);
|
|
1874
1808
|
return Qnil;
|
|
1875
1809
|
}
|
|
1876
1810
|
|
|
@@ -1902,6 +1836,7 @@ static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_
|
|
|
1902
1836
|
*/
|
|
1903
1837
|
static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
|
|
1904
1838
|
{
|
|
1839
|
+
rb_check_frozen(self);
|
|
1905
1840
|
GET_STATE(self);
|
|
1906
1841
|
buffer_initial_length_set(state, buffer_initial_length);
|
|
1907
1842
|
return Qnil;
|
|
@@ -1910,6 +1845,7 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l
|
|
|
1910
1845
|
struct configure_state_data {
|
|
1911
1846
|
JSON_Generator_State *state;
|
|
1912
1847
|
VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated
|
|
1848
|
+
VALUE unknown_keywords;
|
|
1913
1849
|
};
|
|
1914
1850
|
|
|
1915
1851
|
static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value)
|
|
@@ -1934,15 +1870,25 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1934
1870
|
else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
|
|
1935
1871
|
else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
|
|
1936
1872
|
else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
|
|
1937
|
-
else if (key == sym_depth) { state->depth =
|
|
1873
|
+
else if (key == sym_depth) { state->depth = depth_config(val); }
|
|
1938
1874
|
else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
|
|
1939
1875
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1940
|
-
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
1941
1876
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
|
1877
|
+
else if (key == sym_allow_duplicate_key) { state->allow_duplicate_key = RTEST(val); }
|
|
1942
1878
|
else if (key == sym_as_json) {
|
|
1943
1879
|
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
|
|
1880
|
+
state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
|
|
1944
1881
|
state_write_value(data, &state->as_json, proc);
|
|
1945
1882
|
}
|
|
1883
|
+
else if (key == sym_sort_keys) {
|
|
1884
|
+
state_write_value(data, &state->sort_keys, normalize_sort_keys(val));
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
if (!data->unknown_keywords) {
|
|
1888
|
+
data->unknown_keywords = rb_obj_hide(rb_ary_new());
|
|
1889
|
+
}
|
|
1890
|
+
rb_ary_push(data->unknown_keywords, key);
|
|
1891
|
+
}
|
|
1946
1892
|
return ST_CONTINUE;
|
|
1947
1893
|
}
|
|
1948
1894
|
|
|
@@ -1956,48 +1902,56 @@ static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE con
|
|
|
1956
1902
|
|
|
1957
1903
|
struct configure_state_data data = {
|
|
1958
1904
|
.state = state,
|
|
1959
|
-
.vstate = vstate
|
|
1905
|
+
.vstate = vstate,
|
|
1906
|
+
.unknown_keywords = Qfalse,
|
|
1960
1907
|
};
|
|
1961
1908
|
|
|
1962
1909
|
// We assume in most cases few keys are set so it's faster to go over
|
|
1963
1910
|
// the provided keys than to check all possible keys.
|
|
1964
1911
|
rb_hash_foreach(config, configure_state_i, (VALUE)&data);
|
|
1912
|
+
|
|
1913
|
+
raise_argument_error_on_unknown_keywords(data.unknown_keywords);
|
|
1965
1914
|
}
|
|
1966
1915
|
|
|
1967
1916
|
static VALUE cState_configure(VALUE self, VALUE opts)
|
|
1968
1917
|
{
|
|
1918
|
+
rb_check_frozen(self);
|
|
1969
1919
|
GET_STATE(self);
|
|
1970
1920
|
configure_state(state, self, opts);
|
|
1971
1921
|
return self;
|
|
1972
1922
|
}
|
|
1973
1923
|
|
|
1974
|
-
static VALUE
|
|
1924
|
+
static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
|
|
1975
1925
|
{
|
|
1976
1926
|
JSON_Generator_State state = {0};
|
|
1977
1927
|
state_init(&state);
|
|
1978
1928
|
configure_state(&state, Qfalse, opts);
|
|
1979
1929
|
|
|
1980
1930
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
|
1981
|
-
FBuffer buffer = {
|
|
1982
|
-
|
|
1983
|
-
};
|
|
1984
|
-
fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1931
|
+
FBuffer buffer = { 0 };
|
|
1932
|
+
fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
|
|
1985
1933
|
|
|
1986
1934
|
struct generate_json_data data = {
|
|
1987
1935
|
.buffer = &buffer,
|
|
1988
1936
|
.vstate = Qfalse,
|
|
1989
1937
|
.state = &state,
|
|
1938
|
+
.depth = state.depth,
|
|
1990
1939
|
.obj = obj,
|
|
1991
|
-
.func =
|
|
1940
|
+
.func = func,
|
|
1992
1941
|
};
|
|
1993
|
-
|
|
1942
|
+
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
1943
|
+
}
|
|
1994
1944
|
|
|
1995
|
-
|
|
1945
|
+
static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1946
|
+
{
|
|
1947
|
+
return cState_m_do_generate(klass, obj, opts, io, generate_json);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
1951
|
+
{
|
|
1952
|
+
return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
|
|
1996
1953
|
}
|
|
1997
1954
|
|
|
1998
|
-
/*
|
|
1999
|
-
*
|
|
2000
|
-
*/
|
|
2001
1955
|
void Init_generator(void)
|
|
2002
1956
|
{
|
|
2003
1957
|
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
@@ -2015,6 +1969,8 @@ void Init_generator(void)
|
|
|
2015
1969
|
VALUE mExt = rb_define_module_under(mJSON, "Ext");
|
|
2016
1970
|
VALUE mGenerator = rb_define_module_under(mExt, "Generator");
|
|
2017
1971
|
|
|
1972
|
+
rb_global_variable(&default_sort_keys_proc);
|
|
1973
|
+
|
|
2018
1974
|
rb_global_variable(&eGeneratorError);
|
|
2019
1975
|
eGeneratorError = rb_path2class("JSON::GeneratorError");
|
|
2020
1976
|
|
|
@@ -2024,6 +1980,8 @@ void Init_generator(void)
|
|
|
2024
1980
|
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
|
|
2025
1981
|
rb_define_alloc_func(cState, cState_s_allocate);
|
|
2026
1982
|
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
|
|
1983
|
+
rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1);
|
|
1984
|
+
|
|
2027
1985
|
rb_define_method(cState, "initialize", cState_initialize, -1);
|
|
2028
1986
|
rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
|
|
2029
1987
|
rb_define_private_method(cState, "_configure", cState_configure, 1);
|
|
@@ -2046,9 +2004,6 @@ void Init_generator(void)
|
|
|
2046
2004
|
rb_define_method(cState, "script_safe", cState_script_safe, 0);
|
|
2047
2005
|
rb_define_method(cState, "script_safe?", cState_script_safe, 0);
|
|
2048
2006
|
rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
|
|
2049
|
-
rb_define_alias(cState, "escape_slash", "script_safe");
|
|
2050
|
-
rb_define_alias(cState, "escape_slash?", "script_safe?");
|
|
2051
|
-
rb_define_alias(cState, "escape_slash=", "script_safe=");
|
|
2052
2007
|
rb_define_method(cState, "strict", cState_strict, 0);
|
|
2053
2008
|
rb_define_method(cState, "strict?", cState_strict, 0);
|
|
2054
2009
|
rb_define_method(cState, "strict=", cState_strict_set, 1);
|
|
@@ -2062,51 +2017,14 @@ void Init_generator(void)
|
|
|
2062
2017
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2063
2018
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2064
2019
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
2065
|
-
|
|
2066
|
-
|
|
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);
|
|
2020
|
+
rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
|
|
2021
|
+
rb_define_method(cState, "sort_keys", cState_sort_keys_p, 0);
|
|
2022
|
+
rb_define_method(cState, "sort_keys=", cState_sort_keys_set, 1);
|
|
2101
2023
|
|
|
2102
|
-
|
|
2103
|
-
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
|
2024
|
+
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2104
2025
|
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
|
|
2109
|
-
rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
|
|
2026
|
+
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
2027
|
+
rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
|
|
2110
2028
|
|
|
2111
2029
|
rb_global_variable(&Encoding_UTF_8);
|
|
2112
2030
|
Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
|
|
@@ -2114,10 +2032,6 @@ void Init_generator(void)
|
|
|
2114
2032
|
i_to_s = rb_intern("to_s");
|
|
2115
2033
|
i_to_json = rb_intern("to_json");
|
|
2116
2034
|
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
2035
|
i_encode = rb_intern("encode");
|
|
2122
2036
|
|
|
2123
2037
|
sym_indent = ID2SYM(rb_intern("indent"));
|
|
@@ -2131,9 +2045,10 @@ void Init_generator(void)
|
|
|
2131
2045
|
sym_depth = ID2SYM(rb_intern("depth"));
|
|
2132
2046
|
sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length"));
|
|
2133
2047
|
sym_script_safe = ID2SYM(rb_intern("script_safe"));
|
|
2134
|
-
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
|
2135
2048
|
sym_strict = ID2SYM(rb_intern("strict"));
|
|
2136
2049
|
sym_as_json = ID2SYM(rb_intern("as_json"));
|
|
2050
|
+
sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
|
|
2051
|
+
sym_sort_keys = ID2SYM(rb_intern("sort_keys"));
|
|
2137
2052
|
|
|
2138
2053
|
usascii_encindex = rb_usascii_encindex();
|
|
2139
2054
|
utf8_encindex = rb_utf8_encindex();
|
|
@@ -2141,22 +2056,5 @@ void Init_generator(void)
|
|
|
2141
2056
|
|
|
2142
2057
|
rb_require("json/ext/generator/state");
|
|
2143
2058
|
|
|
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
|
-
}
|
|
2059
|
+
simd_impl = find_simd_implementation();
|
|
2162
2060
|
}
|