json 2.2.0 → 2.5.0

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +51 -0
  3. data/LICENSE +56 -0
  4. data/README.md +17 -1
  5. data/VERSION +1 -1
  6. data/ext/json/ext/generator/generator.c +222 -48
  7. data/ext/json/ext/generator/generator.h +5 -2
  8. data/ext/json/ext/parser/extconf.rb +25 -0
  9. data/ext/json/ext/parser/parser.c +150 -102
  10. data/ext/json/ext/parser/parser.h +1 -0
  11. data/ext/json/ext/parser/parser.rl +80 -32
  12. data/ext/json/extconf.rb +1 -0
  13. data/json.gemspec +0 -0
  14. data/lib/json.rb +549 -29
  15. data/lib/json/add/bigdecimal.rb +2 -2
  16. data/lib/json/add/complex.rb +2 -3
  17. data/lib/json/add/rational.rb +2 -3
  18. data/lib/json/add/regexp.rb +2 -2
  19. data/lib/json/common.rb +370 -125
  20. data/lib/json/pure/generator.rb +31 -10
  21. data/lib/json/pure/parser.rb +31 -5
  22. data/lib/json/version.rb +1 -1
  23. data/tests/fixtures/fail29.json +1 -0
  24. data/tests/fixtures/fail30.json +1 -0
  25. data/tests/fixtures/fail31.json +1 -0
  26. data/tests/fixtures/fail32.json +1 -0
  27. data/tests/json_addition_test.rb +0 -4
  28. data/tests/json_common_interface_test.rb +47 -4
  29. data/tests/json_fixtures_test.rb +9 -1
  30. data/tests/json_generator_test.rb +30 -8
  31. data/tests/json_parser_test.rb +39 -14
  32. data/tests/lib/core_assertions.rb +763 -0
  33. data/tests/lib/envutil.rb +365 -0
  34. data/tests/lib/find_executable.rb +22 -0
  35. data/tests/lib/helper.rb +4 -0
  36. data/tests/ractor_test.rb +30 -0
  37. data/tests/test_helper.rb +3 -3
  38. metadata +30 -40
  39. data/.gitignore +0 -17
  40. data/.travis.yml +0 -23
  41. data/README-json-jruby.md +0 -33
  42. data/Rakefile +0 -408
  43. data/diagrams/.keep +0 -0
  44. data/install.rb +0 -23
  45. data/java/src/json/ext/ByteListTranscoder.java +0 -166
  46. data/java/src/json/ext/Generator.java +0 -443
  47. data/java/src/json/ext/GeneratorMethods.java +0 -231
  48. data/java/src/json/ext/GeneratorService.java +0 -42
  49. data/java/src/json/ext/GeneratorState.java +0 -490
  50. data/java/src/json/ext/OptionsReader.java +0 -113
  51. data/java/src/json/ext/Parser.java +0 -2362
  52. data/java/src/json/ext/Parser.rl +0 -893
  53. data/java/src/json/ext/ParserService.java +0 -34
  54. data/java/src/json/ext/RuntimeInfo.java +0 -116
  55. data/java/src/json/ext/StringDecoder.java +0 -166
  56. data/java/src/json/ext/StringEncoder.java +0 -111
  57. data/java/src/json/ext/Utils.java +0 -88
  58. data/json-java.gemspec +0 -38
  59. data/json_pure.gemspec +0 -38
  60. data/references/rfc7159.txt +0 -899
  61. data/tools/diff.sh +0 -18
  62. data/tools/fuzz.rb +0 -131
  63. data/tools/server.rb +0 -62
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d5aa01bc44bdf00e8465d5acbde009357987e98ac653672feba63b81f98dfc8
4
- data.tar.gz: 5845322c2daa41d815c4b845c6561d2addf86b459a5c093590317210d6d85901
3
+ metadata.gz: ae21d647db9c8291ae1fd3137d82feff3c0f7d575c244bd23902332179ef660b
4
+ data.tar.gz: d51e458a1de384797e9a11ca9413f74ff58f067e4478a4d54d220527387914fd
5
5
  SHA512:
6
- metadata.gz: f553341d9b8c4d20788255a35a48816a59cacb408f183e8a6009ab9326b66d80078961d4cc9da8e62155e4bb00f31dedddb449cf30afdb386ab6b49e248299dd
7
- data.tar.gz: e74601af87a7ec32952fd596b4981b0d26c4f96ddb88979a26b2fb06604dff204d38ba7c120cd0389756edb83cbd81b6fc569d16f5f7740b8b2915d63cdc8f22
6
+ metadata.gz: f7b8eaa3f4cb78f26cdb0a4629529372baa32b9e30f16bcfe8955581e6e886561f3da4b396fd711a1ffdad04dec9657f4f0953bf4283a99d9c5243353272ad11
7
+ data.tar.gz: fe145361cd4b6bfd2fdb038ae5ab373d9f641893f43dab26460bc085833687ce78e105f2b18cff2c11d4cf974a32da082954d0117fffbeb8f75548272392201b
data/CHANGES.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Changes
2
2
 
3
+ ## 2020-12-22 (2.5.0)
4
+
5
+ * Ready to Ractor-safe at Ruby 3.0.
6
+
7
+ ## 2020-12-17 (2.4.1)
8
+
9
+ * Restore version.rb with 2.4.1
10
+
11
+ ## 2020-12-15 (2.4.0)
12
+
13
+ * Implement a freeze: parser option #447
14
+ * Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449
15
+ * Fix JSON.load_file doc #448
16
+ * Fix pure parser with unclosed arrays / objects #425
17
+ * bundle the LICENSE file in the gem #444
18
+ * Add an option to escape forward slash character #405
19
+ * RDoc for JSON #439 #446 #442 #434 #433 #430
20
+
21
+ ## 2020-06-30 (2.3.1)
22
+
23
+ * Spelling and grammar fixes for comments. Pull request #191 by Josh
24
+ Kline.
25
+ * Enhance generic JSON and #generate docs. Pull request #347 by Victor
26
+ Shepelev.
27
+ * Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev.
28
+ * Baseline changes to help (JRuby) development. Pull request #371 by Karol
29
+ Bucek.
30
+ * Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI.
31
+ * Remove invalid JSON.generate description from JSON module rdoc. Pull
32
+ request #384 by Jeremy Evans.
33
+ * Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze.
34
+ * Rdoc enhancements. Pull request #413 by Burdette Lamar.
35
+ * Fixtures/ are not being tested... Pull request #416 by Marc-André
36
+ Lafortune.
37
+ * Use frozen string for hash key. Pull request #420 by Marc-André
38
+ Lafortune.
39
+ * Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette
40
+ Lamar.
41
+ * Small typo fix. Pull request #423 by Marc-André Lafortune.
42
+
43
+ ## 2019-12-11 (2.3.0)
44
+ * Fix default of `create_additions` to always be `false` for `JSON(user_input)`
45
+ and `JSON.parse(user_input, nil)`.
46
+ Note that `JSON.load` remains with default `true` and is meant for internal
47
+ serialization of trusted data. [CVE-2020-10663]
48
+ * Fix passing args all #to_json in json/add/*.
49
+ * Fix encoding issues
50
+ * Fix issues of keyword vs positional parameter
51
+ * Fix JSON::Parser against bigdecimal updates
52
+ * Bug fixes to JRuby port
53
+
3
54
  ## 2019-02-21 (2.2.0)
4
55
  * Adds support for 2.6 BigDecimal and ruby standard library Set datetype.
5
56
 
