oj 3.11.5 → 3.16.5

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 (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +19 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +20 -6
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +10 -9
  9. data/ext/oj/circarray.c +8 -6
  10. data/ext/oj/circarray.h +2 -2
  11. data/ext/oj/code.c +19 -33
  12. data/ext/oj/code.h +2 -2
  13. data/ext/oj/compat.c +27 -77
  14. data/ext/oj/custom.c +86 -179
  15. data/ext/oj/debug.c +126 -0
  16. data/ext/oj/dump.c +256 -249
  17. data/ext/oj/dump.h +26 -12
  18. data/ext/oj/dump_compat.c +565 -642
  19. data/ext/oj/dump_leaf.c +17 -63
  20. data/ext/oj/dump_object.c +65 -187
  21. data/ext/oj/dump_strict.c +27 -51
  22. data/ext/oj/encoder.c +43 -0
  23. data/ext/oj/err.c +2 -13
  24. data/ext/oj/err.h +24 -8
  25. data/ext/oj/extconf.rb +21 -6
  26. data/ext/oj/fast.c +149 -149
  27. data/ext/oj/intern.c +313 -0
  28. data/ext/oj/intern.h +22 -0
  29. data/ext/oj/mem.c +318 -0
  30. data/ext/oj/mem.h +53 -0
  31. data/ext/oj/mimic_json.c +121 -106
  32. data/ext/oj/object.c +85 -162
  33. data/ext/oj/odd.c +89 -67
  34. data/ext/oj/odd.h +15 -15
  35. data/ext/oj/oj.c +542 -411
  36. data/ext/oj/oj.h +99 -73
  37. data/ext/oj/parse.c +175 -187
  38. data/ext/oj/parse.h +26 -24
  39. data/ext/oj/parser.c +1600 -0
  40. data/ext/oj/parser.h +101 -0
  41. data/ext/oj/rails.c +112 -159
  42. data/ext/oj/rails.h +1 -1
  43. data/ext/oj/reader.c +11 -14
  44. data/ext/oj/reader.h +4 -2
  45. data/ext/oj/resolve.c +5 -24
  46. data/ext/oj/rxclass.c +7 -6
  47. data/ext/oj/rxclass.h +1 -1
  48. data/ext/oj/saj.c +22 -33
  49. data/ext/oj/saj2.c +584 -0
  50. data/ext/oj/saj2.h +23 -0
  51. data/ext/oj/scp.c +5 -28
  52. data/ext/oj/sparse.c +28 -72
  53. data/ext/oj/stream_writer.c +50 -40
  54. data/ext/oj/strict.c +56 -61
  55. data/ext/oj/string_writer.c +72 -39
  56. data/ext/oj/trace.h +31 -4
  57. data/ext/oj/usual.c +1218 -0
  58. data/ext/oj/usual.h +69 -0
  59. data/ext/oj/util.h +1 -1
  60. data/ext/oj/val_stack.c +14 -3
  61. data/ext/oj/val_stack.h +8 -7
  62. data/ext/oj/validate.c +46 -0
  63. data/ext/oj/wab.c +63 -88
  64. data/lib/oj/active_support_helper.rb +1 -3
  65. data/lib/oj/bag.rb +7 -1
  66. data/lib/oj/easy_hash.rb +4 -5
  67. data/lib/oj/error.rb +1 -2
  68. data/lib/oj/json.rb +162 -150
  69. data/lib/oj/mimic.rb +9 -7
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/schandler.rb +5 -4
  72. data/lib/oj/state.rb +12 -8
  73. data/lib/oj/version.rb +1 -2
  74. data/lib/oj.rb +2 -0
  75. data/pages/Compatibility.md +1 -1
  76. data/pages/InstallOptions.md +20 -0
  77. data/pages/JsonGem.md +15 -0
  78. data/pages/Modes.md +8 -3
  79. data/pages/Options.md +43 -5
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +14 -2
  82. data/test/_test_active.rb +8 -9
  83. data/test/_test_active_mimic.rb +7 -8
  84. data/test/_test_mimic_rails.rb +17 -20
  85. data/test/activerecord/result_test.rb +5 -6
  86. data/test/activesupport6/encoding_test.rb +63 -28
  87. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  88. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  89. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  90. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  91. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  92. data/test/files.rb +15 -15
  93. data/test/foo.rb +16 -45
  94. data/test/helper.rb +11 -8
  95. data/test/isolated/shared.rb +3 -2
  96. data/test/json_gem/json_addition_test.rb +2 -2
  97. data/test/json_gem/json_common_interface_test.rb +8 -6
  98. data/test/json_gem/json_encoding_test.rb +0 -0
  99. data/test/json_gem/json_ext_parser_test.rb +1 -0
  100. data/test/json_gem/json_fixtures_test.rb +3 -2
  101. data/test/json_gem/json_generator_test.rb +56 -38
  102. data/test/json_gem/json_generic_object_test.rb +11 -11
  103. data/test/json_gem/json_parser_test.rb +54 -47
  104. data/test/json_gem/json_string_matching_test.rb +9 -9
  105. data/test/json_gem/test_helper.rb +7 -3
  106. data/test/mem.rb +34 -0
  107. data/test/perf.rb +22 -27
  108. data/test/perf_compat.rb +31 -33
  109. data/test/perf_dump.rb +50 -0
  110. data/test/perf_fast.rb +80 -82
  111. data/test/perf_file.rb +27 -29
  112. data/test/perf_object.rb +65 -69
  113. data/test/perf_once.rb +59 -0
  114. data/test/perf_parser.rb +183 -0
  115. data/test/perf_saj.rb +46 -54
  116. data/test/perf_scp.rb +58 -69
  117. data/test/perf_simple.rb +41 -39
  118. data/test/perf_strict.rb +74 -82
  119. data/test/perf_wab.rb +67 -69
  120. data/test/prec.rb +5 -5
  121. data/test/sample/change.rb +0 -1
  122. data/test/sample/dir.rb +0 -1
  123. data/test/sample/doc.rb +0 -1
  124. data/test/sample/file.rb +0 -1
  125. data/test/sample/group.rb +0 -1
  126. data/test/sample/hasprops.rb +0 -1
  127. data/test/sample/layer.rb +0 -1
  128. data/test/sample/rect.rb +0 -1
  129. data/test/sample/shape.rb +0 -1
  130. data/test/sample/text.rb +0 -1
  131. data/test/sample.rb +16 -16
  132. data/test/sample_json.rb +8 -8
  133. data/test/test_compat.rb +95 -43
  134. data/test/test_custom.rb +73 -51
  135. data/test/test_debian.rb +7 -10
  136. data/test/test_fast.rb +135 -79
  137. data/test/test_file.rb +41 -30
  138. data/test/test_gc.rb +16 -5
  139. data/test/test_generate.rb +5 -5
  140. data/test/test_hash.rb +5 -5
  141. data/test/test_integer_range.rb +9 -9
  142. data/test/test_null.rb +20 -20
  143. data/test/test_object.rb +99 -96
  144. data/test/test_parser.rb +11 -0
  145. data/test/test_parser_debug.rb +27 -0
  146. data/test/test_parser_saj.rb +337 -0
  147. data/test/test_parser_usual.rb +251 -0
  148. data/test/test_rails.rb +2 -2
  149. data/test/test_saj.rb +10 -8
  150. data/test/test_scp.rb +37 -39
  151. data/test/test_strict.rb +40 -32
  152. data/test/test_various.rb +165 -84
  153. data/test/test_wab.rb +48 -44
  154. data/test/test_writer.rb +47 -47
  155. data/test/tests.rb +13 -5
  156. data/test/tests_mimic.rb +12 -3
  157. data/test/tests_mimic_addition.rb +12 -3
  158. metadata +74 -128
  159. data/ext/oj/hash.c +0 -131
  160. data/ext/oj/hash.h +0 -19
  161. data/ext/oj/hash_test.c +0 -491
  162. data/test/activesupport4/decoding_test.rb +0 -108
  163. data/test/activesupport4/encoding_test.rb +0 -531
  164. data/test/activesupport4/test_helper.rb +0 -41
  165. data/test/activesupport5/test_helper.rb +0 -72
  166. data/test/bar.rb +0 -35
  167. data/test/baz.rb +0 -16
  168. data/test/zoo.rb +0 -13
data/ext/oj/oj.c CHANGED
@@ -13,7 +13,8 @@
13
13
 
14
14
  #include "dump.h"
15
15
  #include "encode.h"
16
- #include "hash.h"
16
+ #include "intern.h"
17
+ #include "mem.h"
17
18
  #include "odd.h"
18
19
  #include "parse.h"
19
20
  #include "rails.h"
@@ -21,7 +22,7 @@
21
22
  typedef struct _yesNoOpt {
22
23
  VALUE sym;
23
24
  char *attr;
24
- } * YesNoOpt;
25
+ } *YesNoOpt;
25
26
 
26
27
  void Init_oj();
27
28
 
@@ -35,22 +36,22 @@ ID oj_as_json_id;
35
36
  ID oj_begin_id;
36
37
  ID oj_bigdecimal_id;
37
38
  ID oj_end_id;
39
+ ID oj_eofq_id;
38
40
  ID oj_exclude_end_id;
39
41
  ID oj_error_id;
40
42
  ID oj_file_id;
41
43
  ID oj_fileno_id;
42
44
  ID oj_ftype_id;
43
- ID oj_has_key_id;
44
45
  ID oj_hash_end_id;
45
46
  ID oj_hash_key_id;
46
47
  ID oj_hash_set_id;
47
48
  ID oj_hash_start_id;
48
49
  ID oj_iconv_id;
49
- ID oj_instance_variables_id;
50
50
  ID oj_json_create_id;
51
51
  ID oj_length_id;
52
52
  ID oj_new_id;
53
53
  ID oj_parse_id;
54
+ ID oj_plus_id;
54
55
  ID oj_pos_id;
55
56
  ID oj_raw_json_id;
56
57
  ID oj_read_id;
@@ -95,6 +96,7 @@ VALUE oj_indent_sym;
95
96
  VALUE oj_object_class_sym;
96
97
  VALUE oj_quirks_mode_sym;
97
98
  VALUE oj_safe_sym;
99
+ VALUE oj_symbolize_names_sym;
98
100
  VALUE oj_trace_sym;
99
101
 
100
102
  static VALUE allow_blank_sym;
@@ -106,6 +108,9 @@ static VALUE auto_sym;
106
108
  static VALUE bigdecimal_as_decimal_sym;
