oj 3.13.14 → 3.13.22

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/README.md +2 -0
  4. data/ext/oj/buf.h +4 -0
  5. data/ext/oj/compat.c +10 -10
  6. data/ext/oj/custom.c +34 -53
  7. data/ext/oj/dump.c +24 -13
  8. data/ext/oj/dump_compat.c +5 -10
  9. data/ext/oj/dump_object.c +5 -60
  10. data/ext/oj/dump_strict.c +5 -5
  11. data/ext/oj/extconf.rb +5 -4
  12. data/ext/oj/fast.c +15 -13
  13. data/ext/oj/intern.c +6 -9
  14. data/ext/oj/introspect.c +96 -0
  15. data/ext/oj/mimic_json.c +18 -8
  16. data/ext/oj/object.c +42 -41
  17. data/ext/oj/oj.c +27 -4
  18. data/ext/oj/oj.h +4 -1
  19. data/ext/oj/parse.c +111 -76
  20. data/ext/oj/parse.h +2 -0
  21. data/ext/oj/parser.c +61 -4
  22. data/ext/oj/parser.h +12 -0
  23. data/ext/oj/rails.c +5 -10
  24. data/ext/oj/saj2.c +333 -85
  25. data/ext/oj/saj2.h +23 -0
  26. data/ext/oj/sparse.c +4 -0
  27. data/ext/oj/strict.c +13 -13
  28. data/ext/oj/usual.c +82 -129
  29. data/ext/oj/usual.h +68 -0
  30. data/ext/oj/val_stack.c +1 -1
  31. data/ext/oj/validate.c +21 -26
  32. data/ext/oj/wab.c +15 -20
  33. data/lib/oj/saj.rb +20 -6
  34. data/lib/oj/state.rb +1 -1
  35. data/lib/oj/version.rb +1 -1
  36. data/pages/Compatibility.md +1 -1
  37. data/test/bar.rb +3 -1
  38. data/test/helper.rb +8 -2
  39. data/test/json_gem/json_generator_test.rb +3 -4
  40. data/test/json_gem/json_parser_test.rb +8 -1
  41. data/test/json_gem/test_helper.rb +7 -3
  42. data/test/test_compat.rb +25 -0
  43. data/test/test_custom.rb +13 -2
  44. data/test/test_file.rb +23 -7
  45. data/test/test_gc.rb +11 -0
  46. data/test/test_object.rb +3 -10
  47. data/test/test_parser.rb +3 -19
  48. data/test/test_parser_debug.rb +27 -0
  49. data/test/test_parser_saj.rb +92 -2
  50. data/test/test_scp.rb +2 -4
  51. data/test/test_strict.rb +2 -0
  52. data/test/test_various.rb +8 -3
  53. data/test/test_wab.rb +2 -0
  54. data/test/tests.rb +9 -0
  55. data/test/tests_mimic.rb +9 -0
  56. data/test/tests_mimic_addition.rb +9 -0
  57. metadata +7 -107
data/ext/oj/dump_object.c CHANGED
@@ -315,7 +315,6 @@ static void dump_hash_class(VALUE obj, VALUE clas, int depth, Out out) {
315
315
  *out->cur = '\0';
316
316
  }
317
317
 
318
- #ifdef HAVE_RB_IVAR_FOREACH
319
318
  static int dump_attr_cb(ID key, VALUE value, VALUE ov) {
320
319
  Out out = (Out)ov;
321
320
  int depth = out->depth;
@@ -358,7 +357,6 @@ static int dump_attr_cb(ID key, VALUE value, VALUE ov) {
358
357
 
359
358
  return ST_CONTINUE;
360
359
  }
361
- #endif
362
360
 
363
361
  static void dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
364
362
  dump_hash_class(obj, rb_obj_class(obj), depth, out);
@@ -510,19 +508,8 @@ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out)
510
508
  default: break;
511
509
  }
