oj 3.16.10 → 3.16.11

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1095d7a176bd19396bd99cf23df33564dbdf983b36a74254085700a12ce4293
4
- data.tar.gz: ef1b5958d6edf181843066dc4b60a93e861779cfa387b88d0f866d642c3e9a6c
3
+ metadata.gz: 30aea721380a4e3edc306dd19906d8777f230a639ba3427e9394dd543a3a7e3b
4
+ data.tar.gz: b024a9d4513c16c1bfe4fc3c4adeacb1afd9a0e670987476d32cfa2fa74e9b1e
5
5
  SHA512:
6
- metadata.gz: c396a33f0a2e64282f13a1b834c8804429f97de515dffcf1e8cba852fbe645301c9f6b0e7d64552a795c70e0262e8828f650a1ac9243391af8c07a2ecd621f35
7
- data.tar.gz: 43ddbc0d8f2d5bea7580b0f88fa2c4981a661098f3e041aac6f8185029e2018f99ef33478ebbf62b35c31357f1d48adb25f6889b6049054add214a834e8e696a
6
+ metadata.gz: 527ea1162cb135bbe16eefc10a7cb05444182767aca6fa0b6986622e52d7082bcec020c43e663251406c81602018f7d0842c2c5cee37aeca0269560e502d99dd
7
+ data.tar.gz: e49e9f63e373cb0ec21f604f97899f87815b86ef5a5eafad30e7bddbd11e71156f92beaa1259c83609c2d45d2a8aac87c8b27e3e266fcc2bd99a1908327c796d
data/CHANGELOG.md CHANGED
@@ -1,9 +1,15 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.16.11 - 2025-05-29
4
+
5
+ - Fixed range encoding with the :circular option
6
+
3
7
  ## 3.16.10 - 2025-02-24
4
8
 
5
9
  - Changed oj_parser_type to be non-static.
6
10
 
11
+ - Changed ARM versions to used Neon instructions thanks to @samyron.
12
+
7
13
  ## 3.16.9 - 2024-12-28
8
14
 
9
15
  - Fixed `Oj::Parser` create_id size issue #931.
data/ext/oj/custom.c CHANGED
@@ -40,7 +40,7 @@ static void dump_obj_as_str(VALUE obj, int depth, Out out) {
40
40
  static void bigdecimal_dump(VALUE obj, int depth, Out out) {
41
41
  volatile VALUE rstr = oj_safe_string_convert(obj);
42
42
  const char *str = RSTRING_PTR(rstr);
43
- int len = (int)RSTRING_LEN(rstr);
43
+ size_t len = RSTRING_LEN(rstr);
44
44
 
45
45
  if (0 == strcasecmp("Infinity", str)) {
46
46
  str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
@@ -123,7 +123,7 @@ static void date_dump(VALUE obj, int depth, Out out) {
123
123
  case RubyTime:
124
124
  case XmlTime:
125
125
  v = rb_funcall(obj, rb_intern("iso8601"), 0);
126
- oj_dump_cstr(RSTRING_PTR(v), (int)RSTRING_LEN(v), 0, 0, out);
126
+ oj_dump_cstr(RSTRING_PTR(v), RSTRING_LEN(v), 0, 0, out);
127
127
  break;
128
128
  case UnixZTime:
129
129
  v = rb_funcall(obj, rb_intern("to_time"), 0);
@@ -405,7 +405,7 @@ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
405
405
  rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
406
406
  } else {
407
407
  const char *s = RSTRING_PTR(v);
408
- int len = (int)RSTRING_LEN(v);
408
+ size_t len = RSTRING_LEN(v);
409
409
  const char *name = rb_id2name(*odd->attrs);
410
410
  size_t nlen = strlen(name);
411
411
 
@@ -478,7 +478,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
478
478
  } else if (Yes == out->opts->to_json && rb_respond_to(obj, oj_to_json_id)) {
479
479
  volatile VALUE rs;
480
480
  const char *s;
481
- int len;
481
+ size_t len;
482
482
 
483
483
  TRACE(out->opts->trace, "to_json", obj, depth + 1, TraceRubyIn);
484
484
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
@@ -488,7 +488,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
488
488
  }
489
489
  TRACE(out->opts->trace, "to_json", obj, depth + 1, TraceRubyOut);
490
490
  s = RSTRING_PTR(rs);
491
- len = (int)RSTRING_LEN(rs);
491
+ len = RSTRING_LEN(rs);
492
492
 
493
493
  assure_size(out, len + 1);
494
494
  APPEND_CHARS(out->cur, s, len);
@@ -509,7 +509,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
509
509
  if (aj == obj) {
510
510
  volatile VALUE rstr = oj_safe_string_convert(obj);
511
511
 
512
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), false, false, out);
512
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), false, false, out);
513
513
  } else {
514
514
  oj_dump_custom_val(aj, depth, out, true);
515
515
  }
@@ -676,7 +676,8 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
676
676
 
677
677
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
678
678
  size_t size;
679
- int i, cnt;
679
+ size_t i;
680
+ size_t cnt;
680
681
  int d2 = depth + 1;
681
682
  long id = oj_check_circular(a, out);
682
683
 
@@ -684,7 +685,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
684
685
  oj_dump_nil(Qnil, depth, out, false);
685
686
  return;
686
687
  }
687
- cnt = (int)RARRAY_LEN(a);
688
+ cnt = RARRAY_LEN(a);
688
689
  *out->cur++ = '[';
689
690
  assure_size(out, 2);
690
691
  if (0 == cnt) {
@@ -795,7 +796,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
795
796
  volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
796
797
 
797
798
  name = RSTRING_PTR(s);
798
- len = (int)RSTRING_LEN(s);
799
+ len = RSTRING_LEN(s);
799
800
  } else {
800
801
  len = snprintf(num_id, sizeof(num_id), "%d", i);
801
802
  name = num_id;
data/ext/oj/dump.c CHANGED
@@ -152,8 +152,77 @@ inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
152
152
  return calculate_string_size(str, len, newline_friendly_chars);
153
153
  }
154
154
 
