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.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +13 -5
  4. data/{CHANGES → CHANGES.md} +212 -95
  5. data/Gemfile +10 -3
  6. data/{README-json-jruby.markdown → README-json-jruby.md} +0 -0
  7. data/README.md +187 -107
  8. data/Rakefile +36 -104
  9. data/VERSION +1 -1
  10. data/ext/json/ext/fbuffer/fbuffer.h +0 -3
  11. data/ext/json/ext/generator/generator.c +175 -98
  12. data/ext/json/ext/generator/generator.h +0 -6
  13. data/ext/json/ext/parser/extconf.rb +3 -0
  14. data/ext/json/ext/parser/parser.c +380 -483
  15. data/ext/json/ext/parser/parser.h +4 -5
  16. data/ext/json/ext/parser/parser.rl +109 -175
  17. data/ext/json/extconf.rb +0 -1
  18. data/java/src/json/ext/Generator.java +35 -15
  19. data/java/src/json/ext/GeneratorState.java +2 -54
  20. data/java/src/json/ext/OptionsReader.java +1 -1
  21. data/java/src/json/ext/Parser.java +131 -413
  22. data/java/src/json/ext/Parser.rl +47 -122
  23. data/java/src/json/ext/RuntimeInfo.java +0 -4
  24. data/json-java.gemspec +4 -5
  25. data/json.gemspec +0 -0
  26. data/json_pure.gemspec +11 -15
  27. data/lib/json.rb +379 -29
  28. data/lib/json/add/bigdecimal.rb +3 -2
  29. data/lib/json/add/complex.rb +4 -3
  30. data/lib/json/add/core.rb +1 -0
  31. data/lib/json/add/date.rb +1 -1
  32. data/lib/json/add/date_time.rb +1 -1
  33. data/lib/json/add/exception.rb +1 -1
  34. data/lib/json/add/ostruct.rb +3 -3
  35. data/lib/json/add/range.rb +1 -1
  36. data/lib/json/add/rational.rb +3 -2
  37. data/lib/json/add/regexp.rb +3 -3
  38. data/lib/json/add/set.rb +29 -0
  39. data/lib/json/add/struct.rb +1 -1
  40. data/lib/json/add/symbol.rb +1 -1
  41. data/lib/json/add/time.rb +1 -1
  42. data/lib/json/common.rb +335 -128
  43. data/lib/json/ext.rb +0 -6
  44. data/lib/json/generic_object.rb +5 -4
  45. data/lib/json/pure.rb +2 -8
  46. data/lib/json/pure/generator.rb +54 -125
  47. data/lib/json/pure/parser.rb +42 -82
  48. data/lib/json/version.rb +2 -1
  49. data/references/rfc7159.txt +899 -0
  50. data/tests/fixtures/obsolete_fail1.json +1 -0
  51. data/tests/{test_json_addition.rb → json_addition_test.rb} +32 -25
  52. data/tests/json_common_interface_test.rb +126 -0
  53. data/tests/json_encoding_test.rb +107 -0
  54. data/tests/json_ext_parser_test.rb +15 -0
  55. data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +10 -8
  56. data/tests/{test_json_generate.rb → json_generator_test.rb} +112 -39
  57. data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
  58. data/tests/json_parser_test.rb +472 -0
  59. data/tests/json_string_matching_test.rb +38 -0
  60. data/tests/{setup_variant.rb → test_helper.rb} +6 -0
  61. data/tools/fuzz.rb +1 -9
  62. metadata +38 -43
  63. data/TODO +0 -1
  64. data/data/example.json +0 -1
  65. data/data/index.html +0 -38
  66. data/data/prototype.js +0 -4184
  67. data/tests/fixtures/fail1.json +0 -1
  68. data/tests/test_json.rb +0 -519
  69. data/tests/test_json_encoding.rb +0 -65
  70. data/tests/test_json_string_matching.rb +0 -39
  71. 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
- MAKE = ENV['MAKE'] || %w[gmake make].find { |c| system(c, '-v') }
27
- BUNDLE = ENV['BUNDLE'] || %w[bundle].find { |c| system(c, '-v') }
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 { |c| system(c, '-v') }
51
- RAGEL_DOTGEN = %w[rlgen-dot rlgen-cd ragel].find { |c| system(c, '-v') }
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 => [ :clean, :do_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/test_*.rb']
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 do
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
- if ENV.key?('JAVA_HOME')
197
- warn " *** JAVA_HOME was set to #{ENV['JAVA_HOME'].inspect}"
198
- elsif File.directory?(local_java = '/usr/local/java/jdk') ||
199
- File.directory?(local_java = '/usr/lib/jvm/java-6-openjdk')
200
- then
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.5', '-target', '1.5', src
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/test_*.rb']
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/test_*.rb']
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, :gemspec, :test ]
334
+ task :default => [ :clean, :test ]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.6
1
+ 2.3.1
@@ -12,9 +12,6 @@
12
12
  #define RFLOAT_VALUE(val) (RFLOAT(val)->value)
13
13
  #endif
14
14
 
15
- #ifndef RARRAY_PTR
16
- #define RARRAY_PTR(ARRAY) RARRAY(ARRAY)->ptr
17
- #endif
18
15
  #ifndef RARRAY_LEN
19
16
  #define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
20
17
  #endif
@@ -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, CRegexp_MULTILINE, CJSON_SAFE_STATE_PROTOTYPE,
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
- i_quirks_mode, i_pack, i_unpack, i_create_id, i_extend, i_key_p,
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 = trailingBytesForUTF8[c] + 1;
284
- if (end + clen > len) {
285
- rb_raise(rb_path2class("JSON::GeneratorError"),
286
- "partial character in source, but hit end");
287
- }
288
- if (!isLegalUTF8((UTF8 *) p, clen)) {
289
- rb_raise(rb_path2class("JSON::GeneratorError"),
290
- "source sequence is illegal/malformed utf-8");
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
- memccpy(result, ptr, 0, len);
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 rb_ivar_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
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
- static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
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 i, j;
733
- VALUE key, key_to_s, keys;
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
- keys = rb_funcall(obj, i_keys, 0);
740
- for(i = 0; i < RARRAY_LEN(keys); i++) {
741
- if (i > 0) fbuffer_append(buffer, delim, delim_len);
742
- if (object_nl) {
743
- fbuffer_append(buffer, object_nl, object_nl_len);
744
- }
745
- if (indent) {
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
- obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8);
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
- if (!state->quirks_mode && !isArrayOrObject(result)) {
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
- * * *quirks_mode*: Enables quirks_mode for parser, that is for example
994
- * generating single JSON values instead of documents is possible.
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
- if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) {
1051
- CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE);
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 = strdup(RSTRING_PTR(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 = strdup(RSTRING_PTR(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 = strdup(RSTRING_PTR(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 = strdup(RSTRING_PTR(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 = strdup(RSTRING_PTR(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 NaN, Infinity, and -Infinity should be generated, otherwise
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
  }