oj 3.13.7 → 3.13.23

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +75 -0
  3. data/README.md +11 -0
  4. data/ext/oj/buf.h +4 -0
  5. data/ext/oj/circarray.c +1 -1
  6. data/ext/oj/code.c +15 -22
  7. data/ext/oj/compat.c +10 -10
  8. data/ext/oj/custom.c +66 -112
  9. data/ext/oj/dump.c +147 -184
  10. data/ext/oj/dump.h +25 -8
  11. data/ext/oj/dump_compat.c +47 -89
  12. data/ext/oj/dump_leaf.c +14 -58
  13. data/ext/oj/dump_object.c +72 -188
  14. data/ext/oj/dump_strict.c +19 -31
  15. data/ext/oj/encoder.c +43 -0
  16. data/ext/oj/extconf.rb +5 -4
  17. data/ext/oj/fast.c +36 -24
  18. data/ext/oj/intern.c +22 -12
  19. data/ext/oj/intern.h +1 -1
  20. data/ext/oj/mimic_json.c +74 -73
  21. data/ext/oj/object.c +54 -72
  22. data/ext/oj/odd.c +83 -63
  23. data/ext/oj/odd.h +13 -13
  24. data/ext/oj/oj.c +166 -175
  25. data/ext/oj/oj.h +25 -3
  26. data/ext/oj/parse.c +123 -79
  27. data/ext/oj/parse.h +2 -0
  28. data/ext/oj/parser.c +77 -21
  29. data/ext/oj/parser.h +12 -0
  30. data/ext/oj/rails.c +46 -70
  31. data/ext/oj/rails.h +1 -1
  32. data/ext/oj/reader.c +2 -0
  33. data/ext/oj/saj.c +11 -23
  34. data/ext/oj/saj2.c +333 -85
  35. data/ext/oj/saj2.h +23 -0
  36. data/ext/oj/sparse.c +4 -0
  37. data/ext/oj/stream_writer.c +3 -1
  38. data/ext/oj/strict.c +13 -13
  39. data/ext/oj/string_writer.c +12 -5
  40. data/ext/oj/usual.c +86 -131
  41. data/ext/oj/usual.h +68 -0
  42. data/ext/oj/val_stack.c +1 -1
  43. data/ext/oj/validate.c +21 -26
  44. data/ext/oj/wab.c +22 -27
  45. data/lib/oj/saj.rb +20 -6
  46. data/lib/oj/state.rb +1 -1
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Compatibility.md +1 -1
  49. data/pages/JsonGem.md +15 -0
  50. data/pages/Modes.md +6 -3
  51. data/pages/Options.md +6 -0
  52. data/pages/Rails.md +12 -0
  53. data/test/activesupport7/abstract_unit.rb +49 -0
  54. data/test/activesupport7/decoding_test.rb +125 -0
  55. data/test/activesupport7/encoding_test.rb +486 -0
  56. data/test/activesupport7/encoding_test_cases.rb +104 -0
  57. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  58. data/test/bar.rb +3 -8
  59. data/test/bug.rb +16 -0
  60. data/test/foo.rb +71 -7
  61. data/test/helper.rb +8 -2
  62. data/test/json_gem/json_generator_test.rb +5 -4
  63. data/test/json_gem/json_parser_test.rb +8 -1
  64. data/test/json_gem/test_helper.rb +7 -3
  65. data/test/perf_dump.rb +50 -0
  66. data/test/test_compat.rb +25 -0
  67. data/test/test_custom.rb +13 -2
  68. data/test/test_fast.rb +37 -7
  69. data/test/test_file.rb +23 -7
  70. data/test/test_gc.rb +11 -0
  71. data/test/test_object.rb +8 -10
  72. data/test/test_parser.rb +3 -19
  73. data/test/test_parser_debug.rb +27 -0
  74. data/test/test_parser_saj.rb +92 -2
  75. data/test/test_saj.rb +1 -1
  76. data/test/test_scp.rb +2 -4
  77. data/test/test_strict.rb +2 -0
  78. data/test/test_various.rb +32 -2
  79. data/test/test_wab.rb +2 -0
  80. data/test/tests.rb +9 -1
  81. data/test/tests_mimic.rb +9 -0
  82. data/test/tests_mimic_addition.rb +9 -0
  83. metadata +15 -115
