oj 3.16.0 → 3.17.3

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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +107 -0
  3. data/README.md +0 -16
  4. data/ext/oj/cache.c +4 -2
  5. data/ext/oj/cache.h +3 -2
  6. data/ext/oj/compat.c +3 -3
  7. data/ext/oj/custom.c +17 -11
  8. data/ext/oj/dump.c +624 -90
  9. data/ext/oj/dump.h +9 -2
  10. data/ext/oj/dump_compat.c +15 -9
  11. data/ext/oj/dump_leaf.c +1 -1
  12. data/ext/oj/dump_object.c +32 -17
  13. data/ext/oj/dump_strict.c +20 -14
  14. data/ext/oj/extconf.rb +11 -9
  15. data/ext/oj/fast.c +50 -32
  16. data/ext/oj/intern.c +24 -7
  17. data/ext/oj/mimic_json.c +17 -11
  18. data/ext/oj/object.c +21 -13
  19. data/ext/oj/oj.c +387 -140
  20. data/ext/oj/oj.h +59 -55
  21. data/ext/oj/parse.c +248 -47
  22. data/ext/oj/parser.c +179 -89
  23. data/ext/oj/parser.h +4 -2
  24. data/ext/oj/rails.c +64 -42
  25. data/ext/oj/reader.c +1 -1
  26. data/ext/oj/rxclass.c +1 -1
  27. data/ext/oj/rxclass.h +1 -1
  28. data/ext/oj/safe.c +230 -0
  29. data/ext/oj/safe.h +79 -0
  30. data/ext/oj/saj.c +15 -10
  31. data/ext/oj/scp.c +3 -6
  32. data/ext/oj/simd.h +219 -0
  33. data/ext/oj/sparse.c +6 -3
  34. data/ext/oj/stream_writer.c +38 -28
  35. data/ext/oj/strict.c +2 -4
  36. data/ext/oj/string_writer.c +54 -22
  37. data/ext/oj/usual.c +57 -35
  38. data/ext/oj/usual.h +1 -0
  39. data/ext/oj/val_stack.c +13 -2
  40. data/ext/oj/wab.c +5 -4
  41. data/lib/oj/mimic.rb +1 -5
  42. data/lib/oj/schandler.rb +5 -4
  43. data/lib/oj/version.rb +1 -1
  44. data/pages/Encoding.md +1 -1
  45. metadata +36 -100
  46. data/test/_test_active.rb +0 -75
  47. data/test/_test_active_mimic.rb +0 -95
  48. data/test/_test_mimic_rails.rb +0 -123
  49. data/test/activerecord/result_test.rb +0 -31
  50. data/test/activesupport6/abstract_unit.rb +0 -44
  51. data/test/activesupport6/decoding_test.rb +0 -133
  52. data/test/activesupport6/encoding_test.rb +0 -507
  53. data/test/activesupport6/encoding_test_cases.rb +0 -98
  54. data/test/activesupport6/test_common.rb +0 -17
  55. data/test/activesupport6/test_helper.rb +0 -163
  56. data/test/activesupport6/time_zone_test_helpers.rb +0 -39
  57. data/test/activesupport7/abstract_unit.rb +0 -49
  58. data/test/activesupport7/decoding_test.rb +0 -125
  59. data/test/activesupport7/encoding_test.rb +0 -486
  60. data/test/activesupport7/encoding_test_cases.rb +0 -104
  61. data/test/activesupport7/time_zone_test_helpers.rb +0 -47
  62. data/test/files.rb +0 -29
  63. data/test/foo.rb +0 -14
  64. data/test/helper.rb +0 -39
  65. data/test/isolated/shared.rb +0 -309
  66. data/test/isolated/test_mimic_after.rb +0 -13
  67. data/test/isolated/test_mimic_alone.rb +0 -12
  68. data/test/isolated/test_mimic_as_json.rb +0 -45
  69. data/test/isolated/test_mimic_before.rb +0 -13
  70. data/test/isolated/test_mimic_define.rb +0 -28
  71. data/test/isolated/test_mimic_rails_after.rb +0 -22
  72. data/test/isolated/test_mimic_rails_before.rb +0 -21
  73. data/test/isolated/test_mimic_redefine.rb +0 -15
  74. data/test/json_gem/json_addition_test.rb +0 -216
  75. data/test/json_gem/json_common_interface_test.rb +0 -155
  76. data/test/json_gem/json_encoding_test.rb +0 -107
  77. data/test/json_gem/json_ext_parser_test.rb +0 -21
  78. data/test/json_gem/json_fixtures_test.rb +0 -36
  79. data/test/json_gem/json_generator_test.rb +0 -407
  80. data/test/json_gem/json_generic_object_test.rb +0 -90
  81. data/test/json_gem/json_parser_test.rb +0 -477
  82. data/test/json_gem/json_string_matching_test.rb +0 -42
  83. data/test/json_gem/test_helper.rb +0 -30
  84. data/test/mem.rb +0 -34
  85. data/test/perf.rb +0 -102
  86. data/test/perf_compat.rb +0 -128
  87. data/test/perf_dump.rb +0 -50
  88. data/test/perf_fast.rb +0 -162
  89. data/test/perf_file.rb +0 -62
  90. data/test/perf_object.rb +0 -134
  91. data/test/perf_once.rb +0 -59
  92. data/test/perf_parser.rb +0 -183
  93. data/test/perf_saj.rb +0 -101
  94. data/test/perf_scp.rb +0 -140
  95. data/test/perf_simple.rb +0 -289
  96. data/test/perf_strict.rb +0 -137
  97. data/test/perf_wab.rb +0 -129
  98. data/test/prec.rb +0 -23
  99. data/test/sample/change.rb +0 -13
  100. data/test/sample/dir.rb +0 -18
  101. data/test/sample/doc.rb +0 -35
  102. data/test/sample/file.rb +0 -47
  103. data/test/sample/group.rb +0 -15
  104. data/test/sample/hasprops.rb +0 -15
  105. data/test/sample/layer.rb +0 -11
  106. data/test/sample/line.rb +0 -20
  107. data/test/sample/oval.rb +0 -10
  108. data/test/sample/rect.rb +0 -9
  109. data/test/sample/shape.rb +0 -34
  110. data/test/sample/text.rb +0 -19
  111. data/test/sample.rb +0 -54
  112. data/test/sample_json.rb +0 -37
  113. data/test/test_compat.rb +0 -567
  114. data/test/test_custom.rb +0 -554
  115. data/test/test_debian.rb +0 -50
  116. data/test/test_fast.rb +0 -526
  117. data/test/test_file.rb +0 -250
  118. data/test/test_gc.rb +0 -60
  119. data/test/test_generate.rb +0 -21
  120. data/test/test_hash.rb +0 -39
  121. data/test/test_integer_range.rb +0 -72
  122. data/test/test_null.rb +0 -376
  123. data/test/test_object.rb +0 -1016
  124. data/test/test_parser.rb +0 -11
  125. data/test/test_parser_debug.rb +0 -27
  126. data/test/test_parser_saj.rb +0 -337
  127. data/test/test_parser_usual.rb +0 -217
  128. data/test/test_rails.rb +0 -35
  129. data/test/test_saj.rb +0 -188
  130. data/test/test_scp.rb +0 -431
  131. data/test/test_strict.rb +0 -431
  132. data/test/test_various.rb +0 -801
  133. data/test/test_wab.rb +0 -311
  134. data/test/test_writer.rb +0 -380
  135. data/test/tests.rb +0 -33
  136. data/test/tests_mimic.rb +0 -23
  137. data/test/tests_mimic_addition.rb +0 -16
