oj 3.12.2 → 3.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -3
  3. data/ext/oj/buf.h +9 -0
  4. data/ext/oj/cache.c +193 -0
  5. data/ext/oj/cache.h +20 -0
  6. data/ext/oj/compat.c +8 -22
  7. data/ext/oj/custom.c +15 -14
  8. data/ext/oj/debug.c +132 -0
  9. data/ext/oj/dump.c +12 -15
  10. data/ext/oj/dump_compat.c +3 -3
  11. data/ext/oj/dump_object.c +9 -9
  12. data/ext/oj/dump_strict.c +3 -3
  13. data/ext/oj/err.h +19 -0
  14. data/ext/oj/extconf.rb +4 -0
  15. data/ext/oj/fast.c +7 -18
  16. data/ext/oj/intern.c +398 -0
  17. data/ext/oj/intern.h +27 -0
  18. data/ext/oj/mimic_json.c +9 -9
  19. data/ext/oj/object.c +11 -59
  20. data/ext/oj/odd.c +1 -1
  21. data/ext/oj/oj.c +167 -109
  22. data/ext/oj/oj.h +2 -2
  23. data/ext/oj/parse.c +5 -5
  24. data/ext/oj/parser.c +1512 -0
  25. data/ext/oj/parser.h +90 -0
  26. data/ext/oj/rails.c +5 -5
  27. data/ext/oj/resolve.c +2 -20
  28. data/ext/oj/rxclass.c +1 -1
  29. data/ext/oj/saj.c +1 -1
  30. data/ext/oj/saj2.c +348 -0
  31. data/ext/oj/scp.c +1 -1
  32. data/ext/oj/sparse.c +2 -2
  33. data/ext/oj/stream_writer.c +4 -4
  34. data/ext/oj/strict.c +10 -27
  35. data/ext/oj/string_writer.c +2 -2
  36. data/ext/oj/usual.c +1228 -0
  37. data/ext/oj/validate.c +51 -0
  38. data/ext/oj/wab.c +9 -17
  39. data/lib/oj/error.rb +1 -1
  40. data/lib/oj/mimic.rb +1 -1
  41. data/lib/oj/version.rb +1 -1
  42. data/pages/Modes.md +2 -0
  43. data/pages/Options.md +17 -5
  44. data/pages/Parser.md +309 -0
  45. data/pages/Rails.md +2 -2
  46. data/test/json_gem/json_generator_test.rb +1 -1
  47. data/test/perf_parser.rb +184 -0
  48. data/test/test_hash.rb +1 -1
  49. data/test/test_parser.rb +27 -0
  50. data/test/test_parser_saj.rb +245 -0
  51. data/test/test_parser_usual.rb +213 -0
  52. metadata +22 -5
  53. data/ext/oj/hash.c +0 -168
  54. data/ext/oj/hash.h +0 -21
  55. data/ext/oj/hash_test.c +0 -491
data/ext/oj/intern.h ADDED
@@ -0,0 +1,27 @@
1
+ // Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #ifndef OJ_INTERN_H
5
+ #define OJ_INTERN_H
6
+
7
+ #include <stdbool.h>
8
+ #include <ruby.h>
9
+
10
+ struct _parseInfo;
11
+
12
+ extern void oj_hash_init();
13
+
14
+ extern VALUE oj_str_intern(const char *key, size_t len);
15
+ extern VALUE oj_sym_intern(const char *key, size_t len);
16
+ extern ID oj_attr_intern(const char *key, size_t len);
17
+ extern VALUE oj_class_intern(const char * key,
18
+ size_t len,
19
+ bool safe,
20
+ struct _parseInfo *pi,
21
+ int auto_define,
22
+ VALUE error_class);
23
+
24
+ extern void oj_hash_print();
25
+ extern char *oj_strndup(const char *s, size_t len);
26
+
27
+ #endif /* OJ_INTERN_H */
data/ext/oj/mimic_json.c CHANGED
@@ -464,19 +464,19 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
464
464
  } else {
465
465
  h = argv[1];
466
466
  }
