json 2.10.2 → 2.11.3
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 +38 -0
- data/ext/json/ext/fbuffer/fbuffer.h +45 -14
- data/ext/json/ext/generator/generator.c +111 -92
- data/ext/json/ext/parser/extconf.rb +0 -1
- data/ext/json/ext/parser/parser.c +30 -151
- data/ext/json/ext/vendor/fpconv.c +479 -0
- data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
- data/lib/json/common.rb +274 -159
- data/lib/json/truffle_ruby/generator.rb +1 -1
- data/lib/json/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1919e2040a180b81eba1f475c511ace075b32015997b7d58098f93103941f8b2
|
4
|
+
data.tar.gz: d958784bea1136d935835d3e602fae96f97d25208cafec8a68db03619e6d34d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 742da3e909b2b6d8c1c9de5833b11be0f80e3b50f5296973b57f03cd45ae584162ac33bcbeb5b99fa767714b8531fef71b6d7ff559da40c3b04e75026ba3158f
|
7
|
+
data.tar.gz: e55ae407cc5b0da66922a41119b000da925391b58ea9da154a058b15f034334fae9e9c813cda12f4db75936c50e281df00acf8a4053808923d89d5efaa6927af
|
data/CHANGES.md
CHANGED
@@ -1,10 +1,48 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
### 2025-04-25 (2.11.3)
|
4
|
+
|
5
|
+
* Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
|
6
|
+
|
7
|
+
### 2025-04-24 (2.11.2)
|
8
|
+
|
9
|
+
* Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
|
10
|
+
It now emits a deprecation warning.
|
11
|
+
|
12
|
+
### 2025-04-24 (2.11.1)
|
13
|
+
|
14
|
+
* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
|
15
|
+
These were deprecated 16 years ago, but never emited warnings, only undocumented, so are
|
16
|
+
still used by a few gems.
|
17
|
+
|
18
|
+
### 2025-04-24 (2.11.0)
|
19
|
+
|
20
|
+
* Optimize Integer generation to be ~1.8x faster.
|
21
|
+
* Optimize Float generation to be ~10x faster.
|
22
|
+
* Fix `JSON.load` proc argument to substitute the parsed object with the return value.
|
23
|
+
This better match `Marshal.load` behavior.
|
24
|
+
* Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
|
25
|
+
* Deprecate `JSON.load_default_options`.
|
26
|
+
* Deprecate `JSON.unsafe_load_default_options`.
|
27
|
+
* Deprecate `JSON.dump_default_options`.
|
28
|
+
* Deprecate `Kernel#j`
|
29
|
+
* Deprecate `Kernel#jj`
|
30
|
+
* Remove outdated `JSON.iconv`.
|
31
|
+
* Remove `Class#json_creatable?` monkey patch.
|
32
|
+
* Remove deprecated `JSON.restore` method.
|
33
|
+
* Remove deprecated `JSON.unparse` method.
|
34
|
+
* Remove deprecated `JSON.fast_unparse` method.
|
35
|
+
* Remove deprecated `JSON.pretty_unparse` method.
|
36
|
+
* Remove deprecated `JSON::UnparserError` constant.
|
37
|
+
* Remove outdated `JSON::MissingUnicodeSupport` constant.
|
38
|
+
|
3
39
|
### 2025-03-12 (2.10.2)
|
4
40
|
|
5
41
|
* Fix a potential crash in the C extension parser.
|
6
42
|
* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` unadvertently changed it.
|
7
43
|
* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
|
44
|
+
* Ensure parser error snippets are valid UTF-8.
|
45
|
+
* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
|
8
46
|
|
9
47
|
### 2025-02-10 (2.10.1)
|
10
48
|
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
#include "ruby.h"
|
5
5
|
#include "ruby/encoding.h"
|
6
|
+
#include "../vendor/jeaiii-ltoa.h"
|
6
7
|
|
7
8
|
/* shims */
|
8
9
|
/* This is the fallback definition from Ruby 3.4 */
|
@@ -150,6 +151,13 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
|
150
151
|
}
|
151
152
|
}
|
152
153
|
|
154
|
+
/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
|
155
|
+
static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
|
156
|
+
{
|
157
|
+
fb->ptr[fb->len] = chr;
|
158
|
+
fb->len += 1;
|
159
|
+
}
|
160
|
+
|
153
161
|
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
154
162
|
{
|
155
163
|
const char *newstr = StringValuePtr(str);
|
@@ -167,25 +175,48 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
|
167
175
|
fb->len++;
|
168
176
|
}
|
169
177
|
|
170
|
-
static
|
178
|
+
static inline char *fbuffer_cursor(FBuffer *fb)
|
179
|
+
{
|
180
|
+
return fb->ptr + fb->len;
|
181
|
+
}
|
182
|
+
|
183
|
+
static inline void fbuffer_advance_to(FBuffer *fb, char *end)
|
171
184
|
{
|
172
|
-
|
173
|
-
long sign = number;
|
174
|
-
char* tmp = buf;
|
175
|
-
|
176
|
-
if (sign < 0) number = -number;
|
177
|
-
do *tmp-- = digits[number % 10]; while (number /= 10);
|
178
|
-
if (sign < 0) *tmp-- = '-';
|
179
|
-
return buf - tmp;
|
185
|
+
fb->len = end - fb->ptr;
|
180
186
|
}
|
181
187
|
|
182
|
-
|
188
|
+
/*
|
189
|
+
* Appends the decimal string representation of \a number into the buffer.
|
190
|
+
*/
|
183
191
|
static void fbuffer_append_long(FBuffer *fb, long number)
|
184
192
|
{
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
193
|
+
/*
|
194
|
+
* The jeaiii_ultoa() function produces digits left-to-right,
|
195
|
+
* allowing us to write directly into the buffer, but we don't know
|
196
|
+
* the number of resulting characters.
|
197
|
+
*
|
198
|
+
* We do know, however, that the `number` argument is always in the
|
199
|
+
* range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
|
200
|
+
* -4611686018427387904 to 4611686018427387903. The max number of chars
|
201
|
+
* generated is therefore 20 (including a potential sign character).
|
202
|
+
*/
|
203
|
+
|
204
|
+
static const int MAX_CHARS_FOR_LONG = 20;
|
205
|
+
|
206
|
+
fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
|
207
|
+
|
208
|
+
if (number < 0) {
|
209
|
+
fbuffer_append_reserved_char(fb, '-');
|
210
|
+
|
211
|
+
/*
|
212
|
+
* Since number is always > LONG_MIN, `-number` will not overflow
|
213
|
+
* and is always the positive abs() value.
|
214
|
+
*/
|
215
|
+
number = -number;
|
216
|
+
}
|
217
|
+
|
218
|
+
char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
|
219
|
+
fbuffer_advance_to(fb, end);
|
189
220
|
}
|
190
221
|
|
191
222
|
static VALUE fbuffer_finalize(FBuffer *fb)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "ruby.h"
|
2
2
|
#include "../fbuffer/fbuffer.h"
|
3
|
+
#include "../vendor/fpconv.c"
|
3
4
|
|
4
5
|
#include <math.h>
|
5
6
|
#include <ctype.h>
|
@@ -44,7 +45,7 @@ static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_n
|
|
44
45
|
|
45
46
|
struct generate_json_data;
|
46
47
|
|
47
|
-
typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data,
|
48
|
+
typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
48
49
|
|
49
50
|
struct generate_json_data {
|
50
51
|
FBuffer *buffer;
|
@@ -56,20 +57,20 @@ struct generate_json_data {
|
|
56
57
|
|
57
58
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
58
59
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
59
|
-
static void generate_json(FBuffer *buffer, struct generate_json_data *data,
|
60
|
-
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data,
|
61
|
-
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data,
|
62
|
-
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data,
|
63
|
-
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data,
|
64
|
-
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data,
|
65
|
-
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data,
|
60
|
+
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
61
|
+
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
62
|
+
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
63
|
+
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
64
|
+
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
65
|
+
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
66
|
+
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
66
67
|
#ifdef RUBY_INTEGER_UNIFICATION
|
67
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data,
|
68
|
+
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
68
69
|
#endif
|
69
|
-
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data,
|
70
|
-
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data,
|
71
|
-
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data,
|
72
|
-
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data,
|
70
|
+
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
71
|
+
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
72
|
+
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
73
|
+
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
|
73
74
|
|
74
75
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
75
76
|
|
@@ -801,12 +802,12 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
801
802
|
int j;
|
802
803
|
|
803
804
|
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
|
804
|
-
if (RB_UNLIKELY(state->object_nl)) {
|
805
|
-
fbuffer_append_str(buffer, state->object_nl);
|
805
|
+
if (RB_UNLIKELY(data->state->object_nl)) {
|
806
|
+
fbuffer_append_str(buffer, data->state->object_nl);
|
806
807
|
}
|
807
|
-
if (RB_UNLIKELY(state->indent)) {
|
808
|
+
if (RB_UNLIKELY(data->state->indent)) {
|
808
809
|
for (j = 0; j < depth; j++) {
|
809
|
-
fbuffer_append_str(buffer, state->indent);
|
810
|
+
fbuffer_append_str(buffer, data->state->indent);
|
810
811
|
}
|
811
812
|
}
|
812
813
|
|
@@ -828,21 +829,22 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
828
829
|
}
|
829
830
|
|
830
831
|
if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
|
831
|
-
generate_json_string(buffer, data,
|
832
|
+
generate_json_string(buffer, data, key_to_s);
|
832
833
|
} else {
|
833
|
-
generate_json(buffer, data,
|
834
|
+
generate_json(buffer, data, key_to_s);
|
834
835
|
}
|
835
|
-
if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before);
|
836
|
+
if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before);
|
836
837
|
fbuffer_append_char(buffer, ':');
|
837
|
-
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
|
838
|
-
generate_json(buffer, data,
|
838
|
+
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
|
839
|
+
generate_json(buffer, data, val);
|
839
840
|
|
840
841
|
arg->iter++;
|
841
842
|
return ST_CONTINUE;
|
842
843
|
}
|
843
844
|
|
844
|
-
static inline long increase_depth(
|
845
|
+
static inline long increase_depth(struct generate_json_data *data)
|
845
846
|
{
|
847
|
+
JSON_Generator_State *state = data->state;
|
846
848
|
long depth = ++state->depth;
|
847
849
|
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
848
850
|
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
@@ -850,14 +852,14 @@ static inline long increase_depth(JSON_Generator_State *state)
|
|
850
852
|
return depth;
|
851
853
|
}
|
852
854
|
|
853
|
-
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data,
|
855
|
+
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
854
856
|
{
|
855
857
|
int j;
|
856
|
-
long depth = increase_depth(
|
858
|
+
long depth = increase_depth(data);
|
857
859
|
|
858
860
|
if (RHASH_SIZE(obj) == 0) {
|
859
861
|
fbuffer_append(buffer, "{}", 2);
|
860
|
-
--state->depth;
|
862
|
+
--data->state->depth;
|
861
863
|
return;
|
862
864
|
}
|
863
865
|
|
@@ -869,49 +871,49 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
869
871
|
};
|
870
872
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
871
873
|
|
872
|
-
depth = --state->depth;
|
873
|
-
if (RB_UNLIKELY(state->object_nl)) {
|
874
|
-
fbuffer_append_str(buffer, state->object_nl);
|
875
|
-
if (RB_UNLIKELY(state->indent)) {
|
874
|
+
depth = --data->state->depth;
|
875
|
+
if (RB_UNLIKELY(data->state->object_nl)) {
|
876
|
+
fbuffer_append_str(buffer, data->state->object_nl);
|
877
|
+
if (RB_UNLIKELY(data->state->indent)) {
|
876
878
|
for (j = 0; j < depth; j++) {
|
877
|
-
fbuffer_append_str(buffer, state->indent);
|
879
|
+
fbuffer_append_str(buffer, data->state->indent);
|
878
880
|
}
|
879
881
|
}
|
880
882
|
}
|
881
883
|
fbuffer_append_char(buffer, '}');
|
882
884
|
}
|
883
885
|
|
884
|
-
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data,
|
886
|
+
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
885
887
|
{
|
886
888
|
int i, j;
|
887
|
-
long depth = increase_depth(
|
889
|
+
long depth = increase_depth(data);
|
888
890
|
|
889
891
|
if (RARRAY_LEN(obj) == 0) {
|
890
892
|
fbuffer_append(buffer, "[]", 2);
|
891
|
-
--state->depth;
|
893
|
+
--data->state->depth;
|
892
894
|
return;
|
893
895
|
}
|
894
896
|
|
895
897
|
fbuffer_append_char(buffer, '[');
|
896
|
-
if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
|
898
|
+
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
897
899
|
for(i = 0; i < RARRAY_LEN(obj); i++) {
|
898
900
|
if (i > 0) {
|
899
901
|
fbuffer_append_char(buffer, ',');
|
900
|
-
if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
|
902
|
+
if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
|
901
903
|
}
|
902
|
-
if (RB_UNLIKELY(state->indent)) {
|
904
|
+
if (RB_UNLIKELY(data->state->indent)) {
|
903
905
|
for (j = 0; j < depth; j++) {
|
904
|
-
fbuffer_append_str(buffer, state->indent);
|
906
|
+
fbuffer_append_str(buffer, data->state->indent);
|
905
907
|
}
|
906
908
|
}
|
907
|
-
generate_json(buffer, data,
|
909
|
+
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
908
910
|
}
|
909
|
-
state->depth = --depth;
|
910
|
-
if (RB_UNLIKELY(state->array_nl)) {
|
911
|
-
fbuffer_append_str(buffer, state->array_nl);
|
912
|
-
if (RB_UNLIKELY(state->indent)) {
|
911
|
+
data->state->depth = --depth;
|
912
|
+
if (RB_UNLIKELY(data->state->array_nl)) {
|
913
|
+
fbuffer_append_str(buffer, data->state->array_nl);
|
914
|
+
if (RB_UNLIKELY(data->state->indent)) {
|
913
915
|
for (j = 0; j < depth; j++) {
|
914
|
-
fbuffer_append_str(buffer, state->indent);
|
916
|
+
fbuffer_append_str(buffer, data->state->indent);
|
915
917
|
}
|
916
918
|
}
|
917
919
|
}
|
@@ -960,7 +962,7 @@ static inline VALUE ensure_valid_encoding(VALUE str)
|
|
960
962
|
return str;
|
961
963
|
}
|
962
964
|
|
963
|
-
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data,
|
965
|
+
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
964
966
|
{
|
965
967
|
obj = ensure_valid_encoding(obj);
|
966
968
|
|
@@ -976,9 +978,9 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
976
978
|
switch(rb_enc_str_coderange(obj)) {
|
977
979
|
case ENC_CODERANGE_7BIT:
|
978
980
|
case ENC_CODERANGE_VALID:
|
979
|
-
if (RB_UNLIKELY(state->ascii_only)) {
|
980
|
-
convert_UTF8_to_ASCII_only_JSON(&search, state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
981
|
-
} else if (RB_UNLIKELY(state->script_safe)) {
|
981
|
+
if (RB_UNLIKELY(data->state->ascii_only)) {
|
982
|
+
convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
|
983
|
+
} else if (RB_UNLIKELY(data->state->script_safe)) {
|
982
984
|
convert_UTF8_to_script_safe_JSON(&search);
|
983
985
|
} else {
|
984
986
|
convert_UTF8_to_JSON(&search);
|
@@ -991,7 +993,7 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
991
993
|
fbuffer_append_char(buffer, '"');
|
992
994
|
}
|
993
995
|
|
994
|
-
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data,
|
996
|
+
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
995
997
|
{
|
996
998
|
VALUE tmp;
|
997
999
|
if (rb_respond_to(obj, i_to_json)) {
|
@@ -1001,100 +1003,117 @@ static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *d
|
|
1001
1003
|
} else {
|
1002
1004
|
tmp = rb_funcall(obj, i_to_s, 0);
|
1003
1005
|
Check_Type(tmp, T_STRING);
|
1004
|
-
generate_json_string(buffer, data,
|
1006
|
+
generate_json_string(buffer, data, tmp);
|
1005
1007
|
}
|
1006
1008
|
}
|
1007
1009
|
|
1008
|
-
static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data,
|
1010
|
+
static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1009
1011
|
{
|
1010
|
-
if (state->strict) {
|
1011
|
-
generate_json_string(buffer, data,
|
1012
|
+
if (data->state->strict) {
|
1013
|
+
generate_json_string(buffer, data, rb_sym2str(obj));
|
1012
1014
|
} else {
|
1013
|
-
generate_json_fallback(buffer, data,
|
1015
|
+
generate_json_fallback(buffer, data, obj);
|
1014
1016
|
}
|
1015
1017
|
}
|
1016
1018
|
|
1017
|
-
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data,
|
1019
|
+
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1018
1020
|
{
|
1019
1021
|
fbuffer_append(buffer, "null", 4);
|
1020
1022
|
}
|
1021
1023
|
|
1022
|
-
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data,
|
1024
|
+
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1023
1025
|
{
|
1024
1026
|
fbuffer_append(buffer, "false", 5);
|
1025
1027
|
}
|
1026
1028
|
|
1027
|
-
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data,
|
1029
|
+
static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1028
1030
|
{
|
1029
1031
|
fbuffer_append(buffer, "true", 4);
|
1030
1032
|
}
|
1031
1033
|
|
1032
|
-
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data,
|
1034
|
+
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1033
1035
|
{
|
1034
1036
|
fbuffer_append_long(buffer, FIX2LONG(obj));
|
1035
1037
|
}
|
1036
1038
|
|
1037
|
-
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data,
|
1039
|
+
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1038
1040
|
{
|
1039
1041
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
1040
1042
|
fbuffer_append_str(buffer, tmp);
|
1041
1043
|
}
|
1042
1044
|
|
1043
1045
|
#ifdef RUBY_INTEGER_UNIFICATION
|
1044
|
-
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data,
|
1046
|
+
static void generate_json_integer(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1045
1047
|
{
|
1046
1048
|
if (FIXNUM_P(obj))
|
1047
|
-
generate_json_fixnum(buffer, data,
|
1049
|
+
generate_json_fixnum(buffer, data, obj);
|
1048
1050
|
else
|
1049
|
-
generate_json_bignum(buffer, data,
|
1051
|
+
generate_json_bignum(buffer, data, obj);
|
1050
1052
|
}
|
1051
1053
|
#endif
|
1052
1054
|
|
1053
|
-
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data,
|
1055
|
+
static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1054
1056
|
{
|
1055
1057
|
double value = RFLOAT_VALUE(obj);
|
1056
|
-
char allow_nan = state->allow_nan;
|
1057
|
-
if (
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1058
|
+
char allow_nan = data->state->allow_nan;
|
1059
|
+
if (isinf(value) || isnan(value)) {
|
1060
|
+
/* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
|
1061
|
+
if (!allow_nan) {
|
1062
|
+
if (data->state->strict && data->state->as_json) {
|
1063
|
+
VALUE casted_obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
|
1061
1064
|
if (casted_obj != obj) {
|
1062
|
-
increase_depth(
|
1063
|
-
generate_json(buffer, data,
|
1064
|
-
state->depth--;
|
1065
|
+
increase_depth(data);
|
1066
|
+
generate_json(buffer, data, casted_obj);
|
1067
|
+
data->state->depth--;
|
1065
1068
|
return;
|
1066
1069
|
}
|
1067
1070
|
}
|
1068
1071
|
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
|
1069
1072
|
}
|
1073
|
+
|
1074
|
+
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
1075
|
+
fbuffer_append_str(buffer, tmp);
|
1076
|
+
return;
|
1070
1077
|
}
|
1071
|
-
|
1078
|
+
|
1079
|
+
/* This implementation writes directly into the buffer. We reserve
|
1080
|
+
* the 24 characters that fpconv_dtoa states as its maximum, plus
|
1081
|
+
* 2 more characters for the potential ".0" suffix.
|
1082
|
+
*/
|
1083
|
+
fbuffer_inc_capa(buffer, 26);
|
1084
|
+
char* d = buffer->ptr + buffer->len;
|
1085
|
+
int len = fpconv_dtoa(value, d);
|
1086
|
+
|
1087
|
+
/* fpconv_dtoa converts a float to its shortest string representation,
|
1088
|
+
* but it adds a ".0" if this is a plain integer.
|
1089
|
+
*/
|
1090
|
+
buffer->len += len;
|
1072
1091
|
}
|
1073
1092
|
|
1074
|
-
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data,
|
1093
|
+
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1075
1094
|
{
|
1076
1095
|
VALUE fragment = RSTRUCT_GET(obj, 0);
|
1077
1096
|
Check_Type(fragment, T_STRING);
|
1078
1097
|
fbuffer_append_str(buffer, fragment);
|
1079
1098
|
}
|
1080
1099
|
|
1081
|
-
static void generate_json(FBuffer *buffer, struct generate_json_data *data,
|
1100
|
+
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
1082
1101
|
{
|
1083
1102
|
bool as_json_called = false;
|
1084
1103
|
start:
|
1085
1104
|
if (obj == Qnil) {
|
1086
|
-
generate_json_null(buffer, data,
|
1105
|
+
generate_json_null(buffer, data, obj);
|
1087
1106
|
} else if (obj == Qfalse) {
|
1088
|
-
generate_json_false(buffer, data,
|
1107
|
+
generate_json_false(buffer, data, obj);
|
1089
1108
|
} else if (obj == Qtrue) {
|
1090
|
-
generate_json_true(buffer, data,
|
1109
|
+
generate_json_true(buffer, data, obj);
|
1091
1110
|
} else if (RB_SPECIAL_CONST_P(obj)) {
|
1092
1111
|
if (RB_FIXNUM_P(obj)) {
|
1093
|
-
generate_json_fixnum(buffer, data,
|
1112
|
+
generate_json_fixnum(buffer, data, obj);
|
1094
1113
|
} else if (RB_FLONUM_P(obj)) {
|
1095
|
-
generate_json_float(buffer, data,
|
1114
|
+
generate_json_float(buffer, data, obj);
|
1096
1115
|
} else if (RB_STATIC_SYM_P(obj)) {
|
1097
|
-
generate_json_symbol(buffer, data,
|
1116
|
+
generate_json_symbol(buffer, data, obj);
|
1098
1117
|
} else {
|
1099
1118
|
goto general;
|
1100
1119
|
}
|
@@ -1102,43 +1121,43 @@ start:
|
|
1102
1121
|
VALUE klass = RBASIC_CLASS(obj);
|
1103
1122
|
switch (RB_BUILTIN_TYPE(obj)) {
|
1104
1123
|
case T_BIGNUM:
|
1105
|
-
generate_json_bignum(buffer, data,
|
1124
|
+
generate_json_bignum(buffer, data, obj);
|
1106
1125
|
break;
|
1107
1126
|
case T_HASH:
|
1108
1127
|
if (klass != rb_cHash) goto general;
|
1109
|
-
generate_json_object(buffer, data,
|
1128
|
+
generate_json_object(buffer, data, obj);
|
1110
1129
|
break;
|
1111
1130
|
case T_ARRAY:
|
1112
1131
|
if (klass != rb_cArray) goto general;
|
1113
|
-
generate_json_array(buffer, data,
|
1132
|
+
generate_json_array(buffer, data, obj);
|
1114
1133
|
break;
|
1115
1134
|
case T_STRING:
|
1116
1135
|
if (klass != rb_cString) goto general;
|
1117
|
-
generate_json_string(buffer, data,
|
1136
|
+
generate_json_string(buffer, data, obj);
|
1118
1137
|
break;
|
1119
1138
|
case T_SYMBOL:
|
1120
|
-
generate_json_symbol(buffer, data,
|
1139
|
+
generate_json_symbol(buffer, data, obj);
|
1121
1140
|
break;
|
1122
1141
|
case T_FLOAT:
|
1123
1142
|
if (klass != rb_cFloat) goto general;
|
1124
|
-
generate_json_float(buffer, data,
|
1143
|
+
generate_json_float(buffer, data, obj);
|
1125
1144
|
break;
|
1126
1145
|
case T_STRUCT:
|
1127
1146
|
if (klass != cFragment) goto general;
|
1128
|
-
generate_json_fragment(buffer, data,
|
1147
|
+
generate_json_fragment(buffer, data, obj);
|
1129
1148
|
break;
|
1130
1149
|
default:
|
1131
1150
|
general:
|
1132
|
-
if (state->strict) {
|
1133
|
-
if (RTEST(state->as_json) && !as_json_called) {
|
1134
|
-
obj = rb_proc_call_with_block(state->as_json, 1, &obj, Qnil);
|
1151
|
+
if (data->state->strict) {
|
1152
|
+
if (RTEST(data->state->as_json) && !as_json_called) {
|
1153
|
+
obj = rb_proc_call_with_block(data->state->as_json, 1, &obj, Qnil);
|
1135
1154
|
as_json_called = true;
|
1136
1155
|
goto start;
|
1137
1156
|
} else {
|
1138
1157
|
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
|
1139
1158
|
}
|
1140
1159
|
} else {
|
1141
|
-
generate_json_fallback(buffer, data,
|
1160
|
+
generate_json_fallback(buffer, data, obj);
|
1142
1161
|
}
|
1143
1162
|
}
|
1144
1163
|
}
|
@@ -1148,7 +1167,7 @@ static VALUE generate_json_try(VALUE d)
|
|
1148
1167
|
{
|
1149
1168
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
1150
1169
|
|
1151
|
-
data->func(data->buffer, data, data->
|
1170
|
+
data->func(data->buffer, data, data->obj);
|
1152
1171
|
|
1153
1172
|
return Qnil;
|
1154
1173
|
}
|
@@ -4,7 +4,6 @@ require 'mkmf'
|
|
4
4
|
have_func("rb_enc_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
|
5
5
|
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
|
6
6
|
have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
|
7
|
-
have_func("rb_category_warn", "ruby.h") # Missing on TruffleRuby
|
8
7
|
have_func("strnlen", "string.h") # Missing on Solaris 10
|
9
8
|
|
10
9
|
append_cflags("-std=c99")
|