data/ext/oj/object.c CHANGED
@@ -35,7 +35,7 @@ static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
35
35
  return ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
36
36
  }
37
37
  if (Yes == pi->options.sym_key) {
38
- return ID2SYM(rb_intern3(kval->key, kval->klen, oj_utf8_encoding));
38
+ return ID2SYM(rb_intern3(kval->key, kval->klen, oj_utf8_encoding));
39
39
  }
40
40
  #if HAVE_RB_ENC_INTERNED_STR
41
41
  rkey = rb_enc_interned_str(kval->key, kval->klen, oj_utf8_encoding);
@@ -60,16 +60,11 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
60
60
  }
61
61
  rstr = oj_circ_array_get(pi->circ_array, i);
62
62
  } else {
63
- rstr = rb_utf8_str_new(str, len);
63
+ rstr = rb_utf8_str_new(str, len);
64
64
  }
65
65
  return rstr;
66
66
  }
67
67
 
68
- #if (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
69
- static VALUE oj_parse_xml_time(const char *str, int len) {
70
- return rb_funcall(rb_cTime, oj_parse_id, 1, rb_str_new(str, len));
71
- }
72
- #else
73
68
  // The much faster approach (4x faster)
74
69
  static int parse_num(const char *str, const char *end, int cnt) {
75
70
  int n = 0;
@@ -201,7 +196,6 @@ oj_parse_xml_time(const char *str, int len) {
201
196
  }
202
197
  return rb_funcall2(rb_cTime, oj_new_id, 7, args);
203
198
  }
204
- #endif
205
199
 
206
200
  static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t len) {
207
201
  const char *key = kval->key;
@@ -226,13 +220,10 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
226
220
  }
227
221
  parent->val = odd->clas;
228
222
  parent->odd_args = oj_odd_alloc_args(odd);
229
- } break;
230
- case 'm':
231
- parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
232
- break;
233
- case 's':
234
- parent->val = rb_utf8_str_new(str, len);
235
223
  break;
224
+ }
225
+ case 'm': parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding)); break;
226
+ case 's': parent->val = rb_utf8_str_new(str, len); break;
236
227
  case 'c': // class
237
228
  {
238
229
  VALUE clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
@@ -242,7 +233,8 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
242
233
  } else {
243
234
  parent->val = clas;
244
235
  }
245
- } break;
236
+ break;
237
+ }
246
238
  case 't': // time
247
239
  parent->val = oj_parse_xml_time(str, (int)len);
248
240
  break;
@@ -277,27 +269,17 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
277
269
  // match the expected value.
278
270
  parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
279
271
  } else if (ni->has_exp) {
280
- int64_t t = (int64_t)(ni->i + ni->exp);
281
- struct _timeInfo ti;
282
- VALUE args[8];
283
-
284
- sec_as_time(t, &ti);
285
- args[0] = LONG2NUM((long)(ti.year));
286
- args[1] = LONG2NUM(ti.mon);
287
- args[2] = LONG2NUM(ti.day);
288
- args[3] = LONG2NUM(ti.hour);
289
- args[4] = LONG2NUM(ti.min);
290
- args[5] = rb_float_new((double)ti.sec + ((double)nsec + 0.5) / 1000000000.0);
291
- args[6] = LONG2NUM(ni->exp);
292
- 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);
293
276
  } else {
294
277
  parent->val = rb_time_nano_new(ni->i, (long)nsec);
295
278
  }
296
279
  }
297
280
  break;
298
- case 'i': // circular index
299
- if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp &&
300
- 0 != pi->circ_array) { // fixnum
281
+ case 'i': // circular index
282
+ if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
301
283
  if (Qnil == parent->val) {
302
284
  parent->val = rb_hash_new();
303
285
  }
@@ -334,7 +316,7 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
334
316
  int i, cnt = (int)RARRAY_LEN(e1);
335
317
 
336
318
  for (i = 0; i < cnt; i++) {
337
- rstr = rb_ary_entry(e1, i);
319
+ rstr = RARRAY_AREF(e1, i);
338
320
  args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
339
321
  }
340
322
  sc = rb_funcall2(rb_cStruct, oj_new_id, cnt, args);
@@ -342,26 +324,30 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
342
324
  // If struct is not defined then we let this fail and raise an exception.
343
325
  sc = oj_name2struct(pi, *RARRAY_PTR(value), rb_eArgError);
344
326
  }