467
- if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_indent_sym)) {
467
+ if (!oj_hash_has_key(h, oj_indent_sym)) {
468
468
  rb_hash_aset(h, oj_indent_sym, rb_str_new2(" "));
469
469
  }
470
- if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_space_before_sym)) {
470
+ if (!oj_hash_has_key(h, oj_space_before_sym)) {
471
471
  rb_hash_aset(h, oj_space_before_sym, rb_str_new2(""));
472
472
  }
473
- if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_space_sym)) {
473
+ if (!oj_hash_has_key(h, oj_space_sym)) {
474
474
  rb_hash_aset(h, oj_space_sym, rb_str_new2(" "));
475
475
  }
476
- if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_object_nl_sym)) {
476
+ if (!oj_hash_has_key(h, oj_object_nl_sym)) {
477
477
  rb_hash_aset(h, oj_object_nl_sym, rb_str_new2("\n"));
478
478
  }
479
- if (Qfalse == rb_funcall(h, oj_has_key_id, 1, oj_array_nl_sym)) {
479
+ if (!oj_hash_has_key(h, oj_array_nl_sym)) {
480
480
  rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
481
481
  }
482
482
  if (Qundef == state_class) {
@@ -548,7 +548,7 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
548
548
  pi.options.allow_nan = (Qtrue == v) ? Yes : No;
549
549
  }
550
550
 
551
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
551
+ if (oj_hash_has_key(ropts, oj_hash_class_sym)) {
552
552
  if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
553
553
  pi.options.hash_class = Qnil;
554
554
  } else {
@@ -556,7 +556,7 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
556
556
  pi.options.hash_class = v;
557
557
  }
558
558
  }
559
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
559
+ if (oj_hash_has_key(ropts, oj_object_class_sym)) {
560
560
  if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
561
561
  pi.options.hash_class = Qnil;
562
562
  } else {
@@ -564,7 +564,7 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
564
564
  pi.options.hash_class = v;
565
565
  }
566
566
  }
567
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
567
+ if (oj_hash_has_key(ropts, oj_array_class_sym)) {
568
568
  if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
569
569
  pi.options.array_class = Qnil;
570
570
  } else {
@@ -572,7 +572,7 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
572
572
  pi.options.array_class = v;
573
573
  }
574
574
  }
575
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
575
+ if (oj_hash_has_key(ropts, oj_decimal_class_sym)) {
576
576
  pi.options.compat_bigdec = (oj_bigdecimal_class ==
577
577
  rb_hash_lookup(ropts, oj_decimal_class_sym));
578
578
  }
data/ext/oj/object.c CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  #include "encode.h"
9
9
  #include "err.h"
10
- #include "hash.h"
10
+ #include "intern.h"
11
11
  #include "odd.h"
12
12
  #include "oj.h"
13
13
  #include "parse.h"
@@ -59,17 +59,17 @@ static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
59
59
  }
60
60
  #else
61
61
  if (':' == k1) {
62
- rkey = rb_str_new(kval->key + 1, kval->klen - 1);
63
- rkey = oj_encode(rkey);
64
- rkey = rb_str_intern(rkey);
62
+ rkey = ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
65
63
  } else {
66
- rkey = rb_str_new(kval->key, kval->klen);
67
- rkey = oj_encode(rkey);
68
64
  if (Yes == pi->options.sym_key) {
69
- rkey = rb_str_intern(rkey);
65
+ rkey = ID2SYM(rb_intern3(kval->key, kval->klen, oj_utf8_encoding));
66
+ } else {
67
+ rkey = rb_str_new(kval->key, kval->klen);
68
+ rkey = oj_encode(rkey);
70
69
  }
71
70
  }
72
71
  #endif
72
+ OBJ_FREEZE(rkey);
73
73
  return rkey;
74
74
  }
75
75
 
@@ -77,9 +77,7 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
77
77
  volatile VALUE rstr = Qnil;
78
78
 
79
79
  if (':' == *orig && 0 < len) {
80
- rstr = rb_str_new(str + 1, len - 1);
81
- rstr = oj_encode(rstr);
82
- rstr = rb_funcall(rstr, oj_to_sym_id, 0);
80
+ rstr = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
83
81
  } else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
84
82
  long i = read_long(str + 2, len - 2);
85
83
 
@@ -258,9 +256,7 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
258
256
  parent->odd_args = oj_odd_alloc_args(odd);
259
257
  } break;
260
258
  case 'm':