107
109
  static VALUE bigdecimal_load_sym;
108
110
  static VALUE bigdecimal_sym;
111
+ static VALUE cache_keys_sym;
112
+ static VALUE cache_str_sym;
113
+ static VALUE cache_string_sym;
109
114
  static VALUE circular_sym;
110
115
  static VALUE class_cache_sym;
111
116
  static VALUE compat_bigdecimal_sym;
@@ -117,6 +122,7 @@ static VALUE escape_mode_sym;
117
122
  static VALUE integer_range_sym;
118
123
  static VALUE fast_sym;
119
124
  static VALUE float_prec_sym;
125
+ static VALUE float_format_sym;
120
126
  static VALUE float_sym;
121
127
  static VALUE huge_sym;
122
128
  static VALUE ignore_sym;
@@ -129,11 +135,13 @@ static VALUE newline_sym;
129
135
  static VALUE nilnil_sym;
130
136
  static VALUE null_sym;
131
137
  static VALUE object_sym;
138
+ static VALUE omit_null_byte_sym;
132
139
  static VALUE omit_nil_sym;
133
140
  static VALUE rails_sym;
134
141
  static VALUE raise_sym;
135
142
  static VALUE ruby_sym;
136
143
  static VALUE sec_prec_sym;
144
+ static VALUE slash_sym;
137
145
  static VALUE strict_sym;
138
146
  static VALUE symbol_keys_sym;
139
147
  static VALUE time_format_sym;
@@ -149,7 +157,8 @@ static VALUE word_sym;
149
157
  static VALUE xmlschema_sym;
150
158
  static VALUE xss_safe_sym;
151
159
 
152
- rb_encoding *oj_utf8_encoding = 0;
160
+ rb_encoding *oj_utf8_encoding = 0;
161
+ int oj_utf8_encoding_index = 0;
153
162
 
154
163
  #ifdef HAVE_PTHREAD_MUTEX_INIT
155
164
  pthread_mutex_t oj_cache_mutex;
@@ -157,6 +166,8 @@ pthread_mutex_t oj_cache_mutex;
157
166
  VALUE oj_cache_mutex = Qnil;
158
167
  #endif
159
168
 
169
+ extern void oj_parser_init();
170
+
160
171
  const char oj_json_class[] = "json_class";
161
172
 
