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/encoder.c ADDED
@@ -0,0 +1,43 @@
1
+ // Copyright (c) 2011, 2022 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #include "oj.h"
5
+
6
+ typedef struct _encoder {
7
+ int indent; // indention for dump, default 2
8
+ char circular; // YesNo
9
+ char escape_mode; // Escape_Mode
10
+ char mode; // Mode
11
+ char time_format; // TimeFormat
12
+ char bigdec_as_num; // YesNo
13
+ char to_hash; // YesNo
14
+ char to_json; // YesNo
15
+ char as_json; // YesNo
16
+ char raw_json; // YesNo
17
+ char trace; // YesNo
18
+ char sec_prec_set; // boolean (0 or 1)
19
+ char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
20
+ int64_t int_range_min; // dump numbers below as string
21
+ int64_t int_range_max; // dump numbers above as string
22
+ const char* create_id; // 0 or string
23
+ size_t create_id_len; // length of create_id
24
+ int sec_prec; // second precision when dumping time
25
+ char float_prec; // float precision, linked to float_fmt
26
+ char float_fmt[7]; // float format for dumping, if empty use Ruby
27
+ struct _dumpOpts dump_opts;
28
+ struct _rxClass str_rx;
29
+ VALUE* ignore; // Qnil terminated array of classes or NULL
30
+ } * Encoder;
31
+
32
+ /*
33
+ rb_define_module_function(Oj, "encode", encode, -1);
34
+ rb_define_module_function(Oj, "to_file", to_file, -1); // or maybe just write
35
+ rb_define_module_function(Oj, "to_stream", to_stream, -1);
36
+ */
37
+
38
+ // write(to, obj)
39
+ // if to is a string then open file
40
+ // else if stream then write to stream
41
+ // handle non-blocking
42
+
43
+ // should each mode have a different encoder or use delegates like the parser?
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
@@ -13,6 +13,7 @@
13
13
 
14
14
  #include "encode.h"
15
15
  #include "oj.h"
16
+ #include "dump.h"
16
17
 
17
18
  // maximum to allocate on the stack, arbitrary limit
18
19
  #define SMALL_JSON 65536
@@ -676,21 +677,23 @@ static void free_doc_cb(void *x) {
676
677
  }
677
678
 
678
679
  static void mark_leaf(Leaf leaf) {
679
- switch (leaf->value_type) {
680
- case COL_VAL:
681
- if (NULL != leaf->elements) {
682
- Leaf first = leaf->elements->next;
683
- 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;
684
686
 
685
- do {
686
- mark_leaf(e);
687
- e = e->next;
688
- } while (e != first);
689
- }
690
- break;
691
- 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;
692
694
 
693
- default: break;
695
+ default: break;
696
+ }
694
697
  }
695
698
  }
696
699
 
@@ -771,7 +774,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
771
774
  pi.doc = doc;
772
775
  #if IS_WINDOWS
773
776
  // assume a 1M stack and give half to ruby
774
- pi.stack_min = (void*)((char*)&pi - (512 * 1024));
777
+ pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
775
778
  #else
776
779
  {
777
780
  struct rlimit lim;
@@ -879,6 +882,10 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
879
882
  }
880
883
  } else if (NULL == leaf->elements) {
881
884
  leaf = NULL;
885
+ } else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
886
+ // We are trying to get a children of a leaf, which
887
+ // doesn't exist.
888
+ leaf = NULL;
882
889
  } else if (COL_VAL == leaf->value_type) {
883
890
  Leaf first = leaf->elements->next;
884
891
  Leaf e = first;
@@ -1373,8 +1380,8 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1373
1380
  * Returns true if the value at the location identified by the path exists.
1374
1381
  * @param [String] path path to the location
1375
1382
  * @example
1376
- * Oj::Doc.open('[1,2]') { |doc| doc.exists('/1') } #=> true
1377
- * Oj::Doc.open('[1,2]') { |doc| doc.exists('/3') } #=> false
1383
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
1384
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
1378
1385
  */
1379
1386
  static VALUE doc_exists(VALUE self, VALUE str) {
1380
1387
  Doc doc;
@@ -1488,6 +1495,7 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1488
1495
  Doc doc = self_doc(self);
1489
1496
  const char *path = 0;
1490
1497
  size_t wlen;
1498
+ Leaf * where_orig = doc->where;
1491
1499
 
1492
1500
  wlen = doc->where - doc->where_path;
1493
1501
  if (0 < wlen) {
@@ -1504,9 +1512,13 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1504
1512
  if (0 < wlen) {
1505
1513
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1506
1514
  }
1515
+ doc->where = where_orig;
1507
1516
  return Qnil;
1508
1517
  }
1509
1518
  }
1519
+ if (NULL == doc->where || NULL == *doc->where) {
1520
+ return Qnil;
1521
+ }
1510
1522
  if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1511
1523
  Leaf first = (*doc->where)->elements->next;
1512
1524
  Leaf e = first;
@@ -1521,6 +1533,7 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1521
1533
  if (0 < wlen) {
1522
1534
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1523
1535
  }
1536
+ doc->where = where_orig;
1524
1537
  }