512
510
  {
513
- int cnt;
514
- #ifdef HAVE_RB_IVAR_COUNT
515
- cnt = (int)rb_ivar_count(obj);
516
- #else
517
- volatile VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
518
- VALUE * np = RARRAY_PTR(vars);
519
- ID vid;
520
- const char * attr;
521
- int i;
522
- int first = 1;
523
-
524
- cnt = (int)RARRAY_LEN(vars);
525
- #endif
511
+ int cnt = (int)rb_ivar_count(obj);
512
+
526
513
  if (Qundef != clas && 0 < cnt) {
527
514
  *out->cur++ = ',';
528
515
  }
@@ -535,52 +522,10 @@ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out)
535
522
  }
536
523
  }
537
524
  out->depth = depth + 1;
538
- #ifdef HAVE_RB_IVAR_FOREACH
539
525
  rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
540
526
  if (',' == *(out->cur - 1)) {
541
527
  out->cur--; // backup to overwrite last comma
542
528
  }
543
- #else
544
- size = d2 * out->indent + 1;
545
- for (i = cnt; 0 < i; i--, np++) {
546
- VALUE value;
547
-
548
- vid = rb_to_id(*np);
549
- attr = rb_id2name(vid);
550
- if (Yes == out->opts->ignore_under && '@' == *attr && '_' == attr[1]) {
551
- continue;
552
- }
553
- value = rb_ivar_get(obj, vid);
554
-
555
- if (dump_ignore(out->opts, value)) {
556
- continue;
557
- }
558
- if (out->omit_nil && Qnil == value) {
559
- continue;
560
- }
561
- if (first) {
562
- first = 0;
563
- } else {
564
- *out->cur++ = ',';
565
- }
566
- assure_size(out, size);
567
- fill_indent(out, d2);
568
- if ('@' == *attr) {
569
- attr++;
570
- oj_dump_cstr(attr, strlen(attr), 0, 0, out);
571
- } else {
572
- char buf[32];
573
-
574
- *buf = '~';
575
- strncpy(buf + 1, attr, sizeof(buf) - 2);
576
- buf[sizeof(buf) - 1] = '\0';
577
- oj_dump_cstr(buf, strlen(attr) + 1, 0, 0, out);
578
- }
579
- *out->cur++ = ':';
580
- oj_dump_obj_val(value, d2, out);
581
- assure_size(out, 2);
582
- }
583
- #endif
584
529
  if (rb_obj_is_kind_of(obj, rb_eException)) {
585
530
  volatile VALUE rv;
586
531
 
@@ -737,7 +682,7 @@ static DumpFunc obj_funcs[] = {
737
682
  void oj_dump_obj_val(VALUE obj, int depth, Out out) {
738
683
  int type = rb_type(obj);
739
684
 
740
- if (Yes == out->opts->trace) {
685
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
741
686
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
742
687
  }
743
688
  if (MAX_DEPTH < depth) {
@@ -748,14 +693,14 @@ void oj_dump_obj_val(VALUE obj, int depth, Out out) {
748
693
 
749
694
  if (NULL != f) {
750
695
  f(obj, depth, out, false);
751
- if (Yes == out->opts->trace) {
696
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
752
697
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
753
698
  }
754
699
  return;
755
700
  }
756
701
  }
757
702
  oj_dump_nil(Qnil, depth, out, false);
758
- if (Yes == out->opts->trace) {
703
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
759
704
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
760
705
  }
761
706
  }
data/ext/oj/dump_strict.c CHANGED
@@ -338,7 +338,7 @@ static DumpFunc strict_funcs[] = {
338
338
  void oj_dump_strict_val(VALUE obj, int depth, Out out) {
339
339
  int type = rb_type(obj);
340
340
 
341
- if (Yes == out->opts->trace) {
341
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
342
342
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
343
343
  }
344
344
  if (MAX_DEPTH < depth) {
@@ -349,7 +349,7 @@ void oj_dump_strict_val(VALUE obj, int depth, Out out) {
349
349
 
350
350
  if (NULL != f) {
351
351
  f(obj, depth, out, false);
352
- if (Yes == out->opts->trace) {
352
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
353
353
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
354
354
  }
355
355
  return;
@@ -386,7 +386,7 @@ static DumpFunc null_funcs[] = {
386
386
  void oj_dump_null_val(VALUE obj, int depth, Out out) {
387
387
  int type = rb_type(obj);
388
388
 
389
- if (Yes == out->opts->trace) {
389
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
390
390
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
391
391
  }
392
392
  if (MAX_DEPTH < depth) {
@@ -397,14 +397,14 @@ void oj_dump_null_val(VALUE obj, int depth, Out out) {
397
397
 
398
398
  if (NULL != f) {
399
399
  f(obj, depth, out, false);
400
- if (Yes == out->opts->trace) {
400
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
401
401
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
402
402
  }
403
403
  return;
404
404
  }
405
405
  }
406
406
  oj_dump_nil(Qnil, depth, out, false);
407
- if (Yes == out->opts->trace) {
407
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
408
408
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
409
409
  }
410
410
  }
data/ext/oj/extconf.rb CHANGED
@@ -23,14 +23,10 @@ dflags = {
23
23
  'RSTRUCT_LEN_RETURNS_INTEGER_OBJECT' => ('ruby' == type && '2' == version[0] && '4' == version[1] && '1' >= version[2]) ? 1 : 0,
24
24
  }
25
25
 
26
- have_func('rb_time_timespec')
27
- have_func('rb_ivar_count')
28
- have_func('rb_ivar_foreach')
29
26
  # Support for compaction.
30
27
  have_func('rb_gc_mark_movable')
31
28
  have_func('stpcpy')
32
29
  have_func('pthread_mutex_init')
33
- have_func('rb_enc_associate')
34
30
  have_func('rb_enc_interned_str')
35
31
  have_func('rb_ext_ractor_safe', 'ruby.h')
36
32
  # rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
@@ -38,6 +34,11 @@ have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == ve
38
34
 
39
35
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
40
36
 
37
+ if with_config('--with-sse42')
38
+ $CPPFLAGS += ' -msse4.2'
39
+ dflags['OJ_USE_SSE4_2'] = 1
40
+ end
41
+
41
42
  dflags.each do |k,v|
42
43
  if v.nil?
43
44
  $CPPFLAGS += " -D#{k}"
data/ext/oj/fast.c CHANGED
@@ -677,21 +677,23 @@ static void free_doc_cb(void *x) {
677
677
  }
678
678
 
679
679
  static void mark_leaf(Leaf leaf) {
680
- switch (leaf->value_type) {
681
- case COL_VAL:
682
- if (NULL != leaf->elements) {
683
- Leaf first = leaf->elements->next;
684
- Leaf e = first;
680
+ if (NULL != leaf) {
681
+ switch (leaf->value_type) {
682
+ case COL_VAL:
683
+ if (NULL != leaf->elements) {
684
+ Leaf first = leaf->elements->next;
685
+ Leaf e = first;
685
686
 
686
- do {
687
- mark_leaf(e);
688
- e = e->next;
689
- } while (e != first);
690
- }
691
- break;
692
- case RUBY_VAL: mark(leaf->value); break;
687
+ do {
688
+ mark_leaf(e);
689
+ e = e->next;
690
+ } while (e != first);
691
+ }
692
+ break;
693
+ case RUBY_VAL: mark(leaf->value); break;
693
694
 
694
- default: break;
695
+ default: break;
696
+ }
695
697
  }
696
698
  }
697
699
 
data/ext/oj/intern.c CHANGED
@@ -37,13 +37,10 @@ typedef struct _hash {
37
37
  struct _hash class_hash;
38
38
  struct _hash attr_hash;
39
39
 
40
- static struct _cache *str_cache = NULL;
41
40
  static VALUE str_cache_obj;
42
41
 
43
- static struct _cache *sym_cache = NULL;
44
42
  static VALUE sym_cache_obj;
45
43
 
46
- static struct _cache *attr_cache = NULL;
47
44
  static VALUE attr_cache_obj;
48
45
 
49
46
  static VALUE form_str(const char *str, size_t len) {
@@ -93,15 +90,15 @@ void oj_hash_init(void) {
93
90
  rb_gc_register_address(&cache_class);
94
91
  rb_undef_alloc_func(cache_class);
95
92
 
96
- str_cache = cache_create(0, form_str, true, true);
93
+ struct _cache *str_cache = cache_create(0, form_str, true, true);
97
94
  str_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, str_cache);
98
95
  rb_gc_register_address(&str_cache_obj);
99
96
 
100
- sym_cache = cache_create(0, form_sym, true, true);
97
+ struct _cache *sym_cache = cache_create(0, form_sym, true, true);
101
98
  sym_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, sym_cache);
102
99
  rb_gc_register_address(&sym_cache_obj);
103
100
 
104
- attr_cache = cache_create(0, form_attr, false, true);
101
+ struct _cache *attr_cache = cache_create(0, form_attr, false, true);
105
102
  attr_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, attr_cache);
106
103
  rb_gc_register_address(&attr_cache_obj);
107
104
 
@@ -122,18 +119,18 @@ oj_str_intern(const char *key, size_t len) {
122
119
  #if HAVE_RB_ENC_INTERNED_STR && 0
123
120
  return rb_enc_interned_str(key, len, rb_utf8_encoding());
124
121
  #else
125
- return cache_intern(str_cache, key, len);
122
+ return cache_intern(DATA_PTR(str_cache_obj), key, len);
126
123
  #endif
127
124
  }
128
125
 
129
126
  VALUE
130
127
  oj_sym_intern(const char *key, size_t len) {
131
- return cache_intern(sym_cache, key, len);
128
+ return cache_intern(DATA_PTR(sym_cache_obj), key, len);
132
129
  }
133
130
 
134
131
  ID
135
132
  oj_attr_intern(const char *key, size_t len) {
136
- return cache_intern(attr_cache, key, len);
133
+ return cache_intern(DATA_PTR(attr_cache_obj), key, len);
137
134
  }
138
135
 
139
136
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
@@ -0,0 +1,96 @@
1
+
2
+ #include "cache.h"
3
+ #include "oj.h"
4
+ #include "parser.h"
5
+ #include "usual.h"
6
+
7
+ #define BYTE_OFFSETS_STACK_INC_SIZE 256
8
+
9
+ VALUE mod = Qnil;
10
+
11
+ typedef struct _introspect {
12
+ struct _usual usual;
13
+ void (*start)(struct _ojParser *p);
14
+ void (*free)(struct _ojParser *p);
15
+ void (*mark)(struct _ojParser *p);
16
+ void (*open_object)(struct _ojParser *p);
17
+ void (*close_object)(struct _ojParser *p);
18
+
19
+ int len;
20
+ int cur;
21
+ long *stack;
22
+
23
+ } * Introspect;
24
+
25
+ static void dfree(ojParser p) {
26
+ Introspect d = (Introspect)p->ctx;
27
+
28
+ xfree(d->stack);
29
+ // As with the mark() function, this function should still work.
30
+ d->free(p);
31
+ }
32
+
33
+ static void start(ojParser p) {
34
+ Introspect d = (Introspect)p->ctx;
35
+
36
+ d->start(p);
37
+
38
+ d->stack = ALLOC_N(long, BYTE_OFFSETS_STACK_INC_SIZE);
39
+ d->len = BYTE_OFFSETS_STACK_INC_SIZE;
40
+ d->cur = 0;
41
+ }
42
+
43
+ static void open_object(ojParser p) {
44
+ Introspect d = (Introspect)p->ctx;
45
+
46
+ d->open_object(p);
47
+
48
+ // TBD add offset
49
+ }
50
+
51
+ static void close_object(ojParser p) {
52
+ Introspect d = (Introspect)p->ctx;
53
+
54
+ d->close_object(p);
55
+
56
+ // TBD set offset on hash
57
+ }
58
+
59
+ static VALUE new_parser(VALUE self, VALUE ropts) {
60
+ volatile VALUE pv = oj_parser_new();
61
+ ojParser p = (ojParser)DATA_PTR(pv);
62
+ Introspect d = ALLOC(struct _introspect);
63
+
64
+ oj_init_usual(p, &d->usual);
65
+
66
+ // Set the options before switching to the Introspect delegate.
67
+ p->ctx = (void *)d;
68
+
69
+ d->free = p->free;
70
+ p->free = dfree;
71
+
72
+ d->start = p->start;
73
+ p->start = start;
74
+
75
+ // We are cheating with the mark, free, and options functions. Since
76
+ // struct _usual is the first member of struct _introspect the cast to
77
+ // Usual in the usual.c mark() function should still work just fine.
78
+
79
+ d->open_object = p->funcs[TOP_FUN].open_object;
80
+ p->funcs[TOP_FUN].open_object = open_object;
81
+
82
+ d->close_object = p->funcs[TOP_FUN].close_object;
83
+ p->funcs[TOP_FUN].close_object = close_object;
84
+
85
+ Check_Type(ropts, T_HASH);
86
+ oj_parser_set_option(p, ropts);
87
+
88
+ return pv;
89
+ }
90
+
91
+ void Init_introspect(void) {
92
+ mod = rb_define_module("Introspect");
93
+ rb_gc_register_address(&mod);
94
+
95
+ rb_define_module_function(mod, "parser", new_parser, 1);
96
+ }
data/ext/oj/mimic_json.c CHANGED
@@ -388,6 +388,10 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
388
388
  VALUE active_hack[1];
389
389
 
390
390
  if (Qundef == state_class) {
391
+ rb_warn(
392
+ "Oj::Rails.mimic_JSON was called implicitly. "
393
+ "Call it explicitly beforehand if you want to remove this warning."
394
+ );
391
395
  oj_define_mimic_json(0, NULL, Qnil);
392
396
  }
393
397
  active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
@@ -481,6 +485,10 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
481
485
  rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
482
486
  }
483
487
  if (Qundef == state_class) {
488
+ rb_warn(
489
+ "Oj::Rails.mimic_JSON was called implicitly. "
490
+ "Call it explicitly beforehand if you want to remove this warning."
491
+ );
484
492
  oj_define_mimic_json(0, NULL, Qnil);
485
493
  }
486
494
  rargs[1] = rb_funcall(state_class, oj_new_id, 1, h);
@@ -536,14 +544,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE info) {
536
544
  }
537
545
  } else if (oj_decimal_class_sym == k) {
538
546
  pi->options.compat_bigdec = (oj_bigdecimal_class == v);
539
- } else if (oj_max_nesting_sym == k) {
540
- if (Qtrue == v) {
541
- pi->max_depth = 100;
542
- } else if (Qfalse == v || Qnil == v) {
543
- pi->max_depth = 0;
544
- } else if (T_FIXNUM == rb_type(v)) {
545
- pi->max_depth = NUM2INT(v);
546
- }
547
547
  }
548
548
  return ST_CONTINUE;
549
549
  }
@@ -573,11 +573,21 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
573
573
  pi.max_depth = 100;
574
574
 
575
575
  if (Qnil != ropts) {
576
+ VALUE v;
577
+
576
578
  if (T_HASH != rb_type(ropts)) {
577
579
  rb_raise(rb_eArgError, "options must be a hash.");
578
580
  }
579
581
 
580
582
  rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
583
+ v = rb_hash_lookup(ropts, oj_max_nesting_sym);
584
+ if (Qtrue == v) {
585
+ pi.max_depth = 100;
586
+ } else if (Qfalse == v || Qnil == v) {
587
+ pi.max_depth = 0;
588
+ } else if (T_FIXNUM == rb_type(v)) {
589
+ pi.max_depth = NUM2INT(v);
590
+ }
581
591
  oj_parse_opt_match_string(&pi.options.str_rx, ropts);
582
592
  if (Yes == pi.options.create_ok && Yes == pi.options.sym_key) {
583
593
  rb_raise(rb_eArgError, ":symbolize_names and :create_additions can not both be true.");
data/ext/oj/object.c CHANGED
@@ -67,9 +67,9 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
67
67
 
68
68
  // The much faster approach (4x faster)
69
69
  static int parse_num(const char *str, const char *end, int cnt) {
70
- int n = 0;
70
+ int n = 0;
71
71
  char c;
72
- int i;
72
+ int i;
73
73
 
74
74
  for (i = cnt; 0 < i; i--, str++) {
75
75
  c = *str;
@@ -83,9 +83,9 @@ static int parse_num(const char *str, const char *end, int cnt) {
83
83
 
84
84
  VALUE
85
85
  oj_parse_xml_time(const char *str, int len) {
86
- VALUE args[8];
86
+ VALUE args[8];
87
87
  const char *end = str + len;
88
- int n;
88
+ int n;
89
89
 
90
90
  // year
91
91
  if (0 > (n = parse_num(str, end, 4))) {
@@ -269,19 +269,10 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
269
269
  // match the expected value.
270
270
  parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
271
271
  } else if (ni->has_exp) {
272
- int64_t t = (int64_t)(ni->i + ni->exp);
273
- struct _timeInfo ti;
274
- VALUE args[8];
275
-
276
- sec_as_time(t, &ti);
277
- args[0] = LONG2NUM((long)(ti.year));
278
- args[1] = LONG2NUM(ti.mon);
279
- args[2] = LONG2NUM(ti.day);
280
- args[3] = LONG2NUM(ti.hour);
281
- args[4] = LONG2NUM(ti.min);
282
- args[5] = rb_float_new((double)ti.sec + ((double)nsec + 0.5) / 1000000000.0);
283
- args[6] = LONG2NUM(ni->exp);
284
- parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
272
+ struct timespec ts;
273
+ ts.tv_sec = ni->i;
274
+ ts.tv_nsec = nsec;
275
+ parent->val = rb_time_timespec_new(&ts, (int)ni->exp);
285
276
  } else {
286
277
  parent->val = rb_time_nano_new(ni->i, (long)nsec);
287
278
  }
@@ -333,26 +324,30 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
333
324
  // If struct is not defined then we let this fail and raise an exception.
334
325
  sc = oj_name2struct(pi, *RARRAY_PTR(value), rb_eArgError);
335
326
  }
336
- // Create a properly initialized struct instance without calling the initialize method.
337
- parent->val = rb_obj_alloc(sc);
338
- // If the JSON array has more entries than the struct class allows, we record an error.
327
+ if (sc == rb_cRange) {
328
+ parent->val = rb_class_new_instance(len - 1, RARRAY_PTR(value) + 1, rb_cRange);
329
+ } else {
330
+ // Create a properly initialized struct instance without calling the initialize method.
331
+ parent->val = rb_obj_alloc(sc);
332
+ // If the JSON array has more entries than the struct class allows, we record an error.
339
333
  #ifdef RSTRUCT_LEN
340
334
  #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
341
335
  slen = (int)NUM2LONG(RSTRUCT_LEN(parent->val));
342
336
  #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
343
- slen = (int)RSTRUCT_LEN(parent->val);
337
+ slen = (int)RSTRUCT_LEN(parent->val);
344
338
  #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
345
339
  #else
346
- slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
340
+ slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
347
341
  #endif
348
- // MRI >= 1.9
349
- if (len - 1 > slen) {
350
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
351
- } else {
352
- int i;
342
+ // MRI >= 1.9
343
+ if (len - 1 > slen) {
344
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
345
+ } else {
346
+ int i;
353
347
 
354
- for (i = 0; i < len - 1; i++) {
355
- rb_struct_aset(parent->val, INT2FIX(i), RARRAY_PTR(value)[i + 1]);
348
+ for (i = 0; i < len - 1; i++) {
349
+ rb_struct_aset(parent->val, INT2FIX(i), RARRAY_PTR(value)[i + 1]);
350
+ }
356
351
  }
357
352
  }
358
353
  return 1;
@@ -374,11 +369,17 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
374
369
  }
375
370
 
376
371
  void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
377
- rb_ivar_set(parent->val, oj_attr_intern(kval->key, kval->klen), value);
372
+ if (kval->klen == 5 && strncmp("~mesg", kval->key, 5) == 0 && rb_obj_is_kind_of(parent->val, rb_eException)) {
373
+ parent->val = rb_funcall(parent->val, rb_intern("exception"), 1, value);
374
+ } else if (kval->klen == 3 && strncmp("~bt", kval->key, 3) == 0 && rb_obj_is_kind_of(parent->val, rb_eException)) {
375
+ rb_funcall(parent->val, rb_intern("set_backtrace"), 1, value);
376
+ } else {
377
+ rb_ivar_set(parent->val, oj_attr_intern(kval->key, kval->klen), value);
378
+ }
378
379
  }
379
380
 
380
381
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
381
- const char * key = kval->key;
382
+ const char *key = kval->key;
382
383
  int klen = kval->klen;
383
384
  Val parent = stack_peek(&pi->stack);
384
385
  volatile VALUE rval = Qnil;
@@ -445,13 +446,13 @@ WHICH_TYPE:
445
446
  rb_class2name(rb_obj_class(parent->val)));
446
447
  return;
447
448
  }
448
- if (Yes == pi->options.trace) {
449
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
449
450
  oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
450
451
  }
451
452
  }
452
453
 
453
454
  static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
454
- const char * key = kval->key;
455
+ const char *key = kval->key;
455
456
  int klen = kval->klen;
456
457
  Val parent = stack_peek(&pi->stack);
457
458
  volatile VALUE rval = Qnil;
@@ -516,7 +517,7 @@ WHICH_TYPE:
516
517
  rb_class2name(rb_obj_class(parent->val)));
517
518
  return;
518
519
  }
519
- if (Yes == pi->options.trace) {
520
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
520
521
  oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, rval);
521
522
  }
522
523
  }
@@ -602,13 +603,13 @@ WHICH_TYPE:
602
603
  rb_class2name(rb_obj_class(parent->val)));
603
604
  return;
604
605
  }
605
- if (Yes == pi->options.trace) {
606
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
606
607
  oj_trace_parse_call("add_value", pi, __FILE__, __LINE__, value);
607
608
  }
608
609
  }
609
610
 
610
611
  static VALUE start_hash(ParseInfo pi) {
611
- if (Yes == pi->options.trace) {
612
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
612
613
  oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
613
614
  }
614
615
  return Qnil;
@@ -626,7 +627,7 @@ static void end_hash(ParseInfo pi) {
626
627
  oj_odd_free(oa);
627
628
  parent->odd_args = NULL;
628
629
  }
629
- if (Yes == pi->options.trace) {
630
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
630
631
  oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
631
632
  }
632
633
  }
@@ -654,7 +655,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
654
655
  }
655
656
  rval = str_to_value(pi, str, len, orig);
656
657
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
657
- if (Yes == pi->options.trace) {
658
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
658
659
  oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rval);
659
660
  }
660
661
  }
@@ -663,21 +664,21 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
663
664
  volatile VALUE rval = oj_num_as_value(ni);
664
665
 
665
666
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
666
- if (Yes == pi->options.trace) {
667
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
667
668
  oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
668
669
  }
669
670
  }
670
671
 
671
672
  static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
672
673
  pi->stack.head->val = str_to_value(pi, str, len, orig);
673
- if (Yes == pi->options.trace) {
674
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
674
675
  oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
675
676
  }
676
677
  }
677
678
 
678
679
  static void add_num(ParseInfo pi, NumInfo ni) {
679
680
  pi->stack.head->val = oj_num_as_value(ni);
680
- if (Yes == pi->options.trace) {
681
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
681
682
  oj_trace_parse_call("add_num", pi, __FILE__, __LINE__, pi->stack.head->val);
682
683
  }
683
684
  }