155
+ #ifdef HAVE_SIMD_NEON
156
+ inline static uint8x16x4_t load_uint8x16_4(const unsigned char *table) {
157
+ uint8x16x4_t tab;
158
+ tab.val[0] = vld1q_u8(table);
159
+ tab.val[1] = vld1q_u8(table + 16);
160
+ tab.val[2] = vld1q_u8(table + 32);
161
+ tab.val[3] = vld1q_u8(table + 48);
162
+ return tab;
163
+ }
164
+
165
+ static uint8x16x4_t hibit_friendly_chars_neon[2];
166
+ static uint8x16x4_t rails_friendly_chars_neon[2];
167
+ static uint8x16x4_t rails_xss_friendly_chars_neon[4];
168
+
169
+ void initialize_neon(void) {
170
+ // We only need the first 128 bytes of the hibit friendly chars table. Everything above 127 is
171
+ // set to 1. If that ever changes, the code will need to be updated.
172
+ hibit_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)hibit_friendly_chars);
173
+ hibit_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)hibit_friendly_chars + 64);
174
+
175
+ // rails_friendly_chars is the same as hibit_friendly_chars. Only the first 128 bytes have values
176
+ // that are not '1'. If that ever changes, the code will need to be updated.
177
+ rails_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)rails_friendly_chars);
178
+ rails_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)rails_friendly_chars + 64);
179
+
180
+ rails_xss_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars);
181
+ rails_xss_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 64);
182
+ rails_xss_friendly_chars_neon[2] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 128);
183
+ rails_xss_friendly_chars_neon[3] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 192);
184
+
185
+ // All bytes should be 0 except for those that need more than 1 byte of output. This will allow the
186
+ // code to limit the lookups to the first 128 bytes (values 0 - 127). Bytes above 127 will result
187
+ // in 0 with the vqtbl4q_u8 instruction.
188
+ uint8x16_t one = vdupq_n_u8('1');
189
+ for (int i = 0; i < 2; i++) {
190
+ for (int j = 0; j < 4; j++) {
191
+ hibit_friendly_chars_neon[i].val[j] = vsubq_u8(hibit_friendly_chars_neon[i].val[j], one);
192
+ rails_friendly_chars_neon[i].val[j] = vsubq_u8(rails_friendly_chars_neon[i].val[j], one);
193
+ }
194
+ }
195
+
196
+ for (int i = 0; i < 4; i++) {
197
+ for (int j = 0; j < 4; j++) {
198
+ rails_xss_friendly_chars_neon[i].val[j] = vsubq_u8(rails_xss_friendly_chars_neon[i].val[j], one);
199
+ }
200
+ }
201
+ }
202
+ #endif
203
+
155
204
  inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
205
+ #ifdef HAVE_SIMD_NEON
206
+ size_t size = 0;
207
+ size_t i = 0;
208
+
209
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
210
+ size += sizeof(uint8x16_t);
211
+
212
+ // See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
213
+ uint8x16_t chunk = vld1q_u8(str);
214
+ uint8x16_t tmp1 = vqtbl4q_u8(hibit_friendly_chars_neon[0], chunk);
215
+ uint8x16_t tmp2 = vqtbl4q_u8(hibit_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
216
+ uint8x16_t result = vorrq_u8(tmp1, tmp2);
217
+ uint8_t tmp = vaddvq_u8(result);
218
+ size += tmp;
219
+ }
220
+
221
+ size_t total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
222
+ return total;
223
+ #else
156
224
  return calculate_string_size(str, len, hibit_friendly_chars);
225
+ #endif
157
226
  }
158
227
 
159
228
  inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
@@ -184,9 +253,43 @@ inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
184
253
 
185
254
  inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
186
255
  long size = 0;
187
- size_t i = len;
188
256
  uint8_t hi = 0;
189
257
 
258
+ #ifdef HAVE_SIMD_NEON
259
+ size_t i = 0;
260
+
261
+ uint8x16_t has_some_hibit = vdupq_n_u8(0);
262
+ uint8x16_t hibit = vdupq_n_u8(0x80);
263
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
264
+ size += sizeof(uint8x16_t);
265
+
266
+ uint8x16_t chunk = vld1q_u8(str);
267
+
268
+ // Check to see if any of these bytes have the high bit set.
269
+ has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
270
+
271
+ uint8x16_t tmp1 = vqtbl4q_u8(rails_xss_friendly_chars_neon[0], chunk);
272
+ uint8x16_t tmp2 = vqtbl4q_u8(rails_xss_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
273
+ uint8x16_t tmp3 = vqtbl4q_u8(rails_xss_friendly_chars_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
274
+ uint8x16_t tmp4 = vqtbl4q_u8(rails_xss_friendly_chars_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
275
+ uint8x16_t result = vorrq_u8(tmp4, vorrq_u8(tmp3, vorrq_u8(tmp1, tmp2)));
276
+ uint8_t tmp = vaddvq_u8(result);
277
+ size += tmp;
278
+ }
279
+
280
+ // 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
281
+ hi = vmaxvq_u8(has_some_hibit) != 0;
282
+
283
+ for (; i < len; str++, i++) {
284
+ size += rails_xss_friendly_chars[*str] - '0';
285
+ hi |= *str & 0x80;
286
+ }
287
+ if (0 == hi) {
288
+ return size;
289
+ }
290
+ return -(size);
291
+ #else
292
+ size_t i = len;
190
293
  for (; 0 < i; str++, i--) {
191
294
  size += rails_xss_friendly_chars[*str];
192
295
  hi |= *str & 0x80;
@@ -195,13 +298,47 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
195
298
  return size - len * (size_t)'0';
196
299
  }
197
300
  return -(size - len * (size_t)'0');
301
+ #endif /* HAVE_SIMD_NEON */
198
302
  }
199
303
 
200
304
  inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
201
305
  long size = 0;
202
- size_t i = len;
203
306
  uint8_t hi = 0;
307
+ #ifdef HAVE_SIMD_NEON
308
+ size_t i = 0;
309
+
310
+ uint8x16_t has_some_hibit = vdupq_n_u8(0);
311
+ uint8x16_t hibit = vdupq_n_u8(0x80);
312
+
313
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
314
+ size += sizeof(uint8x16_t);
315
+
316
+ // See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
317
+ uint8x16_t chunk = vld1q_u8(str);
318
+
319
+ // Check to see if any of these bytes have the high bit set.
320
+ has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
204
321
 
322
+ uint8x16_t tmp1 = vqtbl4q_u8(rails_friendly_chars_neon[0], chunk);
323
+ uint8x16_t tmp2 = vqtbl4q_u8(rails_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
324
+ uint8x16_t result = vorrq_u8(tmp1, tmp2);
325
+ uint8_t tmp = vaddvq_u8(result);
326
+ size += tmp;
327
+ }
328
+
329
+ // 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
330
+ hi = vmaxvq_u8(has_some_hibit) != 0;
331
+
332
+ for (; i < len; str++, i++) {
333
+ size += rails_friendly_chars[*str] - '0';
334
+ hi |= *str & 0x80;
335
+ }
336
+ if (0 == hi) {
337
+ return size;
338
+ }
339
+ return -(size);
340
+ #else
341
+ size_t i = len;
205
342
  for (; 0 < i; str++, i--) {
206
343
  size += rails_friendly_chars[*str];
207
344
  hi |= *str & 0x80;
@@ -210,9 +347,10 @@ inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
210
347
  return size - len * (size_t)'0';
211
348
  }
212
349
  return -(size - len * (size_t)'0');
350
+ #endif /* HAVE_SIMD_NEON */
213
351
  }
214
352
 