data/ext/oj/oj.c CHANGED
@@ -18,6 +18,9 @@
18
18
  #include "odd.h"
19
19
  #include "parse.h"
20
20
  #include "rails.h"
21
+ #include "simd.h"
22
+
23
+ #define MAX_INDENT 16
21
24
 
22
25
  typedef struct _yesNoOpt {
23
26
  VALUE sym;
@@ -33,10 +36,10 @@ ID oj_array_append_id;
33
36
  ID oj_array_end_id;
34
37
  ID oj_array_start_id;
35
38
  ID oj_as_json_id;
36
- ID oj_at_id;
37
39
  ID oj_begin_id;
38
40
  ID oj_bigdecimal_id;
39
41
  ID oj_end_id;
42
+ ID oj_eofq_id;
40
43
  ID oj_exclude_end_id;
41
44
  ID oj_error_id;
42
45
  ID oj_file_id;
@@ -51,6 +54,7 @@ ID oj_json_create_id;
51
54
  ID oj_length_id;
52
55
  ID oj_new_id;
53
56
  ID oj_parse_id;
57
+ ID oj_plus_id;
54
58
  ID oj_pos_id;
55
59
  ID oj_raw_json_id;
56
60
  ID oj_read_id;
@@ -91,9 +95,7 @@ VALUE oj_array_class_sym;
91
95
  VALUE oj_create_additions_sym;
92
96
  VALUE oj_decimal_class_sym;
93
97
  VALUE oj_hash_class_sym;
94
- VALUE oj_in_sym;
95
98
  VALUE oj_indent_sym;
96
- VALUE oj_nanosecond_sym;
97
99
  VALUE oj_object_class_sym;
98
100
  VALUE oj_quirks_mode_sym;
99
101
  VALUE oj_safe_sym;
@@ -120,7 +122,9 @@ static VALUE create_id_sym;
120
122
  static VALUE custom_sym;
121
123
  static VALUE empty_string_sym;
122
124
  static VALUE escape_mode_sym;
125
+ static VALUE except_sym;
123
126
  static VALUE integer_range_sym;
127
+ static VALUE max_integer_digits_sym;
124
128
  static VALUE fast_sym;
125
129
  static VALUE float_prec_sym;
126
130
  static VALUE float_format_sym;
@@ -138,6 +142,7 @@ static VALUE null_sym;
138
142
  static VALUE object_sym;
139
143
  static VALUE omit_null_byte_sym;
140
144
  static VALUE omit_nil_sym;
145
+ static VALUE only_sym;
141
146
  static VALUE rails_sym;
142
147
  static VALUE raise_sym;
143
148
  static VALUE ruby_sym;
@@ -167,6 +172,8 @@ pthread_mutex_t oj_cache_mutex;
167
172
  VALUE oj_cache_mutex = Qnil;
168
173
  #endif
169
174
 
175
+ SIMD_Implementation SIMD_Impl = SIMD_NONE;
176
+
170
177
  extern void oj_parser_init();
171
178
 
172
179
  const char oj_json_class[] = "json_class";
@@ -202,6 +209,7 @@ struct _options oj_default_options = {
202
209
  0, // cache_str
203
210
  0, // int_range_min
204
211
  0, // int_range_max
212
+ 0, // max_integer_digits
205
213
  oj_json_class, // create_id
206
214
  10, // create_id_len
207
215
  9, // sec_prec
@@ -226,6 +234,8 @@ struct _options oj_default_options = {
226
234
  false, // omit_nil
227
235
  false, // omit_null_byte
228
236
  MAX_DEPTH, // max_depth
237
+ NULL, // only
238
+ NULL, // except
229
239
  },
230
240
  {
231
241
  // str_rx
@@ -236,76 +246,109 @@ struct _options oj_default_options = {
236
246
  NULL,
237
247
  };
238
248
 
249
+ static VALUE only_array_from_string(const char *str) {
250
+ volatile VALUE a = Qnil;
251
+
252
+ if (NULL != str && 2 < strlen(str)) {
253
+ str++;
254
+ char *cp;
255
+
256
+ a = rb_ary_new();
257
+ while (NULL != (cp = strchr(str, ':'))) {
258
+ rb_ary_push(a, rb_id2sym(rb_intern2(str, cp - str)));
259
+ str = cp + 1;
260
+ }
261
+ }
262
+ return a;
263
+ }
264
+
239
265
  /* Document-method: default_options()
240
266
  * call-seq: default_options()
241
267
  *
242
268
  * Returns the default load and dump options as a Hash. The options are
243
- * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in an JSON
244
- *document, zero or nil is no newline between JSON elements, negative indicates no newline between
245
- *top level JSON elements in a stream, a String indicates the string should be used for indentation
246
- * - *:circular* [_Boolean_|_nil_] support circular references while dumping as well as shared
247
- *references
269
+ * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element
270
+ * in an JSON document, zero or nil is no newline between JSON elements,
271
+ * negative indicates no newline between top level JSON elements in a stream,
272
+ * a String indicates the string should be used for indentation
273
+ * - *:circular* [_Boolean_|_nil_] support circular references while dumping as
274
+ * well as shared references
248
275
  * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist
249
276
  * - *:symbol_keys* [_Boolean_|_nil_] use symbols instead of strings for hash keys
250
- * - *:escape_mode* [_:newline_|_:json_|_:slash_|_:xss_safe_|_:ascii_|_:unicode_xss_|_nil_] determines the
251
- *characters to escape
252
- * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing (if dynamically modifying
253
- *classes or reloading classes then don't use this)
254
- * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump modes
255
- *to use for JSON
277
+ * - *:escape_mode* [_:newline_|_:json_|_:slash_|_:xss_safe_|_:ascii_|_:unicode_xss_|_nil_]
278
+ * determines the characters to escape
279
+ * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing (if dynamically
280
+ * modifying classes or reloading classes then don't use this)
281
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and
282
+ * dump modes to use for JSON
256
283
  * - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping
257
- * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
258
- * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_:fast_|_:ruby_] load decimals as BigDecimal instead
259
- *of as a Float. :auto pick the most precise for the number of digits. :float should be the same as
260
- *ruby. :fast may require rounding but is must faster.
261
- * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float when in
262
- *compat or rails mode.
263
- * - *:create_id* [_String_|_nil_] create id for json compatible object encoding, default is
264
- *'json_class'
265
- * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on
266
- *load.
267
- * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
268
- *seconds portion of time
269
- * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
270
- *indicates use Ruby
271
- * - *:float_format* [_String_] the C printf format string for printing floats. Default follows
272
- * the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
284
+ * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or
285
+ * as a String
286
+ * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_:fast_|_:ruby_] load decimals
287
+ * as BigDecimal instead of as a Float. :auto pick the most precise for the number
288
+ * of digits. :float should be the same as ruby. :fast may require rounding but is
289
+ * must faster.
290
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as
291
+ * a Float when in compat or rails mode.
292
+ * - *:create_id* [_String_|_nil_] create id for json compatible object encoding,
293
+ * default is 'json_class'
294
+ * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using
295
+ * create_id on load.
296
+ * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when
297
+ * dumping the seconds portion of time
298
+ * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping
299
+ * floats, 0 indicates use Ruby
300
+ * - *:float_format* [_String_] the C printf format string for printing floats.
301
+ * Default follows the float_precision and will be changed if float_precision is
302
+ * changed. The string can be no more than 6 bytes.
273
303
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
274
304
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
275
305
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
276
- * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an
277
- *Exception
306
+ * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and
307
+ * not raise an Exception
278
308
  * - *:empty_string* [_Boolean_|_nil_] if true an empty input will not raise an Exception
279
309
  * - *:allow_gc* [_Boolean_|_nil_] allow or prohibit GC during parsing, default is true (allow)
280
- * - *:quirks_mode* [_true,_|_false_|_nil_] Allow single JSON values instead of documents, default
281
- *is true (allow)
282
- * - *:allow_invalid_unicode* [_true,_|_false_|_nil_] Allow invalid unicode, default is false (don't
283
- *allow)
284
- * - *:allow_nan* [_true,_|_false_|_nil_] Allow Nan, Infinity, and -Infinity to be parsed, default
285
- *is true (allow)
286
- * - *:indent_str* [_String_|_nil_] String to use for indentation, overriding the indent option is
287
- *not nil
288
- * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields
289
- * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields
310
+ * - *:quirks_mode* [_true,_|_false_|_nil_] Allow single JSON values instead of
311
+ * documents, default is true (allow)
312
+ * - *:allow_invalid_unicode* [_true,_|_false_|_nil_] Allow invalid unicode,
313
+ * default is false (don't allow)
314
+ * - *:allow_nan* [_true,_|_false_|_nil_] Allow Nan, Infinity, and -Infinity to
315
+ * be parsed, default is true (allow)
316
+ * - *:indent_str* [_String_|_nil_] String to use for indentation, overriding the
317
+ * indent option is not nil
318
+ * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON
319
+ * object fields
320
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in
321
+ * JSON object fields
290
322
  * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value
291
323
  * - *:array_nl* [_String_|_nil_] String to use after a JSON array value
292
- * - *:nan* [_:null_|_:huge_|_:word_|_:raise_|_:auto_] how to dump Infinity and NaN. :null places a
293
- *null, :huge places a huge number, :word places Infinity or NaN, :raise raises and exception, :auto
294
- *uses default for each mode.
295
- * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be
296
- *used
324
+ * - *:nan* [_:null_|_:huge_|_:word_|_:raise_|_:auto_] how to dump Infinity and
325
+ * NaN. :null places a null, :huge places a huge number, :word places Infinity
326
+ * or NaN, :raise raises and exception, :auto uses default for each mode.
327
+ * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load,
328
+ * :object_class can also be used
297
329
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
298
- * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
299
- * - *:omit_null_byte* [_true_|_false_] if true null bytes in strings will be omitted when dumping
330
+ * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil
331
+ * values are omitted
332
+ * - *:omit_null_byte* [_true_|_false_] if true null bytes in strings will be
333
+ * omitted when dumping
300
334
  * - *:ignore* [_nil_|_Array_] either nil or an Array of classes to ignore when dumping
301
- * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when dumping in
302
- *object or custom mode.
335
+ * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are
336
+ * ignored when dumping in object or custom mode.
303
337
  * - *:cache_keys* [_Boolean_] if true then hash keys are cached if less than 35 bytes.
304
- * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
338
+ * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less
339
+ * than this are cached)
305
340
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
306
- * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false (trace is off)
307
- * - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default is false (safe is
308
- *off)
341
+ * - *:max_integer_digits* [_Fixnum_] Maximum number of decimal digits allowed in a
342
+ * parsed integer. When the limit is exceeded a parse error is raised. 0 (the
343
+ * default) disables the limit. Setting a reasonable limit is recommended when
344
+ * parsing untrusted input to mitigate CPU-DoS attacks. Only applies to the
345
+ * legacy parsers (Oj.load, Oj::Doc, JSON.parse mimic); Oj::Parser is unaffected.
346
+ * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false
347
+ * (trace is off)
348
+ * - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default
349
+ * is false (safe is off)
350
+ * - *:only* [_nil,_|_Array_] A list of the fields to encode. All others are skipped.
351
+ * - *:except* [_nil,_|_Array_] A list of the fields to not encode. All others are encoded.
309
352
  *
310
353
  * Return [_Hash_] all current option settings.
311
354
  */
@@ -414,6 +457,7 @@ static VALUE get_def_opts(VALUE self) {
414
457
  } else {
415
458
  rb_hash_aset(opts, integer_range_sym, Qnil);
416
459
  }
460
+ rb_hash_aset(opts, max_integer_digits_sym, LONG2NUM((long)oj_default_options.max_integer_digits));
417
461
  switch (oj_default_options.escape_mode) {
418
462
  case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
419
463
  case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
@@ -471,6 +515,9 @@ static VALUE get_def_opts(VALUE self) {
471
515
  rb_hash_aset(opts, oj_hash_class_sym, oj_default_options.hash_class);
472
516
  rb_hash_aset(opts, oj_array_class_sym, oj_default_options.array_class);
473
517
 
518
+ rb_hash_aset(opts, only_sym, only_array_from_string(oj_default_options.dump_opts.only));
519
+ rb_hash_aset(opts, except_sym, only_array_from_string(oj_default_options.dump_opts.except));
520
+
474
521
  if (NULL == oj_default_options.ignore) {
475
522
  rb_hash_aset(opts, ignore_sym, Qnil);
476
523
  } else {
@@ -490,73 +537,72 @@ static VALUE get_def_opts(VALUE self) {
490
537
  *
491
538
  * Sets the default options for load and dump.
492
539
  * - *opts* [_Hash_] options to change
493
- * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element in a JSON
494
- *document or the String to use for indentation.
540
+ * - *:indent* [_Fixnum_|_String_|_nil_] number of spaces to indent each element
541
+ * in a JSON document or the String to use for indentation.
495
542
  * - :circular [_Boolean_|_nil_] support circular references while dumping.
496
543
  * - *:auto_define* [_Boolean_|_nil_] automatically define classes if they do not exist.
497
544
  * - *:symbol_keys* [_Boolean_|_nil_] convert hash keys to symbols.
498
545
  * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing.
499
- * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] mode encodes all
500
- *high-bit characters as escaped sequences if :ascii, :json is standand UTF-8 JSON encoding,
501
- *:newline is the same as :json but newlines are not escaped, :unicode_xss allows unicode but
502
- *escapes &, <, and >, and any \u20xx characters along with some others, and :xss_safe escapes &, <,
503
- *and >, and some others.
504
- * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a
505
- *String.
506
- * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as BigDecimal instead
507
- *of as a Float. :auto pick the most precise for the number of digits.
508
- * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float in
509
- *compat mode.
510
- * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump mode
511
- *to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat
512
- *attempts to extract variable values from an Object using to_json() or to_hash() then it walks the
513
- *Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods
514
- *and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported
515
- *Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more
516
- *mimics rails and Active behavior.
517
- * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat mode :unix
518
- *decimal number denoting the number of seconds since 1/1/1970, :unix_zone decimal number denoting
519
- *the number of seconds since 1/1/1970 plus the utc_offset in the exponent, :xmlschema date-time
520
- *format taken from XML Schema as a String, :ruby Time.to_s formatted String.
546
+ * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_]
547
+ * mode encodes all high-bit characters as escaped sequences if :ascii, :json
548
+ * is standand UTF-8 JSON encoding, :newline is the same as :json but newlines
549
+ * are not escaped, :unicode_xss allows unicode but escapes &, <, and >, and
550
+ * any \u20xx characters along with some others, and :xss_safe escapes &, <,
551
+ * and >, and some others.
552
+ * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal
553
+ * number or as a String.
554
+ * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as
555
+ * BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
556
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead
557
+ * of as a Float in compat mode.
558
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load
559
+ * and dump mode to use for JSON :strict raises an exception when a non-supported
560
+ * Object is encountered. :compat attempts to extract variable values from an
561
+ * Object using to_json() or to_hash() then it walks the Object's variables if
562
+ * neither is found. The :object mode ignores to_hash() and to_json() methods
563
+ * and encodes variables using code internal to the Oj gem. The :null mode
564
+ * ignores non-supported Objects and replaces them with a null. The :custom
565
+ * mode honors all dump options. The :rails more mimics rails and Active behavior.
566
+ * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat
567
+ * mode :unix decimal number denoting the number of seconds since 1/1/1970,
568
+ * :unix_zone decimal number denoting the number of seconds since 1/1/1970
569
+ * plus the utc_offset in the exponent, :xmlschema date-time format taken
570
+ * from XML Schema as a String, :ruby Time.to_s formatted String.
521
571
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding
522
- * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on
523
- *load.
524
- * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
525
- *seconds portion of time.
526
- * - *:float_format* [_String_] the C printf format string for printing floats. Default follows
527
- * the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
528
- * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
529
- *indicates use Ruby.
572
+ * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on load.
573
+ * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal
574
+ * when dumping the seconds portion of time.
575
+ * - *:float_format* [_String_] the C printf format string for printing floats.
576
+ * Default follows the float_precision and will be changed if float_precision
577
+ * is changed. The string can be no more than 6 bytes.
578
+ * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby.
530
579
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
531
580
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false.
532
581
  * - *:use_to_hash* [_Boolean_|_nil_] call to_hash() methods on dump, default is false.
533
582
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false.
534
- * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an
535
- *Exception.
583
+ * - *:nilnil* [_Boolean_|_nil_] if true a nil input to load will return nil and not raise an Exception.
536
584
  * - *:allow_gc* [_Boolean_|_nil_] allow or prohibit GC during parsing, default is true (allow).
537
- * - *:quirks_mode* [_Boolean_|_nil_] allow single JSON values instead of documents, default is
538
- *true (allow).
539
- * - *:allow_invalid_unicode* [_Boolean_|_nil_] allow invalid unicode, default is false (don't
540
- *allow).
585
+ * - *:quirks_mode* [_Boolean_|_nil_] allow single JSON values instead of documents, default is true (allow).
586
+ * - *:allow_invalid_unicode* [_Boolean_|_nil_] allow invalid unicode, default is false (don't allow).
541
587
  * - *:allow_nan* [_Boolean_|_nil_] allow Nan, Infinity, and -Infinity, default is true (allow).
542
588
  * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields.
543
- * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object
544
- *fields.
589
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields.
545
590
  * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value.
546
591
  * - *:array_nl* [_String_|_nil_] String to use after a JSON array value
547
- * - *:nan* [_:null_|_:huge_|_:word_|_:raise_] how to dump Infinity and NaN in null, strict, and
548
- *compat mode. :null places a null, :huge places a huge number, :word places Infinity or NaN, :raise
549
- *raises and exception, :auto uses default for each mode.
550
- * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be
551
- *used.
592
+ * - *:nan* [_:null_|_:huge_|_:word_|_:raise_] how to dump Infinity and NaN in null,
593
+ * strict, and compat mode. :null places a null, :huge places a huge number, :word
594
+ * places Infinity or NaN, :raise raises and exception, :auto uses default for each mode.
595
+ * - *:hash_class* [_Class_|_nil_] Class to use instead of Hash on load, :object_class can also be used.
552
596
  * - *:array_class* [_Class_|_nil_] Class to use instead of Array on load.
553
597
  * - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted.
554
598
  * - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
555
- * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when
556
- *dumping in object or custom mode.
599
+ * - *:ignore_under* [_Boolean_] if true then attributes that start with _ are
600
+ * ignored when dumping in object or custom mode.
557
601
  * - *:cache_keys* [_Boolean_] if true then hash keys are cached
558
602
  * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
559
603
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
604
+ * - *:max_integer_digits* [_Fixnum_] Maximum decimal digits in a parsed integer
605
+ * (0 = unlimited). Use to mitigate CPU-DoS via huge integer values in JSON.
560
606
  * - *:trace* [_Boolean_] turn trace on or off.
561
607
  * - *:safe* [_Boolean_] turn safe mimic on or off.
562
608
  */
@@ -616,6 +662,95 @@ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
616
662
  return false;
617
663
  }
618
664
 
665
+ static const char *make_only_value(VALUE v) {
666
+ switch (rb_type(v)) {
667
+ case RUBY_T_NIL:
668
+ case RUBY_T_NONE: return NULL;
669
+ case RUBY_T_ARRAY: {
670
+ long len = rb_array_len(v);
671
+ long i;
672
+ long size = 0;
673
+ char *buf;
674
+ char *bp;
675
+
676
+ for (i = 0; i < len; i++) {
677
+ VALUE x = rb_ary_entry(v, i);
678
+
679
+ switch (rb_type(x)) {
680
+ case RUBY_T_SYMBOL:
681
+ size += strlen(rb_id2name(rb_sym2id(x)));
682
+ size++;
683
+ break;
684
+ case RUBY_T_STRING:
685
+ size += strlen(StringValueCStr(x));
686
+ size++;
687
+ break;
688
+ default: rb_raise(rb_eArgError, ":only and :except must be nil, symbol, string, or array."); break;
689
+ }
690
+ }
691
+ if (0 == size) {
692
+ return NULL;
693
+ }
694
+ buf = OJ_R_ALLOC_N(char, size + 2);
695
+ bp = buf;
696
+ *bp++ = ':';
697
+ for (i = 0; i < len; i++) {
698
+ VALUE x = rb_ary_entry(v, i);
699
+ const char *str;
700
+
701
+ switch (rb_type(x)) {
702
+ case RUBY_T_SYMBOL:
703
+ str = rb_id2name(rb_sym2id(x));
704
+ size = strlen(str);
705
+ memcpy(bp, str, size);
706
+ bp += size;
707
+ *bp++ = ':';
708
+ break;
709
+ case RUBY_T_STRING:
710
+ str = StringValueCStr(x);
711
+ size = strlen(str);
712
+ memcpy(bp, str, size);
713
+ bp += size;
714
+ *bp++ = ':';
715
+ break;
716
+ default:
717
+ // ignore
718
+ break;
719
+ }
720
+ }
721
+ *bp = '\0';
722
+
723
+ return buf;
724
+ }
725
+ case RUBY_T_STRING: {
726
+ const char *str = StringValueCStr(v);
727
+ size_t size = strlen(str);
728
+ char *buf = OJ_R_ALLOC_N(char, size + 3);
729
+
730
+ buf[0] = ':';
731
+ strcpy(buf + 1, str);
732
+ buf[size + 1] = ':';
733
+ buf[size + 2] = '\0';
734
+
735
+ return buf;
736
+ }
737
+ case RUBY_T_SYMBOL: {
738
+ const char *str = rb_id2name(rb_sym2id(v));
739
+ size_t size = strlen(str);
740
+ char *buf = OJ_R_ALLOC_N(char, size + 3);
741
+
742
+ buf[0] = ':';
743
+ strcpy(buf + 1, str);
744
+ buf[size + 1] = ':';
745
+ buf[size + 2] = '\0';
746
+
747
+ return buf;
748
+ }
749
+ default: rb_raise(rb_eArgError, ":only and zzz :except must be nil, symbol, string, or array."); break;
750
+ }
751
+ return NULL;
752
+ }
753
+
619
754
  static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
620
755
  Options copts = (Options)opts;
621
756
  size_t len;
@@ -633,7 +768,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
633
768
  case T_FIXNUM:
634
769
  copts->dump_opts.indent_size = 0;
635
770
  *copts->dump_opts.indent_str = '\0';
636
- copts->indent = FIX2INT(v);
771
+ if (MAX_INDENT < FIX2INT(v)) {
772
+ rb_raise(rb_eArgError, "indent is limited to %d characters.", MAX_INDENT);
773
+ }
774
+ copts->indent = FIX2INT(v);
637
775
  break;
638
776
  case T_STRING:
639
777
  if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
@@ -917,12 +1055,12 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
917
1055
  OJ_R_FREE(copts->ignore);
918
1056
  copts->ignore = NULL;
919
1057
  if (Qnil != v) {
920
- int cnt;
1058
+ size_t cnt;
921
1059
 
922
1060
  rb_check_type(v, T_ARRAY);
923
- cnt = (int)RARRAY_LEN(v);
1061
+ cnt = RARRAY_LEN(v);
924
1062
  if (0 < cnt) {
925
- int i;
1063
+ size_t i;
926
1064
 
927
1065
  copts->ignore = OJ_R_ALLOC_N(VALUE, cnt + 1);
928
1066
  for (i = 0; i < cnt; i++) {
@@ -948,6 +1086,20 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
948
1086
  } else if (Qfalse != v) {
949
1087
  rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
950
1088
  }
1089
+ } else if (max_integer_digits_sym == k) {
1090
+ if (Qnil == v || Qfalse == v) {
1091
+ copts->max_integer_digits = 0;
1092
+ } else if (T_FIXNUM == rb_type(v)) {
1093
+ long n = FIX2LONG(v);
1094
+
1095
+ if (n < 0) {
1096
+ rb_raise(rb_eArgError, ":max_integer_digits must be >= 0.");
1097
+ }
1098
+
1099
+ copts->max_integer_digits = (size_t)n;
1100
+ } else {
1101
+ rb_raise(rb_eArgError, ":max_integer_digits must be a non-negative Integer.");
1102
+ }
951
1103
  } else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
952
1104
  if (Qnil == v) {
953
1105
  return ST_CONTINUE;
@@ -967,11 +1119,23 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
967
1119
  }
968
1120
  } else if (float_format_sym == k) {
969
1121
  rb_check_type(v, T_STRING);
970
- if (6 < (int)RSTRING_LEN(v)) {
1122
+ if (6 < RSTRING_LEN(v)) {
971
1123
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
972
1124
  }
973
1125
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
974
1126
  copts->float_fmt[RSTRING_LEN(v)] = '\0';
1127
+ } else if (only_sym == k) {
1128
+ if (NULL != copts->dump_opts.only) {
1129
+ OJ_R_FREE((void *)copts->dump_opts.only);
1130
+ copts->dump_opts.only = NULL;
1131
+ }
1132
+ copts->dump_opts.only = make_only_value(v);
1133
+ } else if (except_sym == k) {
1134
+ if (NULL != copts->dump_opts.except) {
1135
+ OJ_R_FREE((void *)copts->dump_opts.except);
1136
+ copts->dump_opts.except = NULL;
1137
+ }
1138
+ copts->dump_opts.except = make_only_value(v);
975
1139
  }
976
1140
  return ST_CONTINUE;
977
1141
  }
@@ -1182,7 +1346,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1182
1346
  OJ_FREE(wide_path);
1183
1347
  }
1184
1348
  #else
1185
- fd = open(path, O_RDONLY);
1349
+ fd = open(path, O_RDONLY);
1186
1350
  #endif
1187
1351
  if (0 == fd) {
1188
1352
  rb_raise(rb_eIOError, "%s", strerror(errno));
@@ -1271,8 +1435,7 @@ static VALUE dump_body(VALUE a) {
1271
1435
  if (0 == arg->out->buf) {
1272
1436
  rb_raise(rb_eNoMemError, "Not enough memory.");
1273
1437
  }
1274
- rstr = rb_str_new2(arg->out->buf);
1275
- rstr = oj_encode(rstr);
1438
+ rstr = rb_utf8_str_new_cstr(arg->out->buf);
1276
1439
 
1277
1440
  return rstr;
1278
1441
  }
@@ -1329,18 +1492,16 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1329
1492
  * will be called. The mode is set to :compat.
1330
1493
  * - *obj* [_Object_] Object to serialize as an JSON document String
1331
1494
  * - *options* [_Hash_]
1332
- * - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100. If a Fixnum nesting
1333
- * is set to the provided value. The option to detect circular references is available but is not
1334
- * compatible with the json gem., default is false or unlimited.
1335
- * - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be
1336
- * used as appropriate, default is true.
1337
- * - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true
1338
- * (allow).
1339
- * - *:indent* [_String_|_nil_] String to use for indentation, overriding the indent option if not
1340
- * nil.
1495
+ * - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100.
1496
+ * If a Fixnum nesting is set to the provided value. The option to detect
1497
+ * circular references is available but is not compatible with the json gem.,
1498
+ * default is false or unlimited.
1499
+ * - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and
1500
+ * Infinity will be used as appropriate, default is true.
1501
+ * - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true (allow).
1502
+ * - *:indent* [_String_|_nil_] String to use for indentation, overriding the indent option if not nil.
1341
1503
  * - *:space* [_String_|_nil_] String to use for the space after the colon in JSON object fields.
1342
- * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object
1343
- * fields.
1504
+ * - *:space_before* [_String_|_nil_] String to use before the colon separator in JSON object fields.
1344
1505
  * - *:object_nl* [_String_|_nil_] String to use after a JSON object field value.
1345
1506
  * - *:array_nl* [_String_|_nil_] String to use after a JSON array value.
1346
1507
  * - *:trace* [_Boolean_] If true trace is turned on.
@@ -1374,8 +1535,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1374
1535
  if (0 == out.buf) {
1375
1536
  rb_raise(rb_eNoMemError, "Not enough memory.");
1376
1537
  }
1377
- rstr = rb_str_new2(out.buf);
1378
- rstr = oj_encode(rstr);
1538
+ rstr = rb_utf8_str_new_cstr(out.buf);
1379
1539
 
1380
1540
  oj_out_free(&out);
1381
1541
 
@@ -1435,10 +1595,10 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
1435
1595
  *
1436
1596
  * - *clas* [_Class__|_Module_] Class or Module to be made special
1437
1597
  * - *create_object* [_Object_] object to call the create method on
1438
- * - *create_method* [_Symbol_] method on the clas that will create a new instance of the clas when
1439
- * given all the member values in the order specified.
1440
- * - *members* [_Symbol__|_String_] methods used to get the member values from instances of the
1441
- * clas.
1598
+ * - *create_method* [_Symbol_] method on the clas that will create a new instance
1599
+ * of the clas when given all the member values in the order specified.
1600
+ * - *members* [_Symbol__|_String_] methods used to get the member values from
1601
+ * instances of the clas.
1442
1602
  */
1443
1603
  static VALUE register_odd(int argc, VALUE *argv, VALUE self) {
1444
1604
  if (3 > argc) {
@@ -1471,10 +1631,10 @@ static VALUE register_odd(int argc, VALUE *argv, VALUE self) {
1471
1631
  *
1472
1632
  * - *clas* [_Class_|_Module_] Class or Module to be made special
1473
1633
  * - *create_object* [_Object_] object to call the create method on
1474
- * - *create_method* [_Symbol_] method on the clas that will create a new instance of the clas when
1475
- *given all the member values in the order specified.
1476
- * - *dump_method* [_Symbol_|_String_] method to call on the object being serialized to generate the
1477
- *raw JSON.
1634
+ * - *create_method* [_Symbol_] method on the clas that will create a new instance
1635
+ * of the clas when given all the member values in the order specified.
1636
+ * - *dump_method* [_Symbol_|_String_] method to call on the object being
1637
+ * serialized to generate the raw JSON.
1478
1638
  */
1479
1639
  static VALUE register_odd_raw(int argc, VALUE *argv, VALUE self) {
1480
1640
  if (3 > argc) {
@@ -1700,8 +1860,8 @@ extern VALUE oj_define_mimic_json(int argc, VALUE *argv, VALUE self);
1700
1860
  * - *:space_before* [_String_] String placed before a : delimiter
1701
1861
  * - *:object_nl* [_String_] String placed after a JSON object
1702
1862
  * - *:array_nl* [_String_] String placed after a JSON array
1703
- * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output.
1704
- * Note JSON.generate does support this even if it is not documented.
1863
+ * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters
1864
+ * in the output. Note JSON.generate does support this even if it is not documented.
1705
1865
  *
1706
1866
  * Returns [_String_]generated JSON.
1707
1867
  */
@@ -1758,8 +1918,8 @@ static VALUE mem_report(VALUE self) {
1758
1918
  * global and options to methods allow additional behavior modifications. The
1759
1919
  * modes are:
1760
1920
  *
1761
- * - *:strict* mode will only allow the 7 basic JSON types to be serialized. Any other Object
1762
- * will raise an Exception.
1921
+ * - *:strict* mode will only allow the 7 basic JSON types to be serialized.
1922
+ * Any other Object will raise an Exception.
1763
1923
  *
1764
1924
  * - *:null* mode is similar to the :strict mode except any Object that is not
1765
1925
  * one of the JSON base types is replaced by a JSON null.
@@ -1777,6 +1937,78 @@ static VALUE mem_report(VALUE self) {
1777
1937
  *
1778
1938
  * - *:wab* specifically for WAB data exchange.
1779
1939
  */
1940
+
1941
+ // =============================================================================
1942
+ // Runtime SIMD CPU detection
1943
+ // Cross-platform support for Windows (MSVC), Linux, and macOS (GCC/Clang)
1944
+ // =============================================================================
1945
+ SIMD_Implementation oj_get_simd_implementation(void) {
1946
+ #ifdef HAVE_SIMD_X86
1947
+ // x86/x86_64 runtime detection
1948
+
1949
+ #if defined(_MSC_VER)
1950
+ // MSVC: Use __cpuid intrinsic
1951
+ int cpu_info[4];
1952
+ __cpuid(cpu_info, 1);
1953
+
1954
+ // Check for SSE4.2 (bit 20 of ECX)
1955
+ if (cpu_info[2] & (1 << 20)) {
1956
+ return SIMD_SSE42;
1957
+ }
1958
+ // Check for SSE2 (bit 26 of EDX)
1959
+ if (cpu_info[3] & (1 << 26)) {
1960
+ return SIMD_SSE2;
1961
+ }
1962
+
1963
+ #elif defined(__GNUC__) || defined(__clang__)
1964
+ // GCC/Clang: Use __builtin_cpu_supports if available
1965
+ #if defined(__has_builtin)
1966
+ #if __has_builtin(__builtin_cpu_supports)
1967
+ #define OJ_HAS_BUILTIN_CPU_SUPPORTS 1
1968
+ #endif
1969
+ #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
1970
+ // GCC 4.8+ has __builtin_cpu_supports
1971
+ #define OJ_HAS_BUILTIN_CPU_SUPPORTS 1
1972
+ #endif
1973
+
1974
+ #ifdef OJ_HAS_BUILTIN_CPU_SUPPORTS
1975
+ #ifdef HAVE_SIMD_SSE4_2
1976
+ if (__builtin_cpu_supports("sse4.2")) {
1977
+ return SIMD_SSE42;
1978
+ }
1979
+ #endif
1980
+ #ifdef HAVE_SIMD_SSE2
1981
+ if (__builtin_cpu_supports("sse2")) {
1982
+ return SIMD_SSE2;
1983
+ }
1984
+ #endif
1985
+ #else
1986
+ // Fallback: Use CPUID instruction directly
1987
+ unsigned int eax, ebx, ecx, edx;
1988
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
1989
+ // Check for SSE4.2 (bit 20 of ECX)
1990
+ if (ecx & (1 << 20)) {
1991
+ return SIMD_SSE42;
1992
+ }
1993
+ // Check for SSE2 (bit 26 of EDX)
1994
+ if (edx & (1 << 26)) {
1995
+ return SIMD_SSE2;
1996
+ }
1997
+ }
1998
+ #endif // OJ_HAS_BUILTIN_CPU_SUPPORTS
1999
+
2000
+ #endif // _MSC_VER vs GCC/Clang
2001
+
2002
+ #endif // HAVE_SIMD_X86
2003
+
2004
+ #ifdef HAVE_SIMD_NEON
2005
+ // ARM NEON is always available on ARM64 and detected at compile time
2006
+ return SIMD_NEON;
2007
+ #endif
2008
+
2009
+ return SIMD_NONE;
2010
+ }
2011
+
1780
2012
  void Init_oj(void) {
1781
2013
  int err = 0;
1782
2014
 
@@ -1843,10 +2075,10 @@ void Init_oj(void) {
1843
2075
  oj_array_end_id = rb_intern("array_end");
1844
2076
  oj_array_start_id = rb_intern("array_start");
1845
2077
  oj_as_json_id = rb_intern("as_json");
1846
- oj_at_id = rb_intern("at");
1847
2078
  oj_begin_id = rb_intern("begin");
1848
2079
  oj_bigdecimal_id = rb_intern("BigDecimal");
1849
2080
  oj_end_id = rb_intern("end");
2081
+ oj_eofq_id = rb_intern("eof?");
1850
2082
  oj_error_id = rb_intern("error");
1851
2083
  oj_exclude_end_id = rb_intern("exclude_end?");
1852
2084
  oj_file_id = rb_intern("file?");
@@ -1861,6 +2093,7 @@ void Init_oj(void) {
1861
2093
  oj_length_id = rb_intern("length");
1862
2094
  oj_new_id = rb_intern("new");
1863
2095
  oj_parse_id = rb_intern("parse");
2096
+ oj_plus_id = rb_intern("+");
1864
2097
  oj_pos_id = rb_intern("pos");
1865
2098
  oj_raw_json_id = rb_intern("raw_json");
1866
2099
  oj_read_id = rb_intern("read");
@@ -1949,6 +2182,8 @@ void Init_oj(void) {
1949
2182
  rb_gc_register_address(&escape_mode_sym);
1950
2183
  integer_range_sym = ID2SYM(rb_intern("integer_range"));
1951
2184
  rb_gc_register_address(&integer_range_sym);
2185
+ max_integer_digits_sym = ID2SYM(rb_intern("max_integer_digits"));
2186
+ rb_gc_register_address(&max_integer_digits_sym);
1952
2187
  fast_sym = ID2SYM(rb_intern("fast"));
1953
2188
  rb_gc_register_address(&fast_sym);
1954
2189
  float_format_sym = ID2SYM(rb_intern("float_format"));
@@ -1993,14 +2228,10 @@ void Init_oj(void) {
1993
2228
  rb_gc_register_address(&oj_decimal_class_sym);
1994
2229
  oj_hash_class_sym = ID2SYM(rb_intern("hash_class"));
1995
2230
  rb_gc_register_address(&oj_hash_class_sym);
1996
- oj_in_sym = ID2SYM(rb_intern("in"));
1997
- rb_gc_register_address(&oj_in_sym);
1998
2231
  oj_indent_sym = ID2SYM(rb_intern("indent"));
1999
2232
  rb_gc_register_address(&oj_indent_sym);
2000
2233
  oj_max_nesting_sym = ID2SYM(rb_intern("max_nesting"));
2001
2234
  rb_gc_register_address(&oj_max_nesting_sym);
2002
- oj_nanosecond_sym = ID2SYM(rb_intern("nanosecond"));
2003
- rb_gc_register_address(&oj_nanosecond_sym);
2004
2235
  oj_object_class_sym = ID2SYM(rb_intern("object_class"));
2005
2236
  rb_gc_register_address(&oj_object_class_sym);
2006
2237
  oj_object_nl_sym = ID2SYM(rb_intern("object_nl"));
@@ -2059,6 +2290,10 @@ void Init_oj(void) {
2059
2290
  rb_gc_register_address(&xmlschema_sym);
2060
2291
  xss_safe_sym = ID2SYM(rb_intern("xss_safe"));
2061
2292
  rb_gc_register_address(&xss_safe_sym);
2293
+ only_sym = ID2SYM(rb_intern("only"));
2294
+ rb_gc_register_address(&only_sym);
2295
+ except_sym = ID2SYM(rb_intern("except"));
2296
+ rb_gc_register_address(&except_sym);
2062
2297
 
2063
2298
  oj_slash_string = rb_str_new2("/");
2064
2299
  rb_gc_register_address(&oj_slash_string);
@@ -2080,6 +2315,18 @@ void Init_oj(void) {
2080
2315
  #endif
2081
2316
  oj_init_doc();
2082
2317
 
2318
+ SIMD_Impl = oj_get_simd_implementation();
2319
+
2083
2320
  oj_parser_init();
2084
2321
  oj_scanner_init();
2322
+
2323
+ #ifdef HAVE_SIMD_NEON
2324
+ initialize_neon();
2325
+ #endif /* HAVE_SIMD_NEON */
2326
+
2327
+ #ifdef HAVE_SIMD_SSE4_2
2328
+ if (SIMD_Impl == SIMD_SSE42) {
2329
+ initialize_sse42();
2330
+ }
2331
+ #endif /* HAVE_SIMD_SSE4_2 */
2085
2332
  }