1525
1538
  return Qnil;
1526
1539
  }
@@ -1600,18 +1613,15 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1600
1613
  volatile VALUE rjson;
1601
1614
 
1602
1615
  if (0 == filename) {
1603
- char buf[4096];
1604
1616
  struct _out out;
1605
1617
 
1606
- out.buf = buf;
1607
- out.end = buf + sizeof(buf) - 10;
1608
- out.allocated = false;
1618
+ oj_out_init(&out);
1619
+
1609
1620
  out.omit_nil = oj_default_options.dump_opts.omit_nil;
1610
1621
  oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1611
1622
  rjson = rb_str_new2(out.buf);
1612
- if (out.allocated) {
1613
- xfree(out.buf);
1614
- }
1623
+
1624
+ oj_out_free(&out);
1615
1625
  } else {
1616
1626
  oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1617
1627
  rjson = Qnil;
@@ -1707,8 +1717,10 @@ static VALUE doc_not_implemented(VALUE self) {
1707
1717
  * # Now try again using a path to Oj::Doc.fetch() directly and not using a
1708
1718
  * block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
1709
1719
  */
1710
- void oj_init_doc() {
1720
+ void oj_init_doc(void) {
1711
1721
  oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
1722
+ rb_gc_register_address(&oj_doc_class);
1723
+ rb_undef_alloc_func(oj_doc_class);
1712
1724
  rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
1713
1725
  rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
1714
1726
  rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
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) {
@@ -86,18 +83,22 @@ static VALUE form_attr(const char *str, size_t len) {
86
83
  return (VALUE)rb_intern3(buf, len + 1, oj_utf8_encoding);
87
84
  }
88
85
 
89
- void oj_hash_init() {
86
+ void oj_hash_init(void) {
90
87
  VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
88
+ rb_undef_alloc_func(cache_class);
91
89
 
92
- str_cache = cache_create(0, form_str, true, true);
90
+ rb_gc_register_address(&cache_class);
91
+ rb_undef_alloc_func(cache_class);
92
+
93
+ struct _cache *str_cache = cache_create(0, form_str, true, true);
93
94
  str_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, str_cache);
94
95
  rb_gc_register_address(&str_cache_obj);
95
96
 
96
- sym_cache = cache_create(0, form_sym, true, true);
97
+ struct _cache *sym_cache = cache_create(0, form_sym, true, true);
97
98
  sym_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, sym_cache);
98
99
  rb_gc_register_address(&sym_cache_obj);
99
100
 
100
- attr_cache = cache_create(0, form_attr, false, true);
101
+ struct _cache *attr_cache = cache_create(0, form_attr, false, true);
101
102
  attr_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, attr_cache);
102
103
  rb_gc_register_address(&attr_cache_obj);
103
104
 
@@ -118,18 +119,18 @@ oj_str_intern(const char *key, size_t len) {
118
119
  #if HAVE_RB_ENC_INTERNED_STR && 0
119
120
  return rb_enc_interned_str(key, len, rb_utf8_encoding());
120
121
  #else
121
- return cache_intern(str_cache, key, len);
122
+ return cache_intern(DATA_PTR(str_cache_obj), key, len);
122
123
  #endif
123
124
  }
124
125
 
125
126
  VALUE
126
127
  oj_sym_intern(const char *key, size_t len) {
127
- return cache_intern(sym_cache, key, len);
128
+ return cache_intern(DATA_PTR(sym_cache_obj), key, len);
128
129
  }
129
130
 
130
131
  ID
131
132
  oj_attr_intern(const char *key, size_t len) {
132
- return cache_intern(attr_cache, key, len);
133
+ return cache_intern(DATA_PTR(attr_cache_obj), key, len);
133
134
  }
134
135
 
135
136
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
@@ -186,6 +187,7 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
186
187
  char * end = class_name + sizeof(class_name) - 1;
187
188
  char * s;
188
189
  const char *n = name;
190
+ size_t nlen = len;
189
191
 
190
192
  clas = rb_cObject;
191
193
  for (s = class_name; 0 < len; n++, len--) {
@@ -208,7 +210,12 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
208
210
  }
209
211
  *s = '\0';
210
212
  if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
211
- oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class %s is not defined", name);
213
+ if (sizeof(class_name) <= nlen) {
214
+ nlen = sizeof(class_name) - 1;
215
+ }
216
+ strncpy(class_name, name, nlen);
217
+ class_name[nlen] = '\0';
218
+ oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class '%s' is not defined", class_name);
212
219
  if (Qnil != error_class) {
213
220
  pi->err_class = error_class;
214
221
  }
@@ -269,6 +276,7 @@ VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int
269
276
  bucket->len = len;
270
277
  bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
271
278
  }
279
+ rb_gc_register_mark_object(bucket->val);
272
280
  return bucket->val;
273
281
  }
274
282
 
@@ -281,8 +289,10 @@ char *oj_strndup(const char *s, size_t len) {
281
289
  return d;
282
290
  }
283
291
 
284
- void intern_cleanup() {
292
+ /*
293
+ void intern_cleanup(void) {
285
294
  cache_free(str_cache);
286
295
  cache_free(sym_cache);
287
296
  cache_free(attr_cache);
288
297
  }
298
+ */
data/ext/oj/intern.h CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  struct _parseInfo;
11
11
 
12
- extern void oj_hash_init();
12
+ extern void oj_hash_init(void);
13
13
 
14
14
  extern VALUE oj_str_intern(const char *key, size_t len);
15
15
  extern VALUE oj_sym_intern(const char *key, size_t len);
data/ext/oj/mimic_json.c CHANGED
@@ -6,8 +6,6 @@
6
6
  #include "oj.h"
7
7
  #include "parse.h"
8
8
 
9
- static VALUE symbolize_names_sym = Qundef;
10
-
11
9
  extern const char oj_json_class[];
12
10
 
13
11
  VALUE oj_array_nl_sym;
@@ -200,7 +198,6 @@ static int mimic_limit_arg(VALUE a) {
200
198
  * Returns [_String_] a JSON string.
201
199
  */
202
200
  static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
203
- char buf[4096];
204
201
  struct _out out;
205
202
  struct _options copts = oj_default_options;
206
203
  VALUE rstr;
@@ -208,9 +205,9 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
208
205
 
209
206
  copts.str_rx.head = NULL;
210
207
  copts.str_rx.tail = NULL;
211
- out.buf = buf;
212
- out.end = buf + sizeof(buf) - 10;
213
- out.allocated = false;
208
+
209
+ oj_out_init(&out);
210
+
214
211
  out.caller = CALLER_DUMP;
215
212
  copts.escape_mode = JXEsc;
216
213
  copts.mode = CompatMode;
@@ -259,9 +256,9 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
259
256
  rb_funcall2(io, oj_write_id, 1, args);
260
257
  rstr = io;
261
258
  }
262
- if (out.allocated) {
263
- xfree(out.buf);
264
- }
259
+
260
+ oj_out_free(&out);
261
+
265
262
  return rstr;
266
263
  }
267
264
 
@@ -274,7 +271,7 @@ static int mimic_walk(VALUE key, VALUE obj, VALUE proc) {
274
271
  size_t i;
275
272
 
276
273
  for (i = 0; i < cnt; i++) {
277
- mimic_walk(Qnil, rb_ary_entry(obj, i), proc);
274
+ mimic_walk(Qnil, RARRAY_AREF(obj, i), proc);
278
275
  }
279
276
  break;
280
277
  }
@@ -360,15 +357,16 @@ static VALUE mimic_dump_load(int argc, VALUE *argv, VALUE self) {
360
357
  }
361
358
 
362
359
  static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
363
- char buf[4096];
364
360
  struct _out out;
365
361
  VALUE rstr;
366
362
 
367
- memset(buf, 0, sizeof(buf));
363
+ if (0 == argc) {
364
+ rb_raise(rb_eArgError, "wrong number of arguments (0))");
365
+ }
366
+ memset(out.stack_buffer, 0, sizeof(out.stack_buffer));
367
+
368
+ oj_out_init(&out);
368
369
 
369
- out.buf = buf;
370
- out.end = buf + sizeof(buf) - 10;
371
- out.allocated = false;
372
370
  out.omit_nil = copts->dump_opts.omit_nil;
373
371
  out.caller = CALLER_GENERATE;
374
372
  // For obj.to_json or generate nan is not allowed but if called from dump
@@ -390,6 +388,10 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
390
388
  VALUE active_hack[1];
391
389
 
392
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
+ );
393
395
  oj_define_mimic_json(0, NULL, Qnil);