215
- const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
353
+ const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, size_t *lenp) {
216
354
  const char *str = NULL;
217
355
 
218
356
  if (AutoNan == opt) {
@@ -477,7 +615,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
477
615
  void oj_dump_ruby_time(VALUE obj, Out out) {
478
616
  volatile VALUE rstr = oj_safe_string_convert(obj);
479
617
 
480
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
618
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
481
619
  }
482
620
 
483
621
  void oj_dump_xml_time(VALUE obj, Out out) {
@@ -711,13 +849,13 @@ void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
711
849
  rb_encoding *enc = rb_enc_from_index(idx);
712
850
  obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
713
851
  }
714
- oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
852
+ oj_dump_cstr(RSTRING_PTR(obj), RSTRING_LEN(obj), 0, 0, out);
715
853
  }
716
854
 
717
855
  void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
718
856
  volatile VALUE s = rb_sym2str(obj);
719
857
 
720
- oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
858
+ oj_dump_cstr(RSTRING_PTR(s), RSTRING_LEN(s), 0, 0, out);
721
859
  }
722
860
 
723
861
  static void debug_raise(const char *orig, size_t cnt, int line) {
@@ -758,9 +896,49 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
758
896
  }
759
897
  }
760
898
 
899
+ #ifdef HAVE_SIMD_NEON
900
+ typedef struct _neon_match_result {
901
+ uint8x16_t needs_escape;
902
+ bool has_some_hibit;
903
+ bool do_unicode_validation;
904
+ } neon_match_result;
905
+
906
+ #if defined(__clang__) || defined(__GNUC__)
907
+ #define FORCE_INLINE __attribute__((always_inline))
908
+ #else
909
+ #define FORCE_INLINE
910
+ #endif
911
+
912
+ static inline FORCE_INLINE neon_match_result
913
+ neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool do_unicode_validation, bool has_hi) {
914
+ neon_match_result result = {.has_some_hibit = false, .do_unicode_validation = false};
915
+
916
+ uint8x16_t chunk = vld1q_u8((const unsigned char *)str);
917
+ uint8x16_t tmp1 = vqtbl4q_u8(cmap_neon[0], chunk);
918
+ uint8x16_t tmp2 = vqtbl4q_u8(cmap_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
919
+ result.needs_escape = vorrq_u8(tmp1, tmp2);
920
+ if (neon_table_size > 2) {
921
+ uint8x16_t tmp3 = vqtbl4q_u8(cmap_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
922
+ uint8x16_t tmp4 = vqtbl4q_u8(cmap_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
923
+ result.needs_escape = vorrq_u8(result.needs_escape, vorrq_u8(tmp4, tmp3));
924
+ }
925
+ if (has_hi && do_unicode_validation) {
926
+ uint8x16_t has_some_hibit = vandq_u8(chunk, vdupq_n_u8(0x80));
927
+ result.has_some_hibit = vmaxvq_u8(has_some_hibit) != 0;
928
+ result.do_unicode_validation = has_hi && do_unicode_validation && result.has_some_hibit;
929
+ }
930
+ return result;
931
+ }
932
+
933
+ #endif /* HAVE_SIMD_NEON */
934
+
761
935
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
762
- size_t size;
763
- char *cmap;
936
+ size_t size;
937
+ char *cmap;
938
+ #ifdef HAVE_SIMD_NEON
939
+ uint8x16x4_t *cmap_neon = NULL;
940
+ int neon_table_size;
941
+ #endif /* HAVE_SIMD_NEON */
764
942
  const char *orig = str;
765
943
  bool has_hi = false;
766
944
  bool do_unicode_validation = false;
@@ -792,7 +970,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
792
970
  long sz;
793
971
 
794
972
  cmap = rails_xss_friendly_chars;
795
- sz = rails_xss_friendly_size((uint8_t *)str, cnt);
973
+ #ifdef HAVE_SIMD_NEON
974
+ cmap_neon = rails_xss_friendly_chars_neon;
975
+ neon_table_size = 4;
976
+ #endif /* HAVE_NEON_SIMD */
977
+ sz = rails_xss_friendly_size((uint8_t *)str, cnt);
796
978
  if (sz < 0) {
797
979
  has_hi = true;
798
980
  size = (size_t)-sz;
@@ -805,7 +987,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
805
987
  case RailsEsc: {
806
988
  long sz;
807
989
  cmap = rails_friendly_chars;
808
- sz = rails_friendly_size((uint8_t *)str, cnt);
990
+ #ifdef HAVE_SIMD_NEON
991
+ cmap_neon = rails_friendly_chars_neon;
992
+ neon_table_size = 2;
993
+ #endif /* HAVE_NEON_SIMD */
994
+ sz = rails_friendly_size((uint8_t *)str, cnt);
809
995
  if (sz < 0) {
810
996
  has_hi = true;
811
997
  size = (size_t)-sz;
@@ -816,7 +1002,12 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
816
1002
  break;
817
1003
  }
818
1004
  case JSONEsc:
819
- default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
1005
+ default: cmap = hibit_friendly_chars;
1006
+ #ifdef HAVE_SIMD_NEON
1007
+ cmap_neon = hibit_friendly_chars_neon;
1008
+ neon_table_size = 2;
1009
+ #endif /* HAVE_NEON_SIMD */
1010
+ size = hibit_friendly_size((uint8_t *)str, cnt);
820
1011
  }
821
1012
  assure_size(out, size + BUFFER_EXTRA);
822
1013
  *out->cur++ = '"';
@@ -842,8 +1033,116 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
842
1033
  if (is_sym) {
843
1034
  *out->cur++ = ':';
844
1035
  }
1036
+ #ifdef HAVE_SIMD_NEON
1037
+ const char *chunk_start;
1038
+ const char *chunk_end;
1039
+ const char *cursor = str;
1040
+ int neon_state = (cmap_neon != NULL) ? 1 : 4;
1041
+ char matches[16];
1042
+ bool do_hi_validation = false;
1043
+ // uint64_t neon_match_mask = 0;
1044
+ #define SEARCH_FLUSH \
1045
+ if (str > cursor) { \
1046
+ APPEND_CHARS(out->cur, cursor, str - cursor); \
1047
+ cursor = str; \
1048
+ }
1049
+
1050
+ loop:
1051
+ #endif /* HAVE_SIMD_NEON */
845
1052
  for (; str < end; str++) {
846
- switch (cmap[(uint8_t)*str]) {
1053
+ char action = 0;
1054
+ #ifdef HAVE_SIMD_NEON
1055
+ /* neon_state:
1056
+ * 1: Scanning for matches. There must be at least
1057
+ sizeof(uint8x16_t) bytes of input data to use SIMD and
1058
+ cmap_neon must be non-null.
1059
+ * 2: Matches have been found. Will set str to the position of the
1060
+ * next match and set the state to 3.
1061
+ * If there are no more matches it will transition to state 1.
1062
+ * 4: Fallback to the scalar algorithm. Not enough data to use
1063
+ * SIMD.
1064
+ */
1065
+ #define NEON_SET_STATE(state) \
1066
+ neon_state = state; \
1067
+ goto loop;
1068
+ #define NEON_RETURN_TO_STATE(state) neon_state = state;
1069
+ switch (neon_state) {
1070
+ case 1: {
1071
+ while (true) {
1072
+ const char *chunk_ptr = NULL;
1073
+ if (str + sizeof(uint8x16_t) <= end) {
1074
+ chunk_ptr = str;
1075
+ chunk_start = str;
1076
+ chunk_end = str + sizeof(uint8x16_t);
1077
+ } else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
1078
+ memset(out->cur, 'A', sizeof(uint8x16_t));
1079
+ memcpy(out->cur, str, (end - str));
1080
+ chunk_ptr = out->cur;
1081
+ chunk_start = str;
1082
+ chunk_end = end;
1083
+ } else {
1084
+ SEARCH_FLUSH;
1085
+ NEON_SET_STATE(4);
1086
+ break; /* Unreachable */
1087
+ }
1088
+ neon_match_result result = neon_update(chunk_ptr,
1089
+ cmap_neon,
1090
+ neon_table_size,
1091
+ do_unicode_validation,
1092
+ has_hi);
1093
+ if ((result.do_unicode_validation) || vmaxvq_u8(result.needs_escape) != 0) {
1094
+ SEARCH_FLUSH;
1095
+ uint8x16_t actions = vaddq_u8(result.needs_escape, vdupq_n_u8('1'));
1096
+ do_hi_validation = result.do_unicode_validation;
1097
+ vst1q_u8((unsigned char *)matches, actions);
1098
+ NEON_SET_STATE(2);
1099
+ break; /* Unreachable */
1100
+ }
1101
+ str = chunk_end;
1102
+ }
1103
+ // We must have run out of data to use SIMD. Go to state 4.
1104
+ SEARCH_FLUSH;
1105
+ NEON_SET_STATE(4);
1106
+ } break;
1107
+ case 3:
1108
+ cursor = str;
1109
+ // This fall through is intentional. We return to state 3 after we process
1110
+ // a byte (or multiple). We return to this state to ensure the cursor is
1111
+ // pointing to the correct location. We then resume looking for matches
1112
+ // within the previously processed chunk.
1113
+ case 2:
1114
+ if (str >= chunk_end) {
1115
+ NEON_SET_STATE(1);
1116
+ }
1117
+ if (!do_hi_validation) {
1118
+ long i = str - chunk_start;
1119
+ for (; str < chunk_end; i++) {
1120
+ if ((action = matches[i]) != '1') {
1121
+ break;
1122
+ }
1123
+ *out->cur++ = *str++;
1124
+ }
1125
+ // The loop above may have advanced str and directly output them to out->cur.
1126
+ // Ensure cursor is set appropriately.
1127
+ cursor = str;
1128
+ if (str >= chunk_end) {
1129
+ // We must have advanced past the end... we are done.
1130
+ NEON_SET_STATE(1);
1131
+ }
1132
+ } else {
1133
+ long match_index = str - chunk_start;
1134
+ action = matches[match_index];
1135
+ }
1136
+ NEON_RETURN_TO_STATE(3);
1137
+ break;
1138
+ case 4: action = cmap[(uint8_t)*str];
1139
+ }
1140
+ #undef NEON_SET_STATE
1141
+ #undef NEON_RETURN_TO_STATE
1142
+ #else
1143
+ action = cmap[(uint8_t)*str];
1144
+ #endif /* HAVE_SIMD_NEON */
1145
+ switch (action) {
847
1146
  case '1':
848
1147
  if (do_unicode_validation && check_start <= str) {
849
1148
  if (0 != (0x80 & (uint8_t)*str)) {
@@ -906,7 +1205,7 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
906
1205
  *out->cur++ = '"';
907
1206
  }
908
1207
  if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
909
- uint8_t c = (uint8_t) * (str - 1);
1208
+ uint8_t c = (uint8_t)*(str - 1);
910
1209
  int i;
911
1210
  int scnt = (int)(str - orig);
912
1211
 
@@ -957,7 +1256,7 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
957
1256
  void oj_dump_obj_to_s(VALUE obj, Out out) {
958
1257
  volatile VALUE rstr = oj_safe_string_convert(obj);
959
1258
 
960
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1259
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
961
1260
  }
962
1261
 
963
1262
  void oj_dump_raw(const char *str, size_t cnt, Out out) {
@@ -1092,7 +1391,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1092
1391
 
1093
1392
  void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1094
1393
  volatile VALUE rs = rb_big2str(obj, 10);
1095
- int cnt = (int)RSTRING_LEN(rs);
1394
+ size_t cnt = RSTRING_LEN(rs);
1096
1395
  bool dump_as_string = false;
1097
1396
 
1098
1397
  if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
@@ -1114,7 +1413,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1114
1413
  char buf[64];
1115
1414
  char *b;
1116
1415
  double d = rb_num2dbl(obj);
1117
- int cnt = 0;
1416
+ size_t cnt = 0;
1118
1417
 
1119
1418
  if (0.0 == d) {
1120
1419
  b = buf;
@@ -1225,7 +1524,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1225
1524
  } else if (0 == out->opts->float_prec) {
1226
1525
  volatile VALUE rstr = oj_safe_string_convert(obj);
1227
1526
 
1228
- cnt = (int)RSTRING_LEN(rstr);
1527
+ cnt = RSTRING_LEN(rstr);
1229
1528
  if ((int)sizeof(buf) <= cnt) {
1230
1529
  cnt = sizeof(buf) - 1;
1231
1530
  }
@@ -1239,8 +1538,8 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1239
1538
  *out->cur = '\0';
1240
1539
  }
1241
1540
 
1242
- int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
1243
- int cnt = snprintf(buf, blen, format, d);
1541
+ size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
1542
+ size_t cnt = snprintf(buf, blen, format, d);
1244
1543
 
1245
1544
  // Round off issues at 16 significant digits so check for obvious ones of
1246
1545
  // 0001 and 9999.
@@ -1248,7 +1547,7 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1248
1547
  volatile VALUE rstr = oj_safe_string_convert(obj);
1249
1548
 
1250
1549
  strcpy(buf, RSTRING_PTR(rstr));
1251
- cnt = (int)RSTRING_LEN(rstr);
1550
+ cnt = RSTRING_LEN(rstr);
1252
1551
  }
1253
1552
  return cnt;
1254
1553
  }
data/ext/oj/dump.h CHANGED
@@ -7,12 +7,17 @@
7
7
  #include <ruby.h>
8
8
 
9
9
  #include "oj.h"
10
+ #include "simd.h"
10
11
 
11
12
  #define MAX_DEPTH 1000
12
13
 
13
14
  // Extra padding at end of buffer.
14
15
  #define BUFFER_EXTRA 64
15
16
 
17
+ #ifdef HAVE_SIMD_NEON
18
+ extern void initialize_neon(void);
19
+ #endif /* HAVE_SIMD_NEON */
20
+
16
21
  extern void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok);
17
22
  extern void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok);
18
23
  extern void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok);
@@ -30,7 +35,7 @@ extern void oj_dump_xml_time(VALUE obj, Out out);
30
35
  extern void oj_dump_time(VALUE obj, Out out, int withZone);
31
36
  extern void oj_dump_obj_to_s(VALUE obj, Out out);
32
37
 
33
- extern const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp);
38
+ extern const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, size_t *lenp);
34
39
 
35
40
  // initialize an out buffer with the provided stack allocated memory
36
41
  extern void oj_out_init(Out out);
@@ -53,7 +58,7 @@ extern void oj_dump_raw_json(VALUE obj, int depth, Out out);
53
58
  extern VALUE oj_add_to_json(int argc, VALUE *argv, VALUE self);
54
59
  extern VALUE oj_remove_to_json(int argc, VALUE *argv, VALUE self);
55
60
 
56
- extern int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format);
61
+ extern size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format);
57
62
 
