oj 3.16.11 → 3.16.12

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: 30aea721380a4e3edc306dd19906d8777f230a639ba3427e9394dd543a3a7e3b
4
- data.tar.gz: b024a9d4513c16c1bfe4fc3c4adeacb1afd9a0e670987476d32cfa2fa74e9b1e
3
+ metadata.gz: 6e2053531cd4c7c7b49bf16ddafbfda868e67e66c1bfbec1b06daaa0ba3f1c45
4
+ data.tar.gz: ce029b2f90660922dd8fb335c23463a1fe1d81317d56e48f2cfddc9271de2d15
5
5
  SHA512:
6
- metadata.gz: 527ea1162cb135bbe16eefc10a7cb05444182767aca6fa0b6986622e52d7082bcec020c43e663251406c81602018f7d0842c2c5cee37aeca0269560e502d99dd
7
- data.tar.gz: e49e9f63e373cb0ec21f604f97899f87815b86ef5a5eafad30e7bddbd11e71156f92beaa1259c83609c2d45d2a8aac87c8b27e3e266fcc2bd99a1908327c796d
6
+ metadata.gz: deb7f1447b5022adad6d7387b8a8bfd866d399abc2d9e434f7e6d321fa73cb1738ff9aa7ee22ac064c455d5d3951ba7469a30720c9474af5e96c70eaa5b5303a
7
+ data.tar.gz: b9d28d76c714947c1e6b133225e348838ad13f1c8b7dc82f0fee261272259cd0e83b911f2f2762c55a719f93e669275ad32d393f371c264a4e587e49b1c3a84b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.16.12 - 2025-10-29
4
+
5
+ - Fixed dump realloc bug that occurred when using the compat mode dump options.
6
+
3
7
  ## 3.16.11 - 2025-05-29
4
8
 
5
9
  - Fixed range encoding with the :circular option
data/ext/oj/dump.c CHANGED
@@ -252,38 +252,46 @@ inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
252
252
  }
253
253
 
254
254
  inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
255
- long size = 0;
256
- uint8_t hi = 0;
255
+ long size = 0;
256
+ uint32_t hi = 0;
257
257
 
258
258
  #ifdef HAVE_SIMD_NEON
259
259
  size_t i = 0;
260
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);
261
+ if (len >= sizeof(uint8x16_t)) {
262
+ uint8x16_t has_some_hibit = vdupq_n_u8(0);
263
+ uint8x16_t hibit = vdupq_n_u8(0x80);
265
264
 
266
- uint8x16_t chunk = vld1q_u8(str);
265
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
266
+ size += sizeof(uint8x16_t);
267
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));
268
+ uint8x16_t chunk = vld1q_u8(str);
270
269
 
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;
270
+ // Check to see if any of these bytes have the high bit set.
271
+ has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
272
+
273
+ uint8x16_t tmp1 = vqtbl4q_u8(rails_xss_friendly_chars_neon[0], chunk);
274
+ uint8x16_t tmp2 = vqtbl4q_u8(rails_xss_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
275
+ uint8x16_t tmp3 = vqtbl4q_u8(rails_xss_friendly_chars_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
276
+ uint8x16_t tmp4 = vqtbl4q_u8(rails_xss_friendly_chars_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
277
+ uint8x16_t result = vorrq_u8(tmp4, vorrq_u8(tmp3, vorrq_u8(tmp1, tmp2)));
278
+ uint8_t tmp = vaddvq_u8(result);
279
+ size += tmp;
280
+ }
281
+
282
+ // 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
283
+ hi = vmaxvq_u8(has_some_hibit) != 0;
278
284
  }
279
285
 
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;
286
+ size_t len_remaining = len - i;
282
287
 
283
288
  for (; i < len; str++, i++) {
284
- size += rails_xss_friendly_chars[*str] - '0';
289
+ size += rails_xss_friendly_chars[*str];
285
290
  hi |= *str & 0x80;
286
291
  }
292
+
293
+ size -= (len_remaining * ((size_t)'0'));
294
+
287
295
  if (0 == hi) {
288
296
  return size;
289
297
  }
@@ -302,37 +310,43 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
302
310
  }
303
311
 
304
312
  inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
305
- long size = 0;
306
- uint8_t hi = 0;
313
+ long size = 0;
314
+ uint32_t hi = 0;
307
315
  #ifdef HAVE_SIMD_NEON
308
- size_t i = 0;
316
+ size_t i = 0;
317
+ long extra = 0;
309
318
 