394
396
  }
395
397
  active_hack[0] = rb_funcall(state_class, oj_new_id, 0);
@@ -400,9 +402,9 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
400
402
  }
401
403
  rstr = rb_str_new2(out.buf);
402
404
  rstr = oj_encode(rstr);
403
- if (out.allocated) {
404
- xfree(out.buf);
405
- }
405
+
406
+ oj_out_free(&out);
407
+
406
408
  return rstr;
407
409
  }
408
410
 
@@ -459,9 +461,12 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
459
461
  // a Hash. I haven't dug deep enough to find out why but using a State
460
462
  // instance and not a Hash gives the desired behavior.
461
463
  *rargs = *argv;
462
- if (1 == argc) {
464
+ if (0 == argc) {
465
+ rb_raise(rb_eArgError, "wrong number of arguments (0))");
466
+ }
467
+ if (1 == argc || Qnil == argv[1]) {
463
468
  h = rb_hash_new();
464
- } else {
469
+ } else {
465
470
  h = argv[1];
466
471
  }
467
472
  if (!oj_hash_has_key(h, oj_indent_sym)) {
@@ -480,6 +485,10 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
480
485
  rb_hash_aset(h, oj_array_nl_sym, rb_str_new2("\n"));
481
486
  }
482
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
+ );
483
492
  oj_define_mimic_json(0, NULL, Qnil);