345
- // Create a properly initialized struct instance without calling the initialize method.
346
- parent->val = rb_obj_alloc(sc);
347
- // 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.
348
333
  #ifdef RSTRUCT_LEN
349
334
  #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
350
335
  slen = (int)NUM2LONG(RSTRUCT_LEN(parent->val));
351
336
  #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
352
- slen = (int)RSTRUCT_LEN(parent->val);
337
+ slen = (int)RSTRUCT_LEN(parent->val);
353
338
  #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
354
339
  #else
355
- slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
340
+ slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
356
341
  #endif
357
- // MRI >= 1.9
358
- if (len - 1 > slen) {
359
- oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
360
- } else {
361
- 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;
362
347
 
363
- for (i = 0; i < len - 1; i++) {
364
- 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
+ }
365
351
  }
366
352
  }
367
353
  return 1;
@@ -383,11 +369,17 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
383
369
  }
384
370
 
385
371
  void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
386
- 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
+ }
387
379
  }
388
380
 
389
381
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
390
- const char * key = kval->key;
382
+ const char *key = kval->key;
391
383
  int klen = kval->klen;
392
384
  Val parent = stack_peek(&pi->stack);
393
385
  volatile VALUE rval = Qnil;
@@ -402,9 +394,7 @@ WHICH_TYPE:
402
394
  }
403
395
  break;
404
396
  case T_HASH:
405
- rb_hash_aset(parent->val,
406
- calc_hash_key(pi, kval, parent->k1),
407
- str_to_value(pi, str, len, orig));
397
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), str_to_value(pi, str, len, orig));
408
398
  break;
409
399
  case T_STRING:
410
400
  rval = str_to_value(pi, str, len, orig);
@@ -456,13 +446,13 @@ WHICH_TYPE:
456
446
  rb_class2name(rb_obj_class(parent->val)));
457
447
  return;
458
448
  }
459
- if (Yes == pi->options.trace) {
449
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
460
450
  oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
461
451
  }
462
452
  }
463
453
 
464
454
  static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
465
- const char * key = kval->key;
455
+ const char *key = kval->key;
466
456
  int klen = kval->klen;
467
457
  Val parent = stack_peek(&pi->stack);
468
458
  volatile VALUE rval = Qnil;
@@ -481,8 +471,8 @@ WHICH_TYPE:
481
471
  rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), rval);
482
472
  break;
483
473
  case T_OBJECT:
484
- if (2 == klen && '^' == *key && 'i' == key[1] && !ni->infinity && !ni->neg &&
485
- 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
474
+ if (2 == klen && '^' == *key && 'i' == key[1] && !ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp &&
475
+ 0 != pi->circ_array) { // fixnum
486
476
  oj_circ_array_set(pi->circ_array, parent->val, ni->i);
487
477
  } else {
488
478
  rval = oj_num_as_value(ni);
@@ -527,7 +517,7 @@ WHICH_TYPE:
527
517
  rb_class2name(rb_obj_class(parent->val)));
528
518
  return;
529
519
  }
530
- if (Yes == pi->options.trace) {
520
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
531
521
  oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, rval);
532
522
  }
533
523
  }
@@ -559,11 +549,7 @@ WHICH_TYPE:
559
549
  volatile VALUE *a = RARRAY_PTR(value);
560
550
 
561
551
  if (2 != len) {
562
- oj_set_error_at(pi,
563
- oj_parse_error_class,
564
- __FILE__,
565
- __LINE__,
566
- "invalid hash pair");
552
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
567
553
  return;
568
554
  }
569
555
  rb_hash_aset(parent->val, *a, a[1]);
@@ -617,13 +603,13 @@ WHICH_TYPE:
617
603
  rb_class2name(rb_obj_class(parent->val)));
618
604
  return;
619
605
  }
620
- if (Yes == pi->options.trace) {
606
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
621
607
  oj_trace_parse_call("add_value", pi, __FILE__, __LINE__, value);
622
608
  }
623
609
  }
624
610
 
