oj 3.13.14 → 3.13.22

Sign up to get free protection for your applications and to get access to all the features.
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
  }