484
493
  }
485
494
  rargs[1] = rb_funcall(state_class, oj_new_id, 1, h);
@@ -501,6 +510,44 @@ oj_mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
501
510
  return mimic_generate_core(2, rargs, &copts);
502
511
  }
503
512
 
513
+ static int parse_options_cb(VALUE k, VALUE v, VALUE info) {
514
+ struct _parseInfo *pi = (struct _parseInfo *)info;
515
+
516
+ if (oj_symbolize_names_sym == k) {
517
+ pi->options.sym_key = (Qtrue == v) ? Yes : No;
518
+ } else if (oj_quirks_mode_sym == k) {
519
+ pi->options.quirks_mode = (Qtrue == v) ? Yes : No;
520
+ } else if (oj_create_additions_sym == k) {
521
+ pi->options.create_ok = (Qtrue == v) ? Yes : No;
522
+ } else if (oj_allow_nan_sym == k) {
523
+ pi->options.allow_nan = (Qtrue == v) ? Yes : No;
524
+ } else if (oj_hash_class_sym == k) {
525
+ if (Qnil == v) {
526
+ pi->options.hash_class = Qnil;
527
+ } else {
528
+ rb_check_type(v, T_CLASS);
529
+ pi->options.hash_class = v;
530
+ }
531
+ } else if (oj_object_class_sym == k) {
532
+ if (Qnil == v) {
533
+ pi->options.hash_class = Qnil;
534
+ } else {
535
+ rb_check_type(v, T_CLASS);
536
+ pi->options.hash_class = v;
537
+ }
538
+ } else if (oj_array_class_sym == k) {
539
+ if (Qnil == v) {
540
+ pi->options.array_class = Qnil;
541
+ } else {
542
+ rb_check_type(v, T_CLASS);
543
+ pi->options.array_class = v;
544
+ }
545
+ } else if (oj_decimal_class_sym == k) {
546
+ pi->options.compat_bigdec = (oj_bigdecimal_class == v);
547
+ }
548
+ return ST_CONTINUE;
549
+ }
550
+
504
551
  static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