625
611
  static VALUE start_hash(ParseInfo pi) {
626
- if (Yes == pi->options.trace) {
612
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
627
613
  oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
628
614
  }
629
615
  return Qnil;
@@ -637,14 +623,11 @@ static void end_hash(ParseInfo pi) {
637
623
  } else if (NULL != parent->odd_args) {
638
624
  OddArgs oa = parent->odd_args;
639
625
 
640
- parent->val = rb_funcall2(oa->odd->create_obj,
641
- oa->odd->create_op,
642
- oa->odd->attr_cnt,
643
- oa->args);
626
+ parent->val = rb_funcall2(oa->odd->create_obj, oa->odd->create_op, oa->odd->attr_cnt, oa->args);
644
627
  oj_odd_free(oa);
645
628
  parent->odd_args = NULL;
646
629
  }
647
- if (Yes == pi->options.trace) {
630
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
648
631
  oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
649
632
  }
650
633
  }
@@ -653,8 +636,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
653
636
  volatile VALUE rval = Qnil;
654
637
 
655
638
  // orig lets us know whether the string was ^r1 or \u005er1
656
- if (3 <= len && 0 != pi->circ_array && '^' == orig[0] &&
657
- 0 == rb_array_len(stack_peek(&pi->stack)->val)) {
639
+ if (3 <= len && 0 != pi->circ_array && '^' == orig[0] && 0 == rb_array_len(stack_peek(&pi->stack)->val)) {
658
640
  if ('i' == str[1]) {
659
641
  long i = read_long(str + 2, len - 2);
660
642
 
@@ -673,7 +655,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
673
655
  }
674
656
  rval = str_to_value(pi, str, len, orig);
675
657
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
676
- if (Yes == pi->options.trace) {
658
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
677
659
  oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rval);
678
660
  }
679
661
  }
@@ -682,21 +664,21 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
682
664
  volatile VALUE rval = oj_num_as_value(ni);
683
665
 
684
666
  rb_ary_push(stack_peek(&pi->stack)->val, rval);
685
- if (Yes == pi->options.trace) {
667
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
686
668
  oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
687
669
  }
688
670
  }
689
671
 
690
672
  static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
691
673
  pi->stack.head->val = str_to_value(pi, str, len, orig);
692
- if (Yes == pi->options.trace) {
674
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
693
675
  oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
694
676
  }
695
677
  }
696
678
 