58
63
  extern time_t oj_sec_from_time_hard_way(VALUE obj);
59
64
 
data/ext/oj/dump_compat.c CHANGED
@@ -103,7 +103,7 @@ static void dump_values_array(VALUE *values, int depth, Out out) {
103
103
  static void dump_to_json(VALUE obj, Out out) {
104
104
  volatile VALUE rs;
105
105
  const char *s;
106
- int len;
106
+ size_t len;
107
107
 
108
108
  TRACE(out->opts->trace, "to_json", obj, 0, TraceRubyIn);
109
109
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
@@ -115,7 +115,7 @@ static void dump_to_json(VALUE obj, Out out) {
115
115
 
116
116
  StringValue(rs);
117
117
  s = RSTRING_PTR(rs);
118
- len = (int)RSTRING_LEN(rs);
118
+ len = RSTRING_LEN(rs);
119
119
 
120
120
  assure_size(out, len + 1);
121
121
  APPEND_CHARS(out->cur, s, len);
@@ -124,7 +124,8 @@ static void dump_to_json(VALUE obj, Out out) {
124
124
 
125
125
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
126
126
  size_t size;
127
- int i, cnt;
127
+ size_t i;
128
+ size_t cnt;
128
129
  int d2 = depth + 1;
129
130
  long id = oj_check_circular(a, out);
130
131
 
@@ -136,7 +137,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
136
137
  dump_to_json(a, out);
137
138
  return;
138
139
  }
139
- cnt = (int)RARRAY_LEN(a);
140
+ cnt = RARRAY_LEN(a);
140
141
  *out->cur++ = '[';
141
142
  assure_size(out, 2);
142
143
  if (0 == cnt) {
@@ -552,7 +553,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
552
553
  char buf[64];
553
554
  char *b;
554
555
  double d = rb_num2dbl(obj);
555
- int cnt = 0;
556
+ size_t cnt = 0;
556
557
 
557
558
  if (0.0 == d) {
558
559
  b = buf;
@@ -590,7 +591,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
590
591
  volatile VALUE rstr = oj_safe_string_convert(obj);
591
592
 
592
593
  strcpy(buf, RSTRING_PTR(rstr));
593
- cnt = (int)RSTRING_LEN(rstr);
594
+ cnt = RSTRING_LEN(rstr);
594
595
  }
595
596
  assure_size(out, cnt);
596
597
  APPEND_CHARS(out->cur, buf, cnt);
@@ -800,7 +801,7 @@ static void dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
800
801
  // rb_big2str can not so unless overridden by using add_to_json(Integer)
801
802
  // this must use to_s to pass the json gem unit tests.
802
803
  volatile VALUE rs;
803
- int cnt;
804
+ size_t cnt;
804
805
  bool dump_as_string = false;
805
806
 
806
807
  if (use_bignum_alt) {
@@ -809,7 +810,7 @@ static void dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
809
810
  rs = oj_safe_string_convert(obj);
810
811
  }
811
812
  rb_check_type(rs, T_STRING);
812
- cnt = (int)RSTRING_LEN(rs);
813
+ cnt = RSTRING_LEN(rs);
813
814
 
814
815
  if (out->opts->int_range_min != 0 || out->opts->int_range_max != 0) {
815
816
  dump_as_string = true; // Bignum cannot be inside of Fixnum range
data/ext/oj/dump_leaf.c CHANGED
@@ -17,7 +17,7 @@ inline static void dump_chars(const char *s, size_t size, Out out) {
17
17
  static void dump_leaf_str(Leaf leaf, Out out) {
18
18
  switch (leaf->value_type) {
19
19
  case STR_VAL: oj_dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out); break;
20
- case RUBY_VAL: oj_dump_cstr(StringValueCStr(leaf->value), (int)RSTRING_LEN(leaf->value), 0, 0, out); break;
20
+ case RUBY_VAL: oj_dump_cstr(StringValueCStr(leaf->value), RSTRING_LEN(leaf->value), 0, 0, out); break;
21
21
  case COL_VAL:
22
22
  default: rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type); break;
23
23
  }
data/ext/oj/dump_object.c CHANGED
@@ -33,7 +33,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
33
33
  if (oj_bigdecimal_class == clas) {
34
34
  volatile VALUE rstr = oj_safe_string_convert(obj);
35
35
  const char *str = RSTRING_PTR(rstr);
36
- int len = (int)RSTRING_LEN(rstr);
36
+ size_t len = RSTRING_LEN(rstr);
37
37
 
38
38
  if (No != out->opts->bigdec_as_num) {
39
39
  oj_dump_raw(str, len, out);
@@ -62,7 +62,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
62
62
  if (oj_bigdecimal_class == clas) {
63
63
  volatile VALUE rstr = oj_safe_string_convert(obj);
64
64
  const char *str = RSTRING_PTR(rstr);
65
- int len = (int)RSTRING_LEN(rstr);
65
+ size_t len = RSTRING_LEN(rstr);
66
66
 
67
67
  if (0 == strcasecmp("Infinity", str)) {
68
68
  str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
@@ -95,7 +95,8 @@ static void dump_class(VALUE obj, int depth, Out out, bool as_ok) {
95
95
 
96
96
  static void dump_array_class(VALUE a, VALUE clas, int depth, Out out) {
97
97
  size_t size;
98
- int i, cnt;
98
+ size_t i;
99
+ size_t cnt;
99
100
  int d2 = depth + 1;
100
101
  long id = oj_check_circular(a, out);
101
102
 
@@ -106,7 +107,7 @@ static void dump_array_class(VALUE a, VALUE clas, int depth, Out out) {
106
107
  dump_obj_attrs(a, clas, 0, depth, out);
107
108
  return;
108
109
  }
109
- cnt = (int)RARRAY_LEN(a);
110
+ cnt = RARRAY_LEN(a);
110
111
  *out->cur++ = '[';
111
112
  if (0 < id) {
112
113
  assure_size(out, d2 * out->indent + 16);
@@ -181,7 +182,7 @@ static void dump_str_class(VALUE obj, VALUE clas, int depth, Out out) {
181
182
  dump_obj_attrs(obj, clas, 0, depth, out);
182
183
  } else {
183
184
  const char *s = RSTRING_PTR(obj);
184
- size_t len = (int)RSTRING_LEN(obj);
185
+ size_t len = RSTRING_LEN(obj);
185
186
  char s1 = s[1];
186
187
 
187
188
  oj_dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
@@ -195,7 +196,7 @@ static void dump_str(VALUE obj, int depth, Out out, bool as_ok) {
195
196
  static void dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
196
197
  volatile VALUE s = rb_sym2str(obj);
197
198
 
198
- oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 1, 0, out);
199
+ oj_dump_cstr(RSTRING_PTR(s), RSTRING_LEN(s), 1, 0, out);
199
200
  }
200
201
 
201
202
  static int hash_cb(VALUE key, VALUE value, VALUE ov) {
@@ -389,7 +390,7 @@ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
389
390
  rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
390
391
  } else {
391
392
  const char *s = RSTRING_PTR(v);
392
- int len = (int)RSTRING_LEN(v);
393
+ size_t len = RSTRING_LEN(v);
393
394
  const char *name = rb_id2name(*odd->attrs);
394
395
  size_t nlen = strlen(name);
395
396
 
@@ -489,7 +490,7 @@ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out)
489
490
  *out->cur++ = ',';
490
491
  fill_indent(out, d2);
491
492
  APPEND_CHARS(out->cur, "\"self\":", 7);
492
- oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
493
+ oj_dump_cstr(RSTRING_PTR(obj), RSTRING_LEN(obj), 0, 0, out);
493
494
  break;
494
495
  case T_ARRAY:
495
496
  assure_size(out, d2 * out->indent + 14);
@@ -564,11 +565,12 @@ static void dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
564
565
  static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
565
566
  VALUE clas = rb_obj_class(obj);
566
567
  const char *class_name = rb_class2name(clas);
567
- int i;
568
- int d2 = depth + 1;
569
- int d3 = d2 + 1;
570
- size_t len = strlen(class_name);
571
- size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
568
+ size_t i;
569
+ int d2 = depth + 1;
570
+ int d3 = d2 + 1;
571
+ size_t len = strlen(class_name);
572
+ size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
573
+ char circular = out->opts->circular;
572
574
 
573
575
  assure_size(out, size);
574
576
  *out->cur++ = '{';
@@ -577,14 +579,14 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
577
579
  if ('#' == *class_name) {
578
580
  VALUE ma = rb_struct_s_members(clas);
579
581
  const char *name;
580
- int cnt = (int)RARRAY_LEN(ma);
582
+ size_t cnt = RARRAY_LEN(ma);
581
583
 
582
584
  *out->cur++ = '[';
583
585
  for (i = 0; i < cnt; i++) {
584
586
  volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
585
587
 
586
588
  name = RSTRING_PTR(s);
587
- len = (int)RSTRING_LEN(s);
589
+ len = RSTRING_LEN(s);
588
590
  size = len + 3;
589
591
  assure_size(out, size);
590
592
  if (0 < i) {
@@ -607,14 +609,18 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
607
609
  {
608
610
  VALUE v;
609
611
  int cnt;
612
+
610
613
  #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
611
614
  cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
612
615
  #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
613
616
  cnt = (int)RSTRUCT_LEN(obj);
614
617
  #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
615
618
 
616
- for (i = 0; i < cnt; i++) {
617
- v = RSTRUCT_GET(obj, i);
619
+ if (0 == strcmp(class_name, "Range")) {
620
+ out->opts->circular = 'n';
621
+ }
622
+ for (i = 0; i < (size_t)cnt; i++) {
623
+ v = RSTRUCT_GET(obj, (int)i);
618
624
  if (dump_ignore(out->opts, v)) {
619
625
  v = Qnil;
620
626
  }
@@ -630,6 +636,9 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
630
636
  // class in interpreted Ruby so length() may not be defined.
631
637
  int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
632
638
 
639
+ if (0 == strcmp(class_name, "Range")) {
640
+ out->opts->circular = 'n';
641
+ }
633
642
  for (i = 0; i < slen; i++) {
634
643
  assure_size(out, size);
635
644
  fill_indent(out, d3);
@@ -641,6 +650,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
641
650
  }
642
651
  }
643
652
  #endif
653
+ out->opts->circular = circular;
644
654
  out->cur--;
645
655
  APPEND_CHARS(out->cur, "]}", 2);
646
656
  *out->cur = '\0';
data/ext/oj/dump_strict.c CHANGED
@@ -30,7 +30,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
30
30
  char buf[64];
31
31
  char* b;
32
32
  double d = rb_num2dbl(obj);
33
- int cnt = 0;
33
+ size_t cnt = 0;
34
34
 
35
35
  if (0.0 == d) {
36
36
  b = buf;
@@ -92,7 +92,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
92
92
  } else if (0 == out->opts->float_prec) {
93
93
  volatile VALUE rstr = oj_safe_string_convert(obj);
94
94
 
95
- cnt = (int)RSTRING_LEN(rstr);
95
+ cnt = RSTRING_LEN(rstr);
96
96
  if ((int)sizeof(buf) <= cnt) {
97
97
  cnt = sizeof(buf) - 1;
98
98
  }
@@ -109,7 +109,8 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
109
109
 
110
110
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
111
111
  size_t size;
112
- int i, cnt;
112
+ size_t i;
113
+ size_t cnt;
113
114
  int d2 = depth + 1;
114
115
 
115
116
  if (Yes == out->opts->circular) {
@@ -118,7 +119,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
118
119
  return;
119
120
  }
120
121
  }
121
- cnt = (int)RARRAY_LEN(a);
122
+ cnt = RARRAY_LEN(a);
122
123
  *out->cur++ = '[';
123
124
  size = 2;
124
125
  assure_size(out, size);
@@ -290,7 +291,7 @@ static void dump_data_strict(VALUE obj, int depth, Out out, bool as_ok) {
290
291
  if (oj_bigdecimal_class == clas) {
291
292
  volatile VALUE rstr = oj_safe_string_convert(obj);
292
293
 
293
- oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
294
+ oj_dump_raw(RSTRING_PTR(rstr), RSTRING_LEN(rstr), out);
294
295
  } else {
295
296
  raise_strict(obj);
296
297
  }
@@ -302,7 +303,7 @@ static void dump_data_null(VALUE obj, int depth, Out out, bool as_ok) {
302
303
  if (oj_bigdecimal_class == clas) {
303
304
  volatile VALUE rstr = oj_safe_string_convert(obj);
304
305
 
305
- oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
306
+ oj_dump_raw(RSTRING_PTR(rstr), RSTRING_LEN(rstr), out);
306
307
  } else {
307
308
  oj_dump_nil(Qnil, depth, out, false);
308
309
  }
data/ext/oj/fast.c CHANGED
@@ -1086,7 +1086,7 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1086
1086
  int given = rb_block_given_p();
1087
1087
 
1088
1088
  Check_Type(str, T_STRING);
1089
- len = (int)RSTRING_LEN(str) + 1;
1089
+ len = RSTRING_LEN(str) + 1;
1090
1090
  json = OJ_R_ALLOC_N(char, len);
1091
1091
 
1092
1092
  memcpy(json, StringValuePtr(str), len);
data/ext/oj/object.c CHANGED
@@ -305,7 +305,7 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
305
305
 
306
306
  static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, volatile VALUE value) {
307
307
  if (T_ARRAY == rb_type(value)) {
308
- int len = (int)RARRAY_LEN(value);
308
+ size_t len = RARRAY_LEN(value);
309
309
 
310
310
  if (2 == klen && 'u' == key[1]) {
311
311
  volatile VALUE sc;
@@ -321,19 +321,20 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
321
321
  if (T_ARRAY == rb_type(e1)) {
322
322
  VALUE args[1024];
323
323
  volatile VALUE rstr;
324
- int i, cnt = (int)RARRAY_LEN(e1);
324
+ size_t i;
325
+ size_t cnt = RARRAY_LEN(e1);
325
326
 
326
327
  for (i = 0; i < cnt; i++) {
327
328
  rstr = RARRAY_AREF(e1, i);
328
329
  args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
329
330
  }
330
- sc = rb_funcall2(rb_cStruct, oj_new_id, cnt, args);
331
+ sc = rb_funcall2(rb_cStruct, oj_new_id, (int)cnt, args);
331
332
  } else {
332
333
  // If struct is not defined then we let this fail and raise an exception.
333
334
  sc = oj_name2struct(pi, *RARRAY_CONST_PTR(value), rb_eArgError);
334
335
  }
335
336
  if (sc == rb_cRange) {
336
- parent->val = rb_class_new_instance(len - 1, RARRAY_CONST_PTR(value) + 1, rb_cRange);
337
+ parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
337
338
  } else {
338
339
  // Create a properly initialized struct instance without calling the initialize method.
339
340
  parent->val = rb_obj_alloc(sc);
@@ -348,10 +349,10 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
348
349
  slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
349
350
  #endif
350
351
  // MRI >= 1.9
351
- if (len - 1 > slen) {
352
+ if (len - 1 > (size_t)slen) {
352
353
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
353
354
  } else {
354
- int i;
355
+ size_t i;
355
356
 
356
357
  for (i = 0; i < len - 1; i++) {
357
358
  rb_struct_aset(parent->val, INT2FIX(i), RARRAY_CONST_PTR(value)[i + 1]);
@@ -698,9 +699,8 @@ oj_object_parse(int argc, VALUE *argv, VALUE self) {
698
699
 
699
700
  if (T_STRING == rb_type(*argv)) {
700
701
  return oj_pi_parse(argc, argv, &pi, 0, 0, 1);
701
- } else {
702
- return oj_pi_sparse(argc, argv, &pi, 0);
703
702
  }
703
+ return oj_pi_sparse(argc, argv, &pi, 0);
704
704
  }
705
705
 
706
706
  VALUE
data/ext/oj/oj.c CHANGED
@@ -18,6 +18,7 @@
18
18
  #include "odd.h"
19
19
  #include "parse.h"
20
20
  #include "rails.h"
21
+ #include "simd.h"
21
22
 
22
23
  typedef struct _yesNoOpt {
23
24
  VALUE sym;
@@ -923,12 +924,12 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
923
924
  OJ_R_FREE(copts->ignore);
924
925
  copts->ignore = NULL;
925
926
  if (Qnil != v) {
926
- int cnt;
927
+ size_t cnt;
927
928
 
928
929
  rb_check_type(v, T_ARRAY);
929
- cnt = (int)RARRAY_LEN(v);
930
+ cnt = RARRAY_LEN(v);
930
931
  if (0 < cnt) {
931
- int i;
932
+ size_t i;
932
933
 
933
934
  copts->ignore = OJ_R_ALLOC_N(VALUE, cnt + 1);
934
935
  for (i = 0; i < cnt; i++) {
@@ -973,7 +974,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
973
974
  }
974
975
  } else if (float_format_sym == k) {
975
976
  rb_check_type(v, T_STRING);
976
- if (6 < (int)RSTRING_LEN(v)) {
977
+ if (6 < RSTRING_LEN(v)) {
977
978
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
978
979
  }
979
980
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
@@ -1188,7 +1189,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1188
1189
  OJ_FREE(wide_path);
1189
1190
  }
1190
1191
  #else
1191
- fd = open(path, O_RDONLY);
1192
+ fd = open(path, O_RDONLY);
1192
1193
  #endif
1193
1194
  if (0 == fd) {
1194
1195
  rb_raise(rb_eIOError, "%s", strerror(errno));
@@ -2081,4 +2082,8 @@ void Init_oj(void) {
2081
2082
 
2082
2083
  oj_parser_init();
2083
2084
  oj_scanner_init();
2085
+
2086
+ #ifdef HAVE_SIMD_NEON
2087
+ initialize_neon();
2088
+ #endif /* HAVE_SIMD_NEON */
2084
2089
  }
data/ext/oj/rails.c CHANGED
@@ -140,7 +140,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
140
140
  volatile VALUE v;
141
141
  int cnt;
142
142
  int i;
143
- int len;
143
+ size_t len;
144
144
  const char *name;
145
145
 
146
146
  #ifdef RSTRUCT_LEN
@@ -161,7 +161,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
161
161
  volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
162
162
 
163
163
  name = RSTRING_PTR(s);
164
- len = (int)RSTRING_LEN(s);
164
+ len = RSTRING_LEN(s);
165
165
  assure_size(out, size + sep_len + 6);
166
166
  if (0 < i) {
167
167
  *out->cur++ = ',';
@@ -205,11 +205,11 @@ static void dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
205
205
  if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
206
206
  oj_dump_nil(Qnil, depth, out, false);
207
207
  } else if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) {
208
- oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
208
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
209
209
  } else if (Yes == out->opts->bigdec_as_num) {
210
- oj_dump_raw(str, (int)RSTRING_LEN(rstr), out);
210
+ oj_dump_raw(str, RSTRING_LEN(rstr), out);
211
211
  } else {
212
- oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
212
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
213
213
  }
214
214
  }
215
215
 
@@ -330,14 +330,14 @@ static void dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) {
330
330
  static void dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
331
331
  volatile VALUE rstr = oj_safe_string_convert(obj);
332
332
 
333
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
333
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
334
334
  }
335
335
 
336
336
  static ID parameters_id = 0;
337
337
 
338
338
  typedef struct _strLen {
339
339
  const char *str;
340
- int len;
340
+ size_t len;
341
341
  } *StrLen;
342
342
 
343
343
  static void dump_actioncontroller_parameters(VALUE obj, int depth, Out out, bool as_ok) {
@@ -352,10 +352,10 @@ static StrLen columns_array(VALUE rcols, int *ccnt) {
352
352
  volatile VALUE v;
353
353
  StrLen cp;
354
354
  StrLen cols;
355
- int i;
356
- int cnt = (int)RARRAY_LEN(rcols);
355
+ size_t i;
356
+ size_t cnt = RARRAY_LEN(rcols);
357
357
 
358
- *ccnt = cnt;
358
+ *ccnt = (int)cnt;
359
359
  cols = OJ_R_ALLOC_N(struct _strLen, cnt);
360
360
  for (i = 0, cp = cols; i < cnt; i++, cp++) {
361
361
  v = RARRAY_AREF(rcols, i);
@@ -363,7 +363,7 @@ static StrLen columns_array(VALUE rcols, int *ccnt) {
363
363
  v = oj_safe_string_convert(v);
364
364
  }
365
365
  cp->str = StringValuePtr(v);
366
- cp->len = (int)RSTRING_LEN(v);
366
+ cp->len = RSTRING_LEN(v);
367
367
  }
368
368
  return cols;
369
369
  }
@@ -424,7 +424,8 @@ static void dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok)
424
424
  volatile VALUE rows;
425
425
  StrLen cols;
426
426
  int ccnt = 0;
427
- int i, rcnt;
427
+ size_t i;
428
+ size_t rcnt;
428
429
  size_t size;
429
430
  int d2 = depth + 1;
430
431
 
@@ -435,7 +436,7 @@ static void dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok)
435
436
  out->argc = 0;
436
437
  cols = columns_array(rb_ivar_get(obj, columns_id), &ccnt);
437
438
  rows = rb_ivar_get(obj, rows_id);
438
- rcnt = (int)RARRAY_LEN(rows);
439
+ rcnt = RARRAY_LEN(rows);
439
440
  assure_size(out, 2);
440
441
  *out->cur++ = '[';
441
442
  if (out->opts->dump_opts.use) {
@@ -1173,7 +1174,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1173
1174
  char buf[64];
1174
1175
  char *b;
1175
1176
  double d = rb_num2dbl(obj);
1176
- int cnt = 0;
1177
+ size_t cnt = 0;
1177
1178
 
1178
1179
  if (0.0 == d) {
1179
1180
  b = buf;
@@ -1194,7 +1195,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1194
1195
  volatile VALUE rstr = oj_safe_string_convert(obj);
1195
1196
 
1196
1197
  strcpy(buf, RSTRING_PTR(rstr));
1197
- cnt = (int)RSTRING_LEN(rstr);
1198
+ cnt = RSTRING_LEN(rstr);
1198
1199
  }
1199
1200
  }
1200
1201
  assure_size(out, cnt);
@@ -1206,7 +1207,8 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1206
1207
 
1207
1208
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
1208
1209
  size_t size;
1209
- int i, cnt;
1210
+ size_t i;
1211
+ size_t cnt;
1210
1212
  int d2 = depth + 1;
1211
1213
 
1212
1214
  if (Yes == out->opts->circular) {
@@ -1220,7 +1222,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
1220
1222
  dump_as_json(a, depth, out, false);
1221
1223
  return;
1222
1224
  }
1223
- cnt = (int)RARRAY_LEN(a);
1225
+ cnt = RARRAY_LEN(a);
1224
1226
  *out->cur++ = '[';
1225
1227
  size = 2;
1226
1228
  assure_size(out, size);
data/ext/oj/simd.h ADDED
@@ -0,0 +1,10 @@
1
+ #ifndef OJ_SIMD_H
2
+ #define OJ_SIMD_H
3
+
4
+ #if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64)
5
+ #define HAVE_SIMD_NEON 1
6
+ #define SIMD_MINIMUM_THRESHOLD 6
7
+ #include <arm_neon.h>
8
+ #endif
9
+
10
+ #endif /* OJ_SIMD_H */
data/ext/oj/wab.c CHANGED
@@ -111,10 +111,11 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
111
111
 
112
112
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
113
113
  size_t size;
114
- int i, cnt;
114
+ size_t i;
115
+ size_t cnt;
115
116
  int d2 = depth + 1;
116
117
 
117
- cnt = (int)RARRAY_LEN(a);
118
+ cnt = RARRAY_LEN(a);
118
119
  *out->cur++ = '[';
119
120
  size = 2;
120
121
  assure_size(out, size);
@@ -226,7 +227,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
226
227
  } else if (oj_bigdecimal_class == clas) {
227
228
  volatile VALUE rstr = oj_safe_string_convert(obj);
228
229
 
229
- oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
230
+ oj_dump_raw(RSTRING_PTR(rstr), RSTRING_LEN(rstr), out);
230
231
  } else if (resolve_wab_uuid_class() == clas) {
231
232
  oj_dump_str(oj_safe_string_convert(obj), depth, out, false);
232
233
  } else if (resolve_uri_http_class() == clas) {
data/lib/oj/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Oj
2
2
  # Current version of the module.
3
- VERSION = '3.16.10'
3
+ VERSION = '3.16.11'
4
4
  end
data/pages/Encoding.md CHANGED
@@ -15,7 +15,7 @@ in a JSON document. The formatting follows these rules.
15
15
  * The `'^'` character denotes a special key value when in a JSON Object sequence.
16
16
 
17
17
  * A Ruby String that starts with `':'`or the sequence `'^i'` or `'^r'` are
18
- encoded by excaping the first character so that it appears as `'\u005e'` or
18
+ encoded by escaping the first character so that it appears as `'\u005e'` or
19
19
  `'\u003a'` instead of `':'` or `'^'`.
20
20
 
21
21
  * A `"^c"` JSON Object key indicates the value should be converted to a Ruby
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.16.10
4
+ version: 3.16.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-25 00:00:00.000000000 Z
10
+ date: 2025-05-30 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bigdecimal
@@ -162,6 +162,7 @@ files:
162
162
  - ext/oj/saj2.c
163
163
  - ext/oj/saj2.h
164
164
  - ext/oj/scp.c
165
+ - ext/oj/simd.h
165
166
  - ext/oj/sparse.c
166
167
  - ext/oj/stream_writer.c
167
168
  - ext/oj/strict.c