505
552
  struct _parseInfo pi;
506
553
  VALUE ropts;
@@ -531,50 +578,8 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
531
578
  if (T_HASH != rb_type(ropts)) {
532
579
  rb_raise(rb_eArgError, "options must be a hash.");
533
580
  }
534
- if (Qundef == symbolize_names_sym) {
535
- symbolize_names_sym = ID2SYM(rb_intern("symbolize_names"));
536
- rb_gc_register_address(&symbolize_names_sym);
537
- }
538
- if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
539
- pi.options.sym_key = (Qtrue == v) ? Yes : No;
540
- }
541
- if (Qnil != (v = rb_hash_lookup(ropts, oj_quirks_mode_sym))) {
542
- pi.options.quirks_mode = (Qtrue == v) ? Yes : No;
543
- }
544
- if (Qnil != (v = rb_hash_lookup(ropts, oj_create_additions_sym))) {
545
- pi.options.create_ok = (Qtrue == v) ? Yes : No;
546
- }
547
- if (Qnil != (v = rb_hash_lookup(ropts, oj_allow_nan_sym))) {
548
- pi.options.allow_nan = (Qtrue == v) ? Yes : No;
549
- }
550
581
 
551
- if (oj_hash_has_key(ropts, oj_hash_class_sym)) {
552
- if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
553
- pi.options.hash_class = Qnil;
554
- } else {
555
- rb_check_type(v, T_CLASS);
556
- pi.options.hash_class = v;
557
- }
558
- }
559
- if (oj_hash_has_key(ropts, oj_object_class_sym)) {
560
- if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
561
- pi.options.hash_class = Qnil;
562
- } else {
563
- rb_check_type(v, T_CLASS);
564
- pi.options.hash_class = v;
565
- }
566
- }
567
- if (oj_hash_has_key(ropts, oj_array_class_sym)) {
568
- if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
569
- pi.options.array_class = Qnil;
570
- } else {
571
- rb_check_type(v, T_CLASS);
572
- pi.options.array_class = v;
573
- }
574
- }
575
- if (oj_hash_has_key(ropts, oj_decimal_class_sym)) {
576
- pi.options.compat_bigdec = (oj_bigdecimal_class == rb_hash_lookup(ropts, oj_decimal_class_sym));
577
- }
582
+ rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
578
583
  v = rb_hash_lookup(ropts, oj_max_nesting_sym);
579
584
  if (Qtrue == v) {
580
585
  pi.max_depth = 100;
@@ -748,16 +753,15 @@ static struct _options mimic_object_to_json_options = {0, // indent
748
753
  }};
749
754
 
750
755
  static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
751
- char buf[4096];
752
756
  struct _out out;
753
757
  VALUE rstr;
754
758
  struct _options copts = oj_default_options;
755
759
 
756
760
  copts.str_rx.head = NULL;
757
761
  copts.str_rx.tail = NULL;
758
- out.buf = buf;
759
- out.end = buf + sizeof(buf) - 10;
760
- out.allocated = false;
762
+
763
+ oj_out_init(&out);
764
+
761
765
  out.omit_nil = copts.dump_opts.omit_nil;
762
766
  copts.mode = CompatMode;
763
767
  copts.to_json = No;
@@ -773,9 +777,9 @@ static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
773
777
  }
774
778
  rstr = rb_str_new2(out.buf);
775
779
  rstr = oj_encode(rstr);
776
- if (out.allocated) {
777
- xfree(out.buf);
778
- }
780
+
781
+ oj_out_free(&out);
782
+
779
783
  return rstr;
780
784
  }
781
785
 
@@ -852,9 +856,6 @@ void oj_mimic_json_methods(VALUE json) {
852
856
  // Pull in the JSON::State mimic file.
853
857
  state_class = rb_const_get_at(generator, rb_intern("State"));
854
858
  rb_gc_register_mark_object(state_class);
855
-
856
- symbolize_names_sym = ID2SYM(rb_intern("symbolize_names"));
857
- rb_gc_register_address(&symbolize_names_sym);
858
859
  }
859
860
 
860
861
  /* Document-module: JSON