310
- uint8x16_t has_some_hibit = vdupq_n_u8(0);
311
- uint8x16_t hibit = vdupq_n_u8(0x80);
319
+ if (len >= sizeof(uint8x16_t)) {
320
+ uint8x16_t has_some_hibit = vdupq_n_u8(0);
321
+ uint8x16_t hibit = vdupq_n_u8(0x80);
312
322
 
313
- for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
314
- size += sizeof(uint8x16_t);
323
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
324
+ size += sizeof(uint8x16_t);
315
325
 
316
- // See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
317
- uint8x16_t chunk = vld1q_u8(str);
326
+ // See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
327
+ uint8x16_t chunk = vld1q_u8(str);
318
328
 
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));
329
+ // Check to see if any of these bytes have the high bit set.
330
+ has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
321
331
 
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
- }
332
+ uint8x16_t tmp1 = vqtbl4q_u8(rails_friendly_chars_neon[0], chunk);
333
+ uint8x16_t tmp2 = vqtbl4q_u8(rails_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
334
+ uint8x16_t result = vorrq_u8(tmp1, tmp2);
335
+ uint8_t tmp = vaddvq_u8(result);
336
+ size += tmp;
337
+ }
328
338
 
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;
339
+ // 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
340
+ hi = vmaxvq_u8(has_some_hibit) != 0;
341
+ }
331
342
 
332
- for (; i < len; str++, i++) {
333
- size += rails_friendly_chars[*str] - '0';
343
+ for (; i < len; str++, i++, extra++) {
344
+ size += rails_friendly_chars[*str];
334
345
  hi |= *str & 0x80;
335
346
  }
347
+
348
+ size -= (extra * ((size_t)'0'));
349
+
336
350
  if (0 == hi) {
337
351
  return size;
338
352
  }
@@ -896,6 +910,12 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
896
910
  }
897
911
  }
898
912
 
913
+ #if defined(__clang__) || defined(__GNUC__)
914
+ #define FORCE_INLINE __attribute__((always_inline))
915
+ #else
916
+ #define FORCE_INLINE
917
+ #endif
918
+
899
919
  #ifdef HAVE_SIMD_NEON
900
920
  typedef struct _neon_match_result {
901
921
  uint8x16_t needs_escape;
@@ -903,12 +923,6 @@ typedef struct _neon_match_result {
903
923
  bool do_unicode_validation;
904
924
  } neon_match_result;
905
925
 
906
- #if defined(__clang__) || defined(__GNUC__)
907
- #define FORCE_INLINE __attribute__((always_inline))
908
- #else
909
- #define FORCE_INLINE
910
- #endif
911
-
912
926
  static inline FORCE_INLINE neon_match_result
913
927
  neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool do_unicode_validation, bool has_hi) {
914
928
  neon_match_result result = {.has_some_hibit = false, .do_unicode_validation = false};
@@ -932,12 +946,83 @@ neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool
932
946
 
933
947
  #endif /* HAVE_SIMD_NEON */
934
948
 
949
+ static inline FORCE_INLINE const char *process_character(char action,
950
+ const char *str,
951
+ const char *end,
952
+ Out out,
953
+ const char *orig,
954
+ bool do_unicode_validation,
955
+ const char **check_start_) {
956
+ const char *check_start = *check_start_;
957
+ switch (action) {
958
+ case '1':
959
+ if (do_unicode_validation && check_start <= str) {
960
+ if (0 != (0x80 & (uint8_t)*str)) {
961
+ if (0xC0 == (0xC0 & (uint8_t)*str)) {
962
+ *check_start_ = check_unicode(str, end, orig);
963
+ } else {
964
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
965
+ }
966
+ }
967
+ }
968
+ *out->cur++ = *str;
969
+ break;
970
+ case '2':
971
+ *out->cur++ = '\\';
972
+ switch (*str) {
973
+ case '\\': *out->cur++ = '\\'; break;
974
+ case '\b': *out->cur++ = 'b'; break;
975
+ case '\t': *out->cur++ = 't'; break;
976
+ case '\n': *out->cur++ = 'n'; break;
977
+ case '\f': *out->cur++ = 'f'; break;
978
+ case '\r': *out->cur++ = 'r'; break;
979
+ default: *out->cur++ = *str; break;
980
+ }
981
+ break;
982
+ case '3': // Unicode
983
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
984
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
985
+ str = dump_unicode(str, end, out, orig);
986
+ } else {
987
+ *check_start_ = check_unicode(str, end, orig);
988
+ *out->cur++ = *str;
989
+ }
990
+ break;
991
+ }
992
+ str = dump_unicode(str, end, out, orig);
993
+ break;
994
+ case '6': // control characters
995
+ if (*(uint8_t *)str < 0x80) {
996
+ if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
997
+ break;
998
+ }
999
+ APPEND_CHARS(out->cur, "\\u00", 4);
1000
+ dump_hex((uint8_t)*str, out);
1001
+ } else {
1002
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
1003
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
1004
+ str = dump_unicode(str, end, out, orig);
1005
+ } else {
1006
+ *check_start_ = check_unicode(str, end, orig);
1007
+ *out->cur++ = *str;
1008
+ }
1009
+ break;
1010
+ }
1011
+ str = dump_unicode(str, end, out, orig);
1012
+ }
1013
+ break;
1014
+ default: break; // ignore, should never happen if the table is correct
1015
+ }
1016
+
1017
+ return str;
1018
+ }
1019
+
935
1020
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
936
1021
  size_t size;