261
- parent->val = rb_str_new(str + 1, len - 1);
262
- parent->val = oj_encode(parent->val);
263
- parent->val = rb_funcall(parent->val, oj_to_sym_id, 0);
259
+ parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
264
260
  break;
265
261
  case 's':
266
262
  parent->val = rb_str_new(str, len);
@@ -305,7 +301,7 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
305
301
  }
306
302
  if (86400 == ni->exp) { // UTC time
307
303
  parent->val = rb_time_nano_new(ni->i, (long)nsec);
308
- // Since the ruby C routines alway create local time, the
304
+ // Since the ruby C routines always create local time, the
309
305
  // offset and then a conversion to UTC keeps makes the time
310
306
  // match the expected value.
311
307
  parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
@@ -416,51 +412,7 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
416
412
  }
417
413
 
418
414
  void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
419
- const char *key = kval->key;
420
- int klen = kval->klen;
421
- ID var_id;
422
- ID * slot;
423
-
424
- #ifdef HAVE_PTHREAD_MUTEX_INIT
425
- pthread_mutex_lock(&oj_cache_mutex);
426
- #else
427
- rb_mutex_lock(oj_cache_mutex);
428
- #endif
429
- if (0 == (var_id = oj_attr_hash_get(key, klen, &slot))) {
430
- char attr[256];
431
-
432
- if ((int)sizeof(attr) <= klen + 2) {
433
- char *buf = ALLOC_N(char, klen + 2);
434
-
435
- if ('~' == *key) {
436
- memcpy(buf, key + 1, klen - 1);
437
- buf[klen - 1] = '\0';
438
- } else {
439
- *buf = '@';
440
- memcpy(buf + 1, key, klen);
441
- buf[klen + 1] = '\0';
442
- }
443
- var_id = rb_intern(buf);
444
- xfree(buf);
445
- } else {
446
- if ('~' == *key) {
447
- memcpy(attr, key + 1, klen - 1);
448
- attr[klen - 1] = '\0';
449
- } else {
450
- *attr = '@';
451
- memcpy(attr + 1, key, klen);
452
- attr[klen + 1] = '\0';
453
- }
454
- var_id = rb_intern(attr);
455
- }
456
- *slot = var_id;
457
- }
458
- #ifdef HAVE_PTHREAD_MUTEX_INIT
459
- pthread_mutex_unlock(&oj_cache_mutex);
460
- #else
461
- rb_mutex_unlock(oj_cache_mutex);
462
- #endif
463
- rb_ivar_set(parent->val, var_id, value);
415
+ rb_ivar_set(parent->val, oj_attr_intern(kval->key, kval->klen), value);
464
416
  }
465
417
 
