json 1.8.6 → 2.3.1
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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +13 -5
- data/{CHANGES → CHANGES.md} +212 -95
- data/Gemfile +10 -3
- data/{README-json-jruby.markdown → README-json-jruby.md} +0 -0
- data/README.md +187 -107
- data/Rakefile +36 -104
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +175 -98
- data/ext/json/ext/generator/generator.h +0 -6
- data/ext/json/ext/parser/extconf.rb +3 -0
- data/ext/json/ext/parser/parser.c +380 -483
- data/ext/json/ext/parser/parser.h +4 -5
- data/ext/json/ext/parser/parser.rl +109 -175
- data/ext/json/extconf.rb +0 -1
- data/java/src/json/ext/Generator.java +35 -15
- data/java/src/json/ext/GeneratorState.java +2 -54
- data/java/src/json/ext/OptionsReader.java +1 -1
- data/java/src/json/ext/Parser.java +131 -413
- data/java/src/json/ext/Parser.rl +47 -122
- data/java/src/json/ext/RuntimeInfo.java +0 -4
- data/json-java.gemspec +4 -5
- data/json.gemspec +0 -0
- data/json_pure.gemspec +11 -15
- data/lib/json.rb +379 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -3
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +3 -3
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +3 -2
- data/lib/json/add/regexp.rb +3 -3
- data/lib/json/add/set.rb +29 -0
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -1
- data/lib/json/add/time.rb +1 -1
- data/lib/json/common.rb +335 -128
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure.rb +2 -8
- data/lib/json/pure/generator.rb +54 -125
- data/lib/json/pure/parser.rb +42 -82
- data/lib/json/version.rb +2 -1
- data/references/rfc7159.txt +899 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +32 -25
- data/tests/json_common_interface_test.rb +126 -0
- data/tests/json_encoding_test.rb +107 -0
- data/tests/json_ext_parser_test.rb +15 -0
- data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +10 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +112 -39
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +472 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/{setup_variant.rb → test_helper.rb} +6 -0
- data/tools/fuzz.rb +1 -9
- metadata +38 -43
- data/TODO +0 -1
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- data/tests/fixtures/fail1.json +0 -1
- data/tests/test_json.rb +0 -519
- data/tests/test_json_encoding.rb +0 -65
- data/tests/test_json_string_matching.rb +0 -39
- data/tests/test_json_unicode.rb +0 -72
data/Rakefile
CHANGED
|
@@ -23,8 +23,13 @@ class UndocumentedTestTask < Rake::TestTask
|
|
|
23
23
|
def desc(*) end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
which = lambda { |c|
|
|
27
|
+
w = `which #{c}`
|
|
28
|
+
break w.chomp unless w.empty?
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
MAKE = ENV['MAKE'] || %w[gmake make].find(&which)
|
|
32
|
+
BUNDLE = ENV['BUNDLE'] || %w[bundle].find(&which)
|
|
28
33
|
PKG_NAME = 'json'
|
|
29
34
|
PKG_TITLE = 'JSON Implementation for Ruby'
|
|
30
35
|
PKG_VERSION = File.read('VERSION').chomp
|
|
@@ -47,8 +52,8 @@ JAVA_CLASSES = []
|
|
|
47
52
|
JRUBY_PARSER_JAR = File.expand_path("lib/json/ext/parser.jar")
|
|
48
53
|
JRUBY_GENERATOR_JAR = File.expand_path("lib/json/ext/generator.jar")
|
|
49
54
|
|
|
50
|
-
RAGEL_CODEGEN = %w[rlcodegen rlgen-cd ragel].find
|
|
51
|
-
RAGEL_DOTGEN = %w[rlgen-dot rlgen-cd ragel].find
|
|
55
|
+
RAGEL_CODEGEN = %w[rlcodegen rlgen-cd ragel].find(&which)
|
|
56
|
+
RAGEL_DOTGEN = %w[rlgen-dot rlgen-cd ragel].find(&which)
|
|
52
57
|
|
|
53
58
|
desc "Installing library (pure)"
|
|
54
59
|
task :install_pure => :version do
|
|
@@ -73,89 +78,12 @@ task :install_ext => [ :compile, :install_pure, :install_ext_really ]
|
|
|
73
78
|
desc "Installing library (extension)"
|
|
74
79
|
task :install => :install_ext
|
|
75
80
|
|
|
76
|
-
if defined?(Gem) and defined?(Gem::PackageTask)
|
|
77
|
-
spec_pure = Gem::Specification.new do |s|
|
|
78
|
-
s.name = 'json_pure'
|
|
79
|
-
s.version = PKG_VERSION
|
|
80
|
-
s.summary = PKG_TITLE
|
|
81
|
-
s.description = "This is a JSON implementation in pure Ruby."
|
|
82
|
-
|
|
83
|
-
s.files = PKG_FILES
|
|
84
|
-
|
|
85
|
-
s.require_path = 'lib'
|
|
86
|
-
s.add_development_dependency 'rake'
|
|
87
|
-
s.add_development_dependency 'test-unit', '~> 2.0'
|
|
88
|
-
|
|
89
|
-
s.extra_rdoc_files << 'README.md'
|
|
90
|
-
s.rdoc_options <<
|
|
91
|
-
'--title' << 'JSON implemention for Ruby' << '--main' << 'README.md'
|
|
92
|
-
s.test_files.concat Dir['./tests/test_*.rb']
|
|
93
|
-
|
|
94
|
-
s.author = "Florian Frank"
|
|
95
|
-
s.email = "flori@ping.de"
|
|
96
|
-
s.homepage = "http://flori.github.com/#{PKG_NAME}"
|
|
97
|
-
s.license = 'Ruby'
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
desc 'Creates a json_pure.gemspec file'
|
|
101
|
-
task :gemspec_pure => :version do
|
|
102
|
-
File.open('json_pure.gemspec', 'w') do |gemspec|
|
|
103
|
-
gemspec.write spec_pure.to_ruby
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
Gem::PackageTask.new(spec_pure) do |pkg|
|
|
108
|
-
pkg.need_tar = true
|
|
109
|
-
pkg.package_files = PKG_FILES
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
spec_ext = Gem::Specification.new do |s|
|
|
113
|
-
s.name = 'json'
|
|
114
|
-
s.version = PKG_VERSION
|
|
115
|
-
s.summary = PKG_TITLE
|
|
116
|
-
s.description = "This is a JSON implementation as a Ruby extension in C."
|
|
117
|
-
|
|
118
|
-
s.files = PKG_FILES
|
|
119
|
-
|
|
120
|
-
s.extensions = FileList['ext/**/extconf.rb']
|
|
121
|
-
|
|
122
|
-
s.require_path = 'lib'
|
|
123
|
-
s.add_development_dependency 'rake'
|
|
124
|
-
s.add_development_dependency 'test-unit', '~> 2.0'
|
|
125
|
-
|
|
126
|
-
s.extra_rdoc_files << 'README.md'
|
|
127
|
-
s.rdoc_options <<
|
|
128
|
-
'--title' << 'JSON implemention for Ruby' << '--main' << 'README.md'
|
|
129
|
-
s.test_files.concat Dir['./tests/test_*.rb']
|
|
130
|
-
|
|
131
|
-
s.author = "Florian Frank"
|
|
132
|
-
s.email = "flori@ping.de"
|
|
133
|
-
s.homepage = "http://flori.github.com/#{PKG_NAME}"
|
|
134
|
-
s.license = 'Ruby'
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
desc 'Creates a json.gemspec file'
|
|
138
|
-
task :gemspec_ext => :version do
|
|
139
|
-
File.open('json.gemspec', 'w') do |gemspec|
|
|
140
|
-
gemspec.write spec_ext.to_ruby
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
Gem::PackageTask.new(spec_ext) do |pkg|
|
|
145
|
-
pkg.need_tar = true
|
|
146
|
-
pkg.package_files = PKG_FILES
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
desc 'Create all gemspec files'
|
|
151
|
-
task :gemspec => [ :gemspec_pure, :gemspec_ext ]
|
|
152
|
-
end
|
|
153
|
-
|
|
154
81
|
desc m = "Writing version information for #{PKG_VERSION}"
|
|
155
82
|
task :version do
|
|
156
83
|
puts m
|
|
157
84
|
File.open(File.join('lib', 'json', 'version.rb'), 'w') do |v|
|
|
158
85
|
v.puts <<EOT
|
|
86
|
+
# frozen_string_literal: false
|
|
159
87
|
module JSON
|
|
160
88
|
# JSON version
|
|
161
89
|
VERSION = '#{PKG_VERSION}'
|
|
@@ -168,22 +96,24 @@ EOT
|
|
|
168
96
|
end
|
|
169
97
|
end
|
|
170
98
|
|
|
99
|
+
task :check_env do
|
|
100
|
+
ENV.key?('JSON') or fail "JSON env var is required"
|
|
101
|
+
end
|
|
102
|
+
|
|
171
103
|
desc "Testing library (pure ruby)"
|
|
172
|
-
task :test_pure => [ :
|
|
104
|
+
task :test_pure => [ :set_env_pure, :check_env, :do_test_pure ]
|
|
105
|
+
task(:set_env_pure) { ENV['JSON'] = 'pure' }
|
|
173
106
|
|
|
174
107
|
UndocumentedTestTask.new do |t|
|
|
175
108
|
t.name = 'do_test_pure'
|
|
176
|
-
t.libs << 'lib'
|
|
177
|
-
t.test_files = FileList['tests
|
|
109
|
+
t.libs << 'lib' << 'tests'
|
|
110
|
+
t.test_files = FileList['tests/*_test.rb']
|
|
178
111
|
t.verbose = true
|
|
179
112
|
t.options = '-v'
|
|
180
113
|
end
|
|
181
114
|
|
|
182
115
|
desc "Testing library (pure ruby and extension)"
|
|
183
|
-
task :test
|
|
184
|
-
sh "env JSON=pure #{BUNDLE} exec rake test_pure" or exit 1
|
|
185
|
-
sh "env JSON=ext #{BUNDLE} exec rake test_ext" or exit 1
|
|
186
|
-
end
|
|
116
|
+
task :test => [ :test_pure, :test_ext ]
|
|
187
117
|
|
|
188
118
|
namespace :gems do
|
|
189
119
|
desc 'Install all development gems'
|
|
@@ -193,13 +123,11 @@ namespace :gems do
|
|
|
193
123
|
end
|
|
194
124
|
|
|
195
125
|
if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
ENV['JAVA_HOME'] = local_java
|
|
202
|
-
end
|
|
126
|
+
ENV['JAVA_HOME'] ||= [
|
|
127
|
+
'/usr/local/java/jdk',
|
|
128
|
+
'/usr/lib/jvm/java-6-openjdk',
|
|
129
|
+
'/Library/Java/Home',
|
|
130
|
+
].find { |c| File.directory?(c) }
|
|
203
131
|
if ENV['JAVA_HOME']
|
|
204
132
|
warn " *** JAVA_HOME is set to #{ENV['JAVA_HOME'].inspect}"
|
|
205
133
|
ENV['PATH'] = ENV['PATH'].split(/:/).unshift(java_path = "#{ENV['JAVA_HOME']}/bin") * ':'
|
|
@@ -233,7 +161,7 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
|
|
|
233
161
|
classpath = (Dir['java/lib/*.jar'] << 'java/src' << JRUBY_JAR) * ':'
|
|
234
162
|
obj = src.sub(/\.java\Z/, '.class')
|
|
235
163
|
file obj => src do
|
|
236
|
-
sh 'javac', '-classpath', classpath, '-source', '1.
|
|
164
|
+
sh 'javac', '-classpath', classpath, '-source', '1.6', '-target', '1.6', src
|
|
237
165
|
end
|
|
238
166
|
JAVA_CLASSES << obj
|
|
239
167
|
end
|
|
@@ -252,12 +180,13 @@ if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
|
|
|
252
180
|
end
|
|
253
181
|
|
|
254
182
|
desc "Testing library (jruby)"
|
|
255
|
-
task :test_ext => [ :create_jar, :do_test_ext ]
|
|
183
|
+
task :test_ext => [ :set_env_ext, :create_jar, :check_env, :do_test_ext ]
|
|
184
|
+
task(:set_env_ext) { ENV['JSON'] = 'ext' }
|
|
256
185
|
|
|
257
186
|
UndocumentedTestTask.new do |t|
|
|
258
187
|
t.name = 'do_test_ext'
|
|
259
|
-
t.libs << 'lib'
|
|
260
|
-
t.test_files = FileList['tests
|
|
188
|
+
t.libs << 'lib' << 'tests'
|
|
189
|
+
t.test_files = FileList['tests/*_test.rb']
|
|
261
190
|
t.verbose = true
|
|
262
191
|
t.options = '-v'
|
|
263
192
|
end
|
|
@@ -326,12 +255,12 @@ else
|
|
|
326
255
|
end
|
|
327
256
|
|
|
328
257
|
desc "Testing library (extension)"
|
|
329
|
-
task :test_ext => [ :compile, :do_test_ext ]
|
|
258
|
+
task :test_ext => [ :check_env, :compile, :do_test_ext ]
|
|
330
259
|
|
|
331
260
|
UndocumentedTestTask.new do |t|
|
|
332
261
|
t.name = 'do_test_ext'
|
|
333
|
-
t.libs << 'ext' << 'lib'
|
|
334
|
-
t.test_files = FileList['tests
|
|
262
|
+
t.libs << 'ext' << 'lib' << 'tests'
|
|
263
|
+
t.test_files = FileList['tests/*_test.rb']
|
|
335
264
|
t.verbose = true
|
|
336
265
|
t.options = '-v'
|
|
337
266
|
end
|
|
@@ -357,6 +286,9 @@ else
|
|
|
357
286
|
sh "ragel -x parser.rl | #{RAGEL_CODEGEN} -G2"
|
|
358
287
|
end
|
|
359
288
|
src = File.read("parser.c").gsub(/[ \t]+$/, '')
|
|
289
|
+
src.gsub!(/^static const int (JSON_.*=.*);$/, 'enum {\1};')
|
|
290
|
+
src.gsub!(/0 <= \(\*p\) && \(\*p\) <= 31/, "0 <= (signed char)(*p) && (*p) <= 31")
|
|
291
|
+
src[0, 0] = "/* This file is automatically generated from parser.rl by using ragel */"
|
|
360
292
|
File.open("parser.c", "w") {|f| f.print src}
|
|
361
293
|
end
|
|
362
294
|
end
|
|
@@ -399,4 +331,4 @@ else
|
|
|
399
331
|
end
|
|
400
332
|
|
|
401
333
|
desc "Compile in the the source directory"
|
|
402
|
-
task :default => [ :clean, :
|
|
334
|
+
task :default => [ :clean, :test ]
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2.3.1
|
|
@@ -15,12 +15,12 @@ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
|
|
|
15
15
|
#endif
|
|
16
16
|
mFloat, mString, mString_Extend,
|
|
17
17
|
mTrueClass, mFalseClass, mNilClass, eGeneratorError,
|
|
18
|
-
eNestingError,
|
|
18
|
+
eNestingError,
|
|
19
19
|
i_SAFE_STATE_PROTOTYPE;
|
|
20
20
|
|
|
21
21
|
static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
|
|
22
22
|
i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
|
|
23
|
-
|
|
23
|
+
i_pack, i_unpack, i_create_id, i_extend, i_key_p,
|
|
24
24
|
i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
|
|
25
25
|
i_buffer_initial_length, i_dup;
|
|
26
26
|
|
|
@@ -222,6 +222,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
|
|
|
222
222
|
unicode_escape_to_buffer(buffer, buf, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START));
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
+
RB_GC_GUARD(string);
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
/* Converts string to a JSON string in FBuffer buffer, where only the
|
|
@@ -236,6 +237,7 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
|
|
|
236
237
|
int escape_len;
|
|
237
238
|
unsigned char c;
|
|
238
239
|
char buf[6] = { '\\', 'u' };
|
|
240
|
+
int ascii_only = rb_enc_str_asciionly_p(string);
|
|
239
241
|
|
|
240
242
|
for (start = 0, end = 0; end < len;) {
|
|
241
243
|
p = ptr + end;
|
|
@@ -280,14 +282,17 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
|
|
|
280
282
|
break;
|
|
281
283
|
default:
|
|
282
284
|
{
|
|
283
|
-
unsigned short clen =
|
|
284
|
-
if (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
unsigned short clen = 1;
|
|
286
|
+
if (!ascii_only) {
|
|
287
|
+
clen += trailingBytesForUTF8[c];
|
|
288
|
+
if (end + clen > len) {
|
|
289
|
+
rb_raise(rb_path2class("JSON::GeneratorError"),
|
|
290
|
+
"partial character in source, but hit end");
|
|
291
|
+
}
|
|
292
|
+
if (!isLegalUTF8((UTF8 *) p, clen)) {
|
|
293
|
+
rb_raise(rb_path2class("JSON::GeneratorError"),
|
|
294
|
+
"source sequence is illegal/malformed utf-8");
|
|
295
|
+
}
|
|
291
296
|
}
|
|
292
297
|
end += clen;
|
|
293
298
|
}
|
|
@@ -307,7 +312,7 @@ static char *fstrndup(const char *ptr, unsigned long len) {
|
|
|
307
312
|
char *result;
|
|
308
313
|
if (len <= 0) return NULL;
|
|
309
314
|
result = ALLOC_N(char, len);
|
|
310
|
-
|
|
315
|
+
memcpy(result, ptr, len);
|
|
311
316
|
return result;
|
|
312
317
|
}
|
|
313
318
|
|
|
@@ -323,6 +328,76 @@ static char *fstrndup(const char *ptr, unsigned long len) {
|
|
|
323
328
|
*
|
|
324
329
|
*/
|
|
325
330
|
|
|
331
|
+
/* Explanation of the following: that's the only way to not pollute
|
|
332
|
+
* standard library's docs with GeneratorMethods::<ClassName> which
|
|
333
|
+
* are uninformative and take a large place in a list of classes
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
/*
|
|
337
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods
|
|
338
|
+
* :nodoc:
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
/*
|
|
342
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Array
|
|
343
|
+
* :nodoc:
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
/*
|
|
347
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
|
|
348
|
+
* :nodoc:
|
|
349
|
+
*/
|
|
350
|
+
|
|
351
|
+
/*
|
|
352
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
|
|
353
|
+
* :nodoc:
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
/*
|
|
357
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
|
|
358
|
+
* :nodoc:
|
|
359
|
+
*/
|
|
360
|
+
|
|
361
|
+
/*
|
|
362
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Float
|
|
363
|
+
* :nodoc:
|
|
364
|
+
*/
|
|
365
|
+
|
|
366
|
+
/*
|
|
367
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
|
|
368
|
+
* :nodoc:
|
|
369
|
+
*/
|
|
370
|
+
|
|
371
|
+
/*
|
|
372
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
|
|
373
|
+
* :nodoc:
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
/*
|
|
377
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
|
|
378
|
+
* :nodoc:
|
|
379
|
+
*/
|
|
380
|
+
|
|
381
|
+
/*
|
|
382
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::Object
|
|
383
|
+
* :nodoc:
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
/*
|
|
387
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::String
|
|
388
|
+
* :nodoc:
|
|
389
|
+
*/
|
|
390
|
+
|
|
391
|
+
/*
|
|
392
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
|
|
393
|
+
* :nodoc:
|
|
394
|
+
*/
|
|
395
|
+
|
|
396
|
+
/*
|
|
397
|
+
* Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
|
|
398
|
+
* :nodoc:
|
|
399
|
+
*/
|
|
400
|
+
|
|
326
401
|
/*
|
|
327
402
|
* call-seq: to_json(state = nil)
|
|
328
403
|
*
|
|
@@ -641,8 +716,6 @@ static VALUE cState_configure(VALUE self, VALUE opts)
|
|
|
641
716
|
state->allow_nan = RTEST(tmp);
|
|
642
717
|
tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
|
|
643
718
|
state->ascii_only = RTEST(tmp);
|
|
644
|
-
tmp = rb_hash_aref(opts, ID2SYM(i_quirks_mode));
|
|
645
|
-
state->quirks_mode = RTEST(tmp);
|
|
646
719
|
return self;
|
|
647
720
|
}
|
|
648
721
|
|
|
@@ -676,7 +749,6 @@ static VALUE cState_to_h(VALUE self)
|
|
|
676
749
|
rb_hash_aset(result, ID2SYM(i_array_nl), rb_str_new(state->array_nl, state->array_nl_len));
|
|
677
750
|
rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
|
|
678
751
|
rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
|
|
679
|
-
rb_hash_aset(result, ID2SYM(i_quirks_mode), state->quirks_mode ? Qtrue : Qfalse);
|
|
680
752
|
rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
|
|
681
753
|
rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
|
|
682
754
|
rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
|
|
@@ -694,7 +766,7 @@ static VALUE cState_aref(VALUE self, VALUE name)
|
|
|
694
766
|
if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) {
|
|
695
767
|
return rb_funcall(self, i_send, 1, name);
|
|
696
768
|
} else {
|
|
697
|
-
return
|
|
769
|
+
return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
|
|
698
770
|
}
|
|
699
771
|
}
|
|
700
772
|
|
|
@@ -717,43 +789,83 @@ static VALUE cState_aset(VALUE self, VALUE name, VALUE value)
|
|
|
717
789
|
return Qnil;
|
|
718
790
|
}
|
|
719
791
|
|
|
720
|
-
|
|
792
|
+
struct hash_foreach_arg {
|
|
793
|
+
FBuffer *buffer;
|
|
794
|
+
JSON_Generator_State *state;
|
|
795
|
+
VALUE Vstate;
|
|
796
|
+
int iter;
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
static int
|
|
800
|
+
json_object_i(VALUE key, VALUE val, VALUE _arg)
|
|
721
801
|
{
|
|
802
|
+
struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg;
|
|
803
|
+
FBuffer *buffer = arg->buffer;
|
|
804
|
+
JSON_Generator_State *state = arg->state;
|
|
805
|
+
VALUE Vstate = arg->Vstate;
|
|
806
|
+
|
|
722
807
|
char *object_nl = state->object_nl;
|
|
723
808
|
long object_nl_len = state->object_nl_len;
|
|
724
809
|
char *indent = state->indent;
|
|
725
810
|
long indent_len = state->indent_len;
|
|
726
|
-
long max_nesting = state->max_nesting;
|
|
727
811
|
char *delim = FBUFFER_PTR(state->object_delim);
|
|
728
812
|
long delim_len = FBUFFER_LEN(state->object_delim);
|
|
729
813
|
char *delim2 = FBUFFER_PTR(state->object_delim2);
|
|
730
814
|
long delim2_len = FBUFFER_LEN(state->object_delim2);
|
|
815
|
+
long depth = state->depth;
|
|
816
|
+
int j;
|
|
817
|
+
VALUE klass, key_to_s;
|
|
818
|
+
|
|
819
|
+
if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len);
|
|
820
|
+
if (object_nl) {
|
|
821
|
+
fbuffer_append(buffer, object_nl, object_nl_len);
|
|
822
|
+
}
|
|
823
|
+
if (indent) {
|
|
824
|
+
for (j = 0; j < depth; j++) {
|
|
825
|
+
fbuffer_append(buffer, indent, indent_len);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
klass = CLASS_OF(key);
|
|
830
|
+
if (klass == rb_cString) {
|
|
831
|
+
key_to_s = key;
|
|
832
|
+
} else if (klass == rb_cSymbol) {
|
|
833
|
+
key_to_s = rb_id2str(SYM2ID(key));
|
|
834
|
+
} else {
|
|
835
|
+
key_to_s = rb_funcall(key, i_to_s, 0);
|
|
836
|
+
}
|
|
837
|
+
Check_Type(key_to_s, T_STRING);
|
|
838
|
+
generate_json(buffer, Vstate, state, key_to_s);
|
|
839
|
+
fbuffer_append(buffer, delim2, delim2_len);
|
|
840
|
+
generate_json(buffer, Vstate, state, val);
|
|
841
|
+
|
|
842
|
+
arg->iter++;
|
|
843
|
+
return ST_CONTINUE;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
847
|
+
{
|
|
848
|
+
char *object_nl = state->object_nl;
|
|
849
|
+
long object_nl_len = state->object_nl_len;
|
|
850
|
+
char *indent = state->indent;
|
|
851
|
+
long indent_len = state->indent_len;
|
|
852
|
+
long max_nesting = state->max_nesting;
|
|
731
853
|
long depth = ++state->depth;
|
|
732
|
-
int
|
|
733
|
-
|
|
854
|
+
int j;
|
|
855
|
+
struct hash_foreach_arg arg;
|
|
856
|
+
|
|
734
857
|
if (max_nesting != 0 && depth > max_nesting) {
|
|
735
858
|
fbuffer_free(buffer);
|
|
736
859
|
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
|
|
737
860
|
}
|
|
738
861
|
fbuffer_append_char(buffer, '{');
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
for (j = 0; j < depth; j++) {
|
|
747
|
-
fbuffer_append(buffer, indent, indent_len);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
key = rb_ary_entry(keys, i);
|
|
751
|
-
key_to_s = rb_funcall(key, i_to_s, 0);
|
|
752
|
-
Check_Type(key_to_s, T_STRING);
|
|
753
|
-
generate_json(buffer, Vstate, state, key_to_s);
|
|
754
|
-
fbuffer_append(buffer, delim2, delim2_len);
|
|
755
|
-
generate_json(buffer, Vstate, state, rb_hash_aref(obj, key));
|
|
756
|
-
}
|
|
862
|
+
|
|
863
|
+
arg.buffer = buffer;
|
|
864
|
+
arg.state = state;
|
|
865
|
+
arg.Vstate = Vstate;
|
|
866
|
+
arg.iter = 0;
|
|
867
|
+
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
|
|
868
|
+
|
|
757
869
|
depth = --state->depth;
|
|
758
870
|
if (object_nl) {
|
|
759
871
|
fbuffer_append(buffer, object_nl, object_nl_len);
|
|
@@ -804,11 +916,22 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
|
|
|
804
916
|
fbuffer_append_char(buffer, ']');
|
|
805
917
|
}
|
|
806
918
|
|
|
919
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
|
920
|
+
static int enc_utf8_compatible_p(rb_encoding *enc)
|
|
921
|
+
{
|
|
922
|
+
if (enc == rb_usascii_encoding()) return 1;
|
|
923
|
+
if (enc == rb_utf8_encoding()) return 1;
|
|
924
|
+
return 0;
|
|
925
|
+
}
|
|
926
|
+
#endif
|
|
927
|
+
|
|
807
928
|
static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
808
929
|
{
|
|
809
930
|
fbuffer_append_char(buffer, '"');
|
|
810
931
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
811
|
-
|
|
932
|
+
if (!enc_utf8_compatible_p(rb_enc_get(obj))) {
|
|
933
|
+
obj = rb_str_encode(obj, CEncoding_UTF_8, 0, Qnil);
|
|
934
|
+
}
|
|
812
935
|
#endif
|
|
813
936
|
if (state->ascii_only) {
|
|
814
937
|
convert_UTF8_to_JSON_ASCII(buffer, obj);
|
|
@@ -853,7 +976,6 @@ static void generate_json_integer(FBuffer *buffer, VALUE Vstate, JSON_Generator_
|
|
|
853
976
|
generate_json_bignum(buffer, Vstate, state, obj);
|
|
854
977
|
}
|
|
855
978
|
#endif
|
|
856
|
-
|
|
857
979
|
static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
|
|
858
980
|
{
|
|
859
981
|
double value = RFLOAT_VALUE(obj);
|
|
@@ -943,21 +1065,6 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj)
|
|
|
943
1065
|
return fbuffer_to_s(buffer);
|
|
944
1066
|
}
|
|
945
1067
|
|
|
946
|
-
/*
|
|
947
|
-
* This function returns true if string is either a JSON array or JSON object.
|
|
948
|
-
* It might suffer from false positives, e. g. syntactically incorrect JSON in
|
|
949
|
-
* the string or certain UTF-8 characters on the right hand side.
|
|
950
|
-
*/
|
|
951
|
-
static int isArrayOrObject(VALUE string)
|
|
952
|
-
{
|
|
953
|
-
long string_len = RSTRING_LEN(string);
|
|
954
|
-
char *p = RSTRING_PTR(string), *q = p + string_len - 1;
|
|
955
|
-
if (string_len < 2) return 0;
|
|
956
|
-
for (; p < q && isspace((unsigned char)*p); p++);
|
|
957
|
-
for (; q > p && isspace((unsigned char)*q); q--);
|
|
958
|
-
return (*p == '[' && *q == ']') || (*p == '{' && *q == '}');
|
|
959
|
-
}
|
|
960
|
-
|
|
961
1068
|
/*
|
|
962
1069
|
* call-seq: generate(obj)
|
|
963
1070
|
*
|
|
@@ -969,9 +1076,7 @@ static VALUE cState_generate(VALUE self, VALUE obj)
|
|
|
969
1076
|
{
|
|
970
1077
|
VALUE result = cState_partial_generate(self, obj);
|
|
971
1078
|
GET_STATE(self);
|
|
972
|
-
|
|
973
|
-
rb_raise(eGeneratorError, "only generation of JSON objects or arrays allowed");
|
|
974
|
-
}
|
|
1079
|
+
(void)state;
|
|
975
1080
|
return result;
|
|
976
1081
|
}
|
|
977
1082
|
|
|
@@ -990,8 +1095,8 @@ static VALUE cState_generate(VALUE self, VALUE obj)
|
|
|
990
1095
|
* * *allow_nan*: true if NaN, Infinity, and -Infinity should be
|
|
991
1096
|
* generated, otherwise an exception is thrown, if these values are
|
|
992
1097
|
* encountered. This options defaults to false.
|
|
993
|
-
* * *
|
|
994
|
-
*
|
|
1098
|
+
* * *ascii_only*: true if only ASCII characters should be generated. This
|
|
1099
|
+
* option defaults to false.
|
|
995
1100
|
* * *buffer_initial_length*: sets the initial length of the generator's
|
|
996
1101
|
* internal buffer.
|
|
997
1102
|
*/
|
|
@@ -1047,10 +1152,8 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
|
|
|
1047
1152
|
} else if (rb_obj_is_kind_of(opts, rb_cHash)) {
|
|
1048
1153
|
return rb_funcall(self, i_new, 1, opts);
|
|
1049
1154
|
} else {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
}
|
|
1053
|
-
return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0);
|
|
1155
|
+
VALUE prototype = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE);
|
|
1156
|
+
return rb_funcall(prototype, i_dup, 0);
|
|
1054
1157
|
}
|
|
1055
1158
|
}
|
|
1056
1159
|
|
|
@@ -1084,7 +1187,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
|
|
|
1084
1187
|
}
|
|
1085
1188
|
} else {
|
|
1086
1189
|
if (state->indent) ruby_xfree(state->indent);
|
|
1087
|
-
state->indent =
|
|
1190
|
+
state->indent = fstrndup(RSTRING_PTR(indent), len);
|
|
1088
1191
|
state->indent_len = len;
|
|
1089
1192
|
}
|
|
1090
1193
|
return Qnil;
|
|
@@ -1122,7 +1225,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
|
|
|
1122
1225
|
}
|
|
1123
1226
|
} else {
|
|
1124
1227
|
if (state->space) ruby_xfree(state->space);
|
|
1125
|
-
state->space =
|
|
1228
|
+
state->space = fstrndup(RSTRING_PTR(space), len);
|
|
1126
1229
|
state->space_len = len;
|
|
1127
1230
|
}
|
|
1128
1231
|
return Qnil;
|
|
@@ -1158,7 +1261,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
|
|
|
1158
1261
|
}
|
|
1159
1262
|
} else {
|
|
1160
1263
|
if (state->space_before) ruby_xfree(state->space_before);
|
|
1161
|
-
state->space_before =
|
|
1264
|
+
state->space_before = fstrndup(RSTRING_PTR(space_before), len);
|
|
1162
1265
|
state->space_before_len = len;
|
|
1163
1266
|
}
|
|
1164
1267
|
return Qnil;
|
|
@@ -1195,7 +1298,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
|
|
|
1195
1298
|
}
|
|
1196
1299
|
} else {
|
|
1197
1300
|
if (state->object_nl) ruby_xfree(state->object_nl);
|
|
1198
|
-
state->object_nl =
|
|
1301
|
+
state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
|
|
1199
1302
|
state->object_nl_len = len;
|
|
1200
1303
|
}
|
|
1201
1304
|
return Qnil;
|
|
@@ -1230,7 +1333,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
|
|
|
1230
1333
|
}
|
|
1231
1334
|
} else {
|
|
1232
1335
|
if (state->array_nl) ruby_xfree(state->array_nl);
|
|
1233
|
-
state->array_nl =
|
|
1336
|
+
state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
|
|
1234
1337
|
state->array_nl_len = len;
|
|
1235
1338
|
}
|
|
1236
1339
|
return Qnil;
|
|
@@ -1289,7 +1392,7 @@ static VALUE cState_allow_nan_p(VALUE self)
|
|
|
1289
1392
|
/*
|
|
1290
1393
|
* call-seq: ascii_only?
|
|
1291
1394
|
*
|
|
1292
|
-
* Returns true, if
|
|
1395
|
+
* Returns true, if only ASCII characters should be generated. Otherwise
|
|
1293
1396
|
* returns false.
|
|
1294
1397
|
*/
|
|
1295
1398
|
static VALUE cState_ascii_only_p(VALUE self)
|
|
@@ -1298,29 +1401,6 @@ static VALUE cState_ascii_only_p(VALUE self)
|
|
|
1298
1401
|
return state->ascii_only ? Qtrue : Qfalse;
|
|
1299
1402
|
}
|
|
1300
1403
|
|
|
1301
|
-
/*
|
|
1302
|
-
* call-seq: quirks_mode?
|
|
1303
|
-
*
|
|
1304
|
-
* Returns true, if quirks mode is enabled. Otherwise returns false.
|
|
1305
|
-
*/
|
|
1306
|
-
static VALUE cState_quirks_mode_p(VALUE self)
|
|
1307
|
-
{
|
|
1308
|
-
GET_STATE(self);
|
|
1309
|
-
return state->quirks_mode ? Qtrue : Qfalse;
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
/*
|
|
1313
|
-
* call-seq: quirks_mode=(enable)
|
|
1314
|
-
*
|
|
1315
|
-
* If set to true, enables the quirks_mode mode.
|
|
1316
|
-
*/
|
|
1317
|
-
static VALUE cState_quirks_mode_set(VALUE self, VALUE enable)
|
|
1318
|
-
{
|
|
1319
|
-
GET_STATE(self);
|
|
1320
|
-
state->quirks_mode = RTEST(enable);
|
|
1321
|
-
return Qnil;
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
1404
|
/*
|
|
1325
1405
|
* call-seq: depth
|
|
1326
1406
|
*
|
|
@@ -1380,6 +1460,7 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l
|
|
|
1380
1460
|
*/
|
|
1381
1461
|
void Init_generator(void)
|
|
1382
1462
|
{
|
|
1463
|
+
#undef rb_intern
|
|
1383
1464
|
rb_require("json/common");
|
|
1384
1465
|
|
|
1385
1466
|
mJSON = rb_define_module("JSON");
|
|
@@ -1388,6 +1469,8 @@ void Init_generator(void)
|
|
|
1388
1469
|
|
|
1389
1470
|
eGeneratorError = rb_path2class("JSON::GeneratorError");
|
|
1390
1471
|
eNestingError = rb_path2class("JSON::NestingError");
|
|
1472
|
+
rb_gc_register_mark_object(eGeneratorError);
|
|
1473
|
+
rb_gc_register_mark_object(eNestingError);
|
|
1391
1474
|
|
|
1392
1475
|
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
|
|
1393
1476
|
rb_define_alloc_func(cState, cState_s_allocate);
|
|
@@ -1409,9 +1492,6 @@ void Init_generator(void)
|
|
|
1409
1492
|
rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
|
|
1410
1493
|
rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
|
|
1411
1494
|
rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
|
|
1412
|
-
rb_define_method(cState, "quirks_mode?", cState_quirks_mode_p, 0);
|
|
1413
|
-
rb_define_method(cState, "quirks_mode", cState_quirks_mode_p, 0);
|
|
1414
|
-
rb_define_method(cState, "quirks_mode=", cState_quirks_mode_set, 1);
|
|
1415
1495
|
rb_define_method(cState, "depth", cState_depth, 0);
|
|
1416
1496
|
rb_define_method(cState, "depth=", cState_depth_set, 1);
|
|
1417
1497
|
rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
|
|
@@ -1456,7 +1536,6 @@ void Init_generator(void)
|
|
|
1456
1536
|
mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
|
|
1457
1537
|
rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
|
|
1458
1538
|
|
|
1459
|
-
CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE"));
|
|
1460
1539
|
i_to_s = rb_intern("to_s");
|
|
1461
1540
|
i_to_json = rb_intern("to_json");
|
|
1462
1541
|
i_new = rb_intern("new");
|
|
@@ -1468,7 +1547,6 @@ void Init_generator(void)
|
|
|
1468
1547
|
i_max_nesting = rb_intern("max_nesting");
|
|
1469
1548
|
i_allow_nan = rb_intern("allow_nan");
|
|
1470
1549
|
i_ascii_only = rb_intern("ascii_only");
|
|
1471
|
-
i_quirks_mode = rb_intern("quirks_mode");
|
|
1472
1550
|
i_depth = rb_intern("depth");
|
|
1473
1551
|
i_buffer_initial_length = rb_intern("buffer_initial_length");
|
|
1474
1552
|
i_pack = rb_intern("pack");
|
|
@@ -1488,5 +1566,4 @@ void Init_generator(void)
|
|
|
1488
1566
|
i_encode = rb_intern("encode");
|
|
1489
1567
|
#endif
|
|
1490
1568
|
i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE");
|
|
1491
|
-
CJSON_SAFE_STATE_PROTOTYPE = Qnil;
|
|
1492
1569
|
}
|