json 2.18.0 → 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 +11 -1
- data/ext/json/ext/fbuffer/fbuffer.h +21 -19
- data/ext/json/ext/generator/generator.c +97 -314
- data/ext/json/ext/json.h +8 -0
- data/ext/json/ext/parser/parser.c +101 -100
- data/ext/json/ext/simd/simd.h +28 -11
- data/ext/json/ext/vendor/fpconv.c +1 -1
- data/lib/json/common.rb +41 -10
- data/lib/json/ext/generator/state.rb +1 -1
- data/lib/json/truffle_ruby/generator.rb +13 -1
- data/lib/json/version.rb +1 -1
- data/lib/json.rb +33 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2e0f63481c8ba1c4f76f44a86ac9f1814e82fb396125e41e95efddc8e259fe64
|
|
4
|
+
data.tar.gz: a070ae0776f2db0519ec672d3a32be7dee2be2ee10d0bc35c9dde6581ec8c4a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc49905c26e2173018856dba7c4ae10ef74f015233b607f977b42ceea5f1d9e511f9ad4a7c22d4aed66e4ef8c60ad23a506d8bb0e053c0155ea2f7eb40a82d06
|
|
7
|
+
data.tar.gz: 61c1367d7b91621a34c0fc9fc29802534f14d27c1b9c50ecfa09d68d100fcda840847393063ec4c477437261398c559b93f292d2594bdf1e597c4b5e33959322
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-03-06 (2.19.0)
|
|
6
|
+
|
|
7
|
+
* Fix `allow_blank` parsing option to no longer allow invalid types (e.g. `load([], allow_blank: true)` now raise a type error).
|
|
8
|
+
* Add `allow_invalid_escape` parsing option to ignore backslashes that aren't followed by one of the valid escape characters.
|
|
9
|
+
|
|
10
|
+
### 2026-02-03 (2.18.1)
|
|
11
|
+
|
|
12
|
+
* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
|
|
13
|
+
without first invoking a user defined `#to_json` method.
|
|
14
|
+
|
|
5
15
|
### 2025-12-11 (2.18.0)
|
|
6
16
|
|
|
7
17
|
* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
|
|
@@ -66,7 +76,7 @@
|
|
|
66
76
|
* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
|
|
67
77
|
* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
|
|
68
78
|
* Fix `JSON.unsafe_load` usage with proc
|
|
69
|
-
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
79
|
+
* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
|
|
70
80
|
* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
|
|
71
81
|
|
|
72
82
|
### 2025-07-28 (2.13.2)
|
|
@@ -11,11 +11,11 @@ enum fbuffer_type {
|
|
|
11
11
|
|
|
12
12
|
typedef struct FBufferStruct {
|
|
13
13
|
enum fbuffer_type type;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
size_t initial_length;
|
|
15
|
+
size_t len;
|
|
16
|
+
size_t capa;
|
|
17
17
|
#if JSON_DEBUG
|
|
18
|
-
|
|
18
|
+
size_t requested;
|
|
19
19
|
#endif
|
|
20
20
|
char *ptr;
|
|
21
21
|
VALUE io;
|
|
@@ -32,12 +32,12 @@ typedef struct FBufferStruct {
|
|
|
32
32
|
|
|
33
33
|
static void fbuffer_free(FBuffer *fb);
|
|
34
34
|
static void fbuffer_clear(FBuffer *fb);
|
|
35
|
-
static void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
35
|
+
static void fbuffer_append(FBuffer *fb, const char *newstr, size_t len);
|
|
36
36
|
static void fbuffer_append_long(FBuffer *fb, long number);
|
|
37
37
|
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
|
38
38
|
static VALUE fbuffer_finalize(FBuffer *fb);
|
|
39
39
|
|
|
40
|
-
static void fbuffer_stack_init(FBuffer *fb,
|
|
40
|
+
static void fbuffer_stack_init(FBuffer *fb, size_t initial_length, char *stack_buffer, size_t stack_buffer_size)
|
|
41
41
|
{
|
|
42
42
|
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
|
43
43
|
if (stack_buffer) {
|
|
@@ -50,7 +50,7 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
|
|
|
50
50
|
#endif
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
static inline void fbuffer_consumed(FBuffer *fb,
|
|
53
|
+
static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
|
|
54
54
|
{
|
|
55
55
|
#if JSON_DEBUG
|
|
56
56
|
if (consumed > fb->requested) {
|
|
@@ -79,7 +79,7 @@ static void fbuffer_flush(FBuffer *fb)
|
|
|
79
79
|
fbuffer_clear(fb);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
static void fbuffer_realloc(FBuffer *fb,
|
|
82
|
+
static void fbuffer_realloc(FBuffer *fb, size_t required)
|
|
83
83
|
{
|
|
84
84
|
if (required > fb->capa) {
|
|
85
85
|
if (fb->type == FBUFFER_STACK_ALLOCATED) {
|
|
@@ -94,7 +94,7 @@ static void fbuffer_realloc(FBuffer *fb, unsigned long required)
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
static void fbuffer_do_inc_capa(FBuffer *fb,
|
|
97
|
+
static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
|
|
98
98
|
{
|
|
99
99
|
if (RB_UNLIKELY(fb->io)) {
|
|
100
100
|
if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
|
|
@@ -108,7 +108,7 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
size_t required;
|
|
112
112
|
|
|
113
113
|
if (RB_UNLIKELY(!fb->ptr)) {
|
|
114
114
|
fb->ptr = ALLOC_N(char, fb->initial_length);
|
|
@@ -120,7 +120,7 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
120
120
|
fbuffer_realloc(fb, required);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
static inline void fbuffer_inc_capa(FBuffer *fb,
|
|
123
|
+
static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
|
|
124
124
|
{
|
|
125
125
|
#if JSON_DEBUG
|
|
126
126
|
fb->requested = requested;
|
|
@@ -131,13 +131,13 @@ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr,
|
|
134
|
+
static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
|
|
135
135
|
{
|
|
136
136
|
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
137
137
|
fbuffer_consumed(fb, len);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
static inline void fbuffer_append(FBuffer *fb, const char *newstr,
|
|
140
|
+
static inline void fbuffer_append(FBuffer *fb, const char *newstr, size_t len)
|
|
141
141
|
{
|
|
142
142
|
if (len > 0) {
|
|
143
143
|
fbuffer_inc_capa(fb, len);
|
|
@@ -161,23 +161,25 @@ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
|
|
161
161
|
|
|
162
162
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
|
163
163
|
{
|
|
164
|
-
const char *
|
|
165
|
-
|
|
164
|
+
const char *ptr;
|
|
165
|
+
size_t len;
|
|
166
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
166
167
|
|
|
167
|
-
fbuffer_append(fb,
|
|
168
|
+
fbuffer_append(fb, ptr, len);
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
|
|
171
172
|
{
|
|
172
|
-
const char *
|
|
173
|
-
|
|
173
|
+
const char *ptr;
|
|
174
|
+
size_t len;
|
|
175
|
+
RSTRING_GETMEM(str, ptr, len);
|
|
174
176
|
|
|
175
177
|
fbuffer_inc_capa(fb, repeat * len);
|
|
176
178
|
while (repeat) {
|
|
177
179
|
#if JSON_DEBUG
|
|
178
180
|
fb->requested = len;
|
|
179
181
|
#endif
|
|
180
|
-
fbuffer_append_reserved(fb,
|
|
182
|
+
fbuffer_append_reserved(fb, ptr, len);
|
|
181
183
|
repeat--;
|
|
182
184
|
}
|
|
183
185
|
}
|
|
@@ -63,6 +63,8 @@ struct generate_json_data {
|
|
|
63
63
|
long depth;
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
static SIMD_Implementation simd_impl;
|
|
67
|
+
|
|
66
68
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
67
69
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
|
68
70
|
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -72,9 +74,6 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
|
72
74
|
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
73
75
|
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
74
76
|
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
75
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
76
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
77
|
-
#endif
|
|
78
77
|
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
79
78
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
80
79
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
|
@@ -155,8 +154,6 @@ static const unsigned char escape_table_basic[256] = {
|
|
|
155
154
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
156
155
|
};
|
|
157
156
|
|
|
158
|
-
static unsigned char (*search_escape_basic_impl)(search_state *);
|
|
159
|
-
|
|
160
157
|
static inline unsigned char search_escape_basic(search_state *search)
|
|
161
158
|
{
|
|
162
159
|
while (search->ptr < search->end) {
|
|
@@ -212,11 +209,39 @@ ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
|
|
|
212
209
|
* Everything else (should be UTF-8) is just passed through and
|
|
213
210
|
* appended to the result.
|
|
214
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
|
+
|
|
215
222
|
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
216
223
|
{
|
|
217
|
-
|
|
224
|
+
#ifdef HAVE_SIMD
|
|
225
|
+
#if defined(HAVE_SIMD_NEON)
|
|
226
|
+
while (search_escape_basic_neon(search)) {
|
|
218
227
|
escape_UTF8_char_basic(search);
|
|
219
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)) {
|
|
242
|
+
escape_UTF8_char_basic(search);
|
|
243
|
+
}
|
|
244
|
+
#endif /* HAVE_SIMD */
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
@@ -260,6 +285,8 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
|
|
|
260
285
|
|
|
261
286
|
ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
|
|
262
287
|
{
|
|
288
|
+
RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
|
|
289
|
+
|
|
263
290
|
// Flush the buffer so everything up until the last 'len' characters are unflushed.
|
|
264
291
|
search_flush(search);
|
|
265
292
|
|
|
@@ -269,12 +296,18 @@ ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned
|
|
|
269
296
|
char *s = (buf->ptr + buf->len);
|
|
270
297
|
|
|
271
298
|
// Pad the buffer with dummy characters that won't need escaping.
|
|
272
|
-
// This seem
|
|
273
|
-
|
|
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);
|
|
274
302
|
|
|
275
303
|
// Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
|
|
276
304
|
// to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
|
|
277
|
-
|
|
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
|
+
}
|
|
278
311
|
|
|
279
312
|
return s;
|
|
280
313
|
}
|
|
@@ -667,233 +700,6 @@ static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned
|
|
|
667
700
|
}
|
|
668
701
|
}
|
|
669
702
|
|
|
670
|
-
/*
|
|
671
|
-
* Document-module: JSON::Ext::Generator
|
|
672
|
-
*
|
|
673
|
-
* This is the JSON generator implemented as a C extension. It can be
|
|
674
|
-
* configured to be used by setting
|
|
675
|
-
*
|
|
676
|
-
* JSON.generator = JSON::Ext::Generator
|
|
677
|
-
*
|
|
678
|
-
* with the method generator= in JSON.
|
|
679
|
-
*
|
|
680
|
-
*/
|
|
681
|
-
|
|
682
|
-
/* Explanation of the following: that's the only way to not pollute
|
|
683
|
-
* standard library's docs with GeneratorMethods::<ClassName> which
|
|
684
|
-
* are uninformative and take a large place in a list of classes
|
|
685
|
-
*/
|
|
686
|
-
|
|
687
|
-
/*
|
|
688
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods
|
|
689
|
-
* :nodoc:
|
|
690
|
-
*/
|
|
691
|
-
|
|
692
|
-
/*
|
|
693
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Array
|
|
694
|
-
* :nodoc:
|
|
695
|
-
*/
|
|
696
|
-
|
|
697
|
-
/*
|
|
698
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
|
|
699
|
-
* :nodoc:
|
|
700
|
-
*/
|
|
701
|
-
|
|
702
|
-
/*
|
|
703
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
|
|
704
|
-
* :nodoc:
|
|
705
|
-
*/
|
|
706
|
-
|
|
707
|
-
/*
|
|
708
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
|
|
709
|
-
* :nodoc:
|
|
710
|
-
*/
|
|
711
|
-
|
|
712
|
-
/*
|
|
713
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Float
|
|
714
|
-
* :nodoc:
|
|
715
|
-
*/
|
|
716
|
-
|
|
717
|
-
/*
|
|
718
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
|
|
719
|
-
* :nodoc:
|
|
720
|
-
*/
|
|
721
|
-
|
|
722
|
-
/*
|
|
723
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
|
|
724
|
-
* :nodoc:
|
|
725
|
-
*/
|
|
726
|
-
|
|
727
|
-
/*
|
|
728
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
|
|
729
|
-
* :nodoc:
|
|
730
|
-
*/
|
|
731
|
-
|
|
732
|
-
/*
|
|
733
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::Object
|
|
734
|
-
* :nodoc:
|
|
735
|
-
*/
|
|
736
|
-
|
|
737
|
-
/*
|
|
738
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::String
|
|
739
|
-
* :nodoc:
|
|
740
|
-
*/
|
|
741
|
-
|
|
742
|
-
/*
|
|
743
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
|
|
744
|
-
* :nodoc:
|
|
745
|
-
*/
|
|
746
|
-
|
|
747
|
-
/*
|
|
748
|
-
* Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
|
|
749
|
-
* :nodoc:
|
|
750
|
-
*/
|
|
751
|
-
|
|
752
|
-
/*
|
|
753
|
-
* call-seq: to_json(state = nil)
|
|
754
|
-
*
|
|
755
|
-
* Returns a JSON string containing a JSON object, that is generated from
|
|
756
|
-
* this Hash instance.
|
|
757
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
758
|
-
* produced JSON string output further.
|
|
759
|
-
*/
|
|
760
|
-
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
|
|
761
|
-
{
|
|
762
|
-
rb_check_arity(argc, 0, 1);
|
|
763
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
764
|
-
return cState_partial_generate(Vstate, self, generate_json_object, Qfalse);
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
/*
|
|
768
|
-
* call-seq: to_json(state = nil)
|
|
769
|
-
*
|
|
770
|
-
* Returns a JSON string containing a JSON array, that is generated from
|
|
771
|
-
* this Array instance.
|
|
772
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
773
|
-
* produced JSON string output further.
|
|
774
|
-
*/
|
|
775
|
-
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self)
|
|
776
|
-
{
|
|
777
|
-
rb_check_arity(argc, 0, 1);
|
|
778
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
779
|
-
return cState_partial_generate(Vstate, self, generate_json_array, Qfalse);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
783
|
-
/*
|
|
784
|
-
* call-seq: to_json(*)
|
|
785
|
-
*
|
|
786
|
-
* Returns a JSON string representation for this Integer number.
|
|
787
|
-
*/
|
|
788
|
-
static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
|
|
789
|
-
{
|
|
790
|
-
rb_check_arity(argc, 0, 1);
|
|
791
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
792
|
-
return cState_partial_generate(Vstate, self, generate_json_integer, Qfalse);
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
#else
|
|
796
|
-
/*
|
|
797
|
-
* call-seq: to_json(*)
|
|
798
|
-
*
|
|
799
|
-
* Returns a JSON string representation for this Integer number.
|
|
800
|
-
*/
|
|
801
|
-
static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
|
|
802
|
-
{
|
|
803
|
-
rb_check_arity(argc, 0, 1);
|
|
804
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
805
|
-
return cState_partial_generate(Vstate, self, generate_json_fixnum, Qfalse);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
/*
|
|
809
|
-
* call-seq: to_json(*)
|
|
810
|
-
*
|
|
811
|
-
* Returns a JSON string representation for this Integer number.
|
|
812
|
-
*/
|
|
813
|
-
static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
|
|
814
|
-
{
|
|
815
|
-
rb_check_arity(argc, 0, 1);
|
|
816
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
817
|
-
return cState_partial_generate(Vstate, self, generate_json_bignum, Qfalse);
|
|
818
|
-
}
|
|
819
|
-
#endif
|
|
820
|
-
|
|
821
|
-
/*
|
|
822
|
-
* call-seq: to_json(*)
|
|
823
|
-
*
|
|
824
|
-
* Returns a JSON string representation for this Float number.
|
|
825
|
-
*/
|
|
826
|
-
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
827
|
-
{
|
|
828
|
-
rb_check_arity(argc, 0, 1);
|
|
829
|
-
VALUE Vstate = cState_from_state_s(cState, argc == 1 ? argv[0] : Qnil);
|
|
830
|
-
return cState_partial_generate(Vstate, self, generate_json_float, Qfalse);
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/*
|
|
834
|
-
* call-seq: to_json(*)
|
|
835
|
-
*
|
|
836
|
-
* This string should be encoded with UTF-8 A call to this method
|
|
837
|
-
* returns a JSON string encoded with UTF16 big endian characters as
|
|
838
|
-
* \u????.
|
|
839
|
-
*/
|
|
840
|
-
static VALUE mString_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_string, Qfalse);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
/*
|
|
848
|
-
* call-seq: to_json(*)
|
|
849
|
-
*
|
|
850
|
-
* Returns a JSON string for true: 'true'.
|
|
851
|
-
*/
|
|
852
|
-
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
853
|
-
{
|
|
854
|
-
rb_check_arity(argc, 0, 1);
|
|
855
|
-
return rb_utf8_str_new("true", 4);
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
/*
|
|
859
|
-
* call-seq: to_json(*)
|
|
860
|
-
*
|
|
861
|
-
* Returns a JSON string for false: 'false'.
|
|
862
|
-
*/
|
|
863
|
-
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
864
|
-
{
|
|
865
|
-
rb_check_arity(argc, 0, 1);
|
|
866
|
-
return rb_utf8_str_new("false", 5);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
/*
|
|
870
|
-
* call-seq: to_json(*)
|
|
871
|
-
*
|
|
872
|
-
* Returns a JSON string for nil: 'null'.
|
|
873
|
-
*/
|
|
874
|
-
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
875
|
-
{
|
|
876
|
-
rb_check_arity(argc, 0, 1);
|
|
877
|
-
return rb_utf8_str_new("null", 4);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
/*
|
|
881
|
-
* call-seq: to_json(*)
|
|
882
|
-
*
|
|
883
|
-
* Converts this object to a string (calling #to_s), converts
|
|
884
|
-
* it to a JSON string, and returns the result. This is a fallback, if no
|
|
885
|
-
* special method #to_json was defined for some object.
|
|
886
|
-
*/
|
|
887
|
-
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
|
|
888
|
-
{
|
|
889
|
-
VALUE state;
|
|
890
|
-
VALUE string = rb_funcall(self, i_to_s, 0);
|
|
891
|
-
rb_scan_args(argc, argv, "01", &state);
|
|
892
|
-
Check_Type(string, T_STRING);
|
|
893
|
-
state = cState_from_state_s(cState, state);
|
|
894
|
-
return cState_partial_generate(state, string, generate_json_string, Qfalse);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
703
|
static void State_mark(void *ptr)
|
|
898
704
|
{
|
|
899
705
|
JSON_Generator_State *state = ptr;
|
|
@@ -1091,6 +897,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data
|
|
|
1091
897
|
search.matches_mask = 0;
|
|
1092
898
|
search.has_matches = false;
|
|
1093
899
|
search.chunk_base = NULL;
|
|
900
|
+
search.chunk_end = NULL;
|
|
1094
901
|
#endif /* HAVE_SIMD */
|
|
1095
902
|
|
|
1096
903
|
switch (rb_enc_str_coderange(obj)) {
|
|
@@ -1337,18 +1144,8 @@ static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *dat
|
|
|
1337
1144
|
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1338
1145
|
{
|
|
1339
1146
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1340
|
-
fbuffer_append_str(buffer, tmp);
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
1344
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1345
|
-
{
|
|
1346
|
-
if (FIXNUM_P(obj))
|
|
1347
|
-
generate_json_fixnum(buffer, data, obj);
|
|
1348
|
-
else
|
|
1349
|
-
generate_json_bignum(buffer, data, obj);
|
|
1147
|
+
fbuffer_append_str(buffer, StringValue(tmp));
|
|
1350
1148
|
}
|
|
1351
|
-
#endif
|
|
1352
1149
|
|
|
1353
1150
|
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1354
1151
|
{
|
|
@@ -1393,7 +1190,7 @@ static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *d
|
|
|
1393
1190
|
fbuffer_append_str(buffer, fragment);
|
|
1394
1191
|
}
|
|
1395
1192
|
|
|
1396
|
-
static void
|
|
1193
|
+
static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
|
|
1397
1194
|
{
|
|
1398
1195
|
bool as_json_called = false;
|
|
1399
1196
|
start:
|
|
@@ -1420,15 +1217,15 @@ start:
|
|
|
1420
1217
|
generate_json_bignum(buffer, data, obj);
|
|
1421
1218
|
break;
|
|
1422
1219
|
case T_HASH:
|
|
1423
|
-
if (klass != rb_cHash) goto general;
|
|
1220
|
+
if (fallback && klass != rb_cHash) goto general;
|
|
1424
1221
|
generate_json_object(buffer, data, obj);
|
|
1425
1222
|
break;
|
|
1426
1223
|
case T_ARRAY:
|
|
1427
|
-
if (klass != rb_cArray) goto general;
|
|
1224
|
+
if (fallback && klass != rb_cArray) goto general;
|
|
1428
1225
|
generate_json_array(buffer, data, obj);
|
|
1429
1226
|
break;
|
|
1430
1227
|
case T_STRING:
|
|
1431
|
-
if (klass != rb_cString) goto general;
|
|
1228
|
+
if (fallback && klass != rb_cString) goto general;
|
|
1432
1229
|
|
|
1433
1230
|
if (RB_LIKELY(valid_json_string_p(obj))) {
|
|
1434
1231
|
raw_generate_json_string(buffer, data, obj);
|
|
@@ -1444,7 +1241,7 @@ start:
|
|
|
1444
1241
|
generate_json_symbol(buffer, data, obj);
|
|
1445
1242
|
break;
|
|
1446
1243
|
case T_FLOAT:
|
|
1447
|
-
if (klass != rb_cFloat) goto general;
|
|
1244
|
+
if (fallback && klass != rb_cFloat) goto general;
|
|
1448
1245
|
generate_json_float(buffer, data, obj);
|
|
1449
1246
|
break;
|
|
1450
1247
|
case T_STRUCT:
|
|
@@ -1468,6 +1265,16 @@ start:
|
|
|
1468
1265
|
}
|
|
1469
1266
|
}
|
|
1470
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
|
+
|
|
1471
1278
|
static VALUE generate_json_try(VALUE d)
|
|
1472
1279
|
{
|
|
1473
1280
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
@@ -1485,7 +1292,7 @@ static VALUE generate_json_ensure(VALUE d)
|
|
|
1485
1292
|
return Qundef;
|
|
1486
1293
|
}
|
|
1487
1294
|
|
|
1488
|
-
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)
|
|
1489
1296
|
{
|
|
1490
1297
|
GET_STATE(self);
|
|
1491
1298
|
|
|
@@ -1522,6 +1329,15 @@ static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
|
1522
1329
|
return cState_partial_generate(self, obj, generate_json, io);
|
|
1523
1330
|
}
|
|
1524
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);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1525
1341
|
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
1526
1342
|
{
|
|
1527
1343
|
rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
|
|
@@ -1759,6 +1575,17 @@ static long long_config(VALUE num)
|
|
|
1759
1575
|
return RTEST(num) ? FIX2LONG(num) : 0;
|
|
1760
1576
|
}
|
|
1761
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
|
+
|
|
1762
1589
|
/*
|
|
1763
1590
|
* call-seq: max_nesting=(depth)
|
|
1764
1591
|
*
|
|
@@ -1915,7 +1742,7 @@ static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
|
1915
1742
|
{
|
|
1916
1743
|
rb_check_frozen(self);
|
|
1917
1744
|
GET_STATE(self);
|
|
1918
|
-
state->depth =
|
|
1745
|
+
state->depth = depth_config(depth);
|
|
1919
1746
|
return Qnil;
|
|
1920
1747
|
}
|
|
1921
1748
|
|
|
@@ -1980,7 +1807,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1980
1807
|
else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
|
|
1981
1808
|
else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
|
|
1982
1809
|
else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
|
|
1983
|
-
else if (key == sym_depth) { state->depth =
|
|
1810
|
+
else if (key == sym_depth) { state->depth = depth_config(val); }
|
|
1984
1811
|
else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
|
|
1985
1812
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1986
1813
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
@@ -2020,7 +1847,7 @@ static VALUE cState_configure(VALUE self, VALUE opts)
|
|
|
2020
1847
|
return self;
|
|
2021
1848
|
}
|
|
2022
1849
|
|
|
2023
|
-
static VALUE
|
|
1850
|
+
static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
|
|
2024
1851
|
{
|
|
2025
1852
|
JSON_Generator_State state = {0};
|
|
2026
1853
|
state_init(&state);
|
|
@@ -2038,14 +1865,21 @@ static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
|
|
|
2038
1865
|
.state = &state,
|
|
2039
1866
|
.depth = state.depth,
|
|
2040
1867
|
.obj = obj,
|
|
2041
|
-
.func =
|
|
1868
|
+
.func = func,
|
|
2042
1869
|
};
|
|
2043
1870
|
return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
|
|
2044
1871
|
}
|
|
2045
1872
|
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
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);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
2049
1883
|
void Init_generator(void)
|
|
2050
1884
|
{
|
|
2051
1885
|
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
@@ -2110,46 +1944,12 @@ void Init_generator(void)
|
|
|
2110
1944
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
2111
1945
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
2112
1946
|
rb_define_method(cState, "generate", cState_generate, -1);
|
|
1947
|
+
rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
|
|
2113
1948
|
|
|
2114
1949
|
rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
|
|
2115
1950
|
|
|
2116
1951
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
2117
|
-
|
|
2118
|
-
VALUE mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
|
|
2119
|
-
|
|
2120
|
-
VALUE mObject = rb_define_module_under(mGeneratorMethods, "Object");
|
|
2121
|
-
rb_define_method(mObject, "to_json", mObject_to_json, -1);
|
|
2122
|
-
|
|
2123
|
-
VALUE mHash = rb_define_module_under(mGeneratorMethods, "Hash");
|
|
2124
|
-
rb_define_method(mHash, "to_json", mHash_to_json, -1);
|
|
2125
|
-
|
|
2126
|
-
VALUE mArray = rb_define_module_under(mGeneratorMethods, "Array");
|
|
2127
|
-
rb_define_method(mArray, "to_json", mArray_to_json, -1);
|
|
2128
|
-
|
|
2129
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
|
2130
|
-
VALUE mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
|
|
2131
|
-
rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
|
|
2132
|
-
#else
|
|
2133
|
-
VALUE mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
|
|
2134
|
-
rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
|
|
2135
|
-
|
|
2136
|
-
VALUE mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
|
|
2137
|
-
rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
|
|
2138
|
-
#endif
|
|
2139
|
-
VALUE mFloat = rb_define_module_under(mGeneratorMethods, "Float");
|
|
2140
|
-
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
|
2141
|
-
|
|
2142
|
-
VALUE mString = rb_define_module_under(mGeneratorMethods, "String");
|
|
2143
|
-
rb_define_method(mString, "to_json", mString_to_json, -1);
|
|
2144
|
-
|
|
2145
|
-
VALUE mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
|
|
2146
|
-
rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
|
|
2147
|
-
|
|
2148
|
-
VALUE mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
|
|
2149
|
-
rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
|
|
2150
|
-
|
|
2151
|
-
VALUE mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
|
|
2152
|
-
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);
|
|
2153
1953
|
|
|
2154
1954
|
rb_global_variable(&Encoding_UTF_8);
|
|
2155
1955
|
Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
|
|
@@ -2181,22 +1981,5 @@ void Init_generator(void)
|
|
|
2181
1981
|
|
|
2182
1982
|
rb_require("json/ext/generator/state");
|
|
2183
1983
|
|
|
2184
|
-
|
|
2185
|
-
switch (find_simd_implementation()) {
|
|
2186
|
-
#ifdef HAVE_SIMD
|
|
2187
|
-
#ifdef HAVE_SIMD_NEON
|
|
2188
|
-
case SIMD_NEON:
|
|
2189
|
-
search_escape_basic_impl = search_escape_basic_neon;
|
|
2190
|
-
break;
|
|
2191
|
-
#endif /* HAVE_SIMD_NEON */
|
|
2192
|
-
#ifdef HAVE_SIMD_SSE2
|
|
2193
|
-
case SIMD_SSE2:
|
|
2194
|
-
search_escape_basic_impl = search_escape_basic_sse2;
|
|
2195
|
-
break;
|
|
2196
|
-
#endif /* HAVE_SIMD_SSE2 */
|
|
2197
|
-
#endif /* HAVE_SIMD */
|
|
2198
|
-
default:
|
|
2199
|
-
search_escape_basic_impl = search_escape_basic;
|
|
2200
|
-
break;
|
|
2201
|
-
}
|
|
1984
|
+
simd_impl = find_simd_implementation();
|
|
2202
1985
|
}
|
data/ext/json/ext/json.h
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
#include "ruby/encoding.h"
|
|
6
6
|
#include <stdint.h>
|
|
7
7
|
|
|
8
|
+
#ifndef RBIMPL_ASSERT_OR_ASSUME
|
|
9
|
+
# define RBIMPL_ASSERT_OR_ASSUME(x)
|
|
10
|
+
#endif
|
|
11
|
+
|
|
8
12
|
#if defined(RUBY_DEBUG) && RUBY_DEBUG
|
|
9
13
|
# define JSON_ASSERT RUBY_ASSERT
|
|
10
14
|
#else
|
|
@@ -51,8 +55,12 @@ typedef unsigned char _Bool;
|
|
|
51
55
|
#endif
|
|
52
56
|
|
|
53
57
|
#ifndef NORETURN
|
|
58
|
+
#if defined(__has_attribute) && __has_attribute(noreturn)
|
|
59
|
+
#define NORETURN(x) __attribute__((noreturn)) x
|
|
60
|
+
#else
|
|
54
61
|
#define NORETURN(x) x
|
|
55
62
|
#endif
|
|
63
|
+
#endif
|
|
56
64
|
|
|
57
65
|
#ifndef NOINLINE
|
|
58
66
|
#if defined(__has_attribute) && __has_attribute(noinline)
|
|
@@ -7,8 +7,9 @@ static VALUE CNaN, CInfinity, CMinusInfinity;
|
|
|
7
7
|
|
|
8
8
|
static ID i_new, i_try_convert, i_uminus, i_encode;
|
|
9
9
|
|
|
10
|
-
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters,
|
|
11
|
-
sym_decimal_class, sym_on_load,
|
|
10
|
+
static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters,
|
|
11
|
+
sym_allow_invalid_escape, sym_symbolize_names, sym_freeze, sym_decimal_class, sym_on_load,
|
|
12
|
+
sym_allow_duplicate_key;
|
|
12
13
|
|
|
13
14
|
static int binary_encindex;
|
|
14
15
|
static int utf8_encindex;
|
|
@@ -336,6 +337,7 @@ typedef struct JSON_ParserStruct {
|
|
|
336
337
|
bool allow_nan;
|
|
337
338
|
bool allow_trailing_comma;
|
|
338
339
|
bool allow_control_characters;
|
|
340
|
+
bool allow_invalid_escape;
|
|
339
341
|
bool symbolize_names;
|
|
340
342
|
bool freeze;
|
|
341
343
|
} JSON_ParserConfig;
|
|
@@ -400,10 +402,7 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
|
|
|
400
402
|
|
|
401
403
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
|
402
404
|
|
|
403
|
-
|
|
404
|
-
RBIMPL_ATTR_NORETURN()
|
|
405
|
-
#endif
|
|
406
|
-
static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
405
|
+
NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
407
406
|
{
|
|
408
407
|
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
|
|
409
408
|
long line, column;
|
|
@@ -449,10 +448,7 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
|
449
448
|
rb_exc_raise(exc);
|
|
450
449
|
}
|
|
451
450
|
|
|
452
|
-
|
|
453
|
-
RBIMPL_ATTR_NORETURN()
|
|
454
|
-
#endif
|
|
455
|
-
static void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at)
|
|
451
|
+
NORETURN(static) void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at)
|
|
456
452
|
{
|
|
457
453
|
state->cursor = at;
|
|
458
454
|
raise_parse_error(format, state);
|
|
@@ -477,23 +473,24 @@ static const signed char digit_values[256] = {
|
|
|
477
473
|
-1, -1, -1, -1, -1, -1, -1
|
|
478
474
|
};
|
|
479
475
|
|
|
480
|
-
static uint32_t unescape_unicode(JSON_ParserState *state, const
|
|
481
|
-
{
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
476
|
+
static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe)
|
|
477
|
+
{
|
|
478
|
+
if (RB_UNLIKELY(sp > spe - 4)) {
|
|
479
|
+
raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const unsigned char *p = (const unsigned char *)sp;
|
|
483
|
+
|
|
484
|
+
const signed char b0 = digit_values[p[0]];
|
|
485
|
+
const signed char b1 = digit_values[p[1]];
|
|
486
|
+
const signed char b2 = digit_values[p[2]];
|
|
487
|
+
const signed char b3 = digit_values[p[3]];
|
|
488
|
+
|
|
489
|
+
if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) {
|
|
490
|
+
raise_parse_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3;
|
|
497
494
|
}
|
|
498
495
|
|
|
499
496
|
#define GET_PARSER_CONFIG \
|
|
@@ -643,7 +640,7 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserCon
|
|
|
643
640
|
typedef struct _json_unescape_positions {
|
|
644
641
|
long size;
|
|
645
642
|
const char **positions;
|
|
646
|
-
|
|
643
|
+
unsigned long additional_backslashes;
|
|
647
644
|
} JSON_UnescapePositions;
|
|
648
645
|
|
|
649
646
|
static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
|
|
@@ -657,7 +654,8 @@ static inline const char *json_next_backslash(const char *pe, const char *string
|
|
|
657
654
|
}
|
|
658
655
|
}
|
|
659
656
|
|
|
660
|
-
if (positions->
|
|
657
|
+
if (positions->additional_backslashes) {
|
|
658
|
+
positions->additional_backslashes--;
|
|
661
659
|
return memchr(pe, '\\', stringEnd - pe);
|
|
662
660
|
}
|
|
663
661
|
|
|
@@ -707,50 +705,41 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
707
705
|
case 'f':
|
|
708
706
|
APPEND_CHAR('\f');
|
|
709
707
|
break;
|
|
710
|
-
case 'u':
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (
|
|
729
|
-
raise_parse_error_at("
|
|
730
|
-
}
|
|
731
|
-
if (pe[0] == '\\' && pe[1] == 'u') {
|
|
732
|
-
uint32_t sur = unescape_unicode(state, (unsigned char *) pe + 2);
|
|
733
|
-
|
|
734
|
-
if ((sur & 0xFC00) != 0xDC00) {
|
|
735
|
-
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16)
|
|
739
|
-
| (sur & 0x3FF));
|
|
740
|
-
pe += 5;
|
|
741
|
-
} else {
|
|
742
|
-
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
743
|
-
break;
|
|
708
|
+
case 'u': {
|
|
709
|
+
uint32_t ch = unescape_unicode(state, ++pe, stringEnd);
|
|
710
|
+
pe += 3;
|
|
711
|
+
/* To handle values above U+FFFF, we take a sequence of
|
|
712
|
+
* \uXXXX escapes in the U+D800..U+DBFF then
|
|
713
|
+
* U+DC00..U+DFFF ranges, take the low 10 bits from each
|
|
714
|
+
* to make a 20-bit number, then add 0x10000 to get the
|
|
715
|
+
* final codepoint.
|
|
716
|
+
*
|
|
717
|
+
* See Unicode 15: 3.8 "Surrogates", 5.3 "Handling
|
|
718
|
+
* Surrogate Pairs in UTF-16", and 23.6 "Surrogates
|
|
719
|
+
* Area".
|
|
720
|
+
*/
|
|
721
|
+
if ((ch & 0xFC00) == 0xD800) {
|
|
722
|
+
pe++;
|
|
723
|
+
if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) {
|
|
724
|
+
uint32_t sur = unescape_unicode(state, pe + 2, stringEnd);
|
|
725
|
+
|
|
726
|
+
if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) {
|
|
727
|
+
raise_parse_error_at("invalid surrogate pair at %s", state, p);
|
|
744
728
|
}
|
|
745
|
-
}
|
|
746
729
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
730
|
+
ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF));
|
|
731
|
+
pe += 5;
|
|
732
|
+
} else {
|
|
733
|
+
raise_parse_error_at("incomplete surrogate pair at %s", state, p);
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
752
736
|
}
|
|
737
|
+
|
|
738
|
+
int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
|
|
739
|
+
buffer += unescape_len;
|
|
740
|
+
p = ++pe;
|
|
753
741
|
break;
|
|
742
|
+
}
|
|
754
743
|
default:
|
|
755
744
|
if ((unsigned char)*pe < 0x20) {
|
|
756
745
|
if (!config->allow_control_characters) {
|
|
@@ -759,6 +748,8 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
759
748
|
}
|
|
760
749
|
raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
|
|
761
750
|
}
|
|
751
|
+
} else if (config->allow_invalid_escape) {
|
|
752
|
+
APPEND_CHAR(*pe);
|
|
762
753
|
} else {
|
|
763
754
|
raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
|
|
764
755
|
}
|
|
@@ -783,20 +774,39 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
|
|
|
783
774
|
}
|
|
784
775
|
|
|
785
776
|
#define MAX_FAST_INTEGER_SIZE 18
|
|
777
|
+
#define MAX_NUMBER_STACK_BUFFER 128
|
|
786
778
|
|
|
787
|
-
|
|
779
|
+
typedef VALUE (*json_number_decode_func_t)(const char *ptr);
|
|
780
|
+
|
|
781
|
+
static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func)
|
|
788
782
|
{
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
783
|
+
if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) {
|
|
784
|
+
char buffer[MAX_NUMBER_STACK_BUFFER];
|
|
785
|
+
MEMCPY(buffer, start, char, len);
|
|
786
|
+
buffer[len] = '\0';
|
|
787
|
+
return func(buffer);
|
|
788
|
+
} else {
|
|
789
|
+
VALUE buffer_v = rb_str_tmp_new(len);
|
|
790
|
+
char *buffer = RSTRING_PTR(buffer_v);
|
|
791
|
+
MEMCPY(buffer, start, char, len);
|
|
792
|
+
buffer[len] = '\0';
|
|
793
|
+
VALUE number = func(buffer);
|
|
794
|
+
RB_GC_GUARD(buffer_v);
|
|
795
|
+
return number;
|
|
796
|
+
}
|
|
796
797
|
}
|
|
797
798
|
|
|
798
|
-
static
|
|
799
|
-
|
|
799
|
+
static VALUE json_decode_inum(const char *buffer)
|
|
800
|
+
{
|
|
801
|
+
return rb_cstr2inum(buffer, 10);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
|
|
805
|
+
{
|
|
806
|
+
return json_decode_large_number(start, len, json_decode_inum);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
|
|
800
810
|
{
|
|
801
811
|
if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) {
|
|
802
812
|
if (negative) {
|
|
@@ -808,22 +818,14 @@ json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const
|
|
|
808
818
|
return json_decode_large_integer(start, end - start);
|
|
809
819
|
}
|
|
810
820
|
|
|
811
|
-
static VALUE
|
|
821
|
+
static VALUE json_decode_dnum(const char *buffer)
|
|
812
822
|
{
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
MEMCPY(buffer, start, char, len);
|
|
816
|
-
buffer[len] = '\0';
|
|
817
|
-
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
818
|
-
}
|
|
823
|
+
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
824
|
+
}
|
|
819
825
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
buffer[len] = '\0';
|
|
824
|
-
VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
|
825
|
-
RB_ALLOCV_END(buffer_v);
|
|
826
|
-
return number;
|
|
826
|
+
NOINLINE(static) VALUE json_decode_large_float(const char *start, long len)
|
|
827
|
+
{
|
|
828
|
+
return json_decode_large_number(start, len, json_decode_dnum);
|
|
827
829
|
}
|
|
828
830
|
|
|
829
831
|
/* Ruby JSON optimized float decoder using vendored Ryu algorithm
|
|
@@ -875,7 +877,7 @@ static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
|
|
|
875
877
|
return Qfalse;
|
|
876
878
|
}
|
|
877
879
|
|
|
878
|
-
static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
|
880
|
+
NOINLINE(static) void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
|
|
879
881
|
{
|
|
880
882
|
VALUE message = rb_sprintf(
|
|
881
883
|
"detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
|
|
@@ -886,10 +888,7 @@ static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_
|
|
|
886
888
|
RB_GC_GUARD(message);
|
|
887
889
|
}
|
|
888
890
|
|
|
889
|
-
|
|
890
|
-
RBIMPL_ATTR_NORETURN()
|
|
891
|
-
#endif
|
|
892
|
-
static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
|
891
|
+
NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
|
|
893
892
|
{
|
|
894
893
|
VALUE message = rb_sprintf(
|
|
895
894
|
"duplicate key %"PRIsVALUE,
|
|
@@ -992,7 +991,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
|
|
|
992
991
|
JSON_UnescapePositions positions = {
|
|
993
992
|
.size = 0,
|
|
994
993
|
.positions = backslashes,
|
|
995
|
-
.
|
|
994
|
+
.additional_backslashes = 0,
|
|
996
995
|
};
|
|
997
996
|
|
|
998
997
|
do {
|
|
@@ -1007,7 +1006,7 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
|
|
|
1007
1006
|
backslashes[positions.size] = state->cursor;
|
|
1008
1007
|
positions.size++;
|
|
1009
1008
|
} else {
|
|
1010
|
-
positions.
|
|
1009
|
+
positions.additional_backslashes++;
|
|
1011
1010
|
}
|
|
1012
1011
|
state->cursor++;
|
|
1013
1012
|
break;
|
|
@@ -1440,6 +1439,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
|
|
|
1440
1439
|
else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
|
|
1441
1440
|
else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
|
|
1442
1441
|
else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
|
|
1442
|
+
else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
|
|
1443
1443
|
else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
|
|
1444
1444
|
else if (key == sym_freeze) { config->freeze = RTEST(val); }
|
|
1445
1445
|
else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
|
|
@@ -1658,6 +1658,7 @@ void Init_parser(void)
|
|
|
1658
1658
|
sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
|
|
1659
1659
|
sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
|
|
1660
1660
|
sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
|
|
1661
|
+
sym_allow_invalid_escape = ID2SYM(rb_intern("allow_invalid_escape"));
|
|
1661
1662
|
sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
|
1662
1663
|
sym_freeze = ID2SYM(rb_intern("freeze"));
|
|
1663
1664
|
sym_on_load = ID2SYM(rb_intern("on_load"));
|
data/ext/json/ext/simd/simd.h
CHANGED
|
@@ -58,7 +58,34 @@ static inline int trailing_zeros(int input)
|
|
|
58
58
|
|
|
59
59
|
#ifdef JSON_ENABLE_SIMD
|
|
60
60
|
|
|
61
|
-
#define SIMD_MINIMUM_THRESHOLD
|
|
61
|
+
#define SIMD_MINIMUM_THRESHOLD 4
|
|
62
|
+
|
|
63
|
+
ALWAYS_INLINE(static) void json_fast_memcpy16(char *dst, const char *src, size_t len)
|
|
64
|
+
{
|
|
65
|
+
RBIMPL_ASSERT_OR_ASSUME(len < 16);
|
|
66
|
+
RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD); // 4
|
|
67
|
+
#if defined(__has_builtin) && __has_builtin(__builtin_memcpy)
|
|
68
|
+
// If __builtin_memcpy is available, use it to copy between SIMD_MINIMUM_THRESHOLD (4) and vec_len-1 (15) bytes.
|
|
69
|
+
// These copies overlap. The first copy will copy the first 8 (or 4) bytes. The second copy will copy
|
|
70
|
+
// the last 8 (or 4) bytes but overlap with the first copy. The overlapping bytes will be in the correct
|
|
71
|
+
// position in both copies.
|
|
72
|
+
|
|
73
|
+
// Please do not attempt to replace __builtin_memcpy with memcpy without profiling and/or looking at the
|
|
74
|
+
// generated assembly. On clang-specifically (tested on Apple clang version 17.0.0 (clang-1700.0.13.3)),
|
|
75
|
+
// when using memcpy, the compiler will notice the only difference is a 4 or 8 and generate a conditional
|
|
76
|
+
// select instruction instead of direct loads and stores with a branch. This ends up slower than the branch
|
|
77
|
+
// plus two loads and stores generated when using __builtin_memcpy.
|
|
78
|
+
if (len >= 8) {
|
|
79
|
+
__builtin_memcpy(dst, src, 8);
|
|
80
|
+
__builtin_memcpy(dst + len - 8, src + len - 8, 8);
|
|
81
|
+
} else {
|
|
82
|
+
__builtin_memcpy(dst, src, 4);
|
|
83
|
+
__builtin_memcpy(dst + len - 4, src + len - 4, 4);
|
|
84
|
+
}
|
|
85
|
+
#else
|
|
86
|
+
MEMCPY(dst, src, char, len);
|
|
87
|
+
#endif
|
|
88
|
+
}
|
|
62
89
|
|
|
63
90
|
#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64)
|
|
64
91
|
#include <arm_neon.h>
|
|
@@ -106,16 +133,6 @@ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *en
|
|
|
106
133
|
return 0;
|
|
107
134
|
}
|
|
108
135
|
|
|
109
|
-
static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table)
|
|
110
|
-
{
|
|
111
|
-
uint8x16x4_t tab;
|
|
112
|
-
tab.val[0] = vld1q_u8(table);
|
|
113
|
-
tab.val[1] = vld1q_u8(table+16);
|
|
114
|
-
tab.val[2] = vld1q_u8(table+32);
|
|
115
|
-
tab.val[3] = vld1q_u8(table+48);
|
|
116
|
-
return tab;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
136
|
#endif /* ARM Neon Support.*/
|
|
120
137
|
|
|
121
138
|
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
data/lib/json/common.rb
CHANGED
|
@@ -156,15 +156,17 @@ module JSON
|
|
|
156
156
|
def generator=(generator) # :nodoc:
|
|
157
157
|
old, $VERBOSE = $VERBOSE, nil
|
|
158
158
|
@generator = generator
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
159
|
+
if generator.const_defined?(:GeneratorMethods)
|
|
160
|
+
generator_methods = generator::GeneratorMethods
|
|
161
|
+
for const in generator_methods.constants
|
|
162
|
+
klass = const_get(const)
|
|
163
|
+
modul = generator_methods.const_get(const)
|
|
164
|
+
klass.class_eval do
|
|
165
|
+
instance_methods(false).each do |m|
|
|
166
|
+
m.to_s == 'to_json' and remove_method m
|
|
167
|
+
end
|
|
168
|
+
include modul
|
|
166
169
|
end
|
|
167
|
-
include modul
|
|
168
170
|
end
|
|
169
171
|
end
|
|
170
172
|
self.state = generator::State
|
|
@@ -878,7 +880,7 @@ module JSON
|
|
|
878
880
|
end
|
|
879
881
|
end
|
|
880
882
|
|
|
881
|
-
if opts[:allow_blank] && (source.nil? || source.empty?)
|
|
883
|
+
if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
|
|
882
884
|
source = 'null'
|
|
883
885
|
end
|
|
884
886
|
|
|
@@ -1036,7 +1038,8 @@ module JSON
|
|
|
1036
1038
|
# JSON.new(options = nil, &block)
|
|
1037
1039
|
#
|
|
1038
1040
|
# Argument +options+, if given, contains a \Hash of options for both parsing and generating.
|
|
1039
|
-
# See {Parsing Options}[
|
|
1041
|
+
# See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
|
|
1042
|
+
# and {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
1040
1043
|
#
|
|
1041
1044
|
# For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
|
|
1042
1045
|
# encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
|
|
@@ -1095,6 +1098,30 @@ module JSON
|
|
|
1095
1098
|
load(File.read(path, encoding: Encoding::UTF_8))
|
|
1096
1099
|
end
|
|
1097
1100
|
end
|
|
1101
|
+
|
|
1102
|
+
module GeneratorMethods
|
|
1103
|
+
# call-seq: to_json(*)
|
|
1104
|
+
#
|
|
1105
|
+
# Converts this object into a JSON string.
|
|
1106
|
+
# If this object doesn't directly maps to a JSON native type,
|
|
1107
|
+
# first convert it to a string (calling #to_s), then converts
|
|
1108
|
+
# it to a JSON string, and returns the result.
|
|
1109
|
+
# This is a fallback, if no special method #to_json was defined for some object.
|
|
1110
|
+
def to_json(state = nil, *)
|
|
1111
|
+
obj = case self
|
|
1112
|
+
when nil, false, true, Integer, Float, Array, Hash
|
|
1113
|
+
self
|
|
1114
|
+
else
|
|
1115
|
+
"#{self}"
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
if state.nil?
|
|
1119
|
+
JSON::State._generate_no_fallback(obj, nil, nil)
|
|
1120
|
+
else
|
|
1121
|
+
JSON::State.from_state(state)._generate_no_fallback(obj)
|
|
1122
|
+
end
|
|
1123
|
+
end
|
|
1124
|
+
end
|
|
1098
1125
|
end
|
|
1099
1126
|
|
|
1100
1127
|
module ::Kernel
|
|
@@ -1140,3 +1167,7 @@ module ::Kernel
|
|
|
1140
1167
|
JSON[object, opts]
|
|
1141
1168
|
end
|
|
1142
1169
|
end
|
|
1170
|
+
|
|
1171
|
+
class Object
|
|
1172
|
+
include JSON::GeneratorMethods
|
|
1173
|
+
end
|
|
@@ -9,7 +9,7 @@ module JSON
|
|
|
9
9
|
# Instantiates a new State object, configured by _opts_.
|
|
10
10
|
#
|
|
11
11
|
# Argument +opts+, if given, contains a \Hash of options for the generation.
|
|
12
|
-
# See {Generating Options}[
|
|
12
|
+
# See {Generating Options}[rdoc-ref:JSON@Generating+Options].
|
|
13
13
|
def initialize(opts = nil)
|
|
14
14
|
if opts && !opts.empty?
|
|
15
15
|
configure(opts)
|
|
@@ -211,7 +211,14 @@ module JSON
|
|
|
211
211
|
|
|
212
212
|
# This integer returns the current depth data structure nesting in the
|
|
213
213
|
# generated JSON.
|
|
214
|
-
|
|
214
|
+
attr_reader :depth
|
|
215
|
+
|
|
216
|
+
def depth=(depth)
|
|
217
|
+
if depth.negative?
|
|
218
|
+
raise ArgumentError, "depth must be >= 0 (got #{depth})"
|
|
219
|
+
end
|
|
220
|
+
@depth = depth
|
|
221
|
+
end
|
|
215
222
|
|
|
216
223
|
def check_max_nesting # :nodoc:
|
|
217
224
|
return if @max_nesting.zero?
|
|
@@ -260,6 +267,11 @@ module JSON
|
|
|
260
267
|
else
|
|
261
268
|
raise TypeError, "can't convert #{opts.class} into Hash"
|
|
262
269
|
end
|
|
270
|
+
|
|
271
|
+
if opts[:depth]&.negative?
|
|
272
|
+
raise ArgumentError, "depth must be >= 0 (got #{opts[:depth]})"
|
|
273
|
+
end
|
|
274
|
+
|
|
263
275
|
opts.each do |key, value|
|
|
264
276
|
instance_variable_set "@#{key}", value
|
|
265
277
|
end
|
data/lib/json/version.rb
CHANGED
data/lib/json.rb
CHANGED
|
@@ -6,6 +6,15 @@ require 'json/common'
|
|
|
6
6
|
#
|
|
7
7
|
# \JSON is a lightweight data-interchange format.
|
|
8
8
|
#
|
|
9
|
+
# \JSON is easy for us humans to read and write,
|
|
10
|
+
# and equally simple for machines to read (parse) and write (generate).
|
|
11
|
+
#
|
|
12
|
+
# \JSON is language-independent, making it an ideal interchange format
|
|
13
|
+
# for applications in differing programming languages
|
|
14
|
+
# and on differing operating systems.
|
|
15
|
+
#
|
|
16
|
+
# == \JSON Values
|
|
17
|
+
#
|
|
9
18
|
# A \JSON value is one of the following:
|
|
10
19
|
# - Double-quoted text: <tt>"foo"</tt>.
|
|
11
20
|
# - Number: +1+, +1.0+, +2.0e2+.
|
|
@@ -173,6 +182,30 @@ require 'json/common'
|
|
|
173
182
|
# When enabled:
|
|
174
183
|
# JSON.parse('[1,]', allow_trailing_comma: true) # => [1]
|
|
175
184
|
#
|
|
185
|
+
# ---
|
|
186
|
+
#
|
|
187
|
+
# Option +allow_control_characters+ (boolean) specifies whether to allow
|
|
188
|
+
# unescaped ASCII control characters, such as newlines, in strings;
|
|
189
|
+
# defaults to +false+.
|
|
190
|
+
#
|
|
191
|
+
# With the default, +false+:
|
|
192
|
+
# JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError)
|
|
193
|
+
#
|
|
194
|
+
# When enabled:
|
|
195
|
+
# JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld"
|
|
196
|
+
#
|
|
197
|
+
# ---
|
|
198
|
+
#
|
|
199
|
+
# Option +allow_invalid_escape+ (boolean) specifies whether to ignore backslahes that are followed
|
|
200
|
+
# by an invalid escape character in strings;
|
|
201
|
+
# defaults to +false+.
|
|
202
|
+
#
|
|
203
|
+
# With the default, +false+:
|
|
204
|
+
# JSON.parse('"Hell\o"') # invalid escape character in string (JSON::ParserError)
|
|
205
|
+
#
|
|
206
|
+
# When enabled:
|
|
207
|
+
# JSON.parse('"Hell\o"', allow_invalid_escape: true) # => "Hello"
|
|
208
|
+
#
|
|
176
209
|
# ====== Output Options
|
|
177
210
|
#
|
|
178
211
|
# Option +freeze+ (boolean) specifies whether the returned objects will be frozen;
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: json
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Frank
|
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: '0'
|
|
86
86
|
requirements: []
|
|
87
|
-
rubygems_version:
|
|
87
|
+
rubygems_version: 4.0.3
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: JSON Implementation for Ruby
|
|
90
90
|
test_files: []
|