937
1022
  char *cmap;
938
1023
  #ifdef HAVE_SIMD_NEON
939
- uint8x16x4_t *cmap_neon = NULL;
940
- int neon_table_size;
1024
+ uint8x16x4_t *cmap_neon = NULL;
1025
+ int neon_table_size = 0;
941
1026
  #endif /* HAVE_SIMD_NEON */
942
1027
  const char *orig = str;
943
1028
  bool has_hi = false;
@@ -1036,171 +1121,83 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1036
1121
  #ifdef HAVE_SIMD_NEON
1037
1122
  const char *chunk_start;
1038
1123
  const char *chunk_end;
1039
- const char *cursor = str;
1040
- int neon_state = (cmap_neon != NULL) ? 1 : 4;
1124
+ const char *cursor = str;
1125
+ bool use_neon = (cmap_neon != NULL && cnt >= (sizeof(uint8x16_t))) ? true : false;
1041
1126
  char matches[16];
1042
- bool do_hi_validation = false;
1043
- // uint64_t neon_match_mask = 0;
1044
1127
  #define SEARCH_FLUSH \
1045
1128
  if (str > cursor) { \
1046
1129
  APPEND_CHARS(out->cur, cursor, str - cursor); \
1047
1130
  cursor = str; \
1048
1131
  }
1049
1132
 
1050
- loop:
1051
1133
  #endif /* HAVE_SIMD_NEON */