466
418
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
data/ext/oj/odd.c CHANGED
@@ -206,7 +206,7 @@ void oj_reg_odd(VALUE clas,
206
206
  *fp = 0;
207
207
  switch (rb_type(*members)) {
208
208
  case T_STRING:
209
- if (NULL == (*np = strdup(rb_string_value_ptr(members)))) {
209
+ if (NULL == (*np = strdup(RSTRING_PTR(*members)))) {
210
210
  rb_raise(rb_eNoMemError, "for attribute name.");
211
211
  }
212
212
  break;
data/ext/oj/oj.c CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  #include "dump.h"
15
15
  #include "encode.h"
16
- #include "hash.h"
16
+ #include "intern.h"
17
17
  #include "odd.h"
18
18
  #include "parse.h"
19
19
  #include "rails.h"
@@ -40,7 +40,6 @@ ID oj_error_id;
40
40
  ID oj_file_id;
41
41
  ID oj_fileno_id;
42
42
  ID oj_ftype_id;
43
- ID oj_has_key_id;
44
43
  ID oj_hash_end_id;
45
44
  ID oj_hash_key_id;
46
45
  ID oj_hash_set_id;
@@ -159,6 +158,8 @@ pthread_mutex_t oj_cache_mutex;
159
158
  VALUE oj_cache_mutex = Qnil;
160
159
  #endif
161
160
 
161
+ extern void oj_parser_init();
162
+
162
163
  const char oj_json_class[] = "json_class";
163
164
 
164
165
  struct _options oj_default_options = {
@@ -508,7 +509,7 @@ static VALUE get_def_opts(VALUE self) {
508
509
  * Sets the default options for load and dump.
509
510
  * - *opts* [_Hash_] options to change
510
511
  * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in a JSON
511
- *document or the String to use for identation.
512
+ *document or the String to use for indentation.
512
513
  * - :circular [_Boolean_|_nil_] support circular references while dumping.
513
514
  * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist.
514
515
  * - *:symbol_keys* [_Boolean_|_nil_] convert hash keys to symbols.
@@ -582,7 +583,16 @@ static VALUE set_def_opts(VALUE self, VALUE opts) {
582
583
  return Qnil;
583
584
  }
584
585
 
585
- void oj_parse_options(VALUE ropts, Options copts) {
586
+ bool oj_hash_has_key(VALUE hash, VALUE key)
587
+ {
588
+ if (Qundef == rb_hash_lookup2(hash, key, Qundef)) {
589
+ return false;
590
+ }
591
+ return true;
592
+ }
593
+
594
+ bool set_yesno_options(VALUE key, VALUE value, Options copts)
595
+ {
586
596
  struct _yesNoOpt ynos[] = {{circular_sym, &copts->circular},
587
597
  {auto_define_sym, &copts->auto_define},
588
598
  {symbol_keys_sym, &copts->sym_key},
@@ -605,15 +615,37 @@ void oj_parse_options(VALUE ropts, Options copts) {
605
615
  {oj_create_additions_sym, &copts->create_ok},
606
616
  {cache_keys_sym, &copts->cache_keys},
607
617
  {Qnil, 0}};
608
- YesNoOpt o;
609
- volatile VALUE v;
610
- size_t len;
618
+ YesNoOpt o;
619
+
620
+ for (o = ynos; 0 != o->attr; o++) {
621
+ if (key == o->sym) {
622
+ if (Qnil == value) {
623
+ *o->attr = NotSet;
624
+ } else if (Qtrue == value) {
625
+ *o->attr = Yes;
626
+ } else if (Qfalse == value) {
627
+ *o->attr = No;
628
+ } else {
629
+ rb_raise(rb_eArgError,
630
+ "%s must be true, false, or nil.",
631
+ rb_id2name(key));
632
+ }
633
+ return true;
634
+ }
635
+ }
636
+ return false;
637
+ }
611
638
 
612
- if (T_HASH != rb_type(ropts)) {
613
- return;
639
+ static int parse_options_cb(VALUE k, VALUE v, VALUE opts)
640
+ {
641
+ Options copts = (Options)opts;
642
+ size_t len;
643
+
644
+ if (set_yesno_options(k, v, copts)) {
645
+ return ST_CONTINUE;
614
646
  }
615
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_indent_sym)) {
616
- v = rb_hash_lookup(ropts, oj_indent_sym);
647
+
648
+ if (oj_indent_sym == k) {
617
649
  switch (rb_type(v)) {
618
650
  case T_NIL:
619
651
  copts->dump_opts.indent_size = 0;
@@ -637,8 +669,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
637
669
  break;
638
670
  default: rb_raise(rb_eTypeError, "indent must be a Fixnum, String, or nil."); break;
639
671
  }
640
- }
641
- if (Qnil != (v = rb_hash_lookup(ropts, float_prec_sym))) {
672
+ } else if (float_prec_sym == k) {
642
673
  int n;
643
674
 
644
675
  #ifdef RUBY_INTEGER_UNIFICATION
@@ -661,8 +692,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
661
692
  sprintf(copts->float_fmt, "%%0.%dg", n);
662
693
  copts->float_prec = n;
663
694
  }
664
- }
665
- if (Qnil != (v = rb_hash_lookup(ropts, cache_str_sym))) {
695
+ } else if (cache_str_sym == k) {
666
696
  int n;
667
697
 
668
698
  #ifdef RUBY_INTEGER_UNIFICATION
@@ -683,8 +713,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
683
713
  }
684
714
  copts->cache_str = (char)n;
685
715
  }
686
- }
687
- if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
716
+ } else if (sec_prec_sym == k) {
688
717
  int n;
689
718
 
690
719
  #ifdef RUBY_INTEGER_UNIFICATION
@@ -707,8 +736,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
707
736
  copts->sec_prec_set = true;
708
737
  }
709
738
  copts->sec_prec = n;
710
- }
711
- if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
739
+ } else if (mode_sym == k) {
712
740
  if (wab_sym == v) {
713
741
  copts->mode = WabMode;
714
742
  } else if (object_sym == v) {
@@ -727,8 +755,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
727
755
  rb_raise(rb_eArgError,
728
756
  ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
729
757
  }
730
- }
731
- if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
758
+ } else if (time_format_sym == k) {
732
759
  if (unix_sym == v) {
733
760
  copts->time_format = UnixTime;
734
761
  } else if (unix_zone_sym == v) {
@@ -740,8 +767,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
740
767
  } else {
741
768
  rb_raise(rb_eArgError, ":time_format must be :unix, :unix_zone, :xmlschema, or :ruby.");
742
769
  }
743
- }
744
- if (Qnil != (v = rb_hash_lookup(ropts, escape_mode_sym))) {
770
+ } else if (escape_mode_sym == k) {
745
771
  if (newline_sym == v) {
746
772
  copts->escape_mode = NLEsc;
747
773
  } else if (json_sym == v) {
@@ -756,8 +782,11 @@ void oj_parse_options(VALUE ropts, Options copts) {
756
782
  rb_raise(rb_eArgError,
757
783
  ":encoding must be :newline, :json, :xss_safe, :unicode_xss, or :ascii.");
758
784
  }
759
- }
760
- if (Qnil != (v = rb_hash_lookup(ropts, bigdecimal_load_sym))) {
785
+ } else if (bigdecimal_load_sym == k) {
786
+ if (Qnil == v) {
787
+ return ST_CONTINUE;
788
+ }
789
+
761
790
  if (bigdecimal_sym == v || Qtrue == v) {
762
791
  copts->bigdec_load = BigDec;
763
792
  } else if (float_sym == v) {
@@ -769,12 +798,13 @@ void oj_parse_options(VALUE ropts, Options copts) {
769
798
  } else {
770
799
  rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
771
800
  }
772
- }
773
- if (Qnil != (v = rb_hash_lookup(ropts, compat_bigdecimal_sym))) {
801
+ } else if (compat_bigdecimal_sym == k) {
802
+ if (Qnil == v) {
803
+ return ST_CONTINUE;
804
+ }
805
+
774
806
  copts->compat_bigdec = (Qtrue == v);
775
- }
776
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
777
- v = rb_hash_lookup(ropts, oj_decimal_class_sym);
807
+ } else if (oj_decimal_class_sym == k) {
778
808
  if (rb_cFloat == v) {
779
809
  copts->compat_bigdec = false;
780
810
  } else if (oj_bigdecimal_class == v) {
@@ -782,9 +812,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
782
812
  } else {
783
813
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
784
814
  }
785
- }
786
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, create_id_sym)) {
787
- v = rb_hash_lookup(ropts, create_id_sym);
815
+ } else if (create_id_sym == k) {
788
816
  if (Qnil == v) {
789
817
  if (oj_json_class != oj_default_options.create_id && NULL != copts->create_id) {
790
818
  xfree((char *)oj_default_options.create_id);
@@ -803,25 +831,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
803
831
  } else {
804
832
  rb_raise(rb_eArgError, ":create_id must be string.");
805
833
  }
806
- }
807
- for (o = ynos; 0 != o->attr; o++) {
808
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, o->sym)) {
809
- v = rb_hash_lookup(ropts, o->sym);
810
- if (Qnil == v) {
811
- *o->attr = NotSet;
812
- } else if (Qtrue == v) {
813
- *o->attr = Yes;
814
- } else if (Qfalse == v) {
815
- *o->attr = No;
816
- } else {
817
- rb_raise(rb_eArgError,
818
- "%s must be true, false, or nil.",
819
- rb_id2name(SYM2ID(o->sym)));
820
- }
821
- }
822
- }
823
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_space_sym)) {
824
- if (Qnil == (v = rb_hash_lookup(ropts, oj_space_sym))) {
834
+ } else if (oj_space_sym == k) {
835
+ if (Qnil == v) {
825
836
  copts->dump_opts.after_size = 0;
826
837
  *copts->dump_opts.after_sep = '\0';
827
838
  } else {
@@ -834,9 +845,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
834
845
  strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
835
846
  copts->dump_opts.after_size = (uint8_t)len;
836
847
  }
837
- }
838
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_space_before_sym)) {
839
- if (Qnil == (v = rb_hash_lookup(ropts, oj_space_before_sym))) {
848
+ } else if (oj_space_before_sym == k) {
849
+ if (Qnil == v) {
840
850
  copts->dump_opts.before_size = 0;
841
851
  *copts->dump_opts.before_sep = '\0';
842
852
  } else {
@@ -849,9 +859,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
849
859
  strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
850
860
  copts->dump_opts.before_size = (uint8_t)len;
851
861
  }
852
- }
853
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_nl_sym)) {
854
- if (Qnil == (v = rb_hash_lookup(ropts, oj_object_nl_sym))) {
862
+ } else if (oj_object_nl_sym == k) {
863
+ if (Qnil == v) {
855
864
  copts->dump_opts.hash_size = 0;
856
865
  *copts->dump_opts.hash_nl = '\0';
857
866
  } else {
@@ -864,9 +873,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
864
873
  strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
865
874
  copts->dump_opts.hash_size = (uint8_t)len;
866
875
  }
867
- }
868
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_nl_sym)) {
869
- if (Qnil == (v = rb_hash_lookup(ropts, oj_array_nl_sym))) {
876
+ } else if (oj_array_nl_sym == k) {
877
+ if (Qnil == v) {
870
878
  copts->dump_opts.array_size = 0;
871
879
  *copts->dump_opts.array_nl = '\0';
872
880
  } else {
@@ -879,8 +887,11 @@ void oj_parse_options(VALUE ropts, Options copts) {
879
887
  strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
880
888
  copts->dump_opts.array_size = (uint8_t)len;
881
889
  }
882
- }
883
- if (Qnil != (v = rb_hash_lookup(ropts, nan_sym))) {
890
+ } else if (nan_sym == k) {
891
+ if (Qnil == v) {
892
+ return ST_CONTINUE;
893
+ }
894
+
884
895
  if (null_sym == v) {
885
896
  copts->dump_opts.nan_dump = NullNan;
886
897
  } else if (huge_sym == v) {
@@ -894,11 +905,11 @@ void oj_parse_options(VALUE ropts, Options copts) {
894
905
  } else {
895
906
  rb_raise(rb_eArgError, ":nan must be :null, :huge, :word, :raise, or :auto.");
896
907
  }
897
- }
898
- copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
899
- 0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
900
- 0 < copts->dump_opts.array_size);
901
- if (Qnil != (v = rb_hash_lookup(ropts, omit_nil_sym))) {
908
+ } else if (omit_nil_sym == k) {
909
+ if (Qnil == v) {
910
+ return ST_CONTINUE;
911
+ }
912
+
902
913
  if (Qtrue == v) {
903
914
  copts->dump_opts.omit_nil = true;
904
915
  } else if (Qfalse == v) {
@@ -906,43 +917,38 @@ void oj_parse_options(VALUE ropts, Options copts) {
906
917
  } else {
907
918
  rb_raise(rb_eArgError, ":omit_nil must be true or false.");
908
919
  }
909
- }
910
- // This is here only for backwards compatibility with the original Oj.
911
- v = rb_hash_lookup(ropts, oj_ascii_only_sym);
912
- if (Qtrue == v) {
913
- copts->escape_mode = ASCIIEsc;
914
- } else if (Qfalse == v) {
915
- copts->escape_mode = JSONEsc;
916
- }
917
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
918
- if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
920
+ } else if(oj_ascii_only_sym == k) {
921
+ // This is here only for backwards compatibility with the original Oj.
922
+ if (Qtrue == v) {
923
+ copts->escape_mode = ASCIIEsc;
924
+ } else if (Qfalse == v) {
925
+ copts->escape_mode = JSONEsc;
926
+ }
927
+ } else if (oj_hash_class_sym == k) {
928
+ if (Qnil == v) {
919
929
  copts->hash_class = Qnil;
920
930
  } else {
921
931
  rb_check_type(v, T_CLASS);
922
932
  copts->hash_class = v;
923
933
  }
924
- }
925
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
926
- if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
934
+ } else if (oj_object_class_sym == k) {
935
+ if (Qnil == v) {
927
936
  copts->hash_class = Qnil;
928
937
  } else {
929
938
  rb_check_type(v, T_CLASS);
930
939
  copts->hash_class = v;
931
940
  }
932
- }
933
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
934
- if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
941
+ } else if (oj_array_class_sym == k) {
942
+ if (Qnil == v) {
935
943
  copts->array_class = Qnil;
936
944
  } else {
937
945
  rb_check_type(v, T_CLASS);
938
946
  copts->array_class = v;
939
947
  }
940
- }
941
- oj_parse_opt_match_string(&copts->str_rx, ropts);
942
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, ignore_sym)) {
948
+ } else if (ignore_sym == k) {
943
949
  xfree(copts->ignore);
944
950
  copts->ignore = NULL;
945
- if (Qnil != (v = rb_hash_lookup(ropts, ignore_sym))) {
951
+ if (Qnil != v) {
946
952
  int cnt;
947
953
 
948
954
  rb_check_type(v, T_ARRAY);
@@ -957,8 +963,11 @@ void oj_parse_options(VALUE ropts, Options copts) {
957
963
  copts->ignore[i] = Qnil;
958
964
  }
959
965
  }
960
- }
961
- if (Qnil != (v = rb_hash_lookup(ropts, integer_range_sym))) {
966
+ } else if (integer_range_sym == k) {
967
+ if (Qnil == v) {
968
+ return ST_CONTINUE;
969
+ }
970
+
962
971
  if (TYPE(v) == T_STRUCT && rb_obj_class(v) == rb_cRange) {
963
972
  VALUE min = rb_funcall(v, oj_begin_id, 0);
964
973
  VALUE max = rb_funcall(v, oj_end_id, 0);
@@ -973,6 +982,22 @@ void oj_parse_options(VALUE ropts, Options copts) {
973
982
  rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
974
983
  }
975
984
  }
985
+
986
+ return ST_CONTINUE;
987
+ }
988
+
989
+ void oj_parse_options(VALUE ropts, Options copts) {
990
+ if (T_HASH != rb_type(ropts)) {
991
+ return;
992
+ }
993
+
994
+ rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
995
+ oj_parse_opt_match_string(&copts->str_rx, ropts);
996
+
997
+ copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
998
+ 0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
999
+ 0 < copts->dump_opts.array_size);
1000
+ return;
976
1001
  }