data/LICENSE ADDED
@@ -0,0 +1,56 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the
3
+ 2-clause BSDL (see the file BSDL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
data/README.md CHANGED
@@ -390,6 +390,22 @@ Here are the median comparisons for completeness' sake:
390
390
  secs/call
391
391
  ```
392
392
 
393
+ ## Development
394
+
395
+ ### Release
396
+
397
+ Update the json.gemspec and json-java.gemspec.
398
+
399
+ ```
400
+ rbenv shell 2.6.5
401
+ rake build
402
+ gem push pkg/json-2.3.0.gem
403
+
404
+ rbenv shell jruby-9.2.9.0
405
+ rake build
406
+ gem push pkg/json-2.3.0-java.gem
407
+ ```
408
+
393
409
  ## Author
394
410
 
395
411
  Florian Frank <mailto:flori@ping.de>
@@ -406,4 +422,4 @@ The latest version of this library can be downloaded at
406
422
 
407
423
  Online Documentation should be located at
408
424
 
409
- * http://json.rubyforge.org
425
+ * https://www.rubydoc.info/gems/json
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.0
1
+ 2.5.0
@@ -15,14 +15,13 @@ 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,
19
- i_SAFE_STATE_PROTOTYPE;
18
+ eNestingError;
20
19
 
21
20
  static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
22
21
  i_object_nl, i_array_nl, i_max_nesting, i_allow_nan, i_ascii_only,
23
22
  i_pack, i_unpack, i_create_id, i_extend, i_key_p,
24
23
  i_aref, i_send, i_respond_to_p, i_match, i_keys, i_depth,
25
- i_buffer_initial_length, i_dup;
24
+ i_buffer_initial_length, i_dup, i_escape_slash;
26
25
 
27
26
  /*
28
27
  * Copyright 2001-2004 Unicode, Inc.
@@ -130,7 +129,7 @@ static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16
130
129
 
131
130
  /* Converts string to a JSON string in FBuffer buffer, where all but the ASCII
132
131
  * and control characters are JSON escaped. */
133
- static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
132
+ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string, char escape_slash)
134
133
  {
135
134
  const UTF8 *source = (UTF8 *) RSTRING_PTR(string);
136
135
  const UTF8 *sourceEnd = source + RSTRING_LEN(string);
@@ -180,6 +179,11 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
180
179
  case '"':
181
180
  fbuffer_append(buffer, "\\\"", 2);
182
181
  break;
182
+ case '/':
183
+ if(escape_slash) {
184
+ fbuffer_append(buffer, "\\/", 2);
185
+ break;
186
+ }
183
187
  default:
184
188
  fbuffer_append_char(buffer, (char)ch);
185
189
  break;
@@ -229,7 +233,7 @@ static void convert_UTF8_to_JSON_ASCII(FBuffer *buffer, VALUE string)
229
233
  * characters required by the JSON standard are JSON escaped. The remaining
230
234
  * characters (should be UTF8) are just passed through and appended to the
231
235
  * result. */
232
- static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
236
+ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string, char escape_slash)
233
237
  {
234
238
  const char *ptr = RSTRING_PTR(string), *p;
235
239
  unsigned long len = RSTRING_LEN(string), start = 0, end = 0;
@@ -237,6 +241,7 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
237
241
  int escape_len;
238
242
  unsigned char c;
239
243
  char buf[6] = { '\\', 'u' };
244
+ int ascii_only = rb_enc_str_asciionly_p(string);
240
245
 
241
246
  for (start = 0, end = 0; end < len;) {
242
247
  p = ptr + end;
@@ -279,16 +284,25 @@ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string)
279
284
  escape = "\\\"";
280
285
  escape_len = 2;
281
286
  break;
287
+ case '/':
288
+ if(escape_slash) {
289
+ escape = "\\/";
290
+ escape_len = 2;
291
+ break;
292
+ }
282
293
  default:
283
294
  {
284
- unsigned short clen = trailingBytesForUTF8[c] + 1;
285
- if (end + clen > len) {
286
- rb_raise(rb_path2class("JSON::GeneratorError"),
287
- "partial character in source, but hit end");
288
- }
289
- if (!isLegalUTF8((UTF8 *) p, clen)) {
290
- rb_raise(rb_path2class("JSON::GeneratorError"),
291
- "source sequence is illegal/malformed utf-8");
295
+ unsigned short clen = 1;
296
+ if (!ascii_only) {
297
+ clen += trailingBytesForUTF8[c];
298
+ if (end + clen > len) {
299
+ rb_raise(rb_path2class("JSON::GeneratorError"),
300
+ "partial character in source, but hit end");
301
+ }
302
+ if (!isLegalUTF8((UTF8 *) p, clen)) {
303
+ rb_raise(rb_path2class("JSON::GeneratorError"),
304
+ "source sequence is illegal/malformed utf-8");
305
+ }
292
306
  }
293
307
  end += clen;
294
308
  }
@@ -324,6 +338,76 @@ static char *fstrndup(const char *ptr, unsigned long len) {
324
338
  *
325
339
  */
326
340
 
341
+ /* Explanation of the following: that's the only way to not pollute
342
+ * standard library's docs with GeneratorMethods::<ClassName> which
343
+ * are uninformative and take a large place in a list of classes
344
+ */
345
+
346
+ /*
347
+ * Document-module: JSON::Ext::Generator::GeneratorMethods
348
+ * :nodoc:
349
+ */
350
+
351
+ /*
352
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Array
353
+ * :nodoc:
354
+ */
355
+
356
+ /*
357
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Bignum
358
+ * :nodoc:
359
+ */
360
+
361
+ /*
362
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::FalseClass
363
+ * :nodoc:
364
+ */
365
+
366
+ /*
367
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Fixnum
368
+ * :nodoc:
369
+ */
370
+
371
+ /*
372
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Float
373
+ * :nodoc:
374
+ */
375
+
376
+ /*
377
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Hash
378
+ * :nodoc:
379
+ */
380
+
381
+ /*
382
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Integer
383
+ * :nodoc:
384
+ */
385
+
386
+ /*
387
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::NilClass
388
+ * :nodoc:
389
+ */
390
+
391
+ /*
392
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::Object
393
+ * :nodoc:
394
+ */
395
+
396
+ /*
397
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::String
398
+ * :nodoc:
399
+ */
400
+
401
+ /*
402
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::String::Extend
403
+ * :nodoc:
404
+ */
405
+
406
+ /*
407
+ * Document-module: JSON::Ext::Generator::GeneratorMethods::TrueClass
408
+ * :nodoc:
409
+ */
410
+
327
411
  /*
328
412
  * call-seq: to_json(state = nil)
329
413
  *
@@ -535,13 +619,18 @@ static size_t State_memsize(const void *ptr)
535
619
  return size;
536
620
  }
537
621
 
622
+ #ifndef HAVE_RB_EXT_RACTOR_SAFE
623
+ # undef RUBY_TYPED_FROZEN_SHAREABLE
624
+ # define RUBY_TYPED_FROZEN_SHAREABLE 0
625
+ #endif
626
+
538
627
  #ifdef NEW_TYPEDDATA_WRAPPER
539
628
  static const rb_data_type_t JSON_Generator_State_type = {
540
629
  "JSON/Generator/State",
541
630
  {NULL, State_free, State_memsize,},
542
631
  #ifdef RUBY_TYPED_FREE_IMMEDIATELY
543
632
  0, 0,
544
- RUBY_TYPED_FREE_IMMEDIATELY,
633
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
545
634
  #endif
546
635
  };
547
636
  #endif
@@ -642,6 +731,8 @@ static VALUE cState_configure(VALUE self, VALUE opts)
642
731
  state->allow_nan = RTEST(tmp);
643
732
  tmp = rb_hash_aref(opts, ID2SYM(i_ascii_only));
644
733
  state->ascii_only = RTEST(tmp);
734
+ tmp = rb_hash_aref(opts, ID2SYM(i_escape_slash));
735
+ state->escape_slash = RTEST(tmp);
645
736
  return self;
646
737
  }
647
738
 
@@ -676,6 +767,7 @@ static VALUE cState_to_h(VALUE self)
676
767
  rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
677
768
  rb_hash_aset(result, ID2SYM(i_ascii_only), state->ascii_only ? Qtrue : Qfalse);
678
769
  rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
770
+ rb_hash_aset(result, ID2SYM(i_escape_slash), state->escape_slash ? Qtrue : Qfalse);
679
771
  rb_hash_aset(result, ID2SYM(i_depth), LONG2FIX(state->depth));
680
772
  rb_hash_aset(result, ID2SYM(i_buffer_initial_length), LONG2FIX(state->buffer_initial_length));
681
773
  return result;
@@ -692,7 +784,7 @@ static VALUE cState_aref(VALUE self, VALUE name)
692
784
  if (RTEST(rb_funcall(self, i_respond_to_p, 1, name))) {
693
785
  return rb_funcall(self, i_send, 1, name);
694
786
  } else {
695
- return rb_ivar_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
787
+ return rb_attr_get(self, rb_intern_str(rb_str_concat(rb_str_new2("@"), name)));
696
788
  }
697
789
  }
698
790
 
@@ -715,43 +807,83 @@ static VALUE cState_aset(VALUE self, VALUE name, VALUE value)
715
807
  return Qnil;
716
808
  }
717
809
 
718
- static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
810
+ struct hash_foreach_arg {
811
+ FBuffer *buffer;
812
+ JSON_Generator_State *state;
813
+ VALUE Vstate;
814
+ int iter;
815
+ };
816
+
817
+ static int
818
+ json_object_i(VALUE key, VALUE val, VALUE _arg)
719
819
  {
820
+ struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg;
821
+ FBuffer *buffer = arg->buffer;
822
+ JSON_Generator_State *state = arg->state;
823
+ VALUE Vstate = arg->Vstate;
824
+
720
825
  char *object_nl = state->object_nl;
721
826
  long object_nl_len = state->object_nl_len;
722
827
  char *indent = state->indent;
723
828
  long indent_len = state->indent_len;
724
- long max_nesting = state->max_nesting;
725
829
  char *delim = FBUFFER_PTR(state->object_delim);
726
830
  long delim_len = FBUFFER_LEN(state->object_delim);
727
831
  char *delim2 = FBUFFER_PTR(state->object_delim2);
728
832
  long delim2_len = FBUFFER_LEN(state->object_delim2);
833
+ long depth = state->depth;
834
+ int j;
835
+ VALUE klass, key_to_s;
836
+
837
+ if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len);
838
+ if (object_nl) {
839
+ fbuffer_append(buffer, object_nl, object_nl_len);
840
+ }
841
+ if (indent) {
842
+ for (j = 0; j < depth; j++) {
843
+ fbuffer_append(buffer, indent, indent_len);
844
+ }
845
+ }
846
+
847
+ klass = CLASS_OF(key);
848
+ if (klass == rb_cString) {
849
+ key_to_s = key;
850
+ } else if (klass == rb_cSymbol) {
851
+ key_to_s = rb_id2str(SYM2ID(key));
852
+ } else {
853
+ key_to_s = rb_funcall(key, i_to_s, 0);
854
+ }
855
+ Check_Type(key_to_s, T_STRING);
856
+ generate_json(buffer, Vstate, state, key_to_s);
857
+ fbuffer_append(buffer, delim2, delim2_len);
858
+ generate_json(buffer, Vstate, state, val);
859
+
860
+ arg->iter++;
861
+ return ST_CONTINUE;
862
+ }
863
+
864
+ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
865
+ {
866
+ char *object_nl = state->object_nl;
867
+ long object_nl_len = state->object_nl_len;
868
+ char *indent = state->indent;
869
+ long indent_len = state->indent_len;
870
+ long max_nesting = state->max_nesting;
729
871
  long depth = ++state->depth;
730
- int i, j;
731
- VALUE key, key_to_s, keys;
872
+ int j;
873
+ struct hash_foreach_arg arg;
874
+
732
875
  if (max_nesting != 0 && depth > max_nesting) {
733
876
  fbuffer_free(buffer);
734
877
  rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
735
878
  }
736
879
  fbuffer_append_char(buffer, '{');
737
- keys = rb_funcall(obj, i_keys, 0);
738
- for(i = 0; i < RARRAY_LEN(keys); i++) {
739
- if (i > 0) fbuffer_append(buffer, delim, delim_len);
740
- if (object_nl) {
741
- fbuffer_append(buffer, object_nl, object_nl_len);
742
- }
743
- if (indent) {
744
- for (j = 0; j < depth; j++) {
745
- fbuffer_append(buffer, indent, indent_len);
746
- }
747
- }
748
- key = rb_ary_entry(keys, i);
749
- key_to_s = rb_funcall(key, i_to_s, 0);
750
- Check_Type(key_to_s, T_STRING);
751
- generate_json(buffer, Vstate, state, key_to_s);
752
- fbuffer_append(buffer, delim2, delim2_len);
753
- generate_json(buffer, Vstate, state, rb_hash_aref(obj, key));
754
- }
880
+
881
+ arg.buffer = buffer;
882
+ arg.state = state;
883
+ arg.Vstate = Vstate;
884
+ arg.iter = 0;
885
+ rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
886
+
755
887
  depth = --state->depth;
756
888
  if (object_nl) {
757
889
  fbuffer_append(buffer, object_nl, object_nl_len);
@@ -802,16 +934,27 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
802
934
  fbuffer_append_char(buffer, ']');
803
935
  }
804
936
 
937
+ #ifdef HAVE_RUBY_ENCODING_H
938
+ static int enc_utf8_compatible_p(rb_encoding *enc)
939
+ {
940
+ if (enc == rb_usascii_encoding()) return 1;
941
+ if (enc == rb_utf8_encoding()) return 1;
942
+ return 0;
943
+ }
944
+ #endif
945
+
805
946
  static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
806
947
  {
807
948
  fbuffer_append_char(buffer, '"');
808
949
  #ifdef HAVE_RUBY_ENCODING_H
809
- obj = rb_funcall(obj, i_encode, 1, CEncoding_UTF_8);
950
+ if (!enc_utf8_compatible_p(rb_enc_get(obj))) {
951
+ obj = rb_str_encode(obj, CEncoding_UTF_8, 0, Qnil);
952
+ }
810
953
  #endif
811
954
  if (state->ascii_only) {
812
- convert_UTF8_to_JSON_ASCII(buffer, obj);
955
+ convert_UTF8_to_JSON_ASCII(buffer, obj, state->escape_slash);
813
956
  } else {
814
- convert_UTF8_to_JSON(buffer, obj);
957
+ convert_UTF8_to_JSON(buffer, obj, state->escape_slash);
815
958
  }
816
959
  fbuffer_append_char(buffer, '"');
817
960
  }
@@ -970,6 +1113,8 @@ static VALUE cState_generate(VALUE self, VALUE obj)
970
1113
  * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
971
1114
  * generated, otherwise an exception is thrown, if these values are
972
1115
  * encountered. This options defaults to false.
1116
+ * * *ascii_only*: true if only ASCII characters should be generated. This
1117
+ * option defaults to false.
973
1118
  * * *buffer_initial_length*: sets the initial length of the generator's
974
1119
  * internal buffer.
975
1120
  */
@@ -1025,10 +1170,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
1025
1170
  } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
1026
1171
  return rb_funcall(self, i_new, 1, opts);
1027
1172
  } else {
1028
- if (NIL_P(CJSON_SAFE_STATE_PROTOTYPE)) {
1029
- CJSON_SAFE_STATE_PROTOTYPE = rb_const_get(mJSON, i_SAFE_STATE_PROTOTYPE);
1030
- }
1031
- return rb_funcall(CJSON_SAFE_STATE_PROTOTYPE, i_dup, 0);
1173
+ return rb_class_new_instance(0, NULL, cState);
1032
1174
  }
1033
1175
  }
1034
1176
 
@@ -1252,6 +1394,31 @@ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
1252
1394
  return state->max_nesting = FIX2LONG(depth);
1253
1395
  }
1254
1396
 
1397
+ /*
1398
+ * call-seq: escape_slash
1399
+ *
1400
+ * If this boolean is true, the forward slashes will be escaped in
1401
+ * the json output.
1402
+ */
1403
+ static VALUE cState_escape_slash(VALUE self)
1404
+ {
1405
+ GET_STATE(self);
1406
+ return state->escape_slash ? Qtrue : Qfalse;
1407
+ }
1408
+
1409
+ /*
1410
+ * call-seq: escape_slash=(depth)
1411
+ *
1412
+ * This sets whether or not the forward slashes will be escaped in
1413
+ * the json output.
1414
+ */
1415
+ static VALUE cState_escape_slash_set(VALUE self, VALUE enable)
1416
+ {
1417
+ GET_STATE(self);
1418
+ state->escape_slash = RTEST(enable);
1419
+ return Qnil;
1420
+ }
1421
+
1255
1422
  /*
1256
1423
  * call-seq: allow_nan?
1257
1424
  *
@@ -1267,7 +1434,7 @@ static VALUE cState_allow_nan_p(VALUE self)
1267
1434
  /*
1268
1435
  * call-seq: ascii_only?
1269
1436
  *
1270
- * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
1437
+ * Returns true, if only ASCII characters should be generated. Otherwise
1271
1438
  * returns false.
1272
1439
  */
1273
1440
  static VALUE cState_ascii_only_p(VALUE self)
@@ -1335,6 +1502,10 @@ static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_l
1335
1502
  */
1336
1503
  void Init_generator(void)
1337
1504
  {
1505
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
1506
+ rb_ext_ractor_safe(true);
1507
+ #endif
1508
+
1338
1509
  #undef rb_intern
1339
1510
  rb_require("json/common");
1340
1511
 
@@ -1344,6 +1515,8 @@ void Init_generator(void)
1344
1515
 
1345
1516
  eGeneratorError = rb_path2class("JSON::GeneratorError");
1346
1517
  eNestingError = rb_path2class("JSON::NestingError");
1518
+ rb_gc_register_mark_object(eGeneratorError);
1519
+ rb_gc_register_mark_object(eNestingError);
1347
1520
 
1348
1521
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
1349
1522
  rb_define_alloc_func(cState, cState_s_allocate);
@@ -1362,6 +1535,9 @@ void Init_generator(void)
1362
1535
  rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
1363
1536
  rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
1364
1537
  rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
1538
+ rb_define_method(cState, "escape_slash", cState_escape_slash, 0);
1539
+ rb_define_method(cState, "escape_slash?", cState_escape_slash, 0);
1540
+ rb_define_method(cState, "escape_slash=", cState_escape_slash_set, 1);
1365
1541
  rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
1366
1542
  rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
1367
1543
  rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
@@ -1409,7 +1585,6 @@ void Init_generator(void)
1409
1585
  mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
1410
1586
  rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
1411
1587
 
1412
- CRegexp_MULTILINE = rb_const_get(rb_cRegexp, rb_intern("MULTILINE"));
1413
1588
  i_to_s = rb_intern("to_s");
1414
1589
  i_to_json = rb_intern("to_json");
1415
1590
  i_new = rb_intern("new");
@@ -1419,6 +1594,7 @@ void Init_generator(void)
1419
1594
  i_object_nl = rb_intern("object_nl");
1420
1595
  i_array_nl = rb_intern("array_nl");
1421
1596
  i_max_nesting = rb_intern("max_nesting");
1597
+ i_escape_slash = rb_intern("escape_slash");
1422
1598
  i_allow_nan = rb_intern("allow_nan");
1423
1599
  i_ascii_only = rb_intern("ascii_only");
1424
1600
  i_depth = rb_intern("depth");
@@ -1439,6 +1615,4 @@ void Init_generator(void)
1439
1615
  i_encoding = rb_intern("encoding");
1440
1616
  i_encode = rb_intern("encode");
1441
1617
  #endif
1442
- i_SAFE_STATE_PROTOTYPE = rb_intern("SAFE_STATE_PROTOTYPE");
1443
- CJSON_SAFE_STATE_PROTOTYPE = Qnil;
1444
1618
  }