162
173
  struct _options oj_default_options = {
@@ -186,6 +197,8 @@ struct _options oj_default_options = {
186
197
  No, // safe
187
198
  false, // sec_prec_set
188
199
  No, // ignore_under
200
+ Yes, // cache_keys
201
+ 0, // cache_str
189
202
  0, // int_range_min
190
203
  0, // int_range_max
191
204
  oj_json_class, // create_id
@@ -210,6 +223,7 @@ struct _options oj_default_options = {
210
223
  0, // array_size
211
224
  AutoNan, // nan_dump
212
225
  false, // omit_nil
226
+ false, // omit_null_byte
213
227
  MAX_DEPTH, // max_depth
214
228
  },
215
229
  {
@@ -218,74 +232,89 @@ struct _options oj_default_options = {
218
232
  NULL, // tail
219
233
  {'\0'}, // err
220
234
  },
221
- NULL, // ignore
235
+ NULL,
222
236
  };
223
237
 
224
238
  /* Document-method: default_options()
225
239
  * call-seq: default_options()
226
240
  *
227
241
  * Returns the default load and dump options as a Hash. The options are
228
- * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in an JSON
229
- *document, zero or nil is no newline between JSON elements, negative indicates no newline between
230
- *top level JSON elements in a stream, a String indicates the string should be used for indentation
231
- * - *:circular* [_Boolean_|_nil_] support circular references while dumping as well as shared
232
- *references
242
+ * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element
243
+ * in an JSON document, zero or nil is no newline between JSON elements,
244
+ * negative indicates no newline between top level JSON elements in a stream,
245
+ * a String indicates the string should be used for indentation
246
+ * - *:circular* [_Boolean_|_nil_] support circular references while dumping as
247
+ * well as shared references
233
248
  * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist
234
249
  * - *:symbol_keys* [_Boolean_|_nil_] use symbols instead of strings for hash keys
235
- * - *:escape_mode* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] determines the
236
- *characters to escape
237
- * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing (if dynamically modifying
238
- *classes or reloading classes then don't use this)
239
- * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump modes
240
- *to use for JSON
250
+ * - *:escape_mode* [_:newline_|_:json_|_:slash_|_:xss_safe_|_:ascii_|_:unicode_xss_|_nil_]
251
+ * determines the characters to escape
252
+ * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing (if dynamically
253
+ * modifying classes or reloading classes then don't use this)
254
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and
255
+ * dump modes to use for JSON
241
256
  * - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping
242
- * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
243
- * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_:fast_] load decimals as BigDecimal instead
244
- *of as a Float. :auto pick the most precise for the number of digits. :float should be the same as
245
- *ruby. :fast may require rounding but is must faster.
246
- * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float when in
247
- *compat or rails mode.
248
- * - *:create_id* [_String_|_nil_] create id for json compatible object encoding, default is
249
- *'json_class'
250
- * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on
251
- *load.
252
- * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
253
- *seconds portion of time
254
- * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
255
- *indicates use Ruby
257
+ * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or
258
+ * as a String
259
+ * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_:fast_|_:ruby_] load decimals
260
+ * as BigDecimal instead of as a Float. :auto pick the most precise for the number
261
+ * of digits. :float should be the same as ruby. :fast may require rounding but is
262
+ * must faster.
263
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as
264
+ * a Float when in compat or rails mode.
265
+ * - *:create_id* [_String_|_nil_] create id for json compatible object encoding,
266
+ * default is 'json_class'
267
+ * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using
268
+ * create_id on load.
269
+ * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when
270
+ * dumping the seconds portion of time
271
+ * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping
272
+ * floats, 0 indicates use Ruby
273
+ * - *:float_format* [_String_] the C printf format string for printing floats.
274
+ * Default follows the float_precision and will be changed if float_precision is
275
+ * changed. The string can be no more than 6 bytes.
256
276
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
257
277
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
258
278
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
259
- * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an
260
- *Exception
279
+ * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and
280
+ * not raise an Exception
261
281
  * - *:empty_string* [_Boolean_|_nil_] if true an empty input will not raise an Exception
262
282
  * - *:allow_gc* [_Boolean_|_nil_] allow or prohibit GC during parsing, default is true (allow)
263
- * - *:quirks_mode* [_true,_|_false_|_nil_] Allow single JSON values instead of documents, default
264
- *is true (allow)
265
- * - *:allow_invalid_unicode* [_true,_|_false_|_nil_] Allow invalid unicode, default is false (don't
266
- *allow)
267
- * - *:allow_nan* [_true,_|_false_|_nil_] Allow Nan, Infinity, and -Infinity to be parsed, default
268
- *is true (allow)
269
- * - *:indent_str* [_String_|_nil_] String to use for indentation, overriding the indent option is
270
- *not nil
271
- * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields
272
- * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields
283
+ * - *:quirks_mode* [_true,_|_false_|_nil_] Allow single JSON values instead of
284
+ * documents, default is true (allow)
285
+ * - *:allow_invalid_unicode* [_true,_|_false_|_nil_] Allow invalid unicode,
286
+ * default is false (don't allow)
287
+ * - *:allow_nan* [_true,_|_false_|_nil_] Allow Nan, Infinity, and -Infinity to
288
+ * be parsed, default is true (allow)
289
+ * - *:indent_str* [_String_|_nil_] String to use for indentation, overriding the
290
+ * indent option is not nil
291
+ * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON
292
+ * object fields
293
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in
294
+ * JSON object fields
273
295
  * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value
274
296
  * - *:array_nl* [_String_|_nil_] String to use after a JSON array value
275
- * - *:nan* [_:null_|_:huge_|_:word_|_:raise_|_:auto_] how to dump Infinity and NaN. :null places a
276
- *null, :huge places a huge number, :word places Infinity or NaN, :raise raises and exception, :auto
277
- *uses default for each mode.
278
- * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be
279
- *used
297
+ * - *:nan* [_:null_|_:huge_|_:word_|_:raise_|_:auto_] how to dump Infinity and
298
+ * NaN. :null places a null, :huge places a huge number, :word places Infinity
299
+ * or NaN, :raise raises and exception, :auto uses default for each mode.
300
+ * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load,
301
+ * :object_class can also be used
280
302
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
281
- * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
282
- * - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
283
- * - *:ignore_under* [Boolean] if true then attributes that start with _ are ignored when dumping in
284
- *object or custom mode.
303
+ * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil
304
+ * values are omitted
305
+ * - *:omit_null_byte* [_true_|_false_] if true null bytes in strings will be
306
+ * omitted when dumping
307
+ * - *:ignore* [_nil_|_Array_] either nil or an Array of classes to ignore when dumping
308
+ * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are
309
+ * ignored when dumping in object or custom mode.
310
+ * - *:cache_keys* [_Boolean_] if true then hash keys are cached if less than 35 bytes.
311
+ * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less
312
+ * than this are cached)
285
313
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
286
- * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false (trace is off)
287
- * - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default is false (safe is
288
- *off)
314
+ * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false
315
+ * (trace is off)
316
+ * - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default
317
+ * is false (safe is off)
289
318
  *
290
319
  * Return [_Hash_] all current option settings.
291
320
  */
@@ -300,100 +329,78 @@ static VALUE get_def_opts(VALUE self) {
300
329
  rb_hash_aset(opts, sec_prec_sym, INT2FIX(oj_default_options.sec_prec));
301
330
  rb_hash_aset(opts,
302
331
  circular_sym,
303
- (Yes == oj_default_options.circular)
304
- ? Qtrue
305
- : ((No == oj_default_options.circular) ? Qfalse : Qnil));
306
- rb_hash_aset(opts,
307
- class_cache_sym,
308
- (Yes == oj_default_options.class_cache)
309
- ? Qtrue
310
- : ((No == oj_default_options.class_cache) ? Qfalse : Qnil));
311
- rb_hash_aset(opts,
312
- auto_define_sym,
313
- (Yes == oj_default_options.auto_define)
314
- ? Qtrue
315
- : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
332
+ (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
333
+ rb_hash_aset(
334
+ opts,
335
+ class_cache_sym,
336
+ (Yes == oj_default_options.class_cache) ? Qtrue : ((No == oj_default_options.class_cache) ? Qfalse : Qnil));
337
+ rb_hash_aset(
338
+ opts,
339
+ auto_define_sym,
340
+ (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
316
341
  rb_hash_aset(opts,
317
342
  symbol_keys_sym,
318
- (Yes == oj_default_options.sym_key)
319
- ? Qtrue
320
- : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
321
- rb_hash_aset(opts,
322
- bigdecimal_as_decimal_sym,
323
- (Yes == oj_default_options.bigdec_as_num)
324
- ? Qtrue
325
- : ((No == oj_default_options.bigdec_as_num) ? Qfalse : Qnil));
326
- rb_hash_aset(opts,
327
- oj_create_additions_sym,
328
- (Yes == oj_default_options.create_ok)
329
- ? Qtrue
330
- : ((No == oj_default_options.create_ok) ? Qfalse : Qnil));
343
+ (Yes == oj_default_options.sym_key) ? Qtrue : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
344
+ rb_hash_aset(
345
+ opts,
346
+ bigdecimal_as_decimal_sym,
347
+ (Yes == oj_default_options.bigdec_as_num) ? Qtrue : ((No == oj_default_options.bigdec_as_num) ? Qfalse : Qnil));
348
+ rb_hash_aset(
349
+ opts,
350
+ oj_create_additions_sym,
351
+ (Yes == oj_default_options.create_ok) ? Qtrue : ((No == oj_default_options.create_ok) ? Qfalse : Qnil));
331
352
  rb_hash_aset(opts,
332
353
  use_to_json_sym,
333
- (Yes == oj_default_options.to_json)
334
- ? Qtrue
335
- : ((No == oj_default_options.to_json) ? Qfalse : Qnil));
354
+ (Yes == oj_default_options.to_json) ? Qtrue : ((No == oj_default_options.to_json) ? Qfalse : Qnil));
336
355
  rb_hash_aset(opts,
337
356
  use_to_hash_sym,
338
- (Yes == oj_default_options.to_hash)
339
- ? Qtrue
340
- : ((No == oj_default_options.to_hash) ? Qfalse : Qnil));
357
+ (Yes == oj_default_options.to_hash) ? Qtrue : ((No == oj_default_options.to_hash) ? Qfalse : Qnil));
341
358
  rb_hash_aset(opts,
342
359
  use_as_json_sym,
343
- (Yes == oj_default_options.as_json)
344
- ? Qtrue
345
- : ((No == oj_default_options.as_json) ? Qfalse : Qnil));
360
+ (Yes == oj_default_options.as_json) ? Qtrue : ((No == oj_default_options.as_json) ? Qfalse : Qnil));
346
361
  rb_hash_aset(opts,
347
362
  use_raw_json_sym,
348
- (Yes == oj_default_options.raw_json)
349
- ? Qtrue
350
- : ((No == oj_default_options.raw_json) ? Qfalse : Qnil));
363
+ (Yes == oj_default_options.raw_json) ? Qtrue : ((No == oj_default_options.raw_json) ? Qfalse : Qnil));
351
364
  rb_hash_aset(opts,
352
365
  nilnil_sym,
353
- (Yes == oj_default_options.nilnil)
354
- ? Qtrue
355
- : ((No == oj_default_options.nilnil) ? Qfalse : Qnil));
356
- rb_hash_aset(opts,
357
- empty_string_sym,
358
- (Yes == oj_default_options.empty_string)
359
- ? Qtrue
360
- : ((No == oj_default_options.empty_string) ? Qfalse : Qnil));
366
+ (Yes == oj_default_options.nilnil) ? Qtrue : ((No == oj_default_options.nilnil) ? Qfalse : Qnil));
367
+ rb_hash_aset(
368
+ opts,
369
+ empty_string_sym,
370
+ (Yes == oj_default_options.empty_string) ? Qtrue : ((No == oj_default_options.empty_string) ? Qfalse : Qnil));
361
371
  rb_hash_aset(opts,
362
372
  allow_gc_sym,
363
- (Yes == oj_default_options.allow_gc)
364
- ? Qtrue
365
- : ((No == oj_default_options.allow_gc) ? Qfalse : Qnil));
366
- rb_hash_aset(opts,
367
- oj_quirks_mode_sym,
368
- (Yes == oj_default_options.quirks_mode)
369
- ? Qtrue
370
- : ((No == oj_default_options.quirks_mode) ? Qfalse : Qnil));
371
- rb_hash_aset(opts,
372
- allow_invalid_unicode_sym,
373
- (Yes == oj_default_options.allow_invalid)
374
- ? Qtrue
375
- : ((No == oj_default_options.allow_invalid) ? Qfalse : Qnil));
376
- rb_hash_aset(opts,
377
- oj_allow_nan_sym,
378
- (Yes == oj_default_options.allow_nan)
379
- ? Qtrue
380
- : ((No == oj_default_options.allow_nan) ? Qfalse : Qnil));
373
+ (Yes == oj_default_options.allow_gc) ? Qtrue : ((No == oj_default_options.allow_gc) ? Qfalse : Qnil));
374
+ rb_hash_aset(
375
+ opts,
376
+ oj_quirks_mode_sym,
377
+ (Yes == oj_default_options.quirks_mode) ? Qtrue : ((No == oj_default_options.quirks_mode) ? Qfalse : Qnil));
378
+ rb_hash_aset(
379
+ opts,
380
+ allow_invalid_unicode_sym,
381
+ (Yes == oj_default_options.allow_invalid) ? Qtrue : ((No == oj_default_options.allow_invalid) ? Qfalse : Qnil));
382
+ rb_hash_aset(
383
+ opts,
384
+ oj_allow_nan_sym,
385
+ (Yes == oj_default_options.allow_nan) ? Qtrue : ((No == oj_default_options.allow_nan) ? Qfalse : Qnil));
381
386
  rb_hash_aset(opts,
382
387
  oj_trace_sym,
383
- (Yes == oj_default_options.trace)
384
- ? Qtrue
385
- : ((No == oj_default_options.trace) ? Qfalse : Qnil));
388
+ (Yes == oj_default_options.trace) ? Qtrue : ((No == oj_default_options.trace) ? Qfalse : Qnil));
386
389
  rb_hash_aset(opts,
387
390
  oj_safe_sym,
388
- (Yes == oj_default_options.safe)
389
- ? Qtrue
390
- : ((No == oj_default_options.safe) ? Qfalse : Qnil));
391
+ (Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
391
392
  rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
392
- rb_hash_aset(opts,
393
- ignore_under_sym,
394
- (Yes == oj_default_options.ignore_under)
395
- ? Qtrue
396
- : ((No == oj_default_options.ignore_under) ? Qfalse : Qnil));
393
+ rb_hash_aset(opts, float_format_sym, rb_str_new_cstr(oj_default_options.float_fmt));
394
+ rb_hash_aset(opts, cache_str_sym, INT2FIX(oj_default_options.cache_str));
395
+ rb_hash_aset(
396
+ opts,
397
+ ignore_under_sym,
398
+ (Yes == oj_default_options.ignore_under) ? Qtrue : ((No == oj_default_options.ignore_under) ? Qfalse : Qnil));
399
+ rb_hash_aset(
400
+ opts,
401
+ cache_keys_sym,
402
+ (Yes == oj_default_options.cache_keys) ? Qtrue : ((No == oj_default_options.cache_keys) ? Qfalse : Qnil));
403
+
397
404
  switch (oj_default_options.mode) {
398
405
  case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
399
406
  case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
@@ -419,6 +426,7 @@ static VALUE get_def_opts(VALUE self) {
419
426
  switch (oj_default_options.escape_mode) {
420
427
  case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
421
428
  case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
429
+ case SlashEsc: rb_hash_aset(opts, escape_mode_sym, slash_sym); break;
422
430
  case XSSEsc: rb_hash_aset(opts, escape_mode_sym, xss_safe_sym); break;
423
431
  case ASCIIEsc: rb_hash_aset(opts, escape_mode_sym, ascii_sym); break;
424
432
  case JXEsc: rb_hash_aset(opts, escape_mode_sym, unicode_xss_sym); break;
@@ -439,30 +447,25 @@ static VALUE get_def_opts(VALUE self) {
439
447
  default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
440
448
  }
441
449
  rb_hash_aset(opts, compat_bigdecimal_sym, oj_default_options.compat_bigdec ? Qtrue : Qfalse);
450
+ rb_hash_aset(opts,
451
+ create_id_sym,
452
+ (NULL == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
442
453
  rb_hash_aset(
443
454
  opts,
444
- create_id_sym,
445
- (NULL == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
446
- rb_hash_aset(opts,
447
- oj_space_sym,
448
- (0 == oj_default_options.dump_opts.after_size)
449
- ? Qnil
450
- : rb_str_new2(oj_default_options.dump_opts.after_sep));
451
- rb_hash_aset(opts,
452
- oj_space_before_sym,
453
- (0 == oj_default_options.dump_opts.before_size)
454
- ? Qnil
455
- : rb_str_new2(oj_default_options.dump_opts.before_sep));
456
- rb_hash_aset(opts,
457
- oj_object_nl_sym,
458
- (0 == oj_default_options.dump_opts.hash_size)
459
- ? Qnil
460
- : rb_str_new2(oj_default_options.dump_opts.hash_nl));
461
- rb_hash_aset(opts,
462
- oj_array_nl_sym,
463
- (0 == oj_default_options.dump_opts.array_size)
464
- ? Qnil
465
- : rb_str_new2(oj_default_options.dump_opts.array_nl));
455
+ oj_space_sym,
456
+ (0 == oj_default_options.dump_opts.after_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.after_sep));
457
+ rb_hash_aset(
458
+ opts,
459
+ oj_space_before_sym,
460
+ (0 == oj_default_options.dump_opts.before_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.before_sep));
461
+ rb_hash_aset(
462
+ opts,
463
+ oj_object_nl_sym,
464
+ (0 == oj_default_options.dump_opts.hash_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.hash_nl));
465
+ rb_hash_aset(
466
+ opts,
467
+ oj_array_nl_sym,
468
+ (0 == oj_default_options.dump_opts.array_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.array_nl));
466
469
 
467
470
  switch (oj_default_options.dump_opts.nan_dump) {
468
471
  case NullNan: rb_hash_aset(opts, nan_sym, null_sym); break;
@@ -473,13 +476,14 @@ static VALUE get_def_opts(VALUE self) {
473
476
  default: rb_hash_aset(opts, nan_sym, auto_sym); break;
474
477
  }
475
478
  rb_hash_aset(opts, omit_nil_sym, oj_default_options.dump_opts.omit_nil ? Qtrue : Qfalse);
479
+ rb_hash_aset(opts, omit_null_byte_sym, oj_default_options.dump_opts.omit_null_byte ? Qtrue : Qfalse);
476
480
  rb_hash_aset(opts, oj_hash_class_sym, oj_default_options.hash_class);
477
481
  rb_hash_aset(opts, oj_array_class_sym, oj_default_options.array_class);
478
482
 
479
483
  if (NULL == oj_default_options.ignore) {
480
484
  rb_hash_aset(opts, ignore_sym, Qnil);
481
485
  } else {
482
- VALUE * vp;
486
+ VALUE *vp;
483
487
  volatile VALUE a = rb_ary_new();
484
488
 
485
489
  for (vp = oj_default_options.ignore; Qnil != *vp; vp++) {
@@ -495,68 +499,69 @@ static VALUE get_def_opts(VALUE self) {
495
499
  *
496
500
  * Sets the default options for load and dump.
497
501
  * - *opts* [_Hash_] options to change
498
- * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in a JSON
499
- *document or the String to use for identation.
502
+ * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element
503
+ * in a JSON document or the String to use for indentation.
500
504
  * - :circular [_Boolean_|_nil_] support circular references while dumping.
501
505
  * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist.
502
506
  * - *:symbol_keys* [_Boolean_|_nil_] convert hash keys to symbols.
503
507
  * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing.
504
- * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] mode encodes all
505
- *high-bit characters as escaped sequences if :ascii, :json is standand UTF-8 JSON encoding,
506
- *:newline is the same as :json but newlines are not escaped, :unicode_xss allows unicode but
507
- *escapes &, <, and >, and any \u20xx characters along with some others, and :xss_safe escapes &, <,
508
- *and >, and some others.
509
- * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a
510
- *String.
511
- * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as BigDecimal instead
512
- *of as a Float. :auto pick the most precise for the number of digits.
513
- * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float in
514
- *compat mode.
515
- * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump mode
516
- *to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat
517
- *attempts to extract variable values from an Object using to_json() or to_hash() then it walks the
518
- *Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods
519
- *and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported
520
- *Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more
521
- *mimics rails and Active behavior.
522
- * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat mode :unix
523
- *decimal number denoting the number of seconds since 1/1/1970, :unix_zone decimal number denoting
524
- *the number of seconds since 1/1/1970 plus the utc_offset in the exponent, :xmlschema date-time
525
- *format taken from XML Schema as a String, :ruby Time.to_s formatted String.
508
+ * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_]
509
+ * mode encodes all high-bit characters as escaped sequences if :ascii, :json
510
+ * is standand UTF-8 JSON encoding, :newline is the same as :json but newlines
511
+ * are not escaped, :unicode_xss allows unicode but escapes &, <, and >, and
512
+ * any \u20xx characters along with some others, and :xss_safe escapes &, <,
513
+ * and >, and some others.
514
+ * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal
515
+ * number or as a String.
516
+ * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as
517
+ * BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
518
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead
519
+ * of as a Float in compat mode.
520
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load
521
+ * and dump mode to use for JSON :strict raises an exception when a non-supported
522
+ * Object is encountered. :compat attempts to extract variable values from an
523
+ * Object using to_json() or to_hash() then it walks the Object's variables if
524
+ * neither is found. The :object mode ignores to_hash() and to_json() methods
525
+ * and encodes variables using code internal to the Oj gem. The :null mode
526
+ * ignores non-supported Objects and replaces them with a null. The :custom
527
+ * mode honors all dump options. The :rails more mimics rails and Active behavior.
528
+ * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat
529
+ * mode :unix decimal number denoting the number of seconds since 1/1/1970,
530
+ * :unix_zone decimal number denoting the number of seconds since 1/1/1970
531
+ * plus the utc_offset in the exponent, :xmlschema date-time format taken
532
+ * from XML Schema as a String, :ruby Time.to_s formatted String.
526
533
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding
527
- * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on
528
- *load.
529
- * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
530
- *seconds portion of time.
531
- * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
532
- *indicates use Ruby.
534
+ * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on load.
535
+ * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal
536
+ * when dumping the seconds portion of time.
537
+ * - *:float_format* [_String_] the C printf format string for printing floats.
538
+ * Default follows the float_precision and will be changed if float_precision
539
+ * is changed. The string can be no more than 6 bytes.
540
+ * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby.
533
541
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
534
542
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false.
535
543
  * - *:use_to_hash* [_Boolean_|_nil_] call to_hash() methods on dump, default is false.
536
544
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false.
537
- * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an
538
- *Exception.
545
+ * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an Exception.
539
546
  * - *:allow_gc* [_Boolean_|_nil_] allow or prohibit GC during parsing, default is true (allow).
540
- * - *:quirks_mode* [_Boolean_|_nil_] allow single JSON values instead of documents, default is
541
- *true (allow).
542
- * - *:allow_invalid_unicode* [_Boolean_|_nil_] allow invalid unicode, default is false (don't
543
- *allow).
547
+ * - *:quirks_mode* [_Boolean_|_nil_] allow single JSON values instead of documents, default is true (allow).
548
+ * - *:allow_invalid_unicode* [_Boolean_|_nil_] allow invalid unicode, default is false (don't allow).
544
549
  * - *:allow_nan* [_Boolean_|_nil_] allow Nan, Infinity, and -Infinity, default is true (allow).
545
550
  * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields.
546
- * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object
547
- *fields.
551
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields.
548
552
  * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value.
549
553
  * - *:array_nl* [_String_|_nil_] String to use after a JSON array value
550
- * - *:nan* [_:null_|_:huge_|_:word_|_:raise_] how to dump Infinity and NaN in null, strict, and
551
- *compat mode. :null places a null, :huge places a huge number, :word places Infinity or NaN, :raise
552
- *raises and exception, :auto uses default for each mode.
553
- * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be
554
- *used.
554
+ * - *:nan* [_:null_|_:huge_|_:word_|_:raise_] how to dump Infinity and NaN in null,
555
+ * strict, and compat mode. :null places a null, :huge places a huge number, :word
556
+ * places Infinity or NaN, :raise raises and exception, :auto uses default for each mode.
557
+ * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be used.
555
558
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load.
556
559
  * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted.
557
560
  * - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
558
- * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when
559
- *dumping in object or custom mode.
561
+ * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are
562
+ * ignored when dumping in object or custom mode.
563
+ * - *:cache_keys* [_Boolean_] if true then hash keys are cached
564
+ * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
560
565
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
561
566
  * - *:trace* [_Boolean_] turn trace on or off.
562
567
  * - *:safe* [_Boolean_] turn safe mimic on or off.
@@ -568,7 +573,14 @@ static VALUE set_def_opts(VALUE self, VALUE opts) {
568
573
  return Qnil;
569
574
  }
570
575
 
571
- void oj_parse_options(VALUE ropts, Options copts) {
576
+ bool oj_hash_has_key(VALUE hash, VALUE key) {
577
+ if (Qundef == rb_hash_lookup2(hash, key, Qundef)) {
578
+ return false;
579
+ }
580
+ return true;
581
+ }
582
+
583
+ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
572
584
  struct _yesNoOpt ynos[] = {{circular_sym, &copts->circular},
573
585
  {auto_define_sym, &copts->auto_define},
574
586
  {symbol_keys_sym, &copts->sym_key},
@@ -589,16 +601,35 @@ void oj_parse_options(VALUE ropts, Options copts) {
589
601
  {oj_safe_sym, &copts->safe},
590
602
  {ignore_under_sym, &copts->ignore_under},
591
603
  {oj_create_additions_sym, &copts->create_ok},
604
+ {cache_keys_sym, &copts->cache_keys},
592
605
  {Qnil, 0}};
593
606
  YesNoOpt o;
594
- volatile VALUE v;
595
- size_t len;
596
607
 
597
- if (T_HASH != rb_type(ropts)) {
598
- return;
608
+ for (o = ynos; 0 != o->attr; o++) {
609
+ if (key == o->sym) {
610
+ if (Qnil == value) {
611
+ *o->attr = NotSet;
612
+ } else if (Qtrue == value) {
613
+ *o->attr = Yes;
614
+ } else if (Qfalse == value) {
615
+ *o->attr = No;
616
+ } else {
617
+ rb_raise(rb_eArgError, "%s must be true, false, or nil.", rb_id2name(key));
618
+ }
619
+ return true;
620
+ }
599
621
  }
600
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_indent_sym)) {
601
- v = rb_hash_lookup(ropts, oj_indent_sym);
622
+ return false;
623
+ }
624
+
625
+ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
626
+ Options copts = (Options)opts;
627
+ size_t len;
628
+
629
+ if (set_yesno_options(k, v, copts)) {
630
+ return ST_CONTINUE;
631
+ }
632
+ if (oj_indent_sym == k) {
602
633
  switch (rb_type(v)) {
603
634
  case T_NIL:
604
635
  copts->dump_opts.indent_size = 0;
@@ -622,19 +653,12 @@ void oj_parse_options(VALUE ropts, Options copts) {
622
653
  break;
623
654
  default: rb_raise(rb_eTypeError, "indent must be a Fixnum, String, or nil."); break;
624
655
  }
625
- }
626
- if (Qnil != (v = rb_hash_lookup(ropts, float_prec_sym))) {
656
+ } else if (float_prec_sym == k) {
627
657
  int n;
628
658
 
629
- #ifdef RUBY_INTEGER_UNIFICATION
630
659
  if (rb_cInteger != rb_obj_class(v)) {
631
660
  rb_raise(rb_eArgError, ":float_precision must be a Integer.");
632
661
  }
633
- #else
634
- if (T_FIXNUM != rb_type(v)) {
635
- rb_raise(rb_eArgError, ":float_precision must be a Fixnum.");
636
- }
637
- #endif
638
662
  n = FIX2INT(v);
639
663
  if (0 >= n) {
640
664
  *copts->float_fmt = '\0';
@@ -646,19 +670,27 @@ void oj_parse_options(VALUE ropts, Options copts) {
646
670
  sprintf(copts->float_fmt, "%%0.%dg", n);
647
671
  copts->float_prec = n;
648
672
  }
649
- }
650
- if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
673
+ } else if (cache_str_sym == k || cache_string_sym == k) {
651
674
  int n;
652
675
 
653
- #ifdef RUBY_INTEGER_UNIFICATION
654
676
  if (rb_cInteger != rb_obj_class(v)) {
655
- rb_raise(rb_eArgError, ":second_precision must be a Integer.");
677
+ rb_raise(rb_eArgError, ":cache_str must be a Integer.");
656
678
  }
657
- #else
658
- if (T_FIXNUM != rb_type(v)) {
659
- rb_raise(rb_eArgError, ":second_precision must be a Fixnum.");
679
+ n = FIX2INT(v);
680
+ if (0 >= n) {
681
+ copts->cache_str = 0;
682
+ } else {
683
+ if (32 < n) {
684
+ n = 32;
685
+ }
686
+ copts->cache_str = (char)n;
687
+ }
688
+ } else if (sec_prec_sym == k) {
689
+ int n;
690
+
691
+ if (rb_cInteger != rb_obj_class(v)) {
692
+ rb_raise(rb_eArgError, ":second_precision must be a Integer.");
660
693
  }
661
- #endif
662
694
  n = NUM2INT(v);
663
695
  if (0 > n) {
664
696
  n = 0;
@@ -670,8 +702,7 @@ void oj_parse_options(VALUE ropts, Options copts) {
670
702
  copts->sec_prec_set = true;
671
703
  }
672
704
  copts->sec_prec = n;
673
- }
674
- if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
705
+ } else if (mode_sym == k) {
675
706
  if (wab_sym == v) {
676
707
  copts->mode = WabMode;
677
708
  } else if (object_sym == v) {
@@ -687,11 +718,9 @@ void oj_parse_options(VALUE ropts, Options copts) {
687
718
  } else if (rails_sym == v) {
688
719
  copts->mode = RailsMode;
689
720
  } else {
690
- rb_raise(rb_eArgError,
691
- ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
721
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
692
722
  }
693
- }
694
- if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
723
+ } else if (time_format_sym == k) {
695
724
  if (unix_sym == v) {
696
725
  copts->time_format = UnixTime;
697
726
  } else if (unix_zone_sym == v) {
@@ -703,12 +732,13 @@ void oj_parse_options(VALUE ropts, Options copts) {
703
732
  } else {
704
733
  rb_raise(rb_eArgError, ":time_format must be :unix, :unix_zone, :xmlschema, or :ruby.");
705
734
  }
706
- }
707
- if (Qnil != (v = rb_hash_lookup(ropts, escape_mode_sym))) {
735
+ } else if (escape_mode_sym == k) {
708
736
  if (newline_sym == v) {
709
737
  copts->escape_mode = NLEsc;
710
738
  } else if (json_sym == v) {
711
739
  copts->escape_mode = JSONEsc;
740
+ } else if (slash_sym == v) {
741
+ copts->escape_mode = SlashEsc;
712
742
  } else if (xss_safe_sym == v) {
713
743
  copts->escape_mode = XSSEsc;
714
744
  } else if (ascii_sym == v) {
@@ -716,11 +746,13 @@ void oj_parse_options(VALUE ropts, Options copts) {
716
746
  } else if (unicode_xss_sym == v) {
717
747
  copts->escape_mode = JXEsc;
718
748
  } else {
719
- rb_raise(rb_eArgError,
720
- ":encoding must be :newline, :json, :xss_safe, :unicode_xss, or :ascii.");
749
+ rb_raise(rb_eArgError, ":encoding must be :newline, :json, :xss_safe, :unicode_xss, or :ascii.");
721
750
  }
722
- }
723
- if (Qnil != (v = rb_hash_lookup(ropts, bigdecimal_load_sym))) {
751
+ } else if (bigdecimal_load_sym == k) {
752
+ if (Qnil == v) {
753
+ return ST_CONTINUE;
754
+ }
755
+
724
756
  if (bigdecimal_sym == v || Qtrue == v) {
725
757
  copts->bigdec_load = BigDec;
726
758
  } else if (float_sym == v) {
@@ -732,12 +764,12 @@ void oj_parse_options(VALUE ropts, Options copts) {
732
764
  } else {
733
765
  rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
734
766
  }
735
- }
736
- if (Qnil != (v = rb_hash_lookup(ropts, compat_bigdecimal_sym))) {
767
+ } else if (compat_bigdecimal_sym == k) {
768
+ if (Qnil == v) {
769
+ return ST_CONTINUE;
770
+ }
737
771
  copts->compat_bigdec = (Qtrue == v);
738
- }
739
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
740
- v = rb_hash_lookup(ropts, oj_decimal_class_sym);
772
+ } else if (oj_decimal_class_sym == k) {
741
773
  if (rb_cFloat == v) {
742
774
  copts->compat_bigdec = false;
743
775
  } else if (oj_bigdecimal_class == v) {
@@ -745,12 +777,10 @@ void oj_parse_options(VALUE ropts, Options copts) {
745
777
  } else {
746
778
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
747
779
  }
748
- }
749
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, create_id_sym)) {
750
- v = rb_hash_lookup(ropts, create_id_sym);
780
+ } else if (create_id_sym == k) {
751
781
  if (Qnil == v) {
752
782
  if (oj_json_class != oj_default_options.create_id && NULL != copts->create_id) {
753
- xfree((char *)oj_default_options.create_id);
783
+ OJ_R_FREE((char *)oj_default_options.create_id);
754
784
  }
755
785
  copts->create_id = NULL;
756
786
  copts->create_id_len = 0;
@@ -759,32 +789,15 @@ void oj_parse_options(VALUE ropts, Options copts) {
759
789
 
760
790
  len = RSTRING_LEN(v);
761
791
  if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
762
- copts->create_id = ALLOC_N(char, len + 1);
792
+ copts->create_id = OJ_R_ALLOC_N(char, len + 1);
763
793
  strcpy((char *)copts->create_id, str);
764
794
  copts->create_id_len = len;
765
795
  }
766
796
  } else {
767
797
  rb_raise(rb_eArgError, ":create_id must be string.");
768
798
  }
769
- }
770
- for (o = ynos; 0 != o->attr; o++) {
771
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, o->sym)) {
772
- v = rb_hash_lookup(ropts, o->sym);
773
- if (Qnil == v) {
774
- *o->attr = NotSet;
775
- } else if (Qtrue == v) {
776
- *o->attr = Yes;
777
- } else if (Qfalse == v) {
778
- *o->attr = No;
779
- } else {
780
- rb_raise(rb_eArgError,
781
- "%s must be true, false, or nil.",
782
- rb_id2name(SYM2ID(o->sym)));
783
- }
784
- }
785
- }
786
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_space_sym)) {
787
- if (Qnil == (v = rb_hash_lookup(ropts, oj_space_sym))) {
799
+ } else if (oj_space_sym == k) {
800
+ if (Qnil == v) {
788
801
  copts->dump_opts.after_size = 0;
789
802
  *copts->dump_opts.after_sep = '\0';
790
803
  } else {
@@ -797,9 +810,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
797
810
  strcpy(copts->dump_opts.after_sep, StringValuePtr(v));
798
811
  copts->dump_opts.after_size = (uint8_t)len;
799
812
  }
800
- }
801
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_space_before_sym)) {
802
- if (Qnil == (v = rb_hash_lookup(ropts, oj_space_before_sym))) {
813
+ } else if (oj_space_before_sym == k) {
814
+ if (Qnil == v) {
803
815
  copts->dump_opts.before_size = 0;
804
816
  *copts->dump_opts.before_sep = '\0';
805
817
  } else {
@@ -812,9 +824,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
812
824
  strcpy(copts->dump_opts.before_sep, StringValuePtr(v));
813
825
  copts->dump_opts.before_size = (uint8_t)len;
814
826
  }
815
- }
816
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_nl_sym)) {
817
- if (Qnil == (v = rb_hash_lookup(ropts, oj_object_nl_sym))) {
827
+ } else if (oj_object_nl_sym == k) {
828
+ if (Qnil == v) {
818
829
  copts->dump_opts.hash_size = 0;
819
830
  *copts->dump_opts.hash_nl = '\0';
820
831
  } else {
@@ -827,9 +838,8 @@ void oj_parse_options(VALUE ropts, Options copts) {
827
838
  strcpy(copts->dump_opts.hash_nl, StringValuePtr(v));
828
839
  copts->dump_opts.hash_size = (uint8_t)len;
829
840
  }
830
- }
831
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_nl_sym)) {
832
- if (Qnil == (v = rb_hash_lookup(ropts, oj_array_nl_sym))) {
841
+ } else if (oj_array_nl_sym == k) {
842
+ if (Qnil == v) {
833
843
  copts->dump_opts.array_size = 0;
834
844
  *copts->dump_opts.array_nl = '\0';
835
845
  } else {
@@ -842,8 +852,10 @@ void oj_parse_options(VALUE ropts, Options copts) {
842
852
  strcpy(copts->dump_opts.array_nl, StringValuePtr(v));
843
853
  copts->dump_opts.array_size = (uint8_t)len;
844
854
  }
845
- }
846
- if (Qnil != (v = rb_hash_lookup(ropts, nan_sym))) {
855
+ } else if (nan_sym == k) {
856
+ if (Qnil == v) {
857
+ return ST_CONTINUE;
858
+ }
847
859
  if (null_sym == v) {
848
860
  copts->dump_opts.nan_dump = NullNan;
849
861
  } else if (huge_sym == v) {
@@ -857,11 +869,10 @@ void oj_parse_options(VALUE ropts, Options copts) {
857
869
  } else {
858
870
  rb_raise(rb_eArgError, ":nan must be :null, :huge, :word, :raise, or :auto.");
859
871
  }
860
- }
861
- copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
862
- 0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
863
- 0 < copts->dump_opts.array_size);
864
- if (Qnil != (v = rb_hash_lookup(ropts, omit_nil_sym))) {
872
+ } else if (omit_nil_sym == k) {
873
+ if (Qnil == v) {
874
+ return ST_CONTINUE;
875
+ }
865
876
  if (Qtrue == v) {
866
877
  copts->dump_opts.omit_nil = true;
867
878
  } else if (Qfalse == v) {
@@ -869,43 +880,49 @@ void oj_parse_options(VALUE ropts, Options copts) {
869
880
  } else {
870
881
  rb_raise(rb_eArgError, ":omit_nil must be true or false.");
871
882
  }
872
- }
873
- // This is here only for backwards compatibility with the original Oj.
874
- v = rb_hash_lookup(ropts, oj_ascii_only_sym);
875
- if (Qtrue == v) {
876
- copts->escape_mode = ASCIIEsc;
877
- } else if (Qfalse == v) {
878
- copts->escape_mode = JSONEsc;
879
- }
880
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_hash_class_sym)) {
881
- if (Qnil == (v = rb_hash_lookup(ropts, oj_hash_class_sym))) {
883
+ } else if (omit_null_byte_sym == k) {
884
+ if (Qnil == v) {
885
+ return ST_CONTINUE;
886
+ }
887
+ if (Qtrue == v) {
888
+ copts->dump_opts.omit_null_byte = true;
889
+ } else if (Qfalse == v) {
890
+ copts->dump_opts.omit_null_byte = false;
891
+ } else {
892
+ rb_raise(rb_eArgError, ":omit_null_byte must be true or false.");
893
+ }
894
+ } else if (oj_ascii_only_sym == k) {
895
+ // This is here only for backwards compatibility with the original Oj.
896
+ if (Qtrue == v) {
897
+ copts->escape_mode = ASCIIEsc;
898
+ } else if (Qfalse == v) {
899
+ copts->escape_mode = JSONEsc;
900
+ }
901
+ } else if (oj_hash_class_sym == k) {
902
+ if (Qnil == v) {
882
903
  copts->hash_class = Qnil;
883
904
  } else {
884
905
  rb_check_type(v, T_CLASS);
885
906
  copts->hash_class = v;
886
907
  }
887
- }
888
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_object_class_sym)) {
889
- if (Qnil == (v = rb_hash_lookup(ropts, oj_object_class_sym))) {
908
+ } else if (oj_object_class_sym == k) {
909
+ if (Qnil == v) {
890
910
  copts->hash_class = Qnil;
891
911
  } else {
892
912
  rb_check_type(v, T_CLASS);
893
913
  copts->hash_class = v;
894
914
  }
895
- }
896
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_array_class_sym)) {
897
- if (Qnil == (v = rb_hash_lookup(ropts, oj_array_class_sym))) {
915
+ } else if (oj_array_class_sym == k) {
916
+ if (Qnil == v) {
898
917
  copts->array_class = Qnil;
899
918
  } else {
900
919
  rb_check_type(v, T_CLASS);
901
920
  copts->array_class = v;
902
921
  }
903
- }
904
- oj_parse_opt_match_string(&copts->str_rx, ropts);
905
- if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, ignore_sym)) {
906
- xfree(copts->ignore);
922
+ } else if (ignore_sym == k) {
923
+ OJ_R_FREE(copts->ignore);
907
924
  copts->ignore = NULL;
908
- if (Qnil != (v = rb_hash_lookup(ropts, ignore_sym))) {
925
+ if (Qnil != v) {
909
926
  int cnt;
910
927
 
911
928
  rb_check_type(v, T_ARRAY);
@@ -913,16 +930,18 @@ void oj_parse_options(VALUE ropts, Options copts) {
913
930
  if (0 < cnt) {
914
931
  int i;
915
932
 
916
- copts->ignore = ALLOC_N(VALUE, cnt + 1);
933
+ copts->ignore = OJ_R_ALLOC_N(VALUE, cnt + 1);
917
934
  for (i = 0; i < cnt; i++) {
918
- copts->ignore[i] = rb_ary_entry(v, i);
935
+ copts->ignore[i] = RARRAY_AREF(v, i);
919
936
  }
920
937
  copts->ignore[i] = Qnil;
921
938
  }
922
939
  }
923
- }
924
- if (Qnil != (v = rb_hash_lookup(ropts, integer_range_sym))) {
925
- if (TYPE(v) == T_STRUCT && rb_obj_class(v) == rb_cRange) {
940
+ } else if (integer_range_sym == k) {
941
+ if (Qnil == v) {
942
+ return ST_CONTINUE;
943
+ }
944
+ if (rb_obj_class(v) == rb_cRange) {
926
945
  VALUE min = rb_funcall(v, oj_begin_id, 0);
927
946
  VALUE max = rb_funcall(v, oj_end_id, 0);
928
947
 
@@ -935,7 +954,45 @@ void oj_parse_options(VALUE ropts, Options copts) {
935
954
  } else if (Qfalse != v) {
936
955
  rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
937
956
  }
957
+ } else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
958
+ if (Qnil == v) {
959
+ return ST_CONTINUE;
960
+ }
961
+ copts->sym_key = (Qtrue == v) ? Yes : No;
962
+
963
+ } else if (oj_max_nesting_sym == k) {
964
+ if (Qtrue == v) {
965
+ copts->dump_opts.max_depth = 100;
966
+ } else if (Qfalse == v || Qnil == v) {
967
+ copts->dump_opts.max_depth = MAX_DEPTH;
968
+ } else if (T_FIXNUM == rb_type(v)) {
969
+ copts->dump_opts.max_depth = NUM2INT(v);
970
+ if (0 >= copts->dump_opts.max_depth) {
971
+ copts->dump_opts.max_depth = MAX_DEPTH;
972
+ }
973
+ }
974
+ } else if (float_format_sym == k) {
975
+ rb_check_type(v, T_STRING);
976
+ if (6 < (int)RSTRING_LEN(v)) {
977
+ rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
978
+ }
979
+ strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
980
+ copts->float_fmt[RSTRING_LEN(v)] = '\0';
981
+ }
982
+ return ST_CONTINUE;
983
+ }
984
+
985
+ void oj_parse_options(VALUE ropts, Options copts) {
986
+ if (T_HASH != rb_type(ropts)) {
987
+ return;
938
988
  }
989
+ rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
990
+ oj_parse_opt_match_string(&copts->str_rx, ropts);
991
+
992
+ copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
993
+ 0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
994
+ 0 < copts->dump_opts.array_size);
995
+ return;
939
996
  }
940
997
 
941
998
  static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
@@ -951,9 +1008,7 @@ static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
951
1008
  rb_raise(rb_eArgError, "%s", rc->err);
952
1009
  }
953
1010
  break;
954
- default:
955
- rb_raise(rb_eArgError, "for :match_string, keys must either a String or RegExp.");
956
- break;
1011
+ default: rb_raise(rb_eArgError, "for :match_string, keys must either a String or RegExp."); break;
957
1012
  }
958
1013
  return ST_CONTINUE;
959
1014
  }
@@ -1086,7 +1141,7 @@ static VALUE load(int argc, VALUE *argv, VALUE self) {
1086
1141
  * Returns [_Object_|_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
1087
1142
  */
1088
1143
  static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1089
- char * path;
1144
+ char *path;
1090
1145
  int fd;
1091
1146
  Mode mode = oj_default_options.mode;
1092
1147
  struct _parseInfo pi;
@@ -1094,7 +1149,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1094
1149
  if (1 > argc) {
1095
1150
  rb_raise(rb_eArgError, "Wrong number of arguments to load().");
1096
1151
  }
1097
- Check_Type(*argv, T_STRING);
1152
+ path = StringValuePtr(*argv);
1098
1153
  parse_info_init(&pi);
1099
1154
  pi.options = oj_default_options;
1100
1155
  pi.handler = Qnil;
@@ -1121,14 +1176,21 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1121
1176
  } else if (wab_sym == v) {
1122
1177
  mode = WabMode;
1123
1178
  } else {
1124
- rb_raise(
1125
- rb_eArgError,
1126
- ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
1179
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
1127
1180
  }
1128
1181
  }
1129
1182
  }
1130
- path = StringValuePtr(*argv);
1131
- if (0 == (fd = open(path, O_RDONLY))) {
1183
+ #ifdef _WIN32
1184
+ {
1185
+ WCHAR *wide_path;
1186
+ wide_path = rb_w32_mbstr_to_wstr(CP_UTF8, path, -1, NULL);
1187
+ fd = rb_w32_wopen(wide_path, O_RDONLY);
1188
+ OJ_FREE(wide_path);
1189
+ }
1190
+ #else
1191
+ fd = open(path, O_RDONLY);
1192
+ #endif
1193
+ if (0 == fd) {
1132
1194
  rb_raise(rb_eIOError, "%s", strerror(errno));
1133
1195
  }
1134
1196
  switch (mode) {
@@ -1200,6 +1262,35 @@ static VALUE safe_load(VALUE self, VALUE doc) {
1200
1262
  * - *io* [_IO__|_String_] IO Object to read from
1201
1263
  */
1202
1264
 
1265
+ struct dump_arg {
1266
+ struct _out *out;
1267
+ struct _options *copts;
1268
+ int argc;
1269
+ VALUE *argv;
1270
+ };
1271
+
1272
+ static VALUE dump_body(VALUE a) {
1273
+ volatile struct dump_arg *arg = (void *)a;
1274
+ VALUE rstr;
1275
+
1276
+ oj_dump_obj_to_json_using_params(*arg->argv, arg->copts, arg->out, arg->argc - 1, arg->argv + 1);
1277
+ if (0 == arg->out->buf) {
1278
+ rb_raise(rb_eNoMemError, "Not enough memory.");
1279
+ }
1280
+ rstr = rb_str_new2(arg->out->buf);
1281
+ rstr = oj_encode(rstr);
1282
+
1283
+ return rstr;
1284
+ }
1285
+
1286
+ static VALUE dump_ensure(VALUE a) {
1287
+ volatile struct dump_arg *arg = (void *)a;
1288
+
1289
+ oj_out_free(arg->out);
1290
+
1291
+ return Qnil;
1292
+ }
1293
+
1203
1294
  /* Document-method: dump
1204
1295
  * call-seq: dump(obj, options={})
1205
1296
  *
@@ -1208,10 +1299,9 @@ static VALUE safe_load(VALUE self, VALUE doc) {
1208
1299
  * - *options* [_Hash_] same as default_options
1209
1300
  */
1210
1301
  static VALUE dump(int argc, VALUE *argv, VALUE self) {
1211
- char buf[4096];
1302
+ struct dump_arg arg;
1212
1303
  struct _out out;
1213
1304
  struct _options copts = oj_default_options;
1214
- VALUE rstr;
1215
1305
 
1216
1306
  if (1 > argc) {
1217
1307
  rb_raise(rb_eArgError, "wrong number of arguments (0 for 1).");
@@ -1225,21 +1315,17 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1225
1315
  if (CompatMode == copts.mode && copts.escape_mode != ASCIIEsc) {
1226
1316
  copts.escape_mode = JSONEsc;
1227
1317
  }
1228
- out.buf = buf;
1229
- out.end = buf + sizeof(buf) - 10;
1230
- out.allocated = false;
1231
- out.omit_nil = copts.dump_opts.omit_nil;
1232
- out.caller = CALLER_DUMP;
1233
- oj_dump_obj_to_json_using_params(*argv, &copts, &out, argc - 1, argv + 1);
1234
- if (0 == out.buf) {
1235
- rb_raise(rb_eNoMemError, "Not enough memory.");
1236
- }
1237
- rstr = rb_str_new2(out.buf);
1238
- rstr = oj_encode(rstr);
1239
- if (out.allocated) {
1240
- xfree(out.buf);
1241
- }
1242
- return rstr;
1318
+ arg.out = &out;
1319
+ arg.copts = &copts;
1320
+ arg.argc = argc;
1321
+ arg.argv = argv;
1322
+
1323
+ oj_out_init(arg.out);
1324
+
1325
+ arg.out->omit_nil = copts.dump_opts.omit_nil;
1326
+ arg.out->omit_null_byte = copts.dump_opts.omit_null_byte;
1327
+
1328
+ return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
1243
1329
  }
1244
1330
 
1245
1331
  /* Document-method: to_json
@@ -1249,17 +1335,16 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1249
1335
  * will be called. The mode is set to :compat.
1250
1336
  * - *obj* [_Object_] Object to serialize as an JSON document String
1251
1337
  * - *options* [_Hash_]
1252
- * - *:max_nesting* [_boolean_] It true nesting is limited to 100. The option to detect circular
1253
- * references is available but is not compatible with the json gem., default is false
1254
- * - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be
1255
- * used as appropriate, default is true.
1256
- * - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true
1257
- * (allow).
1258
- * - *:indent* [_String_|_nil_] String to use for indentation, overriding the indent option if not
1259
- * nil.
1338
+ * - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100.
1339
+ * If a Fixnum nesting is set to the provided value. The option to detect
1340
+ * circular references is available but is not compatible with the json gem.,
1341
+ * default is false or unlimited.
1342
+ * - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and
1343
+ * Infinity will be used as appropriate, default is true.
1344
+ * - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true (allow).
1345
+ * - *:indent* [_String_|_nil_] String to use for indentation, overriding the indent option if not nil.
1260
1346
  * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields.
1261
- * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object
1262
- * fields.
1347
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields.
1263
1348
  * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value.
1264
1349
  * - *:array_nl* [_String_|_nil_] String to use after a JSON array value.
1265
1350
  * - *:trace* [_Boolean_] If true trace is turned on.
@@ -1267,7 +1352,6 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1267
1352
  * Returns [_String_] the encoded JSON.
1268
1353
  */
1269
1354
  static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1270
- char buf[4096];
1271
1355
  struct _out out;
1272
1356
  struct _options copts = oj_default_options;
1273
1357
  VALUE rstr;
@@ -1282,10 +1366,11 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1282
1366
  }
1283
1367
  copts.mode = CompatMode;
1284
1368
  copts.to_json = Yes;
1285
- out.buf = buf;
1286
- out.end = buf + sizeof(buf) - 10;
1287
- out.allocated = false;
1288
- out.omit_nil = copts.dump_opts.omit_nil;
1369
+
1370
+ oj_out_init(&out);
1371
+
1372
+ out.omit_nil = copts.dump_opts.omit_nil;
1373
+ out.omit_null_byte = copts.dump_opts.omit_null_byte;
1289
1374
  // For obj.to_json or generate nan is not allowed but if called from dump
1290
1375
  // it is.
1291
1376
  oj_dump_obj_to_json_using_params(*argv, &copts, &out, argc - 1, argv + 1);
@@ -1295,9 +1380,9 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1295
1380
  }
1296
1381
  rstr = rb_str_new2(out.buf);
1297
1382
  rstr = oj_encode(rstr);
1298
- if (out.allocated) {
1299
- xfree(out.buf);
1300
- }
1383
+
1384
+ oj_out_free(&out);
1385
+
1301
1386
  return rstr;
1302
1387
  }
1303
1388
 
@@ -1307,7 +1392,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1307
1392
  * Dumps an Object to the specified file.
1308
1393
  * - *file* [_String_] _path file path to write the JSON document to
1309
1394
  * - *obj* [_Object_] Object to serialize as an JSON document String
1310
- * - *options* [_Hash_] formating options
1395
+ * - *options* [_Hash_] formatting options
1311
1396
  * - *:indent* [_Fixnum_] format expected
1312
1397
  * - *:circular* [_Boolean_] allow circular references, default: false
1313
1398
  */
@@ -1317,7 +1402,6 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1317
1402
  if (3 == argc) {
1318
1403
  oj_parse_options(argv[2], &copts);
1319
1404
  }
1320
- Check_Type(*argv, T_STRING);
1321
1405
  oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
1322
1406
 
1323
1407
  return Qnil;
@@ -1329,7 +1413,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1329
1413
  * Dumps an Object to the specified IO stream.
1330
1414
  * - *io* [_IO_] IO stream to write the JSON document to
1331
1415
  * - *obj* [_Object_] Object to serialize as an JSON document String
1332
- * - *options* [_Hash_] formating options
1416
+ * - *options* [_Hash_] formatting options
1333
1417
  * - *:indent* [_Fixnum_] format expected
1334
1418
  * - *:circular* [_Boolean_] allow circular references, default: false
1335
1419
  */
@@ -1355,10 +1439,10 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
1355
1439
  *
1356
1440
  * - *clas* [_Class__|_Module_] Class or Module to be made special
1357
1441
  * - *create_object* [_Object_] object to call the create method on
1358
- * - *create_method* [_Symbol_] method on the clas that will create a new instance of the clas when
1359
- * given all the member values in the order specified.
1360
- * - *members* [_Symbol__|_String_] methods used to get the member values from instances of the
1361
- * clas.
1442
+ * - *create_method* [_Symbol_] method on the clas that will create a new instance
1443
+ * of the clas when given all the member values in the order specified.
1444
+ * - *members* [_Symbol__|_String_] methods used to get the member values from
1445
+ * instances of the clas.
1362
1446
  */
1363
1447
  static VALUE register_odd(int argc, VALUE *argv, VALUE self) {
1364
1448
  if (3 > argc) {
@@ -1391,10 +1475,10 @@ static VALUE register_odd(int argc, VALUE *argv, VALUE self) {
1391
1475
  *
1392
1476
  * - *clas* [_Class_|_Module_] Class or Module to be made special
1393
1477
  * - *create_object* [_Object_] object to call the create method on
1394
- * - *create_method* [_Symbol_] method on the clas that will create a new instance of the clas when
1395
- *given all the member values in the order specified.
1396
- * - *dump_method* [_Symbol_|_String_] method to call on the object being serialized to generate the
1397
- *raw JSON.
1478
+ * - *create_method* [_Symbol_] method on the clas that will create a new instance
1479
+ * of the clas when given all the member values in the order specified.
1480
+ * - *dump_method* [_Symbol_|_String_] method to call on the object being
1481
+ * serialized to generate the raw JSON.
1398
1482
  */
1399
1483
  static VALUE register_odd_raw(int argc, VALUE *argv, VALUE self) {
1400
1484
  if (3 > argc) {
@@ -1620,8 +1704,8 @@ extern VALUE oj_define_mimic_json(int argc, VALUE *argv, VALUE self);
1620
1704
  * - *:space_before* [_String_] String placed before a : delimiter
1621
1705
  * - *:object_nl* [_String_] String placed after a JSON object
1622
1706
  * - *:array_nl* [_String_] String placed after a JSON array
1623
- * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output.
1624
- * Note JSON.generate does support this even if it is not documented.
1707
+ * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters
1708
+ * in the output. Note JSON.generate does support this even if it is not documented.
1625
1709
  *
1626
1710
  * Returns [_String_]generated JSON.
1627
1711
  */
@@ -1636,13 +1720,20 @@ extern VALUE oj_optimize_rails(VALUE self);
1636
1720
 
1637
1721
  /*
1638
1722
  extern void oj_hash_test();
1639
-
1640
1723
  static VALUE
1641
1724
  hash_test(VALUE self) {
1642
1725
  oj_hash_test();
1643
1726
  return Qnil;
1644
1727
  }
1645
1728
  */
1729
+ /*
1730
+ extern void oj_hash_sizes();
1731
+ static VALUE
1732
+ hash_test(VALUE self) {
1733
+ oj_hash_sizes();
1734
+ return Qnil;
1735
+ }
1736
+ */
1646
1737
 
1647
1738
  static VALUE protect_require(VALUE x) {
1648
1739
  rb_require("time");
@@ -1650,6 +1741,18 @@ static VALUE protect_require(VALUE x) {
1650
1741
  return Qnil;
1651
1742
  }
1652
1743
 
1744
+ extern void print_all_odds(const char *label);
1745
+
1746
+ static VALUE debug_odd(VALUE self, VALUE label) {
1747
+ print_all_odds(RSTRING_PTR(label));
1748
+ return Qnil;
1749
+ }
1750
+
1751
+ static VALUE mem_report(VALUE self) {
1752
+ oj_mem_report();
1753
+ return Qnil;
1754
+ }
1755
+
1653
1756
  /* Document-module: Oj
1654
1757
  *
1655
1758
  * Optimized JSON (Oj), as the name implies was written to provide speed
@@ -1659,8 +1762,8 @@ static VALUE protect_require(VALUE x) {
1659
1762
  * global and options to methods allow additional behavior modifications. The
1660
1763
  * modes are:
1661
1764
  *
1662
- * - *:strict* mode will only allow the 7 basic JSON types to be serialized. Any other Object
1663
- * will raise an Exception.
1765
+ * - *:strict* mode will only allow the 7 basic JSON types to be serialized.
1766
+ * Any other Object will raise an Exception.
1664
1767
  *
1665
1768
  * - *:null* mode is similar to the :strict mode except any Object that is not
1666
1769
  * one of the JSON base types is replaced by a JSON null.
@@ -1678,12 +1781,19 @@ static VALUE protect_require(VALUE x) {
1678
1781
  *
1679
1782
  * - *:wab* specifically for WAB data exchange.
1680
1783
  */
1681
- void Init_oj() {
1784
+ void Init_oj(void) {
1682
1785
  int err = 0;
1683
1786
 
1787
+ #if HAVE_RB_EXT_RACTOR_SAFE
1788
+ rb_ext_ractor_safe(true);
1789
+ #endif
1684
1790
  Oj = rb_define_module("Oj");
1791
+ rb_gc_register_address(&Oj);
1685
1792
 
1686
1793
  oj_cstack_class = rb_define_class_under(Oj, "CStack", rb_cObject);
1794
+ rb_gc_register_address(&oj_cstack_class);
1795
+
1796
+ rb_undef_alloc_func(oj_cstack_class);
1687
1797
 
1688
1798
  oj_string_writer_init();
1689
1799
  oj_stream_writer_init();
@@ -1692,9 +1802,11 @@ void Init_oj() {
1692
1802
  // On Rubinius the require fails but can be done from a ruby file.
1693
1803
  rb_protect(protect_require, Qnil, &err);
1694
1804
  rb_require("stringio");
1695
- oj_utf8_encoding = rb_enc_find("UTF-8");
1805
+ oj_utf8_encoding_index = rb_enc_find_index("UTF-8");
1806
+ oj_utf8_encoding = rb_enc_from_index(oj_utf8_encoding_index);
1696
1807
 
1697
1808
  // rb_define_module_function(Oj, "hash_test", hash_test, 0);
1809
+ rb_define_module_function(Oj, "debug_odd", debug_odd, 1);
1698
1810
 
1699
1811
  rb_define_module_function(Oj, "default_options", get_def_opts, 0);
1700
1812
  rb_define_module_function(Oj, "default_options=", set_def_opts, 1);
@@ -1728,50 +1840,52 @@ void Init_oj() {
1728
1840
 
1729
1841
  rb_define_module_function(Oj, "optimize_rails", oj_optimize_rails, 0);
1730
1842
 
1731
- oj_add_value_id = rb_intern("add_value");
1732
- oj_array_append_id = rb_intern("array_append");
1733
- oj_array_end_id = rb_intern("array_end");
1734
- oj_array_start_id = rb_intern("array_start");
1735
- oj_as_json_id = rb_intern("as_json");
1736
- oj_begin_id = rb_intern("begin");
1737
- oj_bigdecimal_id = rb_intern("BigDecimal");
1738
- oj_end_id = rb_intern("end");
1739
- oj_error_id = rb_intern("error");
1740
- oj_exclude_end_id = rb_intern("exclude_end?");
1741
- oj_file_id = rb_intern("file?");
1742
- oj_fileno_id = rb_intern("fileno");
1743
- oj_ftype_id = rb_intern("ftype");
1744
- oj_has_key_id = rb_intern("has_key?");
1745
- oj_hash_end_id = rb_intern("hash_end");
1746
- oj_hash_key_id = rb_intern("hash_key");
1747
- oj_hash_set_id = rb_intern("hash_set");
1748
- oj_hash_start_id = rb_intern("hash_start");
1749
- oj_iconv_id = rb_intern("iconv");
1750
- oj_instance_variables_id = rb_intern("instance_variables");
1751
- oj_json_create_id = rb_intern("json_create");
1752
- oj_length_id = rb_intern("length");
1753
- oj_new_id = rb_intern("new");
1754
- oj_parse_id = rb_intern("parse");
1755
- oj_pos_id = rb_intern("pos");
1756
- oj_raw_json_id = rb_intern("raw_json");
1757
- oj_read_id = rb_intern("read");
1758
- oj_readpartial_id = rb_intern("readpartial");
1759
- oj_replace_id = rb_intern("replace");
1760
- oj_stat_id = rb_intern("stat");
1761
- oj_string_id = rb_intern("string");
1762
- oj_to_h_id = rb_intern("to_h");
1763
- oj_to_hash_id = rb_intern("to_hash");
1764
- oj_to_json_id = rb_intern("to_json");
1765
- oj_to_s_id = rb_intern("to_s");
1766
- oj_to_sym_id = rb_intern("to_sym");
1767
- oj_to_time_id = rb_intern("to_time");
1768
- oj_tv_nsec_id = rb_intern("tv_nsec");
1769
- oj_tv_sec_id = rb_intern("tv_sec");
1770
- oj_tv_usec_id = rb_intern("tv_usec");
1771
- oj_utc_id = rb_intern("utc");
1772
- oj_utc_offset_id = rb_intern("utc_offset");
1773
- oj_utcq_id = rb_intern("utc?");
1774
- oj_write_id = rb_intern("write");
1843
+ rb_define_module_function(Oj, "mem_report", mem_report, 0);
1844
+
1845
+ oj_add_value_id = rb_intern("add_value");
1846
+ oj_array_append_id = rb_intern("array_append");
1847
+ oj_array_end_id = rb_intern("array_end");
1848
+ oj_array_start_id = rb_intern("array_start");
1849
+ oj_as_json_id = rb_intern("as_json");
1850
+ oj_begin_id = rb_intern("begin");
1851
+ oj_bigdecimal_id = rb_intern("BigDecimal");
1852
+ oj_end_id = rb_intern("end");
1853
+ oj_eofq_id = rb_intern("eof?");
1854
+ oj_error_id = rb_intern("error");
1855
+ oj_exclude_end_id = rb_intern("exclude_end?");
1856
+ oj_file_id = rb_intern("file?");
1857
+ oj_fileno_id = rb_intern("fileno");
1858
+ oj_ftype_id = rb_intern("ftype");
1859
+ oj_hash_end_id = rb_intern("hash_end");
1860
+ oj_hash_key_id = rb_intern("hash_key");
1861
+ oj_hash_set_id = rb_intern("hash_set");
1862
+ oj_hash_start_id = rb_intern("hash_start");
1863
+ oj_iconv_id = rb_intern("iconv");
1864
+ oj_json_create_id = rb_intern("json_create");
1865
+ oj_length_id = rb_intern("length");
1866
+ oj_new_id = rb_intern("new");
1867
+ oj_parse_id = rb_intern("parse");
1868
+ oj_plus_id = rb_intern("+");
1869
+ oj_pos_id = rb_intern("pos");
1870
+ oj_raw_json_id = rb_intern("raw_json");
1871
+ oj_read_id = rb_intern("read");
1872
+ oj_readpartial_id = rb_intern("readpartial");
1873
+ oj_replace_id = rb_intern("replace");
1874
+ oj_stat_id = rb_intern("stat");
1875
+ oj_string_id = rb_intern("string");
1876
+ oj_to_h_id = rb_intern("to_h");
1877
+ oj_to_hash_id = rb_intern("to_hash");
1878
+ oj_to_json_id = rb_intern("to_json");
1879
+ oj_to_s_id = rb_intern("to_s");
1880
+ oj_to_sym_id = rb_intern("to_sym");
1881
+ oj_to_time_id = rb_intern("to_time");
1882
+ oj_tv_nsec_id = rb_intern("tv_nsec");
1883
+ oj_tv_sec_id = rb_intern("tv_sec");
1884
+ oj_tv_usec_id = rb_intern("tv_usec");
1885
+ oj_utc_id = rb_intern("utc");
1886
+ oj_utc_offset_id = rb_intern("utc_offset");
1887
+ oj_utcq_id = rb_intern("utc?");
1888
+ oj_write_id = rb_intern("write");
1775
1889
 
1776
1890
  rb_require("oj/bag");
1777
1891
  rb_require("oj/error");
@@ -1816,6 +1930,12 @@ void Init_oj() {
1816
1930
  rb_gc_register_address(&bigdecimal_load_sym);
1817
1931
  bigdecimal_sym = ID2SYM(rb_intern("bigdecimal"));
1818
1932
  rb_gc_register_address(&bigdecimal_sym);
1933
+ cache_keys_sym = ID2SYM(rb_intern("cache_keys"));
1934
+ rb_gc_register_address(&cache_keys_sym);
1935
+ cache_str_sym = ID2SYM(rb_intern("cache_str"));
1936
+ rb_gc_register_address(&cache_str_sym);
1937
+ cache_string_sym = ID2SYM(rb_intern("cache_string"));
1938
+ rb_gc_register_address(&cache_string_sym);
1819
1939
  circular_sym = ID2SYM(rb_intern("circular"));
1820
1940
  rb_gc_register_address(&circular_sym);
1821
1941
  class_cache_sym = ID2SYM(rb_intern("class_cache"));
@@ -1836,6 +1956,8 @@ void Init_oj() {
1836
1956
  rb_gc_register_address(&integer_range_sym);
1837
1957
  fast_sym = ID2SYM(rb_intern("fast"));
1838
1958
  rb_gc_register_address(&fast_sym);
1959
+ float_format_sym = ID2SYM(rb_intern("float_format"));
1960
+ rb_gc_register_address(&float_format_sym);
1839
1961
  float_prec_sym = ID2SYM(rb_intern("float_precision"));
1840
1962
  rb_gc_register_address(&float_prec_sym);
1841
1963
  float_sym = ID2SYM(rb_intern("float"));
@@ -1888,6 +2010,8 @@ void Init_oj() {
1888
2010
  rb_gc_register_address(&oj_quirks_mode_sym);
1889
2011
  oj_safe_sym = ID2SYM(rb_intern("safe"));
1890
2012
  rb_gc_register_address(&oj_safe_sym);
2013
+ omit_null_byte_sym = ID2SYM(rb_intern("omit_null_byte"));
2014
+ rb_gc_register_address(&omit_null_byte_sym);
1891
2015
  oj_space_before_sym = ID2SYM(rb_intern("space_before"));
1892
2016
  rb_gc_register_address(&oj_space_before_sym);
1893
2017
  oj_space_sym = ID2SYM(rb_intern("space"));
@@ -1904,10 +2028,14 @@ void Init_oj() {
1904
2028
  rb_gc_register_address(&ruby_sym);
1905
2029
  sec_prec_sym = ID2SYM(rb_intern("second_precision"));
1906
2030
  rb_gc_register_address(&sec_prec_sym);
2031
+ slash_sym = ID2SYM(rb_intern("slash"));
2032
+ rb_gc_register_address(&slash_sym);
1907
2033
  strict_sym = ID2SYM(rb_intern("strict"));
1908
2034
  rb_gc_register_address(&strict_sym);
1909
2035
  symbol_keys_sym = ID2SYM(rb_intern("symbol_keys"));
1910
2036
  rb_gc_register_address(&symbol_keys_sym);
2037
+ oj_symbolize_names_sym = ID2SYM(rb_intern("symbolize_names"));
2038
+ rb_gc_register_address(&oj_symbolize_names_sym);
1911
2039
  time_format_sym = ID2SYM(rb_intern("time_format"));
1912
2040
  rb_gc_register_address(&time_format_sym);
1913
2041
  unicode_xss_sym = ID2SYM(rb_intern("unicode_xss"));
@@ -1952,4 +2080,7 @@ void Init_oj() {
1952
2080
  rb_gc_register_address(&oj_cache_mutex);
1953
2081
  #endif
1954
2082
  oj_init_doc();
2083
+
2084
+ oj_parser_init();
2085
+ oj_scanner_init();
1955
2086
  }