977
1002
 
978
1003
  static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
@@ -1237,6 +1262,38 @@ static VALUE safe_load(VALUE self, VALUE doc) {
1237
1262
  * - *io* [_IO__|_String_] IO Object to read from
1238
1263
  */
1239
1264
 
1265
+ struct dump_arg {
1266
+ struct _out *out;
1267
+ struct _options *copts;
1268
+ int argc;
1269
+ VALUE *argv;
1270
+ };
1271
+
1272
+ static VALUE dump_body(VALUE a)
1273
+ {
1274
+ volatile struct dump_arg *arg = (void *)a;
1275
+ VALUE rstr;
1276
+
1277
+ oj_dump_obj_to_json_using_params(*arg->argv, arg->copts, arg->out, arg->argc - 1, arg->argv + 1);
1278
+ if (0 == arg->out->buf) {
1279
+ rb_raise(rb_eNoMemError, "Not enough memory.");
1280
+ }
1281
+ rstr = rb_str_new2(arg->out->buf);
1282
+ rstr = oj_encode(rstr);
1283
+
1284
+ return rstr;
1285
+ }
1286
+
1287
+ static VALUE dump_ensure(VALUE a)
1288
+ {
1289
+ volatile struct dump_arg *arg = (void *)a;
1290
+
1291
+ if (arg->out->allocated) {
1292
+ xfree(arg->out->buf);
1293
+ }
1294
+ return Qnil;
1295
+ }
1296
+
1240
1297
  /* Document-method: dump
1241
1298
  * call-seq: dump(obj, options={})
1242
1299
  *
@@ -1246,9 +1303,9 @@ static VALUE safe_load(VALUE self, VALUE doc) {
1246
1303
  */
1247
1304
  static VALUE dump(int argc, VALUE *argv, VALUE self) {
1248
1305
  char buf[4096];
1306
+ struct dump_arg arg;
1249
1307
  struct _out out;
1250
1308
  struct _options copts = oj_default_options;
1251
- VALUE rstr;
1252
1309
 
1253
1310
  if (1 > argc) {
1254
1311
  rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
@@ -1262,21 +1319,18 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1262
1319
  if (CompatMode == copts.mode && copts.escape_mode != ASCIIEsc) {
1263
1320
  copts.escape_mode = JSONEsc;
1264
1321
  }
1265
- out.buf = buf;
1266
- out.end = buf + sizeof(buf) - 10;
1267
- out.allocated = false;
1268
- out.omit_nil = copts.dump_opts.omit_nil;
1269
- out.caller = CALLER_DUMP;
1270
- oj_dump_obj_to_json_using_params(*argv, &copts, &out, argc - 1, argv + 1);
1271
- if (0 == out.buf) {
1272
- rb_raise(rb_eNoMemError, "Not enough memory.");
1273
- }
1274
- rstr = rb_str_new2(out.buf);
1275
- rstr = oj_encode(rstr);
1276
- if (out.allocated) {
1277
- xfree(out.buf);
1278
- }
1279
- return rstr;
1322
+ arg.out = &out;
1323
+ arg.copts = &copts;
1324
+ arg.argc = argc;
1325
+ arg.argv = argv;
1326
+
1327
+ arg.out->buf = buf;
1328
+ arg.out->end = buf + sizeof(buf) - 10;
1329
+ arg.out->allocated = false;
1330
+ arg.out->omit_nil = copts.dump_opts.omit_nil;
1331
+ arg.out->caller = CALLER_DUMP;
1332
+
1333
+ return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
1280
1334
  }
1281
1335
 
1282
1336
  /* Document-method: to_json
@@ -1344,7 +1398,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1344
1398
  * Dumps an Object to the specified file.
1345
1399
  * - *file* [_String_] _path file path to write the JSON document to
1346
1400
  * - *obj* [_Object_] Object to serialize as an JSON document String
1347
- * - *options* [_Hash_] formating options
1401
+ * - *options* [_Hash_] formatting options
1348
1402
  * - *:indent* [_Fixnum_] format expected
1349
1403
  * - *:circular* [_Boolean_] allow circular references, default: false
1350
1404
  */
@@ -1366,7 +1420,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1366
1420
  * Dumps an Object to the specified IO stream.
1367
1421
  * - *io* [_IO_] IO stream to write the JSON document to
1368
1422
  * - *obj* [_Object_] Object to serialize as an JSON document String
1369
- * - *options* [_Hash_] formating options
1423
+ * - *options* [_Hash_] formatting options
1370
1424
  * - *:indent* [_Fixnum_] format expected
1371
1425
  * - *:circular* [_Boolean_] allow circular references, default: false
1372
1426
  */
@@ -1725,6 +1779,9 @@ static VALUE protect_require(VALUE x) {
1725
1779
  void Init_oj() {
1726
1780
  int err = 0;
1727
1781
 
1782
+ #if HAVE_RB_EXT_RACTOR_SAFE
1783
+ rb_ext_ractor_safe(true);
1784
+ #endif
1728
1785
  Oj = rb_define_module("Oj");
1729
1786
 
1730
1787
  oj_cstack_class = rb_define_class_under(Oj, "CStack", rb_cObject);
@@ -1785,7 +1842,6 @@ void Init_oj() {
1785
1842
  oj_file_id = rb_intern("file?");
1786
1843
  oj_fileno_id = rb_intern("fileno");
1787
1844
  oj_ftype_id = rb_intern("ftype");
1788
- oj_has_key_id = rb_intern("has_key?");
1789
1845
  oj_hash_end_id = rb_intern("hash_end");
1790
1846
  oj_hash_key_id = rb_intern("hash_key");
1791
1847
  oj_hash_set_id = rb_intern("hash_set");
@@ -2000,4 +2056,6 @@ void Init_oj() {
2000
2056
  rb_gc_register_address(&oj_cache_mutex);
2001
2057
  #endif
2002
2058
  oj_init_doc();
2059
+
2060
+ oj_parser_init();
2003
2061
  }