1052
- for (; str < end; str++) {
1053
- char action = 0;
1054
1134
  #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
- }
1135
+ if (use_neon) {
1136
+ while (str < end) {
1137
+ const char *chunk_ptr = NULL;
1138
+ if (str + sizeof(uint8x16_t) <= end) {
1139
+ chunk_ptr = str;
1140
+ chunk_start = str;
1141
+ chunk_end = str + sizeof(uint8x16_t);
1142
+ } else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
1143
+ memset(out->cur, 'A', sizeof(uint8x16_t));
1144
+ memcpy(out->cur, str, (end - str));
1145
+ chunk_ptr = out->cur;
1146
+ chunk_start = str;
1147
+ chunk_end = end;
1132
1148
  } else {
1133
- long match_index = str - chunk_start;
1134
- action = matches[match_index];
1149
+ break;
1135
1150
  }
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) {
1146
- case '1':
1147
- if (do_unicode_validation && check_start <= str) {
1148
- if (0 != (0x80 & (uint8_t)*str)) {
1149
- if (0xC0 == (0xC0 & (uint8_t)*str)) {
1150
- check_start = check_unicode(str, end, orig);
1151
- } else {
1152
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
1151
+ neon_match_result result = neon_update(chunk_ptr,
1152
+ cmap_neon,
1153
+ neon_table_size,
1154
+ do_unicode_validation,
1155
+ has_hi);
1156
+ if ((result.do_unicode_validation) || vmaxvq_u8(result.needs_escape) != 0) {
1157
+ SEARCH_FLUSH;
1158
+ uint8x16_t actions = vaddq_u8(result.needs_escape, vdupq_n_u8('1'));
1159
+ uint8_t num_matches = vaddvq_u8(vandq_u8(result.needs_escape, vdupq_n_u8(0x1)));
1160
+ vst1q_u8((unsigned char *)matches, actions);
1161
+ bool process_each = result.do_unicode_validation || (num_matches > sizeof(uint8x16_t) / 2);
1162
+ // If no byte in this chunk had the high bit set then we can skip
1163
+ // all of the '1' bytes by directly copying them to the output.
1164
+ if (!process_each) {
1165
+ while (str < chunk_end) {
1166
+ long i = str - chunk_start;
1167
+ char action;
1168
+ while (str < chunk_end && (action = matches[i++]) == '1') {
1169
+ *out->cur++ = *str++;
1170
+ }
1171
+ cursor = str;
1172
+ if (str >= chunk_end) {
1173
+ break;
1174
+ }
1175
+ str = process_character(action, str, end, out, orig, do_unicode_validation, &check_start);
1176
+ str++;
1153
1177
  }
1154
- }
1155
- }
1156
- *out->cur++ = *str;
1157
- break;
1158
- case '2':
1159
- *out->cur++ = '\\';
1160
- switch (*str) {
1161
- case '\\': *out->cur++ = '\\'; break;
1162
- case '\b': *out->cur++ = 'b'; break;
1163
- case '\t': *out->cur++ = 't'; break;
1164
- case '\n': *out->cur++ = 'n'; break;
1165
- case '\f': *out->cur++ = 'f'; break;
1166
- case '\r': *out->cur++ = 'r'; break;
1167
- default: *out->cur++ = *str; break;
1168
- }
1169
- break;
1170
- case '3': // Unicode
1171
- if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
1172
- if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
1173
- str = dump_unicode(str, end, out, orig);
1174
1178
  } else {
1175
- check_start = check_unicode(str, end, orig);
1176
- *out->cur++ = *str;
1177
- }
1178
- break;
1179
- }
1180
- str = dump_unicode(str, end, out, orig);
1181
- break;
1182
- case '6': // control characters
1183
- if (*(uint8_t *)str < 0x80) {
1184
- if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
1185
- break;
1186
- }
1187
- APPEND_CHARS(out->cur, "\\u00", 4);
1188
- dump_hex((uint8_t)*str, out);
1189
- } else {
1190
- if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
1191
- if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
1192
- str = dump_unicode(str, end, out, orig);
1193
- } else {
1194
- check_start = check_unicode(str, end, orig);
1195
- *out->cur++ = *str;
1179
+ while (str < chunk_end) {
1180
+ long match_index = str - chunk_start;
1181
+ str = process_character(matches[match_index],
1182
+ str,
1183
+ end,
1184
+ out,
1185
+ orig,
1186
+ do_unicode_validation,
1187
+ &check_start);
1188
+ str++;
1196
1189
  }
1197
- break;
1198
1190
  }
1199
- str = dump_unicode(str, end, out, orig);
1191
+ cursor = str;
1192
+ continue;
1200
1193
  }
1201
- break;
1202
- default: break; // ignore, should never happen if the table is correct
1194
+ str = chunk_end;
1203
1195
  }
1196
+ SEARCH_FLUSH;
1197
+ }
1198
+ #endif /* HAVE_SIMD_NEON */
1199
+ for (; str < end; str++) {
1200
+ str = process_character(cmap[(uint8_t)*str], str, end, out, orig, do_unicode_validation, &check_start);
1204
1201
  }
1205
1202
  *out->cur++ = '"';
1206
1203
  }
data/ext/oj/dump_compat.c CHANGED
@@ -148,10 +148,10 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
148
148
  } else {
149
149
  size = d2 * out->indent + 2;
150
150
  }
151
- assure_size(out, size * cnt);
152
151
  cnt--;
153
152
  for (i = 0; i <= cnt; i++) {
154
153
  if (out->opts->dump_opts.use) {
154
+ assure_size(out, size);
155
155
  if (0 < out->opts->dump_opts.array_size) {
156
156
  APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
157
157
  }
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.11'
3
+ VERSION = '3.16.12'
4
4
  end
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.11
4
+ version: 3.16.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-30 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bigdecimal
@@ -91,9 +91,9 @@ executables: []
91
91
  extensions:
92
92
  - ext/oj/extconf.rb
93
93
  extra_rdoc_files:
94
- - README.md
95
- - LICENSE
96
94
  - CHANGELOG.md
95
+ - LICENSE
96
+ - README.md
97
97
  - RELEASE_NOTES.md
98
98
  - pages/Advanced.md
99
99
  - pages/Compatibility.md
@@ -229,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
229
  - !ruby/object:Gem::Version
230
230
  version: '0'
231
231
  requirements: []
232
- rubygems_version: 3.6.2
232
+ rubygems_version: 3.6.9
233
233
  specification_version: 4
234
234
  summary: A fast JSON parser and serializer.
235
235
  test_files: []