json 2.9.0 → 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 +60 -0
- data/LEGAL +0 -52
- data/README.md +75 -2
- data/ext/json/ext/fbuffer/fbuffer.h +46 -24
- data/ext/json/ext/generator/generator.c +479 -329
- data/ext/json/ext/parser/extconf.rb +1 -1
- data/ext/json/ext/parser/parser.c +738 -2603
- data/ext/json/ext/vendor/fpconv.c +479 -0
- data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
- data/json.gemspec +3 -4
- data/lib/json/add/symbol.rb +7 -2
- data/lib/json/common.rb +374 -168
- data/lib/json/ext/generator/state.rb +1 -11
- data/lib/json/ext.rb +26 -4
- data/lib/json/truffle_ruby/generator.rb +111 -50
- data/lib/json/version.rb +1 -1
- metadata +8 -11
- data/ext/json/ext/parser/parser.rl +0 -1457
|
@@ -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>
|
|
@@ -12,6 +13,7 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
12
13
|
VALUE space_before;
|
|
13
14
|
VALUE object_nl;
|
|
14
15
|
VALUE array_nl;
|
|
16
|
+
VALUE as_json;
|
|
15
17
|
|
|
16
18
|
long max_nesting;
|
|
17
19
|
long depth;
|
|
@@ -27,11 +29,11 @@ typedef struct JSON_Generator_StateStruct {
|
|
|
27
29
|
#define RB_UNLIKELY(cond) (cond)
|
|
28
30
|
#endif
|
|
29
31
|
|
|
30
|
-
static VALUE mJSON, cState, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
32
|
+
static VALUE mJSON, cState, cFragment, mString_Extend, eGeneratorError, eNestingError, Encoding_UTF_8;
|
|
31
33
|
|
|
32
34
|
static ID i_to_s, i_to_json, i_new, i_pack, i_unpack, i_create_id, i_extend, i_encode;
|
|
33
|
-
static
|
|
34
|
-
|
|
35
|
+
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan,
|
|
36
|
+
sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json;
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
#define GET_STATE_TO(self, state) \
|
|
@@ -43,7 +45,7 @@ static ID sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl,
|
|
|
43
45
|
|
|
44
46
|
struct generate_json_data;
|
|
45
47
|
|
|
46
|
-
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);
|
|
47
49
|
|
|
48
50
|
struct generate_json_data {
|
|
49
51
|
FBuffer *buffer;
|
|
@@ -55,19 +57,20 @@ struct generate_json_data {
|
|
|
55
57
|
|
|
56
58
|
static VALUE cState_from_state_s(VALUE self, VALUE opts);
|
|
57
59
|
static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
|
|
58
|
-
static void generate_json(FBuffer *buffer, struct generate_json_data *data,
|
|
59
|
-
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data,
|
|
60
|
-
static void generate_json_array(FBuffer *buffer, struct generate_json_data *data,
|
|
61
|
-
static void generate_json_string(FBuffer *buffer, struct generate_json_data *data,
|
|
62
|
-
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data,
|
|
63
|
-
static void generate_json_false(FBuffer *buffer, struct generate_json_data *data,
|
|
64
|
-
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);
|
|
65
67
|
#ifdef RUBY_INTEGER_UNIFICATION
|
|
66
|
-
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);
|
|
67
69
|
#endif
|
|
68
|
-
static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data,
|
|
69
|
-
static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data,
|
|
70
|
-
static void generate_json_float(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);
|
|
71
74
|
|
|
72
75
|
static int usascii_encindex, utf8_encindex, binary_encindex;
|
|
73
76
|
|
|
@@ -96,6 +99,75 @@ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
|
96
99
|
raise_generator_error_str(invalid_object, str);
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
// 0 - single byte char that don't need to be escaped.
|
|
103
|
+
// (x | 8) - char that needs to be escaped.
|
|
104
|
+
static const unsigned char CHAR_LENGTH_MASK = 7;
|
|
105
|
+
static const unsigned char ESCAPE_MASK = 8;
|
|
106
|
+
|
|
107
|
+
typedef struct _search_state {
|
|
108
|
+
const char *ptr;
|
|
109
|
+
const char *end;
|
|
110
|
+
const char *cursor;
|
|
111
|
+
FBuffer *buffer;
|
|
112
|
+
} search_state;
|
|
113
|
+
|
|
114
|
+
static inline void search_flush(search_state *search)
|
|
115
|
+
{
|
|
116
|
+
fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor);
|
|
117
|
+
search->cursor = search->ptr;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static const unsigned char escape_table_basic[256] = {
|
|
121
|
+
// ASCII Control Characters
|
|
122
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
123
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
124
|
+
// ASCII Characters
|
|
125
|
+
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
|
|
126
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
127
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
128
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
|
|
129
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
130
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
static inline unsigned char search_escape_basic(search_state *search)
|
|
134
|
+
{
|
|
135
|
+
while (search->ptr < search->end) {
|
|
136
|
+
if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) {
|
|
137
|
+
search_flush(search);
|
|
138
|
+
return 1;
|
|
139
|
+
} else {
|
|
140
|
+
search->ptr++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
search_flush(search);
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static inline void escape_UTF8_char_basic(search_state *search) {
|
|
148
|
+
const unsigned char ch = (unsigned char)*search->ptr;
|
|
149
|
+
switch (ch) {
|
|
150
|
+
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
|
|
151
|
+
case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
|
|
152
|
+
case '/': fbuffer_append(search->buffer, "\\/", 2); break;
|
|
153
|
+
case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
|
|
154
|
+
case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
|
|
155
|
+
case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
|
|
156
|
+
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
|
|
157
|
+
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
|
|
158
|
+
default: {
|
|
159
|
+
const char *hexdig = "0123456789abcdef";
|
|
160
|
+
char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
|
|
161
|
+
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
|
162
|
+
scratch[5] = hexdig[ch & 0xf];
|
|
163
|
+
fbuffer_append(search->buffer, scratch, 6);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
search->ptr++;
|
|
168
|
+
search->cursor = search->ptr;
|
|
169
|
+
}
|
|
170
|
+
|
|
99
171
|
/* Converts in_string to a JSON string (without the wrapping '"'
|
|
100
172
|
* characters) in FBuffer out_buffer.
|
|
101
173
|
*
|
|
@@ -106,282 +178,241 @@ static void raise_generator_error(VALUE invalid_object, const char *fmt, ...)
|
|
|
106
178
|
*
|
|
107
179
|
* - If out_ascii_only: non-ASCII characters (>0x7F)
|
|
108
180
|
*
|
|
109
|
-
* - If
|
|
181
|
+
* - If script_safe: forwardslash (/), line separator (U+2028), and
|
|
110
182
|
* paragraph separator (U+2029)
|
|
111
183
|
*
|
|
112
184
|
* Everything else (should be UTF-8) is just passed through and
|
|
113
185
|
* appended to the result.
|
|
114
186
|
*/
|
|
115
|
-
static void convert_UTF8_to_JSON(
|
|
187
|
+
static inline void convert_UTF8_to_JSON(search_state *search)
|
|
116
188
|
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
unsigned long len = RSTRING_LEN(str);
|
|
122
|
-
|
|
123
|
-
unsigned long beg = 0, pos = 0;
|
|
124
|
-
|
|
125
|
-
#define FLUSH_POS(bytes) if (pos > beg) { fbuffer_append(out_buffer, &ptr[beg], pos - beg); } pos += bytes; beg = pos;
|
|
126
|
-
|
|
127
|
-
while (pos < len) {
|
|
128
|
-
unsigned char ch = ptr[pos];
|
|
129
|
-
unsigned char ch_len = escape_table[ch];
|
|
130
|
-
/* JSON encoding */
|
|
189
|
+
while (search_escape_basic(search)) {
|
|
190
|
+
escape_UTF8_char_basic(search);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
131
193
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
194
|
+
static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) {
|
|
195
|
+
const unsigned char ch = (unsigned char)*search->ptr;
|
|
196
|
+
switch (ch_len) {
|
|
197
|
+
case 1: {
|
|
198
|
+
switch (ch) {
|
|
199
|
+
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
|
|
200
|
+
case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
|
|
201
|
+
case '/': fbuffer_append(search->buffer, "\\/", 2); break;
|
|
202
|
+
case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
|
|
203
|
+
case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
|
|
204
|
+
case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
|
|
205
|
+
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
|
|
206
|
+
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
|
|
207
|
+
default: {
|
|
208
|
+
const char *hexdig = "0123456789abcdef";
|
|
209
|
+
char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
|
|
210
|
+
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
|
211
|
+
scratch[5] = hexdig[ch & 0xf];
|
|
212
|
+
fbuffer_append(search->buffer, scratch, 6);
|
|
154
213
|
break;
|
|
155
214
|
}
|
|
156
|
-
case 3: {
|
|
157
|
-
unsigned char b2 = ptr[pos + 1];
|
|
158
|
-
if (RB_UNLIKELY(out_script_safe && ch == 0xE2 && b2 == 0x80)) {
|
|
159
|
-
unsigned char b3 = ptr[pos + 2];
|
|
160
|
-
if (b3 == 0xA8) {
|
|
161
|
-
FLUSH_POS(3);
|
|
162
|
-
fbuffer_append(out_buffer, "\\u2028", 6);
|
|
163
|
-
break;
|
|
164
|
-
} else if (b3 == 0xA9) {
|
|
165
|
-
FLUSH_POS(3);
|
|
166
|
-
fbuffer_append(out_buffer, "\\u2029", 6);
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// fallthrough
|
|
171
|
-
}
|
|
172
|
-
default:
|
|
173
|
-
pos += ch_len;
|
|
174
|
-
break;
|
|
175
215
|
}
|
|
176
|
-
|
|
177
|
-
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case 3: {
|
|
219
|
+
if (search->ptr[2] & 1) {
|
|
220
|
+
fbuffer_append(search->buffer, "\\u2029", 6);
|
|
221
|
+
} else {
|
|
222
|
+
fbuffer_append(search->buffer, "\\u2028", 6);
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
178
225
|
}
|
|
179
226
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (beg < len) {
|
|
183
|
-
fbuffer_append(out_buffer, &ptr[beg], len - beg);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
RB_GC_GUARD(str);
|
|
227
|
+
search->cursor = (search->ptr += ch_len);
|
|
187
228
|
}
|
|
188
229
|
|
|
189
|
-
static const char
|
|
230
|
+
static const unsigned char script_safe_escape_table[256] = {
|
|
190
231
|
// ASCII Control Characters
|
|
191
|
-
|
|
192
|
-
|
|
232
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
233
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
193
234
|
// ASCII Characters
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
235
|
+
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/'
|
|
236
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
237
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
238
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
|
|
239
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
240
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
200
241
|
// Continuation byte
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
242
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
243
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
244
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
245
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
205
246
|
// First byte of a 2-byte code point
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// First byte of a
|
|
209
|
-
|
|
210
|
-
//First byte of a 4+byte code point
|
|
211
|
-
|
|
247
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
248
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
249
|
+
// First byte of a 3-byte code point
|
|
250
|
+
3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029
|
|
251
|
+
//First byte of a 4+ byte code point
|
|
252
|
+
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
|
|
212
253
|
};
|
|
213
254
|
|
|
214
|
-
static
|
|
215
|
-
// ASCII Control Characters
|
|
216
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
217
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
218
|
-
// ASCII Characters
|
|
219
|
-
0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, // '"' and '/'
|
|
220
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
221
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
222
|
-
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, // '\\'
|
|
223
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
224
|
-
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
225
|
-
// Continuation byte
|
|
226
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
227
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
228
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
229
|
-
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
230
|
-
// First byte of a 2-byte code point
|
|
231
|
-
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
232
|
-
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
233
|
-
// First byte of a 4-byte code point
|
|
234
|
-
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
|
|
235
|
-
//First byte of a 4+byte code point
|
|
236
|
-
4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1,
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
static void convert_ASCII_to_JSON(FBuffer *out_buffer, VALUE str, const char escape_table[256])
|
|
255
|
+
static inline unsigned char search_script_safe_escape(search_state *search)
|
|
240
256
|
{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const char *ptr = RSTRING_PTR(str);
|
|
245
|
-
unsigned long len = RSTRING_LEN(str);
|
|
257
|
+
while (search->ptr < search->end) {
|
|
258
|
+
unsigned char ch = (unsigned char)*search->ptr;
|
|
259
|
+
unsigned char ch_len = script_safe_escape_table[ch];
|
|
246
260
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
case '\\': fbuffer_append(out_buffer, "\\\\", 2); break;
|
|
261
|
-
case '/': fbuffer_append(out_buffer, "\\/", 2); break;
|
|
262
|
-
case '\b': fbuffer_append(out_buffer, "\\b", 2); break;
|
|
263
|
-
case '\f': fbuffer_append(out_buffer, "\\f", 2); break;
|
|
264
|
-
case '\n': fbuffer_append(out_buffer, "\\n", 2); break;
|
|
265
|
-
case '\r': fbuffer_append(out_buffer, "\\r", 2); break;
|
|
266
|
-
case '\t': fbuffer_append(out_buffer, "\\t", 2); break;
|
|
267
|
-
default:
|
|
268
|
-
scratch[2] = '0';
|
|
269
|
-
scratch[3] = '0';
|
|
270
|
-
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
|
271
|
-
scratch[5] = hexdig[ch & 0xf];
|
|
272
|
-
fbuffer_append(out_buffer, scratch, 6);
|
|
261
|
+
if (RB_UNLIKELY(ch_len)) {
|
|
262
|
+
if (ch_len & ESCAPE_MASK) {
|
|
263
|
+
if (RB_UNLIKELY(ch_len == 11)) {
|
|
264
|
+
const unsigned char *uptr = (const unsigned char *)search->ptr;
|
|
265
|
+
if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) {
|
|
266
|
+
search->ptr += 3;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
search_flush(search);
|
|
271
|
+
return ch_len & CHAR_LENGTH_MASK;
|
|
272
|
+
} else {
|
|
273
|
+
search->ptr += ch_len;
|
|
273
274
|
}
|
|
275
|
+
} else {
|
|
276
|
+
search->ptr++;
|
|
274
277
|
}
|
|
275
|
-
|
|
276
|
-
pos++;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (beg < len) {
|
|
280
|
-
fbuffer_append(out_buffer, &ptr[beg], len - beg);
|
|
281
278
|
}
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
search_flush(search);
|
|
280
|
+
return 0;
|
|
284
281
|
}
|
|
285
282
|
|
|
286
|
-
static void
|
|
283
|
+
static void convert_UTF8_to_script_safe_JSON(search_state *search)
|
|
287
284
|
{
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
unsigned long beg = 0, pos = 0;
|
|
285
|
+
unsigned char ch_len;
|
|
286
|
+
while ((ch_len = search_script_safe_escape(search))) {
|
|
287
|
+
escape_UTF8_char(search, ch_len);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
295
290
|
|
|
296
|
-
|
|
291
|
+
static const unsigned char ascii_only_escape_table[256] = {
|
|
292
|
+
// ASCII Control Characters
|
|
293
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
294
|
+
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
295
|
+
// ASCII Characters
|
|
296
|
+
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
|
|
297
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
298
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
299
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
|
|
300
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
301
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
302
|
+
// Continuation byte
|
|
303
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
304
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
305
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
306
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
307
|
+
// First byte of a 2-byte code point
|
|
308
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
309
|
+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
310
|
+
// First byte of a 3-byte code point
|
|
311
|
+
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
312
|
+
//First byte of a 4+ byte code point
|
|
313
|
+
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
|
|
314
|
+
};
|
|
297
315
|
|
|
298
|
-
|
|
299
|
-
|
|
316
|
+
static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256])
|
|
317
|
+
{
|
|
318
|
+
while (search->ptr < search->end) {
|
|
319
|
+
unsigned char ch = (unsigned char)*search->ptr;
|
|
300
320
|
unsigned char ch_len = escape_table[ch];
|
|
301
321
|
|
|
302
322
|
if (RB_UNLIKELY(ch_len)) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
323
|
+
search_flush(search);
|
|
324
|
+
return ch_len & CHAR_LENGTH_MASK;
|
|
325
|
+
} else {
|
|
326
|
+
search->ptr++;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
search_flush(search);
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len) {
|
|
334
|
+
const unsigned char ch = (unsigned char)*search->ptr;
|
|
335
|
+
switch (ch_len) {
|
|
336
|
+
case 1: {
|
|
337
|
+
switch (ch) {
|
|
338
|
+
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
|
|
339
|
+
case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
|
|
340
|
+
case '/': fbuffer_append(search->buffer, "\\/", 2); break;
|
|
341
|
+
case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
|
|
342
|
+
case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
|
|
343
|
+
case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
|
|
344
|
+
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
|
|
345
|
+
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
|
|
346
|
+
default: {
|
|
347
|
+
const char *hexdig = "0123456789abcdef";
|
|
348
|
+
char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
|
|
349
|
+
scratch[4] = hexdig[(ch >> 4) & 0xf];
|
|
350
|
+
scratch[5] = hexdig[ch & 0xf];
|
|
351
|
+
fbuffer_append(search->buffer, scratch, 6);
|
|
324
352
|
break;
|
|
325
353
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
case 3:
|
|
333
|
-
wchar = ptr[pos] & 0x0F;
|
|
334
|
-
break;
|
|
335
|
-
case 4:
|
|
336
|
-
wchar = ptr[pos] & 0x07;
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
default: {
|
|
358
|
+
const char *hexdig = "0123456789abcdef";
|
|
359
|
+
char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
|
|
339
360
|
|
|
340
|
-
|
|
341
|
-
wchar = (wchar << 6) | (ptr[pos+i] & 0x3F);
|
|
342
|
-
}
|
|
361
|
+
uint32_t wchar = 0;
|
|
343
362
|
|
|
344
|
-
|
|
363
|
+
switch(ch_len) {
|
|
364
|
+
case 2:
|
|
365
|
+
wchar = ch & 0x1F;
|
|
366
|
+
break;
|
|
367
|
+
case 3:
|
|
368
|
+
wchar = ch & 0x0F;
|
|
369
|
+
break;
|
|
370
|
+
case 4:
|
|
371
|
+
wchar = ch & 0x07;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
345
374
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
scratch[4] = hexdig[(wchar >> 4) & 0xf];
|
|
350
|
-
scratch[5] = hexdig[wchar & 0xf];
|
|
351
|
-
fbuffer_append(out_buffer, scratch, 6);
|
|
352
|
-
} else {
|
|
353
|
-
uint16_t hi, lo;
|
|
354
|
-
wchar -= 0x10000;
|
|
355
|
-
hi = 0xD800 + (uint16_t)(wchar >> 10);
|
|
356
|
-
lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
|
|
357
|
-
|
|
358
|
-
scratch[2] = hexdig[hi >> 12];
|
|
359
|
-
scratch[3] = hexdig[(hi >> 8) & 0xf];
|
|
360
|
-
scratch[4] = hexdig[(hi >> 4) & 0xf];
|
|
361
|
-
scratch[5] = hexdig[hi & 0xf];
|
|
362
|
-
|
|
363
|
-
scratch[8] = hexdig[lo >> 12];
|
|
364
|
-
scratch[9] = hexdig[(lo >> 8) & 0xf];
|
|
365
|
-
scratch[10] = hexdig[(lo >> 4) & 0xf];
|
|
366
|
-
scratch[11] = hexdig[lo & 0xf];
|
|
367
|
-
|
|
368
|
-
fbuffer_append(out_buffer, scratch, 12);
|
|
369
|
-
}
|
|
375
|
+
for (short i = 1; i < ch_len; i++) {
|
|
376
|
+
wchar = (wchar << 6) | (search->ptr[i] & 0x3F);
|
|
377
|
+
}
|
|
370
378
|
|
|
371
|
-
|
|
372
|
-
|
|
379
|
+
if (wchar <= 0xFFFF) {
|
|
380
|
+
scratch[2] = hexdig[wchar >> 12];
|
|
381
|
+
scratch[3] = hexdig[(wchar >> 8) & 0xf];
|
|
382
|
+
scratch[4] = hexdig[(wchar >> 4) & 0xf];
|
|
383
|
+
scratch[5] = hexdig[wchar & 0xf];
|
|
384
|
+
fbuffer_append(search->buffer, scratch, 6);
|
|
385
|
+
} else {
|
|
386
|
+
uint16_t hi, lo;
|
|
387
|
+
wchar -= 0x10000;
|
|
388
|
+
hi = 0xD800 + (uint16_t)(wchar >> 10);
|
|
389
|
+
lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
|
|
390
|
+
|
|
391
|
+
scratch[2] = hexdig[hi >> 12];
|
|
392
|
+
scratch[3] = hexdig[(hi >> 8) & 0xf];
|
|
393
|
+
scratch[4] = hexdig[(hi >> 4) & 0xf];
|
|
394
|
+
scratch[5] = hexdig[hi & 0xf];
|
|
395
|
+
|
|
396
|
+
scratch[8] = hexdig[lo >> 12];
|
|
397
|
+
scratch[9] = hexdig[(lo >> 8) & 0xf];
|
|
398
|
+
scratch[10] = hexdig[(lo >> 4) & 0xf];
|
|
399
|
+
scratch[11] = hexdig[lo & 0xf];
|
|
400
|
+
|
|
401
|
+
fbuffer_append(search->buffer, scratch, 12);
|
|
373
402
|
}
|
|
374
|
-
|
|
375
|
-
|
|
403
|
+
|
|
404
|
+
break;
|
|
376
405
|
}
|
|
377
406
|
}
|
|
378
|
-
|
|
407
|
+
search->cursor = (search->ptr += ch_len);
|
|
408
|
+
}
|
|
379
409
|
|
|
380
|
-
|
|
381
|
-
|
|
410
|
+
static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256])
|
|
411
|
+
{
|
|
412
|
+
unsigned char ch_len;
|
|
413
|
+
while ((ch_len = search_ascii_only_escape(search, escape_table))) {
|
|
414
|
+
full_escape_UTF8_char(search, ch_len);
|
|
382
415
|
}
|
|
383
|
-
|
|
384
|
-
RB_GC_GUARD(str);
|
|
385
416
|
}
|
|
386
417
|
|
|
387
418
|
/*
|
|
@@ -674,6 +705,7 @@ static void State_mark(void *ptr)
|
|
|
674
705
|
rb_gc_mark_movable(state->space_before);
|
|
675
706
|
rb_gc_mark_movable(state->object_nl);
|
|
676
707
|
rb_gc_mark_movable(state->array_nl);
|
|
708
|
+
rb_gc_mark_movable(state->as_json);
|
|
677
709
|
}
|
|
678
710
|
|
|
679
711
|
static void State_compact(void *ptr)
|
|
@@ -684,6 +716,7 @@ static void State_compact(void *ptr)
|
|
|
684
716
|
state->space_before = rb_gc_location(state->space_before);
|
|
685
717
|
state->object_nl = rb_gc_location(state->object_nl);
|
|
686
718
|
state->array_nl = rb_gc_location(state->array_nl);
|
|
719
|
+
state->as_json = rb_gc_location(state->as_json);
|
|
687
720
|
}
|
|
688
721
|
|
|
689
722
|
static void State_free(void *ptr)
|
|
@@ -740,6 +773,7 @@ static void vstate_spill(struct generate_json_data *data)
|
|
|
740
773
|
RB_OBJ_WRITTEN(vstate, Qundef, state->space_before);
|
|
741
774
|
RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
|
|
742
775
|
RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
|
|
776
|
+
RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
|
|
743
777
|
}
|
|
744
778
|
|
|
745
779
|
static inline VALUE vstate_get(struct generate_json_data *data)
|
|
@@ -768,12 +802,12 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
768
802
|
int j;
|
|
769
803
|
|
|
770
804
|
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
|
|
771
|
-
if (RB_UNLIKELY(state->object_nl)) {
|
|
772
|
-
fbuffer_append_str(buffer, state->object_nl);
|
|
805
|
+
if (RB_UNLIKELY(data->state->object_nl)) {
|
|
806
|
+
fbuffer_append_str(buffer, data->state->object_nl);
|
|
773
807
|
}
|
|
774
|
-
if (RB_UNLIKELY(state->indent)) {
|
|
808
|
+
if (RB_UNLIKELY(data->state->indent)) {
|
|
775
809
|
for (j = 0; j < depth; j++) {
|
|
776
|
-
fbuffer_append_str(buffer, state->indent);
|
|
810
|
+
fbuffer_append_str(buffer, data->state->indent);
|
|
777
811
|
}
|
|
778
812
|
}
|
|
779
813
|
|
|
@@ -795,32 +829,37 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
795
829
|
}
|
|
796
830
|
|
|
797
831
|
if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
|
|
798
|
-
generate_json_string(buffer, data,
|
|
832
|
+
generate_json_string(buffer, data, key_to_s);
|
|
799
833
|
} else {
|
|
800
|
-
generate_json(buffer, data,
|
|
834
|
+
generate_json(buffer, data, key_to_s);
|
|
801
835
|
}
|
|
802
|
-
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);
|
|
803
837
|
fbuffer_append_char(buffer, ':');
|
|
804
|
-
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
|
|
805
|
-
generate_json(buffer, data,
|
|
838
|
+
if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
|
|
839
|
+
generate_json(buffer, data, val);
|
|
806
840
|
|
|
807
841
|
arg->iter++;
|
|
808
842
|
return ST_CONTINUE;
|
|
809
843
|
}
|
|
810
844
|
|
|
811
|
-
static
|
|
845
|
+
static inline long increase_depth(struct generate_json_data *data)
|
|
812
846
|
{
|
|
813
|
-
|
|
847
|
+
JSON_Generator_State *state = data->state;
|
|
814
848
|
long depth = ++state->depth;
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
if (max_nesting != 0 && depth > max_nesting) {
|
|
849
|
+
if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
|
|
818
850
|
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
|
819
851
|
}
|
|
852
|
+
return depth;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
856
|
+
{
|
|
857
|
+
int j;
|
|
858
|
+
long depth = increase_depth(data);
|
|
820
859
|
|
|
821
860
|
if (RHASH_SIZE(obj) == 0) {
|
|
822
861
|
fbuffer_append(buffer, "{}", 2);
|
|
823
|
-
--state->depth;
|
|
862
|
+
--data->state->depth;
|
|
824
863
|
return;
|
|
825
864
|
}
|
|
826
865
|
|
|
@@ -832,53 +871,49 @@ static void generate_json_object(FBuffer *buffer, struct generate_json_data *dat
|
|
|
832
871
|
};
|
|
833
872
|
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
834
873
|
|
|
835
|
-
depth = --state->depth;
|
|
836
|
-
if (RB_UNLIKELY(state->object_nl)) {
|
|
837
|
-
fbuffer_append_str(buffer, state->object_nl);
|
|
838
|
-
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)) {
|
|
839
878
|
for (j = 0; j < depth; j++) {
|
|
840
|
-
fbuffer_append_str(buffer, state->indent);
|
|
879
|
+
fbuffer_append_str(buffer, data->state->indent);
|
|
841
880
|
}
|
|
842
881
|
}
|
|
843
882
|
}
|
|
844
883
|
fbuffer_append_char(buffer, '}');
|
|
845
884
|
}
|
|
846
885
|
|
|
847
|
-
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)
|
|
848
887
|
{
|
|
849
|
-
long max_nesting = state->max_nesting;
|
|
850
|
-
long depth = ++state->depth;
|
|
851
888
|
int i, j;
|
|
852
|
-
|
|
853
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
|
854
|
-
}
|
|
889
|
+
long depth = increase_depth(data);
|
|
855
890
|
|
|
856
891
|
if (RARRAY_LEN(obj) == 0) {
|
|
857
892
|
fbuffer_append(buffer, "[]", 2);
|
|
858
|
-
--state->depth;
|
|
893
|
+
--data->state->depth;
|
|
859
894
|
return;
|
|
860
895
|
}
|
|
861
896
|
|
|
862
897
|
fbuffer_append_char(buffer, '[');
|
|
863
|
-
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);
|
|
864
899
|
for(i = 0; i < RARRAY_LEN(obj); i++) {
|
|
865
900
|
if (i > 0) {
|
|
866
901
|
fbuffer_append_char(buffer, ',');
|
|
867
|
-
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);
|
|
868
903
|
}
|
|
869
|
-
if (RB_UNLIKELY(state->indent)) {
|
|
904
|
+
if (RB_UNLIKELY(data->state->indent)) {
|
|
870
905
|
for (j = 0; j < depth; j++) {
|
|
871
|
-
fbuffer_append_str(buffer, state->indent);
|
|
906
|
+
fbuffer_append_str(buffer, data->state->indent);
|
|
872
907
|
}
|
|
873
908
|
}
|
|
874
|
-
generate_json(buffer, data,
|
|
909
|
+
generate_json(buffer, data, RARRAY_AREF(obj, i));
|
|
875
910
|
}
|
|
876
|
-
state->depth = --depth;
|
|
877
|
-
if (RB_UNLIKELY(state->array_nl)) {
|
|
878
|
-
fbuffer_append_str(buffer, state->array_nl);
|
|
879
|
-
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)) {
|
|
880
915
|
for (j = 0; j < depth; j++) {
|
|
881
|
-
fbuffer_append_str(buffer, state->indent);
|
|
916
|
+
fbuffer_append_str(buffer, data->state->indent);
|
|
882
917
|
}
|
|
883
918
|
}
|
|
884
919
|
}
|
|
@@ -927,21 +962,28 @@ static inline VALUE ensure_valid_encoding(VALUE str)
|
|
|
927
962
|
return str;
|
|
928
963
|
}
|
|
929
964
|
|
|
930
|
-
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)
|
|
931
966
|
{
|
|
932
967
|
obj = ensure_valid_encoding(obj);
|
|
933
968
|
|
|
934
969
|
fbuffer_append_char(buffer, '"');
|
|
935
970
|
|
|
971
|
+
long len;
|
|
972
|
+
search_state search;
|
|
973
|
+
search.buffer = buffer;
|
|
974
|
+
RSTRING_GETMEM(obj, search.ptr, len);
|
|
975
|
+
search.cursor = search.ptr;
|
|
976
|
+
search.end = search.ptr + len;
|
|
977
|
+
|
|
936
978
|
switch(rb_enc_str_coderange(obj)) {
|
|
937
979
|
case ENC_CODERANGE_7BIT:
|
|
938
|
-
convert_ASCII_to_JSON(buffer, obj, state->script_safe ? script_safe_escape_table : escape_table);
|
|
939
|
-
break;
|
|
940
980
|
case ENC_CODERANGE_VALID:
|
|
941
|
-
if (RB_UNLIKELY(state->ascii_only)) {
|
|
942
|
-
convert_UTF8_to_ASCII_only_JSON(
|
|
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)) {
|
|
984
|
+
convert_UTF8_to_script_safe_JSON(&search);
|
|
943
985
|
} else {
|
|
944
|
-
convert_UTF8_to_JSON(
|
|
986
|
+
convert_UTF8_to_JSON(&search);
|
|
945
987
|
}
|
|
946
988
|
break;
|
|
947
989
|
default:
|
|
@@ -951,69 +993,127 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
|
|
|
951
993
|
fbuffer_append_char(buffer, '"');
|
|
952
994
|
}
|
|
953
995
|
|
|
954
|
-
static void
|
|
996
|
+
static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
997
|
+
{
|
|
998
|
+
VALUE tmp;
|
|
999
|
+
if (rb_respond_to(obj, i_to_json)) {
|
|
1000
|
+
tmp = rb_funcall(obj, i_to_json, 1, vstate_get(data));
|
|
1001
|
+
Check_Type(tmp, T_STRING);
|
|
1002
|
+
fbuffer_append_str(buffer, tmp);
|
|
1003
|
+
} else {
|
|
1004
|
+
tmp = rb_funcall(obj, i_to_s, 0);
|
|
1005
|
+
Check_Type(tmp, T_STRING);
|
|
1006
|
+
generate_json_string(buffer, data, tmp);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1011
|
+
{
|
|
1012
|
+
if (data->state->strict) {
|
|
1013
|
+
generate_json_string(buffer, data, rb_sym2str(obj));
|
|
1014
|
+
} else {
|
|
1015
|
+
generate_json_fallback(buffer, data, obj);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
955
1020
|
{
|
|
956
1021
|
fbuffer_append(buffer, "null", 4);
|
|
957
1022
|
}
|
|
958
1023
|
|
|
959
|
-
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)
|
|
960
1025
|
{
|
|
961
1026
|
fbuffer_append(buffer, "false", 5);
|
|
962
1027
|
}
|
|
963
1028
|
|
|
964
|
-
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)
|
|
965
1030
|
{
|
|
966
1031
|
fbuffer_append(buffer, "true", 4);
|
|
967
1032
|
}
|
|
968
1033
|
|
|
969
|
-
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)
|
|
970
1035
|
{
|
|
971
1036
|
fbuffer_append_long(buffer, FIX2LONG(obj));
|
|
972
1037
|
}
|
|
973
1038
|
|
|
974
|
-
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)
|
|
975
1040
|
{
|
|
976
1041
|
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
977
1042
|
fbuffer_append_str(buffer, tmp);
|
|
978
1043
|
}
|
|
979
1044
|
|
|
980
1045
|
#ifdef RUBY_INTEGER_UNIFICATION
|
|
981
|
-
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)
|
|
982
1047
|
{
|
|
983
1048
|
if (FIXNUM_P(obj))
|
|
984
|
-
generate_json_fixnum(buffer, data,
|
|
1049
|
+
generate_json_fixnum(buffer, data, obj);
|
|
985
1050
|
else
|
|
986
|
-
generate_json_bignum(buffer, data,
|
|
1051
|
+
generate_json_bignum(buffer, data, obj);
|
|
987
1052
|
}
|
|
988
1053
|
#endif
|
|
989
1054
|
|
|
990
|
-
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)
|
|
991
1056
|
{
|
|
992
1057
|
double value = RFLOAT_VALUE(obj);
|
|
993
|
-
char allow_nan = state->allow_nan;
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
if (
|
|
997
|
-
|
|
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);
|
|
1064
|
+
if (casted_obj != obj) {
|
|
1065
|
+
increase_depth(data);
|
|
1066
|
+
generate_json(buffer, data, casted_obj);
|
|
1067
|
+
data->state->depth--;
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
|
|
998
1072
|
}
|
|
1073
|
+
|
|
1074
|
+
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
1075
|
+
fbuffer_append_str(buffer, tmp);
|
|
1076
|
+
return;
|
|
999
1077
|
}
|
|
1000
|
-
|
|
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;
|
|
1001
1091
|
}
|
|
1002
1092
|
|
|
1003
|
-
static void
|
|
1093
|
+
static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1004
1094
|
{
|
|
1005
|
-
VALUE
|
|
1095
|
+
VALUE fragment = RSTRUCT_GET(obj, 0);
|
|
1096
|
+
Check_Type(fragment, T_STRING);
|
|
1097
|
+
fbuffer_append_str(buffer, fragment);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
|
|
1101
|
+
{
|
|
1102
|
+
bool as_json_called = false;
|
|
1103
|
+
start:
|
|
1006
1104
|
if (obj == Qnil) {
|
|
1007
|
-
generate_json_null(buffer, data,
|
|
1105
|
+
generate_json_null(buffer, data, obj);
|
|
1008
1106
|
} else if (obj == Qfalse) {
|
|
1009
|
-
generate_json_false(buffer, data,
|
|
1107
|
+
generate_json_false(buffer, data, obj);
|
|
1010
1108
|
} else if (obj == Qtrue) {
|
|
1011
|
-
generate_json_true(buffer, data,
|
|
1109
|
+
generate_json_true(buffer, data, obj);
|
|
1012
1110
|
} else if (RB_SPECIAL_CONST_P(obj)) {
|
|
1013
1111
|
if (RB_FIXNUM_P(obj)) {
|
|
1014
|
-
generate_json_fixnum(buffer, data,
|
|
1112
|
+
generate_json_fixnum(buffer, data, obj);
|
|
1015
1113
|
} else if (RB_FLONUM_P(obj)) {
|
|
1016
|
-
generate_json_float(buffer, data,
|
|
1114
|
+
generate_json_float(buffer, data, obj);
|
|
1115
|
+
} else if (RB_STATIC_SYM_P(obj)) {
|
|
1116
|
+
generate_json_symbol(buffer, data, obj);
|
|
1017
1117
|
} else {
|
|
1018
1118
|
goto general;
|
|
1019
1119
|
}
|
|
@@ -1021,36 +1121,43 @@ static void generate_json(FBuffer *buffer, struct generate_json_data *data, JSON
|
|
|
1021
1121
|
VALUE klass = RBASIC_CLASS(obj);
|
|
1022
1122
|
switch (RB_BUILTIN_TYPE(obj)) {
|
|
1023
1123
|
case T_BIGNUM:
|
|
1024
|
-
generate_json_bignum(buffer, data,
|
|
1124
|
+
generate_json_bignum(buffer, data, obj);
|
|
1025
1125
|
break;
|
|
1026
1126
|
case T_HASH:
|
|
1027
1127
|
if (klass != rb_cHash) goto general;
|
|
1028
|
-
generate_json_object(buffer, data,
|
|
1128
|
+
generate_json_object(buffer, data, obj);
|
|
1029
1129
|
break;
|
|
1030
1130
|
case T_ARRAY:
|
|
1031
1131
|
if (klass != rb_cArray) goto general;
|
|
1032
|
-
generate_json_array(buffer, data,
|
|
1132
|
+
generate_json_array(buffer, data, obj);
|
|
1033
1133
|
break;
|
|
1034
1134
|
case T_STRING:
|
|
1035
1135
|
if (klass != rb_cString) goto general;
|
|
1036
|
-
generate_json_string(buffer, data,
|
|
1136
|
+
generate_json_string(buffer, data, obj);
|
|
1137
|
+
break;
|
|
1138
|
+
case T_SYMBOL:
|
|
1139
|
+
generate_json_symbol(buffer, data, obj);
|
|
1037
1140
|
break;
|
|
1038
1141
|
case T_FLOAT:
|
|
1039
1142
|
if (klass != rb_cFloat) goto general;
|
|
1040
|
-
generate_json_float(buffer, data,
|
|
1143
|
+
generate_json_float(buffer, data, obj);
|
|
1144
|
+
break;
|
|
1145
|
+
case T_STRUCT:
|
|
1146
|
+
if (klass != cFragment) goto general;
|
|
1147
|
+
generate_json_fragment(buffer, data, obj);
|
|
1041
1148
|
break;
|
|
1042
1149
|
default:
|
|
1043
1150
|
general:
|
|
1044
|
-
if (state->strict) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
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);
|
|
1154
|
+
as_json_called = true;
|
|
1155
|
+
goto start;
|
|
1156
|
+
} else {
|
|
1157
|
+
raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
|
|
1158
|
+
}
|
|
1050
1159
|
} else {
|
|
1051
|
-
|
|
1052
|
-
Check_Type(tmp, T_STRING);
|
|
1053
|
-
generate_json_string(buffer, data, state, tmp);
|
|
1160
|
+
generate_json_fallback(buffer, data, obj);
|
|
1054
1161
|
}
|
|
1055
1162
|
}
|
|
1056
1163
|
}
|
|
@@ -1060,7 +1167,7 @@ static VALUE generate_json_try(VALUE d)
|
|
|
1060
1167
|
{
|
|
1061
1168
|
struct generate_json_data *data = (struct generate_json_data *)d;
|
|
1062
1169
|
|
|
1063
|
-
data->func(data->buffer, data, data->
|
|
1170
|
+
data->func(data->buffer, data, data->obj);
|
|
1064
1171
|
|
|
1065
1172
|
return Qnil;
|
|
1066
1173
|
}
|
|
@@ -1097,8 +1204,19 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func,
|
|
|
1097
1204
|
return fbuffer_finalize(&buffer);
|
|
1098
1205
|
}
|
|
1099
1206
|
|
|
1100
|
-
|
|
1207
|
+
/* call-seq:
|
|
1208
|
+
* generate(obj) -> String
|
|
1209
|
+
* generate(obj, anIO) -> anIO
|
|
1210
|
+
*
|
|
1211
|
+
* Generates a valid JSON document from object +obj+ and returns the
|
|
1212
|
+
* result. If no valid JSON document can be created this method raises a
|
|
1213
|
+
* GeneratorError exception.
|
|
1214
|
+
*/
|
|
1215
|
+
static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
|
|
1101
1216
|
{
|
|
1217
|
+
rb_check_arity(argc, 1, 2);
|
|
1218
|
+
VALUE obj = argv[0];
|
|
1219
|
+
VALUE io = argc > 1 ? argv[1] : Qnil;
|
|
1102
1220
|
VALUE result = cState_partial_generate(self, obj, generate_json, io);
|
|
1103
1221
|
GET_STATE(self);
|
|
1104
1222
|
(void)state;
|
|
@@ -1132,6 +1250,7 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
|
1132
1250
|
objState->space_before = origState->space_before;
|
|
1133
1251
|
objState->object_nl = origState->object_nl;
|
|
1134
1252
|
objState->array_nl = origState->array_nl;
|
|
1253
|
+
objState->as_json = origState->as_json;
|
|
1135
1254
|
return obj;
|
|
1136
1255
|
}
|
|
1137
1256
|
|
|
@@ -1283,6 +1402,28 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
|
1283
1402
|
return Qnil;
|
|
1284
1403
|
}
|
|
1285
1404
|
|
|
1405
|
+
/*
|
|
1406
|
+
* call-seq: as_json()
|
|
1407
|
+
*
|
|
1408
|
+
* This string is put at the end of a line that holds a JSON array.
|
|
1409
|
+
*/
|
|
1410
|
+
static VALUE cState_as_json(VALUE self)
|
|
1411
|
+
{
|
|
1412
|
+
GET_STATE(self);
|
|
1413
|
+
return state->as_json;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/*
|
|
1417
|
+
* call-seq: as_json=(as_json)
|
|
1418
|
+
*
|
|
1419
|
+
* This string is put at the end of a line that holds a JSON array.
|
|
1420
|
+
*/
|
|
1421
|
+
static VALUE cState_as_json_set(VALUE self, VALUE as_json)
|
|
1422
|
+
{
|
|
1423
|
+
GET_STATE(self);
|
|
1424
|
+
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
|
|
1425
|
+
return Qnil;
|
|
1426
|
+
}
|
|
1286
1427
|
|
|
1287
1428
|
/*
|
|
1288
1429
|
* call-seq: check_circular?
|
|
@@ -1504,6 +1645,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
|
|
|
1504
1645
|
else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
|
|
1505
1646
|
else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
|
|
1506
1647
|
else if (key == sym_strict) { state->strict = RTEST(val); }
|
|
1648
|
+
else if (key == sym_as_json) { state->as_json = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse; }
|
|
1507
1649
|
return ST_CONTINUE;
|
|
1508
1650
|
}
|
|
1509
1651
|
|
|
@@ -1564,6 +1706,10 @@ void Init_generator(void)
|
|
|
1564
1706
|
rb_require("json/common");
|
|
1565
1707
|
|
|
1566
1708
|
mJSON = rb_define_module("JSON");
|
|
1709
|
+
|
|
1710
|
+
rb_global_variable(&cFragment);
|
|
1711
|
+
cFragment = rb_const_get(mJSON, rb_intern("Fragment"));
|
|
1712
|
+
|
|
1567
1713
|
VALUE mExt = rb_define_module_under(mJSON, "Ext");
|
|
1568
1714
|
VALUE mGenerator = rb_define_module_under(mExt, "Generator");
|
|
1569
1715
|
|
|
@@ -1591,6 +1737,8 @@ void Init_generator(void)
|
|
|
1591
1737
|
rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
|
|
1592
1738
|
rb_define_method(cState, "array_nl", cState_array_nl, 0);
|
|
1593
1739
|
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
|
|
1740
|
+
rb_define_method(cState, "as_json", cState_as_json, 0);
|
|
1741
|
+
rb_define_method(cState, "as_json=", cState_as_json_set, 1);
|
|
1594
1742
|
rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
|
|
1595
1743
|
rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
|
|
1596
1744
|
rb_define_method(cState, "script_safe", cState_script_safe, 0);
|
|
@@ -1611,7 +1759,8 @@ void Init_generator(void)
|
|
|
1611
1759
|
rb_define_method(cState, "depth=", cState_depth_set, 1);
|
|
1612
1760
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
1613
1761
|
rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
|
|
1614
|
-
|
|
1762
|
+
rb_define_method(cState, "generate", cState_generate, -1);
|
|
1763
|
+
rb_define_alias(cState, "generate_new", "generate"); // :nodoc:
|
|
1615
1764
|
|
|
1616
1765
|
rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
|
|
1617
1766
|
|
|
@@ -1682,6 +1831,7 @@ void Init_generator(void)
|
|
|
1682
1831
|
sym_script_safe = ID2SYM(rb_intern("script_safe"));
|
|
1683
1832
|
sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
|
|
1684
1833
|
sym_strict = ID2SYM(rb_intern("strict"));
|
|
1834
|
+
sym_as_json = ID2SYM(rb_intern("as_json"));
|
|
1685
1835
|
|
|
1686
1836
|
usascii_encindex = rb_usascii_encindex();
|
|
1687
1837
|
utf8_encindex = rb_utf8_encindex();
|