697
679
  static void add_num(ParseInfo pi, NumInfo ni) {
698
680
  pi->stack.head->val = oj_num_as_value(ni);
699
- if (Yes == pi->options.trace) {
681
+ if (RB_UNLIKELY(Yes == pi->options.trace)) {
700
682
  oj_trace_parse_call("add_num", pi, __FILE__, __LINE__, pi->stack.head->val);
701
683
  }
702
684
  }
data/ext/oj/odd.c CHANGED
@@ -5,28 +5,27 @@
5
5
 
6
6
  #include <string.h>
7
7
 
8
- static struct _odd _odds[4]; // bump up if new initial Odd classes are added
9
- static struct _odd *odds = _odds;
10
- static long odd_cnt = 0;
11
- static ID sec_id;
12
- static ID sec_fraction_id;
13
- static ID to_f_id;
14
- static ID numerator_id;
15
- static ID denominator_id;
16
- static ID rational_id;
17
- static VALUE rational_class;
8
+ static Odd odds = NULL;
9
+ static ID sec_id;
10
+ static ID sec_fraction_id;
11
+ static ID to_f_id;
12
+ static ID numerator_id;
13
+ static ID denominator_id;
14
+ static ID rational_id;
18
15
 
19
16
  static void set_class(Odd odd, const char *classname) {
20
17
  const char **np;
21
- ID * idp;
18
+ ID *idp;
22
19
 
23
- odd->classname = classname;
24
- odd->clen = strlen(classname);
25
- odd->clas = rb_const_get(rb_cObject, rb_intern(classname));
20
+ odd->classname = classname;
21
+ odd->clen = strlen(classname);
22
+ odd->clas = rb_const_get(rb_cObject, rb_intern(classname));
23
+ rb_gc_register_mark_object(odd->clas);
26
24
  odd->create_obj = odd->clas;
27
- odd->create_op = rb_intern("new");
28
- odd->is_module = (T_MODULE == rb_type(odd->clas));
29
- odd->raw = 0;
25
+ rb_gc_register_mark_object(odd->create_obj);
26
+ odd->create_op = rb_intern("new");
27
+ odd->is_module = (T_MODULE == rb_type(odd->clas));
28
+ odd->raw = 0;
30
29
  for (np = odd->attr_names, idp = odd->attrs; 0 != *np; np++, idp++) {
31
30
  *idp = rb_intern(*np);
32
31
  }
@@ -37,15 +36,48 @@ static VALUE get_datetime_secs(VALUE obj) {
37
36
  volatile VALUE rsecs = rb_funcall(obj, sec_id, 0);
38
37
  volatile VALUE rfrac = rb_funcall(obj, sec_fraction_id, 0);
39
38
  long sec = NUM2LONG(rsecs);
40
- long long num = rb_num2ll(rb_funcall(rfrac, numerator_id, 0));
41
- long long den = rb_num2ll(rb_funcall(rfrac, denominator_id, 0));
39
+ long long num = NUM2LL(rb_funcall(rfrac, numerator_id, 0));
40
+ long long den = NUM2LL(rb_funcall(rfrac, denominator_id, 0));
42
41
 
43
42
  num += sec * den;
44
43
 
45
44
  return rb_funcall(rb_cObject, rational_id, 2, rb_ll2inum(num), rb_ll2inum(den));
46
45
  }
47
46
 
48
- void oj_odd_init() {
47
+ static void print_odd(Odd odd) {
48
+ const char **np;
49
+ int i;
50
+
51
+ printf(" %s {\n", odd->classname);
52
+ printf(" attr_cnt: %d %p\n", odd->attr_cnt, (void *)odd->attr_names);
53
+ printf(" attr_names: %p\n", (void *)*odd->attr_names);
54
+ printf(" attr_names: %c\n", **odd->attr_names);
55
+ for (i = odd->attr_cnt, np = odd->attr_names; 0 < i; i--, np++) {
56
+ printf(" %d %s\n", i, *np);
57
+ }
58
+ printf(" }\n");
59
+ }
60
+
61
+ void print_all_odds(const char *label) {
62
+ Odd odd;
63
+ printf("@ %s {\n", label);
64
+ for (odd = odds; NULL != odd; odd = odd->next) {
65
+ print_odd(odd);
66
+ }
67
+ printf("}\n");
68
+ }
69
+
70
+ static Odd odd_create(void) {
71
+ Odd odd = ALLOC(struct _odd);
72
+
73
+ memset(odd, 0, sizeof(struct _odd));
74
+ odd->next = odds;
75
+ odds = odd;
76
+
77
+ return odd;
78
+ }
79
+
80
+ void oj_odd_init(void) {
49
81
  Odd odd;
50
82
  const char **np;
51
83
 
@@ -55,11 +87,9 @@ void oj_odd_init() {
55
87
  numerator_id = rb_intern("numerator");
56
88
  denominator_id = rb_intern("denominator");
57
89
  rational_id = rb_intern("Rational");
58
- rational_class = rb_const_get(rb_cObject, rational_id);
59
90
 
60
- memset(_odds, 0, sizeof(_odds));
61
- odd = odds;
62
91
  // Rational
92
+ odd = odd_create();
63
93
  np = odd->attr_names;
64
94
  *np++ = "numerator";
65
95
  *np++ = "denominator";
@@ -68,8 +98,9 @@ void oj_odd_init() {
68
98
  odd->create_obj = rb_cObject;
69
99
  odd->create_op = rational_id;
70
100
  odd->attr_cnt = 2;
101
+
71
102
  // Date
72
- odd++;
103
+ odd = odd_create();
73
104
  np = odd->attr_names;
74
105
  *np++ = "year";
75
106
  *np++ = "month";
@@ -78,8 +109,9 @@ void oj_odd_init() {
78
109
  *np++ = 0;
79
110
  set_class(odd, "Date");
80
111
  odd->attr_cnt = 4;
112
+
81
113
  // DateTime
82
- odd++;
114
+ odd = odd_create();
83
115
  np = odd->attr_names;
84
116
  *np++ = "year";
85
117
  *np++ = "month";
@@ -93,8 +125,9 @@ void oj_odd_init() {
93
125
  set_class(odd, "DateTime");
94
126
  odd->attr_cnt = 8;
95
127
  odd->attrFuncs[5] = get_datetime_secs;
128
+
96
129
  // Range
97
- odd++;
130
+ odd = odd_create();
98
131
  np = odd->attr_names;
99
132
  *np++ = "begin";
100
133
  *np++ = "end";
@@ -102,15 +135,13 @@ void oj_odd_init() {
102
135
  *np++ = 0;
103
136
  set_class(odd, "Range");
104
137
  odd->attr_cnt = 3;
105
-
106
- odd_cnt = odd - odds + 1;
107
138
  }
108
139
 
109
140
  Odd oj_get_odd(VALUE clas) {
110
141
  Odd odd;
111
142
  const char *classname = NULL;
112
143
 
113
- for (odd = odds + odd_cnt - 1; odds <= odd; odd--) {
144
+ for (odd = odds; NULL != odd; odd = odd->next) {
114
145
  if (clas == odd->clas) {
115
146
  return odd;
116
147
  }
@@ -129,21 +160,20 @@ Odd oj_get_odd(VALUE clas) {
129
160
  Odd oj_get_oddc(const char *classname, size_t len) {
130
161
  Odd odd;
131
162
 
132
- for (odd = odds + odd_cnt - 1; odds <= odd; odd--) {
163
+ for (odd = odds; NULL != odd; odd = odd->next) {
133
164
  if (len == odd->clen && 0 == strncmp(classname, odd->classname, len)) {
134
165
  return odd;
135
166
  }
136
- if (odd->is_module && 0 == strncmp(odd->classname, classname, odd->clen) &&
137
- ':' == classname[odd->clen]) {
167
+ if (odd->is_module && 0 == strncmp(odd->classname, classname, odd->clen) && ':' == classname[odd->clen]) {
138
168
  return odd;
139
169
  }
140
170
  }
141
- return 0;
171
+ return NULL;
142
172
  }
143
173
 
144
174
  OddArgs oj_odd_alloc_args(Odd odd) {
145
175
  OddArgs oa = ALLOC_N(struct _oddArgs, 1);
146
- VALUE * a;
176
+ VALUE *a;
147
177
  int i;
148
178
 
149
179
  oa->odd = odd;
@@ -159,11 +189,10 @@ void oj_odd_free(OddArgs args) {
159
189
 
160
190
  int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
161
191
  const char **np;
162
- VALUE * vp;
192
+ VALUE *vp;
163
193
  int i;
164
194
 
165
- for (i = args->odd->attr_cnt, np = args->odd->attr_names, vp = args->args; 0 < i;
166
- i--, np++, vp++) {
195
+ for (i = args->odd->attr_cnt, np = args->odd->attr_names, vp = args->args; 0 < i; i--, np++, vp++) {
167
196
  if (0 == strncmp(key, *np, klen) && '\0' == *((*np) + klen)) {
168
197
  *vp = value;
169
198
  return 0;
@@ -172,37 +201,26 @@ int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
172
201
  return -1;
173
202
  }
174
203
 
175
- void oj_reg_odd(VALUE clas,
176
- VALUE create_object,
177
- VALUE create_method,
178
- int mcnt,
179
- VALUE *members,
180
- bool raw) {
204
+ void oj_reg_odd(VALUE clas, VALUE create_object, VALUE create_method, int mcnt, VALUE *members, bool raw) {
181
205
  Odd odd;
182
206
  const char **np;
183
- ID * ap;
207
+ ID *ap;
184
208
  AttrGetFunc *fp;
185
209
 
186
- if (_odds == odds) {
187
- odds = ALLOC_N(struct _odd, odd_cnt + 1);
188
-
189
- memcpy(odds, _odds, sizeof(struct _odd) * odd_cnt);
190
- } else {
191
- REALLOC_N(odds, struct _odd, odd_cnt + 1);
192
- }
193
- odd = odds + odd_cnt;
210
+ odd = odd_create();
194
211
  odd->clas = clas;
212
+ rb_gc_register_mark_object(odd->clas);
195
213
  if (NULL == (odd->classname = strdup(rb_class2name(clas)))) {
196
- rb_raise(rb_eNoMemError, "for attribute name.");
214
+ rb_raise(rb_eNoMemError, "for class name.");
197
215
  }
198
216
  odd->clen = strlen(odd->classname);
199
217
  odd->create_obj = create_object;
200
- odd->create_op = SYM2ID(create_method);
201
- odd->attr_cnt = mcnt;
202
- odd->is_module = (T_MODULE == rb_type(clas));
203
- odd->raw = raw;
204
- for (ap = odd->attrs, np = odd->attr_names, fp = odd->attrFuncs; 0 < mcnt;
205
- mcnt--, ap++, np++, members++, fp++) {
218
+ rb_gc_register_mark_object(odd->create_obj);
219
+ odd->create_op = SYM2ID(create_method);
220
+ odd->attr_cnt = mcnt;
221
+ odd->is_module = (T_MODULE == rb_type(clas));
222
+ odd->raw = raw;
223
+ for (ap = odd->attrs, np = odd->attr_names, fp = odd->attrFuncs; 0 < mcnt; mcnt--, ap++, np++, members++, fp++) {
206
224
  *fp = 0;
207
225
  switch (rb_type(*members)) {
208
226
  case T_STRING:
@@ -210,14 +228,16 @@ void oj_reg_odd(VALUE clas,
210
228
  rb_raise(rb_eNoMemError, "for attribute name.");
211
229
  }
212
230
  break;
213
- case T_SYMBOL: *np = rb_id2name(SYM2ID(*members)); break;
214
- default:
215
- rb_raise(rb_eArgError, "registered member identifiers must be Strings or Symbols.");
231
+ case T_SYMBOL:
232
+ // The symbol can move and invalidate the name so make a copy.
233
+ if (NULL == (*np = strdup(rb_id2name(SYM2ID(*members))))) {
234
+ rb_raise(rb_eNoMemError, "for attribute name.");
235
+ }
216
236
  break;
237
+ default: rb_raise(rb_eArgError, "registered member identifiers must be Strings or Symbols."); break;
217
238
  }
218
239
  *ap = rb_intern(*np);
219
240
  }
220
241
  *np = 0;
221
242
  *ap = 0;
222
- odd_cnt++;
223
243
  }
data/ext/oj/odd.h CHANGED
@@ -13,17 +13,18 @@
13
13
  typedef VALUE (*AttrGetFunc)(VALUE obj);
14
14
 
15
15
  typedef struct _odd {
16
- const char *classname;
17
- size_t clen;
18
- VALUE clas; // Ruby class or module
19
- VALUE create_obj;
20
- ID create_op;
21
- int attr_cnt;
22
- bool is_module;
23
- bool raw;
24
- const char *attr_names[MAX_ODD_ARGS]; // NULL terminated attr names
25
- ID attrs[MAX_ODD_ARGS]; // 0 terminated attr IDs
26
- AttrGetFunc attrFuncs[MAX_ODD_ARGS];
16
+ struct _odd *next;
17
+ const char * classname;
18
+ size_t clen;
19
+ VALUE clas; // Ruby class or module
20
+ VALUE create_obj;
21
+ ID create_op;
22
+ int attr_cnt;
23
+ bool is_module;
24
+ bool raw;
25
+ const char * attr_names[MAX_ODD_ARGS]; // NULL terminated attr names
26
+ ID attrs[MAX_ODD_ARGS]; // 0 terminated attr IDs
27
+ AttrGetFunc attrFuncs[MAX_ODD_ARGS];
27
28
  } * Odd;
28
29
 
29
30
  typedef struct _oddArgs {
@@ -37,7 +38,6 @@ extern Odd oj_get_oddc(const char *classname, size_t len);
37
38
  extern OddArgs oj_odd_alloc_args(Odd odd);
38
39
  extern void oj_odd_free(OddArgs args);
39
40
  extern int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value);
40
- extern void
41
- oj_reg_odd(VALUE clas, VALUE create_object, VALUE create_method, int mcnt, VALUE *members, bool raw);
41
+ extern void oj_reg_odd(VALUE clas, VALUE create_object, VALUE create_method, int mcnt, VALUE *members, bool raw);
42
42
 
43
43
  #endif /* OJ_ODD_H */