json_pure 1.2.4 → 1.4.6
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.
- data/CHANGES +32 -0
- data/README +14 -18
- data/Rakefile +19 -14
- data/VERSION +1 -1
- data/benchmarks/generator2_benchmark.rb +222 -0
- data/benchmarks/generator_benchmark.rb +63 -4
- data/benchmarks/ohai.json +1216 -0
- data/benchmarks/ohai.ruby +1 -0
- data/benchmarks/parser2_benchmark.rb +251 -0
- data/benchmarks/parser_benchmark.rb +52 -4
- data/ext/json/ext/generator/extconf.rb +14 -5
- data/ext/json/ext/generator/generator.c +880 -416
- data/ext/json/ext/generator/generator.h +197 -0
- data/ext/json/ext/parser/extconf.rb +8 -4
- data/ext/json/ext/parser/parser.c +225 -183
- data/ext/json/ext/parser/parser.h +71 -0
- data/ext/json/ext/parser/parser.rl +151 -109
- data/lib/json/common.rb +53 -42
- data/lib/json/pure/generator.rb +111 -120
- data/lib/json/pure/parser.rb +29 -27
- data/lib/json/version.rb +1 -1
- data/tests/test_json.rb +70 -3
- data/tests/test_json_addition.rb +6 -6
- data/tests/test_json_encoding.rb +4 -3
- data/tests/test_json_generate.rb +66 -7
- data/tests/test_json_unicode.rb +20 -6
- metadata +10 -8
- data/ext/json/ext/generator/unicode.c +0 -180
- data/ext/json/ext/generator/unicode.h +0 -53
- data/ext/json/ext/parser/unicode.c +0 -154
- data/ext/json/ext/parser/unicode.h +0 -58
|
@@ -1,294 +1,444 @@
|
|
|
1
|
-
#include
|
|
2
|
-
#include "ruby.h"
|
|
3
|
-
#if HAVE_RUBY_ST_H
|
|
4
|
-
#include "ruby/st.h"
|
|
5
|
-
#endif
|
|
6
|
-
#if HAVE_ST_H
|
|
7
|
-
#include "st.h"
|
|
8
|
-
#endif
|
|
9
|
-
#include "unicode.h"
|
|
10
|
-
#include <math.h>
|
|
11
|
-
|
|
12
|
-
#ifndef RHASH_SIZE
|
|
13
|
-
#define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
|
|
14
|
-
#endif
|
|
15
|
-
|
|
16
|
-
#ifndef RFLOAT_VALUE
|
|
17
|
-
#define RFLOAT_VALUE(val) (RFLOAT(val)->value)
|
|
18
|
-
#endif
|
|
19
|
-
|
|
20
|
-
#define option_given_p(opts, key) RTEST(rb_funcall(opts, i_key_p, 1, key))
|
|
1
|
+
#include "generator.h"
|
|
21
2
|
|
|
22
3
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
23
|
-
|
|
24
|
-
#define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
|
|
25
|
-
static VALUE mEncoding_UTF_8;
|
|
4
|
+
static VALUE CEncoding_UTF_8;
|
|
26
5
|
static ID i_encoding, i_encode;
|
|
27
|
-
#else
|
|
28
|
-
#define FORCE_UTF8(obj)
|
|
29
6
|
#endif
|
|
30
7
|
|
|
31
|
-
#define check_max_nesting(state, depth) do { \
|
|
32
|
-
long current_nesting = 1 + depth; \
|
|
33
|
-
if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
|
|
34
|
-
rb_raise(eNestingError, "nesting of %ld is too deep", current_nesting); \
|
|
35
|
-
} while (0);
|
|
36
|
-
|
|
37
8
|
static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
|
|
38
|
-
mHash, mArray,
|
|
9
|
+
mHash, mArray, mFixnum, mBignum, mFloat, mString, mString_Extend,
|
|
39
10
|
mTrueClass, mFalseClass, mNilClass, eGeneratorError,
|
|
40
|
-
|
|
11
|
+
eNestingError, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE,
|
|
12
|
+
i_SAFE_STATE_PROTOTYPE;
|
|
41
13
|
|
|
42
14
|
static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
|
|
43
|
-
i_object_nl, i_array_nl,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
typedef struct JSON_Generator_StateStruct {
|
|
48
|
-
VALUE indent;
|
|
49
|
-
VALUE space;
|
|
50
|
-
VALUE space_before;
|
|
51
|
-
VALUE object_nl;
|
|
52
|
-
VALUE array_nl;
|
|
53
|
-
int check_circular;
|
|
54
|
-
VALUE seen;
|
|
55
|
-
VALUE memo;
|
|
56
|
-
VALUE depth;
|
|
57
|
-
long max_nesting;
|
|
58
|
-
int flag;
|
|
59
|
-
int allow_nan;
|
|
60
|
-
} JSON_Generator_State;
|
|
61
|
-
|
|
62
|
-
#define GET_STATE(self) \
|
|
63
|
-
JSON_Generator_State *state; \
|
|
64
|
-
Data_Get_Struct(self, JSON_Generator_State, state);
|
|
15
|
+
i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
|
|
16
|
+
i_pack, i_unpack, i_create_id, i_extend, i_key_p, i_aref, i_send,
|
|
17
|
+
i_respond_to_p, i_match, i_keys, i_depth, i_dup;
|
|
65
18
|
|
|
66
|
-
/*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
19
|
+
/*
|
|
20
|
+
* Copyright 2001-2004 Unicode, Inc.
|
|
21
|
+
*
|
|
22
|
+
* Disclaimer
|
|
23
|
+
*
|
|
24
|
+
* This source code is provided as is by Unicode, Inc. No claims are
|
|
25
|
+
* made as to fitness for any particular purpose. No warranties of any
|
|
26
|
+
* kind are expressed or implied. The recipient agrees to determine
|
|
27
|
+
* applicability of information provided. If this file has been
|
|
28
|
+
* purchased on magnetic or optical media from Unicode, Inc., the
|
|
29
|
+
* sole remedy for any claim will be exchange of defective media
|
|
30
|
+
* within 90 days of receipt.
|
|
31
|
+
*
|
|
32
|
+
* Limitations on Rights to Redistribute This Code
|
|
33
|
+
*
|
|
34
|
+
* Unicode, Inc. hereby grants the right to freely use the information
|
|
35
|
+
* supplied in this file in the creation of products supporting the
|
|
36
|
+
* Unicode Standard, and to make copies of this file in any form
|
|
37
|
+
* for internal or external distribution as long as this notice
|
|
38
|
+
* remains attached.
|
|
76
39
|
*/
|
|
77
40
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
41
|
+
/*
|
|
42
|
+
* Index into the table below with the first byte of a UTF-8 sequence to
|
|
43
|
+
* get the number of trailing bytes that are supposed to follow it.
|
|
44
|
+
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
|
|
45
|
+
* left as-is for anyone who may want to do such conversion, which was
|
|
46
|
+
* allowed in earlier algorithms.
|
|
47
|
+
*/
|
|
48
|
+
static const char trailingBytesForUTF8[256] = {
|
|
49
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
50
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
51
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
52
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
53
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
54
|
+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
55
|
+
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
56
|
+
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* Magic values subtracted from a buffer value during UTF8 conversion.
|
|
61
|
+
* This table contains as many values as there might be trailing bytes
|
|
62
|
+
* in a UTF-8 sequence.
|
|
63
|
+
*/
|
|
64
|
+
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
|
65
|
+
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
|
|
69
|
+
* This must be called with the length pre-determined by the first byte.
|
|
70
|
+
* If not calling this from ConvertUTF8to*, then the length can be set by:
|
|
71
|
+
* length = trailingBytesForUTF8[*source]+1;
|
|
72
|
+
* and the sequence is illegal right away if there aren't that many bytes
|
|
73
|
+
* available.
|
|
74
|
+
* If presented with a length > 4, this returns 0. The Unicode
|
|
75
|
+
* definition of UTF-8 goes up to 4-byte sequences.
|
|
76
|
+
*/
|
|
77
|
+
static unsigned char isLegalUTF8(const UTF8 *source, int length)
|
|
78
|
+
{
|
|
79
|
+
UTF8 a;
|
|
80
|
+
const UTF8 *srcptr = source+length;
|
|
81
|
+
switch (length) {
|
|
82
|
+
default: return 0;
|
|
83
|
+
/* Everything else falls through when "1"... */
|
|
84
|
+
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
|
|
85
|
+
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0;
|
|
86
|
+
case 2: if ((a = (*--srcptr)) > 0xBF) return 0;
|
|
87
|
+
|
|
88
|
+
switch (*source) {
|
|
89
|
+
/* no fall-through in this inner switch */
|
|
90
|
+
case 0xE0: if (a < 0xA0) return 0; break;
|
|
91
|
+
case 0xED: if (a > 0x9F) return 0; break;
|
|
92
|
+
case 0xF0: if (a < 0x90) return 0; break;
|
|
93
|
+
case 0xF4: if (a > 0x8F) return 0; break;
|
|
94
|
+
default: if (a < 0x80) return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case 1: if (*source >= 0x80 && *source < 0xC2) return 0;
|
|
98
|
+
}
|
|
99
|
+
if (*source > 0xF4) return 0;
|
|
100
|
+
return 1;
|
|
132
101
|
}
|
|
133
102
|
|
|
134
|
-
|
|
103
|
+
/* Escapes the UTF16 character and stores the result in the buffer buf. */
|
|
104
|
+
static void unicode_escape(char *buf, UTF16 character)
|
|
135
105
|
{
|
|
136
|
-
|
|
106
|
+
const char *digits = "0123456789abcdef";
|
|
137
107
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
OBJ_INFECT(buf, tmp);
|
|
144
|
-
rb_str_buf_cat2(buf, ":");
|
|
145
|
-
tmp = rb_funcall(value, i_to_json, 0);
|
|
146
|
-
Check_Type(tmp, T_STRING);
|
|
147
|
-
rb_str_buf_append(buf, tmp);
|
|
148
|
-
OBJ_INFECT(buf, tmp);
|
|
108
|
+
buf[2] = digits[character >> 12];
|
|
109
|
+
buf[3] = digits[(character >> 8) & 0xf];
|
|
110
|
+
buf[4] = digits[(character >> 4) & 0xf];
|
|
111
|
+
buf[5] = digits[character & 0xf];
|
|
112
|
+
}
|
|
149
113
|
|
|
150
|
-
|
|
114
|
+
/* Escapes the UTF16 character and stores the result in the buffer buf, then
|
|
115
|
+
* the buffer buf іs appended to the FBuffer buffer. */
|
|
116
|
+
static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
|
|
117
|
+
character)
|
|
118
|
+
{
|
|
119
|
+
unicode_escape(buf, character);
|
|
120
|
+
fbuffer_append(buffer, buf, 6);
|
|
151
121
|
}
|
|
152
122
|
|
|
153
|
-
/*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* Returns a JSON string containing a JSON object, that is unparsed from
|
|
157
|
-
* this Hash instance.
|
|
158
|
-
* _state_ is a JSON::State object, that can also be used to configure the
|
|
159
|
-
* produced JSON string output further.
|
|
160
|
-
* _depth_ is used to find out nesting depth, to indent accordingly.
|
|
161
|
-
*/
|
|
162
|
-
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
|
|
123
|
+
/* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
|
|
124
|
+
* and control characters are JSON escaped. */
|
|
125
|
+
static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
|
|
163
126
|
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
127
|
+
const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
|
|
128
|
+
const UTF8 *sourceEnd = source + RSTRING_LEN(string);
|
|
129
|
+
char buf[6] = { '\\', 'u' };
|
|
130
|
+
|
|
131
|
+
while (source < sourceEnd) {
|
|
132
|
+
UTF32 ch = 0;
|
|
133
|
+
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
|
134
|
+
if (source + extraBytesToRead >= sourceEnd) {
|
|
135
|
+
rb_raise(rb_path2class("JSON::GeneratorError"),
|
|
136
|
+
"partial character in source, but hit end");
|
|
137
|
+
}
|
|
138
|
+
if (!isLegalUTF8(source, extraBytesToRead+1)) {
|
|
139
|
+
rb_raise(rb_path2class("JSON::GeneratorError"),
|
|
140
|
+
"source sequence is illegal/malformed utf-8");
|
|
141
|
+
}
|
|
142
|
+
/*
|
|
143
|
+
* The cases all fall through. See "Note A" below.
|
|
144
|
+
*/
|
|
145
|
+
switch (extraBytesToRead) {
|
|
146
|
+
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
|
147
|
+
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
|
148
|
+
case 3: ch += *source++; ch <<= 6;
|
|
149
|
+
case 2: ch += *source++; ch <<= 6;
|
|
150
|
+
case 1: ch += *source++; ch <<= 6;
|
|
151
|
+
case 0: ch += *source++;
|
|
152
|
+
}
|
|
153
|
+
ch -= offsetsFromUTF8[extraBytesToRead];
|
|
154
|
+
|
|
155
|
+
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
|
|
156
|
+
/* UTF-16 surrogate values are illegal in UTF-32 */
|
|
157
|
+
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
|
158
|
+
#if UNI_STRICT_CONVERSION
|
|
159
|
+
source -= (extraBytesToRead+1); /* return to the illegal value itself */
|
|
160
|
+
rb_raise(rb_path2class("JSON::GeneratorError"),
|
|
161
|
+
"source sequence is illegal/malformed utf-8");
|
|
162
|
+
#else
|
|
163
|
+
unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
|
|
164
|
+
#endif
|
|
165
|
+
} else {
|
|
166
|
+
/* normal case */
|
|
167
|
+
if (ch >= 0x20 && ch <= 0x7f) {
|
|
168
|
+
switch (ch) {
|
|
169
|
+
case '\\':
|
|
170
|
+
fbuffer_append(buffer, "\\\\", 2);
|
|
171
|
+
break;
|
|
172
|
+
case '"':
|
|
173
|
+
fbuffer_append(buffer, "\\\"", 2);
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
fbuffer_append_char(buffer, (char)ch);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
switch (ch) {
|
|
181
|
+
case '\n':
|
|
182
|
+
fbuffer_append(buffer, "\\n", 2);
|
|
183
|
+
break;
|
|
184
|
+
case '\r':
|
|
185
|
+
fbuffer_append(buffer, "\\r", 2);
|
|
186
|
+
break;
|
|
187
|
+
case '\t':
|
|
188
|
+
fbuffer_append(buffer, "\\t", 2);
|
|
189
|
+
break;
|
|
190
|
+
case '\f':
|
|
191
|
+
fbuffer_append(buffer, "\\f", 2);
|
|
192
|
+
break;
|
|
193
|
+
case '\b':
|
|
194
|
+
fbuffer_append(buffer, "\\b", 2);
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
unicode_escape_to_buffer(buffer, buf, (UTF16) ch);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
183
201
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
202
|
+
} else if (ch > UNI_MAX_UTF16) {
|
|
203
|
+
#if UNI_STRICT_CONVERSION
|
|
204
|
+
source -= (extraBytesToRead+1); /* return to the start */
|
|
205
|
+
rb_raise(rb_path2class("JSON::GeneratorError"),
|
|
206
|
+
"source sequence is illegal/malformed utf8");
|
|
207
|
+
#else
|
|
208
|
+
unicode_escape_to_buffer(buffer, buf, UNI_REPLACEMENT_CHAR);
|
|
209
|
+
#endif
|
|
187
210
|
} else {
|
|
188
|
-
|
|
211
|
+
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
|
212
|
+
ch -= halfBase;
|
|
213
|
+
unicode_escape_to_buffer(buffer, buf, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START));
|
|
214
|
+
unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
|
|
189
215
|
}
|
|
190
216
|
}
|
|
191
|
-
OBJ_INFECT(result, self);
|
|
192
|
-
FORCE_UTF8(result);
|
|
193
|
-
return result;
|
|
194
217
|
}
|
|
195
218
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
/* Converts string to a JSON string in FBuffer buffer, where only the
|
|
220
|
+
* characters required by the JSON standard are JSON escaped. The remaining
|
|
221
|
+
* characters (should be UTF8) are just passed through and appended to the
|
|
222
|
+
* result. */
|
|
223
|
+
static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
|
|
224
|
+
{
|
|
225
|
+
const char *ptr = RSTRING_PTR(string), *p;
|
|
226
|
+
int len = RSTRING_LEN(string), start = 0, end = 0;
|
|
227
|
+
const char *escape = NULL;
|
|
228
|
+
int escape_len;
|
|
229
|
+
unsigned char c;
|
|
230
|
+
char buf[6] = { '\\', 'u' };
|
|
231
|
+
|
|
232
|
+
for (start = 0, end = 0; end < len;) {
|
|
233
|
+
p = ptr + end;
|
|
234
|
+
c = (unsigned char) *p;
|
|
235
|
+
if (c < 0x20) {
|
|
236
|
+
switch (c) {
|
|
237
|
+
case '\n':
|
|
238
|
+
escape = "\\n";
|
|
239
|
+
escape_len = 2;
|
|
240
|
+
break;
|
|
241
|
+
case '\r':
|
|
242
|
+
escape = "\\r";
|
|
243
|
+
escape_len = 2;
|
|
244
|
+
break;
|
|
245
|
+
case '\t':
|
|
246
|
+
escape = "\\t";
|
|
247
|
+
escape_len = 2;
|
|
248
|
+
break;
|
|
249
|
+
case '\f':
|
|
250
|
+
escape = "\\f";
|
|
251
|
+
escape_len = 2;
|
|
252
|
+
break;
|
|
253
|
+
case '\b':
|
|
254
|
+
escape = "\\b";
|
|
255
|
+
escape_len = 2;
|
|
256
|
+
break;
|
|
257
|
+
default:
|
|
258
|
+
unicode_escape(buf, (UTF16) *p);
|
|
259
|
+
escape = buf;
|
|
260
|
+
escape_len = 6;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
switch (c) {
|
|
265
|
+
case '\\':
|
|
266
|
+
escape = "\\\\";
|
|
267
|
+
escape_len = 2;
|
|
268
|
+
break;
|
|
269
|
+
case '"':
|
|
270
|
+
escape = "\\\"";
|
|
271
|
+
escape_len = 2;
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
end++;
|
|
275
|
+
continue;
|
|
276
|
+
break;
|
|
219
277
|
}
|
|
220
|
-
OBJ_INFECT(result, element);
|
|
221
|
-
if (i > 0) rb_str_buf_append(result, delim);
|
|
222
|
-
rb_str_buf_append(result, shift);
|
|
223
|
-
element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
|
|
224
|
-
Check_Type(element, T_STRING);
|
|
225
|
-
rb_str_buf_append(result, element);
|
|
226
|
-
}
|
|
227
|
-
if (RSTRING_LEN(state->array_nl)) {
|
|
228
|
-
rb_str_buf_append(result, state->array_nl);
|
|
229
|
-
rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
|
|
230
278
|
}
|
|
231
|
-
|
|
232
|
-
|
|
279
|
+
fbuffer_append(buffer, ptr + start, end - start);
|
|
280
|
+
fbuffer_append(buffer, escape, escape_len);
|
|
281
|
+
start = ++end;
|
|
282
|
+
escape = NULL;
|
|
283
|
+
}
|
|
284
|
+
fbuffer_append(buffer, ptr + start, end - start);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
static char *fstrndup(const char *ptr, int len) {
|
|
288
|
+
char *result;
|
|
289
|
+
if (len <= 0) return NULL;
|
|
290
|
+
result = ALLOC_N(char, len);
|
|
291
|
+
memccpy(result, ptr, 0, len);
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* fbuffer implementation */
|
|
296
|
+
|
|
297
|
+
static FBuffer *fbuffer_alloc()
|
|
298
|
+
{
|
|
299
|
+
FBuffer *fb = ALLOC(FBuffer);
|
|
300
|
+
memset((void *) fb, 0, sizeof(FBuffer));
|
|
301
|
+
fb->initial_length = FBUFFER_INITIAL_LENGTH;
|
|
302
|
+
return fb;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static FBuffer *fbuffer_alloc_with_length(unsigned int initial_length)
|
|
306
|
+
{
|
|
307
|
+
FBuffer *fb;
|
|
308
|
+
assert(initial_length > 0);
|
|
309
|
+
fb = ALLOC(FBuffer);
|
|
310
|
+
memset((void *) fb, 0, sizeof(FBuffer));
|
|
311
|
+
fb->initial_length = initial_length;
|
|
312
|
+
return fb;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static void fbuffer_free(FBuffer *fb)
|
|
316
|
+
{
|
|
317
|
+
if (fb->ptr) ruby_xfree(fb->ptr);
|
|
318
|
+
ruby_xfree(fb);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static void fbuffer_free_only_buffer(FBuffer *fb)
|
|
322
|
+
{
|
|
323
|
+
ruby_xfree(fb);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
static void fbuffer_clear(FBuffer *fb)
|
|
327
|
+
{
|
|
328
|
+
fb->len = 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
static void fbuffer_inc_capa(FBuffer *fb, unsigned int requested)
|
|
332
|
+
{
|
|
333
|
+
unsigned int required;
|
|
334
|
+
|
|
335
|
+
if (!fb->ptr) {
|
|
336
|
+
fb->ptr = ALLOC_N(char, fb->initial_length);
|
|
337
|
+
fb->capa = fb->initial_length;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
for (required = fb->capa; requested > required - fb->len; required <<= 1);
|
|
341
|
+
|
|
342
|
+
if (required > fb->capa) {
|
|
343
|
+
REALLOC_N(fb->ptr, char, required);
|
|
344
|
+
fb->capa = required;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned int len)
|
|
349
|
+
{
|
|
350
|
+
if (len > 0) {
|
|
351
|
+
fbuffer_inc_capa(fb, len);
|
|
352
|
+
MEMCPY(fb->ptr + fb->len, newstr, char, len);
|
|
353
|
+
fb->len += len;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
static void fbuffer_append_char(FBuffer *fb, char newchr)
|
|
358
|
+
{
|
|
359
|
+
fbuffer_inc_capa(fb, 1);
|
|
360
|
+
*(fb->ptr + fb->len) = newchr;
|
|
361
|
+
fb->len++;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
static void freverse(char *start, char *end)
|
|
365
|
+
{
|
|
366
|
+
char c;
|
|
367
|
+
|
|
368
|
+
while (end > start) {
|
|
369
|
+
c = *end, *end-- = *start, *start++ = c;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
static int fltoa(long number, char *buf)
|
|
374
|
+
{
|
|
375
|
+
static char digits[] = "0123456789";
|
|
376
|
+
long sign = number;
|
|
377
|
+
char* tmp = buf;
|
|
378
|
+
|
|
379
|
+
if (sign < 0) number = -number;
|
|
380
|
+
do *tmp++ = digits[number % 10]; while (number /= 10);
|
|
381
|
+
if (sign < 0) *tmp++ = '-';
|
|
382
|
+
freverse(buf, tmp - 1);
|
|
383
|
+
return tmp - buf;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
static void fbuffer_append_long(FBuffer *fb, long number)
|
|
387
|
+
{
|
|
388
|
+
char buf[20];
|
|
389
|
+
int len = fltoa(number, buf);
|
|
390
|
+
fbuffer_append(fb, buf, len);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
static FBuffer *fbuffer_dup(FBuffer *fb)
|
|
394
|
+
{
|
|
395
|
+
int len = fb->len;
|
|
396
|
+
FBuffer *result;
|
|
397
|
+
|
|
398
|
+
if (len > 0) {
|
|
399
|
+
result = fbuffer_alloc_with_length(len);
|
|
400
|
+
fbuffer_append(result, FBUFFER_PAIR(fb));
|
|
233
401
|
} else {
|
|
234
|
-
result =
|
|
235
|
-
OBJ_INFECT(result, self);
|
|
236
|
-
if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
|
|
237
|
-
shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
|
|
238
|
-
|
|
239
|
-
rb_str_buf_cat2(result, "[");
|
|
240
|
-
rb_str_buf_append(result, state->array_nl);
|
|
241
|
-
for (i = 0; i < len; i++) {
|
|
242
|
-
VALUE element = RARRAY_PTR(self)[i];
|
|
243
|
-
OBJ_INFECT(result, element);
|
|
244
|
-
if (i > 0) rb_str_buf_append(result, delim);
|
|
245
|
-
rb_str_buf_append(result, shift);
|
|
246
|
-
element = rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1));
|
|
247
|
-
Check_Type(element, T_STRING);
|
|
248
|
-
rb_str_buf_append(result, element);
|
|
249
|
-
}
|
|
250
|
-
rb_str_buf_append(result, state->array_nl);
|
|
251
|
-
if (RSTRING_LEN(state->array_nl)) {
|
|
252
|
-
rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
|
|
253
|
-
}
|
|
254
|
-
rb_str_buf_cat2(result, "]");
|
|
402
|
+
result = fbuffer_alloc();
|
|
255
403
|
}
|
|
256
404
|
return result;
|
|
257
405
|
}
|
|
258
406
|
|
|
407
|
+
/*
|
|
408
|
+
* Document-module: JSON::Ext::Generator
|
|
409
|
+
*
|
|
410
|
+
* This is the JSON generator implemented as a C extension. It can be
|
|
411
|
+
* configured to be used by setting
|
|
412
|
+
*
|
|
413
|
+
* JSON.generator = JSON::Ext::Generator
|
|
414
|
+
*
|
|
415
|
+
* with the method generator= in JSON.
|
|
416
|
+
*
|
|
417
|
+
*/
|
|
418
|
+
|
|
419
|
+
/*
|
|
420
|
+
* call-seq: to_json(state = nil)
|
|
421
|
+
*
|
|
422
|
+
* Returns a JSON string containing a JSON object, that is generated from
|
|
423
|
+
* this Hash instance.
|
|
424
|
+
* _state_ is a JSON::State object, that can also be used to configure the
|
|
425
|
+
* produced JSON string output further.
|
|
426
|
+
*/
|
|
427
|
+
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
|
|
428
|
+
{
|
|
429
|
+
GENERATE_JSON(object);
|
|
430
|
+
}
|
|
431
|
+
|
|
259
432
|
/*
|
|
260
|
-
* call-seq: to_json(state = nil
|
|
433
|
+
* call-seq: to_json(state = nil)
|
|
261
434
|
*
|
|
262
|
-
* Returns a JSON string containing a JSON array, that is
|
|
435
|
+
* Returns a JSON string containing a JSON array, that is generated from
|
|
263
436
|
* this Array instance.
|
|
264
437
|
* _state_ is a JSON::State object, that can also be used to configure the
|
|
265
438
|
* produced JSON string output further.
|
|
266
|
-
* _depth_ is used to find out nesting depth, to indent accordingly.
|
|
267
439
|
*/
|
|
268
440
|
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
|
|
272
|
-
if (NIL_P(Vstate)) {
|
|
273
|
-
long i, len = RARRAY_LEN(self);
|
|
274
|
-
result = rb_str_buf_new(2 + 2 * len);
|
|
275
|
-
rb_str_buf_cat2(result, "[");
|
|
276
|
-
OBJ_INFECT(result, self);
|
|
277
|
-
for (i = 0; i < len; i++) {
|
|
278
|
-
VALUE element = RARRAY_PTR(self)[i];
|
|
279
|
-
OBJ_INFECT(result, element);
|
|
280
|
-
if (i > 0) rb_str_buf_cat2(result, ",");
|
|
281
|
-
element = rb_funcall(element, i_to_json, 0);
|
|
282
|
-
Check_Type(element, T_STRING);
|
|
283
|
-
rb_str_buf_append(result, element);
|
|
284
|
-
}
|
|
285
|
-
rb_str_buf_cat2(result, "]");
|
|
286
|
-
} else {
|
|
287
|
-
result = mArray_json_transfrom(self, Vstate, Vdepth);
|
|
288
|
-
}
|
|
289
|
-
OBJ_INFECT(result, self);
|
|
290
|
-
FORCE_UTF8(result);
|
|
291
|
-
return result;
|
|
441
|
+
GENERATE_JSON(array);
|
|
292
442
|
}
|
|
293
443
|
|
|
294
444
|
/*
|
|
@@ -296,11 +446,19 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
|
|
|
296
446
|
*
|
|
297
447
|
* Returns a JSON string representation for this Integer number.
|
|
298
448
|
*/
|
|
299
|
-
static VALUE
|
|
449
|
+
static VALUE mFixnum_to_json(int argc, VALUE *argv, VALUE self)
|
|
300
450
|
{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
451
|
+
GENERATE_JSON(fixnum);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/*
|
|
455
|
+
* call-seq: to_json(*)
|
|
456
|
+
*
|
|
457
|
+
* Returns a JSON string representation for this Integer number.
|
|
458
|
+
*/
|
|
459
|
+
static VALUE mBignum_to_json(int argc, VALUE *argv, VALUE self)
|
|
460
|
+
{
|
|
461
|
+
GENERATE_JSON(bignum);
|
|
304
462
|
}
|
|
305
463
|
|
|
306
464
|
/*
|
|
@@ -310,30 +468,7 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
310
468
|
*/
|
|
311
469
|
static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
312
470
|
{
|
|
313
|
-
|
|
314
|
-
VALUE Vstate, rest, tmp, result;
|
|
315
|
-
double value = RFLOAT_VALUE(self);
|
|
316
|
-
rb_scan_args(argc, argv, "01*", &Vstate, &rest);
|
|
317
|
-
if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
|
|
318
|
-
if (isinf(value)) {
|
|
319
|
-
if (state && state->allow_nan) {
|
|
320
|
-
result = rb_funcall(self, i_to_s, 0);
|
|
321
|
-
} else {
|
|
322
|
-
tmp = rb_funcall(self, i_to_s, 0);
|
|
323
|
-
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
|
324
|
-
}
|
|
325
|
-
} else if (isnan(value)) {
|
|
326
|
-
if (state && state->allow_nan) {
|
|
327
|
-
result = rb_funcall(self, i_to_s, 0);
|
|
328
|
-
} else {
|
|
329
|
-
tmp = rb_funcall(self, i_to_s, 0);
|
|
330
|
-
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
|
331
|
-
}
|
|
332
|
-
} else {
|
|
333
|
-
result = rb_funcall(self, i_to_s, 0);
|
|
334
|
-
}
|
|
335
|
-
FORCE_UTF8(result);
|
|
336
|
-
return result;
|
|
471
|
+
GENERATE_JSON(float);
|
|
337
472
|
}
|
|
338
473
|
|
|
339
474
|
/*
|
|
@@ -343,7 +478,6 @@ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
343
478
|
*/
|
|
344
479
|
static VALUE mString_included_s(VALUE self, VALUE modul) {
|
|
345
480
|
VALUE result = rb_funcall(modul, i_extend, 1, mString_Extend);
|
|
346
|
-
FORCE_UTF8(result);
|
|
347
481
|
return result;
|
|
348
482
|
}
|
|
349
483
|
|
|
@@ -356,38 +490,24 @@ static VALUE mString_included_s(VALUE self, VALUE modul) {
|
|
|
356
490
|
*/
|
|
357
491
|
static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
|
|
358
492
|
{
|
|
359
|
-
|
|
360
|
-
rb_str_buf_cat2(result, "\"");
|
|
361
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
|
362
|
-
if (rb_funcall(self, i_encoding, 0) == mEncoding_UTF_8) {
|
|
363
|
-
JSON_convert_UTF8_to_JSON(result, self, strictConversion);
|
|
364
|
-
} else {
|
|
365
|
-
VALUE string = rb_funcall(self, i_encode, 1, mEncoding_UTF_8);
|
|
366
|
-
JSON_convert_UTF8_to_JSON(result, string, strictConversion);
|
|
367
|
-
}
|
|
368
|
-
#else
|
|
369
|
-
JSON_convert_UTF8_to_JSON(result, self, strictConversion);
|
|
370
|
-
#endif
|
|
371
|
-
rb_str_buf_cat2(result, "\"");
|
|
372
|
-
FORCE_UTF8(result);
|
|
373
|
-
return result;
|
|
493
|
+
GENERATE_JSON(string);
|
|
374
494
|
}
|
|
375
495
|
|
|
376
496
|
/*
|
|
377
497
|
* call-seq: to_json_raw_object()
|
|
378
498
|
*
|
|
379
499
|
* This method creates a raw object hash, that can be nested into
|
|
380
|
-
* other data structures and will be
|
|
500
|
+
* other data structures and will be generated as a raw string. This
|
|
381
501
|
* method should be used, if you want to convert raw strings to JSON
|
|
382
502
|
* instead of UTF-8 strings, e. g. binary data.
|
|
383
503
|
*/
|
|
384
|
-
static VALUE mString_to_json_raw_object(VALUE self)
|
|
504
|
+
static VALUE mString_to_json_raw_object(VALUE self)
|
|
505
|
+
{
|
|
385
506
|
VALUE ary;
|
|
386
507
|
VALUE result = rb_hash_new();
|
|
387
508
|
rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
|
|
388
509
|
ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
|
|
389
510
|
rb_hash_aset(result, rb_str_new2("raw"), ary);
|
|
390
|
-
FORCE_UTF8(result);
|
|
391
511
|
return result;
|
|
392
512
|
}
|
|
393
513
|
|
|
@@ -397,12 +517,11 @@ static VALUE mString_to_json_raw_object(VALUE self) {
|
|
|
397
517
|
* This method creates a JSON text from the result of a call to
|
|
398
518
|
* to_json_raw_object of this String.
|
|
399
519
|
*/
|
|
400
|
-
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
|
|
401
|
-
|
|
520
|
+
static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self)
|
|
521
|
+
{
|
|
522
|
+
VALUE obj = mString_to_json_raw_object(self);
|
|
402
523
|
Check_Type(obj, T_HASH);
|
|
403
|
-
|
|
404
|
-
FORCE_UTF8(result);
|
|
405
|
-
return result;
|
|
524
|
+
return mHash_to_json(argc, argv, obj);
|
|
406
525
|
}
|
|
407
526
|
|
|
408
527
|
/*
|
|
@@ -411,7 +530,8 @@ static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
|
|
|
411
530
|
* Raw Strings are JSON Objects (the raw bytes are stored in an array for the
|
|
412
531
|
* key "raw"). The Ruby String can be created by this module method.
|
|
413
532
|
*/
|
|
414
|
-
static VALUE mString_Extend_json_create(VALUE self, VALUE o)
|
|
533
|
+
static VALUE mString_Extend_json_create(VALUE self, VALUE o)
|
|
534
|
+
{
|
|
415
535
|
VALUE ary;
|
|
416
536
|
Check_Type(o, T_HASH);
|
|
417
537
|
ary = rb_hash_aref(o, rb_str_new2("raw"));
|
|
@@ -425,9 +545,7 @@ static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
|
|
|
425
545
|
*/
|
|
426
546
|
static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
427
547
|
{
|
|
428
|
-
|
|
429
|
-
FORCE_UTF8(result);
|
|
430
|
-
return result;
|
|
548
|
+
GENERATE_JSON(true);
|
|
431
549
|
}
|
|
432
550
|
|
|
433
551
|
/*
|
|
@@ -437,9 +555,7 @@ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
437
555
|
*/
|
|
438
556
|
static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
439
557
|
{
|
|
440
|
-
|
|
441
|
-
FORCE_UTF8(result);
|
|
442
|
-
return result;
|
|
558
|
+
GENERATE_JSON(false);
|
|
443
559
|
}
|
|
444
560
|
|
|
445
561
|
/*
|
|
@@ -448,9 +564,7 @@ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
448
564
|
*/
|
|
449
565
|
static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
450
566
|
{
|
|
451
|
-
|
|
452
|
-
FORCE_UTF8(result);
|
|
453
|
-
return result;
|
|
567
|
+
GENERATE_JSON(null);
|
|
454
568
|
}
|
|
455
569
|
|
|
456
570
|
/*
|
|
@@ -462,42 +576,38 @@ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
|
|
|
462
576
|
*/
|
|
463
577
|
static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
|
|
464
578
|
{
|
|
465
|
-
VALUE
|
|
579
|
+
VALUE state;
|
|
580
|
+
VALUE string = rb_funcall(self, i_to_s, 0);
|
|
581
|
+
rb_scan_args(argc, argv, "01", &state);
|
|
466
582
|
Check_Type(string, T_STRING);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return result;
|
|
583
|
+
state = cState_from_state_s(cState, state);
|
|
584
|
+
return cState_partial_generate(state, string);
|
|
470
585
|
}
|
|
471
586
|
|
|
472
|
-
|
|
473
|
-
* Document-class: JSON::Ext::Generator::State
|
|
474
|
-
*
|
|
475
|
-
* This class is used to create State instances, that are use to hold data
|
|
476
|
-
* while generating a JSON text from a a Ruby data structure.
|
|
477
|
-
*/
|
|
478
|
-
|
|
479
|
-
static void State_mark(JSON_Generator_State *state)
|
|
587
|
+
static void State_free(JSON_Generator_State *state)
|
|
480
588
|
{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
589
|
+
if (state->indent) ruby_xfree(state->indent);
|
|
590
|
+
if (state->space) ruby_xfree(state->space);
|
|
591
|
+
if (state->space_before) ruby_xfree(state->space_before);
|
|
592
|
+
if (state->object_nl) ruby_xfree(state->object_nl);
|
|
593
|
+
if (state->array_nl) ruby_xfree(state->array_nl);
|
|
594
|
+
if (state->array_delim) fbuffer_free(state->array_delim);
|
|
595
|
+
if (state->object_delim) fbuffer_free(state->object_delim);
|
|
596
|
+
if (state->object_delim2) fbuffer_free(state->object_delim2);
|
|
597
|
+
ruby_xfree(state);
|
|
489
598
|
}
|
|
490
599
|
|
|
491
600
|
static JSON_Generator_State *State_allocate()
|
|
492
601
|
{
|
|
493
602
|
JSON_Generator_State *state = ALLOC(JSON_Generator_State);
|
|
603
|
+
MEMZERO(state, JSON_Generator_State, 1);
|
|
494
604
|
return state;
|
|
495
605
|
}
|
|
496
606
|
|
|
497
607
|
static VALUE cState_s_allocate(VALUE klass)
|
|
498
608
|
{
|
|
499
609
|
JSON_Generator_State *state = State_allocate();
|
|
500
|
-
return Data_Wrap_Struct(klass,
|
|
610
|
+
return Data_Wrap_Struct(klass, NULL, State_free, state);
|
|
501
611
|
}
|
|
502
612
|
|
|
503
613
|
/*
|
|
@@ -518,35 +628,43 @@ static VALUE cState_configure(VALUE self, VALUE opts)
|
|
|
518
628
|
opts = tmp;
|
|
519
629
|
tmp = rb_hash_aref(opts, ID2SYM(i_indent));
|
|
520
630
|
if (RTEST(tmp)) {
|
|
631
|
+
int len;
|
|
521
632
|
Check_Type(tmp, T_STRING);
|
|
522
|
-
|
|
633
|
+
len = RSTRING_LEN(tmp);
|
|
634
|
+
state->indent = fstrndup(RSTRING_PTR(tmp), len);
|
|
635
|
+
state->indent_len = len;
|
|
523
636
|
}
|
|
524
637
|
tmp = rb_hash_aref(opts, ID2SYM(i_space));
|
|
525
638
|
if (RTEST(tmp)) {
|
|
639
|
+
int len;
|
|
526
640
|
Check_Type(tmp, T_STRING);
|
|
527
|
-
|
|
641
|
+
len = RSTRING_LEN(tmp);
|
|
642
|
+
state->space = fstrndup(RSTRING_PTR(tmp), len);
|
|
643
|
+
state->space_len = len;
|
|
528
644
|
}
|
|
529
645
|
tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
|
|
530
646
|
if (RTEST(tmp)) {
|
|
647
|
+
int len;
|
|
531
648
|
Check_Type(tmp, T_STRING);
|
|
532
|
-
|
|
649
|
+
len = RSTRING_LEN(tmp);
|
|
650
|
+
state->space_before = fstrndup(RSTRING_PTR(tmp), len);
|
|
651
|
+
state->space_before_len = len;
|
|
533
652
|
}
|
|
534
653
|
tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
|
|
535
654
|
if (RTEST(tmp)) {
|
|
655
|
+
int len;
|
|
536
656
|
Check_Type(tmp, T_STRING);
|
|
537
|
-
|
|
657
|
+
len = RSTRING_LEN(tmp);
|
|
658
|
+
state->array_nl = fstrndup(RSTRING_PTR(tmp), len);
|
|
659
|
+
state->array_nl_len = len;
|
|
538
660
|
}
|
|
539
661
|
tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
|
|
540
662
|
if (RTEST(tmp)) {
|
|
663
|
+
int len;
|
|
541
664
|
Check_Type(tmp, T_STRING);
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
if (option_given_p(opts, tmp)) {
|
|
546
|
-
tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
|
|
547
|
-
state->check_circular = RTEST(tmp);
|
|
548
|
-
} else {
|
|
549
|
-
state->check_circular = 1;
|
|
665
|
+
len = RSTRING_LEN(tmp);
|
|
666
|
+
state->object_nl = fstrndup(RSTRING_PTR(tmp), len);
|
|
667
|
+
state->object_nl_len = len;
|
|
550
668
|
}
|
|
551
669
|
tmp = ID2SYM(i_max_nesting);
|
|
552
670
|
state->max_nesting = 19;
|
|
@@ -559,8 +677,21 @@ static VALUE cState_configure(VALUE self, VALUE opts)
|
|
|
559
677
|
state->max_nesting = 0;
|
|
560
678
|
}
|
|
561
679
|
}
|
|
680
|
+
tmp = ID2SYM(i_depth);
|
|
681
|
+
state->depth = 0;
|
|
682
|
+
if (option_given_p(opts, tmp)) {
|
|
683
|
+
VALUE depth = rb_hash_aref(opts, tmp);
|
|
684
|
+
if (RTEST(depth)) {
|
|
685
|
+
Check_Type(depth, T_FIXNUM);
|
|
686
|
+
state->depth = FIX2LONG(depth);
|
|
687
|
+
} else {
|
|
688
|
+
state->depth = 0;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
562
691
|
tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
|
|
563
692
|
state->allow_nan = RTEST(tmp);
|
|
693
|
+
tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
|
|
694
|
+
state->ascii_only = RTEST(tmp);
|
|
564
695
|
return self;
|
|
565
696
|
}
|
|
566
697
|
|
|
@@ -574,22 +705,23 @@ static VALUE cState_to_h(VALUE self)
|
|
|
574
705
|
{
|
|
575
706
|
VALUE result = rb_hash_new();
|
|
576
707
|
GET_STATE(self);
|
|
577
|
-
rb_hash_aset(result, ID2SYM(i_indent), state->indent);
|
|
578
|
-
rb_hash_aset(result, ID2SYM(i_space), state->space);
|
|
579
|
-
rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
|
|
580
|
-
rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
|
|
581
|
-
rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
|
|
582
|
-
rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
|
|
708
|
+
rb_hash_aset(result, ID2SYM(i_indent), rb_str_new(state->indent, state->indent_len));
|
|
709
|
+
rb_hash_aset(result, ID2SYM(i_space), rb_str_new(state->space, state->space_len));
|
|
710
|
+
rb_hash_aset(result, ID2SYM(i_space_before), rb_str_new(state->space_before, state->space_before_len));
|
|
711
|
+
rb_hash_aset(result, ID2SYM(i_object_nl), rb_str_new(state->object_nl, state->object_nl_len));
|
|
712
|
+
rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len));
|
|
583
713
|
rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
|
|
714
|
+
rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
|
|
584
715
|
rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
|
|
716
|
+
rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
|
|
585
717
|
return result;
|
|
586
718
|
}
|
|
587
719
|
|
|
588
720
|
/*
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
721
|
+
* call-seq: [](name)
|
|
722
|
+
*
|
|
723
|
+
* Return the value returned by method +name+.
|
|
724
|
+
*/
|
|
593
725
|
static VALUE cState_aref(VALUE self, VALUE name)
|
|
594
726
|
{
|
|
595
727
|
GET_STATE(self);
|
|
@@ -600,6 +732,248 @@ static VALUE cState_aref(VALUE self, VALUE name)
|
|
|
600
732
|
}
|
|
601
733
|
}
|
|
602
734
|
|
|
735
|
+
static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
736
|
+
{
|
|
737
|
+
char *object_nl = state->object_nl;
|
|
738
|
+
long object_nl_len = state->object_nl_len;
|
|
739
|
+
char *indent = state->indent;
|
|
740
|
+
long indent_len = state->indent_len;
|
|
741
|
+
long max_nesting = state->max_nesting;
|
|
742
|
+
char *delim = FBUFFER_PTR(state->object_delim);
|
|
743
|
+
long delim_len = FBUFFER_LEN(state->object_delim);
|
|
744
|
+
char *delim2 = FBUFFER_PTR(state->object_delim2);
|
|
745
|
+
long delim2_len = FBUFFER_LEN(state->object_delim2);
|
|
746
|
+
long depth = ++state->depth;
|
|
747
|
+
int i, j;
|
|
748
|
+
VALUE key, key_to_s, keys;
|
|
749
|
+
if (max_nesting != 0 && depth > max_nesting) {
|
|
750
|
+
fbuffer_free(buffer);
|
|
751
|
+
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
|
752
|
+
}
|
|
753
|
+
fbuffer_append_char(buffer, '{');
|
|
754
|
+
keys = rb_funcall(obj, i_keys, 0);
|
|
755
|
+
for(i = 0; i < RARRAY_LEN(keys); i++) {
|
|
756
|
+
if (i > 0) fbuffer_append(buffer, delim, delim_len);
|
|
757
|
+
if (object_nl) {
|
|
758
|
+
fbuffer_append(buffer, object_nl, object_nl_len);
|
|
759
|
+
}
|
|
760
|
+
if (indent) {
|
|
761
|
+
for (j = 0; j < depth; j++) {
|
|
762
|
+
fbuffer_append(buffer, indent, indent_len);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
key = rb_ary_entry(keys, i);
|
|
766
|
+
key_to_s = rb_funcall(key, i_to_s, 0);
|
|
767
|
+
Check_Type(key_to_s, T_STRING);
|
|
768
|
+
generate_json(buffer, Vstate, state, key_to_s);
|
|
769
|
+
fbuffer_append(buffer, delim2, delim2_len);
|
|
770
|
+
generate_json(buffer, Vstate, state, rb_hash_aref(obj, key));
|
|
771
|
+
}
|
|
772
|
+
depth = --state->depth;
|
|
773
|
+
if (object_nl) {
|
|
774
|
+
fbuffer_append(buffer, object_nl, object_nl_len);
|
|
775
|
+
if (indent) {
|
|
776
|
+
for (j = 0; j < depth; j++) {
|
|
777
|
+
fbuffer_append(buffer, indent, indent_len);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
fbuffer_append_char(buffer, '}');
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
785
|
+
{
|
|
786
|
+
char *array_nl = state->array_nl;
|
|
787
|
+
long array_nl_len = state->array_nl_len;
|
|
788
|
+
char *indent = state->indent;
|
|
789
|
+
long indent_len = state->indent_len;
|
|
790
|
+
long max_nesting = state->max_nesting;
|
|
791
|
+
char *delim = FBUFFER_PTR(state->array_delim);
|
|
792
|
+
long delim_len = FBUFFER_LEN(state->array_delim);
|
|
793
|
+
long depth = ++state->depth;
|
|
794
|
+
int i, j;
|
|
795
|
+
if (max_nesting != 0 && depth > max_nesting) {
|
|
796
|
+
fbuffer_free(buffer);
|
|
797
|
+
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
|
798
|
+
}
|
|
799
|
+
fbuffer_append_char(buffer, '[');
|
|
800
|
+
if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len);
|
|
801
|
+
for(i = 0; i < RARRAY_LEN(obj); i++) {
|
|
802
|
+
if (i > 0) fbuffer_append(buffer, delim, delim_len);
|
|
803
|
+
if (indent) {
|
|
804
|
+
for (j = 0; j < depth; j++) {
|
|
805
|
+
fbuffer_append(buffer, indent, indent_len);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
generate_json(buffer, Vstate, state, rb_ary_entry(obj, i));
|
|
809
|
+
}
|
|
810
|
+
state->depth = --depth;
|
|
811
|
+
if (array_nl) {
|
|
812
|
+
fbuffer_append(buffer, array_nl, array_nl_len);
|
|
813
|
+
if (indent) {
|
|
814
|
+
for (j = 0; j < depth; j++) {
|
|
815
|
+
fbuffer_append(buffer, indent, indent_len);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
fbuffer_append_char(buffer, ']');
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
823
|
+
{
|
|
824
|
+
fbuffer_append_char(buffer, '"');
|
|
825
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
|
826
|
+
obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8);
|
|
827
|
+
#endif
|
|
828
|
+
if (state->ascii_only) {
|
|
829
|
+
convert_UTF8_to_JSON_ASCII(buffer, obj);
|
|
830
|
+
} else {
|
|
831
|
+
convert_UTF8_to_JSON(buffer, obj);
|
|
832
|
+
}
|
|
833
|
+
fbuffer_append_char(buffer, '"');
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
static void generate_json_null(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
837
|
+
{
|
|
838
|
+
fbuffer_append(buffer, "null", 4);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
static void generate_json_false(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
842
|
+
{
|
|
843
|
+
fbuffer_append(buffer, "false", 5);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
static void generate_json_true(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
847
|
+
{
|
|
848
|
+
fbuffer_append(buffer, "true", 4);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
static void generate_json_fixnum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
852
|
+
{
|
|
853
|
+
fbuffer_append_long(buffer, FIX2LONG(obj));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
static void generate_json_bignum(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
857
|
+
{
|
|
858
|
+
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
859
|
+
fbuffer_append(buffer, RSTRING_PAIR(tmp));
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
863
|
+
{
|
|
864
|
+
double value = RFLOAT_VALUE(obj);
|
|
865
|
+
char allow_nan = state->allow_nan;
|
|
866
|
+
VALUE tmp = rb_funcall(obj, i_to_s, 0);
|
|
867
|
+
if (!allow_nan) {
|
|
868
|
+
if (isinf(value)) {
|
|
869
|
+
fbuffer_free(buffer);
|
|
870
|
+
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
|
871
|
+
} else if (isnan(value)) {
|
|
872
|
+
fbuffer_free(buffer);
|
|
873
|
+
rb_raise(eGeneratorError, "%u: %s not allowed in JSON", __LINE__, StringValueCStr(tmp));
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
fbuffer_append(buffer, RSTRING_PAIR(tmp));
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
880
|
+
{
|
|
881
|
+
VALUE tmp;
|
|
882
|
+
VALUE klass = CLASS_OF(obj);
|
|
883
|
+
if (klass == rb_cHash) {
|
|
884
|
+
generate_json_object(buffer, Vstate, state, obj);
|
|
885
|
+
} else if (klass == rb_cArray) {
|
|
886
|
+
generate_json_array(buffer, Vstate, state, obj);
|
|
887
|
+
} else if (klass == rb_cString) {
|
|
888
|
+
generate_json_string(buffer, Vstate, state, obj);
|
|
889
|
+
} else if (obj == Qnil) {
|
|
890
|
+
generate_json_null(buffer, Vstate, state, obj);
|
|
891
|
+
} else if (obj == Qfalse) {
|
|
892
|
+
generate_json_false(buffer, Vstate, state, obj);
|
|
893
|
+
} else if (obj == Qtrue) {
|
|
894
|
+
generate_json_true(buffer, Vstate, state, obj);
|
|
895
|
+
} else if (klass == rb_cFixnum) {
|
|
896
|
+
generate_json_fixnum(buffer, Vstate, state, obj);
|
|
897
|
+
} else if (klass == rb_cBignum) {
|
|
898
|
+
generate_json_bignum(buffer, Vstate, state, obj);
|
|
899
|
+
} else if (klass == rb_cFloat) {
|
|
900
|
+
generate_json_float(buffer, Vstate, state, obj);
|
|
901
|
+
} else if (rb_respond_to(obj, i_to_json)) {
|
|
902
|
+
tmp = rb_funcall(obj, i_to_json, 1, Vstate);
|
|
903
|
+
Check_Type(tmp, T_STRING);
|
|
904
|
+
fbuffer_append(buffer, RSTRING_PAIR(tmp));
|
|
905
|
+
} else {
|
|
906
|
+
tmp = rb_funcall(obj, i_to_s, 0);
|
|
907
|
+
Check_Type(tmp, T_STRING);
|
|
908
|
+
generate_json(buffer, Vstate, state, tmp);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
static FBuffer *cState_prepare_buffer(VALUE self)
|
|
913
|
+
{
|
|
914
|
+
FBuffer *buffer = fbuffer_alloc();
|
|
915
|
+
GET_STATE(self);
|
|
916
|
+
|
|
917
|
+
if (state->object_delim) {
|
|
918
|
+
fbuffer_clear(state->object_delim);
|
|
919
|
+
} else {
|
|
920
|
+
state->object_delim = fbuffer_alloc_with_length(16);
|
|
921
|
+
}
|
|
922
|
+
fbuffer_append_char(state->object_delim, ',');
|
|
923
|
+
if (state->object_delim2) {
|
|
924
|
+
fbuffer_clear(state->object_delim2);
|
|
925
|
+
} else {
|
|
926
|
+
state->object_delim2 = fbuffer_alloc_with_length(16);
|
|
927
|
+
}
|
|
928
|
+
fbuffer_append_char(state->object_delim2, ':');
|
|
929
|
+
if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len);
|
|
930
|
+
|
|
931
|
+
if (state->array_delim) {
|
|
932
|
+
fbuffer_clear(state->array_delim);
|
|
933
|
+
} else {
|
|
934
|
+
state->array_delim = fbuffer_alloc_with_length(16);
|
|
935
|
+
}
|
|
936
|
+
fbuffer_append_char(state->array_delim, ',');
|
|
937
|
+
if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len);
|
|
938
|
+
return buffer;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
static VALUE fbuffer_to_s(FBuffer *fb)
|
|
942
|
+
{
|
|
943
|
+
VALUE result = rb_str_new(FBUFFER_PAIR(fb));
|
|
944
|
+
fbuffer_free(fb);
|
|
945
|
+
FORCE_UTF8(result);
|
|
946
|
+
return result;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
static VALUE cState_partial_generate(VALUE self, VALUE obj)
|
|
950
|
+
{
|
|
951
|
+
FBuffer *buffer = cState_prepare_buffer(self);
|
|
952
|
+
GET_STATE(self);
|
|
953
|
+
generate_json(buffer, self, state, obj);
|
|
954
|
+
return fbuffer_to_s(buffer);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/*
|
|
958
|
+
* call-seq: generate(obj)
|
|
959
|
+
*
|
|
960
|
+
* Generates a valid JSON document from object +obj+ and returns the
|
|
961
|
+
* result. If no valid JSON document can be created this method raises a
|
|
962
|
+
* GeneratorError exception.
|
|
963
|
+
*/
|
|
964
|
+
static VALUE cState_generate(VALUE self, VALUE obj)
|
|
965
|
+
{
|
|
966
|
+
VALUE result = cState_partial_generate(self, obj);
|
|
967
|
+
VALUE re, args[2];
|
|
968
|
+
args[0] = rb_str_new2("\\A\\s*(?:\\[.*\\]|\\{.*\\})\\s*\\Z");
|
|
969
|
+
args[1] = CRegexp_MULTILINE;
|
|
970
|
+
re = rb_class_new_instance(2, args, rb_cRegexp);
|
|
971
|
+
if (NIL_P(rb_funcall(re, i_match, 1, result))) {
|
|
972
|
+
rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed");
|
|
973
|
+
}
|
|
974
|
+
return result;
|
|
975
|
+
}
|
|
976
|
+
|
|
603
977
|
/*
|
|
604
978
|
* call-seq: new(opts = {})
|
|
605
979
|
*
|
|
@@ -612,8 +986,6 @@ static VALUE cState_aref(VALUE self, VALUE name)
|
|
|
612
986
|
* * *space_before*: a string that is put before a : pair delimiter (default: ''),
|
|
613
987
|
* * *object_nl*: a string that is put at the end of a JSON object (default: ''),
|
|
614
988
|
* * *array_nl*: a string that is put at the end of a JSON array (default: ''),
|
|
615
|
-
* * *check_circular*: true if checking for circular data structures
|
|
616
|
-
* should be done, false (the default) otherwise.
|
|
617
989
|
* * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
|
618
990
|
* generated, otherwise an exception is thrown, if these values are
|
|
619
991
|
* encountered. This options defaults to false.
|
|
@@ -622,26 +994,38 @@ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
|
|
|
622
994
|
{
|
|
623
995
|
VALUE opts;
|
|
624
996
|
GET_STATE(self);
|
|
625
|
-
|
|
997
|
+
state->max_nesting = 19;
|
|
626
998
|
rb_scan_args(argc, argv, "01", &opts);
|
|
627
|
-
|
|
628
|
-
state->space = rb_str_new2("");
|
|
629
|
-
state->space_before = rb_str_new2("");
|
|
630
|
-
state->array_nl = rb_str_new2("");
|
|
631
|
-
state->object_nl = rb_str_new2("");
|
|
632
|
-
if (NIL_P(opts)) {
|
|
633
|
-
state->check_circular = 1;
|
|
634
|
-
state->allow_nan = 0;
|
|
635
|
-
state->max_nesting = 19;
|
|
636
|
-
} else {
|
|
637
|
-
cState_configure(self, opts);
|
|
638
|
-
}
|
|
639
|
-
state->seen = rb_hash_new();
|
|
640
|
-
state->memo = Qnil;
|
|
641
|
-
state->depth = INT2FIX(0);
|
|
999
|
+
if (!NIL_P(opts)) cState_configure(self, opts);
|
|
642
1000
|
return self;
|
|
643
1001
|
}
|
|
644
1002
|
|
|
1003
|
+
/*
|
|
1004
|
+
* call-seq: initialize_copy(orig)
|
|
1005
|
+
*
|
|
1006
|
+
* Initializes this object from orig if it to be duplicated/cloned and returns
|
|
1007
|
+
* it.
|
|
1008
|
+
*/
|
|
1009
|
+
static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
1010
|
+
{
|
|
1011
|
+
JSON_Generator_State *objState, *origState;
|
|
1012
|
+
|
|
1013
|
+
Data_Get_Struct(obj, JSON_Generator_State, objState);
|
|
1014
|
+
Data_Get_Struct(orig, JSON_Generator_State, origState);
|
|
1015
|
+
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
|
|
1016
|
+
|
|
1017
|
+
MEMCPY(objState, origState, JSON_Generator_State, 1);
|
|
1018
|
+
objState->indent = fstrndup(origState->indent, origState->indent_len);
|
|
1019
|
+
objState->space = fstrndup(origState->space, origState->space_len);
|
|
1020
|
+
objState->space_before = fstrndup(origState->space_before, origState->space_before_len);
|
|
1021
|
+
objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len);
|
|
1022
|
+
objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len);
|
|
1023
|
+
if (origState->array_delim) objState->array_delim = fbuffer_dup(origState->array_delim);
|
|
1024
|
+
if (origState->object_delim) objState->object_delim = fbuffer_dup(origState->object_delim);
|
|
1025
|
+
if (origState->object_delim2) objState->object_delim2 = fbuffer_dup(origState->object_delim2);
|
|
1026
|
+
return obj;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
645
1029
|
/*
|
|
646
1030
|
* call-seq: from_state(opts)
|
|
647
1031
|
*
|
|
@@ -656,7 +1040,10 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
|
|
|
656
1040
|
} else if (rb_obj_is_kind_of(opts, rb_cHash)) {
|
|
657
1041
|
return rb_funcall(self, i_new, 1, opts);
|
|
658
1042
|
} else {
|
|
659
|
-
|
|
1043
|
+
if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) {
|
|
1044
|
+
CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE);
|
|
1045
|
+
}
|
|
1046
|
+
return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0);
|
|
660
1047
|
}
|
|
661
1048
|
}
|
|
662
1049
|
|
|
@@ -668,7 +1055,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
|
|
|
668
1055
|
static VALUE cState_indent(VALUE self)
|
|
669
1056
|
{
|
|
670
1057
|
GET_STATE(self);
|
|
671
|
-
return state->indent;
|
|
1058
|
+
return state->indent ? rb_str_new2(state->indent) : rb_str_new2("");
|
|
672
1059
|
}
|
|
673
1060
|
|
|
674
1061
|
/*
|
|
@@ -678,9 +1065,22 @@ static VALUE cState_indent(VALUE self)
|
|
|
678
1065
|
*/
|
|
679
1066
|
static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
680
1067
|
{
|
|
1068
|
+
int len;
|
|
681
1069
|
GET_STATE(self);
|
|
682
1070
|
Check_Type(indent, T_STRING);
|
|
683
|
-
|
|
1071
|
+
len = RSTRING_LEN(indent);
|
|
1072
|
+
if (len == 0) {
|
|
1073
|
+
if (state->indent) {
|
|
1074
|
+
ruby_xfree(state->indent);
|
|
1075
|
+
state->indent = NULL;
|
|
1076
|
+
state->indent_len = 0;
|
|
1077
|
+
}
|
|
1078
|
+
} else {
|
|
1079
|
+
if (state->indent) ruby_xfree(state->indent);
|
|
1080
|
+
state->indent = strdup(RSTRING_PTR(indent));
|
|
1081
|
+
state->indent_len = len;
|
|
1082
|
+
}
|
|
1083
|
+
return Qnil;
|
|
684
1084
|
}
|
|
685
1085
|
|
|
686
1086
|
/*
|
|
@@ -692,7 +1092,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
|
692
1092
|
static VALUE cState_space(VALUE self)
|
|
693
1093
|
{
|
|
694
1094
|
GET_STATE(self);
|
|
695
|
-
return state->space;
|
|
1095
|
+
return state->space ? rb_str_new2(state->space) : rb_str_new2("");
|
|
696
1096
|
}
|
|
697
1097
|
|
|
698
1098
|
/*
|
|
@@ -703,9 +1103,22 @@ static VALUE cState_space(VALUE self)
|
|
|
703
1103
|
*/
|
|
704
1104
|
static VALUE cState_space_set(VALUE self, VALUE space)
|
|
705
1105
|
{
|
|
1106
|
+
int len;
|
|
706
1107
|
GET_STATE(self);
|
|
707
1108
|
Check_Type(space, T_STRING);
|
|
708
|
-
|
|
1109
|
+
len = RSTRING_LEN(space);
|
|
1110
|
+
if (len == 0) {
|
|
1111
|
+
if (state->space) {
|
|
1112
|
+
ruby_xfree(state->space);
|
|
1113
|
+
state->space = NULL;
|
|
1114
|
+
state->space_len = 0;
|
|
1115
|
+
}
|
|
1116
|
+
} else {
|
|
1117
|
+
if (state->space) ruby_xfree(state->space);
|
|
1118
|
+
state->space = strdup(RSTRING_PTR(space));
|
|
1119
|
+
state->space_len = len;
|
|
1120
|
+
}
|
|
1121
|
+
return Qnil;
|
|
709
1122
|
}
|
|
710
1123
|
|
|
711
1124
|
/*
|
|
@@ -716,7 +1129,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
|
|
|
716
1129
|
static VALUE cState_space_before(VALUE self)
|
|
717
1130
|
{
|
|
718
1131
|
GET_STATE(self);
|
|
719
|
-
return state->space_before;
|
|
1132
|
+
return state->space_before ? rb_str_new2(state->space_before) : rb_str_new2("");
|
|
720
1133
|
}
|
|
721
1134
|
|
|
722
1135
|
/*
|
|
@@ -726,9 +1139,22 @@ static VALUE cState_space_before(VALUE self)
|
|
|
726
1139
|
*/
|
|
727
1140
|
static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
728
1141
|
{
|
|
1142
|
+
int len;
|
|
729
1143
|
GET_STATE(self);
|
|
730
1144
|
Check_Type(space_before, T_STRING);
|
|
731
|
-
|
|
1145
|
+
len = RSTRING_LEN(space_before);
|
|
1146
|
+
if (len == 0) {
|
|
1147
|
+
if (state->space_before) {
|
|
1148
|
+
ruby_xfree(state->space_before);
|
|
1149
|
+
state->space_before = NULL;
|
|
1150
|
+
state->space_before_len = 0;
|
|
1151
|
+
}
|
|
1152
|
+
} else {
|
|
1153
|
+
if (state->space_before) ruby_xfree(state->space_before);
|
|
1154
|
+
state->space_before = strdup(RSTRING_PTR(space_before));
|
|
1155
|
+
state->space_before_len = len;
|
|
1156
|
+
}
|
|
1157
|
+
return Qnil;
|
|
732
1158
|
}
|
|
733
1159
|
|
|
734
1160
|
/*
|
|
@@ -740,7 +1166,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
|
740
1166
|
static VALUE cState_object_nl(VALUE self)
|
|
741
1167
|
{
|
|
742
1168
|
GET_STATE(self);
|
|
743
|
-
return state->object_nl;
|
|
1169
|
+
return state->object_nl ? rb_str_new2(state->object_nl) : rb_str_new2("");
|
|
744
1170
|
}
|
|
745
1171
|
|
|
746
1172
|
/*
|
|
@@ -751,9 +1177,21 @@ static VALUE cState_object_nl(VALUE self)
|
|
|
751
1177
|
*/
|
|
752
1178
|
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
753
1179
|
{
|
|
1180
|
+
int len;
|
|
754
1181
|
GET_STATE(self);
|
|
755
1182
|
Check_Type(object_nl, T_STRING);
|
|
756
|
-
|
|
1183
|
+
len = RSTRING_LEN(object_nl);
|
|
1184
|
+
if (len == 0) {
|
|
1185
|
+
if (state->object_nl) {
|
|
1186
|
+
ruby_xfree(state->object_nl);
|
|
1187
|
+
state->object_nl = NULL;
|
|
1188
|
+
}
|
|
1189
|
+
} else {
|
|
1190
|
+
if (state->object_nl) ruby_xfree(state->object_nl);
|
|
1191
|
+
state->object_nl = strdup(RSTRING_PTR(object_nl));
|
|
1192
|
+
state->object_nl_len = len;
|
|
1193
|
+
}
|
|
1194
|
+
return Qnil;
|
|
757
1195
|
}
|
|
758
1196
|
|
|
759
1197
|
/*
|
|
@@ -764,7 +1202,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
|
764
1202
|
static VALUE cState_array_nl(VALUE self)
|
|
765
1203
|
{
|
|
766
1204
|
GET_STATE(self);
|
|
767
|
-
return state->array_nl;
|
|
1205
|
+
return state->array_nl ? rb_str_new2(state->array_nl) : rb_str_new2("");
|
|
768
1206
|
}
|
|
769
1207
|
|
|
770
1208
|
/*
|
|
@@ -774,21 +1212,34 @@ static VALUE cState_array_nl(VALUE self)
|
|
|
774
1212
|
*/
|
|
775
1213
|
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
776
1214
|
{
|
|
1215
|
+
int len;
|
|
777
1216
|
GET_STATE(self);
|
|
778
1217
|
Check_Type(array_nl, T_STRING);
|
|
779
|
-
|
|
1218
|
+
len = RSTRING_LEN(array_nl);
|
|
1219
|
+
if (len == 0) {
|
|
1220
|
+
if (state->array_nl) {
|
|
1221
|
+
ruby_xfree(state->array_nl);
|
|
1222
|
+
state->array_nl = NULL;
|
|
1223
|
+
}
|
|
1224
|
+
} else {
|
|
1225
|
+
if (state->array_nl) ruby_xfree(state->array_nl);
|
|
1226
|
+
state->array_nl = strdup(RSTRING_PTR(array_nl));
|
|
1227
|
+
state->array_nl_len = len;
|
|
1228
|
+
}
|
|
1229
|
+
return Qnil;
|
|
780
1230
|
}
|
|
781
1231
|
|
|
1232
|
+
|
|
782
1233
|
/*
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1234
|
+
* call-seq: check_circular?
|
|
1235
|
+
*
|
|
1236
|
+
* Returns true, if circular data structures should be checked,
|
|
1237
|
+
* otherwise returns false.
|
|
1238
|
+
*/
|
|
788
1239
|
static VALUE cState_check_circular_p(VALUE self)
|
|
789
1240
|
{
|
|
790
1241
|
GET_STATE(self);
|
|
791
|
-
return state->
|
|
1242
|
+
return state->max_nesting ? Qtrue : Qfalse;
|
|
792
1243
|
}
|
|
793
1244
|
|
|
794
1245
|
/*
|
|
@@ -813,8 +1264,7 @@ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
|
|
|
813
1264
|
{
|
|
814
1265
|
GET_STATE(self);
|
|
815
1266
|
Check_Type(depth, T_FIXNUM);
|
|
816
|
-
state->max_nesting = FIX2LONG(depth);
|
|
817
|
-
return Qnil;
|
|
1267
|
+
return state->max_nesting = FIX2LONG(depth);
|
|
818
1268
|
}
|
|
819
1269
|
|
|
820
1270
|
/*
|
|
@@ -830,37 +1280,39 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
830
1280
|
}
|
|
831
1281
|
|
|
832
1282
|
/*
|
|
833
|
-
* call-seq:
|
|
1283
|
+
* call-seq: ascii_only?
|
|
834
1284
|
*
|
|
835
|
-
* Returns
|
|
1285
|
+
* Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
|
|
1286
|
+
* returns false.
|
|
836
1287
|
*/
|
|
837
|
-
static VALUE
|
|
1288
|
+
static VALUE cState_ascii_only_p(VALUE self)
|
|
838
1289
|
{
|
|
839
1290
|
GET_STATE(self);
|
|
840
|
-
return
|
|
1291
|
+
return state->ascii_only ? Qtrue : Qfalse;
|
|
841
1292
|
}
|
|
842
1293
|
|
|
843
1294
|
/*
|
|
844
|
-
* call-seq:
|
|
1295
|
+
* call-seq: depth
|
|
845
1296
|
*
|
|
846
|
-
*
|
|
847
|
-
* data structure is rendered).
|
|
1297
|
+
* This integer returns the current depth of data structure nesting.
|
|
848
1298
|
*/
|
|
849
|
-
static VALUE
|
|
1299
|
+
static VALUE cState_depth(VALUE self)
|
|
850
1300
|
{
|
|
851
1301
|
GET_STATE(self);
|
|
852
|
-
return
|
|
1302
|
+
return LONG2FIX(state->depth);
|
|
853
1303
|
}
|
|
854
1304
|
|
|
855
1305
|
/*
|
|
856
|
-
* call-seq:
|
|
1306
|
+
* call-seq: depth=(depth)
|
|
857
1307
|
*
|
|
858
|
-
*
|
|
1308
|
+
* This sets the maximum level of data structure nesting in the generated JSON
|
|
1309
|
+
* to the integer depth, max_nesting = 0 if no maximum should be checked.
|
|
859
1310
|
*/
|
|
860
|
-
static VALUE
|
|
1311
|
+
static VALUE cState_depth_set(VALUE self, VALUE depth)
|
|
861
1312
|
{
|
|
862
1313
|
GET_STATE(self);
|
|
863
|
-
|
|
1314
|
+
Check_Type(depth, T_FIXNUM);
|
|
1315
|
+
return state->depth = FIX2LONG(depth);
|
|
864
1316
|
}
|
|
865
1317
|
|
|
866
1318
|
/*
|
|
@@ -869,17 +1321,19 @@ static VALUE cState_forget(VALUE self, VALUE object)
|
|
|
869
1321
|
void Init_generator()
|
|
870
1322
|
{
|
|
871
1323
|
rb_require("json/common");
|
|
1324
|
+
|
|
872
1325
|
mJSON = rb_define_module("JSON");
|
|
873
1326
|
mExt = rb_define_module_under(mJSON, "Ext");
|
|
874
1327
|
mGenerator = rb_define_module_under(mExt, "Generator");
|
|
1328
|
+
|
|
875
1329
|
eGeneratorError = rb_path2class("JSON::GeneratorError");
|
|
876
|
-
eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
|
|
877
1330
|
eNestingError = rb_path2class("JSON::NestingError");
|
|
1331
|
+
|
|
878
1332
|
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
|
|
879
1333
|
rb_define_alloc_func(cState, cState_s_allocate);
|
|
880
1334
|
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
|
|
881
1335
|
rb_define_method(cState, "initialize", cState_initialize, -1);
|
|
882
|
-
|
|
1336
|
+
rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
|
|
883
1337
|
rb_define_method(cState, "indent", cState_indent, 0);
|
|
884
1338
|
rb_define_method(cState, "indent=", cState_indent_set, 1);
|
|
885
1339
|
rb_define_method(cState, "space", cState_space, 0);
|
|
@@ -890,16 +1344,17 @@ void Init_generator()
|
|
|
890
1344
|
rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
|
|
891
1345
|
rb_define_method(cState, "array_nl", cState_array_nl, 0);
|
|
892
1346
|
rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
|
|
893
|
-
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
|
|
894
1347
|
rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
|
|
895
1348
|
rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
|
|
1349
|
+
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
|
|
896
1350
|
rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
|
|
897
|
-
rb_define_method(cState, "
|
|
898
|
-
rb_define_method(cState, "
|
|
899
|
-
rb_define_method(cState, "
|
|
1351
|
+
rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
|
|
1352
|
+
rb_define_method(cState, "depth", cState_depth, 0);
|
|
1353
|
+
rb_define_method(cState, "depth=", cState_depth_set, 1);
|
|
900
1354
|
rb_define_method(cState, "configure", cState_configure, 1);
|
|
901
1355
|
rb_define_method(cState, "to_h", cState_to_h, 0);
|
|
902
1356
|
rb_define_method(cState, "[]", cState_aref, 1);
|
|
1357
|
+
rb_define_method(cState, "generate", cState_generate, 1);
|
|
903
1358
|
|
|
904
1359
|
mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
|
|
905
1360
|
mObject = rb_define_module_under(mGeneratorMethods, "Object");
|
|
@@ -908,8 +1363,10 @@ void Init_generator()
|
|
|
908
1363
|
rb_define_method(mHash, "to_json", mHash_to_json, -1);
|
|
909
1364
|
mArray = rb_define_module_under(mGeneratorMethods, "Array");
|
|
910
1365
|
rb_define_method(mArray, "to_json", mArray_to_json, -1);
|
|
911
|
-
|
|
912
|
-
rb_define_method(
|
|
1366
|
+
mFixnum = rb_define_module_under(mGeneratorMethods, "Fixnum");
|
|
1367
|
+
rb_define_method(mFixnum, "to_json", mFixnum_to_json, -1);
|
|
1368
|
+
mBignum = rb_define_module_under(mGeneratorMethods, "Bignum");
|
|
1369
|
+
rb_define_method(mBignum, "to_json", mBignum_to_json, -1);
|
|
913
1370
|
mFloat = rb_define_module_under(mGeneratorMethods, "Float");
|
|
914
1371
|
rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
|
|
915
1372
|
mString = rb_define_module_under(mGeneratorMethods, "String");
|
|
@@ -926,6 +1383,7 @@ void Init_generator()
|
|
|
926
1383
|
mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
|
|
927
1384
|
rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
|
|
928
1385
|
|
|
1386
|
+
CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE"));
|
|
929
1387
|
i_to_s = rb_intern("to_s");
|
|
930
1388
|
i_to_json = rb_intern("to_json");
|
|
931
1389
|
i_new = rb_intern("new");
|
|
@@ -934,9 +1392,10 @@ void Init_generator()
|
|
|
934
1392
|
i_space_before = rb_intern("space_before");
|
|
935
1393
|
i_object_nl = rb_intern("object_nl");
|
|
936
1394
|
i_array_nl = rb_intern("array_nl");
|
|
937
|
-
i_check_circular = rb_intern("check_circular");
|
|
938
1395
|
i_max_nesting = rb_intern("max_nesting");
|
|
939
1396
|
i_allow_nan = rb_intern("allow_nan");
|
|
1397
|
+
i_ascii_only = rb_intern("ascii_only");
|
|
1398
|
+
i_depth = rb_intern("depth");
|
|
940
1399
|
i_pack = rb_intern("pack");
|
|
941
1400
|
i_unpack = rb_intern("unpack");
|
|
942
1401
|
i_create_id = rb_intern("create_id");
|
|
@@ -945,9 +1404,14 @@ void Init_generator()
|
|
|
945
1404
|
i_aref = rb_intern("[]");
|
|
946
1405
|
i_send = rb_intern("__send__");
|
|
947
1406
|
i_respond_to_p = rb_intern("respond_to?");
|
|
1407
|
+
i_match = rb_intern("match");
|
|
1408
|
+
i_keys = rb_intern("keys");
|
|
1409
|
+
i_dup = rb_intern("dup");
|
|
948
1410
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
949
|
-
|
|
1411
|
+
CEncoding_UTF_8 = rb_funcall(rb_path2class("Encoding"), rb_intern("find"), 1, rb_str_new2("utf-8"));
|
|
950
1412
|
i_encoding = rb_intern("encoding");
|
|
951
1413
|
i_encode = rb_intern("encode");
|
|
952
1414
|
#endif
|
|
1415
|
+
i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE");
|
|
1416
|
+
CJSON_SAFE_STATE_PROTOTYPE = Qnil;
|
|
953
1417
|
}
|